@maplibre/geojson-vt 6.1.1 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"geojsonvt.d.ts","sourceRoot":"","sources":["../src/geojsonvt.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAGvE,OAAO,KAAK,EAAC,qBAAqB,EAAgD,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAE7J,eAAO,MAAM,cAAc,EAAE,gBAc5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,OAAO,CAAmB;IAElC,OAAO,CAAC,MAAM,CAAC,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAqB;gBAE1B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,gBAAgB;IA8B7D,OAAO,CAAC,eAAe;IAMvB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI;IAUzF;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO;IAqBpF;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO;IAK1B;;;;OAIG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB;IAa1E;;;;OAIG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIzD;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAIrE;;;;;;OAMG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;CAG9G"}
1
+ {"version":3,"file":"geojsonvt.d.ts","sourceRoot":"","sources":["../src/geojsonvt.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAGvE,OAAO,KAAK,EAAC,qBAAqB,EAAgD,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAE7J,eAAO,MAAM,cAAc,EAAE,gBAe5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,OAAO,CAAmB;IAElC,OAAO,CAAC,MAAM,CAAC,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAqB;gBAE1B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,gBAAgB;IA8B7D,OAAO,CAAC,eAAe;IAMvB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI;IAUzF;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO;IAqBpF;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO;IAK1B;;;;OAIG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB;IAa1E;;;;OAIG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIzD;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAIrE;;;;;;OAMG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;CAG9G"}
@@ -1 +1 @@
1
- {"version":3,"file":"simplify.d.ts","sourceRoot":"","sources":["../src/simplify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAqC1F"}
1
+ {"version":3,"file":"simplify.d.ts","sourceRoot":"","sources":["../src/simplify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAQ1F"}
@@ -23,6 +23,12 @@ export declare class TileIndex implements GeoJSONVTTileIndex {
23
23
  getLeaves(_clusterId: number, _limit?: number, _offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null;
24
24
  /** {@inheritdoc} */
25
25
  getTile(z: number, x: number, y: number): GeoJSONVTTile | null;
26
+ /**
27
+ * The features the top tile starts from: all of them in a wrapping world, where wrapping already put them inside,
28
+ * and only what lies inside the world square and its buffer with `worldCoordinates`. The index keeps the rest, so
29
+ * an update can still move a feature in from outside.
30
+ */
31
+ private clipToWorld;
26
32
  /**
27
33
  * splits features from a parent tile to sub-tiles.
28
34
  * z, x, and y are the coordinates of the parent tile
@@ -1 +1 @@
1
- {"version":3,"file":"tile-index.d.ts","sourceRoot":"","sources":["../src/tile-index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjK,qBAAa,SAAU,YAAW,kBAAkB;IAWpC,OAAO,CAAC,OAAO;IAT3B,OAAO,CAAC,UAAU,CAAkD;IACpE,OAAO,CAAC,KAAK,CAAa;IAE1B,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAAA;KAAC,CAAC;IACrD,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAM;gBAGvB,OAAO,EAAE,gBAAgB;IAO7C,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAWtD,oBAAoB;IACpB,WAAW,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA2BtH,oBAAoB;IAEpB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI1D,oBAAoB;IAEpB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAI/D,oBAAoB;IAEpB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;IAIzG,oBAAoB;IACpB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAwC9D;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS;IA4FjB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuE1B"}
1
+ {"version":3,"file":"tile-index.d.ts","sourceRoot":"","sources":["../src/tile-index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjK,qBAAa,SAAU,YAAW,kBAAkB;IAWpC,OAAO,CAAC,OAAO;IAT3B,OAAO,CAAC,UAAU,CAAkD;IACpE,OAAO,CAAC,KAAK,CAAa;IAE1B,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAAA;KAAC,CAAC;IACrD,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAM;gBAGvB,OAAO,EAAE,gBAAgB;IAO7C,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAWtD,oBAAoB;IACpB,WAAW,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA4BtH,oBAAoB;IAEpB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI1D,oBAAoB;IAEpB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAI/D,oBAAoB;IAEpB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;IAIzG,oBAAoB;IACpB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAyC9D;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAOnB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS;IA4FjB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuE1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAA+B,MAAM,eAAe,CAAC;AAG7G,wBAAgB,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,CAehH"}
1
+ {"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAA+B,MAAM,eAAe,CAAC;AAG7G,wBAAgB,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,CAiBhH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maplibre/geojson-vt",
3
- "version": "6.1.1",
3
+ "version": "6.2.0",
4
4
  "description": "Slice GeoJSON data into vector tiles efficiently",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,16 +27,16 @@
27
27
  "@eslint/js": "^10.0.1",
28
28
  "@types/benchmark": "^2.1.5",
29
29
  "@types/geojson": "^7946.0.16",
30
- "@types/node": "^26.0.1",
31
- "@vitest/coverage-v8": "^4.1.8",
30
+ "@types/node": "^26.6.5",
31
+ "@vitest/coverage-v8": "^5.0.3",
32
32
  "benchmark": "^2.1.4",
33
- "eslint": "^10.6.0",
34
- "rolldown": "^1.1.3",
35
- "tsx": "^4.22.4",
36
- "typedoc": "^0.28.19",
33
+ "eslint": "^10.12.0",
34
+ "rolldown": "^1.2.13",
35
+ "tsx": "^4.23.15",
36
+ "typedoc": "^0.28.20",
37
37
  "typescript": "^6.0.3",
38
- "typescript-eslint": "^8.62.1",
39
- "vitest": "^4.1.9"
38
+ "typescript-eslint": "^8.71.1",
39
+ "vitest": "^5.0.3"
40
40
  },
41
41
  "license": "ISC",
42
42
  "scripts": {
package/src/clip.ts CHANGED
@@ -184,13 +184,13 @@ function clipLine(geom: SliceFixedArray, newGeom: SliceArray[], start: number, e
184
184
 
185
185
  if (a < start) {
186
186
  // ---|--> | (line enters the clip region from the left)
187
- if (b > start) {
187
+ if (b >= start) {
188
188
  t = intersect(slice, ax, ay, bx, by, start);
189
189
  if (trackMetrics) slice.start = len + segLen * t;
190
190
  }
191
191
  } else if (a > end) {
192
192
  // | <--|--- (line enters the clip region from the right)
193
- if (b < end) {
193
+ if (b <= end) {
194
194
  t = intersect(slice, ax, ay, bx, by, end);
195
195
  if (trackMetrics) slice.start = len + segLen * t;
196
196
  }
@@ -203,3 +203,106 @@ test('does not throw on zero items', () => {
203
203
  expect(index.getClusters([-180, -85, 180, 85], 0)).toEqual([]);
204
204
  }).not.toThrow();
205
205
  });
206
+
207
+ test('does not keep the cluster properties of an earlier load', () => {
208
+ const index = new ClusterTileIndex({
209
+ map: (props) => ({sum: (props as {scalerank: number})?.scalerank}),
210
+ reduce: (a, b) => { (a as {sum: number}).sum += (b as {sum: number}).sum; },
211
+ radius: 100
212
+ });
213
+ index.load(places.features);
214
+ const count = index.clusterProps.length;
215
+
216
+ index.load(places.features);
217
+
218
+ expect(index.clusterProps.length).toBe(count);
219
+ });
220
+
221
+ test('takes points and the getClusters bounding box in world coordinates with worldCoordinates', () => {
222
+ const index = new ClusterTileIndex({}, true);
223
+ index.load([
224
+ {type: 'Feature', properties: {name: 'a'}, geometry: {type: 'Point', coordinates: [0.25, 0.25]}},
225
+ {type: 'Feature', properties: {name: 'b'}, geometry: {type: 'Point', coordinates: [0.75, 0.75]}}
226
+ ]);
227
+
228
+ expect(index.getClusters([0, 0, 0.5, 0.5], 0).map(f => f.geometry.coordinates)).toEqual([[0.25, 0.25]]);
229
+ });
230
+
231
+ test('leaves points outside the world out of load with worldCoordinates', () => {
232
+ const index = new ClusterTileIndex({}, true);
233
+ index.load([
234
+ {type: 'Feature', properties: {name: 'inside'}, geometry: {type: 'Point', coordinates: [0.99, 0.5]}},
235
+ {type: 'Feature', properties: {name: 'outside'}, geometry: {type: 'Point', coordinates: [1.03, 0.5]}}
236
+ ]);
237
+
238
+ expect(index.getTile(0, 0, 0).features.map(feature => feature.geometry)).toEqual([[[507, 256]]]);
239
+ });
240
+
241
+ test('returns no tile outside the world with worldCoordinates', () => {
242
+ const index = new ClusterTileIndex({}, true);
243
+ index.load([
244
+ {type: 'Feature', properties: {name: 'west'}, geometry: {type: 'Point', coordinates: [0.01, 0.5]}},
245
+ {type: 'Feature', properties: {name: 'east'}, geometry: {type: 'Point', coordinates: [0.99, 0.5]}},
246
+ {type: 'Feature', properties: {name: 'north'}, geometry: {type: 'Point', coordinates: [0.5, 0.01]}},
247
+ {type: 'Feature', properties: {name: 'south'}, geometry: {type: 'Point', coordinates: [0.5, 0.99]}}
248
+ ]);
249
+
250
+ expect([index.getTile(0, -1, 0), index.getTile(0, 1, 0), index.getTile(0, 0, -1), index.getTile(0, 0, 1)]).toEqual([null, null, null, null]);
251
+ });
252
+
253
+ test('limits minZoom and maxZoom to 30, the highest zoom level a cluster id can hold', () => {
254
+ const index = new ClusterTileIndex({minZoom: 40, maxZoom: 40});
255
+ const point: GeoJSON.Feature<GeoJSON.Point> = {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [10, 10]}};
256
+ index.load([point, point, point]);
257
+
258
+ const cluster = index.getClusters([-180, -90, 180, 90], 30)[0];
259
+ const clusterId = (cluster.properties as ClusterProperties).cluster_id;
260
+
261
+ expect(index.getLeaves(clusterId)).toHaveLength(3);
262
+ expect(index.getChildren(clusterId)).toHaveLength(3);
263
+ expect(index.getClusterExpansionZoom(clusterId)).toBe(31);
264
+ });
265
+
266
+ test('clusters each position of a MultiPoint feature', () => {
267
+ const multiPoint: GeoJSON.Feature<GeoJSON.MultiPoint> = {
268
+ type: 'Feature',
269
+ id: 7,
270
+ properties: {name: 'multi'},
271
+ geometry: {type: 'MultiPoint', coordinates: [[20, 20], [20.0001, 20], [60, 20]]}
272
+ };
273
+ const index = new ClusterTileIndex();
274
+ index.load([multiPoint]);
275
+
276
+ expect(index.getTile(0, 0, 0).features).toEqual([
277
+ {type: 1, geometry: [[284, 227]], tags: {cluster: true, cluster_id: 20, point_count: 2, point_count_abbreviated: 2}, id: 20},
278
+ {type: 1, geometry: [[341, 227]], tags: {name: 'multi'}, id: 7}
279
+ ]);
280
+ });
281
+
282
+ test('does not cluster points that are farther apart than the radius on a high zoom level', () => {
283
+ const index = new ClusterTileIndex({
284
+ maxZoom: 20,
285
+ extent: 8192,
286
+ radius: 16
287
+ });
288
+ index.load([
289
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426798, 53.943034]}},
290
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426796, 53.943034]}}
291
+ ]);
292
+
293
+ expect(index.getClusters([-180, -90, 180, 90], 20)).toHaveLength(2);
294
+ });
295
+
296
+ test('returns a cluster within a step of the mean of its points with worldCoordinates', () => {
297
+ const index = new ClusterTileIndex({}, true);
298
+ index.load([
299
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [0.54, 0.42]}},
300
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [0.54, 0.42]}},
301
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [0.56, 0.4]}}
302
+ ]);
303
+
304
+ const [x, y] = index.getClusters([0, 0, 1, 1], 0)[0].geometry.coordinates;
305
+
306
+ expect(x).toBeCloseTo(0.5466666667, 9);
307
+ expect(y).toBeCloseTo(0.4133333333, 9);
308
+ });