@loaders.gl/mvt 4.3.0-alpha.1 → 4.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dist.dev.js +1289 -807
- package/dist/dist.min.js +1 -1
- package/dist/index.cjs +923 -773
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/lib/parse-mvt.d.ts +1 -1
- package/dist/lib/parse-mvt.js +2 -30
- package/dist/lib/parse-tilejson.d.ts +4 -4
- package/dist/lib/parse-tilejson.d.ts.map +1 -1
- package/dist/lib/parse-tilejson.js +1 -1
- package/dist/lib/utils/geometry-utils.d.ts +38 -1
- package/dist/lib/utils/geometry-utils.d.ts.map +1 -1
- package/dist/lib/utils/geometry-utils.js +65 -6
- package/dist/lib/vector-tile/vector-tile-feature.d.ts +28 -9
- package/dist/lib/vector-tile/vector-tile-feature.d.ts.map +1 -1
- package/dist/lib/vector-tile/vector-tile-feature.js +47 -50
- package/dist/lib/{geojsonvt/clip.d.ts → vector-tiler/features/clip-features.d.ts} +4 -4
- package/dist/lib/vector-tiler/features/clip-features.d.ts.map +1 -0
- package/dist/lib/{geojsonvt/clip.js → vector-tiler/features/clip-features.js} +4 -4
- package/dist/lib/vector-tiler/features/convert-feature.d.ts +18 -0
- package/dist/lib/vector-tiler/features/convert-feature.d.ts.map +1 -0
- package/dist/lib/vector-tiler/features/convert-feature.js +140 -0
- package/dist/lib/vector-tiler/features/proto-feature.d.ts +30 -0
- package/dist/lib/vector-tiler/features/proto-feature.d.ts.map +1 -0
- package/dist/lib/vector-tiler/features/proto-feature.js +52 -0
- package/dist/lib/{geojsonvt/simplify.d.ts → vector-tiler/features/simplify-path.d.ts} +2 -2
- package/dist/lib/vector-tiler/features/simplify-path.d.ts.map +1 -0
- package/dist/lib/{geojsonvt/simplify.js → vector-tiler/features/simplify-path.js} +3 -3
- package/dist/lib/{geojsonvt/wrap.d.ts → vector-tiler/features/wrap-features.d.ts} +5 -5
- package/dist/lib/vector-tiler/features/wrap-features.d.ts.map +1 -0
- package/dist/lib/{geojsonvt/wrap.js → vector-tiler/features/wrap-features.js} +33 -26
- package/dist/lib/vector-tiler/proto-tile.d.ts +40 -0
- package/dist/lib/vector-tiler/proto-tile.d.ts.map +1 -0
- package/dist/lib/vector-tiler/proto-tile.js +138 -0
- package/dist/lib/vector-tiler/tile-to-geojson.d.ts +12 -0
- package/dist/lib/vector-tiler/tile-to-geojson.d.ts.map +1 -0
- package/dist/lib/vector-tiler/tile-to-geojson.js +81 -0
- package/dist/lib/vector-tiler/transform-tile.d.ts +7 -0
- package/dist/lib/vector-tiler/transform-tile.d.ts.map +1 -0
- package/dist/lib/vector-tiler/transform-tile.js +41 -0
- package/dist/mvt-loader.d.ts +1 -1
- package/dist/mvt-loader.js +1 -1
- package/dist/mvt-source.d.ts +35 -18
- package/dist/mvt-source.d.ts.map +1 -1
- package/dist/mvt-source.js +30 -10
- package/dist/mvt-worker.js +101 -56
- package/dist/table-tile-source.d.ts +148 -0
- package/dist/table-tile-source.d.ts.map +1 -0
- package/dist/table-tile-source.js +420 -0
- package/dist/tilejson-loader.js +1 -1
- package/package.json +7 -6
- package/src/index.ts +14 -7
- package/src/lib/parse-mvt.ts +4 -33
- package/src/lib/parse-tilejson.ts +6 -6
- package/src/lib/utils/geometry-utils.ts +66 -1
- package/src/lib/vector-tile/vector-tile-feature.ts +65 -56
- package/src/lib/{geojsonvt/clip.ts → vector-tiler/features/clip-features.ts} +8 -8
- package/src/lib/vector-tiler/features/convert-feature.ts +191 -0
- package/src/lib/vector-tiler/features/proto-feature.ts +104 -0
- package/src/lib/{geojsonvt/simplify.ts → vector-tiler/features/simplify-path.ts} +8 -3
- package/src/lib/{geojsonvt/wrap.ts → vector-tiler/features/wrap-features.ts} +44 -29
- package/src/lib/vector-tiler/proto-tile.ts +217 -0
- package/src/lib/vector-tiler/tile-to-geojson.ts +105 -0
- package/src/lib/vector-tiler/transform-tile.ts +57 -0
- package/src/mvt-source.ts +47 -24
- package/src/table-tile-source.ts +553 -0
- package/src/tilejson-loader.ts +2 -2
- package/dist/geojson-tile-source.d.ts +0 -79
- package/dist/geojson-tile-source.d.ts.map +0 -1
- package/dist/geojson-tile-source.js +0 -319
- package/dist/lib/geojsonvt/clip.d.ts.map +0 -1
- package/dist/lib/geojsonvt/convert.d.ts +0 -10
- package/dist/lib/geojsonvt/convert.d.ts.map +0 -1
- package/dist/lib/geojsonvt/convert.js +0 -132
- package/dist/lib/geojsonvt/feature.d.ts +0 -3
- package/dist/lib/geojsonvt/feature.d.ts.map +0 -1
- package/dist/lib/geojsonvt/feature.js +0 -44
- package/dist/lib/geojsonvt/simplify.d.ts.map +0 -1
- package/dist/lib/geojsonvt/tile.d.ts +0 -38
- package/dist/lib/geojsonvt/tile.d.ts.map +0 -1
- package/dist/lib/geojsonvt/tile.js +0 -123
- package/dist/lib/geojsonvt/transform.d.ts +0 -7
- package/dist/lib/geojsonvt/transform.d.ts.map +0 -1
- package/dist/lib/geojsonvt/transform.js +0 -41
- package/dist/lib/geojsonvt/wrap.d.ts.map +0 -1
- package/src/geojson-tile-source.ts +0 -422
- package/src/lib/geojsonvt/convert.ts +0 -160
- package/src/lib/geojsonvt/feature.ts +0 -47
- package/src/lib/geojsonvt/tile.ts +0 -187
- package/src/lib/geojsonvt/transform.ts +0 -57
- /package/src/lib/{geojsonvt → vector-tiler}/LICENSE +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT AND ISC
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
// Based on https://github.com/mapbox/geojson-vt under compatible ISC license
|
|
5
|
+
import { log } from '@loaders.gl/loader-utils';
|
|
6
|
+
import { deduceTableSchema } from '@loaders.gl/schema';
|
|
7
|
+
import { Stats, Stat } from '@probe.gl/stats';
|
|
8
|
+
import { createProtoTile } from "./lib/vector-tiler/proto-tile.js";
|
|
9
|
+
import { transformTile } from "./lib/vector-tiler/transform-tile.js"; // coordinate transformation
|
|
10
|
+
import { convertTileToGeoJSON } from "./lib/vector-tiler/tile-to-geojson.js"; // tile clipping and wrapping
|
|
11
|
+
import { convertFeaturesToProtoFeature } from "./lib/vector-tiler/features/convert-feature.js";
|
|
12
|
+
import { clipFeatures } from "./lib/vector-tiler/features/clip-features.js"; // stripe clipping algorithm
|
|
13
|
+
import { wrapFeatures } from "./lib/vector-tiler/features/wrap-features.js"; // date line processing
|
|
14
|
+
/** Options to configure tiling */
|
|
15
|
+
export const TableTileSource = {
|
|
16
|
+
name: 'TableTiler',
|
|
17
|
+
id: 'table-tiler',
|
|
18
|
+
version: '0.0.0',
|
|
19
|
+
extensions: ['mvt'],
|
|
20
|
+
mimeTypes: ['application/octet-stream'],
|
|
21
|
+
options: {
|
|
22
|
+
table: {
|
|
23
|
+
coordinates: 'local',
|
|
24
|
+
promoteId: undefined,
|
|
25
|
+
maxZoom: 14,
|
|
26
|
+
indexMaxZoom: 5,
|
|
27
|
+
maxPointsPerTile: 10000,
|
|
28
|
+
tolerance: 3,
|
|
29
|
+
extent: 4096,
|
|
30
|
+
buffer: 64,
|
|
31
|
+
generateId: undefined
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
type: 'table',
|
|
35
|
+
testURL: (url) => url.endsWith('.geojson'),
|
|
36
|
+
createDataSource(url, options) {
|
|
37
|
+
const needsLoading = typeof url === 'string' || url instanceof Blob;
|
|
38
|
+
const loader = options?.table?.loaders?.[0];
|
|
39
|
+
const tablePromise = needsLoading ? loadTable(url, loader) : url;
|
|
40
|
+
return new DynamicVectorTileSource(tablePromise, options);
|
|
41
|
+
}
|
|
42
|
+
// @ts-expect-error
|
|
43
|
+
};
|
|
44
|
+
async function loadTable(url, loader) {
|
|
45
|
+
if (typeof url === 'string') {
|
|
46
|
+
const response = await fetch(url);
|
|
47
|
+
const data = await response.arrayBuffer();
|
|
48
|
+
return (await loader.parse(data));
|
|
49
|
+
}
|
|
50
|
+
const data = await url.arrayBuffer();
|
|
51
|
+
return (await loader.parse(data)); // options.loaders, options.loadOptions)
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Dynamically vector tiles a table (the table needs a geometry column)
|
|
55
|
+
* - Tiles are generated when requested.
|
|
56
|
+
* - Each tile contains a tables of clipped features.
|
|
57
|
+
*
|
|
58
|
+
* @note - Currently only accepts `GeoJSONTable` tables
|
|
59
|
+
* @note - Currently only outputs `GeoJSONTable`
|
|
60
|
+
* @note - (can be initialized with a promise that resolves to GeoJSONTable).
|
|
61
|
+
*
|
|
62
|
+
* @todo - metadata should scan all rows to determine schema
|
|
63
|
+
* @todo - metadata scan all rows to determine tilestats (field values[] etc).
|
|
64
|
+
* @todo - handle binary input tables
|
|
65
|
+
* @todo - generate binary output tables
|
|
66
|
+
* @todo - how does TileSourceLayer specify coordinates / decided which layer to render with
|
|
67
|
+
*/
|
|
68
|
+
export class DynamicVectorTileSource {
|
|
69
|
+
/** Global stats for all DynamicVectorTileSources */
|
|
70
|
+
static stats = new Stats({
|
|
71
|
+
id: 'table-tile-source-all',
|
|
72
|
+
stats: [new Stat('count', 'tiles'), new Stat('count', 'features')]
|
|
73
|
+
});
|
|
74
|
+
/** Stats for this DynamicVectorTileSource */
|
|
75
|
+
stats = new Stats({
|
|
76
|
+
id: 'table-tile-source',
|
|
77
|
+
stats: [new Stat('tiles', 'count'), new Stat('features', 'count')]
|
|
78
|
+
});
|
|
79
|
+
/** MIME type of the tiles emitted by this tile source */
|
|
80
|
+
mimeType = 'application/vnd.mapbox-vector-tile';
|
|
81
|
+
localCoordinates = true;
|
|
82
|
+
/** The props that this tile source was created with */
|
|
83
|
+
// @ts-expect-error
|
|
84
|
+
props;
|
|
85
|
+
/* Schema of the data */
|
|
86
|
+
schema = null;
|
|
87
|
+
/** Map of generated tiles, indexed by stringified tile coordinates */
|
|
88
|
+
tiles = {};
|
|
89
|
+
/** Array of tile coordinates */
|
|
90
|
+
tileCoords = [];
|
|
91
|
+
/** Input data has loaded, initial top-level tiling is done, sync methods can now be called */
|
|
92
|
+
ready;
|
|
93
|
+
/** Metadata for the tile source (generated TileJSON/tilestats */
|
|
94
|
+
metadata;
|
|
95
|
+
constructor(table, props) {
|
|
96
|
+
// @ts-expect-error
|
|
97
|
+
this.props = { ...TableTileSource.options.table, ...props?.table };
|
|
98
|
+
this.getTileData = this.getTileData.bind(this);
|
|
99
|
+
this.ready = this.initializeTilesAsync(table);
|
|
100
|
+
this.metadata = this.getMetadata();
|
|
101
|
+
}
|
|
102
|
+
async initializeTilesAsync(tablePromise) {
|
|
103
|
+
const table = await tablePromise;
|
|
104
|
+
this.schema = deduceTableSchema(table);
|
|
105
|
+
this.createRootTiles(table);
|
|
106
|
+
}
|
|
107
|
+
async getMetadata() {
|
|
108
|
+
await this.ready;
|
|
109
|
+
return { schema: this.schema, minZoom: 0, maxZoom: this.props.maxZoom };
|
|
110
|
+
}
|
|
111
|
+
async getSchema() {
|
|
112
|
+
await this.ready;
|
|
113
|
+
return this.schema;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get a tile at the specified index
|
|
117
|
+
* @param tileIndex z, x, y of tile
|
|
118
|
+
* @returns
|
|
119
|
+
*/
|
|
120
|
+
async getVectorTile(tileIndex) {
|
|
121
|
+
await this.ready;
|
|
122
|
+
const table = this.getTileSync(tileIndex);
|
|
123
|
+
log.info(2, 'getVectorTile', tileIndex, table)();
|
|
124
|
+
return table;
|
|
125
|
+
}
|
|
126
|
+
async getTile(tileIndex) {
|
|
127
|
+
await this.ready;
|
|
128
|
+
return this.getTileSync(tileIndex);
|
|
129
|
+
}
|
|
130
|
+
async getTileData(tileParams) {
|
|
131
|
+
const { x, y, z } = tileParams.index;
|
|
132
|
+
const tile = await this.getVectorTile({ x, y, z });
|
|
133
|
+
return tile?.features || [];
|
|
134
|
+
}
|
|
135
|
+
// Implementation
|
|
136
|
+
/**
|
|
137
|
+
* Synchronously request a tile
|
|
138
|
+
* @note Application must await `source.ready` before calling sync methods.
|
|
139
|
+
*/
|
|
140
|
+
getTileSync(tileIndex) {
|
|
141
|
+
const protoTile = this.getProtoTile(tileIndex);
|
|
142
|
+
if (!protoTile) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return convertTileToGeoJSON(protoTile, {
|
|
146
|
+
coordinates: this.props.coordinates,
|
|
147
|
+
tileIndex,
|
|
148
|
+
extent: this.props.extent
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create the initial tiles
|
|
153
|
+
* @note the tiles stores all the features together with additional data
|
|
154
|
+
*/
|
|
155
|
+
createRootTiles(table) {
|
|
156
|
+
if (this.props.maxZoom < 0 || this.props.maxZoom > 24) {
|
|
157
|
+
throw new Error('maxZoom should be in the 0-24 range');
|
|
158
|
+
}
|
|
159
|
+
if (this.props.promoteId && this.props.generateId) {
|
|
160
|
+
throw new Error('promoteId and generateId cannot be used together.');
|
|
161
|
+
}
|
|
162
|
+
log.log(1, 'DynamicVectorTileSource creating root tiles', this.props)();
|
|
163
|
+
// projects and adds simplification info
|
|
164
|
+
log.time(1, 'preprocess table')();
|
|
165
|
+
let features = convertFeaturesToProtoFeature(table, this.props);
|
|
166
|
+
log.timeEnd(1, 'preprocess table')();
|
|
167
|
+
// wraps features (ie extreme west and extreme east)
|
|
168
|
+
log.time(1, 'generate tiles')();
|
|
169
|
+
features = wrapFeatures(features, this.props);
|
|
170
|
+
// start slicing from the top tile down
|
|
171
|
+
if (features.length === 0) {
|
|
172
|
+
log.log(1, 'DynamicVectorTileSource: no features generated')();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.splitTile(features, 0, 0, 0);
|
|
176
|
+
const rootTile = this.tiles[0];
|
|
177
|
+
log.log(1, `root tile features: ${rootTile.numFeatures}, points: ${rootTile.numPoints}`)();
|
|
178
|
+
log.timeEnd(1, 'generate tiles')();
|
|
179
|
+
log.log(1, `DynamicVectorTileSource: tiles generated: ${this.stats.get('total').count}`, this.stats)();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Return geojsonvt-style "half formed" vector tile
|
|
183
|
+
* @note Application must await `source.ready` before calling sync methods.
|
|
184
|
+
*/
|
|
185
|
+
// eslint-disable-next-line complexity, max-statements
|
|
186
|
+
getProtoTile(tileIndex) {
|
|
187
|
+
const { z, y } = tileIndex;
|
|
188
|
+
let { x } = tileIndex;
|
|
189
|
+
// z = +z;
|
|
190
|
+
// x = +x;
|
|
191
|
+
// y = +y;
|
|
192
|
+
const { extent } = this.props;
|
|
193
|
+
if (z < 0 || z > 24) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const z2 = 1 << z;
|
|
197
|
+
x = (x + z2) & (z2 - 1); // wrapFeatures tile x coordinate
|
|
198
|
+
const id = toID(z, x, y);
|
|
199
|
+
if (this.tiles[id]) {
|
|
200
|
+
return transformTile(this.tiles[id], extent);
|
|
201
|
+
}
|
|
202
|
+
log.log(log, 'drilling down to z%d-%d-%d', z, x, y)();
|
|
203
|
+
let z0 = z;
|
|
204
|
+
let x0 = x;
|
|
205
|
+
let y0 = y;
|
|
206
|
+
let parent;
|
|
207
|
+
while (!parent && z0 > 0) {
|
|
208
|
+
z0--;
|
|
209
|
+
x0 = x0 >> 1;
|
|
210
|
+
y0 = y0 >> 1;
|
|
211
|
+
parent = this.tiles[toID(z0, x0, y0)];
|
|
212
|
+
}
|
|
213
|
+
if (!parent || !parent.sourceFeatures) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
// if we found a parent tile containing the original geometry, we can drill down from it
|
|
217
|
+
log.log(1, 'found parent tile z%d-%d-%d', z0, x0, y0)();
|
|
218
|
+
log.time(1, 'drilling down')();
|
|
219
|
+
this.splitTile(parent.sourceFeatures, z0, x0, y0, z, x, y);
|
|
220
|
+
log.timeEnd(1, 'drilling down')();
|
|
221
|
+
return this.tiles[id] ? transformTile(this.tiles[id], extent) : null;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* splits features from a parent tile to sub-tiles.
|
|
225
|
+
* @param z, x, and y are the coordinates of the parent tile
|
|
226
|
+
* @param cz, cx, and cy are the coordinates of the target tile
|
|
227
|
+
*
|
|
228
|
+
* If no target tile is specified, splitting stops when we reach the maximum
|
|
229
|
+
* zoom or the number of points is low as specified in the props.
|
|
230
|
+
*/
|
|
231
|
+
// eslint-disable-next-line max-params, max-statements, complexity
|
|
232
|
+
splitTile(features, z, x, y, cz, cx, cy) {
|
|
233
|
+
const stack = [features, z, x, y];
|
|
234
|
+
// avoid recursion by using a processing queue
|
|
235
|
+
while (stack.length) {
|
|
236
|
+
y = stack.pop();
|
|
237
|
+
x = stack.pop();
|
|
238
|
+
z = stack.pop();
|
|
239
|
+
features = stack.pop();
|
|
240
|
+
const z2 = 1 << z;
|
|
241
|
+
const id = toID(z, x, y);
|
|
242
|
+
let tile = this.tiles[id];
|
|
243
|
+
if (!tile) {
|
|
244
|
+
log.time(2, 'tile creation')();
|
|
245
|
+
tile = this.tiles[id] = createProtoTile(features, z, x, y, this.props);
|
|
246
|
+
this.tileCoords.push({ z, x, y });
|
|
247
|
+
const key = `z${z}`;
|
|
248
|
+
let stat = this.stats.get(key, 'count');
|
|
249
|
+
stat.incrementCount();
|
|
250
|
+
stat = this.stats.get('total');
|
|
251
|
+
stat.incrementCount();
|
|
252
|
+
stat = DynamicVectorTileSource.stats.get(key, 'count');
|
|
253
|
+
stat.incrementCount();
|
|
254
|
+
stat = DynamicVectorTileSource.stats.get('total');
|
|
255
|
+
stat.incrementCount();
|
|
256
|
+
log.log(2, 'tile z%d-%d-%d (features: %d, points: %d, simplified: %d)', z, x, y, tile.numFeatures, tile.numPoints, tile.numSimplified)();
|
|
257
|
+
log.timeEnd(2, 'tile creation')();
|
|
258
|
+
}
|
|
259
|
+
// save reference to original geometry in tile so that we can drill down later if we stop now
|
|
260
|
+
tile.sourceFeatures = features;
|
|
261
|
+
/* eslint-disable no-continue */
|
|
262
|
+
// if it's the first-pass tiling
|
|
263
|
+
if (cz === undefined) {
|
|
264
|
+
// stop tiling if we reached max zoom, or if the tile is too simple
|
|
265
|
+
if (z === this.props.indexMaxZoom || tile.numPoints <= this.props.maxPointsPerTile) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// if a drilldown to a specific tile
|
|
269
|
+
}
|
|
270
|
+
else if (z === this.props.maxZoom || z === cz) {
|
|
271
|
+
// stop tiling if we reached base zoom or our target tile zoom
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
else if (cz !== undefined) {
|
|
275
|
+
// stop tiling if it's not an ancestor of the target tile
|
|
276
|
+
const zoomSteps = cz - z;
|
|
277
|
+
// @ts-expect-error TODO fix the types of cx cy
|
|
278
|
+
if (x !== cx >> zoomSteps || y !== cy >> zoomSteps) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// if we slice further down, no need to keep source geometry
|
|
283
|
+
tile.sourceFeatures = null;
|
|
284
|
+
if (features.length === 0)
|
|
285
|
+
continue;
|
|
286
|
+
log.time(2, 'clipping tile')();
|
|
287
|
+
// values we'll use for clipping
|
|
288
|
+
const k1 = (0.5 * this.props.buffer) / this.props.extent;
|
|
289
|
+
const k2 = 0.5 - k1;
|
|
290
|
+
const k3 = 0.5 + k1;
|
|
291
|
+
const k4 = 1 + k1;
|
|
292
|
+
let tl = null;
|
|
293
|
+
let bl = null;
|
|
294
|
+
let tr = null;
|
|
295
|
+
let br = null;
|
|
296
|
+
let left = clipFeatures(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, this.props);
|
|
297
|
+
let right = clipFeatures(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, this.props);
|
|
298
|
+
// @ts-expect-error - unclear why this is needed?
|
|
299
|
+
features = null;
|
|
300
|
+
if (left) {
|
|
301
|
+
tl = clipFeatures(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props);
|
|
302
|
+
bl = clipFeatures(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props);
|
|
303
|
+
left = null;
|
|
304
|
+
}
|
|
305
|
+
if (right) {
|
|
306
|
+
tr = clipFeatures(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props);
|
|
307
|
+
br = clipFeatures(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props);
|
|
308
|
+
right = null;
|
|
309
|
+
}
|
|
310
|
+
log.timeEnd(2, 'clipping tile')();
|
|
311
|
+
stack.push(tl || [], z + 1, x * 2, y * 2);
|
|
312
|
+
stack.push(bl || [], z + 1, x * 2, y * 2 + 1);
|
|
313
|
+
stack.push(tr || [], z + 1, x * 2 + 1, y * 2);
|
|
314
|
+
stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function toID(z, x, y) {
|
|
319
|
+
return ((1 << z) * y + x) * 32 + z;
|
|
320
|
+
}
|
|
321
|
+
/*
|
|
322
|
+
|
|
323
|
+
// eslint-disable-next-line max-statements, complexity
|
|
324
|
+
function convertToGeoJSONTable(
|
|
325
|
+
vtTile: ProtoTile,
|
|
326
|
+
props: {
|
|
327
|
+
coordinates: 'local' | 'wgs84' | 'EPSG:4326';
|
|
328
|
+
tileIndex: {x: number; y: number; z: number};
|
|
329
|
+
extent: number;
|
|
330
|
+
}
|
|
331
|
+
): GeoJSONTable | null {
|
|
332
|
+
const features: Feature[] = [];
|
|
333
|
+
for (const rawFeature of vtTile.features) {
|
|
334
|
+
if (!rawFeature || !rawFeature.geometry) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let type:
|
|
339
|
+
| 'Point'
|
|
340
|
+
| 'MultiPoint'
|
|
341
|
+
| 'LineString'
|
|
342
|
+
| 'MultiLineString'
|
|
343
|
+
| 'Polygon'
|
|
344
|
+
| 'MultiPolygon';
|
|
345
|
+
|
|
346
|
+
let coordinates: any;
|
|
347
|
+
|
|
348
|
+
// raw geometry
|
|
349
|
+
switch (rawFeature.type) {
|
|
350
|
+
case 1:
|
|
351
|
+
if (rawFeature.geometry.length === 1) {
|
|
352
|
+
type = 'Point';
|
|
353
|
+
coordinates = rawFeature.geometry[0];
|
|
354
|
+
} else {
|
|
355
|
+
type = 'MultiPoint';
|
|
356
|
+
coordinates = rawFeature.geometry;
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
case 2:
|
|
360
|
+
if (rawFeature.geometry.length === 1) {
|
|
361
|
+
type = 'LineString';
|
|
362
|
+
coordinates = rawFeature.geometry[0];
|
|
363
|
+
} else {
|
|
364
|
+
type = 'MultiLineString';
|
|
365
|
+
coordinates = rawFeature.geometry;
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
case 3:
|
|
369
|
+
if (rawFeature.geometry.length > 1) {
|
|
370
|
+
type = 'MultiPolygon';
|
|
371
|
+
coordinates = [rawFeature.geometry];
|
|
372
|
+
} else {
|
|
373
|
+
type = 'Polygon';
|
|
374
|
+
coordinates = rawFeature.geometry;
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
default:
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
switch (props.coordinates) {
|
|
382
|
+
case 'EPSG:4326':
|
|
383
|
+
case 'wgs84':
|
|
384
|
+
projectToLngLat(coordinates, props.tileIndex, props.extent);
|
|
385
|
+
break;
|
|
386
|
+
|
|
387
|
+
case 'local':
|
|
388
|
+
convertToLocalCoordinates(coordinates, props.extent);
|
|
389
|
+
break;
|
|
390
|
+
|
|
391
|
+
default:
|
|
392
|
+
throw new Error(`Unsupported CRS ${props.coordinates}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const feature: Feature = {
|
|
396
|
+
type: 'Feature',
|
|
397
|
+
geometry: {
|
|
398
|
+
type,
|
|
399
|
+
coordinates
|
|
400
|
+
},
|
|
401
|
+
properties: rawFeature.tags || {},
|
|
402
|
+
id: rawFeature.id
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
features.push(feature);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (features.length === 0) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const table: GeoJSONTable = {
|
|
413
|
+
shape: 'geojson-table',
|
|
414
|
+
type: 'FeatureCollection',
|
|
415
|
+
features
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
return table;
|
|
419
|
+
}
|
|
420
|
+
*/
|
package/dist/tilejson-loader.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { parseTileJSON } from "./lib/parse-tilejson.js";
|
|
5
5
|
// __VERSION__ is injected by babel-plugin-version-inline
|
|
6
6
|
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
|
|
7
|
-
const VERSION = typeof "4.
|
|
7
|
+
const VERSION = typeof "4.3.0-alpha.2" !== 'undefined' ? "4.3.0-alpha.2" : 'latest';
|
|
8
8
|
/**
|
|
9
9
|
* Loader for TileJSON metadata
|
|
10
10
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loaders.gl/mvt",
|
|
3
3
|
"description": "Loader for Mapbox Vector Tiles",
|
|
4
|
-
"version": "4.3.0-alpha.
|
|
4
|
+
"version": "4.3.0-alpha.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"publishConfig": {
|
|
@@ -41,11 +41,12 @@
|
|
|
41
41
|
"build-worker": "esbuild src/workers/mvt-worker.ts --bundle --outfile=dist/mvt-worker.js --define:__VERSION__=\\\"$npm_package_version\\\""
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@loaders.gl/gis": "4.3.0-alpha.
|
|
45
|
-
"@loaders.gl/images": "4.3.0-alpha.
|
|
46
|
-
"@loaders.gl/loader-utils": "4.3.0-alpha.
|
|
47
|
-
"@loaders.gl/schema": "4.3.0-alpha.
|
|
44
|
+
"@loaders.gl/gis": "4.3.0-alpha.3",
|
|
45
|
+
"@loaders.gl/images": "4.3.0-alpha.3",
|
|
46
|
+
"@loaders.gl/loader-utils": "4.3.0-alpha.3",
|
|
47
|
+
"@loaders.gl/schema": "4.3.0-alpha.3",
|
|
48
48
|
"@math.gl/polygon": "^4.0.0",
|
|
49
|
+
"@probe.gl/stats": "^4.0.0",
|
|
49
50
|
"pbf": "^3.2.1"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
@@ -54,5 +55,5 @@
|
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"@loaders.gl/core": "^4.0.0"
|
|
56
57
|
},
|
|
57
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "3213679d79e6ff2814d48fd3337acfa446c74099"
|
|
58
59
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export {MVTLoader, MVTWorkerLoader} from './mvt-loader';
|
|
5
|
+
// TileJSONLoader
|
|
7
6
|
|
|
8
|
-
export type {TileJSON} from './lib/parse-tilejson';
|
|
9
|
-
export type {TileJSONLoaderOptions} from './tilejson-loader';
|
|
10
7
|
export {TileJSONLoader} from './tilejson-loader';
|
|
8
|
+
export type {TileJSONLoaderOptions} from './tilejson-loader';
|
|
9
|
+
export type {TileJSON} from './lib/parse-tilejson';
|
|
10
|
+
|
|
11
|
+
// MVTLoader
|
|
12
|
+
|
|
13
|
+
export {MVTLoader, MVTWorkerLoader} from './mvt-loader';
|
|
14
|
+
export type {MVTLoaderOptions} from './mvt-loader';
|
|
15
|
+
|
|
16
|
+
// MVTSource
|
|
11
17
|
|
|
12
18
|
export {MVTSource} from './mvt-source';
|
|
19
|
+
export type {MVTTileSource, MVTTileSourceProps} from './mvt-source';
|
|
13
20
|
|
|
14
|
-
//
|
|
21
|
+
// TableTileSource (dynamically tiles a table)
|
|
15
22
|
|
|
16
|
-
export
|
|
17
|
-
export {
|
|
23
|
+
export {TableTileSource} from './table-tile-source';
|
|
24
|
+
export type {DynamicVectorTileSource, DynamicVectorTileSourceProps} from './table-tile-source';
|
package/src/lib/parse-mvt.ts
CHANGED
|
@@ -155,9 +155,9 @@ function getDecodedFeature(
|
|
|
155
155
|
options: MVTOptions,
|
|
156
156
|
layerName: string
|
|
157
157
|
): Feature {
|
|
158
|
-
const decodedFeature = feature.
|
|
159
|
-
|
|
160
|
-
options.
|
|
158
|
+
const decodedFeature = feature.toGeoJSONFeature(
|
|
159
|
+
options.coordinates || 'local',
|
|
160
|
+
options.tileIndex
|
|
161
161
|
);
|
|
162
162
|
|
|
163
163
|
// Add layer name to GeoJSON properties
|
|
@@ -179,10 +179,7 @@ function getDecodedFeatureBinary(
|
|
|
179
179
|
options: MVTOptions,
|
|
180
180
|
layerName: string
|
|
181
181
|
): FlatFeature {
|
|
182
|
-
const decodedFeature = feature.
|
|
183
|
-
// @ts-expect-error
|
|
184
|
-
options.coordinates === 'wgs84' ? options.tileIndex : transformToLocalCoordinatesBinary
|
|
185
|
-
);
|
|
182
|
+
const decodedFeature = feature.toBinaryFeature(options.coordinates || 'local', options.tileIndex);
|
|
186
183
|
|
|
187
184
|
// Add layer name to GeoJSON properties
|
|
188
185
|
if (options.layerProperty && decodedFeature.properties) {
|
|
@@ -191,29 +188,3 @@ function getDecodedFeatureBinary(
|
|
|
191
188
|
|
|
192
189
|
return decodedFeature;
|
|
193
190
|
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* @param line
|
|
197
|
-
* @param feature
|
|
198
|
-
*/
|
|
199
|
-
function transformToLocalCoordinates(line: number[], feature: {extent: any}): void {
|
|
200
|
-
// This function transforms local coordinates in a
|
|
201
|
-
// [0 - bufferSize, this.extent + bufferSize] range to a
|
|
202
|
-
// [0 - (bufferSize / this.extent), 1 + (bufferSize / this.extent)] range.
|
|
203
|
-
// The resulting extent would be 1.
|
|
204
|
-
const {extent} = feature;
|
|
205
|
-
for (let i = 0; i < line.length; i++) {
|
|
206
|
-
const p = line[i];
|
|
207
|
-
p[0] /= extent;
|
|
208
|
-
p[1] /= extent;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function transformToLocalCoordinatesBinary(data: number[], feature: {extent: any}) {
|
|
213
|
-
// For the binary code path, the feature data is just
|
|
214
|
-
// one big flat array, so we just divide each value
|
|
215
|
-
const {extent} = feature;
|
|
216
|
-
for (let i = 0, il = data.length; i < il; ++i) {
|
|
217
|
-
data[i] /= extent;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -16,11 +16,6 @@ export type TileJSON = {
|
|
|
16
16
|
tileFormat?: string;
|
|
17
17
|
tilesetType?: string;
|
|
18
18
|
|
|
19
|
-
/** Generating application. Tippecanoe adds this. */
|
|
20
|
-
generator?: string;
|
|
21
|
-
/** Generating application options. Tippecanoe adds this. */
|
|
22
|
-
generatorOptions?: string;
|
|
23
|
-
|
|
24
19
|
/** Tile indexing scheme */
|
|
25
20
|
scheme?: 'xyz' | 'tms';
|
|
26
21
|
/** Sharded URLs */
|
|
@@ -38,6 +33,11 @@ export type TileJSON = {
|
|
|
38
33
|
// Combination of tilestats (if present) and tilejson layer information
|
|
39
34
|
layers?: TileJSONLayer[];
|
|
40
35
|
|
|
36
|
+
/** Generating application. Tippecanoe adds this. */
|
|
37
|
+
generator?: string;
|
|
38
|
+
/** Generating application options. Tippecanoe adds this. */
|
|
39
|
+
generatorOptions?: string;
|
|
40
|
+
|
|
41
41
|
/** Any nested JSON metadata */
|
|
42
42
|
metaJson?: any | null;
|
|
43
43
|
};
|
|
@@ -419,7 +419,7 @@ function attributeTypeToFieldType(aType: string): {type: string} {
|
|
|
419
419
|
const type = aType.toLowerCase();
|
|
420
420
|
if (!type || !attrTypeMap[type]) {
|
|
421
421
|
// console.warn(
|
|
422
|
-
// `cannot convert
|
|
422
|
+
// `cannot convert feature type ${type} to loaders.gl data type, use string by default`
|
|
423
423
|
// );
|
|
424
424
|
}
|
|
425
425
|
return attrTypeMap[type] || {type: 'string'};
|
|
@@ -20,16 +20,80 @@ export function signedArea(ring: number[][]) {
|
|
|
20
20
|
return sum;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* This function projects local coordinates in a
|
|
25
|
+
* [0 - bufferSize, this.extent + bufferSize] range to a
|
|
26
|
+
* [0 - (bufferSize / this.extent), 1 + (bufferSize / this.extent)] range.
|
|
27
|
+
* The resulting extent would be 1.
|
|
28
|
+
* @param line
|
|
29
|
+
* @param feature
|
|
30
|
+
*/
|
|
31
|
+
export function convertToLocalCoordinates(
|
|
32
|
+
coordinates: number[] | number[][] | number[][][] | number[][][][],
|
|
33
|
+
extent: number
|
|
34
|
+
): void {
|
|
35
|
+
if (Array.isArray(coordinates[0])) {
|
|
36
|
+
for (const subcoords of coordinates) {
|
|
37
|
+
convertToLocalCoordinates(subcoords as number[] | number[][] | number[][][], extent);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Just a point
|
|
43
|
+
const p = coordinates as number[];
|
|
44
|
+
p[0] /= extent;
|
|
45
|
+
p[1] /= extent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* For the binary code path, the feature data is just
|
|
50
|
+
* one big flat array, so we just divide each value
|
|
51
|
+
* @param data
|
|
52
|
+
* @param feature
|
|
53
|
+
*/
|
|
54
|
+
export function convertToLocalCoordinatesFlat(data: number[], extent: number): void {
|
|
55
|
+
for (let i = 0; i < data.length; ++i) {
|
|
56
|
+
data[i] /= extent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
23
60
|
/**
|
|
24
61
|
* Projects local tile coordinates to lngLat in place.
|
|
25
62
|
* @param points
|
|
26
63
|
* @param tileIndex
|
|
27
64
|
*/
|
|
65
|
+
export function projectToLngLat(
|
|
66
|
+
line: number[] | number[][] | number[][][],
|
|
67
|
+
tileIndex: {x: number; y: number; z: number},
|
|
68
|
+
extent: number
|
|
69
|
+
): void {
|
|
70
|
+
if (typeof line[0][0] !== 'number') {
|
|
71
|
+
for (const point of line) {
|
|
72
|
+
// @ts-expect-error
|
|
73
|
+
projectToLngLat(point, tileIndex, extent);
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const size = extent * Math.pow(2, tileIndex.z);
|
|
78
|
+
const x0 = extent * tileIndex.x;
|
|
79
|
+
const y0 = extent * tileIndex.y;
|
|
80
|
+
for (let j = 0; j < line.length; j++) {
|
|
81
|
+
const p = line[j];
|
|
82
|
+
p[0] = ((p[0] + x0) * 360) / size - 180;
|
|
83
|
+
const y2 = 180 - ((p[1] + y0) * 360) / size;
|
|
84
|
+
p[1] = (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Projects local tile coordinates to lngLat in place.
|
|
90
|
+
* @param points
|
|
91
|
+
* @param tileIndex
|
|
28
92
|
export function projectTileCoordinatesToLngLat(
|
|
29
93
|
points: number[][],
|
|
30
94
|
tileIndex: {x: number; y: number; z: number},
|
|
31
95
|
extent: number
|
|
32
|
-
) {
|
|
96
|
+
): void {
|
|
33
97
|
const {x, y, z} = tileIndex;
|
|
34
98
|
const size = extent * Math.pow(2, z);
|
|
35
99
|
const x0 = extent * x;
|
|
@@ -41,6 +105,7 @@ export function projectTileCoordinatesToLngLat(
|
|
|
41
105
|
p[1] = (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;
|
|
42
106
|
}
|
|
43
107
|
}
|
|
108
|
+
*/
|
|
44
109
|
|
|
45
110
|
/**
|
|
46
111
|
*
|