Initial commit
This commit is contained in:
39
node_modules/@maplibre/geojson-vt/LICENSE
generated
vendored
Normal file
39
node_modules/@maplibre/geojson-vt/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 MapLibre contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2024, Mapbox
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
118
node_modules/@maplibre/geojson-vt/README.md
generated
vendored
Normal file
118
node_modules/@maplibre/geojson-vt/README.md
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://maplibre.org/img/maplibre-logos/maplibre-logo-for-dark-bg.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg">
|
||||
<img alt="MapLibre Logo" src="https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg" width="200">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
## geojson-vt — GeoJSON Vector Tiles
|
||||
|
||||
A highly efficient JavaScript library for **slicing GeoJSON data into vector tiles on the fly**,
|
||||
primarily designed to enable rendering and interacting with large geospatial datasets
|
||||
on the browser side (without a server).
|
||||
|
||||
Created to power GeoJSON in [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js),
|
||||
but can be useful in other visualization platforms
|
||||
like [Leaflet](https://github.com/Leaflet/Leaflet), [OpenLayers](https://openlayers.org/) and [d3](https://github.com/mbostock/d3),
|
||||
as well as Node.js server applications.
|
||||
|
||||
Resulting tiles conform to the JSON equivalent
|
||||
of the [vector tile specification](https://github.com/mapbox/vector-tile-spec/).
|
||||
To make data rendering and interaction fast, the tiles are simplified,
|
||||
retaining the minimum level of detail appropriate for each zoom level
|
||||
(simplifying shapes, filtering out tiny polygons and polylines).
|
||||
|
||||
### Demo
|
||||
|
||||
Here's **geojson-vt** action in [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js),
|
||||
dynamically loading a 100Mb US zip codes GeoJSON with 5.4 million points:
|
||||
|
||||

|
||||
|
||||
There's a convenient [debug page](http://maplibre.org/geojson-vt/debug/) to test out **geojson-vt** on different data.
|
||||
Just drag any GeoJSON on the page, watching the console.
|
||||
|
||||

|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
// build an initial index of tiles
|
||||
var tileIndex = new GeoJSONVT(geoJSON);
|
||||
|
||||
// request a particular tile
|
||||
var features = tileIndex.getTile(z, x, y).features;
|
||||
|
||||
// show an array of tile coordinates created so far
|
||||
console.log(tileIndex.tileCoords); // [{z: 0, x: 0, y: 0}, ...]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
You can fine-tune the results with an options object,
|
||||
although the defaults are sensible and work well for most use cases.
|
||||
|
||||
```js
|
||||
var tileIndex = new GeoJSONVT(data, {
|
||||
maxZoom: 14, // max zoom to preserve detail on; can't be higher than 24
|
||||
tolerance: 3, // simplification tolerance (higher means simpler)
|
||||
extent: 4096, // tile extent (both width and height)
|
||||
buffer: 64, // tile buffer on each side
|
||||
debug: 0, // logging level (0 to disable, 1 or 2)
|
||||
lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features
|
||||
promoteId: null, // name of a feature property to promote to feature.id. Cannot be used with `generateId`
|
||||
generateId: false, // whether to generate feature ids. Cannot be used with `promoteId`
|
||||
updateable: false, // whether the tile index can be updated (with the caveat of a stored simplified copy)
|
||||
indexMaxZoom: 5, // max zoom in the initial tile index
|
||||
indexMaxPoints: 100000 // max number of points per tile in the index
|
||||
});
|
||||
```
|
||||
|
||||
By default, tiles at zoom levels above `indexMaxZoom` are generated on the fly, but you can pre-generate all possible tiles for `data` by setting `indexMaxZoom` and `maxZoom` to the same value, setting `indexMaxPoints` to `0`, and then accessing the resulting tile coordinates from the `tileCoords` property of `tileIndex`.
|
||||
|
||||
The `promoteId` and `generateId` options ignore existing `id` values on the feature objects.
|
||||
|
||||
GeoJSON-VT only operates on zoom levels up to 24.
|
||||
|
||||
### Update
|
||||
|
||||
For incremental updates to the tile index, you can use `updateData` to change features without having to recreate a fresh index. `updateData` takes a diff object as a parameter with the following properties:
|
||||
|
||||
```js
|
||||
var diff = {
|
||||
removeAll: false, // set to true to clear all features
|
||||
remove: ['id1', 'id2'], // array of feature ids to remove
|
||||
add: [feature1, feature2], // array of GeoJSON features to add
|
||||
update: [ // array of feature update objects
|
||||
{
|
||||
id: 'feature1', // required - id of feature to update
|
||||
newGeometry: {type: 'Point', ...}, // new geometry for the feature
|
||||
removeAllProperties: false, // remove all properties
|
||||
removeProperties: ['prop1', 'prop2'], // array of property keys to remove
|
||||
addOrUpdateProperties: [ // array of properties to add/update
|
||||
{key: 'name', value: 'New Name'},
|
||||
{key: 'population', value: 5000}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
tileIndex.updateData(diff);
|
||||
```
|
||||
|
||||
All properties in the diff are optional, but at least one operation should be specified. Remove operations are applied before add/update operations.
|
||||
|
||||
To use `updateData`, the index must be created with the `updateable: true` option.
|
||||
|
||||
### Install
|
||||
|
||||
Install using NPM (`npm install @maplibre/geojson-vt`), then:
|
||||
|
||||
```js
|
||||
import {GeoJSONVT} from '@maplibre/geojson-vt';
|
||||
```
|
||||
|
||||
### Getting Involved
|
||||
|
||||
Join the #maplibre slack channel at OSMUS: get an invite at https://slack.openstreetmap.us/
|
||||
22
node_modules/@maplibre/geojson-vt/dist/clip.d.ts
generated
vendored
Normal file
22
node_modules/@maplibre/geojson-vt/dist/clip.d.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
|
||||
export declare const enum AxisType {
|
||||
X = 0,
|
||||
Y = 1
|
||||
}
|
||||
/**
|
||||
* clip features between two vertical or horizontal axis-parallel lines:
|
||||
* | |
|
||||
* ___|___ | /
|
||||
* / | \____|____/
|
||||
* | |
|
||||
*
|
||||
* @param features - the features to clip
|
||||
* @param scale - the scale to divide start and end inputs
|
||||
* @param start - the start of the clip range
|
||||
* @param end - the end of the clip range
|
||||
* @param axis - which axis to clip against
|
||||
* @param minAll - the minimum for all features in the relevant axis
|
||||
* @param maxAll - the maximum for all features in the relevant axis
|
||||
*/
|
||||
export declare function clip(features: GeoJSONVTInternalFeature[], scale: number, start: number, end: number, axis: AxisType, minAll: number, maxAll: number, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] | null;
|
||||
//# sourceMappingURL=clip.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/clip.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/clip.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"clip.d.ts","sourceRoot":"","sources":["../src/clip.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAyN,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAE1T,0BAAkB,QAAQ;IACtB,CAAC,IAAI;IACL,CAAC,IAAI;CACR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,GAAG,IAAI,CA2DlN"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/clip.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/clip.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=clip.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/clip.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/clip.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"clip.test.d.ts","sourceRoot":"","sources":["../src/clip.test.ts"],"names":[],"mappings":""}
|
||||
76
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.d.ts
generated
vendored
Normal file
76
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import KDBush from 'kdbush';
|
||||
import type { ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalFeature, GeoJSONVTInternalPointFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions } from './definitions';
|
||||
/** @internal */
|
||||
export type KDBushWithData = KDBush & {
|
||||
flatData: number[];
|
||||
};
|
||||
export declare const defaultClusterOptions: Required<SuperclusterOptions>;
|
||||
/**
|
||||
* This class allow clustering of geojson points.
|
||||
*/
|
||||
export declare class ClusterTileIndex implements GeoJSONVTTileIndex {
|
||||
options: Required<SuperclusterOptions>;
|
||||
trees: KDBushWithData[];
|
||||
stride: number;
|
||||
clusterProps: Record<string, unknown>[];
|
||||
points: GeoJSONVTInternalPointFeature[];
|
||||
constructor(options?: SuperclusterOptions);
|
||||
/**
|
||||
* Loads GeoJSON point features and builds the internal clustering index.
|
||||
* @param points - GeoJSON point features to cluster.
|
||||
*/
|
||||
load(points: GeoJSON.Feature<GeoJSON.Point>[]): void;
|
||||
/**
|
||||
* @internal
|
||||
* Loads internal GeoJSONVT point features from a data source and builds the clustering index.
|
||||
* @param features - {@link GeoJSONVTInternalFeature} data source features to filter and cluster.
|
||||
*/
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void;
|
||||
/**
|
||||
* @internal
|
||||
* Updates the cluster data by rebuilding.
|
||||
* @param features
|
||||
*/
|
||||
updateIndex(features: GeoJSONVTInternalFeature[], _affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void;
|
||||
private createIndex;
|
||||
/**
|
||||
* Returns clusters and/or points within a bounding box at a given zoom level.
|
||||
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order.
|
||||
* @param zoom - Zoom level to query.
|
||||
*/
|
||||
getClusters(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeature[];
|
||||
private getClustersInternal;
|
||||
/**
|
||||
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
|
||||
* @param clusterId - The target cluster id.
|
||||
*/
|
||||
getChildren(clusterId: number): ClusterOrPointFeature[];
|
||||
/**
|
||||
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @param limit - Maximum number of points to return (defaults to `10`).
|
||||
* @param offset - Number of points to skip before collecting results (defaults to `0`).
|
||||
*/
|
||||
getLeaves(clusterId: number, limit?: number, offset?: number): GeoJSON.Feature<GeoJSON.Point>[];
|
||||
/**
|
||||
* Generates a vector-tile-like representation of a single tile.
|
||||
* @param z - Tile zoom.
|
||||
* @param x - Tile x coordinate.
|
||||
* @param y - Tile y coordinate.
|
||||
*/
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null;
|
||||
/**
|
||||
* Returns the zoom level at which a cluster expands into multiple children.
|
||||
* @param clusterId - The target cluster id.
|
||||
*/
|
||||
getClusterExpansionZoom(clusterId: number): number;
|
||||
private appendLeaves;
|
||||
private createTree;
|
||||
private addTileFeatures;
|
||||
private limitZoom;
|
||||
private cluster;
|
||||
private getOriginId;
|
||||
private getOriginZoom;
|
||||
private map;
|
||||
}
|
||||
//# sourceMappingURL=cluster-tile-index.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cluster-tile-index.d.ts","sourceRoot":"","sources":["../src/cluster-tile-index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,OAAO,KAAK,EAAiB,qBAAqB,EAAqB,kBAAkB,EAAoB,wBAAwB,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAQjP,gBAAgB;AAChB,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,mBAAmB,CAW/D,CAAC;AAQF;;GAEG;AACH,qBAAa,gBAAiB,YAAW,kBAAkB;IACvD,OAAO,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IACvC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACxC,MAAM,EAAE,6BAA6B,EAAE,CAAC;gBAE5B,OAAO,CAAC,EAAE,mBAAmB;IAQzC;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI;IAyBpD;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI;IAWtD;;;;OAIG;IACH,WAAW,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,SAAS,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB;IAKlH,OAAO,CAAC,WAAW;IAiDnB;;;;OAIG;IACI,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,EAAE;IAKjG,OAAO,CAAC,mBAAmB;IA0B3B;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,qBAAqB,EAAE;IA4BvD;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;IAU/F;;;;;OAKG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAsC9D;;;OAGG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIlD,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,eAAe;IA2CvB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,OAAO;IAmFf,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,GAAG;CASd"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=cluster-tile-index.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/cluster-tile-index.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cluster-tile-index.test.d.ts","sourceRoot":"","sources":["../src/cluster-tile-index.test.ts"],"names":[],"mappings":""}
|
||||
17
node_modules/@maplibre/geojson-vt/dist/convert.d.ts
generated
vendored
Normal file
17
node_modules/@maplibre/geojson-vt/dist/convert.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
|
||||
/**
|
||||
* converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
|
||||
* @param data
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export declare function convertToInternal(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[];
|
||||
/**
|
||||
* Convert longitude to spherical mercator in [0..1] range
|
||||
*/
|
||||
export declare function projectX(x: number): number;
|
||||
/**
|
||||
* Convert latitude to spherical mercator in [0..1] range
|
||||
*/
|
||||
export declare function projectY(y: number): number;
|
||||
//# sourceMappingURL=convert.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/convert.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/convert.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,wBAAwB,EAAE,gBAAgB,EAAoB,MAAM,eAAe,CAAC;AAEjG;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,CAiB9G;AA6JD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,UAEjC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,UAIjC"}
|
||||
19
node_modules/@maplibre/geojson-vt/dist/deconvert.d.ts
generated
vendored
Normal file
19
node_modules/@maplibre/geojson-vt/dist/deconvert.d.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { GeoJSONVTInternalFeature } from './definitions';
|
||||
/**
|
||||
* Converts internal source features back to GeoJSON format.
|
||||
*/
|
||||
export declare function convertToGeoJSON(source: GeoJSONVTInternalFeature[]): GeoJSON.GeoJSON;
|
||||
/**
|
||||
* Converts a single internal feature to GeoJSON format.
|
||||
*/
|
||||
export declare function featureToGeoJSON(feature: GeoJSONVTInternalFeature): GeoJSON.Feature;
|
||||
export declare function unprojectPoints(coords: number[]): GeoJSON.Position[];
|
||||
/**
|
||||
* Convert spherical mercator in [0..1] range to longitude
|
||||
*/
|
||||
export declare function unprojectX(x: number): number;
|
||||
/**
|
||||
* Convert spherical mercator in [0..1] range to latitude
|
||||
*/
|
||||
export declare function unprojectY(y: number): number;
|
||||
//# sourceMappingURL=deconvert.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/deconvert.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/deconvert.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"deconvert.d.ts","sourceRoot":"","sources":["../src/deconvert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,eAAe,CAAC;AAE5D;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,wBAAwB,EAAE,GAAG,OAAO,CAAC,OAAO,CAOpF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,OAAO,CAWnF;AAoCD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAQpE;AAMD;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG5C"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/deconvert.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/deconvert.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=deconvert.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/deconvert.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/deconvert.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"deconvert.test.d.ts","sourceRoot":"","sources":["../src/deconvert.test.ts"],"names":[],"mappings":""}
|
||||
241
node_modules/@maplibre/geojson-vt/dist/definitions.d.ts
generated
vendored
Normal file
241
node_modules/@maplibre/geojson-vt/dist/definitions.d.ts
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
export type GeoJSONVTOptions = {
|
||||
/**
|
||||
* Max zoom to preserve detail on
|
||||
* @default 14
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* Max zoom in the tile index
|
||||
* @default 5
|
||||
*/
|
||||
indexMaxZoom?: number;
|
||||
/**
|
||||
* Max number of points per tile in the tile index
|
||||
* @default 100000
|
||||
*/
|
||||
indexMaxPoints?: number;
|
||||
/**
|
||||
* Simplification tolerance (higher means simpler)
|
||||
* @default 3
|
||||
*/
|
||||
tolerance?: number;
|
||||
/**
|
||||
* Tile extent
|
||||
* @default 4096
|
||||
*/
|
||||
extent?: number;
|
||||
/**
|
||||
* Tile buffer on each side
|
||||
* @default 64
|
||||
*/
|
||||
buffer?: number;
|
||||
/**
|
||||
* Whether to calculate line metrics
|
||||
* @default false
|
||||
*/
|
||||
lineMetrics?: boolean;
|
||||
/**
|
||||
* Name of a feature property to be promoted to feature.id
|
||||
*/
|
||||
promoteId?: string | null;
|
||||
/**
|
||||
* Whether to generate feature ids. Cannot be used with promoteId
|
||||
* @default false
|
||||
*/
|
||||
generateId?: boolean;
|
||||
/**
|
||||
* Whether geojson can be updated (with caveat of a stored simplified copy)
|
||||
* @default false
|
||||
*/
|
||||
updateable?: boolean;
|
||||
/**
|
||||
* Logging level (0, 1 or 2)
|
||||
* @default 0
|
||||
*/
|
||||
debug?: number;
|
||||
/**
|
||||
* Enable Supercluster for point features.
|
||||
* @default false
|
||||
*/
|
||||
cluster?: boolean;
|
||||
/**
|
||||
* Options for the Supercluster point clustering algorithm.
|
||||
* @see {@link SuperclusterOptions}
|
||||
*/
|
||||
clusterOptions?: SuperclusterOptions;
|
||||
};
|
||||
export type GeoJSONToTileOptions = GeoJSONVTOptions & {
|
||||
/**
|
||||
* Whether to wrap features around the antimeridian
|
||||
* @default false
|
||||
*/
|
||||
wrap?: boolean;
|
||||
/**
|
||||
* Whether to clip features to the tile boundary
|
||||
* @default false
|
||||
*/
|
||||
clip?: boolean;
|
||||
};
|
||||
export type StartEndSizeArray = number[] & {
|
||||
start?: number;
|
||||
end?: number;
|
||||
size?: number;
|
||||
};
|
||||
export type PartialGeoJSONVTFeature = {
|
||||
id?: number | string | undefined;
|
||||
tags: GeoJSON.GeoJsonProperties;
|
||||
minX?: number;
|
||||
minY?: number;
|
||||
maxX?: number;
|
||||
maxY?: number;
|
||||
};
|
||||
export type GeoJSONVTInternalPointFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'Point';
|
||||
geometry: number[];
|
||||
};
|
||||
export type GeoJSONVTInternalMultiPointFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiPoint';
|
||||
geometry: number[];
|
||||
};
|
||||
export type GeoJSONVTInternalLineStringFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'LineString';
|
||||
geometry: StartEndSizeArray;
|
||||
};
|
||||
export type GeoJSONVTInternalMultiLineStringFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiLineString';
|
||||
geometry: StartEndSizeArray[];
|
||||
};
|
||||
export type GeoJSONVTInternalPolygonFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'Polygon';
|
||||
geometry: StartEndSizeArray[];
|
||||
};
|
||||
export type GeoJSONVTInternalMultiPolygonFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiPolygon';
|
||||
geometry: StartEndSizeArray[][];
|
||||
};
|
||||
export type GeoJSONVTInternalFeature = GeoJSONVTInternalPointFeature | GeoJSONVTInternalMultiPointFeature | GeoJSONVTInternalLineStringFeature | GeoJSONVTInternalMultiLineStringFeature | GeoJSONVTInternalPolygonFeature | GeoJSONVTInternalMultiPolygonFeature;
|
||||
/**
|
||||
* The geojson properies related to a cluster.
|
||||
*/
|
||||
export type ClusterProperties = {
|
||||
cluster: true;
|
||||
cluster_id: number;
|
||||
point_count: number;
|
||||
point_count_abbreviated: string | number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
/**
|
||||
* A geojson point with cluster properties, see {@link ClusterProperties}.
|
||||
*/
|
||||
export type ClusterFeature = GeoJSON.Feature<GeoJSON.Point, ClusterProperties>;
|
||||
/**
|
||||
* A geojson point that is either a regular point or a cluster, which is a point with cluster properties.
|
||||
* See {@link ClusterFeature} for more information
|
||||
*/
|
||||
export type ClusterOrPointFeature = ClusterFeature | GeoJSON.Feature<GeoJSON.Point>;
|
||||
export type GeoJSONVTInternalTileFeaturePoint = {
|
||||
id?: number | string | undefined;
|
||||
type: 1;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: number[];
|
||||
};
|
||||
export type GeoJSONVTInternalTileFeatureNonPoint = {
|
||||
id?: number | string | undefined;
|
||||
type: 2 | 3;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: number[][];
|
||||
};
|
||||
export type GeoJSONVTInternalTileFeature = GeoJSONVTInternalTileFeaturePoint | GeoJSONVTInternalTileFeatureNonPoint;
|
||||
export type GeoJSONVTInternalTile = {
|
||||
transformed: boolean;
|
||||
features: GeoJSONVTInternalTileFeature[];
|
||||
source: GeoJSONVTInternalFeature[] | null;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
minX?: number;
|
||||
minY?: number;
|
||||
maxX?: number;
|
||||
maxY?: number;
|
||||
numPoints?: number;
|
||||
numSimplified?: number;
|
||||
numFeatures?: number;
|
||||
};
|
||||
export type GeoJSONVTFeaturePoint = {
|
||||
id?: number | string | undefined;
|
||||
type: 1;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: [number, number][];
|
||||
};
|
||||
export type GeoJSONVTFeatureNonPoint = {
|
||||
id?: number | string | undefined;
|
||||
type: 2 | 3;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: [number, number][][];
|
||||
};
|
||||
export type GeoJSONVTFeature = GeoJSONVTFeaturePoint | GeoJSONVTFeatureNonPoint;
|
||||
export type GeoJSONVTTile = GeoJSONVTInternalTile & {
|
||||
transformed: true;
|
||||
features: GeoJSONVTFeature[];
|
||||
};
|
||||
export interface GeoJSONVTTileIndex {
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void;
|
||||
updateIndex(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void;
|
||||
getClusterExpansionZoom(clusterId: number): number | null;
|
||||
getChildren(clusterId: number): ClusterOrPointFeature[] | null;
|
||||
getLeaves(clusterId: number, limit?: number, offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null;
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null;
|
||||
}
|
||||
export type SuperclusterOptions = {
|
||||
/**
|
||||
* Min zoom to generate clusters on
|
||||
* @default 0
|
||||
*/
|
||||
minZoom?: number;
|
||||
/**
|
||||
* Max zoom level to cluster the points on
|
||||
* @default 16
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* Minimum points to form a cluster
|
||||
* @default 2
|
||||
*/
|
||||
minPoints?: number;
|
||||
/**
|
||||
* Cluster radius in pixels
|
||||
* @default 40
|
||||
*/
|
||||
radius?: number;
|
||||
/**
|
||||
* Tile extent (radius is calculated relative to it)
|
||||
* @default 512
|
||||
*/
|
||||
extent?: number;
|
||||
/**
|
||||
* Size of the KD-tree leaf node, affects performance
|
||||
* @default 64
|
||||
*/
|
||||
nodeSize?: number;
|
||||
/**
|
||||
* Whether to log timing info
|
||||
* @default false
|
||||
*/
|
||||
log?: boolean;
|
||||
/**
|
||||
* Whether to generate numeric ids for input features (in vector tiles)
|
||||
* @default false
|
||||
*/
|
||||
generateId?: boolean;
|
||||
/**
|
||||
* A reduce function for calculating custom cluster properties
|
||||
* @default null
|
||||
*/
|
||||
reduce?: ((accumulated: Record<string, unknown>, props: Record<string, unknown>) => void) | null;
|
||||
/**
|
||||
* Properties to use for individual points when running the reducer
|
||||
* @default props => props
|
||||
*/
|
||||
map?: (props: GeoJSON.GeoJsonProperties) => Record<string, unknown>;
|
||||
};
|
||||
//# sourceMappingURL=definitions.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/definitions.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/definitions.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
74
node_modules/@maplibre/geojson-vt/dist/difference.d.ts
generated
vendored
Normal file
74
node_modules/@maplibre/geojson-vt/dist/difference.d.ts
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
|
||||
export type GeoJSONVTSourceDiff = {
|
||||
/**
|
||||
* If true, clear all existing features
|
||||
*/
|
||||
removeAll?: boolean;
|
||||
/**
|
||||
* Array of feature IDs to remove
|
||||
*/
|
||||
remove?: (string | number)[];
|
||||
/**
|
||||
* Array of GeoJSON features to add
|
||||
*/
|
||||
add?: GeoJSON.Feature[];
|
||||
/**
|
||||
* Array of per-feature updates
|
||||
*/
|
||||
update?: GeoJSONVTFeatureDiff[];
|
||||
};
|
||||
export type GeoJSONVTFeatureDiff = {
|
||||
/**
|
||||
* ID of the feature being updated
|
||||
*/
|
||||
id: string | number;
|
||||
/**
|
||||
* Optional new geometry
|
||||
*/
|
||||
newGeometry?: GeoJSON.Geometry;
|
||||
/**
|
||||
* Remove all properties if true
|
||||
*/
|
||||
removeAllProperties?: boolean;
|
||||
/**
|
||||
* Specific properties to delete
|
||||
*/
|
||||
removeProperties?: string[];
|
||||
/**
|
||||
* Properties to add or update
|
||||
*/
|
||||
addOrUpdateProperties?: {
|
||||
key: string;
|
||||
value: unknown;
|
||||
}[];
|
||||
};
|
||||
export type ApplySourceDiffResult = {
|
||||
/**
|
||||
* The features affected by this update, which should be used to invalidate tiles
|
||||
*/
|
||||
affected: GeoJSONVTInternalFeature[];
|
||||
/**
|
||||
* The updated source data, which should replace the existing source data in the index
|
||||
*/
|
||||
source: GeoJSONVTInternalFeature[];
|
||||
};
|
||||
type HashedGeoJSONVTSourceDiff = {
|
||||
removeAll?: boolean | undefined;
|
||||
remove: Set<string | number>;
|
||||
add: Map<string | number | undefined, GeoJSON.Feature>;
|
||||
update: Map<string | number, GeoJSONVTFeatureDiff>;
|
||||
};
|
||||
/**
|
||||
* Applies a GeoJSON Source Diff to an existing set of simplified features
|
||||
* @param source
|
||||
* @param dataDiff
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export declare function applySourceDiff(source: GeoJSONVTInternalFeature[], dataDiff: GeoJSONVTSourceDiff, options: GeoJSONVTOptions): ApplySourceDiffResult;
|
||||
/**
|
||||
* Convert a GeoJSON Source Diff to an idempotent hashed representation using Sets and Maps
|
||||
*/
|
||||
export declare function diffToHashed(diff: GeoJSONVTSourceDiff): HashedGeoJSONVTSourceDiff;
|
||||
export {};
|
||||
//# sourceMappingURL=difference.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/difference.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/difference.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"difference.d.ts","sourceRoot":"","sources":["../src/difference.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,mBAAmB,GAAG;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC7B;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;OAEG;IACH,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;IAC/B;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;OAEG;IACH,qBAAqB,CAAC,EAAE;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,OAAO,CAAC;KAClB,EAAE,CAAC;CACP,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAChC;;OAEG;IACH,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC;;OAEG;IACH,MAAM,EAAE,wBAAwB,EAAE,CAAC;CACtC,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC7B,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC7B,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,oBAAoB,CAAC,CAAC;CACtD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,qBAAqB,CA6DnJ;AAqED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,yBAAyB,CAejF"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/difference.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/difference.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=difference.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/difference.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/difference.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"difference.test.d.ts","sourceRoot":"","sources":["../src/difference.test.ts"],"names":[],"mappings":""}
|
||||
20
node_modules/@maplibre/geojson-vt/dist/feature.d.ts
generated
vendored
Normal file
20
node_modules/@maplibre/geojson-vt/dist/feature.d.ts
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature } from "./definitions";
|
||||
type FeatureTypeMap = {
|
||||
Point: GeoJSONVTInternalPointFeature["geometry"];
|
||||
MultiPoint: GeoJSONVTInternalMultiPointFeature["geometry"];
|
||||
LineString: GeoJSONVTInternalLineStringFeature["geometry"];
|
||||
MultiLineString: GeoJSONVTInternalMultiLineStringFeature["geometry"];
|
||||
Polygon: GeoJSONVTInternalPolygonFeature["geometry"];
|
||||
MultiPolygon: GeoJSONVTInternalMultiPolygonFeature["geometry"];
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param id - the feature's ID
|
||||
* @param type - the feature's type
|
||||
* @param geom - the feature's geometry
|
||||
* @param tags - the feature's properties
|
||||
* @returns the created feature
|
||||
*/
|
||||
export declare function createFeature<T extends GeoJSONVTInternalFeature["type"]>(id: number | string | undefined, type: T, geom: FeatureTypeMap[T], tags: GeoJSON.GeoJsonProperties): GeoJSONVTInternalFeature;
|
||||
export {};
|
||||
//# sourceMappingURL=feature.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/feature.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/feature.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,kCAAkC,EAAE,uCAAuC,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAErR,KAAK,cAAc,GAAG;IAClB,KAAK,EAAE,6BAA6B,CAAC,UAAU,CAAC,CAAC;IACjD,UAAU,EAAE,kCAAkC,CAAC,UAAU,CAAC,CAAC;IAC3D,UAAU,EAAE,kCAAkC,CAAC,UAAU,CAAC,CAAC;IAC3D,eAAe,EAAE,uCAAuC,CAAC,UAAU,CAAC,CAAC;IACrE,OAAO,EAAE,+BAA+B,CAAC,UAAU,CAAC,CAAC;IACrD,YAAY,EAAE,oCAAoC,CAAC,UAAU,CAAC,CAAC;CAClE,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,wBAAwB,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,iBAAiB,GAAG,wBAAwB,CA0CtM"}
|
||||
35
node_modules/@maplibre/geojson-vt/dist/geojson-to-tile.d.ts
generated
vendored
Normal file
35
node_modules/@maplibre/geojson-vt/dist/geojson-to-tile.d.ts
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { GeoJSONToTileOptions, GeoJSONVTTile } from './definitions';
|
||||
/**
|
||||
* Converts GeoJSON data directly to a single vector tile without building a tile index.
|
||||
*
|
||||
* Unlike the {@link GeoJSONVT} class which builds a hierarchical tile index for efficient
|
||||
* repeated tile access, this function generates a single tile on-demand. This is useful when:
|
||||
* - You only need one specific tile and don't need to query multiple tiles
|
||||
* - The source data is already spatially filtered to the tile's bounding box
|
||||
* - You want to avoid the overhead of building a full tile index
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import {geoJSONToTile} from '@maplibre/geojson-vt';
|
||||
*
|
||||
* const geojson = {
|
||||
* type: 'FeatureCollection',
|
||||
* features: [{
|
||||
* type: 'Feature',
|
||||
* geometry: { type: 'Point', coordinates: [-77.03, 38.90] },
|
||||
* properties: { name: 'Washington, D.C.' }
|
||||
* }]
|
||||
* };
|
||||
*
|
||||
* const tile = geoJSONToTile(geojson, 10, 292, 391, { extent: 4096 });
|
||||
* ```
|
||||
*
|
||||
* @param data - GeoJSON data (Feature, FeatureCollection, or Geometry)
|
||||
* @param z - Tile zoom level
|
||||
* @param x - Tile x coordinate
|
||||
* @param y - Tile y coordinate
|
||||
* @param options - Optional configuration for tile generation
|
||||
* @returns The generated tile with geometries in tile coordinates, or null if no features
|
||||
*/
|
||||
export declare function geoJSONToTile(data: GeoJSON.GeoJSON, z: number, x: number, y: number, options?: GeoJSONToTileOptions): GeoJSONVTTile;
|
||||
//# sourceMappingURL=geojson-to-tile.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/geojson-to-tile.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/geojson-to-tile.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"geojson-to-tile.d.ts","sourceRoot":"","sources":["../src/geojson-to-tile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAgBvI"}
|
||||
2293
node_modules/@maplibre/geojson-vt/dist/geojson-vt-dev.js
generated
vendored
Normal file
2293
node_modules/@maplibre/geojson-vt/dist/geojson-vt-dev.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/geojson-vt/dist/geojson-vt.js
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/geojson-vt.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1957
node_modules/@maplibre/geojson-vt/dist/geojson-vt.mjs
generated
vendored
Normal file
1957
node_modules/@maplibre/geojson-vt/dist/geojson-vt.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/geojson-vt/dist/geojson-vt.mjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/geojson-vt.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
76
node_modules/@maplibre/geojson-vt/dist/geojsonvt.d.ts
generated
vendored
Normal file
76
node_modules/@maplibre/geojson-vt/dist/geojsonvt.d.ts
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import { type GeoJSONVTSourceDiff } from './difference';
|
||||
import type { ClusterOrPointFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions } from './definitions';
|
||||
export declare const defaultOptions: GeoJSONVTOptions;
|
||||
/**
|
||||
* Main class for creating and managing a vector tile index from GeoJSON data.
|
||||
*/
|
||||
export declare class GeoJSONVT {
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
get tiles(): any;
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
get stats(): any;
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
get total(): any;
|
||||
private options;
|
||||
private source?;
|
||||
private tileIndex;
|
||||
constructor(data: GeoJSON.GeoJSON, options?: GeoJSONVTOptions);
|
||||
private initializeIndex;
|
||||
/**
|
||||
* Given z, x, and y tile coordinates, returns the corresponding tile with geometries in tile coordinates, much like MVT data is stored.
|
||||
* @param z - tile zoom level
|
||||
* @param x - tile x coordinate
|
||||
* @param y - tile y coordinate
|
||||
* @returns the transformed tile or null if not found
|
||||
*/
|
||||
getTile(z: number | string, x: number | string, y: number | string): GeoJSONVTTile | null;
|
||||
/**
|
||||
* Updates the source data feature set using a {@link GeoJSONVTSourceDiff}
|
||||
* @param diff - the source diff object
|
||||
*/
|
||||
updateData(diff: GeoJSONVTSourceDiff, filter?: (feature: GeoJSON.Feature) => boolean): void;
|
||||
/**
|
||||
* Filter an update using a predicate function. Returns the affected and updated source features.
|
||||
*/
|
||||
private filterUpdate;
|
||||
/**
|
||||
* Returns source data as GeoJSON - only available when `updateable` option is set to true.
|
||||
*/
|
||||
getData(): GeoJSON.GeoJSON;
|
||||
/**
|
||||
* Update supercluster options and regenerate the index.
|
||||
* @param cluster - whether to enable clustering
|
||||
* @param clusterOptions - {@link SuperclusterOptions}
|
||||
*/
|
||||
updateClusterOptions(cluster: boolean, clusterOptions: SuperclusterOptions): void;
|
||||
/**
|
||||
* Returns the zoom level at which a cluster expands into multiple children.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @returns the expansion zoom or null in case of non-clustered source
|
||||
*/
|
||||
getClusterExpansionZoom(clusterId: number): number | null;
|
||||
/**
|
||||
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @returns the immediate children or null in case of non-clustered source
|
||||
*/
|
||||
getClusterChildren(clusterId: number): ClusterOrPointFeature[] | null;
|
||||
/**
|
||||
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @param limit - Maximum number of points to return (defaults to `10`).
|
||||
* @param offset - Number of points to skip before collecting results (defaults to `0`).
|
||||
* @returns leaf point features under a cluster or null in case of non-clustered source
|
||||
*/
|
||||
getClusterLeaves(clusterId: number, limit: number, offset: number): GeoJSON.Feature<GeoJSON.Point>[] | null;
|
||||
}
|
||||
//# sourceMappingURL=geojsonvt.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/geojsonvt.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/geojsonvt.d.ts.map
generated
vendored
Normal file
@@ -0,0 +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,gBAc5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAElB;;;OAGG;IACH,IAAW,KAAK,QAGf;IACD;;;OAGG;IACH,IAAW,KAAK,QAGf;IACA;;;MAGE;IACH,IAAW,KAAK,QAGf;IAED,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"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/geojsonvt.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/geojsonvt.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=geojsonvt.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/geojsonvt.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/geojsonvt.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"geojsonvt.test.d.ts","sourceRoot":"","sources":["../src/geojsonvt.test.ts"],"names":[],"mappings":""}
|
||||
10
node_modules/@maplibre/geojson-vt/dist/index.d.ts
generated
vendored
Normal file
10
node_modules/@maplibre/geojson-vt/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { GeoJSONVTFeatureDiff, GeoJSONVTSourceDiff } from './difference';
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTOptions, GeoJSONToTileOptions, PartialGeoJSONVTFeature, StartEndSizeArray, ClusterProperties, ClusterFeature, ClusterOrPointFeature, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, GeoJSONVTTileIndex, SuperclusterOptions } from './definitions';
|
||||
import type { KDBushWithData } from './cluster-tile-index';
|
||||
import { GeoJSONVT } from './geojsonvt';
|
||||
import { ClusterTileIndex } from './cluster-tile-index';
|
||||
import { geoJSONToTile } from './geojson-to-tile';
|
||||
import { GEOJSONVT_CLIP_START, GEOJSONVT_CLIP_END } from './tile';
|
||||
export { GeoJSONVT, ClusterTileIndex as Supercluster, geoJSONToTile, GEOJSONVT_CLIP_START, GEOJSONVT_CLIP_END };
|
||||
export type { GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONToTileOptions, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, PartialGeoJSONVTFeature, StartEndSizeArray, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTSourceDiff, GeoJSONVTFeatureDiff, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalPointFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTInternalMultiPolygonFeature, SuperclusterOptions, ClusterProperties, ClusterFeature, ClusterOrPointFeature, KDBushWithData, GeoJSONVTTileIndex };
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/index.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +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,iBAAiB,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;AAC1pB,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,iBAAiB,EACjB,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"}
|
||||
9
node_modules/@maplibre/geojson-vt/dist/simplify.d.ts
generated
vendored
Normal file
9
node_modules/@maplibre/geojson-vt/dist/simplify.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* calculate simplification data using optimized Douglas-Peucker algorithm
|
||||
* @param coords - flat array of coordinates
|
||||
* @param first - index of the first coordinate in the segment
|
||||
* @param last - index of the last coordinate in the segment
|
||||
* @param sqTolerance - square tolerance value
|
||||
*/
|
||||
export declare function simplify(coords: number[], first: number, last: number, sqTolerance: number): void;
|
||||
//# sourceMappingURL=simplify.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/simplify.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/simplify.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"simplify.d.ts","sourceRoot":"","sources":["../src/simplify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAqC1F"}
|
||||
2
node_modules/@maplibre/geojson-vt/dist/simplify.test.d.ts
generated
vendored
Normal file
2
node_modules/@maplibre/geojson-vt/dist/simplify.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=simplify.test.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/simplify.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/simplify.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"simplify.test.d.ts","sourceRoot":"","sources":["../src/simplify.test.ts"],"names":[],"mappings":""}
|
||||
51
node_modules/@maplibre/geojson-vt/dist/tile-index.d.ts
generated
vendored
Normal file
51
node_modules/@maplibre/geojson-vt/dist/tile-index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions, ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalTile, GeoJSONVTTile } from "./definitions";
|
||||
export declare class TileIndex implements GeoJSONVTTileIndex {
|
||||
private options;
|
||||
private tileCoords;
|
||||
/** @internal */
|
||||
tiles: {
|
||||
[key: string]: GeoJSONVTInternalTile;
|
||||
};
|
||||
/** @internal */
|
||||
stats: {
|
||||
[key: string]: number;
|
||||
};
|
||||
/** @internal */
|
||||
total: number;
|
||||
constructor(options: GeoJSONVTOptions);
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void;
|
||||
/** {@inheritdoc} */
|
||||
updateIndex(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void;
|
||||
/** {@inheritdoc} */
|
||||
getClusterExpansionZoom(_clusterId: number): number | null;
|
||||
/** {@inheritdoc} */
|
||||
getChildren(_clusterId: number): ClusterOrPointFeature[] | null;
|
||||
/** {@inheritdoc} */
|
||||
getLeaves(_clusterId: number, _limit?: number, _offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null;
|
||||
/** {@inheritdoc} */
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null;
|
||||
/**
|
||||
* splits features from a parent tile to sub-tiles.
|
||||
* z, x, and y are the coordinates of the parent tile
|
||||
* cz, cx, and cy are the coordinates of the target tile
|
||||
*
|
||||
* If no target tile is specified, splitting stops when we reach the maximum
|
||||
* zoom or the number of points is low as specified in the options.
|
||||
* @internal
|
||||
* @param features - features to split
|
||||
* @param z - tile zoom level
|
||||
* @param x - tile x coordinate
|
||||
* @param y - tile y coordinate
|
||||
* @param cz - target tile zoom level
|
||||
* @param cx - target tile x coordinate
|
||||
* @param cy - target tile y coordinate
|
||||
*/
|
||||
private splitTile;
|
||||
/**
|
||||
* Invalidates (removes) tiles affected by the provided features
|
||||
* @internal
|
||||
* @param features
|
||||
*/
|
||||
private invalidateTiles;
|
||||
}
|
||||
//# sourceMappingURL=tile-index.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/tile-index.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/tile-index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +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;IAEpE,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;IAC3C,gBAAgB;IACT,KAAK,EAAE,MAAM,CAAK;gBAEL,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;IA2BtH,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;IAwC9D;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS;IA4FjB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuE1B"}
|
||||
14
node_modules/@maplibre/geojson-vt/dist/tile.d.ts
generated
vendored
Normal file
14
node_modules/@maplibre/geojson-vt/dist/tile.d.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalTile, GeoJSONVTOptions } from "./definitions";
|
||||
export declare const GEOJSONVT_CLIP_START = "geojsonvt_clip_start";
|
||||
export declare const GEOJSONVT_CLIP_END = "geojsonvt_clip_end";
|
||||
/**
|
||||
* Creates a tile object from the given features
|
||||
* @param features - the features to include in the tile
|
||||
* @param z
|
||||
* @param tx
|
||||
* @param ty
|
||||
* @param options - the options object
|
||||
* @returns the created tile
|
||||
*/
|
||||
export declare function createTile(features: GeoJSONVTInternalFeature[], z: number, tx: number, ty: number, options: GeoJSONVTOptions): GeoJSONVTInternalTile;
|
||||
//# sourceMappingURL=tile.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/tile.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/tile.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tile.d.ts","sourceRoot":"","sources":["../src/tile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAyN,qBAAqB,EAAgC,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAE/W,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAC3D,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,qBAAqB,CAwBpJ"}
|
||||
10
node_modules/@maplibre/geojson-vt/dist/transform.d.ts
generated
vendored
Normal file
10
node_modules/@maplibre/geojson-vt/dist/transform.d.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { GeoJSONVTInternalTile, GeoJSONVTTile } from "./definitions";
|
||||
/**
|
||||
* Transforms the coordinates of each feature in the given tile from
|
||||
* mercator-projected space into (extent x extent) tile space.
|
||||
* @param tile - the tile to transform, this gets modified in place
|
||||
* @param extent - the tile extent (usually 4096)
|
||||
* @returns the transformed tile
|
||||
*/
|
||||
export declare function transformTile(tile: GeoJSONVTInternalTile, extent: number): GeoJSONVTTile;
|
||||
//# sourceMappingURL=transform.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/transform.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/transform.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmD,qBAAqB,EAA2E,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpM;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAmBxF"}
|
||||
3
node_modules/@maplibre/geojson-vt/dist/wrap.d.ts
generated
vendored
Normal file
3
node_modules/@maplibre/geojson-vt/dist/wrap.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
|
||||
export declare function wrap(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): GeoJSONVTInternalFeature[];
|
||||
//# sourceMappingURL=wrap.d.ts.map
|
||||
1
node_modules/@maplibre/geojson-vt/dist/wrap.d.ts.map
generated
vendored
Normal file
1
node_modules/@maplibre/geojson-vt/dist/wrap.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAGnG,wBAAgB,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,CAehH"}
|
||||
68
node_modules/@maplibre/geojson-vt/package.json
generated
vendored
Normal file
68
node_modules/@maplibre/geojson-vt/package.json
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "@maplibre/geojson-vt",
|
||||
"version": "6.0.4",
|
||||
"description": "Slice GeoJSON data into vector tiles efficiently",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/geojson-vt.mjs",
|
||||
"default": "./dist/geojson-vt.js"
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [
|
||||
"spatial",
|
||||
"geojson",
|
||||
"tiles",
|
||||
"geometry"
|
||||
],
|
||||
"author": "Vladimir Agafonkin",
|
||||
"module": "dist/geojson-vt.mjs",
|
||||
"main": "dist/geojson-vt.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"jsdelivr": "dist/geojson-vt.js",
|
||||
"unpkg": "dist/geojson-vt.js",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-terser": "^1.0.0",
|
||||
"@rollup/plugin-typescript": "^12.3.0",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/node": "^25.5.0",
|
||||
"@vitest/coverage-v8": "^4.1.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"eslint": "^10.0.3",
|
||||
"rollup": "^4.59.0",
|
||||
"tslib": "^2.8.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typedoc": "^0.28.17",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.57.1",
|
||||
"vitest": "^4.0.17"
|
||||
},
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"lint": "eslint",
|
||||
"test": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"build": "rollup -c && tsc -p tsconfig.declaration.json",
|
||||
"watch": "rollup -cw",
|
||||
"start": "npm run watch",
|
||||
"bench": "tsx bench/benchmark.ts",
|
||||
"docs": "typedoc && mkdir -p docs/debug && cp -r debug/* docs/debug && find docs/debug -name '*.html' -exec sed -i 's|../dist/geojson-vt-dev.js|https://unpkg.com/@maplibre/geojson-vt@latest/dist/geojson-vt.js|g' {} +",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maplibre/geojson-vt"
|
||||
},
|
||||
"dependencies": {
|
||||
"kdbush": "^4.0.2"
|
||||
}
|
||||
}
|
||||
79
node_modules/@maplibre/geojson-vt/src/clip.test.ts
generated
vendored
Normal file
79
node_modules/@maplibre/geojson-vt/src/clip.test.ts
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
import {test, expect} from 'vitest';
|
||||
import {clip} from './clip';
|
||||
import type { StartEndSizeArray } from './definitions';
|
||||
|
||||
const geom1 = [0,0,0,50,0,0,50,10,0,20,10,0,20,20,0,30,20,0,30,30,0,50,30,0,50,40,0,25,40,0,25,50,0,0,50,0,0,60,0,25,60,0];
|
||||
const geom2 = [0,0,0,50,0,0,50,10,0,0,10,0];
|
||||
|
||||
test('clips polylines', () => {
|
||||
|
||||
const clipped = clip([
|
||||
{geometry: geom1, type: 'LineString', tags: { "1": 1}, minX: 0, minY: 0, maxX: 50, maxY: 60},
|
||||
{geometry: geom2, type: 'LineString', tags: { "2": 2}, minX: 0, minY: 0, maxX: 50, maxY: 10}
|
||||
], 1, 10, 40, 0, -Infinity, Infinity, {});
|
||||
|
||||
const expected = [
|
||||
{id: null as string, type: 'MultiLineString', geometry: [
|
||||
[10,0,1,40,0,1],
|
||||
[40,10,1,20,10,0,20,20,0,30,20,0,30,30,0,40,30,1],
|
||||
[40,40,1,25,40,0,25,50,0,10,50,1],
|
||||
[10,60,1,25,60,0]], tags: {"1": 1}, minX: 10, minY: 0, maxX: 40, maxY: 60},
|
||||
{id: null as string, type: 'MultiLineString', geometry: [
|
||||
[10,0,1,40,0,1],
|
||||
[40,10,1,10,10,1]], tags: {"2": 2}, minX: 10, minY: 0, maxX: 40, maxY: 10}
|
||||
];
|
||||
|
||||
expect(JSON.stringify(clipped)).toEqual(JSON.stringify(expected));
|
||||
});
|
||||
|
||||
test('clips lines with line metrics on', () => {
|
||||
|
||||
const geom = geom1.slice() as StartEndSizeArray;
|
||||
geom.size = 0;
|
||||
for (let i = 0; i < geom.length - 3; i += 3) {
|
||||
const dx = geom[i + 3] - geom[i];
|
||||
const dy = geom[i + 4] - geom[i + 1];
|
||||
geom.size += Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
geom.start = 0;
|
||||
geom.end = geom.size;
|
||||
|
||||
const clipped = clip([{id: 1, geometry: geom, type: 'LineString', tags: {}, minX: 0, minY: 0, maxX: 50, maxY: 60}],
|
||||
1, 10, 40, 0, -Infinity, Infinity, {lineMetrics: true});
|
||||
|
||||
expect(
|
||||
clipped.map(f => [(f.geometry as StartEndSizeArray).start, (f.geometry as StartEndSizeArray).end])).toEqual(
|
||||
[[10, 40], [70, 130], [160, 200], [230, 245]]
|
||||
);
|
||||
});
|
||||
|
||||
function closed(geometry: number[]): number[][] {
|
||||
return [geometry.concat(geometry.slice(0, 3))];
|
||||
}
|
||||
|
||||
test('clips polygons', () => {
|
||||
|
||||
const clipped = clip([
|
||||
{geometry: closed(geom1), type: 'Polygon', tags: {"1": 1}, minX: 0, minY: 0, maxX: 50, maxY: 60},
|
||||
{geometry: closed(geom2), type: 'Polygon', tags: {"2": 2}, minX: 0, minY: 0, maxX: 50, maxY: 10}
|
||||
], 1, 10, 40, 0, -Infinity, Infinity, {});
|
||||
|
||||
const expected = [
|
||||
{id: null as string, type: 'Polygon', geometry: [[10,0,1,40,0,1,40,10,1,20,10,0,20,20,0,30,20,0,30,30,0,40,30,1,40,40,1,25,40,0,25,50,0,10,50,1,10,60,1,25,60,0,10,24,1,10,0,1]], tags: {"1": 1}, minX: 10, minY: 0, maxX: 40, maxY: 60},
|
||||
{id: null as string, type: 'Polygon', geometry: [[10,0,1,40,0,1,40,10,1,10,10,1,10,0,1]], tags: {"2": 2}, minX: 10, minY: 0, maxX: 40, maxY: 10}
|
||||
];
|
||||
|
||||
expect(JSON.stringify(clipped)).toEqual(JSON.stringify(expected));
|
||||
});
|
||||
|
||||
test('clips points', () => {
|
||||
|
||||
const clipped = clip([
|
||||
{geometry: geom1, type: 'MultiPoint', tags: {"1": 1}, minX: 0, minY: 0, maxX: 50, maxY: 60},
|
||||
{geometry: geom2, type: 'MultiPoint', tags: {"2": 2}, minX: 0, minY: 0, maxX: 50, maxY: 10}
|
||||
], 1, 10, 40, 0, -Infinity, Infinity, {});
|
||||
|
||||
expect(clipped).toEqual([{id: null, type: 'MultiPoint',
|
||||
geometry: [20,10,0,20,20,0,30,20,0,30,30,0,25,40,0,25,50,0,25,60,0], tags: {"1": 1}, minX: 20, minY: 10, maxX: 30, maxY: 60}]);
|
||||
});
|
||||
270
node_modules/@maplibre/geojson-vt/src/clip.ts
generated
vendored
Normal file
270
node_modules/@maplibre/geojson-vt/src/clip.ts
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
|
||||
import {createFeature} from './feature';
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTOptions, StartEndSizeArray } from './definitions';
|
||||
|
||||
export const enum AxisType {
|
||||
X = 0,
|
||||
Y = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* clip features between two vertical or horizontal axis-parallel lines:
|
||||
* | |
|
||||
* ___|___ | /
|
||||
* / | \____|____/
|
||||
* | |
|
||||
*
|
||||
* @param features - the features to clip
|
||||
* @param scale - the scale to divide start and end inputs
|
||||
* @param start - the start of the clip range
|
||||
* @param end - the end of the clip range
|
||||
* @param axis - which axis to clip against
|
||||
* @param minAll - the minimum for all features in the relevant axis
|
||||
* @param maxAll - the maximum for all features in the relevant axis
|
||||
*/
|
||||
export function clip(features: GeoJSONVTInternalFeature[], scale: number, start: number, end: number, axis: AxisType, minAll: number, maxAll: number, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] | null {
|
||||
start /= scale;
|
||||
end /= scale;
|
||||
|
||||
if (minAll >= start && maxAll < end) { // trivial accept
|
||||
return features;
|
||||
}
|
||||
|
||||
if (maxAll < start || minAll >= end) { // trivial reject
|
||||
return null;
|
||||
}
|
||||
|
||||
const clipped: GeoJSONVTInternalFeature[] = [];
|
||||
|
||||
for (const feature of features) {
|
||||
const min = axis === AxisType.X ? feature.minX : feature.minY;
|
||||
const max = axis === AxisType.X ? feature.maxX : feature.maxY;
|
||||
|
||||
if (min >= start && max < end) { // trivial accept
|
||||
clipped.push(feature);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (max < start || min >= end) { // trivial reject
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (feature.type) {
|
||||
case 'Point':
|
||||
case 'MultiPoint': {
|
||||
clipPointFeature(feature, clipped, start, end, axis);
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'LineString': {
|
||||
clipLineStringFeature(feature, clipped, start, end, axis, options);
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'MultiLineString': {
|
||||
clipMultiLineStringFeature(feature, clipped, start, end, axis);
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'Polygon': {
|
||||
clipPolygonFeature(feature, clipped, start, end, axis);
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'MultiPolygon': {
|
||||
clipMultiPolygonFeature(feature, clipped, start, end, axis);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!clipped.length) return null;
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
function clipPointFeature(feature: GeoJSONVTInternalPointFeature | GeoJSONVTInternalMultiPointFeature, clipped: GeoJSONVTInternalFeature[], start: number, end: number, axis: AxisType) {
|
||||
const geom: number[] = [];
|
||||
|
||||
clipPoints(feature.geometry, geom, start, end, axis);
|
||||
if (!geom.length) return;
|
||||
|
||||
const type = geom.length === 3 ? 'Point' : 'MultiPoint';
|
||||
clipped.push(createFeature(feature.id, type, geom, feature.tags));
|
||||
}
|
||||
|
||||
function clipLineStringFeature(feature: GeoJSONVTInternalLineStringFeature, clipped: GeoJSONVTInternalFeature[], start: number, end: number, axis: AxisType, options: GeoJSONVTOptions) {
|
||||
const geom: StartEndSizeArray[] = [];
|
||||
|
||||
clipLine(feature.geometry, geom, start, end, axis, false, options.lineMetrics);
|
||||
if (!geom.length) return;
|
||||
|
||||
if (options.lineMetrics) {
|
||||
for (const line of geom) {
|
||||
clipped.push(createFeature(feature.id, 'LineString', line, feature.tags));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (geom.length > 1) {
|
||||
clipped.push(createFeature(feature.id, 'MultiLineString', geom, feature.tags));
|
||||
return;
|
||||
}
|
||||
|
||||
clipped.push(createFeature(feature.id, 'LineString', geom[0], feature.tags));
|
||||
}
|
||||
|
||||
function clipMultiLineStringFeature(feature: GeoJSONVTInternalMultiLineStringFeature, clipped: GeoJSONVTInternalFeature[], start: number, end: number, axis: AxisType) {
|
||||
const geom: StartEndSizeArray[] = [];
|
||||
|
||||
clipLines(feature.geometry, geom, start, end, axis, false);
|
||||
if (!geom.length) return;
|
||||
|
||||
if (geom.length === 1) {
|
||||
clipped.push(createFeature(feature.id, 'LineString', geom[0], feature.tags));
|
||||
return;
|
||||
}
|
||||
|
||||
clipped.push(createFeature(feature.id,'MultiLineString', geom, feature.tags));
|
||||
}
|
||||
|
||||
function clipPolygonFeature(feature: GeoJSONVTInternalPolygonFeature, clipped: GeoJSONVTInternalFeature[], start: number, end: number, axis: AxisType) {
|
||||
const geom: StartEndSizeArray[] = [];
|
||||
|
||||
clipLines(feature.geometry, geom, start, end, axis, true);
|
||||
if (!geom.length) return;
|
||||
|
||||
clipped.push(createFeature(feature.id, 'Polygon', geom, feature.tags));
|
||||
}
|
||||
|
||||
function clipMultiPolygonFeature(feature: GeoJSONVTInternalMultiPolygonFeature, clipped: GeoJSONVTInternalFeature[], start: number, end: number, axis: AxisType) {
|
||||
const geom: StartEndSizeArray[][] = [];
|
||||
|
||||
for (const polygon of feature.geometry) {
|
||||
const newPolygon: StartEndSizeArray[] = [];
|
||||
|
||||
clipLines(polygon, newPolygon, start, end, axis, true);
|
||||
if (!newPolygon.length) continue;
|
||||
|
||||
geom.push(newPolygon);
|
||||
}
|
||||
if (!geom.length) return;
|
||||
|
||||
clipped.push(createFeature(feature.id, 'MultiPolygon', geom, feature.tags));
|
||||
}
|
||||
|
||||
function clipPoints(geom: number[], newGeom: number[], start: number, end: number, axis: AxisType) {
|
||||
for (let i = 0; i < geom.length; i += 3) {
|
||||
const a = geom[i + axis];
|
||||
|
||||
if (a >= start && a <= end) {
|
||||
addPoint(newGeom, geom[i], geom[i + 1], geom[i + 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clipLine(geom: StartEndSizeArray, newGeom: StartEndSizeArray[], start: number, end: number, axis: AxisType, isPolygon: boolean, trackMetrics: boolean) {
|
||||
|
||||
let slice = newSlice(geom);
|
||||
const intersect = axis === AxisType.X ? intersectX : intersectY;
|
||||
let len = geom.start;
|
||||
let segLen, t;
|
||||
|
||||
for (let i = 0; i < geom.length - 3; i += 3) {
|
||||
const ax = geom[i];
|
||||
const ay = geom[i + 1];
|
||||
const az = geom[i + 2];
|
||||
const bx = geom[i + 3];
|
||||
const by = geom[i + 4];
|
||||
const a = axis === AxisType.X ? ax : ay;
|
||||
const b = axis === AxisType.X ? bx : by;
|
||||
let exited = false;
|
||||
|
||||
if (trackMetrics) segLen = Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2));
|
||||
|
||||
if (a < start) {
|
||||
// ---|--> | (line enters the clip region from the left)
|
||||
if (b > start) {
|
||||
t = intersect(slice, ax, ay, bx, by, start);
|
||||
if (trackMetrics) slice.start = len + segLen * t;
|
||||
}
|
||||
} else if (a > end) {
|
||||
// | <--|--- (line enters the clip region from the right)
|
||||
if (b < end) {
|
||||
t = intersect(slice, ax, ay, bx, by, end);
|
||||
if (trackMetrics) slice.start = len + segLen * t;
|
||||
}
|
||||
} else {
|
||||
addPoint(slice, ax, ay, az);
|
||||
}
|
||||
|
||||
if (b < start && a >= start) {
|
||||
// <--|--- | or <--|-----|--- (line exits the clip region on the left)
|
||||
t = intersect(slice, ax, ay, bx, by, start);
|
||||
exited = true;
|
||||
}
|
||||
|
||||
if (b > end && a <= end) {
|
||||
// | ---|--> or ---|-----|--> (line exits the clip region on the right)
|
||||
t = intersect(slice, ax, ay, bx, by, end);
|
||||
exited = true;
|
||||
}
|
||||
|
||||
if (!isPolygon && exited) {
|
||||
if (trackMetrics) slice.end = len + segLen * t;
|
||||
newGeom.push(slice);
|
||||
slice = newSlice(geom);
|
||||
}
|
||||
|
||||
if (trackMetrics) len += segLen;
|
||||
}
|
||||
|
||||
// add the last point
|
||||
let last = geom.length - 3;
|
||||
const ax = geom[last];
|
||||
const ay = geom[last + 1];
|
||||
const az = geom[last + 2];
|
||||
const a = axis === AxisType.X ? ax : ay;
|
||||
if (a >= start && a <= end) addPoint(slice, ax, ay, az);
|
||||
|
||||
// close the polygon if its endpoints are not the same after clipping
|
||||
last = slice.length - 3;
|
||||
if (isPolygon && last >= 3 && (slice[last] !== slice[0] || slice[last + 1] !== slice[1])) {
|
||||
addPoint(slice, slice[0], slice[1], slice[2]);
|
||||
}
|
||||
|
||||
// add the final slice
|
||||
if (slice.length) {
|
||||
newGeom.push(slice);
|
||||
}
|
||||
}
|
||||
|
||||
function newSlice(line: StartEndSizeArray): StartEndSizeArray {
|
||||
const slice: StartEndSizeArray = [];
|
||||
slice.size = line.size;
|
||||
slice.start = line.start;
|
||||
slice.end = line.end;
|
||||
return slice;
|
||||
}
|
||||
|
||||
function clipLines(geom: StartEndSizeArray[], newGeom: StartEndSizeArray[], start: number, end: number, axis: AxisType, isPolygon: boolean) {
|
||||
for (const line of geom) {
|
||||
clipLine(line, newGeom, start, end, axis, isPolygon, false);
|
||||
}
|
||||
}
|
||||
|
||||
function addPoint(out: number[], x: number, y: number, z: number) {
|
||||
out.push(x, y, z);
|
||||
}
|
||||
|
||||
function intersectX(out: StartEndSizeArray, ax: number, ay: number, bx: number, by: number, x: number) {
|
||||
const t = (x - ax) / (bx - ax);
|
||||
addPoint(out, x, ay + (by - ay) * t, 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
function intersectY(out: StartEndSizeArray, ax: number, ay: number, bx: number, by: number, y: number) {
|
||||
const t = (y - ay) / (by - ay);
|
||||
addPoint(out, ax + (bx - ax) * t, y, 1);
|
||||
return t;
|
||||
}
|
||||
205
node_modules/@maplibre/geojson-vt/src/cluster-tile-index.test.ts
generated
vendored
Normal file
205
node_modules/@maplibre/geojson-vt/src/cluster-tile-index.test.ts
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
import {test, expect} from 'vitest';
|
||||
import {readFileSync} from 'fs';
|
||||
import {ClusterTileIndex} from './cluster-tile-index';
|
||||
import type {ClusterProperties, GeoJSONVTTile} from './definitions';
|
||||
|
||||
const places = JSON.parse(readFileSync(new URL('../test/fixtures/places.json', import.meta.url), 'utf-8')) as GeoJSON.FeatureCollection<GeoJSON.Point>;
|
||||
const placesTile = JSON.parse(readFileSync(new URL('../test/fixtures/places-z0-0-0.json', import.meta.url), 'utf-8')) as GeoJSONVTTile;
|
||||
const placesTileMin5 = JSON.parse(readFileSync(new URL('../test/fixtures/places-z0-0-0-min5.json', import.meta.url), 'utf-8')) as GeoJSONVTTile;
|
||||
|
||||
test('generates clusters properly', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
const tile = index.getTile(0, 0, 0);
|
||||
expect(tile?.features).toEqual(placesTile.features);
|
||||
});
|
||||
|
||||
test('supports minPoints option', () => {
|
||||
const index = new ClusterTileIndex({minPoints: 5});
|
||||
index.load(places.features);
|
||||
const tile = index.getTile(0, 0, 0);
|
||||
expect(tile?.features).toEqual(placesTileMin5.features);
|
||||
});
|
||||
|
||||
test('returns children of a cluster', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
const childCounts = index.getChildren(163).map(p => (p.properties as ClusterProperties)?.point_count || 1);
|
||||
expect(childCounts).toEqual([6, 7, 2, 1]);
|
||||
});
|
||||
|
||||
test('returns leaves of a cluster', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
const leafNames = index.getLeaves(163, 10, 5).map(p => (p.properties as {name: string} | null)?.name);
|
||||
expect(leafNames).toEqual([
|
||||
'Niagara Falls',
|
||||
'Cape San Blas',
|
||||
'Cape Sable',
|
||||
'Cape Canaveral',
|
||||
'San Salvador',
|
||||
'Cabo Gracias a Dios',
|
||||
'I. de Cozumel',
|
||||
'Grand Cayman',
|
||||
'Miquelon',
|
||||
'Cape Bauld'
|
||||
]);
|
||||
});
|
||||
|
||||
test('generates unique ids with generateId option', () => {
|
||||
const index = new ClusterTileIndex({generateId: true});
|
||||
index.load(places.features);
|
||||
const tile = index.getTile(0, 0, 0)!;
|
||||
const ids = tile.features.filter(f => !(f.tags as ClusterProperties)?.cluster).map(f => f.id);
|
||||
expect(ids).toEqual([12, 20, 21, 22, 24, 28, 30, 62, 81, 118, 119, 125, 81, 118]);
|
||||
});
|
||||
|
||||
test('getLeaves handles null-property features', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features.concat([{
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-79.04411780507252, 43.08771393436908]
|
||||
}
|
||||
}]));
|
||||
const leaves = index.getLeaves(164, 1, 6);
|
||||
expect(leaves[0].properties).toBe(null);
|
||||
});
|
||||
|
||||
test('returns cluster expansion zoom', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
expect(index.getClusterExpansionZoom(163)).toBe(1);
|
||||
expect(index.getClusterExpansionZoom(195)).toBe(1);
|
||||
expect(index.getClusterExpansionZoom(580)).toBe(2);
|
||||
expect(index.getClusterExpansionZoom(1156)).toBe(2);
|
||||
expect(index.getClusterExpansionZoom(4133)).toBe(3);
|
||||
});
|
||||
|
||||
test('returns cluster expansion zoom for maxZoom', () => {
|
||||
const index = new ClusterTileIndex({
|
||||
radius: 60,
|
||||
extent: 256,
|
||||
maxZoom: 4,
|
||||
});
|
||||
index.load(places.features);
|
||||
|
||||
expect(index.getClusterExpansionZoom(2503)).toBe(5);
|
||||
});
|
||||
|
||||
test('aggregates cluster properties with reduce', () => {
|
||||
const index = new ClusterTileIndex({
|
||||
map: (props) => ({sum: (props as {scalerank: number})?.scalerank}),
|
||||
reduce: (a, b) => { (a as {sum: number}).sum += (b as {sum: number}).sum; },
|
||||
radius: 100
|
||||
});
|
||||
index.load(places.features);
|
||||
|
||||
expect(index.getTile(1, 0, 0)!.features.map(f => (f.tags as {sum?: number})?.sum).filter(Boolean)).toEqual(
|
||||
[146, 84, 63, 23, 34, 12, 19, 29, 8, 8, 80, 35]);
|
||||
expect(index.getTile(0, 0, 0)!.features.map(f => (f.tags as {sum?: number})?.sum).filter(Boolean)).toEqual(
|
||||
[298, 122, 12, 36, 98, 7, 24, 8, 125, 98, 125, 12, 36, 8]);
|
||||
});
|
||||
|
||||
test('uses default map function with reduce', () => {
|
||||
const index = new ClusterTileIndex({
|
||||
reduce: () => {},
|
||||
radius: 100
|
||||
});
|
||||
index.load(places.features);
|
||||
|
||||
expect(index.getTile(0, 0, 0)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('returns clusters when query crosses international dateline', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load([
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-178.989, 0]
|
||||
}
|
||||
}, {
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-178.990, 0]
|
||||
}
|
||||
}, {
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-178.991, 0]
|
||||
}
|
||||
}, {
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-178.992, 0]
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const nonCrossing = index.getClusters([-179, -10, -177, 10], 1);
|
||||
const crossing = index.getClusters([179, -10, -177, 10], 1);
|
||||
|
||||
expect(nonCrossing.length).toBeGreaterThan(0);
|
||||
expect(crossing.length).toBeGreaterThan(0);
|
||||
expect(nonCrossing.length).toBe(crossing.length);
|
||||
});
|
||||
|
||||
test('does not crash on weird bbox values', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
expect(index.getClusters([129.426390, -103.720017, -445.930843, 114.518236], 1).length).toBe(26);
|
||||
expect(index.getClusters([112.207836, -84.578666, -463.149397, 120.169159], 1).length).toBe(27);
|
||||
expect(index.getClusters([129.886277, -82.332680, -445.470956, 120.390930], 1).length).toBe(26);
|
||||
expect(index.getClusters([458.220043, -84.239039, -117.137190, 120.206585], 1).length).toBe(25);
|
||||
expect(index.getClusters([456.713058, -80.354196, -118.644175, 120.539148], 1).length).toBe(25);
|
||||
expect(index.getClusters([453.105328, -75.857422, -122.251904, 120.732760], 1).length).toBe(25);
|
||||
expect(index.getClusters([-180, -90, 180, 90], 1).length).toBe(61);
|
||||
});
|
||||
|
||||
test('does not crash on non-integer zoom values', () => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load(places.features);
|
||||
expect(index.getClusters([179, -10, -177, 10], 1.25)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('makes sure same-location points are clustered', () => {
|
||||
const index = new ClusterTileIndex({
|
||||
maxZoom: 20,
|
||||
extent: 8192,
|
||||
radius: 16
|
||||
});
|
||||
index.load([
|
||||
{type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426798, 53.943034]}},
|
||||
{type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426798, 53.943034]}}
|
||||
]);
|
||||
|
||||
expect(index.trees[20].ids.length).toBe(1);
|
||||
});
|
||||
|
||||
test('makes sure unclustered point coords are not rounded', () => {
|
||||
const index = new ClusterTileIndex({maxZoom: 19});
|
||||
index.load([
|
||||
{type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [173.19150559062456, -41.340357424709275]}}
|
||||
]);
|
||||
|
||||
expect(index.getTile(20, 1028744, 656754)!.features[0].geometry[0]).toEqual([421, 281]);
|
||||
});
|
||||
|
||||
test('does not throw on zero items', () => {
|
||||
expect(() => {
|
||||
const index = new ClusterTileIndex();
|
||||
index.load([]);
|
||||
expect(index.getClusters([-180, -85, 180, 85], 0)).toEqual([]);
|
||||
}).not.toThrow();
|
||||
});
|
||||
516
node_modules/@maplibre/geojson-vt/src/cluster-tile-index.ts
generated
vendored
Normal file
516
node_modules/@maplibre/geojson-vt/src/cluster-tile-index.ts
generated
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
import KDBush from 'kdbush';
|
||||
import {projectX, projectY} from './convert';
|
||||
import {unprojectX, unprojectY, featureToGeoJSON} from './deconvert';
|
||||
import type {ClusterFeature, ClusterOrPointFeature, ClusterProperties, GeoJSONVTTileIndex, GeoJSONVTFeature, GeoJSONVTInternalFeature, GeoJSONVTInternalPointFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions} from './definitions';
|
||||
|
||||
type ClusterFeatureInternal = GeoJSONVTInternalPointFeature & {
|
||||
tags: ClusterProperties;
|
||||
};
|
||||
|
||||
type ClusterOrPointFeatureInternal = ClusterFeatureInternal | GeoJSONVTInternalPointFeature;
|
||||
|
||||
/** @internal */
|
||||
export type KDBushWithData = KDBush & {
|
||||
flatData: number[];
|
||||
};
|
||||
|
||||
export const defaultClusterOptions: Required<SuperclusterOptions> = {
|
||||
minZoom: 0,
|
||||
maxZoom: 16,
|
||||
minPoints: 2,
|
||||
radius: 40,
|
||||
extent: 512,
|
||||
nodeSize: 64,
|
||||
log: false,
|
||||
generateId: false,
|
||||
reduce: null,
|
||||
map: (props) => props as Record<string, unknown>
|
||||
};
|
||||
|
||||
const OFFSET_ZOOM = 2;
|
||||
const OFFSET_ID = 3;
|
||||
const OFFSET_PARENT = 4;
|
||||
const OFFSET_NUM = 5;
|
||||
const OFFSET_PROP = 6;
|
||||
|
||||
/**
|
||||
* This class allow clustering of geojson points.
|
||||
*/
|
||||
export class ClusterTileIndex implements GeoJSONVTTileIndex {
|
||||
options: Required<SuperclusterOptions>;
|
||||
trees: KDBushWithData[];
|
||||
stride: number;
|
||||
clusterProps: Record<string, unknown>[];
|
||||
points: GeoJSONVTInternalPointFeature[];
|
||||
|
||||
constructor(options?: SuperclusterOptions) {
|
||||
this.options = Object.assign(Object.create(defaultClusterOptions), options) as Required<SuperclusterOptions>;
|
||||
this.trees = new Array(this.options.maxZoom + 1);
|
||||
this.stride = this.options.reduce ? 7 : 6;
|
||||
this.clusterProps = [];
|
||||
this.points = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads GeoJSON point features and builds the internal clustering index.
|
||||
* @param points - GeoJSON point features to cluster.
|
||||
*/
|
||||
load(points: GeoJSON.Feature<GeoJSON.Point>[]): void {
|
||||
const features: GeoJSONVTInternalPointFeature[] = [];
|
||||
|
||||
// Convert GeoJSON point features to GeoJSONVT internal point features
|
||||
for (const point of points) {
|
||||
if (!point.geometry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [lng, lat] = point.geometry.coordinates;
|
||||
const [x, y] = [projectX(lng), projectY(lat)];
|
||||
|
||||
const feature: GeoJSONVTInternalPointFeature = {
|
||||
id: point.id,
|
||||
type: 'Point',
|
||||
geometry: [x, y],
|
||||
tags: point.properties
|
||||
};
|
||||
|
||||
features.push(feature);
|
||||
}
|
||||
|
||||
this.createIndex(features);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Loads internal GeoJSONVT point features from a data source and builds the clustering index.
|
||||
* @param features - {@link GeoJSONVTInternalFeature} data source features to filter and cluster.
|
||||
*/
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void {
|
||||
const points: GeoJSONVTInternalPointFeature[] = [];
|
||||
|
||||
for (const feature of features) {
|
||||
if (feature.type !== 'Point') continue;
|
||||
points.push(feature);
|
||||
}
|
||||
|
||||
this.createIndex(points);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Updates the cluster data by rebuilding.
|
||||
* @param features
|
||||
*/
|
||||
updateIndex(features: GeoJSONVTInternalFeature[], _affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions) {
|
||||
this.options = Object.assign(Object.create(defaultClusterOptions), options.clusterOptions) as Required<SuperclusterOptions>;
|
||||
this.initialize(features);
|
||||
}
|
||||
|
||||
private createIndex(points: GeoJSONVTInternalPointFeature[]): void {
|
||||
const {log, minZoom, maxZoom} = this.options;
|
||||
|
||||
if (log) console.time('total time');
|
||||
|
||||
const timerId = `prepare ${points.length} points`;
|
||||
if (log) console.time(timerId);
|
||||
|
||||
this.points = points;
|
||||
|
||||
// generate a cluster object for each point and index input points into a KD-tree
|
||||
const data: number[] = [];
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const p = points[i];
|
||||
if (!p?.geometry) continue;
|
||||
|
||||
let [x, y] = p.geometry;
|
||||
x = Math.fround(x);
|
||||
y = Math.fround(y);
|
||||
|
||||
// store internal point/cluster data in flat numeric arrays for performance
|
||||
data.push(
|
||||
x, y, // projected point coordinates
|
||||
Infinity, // the last zoom the point was processed at
|
||||
i, // index of the source feature in the original input array
|
||||
-1, // parent cluster id
|
||||
1 // number of points in a cluster
|
||||
);
|
||||
if (this.options.reduce) data.push(0); // noop
|
||||
}
|
||||
let tree = this.trees[maxZoom + 1] = this.createTree(data);
|
||||
|
||||
if (log) console.timeEnd(timerId);
|
||||
|
||||
// cluster points on max zoom, then cluster the results on previous zoom, etc.;
|
||||
// results in a cluster hierarchy across zoom levels
|
||||
for (let z = maxZoom; z >= minZoom; z--) {
|
||||
const now = Date.now();
|
||||
|
||||
// create a new set of clusters for the zoom and index them with a KD-tree
|
||||
tree = this.trees[z] = this.createTree(this.cluster(tree, z));
|
||||
|
||||
if (log) console.log('z%d: %d clusters in %dms', z, tree.numItems, Date.now() - now);
|
||||
}
|
||||
|
||||
if (log) console.timeEnd('total time');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns clusters and/or points within a bounding box at a given zoom level.
|
||||
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order.
|
||||
* @param zoom - Zoom level to query.
|
||||
*/
|
||||
public getClusters(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeature[] {
|
||||
const clusterInternal = this.getClustersInternal(bbox, zoom);
|
||||
return clusterInternal.map((f) => featureToGeoJSON(f) as ClusterOrPointFeature);
|
||||
}
|
||||
|
||||
private getClustersInternal(bbox: [number, number, number, number], zoom: number): ClusterOrPointFeatureInternal[] {
|
||||
let minLng = ((bbox[0] + 180) % 360 + 360) % 360 - 180;
|
||||
const minLat = Math.max(-90, Math.min(90, bbox[1]));
|
||||
let maxLng = bbox[2] === 180 ? 180 : ((bbox[2] + 180) % 360 + 360) % 360 - 180;
|
||||
const maxLat = Math.max(-90, Math.min(90, bbox[3]));
|
||||
|
||||
if (bbox[2] - bbox[0] >= 360) {
|
||||
minLng = -180;
|
||||
maxLng = 180;
|
||||
} else if (minLng > maxLng) {
|
||||
const easternHem = this.getClustersInternal([minLng, minLat, 180, maxLat], zoom);
|
||||
const westernHem = this.getClustersInternal([-180, minLat, maxLng, maxLat], zoom);
|
||||
return easternHem.concat(westernHem);
|
||||
}
|
||||
|
||||
const tree = this.trees[this.limitZoom(zoom)];
|
||||
const ids = tree.range(projectX(minLng), projectY(maxLat), projectX(maxLng), projectY(minLat));
|
||||
const data = tree.flatData;
|
||||
const clusters: ClusterOrPointFeatureInternal[] = [];
|
||||
for (const id of ids) {
|
||||
const k = this.stride * id;
|
||||
clusters.push(data[k + OFFSET_NUM] > 1 ? getClusterFeature(data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
|
||||
* @param clusterId - The target cluster id.
|
||||
*/
|
||||
getChildren(clusterId: number): ClusterOrPointFeature[] {
|
||||
const originId = this.getOriginId(clusterId);
|
||||
const originZoom = this.getOriginZoom(clusterId);
|
||||
const clusterError = new Error('No cluster with the specified id: ' + clusterId);
|
||||
|
||||
const tree = this.trees[originZoom];
|
||||
if (!tree) throw clusterError;
|
||||
|
||||
const data = tree.flatData;
|
||||
if (originId * this.stride >= data.length) throw clusterError;
|
||||
|
||||
const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1));
|
||||
const x = data[originId * this.stride];
|
||||
const y = data[originId * this.stride + 1];
|
||||
const ids = tree.within(x, y, r);
|
||||
const children: ClusterOrPointFeature[] = [];
|
||||
for (const id of ids) {
|
||||
const k = id * this.stride;
|
||||
if (data[k + OFFSET_PARENT] === clusterId) {
|
||||
children.push(data[k + OFFSET_NUM] > 1 ? getClusterGeoJSON(data, k, this.clusterProps) : featureToGeoJSON(this.points[data[k + OFFSET_ID]]) as GeoJSON.Feature<GeoJSON.Point>);
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length === 0) throw clusterError;
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @param limit - Maximum number of points to return (defaults to `10`).
|
||||
* @param offset - Number of points to skip before collecting results (defaults to `0`).
|
||||
*/
|
||||
getLeaves(clusterId: number, limit?: number, offset?: number): GeoJSON.Feature<GeoJSON.Point>[] {
|
||||
limit = limit || 10;
|
||||
offset = offset || 0;
|
||||
|
||||
const leaves: GeoJSON.Feature<GeoJSON.Point>[] = [];
|
||||
this.appendLeaves(leaves, clusterId, limit, offset, 0);
|
||||
|
||||
return leaves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a vector-tile-like representation of a single tile.
|
||||
* @param z - Tile zoom.
|
||||
* @param x - Tile x coordinate.
|
||||
* @param y - Tile y coordinate.
|
||||
*/
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null {
|
||||
const tree = this.trees[this.limitZoom(z)];
|
||||
if (!tree) {
|
||||
return null;
|
||||
}
|
||||
const z2 = Math.pow(2, z);
|
||||
const {extent, radius} = this.options;
|
||||
const p = radius / extent;
|
||||
const top = (y - p) / z2;
|
||||
const bottom = (y + 1 + p) / z2;
|
||||
|
||||
const tile: GeoJSONVTTile = {
|
||||
transformed: true,
|
||||
features: [],
|
||||
source: null,
|
||||
x: x,
|
||||
y: y,
|
||||
z: z
|
||||
};
|
||||
|
||||
this.addTileFeatures(
|
||||
tree.range((x - p) / z2, top, (x + 1 + p) / z2, bottom),
|
||||
tree.flatData, x, y, z2, tile);
|
||||
|
||||
if (x === 0) {
|
||||
this.addTileFeatures(
|
||||
tree.range(1 - p / z2, top, 1, bottom),
|
||||
tree.flatData, z2, y, z2, tile);
|
||||
}
|
||||
if (x === z2 - 1) {
|
||||
this.addTileFeatures(
|
||||
tree.range(0, top, p / z2, bottom),
|
||||
tree.flatData, -1, y, z2, tile);
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zoom level at which a cluster expands into multiple children.
|
||||
* @param clusterId - The target cluster id.
|
||||
*/
|
||||
getClusterExpansionZoom(clusterId: number): number {
|
||||
return this.getOriginZoom(clusterId);
|
||||
}
|
||||
|
||||
private appendLeaves(result: GeoJSON.Feature<GeoJSON.Point>[], clusterId: number, limit: number, offset: number, skipped: number): number {
|
||||
const children = this.getChildren(clusterId);
|
||||
|
||||
for (const child of children) {
|
||||
const props = child.properties as ClusterProperties | null;
|
||||
|
||||
if (props?.cluster) {
|
||||
if (skipped + props.point_count <= offset) {
|
||||
// skip the whole cluster
|
||||
skipped += props.point_count;
|
||||
} else {
|
||||
// enter the cluster
|
||||
skipped = this.appendLeaves(result, props.cluster_id, limit, offset, skipped);
|
||||
// exit the cluster
|
||||
}
|
||||
} else if (skipped < offset) {
|
||||
// skip a single point
|
||||
skipped++;
|
||||
} else {
|
||||
// add a single point
|
||||
result.push(child as GeoJSON.Feature<GeoJSON.Point>);
|
||||
}
|
||||
if (result.length === limit) break;
|
||||
}
|
||||
|
||||
return skipped;
|
||||
}
|
||||
|
||||
private createTree(data: number[]): KDBushWithData {
|
||||
const tree = new KDBush(data.length / this.stride | 0, this.options.nodeSize, Float32Array) as unknown as KDBushWithData;
|
||||
for (let i = 0; i < data.length; i += this.stride) tree.add(data[i], data[i + 1]);
|
||||
tree.finish();
|
||||
tree.flatData = data;
|
||||
tree.data = null; // clear original data to free memory as it isn't used later on.
|
||||
return tree;
|
||||
}
|
||||
|
||||
private addTileFeatures(ids: number[], data: number[], x: number, y: number, z2: number, tile: GeoJSONVTTile): void {
|
||||
for (const i of ids) {
|
||||
const k = i * this.stride;
|
||||
const isCluster = data[k + OFFSET_NUM] > 1;
|
||||
|
||||
let tags: GeoJSON.GeoJsonProperties | ClusterProperties;
|
||||
let px: number;
|
||||
let py: number;
|
||||
if (isCluster) {
|
||||
tags = getClusterProperties(data, k, this.clusterProps);
|
||||
px = data[k];
|
||||
py = data[k + 1];
|
||||
} else {
|
||||
const p = this.points[data[k + OFFSET_ID]];
|
||||
tags = p.tags;
|
||||
[px, py] = p.geometry;
|
||||
}
|
||||
|
||||
const f: GeoJSONVTFeature = {
|
||||
type: 1,
|
||||
geometry: [[
|
||||
Math.round(this.options.extent * (px * z2 - x)),
|
||||
Math.round(this.options.extent * (py * z2 - y))
|
||||
]],
|
||||
tags
|
||||
};
|
||||
|
||||
// assign id
|
||||
let id: number | string | undefined;
|
||||
if (isCluster || this.options.generateId) {
|
||||
// optionally generate id for points
|
||||
id = data[k + OFFSET_ID];
|
||||
} else {
|
||||
// keep id if already assigned
|
||||
id = this.points[data[k + OFFSET_ID]].id as number | string | undefined;
|
||||
}
|
||||
|
||||
if (id !== undefined) f.id = id;
|
||||
|
||||
tile.features.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
private limitZoom(z: number): number {
|
||||
return Math.max(this.options.minZoom, Math.min(Math.floor(+z), this.options.maxZoom + 1));
|
||||
}
|
||||
|
||||
private cluster(tree: KDBushWithData, zoom: number): number[] {
|
||||
const {radius, extent, reduce, minPoints} = this.options;
|
||||
const r = radius / (extent * Math.pow(2, zoom));
|
||||
const data = tree.flatData;
|
||||
const nextData: number[] = [];
|
||||
const stride = this.stride;
|
||||
|
||||
// loop through each point
|
||||
for (let i = 0; i < data.length; i += stride) {
|
||||
// if we've already visited the point at this zoom level, skip it
|
||||
if (data[i + OFFSET_ZOOM] <= zoom) continue;
|
||||
data[i + OFFSET_ZOOM] = zoom;
|
||||
|
||||
// find all nearby points
|
||||
const x = data[i];
|
||||
const y = data[i + 1];
|
||||
const neighborIds = tree.within(data[i], data[i + 1], r);
|
||||
|
||||
const numPointsOrigin = data[i + OFFSET_NUM];
|
||||
let numPoints = numPointsOrigin;
|
||||
|
||||
// count the number of points in a potential cluster
|
||||
for (const neighborId of neighborIds) {
|
||||
const k = neighborId * stride;
|
||||
// filter out neighbors that are already processed
|
||||
if (data[k + OFFSET_ZOOM] > zoom) numPoints += data[k + OFFSET_NUM];
|
||||
}
|
||||
|
||||
// if there were neighbors to merge, and there are enough points to form a cluster
|
||||
if (numPoints > numPointsOrigin && numPoints >= minPoints) {
|
||||
let wx = x * numPointsOrigin;
|
||||
let wy = y * numPointsOrigin;
|
||||
|
||||
let clusterProperties: Record<string, unknown> | undefined;
|
||||
let clusterPropIndex = -1;
|
||||
|
||||
// encode both zoom and point index on which the cluster originated -- offset by total length of features
|
||||
const id = ((i / stride | 0) << 5) + (zoom + 1) + this.points.length;
|
||||
|
||||
for (const neighborId of neighborIds) {
|
||||
const k = neighborId * stride;
|
||||
|
||||
if (data[k + OFFSET_ZOOM] <= zoom) continue;
|
||||
data[k + OFFSET_ZOOM] = zoom; // save the zoom (so it doesn't get processed twice)
|
||||
|
||||
const numPoints2 = data[k + OFFSET_NUM];
|
||||
wx += data[k] * numPoints2; // accumulate coordinates for calculating weighted center
|
||||
wy += data[k + 1] * numPoints2;
|
||||
|
||||
data[k + OFFSET_PARENT] = id;
|
||||
|
||||
if (reduce) {
|
||||
if (!clusterProperties) {
|
||||
clusterProperties = this.map(data, i, true);
|
||||
clusterPropIndex = this.clusterProps.length;
|
||||
this.clusterProps.push(clusterProperties);
|
||||
}
|
||||
reduce(clusterProperties, this.map(data, k));
|
||||
}
|
||||
}
|
||||
|
||||
data[i + OFFSET_PARENT] = id;
|
||||
nextData.push(wx / numPoints, wy / numPoints, Infinity, id, -1, numPoints);
|
||||
if (reduce) nextData.push(clusterPropIndex);
|
||||
|
||||
} else { // left points as unclustered
|
||||
for (let j = 0; j < stride; j++) nextData.push(data[i + j]);
|
||||
|
||||
if (numPoints > 1) {
|
||||
for (const neighborId of neighborIds) {
|
||||
const k = neighborId * stride;
|
||||
if (data[k + OFFSET_ZOOM] <= zoom) continue;
|
||||
data[k + OFFSET_ZOOM] = zoom;
|
||||
for (let j = 0; j < stride; j++) nextData.push(data[k + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nextData;
|
||||
}
|
||||
|
||||
// get index of the point from which the cluster originated
|
||||
private getOriginId(clusterId: number): number {
|
||||
return (clusterId - this.points.length) >> 5;
|
||||
}
|
||||
|
||||
// get zoom of the point from which the cluster originated
|
||||
private getOriginZoom(clusterId: number): number {
|
||||
return (clusterId - this.points.length) % 32;
|
||||
}
|
||||
|
||||
private map(data: number[], i: number, clone?: boolean): Record<string, unknown> {
|
||||
if (data[i + OFFSET_NUM] > 1) {
|
||||
const props = this.clusterProps[data[i + OFFSET_PROP]];
|
||||
return clone ? Object.assign({}, props) : props;
|
||||
}
|
||||
const original = this.points[data[i + OFFSET_ID]].tags;
|
||||
const result = this.options.map(original);
|
||||
return clone && result === original ? Object.assign({}, result) : result;
|
||||
}
|
||||
}
|
||||
|
||||
function getClusterFeature(data: number[], i: number, clusterProps: Record<string, unknown>[]): ClusterFeatureInternal {
|
||||
return {
|
||||
id: data[i + OFFSET_ID],
|
||||
type: 'Point',
|
||||
tags: getClusterProperties(data, i, clusterProps),
|
||||
geometry: [data[i], data[i + 1]]
|
||||
};
|
||||
}
|
||||
|
||||
function getClusterGeoJSON(data: number[], i: number, clusterProps: Record<string, unknown>[]): ClusterFeature {
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: data[i + OFFSET_ID],
|
||||
properties: getClusterProperties(data, i, clusterProps),
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [unprojectX(data[i]), unprojectY(data[i + 1])]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getClusterProperties(data: number[], i: number, clusterProps: Record<string, unknown>[]): ClusterProperties {
|
||||
const count = data[i + OFFSET_NUM];
|
||||
const abbrev =
|
||||
count >= 10000 ? `${Math.round(count / 1000) }k` :
|
||||
count >= 1000 ? `${Math.round(count / 100) / 10 }k` : count;
|
||||
const propIndex = data[i + OFFSET_PROP];
|
||||
const properties = propIndex === -1 ? {} : Object.assign({}, clusterProps[propIndex]);
|
||||
|
||||
return Object.assign(properties, {
|
||||
cluster: true as const,
|
||||
cluster_id: data[i + OFFSET_ID],
|
||||
point_count: count,
|
||||
point_count_abbreviated: abbrev
|
||||
});
|
||||
}
|
||||
200
node_modules/@maplibre/geojson-vt/src/convert.ts
generated
vendored
Normal file
200
node_modules/@maplibre/geojson-vt/src/convert.ts
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
import {simplify} from './simplify';
|
||||
import {createFeature} from './feature';
|
||||
import type {GeoJSONVTInternalFeature, GeoJSONVTOptions, StartEndSizeArray} from './definitions';
|
||||
|
||||
/**
|
||||
* converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
|
||||
* @param data
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export function convertToInternal(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
|
||||
const features: GeoJSONVTInternalFeature[] = [];
|
||||
|
||||
switch (data.type) {
|
||||
case 'FeatureCollection':
|
||||
for (let i = 0; i < data.features.length; i++) {
|
||||
featureToInternal(features, data.features[i], options, i);
|
||||
}
|
||||
break;
|
||||
case 'Feature':
|
||||
featureToInternal(features, data, options);
|
||||
break;
|
||||
default:
|
||||
featureToInternal(features, {type: "Feature" as const, geometry: data, properties: undefined}, options);
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
function featureToInternal(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number) {
|
||||
if (!geojson.geometry) return;
|
||||
|
||||
if (geojson.geometry.type === 'GeometryCollection') {
|
||||
convertGeometryCollection(features, geojson, geojson.geometry, options, index);
|
||||
return;
|
||||
}
|
||||
|
||||
const coords = geojson.geometry.coordinates;
|
||||
if (!coords?.length) return;
|
||||
|
||||
const id = getFeatureId(geojson, options, index);
|
||||
const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
|
||||
|
||||
switch (geojson.geometry.type) {
|
||||
case 'Point':
|
||||
convertPointFeature(features, id, geojson.geometry, geojson.properties);
|
||||
return;
|
||||
|
||||
case 'MultiPoint':
|
||||
convertMultiPointFeature(features, id, geojson.geometry, geojson.properties);
|
||||
return;
|
||||
|
||||
case 'LineString':
|
||||
convertLineStringFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
||||
return;
|
||||
|
||||
case 'MultiLineString':
|
||||
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, options, geojson.properties);
|
||||
return;
|
||||
|
||||
case 'Polygon':
|
||||
convertPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
||||
return;
|
||||
|
||||
case 'MultiPolygon':
|
||||
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
|
||||
return;
|
||||
|
||||
default:
|
||||
throw new Error('Input data is not a valid GeoJSON object.');
|
||||
}
|
||||
}
|
||||
|
||||
function getFeatureId(geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number): number | string | undefined {
|
||||
if (options.promoteId) {
|
||||
return geojson.properties?.[options.promoteId];
|
||||
}
|
||||
if (options.generateId) {
|
||||
return index || 0;
|
||||
}
|
||||
return geojson.id;
|
||||
}
|
||||
|
||||
function convertGeometryCollection(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, geometry: GeoJSON.GeometryCollection, options: GeoJSONVTOptions, index?: number) {
|
||||
for (const geom of geometry.geometries) {
|
||||
featureToInternal(features, {
|
||||
id: geojson.id,
|
||||
type: 'Feature',
|
||||
geometry: geom,
|
||||
properties: geojson.properties
|
||||
}, options, index);
|
||||
}
|
||||
}
|
||||
|
||||
function convertPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Point, properties: GeoJSON.GeoJsonProperties) {
|
||||
const out: number[] = [];
|
||||
out.push(projectX(geom.coordinates[0]), projectY(geom.coordinates[1]), 0);
|
||||
features.push(createFeature(id, 'Point', out, properties));
|
||||
}
|
||||
|
||||
function convertMultiPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPoint, properties: GeoJSON.GeoJsonProperties) {
|
||||
const out: number[] = [];
|
||||
for (const coords of geom.coordinates) {
|
||||
out.push(projectX(coords[0]), projectY(coords[1]), 0);
|
||||
}
|
||||
features.push(createFeature(id, 'MultiPoint', out, properties));
|
||||
}
|
||||
|
||||
function convertLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.LineString, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
|
||||
const out: StartEndSizeArray = [];
|
||||
convertLine(geom.coordinates, out, tolerance, false);
|
||||
features.push(createFeature(id, 'LineString', out, properties));
|
||||
}
|
||||
|
||||
function convertMultiLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiLineString, tolerance: number, options: GeoJSONVTOptions, properties: GeoJSON.GeoJsonProperties) {
|
||||
if (options.lineMetrics) {
|
||||
// explode into linestrings to be able to track metrics
|
||||
for (const line of geom.coordinates) {
|
||||
const out: StartEndSizeArray = [];
|
||||
convertLine(line, out, tolerance, false);
|
||||
features.push(createFeature(id, 'LineString', out, properties));
|
||||
}
|
||||
} else {
|
||||
const out: StartEndSizeArray[] = [];
|
||||
convertLines(geom.coordinates, out, tolerance, false);
|
||||
features.push(createFeature(id, 'MultiLineString', out, properties));
|
||||
}
|
||||
}
|
||||
|
||||
function convertPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Polygon, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
|
||||
const out: StartEndSizeArray[] = [];
|
||||
convertLines(geom.coordinates, out, tolerance, true);
|
||||
features.push(createFeature(id, 'Polygon', out, properties));
|
||||
}
|
||||
|
||||
function convertMultiPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPolygon, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
|
||||
const out: StartEndSizeArray[][] = [];
|
||||
for (const polygon of geom.coordinates) {
|
||||
const polygonOut: StartEndSizeArray[] = [];
|
||||
convertLines(polygon, polygonOut, tolerance, true);
|
||||
out.push(polygonOut);
|
||||
}
|
||||
features.push(createFeature(id, 'MultiPolygon', out, properties));
|
||||
}
|
||||
|
||||
function convertLine(ring: GeoJSON.Position[], out: StartEndSizeArray, tolerance: number, isPolygon: boolean) {
|
||||
let x0, y0;
|
||||
let size = 0;
|
||||
|
||||
for (let j = 0; j < ring.length; j++) {
|
||||
const x = projectX(ring[j][0]);
|
||||
const y = projectY(ring[j][1]);
|
||||
|
||||
out.push(x, y, 0);
|
||||
|
||||
if (j > 0) {
|
||||
if (isPolygon) {
|
||||
size += (x0 * y - x * y0) / 2; // area
|
||||
} else {
|
||||
size += Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)); // length
|
||||
}
|
||||
}
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
|
||||
const last = out.length - 3;
|
||||
out[2] = 1;
|
||||
if (tolerance > 0) simplify(out, 0, last, tolerance);
|
||||
out[last + 2] = 1;
|
||||
|
||||
out.size = Math.abs(size);
|
||||
out.start = 0;
|
||||
out.end = out.size;
|
||||
}
|
||||
|
||||
function convertLines(rings: GeoJSON.Position[][], out: StartEndSizeArray[], tolerance: number, isPolygon: boolean) {
|
||||
for (let i = 0; i < rings.length; i++) {
|
||||
const geom: StartEndSizeArray = [];
|
||||
convertLine(rings[i], geom, tolerance, isPolygon);
|
||||
out.push(geom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert longitude to spherical mercator in [0..1] range
|
||||
*/
|
||||
export function projectX(x: number) {
|
||||
return x / 360 + 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert latitude to spherical mercator in [0..1] range
|
||||
*/
|
||||
export function projectY(y: number) {
|
||||
const sin = Math.sin(y * Math.PI / 180);
|
||||
const y2 = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
||||
return y2 < 0 ? 0 : y2 > 1 ? 1 : y2;
|
||||
}
|
||||
153
node_modules/@maplibre/geojson-vt/src/deconvert.test.ts
generated
vendored
Normal file
153
node_modules/@maplibre/geojson-vt/src/deconvert.test.ts
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
|
||||
import {test, expect} from 'vitest';
|
||||
import {featureToGeoJSON, unprojectX, unprojectY} from './deconvert';
|
||||
import {projectX, projectY} from './convert';
|
||||
import type {GeoJSONVTInternalFeature} from './definitions';
|
||||
|
||||
test('project/unproject roundtrip retains precision', () => {
|
||||
const coords = [
|
||||
{lng: 0, lat: 0},
|
||||
{lng: 90, lat: 45.0564839289},
|
||||
{lng: -90, lat: -45.0564839289},
|
||||
{lng: 180, lat: 85.0511287798},
|
||||
{lng: -180, lat: -85.0511287798},
|
||||
{lng: 45.1234567895, lat: 23.5456789012},
|
||||
{lng: -123.9876543210, lat: -67.8901234564},
|
||||
{lng: 0.0000000001, lat: 0.0000000001},
|
||||
{lng: 179.9999999999, lat: 85},
|
||||
{lng: -179.9999999999, lat: -85}
|
||||
];
|
||||
|
||||
for (const {lng, lat} of coords) {
|
||||
expect(unprojectX(projectX(lng))).toBeCloseTo(lng, 10);
|
||||
expect(unprojectY(projectY(lat))).toBeCloseTo(lat, 10);
|
||||
}
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts Point geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'Point',
|
||||
id: 'point1',
|
||||
geometry: [0.5, 0.5, 0],
|
||||
tags: {name: 'Test Point'},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.type).toBe('Feature');
|
||||
expect(result.geometry.type).toBe('Point');
|
||||
expect((result.geometry as GeoJSON.Point).coordinates).toEqual([0, 0]);
|
||||
expect(result.id).toBe('point1');
|
||||
expect(result.properties).toEqual({name: 'Test Point'});
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts MultiPoint geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'MultiPoint',
|
||||
id: 'multipoint1',
|
||||
geometry: [0.5, 0.5, 0, 0.525, 0.5, 0],
|
||||
tags: {},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.525, maxY: 0.5
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.geometry.type).toBe('MultiPoint');
|
||||
expect((result.geometry as GeoJSON.MultiPoint).coordinates.length).toBe(2);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts LineString geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'LineString',
|
||||
id: 'line1',
|
||||
geometry: [0.5, 0.5, 0, 0.525, 0.5, 0, 0.525, 0.525, 0],
|
||||
tags: {highway: 'primary'},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.525, maxY: 0.525
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.geometry.type).toBe('LineString');
|
||||
expect((result.geometry as GeoJSON.LineString).coordinates.length).toBe(3);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts MultiLineString geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'MultiLineString',
|
||||
id: 'multiline1',
|
||||
geometry: [
|
||||
[0.5, 0.5, 0, 0.525, 0.5, 0],
|
||||
[0.55, 0.55, 0, 0.575, 0.55, 0]
|
||||
],
|
||||
tags: {},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.575, maxY: 0.55
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.geometry.type).toBe('MultiLineString');
|
||||
expect((result.geometry as GeoJSON.MultiLineString).coordinates.length).toBe(2);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts Polygon geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'Polygon',
|
||||
id: 'polygon1',
|
||||
geometry: [
|
||||
[0.5, 0.5, 0, 0.6, 0.5, 0, 0.6, 0.6, 0, 0.5, 0.6, 0, 0.5, 0.5, 0],
|
||||
[0.52, 0.52, 0, 0.58, 0.52, 0, 0.58, 0.58, 0, 0.52, 0.58, 0, 0.52, 0.52, 0]
|
||||
],
|
||||
tags: {landuse: 'residential'},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.6, maxY: 0.6
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.geometry.type).toBe('Polygon');
|
||||
expect((result.geometry as GeoJSON.Polygon).coordinates.length).toBe(2);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: converts MultiPolygon geometry', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'MultiPolygon',
|
||||
id: 'multipolygon1',
|
||||
geometry: [
|
||||
[[0.5, 0.5, 0, 0.52, 0.5, 0, 0.52, 0.52, 0, 0.5, 0.52, 0, 0.5, 0.5, 0]],
|
||||
[[0.55, 0.55, 0, 0.57, 0.55, 0, 0.57, 0.57, 0, 0.55, 0.57, 0, 0.55, 0.55, 0]]
|
||||
],
|
||||
tags: {},
|
||||
minX: 0.5, minY: 0.5, maxX: 0.57, maxY: 0.57
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
|
||||
expect(result.geometry.type).toBe('MultiPolygon');
|
||||
expect((result.geometry as GeoJSON.MultiPolygon).coordinates.length).toBe(2);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: handles various id types', () => {
|
||||
const noId: GeoJSONVTInternalFeature = {type: 'Point', geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
|
||||
const stringId: GeoJSONVTInternalFeature = {type: 'Point', id: 'string-id', geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
|
||||
const numberId: GeoJSONVTInternalFeature = {type: 'Point', id: 42, geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
|
||||
|
||||
expect(featureToGeoJSON(noId).id).toBeUndefined();
|
||||
expect(featureToGeoJSON(stringId).id).toBe('string-id');
|
||||
expect(featureToGeoJSON(numberId).id).toBe(42);
|
||||
});
|
||||
|
||||
test('featureToGeoJSON: correctly unprojects known coordinates', () => {
|
||||
const feature: GeoJSONVTInternalFeature = {
|
||||
type: 'MultiPoint',
|
||||
geometry: [0.5, 0.5, 0, 0, 0.5, 0, 1, 0.5, 0],
|
||||
tags: {},
|
||||
minX: 0, minY: 0.5, maxX: 1, maxY: 0.5
|
||||
};
|
||||
|
||||
const result = featureToGeoJSON(feature);
|
||||
const coords = (result.geometry as GeoJSON.MultiPoint).coordinates;
|
||||
|
||||
expect(coords[0]).toEqual([0, 0]);
|
||||
expect(coords[1]).toEqual([-180, 0]);
|
||||
expect(coords[2]).toEqual([180, 0]);
|
||||
});
|
||||
92
node_modules/@maplibre/geojson-vt/src/deconvert.ts
generated
vendored
Normal file
92
node_modules/@maplibre/geojson-vt/src/deconvert.ts
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import type {GeoJSONVTInternalFeature} from './definitions';
|
||||
|
||||
/**
|
||||
* Converts internal source features back to GeoJSON format.
|
||||
*/
|
||||
export function convertToGeoJSON(source: GeoJSONVTInternalFeature[]): GeoJSON.GeoJSON {
|
||||
const geojson: GeoJSON.GeoJSON = {
|
||||
type: 'FeatureCollection',
|
||||
features: source.map(feature => featureToGeoJSON(feature))
|
||||
};
|
||||
|
||||
return geojson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single internal feature to GeoJSON format.
|
||||
*/
|
||||
export function featureToGeoJSON(feature: GeoJSONVTInternalFeature): GeoJSON.Feature {
|
||||
const geojsonFeature: GeoJSON.Feature = {
|
||||
type: 'Feature',
|
||||
geometry: geometryToGeoJSON(feature),
|
||||
properties: feature.tags
|
||||
};
|
||||
if (feature.id != null) {
|
||||
geojsonFeature.id = feature.id;
|
||||
}
|
||||
|
||||
return geojsonFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single internal feature geometry to GeoJSON format.
|
||||
*/
|
||||
function geometryToGeoJSON(feature: GeoJSONVTInternalFeature): GeoJSON.Geometry {
|
||||
const {type, geometry} = feature;
|
||||
|
||||
switch (type) {
|
||||
case 'Point':
|
||||
return {
|
||||
type: type,
|
||||
coordinates: unprojectPoint(geometry[0], geometry[1])
|
||||
};
|
||||
case 'MultiPoint':
|
||||
case 'LineString':
|
||||
return {
|
||||
type: type,
|
||||
coordinates: unprojectPoints(geometry)
|
||||
};
|
||||
case 'MultiLineString':
|
||||
case 'Polygon':
|
||||
return {
|
||||
type: type,
|
||||
coordinates: geometry.map(ring => unprojectPoints(ring))
|
||||
};
|
||||
case 'MultiPolygon':
|
||||
return {
|
||||
type: type,
|
||||
coordinates: geometry.map(polygon =>
|
||||
polygon.map(ring => unprojectPoints(ring))
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function unprojectPoints(coords: number[]): GeoJSON.Position[] {
|
||||
const result: GeoJSON.Position[] = [];
|
||||
|
||||
for (let i = 0; i < coords.length; i += 3) {
|
||||
result.push(unprojectPoint(coords[i], coords[i + 1]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function unprojectPoint(x: number, y: number): GeoJSON.Position {
|
||||
return [unprojectX(x), unprojectY(y)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert spherical mercator in [0..1] range to longitude
|
||||
*/
|
||||
export function unprojectX(x: number): number {
|
||||
return (x - 0.5) * 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert spherical mercator in [0..1] range to latitude
|
||||
*/
|
||||
export function unprojectY(y: number): number {
|
||||
const y2 = (180 - y * 360) * Math.PI / 180;
|
||||
return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
|
||||
}
|
||||
264
node_modules/@maplibre/geojson-vt/src/definitions.ts
generated
vendored
Normal file
264
node_modules/@maplibre/geojson-vt/src/definitions.ts
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
export type GeoJSONVTOptions = {
|
||||
/**
|
||||
* Max zoom to preserve detail on
|
||||
* @default 14
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* Max zoom in the tile index
|
||||
* @default 5
|
||||
*/
|
||||
indexMaxZoom?: number;
|
||||
/**
|
||||
* Max number of points per tile in the tile index
|
||||
* @default 100000
|
||||
*/
|
||||
indexMaxPoints?: number;
|
||||
/**
|
||||
* Simplification tolerance (higher means simpler)
|
||||
* @default 3
|
||||
*/
|
||||
tolerance?: number;
|
||||
/**
|
||||
* Tile extent
|
||||
* @default 4096
|
||||
*/
|
||||
extent?: number;
|
||||
/**
|
||||
* Tile buffer on each side
|
||||
* @default 64
|
||||
*/
|
||||
buffer?: number;
|
||||
/**
|
||||
* Whether to calculate line metrics
|
||||
* @default false
|
||||
*/
|
||||
lineMetrics?: boolean;
|
||||
/**
|
||||
* Name of a feature property to be promoted to feature.id
|
||||
*/
|
||||
promoteId?: string | null;
|
||||
/**
|
||||
* Whether to generate feature ids. Cannot be used with promoteId
|
||||
* @default false
|
||||
*/
|
||||
generateId?: boolean;
|
||||
/**
|
||||
* Whether geojson can be updated (with caveat of a stored simplified copy)
|
||||
* @default false
|
||||
*/
|
||||
updateable?: boolean;
|
||||
/**
|
||||
* Logging level (0, 1 or 2)
|
||||
* @default 0
|
||||
*/
|
||||
debug?: number;
|
||||
/**
|
||||
* Enable Supercluster for point features.
|
||||
* @default false
|
||||
*/
|
||||
cluster?: boolean;
|
||||
/**
|
||||
* Options for the Supercluster point clustering algorithm.
|
||||
* @see {@link SuperclusterOptions}
|
||||
*/
|
||||
clusterOptions?: SuperclusterOptions;
|
||||
};
|
||||
|
||||
export type GeoJSONToTileOptions = GeoJSONVTOptions & {
|
||||
/**
|
||||
* Whether to wrap features around the antimeridian
|
||||
* @default false
|
||||
*/
|
||||
wrap?: boolean;
|
||||
/**
|
||||
* Whether to clip features to the tile boundary
|
||||
* @default false
|
||||
*/
|
||||
clip?: boolean;
|
||||
};
|
||||
|
||||
export type StartEndSizeArray = number[] & { start?: number; end?: number; size?: number };
|
||||
|
||||
export type PartialGeoJSONVTFeature = {
|
||||
id?: number | string | undefined;
|
||||
tags: GeoJSON.GeoJsonProperties;
|
||||
minX?: number;
|
||||
minY?: number;
|
||||
maxX?: number;
|
||||
maxY?: number;
|
||||
}
|
||||
|
||||
export type GeoJSONVTInternalPointFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'Point';
|
||||
geometry: number[];
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalMultiPointFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiPoint';
|
||||
geometry: number[];
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalLineStringFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'LineString';
|
||||
geometry: StartEndSizeArray;
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalMultiLineStringFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiLineString';
|
||||
geometry: StartEndSizeArray[];
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalPolygonFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'Polygon';
|
||||
geometry: StartEndSizeArray[];
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalMultiPolygonFeature = PartialGeoJSONVTFeature & {
|
||||
type: 'MultiPolygon';
|
||||
geometry: StartEndSizeArray[][];
|
||||
};
|
||||
|
||||
export type GeoJSONVTInternalFeature =
|
||||
| GeoJSONVTInternalPointFeature
|
||||
| GeoJSONVTInternalMultiPointFeature
|
||||
| GeoJSONVTInternalLineStringFeature
|
||||
| GeoJSONVTInternalMultiLineStringFeature
|
||||
| GeoJSONVTInternalPolygonFeature
|
||||
| GeoJSONVTInternalMultiPolygonFeature;
|
||||
|
||||
/**
|
||||
* The geojson properies related to a cluster.
|
||||
*/
|
||||
export type ClusterProperties = {
|
||||
cluster: true;
|
||||
cluster_id: number;
|
||||
point_count: number;
|
||||
point_count_abbreviated: string | number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* A geojson point with cluster properties, see {@link ClusterProperties}.
|
||||
*/
|
||||
export type ClusterFeature = GeoJSON.Feature<GeoJSON.Point, ClusterProperties>;
|
||||
|
||||
/**
|
||||
* A geojson point that is either a regular point or a cluster, which is a point with cluster properties.
|
||||
* See {@link ClusterFeature} for more information
|
||||
*/
|
||||
export type ClusterOrPointFeature = ClusterFeature | GeoJSON.Feature<GeoJSON.Point>;
|
||||
|
||||
export type GeoJSONVTInternalTileFeaturePoint = {
|
||||
id? : number | string | undefined;
|
||||
type: 1;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: number[];
|
||||
}
|
||||
|
||||
export type GeoJSONVTInternalTileFeatureNonPoint = {
|
||||
id? : number | string | undefined;
|
||||
type: 2 | 3;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: number[][];
|
||||
}
|
||||
export type GeoJSONVTInternalTileFeature = GeoJSONVTInternalTileFeaturePoint | GeoJSONVTInternalTileFeatureNonPoint;
|
||||
|
||||
export type GeoJSONVTInternalTile = {
|
||||
transformed: boolean;
|
||||
features: GeoJSONVTInternalTileFeature[];
|
||||
source: GeoJSONVTInternalFeature[] | null;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
minX?: number;
|
||||
minY?: number;
|
||||
maxX?: number;
|
||||
maxY?: number;
|
||||
numPoints?: number;
|
||||
numSimplified?: number;
|
||||
numFeatures?: number;
|
||||
}
|
||||
|
||||
export type GeoJSONVTFeaturePoint = {
|
||||
id? : number | string | undefined;
|
||||
type: 1;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: [number, number][]
|
||||
}
|
||||
|
||||
export type GeoJSONVTFeatureNonPoint = {
|
||||
id? : number | string | undefined;
|
||||
type: 2 | 3;
|
||||
tags: GeoJSON.GeoJsonProperties | null;
|
||||
geometry: [number, number][][]
|
||||
}
|
||||
|
||||
export type GeoJSONVTFeature = GeoJSONVTFeaturePoint | GeoJSONVTFeatureNonPoint;
|
||||
|
||||
export type GeoJSONVTTile = GeoJSONVTInternalTile & {
|
||||
transformed: true;
|
||||
features: GeoJSONVTFeature[]
|
||||
}
|
||||
|
||||
export interface GeoJSONVTTileIndex {
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void;
|
||||
updateIndex(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void;
|
||||
getClusterExpansionZoom(clusterId: number): number | null;
|
||||
getChildren(clusterId: number): ClusterOrPointFeature[] | null;
|
||||
getLeaves(clusterId: number, limit?: number, offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null
|
||||
}
|
||||
|
||||
export type SuperclusterOptions = {
|
||||
/**
|
||||
* Min zoom to generate clusters on
|
||||
* @default 0
|
||||
*/
|
||||
minZoom?: number;
|
||||
/**
|
||||
* Max zoom level to cluster the points on
|
||||
* @default 16
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* Minimum points to form a cluster
|
||||
* @default 2
|
||||
*/
|
||||
minPoints?: number;
|
||||
/**
|
||||
* Cluster radius in pixels
|
||||
* @default 40
|
||||
*/
|
||||
radius?: number;
|
||||
/**
|
||||
* Tile extent (radius is calculated relative to it)
|
||||
* @default 512
|
||||
*/
|
||||
extent?: number;
|
||||
/**
|
||||
* Size of the KD-tree leaf node, affects performance
|
||||
* @default 64
|
||||
*/
|
||||
nodeSize?: number;
|
||||
/**
|
||||
* Whether to log timing info
|
||||
* @default false
|
||||
*/
|
||||
log?: boolean;
|
||||
/**
|
||||
* Whether to generate numeric ids for input features (in vector tiles)
|
||||
* @default false
|
||||
*/
|
||||
generateId?: boolean;
|
||||
/**
|
||||
* A reduce function for calculating custom cluster properties
|
||||
* @default null
|
||||
*/
|
||||
reduce?: ((accumulated: Record<string, unknown>, props: Record<string, unknown>) => void) | null;
|
||||
/**
|
||||
* Properties to use for individual points when running the reducer
|
||||
* @default props => props
|
||||
*/
|
||||
map?: (props: GeoJSON.GeoJsonProperties) => Record<string, unknown>;
|
||||
};
|
||||
270
node_modules/@maplibre/geojson-vt/src/difference.test.ts
generated
vendored
Normal file
270
node_modules/@maplibre/geojson-vt/src/difference.test.ts
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
import {test, expect} from 'vitest';
|
||||
import {applySourceDiff} from './difference';
|
||||
|
||||
const options = {
|
||||
maxZoom: 14,
|
||||
indexMaxZoom: 5,
|
||||
indexMaxPoints: 100000,
|
||||
tolerance: 3,
|
||||
extent: 4096,
|
||||
buffer: 64,
|
||||
updateable: true
|
||||
};
|
||||
|
||||
test('applySourceDiff: adds a feature using the feature id', () => {
|
||||
const point = {
|
||||
type: 'Feature' as const,
|
||||
id: 'point',
|
||||
geometry: {
|
||||
type: 'Point' as const,
|
||||
coordinates: [0, 0]
|
||||
},
|
||||
properties: {},
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([], {
|
||||
add: [point]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
expect(source[0].id).toBe('point');
|
||||
});
|
||||
|
||||
test('applySourceDiff: adds a feature using the promoteId', () => {
|
||||
const point2 = {
|
||||
type: 'Feature' as const,
|
||||
geometry: {
|
||||
type: 'Point' as const,
|
||||
coordinates: [0, 0],
|
||||
},
|
||||
properties: {
|
||||
promoteId: 'point2'
|
||||
},
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([], {
|
||||
add: [point2]
|
||||
}, {promoteId: 'promoteId'});
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
expect(source[0].id).toBe('point2');
|
||||
});
|
||||
|
||||
test('applySourceDiff: removes a feature by its id', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const point2 = {
|
||||
type: 'Point' as const,
|
||||
id: 'point2',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point, point2], {
|
||||
remove: ['point2'],
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
expect(source[0].id).toBe('point');
|
||||
});
|
||||
|
||||
test('applySourceDiff: removeAll clears all features', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const point2 = {
|
||||
type: 'Point' as const,
|
||||
id: 'point2',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const source = [point, point2];
|
||||
const result = applySourceDiff(source, {
|
||||
removeAll: true
|
||||
}, options);
|
||||
|
||||
expect(source).toEqual(result.affected);
|
||||
expect(result.source).toEqual([]);
|
||||
});
|
||||
|
||||
test('applySourceDiff: updates a feature geometry', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{
|
||||
id: 'point',
|
||||
newGeometry: {
|
||||
type: 'Point',
|
||||
coordinates: [1, 0]
|
||||
}
|
||||
}]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
expect(source[0].id).toBe('point');
|
||||
expect(source[0].geometry[0]).toBe(0.5027777777777778);
|
||||
expect(source[0].geometry[1]).toBe(0.5);
|
||||
});
|
||||
|
||||
test('applySourceDiff: adds properties', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{
|
||||
id: 'point',
|
||||
addOrUpdateProperties: [
|
||||
{key: 'prop', value: 'value'},
|
||||
{key: 'prop2', value: 'value2'}
|
||||
]
|
||||
}]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
const tags = source[0].tags;
|
||||
expect(Object.keys(tags).length).toBe(2);
|
||||
expect(tags.prop).toBe('value');
|
||||
expect(tags.prop2).toBe('value2');
|
||||
});
|
||||
|
||||
test('applySourceDiff: updates properties', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {prop: 'value', prop2: 'value2'},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{
|
||||
id: 'point',
|
||||
addOrUpdateProperties: [
|
||||
{key: 'prop2', value: 'value3'}
|
||||
]
|
||||
}]
|
||||
}, options);
|
||||
expect(source.length).toBe(1);
|
||||
|
||||
const tags2 = source[0].tags;
|
||||
expect(Object.keys(tags2).length).toBe(2);
|
||||
expect(tags2.prop).toBe('value');
|
||||
expect(tags2.prop2).toBe('value3');
|
||||
});
|
||||
|
||||
test('applySourceDiff: removes properties', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {prop: 'value', prop2: 'value2'},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{
|
||||
id: 'point',
|
||||
removeProperties: ['prop2']
|
||||
}]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
const tags3 = source[0].tags;
|
||||
expect(Object.keys(tags3).length).toBe(1);
|
||||
expect(tags3.prop).toBe('value');
|
||||
});
|
||||
|
||||
test('applySourceDiff: removes all properties', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {prop: 'value', prop2: 'value2'},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{
|
||||
id: 'point',
|
||||
removeAllProperties: true,
|
||||
}]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
expect(Object.keys(source[0].tags).length).toBe(0);
|
||||
});
|
||||
|
||||
test('applySourceDiff: empty update preserves properties', () => {
|
||||
const point = {
|
||||
type: 'Point' as const,
|
||||
id: 'point',
|
||||
geometry: [0, 0],
|
||||
tags: {prop: 'value', prop2: 'value2'},
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0
|
||||
};
|
||||
|
||||
const {source} = applySourceDiff([point], {
|
||||
update: [{id: 'point'}]
|
||||
}, options);
|
||||
|
||||
expect(source.length).toBe(1);
|
||||
const tags2 = source[0].tags;
|
||||
expect(Object.keys(tags2).length).toBe(2);
|
||||
expect(tags2.prop).toBe('value');
|
||||
expect(tags2.prop2).toBe('value2');
|
||||
});
|
||||
223
node_modules/@maplibre/geojson-vt/src/difference.ts
generated
vendored
Normal file
223
node_modules/@maplibre/geojson-vt/src/difference.ts
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
import {convertToInternal} from './convert';
|
||||
import {wrap} from './wrap';
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions } from './definitions';
|
||||
|
||||
export type GeoJSONVTSourceDiff = {
|
||||
/**
|
||||
* If true, clear all existing features
|
||||
*/
|
||||
removeAll?: boolean;
|
||||
/**
|
||||
* Array of feature IDs to remove
|
||||
*/
|
||||
remove?: (string | number)[];
|
||||
/**
|
||||
* Array of GeoJSON features to add
|
||||
*/
|
||||
add?: GeoJSON.Feature[];
|
||||
/**
|
||||
* Array of per-feature updates
|
||||
*/
|
||||
update?: GeoJSONVTFeatureDiff[];
|
||||
};
|
||||
|
||||
export type GeoJSONVTFeatureDiff = {
|
||||
/**
|
||||
* ID of the feature being updated
|
||||
*/
|
||||
id: string | number;
|
||||
/**
|
||||
* Optional new geometry
|
||||
*/
|
||||
newGeometry?: GeoJSON.Geometry;
|
||||
/**
|
||||
* Remove all properties if true
|
||||
*/
|
||||
removeAllProperties?: boolean;
|
||||
/**
|
||||
* Specific properties to delete
|
||||
*/
|
||||
removeProperties?: string[];
|
||||
/**
|
||||
* Properties to add or update
|
||||
*/
|
||||
addOrUpdateProperties?: {
|
||||
key: string;
|
||||
value: unknown;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type ApplySourceDiffResult = {
|
||||
/**
|
||||
* The features affected by this update, which should be used to invalidate tiles
|
||||
*/
|
||||
affected: GeoJSONVTInternalFeature[];
|
||||
/**
|
||||
* The updated source data, which should replace the existing source data in the index
|
||||
*/
|
||||
source: GeoJSONVTInternalFeature[];
|
||||
};
|
||||
|
||||
type HashedGeoJSONVTSourceDiff = {
|
||||
removeAll?: boolean | undefined;
|
||||
remove: Set<string | number>;
|
||||
add: Map<string | number | undefined, GeoJSON.Feature>;
|
||||
update: Map<string | number, GeoJSONVTFeatureDiff>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies a GeoJSON Source Diff to an existing set of simplified features
|
||||
* @param source
|
||||
* @param dataDiff
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export function applySourceDiff(source: GeoJSONVTInternalFeature[], dataDiff: GeoJSONVTSourceDiff, options: GeoJSONVTOptions): ApplySourceDiffResult {
|
||||
// convert diff to sets/maps for o(1) lookups
|
||||
const diff = diffToHashed(dataDiff);
|
||||
|
||||
// collection for features that will be affected by this update and used to invalidate tiles
|
||||
let affected: GeoJSONVTInternalFeature[] = [];
|
||||
|
||||
if (diff.removeAll) {
|
||||
affected = source;
|
||||
source = [];
|
||||
}
|
||||
|
||||
if (diff.remove.size || diff.add.size) {
|
||||
const removeFeatures = [];
|
||||
|
||||
// Collect features to remove (explicit removals + replacements via add)
|
||||
for (const feature of source) {
|
||||
if (diff.remove.has(feature.id) || diff.add.has(feature.id)) {
|
||||
removeFeatures.push(feature);
|
||||
}
|
||||
}
|
||||
|
||||
if (removeFeatures.length) {
|
||||
affected.push(...removeFeatures);
|
||||
const removeIds = new Set(removeFeatures.map(f => f.id));
|
||||
source = source.filter(f => !removeIds.has(f.id));
|
||||
}
|
||||
|
||||
if (diff.add.size) {
|
||||
let addFeatures = convertToInternal({type: 'FeatureCollection', features: Array.from(diff.add.values())}, options);
|
||||
addFeatures = wrap(addFeatures, options);
|
||||
affected.push(...addFeatures);
|
||||
source.push(...addFeatures);
|
||||
}
|
||||
}
|
||||
|
||||
if (diff.update.size) {
|
||||
// Features can be duplicated across the antimeridian (wrap) in a single tile, so must update all instances with the same id
|
||||
for (const [id, update] of diff.update) {
|
||||
const oldFeatures = [];
|
||||
const keepFeatures = [];
|
||||
|
||||
for (const feature of source) {
|
||||
if (feature.id === id) {
|
||||
oldFeatures.push(feature);
|
||||
} else {
|
||||
keepFeatures.push(feature);
|
||||
}
|
||||
}
|
||||
if (!oldFeatures.length) continue;
|
||||
|
||||
const updatedFeatures = getUpdatedFeatures(oldFeatures, update, options);
|
||||
if (!updatedFeatures.length) continue;
|
||||
|
||||
affected.push(...oldFeatures, ...updatedFeatures);
|
||||
keepFeatures.push(...updatedFeatures);
|
||||
source = keepFeatures;
|
||||
}
|
||||
}
|
||||
|
||||
return {affected, source};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets updated simplified feature(s) based on a diff update object.
|
||||
* @param vtFeatures - the original features
|
||||
* @param update - the update object to apply
|
||||
* @param options - the options to use for the wrap method
|
||||
* @returns Updated features. If geometry is updated, returns new feature(s) converted from geojson and wrapped. If only properties are updated, returns feature(s) with tags updated.
|
||||
*/
|
||||
function getUpdatedFeatures(vtFeatures: GeoJSONVTInternalFeature[], update: GeoJSONVTFeatureDiff, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
|
||||
const changeGeometry = !!update.newGeometry;
|
||||
const changeProps =
|
||||
update.removeAllProperties ||
|
||||
update.removeProperties?.length > 0 ||
|
||||
update.addOrUpdateProperties?.length > 0;
|
||||
|
||||
// if geometry changed, need to create a new geojson feature and convert to internal format
|
||||
if (changeGeometry) {
|
||||
const vtFeature = vtFeatures[0];
|
||||
const geojsonFeature = {
|
||||
type: 'Feature' as const,
|
||||
id: vtFeature.id,
|
||||
geometry: update.newGeometry,
|
||||
properties: changeProps ? applyPropertyUpdates(vtFeature.tags, update) : vtFeature.tags
|
||||
};
|
||||
|
||||
let features = convertToInternal({type: 'FeatureCollection', features: [geojsonFeature]}, options);
|
||||
features = wrap(features, options);
|
||||
return features;
|
||||
}
|
||||
|
||||
if (changeProps) {
|
||||
const updated = [];
|
||||
for (const vtFeature of vtFeatures) {
|
||||
const feature = {...vtFeature};
|
||||
feature.tags = applyPropertyUpdates(feature.tags, update);
|
||||
updated.push(feature);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* helper to apply property updates from a diff update object to a properties object
|
||||
*/
|
||||
function applyPropertyUpdates(tags: GeoJSON.GeoJsonProperties, update: GeoJSONVTFeatureDiff): GeoJSON.GeoJsonProperties {
|
||||
if (update.removeAllProperties) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const properties = {...tags || {}};
|
||||
|
||||
if (update.removeProperties) {
|
||||
for (const key of update.removeProperties) {
|
||||
delete properties[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (update.addOrUpdateProperties) {
|
||||
for (const {key, value} of update.addOrUpdateProperties) {
|
||||
properties[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a GeoJSON Source Diff to an idempotent hashed representation using Sets and Maps
|
||||
*/
|
||||
export function diffToHashed(diff: GeoJSONVTSourceDiff): HashedGeoJSONVTSourceDiff {
|
||||
if (!diff) return {
|
||||
remove: new Set(),
|
||||
add: new Map(),
|
||||
update: new Map()
|
||||
};
|
||||
|
||||
const hashed: HashedGeoJSONVTSourceDiff = {
|
||||
removeAll: diff.removeAll,
|
||||
remove: new Set(diff.remove || []),
|
||||
add: new Map(diff.add?.map(feature => [feature.id, feature])),
|
||||
update: new Map(diff.update?.map(update => [update.id, update]))
|
||||
};
|
||||
|
||||
return hashed;
|
||||
}
|
||||
71
node_modules/@maplibre/geojson-vt/src/feature.ts
generated
vendored
Normal file
71
node_modules/@maplibre/geojson-vt/src/feature.ts
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature } from "./definitions";
|
||||
|
||||
type FeatureTypeMap = {
|
||||
Point: GeoJSONVTInternalPointFeature["geometry"];
|
||||
MultiPoint: GeoJSONVTInternalMultiPointFeature["geometry"];
|
||||
LineString: GeoJSONVTInternalLineStringFeature["geometry"];
|
||||
MultiLineString: GeoJSONVTInternalMultiLineStringFeature["geometry"];
|
||||
Polygon: GeoJSONVTInternalPolygonFeature["geometry"];
|
||||
MultiPolygon: GeoJSONVTInternalMultiPolygonFeature["geometry"];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id - the feature's ID
|
||||
* @param type - the feature's type
|
||||
* @param geom - the feature's geometry
|
||||
* @param tags - the feature's properties
|
||||
* @returns the created feature
|
||||
*/
|
||||
export function createFeature<T extends GeoJSONVTInternalFeature["type"]>(id: number | string | undefined, type: T, geom: FeatureTypeMap[T], tags: GeoJSON.GeoJsonProperties): GeoJSONVTInternalFeature {
|
||||
// This is mostly for TypeScript type narrowing
|
||||
const data = { type, geom } as { [K in GeoJSONVTInternalFeature["type"]]: { type: K, geom: FeatureTypeMap[K] } }[GeoJSONVTInternalFeature["type"]];
|
||||
|
||||
const feature = {
|
||||
id: id == null ? null : id,
|
||||
type: data.type,
|
||||
geometry: data.geom,
|
||||
tags,
|
||||
minX: Infinity,
|
||||
minY: Infinity,
|
||||
maxX: -Infinity,
|
||||
maxY: -Infinity
|
||||
} as GeoJSONVTInternalFeature;
|
||||
|
||||
switch (data.type) {
|
||||
case 'Point':
|
||||
case 'MultiPoint':
|
||||
case 'LineString':
|
||||
calcLineBBox(feature, data.geom);
|
||||
break;
|
||||
|
||||
case 'Polygon':
|
||||
// the outer ring (ie [0]) contains all inner rings
|
||||
calcLineBBox(feature, data.geom[0]);
|
||||
break;
|
||||
|
||||
case 'MultiLineString':
|
||||
for (const line of data.geom) {
|
||||
calcLineBBox(feature, line);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'MultiPolygon':
|
||||
for (const polygon of data.geom) {
|
||||
// the outer ring (ie [0]) contains all inner rings
|
||||
calcLineBBox(feature, polygon[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
function calcLineBBox(feature: GeoJSONVTInternalFeature, geom: number[]) {
|
||||
for (let i = 0; i < geom.length; i += 3) {
|
||||
feature.minX = Math.min(feature.minX, geom[i]);
|
||||
feature.minY = Math.min(feature.minY, geom[i + 1]);
|
||||
feature.maxX = Math.max(feature.maxX, geom[i]);
|
||||
feature.maxY = Math.max(feature.maxY, geom[i + 1]);
|
||||
}
|
||||
}
|
||||
58
node_modules/@maplibre/geojson-vt/src/geojson-to-tile.ts
generated
vendored
Normal file
58
node_modules/@maplibre/geojson-vt/src/geojson-to-tile.ts
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import {AxisType, clip} from './clip';
|
||||
import {convertToInternal} from './convert';
|
||||
import {defaultOptions} from './geojsonvt';
|
||||
import {createTile} from './tile';
|
||||
import {transformTile} from './transform';
|
||||
import {wrap} from './wrap';
|
||||
import type {GeoJSONToTileOptions, GeoJSONVTTile} from './definitions';
|
||||
|
||||
/**
|
||||
* Converts GeoJSON data directly to a single vector tile without building a tile index.
|
||||
*
|
||||
* Unlike the {@link GeoJSONVT} class which builds a hierarchical tile index for efficient
|
||||
* repeated tile access, this function generates a single tile on-demand. This is useful when:
|
||||
* - You only need one specific tile and don't need to query multiple tiles
|
||||
* - The source data is already spatially filtered to the tile's bounding box
|
||||
* - You want to avoid the overhead of building a full tile index
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import {geoJSONToTile} from '@maplibre/geojson-vt';
|
||||
*
|
||||
* const geojson = {
|
||||
* type: 'FeatureCollection',
|
||||
* features: [{
|
||||
* type: 'Feature',
|
||||
* geometry: { type: 'Point', coordinates: [-77.03, 38.90] },
|
||||
* properties: { name: 'Washington, D.C.' }
|
||||
* }]
|
||||
* };
|
||||
*
|
||||
* const tile = geoJSONToTile(geojson, 10, 292, 391, { extent: 4096 });
|
||||
* ```
|
||||
*
|
||||
* @param data - GeoJSON data (Feature, FeatureCollection, or Geometry)
|
||||
* @param z - Tile zoom level
|
||||
* @param x - Tile x coordinate
|
||||
* @param y - Tile y coordinate
|
||||
* @param options - Optional configuration for tile generation
|
||||
* @returns The generated tile with geometries in tile coordinates, or null if no features
|
||||
*/
|
||||
|
||||
export function geoJSONToTile(data: GeoJSON.GeoJSON, z: number, x: number, y: number, options: GeoJSONToTileOptions = {}): GeoJSONVTTile {
|
||||
options = {...defaultOptions, ...options};
|
||||
const {wrap: shouldWrap = false, clip: shouldClip = false} = options;
|
||||
|
||||
let features = convertToInternal(data, options);
|
||||
if (shouldWrap) {
|
||||
features = wrap(features, options);
|
||||
}
|
||||
if (shouldClip || options.lineMetrics) {
|
||||
const pow2 = 1 << z;
|
||||
const buffer = options.buffer / options.extent;
|
||||
const left = clip(features, pow2, (x - buffer), (x + 1 + buffer), AxisType.X, -1, 2, options);
|
||||
features = clip(left || [], pow2, (y - buffer), (y + 1 + buffer), AxisType.Y, -1, 2, options);
|
||||
}
|
||||
|
||||
return transformTile(createTile(features ?? [], z, x, y, options), options.extent);
|
||||
}
|
||||
39
node_modules/@maplibre/geojson-vt/src/geojsonvt.test.ts
generated
vendored
Normal file
39
node_modules/@maplibre/geojson-vt/src/geojsonvt.test.ts
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import {test, expect} from 'vitest';
|
||||
import {GeoJSONVT} from '.';
|
||||
|
||||
test('publicly exposed cluster methods: getClusterExpansionZoom, getClusterChildren, getClusterLeaves', () => {
|
||||
const points = {
|
||||
type: 'FeatureCollection' as const,
|
||||
features: Array.from({length: 20}, (_, i) => ({
|
||||
type: 'Feature' as const,
|
||||
geometry: {type: 'Point' as const, coordinates: [i * 0.0001, i * 0.0001]},
|
||||
properties: {name: `Point ${i}`}
|
||||
}))
|
||||
};
|
||||
|
||||
const index = new GeoJSONVT(points, {
|
||||
updateable: true,
|
||||
cluster: true,
|
||||
clusterOptions: {radius: 100}
|
||||
});
|
||||
|
||||
const tile = index.getTile(0, 0, 0);
|
||||
const cluster = tile.features.find(f => (f.tags as {cluster?: boolean})?.cluster);
|
||||
const clusterId = (cluster.tags as {cluster_id: number}).cluster_id;
|
||||
|
||||
expect(index.getClusterExpansionZoom(clusterId)).toBeGreaterThan(0);
|
||||
expect(index.getClusterChildren(clusterId).length).toBeGreaterThan(0);
|
||||
expect(index.getClusterLeaves(clusterId, 5, 0).length).toBeLessThanOrEqual(5);
|
||||
});
|
||||
|
||||
test('publicly exposed cluster methods: return undefined when clustering is disabled', () => {
|
||||
const index = new GeoJSONVT({type: 'FeatureCollection', features: []}, {
|
||||
updateable: true,
|
||||
cluster: false,
|
||||
clusterOptions: {radius: 100}
|
||||
});
|
||||
|
||||
expect(index.getClusterExpansionZoom(123)).toBeNull();
|
||||
expect(index.getClusterChildren(123)).toBeNull();
|
||||
expect(index.getClusterLeaves(123, 10, 0)).toBeNull();
|
||||
});
|
||||
209
node_modules/@maplibre/geojson-vt/src/geojsonvt.ts
generated
vendored
Normal file
209
node_modules/@maplibre/geojson-vt/src/geojsonvt.ts
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
import {convertToInternal} from './convert';
|
||||
import {convertToGeoJSON, featureToGeoJSON} from './deconvert';
|
||||
import {wrap} from './wrap';
|
||||
import {applySourceDiff, type GeoJSONVTSourceDiff} from './difference';
|
||||
import {ClusterTileIndex, defaultClusterOptions} from './cluster-tile-index';
|
||||
import {TileIndex} from './tile-index';
|
||||
import type {ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalFeature, GeoJSONVTOptions, GeoJSONVTTile, SuperclusterOptions} from './definitions';
|
||||
|
||||
export const defaultOptions: GeoJSONVTOptions = {
|
||||
maxZoom: 14,
|
||||
indexMaxZoom: 5,
|
||||
indexMaxPoints: 100000,
|
||||
tolerance: 3,
|
||||
extent: 4096,
|
||||
buffer: 64,
|
||||
lineMetrics: false,
|
||||
promoteId: null,
|
||||
generateId: false,
|
||||
updateable: false,
|
||||
cluster: false,
|
||||
clusterOptions: defaultClusterOptions,
|
||||
debug: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Main class for creating and managing a vector tile index from GeoJSON data.
|
||||
*/
|
||||
export class GeoJSONVT {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
public get tiles() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this.tileIndex as any)?.tiles ?? {};
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
public get stats() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this.tileIndex as any).stats;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* This is for the tests
|
||||
*/
|
||||
public get total() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this.tileIndex as any).total;
|
||||
}
|
||||
|
||||
private options: GeoJSONVTOptions;
|
||||
|
||||
private source?: GeoJSONVTInternalFeature[];
|
||||
private tileIndex: GeoJSONVTTileIndex;
|
||||
|
||||
constructor(data: GeoJSON.GeoJSON, options?: GeoJSONVTOptions) {
|
||||
options = this.options = Object.assign({}, defaultOptions, options);
|
||||
|
||||
const debug = options.debug;
|
||||
|
||||
if (debug) console.time('preprocess data');
|
||||
|
||||
if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error('maxZoom should be in the 0-24 range');
|
||||
if (options.promoteId && options.generateId) throw new Error('promoteId and generateId cannot be used together.');
|
||||
|
||||
// projects and adds simplification info
|
||||
let features = convertToInternal(data, options);
|
||||
|
||||
if (debug) {
|
||||
console.timeEnd('preprocess data');
|
||||
console.log('index: maxZoom: %d, maxPoints: %d', options.indexMaxZoom, options.indexMaxPoints);
|
||||
console.time('generate tiles');
|
||||
}
|
||||
|
||||
// wraps features (ie extreme west and extreme east)
|
||||
features = wrap(features, options);
|
||||
|
||||
// for updateable indexes, store a copy of the original simplified features
|
||||
if (options.updateable) {
|
||||
this.source = features;
|
||||
}
|
||||
|
||||
this.initializeIndex(features, options);
|
||||
}
|
||||
|
||||
private initializeIndex(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions) {
|
||||
this.tileIndex = options.cluster ? new ClusterTileIndex(options.clusterOptions) : new TileIndex(options);
|
||||
if (!features.length) return;
|
||||
this.tileIndex.initialize(features);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given z, x, and y tile coordinates, returns the corresponding tile with geometries in tile coordinates, much like MVT data is stored.
|
||||
* @param z - tile zoom level
|
||||
* @param x - tile x coordinate
|
||||
* @param y - tile y coordinate
|
||||
* @returns the transformed tile or null if not found
|
||||
*/
|
||||
getTile(z: number | string, x: number | string, y: number | string): GeoJSONVTTile | null {
|
||||
z = +z;
|
||||
x = +x;
|
||||
y = +y;
|
||||
|
||||
if (z < 0 || z > 24) return null;
|
||||
|
||||
return this.tileIndex.getTile(z, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the source data feature set using a {@link GeoJSONVTSourceDiff}
|
||||
* @param diff - the source diff object
|
||||
*/
|
||||
updateData(diff: GeoJSONVTSourceDiff, filter?: (feature: GeoJSON.Feature) => boolean) {
|
||||
const options = this.options;
|
||||
|
||||
if (!options.updateable) throw new Error('to update tile geojson `updateable` option must be set to true');
|
||||
|
||||
// apply diff and collect affected features and updated source that will be used to invalidate tiles
|
||||
let {affected, source} = applySourceDiff(this.source, diff, options);
|
||||
|
||||
if (filter) {
|
||||
({affected, source} = this.filterUpdate(source, affected, filter));
|
||||
}
|
||||
|
||||
// nothing has changed
|
||||
if (!affected.length) return;
|
||||
|
||||
// update source with new simplified feature set
|
||||
this.source = source;
|
||||
|
||||
this.tileIndex.updateIndex(source, affected, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter an update using a predicate function. Returns the affected and updated source features.
|
||||
*/
|
||||
private filterUpdate(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], predicate: (feature: GeoJSON.Feature) => boolean) {
|
||||
const removeIds = new Set();
|
||||
|
||||
for (const feature of source) {
|
||||
if (feature.id == undefined) continue;
|
||||
if (predicate(featureToGeoJSON(feature))) continue;
|
||||
affected.push(feature);
|
||||
removeIds.add(feature.id);
|
||||
}
|
||||
source = source.filter(feature => !removeIds.has(feature.id));
|
||||
|
||||
return {affected, source};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns source data as GeoJSON - only available when `updateable` option is set to true.
|
||||
*/
|
||||
getData(): GeoJSON.GeoJSON {
|
||||
if (!this.options.updateable) throw new Error('to retrieve data the `updateable` option must be set to true');
|
||||
return convertToGeoJSON(this.source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update supercluster options and regenerate the index.
|
||||
* @param cluster - whether to enable clustering
|
||||
* @param clusterOptions - {@link SuperclusterOptions}
|
||||
*/
|
||||
updateClusterOptions(cluster: boolean, clusterOptions: SuperclusterOptions) {
|
||||
const wasCluster = this.options.cluster;
|
||||
this.options.cluster = cluster;
|
||||
this.options.clusterOptions = clusterOptions;
|
||||
|
||||
if (wasCluster == cluster) {
|
||||
this.tileIndex.updateIndex(this.source, [], this.options);
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializeIndex(this.source, this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zoom level at which a cluster expands into multiple children.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @returns the expansion zoom or null in case of non-clustered source
|
||||
*/
|
||||
getClusterExpansionZoom(clusterId: number): number | null {
|
||||
return this.tileIndex.getClusterExpansionZoom(clusterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @returns the immediate children or null in case of non-clustered source
|
||||
*/
|
||||
getClusterChildren(clusterId: number): ClusterOrPointFeature[] | null {
|
||||
return this.tileIndex.getChildren(clusterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
|
||||
* @param clusterId - The target cluster id.
|
||||
* @param limit - Maximum number of points to return (defaults to `10`).
|
||||
* @param offset - Number of points to skip before collecting results (defaults to `0`).
|
||||
* @returns leaf point features under a cluster or null in case of non-clustered source
|
||||
*/
|
||||
getClusterLeaves(clusterId: number, limit: number, offset: number): GeoJSON.Feature<GeoJSON.Point>[] | null {
|
||||
return this.tileIndex.getLeaves(clusterId, limit, offset);
|
||||
}
|
||||
}
|
||||
46
node_modules/@maplibre/geojson-vt/src/index.ts
generated
vendored
Normal file
46
node_modules/@maplibre/geojson-vt/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
import type {GeoJSONVTFeatureDiff, GeoJSONVTSourceDiff} from './difference';
|
||||
import type {GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTOptions, GeoJSONToTileOptions, PartialGeoJSONVTFeature, StartEndSizeArray, ClusterProperties, ClusterFeature, ClusterOrPointFeature, GeoJSONVTTile, GeoJSONVTFeature, GeoJSONVTFeaturePoint, GeoJSONVTFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, GeoJSONVTTileIndex, SuperclusterOptions} from './definitions';
|
||||
import type {KDBushWithData} from './cluster-tile-index';
|
||||
import {GeoJSONVT} from './geojsonvt';
|
||||
import {ClusterTileIndex} from './cluster-tile-index';
|
||||
import {geoJSONToTile} from './geojson-to-tile';
|
||||
import {GEOJSONVT_CLIP_START, GEOJSONVT_CLIP_END} from './tile';
|
||||
|
||||
export {
|
||||
GeoJSONVT,
|
||||
ClusterTileIndex as Supercluster,
|
||||
geoJSONToTile,
|
||||
GEOJSONVT_CLIP_START,
|
||||
GEOJSONVT_CLIP_END
|
||||
}
|
||||
|
||||
export type {
|
||||
GeoJSONVTInternalFeature,
|
||||
GeoJSONVTOptions,
|
||||
GeoJSONToTileOptions,
|
||||
GeoJSONVTInternalTile,
|
||||
GeoJSONVTInternalTileFeature,
|
||||
PartialGeoJSONVTFeature,
|
||||
StartEndSizeArray,
|
||||
GeoJSONVTTile,
|
||||
GeoJSONVTFeature,
|
||||
GeoJSONVTSourceDiff,
|
||||
GeoJSONVTFeatureDiff,
|
||||
GeoJSONVTFeaturePoint,
|
||||
GeoJSONVTFeatureNonPoint,
|
||||
GeoJSONVTInternalTileFeaturePoint,
|
||||
GeoJSONVTInternalTileFeatureNonPoint,
|
||||
GeoJSONVTInternalPointFeature,
|
||||
GeoJSONVTInternalMultiPointFeature,
|
||||
GeoJSONVTInternalLineStringFeature,
|
||||
GeoJSONVTInternalMultiLineStringFeature,
|
||||
GeoJSONVTInternalPolygonFeature,
|
||||
GeoJSONVTInternalMultiPolygonFeature,
|
||||
SuperclusterOptions,
|
||||
ClusterProperties,
|
||||
ClusterFeature,
|
||||
ClusterOrPointFeature,
|
||||
KDBushWithData,
|
||||
GeoJSONVTTileIndex
|
||||
};
|
||||
73
node_modules/@maplibre/geojson-vt/src/simplify.test.ts
generated
vendored
Normal file
73
node_modules/@maplibre/geojson-vt/src/simplify.test.ts
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
import {test, expect} from 'vitest';
|
||||
import {simplify} from './simplify';
|
||||
|
||||
const points = [
|
||||
[0.22455,0.25015],[0.22691,0.24419],[0.23331,0.24145],[0.23498,0.23606],
|
||||
[0.24421,0.23276],[0.26259,0.21531],[0.26776,0.21381],[0.27357,0.20184],
|
||||
[0.27312,0.19216],[0.27762,0.18903],[0.28036,0.18141],[0.28651,0.17774],
|
||||
[0.29241,0.15937],[0.29691,0.15564],[0.31495,0.15137],[0.31975,0.14516],
|
||||
[0.33033,0.13757],[0.34148,0.13996],[0.36998,0.13789],[0.38739,0.14251],
|
||||
[0.39128,0.13939],[0.40952,0.14114],[0.41482,0.13975],[0.42772,0.12730],
|
||||
[0.43960,0.11974],[0.47493,0.10787],[0.48651,0.10675],[0.48920,0.10945],
|
||||
[0.49379,0.10863],[0.50474,0.11966],[0.51296,0.12235],[0.51863,0.12089],
|
||||
[0.52409,0.12688],[0.52957,0.12786],[0.53421,0.14093],[0.53927,0.14724],
|
||||
[0.56769,0.14891],[0.57525,0.15726],[0.58062,0.15815],[0.60153,0.15685],
|
||||
[0.61774,0.15986],[0.62200,0.16704],[0.62955,0.19460],[0.63890,0.19561],
|
||||
[0.64126,0.20081],[0.65177,0.20456],[0.67155,0.22255],[0.68368,0.21745],
|
||||
[0.69525,0.21915],[0.70064,0.21798],[0.70312,0.21436],[0.71226,0.21587],
|
||||
[0.72149,0.21281],[0.72781,0.21336],[0.72998,0.20873],[0.73532,0.20820],
|
||||
[0.73994,0.20477],[0.76998,0.20842],[0.77960,0.21687],[0.78420,0.21816],
|
||||
[0.80024,0.21462],[0.81053,0.21973],[0.81719,0.22682],[0.82077,0.23617],
|
||||
[0.82723,0.23616],[0.82989,0.23989],[0.85100,0.24894],[0.85988,0.25549],
|
||||
[0.86521,0.26853],[0.85795,0.28030],[0.86548,0.29145],[0.86681,0.29866],
|
||||
[0.86468,0.30271],[0.86779,0.30617],[0.85987,0.31137],[0.86008,0.31435],
|
||||
[0.85829,0.31494],[0.85810,0.32760],[0.85454,0.33540],[0.86092,0.34300],
|
||||
[0.85643,0.35015],[0.85142,0.35296],[0.84984,0.35959],[0.85456,0.36553],
|
||||
[0.84974,0.37038],[0.84409,0.37189],[0.84475,0.38044],[0.84152,0.38367],
|
||||
[0.83957,0.39040],[0.84559,0.39905],[0.84840,0.40755],[0.84371,0.41130],
|
||||
[0.84409,0.41988],[0.83951,0.43276],[0.84133,0.44104],[0.84762,0.44922],
|
||||
[0.84716,0.45844],[0.85138,0.46279],[0.85397,0.47115],[0.86636,0.48077]
|
||||
];
|
||||
|
||||
const simplified = [
|
||||
[0.22455,0.25015],[0.26776,0.21381],[0.29691,0.15564],[0.33033,0.13757],
|
||||
[0.40952,0.14114],[0.4396,0.11974],[0.48651,0.10675],[0.52957,0.12786],
|
||||
[0.53927,0.14724],[0.56769,0.14891],[0.61774,0.15986],[0.62955,0.1946],
|
||||
[0.67155,0.22255],[0.72781,0.21336],[0.73994,0.20477],[0.76998,0.20842],
|
||||
[0.7842,0.21816],[0.80024,0.21462],[0.82077,0.23617],[0.85988,0.25549],
|
||||
[0.86521,0.26853],[0.85795,0.2803],[0.86779,0.30617],[0.85829,0.31494],
|
||||
[0.85454,0.3354],[0.86092,0.343],[0.84984,0.35959],[0.85456,0.36553],
|
||||
[0.84409,0.37189],[0.83957,0.3904],[0.8484,0.40755],[0.83951,0.43276],
|
||||
[0.85397,0.47115],[0.86636,0.48077]
|
||||
];
|
||||
|
||||
test('simplifies points correctly with the given tolerance', () => {
|
||||
const coords = [];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
coords.push(points[i][0], points[i][1], 0);
|
||||
}
|
||||
|
||||
coords[2] = 1;
|
||||
coords[coords.length - 1] = 1;
|
||||
simplify(coords, 0, coords.length - 3, 0.001 * 0.001);
|
||||
|
||||
const result = [];
|
||||
for (let i = 0; i < coords.length; i += 3) {
|
||||
if (coords[i + 2] > 0.005 * 0.005) {
|
||||
result.push([coords[i], coords[i + 1]]);
|
||||
}
|
||||
}
|
||||
expect(result).toEqual(simplified);
|
||||
});
|
||||
|
||||
test('does not throw max call stack error on bad long input', () => {
|
||||
const coords: number[] = [];
|
||||
for (let i = 0; i < 1400; i++) {
|
||||
coords.push(0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
simplify(coords, 0, coords.length, 2e-15);
|
||||
}).not.toThrow();
|
||||
});
|
||||
78
node_modules/@maplibre/geojson-vt/src/simplify.ts
generated
vendored
Normal file
78
node_modules/@maplibre/geojson-vt/src/simplify.ts
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* calculate simplification data using optimized Douglas-Peucker algorithm
|
||||
* @param coords - flat array of coordinates
|
||||
* @param first - index of the first coordinate in the segment
|
||||
* @param last - index of the last coordinate in the segment
|
||||
* @param sqTolerance - square tolerance value
|
||||
*/
|
||||
export function simplify(coords: number[], first: number, last: number, sqTolerance: number) {
|
||||
let maxSqDist = sqTolerance;
|
||||
const mid = first + ((last - first) >> 1);
|
||||
let minPosToMid = last - first;
|
||||
let index;
|
||||
|
||||
const ax = coords[first];
|
||||
const ay = coords[first + 1];
|
||||
const bx = coords[last];
|
||||
const by = coords[last + 1];
|
||||
|
||||
for (let i = first + 3; i < last; i += 3) {
|
||||
const d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by);
|
||||
|
||||
if (d > maxSqDist) {
|
||||
index = i;
|
||||
maxSqDist = d;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d === maxSqDist) {
|
||||
// a workaround to ensure we choose a pivot close to the middle of the list,
|
||||
// reducing recursion depth, for certain degenerate inputs
|
||||
// https://github.com/mapbox/geojson-vt/issues/104
|
||||
const posToMid = Math.abs(i - mid);
|
||||
if (posToMid < minPosToMid) {
|
||||
index = i;
|
||||
minPosToMid = posToMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSqDist > sqTolerance) {
|
||||
if (index - first > 3) simplify(coords, first, index, sqTolerance);
|
||||
coords[index + 2] = maxSqDist;
|
||||
if (last - index > 3) simplify(coords, index, last, sqTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Claculates the square distance from a point to a segment
|
||||
* @param px - x coordinate of the point
|
||||
* @param py - y coordinate of the point
|
||||
* @param x - x coordinate of the first segment endpoint
|
||||
* @param y - y coordinate of the first segment endpoint
|
||||
* @param bx - x coordinate of the second segment endpoint
|
||||
* @param by - y coordinate of the second segment endpoint
|
||||
* @returns square distance from a point to a segment
|
||||
*/
|
||||
function getSqSegDist(px: number, py: number, x: number, y: number, bx: number, by: number): number {
|
||||
let dx = bx - x;
|
||||
let dy = by - y;
|
||||
|
||||
if (dx !== 0 || dy !== 0) {
|
||||
const t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
|
||||
|
||||
if (t > 1) {
|
||||
x = bx;
|
||||
y = by;
|
||||
|
||||
} else if (t > 0) {
|
||||
x += dx * t;
|
||||
y += dy * t;
|
||||
}
|
||||
}
|
||||
|
||||
dx = px - x;
|
||||
dy = py - y;
|
||||
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
310
node_modules/@maplibre/geojson-vt/src/tile-index.ts
generated
vendored
Normal file
310
node_modules/@maplibre/geojson-vt/src/tile-index.ts
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
import { AxisType, clip } from "./clip";
|
||||
import { createTile } from "./tile";
|
||||
import { transformTile } from "./transform";
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions, ClusterOrPointFeature, GeoJSONVTTileIndex, GeoJSONVTInternalTile, GeoJSONVTTile } from "./definitions";
|
||||
|
||||
export class TileIndex implements GeoJSONVTTileIndex {
|
||||
|
||||
private tileCoords: {z: number, x: number, y: number, id: number}[];
|
||||
|
||||
/** @internal */
|
||||
public tiles: {[key: string]: GeoJSONVTInternalTile};
|
||||
/** @internal */
|
||||
public stats: {[key: string]: number} = {};
|
||||
/** @internal */
|
||||
public total: number = 0;
|
||||
|
||||
constructor(private options: GeoJSONVTOptions) {
|
||||
this.tiles = {};
|
||||
this.tileCoords = [];
|
||||
this.stats = {};
|
||||
this.total = 0;
|
||||
}
|
||||
|
||||
initialize(features: GeoJSONVTInternalFeature[]): void {
|
||||
// start slicing from the top tile down
|
||||
this.splitTile(features, 0, 0, 0);
|
||||
|
||||
if (this.options.debug) {
|
||||
if (features.length) console.log('features: %d, points: %d', this.tiles[0].numFeatures, this.tiles[0].numPoints);
|
||||
console.timeEnd('generate tiles');
|
||||
console.log('tiles generated:', this.total, JSON.stringify(this.stats));
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
updateIndex(source: GeoJSONVTInternalFeature[], affected: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): void {
|
||||
if (options.debug > 1) {
|
||||
console.log('invalidating tiles');
|
||||
console.time('invalidating');
|
||||
}
|
||||
|
||||
this.invalidateTiles(affected);
|
||||
|
||||
if (options.debug > 1) console.timeEnd('invalidating');
|
||||
|
||||
// re-generate root tile with updated feature set
|
||||
const [z, x, y] = [0, 0, 0];
|
||||
const rootTile = createTile(source, z, x, y, options);
|
||||
rootTile.source = source;
|
||||
|
||||
// update tile index with new root tile - ready for getTile calls
|
||||
const id = toID(z, x, y);
|
||||
this.tiles[id] = rootTile;
|
||||
this.tileCoords.push({z, x, y, id});
|
||||
|
||||
if (options.debug) {
|
||||
const key = `z${ z}`;
|
||||
this.stats[key] = (this.stats[key] || 0) + 1;
|
||||
this.total++;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
getClusterExpansionZoom(_clusterId: number): number | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
getChildren(_clusterId: number): ClusterOrPointFeature[] | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
getLeaves(_clusterId: number, _limit?: number, _offset?: number): GeoJSON.Feature<GeoJSON.Point>[] | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
getTile(z: number, x: number, y: number): GeoJSONVTTile | null {
|
||||
const {extent, debug} = this.options;
|
||||
|
||||
const z2 = 1 << z;
|
||||
x = (x + z2) & (z2 - 1); // wrap tile x coordinate
|
||||
|
||||
const id = toID(z, x, y);
|
||||
if (this.tiles[id]) {
|
||||
return transformTile(this.tiles[id], extent);
|
||||
}
|
||||
|
||||
if (debug > 1) console.log('drilling down to z%d-%d-%d', z, x, y);
|
||||
|
||||
let z0 = z;
|
||||
let x0 = x;
|
||||
let y0 = y;
|
||||
let parent;
|
||||
|
||||
while (!parent && z0 > 0) {
|
||||
z0--;
|
||||
x0 = x0 >> 1;
|
||||
y0 = y0 >> 1;
|
||||
parent = this.tiles[toID(z0, x0, y0)];
|
||||
}
|
||||
|
||||
if (!parent?.source) return null;
|
||||
|
||||
// if we found a parent tile containing the original geometry, we can drill down from it
|
||||
if (debug > 1) {
|
||||
console.log('found parent tile z%d-%d-%d', z0, x0, y0);
|
||||
console.time('drilling down');
|
||||
}
|
||||
this.splitTile(parent.source, z0, x0, y0, z, x, y);
|
||||
if (debug > 1) console.timeEnd('drilling down');
|
||||
|
||||
if (!this.tiles[id]) return null;
|
||||
|
||||
return transformTile(this.tiles[id], extent);
|
||||
}
|
||||
|
||||
/**
|
||||
* splits features from a parent tile to sub-tiles.
|
||||
* z, x, and y are the coordinates of the parent tile
|
||||
* cz, cx, and cy are the coordinates of the target tile
|
||||
*
|
||||
* If no target tile is specified, splitting stops when we reach the maximum
|
||||
* zoom or the number of points is low as specified in the options.
|
||||
* @internal
|
||||
* @param features - features to split
|
||||
* @param z - tile zoom level
|
||||
* @param x - tile x coordinate
|
||||
* @param y - tile y coordinate
|
||||
* @param cz - target tile zoom level
|
||||
* @param cx - target tile x coordinate
|
||||
* @param cy - target tile y coordinate
|
||||
*/
|
||||
private splitTile(features: GeoJSONVTInternalFeature[], z: number, x: number, y: number, cz?: number, cx?: number, cy?: number) {
|
||||
|
||||
const stack = [features, z, x, y];
|
||||
const options = this.options;
|
||||
const debug = options.debug;
|
||||
|
||||
// avoid recursion by using a processing queue
|
||||
while (stack.length) {
|
||||
y = stack.pop() as number;
|
||||
x = stack.pop() as number;
|
||||
z = stack.pop() as number;
|
||||
features = stack.pop() as GeoJSONVTInternalFeature[];
|
||||
|
||||
const z2 = 1 << z;
|
||||
const id = toID(z, x, y);
|
||||
let tile = this.tiles[id];
|
||||
|
||||
if (!tile) {
|
||||
if (debug > 1) console.time('creation');
|
||||
|
||||
tile = this.tiles[id] = createTile(features, z, x, y, options);
|
||||
this.tileCoords.push({z, x, y, id});
|
||||
|
||||
if (debug) {
|
||||
if (debug > 1) {
|
||||
console.log('tile z%d-%d-%d (features: %d, points: %d, simplified: %d)',
|
||||
z, x, y, tile.numFeatures, tile.numPoints, tile.numSimplified);
|
||||
console.timeEnd('creation');
|
||||
}
|
||||
const key = `z${ z}`;
|
||||
this.stats[key] = (this.stats[key] || 0) + 1;
|
||||
this.total++;
|
||||
}
|
||||
}
|
||||
|
||||
// save reference to original geometry in tile so that we can drill down later if we stop now
|
||||
tile.source = features;
|
||||
|
||||
// if it's the first-pass tiling
|
||||
if (cz == null) {
|
||||
// stop tiling if we reached max zoom, or if the tile is too simple
|
||||
if (z === options.indexMaxZoom || tile.numPoints <= options.indexMaxPoints) continue;
|
||||
// if a drilldown to a specific tile
|
||||
} else if (z === options.maxZoom || z === cz) {
|
||||
// stop tiling if we reached base zoom or our target tile zoom
|
||||
continue;
|
||||
} else if (cz != null) {
|
||||
// stop tiling if it's not an ancestor of the target tile
|
||||
const zoomSteps = cz - z;
|
||||
if (x !== cx >> zoomSteps || y !== cy >> zoomSteps) continue;
|
||||
}
|
||||
|
||||
// if we slice further down, no need to keep source geometry
|
||||
tile.source = null;
|
||||
|
||||
if (!features.length) continue;
|
||||
|
||||
if (debug > 1) console.time('clipping');
|
||||
|
||||
// values we'll use for clipping
|
||||
const k1 = 0.5 * options.buffer / options.extent;
|
||||
const k2 = 0.5 - k1;
|
||||
const k3 = 0.5 + k1;
|
||||
const k4 = 1 + k1;
|
||||
|
||||
let tl = null;
|
||||
let bl = null;
|
||||
let tr = null;
|
||||
let br = null;
|
||||
|
||||
const left = clip(features, z2, x - k1, x + k3, AxisType.X, tile.minX, tile.maxX, options);
|
||||
const right = clip(features, z2, x + k2, x + k4, AxisType.X, tile.minX, tile.maxX, options);
|
||||
|
||||
if (left) {
|
||||
tl = clip(left, z2, y - k1, y + k3, AxisType.Y, tile.minY, tile.maxY, options);
|
||||
bl = clip(left, z2, y + k2, y + k4, AxisType.Y, tile.minY, tile.maxY, options);
|
||||
}
|
||||
|
||||
if (right) {
|
||||
tr = clip(right, z2, y - k1, y + k3, AxisType.Y, tile.minY, tile.maxY, options);
|
||||
br = clip(right, z2, y + k2, y + k4, AxisType.Y, tile.minY, tile.maxY, options);
|
||||
}
|
||||
|
||||
if (debug > 1) console.timeEnd('clipping');
|
||||
|
||||
stack.push(tl || [], z + 1, x * 2, y * 2);
|
||||
stack.push(bl || [], z + 1, x * 2, y * 2 + 1);
|
||||
stack.push(tr || [], z + 1, x * 2 + 1, y * 2);
|
||||
stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates (removes) tiles affected by the provided features
|
||||
* @internal
|
||||
* @param features
|
||||
*/
|
||||
private invalidateTiles(features: GeoJSONVTInternalFeature[]) {
|
||||
if (!features.length) return;
|
||||
const options = this.options;
|
||||
const {debug} = options;
|
||||
|
||||
// calculate bounding box of all features for trivial reject
|
||||
let minX = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let minY = Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
for (const feature of features) {
|
||||
minX = Math.min(minX, feature.minX);
|
||||
maxX = Math.max(maxX, feature.maxX);
|
||||
minY = Math.min(minY, feature.minY);
|
||||
maxY = Math.max(maxY, feature.maxY);
|
||||
}
|
||||
|
||||
// tile buffer clipping value - not halved as in splitTile above because checking against tile's own extent
|
||||
const k1 = options.buffer / options.extent;
|
||||
|
||||
// track removed tile ids for o(1) lookup
|
||||
const removedLookup = new Set();
|
||||
|
||||
// iterate through existing tiles and remove ones that are affected by features
|
||||
for (const id in this.tiles) {
|
||||
const tile = this.tiles[id];
|
||||
|
||||
// calculate tile bounds including buffer
|
||||
const z2 = 1 << tile.z;
|
||||
const tileMinX = (tile.x - k1) / z2;
|
||||
const tileMaxX = (tile.x + 1 + k1) / z2;
|
||||
const tileMinY = (tile.y - k1) / z2;
|
||||
const tileMaxY = (tile.y + 1 + k1) / z2;
|
||||
|
||||
// trivial reject if feature bounds don't intersect tile
|
||||
if (maxX < tileMinX || minX >= tileMaxX ||
|
||||
maxY < tileMinY || minY >= tileMaxY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if any feature intersects with the tile
|
||||
let intersects = false;
|
||||
for (const feature of features) {
|
||||
if (feature.maxX >= tileMinX && feature.minX < tileMaxX &&
|
||||
feature.maxY >= tileMinY && feature.minY < tileMaxY) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!intersects) continue;
|
||||
|
||||
if (debug) {
|
||||
if (debug > 1) {
|
||||
console.log('invalidate tile z%d-%d-%d (features: %d, points: %d, simplified: %d)',
|
||||
tile.z, tile.x, tile.y, tile.numFeatures, tile.numPoints, tile.numSimplified);
|
||||
}
|
||||
const key = `z${ tile.z}`;
|
||||
this.stats[key] = (this.stats[key] || 0) - 1;
|
||||
this.total--;
|
||||
}
|
||||
|
||||
delete this.tiles[id];
|
||||
removedLookup.add(id);
|
||||
}
|
||||
|
||||
// remove tile coords that are no longer in the index
|
||||
if (removedLookup.size) {
|
||||
this.tileCoords = this.tileCoords.filter(c => !removedLookup.has(c.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toID(z: number, x: number, y: number): number {
|
||||
return (((1 << z) * y + x) * 32) + z;
|
||||
}
|
||||
184
node_modules/@maplibre/geojson-vt/src/tile.ts
generated
vendored
Normal file
184
node_modules/@maplibre/geojson-vt/src/tile.ts
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTInternalLineStringFeature, GeoJSONVTInternalMultiLineStringFeature, GeoJSONVTInternalMultiPointFeature, GeoJSONVTInternalMultiPolygonFeature, GeoJSONVTInternalPointFeature, GeoJSONVTInternalPolygonFeature, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeature, GeoJSONVTOptions, StartEndSizeArray } from "./definitions";
|
||||
|
||||
export const GEOJSONVT_CLIP_START = 'geojsonvt_clip_start';
|
||||
export const GEOJSONVT_CLIP_END = 'geojsonvt_clip_end';
|
||||
|
||||
/**
|
||||
* Creates a tile object from the given features
|
||||
* @param features - the features to include in the tile
|
||||
* @param z
|
||||
* @param tx
|
||||
* @param ty
|
||||
* @param options - the options object
|
||||
* @returns the created tile
|
||||
*/
|
||||
export function createTile(features: GeoJSONVTInternalFeature[], z: number, tx: number, ty: number, options: GeoJSONVTOptions): GeoJSONVTInternalTile {
|
||||
const tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
|
||||
|
||||
const tile = {
|
||||
transformed: false,
|
||||
features: [] as GeoJSONVTInternalTileFeature[],
|
||||
source: null as GeoJSONVTInternalFeature[],
|
||||
x: tx,
|
||||
y: ty,
|
||||
z: z,
|
||||
minX: 2,
|
||||
minY: 1,
|
||||
maxX: -1,
|
||||
maxY: 0,
|
||||
numPoints: 0,
|
||||
numSimplified: 0,
|
||||
numFeatures: features.length
|
||||
};
|
||||
|
||||
for (const feature of features) {
|
||||
addFeature(tile, feature, tolerance, options);
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
function addFeature(tile: GeoJSONVTInternalTile, feature: GeoJSONVTInternalFeature, tolerance: number, options: GeoJSONVTOptions) {
|
||||
tile.minX = Math.min(tile.minX, feature.minX);
|
||||
tile.minY = Math.min(tile.minY, feature.minY);
|
||||
tile.maxX = Math.max(tile.maxX, feature.maxX);
|
||||
tile.maxY = Math.max(tile.maxY, feature.maxY);
|
||||
|
||||
switch (feature.type) {
|
||||
case 'Point':
|
||||
case 'MultiPoint':
|
||||
addPointsTileFeature(tile, feature);
|
||||
return;
|
||||
case 'LineString':
|
||||
addLineTileFeautre(tile, feature, tolerance, options);
|
||||
return;
|
||||
case 'MultiLineString':
|
||||
case 'Polygon':
|
||||
addLinesTileFeature(tile, feature, tolerance);
|
||||
return;
|
||||
case 'MultiPolygon':
|
||||
addMultiPolygonTileFeature(tile, feature, tolerance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function addPointsTileFeature(tile: GeoJSONVTInternalTile, feature: GeoJSONVTInternalPointFeature | GeoJSONVTInternalMultiPointFeature) {
|
||||
const geometry: number[] = [];
|
||||
for (let i = 0; i < feature.geometry.length; i += 3) {
|
||||
geometry.push(feature.geometry[i] , feature.geometry[i + 1]);
|
||||
tile.numPoints++;
|
||||
tile.numSimplified++;
|
||||
}
|
||||
if (!geometry.length) return;
|
||||
const tileFeature: GeoJSONVTInternalTileFeature = {
|
||||
type: 1,
|
||||
tags: feature.tags || null,
|
||||
geometry: geometry
|
||||
};
|
||||
if (feature.id !== null) {
|
||||
tileFeature.id = feature.id;
|
||||
}
|
||||
tile.features.push(tileFeature);
|
||||
}
|
||||
|
||||
function addLineTileFeautre(tile: GeoJSONVTInternalTile, feature: GeoJSONVTInternalLineStringFeature, tolerance: number, options: GeoJSONVTOptions) {
|
||||
const geometry: number[][] = [];
|
||||
addLine(geometry, feature.geometry, tile, tolerance, false, false);
|
||||
if (!geometry.length) return;
|
||||
let tags = feature.tags || null;
|
||||
if (options.lineMetrics) {
|
||||
tags = {};
|
||||
for (const key in feature.tags) tags[key] = feature.tags[key];
|
||||
tags[GEOJSONVT_CLIP_START] = feature.geometry.start / feature.geometry.size;
|
||||
tags[GEOJSONVT_CLIP_END] = feature.geometry.end / feature.geometry.size;
|
||||
}
|
||||
const tileFeature: GeoJSONVTInternalTileFeature = {
|
||||
type: 2,
|
||||
tags: tags,
|
||||
geometry: geometry
|
||||
}
|
||||
if (feature.id !== null) {
|
||||
tileFeature.id = feature.id;
|
||||
}
|
||||
tile.features.push(tileFeature);
|
||||
}
|
||||
|
||||
function addLinesTileFeature(tile: GeoJSONVTInternalTile, feature: GeoJSONVTInternalPolygonFeature | GeoJSONVTInternalMultiLineStringFeature, tolerance: number) {
|
||||
const geometry: number[][] = [];
|
||||
for (let i = 0; i < feature.geometry.length; i++) {
|
||||
addLine(geometry, feature.geometry[i], tile, tolerance, feature.type === 'Polygon', i === 0);
|
||||
}
|
||||
if (!geometry.length) return;
|
||||
const tileFeature: GeoJSONVTInternalTileFeature = {
|
||||
type: feature.type === 'Polygon' ? 3 : 2,
|
||||
tags: feature.tags || null,
|
||||
geometry: geometry
|
||||
}
|
||||
if (feature.id !== null) {
|
||||
tileFeature.id = feature.id;
|
||||
}
|
||||
tile.features.push(tileFeature);
|
||||
}
|
||||
|
||||
function addMultiPolygonTileFeature(tile: GeoJSONVTInternalTile, feature: GeoJSONVTInternalMultiPolygonFeature, tolerance: number) {
|
||||
const geometry: number[][] = [];
|
||||
for (let k = 0; k < feature.geometry.length; k++) {
|
||||
const polygon = feature.geometry[k];
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
addLine(geometry, polygon[i], tile, tolerance, true, i === 0);
|
||||
}
|
||||
}
|
||||
if (!geometry.length) return;
|
||||
const tileFeature: GeoJSONVTInternalTileFeature = {
|
||||
type: 3,
|
||||
tags: feature.tags || null,
|
||||
geometry: geometry
|
||||
}
|
||||
if (feature.id !== null) {
|
||||
tileFeature.id = feature.id;
|
||||
}
|
||||
tile.features.push(tileFeature);
|
||||
}
|
||||
|
||||
function addLine(result: number[][], geom: StartEndSizeArray, tile: GeoJSONVTInternalTile, tolerance: number, isPolygon: boolean, isOuter: boolean) {
|
||||
const sqTolerance = tolerance * tolerance;
|
||||
|
||||
if (tolerance > 0 && (geom.size < (isPolygon ? sqTolerance : tolerance))) {
|
||||
tile.numPoints += geom.length / 3;
|
||||
return;
|
||||
}
|
||||
|
||||
const ring = [];
|
||||
|
||||
for (let i = 0; i < geom.length; i += 3) {
|
||||
if (tolerance === 0 || geom[i + 2] > sqTolerance) {
|
||||
tile.numSimplified++;
|
||||
ring.push(geom[i], geom[i + 1]);
|
||||
}
|
||||
tile.numPoints++;
|
||||
}
|
||||
|
||||
if (isPolygon) rewind(ring, isOuter);
|
||||
|
||||
result.push(ring);
|
||||
}
|
||||
|
||||
function rewind(ring: number[], clockwise: boolean) {
|
||||
let area = 0;
|
||||
|
||||
for (let i = 0, len = ring.length, j = len - 2; i < len; j = i, i += 2) {
|
||||
area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]);
|
||||
}
|
||||
|
||||
if (area > 0 !== clockwise) return;
|
||||
|
||||
for (let i = 0, len = ring.length; i < len / 2; i += 2) {
|
||||
const x = ring[i];
|
||||
const y = ring[i + 1];
|
||||
|
||||
ring[i] = ring[len - 2 - i];
|
||||
ring[i + 1] = ring[len - 1 - i];
|
||||
ring[len - 2 - i] = x;
|
||||
ring[len - 1 - i] = y;
|
||||
}
|
||||
}
|
||||
72
node_modules/@maplibre/geojson-vt/src/transform.ts
generated
vendored
Normal file
72
node_modules/@maplibre/geojson-vt/src/transform.ts
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { GeoJSONVTFeatureNonPoint, GeoJSONVTFeaturePoint, GeoJSONVTInternalTile, GeoJSONVTInternalTileFeatureNonPoint, GeoJSONVTInternalTileFeaturePoint, GeoJSONVTTile } from "./definitions";
|
||||
|
||||
/**
|
||||
* Transforms the coordinates of each feature in the given tile from
|
||||
* mercator-projected space into (extent x extent) tile space.
|
||||
* @param tile - the tile to transform, this gets modified in place
|
||||
* @param extent - the tile extent (usually 4096)
|
||||
* @returns the transformed tile
|
||||
*/
|
||||
export function transformTile(tile: GeoJSONVTInternalTile, extent: number): GeoJSONVTTile {
|
||||
if (tile.transformed) {
|
||||
return tile as GeoJSONVTTile;
|
||||
}
|
||||
|
||||
const z2 = 1 << tile.z;
|
||||
const tx = tile.x;
|
||||
const ty = tile.y;
|
||||
|
||||
for (const feature of tile.features) {
|
||||
if (feature.type === 1) {
|
||||
transformPointFeature(feature, extent, z2, tx, ty);
|
||||
} else {
|
||||
transformNonPointFeature(feature, extent, z2, tx, ty);
|
||||
}
|
||||
}
|
||||
tile.transformed = true;
|
||||
|
||||
return tile as GeoJSONVTTile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a single point feature from mercator-projected space into (extent x extent) tile space.
|
||||
*/
|
||||
function transformPointFeature(feature: GeoJSONVTInternalTileFeaturePoint, extent: number, z2: number, tx: number, ty: number): GeoJSONVTFeaturePoint {
|
||||
const transformed = feature as unknown as GeoJSONVTFeaturePoint;
|
||||
|
||||
const geometry = feature.geometry;
|
||||
const point: GeoJSONVTFeaturePoint["geometry"] = [];
|
||||
for (let i = 0; i < geometry.length; i += 2) {
|
||||
point.push(transformPoint(geometry[i], geometry[i + 1], extent, z2, tx, ty));
|
||||
}
|
||||
transformed.geometry = point;
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a single non-point feature from mercator-projected space into (extent x extent) tile space.
|
||||
*/
|
||||
function transformNonPointFeature(feature: GeoJSONVTInternalTileFeatureNonPoint, extent: number, z2: number, tx: number, ty: number): GeoJSONVTFeatureNonPoint {
|
||||
const transformed = feature as unknown as GeoJSONVTFeatureNonPoint;
|
||||
|
||||
const geometry = feature.geometry;
|
||||
const nonPoint: GeoJSONVTFeatureNonPoint["geometry"] = [];
|
||||
for (const geom of geometry) {
|
||||
const ring: GeoJSONVTFeaturePoint["geometry"] = [];
|
||||
for (let i = 0; i < geom.length; i += 2) {
|
||||
ring.push(transformPoint(geom[i], geom[i + 1], extent, z2, tx, ty));
|
||||
}
|
||||
nonPoint.push(ring);
|
||||
}
|
||||
transformed.geometry = nonPoint;
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
function transformPoint(x: number, y: number, extent: number, z2: number, tx: number, ty: number): [number, number] {
|
||||
return [
|
||||
Math.round(extent * (x * z2 - tx)),
|
||||
Math.round(extent * (y * z2 - ty))
|
||||
];
|
||||
}
|
||||
81
node_modules/@maplibre/geojson-vt/src/wrap.ts
generated
vendored
Normal file
81
node_modules/@maplibre/geojson-vt/src/wrap.ts
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
import {AxisType, clip} from './clip';
|
||||
import type { GeoJSONVTInternalFeature, GeoJSONVTOptions, StartEndSizeArray } from './definitions';
|
||||
import {createFeature} from './feature';
|
||||
|
||||
export function wrap(features: GeoJSONVTInternalFeature[], options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
|
||||
const buffer = options.buffer / options.extent;
|
||||
let merged = features;
|
||||
|
||||
const left = clip(features, 1, -1 - buffer, buffer, AxisType.X, -1, 2, options); // left world copy
|
||||
const right = clip(features, 1, 1 - buffer, 2 + buffer, AxisType.X, -1, 2, options); // right world copy
|
||||
|
||||
if (!left && !right) return merged;
|
||||
|
||||
merged = clip(features, 1, -buffer, 1 + buffer, AxisType.X, -1, 2, options) || []; // center world copy
|
||||
|
||||
if (left) merged = shiftFeatureCoords(left, 1).concat(merged); // merge left into center
|
||||
if (right) merged = merged.concat(shiftFeatureCoords(right, -1)); // merge right into center
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function shiftFeatureCoords(features: GeoJSONVTInternalFeature[], offset: number): GeoJSONVTInternalFeature[] {
|
||||
const newFeatures = [];
|
||||
|
||||
for (const feature of features) {
|
||||
switch (feature.type) {
|
||||
case 'Point':
|
||||
case 'MultiPoint':
|
||||
case 'LineString': {
|
||||
const newGeometry = shiftCoords(feature.geometry, offset);
|
||||
|
||||
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'MultiLineString':
|
||||
case 'Polygon': {
|
||||
const newGeometry = [];
|
||||
for (const line of feature.geometry) {
|
||||
newGeometry.push(shiftCoords(line, offset));
|
||||
}
|
||||
|
||||
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'MultiPolygon': {
|
||||
const newGeometry = [];
|
||||
for (const polygon of feature.geometry) {
|
||||
const newPolygon = [];
|
||||
for (const line of polygon) {
|
||||
newPolygon.push(shiftCoords(line, offset));
|
||||
}
|
||||
newGeometry.push(newPolygon);
|
||||
}
|
||||
|
||||
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newFeatures;
|
||||
}
|
||||
|
||||
function shiftCoords(points: StartEndSizeArray, offset: number): number[] | StartEndSizeArray {
|
||||
const newPoints: StartEndSizeArray = [];
|
||||
newPoints.size = points.size;
|
||||
|
||||
if (points.start !== undefined) {
|
||||
newPoints.start = points.start;
|
||||
newPoints.end = points.end;
|
||||
}
|
||||
|
||||
for (let i = 0; i < points.length; i += 3) {
|
||||
newPoints.push(points[i] + offset, points[i + 1], points[i + 2]);
|
||||
}
|
||||
|
||||
return newPoints;
|
||||
}
|
||||
Reference in New Issue
Block a user