@maptiler/sdk 1.2.0 → 2.0.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/.eslintrc.cjs +1 -0
- package/dist/maptiler-sdk.css +1 -1
- package/dist/maptiler-sdk.d.ts +190 -241
- package/dist/maptiler-sdk.min.mjs +3 -3
- package/dist/maptiler-sdk.mjs +487 -460
- package/dist/maptiler-sdk.mjs.map +1 -1
- package/package.json +3 -3
- package/readme.md +211 -6
- package/CHANGELOG.md +0 -157
- package/colorramp.md +0 -93
- package/dist/maptiler-sdk.umd.js +0 -6997
- package/dist/maptiler-sdk.umd.js.map +0 -1
- package/dist/maptiler-sdk.umd.min.js +0 -582
- package/src/AttributionControl.ts +0 -13
- package/src/CanvasSource.ts +0 -13
- package/src/FullscreenControl.ts +0 -13
- package/src/GeoJSONSource.ts +0 -13
- package/src/GeolocateControl.ts +0 -13
- package/src/ImageSource.ts +0 -13
- package/src/LogoControl.ts +0 -13
- package/src/Map.ts +0 -1328
- package/src/MaptilerGeolocateControl.ts +0 -207
- package/src/MaptilerLogoControl.ts +0 -58
- package/src/MaptilerNavigationControl.ts +0 -69
- package/src/MaptilerTerrainControl.ts +0 -72
- package/src/Marker.ts +0 -13
- package/src/Minimap.ts +0 -373
- package/src/NavigationControl.ts +0 -13
- package/src/Point.ts +0 -334
- package/src/Popup.ts +0 -13
- package/src/RasterDEMTileSource.ts +0 -13
- package/src/RasterTileSource.ts +0 -13
- package/src/ScaleControl.ts +0 -13
- package/src/Style.ts +0 -13
- package/src/TerrainControl.ts +0 -13
- package/src/VectorTileSource.ts +0 -13
- package/src/VideoSource.ts +0 -13
- package/src/colorramp.ts +0 -1216
- package/src/config.ts +0 -96
- package/src/converters/index.ts +0 -1
- package/src/converters/xml.ts +0 -681
- package/src/defaults.ts +0 -20
- package/src/helpers/index.ts +0 -27
- package/src/helpers/stylehelper.ts +0 -395
- package/src/helpers/vectorlayerhelpers.ts +0 -1511
- package/src/index.ts +0 -242
- package/src/language.ts +0 -183
- package/src/mapstyle.ts +0 -46
- package/src/style/style_template.css +0 -146
- package/src/style/svg/v6-compass.svg +0 -12
- package/src/style/svg/v6-fullscreen-off.svg +0 -7
- package/src/style/svg/v6-fullscreen.svg +0 -7
- package/src/style/svg/v6-geolocate-active-error.svg +0 -10
- package/src/style/svg/v6-geolocate-active.svg +0 -7
- package/src/style/svg/v6-geolocate-background.svg +0 -8
- package/src/style/svg/v6-geolocate-disabled.svg +0 -10
- package/src/style/svg/v6-geolocate.svg +0 -7
- package/src/style/svg/v6-terrain-on.svg +0 -7
- package/src/style/svg/v6-terrain.svg +0 -7
- package/src/style/svg/v6-zoom-minus.svg +0 -7
- package/src/style/svg/v6-zoom-plus.svg +0 -7
- package/src/tools.ts +0 -171
- package/src/unit.ts +0 -1
package/src/Map.ts
DELETED
|
@@ -1,1328 +0,0 @@
|
|
|
1
|
-
import maplibregl from "maplibre-gl";
|
|
2
|
-
import { Base64 } from "js-base64";
|
|
3
|
-
import type {
|
|
4
|
-
StyleSpecification,
|
|
5
|
-
MapOptions as MapOptionsML,
|
|
6
|
-
ControlPosition,
|
|
7
|
-
StyleSwapOptions,
|
|
8
|
-
StyleOptions,
|
|
9
|
-
MapDataEvent,
|
|
10
|
-
Tile,
|
|
11
|
-
RasterDEMSourceSpecification,
|
|
12
|
-
RequestTransformFunction,
|
|
13
|
-
Source,
|
|
14
|
-
LayerSpecification,
|
|
15
|
-
SourceSpecification,
|
|
16
|
-
CustomLayerInterface,
|
|
17
|
-
FilterSpecification,
|
|
18
|
-
StyleSetterOptions,
|
|
19
|
-
ExpressionSpecification,
|
|
20
|
-
} from "maplibre-gl";
|
|
21
|
-
import { ReferenceMapStyle, MapStyleVariant } from "@maptiler/client";
|
|
22
|
-
import { config, MAPTILER_SESSION_ID, SdkConfig } from "./config";
|
|
23
|
-
import { defaults } from "./defaults";
|
|
24
|
-
import { MaptilerLogoControl } from "./MaptilerLogoControl";
|
|
25
|
-
import { combineTransformRequest, enableRTL } from "./tools";
|
|
26
|
-
import {
|
|
27
|
-
getBrowserLanguage,
|
|
28
|
-
isLanguageSupported,
|
|
29
|
-
Language,
|
|
30
|
-
LanguageString,
|
|
31
|
-
} from "./language";
|
|
32
|
-
import { styleToStyle } from "./mapstyle";
|
|
33
|
-
import { MaptilerTerrainControl } from "./MaptilerTerrainControl";
|
|
34
|
-
import { MaptilerNavigationControl } from "./MaptilerNavigationControl";
|
|
35
|
-
import { geolocation } from "@maptiler/client";
|
|
36
|
-
import { MaptilerGeolocateControl } from "./MaptilerGeolocateControl";
|
|
37
|
-
import { AttributionControl } from "./AttributionControl";
|
|
38
|
-
import { ScaleControl } from "./ScaleControl";
|
|
39
|
-
import { FullscreenControl } from "./FullscreenControl";
|
|
40
|
-
|
|
41
|
-
import Minimap from "./Minimap";
|
|
42
|
-
import type { MinimapOptionsInput } from "./Minimap";
|
|
43
|
-
|
|
44
|
-
export type LoadWithTerrainEvent = {
|
|
45
|
-
type: "loadWithTerrain";
|
|
46
|
-
target: Map;
|
|
47
|
-
terrain: {
|
|
48
|
-
source: string;
|
|
49
|
-
exaggeration: number;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const GeolocationType: {
|
|
54
|
-
POINT: "POINT";
|
|
55
|
-
COUNTRY: "COUNTRY";
|
|
56
|
-
} = {
|
|
57
|
-
POINT: "POINT",
|
|
58
|
-
COUNTRY: "COUNTRY",
|
|
59
|
-
} as const;
|
|
60
|
-
|
|
61
|
-
type MapTerrainDataEvent = MapDataEvent & {
|
|
62
|
-
isSourceLoaded: boolean;
|
|
63
|
-
tile: Tile;
|
|
64
|
-
sourceId: string;
|
|
65
|
-
source: RasterDEMSourceSpecification;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Options to provide to the `Map` constructor
|
|
70
|
-
*/
|
|
71
|
-
export type MapOptions = Omit<MapOptionsML, "style" | "maplibreLogo"> & {
|
|
72
|
-
/**
|
|
73
|
-
* Style of the map. Can be:
|
|
74
|
-
* - a full style URL (possibly with API key)
|
|
75
|
-
* - a shorthand with only the MapTIler style name (eg. `"streets-v2"`)
|
|
76
|
-
* - a longer form with the prefix `"maptiler://"` (eg. `"maptiler://streets-v2"`)
|
|
77
|
-
*/
|
|
78
|
-
style?: ReferenceMapStyle | MapStyleVariant | StyleSpecification | string;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Define the language of the map. This can be done directly with a language ISO code (eg. "en")
|
|
82
|
-
* or with a built-in shorthand (eg. Language.ENGLISH).
|
|
83
|
-
* Note that this is equivalent to setting the `config.primaryLanguage` and will overwrite it.
|
|
84
|
-
*/
|
|
85
|
-
language?: LanguageString;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Define the MapTiler Cloud API key to be used. This is strictly equivalent to setting
|
|
89
|
-
* `config.apiKey` and will overwrite it.
|
|
90
|
-
*/
|
|
91
|
-
apiKey?: string;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Shows or hides the MapTiler logo in the bottom left corner.
|
|
95
|
-
*
|
|
96
|
-
* For paid plans:
|
|
97
|
-
* - `true` shows MapTiler logo
|
|
98
|
-
* - `false` hodes MapTiler logo
|
|
99
|
-
* - default: `false` (hide)
|
|
100
|
-
*
|
|
101
|
-
* For free plans: MapTiler logo always shows, regardless of the value.
|
|
102
|
-
*/
|
|
103
|
-
maptilerLogo?: boolean;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Enables 3D terrain if `true`. (default: `false`)
|
|
107
|
-
*/
|
|
108
|
-
terrain?: boolean;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Exaggeration factor of the terrain. (default: `1`, no exaggeration)
|
|
112
|
-
*/
|
|
113
|
-
terrainExaggeration?: number;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Show the navigation control. (default: `true`, will hide if `false`)
|
|
117
|
-
*/
|
|
118
|
-
navigationControl?: boolean | ControlPosition;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Show the terrain control. (default: `false`, will show if `true`)
|
|
122
|
-
*/
|
|
123
|
-
terrainControl?: boolean | ControlPosition;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Show the geolocate control. (default: `true`, will hide if `false`)
|
|
127
|
-
*/
|
|
128
|
-
geolocateControl?: boolean | ControlPosition;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Show the scale control. (default: `false`, will show if `true`)
|
|
132
|
-
*/
|
|
133
|
-
scaleControl?: boolean | ControlPosition;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Show the full screen control. (default: `false`, will show if `true`)
|
|
137
|
-
*/
|
|
138
|
-
fullscreenControl?: boolean | ControlPosition;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Display a minimap in a user defined corner of the map. (default: `bottom-left` corner)
|
|
142
|
-
* If set to true, the map will assume it is a minimap and forego the attribution control.
|
|
143
|
-
*/
|
|
144
|
-
minimap?: boolean | ControlPosition | MinimapOptionsInput;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* attributionControl
|
|
148
|
-
*/
|
|
149
|
-
forceNoAttributionControl?: boolean;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Method to position the map at a given geolocation. Only if:
|
|
153
|
-
* - `hash` is `false`
|
|
154
|
-
* - `center` is not provided
|
|
155
|
-
*
|
|
156
|
-
* If the value is `true` of `"POINT"` (given by `GeolocationType.POINT`) then the positionning uses the MapTiler Cloud
|
|
157
|
-
* Geolocation to find the non-GPS location point.
|
|
158
|
-
* The zoom level can be provided in the `Map` constructor with the `zoom` option or will be `13` if not provided.
|
|
159
|
-
*
|
|
160
|
-
* If the value is `"COUNTRY"` (given by `GeolocationType.COUNTRY`) then the map is centered around the bounding box of the country.
|
|
161
|
-
* In this case, the `zoom` option will be ignored.
|
|
162
|
-
*
|
|
163
|
-
* If the value is `false`, no geolocation is performed and the map centering and zooming depends on other options or on
|
|
164
|
-
* the built-in defaults.
|
|
165
|
-
*
|
|
166
|
-
* If this option is non-false and the options `center` is also provided, then `center` prevails.
|
|
167
|
-
*
|
|
168
|
-
* Default: `false`
|
|
169
|
-
*/
|
|
170
|
-
geolocate?: (typeof GeolocationType)[keyof typeof GeolocationType] | boolean;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* The Map class can be instanciated to display a map in a `<div>`
|
|
175
|
-
*/
|
|
176
|
-
export class Map extends maplibregl.Map {
|
|
177
|
-
private isTerrainEnabled = false;
|
|
178
|
-
private terrainExaggeration = 1;
|
|
179
|
-
private primaryLanguage: LanguageString;
|
|
180
|
-
private terrainGrowing = false;
|
|
181
|
-
private terrainFlattening = false;
|
|
182
|
-
private minimap?: Minimap;
|
|
183
|
-
private forceLanguageUpdate: boolean;
|
|
184
|
-
private languageAlwaysBeenStyle: boolean;
|
|
185
|
-
|
|
186
|
-
constructor(options: MapOptions) {
|
|
187
|
-
if (options.apiKey) {
|
|
188
|
-
config.apiKey = options.apiKey;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const style = styleToStyle(options.style);
|
|
192
|
-
const hashPreConstructor = location.hash;
|
|
193
|
-
|
|
194
|
-
if (!config.apiKey) {
|
|
195
|
-
console.warn(
|
|
196
|
-
"MapTiler Cloud API key is not set. Visit https://maptiler.com and try Cloud for free!",
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// calling the map constructor with full length style
|
|
201
|
-
super({
|
|
202
|
-
...options,
|
|
203
|
-
style,
|
|
204
|
-
maplibreLogo: false,
|
|
205
|
-
transformRequest: combineTransformRequest(options.transformRequest),
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
this.primaryLanguage = options.language ?? config.primaryLanguage;
|
|
209
|
-
this.forceLanguageUpdate =
|
|
210
|
-
this.primaryLanguage === Language.STYLE ||
|
|
211
|
-
this.primaryLanguage === Language.STYLE_LOCK
|
|
212
|
-
? false
|
|
213
|
-
: true;
|
|
214
|
-
this.languageAlwaysBeenStyle = this.primaryLanguage === Language.STYLE;
|
|
215
|
-
this.terrainExaggeration =
|
|
216
|
-
options.terrainExaggeration ?? this.terrainExaggeration;
|
|
217
|
-
|
|
218
|
-
// Map centering and geolocation
|
|
219
|
-
this.once("styledata", async () => {
|
|
220
|
-
// Not using geolocation centering if...
|
|
221
|
-
|
|
222
|
-
// the geolcoate option is not provided or is falsy
|
|
223
|
-
if (!options.geolocate) {
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ... a center is provided in options
|
|
228
|
-
if (options.center) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ... the hash option is enabled and a hash is present in the URL
|
|
233
|
-
if (options.hash && !!hashPreConstructor) {
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// If the geolocation is set to COUNTRY:
|
|
238
|
-
try {
|
|
239
|
-
if (options.geolocate === GeolocationType.COUNTRY) {
|
|
240
|
-
await this.fitToIpBounds();
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
} catch (e) {
|
|
244
|
-
// not raising
|
|
245
|
-
console.warn((e as Error).message);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// As a fallback, we want to center the map on the visitor. First with IP geolocation...
|
|
249
|
-
let ipLocatedCameraHash: string;
|
|
250
|
-
try {
|
|
251
|
-
await this.centerOnIpPoint(options.zoom);
|
|
252
|
-
ipLocatedCameraHash = this.getCameraHash();
|
|
253
|
-
} catch (e) {
|
|
254
|
-
// not raising
|
|
255
|
-
console.warn((e as Error).message);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// A more precise localization
|
|
259
|
-
|
|
260
|
-
// This more advanced localization is commented out because the easeTo animation
|
|
261
|
-
// triggers an error if the terrain grow is enabled (due to being nable to project the center while moving)
|
|
262
|
-
|
|
263
|
-
// Then, the get a more precise location, we rely on the browser location, but only if it was already granted
|
|
264
|
-
// before (we don't want to ask wih a popup at launch time)
|
|
265
|
-
const locationResult = await navigator.permissions.query({
|
|
266
|
-
name: "geolocation",
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
if (locationResult.state === "granted") {
|
|
270
|
-
navigator.geolocation.getCurrentPosition(
|
|
271
|
-
// success callback
|
|
272
|
-
(data) => {
|
|
273
|
-
// If the user has already moved since the ip location, then we no longer want to move the center
|
|
274
|
-
if (ipLocatedCameraHash !== this.getCameraHash()) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (this.terrain) {
|
|
279
|
-
this.easeTo({
|
|
280
|
-
center: [data.coords.longitude, data.coords.latitude],
|
|
281
|
-
zoom: options.zoom || 12,
|
|
282
|
-
duration: 2000,
|
|
283
|
-
});
|
|
284
|
-
} else {
|
|
285
|
-
this.once("terrain", () => {
|
|
286
|
-
this.easeTo({
|
|
287
|
-
center: [data.coords.longitude, data.coords.latitude],
|
|
288
|
-
zoom: options.zoom || 12,
|
|
289
|
-
duration: 2000,
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
// error callback
|
|
296
|
-
null,
|
|
297
|
-
|
|
298
|
-
// options
|
|
299
|
-
{
|
|
300
|
-
maximumAge: 24 * 3600 * 1000, // a day in millisec
|
|
301
|
-
timeout: 5000, // milliseconds
|
|
302
|
-
enableHighAccuracy: false,
|
|
303
|
-
},
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// If the config includes language changing, we must update the map language
|
|
309
|
-
this.on("styledata", () => {
|
|
310
|
-
this.setPrimaryLanguage(this.primaryLanguage);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// this even is in charge of reaplying the terrain elevation after the
|
|
314
|
-
// style has changed because depending on the src/tgt style,
|
|
315
|
-
// the style logic is not always able to resolve the application of terrain
|
|
316
|
-
this.on("styledata", () => {
|
|
317
|
-
// the styling resolver did no manage to reaply the terrain,
|
|
318
|
-
// so let's reload it
|
|
319
|
-
if (this.getTerrain() === null && this.isTerrainEnabled) {
|
|
320
|
-
this.enableTerrain(this.terrainExaggeration);
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// load the Right-to-Left text plugin (will happen only once)
|
|
325
|
-
this.once("load", async () => {
|
|
326
|
-
enableRTL();
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Update logo and attibution
|
|
330
|
-
this.once("load", async () => {
|
|
331
|
-
let tileJsonContent = { logo: null };
|
|
332
|
-
|
|
333
|
-
try {
|
|
334
|
-
const possibleSources = Object.keys(this.style.sourceCaches)
|
|
335
|
-
.map((sourceName) => this.getSource(sourceName))
|
|
336
|
-
.filter(
|
|
337
|
-
(s: Source | undefined) =>
|
|
338
|
-
s &&
|
|
339
|
-
"url" in s &&
|
|
340
|
-
typeof s.url === "string" &&
|
|
341
|
-
s?.url.includes("tiles.json"),
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
const styleUrl = new URL(
|
|
345
|
-
(possibleSources[0] as maplibregl.VectorTileSource).url,
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
if (!styleUrl.searchParams.has("key")) {
|
|
349
|
-
styleUrl.searchParams.append("key", config.apiKey);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const tileJsonRes = await fetch(styleUrl.href);
|
|
353
|
-
tileJsonContent = await tileJsonRes.json();
|
|
354
|
-
} catch (e) {
|
|
355
|
-
// No tiles.json found (should not happen on maintained styles)
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// The attribution and logo must show when required
|
|
359
|
-
if (options.forceNoAttributionControl !== true) {
|
|
360
|
-
if ("logo" in tileJsonContent && tileJsonContent.logo) {
|
|
361
|
-
const logoURL: string = tileJsonContent.logo;
|
|
362
|
-
|
|
363
|
-
this.addControl(
|
|
364
|
-
new MaptilerLogoControl({ logoURL }),
|
|
365
|
-
options.logoPosition,
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
// if attribution in option is `false` but the the logo shows up in the tileJson, then the attribution must show anyways
|
|
369
|
-
if (options.attributionControl === false) {
|
|
370
|
-
this.addControl(
|
|
371
|
-
new AttributionControl({
|
|
372
|
-
customAttribution: options.customAttribution,
|
|
373
|
-
}),
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
} else if (options.maptilerLogo) {
|
|
377
|
-
this.addControl(new MaptilerLogoControl(), options.logoPosition);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// the other controls at init time but be after
|
|
382
|
-
// (due to the async nature of logo control)
|
|
383
|
-
|
|
384
|
-
// By default, no scale control
|
|
385
|
-
if (options.scaleControl) {
|
|
386
|
-
// default position, if not provided, is top left corner
|
|
387
|
-
const position = (
|
|
388
|
-
options.scaleControl === true || options.scaleControl === undefined
|
|
389
|
-
? "bottom-right"
|
|
390
|
-
: options.scaleControl
|
|
391
|
-
) as ControlPosition;
|
|
392
|
-
|
|
393
|
-
const scaleControl = new ScaleControl({ unit: config.unit });
|
|
394
|
-
this.addControl(scaleControl, position);
|
|
395
|
-
config.on("unit", (unit) => {
|
|
396
|
-
scaleControl.setUnit(unit);
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (options.navigationControl !== false) {
|
|
401
|
-
// default position, if not provided, is top left corner
|
|
402
|
-
const position = (
|
|
403
|
-
options.navigationControl === true ||
|
|
404
|
-
options.navigationControl === undefined
|
|
405
|
-
? "top-right"
|
|
406
|
-
: options.navigationControl
|
|
407
|
-
) as ControlPosition;
|
|
408
|
-
this.addControl(new MaptilerNavigationControl(), position);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (options.geolocateControl !== false) {
|
|
412
|
-
// default position, if not provided, is top left corner
|
|
413
|
-
const position = (
|
|
414
|
-
options.geolocateControl === true ||
|
|
415
|
-
options.geolocateControl === undefined
|
|
416
|
-
? "top-right"
|
|
417
|
-
: options.geolocateControl
|
|
418
|
-
) as ControlPosition;
|
|
419
|
-
|
|
420
|
-
this.addControl(
|
|
421
|
-
// new maplibregl.GeolocateControl({
|
|
422
|
-
new MaptilerGeolocateControl({
|
|
423
|
-
positionOptions: {
|
|
424
|
-
enableHighAccuracy: true,
|
|
425
|
-
maximumAge: 0,
|
|
426
|
-
timeout: 6000 /* 6 sec */,
|
|
427
|
-
},
|
|
428
|
-
fitBoundsOptions: {
|
|
429
|
-
maxZoom: 15,
|
|
430
|
-
},
|
|
431
|
-
trackUserLocation: true,
|
|
432
|
-
showAccuracyCircle: true,
|
|
433
|
-
showUserLocation: true,
|
|
434
|
-
}),
|
|
435
|
-
position,
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (options.terrainControl) {
|
|
440
|
-
// default position, if not provided, is top left corner
|
|
441
|
-
const position = (
|
|
442
|
-
options.terrainControl === true ||
|
|
443
|
-
options.terrainControl === undefined
|
|
444
|
-
? "top-right"
|
|
445
|
-
: options.terrainControl
|
|
446
|
-
) as ControlPosition;
|
|
447
|
-
this.addControl(new MaptilerTerrainControl(), position);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// By default, no fullscreen control
|
|
451
|
-
if (options.fullscreenControl) {
|
|
452
|
-
// default position, if not provided, is top left corner
|
|
453
|
-
const position = (
|
|
454
|
-
options.fullscreenControl === true ||
|
|
455
|
-
options.fullscreenControl === undefined
|
|
456
|
-
? "top-right"
|
|
457
|
-
: options.fullscreenControl
|
|
458
|
-
) as ControlPosition;
|
|
459
|
-
|
|
460
|
-
this.addControl(new FullscreenControl({}), position);
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// Creating a custom event: "loadWithTerrain"
|
|
465
|
-
// that fires only once when both:
|
|
466
|
-
// - the map has full loaded (corresponds to the the "load" event)
|
|
467
|
-
// - the terrain has loaded (corresponds to the "terrain" event with terrain beion non-null)
|
|
468
|
-
// This custom event is necessary to wait for when the map is instanciated with `terrain: true`
|
|
469
|
-
// and some animation (flyTo, easeTo) are running from the begining.
|
|
470
|
-
let loadEventTriggered = false;
|
|
471
|
-
let terrainEventTriggered = false;
|
|
472
|
-
let terrainEventData: LoadWithTerrainEvent;
|
|
473
|
-
|
|
474
|
-
this.once("load", () => {
|
|
475
|
-
loadEventTriggered = true;
|
|
476
|
-
if (terrainEventTriggered) {
|
|
477
|
-
this.fire("loadWithTerrain", terrainEventData);
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
this.once("style.load", () => {
|
|
482
|
-
const { minimap } = options;
|
|
483
|
-
if (typeof minimap === "object") {
|
|
484
|
-
const {
|
|
485
|
-
zoom,
|
|
486
|
-
center,
|
|
487
|
-
style,
|
|
488
|
-
language,
|
|
489
|
-
apiKey,
|
|
490
|
-
maptilerLogo,
|
|
491
|
-
antialias,
|
|
492
|
-
refreshExpiredTiles,
|
|
493
|
-
maxBounds,
|
|
494
|
-
scrollZoom,
|
|
495
|
-
minZoom,
|
|
496
|
-
maxZoom,
|
|
497
|
-
boxZoom,
|
|
498
|
-
locale,
|
|
499
|
-
fadeDuration,
|
|
500
|
-
crossSourceCollisions,
|
|
501
|
-
clickTolerance,
|
|
502
|
-
bounds,
|
|
503
|
-
fitBoundsOptions,
|
|
504
|
-
pixelRatio,
|
|
505
|
-
validateStyle,
|
|
506
|
-
} = options;
|
|
507
|
-
this.minimap = new Minimap(minimap, {
|
|
508
|
-
zoom,
|
|
509
|
-
center,
|
|
510
|
-
style,
|
|
511
|
-
language,
|
|
512
|
-
apiKey,
|
|
513
|
-
container: "null",
|
|
514
|
-
maptilerLogo,
|
|
515
|
-
antialias,
|
|
516
|
-
refreshExpiredTiles,
|
|
517
|
-
maxBounds,
|
|
518
|
-
scrollZoom,
|
|
519
|
-
minZoom,
|
|
520
|
-
maxZoom,
|
|
521
|
-
boxZoom,
|
|
522
|
-
locale,
|
|
523
|
-
fadeDuration,
|
|
524
|
-
crossSourceCollisions,
|
|
525
|
-
clickTolerance,
|
|
526
|
-
bounds,
|
|
527
|
-
fitBoundsOptions,
|
|
528
|
-
pixelRatio,
|
|
529
|
-
validateStyle,
|
|
530
|
-
});
|
|
531
|
-
this.addControl(this.minimap, minimap.position ?? "bottom-left");
|
|
532
|
-
} else if (minimap === true) {
|
|
533
|
-
this.minimap = new Minimap({}, options);
|
|
534
|
-
this.addControl(this.minimap, "bottom-left");
|
|
535
|
-
} else if (minimap !== undefined && minimap !== false) {
|
|
536
|
-
this.minimap = new Minimap({}, options);
|
|
537
|
-
this.addControl(this.minimap, minimap);
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
const terrainCallback = (evt: LoadWithTerrainEvent) => {
|
|
542
|
-
if (!evt.terrain) return;
|
|
543
|
-
terrainEventTriggered = true;
|
|
544
|
-
terrainEventData = {
|
|
545
|
-
type: "loadWithTerrain",
|
|
546
|
-
target: this,
|
|
547
|
-
terrain: evt.terrain,
|
|
548
|
-
};
|
|
549
|
-
this.off("terrain", terrainCallback);
|
|
550
|
-
|
|
551
|
-
if (loadEventTriggered) {
|
|
552
|
-
this.fire("loadWithTerrain", terrainEventData as LoadWithTerrainEvent);
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
this.on("terrain", terrainCallback);
|
|
557
|
-
|
|
558
|
-
// enable 3D terrain if provided in options
|
|
559
|
-
if (options.terrain) {
|
|
560
|
-
this.enableTerrain(
|
|
561
|
-
options.terrainExaggeration ?? this.terrainExaggeration,
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Awaits for _this_ Map instance to be "loaded" and returns a Promise to the Map.
|
|
568
|
-
* If _this_ Map instance is already loaded, the Promise is resolved directly,
|
|
569
|
-
* otherwise, it is resolved as a result of the "load" event.
|
|
570
|
-
* @returns
|
|
571
|
-
*/
|
|
572
|
-
async onLoadAsync() {
|
|
573
|
-
return new Promise<Map>((resolve) => {
|
|
574
|
-
if (this.loaded()) {
|
|
575
|
-
return resolve(this);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
this.once("load", () => {
|
|
579
|
-
resolve(this);
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Awaits for _this_ Map instance to be "loaded" as well as with terrain being non-null for the first time
|
|
586
|
-
* and returns a Promise to the Map.
|
|
587
|
-
* If _this_ Map instance is already loaded with terrain, the Promise is resolved directly,
|
|
588
|
-
* otherwise, it is resolved as a result of the "loadWithTerrain" event.
|
|
589
|
-
* @returns
|
|
590
|
-
*/
|
|
591
|
-
async onLoadWithTerrainAsync() {
|
|
592
|
-
return new Promise<Map>((resolve) => {
|
|
593
|
-
if (this.loaded() && this.terrain) {
|
|
594
|
-
return resolve(this);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
this.once("loadWithTerrain", () => {
|
|
598
|
-
resolve(this);
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Update the style of the map.
|
|
605
|
-
* Can be:
|
|
606
|
-
* - a full style URL (possibly with API key)
|
|
607
|
-
* - a shorthand with only the MapTIler style name (eg. `"streets-v2"`)
|
|
608
|
-
* - a longer form with the prefix `"maptiler://"` (eg. `"maptiler://streets-v2"`)
|
|
609
|
-
*/
|
|
610
|
-
override setStyle(
|
|
611
|
-
style:
|
|
612
|
-
| null
|
|
613
|
-
| ReferenceMapStyle
|
|
614
|
-
| MapStyleVariant
|
|
615
|
-
| StyleSpecification
|
|
616
|
-
| string,
|
|
617
|
-
options?: StyleSwapOptions & StyleOptions,
|
|
618
|
-
): this {
|
|
619
|
-
this.minimap?.setStyle(style);
|
|
620
|
-
this.forceLanguageUpdate = true;
|
|
621
|
-
|
|
622
|
-
this.once("idle", () => {
|
|
623
|
-
this.forceLanguageUpdate = false;
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
return super.setStyle(styleToStyle(style), options);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Adds a [MapLibre style layer](https://maplibre.org/maplibre-style-spec/layers)
|
|
631
|
-
* to the map's style.
|
|
632
|
-
*
|
|
633
|
-
* A layer defines how data from a specified source will be styled. Read more about layer types
|
|
634
|
-
* and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/layers).
|
|
635
|
-
*
|
|
636
|
-
* @param layer - The layer to add,
|
|
637
|
-
* conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
|
|
638
|
-
* less commonly, the {@link CustomLayerInterface} specification.
|
|
639
|
-
* The MapLibre Style Specification's layer definition is appropriate for most layers.
|
|
640
|
-
*
|
|
641
|
-
* @param beforeId - The ID of an existing layer to insert the new layer before,
|
|
642
|
-
* resulting in the new layer appearing visually beneath the existing layer.
|
|
643
|
-
* If this argument is not specified, the layer will be appended to the end of the layers array
|
|
644
|
-
* and appear visually above all other layers.
|
|
645
|
-
*
|
|
646
|
-
* @returns `this`
|
|
647
|
-
*/
|
|
648
|
-
addLayer(
|
|
649
|
-
layer:
|
|
650
|
-
| (LayerSpecification & {
|
|
651
|
-
source?: string | SourceSpecification;
|
|
652
|
-
})
|
|
653
|
-
| CustomLayerInterface,
|
|
654
|
-
beforeId?: string,
|
|
655
|
-
): this {
|
|
656
|
-
this.minimap?.addLayer(layer, beforeId);
|
|
657
|
-
return super.addLayer(layer, beforeId);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* Moves a layer to a different z-position.
|
|
662
|
-
*
|
|
663
|
-
* @param id - The ID of the layer to move.
|
|
664
|
-
* @param beforeId - The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map.
|
|
665
|
-
* @returns `this`
|
|
666
|
-
*
|
|
667
|
-
* @example
|
|
668
|
-
* Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map.
|
|
669
|
-
* ```ts
|
|
670
|
-
* map.moveLayer('polygon', 'country-label');
|
|
671
|
-
* ```
|
|
672
|
-
*/
|
|
673
|
-
moveLayer(id: string, beforeId?: string): this {
|
|
674
|
-
this.minimap?.moveLayer(id, beforeId);
|
|
675
|
-
return super.moveLayer(id, beforeId);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* Removes the layer with the given ID from the map's style.
|
|
680
|
-
*
|
|
681
|
-
* An {@link ErrorEvent} will be fired if the image parameter is invald.
|
|
682
|
-
*
|
|
683
|
-
* @param id - The ID of the layer to remove
|
|
684
|
-
* @returns `this`
|
|
685
|
-
*
|
|
686
|
-
* @example
|
|
687
|
-
* If a layer with ID 'state-data' exists, remove it.
|
|
688
|
-
* ```ts
|
|
689
|
-
* if (map.getLayer('state-data')) map.removeLayer('state-data');
|
|
690
|
-
* ```
|
|
691
|
-
*/
|
|
692
|
-
removeLayer(id: string): this {
|
|
693
|
-
this.minimap?.removeLayer(id);
|
|
694
|
-
return super.removeLayer(id);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Sets the zoom extent for the specified style layer. The zoom extent includes the
|
|
699
|
-
* [minimum zoom level](https://maplibre.org/maplibre-style-spec/layers/#minzoom)
|
|
700
|
-
* and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
|
|
701
|
-
* at which the layer will be rendered.
|
|
702
|
-
*
|
|
703
|
-
* Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
|
|
704
|
-
* minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
|
|
705
|
-
* zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
|
|
706
|
-
* layer will not be rendered at all zoom levels in the zoom range.
|
|
707
|
-
*/
|
|
708
|
-
setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number): this {
|
|
709
|
-
this.minimap?.setLayerZoomRange(layerId, minzoom, maxzoom);
|
|
710
|
-
return super.setLayerZoomRange(layerId, minzoom, maxzoom);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
/**
|
|
714
|
-
* Sets the filter for the specified style layer.
|
|
715
|
-
*
|
|
716
|
-
* Filters control which features a style layer renders from its source.
|
|
717
|
-
* Any feature for which the filter expression evaluates to `true` will be
|
|
718
|
-
* rendered on the map. Those that are false will be hidden.
|
|
719
|
-
*
|
|
720
|
-
* Use `setFilter` to show a subset of your source data.
|
|
721
|
-
*
|
|
722
|
-
* To clear the filter, pass `null` or `undefined` as the second parameter.
|
|
723
|
-
*/
|
|
724
|
-
setFilter(
|
|
725
|
-
layerId: string,
|
|
726
|
-
filter?: FilterSpecification | null,
|
|
727
|
-
options?: StyleSetterOptions,
|
|
728
|
-
): this {
|
|
729
|
-
this.minimap?.setFilter(layerId, filter, options);
|
|
730
|
-
return super.setFilter(layerId, filter, options);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Sets the value of a paint property in the specified style layer.
|
|
735
|
-
*
|
|
736
|
-
* @param layerId - The ID of the layer to set the paint property in.
|
|
737
|
-
* @param name - The name of the paint property to set.
|
|
738
|
-
* @param value - The value of the paint property to set.
|
|
739
|
-
* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
|
|
740
|
-
* @param options - Options object.
|
|
741
|
-
* @returns `this`
|
|
742
|
-
* @example
|
|
743
|
-
* ```ts
|
|
744
|
-
* map.setPaintProperty('my-layer', 'fill-color', '#faafee');
|
|
745
|
-
* ```
|
|
746
|
-
*/
|
|
747
|
-
setPaintProperty(
|
|
748
|
-
layerId: string,
|
|
749
|
-
name: string,
|
|
750
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
751
|
-
value: any,
|
|
752
|
-
options?: StyleSetterOptions,
|
|
753
|
-
): this {
|
|
754
|
-
this.minimap?.setPaintProperty(layerId, name, value, options);
|
|
755
|
-
return super.setPaintProperty(layerId, name, value, options);
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
/**
|
|
759
|
-
* Sets the value of a layout property in the specified style layer.
|
|
760
|
-
* Layout properties define how the layer is styled.
|
|
761
|
-
* Layout properties for layers of the same type are documented together.
|
|
762
|
-
* Layers of different types have different layout properties.
|
|
763
|
-
* See the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/) for the complete list of layout properties.
|
|
764
|
-
* @param layerId - The ID of the layer to set the layout property in.
|
|
765
|
-
* @param name - The name of the layout property to set.
|
|
766
|
-
* @param value - The value of the layout property to set.
|
|
767
|
-
* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
|
|
768
|
-
* @param options - Options object.
|
|
769
|
-
* @returns `this`
|
|
770
|
-
*/
|
|
771
|
-
setLayoutProperty(
|
|
772
|
-
layerId: string,
|
|
773
|
-
name: string,
|
|
774
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
775
|
-
value: any,
|
|
776
|
-
options?: StyleSetterOptions,
|
|
777
|
-
): this {
|
|
778
|
-
this.minimap?.setLayoutProperty(layerId, name, value, options);
|
|
779
|
-
return super.setLayoutProperty(layerId, name, value, options);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
* Sets the value of the style's glyphs property.
|
|
784
|
-
*
|
|
785
|
-
* @param glyphsUrl - Glyph URL to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/glyphs/).
|
|
786
|
-
* @param options - Options object.
|
|
787
|
-
* @returns `this`
|
|
788
|
-
* @example
|
|
789
|
-
* ```ts
|
|
790
|
-
* map.setGlyphs('https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf');
|
|
791
|
-
* ```
|
|
792
|
-
*/
|
|
793
|
-
setGlyphs(glyphsUrl: string | null, options?: StyleSetterOptions): this {
|
|
794
|
-
this.minimap?.setGlyphs(glyphsUrl, options);
|
|
795
|
-
return super.setGlyphs(glyphsUrl, options);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
private getStyleLanguage(): string | null {
|
|
799
|
-
if (!this.style.stylesheet.metadata) return null;
|
|
800
|
-
if (typeof this.style.stylesheet.metadata !== "object") return null;
|
|
801
|
-
|
|
802
|
-
if (
|
|
803
|
-
"maptiler:language" in this.style.stylesheet.metadata &&
|
|
804
|
-
typeof this.style.stylesheet.metadata["maptiler:language"] === "string"
|
|
805
|
-
) {
|
|
806
|
-
return this.style.stylesheet.metadata["maptiler:language"];
|
|
807
|
-
} else {
|
|
808
|
-
return null;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
/**
|
|
813
|
-
* Define the primary language of the map. Note that not all the languages shorthands provided are available.
|
|
814
|
-
*/
|
|
815
|
-
setLanguage(language: LanguageString | string): void {
|
|
816
|
-
this.minimap?.map?.setLanguage(language);
|
|
817
|
-
this.onStyleReady(() => {
|
|
818
|
-
this.setPrimaryLanguage(language);
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Define the primary language of the map. Note that not all the languages shorthands provided are available.
|
|
824
|
-
*/
|
|
825
|
-
|
|
826
|
-
private setPrimaryLanguage(language: LanguageString | string) {
|
|
827
|
-
const styleLanguage = this.getStyleLanguage();
|
|
828
|
-
|
|
829
|
-
// If the language is set to `STYLE` (which is the SDK default), but the language defined in
|
|
830
|
-
// the style is `auto`, we need to bypass some verification and modify the languages anyway
|
|
831
|
-
if (
|
|
832
|
-
!(
|
|
833
|
-
language === Language.STYLE &&
|
|
834
|
-
(styleLanguage === Language.AUTO || styleLanguage === Language.VISITOR)
|
|
835
|
-
)
|
|
836
|
-
) {
|
|
837
|
-
if (language !== Language.STYLE) {
|
|
838
|
-
this.languageAlwaysBeenStyle = false;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
if (this.languageAlwaysBeenStyle) {
|
|
842
|
-
return;
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
// No need to change the language
|
|
846
|
-
if (this.primaryLanguage === language && !this.forceLanguageUpdate) {
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if (!isLanguageSupported(language as string)) {
|
|
852
|
-
console.warn(`The language "${language}" is not supported.`);
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
if (this.primaryLanguage === Language.STYLE_LOCK) {
|
|
857
|
-
console.warn(
|
|
858
|
-
"The language cannot be changed because this map has been instantiated with the STYLE_LOCK language flag.",
|
|
859
|
-
);
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
this.primaryLanguage = language as LanguageString;
|
|
864
|
-
let languageNonStyle: LanguageString = language as LanguageString;
|
|
865
|
-
|
|
866
|
-
// STYLE needs to be translated into one of the other language,
|
|
867
|
-
// this is why it's addressed first
|
|
868
|
-
if (language === Language.STYLE) {
|
|
869
|
-
if (!styleLanguage) {
|
|
870
|
-
console.warn("The style has no default languages.");
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
if (!isLanguageSupported(styleLanguage)) {
|
|
875
|
-
console.warn("The language defined in the style is not valid.");
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
languageNonStyle = styleLanguage as LanguageString;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// may be overwritten below
|
|
883
|
-
let langStr: string | LanguageString = Language.LOCAL;
|
|
884
|
-
|
|
885
|
-
// will be overwritten below
|
|
886
|
-
let replacer: ExpressionSpecification | string = `{${langStr}}`;
|
|
887
|
-
|
|
888
|
-
if (languageNonStyle == Language.VISITOR) {
|
|
889
|
-
langStr = getBrowserLanguage();
|
|
890
|
-
replacer = [
|
|
891
|
-
"case",
|
|
892
|
-
["all", ["has", langStr], ["has", Language.LOCAL]],
|
|
893
|
-
[
|
|
894
|
-
"case",
|
|
895
|
-
["==", ["get", langStr], ["get", Language.LOCAL]],
|
|
896
|
-
["get", Language.LOCAL],
|
|
897
|
-
|
|
898
|
-
[
|
|
899
|
-
"format",
|
|
900
|
-
["get", langStr],
|
|
901
|
-
{ "font-scale": 0.8 },
|
|
902
|
-
"\n",
|
|
903
|
-
["get", Language.LOCAL],
|
|
904
|
-
{ "font-scale": 1.1 },
|
|
905
|
-
],
|
|
906
|
-
],
|
|
907
|
-
|
|
908
|
-
["get", Language.LOCAL],
|
|
909
|
-
];
|
|
910
|
-
} else if (languageNonStyle == Language.VISITOR_ENGLISH) {
|
|
911
|
-
langStr = Language.ENGLISH;
|
|
912
|
-
replacer = [
|
|
913
|
-
"case",
|
|
914
|
-
["all", ["has", langStr], ["has", Language.LOCAL]],
|
|
915
|
-
[
|
|
916
|
-
"case",
|
|
917
|
-
["==", ["get", langStr], ["get", Language.LOCAL]],
|
|
918
|
-
["get", Language.LOCAL],
|
|
919
|
-
|
|
920
|
-
[
|
|
921
|
-
"format",
|
|
922
|
-
["get", langStr],
|
|
923
|
-
{ "font-scale": 0.8 },
|
|
924
|
-
"\n",
|
|
925
|
-
["get", Language.LOCAL],
|
|
926
|
-
{ "font-scale": 1.1 },
|
|
927
|
-
],
|
|
928
|
-
],
|
|
929
|
-
["get", Language.LOCAL],
|
|
930
|
-
];
|
|
931
|
-
} else if (languageNonStyle === Language.AUTO) {
|
|
932
|
-
langStr = getBrowserLanguage();
|
|
933
|
-
replacer = [
|
|
934
|
-
"case",
|
|
935
|
-
["has", langStr],
|
|
936
|
-
["get", langStr],
|
|
937
|
-
["get", Language.LOCAL],
|
|
938
|
-
];
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
// This is for using the regular names as {name}
|
|
942
|
-
else if (languageNonStyle === Language.LOCAL) {
|
|
943
|
-
langStr = Language.LOCAL;
|
|
944
|
-
replacer = `{${langStr}}`;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// This section is for the regular language ISO codes
|
|
948
|
-
else {
|
|
949
|
-
langStr = languageNonStyle;
|
|
950
|
-
replacer = [
|
|
951
|
-
"case",
|
|
952
|
-
["has", langStr],
|
|
953
|
-
["get", langStr],
|
|
954
|
-
["get", Language.LOCAL],
|
|
955
|
-
];
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
const { layers } = this.getStyle();
|
|
959
|
-
|
|
960
|
-
for (const { id, layout } of layers) {
|
|
961
|
-
if (!layout) {
|
|
962
|
-
continue;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
if (!("text-field" in layout)) {
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const textFieldLayoutProp = this.getLayoutProperty(id, "text-field");
|
|
970
|
-
|
|
971
|
-
// If the label is not about a name, then we don't translate it
|
|
972
|
-
if (
|
|
973
|
-
typeof textFieldLayoutProp === "string" &&
|
|
974
|
-
(textFieldLayoutProp.toLowerCase().includes("ref") ||
|
|
975
|
-
textFieldLayoutProp.toLowerCase().includes("housenumber"))
|
|
976
|
-
) {
|
|
977
|
-
continue;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
this.setLayoutProperty(id, "text-field", replacer);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
* Get the primary language
|
|
986
|
-
* @returns
|
|
987
|
-
*/
|
|
988
|
-
getPrimaryLanguage(): LanguageString {
|
|
989
|
-
return this.primaryLanguage;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
/**
|
|
993
|
-
* Get the exaggeration factor applied to the terrain
|
|
994
|
-
* @returns
|
|
995
|
-
*/
|
|
996
|
-
getTerrainExaggeration(): number {
|
|
997
|
-
return this.terrainExaggeration;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* Know if terrian is enabled or not
|
|
1002
|
-
* @returns
|
|
1003
|
-
*/
|
|
1004
|
-
hasTerrain(): boolean {
|
|
1005
|
-
return this.isTerrainEnabled;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
private growTerrain(exaggeration: number, durationMs = 1000) {
|
|
1009
|
-
// This method assumes the terrain is already built
|
|
1010
|
-
if (!this.terrain) {
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
const startTime = performance.now();
|
|
1015
|
-
// This is supposedly 0, but it could be something else (e.g. already in the middle of growing, or user defined other)
|
|
1016
|
-
const currentExaggeration = this.terrain.exaggeration;
|
|
1017
|
-
const deltaExaggeration = exaggeration - currentExaggeration;
|
|
1018
|
-
|
|
1019
|
-
// This is again called in a requestAnimationFrame ~loop, until the terrain has grown enough
|
|
1020
|
-
// that it has reached the target
|
|
1021
|
-
const updateExaggeration = () => {
|
|
1022
|
-
if (!this.terrain) {
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
// If the flattening animation is triggered while the growing animation
|
|
1027
|
-
// is running, then the flattening animation is stopped
|
|
1028
|
-
if (this.terrainFlattening) {
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
// normalized value in interval [0, 1] of where we are currently in the animation loop
|
|
1033
|
-
const positionInLoop = (performance.now() - startTime) / durationMs;
|
|
1034
|
-
|
|
1035
|
-
// The animation goes on until we reached 99% of the growing sequence duration
|
|
1036
|
-
if (positionInLoop < 0.99) {
|
|
1037
|
-
const exaggerationFactor = 1 - Math.pow(1 - positionInLoop, 4);
|
|
1038
|
-
const newExaggeration =
|
|
1039
|
-
currentExaggeration + exaggerationFactor * deltaExaggeration;
|
|
1040
|
-
this.terrain.exaggeration = newExaggeration;
|
|
1041
|
-
requestAnimationFrame(updateExaggeration);
|
|
1042
|
-
} else {
|
|
1043
|
-
this.terrainGrowing = false;
|
|
1044
|
-
this.terrainFlattening = false;
|
|
1045
|
-
this.terrain.exaggeration = exaggeration;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
this.triggerRepaint();
|
|
1049
|
-
};
|
|
1050
|
-
|
|
1051
|
-
this.terrainGrowing = true;
|
|
1052
|
-
this.terrainFlattening = false;
|
|
1053
|
-
requestAnimationFrame(updateExaggeration);
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* Enables the 3D terrain visualization
|
|
1058
|
-
*/
|
|
1059
|
-
enableTerrain(exaggeration = this.terrainExaggeration) {
|
|
1060
|
-
if (exaggeration < 0) {
|
|
1061
|
-
console.warn("Terrain exaggeration cannot be negative.");
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
// This function is mapped to a map "data" event. It checks that the terrain
|
|
1066
|
-
// tiles are loaded and when so, it starts an animation to make the terrain grow
|
|
1067
|
-
const dataEventTerrainGrow = async (evt: MapTerrainDataEvent) => {
|
|
1068
|
-
if (!this.terrain) {
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
if (
|
|
1073
|
-
evt.type !== "data" ||
|
|
1074
|
-
evt.dataType !== "source" ||
|
|
1075
|
-
!("source" in evt)
|
|
1076
|
-
) {
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
if (evt.sourceId !== "maptiler-terrain") {
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
const source = evt.source;
|
|
1085
|
-
|
|
1086
|
-
if (source.type !== "raster-dem") {
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
if (!evt.isSourceLoaded) {
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// We shut this event off because we want it to happen only once.
|
|
1095
|
-
// Yet, we cannot use the "once" method because only the last event of the series
|
|
1096
|
-
// has `isSourceLoaded` true
|
|
1097
|
-
this.off("data", dataEventTerrainGrow);
|
|
1098
|
-
|
|
1099
|
-
this.growTerrain(exaggeration);
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
// This is put into a function so that it can be called regardless
|
|
1103
|
-
// of the loading state of _this_ the map instance
|
|
1104
|
-
const addTerrain = () => {
|
|
1105
|
-
// When style is changed,
|
|
1106
|
-
this.isTerrainEnabled = true;
|
|
1107
|
-
this.terrainExaggeration = exaggeration;
|
|
1108
|
-
|
|
1109
|
-
// Mapping it to the "data" event so that we can check that the terrain
|
|
1110
|
-
// growing starts only when terrain tiles are loaded (to reduce glitching)
|
|
1111
|
-
this.on("data", dataEventTerrainGrow);
|
|
1112
|
-
|
|
1113
|
-
this.addSource(defaults.terrainSourceId, {
|
|
1114
|
-
type: "raster-dem",
|
|
1115
|
-
url: defaults.terrainSourceURL,
|
|
1116
|
-
});
|
|
1117
|
-
|
|
1118
|
-
// Setting up the terrain with a 0 exaggeration factor
|
|
1119
|
-
// so it loads ~seamlessly and then can grow from there
|
|
1120
|
-
this.setTerrain({
|
|
1121
|
-
source: defaults.terrainSourceId,
|
|
1122
|
-
exaggeration: 0,
|
|
1123
|
-
});
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
// The terrain has already been loaded,
|
|
1127
|
-
// we just update the exaggeration.
|
|
1128
|
-
if (this.getTerrain()) {
|
|
1129
|
-
this.isTerrainEnabled = true;
|
|
1130
|
-
this.growTerrain(exaggeration);
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
if (this.loaded() || this.isTerrainEnabled) {
|
|
1135
|
-
addTerrain();
|
|
1136
|
-
} else {
|
|
1137
|
-
this.once("load", () => {
|
|
1138
|
-
if (this.getTerrain() && this.getSource(defaults.terrainSourceId)) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
addTerrain();
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
/**
|
|
1147
|
-
* Disable the 3D terrain visualization
|
|
1148
|
-
*/
|
|
1149
|
-
disableTerrain() {
|
|
1150
|
-
// It could be disabled already
|
|
1151
|
-
if (!this.terrain) {
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
this.isTerrainEnabled = false;
|
|
1156
|
-
// this.stopFlattening = false;
|
|
1157
|
-
|
|
1158
|
-
// Duration of the animation in millisec
|
|
1159
|
-
const animationLoopDuration = 1 * 1000;
|
|
1160
|
-
const startTime = performance.now();
|
|
1161
|
-
// This is supposedly 0, but it could be something else (e.g. already in the middle of growing, or user defined other)
|
|
1162
|
-
const currentExaggeration = this.terrain.exaggeration;
|
|
1163
|
-
|
|
1164
|
-
// This is again called in a requestAnimationFrame ~loop, until the terrain has grown enough
|
|
1165
|
-
// that it has reached the target
|
|
1166
|
-
const updateExaggeration = () => {
|
|
1167
|
-
if (!this.terrain) {
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
// If the growing animation is triggered while flattening,
|
|
1172
|
-
// then we exist the flatening
|
|
1173
|
-
if (this.terrainGrowing) {
|
|
1174
|
-
return;
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
// normalized value in interval [0, 1] of where we are currently in the animation loop
|
|
1178
|
-
const positionInLoop =
|
|
1179
|
-
(performance.now() - startTime) / animationLoopDuration;
|
|
1180
|
-
|
|
1181
|
-
// The animation goes on until we reached 99% of the growing sequence duration
|
|
1182
|
-
if (positionInLoop < 0.99) {
|
|
1183
|
-
const exaggerationFactor = Math.pow(1 - positionInLoop, 4);
|
|
1184
|
-
const newExaggeration = currentExaggeration * exaggerationFactor;
|
|
1185
|
-
this.terrain.exaggeration = newExaggeration;
|
|
1186
|
-
requestAnimationFrame(updateExaggeration);
|
|
1187
|
-
} else {
|
|
1188
|
-
this.terrain.exaggeration = 0;
|
|
1189
|
-
this.terrainGrowing = false;
|
|
1190
|
-
this.terrainFlattening = false;
|
|
1191
|
-
// @ts-expect-error - https://github.com/maplibre/maplibre-gl-js/issues/2992
|
|
1192
|
-
this.setTerrain();
|
|
1193
|
-
if (this.getSource(defaults.terrainSourceId)) {
|
|
1194
|
-
this.removeSource(defaults.terrainSourceId);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
this.triggerRepaint();
|
|
1199
|
-
};
|
|
1200
|
-
|
|
1201
|
-
this.terrainGrowing = false;
|
|
1202
|
-
this.terrainFlattening = true;
|
|
1203
|
-
requestAnimationFrame(updateExaggeration);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
/**
|
|
1207
|
-
* Sets the 3D terrain exageration factor.
|
|
1208
|
-
* If the terrain was not enabled prior to the call of this method,
|
|
1209
|
-
* the method `.enableTerrain()` will be called.
|
|
1210
|
-
* If `animate` is `true`, the terrain transformation will be animated in the span of 1 second.
|
|
1211
|
-
* If `animate` is `false`, no animated transition to the newly defined exaggeration.
|
|
1212
|
-
*/
|
|
1213
|
-
setTerrainExaggeration(exaggeration: number, animate = true) {
|
|
1214
|
-
if (!animate && this.terrain) {
|
|
1215
|
-
this.terrainExaggeration = exaggeration;
|
|
1216
|
-
this.terrain.exaggeration = exaggeration;
|
|
1217
|
-
this.triggerRepaint();
|
|
1218
|
-
} else {
|
|
1219
|
-
this.enableTerrain(exaggeration);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
/**
|
|
1224
|
-
* Perform an action when the style is ready. It could be at the moment of calling this method
|
|
1225
|
-
* or later.
|
|
1226
|
-
*/
|
|
1227
|
-
private onStyleReady(cb: () => void) {
|
|
1228
|
-
if (this.isStyleLoaded()) {
|
|
1229
|
-
cb();
|
|
1230
|
-
} else {
|
|
1231
|
-
this.once("styledata", () => {
|
|
1232
|
-
cb();
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
async fitToIpBounds() {
|
|
1238
|
-
const ipGeolocateResult = await geolocation.info();
|
|
1239
|
-
this.fitBounds(
|
|
1240
|
-
ipGeolocateResult.country_bounds as [number, number, number, number],
|
|
1241
|
-
{
|
|
1242
|
-
duration: 0,
|
|
1243
|
-
padding: 100,
|
|
1244
|
-
},
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
async centerOnIpPoint(zoom: number | undefined) {
|
|
1249
|
-
const ipGeolocateResult = await geolocation.info();
|
|
1250
|
-
this.jumpTo({
|
|
1251
|
-
center: [
|
|
1252
|
-
ipGeolocateResult?.longitude ?? 0,
|
|
1253
|
-
ipGeolocateResult?.latitude ?? 0,
|
|
1254
|
-
],
|
|
1255
|
-
zoom: zoom || 11,
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
getCameraHash() {
|
|
1260
|
-
const hashBin = new Float32Array(5);
|
|
1261
|
-
const center = this.getCenter();
|
|
1262
|
-
hashBin[0] = center.lng;
|
|
1263
|
-
hashBin[1] = center.lat;
|
|
1264
|
-
hashBin[2] = this.getZoom();
|
|
1265
|
-
hashBin[3] = this.getPitch();
|
|
1266
|
-
hashBin[4] = this.getBearing();
|
|
1267
|
-
return Base64.fromUint8Array(new Uint8Array(hashBin.buffer));
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
/**
|
|
1271
|
-
* Get the SDK config object.
|
|
1272
|
-
* This is convenient to dispatch the SDK configuration to externally built layers
|
|
1273
|
-
* that do not directly have access to the SDK configuration but do have access to a Map instance.
|
|
1274
|
-
*/
|
|
1275
|
-
getSdkConfig(): SdkConfig {
|
|
1276
|
-
return config;
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
/**
|
|
1280
|
-
* Get the MapTiler session ID. Convenient to dispatch to externaly built component
|
|
1281
|
-
* that do not directly have access to the SDK configuration but do have access to a Map instance.
|
|
1282
|
-
* @returns
|
|
1283
|
-
*/
|
|
1284
|
-
getMaptilerSessionId(): string {
|
|
1285
|
-
return MAPTILER_SESSION_ID;
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
/**
|
|
1289
|
-
* Updates the requestManager's transform request with a new function.
|
|
1290
|
-
*
|
|
1291
|
-
* @param transformRequest A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests.
|
|
1292
|
-
* Expected to return an object with a `url` property and optionally `headers` and `credentials` properties
|
|
1293
|
-
*
|
|
1294
|
-
* @returns {Map} `this`
|
|
1295
|
-
*
|
|
1296
|
-
* @example
|
|
1297
|
-
* map.setTransformRequest((url: string, resourceType: string) => {});
|
|
1298
|
-
*/
|
|
1299
|
-
override setTransformRequest(
|
|
1300
|
-
transformRequest: RequestTransformFunction,
|
|
1301
|
-
): this {
|
|
1302
|
-
super.setTransformRequest(combineTransformRequest(transformRequest));
|
|
1303
|
-
return this;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
/**
|
|
1307
|
-
* Loads an image. This is an async equivalent of `Map.loadImage`
|
|
1308
|
-
*/
|
|
1309
|
-
async loadImageAsync(
|
|
1310
|
-
url: string,
|
|
1311
|
-
): Promise<HTMLImageElement | ImageBitmap | null | undefined> {
|
|
1312
|
-
return new Promise((resolve, reject) => {
|
|
1313
|
-
this.loadImage(
|
|
1314
|
-
url,
|
|
1315
|
-
(
|
|
1316
|
-
error: Error | null | undefined,
|
|
1317
|
-
image: HTMLImageElement | ImageBitmap | null | undefined,
|
|
1318
|
-
) => {
|
|
1319
|
-
if (error) {
|
|
1320
|
-
reject(error);
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
resolve(image);
|
|
1324
|
-
},
|
|
1325
|
-
);
|
|
1326
|
-
});
|
|
1327
|
-
}
|
|
1328
|
-
}
|