@tileflow/core 0.1.0-alpha.14 → 0.1.0-alpha.17
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 +119 -6
- package/dist/browser.d.ts +74 -0
- package/dist/browser.js +320 -0
- package/dist/index.d.ts +701 -464
- package/dist/index.js +1491 -282
- package/dist/runtime-BMZ_tuba.d.ts +656 -0
- package/package.json +13 -5
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) {
|
|
@@ -517,9 +549,21 @@ var moduleRoadDefaults = {
|
|
|
517
549
|
detail: "streets",
|
|
518
550
|
extras: { ferry: false, paths: false, rail: false },
|
|
519
551
|
hierarchy: "clear",
|
|
552
|
+
oneWayMarkers: false,
|
|
520
553
|
outline: "subtle",
|
|
521
554
|
weight: "regular"
|
|
522
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
|
|
566
|
+
};
|
|
523
567
|
var highwayRoadClasses = ["motorway", "trunk"];
|
|
524
568
|
var arterialRoadClasses = ["primary", "secondary", "tertiary"];
|
|
525
569
|
var majorRoadClasses = [...highwayRoadClasses, ...arterialRoadClasses];
|
|
@@ -543,8 +587,10 @@ function roads(options = {}) {
|
|
|
543
587
|
...options.detail ? { detail: options.detail } : {},
|
|
544
588
|
...options.extras ? { extras: { ...options.extras } } : {},
|
|
545
589
|
...options.hierarchy ? { hierarchy: options.hierarchy } : {},
|
|
590
|
+
...options.oneWayMarkers !== void 0 ? { oneWayMarkers: options.oneWayMarkers } : {},
|
|
546
591
|
...options.outline ? { outline: options.outline } : {},
|
|
547
|
-
...options.weight ? { weight: options.weight } : {}
|
|
592
|
+
...options.weight ? { weight: options.weight } : {},
|
|
593
|
+
...options.widthScale ? { widthScale: { ...options.widthScale } } : {}
|
|
548
594
|
};
|
|
549
595
|
}
|
|
550
596
|
function resolveRoads(legacyRoads, moduleConfig, fallback) {
|
|
@@ -558,8 +604,10 @@ function resolveRoads(legacyRoads, moduleConfig, fallback) {
|
|
|
558
604
|
rail: moduleConfig?.extras?.rail ?? defaults.extras.rail
|
|
559
605
|
},
|
|
560
606
|
hierarchy: moduleConfig?.hierarchy ?? defaults.hierarchy,
|
|
607
|
+
oneWayMarkers: moduleConfig?.oneWayMarkers ?? defaults.oneWayMarkers,
|
|
561
608
|
outline: moduleConfig?.outline ?? defaults.outline,
|
|
562
|
-
weight: moduleConfig?.weight ?? defaults.weight
|
|
609
|
+
weight: moduleConfig?.weight ?? defaults.weight,
|
|
610
|
+
...moduleConfig?.widthScale ? { widthScale: { ...defaultRoadWidthScale, ...moduleConfig.widthScale } } : {}
|
|
563
611
|
};
|
|
564
612
|
return {
|
|
565
613
|
...resolved,
|
|
@@ -603,7 +651,7 @@ function roadStyleMetrics(options) {
|
|
|
603
651
|
weightScale
|
|
604
652
|
};
|
|
605
653
|
}
|
|
606
|
-
function roadLayers(source, sourceLayer, colors, options) {
|
|
654
|
+
function roadLayers(source, sourceLayer, colors, options, typography) {
|
|
607
655
|
if (options.detail === "none") {
|
|
608
656
|
return [];
|
|
609
657
|
}
|
|
@@ -647,7 +695,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
647
695
|
[14, 2.2],
|
|
648
696
|
[16, 4.8]
|
|
649
697
|
],
|
|
650
|
-
metrics
|
|
698
|
+
metrics,
|
|
699
|
+
options.widthScale
|
|
651
700
|
)
|
|
652
701
|
}
|
|
653
702
|
});
|
|
@@ -672,7 +721,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
672
721
|
[14, 1],
|
|
673
722
|
[16, 2.4]
|
|
674
723
|
],
|
|
675
|
-
metrics
|
|
724
|
+
metrics,
|
|
725
|
+
options.widthScale
|
|
676
726
|
)
|
|
677
727
|
}
|
|
678
728
|
});
|
|
@@ -693,7 +743,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
693
743
|
[12, 1.8],
|
|
694
744
|
[16, 5.4]
|
|
695
745
|
],
|
|
696
|
-
metrics
|
|
746
|
+
metrics,
|
|
747
|
+
options.widthScale
|
|
697
748
|
)
|
|
698
749
|
}
|
|
699
750
|
});
|
|
@@ -739,7 +790,7 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
739
790
|
[14, 0.8],
|
|
740
791
|
[16, 2.2]
|
|
741
792
|
],
|
|
742
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
793
|
+
metrics.weightScale * metrics.minorWidthScale * (options.widthScale?.path ?? 1)
|
|
743
794
|
)
|
|
744
795
|
}
|
|
745
796
|
});
|
|
@@ -782,7 +833,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
782
833
|
[14, 0.6],
|
|
783
834
|
[16, 2.2]
|
|
784
835
|
],
|
|
785
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
836
|
+
metrics.weightScale * metrics.minorWidthScale,
|
|
837
|
+
options.widthScale
|
|
786
838
|
)
|
|
787
839
|
}
|
|
788
840
|
});
|
|
@@ -803,7 +855,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
803
855
|
[12, 0.8],
|
|
804
856
|
[16, 3]
|
|
805
857
|
],
|
|
806
|
-
metrics.weightScale * metrics.minorWidthScale
|
|
858
|
+
metrics.weightScale * metrics.minorWidthScale,
|
|
859
|
+
options.widthScale
|
|
807
860
|
)
|
|
808
861
|
}
|
|
809
862
|
});
|
|
@@ -825,7 +878,8 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
825
878
|
[12, 1.2],
|
|
826
879
|
[16, 4]
|
|
827
880
|
],
|
|
828
|
-
metrics.weightScale * metrics.majorWidthScale
|
|
881
|
+
metrics.weightScale * metrics.majorWidthScale,
|
|
882
|
+
options.widthScale
|
|
829
883
|
)
|
|
830
884
|
}
|
|
831
885
|
},
|
|
@@ -845,11 +899,15 @@ function roadLayers(source, sourceLayer, colors, options) {
|
|
|
845
899
|
[14, 1.6],
|
|
846
900
|
[16, 4.4]
|
|
847
901
|
],
|
|
848
|
-
metrics
|
|
902
|
+
metrics,
|
|
903
|
+
options.widthScale
|
|
849
904
|
)
|
|
850
905
|
}
|
|
851
906
|
}
|
|
852
907
|
);
|
|
908
|
+
if (options.oneWayMarkers) {
|
|
909
|
+
layers.push(oneWayMarkerLayer(source, sourceLayer, colors, typography));
|
|
910
|
+
}
|
|
853
911
|
return layers;
|
|
854
912
|
}
|
|
855
913
|
function roadDefaultsFromLegacyMode(mode) {
|
|
@@ -858,6 +916,7 @@ function roadDefaultsFromLegacyMode(mode) {
|
|
|
858
916
|
detail: "none",
|
|
859
917
|
extras: { ferry: false, paths: false, rail: false },
|
|
860
918
|
hierarchy: "clear",
|
|
919
|
+
oneWayMarkers: false,
|
|
861
920
|
outline: "subtle",
|
|
862
921
|
weight: "regular"
|
|
863
922
|
};
|
|
@@ -867,6 +926,7 @@ function roadDefaultsFromLegacyMode(mode) {
|
|
|
867
926
|
detail: "streets",
|
|
868
927
|
extras: { ferry: false, paths: false, rail: false },
|
|
869
928
|
hierarchy: "subtle",
|
|
929
|
+
oneWayMarkers: false,
|
|
870
930
|
outline: "subtle",
|
|
871
931
|
weight: "thin"
|
|
872
932
|
};
|
|
@@ -876,6 +936,7 @@ function roadDefaultsFromLegacyMode(mode) {
|
|
|
876
936
|
detail: "all",
|
|
877
937
|
extras: { ferry: true, paths: true, rail: true },
|
|
878
938
|
hierarchy: "clear",
|
|
939
|
+
oneWayMarkers: false,
|
|
879
940
|
outline: "strong",
|
|
880
941
|
weight: "regular"
|
|
881
942
|
};
|
|
@@ -884,6 +945,7 @@ function roadDefaultsFromLegacyMode(mode) {
|
|
|
884
945
|
detail: "streets",
|
|
885
946
|
extras: { ferry: true, paths: true, rail: true },
|
|
886
947
|
hierarchy: "clear",
|
|
948
|
+
oneWayMarkers: false,
|
|
887
949
|
outline: "subtle",
|
|
888
950
|
weight: "regular"
|
|
889
951
|
};
|
|
@@ -898,33 +960,101 @@ function classFilter(classes) {
|
|
|
898
960
|
return ["match", ["get", "class"], classes, true, false];
|
|
899
961
|
}
|
|
900
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
|
+
];
|
|
972
|
+
}
|
|
973
|
+
function roadOpacityAtZoom(majorOpacity, minorOpacity, streetOpacity, serviceOpacity) {
|
|
901
974
|
return [
|
|
902
975
|
"case",
|
|
903
976
|
classFilter(majorRoadClasses),
|
|
904
977
|
majorOpacity,
|
|
905
978
|
classFilter(streetRoadClasses),
|
|
906
|
-
|
|
979
|
+
streetOpacity,
|
|
907
980
|
classFilter(serviceRoadClasses),
|
|
908
|
-
|
|
981
|
+
serviceOpacity,
|
|
909
982
|
minorOpacity
|
|
910
983
|
];
|
|
911
984
|
}
|
|
912
|
-
function roadWidthExpression(stops, metrics) {
|
|
985
|
+
function roadWidthExpression(stops, metrics, widthScale) {
|
|
913
986
|
return [
|
|
914
|
-
"
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
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
|
+
])
|
|
918
1008
|
];
|
|
919
1009
|
}
|
|
920
|
-
function zoomWidth(stops, scale) {
|
|
1010
|
+
function zoomWidth(stops, scale, widthScale) {
|
|
921
1011
|
return [
|
|
922
1012
|
"interpolate",
|
|
923
1013
|
["linear"],
|
|
924
1014
|
["zoom"],
|
|
925
|
-
...stops.flatMap(([zoom, width]) => [
|
|
1015
|
+
...stops.flatMap(([zoom, width]) => [
|
|
1016
|
+
zoom,
|
|
1017
|
+
widthScale ? ["*", width * scale, roadClassWidthScaleExpression(widthScale)] : width * scale
|
|
1018
|
+
])
|
|
1019
|
+
];
|
|
1020
|
+
}
|
|
1021
|
+
function roadClassWidthScaleExpression(widthScale) {
|
|
1022
|
+
return [
|
|
1023
|
+
"match",
|
|
1024
|
+
["get", "class"],
|
|
1025
|
+
...Object.entries(widthScale).flatMap(([roadClass, scale]) => [roadClass, scale]),
|
|
1026
|
+
1
|
|
926
1027
|
];
|
|
927
1028
|
}
|
|
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
|
+
};
|
|
1057
|
+
}
|
|
928
1058
|
function roadClassColorExpression(colors) {
|
|
929
1059
|
return [
|
|
930
1060
|
"match",
|
|
@@ -966,6 +1096,7 @@ function labels(options = {}) {
|
|
|
966
1096
|
type: "labels",
|
|
967
1097
|
...options.language ? { language: options.language } : {},
|
|
968
1098
|
...options.places ? { places: options.places } : {},
|
|
1099
|
+
...options.roadClasses ? { roadClasses: [...options.roadClasses] } : {},
|
|
969
1100
|
...options.roads ? { roads: options.roads } : {},
|
|
970
1101
|
...options.water ? { water: options.water } : {}
|
|
971
1102
|
};
|
|
@@ -976,6 +1107,7 @@ function resolveLabels(legacyLabels, moduleConfig, fallback) {
|
|
|
976
1107
|
const resolved = {
|
|
977
1108
|
language: moduleConfig?.language ?? defaults.language,
|
|
978
1109
|
places: moduleConfig?.places ?? defaults.places,
|
|
1110
|
+
...moduleConfig?.roadClasses ? { roadClasses: moduleConfig.roadClasses } : {},
|
|
979
1111
|
roads: moduleConfig?.roads ?? defaults.roads,
|
|
980
1112
|
water: moduleConfig?.water ?? defaults.water
|
|
981
1113
|
};
|
|
@@ -1017,7 +1149,7 @@ function placeLabelLayers(source, sourceLayer, colors, labelsConfig, typography)
|
|
|
1017
1149
|
filter: labelsConfig.places === "major" ? ["all", ["has", "name"], classFilter2(majorPlaceClasses)] : ["has", "name"],
|
|
1018
1150
|
layout: {
|
|
1019
1151
|
"text-field": labelTextField(labelsConfig.language),
|
|
1020
|
-
"text-font": textFont(typography),
|
|
1152
|
+
"text-font": textFont(typography, "places"),
|
|
1021
1153
|
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
|
|
1022
1154
|
"text-letter-spacing": 0,
|
|
1023
1155
|
"text-padding": 4
|
|
@@ -1031,7 +1163,11 @@ function placeLabelLayers(source, sourceLayer, colors, labelsConfig, typography)
|
|
|
1031
1163
|
];
|
|
1032
1164
|
}
|
|
1033
1165
|
function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig, typography) {
|
|
1034
|
-
const classes = visibleRoadLabelClasses(
|
|
1166
|
+
const classes = visibleRoadLabelClasses(
|
|
1167
|
+
labelsConfig.roads,
|
|
1168
|
+
roadsConfig,
|
|
1169
|
+
labelsConfig.roadClasses
|
|
1170
|
+
);
|
|
1035
1171
|
const majorClasses = classes.filter(isMajorRoadClass);
|
|
1036
1172
|
const minorClasses = classes.filter((roadClass) => !isMajorRoadClass(roadClass));
|
|
1037
1173
|
if (classes.length === 0) return [];
|
|
@@ -1039,7 +1175,7 @@ function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig,
|
|
|
1039
1175
|
const sharedLayout = {
|
|
1040
1176
|
"symbol-placement": "line",
|
|
1041
1177
|
"text-field": labelTextField(labelsConfig.language),
|
|
1042
|
-
"text-font": textFont(typography),
|
|
1178
|
+
"text-font": textFont(typography, "roads"),
|
|
1043
1179
|
"text-rotation-alignment": "map",
|
|
1044
1180
|
"text-size": ["interpolate", ["linear"], ["zoom"], 11, 11, 14, 13, 17, 15]
|
|
1045
1181
|
};
|
|
@@ -1061,7 +1197,7 @@ function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig,
|
|
|
1061
1197
|
"symbol-placement": "line",
|
|
1062
1198
|
"symbol-spacing": 240,
|
|
1063
1199
|
"text-field": ["get", "ref"],
|
|
1064
|
-
"text-font": textFont(typography),
|
|
1200
|
+
"text-font": textFont(typography, "roads"),
|
|
1065
1201
|
"text-rotation-alignment": "viewport",
|
|
1066
1202
|
"text-size": 10
|
|
1067
1203
|
},
|
|
@@ -1113,7 +1249,7 @@ function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, col
|
|
|
1113
1249
|
filter: ["all", ["==", ["geometry-type"], "Point"], waterFilter],
|
|
1114
1250
|
layout: {
|
|
1115
1251
|
"text-field": labelTextField(labelsConfig.language),
|
|
1116
|
-
"text-font": textFont(typography),
|
|
1252
|
+
"text-font": textFont(typography, "water"),
|
|
1117
1253
|
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 11, 8, 14]
|
|
1118
1254
|
},
|
|
1119
1255
|
paint
|
|
@@ -1128,7 +1264,7 @@ function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, col
|
|
|
1128
1264
|
layout: {
|
|
1129
1265
|
"symbol-placement": "line",
|
|
1130
1266
|
"text-field": labelTextField(labelsConfig.language),
|
|
1131
|
-
"text-font": textFont(typography),
|
|
1267
|
+
"text-font": textFont(typography, "water"),
|
|
1132
1268
|
"text-size": 13
|
|
1133
1269
|
},
|
|
1134
1270
|
paint
|
|
@@ -1144,14 +1280,14 @@ function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, col
|
|
|
1144
1280
|
"symbol-placement": "line",
|
|
1145
1281
|
"symbol-spacing": 320,
|
|
1146
1282
|
"text-field": labelTextField(labelsConfig.language),
|
|
1147
|
-
"text-font": textFont(typography),
|
|
1283
|
+
"text-font": textFont(typography, "water"),
|
|
1148
1284
|
"text-size": 12
|
|
1149
1285
|
},
|
|
1150
1286
|
paint
|
|
1151
1287
|
}
|
|
1152
1288
|
];
|
|
1153
1289
|
}
|
|
1154
|
-
function visibleRoadLabelClasses(detail, roadsConfig) {
|
|
1290
|
+
function visibleRoadLabelClasses(detail, roadsConfig, eligibleClasses) {
|
|
1155
1291
|
if (detail === "none" || roadsConfig.detail === "none") return [];
|
|
1156
1292
|
const visibleRoadClasses = [
|
|
1157
1293
|
...roadClassesForDetail(roadsConfig.detail),
|
|
@@ -1159,7 +1295,10 @@ function visibleRoadLabelClasses(detail, roadsConfig) {
|
|
|
1159
1295
|
];
|
|
1160
1296
|
const requestedClasses = roadLabelClasses(detail);
|
|
1161
1297
|
const visible = new Set(visibleRoadClasses);
|
|
1162
|
-
|
|
1298
|
+
const eligible = eligibleClasses ? new Set(eligibleClasses) : void 0;
|
|
1299
|
+
return requestedClasses.filter(
|
|
1300
|
+
(roadClass) => visible.has(roadClass) && (!eligible || eligible.has(roadClass))
|
|
1301
|
+
);
|
|
1163
1302
|
}
|
|
1164
1303
|
function roadLabelClasses(detail) {
|
|
1165
1304
|
return [...roadClassesForDetail(detail), ...detail === "all" ? ["path"] : []];
|
|
@@ -1206,20 +1345,33 @@ function poi(options = {}) {
|
|
|
1206
1345
|
return {
|
|
1207
1346
|
type: "poi",
|
|
1208
1347
|
...options.categories ? { categories: options.categories } : {},
|
|
1348
|
+
...options.classMapping ? { classMapping: { ...options.classMapping } } : {},
|
|
1349
|
+
...options.color ? { color: options.color } : {},
|
|
1350
|
+
...options.density ? { density: options.density } : {},
|
|
1209
1351
|
...options.icons !== void 0 ? { icons: options.icons } : {},
|
|
1210
1352
|
...options.labels ? { labels: options.labels } : {},
|
|
1211
1353
|
...options.minZoom !== void 0 ? { minZoom: options.minZoom } : {},
|
|
1354
|
+
...options.placement ? { placement: { ...options.placement } } : {},
|
|
1212
1355
|
...options.preset ? { preset: options.preset } : {}
|
|
1213
1356
|
};
|
|
1214
1357
|
}
|
|
1215
1358
|
function resolvePoi(legacyPoi, moduleConfig, fallback) {
|
|
1216
1359
|
const mode = moduleConfig?.preset ?? legacyPoi ?? fallback;
|
|
1360
|
+
const density = moduleConfig?.density;
|
|
1217
1361
|
return {
|
|
1218
|
-
categories: moduleConfig
|
|
1362
|
+
...moduleConfig?.categories ? { categories: moduleConfig.categories } : {},
|
|
1363
|
+
classMapping: resolvePoiClassMapping(moduleConfig?.classMapping),
|
|
1364
|
+
color: moduleConfig?.color ?? "uniform",
|
|
1365
|
+
...density ? { density } : {},
|
|
1219
1366
|
icons: moduleConfig?.icons ?? (mode === "none" ? false : "essential"),
|
|
1220
1367
|
labels: moduleConfig?.labels ?? poiLabelsFromMode(mode),
|
|
1221
|
-
minZoom: moduleConfig?.minZoom ?? defaultPoiMinZoom[mode],
|
|
1222
|
-
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
|
+
}
|
|
1223
1375
|
};
|
|
1224
1376
|
}
|
|
1225
1377
|
function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIconSpriteId2, customIconMapping) {
|
|
@@ -1228,21 +1380,29 @@ function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIco
|
|
|
1228
1380
|
}
|
|
1229
1381
|
const layout = {
|
|
1230
1382
|
"text-field": labelTextField("auto"),
|
|
1231
|
-
"text-font": textFont(typography),
|
|
1383
|
+
"text-font": textFont(typography, "poi"),
|
|
1232
1384
|
"text-size": ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 10, 17, 12],
|
|
1233
1385
|
"text-letter-spacing": 0,
|
|
1234
|
-
"text-padding":
|
|
1386
|
+
"text-padding": poiConfig.placement.textPadding
|
|
1235
1387
|
};
|
|
1388
|
+
if (poiConfig.density) {
|
|
1389
|
+
layout["symbol-sort-key"] = poiPriorityExpression(poiConfig.classMapping);
|
|
1390
|
+
}
|
|
1236
1391
|
if (poiConfig.labels === "none") {
|
|
1237
1392
|
delete layout["text-field"];
|
|
1238
1393
|
}
|
|
1239
1394
|
if (poiConfig.icons) {
|
|
1240
|
-
layout["icon-image"] = poiIconImageExpression(
|
|
1241
|
-
|
|
1242
|
-
|
|
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;
|
|
1243
1402
|
layout["icon-size"] = ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 0.85, 17, 1];
|
|
1244
1403
|
layout["text-anchor"] = "top";
|
|
1245
1404
|
layout["text-offset"] = [0, 0.75];
|
|
1405
|
+
if (poiConfig.placement.coupleIconAndLabel) layout["text-optional"] = false;
|
|
1246
1406
|
}
|
|
1247
1407
|
return [
|
|
1248
1408
|
{
|
|
@@ -1251,10 +1411,10 @@ function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIco
|
|
|
1251
1411
|
source,
|
|
1252
1412
|
"source-layer": sourceLayer,
|
|
1253
1413
|
minzoom: poiConfig.minZoom,
|
|
1254
|
-
...poiConfig
|
|
1414
|
+
...poiFilter(poiConfig) ? { filter: poiFilter(poiConfig) } : {},
|
|
1255
1415
|
layout,
|
|
1256
1416
|
paint: {
|
|
1257
|
-
"text-color": colors.poi.label,
|
|
1417
|
+
"text-color": poiConfig.color === "category" ? poiCategoryColorExpression(colors, poiConfig.classMapping) : colors.poi.label,
|
|
1258
1418
|
"text-halo-color": colors.poi.halo ?? alpha(colors.background, 0.78),
|
|
1259
1419
|
"text-halo-width": 1
|
|
1260
1420
|
}
|
|
@@ -1267,26 +1427,112 @@ function poiLabelsFromMode(mode) {
|
|
|
1267
1427
|
if (mode === "full") return "full";
|
|
1268
1428
|
return "balanced";
|
|
1269
1429
|
}
|
|
1270
|
-
function poiCategoryFilter(categories) {
|
|
1271
|
-
|
|
1430
|
+
function poiCategoryFilter(categories, classMapping = poiCategoryClassNames) {
|
|
1431
|
+
const classes = categories.flatMap((category) => poiCategoryClasses(category, classMapping));
|
|
1432
|
+
return ["any", ["in", "class", ...classes], ["in", "subclass", ...classes]];
|
|
1272
1433
|
}
|
|
1273
|
-
function poiIconImageExpression(customIconSpriteId2, customIconMapping = {}) {
|
|
1274
|
-
const matchParts = [
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping)
|
|
1279
|
-
);
|
|
1280
|
-
}
|
|
1434
|
+
function poiIconImageExpression(customIconSpriteId2, customIconMapping = {}, classMapping = poiCategoryClassNames) {
|
|
1435
|
+
const matchParts = Object.keys(classMapping).flatMap((category) => [
|
|
1436
|
+
category,
|
|
1437
|
+
poiIconImageFallback(category, customIconSpriteId2, customIconMapping)
|
|
1438
|
+
]);
|
|
1281
1439
|
return [
|
|
1282
1440
|
"match",
|
|
1283
|
-
|
|
1441
|
+
poiCategoryExpression("default", classMapping),
|
|
1284
1442
|
...matchParts,
|
|
1285
1443
|
poiIconImageFallback("default", customIconSpriteId2, customIconMapping)
|
|
1286
1444
|
];
|
|
1287
1445
|
}
|
|
1288
|
-
function
|
|
1289
|
-
|
|
1446
|
+
function poiCategoryExpression(fallback = "", classMapping = poiCategoryClassNames) {
|
|
1447
|
+
const categoryMatches = poiCategoryMatchParts(classMapping);
|
|
1448
|
+
return [
|
|
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
|
|
1488
|
+
];
|
|
1489
|
+
return ["+", ["*", categoryPenalty, 1e3], ["to-number", ["get", "rank"], 999]];
|
|
1490
|
+
}
|
|
1491
|
+
function poiDensityFilter(density, classMapping = poiCategoryClassNames) {
|
|
1492
|
+
if (density === "dense") return void 0;
|
|
1493
|
+
const cutoffs = poiDensityRankCutoffs[density];
|
|
1494
|
+
return [
|
|
1495
|
+
"any",
|
|
1496
|
+
...Object.keys(classMapping).map((category) => [
|
|
1497
|
+
"all",
|
|
1498
|
+
poiCategoryFilter([category], classMapping),
|
|
1499
|
+
["any", ["!has", "rank"], ["<=", "rank", cutoffs[category] ?? cutoffs.default]]
|
|
1500
|
+
])
|
|
1501
|
+
];
|
|
1502
|
+
}
|
|
1503
|
+
function poiCategoryColorExpression(colors, classMapping = poiCategoryClassNames) {
|
|
1504
|
+
return [
|
|
1505
|
+
"match",
|
|
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;
|
|
1290
1536
|
}
|
|
1291
1537
|
function poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping) {
|
|
1292
1538
|
const osmBrightIcon = `${osmBrightIconNameForSemanticIcon(iconName)}_11`;
|
|
@@ -1305,55 +1551,50 @@ function osmBrightIconNameForSemanticIcon(iconName) {
|
|
|
1305
1551
|
}
|
|
1306
1552
|
var poiCategoryClassNames = {
|
|
1307
1553
|
coffee: ["cafe"],
|
|
1308
|
-
culture: [
|
|
1554
|
+
culture: [
|
|
1555
|
+
"museum",
|
|
1556
|
+
"gallery",
|
|
1557
|
+
"art_gallery",
|
|
1558
|
+
"theatre",
|
|
1559
|
+
"cinema",
|
|
1560
|
+
"library",
|
|
1561
|
+
"attraction",
|
|
1562
|
+
"garden",
|
|
1563
|
+
"monument"
|
|
1564
|
+
],
|
|
1309
1565
|
education: ["school", "college", "university"],
|
|
1310
1566
|
food: ["restaurant", "fast_food", "bar", "pub", "biergarten"],
|
|
1311
1567
|
health: ["hospital", "pharmacy", "doctors", "dentist"],
|
|
1312
|
-
lodging: ["hotel", "motel", "hostel", "guest_house"],
|
|
1568
|
+
lodging: ["lodging", "hotel", "motel", "hostel", "guest_house"],
|
|
1313
1569
|
services: ["bank", "post", "toilet", "police", "fire_station", "town_hall"],
|
|
1314
1570
|
shopping: ["shop", "mall", "supermarket", "marketplace"],
|
|
1315
1571
|
transit: ["railway", "station", "bus", "bus_stop", "subway", "tram"]
|
|
1316
1572
|
};
|
|
1317
|
-
var
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
police: "services",
|
|
1343
|
-
post: "services",
|
|
1344
|
-
pub: "food",
|
|
1345
|
-
railway: "transit",
|
|
1346
|
-
restaurant: "food",
|
|
1347
|
-
school: "education",
|
|
1348
|
-
shop: "shopping",
|
|
1349
|
-
station: "transit",
|
|
1350
|
-
subway: "transit",
|
|
1351
|
-
supermarket: "shopping",
|
|
1352
|
-
theatre: "culture",
|
|
1353
|
-
toilet: "services",
|
|
1354
|
-
town_hall: "services",
|
|
1355
|
-
tram: "transit",
|
|
1356
|
-
university: "education"
|
|
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
|
+
}
|
|
1357
1598
|
};
|
|
1358
1599
|
var semanticToOsmBrightIconName = {
|
|
1359
1600
|
coffee: "cafe",
|
|
@@ -1378,7 +1619,8 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1378
1619
|
"background-color": colors.background
|
|
1379
1620
|
}
|
|
1380
1621
|
},
|
|
1381
|
-
|
|
1622
|
+
semanticLanduseLayer(source, sourceLayers.landuse, colors),
|
|
1623
|
+
semanticLandcoverLayer(source, sourceLayers.landcover, colors),
|
|
1382
1624
|
fillLayer(
|
|
1383
1625
|
"parks",
|
|
1384
1626
|
source,
|
|
@@ -1387,8 +1629,15 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1387
1629
|
parkOpacity(options.density)
|
|
1388
1630
|
),
|
|
1389
1631
|
fillLayer("water", source, sourceLayers.water, colors.hydro.water, 1),
|
|
1390
|
-
...buildingLayers(
|
|
1391
|
-
|
|
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),
|
|
1392
1641
|
...boundaryLayers(source, sourceLayers.boundary, colors),
|
|
1393
1642
|
...labelLayers(source, sourceLayers, colors, options.labels, options.roads, options.typography),
|
|
1394
1643
|
...poiLayers(
|
|
@@ -1402,6 +1651,61 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
1402
1651
|
)
|
|
1403
1652
|
];
|
|
1404
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
|
+
}
|
|
1405
1709
|
function fillLayer(id, source, sourceLayer, color, opacity) {
|
|
1406
1710
|
return {
|
|
1407
1711
|
id,
|
|
@@ -1414,7 +1718,7 @@ function fillLayer(id, source, sourceLayer, color, opacity) {
|
|
|
1414
1718
|
}
|
|
1415
1719
|
};
|
|
1416
1720
|
}
|
|
1417
|
-
function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
1721
|
+
function buildingLayers(source, sourceLayer, colors, density, buildings, buildingStyle) {
|
|
1418
1722
|
if (buildings === "hidden") {
|
|
1419
1723
|
return [];
|
|
1420
1724
|
}
|
|
@@ -1481,8 +1785,12 @@ function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
|
1481
1785
|
"source-layer": sourceLayer,
|
|
1482
1786
|
minzoom: 13,
|
|
1483
1787
|
paint: {
|
|
1484
|
-
"fill-color":
|
|
1485
|
-
|
|
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)
|
|
1486
1794
|
}
|
|
1487
1795
|
},
|
|
1488
1796
|
{
|
|
@@ -1492,13 +1800,33 @@ function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
|
1492
1800
|
"source-layer": sourceLayer,
|
|
1493
1801
|
minzoom: 15,
|
|
1494
1802
|
paint: {
|
|
1495
|
-
"line-color":
|
|
1496
|
-
|
|
1497
|
-
|
|
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
|
|
1498
1810
|
}
|
|
1499
1811
|
}
|
|
1500
1812
|
];
|
|
1501
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
|
+
}
|
|
1502
1830
|
function boundaryLayers(source, sourceLayer, colors) {
|
|
1503
1831
|
return [
|
|
1504
1832
|
{
|
|
@@ -1548,12 +1876,15 @@ function applyLayerOverride(layers, layerId, override) {
|
|
|
1548
1876
|
return insertLayer(layers, layer, override.before);
|
|
1549
1877
|
}
|
|
1550
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);
|
|
1551
1882
|
const patchedLayer = stripControlFields({
|
|
1552
1883
|
...previousLayer,
|
|
1553
1884
|
...override,
|
|
1554
|
-
layout
|
|
1555
|
-
paint
|
|
1556
|
-
metadata
|
|
1885
|
+
...layout ? { layout } : {},
|
|
1886
|
+
...paint ? { paint } : {},
|
|
1887
|
+
...metadata ? { metadata } : {}
|
|
1557
1888
|
});
|
|
1558
1889
|
const nextLayers = [...layers];
|
|
1559
1890
|
nextLayers[index] = patchedLayer;
|
|
@@ -1580,28 +1911,206 @@ function mergeRecords(previous, next) {
|
|
|
1580
1911
|
}
|
|
1581
1912
|
function stripControlFields(layer) {
|
|
1582
1913
|
const { before, ...styleLayer } = layer;
|
|
1583
|
-
return styleLayer;
|
|
1914
|
+
return Object.fromEntries(Object.entries(styleLayer).filter(([, value]) => value !== void 0));
|
|
1584
1915
|
}
|
|
1585
1916
|
function isRecord2(value) {
|
|
1586
1917
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1587
1918
|
}
|
|
1588
1919
|
|
|
1589
1920
|
// src/schema.ts
|
|
1921
|
+
import { z as z2 } from "zod";
|
|
1922
|
+
|
|
1923
|
+
// src/capture-scene.ts
|
|
1590
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
|
|
1591
2085
|
var legacyThemeNames = /* @__PURE__ */ new Set(["standard", "light", "dark", "minimal", "osm-bright-2"]);
|
|
1592
|
-
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_-]+$/, {
|
|
1593
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"
|
|
1594
2103
|
});
|
|
1595
|
-
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})$/, {
|
|
1596
2105
|
message: "Expected a hex color like #F6F7F3 or #F6F7F3FF"
|
|
1597
2106
|
});
|
|
1598
2107
|
function colorGroupSchema(keys) {
|
|
1599
2108
|
const shape = Object.fromEntries(
|
|
1600
2109
|
keys.map((key) => [key, hexColorSchema.optional()])
|
|
1601
2110
|
);
|
|
1602
|
-
return
|
|
2111
|
+
return z2.object(shape).strict();
|
|
1603
2112
|
}
|
|
1604
|
-
var colorConfigSchema =
|
|
2113
|
+
var colorConfigSchema = z2.object({
|
|
1605
2114
|
accent: hexColorSchema.optional(),
|
|
1606
2115
|
background: hexColorSchema.optional(),
|
|
1607
2116
|
boundary: hexColorSchema.optional(),
|
|
@@ -1619,7 +2128,15 @@ var colorConfigSchema = z.object({
|
|
|
1619
2128
|
textMuted: hexColorSchema.optional(),
|
|
1620
2129
|
water: hexColorSchema.optional(),
|
|
1621
2130
|
boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
|
|
1622
|
-
buildings: colorGroupSchema([
|
|
2131
|
+
buildings: colorGroupSchema([
|
|
2132
|
+
"extrusion",
|
|
2133
|
+
"fill",
|
|
2134
|
+
"highRise",
|
|
2135
|
+
"highRiseOutline",
|
|
2136
|
+
"lowRise",
|
|
2137
|
+
"lowRiseOutline",
|
|
2138
|
+
"outline"
|
|
2139
|
+
]).optional(),
|
|
1623
2140
|
hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
|
|
1624
2141
|
labels: colorGroupSchema([
|
|
1625
2142
|
"country",
|
|
@@ -1668,9 +2185,17 @@ var colorConfigSchema = z.object({
|
|
|
1668
2185
|
"tunnel"
|
|
1669
2186
|
]).optional()
|
|
1670
2187
|
}).strict();
|
|
1671
|
-
var themeModulesSchema =
|
|
2188
|
+
var themeModulesSchema = z2.object({
|
|
1672
2189
|
boundaries: colorGroupSchema(["admin", "disputed", "major", "maritime"]).optional(),
|
|
1673
|
-
buildings: colorGroupSchema([
|
|
2190
|
+
buildings: colorGroupSchema([
|
|
2191
|
+
"extrusion",
|
|
2192
|
+
"fill",
|
|
2193
|
+
"highRise",
|
|
2194
|
+
"highRiseOutline",
|
|
2195
|
+
"lowRise",
|
|
2196
|
+
"lowRiseOutline",
|
|
2197
|
+
"outline"
|
|
2198
|
+
]).optional(),
|
|
1674
2199
|
hydro: colorGroupSchema(["ferry", "label", "water", "waterway"]).optional(),
|
|
1675
2200
|
labels: colorGroupSchema([
|
|
1676
2201
|
"country",
|
|
@@ -1719,170 +2244,228 @@ var themeModulesSchema = z.object({
|
|
|
1719
2244
|
"tunnel"
|
|
1720
2245
|
]).optional()
|
|
1721
2246
|
}).strict();
|
|
1722
|
-
var styleLayerOverrideSchema =
|
|
1723
|
-
before:
|
|
1724
|
-
filter:
|
|
1725
|
-
layout:
|
|
1726
|
-
maxzoom:
|
|
1727
|
-
metadata:
|
|
1728
|
-
minzoom:
|
|
1729
|
-
paint:
|
|
1730
|
-
source:
|
|
1731
|
-
"source-layer":
|
|
1732
|
-
type:
|
|
1733
|
-
}).catchall(
|
|
1734
|
-
var
|
|
1735
|
-
font:
|
|
1736
|
-
fontFamily:
|
|
1737
|
-
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()
|
|
1738
2263
|
}).strict();
|
|
1739
|
-
var
|
|
1740
|
-
|
|
1741
|
-
|
|
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()
|
|
1742
2273
|
}).strict();
|
|
1743
|
-
var tileflowThemeSchema =
|
|
2274
|
+
var tileflowThemeSchema = z2.object({
|
|
1744
2275
|
colors: colorConfigSchema.optional(),
|
|
1745
|
-
extends:
|
|
2276
|
+
extends: z2.string().trim().min(1).optional(),
|
|
1746
2277
|
fonts: fontsSchema.optional(),
|
|
1747
|
-
layers:
|
|
1748
|
-
mode:
|
|
2278
|
+
layers: z2.record(z2.string(), styleLayerOverrideSchema).optional(),
|
|
2279
|
+
mode: z2.enum(["light", "dark"]).optional(),
|
|
1749
2280
|
modules: themeModulesSchema.optional(),
|
|
1750
2281
|
typography: typographySchema.optional()
|
|
1751
2282
|
}).strict();
|
|
1752
2283
|
var themeConfigSchema = tileflowThemeSchema;
|
|
1753
|
-
var sourceLayersSchema =
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
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()
|
|
1765
2297
|
}).strict();
|
|
1766
|
-
var basemapSchema =
|
|
1767
|
-
type:
|
|
1768
|
-
attribution:
|
|
1769
|
-
glyphs:
|
|
1770
|
-
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(),
|
|
1771
2303
|
sourceLayers: sourceLayersSchema.optional(),
|
|
1772
|
-
sprite:
|
|
1773
|
-
tileset:
|
|
1774
|
-
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()
|
|
1775
2308
|
}).strict();
|
|
1776
|
-
var roadDetailSchema =
|
|
1777
|
-
var
|
|
1778
|
-
|
|
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"),
|
|
1779
2323
|
detail: roadDetailSchema.optional(),
|
|
1780
|
-
extras:
|
|
1781
|
-
ferry:
|
|
1782
|
-
paths:
|
|
1783
|
-
rail:
|
|
2324
|
+
extras: z2.object({
|
|
2325
|
+
ferry: z2.boolean().optional(),
|
|
2326
|
+
paths: z2.boolean().optional(),
|
|
2327
|
+
rail: z2.boolean().optional()
|
|
1784
2328
|
}).strict().optional(),
|
|
1785
|
-
hierarchy:
|
|
1786
|
-
|
|
1787
|
-
|
|
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()
|
|
1788
2334
|
}).strict();
|
|
1789
|
-
var labelDetailSchema =
|
|
1790
|
-
var labelsModuleSchema =
|
|
1791
|
-
type:
|
|
1792
|
-
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(),
|
|
1793
2339
|
places: labelDetailSchema.optional(),
|
|
2340
|
+
roadClasses: z2.array(roadClassSchema).min(1).max(9).optional(),
|
|
1794
2341
|
roads: roadDetailSchema.optional(),
|
|
1795
2342
|
water: labelDetailSchema.optional()
|
|
1796
2343
|
}).strict();
|
|
1797
|
-
var poiModuleSchema =
|
|
1798
|
-
type:
|
|
1799
|
-
categories:
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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()
|
|
1804
2359
|
}).strict();
|
|
1805
|
-
var styleOverrideModuleSchema =
|
|
1806
|
-
type:
|
|
1807
|
-
layers:
|
|
1808
|
-
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()
|
|
1809
2364
|
}).strict();
|
|
1810
|
-
var moduleSchema =
|
|
2365
|
+
var moduleSchema = z2.discriminatedUnion("type", [
|
|
1811
2366
|
labelsModuleSchema,
|
|
1812
2367
|
poiModuleSchema,
|
|
1813
2368
|
roadsModuleSchema,
|
|
1814
2369
|
styleOverrideModuleSchema
|
|
1815
2370
|
]);
|
|
1816
|
-
var iconSetSchema =
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
extends:
|
|
1820
|
-
mapping:
|
|
1821
|
-
source:
|
|
1822
|
-
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()
|
|
1823
2378
|
}).strict()
|
|
1824
2379
|
]);
|
|
1825
|
-
var viewSchema =
|
|
1826
|
-
bearing:
|
|
1827
|
-
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, {
|
|
1828
2383
|
message: "Expected center as [lng, lat] within world bounds"
|
|
1829
2384
|
}).optional(),
|
|
1830
|
-
zoom:
|
|
2385
|
+
zoom: z2.number().finite().min(0).max(24).optional()
|
|
1831
2386
|
}).strict();
|
|
1832
|
-
var
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
sourceId: z.string().min(1).optional(),
|
|
1838
|
-
tileset: z.string().trim().min(1).optional(),
|
|
1839
|
-
url: z.url().optional()
|
|
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()
|
|
1840
2392
|
}).strict();
|
|
1841
|
-
var
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
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()
|
|
2401
|
+
}).strict();
|
|
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()
|
|
1847
2409
|
}).strict();
|
|
1848
|
-
var tileflowMapSchema =
|
|
2410
|
+
var tileflowMapSchema = z2.object({
|
|
1849
2411
|
basemap: basemapSchema.optional(),
|
|
1850
|
-
name:
|
|
1851
|
-
tileset:
|
|
1852
|
-
theme:
|
|
1853
|
-
modules:
|
|
1854
|
-
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(),
|
|
1855
2417
|
colors: colorConfigSchema.optional(),
|
|
1856
2418
|
typography: typographySchema.optional(),
|
|
1857
|
-
density:
|
|
1858
|
-
roads:
|
|
1859
|
-
labels:
|
|
1860
|
-
poi:
|
|
1861
|
-
buildings:
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
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(),
|
|
1865
2428
|
icons: iconSetSchema.optional(),
|
|
1866
|
-
sprite:
|
|
2429
|
+
sprite: z2.string().min(1).optional(),
|
|
1867
2430
|
tiles: tilesSchema.optional(),
|
|
1868
2431
|
view: viewSchema.optional(),
|
|
1869
|
-
allowedOrigins:
|
|
2432
|
+
allowedOrigins: z2.array(z2.string().trim().min(1).max(253)).max(20).optional()
|
|
1870
2433
|
}).strict();
|
|
1871
|
-
var configSchema = tileflowMapSchema;
|
|
1872
|
-
var tilesetConfigSchema =
|
|
1873
|
-
id:
|
|
1874
|
-
name:
|
|
1875
|
-
attribution:
|
|
1876
|
-
sourceLayers: sourceLayersSchema.optional()
|
|
1877
|
-
|
|
1878
|
-
var tileflowProjectSchema = z.object({
|
|
1879
|
-
icons: z.record(identifierSchema, iconSetSchema).optional(),
|
|
1880
|
-
themes: z.record(identifierSchema, themeConfigSchema).optional(),
|
|
1881
|
-
tilesets: z.record(identifierSchema, tilesetConfigSchema).optional(),
|
|
1882
|
-
maps: z.record(identifierSchema, configSchema).refine((maps) => {
|
|
1883
|
-
return Object.keys(maps).length > 0;
|
|
1884
|
-
}, "Expected at least one map")
|
|
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()
|
|
1885
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
|
+
);
|
|
1886
2469
|
var projectConfigSchema = tileflowProjectSchema;
|
|
1887
2470
|
function parseTileflowMap(input) {
|
|
1888
2471
|
const parsed = configSchema.parse(input);
|
|
@@ -1939,17 +2522,54 @@ function validateConfig(input) {
|
|
|
1939
2522
|
var validateTileflowConfig = validateConfig;
|
|
1940
2523
|
function isProjectConfigLike(input) {
|
|
1941
2524
|
return Boolean(
|
|
1942
|
-
input && typeof input === "object" && "maps"
|
|
2525
|
+
input && typeof input === "object" && Object.hasOwn(input, "maps") && typeof input.maps === "object"
|
|
1943
2526
|
);
|
|
1944
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
|
+
}
|
|
1945
2553
|
function validateProjectThemeReferences(project) {
|
|
1946
2554
|
const themeNames = new Set(Object.keys(project.themes ?? {}));
|
|
1947
2555
|
const themeMessages = validateThemeExtends(project.themes, themeNames);
|
|
1948
2556
|
const iconMessages = validateProjectIconReferences(project);
|
|
2557
|
+
const sceneMessages = validateProjectSceneReferences(project);
|
|
1949
2558
|
const mapMessages = Object.entries(project.maps).flatMap(
|
|
1950
2559
|
([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
|
|
1951
2560
|
);
|
|
1952
|
-
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
|
+
);
|
|
1953
2573
|
}
|
|
1954
2574
|
function validateMapThemeReference(config, themeNames, path) {
|
|
1955
2575
|
if (!config.theme) {
|
|
@@ -2019,7 +2639,7 @@ function validateProjectIconReferences(project) {
|
|
|
2019
2639
|
const iconSets = project.icons ?? {};
|
|
2020
2640
|
for (const [iconSetName, iconSet] of Object.entries(iconSets)) {
|
|
2021
2641
|
const extendedName = iconSetReference(iconSet);
|
|
2022
|
-
if (extendedName && !iconSets
|
|
2642
|
+
if (extendedName && !Object.hasOwn(iconSets, extendedName)) {
|
|
2023
2643
|
messages.push({
|
|
2024
2644
|
level: "error",
|
|
2025
2645
|
path: iconSetExtendsPath(`icons.${iconSetName}`, iconSet),
|
|
@@ -2041,7 +2661,7 @@ function validateProjectIconReferences(project) {
|
|
|
2041
2661
|
if (!isIconSetObject(mapConfig.icons) || !mapConfig.icons.extends) {
|
|
2042
2662
|
continue;
|
|
2043
2663
|
}
|
|
2044
|
-
if (!iconSets
|
|
2664
|
+
if (!Object.hasOwn(iconSets, mapConfig.icons.extends)) {
|
|
2045
2665
|
messages.push({
|
|
2046
2666
|
level: "error",
|
|
2047
2667
|
path: `maps.${mapName}.icons.extends`,
|
|
@@ -2055,8 +2675,8 @@ function findIconSetCycle(iconSetName, iconSets, path) {
|
|
|
2055
2675
|
if (path.includes(iconSetName)) {
|
|
2056
2676
|
return [...path, iconSetName];
|
|
2057
2677
|
}
|
|
2058
|
-
const nextIconSetName = iconSetReference(iconSets?.[iconSetName]);
|
|
2059
|
-
if (!nextIconSetName || !iconSets
|
|
2678
|
+
const nextIconSetName = Object.hasOwn(iconSets ?? {}, iconSetName) ? iconSetReference(iconSets?.[iconSetName]) : void 0;
|
|
2679
|
+
if (!nextIconSetName || !Object.hasOwn(iconSets ?? {}, nextIconSetName)) {
|
|
2060
2680
|
return void 0;
|
|
2061
2681
|
}
|
|
2062
2682
|
return findIconSetCycle(nextIconSetName, iconSets, [...path, iconSetName]);
|
|
@@ -2140,6 +2760,7 @@ function applyOsmBrightControls(layers, options) {
|
|
|
2140
2760
|
return applyPoiLayerControls(
|
|
2141
2761
|
layer,
|
|
2142
2762
|
options.poi,
|
|
2763
|
+
options.colors,
|
|
2143
2764
|
options.customIconSpriteId,
|
|
2144
2765
|
options.customIconMapping
|
|
2145
2766
|
);
|
|
@@ -2166,7 +2787,8 @@ function applyRoadLayerControls(layer, roads2) {
|
|
|
2166
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;
|
|
2167
2788
|
if (type === "line") {
|
|
2168
2789
|
paint["line-opacity"] = scaleStyleValue(paint["line-opacity"], opacityScale, 1);
|
|
2169
|
-
|
|
2790
|
+
const scaledWidth = scaleStyleValue(paint["line-width"], widthScale, 1);
|
|
2791
|
+
paint["line-width"] = roads2.widthScale ? multiplyStyleValue(scaledWidth, roadClassWidthScaleExpression(roads2.widthScale)) : scaledWidth;
|
|
2170
2792
|
nextLayer.paint = paint;
|
|
2171
2793
|
}
|
|
2172
2794
|
if (type === "fill") {
|
|
@@ -2187,6 +2809,22 @@ function applyRoadLayerControls(layer, roads2) {
|
|
|
2187
2809
|
}
|
|
2188
2810
|
return nextLayer;
|
|
2189
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
|
+
}
|
|
2190
2828
|
function isRoadLayerVisible(group, roads2) {
|
|
2191
2829
|
if (roads2.detail === "none") return false;
|
|
2192
2830
|
if (group === "paths") return roads2.extras.paths;
|
|
@@ -2246,7 +2884,9 @@ function applyLabelLayerControls(layer, labels2, roads2) {
|
|
|
2246
2884
|
layout["text-field"] = labelTextField(labels2.language);
|
|
2247
2885
|
}
|
|
2248
2886
|
if (group === "roads") {
|
|
2249
|
-
const allowedClasses = (roads2 ? visibleRoadLabelClasses(detail, roads2) : roadLabelClasses(detail)
|
|
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));
|
|
2250
2890
|
if (allowedClasses.length === 0) {
|
|
2251
2891
|
return setLayerVisibility(layer, "none");
|
|
2252
2892
|
}
|
|
@@ -2262,15 +2902,24 @@ function applyLabelLayerControls(layer, labels2, roads2) {
|
|
|
2262
2902
|
nextLayer.layout = layout;
|
|
2263
2903
|
return nextLayer;
|
|
2264
2904
|
}
|
|
2265
|
-
function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMapping) {
|
|
2905
|
+
function applyPoiLayerControls(layer, poi2, colors, customIconSpriteId2, customIconMapping) {
|
|
2266
2906
|
if (poi2.mode === "none") {
|
|
2267
2907
|
return setLayerVisibility(layer, "none");
|
|
2268
2908
|
}
|
|
2269
2909
|
const nextLayer = setLayerVisibility(layer, "visible");
|
|
2270
2910
|
const layout = isRecord3(nextLayer.layout) ? { ...nextLayer.layout } : {};
|
|
2911
|
+
const paint = isRecord3(nextLayer.paint) ? { ...nextLayer.paint } : {};
|
|
2271
2912
|
nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, poi2.minZoom);
|
|
2272
2913
|
if (poi2.categories && sourceLayerValue(nextLayer) === "poi") {
|
|
2273
|
-
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);
|
|
2274
2923
|
}
|
|
2275
2924
|
if (poi2.labels === "none") {
|
|
2276
2925
|
delete layout["text-field"];
|
|
@@ -2280,10 +2929,21 @@ function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMappi
|
|
|
2280
2929
|
if (poi2.icons === false) {
|
|
2281
2930
|
delete layout["icon-image"];
|
|
2282
2931
|
} else if ("icon-image" in layout || customIconSpriteId2) {
|
|
2283
|
-
layout["icon-image"] = poiIconImageExpression(
|
|
2284
|
-
|
|
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);
|
|
2285
2944
|
}
|
|
2286
2945
|
nextLayer.layout = layout;
|
|
2946
|
+
nextLayer.paint = paint;
|
|
2287
2947
|
return nextLayer;
|
|
2288
2948
|
}
|
|
2289
2949
|
function labelGroupDetail(labels2, group) {
|
|
@@ -4909,8 +5569,8 @@ var style_default = {
|
|
|
4909
5569
|
};
|
|
4910
5570
|
|
|
4911
5571
|
// src/templates/osm-bright/theme.ts
|
|
4912
|
-
function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
4913
|
-
if (!theme.custom && theme.name === "light") {
|
|
5572
|
+
function applyOsmBrightTheme(layers, colors, theme, typography, typographyOverridden = theme.typographyOverridden) {
|
|
5573
|
+
if (!theme.custom && theme.name === "light" && !typographyOverridden) {
|
|
4914
5574
|
return layers;
|
|
4915
5575
|
}
|
|
4916
5576
|
return layers.map((layer) => {
|
|
@@ -4950,8 +5610,8 @@ function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
|
4950
5610
|
paint["text-halo-color"] = colors.labels.halo;
|
|
4951
5611
|
paintChanged = true;
|
|
4952
5612
|
}
|
|
4953
|
-
if (
|
|
4954
|
-
layout["text-font"] = textFont(typography);
|
|
5613
|
+
if (typographyOverridden && "text-font" in layout) {
|
|
5614
|
+
layout["text-font"] = textFont(typography, typographyDomain(id, sourceLayer));
|
|
4955
5615
|
layoutChanged = true;
|
|
4956
5616
|
}
|
|
4957
5617
|
}
|
|
@@ -4964,6 +5624,16 @@ function applyOsmBrightTheme(layers, colors, theme, typography) {
|
|
|
4964
5624
|
return nextLayer;
|
|
4965
5625
|
});
|
|
4966
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
|
+
}
|
|
4967
5637
|
function osmBrightFillColor(id, sourceLayer, colors, theme) {
|
|
4968
5638
|
if (sourceLayer === "water" || id.includes("water")) {
|
|
4969
5639
|
return hasBaseColor(theme, ["water"]) || hasNestedModuleColor(theme, "hydro", "water") ? colors.hydro.water : void 0;
|
|
@@ -5183,16 +5853,22 @@ function createOsmBrightStyle(options) {
|
|
|
5183
5853
|
layers,
|
|
5184
5854
|
options.colors,
|
|
5185
5855
|
options.theme,
|
|
5186
|
-
options.typography
|
|
5856
|
+
options.typography,
|
|
5857
|
+
options.typographyOverridden
|
|
5187
5858
|
);
|
|
5188
5859
|
const controlledLayers = applyOsmBrightControls(themedLayers, {
|
|
5860
|
+
colors: options.colors,
|
|
5189
5861
|
customIconMapping: options.customIconMapping,
|
|
5190
5862
|
customIconSpriteId: options.customIconSpriteId,
|
|
5191
5863
|
labels: options.labelsModule,
|
|
5192
5864
|
poi: options.poiModule,
|
|
5193
5865
|
roads: options.roadsModule
|
|
5194
5866
|
});
|
|
5195
|
-
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;
|
|
5196
5872
|
return {
|
|
5197
5873
|
...template,
|
|
5198
5874
|
name: options.name,
|
|
@@ -5222,6 +5898,13 @@ function createOsmBrightStyle(options) {
|
|
|
5222
5898
|
}
|
|
5223
5899
|
};
|
|
5224
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
|
+
}
|
|
5225
5908
|
function cloneJson(value) {
|
|
5226
5909
|
return JSON.parse(JSON.stringify(value));
|
|
5227
5910
|
}
|
|
@@ -5236,7 +5919,9 @@ function remapOsmBrightSource(layer, sourceId) {
|
|
|
5236
5919
|
}
|
|
5237
5920
|
|
|
5238
5921
|
// src/compiler.ts
|
|
5922
|
+
var tileflowHostedNotoSansWeights = ["regular", "bold"];
|
|
5239
5923
|
var defaultSourceLayers = {
|
|
5924
|
+
landcover: "landcover",
|
|
5240
5925
|
landuse: "landuse",
|
|
5241
5926
|
park: "park",
|
|
5242
5927
|
water: "water",
|
|
@@ -5302,6 +5987,15 @@ function createStyle(input = {}, options = {}) {
|
|
|
5302
5987
|
theme.preset.roads ?? defaultConfig.roads
|
|
5303
5988
|
);
|
|
5304
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
|
+
};
|
|
5305
5999
|
const resolvedLabels = resolveLabels(
|
|
5306
6000
|
config.labels,
|
|
5307
6001
|
labelsModule,
|
|
@@ -5309,14 +6003,18 @@ function createStyle(input = {}, options = {}) {
|
|
|
5309
6003
|
);
|
|
5310
6004
|
const { mode: resolvedLabelsMode, ...resolvedLabelsModule } = resolvedLabels;
|
|
5311
6005
|
const resolvedPoi = resolvePoi(config.poi, poiModule, theme.preset.poi ?? defaultConfig.poi);
|
|
6006
|
+
const { mode: poi2, ...resolvedPoiModule } = resolvedPoi;
|
|
5312
6007
|
const labels2 = resolvedLabelsMode;
|
|
5313
|
-
const poi2 = resolvedPoi.mode;
|
|
5314
6008
|
const buildings = config.buildings ?? "flat";
|
|
5315
6009
|
const colors = resolveColors(config.colors, theme.colors, {}, theme.modules);
|
|
5316
6010
|
const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
|
|
5317
6011
|
const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
|
|
5318
6012
|
const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
|
|
5319
|
-
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
|
+
);
|
|
5320
6018
|
const typography = resolveTypography(config.typography, theme.typography);
|
|
5321
6019
|
const terrain = resolveTerrain(config.terrain, tileBaseUrl);
|
|
5322
6020
|
const rendererPreference = config.renderer ?? "auto";
|
|
@@ -5324,10 +6022,11 @@ function createStyle(input = {}, options = {}) {
|
|
|
5324
6022
|
const useOsmBrightTemplate = rendererPreference === "osm-bright" || rendererPreference === "auto" && canUseOsmBrightTemplate;
|
|
5325
6023
|
if (rendererPreference === "osm-bright" && !canUseOsmBrightTemplate) {
|
|
5326
6024
|
throw new Error(
|
|
5327
|
-
'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.'
|
|
5328
6026
|
);
|
|
5329
6027
|
}
|
|
5330
6028
|
const glyphs = config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`;
|
|
6029
|
+
validateHostedTypography(typography, glyphs, tileBaseUrl);
|
|
5331
6030
|
const iconSet = resolveIconSet(config.icons, options.iconSets);
|
|
5332
6031
|
const iconSprite = iconSet?.sprite;
|
|
5333
6032
|
const basemapSprite = basemap.sprite ?? (basemap.type === "osm" ? `${tileBaseUrl}/sprites/osm-bright/sprite` : void 0);
|
|
@@ -5340,32 +6039,38 @@ function createStyle(input = {}, options = {}) {
|
|
|
5340
6039
|
"tileflow:themeMode": theme.mode,
|
|
5341
6040
|
"tileflow:density": density,
|
|
5342
6041
|
"tileflow:roads": resolvedRoadMode,
|
|
5343
|
-
"tileflow:roadsModule":
|
|
6042
|
+
...roadModule ? { "tileflow:roadsModule": resolvedRoadModuleMetadata } : {},
|
|
5344
6043
|
"tileflow:labels": labels2,
|
|
5345
|
-
"tileflow:labelsModule":
|
|
6044
|
+
...labelsModule ? { "tileflow:labelsModule": resolvedLabelsModule } : {},
|
|
5346
6045
|
"tileflow:poi": poi2,
|
|
5347
|
-
"tileflow:poiModule":
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
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
|
+
} : {},
|
|
5353
6054
|
"tileflow:buildings": buildings,
|
|
6055
|
+
...config.buildingStyle ? { "tileflow:buildingStyle": config.buildingStyle } : {},
|
|
5354
6056
|
"tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
|
|
5355
6057
|
"tileflow:themeModules": Object.keys(theme.moduleOverrides),
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
6058
|
+
...terrain ? {
|
|
6059
|
+
"tileflow:terrain": {
|
|
6060
|
+
encoding: terrain.source.encoding,
|
|
6061
|
+
mode: terrain.mode,
|
|
6062
|
+
sourceId: terrain.sourceId
|
|
6063
|
+
}
|
|
6064
|
+
} : {},
|
|
5361
6065
|
"tileflow:renderer": useOsmBrightTemplate ? "osm-bright" : "generated",
|
|
5362
6066
|
"tileflow:rendererPreference": rendererPreference,
|
|
5363
|
-
"tileflow:typography": typography,
|
|
6067
|
+
"tileflow:typography": typographyMetadata(typography),
|
|
5364
6068
|
"tileflow:colors": colors,
|
|
5365
6069
|
"tileflow:tileBaseUrl": tileBaseUrl,
|
|
5366
6070
|
"tileflow:tileset": tileset,
|
|
6071
|
+
...tilesetVersion ? { "tileflow:tilesetVersion": tilesetVersion } : {},
|
|
5367
6072
|
"tileflow:tiles": "tileflow",
|
|
5368
|
-
"tileflow:view": config.view
|
|
6073
|
+
...config.view ? { "tileflow:view": config.view } : {}
|
|
5369
6074
|
};
|
|
5370
6075
|
if (useOsmBrightTemplate) {
|
|
5371
6076
|
const style2 = createOsmBrightStyle({
|
|
@@ -5380,6 +6085,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5380
6085
|
theme,
|
|
5381
6086
|
tilesUrl,
|
|
5382
6087
|
typography,
|
|
6088
|
+
typographyOverridden: Boolean(config.typography) || theme.typographyOverridden,
|
|
5383
6089
|
labelsModule: resolvedLabels,
|
|
5384
6090
|
poi: poi2,
|
|
5385
6091
|
poiModule: resolvedPoi,
|
|
@@ -5402,6 +6108,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5402
6108
|
);
|
|
5403
6109
|
const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
|
|
5404
6110
|
buildings,
|
|
6111
|
+
buildingStyle: config.buildingStyle,
|
|
5405
6112
|
density,
|
|
5406
6113
|
labels: resolvedLabels,
|
|
5407
6114
|
poi: resolvedPoi,
|
|
@@ -5439,7 +6146,7 @@ function createStyle(input = {}, options = {}) {
|
|
|
5439
6146
|
return style;
|
|
5440
6147
|
}
|
|
5441
6148
|
function createStyleFromProject(projectConfig, mapName, options = {}) {
|
|
5442
|
-
const config = projectConfig.maps[mapName];
|
|
6149
|
+
const config = Object.hasOwn(projectConfig.maps, mapName) ? projectConfig.maps[mapName] : void 0;
|
|
5443
6150
|
if (!config) {
|
|
5444
6151
|
throw new Error(`Unknown Tileflow map: ${mapName}`);
|
|
5445
6152
|
}
|
|
@@ -5458,11 +6165,13 @@ function createStyleFromProject(projectConfig, mapName, options = {}) {
|
|
|
5458
6165
|
...baseBasemap,
|
|
5459
6166
|
tileset: resolvedTileset,
|
|
5460
6167
|
attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
|
|
6168
|
+
version: baseBasemap.version ?? tilesetConfig?.version,
|
|
5461
6169
|
...resolvedBasemapSourceLayers ? { sourceLayers: resolvedBasemapSourceLayers } : {}
|
|
5462
6170
|
},
|
|
5463
6171
|
tiles: {
|
|
5464
6172
|
...tileOptions,
|
|
5465
6173
|
tileset: resolvedTileset,
|
|
6174
|
+
version: tileOptions.version ?? baseBasemap.version ?? tilesetConfig?.version,
|
|
5466
6175
|
...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
|
|
5467
6176
|
}
|
|
5468
6177
|
},
|
|
@@ -5501,7 +6210,7 @@ function resolveSourceLayers(mapLayers, tilesetLayers) {
|
|
|
5501
6210
|
}
|
|
5502
6211
|
function isOsmBrightTemplateCompatible(config, basemap, theme) {
|
|
5503
6212
|
const templateTheme = theme.custom || theme.name === "light" || theme.name === "dark";
|
|
5504
|
-
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(
|
|
5505
6214
|
(moduleConfig) => moduleConfig.type !== "styleOverride" && moduleConfig.type !== "labels" && moduleConfig.type !== "poi" && moduleConfig.type !== "roads"
|
|
5506
6215
|
) && !basemap.sourceLayers && !config.tiles?.sourceLayers;
|
|
5507
6216
|
}
|
|
@@ -5519,6 +6228,23 @@ function hasEffectiveColorOverrides(colors) {
|
|
|
5519
6228
|
return typeof value === "string";
|
|
5520
6229
|
});
|
|
5521
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
|
+
}
|
|
5522
6248
|
function isRecord5(value) {
|
|
5523
6249
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5524
6250
|
}
|
|
@@ -5531,6 +6257,12 @@ function normalizeStyleBaseUrl(value) {
|
|
|
5531
6257
|
function normalizeUrl(value) {
|
|
5532
6258
|
return value.replace(/\/+$/, "");
|
|
5533
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
|
+
}
|
|
5534
6266
|
function resolveIconSet(icons, iconSets, path = []) {
|
|
5535
6267
|
if (!icons) {
|
|
5536
6268
|
return void 0;
|
|
@@ -5573,12 +6305,455 @@ function resolveSprite(basemapSprite, iconSprite) {
|
|
|
5573
6305
|
function isSpriteReference(value) {
|
|
5574
6306
|
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:");
|
|
5575
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
|
+
}
|
|
5576
6325
|
|
|
5577
6326
|
// src/config.ts
|
|
5578
6327
|
function defineTileflow(config) {
|
|
5579
6328
|
return config;
|
|
5580
6329
|
}
|
|
5581
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
|
+
|
|
5582
6757
|
// src/runtime.ts
|
|
5583
6758
|
var defaultTileflowManifestUrl = "/tileflow/manifest.json";
|
|
5584
6759
|
var defaultTileflowStyleBaseUrl = "http://localhost:3333";
|
|
@@ -5857,6 +7032,7 @@ function getTileflowLocationOrigin() {
|
|
|
5857
7032
|
return window.location.origin;
|
|
5858
7033
|
}
|
|
5859
7034
|
export {
|
|
7035
|
+
compareCodeUnits,
|
|
5860
7036
|
configSchema,
|
|
5861
7037
|
createManifest,
|
|
5862
7038
|
createStyle,
|
|
@@ -5865,14 +7041,21 @@ export {
|
|
|
5865
7041
|
defaultTileflowManifestUrl,
|
|
5866
7042
|
defaultTileflowStyleBaseUrl,
|
|
5867
7043
|
defineTileflow,
|
|
7044
|
+
diffTileflowIconMappings,
|
|
7045
|
+
diffTileflowIconPackageManifests,
|
|
5868
7046
|
getDefaultColors2 as getDefaultColors,
|
|
5869
7047
|
getDefaultTileflowStyleBaseUrl,
|
|
5870
7048
|
getTileflowAnalyticsApiUrl,
|
|
7049
|
+
hashTileflowIconPackageManifest,
|
|
7050
|
+
hashTileflowRenderedIconPixels,
|
|
5871
7051
|
inferTileflowAnalyticsFromStyleUrl,
|
|
7052
|
+
inspectTileflowIconReferences,
|
|
5872
7053
|
isTileflowLocalDevHost,
|
|
5873
7054
|
labels,
|
|
5874
7055
|
loadTileflowManifest,
|
|
5875
7056
|
mergeTileflowAnalytics,
|
|
7057
|
+
normalizeTileflowCaptureId,
|
|
7058
|
+
normalizeTileflowCaptureScene,
|
|
5876
7059
|
normalizeTileflowStaticImageSize,
|
|
5877
7060
|
normalizeTileflowUrl,
|
|
5878
7061
|
osm,
|
|
@@ -5880,17 +7063,43 @@ export {
|
|
|
5880
7063
|
parseTileflowProject,
|
|
5881
7064
|
poi,
|
|
5882
7065
|
resolveTileflowAnalyticsRequestUrl,
|
|
7066
|
+
resolveTileflowIconMapping,
|
|
5883
7067
|
resolveTileflowManifestMap,
|
|
5884
7068
|
resolveTileflowMapMode,
|
|
5885
7069
|
resolveTileflowRuntimeStyle,
|
|
5886
7070
|
resolveTileflowStaticImageUrl,
|
|
5887
7071
|
resolveTileflowStyleUrl,
|
|
5888
7072
|
roads,
|
|
7073
|
+
serializeCanonicalJson,
|
|
7074
|
+
serializeTileflowIconPackageManifest,
|
|
7075
|
+
sha256Hex,
|
|
5889
7076
|
shouldLoadTileflowManifest,
|
|
5890
7077
|
startTileflowSession,
|
|
5891
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,
|
|
5892
7100
|
tileflowMapSchema,
|
|
5893
7101
|
tileflowProjectSchema,
|
|
7102
|
+
tileflowRenderedIconManifestEntrySchema,
|
|
5894
7103
|
tileflowThemeSchema,
|
|
5895
7104
|
validateConfig,
|
|
5896
7105
|
validateTileflowConfig
|