@maplibre/geojson-vt 6.1.2 → 6.3.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.
- package/README.md +8 -0
- package/dist/cluster-tile-index.d.ts +43 -5
- package/dist/cluster-tile-index.d.ts.map +1 -1
- package/dist/convert.d.ts +2 -9
- package/dist/convert.d.ts.map +1 -1
- package/dist/deconvert.d.ts +4 -11
- package/dist/deconvert.d.ts.map +1 -1
- package/dist/definitions.d.ts +47 -7
- package/dist/definitions.d.ts.map +1 -1
- package/dist/difference.d.ts +2 -1
- package/dist/difference.d.ts.map +1 -1
- package/dist/geojson-vt-dev.js +462 -181
- package/dist/geojson-vt.js +1 -1
- package/dist/geojson-vt.mjs +459 -180
- package/dist/geojson-vt.mjs.map +1 -1
- package/dist/geojsonvt.d.ts +8 -4
- package/dist/geojsonvt.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/tile-index.d.ts +7 -0
- package/dist/tile-index.d.ts.map +1 -1
- package/dist/world-coordinate-helper.d.ts +13 -0
- package/dist/world-coordinate-helper.d.ts.map +1 -0
- package/dist/wrap.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/clip.ts +2 -2
- package/src/cluster-tile-index.test.ts +123 -0
- package/src/cluster-tile-index.ts +270 -99
- package/src/convert.ts +49 -44
- package/src/deconvert.test.ts +4 -4
- package/src/deconvert.ts +15 -29
- package/src/definitions.ts +48 -7
- package/src/difference.test.ts +71 -0
- package/src/difference.ts +50 -21
- package/src/geojson-to-tile.ts +2 -2
- package/src/geojsonvt.test.ts +387 -1
- package/src/geojsonvt.ts +25 -10
- package/src/index.ts +2 -1
- package/src/tile-index.ts +19 -3
- package/src/world-coordinate-helper.ts +63 -0
- package/src/wrap.ts +2 -0
package/dist/geojson-vt-dev.js
CHANGED
|
@@ -134,6 +134,51 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/world-coordinate-helper.ts
|
|
139
|
+
/**
|
|
140
|
+
* Longitude and latitude, which spherical mercator puts in the world square.
|
|
141
|
+
*/
|
|
142
|
+
var MercatorWorldCoordinateHelper = class {
|
|
143
|
+
projectX(lng) {
|
|
144
|
+
return lng / 360 + .5;
|
|
145
|
+
}
|
|
146
|
+
projectY(lat) {
|
|
147
|
+
const sin = Math.sin(lat * Math.PI / 180);
|
|
148
|
+
const y = .5 - .25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
|
149
|
+
return y < 0 ? 0 : y > 1 ? 1 : y;
|
|
150
|
+
}
|
|
151
|
+
unprojectX(x) {
|
|
152
|
+
return (x - .5) * 360;
|
|
153
|
+
}
|
|
154
|
+
unprojectY(y) {
|
|
155
|
+
const y2 = (180 - y * 360) * Math.PI / 180;
|
|
156
|
+
return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Positions in the unit square already, see `coordinates` in {@link GeoJSONVTOptions}.
|
|
161
|
+
*/
|
|
162
|
+
var IdentityWorldCoordinateHelper = class {
|
|
163
|
+
projectX(x) {
|
|
164
|
+
return x;
|
|
165
|
+
}
|
|
166
|
+
projectY(y) {
|
|
167
|
+
return y;
|
|
168
|
+
}
|
|
169
|
+
unprojectX(x) {
|
|
170
|
+
return x;
|
|
171
|
+
}
|
|
172
|
+
unprojectY(y) {
|
|
173
|
+
return y;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const mercatorWorldCoordinateHelper = new MercatorWorldCoordinateHelper();
|
|
177
|
+
const identityWorldCoordinateHelper = new IdentityWorldCoordinateHelper();
|
|
178
|
+
function getWorldCoordinateHelper(coordinates) {
|
|
179
|
+
return coordinates === "unitSquare" ? identityWorldCoordinateHelper : mercatorWorldCoordinateHelper;
|
|
180
|
+
}
|
|
181
|
+
|
|
137
182
|
//#endregion
|
|
138
183
|
//#region src/convert.ts
|
|
139
184
|
const MAX_GEOMETRY_COLLECTION_DEPTH = 1024;
|
|
@@ -141,25 +186,38 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
141
186
|
* converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
|
|
142
187
|
* @param data
|
|
143
188
|
* @param options
|
|
189
|
+
* @param unlocated - the features without coordinates, by id: a feature with an id that converts to nothing is set in it, and one that converts to something is deleted from it
|
|
144
190
|
* @returns
|
|
145
191
|
*/
|
|
146
|
-
function convertToInternal(data, options) {
|
|
192
|
+
function convertToInternal(data, options, unlocated) {
|
|
147
193
|
const features = [];
|
|
148
194
|
switch (data.type) {
|
|
149
195
|
case "FeatureCollection":
|
|
150
|
-
for (let i = 0; i < data.features.length; i++)
|
|
196
|
+
for (let i = 0; i < data.features.length; i++) convertFeature(features, data.features[i], options, unlocated, i);
|
|
151
197
|
break;
|
|
152
198
|
case "Feature":
|
|
153
|
-
|
|
199
|
+
convertFeature(features, data, options, unlocated);
|
|
154
200
|
break;
|
|
155
|
-
default:
|
|
201
|
+
default: convertFeature(features, {
|
|
156
202
|
type: "Feature",
|
|
157
203
|
geometry: data,
|
|
158
204
|
properties: void 0
|
|
159
|
-
}, options);
|
|
205
|
+
}, options, unlocated);
|
|
160
206
|
}
|
|
161
207
|
return features;
|
|
162
208
|
}
|
|
209
|
+
function convertFeature(features, geojson, options, unlocated, index) {
|
|
210
|
+
const count = features.length;
|
|
211
|
+
featureToInternal(features, geojson, options, index);
|
|
212
|
+
if (!unlocated) return;
|
|
213
|
+
const id = getFeatureId(geojson, options, index);
|
|
214
|
+
if (id == null) return;
|
|
215
|
+
if (features.length > count) unlocated.delete(id);
|
|
216
|
+
else unlocated.set(id, {
|
|
217
|
+
...geojson,
|
|
218
|
+
id
|
|
219
|
+
});
|
|
220
|
+
}
|
|
163
221
|
function featureToInternal(features, geojson, options, index, depth = 0) {
|
|
164
222
|
if (!geojson.geometry) return;
|
|
165
223
|
if (depth > MAX_GEOMETRY_COLLECTION_DEPTH) throw new Error("GeometryCollection nesting exceeds supported depth: 1024");
|
|
@@ -170,24 +228,25 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
170
228
|
if (!geojson.geometry.coordinates?.length) return;
|
|
171
229
|
const id = getFeatureId(geojson, options, index);
|
|
172
230
|
const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
|
|
231
|
+
const worldCoordinateHelper = getWorldCoordinateHelper(options.coordinates);
|
|
173
232
|
switch (geojson.geometry.type) {
|
|
174
233
|
case "Point":
|
|
175
|
-
convertPointFeature(features, id, geojson.geometry, geojson.properties);
|
|
234
|
+
convertPointFeature(features, id, geojson.geometry, worldCoordinateHelper, geojson.properties);
|
|
176
235
|
return;
|
|
177
236
|
case "MultiPoint":
|
|
178
|
-
convertMultiPointFeature(features, id, geojson.geometry, geojson.properties);
|
|
237
|
+
convertMultiPointFeature(features, id, geojson.geometry, worldCoordinateHelper, geojson.properties);
|
|
179
238
|
return;
|
|
180
239
|
case "LineString":
|
|
181
|
-
convertLineStringFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
|
240
|
+
convertLineStringFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
182
241
|
return;
|
|
183
242
|
case "MultiLineString":
|
|
184
|
-
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, options, geojson.properties);
|
|
243
|
+
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, options, geojson.properties);
|
|
185
244
|
return;
|
|
186
245
|
case "Polygon":
|
|
187
|
-
convertPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
|
246
|
+
convertPolygonFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
188
247
|
return;
|
|
189
248
|
case "MultiPolygon":
|
|
190
|
-
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
|
249
|
+
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
191
250
|
return;
|
|
192
251
|
default: throw new Error("Input data is not a valid GeoJSON object.");
|
|
193
252
|
}
|
|
@@ -205,53 +264,55 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
205
264
|
properties: geojson.properties
|
|
206
265
|
}, options, index, depth);
|
|
207
266
|
}
|
|
208
|
-
function convertPointFeature(features, id, geom, properties) {
|
|
267
|
+
function convertPointFeature(features, id, geom, worldCoordinateHelper, properties) {
|
|
209
268
|
const out = [];
|
|
210
|
-
out.push(projectX(geom.coordinates[0]), projectY(geom.coordinates[1]), 0);
|
|
269
|
+
out.push(worldCoordinateHelper.projectX(geom.coordinates[0]), worldCoordinateHelper.projectY(geom.coordinates[1]), 0);
|
|
211
270
|
features.push(createFeature(id, "Point", out, properties));
|
|
212
271
|
}
|
|
213
|
-
function convertMultiPointFeature(features, id, geom, properties) {
|
|
272
|
+
function convertMultiPointFeature(features, id, geom, worldCoordinateHelper, properties) {
|
|
214
273
|
const out = [];
|
|
215
|
-
for (const coords of geom.coordinates) out.push(projectX(coords[0]), projectY(coords[1]), 0);
|
|
274
|
+
for (const coords of geom.coordinates) out.push(worldCoordinateHelper.projectX(coords[0]), worldCoordinateHelper.projectY(coords[1]), 0);
|
|
216
275
|
features.push(createFeature(id, "MultiPoint", out, properties));
|
|
217
276
|
}
|
|
218
|
-
function convertLineStringFeature(features, id, geom, tolerance, properties) {
|
|
277
|
+
function convertLineStringFeature(features, id, geom, tolerance, worldCoordinateHelper, properties) {
|
|
219
278
|
const out = { points: [] };
|
|
220
|
-
convertLine(geom.coordinates, out, tolerance, false);
|
|
279
|
+
convertLine(geom.coordinates, out, tolerance, false, worldCoordinateHelper);
|
|
221
280
|
features.push(createFeature(id, "LineString", out, properties));
|
|
222
281
|
}
|
|
223
|
-
function convertMultiLineStringFeature(features, id, geom, tolerance, options, properties) {
|
|
282
|
+
function convertMultiLineStringFeature(features, id, geom, tolerance, worldCoordinateHelper, options, properties) {
|
|
224
283
|
if (options.lineMetrics) for (const line of geom.coordinates) {
|
|
225
284
|
const out = { points: [] };
|
|
226
|
-
convertLine(line, out, tolerance, false);
|
|
285
|
+
convertLine(line, out, tolerance, false, worldCoordinateHelper);
|
|
227
286
|
features.push(createFeature(id, "LineString", out, properties));
|
|
228
287
|
}
|
|
229
288
|
else {
|
|
230
289
|
const out = [];
|
|
231
|
-
convertLines(geom.coordinates, out, tolerance, false);
|
|
290
|
+
convertLines(geom.coordinates, out, tolerance, false, worldCoordinateHelper);
|
|
232
291
|
features.push(createFeature(id, "MultiLineString", out, properties));
|
|
233
292
|
}
|
|
234
293
|
}
|
|
235
|
-
function convertPolygonFeature(features, id, geom, tolerance, properties) {
|
|
294
|
+
function convertPolygonFeature(features, id, geom, tolerance, worldCoordinateHelper, properties) {
|
|
236
295
|
const out = [];
|
|
237
|
-
convertLines(geom.coordinates, out, tolerance, true);
|
|
296
|
+
convertLines(geom.coordinates, out, tolerance, true, worldCoordinateHelper);
|
|
238
297
|
features.push(createFeature(id, "Polygon", out, properties));
|
|
239
298
|
}
|
|
240
|
-
function convertMultiPolygonFeature(features, id, geom, tolerance, properties) {
|
|
299
|
+
function convertMultiPolygonFeature(features, id, geom, tolerance, worldCoordinateHelper, properties) {
|
|
241
300
|
const out = [];
|
|
242
301
|
for (const polygon of geom.coordinates) {
|
|
243
302
|
const polygonOut = [];
|
|
244
|
-
convertLines(polygon, polygonOut, tolerance, true);
|
|
303
|
+
convertLines(polygon, polygonOut, tolerance, true, worldCoordinateHelper);
|
|
304
|
+
if (!polygonOut.length) continue;
|
|
245
305
|
out.push(polygonOut);
|
|
246
306
|
}
|
|
307
|
+
if (!out.length) return;
|
|
247
308
|
features.push(createFeature(id, "MultiPolygon", out, properties));
|
|
248
309
|
}
|
|
249
|
-
function convertLine(ring, out, tolerance, isPolygon) {
|
|
310
|
+
function convertLine(ring, out, tolerance, isPolygon, worldCoordinateHelper) {
|
|
250
311
|
let x0, y0;
|
|
251
312
|
let size = 0;
|
|
252
313
|
for (let j = 0; j < ring.length; j++) {
|
|
253
|
-
const x = projectX(ring[j][0]);
|
|
254
|
-
const y = projectY(ring[j][1]);
|
|
314
|
+
const x = worldCoordinateHelper.projectX(ring[j][0]);
|
|
315
|
+
const y = worldCoordinateHelper.projectY(ring[j][1]);
|
|
255
316
|
out.points.push(x, y, 0);
|
|
256
317
|
if (j > 0) {
|
|
257
318
|
if (isPolygon) size += (x0 * y - x * y0) / 2;
|
|
@@ -269,46 +330,32 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
269
330
|
out.start = 0;
|
|
270
331
|
out.end = out.size;
|
|
271
332
|
}
|
|
272
|
-
function convertLines(rings, out, tolerance, isPolygon) {
|
|
333
|
+
function convertLines(rings, out, tolerance, isPolygon, worldCoordinateHelper) {
|
|
273
334
|
for (let i = 0; i < rings.length; i++) {
|
|
274
335
|
const geom = { points: [] };
|
|
275
|
-
convertLine(rings[i], geom, tolerance, isPolygon);
|
|
336
|
+
convertLine(rings[i], geom, tolerance, isPolygon, worldCoordinateHelper);
|
|
276
337
|
out.push(geom);
|
|
277
338
|
}
|
|
278
339
|
}
|
|
279
|
-
/**
|
|
280
|
-
* Convert longitude to spherical mercator in [0..1] range
|
|
281
|
-
*/
|
|
282
|
-
function projectX(x) {
|
|
283
|
-
return x / 360 + .5;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Convert latitude to spherical mercator in [0..1] range
|
|
287
|
-
*/
|
|
288
|
-
function projectY(y) {
|
|
289
|
-
const sin = Math.sin(y * Math.PI / 180);
|
|
290
|
-
const y2 = .5 - .25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
|
291
|
-
return y2 < 0 ? 0 : y2 > 1 ? 1 : y2;
|
|
292
|
-
}
|
|
293
340
|
|
|
294
341
|
//#endregion
|
|
295
342
|
//#region src/deconvert.ts
|
|
296
343
|
/**
|
|
297
344
|
* Converts internal source features back to GeoJSON format.
|
|
298
345
|
*/
|
|
299
|
-
function convertToGeoJSON(source) {
|
|
346
|
+
function convertToGeoJSON(source, worldCoordinateHelper = mercatorWorldCoordinateHelper) {
|
|
300
347
|
return {
|
|
301
348
|
type: "FeatureCollection",
|
|
302
|
-
features: source.map((feature) => featureToGeoJSON(feature))
|
|
349
|
+
features: source.map((feature) => featureToGeoJSON(feature, worldCoordinateHelper))
|
|
303
350
|
};
|
|
304
351
|
}
|
|
305
352
|
/**
|
|
306
353
|
* Converts a single internal feature to GeoJSON format.
|
|
307
354
|
*/
|
|
308
|
-
function featureToGeoJSON(feature) {
|
|
355
|
+
function featureToGeoJSON(feature, worldCoordinateHelper = mercatorWorldCoordinateHelper) {
|
|
309
356
|
const geojsonFeature = {
|
|
310
357
|
type: "Feature",
|
|
311
|
-
geometry: geometryToGeoJSON(feature),
|
|
358
|
+
geometry: geometryToGeoJSON(feature, worldCoordinateHelper),
|
|
312
359
|
properties: feature.tags
|
|
313
360
|
};
|
|
314
361
|
if (feature.id != null) geojsonFeature.id = feature.id;
|
|
@@ -317,52 +364,39 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
317
364
|
/**
|
|
318
365
|
* Converts a single internal feature geometry to GeoJSON format.
|
|
319
366
|
*/
|
|
320
|
-
function geometryToGeoJSON(feature) {
|
|
367
|
+
function geometryToGeoJSON(feature, worldCoordinateHelper) {
|
|
321
368
|
const { type, geometry } = feature;
|
|
322
369
|
switch (type) {
|
|
323
370
|
case "Point": return {
|
|
324
371
|
type,
|
|
325
|
-
coordinates: unprojectPoint(geometry[0], geometry[1])
|
|
372
|
+
coordinates: unprojectPoint(geometry[0], geometry[1], worldCoordinateHelper)
|
|
326
373
|
};
|
|
327
374
|
case "MultiPoint": return {
|
|
328
375
|
type,
|
|
329
|
-
coordinates: unprojectPoints(geometry)
|
|
376
|
+
coordinates: unprojectPoints(geometry, worldCoordinateHelper)
|
|
330
377
|
};
|
|
331
378
|
case "LineString": return {
|
|
332
379
|
type,
|
|
333
|
-
coordinates: unprojectPoints(geometry.points)
|
|
380
|
+
coordinates: unprojectPoints(geometry.points, worldCoordinateHelper)
|
|
334
381
|
};
|
|
335
382
|
case "MultiLineString":
|
|
336
383
|
case "Polygon": return {
|
|
337
384
|
type,
|
|
338
|
-
coordinates: geometry.map((ring) => unprojectPoints(ring.points))
|
|
385
|
+
coordinates: geometry.map((ring) => unprojectPoints(ring.points, worldCoordinateHelper))
|
|
339
386
|
};
|
|
340
387
|
case "MultiPolygon": return {
|
|
341
388
|
type,
|
|
342
|
-
coordinates: geometry.map((polygon) => polygon.map((ring) => unprojectPoints(ring.points)))
|
|
389
|
+
coordinates: geometry.map((polygon) => polygon.map((ring) => unprojectPoints(ring.points, worldCoordinateHelper)))
|
|
343
390
|
};
|
|
344
391
|
}
|
|
345
392
|
}
|
|
346
|
-
function unprojectPoints(coords) {
|
|
393
|
+
function unprojectPoints(coords, worldCoordinateHelper = mercatorWorldCoordinateHelper) {
|
|
347
394
|
const result = [];
|
|
348
|
-
for (let i = 0; i < coords.length; i += 3) result.push(unprojectPoint(coords[i], coords[i + 1]));
|
|
395
|
+
for (let i = 0; i < coords.length; i += 3) result.push(unprojectPoint(coords[i], coords[i + 1], worldCoordinateHelper));
|
|
349
396
|
return result;
|
|
350
397
|
}
|
|
351
|
-
function unprojectPoint(x, y) {
|
|
352
|
-
return [unprojectX(x), unprojectY(y)];
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Convert spherical mercator in [0..1] range to longitude
|
|
356
|
-
*/
|
|
357
|
-
function unprojectX(x) {
|
|
358
|
-
return (x - .5) * 360;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Convert spherical mercator in [0..1] range to latitude
|
|
362
|
-
*/
|
|
363
|
-
function unprojectY(y) {
|
|
364
|
-
const y2 = (180 - y * 360) * Math.PI / 180;
|
|
365
|
-
return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
|
|
398
|
+
function unprojectPoint(x, y, worldCoordinateHelper) {
|
|
399
|
+
return [worldCoordinateHelper.unprojectX(x), worldCoordinateHelper.unprojectY(y)];
|
|
366
400
|
}
|
|
367
401
|
|
|
368
402
|
//#endregion
|
|
@@ -488,12 +522,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
488
522
|
let exited = false;
|
|
489
523
|
if (trackMetrics) segLen = Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2));
|
|
490
524
|
if (a < start) {
|
|
491
|
-
if (b
|
|
525
|
+
if (b >= start) {
|
|
492
526
|
t = intersect(slice, ax, ay, bx, by, start);
|
|
493
527
|
if (trackMetrics) slice.start = len + segLen * t;
|
|
494
528
|
}
|
|
495
529
|
} else if (a > end) {
|
|
496
|
-
if (b
|
|
530
|
+
if (b <= end) {
|
|
497
531
|
t = intersect(slice, ax, ay, bx, by, end);
|
|
498
532
|
if (trackMetrics) slice.start = len + segLen * t;
|
|
499
533
|
}
|
|
@@ -554,6 +588,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
554
588
|
//#endregion
|
|
555
589
|
//#region src/wrap.ts
|
|
556
590
|
function wrap(features, options) {
|
|
591
|
+
if (!options.wrap) return features;
|
|
557
592
|
const buffer = options.buffer / options.extent;
|
|
558
593
|
let merged = features;
|
|
559
594
|
const left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2, options);
|
|
@@ -624,16 +659,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
624
659
|
* @param source
|
|
625
660
|
* @param dataDiff
|
|
626
661
|
* @param options
|
|
662
|
+
* @param unlocated - the features without coordinates, by id, so that an update can give them some
|
|
627
663
|
* @returns
|
|
628
664
|
*/
|
|
629
|
-
function applySourceDiff(source, dataDiff, options) {
|
|
665
|
+
function applySourceDiff(source, dataDiff, options, unlocated) {
|
|
630
666
|
const diff = diffToHashed(dataDiff, options);
|
|
631
667
|
let affected = [];
|
|
632
668
|
if (diff.removeAll) {
|
|
633
669
|
affected = source;
|
|
634
670
|
source = [];
|
|
671
|
+
unlocated?.clear();
|
|
635
672
|
}
|
|
636
673
|
if (diff.remove.size || diff.add.size) {
|
|
674
|
+
for (const id of diff.remove) unlocated?.delete(id);
|
|
637
675
|
const removeFeatures = [];
|
|
638
676
|
for (const feature of source) if (diff.remove.has(feature.id) || diff.add.has(feature.id)) removeFeatures.push(feature);
|
|
639
677
|
if (removeFeatures.length) {
|
|
@@ -645,7 +683,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
645
683
|
let addFeatures = convertToInternal({
|
|
646
684
|
type: "FeatureCollection",
|
|
647
685
|
features: Array.from(diff.add.values())
|
|
648
|
-
}, options);
|
|
686
|
+
}, options, unlocated);
|
|
649
687
|
addFeatures = wrap(addFeatures, options);
|
|
650
688
|
affected = affected.concat(addFeatures);
|
|
651
689
|
source = source.concat(addFeatures);
|
|
@@ -658,8 +696,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
658
696
|
else keepFeatures.push(feature);
|
|
659
697
|
for (const [id, update] of diff.update) {
|
|
660
698
|
const oldFeatures = oldFeaturesMap.get(id);
|
|
661
|
-
if (!oldFeatures || oldFeatures.length === 0)
|
|
662
|
-
|
|
699
|
+
if (!oldFeatures || oldFeatures.length === 0) {
|
|
700
|
+
if (!unlocated?.has(id)) continue;
|
|
701
|
+
const locatedFeatures = getUpdatedUnlocatedFeatures(id, update, options, unlocated);
|
|
702
|
+
affected = affected.concat(locatedFeatures);
|
|
703
|
+
keepFeatures = keepFeatures.concat(locatedFeatures);
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
const updatedFeatures = getUpdatedFeatures(oldFeatures, update, options, unlocated);
|
|
663
707
|
affected = affected.concat(oldFeatures, updatedFeatures);
|
|
664
708
|
keepFeatures = keepFeatures.concat(updatedFeatures);
|
|
665
709
|
}
|
|
@@ -675,25 +719,16 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
675
719
|
* @param vtFeatures - the original features
|
|
676
720
|
* @param update - the update object to apply
|
|
677
721
|
* @param options - the options to use for the wrap method
|
|
678
|
-
* @
|
|
722
|
+
* @param unlocated - the features without coordinates, by id, which a new geometry without coordinates adds the feature to
|
|
723
|
+
* @returns Updated features. If geometry is updated, returns new feature(s) converted from geojson and wrapped, with the id the feature already has. If only properties are updated, returns feature(s) with tags updated.
|
|
679
724
|
*/
|
|
680
|
-
function getUpdatedFeatures(vtFeatures, update, options) {
|
|
725
|
+
function getUpdatedFeatures(vtFeatures, update, options, unlocated) {
|
|
681
726
|
const changeGeometry = !!update.newGeometry;
|
|
682
|
-
const changeProps = update
|
|
727
|
+
const changeProps = hasPropertyChanges(update);
|
|
683
728
|
if (changeGeometry) {
|
|
684
729
|
const vtFeature = vtFeatures[0];
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
id: vtFeature.id,
|
|
688
|
-
geometry: update.newGeometry,
|
|
689
|
-
properties: changeProps ? applyPropertyUpdates(vtFeature.tags, update) : vtFeature.tags
|
|
690
|
-
};
|
|
691
|
-
let features = convertToInternal({
|
|
692
|
-
type: "FeatureCollection",
|
|
693
|
-
features: [geojsonFeature]
|
|
694
|
-
}, options);
|
|
695
|
-
features = wrap(features, options);
|
|
696
|
-
return features;
|
|
730
|
+
const properties = changeProps ? applyPropertyUpdates(vtFeature.tags, update) : vtFeature.tags;
|
|
731
|
+
return convertNewGeometry(vtFeature.id, update.newGeometry, properties, options, unlocated);
|
|
697
732
|
}
|
|
698
733
|
if (changeProps) {
|
|
699
734
|
const updated = [];
|
|
@@ -707,6 +742,44 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
707
742
|
return vtFeatures;
|
|
708
743
|
}
|
|
709
744
|
/**
|
|
745
|
+
* Gets the features an update gives a feature without coordinates: none, unless its new geometry has some.
|
|
746
|
+
* Its property changes apply either way.
|
|
747
|
+
*/
|
|
748
|
+
function getUpdatedUnlocatedFeatures(id, update, options, unlocated) {
|
|
749
|
+
const feature = unlocated.get(id);
|
|
750
|
+
const properties = hasPropertyChanges(update) ? applyPropertyUpdates(feature.properties, update) : feature.properties;
|
|
751
|
+
if (!update.newGeometry) {
|
|
752
|
+
unlocated.set(id, {
|
|
753
|
+
...feature,
|
|
754
|
+
properties
|
|
755
|
+
});
|
|
756
|
+
return [];
|
|
757
|
+
}
|
|
758
|
+
return convertNewGeometry(id, update.newGeometry, properties, options, unlocated);
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Converts the new geometry of a feature to internal format, keeping the id the feature already has
|
|
762
|
+
*/
|
|
763
|
+
function convertNewGeometry(id, geometry, properties, options, unlocated) {
|
|
764
|
+
const features = convertToInternal({
|
|
765
|
+
type: "FeatureCollection",
|
|
766
|
+
features: [{
|
|
767
|
+
type: "Feature",
|
|
768
|
+
id,
|
|
769
|
+
geometry,
|
|
770
|
+
properties
|
|
771
|
+
}]
|
|
772
|
+
}, {
|
|
773
|
+
...options,
|
|
774
|
+
promoteId: void 0,
|
|
775
|
+
generateId: false
|
|
776
|
+
}, unlocated);
|
|
777
|
+
return wrap(features, options);
|
|
778
|
+
}
|
|
779
|
+
function hasPropertyChanges(update) {
|
|
780
|
+
return update.removeAllProperties || update.removeProperties?.length > 0 || update.addOrUpdateProperties?.length > 0;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
710
783
|
* helper to apply property updates from a diff update object to a properties object
|
|
711
784
|
*/
|
|
712
785
|
function applyPropertyUpdates(tags, update) {
|
|
@@ -728,7 +801,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
728
801
|
return {
|
|
729
802
|
removeAll: diff.removeAll,
|
|
730
803
|
remove: new Set(diff.remove || []),
|
|
731
|
-
add: new Map(diff.add?.map((feature) => [options.promoteId ? feature.properties[options.promoteId] : feature.id, feature])),
|
|
804
|
+
add: new Map(diff.add?.map((feature) => [options.promoteId ? feature.properties?.[options.promoteId] : feature.id, feature])),
|
|
732
805
|
update: new Map(diff.update?.map((update) => [update.id, update]))
|
|
733
806
|
};
|
|
734
807
|
}
|
|
@@ -1031,54 +1104,105 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1031
1104
|
log: false,
|
|
1032
1105
|
generateId: false,
|
|
1033
1106
|
reduce: null,
|
|
1034
|
-
map: (props) => props
|
|
1107
|
+
map: (props) => props,
|
|
1108
|
+
coordinates: "lngLat",
|
|
1109
|
+
wrap: true
|
|
1035
1110
|
};
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1111
|
+
/**
|
|
1112
|
+
* The highest zoom level to cluster on. A cluster id keeps its zoom level in five bits.
|
|
1113
|
+
*/
|
|
1114
|
+
const MAX_ZOOM = 30;
|
|
1115
|
+
/**
|
|
1116
|
+
* Projected coordinates, which are 0 to 1 inside the world, are stored as integers with this many steps per world,
|
|
1117
|
+
* so that they take half the memory of doubles. One step is ~4 cm at the equator.
|
|
1118
|
+
*/
|
|
1119
|
+
const SCALE = 1073741824;
|
|
1120
|
+
/**
|
|
1121
|
+
* The largest value a 32-bit integer holds.
|
|
1122
|
+
*/
|
|
1123
|
+
const MAX_INTEGER = 2147483647;
|
|
1124
|
+
/**
|
|
1125
|
+
* A projected coordinate in steps from the center of the world, see `SCALE`.
|
|
1126
|
+
*/
|
|
1127
|
+
function encode(coord) {
|
|
1128
|
+
return (coord - .5) * SCALE;
|
|
1129
|
+
}
|
|
1130
|
+
function decode(value) {
|
|
1131
|
+
return value / SCALE + .5;
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Whether a projected coordinate fits in a 32-bit integer. One that is more than two worlds from the center of the
|
|
1135
|
+
* world does not, and neither does one that is not a number.
|
|
1136
|
+
*/
|
|
1137
|
+
function fitsInteger(coord) {
|
|
1138
|
+
return Math.abs(encode(coord)) < MAX_INTEGER;
|
|
1139
|
+
}
|
|
1140
|
+
const OFFSET_ZOOM = 0;
|
|
1141
|
+
const OFFSET_ID = 1;
|
|
1142
|
+
const OFFSET_PARENT = 2;
|
|
1143
|
+
const OFFSET_NUM = 3;
|
|
1144
|
+
const OFFSET_PROP = 4;
|
|
1041
1145
|
/**
|
|
1042
1146
|
* This class allow clustering of geojson points.
|
|
1043
1147
|
*/
|
|
1044
1148
|
var ClusterTileIndex = class {
|
|
1045
1149
|
constructor(options) {
|
|
1046
|
-
this.options =
|
|
1150
|
+
this.options = createOptions(options);
|
|
1151
|
+
this.worldCoordinateHelper = getWorldCoordinateHelper(this.options.coordinates);
|
|
1047
1152
|
this.trees = new Array(this.options.maxZoom + 1);
|
|
1048
|
-
this.stride = this.options.reduce ?
|
|
1153
|
+
this.stride = this.options.reduce ? 5 : 4;
|
|
1049
1154
|
this.clusterProps = [];
|
|
1050
1155
|
this.points = [];
|
|
1051
1156
|
}
|
|
1052
1157
|
/**
|
|
1053
|
-
* Loads GeoJSON
|
|
1054
|
-
*
|
|
1158
|
+
* Loads GeoJSON Point and MultiPoint features and builds the internal clustering index.
|
|
1159
|
+
* Each position of a MultiPoint is clustered as a point of its own, with the id and properties of its feature.
|
|
1160
|
+
* @param points - GeoJSON Point and MultiPoint features to cluster.
|
|
1055
1161
|
*/
|
|
1056
1162
|
load(points) {
|
|
1057
1163
|
const features = [];
|
|
1058
1164
|
for (const point of points) {
|
|
1059
1165
|
if (!point.geometry) continue;
|
|
1060
|
-
const
|
|
1061
|
-
const [
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1166
|
+
const positions = point.geometry.type === "MultiPoint" ? point.geometry.coordinates : [point.geometry.coordinates];
|
|
1167
|
+
for (const [lng, lat] of positions) {
|
|
1168
|
+
const [x, y] = [this.worldCoordinateHelper.projectX(lng), this.worldCoordinateHelper.projectY(lat)];
|
|
1169
|
+
if (!this.options.wrap && !isInWorld([x, y])) continue;
|
|
1170
|
+
const feature = {
|
|
1171
|
+
id: point.id,
|
|
1172
|
+
type: "Point",
|
|
1173
|
+
geometry: [x, y],
|
|
1174
|
+
tags: point.properties
|
|
1175
|
+
};
|
|
1176
|
+
features.push(feature);
|
|
1177
|
+
}
|
|
1069
1178
|
}
|
|
1070
1179
|
this.createIndex(features);
|
|
1071
1180
|
}
|
|
1072
1181
|
/**
|
|
1073
1182
|
* @internal
|
|
1074
1183
|
* Loads internal GeoJSONVT point features from a data source and builds the clustering index.
|
|
1184
|
+
* Each position of a MultiPoint feature becomes a point of its own, with the id and properties of its feature.
|
|
1075
1185
|
* @param features - {@link GeoJSONVTInternalFeature} data source features to filter and cluster.
|
|
1076
1186
|
*/
|
|
1077
1187
|
initialize(features) {
|
|
1078
1188
|
const points = [];
|
|
1079
1189
|
for (const feature of features) {
|
|
1080
|
-
if (feature.type
|
|
1081
|
-
|
|
1190
|
+
if (feature.type === "Point") {
|
|
1191
|
+
if (!this.options.wrap && !isInWorld(feature.geometry)) continue;
|
|
1192
|
+
points.push(feature);
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
if (feature.type !== "MultiPoint") continue;
|
|
1196
|
+
for (let i = 0; i < feature.geometry.length; i += 3) {
|
|
1197
|
+
const geometry = [feature.geometry[i], feature.geometry[i + 1]];
|
|
1198
|
+
if (!this.options.wrap && !isInWorld(geometry)) continue;
|
|
1199
|
+
points.push({
|
|
1200
|
+
id: feature.id,
|
|
1201
|
+
type: "Point",
|
|
1202
|
+
geometry,
|
|
1203
|
+
tags: feature.tags
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1082
1206
|
}
|
|
1083
1207
|
this.createIndex(points);
|
|
1084
1208
|
}
|
|
@@ -1088,41 +1212,113 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1088
1212
|
* @param features
|
|
1089
1213
|
*/
|
|
1090
1214
|
updateIndex(features, _affected, options) {
|
|
1091
|
-
this.options =
|
|
1215
|
+
this.options = createOptions({
|
|
1216
|
+
...options.clusterOptions,
|
|
1217
|
+
coordinates: this.options.coordinates,
|
|
1218
|
+
wrap: this.options.wrap
|
|
1219
|
+
});
|
|
1092
1220
|
this.initialize(features);
|
|
1093
1221
|
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Builds a tree per zoom level from the points. A point whose position does not fit in an integer is left out,
|
|
1224
|
+
* see `fitsInteger`.
|
|
1225
|
+
*/
|
|
1094
1226
|
createIndex(points) {
|
|
1095
1227
|
const { log, minZoom, maxZoom } = this.options;
|
|
1228
|
+
this.trees = new Array(maxZoom + 1);
|
|
1229
|
+
this.stride = this.options.reduce ? 5 : 4;
|
|
1230
|
+
this.clusterProps = [];
|
|
1096
1231
|
if (log) console.time("total time");
|
|
1097
1232
|
const timerId = `prepare ${points.length} points`;
|
|
1098
1233
|
if (log) console.time(timerId);
|
|
1099
1234
|
this.points = points;
|
|
1100
|
-
const
|
|
1235
|
+
const stride = this.stride;
|
|
1236
|
+
const notProcessed = maxZoom + 1;
|
|
1237
|
+
const coords = new Int32Array(points.length * 2);
|
|
1238
|
+
const data = new Int32Array(points.length * stride);
|
|
1239
|
+
let numItems = 0;
|
|
1101
1240
|
for (let i = 0; i < points.length; i++) {
|
|
1102
1241
|
const p = points[i];
|
|
1103
|
-
if (!p?.geometry) continue;
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
data
|
|
1108
|
-
|
|
1242
|
+
if (!p?.geometry || !fitsInteger(p.geometry[0]) || !fitsInteger(p.geometry[1])) continue;
|
|
1243
|
+
coords[numItems * 2] = Math.round(encode(p.geometry[0]));
|
|
1244
|
+
coords[numItems * 2 + 1] = Math.round(encode(p.geometry[1]));
|
|
1245
|
+
const k = numItems * stride;
|
|
1246
|
+
data[k + OFFSET_ZOOM] = notProcessed;
|
|
1247
|
+
data[k + OFFSET_ID] = i;
|
|
1248
|
+
data[k + OFFSET_PARENT] = -1;
|
|
1249
|
+
data[k + OFFSET_NUM] = 1;
|
|
1250
|
+
numItems++;
|
|
1109
1251
|
}
|
|
1110
|
-
let tree = this.trees[maxZoom + 1] = this.createTree(data);
|
|
1252
|
+
let tree = this.trees[maxZoom + 1] = this.createTree(coords, data, numItems);
|
|
1111
1253
|
if (log) console.timeEnd(timerId);
|
|
1254
|
+
const nextCoords = new Int32Array(numItems * 2);
|
|
1255
|
+
const nextData = new Int32Array(numItems * stride);
|
|
1112
1256
|
for (let z = maxZoom; z >= minZoom; z--) {
|
|
1113
1257
|
const now = Date.now();
|
|
1114
|
-
|
|
1258
|
+
const nextNumItems = this.cluster(tree, z, nextCoords, nextData);
|
|
1259
|
+
if (!this.isSameAsTree(tree, nextData, nextNumItems)) tree = this.createTree(nextCoords.slice(0, nextNumItems * 2), nextData.slice(0, nextNumItems * stride), nextNumItems);
|
|
1260
|
+
this.trees[z] = tree;
|
|
1115
1261
|
if (log) console.log("z%d: %d clusters in %dms", z, tree.numItems, Date.now() - now);
|
|
1116
1262
|
}
|
|
1117
1263
|
if (log) console.timeEnd("total time");
|
|
1118
1264
|
}
|
|
1119
1265
|
/**
|
|
1120
1266
|
* Returns clusters and/or points within a bounding box at a given zoom level.
|
|
1121
|
-
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order
|
|
1267
|
+
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order, or `[minX, minY, maxX, maxY]` in the unit square with `coordinates: 'unitSquare'`.
|
|
1268
|
+
* In a world that wraps, the box can reach across the antimeridian. In one that doesn't, only its part inside the world counts.
|
|
1122
1269
|
* @param zoom - Zoom level to query.
|
|
1123
1270
|
*/
|
|
1124
1271
|
getClusters(bbox, zoom) {
|
|
1125
|
-
return this.getClustersInternal(bbox, zoom).map((f) => featureToGeoJSON(f));
|
|
1272
|
+
return (!this.options.wrap ? this.getClustersInWorld(bbox, zoom) : this.options.coordinates === "unitSquare" ? this.getClustersAcrossEdge(bbox, zoom) : this.getClustersInternal(bbox, zoom)).map((f) => featureToGeoJSON(f, this.worldCoordinateHelper));
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* The clusters and points in the part of a box inside a world that ends at its edges.
|
|
1276
|
+
*/
|
|
1277
|
+
getClustersInWorld(bbox, zoom) {
|
|
1278
|
+
const lngLat = this.options.coordinates !== "unitSquare";
|
|
1279
|
+
const y1 = this.worldCoordinateHelper.projectY(lngLat ? Math.max(-90, Math.min(90, bbox[1])) : bbox[1]);
|
|
1280
|
+
const y2 = this.worldCoordinateHelper.projectY(lngLat ? Math.max(-90, Math.min(90, bbox[3])) : bbox[3]);
|
|
1281
|
+
const minX = Math.max(0, this.worldCoordinateHelper.projectX(bbox[0]));
|
|
1282
|
+
const maxX = Math.min(1, this.worldCoordinateHelper.projectX(bbox[2]));
|
|
1283
|
+
if (minX > maxX) return [];
|
|
1284
|
+
return this.getClustersInRange([
|
|
1285
|
+
minX,
|
|
1286
|
+
Math.min(y1, y2),
|
|
1287
|
+
maxX,
|
|
1288
|
+
Math.max(y1, y2)
|
|
1289
|
+
], zoom);
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* The clusters and points in a box of a unit square that wraps, where the box can reach across its left or right edge.
|
|
1293
|
+
*/
|
|
1294
|
+
getClustersAcrossEdge([minX, minY, maxX, maxY], zoom) {
|
|
1295
|
+
if (maxX < minX) maxX += 1;
|
|
1296
|
+
if (maxX - minX >= 1) return this.getClustersInRange([
|
|
1297
|
+
0,
|
|
1298
|
+
minY,
|
|
1299
|
+
1,
|
|
1300
|
+
maxY
|
|
1301
|
+
], zoom);
|
|
1302
|
+
const shift = Math.floor(minX);
|
|
1303
|
+
const west = minX - shift;
|
|
1304
|
+
const east = maxX - shift;
|
|
1305
|
+
if (east <= 1) return this.getClustersInRange([
|
|
1306
|
+
west,
|
|
1307
|
+
minY,
|
|
1308
|
+
east,
|
|
1309
|
+
maxY
|
|
1310
|
+
], zoom);
|
|
1311
|
+
return this.getClustersInRange([
|
|
1312
|
+
west,
|
|
1313
|
+
minY,
|
|
1314
|
+
1,
|
|
1315
|
+
maxY
|
|
1316
|
+
], zoom).concat(this.getClustersInRange([
|
|
1317
|
+
0,
|
|
1318
|
+
minY,
|
|
1319
|
+
east - 1,
|
|
1320
|
+
maxY
|
|
1321
|
+
], zoom));
|
|
1126
1322
|
}
|
|
1127
1323
|
getClustersInternal(bbox, zoom) {
|
|
1128
1324
|
let minLng = ((bbox[0] + 180) % 360 + 360) % 360 - 180;
|
|
@@ -1147,13 +1343,25 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1147
1343
|
], zoom);
|
|
1148
1344
|
return easternHem.concat(westernHem);
|
|
1149
1345
|
}
|
|
1346
|
+
return this.getClustersInRange([
|
|
1347
|
+
this.worldCoordinateHelper.projectX(minLng),
|
|
1348
|
+
this.worldCoordinateHelper.projectY(maxLat),
|
|
1349
|
+
this.worldCoordinateHelper.projectX(maxLng),
|
|
1350
|
+
this.worldCoordinateHelper.projectY(minLat)
|
|
1351
|
+
], zoom);
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* The clusters and points in a box of the internal coordinates, `[minX, minY, maxX, maxY]`.
|
|
1355
|
+
*/
|
|
1356
|
+
getClustersInRange([minX, minY, maxX, maxY], zoom) {
|
|
1150
1357
|
const tree = this.trees[this.limitZoom(zoom)];
|
|
1151
|
-
const ids = tree.range(
|
|
1358
|
+
const ids = tree.range(encode(minX), encode(minY), encode(maxX), encode(maxY));
|
|
1359
|
+
const coords = tree.flatCoords;
|
|
1152
1360
|
const data = tree.flatData;
|
|
1153
1361
|
const clusters = [];
|
|
1154
1362
|
for (const id of ids) {
|
|
1155
1363
|
const k = this.stride * id;
|
|
1156
|
-
clusters.push(data[k + OFFSET_NUM] > 1 ? getClusterFeature(data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
|
|
1364
|
+
clusters.push(data[k + OFFSET_NUM] > 1 ? getClusterFeature(coords, id, data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
|
|
1157
1365
|
}
|
|
1158
1366
|
return clusters;
|
|
1159
1367
|
}
|
|
@@ -1167,16 +1375,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1167
1375
|
const clusterError = /* @__PURE__ */ new Error("No cluster with the specified id: " + clusterId);
|
|
1168
1376
|
const tree = this.trees[originZoom];
|
|
1169
1377
|
if (!tree) throw clusterError;
|
|
1378
|
+
const coords = tree.flatCoords;
|
|
1170
1379
|
const data = tree.flatData;
|
|
1171
|
-
if (originId
|
|
1172
|
-
const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1));
|
|
1173
|
-
const x =
|
|
1174
|
-
const y =
|
|
1380
|
+
if (originId >= tree.numItems) throw clusterError;
|
|
1381
|
+
const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1)) * SCALE;
|
|
1382
|
+
const x = coords[originId * 2];
|
|
1383
|
+
const y = coords[originId * 2 + 1];
|
|
1175
1384
|
const ids = tree.within(x, y, r);
|
|
1176
1385
|
const children = [];
|
|
1177
1386
|
for (const id of ids) {
|
|
1178
1387
|
const k = id * this.stride;
|
|
1179
|
-
if (data[k + OFFSET_PARENT] === clusterId) children.push(data[k + OFFSET_NUM] > 1 ? getClusterGeoJSON(data, k, this.clusterProps) : featureToGeoJSON(this.points[data[k + OFFSET_ID]]));
|
|
1388
|
+
if (data[k + OFFSET_PARENT] === clusterId) children.push(data[k + OFFSET_NUM] > 1 ? getClusterGeoJSON(coords, id, data, k, this.clusterProps, this.worldCoordinateHelper) : featureToGeoJSON(this.points[data[k + OFFSET_ID]], this.worldCoordinateHelper));
|
|
1180
1389
|
}
|
|
1181
1390
|
if (children.length === 0) throw clusterError;
|
|
1182
1391
|
return children;
|
|
@@ -1204,10 +1413,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1204
1413
|
const tree = this.trees[this.limitZoom(z)];
|
|
1205
1414
|
if (!tree) return null;
|
|
1206
1415
|
const z2 = Math.pow(2, z);
|
|
1416
|
+
if (!this.options.wrap && (x < 0 || x >= z2 || y < 0 || y >= z2)) return null;
|
|
1207
1417
|
const { extent, radius } = this.options;
|
|
1208
1418
|
const p = radius / extent;
|
|
1209
|
-
const top = (y - p) / z2;
|
|
1210
|
-
const bottom = (y + 1 + p) / z2;
|
|
1419
|
+
const top = encode((y - p) / z2);
|
|
1420
|
+
const bottom = encode((y + 1 + p) / z2);
|
|
1211
1421
|
const tile = {
|
|
1212
1422
|
transformed: true,
|
|
1213
1423
|
features: [],
|
|
@@ -1216,9 +1426,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1216
1426
|
y,
|
|
1217
1427
|
z
|
|
1218
1428
|
};
|
|
1219
|
-
this.addTileFeatures(tree.range((x - p) / z2, top, (x + 1 + p) / z2, bottom), tree
|
|
1220
|
-
if (x === 0) this.addTileFeatures(tree.range(1 - p / z2, top, 1, bottom), tree
|
|
1221
|
-
if (x === z2 - 1) this.addTileFeatures(tree.range(0, top, p / z2, bottom), tree
|
|
1429
|
+
this.addTileFeatures(tree.range(encode((x - p) / z2), top, encode((x + 1 + p) / z2), bottom), tree, x, y, z2, tile);
|
|
1430
|
+
if (this.options.wrap && x === 0) this.addTileFeatures(tree.range(encode(1 - p / z2), top, encode(1), bottom), tree, z2, y, z2, tile);
|
|
1431
|
+
if (this.options.wrap && x === z2 - 1) this.addTileFeatures(tree.range(encode(0), top, encode(p / z2), bottom), tree, -1, y, z2, tile);
|
|
1222
1432
|
return tile;
|
|
1223
1433
|
}
|
|
1224
1434
|
/**
|
|
@@ -1241,15 +1451,27 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1241
1451
|
}
|
|
1242
1452
|
return skipped;
|
|
1243
1453
|
}
|
|
1244
|
-
createTree(data) {
|
|
1245
|
-
const tree = new KDBush(
|
|
1246
|
-
for (let i = 0; i <
|
|
1454
|
+
createTree(coords, data, numItems) {
|
|
1455
|
+
const tree = new KDBush(numItems, this.options.nodeSize, Int32Array);
|
|
1456
|
+
for (let i = 0; i < numItems; i++) tree.add(coords[i * 2], coords[i * 2 + 1]);
|
|
1247
1457
|
tree.finish();
|
|
1458
|
+
tree.flatCoords = coords;
|
|
1248
1459
|
tree.flatData = data;
|
|
1249
1460
|
tree.data = null;
|
|
1250
1461
|
return tree;
|
|
1251
1462
|
}
|
|
1252
|
-
|
|
1463
|
+
/**
|
|
1464
|
+
* Whether clustering a tree left all its points and clusters as they were, in the same order.
|
|
1465
|
+
*/
|
|
1466
|
+
isSameAsTree(tree, nextData, nextNumItems) {
|
|
1467
|
+
if (nextNumItems !== tree.numItems) return false;
|
|
1468
|
+
const data = tree.flatData;
|
|
1469
|
+
for (let k = OFFSET_ID; k < nextNumItems * this.stride; k += this.stride) if (nextData[k] !== data[k]) return false;
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
addTileFeatures(ids, tree, x, y, z2, tile) {
|
|
1473
|
+
const coords = tree.flatCoords;
|
|
1474
|
+
const data = tree.flatData;
|
|
1253
1475
|
for (const i of ids) {
|
|
1254
1476
|
const k = i * this.stride;
|
|
1255
1477
|
const isCluster = data[k + OFFSET_NUM] > 1;
|
|
@@ -1258,8 +1480,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1258
1480
|
let py;
|
|
1259
1481
|
if (isCluster) {
|
|
1260
1482
|
tags = getClusterProperties(data, k, this.clusterProps);
|
|
1261
|
-
px =
|
|
1262
|
-
py =
|
|
1483
|
+
px = decode(coords[i * 2]);
|
|
1484
|
+
py = decode(coords[i * 2 + 1]);
|
|
1263
1485
|
} else {
|
|
1264
1486
|
const p = this.points[data[k + OFFSET_ID]];
|
|
1265
1487
|
tags = p.tags;
|
|
@@ -1280,22 +1502,30 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1280
1502
|
limitZoom(z) {
|
|
1281
1503
|
return Math.max(this.options.minZoom, Math.min(Math.floor(+z), this.options.maxZoom + 1));
|
|
1282
1504
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1505
|
+
/**
|
|
1506
|
+
* Clusters the points of a tree on a zoom level, writing the result to `nextCoords` and `nextData`.
|
|
1507
|
+
* @returns the number of points and clusters written
|
|
1508
|
+
*/
|
|
1509
|
+
cluster(tree, zoom, nextCoords, nextData) {
|
|
1510
|
+
const { radius, extent, reduce, minPoints, maxZoom } = this.options;
|
|
1511
|
+
const r = radius / (extent * Math.pow(2, zoom)) * SCALE;
|
|
1512
|
+
const notProcessed = maxZoom + 1;
|
|
1513
|
+
const coords = tree.flatCoords;
|
|
1286
1514
|
const data = tree.flatData;
|
|
1287
|
-
const nextData = [];
|
|
1288
1515
|
const stride = this.stride;
|
|
1289
|
-
|
|
1516
|
+
const neighborIds = new Uint32Array(tree.numItems);
|
|
1517
|
+
let nextNumItems = 0;
|
|
1518
|
+
for (let index = 0; index < tree.numItems; index++) {
|
|
1519
|
+
const i = index * stride;
|
|
1290
1520
|
if (data[i + OFFSET_ZOOM] <= zoom) continue;
|
|
1291
1521
|
data[i + OFFSET_ZOOM] = zoom;
|
|
1292
|
-
const x =
|
|
1293
|
-
const y =
|
|
1294
|
-
const
|
|
1522
|
+
const x = coords[index * 2];
|
|
1523
|
+
const y = coords[index * 2 + 1];
|
|
1524
|
+
const numNeighbors = tree.withinInto(x, y, r, neighborIds);
|
|
1295
1525
|
const numPointsOrigin = data[i + OFFSET_NUM];
|
|
1296
1526
|
let numPoints = numPointsOrigin;
|
|
1297
|
-
for (
|
|
1298
|
-
const k =
|
|
1527
|
+
for (let n = 0; n < numNeighbors; n++) {
|
|
1528
|
+
const k = neighborIds[n] * stride;
|
|
1299
1529
|
if (data[k + OFFSET_ZOOM] > zoom) numPoints += data[k + OFFSET_NUM];
|
|
1300
1530
|
}
|
|
1301
1531
|
if (numPoints > numPointsOrigin && numPoints >= minPoints) {
|
|
@@ -1303,14 +1533,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1303
1533
|
let wy = y * numPointsOrigin;
|
|
1304
1534
|
let clusterProperties;
|
|
1305
1535
|
let clusterPropIndex = -1;
|
|
1306
|
-
const id = (
|
|
1307
|
-
for (
|
|
1308
|
-
const
|
|
1536
|
+
const id = (index << 5) + (zoom + 1) + this.points.length;
|
|
1537
|
+
for (let n = 0; n < numNeighbors; n++) {
|
|
1538
|
+
const neighborIndex = neighborIds[n];
|
|
1539
|
+
const k = neighborIndex * stride;
|
|
1309
1540
|
if (data[k + OFFSET_ZOOM] <= zoom) continue;
|
|
1310
1541
|
data[k + OFFSET_ZOOM] = zoom;
|
|
1311
1542
|
const numPoints2 = data[k + OFFSET_NUM];
|
|
1312
|
-
wx +=
|
|
1313
|
-
wy +=
|
|
1543
|
+
wx += coords[neighborIndex * 2] * numPoints2;
|
|
1544
|
+
wy += coords[neighborIndex * 2 + 1] * numPoints2;
|
|
1314
1545
|
data[k + OFFSET_PARENT] = id;
|
|
1315
1546
|
if (reduce) {
|
|
1316
1547
|
if (!clusterProperties) {
|
|
@@ -1322,19 +1553,33 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1322
1553
|
}
|
|
1323
1554
|
}
|
|
1324
1555
|
data[i + OFFSET_PARENT] = id;
|
|
1325
|
-
|
|
1326
|
-
|
|
1556
|
+
nextCoords[nextNumItems * 2] = Math.round(wx / numPoints);
|
|
1557
|
+
nextCoords[nextNumItems * 2 + 1] = Math.round(wy / numPoints);
|
|
1558
|
+
const next = nextNumItems * stride;
|
|
1559
|
+
nextData[next + OFFSET_ZOOM] = notProcessed;
|
|
1560
|
+
nextData[next + OFFSET_ID] = id;
|
|
1561
|
+
nextData[next + OFFSET_PARENT] = -1;
|
|
1562
|
+
nextData[next + OFFSET_NUM] = numPoints;
|
|
1563
|
+
if (reduce) nextData[next + OFFSET_PROP] = clusterPropIndex;
|
|
1564
|
+
nextNumItems++;
|
|
1327
1565
|
} else {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1566
|
+
nextCoords[nextNumItems * 2] = x;
|
|
1567
|
+
nextCoords[nextNumItems * 2 + 1] = y;
|
|
1568
|
+
for (let j = 0; j < stride; j++) nextData[nextNumItems * stride + j] = data[i + j];
|
|
1569
|
+
nextNumItems++;
|
|
1570
|
+
if (numPoints > 1) for (let n = 0; n < numNeighbors; n++) {
|
|
1571
|
+
const neighborIndex = neighborIds[n];
|
|
1572
|
+
const k = neighborIndex * stride;
|
|
1331
1573
|
if (data[k + OFFSET_ZOOM] <= zoom) continue;
|
|
1332
1574
|
data[k + OFFSET_ZOOM] = zoom;
|
|
1333
|
-
|
|
1575
|
+
nextCoords[nextNumItems * 2] = coords[neighborIndex * 2];
|
|
1576
|
+
nextCoords[nextNumItems * 2 + 1] = coords[neighborIndex * 2 + 1];
|
|
1577
|
+
for (let j = 0; j < stride; j++) nextData[nextNumItems * stride + j] = data[k + j];
|
|
1578
|
+
nextNumItems++;
|
|
1334
1579
|
}
|
|
1335
1580
|
}
|
|
1336
1581
|
}
|
|
1337
|
-
return
|
|
1582
|
+
return nextNumItems;
|
|
1338
1583
|
}
|
|
1339
1584
|
getOriginId(clusterId) {
|
|
1340
1585
|
return clusterId - this.points.length >> 5;
|
|
@@ -1352,22 +1597,28 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1352
1597
|
return clone && result === original ? Object.assign({}, result) : result;
|
|
1353
1598
|
}
|
|
1354
1599
|
};
|
|
1355
|
-
function
|
|
1600
|
+
function createOptions(options) {
|
|
1601
|
+
const result = Object.assign(Object.create(defaultClusterOptions), options);
|
|
1602
|
+
result.minZoom = Math.min(result.minZoom, MAX_ZOOM);
|
|
1603
|
+
result.maxZoom = Math.min(result.maxZoom, MAX_ZOOM);
|
|
1604
|
+
return result;
|
|
1605
|
+
}
|
|
1606
|
+
function getClusterFeature(coords, index, data, i, clusterProps) {
|
|
1356
1607
|
return {
|
|
1357
1608
|
id: data[i + OFFSET_ID],
|
|
1358
1609
|
type: "Point",
|
|
1359
1610
|
tags: getClusterProperties(data, i, clusterProps),
|
|
1360
|
-
geometry: [
|
|
1611
|
+
geometry: [decode(coords[index * 2]), decode(coords[index * 2 + 1])]
|
|
1361
1612
|
};
|
|
1362
1613
|
}
|
|
1363
|
-
function getClusterGeoJSON(data, i, clusterProps) {
|
|
1614
|
+
function getClusterGeoJSON(coords, index, data, i, clusterProps, worldCoordinateHelper) {
|
|
1364
1615
|
return {
|
|
1365
1616
|
type: "Feature",
|
|
1366
1617
|
id: data[i + OFFSET_ID],
|
|
1367
1618
|
properties: getClusterProperties(data, i, clusterProps),
|
|
1368
1619
|
geometry: {
|
|
1369
1620
|
type: "Point",
|
|
1370
|
-
coordinates: [unprojectX(
|
|
1621
|
+
coordinates: [worldCoordinateHelper.unprojectX(decode(coords[index * 2])), worldCoordinateHelper.unprojectY(decode(coords[index * 2 + 1]))]
|
|
1371
1622
|
}
|
|
1372
1623
|
};
|
|
1373
1624
|
}
|
|
@@ -1383,6 +1634,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1383
1634
|
point_count_abbreviated: abbrev
|
|
1384
1635
|
});
|
|
1385
1636
|
}
|
|
1637
|
+
function isInWorld(geometry) {
|
|
1638
|
+
return geometry[0] >= 0 && geometry[0] <= 1 && geometry[1] >= 0 && geometry[1] <= 1;
|
|
1639
|
+
}
|
|
1386
1640
|
|
|
1387
1641
|
//#endregion
|
|
1388
1642
|
//#region src/tile.ts
|
|
@@ -1594,7 +1848,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1594
1848
|
this.total = 0;
|
|
1595
1849
|
}
|
|
1596
1850
|
initialize(features) {
|
|
1597
|
-
this.splitTile(features, 0, 0, 0);
|
|
1851
|
+
this.splitTile(this.clipToWorld(features), 0, 0, 0);
|
|
1598
1852
|
if (this.options.debug) {
|
|
1599
1853
|
if (features.length) console.log("features: %d, points: %d", this.tiles[0].numFeatures, this.tiles[0].numPoints);
|
|
1600
1854
|
console.timeEnd("generate tiles");
|
|
@@ -1614,8 +1868,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1614
1868
|
0,
|
|
1615
1869
|
0
|
|
1616
1870
|
];
|
|
1617
|
-
const
|
|
1618
|
-
rootTile
|
|
1871
|
+
const rootSource = this.clipToWorld(source);
|
|
1872
|
+
const rootTile = createTile(rootSource, z, x, y, options);
|
|
1873
|
+
rootTile.source = rootSource;
|
|
1619
1874
|
const id = toID(z, x, y);
|
|
1620
1875
|
this.tiles[id] = rootTile;
|
|
1621
1876
|
this.tileCoords.push({
|
|
@@ -1646,6 +1901,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1646
1901
|
getTile(z, x, y) {
|
|
1647
1902
|
const { extent, debug } = this.options;
|
|
1648
1903
|
const z2 = 1 << z;
|
|
1904
|
+
if (!this.options.wrap && (x < 0 || x >= z2)) return null;
|
|
1649
1905
|
x = x + z2 & z2 - 1;
|
|
1650
1906
|
const id = toID(z, x, y);
|
|
1651
1907
|
if (this.tiles[id]) return transformTile(this.tiles[id], extent);
|
|
@@ -1671,6 +1927,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1671
1927
|
return transformTile(this.tiles[id], extent);
|
|
1672
1928
|
}
|
|
1673
1929
|
/**
|
|
1930
|
+
* The features the top tile starts from. A world that wraps keeps them all left and right, where wrapping already
|
|
1931
|
+
* put them inside, and one that doesn't only what lies inside the world square and its buffer. Positions in the
|
|
1932
|
+
* unit square are also cut above and below in a world that wraps, where lng/lat is clamped already. The index keeps
|
|
1933
|
+
* the rest, so an update can still move a feature in from outside.
|
|
1934
|
+
*/
|
|
1935
|
+
clipToWorld(features) {
|
|
1936
|
+
const { wrap, coordinates } = this.options;
|
|
1937
|
+
if (wrap && coordinates !== "unitSquare") return features;
|
|
1938
|
+
const buffer = this.options.buffer / this.options.extent;
|
|
1939
|
+
const inX = wrap ? features : clip(features, 1, -buffer, 1 + buffer, 0, -Infinity, Infinity, this.options) || [];
|
|
1940
|
+
return clip(inX, 1, -buffer, 1 + buffer, 1, -Infinity, Infinity, this.options) || [];
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1674
1943
|
* splits features from a parent tile to sub-tiles.
|
|
1675
1944
|
* z, x, and y are the coordinates of the parent tile
|
|
1676
1945
|
* cz, cx, and cy are the coordinates of the target tile
|
|
@@ -1824,7 +2093,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1824
2093
|
updateable: false,
|
|
1825
2094
|
cluster: false,
|
|
1826
2095
|
clusterOptions: defaultClusterOptions,
|
|
1827
|
-
debug: 0
|
|
2096
|
+
debug: 0,
|
|
2097
|
+
coordinates: "lngLat",
|
|
2098
|
+
wrap: true
|
|
1828
2099
|
};
|
|
1829
2100
|
/**
|
|
1830
2101
|
* Main class for creating and managing a vector tile index from GeoJSON data.
|
|
@@ -1832,11 +2103,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1832
2103
|
var GeoJSONVT = class {
|
|
1833
2104
|
constructor(data, options) {
|
|
1834
2105
|
options = this.options = Object.assign({}, defaultOptions, options);
|
|
2106
|
+
this.worldCoordinateHelper = getWorldCoordinateHelper(options.coordinates);
|
|
1835
2107
|
const debug = options.debug;
|
|
1836
2108
|
if (debug) console.time("preprocess data");
|
|
1837
2109
|
if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error("maxZoom should be in the 0-24 range");
|
|
1838
2110
|
if (options.promoteId && options.generateId) throw new Error("promoteId and generateId cannot be used together.");
|
|
1839
|
-
|
|
2111
|
+
if (options.updateable) this.unlocated = /* @__PURE__ */ new Map();
|
|
2112
|
+
let features = convertToInternal(data, options, this.unlocated);
|
|
1840
2113
|
if (debug) {
|
|
1841
2114
|
console.timeEnd("preprocess data");
|
|
1842
2115
|
console.log("index: maxZoom: %d, maxPoints: %d", options.indexMaxZoom, options.indexMaxPoints);
|
|
@@ -1847,7 +2120,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1847
2120
|
this.initializeIndex(features, options);
|
|
1848
2121
|
}
|
|
1849
2122
|
initializeIndex(features, options) {
|
|
1850
|
-
this.tileIndex = options.cluster ? new ClusterTileIndex(
|
|
2123
|
+
this.tileIndex = options.cluster ? new ClusterTileIndex({
|
|
2124
|
+
...options.clusterOptions,
|
|
2125
|
+
coordinates: options.coordinates,
|
|
2126
|
+
wrap: options.wrap
|
|
2127
|
+
}) : new TileIndex(options);
|
|
1851
2128
|
if (!features.length) return;
|
|
1852
2129
|
this.tileIndex.initialize(features);
|
|
1853
2130
|
}
|
|
@@ -1872,24 +2149,27 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1872
2149
|
updateData(diff, filter) {
|
|
1873
2150
|
const options = this.options;
|
|
1874
2151
|
if (!options.updateable) throw new Error("to update tile geojson `updateable` option must be set to true");
|
|
1875
|
-
let { affected, source } = applySourceDiff(this.source, diff, options);
|
|
2152
|
+
let { affected, source } = applySourceDiff(this.source, diff, options, this.unlocated);
|
|
1876
2153
|
if (filter) ({affected, source} = this.filterUpdate(source, affected, filter));
|
|
1877
2154
|
if (!affected.length) return;
|
|
1878
2155
|
this.source = source;
|
|
1879
2156
|
this.tileIndex.updateIndex(source, affected, options);
|
|
1880
2157
|
}
|
|
1881
2158
|
/**
|
|
1882
|
-
* Filter an update using a predicate function
|
|
2159
|
+
* Filter an update using a predicate function, the features an update emptied included, so that one the filter
|
|
2160
|
+
* rejects can't come back with a later update. A feature without a geometry at all isn't passed to the predicate.
|
|
2161
|
+
* Returns the affected and updated source features.
|
|
1883
2162
|
*/
|
|
1884
2163
|
filterUpdate(source, affected, predicate) {
|
|
1885
2164
|
const removeIds = /* @__PURE__ */ new Set();
|
|
1886
2165
|
for (const feature of source) {
|
|
1887
2166
|
if (feature.id == void 0) continue;
|
|
1888
|
-
if (predicate(featureToGeoJSON(feature))) continue;
|
|
2167
|
+
if (predicate(featureToGeoJSON(feature, this.worldCoordinateHelper))) continue;
|
|
1889
2168
|
affected.push(feature);
|
|
1890
2169
|
removeIds.add(feature.id);
|
|
1891
2170
|
}
|
|
1892
2171
|
source = source.filter((feature) => !removeIds.has(feature.id));
|
|
2172
|
+
for (const [id, feature] of this.unlocated ?? []) if (feature.geometry && !predicate(feature)) this.unlocated.delete(id);
|
|
1893
2173
|
return {
|
|
1894
2174
|
affected,
|
|
1895
2175
|
source
|
|
@@ -1900,12 +2180,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1900
2180
|
*/
|
|
1901
2181
|
getData() {
|
|
1902
2182
|
if (!this.options.updateable) throw new Error("to retrieve data the `updateable` option must be set to true");
|
|
1903
|
-
return convertToGeoJSON(this.source);
|
|
2183
|
+
return convertToGeoJSON(this.source, this.worldCoordinateHelper);
|
|
1904
2184
|
}
|
|
1905
2185
|
/**
|
|
1906
2186
|
* Update supercluster options and regenerate the index.
|
|
1907
2187
|
* @param cluster - whether to enable clustering
|
|
1908
|
-
* @param clusterOptions - {@link SuperclusterOptions}
|
|
2188
|
+
* @param clusterOptions - {@link SuperclusterOptions} without `coordinates` and `wrap`, which come from the index options
|
|
1909
2189
|
*/
|
|
1910
2190
|
updateClusterOptions(cluster, clusterOptions) {
|
|
1911
2191
|
const wasCluster = this.options.cluster;
|
|
@@ -1982,9 +2262,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1982
2262
|
function geoJSONToTile(data, z, x, y, options = {}) {
|
|
1983
2263
|
options = {
|
|
1984
2264
|
...defaultOptions,
|
|
2265
|
+
wrap: false,
|
|
1985
2266
|
...options
|
|
1986
2267
|
};
|
|
1987
|
-
const { wrap: shouldWrap
|
|
2268
|
+
const { wrap: shouldWrap, clip: shouldClip = false } = options;
|
|
1988
2269
|
let features = convertToInternal(data, options);
|
|
1989
2270
|
if (shouldWrap) features = wrap(features, options);
|
|
1990
2271
|
if (shouldClip || options.lineMetrics) {
|