@maplibre/geojson-vt 6.2.0 → 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 +7 -2
- package/dist/cluster-tile-index.d.ts +12 -9
- 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 +38 -10
- 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 +263 -133
- package/dist/geojson-vt.js +1 -1
- package/dist/geojson-vt.mjs +261 -132
- 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 +4 -3
- 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/package.json +1 -1
- package/src/cluster-tile-index.test.ts +28 -8
- package/src/cluster-tile-index.ts +53 -27
- package/src/convert.ts +45 -47
- package/src/deconvert.test.ts +4 -4
- package/src/deconvert.ts +15 -31
- package/src/definitions.ts +39 -10
- package/src/difference.ts +48 -19
- package/src/geojson-to-tile.ts +2 -2
- package/src/geojsonvt.test.ts +171 -20
- package/src/geojsonvt.ts +24 -10
- package/src/index.ts +2 -1
- package/src/tile-index.ts +8 -6
- package/src/world-coordinate-helper.ts +63 -0
- package/src/wrap.ts +1 -1
package/src/geojsonvt.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {convertToInternal} from './convert';
|
|
2
2
|
import {convertToGeoJSON, featureToGeoJSON} from './deconvert';
|
|
3
|
+
import {getWorldCoordinateHelper, type WorldCoordinateHelper} from './world-coordinate-helper';
|
|
3
4
|
import {wrap} from './wrap';
|
|
4
5
|
import {applySourceDiff, type GeoJSONVTSourceDiff} from './difference';
|
|
5
6
|
import {ClusterTileIndex, defaultClusterOptions} from './cluster-tile-index';
|
|
6
7
|
import {TileIndex} from './tile-index';
|
|
7
|
-
import type {ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONVTTile
|
|
8
|
+
import type {ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONVTTile} from './definitions';
|
|
8
9
|
|
|
9
10
|
export const defaultOptions: GeoJSONVTOptions = {
|
|
10
11
|
maxZoom: 14,
|
|
@@ -20,7 +21,8 @@ export const defaultOptions: GeoJSONVTOptions = {
|
|
|
20
21
|
cluster: false,
|
|
21
22
|
clusterOptions: defaultClusterOptions,
|
|
22
23
|
debug: 0,
|
|
23
|
-
|
|
24
|
+
coordinates: 'lngLat',
|
|
25
|
+
wrap: true
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -28,12 +30,15 @@ export const defaultOptions: GeoJSONVTOptions = {
|
|
|
28
30
|
*/
|
|
29
31
|
export class GeoJSONVT {
|
|
30
32
|
private options: GeoJSONVTOptions;
|
|
33
|
+
private worldCoordinateHelper: WorldCoordinateHelper;
|
|
31
34
|
|
|
32
35
|
private source?: GeoJSONVTInternalFeature[];
|
|
36
|
+
private unlocated?: Map<string | number, GeoJSON.Feature>;
|
|
33
37
|
private tileIndex: GeoJSONVTTileIndex;
|
|
34
38
|
|
|
35
39
|
constructor(data: GeoJSON.GeoJSON, options?: GeoJSONVTOptions) {
|
|
36
40
|
options = this.options = Object.assign({}, defaultOptions, options);
|
|
41
|
+
this.worldCoordinateHelper = getWorldCoordinateHelper(options.coordinates);
|
|
37
42
|
|
|
38
43
|
const debug = options.debug;
|
|
39
44
|
|
|
@@ -42,8 +47,11 @@ export class GeoJSONVT {
|
|
|
42
47
|
if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error('maxZoom should be in the 0-24 range');
|
|
43
48
|
if (options.promoteId && options.generateId) throw new Error('promoteId and generateId cannot be used together.');
|
|
44
49
|
|
|
50
|
+
// for updateable indexes, keep the properties of the features without coordinates, so that an update can give them some
|
|
51
|
+
if (options.updateable) this.unlocated = new Map();
|
|
52
|
+
|
|
45
53
|
// projects and adds simplification info
|
|
46
|
-
let features = convertToInternal(data, options);
|
|
54
|
+
let features = convertToInternal(data, options, this.unlocated);
|
|
47
55
|
|
|
48
56
|
if (debug) {
|
|
49
57
|
console.timeEnd('preprocess data');
|
|
@@ -63,7 +71,7 @@ export class GeoJSONVT {
|
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
private initializeIndex(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions) {
|
|
66
|
-
this.tileIndex = options.cluster ? new ClusterTileIndex(options.clusterOptions, options.
|
|
74
|
+
this.tileIndex = options.cluster ? new ClusterTileIndex({...options.clusterOptions, coordinates: options.coordinates, wrap: options.wrap}) : new TileIndex(options);
|
|
67
75
|
if (!features.length) return;
|
|
68
76
|
this.tileIndex.initialize(features);
|
|
69
77
|
}
|
|
@@ -95,7 +103,7 @@ export class GeoJSONVT {
|
|
|
95
103
|
if (!options.updateable) throw new Error('to update tile geojson `updateable` option must be set to true');
|
|
96
104
|
|
|
97
105
|
// apply diff and collect affected features and updated source that will be used to invalidate tiles
|
|
98
|
-
let {affected, source} = applySourceDiff(this.source, diff, options);
|
|
106
|
+
let {affected, source} = applySourceDiff(this.source, diff, options, this.unlocated);
|
|
99
107
|
|
|
100
108
|
if (filter) {
|
|
101
109
|
({affected, source} = this.filterUpdate(source, affected, filter));
|
|
@@ -111,19 +119,25 @@ export class GeoJSONVT {
|
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
/**
|
|
114
|
-
* Filter an update using a predicate function
|
|
122
|
+
* Filter an update using a predicate function, the features an update emptied included, so that one the filter
|
|
123
|
+
* rejects can't come back with a later update. A feature without a geometry at all isn't passed to the predicate.
|
|
124
|
+
* Returns the affected and updated source features.
|
|
115
125
|
*/
|
|
116
126
|
private filterUpdate(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], predicate: (feature: GeoJSON.Feature) => boolean) {
|
|
117
127
|
const removeIds = new Set();
|
|
118
128
|
|
|
119
129
|
for (const feature of source) {
|
|
120
130
|
if (feature.id == undefined) continue;
|
|
121
|
-
if (predicate(featureToGeoJSON(feature, this.
|
|
131
|
+
if (predicate(featureToGeoJSON(feature, this.worldCoordinateHelper))) continue;
|
|
122
132
|
affected.push(feature);
|
|
123
133
|
removeIds.add(feature.id);
|
|
124
134
|
}
|
|
125
135
|
source = source.filter(feature => !removeIds.has(feature.id));
|
|
126
136
|
|
|
137
|
+
for (const [id, feature] of this.unlocated ?? []) {
|
|
138
|
+
if (feature.geometry && !predicate(feature)) this.unlocated.delete(id);
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
return {affected, source};
|
|
128
142
|
}
|
|
129
143
|
|
|
@@ -132,15 +146,15 @@ export class GeoJSONVT {
|
|
|
132
146
|
*/
|
|
133
147
|
getData(): GeoJSON.GeoJSON {
|
|
134
148
|
if (!this.options.updateable) throw new Error('to retrieve data the `updateable` option must be set to true');
|
|
135
|
-
return convertToGeoJSON(this.source, this.
|
|
149
|
+
return convertToGeoJSON(this.source, this.worldCoordinateHelper);
|
|
136
150
|
}
|
|
137
151
|
|
|
138
152
|
/**
|
|
139
153
|
* Update supercluster options and regenerate the index.
|
|
140
154
|
* @param cluster - whether to enable clustering
|
|
141
|
-
* @param clusterOptions - {@link SuperclusterOptions}
|
|
155
|
+
* @param clusterOptions - {@link SuperclusterOptions} without `coordinates` and `wrap`, which come from the index options
|
|
142
156
|
*/
|
|
143
|
-
updateClusterOptions(cluster: boolean, clusterOptions:
|
|
157
|
+
updateClusterOptions(cluster: boolean, clusterOptions: GeoJSONVTOptions['clusterOptions']) {
|
|
144
158
|
const wasCluster = this.options.cluster;
|
|
145
159
|
this.options.cluster = cluster;
|
|
146
160
|
this.options.clusterOptions = clusterOptions;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import type {GeoJSONVTFeatureDiff, GeoJSONVTSourceDiff} from './difference';
|
|
3
|
-
import type {GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTOptions, GeoJSONToTileOptions, PartialGeoJSONVTFeature, SliceArray, SliceFixedArray, ClusterProperties, ClusterFeature, ClusterOrPointFeature, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, GeoJSONVTTileIndex, SuperclusterOptions} from './definitions';
|
|
3
|
+
import type {GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTOptions, GeoJSONToTileOptions, PartialGeoJSONVTFeature, SliceArray, SliceFixedArray, ClusterProperties, ClusterFeature, ClusterOrPointFeature, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, GeoJSONVTTileIndex, SuperclusterOptions, GeoJSONVTCoordinatesType} from './definitions';
|
|
4
4
|
import type {KDBushWithData} from './cluster-tile-index';
|
|
5
5
|
import {GeoJSONVT} from './geojsonvt';
|
|
6
6
|
import {ClusterTileIndex} from './cluster-tile-index';
|
|
@@ -39,6 +39,7 @@ export type {
|
|
|
39
39
|
GeoJSONVTInternalPolygonFeature,
|
|
40
40
|
GeoJSONVTInternalMultiPolygonFeature,
|
|
41
41
|
SuperclusterOptions,
|
|
42
|
+
GeoJSONVTCoordinatesType,
|
|
42
43
|
ClusterProperties,
|
|
43
44
|
ClusterFeature,
|
|
44
45
|
ClusterOrPointFeature,
|
package/src/tile-index.ts
CHANGED
|
@@ -84,7 +84,7 @@ export class TileIndex implements GeoJSONVTTileIndex {
|
|
|
84
84
|
const {extent, debug} = this.options;
|
|
85
85
|
|
|
86
86
|
const z2 = 1 << z;
|
|
87
|
-
if (this.options.
|
|
87
|
+
if (!this.options.wrap && (x < 0 || x >= z2)) return null; // a row outside the world has no tile either
|
|
88
88
|
x = (x + z2) & (z2 - 1); // wrap tile x coordinate
|
|
89
89
|
|
|
90
90
|
const id = toID(z, x, y);
|
|
@@ -122,14 +122,16 @@ export class TileIndex implements GeoJSONVTTileIndex {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* The features the top tile starts from
|
|
126
|
-
* and only what lies inside the world square and its buffer
|
|
127
|
-
*
|
|
125
|
+
* The features the top tile starts from. A world that wraps keeps them all left and right, where wrapping already
|
|
126
|
+
* put them inside, and one that doesn't only what lies inside the world square and its buffer. Positions in the
|
|
127
|
+
* unit square are also cut above and below in a world that wraps, where lng/lat is clamped already. The index keeps
|
|
128
|
+
* the rest, so an update can still move a feature in from outside.
|
|
128
129
|
*/
|
|
129
130
|
private clipToWorld(features: GeoJSONVTInternalFeature[]): GeoJSONVTInternalFeature[] {
|
|
130
|
-
|
|
131
|
+
const {wrap, coordinates} = this.options;
|
|
132
|
+
if (wrap && coordinates !== 'unitSquare') return features;
|
|
131
133
|
const buffer = this.options.buffer / this.options.extent;
|
|
132
|
-
const inX = clip(features, 1, -buffer, 1 + buffer, AxisType.X, -Infinity, Infinity, this.options) || [];
|
|
134
|
+
const inX = wrap ? features : clip(features, 1, -buffer, 1 + buffer, AxisType.X, -Infinity, Infinity, this.options) || [];
|
|
133
135
|
return clip(inX, 1, -buffer, 1 + buffer, AxisType.Y, -Infinity, Infinity, this.options) || [];
|
|
134
136
|
}
|
|
135
137
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type {GeoJSONVTCoordinatesType} from './definitions';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How the positions of the input relate to the world square the tiles cover, in which x and y run from 0 to 1.
|
|
5
|
+
*/
|
|
6
|
+
export interface WorldCoordinateHelper {
|
|
7
|
+
projectX(x: number): number;
|
|
8
|
+
projectY(y: number): number;
|
|
9
|
+
unprojectX(x: number): number;
|
|
10
|
+
unprojectY(y: number): number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Longitude and latitude, which spherical mercator puts in the world square.
|
|
15
|
+
*/
|
|
16
|
+
class MercatorWorldCoordinateHelper implements WorldCoordinateHelper {
|
|
17
|
+
projectX(lng: number): number {
|
|
18
|
+
return lng / 360 + 0.5;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
projectY(lat: number): number {
|
|
22
|
+
const sin = Math.sin(lat * Math.PI / 180);
|
|
23
|
+
const y = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
|
24
|
+
return y < 0 ? 0 : y > 1 ? 1 : y;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
unprojectX(x: number): number {
|
|
28
|
+
return (x - 0.5) * 360;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
unprojectY(y: number): number {
|
|
32
|
+
const y2 = (180 - y * 360) * Math.PI / 180;
|
|
33
|
+
return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Positions in the unit square already, see `coordinates` in {@link GeoJSONVTOptions}.
|
|
39
|
+
*/
|
|
40
|
+
class IdentityWorldCoordinateHelper implements WorldCoordinateHelper {
|
|
41
|
+
projectX(x: number): number {
|
|
42
|
+
return x;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
projectY(y: number): number {
|
|
46
|
+
return y;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
unprojectX(x: number): number {
|
|
50
|
+
return x;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
unprojectY(y: number): number {
|
|
54
|
+
return y;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const mercatorWorldCoordinateHelper: WorldCoordinateHelper = new MercatorWorldCoordinateHelper();
|
|
59
|
+
const identityWorldCoordinateHelper: WorldCoordinateHelper = new IdentityWorldCoordinateHelper();
|
|
60
|
+
|
|
61
|
+
export function getWorldCoordinateHelper(coordinates?: GeoJSONVTCoordinatesType): WorldCoordinateHelper {
|
|
62
|
+
return coordinates === 'unitSquare' ? identityWorldCoordinateHelper : mercatorWorldCoordinateHelper;
|
|
63
|
+
}
|
package/src/wrap.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { GeoJSONVTInternalFeature, GeoJSONVTOptions, SliceArray, SliceFixed
|
|
|
4
4
|
import {createFeature, optimizeLineMemory} from './feature';
|
|
5
5
|
|
|
6
6
|
export function wrap(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
|
|
7
|
-
if (options.
|
|
7
|
+
if (!options.wrap) return features;
|
|
8
8
|
|
|
9
9
|
const buffer = options.buffer / options.extent;
|
|
10
10
|
let merged = features;
|