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