diff --git a/src/components/Map.jsx b/src/components/Map.jsx
index a76860b..e92d38f 100644
--- a/src/components/Map.jsx
+++ b/src/components/Map.jsx
@@ -19,6 +19,7 @@ import DefsMarkers from './presentational/Map/DefsMarkers.jsx'
// NB: important constants for map, TODO: make statics
const supportedMapboxMap = ['streets', 'satellite']
const defaultToken = 'your_token'
+const clusterId = 'clusters'
class Map extends React.Component {
constructor () {
@@ -30,7 +31,8 @@ class Map extends React.Component {
this.index = null
this.state = {
mapTransformX: 0,
- mapTransformY: 0
+ mapTransformY: 0,
+ clusters: []
}
this.styleLocation = this.styleLocation.bind(this)
}
@@ -42,6 +44,9 @@ class Map extends React.Component {
}
componentWillReceiveProps (nextProps) {
+ if (hash(nextProps.domain.locations) !== hash(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) &&
@@ -77,6 +82,13 @@ class Map extends React.Component {
.setMaxZoom(mapConf.maxZoom)
.setMaxBounds(mapConf.maxBounds)
+ // Initialize supercluster index
+ const index = new Supercluster({
+ radius: mapConf.clusterRadius,
+ maxZoom: mapConf.maxZoom,
+ minZoom: mapConf.minZoom
+ })
+
let firstLayer
if ((supportedMapboxMap.indexOf(this.props.ui.tiles) !== -1) && process.env.MAPBOX_TOKEN && process.env.MAPBOX_TOKEN !== defaultToken) {
@@ -96,53 +108,42 @@ class Map extends React.Component {
map.keyboard.disable()
map.zoomControl.remove()
- map.on('moveend', () => this.updateClusters());
- map.on('move zoomend viewreset moveend', () => this.alignLayers())
+
+ map.on('moveend', () => {
+ this.update()
+ this.alignLayers()
+ })
+ map.on('move zoomend viewreset', () => this.alignLayers())
map.on('zoomstart', () => { if (this.svgRef.current !== null) this.svgRef.current.classList.add('hide') })
map.on('zoomend', () => { if (this.svgRef.current !== null) this.svgRef.current.classList.remove('hide') })
window.addEventListener('resize', () => { this.alignLayers() })
this.map = map
- }
-
- // createClusterIcon(feature, latlng) {
- // if (!feature.properties.cluster) return L.marker(latlng);
-
- // const count = feature.properties.point_count;
- // const size =
- // count < 100 ? 'small' :
- // count < 1000 ? 'medium' : 'large';
- // const icon = L.divIcon({
- // html: `
${ feature.properties.point_count_abbreviated }
`,
- // className: `marker-cluster marker-cluster-${ size}`,
- // iconSize: L.point(40, 40)
- // });
- // return L.marker(latlng, {icon});
- // }
-
- initializeSupercluster (locations) {
- const { map: mapConf } = this.props.app
- if (locations.length === 0) return
- const geoJSON = locations.map(this.locationToGeoJSON)
- // initialize supercluster
- const index = new Supercluster({
- radius: 40,
- maxZoom: mapConf.maxZoom,
- minZoom: mapConf.minZoom
- }).load(geoJSON)
- // Empty Layer Group that will receive the clusters data on the fly.
- var markers = L.geoJSON(geoJSON, {}).addTo(this.map);
- markers.id = 'clusters'
this.index = index
}
- updateClusters () {
- var bounds = this.map.getBounds();
- var bbox = [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()];
- var zoom = this.map.getZoom();
- var clusters = this.index.getClusters(bbox, zoom);
- // markers.clearLayers();
- // markers.addData(clusters);
+ getMapDetails () {
+ const bounds = this.map.getBounds()
+ const bbox = [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]
+ const zoom = this.map.getZoom()
+ return [bbox, zoom]
+ }
+
+ update () {
+ const [bbox, zoom] = this.getMapDetails()
+ this.setState({
+ clusters: this.index.getClusters(bbox, zoom)
+ })
+ }
+
+ loadClusterData (locations) {
+ if (locations && locations.length !== 0) {
+ const geoJSON = locations.map(this.locationToGeoJSON)
+ if (this.index) {
+ this.index.load(geoJSON)
+ this.update()
+ }
+ }
}
alignLayers () {
@@ -171,13 +172,14 @@ class Map extends React.Component {
}
locationToGeoJSON (location) {
- const { x, y } = this.projectPoint([location.latitude, location.longitude])
const feature = {
type: 'Feature',
- properties: {},
+ properties: {
+ cluster: false
+ },
geometry: {
type: 'Point',
- coordinates: [x, y]
+ coordinates: [location.longitude, location.latitude]
}
}
return feature
@@ -245,15 +247,6 @@ class Map extends React.Component {
)
}
- renderClusters () {
- if (this.index === null) return
- var bounds = this.map.getBounds();
- var bbox = [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()];
- var zoom = this.map.getZoom();
- var clusters = this.index.getClusters(bbox, zoom);
- console.info(this.map)
- console.info('CLUSTERS: ', clusters)
- }
/**
* Determines additional styles on the for each location.
* A location consists of an array of events (see selectors). The function
@@ -306,7 +299,6 @@ class Map extends React.Component {
render () {
const { isShowingSites } = this.props.app.flags
- this.initializeSupercluster(this.props.domain.locations)
const classes = this.props.app.narrative ? 'map-wrapper narrative-mode' : 'map-wrapper'
const innerMap = this.map ? (
@@ -316,7 +308,6 @@ class Map extends React.Component {
{this.renderShapes()}
{this.renderNarratives()}
{this.renderEvents()}
- {/* {this.renderClusters()} */}
{this.renderSelected()}
) : null
diff --git a/src/store/initial.js b/src/store/initial.js
index 1b9ab6d..1842230 100644
--- a/src/store/initial.js
+++ b/src/store/initial.js
@@ -52,7 +52,8 @@ const initial = {
minZoom: 6,
maxZoom: 18,
bounds: null,
- maxBounds: [[180, -180], [-180, 180]]
+ maxBounds: [[180, -180], [-180, 180]],
+ clusterRadius: 100,
},
timeline: {
dimensions: {