@maplibre/geojson-vt 5.0.3 → 6.0.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.
Files changed (65) hide show
  1. package/README.md +3 -13
  2. package/dist/clip.d.ts +22 -0
  3. package/dist/clip.d.ts.map +1 -0
  4. package/dist/clip.test.d.ts +2 -0
  5. package/dist/clip.test.d.ts.map +1 -0
  6. package/dist/cluster-tile-index.d.ts +76 -0
  7. package/dist/cluster-tile-index.d.ts.map +1 -0
  8. package/dist/cluster-tile-index.test.d.ts +2 -0
  9. package/dist/cluster-tile-index.test.d.ts.map +1 -0
  10. package/dist/convert.d.ts +17 -0
  11. package/dist/convert.d.ts.map +1 -0
  12. package/dist/deconvert.d.ts +19 -0
  13. package/dist/deconvert.d.ts.map +1 -0
  14. package/dist/deconvert.test.d.ts +2 -0
  15. package/dist/deconvert.test.d.ts.map +1 -0
  16. package/dist/definitions.d.ts +241 -0
  17. package/dist/definitions.d.ts.map +1 -0
  18. package/dist/difference.d.ts +67 -0
  19. package/dist/difference.d.ts.map +1 -0
  20. package/dist/difference.test.d.ts +2 -0
  21. package/dist/difference.test.d.ts.map +1 -0
  22. package/dist/feature.d.ts +20 -0
  23. package/dist/feature.d.ts.map +1 -0
  24. package/dist/geojson-to-tile.d.ts +35 -0
  25. package/dist/geojson-to-tile.d.ts.map +1 -0
  26. package/dist/geojson-vt-dev.js +1582 -478
  27. package/dist/geojson-vt.js +1 -1
  28. package/dist/geojson-vt.mjs +1250 -473
  29. package/dist/geojson-vt.mjs.map +1 -1
  30. package/dist/geojsonvt.d.ts +76 -0
  31. package/dist/geojsonvt.d.ts.map +1 -0
  32. package/dist/geojsonvt.test.d.ts +2 -0
  33. package/dist/geojsonvt.test.d.ts.map +1 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/simplify.d.ts +9 -0
  37. package/dist/simplify.d.ts.map +1 -0
  38. package/dist/simplify.test.d.ts +2 -0
  39. package/dist/simplify.test.d.ts.map +1 -0
  40. package/dist/tile-index.d.ts +51 -0
  41. package/dist/tile-index.d.ts.map +1 -0
  42. package/dist/tile.d.ts +12 -0
  43. package/dist/tile.d.ts.map +1 -0
  44. package/dist/transform.d.ts +10 -0
  45. package/dist/transform.d.ts.map +1 -0
  46. package/dist/wrap.d.ts +3 -0
  47. package/dist/wrap.d.ts.map +1 -0
  48. package/package.json +26 -12
  49. package/src/clip.ts +119 -81
  50. package/src/cluster-tile-index.test.ts +205 -0
  51. package/src/cluster-tile-index.ts +513 -0
  52. package/src/convert.ts +97 -75
  53. package/src/deconvert.test.ts +153 -0
  54. package/src/deconvert.ts +92 -0
  55. package/src/definitions.ts +196 -18
  56. package/src/difference.ts +3 -3
  57. package/src/feature.ts +11 -4
  58. package/src/geojson-to-tile.ts +58 -0
  59. package/src/geojsonvt.test.ts +39 -0
  60. package/src/geojsonvt.ts +209 -0
  61. package/src/index.ts +27 -378
  62. package/src/tile-index.ts +310 -0
  63. package/src/tile.ts +92 -103
  64. package/src/transform.ts +41 -39
  65. package/src/wrap.ts +4 -4
@@ -53,34 +53,212 @@ export type GeoJSONVTOptions = {
53
53
  * @default 0
54
54
  */
55
55
  debug?: number;
56
+ /**
57
+ * Enable Supercluster for point features.
58
+ * @default false
59
+ */
60
+ cluster?: boolean;
61
+ /**
62
+ * Options for the Supercluster point clustering algorithm.
63
+ * @see {@link SuperclusterOptions}
64
+ */
65
+ clusterOptions?: SuperclusterOptions;
56
66
  };
57
67
 
68
+ export type GeoJSONToTileOptions = GeoJSONVTOptions & {
69
+ /**
70
+ * Whether to wrap features around the antimeridian
71
+ * @default false
72
+ */
73
+ wrap?: boolean;
74
+ /**
75
+ * Whether to clip features to the tile boundary
76
+ * @default false
77
+ */
78
+ clip?: boolean;
79
+ };
58
80
 
59
81
  export type StartEndSizeArray = number[] & { start?: number; end?: number; size?: number };
60
82
 
61
83
  export type PartialGeoJSONVTFeature = {
62
84
  id?: number | string | undefined;
63
85
  tags: GeoJSON.GeoJsonProperties;
64
- minX: number;
65
- minY: number;
66
- maxX: number;
67
- maxY: number;
86
+ minX?: number;
87
+ minY?: number;
88
+ maxX?: number;
89
+ maxY?: number;
90
+ }
91
+
92
+ export type GeoJSONVTInternalPointFeature = PartialGeoJSONVTFeature & {
93
+ type: 'Point';
94
+ geometry: number[];
95
+ };
96
+
97
+ export type GeoJSONVTInternalMultiPointFeature = PartialGeoJSONVTFeature & {
98
+ type: 'MultiPoint';
99
+ geometry: number[];
100
+ };
101
+
102
+ export type GeoJSONVTInternalLineStringFeature = PartialGeoJSONVTFeature & {
103
+ type: 'LineString';
104
+ geometry: StartEndSizeArray;
105
+ };
106
+
107
+ export type GeoJSONVTInternalMultiLineStringFeature = PartialGeoJSONVTFeature & {
108
+ type: 'MultiLineString';
109
+ geometry: StartEndSizeArray[];
110
+ };
111
+
112
+ export type GeoJSONVTInternalPolygonFeature = PartialGeoJSONVTFeature & {
113
+ type: 'Polygon';
114
+ geometry: StartEndSizeArray[];
115
+ };
116
+
117
+ export type GeoJSONVTInternalMultiPolygonFeature = PartialGeoJSONVTFeature & {
118
+ type: 'MultiPolygon';
119
+ geometry: StartEndSizeArray[][];
120
+ };
121
+
122
+ export type GeoJSONVTInternalFeature =
123
+ | GeoJSONVTInternalPointFeature
124
+ | GeoJSONVTInternalMultiPointFeature
125
+ | GeoJSONVTInternalLineStringFeature
126
+ | GeoJSONVTInternalMultiLineStringFeature
127
+ | GeoJSONVTInternalPolygonFeature
128
+ | GeoJSONVTInternalMultiPolygonFeature;
129
+
130
+ /**
131
+ * The geojson properies related to a cluster.
132
+ */
133
+ export type ClusterProperties = {
134
+ cluster: true;
135
+ cluster_id: number;
136
+ point_count: number;
137
+ point_count_abbreviated: string | number;
138
+ [key: string]: unknown;
139
+ };
140
+
141
+ /**
142
+ * A geojson point with cluster properties, see {@link ClusterProperties}.
143
+ */
144
+ export type ClusterFeature = GeoJSON.Feature<GeoJSON.Point, ClusterProperties>;
145
+
146
+ /**
147
+ * A geojson point that is either a regular point or a cluster, which is a point with cluster properties.
148
+ * See {@link ClusterFeature} for more information
149
+ */
150
+ export type ClusterOrPointFeature = ClusterFeature | GeoJSON.Feature<GeoJSON.Point>;
151
+
152
+ export type GeoJSONVTInternalTileFeaturePoint = {
153
+ id? : number | string | undefined;
154
+ type: 1;
155
+ tags: GeoJSON.GeoJsonProperties | null;
156
+ geometry: number[];
157
+ }
158
+
159
+ export type GeoJSONVTInternalTileFeatureNonPoint = {
160
+ id? : number | string | undefined;
161
+ type: 2 | 3;
162
+ tags: GeoJSON.GeoJsonProperties | null;
163
+ geometry: number[][];
164
+ }
165
+ export type GeoJSONVTInternalTileFeature = GeoJSONVTInternalTileFeaturePoint | GeoJSONVTInternalTileFeatureNonPoint;
166
+
167
+ export type GeoJSONVTInternalTile = {
168
+ transformed: boolean;
169
+ features: GeoJSONVTInternalTileFeature[];
170
+ source: GeoJSONVTInternalFeature[] | null;
171
+ x: number;
172
+ y: number;
173
+ z: number;
174
+ minX?: number;
175
+ minY?: number;
176
+ maxX?: number;
177
+ maxY?: number;
178
+ numPoints?: number;
179
+ numSimplified?: number;
180
+ numFeatures?: number;
181
+ }
182
+
183
+ export type GeoJSONVTFeaturePoint = {
184
+ id? : number | string | undefined;
185
+ type: 1;
186
+ tags: GeoJSON.GeoJsonProperties | null;
187
+ geometry: [number, number][]
68
188
  }
69
189
 
70
- export type GeometryTypeMap = {
71
- Point: number[];
72
- MultiPoint: number[];
73
- LineString: StartEndSizeArray;
74
- MultiLineString: StartEndSizeArray[];
75
- Polygon: StartEndSizeArray[];
76
- MultiPolygon: StartEndSizeArray[][];
190
+ export type GeoJSONVTFeatureNonPoint = {
191
+ id? : number | string | undefined;
192
+ type: 2 | 3;
193
+ tags: GeoJSON.GeoJsonProperties | null;
194
+ geometry: [number, number][][]
77
195
  }
78
196
 
79
- export type GeometryType = "Point" | "MultiPoint" | "LineString" | "MultiLineString" | "Polygon" | "MultiPolygon";
197
+ export type GeoJSONVTFeature = GeoJSONVTFeaturePoint | GeoJSONVTFeatureNonPoint;
198
+
199
+ export type GeoJSONVTTile = GeoJSONVTInternalTile & {
200
+ transformed: true;
201
+ features: GeoJSONVTFeature[]
202
+ }
80
203
 
81
- export type GeoJSONVTInternalFeature = {
82
- [K in GeometryType]: PartialGeoJSONVTFeature & {
83
- type: K;
84
- geometry: GeometryTypeMap[K];
85
- }
86
- }[GeometryType];
204
+ export interface GeoJSONVTTileIndex {
205
+ initialize(features: GeoJSONVTInternalFeature[]): void;
206
+ updateIndex(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void;
207
+ getClusterExpansionZoom(clusterId: number): number | null;
208
+ getChildren(clusterId: number): ClusterOrPointFeature[] | null;
209
+ getLeaves(clusterId: number, limit?: number, offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null
210
+ getTile(z: number, x: number, y: number): GeoJSONVTTile | null
211
+ }
212
+
213
+ export type SuperclusterOptions = {
214
+ /**
215
+ * Min zoom to generate clusters on
216
+ * @default 0
217
+ */
218
+ minZoom?: number;
219
+ /**
220
+ * Max zoom level to cluster the points on
221
+ * @default 16
222
+ */
223
+ maxZoom?: number;
224
+ /**
225
+ * Minimum points to form a cluster
226
+ * @default 2
227
+ */
228
+ minPoints?: number;
229
+ /**
230
+ * Cluster radius in pixels
231
+ * @default 40
232
+ */
233
+ radius?: number;
234
+ /**
235
+ * Tile extent (radius is calculated relative to it)
236
+ * @default 512
237
+ */
238
+ extent?: number;
239
+ /**
240
+ * Size of the KD-tree leaf node, affects performance
241
+ * @default 64
242
+ */
243
+ nodeSize?: number;
244
+ /**
245
+ * Whether to log timing info
246
+ * @default false
247
+ */
248
+ log?: boolean;
249
+ /**
250
+ * Whether to generate numeric ids for input features (in vector tiles)
251
+ * @default false
252
+ */
253
+ generateId?: boolean;
254
+ /**
255
+ * A reduce function for calculating custom cluster properties
256
+ * @default null
257
+ */
258
+ reduce?: ((accumulated: Record<string, unknown>, props: Record<string, unknown>) => void) | null;
259
+ /**
260
+ * Properties to use for individual points when running the reducer
261
+ * @default props => props
262
+ */
263
+ map?: (props: GeoJSON.GeoJsonProperties) => Record<string, unknown>;
264
+ };
package/src/difference.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {convert} from './convert';
1
+ import {convertToInternal} from './convert';
2
2
  import {wrap} from './wrap';
3
3
  import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
4
4
 
@@ -103,7 +103,7 @@ export function applySourceDiff(source: GeoJSONVTInternalFeature[], dataDiff: Ge
103
103
  // convert and add new features
104
104
  if (diff.add.size) {
105
105
  // projects and adds simplification info
106
- let addFeatures = convert({type: 'FeatureCollection', features: Array.from(diff.add.values())}, options);
106
+ let addFeatures = convertToInternal({type: 'FeatureCollection', features: Array.from(diff.add.values())}, options);
107
107
 
108
108
  // wraps features (ie extreme west and extreme east)
109
109
  addFeatures = wrap(addFeatures, options);
@@ -154,7 +154,7 @@ function getUpdatedFeature(vtFeature: GeoJSONVTInternalFeature, update: GeoJSONV
154
154
  };
155
155
 
156
156
  // projects and adds simplification info
157
- let features = convert({type: 'FeatureCollection', features: [geojsonFeature]}, options);
157
+ let features = convertToInternal({type: 'FeatureCollection', features: [geojsonFeature]}, options);
158
158
 
159
159
  // wraps features (ie extreme west and extreme east)
160
160
  features = wrap(features, options);
package/src/feature.ts CHANGED
@@ -1,6 +1,13 @@
1
- import type { GeoJSONVTInternalFeature, GeometryType, GeometryTypeMap } from "./definitions";
1
+ import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature } from "./definitions";
2
2
 
3
- export type SupportedGeometries = GeoJSON.Point | GeoJSON.MultiPoint | GeoJSON.LineString | GeoJSON.MultiLineString | GeoJSON.Polygon | GeoJSON.MultiPolygon;
3
+ type FeatureTypeMap = {
4
+ Point: GeoJSONVTInternalPointFeature["geometry"];
5
+ MultiPoint: GeoJSONVTInternalMultiPointFeature["geometry"];
6
+ LineString: GeoJSONVTInternalLineStringFeature["geometry"];
7
+ MultiLineString: GeoJSONVTInternalMultiLineStringFeature["geometry"];
8
+ Polygon: GeoJSONVTInternalPolygonFeature["geometry"];
9
+ MultiPolygon: GeoJSONVTInternalMultiPolygonFeature["geometry"];
10
+ };
4
11
 
5
12
  /**
6
13
  *
@@ -10,9 +17,9 @@ export type SupportedGeometries = GeoJSON.Point | GeoJSON.MultiPoint | GeoJSON.L
10
17
  * @param tags - the feature's properties
11
18
  * @returns the created feature
12
19
  */
13
- export function createFeature<T extends GeometryType>(id: number | string | undefined, type: T, geom: GeometryTypeMap[T], tags: GeoJSON.GeoJsonProperties): GeoJSONVTInternalFeature {
20
+ export function createFeature<T extends GeoJSONVTInternalFeature["type"]>(id: number | string | undefined, type: T, geom: FeatureTypeMap[T], tags: GeoJSON.GeoJsonProperties): GeoJSONVTInternalFeature {
14
21
  // This is mostly for TypeScript type narrowing
15
- const data = { type, geom } as { [K in GeometryType]: { type: K, geom: GeometryTypeMap[K] } }[GeometryType];
22
+ const data = { type, geom } as { [K in GeoJSONVTInternalFeature["type"]]: { type: K, geom: FeatureTypeMap[K] } }[GeoJSONVTInternalFeature["type"]];
16
23
 
17
24
  const feature = {
18
25
  id: id == null ? null : id,
@@ -0,0 +1,58 @@
1
+ import {AxisType, clip} from './clip';
2
+ import {convertToInternal} from './convert';
3
+ import {defaultOptions} from './geojsonvt';
4
+ import {createTile} from './tile';
5
+ import {transformTile} from './transform';
6
+ import {wrap} from './wrap';
7
+ import type {GeoJSONToTileOptions, GeoJSONVTTile} from './definitions';
8
+
9
+ /**
10
+ * Converts GeoJSON data directly to a single vector tile without building a tile index.
11
+ *
12
+ * Unlike the {@link GeoJSONVT} class which builds a hierarchical tile index for efficient
13
+ * repeated tile access, this function generates a single tile on-demand. This is useful when:
14
+ * - You only need one specific tile and don't need to query multiple tiles
15
+ * - The source data is already spatially filtered to the tile's bounding box
16
+ * - You want to avoid the overhead of building a full tile index
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import {geoJSONToTile} from '@maplibre/geojson-vt';
21
+ *
22
+ * const geojson = {
23
+ * type: 'FeatureCollection',
24
+ * features: [{
25
+ * type: 'Feature',
26
+ * geometry: { type: 'Point', coordinates: [-77.03, 38.90] },
27
+ * properties: { name: 'Washington, D.C.' }
28
+ * }]
29
+ * };
30
+ *
31
+ * const tile = geoJSONToTile(geojson, 10, 292, 391, { extent: 4096 });
32
+ * ```
33
+ *
34
+ * @param data - GeoJSON data (Feature, FeatureCollection, or Geometry)
35
+ * @param z - Tile zoom level
36
+ * @param x - Tile x coordinate
37
+ * @param y - Tile y coordinate
38
+ * @param options - Optional configuration for tile generation
39
+ * @returns The generated tile with geometries in tile coordinates, or null if no features
40
+ */
41
+
42
+ export function geoJSONToTile(data: GeoJSON.GeoJSON, z: number, x: number, y: number, options: GeoJSONToTileOptions = {}): GeoJSONVTTile {
43
+ options = {...defaultOptions, ...options};
44
+ const {wrap: shouldWrap = false, clip: shouldClip = false} = options;
45
+
46
+ let features = convertToInternal(data, options);
47
+ if (shouldWrap) {
48
+ features = wrap(features, options);
49
+ }
50
+ if (shouldClip || options.lineMetrics) {
51
+ const pow2 = 1 << z;
52
+ const buffer = options.buffer / options.extent;
53
+ const left = clip(features, pow2, (x - buffer), (x + 1 + buffer), AxisType.X, -1, 2, options);
54
+ features = clip(left || [], pow2, (y - buffer), (y + 1 + buffer), AxisType.Y, -1, 2, options);
55
+ }
56
+
57
+ return transformTile(createTile(features ?? [], z, x, y, options), options.extent);
58
+ }
@@ -0,0 +1,39 @@
1
+ import {test, expect} from 'vitest';
2
+ import {GeoJSONVT} from '.';
3
+
4
+ test('publicly exposed cluster methods: getClusterExpansionZoom, getClusterChildren, getClusterLeaves', () => {
5
+ const points = {
6
+ type: 'FeatureCollection' as const,
7
+ features: Array.from({length: 20}, (_, i) => ({
8
+ type: 'Feature' as const,
9
+ geometry: {type: 'Point' as const, coordinates: [i * 0.0001, i * 0.0001]},
10
+ properties: {name: `Point ${i}`}
11
+ }))
12
+ };
13
+
14
+ const index = new GeoJSONVT(points, {
15
+ updateable: true,
16
+ cluster: true,
17
+ clusterOptions: {radius: 100}
18
+ });
19
+
20
+ const tile = index.getTile(0, 0, 0);
21
+ const cluster = tile.features.find(f => (f.tags as {cluster?: boolean})?.cluster);
22
+ const clusterId = (cluster.tags as {cluster_id: number}).cluster_id;
23
+
24
+ expect(index.getClusterExpansionZoom(clusterId)).toBeGreaterThan(0);
25
+ expect(index.getClusterChildren(clusterId).length).toBeGreaterThan(0);
26
+ expect(index.getClusterLeaves(clusterId, 5, 0).length).toBeLessThanOrEqual(5);
27
+ });
28
+
29
+ test('publicly exposed cluster methods: return undefined when clustering is disabled', () => {
30
+ const index = new GeoJSONVT({type: 'FeatureCollection', features: []}, {
31
+ updateable: true,
32
+ cluster: false,
33
+ clusterOptions: {radius: 100}
34
+ });
35
+
36
+ expect(index.getClusterExpansionZoom(123)).toBeNull();
37
+ expect(index.getClusterChildren(123)).toBeNull();
38
+ expect(index.getClusterLeaves(123, 10, 0)).toBeNull();
39
+ });
@@ -0,0 +1,209 @@
1
+ import {convertToInternal} from './convert';
2
+ import {convertToGeoJSON, featureToGeoJSON} from './deconvert';
3
+ import {wrap} from './wrap';
4
+ import {applySourceDiff, type GeoJSONVTSourceDiff} from './difference';
5
+ import {ClusterTileIndex, defaultClusterOptions} from './cluster-tile-index';
6
+ import {TileIndex} from './tile-index';
7
+ import type {ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions} from './definitions';
8
+
9
+ export const defaultOptions: GeoJSONVTOptions = {
10
+ maxZoom: 14,
11
+ indexMaxZoom: 5,
12
+ indexMaxPoints: 100000,
13
+ tolerance: 3,
14
+ extent: 4096,
15
+ buffer: 64,
16
+ lineMetrics: false,
17
+ promoteId: null,
18
+ generateId: false,
19
+ updateable: false,
20
+ cluster: false,
21
+ clusterOptions: defaultClusterOptions,
22
+ debug: 0
23
+ };
24
+
25
+ /**
26
+ * Main class for creating and managing a vector tile index from GeoJSON data.
27
+ */
28
+ export class GeoJSONVT {
29
+
30
+ /**
31
+ * @internal
32
+ * This is for the tests
33
+ */
34
+ public get tiles() {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ return (this.tileIndex as any)?.tiles ?? {};
37
+ }
38
+ /**
39
+ * @internal
40
+ * This is for the tests
41
+ */
42
+ public get stats() {
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ return (this.tileIndex as any).stats;
45
+ }
46
+ /**
47
+ * @internal
48
+ * This is for the tests
49
+ */
50
+ public get total() {
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ return (this.tileIndex as any).total;
53
+ }
54
+
55
+ private options: GeoJSONVTOptions;
56
+
57
+ private source?: GeoJSONVTInternalFeature[];
58
+ private tileIndex: GeoJSONVTTileIndex;
59
+
60
+ constructor(data: GeoJSON.GeoJSON, options?: GeoJSONVTOptions) {
61
+ options = this.options = Object.assign({}, defaultOptions, options);
62
+
63
+ const debug = options.debug;
64
+
65
+ if (debug) console.time('preprocess data');
66
+
67
+ if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error('maxZoom should be in the 0-24 range');
68
+ if (options.promoteId && options.generateId) throw new Error('promoteId and generateId cannot be used together.');
69
+
70
+ // projects and adds simplification info
71
+ let features = convertToInternal(data, options);
72
+
73
+ if (debug) {
74
+ console.timeEnd('preprocess data');
75
+ console.log('index: maxZoom: %d, maxPoints: %d', options.indexMaxZoom, options.indexMaxPoints);
76
+ console.time('generate tiles');
77
+ }
78
+
79
+ // wraps features (ie extreme west and extreme east)
80
+ features = wrap(features, options);
81
+
82
+ // for updateable indexes, store a copy of the original simplified features
83
+ if (options.updateable) {
84
+ this.source = features;
85
+ }
86
+
87
+ this.initializeIndex(features, options);
88
+ }
89
+
90
+ private initializeIndex(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions) {
91
+ this.tileIndex = options.cluster ? new ClusterTileIndex(options.clusterOptions) : new TileIndex(options);
92
+ if (!features.length) return;
93
+ this.tileIndex.initialize(features);
94
+ }
95
+
96
+ /**
97
+ * Given z, x, and y tile coordinates, returns the corresponding tile with geometries in tile coordinates, much like MVT data is stored.
98
+ * @param z - tile zoom level
99
+ * @param x - tile x coordinate
100
+ * @param y - tile y coordinate
101
+ * @returns the transformed tile or null if not found
102
+ */
103
+ getTile(z: number | string, x: number | string, y: number | string): GeoJSONVTTile | null {
104
+ z = +z;
105
+ x = +x;
106
+ y = +y;
107
+
108
+ if (z < 0 || z > 24) return null;
109
+
110
+ return this.tileIndex.getTile(z, x, y);
111
+ }
112
+
113
+ /**
114
+ * Updates the source data feature set using a {@link GeoJSONVTSourceDiff}
115
+ * @param diff - the source diff object
116
+ */
117
+ updateData(diff: GeoJSONVTSourceDiff, filter?: (feature: GeoJSON.Feature) => boolean) {
118
+ const options = this.options;
119
+
120
+ if (!options.updateable) throw new Error('to update tile geojson `updateable` option must be set to true');
121
+
122
+ // apply diff and collect affected features and updated source that will be used to invalidate tiles
123
+ let {affected, source} = applySourceDiff(this.source, diff, options);
124
+
125
+ if (filter) {
126
+ ({affected, source} = this.filterUpdate(source, affected, filter));
127
+ }
128
+
129
+ // nothing has changed
130
+ if (!affected.length) return;
131
+
132
+ // update source with new simplified feature set
133
+ this.source = source;
134
+
135
+ this.tileIndex.updateIndex(source, affected, options);
136
+ }
137
+
138
+ /**
139
+ * Filter an update using a predicate function. Returns the affected and updated source features.
140
+ */
141
+ private filterUpdate(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], predicate: (feature: GeoJSON.Feature) => boolean) {
142
+ const removeIds = new Set();
143
+
144
+ for (const feature of source) {
145
+ if (feature.id == undefined) continue;
146
+ if (predicate(featureToGeoJSON(feature))) continue;
147
+ affected.push(feature);
148
+ removeIds.add(feature.id);
149
+ }
150
+ source = source.filter(feature => !removeIds.has(feature.id));
151
+
152
+ return {affected, source};
153
+ }
154
+
155
+ /**
156
+ * Returns source data as GeoJSON - only available when `updateable` option is set to true.
157
+ */
158
+ getData(): GeoJSON.GeoJSON {
159
+ if (!this.options.updateable) throw new Error('to retrieve data the `updateable` option must be set to true');
160
+ return convertToGeoJSON(this.source);
161
+ }
162
+
163
+ /**
164
+ * Update supercluster options and regenerate the index.
165
+ * @param cluster - whether to enable clustering
166
+ * @param clusterOptions - {@link SuperclusterOptions}
167
+ */
168
+ updateClusterOptions(cluster: boolean, clusterOptions: SuperclusterOptions) {
169
+ const wasCluster = this.options.cluster;
170
+ this.options.cluster = cluster;
171
+ this.options.clusterOptions = clusterOptions;
172
+
173
+ if (wasCluster == cluster) {
174
+ this.tileIndex.updateIndex(this.source, [], this.options);
175
+ return;
176
+ }
177
+
178
+ this.initializeIndex(this.source, this.options);
179
+ }
180
+
181
+ /**
182
+ * Returns the zoom level at which a cluster expands into multiple children.
183
+ * @param clusterId - The target cluster id.
184
+ * @returns the expansion zoom or null in case of non-clustered source
185
+ */
186
+ getClusterExpansionZoom(clusterId: number): number | null {
187
+ return this.tileIndex.getClusterExpansionZoom(clusterId);
188
+ }
189
+
190
+ /**
191
+ * Returns the immediate children (clusters or points) of a cluster as GeoJSON.
192
+ * @param clusterId - The target cluster id.
193
+ * @returns the immediate children or null in case of non-clustered source
194
+ */
195
+ getClusterChildren(clusterId: number): ClusterOrPointFeature[] | null {
196
+ return this.tileIndex.getChildren(clusterId);
197
+ }
198
+
199
+ /**
200
+ * Returns leaf point features under a cluster, paginated by `limit` and `offset`.
201
+ * @param clusterId - The target cluster id.
202
+ * @param limit - Maximum number of points to return (defaults to `10`).
203
+ * @param offset - Number of points to skip before collecting results (defaults to `0`).
204
+ * @returns leaf point features under a cluster or null in case of non-clustered source
205
+ */
206
+ getClusterLeaves(clusterId: number, limit: number, offset: number): GeoJSON.Feature<GeoJSON.Point>[] | null {
207
+ return this.tileIndex.getLeaves(clusterId, limit, offset);
208
+ }
209
+ }