@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.
@@ -1,12 +1,14 @@
1
1
  import { type GeoJSONVTSourceDiff } from './difference';
2
- import type { ClusterOrPointFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions } from './definitions';
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. Returns the affected and updated source features.
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: SuperclusterOptions): void;
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.
@@ -1 +1 @@
1
- {"version":3,"file":"geojsonvt.d.ts","sourceRoot":"","sources":["../src/geojsonvt.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAGvE,OAAO,KAAK,EAAC,qBAAqB,EAAgD,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAE7J,eAAO,MAAM,cAAc,EAAE,gBAe5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,OAAO,CAAmB;IAElC,OAAO,CAAC,MAAM,CAAC,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAqB;gBAE1B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,gBAAgB;IA8B7D,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;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO;IAK1B;;;;OAIG;IACH,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB;IAa1E;;;;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"}
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
@@ -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;AACpqB,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,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EACrB,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"}
@@ -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: all of them in a wrapping world, where wrapping already put them inside,
28
- * and only what lies inside the world square and its buffer with `worldCoordinates`. The index keeps the rest, so
29
- * an update can still move a feature in from outside.
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
  /**
@@ -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;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAOnB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS;IA4FjB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuE1B"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maplibre/geojson-vt",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "description": "Slice GeoJSON data into vector tiles efficiently",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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 world coordinates with worldCoordinates', () => {
222
- const index = new ClusterTileIndex({}, true);
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 with worldCoordinates', () => {
232
- const index = new ClusterTileIndex({}, true);
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 with worldCoordinates', () => {
242
- const index = new ClusterTileIndex({}, true);
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 with worldCoordinates', () => {
297
- const index = new ClusterTileIndex({}, true);
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 {projectX, projectY} from './convert';
3
- import {unprojectX, unprojectY, featureToGeoJSON} from './deconvert';
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, worldCoordinates = false) {
94
+ constructor(options?: SuperclusterOptions) {
99
95
  this.options = createOptions(options);
100
- this.worldCoordinates = worldCoordinates;
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.worldCoordinates), projectY(lat, this.worldCoordinates)];
124
- if (this.worldCoordinates && !isInWorld([x, y])) continue;
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.worldCoordinates && !isInWorld(feature.geometry)) continue;
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.worldCoordinates && !isInWorld(geometry)) continue;
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 world coordinates.
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 = this.worldCoordinates ? this.getClustersInRange(bbox, zoom) : this.getClustersInternal(bbox, zoom);
250
- return clusterInternal.map((f) => featureToGeoJSON(f, this.worldCoordinates) as ClusterOrPointFeature);
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.worldCoordinates) : featureToGeoJSON(this.points[data[k + OFFSET_ID]], this.worldCoordinates) as GeoJSON.Feature<GeoJSON.Point>);
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.worldCoordinates && (x < 0 || x >= z2 || y < 0 || y >= z2)) return null;
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 (!this.worldCoordinates && x === 0) {
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 (!this.worldCoordinates && x === z2 - 1) {
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>[], worldCoordinates: boolean): ClusterFeature {
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]), worldCoordinates), unprojectY(decode(coords[index * 2 + 1]), worldCoordinates)]
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
- featureToInternal(features, data.features[i], options, i);
22
+ convertFeature(features, data.features[i], options, unlocated, i);
21
23
  }
22
24
  break;
23
25
  case 'Feature':
24
- featureToInternal(features, data, options);
26
+ convertFeature(features, data, options, unlocated);
25
27
  break;
26
28
  default:
27
- featureToInternal(features, {type: "Feature" as const, geometry: data, properties: undefined}, options);
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 worldCoordinates = options.worldCoordinates === true;
66
+ const worldCoordinateHelper = getWorldCoordinateHelper(options.coordinates);
51
67
 
52
68
  switch (geojson.geometry.type) {
53
69
  case 'Point':
54
- convertPointFeature(features, id, geojson.geometry, worldCoordinates, geojson.properties);
70
+ convertPointFeature(features, id, geojson.geometry, worldCoordinateHelper, geojson.properties);
55
71
  return;
56
72
 
57
73
  case 'MultiPoint':
58
- convertMultiPointFeature(features, id, geojson.geometry, worldCoordinates, geojson.properties);
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, worldCoordinates, geojson.properties);
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, worldCoordinates, options, geojson.properties);
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, worldCoordinates, geojson.properties);
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, worldCoordinates, geojson.properties);
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, worldCoordinates: boolean, properties: GeoJSON.GeoJsonProperties) {
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], worldCoordinates), projectY(geom.coordinates[1], worldCoordinates), 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, worldCoordinates: boolean, properties: GeoJSON.GeoJsonProperties) {
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], worldCoordinates), projectY(coords[1], worldCoordinates), 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, worldCoordinates: boolean, properties: GeoJSON.GeoJsonProperties) {
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, worldCoordinates);
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, worldCoordinates: boolean, options: GeoJSONVTOptions, properties: GeoJSON.GeoJsonProperties) {
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, worldCoordinates);
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, worldCoordinates);
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, worldCoordinates: boolean, properties: GeoJSON.GeoJsonProperties) {
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, worldCoordinates);
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, worldCoordinates: boolean, properties: GeoJSON.GeoJsonProperties) {
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, worldCoordinates);
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, worldCoordinates: 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], worldCoordinates);
164
- const y = projectY(ring[j][1], worldCoordinates);
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, worldCoordinates: 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, worldCoordinates);
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
- }
@@ -1,7 +1,7 @@
1
1
 
2
2
  import {test, expect} from 'vitest';
3
- import {featureToGeoJSON, unprojectX, unprojectY} from './deconvert';
4
- import {projectX, projectY} from './convert';
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