@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.
- package/README.md +3 -13
- package/dist/clip.d.ts +22 -0
- package/dist/clip.d.ts.map +1 -0
- package/dist/clip.test.d.ts +2 -0
- package/dist/clip.test.d.ts.map +1 -0
- package/dist/cluster-tile-index.d.ts +76 -0
- package/dist/cluster-tile-index.d.ts.map +1 -0
- package/dist/cluster-tile-index.test.d.ts +2 -0
- package/dist/cluster-tile-index.test.d.ts.map +1 -0
- package/dist/convert.d.ts +17 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/deconvert.d.ts +19 -0
- package/dist/deconvert.d.ts.map +1 -0
- package/dist/deconvert.test.d.ts +2 -0
- package/dist/deconvert.test.d.ts.map +1 -0
- package/dist/definitions.d.ts +241 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/difference.d.ts +67 -0
- package/dist/difference.d.ts.map +1 -0
- package/dist/difference.test.d.ts +2 -0
- package/dist/difference.test.d.ts.map +1 -0
- package/dist/feature.d.ts +20 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/geojson-to-tile.d.ts +35 -0
- package/dist/geojson-to-tile.d.ts.map +1 -0
- package/dist/geojson-vt-dev.js +1582 -478
- package/dist/geojson-vt.js +1 -1
- package/dist/geojson-vt.mjs +1250 -473
- package/dist/geojson-vt.mjs.map +1 -1
- package/dist/geojsonvt.d.ts +76 -0
- package/dist/geojsonvt.d.ts.map +1 -0
- package/dist/geojsonvt.test.d.ts +2 -0
- package/dist/geojsonvt.test.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/simplify.d.ts +9 -0
- package/dist/simplify.d.ts.map +1 -0
- package/dist/simplify.test.d.ts +2 -0
- package/dist/simplify.test.d.ts.map +1 -0
- package/dist/tile-index.d.ts +51 -0
- package/dist/tile-index.d.ts.map +1 -0
- package/dist/tile.d.ts +12 -0
- package/dist/tile.d.ts.map +1 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/wrap.d.ts +3 -0
- package/dist/wrap.d.ts.map +1 -0
- package/package.json +26 -12
- package/src/clip.ts +119 -81
- package/src/cluster-tile-index.test.ts +205 -0
- package/src/cluster-tile-index.ts +513 -0
- package/src/convert.ts +97 -75
- package/src/deconvert.test.ts +153 -0
- package/src/deconvert.ts +92 -0
- package/src/definitions.ts +196 -18
- package/src/difference.ts +3 -3
- package/src/feature.ts +11 -4
- package/src/geojson-to-tile.ts +58 -0
- package/src/geojsonvt.test.ts +39 -0
- package/src/geojsonvt.ts +209 -0
- package/src/index.ts +27 -378
- package/src/tile-index.ts +310 -0
- package/src/tile.ts +92 -103
- package/src/transform.ts +41 -39
- package/src/wrap.ts +4 -4
package/src/definitions.ts
CHANGED
|
@@ -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
|
|
65
|
-
minY
|
|
66
|
-
maxX
|
|
67
|
-
maxY
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
197
|
+
export type GeoJSONVTFeature = GeoJSONVTFeaturePoint | GeoJSONVTFeatureNonPoint;
|
|
198
|
+
|
|
199
|
+
export type GeoJSONVTTile = GeoJSONVTInternalTile & {
|
|
200
|
+
transformed: true;
|
|
201
|
+
features: GeoJSONVTFeature[]
|
|
202
|
+
}
|
|
80
203
|
|
|
81
|
-
export
|
|
82
|
-
[
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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,
|
|
1
|
+
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature } from "./definitions";
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
|
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
|
+
});
|
package/src/geojsonvt.ts
ADDED
|
@@ -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
|
+
}
|