maplibre-gl 3.3.0 → 3.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/LICENSE.txt +1 -1
- package/README.md +3 -2
- package/build/generate-dist-package.js +7 -2
- package/build/generate-typings.ts +1 -1
- package/dist/LICENSE.txt +116 -0
- 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 +243 -100
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +28 -15
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +48 -47
- package/src/data/dem_data.test.ts +120 -165
- package/src/data/dem_data.ts +38 -18
- package/src/render/program.ts +15 -0
- package/src/source/image_source.test.ts +17 -24
- package/src/source/raster_dem_tile_source.ts +15 -2
- package/src/source/raster_dem_tile_worker_source.ts +2 -2
- package/src/source/raster_tile_source.test.ts +1 -1
- package/src/source/raster_tile_source.ts +1 -1
- package/src/source/terrain_source_cache.test.ts +1 -1
- package/src/source/vector_tile_source.test.ts +1 -1
- package/src/source/vector_tile_source.ts +0 -1
- package/src/source/vector_tile_worker_source.test.ts +45 -1
- package/src/source/vector_tile_worker_source.ts +19 -6
- package/src/source/video_source.ts +4 -0
- package/src/source/worker_source.ts +6 -2
- package/src/style/load_glyph_range.test.ts +6 -8
- package/src/style/load_sprite.test.ts +48 -71
- package/src/style/style.test.ts +19 -49
- package/src/style/style.ts +14 -8
- package/src/style/style_layer/line_style_layer.test.ts +50 -0
- package/src/style/style_layer/line_style_layer.ts +8 -4
- package/src/style/style_layer/variable_text_anchor.ts +1 -1
- package/src/ui/control/navigation_control.ts +0 -1
- package/src/ui/handler/scroll_zoom.ts +6 -0
- package/src/ui/handler_manager.ts +2 -1
- package/src/ui/map.test.ts +37 -8
- package/src/ui/map.ts +15 -13
- package/src/ui/marker.test.ts +25 -0
- package/src/ui/marker.ts +9 -2
- package/src/ui/popup.ts +1 -1
- package/src/util/ajax.test.ts +1 -1
- package/src/util/image_request.test.ts +1 -1
- package/src/util/test/util.ts +12 -0
- package/src/util/throttle.ts +7 -3
package/src/style/style.test.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '../source/rtl_text_plugin';
|
|
14
14
|
import {browser} from '../util/browser';
|
|
15
15
|
import {OverscaledTileID} from '../source/tile_id';
|
|
16
|
-
import {
|
|
16
|
+
import {fakeServer, type FakeServer} from 'nise';
|
|
17
17
|
|
|
18
18
|
import {EvaluationParameters} from './evaluation_parameters';
|
|
19
19
|
import {LayerSpecification, GeoJSONSourceSpecification, FilterSpecification, SourceSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
@@ -78,21 +78,17 @@ function createStyle(map = getStubMap()) {
|
|
|
78
78
|
return style;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
let
|
|
82
|
-
let sinonFakeServer;
|
|
81
|
+
let server: FakeServer;
|
|
83
82
|
let mockConsoleError;
|
|
84
83
|
|
|
85
84
|
beforeEach(() => {
|
|
86
85
|
global.fetch = null;
|
|
87
|
-
|
|
88
|
-
sinonFakeXMLServer = fakeXhr.useFakeXMLHttpRequest();
|
|
89
|
-
|
|
86
|
+
server = fakeServer.create();
|
|
90
87
|
mockConsoleError = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
91
88
|
});
|
|
92
89
|
|
|
93
90
|
afterEach(() => {
|
|
94
|
-
|
|
95
|
-
sinonFakeServer.restore();
|
|
91
|
+
server.restore();
|
|
96
92
|
mockConsoleError.mockRestore();
|
|
97
93
|
});
|
|
98
94
|
|
|
@@ -115,12 +111,12 @@ describe('Style', () => {
|
|
|
115
111
|
|
|
116
112
|
test('loads plugin immediately if already registered', done => {
|
|
117
113
|
clearRTLTextPlugin();
|
|
118
|
-
|
|
114
|
+
server.respondWith('/plugin.js', 'doesn\'t matter');
|
|
119
115
|
setRTLTextPlugin('/plugin.js', (error) => {
|
|
120
116
|
expect(error).toMatch(/Cannot set the state of the rtl-text-plugin when not in the web-worker context/);
|
|
121
117
|
done();
|
|
122
118
|
});
|
|
123
|
-
|
|
119
|
+
server.respond();
|
|
124
120
|
new Style(getStubMap());
|
|
125
121
|
});
|
|
126
122
|
|
|
@@ -152,7 +148,7 @@ describe('Style', () => {
|
|
|
152
148
|
jest.spyOn(style.sourceCaches['vector'], 'reload');
|
|
153
149
|
|
|
154
150
|
clearRTLTextPlugin();
|
|
155
|
-
|
|
151
|
+
server.respondWith('/plugin.js', 'doesn\'t matter');
|
|
156
152
|
const _broadcast = style.dispatcher.broadcast;
|
|
157
153
|
style.dispatcher.broadcast = function (type, state, callback) {
|
|
158
154
|
if (type === 'syncRTLPluginState') {
|
|
@@ -171,7 +167,7 @@ describe('Style', () => {
|
|
|
171
167
|
done();
|
|
172
168
|
}, 0);
|
|
173
169
|
});
|
|
174
|
-
|
|
170
|
+
server.respond();
|
|
175
171
|
});
|
|
176
172
|
});
|
|
177
173
|
});
|
|
@@ -211,15 +207,15 @@ describe('Style#loadURL', () => {
|
|
|
211
207
|
});
|
|
212
208
|
|
|
213
209
|
style.loadURL('style.json');
|
|
214
|
-
|
|
215
|
-
|
|
210
|
+
server.respondWith(JSON.stringify(createStyleJSON({version: 'invalid'})));
|
|
211
|
+
server.respond();
|
|
216
212
|
});
|
|
217
213
|
|
|
218
214
|
test('cancels pending requests if removed', () => {
|
|
219
215
|
const style = new Style(getStubMap());
|
|
220
216
|
style.loadURL('style.json');
|
|
221
217
|
style._remove();
|
|
222
|
-
expect(
|
|
218
|
+
expect((server.lastRequest as any).aborted).toBe(true);
|
|
223
219
|
});
|
|
224
220
|
});
|
|
225
221
|
|
|
@@ -266,21 +262,8 @@ describe('Style#loadJSON', () => {
|
|
|
266
262
|
// stub Image so we can invoke 'onload'
|
|
267
263
|
// https://github.com/jsdom/jsdom/commit/58a7028d0d5b6aacc5b435daee9fd8f9eacbb14c
|
|
268
264
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const requests = [];
|
|
272
|
-
sinonFakeXMLServer.onCreate = req => { requests.push(req); };
|
|
273
|
-
const respond = () => {
|
|
274
|
-
let req = requests.find(req => req.url === 'http://example.com/sprite.png');
|
|
275
|
-
req.setStatus(200);
|
|
276
|
-
req.response = new ArrayBuffer(8);
|
|
277
|
-
req.onload();
|
|
278
|
-
|
|
279
|
-
req = requests.find(req => req.url === 'http://example.com/sprite.json');
|
|
280
|
-
req.setStatus(200);
|
|
281
|
-
req.response = '{}';
|
|
282
|
-
req.onload();
|
|
283
|
-
};
|
|
265
|
+
server.respondWith('GET', 'http://example.com/sprite.png', new ArrayBuffer(8));
|
|
266
|
+
server.respondWith('GET', 'http://example.com/sprite.json', '{}');
|
|
284
267
|
|
|
285
268
|
const style = new Style(getStubMap());
|
|
286
269
|
|
|
@@ -303,7 +286,7 @@ describe('Style#loadJSON', () => {
|
|
|
303
286
|
done();
|
|
304
287
|
});
|
|
305
288
|
|
|
306
|
-
respond();
|
|
289
|
+
server.respond();
|
|
307
290
|
});
|
|
308
291
|
});
|
|
309
292
|
|
|
@@ -315,21 +298,8 @@ describe('Style#loadJSON', () => {
|
|
|
315
298
|
// stub Image so we can invoke 'onload'
|
|
316
299
|
// https://github.com/jsdom/jsdom/commit/58a7028d0d5b6aacc5b435daee9fd8f9eacbb14c
|
|
317
300
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const requests = [];
|
|
321
|
-
sinonFakeXMLServer.onCreate = req => { requests.push(req); };
|
|
322
|
-
const respond = () => {
|
|
323
|
-
let req = requests.find(req => req.url === 'http://example.com/sprite.png');
|
|
324
|
-
req.setStatus(200);
|
|
325
|
-
req.response = new ArrayBuffer(8);
|
|
326
|
-
req.onload();
|
|
327
|
-
|
|
328
|
-
req = requests.find(req => req.url === 'http://example.com/sprite.json');
|
|
329
|
-
req.setStatus(200);
|
|
330
|
-
req.response = '{"image1": {"width": 1, "height": 1, "x": 0, "y": 0, "pixelRatio": 1.0}}';
|
|
331
|
-
req.onload();
|
|
332
|
-
};
|
|
301
|
+
server.respondWith('GET', 'http://example.com/sprite.png', new ArrayBuffer(8));
|
|
302
|
+
server.respondWith('GET', 'http://example.com/sprite.json', '{"image1": {"width": 1, "height": 1, "x": 0, "y": 0, "pixelRatio": 1.0}}');
|
|
333
303
|
|
|
334
304
|
const style = new Style(getStubMap());
|
|
335
305
|
|
|
@@ -357,7 +327,7 @@ describe('Style#loadJSON', () => {
|
|
|
357
327
|
});
|
|
358
328
|
});
|
|
359
329
|
|
|
360
|
-
respond();
|
|
330
|
+
server.respond();
|
|
361
331
|
});
|
|
362
332
|
});
|
|
363
333
|
|
|
@@ -751,7 +721,7 @@ describe('Style#setState', () => {
|
|
|
751
721
|
});
|
|
752
722
|
|
|
753
723
|
test('Issue #3893: compare new source options against originally provided options rather than normalized properties', done => {
|
|
754
|
-
|
|
724
|
+
server.respondWith('/tilejson.json', JSON.stringify({
|
|
755
725
|
tiles: ['http://tiles.server']
|
|
756
726
|
}));
|
|
757
727
|
const initial = createStyleJSON();
|
|
@@ -767,7 +737,7 @@ describe('Style#setState', () => {
|
|
|
767
737
|
style.setState(initial);
|
|
768
738
|
done();
|
|
769
739
|
});
|
|
770
|
-
|
|
740
|
+
server.respond();
|
|
771
741
|
});
|
|
772
742
|
|
|
773
743
|
test('return true if there is a change', done => {
|
package/src/style/style.ts
CHANGED
|
@@ -191,6 +191,12 @@ export type StyleSwapOptions = {
|
|
|
191
191
|
transformStyle?: TransformStyleFunction;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Specifies a layer to be added to a {@link Style}. In addition to a standard {@link LayerSpecification}
|
|
196
|
+
* or a {@link CustomLayerInterface}, a {@link LayerSpecification} with an embedded {@link SourceSpecification} can also be provided.
|
|
197
|
+
*/
|
|
198
|
+
export type AddLayerObject = LayerSpecification | (Omit<LayerSpecification, 'source'> & {source: SourceSpecification}) | CustomLayerInterface;
|
|
199
|
+
|
|
194
200
|
/**
|
|
195
201
|
* The Style base class
|
|
196
202
|
*/
|
|
@@ -362,7 +368,7 @@ export class Style extends Evented {
|
|
|
362
368
|
|
|
363
369
|
this.light = new Light(this.stylesheet.light);
|
|
364
370
|
|
|
365
|
-
this.map.setTerrain(this.stylesheet.terrain);
|
|
371
|
+
this.map.setTerrain(this.stylesheet.terrain ?? null);
|
|
366
372
|
|
|
367
373
|
this.fire(new Event('data', {dataType: 'style'}));
|
|
368
374
|
this.fire(new Event('style.load'));
|
|
@@ -848,7 +854,7 @@ export class Style extends Evented {
|
|
|
848
854
|
* @param options - Style setter options.
|
|
849
855
|
* @returns `this`.
|
|
850
856
|
*/
|
|
851
|
-
addLayer(layerObject:
|
|
857
|
+
addLayer(layerObject: AddLayerObject, before?: string, options: StyleSetterOptions = {}): this {
|
|
852
858
|
this._checkLoaded();
|
|
853
859
|
|
|
854
860
|
const id = layerObject.id;
|
|
@@ -858,7 +864,7 @@ export class Style extends Evented {
|
|
|
858
864
|
return;
|
|
859
865
|
}
|
|
860
866
|
|
|
861
|
-
let layer
|
|
867
|
+
let layer: ReturnType<typeof createStyleLayer>;
|
|
862
868
|
if (layerObject.type === 'custom') {
|
|
863
869
|
|
|
864
870
|
if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return;
|
|
@@ -866,17 +872,17 @@ export class Style extends Evented {
|
|
|
866
872
|
layer = createStyleLayer(layerObject);
|
|
867
873
|
|
|
868
874
|
} else {
|
|
869
|
-
if (
|
|
870
|
-
this.addSource(id,
|
|
875
|
+
if ('source' in layerObject && typeof layerObject.source === 'object') {
|
|
876
|
+
this.addSource(id, layerObject.source);
|
|
871
877
|
layerObject = clone(layerObject);
|
|
872
|
-
layerObject =
|
|
878
|
+
layerObject = extend(layerObject, {source: id});
|
|
873
879
|
}
|
|
874
880
|
|
|
875
881
|
// this layer is not in the style.layers array, so we pass an impossible array index
|
|
876
882
|
if (this._validate(validateStyle.layer,
|
|
877
883
|
`layers.${id}`, layerObject, {arrayIndex: -1}, options)) return;
|
|
878
884
|
|
|
879
|
-
layer = createStyleLayer(layerObject);
|
|
885
|
+
layer = createStyleLayer(layerObject as LayerSpecification | CustomLayerInterface);
|
|
880
886
|
this._validateLayer(layer);
|
|
881
887
|
|
|
882
888
|
layer.setEventedParent(this, {layer: {id}});
|
|
@@ -994,7 +1000,7 @@ export class Style extends Evented {
|
|
|
994
1000
|
* @param id - id of the desired layer
|
|
995
1001
|
* @returns a layer, if one with the given `id` exists
|
|
996
1002
|
*/
|
|
997
|
-
getLayer(id: string): StyleLayer {
|
|
1003
|
+
getLayer(id: string): StyleLayer | undefined {
|
|
998
1004
|
return this._layers[id];
|
|
999
1005
|
}
|
|
1000
1006
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {createStyleLayer} from '../create_style_layer';
|
|
2
|
+
import {extend} from '../../util/util';
|
|
3
|
+
import {LineStyleLayer} from './line_style_layer';
|
|
4
|
+
|
|
5
|
+
describe('LineStyleLayer', () => {
|
|
6
|
+
function createLineLayer(layer?) {
|
|
7
|
+
return extend({
|
|
8
|
+
type: 'line',
|
|
9
|
+
source: 'line',
|
|
10
|
+
id: 'line',
|
|
11
|
+
paint: {
|
|
12
|
+
'line-color': 'red',
|
|
13
|
+
'line-width': 14,
|
|
14
|
+
'line-gradient': [
|
|
15
|
+
'interpolate',
|
|
16
|
+
['linear'],
|
|
17
|
+
['line-progress'],
|
|
18
|
+
0,
|
|
19
|
+
'blue',
|
|
20
|
+
1,
|
|
21
|
+
'red'
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}, layer);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('updating with valid line-gradient updates this.gradientVersion', () => {
|
|
28
|
+
const lineLayer = createStyleLayer(createLineLayer()) as LineStyleLayer;
|
|
29
|
+
const gradientVersion = lineLayer.gradientVersion;
|
|
30
|
+
|
|
31
|
+
lineLayer.setPaintProperty('line-gradient', [
|
|
32
|
+
'interpolate',
|
|
33
|
+
['linear'],
|
|
34
|
+
['line-progress'],
|
|
35
|
+
0,
|
|
36
|
+
'red',
|
|
37
|
+
1,
|
|
38
|
+
'blue'
|
|
39
|
+
]);
|
|
40
|
+
expect(lineLayer.gradientVersion).toBeGreaterThan(gradientVersion);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('updating with invalid line-gradient updates this.gradientVersion', () => {
|
|
44
|
+
const lineLayer = createStyleLayer(createLineLayer()) as LineStyleLayer;
|
|
45
|
+
const gradientVersion = lineLayer.gradientVersion;
|
|
46
|
+
|
|
47
|
+
lineLayer.setPaintProperty('line-gradient', null);
|
|
48
|
+
expect(lineLayer.gradientVersion).toBeGreaterThan(gradientVersion);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -9,8 +9,8 @@ import {extend} from '../../util/util';
|
|
|
9
9
|
import {EvaluationParameters} from '../evaluation_parameters';
|
|
10
10
|
import {Transitionable, Transitioning, Layout, PossiblyEvaluated, DataDrivenProperty} from '../properties';
|
|
11
11
|
|
|
12
|
-
import {Step} from '@maplibre/maplibre-gl-style-spec';
|
|
13
|
-
import type {FeatureState,
|
|
12
|
+
import {isZoomExpression, Step} from '@maplibre/maplibre-gl-style-spec';
|
|
13
|
+
import type {FeatureState, LayerSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
14
14
|
import type {Bucket, BucketParameters} from '../../data/bucket';
|
|
15
15
|
import type {LineLayoutProps, LinePaintProps} from './line_style_layer_properties.g';
|
|
16
16
|
import type {Transform} from '../../geo/transform';
|
|
@@ -60,8 +60,12 @@ export class LineStyleLayer extends StyleLayer {
|
|
|
60
60
|
|
|
61
61
|
_handleSpecialPaintPropertyUpdate(name: string) {
|
|
62
62
|
if (name === 'line-gradient') {
|
|
63
|
-
const expression
|
|
64
|
-
|
|
63
|
+
const expression = this.gradientExpression();
|
|
64
|
+
if (isZoomExpression(expression)) {
|
|
65
|
+
this.stepInterpolant = expression._styleExpression.expression instanceof Step;
|
|
66
|
+
} else {
|
|
67
|
+
this.stepInterpolant = false;
|
|
68
|
+
}
|
|
65
69
|
this.gradientVersion = (this.gradientVersion + 1) % Number.MAX_SAFE_INTEGER;
|
|
66
70
|
}
|
|
67
71
|
}
|
|
@@ -32,7 +32,7 @@ export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, numb
|
|
|
32
32
|
let x = 0, y = 0;
|
|
33
33
|
if (radialOffset < 0) radialOffset = 0; // Ignore negative offset.
|
|
34
34
|
// solve for r where r^2 + r^2 = radialOffset^2
|
|
35
|
-
const hypotenuse = radialOffset / Math.
|
|
35
|
+
const hypotenuse = radialOffset / Math.SQRT2;
|
|
36
36
|
switch (anchor) {
|
|
37
37
|
case 'top-right':
|
|
38
38
|
case 'top-left':
|
|
@@ -43,7 +43,6 @@ const defaultOptions: NavigationOptions = {
|
|
|
43
43
|
* map.addControl(nav, 'top-left');
|
|
44
44
|
* ```
|
|
45
45
|
* @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigation/)
|
|
46
|
-
* @see [Add a third party vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/third-party/)
|
|
47
46
|
*/
|
|
48
47
|
export class NavigationControl implements IControl {
|
|
49
48
|
_map: Map;
|
|
@@ -351,5 +351,11 @@ export class ScrollZoomHandler implements Handler {
|
|
|
351
351
|
|
|
352
352
|
reset() {
|
|
353
353
|
this._active = false;
|
|
354
|
+
this._zooming = false;
|
|
355
|
+
delete this._targetZoom;
|
|
356
|
+
if (this._finishTimeout) {
|
|
357
|
+
clearTimeout(this._finishTimeout);
|
|
358
|
+
delete this._finishTimeout;
|
|
359
|
+
}
|
|
354
360
|
}
|
|
355
361
|
}
|
|
@@ -17,6 +17,7 @@ import {DragPanHandler} from './handler/shim/drag_pan';
|
|
|
17
17
|
import {DragRotateHandler} from './handler/shim/drag_rotate';
|
|
18
18
|
import {TwoFingersTouchZoomRotateHandler} from './handler/shim/two_fingers_touch';
|
|
19
19
|
import {extend} from '../util/util';
|
|
20
|
+
import {browser} from '../util/browser';
|
|
20
21
|
import Point from '@mapbox/point-geometry';
|
|
21
22
|
|
|
22
23
|
export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent;
|
|
@@ -583,7 +584,7 @@ export class HandlerManager {
|
|
|
583
584
|
|
|
584
585
|
const shouldSnapToNorth = bearing => bearing !== 0 && -this._bearingSnap < bearing && bearing < this._bearingSnap;
|
|
585
586
|
|
|
586
|
-
if (inertialEase) {
|
|
587
|
+
if (inertialEase && (inertialEase.essential || !browser.prefersReducedMotion)) {
|
|
587
588
|
if (shouldSnapToNorth(inertialEase.bearing || this._map.getBearing())) {
|
|
588
589
|
inertialEase.bearing = 0;
|
|
589
590
|
}
|
package/src/ui/map.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {OverscaledTileID} from '../source/tile_id';
|
|
|
6
6
|
import {Event, ErrorEvent} from '../util/evented';
|
|
7
7
|
import simulate from '../../test/unit/lib/simulate_interaction';
|
|
8
8
|
import {fixedLngLat, fixedNum} from '../../test/unit/lib/fixed';
|
|
9
|
-
import {LayerSpecification, SourceSpecification, StyleSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
9
|
+
import {GeoJSONSourceSpecification, LayerSpecification, SourceSpecification, StyleSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
10
10
|
import {RequestTransformFunction} from '../util/request_manager';
|
|
11
11
|
import {extend} from '../util/util';
|
|
12
12
|
import {LngLatBoundsLike} from '../geo/lng_lat_bounds';
|
|
@@ -675,6 +675,19 @@ describe('Map', () => {
|
|
|
675
675
|
map.addSource('fill', source);
|
|
676
676
|
});
|
|
677
677
|
|
|
678
|
+
test('a layer can be added with an embedded source specification', () => {
|
|
679
|
+
const map = createMap({deleteStyle: true});
|
|
680
|
+
const source: GeoJSONSourceSpecification = {
|
|
681
|
+
type: 'geojson',
|
|
682
|
+
data: {type: 'Point', coordinates: [0, 0]}
|
|
683
|
+
};
|
|
684
|
+
map.addLayer({
|
|
685
|
+
id: 'foo',
|
|
686
|
+
type: 'symbol',
|
|
687
|
+
source
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
|
|
678
691
|
test('returns the style with added source and layer', done => {
|
|
679
692
|
const style = createStyle();
|
|
680
693
|
const map = createMap({style});
|
|
@@ -848,21 +861,30 @@ describe('Map', () => {
|
|
|
848
861
|
|
|
849
862
|
const map = createMap();
|
|
850
863
|
|
|
851
|
-
const
|
|
852
|
-
const
|
|
864
|
+
const updateSpy = jest.spyOn(map, '_update');
|
|
865
|
+
const resizeSpy = jest.spyOn(map, 'resize');
|
|
853
866
|
|
|
854
867
|
// The initial "observe" event fired by ResizeObserver should be captured/muted
|
|
855
868
|
// in the map constructor
|
|
856
869
|
|
|
857
870
|
observerCallback();
|
|
858
|
-
expect(
|
|
859
|
-
expect(
|
|
871
|
+
expect(updateSpy).not.toHaveBeenCalled();
|
|
872
|
+
expect(resizeSpy).not.toHaveBeenCalled();
|
|
873
|
+
|
|
874
|
+
// The next "observe" event should fire a resize / _update
|
|
860
875
|
|
|
861
|
-
|
|
876
|
+
observerCallback();
|
|
877
|
+
expect(updateSpy).toHaveBeenCalled();
|
|
878
|
+
expect(resizeSpy).toHaveBeenCalledTimes(1);
|
|
862
879
|
|
|
880
|
+
// Additional "observe" events should be throttled
|
|
881
|
+
observerCallback();
|
|
882
|
+
observerCallback();
|
|
863
883
|
observerCallback();
|
|
864
|
-
|
|
865
|
-
expect(
|
|
884
|
+
observerCallback();
|
|
885
|
+
expect(resizeSpy).toHaveBeenCalledTimes(1);
|
|
886
|
+
await new Promise((resolve) => { setTimeout(resolve, 100); });
|
|
887
|
+
expect(resizeSpy).toHaveBeenCalledTimes(2);
|
|
866
888
|
});
|
|
867
889
|
|
|
868
890
|
test('width and height correctly rounded', () => {
|
|
@@ -2569,6 +2591,13 @@ describe('Map', () => {
|
|
|
2569
2591
|
});
|
|
2570
2592
|
});
|
|
2571
2593
|
|
|
2594
|
+
describe('#getTerrain', () => {
|
|
2595
|
+
test('returns null when not set', () => {
|
|
2596
|
+
const map = createMap();
|
|
2597
|
+
expect(map.getTerrain()).toBeNull();
|
|
2598
|
+
});
|
|
2599
|
+
});
|
|
2600
|
+
|
|
2572
2601
|
describe('#setCooperativeGestures', () => {
|
|
2573
2602
|
test('returns self', () => {
|
|
2574
2603
|
const map = createMap();
|
package/src/ui/map.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {RGBAImage} from '../util/image';
|
|
|
25
25
|
import {Event, ErrorEvent, Listener} from '../util/evented';
|
|
26
26
|
import {MapEventType, MapLayerEventType, MapMouseEvent, MapSourceDataEvent, MapStyleDataEvent} from './events';
|
|
27
27
|
import {TaskQueue} from '../util/task_queue';
|
|
28
|
+
import {throttle} from '../util/throttle';
|
|
28
29
|
import {webpSupported} from '../util/webp_supported';
|
|
29
30
|
import {PerformanceMarkers, PerformanceUtils} from '../util/performance';
|
|
30
31
|
import {Source, SourceClass} from '../source/source';
|
|
@@ -33,9 +34,8 @@ import {StyleLayer} from '../style/style_layer';
|
|
|
33
34
|
import type {RequestTransformFunction} from '../util/request_manager';
|
|
34
35
|
import type {LngLatLike} from '../geo/lng_lat';
|
|
35
36
|
import type {LngLatBoundsLike} from '../geo/lng_lat_bounds';
|
|
36
|
-
import type {FeatureIdentifier, StyleOptions, StyleSetterOptions} from '../style/style';
|
|
37
|
+
import type {AddLayerObject, FeatureIdentifier, StyleOptions, StyleSetterOptions} from '../style/style';
|
|
37
38
|
import type {MapDataEvent} from './events';
|
|
38
|
-
import type {CustomLayerInterface} from '../style/style_layer/custom_style_layer';
|
|
39
39
|
import type {StyleImage, StyleImageInterface, StyleImageMetadata} from '../style/style_image';
|
|
40
40
|
import type {PointLike} from './camera';
|
|
41
41
|
import type {ScrollZoomHandler} from './handler/scroll_zoom';
|
|
@@ -51,7 +51,6 @@ import {defaultLocale} from './default_locale';
|
|
|
51
51
|
import type {TaskID} from '../util/task_queue';
|
|
52
52
|
import type {Cancelable} from '../types/cancelable';
|
|
53
53
|
import type {
|
|
54
|
-
LayerSpecification,
|
|
55
54
|
FilterSpecification,
|
|
56
55
|
StyleSpecification,
|
|
57
56
|
LightSpecification,
|
|
@@ -640,15 +639,17 @@ export class Map extends Camera {
|
|
|
640
639
|
if (typeof window !== 'undefined') {
|
|
641
640
|
addEventListener('online', this._onWindowOnline, false);
|
|
642
641
|
let initialResizeEventCaptured = false;
|
|
642
|
+
const throttledResizeCallback = throttle((entries: ResizeObserverEntry[]) => {
|
|
643
|
+
if (this._trackResize && !this._removed) {
|
|
644
|
+
this.resize(entries)._update();
|
|
645
|
+
}
|
|
646
|
+
}, 50);
|
|
643
647
|
this._resizeObserver = new ResizeObserver((entries) => {
|
|
644
648
|
if (!initialResizeEventCaptured) {
|
|
645
649
|
initialResizeEventCaptured = true;
|
|
646
650
|
return;
|
|
647
651
|
}
|
|
648
|
-
|
|
649
|
-
if (this._trackResize) {
|
|
650
|
-
this.resize(entries)._update();
|
|
651
|
-
}
|
|
652
|
+
throttledResizeCallback(entries);
|
|
652
653
|
});
|
|
653
654
|
this._resizeObserver.observe(this._container);
|
|
654
655
|
}
|
|
@@ -1962,7 +1963,7 @@ export class Map extends Camera {
|
|
|
1962
1963
|
* map.setTerrain({ source: 'terrain' });
|
|
1963
1964
|
* ```
|
|
1964
1965
|
*/
|
|
1965
|
-
setTerrain(options: TerrainSpecification): this {
|
|
1966
|
+
setTerrain(options: TerrainSpecification | null): this {
|
|
1966
1967
|
this.style._checkLoaded();
|
|
1967
1968
|
|
|
1968
1969
|
// clear event handlers
|
|
@@ -2017,8 +2018,8 @@ export class Map extends Camera {
|
|
|
2017
2018
|
* map.getTerrain(); // { source: 'terrain' };
|
|
2018
2019
|
* ```
|
|
2019
2020
|
*/
|
|
2020
|
-
getTerrain(): TerrainSpecification {
|
|
2021
|
-
return this.terrain
|
|
2021
|
+
getTerrain(): TerrainSpecification | null {
|
|
2022
|
+
return this.terrain?.options ?? null;
|
|
2022
2023
|
}
|
|
2023
2024
|
|
|
2024
2025
|
/**
|
|
@@ -2346,7 +2347,7 @@ export class Map extends Camera {
|
|
|
2346
2347
|
*
|
|
2347
2348
|
* @param layer - The layer to add,
|
|
2348
2349
|
* conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
|
|
2349
|
-
* less commonly, the {@link CustomLayerInterface} specification.
|
|
2350
|
+
* less commonly, the {@link CustomLayerInterface} specification. Can also be a layer definition with an embedded source definition.
|
|
2350
2351
|
* The MapLibre Style Specification's layer definition is appropriate for most layers.
|
|
2351
2352
|
*
|
|
2352
2353
|
* @param beforeId - The ID of an existing layer to insert the new layer before,
|
|
@@ -2418,7 +2419,7 @@ export class Map extends Camera {
|
|
|
2418
2419
|
* @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/vector-source/)
|
|
2419
2420
|
* @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/wms/)
|
|
2420
2421
|
*/
|
|
2421
|
-
addLayer(layer:
|
|
2422
|
+
addLayer(layer: AddLayerObject, beforeId?: string) {
|
|
2422
2423
|
this._lazyInitEmptyStyle();
|
|
2423
2424
|
this.style.addLayer(layer, beforeId);
|
|
2424
2425
|
return this._update(true);
|
|
@@ -2475,7 +2476,7 @@ export class Map extends Camera {
|
|
|
2475
2476
|
* @see [Filter symbols by toggling a list](https://maplibre.org/maplibre-gl-js/docs/examples/filter-markers/)
|
|
2476
2477
|
* @see [Filter symbols by text input](https://maplibre.org/maplibre-gl-js/docs/examples/filter-markers-by-input/)
|
|
2477
2478
|
*/
|
|
2478
|
-
getLayer(id: string): StyleLayer {
|
|
2479
|
+
getLayer(id: string): StyleLayer | undefined {
|
|
2479
2480
|
return this.style.getLayer(id);
|
|
2480
2481
|
}
|
|
2481
2482
|
|
|
@@ -2561,6 +2562,7 @@ export class Map extends Camera {
|
|
|
2561
2562
|
* @param name - The name of the paint property to set.
|
|
2562
2563
|
* @param value - The value of the paint property to set.
|
|
2563
2564
|
* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
|
|
2565
|
+
* Pass `null` to unset the existing value.
|
|
2564
2566
|
* @param options - Options object.
|
|
2565
2567
|
* @returns `this`
|
|
2566
2568
|
* @example
|
package/src/ui/marker.test.ts
CHANGED
|
@@ -811,4 +811,29 @@ describe('marker', () => {
|
|
|
811
811
|
|
|
812
812
|
map.remove();
|
|
813
813
|
});
|
|
814
|
+
|
|
815
|
+
test('Marker after the terrain event must listen to the render event till is fully loaded', async () => {
|
|
816
|
+
const map = createMap();
|
|
817
|
+
|
|
818
|
+
new Marker()
|
|
819
|
+
.setLngLat([1, 1])
|
|
820
|
+
.addTo(map);
|
|
821
|
+
|
|
822
|
+
expect(map._oneTimeListeners.render).toBeUndefined();
|
|
823
|
+
|
|
824
|
+
map.fire('terrain');
|
|
825
|
+
expect(map._oneTimeListeners.render).toHaveLength(1);
|
|
826
|
+
|
|
827
|
+
map.fire('render');
|
|
828
|
+
expect(map._oneTimeListeners.render).toHaveLength(1);
|
|
829
|
+
|
|
830
|
+
map.fire('render');
|
|
831
|
+
expect(map._oneTimeListeners.render).toHaveLength(1);
|
|
832
|
+
|
|
833
|
+
// await idle to be fully loaded
|
|
834
|
+
await map.once('idle');
|
|
835
|
+
map.fire('render');
|
|
836
|
+
expect(map._oneTimeListeners.render).toHaveLength(0);
|
|
837
|
+
map.remove();
|
|
838
|
+
});
|
|
814
839
|
});
|
package/src/ui/marker.ts
CHANGED
|
@@ -297,6 +297,8 @@ export class Marker extends Evented {
|
|
|
297
297
|
map.getCanvasContainer().appendChild(this._element);
|
|
298
298
|
map.on('move', this._update);
|
|
299
299
|
map.on('moveend', this._update);
|
|
300
|
+
map.on('terrain', this._update);
|
|
301
|
+
|
|
300
302
|
this.setDraggable(this._draggable);
|
|
301
303
|
this._update();
|
|
302
304
|
|
|
@@ -419,7 +421,7 @@ export class Marker extends Evented {
|
|
|
419
421
|
if (!('offset' in popup.options)) {
|
|
420
422
|
const markerHeight = 41 - (5.8 / 2);
|
|
421
423
|
const markerRadius = 13.5;
|
|
422
|
-
const linearOffset = Math.
|
|
424
|
+
const linearOffset = Math.abs(markerRadius) / Math.SQRT2;
|
|
423
425
|
popup.options.offset = this._defaultMarker ? {
|
|
424
426
|
'top': [0, 0],
|
|
425
427
|
'top-left': [0, 0],
|
|
@@ -504,9 +506,14 @@ export class Marker extends Evented {
|
|
|
504
506
|
return this;
|
|
505
507
|
}
|
|
506
508
|
|
|
507
|
-
_update = (e?: { type: 'move' | 'moveend' }) => {
|
|
509
|
+
_update = (e?: { type: 'move' | 'moveend' | 'terrain' | 'render' }) => {
|
|
508
510
|
if (!this._map) return;
|
|
509
511
|
|
|
512
|
+
const isFullyLoaded = this._map.loaded() && !this._map.isMoving();
|
|
513
|
+
if (e?.type === 'terrain' || (e?.type === 'render' && !isFullyLoaded)) {
|
|
514
|
+
this._map.once('render', this._update);
|
|
515
|
+
}
|
|
516
|
+
|
|
510
517
|
if (this._map.transform.renderWorldCopies) {
|
|
511
518
|
this._lngLat = smartWrap(this._lngLat, this._pos, this._map.transform);
|
|
512
519
|
}
|
package/src/ui/popup.ts
CHANGED
|
@@ -627,7 +627,7 @@ function normalizeOffset(offset?: Offset | null) {
|
|
|
627
627
|
|
|
628
628
|
} else if (typeof offset === 'number') {
|
|
629
629
|
// input specifies a radius from which to calculate offsets at all positions
|
|
630
|
-
const cornerOffset = Math.round(Math.
|
|
630
|
+
const cornerOffset = Math.round(Math.abs(offset) / Math.SQRT2);
|
|
631
631
|
return {
|
|
632
632
|
'center': new Point(0, 0),
|
|
633
633
|
'top': new Point(0, offset),
|
package/src/util/ajax.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
sameOrigin
|
|
7
7
|
} from './ajax';
|
|
8
8
|
|
|
9
|
-
import {fakeServer, FakeServer} from 'nise';
|
|
9
|
+
import {fakeServer, type FakeServer} from 'nise';
|
|
10
10
|
import {destroyFetchMock, FetchMock, RequestMock, setupFetchMock} from './test/mock_fetch';
|
|
11
11
|
|
|
12
12
|
function readAsText(blob) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {config} from './config';
|
|
2
2
|
import {webpSupported} from './webp_supported';
|
|
3
3
|
import {stubAjaxGetImage} from './test/util';
|
|
4
|
-
import {fakeServer, FakeServer} from 'nise';
|
|
4
|
+
import {fakeServer, type FakeServer} from 'nise';
|
|
5
5
|
import {ImageRequest, ImageRequestQueueItem} from './image_request';
|
|
6
6
|
import * as ajax from './ajax';
|
|
7
7
|
|
package/src/util/test/util.ts
CHANGED
|
@@ -132,3 +132,15 @@ export function stubAjaxGetImage(createImageBitmap) {
|
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* This should be used in test that use nise since the internal buffer returned from a file is not an instance of ArrayBuffer for some reason.
|
|
138
|
+
* @param data - the data read from a file, for example by `fs.readFileSync(...)`
|
|
139
|
+
* @returns a copy of the data in the file in `ArrayBuffer` format
|
|
140
|
+
*/
|
|
141
|
+
export function bufferToArrayBuffer(data: Buffer): ArrayBuffer {
|
|
142
|
+
const newBuffer = new ArrayBuffer(data.buffer.byteLength);
|
|
143
|
+
const view = new Uint8Array(newBuffer);
|
|
144
|
+
data.copy(view);
|
|
145
|
+
return view.buffer;
|
|
146
|
+
}
|