maplibre-gl 6.2.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/maplibre-gl-dev.mjs +1108 -427
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +359 -291
- package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared.mjs +2 -2
- package/dist/maplibre-gl-shared.mjs.map +1 -1
- package/dist/maplibre-gl-worker-dev.mjs +23 -49
- package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
- package/dist/maplibre-gl-worker.mjs +2 -2
- package/dist/maplibre-gl-worker.mjs.map +1 -1
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +467 -151
- package/dist/maplibre-gl.mjs +49 -49
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +12 -22
- package/src/data/bucket/fill_extrusion_bucket.ts +1 -13
- package/src/data/bucket/round_polygon_corners.test.ts +84 -36
- package/src/data/bucket/round_polygon_corners.ts +90 -39
- package/src/data/extent_bounds.ts +28 -1
- package/src/data/feature_index.bench.ts +21 -0
- package/src/data/segment.test.ts +2 -2
- package/src/geo/edge_insets.test.ts +1 -1
- package/src/geo/lng_lat.test.ts +7 -7
- package/src/geo/lng_lat_bounds.test.ts +4 -4
- package/src/geo/mercator_coordinate.test.ts +2 -2
- package/src/geo/projection/camera_helper.ts +5 -0
- package/src/geo/projection/globe_transform.test.ts +154 -1
- package/src/geo/projection/globe_transform.ts +7 -3
- package/src/geo/projection/globe_utils.ts +189 -2
- package/src/geo/projection/mercator_camera_helper.ts +1 -1
- package/src/geo/projection/mercator_transform.ts +21 -5
- package/src/geo/projection/vertical_perspective_camera_helper.test.ts +24 -0
- package/src/geo/projection/vertical_perspective_camera_helper.ts +71 -41
- package/src/geo/projection/vertical_perspective_transform.ts +7 -1
- package/src/geo/transform_interface.ts +13 -1
- package/src/index.ts +7 -2
- package/src/render/image_atlas.test.ts +180 -0
- package/src/render/image_atlas.ts +52 -6
- package/src/render/image_manager.test.ts +111 -0
- package/src/render/image_manager.ts +143 -144
- package/src/render/painter.ts +6 -14
- package/src/render/pattern_atlas.test.ts +131 -0
- package/src/render/pattern_atlas.ts +144 -0
- package/src/render/update_pattern_positions_in_program.test.ts +1 -1
- package/src/shaders/glsl/_prelude.vertex.glsl +10 -11
- package/src/shaders/glsl/_prelude.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/circle.vertex.glsl +4 -4
- package/src/shaders/glsl/circle.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/color_relief.fragment.glsl +1 -2
- package/src/shaders/glsl/color_relief.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/fill_extrusion.vertex.glsl +3 -3
- package/src/shaders/glsl/fill_extrusion.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/fill_extrusion_pattern.vertex.glsl +5 -5
- package/src/shaders/glsl/fill_extrusion_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/heatmap.vertex.glsl +4 -4
- package/src/shaders/glsl/heatmap.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/hillshade_prepare.fragment.glsl +12 -12
- package/src/shaders/glsl/hillshade_prepare.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line.vertex.glsl +7 -7
- package/src/shaders/glsl/line.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient.vertex.glsl +6 -6
- package/src/shaders/glsl/line_gradient.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient_sdf.vertex.glsl +7 -7
- package/src/shaders/glsl/line_gradient_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_pattern.vertex.glsl +7 -7
- package/src/shaders/glsl/line_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_sdf.vertex.glsl +7 -7
- package/src/shaders/glsl/line_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/raster.fragment.glsl +4 -4
- package/src/shaders/glsl/raster.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/raster.vertex.glsl +11 -6
- package/src/shaders/glsl/raster.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_icon.vertex.glsl +5 -5
- package/src/shaders/glsl/symbol_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_sdf.vertex.glsl +5 -5
- package/src/shaders/glsl/symbol_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl +6 -6
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
- package/src/source/canvas_source.test.ts +31 -0
- package/src/source/canvas_source.ts +2 -1
- package/src/source/geojson_worker_source.test.ts +88 -0
- package/src/source/geojson_worker_source.ts +22 -39
- package/src/source/image_source.test.ts +246 -38
- package/src/source/image_source.ts +132 -19
- package/src/source/raster_dem_tile_source.test.ts +37 -3
- package/src/source/raster_dem_tile_source.ts +1 -2
- package/src/source/raster_dem_tile_worker_source.test.ts +1 -1
- package/src/source/raster_tile_source.test.ts +5 -26
- package/src/source/raster_tile_source.ts +4 -3
- package/src/source/vector_tile_worker_source.test.ts +94 -2
- package/src/source/vector_tile_worker_source.ts +18 -27
- package/src/source/video_source.test.ts +12 -8
- package/src/source/worker_tile.bench.ts +10 -0
- package/src/source/worker_tile.ts +0 -3
- package/src/style/create_style_layer.bench.ts +15 -0
- package/src/style/properties.bench.ts +81 -0
- package/src/style/style.test.ts +75 -10
- package/src/style/style.ts +37 -64
- package/src/style/style_image.ts +67 -9
- package/src/style/style_layer.bench.ts +65 -0
- package/src/style/style_layer.test.ts +1 -1
- package/src/style/style_layer.ts +2 -2
- package/src/style/validate_style.bench.ts +11 -0
- package/src/style/validate_style.test.ts +2 -2
- package/src/symbol/anchor.test.ts +2 -2
- package/src/symbol/collision_index.bench.ts +116 -0
- package/src/symbol/cross_tile_symbol_index.bench.ts +49 -0
- package/src/symbol/cross_tile_symbol_index.test.ts +31 -0
- package/src/symbol/cross_tile_symbol_index.ts +67 -37
- package/src/symbol/shaping.test.ts +2 -1
- package/src/symbol/symbol_layout.bench.ts +30 -0
- package/src/tile/tile_id.test.ts +1 -1
- package/src/tile/tile_manager.test.ts +69 -1
- package/src/tile/tile_manager.ts +16 -11
- package/src/ui/control/fullscreen_control.test.ts +10 -10
- package/src/ui/control/geolocate_control.test.ts +3 -5
- package/src/ui/control/globe_control.test.ts +6 -6
- package/src/ui/events.ts +1 -1
- package/src/ui/handler/scroll_zoom.test.ts +50 -0
- package/src/ui/handler/two_fingers_touch.test.ts +17 -6
- package/src/ui/handler_inertia.test.ts +91 -0
- package/src/ui/handler_inertia.ts +65 -14
- package/src/ui/handler_manager.test.ts +243 -3
- package/src/ui/handler_manager.ts +79 -16
- package/src/ui/map.ts +17 -3
- package/src/ui/map_tests/map_cross_window.test.ts +2 -2
- package/src/ui/map_tests/map_events.test.ts +3 -3
- package/src/ui/map_tests/map_images.test.ts +55 -0
- package/src/ui/map_tests/map_style.test.ts +9 -8
- package/src/ui/map_tests/map_webgl.test.ts +30 -0
- package/src/ui/marker.test.ts +177 -42
- package/src/ui/marker.ts +120 -10
- package/src/ui/popup.test.ts +14 -14
- package/src/util/evented.test.ts +33 -28
- package/src/util/evented.ts +69 -43
- package/src/util/image_request.ts +31 -1
- package/src/util/test/null_gl.ts +10 -0
- package/src/util/test/util.ts +2 -1
- package/src/util/util.test.ts +1 -1
- package/src/util/web_worker_transfer.test.ts +5 -5
- package/src/webgl/context.ts +20 -0
- package/src/webgl/draw/draw_background.ts +1 -1
- package/src/webgl/draw/draw_raster.ts +11 -6
- package/src/webgl/program/pattern.ts +3 -3
- package/src/webgl/program/raster_program.ts +11 -0
- package/src/webgl/program.ts +31 -16
- package/src/webgl/texture.test.ts +16 -1
- package/src/webgl/texture.ts +3 -0
- package/src/webgl/vertex_buffer.test.ts +17 -2
- package/src/webgl/vertex_buffer.ts +24 -13
|
@@ -55,11 +55,10 @@ export class RasterDEMTileSource extends RasterTileSource implements Source {
|
|
|
55
55
|
|
|
56
56
|
override async loadTile(tile: Tile): Promise<void> {
|
|
57
57
|
const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
|
|
58
|
-
const request = await this.map._requestManager.transformRequest(url, ResourceType.Tile);
|
|
59
58
|
tile.neighboringTiles = this._getNeighboringTiles(tile.tileID);
|
|
60
59
|
tile.abortController = new AbortController();
|
|
61
60
|
try {
|
|
62
|
-
const response = await ImageRequest.
|
|
61
|
+
const response = await ImageRequest.transformAndGetImage(this.map._requestManager, url, ResourceType.Tile, tile.abortController, this.map._refreshExpiredTiles, {colorSpaceConversion: 'none'});
|
|
63
62
|
delete tile.abortController;
|
|
64
63
|
if (tile.aborted) {
|
|
65
64
|
tile.state = 'unloaded';
|
|
@@ -445,36 +445,18 @@ describe('RasterTileSource', () => {
|
|
|
445
445
|
expect(expiryDataSpy).toHaveBeenCalledTimes(1);
|
|
446
446
|
});
|
|
447
447
|
|
|
448
|
-
test('
|
|
449
|
-
let transformStarted: () => void;
|
|
450
|
-
const transformCalled = new Promise<void>((resolve) => {
|
|
451
|
-
transformStarted = resolve;
|
|
452
|
-
});
|
|
448
|
+
test('does not request a tile that was aborted while its request was being transformed', async () => {
|
|
453
449
|
let releaseTransform: (params: {url: string}) => void;
|
|
454
450
|
const source = createSource({
|
|
455
451
|
tiles: ['http://example.com/{z}/{x}/{y}.png']
|
|
456
452
|
}, (url, type) => {
|
|
457
453
|
if (type !== 'Tile') return {url};
|
|
458
|
-
transformStarted();
|
|
459
454
|
return new Promise<{url: string}>((resolve) => {
|
|
460
455
|
releaseTransform = resolve;
|
|
461
456
|
});
|
|
462
457
|
});
|
|
463
458
|
source.tiles = ['http://example.com/{z}/{x}/{y}.png'];
|
|
464
459
|
|
|
465
|
-
const image = {width: 256, height: 256} as ImageBitmap;
|
|
466
|
-
let requestController: AbortController;
|
|
467
|
-
let tileControllerAtRequestTime: AbortController;
|
|
468
|
-
const getImageSpy = vi.spyOn(ImageRequest, 'getImage').mockImplementation(async (_request, abortController) => {
|
|
469
|
-
requestController = abortController;
|
|
470
|
-
tileControllerAtRequestTime = tile.abortController;
|
|
471
|
-
return {data: image};
|
|
472
|
-
});
|
|
473
|
-
source.map.painter = {
|
|
474
|
-
context: {gl: {}},
|
|
475
|
-
getTileTexture: () => ({update: vi.fn()})
|
|
476
|
-
} as any;
|
|
477
|
-
|
|
478
460
|
const tile = {
|
|
479
461
|
tileID: new OverscaledTileID(10, 0, 10, 5, 5),
|
|
480
462
|
state: 'loading',
|
|
@@ -482,16 +464,13 @@ describe('RasterTileSource', () => {
|
|
|
482
464
|
} as any as Tile;
|
|
483
465
|
|
|
484
466
|
const loadPromise = source.loadTile(tile);
|
|
485
|
-
|
|
486
|
-
await source.abortTile(tile);
|
|
467
|
+
tile.aborted = true;
|
|
468
|
+
await source.abortTile(tile);
|
|
487
469
|
releaseTransform({url: 'http://example.com/10/5/5.png'});
|
|
488
470
|
await expect(loadPromise).resolves.toBeUndefined();
|
|
489
471
|
|
|
490
|
-
expect(
|
|
491
|
-
|
|
492
|
-
// arriving while it is in flight reaches exactly this request.
|
|
493
|
-
expect(requestController).toBeInstanceOf(AbortController);
|
|
494
|
-
expect(requestController).toBe(tileControllerAtRequestTime);
|
|
472
|
+
expect(server.requests).toHaveLength(0);
|
|
473
|
+
expect(tile.state).toBe('unloaded');
|
|
495
474
|
});
|
|
496
475
|
|
|
497
476
|
test('does not throw when tile is aborted', async () => {
|
|
@@ -206,11 +206,12 @@ export class RasterTileSource extends Evented<SourceEventType> implements Source
|
|
|
206
206
|
const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
|
|
207
207
|
const premultiply = this._premultiplyAlpha;
|
|
208
208
|
const imageBitmapOptions = premultiply ? undefined : {premultiplyAlpha: 'none'} as const;
|
|
209
|
-
const request = await this.map._requestManager.transformRequest(url, ResourceType.Tile);
|
|
210
209
|
tile.abortController = new AbortController();
|
|
211
210
|
try {
|
|
212
|
-
const response = await ImageRequest.
|
|
213
|
-
|
|
211
|
+
const response = await ImageRequest.transformAndGetImage(
|
|
212
|
+
this.map._requestManager,
|
|
213
|
+
url,
|
|
214
|
+
ResourceType.Tile,
|
|
214
215
|
tile.abortController,
|
|
215
216
|
this.map._refreshExpiredTiles,
|
|
216
217
|
imageBitmapOptions
|
|
@@ -170,6 +170,100 @@ describe('vector tile worker source', () => {
|
|
|
170
170
|
expect(res.rawTileData).toStrictEqual(rawTileData);
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
+
test('VectorTileWorkerSource.reloadTile includes rawTileData in response if loadTile was aborted', async () => {
|
|
174
|
+
const rawTileData = new ArrayBuffer(0);
|
|
175
|
+
|
|
176
|
+
const layerIndex = new StyleLayerIndex([{
|
|
177
|
+
id: 'test',
|
|
178
|
+
source: 'source',
|
|
179
|
+
'source-layer': 'test',
|
|
180
|
+
type: 'symbol',
|
|
181
|
+
layout: {
|
|
182
|
+
'icon-image': 'hello',
|
|
183
|
+
'text-font': ['StandardFont-Bold'],
|
|
184
|
+
'text-field': '{name}'
|
|
185
|
+
}
|
|
186
|
+
}]);
|
|
187
|
+
|
|
188
|
+
let sendAsyncShouldAbort = false;
|
|
189
|
+
const actor = {
|
|
190
|
+
sendAsync: (message: {type: string; data: unknown}, abortController: AbortController) => {
|
|
191
|
+
if (sendAsyncShouldAbort) {
|
|
192
|
+
return new Promise((_resolve, reject) => {
|
|
193
|
+
reject('aborted by test');
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const res = setTimeout(() => {
|
|
199
|
+
const response = message.type === 'getImages' ?
|
|
200
|
+
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
|
|
201
|
+
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}};
|
|
202
|
+
resolve(response);
|
|
203
|
+
}, 100);
|
|
204
|
+
abortController.signal.addEventListener('abort', () => {
|
|
205
|
+
clearTimeout(res);
|
|
206
|
+
reject('aborted by abortController');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const source = new VectorTileWorkerSource(actor, layerIndex, ['hello']);
|
|
213
|
+
source.loadVectorTile = (_params, _rawData) => {
|
|
214
|
+
return {
|
|
215
|
+
vectorTile: {
|
|
216
|
+
layers: {
|
|
217
|
+
test: {
|
|
218
|
+
version: 2,
|
|
219
|
+
name: 'test',
|
|
220
|
+
extent: 8192,
|
|
221
|
+
length: 1,
|
|
222
|
+
feature: (featureIndex: number) => ({
|
|
223
|
+
extent: 8192,
|
|
224
|
+
type: 1,
|
|
225
|
+
id: featureIndex,
|
|
226
|
+
properties: {
|
|
227
|
+
name: 'test'
|
|
228
|
+
},
|
|
229
|
+
loadGeometry () {
|
|
230
|
+
return [[new Point(0, 0)]];
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
rawData: rawTileData
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
server.respondWith(request => {
|
|
241
|
+
request.respond(200, {'Content-Type': 'application/pbf'}, rawTileData as any);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
sendAsyncShouldAbort = true;
|
|
245
|
+
const loadTilePromise = source.loadTile({
|
|
246
|
+
source: 'source',
|
|
247
|
+
uid: 0,
|
|
248
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
249
|
+
request: {url: 'http://localhost:2900/faketile.pbf'},
|
|
250
|
+
subdivisionGranularity: SubdivisionGranularitySetting.noSubdivision,
|
|
251
|
+
} as any as WorkerTileParameters);
|
|
252
|
+
server.respond();
|
|
253
|
+
await expect(loadTilePromise).rejects.toThrow(/aborted/);
|
|
254
|
+
|
|
255
|
+
sendAsyncShouldAbort = false;
|
|
256
|
+
const res = await source.reloadTile({
|
|
257
|
+
source: 'source',
|
|
258
|
+
uid: 0,
|
|
259
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
260
|
+
subdivisionGranularity: SubdivisionGranularitySetting.noSubdivision,
|
|
261
|
+
} as any as WorkerTileParameters) as WorkerTileWithData;
|
|
262
|
+
expect(res).toBeDefined();
|
|
263
|
+
expect(res.rawTileData).toBeDefined();
|
|
264
|
+
expect(res.rawTileData).toStrictEqual(rawTileData);
|
|
265
|
+
});
|
|
266
|
+
|
|
173
267
|
test('VectorTileWorkerSource.loadTile reparses tile if reloadTile is called during reparsing', async () => {
|
|
174
268
|
const rawTileData = new ArrayBuffer(0);
|
|
175
269
|
const loadVectorData = (_params, _rawData) => {
|
|
@@ -193,7 +287,6 @@ describe('vector tile worker source', () => {
|
|
|
193
287
|
const parseWorkerTileMock = vi
|
|
194
288
|
.spyOn(WorkerTile.prototype, 'parse')
|
|
195
289
|
.mockImplementation(function(this: WorkerTile, _data, _layerIndex, _availableImages, _actor) {
|
|
196
|
-
this.status = 'parsing';
|
|
197
290
|
return new Promise((resolve) => {
|
|
198
291
|
setTimeout(() => resolve({} as WorkerTileResult), 20);
|
|
199
292
|
});
|
|
@@ -248,7 +341,6 @@ describe('vector tile worker source', () => {
|
|
|
248
341
|
const parseWorkerTileMock = vi
|
|
249
342
|
.spyOn(WorkerTile.prototype, 'parse')
|
|
250
343
|
.mockImplementation(function(this: WorkerTile, _data, _layerIndex, _availableImages, _actor) {
|
|
251
|
-
this.status = 'parsing';
|
|
252
344
|
return new Promise((resolve) => {
|
|
253
345
|
setTimeout(() => resolve({} as WorkerTileResult), 20);
|
|
254
346
|
});
|
|
@@ -3,7 +3,7 @@ import {VectorTile} from '@mapbox/vector-tile';
|
|
|
3
3
|
import {fromVectorTileJs, type VectorTileLayerLike, type VectorTileLike} from '@maplibre/vt-pbf';
|
|
4
4
|
import {type ExpiryData, getArrayBuffer} from '../util/ajax.ts';
|
|
5
5
|
import {WorkerTile} from './worker_tile.ts';
|
|
6
|
-
import {WorkerTileState
|
|
6
|
+
import {WorkerTileState} from './worker_tile_state.ts';
|
|
7
7
|
import {BoundedLRUCache} from '../tile/tile_cache.ts';
|
|
8
8
|
import {ensureError, extend} from '../util/util.ts';
|
|
9
9
|
import {RequestPerformance} from '../util/request_performance.ts';
|
|
@@ -106,17 +106,12 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
106
106
|
|
|
107
107
|
workerTile.vectorTile = vectorTile;
|
|
108
108
|
this.tileState.markLoaded(uid, workerTile);
|
|
109
|
+
const parsingState = {rawData, cacheControl, resourceTiming};
|
|
110
|
+
this.tileState.setParsing(uid, parsingState);
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
this.tileState.setParsing(uid, parseState);
|
|
112
|
-
try {
|
|
113
|
-
return await this._parseWorkerTile(workerTile, params, parseState);
|
|
114
|
-
} finally {
|
|
115
|
-
this.tileState.removeParsing(uid);
|
|
116
|
-
}
|
|
112
|
+
return await this._parseWorkerTile(workerTile, params);
|
|
117
113
|
} catch (err) {
|
|
118
114
|
this.tileState.finishLoading(uid);
|
|
119
|
-
workerTile.status = 'done';
|
|
120
115
|
this.tileState.markLoaded(uid, workerTile);
|
|
121
116
|
throw err;
|
|
122
117
|
}
|
|
@@ -128,16 +123,23 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
128
123
|
return extend({etagUnmodified: true as const}, cacheControl, resourceTiming);
|
|
129
124
|
}
|
|
130
125
|
|
|
131
|
-
async _parseWorkerTile(workerTile: WorkerTile, params: WorkerTileParameters
|
|
126
|
+
async _parseWorkerTile(workerTile: WorkerTile, params: WorkerTileParameters): Promise<WorkerTileResult> {
|
|
127
|
+
const parseState = this.tileState.getParsing(workerTile.uid);
|
|
128
|
+
|
|
132
129
|
let result = await workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, params.subdivisionGranularity);
|
|
133
130
|
|
|
131
|
+
// We need to pass rawTileData back to the main thread so that it can be stored in the Tile and FeatureIndex.
|
|
132
|
+
// After the main thread has successfully received and stored rawTileData,
|
|
133
|
+
// we no longer need to store it in the worker or transfer additional copies of it.
|
|
134
134
|
if (parseState) {
|
|
135
135
|
const {rawData, cacheControl, resourceTiming} = parseState;
|
|
136
136
|
// Overzoomed tiles are always re-encoded to MVT protobuf by _getOverzoomTile
|
|
137
137
|
const encoding = params.overzoomParameters ? 'mvt' : params.encoding;
|
|
138
|
-
//
|
|
138
|
+
// Return a copy of rawData to the main thread to avoid clearing the worker's buffer
|
|
139
139
|
result = extend({rawTileData: rawData.slice(0), encoding}, result, cacheControl, resourceTiming);
|
|
140
|
+
this.tileState.removeParsing(workerTile.uid);
|
|
140
141
|
}
|
|
142
|
+
// else: this seems like a missing case where cache control is lost? see #3309
|
|
141
143
|
|
|
142
144
|
return result;
|
|
143
145
|
}
|
|
@@ -209,27 +211,16 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
209
211
|
*/
|
|
210
212
|
async reloadTile(params: WorkerTileParameters): Promise<WorkerTileResult> {
|
|
211
213
|
const uid = params.uid;
|
|
212
|
-
|
|
213
214
|
const workerTile = this.tileState.getLoaded(uid);
|
|
214
215
|
if (!workerTile) throw new Error('Should not be trying to reload a tile that was never loaded or has been removed');
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// if we are cancelling the original parse, make sure to pass the rawTileData from the original parse
|
|
220
|
-
const parseState = this.tileState.getParsing(uid);
|
|
221
|
-
try {
|
|
222
|
-
return await this._parseWorkerTile(workerTile, params, parseState);
|
|
223
|
-
} finally {
|
|
224
|
-
this.tileState.removeParsing(uid);
|
|
225
|
-
}
|
|
217
|
+
// If there was no vector tile data on the initial load, don't try to reparse the tile.
|
|
218
|
+
if (!workerTile.vectorTile) {
|
|
219
|
+
return;
|
|
226
220
|
}
|
|
227
221
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (workerTile.status === 'done' && workerTile.vectorTile) {
|
|
231
|
-
return await this._parseWorkerTile(workerTile, params);
|
|
232
|
-
}
|
|
222
|
+
workerTile.showCollisionBoxes = params.showCollisionBoxes;
|
|
223
|
+
return await this._parseWorkerTile(workerTile, params);
|
|
233
224
|
}
|
|
234
225
|
|
|
235
226
|
/**
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {describe,
|
|
2
|
-
import {VideoSource} from './video_source.ts';
|
|
3
|
-
import {extend} from '../util/util.ts';
|
|
1
|
+
import {describe, expect, test, vi} from 'vitest';
|
|
4
2
|
import {getMockDispatcher, waitForEvent} from '../util/test/util.ts';
|
|
5
|
-
|
|
6
|
-
import
|
|
3
|
+
import {extend} from '../util/util.ts';
|
|
4
|
+
import {VideoSource} from './video_source.ts';
|
|
5
|
+
import {MercatorTransform} from '../geo/projection/mercator_transform.ts';
|
|
7
6
|
import {Tile} from '../tile/tile.ts';
|
|
8
7
|
import {OverscaledTileID} from '../tile/tile_id.ts';
|
|
9
8
|
import {Evented} from '../util/evented.ts';
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
9
|
+
import type {IReadonlyTransform} from '../geo/transform_interface.ts';
|
|
10
|
+
import type {Coordinates} from './image_source.ts';
|
|
12
11
|
|
|
13
12
|
class StubMap extends Evented {
|
|
14
13
|
transform: IReadonlyTransform;
|
|
@@ -117,7 +116,7 @@ describe('VideoSource', () => {
|
|
|
117
116
|
expect(tile.state).toBe('loaded');
|
|
118
117
|
});
|
|
119
118
|
|
|
120
|
-
test('onRemove removes playing listener and
|
|
119
|
+
test('onRemove removes playing listener, pauses video and deletes the texture', () => {
|
|
121
120
|
const video = window.document.createElement('video');
|
|
122
121
|
const removeListenerSpy = vi.spyOn(video, 'removeEventListener');
|
|
123
122
|
const pauseSpy = vi.spyOn(video, 'pause').mockImplementation(() => {});
|
|
@@ -129,9 +128,14 @@ describe('VideoSource', () => {
|
|
|
129
128
|
});
|
|
130
129
|
source.video = video;
|
|
131
130
|
|
|
131
|
+
const texture = {destroy: vi.fn()} as any;
|
|
132
|
+
source.texture = texture;
|
|
133
|
+
|
|
132
134
|
source.onRemove();
|
|
133
135
|
|
|
134
136
|
expect(removeListenerSpy).toHaveBeenCalledWith('playing', expect.any(Function));
|
|
135
137
|
expect(pauseSpy).toHaveBeenCalled();
|
|
138
|
+
expect(texture.destroy).toHaveBeenCalledTimes(1);
|
|
139
|
+
expect(source.texture).toBeNull();
|
|
136
140
|
});
|
|
137
141
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {bench} from 'vitest';
|
|
2
|
+
import {fixtureTiles, parseTile} from '../../test/bench/lib/parse_tile.ts';
|
|
3
|
+
|
|
4
|
+
await Promise.all(fixtureTiles.map(tile => parseTile(tile, false)));
|
|
5
|
+
|
|
6
|
+
bench('WorkerTile.parse', async () => {
|
|
7
|
+
for (const tile of fixtureTiles) {
|
|
8
|
+
await parseTile(tile, false);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
@@ -37,7 +37,6 @@ export class WorkerTile {
|
|
|
37
37
|
collectResourceTiming: boolean;
|
|
38
38
|
returnDependencies: boolean;
|
|
39
39
|
|
|
40
|
-
status: 'parsing' | 'done';
|
|
41
40
|
data: VectorTileLike;
|
|
42
41
|
collisionBoxArray: CollisionBoxArray;
|
|
43
42
|
|
|
@@ -61,7 +60,6 @@ export class WorkerTile {
|
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
async parse(data: VectorTileLike, layerIndex: StyleLayerIndex, availableImages: string[], actor: IActor, subdivisionGranularity: SubdivisionGranularitySetting): Promise<WorkerTileResult> {
|
|
64
|
-
this.status = 'parsing';
|
|
65
63
|
this.data = data;
|
|
66
64
|
|
|
67
65
|
this.collisionBoxArray = new CollisionBoxArray();
|
|
@@ -192,7 +190,6 @@ export class WorkerTile {
|
|
|
192
190
|
}
|
|
193
191
|
}
|
|
194
192
|
|
|
195
|
-
this.status = 'done';
|
|
196
193
|
return {
|
|
197
194
|
buckets: Object.values(buckets).filter(b => !b.isEmpty()),
|
|
198
195
|
featureIndex,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {bench} from 'vitest';
|
|
2
|
+
import {derefLayers} from '@maplibre/maplibre-gl-style-spec';
|
|
3
|
+
import {createStyleLayer} from './create_style_layer.ts';
|
|
4
|
+
import brightV9 from '../../test/integration/assets/styles/bright-v9.json' with {type: 'json'};
|
|
5
|
+
|
|
6
|
+
import type {StyleSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
7
|
+
|
|
8
|
+
const style = brightV9 as unknown as StyleSpecification;
|
|
9
|
+
const layers = derefLayers(style.layers);
|
|
10
|
+
|
|
11
|
+
bench('createStyleLayer', () => {
|
|
12
|
+
for (const layer of layers) {
|
|
13
|
+
createStyleLayer(layer, {});
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {bench, describe} from 'vitest';
|
|
2
|
+
import {latest as spec, convertFunction, isFunction, createFunction, createPropertyExpression} from '@maplibre/maplibre-gl-style-spec';
|
|
3
|
+
import brightV9 from '../../test/integration/assets/styles/bright-v9.json' with {type: 'json'};
|
|
4
|
+
|
|
5
|
+
import type {StyleSpecification, StylePropertyExpression, StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
6
|
+
|
|
7
|
+
type Sample = {
|
|
8
|
+
propertySpec: StylePropertySpecification;
|
|
9
|
+
rawValue: unknown;
|
|
10
|
+
rawExpression: unknown;
|
|
11
|
+
compiledFunction: StylePropertyExpression;
|
|
12
|
+
compiledExpression: StylePropertyExpression;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function sample(rawValue: unknown, propertySpec: StylePropertySpecification): Sample {
|
|
16
|
+
const rawExpression = convertFunction(rawValue, propertySpec);
|
|
17
|
+
const compiledFunction = createFunction(rawValue, propertySpec) as StylePropertyExpression;
|
|
18
|
+
const compiledExpression = createPropertyExpression(rawExpression, 'expression', propertySpec);
|
|
19
|
+
if (compiledExpression.result === 'error') {
|
|
20
|
+
throw new Error(compiledExpression.value.map(err => `${err.key}: ${err.message}`).join(', '));
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
propertySpec,
|
|
24
|
+
rawValue,
|
|
25
|
+
rawExpression,
|
|
26
|
+
compiledFunction,
|
|
27
|
+
compiledExpression: compiledExpression.value
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function collect(style: StyleSpecification): Sample[] {
|
|
32
|
+
const samples: Sample[] = [];
|
|
33
|
+
|
|
34
|
+
for (const layer of style.layers) {
|
|
35
|
+
if (!layer.type) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const key in layer.paint) {
|
|
40
|
+
if (isFunction(layer.paint[key])) {
|
|
41
|
+
samples.push(sample(layer.paint[key], spec[`paint_${layer.type}`][key]));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const key in layer.layout) {
|
|
46
|
+
if (isFunction(layer.layout[key])) {
|
|
47
|
+
samples.push(sample(layer.layout[key], spec[`layout_${layer.type}`][key]));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return samples;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const samples = collect(brightV9 as unknown as StyleSpecification);
|
|
56
|
+
|
|
57
|
+
describe('style property functions', () => {
|
|
58
|
+
bench('createFunction', () => {
|
|
59
|
+
for (const {rawValue, propertySpec} of samples) {
|
|
60
|
+
createFunction(rawValue, propertySpec);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
bench('evaluate function', () => {
|
|
65
|
+
for (const {compiledFunction} of samples) {
|
|
66
|
+
compiledFunction.evaluate({zoom: 0});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
bench('createPropertyExpression', () => {
|
|
71
|
+
for (const {rawExpression, propertySpec} of samples) {
|
|
72
|
+
createPropertyExpression(rawExpression, 'expression', propertySpec);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
bench('evaluate expression', () => {
|
|
77
|
+
for (const {compiledExpression} of samples) {
|
|
78
|
+
compiledExpression.evaluate({zoom: 0});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
package/src/style/style.test.ts
CHANGED
|
@@ -3,13 +3,15 @@ import {Style} from './style.ts';
|
|
|
3
3
|
import {TileManager} from '../tile/tile_manager.ts';
|
|
4
4
|
import {StyleLayer} from './style_layer.ts';
|
|
5
5
|
import {extend} from '../util/util.ts';
|
|
6
|
-
import {Event} from '../util/evented.ts';
|
|
6
|
+
import {ErrorEvent, Event} from '../util/evented.ts';
|
|
7
7
|
import {type AJAXError} from '../util/ajax.ts';
|
|
8
|
+
import {MapSourceDataEvent} from '../ui/events.ts';
|
|
8
9
|
import {RGBAImage} from '../util/image.ts';
|
|
9
10
|
import {rtlMainThreadPluginFactory} from '../source/rtl_text_plugin_main_thread.ts';
|
|
10
11
|
import {browser} from '../util/browser.ts';
|
|
11
12
|
import {OverscaledTileID} from '../tile/tile_id.ts';
|
|
12
13
|
import {fakeServer, type FakeServer} from 'nise';
|
|
14
|
+
import {ImageRequest} from '../util/image_request.ts';
|
|
13
15
|
|
|
14
16
|
import {type EvaluationParameters} from './evaluation_parameters.ts';
|
|
15
17
|
import {Color, type Feature, type LayerSpecification, type GeoJSONSourceSpecification, type FilterSpecification, type SourceSpecification, type StyleSpecification, type SymbolLayerSpecification, type SkySpecification, type CameraFunctionSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
@@ -72,6 +74,7 @@ let mockConsoleError: MockInstance;
|
|
|
72
74
|
beforeEach(() => {
|
|
73
75
|
global.fetch = null;
|
|
74
76
|
server = fakeServer.create();
|
|
77
|
+
ImageRequest.resetRequestQueue();
|
|
75
78
|
mockConsoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
76
79
|
});
|
|
77
80
|
|
|
@@ -374,7 +377,7 @@ describe('Style.loadJSON', () => {
|
|
|
374
377
|
}));
|
|
375
378
|
|
|
376
379
|
await style.once('style.load');
|
|
377
|
-
expect(style.tileManagers['mapLibre']
|
|
380
|
+
expect(style.tileManagers['mapLibre']).toBeInstanceOf(TileManager);
|
|
378
381
|
});
|
|
379
382
|
|
|
380
383
|
test('creates layers', async () => {
|
|
@@ -396,7 +399,7 @@ describe('Style.loadJSON', () => {
|
|
|
396
399
|
});
|
|
397
400
|
|
|
398
401
|
await style.once('style.load');
|
|
399
|
-
expect(style.getLayer('fill')
|
|
402
|
+
expect(style.getLayer('fill')).toBeInstanceOf(StyleLayer);
|
|
400
403
|
});
|
|
401
404
|
|
|
402
405
|
test('transforms sprite json and image URLs before request', async () => {
|
|
@@ -460,7 +463,7 @@ describe('Style.loadJSON', () => {
|
|
|
460
463
|
const errorPromise = style.once('error');
|
|
461
464
|
|
|
462
465
|
await style.once('style.load');
|
|
463
|
-
style._layers.background.fire(new
|
|
466
|
+
style._layers.background.fire(new ErrorEvent(new Error('test'), {mapLibre: true}));
|
|
464
467
|
|
|
465
468
|
const e = await errorPromise;
|
|
466
469
|
expect((e as any).layer).toEqual({id: 'background'});
|
|
@@ -1347,8 +1350,8 @@ describe('Style.addSource', () => {
|
|
|
1347
1350
|
});
|
|
1348
1351
|
|
|
1349
1352
|
style.addSource('source-id', source); // fires data twice
|
|
1350
|
-
style.tileManagers['source-id'].fire(new
|
|
1351
|
-
style.tileManagers['source-id'].fire(new
|
|
1353
|
+
style.tileManagers['source-id'].fire(new ErrorEvent(new Error('test')));
|
|
1354
|
+
style.tileManagers['source-id'].fire(new MapSourceDataEvent('data'));
|
|
1352
1355
|
|
|
1353
1356
|
await expect(Promise.all(promises)).resolves.toBeDefined();
|
|
1354
1357
|
});
|
|
@@ -1446,8 +1449,8 @@ describe('Style.removeSource', () => {
|
|
|
1446
1449
|
|
|
1447
1450
|
style.on('data', () => expect(false).toBeTruthy());
|
|
1448
1451
|
style.on('error', () => expect(false).toBeTruthy());
|
|
1449
|
-
tileManager.fire(new
|
|
1450
|
-
tileManager.fire(new
|
|
1452
|
+
tileManager.fire(new MapSourceDataEvent('data'));
|
|
1453
|
+
tileManager.fire(new ErrorEvent(new Error('test')));
|
|
1451
1454
|
});
|
|
1452
1455
|
});
|
|
1453
1456
|
|
|
@@ -1704,6 +1707,68 @@ describe('Style.setSprite', () => {
|
|
|
1704
1707
|
});
|
|
1705
1708
|
});
|
|
1706
1709
|
|
|
1710
|
+
describe('Style._loadSprite', () => {
|
|
1711
|
+
const FIRST_SPRITE_URL = 'http://example.com/sprite1';
|
|
1712
|
+
const SECOND_SPRITE_URL = 'http://example.com/sprite2';
|
|
1713
|
+
|
|
1714
|
+
const spriteJSON = (...imageIds: string[]) => JSON.stringify(
|
|
1715
|
+
Object.fromEntries(imageIds.map(id => [id, {width: 1, height: 1, x: 0, y: 0, pixelRatio: 1}]))
|
|
1716
|
+
);
|
|
1717
|
+
|
|
1718
|
+
/** Loads a sprite through `setSprite` and resolves once it has been applied to the image manager */
|
|
1719
|
+
function loadSprite(style: Style, url: string): Promise<void> {
|
|
1720
|
+
return new Promise((resolve, reject) => {
|
|
1721
|
+
style.setSprite(url, {}, (err) => err ? reject(err) : resolve());
|
|
1722
|
+
sleep(0).then(() => server.respond());
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
async function createLoadedStyle(): Promise<Style> {
|
|
1727
|
+
const style = new Style(getStubMap());
|
|
1728
|
+
style.loadJSON(createStyleJSON());
|
|
1729
|
+
await style.once('style.load');
|
|
1730
|
+
return style;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
beforeEach(() => {
|
|
1734
|
+
vi.spyOn(browser, 'getImageData');
|
|
1735
|
+
server.respondWith('GET', /sprite\d\.png$/, new ArrayBuffer(8) as any);
|
|
1736
|
+
server.respondWith('GET', `${FIRST_SPRITE_URL}.json`, spriteJSON('sharedImage', 'firstSpriteOnlyImage'));
|
|
1737
|
+
server.respondWith('GET', `${SECOND_SPRITE_URL}.json`, spriteJSON('sharedImage', 'secondSpriteOnlyImage'));
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
test('removes the images of the previous sprite that are absent from the new one', async () => {
|
|
1741
|
+
const style = await createLoadedStyle();
|
|
1742
|
+
|
|
1743
|
+
await loadSprite(style, FIRST_SPRITE_URL);
|
|
1744
|
+
expect(style.imageManager.listImages().sort()).toEqual(['firstSpriteOnlyImage', 'sharedImage']);
|
|
1745
|
+
|
|
1746
|
+
await loadSprite(style, SECOND_SPRITE_URL);
|
|
1747
|
+
expect(style.imageManager.listImages().sort()).toEqual(['secondSpriteOnlyImage', 'sharedImage']);
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
test('leaves no image behind when the sprite is unset after having been replaced', async () => {
|
|
1751
|
+
const style = await createLoadedStyle();
|
|
1752
|
+
await loadSprite(style, FIRST_SPRITE_URL);
|
|
1753
|
+
await loadSprite(style, SECOND_SPRITE_URL);
|
|
1754
|
+
|
|
1755
|
+
style.setSprite(undefined);
|
|
1756
|
+
|
|
1757
|
+
expect(style.imageManager.listImages()).toEqual([]);
|
|
1758
|
+
});
|
|
1759
|
+
|
|
1760
|
+
test('only marks the images shared with the previous sprite as updated, so that returning to a sprite does not accumulate per-frame patching work', async () => {
|
|
1761
|
+
const style = await createLoadedStyle();
|
|
1762
|
+
await loadSprite(style, FIRST_SPRITE_URL);
|
|
1763
|
+
const updateVersionAfterFirstLoad = style.imageManager.updateVersion;
|
|
1764
|
+
|
|
1765
|
+
await loadSprite(style, SECOND_SPRITE_URL);
|
|
1766
|
+
await loadSprite(style, FIRST_SPRITE_URL);
|
|
1767
|
+
|
|
1768
|
+
expect(style.imageManager.updateVersion - updateVersionAfterFirstLoad).toBe(2);
|
|
1769
|
+
});
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1707
1772
|
describe('Style.setGeoJSONSourceData', () => {
|
|
1708
1773
|
const geoJSON = {type: 'FeatureCollection', features: []} as GeoJSON.GeoJSON;
|
|
1709
1774
|
|
|
@@ -2297,7 +2362,7 @@ describe('Style.addLayer', () => {
|
|
|
2297
2362
|
type: 'background'
|
|
2298
2363
|
});
|
|
2299
2364
|
|
|
2300
|
-
style._layers.background.fire(new
|
|
2365
|
+
style._layers.background.fire(new ErrorEvent(new Error('test'), {mapLibre: true}));
|
|
2301
2366
|
|
|
2302
2367
|
const e = await errorPromise;
|
|
2303
2368
|
expect((e as any).layer).toEqual({id: 'background'});
|
|
@@ -2614,7 +2679,7 @@ describe('Style.removeLayer', () => {
|
|
|
2614
2679
|
// Bind a listener to prevent fallback Evented error reporting.
|
|
2615
2680
|
layer.on('error', () => {});
|
|
2616
2681
|
|
|
2617
|
-
layer.fire(new
|
|
2682
|
+
layer.fire(new ErrorEvent(new Error('test'), {mapLibre: true}));
|
|
2618
2683
|
|
|
2619
2684
|
expect(styleErrorListener).not.toHaveBeenCalled();
|
|
2620
2685
|
});
|