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.
Files changed (88) hide show
  1. package/build/generate-docs.ts +1 -1
  2. package/build/generate-struct-arrays.ts +3 -2
  3. package/dist/maplibre-gl-csp-worker.js +1 -1
  4. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  5. package/dist/maplibre-gl-csp.js +1 -1
  6. package/dist/maplibre-gl-csp.js.map +1 -1
  7. package/dist/maplibre-gl-dev.js +351 -281
  8. package/dist/maplibre-gl-dev.js.map +1 -1
  9. package/dist/maplibre-gl.d.ts +254 -181
  10. package/dist/maplibre-gl.js +4 -4
  11. package/dist/maplibre-gl.js.map +1 -1
  12. package/package.json +30 -30
  13. package/src/data/array_types.g.ts +32 -10
  14. package/src/data/bucket/circle_bucket.ts +1 -0
  15. package/src/data/bucket/line_bucket.ts +1 -0
  16. package/src/data/bucket/symbol_bucket.ts +1 -0
  17. package/src/data/feature_index.ts +1 -0
  18. package/src/data/program_configuration.ts +1 -0
  19. package/src/data/segment.ts +2 -0
  20. package/src/geo/transform.test.ts +9 -16
  21. package/src/geo/transform.ts +11 -32
  22. package/src/gl/context.ts +1 -0
  23. package/src/gl/framebuffer.ts +1 -0
  24. package/src/gl/index_buffer.ts +1 -0
  25. package/src/gl/render_pool.ts +2 -1
  26. package/src/gl/vertex_buffer.ts +1 -0
  27. package/src/render/draw_fill.test.ts +1 -1
  28. package/src/render/draw_symbol.test.ts +3 -3
  29. package/src/render/image_atlas.ts +1 -0
  30. package/src/render/line_atlas.ts +1 -0
  31. package/src/render/painter.ts +1 -1
  32. package/src/render/program.ts +1 -1
  33. package/src/render/render_to_texture.ts +31 -14
  34. package/src/render/terrain.test.ts +17 -0
  35. package/src/render/terrain.ts +34 -2
  36. package/src/render/texture.ts +1 -0
  37. package/src/render/uniform_binding.ts +2 -0
  38. package/src/render/vertex_array_object.ts +1 -0
  39. package/src/shaders/symbol_sdf.fragment.glsl +9 -3
  40. package/src/shaders/symbol_sdf.fragment.glsl.g.ts +1 -1
  41. package/src/source/canvas_source.ts +1 -3
  42. package/src/source/geojson_source.ts +1 -3
  43. package/src/source/image_source.ts +2 -4
  44. package/src/source/raster_dem_tile_source.test.ts +14 -0
  45. package/src/source/raster_dem_tile_source.ts +0 -11
  46. package/src/source/raster_tile_source.test.ts +13 -0
  47. package/src/source/source_cache.ts +1 -0
  48. package/src/source/source_state.ts +1 -0
  49. package/src/source/terrain_source_cache.ts +1 -0
  50. package/src/source/tile.ts +1 -0
  51. package/src/source/tile_cache.ts +1 -1
  52. package/src/source/tile_id.ts +1 -0
  53. package/src/source/vector_tile_worker_source.test.ts +116 -67
  54. package/src/source/vector_tile_worker_source.ts +30 -16
  55. package/src/source/worker_source.ts +1 -0
  56. package/src/source/worker_tile.test.ts +143 -0
  57. package/src/source/worker_tile.ts +26 -7
  58. package/src/style/evaluation_parameters.ts +1 -0
  59. package/src/style/properties.ts +14 -0
  60. package/src/style/style.ts +1 -0
  61. package/src/style/style_glyph.ts +1 -0
  62. package/src/symbol/collision_index.ts +1 -0
  63. package/src/symbol/grid_index.ts +1 -0
  64. package/src/ui/camera.test.ts +12 -9
  65. package/src/ui/camera.ts +77 -95
  66. package/src/ui/handler/box_zoom.ts +1 -3
  67. package/src/ui/handler/click_zoom.ts +1 -3
  68. package/src/ui/handler/keyboard.ts +1 -3
  69. package/src/ui/handler/scroll_zoom.ts +1 -3
  70. package/src/ui/handler/shim/dblclick_zoom.ts +1 -3
  71. package/src/ui/handler/shim/drag_pan.ts +1 -3
  72. package/src/ui/handler/shim/drag_rotate.ts +1 -3
  73. package/src/ui/handler/shim/two_fingers_touch.ts +1 -3
  74. package/src/ui/handler/transform-provider.ts +1 -0
  75. package/src/ui/handler/two_fingers_touch.ts +1 -3
  76. package/src/ui/handler_manager.ts +2 -2
  77. package/src/ui/hash.ts +1 -2
  78. package/src/ui/map.test.ts +17 -12
  79. package/src/ui/map.ts +132 -44
  80. package/src/ui/map_events.test.ts +76 -0
  81. package/src/ui/marker.test.ts +1 -1
  82. package/src/util/ajax.test.ts +33 -0
  83. package/src/util/ajax.ts +5 -0
  84. package/src/util/image.ts +1 -0
  85. package/src/util/image_request.ts +2 -2
  86. package/src/util/performance.ts +1 -2
  87. package/src/util/struct_array.ts +5 -1
  88. 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#reloadTile queues a reload when parsing is in progress', () => {
91
- const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
92
- const parse = jest.fn();
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
- source.loaded = {
95
- '0': {
96
- status: 'done',
97
- vectorTile: {},
98
- parse
99
- } as any as WorkerTile
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 callback1 = jest.fn();
103
- const callback2 = jest.fn();
104
- source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
105
- expect(parse).toHaveBeenCalledTimes(1);
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
- source.loaded[0].status = 'parsing';
108
- source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
109
- expect(parse).toHaveBeenCalledTimes(1);
137
+ return {
138
+ cancel: () => clearTimeout(res)
139
+ };
140
+ });
110
141
 
111
- parse.mock.calls[0][4]();
112
- expect(parse).toHaveBeenCalledTimes(2);
113
- expect(callback1).toHaveBeenCalledTimes(1);
114
- expect(callback2).toHaveBeenCalledTimes(0);
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
- parse.mock.calls[1][4]();
117
- expect(callback1).toHaveBeenCalledTimes(1);
118
- expect(callback2).toHaveBeenCalledTimes(1);
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 handles multiple pending reloads', () => {
122
- // https://github.com/mapbox/mapbox-gl-js/issues/6308
123
- const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
124
- const parse = jest.fn();
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
- source.loaded = {
127
- '0': {
128
- status: 'done',
129
- vectorTile: {},
130
- parse
131
- } as any as WorkerTile
132
- };
177
+ const layerIndex = new StyleLayerIndex([{
178
+ id: 'test',
179
+ source: 'source',
180
+ 'source-layer': 'test',
181
+ type: 'fill'
182
+ }]);
133
183
 
134
- const callback1 = jest.fn();
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
- source.loaded[0].status = 'parsing';
141
- source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
142
- expect(parse).toHaveBeenCalledTimes(1);
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
- parse.mock.calls[0][4]();
145
- expect(parse).toHaveBeenCalledTimes(2);
146
- expect(callback1).toHaveBeenCalledTimes(1);
147
- expect(callback2).toHaveBeenCalledTimes(0);
148
- expect(callback3).toHaveBeenCalledTimes(0);
149
-
150
- source.reloadTile({uid: 0} as any as WorkerTileParameters, callback3);
151
- expect(parse).toHaveBeenCalledTimes(2);
152
- expect(callback1).toHaveBeenCalledTimes(1);
153
- expect(callback2).toHaveBeenCalledTimes(0);
154
- expect(callback3).toHaveBeenCalledTimes(0);
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
- uid = params.uid,
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.reloadCallback = done;
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, done);
177
+ workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, callback);
164
178
  } else {
165
- done();
179
+ callback();
166
180
  }
167
181
  }
168
182
  }
@@ -41,6 +41,7 @@ export type WorkerDEMTileParameters = TileParameters & {
41
41
  };
42
42
 
43
43
  /**
44
+ * @internal
44
45
  * The worker tile's result type
45
46
  */
46
47
  export type WorkerTileResult = {
@@ -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
  }
@@ -11,6 +11,7 @@ export type CrossfadeParameters = {
11
11
  };
12
12
 
13
13
  /**
14
+ * @internal
14
15
  * A parameter that can be evaluated to a value
15
16
  */
16
17
  export class EvaluationParameters {
@@ -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