maplibre-gl 2.2.0-pre.2 → 2.2.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.
Files changed (169) hide show
  1. package/README.md +73 -8
  2. package/build/generate-debug-index-file.ts +19 -0
  3. package/build/generate-style-code.ts +6 -1
  4. package/build/generate-style-spec.ts +151 -35
  5. package/build/rollup_plugins.ts +4 -1
  6. package/dist/maplibre-gl-csp-worker.js +1 -1
  7. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  8. package/dist/maplibre-gl-csp.js +1 -1
  9. package/dist/maplibre-gl-csp.js.map +1 -1
  10. package/dist/maplibre-gl-dev.js +1317 -4304
  11. package/dist/maplibre-gl.css +1 -1
  12. package/dist/maplibre-gl.d.ts +443 -145
  13. package/dist/maplibre-gl.js +4 -4
  14. package/dist/maplibre-gl.js.map +1 -1
  15. package/package.json +70 -67
  16. package/src/css/maplibre-gl.css +48 -32
  17. package/src/data/bucket/fill_bucket.test.ts +1 -0
  18. package/src/data/bucket/symbol_bucket.test.ts +2 -0
  19. package/src/data/bucket/symbol_bucket.ts +1 -1
  20. package/src/data/evaluation_feature.ts +1 -1
  21. package/src/data/program_configuration.ts +2 -2
  22. package/src/geo/transform.test.ts +34 -1
  23. package/src/geo/transform.ts +25 -15
  24. package/src/gl/vertex_buffer.ts +4 -4
  25. package/src/index.ts +1 -1
  26. package/src/render/draw_debug.ts +1 -1
  27. package/src/render/draw_symbol.test.ts +2 -23
  28. package/src/render/draw_terrain.ts +1 -1
  29. package/src/render/image_atlas.ts +1 -0
  30. package/src/render/image_manager.ts +1 -0
  31. package/src/render/program/debug_program.ts +1 -1
  32. package/src/render/render_to_texture.ts +3 -0
  33. package/src/render/terrain.test.ts +119 -17
  34. package/src/render/terrain.ts +39 -21
  35. package/src/shaders/README.md +2 -2
  36. package/src/shaders/shaders.ts +3 -1
  37. package/src/source/geojson_worker_source.test.ts +2 -2
  38. package/src/source/geojson_wrapper.test.ts +1 -1
  39. package/src/source/image_source.test.ts +8 -8
  40. package/src/source/image_source.ts +1 -1
  41. package/src/source/load_tilejson.ts +6 -1
  42. package/src/source/pixels_to_tile_units.ts +1 -1
  43. package/src/source/raster_tile_source.test.ts +1 -1
  44. package/src/source/source_cache.test.ts +12 -12
  45. package/src/source/source_cache.ts +1 -1
  46. package/src/source/terrain_source_cache.test.ts +17 -2
  47. package/src/source/terrain_source_cache.ts +16 -12
  48. package/src/source/vector_tile_source.test.ts +1 -1
  49. package/src/source/vector_tile_worker_source.test.ts +1 -1
  50. package/src/source/video_source.test.ts +2 -2
  51. package/src/style/light.test.ts +1 -1
  52. package/src/style/load_sprite.ts +1 -1
  53. package/src/style/parse_glyph_pbf.ts +1 -1
  54. package/src/style/style.test.ts +3 -3
  55. package/src/style/style.ts +2 -2
  56. package/src/style/style_layer/background_style_layer_properties.g.ts +1 -0
  57. package/src/style/style_layer/circle_style_layer_properties.g.ts +1 -0
  58. package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +1 -0
  59. package/src/style/style_layer/fill_style_layer_properties.g.ts +1 -0
  60. package/src/style/style_layer/heatmap_style_layer_properties.g.ts +1 -0
  61. package/src/style/style_layer/hillshade_style_layer_properties.g.ts +1 -0
  62. package/src/style/style_layer/line_style_layer_properties.g.ts +1 -0
  63. package/src/style/style_layer/raster_style_layer_properties.g.ts +1 -0
  64. package/src/style/style_layer/symbol_style_layer.ts +16 -1
  65. package/src/style/style_layer/symbol_style_layer_properties.g.ts +4 -3
  66. package/src/style-spec/CHANGELOG.md +5 -0
  67. package/src/style-spec/composite.test.ts +2 -0
  68. package/src/style-spec/composite.ts +3 -2
  69. package/src/style-spec/diff.test.ts +3 -3
  70. package/src/style-spec/empty.ts +3 -2
  71. package/src/style-spec/expression/compound_expression.ts +0 -4
  72. package/src/style-spec/expression/definitions/assertion.ts +0 -18
  73. package/src/style-spec/expression/definitions/at.ts +0 -4
  74. package/src/style-spec/expression/definitions/case.ts +0 -6
  75. package/src/style-spec/expression/definitions/coalesce.ts +0 -6
  76. package/src/style-spec/expression/definitions/coercion.ts +13 -18
  77. package/src/style-spec/expression/definitions/collator.ts +0 -10
  78. package/src/style-spec/expression/definitions/comparison.ts +0 -6
  79. package/src/style-spec/expression/definitions/format.ts +0 -19
  80. package/src/style-spec/expression/definitions/image.ts +0 -4
  81. package/src/style-spec/expression/definitions/in.ts +0 -4
  82. package/src/style-spec/expression/definitions/index_of.ts +0 -8
  83. package/src/style-spec/expression/definitions/interpolate.ts +1 -25
  84. package/src/style-spec/expression/definitions/length.ts +0 -6
  85. package/src/style-spec/expression/definitions/let.ts +0 -9
  86. package/src/style-spec/expression/definitions/literal.ts +1 -23
  87. package/src/style-spec/expression/definitions/match.ts +0 -41
  88. package/src/style-spec/expression/definitions/number_format.ts +0 -17
  89. package/src/style-spec/expression/definitions/slice.ts +0 -8
  90. package/src/style-spec/expression/definitions/step.ts +0 -11
  91. package/src/style-spec/expression/definitions/var.ts +0 -4
  92. package/src/style-spec/expression/definitions/within.ts +0 -5
  93. package/src/style-spec/expression/expression.test.ts +1 -1
  94. package/src/style-spec/expression/expression.ts +3 -3
  95. package/src/style-spec/expression/index.ts +8 -2
  96. package/src/style-spec/expression/parsing_context.ts +2 -0
  97. package/src/style-spec/expression/types/formatted.ts +0 -23
  98. package/src/style-spec/expression/types/resolved_image.ts +0 -4
  99. package/src/style-spec/expression/types.ts +6 -1
  100. package/src/style-spec/expression/values.ts +9 -4
  101. package/src/style-spec/feature_filter/convert.ts +65 -65
  102. package/src/style-spec/feature_filter/feature_filter.test.ts +45 -4
  103. package/src/style-spec/feature_filter/index.ts +2 -1
  104. package/src/style-spec/function/index.test.ts +117 -1
  105. package/src/style-spec/function/index.ts +24 -12
  106. package/src/style-spec/migrate/expressions.ts +2 -2
  107. package/src/style-spec/migrate/v8.test.ts +2 -0
  108. package/src/style-spec/migrate/v8.ts +8 -7
  109. package/src/style-spec/migrate/v9.test.ts +6 -4
  110. package/src/style-spec/migrate/v9.ts +3 -2
  111. package/src/style-spec/migrate.test.ts +3 -1
  112. package/src/style-spec/migrate.ts +5 -4
  113. package/src/style-spec/package.json +1 -1
  114. package/src/style-spec/read_style.ts +2 -1
  115. package/src/style-spec/reference/latest.ts +1 -1
  116. package/src/style-spec/reference/v8.json +9 -6
  117. package/src/style-spec/style-spec.test.ts +2 -1
  118. package/src/style-spec/style-spec.ts +8 -0
  119. package/src/style-spec/types.g.ts +152 -36
  120. package/src/style-spec/util/extend.ts +1 -1
  121. package/src/style-spec/util/interpolate.test.ts +5 -0
  122. package/src/style-spec/util/interpolate.ts +12 -0
  123. package/src/style-spec/util/padding.test.ts +27 -0
  124. package/src/style-spec/util/padding.ts +64 -0
  125. package/src/style-spec/util/ref_properties.ts +2 -1
  126. package/src/style-spec/validate/validate.ts +3 -1
  127. package/src/style-spec/validate/validate_expression.ts +2 -1
  128. package/src/style-spec/validate/validate_function.ts +2 -2
  129. package/src/style-spec/validate/validate_glyphs_url.ts +1 -1
  130. package/src/style-spec/validate/validate_object.ts +2 -2
  131. package/src/style-spec/validate/validate_padding.test.ts +82 -0
  132. package/src/style-spec/validate/validate_padding.ts +36 -0
  133. package/src/style-spec/validate_style.min.ts +4 -3
  134. package/src/style-spec/validate_style.ts +4 -3
  135. package/src/symbol/check_max_angle.test.ts +5 -5
  136. package/src/symbol/collision_feature.test.ts +22 -5
  137. package/src/symbol/collision_feature.ts +7 -5
  138. package/src/symbol/collision_index.ts +1 -1
  139. package/src/symbol/get_anchors.test.ts +4 -4
  140. package/src/symbol/{mergelines.test.ts → merge_lines.test.ts} +1 -1
  141. package/src/symbol/{mergelines.ts → merge_lines.ts} +1 -1
  142. package/src/symbol/projection.ts +1 -1
  143. package/src/symbol/quads.test.ts +1 -1
  144. package/src/symbol/shaping.ts +10 -10
  145. package/src/symbol/symbol_layout.ts +5 -4
  146. package/src/symbol/symbol_style_layer.test.ts +1 -1
  147. package/src/symbol/transform_text.ts +3 -3
  148. package/src/ui/camera.test.ts +11 -11
  149. package/src/ui/control/geolocate_control.ts +1 -1
  150. package/src/ui/control/terrain_control.ts +4 -4
  151. package/src/ui/handler/cooperative_gestures.test.ts +167 -0
  152. package/src/ui/handler/drag_pan.test.ts +2 -1
  153. package/src/ui/handler/scroll_zoom.ts +7 -0
  154. package/src/ui/handler/touch_pan.ts +22 -2
  155. package/src/ui/handler/touch_zoom_rotate.ts +18 -1
  156. package/src/ui/handler_manager.ts +2 -2
  157. package/src/ui/map.test.ts +17 -17
  158. package/src/ui/map.ts +76 -8
  159. package/src/ui/map_events.test.ts +33 -32
  160. package/src/ui/popup.test.ts +2 -2
  161. package/src/util/ajax.test.ts +5 -5
  162. package/src/util/ajax.ts +1 -1
  163. package/src/util/classify_rings.test.ts +27 -27
  164. package/src/util/find_pole_of_inaccessibility.ts +1 -1
  165. package/src/util/primitives.ts +4 -4
  166. package/src/util/resolve_tokens.test.ts +1 -1
  167. package/src/util/smart_wrap.ts +1 -1
  168. package/src/util/tile_request_cache.test.ts +5 -5
  169. package/src/util/util.test.ts +5 -5
@@ -1,5 +1,7 @@
1
1
  import Point from '@mapbox/point-geometry';
2
2
  import {indexTouches} from './handler_util';
3
+ import type Map from '../map';
4
+ import {GestureOptions} from '../map';
3
5
 
4
6
  export default class TouchPanHandler {
5
7
 
@@ -11,12 +13,16 @@ export default class TouchPanHandler {
11
13
  _minTouches: number;
12
14
  _clickTolerance: number;
13
15
  _sum: Point;
16
+ _map: Map;
17
+ _cancelCooperativeMessage: boolean;
14
18
 
15
19
  constructor(options: {
16
20
  clickTolerance: number;
17
- }) {
18
- this._minTouches = 1;
21
+ cooperativeGestures: boolean | GestureOptions;
22
+ }, map: Map) {
23
+ this._minTouches = options.cooperativeGestures ? 2 : 1;
19
24
  this._clickTolerance = options.clickTolerance || 1;
25
+ this._map = map;
20
26
  this.reset();
21
27
  }
22
28
 
@@ -24,6 +30,11 @@ export default class TouchPanHandler {
24
30
  this._active = false;
25
31
  this._touches = {};
26
32
  this._sum = new Point(0, 0);
33
+
34
+ // Put a delay on the cooperative gesture message so it's less twitchy
35
+ setTimeout(() => {
36
+ this._cancelCooperativeMessage = false;
37
+ }, 200);
27
38
  }
28
39
 
29
40
  touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
@@ -31,6 +42,15 @@ export default class TouchPanHandler {
31
42
  }
32
43
 
33
44
  touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
45
+ if (this._map._cooperativeGestures) {
46
+ if (this._minTouches === 2 && mapTouches.length < 2 && !this._cancelCooperativeMessage) {
47
+ // If coop gesture enabled, show panning info to user
48
+ this._map._onCooperativeGesture(e, false, mapTouches.length);
49
+ } else if (!this._cancelCooperativeMessage) {
50
+ // If user is successfully navigating, we don't need this warning until the touch resets
51
+ this._cancelCooperativeMessage = true;
52
+ }
53
+ }
34
54
  if (!this._active || mapTouches.length < this._minTouches) return;
35
55
  e.preventDefault();
36
56
  return this._calculateTransform(e, points, mapTouches);
@@ -1,5 +1,6 @@
1
1
  import Point from '@mapbox/point-geometry';
2
2
  import DOM from '../../util/dom';
3
+ import type Map from '../map';
3
4
 
4
5
  class TwoTouchHandler {
5
6
 
@@ -23,7 +24,6 @@ class TwoTouchHandler {
23
24
  _move(points: [Point, Point], pinchAround: Point, e: TouchEvent) { return {}; } //eslint-disable-line
24
25
 
25
26
  touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
26
- //console.log(e.target, e.targetTouches.length ? e.targetTouches[0].target : null);
27
27
  //log('touchstart', points, e.target.innerHTML, e.targetTouches.length ? e.targetTouches[0].target.innerHTML: undefined);
28
28
  if (this._firstTwoTouches || mapTouches.length < 2) return;
29
29
 
@@ -203,6 +203,13 @@ export class TouchPitchHandler extends TwoTouchHandler {
203
203
  _valid: boolean | void;
204
204
  _firstMove: number;
205
205
  _lastPoints: [Point, Point];
206
+ _map: Map;
207
+ _currentTouchCount: number;
208
+
209
+ constructor(map: Map) {
210
+ super();
211
+ this._map = map;
212
+ }
206
213
 
207
214
  reset() {
208
215
  super.reset();
@@ -211,6 +218,11 @@ export class TouchPitchHandler extends TwoTouchHandler {
211
218
  delete this._lastPoints;
212
219
  }
213
220
 
221
+ touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
222
+ super.touchstart(e, points, mapTouches);
223
+ this._currentTouchCount = mapTouches.length;
224
+ }
225
+
214
226
  _start(points: [Point, Point]) {
215
227
  this._lastPoints = points;
216
228
  if (isVertical(points[0].sub(points[1]))) {
@@ -221,6 +233,11 @@ export class TouchPitchHandler extends TwoTouchHandler {
221
233
  }
222
234
 
223
235
  _move(points: [Point, Point], center: Point, e: TouchEvent) {
236
+ // If cooperative gestures is enabled, we need a 3-finger minimum for this gesture to register
237
+ if (this._map._cooperativeGestures && this._currentTouchCount < 3) {
238
+ return;
239
+ }
240
+
224
241
  const vectorA = points[0].sub(this._lastPoints[0]);
225
242
  const vectorB = points[1].sub(this._lastPoints[1]);
226
243
 
@@ -195,7 +195,7 @@ class HandlerManager {
195
195
  const tapDragZoom = new TapDragZoomHandler();
196
196
  this._add('tapDragZoom', tapDragZoom);
197
197
 
198
- const touchPitch = map.touchPitch = new TouchPitchHandler();
198
+ const touchPitch = map.touchPitch = new TouchPitchHandler(map);
199
199
  this._add('touchPitch', touchPitch);
200
200
 
201
201
  const mouseRotate = new MouseRotateHandler(options);
@@ -205,7 +205,7 @@ class HandlerManager {
205
205
  this._add('mousePitch', mousePitch, ['mouseRotate']);
206
206
 
207
207
  const mousePan = new MousePanHandler(options);
208
- const touchPan = new TouchPanHandler(options);
208
+ const touchPan = new TouchPanHandler(options, map);
209
209
  map.dragPan = new DragPanHandler(el, mousePan, touchPan);
210
210
  this._add('mousePan', mousePan);
211
211
  this._add('touchPan', touchPan, ['touchZoom', 'touchRotate']);
@@ -227,7 +227,7 @@ describe('Map', () => {
227
227
  map.transform.lngRange = [-120, 140];
228
228
  map.transform.latRange = [-60, 80];
229
229
  map.transform.resize(600, 400);
230
- expect(map.transform.zoom).toBe(0.6983039737971012);
230
+ expect(map.transform.zoom).toBe(0.6983039737971014);
231
231
  expect(map.transform.unmodified).toBeTruthy();
232
232
  map.setStyle(createStyle());
233
233
  map.on('style.load', () => {
@@ -598,8 +598,8 @@ describe('Map', () => {
598
598
  expect(parseFloat(map.getBounds().getCenter().lat.toFixed(10))).toBe(0);
599
599
 
600
600
  expect(toFixed(map.getBounds().toArray())).toEqual(toFixed([
601
- [ -70.31249999999976, -57.326521225216965 ],
602
- [ 70.31249999999977, 57.32652122521695 ] ]));
601
+ [-70.31249999999976, -57.326521225216965],
602
+ [70.31249999999977, 57.32652122521695]]));
603
603
  });
604
604
 
605
605
  test('rotated bounds', () => {
@@ -631,7 +631,7 @@ describe('Map', () => {
631
631
 
632
632
  describe('#setMaxBounds', () => {
633
633
  test('constrains map bounds', () => {
634
- const map = createMap({zoom:0});
634
+ const map = createMap({zoom: 0});
635
635
  map.setMaxBounds([[-130.4297, 50.0642], [-61.52344, 24.20688]]);
636
636
  expect(
637
637
  toFixed([[-130.4297000000, 7.0136641176], [-61.5234400000, 60.2398142283]])
@@ -639,7 +639,7 @@ describe('Map', () => {
639
639
  });
640
640
 
641
641
  test('when no argument is passed, map bounds constraints are removed', () => {
642
- const map = createMap({zoom:0});
642
+ const map = createMap({zoom: 0});
643
643
  map.setMaxBounds([[-130.4297, 50.0642], [-61.52344, 24.20688]]);
644
644
  expect(
645
645
  toFixed([[-166.28906999999964, -27.6835270554], [-25.664070000000066, 73.8248206697]])
@@ -664,12 +664,12 @@ describe('Map', () => {
664
664
 
665
665
  describe('#getMaxBounds', () => {
666
666
  test('returns null when no bounds set', () => {
667
- const map = createMap({zoom:0});
667
+ const map = createMap({zoom: 0});
668
668
  expect(map.getMaxBounds()).toBeNull();
669
669
  });
670
670
 
671
671
  test('returns bounds', () => {
672
- const map = createMap({zoom:0});
672
+ const map = createMap({zoom: 0});
673
673
  const bounds = [[-130.4297, 50.0642], [-61.52344, 24.20688]] as LngLatBoundsLike;
674
674
  map.setMaxBounds(bounds);
675
675
  expect(map.getMaxBounds().toArray()).toEqual(bounds);
@@ -718,14 +718,14 @@ describe('Map', () => {
718
718
  });
719
719
 
720
720
  test('#setMinZoom', () => {
721
- const map = createMap({zoom:5});
721
+ const map = createMap({zoom: 5});
722
722
  map.setMinZoom(3.5);
723
723
  map.setZoom(1);
724
724
  expect(map.getZoom()).toBe(3.5);
725
725
  });
726
726
 
727
727
  test('unset minZoom', () => {
728
- const map = createMap({minZoom:5});
728
+ const map = createMap({minZoom: 5});
729
729
  map.setMinZoom(null);
730
730
  map.setZoom(1);
731
731
  expect(map.getZoom()).toBe(1);
@@ -739,7 +739,7 @@ describe('Map', () => {
739
739
  });
740
740
 
741
741
  test('ignore minZooms over maxZoom', () => {
742
- const map = createMap({zoom:2, maxZoom:5});
742
+ const map = createMap({zoom: 2, maxZoom: 5});
743
743
  expect(() => {
744
744
  map.setMinZoom(6);
745
745
  }).toThrow();
@@ -748,14 +748,14 @@ describe('Map', () => {
748
748
  });
749
749
 
750
750
  test('#setMaxZoom', () => {
751
- const map = createMap({zoom:0});
751
+ const map = createMap({zoom: 0});
752
752
  map.setMaxZoom(3.5);
753
753
  map.setZoom(4);
754
754
  expect(map.getZoom()).toBe(3.5);
755
755
  });
756
756
 
757
757
  test('unset maxZoom', () => {
758
- const map = createMap({maxZoom:5});
758
+ const map = createMap({maxZoom: 5});
759
759
  map.setMaxZoom(null);
760
760
  map.setZoom(6);
761
761
  expect(map.getZoom()).toBe(6);
@@ -769,7 +769,7 @@ describe('Map', () => {
769
769
  });
770
770
 
771
771
  test('ignore maxZooms over minZoom', () => {
772
- const map = createMap({minZoom:5});
772
+ const map = createMap({minZoom: 5});
773
773
  expect(() => {
774
774
  map.setMaxZoom(4);
775
775
  }).toThrow();
@@ -779,13 +779,13 @@ describe('Map', () => {
779
779
 
780
780
  test('throw on maxZoom smaller than minZoom at init', () => {
781
781
  expect(() => {
782
- createMap({minZoom:10, maxZoom:5});
782
+ createMap({minZoom: 10, maxZoom: 5});
783
783
  }).toThrow(new Error('maxZoom must be greater than or equal to minZoom'));
784
784
  });
785
785
 
786
786
  test('throw on maxZoom smaller than minZoom at init with falsey maxZoom', () => {
787
787
  expect(() => {
788
- createMap({minZoom:1, maxZoom:0});
788
+ createMap({minZoom: 1, maxZoom: 0});
789
789
  }).toThrow(new Error('maxZoom must be greater than or equal to minZoom'));
790
790
  });
791
791
 
@@ -827,7 +827,7 @@ describe('Map', () => {
827
827
  });
828
828
 
829
829
  test('unset maxPitch', () => {
830
- const map = createMap({maxPitch:10});
830
+ const map = createMap({maxPitch: 10});
831
831
  map.setMaxPitch(null);
832
832
  map.setPitch(20);
833
833
  expect(map.getPitch()).toBe(20);
@@ -841,7 +841,7 @@ describe('Map', () => {
841
841
  });
842
842
 
843
843
  test('ignore maxPitchs over minPitch', () => {
844
- const map = createMap({minPitch:10});
844
+ const map = createMap({minPitch: 10});
845
845
  expect(() => {
846
846
  map.setMaxPitch(0);
847
847
  }).toThrow();
package/src/ui/map.ts CHANGED
@@ -59,7 +59,6 @@ import type {ControlPosition, IControl} from './control/control';
59
59
  import type {MapGeoJSONFeature} from '../util/vectortile_to_geojson';
60
60
 
61
61
  /* eslint-enable no-use-before-define */
62
-
63
62
  export type MapOptions = {
64
63
  hash?: boolean | string;
65
64
  interactive?: boolean;
@@ -86,6 +85,7 @@ export type MapOptions = {
86
85
  doubleClickZoom?: boolean;
87
86
  touchZoomRotate?: boolean;
88
87
  touchPitch?: boolean;
88
+ cooperativeGestures?: boolean | GestureOptions;
89
89
  trackResize?: boolean;
90
90
  center?: LngLatLike;
91
91
  zoom?: number;
@@ -107,6 +107,12 @@ export type MapOptions = {
107
107
  pixelRatio?: number;
108
108
  };
109
109
 
110
+ export type GestureOptions = {
111
+ windowsHelpText?: string;
112
+ macHelpText?: string;
113
+ mobileHelpText?: string;
114
+ };
115
+
110
116
  // See article here: https://medium.com/terria/typescript-transforming-optional-properties-to-required-properties-that-may-be-undefined-7482cb4e1585
111
117
  type Complete<T> = {
112
118
  [P in keyof Required<T>]: Pick<T, P> extends Required<Pick<T, P>> ? T[P] : (T[P] | undefined);
@@ -146,6 +152,7 @@ const defaultOptions = {
146
152
  doubleClickZoom: true,
147
153
  touchZoomRotate: true,
148
154
  touchPitch: true,
155
+ cooperativeGestures: undefined,
149
156
 
150
157
  bearingSnap: 7,
151
158
  clickTolerance: 3,
@@ -217,6 +224,13 @@ const defaultOptions = {
217
224
  * @param {boolean} [options.doubleClickZoom=true] If `true`, the "double click to zoom" interaction is enabled (see {@link DoubleClickZoomHandler}).
218
225
  * @param {boolean|Object} [options.touchZoomRotate=true] If `true`, the "pinch to rotate and zoom" interaction is enabled. An `Object` value is passed as options to {@link TouchZoomRotateHandler#enable}.
219
226
  * @param {boolean|Object} [options.touchPitch=true] If `true`, the "drag to pitch" interaction is enabled. An `Object` value is passed as options to {@link TouchPitchHandler#enable}.
227
+ * @param {boolean|GestureOptions} [options.cooperativeGestures=undefined] If `true` or set to an options object, map is only accessible on desktop while holding Command/Ctrl and only accessible on mobile with two fingers. Interacting with the map using normal gestures will trigger an informational screen. With this option enabled, "drag to pitch" requires a three-finger gesture.
228
+ * A valid options object includes the following properties to customize the text on the informational screen. The values below are the defaults.
229
+ * {
230
+ * windowsHelpText: "Use Ctrl + scroll to zoom the map",
231
+ * macHelpText: "Use ⌘ + scroll to zoom the map",
232
+ * mobileHelpText: "Use two fingers to move the map",
233
+ * }
220
234
  * @param {boolean} [options.trackResize=true] If `true`, the map will automatically resize when the browser window resizes.
221
235
  * @param {LngLatLike} [options.center=[0, 0]] The initial geographical centerpoint of the map. If `center` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `[0, 0]` Note: MapLibre GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
222
236
  * @param {number} [options.zoom=0] The initial zoom level of the map. If `zoom` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`.
@@ -271,6 +285,9 @@ class Map extends Camera {
271
285
  _controlContainer: HTMLElement;
272
286
  _controlPositions: {[_: string]: HTMLElement};
273
287
  _interactive: boolean;
288
+ _cooperativeGestures: boolean | GestureOptions;
289
+ _cooperativeGesturesScreen: HTMLElement;
290
+ _metaPress: boolean;
274
291
  _showTileBoundaries: boolean;
275
292
  _showCollisionBoxes: boolean;
276
293
  _showPadding: boolean;
@@ -381,6 +398,7 @@ class Map extends Camera {
381
398
  super(transform, {bearingSnap: options.bearingSnap});
382
399
 
383
400
  this._interactive = options.interactive;
401
+ this._cooperativeGestures = options.cooperativeGestures;
384
402
  this._maxTileCacheSize = options.maxTileCacheSize;
385
403
  this._failIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
386
404
  this._preserveDrawingBuffer = options.preserveDrawingBuffer;
@@ -446,6 +464,10 @@ class Map extends Camera {
446
464
 
447
465
  this.handlers = new HandlerManager(this, options as CompleteMapOptions);
448
466
 
467
+ if (this._cooperativeGestures) {
468
+ this._setupCooperativeGestures();
469
+ }
470
+
449
471
  const hashName = (typeof options.hash === 'string' && options.hash) || undefined;
450
472
  this._hash = options.hash && (new Hash(hashName)).addTo(this);
451
473
  // don't set position from options if set through hash
@@ -1039,7 +1061,7 @@ class Map extends Camera {
1039
1061
  * | [`dataabort`](#map.event:dataabort) | |
1040
1062
  * | [`sourcedataabort`](#map.event:sourcedataabort) | |
1041
1063
  *
1042
- * @param {string | Listener} layerIdOrListener The ID of a style layer or a listener if no ID is provided. Event will only be triggered if its location
1064
+ * @param {string | Listener} layer The ID of a style layer or a listener if no ID is provided. Event will only be triggered if its location
1043
1065
  * is within a visible feature in this layer. The event will have a `features` property containing
1044
1066
  * an array of the matching features. If `layerIdOrListener` is not supplied, the event will not have a `features` property.
1045
1067
  * Please note that many event types are not compatible with the optional `layerIdOrListener` parameter.
@@ -1129,7 +1151,7 @@ class Map extends Camera {
1129
1151
  * a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave`
1130
1152
  * and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves
1131
1153
  * the map canvas.
1132
- * @param {string} layerIdOrListener The ID of a style layer or a listener if no ID is provided. Only events whose location is within a visible
1154
+ * @param {string} layer The ID of a style layer or a listener if no ID is provided. Only events whose location is within a visible
1133
1155
  * feature in this layer will trigger the listener. The event will have a `features` property containing
1134
1156
  * an array of the matching features.
1135
1157
  * @param {Function} listener The function to be called when the event is fired.
@@ -1173,7 +1195,7 @@ class Map extends Camera {
1173
1195
  * Removes an event listener for layer-specific events previously added with `Map#on`.
1174
1196
  *
1175
1197
  * @param {string} type The event type previously used to install the listener.
1176
- * @param {string} layerIdOrListener The layer ID or listener previously used to install the listener.
1198
+ * @param {string} layer The layer ID or listener previously used to install the listener.
1177
1199
  * @param {Function} listener The function previously installed as a listener.
1178
1200
  * @returns {Map} `this`
1179
1201
  */
@@ -1892,7 +1914,10 @@ class Map extends Camera {
1892
1914
  * A layer defines how data from a specified source will be styled. Read more about layer types
1893
1915
  * and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers).
1894
1916
  *
1895
- * @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers) or, less commonly, the {@link CustomLayerInterface} specification.
1917
+ * TODO: JSDoc can't pass @param {(LayerSpecification & {source?: string | SourceSpecification}) | CustomLayerInterface} layer The layer to add,
1918
+ * @param {Object} layer
1919
+ * conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers) or,
1920
+ * less commonly, the {@link CustomLayerInterface} specification.
1896
1921
  * The MapLibre Style Specification's layer definition is appropriate for most layers.
1897
1922
  *
1898
1923
  * @param {string} layer.id A unique identifer that you define.
@@ -1900,10 +1925,10 @@ class Map extends Camera {
1900
1925
  * A list of layer types is available in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#type).
1901
1926
  *
1902
1927
  * (This can also be `custom`. For more information, see {@link CustomLayerInterface}.)
1903
- * @param {string | Object} [layer.source] The data source for the layer.
1928
+ * @param {string | SourceSpecification} [layer.source] The data source for the layer.
1904
1929
  * Reference a source that has _already been defined_ using the source's unique id.
1905
1930
  * Reference a _new source_ using a source object (as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/)) directly.
1906
- * This is **required** for all `layer.type` options _except_ for `custom`.
1931
+ * This is **required** for all `layer.type` options _except_ for `custom` and `background`.
1907
1932
  * @param {string} [layer.sourceLayer] (optional) The name of the source layer within the specified `layer.source` to use for this style layer.
1908
1933
  * This is only applicable for vector tile sources and is **required** when `layer.source` is of the type `vector`.
1909
1934
  * @param {array} [layer.filter] (optional) An expression specifying conditions on source features.
@@ -1994,7 +2019,7 @@ class Map extends Camera {
1994
2019
  * @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js-docs/example/vector-source/)
1995
2020
  * @see [Add a WMS source](https://maplibre.org/maplibre-gl-js-docs/example/wms/)
1996
2021
  */
1997
- addLayer(layer: LayerSpecification | CustomLayerInterface, beforeId?: string) {
2022
+ addLayer(layer: (LayerSpecification & {source?: string | SourceSpecification}) | CustomLayerInterface, beforeId?: string) {
1998
2023
  this._lazyInitEmptyStyle();
1999
2024
  this.style.addLayer(layer, beforeId);
2000
2025
  return this._update(true);
@@ -2410,6 +2435,35 @@ class Map extends Camera {
2410
2435
  this._container.addEventListener('scroll', this._onMapScroll, false);
2411
2436
  }
2412
2437
 
2438
+ _setupCooperativeGestures() {
2439
+ const container = this._container;
2440
+ this._metaPress = false;
2441
+ this._cooperativeGesturesScreen = DOM.create('div', 'maplibregl-cooperative-gesture-screen', container);
2442
+ let modifierKeyName = 'Control';
2443
+ let desktopMessage = typeof this._cooperativeGestures !== 'boolean' && this._cooperativeGestures.windowsHelpText ? this._cooperativeGestures.windowsHelpText : 'Use Ctrl + scroll to zoom the map';
2444
+ if (navigator.platform.indexOf('Mac') === 0) {
2445
+ desktopMessage = typeof this._cooperativeGestures !== 'boolean' && this._cooperativeGestures.macHelpText ? this._cooperativeGestures.macHelpText : 'Use ⌘ + scroll to zoom the map';
2446
+ modifierKeyName = 'Meta';
2447
+ }
2448
+ const mobileMessage = typeof this._cooperativeGestures !== 'boolean' && this._cooperativeGestures.mobileHelpText ? this._cooperativeGestures.mobileHelpText : 'Use two fingers to move the map';
2449
+ this._cooperativeGesturesScreen.innerHTML = `
2450
+ <div class="maplibregl-desktop-message">${desktopMessage}</div>
2451
+ <div class="maplibregl-mobile-message">${mobileMessage}</div>
2452
+ `;
2453
+ document.addEventListener('keydown', (event) => {
2454
+ if (event.key === modifierKeyName) this._metaPress = true;
2455
+ });
2456
+ document.addEventListener('keyup', (event) => {
2457
+ if (event.key === modifierKeyName) this._metaPress = false;
2458
+ });
2459
+ // Add event to canvas container since gesture container is pointer-events: none
2460
+ this._canvasContainer.addEventListener('wheel', (e) => {
2461
+ this._onCooperativeGesture(e, this._metaPress, 1);
2462
+ }, false);
2463
+ // Remove the traditional pan classes
2464
+ this._canvasContainer.classList.remove('mapboxgl-touch-drag-pan', 'maplibregl-touch-drag-pan');
2465
+ }
2466
+
2413
2467
  _resizeCanvas(width: number, height: number, pixelRatio: number) {
2414
2468
  // Request the required canvas size taking the pixelratio into account.
2415
2469
  this._canvas.width = pixelRatio * width;
@@ -2465,6 +2519,17 @@ class Map extends Camera {
2465
2519
  return false;
2466
2520
  }
2467
2521
 
2522
+ _onCooperativeGesture(event: any, metaPress, touches) {
2523
+ if (!metaPress && touches < 2) {
2524
+ // Alert user how to scroll/pan
2525
+ this._cooperativeGesturesScreen.classList.add('maplibregl-show');
2526
+ setTimeout(() => {
2527
+ this._cooperativeGesturesScreen.classList.remove('maplibregl-show');
2528
+ }, 100);
2529
+ }
2530
+ return false;
2531
+ }
2532
+
2468
2533
  /**
2469
2534
  * Returns a Boolean indicating whether the map is fully loaded.
2470
2535
  *
@@ -2714,6 +2779,9 @@ class Map extends Camera {
2714
2779
  this._canvas.removeEventListener('webglcontextlost', this._contextLost, false);
2715
2780
  DOM.remove(this._canvasContainer);
2716
2781
  DOM.remove(this._controlContainer);
2782
+ if (this._cooperativeGestures) {
2783
+ DOM.remove(this._cooperativeGesturesScreen);
2784
+ }
2717
2785
  this._container.classList.remove('maplibregl-map', 'mapboxgl-map');
2718
2786
 
2719
2787
  PerformanceUtils.clearMetrics();