maplibre-gl 2.3.0 → 2.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/build/generate-query-test-fixtures.ts +3 -2
- package/build/generate-struct-arrays.ts +0 -2
- package/build/generate-style-spec.ts +12 -17
- package/build/rollup_plugins.ts +0 -4
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-dev.js +179 -1369
- package/dist/maplibre-gl.d.ts +15 -16
- package/dist/maplibre-gl.js +4 -4
- package/dist/style-spec/index.d.ts +3 -16
- package/package.json +20 -21
- package/src/data/array_types.g.ts +0 -5
- package/src/data/bucket/fill_bucket.ts +0 -2
- package/src/data/bucket/fill_extrusion_bucket.ts +0 -2
- package/src/data/feature_position_map.ts +1 -2
- package/src/gl/framebuffer.ts +3 -2
- package/src/gl/index_buffer.ts +1 -2
- package/src/gl/vertex_buffer.ts +1 -2
- package/src/index.ts +1 -3
- package/src/render/draw_debug.ts +1 -1
- package/src/render/draw_fill.ts +9 -1
- package/src/render/draw_fill_extrusion.ts +8 -1
- package/src/render/draw_heatmap.ts +5 -3
- package/src/render/draw_hillshade.ts +17 -2
- package/src/render/image_manager.ts +5 -7
- package/src/render/program/line_program.ts +1 -1
- package/src/render/program/pattern.ts +0 -2
- package/src/render/program.ts +12 -9
- package/src/render/vertex_array_object.ts +1 -3
- package/src/source/geojson_worker_source.ts +0 -4
- package/src/source/query_features.ts +0 -3
- package/src/source/rtl_text_plugin.ts +3 -4
- package/src/source/source_cache.ts +0 -2
- package/src/source/tile_id.ts +8 -8
- package/src/source/worker.ts +0 -7
- package/src/source/worker_tile.ts +3 -2
- package/src/style/format_section_override.ts +1 -2
- package/src/style/properties.ts +1 -3
- package/src/style/style.ts +6 -5
- package/src/style/style_layer/custom_style_layer.ts +1 -2
- package/src/style/style_layer/symbol_style_layer.ts +5 -8
- package/src/style-spec/CHANGELOG.md +2 -2
- package/src/style-spec/expression/compound_expression.ts +0 -4
- package/src/style-spec/expression/definitions/assertion.ts +2 -4
- package/src/style-spec/expression/definitions/case.ts +1 -2
- package/src/style-spec/expression/definitions/coalesce.ts +1 -3
- package/src/style-spec/expression/definitions/coercion.ts +2 -4
- package/src/style-spec/expression/definitions/match.ts +0 -3
- package/src/style-spec/expression/index.ts +0 -2
- package/src/style-spec/expression/values.ts +0 -2
- package/src/style-spec/feature_filter/feature_filter.test.ts +39 -2
- package/src/style-spec/function/convert.ts +1 -3
- package/src/style-spec/types.g.ts +12 -17
- package/src/symbol/collision_index.ts +0 -3
- package/src/symbol/path_interpolator.ts +0 -2
- package/src/symbol/placement.ts +14 -9
- package/src/symbol/shaping.ts +0 -4
- package/src/ui/camera.test.ts +64 -1
- package/src/ui/camera.ts +36 -10
- package/src/ui/control/attribution_control.test.ts +62 -0
- package/src/ui/control/attribution_control.ts +4 -2
- package/src/ui/control/geolocate_control.ts +4 -6
- package/src/ui/control/terrain_control.test.ts +60 -0
- package/src/ui/handler/handler_util.ts +1 -2
- package/src/ui/handler/map_event.test.ts +65 -1
- package/src/ui/handler/map_event.ts +4 -1
- package/src/ui/handler/scroll_zoom.ts +0 -2
- package/src/ui/handler_manager.ts +0 -2
- package/src/ui/map.test.ts +105 -0
- package/src/ui/map.ts +8 -1
- package/src/util/ajax.ts +1 -2
- package/src/util/color_ramp.ts +1 -2
- package/src/util/dictionary_coder.ts +1 -3
- package/src/util/dispatcher.ts +1 -4
- package/src/util/dom.ts +0 -3
- package/src/util/image.ts +1 -3
- package/src/util/struct_array.ts +0 -5
- package/src/util/task_queue.ts +1 -3
- package/src/util/web_worker_transfer.ts +5 -6
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {clamp} from '../util/util';
|
|
2
2
|
import Point from '@mapbox/point-geometry';
|
|
3
|
-
import assert from 'assert';
|
|
4
3
|
|
|
5
4
|
class PathInterpolator {
|
|
6
5
|
points: Array<Point>;
|
|
@@ -30,7 +29,6 @@ class PathInterpolator {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
lerp(t: number): Point {
|
|
33
|
-
assert(this.points.length > 0);
|
|
34
32
|
if (this.points.length === 1) {
|
|
35
33
|
return this.points[0];
|
|
36
34
|
}
|
package/src/symbol/placement.ts
CHANGED
|
@@ -6,7 +6,6 @@ import * as projection from './projection';
|
|
|
6
6
|
import {getAnchorJustification, evaluateVariableOffset} from './symbol_layout';
|
|
7
7
|
import {getAnchorAlignment, WritingMode} from './shaping';
|
|
8
8
|
import {mat4} from 'gl-matrix';
|
|
9
|
-
import assert from 'assert';
|
|
10
9
|
import pixelsToTileUnits from '../source/pixels_to_tile_units';
|
|
11
10
|
import Point from '@mapbox/point-geometry';
|
|
12
11
|
import type Transform from '../geo/transform';
|
|
@@ -23,6 +22,7 @@ import type FeatureIndex from '../data/feature_index';
|
|
|
23
22
|
import type {OverscaledTileID} from '../source/tile_id';
|
|
24
23
|
import type {TextAnchor} from './symbol_layout';
|
|
25
24
|
import Terrain from '../render/terrain';
|
|
25
|
+
import {warnOnce} from '../util/util';
|
|
26
26
|
|
|
27
27
|
class OpacityState {
|
|
28
28
|
opacity: number;
|
|
@@ -391,7 +391,7 @@ export class Placement {
|
|
|
391
391
|
this.prevPlacement.placements[symbolInstance.crossTileID].text) {
|
|
392
392
|
prevAnchor = this.prevPlacement.variableOffsets[symbolInstance.crossTileID].anchor;
|
|
393
393
|
}
|
|
394
|
-
|
|
394
|
+
if (symbolInstance.crossTileID === 0) throw new Error('symbolInstance.crossTileID can\'t be 0');
|
|
395
395
|
this.variableOffsets[symbolInstance.crossTileID] = {
|
|
396
396
|
textOffset,
|
|
397
397
|
width,
|
|
@@ -676,7 +676,10 @@ export class Placement {
|
|
|
676
676
|
getElevation
|
|
677
677
|
);
|
|
678
678
|
|
|
679
|
-
|
|
679
|
+
if (placedGlyphCircles.circles.length && placedGlyphCircles.collisionDetected && !showCollisionBoxes) {
|
|
680
|
+
warnOnce('Collisions detected, but collision boxes are not shown');
|
|
681
|
+
}
|
|
682
|
+
|
|
680
683
|
// If text-overlap is set to 'always', force "placedCircles" to true
|
|
681
684
|
// In theory there should always be at least one circle placed
|
|
682
685
|
// in this case, but for now quirks in text-anchor
|
|
@@ -781,15 +784,15 @@ export class Placement {
|
|
|
781
784
|
}
|
|
782
785
|
}
|
|
783
786
|
|
|
784
|
-
|
|
785
|
-
|
|
787
|
+
if (symbolInstance.crossTileID === 0) throw new Error('symbolInstance.crossTileID can\'t be 0');
|
|
788
|
+
if (bucket.bucketInstanceId === 0) throw new Error('bucket.bucketInstanceId can\'t be 0');
|
|
786
789
|
|
|
787
790
|
this.placements[symbolInstance.crossTileID] = new JointPlacement(placeText || alwaysShowText, placeIcon || alwaysShowIcon, offscreen || bucket.justReloaded);
|
|
788
791
|
seenCrossTileIDs[symbolInstance.crossTileID] = true;
|
|
789
792
|
};
|
|
790
793
|
|
|
791
794
|
if (zOrderByViewportY) {
|
|
792
|
-
|
|
795
|
+
if (bucketPart.symbolInstanceStart !== 0) throw new Error('bucket.bucketInstanceId should be 0');
|
|
793
796
|
const symbolIndexes = bucket.getSortedSymbolIndexes(this.transform.angle);
|
|
794
797
|
for (let i = symbolIndexes.length - 1; i >= 0; --i) {
|
|
795
798
|
const symbolIndex = symbolIndexes[i];
|
|
@@ -920,7 +923,9 @@ export class Placement {
|
|
|
920
923
|
// this.lastPlacementChangeTime is the time of the last commit() that
|
|
921
924
|
// resulted in a placement change -- in other words, the start time of
|
|
922
925
|
// the last symbol fade animation
|
|
923
|
-
|
|
926
|
+
if (prevPlacement && prevPlacement.lastPlacementChangeTime === undefined) {
|
|
927
|
+
throw new Error('Last placement time for previous placement is not defined');
|
|
928
|
+
}
|
|
924
929
|
if (placementChanged) {
|
|
925
930
|
this.lastPlacementChangeTime = now;
|
|
926
931
|
} else if (typeof this.lastPlacementChangeTime !== 'number') {
|
|
@@ -1133,8 +1138,8 @@ export class Placement {
|
|
|
1133
1138
|
bucket.textCollisionBox.collisionVertexBuffer.updateData(bucket.textCollisionBox.collisionVertexArray);
|
|
1134
1139
|
}
|
|
1135
1140
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1141
|
+
if (bucket.text.opacityVertexArray.length !== bucket.text.layoutVertexArray.length / 4) throw new Error(`bucket.text.opacityVertexArray.length (= ${bucket.text.opacityVertexArray.length}) !== bucket.text.layoutVertexArray.length (= ${bucket.text.layoutVertexArray.length}) / 4`);
|
|
1142
|
+
if (bucket.icon.opacityVertexArray.length !== bucket.icon.layoutVertexArray.length / 4) throw new Error(`bucket.icon.opacityVertexArray.length (= ${bucket.icon.opacityVertexArray.length}) !== bucket.icon.layoutVertexArray.length (= ${bucket.icon.layoutVertexArray.length}) / 4`);
|
|
1138
1143
|
|
|
1139
1144
|
// Push generated collision circles to the bucket for debug rendering
|
|
1140
1145
|
if (bucket.bucketInstanceId in this.collisionCircleArrays) {
|
package/src/symbol/shaping.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import assert from 'assert';
|
|
2
1
|
import {
|
|
3
2
|
charHasUprightVerticalOrientation,
|
|
4
3
|
charAllowsIdeographicBreaking,
|
|
@@ -811,9 +810,6 @@ function fitIconToText(
|
|
|
811
810
|
iconOffset: [number, number],
|
|
812
811
|
fontScale: number
|
|
813
812
|
): PositionedIcon {
|
|
814
|
-
assert(textFit !== 'none');
|
|
815
|
-
assert(Array.isArray(padding) && padding.length === 4);
|
|
816
|
-
assert(Array.isArray(iconOffset) && iconOffset.length === 2);
|
|
817
813
|
|
|
818
814
|
const image = shapedIcon.image;
|
|
819
815
|
|
package/src/ui/camera.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import Camera from '../ui/camera';
|
|
1
|
+
import Camera, {CameraOptions} from '../ui/camera';
|
|
2
2
|
import Transform from '../geo/transform';
|
|
3
3
|
import TaskQueue, {TaskID} from '../util/task_queue';
|
|
4
4
|
import browser from '../util/browser';
|
|
5
5
|
import {fixedLngLat, fixedNum} from '../../test/unit/lib/fixed';
|
|
6
6
|
import {setMatchMedia} from '../util/test/util';
|
|
7
|
+
import {mercatorZfromAltitude} from '../geo/mercator_coordinate';
|
|
7
8
|
|
|
8
9
|
beforeEach(() => {
|
|
9
10
|
setMatchMedia();
|
|
@@ -55,6 +56,68 @@ function assertTransitionTime(done, camera, min, max) {
|
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
describe('#calculateCameraOptionsFromTo', () => {
|
|
60
|
+
// Choose initial zoom to avoid center being constrained by mercator latitude limits.
|
|
61
|
+
const camera = createCamera({zoom: 1});
|
|
62
|
+
|
|
63
|
+
test('look at north', () => {
|
|
64
|
+
const cameraOptions: CameraOptions = camera.calculateCameraOptionsFromTo({lng: 1, lat: 0}, 0, {lng: 1, lat: 1});
|
|
65
|
+
expect(cameraOptions).toBeDefined();
|
|
66
|
+
expect(cameraOptions.center).toBeDefined();
|
|
67
|
+
expect(cameraOptions.bearing).toBeCloseTo(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('look at west', () => {
|
|
71
|
+
const cameraOptions = camera.calculateCameraOptionsFromTo({lng: 1, lat: 0}, 0, {lng: 0, lat: 0});
|
|
72
|
+
expect(cameraOptions).toBeDefined();
|
|
73
|
+
expect(cameraOptions.bearing).toBeCloseTo(-90);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('pitch 45', () => {
|
|
77
|
+
// altitude same as grounddistance => 45°
|
|
78
|
+
// distance between lng x and lng x+1 is 111.2km at same lat
|
|
79
|
+
const cameraOptions: CameraOptions = camera.calculateCameraOptionsFromTo({lng: 1, lat: 0}, 111200, {lng: 0, lat: 0});
|
|
80
|
+
expect(cameraOptions).toBeDefined();
|
|
81
|
+
expect(cameraOptions.pitch).toBeCloseTo(45);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('pitch 90', () => {
|
|
85
|
+
const cameraOptions = camera.calculateCameraOptionsFromTo({lng: 1, lat: 0}, 0, {lng: 0, lat: 0});
|
|
86
|
+
expect(cameraOptions).toBeDefined();
|
|
87
|
+
expect(cameraOptions.pitch).toBeCloseTo(90);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('pitch 153.435', () => {
|
|
91
|
+
|
|
92
|
+
// distance between lng x and lng x+1 is 111.2km at same lat
|
|
93
|
+
// (elevation difference of cam and center) / 2 = grounddistance =>
|
|
94
|
+
// acos(111.2 / sqrt(111.2² + (111.2 * 2)²)) = acos(1/sqrt(5)) => 63.435 + 90 (looking up) = 153.435
|
|
95
|
+
const cameraOptions: CameraOptions = camera.calculateCameraOptionsFromTo({lng: 1, lat: 0}, 111200, {lng: 0, lat: 0}, 111200 * 3);
|
|
96
|
+
expect(cameraOptions).toBeDefined();
|
|
97
|
+
expect(cameraOptions.pitch).toBeCloseTo(153.435);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('zoom distance 1000', () => {
|
|
101
|
+
const expectedZoom = Math.log2(camera.transform.cameraToCenterDistance / mercatorZfromAltitude(1000, 0) / camera.transform.tileSize);
|
|
102
|
+
const cameraOptions = camera.calculateCameraOptionsFromTo({lng: 0, lat: 0}, 0, {lng: 0, lat: 0}, 1000);
|
|
103
|
+
|
|
104
|
+
expect(cameraOptions).toBeDefined();
|
|
105
|
+
expect(cameraOptions.zoom).toBeCloseTo(expectedZoom);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('zoom distance 1 lng (111.2km), 111.2km altitude away', () => {
|
|
109
|
+
const expectedZoom = Math.log2(camera.transform.cameraToCenterDistance / mercatorZfromAltitude(Math.hypot(111200, 111200), 0) / camera.transform.tileSize);
|
|
110
|
+
const cameraOptions = camera.calculateCameraOptionsFromTo({lng: 0, lat: 0}, 0, {lng: 1, lat: 0}, 111200);
|
|
111
|
+
|
|
112
|
+
expect(cameraOptions).toBeDefined();
|
|
113
|
+
expect(cameraOptions.zoom).toBeCloseTo(expectedZoom);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('same To as From error', () => {
|
|
117
|
+
expect(() => { camera.calculateCameraOptionsFromTo({lng: 0, lat: 0}, 0, {lng: 0, lat: 0}, 0); }).toThrow();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
58
121
|
describe('#jumpTo', () => {
|
|
59
122
|
// Choose initial zoom to avoid center being constrained by mercator latitude limits.
|
|
60
123
|
const camera = createCamera({zoom: 1});
|
package/src/ui/camera.ts
CHANGED
|
@@ -5,7 +5,6 @@ import LngLat from '../geo/lng_lat';
|
|
|
5
5
|
import LngLatBounds from '../geo/lng_lat_bounds';
|
|
6
6
|
import Point from '@mapbox/point-geometry';
|
|
7
7
|
import {Event, Evented} from '../util/evented';
|
|
8
|
-
import assert from 'assert';
|
|
9
8
|
import {Debug} from '../util/debug';
|
|
10
9
|
|
|
11
10
|
import type Transform from '../geo/transform';
|
|
@@ -13,6 +12,7 @@ import type {LngLatLike} from '../geo/lng_lat';
|
|
|
13
12
|
import type {LngLatBoundsLike} from '../geo/lng_lat_bounds';
|
|
14
13
|
import type {TaskID} from '../util/task_queue';
|
|
15
14
|
import type {PaddingOptions} from '../geo/edge_insets';
|
|
15
|
+
import MercatorCoordinate from '../geo/mercator_coordinate';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* A [Point](https://github.com/mapbox/point-geometry) or an array of two numbers representing `x` and `y` screen coordinates in pixels.
|
|
@@ -775,6 +775,41 @@ abstract class Camera extends Evented {
|
|
|
775
775
|
return this.fire(new Event('moveend', eventData));
|
|
776
776
|
}
|
|
777
777
|
|
|
778
|
+
/**
|
|
779
|
+
* Calculates pitch, zoom and bearing for looking at @param newCenter with the camera position being @param newCenter
|
|
780
|
+
* and returns them as Cameraoptions.
|
|
781
|
+
* @memberof Map#
|
|
782
|
+
* @param from The camera to look from
|
|
783
|
+
* @param altitudeFrom The altitude of the camera to look from
|
|
784
|
+
* @param to The center to look at
|
|
785
|
+
* @param altitudeTo Optional altitude of the center to look at. If none given the ground height will be used.
|
|
786
|
+
* @returns {CameraOptions} the calculated camera options
|
|
787
|
+
*/
|
|
788
|
+
calculateCameraOptionsFromTo(from: LngLat, altitudeFrom: number, to: LngLat, altitudeTo: number = 0) : CameraOptions {
|
|
789
|
+
const fromMerc = MercatorCoordinate.fromLngLat(from, altitudeFrom);
|
|
790
|
+
const toMerc = MercatorCoordinate.fromLngLat(to, altitudeTo);
|
|
791
|
+
const dx = toMerc.x - fromMerc.x;
|
|
792
|
+
const dy = toMerc.y - fromMerc.y;
|
|
793
|
+
const dz = toMerc.z - fromMerc.z;
|
|
794
|
+
|
|
795
|
+
const distance3D = Math.hypot(dx, dy, dz);
|
|
796
|
+
if (distance3D === 0) throw new Error('Can\'t calculate camera options with same From and To');
|
|
797
|
+
|
|
798
|
+
const groundDistance = Math.hypot(dx, dy);
|
|
799
|
+
|
|
800
|
+
const zoom = this.transform.scaleZoom(this.transform.cameraToCenterDistance / distance3D / this.transform.tileSize);
|
|
801
|
+
const bearing = (Math.atan2(dx, -dy) * 180) / Math.PI;
|
|
802
|
+
let pitch = (Math.acos(groundDistance / distance3D) * 180) / Math.PI;
|
|
803
|
+
pitch = dz < 0 ? 90 - pitch : 90 + pitch;
|
|
804
|
+
|
|
805
|
+
return {
|
|
806
|
+
center: toMerc.toLngLat(),
|
|
807
|
+
zoom,
|
|
808
|
+
pitch,
|
|
809
|
+
bearing
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
778
813
|
/**
|
|
779
814
|
* Changes any combination of `center`, `zoom`, `bearing`, `pitch`, and `padding` with an animated transition
|
|
780
815
|
* between old and new values. The map will retain its current values for any
|
|
@@ -1273,19 +1308,10 @@ function addAssertions(camera: Camera) { //eslint-disable-line
|
|
|
1273
1308
|
inProgress[name] = false;
|
|
1274
1309
|
|
|
1275
1310
|
camera.on(`${name}start`, () => {
|
|
1276
|
-
assert(!inProgress[name], `"${name}start" fired twice without a "${name}end"`);
|
|
1277
1311
|
inProgress[name] = true;
|
|
1278
|
-
assert(inProgress.move);
|
|
1279
|
-
});
|
|
1280
|
-
|
|
1281
|
-
camera.on(name, () => {
|
|
1282
|
-
assert(inProgress[name]);
|
|
1283
|
-
assert(inProgress.move);
|
|
1284
1312
|
});
|
|
1285
1313
|
|
|
1286
1314
|
camera.on(`${name}end`, () => {
|
|
1287
|
-
assert(inProgress.move);
|
|
1288
|
-
assert(inProgress[name]);
|
|
1289
1315
|
inProgress[name] = false;
|
|
1290
1316
|
});
|
|
1291
1317
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AttributionControl from './attribution_control';
|
|
2
2
|
import {createMap as globalCreateMap, setWebGlContext, setPerformance, setMatchMedia} from '../../util/test/util';
|
|
3
3
|
import simulate from '../../../test/unit/lib/simulate_interaction';
|
|
4
|
+
import {fakeServer} from 'nise';
|
|
4
5
|
|
|
5
6
|
function createMap() {
|
|
6
7
|
|
|
@@ -272,6 +273,67 @@ describe('AttributionControl', () => {
|
|
|
272
273
|
});
|
|
273
274
|
});
|
|
274
275
|
|
|
276
|
+
test('does not show attributions for sources that are used for terrain when they are not in use', done => {
|
|
277
|
+
global.fetch = null;
|
|
278
|
+
const server = fakeServer.create();
|
|
279
|
+
server.respondWith('/source.json', JSON.stringify({
|
|
280
|
+
minzoom: 5,
|
|
281
|
+
maxzoom: 12,
|
|
282
|
+
attribution: 'Terrain',
|
|
283
|
+
tiles: ['http://example.com/{z}/{x}/{y}.pngraw'],
|
|
284
|
+
bounds: [-47, -7, -45, -5]
|
|
285
|
+
}));
|
|
286
|
+
|
|
287
|
+
const attribution = new AttributionControl();
|
|
288
|
+
map.addControl(attribution);
|
|
289
|
+
|
|
290
|
+
map.on('load', () => {
|
|
291
|
+
map.addSource('1', {type: 'raster-dem', url: '/source.json'});
|
|
292
|
+
server.respond();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
let times = 0;
|
|
296
|
+
map.on('data', (e) => {
|
|
297
|
+
if (e.dataType === 'source' && e.sourceDataType === 'visibility') {
|
|
298
|
+
if (++times === 1) {
|
|
299
|
+
expect(attribution._innerContainer.innerHTML).toBe('');
|
|
300
|
+
done();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('shows attributions for sources that are used for terrain', done => {
|
|
307
|
+
global.fetch = null;
|
|
308
|
+
const server = fakeServer.create();
|
|
309
|
+
server.respondWith('/source.json', JSON.stringify({
|
|
310
|
+
minzoom: 5,
|
|
311
|
+
maxzoom: 12,
|
|
312
|
+
attribution: 'Terrain',
|
|
313
|
+
tiles: ['http://example.com/{z}/{x}/{y}.pngraw'],
|
|
314
|
+
bounds: [-47, -7, -45, -5]
|
|
315
|
+
}));
|
|
316
|
+
|
|
317
|
+
const attribution = new AttributionControl();
|
|
318
|
+
map.addControl(attribution);
|
|
319
|
+
|
|
320
|
+
map.on('load', () => {
|
|
321
|
+
map.addSource('1', {type: 'raster-dem', url: '/source.json'});
|
|
322
|
+
server.respond();
|
|
323
|
+
map.setTerrain({source: '1'});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
let times = 0;
|
|
327
|
+
map.on('data', (e) => {
|
|
328
|
+
if (e.dataType === 'source' && e.sourceDataType === 'visibility') {
|
|
329
|
+
if (++times === 1) {
|
|
330
|
+
expect(attribution._innerContainer.innerHTML).toBe('Terrain');
|
|
331
|
+
done();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
275
337
|
test('toggles attributions for sources whose visibility changes when zooming', done => {
|
|
276
338
|
const attribution = new AttributionControl();
|
|
277
339
|
map.addControl(attribution);
|
|
@@ -63,6 +63,7 @@ class AttributionControl implements IControl {
|
|
|
63
63
|
|
|
64
64
|
this._map.on('styledata', this._updateData);
|
|
65
65
|
this._map.on('sourcedata', this._updateData);
|
|
66
|
+
this._map.on('terrain', this._updateData);
|
|
66
67
|
this._map.on('resize', this._updateCompact);
|
|
67
68
|
this._map.on('drag', this._updateCompactMinimize);
|
|
68
69
|
|
|
@@ -74,6 +75,7 @@ class AttributionControl implements IControl {
|
|
|
74
75
|
|
|
75
76
|
this._map.off('styledata', this._updateData);
|
|
76
77
|
this._map.off('sourcedata', this._updateData);
|
|
78
|
+
this._map.off('terrain', this._updateData);
|
|
77
79
|
this._map.off('resize', this._updateCompact);
|
|
78
80
|
this._map.off('drag', this._updateCompactMinimize);
|
|
79
81
|
|
|
@@ -101,7 +103,7 @@ class AttributionControl implements IControl {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
_updateData(e: any) {
|
|
104
|
-
if (e && (e.sourceDataType === 'metadata' || e.sourceDataType === 'visibility' || e.dataType === 'style')) {
|
|
106
|
+
if (e && (e.sourceDataType === 'metadata' || e.sourceDataType === 'visibility' || e.dataType === 'style' || e.type === 'terrain')) {
|
|
105
107
|
this._updateAttributions();
|
|
106
108
|
}
|
|
107
109
|
}
|
|
@@ -131,7 +133,7 @@ class AttributionControl implements IControl {
|
|
|
131
133
|
const sourceCaches = this._map.style.sourceCaches;
|
|
132
134
|
for (const id in sourceCaches) {
|
|
133
135
|
const sourceCache = sourceCaches[id];
|
|
134
|
-
if (sourceCache.used) {
|
|
136
|
+
if (sourceCache.used || sourceCache.usedForTerrain) {
|
|
135
137
|
const source = sourceCache.getSource();
|
|
136
138
|
if (source.attribution && attributions.indexOf(source.attribution) < 0) {
|
|
137
139
|
attributions.push(source.attribution);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {Event, Evented} from '../../util/evented';
|
|
2
2
|
import DOM from '../../util/dom';
|
|
3
3
|
import {extend, bindAll, warnOnce} from '../../util/util';
|
|
4
|
-
import assert from 'assert';
|
|
5
4
|
import LngLat from '../../geo/lng_lat';
|
|
6
5
|
import Marker from '../marker';
|
|
7
6
|
|
|
@@ -195,7 +194,7 @@ class GeolocateControl extends Evented implements IControl {
|
|
|
195
194
|
case 'ACTIVE_ERROR':
|
|
196
195
|
break;
|
|
197
196
|
default:
|
|
198
|
-
|
|
197
|
+
throw new Error(`Unexpected watchState ${this._watchState}`);
|
|
199
198
|
}
|
|
200
199
|
}
|
|
201
200
|
|
|
@@ -244,7 +243,7 @@ class GeolocateControl extends Evented implements IControl {
|
|
|
244
243
|
this._geolocateButton.classList.add('maplibregl-ctrl-geolocate-background', 'mapboxgl-ctrl-geolocate-background');
|
|
245
244
|
break;
|
|
246
245
|
default:
|
|
247
|
-
|
|
246
|
+
throw new Error(`Unexpected watchState ${this._watchState}`);
|
|
248
247
|
}
|
|
249
248
|
}
|
|
250
249
|
|
|
@@ -306,7 +305,6 @@ class GeolocateControl extends Evented implements IControl {
|
|
|
306
305
|
}
|
|
307
306
|
|
|
308
307
|
_updateCircleRadius() {
|
|
309
|
-
assert(this._circleElement);
|
|
310
308
|
const y = this._map._container.clientHeight / 2;
|
|
311
309
|
const a = this._map.unproject([0, y]);
|
|
312
310
|
const b = this._map.unproject([1, y]);
|
|
@@ -485,7 +483,7 @@ class GeolocateControl extends Evented implements IControl {
|
|
|
485
483
|
this.fire(new Event('trackuserlocationstart'));
|
|
486
484
|
break;
|
|
487
485
|
default:
|
|
488
|
-
|
|
486
|
+
throw new Error(`Unexpected watchState ${this._watchState}`);
|
|
489
487
|
}
|
|
490
488
|
|
|
491
489
|
// incoming state setup
|
|
@@ -500,7 +498,7 @@ class GeolocateControl extends Evented implements IControl {
|
|
|
500
498
|
case 'OFF':
|
|
501
499
|
break;
|
|
502
500
|
default:
|
|
503
|
-
|
|
501
|
+
throw new Error(`Unexpected watchState ${this._watchState}`);
|
|
504
502
|
}
|
|
505
503
|
|
|
506
504
|
// manage geolocation.watchPosition / geolocation.clearWatch
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import TerrainControl from './terrain_control';
|
|
2
|
+
import {createMap as globalCreateMap, setWebGlContext, setPerformance, setMatchMedia} from '../../util/test/util';
|
|
3
|
+
|
|
4
|
+
function createMap() {
|
|
5
|
+
|
|
6
|
+
return globalCreateMap({
|
|
7
|
+
attributionControl: false,
|
|
8
|
+
style: {
|
|
9
|
+
version: 8,
|
|
10
|
+
sources: {
|
|
11
|
+
terrain: {
|
|
12
|
+
minzoom: 5,
|
|
13
|
+
maxzoom: 12,
|
|
14
|
+
attribution: 'MapLibre',
|
|
15
|
+
tiles: ['http://example.com/{z}/{x}/{y}.pngraw'],
|
|
16
|
+
bounds: [-47, -7, -45, -5]
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
layers: [],
|
|
20
|
+
owner: 'mapblibre',
|
|
21
|
+
id: 'demotiles',
|
|
22
|
+
},
|
|
23
|
+
hash: true
|
|
24
|
+
}, undefined);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let map;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
setWebGlContext();
|
|
31
|
+
setPerformance();
|
|
32
|
+
setMatchMedia();
|
|
33
|
+
map = createMap();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
map.remove();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('TerrainControl', () => {
|
|
41
|
+
test('appears in top-right by default', () => {
|
|
42
|
+
map.addControl(new TerrainControl({source: 'terrain'}));
|
|
43
|
+
|
|
44
|
+
expect(
|
|
45
|
+
map.getContainer().querySelectorAll('.maplibregl-ctrl-top-right .maplibregl-ctrl-terrain')
|
|
46
|
+
).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('appears in the position specified by the position option', () => {
|
|
50
|
+
map.addControl(new TerrainControl({source: 'terrain'}), 'bottom-right');
|
|
51
|
+
|
|
52
|
+
expect(
|
|
53
|
+
map.getContainer().querySelectorAll('.maplibregl-ctrl-bottom-right .maplibregl-ctrl-terrain')
|
|
54
|
+
).toHaveLength(1);
|
|
55
|
+
|
|
56
|
+
expect(
|
|
57
|
+
map.getContainer().querySelectorAll('.maplibregl-ctrl-top-right .maplibregl-ctrl-terrain')
|
|
58
|
+
).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import assert from 'assert';
|
|
2
1
|
import Point from '@mapbox/point-geometry';
|
|
3
2
|
|
|
4
3
|
export function indexTouches(touches: Array<Touch>, points: Array<Point>) {
|
|
5
|
-
|
|
4
|
+
if (touches.length !== points.length) throw new Error(`The number of touches and points are not equal - touches ${touches.length}, points ${points.length}`);
|
|
6
5
|
const obj = {};
|
|
7
6
|
for (let i = 0; i < touches.length; i++) {
|
|
8
7
|
obj[touches[i].identifier] = points[i];
|
|
@@ -4,7 +4,7 @@ import simulate from '../../../test/unit/lib/simulate_interaction';
|
|
|
4
4
|
import {setMatchMedia, setPerformance, setWebGlContext} from '../../util/test/util';
|
|
5
5
|
|
|
6
6
|
function createMap() {
|
|
7
|
-
return new Map({interactive:
|
|
7
|
+
return new Map({interactive: true, container: DOM.create('div', '', window.document.body)} as any as MapOptions);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
beforeEach(() => {
|
|
@@ -93,4 +93,68 @@ describe('map events', () => {
|
|
|
93
93
|
|
|
94
94
|
map.remove();
|
|
95
95
|
});
|
|
96
|
+
|
|
97
|
+
test('MapEvent handler fires contextmenu on MacOS/Linux, but only at mouseup', () => {
|
|
98
|
+
const map = createMap();
|
|
99
|
+
const target = map.getCanvas();
|
|
100
|
+
map.dragPan.enable();
|
|
101
|
+
|
|
102
|
+
const contextmenu = jest.fn();
|
|
103
|
+
|
|
104
|
+
map.on('contextmenu', contextmenu);
|
|
105
|
+
|
|
106
|
+
simulate.mousedown(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
107
|
+
simulate.contextmenu(map.getCanvas(), {target}); // triggered immediately after mousedown
|
|
108
|
+
expect(contextmenu).toHaveBeenCalledTimes(0);
|
|
109
|
+
simulate.mouseup(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
110
|
+
expect(contextmenu).toHaveBeenCalledTimes(1);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('MapEvent handler does not fire contextmenu on MacOS/Linux, when moved', () => {
|
|
114
|
+
const map = createMap();
|
|
115
|
+
const target = map.getCanvas();
|
|
116
|
+
map.dragPan.enable();
|
|
117
|
+
|
|
118
|
+
const contextmenu = jest.fn();
|
|
119
|
+
|
|
120
|
+
map.on('contextmenu', contextmenu);
|
|
121
|
+
|
|
122
|
+
simulate.mousedown(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
123
|
+
simulate.contextmenu(map.getCanvas(), {target}); // triggered immediately after mousedown
|
|
124
|
+
simulate.mousemove(map.getCanvas(), {target, buttons: 2, clientX: 50, clientY: 10});
|
|
125
|
+
simulate.mouseup(map.getCanvas(), {target, button: 2, clientX: 70, clientY: 10});
|
|
126
|
+
expect(contextmenu).toHaveBeenCalledTimes(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('MapEvent handler fires contextmenu on Windows', () => {
|
|
130
|
+
const map = createMap();
|
|
131
|
+
const target = map.getCanvas();
|
|
132
|
+
map.dragPan.enable();
|
|
133
|
+
|
|
134
|
+
const contextmenu = jest.fn();
|
|
135
|
+
|
|
136
|
+
map.on('contextmenu', contextmenu);
|
|
137
|
+
|
|
138
|
+
simulate.mousedown(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
139
|
+
simulate.mouseup(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
140
|
+
expect(contextmenu).toHaveBeenCalledTimes(0);
|
|
141
|
+
simulate.contextmenu(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10}); // triggered only after mouseup
|
|
142
|
+
expect(contextmenu).toHaveBeenCalledTimes(1);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('MapEvent handler does not fire contextmenu on Windows, when moved', () => {
|
|
146
|
+
const map = createMap();
|
|
147
|
+
const target = map.getCanvas();
|
|
148
|
+
map.dragPan.enable();
|
|
149
|
+
|
|
150
|
+
const contextmenu = jest.fn();
|
|
151
|
+
|
|
152
|
+
map.on('contextmenu', contextmenu);
|
|
153
|
+
|
|
154
|
+
simulate.mousedown(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10});
|
|
155
|
+
simulate.mousemove(map.getCanvas(), {target, buttons: 2, clientX: 50, clientY: 10});
|
|
156
|
+
simulate.mouseup(map.getCanvas(), {target, button: 2, clientX: 50, clientY: 10});
|
|
157
|
+
simulate.contextmenu(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10}); // triggered only after mouseup
|
|
158
|
+
expect(contextmenu).toHaveBeenCalledTimes(0);
|
|
159
|
+
});
|
|
96
160
|
});
|
|
@@ -104,6 +104,7 @@ export class MapEventHandler implements Handler {
|
|
|
104
104
|
export class BlockableMapEventHandler {
|
|
105
105
|
_map: Map;
|
|
106
106
|
_delayContextMenu: boolean;
|
|
107
|
+
_ignoreContextMenu: boolean;
|
|
107
108
|
_contextMenuEvent: MouseEvent;
|
|
108
109
|
|
|
109
110
|
constructor(map: Map) {
|
|
@@ -112,6 +113,7 @@ export class BlockableMapEventHandler {
|
|
|
112
113
|
|
|
113
114
|
reset() {
|
|
114
115
|
this._delayContextMenu = false;
|
|
116
|
+
this._ignoreContextMenu = true;
|
|
115
117
|
delete this._contextMenuEvent;
|
|
116
118
|
}
|
|
117
119
|
|
|
@@ -122,6 +124,7 @@ export class BlockableMapEventHandler {
|
|
|
122
124
|
|
|
123
125
|
mousedown() {
|
|
124
126
|
this._delayContextMenu = true;
|
|
127
|
+
this._ignoreContextMenu = false;
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
mouseup() {
|
|
@@ -135,7 +138,7 @@ export class BlockableMapEventHandler {
|
|
|
135
138
|
if (this._delayContextMenu) {
|
|
136
139
|
// Mac: contextmenu fired on mousedown; we save it until mouseup for consistency's sake
|
|
137
140
|
this._contextMenuEvent = e;
|
|
138
|
-
} else {
|
|
141
|
+
} else if (!this._ignoreContextMenu) {
|
|
139
142
|
// Windows: contextmenu fired on mouseup, so fire event now
|
|
140
143
|
this._map.fire(new MapMouseEvent(e.type, this._map, e));
|
|
141
144
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import assert from 'assert';
|
|
2
1
|
import DOM from '../../util/dom';
|
|
3
2
|
|
|
4
3
|
import {ease as _ease, bindAll, bezier} from '../../util/util';
|
|
@@ -282,7 +281,6 @@ class ScrollZoomHandler {
|
|
|
282
281
|
let finished = false;
|
|
283
282
|
let zoom;
|
|
284
283
|
if (this._type === 'wheel' && startZoom && easing) {
|
|
285
|
-
assert(easing && typeof startZoom === 'number');
|
|
286
284
|
|
|
287
285
|
const t = Math.min((browser.now() - this._lastWheelEventTime) / 200, 1);
|
|
288
286
|
const k = easing(t);
|
|
@@ -19,7 +19,6 @@ import TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate';
|
|
|
19
19
|
import {bindAll, extend} from '../util/util';
|
|
20
20
|
import Point from '@mapbox/point-geometry';
|
|
21
21
|
import LngLat from '../geo/lng_lat';
|
|
22
|
-
import assert from 'assert';
|
|
23
22
|
|
|
24
23
|
export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent;
|
|
25
24
|
|
|
@@ -299,7 +298,6 @@ class HandlerManager {
|
|
|
299
298
|
}
|
|
300
299
|
|
|
301
300
|
this._updatingCamera = true;
|
|
302
|
-
assert(e.timeStamp !== undefined);
|
|
303
301
|
|
|
304
302
|
const inputEvent = e.type === 'renderFrame' ? undefined : (e as any as InputEvent);
|
|
305
303
|
|