@maptiler/sdk 1.1.2 → 1.2.1
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 +15 -5
- package/.github/pull_request_template.md +11 -0
- package/.github/workflows/format-lint.yml +24 -0
- package/CHANGELOG.md +105 -51
- package/colorramp.md +93 -0
- package/dist/maptiler-sdk.d.ts +1226 -124
- package/dist/maptiler-sdk.min.mjs +3 -1
- package/dist/maptiler-sdk.mjs +3582 -483
- package/dist/maptiler-sdk.mjs.map +1 -1
- package/dist/maptiler-sdk.umd.js +4524 -863
- package/dist/maptiler-sdk.umd.js.map +1 -1
- package/dist/maptiler-sdk.umd.min.js +51 -49
- package/package.json +27 -13
- package/readme.md +493 -5
- package/rollup.config.js +2 -16
- package/src/Map.ts +515 -359
- package/src/MaptilerGeolocateControl.ts +23 -20
- package/src/MaptilerLogoControl.ts +3 -3
- package/src/MaptilerNavigationControl.ts +9 -6
- package/src/MaptilerTerrainControl.ts +15 -14
- package/src/Minimap.ts +373 -0
- package/src/Point.ts +3 -5
- package/src/colorramp.ts +1216 -0
- package/src/config.ts +4 -3
- package/src/converters/index.ts +1 -0
- package/src/converters/xml.ts +681 -0
- package/src/defaults.ts +1 -1
- package/src/helpers/index.ts +27 -0
- package/src/helpers/stylehelper.ts +395 -0
- package/src/helpers/vectorlayerhelpers.ts +1511 -0
- package/src/index.ts +90 -121
- package/src/language.ts +116 -79
- package/src/mapstyle.ts +4 -2
- package/src/tools.ts +68 -16
- package/tsconfig.json +8 -5
- package/vite.config.ts +10 -0
- package/demos/maptiler-sdk.css +0 -147
- package/demos/maptiler-sdk.umd.js +0 -4041
- package/demos/mountain.html +0 -67
- package/demos/simple.html +0 -67
- package/demos/transform-request.html +0 -81
package/src/Map.ts
CHANGED
|
@@ -4,11 +4,19 @@ import type {
|
|
|
4
4
|
StyleSpecification,
|
|
5
5
|
MapOptions as MapOptionsML,
|
|
6
6
|
ControlPosition,
|
|
7
|
+
StyleSwapOptions,
|
|
7
8
|
StyleOptions,
|
|
8
9
|
MapDataEvent,
|
|
9
10
|
Tile,
|
|
10
11
|
RasterDEMSourceSpecification,
|
|
11
12
|
RequestTransformFunction,
|
|
13
|
+
Source,
|
|
14
|
+
LayerSpecification,
|
|
15
|
+
SourceSpecification,
|
|
16
|
+
CustomLayerInterface,
|
|
17
|
+
FilterSpecification,
|
|
18
|
+
StyleSetterOptions,
|
|
19
|
+
ExpressionSpecification,
|
|
12
20
|
} from "maplibre-gl";
|
|
13
21
|
import { ReferenceMapStyle, MapStyleVariant } from "@maptiler/client";
|
|
14
22
|
import { config, MAPTILER_SESSION_ID, SdkConfig } from "./config";
|
|
@@ -30,9 +38,8 @@ import { AttributionControl } from "./AttributionControl";
|
|
|
30
38
|
import { ScaleControl } from "./ScaleControl";
|
|
31
39
|
import { FullscreenControl } from "./FullscreenControl";
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
41
|
+
import Minimap from "./Minimap";
|
|
42
|
+
import type { MinimapOptionsInput } from "./Minimap";
|
|
36
43
|
|
|
37
44
|
export type LoadWithTerrainEvent = {
|
|
38
45
|
type: "loadWithTerrain";
|
|
@@ -43,17 +50,6 @@ export type LoadWithTerrainEvent = {
|
|
|
43
50
|
};
|
|
44
51
|
};
|
|
45
52
|
|
|
46
|
-
// StyleSwapOptions is not exported by Maplibre, but we can redefine it (used for setStyle)
|
|
47
|
-
export type TransformStyleFunction = (
|
|
48
|
-
previous: StyleSpecification,
|
|
49
|
-
next: StyleSpecification
|
|
50
|
-
) => StyleSpecification;
|
|
51
|
-
|
|
52
|
-
export type StyleSwapOptions = {
|
|
53
|
-
diff?: boolean;
|
|
54
|
-
transformStyle?: TransformStyleFunction;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
53
|
export const GeolocationType: {
|
|
58
54
|
POINT: "POINT";
|
|
59
55
|
COUNTRY: "COUNTRY";
|
|
@@ -141,6 +137,17 @@ export type MapOptions = Omit<MapOptionsML, "style" | "maplibreLogo"> & {
|
|
|
141
137
|
*/
|
|
142
138
|
fullscreenControl?: boolean | ControlPosition;
|
|
143
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
|
+
|
|
144
151
|
/**
|
|
145
152
|
* Method to position the map at a given geolocation. Only if:
|
|
146
153
|
* - `hash` is `false`
|
|
@@ -169,10 +176,13 @@ export type MapOptions = Omit<MapOptionsML, "style" | "maplibreLogo"> & {
|
|
|
169
176
|
export class Map extends maplibregl.Map {
|
|
170
177
|
private isTerrainEnabled = false;
|
|
171
178
|
private terrainExaggeration = 1;
|
|
172
|
-
private primaryLanguage: LanguageString
|
|
173
|
-
private secondaryLanguage: LanguageString | null = null;
|
|
179
|
+
private primaryLanguage: LanguageString;
|
|
174
180
|
private terrainGrowing = false;
|
|
175
181
|
private terrainFlattening = false;
|
|
182
|
+
private minimap?: Minimap;
|
|
183
|
+
private forceLanguageUpdate: boolean;
|
|
184
|
+
private languageAlwaysBeenStyle: boolean;
|
|
185
|
+
private isReady: boolean = false;
|
|
176
186
|
|
|
177
187
|
constructor(options: MapOptions) {
|
|
178
188
|
if (options.apiKey) {
|
|
@@ -184,7 +194,7 @@ export class Map extends maplibregl.Map {
|
|
|
184
194
|
|
|
185
195
|
if (!config.apiKey) {
|
|
186
196
|
console.warn(
|
|
187
|
-
"MapTiler Cloud API key is not set. Visit https://maptiler.com and try Cloud for free!"
|
|
197
|
+
"MapTiler Cloud API key is not set. Visit https://maptiler.com and try Cloud for free!",
|
|
188
198
|
);
|
|
189
199
|
}
|
|
190
200
|
|
|
@@ -197,7 +207,12 @@ export class Map extends maplibregl.Map {
|
|
|
197
207
|
});
|
|
198
208
|
|
|
199
209
|
this.primaryLanguage = options.language ?? config.primaryLanguage;
|
|
200
|
-
this.
|
|
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;
|
|
201
216
|
this.terrainExaggeration =
|
|
202
217
|
options.terrainExaggeration ?? this.terrainExaggeration;
|
|
203
218
|
|
|
@@ -228,17 +243,17 @@ export class Map extends maplibregl.Map {
|
|
|
228
243
|
}
|
|
229
244
|
} catch (e) {
|
|
230
245
|
// not raising
|
|
231
|
-
console.warn(e.message);
|
|
246
|
+
console.warn((e as Error).message);
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
// As a fallback, we want to center the map on the visitor. First with IP geolocation...
|
|
235
|
-
let ipLocatedCameraHash
|
|
250
|
+
let ipLocatedCameraHash: string;
|
|
236
251
|
try {
|
|
237
252
|
await this.centerOnIpPoint(options.zoom);
|
|
238
253
|
ipLocatedCameraHash = this.getCameraHash();
|
|
239
254
|
} catch (e) {
|
|
240
255
|
// not raising
|
|
241
|
-
console.warn(e.message);
|
|
256
|
+
console.warn((e as Error).message);
|
|
242
257
|
}
|
|
243
258
|
|
|
244
259
|
// A more precise localization
|
|
@@ -286,7 +301,7 @@ export class Map extends maplibregl.Map {
|
|
|
286
301
|
maximumAge: 24 * 3600 * 1000, // a day in millisec
|
|
287
302
|
timeout: 5000, // milliseconds
|
|
288
303
|
enableHighAccuracy: false,
|
|
289
|
-
}
|
|
304
|
+
},
|
|
290
305
|
);
|
|
291
306
|
}
|
|
292
307
|
});
|
|
@@ -294,7 +309,6 @@ export class Map extends maplibregl.Map {
|
|
|
294
309
|
// If the config includes language changing, we must update the map language
|
|
295
310
|
this.on("styledata", () => {
|
|
296
311
|
this.setPrimaryLanguage(this.primaryLanguage);
|
|
297
|
-
this.setSecondaryLanguage(this.secondaryLanguage);
|
|
298
312
|
});
|
|
299
313
|
|
|
300
314
|
// this even is in charge of reaplying the terrain elevation after the
|
|
@@ -321,12 +335,15 @@ export class Map extends maplibregl.Map {
|
|
|
321
335
|
const possibleSources = Object.keys(this.style.sourceCaches)
|
|
322
336
|
.map((sourceName) => this.getSource(sourceName))
|
|
323
337
|
.filter(
|
|
324
|
-
(s:
|
|
325
|
-
|
|
338
|
+
(s: Source | undefined) =>
|
|
339
|
+
s &&
|
|
340
|
+
"url" in s &&
|
|
341
|
+
typeof s.url === "string" &&
|
|
342
|
+
s?.url.includes("tiles.json"),
|
|
326
343
|
);
|
|
327
344
|
|
|
328
345
|
const styleUrl = new URL(
|
|
329
|
-
(possibleSources[0] as maplibregl.VectorTileSource).url
|
|
346
|
+
(possibleSources[0] as maplibregl.VectorTileSource).url,
|
|
330
347
|
);
|
|
331
348
|
|
|
332
349
|
if (!styleUrl.searchParams.has("key")) {
|
|
@@ -340,24 +357,26 @@ export class Map extends maplibregl.Map {
|
|
|
340
357
|
}
|
|
341
358
|
|
|
342
359
|
// The attribution and logo must show when required
|
|
343
|
-
if (
|
|
344
|
-
|
|
360
|
+
if (options.forceNoAttributionControl !== true) {
|
|
361
|
+
if ("logo" in tileJsonContent && tileJsonContent.logo) {
|
|
362
|
+
const logoURL: string = tileJsonContent.logo;
|
|
345
363
|
|
|
346
|
-
this.addControl(
|
|
347
|
-
new MaptilerLogoControl({ logoURL }),
|
|
348
|
-
options.logoPosition
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// if attribution in option is `false` but the the logo shows up in the tileJson, then the attribution must show anyways
|
|
352
|
-
if (options.attributionControl === false) {
|
|
353
364
|
this.addControl(
|
|
354
|
-
new
|
|
355
|
-
|
|
356
|
-
})
|
|
365
|
+
new MaptilerLogoControl({ logoURL }),
|
|
366
|
+
options.logoPosition,
|
|
357
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);
|
|
358
379
|
}
|
|
359
|
-
} else if (options.maptilerLogo) {
|
|
360
|
-
this.addControl(new MaptilerLogoControl(), options.logoPosition);
|
|
361
380
|
}
|
|
362
381
|
|
|
363
382
|
// the other controls at init time but be after
|
|
@@ -414,7 +433,7 @@ export class Map extends maplibregl.Map {
|
|
|
414
433
|
showAccuracyCircle: true,
|
|
415
434
|
showUserLocation: true,
|
|
416
435
|
}),
|
|
417
|
-
position
|
|
436
|
+
position,
|
|
418
437
|
);
|
|
419
438
|
}
|
|
420
439
|
|
|
@@ -441,26 +460,89 @@ export class Map extends maplibregl.Map {
|
|
|
441
460
|
|
|
442
461
|
this.addControl(new FullscreenControl({}), position);
|
|
443
462
|
}
|
|
463
|
+
|
|
464
|
+
this.isReady = true;
|
|
465
|
+
this.fire("ready", { target: this });
|
|
444
466
|
});
|
|
445
467
|
|
|
446
468
|
// Creating a custom event: "loadWithTerrain"
|
|
447
469
|
// that fires only once when both:
|
|
448
|
-
// - the map has full
|
|
470
|
+
// - the map has full ready (corresponds to the the "ready" event)
|
|
449
471
|
// - the terrain has loaded (corresponds to the "terrain" event with terrain beion non-null)
|
|
450
472
|
// This custom event is necessary to wait for when the map is instanciated with `terrain: true`
|
|
451
473
|
// and some animation (flyTo, easeTo) are running from the begining.
|
|
452
474
|
let loadEventTriggered = false;
|
|
453
475
|
let terrainEventTriggered = false;
|
|
454
|
-
let terrainEventData: LoadWithTerrainEvent
|
|
476
|
+
let terrainEventData: LoadWithTerrainEvent;
|
|
455
477
|
|
|
456
|
-
this.once("
|
|
478
|
+
this.once("ready", () => {
|
|
457
479
|
loadEventTriggered = true;
|
|
458
480
|
if (terrainEventTriggered) {
|
|
459
481
|
this.fire("loadWithTerrain", terrainEventData);
|
|
460
482
|
}
|
|
461
483
|
});
|
|
462
484
|
|
|
463
|
-
|
|
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) => {
|
|
464
546
|
if (!evt.terrain) return;
|
|
465
547
|
terrainEventTriggered = true;
|
|
466
548
|
terrainEventData = {
|
|
@@ -480,7 +562,7 @@ export class Map extends maplibregl.Map {
|
|
|
480
562
|
// enable 3D terrain if provided in options
|
|
481
563
|
if (options.terrain) {
|
|
482
564
|
this.enableTerrain(
|
|
483
|
-
options.terrainExaggeration ?? this.terrainExaggeration
|
|
565
|
+
options.terrainExaggeration ?? this.terrainExaggeration,
|
|
484
566
|
);
|
|
485
567
|
}
|
|
486
568
|
}
|
|
@@ -492,12 +574,32 @@ export class Map extends maplibregl.Map {
|
|
|
492
574
|
* @returns
|
|
493
575
|
*/
|
|
494
576
|
async onLoadAsync() {
|
|
495
|
-
return new Promise<Map>((resolve
|
|
577
|
+
return new Promise<Map>((resolve) => {
|
|
496
578
|
if (this.loaded()) {
|
|
497
579
|
return resolve(this);
|
|
498
580
|
}
|
|
499
581
|
|
|
500
|
-
this.once("load", (
|
|
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", () => {
|
|
501
603
|
resolve(this);
|
|
502
604
|
});
|
|
503
605
|
});
|
|
@@ -511,12 +613,12 @@ export class Map extends maplibregl.Map {
|
|
|
511
613
|
* @returns
|
|
512
614
|
*/
|
|
513
615
|
async onLoadWithTerrainAsync() {
|
|
514
|
-
return new Promise<Map>((resolve
|
|
515
|
-
if (this.
|
|
616
|
+
return new Promise<Map>((resolve) => {
|
|
617
|
+
if (this.isReady && this.terrain) {
|
|
516
618
|
return resolve(this);
|
|
517
619
|
}
|
|
518
620
|
|
|
519
|
-
this.once("loadWithTerrain", (
|
|
621
|
+
this.once("loadWithTerrain", () => {
|
|
520
622
|
resolve(this);
|
|
521
623
|
});
|
|
522
624
|
});
|
|
@@ -528,340 +630,379 @@ export class Map extends maplibregl.Map {
|
|
|
528
630
|
* - a full style URL (possibly with API key)
|
|
529
631
|
* - a shorthand with only the MapTIler style name (eg. `"streets-v2"`)
|
|
530
632
|
* - a longer form with the prefix `"maptiler://"` (eg. `"maptiler://streets-v2"`)
|
|
531
|
-
* @param style
|
|
532
|
-
* @param options
|
|
533
|
-
* @returns
|
|
534
633
|
*/
|
|
535
|
-
setStyle(
|
|
536
|
-
style:
|
|
537
|
-
|
|
538
|
-
|
|
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
|
+
|
|
539
650
|
return super.setStyle(styleToStyle(style), options);
|
|
540
651
|
}
|
|
541
652
|
|
|
542
653
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
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`
|
|
546
671
|
*/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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);
|
|
552
682
|
}
|
|
553
683
|
|
|
554
684
|
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
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
|
+
* ```
|
|
557
696
|
*/
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
);
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
if (!isLanguageSupported(language as string)) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
this.primaryLanguage = language;
|
|
571
|
-
|
|
572
|
-
this.onStyleReady(() => {
|
|
573
|
-
if (language === Language.AUTO) {
|
|
574
|
-
return this.setPrimaryLanguage(getBrowserLanguage());
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const layers = this.getStyle().layers;
|
|
697
|
+
moveLayer(id: string, beforeId?: string): this {
|
|
698
|
+
this.minimap?.moveLayer(id, beforeId);
|
|
699
|
+
return super.moveLayer(id, beforeId);
|
|
700
|
+
}
|
|
578
701
|
|
|
579
|
-
|
|
580
|
-
|
|
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
|
+
}
|
|
581
720
|
|
|
582
|
-
|
|
583
|
-
|
|
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
|
+
}
|
|
584
736
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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
|
+
}
|
|
588
756
|
|
|
589
|
-
|
|
590
|
-
|
|
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
|
+
}
|
|
591
781
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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
|
+
}
|
|
599
805
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
+
}
|
|
603
821
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
822
|
+
private getStyleLanguage(): string | null {
|
|
823
|
+
if (!this.style.stylesheet.metadata) return null;
|
|
824
|
+
if (typeof this.style.stylesheet.metadata !== "object") return null;
|
|
607
825
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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
|
+
}
|
|
611
835
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
+
}
|
|
616
845
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
// 2. can be an array with 'get' on its first element (monolingual)
|
|
621
|
-
// 3. can be a string of shape '{name:latin}'
|
|
622
|
-
// 4. can be a string referencing another prop such as '{housenumber}' or '{ref}'
|
|
623
|
-
//
|
|
624
|
-
// The case 1, 2 and 3 will be updated while maintaining their original type and shape.
|
|
625
|
-
// The case 3 will not be updated
|
|
626
|
-
|
|
627
|
-
let regexMatch;
|
|
628
|
-
|
|
629
|
-
// This is case 1
|
|
630
|
-
if (
|
|
631
|
-
Array.isArray(textFieldLayoutProp) &&
|
|
632
|
-
textFieldLayoutProp.length >= 2 &&
|
|
633
|
-
textFieldLayoutProp[0].trim().toLowerCase() === "concat"
|
|
634
|
-
) {
|
|
635
|
-
const newProp = textFieldLayoutProp.slice(); // newProp is Array
|
|
636
|
-
// The style could possibly have defined more than 2 concatenated language strings but we only want to edit the first
|
|
637
|
-
// The style could also define that there are more things being concatenated and not only languages
|
|
638
|
-
|
|
639
|
-
for (let j = 0; j < textFieldLayoutProp.length; j += 1) {
|
|
640
|
-
const elem = textFieldLayoutProp[j];
|
|
641
|
-
|
|
642
|
-
// we are looking for an elem of shape '{name:somelangage}' (string) of `["get", "name:somelanguage"]` (array)
|
|
643
|
-
|
|
644
|
-
// the entry of of shape '{name:somelangage}', possibly with loose spacing
|
|
645
|
-
if (
|
|
646
|
-
(typeof elem === "string" || elem instanceof String) &&
|
|
647
|
-
strLanguageRegex.exec(elem.toString())
|
|
648
|
-
) {
|
|
649
|
-
newProp[j] = replacer;
|
|
650
|
-
break; // we just want to update the primary language
|
|
651
|
-
}
|
|
652
|
-
// the entry is of an array of shape `["get", "name:somelanguage"]`
|
|
653
|
-
else if (
|
|
654
|
-
Array.isArray(elem) &&
|
|
655
|
-
elem.length >= 2 &&
|
|
656
|
-
elem[0].trim().toLowerCase() === "get" &&
|
|
657
|
-
strLanguageInArrayRegex.exec(elem[1].toString())
|
|
658
|
-
) {
|
|
659
|
-
newProp[j] = replacer;
|
|
660
|
-
break; // we just want to update the primary language
|
|
661
|
-
} else if (
|
|
662
|
-
Array.isArray(elem) &&
|
|
663
|
-
elem.length === 4 &&
|
|
664
|
-
elem[0].trim().toLowerCase() === "case"
|
|
665
|
-
) {
|
|
666
|
-
newProp[j] = replacer;
|
|
667
|
-
break; // we just want to update the primary language
|
|
668
|
-
}
|
|
669
|
-
}
|
|
846
|
+
/**
|
|
847
|
+
* Define the primary language of the map. Note that not all the languages shorthands provided are available.
|
|
848
|
+
*/
|
|
670
849
|
|
|
671
|
-
|
|
672
|
-
|
|
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
|
+
}
|
|
673
864
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
textFieldLayoutProp.length >= 2 &&
|
|
678
|
-
textFieldLayoutProp[0].trim().toLowerCase() === "get" &&
|
|
679
|
-
strLanguageInArrayRegex.exec(textFieldLayoutProp[1].toString())
|
|
680
|
-
) {
|
|
681
|
-
const newProp = replacer;
|
|
682
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
683
|
-
}
|
|
865
|
+
if (this.languageAlwaysBeenStyle) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
684
868
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
textFieldLayoutProp instanceof String) &&
|
|
689
|
-
strLanguageRegex.exec(textFieldLayoutProp.toString())
|
|
690
|
-
) {
|
|
691
|
-
const newProp = replacer;
|
|
692
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
693
|
-
} else if (
|
|
694
|
-
Array.isArray(textFieldLayoutProp) &&
|
|
695
|
-
textFieldLayoutProp.length === 4 &&
|
|
696
|
-
textFieldLayoutProp[0].trim().toLowerCase() === "case"
|
|
697
|
-
) {
|
|
698
|
-
const newProp = replacer;
|
|
699
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
700
|
-
} else if (
|
|
701
|
-
(typeof textFieldLayoutProp === "string" ||
|
|
702
|
-
textFieldLayoutProp instanceof String) &&
|
|
703
|
-
(regexMatch = strBilingualRegex.exec(
|
|
704
|
-
textFieldLayoutProp.toString()
|
|
705
|
-
)) !== null
|
|
706
|
-
) {
|
|
707
|
-
const newProp = `{${langStr}}${regexMatch[3]}{name${
|
|
708
|
-
regexMatch[4] || ""
|
|
709
|
-
}}`;
|
|
710
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
711
|
-
} else if (
|
|
712
|
-
(typeof textFieldLayoutProp === "string" ||
|
|
713
|
-
textFieldLayoutProp instanceof String) &&
|
|
714
|
-
(regexMatch = strMoreInfoRegex.exec(
|
|
715
|
-
textFieldLayoutProp.toString()
|
|
716
|
-
)) !== null
|
|
717
|
-
) {
|
|
718
|
-
const newProp = `${regexMatch[1]}{${langStr}}${regexMatch[5]}`;
|
|
719
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
720
|
-
}
|
|
869
|
+
// No need to change the language
|
|
870
|
+
if (this.primaryLanguage === language && !this.forceLanguageUpdate) {
|
|
871
|
+
return;
|
|
721
872
|
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
873
|
+
}
|
|
724
874
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
* Note that most styles do not allow a secondary language and this function only works if the style allows (no force adding)
|
|
728
|
-
* @param language
|
|
729
|
-
*/
|
|
730
|
-
setSecondaryLanguage(language: LanguageString = defaults.secondaryLanguage) {
|
|
731
|
-
// Using the lock flag as a primaty language also applies to the secondary
|
|
732
|
-
if (this.primaryLanguage === Language.STYLE_LOCK) {
|
|
733
|
-
console.warn(
|
|
734
|
-
"The language cannot be changed because this map has been instantiated with the STYLE_LOCK language flag."
|
|
735
|
-
);
|
|
875
|
+
if (!isLanguageSupported(language as string)) {
|
|
876
|
+
console.warn(`The language "${language}" is not supported.`);
|
|
736
877
|
return;
|
|
737
878
|
}
|
|
738
879
|
|
|
739
|
-
if (
|
|
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
|
+
);
|
|
740
884
|
return;
|
|
741
885
|
}
|
|
742
886
|
|
|
743
|
-
this.
|
|
887
|
+
this.primaryLanguage = language as LanguageString;
|
|
888
|
+
let languageNonStyle: LanguageString = language as LanguageString;
|
|
744
889
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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;
|
|
748
896
|
}
|
|
749
897
|
|
|
750
|
-
|
|
898
|
+
if (!isLanguageSupported(styleLanguage)) {
|
|
899
|
+
console.warn("The language defined in the style is not valid.");
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
751
902
|
|
|
752
|
-
|
|
753
|
-
|
|
903
|
+
languageNonStyle = styleLanguage as LanguageString;
|
|
904
|
+
}
|
|
754
905
|
|
|
755
|
-
|
|
756
|
-
|
|
906
|
+
// may be overwritten below
|
|
907
|
+
let langStr: string | LanguageString = Language.LOCAL;
|
|
757
908
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
/^\s*{\s*name\s*(:\s*(\S*))?\s*}(\s*){\s*name\s*(:\s*(\S*))?\s*}$/;
|
|
909
|
+
// will be overwritten below
|
|
910
|
+
let replacer: ExpressionSpecification | string = `{${langStr}}`;
|
|
761
911
|
|
|
762
|
-
|
|
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
|
+
}
|
|
763
964
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
965
|
+
// This is for using the regular names as {name}
|
|
966
|
+
else if (languageNonStyle === Language.LOCAL) {
|
|
967
|
+
langStr = Language.LOCAL;
|
|
968
|
+
replacer = `{${langStr}}`;
|
|
969
|
+
}
|
|
767
970
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
+
}
|
|
771
981
|
|
|
772
|
-
|
|
773
|
-
continue;
|
|
774
|
-
}
|
|
982
|
+
const { layers } = this.getStyle();
|
|
775
983
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
984
|
+
for (const { id, layout } of layers) {
|
|
985
|
+
if (!layout) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
780
988
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
// The value of the 'text-field' property can take multiple shape;
|
|
785
|
-
// 1. can be an array with 'concat' on its first element (most likely means bilingual)
|
|
786
|
-
// 2. can be an array with 'get' on its first element (monolingual)
|
|
787
|
-
// 3. can be a string of shape '{name:latin}'
|
|
788
|
-
// 4. can be a string referencing another prop such as '{housenumber}' or '{ref}'
|
|
789
|
-
//
|
|
790
|
-
// Only the case 1 will be updated because we don't want to change the styling (read: add a secondary language where the original styling is only displaying 1)
|
|
791
|
-
|
|
792
|
-
// This is case 1
|
|
793
|
-
if (
|
|
794
|
-
Array.isArray(textFieldLayoutProp) &&
|
|
795
|
-
textFieldLayoutProp.length >= 2 &&
|
|
796
|
-
textFieldLayoutProp[0].trim().toLowerCase() === "concat"
|
|
797
|
-
) {
|
|
798
|
-
newProp = textFieldLayoutProp.slice(); // newProp is Array
|
|
799
|
-
// The style could possibly have defined more than 2 concatenated language strings but we only want to edit the first
|
|
800
|
-
// The style could also define that there are more things being concatenated and not only languages
|
|
801
|
-
|
|
802
|
-
let languagesAlreadyFound = 0;
|
|
803
|
-
|
|
804
|
-
for (let j = 0; j < textFieldLayoutProp.length; j += 1) {
|
|
805
|
-
const elem = textFieldLayoutProp[j];
|
|
806
|
-
|
|
807
|
-
// we are looking for an elem of shape '{name:somelangage}' (string) of `["get", "name:somelanguage"]` (array)
|
|
808
|
-
|
|
809
|
-
// the entry of of shape '{name:somelangage}', possibly with loose spacing
|
|
810
|
-
if (
|
|
811
|
-
(typeof elem === "string" || elem instanceof String) &&
|
|
812
|
-
strLanguageRegex.exec(elem.toString())
|
|
813
|
-
) {
|
|
814
|
-
if (languagesAlreadyFound === 1) {
|
|
815
|
-
newProp[j] = `{name:${language}}`;
|
|
816
|
-
break; // we just want to update the secondary language
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
languagesAlreadyFound += 1;
|
|
820
|
-
}
|
|
821
|
-
// the entry is of an array of shape `["get", "name:somelanguage"]`
|
|
822
|
-
else if (
|
|
823
|
-
Array.isArray(elem) &&
|
|
824
|
-
elem.length >= 2 &&
|
|
825
|
-
elem[0].trim().toLowerCase() === "get" &&
|
|
826
|
-
strLanguageInArrayRegex.exec(elem[1].toString())
|
|
827
|
-
) {
|
|
828
|
-
if (languagesAlreadyFound === 1) {
|
|
829
|
-
newProp[j][1] = `name:${language}`;
|
|
830
|
-
break; // we just want to update the secondary language
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
languagesAlreadyFound += 1;
|
|
834
|
-
} else if (
|
|
835
|
-
Array.isArray(elem) &&
|
|
836
|
-
elem.length === 4 &&
|
|
837
|
-
elem[0].trim().toLowerCase() === "case"
|
|
838
|
-
) {
|
|
839
|
-
if (languagesAlreadyFound === 1) {
|
|
840
|
-
newProp[j] = ["get", `name:${language}`]; // the situation with 'case' is supposed to only happen with the primary lang
|
|
841
|
-
break; // but in case a styling also does that for secondary...
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
languagesAlreadyFound += 1;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
989
|
+
if (!("text-field" in layout)) {
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
847
992
|
|
|
848
|
-
|
|
849
|
-
}
|
|
993
|
+
const textFieldLayoutProp = this.getLayoutProperty(id, "text-field");
|
|
850
994
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
(
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
) {
|
|
859
|
-
const langStr = language ? `name:${language}` : "name"; // to handle local lang
|
|
860
|
-
newProp = `{name${regexMatch[1] || ""}}${regexMatch[3]}{${langStr}}`;
|
|
861
|
-
this.setLayoutProperty(layer.id, "text-field", newProp);
|
|
862
|
-
}
|
|
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;
|
|
863
1002
|
}
|
|
864
|
-
|
|
1003
|
+
|
|
1004
|
+
this.setLayoutProperty(id, "text-field", replacer);
|
|
1005
|
+
}
|
|
865
1006
|
}
|
|
866
1007
|
|
|
867
1008
|
/**
|
|
@@ -872,14 +1013,6 @@ export class Map extends maplibregl.Map {
|
|
|
872
1013
|
return this.primaryLanguage;
|
|
873
1014
|
}
|
|
874
1015
|
|
|
875
|
-
/**
|
|
876
|
-
* Get the secondary language
|
|
877
|
-
* @returns
|
|
878
|
-
*/
|
|
879
|
-
getSecondaryLanguage(): LanguageString {
|
|
880
|
-
return this.secondaryLanguage;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
1016
|
/**
|
|
884
1017
|
* Get the exaggeration factor applied to the terrain
|
|
885
1018
|
* @returns
|
|
@@ -896,7 +1029,7 @@ export class Map extends maplibregl.Map {
|
|
|
896
1029
|
return this.isTerrainEnabled;
|
|
897
1030
|
}
|
|
898
1031
|
|
|
899
|
-
private growTerrain(exaggeration, durationMs = 1000) {
|
|
1032
|
+
private growTerrain(exaggeration: number, durationMs = 1000) {
|
|
900
1033
|
// This method assumes the terrain is already built
|
|
901
1034
|
if (!this.terrain) {
|
|
902
1035
|
return;
|
|
@@ -946,8 +1079,6 @@ export class Map extends maplibregl.Map {
|
|
|
946
1079
|
|
|
947
1080
|
/**
|
|
948
1081
|
* Enables the 3D terrain visualization
|
|
949
|
-
* @param exaggeration
|
|
950
|
-
* @returns
|
|
951
1082
|
*/
|
|
952
1083
|
enableTerrain(exaggeration = this.terrainExaggeration) {
|
|
953
1084
|
if (exaggeration < 0) {
|
|
@@ -1081,7 +1212,8 @@ export class Map extends maplibregl.Map {
|
|
|
1081
1212
|
this.terrain.exaggeration = 0;
|
|
1082
1213
|
this.terrainGrowing = false;
|
|
1083
1214
|
this.terrainFlattening = false;
|
|
1084
|
-
|
|
1215
|
+
// @ts-expect-error - https://github.com/maplibre/maplibre-gl-js/issues/2992
|
|
1216
|
+
this.setTerrain();
|
|
1085
1217
|
if (this.getSource(defaults.terrainSourceId)) {
|
|
1086
1218
|
this.removeSource(defaults.terrainSourceId);
|
|
1087
1219
|
}
|
|
@@ -1101,8 +1233,6 @@ export class Map extends maplibregl.Map {
|
|
|
1101
1233
|
* the method `.enableTerrain()` will be called.
|
|
1102
1234
|
* If `animate` is `true`, the terrain transformation will be animated in the span of 1 second.
|
|
1103
1235
|
* If `animate` is `false`, no animated transition to the newly defined exaggeration.
|
|
1104
|
-
* @param exaggeration
|
|
1105
|
-
* @param animate
|
|
1106
1236
|
*/
|
|
1107
1237
|
setTerrainExaggeration(exaggeration: number, animate = true) {
|
|
1108
1238
|
if (!animate && this.terrain) {
|
|
@@ -1117,9 +1247,8 @@ export class Map extends maplibregl.Map {
|
|
|
1117
1247
|
/**
|
|
1118
1248
|
* Perform an action when the style is ready. It could be at the moment of calling this method
|
|
1119
1249
|
* or later.
|
|
1120
|
-
* @param cb
|
|
1121
1250
|
*/
|
|
1122
|
-
private onStyleReady(cb) {
|
|
1251
|
+
private onStyleReady(cb: () => void) {
|
|
1123
1252
|
if (this.isStyleLoaded()) {
|
|
1124
1253
|
cb();
|
|
1125
1254
|
} else {
|
|
@@ -1136,14 +1265,17 @@ export class Map extends maplibregl.Map {
|
|
|
1136
1265
|
{
|
|
1137
1266
|
duration: 0,
|
|
1138
1267
|
padding: 100,
|
|
1139
|
-
}
|
|
1268
|
+
},
|
|
1140
1269
|
);
|
|
1141
1270
|
}
|
|
1142
1271
|
|
|
1143
1272
|
async centerOnIpPoint(zoom: number | undefined) {
|
|
1144
1273
|
const ipGeolocateResult = await geolocation.info();
|
|
1145
1274
|
this.jumpTo({
|
|
1146
|
-
center: [
|
|
1275
|
+
center: [
|
|
1276
|
+
ipGeolocateResult?.longitude ?? 0,
|
|
1277
|
+
ipGeolocateResult?.latitude ?? 0,
|
|
1278
|
+
],
|
|
1147
1279
|
zoom: zoom || 11,
|
|
1148
1280
|
});
|
|
1149
1281
|
}
|
|
@@ -1163,7 +1295,6 @@ export class Map extends maplibregl.Map {
|
|
|
1163
1295
|
* Get the SDK config object.
|
|
1164
1296
|
* This is convenient to dispatch the SDK configuration to externally built layers
|
|
1165
1297
|
* that do not directly have access to the SDK configuration but do have access to a Map instance.
|
|
1166
|
-
* @returns
|
|
1167
1298
|
*/
|
|
1168
1299
|
getSdkConfig(): SdkConfig {
|
|
1169
1300
|
return config;
|
|
@@ -1189,8 +1320,33 @@ export class Map extends maplibregl.Map {
|
|
|
1189
1320
|
* @example
|
|
1190
1321
|
* map.setTransformRequest((url: string, resourceType: string) => {});
|
|
1191
1322
|
*/
|
|
1192
|
-
setTransformRequest(
|
|
1323
|
+
override setTransformRequest(
|
|
1324
|
+
transformRequest: RequestTransformFunction,
|
|
1325
|
+
): this {
|
|
1193
1326
|
super.setTransformRequest(combineTransformRequest(transformRequest));
|
|
1194
1327
|
return this;
|
|
1195
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
|
+
}
|
|
1196
1352
|
}
|