@maptiler/sdk 1.1.2 → 1.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 (41) hide show
  1. package/.eslintrc.cjs +15 -5
  2. package/.github/pull_request_template.md +11 -0
  3. package/.github/workflows/format-lint.yml +24 -0
  4. package/CHANGELOG.md +94 -51
  5. package/colorramp.md +93 -0
  6. package/dist/maptiler-sdk.d.ts +1207 -123
  7. package/dist/maptiler-sdk.min.mjs +3 -1
  8. package/dist/maptiler-sdk.mjs +3561 -485
  9. package/dist/maptiler-sdk.mjs.map +1 -1
  10. package/dist/maptiler-sdk.umd.js +3825 -869
  11. package/dist/maptiler-sdk.umd.js.map +1 -1
  12. package/dist/maptiler-sdk.umd.min.js +51 -49
  13. package/package.json +27 -13
  14. package/readme.md +298 -0
  15. package/rollup.config.js +2 -16
  16. package/src/Map.ts +489 -357
  17. package/src/MaptilerGeolocateControl.ts +23 -20
  18. package/src/MaptilerLogoControl.ts +3 -3
  19. package/src/MaptilerNavigationControl.ts +9 -6
  20. package/src/MaptilerTerrainControl.ts +15 -14
  21. package/src/Minimap.ts +373 -0
  22. package/src/Point.ts +3 -5
  23. package/src/colorramp.ts +1216 -0
  24. package/src/config.ts +4 -3
  25. package/src/converters/index.ts +1 -0
  26. package/src/converters/xml.ts +681 -0
  27. package/src/defaults.ts +1 -1
  28. package/src/helpers/index.ts +27 -0
  29. package/src/helpers/stylehelper.ts +395 -0
  30. package/src/helpers/vectorlayerhelpers.ts +1511 -0
  31. package/src/index.ts +10 -0
  32. package/src/language.ts +116 -79
  33. package/src/mapstyle.ts +4 -2
  34. package/src/tools.ts +68 -16
  35. package/tsconfig.json +8 -5
  36. package/vite.config.ts +10 -0
  37. package/demos/maptiler-sdk.css +0 -147
  38. package/demos/maptiler-sdk.umd.js +0 -4041
  39. package/demos/mountain.html +0 -67
  40. package/demos/simple.html +0 -67
  41. package/demos/transform-request.html +0 -81
@@ -1,4 +1,4 @@
1
- import type { LngLatLike } from "maplibre-gl";
1
+ import type { LngLatLike, MapLibreEvent } from "maplibre-gl";
2
2
  import maplibregl from "maplibre-gl";
3
3
  import { GeolocateControl } from "./GeolocateControl";
4
4
  import { DOMcreate } from "./tools";
@@ -7,6 +7,10 @@ const Marker = maplibregl.Marker;
7
7
  const LngLat = maplibregl.LngLat;
8
8
  const LngLatBounds = maplibregl.LngLatBounds;
9
9
 
10
+ type MoveEndEvent = MapLibreEvent<
11
+ MouseEvent | TouchEvent | WheelEvent | undefined
12
+ > & { geolocateSource?: boolean };
13
+
10
14
  /**
11
15
  * The MaptilerGeolocateControl is an extension of the original GeolocateControl
12
16
  * with a few changes. In this version, the active mode persists as long as the
@@ -22,10 +26,10 @@ export class MaptilerGeolocateControl extends GeolocateControl {
22
26
  * @param {Position} position the Geolocation API Position
23
27
  * @private
24
28
  */
25
- _updateCamera(position: GeolocationPosition) {
29
+ _updateCamera = (position: GeolocationPosition) => {
26
30
  const center = new LngLat(
27
31
  position.coords.longitude,
28
- position.coords.latitude
32
+ position.coords.latitude,
29
33
  );
30
34
  const radius = position.coords.accuracy;
31
35
  const bearing = this._map.getBearing();
@@ -37,7 +41,7 @@ export class MaptilerGeolocateControl extends GeolocateControl {
37
41
 
38
42
  const currentMapZoom = this._map.getZoom();
39
43
 
40
- if (currentMapZoom > this.options.fitBoundsOptions.maxZoom) {
44
+ if (currentMapZoom > (this.options?.fitBoundsOptions?.maxZoom ?? 30)) {
41
45
  options.zoom = currentMapZoom;
42
46
  }
43
47
 
@@ -73,30 +77,30 @@ export class MaptilerGeolocateControl extends GeolocateControl {
73
77
 
74
78
  this.lastUpdatedCenter = this._map.getCenter();
75
79
  });
76
- }
80
+ };
77
81
 
78
- _setupUI(supported: boolean) {
82
+ _setupUI = (supported: boolean) => {
79
83
  this.lastUpdatedCenter = this._map.getCenter();
80
84
 
81
85
  this._container.addEventListener("contextmenu", (e: MouseEvent) =>
82
- e.preventDefault()
86
+ e.preventDefault(),
83
87
  );
84
88
  this._geolocateButton = DOMcreate(
85
89
  "button",
86
90
  "maplibregl-ctrl-geolocate",
87
- this._container
91
+ this._container,
88
92
  );
89
93
  DOMcreate(
90
94
  "span",
91
95
  "maplibregl-ctrl-icon",
92
- this._geolocateButton
96
+ this._geolocateButton,
93
97
  ).setAttribute("aria-hidden", "true");
94
98
  this._geolocateButton.type = "button";
95
99
 
96
100
  if (supported === false) {
97
101
  // warnOnce('Geolocation support is not available so the GeolocateControl will be disabled.');
98
102
  const title = this._map._getUIString(
99
- "GeolocateControl.LocationNotAvailable"
103
+ "GeolocateControl.LocationNotAvailable",
100
104
  );
101
105
  this._geolocateButton.disabled = true;
102
106
  this._geolocateButton.title = title;
@@ -115,12 +119,11 @@ export class MaptilerGeolocateControl extends GeolocateControl {
115
119
  // when showUserLocation is enabled, keep the Geolocate button disabled until the device location marker is setup on the map
116
120
  if (this.options.showUserLocation) {
117
121
  this._dotElement = DOMcreate("div", "maplibregl-user-location-dot");
118
-
119
- this._userLocationDotMarker = new Marker(this._dotElement);
122
+ this._userLocationDotMarker = new Marker({ element: this._dotElement });
120
123
 
121
124
  this._circleElement = DOMcreate(
122
125
  "div",
123
- "maplibregl-user-location-accuracy-circle"
126
+ "maplibregl-user-location-accuracy-circle",
124
127
  );
125
128
  this._accuracyCircleMarker = new Marker({
126
129
  element: this._circleElement,
@@ -143,11 +146,11 @@ export class MaptilerGeolocateControl extends GeolocateControl {
143
146
  // is a zoom, rotation or pitch (where the center stays the same) then we can keep the ACTIVE_LOCK
144
147
  // mode ON.
145
148
  if (this.options.trackUserLocation) {
146
- this._map.on("moveend", (event: any) => {
149
+ this._map.on("moveend", (event: MoveEndEvent) => {
147
150
  const fromResize =
148
151
  event.originalEvent && event.originalEvent.type === "resize";
149
152
  const movingDistance = this.lastUpdatedCenter.distanceTo(
150
- this._map.getCenter()
153
+ this._map.getCenter(),
151
154
  );
152
155
 
153
156
  if (
@@ -158,17 +161,17 @@ export class MaptilerGeolocateControl extends GeolocateControl {
158
161
  ) {
159
162
  this._watchState = "BACKGROUND";
160
163
  this._geolocateButton.classList.add(
161
- "maplibregl-ctrl-geolocate-background"
164
+ "maplibregl-ctrl-geolocate-background",
162
165
  );
163
166
  this._geolocateButton.classList.remove(
164
- "maplibregl-ctrl-geolocate-active"
167
+ "maplibregl-ctrl-geolocate-active",
165
168
  );
166
169
 
167
170
  this.fire(new Event("trackuserlocationend"));
168
171
  }
169
172
  });
170
173
  }
171
- }
174
+ };
172
175
 
173
176
  _updateCircleRadius() {
174
177
  if (
@@ -196,9 +199,9 @@ export class MaptilerGeolocateControl extends GeolocateControl {
196
199
  this._circleElement.style.height = `${circleDiameter}px`;
197
200
  }
198
201
 
199
- _onZoom() {
202
+ _onZoom = () => {
200
203
  if (this.options.showUserLocation && this.options.showAccuracyCircle) {
201
204
  this._updateCircleRadius();
202
205
  }
203
- }
206
+ };
204
207
  }
@@ -1,8 +1,7 @@
1
- import maplibregl from "maplibre-gl";
2
1
  import type { LogoOptions as LogoOptionsML } from "maplibre-gl";
3
2
  import { defaults } from "./defaults";
4
- import { Map } from "./Map";
5
3
  import { LogoControl } from "./LogoControl";
4
+ import type { Map } from "./Map";
6
5
 
7
6
  type LogoOptions = LogoOptionsML & {
8
7
  logoURL?: string;
@@ -14,6 +13,7 @@ type LogoOptions = LogoOptionsML & {
14
13
  * any link URL. By default this is using MapTiler logo and URL.
15
14
  */
16
15
  export class MaptilerLogoControl extends LogoControl {
16
+ declare _compact: boolean;
17
17
  private logoURL = "";
18
18
  private linkURL = "";
19
19
 
@@ -26,7 +26,7 @@ export class MaptilerLogoControl extends LogoControl {
26
26
 
27
27
  onAdd(map: Map): HTMLElement {
28
28
  this._map = map;
29
- this._compact = this.options && this.options.compact;
29
+ this._compact = this.options.compact ?? false;
30
30
  this._container = window.document.createElement("div");
31
31
  this._container.className = "maplibregl-ctrl";
32
32
  const anchor = window.document.createElement("a");
@@ -1,7 +1,7 @@
1
1
  import { NavigationControl } from "./NavigationControl";
2
2
 
3
3
  type HTMLButtonElementPlus = HTMLButtonElement & {
4
- clickFunction: (e?: any) => unknown;
4
+ clickFunction: (e?: Event) => unknown;
5
5
  };
6
6
 
7
7
  export class MaptilerNavigationControl extends NavigationControl {
@@ -15,7 +15,7 @@ export class MaptilerNavigationControl extends NavigationControl {
15
15
  // Removing the default click event
16
16
  this._compass.removeEventListener(
17
17
  "click",
18
- (this._compass as HTMLButtonElementPlus).clickFunction
18
+ (this._compass as HTMLButtonElementPlus).clickFunction,
19
19
  );
20
20
 
21
21
  // Adding custom click event
@@ -40,7 +40,7 @@ export class MaptilerNavigationControl extends NavigationControl {
40
40
  */
41
41
  _createButton(
42
42
  className: string,
43
- fn: (e?: any) => unknown
43
+ fn: (e?: Event) => unknown,
44
44
  ): HTMLButtonElementPlus {
45
45
  const button = super._createButton(className, fn) as HTMLButtonElementPlus;
46
46
  button.clickFunction = fn;
@@ -50,17 +50,20 @@ export class MaptilerNavigationControl extends NavigationControl {
50
50
  /**
51
51
  * Overloading: Limit how flat the compass icon can get
52
52
  */
53
- _rotateCompassArrow() {
53
+ _rotateCompassArrow = () => {
54
54
  const rotate = this.options.visualizePitch
55
55
  ? `scale(${Math.min(
56
56
  1.5,
57
57
  1 /
58
- Math.pow(Math.cos(this._map.transform.pitch * (Math.PI / 180)), 0.5)
58
+ Math.pow(
59
+ Math.cos(this._map.transform.pitch * (Math.PI / 180)),
60
+ 0.5,
61
+ ),
59
62
  )}) rotateX(${Math.min(70, this._map.transform.pitch)}deg) rotateZ(${
60
63
  this._map.transform.angle * (180 / Math.PI)
61
64
  }deg)`
62
65
  : `rotate(${this._map.transform.angle * (180 / Math.PI)}deg)`;
63
66
 
64
67
  this._compassIcon.style.transform = rotate;
65
- }
68
+ };
66
69
  }
@@ -1,32 +1,32 @@
1
1
  import { bindAll, DOMcreate, DOMremove } from "./tools";
2
2
 
3
- import { Map } from "./Map";
4
- import maplibregl from "maplibre-gl";
3
+ import type { Map } from "./Map";
4
+ import type { IControl } from "maplibre-gl";
5
5
 
6
6
  /**
7
7
  * A `MaptilerTerrainControl` control adds a button to turn terrain on and off
8
8
  * by triggering the terrain logic that is already deployed in the Map object.
9
9
  */
10
- export class MaptilerTerrainControl implements maplibregl.IControl {
11
- _map: Map;
12
- _container: HTMLElement;
13
- _terrainButton: HTMLButtonElement;
10
+ export class MaptilerTerrainControl implements IControl {
11
+ _map!: Map;
12
+ _container!: HTMLElement;
13
+ _terrainButton!: HTMLButtonElement;
14
14
 
15
15
  constructor() {
16
16
  bindAll(["_toggleTerrain", "_updateTerrainIcon"], this);
17
17
  }
18
18
 
19
- onAdd(map: Map) {
19
+ onAdd(map: Map): HTMLElement {
20
20
  this._map = map;
21
21
  this._container = DOMcreate("div", "maplibregl-ctrl maplibregl-ctrl-group");
22
22
  this._terrainButton = DOMcreate(
23
23
  "button",
24
24
  "maplibregl-ctrl-terrain",
25
- this._container
25
+ this._container,
26
26
  );
27
27
  DOMcreate("span", "maplibregl-ctrl-icon", this._terrainButton).setAttribute(
28
28
  "aria-hidden",
29
- "true"
29
+ "true",
30
30
  );
31
31
  this._terrainButton.type = "button";
32
32
  this._terrainButton.addEventListener("click", this._toggleTerrain);
@@ -36,13 +36,14 @@ export class MaptilerTerrainControl implements maplibregl.IControl {
36
36
  return this._container;
37
37
  }
38
38
 
39
- onRemove() {
39
+ onRemove(): void {
40
40
  DOMremove(this._container);
41
41
  this._map.off("terrain", this._updateTerrainIcon);
42
+ // @ts-expect-error: map will only be undefined on remove
42
43
  this._map = undefined;
43
44
  }
44
45
 
45
- _toggleTerrain() {
46
+ _toggleTerrain(): void {
46
47
  if (this._map.hasTerrain()) {
47
48
  this._map.disableTerrain();
48
49
  } else {
@@ -52,19 +53,19 @@ export class MaptilerTerrainControl implements maplibregl.IControl {
52
53
  this._updateTerrainIcon();
53
54
  }
54
55
 
55
- _updateTerrainIcon() {
56
+ _updateTerrainIcon(): void {
56
57
  this._terrainButton.classList.remove("maplibregl-ctrl-terrain");
57
58
  this._terrainButton.classList.remove("maplibregl-ctrl-terrain-enabled");
58
59
  // if (this._map.terrain) {
59
60
  if (this._map.hasTerrain()) {
60
61
  this._terrainButton.classList.add("maplibregl-ctrl-terrain-enabled");
61
62
  this._terrainButton.title = this._map._getUIString(
62
- "TerrainControl.disableTerrain"
63
+ "TerrainControl.disableTerrain",
63
64
  );
64
65
  } else {
65
66
  this._terrainButton.classList.add("maplibregl-ctrl-terrain");
66
67
  this._terrainButton.title = this._map._getUIString(
67
- "TerrainControl.enableTerrain"
68
+ "TerrainControl.enableTerrain",
68
69
  );
69
70
  }
70
71
  }
package/src/Minimap.ts ADDED
@@ -0,0 +1,373 @@
1
+ /**
2
+ * This is an extension adds support for adding a minimap to one of the map's control containers.
3
+ */
4
+
5
+ import { Map } from "./Map";
6
+ import { DOMcreate, DOMremove } from "./tools";
7
+
8
+ import type {
9
+ ControlPosition,
10
+ CustomLayerInterface,
11
+ FillLayerSpecification,
12
+ FilterSpecification,
13
+ GeoJSONSource,
14
+ IControl,
15
+ LayerSpecification,
16
+ LineLayerSpecification,
17
+ SourceSpecification,
18
+ StyleOptions,
19
+ StyleSetterOptions,
20
+ StyleSpecification,
21
+ StyleSwapOptions,
22
+ } from "maplibre-gl";
23
+ import type { MapOptions } from "./Map";
24
+ import type { MapStyleVariant, ReferenceMapStyle } from "@maptiler/client";
25
+
26
+ export interface ParentRect {
27
+ lineLayout: LineLayerSpecification["layout"];
28
+ linePaint: LineLayerSpecification["paint"];
29
+ fillPaint: FillLayerSpecification["paint"];
30
+ }
31
+
32
+ export interface MinimapOptionsInput {
33
+ /**
34
+ * Style of the map. Can be:
35
+ * - a full style URL (possibly with API key)
36
+ * - a shorthand with only the MapTIler style name (eg. `"streets-v2"`)
37
+ * - a longer form with the prefix `"maptiler://"` (eg. `"maptiler://streets-v2"`)
38
+ */
39
+ style?: ReferenceMapStyle | MapStyleVariant | StyleSpecification | string;
40
+
41
+ /**
42
+ * Set the zoom difference between the parent and the minimap
43
+ * If the parent is zoomed to 10 and the minimap is zoomed to 8, the zoomAdjust should be 2
44
+ * Default: -4
45
+ */
46
+ zoomAdjust?: number;
47
+
48
+ /** Set a zoom of the minimap and don't allow any future changes */
49
+ lockZoom?: number;
50
+
51
+ /** Adjust the pitch only if the user requests */
52
+ pitchAdjust?: boolean;
53
+
54
+ /** Set CSS properties of the container using object key-values */
55
+ containerStyle?: Record<string, string>;
56
+
57
+ /** Set the position of the minimap at either "top-left", "top-right", "bottom-left", or "bottom-right" */
58
+ position?: ControlPosition;
59
+
60
+ /** Set the parentRect fill and/or line options */
61
+ parentRect?: ParentRect;
62
+ }
63
+
64
+ export interface MinimapOptions extends MapOptions {
65
+ zoomAdjust: number;
66
+ pitchAdjust: boolean;
67
+ containerStyle: Record<string, string>;
68
+ parentRect?: ParentRect;
69
+ }
70
+
71
+ export default class Minimap implements IControl {
72
+ #options: MinimapOptions;
73
+ map!: Map;
74
+ #parentMap!: Map;
75
+ #container!: HTMLElement;
76
+ #canvasContainer!: HTMLElement;
77
+ #parentRect?: GeoJSON.Feature<GeoJSON.Polygon>;
78
+ #differentStyle = false;
79
+ #desync?: () => void;
80
+ constructor(options: MinimapOptionsInput, mapOptions: MapOptions) {
81
+ // check if the style is different
82
+ if (options.style !== undefined) this.#differentStyle = true;
83
+ // set options
84
+ this.#options = {
85
+ // set defaults
86
+ zoomAdjust: -4,
87
+ position: "top-right",
88
+ // inherit map options
89
+ ...mapOptions,
90
+ // override any lingering control options
91
+ forceNoAttributionControl: true,
92
+ attributionControl: false,
93
+ navigationControl: false,
94
+ geolocateControl: false,
95
+ maptilerLogo: false,
96
+ minimap: false,
97
+ hash: false,
98
+ pitchAdjust: false,
99
+ // override map options with new user defined minimap options
100
+ ...options,
101
+ containerStyle: {
102
+ border: "1px solid #000",
103
+ width: "400px",
104
+ height: "300px",
105
+ ...(options.containerStyle ?? {}),
106
+ },
107
+ };
108
+ if (options.lockZoom !== undefined) {
109
+ this.#options.minZoom = options.lockZoom;
110
+ this.#options.maxZoom = options.lockZoom;
111
+ }
112
+ }
113
+
114
+ setStyle(
115
+ style:
116
+ | null
117
+ | ReferenceMapStyle
118
+ | MapStyleVariant
119
+ | StyleSpecification
120
+ | string,
121
+ options?: StyleSwapOptions & StyleOptions,
122
+ ): void {
123
+ if (!this.#differentStyle) this.map.setStyle(style, options);
124
+ this.#setParentBounds();
125
+ }
126
+
127
+ addLayer(
128
+ layer:
129
+ | (LayerSpecification & {
130
+ source?: string | SourceSpecification;
131
+ })
132
+ | CustomLayerInterface,
133
+ beforeId?: string,
134
+ ): Map {
135
+ if (!this.#differentStyle) this.map.addLayer(layer, beforeId);
136
+ this.#setParentBounds();
137
+ return this.map;
138
+ }
139
+
140
+ moveLayer(id: string, beforeId?: string): Map {
141
+ if (!this.#differentStyle) this.map.moveLayer(id, beforeId);
142
+ this.#setParentBounds();
143
+ return this.map;
144
+ }
145
+
146
+ removeLayer(id: string): this {
147
+ if (!this.#differentStyle) this.map.removeLayer(id);
148
+ this.#setParentBounds();
149
+ return this;
150
+ }
151
+
152
+ setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number): this {
153
+ if (!this.#differentStyle)
154
+ this.map.setLayerZoomRange(layerId, minzoom, maxzoom);
155
+ this.#setParentBounds();
156
+ return this;
157
+ }
158
+
159
+ setFilter(
160
+ layerId: string,
161
+ filter?: FilterSpecification | null,
162
+ options?: StyleSetterOptions,
163
+ ): this {
164
+ if (!this.#differentStyle) this.map.setFilter(layerId, filter, options);
165
+ this.#setParentBounds();
166
+ return this;
167
+ }
168
+
169
+ setPaintProperty(
170
+ layerId: string,
171
+ name: string,
172
+ // maplibre controlled types
173
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
+ value: any,
175
+ options?: StyleSetterOptions,
176
+ ): this {
177
+ if (!this.#differentStyle)
178
+ this.map.setPaintProperty(layerId, name, value, options);
179
+ this.#setParentBounds();
180
+ return this;
181
+ }
182
+
183
+ setLayoutProperty(
184
+ layerId: string,
185
+ name: string,
186
+ // maplibre controlled types
187
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
+ value: any,
189
+ options?: StyleSetterOptions,
190
+ ): this {
191
+ if (!this.#differentStyle)
192
+ this.map.setLayoutProperty(layerId, name, value, options);
193
+ this.#setParentBounds();
194
+ return this;
195
+ }
196
+
197
+ setGlyphs(glyphsUrl: string | null, options?: StyleSetterOptions): this {
198
+ if (!this.#differentStyle) this.map.setGlyphs(glyphsUrl, options);
199
+ this.#setParentBounds();
200
+ return this;
201
+ }
202
+
203
+ onAdd(parentMap: Map): HTMLElement {
204
+ this.#parentMap = parentMap;
205
+ //prep the container
206
+ this.#container = DOMcreate("div", "maplibregl-ctrl maplibregl-ctrl-group");
207
+ // adjust styling
208
+ for (const [key, value] of Object.entries(this.#options.containerStyle)) {
209
+ this.#container.style.setProperty(key, value);
210
+ }
211
+ this.#options.container = this.#container;
212
+ this.#options.zoom = parentMap.getZoom() + this.#options.zoomAdjust ?? -4;
213
+ this.map = new Map(this.#options);
214
+
215
+ // NOTE: For some reason the DOM doesn't properly update it's size in time
216
+ // for the minimap to convey it's size to the canvas.
217
+ this.map.once("style.load", () => {
218
+ this.map.resize();
219
+ });
220
+
221
+ // set options
222
+ this.map.once("load", () => {
223
+ this.#addParentRect(this.#options.parentRect);
224
+ this.#desync = this.#syncMaps();
225
+ });
226
+
227
+ return this.#container;
228
+ }
229
+
230
+ onRemove(): void {
231
+ this.#desync?.();
232
+ DOMremove(this.#container);
233
+ }
234
+
235
+ #addParentRect(rect?: ParentRect): void {
236
+ if (
237
+ rect === undefined ||
238
+ (rect.linePaint === undefined && rect.fillPaint === undefined)
239
+ ) {
240
+ return;
241
+ }
242
+ this.#parentRect = {
243
+ type: "Feature",
244
+ properties: {
245
+ name: "parentRect",
246
+ },
247
+ geometry: {
248
+ type: "Polygon",
249
+ coordinates: [[[], [], [], [], []]],
250
+ },
251
+ };
252
+
253
+ this.map.addSource("parentRect", {
254
+ type: "geojson",
255
+ data: this.#parentRect,
256
+ });
257
+ if (rect.lineLayout !== undefined || rect.linePaint !== undefined) {
258
+ this.map.addLayer({
259
+ id: "parentRectOutline",
260
+ type: "line",
261
+ source: "parentRect",
262
+ layout: {
263
+ ...rect.lineLayout,
264
+ },
265
+ paint: {
266
+ "line-color": "#FFF",
267
+ "line-width": 1,
268
+ "line-opacity": 0.85,
269
+ ...rect.linePaint,
270
+ },
271
+ });
272
+ }
273
+ if (rect.fillPaint !== undefined) {
274
+ this.map.addLayer({
275
+ id: "parentRectFill",
276
+ type: "fill",
277
+ source: "parentRect",
278
+ layout: {},
279
+ paint: {
280
+ "fill-color": "#08F",
281
+ "fill-opacity": 0.135,
282
+ ...rect.fillPaint,
283
+ },
284
+ });
285
+ }
286
+
287
+ this.#setParentBounds();
288
+ }
289
+
290
+ #setParentBounds() {
291
+ if (this.#parentRect === undefined) return;
292
+
293
+ const { devicePixelRatio } = window;
294
+ const canvas = this.#parentMap.getCanvas();
295
+ const width = canvas.width / devicePixelRatio;
296
+ const height = canvas.height / devicePixelRatio;
297
+
298
+ // Get coordinates for all four corners
299
+ const unproject = this.#parentMap.unproject.bind(this.#parentMap);
300
+ const northWest = unproject([0, 0]);
301
+ const northEast = unproject([width, 0]);
302
+ const southWest = unproject([0, height]);
303
+ const southEast = unproject([width, height]);
304
+
305
+ this.#parentRect.geometry.coordinates = [
306
+ [
307
+ southWest.toArray(),
308
+ southEast.toArray(),
309
+ northEast.toArray(),
310
+ northWest.toArray(),
311
+ southWest.toArray(),
312
+ ],
313
+ ];
314
+
315
+ const source = this.map.getSource("parentRect") as GeoJSONSource;
316
+ source.setData(this.#parentRect);
317
+ }
318
+
319
+ #syncMaps(): () => void {
320
+ const { pitchAdjust } = this.#options;
321
+ // syncing callbacks
322
+ const parentCallback = () => {
323
+ sync("parent");
324
+ };
325
+ const minimapCallback = () => {
326
+ sync("minimap");
327
+ };
328
+
329
+ // on off functions
330
+ const on = () => {
331
+ this.#parentMap.on("move", parentCallback);
332
+ this.map.on("move", minimapCallback);
333
+ };
334
+ const off = () => {
335
+ this.#parentMap.off("move", parentCallback);
336
+ this.map.off("move", minimapCallback);
337
+ };
338
+
339
+ // When one map moves, we turn off the movement listeners
340
+ // on all the maps, move it, then turn the listeners on again
341
+ const sync = (which: "parent" | "minimap") => {
342
+ // OFF
343
+ off();
344
+
345
+ // MOVE
346
+ const from = which === "parent" ? this.#parentMap : this.map;
347
+ const to = which === "parent" ? this.map : this.#parentMap;
348
+ const center = from.getCenter();
349
+ const zoom =
350
+ from.getZoom() +
351
+ (this.#options.zoomAdjust ?? -4) * (which === "parent" ? 1 : -1);
352
+ const bearing = from.getBearing();
353
+ const pitch = from.getPitch();
354
+ to.jumpTo({
355
+ center,
356
+ zoom,
357
+ bearing,
358
+ pitch: pitchAdjust ? pitch : 0,
359
+ });
360
+ // update parent rect
361
+ this.#setParentBounds();
362
+
363
+ // ON
364
+ on();
365
+ };
366
+
367
+ on();
368
+ // return a desync function
369
+ return () => {
370
+ off();
371
+ };
372
+ }
373
+ }
package/src/Point.ts CHANGED
@@ -10,8 +10,6 @@ export type Matrix2 = [number, number, number, number];
10
10
 
11
11
  /**
12
12
  * a point
13
- * @param x
14
- * @param y
15
13
  */
16
14
  export class Point {
17
15
  public x: number;
@@ -249,7 +247,7 @@ export class Point {
249
247
  * @param {Point} other the other point
250
248
  * @return {boolean} whether the points are equal
251
249
  */
252
- equals(other): boolean {
250
+ equals(other: Point): boolean {
253
251
  return this.x === other.x && this.y === other.y;
254
252
  }
255
253
 
@@ -258,7 +256,7 @@ export class Point {
258
256
  * @param {Point} p the other point
259
257
  * @return {Number} distance
260
258
  */
261
- dist(p): number {
259
+ dist(p: Point): number {
262
260
  return Math.sqrt(this.distSqr(p));
263
261
  }
264
262
 
@@ -269,7 +267,7 @@ export class Point {
269
267
  * @param {Point} p the other point
270
268
  * @return {Number} distance
271
269
  */
272
- distSqr(p): number {
270
+ distSqr(p: Point): number {
273
271
  const dx = p.x - this.x;
274
272
  const dy = p.y - this.y;
275
273
  return dx * dx + dy * dy;