maplibre-gl 3.2.0 → 3.2.1
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/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 +223 -219
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +200 -144
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/package.json +25 -25
- package/src/geo/transform.test.ts +9 -16
- package/src/geo/transform.ts +10 -32
- package/src/render/draw_fill.test.ts +1 -1
- package/src/render/draw_symbol.test.ts +3 -3
- package/src/render/painter.ts +0 -1
- package/src/render/program.ts +0 -1
- package/src/render/terrain.test.ts +17 -0
- package/src/render/terrain.ts +31 -2
- 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/vector_tile_worker_source.test.ts +44 -74
- package/src/source/vector_tile_worker_source.ts +5 -16
- package/src/source/worker_tile.test.ts +143 -0
- package/src/source/worker_tile.ts +26 -7
- package/src/ui/camera.test.ts +12 -9
- package/src/ui/camera.ts +76 -94
- 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 +131 -44
- package/src/ui/map_events.test.ts +76 -0
- package/src/ui/marker.test.ts +1 -1
|
@@ -139,30 +139,19 @@ export class VectorTileWorkerSource implements WorkerSource {
|
|
|
139
139
|
* Implements {@link WorkerSource#reloadTile}.
|
|
140
140
|
*/
|
|
141
141
|
reloadTile(params: WorkerTileParameters, callback: WorkerTileCallback) {
|
|
142
|
-
const loaded = this.loaded
|
|
143
|
-
|
|
144
|
-
vtSource = this;
|
|
142
|
+
const loaded = this.loaded;
|
|
143
|
+
const uid = params.uid;
|
|
145
144
|
if (loaded && loaded[uid]) {
|
|
146
145
|
const workerTile = loaded[uid];
|
|
147
146
|
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
147
|
if (workerTile.status === 'parsing') {
|
|
159
|
-
workerTile.
|
|
148
|
+
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, callback);
|
|
160
149
|
} else if (workerTile.status === 'done') {
|
|
161
150
|
// if there was no vector tile data on the initial load, don't try and re-parse tile
|
|
162
151
|
if (workerTile.vectorTile) {
|
|
163
|
-
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor,
|
|
152
|
+
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, callback);
|
|
164
153
|
} else {
|
|
165
|
-
|
|
154
|
+
callback();
|
|
166
155
|
}
|
|
167
156
|
}
|
|
168
157
|
}
|
|
@@ -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/ui/camera.test.ts
CHANGED
|
@@ -1659,10 +1659,12 @@ describe('#flyTo', () => {
|
|
|
1659
1659
|
|
|
1660
1660
|
test('check elevation callbacks', done => {
|
|
1661
1661
|
const camera = createCamera();
|
|
1662
|
+
camera.terrain = {
|
|
1663
|
+
getElevationForLngLatZoom: () => 100,
|
|
1664
|
+
getMinTileElevationForLngLatZoom: () => 200
|
|
1665
|
+
};
|
|
1662
1666
|
camera.transform = {
|
|
1663
1667
|
elevation: 0,
|
|
1664
|
-
freezeElevation: false,
|
|
1665
|
-
getElevation: () => 100,
|
|
1666
1668
|
recalculateZoom: () => true
|
|
1667
1669
|
};
|
|
1668
1670
|
|
|
@@ -1670,15 +1672,15 @@ describe('#flyTo', () => {
|
|
|
1670
1672
|
// expect(camera._elevationCenter).toBe([10, 0]);
|
|
1671
1673
|
expect(camera._elevationStart).toBe(0);
|
|
1672
1674
|
expect(camera._elevationTarget).toBe(100);
|
|
1673
|
-
expect(camera.
|
|
1675
|
+
expect(camera._elevationFreeze).toBeTruthy();
|
|
1674
1676
|
|
|
1675
|
-
camera.
|
|
1677
|
+
camera.terrain.getElevationForLngLatZoom = () => 200;
|
|
1676
1678
|
camera._updateElevation(0.5);
|
|
1677
1679
|
expect(camera._elevationStart).toBe(-100);
|
|
1678
1680
|
expect(camera._elevationTarget).toBe(200);
|
|
1679
1681
|
|
|
1680
1682
|
camera._finalizeElevation();
|
|
1681
|
-
expect(camera.
|
|
1683
|
+
expect(camera._elevationFreeze).toBeFalsy();
|
|
1682
1684
|
|
|
1683
1685
|
done();
|
|
1684
1686
|
});
|
|
@@ -2015,9 +2017,10 @@ describe('queryTerrainElevation', () => {
|
|
|
2015
2017
|
test('should return the correct elevation', () => {
|
|
2016
2018
|
// Set up mock transform and terrain objects
|
|
2017
2019
|
const transform = new Transform(0, 22, 0, 60, true);
|
|
2018
|
-
transform.getElevation = jest.fn().mockReturnValue(200);
|
|
2019
2020
|
transform.elevation = 50;
|
|
2020
|
-
const terrain = {
|
|
2021
|
+
const terrain = {
|
|
2022
|
+
getElevationForLngLatZoom: jest.fn().mockReturnValue(200)
|
|
2023
|
+
} as any as Terrain;
|
|
2021
2024
|
|
|
2022
2025
|
// Set up camera with mock transform and terrain
|
|
2023
2026
|
camera.transform = transform;
|
|
@@ -2029,12 +2032,12 @@ describe('queryTerrainElevation', () => {
|
|
|
2029
2032
|
const result = camera.queryTerrainElevation(lngLatLike);
|
|
2030
2033
|
|
|
2031
2034
|
// Check that transform.getElevation was called with the correct arguments
|
|
2032
|
-
expect(
|
|
2035
|
+
expect(terrain.getElevationForLngLatZoom).toHaveBeenCalledWith(
|
|
2033
2036
|
expect.objectContaining({
|
|
2034
2037
|
lng: lngLatLike[0],
|
|
2035
2038
|
lat: lngLatLike[1],
|
|
2036
2039
|
}),
|
|
2037
|
-
|
|
2040
|
+
transform.tileZoom
|
|
2038
2041
|
);
|
|
2039
2042
|
|
|
2040
2043
|
// Check that the correct elevation value was returned
|