@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/dist/geojsonvt.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type GeoJSONVTSourceDiff } from './difference';
|
|
2
|
-
import type { ClusterOrPointFeature, GeoJSONVTOptions, GeoJSONVTTile
|
|
2
|
+
import type { ClusterOrPointFeature, GeoJSONVTOptions, GeoJSONVTTile } from './definitions';
|
|
3
3
|
export declare const defaultOptions: GeoJSONVTOptions;
|
|
4
4
|
/**
|
|
5
5
|
* Main class for creating and managing a vector tile index from GeoJSON data.
|
|
6
6
|
*/
|
|
7
7
|
export declare class GeoJSONVT {
|
|
8
8
|
private options;
|
|
9
|
+
private worldCoordinateHelper;
|
|
9
10
|
private source?;
|
|
11
|
+
private unlocated?;
|
|
10
12
|
private tileIndex;
|
|
11
13
|
constructor(data: GeoJSON.GeoJSON, options?: GeoJSONVTOptions);
|
|
12
14
|
private initializeIndex;
|
|
@@ -24,7 +26,9 @@ export declare class GeoJSONVT {
|
|
|
24
26
|
*/
|
|
25
27
|
updateData(diff: GeoJSONVTSourceDiff, filter?: (feature: GeoJSON.Feature) => boolean): void;
|
|
26
28
|
/**
|
|
27
|
-
* Filter an update using a predicate function
|
|
29
|
+
* Filter an update using a predicate function, the features an update emptied included, so that one the filter
|
|
30
|
+
* rejects can't come back with a later update. A feature without a geometry at all isn't passed to the predicate.
|
|
31
|
+
* Returns the affected and updated source features.
|
|
28
32
|
*/
|
|
29
33
|
private filterUpdate;
|
|
30
34
|
/**
|
|
@@ -34,9 +38,9 @@ export declare class GeoJSONVT {
|
|
|
34
38
|
/**
|
|
35
39
|
* Update supercluster options and regenerate the index.
|
|
36
40
|
* @param cluster - whether to enable clustering
|
|
37
|
-
* @param clusterOptions - {@link SuperclusterOptions}
|
|
41
|
+
* @param clusterOptions - {@link SuperclusterOptions} without `coordinates` and `wrap`, which come from the index options
|
|
38
42
|
*/
|
|
39
|
-
updateClusterOptions(cluster: boolean, clusterOptions:
|
|
43
|
+
updateClusterOptions(cluster: boolean, clusterOptions: GeoJSONVTOptions['clusterOptions']): void;
|
|
40
44
|
/**
|
|
41
45
|
* Returns the zoom level at which a cluster expands into multiple children.
|
|
42
46
|
* @param clusterId - The target cluster id.
|
package/dist/geojsonvt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geojsonvt.d.ts","sourceRoot":"","sources":["../src/geojsonvt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"geojsonvt.d.ts","sourceRoot":"","sources":["../src/geojsonvt.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAGvE,OAAO,KAAK,EAAC,qBAAqB,EAAgD,gBAAgB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAExI,eAAO,MAAM,cAAc,EAAE,gBAgB5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,qBAAqB,CAAwB;IAErD,OAAO,CAAC,MAAM,CAAC,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAC,CAAwC;IAC1D,OAAO,CAAC,SAAS,CAAqB;gBAE1B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,gBAAgB;IAkC7D,OAAO,CAAC,eAAe;IAMvB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI;IAUzF;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO;IAqBpF;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAkBpB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO;IAK1B;;;;OAIG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,gBAAgB,CAAC,gBAAgB,CAAC;IAazF;;;;OAIG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIzD;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAIrE;;;;;;OAMG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;CAG9G"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { GeoJSONVTFeatureDiff, GeoJSONVTSourceDiff } from './difference';
|
|
2
|
-
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';
|
|
2
|
+
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';
|
|
3
3
|
import type { KDBushWithData } from './cluster-tile-index';
|
|
4
4
|
import { GeoJSONVT } from './geojsonvt';
|
|
5
5
|
import { ClusterTileIndex } from './cluster-tile-index';
|
|
6
6
|
import { geoJSONToTile } from './geojson-to-tile';
|
|
7
7
|
import { GEOJSONVT_CLIP_START, GEOJSONVT_CLIP_END } from './tile';
|
|
8
8
|
export { GeoJSONVT, ClusterTileIndex as Supercluster, geoJSONToTile, GEOJSONVT_CLIP_START, GEOJSONVT_CLIP_END };
|
|
9
|
-
export type { GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONToTileOptions, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, PartialGeoJSONVTFeature, SliceArray, SliceFixedArray, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTSourceDiff, GeoJSONVTFeatureDiff, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalPointFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTInternalMultiPolygonFeature, SuperclusterOptions, ClusterProperties, ClusterFeature, ClusterOrPointFeature, KDBushWithData, GeoJSONVTTileIndex };
|
|
9
|
+
export type { GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONToTileOptions, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, PartialGeoJSONVTFeature, SliceArray, SliceFixedArray, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTSourceDiff, GeoJSONVTFeatureDiff, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalPointFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTInternalMultiPolygonFeature, SuperclusterOptions, GeoJSONVTCoordinatesType, ClusterProperties, ClusterFeature, ClusterOrPointFeature, KDBushWithData, GeoJSONVTTileIndex };
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC5E,OAAO,KAAK,EAAC,wBAAwB,EAAE,kCAAkC,EAAE,uCAAuC,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,+BAA+B,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,iCAAiC,EAAE,oCAAoC,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC5E,OAAO,KAAK,EAAC,wBAAwB,EAAE,kCAAkC,EAAE,uCAAuC,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,+BAA+B,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,iCAAiC,EAAE,oCAAoC,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,wBAAwB,EAAC,MAAM,eAAe,CAAC;AAC9rB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,QAAQ,CAAC;AAEhE,OAAO,EACH,SAAS,EACT,gBAAgB,IAAI,YAAY,EAChC,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EACrB,CAAA;AAED,YAAY,EACR,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,iCAAiC,EACjC,oCAAoC,EACpC,6BAA6B,EAC7B,kCAAkC,EAClC,kCAAkC,EAClC,uCAAuC,EACvC,+BAA+B,EAC/B,oCAAoC,EACpC,mBAAmB,EACnB,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EACrB,CAAC"}
|
package/dist/tile-index.d.ts
CHANGED
|
@@ -24,9 +24,10 @@ export declare class TileIndex implements GeoJSONVTTileIndex {
|
|
|
24
24
|
/** {@inheritdoc} */
|
|
25
25
|
getTile(z: number, x: number, y: number): GeoJSONVTTile | null;
|
|
26
26
|
/**
|
|
27
|
-
* The features the top tile starts from
|
|
28
|
-
* and only what lies inside the world square and its buffer
|
|
29
|
-
*
|
|
27
|
+
* The features the top tile starts from. A world that wraps keeps them all left and right, where wrapping already
|
|
28
|
+
* put them inside, and one that doesn't only what lies inside the world square and its buffer. Positions in the
|
|
29
|
+
* unit square are also cut above and below in a world that wraps, where lng/lat is clamped already. The index keeps
|
|
30
|
+
* the rest, so an update can still move a feature in from outside.
|
|
30
31
|
*/
|
|
31
32
|
private clipToWorld;
|
|
32
33
|
/**
|
package/dist/tile-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tile-index.d.ts","sourceRoot":"","sources":["../src/tile-index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjK,qBAAa,SAAU,YAAW,kBAAkB;IAWpC,OAAO,CAAC,OAAO;IAT3B,OAAO,CAAC,UAAU,CAAkD;IACpE,OAAO,CAAC,KAAK,CAAa;IAE1B,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAAA;KAAC,CAAC;IACrD,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAM;gBAGvB,OAAO,EAAE,gBAAgB;IAO7C,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAWtD,oBAAoB;IACpB,WAAW,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA4BtH,oBAAoB;IAEpB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI1D,oBAAoB;IAEpB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAI/D,oBAAoB;IAEpB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;IAIzG,oBAAoB;IACpB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAyC9D
|
|
1
|
+
{"version":3,"file":"tile-index.d.ts","sourceRoot":"","sources":["../src/tile-index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjK,qBAAa,SAAU,YAAW,kBAAkB;IAWpC,OAAO,CAAC,OAAO;IAT3B,OAAO,CAAC,UAAU,CAAkD;IACpE,OAAO,CAAC,KAAK,CAAa;IAE1B,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAAA;KAAC,CAAC;IACrD,gBAAgB;IACT,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAM;gBAGvB,OAAO,EAAE,gBAAgB;IAO7C,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAWtD,oBAAoB;IACpB,WAAW,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA4BtH,oBAAoB;IAEpB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI1D,oBAAoB;IAEpB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,EAAE,GAAG,IAAI;IAI/D,oBAAoB;IAEpB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;IAIzG,oBAAoB;IACpB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAyC9D;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAQnB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS;IA4FjB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuE1B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GeoJSONVTCoordinatesType } from './definitions';
|
|
2
|
+
/**
|
|
3
|
+
* How the positions of the input relate to the world square the tiles cover, in which x and y run from 0 to 1.
|
|
4
|
+
*/
|
|
5
|
+
export interface WorldCoordinateHelper {
|
|
6
|
+
projectX(x: number): number;
|
|
7
|
+
projectY(y: number): number;
|
|
8
|
+
unprojectX(x: number): number;
|
|
9
|
+
unprojectY(y: number): number;
|
|
10
|
+
}
|
|
11
|
+
export declare const mercatorWorldCoordinateHelper: WorldCoordinateHelper;
|
|
12
|
+
export declare function getWorldCoordinateHelper(coordinates?: GeoJSONVTCoordinatesType): WorldCoordinateHelper;
|
|
13
|
+
//# sourceMappingURL=world-coordinate-helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-coordinate-helper.d.ts","sourceRoot":"","sources":["../src/world-coordinate-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,eAAe,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC;AA+CD,eAAO,MAAM,6BAA6B,EAAE,qBAA2D,CAAC;AAGxG,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,wBAAwB,GAAG,qBAAqB,CAEtG"}
|
package/package.json
CHANGED
|
@@ -218,8 +218,8 @@ test('does not keep the cluster properties of an earlier load', () => {
|
|
|
218
218
|
expect(index.clusterProps.length).toBe(count);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
test('takes points and the getClusters bounding box in
|
|
222
|
-
const index = new ClusterTileIndex({
|
|
221
|
+
test('takes points and the getClusters bounding box in the unit square with unitSquare coordinates', () => {
|
|
222
|
+
const index = new ClusterTileIndex({coordinates: 'unitSquare', wrap: false});
|
|
223
223
|
index.load([
|
|
224
224
|
{type: 'Feature', properties: {name: 'a'}, geometry: {type: 'Point', coordinates: [0.25, 0.25]}},
|
|
225
225
|
{type: 'Feature', properties: {name: 'b'}, geometry: {type: 'Point', coordinates: [0.75, 0.75]}}
|
|
@@ -228,8 +228,8 @@ test('takes points and the getClusters bounding box in world coordinates with wo
|
|
|
228
228
|
expect(index.getClusters([0, 0, 0.5, 0.5], 0).map(f => f.geometry.coordinates)).toEqual([[0.25, 0.25]]);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
test('leaves points outside the world out of load
|
|
232
|
-
const index = new ClusterTileIndex({
|
|
231
|
+
test('leaves points outside the world out of load in a unit square without wrap', () => {
|
|
232
|
+
const index = new ClusterTileIndex({coordinates: 'unitSquare', wrap: false});
|
|
233
233
|
index.load([
|
|
234
234
|
{type: 'Feature', properties: {name: 'inside'}, geometry: {type: 'Point', coordinates: [0.99, 0.5]}},
|
|
235
235
|
{type: 'Feature', properties: {name: 'outside'}, geometry: {type: 'Point', coordinates: [1.03, 0.5]}}
|
|
@@ -238,8 +238,8 @@ test('leaves points outside the world out of load with worldCoordinates', () =>
|
|
|
238
238
|
expect(index.getTile(0, 0, 0).features.map(feature => feature.geometry)).toEqual([[[507, 256]]]);
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
test('returns no tile outside the world
|
|
242
|
-
const index = new ClusterTileIndex({
|
|
241
|
+
test('returns no tile outside the world in a unit square without wrap', () => {
|
|
242
|
+
const index = new ClusterTileIndex({coordinates: 'unitSquare', wrap: false});
|
|
243
243
|
index.load([
|
|
244
244
|
{type: 'Feature', properties: {name: 'west'}, geometry: {type: 'Point', coordinates: [0.01, 0.5]}},
|
|
245
245
|
{type: 'Feature', properties: {name: 'east'}, geometry: {type: 'Point', coordinates: [0.99, 0.5]}},
|
|
@@ -293,8 +293,8 @@ test('does not cluster points that are farther apart than the radius on a high z
|
|
|
293
293
|
expect(index.getClusters([-180, -90, 180, 90], 20)).toHaveLength(2);
|
|
294
294
|
});
|
|
295
295
|
|
|
296
|
-
test('returns a cluster within a step of the mean of its points
|
|
297
|
-
const index = new ClusterTileIndex({
|
|
296
|
+
test('returns a cluster within a step of the mean of its points in a unit square without wrap', () => {
|
|
297
|
+
const index = new ClusterTileIndex({coordinates: 'unitSquare', wrap: false});
|
|
298
298
|
index.load([
|
|
299
299
|
{type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [0.54, 0.42]}},
|
|
300
300
|
{type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [0.54, 0.42]}},
|
|
@@ -306,3 +306,23 @@ test('returns a cluster within a step of the mean of its points with worldCoordi
|
|
|
306
306
|
expect(x).toBeCloseTo(0.5466666667, 9);
|
|
307
307
|
expect(y).toBeCloseTo(0.4133333333, 9);
|
|
308
308
|
});
|
|
309
|
+
|
|
310
|
+
test('getClusters keeps only the part of a box inside a world that does not wrap', () => {
|
|
311
|
+
const index = new ClusterTileIndex({wrap: false});
|
|
312
|
+
index.load([
|
|
313
|
+
{type: 'Feature', properties: {name: 'west'}, geometry: {type: 'Point', coordinates: [-175, 0]}},
|
|
314
|
+
{type: 'Feature', properties: {name: 'east'}, geometry: {type: 'Point', coordinates: [175, 0]}}
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
expect(index.getClusters([170, -20, 190, 20], 17).map(f => f.properties.name)).toEqual(['east']);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('getClusters repeats a box across the edge of a unit square that wraps', () => {
|
|
321
|
+
const index = new ClusterTileIndex({coordinates: 'unitSquare', wrap: true});
|
|
322
|
+
index.load([
|
|
323
|
+
{type: 'Feature', properties: {name: 'west'}, geometry: {type: 'Point', coordinates: [0.01, 0.5]}},
|
|
324
|
+
{type: 'Feature', properties: {name: 'east'}, geometry: {type: 'Point', coordinates: [0.99, 0.5]}}
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
expect(index.getClusters([0.95, 0, 1.05, 1], 17).map(f => f.properties.name).sort()).toEqual(['east', 'west']);
|
|
328
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import KDBush from 'kdbush';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {getWorldCoordinateHelper, type WorldCoordinateHelper} from './world-coordinate-helper';
|
|
3
|
+
import {featureToGeoJSON} from './deconvert';
|
|
4
4
|
import type {ClusterFeature, ClusterOrPointFeature, ClusterProperties, GeoJSONVTTileIndex, GeoJSONVTFeature, GeoJSONVTInternalFeature, GeoJSONVTInternalPointFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions} from './definitions';
|
|
5
5
|
|
|
6
6
|
type ClusterFeatureInternal = GeoJSONVTInternalPointFeature & {
|
|
@@ -31,7 +31,9 @@ export const defaultClusterOptions: Required<SuperclusterOptions> = {
|
|
|
31
31
|
log: false,
|
|
32
32
|
generateId: false,
|
|
33
33
|
reduce: null,
|
|
34
|
-
map: (props) => props as Record<string, unknown
|
|
34
|
+
map: (props) => props as Record<string, unknown>,
|
|
35
|
+
coordinates: 'lngLat',
|
|
36
|
+
wrap: true
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
/**
|
|
@@ -87,17 +89,11 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
87
89
|
stride: number;
|
|
88
90
|
clusterProps: Record<string, unknown>[];
|
|
89
91
|
points: GeoJSONVTInternalPointFeature[];
|
|
90
|
-
|
|
91
|
-
* Whether points are in world coordinates, see `worldCoordinates` in {@link GeoJSONVTOptions}. Points outside the
|
|
92
|
-
* world are then left out of the clusters, tiles at an edge get no points from the opposite one, `getTile` returns
|
|
93
|
-
* `null` outside the world, and clusters come back in world coordinates.
|
|
94
|
-
* @experimental
|
|
95
|
-
*/
|
|
96
|
-
worldCoordinates: boolean;
|
|
92
|
+
private worldCoordinateHelper: WorldCoordinateHelper;
|
|
97
93
|
|
|
98
|
-
constructor(options?: SuperclusterOptions
|
|
94
|
+
constructor(options?: SuperclusterOptions) {
|
|
99
95
|
this.options = createOptions(options);
|
|
100
|
-
this.
|
|
96
|
+
this.worldCoordinateHelper = getWorldCoordinateHelper(this.options.coordinates);
|
|
101
97
|
this.trees = new Array(this.options.maxZoom + 1);
|
|
102
98
|
this.stride = this.options.reduce ? 5 : 4;
|
|
103
99
|
this.clusterProps = [];
|
|
@@ -120,8 +116,8 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
120
116
|
|
|
121
117
|
const positions = point.geometry.type === 'MultiPoint' ? point.geometry.coordinates : [point.geometry.coordinates];
|
|
122
118
|
for (const [lng, lat] of positions) {
|
|
123
|
-
const [x, y] = [projectX(lng, this.
|
|
124
|
-
if (this.
|
|
119
|
+
const [x, y] = [this.worldCoordinateHelper.projectX(lng), this.worldCoordinateHelper.projectY(lat)];
|
|
120
|
+
if (!this.options.wrap && !isInWorld([x, y])) continue;
|
|
125
121
|
|
|
126
122
|
const feature: GeoJSONVTInternalPointFeature = {
|
|
127
123
|
id: point.id,
|
|
@@ -148,7 +144,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
148
144
|
|
|
149
145
|
for (const feature of features) {
|
|
150
146
|
if (feature.type === 'Point') {
|
|
151
|
-
if (this.
|
|
147
|
+
if (!this.options.wrap && !isInWorld(feature.geometry)) continue;
|
|
152
148
|
points.push(feature);
|
|
153
149
|
continue;
|
|
154
150
|
}
|
|
@@ -156,7 +152,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
156
152
|
|
|
157
153
|
for (let i = 0; i < feature.geometry.length; i += 3) {
|
|
158
154
|
const geometry = [feature.geometry[i], feature.geometry[i + 1]];
|
|
159
|
-
if (this.
|
|
155
|
+
if (!this.options.wrap && !isInWorld(geometry)) continue;
|
|
160
156
|
points.push({id: feature.id, type: 'Point', geometry, tags: feature.tags});
|
|
161
157
|
}
|
|
162
158
|
}
|
|
@@ -170,7 +166,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
170
166
|
* @param features
|
|
171
167
|
*/
|
|
172
168
|
updateIndex(features: GeoJSONVTInternalFeature[], _affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions) {
|
|
173
|
-
this.options = createOptions(options.clusterOptions);
|
|
169
|
+
this.options = createOptions({...options.clusterOptions, coordinates: this.options.coordinates, wrap: this.options.wrap});
|
|
174
170
|
this.initialize(features);
|
|
175
171
|
}
|
|
176
172
|
|
|
@@ -242,12 +238,42 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
242
238
|
|
|
243
239
|
/**
|
|
244
240
|
* Returns clusters and/or points within a bounding box at a given zoom level.
|
|
245
|
-
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order, or `[minX, minY, maxX, maxY]` in
|
|
241
|
+
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order, or `[minX, minY, maxX, maxY]` in the unit square with `coordinates: 'unitSquare'`.
|
|
242
|
+
* In a world that wraps, the box can reach across the antimeridian. In one that doesn't, only its part inside the world counts.
|
|
246
243
|
* @param zoom - Zoom level to query.
|
|
247
244
|
*/
|
|
248
245
|
public getClusters(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeature[] {
|
|
249
|
-
const clusterInternal =
|
|
250
|
-
|
|
246
|
+
const clusterInternal =
|
|
247
|
+
!this.options.wrap ? this.getClustersInWorld(bbox, zoom) :
|
|
248
|
+
this.options.coordinates === 'unitSquare' ? this.getClustersAcrossEdge(bbox, zoom) :
|
|
249
|
+
this.getClustersInternal(bbox, zoom);
|
|
250
|
+
return clusterInternal.map((f) => featureToGeoJSON(f, this.worldCoordinateHelper) as ClusterOrPointFeature);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The clusters and points in the part of a box inside a world that ends at its edges.
|
|
255
|
+
*/
|
|
256
|
+
private getClustersInWorld(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeatureInternal[] {
|
|
257
|
+
const lngLat = this.options.coordinates !== 'unitSquare';
|
|
258
|
+
const y1 = this.worldCoordinateHelper.projectY(lngLat ? Math.max(-90, Math.min(90, bbox[1])) : bbox[1]);
|
|
259
|
+
const y2 = this.worldCoordinateHelper.projectY(lngLat ? Math.max(-90, Math.min(90, bbox[3])) : bbox[3]);
|
|
260
|
+
const minX = Math.max(0, this.worldCoordinateHelper.projectX(bbox[0]));
|
|
261
|
+
const maxX = Math.min(1, this.worldCoordinateHelper.projectX(bbox[2]));
|
|
262
|
+
if (minX > maxX) return [];
|
|
263
|
+
return this.getClustersInRange([minX, Math.min(y1, y2), maxX, Math.max(y1, y2)], zoom);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The clusters and points in a box of a unit square that wraps, where the box can reach across its left or right edge.
|
|
268
|
+
*/
|
|
269
|
+
private getClustersAcrossEdge([minX, minY, maxX, maxY]: [number, number, number, number], zoom: number): ClusterOrPointFeatureInternal[] {
|
|
270
|
+
if (maxX < minX) maxX += 1;
|
|
271
|
+
if (maxX - minX >= 1) return this.getClustersInRange([0, minY, 1, maxY], zoom);
|
|
272
|
+
const shift = Math.floor(minX);
|
|
273
|
+
const west = minX - shift;
|
|
274
|
+
const east = maxX - shift;
|
|
275
|
+
if (east <= 1) return this.getClustersInRange([west, minY, east, maxY], zoom);
|
|
276
|
+
return this.getClustersInRange([west, minY, 1, maxY], zoom).concat(this.getClustersInRange([0, minY, east - 1, maxY], zoom));
|
|
251
277
|
}
|
|
252
278
|
|
|
253
279
|
private getClustersInternal(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeatureInternal[] {
|
|
@@ -265,7 +291,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
265
291
|
return easternHem.concat(westernHem);
|
|
266
292
|
}
|
|
267
293
|
|
|
268
|
-
return this.getClustersInRange([projectX(minLng), projectY(maxLat), projectX(maxLng), projectY(minLat)], zoom);
|
|
294
|
+
return this.getClustersInRange([this.worldCoordinateHelper.projectX(minLng), this.worldCoordinateHelper.projectY(maxLat), this.worldCoordinateHelper.projectX(maxLng), this.worldCoordinateHelper.projectY(minLat)], zoom);
|
|
269
295
|
}
|
|
270
296
|
|
|
271
297
|
/**
|
|
@@ -308,7 +334,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
308
334
|
for (const id of ids) {
|
|
309
335
|
const k = id * this.stride;
|
|
310
336
|
if (data[k + OFFSET_PARENT] === clusterId) {
|
|
311
|
-
children.push(data[k + OFFSET_NUM] > 1 ? getClusterGeoJSON(coords, id, data, k, this.clusterProps, this.
|
|
337
|
+
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) as GeoJSON.Feature<GeoJSON.Point>);
|
|
312
338
|
}
|
|
313
339
|
}
|
|
314
340
|
|
|
@@ -345,7 +371,7 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
345
371
|
return null;
|
|
346
372
|
}
|
|
347
373
|
const z2 = Math.pow(2, z);
|
|
348
|
-
if (this.
|
|
374
|
+
if (!this.options.wrap && (x < 0 || x >= z2 || y < 0 || y >= z2)) return null;
|
|
349
375
|
const {extent, radius} = this.options;
|
|
350
376
|
const p = radius / extent;
|
|
351
377
|
const top = encode((y - p) / z2);
|
|
@@ -364,12 +390,12 @@ export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
|
|
364
390
|
tree.range(encode((x - p) / z2), top, encode((x + 1 + p) / z2), bottom),
|
|
365
391
|
tree, x, y, z2, tile);
|
|
366
392
|
|
|
367
|
-
if (
|
|
393
|
+
if (this.options.wrap && x === 0) {
|
|
368
394
|
this.addTileFeatures(
|
|
369
395
|
tree.range(encode(1 - p / z2), top, encode(1), bottom),
|
|
370
396
|
tree, z2, y, z2, tile);
|
|
371
397
|
}
|
|
372
|
-
if (
|
|
398
|
+
if (this.options.wrap && x === z2 - 1) {
|
|
373
399
|
this.addTileFeatures(
|
|
374
400
|
tree.range(encode(0), top, encode(p / z2), bottom),
|
|
375
401
|
tree, -1, y, z2, tile);
|
|
@@ -628,14 +654,14 @@ function getClusterFeature(coords: Int32Array, index: number, data: Int32Array,
|
|
|
628
654
|
};
|
|
629
655
|
}
|
|
630
656
|
|
|
631
|
-
function getClusterGeoJSON(coords: Int32Array, index: number, data: Int32Array, i: number, clusterProps: Record<string, unknown>[],
|
|
657
|
+
function getClusterGeoJSON(coords: Int32Array, index: number, data: Int32Array, i: number, clusterProps: Record<string, unknown>[], worldCoordinateHelper: WorldCoordinateHelper): ClusterFeature {
|
|
632
658
|
return {
|
|
633
659
|
type: 'Feature',
|
|
634
660
|
id: data[i + OFFSET_ID],
|
|
635
661
|
properties: getClusterProperties(data, i, clusterProps),
|
|
636
662
|
geometry: {
|
|
637
663
|
type: 'Point',
|
|
638
|
-
coordinates: [unprojectX(decode(coords[index * 2])
|
|
664
|
+
coordinates: [worldCoordinateHelper.unprojectX(decode(coords[index * 2])), worldCoordinateHelper.unprojectY(decode(coords[index * 2 + 1]))]
|
|
639
665
|
}
|
|
640
666
|
};
|
|
641
667
|
}
|
package/src/convert.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import {simplify} from './simplify';
|
|
3
3
|
import {createFeature, optimizeLineMemory} from './feature';
|
|
4
|
+
import {getWorldCoordinateHelper, type WorldCoordinateHelper} from './world-coordinate-helper';
|
|
4
5
|
import type {GeoJSONVTInternalFeature, GeoJSONVTOptions, SliceArray} from './definitions';
|
|
5
6
|
|
|
6
7
|
const MAX_GEOMETRY_COLLECTION_DEPTH = 1024;
|
|
@@ -9,27 +10,42 @@ const MAX_GEOMETRY_COLLECTION_DEPTH = 1024;
|
|
|
9
10
|
* converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
|
|
10
11
|
* @param data
|
|
11
12
|
* @param options
|
|
13
|
+
* @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
|
|
12
14
|
* @returns
|
|
13
15
|
*/
|
|
14
|
-
export function convertToInternal(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
|
|
16
|
+
export function convertToInternal(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions, unlocated?: Map<string | number, GeoJSON.Feature>): GeoJSONVTInternalFeature[] {
|
|
15
17
|
const features: GeoJSONVTInternalFeature[] = [];
|
|
16
18
|
|
|
17
19
|
switch (data.type) {
|
|
18
20
|
case 'FeatureCollection':
|
|
19
21
|
for (let i = 0; i < data.features.length; i++) {
|
|
20
|
-
|
|
22
|
+
convertFeature(features, data.features[i], options, unlocated, i);
|
|
21
23
|
}
|
|
22
24
|
break;
|
|
23
25
|
case 'Feature':
|
|
24
|
-
|
|
26
|
+
convertFeature(features, data, options, unlocated);
|
|
25
27
|
break;
|
|
26
28
|
default:
|
|
27
|
-
|
|
29
|
+
convertFeature(features, {type: "Feature" as const, geometry: data, properties: undefined}, options, unlocated);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
return features;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function convertFeature(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, options: GeoJSONVTOptions, unlocated?: Map<string | number, GeoJSON.Feature>, index?: number) {
|
|
36
|
+
const count = features.length;
|
|
37
|
+
featureToInternal(features, geojson, options, index);
|
|
38
|
+
if (!unlocated) return;
|
|
39
|
+
|
|
40
|
+
const id = getFeatureId(geojson, options, index);
|
|
41
|
+
if (id == null) return;
|
|
42
|
+
if (features.length > count) {
|
|
43
|
+
unlocated.delete(id);
|
|
44
|
+
} else {
|
|
45
|
+
unlocated.set(id, {...geojson, id});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
function featureToInternal(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number, depth = 0) {
|
|
34
50
|
if (!geojson.geometry) return;
|
|
35
51
|
|
|
@@ -47,31 +63,31 @@ function featureToInternal(features: GeoJSONVTInternalFeature[], geojson: GeoJSO
|
|
|
47
63
|
|
|
48
64
|
const id = getFeatureId(geojson, options, index);
|
|
49
65
|
const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
|
|
50
|
-
const
|
|
66
|
+
const worldCoordinateHelper = getWorldCoordinateHelper(options.coordinates);
|
|
51
67
|
|
|
52
68
|
switch (geojson.geometry.type) {
|
|
53
69
|
case 'Point':
|
|
54
|
-
convertPointFeature(features, id, geojson.geometry,
|
|
70
|
+
convertPointFeature(features, id, geojson.geometry, worldCoordinateHelper, geojson.properties);
|
|
55
71
|
return;
|
|
56
72
|
|
|
57
73
|
case 'MultiPoint':
|
|
58
|
-
convertMultiPointFeature(features, id, geojson.geometry,
|
|
74
|
+
convertMultiPointFeature(features, id, geojson.geometry, worldCoordinateHelper, geojson.properties);
|
|
59
75
|
return;
|
|
60
76
|
|
|
61
77
|
case 'LineString':
|
|
62
|
-
convertLineStringFeature(features, id, geojson.geometry, tolerance,
|
|
78
|
+
convertLineStringFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
63
79
|
return;
|
|
64
80
|
|
|
65
81
|
case 'MultiLineString':
|
|
66
|
-
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance,
|
|
82
|
+
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, options, geojson.properties);
|
|
67
83
|
return;
|
|
68
84
|
|
|
69
85
|
case 'Polygon':
|
|
70
|
-
convertPolygonFeature(features, id, geojson.geometry, tolerance,
|
|
86
|
+
convertPolygonFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
71
87
|
return;
|
|
72
88
|
|
|
73
89
|
case 'MultiPolygon':
|
|
74
|
-
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance,
|
|
90
|
+
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, worldCoordinateHelper, geojson.properties);
|
|
75
91
|
return;
|
|
76
92
|
|
|
77
93
|
default:
|
|
@@ -100,52 +116,52 @@ function convertGeometryCollection(features: GeoJSONVTInternalFeature[], geojson
|
|
|
100
116
|
}
|
|
101
117
|
}
|
|
102
118
|
|
|
103
|
-
function convertPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Point,
|
|
119
|
+
function convertPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Point, worldCoordinateHelper: WorldCoordinateHelper, properties: GeoJSON.GeoJsonProperties) {
|
|
104
120
|
const out: number[] = [];
|
|
105
|
-
out.push(projectX(geom.coordinates[0]
|
|
121
|
+
out.push(worldCoordinateHelper.projectX(geom.coordinates[0]), worldCoordinateHelper.projectY(geom.coordinates[1]), 0);
|
|
106
122
|
features.push(createFeature(id, 'Point', out, properties));
|
|
107
123
|
}
|
|
108
124
|
|
|
109
|
-
function convertMultiPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPoint,
|
|
125
|
+
function convertMultiPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPoint, worldCoordinateHelper: WorldCoordinateHelper, properties: GeoJSON.GeoJsonProperties) {
|
|
110
126
|
const out: number[] = [];
|
|
111
127
|
for (const coords of geom.coordinates) {
|
|
112
|
-
out.push(projectX(coords[0]
|
|
128
|
+
out.push(worldCoordinateHelper.projectX(coords[0]), worldCoordinateHelper.projectY(coords[1]), 0);
|
|
113
129
|
}
|
|
114
130
|
features.push(createFeature(id, 'MultiPoint', out, properties));
|
|
115
131
|
}
|
|
116
132
|
|
|
117
|
-
function convertLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.LineString, tolerance: number,
|
|
133
|
+
function convertLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.LineString, tolerance: number, worldCoordinateHelper: WorldCoordinateHelper, properties: GeoJSON.GeoJsonProperties) {
|
|
118
134
|
const out: SliceArray = {points: []};
|
|
119
|
-
convertLine(geom.coordinates, out, tolerance, false,
|
|
135
|
+
convertLine(geom.coordinates, out, tolerance, false, worldCoordinateHelper);
|
|
120
136
|
features.push(createFeature(id, 'LineString', out, properties));
|
|
121
137
|
}
|
|
122
138
|
|
|
123
|
-
function convertMultiLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiLineString, tolerance: number,
|
|
139
|
+
function convertMultiLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiLineString, tolerance: number, worldCoordinateHelper: WorldCoordinateHelper, options: GeoJSONVTOptions, properties: GeoJSON.GeoJsonProperties) {
|
|
124
140
|
if (options.lineMetrics) {
|
|
125
141
|
// explode into linestrings to be able to track metrics
|
|
126
142
|
for (const line of geom.coordinates) {
|
|
127
143
|
const out: SliceArray = {points: []};
|
|
128
|
-
convertLine(line, out, tolerance, false,
|
|
144
|
+
convertLine(line, out, tolerance, false, worldCoordinateHelper);
|
|
129
145
|
features.push(createFeature(id, 'LineString', out, properties));
|
|
130
146
|
}
|
|
131
147
|
} else {
|
|
132
148
|
const out: SliceArray[] = [];
|
|
133
|
-
convertLines(geom.coordinates, out, tolerance, false,
|
|
149
|
+
convertLines(geom.coordinates, out, tolerance, false, worldCoordinateHelper);
|
|
134
150
|
features.push(createFeature(id, 'MultiLineString', out, properties));
|
|
135
151
|
}
|
|
136
152
|
}
|
|
137
153
|
|
|
138
|
-
function convertPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Polygon, tolerance: number,
|
|
154
|
+
function convertPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Polygon, tolerance: number, worldCoordinateHelper: WorldCoordinateHelper, properties: GeoJSON.GeoJsonProperties) {
|
|
139
155
|
const out: SliceArray[] = [];
|
|
140
|
-
convertLines(geom.coordinates, out, tolerance, true,
|
|
156
|
+
convertLines(geom.coordinates, out, tolerance, true, worldCoordinateHelper);
|
|
141
157
|
features.push(createFeature(id, 'Polygon', out, properties));
|
|
142
158
|
}
|
|
143
159
|
|
|
144
|
-
function convertMultiPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPolygon, tolerance: number,
|
|
160
|
+
function convertMultiPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPolygon, tolerance: number, worldCoordinateHelper: WorldCoordinateHelper, properties: GeoJSON.GeoJsonProperties) {
|
|
145
161
|
const out: SliceArray[][] = [];
|
|
146
162
|
for (const polygon of geom.coordinates) {
|
|
147
163
|
const polygonOut: SliceArray[] = [];
|
|
148
|
-
convertLines(polygon, polygonOut, tolerance, true,
|
|
164
|
+
convertLines(polygon, polygonOut, tolerance, true, worldCoordinateHelper);
|
|
149
165
|
if (!polygonOut.length) continue;
|
|
150
166
|
|
|
151
167
|
out.push(polygonOut);
|
|
@@ -155,13 +171,13 @@ function convertMultiPolygonFeature(features: GeoJSONVTInternalFeature[], id: nu
|
|
|
155
171
|
features.push(createFeature(id, 'MultiPolygon', out, properties));
|
|
156
172
|
}
|
|
157
173
|
|
|
158
|
-
function convertLine(ring: GeoJSON.Position[], out: SliceArray, tolerance: number, isPolygon: boolean,
|
|
174
|
+
function convertLine(ring: GeoJSON.Position[], out: SliceArray, tolerance: number, isPolygon: boolean, worldCoordinateHelper: WorldCoordinateHelper) {
|
|
159
175
|
let x0, y0;
|
|
160
176
|
let size = 0;
|
|
161
177
|
|
|
162
178
|
for (let j = 0; j < ring.length; j++) {
|
|
163
|
-
const x = projectX(ring[j][0]
|
|
164
|
-
const y = projectY(ring[j][1]
|
|
179
|
+
const x = worldCoordinateHelper.projectX(ring[j][0]);
|
|
180
|
+
const y = worldCoordinateHelper.projectY(ring[j][1]);
|
|
165
181
|
|
|
166
182
|
out.points.push(x, y, 0);
|
|
167
183
|
|
|
@@ -187,28 +203,10 @@ function convertLine(ring: GeoJSON.Position[], out: SliceArray, tolerance: numbe
|
|
|
187
203
|
out.end = out.size;
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
function convertLines(rings: GeoJSON.Position[][], out: SliceArray[], tolerance: number, isPolygon: boolean,
|
|
206
|
+
function convertLines(rings: GeoJSON.Position[][], out: SliceArray[], tolerance: number, isPolygon: boolean, worldCoordinateHelper: WorldCoordinateHelper) {
|
|
191
207
|
for (let i = 0; i < rings.length; i++) {
|
|
192
208
|
const geom: SliceArray = {points: []};
|
|
193
|
-
convertLine(rings[i], geom, tolerance, isPolygon,
|
|
209
|
+
convertLine(rings[i], geom, tolerance, isPolygon, worldCoordinateHelper);
|
|
194
210
|
out.push(geom);
|
|
195
211
|
}
|
|
196
212
|
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Convert longitude to spherical mercator in [0..1] range, or keep a world coordinate as it is
|
|
200
|
-
*/
|
|
201
|
-
export function projectX(x: number, worldCoordinates = false) {
|
|
202
|
-
if (worldCoordinates) return x;
|
|
203
|
-
return x / 360 + 0.5;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Convert latitude to spherical mercator in [0..1] range, or keep a world coordinate as it is
|
|
208
|
-
*/
|
|
209
|
-
export function projectY(y: number, worldCoordinates = false) {
|
|
210
|
-
if (worldCoordinates) return y;
|
|
211
|
-
const sin = Math.sin(y * Math.PI / 180);
|
|
212
|
-
const y2 = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
|
213
|
-
return y2 < 0 ? 0 : y2 > 1 ? 1 : y2;
|
|
214
|
-
}
|
package/src/deconvert.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import {test, expect} from 'vitest';
|
|
3
|
-
import {featureToGeoJSON
|
|
4
|
-
import {
|
|
3
|
+
import {featureToGeoJSON} from './deconvert';
|
|
4
|
+
import {mercatorWorldCoordinateHelper} from './world-coordinate-helper';
|
|
5
5
|
import type {GeoJSONVTInternalFeature} from './definitions';
|
|
6
6
|
|
|
7
7
|
test('project/unproject roundtrip retains precision', () => {
|
|
@@ -19,8 +19,8 @@ test('project/unproject roundtrip retains precision', () => {
|
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
for (const {lng, lat} of coords) {
|
|
22
|
-
expect(unprojectX(projectX(lng))).toBeCloseTo(lng, 10);
|
|
23
|
-
expect(unprojectY(projectY(lat))).toBeCloseTo(lat, 10);
|
|
22
|
+
expect(mercatorWorldCoordinateHelper.unprojectX(mercatorWorldCoordinateHelper.projectX(lng))).toBeCloseTo(lng, 10);
|
|
23
|
+
expect(mercatorWorldCoordinateHelper.unprojectY(mercatorWorldCoordinateHelper.projectY(lat))).toBeCloseTo(lat, 10);
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|