@tileflow/core 0.1.0-alpha.13 → 0.1.0-alpha.16
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/README.md +109 -8
- package/dist/index.d.ts +917 -22
- package/dist/index.js +2134 -923
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -115,7 +115,11 @@ var themePresets = {
|
|
|
115
115
|
var defaultTypography = {
|
|
116
116
|
font: "Noto Sans",
|
|
117
117
|
fontFamily: "Noto Sans",
|
|
118
|
-
weight: "regular"
|
|
118
|
+
weight: "regular",
|
|
119
|
+
places: { font: "Noto Sans", fontFamily: "Noto Sans", weight: "regular" },
|
|
120
|
+
roads: { font: "Noto Sans", fontFamily: "Noto Sans", weight: "regular" },
|
|
121
|
+
water: { font: "Noto Sans", fontFamily: "Noto Sans", weight: "regular" },
|
|
122
|
+
poi: { font: "Noto Sans", fontFamily: "Noto Sans", weight: "regular" }
|
|
119
123
|
};
|
|
120
124
|
var fontWeightLabels = {
|
|
121
125
|
regular: "Regular",
|
|
@@ -219,6 +223,10 @@ function resolveColors(overrides = {}, defaults = defaultColors, moduleOverrides
|
|
|
219
223
|
const buildingDefaults = {
|
|
220
224
|
extrusion: mix(base.building, base.text, 0.08),
|
|
221
225
|
fill: base.building,
|
|
226
|
+
highRise: base.building,
|
|
227
|
+
highRiseOutline: mix(base.building, base.text, 0.2),
|
|
228
|
+
lowRise: mix(base.building, base.land, 0.55),
|
|
229
|
+
lowRiseOutline: mix(base.building, base.land, 0.2),
|
|
222
230
|
outline: mix(base.building, base.text, 0.2)
|
|
223
231
|
};
|
|
224
232
|
const boundaryDefaults = {
|
|
@@ -250,15 +258,35 @@ function resolveColors(overrides = {}, defaults = defaultColors, moduleOverrides
|
|
|
250
258
|
};
|
|
251
259
|
}
|
|
252
260
|
function resolveTypography(overrides = {}, defaults = defaultTypography) {
|
|
253
|
-
const
|
|
261
|
+
const base = resolveTypographyStyle(overrides, defaults);
|
|
262
|
+
return {
|
|
263
|
+
...base,
|
|
264
|
+
places: resolveTypographyDomain(overrides.places, overrides, defaults.places, base),
|
|
265
|
+
roads: resolveTypographyDomain(overrides.roads, overrides, defaults.roads, base),
|
|
266
|
+
water: resolveTypographyDomain(overrides.water, overrides, defaults.water, base),
|
|
267
|
+
poi: resolveTypographyDomain(overrides.poi, overrides, defaults.poi, base)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function textFont(typography, domain) {
|
|
271
|
+
const style = domain ? typography[domain] : typography;
|
|
272
|
+
return [`${style.font} ${fontWeightLabels[style.weight]}`];
|
|
273
|
+
}
|
|
274
|
+
function resolveTypographyStyle(overrides, defaults) {
|
|
275
|
+
const font = overrides?.font ?? overrides?.fontFamily ?? defaults.font;
|
|
254
276
|
return {
|
|
255
277
|
font,
|
|
256
278
|
fontFamily: font,
|
|
257
|
-
weight: overrides
|
|
279
|
+
weight: overrides?.weight ?? defaults.weight
|
|
258
280
|
};
|
|
259
281
|
}
|
|
260
|
-
function
|
|
261
|
-
|
|
282
|
+
function resolveTypographyDomain(domainOverrides, globalOverrides, defaults, resolvedGlobal) {
|
|
283
|
+
const globalFontOverridden = Boolean(globalOverrides.font || globalOverrides.fontFamily);
|
|
284
|
+
const font = domainOverrides?.font ?? domainOverrides?.fontFamily ?? (globalFontOverridden ? resolvedGlobal.font : defaults.font);
|
|
285
|
+
return {
|
|
286
|
+
font,
|
|
287
|
+
fontFamily: font,
|
|
288
|
+
weight: domainOverrides?.weight ?? globalOverrides.weight ?? defaults.weight
|
|
289
|
+
};
|
|
262
290
|
}
|
|
263
291
|
function resolveLegacyTheme(theme) {
|
|
264
292
|
const preset = themePresets[theme];
|
|
@@ -342,13 +370,17 @@ function isLegacyTheme(value) {
|
|
|
342
370
|
function resolveThemeTypography(theme) {
|
|
343
371
|
const font = theme.typography?.font ?? theme.typography?.fontFamily ?? theme.fonts?.labels ?? theme.fonts?.body;
|
|
344
372
|
return {
|
|
373
|
+
...theme.typography?.places ? { places: theme.typography.places } : {},
|
|
374
|
+
...theme.typography?.poi ? { poi: theme.typography.poi } : {},
|
|
375
|
+
...theme.typography?.roads ? { roads: theme.typography.roads } : {},
|
|
376
|
+
...theme.typography?.water ? { water: theme.typography.water } : {},
|
|
345
377
|
...theme.typography?.weight ? { weight: theme.typography.weight } : {},
|
|
346
378
|
...font ? { font } : {}
|
|
347
379
|
};
|
|
348
380
|
}
|
|
349
381
|
function hasTypographyConfig(theme) {
|
|
350
382
|
return Boolean(
|
|
351
|
-
theme.typography?.font || theme.typography?.fontFamily || theme.typography?.weight || theme.fonts?.labels || theme.fonts?.body
|
|
383
|
+
theme.typography?.font || theme.typography?.fontFamily || theme.typography?.weight || theme.typography?.places || theme.typography?.poi || theme.typography?.roads || theme.typography?.water || theme.fonts?.labels || theme.fonts?.body
|
|
352
384
|
);
|
|
353
385
|
}
|
|
354
386
|
function normalizeColorAliases(colors) {
|
|
@@ -512,858 +544,1070 @@ function rgbToHex(r, g, b) {
|
|
|
512
544
|
return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
513
545
|
}
|
|
514
546
|
|
|
515
|
-
// src/modules/
|
|
516
|
-
var
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
547
|
+
// src/modules/roads/index.ts
|
|
548
|
+
var moduleRoadDefaults = {
|
|
549
|
+
detail: "streets",
|
|
550
|
+
extras: { ferry: false, paths: false, rail: false },
|
|
551
|
+
hierarchy: "clear",
|
|
552
|
+
oneWayMarkers: false,
|
|
553
|
+
outline: "subtle",
|
|
554
|
+
weight: "regular"
|
|
555
|
+
};
|
|
556
|
+
var defaultRoadWidthScale = {
|
|
557
|
+
motorway: 1,
|
|
558
|
+
trunk: 1,
|
|
559
|
+
primary: 1,
|
|
560
|
+
secondary: 1,
|
|
561
|
+
tertiary: 1,
|
|
562
|
+
minor: 1,
|
|
563
|
+
service: 1,
|
|
564
|
+
track: 1,
|
|
565
|
+
path: 1
|
|
521
566
|
};
|
|
522
|
-
var majorPlaceClasses = ["continent", "country", "state", "city", "town"];
|
|
523
567
|
var highwayRoadClasses = ["motorway", "trunk"];
|
|
524
568
|
var arterialRoadClasses = ["primary", "secondary", "tertiary"];
|
|
525
569
|
var majorRoadClasses = [...highwayRoadClasses, ...arterialRoadClasses];
|
|
526
|
-
var majorRoadClassSet = new Set(majorRoadClasses);
|
|
527
570
|
var streetRoadClasses = ["minor"];
|
|
528
571
|
var serviceRoadClasses = ["service", "track"];
|
|
529
|
-
var
|
|
530
|
-
|
|
531
|
-
|
|
572
|
+
var majorRoadClassSet = new Set(majorRoadClasses);
|
|
573
|
+
function roadClassesForDetail(detail) {
|
|
574
|
+
if (detail === "none") return [];
|
|
575
|
+
return [
|
|
576
|
+
...detail === "highways" ? highwayRoadClasses : majorRoadClasses,
|
|
577
|
+
...detail === "streets" || detail === "all" ? streetRoadClasses : [],
|
|
578
|
+
...detail === "all" ? serviceRoadClasses : []
|
|
579
|
+
];
|
|
580
|
+
}
|
|
581
|
+
function isMajorRoadClass(roadClass) {
|
|
582
|
+
return majorRoadClassSet.has(roadClass);
|
|
583
|
+
}
|
|
584
|
+
function roads(options = {}) {
|
|
532
585
|
return {
|
|
533
|
-
type: "
|
|
534
|
-
...options.
|
|
535
|
-
...options.
|
|
536
|
-
...options.
|
|
537
|
-
...options.
|
|
586
|
+
type: "roads",
|
|
587
|
+
...options.detail ? { detail: options.detail } : {},
|
|
588
|
+
...options.extras ? { extras: { ...options.extras } } : {},
|
|
589
|
+
...options.hierarchy ? { hierarchy: options.hierarchy } : {},
|
|
590
|
+
...options.oneWayMarkers !== void 0 ? { oneWayMarkers: options.oneWayMarkers } : {},
|
|
591
|
+
...options.outline ? { outline: options.outline } : {},
|
|
592
|
+
...options.weight ? { weight: options.weight } : {},
|
|
593
|
+
...options.widthScale ? { widthScale: { ...options.widthScale } } : {}
|
|
538
594
|
};
|
|
539
595
|
}
|
|
540
|
-
function
|
|
541
|
-
const fallbackMode =
|
|
542
|
-
const defaults = moduleConfig ?
|
|
596
|
+
function resolveRoads(legacyRoads, moduleConfig, fallback) {
|
|
597
|
+
const fallbackMode = legacyRoads ?? fallback;
|
|
598
|
+
const defaults = moduleConfig ? moduleRoadDefaults : roadDefaultsFromLegacyMode(fallbackMode);
|
|
543
599
|
const resolved = {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
600
|
+
detail: moduleConfig?.detail ?? defaults.detail,
|
|
601
|
+
extras: {
|
|
602
|
+
ferry: moduleConfig?.extras?.ferry ?? defaults.extras.ferry,
|
|
603
|
+
paths: moduleConfig?.extras?.paths ?? defaults.extras.paths,
|
|
604
|
+
rail: moduleConfig?.extras?.rail ?? defaults.extras.rail
|
|
605
|
+
},
|
|
606
|
+
hierarchy: moduleConfig?.hierarchy ?? defaults.hierarchy,
|
|
607
|
+
oneWayMarkers: moduleConfig?.oneWayMarkers ?? defaults.oneWayMarkers,
|
|
608
|
+
outline: moduleConfig?.outline ?? defaults.outline,
|
|
609
|
+
weight: moduleConfig?.weight ?? defaults.weight,
|
|
610
|
+
...moduleConfig?.widthScale ? { widthScale: { ...defaultRoadWidthScale, ...moduleConfig.widthScale } } : {}
|
|
548
611
|
};
|
|
549
612
|
return {
|
|
550
613
|
...resolved,
|
|
551
|
-
mode:
|
|
614
|
+
mode: legacyModeFromResolvedRoads(resolved)
|
|
552
615
|
};
|
|
553
616
|
}
|
|
554
|
-
function
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
617
|
+
function roadStyleMetrics(options) {
|
|
618
|
+
const hierarchy = {
|
|
619
|
+
subtle: {
|
|
620
|
+
majorOpacity: 0.92,
|
|
621
|
+
majorWidthScale: 1,
|
|
622
|
+
minorOpacity: 0.82,
|
|
623
|
+
minorWidthScale: 0.94
|
|
624
|
+
},
|
|
625
|
+
clear: {
|
|
626
|
+
majorOpacity: 0.92,
|
|
627
|
+
majorWidthScale: 1,
|
|
628
|
+
minorOpacity: 0.68,
|
|
629
|
+
minorWidthScale: 0.84
|
|
630
|
+
},
|
|
631
|
+
strong: {
|
|
632
|
+
majorOpacity: 0.92,
|
|
633
|
+
majorWidthScale: 1,
|
|
634
|
+
minorOpacity: 0.52,
|
|
635
|
+
minorWidthScale: 0.7
|
|
636
|
+
}
|
|
637
|
+
}[options.hierarchy];
|
|
638
|
+
const weightScale = {
|
|
639
|
+
thin: 0.78,
|
|
640
|
+
regular: 1,
|
|
641
|
+
bold: 1.24
|
|
642
|
+
}[options.weight];
|
|
643
|
+
const outlineOpacity = {
|
|
644
|
+
none: 0,
|
|
645
|
+
subtle: 0.18,
|
|
646
|
+
strong: 0.34
|
|
647
|
+
}[options.outline];
|
|
648
|
+
return {
|
|
649
|
+
...hierarchy,
|
|
650
|
+
outlineOpacity,
|
|
651
|
+
weightScale
|
|
652
|
+
};
|
|
574
653
|
}
|
|
575
|
-
function
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
654
|
+
function roadLayers(source, sourceLayer, colors, options, typography) {
|
|
655
|
+
if (options.detail === "none") {
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
658
|
+
const metrics = roadStyleMetrics(options);
|
|
659
|
+
const roadClasses = roadClassesForDetail(options.detail);
|
|
660
|
+
const selectedMajorRoadClasses = roadClasses.filter(isMajorRoadClass);
|
|
661
|
+
const showsStreetRoads = roadClasses.includes("minor");
|
|
662
|
+
const showsServiceRoads = roadClasses.includes("service");
|
|
663
|
+
const transportClasses = [
|
|
664
|
+
...roadClasses,
|
|
665
|
+
...options.extras.paths ? ["path"] : [],
|
|
666
|
+
...options.extras.rail ? ["rail", "transit"] : [],
|
|
667
|
+
...options.extras.ferry ? ["ferry"] : []
|
|
668
|
+
];
|
|
669
|
+
const roadClassColor = roadClassColorExpression(colors);
|
|
670
|
+
const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
|
|
671
|
+
const majorFilter = classFilter(selectedMajorRoadClasses);
|
|
672
|
+
const streetFilter = classFilter(streetRoadClasses);
|
|
673
|
+
const serviceFilter = classFilter(serviceRoadClasses);
|
|
674
|
+
const roadFilter = classFilter(roadClasses);
|
|
675
|
+
const transportFilter = classFilter(transportClasses);
|
|
676
|
+
const layers = [];
|
|
677
|
+
if (options.outline !== "none") {
|
|
678
|
+
layers.push({
|
|
679
|
+
id: "roads-tunnels-casing",
|
|
680
|
+
type: "line",
|
|
581
681
|
source,
|
|
582
682
|
"source-layer": sourceLayer,
|
|
583
|
-
minzoom:
|
|
584
|
-
filter:
|
|
585
|
-
layout: {
|
|
586
|
-
"text-field": labelTextField(labelsConfig.language),
|
|
587
|
-
"text-font": textFont(typography),
|
|
588
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
|
|
589
|
-
"text-letter-spacing": 0,
|
|
590
|
-
"text-padding": 4
|
|
591
|
-
},
|
|
683
|
+
minzoom: 10,
|
|
684
|
+
filter: ["all", ["==", ["get", "brunnel"], "tunnel"], transportFilter],
|
|
592
685
|
paint: {
|
|
593
|
-
"
|
|
594
|
-
"
|
|
595
|
-
"
|
|
686
|
+
"line-color": colors.roads.tunnel,
|
|
687
|
+
"line-dasharray": [0.6, 0.8],
|
|
688
|
+
"line-opacity": roadOpacityExpression(
|
|
689
|
+
metrics.outlineOpacity * 0.85,
|
|
690
|
+
metrics.outlineOpacity * 0.85
|
|
691
|
+
),
|
|
692
|
+
"line-width": roadWidthExpression(
|
|
693
|
+
[
|
|
694
|
+
[10, 0.8],
|
|
695
|
+
[14, 2.2],
|
|
696
|
+
[16, 4.8]
|
|
697
|
+
],
|
|
698
|
+
metrics,
|
|
699
|
+
options.widthScale
|
|
700
|
+
)
|
|
596
701
|
}
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
layers.push({
|
|
705
|
+
id: "roads-tunnels",
|
|
706
|
+
type: "line",
|
|
707
|
+
source,
|
|
708
|
+
"source-layer": sourceLayer,
|
|
709
|
+
minzoom: 10,
|
|
710
|
+
filter: ["all", ["==", ["get", "brunnel"], "tunnel"], transportFilter],
|
|
711
|
+
paint: {
|
|
712
|
+
"line-color": roadClassColor,
|
|
713
|
+
"line-dasharray": [0.6, 0.8],
|
|
714
|
+
"line-opacity": roadOpacityExpression(
|
|
715
|
+
metrics.majorOpacity * 0.28,
|
|
716
|
+
metrics.minorOpacity * 0.28
|
|
717
|
+
),
|
|
718
|
+
"line-width": roadWidthExpression(
|
|
719
|
+
[
|
|
720
|
+
[10, 0.25],
|
|
721
|
+
[14, 1],
|
|
722
|
+
[16, 2.4]
|
|
723
|
+
],
|
|
724
|
+
metrics,
|
|
725
|
+
options.widthScale
|
|
726
|
+
)
|
|
597
727
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig, typography) {
|
|
601
|
-
const classes = visibleRoadLabelClasses(labelsConfig.roads, roadsConfig);
|
|
602
|
-
const majorClasses = classes.filter((roadClass) => majorRoadClassSet.has(roadClass));
|
|
603
|
-
const minorClasses = classes.filter((roadClass) => !majorRoadClassSet.has(roadClass));
|
|
604
|
-
if (classes.length === 0) return [];
|
|
605
|
-
const layers = [];
|
|
606
|
-
const sharedLayout = {
|
|
607
|
-
"symbol-placement": "line",
|
|
608
|
-
"text-field": labelTextField(labelsConfig.language),
|
|
609
|
-
"text-font": textFont(typography),
|
|
610
|
-
"text-rotation-alignment": "map",
|
|
611
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 11, 11, 14, 13, 17, 15]
|
|
612
|
-
};
|
|
613
|
-
const sharedPaint = {
|
|
614
|
-
"text-color": colors.labels.road,
|
|
615
|
-
"text-halo-color": colors.labels.halo,
|
|
616
|
-
"text-halo-width": 1
|
|
617
|
-
};
|
|
618
|
-
if (majorClasses.length > 0) {
|
|
619
|
-
const namedMajorClasses = majorClasses.filter((roadClass) => roadClass !== "motorway");
|
|
728
|
+
});
|
|
729
|
+
if (options.outline !== "none") {
|
|
620
730
|
layers.push({
|
|
621
|
-
id: "
|
|
622
|
-
type: "
|
|
731
|
+
id: "roads-casing",
|
|
732
|
+
type: "line",
|
|
623
733
|
source,
|
|
624
734
|
"source-layer": sourceLayer,
|
|
625
|
-
minzoom:
|
|
626
|
-
filter: ["all",
|
|
627
|
-
|
|
628
|
-
"
|
|
629
|
-
"
|
|
630
|
-
"
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
735
|
+
minzoom: 5,
|
|
736
|
+
filter: ["all", nonTunnelRoadFilter, roadFilter],
|
|
737
|
+
paint: {
|
|
738
|
+
"line-color": colors.roads.casing,
|
|
739
|
+
"line-opacity": roadOpacityExpression(metrics.outlineOpacity, metrics.outlineOpacity),
|
|
740
|
+
"line-width": roadWidthExpression(
|
|
741
|
+
[
|
|
742
|
+
[6, 0.7],
|
|
743
|
+
[12, 1.8],
|
|
744
|
+
[16, 5.4]
|
|
745
|
+
],
|
|
746
|
+
metrics,
|
|
747
|
+
options.widthScale
|
|
748
|
+
)
|
|
749
|
+
}
|
|
636
750
|
});
|
|
637
|
-
if (namedMajorClasses.length > 0) {
|
|
638
|
-
layers.unshift({
|
|
639
|
-
id: "road-labels-major",
|
|
640
|
-
type: "symbol",
|
|
641
|
-
source,
|
|
642
|
-
"source-layer": sourceLayer,
|
|
643
|
-
minzoom: 10,
|
|
644
|
-
filter: ["all", ["has", "name"], classFilter(namedMajorClasses)],
|
|
645
|
-
layout: sharedLayout,
|
|
646
|
-
paint: sharedPaint
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
751
|
}
|
|
650
|
-
if (
|
|
752
|
+
if (options.extras.ferry) {
|
|
651
753
|
layers.push({
|
|
652
|
-
id: "
|
|
653
|
-
type: "
|
|
754
|
+
id: "roads-ferry",
|
|
755
|
+
type: "line",
|
|
654
756
|
source,
|
|
655
757
|
"source-layer": sourceLayer,
|
|
656
|
-
minzoom:
|
|
657
|
-
filter: ["all",
|
|
658
|
-
|
|
659
|
-
|
|
758
|
+
minzoom: 5,
|
|
759
|
+
filter: ["all", nonTunnelRoadFilter, classFilter(["ferry"])],
|
|
760
|
+
paint: {
|
|
761
|
+
"line-color": colors.roads.ferry,
|
|
762
|
+
"line-dasharray": [1.2, 1.1],
|
|
763
|
+
"line-opacity": metrics.majorOpacity * 0.8,
|
|
764
|
+
"line-width": zoomWidth(
|
|
765
|
+
[
|
|
766
|
+
[5, 0.25],
|
|
767
|
+
[12, 0.8],
|
|
768
|
+
[16, 2.6]
|
|
769
|
+
],
|
|
770
|
+
metrics.weightScale
|
|
771
|
+
)
|
|
772
|
+
}
|
|
660
773
|
});
|
|
661
774
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const waterFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter(majorWaterClasses)] : ["has", "name"];
|
|
667
|
-
const waterwayFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter(majorWaterwayClasses)] : ["has", "name"];
|
|
668
|
-
const paint = {
|
|
669
|
-
"text-color": colors.labels.water,
|
|
670
|
-
"text-halo-color": colors.labels.halo,
|
|
671
|
-
"text-halo-width": 1
|
|
672
|
-
};
|
|
673
|
-
return [
|
|
674
|
-
{
|
|
675
|
-
id: "water-labels",
|
|
676
|
-
type: "symbol",
|
|
775
|
+
if (options.extras.paths) {
|
|
776
|
+
layers.push({
|
|
777
|
+
id: "roads-paths",
|
|
778
|
+
type: "line",
|
|
677
779
|
source,
|
|
678
|
-
"source-layer":
|
|
679
|
-
minzoom:
|
|
680
|
-
filter: ["all",
|
|
681
|
-
|
|
682
|
-
"
|
|
683
|
-
"
|
|
684
|
-
"
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
780
|
+
"source-layer": sourceLayer,
|
|
781
|
+
minzoom: 10,
|
|
782
|
+
filter: ["all", nonTunnelRoadFilter, classFilter(["path"])],
|
|
783
|
+
paint: {
|
|
784
|
+
"line-color": colors.roads.path,
|
|
785
|
+
"line-dasharray": [0.7, 0.7],
|
|
786
|
+
"line-opacity": metrics.minorOpacity * 0.77,
|
|
787
|
+
"line-width": zoomWidth(
|
|
788
|
+
[
|
|
789
|
+
[10, 0.25],
|
|
790
|
+
[14, 0.8],
|
|
791
|
+
[16, 2.2]
|
|
792
|
+
],
|
|
793
|
+
metrics.weightScale * metrics.minorWidthScale * (options.widthScale?.path ?? 1)
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
if (options.extras.rail) {
|
|
799
|
+
layers.push({
|
|
800
|
+
id: "roads-rail",
|
|
801
|
+
type: "line",
|
|
802
|
+
source,
|
|
803
|
+
"source-layer": sourceLayer,
|
|
804
|
+
minzoom: 7,
|
|
805
|
+
filter: ["all", nonTunnelRoadFilter, classFilter(["rail", "transit"])],
|
|
806
|
+
paint: {
|
|
807
|
+
"line-color": colors.roads.rail,
|
|
808
|
+
"line-opacity": metrics.minorOpacity * 0.65,
|
|
809
|
+
"line-width": zoomWidth(
|
|
810
|
+
[
|
|
811
|
+
[7, 0.35],
|
|
812
|
+
[12, 0.9],
|
|
813
|
+
[16, 2.4]
|
|
814
|
+
],
|
|
815
|
+
metrics.weightScale * metrics.minorWidthScale
|
|
816
|
+
)
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
if (showsServiceRoads) {
|
|
821
|
+
layers.push({
|
|
822
|
+
id: "roads-service",
|
|
823
|
+
type: "line",
|
|
824
|
+
source,
|
|
825
|
+
"source-layer": sourceLayer,
|
|
826
|
+
minzoom: 14,
|
|
827
|
+
filter: ["all", nonTunnelRoadFilter, serviceFilter],
|
|
828
|
+
paint: {
|
|
829
|
+
"line-color": roadClassColor,
|
|
830
|
+
"line-opacity": metrics.minorOpacity,
|
|
831
|
+
"line-width": zoomWidth(
|
|
832
|
+
[
|
|
833
|
+
[14, 0.6],
|
|
834
|
+
[16, 2.2]
|
|
835
|
+
],
|
|
836
|
+
metrics.weightScale * metrics.minorWidthScale,
|
|
837
|
+
options.widthScale
|
|
838
|
+
)
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
if (showsStreetRoads) {
|
|
843
|
+
layers.push({
|
|
844
|
+
id: "roads-minor",
|
|
845
|
+
type: "line",
|
|
846
|
+
source,
|
|
847
|
+
"source-layer": sourceLayer,
|
|
848
|
+
minzoom: 12,
|
|
849
|
+
filter: ["all", nonTunnelRoadFilter, streetFilter],
|
|
850
|
+
paint: {
|
|
851
|
+
"line-color": roadClassColor,
|
|
852
|
+
"line-opacity": metrics.minorOpacity,
|
|
853
|
+
"line-width": zoomWidth(
|
|
854
|
+
[
|
|
855
|
+
[12, 0.8],
|
|
856
|
+
[16, 3]
|
|
857
|
+
],
|
|
858
|
+
metrics.weightScale * metrics.minorWidthScale,
|
|
859
|
+
options.widthScale
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
layers.push(
|
|
688
865
|
{
|
|
689
|
-
id: "
|
|
690
|
-
type: "
|
|
866
|
+
id: "roads-major",
|
|
867
|
+
type: "line",
|
|
691
868
|
source,
|
|
692
|
-
"source-layer":
|
|
869
|
+
"source-layer": sourceLayer,
|
|
693
870
|
minzoom: 5,
|
|
694
|
-
filter: ["all",
|
|
695
|
-
|
|
696
|
-
"
|
|
697
|
-
"
|
|
698
|
-
"
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
871
|
+
filter: ["all", nonTunnelRoadFilter, majorFilter],
|
|
872
|
+
paint: {
|
|
873
|
+
"line-color": roadClassColor,
|
|
874
|
+
"line-opacity": metrics.majorOpacity,
|
|
875
|
+
"line-width": zoomWidth(
|
|
876
|
+
[
|
|
877
|
+
[5, 0.35],
|
|
878
|
+
[12, 1.2],
|
|
879
|
+
[16, 4]
|
|
880
|
+
],
|
|
881
|
+
metrics.weightScale * metrics.majorWidthScale,
|
|
882
|
+
options.widthScale
|
|
883
|
+
)
|
|
884
|
+
}
|
|
702
885
|
},
|
|
703
886
|
{
|
|
704
|
-
id: "
|
|
705
|
-
type: "
|
|
887
|
+
id: "roads-bridges",
|
|
888
|
+
type: "line",
|
|
706
889
|
source,
|
|
707
|
-
"source-layer":
|
|
890
|
+
"source-layer": sourceLayer,
|
|
708
891
|
minzoom: 10,
|
|
709
|
-
filter: ["all", ["==", ["
|
|
710
|
-
|
|
711
|
-
"
|
|
712
|
-
"
|
|
713
|
-
"
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
892
|
+
filter: ["all", ["==", ["get", "brunnel"], "bridge"], transportFilter],
|
|
893
|
+
paint: {
|
|
894
|
+
"line-color": colors.roads.bridge,
|
|
895
|
+
"line-opacity": roadOpacityExpression(metrics.majorOpacity, metrics.minorOpacity),
|
|
896
|
+
"line-width": roadWidthExpression(
|
|
897
|
+
[
|
|
898
|
+
[10, 0.45],
|
|
899
|
+
[14, 1.6],
|
|
900
|
+
[16, 4.4]
|
|
901
|
+
],
|
|
902
|
+
metrics,
|
|
903
|
+
options.widthScale
|
|
904
|
+
)
|
|
905
|
+
}
|
|
718
906
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (detail === "none" || roadsConfig.detail === "none") return [];
|
|
723
|
-
const visibleRoadClasses = roadsConfig.detail === "highways" ? [...highwayRoadClasses] : [
|
|
724
|
-
...majorRoadClasses,
|
|
725
|
-
...roadsConfig.detail === "streets" || roadsConfig.detail === "all" ? streetRoadClasses : [],
|
|
726
|
-
...roadsConfig.detail === "all" ? serviceRoadClasses : [],
|
|
727
|
-
...roadsConfig.extras.paths ? ["path"] : []
|
|
728
|
-
];
|
|
729
|
-
const requestedClasses = detail === "major" ? [...majorRoadClasses] : [...majorRoadClasses, ...streetRoadClasses, ...serviceRoadClasses, "path"];
|
|
730
|
-
const visible = new Set(visibleRoadClasses);
|
|
731
|
-
return requestedClasses.filter((roadClass) => visible.has(roadClass));
|
|
732
|
-
}
|
|
733
|
-
function labelDefaultsFromLegacyMode(mode) {
|
|
734
|
-
if (mode === "none") {
|
|
735
|
-
return { language: "auto", places: "none", roads: "none", water: "none" };
|
|
736
|
-
}
|
|
737
|
-
if (mode === "essential") {
|
|
738
|
-
return { language: "auto", places: "major", roads: "major", water: "major" };
|
|
907
|
+
);
|
|
908
|
+
if (options.oneWayMarkers) {
|
|
909
|
+
layers.push(oneWayMarkerLayer(source, sourceLayer, colors, typography));
|
|
739
910
|
}
|
|
740
|
-
return
|
|
911
|
+
return layers;
|
|
741
912
|
}
|
|
742
|
-
function
|
|
743
|
-
if (
|
|
744
|
-
return
|
|
913
|
+
function roadDefaultsFromLegacyMode(mode) {
|
|
914
|
+
if (mode === "hidden") {
|
|
915
|
+
return {
|
|
916
|
+
detail: "none",
|
|
917
|
+
extras: { ferry: false, paths: false, rail: false },
|
|
918
|
+
hierarchy: "clear",
|
|
919
|
+
oneWayMarkers: false,
|
|
920
|
+
outline: "subtle",
|
|
921
|
+
weight: "regular"
|
|
922
|
+
};
|
|
745
923
|
}
|
|
746
|
-
if (
|
|
747
|
-
return
|
|
924
|
+
if (mode === "soft") {
|
|
925
|
+
return {
|
|
926
|
+
detail: "streets",
|
|
927
|
+
extras: { ferry: false, paths: false, rail: false },
|
|
928
|
+
hierarchy: "subtle",
|
|
929
|
+
oneWayMarkers: false,
|
|
930
|
+
outline: "subtle",
|
|
931
|
+
weight: "thin"
|
|
932
|
+
};
|
|
748
933
|
}
|
|
749
|
-
|
|
934
|
+
if (mode === "detailed") {
|
|
935
|
+
return {
|
|
936
|
+
detail: "all",
|
|
937
|
+
extras: { ferry: true, paths: true, rail: true },
|
|
938
|
+
hierarchy: "clear",
|
|
939
|
+
oneWayMarkers: false,
|
|
940
|
+
outline: "strong",
|
|
941
|
+
weight: "regular"
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
detail: "streets",
|
|
946
|
+
extras: { ferry: true, paths: true, rail: true },
|
|
947
|
+
hierarchy: "clear",
|
|
948
|
+
oneWayMarkers: false,
|
|
949
|
+
outline: "subtle",
|
|
950
|
+
weight: "regular"
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function legacyModeFromResolvedRoads(options) {
|
|
954
|
+
if (options.detail === "none") return "hidden";
|
|
955
|
+
if (options.hierarchy === "subtle") return "soft";
|
|
956
|
+
if (options.hierarchy === "strong" || options.detail === "all") return "detailed";
|
|
957
|
+
return "standard";
|
|
750
958
|
}
|
|
751
959
|
function classFilter(classes) {
|
|
752
960
|
return ["match", ["get", "class"], classes, true, false];
|
|
753
961
|
}
|
|
754
|
-
function
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
962
|
+
function roadOpacityExpression(majorOpacity, minorOpacity) {
|
|
963
|
+
return [
|
|
964
|
+
"step",
|
|
965
|
+
["zoom"],
|
|
966
|
+
roadOpacityAtZoom(majorOpacity, minorOpacity, 0, 0),
|
|
967
|
+
12,
|
|
968
|
+
roadOpacityAtZoom(majorOpacity, minorOpacity, minorOpacity, 0),
|
|
969
|
+
14,
|
|
970
|
+
roadOpacityAtZoom(majorOpacity, minorOpacity, minorOpacity, minorOpacity)
|
|
971
|
+
];
|
|
762
972
|
}
|
|
763
|
-
|
|
764
|
-
// src/modules/poi/index.ts
|
|
765
|
-
var defaultPoiMinZoom = {
|
|
766
|
-
none: 24,
|
|
767
|
-
minimal: 15,
|
|
768
|
-
balanced: 13,
|
|
769
|
-
full: 11
|
|
770
|
-
};
|
|
771
|
-
function poi(options = {}) {
|
|
772
|
-
return {
|
|
773
|
-
type: "poi",
|
|
774
|
-
...options.categories ? { categories: options.categories } : {},
|
|
775
|
-
...options.icons !== void 0 ? { icons: options.icons } : {},
|
|
776
|
-
...options.labels ? { labels: options.labels } : {},
|
|
777
|
-
...options.minZoom !== void 0 ? { minZoom: options.minZoom } : {},
|
|
778
|
-
...options.preset ? { preset: options.preset } : {}
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
function resolvePoi(legacyPoi, moduleConfig, fallback) {
|
|
782
|
-
const mode = moduleConfig?.preset ?? legacyPoi ?? fallback;
|
|
783
|
-
return {
|
|
784
|
-
categories: moduleConfig?.categories,
|
|
785
|
-
icons: moduleConfig?.icons ?? (mode === "none" ? false : "essential"),
|
|
786
|
-
labels: moduleConfig?.labels ?? poiLabelsFromMode(mode),
|
|
787
|
-
minZoom: moduleConfig?.minZoom ?? defaultPoiMinZoom[mode],
|
|
788
|
-
mode
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIconSpriteId2, customIconMapping) {
|
|
792
|
-
if (poiConfig.mode === "none") {
|
|
793
|
-
return [];
|
|
794
|
-
}
|
|
795
|
-
const layout = {
|
|
796
|
-
"text-field": labelTextField("auto"),
|
|
797
|
-
"text-font": textFont(typography),
|
|
798
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 10, 17, 12],
|
|
799
|
-
"text-letter-spacing": 0,
|
|
800
|
-
"text-padding": 2
|
|
801
|
-
};
|
|
802
|
-
if (poiConfig.labels === "none") {
|
|
803
|
-
delete layout["text-field"];
|
|
804
|
-
}
|
|
805
|
-
if (poiConfig.icons) {
|
|
806
|
-
layout["icon-image"] = poiIconImageExpression(customIconSpriteId2, customIconMapping);
|
|
807
|
-
layout["icon-optional"] = true;
|
|
808
|
-
layout["icon-padding"] = 2;
|
|
809
|
-
layout["icon-size"] = ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 0.85, 17, 1];
|
|
810
|
-
layout["text-anchor"] = "top";
|
|
811
|
-
layout["text-offset"] = [0, 0.75];
|
|
812
|
-
}
|
|
973
|
+
function roadOpacityAtZoom(majorOpacity, minorOpacity, streetOpacity, serviceOpacity) {
|
|
813
974
|
return [
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
paint: {
|
|
823
|
-
"text-color": colors.poi.label,
|
|
824
|
-
"text-halo-color": colors.poi.halo ?? alpha(colors.background, 0.78),
|
|
825
|
-
"text-halo-width": 1
|
|
826
|
-
}
|
|
827
|
-
}
|
|
975
|
+
"case",
|
|
976
|
+
classFilter(majorRoadClasses),
|
|
977
|
+
majorOpacity,
|
|
978
|
+
classFilter(streetRoadClasses),
|
|
979
|
+
streetOpacity,
|
|
980
|
+
classFilter(serviceRoadClasses),
|
|
981
|
+
serviceOpacity,
|
|
982
|
+
minorOpacity
|
|
828
983
|
];
|
|
829
984
|
}
|
|
830
|
-
function
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
985
|
+
function roadWidthExpression(stops, metrics, widthScale) {
|
|
986
|
+
return [
|
|
987
|
+
"interpolate",
|
|
988
|
+
["linear"],
|
|
989
|
+
["zoom"],
|
|
990
|
+
...stops.flatMap(([zoom, width]) => [
|
|
991
|
+
zoom,
|
|
992
|
+
widthScale ? [
|
|
993
|
+
"*",
|
|
994
|
+
[
|
|
995
|
+
"case",
|
|
996
|
+
classFilter(majorRoadClasses),
|
|
997
|
+
width * metrics.weightScale * metrics.majorWidthScale,
|
|
998
|
+
width * metrics.weightScale * metrics.minorWidthScale
|
|
999
|
+
],
|
|
1000
|
+
roadClassWidthScaleExpression(widthScale)
|
|
1001
|
+
] : [
|
|
1002
|
+
"case",
|
|
1003
|
+
classFilter(majorRoadClasses),
|
|
1004
|
+
width * metrics.weightScale * metrics.majorWidthScale,
|
|
1005
|
+
width * metrics.weightScale * metrics.minorWidthScale
|
|
1006
|
+
]
|
|
1007
|
+
])
|
|
1008
|
+
];
|
|
835
1009
|
}
|
|
836
|
-
function
|
|
837
|
-
return [
|
|
1010
|
+
function zoomWidth(stops, scale, widthScale) {
|
|
1011
|
+
return [
|
|
1012
|
+
"interpolate",
|
|
1013
|
+
["linear"],
|
|
1014
|
+
["zoom"],
|
|
1015
|
+
...stops.flatMap(([zoom, width]) => [
|
|
1016
|
+
zoom,
|
|
1017
|
+
widthScale ? ["*", width * scale, roadClassWidthScaleExpression(widthScale)] : width * scale
|
|
1018
|
+
])
|
|
1019
|
+
];
|
|
838
1020
|
}
|
|
839
|
-
function
|
|
840
|
-
const matchParts = [];
|
|
841
|
-
for (const [className, iconName] of Object.entries(poiClassIconNames)) {
|
|
842
|
-
matchParts.push(
|
|
843
|
-
className,
|
|
844
|
-
poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping)
|
|
845
|
-
);
|
|
846
|
-
}
|
|
1021
|
+
function roadClassWidthScaleExpression(widthScale) {
|
|
847
1022
|
return [
|
|
848
1023
|
"match",
|
|
849
1024
|
["get", "class"],
|
|
850
|
-
...
|
|
851
|
-
|
|
1025
|
+
...Object.entries(widthScale).flatMap(([roadClass, scale]) => [roadClass, scale]),
|
|
1026
|
+
1
|
|
852
1027
|
];
|
|
853
1028
|
}
|
|
854
|
-
function
|
|
855
|
-
return
|
|
1029
|
+
function oneWayMarkerLayer(source, sourceLayer, colors, typography) {
|
|
1030
|
+
return {
|
|
1031
|
+
id: "road-oneway-markers",
|
|
1032
|
+
type: "symbol",
|
|
1033
|
+
source,
|
|
1034
|
+
"source-layer": sourceLayer,
|
|
1035
|
+
minzoom: 15,
|
|
1036
|
+
filter: [
|
|
1037
|
+
"any",
|
|
1038
|
+
["==", ["get", "oneway"], 1],
|
|
1039
|
+
["==", ["get", "oneway"], "1"],
|
|
1040
|
+
["==", ["get", "oneway"], true]
|
|
1041
|
+
],
|
|
1042
|
+
layout: {
|
|
1043
|
+
"symbol-placement": "line",
|
|
1044
|
+
"symbol-spacing": 88,
|
|
1045
|
+
"text-field": "\u203A",
|
|
1046
|
+
...typography ? { "text-font": textFont(typography, "roads") } : {},
|
|
1047
|
+
"text-keep-upright": false,
|
|
1048
|
+
"text-rotation-alignment": "map",
|
|
1049
|
+
"text-size": 15
|
|
1050
|
+
},
|
|
1051
|
+
paint: {
|
|
1052
|
+
"text-color": colors.labels.road,
|
|
1053
|
+
"text-halo-color": colors.labels.halo,
|
|
1054
|
+
"text-halo-width": 0.5
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
856
1057
|
}
|
|
857
|
-
function
|
|
858
|
-
const osmBrightIcon = `${osmBrightIconNameForSemanticIcon(iconName)}_11`;
|
|
859
|
-
const customIconName = customIconMapping[iconName] ?? iconName;
|
|
860
|
-
if (!customIconSpriteId2) {
|
|
861
|
-
return ["coalesce", ["image", customIconName], ["image", osmBrightIcon]];
|
|
862
|
-
}
|
|
1058
|
+
function roadClassColorExpression(colors) {
|
|
863
1059
|
return [
|
|
864
|
-
"
|
|
865
|
-
["
|
|
866
|
-
|
|
1060
|
+
"match",
|
|
1061
|
+
["get", "class"],
|
|
1062
|
+
"motorway",
|
|
1063
|
+
colors.roads.motorway,
|
|
1064
|
+
"trunk",
|
|
1065
|
+
colors.roads.trunk,
|
|
1066
|
+
"primary",
|
|
1067
|
+
colors.roads.primary,
|
|
1068
|
+
"secondary",
|
|
1069
|
+
colors.roads.secondary,
|
|
1070
|
+
"rail",
|
|
1071
|
+
colors.roads.rail,
|
|
1072
|
+
"transit",
|
|
1073
|
+
colors.roads.rail,
|
|
1074
|
+
"ferry",
|
|
1075
|
+
colors.roads.ferry,
|
|
1076
|
+
"path",
|
|
1077
|
+
colors.roads.path,
|
|
1078
|
+
"track",
|
|
1079
|
+
colors.roads.path,
|
|
1080
|
+
colors.roads.minor
|
|
867
1081
|
];
|
|
868
1082
|
}
|
|
869
|
-
function osmBrightIconNameForSemanticIcon(iconName) {
|
|
870
|
-
return semanticToOsmBrightIconName[iconName] ?? "marker";
|
|
871
|
-
}
|
|
872
|
-
var poiCategoryClassNames = {
|
|
873
|
-
coffee: ["cafe"],
|
|
874
|
-
culture: ["museum", "gallery", "theatre", "cinema", "library", "attraction"],
|
|
875
|
-
education: ["school", "college", "university"],
|
|
876
|
-
food: ["restaurant", "fast_food", "bar", "pub", "biergarten"],
|
|
877
|
-
health: ["hospital", "pharmacy", "doctors", "dentist"],
|
|
878
|
-
lodging: ["hotel", "motel", "hostel", "guest_house"],
|
|
879
|
-
services: ["bank", "post", "toilet", "police", "fire_station", "town_hall"],
|
|
880
|
-
shopping: ["shop", "mall", "supermarket", "marketplace"],
|
|
881
|
-
transit: ["railway", "station", "bus", "bus_stop", "subway", "tram"]
|
|
882
|
-
};
|
|
883
|
-
var poiClassIconNames = {
|
|
884
|
-
attraction: "culture",
|
|
885
|
-
bank: "services",
|
|
886
|
-
bar: "food",
|
|
887
|
-
biergarten: "food",
|
|
888
|
-
bus: "transit",
|
|
889
|
-
bus_stop: "transit",
|
|
890
|
-
cafe: "coffee",
|
|
891
|
-
cinema: "culture",
|
|
892
|
-
college: "education",
|
|
893
|
-
dentist: "health",
|
|
894
|
-
doctors: "health",
|
|
895
|
-
fast_food: "food",
|
|
896
|
-
fire_station: "services",
|
|
897
|
-
gallery: "culture",
|
|
898
|
-
guest_house: "lodging",
|
|
899
|
-
hospital: "health",
|
|
900
|
-
hostel: "lodging",
|
|
901
|
-
hotel: "lodging",
|
|
902
|
-
library: "culture",
|
|
903
|
-
mall: "shopping",
|
|
904
|
-
marketplace: "shopping",
|
|
905
|
-
motel: "lodging",
|
|
906
|
-
museum: "culture",
|
|
907
|
-
pharmacy: "health",
|
|
908
|
-
police: "services",
|
|
909
|
-
post: "services",
|
|
910
|
-
pub: "food",
|
|
911
|
-
railway: "transit",
|
|
912
|
-
restaurant: "food",
|
|
913
|
-
school: "education",
|
|
914
|
-
shop: "shopping",
|
|
915
|
-
station: "transit",
|
|
916
|
-
subway: "transit",
|
|
917
|
-
supermarket: "shopping",
|
|
918
|
-
theatre: "culture",
|
|
919
|
-
toilet: "services",
|
|
920
|
-
town_hall: "services",
|
|
921
|
-
tram: "transit",
|
|
922
|
-
university: "education"
|
|
923
|
-
};
|
|
924
|
-
var semanticToOsmBrightIconName = {
|
|
925
|
-
coffee: "cafe",
|
|
926
|
-
culture: "museum",
|
|
927
|
-
default: "marker",
|
|
928
|
-
education: "school",
|
|
929
|
-
food: "restaurant",
|
|
930
|
-
health: "hospital",
|
|
931
|
-
lodging: "lodging",
|
|
932
|
-
services: "marker",
|
|
933
|
-
shopping: "shop",
|
|
934
|
-
transit: "railway"
|
|
935
|
-
};
|
|
936
1083
|
|
|
937
|
-
// src/modules/
|
|
938
|
-
var
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
weight: "regular"
|
|
1084
|
+
// src/modules/labels/index.ts
|
|
1085
|
+
var moduleLabelDefaults = {
|
|
1086
|
+
language: "auto",
|
|
1087
|
+
places: "major",
|
|
1088
|
+
roads: "major",
|
|
1089
|
+
water: "major"
|
|
944
1090
|
};
|
|
945
|
-
var
|
|
946
|
-
var
|
|
947
|
-
var
|
|
948
|
-
|
|
949
|
-
var serviceRoadClasses2 = ["service", "track"];
|
|
950
|
-
function roads(options = {}) {
|
|
1091
|
+
var majorPlaceClasses = ["continent", "country", "state", "city", "town"];
|
|
1092
|
+
var majorWaterClasses = ["ocean", "sea", "lake"];
|
|
1093
|
+
var majorWaterwayClasses = ["river"];
|
|
1094
|
+
function labels(options = {}) {
|
|
951
1095
|
return {
|
|
952
|
-
type: "
|
|
953
|
-
...options.
|
|
954
|
-
...options.
|
|
955
|
-
...options.
|
|
956
|
-
...options.
|
|
957
|
-
...options.
|
|
1096
|
+
type: "labels",
|
|
1097
|
+
...options.language ? { language: options.language } : {},
|
|
1098
|
+
...options.places ? { places: options.places } : {},
|
|
1099
|
+
...options.roadClasses ? { roadClasses: [...options.roadClasses] } : {},
|
|
1100
|
+
...options.roads ? { roads: options.roads } : {},
|
|
1101
|
+
...options.water ? { water: options.water } : {}
|
|
958
1102
|
};
|
|
959
1103
|
}
|
|
960
|
-
function
|
|
961
|
-
const fallbackMode =
|
|
962
|
-
const defaults = moduleConfig ?
|
|
1104
|
+
function resolveLabels(legacyLabels, moduleConfig, fallback) {
|
|
1105
|
+
const fallbackMode = legacyLabels ?? fallback;
|
|
1106
|
+
const defaults = moduleConfig ? moduleLabelDefaults : labelDefaultsFromLegacyMode(fallbackMode);
|
|
963
1107
|
const resolved = {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
},
|
|
970
|
-
hierarchy: moduleConfig?.hierarchy ?? defaults.hierarchy,
|
|
971
|
-
outline: moduleConfig?.outline ?? defaults.outline,
|
|
972
|
-
weight: moduleConfig?.weight ?? defaults.weight
|
|
1108
|
+
language: moduleConfig?.language ?? defaults.language,
|
|
1109
|
+
places: moduleConfig?.places ?? defaults.places,
|
|
1110
|
+
...moduleConfig?.roadClasses ? { roadClasses: moduleConfig.roadClasses } : {},
|
|
1111
|
+
roads: moduleConfig?.roads ?? defaults.roads,
|
|
1112
|
+
water: moduleConfig?.water ?? defaults.water
|
|
973
1113
|
};
|
|
974
1114
|
return {
|
|
975
1115
|
...resolved,
|
|
976
|
-
mode:
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
function roadStyleMetrics(options) {
|
|
980
|
-
const hierarchy = {
|
|
981
|
-
subtle: {
|
|
982
|
-
majorOpacity: 0.92,
|
|
983
|
-
majorWidthScale: 1,
|
|
984
|
-
minorOpacity: 0.82,
|
|
985
|
-
minorWidthScale: 0.94
|
|
986
|
-
},
|
|
987
|
-
clear: {
|
|
988
|
-
majorOpacity: 0.92,
|
|
989
|
-
majorWidthScale: 1,
|
|
990
|
-
minorOpacity: 0.68,
|
|
991
|
-
minorWidthScale: 0.84
|
|
992
|
-
},
|
|
993
|
-
strong: {
|
|
994
|
-
majorOpacity: 0.92,
|
|
995
|
-
majorWidthScale: 1,
|
|
996
|
-
minorOpacity: 0.52,
|
|
997
|
-
minorWidthScale: 0.7
|
|
998
|
-
}
|
|
999
|
-
}[options.hierarchy];
|
|
1000
|
-
const weightScale = {
|
|
1001
|
-
thin: 0.78,
|
|
1002
|
-
regular: 1,
|
|
1003
|
-
bold: 1.24
|
|
1004
|
-
}[options.weight];
|
|
1005
|
-
const outlineOpacity = {
|
|
1006
|
-
none: 0,
|
|
1007
|
-
subtle: 0.18,
|
|
1008
|
-
strong: 0.34
|
|
1009
|
-
}[options.outline];
|
|
1010
|
-
return {
|
|
1011
|
-
...hierarchy,
|
|
1012
|
-
outlineOpacity,
|
|
1013
|
-
weightScale
|
|
1116
|
+
mode: moduleConfig ? legacyModeFromResolvedLabels(resolved) : fallbackMode
|
|
1014
1117
|
};
|
|
1015
1118
|
}
|
|
1016
|
-
function
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
const metrics = roadStyleMetrics(options);
|
|
1021
|
-
const selectedMajorRoadClasses = options.detail === "highways" ? [...highwayRoadClasses2] : [...majorRoadClasses2];
|
|
1022
|
-
const showsStreetRoads = options.detail === "streets" || options.detail === "all";
|
|
1023
|
-
const showsServiceRoads = options.detail === "all";
|
|
1024
|
-
const roadClasses = [
|
|
1025
|
-
...selectedMajorRoadClasses,
|
|
1026
|
-
...showsStreetRoads ? streetRoadClasses2 : [],
|
|
1027
|
-
...showsServiceRoads ? serviceRoadClasses2 : []
|
|
1028
|
-
];
|
|
1029
|
-
const transportClasses = [
|
|
1030
|
-
...roadClasses,
|
|
1031
|
-
...options.extras.paths ? ["path"] : [],
|
|
1032
|
-
...options.extras.rail ? ["rail", "transit"] : [],
|
|
1033
|
-
...options.extras.ferry ? ["ferry"] : []
|
|
1034
|
-
];
|
|
1035
|
-
const roadClassColor = roadClassColorExpression(colors);
|
|
1036
|
-
const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
|
|
1037
|
-
const majorFilter = classFilter2(selectedMajorRoadClasses);
|
|
1038
|
-
const streetFilter = classFilter2(streetRoadClasses2);
|
|
1039
|
-
const serviceFilter = classFilter2(serviceRoadClasses2);
|
|
1040
|
-
const roadFilter = classFilter2(roadClasses);
|
|
1041
|
-
const transportFilter = classFilter2(transportClasses);
|
|
1042
|
-
const layers = [];
|
|
1043
|
-
if (options.outline !== "none") {
|
|
1044
|
-
layers.push({
|
|
1045
|
-
id: "roads-tunnels-casing",
|
|
1046
|
-
type: "line",
|
|
1047
|
-
source,
|
|
1048
|
-
"source-layer": sourceLayer,
|
|
1049
|
-
minzoom: 10,
|
|
1050
|
-
filter: ["all", ["==", ["get", "brunnel"], "tunnel"], transportFilter],
|
|
1051
|
-
paint: {
|
|
1052
|
-
"line-color": colors.roads.tunnel,
|
|
1053
|
-
"line-dasharray": [0.6, 0.8],
|
|
1054
|
-
"line-opacity": roadOpacityExpression(
|
|
1055
|
-
metrics.outlineOpacity * 0.85,
|
|
1056
|
-
metrics.outlineOpacity * 0.85
|
|
1057
|
-
),
|
|
1058
|
-
"line-width": roadWidthExpression(
|
|
1059
|
-
[
|
|
1060
|
-
[10, 0.8],
|
|
1061
|
-
[14, 2.2],
|
|
1062
|
-
[16, 4.8]
|
|
1063
|
-
],
|
|
1064
|
-
metrics
|
|
1065
|
-
)
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
layers.push({
|
|
1070
|
-
id: "roads-tunnels",
|
|
1071
|
-
type: "line",
|
|
1072
|
-
source,
|
|
1073
|
-
"source-layer": sourceLayer,
|
|
1074
|
-
minzoom: 10,
|
|
1075
|
-
filter: ["all", ["==", ["get", "brunnel"], "tunnel"], transportFilter],
|
|
1076
|
-
paint: {
|
|
1077
|
-
"line-color": roadClassColor,
|
|
1078
|
-
"line-dasharray": [0.6, 0.8],
|
|
1079
|
-
"line-opacity": roadOpacityExpression(
|
|
1080
|
-
metrics.majorOpacity * 0.28,
|
|
1081
|
-
metrics.minorOpacity * 0.28
|
|
1082
|
-
),
|
|
1083
|
-
"line-width": roadWidthExpression(
|
|
1084
|
-
[
|
|
1085
|
-
[10, 0.25],
|
|
1086
|
-
[14, 1],
|
|
1087
|
-
[16, 2.4]
|
|
1088
|
-
],
|
|
1089
|
-
metrics
|
|
1090
|
-
)
|
|
1091
|
-
}
|
|
1092
|
-
});
|
|
1093
|
-
if (options.outline !== "none") {
|
|
1094
|
-
layers.push({
|
|
1095
|
-
id: "roads-casing",
|
|
1096
|
-
type: "line",
|
|
1119
|
+
function labelLayers(source, sourceLayers, colors, labelsConfig, roadsConfig, typography) {
|
|
1120
|
+
return [
|
|
1121
|
+
...placeLabelLayers(source, sourceLayers.place, colors, labelsConfig, typography),
|
|
1122
|
+
...roadLabelLayers(
|
|
1097
1123
|
source,
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
[
|
|
1106
|
-
[6, 0.7],
|
|
1107
|
-
[12, 1.8],
|
|
1108
|
-
[16, 5.4]
|
|
1109
|
-
],
|
|
1110
|
-
metrics
|
|
1111
|
-
)
|
|
1112
|
-
}
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
if (options.extras.ferry) {
|
|
1116
|
-
layers.push({
|
|
1117
|
-
id: "roads-ferry",
|
|
1118
|
-
type: "line",
|
|
1124
|
+
sourceLayers.roadName,
|
|
1125
|
+
colors,
|
|
1126
|
+
labelsConfig,
|
|
1127
|
+
roadsConfig,
|
|
1128
|
+
typography
|
|
1129
|
+
),
|
|
1130
|
+
...waterLabelLayers(
|
|
1119
1131
|
source,
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
)
|
|
1135
|
-
}
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
if (options.extras.paths) {
|
|
1139
|
-
layers.push({
|
|
1140
|
-
id: "roads-paths",
|
|
1141
|
-
type: "line",
|
|
1132
|
+
sourceLayers.waterName,
|
|
1133
|
+
sourceLayers.waterway,
|
|
1134
|
+
colors,
|
|
1135
|
+
labelsConfig,
|
|
1136
|
+
typography
|
|
1137
|
+
)
|
|
1138
|
+
];
|
|
1139
|
+
}
|
|
1140
|
+
function placeLabelLayers(source, sourceLayer, colors, labelsConfig, typography) {
|
|
1141
|
+
if (labelsConfig.places === "none") return [];
|
|
1142
|
+
return [
|
|
1143
|
+
{
|
|
1144
|
+
id: "place-labels",
|
|
1145
|
+
type: "symbol",
|
|
1142
1146
|
source,
|
|
1143
1147
|
"source-layer": sourceLayer,
|
|
1144
|
-
minzoom:
|
|
1145
|
-
filter: ["all",
|
|
1148
|
+
minzoom: 0,
|
|
1149
|
+
filter: labelsConfig.places === "major" ? ["all", ["has", "name"], classFilter2(majorPlaceClasses)] : ["has", "name"],
|
|
1150
|
+
layout: {
|
|
1151
|
+
"text-field": labelTextField(labelsConfig.language),
|
|
1152
|
+
"text-font": textFont(typography, "places"),
|
|
1153
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
|
|
1154
|
+
"text-letter-spacing": 0,
|
|
1155
|
+
"text-padding": 4
|
|
1156
|
+
},
|
|
1146
1157
|
paint: {
|
|
1147
|
-
"
|
|
1148
|
-
"
|
|
1149
|
-
"
|
|
1150
|
-
"line-width": zoomWidth(
|
|
1151
|
-
[
|
|
1152
|
-
[10, 0.25],
|
|
1153
|
-
[14, 0.8],
|
|
1154
|
-
[16, 2.2]
|
|
1155
|
-
],
|
|
1156
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
1157
|
-
)
|
|
1158
|
+
"text-color": colors.labels.primary,
|
|
1159
|
+
"text-halo-color": colors.labels.halo,
|
|
1160
|
+
"text-halo-width": 1.2
|
|
1158
1161
|
}
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
+
}
|
|
1163
|
+
];
|
|
1164
|
+
}
|
|
1165
|
+
function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig, typography) {
|
|
1166
|
+
const classes = visibleRoadLabelClasses(
|
|
1167
|
+
labelsConfig.roads,
|
|
1168
|
+
roadsConfig,
|
|
1169
|
+
labelsConfig.roadClasses
|
|
1170
|
+
);
|
|
1171
|
+
const majorClasses = classes.filter(isMajorRoadClass);
|
|
1172
|
+
const minorClasses = classes.filter((roadClass) => !isMajorRoadClass(roadClass));
|
|
1173
|
+
if (classes.length === 0) return [];
|
|
1174
|
+
const layers = [];
|
|
1175
|
+
const sharedLayout = {
|
|
1176
|
+
"symbol-placement": "line",
|
|
1177
|
+
"text-field": labelTextField(labelsConfig.language),
|
|
1178
|
+
"text-font": textFont(typography, "roads"),
|
|
1179
|
+
"text-rotation-alignment": "map",
|
|
1180
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 11, 11, 14, 13, 17, 15]
|
|
1181
|
+
};
|
|
1182
|
+
const sharedPaint = {
|
|
1183
|
+
"text-color": colors.labels.road,
|
|
1184
|
+
"text-halo-color": colors.labels.halo,
|
|
1185
|
+
"text-halo-width": 1
|
|
1186
|
+
};
|
|
1187
|
+
if (majorClasses.length > 0) {
|
|
1188
|
+
const namedMajorClasses = majorClasses.filter((roadClass) => roadClass !== "motorway");
|
|
1162
1189
|
layers.push({
|
|
1163
|
-
id: "
|
|
1164
|
-
type: "
|
|
1190
|
+
id: "road-shields",
|
|
1191
|
+
type: "symbol",
|
|
1165
1192
|
source,
|
|
1166
1193
|
"source-layer": sourceLayer,
|
|
1167
|
-
minzoom:
|
|
1168
|
-
filter: ["all",
|
|
1169
|
-
|
|
1170
|
-
"
|
|
1171
|
-
"
|
|
1172
|
-
"
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
1179
|
-
)
|
|
1180
|
-
}
|
|
1194
|
+
minzoom: 8,
|
|
1195
|
+
filter: ["all", ["has", "ref"], classFilter2(majorClasses)],
|
|
1196
|
+
layout: {
|
|
1197
|
+
"symbol-placement": "line",
|
|
1198
|
+
"symbol-spacing": 240,
|
|
1199
|
+
"text-field": ["get", "ref"],
|
|
1200
|
+
"text-font": textFont(typography, "roads"),
|
|
1201
|
+
"text-rotation-alignment": "viewport",
|
|
1202
|
+
"text-size": 10
|
|
1203
|
+
},
|
|
1204
|
+
paint: sharedPaint
|
|
1181
1205
|
});
|
|
1206
|
+
if (namedMajorClasses.length > 0) {
|
|
1207
|
+
layers.unshift({
|
|
1208
|
+
id: "road-labels-major",
|
|
1209
|
+
type: "symbol",
|
|
1210
|
+
source,
|
|
1211
|
+
"source-layer": sourceLayer,
|
|
1212
|
+
minzoom: 10,
|
|
1213
|
+
filter: ["all", ["has", "name"], classFilter2(namedMajorClasses)],
|
|
1214
|
+
layout: sharedLayout,
|
|
1215
|
+
paint: sharedPaint
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1182
1218
|
}
|
|
1183
|
-
if (
|
|
1219
|
+
if (minorClasses.length > 0) {
|
|
1184
1220
|
layers.push({
|
|
1185
|
-
id: "
|
|
1186
|
-
type: "
|
|
1221
|
+
id: "road-labels-minor",
|
|
1222
|
+
type: "symbol",
|
|
1187
1223
|
source,
|
|
1188
1224
|
"source-layer": sourceLayer,
|
|
1189
1225
|
minzoom: 14,
|
|
1190
|
-
filter: ["all",
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
"line-opacity": metrics.minorOpacity,
|
|
1194
|
-
"line-width": zoomWidth(
|
|
1195
|
-
[
|
|
1196
|
-
[14, 0.6],
|
|
1197
|
-
[16, 2.2]
|
|
1198
|
-
],
|
|
1199
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
1200
|
-
)
|
|
1201
|
-
}
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
if (showsStreetRoads) {
|
|
1205
|
-
layers.push({
|
|
1206
|
-
id: "roads-minor",
|
|
1207
|
-
type: "line",
|
|
1208
|
-
source,
|
|
1209
|
-
"source-layer": sourceLayer,
|
|
1210
|
-
minzoom: 12,
|
|
1211
|
-
filter: ["all", nonTunnelRoadFilter, streetFilter],
|
|
1212
|
-
paint: {
|
|
1213
|
-
"line-color": roadClassColor,
|
|
1214
|
-
"line-opacity": metrics.minorOpacity,
|
|
1215
|
-
"line-width": zoomWidth(
|
|
1216
|
-
[
|
|
1217
|
-
[12, 0.8],
|
|
1218
|
-
[16, 3]
|
|
1219
|
-
],
|
|
1220
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
1221
|
-
)
|
|
1222
|
-
}
|
|
1226
|
+
filter: ["all", ["has", "name"], classFilter2(minorClasses)],
|
|
1227
|
+
layout: sharedLayout,
|
|
1228
|
+
paint: sharedPaint
|
|
1223
1229
|
});
|
|
1224
1230
|
}
|
|
1225
|
-
layers
|
|
1231
|
+
return layers;
|
|
1232
|
+
}
|
|
1233
|
+
function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, colors, labelsConfig, typography) {
|
|
1234
|
+
if (labelsConfig.water === "none") return [];
|
|
1235
|
+
const waterFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter2(majorWaterClasses)] : ["has", "name"];
|
|
1236
|
+
const waterwayFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter2(majorWaterwayClasses)] : ["has", "name"];
|
|
1237
|
+
const paint = {
|
|
1238
|
+
"text-color": colors.labels.water,
|
|
1239
|
+
"text-halo-color": colors.labels.halo,
|
|
1240
|
+
"text-halo-width": 1
|
|
1241
|
+
};
|
|
1242
|
+
return [
|
|
1226
1243
|
{
|
|
1227
|
-
id: "
|
|
1228
|
-
type: "
|
|
1244
|
+
id: "water-labels",
|
|
1245
|
+
type: "symbol",
|
|
1229
1246
|
source,
|
|
1230
|
-
"source-layer":
|
|
1231
|
-
minzoom:
|
|
1232
|
-
filter: ["all",
|
|
1233
|
-
|
|
1234
|
-
"
|
|
1235
|
-
"
|
|
1236
|
-
"
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
[12, 1.2],
|
|
1240
|
-
[16, 4]
|
|
1241
|
-
],
|
|
1242
|
-
metrics.weightScale * metrics.majorWidthScale
|
|
1243
|
-
)
|
|
1244
|
-
}
|
|
1247
|
+
"source-layer": waterNameSourceLayer,
|
|
1248
|
+
minzoom: 3,
|
|
1249
|
+
filter: ["all", ["==", ["geometry-type"], "Point"], waterFilter],
|
|
1250
|
+
layout: {
|
|
1251
|
+
"text-field": labelTextField(labelsConfig.language),
|
|
1252
|
+
"text-font": textFont(typography, "water"),
|
|
1253
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 11, 8, 14]
|
|
1254
|
+
},
|
|
1255
|
+
paint
|
|
1245
1256
|
},
|
|
1246
1257
|
{
|
|
1247
|
-
id: "
|
|
1248
|
-
type: "
|
|
1258
|
+
id: "water-line-labels",
|
|
1259
|
+
type: "symbol",
|
|
1249
1260
|
source,
|
|
1250
|
-
"source-layer":
|
|
1261
|
+
"source-layer": waterNameSourceLayer,
|
|
1262
|
+
minzoom: 5,
|
|
1263
|
+
filter: ["all", ["==", ["geometry-type"], "LineString"], waterFilter],
|
|
1264
|
+
layout: {
|
|
1265
|
+
"symbol-placement": "line",
|
|
1266
|
+
"text-field": labelTextField(labelsConfig.language),
|
|
1267
|
+
"text-font": textFont(typography, "water"),
|
|
1268
|
+
"text-size": 13
|
|
1269
|
+
},
|
|
1270
|
+
paint
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
id: "waterway-labels",
|
|
1274
|
+
type: "symbol",
|
|
1275
|
+
source,
|
|
1276
|
+
"source-layer": waterwaySourceLayer,
|
|
1251
1277
|
minzoom: 10,
|
|
1252
|
-
filter: ["all", ["==", ["
|
|
1253
|
-
|
|
1254
|
-
"
|
|
1255
|
-
"
|
|
1256
|
-
"
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
],
|
|
1262
|
-
metrics
|
|
1263
|
-
)
|
|
1264
|
-
}
|
|
1278
|
+
filter: ["all", ["==", ["geometry-type"], "LineString"], waterwayFilter],
|
|
1279
|
+
layout: {
|
|
1280
|
+
"symbol-placement": "line",
|
|
1281
|
+
"symbol-spacing": 320,
|
|
1282
|
+
"text-field": labelTextField(labelsConfig.language),
|
|
1283
|
+
"text-font": textFont(typography, "water"),
|
|
1284
|
+
"text-size": 12
|
|
1285
|
+
},
|
|
1286
|
+
paint
|
|
1265
1287
|
}
|
|
1288
|
+
];
|
|
1289
|
+
}
|
|
1290
|
+
function visibleRoadLabelClasses(detail, roadsConfig, eligibleClasses) {
|
|
1291
|
+
if (detail === "none" || roadsConfig.detail === "none") return [];
|
|
1292
|
+
const visibleRoadClasses = [
|
|
1293
|
+
...roadClassesForDetail(roadsConfig.detail),
|
|
1294
|
+
...roadsConfig.extras.paths ? ["path"] : []
|
|
1295
|
+
];
|
|
1296
|
+
const requestedClasses = roadLabelClasses(detail);
|
|
1297
|
+
const visible = new Set(visibleRoadClasses);
|
|
1298
|
+
const eligible = eligibleClasses ? new Set(eligibleClasses) : void 0;
|
|
1299
|
+
return requestedClasses.filter(
|
|
1300
|
+
(roadClass) => visible.has(roadClass) && (!eligible || eligible.has(roadClass))
|
|
1266
1301
|
);
|
|
1267
|
-
return layers;
|
|
1268
1302
|
}
|
|
1269
|
-
function
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
outline: "subtle",
|
|
1276
|
-
weight: "regular"
|
|
1277
|
-
};
|
|
1303
|
+
function roadLabelClasses(detail) {
|
|
1304
|
+
return [...roadClassesForDetail(detail), ...detail === "all" ? ["path"] : []];
|
|
1305
|
+
}
|
|
1306
|
+
function labelDefaultsFromLegacyMode(mode) {
|
|
1307
|
+
if (mode === "none") {
|
|
1308
|
+
return { language: "auto", places: "none", roads: "none", water: "none" };
|
|
1278
1309
|
}
|
|
1279
|
-
if (mode === "
|
|
1280
|
-
return {
|
|
1281
|
-
detail: "streets",
|
|
1282
|
-
extras: { ferry: false, paths: false, rail: false },
|
|
1283
|
-
hierarchy: "subtle",
|
|
1284
|
-
outline: "subtle",
|
|
1285
|
-
weight: "thin"
|
|
1286
|
-
};
|
|
1310
|
+
if (mode === "essential") {
|
|
1311
|
+
return { language: "auto", places: "major", roads: "major", water: "major" };
|
|
1287
1312
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1313
|
+
return { language: "auto", places: "all", roads: "all", water: "all" };
|
|
1314
|
+
}
|
|
1315
|
+
function legacyModeFromResolvedLabels(options) {
|
|
1316
|
+
if (options.places === "none" && options.roads === "none" && options.water === "none") {
|
|
1317
|
+
return "none";
|
|
1318
|
+
}
|
|
1319
|
+
if (options.places === "major" && options.roads === "major" && options.water === "major") {
|
|
1320
|
+
return "essential";
|
|
1321
|
+
}
|
|
1322
|
+
return "balanced";
|
|
1323
|
+
}
|
|
1324
|
+
function classFilter2(classes) {
|
|
1325
|
+
return ["match", ["get", "class"], classes, true, false];
|
|
1326
|
+
}
|
|
1327
|
+
function labelTextField(language) {
|
|
1328
|
+
if (language === "local") {
|
|
1329
|
+
return ["coalesce", ["get", "name"], ["get", "name:latin"], ["get", "name:en"]];
|
|
1330
|
+
}
|
|
1331
|
+
if (language === "auto") {
|
|
1332
|
+
return ["coalesce", ["get", "name:latin"], ["get", "name"], ["get", "name:en"]];
|
|
1296
1333
|
}
|
|
1334
|
+
return ["coalesce", ["get", `name:${language}`], ["get", "name:latin"], ["get", "name"]];
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// src/modules/poi/index.ts
|
|
1338
|
+
var defaultPoiMinZoom = {
|
|
1339
|
+
none: 24,
|
|
1340
|
+
minimal: 15,
|
|
1341
|
+
balanced: 13,
|
|
1342
|
+
full: 11
|
|
1343
|
+
};
|
|
1344
|
+
function poi(options = {}) {
|
|
1297
1345
|
return {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1346
|
+
type: "poi",
|
|
1347
|
+
...options.categories ? { categories: options.categories } : {},
|
|
1348
|
+
...options.classMapping ? { classMapping: { ...options.classMapping } } : {},
|
|
1349
|
+
...options.color ? { color: options.color } : {},
|
|
1350
|
+
...options.density ? { density: options.density } : {},
|
|
1351
|
+
...options.icons !== void 0 ? { icons: options.icons } : {},
|
|
1352
|
+
...options.labels ? { labels: options.labels } : {},
|
|
1353
|
+
...options.minZoom !== void 0 ? { minZoom: options.minZoom } : {},
|
|
1354
|
+
...options.placement ? { placement: { ...options.placement } } : {},
|
|
1355
|
+
...options.preset ? { preset: options.preset } : {}
|
|
1303
1356
|
};
|
|
1304
1357
|
}
|
|
1305
|
-
function
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1358
|
+
function resolvePoi(legacyPoi, moduleConfig, fallback) {
|
|
1359
|
+
const mode = moduleConfig?.preset ?? legacyPoi ?? fallback;
|
|
1360
|
+
const density = moduleConfig?.density;
|
|
1361
|
+
return {
|
|
1362
|
+
...moduleConfig?.categories ? { categories: moduleConfig.categories } : {},
|
|
1363
|
+
classMapping: resolvePoiClassMapping(moduleConfig?.classMapping),
|
|
1364
|
+
color: moduleConfig?.color ?? "uniform",
|
|
1365
|
+
...density ? { density } : {},
|
|
1366
|
+
icons: moduleConfig?.icons ?? (mode === "none" ? false : "essential"),
|
|
1367
|
+
labels: moduleConfig?.labels ?? poiLabelsFromMode(mode),
|
|
1368
|
+
minZoom: moduleConfig?.minZoom ?? defaultPoiMinZoom[mode] + (density === void 0 ? 0 : poiDensityZoomOffset(density)),
|
|
1369
|
+
mode,
|
|
1370
|
+
placement: {
|
|
1371
|
+
coupleIconAndLabel: moduleConfig?.placement?.coupleIconAndLabel ?? false,
|
|
1372
|
+
iconPadding: moduleConfig?.placement?.iconPadding ?? 2,
|
|
1373
|
+
textPadding: moduleConfig?.placement?.textPadding ?? 2
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1310
1376
|
}
|
|
1311
|
-
function
|
|
1312
|
-
|
|
1377
|
+
function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIconSpriteId2, customIconMapping) {
|
|
1378
|
+
if (poiConfig.mode === "none") {
|
|
1379
|
+
return [];
|
|
1380
|
+
}
|
|
1381
|
+
const layout = {
|
|
1382
|
+
"text-field": labelTextField("auto"),
|
|
1383
|
+
"text-font": textFont(typography, "poi"),
|
|
1384
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 10, 17, 12],
|
|
1385
|
+
"text-letter-spacing": 0,
|
|
1386
|
+
"text-padding": poiConfig.placement.textPadding
|
|
1387
|
+
};
|
|
1388
|
+
if (poiConfig.density) {
|
|
1389
|
+
layout["symbol-sort-key"] = poiPriorityExpression(poiConfig.classMapping);
|
|
1390
|
+
}
|
|
1391
|
+
if (poiConfig.labels === "none") {
|
|
1392
|
+
delete layout["text-field"];
|
|
1393
|
+
}
|
|
1394
|
+
if (poiConfig.icons) {
|
|
1395
|
+
layout["icon-image"] = poiIconImageExpression(
|
|
1396
|
+
customIconSpriteId2,
|
|
1397
|
+
customIconMapping,
|
|
1398
|
+
poiConfig.classMapping
|
|
1399
|
+
);
|
|
1400
|
+
layout["icon-optional"] = !poiConfig.placement.coupleIconAndLabel;
|
|
1401
|
+
layout["icon-padding"] = poiConfig.placement.iconPadding;
|
|
1402
|
+
layout["icon-size"] = ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 0.85, 17, 1];
|
|
1403
|
+
layout["text-anchor"] = "top";
|
|
1404
|
+
layout["text-offset"] = [0, 0.75];
|
|
1405
|
+
if (poiConfig.placement.coupleIconAndLabel) layout["text-optional"] = false;
|
|
1406
|
+
}
|
|
1407
|
+
return [
|
|
1408
|
+
{
|
|
1409
|
+
id: "poi-labels",
|
|
1410
|
+
type: "symbol",
|
|
1411
|
+
source,
|
|
1412
|
+
"source-layer": sourceLayer,
|
|
1413
|
+
minzoom: poiConfig.minZoom,
|
|
1414
|
+
...poiFilter(poiConfig) ? { filter: poiFilter(poiConfig) } : {},
|
|
1415
|
+
layout,
|
|
1416
|
+
paint: {
|
|
1417
|
+
"text-color": poiConfig.color === "category" ? poiCategoryColorExpression(colors, poiConfig.classMapping) : colors.poi.label,
|
|
1418
|
+
"text-halo-color": colors.poi.halo ?? alpha(colors.background, 0.78),
|
|
1419
|
+
"text-halo-width": 1
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
];
|
|
1313
1423
|
}
|
|
1314
|
-
function
|
|
1424
|
+
function poiLabelsFromMode(mode) {
|
|
1425
|
+
if (mode === "none") return "none";
|
|
1426
|
+
if (mode === "minimal") return "minimal";
|
|
1427
|
+
if (mode === "full") return "full";
|
|
1428
|
+
return "balanced";
|
|
1429
|
+
}
|
|
1430
|
+
function poiCategoryFilter(categories, classMapping = poiCategoryClassNames) {
|
|
1431
|
+
const classes = categories.flatMap((category) => poiCategoryClasses(category, classMapping));
|
|
1432
|
+
return ["any", ["in", "class", ...classes], ["in", "subclass", ...classes]];
|
|
1433
|
+
}
|
|
1434
|
+
function poiIconImageExpression(customIconSpriteId2, customIconMapping = {}, classMapping = poiCategoryClassNames) {
|
|
1435
|
+
const matchParts = Object.keys(classMapping).flatMap((category) => [
|
|
1436
|
+
category,
|
|
1437
|
+
poiIconImageFallback(category, customIconSpriteId2, customIconMapping)
|
|
1438
|
+
]);
|
|
1315
1439
|
return [
|
|
1316
|
-
"
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
["step", ["zoom"], 0, 12, minorOpacity],
|
|
1321
|
-
classFilter2(serviceRoadClasses2),
|
|
1322
|
-
["step", ["zoom"], 0, 14, minorOpacity],
|
|
1323
|
-
minorOpacity
|
|
1440
|
+
"match",
|
|
1441
|
+
poiCategoryExpression("default", classMapping),
|
|
1442
|
+
...matchParts,
|
|
1443
|
+
poiIconImageFallback("default", customIconSpriteId2, customIconMapping)
|
|
1324
1444
|
];
|
|
1325
1445
|
}
|
|
1326
|
-
function
|
|
1446
|
+
function poiCategoryExpression(fallback = "", classMapping = poiCategoryClassNames) {
|
|
1447
|
+
const categoryMatches = poiCategoryMatchParts(classMapping);
|
|
1327
1448
|
return [
|
|
1328
|
-
"
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1449
|
+
"match",
|
|
1450
|
+
["get", "subclass"],
|
|
1451
|
+
...categoryMatches,
|
|
1452
|
+
fallbackCategoryExpression(fallback, classMapping)
|
|
1453
|
+
];
|
|
1454
|
+
}
|
|
1455
|
+
function fallbackCategoryExpression(fallback, classMapping) {
|
|
1456
|
+
const categoryMatches = poiCategoryMatchParts(classMapping);
|
|
1457
|
+
return ["match", ["get", "class"], ...categoryMatches, fallback];
|
|
1458
|
+
}
|
|
1459
|
+
function poiCategoryMatchParts(classMapping) {
|
|
1460
|
+
return Object.entries(classMapping).flatMap(([category, classes]) => [classes, category]);
|
|
1461
|
+
}
|
|
1462
|
+
function poiCategoryClasses(category, classMapping = poiCategoryClassNames) {
|
|
1463
|
+
return [...classMapping[category] ?? [category]];
|
|
1464
|
+
}
|
|
1465
|
+
function poiPriorityExpression(classMapping = poiCategoryClassNames) {
|
|
1466
|
+
const categoryPenalty = [
|
|
1467
|
+
"match",
|
|
1468
|
+
poiCategoryExpression("default", classMapping),
|
|
1469
|
+
"transit",
|
|
1470
|
+
0,
|
|
1471
|
+
"culture",
|
|
1472
|
+
1,
|
|
1473
|
+
"health",
|
|
1474
|
+
2,
|
|
1475
|
+
"education",
|
|
1476
|
+
3,
|
|
1477
|
+
"lodging",
|
|
1478
|
+
4,
|
|
1479
|
+
"food",
|
|
1480
|
+
5,
|
|
1481
|
+
"coffee",
|
|
1482
|
+
6,
|
|
1483
|
+
"services",
|
|
1484
|
+
7,
|
|
1485
|
+
"shopping",
|
|
1486
|
+
8,
|
|
1487
|
+
9
|
|
1332
1488
|
];
|
|
1489
|
+
return ["+", ["*", categoryPenalty, 1e3], ["to-number", ["get", "rank"], 999]];
|
|
1333
1490
|
}
|
|
1334
|
-
function
|
|
1491
|
+
function poiDensityFilter(density, classMapping = poiCategoryClassNames) {
|
|
1492
|
+
if (density === "dense") return void 0;
|
|
1493
|
+
const cutoffs = poiDensityRankCutoffs[density];
|
|
1335
1494
|
return [
|
|
1336
|
-
"
|
|
1337
|
-
[
|
|
1338
|
-
|
|
1339
|
-
|
|
1495
|
+
"any",
|
|
1496
|
+
...Object.keys(classMapping).map((category) => [
|
|
1497
|
+
"all",
|
|
1498
|
+
poiCategoryFilter([category], classMapping),
|
|
1499
|
+
["any", ["!has", "rank"], ["<=", "rank", cutoffs[category] ?? cutoffs.default]]
|
|
1500
|
+
])
|
|
1340
1501
|
];
|
|
1341
1502
|
}
|
|
1342
|
-
function
|
|
1503
|
+
function poiCategoryColorExpression(colors, classMapping = poiCategoryClassNames) {
|
|
1343
1504
|
return [
|
|
1344
1505
|
"match",
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1506
|
+
poiCategoryExpression("default", classMapping),
|
|
1507
|
+
...Object.keys(classMapping).flatMap((category) => [
|
|
1508
|
+
category,
|
|
1509
|
+
colors.poi[category] ?? colors.poi.label
|
|
1510
|
+
]),
|
|
1511
|
+
colors.poi.label
|
|
1512
|
+
];
|
|
1513
|
+
}
|
|
1514
|
+
function resolvePoiClassMapping(overrides) {
|
|
1515
|
+
const categories = /* @__PURE__ */ new Set([
|
|
1516
|
+
...Object.keys(poiCategoryClassNames),
|
|
1517
|
+
...Object.keys(overrides ?? {})
|
|
1518
|
+
]);
|
|
1519
|
+
return Object.fromEntries(
|
|
1520
|
+
[...categories].map((category) => [
|
|
1521
|
+
category,
|
|
1522
|
+
[.../* @__PURE__ */ new Set([...poiCategoryClassNames[category] ?? [], ...overrides?.[category] ?? []])]
|
|
1523
|
+
])
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
function poiDensityZoomOffset(density) {
|
|
1527
|
+
if (density === "sparse") return 1;
|
|
1528
|
+
if (density === "dense") return -1;
|
|
1529
|
+
return 0;
|
|
1530
|
+
}
|
|
1531
|
+
function poiFilter(poiConfig) {
|
|
1532
|
+
const categoryFilter = poiConfig.categories ? poiCategoryFilter(poiConfig.categories, poiConfig.classMapping) : void 0;
|
|
1533
|
+
const densityFilter = poiConfig.density ? poiDensityFilter(poiConfig.density, poiConfig.classMapping) : void 0;
|
|
1534
|
+
if (categoryFilter && densityFilter) return ["all", categoryFilter, densityFilter];
|
|
1535
|
+
return categoryFilter ?? densityFilter;
|
|
1536
|
+
}
|
|
1537
|
+
function poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping) {
|
|
1538
|
+
const osmBrightIcon = `${osmBrightIconNameForSemanticIcon(iconName)}_11`;
|
|
1539
|
+
const customIconName = customIconMapping[iconName] ?? iconName;
|
|
1540
|
+
if (!customIconSpriteId2) {
|
|
1541
|
+
return ["coalesce", ["image", customIconName], ["image", osmBrightIcon]];
|
|
1542
|
+
}
|
|
1543
|
+
return [
|
|
1544
|
+
"coalesce",
|
|
1545
|
+
["image", `${customIconSpriteId2}:${customIconName}`],
|
|
1546
|
+
["image", osmBrightIcon]
|
|
1365
1547
|
];
|
|
1366
1548
|
}
|
|
1549
|
+
function osmBrightIconNameForSemanticIcon(iconName) {
|
|
1550
|
+
return semanticToOsmBrightIconName[iconName] ?? "marker";
|
|
1551
|
+
}
|
|
1552
|
+
var poiCategoryClassNames = {
|
|
1553
|
+
coffee: ["cafe"],
|
|
1554
|
+
culture: [
|
|
1555
|
+
"museum",
|
|
1556
|
+
"gallery",
|
|
1557
|
+
"art_gallery",
|
|
1558
|
+
"theatre",
|
|
1559
|
+
"cinema",
|
|
1560
|
+
"library",
|
|
1561
|
+
"attraction",
|
|
1562
|
+
"garden",
|
|
1563
|
+
"monument"
|
|
1564
|
+
],
|
|
1565
|
+
education: ["school", "college", "university"],
|
|
1566
|
+
food: ["restaurant", "fast_food", "bar", "pub", "biergarten"],
|
|
1567
|
+
health: ["hospital", "pharmacy", "doctors", "dentist"],
|
|
1568
|
+
lodging: ["lodging", "hotel", "motel", "hostel", "guest_house"],
|
|
1569
|
+
services: ["bank", "post", "toilet", "police", "fire_station", "town_hall"],
|
|
1570
|
+
shopping: ["shop", "mall", "supermarket", "marketplace"],
|
|
1571
|
+
transit: ["railway", "station", "bus", "bus_stop", "subway", "tram"]
|
|
1572
|
+
};
|
|
1573
|
+
var poiDensityRankCutoffs = {
|
|
1574
|
+
sparse: {
|
|
1575
|
+
coffee: 4,
|
|
1576
|
+
culture: 10,
|
|
1577
|
+
education: 8,
|
|
1578
|
+
food: 6,
|
|
1579
|
+
health: 8,
|
|
1580
|
+
lodging: 8,
|
|
1581
|
+
services: 6,
|
|
1582
|
+
shopping: 4,
|
|
1583
|
+
transit: 14,
|
|
1584
|
+
default: 6
|
|
1585
|
+
},
|
|
1586
|
+
balanced: {
|
|
1587
|
+
coffee: 10,
|
|
1588
|
+
culture: 18,
|
|
1589
|
+
education: 14,
|
|
1590
|
+
food: 14,
|
|
1591
|
+
health: 14,
|
|
1592
|
+
lodging: 16,
|
|
1593
|
+
services: 12,
|
|
1594
|
+
shopping: 10,
|
|
1595
|
+
transit: 24,
|
|
1596
|
+
default: 12
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
var semanticToOsmBrightIconName = {
|
|
1600
|
+
coffee: "cafe",
|
|
1601
|
+
culture: "museum",
|
|
1602
|
+
default: "marker",
|
|
1603
|
+
education: "school",
|
|
1604
|
+
food: "restaurant",
|
|
1605
|
+
health: "hospital",
|
|
1606
|
+
lodging: "lodging",
|
|
1607
|
+
services: "marker",
|
|
1608
|
+
shopping: "shop",
|
|
1609
|
+
transit: "railway"
|
|
1610
|
+
};
|
|
1367
1611
|
|
|
1368
1612
|
// src/basemaps/osm/layers.ts
|
|
1369
1613
|
function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
@@ -1375,7 +1619,8 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1375
1619
|
"background-color": colors.background
|
|
1376
1620
|
}
|
|
1377
1621
|
},
|
|
1378
|
-
|
|
1622
|
+
semanticLanduseLayer(source, sourceLayers.landuse, colors),
|
|
1623
|
+
semanticLandcoverLayer(source, sourceLayers.landcover, colors),
|
|
1379
1624
|
fillLayer(
|
|
1380
1625
|
"parks",
|
|
1381
1626
|
source,
|
|
@@ -1384,8 +1629,15 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1384
1629
|
parkOpacity(options.density)
|
|
1385
1630
|
),
|
|
1386
1631
|
fillLayer("water", source, sourceLayers.water, colors.hydro.water, 1),
|
|
1387
|
-
...buildingLayers(
|
|
1388
|
-
|
|
1632
|
+
...buildingLayers(
|
|
1633
|
+
source,
|
|
1634
|
+
sourceLayers.building,
|
|
1635
|
+
colors,
|
|
1636
|
+
options.density,
|
|
1637
|
+
options.buildings,
|
|
1638
|
+
options.buildingStyle
|
|
1639
|
+
),
|
|
1640
|
+
...roadLayers(source, sourceLayers.road, colors, options.roads, options.typography),
|
|
1389
1641
|
...boundaryLayers(source, sourceLayers.boundary, colors),
|
|
1390
1642
|
...labelLayers(source, sourceLayers, colors, options.labels, options.roads, options.typography),
|
|
1391
1643
|
...poiLayers(
|
|
@@ -1399,6 +1651,61 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1399
1651
|
)
|
|
1400
1652
|
];
|
|
1401
1653
|
}
|
|
1654
|
+
function semanticLanduseLayer(source, sourceLayer, colors) {
|
|
1655
|
+
return {
|
|
1656
|
+
id: "landuse",
|
|
1657
|
+
type: "fill",
|
|
1658
|
+
source,
|
|
1659
|
+
"source-layer": sourceLayer,
|
|
1660
|
+
paint: {
|
|
1661
|
+
"fill-color": [
|
|
1662
|
+
"match",
|
|
1663
|
+
["get", "class"],
|
|
1664
|
+
["commercial", "retail"],
|
|
1665
|
+
colors.landuse.commercial,
|
|
1666
|
+
["industrial", "railway"],
|
|
1667
|
+
colors.landuse.industrial,
|
|
1668
|
+
["school", "university", "hospital", "civic"],
|
|
1669
|
+
colors.landuse.civic,
|
|
1670
|
+
"cemetery",
|
|
1671
|
+
colors.landuse.cemetery,
|
|
1672
|
+
colors.landuse.residential
|
|
1673
|
+
],
|
|
1674
|
+
"fill-opacity": 0.62
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
function semanticLandcoverLayer(source, sourceLayer, colors) {
|
|
1679
|
+
return {
|
|
1680
|
+
id: "landcover",
|
|
1681
|
+
type: "fill",
|
|
1682
|
+
source,
|
|
1683
|
+
"source-layer": sourceLayer,
|
|
1684
|
+
filter: [
|
|
1685
|
+
"match",
|
|
1686
|
+
["get", "class"],
|
|
1687
|
+
["grass", "wood", "forest", "scrub", "farmland", "sand", "beach", "ice", "glacier"],
|
|
1688
|
+
true,
|
|
1689
|
+
false
|
|
1690
|
+
],
|
|
1691
|
+
paint: {
|
|
1692
|
+
"fill-color": [
|
|
1693
|
+
"match",
|
|
1694
|
+
["get", "class"],
|
|
1695
|
+
["wood", "forest"],
|
|
1696
|
+
colors.landcover.wood,
|
|
1697
|
+
["scrub", "farmland"],
|
|
1698
|
+
colors.landcover.protected,
|
|
1699
|
+
["sand", "beach"],
|
|
1700
|
+
colors.landcover.sand,
|
|
1701
|
+
["ice", "glacier"],
|
|
1702
|
+
colors.landcover.ice,
|
|
1703
|
+
colors.landcover.grass
|
|
1704
|
+
],
|
|
1705
|
+
"fill-opacity": 0.88
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1402
1709
|
function fillLayer(id, source, sourceLayer, color, opacity) {
|
|
1403
1710
|
return {
|
|
1404
1711
|
id,
|
|
@@ -1411,7 +1718,7 @@ function fillLayer(id, source, sourceLayer, color, opacity) {
|
|
|
1411
1718
|
}
|
|
1412
1719
|
};
|
|
1413
1720
|
}
|
|
1414
|
-
function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
1721
|
+
function buildingLayers(source, sourceLayer, colors, density, buildings, buildingStyle) {
|
|
1415
1722
|
if (buildings === "hidden") {
|
|
1416
1723
|
return [];
|
|
1417
1724
|
}
|
|
@@ -1478,8 +1785,12 @@ function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
|
1478
1785
|
"source-layer": sourceLayer,
|
|
1479
1786
|
minzoom: 13,
|
|
1480
1787
|
paint: {
|
|
1481
|
-
"fill-color":
|
|
1482
|
-
|
|
1788
|
+
"fill-color": buildingStyle ? buildingHeightColorExpression(
|
|
1789
|
+
buildingStyle.heightThreshold ?? 12,
|
|
1790
|
+
colors.buildings.lowRise,
|
|
1791
|
+
colors.buildings.highRise
|
|
1792
|
+
) : colors.buildings.fill,
|
|
1793
|
+
"fill-opacity": buildingStyle?.fillOpacity ?? (density === "dense" ? 0.62 : 0.5)
|
|
1483
1794
|
}
|
|
1484
1795
|
},
|
|
1485
1796
|
{
|
|
@@ -1489,13 +1800,33 @@ function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
|
1489
1800
|
"source-layer": sourceLayer,
|
|
1490
1801
|
minzoom: 15,
|
|
1491
1802
|
paint: {
|
|
1492
|
-
"line-color":
|
|
1493
|
-
|
|
1494
|
-
|
|
1803
|
+
"line-color": buildingStyle ? buildingHeightColorExpression(
|
|
1804
|
+
buildingStyle.heightThreshold ?? 12,
|
|
1805
|
+
colors.buildings.lowRiseOutline,
|
|
1806
|
+
colors.buildings.highRiseOutline
|
|
1807
|
+
) : colors.buildings.outline,
|
|
1808
|
+
"line-opacity": buildingStyle?.outlineOpacity ?? 0.24,
|
|
1809
|
+
"line-width": buildingStyle?.outlineWidth ?? 0.6
|
|
1495
1810
|
}
|
|
1496
1811
|
}
|
|
1497
1812
|
];
|
|
1498
1813
|
}
|
|
1814
|
+
function buildingHeightColorExpression(threshold, lowRise, highRise) {
|
|
1815
|
+
return [
|
|
1816
|
+
"case",
|
|
1817
|
+
[
|
|
1818
|
+
">=",
|
|
1819
|
+
[
|
|
1820
|
+
"to-number",
|
|
1821
|
+
["coalesce", ["get", "render_height"], ["get", "height"], ["get", "levels"]],
|
|
1822
|
+
0
|
|
1823
|
+
],
|
|
1824
|
+
threshold
|
|
1825
|
+
],
|
|
1826
|
+
highRise,
|
|
1827
|
+
lowRise
|
|
1828
|
+
];
|
|
1829
|
+
}
|
|
1499
1830
|
function boundaryLayers(source, sourceLayer, colors) {
|
|
1500
1831
|
return [
|
|
1501
1832
|
{
|
|
@@ -1545,12 +1876,15 @@ function applyLayerOverride(layers, layerId, override) {
|
|
|
1545
1876
|
return insertLayer(layers, layer, override.before);
|
|
1546
1877
|
}
|
|
1547
1878
|
const previousLayer = layers[index];
|
|
1879
|
+
const layout = mergeRecords(previousLayer.layout, override.layout);
|
|
1880
|
+
const paint = mergeRecords(previousLayer.paint, override.paint);
|
|
1881
|
+
const metadata = mergeRecords(previousLayer.metadata, override.metadata);
|
|
1548
1882
|
const patchedLayer = stripControlFields({
|
|
1549
1883
|
...previousLayer,
|
|
1550
1884
|
...override,
|
|
1551
|
-
layout
|
|
1552
|
-
paint
|
|
1553
|
-
metadata
|
|
1885
|
+
...layout ? { layout } : {},
|
|
1886
|
+
...paint ? { paint } : {},
|
|
1887
|
+
...metadata ? { metadata } : {}
|
|
1554
1888
|
});
|
|
1555
1889
|
const nextLayers = [...layers];
|
|
1556
1890
|
nextLayers[index] = patchedLayer;
|
|
@@ -1577,28 +1911,206 @@ function mergeRecords(previous, next) {
|
|
|
1577
1911
|
}
|
|
1578
1912
|
function stripControlFields(layer) {
|
|
1579
1913
|
const { before, ...styleLayer } = layer;
|
|
1580
|
-
return styleLayer;
|
|
1914
|
+
return Object.fromEntries(Object.entries(styleLayer).filter(([, value]) => value !== void 0));
|
|
1581
1915
|
}
|
|
1582
1916
|
function isRecord2(value) {
|
|
1583
1917
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1584
1918
|
}
|
|
1585
1919
|
|
|
1586
1920
|
// src/schema.ts
|
|
1921
|
+
import { z as z2 } from "zod";
|
|
1922
|
+
|
|
1923
|
+
// src/capture-scene.ts
|
|
1587
1924
|
import { z } from "zod";
|
|
1925
|
+
var tileflowCaptureSceneSchemaVersion = 1;
|
|
1926
|
+
var tileflowCaptureSceneLimits = Object.freeze({
|
|
1927
|
+
applicationPathLength: 2048,
|
|
1928
|
+
boundsPadding: { maximum: 1024, minimum: 0 },
|
|
1929
|
+
captureIdLength: 64,
|
|
1930
|
+
maximumPhysicalPixels: 16777216,
|
|
1931
|
+
pitch: { maximum: 85, minimum: 0 },
|
|
1932
|
+
selectorLength: 256,
|
|
1933
|
+
viewport: { maximum: 4096, minimum: 64 },
|
|
1934
|
+
zoom: { maximum: 24, minimum: 0 }
|
|
1935
|
+
});
|
|
1936
|
+
var finiteLongitudeSchema = z.number().finite().min(-180).max(180);
|
|
1937
|
+
var finiteLatitudeSchema = z.number().finite().min(-90).max(90);
|
|
1938
|
+
var bearingSchema = z.number().finite().min(-180).max(180).optional();
|
|
1939
|
+
var pitchSchema = z.number().finite().min(tileflowCaptureSceneLimits.pitch.minimum).max(tileflowCaptureSceneLimits.pitch.maximum).optional();
|
|
1940
|
+
var tileflowCaptureCenterCameraSchema = z.object({
|
|
1941
|
+
type: z.literal("center"),
|
|
1942
|
+
center: z.tuple([finiteLongitudeSchema, finiteLatitudeSchema]),
|
|
1943
|
+
zoom: z.number().finite().min(tileflowCaptureSceneLimits.zoom.minimum).max(tileflowCaptureSceneLimits.zoom.maximum),
|
|
1944
|
+
bearing: bearingSchema,
|
|
1945
|
+
pitch: pitchSchema
|
|
1946
|
+
}).strict();
|
|
1947
|
+
var tileflowCaptureBoundsCameraSchema = z.object({
|
|
1948
|
+
type: z.literal("bounds"),
|
|
1949
|
+
bounds: z.tuple([
|
|
1950
|
+
finiteLongitudeSchema,
|
|
1951
|
+
finiteLatitudeSchema,
|
|
1952
|
+
finiteLongitudeSchema,
|
|
1953
|
+
finiteLatitudeSchema
|
|
1954
|
+
]),
|
|
1955
|
+
padding: z.number().finite().min(tileflowCaptureSceneLimits.boundsPadding.minimum).max(tileflowCaptureSceneLimits.boundsPadding.maximum).optional(),
|
|
1956
|
+
bearing: bearingSchema,
|
|
1957
|
+
pitch: pitchSchema
|
|
1958
|
+
}).strict().superRefine((camera, context) => {
|
|
1959
|
+
const [west, south, east, north] = camera.bounds;
|
|
1960
|
+
if (south >= north) {
|
|
1961
|
+
context.addIssue({
|
|
1962
|
+
code: "custom",
|
|
1963
|
+
message: "Expected south latitude to be less than north latitude",
|
|
1964
|
+
path: ["bounds"]
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
if (west === east) {
|
|
1968
|
+
context.addIssue({
|
|
1969
|
+
code: "custom",
|
|
1970
|
+
message: "Expected west and east longitude to define a non-empty span",
|
|
1971
|
+
path: ["bounds"]
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
var tileflowCaptureCameraSchema = z.discriminatedUnion("type", [
|
|
1976
|
+
tileflowCaptureCenterCameraSchema,
|
|
1977
|
+
tileflowCaptureBoundsCameraSchema
|
|
1978
|
+
]);
|
|
1979
|
+
var tileflowCaptureViewportSchema = z.object({
|
|
1980
|
+
width: z.number().int().min(tileflowCaptureSceneLimits.viewport.minimum).max(tileflowCaptureSceneLimits.viewport.maximum),
|
|
1981
|
+
height: z.number().int().min(tileflowCaptureSceneLimits.viewport.minimum).max(tileflowCaptureSceneLimits.viewport.maximum),
|
|
1982
|
+
dpr: z.union([z.literal(1), z.literal(2)]).optional()
|
|
1983
|
+
}).strict().superRefine((viewport, context) => {
|
|
1984
|
+
const dpr = viewport.dpr ?? 1;
|
|
1985
|
+
const physicalPixels = viewport.width * viewport.height * dpr * dpr;
|
|
1986
|
+
if (physicalPixels > tileflowCaptureSceneLimits.maximumPhysicalPixels) {
|
|
1987
|
+
context.addIssue({
|
|
1988
|
+
code: "custom",
|
|
1989
|
+
message: `Expected at most ${tileflowCaptureSceneLimits.maximumPhysicalPixels} physical pixels`,
|
|
1990
|
+
path: ["dpr"]
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
});
|
|
1994
|
+
var tileflowCaptureIdSchema = z.string().trim().min(1).max(tileflowCaptureSceneLimits.captureIdLength).regex(/^[A-Za-z0-9_-]+$/, {
|
|
1995
|
+
message: "Expected letters, numbers, underscores, or hyphens"
|
|
1996
|
+
}).refine((name) => name !== "__proto__", {
|
|
1997
|
+
message: "Expected an identifier that cannot mutate an object prototype"
|
|
1998
|
+
});
|
|
1999
|
+
var tileflowCaptureSceneNameSchema = tileflowCaptureIdSchema.refine(
|
|
2000
|
+
(name) => !/^(?:AUX|CON|NUL|PRN|COM[1-9]|LPT[1-9])$/i.test(name),
|
|
2001
|
+
"Expected a portable scene name that is not a reserved filename or prototype key"
|
|
2002
|
+
);
|
|
2003
|
+
var applicationPathSchema = z.string().min(1).max(tileflowCaptureSceneLimits.applicationPathLength).refine(isPortableApplicationPath, {
|
|
2004
|
+
message: "Expected a root-relative application path without an origin, credentials, fragment, backslash, or control character"
|
|
2005
|
+
});
|
|
2006
|
+
var tileflowCaptureMapTargetSchema = z.object({ kind: z.literal("map") }).strict();
|
|
2007
|
+
var tileflowCaptureApplicationTargetSchema = z.object({
|
|
2008
|
+
kind: z.literal("application"),
|
|
2009
|
+
path: applicationPathSchema,
|
|
2010
|
+
captureId: tileflowCaptureIdSchema.optional(),
|
|
2011
|
+
selector: z.string().trim().min(1).max(tileflowCaptureSceneLimits.selectorLength).optional(),
|
|
2012
|
+
frame: z.enum(["map", "viewport"]).optional()
|
|
2013
|
+
}).strict().superRefine((target, context) => {
|
|
2014
|
+
if (target.captureId && target.selector) {
|
|
2015
|
+
context.addIssue({
|
|
2016
|
+
code: "custom",
|
|
2017
|
+
message: "Expected either captureId or selector, not both",
|
|
2018
|
+
path: ["selector"]
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
var tileflowCaptureTargetSchema = z.discriminatedUnion("kind", [
|
|
2023
|
+
tileflowCaptureMapTargetSchema,
|
|
2024
|
+
tileflowCaptureApplicationTargetSchema
|
|
2025
|
+
]);
|
|
2026
|
+
var tileflowCaptureSceneSchema = z.object({
|
|
2027
|
+
map: tileflowCaptureIdSchema,
|
|
2028
|
+
camera: tileflowCaptureCameraSchema,
|
|
2029
|
+
viewport: tileflowCaptureViewportSchema,
|
|
2030
|
+
target: tileflowCaptureTargetSchema.optional()
|
|
2031
|
+
}).strict();
|
|
2032
|
+
function normalizeTileflowCaptureScene(input) {
|
|
2033
|
+
const scene = tileflowCaptureSceneSchema.parse(input);
|
|
2034
|
+
const camera = scene.camera.type === "center" ? {
|
|
2035
|
+
type: scene.camera.type,
|
|
2036
|
+
center: scene.camera.center,
|
|
2037
|
+
zoom: scene.camera.zoom,
|
|
2038
|
+
bearing: scene.camera.bearing ?? 0,
|
|
2039
|
+
pitch: scene.camera.pitch ?? 0
|
|
2040
|
+
} : {
|
|
2041
|
+
type: scene.camera.type,
|
|
2042
|
+
bounds: scene.camera.bounds,
|
|
2043
|
+
padding: scene.camera.padding ?? 0,
|
|
2044
|
+
bearing: scene.camera.bearing ?? 0,
|
|
2045
|
+
pitch: scene.camera.pitch ?? 0
|
|
2046
|
+
};
|
|
2047
|
+
const target = scene.target ?? { kind: "map" };
|
|
2048
|
+
return {
|
|
2049
|
+
map: scene.map,
|
|
2050
|
+
camera,
|
|
2051
|
+
viewport: { ...scene.viewport, dpr: scene.viewport.dpr ?? 1 },
|
|
2052
|
+
target: target.kind === "map" ? target : {
|
|
2053
|
+
kind: target.kind,
|
|
2054
|
+
path: target.path,
|
|
2055
|
+
...target.captureId ? { captureId: target.captureId } : {},
|
|
2056
|
+
...target.selector ? { selector: target.selector } : {},
|
|
2057
|
+
frame: target.frame ?? "map"
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
function normalizeTileflowCaptureId(value) {
|
|
2062
|
+
return value === void 0 ? void 0 : tileflowCaptureIdSchema.parse(value);
|
|
2063
|
+
}
|
|
2064
|
+
function isPortableApplicationPath(value) {
|
|
2065
|
+
if (value !== value.trim() || !value.startsWith("/") || value.startsWith("//") || value.includes("#") || value.includes("\\") || hasControlCharacter(value)) {
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
2068
|
+
try {
|
|
2069
|
+
const base = new URL("http://tileflow.local");
|
|
2070
|
+
const resolved = new URL(value, base);
|
|
2071
|
+
return resolved.origin === base.origin && resolved.username === "" && resolved.password === "" && resolved.pathname.startsWith("/");
|
|
2072
|
+
} catch {
|
|
2073
|
+
return false;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
function hasControlCharacter(value) {
|
|
2077
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
2078
|
+
const code = value.charCodeAt(index);
|
|
2079
|
+
if (code <= 31 || code === 127) return true;
|
|
2080
|
+
}
|
|
2081
|
+
return false;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// src/schema.ts
|
|
1588
2085
|
var legacyThemeNames = /* @__PURE__ */ new Set(["standard", "light", "dark", "minimal", "osm-bright-2"]);
|
|
1589
|
-
var
|
|
2086
|
+
var prototypeSafeInputSchema = z2.unknown().superRefine((value, context) => {
|
|
2087
|
+
const unsafe = findUnsafeObjectStructure(value);
|
|
2088
|
+
if (unsafe) {
|
|
2089
|
+
context.addIssue({
|
|
2090
|
+
code: "custom",
|
|
2091
|
+
message: unsafe.reason === "key" ? "Expected an object without prototype-mutating keys" : "Expected plain configuration objects without inherited properties",
|
|
2092
|
+
path: unsafe.path
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
var identifierSchema = z2.string().trim().regex(/^[A-Za-z0-9_-]+$/, {
|
|
1590
2097
|
message: "Expected letters, numbers, underscores, or hyphens"
|
|
2098
|
+
}).refine((value) => value !== "__proto__", {
|
|
2099
|
+
message: "Expected an identifier that cannot mutate an object prototype"
|
|
2100
|
+
});
|
|
2101
|
+
var sourceVersionSchema = z2.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/, {
|
|
2102
|
+
message: "Expected a portable source version"
|
|
1591
2103
|
});
|
|
1592
|
-
var hexColorSchema =
|
|
2104
|
+
var hexColorSchema = z2.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
1593
2105
|
message: "Expected a hex color like #F6F7F3 or #F6F7F3FF"
|
|
1594
2106
|
});
|
|
1595
2107
|
function colorGroupSchema(keys) {
|
|
1596
2108
|
const shape = Object.fromEntries(
|
|
1597
2109
|
keys.map((key) => [key, hexColorSchema.optional()])
|
|
1598
2110
|
);
|
|
1599
|
-
return
|
|
2111
|
+
return z2.object(shape).strict();
|
|
1600
2112
|
}
|
|
1601
|
-
var colorConfigSchema =
|
|
2113
|
+
var colorConfigSchema = z2.object({
|
|
1602
2114
|
accent: hexColorSchema.optional(),
|
|
1603
2115
|
background: hexColorSchema.optional(),
|
|
1604
2116
|
boundary: hexColorSchema.optional(),
|
|
@@ -1616,7 +2128,15 @@ var colorConfigSchema = z.object({
|
|
|
1616
2128
|
textMuted: hexColorSchema.optional(),
|
|
1617
2129
|
water: hexColorSchema.optional(),
|
|
1618
2130
|
boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
|
|
1619
|
-
buildings: colorGroupSchema([
|
|
2131
|
+
buildings: colorGroupSchema([
|
|
2132
|
+
"extrusion",
|
|
2133
|
+
"fill",
|
|
2134
|
+
"highRise",
|
|
2135
|
+
"highRiseOutline",
|
|
2136
|
+
"lowRise",
|
|
2137
|
+
"lowRiseOutline",
|
|
2138
|
+
"outline"
|
|
2139
|
+
]).optional(),
|
|
1620
2140
|
hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
|
|
1621
2141
|
labels: colorGroupSchema([
|
|
1622
2142
|
"country",
|
|
@@ -1665,9 +2185,17 @@ var colorConfigSchema = z.object({
|
|
|
1665
2185
|
"tunnel"
|
|
1666
2186
|
]).optional()
|
|
1667
2187
|
}).strict();
|
|
1668
|
-
var themeModulesSchema =
|
|
2188
|
+
var themeModulesSchema = z2.object({
|
|
1669
2189
|
boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
|
|
1670
|
-
buildings: colorGroupSchema([
|
|
2190
|
+
buildings: colorGroupSchema([
|
|
2191
|
+
"extrusion",
|
|
2192
|
+
"fill",
|
|
2193
|
+
"highRise",
|
|
2194
|
+
"highRiseOutline",
|
|
2195
|
+
"lowRise",
|
|
2196
|
+
"lowRiseOutline",
|
|
2197
|
+
"outline"
|
|
2198
|
+
]).optional(),
|
|
1671
2199
|
hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
|
|
1672
2200
|
labels: colorGroupSchema([
|
|
1673
2201
|
"country",
|
|
@@ -1716,169 +2244,228 @@ var themeModulesSchema = z.object({
|
|
|
1716
2244
|
"tunnel"
|
|
1717
2245
|
]).optional()
|
|
1718
2246
|
}).strict();
|
|
1719
|
-
var styleLayerOverrideSchema =
|
|
1720
|
-
before:
|
|
1721
|
-
filter:
|
|
1722
|
-
layout:
|
|
1723
|
-
maxzoom:
|
|
1724
|
-
metadata:
|
|
1725
|
-
minzoom:
|
|
1726
|
-
paint:
|
|
1727
|
-
source:
|
|
1728
|
-
"source-layer":
|
|
1729
|
-
type:
|
|
1730
|
-
}).catchall(
|
|
1731
|
-
var
|
|
1732
|
-
font:
|
|
1733
|
-
fontFamily:
|
|
1734
|
-
weight:
|
|
2247
|
+
var styleLayerOverrideSchema = z2.object({
|
|
2248
|
+
before: z2.string().min(1).optional(),
|
|
2249
|
+
filter: z2.unknown().optional(),
|
|
2250
|
+
layout: z2.record(z2.string(), z2.unknown()).optional(),
|
|
2251
|
+
maxzoom: z2.number().finite().min(0).max(24).optional(),
|
|
2252
|
+
metadata: z2.record(z2.string(), z2.unknown()).optional(),
|
|
2253
|
+
minzoom: z2.number().finite().min(0).max(24).optional(),
|
|
2254
|
+
paint: z2.record(z2.string(), z2.unknown()).optional(),
|
|
2255
|
+
source: z2.string().min(1).optional(),
|
|
2256
|
+
"source-layer": z2.string().min(1).optional(),
|
|
2257
|
+
type: z2.string().min(1).optional()
|
|
2258
|
+
}).catchall(z2.unknown());
|
|
2259
|
+
var typographyStyleSchema = z2.object({
|
|
2260
|
+
font: z2.string().trim().min(1).optional(),
|
|
2261
|
+
fontFamily: z2.string().trim().min(1).optional(),
|
|
2262
|
+
weight: z2.enum(["regular", "medium", "semibold", "bold"]).optional()
|
|
1735
2263
|
}).strict();
|
|
1736
|
-
var
|
|
1737
|
-
|
|
1738
|
-
|
|
2264
|
+
var typographySchema = typographyStyleSchema.extend({
|
|
2265
|
+
places: typographyStyleSchema.optional(),
|
|
2266
|
+
poi: typographyStyleSchema.optional(),
|
|
2267
|
+
roads: typographyStyleSchema.optional(),
|
|
2268
|
+
water: typographyStyleSchema.optional()
|
|
2269
|
+
});
|
|
2270
|
+
var fontsSchema = z2.object({
|
|
2271
|
+
body: z2.string().trim().min(1).optional(),
|
|
2272
|
+
labels: z2.string().trim().min(1).optional()
|
|
1739
2273
|
}).strict();
|
|
1740
|
-
var tileflowThemeSchema =
|
|
2274
|
+
var tileflowThemeSchema = z2.object({
|
|
1741
2275
|
colors: colorConfigSchema.optional(),
|
|
1742
|
-
extends:
|
|
2276
|
+
extends: z2.string().trim().min(1).optional(),
|
|
1743
2277
|
fonts: fontsSchema.optional(),
|
|
1744
|
-
layers:
|
|
1745
|
-
mode:
|
|
2278
|
+
layers: z2.record(z2.string(), styleLayerOverrideSchema).optional(),
|
|
2279
|
+
mode: z2.enum(["light", "dark"]).optional(),
|
|
1746
2280
|
modules: themeModulesSchema.optional(),
|
|
1747
2281
|
typography: typographySchema.optional()
|
|
1748
2282
|
}).strict();
|
|
1749
2283
|
var themeConfigSchema = tileflowThemeSchema;
|
|
1750
|
-
var sourceLayersSchema =
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
2284
|
+
var sourceLayersSchema = z2.object({
|
|
2285
|
+
landcover: z2.string().optional(),
|
|
2286
|
+
landuse: z2.string().optional(),
|
|
2287
|
+
park: z2.string().optional(),
|
|
2288
|
+
water: z2.string().optional(),
|
|
2289
|
+
waterName: z2.string().optional(),
|
|
2290
|
+
waterway: z2.string().optional(),
|
|
2291
|
+
road: z2.string().optional(),
|
|
2292
|
+
roadName: z2.string().optional(),
|
|
2293
|
+
building: z2.string().optional(),
|
|
2294
|
+
boundary: z2.string().optional(),
|
|
2295
|
+
place: z2.string().optional(),
|
|
2296
|
+
poi: z2.string().optional()
|
|
1762
2297
|
}).strict();
|
|
1763
|
-
var basemapSchema =
|
|
1764
|
-
type:
|
|
1765
|
-
attribution:
|
|
1766
|
-
glyphs:
|
|
1767
|
-
sourceId:
|
|
2298
|
+
var basemapSchema = z2.object({
|
|
2299
|
+
type: z2.literal("osm"),
|
|
2300
|
+
attribution: z2.string().optional(),
|
|
2301
|
+
glyphs: z2.string().min(1).optional(),
|
|
2302
|
+
sourceId: z2.string().min(1).optional(),
|
|
1768
2303
|
sourceLayers: sourceLayersSchema.optional(),
|
|
1769
|
-
sprite:
|
|
1770
|
-
tileset:
|
|
1771
|
-
url:
|
|
2304
|
+
sprite: z2.string().min(1).optional(),
|
|
2305
|
+
tileset: z2.string().trim().min(1).optional(),
|
|
2306
|
+
url: z2.url().optional(),
|
|
2307
|
+
version: sourceVersionSchema.optional()
|
|
1772
2308
|
}).strict();
|
|
1773
|
-
var
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2309
|
+
var roadDetailSchema = z2.enum(["none", "highways", "major", "streets", "all"]);
|
|
2310
|
+
var roadClassSchema = z2.enum([
|
|
2311
|
+
"motorway",
|
|
2312
|
+
"trunk",
|
|
2313
|
+
"primary",
|
|
2314
|
+
"secondary",
|
|
2315
|
+
"tertiary",
|
|
2316
|
+
"minor",
|
|
2317
|
+
"service",
|
|
2318
|
+
"track",
|
|
2319
|
+
"path"
|
|
2320
|
+
]);
|
|
2321
|
+
var roadsModuleSchema = z2.object({
|
|
2322
|
+
type: z2.literal("roads"),
|
|
2323
|
+
detail: roadDetailSchema.optional(),
|
|
2324
|
+
extras: z2.object({
|
|
2325
|
+
ferry: z2.boolean().optional(),
|
|
2326
|
+
paths: z2.boolean().optional(),
|
|
2327
|
+
rail: z2.boolean().optional()
|
|
1780
2328
|
}).strict().optional(),
|
|
1781
|
-
hierarchy:
|
|
1782
|
-
|
|
1783
|
-
|
|
2329
|
+
hierarchy: z2.enum(["subtle", "clear", "strong"]).optional(),
|
|
2330
|
+
oneWayMarkers: z2.boolean().optional(),
|
|
2331
|
+
outline: z2.enum(["none", "subtle", "strong"]).optional(),
|
|
2332
|
+
weight: z2.enum(["thin", "regular", "bold"]).optional(),
|
|
2333
|
+
widthScale: z2.partialRecord(roadClassSchema, z2.number().finite().min(0.1).max(8)).optional()
|
|
1784
2334
|
}).strict();
|
|
1785
|
-
var labelDetailSchema =
|
|
1786
|
-
var labelsModuleSchema =
|
|
1787
|
-
type:
|
|
1788
|
-
language:
|
|
2335
|
+
var labelDetailSchema = z2.enum(["none", "major", "all"]);
|
|
2336
|
+
var labelsModuleSchema = z2.object({
|
|
2337
|
+
type: z2.literal("labels"),
|
|
2338
|
+
language: z2.string().trim().min(1).optional(),
|
|
1789
2339
|
places: labelDetailSchema.optional(),
|
|
1790
|
-
|
|
2340
|
+
roadClasses: z2.array(roadClassSchema).min(1).max(9).optional(),
|
|
2341
|
+
roads: roadDetailSchema.optional(),
|
|
1791
2342
|
water: labelDetailSchema.optional()
|
|
1792
2343
|
}).strict();
|
|
1793
|
-
var poiModuleSchema =
|
|
1794
|
-
type:
|
|
1795
|
-
categories:
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2344
|
+
var poiModuleSchema = z2.object({
|
|
2345
|
+
type: z2.literal("poi"),
|
|
2346
|
+
categories: z2.array(identifierSchema).optional(),
|
|
2347
|
+
classMapping: z2.record(identifierSchema, z2.array(identifierSchema).min(1).max(64)).optional(),
|
|
2348
|
+
color: z2.enum(["uniform", "category"]).optional(),
|
|
2349
|
+
density: z2.enum(["sparse", "balanced", "dense"]).optional(),
|
|
2350
|
+
icons: z2.union([z2.boolean(), z2.enum(["essential", "full"])]).optional(),
|
|
2351
|
+
labels: z2.enum(["none", "minimal", "balanced", "full"]).optional(),
|
|
2352
|
+
minZoom: z2.number().finite().min(0).max(24).optional(),
|
|
2353
|
+
placement: z2.object({
|
|
2354
|
+
coupleIconAndLabel: z2.boolean().optional(),
|
|
2355
|
+
iconPadding: z2.number().finite().min(0).max(64).optional(),
|
|
2356
|
+
textPadding: z2.number().finite().min(0).max(64).optional()
|
|
2357
|
+
}).strict().optional(),
|
|
2358
|
+
preset: z2.enum(["none", "minimal", "balanced", "full"]).optional()
|
|
1800
2359
|
}).strict();
|
|
1801
|
-
var styleOverrideModuleSchema =
|
|
1802
|
-
type:
|
|
1803
|
-
layers:
|
|
1804
|
-
removeLayers:
|
|
2360
|
+
var styleOverrideModuleSchema = z2.object({
|
|
2361
|
+
type: z2.literal("styleOverride"),
|
|
2362
|
+
layers: z2.record(z2.string(), styleLayerOverrideSchema).optional(),
|
|
2363
|
+
removeLayers: z2.array(z2.string().min(1)).optional()
|
|
1805
2364
|
}).strict();
|
|
1806
|
-
var moduleSchema =
|
|
2365
|
+
var moduleSchema = z2.discriminatedUnion("type", [
|
|
1807
2366
|
labelsModuleSchema,
|
|
1808
2367
|
poiModuleSchema,
|
|
1809
2368
|
roadsModuleSchema,
|
|
1810
2369
|
styleOverrideModuleSchema
|
|
1811
2370
|
]);
|
|
1812
|
-
var iconSetSchema =
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
extends:
|
|
1816
|
-
mapping:
|
|
1817
|
-
source:
|
|
1818
|
-
sprite:
|
|
2371
|
+
var iconSetSchema = z2.union([
|
|
2372
|
+
z2.string().trim().min(1),
|
|
2373
|
+
z2.object({
|
|
2374
|
+
extends: z2.string().trim().min(1).optional(),
|
|
2375
|
+
mapping: z2.record(identifierSchema, z2.string().trim().min(1)).optional(),
|
|
2376
|
+
source: z2.string().trim().min(1).optional(),
|
|
2377
|
+
sprite: z2.string().trim().min(1).optional()
|
|
1819
2378
|
}).strict()
|
|
1820
2379
|
]);
|
|
1821
|
-
var viewSchema =
|
|
1822
|
-
bearing:
|
|
1823
|
-
center:
|
|
2380
|
+
var viewSchema = z2.object({
|
|
2381
|
+
bearing: z2.number().finite().min(-180).max(180).optional(),
|
|
2382
|
+
center: z2.tuple([z2.number().finite(), z2.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
|
|
1824
2383
|
message: "Expected center as [lng, lat] within world bounds"
|
|
1825
2384
|
}).optional(),
|
|
1826
|
-
zoom:
|
|
2385
|
+
zoom: z2.number().finite().min(0).max(24).optional()
|
|
2386
|
+
}).strict();
|
|
2387
|
+
var buildingStyleSchema = z2.object({
|
|
2388
|
+
fillOpacity: z2.number().finite().min(0).max(1).optional(),
|
|
2389
|
+
heightThreshold: z2.number().finite().min(0).max(1e3).optional(),
|
|
2390
|
+
outlineOpacity: z2.number().finite().min(0).max(1).optional(),
|
|
2391
|
+
outlineWidth: z2.number().finite().min(0).max(16).optional()
|
|
1827
2392
|
}).strict();
|
|
1828
|
-
var terrainConfigSchema =
|
|
1829
|
-
attribution:
|
|
1830
|
-
encoding:
|
|
1831
|
-
exaggeration:
|
|
1832
|
-
mode:
|
|
1833
|
-
sourceId:
|
|
1834
|
-
tileset:
|
|
1835
|
-
url:
|
|
2393
|
+
var terrainConfigSchema = z2.object({
|
|
2394
|
+
attribution: z2.string().optional(),
|
|
2395
|
+
encoding: z2.enum(["mapbox", "terrarium"]).optional(),
|
|
2396
|
+
exaggeration: z2.number().min(0).max(10).optional(),
|
|
2397
|
+
mode: z2.enum(["none", "hillshade", "3d"]).optional(),
|
|
2398
|
+
sourceId: z2.string().min(1).optional(),
|
|
2399
|
+
tileset: z2.string().trim().min(1).optional(),
|
|
2400
|
+
url: z2.url().optional()
|
|
1836
2401
|
}).strict();
|
|
1837
|
-
var tilesSchema =
|
|
1838
|
-
sourceId:
|
|
1839
|
-
tileset:
|
|
1840
|
-
url:
|
|
1841
|
-
attribution:
|
|
1842
|
-
sourceLayers: sourceLayersSchema.optional()
|
|
2402
|
+
var tilesSchema = z2.object({
|
|
2403
|
+
sourceId: z2.string().min(1).optional(),
|
|
2404
|
+
tileset: z2.string().trim().min(1).optional(),
|
|
2405
|
+
url: z2.url().optional(),
|
|
2406
|
+
attribution: z2.string().optional(),
|
|
2407
|
+
sourceLayers: sourceLayersSchema.optional(),
|
|
2408
|
+
version: sourceVersionSchema.optional()
|
|
1843
2409
|
}).strict();
|
|
1844
|
-
var tileflowMapSchema =
|
|
2410
|
+
var tileflowMapSchema = z2.object({
|
|
1845
2411
|
basemap: basemapSchema.optional(),
|
|
1846
|
-
name:
|
|
1847
|
-
tileset:
|
|
1848
|
-
theme:
|
|
1849
|
-
modules:
|
|
1850
|
-
layers:
|
|
2412
|
+
name: z2.string().min(1).optional(),
|
|
2413
|
+
tileset: z2.string().trim().min(1).optional(),
|
|
2414
|
+
theme: z2.union([z2.string().trim().min(1), themeConfigSchema]).optional(),
|
|
2415
|
+
modules: z2.array(moduleSchema).optional(),
|
|
2416
|
+
layers: z2.record(z2.string(), styleLayerOverrideSchema).optional(),
|
|
1851
2417
|
colors: colorConfigSchema.optional(),
|
|
1852
2418
|
typography: typographySchema.optional(),
|
|
1853
|
-
density:
|
|
1854
|
-
roads:
|
|
1855
|
-
labels:
|
|
1856
|
-
poi:
|
|
1857
|
-
buildings:
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2419
|
+
density: z2.enum(["clean", "balanced", "dense"]).optional(),
|
|
2420
|
+
roads: z2.enum(["hidden", "soft", "standard", "detailed"]).optional(),
|
|
2421
|
+
labels: z2.enum(["none", "essential", "balanced", "full"]).optional(),
|
|
2422
|
+
poi: z2.enum(["none", "minimal", "balanced", "full"]).optional(),
|
|
2423
|
+
buildings: z2.enum(["hidden", "flat", "3d"]).optional(),
|
|
2424
|
+
buildingStyle: buildingStyleSchema.optional(),
|
|
2425
|
+
terrain: z2.union([z2.enum(["none", "hillshade", "3d"]), terrainConfigSchema]).optional(),
|
|
2426
|
+
renderer: z2.enum(["auto", "osm-bright", "generated"]).optional(),
|
|
2427
|
+
glyphs: z2.string().min(1).optional(),
|
|
1861
2428
|
icons: iconSetSchema.optional(),
|
|
1862
|
-
sprite:
|
|
2429
|
+
sprite: z2.string().min(1).optional(),
|
|
1863
2430
|
tiles: tilesSchema.optional(),
|
|
1864
2431
|
view: viewSchema.optional(),
|
|
1865
|
-
allowedOrigins:
|
|
1866
|
-
}).strict();
|
|
1867
|
-
var configSchema = tileflowMapSchema;
|
|
1868
|
-
var tilesetConfigSchema = z.object({
|
|
1869
|
-
id: z.string().trim().min(1).optional(),
|
|
1870
|
-
name: z.string().trim().min(1).optional(),
|
|
1871
|
-
attribution: z.string().optional(),
|
|
1872
|
-
sourceLayers: sourceLayersSchema.optional()
|
|
2432
|
+
allowedOrigins: z2.array(z2.string().trim().min(1).max(253)).max(20).optional()
|
|
1873
2433
|
}).strict();
|
|
1874
|
-
var
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
2434
|
+
var configSchema = prototypeSafeInputSchema.pipe(tileflowMapSchema);
|
|
2435
|
+
var tilesetConfigSchema = z2.object({
|
|
2436
|
+
id: z2.string().trim().min(1).optional(),
|
|
2437
|
+
name: z2.string().trim().min(1).optional(),
|
|
2438
|
+
attribution: z2.string().optional(),
|
|
2439
|
+
sourceLayers: sourceLayersSchema.optional(),
|
|
2440
|
+
version: sourceVersionSchema.optional()
|
|
1881
2441
|
}).strict();
|
|
2442
|
+
var captureScenesSchema = z2.record(tileflowCaptureSceneNameSchema, tileflowCaptureSceneSchema).superRefine((scenes, context) => {
|
|
2443
|
+
const namesByCaseFold = /* @__PURE__ */ new Map();
|
|
2444
|
+
for (const name of Object.keys(scenes).sort()) {
|
|
2445
|
+
const folded = name.toLowerCase();
|
|
2446
|
+
const existing = namesByCaseFold.get(folded);
|
|
2447
|
+
if (existing !== void 0) {
|
|
2448
|
+
context.addIssue({
|
|
2449
|
+
code: "custom",
|
|
2450
|
+
message: `Expected a portable scene name distinct from "${existing}" on case-insensitive filesystems`,
|
|
2451
|
+
path: [name]
|
|
2452
|
+
});
|
|
2453
|
+
} else {
|
|
2454
|
+
namesByCaseFold.set(folded, name);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
var tileflowProjectSchema = prototypeSafeInputSchema.pipe(
|
|
2459
|
+
z2.object({
|
|
2460
|
+
icons: z2.record(identifierSchema, iconSetSchema).optional(),
|
|
2461
|
+
scenes: captureScenesSchema.optional(),
|
|
2462
|
+
themes: z2.record(identifierSchema, themeConfigSchema).optional(),
|
|
2463
|
+
tilesets: z2.record(identifierSchema, tilesetConfigSchema).optional(),
|
|
2464
|
+
maps: z2.record(identifierSchema, configSchema).refine((maps) => {
|
|
2465
|
+
return Object.keys(maps).length > 0;
|
|
2466
|
+
}, "Expected at least one map")
|
|
2467
|
+
}).strict()
|
|
2468
|
+
);
|
|
1882
2469
|
var projectConfigSchema = tileflowProjectSchema;
|
|
1883
2470
|
function parseTileflowMap(input) {
|
|
1884
2471
|
const parsed = configSchema.parse(input);
|
|
@@ -1935,17 +2522,54 @@ function validateConfig(input) {
|
|
|
1935
2522
|
var validateTileflowConfig = validateConfig;
|
|
1936
2523
|
function isProjectConfigLike(input) {
|
|
1937
2524
|
return Boolean(
|
|
1938
|
-
input && typeof input === "object" && "maps"
|
|
2525
|
+
input && typeof input === "object" && Object.hasOwn(input, "maps") && typeof input.maps === "object"
|
|
1939
2526
|
);
|
|
1940
2527
|
}
|
|
2528
|
+
function findUnsafeObjectStructure(input) {
|
|
2529
|
+
if (!input || typeof input !== "object") return void 0;
|
|
2530
|
+
const pending = [{ path: [], value: input }];
|
|
2531
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
2532
|
+
while (pending.length > 0) {
|
|
2533
|
+
const current = pending.pop();
|
|
2534
|
+
if (!current || visited.has(current.value)) continue;
|
|
2535
|
+
visited.add(current.value);
|
|
2536
|
+
if (!Array.isArray(current.value)) {
|
|
2537
|
+
const prototype = Object.getPrototypeOf(current.value);
|
|
2538
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
2539
|
+
return { path: current.path, reason: "prototype" };
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
for (const key of Object.keys(current.value)) {
|
|
2543
|
+
const path = [...current.path, Array.isArray(current.value) ? Number(key) : key];
|
|
2544
|
+
if (key === "__proto__") return { path, reason: "key" };
|
|
2545
|
+
const descriptor = Object.getOwnPropertyDescriptor(current.value, key);
|
|
2546
|
+
if (descriptor && "value" in descriptor && descriptor.value && typeof descriptor.value === "object") {
|
|
2547
|
+
pending.push({ path, value: descriptor.value });
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
return void 0;
|
|
2552
|
+
}
|
|
1941
2553
|
function validateProjectThemeReferences(project) {
|
|
1942
2554
|
const themeNames = new Set(Object.keys(project.themes ?? {}));
|
|
1943
2555
|
const themeMessages = validateThemeExtends(project.themes, themeNames);
|
|
1944
2556
|
const iconMessages = validateProjectIconReferences(project);
|
|
2557
|
+
const sceneMessages = validateProjectSceneReferences(project);
|
|
1945
2558
|
const mapMessages = Object.entries(project.maps).flatMap(
|
|
1946
2559
|
([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
|
|
1947
2560
|
);
|
|
1948
|
-
return [...themeMessages, ...iconMessages, ...mapMessages];
|
|
2561
|
+
return [...themeMessages, ...iconMessages, ...sceneMessages, ...mapMessages];
|
|
2562
|
+
}
|
|
2563
|
+
function validateProjectSceneReferences(project) {
|
|
2564
|
+
return Object.entries(project.scenes ?? {}).flatMap(
|
|
2565
|
+
([sceneName, scene]) => Object.hasOwn(project.maps, scene.map) ? [] : [
|
|
2566
|
+
{
|
|
2567
|
+
level: "error",
|
|
2568
|
+
path: `scenes.${sceneName}.map`,
|
|
2569
|
+
message: `Unknown Tileflow map "${scene.map}"`
|
|
2570
|
+
}
|
|
2571
|
+
]
|
|
2572
|
+
);
|
|
1949
2573
|
}
|
|
1950
2574
|
function validateMapThemeReference(config, themeNames, path) {
|
|
1951
2575
|
if (!config.theme) {
|
|
@@ -2015,7 +2639,7 @@ function validateProjectIconReferences(project) {
|
|
|
2015
2639
|
const iconSets = project.icons ?? {};
|
|
2016
2640
|
for (const [iconSetName, iconSet] of Object.entries(iconSets)) {
|
|
2017
2641
|
const extendedName = iconSetReference(iconSet);
|
|
2018
|
-
if (extendedName && !iconSets
|
|
2642
|
+
if (extendedName && !Object.hasOwn(iconSets, extendedName)) {
|
|
2019
2643
|
messages.push({
|
|
2020
2644
|
level: "error",
|
|
2021
2645
|
path: iconSetExtendsPath(`icons.${iconSetName}`, iconSet),
|
|
@@ -2037,7 +2661,7 @@ function validateProjectIconReferences(project) {
|
|
|
2037
2661
|
if (!isIconSetObject(mapConfig.icons) || !mapConfig.icons.extends) {
|
|
2038
2662
|
continue;
|
|
2039
2663
|
}
|
|
2040
|
-
if (!iconSets
|
|
2664
|
+
if (!Object.hasOwn(iconSets, mapConfig.icons.extends)) {
|
|
2041
2665
|
messages.push({
|
|
2042
2666
|
level: "error",
|
|
2043
2667
|
path: `maps.${mapName}.icons.extends`,
|
|
@@ -2051,8 +2675,8 @@ function findIconSetCycle(iconSetName, iconSets, path) {
|
|
|
2051
2675
|
if (path.includes(iconSetName)) {
|
|
2052
2676
|
return [...path, iconSetName];
|
|
2053
2677
|
}
|
|
2054
|
-
const nextIconSetName = iconSetReference(iconSets?.[iconSetName]);
|
|
2055
|
-
if (!nextIconSetName || !iconSets
|
|
2678
|
+
const nextIconSetName = Object.hasOwn(iconSets ?? {}, iconSetName) ? iconSetReference(iconSets?.[iconSetName]) : void 0;
|
|
2679
|
+
if (!nextIconSetName || !Object.hasOwn(iconSets ?? {}, nextIconSetName)) {
|
|
2056
2680
|
return void 0;
|
|
2057
2681
|
}
|
|
2058
2682
|
return findIconSetCycle(nextIconSetName, iconSets, [...path, iconSetName]);
|
|
@@ -2136,6 +2760,7 @@ function applyOsmBrightControls(layers, options) {
|
|
|
2136
2760
|
return applyPoiLayerControls(
|
|
2137
2761
|
layer,
|
|
2138
2762
|
options.poi,
|
|
2763
|
+
options.colors,
|
|
2139
2764
|
options.customIconSpriteId,
|
|
2140
2765
|
options.customIconMapping
|
|
2141
2766
|
);
|
|
@@ -2162,7 +2787,8 @@ function applyRoadLayerControls(layer, roads2) {
|
|
|
2162
2787
|
const opacityScale = isCasing ? metrics.outlineOpacity : group === "highways" || group === "major" ? metrics.majorOpacity : group === "ferry" ? metrics.majorOpacity * 0.8 : group === "paths" ? metrics.minorOpacity * 0.77 : group === "rail" ? metrics.minorOpacity * 0.65 : metrics.minorOpacity;
|
|
2163
2788
|
if (type === "line") {
|
|
2164
2789
|
paint["line-opacity"] = scaleStyleValue(paint["line-opacity"], opacityScale, 1);
|
|
2165
|
-
|
|
2790
|
+
const scaledWidth = scaleStyleValue(paint["line-width"], widthScale, 1);
|
|
2791
|
+
paint["line-width"] = roads2.widthScale ? multiplyStyleValue(scaledWidth, roadClassWidthScaleExpression(roads2.widthScale)) : scaledWidth;
|
|
2166
2792
|
nextLayer.paint = paint;
|
|
2167
2793
|
}
|
|
2168
2794
|
if (type === "fill") {
|
|
@@ -2183,6 +2809,22 @@ function applyRoadLayerControls(layer, roads2) {
|
|
|
2183
2809
|
}
|
|
2184
2810
|
return nextLayer;
|
|
2185
2811
|
}
|
|
2812
|
+
function multiplyStyleValue(value, multiplier) {
|
|
2813
|
+
return ["*", legacyCameraFunctionExpression(value), multiplier];
|
|
2814
|
+
}
|
|
2815
|
+
function legacyCameraFunctionExpression(value) {
|
|
2816
|
+
if (!isRecord3(value) || !Array.isArray(value.stops)) return value;
|
|
2817
|
+
const stops = value.stops.filter(
|
|
2818
|
+
(stop) => Array.isArray(stop) && stop.length >= 2 && typeof stop[0] === "number"
|
|
2819
|
+
);
|
|
2820
|
+
if (stops.length === 0) return typeof value.default === "number" ? value.default : 1;
|
|
2821
|
+
return [
|
|
2822
|
+
"interpolate",
|
|
2823
|
+
["exponential", typeof value.base === "number" ? value.base : 1],
|
|
2824
|
+
["zoom"],
|
|
2825
|
+
...stops.flatMap(([zoom, output]) => [zoom, output])
|
|
2826
|
+
];
|
|
2827
|
+
}
|
|
2186
2828
|
function isRoadLayerVisible(group, roads2) {
|
|
2187
2829
|
if (roads2.detail === "none") return false;
|
|
2188
2830
|
if (group === "paths") return roads2.extras.paths;
|
|
@@ -2241,10 +2883,10 @@ function applyLabelLayerControls(layer, labels2, roads2) {
|
|
|
2241
2883
|
if ("text-field" in layout && !isHighwayShieldLayer(id)) {
|
|
2242
2884
|
layout["text-field"] = labelTextField(labels2.language);
|
|
2243
2885
|
}
|
|
2244
|
-
if (group === "roads"
|
|
2245
|
-
const allowedClasses = visibleRoadLabelClasses(detail, roads2).filter(
|
|
2246
|
-
(roadClass) =>
|
|
2247
|
-
);
|
|
2886
|
+
if (group === "roads") {
|
|
2887
|
+
const allowedClasses = (roads2 ? visibleRoadLabelClasses(detail, roads2, labels2.roadClasses) : roadLabelClasses(detail).filter(
|
|
2888
|
+
(roadClass) => !labels2.roadClasses || labels2.roadClasses.includes(roadClass)
|
|
2889
|
+
)).filter((roadClass) => roadLabelLayerClasses(id).includes(roadClass));
|
|
2248
2890
|
if (allowedClasses.length === 0) {
|
|
2249
2891
|
return setLayerVisibility(layer, "none");
|
|
2250
2892
|
}
|
|
@@ -2260,15 +2902,24 @@ function applyLabelLayerControls(layer, labels2, roads2) {
|
|
|
2260
2902
|
nextLayer.layout = layout;
|
|
2261
2903
|
return nextLayer;
|
|
2262
2904
|
}
|
|
2263
|
-
function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMapping) {
|
|
2905
|
+
function applyPoiLayerControls(layer, poi2, colors, customIconSpriteId2, customIconMapping) {
|
|
2264
2906
|
if (poi2.mode === "none") {
|
|
2265
2907
|
return setLayerVisibility(layer, "none");
|
|
2266
2908
|
}
|
|
2267
2909
|
const nextLayer = setLayerVisibility(layer, "visible");
|
|
2268
2910
|
const layout = isRecord3(nextLayer.layout) ? { ...nextLayer.layout } : {};
|
|
2911
|
+
const paint = isRecord3(nextLayer.paint) ? { ...nextLayer.paint } : {};
|
|
2269
2912
|
nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, poi2.minZoom);
|
|
2270
2913
|
if (poi2.categories && sourceLayerValue(nextLayer) === "poi") {
|
|
2271
|
-
nextLayer.filter = combineFilters(
|
|
2914
|
+
nextLayer.filter = combineFilters(
|
|
2915
|
+
nextLayer.filter,
|
|
2916
|
+
poiCategoryFilter(poi2.categories, poi2.classMapping)
|
|
2917
|
+
);
|
|
2918
|
+
}
|
|
2919
|
+
if (poi2.density && sourceLayerValue(nextLayer) === "poi") {
|
|
2920
|
+
const densityFilter = poiDensityFilter(poi2.density, poi2.classMapping);
|
|
2921
|
+
if (densityFilter) nextLayer.filter = combineFilters(nextLayer.filter, densityFilter);
|
|
2922
|
+
layout["symbol-sort-key"] = poiPriorityExpression(poi2.classMapping);
|
|
2272
2923
|
}
|
|
2273
2924
|
if (poi2.labels === "none") {
|
|
2274
2925
|
delete layout["text-field"];
|
|
@@ -2278,10 +2929,21 @@ function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMappi
|
|
|
2278
2929
|
if (poi2.icons === false) {
|
|
2279
2930
|
delete layout["icon-image"];
|
|
2280
2931
|
} else if ("icon-image" in layout || customIconSpriteId2) {
|
|
2281
|
-
layout["icon-image"] = poiIconImageExpression(
|
|
2282
|
-
|
|
2932
|
+
layout["icon-image"] = poiIconImageExpression(
|
|
2933
|
+
customIconSpriteId2,
|
|
2934
|
+
customIconMapping,
|
|
2935
|
+
poi2.classMapping
|
|
2936
|
+
);
|
|
2937
|
+
layout["icon-optional"] = !poi2.placement.coupleIconAndLabel;
|
|
2938
|
+
layout["icon-padding"] = poi2.placement.iconPadding;
|
|
2939
|
+
}
|
|
2940
|
+
layout["text-padding"] = poi2.placement.textPadding;
|
|
2941
|
+
if (poi2.placement.coupleIconAndLabel) layout["text-optional"] = false;
|
|
2942
|
+
if (poi2.color === "category" && "text-color" in paint) {
|
|
2943
|
+
paint["text-color"] = poiCategoryColorExpression(colors, poi2.classMapping);
|
|
2283
2944
|
}
|
|
2284
2945
|
nextLayer.layout = layout;
|
|
2946
|
+
nextLayer.paint = paint;
|
|
2285
2947
|
return nextLayer;
|
|
2286
2948
|
}
|
|
2287
2949
|
function labelGroupDetail(labels2, group) {
|
|
@@ -4907,8 +5569,8 @@ var style_default = {
|
|
|
4907
5569
|
};
|
|
4908
5570
|
|
|
4909
5571
|
// src/templates/osm-bright/theme.ts
|
|
4910
|
-
function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
4911
|
-
if (!theme.custom && theme.name === "light") {
|
|
5572
|
+
function applyOsmBrightTheme(layers, colors, theme, typography, typographyOverridden = theme.typographyOverridden) {
|
|
5573
|
+
if (!theme.custom && theme.name === "light" && !typographyOverridden) {
|
|
4912
5574
|
return layers;
|
|
4913
5575
|
}
|
|
4914
5576
|
return layers.map((layer) => {
|
|
@@ -4948,8 +5610,8 @@ function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
|
4948
5610
|
paint["text-halo-color"] = colors.labels.halo;
|
|
4949
5611
|
paintChanged = true;
|
|
4950
5612
|
}
|
|
4951
|
-
if (
|
|
4952
|
-
layout["text-font"] = textFont(typography);
|
|
5613
|
+
if (typographyOverridden && "text-font" in layout) {
|
|
5614
|
+
layout["text-font"] = textFont(typography, typographyDomain(id, sourceLayer));
|
|
4953
5615
|
layoutChanged = true;
|
|
4954
5616
|
}
|
|
4955
5617
|
}
|
|
@@ -4962,6 +5624,16 @@ function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
|
4962
5624
|
return nextLayer;
|
|
4963
5625
|
});
|
|
4964
5626
|
}
|
|
5627
|
+
function typographyDomain(id, sourceLayer) {
|
|
5628
|
+
if (sourceLayer === "poi" || id.startsWith("poi-")) return "poi";
|
|
5629
|
+
if (sourceLayer === "transportation_name" || id.includes("highway-name") || id.includes("highway-shield")) {
|
|
5630
|
+
return "roads";
|
|
5631
|
+
}
|
|
5632
|
+
if (sourceLayer === "water_name" || sourceLayer === "waterway" || id.includes("water-name")) {
|
|
5633
|
+
return "water";
|
|
5634
|
+
}
|
|
5635
|
+
return "places";
|
|
5636
|
+
}
|
|
4965
5637
|
function osmBrightFillColor(id, sourceLayer, colors, theme) {
|
|
4966
5638
|
if (sourceLayer === "water" || id.includes("water")) {
|
|
4967
5639
|
return hasBaseColor(theme, ["water"]) || hasNestedModuleColor(theme, "hydro", "water") ? colors.hydro.water : void 0;
|
|
@@ -5181,16 +5853,22 @@ function createOsmBrightStyle(options) {
|
|
|
5181
5853
|
layers,
|
|
5182
5854
|
options.colors,
|
|
5183
5855
|
options.theme,
|
|
5184
|
-
options.typography
|
|
5856
|
+
options.typography,
|
|
5857
|
+
options.typographyOverridden
|
|
5185
5858
|
);
|
|
5186
5859
|
const controlledLayers = applyOsmBrightControls(themedLayers, {
|
|
5860
|
+
colors: options.colors,
|
|
5187
5861
|
customIconMapping: options.customIconMapping,
|
|
5188
5862
|
customIconSpriteId: options.customIconSpriteId,
|
|
5189
5863
|
labels: options.labelsModule,
|
|
5190
5864
|
poi: options.poiModule,
|
|
5191
5865
|
roads: options.roadsModule
|
|
5192
5866
|
});
|
|
5193
|
-
const
|
|
5867
|
+
const semanticLayers = options.roadsModule?.oneWayMarkers ? insertBeforeRoadLabels(
|
|
5868
|
+
controlledLayers,
|
|
5869
|
+
oneWayMarkerLayer(options.sourceId, "transportation", options.colors, options.typography)
|
|
5870
|
+
) : controlledLayers;
|
|
5871
|
+
const resolvedLayers = options.terrain ? withTerrainLayers(semanticLayers, options.terrain) : semanticLayers;
|
|
5194
5872
|
return {
|
|
5195
5873
|
...template,
|
|
5196
5874
|
name: options.name,
|
|
@@ -5220,6 +5898,13 @@ function createOsmBrightStyle(options) {
|
|
|
5220
5898
|
}
|
|
5221
5899
|
};
|
|
5222
5900
|
}
|
|
5901
|
+
function insertBeforeRoadLabels(layers, layer) {
|
|
5902
|
+
const index = layers.findIndex(
|
|
5903
|
+
(candidate) => candidate.type === "symbol" && candidate["source-layer"] === "transportation_name"
|
|
5904
|
+
);
|
|
5905
|
+
if (index < 0) return [...layers, layer];
|
|
5906
|
+
return [...layers.slice(0, index), layer, ...layers.slice(index)];
|
|
5907
|
+
}
|
|
5223
5908
|
function cloneJson(value) {
|
|
5224
5909
|
return JSON.parse(JSON.stringify(value));
|
|
5225
5910
|
}
|
|
@@ -5234,7 +5919,9 @@ function remapOsmBrightSource(layer, sourceId) {
|
|
|
5234
5919
|
}
|
|
5235
5920
|
|
|
5236
5921
|
// src/compiler.ts
|
|
5922
|
+
var tileflowHostedNotoSansWeights = ["regular", "bold"];
|
|
5237
5923
|
var defaultSourceLayers = {
|
|
5924
|
+
landcover: "landcover",
|
|
5238
5925
|
landuse: "landuse",
|
|
5239
5926
|
park: "park",
|
|
5240
5927
|
water: "water",
|
|
@@ -5300,6 +5987,15 @@ function createStyle(input = {}, options = {}) {
|
|
|
5300
5987
|
theme.preset.roads ?? defaultConfig.roads
|
|
5301
5988
|
);
|
|
5302
5989
|
const { mode: resolvedRoadMode, ...resolvedRoadModule } = resolvedRoads;
|
|
5990
|
+
const resolvedRoadModuleMetadata = {
|
|
5991
|
+
detail: resolvedRoadModule.detail,
|
|
5992
|
+
extras: resolvedRoadModule.extras,
|
|
5993
|
+
hierarchy: resolvedRoadModule.hierarchy,
|
|
5994
|
+
outline: resolvedRoadModule.outline,
|
|
5995
|
+
weight: resolvedRoadModule.weight,
|
|
5996
|
+
...roadModule?.oneWayMarkers !== void 0 ? { oneWayMarkers: resolvedRoadModule.oneWayMarkers } : {},
|
|
5997
|
+
...roadModule?.widthScale ? { widthScale: resolvedRoadModule.widthScale } : {}
|
|
5998
|
+
};
|
|
5303
5999
|
const resolvedLabels = resolveLabels(
|
|
5304
6000
|
config.labels,
|
|
5305
6001
|
labelsModule,
|
|
@@ -5307,14 +6003,18 @@ function createStyle(input = {}, options = {}) {
|
|
|
5307
6003
|
);
|
|
5308
6004
|
const { mode: resolvedLabelsMode, ...resolvedLabelsModule } = resolvedLabels;
|
|
5309
6005
|
const resolvedPoi = resolvePoi(config.poi, poiModule, theme.preset.poi ?? defaultConfig.poi);
|
|
6006
|
+
const { mode: poi2, ...resolvedPoiModule } = resolvedPoi;
|
|
5310
6007
|
const labels2 = resolvedLabelsMode;
|
|
5311
|
-
const poi2 = resolvedPoi.mode;
|
|
5312
6008
|
const buildings = config.buildings ?? "flat";
|
|
5313
6009
|
const colors = resolveColors(config.colors, theme.colors, {}, theme.modules);
|
|
5314
6010
|
const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
|
|
5315
6011
|
const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
|
|
5316
6012
|
const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
|
|
5317
|
-
const
|
|
6013
|
+
const tilesetVersion = config.tiles?.version ?? basemap.version;
|
|
6014
|
+
const tilesUrl = withArchiveVersion(
|
|
6015
|
+
config.tiles?.url ?? basemap.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`,
|
|
6016
|
+
tilesetVersion
|
|
6017
|
+
);
|
|
5318
6018
|
const typography = resolveTypography(config.typography, theme.typography);
|
|
5319
6019
|
const terrain = resolveTerrain(config.terrain, tileBaseUrl);
|
|
5320
6020
|
const rendererPreference = config.renderer ?? "auto";
|
|
@@ -5322,10 +6022,11 @@ function createStyle(input = {}, options = {}) {
|
|
|
5322
6022
|
const useOsmBrightTemplate = rendererPreference === "osm-bright" || rendererPreference === "auto" && canUseOsmBrightTemplate;
|
|
5323
6023
|
if (rendererPreference === "osm-bright" && !canUseOsmBrightTemplate) {
|
|
5324
6024
|
throw new Error(
|
|
5325
|
-
'Tileflow renderer "osm-bright" is not compatible with this config. Use renderer "auto" or "generated", or remove map-level color overrides,
|
|
6025
|
+
'Tileflow renderer "osm-bright" is not compatible with this config. Use renderer "auto" or "generated", or remove map-level color overrides, density, legacy roads/buildings, custom source layers, and unsupported modules.'
|
|
5326
6026
|
);
|
|
5327
6027
|
}
|
|
5328
6028
|
const glyphs = config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
|
|
6029
|
+
validateHostedTypography(typography, glyphs, tileBaseUrl);
|
|
5329
6030
|
const iconSet = resolveIconSet(config.icons, options.iconSets);
|
|
5330
6031
|
const iconSprite = iconSet?.sprite;
|
|
5331
6032
|
const basemapSprite = basemap.sprite ?? (basemap.type === "osm" ? `${tileBaseUrl}/sprites/osm-bright/sprite` : void 0);
|
|
@@ -5338,32 +6039,38 @@ function createStyle(input = {}, options = {}) {
|
|
|
5338
6039
|
"tileflow:themeMode": theme.mode,
|
|
5339
6040
|
"tileflow:density": density,
|
|
5340
6041
|
"tileflow:roads": resolvedRoadMode,
|
|
5341
|
-
"tileflow:roadsModule":
|
|
6042
|
+
...roadModule ? { "tileflow:roadsModule": resolvedRoadModuleMetadata } : {},
|
|
5342
6043
|
"tileflow:labels": labels2,
|
|
5343
|
-
"tileflow:labelsModule":
|
|
6044
|
+
...labelsModule ? { "tileflow:labelsModule": resolvedLabelsModule } : {},
|
|
5344
6045
|
"tileflow:poi": poi2,
|
|
5345
|
-
"tileflow:poiModule":
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
6046
|
+
...poiModule ? { "tileflow:poiModule": resolvedPoiModule } : {},
|
|
6047
|
+
...iconSet ? {
|
|
6048
|
+
"tileflow:icons": {
|
|
6049
|
+
...customIconSpriteName ? { customSpriteId: customIconSpriteName } : {},
|
|
6050
|
+
...iconSet.mapping ? { mapping: iconSet.mapping } : {},
|
|
6051
|
+
...iconSprite ? { sprite: iconSprite } : {}
|
|
6052
|
+
}
|
|
6053
|
+
} : {},
|
|
5351
6054
|
"tileflow:buildings": buildings,
|
|
6055
|
+
...config.buildingStyle ? { "tileflow:buildingStyle": config.buildingStyle } : {},
|
|
5352
6056
|
"tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
|
|
5353
6057
|
"tileflow:themeModules": Object.keys(theme.moduleOverrides),
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
6058
|
+
...terrain ? {
|
|
6059
|
+
"tileflow:terrain": {
|
|
6060
|
+
encoding: terrain.source.encoding,
|
|
6061
|
+
mode: terrain.mode,
|
|
6062
|
+
sourceId: terrain.sourceId
|
|
6063
|
+
}
|
|
6064
|
+
} : {},
|
|
5359
6065
|
"tileflow:renderer": useOsmBrightTemplate ? "osm-bright" : "generated",
|
|
5360
6066
|
"tileflow:rendererPreference": rendererPreference,
|
|
5361
|
-
"tileflow:typography": typography,
|
|
6067
|
+
"tileflow:typography": typographyMetadata(typography),
|
|
5362
6068
|
"tileflow:colors": colors,
|
|
5363
6069
|
"tileflow:tileBaseUrl": tileBaseUrl,
|
|
5364
6070
|
"tileflow:tileset": tileset,
|
|
6071
|
+
...tilesetVersion ? { "tileflow:tilesetVersion": tilesetVersion } : {},
|
|
5365
6072
|
"tileflow:tiles": "tileflow",
|
|
5366
|
-
"tileflow:view": config.view
|
|
6073
|
+
...config.view ? { "tileflow:view": config.view } : {}
|
|
5367
6074
|
};
|
|
5368
6075
|
if (useOsmBrightTemplate) {
|
|
5369
6076
|
const style2 = createOsmBrightStyle({
|
|
@@ -5378,6 +6085,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5378
6085
|
theme,
|
|
5379
6086
|
tilesUrl,
|
|
5380
6087
|
typography,
|
|
6088
|
+
typographyOverridden: Boolean(config.typography) || theme.typographyOverridden,
|
|
5381
6089
|
labelsModule: resolvedLabels,
|
|
5382
6090
|
poi: poi2,
|
|
5383
6091
|
poiModule: resolvedPoi,
|
|
@@ -5400,6 +6108,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5400
6108
|
);
|
|
5401
6109
|
const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
|
|
5402
6110
|
buildings,
|
|
6111
|
+
buildingStyle: config.buildingStyle,
|
|
5403
6112
|
density,
|
|
5404
6113
|
labels: resolvedLabels,
|
|
5405
6114
|
poi: resolvedPoi,
|
|
@@ -5437,7 +6146,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5437
6146
|
return style;
|
|
5438
6147
|
}
|
|
5439
6148
|
function createStyleFromProject(projectConfig, mapName, options = {}) {
|
|
5440
|
-
const config = projectConfig.maps[mapName];
|
|
6149
|
+
const config = Object.hasOwn(projectConfig.maps, mapName) ? projectConfig.maps[mapName] : void 0;
|
|
5441
6150
|
if (!config) {
|
|
5442
6151
|
throw new Error(`Unknown Tileflow map: ${mapName}`);
|
|
5443
6152
|
}
|
|
@@ -5456,11 +6165,13 @@ function createStyleFromProject(projectConfig, mapName, options = {}) {
|
|
|
5456
6165
|
...baseBasemap,
|
|
5457
6166
|
tileset: resolvedTileset,
|
|
5458
6167
|
attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
|
|
6168
|
+
version: baseBasemap.version ?? tilesetConfig?.version,
|
|
5459
6169
|
...resolvedBasemapSourceLayers ? { sourceLayers: resolvedBasemapSourceLayers } : {}
|
|
5460
6170
|
},
|
|
5461
6171
|
tiles: {
|
|
5462
6172
|
...tileOptions,
|
|
5463
6173
|
tileset: resolvedTileset,
|
|
6174
|
+
version: tileOptions.version ?? baseBasemap.version ?? tilesetConfig?.version,
|
|
5464
6175
|
...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
|
|
5465
6176
|
}
|
|
5466
6177
|
},
|
|
@@ -5499,7 +6210,7 @@ function resolveSourceLayers(mapLayers, tilesetLayers) {
|
|
|
5499
6210
|
}
|
|
5500
6211
|
function isOsmBrightTemplateCompatible(config, basemap, theme) {
|
|
5501
6212
|
const templateTheme = theme.custom || theme.name === "light" || theme.name === "dark";
|
|
5502
|
-
return basemap.type === "osm" && templateTheme && !hasEffectiveColorOverrides(config.colors) && !config.
|
|
6213
|
+
return basemap.type === "osm" && templateTheme && !hasEffectiveColorOverrides(config.colors) && !config.density && !config.roads && !config.buildings && !config.buildingStyle && !config.modules?.some(
|
|
5503
6214
|
(moduleConfig) => moduleConfig.type !== "styleOverride" && moduleConfig.type !== "labels" && moduleConfig.type !== "poi" && moduleConfig.type !== "roads"
|
|
5504
6215
|
) && !basemap.sourceLayers && !config.tiles?.sourceLayers;
|
|
5505
6216
|
}
|
|
@@ -5517,6 +6228,23 @@ function hasEffectiveColorOverrides(colors) {
|
|
|
5517
6228
|
return typeof value === "string";
|
|
5518
6229
|
});
|
|
5519
6230
|
}
|
|
6231
|
+
function typographyMetadata(typography) {
|
|
6232
|
+
const base = {
|
|
6233
|
+
font: typography.font,
|
|
6234
|
+
fontFamily: typography.fontFamily,
|
|
6235
|
+
weight: typography.weight
|
|
6236
|
+
};
|
|
6237
|
+
return {
|
|
6238
|
+
...base,
|
|
6239
|
+
...sameTypographyStyle(typography.places, base) ? {} : { places: typography.places },
|
|
6240
|
+
...sameTypographyStyle(typography.roads, base) ? {} : { roads: typography.roads },
|
|
6241
|
+
...sameTypographyStyle(typography.water, base) ? {} : { water: typography.water },
|
|
6242
|
+
...sameTypographyStyle(typography.poi, base) ? {} : { poi: typography.poi }
|
|
6243
|
+
};
|
|
6244
|
+
}
|
|
6245
|
+
function sameTypographyStyle(left, right) {
|
|
6246
|
+
return left.font === right.font && left.fontFamily === right.fontFamily && left.weight === right.weight;
|
|
6247
|
+
}
|
|
5520
6248
|
function isRecord5(value) {
|
|
5521
6249
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5522
6250
|
}
|
|
@@ -5529,6 +6257,12 @@ function normalizeStyleBaseUrl(value) {
|
|
|
5529
6257
|
function normalizeUrl(value) {
|
|
5530
6258
|
return value.replace(/\/+$/, "");
|
|
5531
6259
|
}
|
|
6260
|
+
function withArchiveVersion(value, version) {
|
|
6261
|
+
if (!version) return value;
|
|
6262
|
+
const url = new URL(value);
|
|
6263
|
+
url.searchParams.set("archiveVersion", version);
|
|
6264
|
+
return url.toString();
|
|
6265
|
+
}
|
|
5532
6266
|
function resolveIconSet(icons, iconSets, path = []) {
|
|
5533
6267
|
if (!icons) {
|
|
5534
6268
|
return void 0;
|
|
@@ -5571,12 +6305,455 @@ function resolveSprite(basemapSprite, iconSprite) {
|
|
|
5571
6305
|
function isSpriteReference(value) {
|
|
5572
6306
|
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:");
|
|
5573
6307
|
}
|
|
6308
|
+
function validateHostedTypography(typography, glyphs, tileBaseUrl) {
|
|
6309
|
+
const hostedGlyphs = `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
|
|
6310
|
+
if (glyphs !== hostedGlyphs) return;
|
|
6311
|
+
for (const [domain, style] of [
|
|
6312
|
+
["default", typography],
|
|
6313
|
+
["places", typography.places],
|
|
6314
|
+
["roads", typography.roads],
|
|
6315
|
+
["water", typography.water],
|
|
6316
|
+
["poi", typography.poi]
|
|
6317
|
+
]) {
|
|
6318
|
+
if (style.font === "Noto Sans" && !tileflowHostedNotoSansWeights.includes(style.weight)) {
|
|
6319
|
+
throw new Error(
|
|
6320
|
+
`Tileflow-hosted Noto Sans does not provide the ${style.weight} weight for ${domain} typography. Use regular or bold, or configure a custom glyphs URL that provides this font stack.`
|
|
6321
|
+
);
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
5574
6325
|
|
|
5575
6326
|
// src/config.ts
|
|
5576
6327
|
function defineTileflow(config) {
|
|
5577
6328
|
return config;
|
|
5578
6329
|
}
|
|
5579
6330
|
|
|
6331
|
+
// src/icon-package.ts
|
|
6332
|
+
import { z as z3 } from "zod";
|
|
6333
|
+
var tileflowIconPackageFileNames = [
|
|
6334
|
+
"sprite.json",
|
|
6335
|
+
"sprite.png",
|
|
6336
|
+
"sprite@2x.json",
|
|
6337
|
+
"sprite@2x.png"
|
|
6338
|
+
];
|
|
6339
|
+
var tileflowHostedIconIdPattern = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
6340
|
+
var tileflowIconPackageLimits = {
|
|
6341
|
+
decodeConcurrency: 4,
|
|
6342
|
+
maxAtlasDimension: 2048,
|
|
6343
|
+
maxDecodedPixelsPerIcon: 4194304,
|
|
6344
|
+
maxGeneratedFileBytes: 4 * 1024 * 1024,
|
|
6345
|
+
maxGeneratedPackageBytes: 8 * 1024 * 1024,
|
|
6346
|
+
maxIconCount: 256,
|
|
6347
|
+
maxSourceBytes: 32 * 1024 * 1024,
|
|
6348
|
+
maxSourceFileBytes: 1024 * 1024
|
|
6349
|
+
};
|
|
6350
|
+
var tileflowHostedAlphaCompatibility = {
|
|
6351
|
+
iconPackages: {
|
|
6352
|
+
maxGeneratedBytesPerProject: 64 * 1024 * 1024,
|
|
6353
|
+
maxRetainedPerProject: 24
|
|
6354
|
+
},
|
|
6355
|
+
maxMapsPerDeploy: 1
|
|
6356
|
+
};
|
|
6357
|
+
var sha256Schema = z3.string().regex(/^[a-f0-9]{64}$/);
|
|
6358
|
+
var tileflowIconPackageContentHashSchema = sha256Schema;
|
|
6359
|
+
var tileflowHostedIconIdSchema = z3.string().regex(tileflowHostedIconIdPattern);
|
|
6360
|
+
var tileflowRenderedIconManifestEntrySchema = z3.object({
|
|
6361
|
+
name: z3.string().min(1).max(256),
|
|
6362
|
+
pixelSha256: z3.object({
|
|
6363
|
+
oneX: sha256Schema,
|
|
6364
|
+
twoX: sha256Schema
|
|
6365
|
+
}).strict()
|
|
6366
|
+
}).strict();
|
|
6367
|
+
var tileflowIconPackageLabelSchema = z3.string().superRefine((value, context) => {
|
|
6368
|
+
if (value !== value.trim() || value.length === 0) {
|
|
6369
|
+
context.addIssue({
|
|
6370
|
+
code: "custom",
|
|
6371
|
+
message: "Icon package labels must be non-empty and have no surrounding whitespace"
|
|
6372
|
+
});
|
|
6373
|
+
}
|
|
6374
|
+
if (Array.from(value).length > 64) {
|
|
6375
|
+
context.addIssue({
|
|
6376
|
+
code: "custom",
|
|
6377
|
+
message: "Icon package labels must contain at most 64 Unicode characters"
|
|
6378
|
+
});
|
|
6379
|
+
}
|
|
6380
|
+
if (!isWellFormedUnicode(value) || containsControlCharacter(value)) {
|
|
6381
|
+
context.addIssue({
|
|
6382
|
+
code: "custom",
|
|
6383
|
+
message: "Icon package labels must contain printable, well-formed Unicode"
|
|
6384
|
+
});
|
|
6385
|
+
}
|
|
6386
|
+
});
|
|
6387
|
+
var spriteDimensionsSchema = z3.object({
|
|
6388
|
+
height: z3.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension),
|
|
6389
|
+
pixelRatio: z3.union([z3.literal(1), z3.literal(2)]),
|
|
6390
|
+
width: z3.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension)
|
|
6391
|
+
}).strict();
|
|
6392
|
+
function iconPackageFileSchema(name, contentType) {
|
|
6393
|
+
return z3.object({
|
|
6394
|
+
byteLength: z3.number().int().nonnegative().max(tileflowIconPackageLimits.maxGeneratedFileBytes),
|
|
6395
|
+
contentType: z3.literal(contentType),
|
|
6396
|
+
name: z3.literal(name),
|
|
6397
|
+
sha256: sha256Schema
|
|
6398
|
+
}).strict();
|
|
6399
|
+
}
|
|
6400
|
+
var tileflowIconPackageManifestSchema = z3.object({
|
|
6401
|
+
files: z3.tuple([
|
|
6402
|
+
iconPackageFileSchema("sprite.json", "application/json"),
|
|
6403
|
+
iconPackageFileSchema("sprite.png", "image/png"),
|
|
6404
|
+
iconPackageFileSchema("sprite@2x.json", "application/json"),
|
|
6405
|
+
iconPackageFileSchema("sprite@2x.png", "image/png")
|
|
6406
|
+
]),
|
|
6407
|
+
format: z3.literal("tileflow-icon-package-v1"),
|
|
6408
|
+
iconNames: z3.array(z3.string().min(1).max(256)).min(1).max(tileflowIconPackageLimits.maxIconCount),
|
|
6409
|
+
renderedIcons: z3.array(tileflowRenderedIconManifestEntrySchema).min(1).max(tileflowIconPackageLimits.maxIconCount),
|
|
6410
|
+
sprites: z3.object({
|
|
6411
|
+
oneX: spriteDimensionsSchema.extend({ pixelRatio: z3.literal(1) }),
|
|
6412
|
+
twoX: spriteDimensionsSchema.extend({ pixelRatio: z3.literal(2) })
|
|
6413
|
+
}).strict()
|
|
6414
|
+
}).strict().superRefine((manifest, context) => {
|
|
6415
|
+
const sortedNames = [...manifest.iconNames].sort(compareCodeUnits);
|
|
6416
|
+
if (sortedNames.some((name, index) => name !== manifest.iconNames[index]) || new Set(manifest.iconNames).size !== manifest.iconNames.length) {
|
|
6417
|
+
context.addIssue({
|
|
6418
|
+
code: "custom",
|
|
6419
|
+
message: "Icon names must be unique and sorted by Unicode code unit",
|
|
6420
|
+
path: ["iconNames"]
|
|
6421
|
+
});
|
|
6422
|
+
}
|
|
6423
|
+
if (manifest.renderedIcons.length !== manifest.iconNames.length || manifest.renderedIcons.some((entry, index) => entry.name !== manifest.iconNames[index])) {
|
|
6424
|
+
context.addIssue({
|
|
6425
|
+
code: "custom",
|
|
6426
|
+
message: "Rendered icon entries must exactly match iconNames in sorted order",
|
|
6427
|
+
path: ["renderedIcons"]
|
|
6428
|
+
});
|
|
6429
|
+
}
|
|
6430
|
+
const totalBytes = manifest.files.reduce((total, file) => total + file.byteLength, 0);
|
|
6431
|
+
if (totalBytes > tileflowIconPackageLimits.maxGeneratedPackageBytes) {
|
|
6432
|
+
context.addIssue({
|
|
6433
|
+
code: "custom",
|
|
6434
|
+
message: `Generated icon package exceeds ${tileflowIconPackageLimits.maxGeneratedPackageBytes} bytes`,
|
|
6435
|
+
path: ["files"]
|
|
6436
|
+
});
|
|
6437
|
+
}
|
|
6438
|
+
if (manifest.sprites.twoX.width !== manifest.sprites.oneX.width * 2 || manifest.sprites.twoX.height !== manifest.sprites.oneX.height * 2) {
|
|
6439
|
+
context.addIssue({
|
|
6440
|
+
code: "custom",
|
|
6441
|
+
message: "The @2x sprite dimensions must be exactly twice the 1x dimensions",
|
|
6442
|
+
path: ["sprites", "twoX"]
|
|
6443
|
+
});
|
|
6444
|
+
}
|
|
6445
|
+
});
|
|
6446
|
+
function compareCodeUnits(left, right) {
|
|
6447
|
+
if (left < right) {
|
|
6448
|
+
return -1;
|
|
6449
|
+
}
|
|
6450
|
+
if (left > right) {
|
|
6451
|
+
return 1;
|
|
6452
|
+
}
|
|
6453
|
+
return 0;
|
|
6454
|
+
}
|
|
6455
|
+
function serializeCanonicalJson(value) {
|
|
6456
|
+
return JSON.stringify(sortJsonValue(value));
|
|
6457
|
+
}
|
|
6458
|
+
function serializeTileflowIconPackageManifest(input) {
|
|
6459
|
+
const manifest = tileflowIconPackageManifestSchema.parse(input);
|
|
6460
|
+
return serializeCanonicalJson(manifest);
|
|
6461
|
+
}
|
|
6462
|
+
async function sha256Hex(input) {
|
|
6463
|
+
const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
6464
|
+
const digestInput = new Uint8Array(bytes.byteLength);
|
|
6465
|
+
digestInput.set(bytes);
|
|
6466
|
+
const digest = await crypto.subtle.digest("SHA-256", digestInput.buffer);
|
|
6467
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6468
|
+
}
|
|
6469
|
+
async function hashTileflowIconPackageManifest(input) {
|
|
6470
|
+
return sha256Hex(serializeTileflowIconPackageManifest(input));
|
|
6471
|
+
}
|
|
6472
|
+
async function hashTileflowRenderedIconPixels(input) {
|
|
6473
|
+
if (!Number.isSafeInteger(input.width) || !Number.isSafeInteger(input.height) || input.width <= 0 || input.height <= 0 || input.width > tileflowIconPackageLimits.maxAtlasDimension || input.height > tileflowIconPackageLimits.maxAtlasDimension) {
|
|
6474
|
+
throw new Error("Rendered icon dimensions are invalid");
|
|
6475
|
+
}
|
|
6476
|
+
if (input.pixelRatio !== 1 && input.pixelRatio !== 2) {
|
|
6477
|
+
throw new Error("Rendered icon pixel ratio must be 1 or 2");
|
|
6478
|
+
}
|
|
6479
|
+
const pixelCount = input.width * input.height;
|
|
6480
|
+
const expectedBytes = pixelCount * 4;
|
|
6481
|
+
if (pixelCount > tileflowIconPackageLimits.maxDecodedPixelsPerIcon || input.rgba.byteLength !== expectedBytes) {
|
|
6482
|
+
throw new Error(`Rendered icon RGBA must contain exactly ${expectedBytes} bytes`);
|
|
6483
|
+
}
|
|
6484
|
+
const normalized = new Uint8Array(input.rgba);
|
|
6485
|
+
for (let offset = 0; offset < normalized.byteLength; offset += 4) {
|
|
6486
|
+
if (normalized[offset + 3] === 0) {
|
|
6487
|
+
normalized[offset] = 0;
|
|
6488
|
+
normalized[offset + 1] = 0;
|
|
6489
|
+
normalized[offset + 2] = 0;
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
const domain = new TextEncoder().encode("tileflow-rendered-icon-pixels-v1\0");
|
|
6493
|
+
const frame = new Uint8Array(domain.byteLength + 9 + normalized.byteLength);
|
|
6494
|
+
frame.set(domain);
|
|
6495
|
+
frame[domain.byteLength] = input.pixelRatio;
|
|
6496
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
6497
|
+
view.setUint32(domain.byteLength + 1, input.width);
|
|
6498
|
+
view.setUint32(domain.byteLength + 5, input.height);
|
|
6499
|
+
frame.set(normalized, domain.byteLength + 9);
|
|
6500
|
+
return sha256Hex(frame);
|
|
6501
|
+
}
|
|
6502
|
+
function sortJsonValue(value) {
|
|
6503
|
+
if (Array.isArray(value)) {
|
|
6504
|
+
return value.map(sortJsonValue);
|
|
6505
|
+
}
|
|
6506
|
+
if (value !== null && typeof value === "object") {
|
|
6507
|
+
return Object.fromEntries(
|
|
6508
|
+
Object.entries(value).sort(([left], [right]) => compareCodeUnits(left, right)).map(([key, child]) => [key, sortJsonValue(child)])
|
|
6509
|
+
);
|
|
6510
|
+
}
|
|
6511
|
+
return value;
|
|
6512
|
+
}
|
|
6513
|
+
function isWellFormedUnicode(value) {
|
|
6514
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
6515
|
+
const code = value.charCodeAt(index);
|
|
6516
|
+
if (code >= 55296 && code <= 56319) {
|
|
6517
|
+
const next = value.charCodeAt(index + 1);
|
|
6518
|
+
if (next < 56320 || next > 57343) {
|
|
6519
|
+
return false;
|
|
6520
|
+
}
|
|
6521
|
+
index += 1;
|
|
6522
|
+
continue;
|
|
6523
|
+
}
|
|
6524
|
+
if (code >= 56320 && code <= 57343) {
|
|
6525
|
+
return false;
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
return true;
|
|
6529
|
+
}
|
|
6530
|
+
function containsControlCharacter(value) {
|
|
6531
|
+
return Array.from(value).some((character) => {
|
|
6532
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
6533
|
+
return codePoint < 32 || codePoint >= 127 && codePoint <= 159;
|
|
6534
|
+
});
|
|
6535
|
+
}
|
|
6536
|
+
|
|
6537
|
+
// src/icon-diff.ts
|
|
6538
|
+
function diffTileflowIconPackageManifests(before, after) {
|
|
6539
|
+
const beforeIcons = new Map(
|
|
6540
|
+
(before?.renderedIcons ?? []).map((entry) => [entry.name, entry.pixelSha256])
|
|
6541
|
+
);
|
|
6542
|
+
const afterIcons = new Map(
|
|
6543
|
+
(after?.renderedIcons ?? []).map((entry) => [entry.name, entry.pixelSha256])
|
|
6544
|
+
);
|
|
6545
|
+
const names = [.../* @__PURE__ */ new Set([...beforeIcons.keys(), ...afterIcons.keys()])].sort(compareCodeUnits);
|
|
6546
|
+
const added = [];
|
|
6547
|
+
const modified = [];
|
|
6548
|
+
const removed = [];
|
|
6549
|
+
let unchangedCount = 0;
|
|
6550
|
+
for (const name of names) {
|
|
6551
|
+
const previous = beforeIcons.get(name);
|
|
6552
|
+
const proposed = afterIcons.get(name);
|
|
6553
|
+
if (!previous) {
|
|
6554
|
+
added.push(name);
|
|
6555
|
+
} else if (!proposed) {
|
|
6556
|
+
removed.push(name);
|
|
6557
|
+
} else if (previous.oneX !== proposed.oneX || previous.twoX !== proposed.twoX) {
|
|
6558
|
+
modified.push(name);
|
|
6559
|
+
} else {
|
|
6560
|
+
unchangedCount += 1;
|
|
6561
|
+
}
|
|
6562
|
+
}
|
|
6563
|
+
return {
|
|
6564
|
+
added,
|
|
6565
|
+
afterBytes: packageBytes(after),
|
|
6566
|
+
beforeBytes: packageBytes(before),
|
|
6567
|
+
modified,
|
|
6568
|
+
removed,
|
|
6569
|
+
unchangedCount
|
|
6570
|
+
};
|
|
6571
|
+
}
|
|
6572
|
+
function diffTileflowIconMappings(before, after) {
|
|
6573
|
+
const previous = before ?? {};
|
|
6574
|
+
const proposed = after ?? {};
|
|
6575
|
+
const keys = [.../* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(proposed)])].sort(
|
|
6576
|
+
compareCodeUnits
|
|
6577
|
+
);
|
|
6578
|
+
const result = { added: [], changed: [], removed: [] };
|
|
6579
|
+
for (const key of keys) {
|
|
6580
|
+
const beforeTarget = previous[key];
|
|
6581
|
+
const afterTarget = proposed[key];
|
|
6582
|
+
if (beforeTarget === void 0 && afterTarget !== void 0) {
|
|
6583
|
+
result.added.push({ after: afterTarget, key });
|
|
6584
|
+
} else if (beforeTarget !== void 0 && afterTarget === void 0) {
|
|
6585
|
+
result.removed.push({ before: beforeTarget, key });
|
|
6586
|
+
} else if (beforeTarget !== afterTarget) {
|
|
6587
|
+
result.changed.push({ after: afterTarget, before: beforeTarget, key });
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
return result;
|
|
6591
|
+
}
|
|
6592
|
+
function inspectTileflowIconReferences(project, mapName, availableIconNames) {
|
|
6593
|
+
const map = Object.hasOwn(project.maps, mapName) ? project.maps[mapName] : void 0;
|
|
6594
|
+
if (!map) {
|
|
6595
|
+
throw new Error(`Unknown Tileflow map: ${mapName}`);
|
|
6596
|
+
}
|
|
6597
|
+
const available = new Set(availableIconNames);
|
|
6598
|
+
const dangling = [];
|
|
6599
|
+
const unanalyzable = [];
|
|
6600
|
+
const mapping = resolveMappingReferences(project, mapName, map.icons);
|
|
6601
|
+
for (const reference of mapping.values()) {
|
|
6602
|
+
if (!available.has(reference.iconName)) {
|
|
6603
|
+
dangling.push({ ...reference, kind: "mapping" });
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
const layers = [];
|
|
6607
|
+
collectThemeLayers(project, mapName, map.theme, layers);
|
|
6608
|
+
if (map.layers) {
|
|
6609
|
+
layers.push({ layers: map.layers, path: `maps.${mapName}.layers` });
|
|
6610
|
+
}
|
|
6611
|
+
for (const [index, moduleConfig] of (map.modules ?? []).entries()) {
|
|
6612
|
+
if (moduleConfig.type === "styleOverride" && moduleConfig.layers) {
|
|
6613
|
+
layers.push({
|
|
6614
|
+
layers: moduleConfig.layers,
|
|
6615
|
+
path: `maps.${mapName}.modules.${index}.layers`
|
|
6616
|
+
});
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
for (const layerGroup of layers) {
|
|
6620
|
+
for (const [layerName, layer] of Object.entries(layerGroup.layers).sort(
|
|
6621
|
+
([left], [right]) => compareCodeUnits(left, right)
|
|
6622
|
+
)) {
|
|
6623
|
+
if (!layer.layout || !Object.prototype.hasOwnProperty.call(layer.layout, "icon-image")) {
|
|
6624
|
+
continue;
|
|
6625
|
+
}
|
|
6626
|
+
const path = `${layerGroup.path}.${layerName}.layout.icon-image`;
|
|
6627
|
+
const inspection = inspectIconImageValue(layer.layout["icon-image"]);
|
|
6628
|
+
for (const iconName of inspection.managedIconNames) {
|
|
6629
|
+
if (!available.has(iconName)) {
|
|
6630
|
+
dangling.push({ iconName, kind: "style-override-literal", path });
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
if (!inspection.complete) {
|
|
6634
|
+
unanalyzable.push({ kind: "style-override-expression", path });
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
const uniqueDangling = uniqueBy(
|
|
6639
|
+
dangling,
|
|
6640
|
+
(item) => `${item.kind}\0${item.path}\0${item.iconName}`
|
|
6641
|
+
).sort(compareReferences);
|
|
6642
|
+
const uniqueUnanalyzable = uniqueBy(unanalyzable, (item) => `${item.kind}\0${item.path}`).sort(
|
|
6643
|
+
(left, right) => compareCodeUnits(left.path, right.path)
|
|
6644
|
+
);
|
|
6645
|
+
return {
|
|
6646
|
+
analysisComplete: uniqueUnanalyzable.length === 0,
|
|
6647
|
+
dangling: uniqueDangling,
|
|
6648
|
+
unanalyzable: uniqueUnanalyzable
|
|
6649
|
+
};
|
|
6650
|
+
}
|
|
6651
|
+
function resolveTileflowIconMapping(project, mapName) {
|
|
6652
|
+
const map = Object.hasOwn(project.maps, mapName) ? project.maps[mapName] : void 0;
|
|
6653
|
+
if (!map) {
|
|
6654
|
+
throw new Error(`Unknown Tileflow map: ${mapName}`);
|
|
6655
|
+
}
|
|
6656
|
+
return Object.fromEntries(
|
|
6657
|
+
[...resolveMappingReferences(project, mapName, map.icons).entries()].sort(([left], [right]) => compareCodeUnits(left, right)).map(([key, reference]) => [key, reference.iconName])
|
|
6658
|
+
);
|
|
6659
|
+
}
|
|
6660
|
+
function packageBytes(manifest) {
|
|
6661
|
+
return manifest?.files.reduce((total, file) => total + file.byteLength, 0) ?? 0;
|
|
6662
|
+
}
|
|
6663
|
+
function resolveMappingReferences(project, mapName, icons) {
|
|
6664
|
+
const result = /* @__PURE__ */ new Map();
|
|
6665
|
+
function apply(value, path, rootPath) {
|
|
6666
|
+
if (!value) {
|
|
6667
|
+
return;
|
|
6668
|
+
}
|
|
6669
|
+
if (typeof value === "string") {
|
|
6670
|
+
const referenced = Object.hasOwn(project.icons ?? {}, value) ? project.icons?.[value] : void 0;
|
|
6671
|
+
if (!referenced) {
|
|
6672
|
+
return;
|
|
6673
|
+
}
|
|
6674
|
+
if (rootPath.includes(value)) {
|
|
6675
|
+
throw new Error(`Circular Tileflow icon set extends: ${[...rootPath, value].join(" -> ")}`);
|
|
6676
|
+
}
|
|
6677
|
+
apply(referenced, `icons.${value}`, [...rootPath, value]);
|
|
6678
|
+
return;
|
|
6679
|
+
}
|
|
6680
|
+
if (value.extends) {
|
|
6681
|
+
apply(value.extends, path, rootPath);
|
|
6682
|
+
}
|
|
6683
|
+
for (const [key, iconName] of Object.entries(value.mapping ?? {}).sort(
|
|
6684
|
+
([left], [right]) => compareCodeUnits(left, right)
|
|
6685
|
+
)) {
|
|
6686
|
+
result.set(key, { iconName, path: `${path}.mapping.${key}` });
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
apply(icons, `maps.${mapName}.icons`, []);
|
|
6690
|
+
return result;
|
|
6691
|
+
}
|
|
6692
|
+
function collectThemeLayers(project, mapName, theme, result) {
|
|
6693
|
+
function apply(value, path, rootPath) {
|
|
6694
|
+
if (!value) {
|
|
6695
|
+
return;
|
|
6696
|
+
}
|
|
6697
|
+
if (typeof value === "string") {
|
|
6698
|
+
const custom = Object.hasOwn(project.themes ?? {}, value) ? project.themes?.[value] : void 0;
|
|
6699
|
+
if (!custom) {
|
|
6700
|
+
return;
|
|
6701
|
+
}
|
|
6702
|
+
if (rootPath.includes(value)) {
|
|
6703
|
+
throw new Error(`Circular Tileflow theme extends: ${[...rootPath, value].join(" -> ")}`);
|
|
6704
|
+
}
|
|
6705
|
+
apply(custom, `themes.${value}`, [...rootPath, value]);
|
|
6706
|
+
return;
|
|
6707
|
+
}
|
|
6708
|
+
if (value.extends) {
|
|
6709
|
+
apply(value.extends, path, rootPath);
|
|
6710
|
+
}
|
|
6711
|
+
if (value.layers) {
|
|
6712
|
+
result.push({ layers: value.layers, path: `${path}.layers` });
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
apply(theme, `maps.${mapName}.theme`, []);
|
|
6716
|
+
}
|
|
6717
|
+
function inspectIconImageValue(value) {
|
|
6718
|
+
if (typeof value === "string") {
|
|
6719
|
+
const managed = managedIconName(value);
|
|
6720
|
+
return { complete: true, managedIconNames: managed ? [managed] : [] };
|
|
6721
|
+
}
|
|
6722
|
+
if (!Array.isArray(value)) {
|
|
6723
|
+
return { complete: false, managedIconNames: [] };
|
|
6724
|
+
}
|
|
6725
|
+
const managedIconNames = /* @__PURE__ */ new Set();
|
|
6726
|
+
collectManagedImageLiterals(value, managedIconNames);
|
|
6727
|
+
const isSingleLiteralImage = value.length === 2 && (value[0] === "image" || value[0] === "literal") && typeof value[1] === "string";
|
|
6728
|
+
return {
|
|
6729
|
+
complete: isSingleLiteralImage,
|
|
6730
|
+
managedIconNames: [...managedIconNames].sort(compareCodeUnits)
|
|
6731
|
+
};
|
|
6732
|
+
}
|
|
6733
|
+
function collectManagedImageLiterals(value, result) {
|
|
6734
|
+
if (!Array.isArray(value)) {
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6737
|
+
if ((value[0] === "image" || value[0] === "literal") && typeof value[1] === "string") {
|
|
6738
|
+
const managed = managedIconName(value[1]);
|
|
6739
|
+
if (managed) {
|
|
6740
|
+
result.add(managed);
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
for (const child of value) {
|
|
6744
|
+
collectManagedImageLiterals(child, result);
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
function managedIconName(value) {
|
|
6748
|
+
return value.startsWith("tileflow:") && value.length > "tileflow:".length ? value.slice("tileflow:".length) : null;
|
|
6749
|
+
}
|
|
6750
|
+
function uniqueBy(values, key) {
|
|
6751
|
+
return [...new Map(values.map((value) => [key(value), value])).values()];
|
|
6752
|
+
}
|
|
6753
|
+
function compareReferences(left, right) {
|
|
6754
|
+
return compareCodeUnits(left.path, right.path) || compareCodeUnits(left.kind, right.kind) || compareCodeUnits(left.iconName, right.iconName);
|
|
6755
|
+
}
|
|
6756
|
+
|
|
5580
6757
|
// src/runtime.ts
|
|
5581
6758
|
var defaultTileflowManifestUrl = "/tileflow/manifest.json";
|
|
5582
6759
|
var defaultTileflowStyleBaseUrl = "http://localhost:3333";
|
|
@@ -5855,6 +7032,7 @@ function getTileflowLocationOrigin() {
|
|
|
5855
7032
|
return window.location.origin;
|
|
5856
7033
|
}
|
|
5857
7034
|
export {
|
|
7035
|
+
compareCodeUnits,
|
|
5858
7036
|
configSchema,
|
|
5859
7037
|
createManifest,
|
|
5860
7038
|
createStyle,
|
|
@@ -5863,14 +7041,21 @@ export {
|
|
|
5863
7041
|
defaultTileflowManifestUrl,
|
|
5864
7042
|
defaultTileflowStyleBaseUrl,
|
|
5865
7043
|
defineTileflow,
|
|
7044
|
+
diffTileflowIconMappings,
|
|
7045
|
+
diffTileflowIconPackageManifests,
|
|
5866
7046
|
getDefaultColors2 as getDefaultColors,
|
|
5867
7047
|
getDefaultTileflowStyleBaseUrl,
|
|
5868
7048
|
getTileflowAnalyticsApiUrl,
|
|
7049
|
+
hashTileflowIconPackageManifest,
|
|
7050
|
+
hashTileflowRenderedIconPixels,
|
|
5869
7051
|
inferTileflowAnalyticsFromStyleUrl,
|
|
7052
|
+
inspectTileflowIconReferences,
|
|
5870
7053
|
isTileflowLocalDevHost,
|
|
5871
7054
|
labels,
|
|
5872
7055
|
loadTileflowManifest,
|
|
5873
7056
|
mergeTileflowAnalytics,
|
|
7057
|
+
normalizeTileflowCaptureId,
|
|
7058
|
+
normalizeTileflowCaptureScene,
|
|
5874
7059
|
normalizeTileflowStaticImageSize,
|
|
5875
7060
|
normalizeTileflowUrl,
|
|
5876
7061
|
osm,
|
|
@@ -5878,17 +7063,43 @@ export {
|
|
|
5878
7063
|
parseTileflowProject,
|
|
5879
7064
|
poi,
|
|
5880
7065
|
resolveTileflowAnalyticsRequestUrl,
|
|
7066
|
+
resolveTileflowIconMapping,
|
|
5881
7067
|
resolveTileflowManifestMap,
|
|
5882
7068
|
resolveTileflowMapMode,
|
|
5883
7069
|
resolveTileflowRuntimeStyle,
|
|
5884
7070
|
resolveTileflowStaticImageUrl,
|
|
5885
7071
|
resolveTileflowStyleUrl,
|
|
5886
7072
|
roads,
|
|
7073
|
+
serializeCanonicalJson,
|
|
7074
|
+
serializeTileflowIconPackageManifest,
|
|
7075
|
+
sha256Hex,
|
|
5887
7076
|
shouldLoadTileflowManifest,
|
|
5888
7077
|
startTileflowSession,
|
|
5889
7078
|
styleOverride,
|
|
7079
|
+
tileflowCaptureApplicationTargetSchema,
|
|
7080
|
+
tileflowCaptureBoundsCameraSchema,
|
|
7081
|
+
tileflowCaptureCameraSchema,
|
|
7082
|
+
tileflowCaptureCenterCameraSchema,
|
|
7083
|
+
tileflowCaptureIdSchema,
|
|
7084
|
+
tileflowCaptureMapTargetSchema,
|
|
7085
|
+
tileflowCaptureSceneLimits,
|
|
7086
|
+
tileflowCaptureSceneNameSchema,
|
|
7087
|
+
tileflowCaptureSceneSchema,
|
|
7088
|
+
tileflowCaptureSceneSchemaVersion,
|
|
7089
|
+
tileflowCaptureTargetSchema,
|
|
7090
|
+
tileflowCaptureViewportSchema,
|
|
7091
|
+
tileflowHostedAlphaCompatibility,
|
|
7092
|
+
tileflowHostedIconIdPattern,
|
|
7093
|
+
tileflowHostedIconIdSchema,
|
|
7094
|
+
tileflowHostedNotoSansWeights,
|
|
7095
|
+
tileflowIconPackageContentHashSchema,
|
|
7096
|
+
tileflowIconPackageFileNames,
|
|
7097
|
+
tileflowIconPackageLabelSchema,
|
|
7098
|
+
tileflowIconPackageLimits,
|
|
7099
|
+
tileflowIconPackageManifestSchema,
|
|
5890
7100
|
tileflowMapSchema,
|
|
5891
7101
|
tileflowProjectSchema,
|
|
7102
|
+
tileflowRenderedIconManifestEntrySchema,
|
|
5892
7103
|
tileflowThemeSchema,
|
|
5893
7104
|
validateConfig,
|
|
5894
7105
|
validateTileflowConfig
|