@maptiler/sdk 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +15 -5
- package/.github/pull_request_template.md +11 -0
- package/.github/workflows/format-lint.yml +24 -0
- package/CHANGELOG.md +94 -51
- package/colorramp.md +93 -0
- package/dist/maptiler-sdk.d.ts +1207 -123
- package/dist/maptiler-sdk.min.mjs +3 -1
- package/dist/maptiler-sdk.mjs +3561 -485
- package/dist/maptiler-sdk.mjs.map +1 -1
- package/dist/maptiler-sdk.umd.js +3825 -869
- 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 +298 -0
- package/rollup.config.js +2 -16
- package/src/Map.ts +489 -357
- 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 +10 -0
- 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,12 @@ 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;
|
|
176
185
|
|
|
177
186
|
constructor(options: MapOptions) {
|
|
178
187
|
if (options.apiKey) {
|
|
@@ -184,7 +193,7 @@ export class Map extends maplibregl.Map {
|
|
|
184
193
|
|
|
185
194
|
if (!config.apiKey) {
|
|
186
195
|
console.warn(
|
|
187
|
-
"MapTiler Cloud API key is not set. Visit https://maptiler.com and try Cloud for free!"
|
|
196
|
+
"MapTiler Cloud API key is not set. Visit https://maptiler.com and try Cloud for free!",
|
|
188
197
|
);
|
|
189
198
|
}
|
|
190
199
|
|
|
@@ -197,7 +206,12 @@ export class Map extends maplibregl.Map {
|
|
|
197
206
|
});
|
|
198
207
|
|
|
199
208
|
this.primaryLanguage = options.language ?? config.primaryLanguage;
|
|
200
|
-
this.
|
|
209
|
+
this.forceLanguageUpdate =
|
|
210
|
+
this.primaryLanguage === Language.STYLE ||
|
|
211
|
+
this.primaryLanguage === Language.STYLE_LOCK
|
|
212
|
+
? false
|
|
213
|
+
: true;
|
|
214
|
+
this.languageAlwaysBeenStyle = this.primaryLanguage === Language.STYLE;
|
|
201
215
|
this.terrainExaggeration =
|
|
202
216
|
options.terrainExaggeration ?? this.terrainExaggeration;
|
|
203
217
|
|
|
@@ -228,17 +242,17 @@ export class Map extends maplibregl.Map {
|
|
|
228
242
|
}
|
|
229
243
|
} catch (e) {
|
|
230
244
|
// not raising
|
|
231
|
-
console.warn(e.message);
|
|
245
|
+
console.warn((e as Error).message);
|
|
232
246
|
}
|
|
233
247
|
|
|
234
248
|
// As a fallback, we want to center the map on the visitor. First with IP geolocation...
|
|
235
|
-
let ipLocatedCameraHash
|
|
249
|
+
let ipLocatedCameraHash: string;
|
|
236
250
|
try {
|
|
237
251
|
await this.centerOnIpPoint(options.zoom);
|
|
238
252
|
ipLocatedCameraHash = this.getCameraHash();
|
|
239
253
|
} catch (e) {
|
|
240
254
|
// not raising
|
|
241
|
-
console.warn(e.message);
|
|
255
|
+
console.warn((e as Error).message);
|
|
242
256
|
}
|
|
243
257
|
|
|
244
258
|
// A more precise localization
|
|
@@ -286,7 +300,7 @@ export class Map extends maplibregl.Map {
|
|
|
286
300
|
maximumAge: 24 * 3600 * 1000, // a day in millisec
|
|
287
301
|
timeout: 5000, // milliseconds
|
|
288
302
|
enableHighAccuracy: false,
|
|
289
|
-
}
|
|
303
|
+
},
|
|
290
304
|
);
|
|
291
305
|
}
|
|
292
306
|
});
|
|
@@ -294,7 +308,6 @@ export class Map extends maplibregl.Map {
|
|
|
294
308
|
// If the config includes language changing, we must update the map language
|
|
295
309
|
this.on("styledata", () => {
|
|
296
310
|
this.setPrimaryLanguage(this.primaryLanguage);
|
|
297
|
-
this.setSecondaryLanguage(this.secondaryLanguage);
|
|
298
311
|
});
|
|
299
312
|
|
|
300
313
|
// this even is in charge of reaplying the terrain elevation after the
|
|
@@ -321,12 +334,15 @@ export class Map extends maplibregl.Map {
|
|
|
321
334
|
const possibleSources = Object.keys(this.style.sourceCaches)
|
|
322
335
|
.map((sourceName) => this.getSource(sourceName))
|
|
323
336
|
.filter(
|
|
324
|
-
(s:
|
|
325
|
-
|
|
337
|
+
(s: Source | undefined) =>
|
|
338
|
+
s &&
|
|
339
|
+
"url" in s &&
|
|
340
|
+
typeof s.url === "string" &&
|
|
341
|
+
s?.url.includes("tiles.json"),
|
|
326
342
|
);
|
|
327
343
|
|
|
328
344
|
const styleUrl = new URL(
|
|
329
|
-
(possibleSources[0] as maplibregl.VectorTileSource).url
|
|
345
|
+
(possibleSources[0] as maplibregl.VectorTileSource).url,
|
|
330
346
|
);
|
|
331
347
|
|
|
332
348
|
if (!styleUrl.searchParams.has("key")) {
|
|
@@ -340,24 +356,26 @@ export class Map extends maplibregl.Map {
|
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
// The attribution and logo must show when required
|
|
343
|
-
if (
|
|
344
|
-
|
|
359
|
+
if (options.forceNoAttributionControl !== true) {
|
|
360
|
+
if ("logo" in tileJsonContent && tileJsonContent.logo) {
|
|
361
|
+
const logoURL: string = tileJsonContent.logo;
|
|
345
362
|
|
|
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
363
|
this.addControl(
|
|
354
|
-
new
|
|
355
|
-
|
|
356
|
-
})
|
|
364
|
+
new MaptilerLogoControl({ logoURL }),
|
|
365
|
+
options.logoPosition,
|
|
357
366
|
);
|
|
367
|
+
|
|
368
|
+
// if attribution in option is `false` but the the logo shows up in the tileJson, then the attribution must show anyways
|
|
369
|
+
if (options.attributionControl === false) {
|
|
370
|
+
this.addControl(
|
|
371
|
+
new AttributionControl({
|
|
372
|
+
customAttribution: options.customAttribution,
|
|
373
|
+
}),
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
} else if (options.maptilerLogo) {
|
|
377
|
+
this.addControl(new MaptilerLogoControl(), options.logoPosition);
|
|
358
378
|
}
|
|
359
|
-
} else if (options.maptilerLogo) {
|
|
360
|
-
this.addControl(new MaptilerLogoControl(), options.logoPosition);
|
|
361
379
|
}
|
|
362
380
|
|
|
363
381
|
// the other controls at init time but be after
|
|
@@ -414,7 +432,7 @@ export class Map extends maplibregl.Map {
|
|
|
414
432
|
showAccuracyCircle: true,
|
|
415
433
|
showUserLocation: true,
|
|
416
434
|
}),
|
|
417
|
-
position
|
|
435
|
+
position,
|
|
418
436
|
);
|
|
419
437
|
}
|
|
420
438
|
|
|
@@ -451,16 +469,76 @@ export class Map extends maplibregl.Map {
|
|
|
451
469
|
// and some animation (flyTo, easeTo) are running from the begining.
|
|
452
470
|
let loadEventTriggered = false;
|
|
453
471
|
let terrainEventTriggered = false;
|
|
454
|
-
let terrainEventData: LoadWithTerrainEvent
|
|
472
|
+
let terrainEventData: LoadWithTerrainEvent;
|
|
455
473
|
|
|
456
|
-
this.once("load", (
|
|
474
|
+
this.once("load", () => {
|
|
457
475
|
loadEventTriggered = true;
|
|
458
476
|
if (terrainEventTriggered) {
|
|
459
477
|
this.fire("loadWithTerrain", terrainEventData);
|
|
460
478
|
}
|
|
461
479
|
});
|
|
462
480
|
|
|
463
|
-
|
|
481
|
+
this.once("style.load", () => {
|
|
482
|
+
const { minimap } = options;
|
|
483
|
+
if (typeof minimap === "object") {
|
|
484
|
+
const {
|
|
485
|
+
zoom,
|
|
486
|
+
center,
|
|
487
|
+
style,
|
|
488
|
+
language,
|
|
489
|
+
apiKey,
|
|
490
|
+
maptilerLogo,
|
|
491
|
+
antialias,
|
|
492
|
+
refreshExpiredTiles,
|
|
493
|
+
maxBounds,
|
|
494
|
+
scrollZoom,
|
|
495
|
+
minZoom,
|
|
496
|
+
maxZoom,
|
|
497
|
+
boxZoom,
|
|
498
|
+
locale,
|
|
499
|
+
fadeDuration,
|
|
500
|
+
crossSourceCollisions,
|
|
501
|
+
clickTolerance,
|
|
502
|
+
bounds,
|
|
503
|
+
fitBoundsOptions,
|
|
504
|
+
pixelRatio,
|
|
505
|
+
validateStyle,
|
|
506
|
+
} = options;
|
|
507
|
+
this.minimap = new Minimap(minimap, {
|
|
508
|
+
zoom,
|
|
509
|
+
center,
|
|
510
|
+
style,
|
|
511
|
+
language,
|
|
512
|
+
apiKey,
|
|
513
|
+
container: "null",
|
|
514
|
+
maptilerLogo,
|
|
515
|
+
antialias,
|
|
516
|
+
refreshExpiredTiles,
|
|
517
|
+
maxBounds,
|
|
518
|
+
scrollZoom,
|
|
519
|
+
minZoom,
|
|
520
|
+
maxZoom,
|
|
521
|
+
boxZoom,
|
|
522
|
+
locale,
|
|
523
|
+
fadeDuration,
|
|
524
|
+
crossSourceCollisions,
|
|
525
|
+
clickTolerance,
|
|
526
|
+
bounds,
|
|
527
|
+
fitBoundsOptions,
|
|
528
|
+
pixelRatio,
|
|
529
|
+
validateStyle,
|
|
530
|
+
});
|
|
531
|
+
this.addControl(this.minimap, minimap.position ?? "bottom-left");
|
|
532
|
+
} else if (minimap === true) {
|
|
533
|
+
this.minimap = new Minimap({}, options);
|
|
534
|
+
this.addControl(this.minimap, "bottom-left");
|
|
535
|
+
} else if (minimap !== undefined && minimap !== false) {
|
|
536
|
+
this.minimap = new Minimap({}, options);
|
|
537
|
+
this.addControl(this.minimap, minimap);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
const terrainCallback = (evt: LoadWithTerrainEvent) => {
|
|
464
542
|
if (!evt.terrain) return;
|
|
465
543
|
terrainEventTriggered = true;
|
|
466
544
|
terrainEventData = {
|
|
@@ -480,7 +558,7 @@ export class Map extends maplibregl.Map {
|
|
|
480
558
|
// enable 3D terrain if provided in options
|
|
481
559
|
if (options.terrain) {
|
|
482
560
|
this.enableTerrain(
|
|
483
|
-
options.terrainExaggeration ?? this.terrainExaggeration
|
|
561
|
+
options.terrainExaggeration ?? this.terrainExaggeration,
|
|
484
562
|
);
|
|
485
563
|
}
|
|
486
564
|
}
|
|
@@ -492,12 +570,12 @@ export class Map extends maplibregl.Map {
|
|
|
492
570
|
* @returns
|
|
493
571
|
*/
|
|
494
572
|
async onLoadAsync() {
|
|
495
|
-
return new Promise<Map>((resolve
|
|
573
|
+
return new Promise<Map>((resolve) => {
|
|
496
574
|
if (this.loaded()) {
|
|
497
575
|
return resolve(this);
|
|
498
576
|
}
|
|
499
577
|
|
|
500
|
-
this.once("load", (
|
|
578
|
+
this.once("load", () => {
|
|
501
579
|
resolve(this);
|
|
502
580
|
});
|
|
503
581
|
});
|
|
@@ -511,12 +589,12 @@ export class Map extends maplibregl.Map {
|
|
|
511
589
|
* @returns
|
|
512
590
|
*/
|
|
513
591
|
async onLoadWithTerrainAsync() {
|
|
514
|
-
return new Promise<Map>((resolve
|
|
592
|
+
return new Promise<Map>((resolve) => {
|
|
515
593
|
if (this.loaded() && this.terrain) {
|
|
516
594
|
return resolve(this);
|
|
517
595
|
}
|
|
518
596
|
|
|
519
|
-
this.once("loadWithTerrain", (
|
|
597
|
+
this.once("loadWithTerrain", () => {
|
|
520
598
|
resolve(this);
|
|
521
599
|
});
|
|
522
600
|
});
|
|
@@ -528,340 +606,379 @@ export class Map extends maplibregl.Map {
|
|
|
528
606
|
* - a full style URL (possibly with API key)
|
|
529
607
|
* - a shorthand with only the MapTIler style name (eg. `"streets-v2"`)
|
|
530
608
|
* - a longer form with the prefix `"maptiler://"` (eg. `"maptiler://streets-v2"`)
|
|
531
|
-
* @param style
|
|
532
|
-
* @param options
|
|
533
|
-
* @returns
|
|
534
609
|
*/
|
|
535
|
-
setStyle(
|
|
536
|
-
style:
|
|
537
|
-
|
|
538
|
-
|
|
610
|
+
override setStyle(
|
|
611
|
+
style:
|
|
612
|
+
| null
|
|
613
|
+
| ReferenceMapStyle
|
|
614
|
+
| MapStyleVariant
|
|
615
|
+
| StyleSpecification
|
|
616
|
+
| string,
|
|
617
|
+
options?: StyleSwapOptions & StyleOptions,
|
|
618
|
+
): this {
|
|
619
|
+
this.minimap?.setStyle(style);
|
|
620
|
+
this.forceLanguageUpdate = true;
|
|
621
|
+
|
|
622
|
+
this.once("idle", () => {
|
|
623
|
+
this.forceLanguageUpdate = false;
|
|
624
|
+
});
|
|
625
|
+
|
|
539
626
|
return super.setStyle(styleToStyle(style), options);
|
|
540
627
|
}
|
|
541
628
|
|
|
542
629
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
630
|
+
* Adds a [MapLibre style layer](https://maplibre.org/maplibre-style-spec/layers)
|
|
631
|
+
* to the map's style.
|
|
632
|
+
*
|
|
633
|
+
* A layer defines how data from a specified source will be styled. Read more about layer types
|
|
634
|
+
* and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/layers).
|
|
635
|
+
*
|
|
636
|
+
* @param layer - The layer to add,
|
|
637
|
+
* conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
|
|
638
|
+
* less commonly, the {@link CustomLayerInterface} specification.
|
|
639
|
+
* The MapLibre Style Specification's layer definition is appropriate for most layers.
|
|
640
|
+
*
|
|
641
|
+
* @param beforeId - The ID of an existing layer to insert the new layer before,
|
|
642
|
+
* resulting in the new layer appearing visually beneath the existing layer.
|
|
643
|
+
* If this argument is not specified, the layer will be appended to the end of the layers array
|
|
644
|
+
* and appear visually above all other layers.
|
|
645
|
+
*
|
|
646
|
+
* @returns `this`
|
|
546
647
|
*/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
648
|
+
addLayer(
|
|
649
|
+
layer:
|
|
650
|
+
| (LayerSpecification & {
|
|
651
|
+
source?: string | SourceSpecification;
|
|
652
|
+
})
|
|
653
|
+
| CustomLayerInterface,
|
|
654
|
+
beforeId?: string,
|
|
655
|
+
): this {
|
|
656
|
+
this.minimap?.addLayer(layer, beforeId);
|
|
657
|
+
return super.addLayer(layer, beforeId);
|
|
552
658
|
}
|
|
553
659
|
|
|
554
660
|
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
661
|
+
* Moves a layer to a different z-position.
|
|
662
|
+
*
|
|
663
|
+
* @param id - The ID of the layer to move.
|
|
664
|
+
* @param beforeId - The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map.
|
|
665
|
+
* @returns `this`
|
|
666
|
+
*
|
|
667
|
+
* @example
|
|
668
|
+
* Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map.
|
|
669
|
+
* ```ts
|
|
670
|
+
* map.moveLayer('polygon', 'country-label');
|
|
671
|
+
* ```
|
|
557
672
|
*/
|
|
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;
|
|
673
|
+
moveLayer(id: string, beforeId?: string): this {
|
|
674
|
+
this.minimap?.moveLayer(id, beforeId);
|
|
675
|
+
return super.moveLayer(id, beforeId);
|
|
676
|
+
}
|
|
578
677
|
|
|
579
|
-
|
|
580
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Removes the layer with the given ID from the map's style.
|
|
680
|
+
*
|
|
681
|
+
* An {@link ErrorEvent} will be fired if the image parameter is invald.
|
|
682
|
+
*
|
|
683
|
+
* @param id - The ID of the layer to remove
|
|
684
|
+
* @returns `this`
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* If a layer with ID 'state-data' exists, remove it.
|
|
688
|
+
* ```ts
|
|
689
|
+
* if (map.getLayer('state-data')) map.removeLayer('state-data');
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
removeLayer(id: string): this {
|
|
693
|
+
this.minimap?.removeLayer(id);
|
|
694
|
+
return super.removeLayer(id);
|
|
695
|
+
}
|
|
581
696
|
|
|
582
|
-
|
|
583
|
-
|
|
697
|
+
/**
|
|
698
|
+
* Sets the zoom extent for the specified style layer. The zoom extent includes the
|
|
699
|
+
* [minimum zoom level](https://maplibre.org/maplibre-style-spec/layers/#minzoom)
|
|
700
|
+
* and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
|
|
701
|
+
* at which the layer will be rendered.
|
|
702
|
+
*
|
|
703
|
+
* Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
|
|
704
|
+
* minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
|
|
705
|
+
* zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
|
|
706
|
+
* layer will not be rendered at all zoom levels in the zoom range.
|
|
707
|
+
*/
|
|
708
|
+
setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number): this {
|
|
709
|
+
this.minimap?.setLayerZoomRange(layerId, minzoom, maxzoom);
|
|
710
|
+
return super.setLayerZoomRange(layerId, minzoom, maxzoom);
|
|
711
|
+
}
|
|
584
712
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
713
|
+
/**
|
|
714
|
+
* Sets the filter for the specified style layer.
|
|
715
|
+
*
|
|
716
|
+
* Filters control which features a style layer renders from its source.
|
|
717
|
+
* Any feature for which the filter expression evaluates to `true` will be
|
|
718
|
+
* rendered on the map. Those that are false will be hidden.
|
|
719
|
+
*
|
|
720
|
+
* Use `setFilter` to show a subset of your source data.
|
|
721
|
+
*
|
|
722
|
+
* To clear the filter, pass `null` or `undefined` as the second parameter.
|
|
723
|
+
*/
|
|
724
|
+
setFilter(
|
|
725
|
+
layerId: string,
|
|
726
|
+
filter?: FilterSpecification | null,
|
|
727
|
+
options?: StyleSetterOptions,
|
|
728
|
+
): this {
|
|
729
|
+
this.minimap?.setFilter(layerId, filter, options);
|
|
730
|
+
return super.setFilter(layerId, filter, options);
|
|
731
|
+
}
|
|
588
732
|
|
|
589
|
-
|
|
590
|
-
|
|
733
|
+
/**
|
|
734
|
+
* Sets the value of a paint property in the specified style layer.
|
|
735
|
+
*
|
|
736
|
+
* @param layerId - The ID of the layer to set the paint property in.
|
|
737
|
+
* @param name - The name of the paint property to set.
|
|
738
|
+
* @param value - The value of the paint property to set.
|
|
739
|
+
* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
|
|
740
|
+
* @param options - Options object.
|
|
741
|
+
* @returns `this`
|
|
742
|
+
* @example
|
|
743
|
+
* ```ts
|
|
744
|
+
* map.setPaintProperty('my-layer', 'fill-color', '#faafee');
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
setPaintProperty(
|
|
748
|
+
layerId: string,
|
|
749
|
+
name: string,
|
|
750
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
751
|
+
value: any,
|
|
752
|
+
options?: StyleSetterOptions,
|
|
753
|
+
): this {
|
|
754
|
+
this.minimap?.setPaintProperty(layerId, name, value, options);
|
|
755
|
+
return super.setPaintProperty(layerId, name, value, options);
|
|
756
|
+
}
|
|
591
757
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
758
|
+
/**
|
|
759
|
+
* Sets the value of a layout property in the specified style layer.
|
|
760
|
+
* Layout properties define how the layer is styled.
|
|
761
|
+
* Layout properties for layers of the same type are documented together.
|
|
762
|
+
* Layers of different types have different layout properties.
|
|
763
|
+
* See the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/) for the complete list of layout properties.
|
|
764
|
+
* @param layerId - The ID of the layer to set the layout property in.
|
|
765
|
+
* @param name - The name of the layout property to set.
|
|
766
|
+
* @param value - The value of the layout property to set.
|
|
767
|
+
* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
|
|
768
|
+
* @param options - Options object.
|
|
769
|
+
* @returns `this`
|
|
770
|
+
*/
|
|
771
|
+
setLayoutProperty(
|
|
772
|
+
layerId: string,
|
|
773
|
+
name: string,
|
|
774
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
775
|
+
value: any,
|
|
776
|
+
options?: StyleSetterOptions,
|
|
777
|
+
): this {
|
|
778
|
+
this.minimap?.setLayoutProperty(layerId, name, value, options);
|
|
779
|
+
return super.setLayoutProperty(layerId, name, value, options);
|
|
780
|
+
}
|
|
599
781
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
782
|
+
/**
|
|
783
|
+
* Sets the value of the style's glyphs property.
|
|
784
|
+
*
|
|
785
|
+
* @param glyphsUrl - Glyph URL to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/glyphs/).
|
|
786
|
+
* @param options - Options object.
|
|
787
|
+
* @returns `this`
|
|
788
|
+
* @example
|
|
789
|
+
* ```ts
|
|
790
|
+
* map.setGlyphs('https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf');
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
setGlyphs(glyphsUrl: string | null, options?: StyleSetterOptions): this {
|
|
794
|
+
this.minimap?.setGlyphs(glyphsUrl, options);
|
|
795
|
+
return super.setGlyphs(glyphsUrl, options);
|
|
796
|
+
}
|
|
603
797
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
798
|
+
private getStyleLanguage(): string | null {
|
|
799
|
+
if (!this.style.stylesheet.metadata) return null;
|
|
800
|
+
if (typeof this.style.stylesheet.metadata !== "object") return null;
|
|
607
801
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
802
|
+
if (
|
|
803
|
+
"maptiler:language" in this.style.stylesheet.metadata &&
|
|
804
|
+
typeof this.style.stylesheet.metadata["maptiler:language"] === "string"
|
|
805
|
+
) {
|
|
806
|
+
return this.style.stylesheet.metadata["maptiler:language"];
|
|
807
|
+
} else {
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
611
811
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
812
|
+
/**
|
|
813
|
+
* Define the primary language of the map. Note that not all the languages shorthands provided are available.
|
|
814
|
+
*/
|
|
815
|
+
setLanguage(language: LanguageString | string): void {
|
|
816
|
+
this.minimap?.map?.setLanguage(language);
|
|
817
|
+
this.onStyleReady(() => {
|
|
818
|
+
this.setPrimaryLanguage(language);
|
|
819
|
+
});
|
|
820
|
+
}
|
|
616
821
|
|
|
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
|
-
}
|
|
822
|
+
/**
|
|
823
|
+
* Define the primary language of the map. Note that not all the languages shorthands provided are available.
|
|
824
|
+
*/
|
|
670
825
|
|
|
671
|
-
|
|
672
|
-
|
|
826
|
+
private setPrimaryLanguage(language: LanguageString | string) {
|
|
827
|
+
const styleLanguage = this.getStyleLanguage();
|
|
828
|
+
|
|
829
|
+
// If the language is set to `STYLE` (which is the SDK default), but the language defined in
|
|
830
|
+
// the style is `auto`, we need to bypass some verification and modify the languages anyway
|
|
831
|
+
if (
|
|
832
|
+
!(
|
|
833
|
+
language === Language.STYLE &&
|
|
834
|
+
(styleLanguage === Language.AUTO || styleLanguage === Language.VISITOR)
|
|
835
|
+
)
|
|
836
|
+
) {
|
|
837
|
+
if (language !== Language.STYLE) {
|
|
838
|
+
this.languageAlwaysBeenStyle = false;
|
|
839
|
+
}
|
|
673
840
|
|
|
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
|
-
}
|
|
841
|
+
if (this.languageAlwaysBeenStyle) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
684
844
|
|
|
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
|
-
}
|
|
845
|
+
// No need to change the language
|
|
846
|
+
if (this.primaryLanguage === language && !this.forceLanguageUpdate) {
|
|
847
|
+
return;
|
|
721
848
|
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
849
|
+
}
|
|
724
850
|
|
|
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
|
-
);
|
|
851
|
+
if (!isLanguageSupported(language as string)) {
|
|
852
|
+
console.warn(`The language "${language}" is not supported.`);
|
|
736
853
|
return;
|
|
737
854
|
}
|
|
738
855
|
|
|
739
|
-
if (
|
|
856
|
+
if (this.primaryLanguage === Language.STYLE_LOCK) {
|
|
857
|
+
console.warn(
|
|
858
|
+
"The language cannot be changed because this map has been instantiated with the STYLE_LOCK language flag.",
|
|
859
|
+
);
|
|
740
860
|
return;
|
|
741
861
|
}
|
|
742
862
|
|
|
743
|
-
this.
|
|
863
|
+
this.primaryLanguage = language as LanguageString;
|
|
864
|
+
let languageNonStyle: LanguageString = language as LanguageString;
|
|
744
865
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
866
|
+
// STYLE needs to be translated into one of the other language,
|
|
867
|
+
// this is why it's addressed first
|
|
868
|
+
if (language === Language.STYLE) {
|
|
869
|
+
if (!styleLanguage) {
|
|
870
|
+
console.warn("The style has no default languages.");
|
|
871
|
+
return;
|
|
748
872
|
}
|
|
749
873
|
|
|
750
|
-
|
|
874
|
+
if (!isLanguageSupported(styleLanguage)) {
|
|
875
|
+
console.warn("The language defined in the style is not valid.");
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
751
878
|
|
|
752
|
-
|
|
753
|
-
|
|
879
|
+
languageNonStyle = styleLanguage as LanguageString;
|
|
880
|
+
}
|
|
754
881
|
|
|
755
|
-
|
|
756
|
-
|
|
882
|
+
// may be overwritten below
|
|
883
|
+
let langStr: string | LanguageString = Language.LOCAL;
|
|
757
884
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
/^\s*{\s*name\s*(:\s*(\S*))?\s*}(\s*){\s*name\s*(:\s*(\S*))?\s*}$/;
|
|
885
|
+
// will be overwritten below
|
|
886
|
+
let replacer: ExpressionSpecification | string = `{${langStr}}`;
|
|
761
887
|
|
|
762
|
-
|
|
888
|
+
if (languageNonStyle == Language.VISITOR) {
|
|
889
|
+
langStr = getBrowserLanguage();
|
|
890
|
+
replacer = [
|
|
891
|
+
"case",
|
|
892
|
+
["all", ["has", langStr], ["has", Language.LOCAL]],
|
|
893
|
+
[
|
|
894
|
+
"case",
|
|
895
|
+
["==", ["get", langStr], ["get", Language.LOCAL]],
|
|
896
|
+
["get", Language.LOCAL],
|
|
897
|
+
|
|
898
|
+
[
|
|
899
|
+
"format",
|
|
900
|
+
["get", langStr],
|
|
901
|
+
{ "font-scale": 0.8 },
|
|
902
|
+
"\n",
|
|
903
|
+
["get", Language.LOCAL],
|
|
904
|
+
{ "font-scale": 1.1 },
|
|
905
|
+
],
|
|
906
|
+
],
|
|
907
|
+
|
|
908
|
+
["get", Language.LOCAL],
|
|
909
|
+
];
|
|
910
|
+
} else if (languageNonStyle == Language.VISITOR_ENGLISH) {
|
|
911
|
+
langStr = Language.ENGLISH;
|
|
912
|
+
replacer = [
|
|
913
|
+
"case",
|
|
914
|
+
["all", ["has", langStr], ["has", Language.LOCAL]],
|
|
915
|
+
[
|
|
916
|
+
"case",
|
|
917
|
+
["==", ["get", langStr], ["get", Language.LOCAL]],
|
|
918
|
+
["get", Language.LOCAL],
|
|
919
|
+
|
|
920
|
+
[
|
|
921
|
+
"format",
|
|
922
|
+
["get", langStr],
|
|
923
|
+
{ "font-scale": 0.8 },
|
|
924
|
+
"\n",
|
|
925
|
+
["get", Language.LOCAL],
|
|
926
|
+
{ "font-scale": 1.1 },
|
|
927
|
+
],
|
|
928
|
+
],
|
|
929
|
+
["get", Language.LOCAL],
|
|
930
|
+
];
|
|
931
|
+
} else if (languageNonStyle === Language.AUTO) {
|
|
932
|
+
langStr = getBrowserLanguage();
|
|
933
|
+
replacer = [
|
|
934
|
+
"case",
|
|
935
|
+
["has", langStr],
|
|
936
|
+
["get", langStr],
|
|
937
|
+
["get", Language.LOCAL],
|
|
938
|
+
];
|
|
939
|
+
}
|
|
763
940
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
941
|
+
// This is for using the regular names as {name}
|
|
942
|
+
else if (languageNonStyle === Language.LOCAL) {
|
|
943
|
+
langStr = Language.LOCAL;
|
|
944
|
+
replacer = `{${langStr}}`;
|
|
945
|
+
}
|
|
767
946
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
947
|
+
// This section is for the regular language ISO codes
|
|
948
|
+
else {
|
|
949
|
+
langStr = languageNonStyle;
|
|
950
|
+
replacer = [
|
|
951
|
+
"case",
|
|
952
|
+
["has", langStr],
|
|
953
|
+
["get", langStr],
|
|
954
|
+
["get", Language.LOCAL],
|
|
955
|
+
];
|
|
956
|
+
}
|
|
771
957
|
|
|
772
|
-
|
|
773
|
-
continue;
|
|
774
|
-
}
|
|
958
|
+
const { layers } = this.getStyle();
|
|
775
959
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
960
|
+
for (const { id, layout } of layers) {
|
|
961
|
+
if (!layout) {
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
780
964
|
|
|
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
|
-
}
|
|
965
|
+
if (!("text-field" in layout)) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
847
968
|
|
|
848
|
-
|
|
849
|
-
}
|
|
969
|
+
const textFieldLayoutProp = this.getLayoutProperty(id, "text-field");
|
|
850
970
|
|
|
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
|
-
}
|
|
971
|
+
// If the label is not about a name, then we don't translate it
|
|
972
|
+
if (
|
|
973
|
+
typeof textFieldLayoutProp === "string" &&
|
|
974
|
+
(textFieldLayoutProp.toLowerCase().includes("ref") ||
|
|
975
|
+
textFieldLayoutProp.toLowerCase().includes("housenumber"))
|
|
976
|
+
) {
|
|
977
|
+
continue;
|
|
863
978
|
}
|
|
864
|
-
|
|
979
|
+
|
|
980
|
+
this.setLayoutProperty(id, "text-field", replacer);
|
|
981
|
+
}
|
|
865
982
|
}
|
|
866
983
|
|
|
867
984
|
/**
|
|
@@ -872,14 +989,6 @@ export class Map extends maplibregl.Map {
|
|
|
872
989
|
return this.primaryLanguage;
|
|
873
990
|
}
|
|
874
991
|
|
|
875
|
-
/**
|
|
876
|
-
* Get the secondary language
|
|
877
|
-
* @returns
|
|
878
|
-
*/
|
|
879
|
-
getSecondaryLanguage(): LanguageString {
|
|
880
|
-
return this.secondaryLanguage;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
992
|
/**
|
|
884
993
|
* Get the exaggeration factor applied to the terrain
|
|
885
994
|
* @returns
|
|
@@ -896,7 +1005,7 @@ export class Map extends maplibregl.Map {
|
|
|
896
1005
|
return this.isTerrainEnabled;
|
|
897
1006
|
}
|
|
898
1007
|
|
|
899
|
-
private growTerrain(exaggeration, durationMs = 1000) {
|
|
1008
|
+
private growTerrain(exaggeration: number, durationMs = 1000) {
|
|
900
1009
|
// This method assumes the terrain is already built
|
|
901
1010
|
if (!this.terrain) {
|
|
902
1011
|
return;
|
|
@@ -946,8 +1055,6 @@ export class Map extends maplibregl.Map {
|
|
|
946
1055
|
|
|
947
1056
|
/**
|
|
948
1057
|
* Enables the 3D terrain visualization
|
|
949
|
-
* @param exaggeration
|
|
950
|
-
* @returns
|
|
951
1058
|
*/
|
|
952
1059
|
enableTerrain(exaggeration = this.terrainExaggeration) {
|
|
953
1060
|
if (exaggeration < 0) {
|
|
@@ -1081,7 +1188,8 @@ export class Map extends maplibregl.Map {
|
|
|
1081
1188
|
this.terrain.exaggeration = 0;
|
|
1082
1189
|
this.terrainGrowing = false;
|
|
1083
1190
|
this.terrainFlattening = false;
|
|
1084
|
-
|
|
1191
|
+
// @ts-expect-error - https://github.com/maplibre/maplibre-gl-js/issues/2992
|
|
1192
|
+
this.setTerrain();
|
|
1085
1193
|
if (this.getSource(defaults.terrainSourceId)) {
|
|
1086
1194
|
this.removeSource(defaults.terrainSourceId);
|
|
1087
1195
|
}
|
|
@@ -1101,8 +1209,6 @@ export class Map extends maplibregl.Map {
|
|
|
1101
1209
|
* the method `.enableTerrain()` will be called.
|
|
1102
1210
|
* If `animate` is `true`, the terrain transformation will be animated in the span of 1 second.
|
|
1103
1211
|
* If `animate` is `false`, no animated transition to the newly defined exaggeration.
|
|
1104
|
-
* @param exaggeration
|
|
1105
|
-
* @param animate
|
|
1106
1212
|
*/
|
|
1107
1213
|
setTerrainExaggeration(exaggeration: number, animate = true) {
|
|
1108
1214
|
if (!animate && this.terrain) {
|
|
@@ -1117,9 +1223,8 @@ export class Map extends maplibregl.Map {
|
|
|
1117
1223
|
/**
|
|
1118
1224
|
* Perform an action when the style is ready. It could be at the moment of calling this method
|
|
1119
1225
|
* or later.
|
|
1120
|
-
* @param cb
|
|
1121
1226
|
*/
|
|
1122
|
-
private onStyleReady(cb) {
|
|
1227
|
+
private onStyleReady(cb: () => void) {
|
|
1123
1228
|
if (this.isStyleLoaded()) {
|
|
1124
1229
|
cb();
|
|
1125
1230
|
} else {
|
|
@@ -1136,14 +1241,17 @@ export class Map extends maplibregl.Map {
|
|
|
1136
1241
|
{
|
|
1137
1242
|
duration: 0,
|
|
1138
1243
|
padding: 100,
|
|
1139
|
-
}
|
|
1244
|
+
},
|
|
1140
1245
|
);
|
|
1141
1246
|
}
|
|
1142
1247
|
|
|
1143
1248
|
async centerOnIpPoint(zoom: number | undefined) {
|
|
1144
1249
|
const ipGeolocateResult = await geolocation.info();
|
|
1145
1250
|
this.jumpTo({
|
|
1146
|
-
center: [
|
|
1251
|
+
center: [
|
|
1252
|
+
ipGeolocateResult?.longitude ?? 0,
|
|
1253
|
+
ipGeolocateResult?.latitude ?? 0,
|
|
1254
|
+
],
|
|
1147
1255
|
zoom: zoom || 11,
|
|
1148
1256
|
});
|
|
1149
1257
|
}
|
|
@@ -1163,7 +1271,6 @@ export class Map extends maplibregl.Map {
|
|
|
1163
1271
|
* Get the SDK config object.
|
|
1164
1272
|
* This is convenient to dispatch the SDK configuration to externally built layers
|
|
1165
1273
|
* that do not directly have access to the SDK configuration but do have access to a Map instance.
|
|
1166
|
-
* @returns
|
|
1167
1274
|
*/
|
|
1168
1275
|
getSdkConfig(): SdkConfig {
|
|
1169
1276
|
return config;
|
|
@@ -1189,8 +1296,33 @@ export class Map extends maplibregl.Map {
|
|
|
1189
1296
|
* @example
|
|
1190
1297
|
* map.setTransformRequest((url: string, resourceType: string) => {});
|
|
1191
1298
|
*/
|
|
1192
|
-
setTransformRequest(
|
|
1299
|
+
override setTransformRequest(
|
|
1300
|
+
transformRequest: RequestTransformFunction,
|
|
1301
|
+
): this {
|
|
1193
1302
|
super.setTransformRequest(combineTransformRequest(transformRequest));
|
|
1194
1303
|
return this;
|
|
1195
1304
|
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Loads an image. This is an async equivalent of `Map.loadImage`
|
|
1308
|
+
*/
|
|
1309
|
+
async loadImageAsync(
|
|
1310
|
+
url: string,
|
|
1311
|
+
): Promise<HTMLImageElement | ImageBitmap | null | undefined> {
|
|
1312
|
+
return new Promise((resolve, reject) => {
|
|
1313
|
+
this.loadImage(
|
|
1314
|
+
url,
|
|
1315
|
+
(
|
|
1316
|
+
error: Error | null | undefined,
|
|
1317
|
+
image: HTMLImageElement | ImageBitmap | null | undefined,
|
|
1318
|
+
) => {
|
|
1319
|
+
if (error) {
|
|
1320
|
+
reject(error);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
resolve(image);
|
|
1324
|
+
},
|
|
1325
|
+
);
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1196
1328
|
}
|