Skip to content

Commit

Permalink
Introduce minPoints option (#156)
Browse files Browse the repository at this point in the history
* introduce minPoints option

* add a test for minPoints
  • Loading branch information
mourner authored Jun 2, 2020
1 parent 119e258 commit ccb429c
Show file tree
Hide file tree
Showing 4 changed files with 722 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Returns the zoom on which the cluster expands into several children (useful for
|------------|---------|-------------------------------------------------------------------|
| minZoom | 0 | Minimum zoom level at which clusters are generated. |
| maxZoom | 16 | Maximum zoom level at which clusters are generated. |
| minPoints | 2 | Minimum number of points to form a cluster. |
| radius | 40 | Cluster radius, in pixels. |
| extent | 512 | (Tiles) Tile extent. Radius is calculated relative to this value. |
| nodeSize | 64 | Size of the KD-tree leaf node. Affects performance. |
Expand Down
65 changes: 42 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import KDBush from 'kdbush';
const defaultOptions = {
minZoom: 0, // min zoom to generate clusters on
maxZoom: 16, // max zoom level to cluster the points on
minPoints: 2, // minimum points to form a cluster
radius: 40, // cluster radius in pixels
extent: 512, // tile extent (radius is calculated relative to it)
nodeSize: 64, // size of the KD-tree leaf node, affects performance
Expand Down Expand Up @@ -229,7 +230,7 @@ export default class Supercluster {

_cluster(points, zoom) {
const clusters = [];
const {radius, extent, reduce} = this.options;
const {radius, extent, reduce, minPoints} = this.options;
const r = radius / (extent * Math.pow(2, zoom));

// loop through each point
Expand All @@ -243,39 +244,57 @@ export default class Supercluster {
const tree = this.trees[zoom + 1];
const neighborIds = tree.within(p.x, p.y, r);

let numPoints = p.numPoints || 1;
let wx = p.x * numPoints;
let wy = p.y * numPoints;

let clusterProperties = reduce && numPoints > 1 ? this._map(p, true) : null;

// encode both zoom and point index on which the cluster originated -- offset by total length of features
const id = (i << 5) + (zoom + 1) + this.points.length;
const numPointsOrigin = p.numPoints || 1;
let numPoints = numPointsOrigin;

// count the number of points in a potential cluster
for (const neighborId of neighborIds) {
const b = tree.points[neighborId];
// filter out neighbors that are already processed
if (b.zoom <= zoom) continue;
b.zoom = zoom; // save the zoom (so it doesn't get processed twice)
if (b.zoom > zoom) numPoints += b.numPoints || 1;
}

if (numPoints >= minPoints) { // enough points to form a cluster
let wx = p.x * numPointsOrigin;
let wy = p.y * numPointsOrigin;

let clusterProperties = reduce && numPointsOrigin > 1 ? this._map(p, true) : null;

// encode both zoom and point index on which the cluster originated -- offset by total length of features
const id = (i << 5) + (zoom + 1) + this.points.length;

for (const neighborId of neighborIds) {
const b = tree.points[neighborId];

if (b.zoom <= zoom) continue;
b.zoom = zoom; // save the zoom (so it doesn't get processed twice)

const numPoints2 = b.numPoints || 1;
wx += b.x * numPoints2; // accumulate coordinates for calculating weighted center
wy += b.y * numPoints2;
const numPoints2 = b.numPoints || 1;
wx += b.x * numPoints2; // accumulate coordinates for calculating weighted center
wy += b.y * numPoints2;

numPoints += numPoints2;
b.parentId = id;
b.parentId = id;

if (reduce) {
if (!clusterProperties) clusterProperties = this._map(p, true);
reduce(clusterProperties, this._map(b));
if (reduce) {
if (!clusterProperties) clusterProperties = this._map(p, true);
reduce(clusterProperties, this._map(b));
}
}
}

if (numPoints === 1) {
clusters.push(p);
} else {
p.parentId = id;
clusters.push(createCluster(wx / numPoints, wy / numPoints, id, numPoints, clusterProperties));

} else { // left points as unclustered
clusters.push(p);

if (numPoints > 1) {
for (const neighborId of neighborIds) {
const b = tree.points[neighborId];
if (b.zoom <= zoom) continue;
b.zoom = zoom;
clusters.push(b);
}
}
}
}

Expand Down
Loading

0 comments on commit ccb429c

Please sign in to comment.