diff --git a/bench.js b/bench.js index 4e734bcf..ef1b7b99 100644 --- a/bench.js +++ b/bench.js @@ -22,7 +22,7 @@ for (let i = 0; i < 1000000; i++) { global.gc(); const size = v8.getHeapStatistics().used_heap_size; -const index = new Supercluster({log: true, maxZoom: 6}).load(points); +const index = new Supercluster({log: true, maxZoom: 6, zoomStep: 0.5}).load(points); global.gc(); console.log(`memory used: ${ Math.round((v8.getHeapStatistics().used_heap_size - size) / 1024) } KB`); diff --git a/index.js b/index.js index dc2601ba..1204dc95 100644 --- a/index.js +++ b/index.js @@ -2,8 +2,9 @@ import KDBush from 'kdbush'; const defaultOptions = { - minZoom: 0, // min zoom to generate clusters on - maxZoom: 16, // max zoom level to cluster the points on + minZoom: 0.0, // min zoom to generate clusters on + maxZoom: 16.0, // max zoom level to cluster the points on + zoomStep: 1, // Indicate the distance in zoom points between cluster computations. E.g. 0.1 = 10 cluster indexes by zoom level. 1 = one cluster index by zoom level. 0.5 = 2 cluster index by zoom level minPoints: 2, // minimum points to form a cluster radius: 40, // cluster radius in pixels extent: 512, // tile extent (radius is calculated relative to it) @@ -25,13 +26,19 @@ const fround = Math.fround || (tmp => ((x) => { tmp[0] = +x; return tmp[0]; }))( export default class Supercluster { constructor(options) { this.options = extend(Object.create(defaultOptions), options); - this.trees = new Array(this.options.maxZoom + 1); + this.zoomStep = +(this.options.zoomStep); + this.numTreesByZoomLevel = Math.ceil(1 / this.zoomStep); + this.zoomRange = this.options.maxZoom - this.options.minZoom; + this.numtrees = (this.zoomRange * this.numTreesByZoomLevel) + 2; + //Trees needed are, the number of trees on each zoom level times + // the range in zoom levels + this.trees = new Array(this.numtrees); } load(points) { - const {log, minZoom, maxZoom, nodeSize} = this.options; + const {log, minZoom, maxZoom, nodeSize, zoomStep} = this.options; - if (log) console.time('total time'); + if (log) console.time('total time', zoomStep); const timerId = `prepare ${ points.length } points`; if (log) console.time(timerId); @@ -44,19 +51,18 @@ export default class Supercluster { if (!points[i].geometry) continue; clusters.push(createPointCluster(points[i], i)); } - this.trees[maxZoom + 1] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); + this.trees[this._zoomToIndex(this.options.maxZoom) + 1] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); if (log) console.timeEnd(timerId); // cluster points on max zoom, then cluster the results on previous zoom, etc.; // results in a cluster hierarchy across zoom levels - for (let z = maxZoom; z >= minZoom; z--) { + for (let z = maxZoom; z >= minZoom; z -= this.zoomStep) { const now = +Date.now(); // create a new set of clusters for the zoom and index them with a KD-tree clusters = this._cluster(clusters, z); - this.trees[z] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); - + this.trees[this._zoomToIndex(z)] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); if (log) console.log('z%d: %d clusters in %dms', z, clusters.length, +Date.now() - now); } @@ -80,7 +86,7 @@ export default class Supercluster { return easternHem.concat(westernHem); } - const tree = this.trees[this._limitZoom(zoom)]; + const tree = this.trees[this._zoomToIndex(zoom)]; const ids = tree.range(lngX(minLng), latY(maxLat), lngX(maxLng), latY(minLat)); const clusters = []; for (const id of ids) { @@ -94,12 +100,12 @@ export default class Supercluster { const originId = this._getOriginId(clusterId); const originZoom = this._getOriginZoom(clusterId); const errorMsg = 'No cluster with the specified id.'; - - const index = this.trees[originZoom]; + const errorMsg2 = 'No point with the specified id.'; + const index = this.trees[this._zoomToIndex(originZoom)]; if (!index) throw new Error(errorMsg); const origin = index.points[originId]; - if (!origin) throw new Error(errorMsg); + if (!origin) throw new Error(errorMsg2); const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1)); const ids = index.within(origin.x, origin.y, r); @@ -127,7 +133,7 @@ export default class Supercluster { } getTile(z, x, y) { - const tree = this.trees[this._limitZoom(z)]; + const tree = this.trees[this._zoomToIndex(z)]; const z2 = Math.pow(2, z); const {extent, radius} = this.options; const p = radius / extent; @@ -160,7 +166,7 @@ export default class Supercluster { let expansionZoom = this._getOriginZoom(clusterId) - 1; while (expansionZoom <= this.options.maxZoom) { const children = this.getChildren(clusterId); - expansionZoom++; + expansionZoom += this.options.zoomStep; if (children.length !== 1) break; clusterId = children[0].properties.cluster_id; } @@ -239,14 +245,11 @@ export default class Supercluster { } } - _limitZoom(z) { - return Math.max(this.options.minZoom, Math.min(Math.floor(+z), this.options.maxZoom + 1)); - } - _cluster(points, zoom) { const clusters = []; const {radius, extent, reduce, minPoints} = this.options; const r = radius / (extent * Math.pow(2, zoom)); + const tree = this.trees[this._zoomToIndex(zoom) + 1]; // loop through each point for (let i = 0; i < points.length; i++) { @@ -256,7 +259,6 @@ export default class Supercluster { p.zoom = zoom; // find all nearby points - const tree = this.trees[zoom + 1]; const neighborIds = tree.within(p.x, p.y, r); const numPointsOrigin = p.numPoints || 1; @@ -277,7 +279,7 @@ export default class Supercluster { 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; + const id = (i << 8) + (this._zoomToIndex(zoom) + 1) + this.points.length; for (const neighborId of neighborIds) { const b = tree.points[neighborId]; @@ -317,16 +319,31 @@ export default class Supercluster { return clusters; } + _zoomToIndex(zoom) { + const clampedZoom = Math.max(this.options.minZoom, Math.min(zoom, this.options.maxZoom + this.zoomStep)); + // Get the index of the tree that better suits the zoom level + const adjustedZoom = clampedZoom - this.options.minZoom; + const stepIndex = Math.round(adjustedZoom * this.numTreesByZoomLevel); + return stepIndex; + } + // get index of the point from which the cluster originated _getOriginId(clusterId) { - return (clusterId - this.points.length) >> 5; + return (clusterId - this.points.length) >> 8; } // get zoom of the point from which the cluster originated + _getOriginIndex(clusterId) { + return (clusterId - this.points.length) % 256; + } + _getOriginZoom(clusterId) { - return (clusterId - this.points.length) % 32; + const originIndex = this._getOriginIndex(clusterId); + const originZoom = (originIndex / this.numTreesByZoomLevel) + this.options.minZoom; + return originZoom; } + _map(point, clone) { if (point.numPoints) { return clone ? extend({}, point.properties) : point.properties; diff --git a/package.json b/package.json index e091abcf..f79f47bd 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "cov": "c8 npm run test", "bench": "node --expose-gc -r esm bench.js", "build": "mkdirp dist && rollup -c", + "fix": "eslint --fix index.js bench.js test/test.js demo/index.js demo/worker.js", "prepublishOnly": "npm run test && npm run build" }, "files": [ diff --git a/test/fixtures/places-z0-0-0-min5.json b/test/fixtures/places-z0-0-0-min5.json index 8c6ec515..1bd50d34 100644 --- a/test/fixtures/places-z0-0-0-min5.json +++ b/test/fixtures/places-z0-0-0-min5.json @@ -2,7 +2,12 @@ "features": [ { "type": 1, - "geometry": [[151, 203]], + "geometry": [ + [ + 151, + 203 + ] + ], "tags": { "cluster": true, "cluster_id": 164, @@ -13,51 +18,76 @@ }, { "type": 1, - "geometry": [[165, 241]], + "geometry": [ + [ + 165, + 241 + ] + ], "tags": { "cluster": true, - "cluster_id": 196, + "cluster_id": 420, "point_count": 20, "point_count_abbreviated": 20 }, - "id": 196 + "id": 420 }, { "type": 1, - "geometry": [[178, 305]], + "geometry": [ + [ + 178, + 305 + ] + ], "tags": { "cluster": true, - "cluster_id": 228, + "cluster_id": 676, "point_count": 14, "point_count_abbreviated": 14 }, - "id": 228 + "id": 676 }, { "type": 1, - "geometry": [[329, 244]], + "geometry": [ + [ + 329, + 244 + ] + ], "tags": { "cluster": true, - "cluster_id": 260, + "cluster_id": 932, "point_count": 10, "point_count_abbreviated": 10 }, - "id": 260 + "id": 932 }, { "type": 1, - "geometry": [[296, 291]], + "geometry": [ + [ + 296, + 291 + ] + ], "tags": { "cluster": true, - "cluster_id": 356, + "cluster_id": 1700, "point_count": 11, "point_count_abbreviated": 11 }, - "id": 356 + "id": 1700 }, { "type": 1, - "geometry": [[90, 416]], + "geometry": [ + [ + 90, + 416 + ] + ], "tags": { "scalerank": 3, "name": "Wright I.", @@ -72,7 +102,12 @@ }, { "type": 1, - "geometry": [[74, 419]], + "geometry": [ + [ + 74, + 419 + ] + ], "tags": { "scalerank": 3, "name": "Dean I.", @@ -87,7 +122,12 @@ }, { "type": 1, - "geometry": [[69, 418]], + "geometry": [ + [ + 69, + 418 + ] + ], "tags": { "scalerank": 3, "name": "Grant I.", @@ -102,7 +142,12 @@ }, { "type": 1, - "geometry": [[49, 425]], + "geometry": [ + [ + 49, + 425 + ] + ], "tags": { "scalerank": 3, "name": "Newman I.", @@ -117,18 +162,28 @@ }, { "type": 1, - "geometry": [[89, 209]], + "geometry": [ + [ + 89, + 209 + ] + ], "tags": { "cluster": true, - "cluster_id": 548, + "cluster_id": 3236, "point_count": 5, "point_count_abbreviated": 5 }, - "id": 548 + "id": 3236 }, { "type": 1, - "geometry": [[106, 226]], + "geometry": [ + [ + 106, + 226 + ] + ], "tags": { "scalerank": 4, "name": "Cabo Corrientes", @@ -143,7 +198,12 @@ }, { "type": 1, - "geometry": [[123, 152]], + "geometry": [ + [ + 123, + 152 + ] + ], "tags": { "scalerank": 3, "name": "Cape Churchill", @@ -158,7 +218,12 @@ }, { "type": 1, - "geometry": [[160, 352]], + "geometry": [ + [ + 160, + 352 + ] + ], "tags": { "scalerank": 3, "name": "Cabo de Hornos", @@ -173,7 +238,12 @@ }, { "type": 1, - "geometry": [[163, 349]], + "geometry": [ + [ + 163, + 349 + ] + ], "tags": { "scalerank": 5, "name": "Cabo San Diego", @@ -188,29 +258,44 @@ }, { "type": 1, - "geometry": [[242, 237]], + "geometry": [ + [ + 242, + 237 + ] + ], "tags": { "cluster": true, - "cluster_id": 964, + "cluster_id": 6564, "point_count": 5, "point_count_abbreviated": 5 }, - "id": 964 + "id": 6564 }, { "type": 1, - "geometry": [[259, 193]], + "geometry": [ + [ + 259, + 193 + ] + ], "tags": { "cluster": true, - "cluster_id": 1092, + "cluster_id": 7588, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 1092 + "id": 7588 }, { "type": 1, - "geometry": [[80, 336]], + "geometry": [ + [ + 80, + 336 + ] + ], "tags": { "scalerank": 3, "name": "Oceanic pole of inaccessibility", @@ -225,7 +310,12 @@ }, { "type": 1, - "geometry": [[452, 377]], + "geometry": [ + [ + 452, + 377 + ] + ], "tags": { "scalerank": 3, "name": "South Magnetic Pole 2005 (est)", @@ -240,7 +330,12 @@ }, { "type": 1, - "geometry": [[93, 32]], + "geometry": [ + [ + 93, + 32 + ] + ], "tags": { "scalerank": 3, "name": "North Magnetic Pole 2005 (est)", @@ -255,7 +350,12 @@ }, { "type": 1, - "geometry": [[159, 84]], + "geometry": [ + [ + 159, + 84 + ] + ], "tags": { "scalerank": 4, "name": "Cape York", @@ -270,7 +370,12 @@ }, { "type": 1, - "geometry": [[194, 149]], + "geometry": [ + [ + 194, + 149 + ] + ], "tags": { "scalerank": 4, "name": "Nunap Isua", @@ -285,7 +390,12 @@ }, { "type": 1, - "geometry": [[227, 139]], + "geometry": [ + [ + 227, + 139 + ] + ], "tags": { "scalerank": 5, "name": "Surtsey", @@ -300,18 +410,28 @@ }, { "type": 1, - "geometry": [[27, 270]], + "geometry": [ + [ + 27, + 270 + ] + ], "tags": { "cluster": true, - "cluster_id": 1444, + "cluster_id": 10404, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 1444 + "id": 10404 }, { "type": 1, - "geometry": [[100, 296]], + "geometry": [ + [ + 100, + 296 + ] + ], "tags": { "scalerank": 4, "name": "I. de Pascua", @@ -326,7 +446,12 @@ }, { "type": 1, - "geometry": [[401, 226]], + "geometry": [ + [ + 401, + 226 + ] + ], "tags": { "scalerank": 4, "name": "Plain of Jars", @@ -341,7 +466,12 @@ }, { "type": 1, - "geometry": [[371, 248]], + "geometry": [ + [ + 371, + 248 + ] + ], "tags": { "scalerank": 5, "name": "Dondra Head", @@ -356,7 +486,12 @@ }, { "type": 1, - "geometry": [[19, 121]], + "geometry": [ + [ + 19, + 121 + ] + ], "tags": { "scalerank": 4, "name": "Cape Hope", @@ -371,7 +506,12 @@ }, { "type": 1, - "geometry": [[33, 109]], + "geometry": [ + [ + 33, + 109 + ] + ], "tags": { "scalerank": 4, "name": "Point Barrow", @@ -386,40 +526,60 @@ }, { "type": 1, - "geometry": [[459, 309]], + "geometry": [ + [ + 459, + 309 + ] + ], "tags": { "cluster": true, - "cluster_id": 1924, + "cluster_id": 14244, "point_count": 8, "point_count_abbreviated": 8 }, - "id": 1924 + "id": 14244 }, { "type": 1, - "geometry": [[483, 272]], + "geometry": [ + [ + 483, + 272 + ] + ], "tags": { "cluster": true, - "cluster_id": 2180, + "cluster_id": 16292, "point_count": 10, "point_count_abbreviated": 10 }, - "id": 2180 + "id": 16292 }, { "type": 1, - "geometry": [[423, 295]], + "geometry": [ + [ + 423, + 295 + ] + ], "tags": { "cluster": true, - "cluster_id": 2340, + "cluster_id": 17572, "point_count": 5, "point_count_abbreviated": 5 }, - "id": 2340 + "id": 17572 }, { "type": 1, - "geometry": [[225, 114]], + "geometry": [ + [ + 225, + 114 + ] + ], "tags": { "scalerank": 5, "name": "Cape Brewster", @@ -434,7 +594,12 @@ }, { "type": 1, - "geometry": [[230, 127]], + "geometry": [ + [ + 230, + 127 + ] + ], "tags": { "scalerank": 5, "name": "Grmsey", @@ -449,7 +614,12 @@ }, { "type": 1, - "geometry": [[210, 21]], + "geometry": [ + [ + 210, + 21 + ] + ], "tags": { "scalerank": 5, "name": "Cape Morris Jesup", @@ -464,7 +634,12 @@ }, { "type": 1, - "geometry": [[238, 154]], + "geometry": [ + [ + 238, + 154 + ] + ], "tags": { "scalerank": 5, "name": "Rockall", @@ -479,29 +654,44 @@ }, { "type": 1, - "geometry": [[484, 235]], + "geometry": [ + [ + 484, + 235 + ] + ], "tags": { "cluster": true, - "cluster_id": 2692, + "cluster_id": 20388, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 2692 + "id": 20388 }, { "type": 1, - "geometry": [[471, 167]], + "geometry": [ + [ + 471, + 167 + ] + ], "tags": { "cluster": true, - "cluster_id": 3236, + "cluster_id": 24740, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 3236 + "id": 24740 }, { "type": 1, - "geometry": [[498, 149]], + "geometry": [ + [ + 498, + 149 + ] + ], "tags": { "scalerank": 5, "name": "Cape Olyutorskiy", @@ -516,7 +706,12 @@ }, { "type": 1, - "geometry": [[511, 142]], + "geometry": [ + [ + 511, + 142 + ] + ], "tags": { "scalerank": 5, "name": "Cape Navarin", @@ -531,7 +726,12 @@ }, { "type": 1, - "geometry": [[469, 106]], + "geometry": [ + [ + 469, + 106 + ] + ], "tags": { "scalerank": 5, "name": "Cape Lopatka", @@ -546,7 +746,12 @@ }, { "type": 1, - "geometry": [[292, 110]], + "geometry": [ + [ + 292, + 110 + ] + ], "tags": { "scalerank": 5, "name": "Nordkapp", @@ -561,7 +766,12 @@ }, { "type": 1, - "geometry": [[205, 263]], + "geometry": [ + [ + 205, + 263 + ] + ], "tags": { "scalerank": 5, "name": "Cabo de São Roque", @@ -576,29 +786,44 @@ }, { "type": 1, - "geometry": [[-29, 272]], + "geometry": [ + [ + -29, + 272 + ] + ], "tags": { "cluster": true, - "cluster_id": 2180, + "cluster_id": 16292, "point_count": 10, "point_count_abbreviated": 10 }, - "id": 2180 + "id": 16292 }, { "type": 1, - "geometry": [[-28, 235]], + "geometry": [ + [ + -28, + 235 + ] + ], "tags": { "cluster": true, - "cluster_id": 2692, + "cluster_id": 20388, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 2692 + "id": 20388 }, { "type": 1, - "geometry": [[-14, 149]], + "geometry": [ + [ + -14, + 149 + ] + ], "tags": { "scalerank": 5, "name": "Cape Olyutorskiy", @@ -613,7 +838,12 @@ }, { "type": 1, - "geometry": [[-1, 142]], + "geometry": [ + [ + -1, + 142 + ] + ], "tags": { "scalerank": 5, "name": "Cape Navarin", @@ -628,18 +858,28 @@ }, { "type": 1, - "geometry": [[539, 270]], + "geometry": [ + [ + 539, + 270 + ] + ], "tags": { "cluster": true, - "cluster_id": 1444, + "cluster_id": 10404, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 1444 + "id": 10404 }, { "type": 1, - "geometry": [[531, 121]], + "geometry": [ + [ + 531, + 121 + ] + ], "tags": { "scalerank": 4, "name": "Cape Hope", @@ -654,7 +894,12 @@ }, { "type": 1, - "geometry": [[545, 109]], + "geometry": [ + [ + 545, + 109 + ] + ], "tags": { "scalerank": 4, "name": "Point Barrow", @@ -668,4 +913,4 @@ } } ] -} +} \ No newline at end of file diff --git a/test/fixtures/places-z0-0-0.json b/test/fixtures/places-z0-0-0.json index c7278d87..0b939af7 100644 --- a/test/fixtures/places-z0-0-0.json +++ b/test/fixtures/places-z0-0-0.json @@ -1,8 +1,14 @@ { + "type": "FeatureCollection", "features": [ { "type": 1, - "geometry": [[150, 205]], + "geometry": [ + [ + 150, + 205 + ] + ], "tags": { "cluster": true, "cluster_id": 164, @@ -13,73 +19,108 @@ }, { "type": 1, - "geometry": [[165, 240]], + "geometry": [ + [ + 165, + 240 + ] + ], "tags": { "cluster": true, - "cluster_id": 196, + "cluster_id": 420, "point_count": 18, "point_count_abbreviated": 18 }, - "id": 196 + "id": 420 }, { "type": 1, - "geometry": [[179, 303]], + "geometry": [ + [ + 179, + 303 + ] + ], "tags": { "cluster": true, - "cluster_id": 228, + "cluster_id": 676, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 228 + "id": 676 }, { "type": 1, - "geometry": [[336, 234]], + "geometry": [ + [ + 336, + 234 + ] + ], "tags": { "cluster": true, - "cluster_id": 260, + "cluster_id": 932, "point_count": 8, "point_count_abbreviated": 8 }, - "id": 260 + "id": 932 }, { "type": 1, - "geometry": [[299, 285]], + "geometry": [ + [ + 299, + 285 + ] + ], "tags": { "cluster": true, - "cluster_id": 292, + "cluster_id": 1188, "point_count": 15, "point_count_abbreviated": 15 }, - "id": 292 + "id": 1188 }, { "type": 1, - "geometry": [[71, 419]], + "geometry": [ + [ + 71, + 419 + ] + ], "tags": { "cluster": true, - "cluster_id": 324, + "cluster_id": 1444, "point_count": 4, "point_count_abbreviated": 4 }, - "id": 324 + "id": 1444 }, { "type": 1, - "geometry": [[92, 212]], + "geometry": [ + [ + 92, + 212 + ] + ], "tags": { "cluster": true, - "cluster_id": 420, + "cluster_id": 2212, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 420 + "id": 2212 }, { "type": 1, - "geometry": [[123, 152]], + "geometry": [ + [ + 123, + 152 + ] + ], "tags": { "scalerank": 3, "name": "Cape Churchill", @@ -94,40 +135,60 @@ }, { "type": 1, - "geometry": [[162, 345]], + "geometry": [ + [ + 162, + 345 + ] + ], "tags": { "cluster": true, - "cluster_id": 581, + "cluster_id": 3493, "point_count": 3, "point_count_abbreviated": 3 }, - "id": 581 + "id": 3493 }, { "type": 1, - "geometry": [[236, 232]], + "geometry": [ + [ + 236, + 232 + ] + ], "tags": { "cluster": true, - "cluster_id": 580, + "cluster_id": 3492, "point_count": 4, "point_count_abbreviated": 4 }, - "id": 580 + "id": 3492 }, { "type": 1, - "geometry": [[259, 193]], + "geometry": [ + [ + 259, + 193 + ] + ], "tags": { "cluster": true, - "cluster_id": 644, + "cluster_id": 4004, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 644 + "id": 4004 }, { "type": 1, - "geometry": [[80, 336]], + "geometry": [ + [ + 80, + 336 + ] + ], "tags": { "scalerank": 3, "name": "Oceanic pole of inaccessibility", @@ -142,7 +203,12 @@ }, { "type": 1, - "geometry": [[452, 377]], + "geometry": [ + [ + 452, + 377 + ] + ], "tags": { "scalerank": 3, "name": "South Magnetic Pole 2005 (est)", @@ -157,7 +223,12 @@ }, { "type": 1, - "geometry": [[93, 32]], + "geometry": [ + [ + 93, + 32 + ] + ], "tags": { "scalerank": 3, "name": "North Magnetic Pole 2005 (est)", @@ -172,7 +243,12 @@ }, { "type": 1, - "geometry": [[159, 84]], + "geometry": [ + [ + 159, + 84 + ] + ], "tags": { "scalerank": 4, "name": "Cape York", @@ -187,29 +263,44 @@ }, { "type": 1, - "geometry": [[220, 147]], + "geometry": [ + [ + 220, + 147 + ] + ], "tags": { "cluster": true, - "cluster_id": 836, + "cluster_id": 5540, "point_count": 3, "point_count_abbreviated": 3 }, - "id": 836 + "id": 5540 }, { "type": 1, - "geometry": [[27, 270]], + "geometry": [ + [ + 27, + 270 + ] + ], "tags": { "cluster": true, - "cluster_id": 900, + "cluster_id": 6052, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 900 + "id": 6052 }, { "type": 1, - "geometry": [[100, 296]], + "geometry": [ + [ + 100, + 296 + ] + ], "tags": { "scalerank": 4, "name": "I. de Pascua", @@ -224,7 +315,12 @@ }, { "type": 1, - "geometry": [[401, 226]], + "geometry": [ + [ + 401, + 226 + ] + ], "tags": { "scalerank": 4, "name": "Plain of Jars", @@ -239,51 +335,76 @@ }, { "type": 1, - "geometry": [[26, 115]], + "geometry": [ + [ + 26, + 115 + ] + ], "tags": { "cluster": true, - "cluster_id": 1157, + "cluster_id": 8101, "point_count": 2, "point_count_abbreviated": 2 }, - "id": 1157 + "id": 8101 }, { "type": 1, - "geometry": [[449, 304]], + "geometry": [ + [ + 449, + 304 + ] + ], "tags": { "cluster": true, - "cluster_id": 1124, + "cluster_id": 7844, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 1124 + "id": 7844 }, { "type": 1, - "geometry": [[455, 272]], + "geometry": [ + [ + 455, + 272 + ] + ], "tags": { "cluster": true, - "cluster_id": 1188, + "cluster_id": 8356, "point_count": 5, "point_count_abbreviated": 5 }, - "id": 1188 + "id": 8356 }, { "type": 1, - "geometry": [[227, 121]], + "geometry": [ + [ + 227, + 121 + ] + ], "tags": { "cluster": true, - "cluster_id": 1701, + "cluster_id": 12453, "point_count": 2, "point_count_abbreviated": 2 }, - "id": 1701 + "id": 12453 }, { "type": 1, - "geometry": [[210, 21]], + "geometry": [ + [ + 210, + 21 + ] + ], "tags": { "scalerank": 5, "name": "Cape Morris Jesup", @@ -298,29 +419,44 @@ }, { "type": 1, - "geometry": [[484, 235]], + "geometry": [ + [ + 484, + 235 + ] + ], "tags": { "cluster": true, - "cluster_id": 1380, + "cluster_id": 9892, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 1380 + "id": 9892 }, { "type": 1, - "geometry": [[503, 260]], + "geometry": [ + [ + 503, + 260 + ] + ], "tags": { "cluster": true, - "cluster_id": 1925, + "cluster_id": 14245, "point_count": 4, "point_count_abbreviated": 4 }, - "id": 1925 + "id": 14245 }, { "type": 1, - "geometry": [[502, 308]], + "geometry": [ + [ + 502, + 308 + ] + ], "tags": { "scalerank": 5, "name": "Cape Reinga", @@ -336,18 +472,28 @@ }, { "type": 1, - "geometry": [[475, 165]], + "geometry": [ + [ + 475, + 165 + ] + ], "tags": { "cluster": true, - "cluster_id": 1668, + "cluster_id": 12196, "point_count": 7, "point_count_abbreviated": 7 }, - "id": 1668 + "id": 12196 }, { "type": 1, - "geometry": [[511, 142]], + "geometry": [ + [ + 511, + 142 + ] + ], "tags": { "scalerank": 5, "name": "Cape Navarin", @@ -362,7 +508,12 @@ }, { "type": 1, - "geometry": [[469, 106]], + "geometry": [ + [ + 469, + 106 + ] + ], "tags": { "scalerank": 5, "name": "Cape Lopatka", @@ -377,7 +528,12 @@ }, { "type": 1, - "geometry": [[292, 110]], + "geometry": [ + [ + 292, + 110 + ] + ], "tags": { "scalerank": 5, "name": "Nordkapp", @@ -392,40 +548,60 @@ }, { "type": 1, - "geometry": [[202, 262]], + "geometry": [ + [ + 202, + 262 + ] + ], "tags": { "cluster": true, - "cluster_id": 4134, + "cluster_id": 31910, "point_count": 2, "point_count_abbreviated": 2 }, - "id": 4134 + "id": 31910 }, { "type": 1, - "geometry": [[-28, 235]], + "geometry": [ + [ + -28, + 235 + ] + ], "tags": { "cluster": true, - "cluster_id": 1380, + "cluster_id": 9892, "point_count": 13, "point_count_abbreviated": 13 }, - "id": 1380 + "id": 9892 }, { "type": 1, - "geometry": [[-9, 260]], + "geometry": [ + [ + -9, + 260 + ] + ], "tags": { "cluster": true, - "cluster_id": 1925, + "cluster_id": 14245, "point_count": 4, "point_count_abbreviated": 4 }, - "id": 1925 + "id": 14245 }, { "type": 1, - "geometry": [[-10, 308]], + "geometry": [ + [ + -10, + 308 + ] + ], "tags": { "scalerank": 5, "name": "Cape Reinga", @@ -441,18 +617,28 @@ }, { "type": 1, - "geometry": [[-37, 165]], + "geometry": [ + [ + -37, + 165 + ] + ], "tags": { "cluster": true, - "cluster_id": 1668, + "cluster_id": 12196, "point_count": 7, "point_count_abbreviated": 7 }, - "id": 1668 + "id": 12196 }, { "type": 1, - "geometry": [[-1, 142]], + "geometry": [ + [ + -1, + 142 + ] + ], "tags": { "scalerank": 5, "name": "Cape Navarin", @@ -467,25 +653,35 @@ }, { "type": 1, - "geometry": [[539, 270]], + "geometry": [ + [ + 539, + 270 + ] + ], "tags": { "cluster": true, - "cluster_id": 900, + "cluster_id": 6052, "point_count": 6, "point_count_abbreviated": 6 }, - "id": 900 + "id": 6052 }, { "type": 1, - "geometry": [[538, 115]], + "geometry": [ + [ + 538, + 115 + ] + ], "tags": { "cluster": true, - "cluster_id": 1157, + "cluster_id": 8101, "point_count": 2, "point_count_abbreviated": 2 }, - "id": 1157 + "id": 8101 } ] -} +} \ No newline at end of file diff --git a/test/test.js b/test/test.js index f48cc76b..4876b129 100644 --- a/test/test.js +++ b/test/test.js @@ -6,6 +6,41 @@ const places = require('./fixtures/places.json'); const placesTile = require('./fixtures/places-z0-0-0.json'); const placesTileMin5 = require('./fixtures/places-z0-0-0-min5.json'); +test('Test indexing with minZoom, maxZoom and zoomStep', (t) => { + const minzoom = 10; + const zoomStep = 0.1; + const index = new Supercluster({minZoom: minzoom, maxZoom: 15, zoomStep}); + + t.same(index._zoomToIndex(minzoom), 0); + t.same(index._zoomToIndex(minzoom + 5 * zoomStep), 5); + t.same(index._zoomToIndex(minzoom + (10 * zoomStep)), 10); + t.end(); +}); + +test('Test indexing with minZoom, maxZoom and zoomStep', (t) => { + const minzoom = 10; + const zoomStep = 1; + const index = new Supercluster({minZoom: minzoom, maxZoom: 20, zoomStep}); + + + t.same(index._zoomToIndex(minzoom), 0); + t.same(index._zoomToIndex(minzoom + 5 * zoomStep), 5); + t.same(index._zoomToIndex(minzoom + 10 * zoomStep), 10); + t.end(); +}); + +test('Test indexing with minZoom, maxZoom and zoomStep', (t) => { + const minzoom = 10; + const zoomStep = 0.2; + const index = new Supercluster({minZoom: minzoom, maxZoom: 20, zoomStep}); + + + t.same(index._zoomToIndex(minzoom), 0); + t.same(index._zoomToIndex(minzoom + 5 * zoomStep), 5); + t.same(index._zoomToIndex(minzoom + 10 * zoomStep), 10); + t.end(); +}); + test('generates clusters properly', (t) => { const index = new Supercluster().load(places.features); const tile = index.getTile(0, 0, 0); @@ -69,10 +104,10 @@ test('getLeaves handles null-property features', (t) => { test('returns cluster expansion zoom', (t) => { const index = new Supercluster().load(places.features); t.same(index.getClusterExpansionZoom(164), 1); - t.same(index.getClusterExpansionZoom(196), 1); - t.same(index.getClusterExpansionZoom(581), 2); - t.same(index.getClusterExpansionZoom(1157), 2); - t.same(index.getClusterExpansionZoom(4134), 3); + t.same(index.getClusterExpansionZoom(420), 1); + t.same(index.getClusterExpansionZoom(3493), 2); + t.same(index.getClusterExpansionZoom(8101), 2); + t.same(index.getClusterExpansionZoom(31910), 3); t.end(); }); @@ -83,7 +118,7 @@ test('returns cluster expansion zoom for maxZoom', (t) => { maxZoom: 4, }).load(places.features); - t.same(index.getClusterExpansionZoom(2504), 5); + t.same(index.getClusterExpansionZoom(18856), 5); t.end(); });