-
Notifications
You must be signed in to change notification settings - Fork 75
Description
Point features (and by extension marker features) support clustering. Once clustered, point.data() returns the clustered data, which is a list of unclustered point records followed by the clustered point records for a particular zoom level. The style accessors reference these records, not the original records set in data(). There isn't currently any official way to get the original data value or to get the points within different clusters.
As such, this results in messy code like
var original_data = [... some array of point data that contains style information on each point ...];
var points = layer.createFeature('point');
points.data(original_data);
points.clustering({radius: 10});
var firstPoint = (prop, d, i) => {
if (d.__cluster) {
d = d.obj;
}
if (d._points === undefined) {
return d[prop];
}
if (d._points.length) {
// we have to access the original data, since we don't have a public way to get to it now.
return original_data[d._points[0].index][prop];
}
return firstPoint(prop, d._clusters[0], i);
}
points.style({
radius: (d, i) => {
// scale the radius based on how many points are included
if (d.__cluster) {
return 5 * (d.obj._count ** 0.5);
}
return 5;
},
// have other styles use style of the first point
fillColor: (d, i) => firstPoint('fillColor', d, i),
strokeColor: (d, i) => firstPoint('strokeColor', d, i),
fillOpacity: (d, i) => firstPoint('fillOpacity', d, i),
strokeOpacity: (d, i) => firstPoint('strokeOpacity', d, i)
});
This is a mess -- we shouldn't need to access the internal methods and properties to do this. There is also an internal __data property that is not used and not set as commented.
We should remove __data and add some convenience methods. (update: see #1389)
I think the following would be appropriate:
points.unclusteredData(): return the original data (rather than adding an overload parameter to thedatamethod, which would be the other approach to this).points.clustered(d): wheredis the data value passed to a style accessor, returns a booleanpoints.clusterPointCount(d): returns the count of points (1 for unclustered)points.clusterPoint(d, cluster_point_index): returns a specific point within the cluster. This is the original data record, not the cluster point record.points.clusterPoints(d, onlyDirect=false, limit=null): return the points associated with a cluser. IfonlyDirectwas set, this would not include sub-clusters. IfonlyDirectis false, this is functionally the list that is used to pull points frompoints.getClusterPoint(d, cluster_point_index). These are the original data records, not the cluster point records.points.clusterClusters(d): return a list of immediate clusters that are children of this cluster. These could be passed to thecluster*methods above to recurse manually.
With this , our code would now look like:
var original_data = [... some array of point data that contains style information on each point ...];
var points = layer.createFeature('point');
points.data(original_data);
points.clustering({radius: 10});
points.style({
radius: (d, i) => 5 * this.clusterPointCount(d) ** 0.5,
fillColor: (d, i) => this.clusterPoint(d, 0).fillColor,
strokeColor: (d, i) => this.clusterPoint(d, 0).strokeColor,
fillOpacity: (d, i) => this.clusterPoint(d, 0).fillOpacity,
strokeOpacity: (d, i) => this.clusterPoint(d, 0).strokeOpacity,
});