maplibre-gl 3.2.0 → 3.2.2
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/build/generate-docs.ts +1 -1
- package/build/generate-struct-arrays.ts +3 -2
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp-worker.js.map +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-csp.js.map +1 -1
- package/dist/maplibre-gl-dev.js +351 -281
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +254 -181
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/package.json +30 -30
- package/src/data/array_types.g.ts +32 -10
- package/src/data/bucket/circle_bucket.ts +1 -0
- package/src/data/bucket/line_bucket.ts +1 -0
- package/src/data/bucket/symbol_bucket.ts +1 -0
- package/src/data/feature_index.ts +1 -0
- package/src/data/program_configuration.ts +1 -0
- package/src/data/segment.ts +2 -0
- package/src/geo/transform.test.ts +9 -16
- package/src/geo/transform.ts +11 -32
- package/src/gl/context.ts +1 -0
- package/src/gl/framebuffer.ts +1 -0
- package/src/gl/index_buffer.ts +1 -0
- package/src/gl/render_pool.ts +2 -1
- package/src/gl/vertex_buffer.ts +1 -0
- package/src/render/draw_fill.test.ts +1 -1
- package/src/render/draw_symbol.test.ts +3 -3
- package/src/render/image_atlas.ts +1 -0
- package/src/render/line_atlas.ts +1 -0
- package/src/render/painter.ts +1 -1
- package/src/render/program.ts +1 -1
- package/src/render/render_to_texture.ts +31 -14
- package/src/render/terrain.test.ts +17 -0
- package/src/render/terrain.ts +34 -2
- package/src/render/texture.ts +1 -0
- package/src/render/uniform_binding.ts +2 -0
- package/src/render/vertex_array_object.ts +1 -0
- package/src/shaders/symbol_sdf.fragment.glsl +9 -3
- package/src/shaders/symbol_sdf.fragment.glsl.g.ts +1 -1
- package/src/source/canvas_source.ts +1 -3
- package/src/source/geojson_source.ts +1 -3
- package/src/source/image_source.ts +2 -4
- package/src/source/raster_dem_tile_source.test.ts +14 -0
- package/src/source/raster_dem_tile_source.ts +0 -11
- package/src/source/raster_tile_source.test.ts +13 -0
- package/src/source/source_cache.ts +1 -0
- package/src/source/source_state.ts +1 -0
- package/src/source/terrain_source_cache.ts +1 -0
- package/src/source/tile.ts +1 -0
- package/src/source/tile_cache.ts +1 -1
- package/src/source/tile_id.ts +1 -0
- package/src/source/vector_tile_worker_source.test.ts +116 -67
- package/src/source/vector_tile_worker_source.ts +30 -16
- package/src/source/worker_source.ts +1 -0
- package/src/source/worker_tile.test.ts +143 -0
- package/src/source/worker_tile.ts +26 -7
- package/src/style/evaluation_parameters.ts +1 -0
- package/src/style/properties.ts +14 -0
- package/src/style/style.ts +1 -0
- package/src/style/style_glyph.ts +1 -0
- package/src/symbol/collision_index.ts +1 -0
- package/src/symbol/grid_index.ts +1 -0
- package/src/ui/camera.test.ts +12 -9
- package/src/ui/camera.ts +77 -95
- package/src/ui/handler/box_zoom.ts +1 -3
- package/src/ui/handler/click_zoom.ts +1 -3
- package/src/ui/handler/keyboard.ts +1 -3
- package/src/ui/handler/scroll_zoom.ts +1 -3
- package/src/ui/handler/shim/dblclick_zoom.ts +1 -3
- package/src/ui/handler/shim/drag_pan.ts +1 -3
- package/src/ui/handler/shim/drag_rotate.ts +1 -3
- package/src/ui/handler/shim/two_fingers_touch.ts +1 -3
- package/src/ui/handler/transform-provider.ts +1 -0
- package/src/ui/handler/two_fingers_touch.ts +1 -3
- package/src/ui/handler_manager.ts +2 -2
- package/src/ui/hash.ts +1 -2
- package/src/ui/map.test.ts +17 -12
- package/src/ui/map.ts +132 -44
- package/src/ui/map_events.test.ts +76 -0
- package/src/ui/marker.test.ts +1 -1
- package/src/util/ajax.test.ts +33 -0
- package/src/util/ajax.ts +5 -0
- package/src/util/image.ts +1 -0
- package/src/util/image_request.ts +2 -2
- package/src/util/performance.ts +1 -2
- package/src/util/struct_array.ts +5 -1
- package/src/util/test/mock_fetch.ts +51 -0
|
@@ -6,7 +6,7 @@ import {VectorTileWorkerSource} from '../source/vector_tile_worker_source';
|
|
|
6
6
|
import {StyleLayerIndex} from '../style/style_layer_index';
|
|
7
7
|
import {fakeServer, FakeServer} from 'nise';
|
|
8
8
|
import {Actor} from '../util/actor';
|
|
9
|
-
import {TileParameters, WorkerTileParameters} from './worker_source';
|
|
9
|
+
import {TileParameters, WorkerTileParameters, WorkerTileResult} from './worker_source';
|
|
10
10
|
import {WorkerTile} from './worker_tile';
|
|
11
11
|
import {setPerformance} from '../util/test/util';
|
|
12
12
|
|
|
@@ -18,7 +18,6 @@ describe('vector tile worker source', () => {
|
|
|
18
18
|
global.fetch = null;
|
|
19
19
|
server = fakeServer.create();
|
|
20
20
|
setPerformance();
|
|
21
|
-
|
|
22
21
|
});
|
|
23
22
|
|
|
24
23
|
afterEach(() => {
|
|
@@ -87,83 +86,133 @@ describe('vector tile worker source', () => {
|
|
|
87
86
|
expect(callback).toHaveBeenCalledTimes(1);
|
|
88
87
|
});
|
|
89
88
|
|
|
90
|
-
test('VectorTileWorkerSource#
|
|
91
|
-
const
|
|
92
|
-
|
|
89
|
+
test('VectorTileWorkerSource#loadTile reparses tile if the reloadTile has been called during parsing', (done) => {
|
|
90
|
+
const rawTileData = new Uint8Array([]);
|
|
91
|
+
function loadVectorData(params, callback) {
|
|
92
|
+
return callback(null, {
|
|
93
|
+
vectorTile: {
|
|
94
|
+
layers: {
|
|
95
|
+
test: {
|
|
96
|
+
version: 2,
|
|
97
|
+
name: 'test',
|
|
98
|
+
extent: 8192,
|
|
99
|
+
length: 1,
|
|
100
|
+
feature: (featureIndex: number) => ({
|
|
101
|
+
extent: 8192,
|
|
102
|
+
type: 1,
|
|
103
|
+
id: featureIndex,
|
|
104
|
+
properties: {
|
|
105
|
+
name: 'test'
|
|
106
|
+
},
|
|
107
|
+
loadGeometry () {
|
|
108
|
+
return [[{x: 0, y: 0}]];
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} as any as vt.VectorTile,
|
|
114
|
+
rawData: rawTileData
|
|
115
|
+
});
|
|
116
|
+
}
|
|
93
117
|
|
|
94
|
-
|
|
95
|
-
'
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
const layerIndex = new StyleLayerIndex([{
|
|
119
|
+
id: 'test',
|
|
120
|
+
source: 'source',
|
|
121
|
+
'source-layer': 'test',
|
|
122
|
+
type: 'symbol',
|
|
123
|
+
layout: {
|
|
124
|
+
'icon-image': 'hello',
|
|
125
|
+
'text-font': ['StandardFont-Bold'],
|
|
126
|
+
'text-field': '{name}'
|
|
127
|
+
}
|
|
128
|
+
}]);
|
|
101
129
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
130
|
+
const send = jest.fn().mockImplementation((type: string, data: unknown, callback: Function) => {
|
|
131
|
+
const res = setTimeout(() => callback(null,
|
|
132
|
+
type === 'getImages' ?
|
|
133
|
+
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
|
|
134
|
+
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}}
|
|
135
|
+
));
|
|
106
136
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
137
|
+
return {
|
|
138
|
+
cancel: () => clearTimeout(res)
|
|
139
|
+
};
|
|
140
|
+
});
|
|
110
141
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
142
|
+
const actor = {
|
|
143
|
+
send
|
|
144
|
+
} as unknown as Actor;
|
|
145
|
+
const source = new VectorTileWorkerSource(actor, layerIndex, ['hello'], loadVectorData);
|
|
146
|
+
source.loadTile({
|
|
147
|
+
source: 'source',
|
|
148
|
+
uid: 0,
|
|
149
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
150
|
+
request: {url: 'http://localhost:2900/faketile.pbf'}
|
|
151
|
+
} as any as WorkerTileParameters, () => {
|
|
152
|
+
done.fail('should not be called');
|
|
153
|
+
});
|
|
115
154
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
source.reloadTile({
|
|
156
|
+
source: 'source',
|
|
157
|
+
uid: '0',
|
|
158
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
159
|
+
} as any as WorkerTileParameters, (err, res) => {
|
|
160
|
+
expect(err).toBeFalsy();
|
|
161
|
+
expect(res).toBeDefined();
|
|
162
|
+
expect(res.rawTileData).toBeDefined();
|
|
163
|
+
expect(res.rawTileData).toStrictEqual(rawTileData);
|
|
164
|
+
done();
|
|
165
|
+
});
|
|
119
166
|
});
|
|
120
167
|
|
|
121
|
-
test('VectorTileWorkerSource#reloadTile
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
168
|
+
test('VectorTileWorkerSource#loadTile reparses tile if reloadTile is called during reparsing', (done) => {
|
|
169
|
+
const rawTileData = new Uint8Array([]);
|
|
170
|
+
function loadVectorData(params, callback) {
|
|
171
|
+
return callback(null, {
|
|
172
|
+
vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
|
|
173
|
+
rawData: rawTileData
|
|
174
|
+
});
|
|
175
|
+
}
|
|
125
176
|
|
|
126
|
-
|
|
127
|
-
'
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
};
|
|
177
|
+
const layerIndex = new StyleLayerIndex([{
|
|
178
|
+
id: 'test',
|
|
179
|
+
source: 'source',
|
|
180
|
+
'source-layer': 'test',
|
|
181
|
+
type: 'fill'
|
|
182
|
+
}]);
|
|
133
183
|
|
|
134
|
-
const
|
|
135
|
-
const callback2 = jest.fn();
|
|
136
|
-
const callback3 = jest.fn();
|
|
137
|
-
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
|
|
138
|
-
expect(parse).toHaveBeenCalledTimes(1);
|
|
184
|
+
const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
|
|
139
185
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
186
|
+
const parseWorkerTileMock = jest
|
|
187
|
+
.spyOn(WorkerTile.prototype, 'parse')
|
|
188
|
+
.mockImplementation(function(data, layerIndex, availableImages, actor, callback) {
|
|
189
|
+
this.status = 'parsing';
|
|
190
|
+
window.setTimeout(() => callback(null, {} as WorkerTileResult), 10);
|
|
191
|
+
});
|
|
143
192
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
parse.mock.calls[1][4]();
|
|
157
|
-
expect(parse).toHaveBeenCalledTimes(3);
|
|
158
|
-
expect(callback1).toHaveBeenCalledTimes(1);
|
|
159
|
-
expect(callback2).toHaveBeenCalledTimes(1);
|
|
160
|
-
expect(callback3).toHaveBeenCalledTimes(0);
|
|
161
|
-
|
|
162
|
-
parse.mock.calls[2][4]();
|
|
163
|
-
expect(callback1).toHaveBeenCalledTimes(1);
|
|
164
|
-
expect(callback2).toHaveBeenCalledTimes(1);
|
|
165
|
-
expect(callback3).toHaveBeenCalledTimes(1);
|
|
193
|
+
let loadCallbackCalled = false;
|
|
194
|
+
source.loadTile({
|
|
195
|
+
source: 'source',
|
|
196
|
+
uid: 0,
|
|
197
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
198
|
+
request: {url: 'http://localhost:2900/faketile.pbf'}
|
|
199
|
+
} as any as WorkerTileParameters, (err, res) => {
|
|
200
|
+
expect(err).toBeFalsy();
|
|
201
|
+
expect(res).toBeDefined();
|
|
202
|
+
loadCallbackCalled = true;
|
|
203
|
+
});
|
|
166
204
|
|
|
205
|
+
source.reloadTile({
|
|
206
|
+
source: 'source',
|
|
207
|
+
uid: '0',
|
|
208
|
+
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
|
|
209
|
+
} as any as WorkerTileParameters, (err, res) => {
|
|
210
|
+
expect(err).toBeFalsy();
|
|
211
|
+
expect(res).toBeDefined();
|
|
212
|
+
expect(parseWorkerTileMock).toHaveBeenCalledTimes(2);
|
|
213
|
+
expect(loadCallbackCalled).toBeTruthy();
|
|
214
|
+
done();
|
|
215
|
+
});
|
|
167
216
|
});
|
|
168
217
|
|
|
169
218
|
test('VectorTileWorkerSource#reloadTile does not reparse tiles with no vectorTile data but does call callback', () => {
|
|
@@ -24,6 +24,12 @@ export type LoadVectorTileResult = {
|
|
|
24
24
|
resourceTiming?: Array<PerformanceResourceTiming>;
|
|
25
25
|
} & ExpiryData;
|
|
26
26
|
|
|
27
|
+
type FetchingState = {
|
|
28
|
+
rawTileData: ArrayBuffer;
|
|
29
|
+
cacheControl: ExpiryData;
|
|
30
|
+
resourceTiming: any;
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
/**
|
|
28
34
|
* The callback when finished loading vector data
|
|
29
35
|
*/
|
|
@@ -66,6 +72,7 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
66
72
|
layerIndex: StyleLayerIndex;
|
|
67
73
|
availableImages: Array<string>;
|
|
68
74
|
loadVectorData: LoadVectorData;
|
|
75
|
+
fetching: {[_: string]: FetchingState };
|
|
69
76
|
loading: {[_: string]: WorkerTile};
|
|
70
77
|
loaded: {[_: string]: WorkerTile};
|
|
71
78
|
|
|
@@ -80,6 +87,7 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
80
87
|
this.layerIndex = layerIndex;
|
|
81
88
|
this.availableImages = availableImages;
|
|
82
89
|
this.loadVectorData = loadVectorData || loadVectorTile;
|
|
90
|
+
this.fetching = {};
|
|
83
91
|
this.loading = {};
|
|
84
92
|
this.loaded = {};
|
|
85
93
|
}
|
|
@@ -124,6 +132,7 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
124
132
|
|
|
125
133
|
workerTile.vectorTile = response.vectorTile;
|
|
126
134
|
workerTile.parse(response.vectorTile, this.layerIndex, this.availableImages, this.actor, (err, result) => {
|
|
135
|
+
delete this.fetching[uid];
|
|
127
136
|
if (err || !result) return callback(err);
|
|
128
137
|
|
|
129
138
|
// Transferring a copy of rawTileData because the worker needs to retain its copy.
|
|
@@ -132,6 +141,8 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
132
141
|
|
|
133
142
|
this.loaded = this.loaded || {};
|
|
134
143
|
this.loaded[uid] = workerTile;
|
|
144
|
+
// keep the original fetching state so that reload tile can pick it up if the original parse is cancelled by reloads' parse
|
|
145
|
+
this.fetching[uid] = {rawTileData, cacheControl, resourceTiming};
|
|
135
146
|
}) as AbortVectorData;
|
|
136
147
|
}
|
|
137
148
|
|
|
@@ -139,30 +150,33 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
139
150
|
* Implements {@link WorkerSource#reloadTile}.
|
|
140
151
|
*/
|
|
141
152
|
reloadTile(params: WorkerTileParameters, callback: WorkerTileCallback) {
|
|
142
|
-
const loaded = this.loaded
|
|
143
|
-
|
|
144
|
-
vtSource = this;
|
|
153
|
+
const loaded = this.loaded;
|
|
154
|
+
const uid = params.uid;
|
|
145
155
|
if (loaded && loaded[uid]) {
|
|
146
156
|
const workerTile = loaded[uid];
|
|
147
157
|
workerTile.showCollisionBoxes = params.showCollisionBoxes;
|
|
148
|
-
|
|
149
|
-
const done = (err?: Error, data?: any) => {
|
|
150
|
-
const reloadCallback = workerTile.reloadCallback;
|
|
151
|
-
if (reloadCallback) {
|
|
152
|
-
delete workerTile.reloadCallback;
|
|
153
|
-
workerTile.parse(workerTile.vectorTile, vtSource.layerIndex, this.availableImages, vtSource.actor, reloadCallback);
|
|
154
|
-
}
|
|
155
|
-
callback(err, data);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
158
|
if (workerTile.status === 'parsing') {
|
|
159
|
-
workerTile.
|
|
159
|
+
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, (err, result) => {
|
|
160
|
+
if (err || !result) return callback(err, result);
|
|
161
|
+
|
|
162
|
+
// if we have cancelled the original parse, make sure to pass the rawTileData from the original fetch
|
|
163
|
+
let parseResult;
|
|
164
|
+
if (this.fetching[uid]) {
|
|
165
|
+
const {rawTileData, cacheControl, resourceTiming} = this.fetching[uid];
|
|
166
|
+
delete this.fetching[uid];
|
|
167
|
+
parseResult = extend({rawTileData: rawTileData.slice(0)}, result, cacheControl, resourceTiming);
|
|
168
|
+
} else {
|
|
169
|
+
parseResult = result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
callback(null, parseResult);
|
|
173
|
+
});
|
|
160
174
|
} else if (workerTile.status === 'done') {
|
|
161
175
|
// if there was no vector tile data on the initial load, don't try and re-parse tile
|
|
162
176
|
if (workerTile.vectorTile) {
|
|
163
|
-
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor,
|
|
177
|
+
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, callback);
|
|
164
178
|
} else {
|
|
165
|
-
|
|
179
|
+
callback();
|
|
166
180
|
}
|
|
167
181
|
}
|
|
168
182
|
}
|
|
@@ -99,4 +99,147 @@ describe('worker tile', () => {
|
|
|
99
99
|
done();
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
|
|
103
|
+
test('WorkerTile#parse would request all types of dependencies', done => {
|
|
104
|
+
const tile = createWorkerTile();
|
|
105
|
+
const layerIndex = new StyleLayerIndex([{
|
|
106
|
+
id: '1',
|
|
107
|
+
type: 'fill',
|
|
108
|
+
source: 'source',
|
|
109
|
+
'source-layer': 'test',
|
|
110
|
+
paint: {
|
|
111
|
+
'fill-pattern': 'hello'
|
|
112
|
+
}
|
|
113
|
+
}, {
|
|
114
|
+
id: 'test',
|
|
115
|
+
source: 'source',
|
|
116
|
+
'source-layer': 'test',
|
|
117
|
+
type: 'symbol',
|
|
118
|
+
layout: {
|
|
119
|
+
'icon-image': 'hello',
|
|
120
|
+
'text-font': ['StandardFont-Bold'],
|
|
121
|
+
'text-field': '{name}'
|
|
122
|
+
}
|
|
123
|
+
}]);
|
|
124
|
+
|
|
125
|
+
const data = {
|
|
126
|
+
layers: {
|
|
127
|
+
test: {
|
|
128
|
+
version: 2,
|
|
129
|
+
name: 'test',
|
|
130
|
+
extent: 8192,
|
|
131
|
+
length: 1,
|
|
132
|
+
feature: (featureIndex: number) => ({
|
|
133
|
+
extent: 8192,
|
|
134
|
+
type: 1,
|
|
135
|
+
id: featureIndex,
|
|
136
|
+
properties: {
|
|
137
|
+
name: 'test'
|
|
138
|
+
},
|
|
139
|
+
loadGeometry () {
|
|
140
|
+
return [[{x: 0, y: 0}]];
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} as any as VectorTile;
|
|
146
|
+
|
|
147
|
+
const send = jest.fn().mockImplementation((type: string, data: unknown, callback: Function) => {
|
|
148
|
+
setTimeout(() => callback(null,
|
|
149
|
+
type === 'getImages' ?
|
|
150
|
+
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
|
|
151
|
+
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}}
|
|
152
|
+
));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const actorMock = {
|
|
156
|
+
send
|
|
157
|
+
} as unknown as Actor;
|
|
158
|
+
tile.parse(data, layerIndex, ['hello'], actorMock, (err, result) => {
|
|
159
|
+
expect(err).toBeFalsy();
|
|
160
|
+
expect(result).toBeDefined();
|
|
161
|
+
expect(send).toHaveBeenCalledTimes(3);
|
|
162
|
+
expect(send).toHaveBeenCalledWith('getImages', expect.objectContaining({'icons': ['hello'], 'type': 'icons'}), expect.any(Function));
|
|
163
|
+
expect(send).toHaveBeenCalledWith('getImages', expect.objectContaining({'icons': ['hello'], 'type': 'patterns'}), expect.any(Function));
|
|
164
|
+
expect(send).toHaveBeenCalledWith('getGlyphs', expect.objectContaining({'source': 'source', 'type': 'glyphs', 'stacks': {'StandardFont-Bold': [101, 115, 116]}}), expect.any(Function));
|
|
165
|
+
done();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('WorkerTile#parse would cancel and only event once on repeated reparsing', done => {
|
|
170
|
+
const tile = createWorkerTile();
|
|
171
|
+
const layerIndex = new StyleLayerIndex([{
|
|
172
|
+
id: '1',
|
|
173
|
+
type: 'fill',
|
|
174
|
+
source: 'source',
|
|
175
|
+
'source-layer': 'test',
|
|
176
|
+
paint: {
|
|
177
|
+
'fill-pattern': 'hello'
|
|
178
|
+
}
|
|
179
|
+
}, {
|
|
180
|
+
id: 'test',
|
|
181
|
+
source: 'source',
|
|
182
|
+
'source-layer': 'test',
|
|
183
|
+
type: 'symbol',
|
|
184
|
+
layout: {
|
|
185
|
+
'icon-image': 'hello',
|
|
186
|
+
'text-font': ['StandardFont-Bold'],
|
|
187
|
+
'text-field': '{name}'
|
|
188
|
+
}
|
|
189
|
+
}]);
|
|
190
|
+
|
|
191
|
+
const data = {
|
|
192
|
+
layers: {
|
|
193
|
+
test: {
|
|
194
|
+
version: 2,
|
|
195
|
+
name: 'test',
|
|
196
|
+
extent: 8192,
|
|
197
|
+
length: 1,
|
|
198
|
+
feature: (featureIndex: number) => ({
|
|
199
|
+
extent: 8192,
|
|
200
|
+
type: 1,
|
|
201
|
+
id: featureIndex,
|
|
202
|
+
properties: {
|
|
203
|
+
name: 'test'
|
|
204
|
+
},
|
|
205
|
+
loadGeometry () {
|
|
206
|
+
return [[{x: 0, y: 0}]];
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} as any as VectorTile;
|
|
212
|
+
|
|
213
|
+
let cancelCount = 0;
|
|
214
|
+
const send = jest.fn().mockImplementation((type: string, data: unknown, callback: Function) => {
|
|
215
|
+
const res = setTimeout(() => callback(null,
|
|
216
|
+
type === 'getImages' ?
|
|
217
|
+
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
|
|
218
|
+
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}}
|
|
219
|
+
));
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
cancel: () => {
|
|
223
|
+
cancelCount += 1;
|
|
224
|
+
clearTimeout(res);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const actorMock = {
|
|
230
|
+
send
|
|
231
|
+
} as unknown as Actor;
|
|
232
|
+
tile.parse(data, layerIndex, ['hello'], actorMock, () => done.fail('should not be called'));
|
|
233
|
+
tile.parse(data, layerIndex, ['hello'], actorMock, () => done.fail('should not be called'));
|
|
234
|
+
tile.parse(data, layerIndex, ['hello'], actorMock, (err, result) => {
|
|
235
|
+
expect(err).toBeFalsy();
|
|
236
|
+
expect(result).toBeDefined();
|
|
237
|
+
expect(cancelCount).toBe(6);
|
|
238
|
+
expect(send).toHaveBeenCalledTimes(9);
|
|
239
|
+
expect(send).toHaveBeenCalledWith('getImages', expect.objectContaining({'icons': ['hello'], 'type': 'icons'}), expect.any(Function));
|
|
240
|
+
expect(send).toHaveBeenCalledWith('getImages', expect.objectContaining({'icons': ['hello'], 'type': 'patterns'}), expect.any(Function));
|
|
241
|
+
expect(send).toHaveBeenCalledWith('getGlyphs', expect.objectContaining({'source': 'source', 'type': 'glyphs', 'stacks': {'StandardFont-Bold': [101, 115, 116]}}), expect.any(Function));
|
|
242
|
+
done();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
102
245
|
});
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
} from '../source/worker_source';
|
|
25
25
|
import type {PromoteIdSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
26
26
|
import type {VectorTile} from '@mapbox/vector-tile';
|
|
27
|
+
import {Cancelable} from '../types/cancelable';
|
|
27
28
|
|
|
28
29
|
export class WorkerTile {
|
|
29
30
|
tileID: OverscaledTileID;
|
|
@@ -43,8 +44,9 @@ export class WorkerTile {
|
|
|
43
44
|
collisionBoxArray: CollisionBoxArray;
|
|
44
45
|
|
|
45
46
|
abort: (() => void);
|
|
46
|
-
reloadCallback: WorkerTileCallback;
|
|
47
47
|
vectorTile: VectorTile;
|
|
48
|
+
inFlightDependencies: Cancelable[];
|
|
49
|
+
dependencySentinel: number;
|
|
48
50
|
|
|
49
51
|
constructor(params: WorkerTileParameters) {
|
|
50
52
|
this.tileID = new OverscaledTileID(params.tileID.overscaledZ, params.tileID.wrap, params.tileID.canonical.z, params.tileID.canonical.x, params.tileID.canonical.y);
|
|
@@ -58,6 +60,8 @@ export class WorkerTile {
|
|
|
58
60
|
this.collectResourceTiming = !!params.collectResourceTiming;
|
|
59
61
|
this.returnDependencies = !!params.returnDependencies;
|
|
60
62
|
this.promoteId = params.promoteId;
|
|
63
|
+
this.inFlightDependencies = [];
|
|
64
|
+
this.dependencySentinel = -1;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
parse(data: VectorTile, layerIndex: StyleLayerIndex, availableImages: Array<string>, actor: Actor, callback: WorkerTileCallback) {
|
|
@@ -138,40 +142,55 @@ export class WorkerTile {
|
|
|
138
142
|
let patternMap: {[_: string]: StyleImage};
|
|
139
143
|
|
|
140
144
|
const stacks = mapObject(options.glyphDependencies, (glyphs) => Object.keys(glyphs).map(Number));
|
|
145
|
+
|
|
146
|
+
this.inFlightDependencies.forEach((request) => request?.cancel());
|
|
147
|
+
this.inFlightDependencies = [];
|
|
148
|
+
|
|
149
|
+
// cancelling seems to be not sufficient, we seems to still manage to get a callback hit, so use a sentinel to drop stale results
|
|
150
|
+
const dependencySentinel = ++this.dependencySentinel;
|
|
141
151
|
if (Object.keys(stacks).length) {
|
|
142
|
-
actor.send('getGlyphs', {uid: this.uid, stacks, source: this.source, tileID: this.tileID, type: 'glyphs'}, (err, result) => {
|
|
152
|
+
this.inFlightDependencies.push(actor.send('getGlyphs', {uid: this.uid, stacks, source: this.source, tileID: this.tileID, type: 'glyphs'}, (err, result) => {
|
|
153
|
+
if (dependencySentinel !== this.dependencySentinel) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
143
156
|
if (!error) {
|
|
144
157
|
error = err;
|
|
145
158
|
glyphMap = result;
|
|
146
159
|
maybePrepare.call(this);
|
|
147
160
|
}
|
|
148
|
-
});
|
|
161
|
+
}));
|
|
149
162
|
} else {
|
|
150
163
|
glyphMap = {};
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
const icons = Object.keys(options.iconDependencies);
|
|
154
167
|
if (icons.length) {
|
|
155
|
-
actor.send('getImages', {icons, source: this.source, tileID: this.tileID, type: 'icons'}, (err, result) => {
|
|
168
|
+
this.inFlightDependencies.push(actor.send('getImages', {icons, source: this.source, tileID: this.tileID, type: 'icons'}, (err, result) => {
|
|
169
|
+
if (dependencySentinel !== this.dependencySentinel) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
156
172
|
if (!error) {
|
|
157
173
|
error = err;
|
|
158
174
|
iconMap = result;
|
|
159
175
|
maybePrepare.call(this);
|
|
160
176
|
}
|
|
161
|
-
});
|
|
177
|
+
}));
|
|
162
178
|
} else {
|
|
163
179
|
iconMap = {};
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
const patterns = Object.keys(options.patternDependencies);
|
|
167
183
|
if (patterns.length) {
|
|
168
|
-
actor.send('getImages', {icons: patterns, source: this.source, tileID: this.tileID, type: 'patterns'}, (err, result) => {
|
|
184
|
+
this.inFlightDependencies.push(actor.send('getImages', {icons: patterns, source: this.source, tileID: this.tileID, type: 'patterns'}, (err, result) => {
|
|
185
|
+
if (dependencySentinel !== this.dependencySentinel) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
169
188
|
if (!error) {
|
|
170
189
|
error = err;
|
|
171
190
|
patternMap = result;
|
|
172
191
|
maybePrepare.call(this);
|
|
173
192
|
}
|
|
174
|
-
});
|
|
193
|
+
}));
|
|
175
194
|
} else {
|
|
176
195
|
patternMap = {};
|
|
177
196
|
}
|
package/src/style/properties.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type CrossFaded<T> = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
+
* @internal
|
|
25
26
|
* Implementations of the `Property` interface:
|
|
26
27
|
*
|
|
27
28
|
* * Hold metadata about a property that's independent of any specific value: stuff like the type of the value,
|
|
@@ -49,6 +50,7 @@ export interface Property<T, R> {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|
|
53
|
+
* @internal
|
|
52
54
|
* `PropertyValue` represents the value part of a property key-value unit. It's used to represent both
|
|
53
55
|
* paint and layout property values, and regardless of whether or not their property supports data-driven
|
|
54
56
|
* expressions.
|
|
@@ -95,6 +97,7 @@ export type TransitionParameters = {
|
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
/**
|
|
100
|
+
* @internal
|
|
98
101
|
* Paint properties are _transitionable_: they can change in a fluid manner, interpolating or cross-fading between
|
|
99
102
|
* old and new value. The duration of the transition, and the delay before it begins, is configurable.
|
|
100
103
|
*
|
|
@@ -125,6 +128,7 @@ class TransitionablePropertyValue<T, R> {
|
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/**
|
|
131
|
+
* @internal
|
|
128
132
|
* `Transitionable` stores a map of all (property name, `TransitionablePropertyValue`) pairs for paint properties of a
|
|
129
133
|
* given layer type. It can calculate the `TransitioningPropertyValue`s for all of them at once, producing a
|
|
130
134
|
* `Transitioning` instance for the same set of properties.
|
|
@@ -196,6 +200,7 @@ export class Transitionable<Props> {
|
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
/**
|
|
203
|
+
* @internal
|
|
199
204
|
* `TransitioningPropertyValue` implements the first of two intermediate steps in the evaluation chain of a paint
|
|
200
205
|
* property value. In this step, transitions between old and new values are handled: as long as the transition is in
|
|
201
206
|
* progress, `TransitioningPropertyValue` maintains a reference to the prior value, and interpolates between it and
|
|
@@ -256,6 +261,7 @@ class TransitioningPropertyValue<T, R> {
|
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
/**
|
|
264
|
+
* @internal
|
|
259
265
|
* `Transitioning` stores a map of all (property name, `TransitioningPropertyValue`) pairs for paint properties of a
|
|
260
266
|
* given layer type. It can calculate the possibly-evaluated values for all of them at once, producing a
|
|
261
267
|
* `PossiblyEvaluated` instance for the same set of properties.
|
|
@@ -370,6 +376,7 @@ type PossiblyEvaluatedValue<T> = {
|
|
|
370
376
|
} | SourceExpression | CompositeExpression;
|
|
371
377
|
|
|
372
378
|
/**
|
|
379
|
+
* @internal
|
|
373
380
|
* `PossiblyEvaluatedPropertyValue` is used for data-driven paint and layout property values. It holds a
|
|
374
381
|
* `PossiblyEvaluatedValue` and the `GlobalProperties` that were used to generate it. You're not allowed to supply
|
|
375
382
|
* a different set of `GlobalProperties` when performing the final evaluation because they would be ignored in the
|
|
@@ -409,6 +416,7 @@ export class PossiblyEvaluatedPropertyValue<T> {
|
|
|
409
416
|
}
|
|
410
417
|
|
|
411
418
|
/**
|
|
419
|
+
* @internal
|
|
412
420
|
* `PossiblyEvaluated` stores a map of all (property name, `R`) pairs for paint or layout properties of a
|
|
413
421
|
* given layer type.
|
|
414
422
|
*/
|
|
@@ -427,6 +435,7 @@ export class PossiblyEvaluated<Props, PossibleEvaluatedProps> {
|
|
|
427
435
|
}
|
|
428
436
|
|
|
429
437
|
/**
|
|
438
|
+
* @internal
|
|
430
439
|
* An implementation of `Property` for properties that do not permit data-driven (source or composite) expressions.
|
|
431
440
|
* This restriction allows us to declare statically that the result of possibly evaluating this kind of property
|
|
432
441
|
* is in fact always the scalar type `T`, and can be used without further evaluating the value on a per-feature basis.
|
|
@@ -455,6 +464,7 @@ export class DataConstantProperty<T> implements Property<T, T> {
|
|
|
455
464
|
}
|
|
456
465
|
|
|
457
466
|
/**
|
|
467
|
+
* @internal
|
|
458
468
|
* An implementation of `Property` for properties that permit data-driven (source or composite) expressions.
|
|
459
469
|
* The result of possibly evaluating this kind of property is `PossiblyEvaluatedPropertyValue<T>`; obtaining
|
|
460
470
|
* a scalar value `T` requires further evaluation on a per-feature basis.
|
|
@@ -529,6 +539,7 @@ export class DataDrivenProperty<T> implements Property<T, PossiblyEvaluatedPrope
|
|
|
529
539
|
}
|
|
530
540
|
|
|
531
541
|
/**
|
|
542
|
+
* @internal
|
|
532
543
|
* An implementation of `Property` for data driven `line-pattern` which are transitioned by cross-fading
|
|
533
544
|
* rather than interpolation.
|
|
534
545
|
*/
|
|
@@ -594,6 +605,7 @@ export class CrossFadedDataDrivenProperty<T> extends DataDrivenProperty<CrossFad
|
|
|
594
605
|
}
|
|
595
606
|
}
|
|
596
607
|
/**
|
|
608
|
+
* @internal
|
|
597
609
|
* An implementation of `Property` for `*-pattern` and `line-dasharray`, which are transitioned by cross-fading
|
|
598
610
|
* rather than interpolation.
|
|
599
611
|
*/
|
|
@@ -635,6 +647,7 @@ export class CrossFadedProperty<T> implements Property<T, CrossFaded<T>> {
|
|
|
635
647
|
}
|
|
636
648
|
|
|
637
649
|
/**
|
|
650
|
+
* @internal
|
|
638
651
|
* An implementation of `Property` for `heatmap-color` and `line-gradient`. Interpolation is a no-op, and
|
|
639
652
|
* evaluation returns a boolean value in order to indicate its presence, but the real
|
|
640
653
|
* evaluation happens in StyleLayer classes.
|
|
@@ -660,6 +673,7 @@ export class ColorRampProperty implements Property<Color, boolean> {
|
|
|
660
673
|
}
|
|
661
674
|
|
|
662
675
|
/**
|
|
676
|
+
* @internal
|
|
663
677
|
* `Properties` holds objects containing default values for the layout or paint property set of a given
|
|
664
678
|
* layer type. These objects are immutable, and they are used as the prototypes for the `_values` members of
|
|
665
679
|
* `Transitionable`, `Transitioning`, `Layout`, and `PossiblyEvaluated`. This allows these classes to avoid
|