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;
|
||||
}
|
||||
116
node_modules/@maplibre/maplibre-gl-style-spec/LICENSE.txt
generated
vendored
Normal file
116
node_modules/@maplibre/maplibre-gl-style-spec/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
Copyright (c) 2020, MapLibre contributors
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of MapLibre GL JS nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Contains code from mapbox-gl-js v1.13 and earlier
|
||||
|
||||
Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
|
||||
|
||||
Copyright (c) 2020, Mapbox
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Mapbox GL JS nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Contains code from glfx.js
|
||||
|
||||
Copyright (C) 2011 by Evan Wallace
|
||||
|
||||
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.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Contains a portion of d3-color https://github.com/d3/d3-color
|
||||
|
||||
Copyright 2010-2016 Mike Bostock
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of contributors may be used to
|
||||
endorse or promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
94
node_modules/@maplibre/maplibre-gl-style-spec/README.md
generated
vendored
Normal file
94
node_modules/@maplibre/maplibre-gl-style-spec/README.md
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/7ff2cda8-f564-4e70-a971-d34152f969f0#gh-light-mode-only" alt="MapLibre Logo" width="200">
|
||||
<img src="https://github.com/user-attachments/assets/cee8376b-9812-40ff-91c6-2d53f9581b83#gh-dark-mode-only" alt="MapLibre Logo" width="200">
|
||||
</p>
|
||||
|
||||
# MapLibre Style Specification & Utilities
|
||||
|
||||
[](https://npmjs.org/package/@maplibre/maplibre-gl-style-spec)
|
||||
[](LICENSE.txt) [](https://opensource.org/licenses/BSD-3-Clause) [](https://codecov.io/gh/maplibre/maplibre-style-spec)
|
||||
|
||||
This repository contains code and reference files that define the MapLibre style specification and provides some utilities for working with MapLibre styles.
|
||||
|
||||
The style specification is used in MapLibre GL JS and in MapLibre Native. Our long-term goal is to have feature parity between the web and the native libraries.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to contribute to the style specification, please open an issue with a design proposal. Once your design proposal has been accepted, you can open a pull request and implement your changes.
|
||||
|
||||
We aim to avoid breaking changes in the MapLibre style specification because it makes life easier for our users.
|
||||
|
||||
## Documentation
|
||||
|
||||
The [documentation](https://maplibre.org/maplibre-style-spec) of the style specification also lives in this repository.
|
||||
We use [Zensical](https://www.zensical.org/).
|
||||
|
||||
To work on the documentation locally, you need to have Docker installed and running.
|
||||
Start Zensical with
|
||||
|
||||
```bash
|
||||
npm run start-docs
|
||||
```
|
||||
|
||||
Most of the documentation is generated (from e.g. `v8.json`).
|
||||
In another terminal, run:
|
||||
|
||||
```bash
|
||||
WATCH=1 npm run generate-docs
|
||||
```
|
||||
|
||||
This will re-run the generation script when needed.
|
||||
|
||||
Note that generated files should not be checked in, and they are excluded in `.gitignore`.
|
||||
Make sure to keep this file up to date and ignore generated files while making sure static Markdown files are not ignored.
|
||||
|
||||
## NPM Package
|
||||
|
||||
|
||||
The MapLibre style specification and utilities are published as a separate npm
|
||||
package so that they can be installed without the bulk of GL JS.
|
||||
|
||||
```bash
|
||||
npm install @maplibre/maplibre-gl-style-spec
|
||||
```
|
||||
|
||||
## CLI Tools
|
||||
|
||||
If you install this package globally, you will have access to several CLI tools.
|
||||
|
||||
```bash
|
||||
npm install @maplibre/maplibre-gl-style-spec --global
|
||||
```
|
||||
|
||||
### `gl-style-migrate`
|
||||
|
||||
This repo contains scripts for migrating GL styles of any version to the latest version (currently v8).
|
||||
You can migrate a style like this:
|
||||
|
||||
```bash
|
||||
$ gl-style-migrate bright-v7.json > bright-v8.json
|
||||
```
|
||||
|
||||
To migrate a file in place, you can use the `sponge` utility from the `moreutils` package:
|
||||
|
||||
```bash
|
||||
$ brew install moreutils
|
||||
$ gl-style-migrate bright.json | sponge bright.json
|
||||
```
|
||||
|
||||
### `gl-style-format`
|
||||
|
||||
```bash
|
||||
$ gl-style-format style.json
|
||||
```
|
||||
|
||||
Will format the given style JSON to use standard indentation and sorted object keys.
|
||||
|
||||
### `gl-style-validate`
|
||||
|
||||
```bash
|
||||
$ gl-style-validate style.json
|
||||
```
|
||||
|
||||
Will validate the given style JSON and print errors to stdout.
|
||||
Provide a `--json` flag to get JSON output.
|
||||
22
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-format.ts
generated
vendored
Normal file
22
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-format.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs';
|
||||
import minimist from 'minimist';
|
||||
import {format} from '../src/format';
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
|
||||
if (argv.help || argv.h || (!argv._.length && process.stdin.isTTY)) {
|
||||
help();
|
||||
} else {
|
||||
console.log(format(JSON.parse(fs.readFileSync(argv._[0]).toString()), argv.space));
|
||||
}
|
||||
|
||||
function help() {
|
||||
console.log('usage:');
|
||||
console.log(' gl-style-format source.json > destination.json');
|
||||
console.log('');
|
||||
console.log('options:');
|
||||
console.log(' --space <num>');
|
||||
console.log(' Number of spaces in output (default "2")');
|
||||
console.log(' Pass "0" for minified output.');
|
||||
}
|
||||
18
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-migrate.ts
generated
vendored
Normal file
18
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-migrate.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs';
|
||||
import minimist from 'minimist';
|
||||
import {format} from '../src/format';
|
||||
import {migrate} from '../src/migrate';
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
|
||||
if (argv.help || argv.h || (!argv._.length && process.stdin.isTTY)) {
|
||||
help();
|
||||
} else {
|
||||
console.log(format(migrate(JSON.parse(fs.readFileSync(argv._[0]).toString()))));
|
||||
}
|
||||
|
||||
function help() {
|
||||
console.log('usage:');
|
||||
console.log(' gl-style-migrate style-v7.json > style-v8.json');
|
||||
}
|
||||
44
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-validate.ts
generated
vendored
Normal file
44
node_modules/@maplibre/maplibre-gl-style-spec/bin/gl-style-validate.ts
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import minimist from 'minimist';
|
||||
import rw from 'rw';
|
||||
import {validateStyle as validate} from '../src/validate_style';
|
||||
|
||||
const argv = minimist(process.argv.slice(2), {
|
||||
boolean: 'json'
|
||||
});
|
||||
|
||||
if (argv.help || argv.h || (!argv._.length && process.stdin.isTTY)) {
|
||||
help();
|
||||
} else {
|
||||
let status = 0;
|
||||
|
||||
if (!argv._.length) {
|
||||
argv._.push('/dev/stdin');
|
||||
}
|
||||
|
||||
argv._.forEach((file) => {
|
||||
const errors = validate(rw.readFileSync(file, 'utf8'));
|
||||
if (errors.length) {
|
||||
if (argv.json) {
|
||||
process.stdout.write(JSON.stringify(errors, null, 2));
|
||||
} else {
|
||||
errors.forEach((e) => {
|
||||
console.log('%s:%d: %s', file, e.line, e.message);
|
||||
});
|
||||
}
|
||||
status = 1;
|
||||
}
|
||||
});
|
||||
|
||||
process.exit(status);
|
||||
}
|
||||
|
||||
function help() {
|
||||
console.log('usage:');
|
||||
console.log(' gl-style-validate file.json');
|
||||
console.log(' gl-style-validate < file.json');
|
||||
console.log('');
|
||||
console.log('options:');
|
||||
console.log('--json output errors as json');
|
||||
}
|
||||
591
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.cjs
generated
vendored
Normal file
591
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.cjs
generated
vendored
Normal file
@@ -0,0 +1,591 @@
|
||||
#!/usr/bin/env node
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('fs')) :
|
||||
typeof define === 'function' && define.amd ? define(['fs'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.fs));
|
||||
})(this, (function (fs) { 'use strict';
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
var minimist$1;
|
||||
var hasRequiredMinimist;
|
||||
|
||||
function requireMinimist () {
|
||||
if (hasRequiredMinimist) return minimist$1;
|
||||
hasRequiredMinimist = 1;
|
||||
|
||||
function hasKey(obj, keys) {
|
||||
var o = obj;
|
||||
keys.slice(0, -1).forEach(function (key) {
|
||||
o = o[key] || {};
|
||||
});
|
||||
|
||||
var key = keys[keys.length - 1];
|
||||
return key in o;
|
||||
}
|
||||
|
||||
function isNumber(x) {
|
||||
if (typeof x === 'number') { return true; }
|
||||
if ((/^0x[0-9a-f]+$/i).test(x)) { return true; }
|
||||
return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x);
|
||||
}
|
||||
|
||||
function isConstructorOrProto(obj, key) {
|
||||
return (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__';
|
||||
}
|
||||
|
||||
minimist$1 = function (args, opts) {
|
||||
if (!opts) { opts = {}; }
|
||||
|
||||
var flags = {
|
||||
bools: {},
|
||||
strings: {},
|
||||
unknownFn: null,
|
||||
};
|
||||
|
||||
if (typeof opts.unknown === 'function') {
|
||||
flags.unknownFn = opts.unknown;
|
||||
}
|
||||
|
||||
if (typeof opts.boolean === 'boolean' && opts.boolean) {
|
||||
flags.allBools = true;
|
||||
} else {
|
||||
[].concat(opts.boolean).filter(Boolean).forEach(function (key) {
|
||||
flags.bools[key] = true;
|
||||
});
|
||||
}
|
||||
|
||||
var aliases = {};
|
||||
|
||||
function aliasIsBoolean(key) {
|
||||
return aliases[key].some(function (x) {
|
||||
return flags.bools[x];
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(opts.alias || {}).forEach(function (key) {
|
||||
aliases[key] = [].concat(opts.alias[key]);
|
||||
aliases[key].forEach(function (x) {
|
||||
aliases[x] = [key].concat(aliases[key].filter(function (y) {
|
||||
return x !== y;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
[].concat(opts.string).filter(Boolean).forEach(function (key) {
|
||||
flags.strings[key] = true;
|
||||
if (aliases[key]) {
|
||||
[].concat(aliases[key]).forEach(function (k) {
|
||||
flags.strings[k] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var defaults = opts.default || {};
|
||||
|
||||
var argv = { _: [] };
|
||||
|
||||
function argDefined(key, arg) {
|
||||
return (flags.allBools && (/^--[^=]+$/).test(arg))
|
||||
|| flags.strings[key]
|
||||
|| flags.bools[key]
|
||||
|| aliases[key];
|
||||
}
|
||||
|
||||
function setKey(obj, keys, value) {
|
||||
var o = obj;
|
||||
for (var i = 0; i < keys.length - 1; i++) {
|
||||
var key = keys[i];
|
||||
if (isConstructorOrProto(o, key)) { return; }
|
||||
if (o[key] === undefined) { o[key] = {}; }
|
||||
if (
|
||||
o[key] === Object.prototype
|
||||
|| o[key] === Number.prototype
|
||||
|| o[key] === String.prototype
|
||||
) {
|
||||
o[key] = {};
|
||||
}
|
||||
if (o[key] === Array.prototype) { o[key] = []; }
|
||||
o = o[key];
|
||||
}
|
||||
|
||||
var lastKey = keys[keys.length - 1];
|
||||
if (isConstructorOrProto(o, lastKey)) { return; }
|
||||
if (
|
||||
o === Object.prototype
|
||||
|| o === Number.prototype
|
||||
|| o === String.prototype
|
||||
) {
|
||||
o = {};
|
||||
}
|
||||
if (o === Array.prototype) { o = []; }
|
||||
if (o[lastKey] === undefined || flags.bools[lastKey] || typeof o[lastKey] === 'boolean') {
|
||||
o[lastKey] = value;
|
||||
} else if (Array.isArray(o[lastKey])) {
|
||||
o[lastKey].push(value);
|
||||
} else {
|
||||
o[lastKey] = [o[lastKey], value];
|
||||
}
|
||||
}
|
||||
|
||||
function setArg(key, val, arg) {
|
||||
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
||||
if (flags.unknownFn(arg) === false) { return; }
|
||||
}
|
||||
|
||||
var value = !flags.strings[key] && isNumber(val)
|
||||
? Number(val)
|
||||
: val;
|
||||
setKey(argv, key.split('.'), value);
|
||||
|
||||
(aliases[key] || []).forEach(function (x) {
|
||||
setKey(argv, x.split('.'), value);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(flags.bools).forEach(function (key) {
|
||||
setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
||||
});
|
||||
|
||||
var notFlags = [];
|
||||
|
||||
if (args.indexOf('--') !== -1) {
|
||||
notFlags = args.slice(args.indexOf('--') + 1);
|
||||
args = args.slice(0, args.indexOf('--'));
|
||||
}
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var key;
|
||||
var next;
|
||||
|
||||
if ((/^--.+=/).test(arg)) {
|
||||
// Using [\s\S] instead of . because js doesn't support the
|
||||
// 'dotall' regex modifier. See:
|
||||
// http://stackoverflow.com/a/1068308/13216
|
||||
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
||||
key = m[1];
|
||||
var value = m[2];
|
||||
if (flags.bools[key]) {
|
||||
value = value !== 'false';
|
||||
}
|
||||
setArg(key, value, arg);
|
||||
} else if ((/^--no-.+/).test(arg)) {
|
||||
key = arg.match(/^--no-(.+)/)[1];
|
||||
setArg(key, false, arg);
|
||||
} else if ((/^--.+/).test(arg)) {
|
||||
key = arg.match(/^--(.+)/)[1];
|
||||
next = args[i + 1];
|
||||
if (
|
||||
next !== undefined
|
||||
&& !(/^(-|--)[^-]/).test(next)
|
||||
&& !flags.bools[key]
|
||||
&& !flags.allBools
|
||||
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, next, arg);
|
||||
i += 1;
|
||||
} else if ((/^(true|false)$/).test(next)) {
|
||||
setArg(key, next === 'true', arg);
|
||||
i += 1;
|
||||
} else {
|
||||
setArg(key, flags.strings[key] ? '' : true, arg);
|
||||
}
|
||||
} else if ((/^-[^-]+/).test(arg)) {
|
||||
var letters = arg.slice(1, -1).split('');
|
||||
|
||||
var broken = false;
|
||||
for (var j = 0; j < letters.length; j++) {
|
||||
next = arg.slice(j + 2);
|
||||
|
||||
if (next === '-') {
|
||||
setArg(letters[j], next, arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((/[A-Za-z]/).test(letters[j]) && next[0] === '=') {
|
||||
setArg(letters[j], next.slice(1), arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
(/[A-Za-z]/).test(letters[j])
|
||||
&& (/-?\d+(\.\d*)?(e-?\d+)?$/).test(next)
|
||||
) {
|
||||
setArg(letters[j], next, arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
|
||||
setArg(letters[j], arg.slice(j + 2), arg);
|
||||
broken = true;
|
||||
break;
|
||||
} else {
|
||||
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
|
||||
}
|
||||
}
|
||||
|
||||
key = arg.slice(-1)[0];
|
||||
if (!broken && key !== '-') {
|
||||
if (
|
||||
args[i + 1]
|
||||
&& !(/^(-|--)[^-]/).test(args[i + 1])
|
||||
&& !flags.bools[key]
|
||||
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, args[i + 1], arg);
|
||||
i += 1;
|
||||
} else if (args[i + 1] && (/^(true|false)$/).test(args[i + 1])) {
|
||||
setArg(key, args[i + 1] === 'true', arg);
|
||||
i += 1;
|
||||
} else {
|
||||
setArg(key, flags.strings[key] ? '' : true, arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
|
||||
argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));
|
||||
}
|
||||
if (opts.stopEarly) {
|
||||
argv._.push.apply(argv._, args.slice(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(defaults).forEach(function (k) {
|
||||
if (!hasKey(argv, k.split('.'))) {
|
||||
setKey(argv, k.split('.'), defaults[k]);
|
||||
|
||||
(aliases[k] || []).forEach(function (x) {
|
||||
setKey(argv, x.split('.'), defaults[k]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (opts['--']) {
|
||||
argv['--'] = notFlags.slice();
|
||||
} else {
|
||||
notFlags.forEach(function (k) {
|
||||
argv._.push(k);
|
||||
});
|
||||
}
|
||||
|
||||
return argv;
|
||||
};
|
||||
return minimist$1;
|
||||
}
|
||||
|
||||
var minimistExports = requireMinimist();
|
||||
var minimist = /*@__PURE__*/getDefaultExportFromCjs(minimistExports);
|
||||
|
||||
var $root = {
|
||||
version: {
|
||||
required: true,
|
||||
type: "enum",
|
||||
values: [
|
||||
8
|
||||
]
|
||||
},
|
||||
name: {
|
||||
type: "string"
|
||||
},
|
||||
metadata: {
|
||||
type: "*"
|
||||
},
|
||||
center: {
|
||||
type: "array",
|
||||
value: "number",
|
||||
length: 2
|
||||
},
|
||||
centerAltitude: {
|
||||
type: "number"
|
||||
},
|
||||
zoom: {
|
||||
type: "number"
|
||||
},
|
||||
bearing: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
period: 360,
|
||||
units: "degrees"
|
||||
},
|
||||
pitch: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
units: "degrees"
|
||||
},
|
||||
roll: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
units: "degrees"
|
||||
},
|
||||
state: {
|
||||
type: "state",
|
||||
"default": {
|
||||
}
|
||||
},
|
||||
light: {
|
||||
type: "light"
|
||||
},
|
||||
sky: {
|
||||
type: "sky"
|
||||
},
|
||||
projection: {
|
||||
type: "projection"
|
||||
},
|
||||
terrain: {
|
||||
type: "terrain"
|
||||
},
|
||||
sources: {
|
||||
required: true,
|
||||
type: "sources"
|
||||
},
|
||||
sprite: {
|
||||
type: "sprite"
|
||||
},
|
||||
glyphs: {
|
||||
type: "string"
|
||||
},
|
||||
"font-faces": {
|
||||
type: "fontFaces"
|
||||
},
|
||||
transition: {
|
||||
type: "transition"
|
||||
},
|
||||
layers: {
|
||||
required: true,
|
||||
type: "array",
|
||||
value: "layer"
|
||||
}
|
||||
};
|
||||
var layer = {
|
||||
id: {
|
||||
type: "string",
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: "enum",
|
||||
values: {
|
||||
fill: {
|
||||
},
|
||||
line: {
|
||||
},
|
||||
symbol: {
|
||||
},
|
||||
circle: {
|
||||
},
|
||||
heatmap: {
|
||||
},
|
||||
"fill-extrusion": {
|
||||
},
|
||||
raster: {
|
||||
},
|
||||
hillshade: {
|
||||
},
|
||||
"color-relief": {
|
||||
},
|
||||
background: {
|
||||
}
|
||||
},
|
||||
required: true
|
||||
},
|
||||
metadata: {
|
||||
type: "*"
|
||||
},
|
||||
source: {
|
||||
type: "string"
|
||||
},
|
||||
"source-layer": {
|
||||
type: "string"
|
||||
},
|
||||
minzoom: {
|
||||
type: "number",
|
||||
minimum: 0,
|
||||
maximum: 24
|
||||
},
|
||||
maxzoom: {
|
||||
type: "number",
|
||||
minimum: 0,
|
||||
maximum: 24
|
||||
},
|
||||
filter: {
|
||||
type: "filter"
|
||||
},
|
||||
layout: {
|
||||
type: "layout"
|
||||
},
|
||||
paint: {
|
||||
type: "paint"
|
||||
}
|
||||
};
|
||||
var latest = {
|
||||
$root: $root,
|
||||
layer: layer};
|
||||
|
||||
// Note: This regex matches even invalid JSON strings, but since we’re
|
||||
// working on the output of `JSON.stringify` we know that only valid strings
|
||||
// are present (unless the user supplied a weird `options.indent` but in
|
||||
// that case we don’t care since the output would be invalid anyway).
|
||||
const stringOrChar = /("(?:[^\\"]|\\.)*")|[:,]/g;
|
||||
|
||||
function stringify(passedObj, options = {}) {
|
||||
const indent = JSON.stringify(
|
||||
[1],
|
||||
undefined,
|
||||
options.indent === undefined ? 2 : options.indent
|
||||
).slice(2, -3);
|
||||
|
||||
const maxLength =
|
||||
indent === ""
|
||||
? Infinity
|
||||
: options.maxLength === undefined
|
||||
? 80
|
||||
: options.maxLength;
|
||||
|
||||
let { replacer } = options;
|
||||
|
||||
return (function _stringify(obj, currentIndent, reserved) {
|
||||
if (obj && typeof obj.toJSON === "function") {
|
||||
obj = obj.toJSON();
|
||||
}
|
||||
|
||||
const string = JSON.stringify(obj, replacer);
|
||||
|
||||
if (string === undefined) {
|
||||
return string;
|
||||
}
|
||||
|
||||
const length = maxLength - currentIndent.length - reserved;
|
||||
|
||||
if (string.length <= length) {
|
||||
const prettified = string.replace(
|
||||
stringOrChar,
|
||||
(match, stringLiteral) => {
|
||||
return stringLiteral || `${match} `;
|
||||
}
|
||||
);
|
||||
if (prettified.length <= length) {
|
||||
return prettified;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacer != null) {
|
||||
obj = JSON.parse(string);
|
||||
replacer = undefined;
|
||||
}
|
||||
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
const nextIndent = currentIndent + indent;
|
||||
const items = [];
|
||||
let index = 0;
|
||||
let start;
|
||||
let end;
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
start = "[";
|
||||
end = "]";
|
||||
const { length } = obj;
|
||||
for (; index < length; index++) {
|
||||
items.push(
|
||||
_stringify(obj[index], nextIndent, index === length - 1 ? 0 : 1) ||
|
||||
"null"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
start = "{";
|
||||
end = "}";
|
||||
const keys = Object.keys(obj);
|
||||
const { length } = keys;
|
||||
for (; index < length; index++) {
|
||||
const key = keys[index];
|
||||
const keyPart = `${JSON.stringify(key)}: `;
|
||||
const value = _stringify(
|
||||
obj[key],
|
||||
nextIndent,
|
||||
keyPart.length + (index === length - 1 ? 0 : 1)
|
||||
);
|
||||
if (value !== undefined) {
|
||||
items.push(keyPart + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
return [start, indent + items.join(`,\n${nextIndent}`), end].join(
|
||||
`\n${currentIndent}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
})(passedObj, "", 0);
|
||||
}
|
||||
|
||||
function sortKeysBy(obj, reference) {
|
||||
const result = {};
|
||||
for (const key in reference) {
|
||||
if (obj[key] !== undefined) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
for (const key in obj) {
|
||||
if (result[key] === undefined) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Format a MapLibre Style. Returns a stringified style with its keys
|
||||
* sorted in the same order as the reference style.
|
||||
*
|
||||
* The optional `space` argument is passed to
|
||||
* [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
|
||||
* to generate formatted output.
|
||||
*
|
||||
* If `space` is unspecified, a default of `2` spaces will be used.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} style a MapLibre Style
|
||||
* @param {number} [space] space argument to pass to `JSON.stringify`
|
||||
* @returns {string} stringified formatted JSON
|
||||
* @example
|
||||
* var fs = require('fs');
|
||||
* var format = require('maplibre-gl-style-spec').format;
|
||||
* var style = fs.readFileSync('./source.json', 'utf8');
|
||||
* fs.writeFileSync('./dest.json', format(style));
|
||||
* fs.writeFileSync('./dest.min.json', format(style, 0));
|
||||
*/
|
||||
function format(style, space = 2) {
|
||||
style = sortKeysBy(style, latest.$root);
|
||||
if (style.layers) {
|
||||
style.layers = style.layers.map((layer) => sortKeysBy(layer, latest.layer));
|
||||
}
|
||||
return stringify(style, { indent: space });
|
||||
}
|
||||
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
|
||||
if (argv.help || argv.h || (!argv._.length && process.stdin.isTTY)) {
|
||||
help();
|
||||
} else {
|
||||
console.log(format(JSON.parse(fs.readFileSync(argv._[0]).toString()), argv.space));
|
||||
}
|
||||
|
||||
function help() {
|
||||
console.log('usage:');
|
||||
console.log(' gl-style-format source.json > destination.json');
|
||||
console.log('');
|
||||
console.log('options:');
|
||||
console.log(' --space <num>');
|
||||
console.log(' Number of spaces in output (default "2")');
|
||||
console.log(' Pass "0" for minified output.');
|
||||
}
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=gl-style-format.cjs.map
|
||||
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.cjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
585
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.mjs
generated
vendored
Normal file
585
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.mjs
generated
vendored
Normal file
@@ -0,0 +1,585 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from 'fs';
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
var minimist$1;
|
||||
var hasRequiredMinimist;
|
||||
|
||||
function requireMinimist () {
|
||||
if (hasRequiredMinimist) return minimist$1;
|
||||
hasRequiredMinimist = 1;
|
||||
|
||||
function hasKey(obj, keys) {
|
||||
var o = obj;
|
||||
keys.slice(0, -1).forEach(function (key) {
|
||||
o = o[key] || {};
|
||||
});
|
||||
|
||||
var key = keys[keys.length - 1];
|
||||
return key in o;
|
||||
}
|
||||
|
||||
function isNumber(x) {
|
||||
if (typeof x === 'number') { return true; }
|
||||
if ((/^0x[0-9a-f]+$/i).test(x)) { return true; }
|
||||
return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x);
|
||||
}
|
||||
|
||||
function isConstructorOrProto(obj, key) {
|
||||
return (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__';
|
||||
}
|
||||
|
||||
minimist$1 = function (args, opts) {
|
||||
if (!opts) { opts = {}; }
|
||||
|
||||
var flags = {
|
||||
bools: {},
|
||||
strings: {},
|
||||
unknownFn: null,
|
||||
};
|
||||
|
||||
if (typeof opts.unknown === 'function') {
|
||||
flags.unknownFn = opts.unknown;
|
||||
}
|
||||
|
||||
if (typeof opts.boolean === 'boolean' && opts.boolean) {
|
||||
flags.allBools = true;
|
||||
} else {
|
||||
[].concat(opts.boolean).filter(Boolean).forEach(function (key) {
|
||||
flags.bools[key] = true;
|
||||
});
|
||||
}
|
||||
|
||||
var aliases = {};
|
||||
|
||||
function aliasIsBoolean(key) {
|
||||
return aliases[key].some(function (x) {
|
||||
return flags.bools[x];
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(opts.alias || {}).forEach(function (key) {
|
||||
aliases[key] = [].concat(opts.alias[key]);
|
||||
aliases[key].forEach(function (x) {
|
||||
aliases[x] = [key].concat(aliases[key].filter(function (y) {
|
||||
return x !== y;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
[].concat(opts.string).filter(Boolean).forEach(function (key) {
|
||||
flags.strings[key] = true;
|
||||
if (aliases[key]) {
|
||||
[].concat(aliases[key]).forEach(function (k) {
|
||||
flags.strings[k] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var defaults = opts.default || {};
|
||||
|
||||
var argv = { _: [] };
|
||||
|
||||
function argDefined(key, arg) {
|
||||
return (flags.allBools && (/^--[^=]+$/).test(arg))
|
||||
|| flags.strings[key]
|
||||
|| flags.bools[key]
|
||||
|| aliases[key];
|
||||
}
|
||||
|
||||
function setKey(obj, keys, value) {
|
||||
var o = obj;
|
||||
for (var i = 0; i < keys.length - 1; i++) {
|
||||
var key = keys[i];
|
||||
if (isConstructorOrProto(o, key)) { return; }
|
||||
if (o[key] === undefined) { o[key] = {}; }
|
||||
if (
|
||||
o[key] === Object.prototype
|
||||
|| o[key] === Number.prototype
|
||||
|| o[key] === String.prototype
|
||||
) {
|
||||
o[key] = {};
|
||||
}
|
||||
if (o[key] === Array.prototype) { o[key] = []; }
|
||||
o = o[key];
|
||||
}
|
||||
|
||||
var lastKey = keys[keys.length - 1];
|
||||
if (isConstructorOrProto(o, lastKey)) { return; }
|
||||
if (
|
||||
o === Object.prototype
|
||||
|| o === Number.prototype
|
||||
|| o === String.prototype
|
||||
) {
|
||||
o = {};
|
||||
}
|
||||
if (o === Array.prototype) { o = []; }
|
||||
if (o[lastKey] === undefined || flags.bools[lastKey] || typeof o[lastKey] === 'boolean') {
|
||||
o[lastKey] = value;
|
||||
} else if (Array.isArray(o[lastKey])) {
|
||||
o[lastKey].push(value);
|
||||
} else {
|
||||
o[lastKey] = [o[lastKey], value];
|
||||
}
|
||||
}
|
||||
|
||||
function setArg(key, val, arg) {
|
||||
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
||||
if (flags.unknownFn(arg) === false) { return; }
|
||||
}
|
||||
|
||||
var value = !flags.strings[key] && isNumber(val)
|
||||
? Number(val)
|
||||
: val;
|
||||
setKey(argv, key.split('.'), value);
|
||||
|
||||
(aliases[key] || []).forEach(function (x) {
|
||||
setKey(argv, x.split('.'), value);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(flags.bools).forEach(function (key) {
|
||||
setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
||||
});
|
||||
|
||||
var notFlags = [];
|
||||
|
||||
if (args.indexOf('--') !== -1) {
|
||||
notFlags = args.slice(args.indexOf('--') + 1);
|
||||
args = args.slice(0, args.indexOf('--'));
|
||||
}
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var key;
|
||||
var next;
|
||||
|
||||
if ((/^--.+=/).test(arg)) {
|
||||
// Using [\s\S] instead of . because js doesn't support the
|
||||
// 'dotall' regex modifier. See:
|
||||
// http://stackoverflow.com/a/1068308/13216
|
||||
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
||||
key = m[1];
|
||||
var value = m[2];
|
||||
if (flags.bools[key]) {
|
||||
value = value !== 'false';
|
||||
}
|
||||
setArg(key, value, arg);
|
||||
} else if ((/^--no-.+/).test(arg)) {
|
||||
key = arg.match(/^--no-(.+)/)[1];
|
||||
setArg(key, false, arg);
|
||||
} else if ((/^--.+/).test(arg)) {
|
||||
key = arg.match(/^--(.+)/)[1];
|
||||
next = args[i + 1];
|
||||
if (
|
||||
next !== undefined
|
||||
&& !(/^(-|--)[^-]/).test(next)
|
||||
&& !flags.bools[key]
|
||||
&& !flags.allBools
|
||||
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, next, arg);
|
||||
i += 1;
|
||||
} else if ((/^(true|false)$/).test(next)) {
|
||||
setArg(key, next === 'true', arg);
|
||||
i += 1;
|
||||
} else {
|
||||
setArg(key, flags.strings[key] ? '' : true, arg);
|
||||
}
|
||||
} else if ((/^-[^-]+/).test(arg)) {
|
||||
var letters = arg.slice(1, -1).split('');
|
||||
|
||||
var broken = false;
|
||||
for (var j = 0; j < letters.length; j++) {
|
||||
next = arg.slice(j + 2);
|
||||
|
||||
if (next === '-') {
|
||||
setArg(letters[j], next, arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((/[A-Za-z]/).test(letters[j]) && next[0] === '=') {
|
||||
setArg(letters[j], next.slice(1), arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
(/[A-Za-z]/).test(letters[j])
|
||||
&& (/-?\d+(\.\d*)?(e-?\d+)?$/).test(next)
|
||||
) {
|
||||
setArg(letters[j], next, arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
|
||||
setArg(letters[j], arg.slice(j + 2), arg);
|
||||
broken = true;
|
||||
break;
|
||||
} else {
|
||||
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
|
||||
}
|
||||
}
|
||||
|
||||
key = arg.slice(-1)[0];
|
||||
if (!broken && key !== '-') {
|
||||
if (
|
||||
args[i + 1]
|
||||
&& !(/^(-|--)[^-]/).test(args[i + 1])
|
||||
&& !flags.bools[key]
|
||||
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, args[i + 1], arg);
|
||||
i += 1;
|
||||
} else if (args[i + 1] && (/^(true|false)$/).test(args[i + 1])) {
|
||||
setArg(key, args[i + 1] === 'true', arg);
|
||||
i += 1;
|
||||
} else {
|
||||
setArg(key, flags.strings[key] ? '' : true, arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
|
||||
argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));
|
||||
}
|
||||
if (opts.stopEarly) {
|
||||
argv._.push.apply(argv._, args.slice(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(defaults).forEach(function (k) {
|
||||
if (!hasKey(argv, k.split('.'))) {
|
||||
setKey(argv, k.split('.'), defaults[k]);
|
||||
|
||||
(aliases[k] || []).forEach(function (x) {
|
||||
setKey(argv, x.split('.'), defaults[k]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (opts['--']) {
|
||||
argv['--'] = notFlags.slice();
|
||||
} else {
|
||||
notFlags.forEach(function (k) {
|
||||
argv._.push(k);
|
||||
});
|
||||
}
|
||||
|
||||
return argv;
|
||||
};
|
||||
return minimist$1;
|
||||
}
|
||||
|
||||
var minimistExports = requireMinimist();
|
||||
var minimist = /*@__PURE__*/getDefaultExportFromCjs(minimistExports);
|
||||
|
||||
var $root = {
|
||||
version: {
|
||||
required: true,
|
||||
type: "enum",
|
||||
values: [
|
||||
8
|
||||
]
|
||||
},
|
||||
name: {
|
||||
type: "string"
|
||||
},
|
||||
metadata: {
|
||||
type: "*"
|
||||
},
|
||||
center: {
|
||||
type: "array",
|
||||
value: "number",
|
||||
length: 2
|
||||
},
|
||||
centerAltitude: {
|
||||
type: "number"
|
||||
},
|
||||
zoom: {
|
||||
type: "number"
|
||||
},
|
||||
bearing: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
period: 360,
|
||||
units: "degrees"
|
||||
},
|
||||
pitch: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
units: "degrees"
|
||||
},
|
||||
roll: {
|
||||
type: "number",
|
||||
"default": 0,
|
||||
units: "degrees"
|
||||
},
|
||||
state: {
|
||||
type: "state",
|
||||
"default": {
|
||||
}
|
||||
},
|
||||
light: {
|
||||
type: "light"
|
||||
},
|
||||
sky: {
|
||||
type: "sky"
|
||||
},
|
||||
projection: {
|
||||
type: "projection"
|
||||
},
|
||||
terrain: {
|
||||
type: "terrain"
|
||||
},
|
||||
sources: {
|
||||
required: true,
|
||||
type: "sources"
|
||||
},
|
||||
sprite: {
|
||||
type: "sprite"
|
||||
},
|
||||
glyphs: {
|
||||
type: "string"
|
||||
},
|
||||
"font-faces": {
|
||||
type: "fontFaces"
|
||||
},
|
||||
transition: {
|
||||
type: "transition"
|
||||
},
|
||||
layers: {
|
||||
required: true,
|
||||
type: "array",
|
||||
value: "layer"
|
||||
}
|
||||
};
|
||||
var layer = {
|
||||
id: {
|
||||
type: "string",
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: "enum",
|
||||
values: {
|
||||
fill: {
|
||||
},
|
||||
line: {
|
||||
},
|
||||
symbol: {
|
||||
},
|
||||
circle: {
|
||||
},
|
||||
heatmap: {
|
||||
},
|
||||
"fill-extrusion": {
|
||||
},
|
||||
raster: {
|
||||
},
|
||||
hillshade: {
|
||||
},
|
||||
"color-relief": {
|
||||
},
|
||||
background: {
|
||||
}
|
||||
},
|
||||
required: true
|
||||
},
|
||||
metadata: {
|
||||
type: "*"
|
||||
},
|
||||
source: {
|
||||
type: "string"
|
||||
},
|
||||
"source-layer": {
|
||||
type: "string"
|
||||
},
|
||||
minzoom: {
|
||||
type: "number",
|
||||
minimum: 0,
|
||||
maximum: 24
|
||||
},
|
||||
maxzoom: {
|
||||
type: "number",
|
||||
minimum: 0,
|
||||
maximum: 24
|
||||
},
|
||||
filter: {
|
||||
type: "filter"
|
||||
},
|
||||
layout: {
|
||||
type: "layout"
|
||||
},
|
||||
paint: {
|
||||
type: "paint"
|
||||
}
|
||||
};
|
||||
var latest = {
|
||||
$root: $root,
|
||||
layer: layer};
|
||||
|
||||
// Note: This regex matches even invalid JSON strings, but since we’re
|
||||
// working on the output of `JSON.stringify` we know that only valid strings
|
||||
// are present (unless the user supplied a weird `options.indent` but in
|
||||
// that case we don’t care since the output would be invalid anyway).
|
||||
const stringOrChar = /("(?:[^\\"]|\\.)*")|[:,]/g;
|
||||
|
||||
function stringify(passedObj, options = {}) {
|
||||
const indent = JSON.stringify(
|
||||
[1],
|
||||
undefined,
|
||||
options.indent === undefined ? 2 : options.indent
|
||||
).slice(2, -3);
|
||||
|
||||
const maxLength =
|
||||
indent === ""
|
||||
? Infinity
|
||||
: options.maxLength === undefined
|
||||
? 80
|
||||
: options.maxLength;
|
||||
|
||||
let { replacer } = options;
|
||||
|
||||
return (function _stringify(obj, currentIndent, reserved) {
|
||||
if (obj && typeof obj.toJSON === "function") {
|
||||
obj = obj.toJSON();
|
||||
}
|
||||
|
||||
const string = JSON.stringify(obj, replacer);
|
||||
|
||||
if (string === undefined) {
|
||||
return string;
|
||||
}
|
||||
|
||||
const length = maxLength - currentIndent.length - reserved;
|
||||
|
||||
if (string.length <= length) {
|
||||
const prettified = string.replace(
|
||||
stringOrChar,
|
||||
(match, stringLiteral) => {
|
||||
return stringLiteral || `${match} `;
|
||||
}
|
||||
);
|
||||
if (prettified.length <= length) {
|
||||
return prettified;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacer != null) {
|
||||
obj = JSON.parse(string);
|
||||
replacer = undefined;
|
||||
}
|
||||
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
const nextIndent = currentIndent + indent;
|
||||
const items = [];
|
||||
let index = 0;
|
||||
let start;
|
||||
let end;
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
start = "[";
|
||||
end = "]";
|
||||
const { length } = obj;
|
||||
for (; index < length; index++) {
|
||||
items.push(
|
||||
_stringify(obj[index], nextIndent, index === length - 1 ? 0 : 1) ||
|
||||
"null"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
start = "{";
|
||||
end = "}";
|
||||
const keys = Object.keys(obj);
|
||||
const { length } = keys;
|
||||
for (; index < length; index++) {
|
||||
const key = keys[index];
|
||||
const keyPart = `${JSON.stringify(key)}: `;
|
||||
const value = _stringify(
|
||||
obj[key],
|
||||
nextIndent,
|
||||
keyPart.length + (index === length - 1 ? 0 : 1)
|
||||
);
|
||||
if (value !== undefined) {
|
||||
items.push(keyPart + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
return [start, indent + items.join(`,\n${nextIndent}`), end].join(
|
||||
`\n${currentIndent}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
})(passedObj, "", 0);
|
||||
}
|
||||
|
||||
function sortKeysBy(obj, reference) {
|
||||
const result = {};
|
||||
for (const key in reference) {
|
||||
if (obj[key] !== undefined) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
for (const key in obj) {
|
||||
if (result[key] === undefined) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Format a MapLibre Style. Returns a stringified style with its keys
|
||||
* sorted in the same order as the reference style.
|
||||
*
|
||||
* The optional `space` argument is passed to
|
||||
* [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
|
||||
* to generate formatted output.
|
||||
*
|
||||
* If `space` is unspecified, a default of `2` spaces will be used.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} style a MapLibre Style
|
||||
* @param {number} [space] space argument to pass to `JSON.stringify`
|
||||
* @returns {string} stringified formatted JSON
|
||||
* @example
|
||||
* var fs = require('fs');
|
||||
* var format = require('maplibre-gl-style-spec').format;
|
||||
* var style = fs.readFileSync('./source.json', 'utf8');
|
||||
* fs.writeFileSync('./dest.json', format(style));
|
||||
* fs.writeFileSync('./dest.min.json', format(style, 0));
|
||||
*/
|
||||
function format(style, space = 2) {
|
||||
style = sortKeysBy(style, latest.$root);
|
||||
if (style.layers) {
|
||||
style.layers = style.layers.map((layer) => sortKeysBy(layer, latest.layer));
|
||||
}
|
||||
return stringify(style, { indent: space });
|
||||
}
|
||||
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
|
||||
if (argv.help || argv.h || (!argv._.length && process.stdin.isTTY)) {
|
||||
help();
|
||||
} else {
|
||||
console.log(format(JSON.parse(fs.readFileSync(argv._[0]).toString()), argv.space));
|
||||
}
|
||||
|
||||
function help() {
|
||||
console.log('usage:');
|
||||
console.log(' gl-style-format source.json > destination.json');
|
||||
console.log('');
|
||||
console.log('options:');
|
||||
console.log(' --space <num>');
|
||||
console.log(' Number of spaces in output (default "2")');
|
||||
console.log(' Pass "0" for minified output.');
|
||||
}
|
||||
//# sourceMappingURL=gl-style-format.mjs.map
|
||||
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.mjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-format.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9439
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs
generated
vendored
Normal file
9439
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9433
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs
generated
vendored
Normal file
9433
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
11370
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs
generated
vendored
Normal file
11370
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
11364
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs
generated
vendored
Normal file
11364
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
12206
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs
generated
vendored
Normal file
12206
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
12440
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.d.ts
generated
vendored
Normal file
12440
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12144
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs
generated
vendored
Normal file
12144
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs.map
generated
vendored
Normal file
1
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
8425
node_modules/@maplibre/maplibre-gl-style-spec/dist/latest.json
generated
vendored
Normal file
8425
node_modules/@maplibre/maplibre-gl-style-spec/dist/latest.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
node_modules/@maplibre/maplibre-gl-style-spec/package.json
generated
vendored
Normal file
95
node_modules/@maplibre/maplibre-gl-style-spec/package.json
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"name": "@maplibre/maplibre-gl-style-spec",
|
||||
"description": "a specification for maplibre styles",
|
||||
"version": "24.8.1",
|
||||
"author": "MapLibre",
|
||||
"keywords": [
|
||||
"mapbox",
|
||||
"mapbox-gl",
|
||||
"mapbox-gl-js",
|
||||
"maplibre",
|
||||
"maplibre-gl",
|
||||
"maplibre-gl-js"
|
||||
],
|
||||
"license": "ISC",
|
||||
"homepage": "https://maplibre.org/maplibre-style-spec/",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "rollup --configPlugin @rollup/plugin-typescript -c rollup.config.ts && cp ./src/reference/v8.json ./dist/latest.json",
|
||||
"generate-style-spec": "node --no-warnings --loader ts-node/esm build/generate-style-spec.ts",
|
||||
"generate-typings": "dts-bundle-generator -o ./dist/index.d.ts ./src/index.ts",
|
||||
"generate-docs": "node ${WATCH+--watch} --no-warnings --loader ts-node/esm build/generate-docs.ts",
|
||||
"start-docs": "docker run --rm -v ${PWD}:/docs zensical/zensical serve --open",
|
||||
"docs": "npm run generate-docs && docker run --rm -v ${PWD}:/docs zensical/zensical build",
|
||||
"test": "vitest",
|
||||
"test-unit": "vitest run --config vitest.config.unit.ts",
|
||||
"test-unit-ci": "vitest run --config vitest.config.unit.ts --coverage",
|
||||
"test-integration": "vitest run --config vitest.config.integration.ts",
|
||||
"test-integration-ci": "vitest run --config vitest.config.integration.ts --coverage",
|
||||
"test-build": "vitest run --config vitest.config.build.ts",
|
||||
"test-build-ci": "vitest run --config vitest.config.build.ts --coverage",
|
||||
"test-watch-roots": "vitest --config vitest.config.unit.ts --watch",
|
||||
"compile": "tsc",
|
||||
"lint": "eslint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"prepare": "npm run generate-style-spec",
|
||||
"fmt": "oxfmt"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maplibre/maplibre-style-spec"
|
||||
},
|
||||
"bin": {
|
||||
"gl-style-migrate": "dist/gl-style-migrate.mjs",
|
||||
"gl-style-validate": "dist/gl-style-validate.mjs",
|
||||
"gl-style-format": "dist/gl-style-format.mjs"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
"bin"
|
||||
],
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"minimist": "^1.2.8",
|
||||
"quickselect": "^3.0.0",
|
||||
"rw": "^1.3.3",
|
||||
"tinyqueue": "^3.0.0"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"devDependencies": {
|
||||
"oxfmt": "^0.42.0",
|
||||
"@rollup/plugin-commonjs": "^29.0.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
"@rollup/plugin-strip": "^3.0.4",
|
||||
"@rollup/plugin-terser": "^1.0.0",
|
||||
"@rollup/plugin-typescript": "^12.3.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/node": "^25.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
||||
"@typescript-eslint/parser": "^8.57.1",
|
||||
"@vitest/coverage-v8": "4.1.2",
|
||||
"@vitest/eslint-plugin": "^1.6.13",
|
||||
"@vitest/ui": "4.1.2",
|
||||
"dts-bundle-generator": "^9.5.1",
|
||||
"eslint": "^10.1.0",
|
||||
"eslint-plugin-jsdoc": "^62.8.1",
|
||||
"glob": "^13.0.6",
|
||||
"globals": "^17.4.0",
|
||||
"rollup": "^4.60.0",
|
||||
"rollup-plugin-preserve-shebang": "^1.0.1",
|
||||
"semver": "^7.7.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "4.1.2"
|
||||
}
|
||||
}
|
||||
52
node_modules/@maplibre/maplibre-gl-style-spec/src/deref.test.ts
generated
vendored
Normal file
52
node_modules/@maplibre/maplibre-gl-style-spec/src/deref.test.ts
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import {derefLayers, type LayerWithRef} from './deref';
|
||||
import {describe, test, expect} from 'vitest';
|
||||
|
||||
describe('deref', () => {
|
||||
test('derefs a ref layer which follows its parent', () => {
|
||||
expect(
|
||||
derefLayers([
|
||||
{
|
||||
id: 'parent',
|
||||
type: 'line'
|
||||
} as LayerWithRef,
|
||||
{
|
||||
id: 'child',
|
||||
ref: 'parent'
|
||||
} as LayerWithRef
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
id: 'parent',
|
||||
type: 'line'
|
||||
},
|
||||
{
|
||||
id: 'child',
|
||||
type: 'line'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('derefs a ref layer which precedes its parent', () => {
|
||||
expect(
|
||||
derefLayers([
|
||||
{
|
||||
id: 'child',
|
||||
ref: 'parent'
|
||||
} as LayerWithRef,
|
||||
{
|
||||
id: 'parent',
|
||||
type: 'line'
|
||||
} as LayerWithRef
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
id: 'child',
|
||||
type: 'line'
|
||||
},
|
||||
{
|
||||
id: 'parent',
|
||||
type: 'line'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
49
node_modules/@maplibre/maplibre-gl-style-spec/src/deref.ts
generated
vendored
Normal file
49
node_modules/@maplibre/maplibre-gl-style-spec/src/deref.ts
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import {refProperties} from './util/ref_properties';
|
||||
import {LayerSpecification} from './types.g';
|
||||
|
||||
export type LayerWithRef = LayerSpecification & {ref?: string};
|
||||
|
||||
function deref(layer: LayerWithRef, parent: LayerSpecification): LayerSpecification {
|
||||
const result: Partial<LayerSpecification> = {};
|
||||
|
||||
for (const k in layer) {
|
||||
if (k !== 'ref') {
|
||||
result[k] = layer[k];
|
||||
}
|
||||
}
|
||||
|
||||
refProperties.forEach((k) => {
|
||||
if (k in parent) {
|
||||
result[k] = parent[k];
|
||||
}
|
||||
});
|
||||
|
||||
return result as LayerSpecification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The input is not modified. The output may contain references to portions
|
||||
* of the input.
|
||||
*
|
||||
* @param layers - array of layers, some of which may contain `ref` properties
|
||||
* whose value is the `id` of another property
|
||||
* @returns a new array where such layers have been augmented with the 'type', 'source', etc. properties
|
||||
* from the parent layer, and the `ref` property has been removed.
|
||||
*/
|
||||
export function derefLayers(layers: LayerWithRef[]): LayerSpecification[] {
|
||||
layers = layers.slice();
|
||||
|
||||
const map = Object.create(null);
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
map[layers[i].id] = layers[i];
|
||||
}
|
||||
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
if ('ref' in layers[i]) {
|
||||
layers[i] = deref(layers[i], map[layers[i].ref]);
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
825
node_modules/@maplibre/maplibre-gl-style-spec/src/diff.test.ts
generated
vendored
Normal file
825
node_modules/@maplibre/maplibre-gl-style-spec/src/diff.test.ts
generated
vendored
Normal file
@@ -0,0 +1,825 @@
|
||||
import {diff} from './diff';
|
||||
import {StyleSpecification} from './types.g';
|
||||
import {describe, test, expect} from 'vitest';
|
||||
|
||||
describe('diff', () => {
|
||||
test('layers id equal', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('version not equal', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
version: 7,
|
||||
layers: [{id: 'a'}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
version: 8,
|
||||
layers: [{id: 'a'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setStyle', args: [{version: 8, layers: [{id: 'a'}]}]}]);
|
||||
});
|
||||
|
||||
test('add layer at the end', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a'}, {id: 'b'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'addLayer', args: [{id: 'b'}, undefined]}]);
|
||||
});
|
||||
|
||||
test('add layer at the beginning', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'b'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a'}, {id: 'b'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'addLayer', args: [{id: 'a'}, 'b']}]);
|
||||
});
|
||||
|
||||
test('remove layer', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a'}, {id: 'b', source: 'foo', nested: [1]}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'removeLayer', args: ['b']}]);
|
||||
});
|
||||
|
||||
test('remove and add layer', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a'}, {id: 'b'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'b'}, {id: 'a'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['a']},
|
||||
{command: 'addLayer', args: [{id: 'a'}, undefined]}
|
||||
]);
|
||||
});
|
||||
|
||||
test('set paint property', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', paint: {foo: 1}}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', paint: {foo: 2}}]
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setPaintProperty', args: ['a', 'foo', 2, null]}]);
|
||||
});
|
||||
|
||||
test('set paint property with light', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', 'paint.light': {foo: 1}}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', 'paint.light': {foo: 2}}]
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setPaintProperty', args: ['a', 'foo', 2, 'light']}]);
|
||||
});
|
||||
|
||||
test('set paint property with ramp', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', paint: {foo: {ramp: [1, 2]}}}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', paint: {foo: {ramp: [1]}}}]
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setPaintProperty', args: ['a', 'foo', {ramp: [1]}, null]}]);
|
||||
});
|
||||
|
||||
test('set layout property', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', layout: {foo: 1}}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', layout: {foo: 2}}]
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setLayoutProperty', args: ['a', 'foo', 2, null]}]);
|
||||
});
|
||||
|
||||
test('set filter', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', filter: ['==', 'foo', 'bar']}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', filter: ['==', 'foo', 'baz']}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setFilter', args: ['a', ['==', 'foo', 'baz']]}]);
|
||||
});
|
||||
|
||||
test('remove source', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {foo: 1}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'removeSource', args: ['foo']}]);
|
||||
});
|
||||
|
||||
test('add source', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
sources: {foo: 1}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'addSource', args: ['foo', 1]}]);
|
||||
});
|
||||
|
||||
test('set goejson source data', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []}
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {type: 'Point', coordinates: [10, 20]}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
command: 'setGeoJSONSourceData',
|
||||
args: [
|
||||
'foo',
|
||||
{
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {type: 'Point', coordinates: [10, 20]}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('remove and add source', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []}
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []},
|
||||
cluster: true
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeSource', args: ['foo']},
|
||||
{
|
||||
command: 'addSource',
|
||||
args: [
|
||||
'foo',
|
||||
{
|
||||
type: 'geojson',
|
||||
cluster: true,
|
||||
data: {type: 'FeatureCollection', features: []}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('remove and add source with clusterRadius', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []},
|
||||
cluster: true
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []},
|
||||
cluster: true,
|
||||
clusterRadius: 100
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeSource', args: ['foo']},
|
||||
{
|
||||
command: 'addSource',
|
||||
args: [
|
||||
'foo',
|
||||
{
|
||||
type: 'geojson',
|
||||
cluster: true,
|
||||
clusterRadius: 100,
|
||||
data: {type: 'FeatureCollection', features: []}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('remove and add source without clusterRadius', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []},
|
||||
cluster: true,
|
||||
clusterRadius: 100
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {
|
||||
foo: {
|
||||
type: 'geojson',
|
||||
data: {type: 'FeatureCollection', features: []},
|
||||
cluster: true
|
||||
}
|
||||
}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeSource', args: ['foo']},
|
||||
{
|
||||
command: 'addSource',
|
||||
args: [
|
||||
'foo',
|
||||
{
|
||||
type: 'geojson',
|
||||
cluster: true,
|
||||
data: {type: 'FeatureCollection', features: []}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('global metadata', () => {
|
||||
expect(
|
||||
diff(
|
||||
{} as StyleSpecification,
|
||||
{
|
||||
metadata: {'maplibre:author': 'nobody'}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('layer metadata', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', metadata: {'maplibre:group': 'Group Name'}}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', metadata: {'maplibre:group': 'Another Name'}}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('set state', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
state: {foo: 1}
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
state: {foo: 2}
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setGlobalState', args: [{foo: 2}]}]);
|
||||
});
|
||||
|
||||
test('set center', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
center: [0, 0]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
center: [1, 1]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setCenter', args: [[1, 1]]}]);
|
||||
});
|
||||
|
||||
test('set centerAltitude to undefined', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
centerAltitude: 1
|
||||
} as StyleSpecification,
|
||||
{} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setCenterAltitude', args: [undefined]}]);
|
||||
});
|
||||
|
||||
test('set centerAltitude', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
centerAltitude: 0
|
||||
} as StyleSpecification,
|
||||
{
|
||||
centerAltitude: 1
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setCenterAltitude', args: [1]}]);
|
||||
});
|
||||
|
||||
test('set zoom', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
zoom: 12
|
||||
} as StyleSpecification,
|
||||
{
|
||||
zoom: 15
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setZoom', args: [15]}]);
|
||||
});
|
||||
|
||||
test('set bearing', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
bearing: 0
|
||||
} as StyleSpecification,
|
||||
{
|
||||
bearing: 180
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setBearing', args: [180]}]);
|
||||
});
|
||||
|
||||
test('set pitch', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
pitch: 0
|
||||
} as StyleSpecification,
|
||||
{
|
||||
pitch: 1
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setPitch', args: [1]}]);
|
||||
});
|
||||
|
||||
test('set roll to undefined', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
roll: 1
|
||||
} as StyleSpecification,
|
||||
{} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setRoll', args: [undefined]}]);
|
||||
});
|
||||
|
||||
test('set roll', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
roll: 0
|
||||
} as StyleSpecification,
|
||||
{
|
||||
roll: 1
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setRoll', args: [1]}]);
|
||||
});
|
||||
|
||||
test('no changes in light', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {
|
||||
anchor: 'map',
|
||||
color: 'white',
|
||||
position: [0, 1, 0],
|
||||
intensity: 1
|
||||
}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {
|
||||
anchor: 'map',
|
||||
color: 'white',
|
||||
position: [0, 1, 0],
|
||||
intensity: 1
|
||||
}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('set light anchor', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {anchor: 'map'}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {anchor: 'viewport'}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setLight', args: [{anchor: 'viewport'}]}]);
|
||||
});
|
||||
|
||||
test('set light color', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {color: 'white'}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {color: 'red'}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setLight', args: [{color: 'red'}]}]);
|
||||
});
|
||||
|
||||
test('set light position', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {position: [0, 1, 0]}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {position: [1, 0, 0]}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setLight', args: [{position: [1, 0, 0]}]}]);
|
||||
});
|
||||
|
||||
test('set light intensity', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {intensity: 1}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {intensity: 10}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setLight', args: [{intensity: 10}]}]);
|
||||
});
|
||||
|
||||
test('set light anchor and color', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
light: {
|
||||
anchor: 'map',
|
||||
color: 'orange',
|
||||
position: [2, 80, 30],
|
||||
intensity: 1.0
|
||||
}
|
||||
} as StyleSpecification,
|
||||
{
|
||||
light: {
|
||||
anchor: 'map',
|
||||
color: 'red',
|
||||
position: [1, 40, 30],
|
||||
intensity: 1.0
|
||||
}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
command: 'setLight',
|
||||
args: [
|
||||
{
|
||||
anchor: 'map',
|
||||
color: 'red',
|
||||
position: [1, 40, 30],
|
||||
intensity: 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('add and remove layer on source change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', source: 'source-one'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', source: 'source-two'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['a']},
|
||||
{command: 'addLayer', args: [{id: 'a', source: 'source-two'}, undefined]}
|
||||
]);
|
||||
});
|
||||
|
||||
test('add and remove layer on type change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', type: 'fill'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', type: 'line'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['a']},
|
||||
{command: 'addLayer', args: [{id: 'a', type: 'line'}, undefined]}
|
||||
]);
|
||||
});
|
||||
|
||||
test('add and remove layer on source-layer change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'a', source: 'a', 'source-layer': 'layer-one'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'a', source: 'a', 'source-layer': 'layer-two'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['a']},
|
||||
{
|
||||
command: 'addLayer',
|
||||
args: [{id: 'a', source: 'a', 'source-layer': 'layer-two'}, undefined]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('add and remove layers on different order and type', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
layers: [{id: 'b'}, {id: 'c'}, {id: 'a', type: 'fill'}]
|
||||
} as StyleSpecification,
|
||||
{
|
||||
layers: [{id: 'c'}, {id: 'a', type: 'line'}, {id: 'b'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['b']},
|
||||
{command: 'addLayer', args: [{id: 'b'}, undefined]},
|
||||
{command: 'removeLayer', args: ['a']},
|
||||
{command: 'addLayer', args: [{id: 'a', type: 'line'}, 'b']}
|
||||
]);
|
||||
});
|
||||
|
||||
test('add and remove layer and source on source data change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {foo: {data: 1}, bar: {}},
|
||||
layers: [
|
||||
{id: 'a', source: 'bar'},
|
||||
{id: 'b', source: 'foo'},
|
||||
{id: 'c', source: 'bar'}
|
||||
]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {foo: {data: 2}, bar: {}},
|
||||
layers: [
|
||||
{id: 'a', source: 'bar'},
|
||||
{id: 'b', source: 'foo'},
|
||||
{id: 'c', source: 'bar'}
|
||||
]
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{command: 'removeLayer', args: ['b']},
|
||||
{command: 'removeSource', args: ['foo']},
|
||||
{command: 'addSource', args: ['foo', {data: 2}]},
|
||||
{command: 'addLayer', args: [{id: 'b', source: 'foo'}, 'c']}
|
||||
]);
|
||||
});
|
||||
|
||||
test('set transition', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sources: {foo: {data: 1}, bar: {}},
|
||||
layers: [{id: 'a', source: 'bar'}]
|
||||
} as any as StyleSpecification,
|
||||
{
|
||||
sources: {foo: {data: 1}, bar: {}},
|
||||
layers: [{id: 'a', source: 'bar'}],
|
||||
transition: 'transition'
|
||||
} as any as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setTransition', args: ['transition']}]);
|
||||
});
|
||||
|
||||
test('no sprite change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sprite: 'a'
|
||||
} as StyleSpecification,
|
||||
{
|
||||
sprite: 'a'
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('set sprite', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sprite: 'a'
|
||||
} as StyleSpecification,
|
||||
{
|
||||
sprite: 'b'
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setSprite', args: ['b']}]);
|
||||
});
|
||||
|
||||
test('set sprite for multiple sprites', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
sprite: 'a'
|
||||
} as StyleSpecification,
|
||||
{
|
||||
sprite: [{id: 'default', url: 'b'}]
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setSprite', args: [[{id: 'default', url: 'b'}]]}]);
|
||||
});
|
||||
|
||||
test('no glyphs change', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
glyphs: 'a'
|
||||
} as StyleSpecification,
|
||||
{
|
||||
glyphs: 'a'
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('set glyphs', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
glyphs: 'a'
|
||||
} as StyleSpecification,
|
||||
{
|
||||
glyphs: 'b'
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setGlyphs', args: ['b']}]);
|
||||
});
|
||||
|
||||
test('remove terrain', () => {
|
||||
expect(
|
||||
diff(
|
||||
{
|
||||
terrain: {
|
||||
source: 'maplibre-dem',
|
||||
exaggeration: 1.5
|
||||
}
|
||||
} as StyleSpecification,
|
||||
{} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setTerrain', args: [undefined]}]);
|
||||
});
|
||||
|
||||
test('add terrain', () => {
|
||||
expect(
|
||||
diff(
|
||||
{} as StyleSpecification,
|
||||
{
|
||||
terrain: {
|
||||
source: 'maplibre-dem',
|
||||
exaggeration: 1.5
|
||||
}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setTerrain', args: [{source: 'maplibre-dem', exaggeration: 1.5}]}]);
|
||||
});
|
||||
|
||||
test('set sky', () => {
|
||||
expect(
|
||||
diff(
|
||||
{} as StyleSpecification,
|
||||
{
|
||||
sky: {
|
||||
'fog-color': 'green',
|
||||
'fog-ground-blend': 0.2
|
||||
}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([{command: 'setSky', args: [{'fog-color': 'green', 'fog-ground-blend': 0.2}]}]);
|
||||
});
|
||||
|
||||
test('set projection', () => {
|
||||
expect(
|
||||
diff(
|
||||
{} as StyleSpecification,
|
||||
{
|
||||
projection: {type: ['vertical-perspective', 'mercator', 0.5]}
|
||||
} as StyleSpecification
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
command: 'setProjection',
|
||||
args: [{type: ['vertical-perspective', 'mercator', 0.5]}]
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
485
node_modules/@maplibre/maplibre-gl-style-spec/src/diff.ts
generated
vendored
Normal file
485
node_modules/@maplibre/maplibre-gl-style-spec/src/diff.ts
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
import {
|
||||
GeoJSONSourceSpecification,
|
||||
LayerSpecification,
|
||||
LightSpecification,
|
||||
ProjectionSpecification,
|
||||
SkySpecification,
|
||||
SourceSpecification,
|
||||
SpriteSpecification,
|
||||
StyleSpecification,
|
||||
TerrainSpecification,
|
||||
TransitionSpecification,
|
||||
StateSpecification
|
||||
} from './types.g';
|
||||
import {deepEqual} from './util/deep_equal';
|
||||
|
||||
/**
|
||||
* Operations that can be performed by the diff.
|
||||
* Below are the operations and their arguments, the arguments should be aligned with the style methods in maplibre-gl-js.
|
||||
*/
|
||||
export type DiffOperationsMap = {
|
||||
setStyle: [StyleSpecification];
|
||||
addLayer: [LayerSpecification, string | null];
|
||||
removeLayer: [string];
|
||||
setPaintProperty: [string, string, unknown, string | null];
|
||||
setLayoutProperty: [string, string, unknown, string | null];
|
||||
setFilter: [string, unknown];
|
||||
addSource: [string, SourceSpecification];
|
||||
removeSource: [string];
|
||||
setGeoJSONSourceData: [string, unknown];
|
||||
setLayerZoomRange: [string, number, number];
|
||||
setLayerProperty: [string, string, unknown];
|
||||
setCenter: [number[]];
|
||||
setCenterAltitude: [number];
|
||||
setZoom: [number];
|
||||
setBearing: [number];
|
||||
setPitch: [number];
|
||||
setRoll: [number];
|
||||
setSprite: [SpriteSpecification];
|
||||
setGlyphs: [string];
|
||||
setTransition: [TransitionSpecification];
|
||||
setLight: [LightSpecification];
|
||||
setTerrain: [TerrainSpecification];
|
||||
setSky: [SkySpecification];
|
||||
setProjection: [ProjectionSpecification];
|
||||
setGlobalState: [StateSpecification];
|
||||
};
|
||||
|
||||
export type DiffOperations = keyof DiffOperationsMap;
|
||||
|
||||
export type DiffCommand<T extends DiffOperations> = {
|
||||
command: T;
|
||||
args: DiffOperationsMap[T];
|
||||
};
|
||||
|
||||
/**
|
||||
* The main reason for this method is to allow type check when adding a command to the array.
|
||||
* @param commands - The commands array to add to
|
||||
* @param command - The command to add
|
||||
*/
|
||||
function addCommand<T extends DiffOperations>(
|
||||
commands: DiffCommand<DiffOperations>[],
|
||||
command: DiffCommand<T>
|
||||
) {
|
||||
commands.push(command);
|
||||
}
|
||||
|
||||
function addSource(
|
||||
sourceId: string,
|
||||
after: {[key: string]: SourceSpecification},
|
||||
commands: DiffCommand<DiffOperations>[]
|
||||
) {
|
||||
addCommand(commands, {command: 'addSource', args: [sourceId, after[sourceId]]});
|
||||
}
|
||||
|
||||
function removeSource(
|
||||
sourceId: string,
|
||||
commands: DiffCommand<DiffOperations>[],
|
||||
sourcesRemoved: {[key: string]: boolean}
|
||||
) {
|
||||
addCommand(commands, {command: 'removeSource', args: [sourceId]});
|
||||
sourcesRemoved[sourceId] = true;
|
||||
}
|
||||
|
||||
function updateSource(
|
||||
sourceId: string,
|
||||
after: {[key: string]: SourceSpecification},
|
||||
commands: DiffCommand<DiffOperations>[],
|
||||
sourcesRemoved: {[key: string]: boolean}
|
||||
) {
|
||||
removeSource(sourceId, commands, sourcesRemoved);
|
||||
addSource(sourceId, after, commands);
|
||||
}
|
||||
|
||||
function canUpdateGeoJSON(
|
||||
before: {[key: string]: SourceSpecification},
|
||||
after: {[key: string]: SourceSpecification},
|
||||
sourceId: string
|
||||
) {
|
||||
let prop;
|
||||
for (prop in before[sourceId]) {
|
||||
if (!Object.prototype.hasOwnProperty.call(before[sourceId], prop)) continue;
|
||||
if (prop !== 'data' && !deepEqual(before[sourceId][prop], after[sourceId][prop])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (prop in after[sourceId]) {
|
||||
if (!Object.prototype.hasOwnProperty.call(after[sourceId], prop)) continue;
|
||||
if (prop !== 'data' && !deepEqual(before[sourceId][prop], after[sourceId][prop])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function diffSources(
|
||||
before: {[key: string]: SourceSpecification},
|
||||
after: {[key: string]: SourceSpecification},
|
||||
commands: DiffCommand<DiffOperations>[],
|
||||
sourcesRemoved: {[key: string]: boolean}
|
||||
) {
|
||||
before = before || ({} as {[key: string]: SourceSpecification});
|
||||
after = after || ({} as {[key: string]: SourceSpecification});
|
||||
|
||||
let sourceId: string;
|
||||
|
||||
// look for sources to remove
|
||||
for (sourceId in before) {
|
||||
if (!Object.prototype.hasOwnProperty.call(before, sourceId)) continue;
|
||||
if (!Object.prototype.hasOwnProperty.call(after, sourceId)) {
|
||||
removeSource(sourceId, commands, sourcesRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
// look for sources to add/update
|
||||
for (sourceId in after) {
|
||||
if (!Object.prototype.hasOwnProperty.call(after, sourceId)) continue;
|
||||
if (!Object.prototype.hasOwnProperty.call(before, sourceId)) {
|
||||
addSource(sourceId, after, commands);
|
||||
} else if (!deepEqual(before[sourceId], after[sourceId])) {
|
||||
if (
|
||||
before[sourceId].type === 'geojson' &&
|
||||
after[sourceId].type === 'geojson' &&
|
||||
canUpdateGeoJSON(before, after, sourceId)
|
||||
) {
|
||||
addCommand(commands, {
|
||||
command: 'setGeoJSONSourceData',
|
||||
args: [sourceId, (after[sourceId] as GeoJSONSourceSpecification).data]
|
||||
});
|
||||
} else {
|
||||
// no update command, must remove then add
|
||||
updateSource(sourceId, after, commands, sourcesRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function diffLayerPropertyChanges(
|
||||
before: LayerSpecification['layout'] | LayerSpecification['paint'],
|
||||
after: LayerSpecification['layout'] | LayerSpecification['paint'],
|
||||
commands: DiffCommand<DiffOperations>[],
|
||||
layerId: string,
|
||||
klass: string | null,
|
||||
command: 'setPaintProperty' | 'setLayoutProperty'
|
||||
) {
|
||||
before = before || ({} as LayerSpecification['layout'] | LayerSpecification['paint']);
|
||||
after = after || ({} as LayerSpecification['layout'] | LayerSpecification['paint']);
|
||||
|
||||
for (const prop in before) {
|
||||
if (!Object.prototype.hasOwnProperty.call(before, prop)) continue;
|
||||
if (!deepEqual(before[prop], after[prop])) {
|
||||
commands.push({command, args: [layerId, prop, after[prop], klass]});
|
||||
}
|
||||
}
|
||||
for (const prop in after) {
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(after, prop) ||
|
||||
Object.prototype.hasOwnProperty.call(before, prop)
|
||||
)
|
||||
continue;
|
||||
if (!deepEqual(before[prop], after[prop])) {
|
||||
commands.push({command, args: [layerId, prop, after[prop], klass]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pluckId(layer: LayerSpecification) {
|
||||
return layer.id;
|
||||
}
|
||||
function indexById(group: {[key: string]: LayerSpecification}, layer: LayerSpecification) {
|
||||
group[layer.id] = layer;
|
||||
return group;
|
||||
}
|
||||
|
||||
function diffLayers(
|
||||
before: LayerSpecification[],
|
||||
after: LayerSpecification[],
|
||||
commands: DiffCommand<DiffOperations>[]
|
||||
) {
|
||||
before = before || [];
|
||||
after = after || [];
|
||||
|
||||
// order of layers by id
|
||||
const beforeOrder = before.map(pluckId);
|
||||
const afterOrder = after.map(pluckId);
|
||||
|
||||
// index of layer by id
|
||||
const beforeIndex = before.reduce(indexById, {});
|
||||
const afterIndex = after.reduce(indexById, {});
|
||||
|
||||
// track order of layers as if they have been mutated
|
||||
const tracker = beforeOrder.slice();
|
||||
|
||||
// layers that have been added do not need to be diffed
|
||||
const clean = Object.create(null);
|
||||
|
||||
let layerId: string;
|
||||
let beforeLayer: LayerSpecification & {source?: string; filter?: unknown};
|
||||
let afterLayer: LayerSpecification & {source?: string; filter?: unknown};
|
||||
let insertBeforeLayerId: string;
|
||||
let prop: string;
|
||||
|
||||
// remove layers
|
||||
for (let i = 0, d = 0; i < beforeOrder.length; i++) {
|
||||
layerId = beforeOrder[i];
|
||||
if (!Object.prototype.hasOwnProperty.call(afterIndex, layerId)) {
|
||||
addCommand(commands, {command: 'removeLayer', args: [layerId]});
|
||||
tracker.splice(tracker.indexOf(layerId, d), 1);
|
||||
} else {
|
||||
// limit where in tracker we need to look for a match
|
||||
d++;
|
||||
}
|
||||
}
|
||||
|
||||
// add/reorder layers
|
||||
for (let i = 0, d = 0; i < afterOrder.length; i++) {
|
||||
// work backwards as insert is before an existing layer
|
||||
layerId = afterOrder[afterOrder.length - 1 - i];
|
||||
|
||||
if (tracker[tracker.length - 1 - i] === layerId) continue;
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(beforeIndex, layerId)) {
|
||||
// remove the layer before we insert at the correct position
|
||||
addCommand(commands, {command: 'removeLayer', args: [layerId]});
|
||||
tracker.splice(tracker.lastIndexOf(layerId, tracker.length - d), 1);
|
||||
} else {
|
||||
// limit where in tracker we need to look for a match
|
||||
d++;
|
||||
}
|
||||
|
||||
// add layer at correct position
|
||||
insertBeforeLayerId = tracker[tracker.length - i];
|
||||
addCommand(commands, {
|
||||
command: 'addLayer',
|
||||
args: [afterIndex[layerId], insertBeforeLayerId]
|
||||
});
|
||||
tracker.splice(tracker.length - i, 0, layerId);
|
||||
clean[layerId] = true;
|
||||
}
|
||||
|
||||
// update layers
|
||||
for (let i = 0; i < afterOrder.length; i++) {
|
||||
layerId = afterOrder[i];
|
||||
beforeLayer = beforeIndex[layerId];
|
||||
afterLayer = afterIndex[layerId];
|
||||
|
||||
// no need to update if previously added (new or moved)
|
||||
if (clean[layerId] || deepEqual(beforeLayer, afterLayer)) continue;
|
||||
|
||||
// If source, source-layer, or type have changes, then remove the layer
|
||||
// and add it back 'from scratch'.
|
||||
if (
|
||||
!deepEqual(beforeLayer.source, afterLayer.source) ||
|
||||
!deepEqual(beforeLayer['source-layer'], afterLayer['source-layer']) ||
|
||||
!deepEqual(beforeLayer.type, afterLayer.type)
|
||||
) {
|
||||
addCommand(commands, {command: 'removeLayer', args: [layerId]});
|
||||
// we add the layer back at the same position it was already in, so
|
||||
// there's no need to update the `tracker`
|
||||
insertBeforeLayerId = tracker[tracker.lastIndexOf(layerId) + 1];
|
||||
addCommand(commands, {command: 'addLayer', args: [afterLayer, insertBeforeLayerId]});
|
||||
continue;
|
||||
}
|
||||
|
||||
// layout, paint, filter, minzoom, maxzoom
|
||||
diffLayerPropertyChanges(
|
||||
beforeLayer.layout,
|
||||
afterLayer.layout,
|
||||
commands,
|
||||
layerId,
|
||||
null,
|
||||
'setLayoutProperty'
|
||||
);
|
||||
diffLayerPropertyChanges(
|
||||
beforeLayer.paint,
|
||||
afterLayer.paint,
|
||||
commands,
|
||||
layerId,
|
||||
null,
|
||||
'setPaintProperty'
|
||||
);
|
||||
if (!deepEqual(beforeLayer.filter, afterLayer.filter)) {
|
||||
addCommand(commands, {command: 'setFilter', args: [layerId, afterLayer.filter]});
|
||||
}
|
||||
if (
|
||||
!deepEqual(beforeLayer.minzoom, afterLayer.minzoom) ||
|
||||
!deepEqual(beforeLayer.maxzoom, afterLayer.maxzoom)
|
||||
) {
|
||||
addCommand(commands, {
|
||||
command: 'setLayerZoomRange',
|
||||
args: [layerId, afterLayer.minzoom, afterLayer.maxzoom]
|
||||
});
|
||||
}
|
||||
|
||||
// handle all other layer props, including paint.*
|
||||
for (prop in beforeLayer) {
|
||||
if (!Object.prototype.hasOwnProperty.call(beforeLayer, prop)) continue;
|
||||
if (
|
||||
prop === 'layout' ||
|
||||
prop === 'paint' ||
|
||||
prop === 'filter' ||
|
||||
prop === 'metadata' ||
|
||||
prop === 'minzoom' ||
|
||||
prop === 'maxzoom'
|
||||
)
|
||||
continue;
|
||||
if (prop.indexOf('paint.') === 0) {
|
||||
diffLayerPropertyChanges(
|
||||
beforeLayer[prop],
|
||||
afterLayer[prop],
|
||||
commands,
|
||||
layerId,
|
||||
prop.slice(6),
|
||||
'setPaintProperty'
|
||||
);
|
||||
} else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) {
|
||||
addCommand(commands, {
|
||||
command: 'setLayerProperty',
|
||||
args: [layerId, prop, afterLayer[prop]]
|
||||
});
|
||||
}
|
||||
}
|
||||
for (prop in afterLayer) {
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(afterLayer, prop) ||
|
||||
Object.prototype.hasOwnProperty.call(beforeLayer, prop)
|
||||
)
|
||||
continue;
|
||||
if (
|
||||
prop === 'layout' ||
|
||||
prop === 'paint' ||
|
||||
prop === 'filter' ||
|
||||
prop === 'metadata' ||
|
||||
prop === 'minzoom' ||
|
||||
prop === 'maxzoom'
|
||||
)
|
||||
continue;
|
||||
if (prop.indexOf('paint.') === 0) {
|
||||
diffLayerPropertyChanges(
|
||||
beforeLayer[prop],
|
||||
afterLayer[prop],
|
||||
commands,
|
||||
layerId,
|
||||
prop.slice(6),
|
||||
'setPaintProperty'
|
||||
);
|
||||
} else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) {
|
||||
addCommand(commands, {
|
||||
command: 'setLayerProperty',
|
||||
args: [layerId, prop, afterLayer[prop]]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff two stylesheet
|
||||
*
|
||||
* Creates semanticly aware diffs that can easily be applied at runtime.
|
||||
* Operations produced by the diff closely resemble the maplibre-gl-js API. Any
|
||||
* error creating the diff will fall back to the 'setStyle' operation.
|
||||
*
|
||||
* Example diff:
|
||||
* [
|
||||
* { command: 'setConstant', args: ['@water', '#0000FF'] },
|
||||
* { command: 'setPaintProperty', args: ['background', 'background-color', 'black'] }
|
||||
* ]
|
||||
*
|
||||
* @private
|
||||
* @param {*} [before] stylesheet to compare from
|
||||
* @param {*} after stylesheet to compare to
|
||||
* @returns Array list of changes
|
||||
*/
|
||||
export function diff(
|
||||
before: StyleSpecification,
|
||||
after: StyleSpecification
|
||||
): DiffCommand<DiffOperations>[] {
|
||||
if (!before) return [{command: 'setStyle', args: [after]}];
|
||||
|
||||
let commands: DiffCommand<DiffOperations>[] = [];
|
||||
|
||||
try {
|
||||
// Handle changes to top-level properties
|
||||
if (!deepEqual(before.version, after.version)) {
|
||||
return [{command: 'setStyle', args: [after]}];
|
||||
}
|
||||
if (!deepEqual(before.center, after.center)) {
|
||||
commands.push({command: 'setCenter', args: [after.center]});
|
||||
}
|
||||
if (!deepEqual(before.state, after.state)) {
|
||||
commands.push({command: 'setGlobalState', args: [after.state]});
|
||||
}
|
||||
if (!deepEqual(before.centerAltitude, after.centerAltitude)) {
|
||||
commands.push({command: 'setCenterAltitude', args: [after.centerAltitude]});
|
||||
}
|
||||
if (!deepEqual(before.zoom, after.zoom)) {
|
||||
commands.push({command: 'setZoom', args: [after.zoom]});
|
||||
}
|
||||
if (!deepEqual(before.bearing, after.bearing)) {
|
||||
commands.push({command: 'setBearing', args: [after.bearing]});
|
||||
}
|
||||
if (!deepEqual(before.pitch, after.pitch)) {
|
||||
commands.push({command: 'setPitch', args: [after.pitch]});
|
||||
}
|
||||
if (!deepEqual(before.roll, after.roll)) {
|
||||
commands.push({command: 'setRoll', args: [after.roll]});
|
||||
}
|
||||
if (!deepEqual(before.sprite, after.sprite)) {
|
||||
commands.push({command: 'setSprite', args: [after.sprite]});
|
||||
}
|
||||
if (!deepEqual(before.glyphs, after.glyphs)) {
|
||||
commands.push({command: 'setGlyphs', args: [after.glyphs]});
|
||||
}
|
||||
if (!deepEqual(before.transition, after.transition)) {
|
||||
commands.push({command: 'setTransition', args: [after.transition]});
|
||||
}
|
||||
if (!deepEqual(before.light, after.light)) {
|
||||
commands.push({command: 'setLight', args: [after.light]});
|
||||
}
|
||||
if (!deepEqual(before.terrain, after.terrain)) {
|
||||
commands.push({command: 'setTerrain', args: [after.terrain]});
|
||||
}
|
||||
if (!deepEqual(before.sky, after.sky)) {
|
||||
commands.push({command: 'setSky', args: [after.sky]});
|
||||
}
|
||||
if (!deepEqual(before.projection, after.projection)) {
|
||||
commands.push({command: 'setProjection', args: [after.projection]});
|
||||
}
|
||||
|
||||
// Handle changes to `sources`
|
||||
// If a source is to be removed, we also--before the removeSource
|
||||
// command--need to remove all the style layers that depend on it.
|
||||
const sourcesRemoved = {};
|
||||
|
||||
// First collect the {add,remove}Source commands
|
||||
const removeOrAddSourceCommands = [];
|
||||
diffSources(before.sources, after.sources, removeOrAddSourceCommands, sourcesRemoved);
|
||||
|
||||
// Push a removeLayer command for each style layer that depends on a
|
||||
// source that's being removed.
|
||||
// Also, exclude any such layers them from the input to `diffLayers`
|
||||
// below, so that diffLayers produces the appropriate `addLayers`
|
||||
// command
|
||||
const beforeLayers = [];
|
||||
if (before.layers) {
|
||||
before.layers.forEach((layer) => {
|
||||
if ('source' in layer && sourcesRemoved[layer.source]) {
|
||||
commands.push({command: 'removeLayer', args: [layer.id]});
|
||||
} else {
|
||||
beforeLayers.push(layer);
|
||||
}
|
||||
});
|
||||
}
|
||||
commands = commands.concat(removeOrAddSourceCommands);
|
||||
|
||||
// Handle changes to `layers`
|
||||
diffLayers(beforeLayers, after.layers, commands);
|
||||
} catch (e) {
|
||||
// fall back to setStyle
|
||||
console.warn('Unable to compute style diff:', e);
|
||||
commands = [{command: 'setStyle', args: [after]}];
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
15
node_modules/@maplibre/maplibre-gl-style-spec/src/empty.test.ts
generated
vendored
Normal file
15
node_modules/@maplibre/maplibre-gl-style-spec/src/empty.test.ts
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import {emptyStyle} from './empty';
|
||||
import {validateStyleMin} from './validate_style.min';
|
||||
import {describe, test, expect} from 'vitest';
|
||||
|
||||
describe('empty', () => {
|
||||
test('it generates something', () => {
|
||||
const style = emptyStyle();
|
||||
expect(style).toBeTruthy();
|
||||
});
|
||||
|
||||
test('generated empty style is a valid style', () => {
|
||||
const errors = validateStyleMin(emptyStyle());
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
30
node_modules/@maplibre/maplibre-gl-style-spec/src/empty.ts
generated
vendored
Normal file
30
node_modules/@maplibre/maplibre-gl-style-spec/src/empty.ts
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import {latest} from './reference/latest';
|
||||
import {StyleSpecification} from './types.g';
|
||||
|
||||
export function emptyStyle(): StyleSpecification {
|
||||
const style = {};
|
||||
|
||||
const version = latest['$version'];
|
||||
for (const styleKey in latest['$root']) {
|
||||
const specification = latest['$root'][styleKey];
|
||||
|
||||
if (specification.required) {
|
||||
let value = null;
|
||||
if (styleKey === 'version') {
|
||||
value = version;
|
||||
} else {
|
||||
if (specification.type === 'array') {
|
||||
value = [];
|
||||
} else {
|
||||
value = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
style[styleKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return style as StyleSpecification;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user