From c310d15579f021e134c4479f595cf4d674ef8db0 Mon Sep 17 00:00:00 2001 From: efarooqui Date: Mon, 19 Oct 2020 13:32:35 -0700 Subject: [PATCH] Created utility function isIdentical; moved DefsClusters into Clusters file --- src/common/utilities.js | 12 ++++++ src/components/Map.jsx | 39 +++++++------------ .../presentational/Map/Clusters.jsx | 14 ++++++- .../presentational/Map/DefsClusters.jsx | 12 ------ 4 files changed, 38 insertions(+), 39 deletions(-) delete mode 100644 src/components/presentational/Map/DefsClusters.jsx diff --git a/src/common/utilities.js b/src/common/utilities.js index 2fb70f9..ad76526 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -1,4 +1,6 @@ import moment from 'moment' +import hash from 'object-hash' + let { DATE_FMT, TIME_FMT } = process.env if (!DATE_FMT) DATE_FMT = 'MM/DD/YYYY' @@ -159,6 +161,10 @@ export function selectTypeFromPathWithPoster (path, poster) { return { type: typeForPath(path), path, poster } } +export function isIdentical (obj1, obj2) { + return hash(obj1) === hash(obj2) +} + export function calcOpacity (num) { /* Events have opacity 0.5 by default, and get added to according to how many * other events there are in the same render. The idea here is that the @@ -169,10 +175,16 @@ export function calcOpacity (num) { } export function calcClusterOpacity (pointCount, totalPoints) { + /* Clusters represent multiple events within a specific radius. The darker the cluster, + the larger the number of underlying events. We use a multiplication factor (50) here as well + to ensure that the larger clusters have an appropriately darker shading. */ return Math.min(0.85, 0.08 + (pointCount / totalPoints) * 50) } export function calcClusterSize (pointCount, totalPoints) { + /* The larger the cluster size, the higher the count of points that the cluster represents. + Just like with opacity, we use a multiplication factor to ensure that clusters with higher point + counts appear larger. */ return Math.min(50, 10 + (pointCount / totalPoints) * 150) } diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 46aa135..231a7a2 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -6,7 +6,6 @@ import Supercluster from 'supercluster' import { connect } from 'react-redux' import * as selectors from '../selectors' -import hash from 'object-hash' import 'leaflet' import Sites from './presentational/Map/Sites.jsx' @@ -16,9 +15,9 @@ import Clusters from './presentational/Map/Clusters.jsx' import SelectedEvents from './presentational/Map/SelectedEvents.jsx' import Narratives from './presentational/Map/Narratives' import DefsMarkers from './presentational/Map/DefsMarkers.jsx' -import DefsClusters from './presentational/Map/DefsClusters.jsx' +import LoadingOverlay from '../components/Overlay/Loading' -import { mapClustersToLocations } from '../common/utilities' +import { mapClustersToLocations, isIdentical } from '../common/utilities' // NB: important constants for map, TODO: make statics const supportedMapboxMap = ['streets', 'satellite'] @@ -31,7 +30,7 @@ class Map extends React.Component { this.onClusterSelect = this.onClusterSelect.bind(this) this.svgRef = React.createRef() this.map = null - this.index = null + this.superclusterIndex = null this.state = { mapTransformX: 0, mapTransformY: 0, @@ -48,16 +47,16 @@ class Map extends React.Component { } componentWillReceiveProps (nextProps) { - if (hash(nextProps.domain.locations) !== hash(this.props.domain.locations)) { + if (!isIdentical(nextProps.domain.locations, this.props.domain.locations)) { this.loadClusterData(nextProps.domain.locations) } // Set appropriate zoom for narrative const { bounds } = nextProps.app.map - if (hash(bounds) !== hash(this.props.app.map.bounds) && + if (!isIdentical(bounds, this.props.app.map.bounds) && bounds !== null) { this.map.fitBounds(bounds) } else { - if (hash(nextProps.app.selected) !== hash(this.props.app.selected)) { + if (!isIdentical(nextProps.app.selected, this.props.app.selected)) { // Fly to first of events selected const eventPoint = (nextProps.app.selected.length > 0) ? nextProps.app.selected[0] : null @@ -88,14 +87,12 @@ class Map extends React.Component { .setMaxBounds(mapConf.maxBounds) // Initialize supercluster index - const index = new Supercluster({ + this.superclusterIndex = new Supercluster({ radius: mapConf.clusterRadius, maxZoom: mapConf.maxZoom, minZoom: mapConf.minZoom }) - this.index = index - let firstLayer if ((supportedMapboxMap.indexOf(this.props.ui.tiles) !== -1) && process.env.MAPBOX_TOKEN && process.env.MAPBOX_TOKEN !== defaultToken) { @@ -138,15 +135,15 @@ class Map extends React.Component { update () { const [bbox, zoom] = this.getMapDetails() - if (this.index && this.state.indexLoaded) { + if (this.superclusterIndex && this.state.indexLoaded) { this.setState({ - clusters: this.index.getClusters(bbox, zoom) + clusters: this.superclusterIndex.getClusters(bbox, zoom) }) } } loadClusterData (locations) { - if (locations && locations.length > 0 && this.index) { + if (locations && locations.length > 0 && this.superclusterIndex) { const convertedLocations = locations.reduce((acc, loc) => { const { longitude, latitude } = loc const validCoordinates = !!latitude && !!longitude @@ -166,7 +163,7 @@ class Map extends React.Component { } return acc }, []) - this.index.load(convertedLocations) + this.superclusterIndex.load(convertedLocations) this.setState({ indexLoaded: true }) this.update() } else { @@ -202,7 +199,7 @@ class Map extends React.Component { onClusterSelect (e) { const { id } = e.target const { longitude, latitude } = e.target.attributes - const expansionZoom = Math.max(this.index.getClusterExpansionZoom(parseInt(id)), this.index.options.minZoom) + const expansionZoom = Math.max(this.superclusterIndex.getClusterExpansionZoom(parseInt(id)), this.superclusterIndex.options.minZoom) this.map.flyTo(new L.LatLng(latitude.value, longitude.value), expansionZoom) } @@ -330,14 +327,6 @@ class Map extends React.Component { ) } - renderClusterGradients () { - return ( - - - - ) - } - renderMarkers () { return ( @@ -353,7 +342,6 @@ class Map extends React.Component { {this.renderTiles()} {this.renderMarkers()} - {this.props.ui.radial ? this.renderClusterGradients() : null} {isShowingSites ? this.renderSites() : null} {this.renderShapes()} {this.renderNarratives()} @@ -391,7 +379,8 @@ function mapStateToProps (state) { map: state.app.map, narrative: state.app.associations.narrative, flags: { - isShowingSites: state.app.flags.isShowingSites + isShowingSites: state.app.flags.isShowingSites, + isFetchingDomain: state.app.flags.isFetchingDomain } }, ui: { diff --git a/src/components/presentational/Map/Clusters.jsx b/src/components/presentational/Map/Clusters.jsx index 62ccaf9..0f59688 100644 --- a/src/components/presentational/Map/Clusters.jsx +++ b/src/components/presentational/Map/Clusters.jsx @@ -3,6 +3,15 @@ import { Portal } from 'react-portal' import colors from '../../../common/global.js' import { calcClusterOpacity, calcClusterSize } from '../../../common/utilities' +const DefsClusters = () => ( + + + + + + +) + function ClusterEvents ({ projectPoint, styleCluster, @@ -27,12 +36,12 @@ function ClusterEvents ({ const totalPoints = calculateTotalPoints() - const styles = ({ + const styles = { fill: isRadial ? "url('#clusterGradient')" : colors.fallbackEventColor, stroke: colors.darkBackground, strokeWidth: 0, fillOpacity: calcClusterOpacity(pointCount, totalPoints) - }) + } return ( @@ -92,6 +101,7 @@ function ClusterEvents ({ return ( + {isRadial ? : null} {clusters.map(renderCluster)} diff --git a/src/components/presentational/Map/DefsClusters.jsx b/src/components/presentational/Map/DefsClusters.jsx deleted file mode 100644 index 1cf264b..0000000 --- a/src/components/presentational/Map/DefsClusters.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' - -const DefsClusters = () => ( - - - - - - -) - -export default DefsClusters