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