@tileflow/core 0.1.0-alpha.35 → 0.1.0-alpha.36
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 +16 -17
- package/dist/browser.d.ts +600 -427
- package/dist/browser.js +1633 -431
- package/dist/build.d.ts +3 -3
- package/dist/build.js +1734 -1093
- package/dist/config-reference.json +2901 -1906
- package/dist/{icon-composition-DSjIKmrK.d.ts → icon-composition-sN3JkXdb.d.ts} +291 -93
- package/dist/index.d.ts +201 -7
- package/dist/index.js +4691 -3509
- package/dist/{manifest-types-Bzxon3dx.d.ts → manifest-types-jrBdBrES.d.ts} +29 -8
- package/dist/manifest.d.ts +2 -2
- package/dist/native.js +57 -57
- package/dist/runtime.d.ts +1 -1
- package/dist/worker/buildings-3d-worker.global.js +1 -0
- package/docs/authoring-model.md +8 -3
- package/docs/boundary-areas-and-choropleths.md +217 -0
- package/docs/buildings-3d.md +102 -21
- package/docs/cartographic-defaults.md +142 -0
- package/docs/data-is-separate-from-design.md +54 -2
- package/docs/hosted-session-authorization.md +2 -4
- package/docs/map-and-terrain-examples.md +10 -3
- package/docs/street-lights.md +22 -6
- package/docs/sun-and-light.md +42 -2
- package/docs/themes-and-module-styles.md +66 -0
- package/docs/trees-3d.md +22 -9
- package/package.json +2 -2
package/dist/build.js
CHANGED
|
@@ -52,13 +52,14 @@ var tileflowDefaultBuildings3DDesign = Object.freeze({
|
|
|
52
52
|
layers: Object.freeze({ building: "building", places: "poi" }),
|
|
53
53
|
color: "#d8d0c0",
|
|
54
54
|
edges: 0,
|
|
55
|
-
|
|
55
|
+
edgeRadius: 0.8,
|
|
56
|
+
lighting: "sun",
|
|
56
57
|
colorVariation: 0,
|
|
57
58
|
verticalGradient: true,
|
|
58
59
|
nightLight: 0,
|
|
59
60
|
windows: false,
|
|
60
|
-
shadows:
|
|
61
|
-
occlusion:
|
|
61
|
+
shadows: true,
|
|
62
|
+
occlusion: true
|
|
62
63
|
});
|
|
63
64
|
function tileflowBuildingsMetadata(design) {
|
|
64
65
|
return { version: 1, ...design };
|
|
@@ -212,6 +213,7 @@ function tileflowWorldV1Schema(options = {}) {
|
|
|
212
213
|
...options,
|
|
213
214
|
capabilities: { ...options.capabilities, bathymetry: true, globalLandcover: true },
|
|
214
215
|
fields: {
|
|
216
|
+
hedge: "hedge",
|
|
215
217
|
shieldKind: "shield_kind",
|
|
216
218
|
shieldLineLengthMeters: "shield_line_length_m",
|
|
217
219
|
shieldNetwork: "shield_network",
|
|
@@ -225,6 +227,7 @@ function tileflowWorldV1Schema(options = {}) {
|
|
|
225
227
|
roadShield: "transportation_shield",
|
|
226
228
|
sidewalk: "sidewalk",
|
|
227
229
|
streetFurniture: "street_furniture",
|
|
230
|
+
trafficControl: "traffic_control",
|
|
228
231
|
...options.layers
|
|
229
232
|
},
|
|
230
233
|
semantics: { ...options.semantics, parkLayer: "protected-only" }
|
|
@@ -650,237 +653,98 @@ function compareCodeUnits(left, right) {
|
|
|
650
653
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
651
654
|
}
|
|
652
655
|
|
|
653
|
-
// src/
|
|
654
|
-
var
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
rangeNm: "range_nm",
|
|
705
|
-
scale: "scale",
|
|
706
|
-
subclass: "subclass",
|
|
707
|
-
update: "update"
|
|
708
|
-
}),
|
|
709
|
-
geometryTypes: Object.freeze({
|
|
710
|
-
aids: Object.freeze(["Point"]),
|
|
711
|
-
coverage: Object.freeze(["Polygon"]),
|
|
712
|
-
hazards: Object.freeze(["Point", "Polygon"]),
|
|
713
|
-
lights: Object.freeze(["Point"]),
|
|
714
|
-
navigationAreas: Object.freeze(["Polygon"]),
|
|
715
|
-
reefs: Object.freeze(["Polygon"]),
|
|
716
|
-
soundings: Object.freeze(["Point"]),
|
|
717
|
-
wrecks: Object.freeze(["Point", "Polygon"])
|
|
718
|
-
}),
|
|
719
|
-
layers: Object.freeze({
|
|
720
|
-
aids: "aid",
|
|
721
|
-
coverage: "coverage",
|
|
722
|
-
hazards: "hazard",
|
|
723
|
-
lights: "light",
|
|
724
|
-
navigationAreas: "navigation_area",
|
|
725
|
-
reefs: "reef",
|
|
726
|
-
soundings: "sounding",
|
|
727
|
-
wrecks: "wreck"
|
|
728
|
-
}),
|
|
729
|
-
product: "nautical-v1",
|
|
730
|
-
schemaVersion: 1
|
|
731
|
-
});
|
|
732
|
-
var tileflowBathymetryNameLayerLimits = Object.freeze({ maxZoomRanges: 128 });
|
|
733
|
-
function resolveMarine(marine, apiBaseUrl = "https://api.tileflow.dev") {
|
|
734
|
-
if (marine === void 0) return void 0;
|
|
735
|
-
if (typeof marine === "string") {
|
|
736
|
-
switch (marine) {
|
|
737
|
-
case "none":
|
|
738
|
-
return {};
|
|
739
|
-
case "bathymetry":
|
|
740
|
-
return { bathymetry: resolveBathymetry({}, apiBaseUrl) };
|
|
741
|
-
case "nautical":
|
|
742
|
-
return { nautical: resolveNauticalSource({}, apiBaseUrl) };
|
|
743
|
-
case "chart":
|
|
744
|
-
return {
|
|
745
|
-
bathymetry: resolveBathymetry({}, apiBaseUrl),
|
|
746
|
-
nautical: resolveNauticalSource({}, apiBaseUrl)
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return {
|
|
751
|
-
...marine.bathymetry === false ? {} : { bathymetry: resolveBathymetry(marine.bathymetry ?? {}, apiBaseUrl) },
|
|
752
|
-
...marine.nautical === false ? {} : { nautical: resolveNauticalSource(marine.nautical ?? {}, apiBaseUrl) }
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
function resolveBathymetry(input, apiBaseUrl) {
|
|
756
|
-
const advanced = "type" in input && input.type === "bathymetry" ? input : void 0;
|
|
757
|
-
const display = advanced?.display ?? "bands";
|
|
758
|
-
const usesVector = display === "bands" || display === "hybrid";
|
|
759
|
-
const usesRelief = display === "relief" || display === "hybrid";
|
|
760
|
-
const vectorConfig = {
|
|
761
|
-
...input.attribution === void 0 ? {} : { attribution: input.attribution },
|
|
762
|
-
...input.sourceId === void 0 ? {} : { sourceId: input.sourceId },
|
|
763
|
-
...input.url === void 0 ? {} : { url: input.url }
|
|
764
|
-
};
|
|
765
|
-
const reliefConfig = advanced?.relief === false ? void 0 : advanced?.relief;
|
|
766
|
-
const reliefVisible = usesRelief && reliefConfig?.visible !== false && !(typeof reliefConfig?.opacity === "number" && reliefConfig.opacity === 0);
|
|
767
|
-
return {
|
|
768
|
-
...advanced?.bands === void 0 ? {} : { bands: advanced.bands },
|
|
769
|
-
...advanced?.contours === void 0 ? {} : { contours: advanced.contours },
|
|
770
|
-
display,
|
|
771
|
-
...advanced?.labels === void 0 ? {} : { labels: advanced.labels },
|
|
772
|
-
...advanced?.vectorLayers === void 0 ? {} : { vectorLayers: cloneJson(advanced.vectorLayers) },
|
|
773
|
-
...reliefVisible ? { relief: resolveBathymetryRelief(reliefConfig ?? {}, apiBaseUrl) } : {},
|
|
774
|
-
...usesVector ? {
|
|
775
|
-
vector: resolveMarineSource({
|
|
776
|
-
config: vectorConfig,
|
|
777
|
-
defaultPath: "/tiles/bathymetry/tiles.json",
|
|
778
|
-
defaultSourceId: tileflowBathymetrySourceId,
|
|
779
|
-
kind: "tileflow-bathymetry",
|
|
780
|
-
product: tileflowBathymetryV1Schema.product,
|
|
781
|
-
apiBaseUrl
|
|
782
|
-
})
|
|
783
|
-
} : {}
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
function resolveBathymetryRelief(style, apiBaseUrl) {
|
|
787
|
-
const sourceId = validateSourceId(style.sourceId ?? tileflowBathymetryDemSourceId);
|
|
788
|
-
const url = validateBathymetryDemUrl(
|
|
789
|
-
style.url ?? new URL("/tiles/bathymetry/dem/tiles.json", normalizeApiBaseUrl2(apiBaseUrl)).toString()
|
|
790
|
-
);
|
|
791
|
-
const encoding = style.encoding ?? "terrarium";
|
|
792
|
-
const tileSize = style.tileSize ?? 512;
|
|
793
|
-
return {
|
|
794
|
-
identity: {
|
|
795
|
-
encoding,
|
|
796
|
-
kind: "tileflow-bathymetry-dem",
|
|
797
|
-
product: tileflowBathymetryV1Schema.product,
|
|
798
|
-
schemaVersion: 1,
|
|
799
|
-
sourceId,
|
|
800
|
-
tileSize,
|
|
801
|
-
url
|
|
802
|
-
},
|
|
803
|
-
source: {
|
|
804
|
-
...style.attribution ? { attribution: validateAttribution(style.attribution) } : {},
|
|
805
|
-
encoding,
|
|
806
|
-
tileSize,
|
|
807
|
-
type: "raster-dem",
|
|
808
|
-
url
|
|
809
|
-
},
|
|
810
|
-
sourceId,
|
|
811
|
-
style: cloneJson(style)
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
function resolveNauticalSource(config, apiBaseUrl) {
|
|
815
|
-
return resolveMarineSource({
|
|
816
|
-
config,
|
|
817
|
-
defaultPath: "/tiles/nautical/tiles.json",
|
|
818
|
-
defaultSourceId: tileflowNauticalSourceId,
|
|
819
|
-
kind: "tileflow-nautical",
|
|
820
|
-
product: tileflowNauticalV1Schema.product,
|
|
821
|
-
apiBaseUrl
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
function resolveMarineSource(options) {
|
|
825
|
-
const sourceId = validateSourceId(options.config.sourceId ?? options.defaultSourceId);
|
|
826
|
-
const url = validatePublicVectorUrl(
|
|
827
|
-
options.config.url ?? new URL(options.defaultPath, normalizeApiBaseUrl2(options.apiBaseUrl)).toString()
|
|
828
|
-
);
|
|
829
|
-
const identity = {
|
|
830
|
-
kind: options.kind,
|
|
831
|
-
product: options.product,
|
|
832
|
-
schemaVersion: 1,
|
|
833
|
-
sourceId,
|
|
834
|
-
url
|
|
835
|
-
};
|
|
836
|
-
return {
|
|
837
|
-
identity,
|
|
838
|
-
source: {
|
|
839
|
-
...options.config.attribution ? { attribution: validateAttribution(options.config.attribution) } : {},
|
|
840
|
-
type: "vector",
|
|
841
|
-
url
|
|
842
|
-
},
|
|
843
|
-
sourceId
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
function validateSourceId(value) {
|
|
847
|
-
const normalized = value.trim();
|
|
848
|
-
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/u.test(normalized) || normalized !== value) {
|
|
849
|
-
throw new Error(
|
|
850
|
-
"Tileflow marine sourceId must be a portable identifier of at most 64 characters."
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
return normalized;
|
|
656
|
+
// src/cartography/contributions.ts
|
|
657
|
+
var tileflowLayerSlots = [
|
|
658
|
+
"background",
|
|
659
|
+
"land",
|
|
660
|
+
"terrain",
|
|
661
|
+
"hydro",
|
|
662
|
+
"building-areas",
|
|
663
|
+
"transport-areas",
|
|
664
|
+
"transport-tunnel-shadow",
|
|
665
|
+
"transport-tunnel-casing",
|
|
666
|
+
"transport-tunnel-fill",
|
|
667
|
+
"transport-pedestrian-areas",
|
|
668
|
+
"aeroways",
|
|
669
|
+
"transport-surface-shadow",
|
|
670
|
+
"transport-surface-casing",
|
|
671
|
+
"transport-surface-fill",
|
|
672
|
+
"transport-bridge-shadow",
|
|
673
|
+
"transport-bridge-casing",
|
|
674
|
+
"transport-bridge-fill",
|
|
675
|
+
"transport-symbols",
|
|
676
|
+
"boundaries",
|
|
677
|
+
"buildings",
|
|
678
|
+
"vegetation",
|
|
679
|
+
"symbols"
|
|
680
|
+
];
|
|
681
|
+
var tileflowLayerBands = [
|
|
682
|
+
"land",
|
|
683
|
+
"relief",
|
|
684
|
+
"water",
|
|
685
|
+
"roads",
|
|
686
|
+
"boundaries",
|
|
687
|
+
"buildings",
|
|
688
|
+
"vegetation",
|
|
689
|
+
"labels"
|
|
690
|
+
];
|
|
691
|
+
var bandSlots = {
|
|
692
|
+
land: ["background", "land"],
|
|
693
|
+
relief: ["terrain", "terrain"],
|
|
694
|
+
water: ["hydro", "hydro"],
|
|
695
|
+
roads: ["transport-areas", "transport-symbols"],
|
|
696
|
+
boundaries: ["boundaries", "boundaries"],
|
|
697
|
+
buildings: ["buildings", "buildings"],
|
|
698
|
+
vegetation: ["vegetation", "vegetation"],
|
|
699
|
+
labels: ["symbols", "symbols"]
|
|
700
|
+
};
|
|
701
|
+
var tileflowBandPlacements = tileflowLayerBands.flatMap(
|
|
702
|
+
(band) => [`below-${band}`, `above-${band}`]
|
|
703
|
+
);
|
|
704
|
+
function tileflowBandPlacementSlot(placement) {
|
|
705
|
+
const [side, band] = splitBandPlacement(placement);
|
|
706
|
+
return bandSlots[band][side === "below" ? 0 : 1];
|
|
854
707
|
}
|
|
855
|
-
function
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
return normalized;
|
|
708
|
+
function tileflowBandPlacementIndex(slotRanks, placement) {
|
|
709
|
+
const [side, band] = splitBandPlacement(placement);
|
|
710
|
+
const [first, last] = bandSlots[band];
|
|
711
|
+
const threshold = side === "below" ? tileflowLayerSlots.indexOf(first) : tileflowLayerSlots.indexOf(last) + 1;
|
|
712
|
+
const index = slotRanks.findIndex((rank) => rank !== void 0 && rank >= threshold);
|
|
713
|
+
return index < 0 ? slotRanks.length : index;
|
|
863
714
|
}
|
|
864
|
-
function
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
}
|
|
871
|
-
return url;
|
|
715
|
+
function splitBandPlacement(placement) {
|
|
716
|
+
const separator = placement.indexOf("-");
|
|
717
|
+
return [
|
|
718
|
+
placement.slice(0, separator),
|
|
719
|
+
placement.slice(separator + 1)
|
|
720
|
+
];
|
|
872
721
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
722
|
+
var tileflowSemanticTargetPattern = /^[a-z][a-z0-9-]*(?:\.[A-Za-z0-9_-]+)*$/u;
|
|
723
|
+
var tileflowRenderStackOperationNamePattern = /^[a-z][A-Za-z0-9_-]*$/u;
|
|
724
|
+
var tileflowCompilerMetadataKeys = Object.freeze({
|
|
725
|
+
owner: "tileflow:compiler-owner",
|
|
726
|
+
slot: "tileflow:compiler-slot",
|
|
727
|
+
target: "tileflow:compiler-target"
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// src/modules/addresses/index.ts
|
|
731
|
+
function addresses(options = {}) {
|
|
732
|
+
return { type: "addresses", ...cloneJson(options) };
|
|
879
733
|
}
|
|
880
734
|
function cloneJson(value) {
|
|
881
735
|
return JSON.parse(JSON.stringify(value));
|
|
882
736
|
}
|
|
883
737
|
|
|
738
|
+
// src/cartography/context.ts
|
|
739
|
+
function typographyTextStyle(typography) {
|
|
740
|
+
return {
|
|
741
|
+
font: typography.font,
|
|
742
|
+
...typography.fallbacks ? { fallbacks: typography.fallbacks } : {},
|
|
743
|
+
...typography.letterSpacing === void 0 ? {} : { letterSpacing: typography.letterSpacing },
|
|
744
|
+
...typography.transform === void 0 ? {} : { transform: typography.transform }
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
884
748
|
// src/cartography/layer-style.ts
|
|
885
749
|
import { Color as Color2 } from "@maplibre/maplibre-gl-style-spec";
|
|
886
750
|
|
|
@@ -1351,494 +1215,194 @@ function applyIconStyle(layer, style) {
|
|
|
1351
1215
|
setPaint(paint, "icon-color", style.color);
|
|
1352
1216
|
setPaint(paint, "icon-halo-blur", style.haloBlur);
|
|
1353
1217
|
setPaint(paint, "icon-halo-color", style.haloColor);
|
|
1354
|
-
setPaint(paint, "icon-halo-width", style.haloWidth);
|
|
1355
|
-
setPaint(paint, "icon-opacity", style.opacity);
|
|
1356
|
-
return applyLayerRange(withLayerParts(layer, layout, paint), style);
|
|
1357
|
-
}
|
|
1358
|
-
function applySymbolPlacement(layer, style) {
|
|
1359
|
-
const layout = { ...asRecord(layer.layout) };
|
|
1360
|
-
if (style.placement !== void 0) layout["symbol-placement"] = style.placement;
|
|
1361
|
-
if (style.priority !== void 0) {
|
|
1362
|
-
layout["symbol-sort-key"] = resolvePriority(style.priority, style.priorityOrder);
|
|
1363
|
-
}
|
|
1364
|
-
setLayout(layout, "symbol-spacing", style.spacing);
|
|
1365
|
-
if (style.zOrder !== void 0) layout["symbol-z-order"] = style.zOrder;
|
|
1366
|
-
return applyLayerRange(withLayerParts(layer, layout), style);
|
|
1367
|
-
}
|
|
1368
|
-
function applySymbolStyle(layer, style) {
|
|
1369
|
-
const range2 = resolveSymbolRange(style);
|
|
1370
|
-
let result = applySymbolPlacement(layer, withoutRange(style));
|
|
1371
|
-
if (style.text) result = applyTextStyle(result, withoutRange(style.text));
|
|
1372
|
-
if (style.icon) result = applyIconStyle(result, withoutRange(style.icon));
|
|
1373
|
-
return applyLayerRange(result, range2);
|
|
1374
|
-
}
|
|
1375
|
-
function createAreaLayers(base, style) {
|
|
1376
|
-
const layers = [];
|
|
1377
|
-
if (style.fill && style.fill.visible !== false) {
|
|
1378
|
-
layers.push({
|
|
1379
|
-
layer: applyFillStyle({ ...base, type: "fill" }, style.fill),
|
|
1380
|
-
phase: "fill"
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
if (style.outline && style.outline.visible !== false) {
|
|
1384
|
-
layers.push({
|
|
1385
|
-
layer: applyLineStyle({ ...base, id: `${base.id}-outline`, type: "line" }, style.outline),
|
|
1386
|
-
phase: "outline"
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
return layers;
|
|
1390
|
-
}
|
|
1391
|
-
function applyCircleStyle(layer, style) {
|
|
1392
|
-
const paint = { ...asRecord(layer.paint) };
|
|
1393
|
-
const layout = { ...asRecord(layer.layout) };
|
|
1394
|
-
setPaint(paint, "circle-blur", style.blur);
|
|
1395
|
-
setPaint(paint, "circle-color", style.color);
|
|
1396
|
-
setPaint(paint, "circle-opacity", style.opacity);
|
|
1397
|
-
if (style.pitchAlignment !== void 0) {
|
|
1398
|
-
paint["circle-pitch-alignment"] = style.pitchAlignment;
|
|
1399
|
-
}
|
|
1400
|
-
if (style.pitchScale !== void 0) paint["circle-pitch-scale"] = style.pitchScale;
|
|
1401
|
-
if (style.priority !== void 0) {
|
|
1402
|
-
layout["circle-sort-key"] = resolvePriority(style.priority, style.priorityOrder);
|
|
1403
|
-
}
|
|
1404
|
-
setPaint(paint, "circle-radius", style.radius);
|
|
1405
|
-
setPaint(paint, "circle-stroke-color", style.strokeColor);
|
|
1406
|
-
setPaint(paint, "circle-stroke-opacity", style.strokeOpacity);
|
|
1407
|
-
setPaint(paint, "circle-stroke-width", style.strokeWidth);
|
|
1408
|
-
setPaint(paint, "circle-translate", style.translate);
|
|
1409
|
-
if (style.translateAnchor !== void 0) {
|
|
1410
|
-
paint["circle-translate-anchor"] = style.translateAnchor;
|
|
1411
|
-
}
|
|
1412
|
-
return applyLayerRange(withLayerParts(layer, layout, paint), style);
|
|
1413
|
-
}
|
|
1414
|
-
function applyExtrusionStyle(layer, style) {
|
|
1415
|
-
const paint = { ...asRecord(layer.paint) };
|
|
1416
|
-
setPaint(paint, "fill-extrusion-base", style.base);
|
|
1417
|
-
setPaint(paint, "fill-extrusion-color", style.color);
|
|
1418
|
-
setPaint(paint, "fill-extrusion-height", style.height);
|
|
1419
|
-
setPaint(paint, "fill-extrusion-opacity", style.opacity);
|
|
1420
|
-
setPaint(paint, "fill-extrusion-pattern", style.pattern);
|
|
1421
|
-
if (style.verticalGradient !== void 0) {
|
|
1422
|
-
paint["fill-extrusion-vertical-gradient"] = style.verticalGradient;
|
|
1423
|
-
}
|
|
1424
|
-
return applyLayerRange(withLayerParts(layer, void 0, paint), style);
|
|
1425
|
-
}
|
|
1426
|
-
function applyLayerRange(layer, range2) {
|
|
1427
|
-
const layout = { ...asRecord(layer.layout) };
|
|
1428
|
-
if (range2.visible !== void 0) layout.visibility = range2.visible ? "visible" : "none";
|
|
1429
|
-
return {
|
|
1430
|
-
...layer,
|
|
1431
|
-
...range2.minZoom === void 0 ? {} : { minzoom: range2.minZoom },
|
|
1432
|
-
...range2.maxZoom === void 0 ? {} : { maxzoom: range2.maxZoom },
|
|
1433
|
-
...Object.keys(layout).length ? { layout } : {}
|
|
1434
|
-
};
|
|
1435
|
-
}
|
|
1436
|
-
function withLayerParts(layer, layout, paint) {
|
|
1437
|
-
return {
|
|
1438
|
-
...layer,
|
|
1439
|
-
...layout && Object.keys(layout).length ? { layout } : {},
|
|
1440
|
-
...paint && Object.keys(paint).length ? { paint } : {}
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
|
-
function resolveSymbolRange(style) {
|
|
1444
|
-
const ranges = [style, style.text, style.icon].filter(
|
|
1445
|
-
(value) => value !== void 0
|
|
1446
|
-
);
|
|
1447
|
-
const result = {};
|
|
1448
|
-
for (const property of ["maxZoom", "minZoom", "visible"]) {
|
|
1449
|
-
const values = ranges.flatMap(
|
|
1450
|
-
(range2) => range2[property] === void 0 ? [] : [range2[property]]
|
|
1451
|
-
);
|
|
1452
|
-
if (new Set(values).size > 1) {
|
|
1453
|
-
throw new Error(
|
|
1454
|
-
`Tileflow symbol range conflict for ${property}; placement, text, and icon share one MapLibre layer.`
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
if (values.length > 0) result[property] = values[0];
|
|
1458
|
-
}
|
|
1459
|
-
return result;
|
|
1460
|
-
}
|
|
1461
|
-
function withoutRange(value) {
|
|
1462
|
-
const { maxZoom: _maxZoom, minZoom: _minZoom, visible: _visible, ...rest } = value;
|
|
1463
|
-
return rest;
|
|
1464
|
-
}
|
|
1465
|
-
function setOverlap(layout, part, style) {
|
|
1466
|
-
if (style.overlap !== void 0) layout[`${part}-overlap`] = style.overlap;
|
|
1467
|
-
else if (style.allowOverlap !== void 0) layout[`${part}-allow-overlap`] = style.allowOverlap;
|
|
1468
|
-
if (layout[`${part}-overlap`] !== void 0) delete layout[`${part}-allow-overlap`];
|
|
1218
|
+
setPaint(paint, "icon-halo-width", style.haloWidth);
|
|
1219
|
+
setPaint(paint, "icon-opacity", style.opacity);
|
|
1220
|
+
return applyLayerRange(withLayerParts(layer, layout, paint), style);
|
|
1469
1221
|
}
|
|
1470
|
-
function
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1222
|
+
function applySymbolPlacement(layer, style) {
|
|
1223
|
+
const layout = { ...asRecord(layer.layout) };
|
|
1224
|
+
if (style.placement !== void 0) layout["symbol-placement"] = style.placement;
|
|
1225
|
+
if (style.priority !== void 0) {
|
|
1226
|
+
layout["symbol-sort-key"] = resolvePriority(style.priority, style.priorityOrder);
|
|
1474
1227
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
if (value !== void 0) paint[property] = toMapLibreStyleValue(value);
|
|
1228
|
+
setLayout(layout, "symbol-spacing", style.spacing);
|
|
1229
|
+
if (style.zOrder !== void 0) layout["symbol-z-order"] = style.zOrder;
|
|
1230
|
+
return applyLayerRange(withLayerParts(layer, layout), style);
|
|
1479
1231
|
}
|
|
1480
|
-
function
|
|
1481
|
-
|
|
1232
|
+
function applySymbolStyle(layer, style) {
|
|
1233
|
+
const range2 = resolveSymbolRange(style);
|
|
1234
|
+
let result = applySymbolPlacement(layer, withoutRange(style));
|
|
1235
|
+
if (style.text) result = applyTextStyle(result, withoutRange(style.text));
|
|
1236
|
+
if (style.icon) result = applyIconStyle(result, withoutRange(style.icon));
|
|
1237
|
+
return applyLayerRange(result, range2);
|
|
1482
1238
|
}
|
|
1483
|
-
function
|
|
1484
|
-
const
|
|
1485
|
-
if (
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
);
|
|
1239
|
+
function createAreaLayers(base, style) {
|
|
1240
|
+
const layers = [];
|
|
1241
|
+
if (style.fill && style.fill.visible !== false) {
|
|
1242
|
+
layers.push({
|
|
1243
|
+
layer: applyFillStyle({ ...base, type: "fill" }, style.fill),
|
|
1244
|
+
phase: "fill"
|
|
1245
|
+
});
|
|
1490
1246
|
}
|
|
1491
|
-
if (
|
|
1492
|
-
|
|
1493
|
-
(
|
|
1494
|
-
|
|
1247
|
+
if (style.outline && style.outline.visible !== false) {
|
|
1248
|
+
layers.push({
|
|
1249
|
+
layer: applyLineStyle({ ...base, id: `${base.id}-outline`, type: "line" }, style.outline),
|
|
1250
|
+
phase: "outline"
|
|
1251
|
+
});
|
|
1495
1252
|
}
|
|
1496
|
-
return
|
|
1497
|
-
}
|
|
1498
|
-
function resolvePriority(value, order) {
|
|
1499
|
-
return order === "lower-first" ? toMapLibreStyleValue(value) : invertPriority(value);
|
|
1500
|
-
}
|
|
1501
|
-
function negateExpressionOutput(value) {
|
|
1502
|
-
return typeof value === "number" ? -value : ["*", -1, value];
|
|
1503
|
-
}
|
|
1504
|
-
function isZoomInput2(value) {
|
|
1505
|
-
return Array.isArray(value) && value.length === 1 && value[0] === "zoom";
|
|
1506
|
-
}
|
|
1507
|
-
function asRecord(value) {
|
|
1508
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1253
|
+
return layers;
|
|
1509
1254
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1255
|
+
function applyCircleStyle(layer, style) {
|
|
1256
|
+
const paint = { ...asRecord(layer.paint) };
|
|
1257
|
+
const layout = { ...asRecord(layer.layout) };
|
|
1258
|
+
setPaint(paint, "circle-blur", style.blur);
|
|
1259
|
+
setPaint(paint, "circle-color", style.color);
|
|
1260
|
+
setPaint(paint, "circle-opacity", style.opacity);
|
|
1261
|
+
if (style.pitchAlignment !== void 0) {
|
|
1262
|
+
paint["circle-pitch-alignment"] = style.pitchAlignment;
|
|
1516
1263
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
if (overlay === void 0) return cloneJson3(base);
|
|
1521
|
-
if (Array.isArray(overlay) || isTileflowExpression(overlay) || isTileflowThemeValueNode(overlay) || isTileflowZoomValue(overlay) || !isRecord2(overlay)) {
|
|
1522
|
-
return cloneJson3(overlay);
|
|
1264
|
+
if (style.pitchScale !== void 0) paint["circle-pitch-scale"] = style.pitchScale;
|
|
1265
|
+
if (style.priority !== void 0) {
|
|
1266
|
+
layout["circle-sort-key"] = resolvePriority(style.priority, style.priorityOrder);
|
|
1523
1267
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
}
|
|
1532
|
-
result[key] = mergeValue(baseRecord[key], overlayValue);
|
|
1268
|
+
setPaint(paint, "circle-radius", style.radius);
|
|
1269
|
+
setPaint(paint, "circle-stroke-color", style.strokeColor);
|
|
1270
|
+
setPaint(paint, "circle-stroke-opacity", style.strokeOpacity);
|
|
1271
|
+
setPaint(paint, "circle-stroke-width", style.strokeWidth);
|
|
1272
|
+
setPaint(paint, "circle-translate", style.translate);
|
|
1273
|
+
if (style.translateAnchor !== void 0) {
|
|
1274
|
+
paint["circle-translate-anchor"] = style.translateAnchor;
|
|
1533
1275
|
}
|
|
1534
|
-
return
|
|
1535
|
-
}
|
|
1536
|
-
function cloneJson3(value) {
|
|
1537
|
-
if (value === void 0) return value;
|
|
1538
|
-
return JSON.parse(JSON.stringify(value));
|
|
1539
|
-
}
|
|
1540
|
-
function isRecord2(value) {
|
|
1541
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
// src/trees-3d.ts
|
|
1545
|
-
var tileflowWorldTreeFields = Object.freeze({
|
|
1546
|
-
height: "height",
|
|
1547
|
-
diameterCrown: "diameter_crown",
|
|
1548
|
-
circumference: "circumference",
|
|
1549
|
-
genus: "genus",
|
|
1550
|
-
species: "species",
|
|
1551
|
-
leafType: "leaf_type",
|
|
1552
|
-
taxon: "taxon",
|
|
1553
|
-
canopyRank: "rank",
|
|
1554
|
-
canopyClearance: "clearance_px_z15",
|
|
1555
|
-
canopyCover: "cover_pct",
|
|
1556
|
-
canopyHeight: "height_m"
|
|
1557
|
-
});
|
|
1558
|
-
var tileflowDefaultUnknownTreeForms = Object.freeze({
|
|
1559
|
-
round: 1,
|
|
1560
|
-
open: 1,
|
|
1561
|
-
tall: 1
|
|
1562
|
-
});
|
|
1563
|
-
var broadleaves = Object.freeze({
|
|
1564
|
-
...Object.fromEntries(
|
|
1565
|
-
[
|
|
1566
|
-
"acacia",
|
|
1567
|
-
"albizia",
|
|
1568
|
-
"arbutus",
|
|
1569
|
-
"bursera",
|
|
1570
|
-
"ceratonia",
|
|
1571
|
-
"delonix",
|
|
1572
|
-
"ficus",
|
|
1573
|
-
"gleditsia",
|
|
1574
|
-
"jacaranda",
|
|
1575
|
-
"olea",
|
|
1576
|
-
"quercus",
|
|
1577
|
-
"robinia",
|
|
1578
|
-
"schinus",
|
|
1579
|
-
"sophora",
|
|
1580
|
-
"styphnolobium",
|
|
1581
|
-
"tamarix"
|
|
1582
|
-
].map((genus) => [genus, "open"])
|
|
1583
|
-
),
|
|
1584
|
-
...Object.fromEntries(
|
|
1585
|
-
[
|
|
1586
|
-
"betula",
|
|
1587
|
-
"celtis",
|
|
1588
|
-
"fraxinus",
|
|
1589
|
-
"ginkgo",
|
|
1590
|
-
"liquidambar",
|
|
1591
|
-
"liriodendron",
|
|
1592
|
-
"magnolia",
|
|
1593
|
-
"platanus",
|
|
1594
|
-
"populus",
|
|
1595
|
-
"pyrus",
|
|
1596
|
-
"ulmus",
|
|
1597
|
-
"zelkova"
|
|
1598
|
-
].map((genus) => [genus, "tall"])
|
|
1599
|
-
),
|
|
1600
|
-
...Object.fromEntries(
|
|
1601
|
-
[
|
|
1602
|
-
"acer",
|
|
1603
|
-
"aesculus",
|
|
1604
|
-
"carpinus",
|
|
1605
|
-
"castanea",
|
|
1606
|
-
"catalpa",
|
|
1607
|
-
"cercis",
|
|
1608
|
-
"citrus",
|
|
1609
|
-
"fagus",
|
|
1610
|
-
"lagerstroemia",
|
|
1611
|
-
"malus",
|
|
1612
|
-
"melia",
|
|
1613
|
-
"morus",
|
|
1614
|
-
"prunus",
|
|
1615
|
-
"sorbus",
|
|
1616
|
-
"tilia"
|
|
1617
|
-
].map((genus) => [genus, "round"])
|
|
1618
|
-
)
|
|
1619
|
-
});
|
|
1620
|
-
var tileflowTreeModelIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
1621
|
-
var tileflowVegetationMetadataKey = "tileflow:vegetation";
|
|
1622
|
-
function tileflowVegetationMetadata(vegetation2) {
|
|
1623
|
-
return { version: 1, ...vegetation2 };
|
|
1276
|
+
return applyLayerRange(withLayerParts(layer, layout, paint), style);
|
|
1624
1277
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
minZoom,
|
|
1637
|
-
opacity: mode === "3d" ? 0.82 : 0.9,
|
|
1638
|
-
pitchAlignment: "map",
|
|
1639
|
-
pitchScale: "map",
|
|
1640
|
-
radius: minZoom < 24 ? zoom.linear([
|
|
1641
|
-
[minZoom, 2.2],
|
|
1642
|
-
[minZoom < 20 ? 20 : 24, 8]
|
|
1643
|
-
]) : 2.2,
|
|
1644
|
-
strokeColor: context.colors.park,
|
|
1645
|
-
strokeOpacity: 0.55,
|
|
1646
|
-
strokeWidth: 0.8
|
|
1647
|
-
},
|
|
1648
|
-
mode,
|
|
1649
|
-
threeDimensional: {
|
|
1650
|
-
barkColor: context.colors.vegetation.tree.bark,
|
|
1651
|
-
broadleafColors: context.colors.vegetation.tree.broadleaf,
|
|
1652
|
-
coniferColors: context.colors.vegetation.tree.conifer,
|
|
1653
|
-
palmColors: context.colors.vegetation.tree.palm,
|
|
1654
|
-
crownScale: 1,
|
|
1655
|
-
heightScale: 1
|
|
1656
|
-
}
|
|
1657
|
-
},
|
|
1658
|
-
request
|
|
1659
|
-
);
|
|
1660
|
-
if (config.enabled === false || !context.data.schema.layers.tree) return [];
|
|
1661
|
-
const resolvedMode = config.mode ?? "3d";
|
|
1662
|
-
return [
|
|
1663
|
-
{
|
|
1664
|
-
kind: "layer",
|
|
1665
|
-
layer: applyCircleStyle(
|
|
1666
|
-
{
|
|
1667
|
-
id: "tileflow-vegetation-trees",
|
|
1668
|
-
type: "circle",
|
|
1669
|
-
source: context.data.sourceId,
|
|
1670
|
-
"source-layer": context.data.schema.layers.tree,
|
|
1671
|
-
// The layer also holds rows of trees as lines; a circle on each vertex would misdraw them.
|
|
1672
|
-
filter: ["==", ["geometry-type"], "Point"],
|
|
1673
|
-
metadata: {
|
|
1674
|
-
"tileflow:vegetation-mode": resolvedMode,
|
|
1675
|
-
...resolvedMode === "3d" ? { "tileflow:vegetation-fallback": "flat-circle" } : {}
|
|
1676
|
-
}
|
|
1677
|
-
},
|
|
1678
|
-
config.flat ?? {}
|
|
1679
|
-
),
|
|
1680
|
-
localOrder: 1,
|
|
1681
|
-
owner: "vegetation",
|
|
1682
|
-
slot: "vegetation",
|
|
1683
|
-
target: "vegetation.trees"
|
|
1684
|
-
}
|
|
1685
|
-
];
|
|
1278
|
+
function applyExtrusionStyle(layer, style) {
|
|
1279
|
+
const paint = { ...asRecord(layer.paint) };
|
|
1280
|
+
setPaint(paint, "fill-extrusion-base", style.base);
|
|
1281
|
+
setPaint(paint, "fill-extrusion-color", style.color);
|
|
1282
|
+
setPaint(paint, "fill-extrusion-height", style.height);
|
|
1283
|
+
setPaint(paint, "fill-extrusion-opacity", style.opacity);
|
|
1284
|
+
setPaint(paint, "fill-extrusion-pattern", style.pattern);
|
|
1285
|
+
if (style.verticalGradient !== void 0) {
|
|
1286
|
+
paint["fill-extrusion-vertical-gradient"] = style.verticalGradient;
|
|
1287
|
+
}
|
|
1288
|
+
return applyLayerRange(withLayerParts(layer, void 0, paint), style);
|
|
1686
1289
|
}
|
|
1687
|
-
function
|
|
1688
|
-
const {
|
|
1689
|
-
if (
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
const resolved = value === void 0 ? void 0 : toMapLibreStyleValue(value);
|
|
1696
|
-
return typeof resolved === "number" && resolved > 0 ? resolved : 1;
|
|
1290
|
+
function applyLayerRange(layer, range2) {
|
|
1291
|
+
const layout = { ...asRecord(layer.layout) };
|
|
1292
|
+
if (range2.visible !== void 0) layout.visibility = range2.visible ? "visible" : "none";
|
|
1293
|
+
return {
|
|
1294
|
+
...layer,
|
|
1295
|
+
...range2.minZoom === void 0 ? {} : { minzoom: range2.minZoom },
|
|
1296
|
+
...range2.maxZoom === void 0 ? {} : { maxzoom: range2.maxZoom },
|
|
1297
|
+
...Object.keys(layout).length ? { layout } : {}
|
|
1697
1298
|
};
|
|
1698
|
-
|
|
1299
|
+
}
|
|
1300
|
+
function withLayerParts(layer, layout, paint) {
|
|
1699
1301
|
return {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
...schema.layers.canopy ? { canopy: schema.layers.canopy } : {}
|
|
1704
|
-
},
|
|
1705
|
-
fields: {
|
|
1706
|
-
...tileflowWorldTreeFields,
|
|
1707
|
-
height: field2("height", tileflowWorldTreeFields.height),
|
|
1708
|
-
diameterCrown: field2("diameterCrown", tileflowWorldTreeFields.diameterCrown),
|
|
1709
|
-
genus: field2("genus", tileflowWorldTreeFields.genus),
|
|
1710
|
-
species: field2("species", tileflowWorldTreeFields.species),
|
|
1711
|
-
leafType: field2("leafType", tileflowWorldTreeFields.leafType),
|
|
1712
|
-
canopyRank: field2("canopyRank", tileflowWorldTreeFields.canopyRank),
|
|
1713
|
-
canopyClearance: field2("canopyClearanceAtZoom15", tileflowWorldTreeFields.canopyClearance),
|
|
1714
|
-
canopyCover: field2("canopyCoverPercent", tileflowWorldTreeFields.canopyCover)
|
|
1715
|
-
},
|
|
1716
|
-
colors: {
|
|
1717
|
-
bark: typeof style.barkColor === "string" ? style.barkColor : tree.bark,
|
|
1718
|
-
broadleaf: colours(style.broadleafColors, tree.broadleaf),
|
|
1719
|
-
conifer: colours(style.coniferColors, tree.conifer),
|
|
1720
|
-
palm: colours(style.palmColors, tree.palm)
|
|
1721
|
-
},
|
|
1722
|
-
heightScale: number(style.heightScale),
|
|
1723
|
-
crownScale: number(style.crownScale),
|
|
1724
|
-
forms: Object.fromEntries(
|
|
1725
|
-
Object.entries(style.forms ?? {}).map(([form, own]) => [
|
|
1726
|
-
form,
|
|
1727
|
-
{
|
|
1728
|
-
heightScale: number(own?.heightScale),
|
|
1729
|
-
crownScale: number(own?.crownScale),
|
|
1730
|
-
...own?.models?.length ? { models: [...own.models] } : {}
|
|
1731
|
-
}
|
|
1732
|
-
])
|
|
1733
|
-
),
|
|
1734
|
-
// Filled by the compiler from the map's prepared models.
|
|
1735
|
-
models: {},
|
|
1736
|
-
finish: style.finish ?? "faceted",
|
|
1737
|
-
edges: (() => {
|
|
1738
|
-
const edges = toMapLibreStyleValue(style.edges ?? 0);
|
|
1739
|
-
return typeof edges === "number" ? edges : 0;
|
|
1740
|
-
})(),
|
|
1741
|
-
...(() => {
|
|
1742
|
-
const edgeColor = style.edgeColor === void 0 ? void 0 : toMapLibreStyleValue(style.edgeColor);
|
|
1743
|
-
return typeof edgeColor === "string" ? { edgeColor } : {};
|
|
1744
|
-
})(),
|
|
1745
|
-
unknownForms: style.unknownForms ?? tileflowDefaultUnknownTreeForms,
|
|
1746
|
-
regions: style.regions ?? [],
|
|
1747
|
-
// Filled by the compiler once the style's layers are final.
|
|
1748
|
-
flatLayers: []
|
|
1302
|
+
...layer,
|
|
1303
|
+
...layout && Object.keys(layout).length ? { layout } : {},
|
|
1304
|
+
...paint && Object.keys(paint).length ? { paint } : {}
|
|
1749
1305
|
};
|
|
1750
1306
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
"
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
"transport-bridge-shadow",
|
|
1769
|
-
"transport-bridge-casing",
|
|
1770
|
-
"transport-bridge-fill",
|
|
1771
|
-
"transport-symbols",
|
|
1772
|
-
"boundaries",
|
|
1773
|
-
"buildings",
|
|
1774
|
-
"vegetation",
|
|
1775
|
-
"symbols"
|
|
1776
|
-
];
|
|
1777
|
-
var tileflowLayerBands = [
|
|
1778
|
-
"land",
|
|
1779
|
-
"relief",
|
|
1780
|
-
"water",
|
|
1781
|
-
"roads",
|
|
1782
|
-
"boundaries",
|
|
1783
|
-
"buildings",
|
|
1784
|
-
"vegetation",
|
|
1785
|
-
"labels"
|
|
1786
|
-
];
|
|
1787
|
-
var bandSlots = {
|
|
1788
|
-
land: ["background", "land"],
|
|
1789
|
-
relief: ["terrain", "terrain"],
|
|
1790
|
-
water: ["hydro", "hydro"],
|
|
1791
|
-
roads: ["transport-areas", "transport-symbols"],
|
|
1792
|
-
boundaries: ["boundaries", "boundaries"],
|
|
1793
|
-
buildings: ["buildings", "buildings"],
|
|
1794
|
-
vegetation: ["vegetation", "vegetation"],
|
|
1795
|
-
labels: ["symbols", "symbols"]
|
|
1796
|
-
};
|
|
1797
|
-
var tileflowBandPlacements = tileflowLayerBands.flatMap(
|
|
1798
|
-
(band) => [`below-${band}`, `above-${band}`]
|
|
1799
|
-
);
|
|
1800
|
-
function tileflowBandPlacementSlot(placement) {
|
|
1801
|
-
const [side, band] = splitBandPlacement(placement);
|
|
1802
|
-
return bandSlots[band][side === "below" ? 0 : 1];
|
|
1307
|
+
function resolveSymbolRange(style) {
|
|
1308
|
+
const ranges = [style, style.text, style.icon].filter(
|
|
1309
|
+
(value) => value !== void 0
|
|
1310
|
+
);
|
|
1311
|
+
const result = {};
|
|
1312
|
+
for (const property of ["maxZoom", "minZoom", "visible"]) {
|
|
1313
|
+
const values = ranges.flatMap(
|
|
1314
|
+
(range2) => range2[property] === void 0 ? [] : [range2[property]]
|
|
1315
|
+
);
|
|
1316
|
+
if (new Set(values).size > 1) {
|
|
1317
|
+
throw new Error(
|
|
1318
|
+
`Tileflow symbol range conflict for ${property}; placement, text, and icon share one MapLibre layer.`
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
if (values.length > 0) result[property] = values[0];
|
|
1322
|
+
}
|
|
1323
|
+
return result;
|
|
1803
1324
|
}
|
|
1804
|
-
function
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
const threshold = side === "below" ? tileflowLayerSlots.indexOf(first) : tileflowLayerSlots.indexOf(last) + 1;
|
|
1808
|
-
const index = slotRanks.findIndex((rank) => rank !== void 0 && rank >= threshold);
|
|
1809
|
-
return index < 0 ? slotRanks.length : index;
|
|
1325
|
+
function withoutRange(value) {
|
|
1326
|
+
const { maxZoom: _maxZoom, minZoom: _minZoom, visible: _visible, ...rest } = value;
|
|
1327
|
+
return rest;
|
|
1810
1328
|
}
|
|
1811
|
-
function
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1329
|
+
function setOverlap(layout, part, style) {
|
|
1330
|
+
if (style.overlap !== void 0) layout[`${part}-overlap`] = style.overlap;
|
|
1331
|
+
else if (style.allowOverlap !== void 0) layout[`${part}-allow-overlap`] = style.allowOverlap;
|
|
1332
|
+
if (layout[`${part}-overlap`] !== void 0) delete layout[`${part}-allow-overlap`];
|
|
1333
|
+
}
|
|
1334
|
+
function withoutGovernedAllowOverlap(layout) {
|
|
1335
|
+
const result = { ...layout };
|
|
1336
|
+
for (const part of ["icon", "text"]) {
|
|
1337
|
+
if (result[`${part}-overlap`] !== void 0) delete result[`${part}-allow-overlap`];
|
|
1338
|
+
}
|
|
1339
|
+
return result;
|
|
1340
|
+
}
|
|
1341
|
+
function setPaint(paint, property, value) {
|
|
1342
|
+
if (value !== void 0) paint[property] = toMapLibreStyleValue(value);
|
|
1343
|
+
}
|
|
1344
|
+
function setLayout(layout, property, value) {
|
|
1345
|
+
if (value !== void 0) layout[property] = toMapLibreStyleValue(value);
|
|
1346
|
+
}
|
|
1347
|
+
function invertPriority(value) {
|
|
1348
|
+
const resolved = toMapLibreStyleValue(value);
|
|
1349
|
+
if (typeof resolved === "number") return -resolved;
|
|
1350
|
+
if (Array.isArray(resolved) && resolved[0] === "interpolate" && isZoomInput2(resolved[2])) {
|
|
1351
|
+
return resolved.map(
|
|
1352
|
+
(entry, index) => index >= 4 && index % 2 === 0 ? negateExpressionOutput(entry) : entry
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
if (Array.isArray(resolved) && resolved[0] === "step" && isZoomInput2(resolved[1])) {
|
|
1356
|
+
return resolved.map(
|
|
1357
|
+
(entry, index) => index === 2 || index >= 4 && index % 2 === 0 ? negateExpressionOutput(entry) : entry
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
return ["*", -1, resolved];
|
|
1361
|
+
}
|
|
1362
|
+
function resolvePriority(value, order) {
|
|
1363
|
+
return order === "lower-first" ? toMapLibreStyleValue(value) : invertPriority(value);
|
|
1364
|
+
}
|
|
1365
|
+
function negateExpressionOutput(value) {
|
|
1366
|
+
return typeof value === "number" ? -value : ["*", -1, value];
|
|
1367
|
+
}
|
|
1368
|
+
function isZoomInput2(value) {
|
|
1369
|
+
return Array.isArray(value) && value.length === 1 && value[0] === "zoom";
|
|
1370
|
+
}
|
|
1371
|
+
function asRecord(value) {
|
|
1372
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1817
1373
|
}
|
|
1818
|
-
var tileflowSemanticTargetPattern = /^[a-z][a-z0-9-]*(?:\.[A-Za-z0-9_-]+)*$/u;
|
|
1819
|
-
var tileflowRenderStackOperationNamePattern = /^[a-z][A-Za-z0-9_-]*$/u;
|
|
1820
|
-
var tileflowCompilerMetadataKeys = Object.freeze({
|
|
1821
|
-
owner: "tileflow:compiler-owner",
|
|
1822
|
-
slot: "tileflow:compiler-slot",
|
|
1823
|
-
target: "tileflow:compiler-target"
|
|
1824
|
-
});
|
|
1825
1374
|
|
|
1826
|
-
// src/
|
|
1827
|
-
function
|
|
1828
|
-
|
|
1375
|
+
// src/cartography/merge.ts
|
|
1376
|
+
function mergeTileflowDesign(base, ...overlays) {
|
|
1377
|
+
let resolved = cloneJson3(base);
|
|
1378
|
+
for (const overlay of overlays) {
|
|
1379
|
+
resolved = mergeValue(resolved, overlay);
|
|
1380
|
+
}
|
|
1381
|
+
return resolved;
|
|
1829
1382
|
}
|
|
1830
|
-
function
|
|
1383
|
+
function mergeValue(base, overlay) {
|
|
1384
|
+
if (overlay === void 0) return cloneJson3(base);
|
|
1385
|
+
if (Array.isArray(overlay) || isTileflowExpression(overlay) || isTileflowThemeValueNode(overlay) || isTileflowZoomValue(overlay) || !isRecord2(overlay)) {
|
|
1386
|
+
return cloneJson3(overlay);
|
|
1387
|
+
}
|
|
1388
|
+
const baseRecord = isRecord2(base) ? base : {};
|
|
1389
|
+
const result = {};
|
|
1390
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(baseRecord), ...Object.keys(overlay)])) {
|
|
1391
|
+
const overlayValue = overlay[key];
|
|
1392
|
+
if (overlayValue === void 0) {
|
|
1393
|
+
if (key in baseRecord) result[key] = cloneJson3(baseRecord[key]);
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
result[key] = mergeValue(baseRecord[key], overlayValue);
|
|
1397
|
+
}
|
|
1398
|
+
return result;
|
|
1399
|
+
}
|
|
1400
|
+
function cloneJson3(value) {
|
|
1401
|
+
if (value === void 0) return value;
|
|
1831
1402
|
return JSON.parse(JSON.stringify(value));
|
|
1832
1403
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
function typographyTextStyle(typography) {
|
|
1836
|
-
return {
|
|
1837
|
-
font: typography.font,
|
|
1838
|
-
...typography.fallbacks ? { fallbacks: typography.fallbacks } : {},
|
|
1839
|
-
...typography.letterSpacing === void 0 ? {} : { letterSpacing: typography.letterSpacing },
|
|
1840
|
-
...typography.transform === void 0 ? {} : { transform: typography.transform }
|
|
1841
|
-
};
|
|
1404
|
+
function isRecord2(value) {
|
|
1405
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1842
1406
|
}
|
|
1843
1407
|
|
|
1844
1408
|
// src/modules/addresses/compiler.ts
|
|
@@ -1893,9 +1457,9 @@ function compileAddresses(request, context) {
|
|
|
1893
1457
|
|
|
1894
1458
|
// src/modules/aeroways/index.ts
|
|
1895
1459
|
function aeroways(options = {}) {
|
|
1896
|
-
return { type: "aeroways", ...
|
|
1460
|
+
return { type: "aeroways", ...cloneJson4(options) };
|
|
1897
1461
|
}
|
|
1898
|
-
function
|
|
1462
|
+
function cloneJson4(value) {
|
|
1899
1463
|
return JSON.parse(JSON.stringify(value));
|
|
1900
1464
|
}
|
|
1901
1465
|
|
|
@@ -2053,9 +1617,9 @@ function compileAeroways(request, context) {
|
|
|
2053
1617
|
|
|
2054
1618
|
// src/modules/boundaries/index.ts
|
|
2055
1619
|
function boundaries(options = {}) {
|
|
2056
|
-
return { type: "boundaries", ...
|
|
1620
|
+
return { type: "boundaries", ...cloneJson5(options) };
|
|
2057
1621
|
}
|
|
2058
|
-
function
|
|
1622
|
+
function cloneJson5(value) {
|
|
2059
1623
|
return JSON.parse(JSON.stringify(value));
|
|
2060
1624
|
}
|
|
2061
1625
|
|
|
@@ -2169,9 +1733,9 @@ function compileBoundaries(request, context) {
|
|
|
2169
1733
|
|
|
2170
1734
|
// src/modules/buildings/index.ts
|
|
2171
1735
|
function buildings(options = {}) {
|
|
2172
|
-
return { type: "buildings", ...
|
|
1736
|
+
return { type: "buildings", ...cloneJson6(options) };
|
|
2173
1737
|
}
|
|
2174
|
-
function
|
|
1738
|
+
function cloneJson6(value) {
|
|
2175
1739
|
return JSON.parse(JSON.stringify(value));
|
|
2176
1740
|
}
|
|
2177
1741
|
|
|
@@ -2448,6 +2012,7 @@ var defaults2 = {
|
|
|
2448
2012
|
hierarchy: "clear",
|
|
2449
2013
|
oneWayMarkers: false,
|
|
2450
2014
|
outline: "subtle",
|
|
2015
|
+
rampOrder: "below-roads",
|
|
2451
2016
|
weight: "regular"
|
|
2452
2017
|
};
|
|
2453
2018
|
var defaultRoadWidthScale = {
|
|
@@ -2492,7 +2057,7 @@ function visibleRoadClasses(options) {
|
|
|
2492
2057
|
].filter((roadClass) => !disabled.has(roadClass));
|
|
2493
2058
|
}
|
|
2494
2059
|
function roads(options = {}) {
|
|
2495
|
-
return { type: "roads", ...
|
|
2060
|
+
return { type: "roads", ...cloneJson7(options) };
|
|
2496
2061
|
}
|
|
2497
2062
|
function resolveRoads(request) {
|
|
2498
2063
|
return {
|
|
@@ -2504,6 +2069,7 @@ function resolveRoads(request) {
|
|
|
2504
2069
|
hierarchy: request?.hierarchy ?? defaults2.hierarchy,
|
|
2505
2070
|
oneWayMarkers: request?.oneWayMarkers ?? defaults2.oneWayMarkers,
|
|
2506
2071
|
outline: request?.outline ?? defaults2.outline,
|
|
2072
|
+
rampOrder: request?.rampOrder ?? defaults2.rampOrder,
|
|
2507
2073
|
weight: request?.weight ?? defaults2.weight,
|
|
2508
2074
|
...request?.widthScale ? {
|
|
2509
2075
|
widthScale: {
|
|
@@ -2540,7 +2106,7 @@ function roadStyleMetrics(options) {
|
|
|
2540
2106
|
weightScale: { thin: 0.78, regular: 1, bold: 1.24 }[options.weight]
|
|
2541
2107
|
};
|
|
2542
2108
|
}
|
|
2543
|
-
function
|
|
2109
|
+
function cloneJson7(value) {
|
|
2544
2110
|
return JSON.parse(JSON.stringify(value));
|
|
2545
2111
|
}
|
|
2546
2112
|
|
|
@@ -2551,12 +2117,13 @@ var defaults3 = {
|
|
|
2551
2117
|
language: "auto",
|
|
2552
2118
|
junctions: true,
|
|
2553
2119
|
places: "major",
|
|
2120
|
+
roadPlacement: "foreground",
|
|
2554
2121
|
roads: "major",
|
|
2555
2122
|
shields: "major",
|
|
2556
2123
|
water: "major"
|
|
2557
2124
|
};
|
|
2558
2125
|
function labels(options = {}) {
|
|
2559
|
-
return { type: "labels", ...
|
|
2126
|
+
return { type: "labels", ...cloneJson8(options) };
|
|
2560
2127
|
}
|
|
2561
2128
|
function resolveLabels(request) {
|
|
2562
2129
|
return {
|
|
@@ -2566,6 +2133,7 @@ function resolveLabels(request) {
|
|
|
2566
2133
|
junctions: request?.junctions ?? defaults3.junctions,
|
|
2567
2134
|
places: request?.places ?? defaults3.places,
|
|
2568
2135
|
...request?.roadClasses ? { roadClasses: [...request.roadClasses] } : {},
|
|
2136
|
+
roadPlacement: request?.roadPlacement ?? defaults3.roadPlacement,
|
|
2569
2137
|
roads: request?.roads ?? defaults3.roads,
|
|
2570
2138
|
shields: request?.shields ?? defaults3.shields,
|
|
2571
2139
|
water: request?.water ?? defaults3.water
|
|
@@ -2577,7 +2145,7 @@ function visibleRoadLabelClasses(detail, roads2, explicitClasses) {
|
|
|
2577
2145
|
const eligible = explicit ? tileflowRoadClasses.filter((roadClass) => explicit.has(roadClass)) : roadClassesWithPaths(detail, detail === "all" && roads2.extras.paths);
|
|
2578
2146
|
return eligible.filter((roadClass) => visibleGeometry.has(roadClass));
|
|
2579
2147
|
}
|
|
2580
|
-
function
|
|
2148
|
+
function cloneJson8(value) {
|
|
2581
2149
|
return JSON.parse(JSON.stringify(value));
|
|
2582
2150
|
}
|
|
2583
2151
|
|
|
@@ -2634,6 +2202,7 @@ var placeLabelOrder = [
|
|
|
2634
2202
|
function compileLabels(request, roadRequest, context) {
|
|
2635
2203
|
if (request?.enabled === false) return [];
|
|
2636
2204
|
const semantics = resolveLabels(request);
|
|
2205
|
+
const groundRoadNames = semantics.roadPlacement === "ground";
|
|
2637
2206
|
const roads2 = resolveRoads(roadRequest);
|
|
2638
2207
|
const { colors } = context;
|
|
2639
2208
|
const rankField = context.data.schema.fields.rank;
|
|
@@ -2722,6 +2291,7 @@ function compileLabels(request, roadRequest, context) {
|
|
|
2722
2291
|
color: colors.labels.road,
|
|
2723
2292
|
minZoom: ["motorway", "trunk", "primary"].includes(roadClass) ? 10 : ["pathway", "footway", "cycleway", "steps", "pedestrian"].includes(roadClass) ? 15 : 13,
|
|
2724
2293
|
placement: "line",
|
|
2294
|
+
...groundRoadNames ? { pitchAlignment: "map", rotationAlignment: "map" } : {},
|
|
2725
2295
|
size: zoom.linear([
|
|
2726
2296
|
[10, 10],
|
|
2727
2297
|
[16, 14]
|
|
@@ -2840,7 +2410,7 @@ function compileLabels(request, roadRequest, context) {
|
|
|
2840
2410
|
result.push(
|
|
2841
2411
|
symbolContribution(
|
|
2842
2412
|
`labels.roads.${roadClass}`,
|
|
2843
|
-
(navigationPriority ? 1200 : 200) + order++,
|
|
2413
|
+
(groundRoadNames ? -1e3 : navigationPriority ? 1200 : 200) + order++,
|
|
2844
2414
|
applySymbolStyle(
|
|
2845
2415
|
{
|
|
2846
2416
|
id: id2,
|
|
@@ -2855,7 +2425,7 @@ function compileLabels(request, roadRequest, context) {
|
|
|
2855
2425
|
},
|
|
2856
2426
|
mergeTileflowDesign(style, { text: { field: field2 } })
|
|
2857
2427
|
),
|
|
2858
|
-
"symbols",
|
|
2428
|
+
groundRoadNames ? "buildings" : "symbols",
|
|
2859
2429
|
{ group: "roads", kind: "road-label", member: roadClass }
|
|
2860
2430
|
)
|
|
2861
2431
|
);
|
|
@@ -3248,13 +2818,18 @@ function symbolContribution(target, localOrder, layer, slot = "symbols", family)
|
|
|
3248
2818
|
|
|
3249
2819
|
// src/modules/land/index.ts
|
|
3250
2820
|
function land(options = {}) {
|
|
3251
|
-
return { type: "land", ...
|
|
2821
|
+
return { type: "land", ...cloneJson9(options) };
|
|
3252
2822
|
}
|
|
3253
|
-
function
|
|
2823
|
+
function cloneJson9(value) {
|
|
3254
2824
|
return JSON.parse(JSON.stringify(value));
|
|
3255
2825
|
}
|
|
3256
2826
|
|
|
3257
2827
|
// src/modules/land/compiler.ts
|
|
2828
|
+
var barrierClasses = [
|
|
2829
|
+
"fence",
|
|
2830
|
+
"wall",
|
|
2831
|
+
"hedge"
|
|
2832
|
+
];
|
|
3258
2833
|
var landuseClasses = {
|
|
3259
2834
|
cemetery: ["cemetery"],
|
|
3260
2835
|
civic: ["community_centre", "place_of_worship", "social_facility"],
|
|
@@ -3281,20 +2856,26 @@ var typedGrassSubclasses = [
|
|
|
3281
2856
|
var protectedLandcoverMatch = { class: ["protected_area"] };
|
|
3282
2857
|
var landcoverClasses = [
|
|
3283
2858
|
["farmland", { class: ["farmland"] }],
|
|
3284
|
-
["grass", { class: ["grass"], excludeSubclass: typedGrassSubclasses }],
|
|
3285
2859
|
["ice", { class: ["ice", "glacier"] }],
|
|
3286
|
-
["meadow", { class: ["grass"], subclass: ["meadow"] }],
|
|
3287
2860
|
["rock", { class: ["rock"] }],
|
|
3288
2861
|
["sand", { class: ["sand", "beach"] }],
|
|
3289
|
-
["scrub", { class: ["grass"], subclass: ["scrub"] }],
|
|
3290
2862
|
["wetland", { class: ["wetland"] }],
|
|
3291
2863
|
["wood", { class: ["wood", "forest"] }],
|
|
3292
2864
|
["protected", protectedLandcoverMatch],
|
|
3293
2865
|
["urbanPark", { class: ["grass"], subclass: ["park", "garden"] }],
|
|
3294
2866
|
["recreationGround", { class: ["grass"], subclass: ["recreation_ground"] }],
|
|
3295
2867
|
["villageGreen", { class: ["grass"], subclass: ["village_green"] }],
|
|
2868
|
+
["grass", { class: ["grass"], excludeSubclass: typedGrassSubclasses }],
|
|
2869
|
+
["meadow", { class: ["grass"], subclass: ["meadow"] }],
|
|
2870
|
+
["scrub", { class: ["grass"], subclass: ["scrub"] }],
|
|
3296
2871
|
["flowerbed", { class: ["grass"], subclass: ["flowerbed"] }]
|
|
3297
2872
|
];
|
|
2873
|
+
var detailedLandcover = /* @__PURE__ */ new Set([
|
|
2874
|
+
"grass",
|
|
2875
|
+
"meadow",
|
|
2876
|
+
"scrub",
|
|
2877
|
+
"flowerbed"
|
|
2878
|
+
]);
|
|
3298
2879
|
function compileLand(request, context) {
|
|
3299
2880
|
const colors = context.colors;
|
|
3300
2881
|
const schema = context.data.schema;
|
|
@@ -3442,12 +3023,12 @@ function compileLand(request, context) {
|
|
|
3442
3023
|
}
|
|
3443
3024
|
};
|
|
3444
3025
|
for (const [name, classes] of Object.entries(landuseClasses)) {
|
|
3445
|
-
if (name === "recreation") continue;
|
|
3026
|
+
if (name === "recreation" || name === "parking") continue;
|
|
3446
3027
|
appendLanduse(name, classes);
|
|
3447
3028
|
}
|
|
3448
|
-
|
|
3029
|
+
const appendLandcover = (name, match3) => {
|
|
3449
3030
|
const style = config.landcover?.[name];
|
|
3450
|
-
if (!style)
|
|
3031
|
+
if (!style) return;
|
|
3451
3032
|
const protectedSource = name === "protected";
|
|
3452
3033
|
const sourceLayer = protectedSource ? schema.layers.park : schema.layers.landcover;
|
|
3453
3034
|
const filter = protectedSource && schema.semantics.parkLayer === "protected-only" ? void 0 : landcoverFilter(classField, subclassField, match3);
|
|
@@ -3478,6 +3059,9 @@ function compileLand(request, context) {
|
|
|
3478
3059
|
target: `land.landcover.${name}.${area.phase}`
|
|
3479
3060
|
});
|
|
3480
3061
|
}
|
|
3062
|
+
};
|
|
3063
|
+
for (const [name, match3] of landcoverClasses) {
|
|
3064
|
+
if (!detailedLandcover.has(name)) appendLandcover(name, match3);
|
|
3481
3065
|
}
|
|
3482
3066
|
if (schema.semantics.parkLayer === "mixed" && config.landcover?.urbanPark) {
|
|
3483
3067
|
for (const area of createAreaLayers(
|
|
@@ -3500,7 +3084,56 @@ function compileLand(request, context) {
|
|
|
3500
3084
|
});
|
|
3501
3085
|
}
|
|
3502
3086
|
}
|
|
3087
|
+
for (const [name, match3] of landcoverClasses) {
|
|
3088
|
+
if (detailedLandcover.has(name)) appendLandcover(name, match3);
|
|
3089
|
+
}
|
|
3090
|
+
appendLanduse("parking", landuseClasses.parking);
|
|
3503
3091
|
appendLanduse("recreation", landuseClasses.recreation);
|
|
3092
|
+
const barrierLayer = schema.layers.trafficControl;
|
|
3093
|
+
for (const name of barrierLayer ? barrierClasses : []) {
|
|
3094
|
+
const style = config.barriers?.[name];
|
|
3095
|
+
const kind = ["==", ["get", subclassField], name];
|
|
3096
|
+
if (style?.area) {
|
|
3097
|
+
contributions.push({
|
|
3098
|
+
kind: "layer",
|
|
3099
|
+
layer: applyFillStyle(
|
|
3100
|
+
{
|
|
3101
|
+
id: `tileflow-barrier-${name}-area`,
|
|
3102
|
+
type: "fill",
|
|
3103
|
+
source,
|
|
3104
|
+
"source-layer": barrierLayer,
|
|
3105
|
+
filter: ["all", ["==", ["geometry-type"], "Polygon"], kind]
|
|
3106
|
+
},
|
|
3107
|
+
style.area
|
|
3108
|
+
),
|
|
3109
|
+
localOrder: localOrder++,
|
|
3110
|
+
owner: "land",
|
|
3111
|
+
slot: "land",
|
|
3112
|
+
target: `land.barriers.${name}.area`
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
for (const phase of ["shadow", "line"]) {
|
|
3116
|
+
const line = style?.[phase];
|
|
3117
|
+
if (!line) continue;
|
|
3118
|
+
contributions.push({
|
|
3119
|
+
kind: "layer",
|
|
3120
|
+
layer: applyLineStyle(
|
|
3121
|
+
{
|
|
3122
|
+
id: `tileflow-barrier-${name}${phase === "shadow" ? "-shadow" : ""}`,
|
|
3123
|
+
type: "line",
|
|
3124
|
+
source,
|
|
3125
|
+
"source-layer": barrierLayer,
|
|
3126
|
+
filter: kind
|
|
3127
|
+
},
|
|
3128
|
+
line
|
|
3129
|
+
),
|
|
3130
|
+
localOrder: localOrder++,
|
|
3131
|
+
owner: "land",
|
|
3132
|
+
slot: "land",
|
|
3133
|
+
target: `land.barriers.${name}.${phase}`
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3504
3137
|
return contributions;
|
|
3505
3138
|
}
|
|
3506
3139
|
function classFilter2(field2, classes) {
|
|
@@ -3525,9 +3158,9 @@ var tileflowLandformClasses = [
|
|
|
3525
3158
|
"arete"
|
|
3526
3159
|
];
|
|
3527
3160
|
function landforms(options = {}) {
|
|
3528
|
-
return { type: "landforms", ...
|
|
3161
|
+
return { type: "landforms", ...cloneJson10(options) };
|
|
3529
3162
|
}
|
|
3530
|
-
function
|
|
3163
|
+
function cloneJson10(value) {
|
|
3531
3164
|
return JSON.parse(JSON.stringify(value));
|
|
3532
3165
|
}
|
|
3533
3166
|
|
|
@@ -3618,44 +3251,275 @@ function compileLandforms(request, language, context) {
|
|
|
3618
3251
|
];
|
|
3619
3252
|
});
|
|
3620
3253
|
}
|
|
3621
|
-
function landformFilter(fields, classes) {
|
|
3622
|
-
const classFilter4 = classes.length === 1 ? ["==", ["get", fields.class], classes[0]] : ["match", ["get", fields.class], classes, true, false];
|
|
3623
|
-
const classZoom = [
|
|
3624
|
-
"match",
|
|
3625
|
-
["get", fields.class],
|
|
3626
|
-
"volcano",
|
|
3627
|
-
[">=", ["zoom"], 9],
|
|
3628
|
-
"peak",
|
|
3629
|
-
[">=", ["zoom"], 10],
|
|
3630
|
-
"saddle",
|
|
3631
|
-
[">=", ["zoom"], 13],
|
|
3632
|
-
["ridge", "cliff", "arete"],
|
|
3633
|
-
[">=", ["zoom"], 14],
|
|
3634
|
-
false
|
|
3635
|
-
];
|
|
3636
|
-
return [
|
|
3637
|
-
"all",
|
|
3638
|
-
["==", ["geometry-type"], "Point"],
|
|
3639
|
-
["has", fields.name],
|
|
3640
|
-
classFilter4,
|
|
3641
|
-
classZoom,
|
|
3642
|
-
["<=", ["to-number", ["get", fields.rank], 99], ["step", ["zoom"], 3, 12, 10, 14, 99]]
|
|
3643
|
-
];
|
|
3254
|
+
function landformFilter(fields, classes) {
|
|
3255
|
+
const classFilter4 = classes.length === 1 ? ["==", ["get", fields.class], classes[0]] : ["match", ["get", fields.class], classes, true, false];
|
|
3256
|
+
const classZoom = [
|
|
3257
|
+
"match",
|
|
3258
|
+
["get", fields.class],
|
|
3259
|
+
"volcano",
|
|
3260
|
+
[">=", ["zoom"], 9],
|
|
3261
|
+
"peak",
|
|
3262
|
+
[">=", ["zoom"], 10],
|
|
3263
|
+
"saddle",
|
|
3264
|
+
[">=", ["zoom"], 13],
|
|
3265
|
+
["ridge", "cliff", "arete"],
|
|
3266
|
+
[">=", ["zoom"], 14],
|
|
3267
|
+
false
|
|
3268
|
+
];
|
|
3269
|
+
return [
|
|
3270
|
+
"all",
|
|
3271
|
+
["==", ["geometry-type"], "Point"],
|
|
3272
|
+
["has", fields.name],
|
|
3273
|
+
classFilter4,
|
|
3274
|
+
classZoom,
|
|
3275
|
+
["<=", ["to-number", ["get", fields.rank], 99], ["step", ["zoom"], 3, 12, 10, 14, 99]]
|
|
3276
|
+
];
|
|
3277
|
+
}
|
|
3278
|
+
function landformContribution(target, localOrder, layer) {
|
|
3279
|
+
return {
|
|
3280
|
+
kind: "layer",
|
|
3281
|
+
layer,
|
|
3282
|
+
localOrder,
|
|
3283
|
+
owner: "landforms",
|
|
3284
|
+
slot: "symbols",
|
|
3285
|
+
target
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// src/modules/nautical/index.ts
|
|
3290
|
+
function nautical(options = {}) {
|
|
3291
|
+
return { type: "nautical", ...cloneJson11(options) };
|
|
3292
|
+
}
|
|
3293
|
+
function cloneJson11(value) {
|
|
3294
|
+
return JSON.parse(JSON.stringify(value));
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
// src/marine/index.ts
|
|
3298
|
+
var tileflowBathymetrySourceId = "tileflow-bathymetry";
|
|
3299
|
+
var tileflowBathymetryDemSourceId = "tileflow-bathymetry-dem";
|
|
3300
|
+
var tileflowNauticalSourceId = "tileflow-nautical";
|
|
3301
|
+
var tileflowBathymetryV1Schema = Object.freeze({
|
|
3302
|
+
fields: Object.freeze({
|
|
3303
|
+
class: "class",
|
|
3304
|
+
depth: "depth",
|
|
3305
|
+
id: "id",
|
|
3306
|
+
maxZoom: "max_zoom",
|
|
3307
|
+
minDepth: "min_depth",
|
|
3308
|
+
minZoom: "min_zoom",
|
|
3309
|
+
name: "name",
|
|
3310
|
+
sortKey: "sort_key"
|
|
3311
|
+
}),
|
|
3312
|
+
layer: "bathymetry",
|
|
3313
|
+
layers: Object.freeze({
|
|
3314
|
+
bands: "bathymetry",
|
|
3315
|
+
contours: "bathymetry_contour",
|
|
3316
|
+
coverage: "bathymetry_coverage",
|
|
3317
|
+
landforms: "seafloor_landform",
|
|
3318
|
+
waterNames: "water_name"
|
|
3319
|
+
}),
|
|
3320
|
+
optionalLayers: Object.freeze(["contours", "coverage", "landforms", "waterNames"]),
|
|
3321
|
+
product: "bathymetry-v1",
|
|
3322
|
+
requiredLayers: Object.freeze(["bands"]),
|
|
3323
|
+
schemaVersion: 1,
|
|
3324
|
+
sources: Object.freeze({
|
|
3325
|
+
dem: Object.freeze({
|
|
3326
|
+
encoding: "terrarium",
|
|
3327
|
+
sourceId: tileflowBathymetryDemSourceId,
|
|
3328
|
+
tileSize: 512
|
|
3329
|
+
}),
|
|
3330
|
+
vector: Object.freeze({ sourceId: tileflowBathymetrySourceId })
|
|
3331
|
+
})
|
|
3332
|
+
});
|
|
3333
|
+
var tileflowNauticalV1Schema = Object.freeze({
|
|
3334
|
+
fields: Object.freeze({
|
|
3335
|
+
cell: "cell",
|
|
3336
|
+
character: "character",
|
|
3337
|
+
class: "class",
|
|
3338
|
+
color: "color",
|
|
3339
|
+
coverage: "coverage",
|
|
3340
|
+
depth: "depth",
|
|
3341
|
+
direction: "direction",
|
|
3342
|
+
edition: "edition",
|
|
3343
|
+
licence: "licence",
|
|
3344
|
+
name: "name",
|
|
3345
|
+
provider: "provider",
|
|
3346
|
+
provenance: "provenance",
|
|
3347
|
+
quality: "quality",
|
|
3348
|
+
rangeNm: "range_nm",
|
|
3349
|
+
scale: "scale",
|
|
3350
|
+
subclass: "subclass",
|
|
3351
|
+
update: "update"
|
|
3352
|
+
}),
|
|
3353
|
+
geometryTypes: Object.freeze({
|
|
3354
|
+
aids: Object.freeze(["Point"]),
|
|
3355
|
+
coverage: Object.freeze(["Polygon"]),
|
|
3356
|
+
hazards: Object.freeze(["Point", "Polygon"]),
|
|
3357
|
+
lights: Object.freeze(["Point"]),
|
|
3358
|
+
navigationAreas: Object.freeze(["Polygon"]),
|
|
3359
|
+
reefs: Object.freeze(["Polygon"]),
|
|
3360
|
+
soundings: Object.freeze(["Point"]),
|
|
3361
|
+
wrecks: Object.freeze(["Point", "Polygon"])
|
|
3362
|
+
}),
|
|
3363
|
+
layers: Object.freeze({
|
|
3364
|
+
aids: "aid",
|
|
3365
|
+
coverage: "coverage",
|
|
3366
|
+
hazards: "hazard",
|
|
3367
|
+
lights: "light",
|
|
3368
|
+
navigationAreas: "navigation_area",
|
|
3369
|
+
reefs: "reef",
|
|
3370
|
+
soundings: "sounding",
|
|
3371
|
+
wrecks: "wreck"
|
|
3372
|
+
}),
|
|
3373
|
+
product: "nautical-v1",
|
|
3374
|
+
schemaVersion: 1
|
|
3375
|
+
});
|
|
3376
|
+
var tileflowBathymetryNameLayerLimits = Object.freeze({ maxZoomRanges: 128 });
|
|
3377
|
+
function resolveMarine(marine, apiBaseUrl = "https://api.tileflow.dev") {
|
|
3378
|
+
if (marine === void 0) return void 0;
|
|
3379
|
+
if (typeof marine === "string") {
|
|
3380
|
+
switch (marine) {
|
|
3381
|
+
case "none":
|
|
3382
|
+
return {};
|
|
3383
|
+
case "bathymetry":
|
|
3384
|
+
return { bathymetry: resolveBathymetry({}, apiBaseUrl) };
|
|
3385
|
+
case "nautical":
|
|
3386
|
+
return { nautical: resolveNauticalSource({}, apiBaseUrl) };
|
|
3387
|
+
case "chart":
|
|
3388
|
+
return {
|
|
3389
|
+
bathymetry: resolveBathymetry({}, apiBaseUrl),
|
|
3390
|
+
nautical: resolveNauticalSource({}, apiBaseUrl)
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
return {
|
|
3395
|
+
...marine.bathymetry === false ? {} : { bathymetry: resolveBathymetry(marine.bathymetry ?? {}, apiBaseUrl) },
|
|
3396
|
+
...marine.nautical === false ? {} : { nautical: resolveNauticalSource(marine.nautical ?? {}, apiBaseUrl) }
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3399
|
+
function resolveBathymetry(input, apiBaseUrl) {
|
|
3400
|
+
const advanced = "type" in input && input.type === "bathymetry" ? input : void 0;
|
|
3401
|
+
const display = advanced?.display ?? "bands";
|
|
3402
|
+
const usesVector = display === "bands" || display === "hybrid";
|
|
3403
|
+
const usesRelief = display === "relief" || display === "hybrid";
|
|
3404
|
+
const vectorConfig = {
|
|
3405
|
+
...input.attribution === void 0 ? {} : { attribution: input.attribution },
|
|
3406
|
+
...input.sourceId === void 0 ? {} : { sourceId: input.sourceId },
|
|
3407
|
+
...input.url === void 0 ? {} : { url: input.url }
|
|
3408
|
+
};
|
|
3409
|
+
const reliefConfig = advanced?.relief === false ? void 0 : advanced?.relief;
|
|
3410
|
+
const reliefVisible = usesRelief && reliefConfig?.visible !== false && !(typeof reliefConfig?.opacity === "number" && reliefConfig.opacity === 0);
|
|
3411
|
+
return {
|
|
3412
|
+
...advanced?.bands === void 0 ? {} : { bands: advanced.bands },
|
|
3413
|
+
...advanced?.contours === void 0 ? {} : { contours: advanced.contours },
|
|
3414
|
+
display,
|
|
3415
|
+
...advanced?.labels === void 0 ? {} : { labels: advanced.labels },
|
|
3416
|
+
...advanced?.vectorLayers === void 0 ? {} : { vectorLayers: cloneJson12(advanced.vectorLayers) },
|
|
3417
|
+
...reliefVisible ? { relief: resolveBathymetryRelief(reliefConfig ?? {}, apiBaseUrl) } : {},
|
|
3418
|
+
...usesVector ? {
|
|
3419
|
+
vector: resolveMarineSource({
|
|
3420
|
+
config: vectorConfig,
|
|
3421
|
+
defaultPath: "/tiles/bathymetry/tiles.json",
|
|
3422
|
+
defaultSourceId: tileflowBathymetrySourceId,
|
|
3423
|
+
kind: "tileflow-bathymetry",
|
|
3424
|
+
product: tileflowBathymetryV1Schema.product,
|
|
3425
|
+
apiBaseUrl
|
|
3426
|
+
})
|
|
3427
|
+
} : {}
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
function resolveBathymetryRelief(style, apiBaseUrl) {
|
|
3431
|
+
const sourceId = validateSourceId(style.sourceId ?? tileflowBathymetryDemSourceId);
|
|
3432
|
+
const url = validateBathymetryDemUrl(
|
|
3433
|
+
style.url ?? new URL("/tiles/bathymetry/dem/tiles.json", normalizeApiBaseUrl2(apiBaseUrl)).toString()
|
|
3434
|
+
);
|
|
3435
|
+
const encoding = style.encoding ?? "terrarium";
|
|
3436
|
+
const tileSize = style.tileSize ?? 512;
|
|
3437
|
+
return {
|
|
3438
|
+
identity: {
|
|
3439
|
+
encoding,
|
|
3440
|
+
kind: "tileflow-bathymetry-dem",
|
|
3441
|
+
product: tileflowBathymetryV1Schema.product,
|
|
3442
|
+
schemaVersion: 1,
|
|
3443
|
+
sourceId,
|
|
3444
|
+
tileSize,
|
|
3445
|
+
url
|
|
3446
|
+
},
|
|
3447
|
+
source: {
|
|
3448
|
+
...style.attribution ? { attribution: validateAttribution(style.attribution) } : {},
|
|
3449
|
+
encoding,
|
|
3450
|
+
tileSize,
|
|
3451
|
+
type: "raster-dem",
|
|
3452
|
+
url
|
|
3453
|
+
},
|
|
3454
|
+
sourceId,
|
|
3455
|
+
style: cloneJson12(style)
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
function resolveNauticalSource(config, apiBaseUrl) {
|
|
3459
|
+
return resolveMarineSource({
|
|
3460
|
+
config,
|
|
3461
|
+
defaultPath: "/tiles/nautical/tiles.json",
|
|
3462
|
+
defaultSourceId: tileflowNauticalSourceId,
|
|
3463
|
+
kind: "tileflow-nautical",
|
|
3464
|
+
product: tileflowNauticalV1Schema.product,
|
|
3465
|
+
apiBaseUrl
|
|
3466
|
+
});
|
|
3644
3467
|
}
|
|
3645
|
-
function
|
|
3468
|
+
function resolveMarineSource(options) {
|
|
3469
|
+
const sourceId = validateSourceId(options.config.sourceId ?? options.defaultSourceId);
|
|
3470
|
+
const url = validatePublicVectorUrl(
|
|
3471
|
+
options.config.url ?? new URL(options.defaultPath, normalizeApiBaseUrl2(options.apiBaseUrl)).toString()
|
|
3472
|
+
);
|
|
3473
|
+
const identity = {
|
|
3474
|
+
kind: options.kind,
|
|
3475
|
+
product: options.product,
|
|
3476
|
+
schemaVersion: 1,
|
|
3477
|
+
sourceId,
|
|
3478
|
+
url
|
|
3479
|
+
};
|
|
3646
3480
|
return {
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3481
|
+
identity,
|
|
3482
|
+
source: {
|
|
3483
|
+
...options.config.attribution ? { attribution: validateAttribution(options.config.attribution) } : {},
|
|
3484
|
+
type: "vector",
|
|
3485
|
+
url
|
|
3486
|
+
},
|
|
3487
|
+
sourceId
|
|
3653
3488
|
};
|
|
3654
3489
|
}
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3490
|
+
function validateSourceId(value) {
|
|
3491
|
+
const normalized = value.trim();
|
|
3492
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/u.test(normalized) || normalized !== value) {
|
|
3493
|
+
throw new Error(
|
|
3494
|
+
"Tileflow marine sourceId must be a portable identifier of at most 64 characters."
|
|
3495
|
+
);
|
|
3496
|
+
}
|
|
3497
|
+
return normalized;
|
|
3498
|
+
}
|
|
3499
|
+
function validateAttribution(value) {
|
|
3500
|
+
const normalized = value.trim();
|
|
3501
|
+
if (!normalized || normalized !== value || normalized.length > 2048) {
|
|
3502
|
+
throw new Error(
|
|
3503
|
+
"Tileflow marine attribution must be non-empty, trimmed, and at most 2048 characters."
|
|
3504
|
+
);
|
|
3505
|
+
}
|
|
3506
|
+
return normalized;
|
|
3507
|
+
}
|
|
3508
|
+
function validateBathymetryDemUrl(value) {
|
|
3509
|
+
const url = validatePublicVectorUrl(value);
|
|
3510
|
+
if (url.startsWith("pmtiles://")) {
|
|
3511
|
+
throw new Error(
|
|
3512
|
+
"Tileflow bathymetry DEM TileJSON must use HTTPS, loopback HTTP, or a root-relative URL."
|
|
3513
|
+
);
|
|
3514
|
+
}
|
|
3515
|
+
return url;
|
|
3516
|
+
}
|
|
3517
|
+
function normalizeApiBaseUrl2(value) {
|
|
3518
|
+
const url = new URL(value);
|
|
3519
|
+
if (url.username || url.password) {
|
|
3520
|
+
throw new Error("Tileflow API base URL must not contain user information.");
|
|
3521
|
+
}
|
|
3522
|
+
return url.toString();
|
|
3659
3523
|
}
|
|
3660
3524
|
function cloneJson12(value) {
|
|
3661
3525
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -4846,6 +4710,29 @@ function withoutVisibility(value) {
|
|
|
4846
4710
|
return rest;
|
|
4847
4711
|
}
|
|
4848
4712
|
|
|
4713
|
+
// src/maps/operations.ts
|
|
4714
|
+
var resetValue = deepFreeze({ $tileflow: "reset" });
|
|
4715
|
+
var disableValue = deepFreeze({ op: "disable" });
|
|
4716
|
+
function isTileflowReset(value) {
|
|
4717
|
+
if (!isPlainRecord(value)) return false;
|
|
4718
|
+
return value.$tileflow === "reset" && Object.keys(value).length === 1;
|
|
4719
|
+
}
|
|
4720
|
+
function isTileflowModuleOperation(value) {
|
|
4721
|
+
if (!isPlainRecord(value) || typeof value.op !== "string") return false;
|
|
4722
|
+
return value.op === "disable" || value.op === "refine";
|
|
4723
|
+
}
|
|
4724
|
+
function deepFreeze(value) {
|
|
4725
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
4726
|
+
Object.freeze(value);
|
|
4727
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
4728
|
+
return value;
|
|
4729
|
+
}
|
|
4730
|
+
function isPlainRecord(value) {
|
|
4731
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
4732
|
+
const prototype = Object.getPrototypeOf(value);
|
|
4733
|
+
return prototype === Object.prototype || prototype === null;
|
|
4734
|
+
}
|
|
4735
|
+
|
|
4849
4736
|
// src/portable-identity-rules.ts
|
|
4850
4737
|
var tileflowPortableIdMaximumLength = 64;
|
|
4851
4738
|
var tileflowPortableIdPattern = /^[a-z][a-z0-9-]{0,63}$/u;
|
|
@@ -4856,6 +4743,38 @@ function isTileflowThemeName(value) {
|
|
|
4856
4743
|
return isTileflowPortableId(value) && value !== "system";
|
|
4857
4744
|
}
|
|
4858
4745
|
|
|
4746
|
+
// src/sun.ts
|
|
4747
|
+
var tileflowSunMetadataKey = "tileflow:sun";
|
|
4748
|
+
var tileflowDefaultSunPreset = Object.freeze({
|
|
4749
|
+
azimuth: 210,
|
|
4750
|
+
altitude: 60,
|
|
4751
|
+
color: "#ffffff",
|
|
4752
|
+
intensity: 0.2,
|
|
4753
|
+
shadowIntensity: 1,
|
|
4754
|
+
ambient: Object.freeze({ color: "#ffffff", intensity: 0.8 })
|
|
4755
|
+
});
|
|
4756
|
+
var degrees = Math.PI / 180;
|
|
4757
|
+
function normalizeTileflowAzimuth(azimuth) {
|
|
4758
|
+
const turned = azimuth % 360;
|
|
4759
|
+
return turned < 0 ? turned + 360 : turned === 0 ? 0 : turned;
|
|
4760
|
+
}
|
|
4761
|
+
function tileflowSunMetadata(preset) {
|
|
4762
|
+
return Object.freeze({ version: 1, ...preset });
|
|
4763
|
+
}
|
|
4764
|
+
function tileflowSunLight(direction, base) {
|
|
4765
|
+
const radial = base?.position?.[0] ?? 1.15;
|
|
4766
|
+
return {
|
|
4767
|
+
...base ?? {},
|
|
4768
|
+
anchor: "map",
|
|
4769
|
+
position: [
|
|
4770
|
+
radial,
|
|
4771
|
+
Number(normalizeTileflowAzimuth(direction.azimuth).toFixed(3)),
|
|
4772
|
+
// MapLibre lights the extrusions from above the horizon only.
|
|
4773
|
+
Number(Math.min(90, Math.max(0, 90 - direction.altitude)).toFixed(3))
|
|
4774
|
+
]
|
|
4775
|
+
};
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4859
4778
|
// src/time-of-day.ts
|
|
4860
4779
|
var tileflowTimesOfDay = ["day", "dawn", "dusk", "night"];
|
|
4861
4780
|
function isTileflowTimeOfDay(value) {
|
|
@@ -4906,38 +4825,6 @@ function resolveTileflowTimeOfDayTheme(variants, theme, timeOfDay) {
|
|
|
4906
4825
|
return result;
|
|
4907
4826
|
}
|
|
4908
4827
|
|
|
4909
|
-
// src/sun.ts
|
|
4910
|
-
var tileflowSunMetadataKey = "tileflow:sun";
|
|
4911
|
-
var tileflowDefaultSunPreset = Object.freeze({
|
|
4912
|
-
azimuth: 210,
|
|
4913
|
-
altitude: 60,
|
|
4914
|
-
color: "#ffffff",
|
|
4915
|
-
intensity: 0.5,
|
|
4916
|
-
shadowIntensity: 1,
|
|
4917
|
-
ambient: Object.freeze({ color: "#ffffff", intensity: 0.5 })
|
|
4918
|
-
});
|
|
4919
|
-
var degrees = Math.PI / 180;
|
|
4920
|
-
function normalizeTileflowAzimuth(azimuth) {
|
|
4921
|
-
const turned = azimuth % 360;
|
|
4922
|
-
return turned < 0 ? turned + 360 : turned === 0 ? 0 : turned;
|
|
4923
|
-
}
|
|
4924
|
-
function tileflowSunMetadata(preset) {
|
|
4925
|
-
return Object.freeze({ version: 1, ...preset });
|
|
4926
|
-
}
|
|
4927
|
-
function tileflowSunLight(direction, base) {
|
|
4928
|
-
const radial = base?.position?.[0] ?? 1.15;
|
|
4929
|
-
return {
|
|
4930
|
-
...base ?? {},
|
|
4931
|
-
anchor: "map",
|
|
4932
|
-
position: [
|
|
4933
|
-
radial,
|
|
4934
|
-
Number(normalizeTileflowAzimuth(direction.azimuth).toFixed(3)),
|
|
4935
|
-
// MapLibre lights the extrusions from above the horizon only.
|
|
4936
|
-
Number(Math.min(90, Math.max(0, 90 - direction.altitude)).toFixed(3))
|
|
4937
|
-
]
|
|
4938
|
-
};
|
|
4939
|
-
}
|
|
4940
|
-
|
|
4941
4828
|
// src/themes/visual-semantics.ts
|
|
4942
4829
|
var visualNumberProperties = /* @__PURE__ */ new Set([
|
|
4943
4830
|
"angle",
|
|
@@ -5865,9 +5752,11 @@ function assertThemeTypographyStyleFields(style, path) {
|
|
|
5865
5752
|
throw themeTypeError(`${path}.transform`, "text transform", style.transform);
|
|
5866
5753
|
}
|
|
5867
5754
|
}
|
|
5868
|
-
function assertThemeLighting(value, path) {
|
|
5869
|
-
|
|
5870
|
-
|
|
5755
|
+
function assertThemeLighting(value, path, allowReset = false) {
|
|
5756
|
+
if (allowReset && isTileflowReset(value)) return;
|
|
5757
|
+
const authored = assertPlainThemeRecord(value, path);
|
|
5758
|
+
assertExactThemeKeys(authored, path, themeLightingKeys);
|
|
5759
|
+
const lighting = allowReset ? Object.fromEntries(Object.entries(authored).filter(([, entry]) => !isTileflowReset(entry))) : authored;
|
|
5871
5760
|
if (Object.hasOwn(lighting, "sun")) {
|
|
5872
5761
|
const sun = assertPlainThemeRecord(lighting.sun, `${path}.sun`);
|
|
5873
5762
|
assertExactThemeKeys(sun, `${path}.sun`, themeSunKeys);
|
|
@@ -5979,7 +5868,7 @@ function assertThemeColorOperationNode(operation, path) {
|
|
|
5979
5868
|
throw themeTypeError(`${path}.operation`, "alpha or mix", operation.operation);
|
|
5980
5869
|
}
|
|
5981
5870
|
function assertPlainThemeRecord(value, path) {
|
|
5982
|
-
if (!
|
|
5871
|
+
if (!isPlainRecord2(value)) throw themeTypeError(path, "plain object", value);
|
|
5983
5872
|
return value;
|
|
5984
5873
|
}
|
|
5985
5874
|
function assertExactThemeKeys(value, path, allowedKeys) {
|
|
@@ -6260,7 +6149,7 @@ function cloneJson14(value) {
|
|
|
6260
6149
|
function isRecord3(value) {
|
|
6261
6150
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6262
6151
|
}
|
|
6263
|
-
function
|
|
6152
|
+
function isPlainRecord2(value) {
|
|
6264
6153
|
if (!isRecord3(value)) return false;
|
|
6265
6154
|
const prototype = Object.getPrototypeOf(value);
|
|
6266
6155
|
return prototype === Object.prototype || prototype === null;
|
|
@@ -6459,35 +6348,47 @@ function resolveColors(overrides = {}, defaults4 = defaultColors, moduleOverride
|
|
|
6459
6348
|
}
|
|
6460
6349
|
};
|
|
6461
6350
|
}
|
|
6462
|
-
|
|
6351
|
+
var baseThemeColorRoles = {
|
|
6352
|
+
background: "surface.background",
|
|
6353
|
+
boundary: "boundaries.default",
|
|
6354
|
+
building: "surface.building",
|
|
6355
|
+
land: "surface.land",
|
|
6356
|
+
park: "surface.park",
|
|
6357
|
+
road: "roads.default",
|
|
6358
|
+
roadCasing: "roads.casing",
|
|
6359
|
+
roadMajor: "roads.major",
|
|
6360
|
+
text: "labels.primary",
|
|
6361
|
+
textHalo: "labels.halo",
|
|
6362
|
+
textMuted: "labels.muted",
|
|
6363
|
+
water: "surface.water"
|
|
6364
|
+
};
|
|
6365
|
+
var tileflowRequiredThemeColorRoles = Object.freeze(Object.values(baseThemeColorRoles));
|
|
6366
|
+
var baseThemeColorRoleSet = new Set(tileflowRequiredThemeColorRoles);
|
|
6367
|
+
function resolveThemeColors(theme, onDecision) {
|
|
6463
6368
|
const tokens = resolveTileflowTheme(theme).tokens.color;
|
|
6464
6369
|
const requireColor = (name) => {
|
|
6465
6370
|
const value = tokens[name];
|
|
6466
|
-
if (value !== void 0)
|
|
6371
|
+
if (value !== void 0) {
|
|
6372
|
+
onDecision?.({ role: name, source: "theme", value: cssColor(value) });
|
|
6373
|
+
return value;
|
|
6374
|
+
}
|
|
6467
6375
|
throw new Error(
|
|
6468
6376
|
`Tileflow semantic compiler requires color token "${name}" in theme "${theme.id}".`
|
|
6469
6377
|
);
|
|
6470
6378
|
};
|
|
6471
|
-
const
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
building: requireColor("surface.building"),
|
|
6481
|
-
land: requireColor("surface.land"),
|
|
6482
|
-
park: requireColor("surface.park"),
|
|
6483
|
-
road: requireColor("roads.default"),
|
|
6484
|
-
roadCasing: requireColor("roads.casing"),
|
|
6485
|
-
roadMajor: requireColor("roads.major"),
|
|
6486
|
-
text: requireColor("labels.primary"),
|
|
6487
|
-
textHalo: requireColor("labels.halo"),
|
|
6488
|
-
textMuted: requireColor("labels.muted"),
|
|
6489
|
-
water: requireColor("surface.water")
|
|
6379
|
+
const groupRoles = [];
|
|
6380
|
+
const optionalGroup = (group, keys) => {
|
|
6381
|
+
if (onDecision) groupRoles.push({ group, keys });
|
|
6382
|
+
return Object.fromEntries(
|
|
6383
|
+
keys.flatMap((key) => {
|
|
6384
|
+
const value = tokens[`${group}.${key}`];
|
|
6385
|
+
return value === void 0 ? [] : [[key, value]];
|
|
6386
|
+
})
|
|
6387
|
+
);
|
|
6490
6388
|
};
|
|
6389
|
+
const base = Object.fromEntries(
|
|
6390
|
+
Object.entries(baseThemeColorRoles).map(([key, role]) => [key, requireColor(role)])
|
|
6391
|
+
);
|
|
6491
6392
|
const modules = {
|
|
6492
6393
|
boundaries: optionalGroup("boundaries", ["admin", "disputed", "major", "maritime"]),
|
|
6493
6394
|
buildings: optionalGroup("buildings", [
|
|
@@ -6583,7 +6484,24 @@ function resolveThemeColors(theme) {
|
|
|
6583
6484
|
])
|
|
6584
6485
|
};
|
|
6585
6486
|
const colors = resolveColors({}, base, {}, modules);
|
|
6586
|
-
|
|
6487
|
+
if (onDecision) {
|
|
6488
|
+
for (const { group, keys } of groupRoles) {
|
|
6489
|
+
for (const key of keys) {
|
|
6490
|
+
const role = `${group}.${key}`;
|
|
6491
|
+
if (baseThemeColorRoleSet.has(role)) continue;
|
|
6492
|
+
onDecision({
|
|
6493
|
+
role,
|
|
6494
|
+
source: tokens[role] === void 0 ? "derived" : "theme",
|
|
6495
|
+
value: colors[group][key]
|
|
6496
|
+
});
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
const optionalColor = (name, fallback) => {
|
|
6501
|
+
const value = cssColor(tokens[name] ?? fallback);
|
|
6502
|
+
onDecision?.({ role: name, source: tokens[name] === void 0 ? "derived" : "theme", value });
|
|
6503
|
+
return value;
|
|
6504
|
+
};
|
|
6587
6505
|
return {
|
|
6588
6506
|
...colors,
|
|
6589
6507
|
hydro: {
|
|
@@ -6721,19 +6639,272 @@ function hexToRgba(hex) {
|
|
|
6721
6639
|
b: Number.parseInt(normalized.slice(4, 6), 16)
|
|
6722
6640
|
};
|
|
6723
6641
|
}
|
|
6724
|
-
function cssColor(hex) {
|
|
6725
|
-
const value = hex.replace("#", "");
|
|
6726
|
-
if (value.length !== 4 && value.length !== 8) {
|
|
6727
|
-
return hex;
|
|
6642
|
+
function cssColor(hex) {
|
|
6643
|
+
const value = hex.replace("#", "");
|
|
6644
|
+
if (value.length !== 4 && value.length !== 8) {
|
|
6645
|
+
return hex;
|
|
6646
|
+
}
|
|
6647
|
+
const { a, b, g, r } = hexToRgba(hex);
|
|
6648
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
6649
|
+
}
|
|
6650
|
+
function roundAlpha(value) {
|
|
6651
|
+
return Math.round(value * 1e3) / 1e3;
|
|
6652
|
+
}
|
|
6653
|
+
function rgbToHex(r, g, b) {
|
|
6654
|
+
return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
// src/modules/roads/roundabouts.ts
|
|
6658
|
+
function compileRoundabouts(request, surfaces, context, contributions) {
|
|
6659
|
+
const { fields, layers } = context.data.schema;
|
|
6660
|
+
if (!layers.circularFeature) return;
|
|
6661
|
+
const drawn = surfaces.filter(({ fill }) => fill && fill.visible !== false);
|
|
6662
|
+
if (!drawn.length) return;
|
|
6663
|
+
const values = (resolve) => drawn.map((surface) => ({
|
|
6664
|
+
condition: tileflowRoadClassFilter(fields, surface.roadClass),
|
|
6665
|
+
value: toMapLibreStyleValue(resolve(surface))
|
|
6666
|
+
}));
|
|
6667
|
+
const widths = values(({ fill }) => fill?.width ?? 1);
|
|
6668
|
+
const casings = values(({ casing }) => casing?.width ?? 0);
|
|
6669
|
+
const gaps = values(({ casing }) => casing?.gapWidth ?? 0);
|
|
6670
|
+
const fallbackWidth = request?.fallbackWidth === void 0 ? void 0 : toMapLibreStyleValue(request.fallbackWidth);
|
|
6671
|
+
const borderWidth = request?.borderWidth === void 0 ? void 0 : toMapLibreStyleValue(request.borderWidth);
|
|
6672
|
+
const radiusAt15 = ["to-number", ["get", fields.circularRadiusAtZoom15], 0];
|
|
6673
|
+
const radiusMeters = ["to-number", ["get", fields.circularRadiusMeters], 0];
|
|
6674
|
+
const innerMeters = ["to-number", ["get", fields.circularInnerRadiusMeters], 0];
|
|
6675
|
+
const outerMeters = ["to-number", ["get", fields.circularOuterRadiusMeters], 0];
|
|
6676
|
+
const physical = [
|
|
6677
|
+
"all",
|
|
6678
|
+
[">", radiusMeters, 0],
|
|
6679
|
+
[">=", innerMeters, 0],
|
|
6680
|
+
[">", outerMeters, innerMeters]
|
|
6681
|
+
];
|
|
6682
|
+
const geometryLevels = zoomLevels([
|
|
6683
|
+
...fallbackWidth === void 0 ? widths.map(({ value }) => value) : [fallbackWidth],
|
|
6684
|
+
...borderWidth === void 0 ? [...widths, ...casings, ...gaps].map(({ value }) => value) : [borderWidth]
|
|
6685
|
+
]);
|
|
6686
|
+
const borderAt = (level) => borderWidth === void 0 ? select(
|
|
6687
|
+
values(({ casing, fill }) => {
|
|
6688
|
+
if (!casing || casing.visible === false) return 0;
|
|
6689
|
+
const casingWidth = atZoom(toMapLibreStyleValue(casing.width ?? 0), level);
|
|
6690
|
+
const gap = atZoom(toMapLibreStyleValue(casing.gapWidth ?? 0), level);
|
|
6691
|
+
const fillWidth = atZoom(toMapLibreStyleValue(fill?.width ?? 1), level);
|
|
6692
|
+
return [
|
|
6693
|
+
"max",
|
|
6694
|
+
0,
|
|
6695
|
+
[
|
|
6696
|
+
"case",
|
|
6697
|
+
[">", gap, 0],
|
|
6698
|
+
["-", ["+", gap, ["*", 2, casingWidth]], fillWidth],
|
|
6699
|
+
["-", casingWidth, fillWidth]
|
|
6700
|
+
]
|
|
6701
|
+
];
|
|
6702
|
+
}),
|
|
6703
|
+
0
|
|
6704
|
+
) : atZoom(borderWidth, level);
|
|
6705
|
+
const geometry = (casing, width) => expression([
|
|
6706
|
+
"interpolate",
|
|
6707
|
+
["exponential", 2],
|
|
6708
|
+
["zoom"],
|
|
6709
|
+
...geometryLevels.flatMap((level) => {
|
|
6710
|
+
const centerline = ["*", radiusAt15, 2 ** (level - 15)];
|
|
6711
|
+
const fallback = fallbackWidth === void 0 ? select(
|
|
6712
|
+
widths.map(({ condition, value }) => ({ condition, value: atZoom(value, level) })),
|
|
6713
|
+
0
|
|
6714
|
+
) : atZoom(fallbackWidth, level);
|
|
6715
|
+
const inner = [
|
|
6716
|
+
"case",
|
|
6717
|
+
physical,
|
|
6718
|
+
["*", centerline, ["/", innerMeters, radiusMeters]],
|
|
6719
|
+
["-", centerline, ["/", fallback, 2]]
|
|
6720
|
+
];
|
|
6721
|
+
const fillWidth = [
|
|
6722
|
+
"case",
|
|
6723
|
+
physical,
|
|
6724
|
+
["*", centerline, ["/", ["-", outerMeters, innerMeters], radiusMeters]],
|
|
6725
|
+
fallback
|
|
6726
|
+
];
|
|
6727
|
+
const border = casing ? borderAt(level) : 0;
|
|
6728
|
+
return [
|
|
6729
|
+
level,
|
|
6730
|
+
width ? ["max", 0, ["+", fillWidth, border]] : ["max", 0, ["-", inner, ["/", border, 2]]]
|
|
6731
|
+
];
|
|
6732
|
+
})
|
|
6733
|
+
]);
|
|
6734
|
+
const phaseStyle = (phase) => ({
|
|
6735
|
+
color: "rgba(0, 0, 0, 0)",
|
|
6736
|
+
minZoom: 15,
|
|
6737
|
+
pitchAlignment: "map",
|
|
6738
|
+
pitchScale: "map",
|
|
6739
|
+
radius: geometry(phase === "casing", false),
|
|
6740
|
+
strokeWidth: geometry(phase === "casing", true),
|
|
6741
|
+
strokeColor: phase === "casing" ? context.colors.roads.casing : expression([
|
|
6742
|
+
"match",
|
|
6743
|
+
["coalesce", ["get", fields.class], "minor"],
|
|
6744
|
+
"motorway",
|
|
6745
|
+
context.colors.roads.motorway,
|
|
6746
|
+
"trunk",
|
|
6747
|
+
context.colors.roads.trunk,
|
|
6748
|
+
"primary",
|
|
6749
|
+
context.colors.roads.primary,
|
|
6750
|
+
["secondary", "tertiary"],
|
|
6751
|
+
context.colors.roads.secondary,
|
|
6752
|
+
context.colors.roads.minor
|
|
6753
|
+
]),
|
|
6754
|
+
strokeOpacity: phaseVisibility(drawn, phase, fields)
|
|
6755
|
+
});
|
|
6756
|
+
for (const phase of ["casing", "fill"]) {
|
|
6757
|
+
const style = mergeTileflowDesign(phaseStyle(phase), request?.[phase]);
|
|
6758
|
+
if (style.visible === false) continue;
|
|
6759
|
+
const eligible = drawn.filter(
|
|
6760
|
+
(surface) => request?.[phase] !== void 0 || surface[phase] && surface[phase]?.visible !== false
|
|
6761
|
+
);
|
|
6762
|
+
if (!eligible.length) continue;
|
|
6763
|
+
contributions.push({
|
|
6764
|
+
kind: "layer",
|
|
6765
|
+
layer: applyCircleStyle(
|
|
6766
|
+
{
|
|
6767
|
+
id: `tileflow-road-circular-${phase}`,
|
|
6768
|
+
type: "circle",
|
|
6769
|
+
source: context.data.sourceId,
|
|
6770
|
+
"source-layer": layers.circularFeature,
|
|
6771
|
+
filter: [
|
|
6772
|
+
"all",
|
|
6773
|
+
["==", ["get", fields.circularKind], "road_ring"],
|
|
6774
|
+
[
|
|
6775
|
+
"any",
|
|
6776
|
+
...eligible.map((surface) => tileflowRoadClassFilter(fields, surface.roadClass))
|
|
6777
|
+
]
|
|
6778
|
+
]
|
|
6779
|
+
},
|
|
6780
|
+
style
|
|
6781
|
+
),
|
|
6782
|
+
localOrder: 900,
|
|
6783
|
+
owner: "roads",
|
|
6784
|
+
// Approach fills cover the ring's border where the two meet.
|
|
6785
|
+
slot: phase === "casing" ? "transport-surface-casing" : "transport-surface-fill",
|
|
6786
|
+
target: `roads.roundabouts.${phase}`
|
|
6787
|
+
});
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6790
|
+
function select(values, fallback) {
|
|
6791
|
+
if (!values.length) return fallback;
|
|
6792
|
+
const serialized = JSON.stringify(values[0].value);
|
|
6793
|
+
if (values.every(({ value }) => JSON.stringify(value) === serialized)) return values[0].value;
|
|
6794
|
+
return ["case", ...values.flatMap(({ condition, value }) => [condition, value]), fallback];
|
|
6795
|
+
}
|
|
6796
|
+
function phaseVisibility(surfaces, phase, fields) {
|
|
6797
|
+
const levels = [
|
|
6798
|
+
...new Set(
|
|
6799
|
+
surfaces.flatMap((surface) => {
|
|
6800
|
+
const style = surface[phase];
|
|
6801
|
+
return [style?.minZoom, style?.maxZoom].filter(
|
|
6802
|
+
(value) => value !== void 0
|
|
6803
|
+
);
|
|
6804
|
+
})
|
|
6805
|
+
)
|
|
6806
|
+
].sort((left, right) => left - right);
|
|
6807
|
+
if (!levels.length) return 1;
|
|
6808
|
+
const at = (level) => select(
|
|
6809
|
+
surfaces.map((surface) => {
|
|
6810
|
+
const style = surface[phase];
|
|
6811
|
+
return {
|
|
6812
|
+
condition: tileflowRoadClassFilter(fields, surface.roadClass),
|
|
6813
|
+
value: (style?.minZoom === void 0 || level >= style.minZoom) && (style?.maxZoom === void 0 || level < style.maxZoom) ? 1 : 0
|
|
6814
|
+
};
|
|
6815
|
+
}),
|
|
6816
|
+
0
|
|
6817
|
+
);
|
|
6818
|
+
return expression([
|
|
6819
|
+
"step",
|
|
6820
|
+
["zoom"],
|
|
6821
|
+
at(-1),
|
|
6822
|
+
...levels.flatMap((level) => [level, at(level)])
|
|
6823
|
+
]);
|
|
6824
|
+
}
|
|
6825
|
+
function zoomLevels(values) {
|
|
6826
|
+
const levels = new Set(Array.from({ length: 10 }, (_, index) => index + 15));
|
|
6827
|
+
for (const value of values) {
|
|
6828
|
+
if (!isZoomCurve(value)) continue;
|
|
6829
|
+
for (let index = 3; index < value.length - 1; index += 2) {
|
|
6830
|
+
const level = value[index];
|
|
6831
|
+
if (level < 15 || level > 24) continue;
|
|
6832
|
+
levels.add(level);
|
|
6833
|
+
if (value[0] === "step" && level > 15) {
|
|
6834
|
+
const previous = index > 3 ? value[index - 2] : 15;
|
|
6835
|
+
levels.add(level - Math.min(1e-6, (level - Math.max(15, previous)) / 2));
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
if (value[0] !== "interpolate") continue;
|
|
6839
|
+
const interpolation = value[1];
|
|
6840
|
+
if (interpolation[0] === "exponential" && interpolation[1] === 2) continue;
|
|
6841
|
+
for (let index = 3; index < value.length - 3; index += 2) {
|
|
6842
|
+
let refine2 = function(left, right) {
|
|
6843
|
+
if (right - left <= 1 / 32) return;
|
|
6844
|
+
const factor = (level) => interpolationFactor2(interpolation, level - lower, upper - lower);
|
|
6845
|
+
const leftFactor = factor(left);
|
|
6846
|
+
const rightFactor = factor(right);
|
|
6847
|
+
const error = Math.max(
|
|
6848
|
+
...[0.25, 0.5, 0.75].map((fraction) => {
|
|
6849
|
+
const level = left + (right - left) * fraction;
|
|
6850
|
+
const sampled = leftFactor + (rightFactor - leftFactor) * interpolationFactor2(["exponential", 2], level - left, right - left);
|
|
6851
|
+
return Math.abs((factor(level) - sampled) * difference);
|
|
6852
|
+
})
|
|
6853
|
+
);
|
|
6854
|
+
if (error <= 0.1) return;
|
|
6855
|
+
const middle = (left + right) / 2;
|
|
6856
|
+
levels.add(middle);
|
|
6857
|
+
refine2(left, middle);
|
|
6858
|
+
refine2(middle, right);
|
|
6859
|
+
};
|
|
6860
|
+
var refine = refine2;
|
|
6861
|
+
const lower = value[index];
|
|
6862
|
+
const upper = value[index + 2];
|
|
6863
|
+
const from = value[index + 1];
|
|
6864
|
+
const to = value[index + 3];
|
|
6865
|
+
if (typeof from !== "number" || typeof to !== "number" || from === to) continue;
|
|
6866
|
+
const difference = to - from;
|
|
6867
|
+
const start = Math.max(15, lower);
|
|
6868
|
+
const end = Math.min(24, upper);
|
|
6869
|
+
if (start >= end) continue;
|
|
6870
|
+
const knots = [start, ...levels].filter((level) => level >= start && level < end);
|
|
6871
|
+
knots.push(end);
|
|
6872
|
+
knots.sort((left, right) => left - right);
|
|
6873
|
+
for (let knot = 0; knot < knots.length - 1; knot++) {
|
|
6874
|
+
refine2(knots[knot], knots[knot + 1]);
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6728
6877
|
}
|
|
6729
|
-
|
|
6730
|
-
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
6878
|
+
return [...levels].sort((left, right) => left - right);
|
|
6731
6879
|
}
|
|
6732
|
-
function
|
|
6733
|
-
|
|
6880
|
+
function interpolationFactor2(interpolation, distance, span) {
|
|
6881
|
+
if (interpolation[0] === "linear" || interpolation[1] === 1) return distance / span;
|
|
6882
|
+
if (interpolation[0] !== "exponential") return Number.NaN;
|
|
6883
|
+
const logarithm = Math.log(interpolation[1]);
|
|
6884
|
+
return Math.expm1(logarithm * distance) / Math.expm1(logarithm * span);
|
|
6734
6885
|
}
|
|
6735
|
-
function
|
|
6736
|
-
return
|
|
6886
|
+
function isZoomCurve(value) {
|
|
6887
|
+
return Array.isArray(value) && (value[0] === "interpolate" && isZoom(value[2]) || value[0] === "step" && isZoom(value[1]));
|
|
6888
|
+
}
|
|
6889
|
+
function isZoom(value) {
|
|
6890
|
+
return Array.isArray(value) && value.length === 1 && value[0] === "zoom";
|
|
6891
|
+
}
|
|
6892
|
+
function atZoom(value, level) {
|
|
6893
|
+
if (!Array.isArray(value) || value[0] === "literal") return value;
|
|
6894
|
+
if (isZoom(value)) return level;
|
|
6895
|
+
if (!isZoomCurve(value)) return value.map((entry) => atZoom(entry, level));
|
|
6896
|
+
const stops = [];
|
|
6897
|
+
for (let index = 3; index < value.length - 1; index += 2) {
|
|
6898
|
+
stops.push({ level: value[index], value: value[index + 1] });
|
|
6899
|
+
}
|
|
6900
|
+
const exact = stops.find((stop) => stop.level === level);
|
|
6901
|
+
if (exact) return exact.value;
|
|
6902
|
+
const lower = [...stops].reverse().find((stop) => stop.level < level);
|
|
6903
|
+
const upper = stops.find((stop) => stop.level > level);
|
|
6904
|
+
if (value[0] === "step") return lower?.value ?? value[2];
|
|
6905
|
+
if (!lower) return upper?.value ?? 0;
|
|
6906
|
+
if (!upper) return lower.value;
|
|
6907
|
+
return ["interpolate", value[1], level, lower.level, lower.value, upper.level, upper.value];
|
|
6737
6908
|
}
|
|
6738
6909
|
|
|
6739
6910
|
// src/modules/roads/compiler.ts
|
|
@@ -6789,6 +6960,7 @@ function compileRoads(request, context) {
|
|
|
6789
6960
|
const metrics = roadStyleMetrics(semantics);
|
|
6790
6961
|
const defaults4 = defaultClassStyles(context, metrics, semantics);
|
|
6791
6962
|
const contributions = [];
|
|
6963
|
+
const roundaboutSurfaces = [];
|
|
6792
6964
|
const { sourceId: source, schema } = context.data;
|
|
6793
6965
|
for (const [classIndex, roadClass] of roadClassOrder.entries()) {
|
|
6794
6966
|
if (!visible.has(roadClass) || request?.classes?.[roadClass]?.enabled === false) continue;
|
|
@@ -6803,6 +6975,8 @@ function compileRoads(request, context) {
|
|
|
6803
6975
|
request?.classes?.[roadClass]
|
|
6804
6976
|
);
|
|
6805
6977
|
const treatments = resolveRoadTreatments(request, schema.fields, roadClass);
|
|
6978
|
+
const roundaboutSurface = { roadClass };
|
|
6979
|
+
roundaboutSurfaces.push(roundaboutSurface);
|
|
6806
6980
|
for (const structure of ["tunnel", "surface", "bridge"]) {
|
|
6807
6981
|
const structureConfig = classConfig[structure];
|
|
6808
6982
|
if (!structureConfig) continue;
|
|
@@ -6825,7 +6999,7 @@ function compileRoads(request, context) {
|
|
|
6825
6999
|
phase,
|
|
6826
7000
|
phaseDefaults.color
|
|
6827
7001
|
);
|
|
6828
|
-
|
|
7002
|
+
const contribution = {
|
|
6829
7003
|
family: roadLinePhysicalFamily(roadClass, structure, phase),
|
|
6830
7004
|
kind: "layer",
|
|
6831
7005
|
layer: applyLineStyle(
|
|
@@ -6853,7 +7027,30 @@ function compileRoads(request, context) {
|
|
|
6853
7027
|
owner: "roads",
|
|
6854
7028
|
slot: structureSlots[structure][phase],
|
|
6855
7029
|
target: `roads.classes.${roadClass}.${structure}.${phase}`
|
|
6856
|
-
}
|
|
7030
|
+
};
|
|
7031
|
+
if (structure === "surface" && phase !== "shadow") {
|
|
7032
|
+
roundaboutSurface[phase] = treatedStyle;
|
|
7033
|
+
}
|
|
7034
|
+
if (phase === "fill" && !isTileflowPathRoadClass(roadClass) && semantics.rampOrder === "below-roads") {
|
|
7035
|
+
const rampFilter = flagFilter(schema.fields.ramp);
|
|
7036
|
+
contributions.push({
|
|
7037
|
+
...contribution,
|
|
7038
|
+
family: {
|
|
7039
|
+
...roadLinePhysicalFamily(roadClass, structure, "fill"),
|
|
7040
|
+
group: `${structure}:rampFill:${roadPhysicalCohort(roadClass)}`,
|
|
7041
|
+
outputKey: `roads.cohorts.${structure}.${roadPhysicalCohort(roadClass)}.rampFill`
|
|
7042
|
+
},
|
|
7043
|
+
layer: {
|
|
7044
|
+
...contribution.layer,
|
|
7045
|
+
id: `tileflow-road-${structure}-${roadClass}-rampFill`,
|
|
7046
|
+
filter: ["all", contribution.layer.filter, rampFilter]
|
|
7047
|
+
},
|
|
7048
|
+
localOrder: -1e3 + classIndex,
|
|
7049
|
+
target: `roads.classes.${roadClass}.${structure}.rampFill`
|
|
7050
|
+
});
|
|
7051
|
+
contribution.layer.filter = ["all", contribution.layer.filter, ["!", rampFilter]];
|
|
7052
|
+
}
|
|
7053
|
+
contributions.push(contribution);
|
|
6857
7054
|
}
|
|
6858
7055
|
const hatch = structureConfig.hatch;
|
|
6859
7056
|
const fill = structureConfig.fill;
|
|
@@ -7033,7 +7230,7 @@ function compileRoads(request, context) {
|
|
|
7033
7230
|
}
|
|
7034
7231
|
}
|
|
7035
7232
|
compileSidewalks(request?.sidewalks, areaStyles.pedestrian?.fill?.color, context, contributions);
|
|
7036
|
-
compileRoundabouts(request?.roundabouts, context, contributions);
|
|
7233
|
+
compileRoundabouts(request?.roundabouts, roundaboutSurfaces, context, contributions);
|
|
7037
7234
|
compileCrossings(request?.crossings, context, contributions);
|
|
7038
7235
|
if (semantics.oneWayMarkers && visible.size > 0) {
|
|
7039
7236
|
contributions.push({
|
|
@@ -7158,81 +7355,6 @@ function compileSidewalks(request, pedestrianAreaColor, context, contributions)
|
|
|
7158
7355
|
});
|
|
7159
7356
|
}
|
|
7160
7357
|
}
|
|
7161
|
-
function compileRoundabouts(request, context, contributions) {
|
|
7162
|
-
const { fields, layers } = context.data.schema;
|
|
7163
|
-
if (!layers.circularFeature) return;
|
|
7164
|
-
const circularFields = {
|
|
7165
|
-
circularInnerRadiusMeters: fields.circularInnerRadiusMeters,
|
|
7166
|
-
circularOuterRadiusMeters: fields.circularOuterRadiusMeters,
|
|
7167
|
-
circularRadiusAtZoom15: fields.circularRadiusAtZoom15,
|
|
7168
|
-
circularRadiusMeters: fields.circularRadiusMeters,
|
|
7169
|
-
class: fields.class
|
|
7170
|
-
};
|
|
7171
|
-
const radius = expression(circularRoadInnerRadius(circularFields, false));
|
|
7172
|
-
const casingRadius = expression(circularRoadInnerRadius(circularFields, true));
|
|
7173
|
-
const config = mergeTileflowDesign(
|
|
7174
|
-
{
|
|
7175
|
-
casing: {
|
|
7176
|
-
color: "rgba(0, 0, 0, 0)",
|
|
7177
|
-
minZoom: 15,
|
|
7178
|
-
pitchAlignment: "map",
|
|
7179
|
-
pitchScale: "map",
|
|
7180
|
-
radius: casingRadius,
|
|
7181
|
-
strokeColor: context.colors.roads.casing,
|
|
7182
|
-
strokeWidth: expression(circularRoadStrokeWidth(circularFields, true))
|
|
7183
|
-
},
|
|
7184
|
-
fill: {
|
|
7185
|
-
color: "rgba(0, 0, 0, 0)",
|
|
7186
|
-
minZoom: 15,
|
|
7187
|
-
pitchAlignment: "map",
|
|
7188
|
-
pitchScale: "map",
|
|
7189
|
-
radius,
|
|
7190
|
-
strokeColor: expression([
|
|
7191
|
-
"match",
|
|
7192
|
-
["coalesce", ["get", fields.class], "minor"],
|
|
7193
|
-
"motorway",
|
|
7194
|
-
context.colors.roads.motorway,
|
|
7195
|
-
"trunk",
|
|
7196
|
-
context.colors.roads.trunk,
|
|
7197
|
-
"primary",
|
|
7198
|
-
context.colors.roads.primary,
|
|
7199
|
-
["secondary", "tertiary"],
|
|
7200
|
-
context.colors.roads.secondary,
|
|
7201
|
-
context.colors.roads.minor
|
|
7202
|
-
]),
|
|
7203
|
-
strokeWidth: expression(circularRoadStrokeWidth(circularFields, false))
|
|
7204
|
-
}
|
|
7205
|
-
},
|
|
7206
|
-
request
|
|
7207
|
-
);
|
|
7208
|
-
const base = {
|
|
7209
|
-
type: "circle",
|
|
7210
|
-
source: context.data.sourceId,
|
|
7211
|
-
"source-layer": layers.circularFeature,
|
|
7212
|
-
filter: ["==", ["get", fields.circularKind], "road_ring"]
|
|
7213
|
-
};
|
|
7214
|
-
if (config.casing && config.casing.visible !== false) {
|
|
7215
|
-
contributions.push({
|
|
7216
|
-
kind: "layer",
|
|
7217
|
-
layer: applyCircleStyle({ ...base, id: "tileflow-road-circular-casing" }, config.casing),
|
|
7218
|
-
localOrder: 900,
|
|
7219
|
-
owner: "roads",
|
|
7220
|
-
// Approach fills must cover the circular casing where the roads meet.
|
|
7221
|
-
slot: "transport-surface-casing",
|
|
7222
|
-
target: "roads.roundabouts.casing"
|
|
7223
|
-
});
|
|
7224
|
-
}
|
|
7225
|
-
if (config.fill && config.fill.visible !== false) {
|
|
7226
|
-
contributions.push({
|
|
7227
|
-
kind: "layer",
|
|
7228
|
-
layer: applyCircleStyle({ ...base, id: "tileflow-road-circular-fill" }, config.fill),
|
|
7229
|
-
localOrder: 900,
|
|
7230
|
-
owner: "roads",
|
|
7231
|
-
slot: "transport-surface-fill",
|
|
7232
|
-
target: "roads.roundabouts.fill"
|
|
7233
|
-
});
|
|
7234
|
-
}
|
|
7235
|
-
}
|
|
7236
7358
|
function compileCrossings(request, context, contributions) {
|
|
7237
7359
|
const { fields, layers } = context.data.schema;
|
|
7238
7360
|
if (!request || !layers.streetFurniture) return;
|
|
@@ -7289,94 +7411,6 @@ function compileCrossings(request, context, contributions) {
|
|
|
7289
7411
|
target: "roads.crossings"
|
|
7290
7412
|
});
|
|
7291
7413
|
}
|
|
7292
|
-
var circularRoadBorderTotalWidth = 1;
|
|
7293
|
-
function circularRoadInnerRadius(fields, casing) {
|
|
7294
|
-
const values = circularRoadValues(fields);
|
|
7295
|
-
const stops = [];
|
|
7296
|
-
for (let level = 15; level <= 24; level += 1) {
|
|
7297
|
-
const centerlineRadius = level === 15 ? values.radiusAtZoom15 : ["*", values.radiusAtZoom15, 2 ** (level - 15)];
|
|
7298
|
-
const physicalInnerRadius = [
|
|
7299
|
-
"*",
|
|
7300
|
-
centerlineRadius,
|
|
7301
|
-
["/", values.innerRadiusMeters, values.radiusMeters]
|
|
7302
|
-
];
|
|
7303
|
-
const baseWidth = circularRoadBaseWidth(fields.class);
|
|
7304
|
-
const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadFallbackScale(level)];
|
|
7305
|
-
const fallbackInnerRadius = ["-", centerlineRadius, ["/", fallbackWidth, 2]];
|
|
7306
|
-
const innerRadius = ["case", values.hasPhysicalRadii, physicalInnerRadius, fallbackInnerRadius];
|
|
7307
|
-
stops.push(level, [
|
|
7308
|
-
"max",
|
|
7309
|
-
0,
|
|
7310
|
-
casing ? ["-", innerRadius, circularRoadBorderTotalWidth / 2] : innerRadius
|
|
7311
|
-
]);
|
|
7312
|
-
}
|
|
7313
|
-
return ["interpolate", ["exponential", 2], ["zoom"], ...stops];
|
|
7314
|
-
}
|
|
7315
|
-
function circularRoadStrokeWidth(fields, casing) {
|
|
7316
|
-
const values = circularRoadValues(fields);
|
|
7317
|
-
const baseWidth = circularRoadBaseWidth(fields.class);
|
|
7318
|
-
const stops = [];
|
|
7319
|
-
for (let level = 15; level <= 24; level += 1) {
|
|
7320
|
-
const centerlineRadius = level === 15 ? values.radiusAtZoom15 : ["*", values.radiusAtZoom15, 2 ** (level - 15)];
|
|
7321
|
-
const physicalWidth = [
|
|
7322
|
-
"*",
|
|
7323
|
-
centerlineRadius,
|
|
7324
|
-
["/", ["-", values.outerRadiusMeters, values.innerRadiusMeters], values.radiusMeters]
|
|
7325
|
-
];
|
|
7326
|
-
const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadFallbackScale(level)];
|
|
7327
|
-
stops.push(level, [
|
|
7328
|
-
"case",
|
|
7329
|
-
values.hasPhysicalRadii,
|
|
7330
|
-
casing ? ["+", physicalWidth, circularRoadBorderTotalWidth] : physicalWidth,
|
|
7331
|
-
casing ? ["+", fallbackWidth, circularRoadBorderTotalWidth] : fallbackWidth
|
|
7332
|
-
]);
|
|
7333
|
-
}
|
|
7334
|
-
return ["interpolate", ["exponential", 2], ["zoom"], ...stops];
|
|
7335
|
-
}
|
|
7336
|
-
function circularRoadBaseWidth(classField) {
|
|
7337
|
-
return [
|
|
7338
|
-
"match",
|
|
7339
|
-
["coalesce", ["get", classField], "minor"],
|
|
7340
|
-
"motorway",
|
|
7341
|
-
6,
|
|
7342
|
-
"trunk",
|
|
7343
|
-
5.5,
|
|
7344
|
-
"primary",
|
|
7345
|
-
5,
|
|
7346
|
-
"secondary",
|
|
7347
|
-
4.5,
|
|
7348
|
-
"tertiary",
|
|
7349
|
-
4,
|
|
7350
|
-
"service",
|
|
7351
|
-
2.5,
|
|
7352
|
-
"track",
|
|
7353
|
-
2,
|
|
7354
|
-
3
|
|
7355
|
-
];
|
|
7356
|
-
}
|
|
7357
|
-
function circularRoadValues(fields) {
|
|
7358
|
-
const radiusAtZoom15 = ["to-number", ["get", fields.circularRadiusAtZoom15], 0];
|
|
7359
|
-
const radiusMeters = ["to-number", ["get", fields.circularRadiusMeters], 0];
|
|
7360
|
-
const outerRadiusMeters = ["to-number", ["get", fields.circularOuterRadiusMeters], 0];
|
|
7361
|
-
const innerRadiusMeters = ["to-number", ["get", fields.circularInnerRadiusMeters], 0];
|
|
7362
|
-
return {
|
|
7363
|
-
hasPhysicalRadii: [
|
|
7364
|
-
"all",
|
|
7365
|
-
[">", radiusMeters, 0],
|
|
7366
|
-
[">", outerRadiusMeters, innerRadiusMeters],
|
|
7367
|
-
[">=", innerRadiusMeters, 0]
|
|
7368
|
-
],
|
|
7369
|
-
innerRadiusMeters,
|
|
7370
|
-
outerRadiusMeters,
|
|
7371
|
-
radiusAtZoom15,
|
|
7372
|
-
radiusMeters
|
|
7373
|
-
};
|
|
7374
|
-
}
|
|
7375
|
-
function circularRoadFallbackScale(level) {
|
|
7376
|
-
const interpolationBase = 1.35;
|
|
7377
|
-
const progress = (interpolationBase ** (level - 15) - 1) / (interpolationBase ** (22 - 15) - 1);
|
|
7378
|
-
return 1 + progress * 1.2;
|
|
7379
|
-
}
|
|
7380
7414
|
function widthMatchedHatchPattern(pattern, widths, renderedWidth, minZoom, maxZoom) {
|
|
7381
7415
|
if (typeof pattern !== "string") {
|
|
7382
7416
|
throw new Error("Road hatch patternWidths requires a literal pattern prefix");
|
|
@@ -7492,7 +7526,12 @@ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor)
|
|
|
7492
7526
|
for (const property of paintProperties) {
|
|
7493
7527
|
const branches = treatments.flatMap(({ condition, style }) => {
|
|
7494
7528
|
const value = style[structure]?.[phase]?.[property];
|
|
7495
|
-
return value === void 0 ? [] : [
|
|
7529
|
+
return value === void 0 ? [] : [
|
|
7530
|
+
{
|
|
7531
|
+
condition,
|
|
7532
|
+
resolve: () => expressionOutput(toMapLibreStyleValue(value))
|
|
7533
|
+
}
|
|
7534
|
+
];
|
|
7496
7535
|
});
|
|
7497
7536
|
if (branches.length > 0) {
|
|
7498
7537
|
if (property === "dash" && isSolidRoadDash(result[property] ?? treatmentPaintDefaults.dash) && branches.every(({ resolve }) => isSolidRoadDash(resolve()))) {
|
|
@@ -7982,13 +8021,227 @@ function modalFilter(classField, subclassField, modes) {
|
|
|
7982
8021
|
["match", ["get", subclassField], modes, true, false]
|
|
7983
8022
|
];
|
|
7984
8023
|
}
|
|
7985
|
-
|
|
7986
|
-
// src/modules/vegetation/index.ts
|
|
7987
|
-
function vegetation(options = {}) {
|
|
7988
|
-
return { type: "vegetation", ...cloneJson16(options) };
|
|
7989
|
-
}
|
|
7990
|
-
function cloneJson16(value) {
|
|
7991
|
-
return JSON.parse(JSON.stringify(value));
|
|
8024
|
+
|
|
8025
|
+
// src/modules/vegetation/index.ts
|
|
8026
|
+
function vegetation(options = {}) {
|
|
8027
|
+
return { type: "vegetation", ...cloneJson16(options) };
|
|
8028
|
+
}
|
|
8029
|
+
function cloneJson16(value) {
|
|
8030
|
+
return JSON.parse(JSON.stringify(value));
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
// src/trees-3d.ts
|
|
8034
|
+
var tileflowWorldTreeFields = Object.freeze({
|
|
8035
|
+
height: "height",
|
|
8036
|
+
diameterCrown: "diameter_crown",
|
|
8037
|
+
circumference: "circumference",
|
|
8038
|
+
genus: "genus",
|
|
8039
|
+
species: "species",
|
|
8040
|
+
leafType: "leaf_type",
|
|
8041
|
+
taxon: "taxon",
|
|
8042
|
+
canopyRank: "rank",
|
|
8043
|
+
canopyClearance: "clearance_px_z15",
|
|
8044
|
+
canopyCover: "cover_pct",
|
|
8045
|
+
canopyHeight: "height_m",
|
|
8046
|
+
subclass: "subclass",
|
|
8047
|
+
hedge: "hedge"
|
|
8048
|
+
});
|
|
8049
|
+
var tileflowDefaultUnknownTreeForms = Object.freeze({
|
|
8050
|
+
round: 1,
|
|
8051
|
+
open: 1,
|
|
8052
|
+
tall: 1
|
|
8053
|
+
});
|
|
8054
|
+
var broadleaves = Object.freeze({
|
|
8055
|
+
...Object.fromEntries(
|
|
8056
|
+
[
|
|
8057
|
+
"acacia",
|
|
8058
|
+
"albizia",
|
|
8059
|
+
"arbutus",
|
|
8060
|
+
"bursera",
|
|
8061
|
+
"ceratonia",
|
|
8062
|
+
"delonix",
|
|
8063
|
+
"ficus",
|
|
8064
|
+
"gleditsia",
|
|
8065
|
+
"jacaranda",
|
|
8066
|
+
"olea",
|
|
8067
|
+
"quercus",
|
|
8068
|
+
"robinia",
|
|
8069
|
+
"schinus",
|
|
8070
|
+
"sophora",
|
|
8071
|
+
"styphnolobium",
|
|
8072
|
+
"tamarix"
|
|
8073
|
+
].map((genus) => [genus, "open"])
|
|
8074
|
+
),
|
|
8075
|
+
...Object.fromEntries(
|
|
8076
|
+
[
|
|
8077
|
+
"betula",
|
|
8078
|
+
"celtis",
|
|
8079
|
+
"fraxinus",
|
|
8080
|
+
"ginkgo",
|
|
8081
|
+
"liquidambar",
|
|
8082
|
+
"liriodendron",
|
|
8083
|
+
"magnolia",
|
|
8084
|
+
"platanus",
|
|
8085
|
+
"populus",
|
|
8086
|
+
"pyrus",
|
|
8087
|
+
"ulmus",
|
|
8088
|
+
"zelkova"
|
|
8089
|
+
].map((genus) => [genus, "tall"])
|
|
8090
|
+
),
|
|
8091
|
+
...Object.fromEntries(
|
|
8092
|
+
[
|
|
8093
|
+
"acer",
|
|
8094
|
+
"aesculus",
|
|
8095
|
+
"carpinus",
|
|
8096
|
+
"castanea",
|
|
8097
|
+
"catalpa",
|
|
8098
|
+
"cercis",
|
|
8099
|
+
"citrus",
|
|
8100
|
+
"fagus",
|
|
8101
|
+
"lagerstroemia",
|
|
8102
|
+
"malus",
|
|
8103
|
+
"melia",
|
|
8104
|
+
"morus",
|
|
8105
|
+
"prunus",
|
|
8106
|
+
"sorbus",
|
|
8107
|
+
"tilia"
|
|
8108
|
+
].map((genus) => [genus, "round"])
|
|
8109
|
+
)
|
|
8110
|
+
});
|
|
8111
|
+
var tileflowTreeModelIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
8112
|
+
var tileflowVegetationMetadataKey = "tileflow:vegetation";
|
|
8113
|
+
function tileflowVegetationMetadata(vegetation2) {
|
|
8114
|
+
return { version: 1, ...vegetation2 };
|
|
8115
|
+
}
|
|
8116
|
+
|
|
8117
|
+
// src/modules/vegetation/compiler.ts
|
|
8118
|
+
function compileVegetation(request, context) {
|
|
8119
|
+
const minZoom = request?.flat?.minZoom ?? 16;
|
|
8120
|
+
const mode = request?.mode ?? "3d";
|
|
8121
|
+
const config = mergeTileflowDesign(
|
|
8122
|
+
{
|
|
8123
|
+
type: "vegetation",
|
|
8124
|
+
enabled: true,
|
|
8125
|
+
flat: {
|
|
8126
|
+
color: context.colors.landcover.wood,
|
|
8127
|
+
minZoom,
|
|
8128
|
+
opacity: mode === "3d" ? 0.82 : 0.9,
|
|
8129
|
+
pitchAlignment: "map",
|
|
8130
|
+
pitchScale: "map",
|
|
8131
|
+
radius: minZoom < 24 ? zoom.linear([
|
|
8132
|
+
[minZoom, 2.2],
|
|
8133
|
+
[minZoom < 20 ? 20 : 24, 8]
|
|
8134
|
+
]) : 2.2,
|
|
8135
|
+
strokeColor: context.colors.park,
|
|
8136
|
+
strokeOpacity: 0.55,
|
|
8137
|
+
strokeWidth: 0.8
|
|
8138
|
+
},
|
|
8139
|
+
mode,
|
|
8140
|
+
threeDimensional: {
|
|
8141
|
+
barkColor: context.colors.vegetation.tree.bark,
|
|
8142
|
+
broadleafColors: context.colors.vegetation.tree.broadleaf,
|
|
8143
|
+
coniferColors: context.colors.vegetation.tree.conifer,
|
|
8144
|
+
palmColors: context.colors.vegetation.tree.palm,
|
|
8145
|
+
crownScale: 1,
|
|
8146
|
+
heightScale: 1
|
|
8147
|
+
}
|
|
8148
|
+
},
|
|
8149
|
+
request
|
|
8150
|
+
);
|
|
8151
|
+
if (config.enabled === false || !context.data.schema.layers.tree) return [];
|
|
8152
|
+
const resolvedMode = config.mode ?? "3d";
|
|
8153
|
+
return [
|
|
8154
|
+
{
|
|
8155
|
+
kind: "layer",
|
|
8156
|
+
layer: applyCircleStyle(
|
|
8157
|
+
{
|
|
8158
|
+
id: "tileflow-vegetation-trees",
|
|
8159
|
+
type: "circle",
|
|
8160
|
+
source: context.data.sourceId,
|
|
8161
|
+
"source-layer": context.data.schema.layers.tree,
|
|
8162
|
+
// The layer also holds rows of trees as lines; a circle on each vertex would misdraw them.
|
|
8163
|
+
filter: ["==", ["geometry-type"], "Point"],
|
|
8164
|
+
metadata: {
|
|
8165
|
+
"tileflow:vegetation-mode": resolvedMode,
|
|
8166
|
+
...resolvedMode === "3d" ? { "tileflow:vegetation-fallback": "flat-circle" } : {}
|
|
8167
|
+
}
|
|
8168
|
+
},
|
|
8169
|
+
config.flat ?? {}
|
|
8170
|
+
),
|
|
8171
|
+
localOrder: 1,
|
|
8172
|
+
owner: "vegetation",
|
|
8173
|
+
slot: "vegetation",
|
|
8174
|
+
target: "vegetation.trees"
|
|
8175
|
+
}
|
|
8176
|
+
];
|
|
8177
|
+
}
|
|
8178
|
+
function tileflowVegetationDescription(request, context) {
|
|
8179
|
+
const { schema } = context.data;
|
|
8180
|
+
if (request?.enabled === false || request?.mode === "flat" || !schema.layers.tree)
|
|
8181
|
+
return void 0;
|
|
8182
|
+
const tree = context.colors.vegetation.tree;
|
|
8183
|
+
const style = request?.threeDimensional ?? {};
|
|
8184
|
+
const colours = (value, fallback) => Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string") ? value : [...fallback];
|
|
8185
|
+
const number = (value) => {
|
|
8186
|
+
const resolved = value === void 0 ? void 0 : toMapLibreStyleValue(value);
|
|
8187
|
+
return typeof resolved === "number" && resolved > 0 ? resolved : 1;
|
|
8188
|
+
};
|
|
8189
|
+
const field2 = (name, fallback) => schema.fields[name] ?? fallback;
|
|
8190
|
+
return {
|
|
8191
|
+
source: context.data.sourceId,
|
|
8192
|
+
layers: {
|
|
8193
|
+
tree: schema.layers.tree,
|
|
8194
|
+
...schema.layers.canopy ? { canopy: schema.layers.canopy } : {},
|
|
8195
|
+
...schema.layers.trafficControl && style.hedges !== false ? { hedge: schema.layers.trafficControl } : {}
|
|
8196
|
+
},
|
|
8197
|
+
fields: {
|
|
8198
|
+
...tileflowWorldTreeFields,
|
|
8199
|
+
height: field2("height", tileflowWorldTreeFields.height),
|
|
8200
|
+
diameterCrown: field2("diameterCrown", tileflowWorldTreeFields.diameterCrown),
|
|
8201
|
+
genus: field2("genus", tileflowWorldTreeFields.genus),
|
|
8202
|
+
species: field2("species", tileflowWorldTreeFields.species),
|
|
8203
|
+
leafType: field2("leafType", tileflowWorldTreeFields.leafType),
|
|
8204
|
+
canopyRank: field2("canopyRank", tileflowWorldTreeFields.canopyRank),
|
|
8205
|
+
canopyClearance: field2("canopyClearanceAtZoom15", tileflowWorldTreeFields.canopyClearance),
|
|
8206
|
+
canopyCover: field2("canopyCoverPercent", tileflowWorldTreeFields.canopyCover),
|
|
8207
|
+
subclass: field2("subclass", tileflowWorldTreeFields.subclass),
|
|
8208
|
+
hedge: field2("hedge", tileflowWorldTreeFields.hedge)
|
|
8209
|
+
},
|
|
8210
|
+
colors: {
|
|
8211
|
+
bark: typeof style.barkColor === "string" ? style.barkColor : tree.bark,
|
|
8212
|
+
broadleaf: colours(style.broadleafColors, tree.broadleaf),
|
|
8213
|
+
conifer: colours(style.coniferColors, tree.conifer),
|
|
8214
|
+
palm: colours(style.palmColors, tree.palm),
|
|
8215
|
+
hedge: colours(style.hedgeColors, tree.broadleaf.slice(0, 2))
|
|
8216
|
+
},
|
|
8217
|
+
heightScale: number(style.heightScale),
|
|
8218
|
+
crownScale: number(style.crownScale),
|
|
8219
|
+
forms: Object.fromEntries(
|
|
8220
|
+
Object.entries(style.forms ?? {}).map(([form, own]) => [
|
|
8221
|
+
form,
|
|
8222
|
+
{
|
|
8223
|
+
heightScale: number(own?.heightScale),
|
|
8224
|
+
crownScale: number(own?.crownScale),
|
|
8225
|
+
...own?.models?.length ? { models: [...own.models] } : {}
|
|
8226
|
+
}
|
|
8227
|
+
])
|
|
8228
|
+
),
|
|
8229
|
+
// Filled by the compiler from the map's prepared models.
|
|
8230
|
+
models: {},
|
|
8231
|
+
finish: style.finish ?? "faceted",
|
|
8232
|
+
edges: (() => {
|
|
8233
|
+
const edges = toMapLibreStyleValue(style.edges ?? 0);
|
|
8234
|
+
return typeof edges === "number" ? edges : 0;
|
|
8235
|
+
})(),
|
|
8236
|
+
...(() => {
|
|
8237
|
+
const edgeColor = style.edgeColor === void 0 ? void 0 : toMapLibreStyleValue(style.edgeColor);
|
|
8238
|
+
return typeof edgeColor === "string" ? { edgeColor } : {};
|
|
8239
|
+
})(),
|
|
8240
|
+
unknownForms: style.unknownForms ?? tileflowDefaultUnknownTreeForms,
|
|
8241
|
+
regions: style.regions ?? [],
|
|
8242
|
+
// Filled by the compiler once the style's layers are final.
|
|
8243
|
+
flatLayers: []
|
|
8244
|
+
};
|
|
7992
8245
|
}
|
|
7993
8246
|
|
|
7994
8247
|
// src/modules/water/index.ts
|
|
@@ -8256,6 +8509,9 @@ function compileWater(request, context, language = "auto") {
|
|
|
8256
8509
|
}
|
|
8257
8510
|
if (sidecarBathymetry) {
|
|
8258
8511
|
const { layers: marineLayers } = tileflowBathymetryV1Schema;
|
|
8512
|
+
const splitPlacement = Boolean(
|
|
8513
|
+
config.bathymetryLandforms?.placementByGeometry || config.bathymetryWaterNames?.placementByGeometry
|
|
8514
|
+
);
|
|
8259
8515
|
const landforms2 = bathymetryProduct?.vectorLayers?.landforms;
|
|
8260
8516
|
const waterNames = bathymetryProduct?.vectorLayers?.waterNames;
|
|
8261
8517
|
if (landforms2) {
|
|
@@ -8263,7 +8519,7 @@ function compileWater(request, context, language = "auto") {
|
|
|
8263
8519
|
availability: landforms2,
|
|
8264
8520
|
context,
|
|
8265
8521
|
language,
|
|
8266
|
-
localOrder: -300,
|
|
8522
|
+
localOrder: splitPlacement ? -900 : -300,
|
|
8267
8523
|
source: sidecarBathymetry.sourceId,
|
|
8268
8524
|
sourceLayer: marineLayers.landforms,
|
|
8269
8525
|
style: config.bathymetryLandforms,
|
|
@@ -8276,7 +8532,7 @@ function compileWater(request, context, language = "auto") {
|
|
|
8276
8532
|
availability: waterNames,
|
|
8277
8533
|
context,
|
|
8278
8534
|
language,
|
|
8279
|
-
localOrder: -150,
|
|
8535
|
+
localOrder: splitPlacement ? -450 : -150,
|
|
8280
8536
|
source: sidecarBathymetry.sourceId,
|
|
8281
8537
|
sourceLayer: marineLayers.waterNames,
|
|
8282
8538
|
style: config.bathymetryWaterNames,
|
|
@@ -8380,65 +8636,80 @@ function appendBathymetryNames(contributions, options) {
|
|
|
8380
8636
|
options.style
|
|
8381
8637
|
);
|
|
8382
8638
|
if (style.visible === false || style.text?.visible === false) return;
|
|
8383
|
-
const layer = applySymbolStyle(
|
|
8384
|
-
{
|
|
8385
|
-
id: options.id,
|
|
8386
|
-
type: "symbol",
|
|
8387
|
-
source: options.source,
|
|
8388
|
-
"source-layer": options.sourceLayer,
|
|
8389
|
-
filter: [
|
|
8390
|
-
"all",
|
|
8391
|
-
["match", ["geometry-type"], ["Point", "LineString", "Polygon"], true, false],
|
|
8392
|
-
["!=", name, ""]
|
|
8393
|
-
]
|
|
8394
|
-
},
|
|
8395
|
-
style
|
|
8396
|
-
);
|
|
8397
8639
|
const sourceMinimum = ["to-number", ["coalesce", ["get", fields.minZoom], 0], 0];
|
|
8398
8640
|
const sourceMaximum = ["to-number", ["coalesce", ["get", fields.maxZoom], 24], 24];
|
|
8399
|
-
const
|
|
8400
|
-
const append = (result, target, order = 0) => {
|
|
8401
|
-
contributions.push({
|
|
8402
|
-
kind: "layer",
|
|
8403
|
-
layer: result,
|
|
8404
|
-
localOrder: options.localOrder + order,
|
|
8405
|
-
owner: "water",
|
|
8406
|
-
slot: "symbols",
|
|
8407
|
-
target
|
|
8408
|
-
});
|
|
8409
|
-
};
|
|
8410
|
-
if (options.availability === true) {
|
|
8411
|
-
append(
|
|
8412
|
-
{
|
|
8413
|
-
...layer,
|
|
8414
|
-
filter: [...baseFilter, [">=", ["zoom"], sourceMinimum], ["<", ["zoom"], sourceMaximum]]
|
|
8415
|
-
},
|
|
8416
|
-
options.target
|
|
8417
|
-
);
|
|
8418
|
-
return;
|
|
8419
|
-
}
|
|
8420
|
-
const ranges = [...options.availability.zoomRanges].sort(
|
|
8641
|
+
const ranges = options.availability === true ? void 0 : [...options.availability.zoomRanges].sort(
|
|
8421
8642
|
(left, right) => left.minZoom - right.minZoom || left.maxZoom - right.maxZoom
|
|
8422
8643
|
);
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8644
|
+
const policy = style.placementByGeometry;
|
|
8645
|
+
const geometryFamilies = [
|
|
8646
|
+
["Point", policy?.point],
|
|
8647
|
+
["LineString", policy?.line],
|
|
8648
|
+
["Polygon", policy?.polygon]
|
|
8649
|
+
];
|
|
8650
|
+
const groups = policy ? ["point", "line", "line-center"].flatMap((placement) => {
|
|
8651
|
+
const geometries = geometryFamilies.filter(([, value]) => value === placement).map(([geometry]) => geometry);
|
|
8652
|
+
return geometries.length ? [{ placement, geometries }] : [];
|
|
8653
|
+
}) : [{ placement: style.placement, geometries: ["Point", "LineString", "Polygon"] }];
|
|
8654
|
+
for (const [groupIndex, group] of groups.entries()) {
|
|
8655
|
+
const suffix = policy ? `-placement-${group.placement}` : "";
|
|
8656
|
+
const placementTarget = group.placement === "line-center" ? "LineCenter" : group.placement === "line" ? "Line" : "Point";
|
|
8657
|
+
const target = policy ? `${options.target}.placement${placementTarget}` : options.target;
|
|
8658
|
+
const layer = applySymbolStyle(
|
|
8428
8659
|
{
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8660
|
+
id: `${options.id}${suffix}`,
|
|
8661
|
+
type: "symbol",
|
|
8662
|
+
source: options.source,
|
|
8663
|
+
"source-layer": options.sourceLayer,
|
|
8433
8664
|
filter: [
|
|
8434
|
-
|
|
8435
|
-
["
|
|
8436
|
-
["
|
|
8665
|
+
"all",
|
|
8666
|
+
["match", ["geometry-type"], group.geometries, true, false],
|
|
8667
|
+
["!=", name, ""]
|
|
8437
8668
|
]
|
|
8438
8669
|
},
|
|
8439
|
-
|
|
8440
|
-
index
|
|
8670
|
+
{ ...style, placement: group.placement }
|
|
8441
8671
|
);
|
|
8672
|
+
const baseFilter = layer.filter;
|
|
8673
|
+
const append = (result, rangeTarget, order = 0) => {
|
|
8674
|
+
contributions.push({
|
|
8675
|
+
kind: "layer",
|
|
8676
|
+
layer: result,
|
|
8677
|
+
localOrder: options.localOrder + order * (policy ? 3 : 1) + groupIndex,
|
|
8678
|
+
owner: "water",
|
|
8679
|
+
slot: "symbols",
|
|
8680
|
+
target: rangeTarget
|
|
8681
|
+
});
|
|
8682
|
+
};
|
|
8683
|
+
if (!ranges) {
|
|
8684
|
+
append(
|
|
8685
|
+
{
|
|
8686
|
+
...layer,
|
|
8687
|
+
filter: [...baseFilter, [">=", ["zoom"], sourceMinimum], ["<", ["zoom"], sourceMaximum]]
|
|
8688
|
+
},
|
|
8689
|
+
target
|
|
8690
|
+
);
|
|
8691
|
+
continue;
|
|
8692
|
+
}
|
|
8693
|
+
for (const [index, range2] of ranges.entries()) {
|
|
8694
|
+
const minzoom = Math.max(Number(layer.minzoom ?? 0), range2.minZoom);
|
|
8695
|
+
const maxzoom = Math.min(Number(layer.maxzoom ?? 24), range2.maxZoom);
|
|
8696
|
+
if (minzoom >= maxzoom) continue;
|
|
8697
|
+
append(
|
|
8698
|
+
{
|
|
8699
|
+
...layer,
|
|
8700
|
+
id: `${layer.id}-cohort-${index}`,
|
|
8701
|
+
minzoom,
|
|
8702
|
+
maxzoom,
|
|
8703
|
+
filter: [
|
|
8704
|
+
...baseFilter,
|
|
8705
|
+
["==", sourceMinimum, range2.minZoom],
|
|
8706
|
+
["==", sourceMaximum, range2.maxZoom]
|
|
8707
|
+
]
|
|
8708
|
+
},
|
|
8709
|
+
`${target}.cohort${index}`,
|
|
8710
|
+
index
|
|
8711
|
+
);
|
|
8712
|
+
}
|
|
8442
8713
|
}
|
|
8443
8714
|
}
|
|
8444
8715
|
function appendBathymetryRelief(contributions, relief, context) {
|
|
@@ -9018,7 +9289,7 @@ var tileflowOverlayPlacements = [
|
|
|
9018
9289
|
];
|
|
9019
9290
|
var tileflowHostedSourceLimit = 16;
|
|
9020
9291
|
function isTileflowRemoveOperation(value) {
|
|
9021
|
-
return
|
|
9292
|
+
return isPlainRecord3(value) && value.op === "remove" && Object.keys(value).length === 1;
|
|
9022
9293
|
}
|
|
9023
9294
|
var TileflowOverlayCompilationError = class extends Error {
|
|
9024
9295
|
code;
|
|
@@ -9112,7 +9383,7 @@ function insertTileflowOverlays(input) {
|
|
|
9112
9383
|
...cloneJson18(layer),
|
|
9113
9384
|
source: overlay.source,
|
|
9114
9385
|
metadata: {
|
|
9115
|
-
...
|
|
9386
|
+
...isPlainRecord3(layer.metadata) ? cloneJson18(layer.metadata) : {},
|
|
9116
9387
|
"tileflow:overlay": overlayId
|
|
9117
9388
|
}
|
|
9118
9389
|
};
|
|
@@ -9150,7 +9421,7 @@ function isSafeLocalPmtilesPath(value) {
|
|
|
9150
9421
|
}
|
|
9151
9422
|
function cloneJson18(value) {
|
|
9152
9423
|
if (Array.isArray(value)) return value.map(cloneJson18);
|
|
9153
|
-
if (!
|
|
9424
|
+
if (!isPlainRecord3(value)) return value;
|
|
9154
9425
|
return Object.fromEntries(
|
|
9155
9426
|
Object.entries(value).map(([key, child]) => [key, cloneJson18(child)])
|
|
9156
9427
|
);
|
|
@@ -9167,12 +9438,202 @@ function tileflowOverlayInsertionIndex(layers, placement) {
|
|
|
9167
9438
|
function compareCodeUnits4(left, right) {
|
|
9168
9439
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
9169
9440
|
}
|
|
9170
|
-
function
|
|
9441
|
+
function isPlainRecord3(value) {
|
|
9171
9442
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9172
9443
|
const prototype = Object.getPrototypeOf(value);
|
|
9173
9444
|
return prototype === Object.prototype || prototype === null;
|
|
9174
9445
|
}
|
|
9175
9446
|
|
|
9447
|
+
// src/boundary-areas/schema.ts
|
|
9448
|
+
var tileflowBoundaryClasses = [
|
|
9449
|
+
"country",
|
|
9450
|
+
"dependency",
|
|
9451
|
+
"region",
|
|
9452
|
+
"province_or_county",
|
|
9453
|
+
"municipality",
|
|
9454
|
+
"district",
|
|
9455
|
+
"neighbourhood"
|
|
9456
|
+
];
|
|
9457
|
+
var tileflowBoundaryClassTiers = ["primary", "secondary"];
|
|
9458
|
+
var tileflowBoundaryAreasV1Schema = Object.freeze({
|
|
9459
|
+
classes: tileflowBoundaryClasses,
|
|
9460
|
+
/** First zoom at which each class appears, by tier. */
|
|
9461
|
+
classMinzoom: Object.freeze({
|
|
9462
|
+
country: Object.freeze({ primary: 0, secondary: 0 }),
|
|
9463
|
+
dependency: Object.freeze({ primary: 0, secondary: 0 }),
|
|
9464
|
+
region: Object.freeze({ primary: 3, secondary: 3 }),
|
|
9465
|
+
province_or_county: Object.freeze({ primary: 6, secondary: 7 }),
|
|
9466
|
+
municipality: Object.freeze({ primary: 8, secondary: 8 }),
|
|
9467
|
+
district: Object.freeze({ primary: 10, secondary: 10 }),
|
|
9468
|
+
neighbourhood: Object.freeze({ primary: 11, secondary: 11 })
|
|
9469
|
+
}),
|
|
9470
|
+
fields: Object.freeze({
|
|
9471
|
+
adminDepth: "admin_depth",
|
|
9472
|
+
adminLevel: "admin_level",
|
|
9473
|
+
class: "class",
|
|
9474
|
+
classTier: "class_tier",
|
|
9475
|
+
iso31661: "iso_3166_1",
|
|
9476
|
+
iso31662: "iso_3166_2",
|
|
9477
|
+
name: "name",
|
|
9478
|
+
nuts: "nuts",
|
|
9479
|
+
ref: "ref",
|
|
9480
|
+
wikidata: "wikidata"
|
|
9481
|
+
}),
|
|
9482
|
+
layers: Object.freeze({ areas: "boundary_area", labels: "boundary_label" }),
|
|
9483
|
+
/** Properties present on `boundary_area` polygons. */
|
|
9484
|
+
areaProperties: Object.freeze([
|
|
9485
|
+
"class",
|
|
9486
|
+
"class_tier",
|
|
9487
|
+
"admin_level",
|
|
9488
|
+
"admin_depth",
|
|
9489
|
+
"ref",
|
|
9490
|
+
"iso_3166_1"
|
|
9491
|
+
]),
|
|
9492
|
+
maxzoom: 12,
|
|
9493
|
+
minzoom: 0,
|
|
9494
|
+
product: "boundaries-v1",
|
|
9495
|
+
schemaVersion: 1,
|
|
9496
|
+
tiers: tileflowBoundaryClassTiers
|
|
9497
|
+
});
|
|
9498
|
+
var tileflowBoundaryFamilies = Object.freeze({
|
|
9499
|
+
boundaries: Object.freeze({
|
|
9500
|
+
attribution: "\xA9 OpenStreetMap contributors",
|
|
9501
|
+
license: "ODbL-1.0",
|
|
9502
|
+
path: "/tiles/boundaries/tiles.json",
|
|
9503
|
+
sourceId: "tileflow-boundaries"
|
|
9504
|
+
})
|
|
9505
|
+
});
|
|
9506
|
+
function isTileflowBoundaryFamily(value) {
|
|
9507
|
+
return typeof value === "string" && Object.hasOwn(tileflowBoundaryFamilies, value);
|
|
9508
|
+
}
|
|
9509
|
+
|
|
9510
|
+
// src/boundary-areas/choropleth.ts
|
|
9511
|
+
var maxScaleStops = 64;
|
|
9512
|
+
var maxCategories = 256;
|
|
9513
|
+
var choroplethScale = Object.freeze({
|
|
9514
|
+
categories(categories) {
|
|
9515
|
+
const entries = Object.entries(categories);
|
|
9516
|
+
if (entries.length < 1 || entries.length > maxCategories) {
|
|
9517
|
+
throw new Error(`A category scale needs between 1 and ${maxCategories} categories.`);
|
|
9518
|
+
}
|
|
9519
|
+
for (const [category, color2] of entries) {
|
|
9520
|
+
if (!category) throw new Error("Category scale keys must not be empty.");
|
|
9521
|
+
assertColor2(color2);
|
|
9522
|
+
}
|
|
9523
|
+
return Object.freeze({ categories: Object.freeze({ ...categories }), kind: "categories" });
|
|
9524
|
+
},
|
|
9525
|
+
linear(stops) {
|
|
9526
|
+
if (stops.length < 2) throw new Error("A linear scale needs at least two stops.");
|
|
9527
|
+
return Object.freeze({ kind: "linear", stops: validateStops(stops) });
|
|
9528
|
+
},
|
|
9529
|
+
step(base, stops) {
|
|
9530
|
+
assertColor2(base);
|
|
9531
|
+
if (stops.length < 1) throw new Error("A step scale needs at least one stop.");
|
|
9532
|
+
return Object.freeze({ base, kind: "step", stops: validateStops(stops) });
|
|
9533
|
+
}
|
|
9534
|
+
});
|
|
9535
|
+
var tileflowBoundaryChoroplethMetadataKey = "tileflow:boundaryChoropleth";
|
|
9536
|
+
function validateStops(stops) {
|
|
9537
|
+
if (stops.length > maxScaleStops) {
|
|
9538
|
+
throw new Error(`A choropleth scale accepts at most ${maxScaleStops} stops.`);
|
|
9539
|
+
}
|
|
9540
|
+
let previous = -Infinity;
|
|
9541
|
+
for (const [value, color2] of stops) {
|
|
9542
|
+
if (!Number.isFinite(value) || value <= previous) {
|
|
9543
|
+
throw new Error("Choropleth scale stops must be finite and strictly increasing.");
|
|
9544
|
+
}
|
|
9545
|
+
assertColor2(color2);
|
|
9546
|
+
previous = value;
|
|
9547
|
+
}
|
|
9548
|
+
return Object.freeze(stops.map((stop) => Object.freeze([stop[0], stop[1]])));
|
|
9549
|
+
}
|
|
9550
|
+
function assertColor2(value) {
|
|
9551
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
9552
|
+
throw new Error("Choropleth colors must be non-empty CSS color strings.");
|
|
9553
|
+
}
|
|
9554
|
+
}
|
|
9555
|
+
|
|
9556
|
+
// src/boundary-areas/source.ts
|
|
9557
|
+
function resolveBoundaryAreas(config, apiBaseUrl = "https://api.tileflow.dev") {
|
|
9558
|
+
if (config === void 0 || config === false) return void 0;
|
|
9559
|
+
if (!isTileflowBoundaryFamily(config.family)) {
|
|
9560
|
+
throw new Error(
|
|
9561
|
+
`Unknown Tileflow Boundaries family: ${String(config.family)}. Expected one of: ${Object.keys(
|
|
9562
|
+
tileflowBoundaryFamilies
|
|
9563
|
+
).join(", ")}.`
|
|
9564
|
+
);
|
|
9565
|
+
}
|
|
9566
|
+
if (config.promoteId !== void 0 && config.promoteId !== "ref") {
|
|
9567
|
+
throw new Error('Tileflow Boundaries promoteId must be "ref" when set.');
|
|
9568
|
+
}
|
|
9569
|
+
const family = tileflowBoundaryFamilies[config.family];
|
|
9570
|
+
const url = validatePublicVectorUrl(
|
|
9571
|
+
config.url ?? new URL(family.path, normalizeApiBaseUrl3(apiBaseUrl)).toString()
|
|
9572
|
+
);
|
|
9573
|
+
const { areas, labels: labels2 } = tileflowBoundaryAreasV1Schema.layers;
|
|
9574
|
+
return Object.freeze({
|
|
9575
|
+
identity: Object.freeze({
|
|
9576
|
+
family: config.family,
|
|
9577
|
+
kind: "tileflow-boundaries",
|
|
9578
|
+
product: tileflowBoundaryAreasV1Schema.product,
|
|
9579
|
+
...config.promoteId ? { promoteId: config.promoteId } : {},
|
|
9580
|
+
schemaVersion: tileflowBoundaryAreasV1Schema.schemaVersion,
|
|
9581
|
+
sourceId: family.sourceId,
|
|
9582
|
+
url
|
|
9583
|
+
}),
|
|
9584
|
+
source: Object.freeze({
|
|
9585
|
+
attribution: family.attribution,
|
|
9586
|
+
...config.promoteId ? { promoteId: Object.freeze({ [areas]: config.promoteId, [labels2]: config.promoteId }) } : {},
|
|
9587
|
+
type: "vector",
|
|
9588
|
+
url
|
|
9589
|
+
}),
|
|
9590
|
+
sourceId: family.sourceId
|
|
9591
|
+
});
|
|
9592
|
+
}
|
|
9593
|
+
function normalizeApiBaseUrl3(value) {
|
|
9594
|
+
const url = new URL(value);
|
|
9595
|
+
if (url.username || url.password) {
|
|
9596
|
+
throw new Error("Tileflow API base URL must not contain user information.");
|
|
9597
|
+
}
|
|
9598
|
+
return url.toString();
|
|
9599
|
+
}
|
|
9600
|
+
|
|
9601
|
+
// src/boundary-areas/compile.ts
|
|
9602
|
+
function validateBoundaryChoroplethOverlays(mapId, overlays, boundaryAreas) {
|
|
9603
|
+
const entries = Object.entries(overlays ?? {});
|
|
9604
|
+
for (const [overlayId, overlay] of entries) {
|
|
9605
|
+
for (const layer of overlay.layers) {
|
|
9606
|
+
const metadata = layer.metadata?.[tileflowBoundaryChoroplethMetadataKey];
|
|
9607
|
+
if (!metadata) continue;
|
|
9608
|
+
const path = `map.${mapId}.overlays.${overlayId}`;
|
|
9609
|
+
if (!boundaryAreas || boundaryAreas.identity.family !== metadata.family) {
|
|
9610
|
+
throw new TileflowOverlayCompilationError(
|
|
9611
|
+
"TF_BOUNDARY_AREAS_REQUIRED",
|
|
9612
|
+
path,
|
|
9613
|
+
`Boundary choropleth "${overlayId}" needs the map to declare boundaryAreas with family "${metadata.family}".`,
|
|
9614
|
+
`Add boundaryAreas: {family: '${metadata.family}'} to the map.`
|
|
9615
|
+
);
|
|
9616
|
+
}
|
|
9617
|
+
if (overlay.source !== tileflowBoundaryFamilies[metadata.family].sourceId) {
|
|
9618
|
+
throw new TileflowOverlayCompilationError(
|
|
9619
|
+
"TF_BOUNDARY_CHOROPLETH_SOURCE",
|
|
9620
|
+
`${path}.source`,
|
|
9621
|
+
`Boundary choropleth "${overlayId}" must draw from ${tileflowBoundaryFamilies[metadata.family].sourceId}.`
|
|
9622
|
+
);
|
|
9623
|
+
}
|
|
9624
|
+
if (metadata.joinBy === "id" && boundaryAreas.identity.promoteId === "ref") {
|
|
9625
|
+
throw new TileflowOverlayCompilationError(
|
|
9626
|
+
"TF_BOUNDARY_CHOROPLETH_JOIN",
|
|
9627
|
+
path,
|
|
9628
|
+
`Boundary choropleth "${overlayId}" joins by id, but boundaryAreas promotes ref as the feature ID.`,
|
|
9629
|
+
"Join the values by ref, or remove promoteId from boundaryAreas."
|
|
9630
|
+
);
|
|
9631
|
+
}
|
|
9632
|
+
}
|
|
9633
|
+
}
|
|
9634
|
+
return overlays;
|
|
9635
|
+
}
|
|
9636
|
+
|
|
9176
9637
|
// src/resolved-map-schema.ts
|
|
9177
9638
|
import { Color as Color3 } from "@maplibre/maplibre-gl-style-spec";
|
|
9178
9639
|
import { z as z5 } from "zod";
|
|
@@ -9310,7 +9771,7 @@ function caseValue(branches, fallback) {
|
|
|
9310
9771
|
}
|
|
9311
9772
|
function step(input, fallback, stops) {
|
|
9312
9773
|
validateStopCount(stops.length, "expr.step");
|
|
9313
|
-
|
|
9774
|
+
validateStops2(stops, "expr.step");
|
|
9314
9775
|
return make([
|
|
9315
9776
|
"step",
|
|
9316
9777
|
node(input),
|
|
@@ -9320,7 +9781,7 @@ function step(input, fallback, stops) {
|
|
|
9320
9781
|
}
|
|
9321
9782
|
function interpolate2(interpolation, input, stops) {
|
|
9322
9783
|
validateStopCount(stops.length, "expr.interpolate");
|
|
9323
|
-
|
|
9784
|
+
validateStops2(stops, "expr.interpolate");
|
|
9324
9785
|
const method = interpolationMethod(interpolation);
|
|
9325
9786
|
return make([
|
|
9326
9787
|
"interpolate",
|
|
@@ -9347,7 +9808,7 @@ function interpolationMethod(interpolation) {
|
|
|
9347
9808
|
}
|
|
9348
9809
|
}
|
|
9349
9810
|
}
|
|
9350
|
-
function
|
|
9811
|
+
function validateStops2(stops, api) {
|
|
9351
9812
|
let previous = -Infinity;
|
|
9352
9813
|
for (const [stop] of stops) {
|
|
9353
9814
|
if (!Number.isFinite(stop) || stop <= previous) {
|
|
@@ -10042,11 +10503,11 @@ function physicalLayerIdForSemanticKey(semanticKey) {
|
|
|
10042
10503
|
semanticKey
|
|
10043
10504
|
);
|
|
10044
10505
|
if (match3) return `tileflow-${match3[1]}-render-${match3[2].replaceAll(".", "-")}`;
|
|
10045
|
-
match3 = /^roads\.classes\.([^.]+)\.(tunnel|surface|bridge)\.(shadow|casing|fill|hatch)$/u.exec(
|
|
10506
|
+
match3 = /^roads\.classes\.([^.]+)\.(tunnel|surface|bridge)\.(shadow|casing|fill|hatch|rampFill)$/u.exec(
|
|
10046
10507
|
semanticKey
|
|
10047
10508
|
);
|
|
10048
10509
|
if (match3) return `tileflow-road-${match3[2]}-${match3[1]}-${match3[3]}`;
|
|
10049
|
-
match3 = /^roads\.cohorts\.(tunnel|surface|bridge)\.([^.]+)\.(shadow|casing|fill)$/u.exec(
|
|
10510
|
+
match3 = /^roads\.cohorts\.(tunnel|surface|bridge)\.([^.]+)\.(shadow|casing|fill|rampFill)$/u.exec(
|
|
10050
10511
|
semanticKey
|
|
10051
10512
|
);
|
|
10052
10513
|
if (match3) return `tileflow-road-${match3[1]}-highzoom-${match3[2]}-${match3[3]}`;
|
|
@@ -10062,6 +10523,8 @@ function physicalLayerIdForSemanticKey(semanticKey) {
|
|
|
10062
10523
|
const base = `tileflow-${match3[1]}-${match3[2]}`;
|
|
10063
10524
|
return match3[3] === "fill" ? base : `${base}-outline`;
|
|
10064
10525
|
}
|
|
10526
|
+
match3 = /^land\.barriers\.(fence|hedge|wall)\.(area|shadow|line)$/u.exec(semanticKey);
|
|
10527
|
+
if (match3) return `tileflow-barrier-${match3[1]}${match3[2] === "line" ? "" : `-${match3[2]}`}`;
|
|
10065
10528
|
match3 = /^land\.compatibility\.legacyPark\.(fill|outline)$/u.exec(semanticKey);
|
|
10066
10529
|
if (match3) {
|
|
10067
10530
|
return match3[1] === "fill" ? "tileflow-landcover-legacy-park" : "tileflow-landcover-legacy-park-outline";
|
|
@@ -10102,10 +10565,14 @@ function physicalLayerIdForSemanticKey(semanticKey) {
|
|
|
10102
10565
|
if (match3) return `tileflow-label-road-cohort-${match3[1]}`;
|
|
10103
10566
|
match3 = /^landforms\.classes\.([^.]+)$/u.exec(semanticKey);
|
|
10104
10567
|
if (match3) return `tileflow-landform-${match3[1]}`;
|
|
10105
|
-
match3 = /^water\.(bathymetryLandforms|bathymetryWaterNames)
|
|
10568
|
+
match3 = /^water\.(bathymetryLandforms|bathymetryWaterNames)(?:\.placement(Point|Line|LineCenter))?(?:\.cohort(\d+))?$/u.exec(
|
|
10569
|
+
semanticKey
|
|
10570
|
+
);
|
|
10106
10571
|
if (match3) {
|
|
10107
10572
|
const base = match3[1] === "bathymetryLandforms" ? "landforms" : "water-names";
|
|
10108
|
-
|
|
10573
|
+
const placement = match3[2] ? `-placement-${match3[2] === "LineCenter" ? "line-center" : match3[2].toLowerCase()}` : "";
|
|
10574
|
+
const cohort = match3[3] === void 0 ? "" : `-cohort-${match3[3]}`;
|
|
10575
|
+
return `tileflow-bathymetry-${base}${placement}${cohort}`;
|
|
10109
10576
|
}
|
|
10110
10577
|
match3 = /^poi\.([A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)$/u.exec(semanticKey);
|
|
10111
10578
|
if (match3) return `tileflow-poi-${match3[1].replaceAll(".", "-")}`;
|
|
@@ -11624,6 +12091,12 @@ var numberValueSchema = z5.union([
|
|
|
11624
12091
|
numberExpressionSchema,
|
|
11625
12092
|
numberZoomValueSchema
|
|
11626
12093
|
]);
|
|
12094
|
+
var nonnegativeNumberLiteralSchema = terrainNumberValueSchema(z5.number().finite().min(0));
|
|
12095
|
+
var nonnegativeNumberValueSchema = z5.union([
|
|
12096
|
+
nonnegativeNumberLiteralSchema,
|
|
12097
|
+
numberExpressionSchema,
|
|
12098
|
+
zoomValueSchemaFor(nonnegativeNumberLiteralSchema)
|
|
12099
|
+
]);
|
|
11627
12100
|
var structuralNumberValueSchema = z5.union([
|
|
11628
12101
|
z5.number().finite(),
|
|
11629
12102
|
structuralExpressionSchema,
|
|
@@ -11974,6 +12447,13 @@ var symbolStyleSchema = z5.object({
|
|
|
11974
12447
|
text: textStyleSchema.optional()
|
|
11975
12448
|
}).strict();
|
|
11976
12449
|
var markerSymbolStyleSchema = symbolStyleSchema.extend({ marker: circleStyleSchema.optional() }).strict();
|
|
12450
|
+
var bathymetryNameStyleSchema = symbolStyleSchema.extend({
|
|
12451
|
+
placementByGeometry: z5.object({
|
|
12452
|
+
point: z5.literal("point"),
|
|
12453
|
+
line: z5.enum(["line", "line-center", "point"]),
|
|
12454
|
+
polygon: z5.enum(["line", "line-center", "point"])
|
|
12455
|
+
}).strict().optional()
|
|
12456
|
+
}).strict();
|
|
11977
12457
|
var poiCategoryStyleSchema = markerSymbolStyleSchema.omit({ priority: true, priorityOrder: true }).strict();
|
|
11978
12458
|
var renderScalarSchema = z5.union([z5.boolean(), z5.number().finite(), z5.string()]);
|
|
11979
12459
|
var renderTargetSchema = z5.string().regex(tileflowSemanticTargetPattern, "Expected a semantic target");
|
|
@@ -12165,9 +12645,19 @@ var roadTreatmentStyleSchema = z5.object({
|
|
|
12165
12645
|
tunnel: roadTreatmentLayerStyleSchema.optional(),
|
|
12166
12646
|
widthScale: themeNumberValueSchema.optional()
|
|
12167
12647
|
}).strict();
|
|
12648
|
+
var barrierStyleSchema = z5.object({
|
|
12649
|
+
area: fillStyleSchema.optional(),
|
|
12650
|
+
shadow: lineStyleSchema.optional(),
|
|
12651
|
+
line: lineStyleSchema.optional()
|
|
12652
|
+
}).strict();
|
|
12168
12653
|
var landModuleSchema = z5.object({
|
|
12169
12654
|
type: z5.literal("land"),
|
|
12170
12655
|
background: backgroundStyleSchema.optional(),
|
|
12656
|
+
barriers: z5.object({
|
|
12657
|
+
fence: barrierStyleSchema.optional(),
|
|
12658
|
+
hedge: barrierStyleSchema.optional(),
|
|
12659
|
+
wall: barrierStyleSchema.optional()
|
|
12660
|
+
}).strict().optional(),
|
|
12171
12661
|
enabled: z5.boolean().optional(),
|
|
12172
12662
|
globalLandcover: fillStyleSchema.optional(),
|
|
12173
12663
|
landcover: z5.object({
|
|
@@ -12207,8 +12697,8 @@ var waterModuleSchema = z5.object({
|
|
|
12207
12697
|
bathymetry: fillStyleSchema.optional(),
|
|
12208
12698
|
bathymetryContours: lineStyleSchema.optional(),
|
|
12209
12699
|
bathymetryLabels: symbolStyleSchema.optional(),
|
|
12210
|
-
bathymetryLandforms:
|
|
12211
|
-
bathymetryWaterNames:
|
|
12700
|
+
bathymetryLandforms: bathymetryNameStyleSchema.optional(),
|
|
12701
|
+
bathymetryWaterNames: bathymetryNameStyleSchema.optional(),
|
|
12212
12702
|
bodies: areaStyleSchema.optional(),
|
|
12213
12703
|
enabled: z5.boolean().optional(),
|
|
12214
12704
|
intermittent: z5.object({ bodies: areaStyleSchema.optional(), waterways: lineStyleSchema.optional() }).strict().optional(),
|
|
@@ -12243,6 +12733,7 @@ var buildingsModuleSchema = z5.object({
|
|
|
12243
12733
|
lighting: z5.enum(["style", "sun"]).optional(),
|
|
12244
12734
|
colorVariation: boundedThemeNumberSchema(0, 1).optional(),
|
|
12245
12735
|
edges: boundedThemeNumberSchema(0, 1).optional(),
|
|
12736
|
+
edgeRadius: z5.number().min(0).max(1).optional(),
|
|
12246
12737
|
edgeColor: themeColorValueSchema.optional(),
|
|
12247
12738
|
nightLight: boundedThemeNumberSchema(0, 2).optional(),
|
|
12248
12739
|
occlusion: z5.boolean().optional(),
|
|
@@ -12369,6 +12860,9 @@ var roadsModuleSchema = z5.object({
|
|
|
12369
12860
|
}).strict().optional(),
|
|
12370
12861
|
oneWayMarkers: z5.boolean().optional(),
|
|
12371
12862
|
outline: z5.enum(["none", "subtle", "strong"]).optional(),
|
|
12863
|
+
rampOrder: z5.enum(["below-roads", "by-class"]).describe(
|
|
12864
|
+
"Ramp fills draw below receiving roads by default. Use by-class to keep ramp fills in their road class order."
|
|
12865
|
+
).optional(),
|
|
12372
12866
|
restrictions: z5.object({
|
|
12373
12867
|
access: roadTreatmentStyleSchema.optional(),
|
|
12374
12868
|
bicycle: roadTreatmentStyleSchema.optional(),
|
|
@@ -12377,7 +12871,16 @@ var roadsModuleSchema = z5.object({
|
|
|
12377
12871
|
toll: roadTreatmentStyleSchema.optional()
|
|
12378
12872
|
}).strict().optional(),
|
|
12379
12873
|
renderStack: renderStackSchema.optional(),
|
|
12380
|
-
roundabouts: z5.object({
|
|
12874
|
+
roundabouts: z5.object({
|
|
12875
|
+
borderWidth: nonnegativeNumberValueSchema.describe(
|
|
12876
|
+
"Extra casing width in pixels, split across both edges. Defaults to the effective road border width."
|
|
12877
|
+
).optional(),
|
|
12878
|
+
casing: circleStyleSchema.optional(),
|
|
12879
|
+
fallbackWidth: nonnegativeNumberValueSchema.describe(
|
|
12880
|
+
"Road width in pixels used only when metric radii are absent. Defaults to the effective surface fill width."
|
|
12881
|
+
).optional(),
|
|
12882
|
+
fill: circleStyleSchema.optional()
|
|
12883
|
+
}).strict().optional(),
|
|
12381
12884
|
serviceTypes: z5.object({
|
|
12382
12885
|
alley: roadTreatmentStyleSchema.optional(),
|
|
12383
12886
|
crossover: roadTreatmentStyleSchema.optional(),
|
|
@@ -12425,6 +12928,9 @@ var labelsModuleSchema = z5.object({
|
|
|
12425
12928
|
language: z5.string().trim().min(1).optional(),
|
|
12426
12929
|
places: z5.enum(["none", "major", "all"]).optional(),
|
|
12427
12930
|
roadClasses: z5.array(roadClassSchema).min(1).optional(),
|
|
12931
|
+
roadPlacement: z5.enum(["ground", "foreground"]).describe(
|
|
12932
|
+
"Street names draw in the foreground by default. Ground names draw below buildings, use map-aligned text by default, and yield collisions to foreground symbols. Shields and junction numbers stay in the foreground."
|
|
12933
|
+
).optional(),
|
|
12428
12934
|
renderStack: renderStackSchema.optional(),
|
|
12429
12935
|
roads: z5.enum(["none", "highways", "major", "streets", "all"]).optional(),
|
|
12430
12936
|
styles: z5.object({
|
|
@@ -12522,6 +13028,8 @@ var modulesSchema = z5.object({
|
|
|
12522
13028
|
broadleafColors: z5.array(themeColorValueSchema).min(1).max(8).optional(),
|
|
12523
13029
|
coniferColors: z5.array(themeColorValueSchema).min(1).max(8).optional(),
|
|
12524
13030
|
palmColors: z5.array(themeColorValueSchema).min(1).max(8).optional(),
|
|
13031
|
+
hedges: z5.boolean().optional(),
|
|
13032
|
+
hedgeColors: z5.array(themeColorValueSchema).min(1).max(8).optional(),
|
|
12525
13033
|
finish: z5.enum(["faceted", "smooth", "toon"]).optional(),
|
|
12526
13034
|
edges: boundedThemeNumberSchema(0, 1).optional(),
|
|
12527
13035
|
edgeColor: themeColorValueSchema.optional(),
|
|
@@ -12598,6 +13106,7 @@ var layerBindingsSchema = z5.object({
|
|
|
12598
13106
|
roadShield: z5.string().trim().min(1).optional(),
|
|
12599
13107
|
sidewalk: z5.string().trim().min(1).optional(),
|
|
12600
13108
|
streetFurniture: z5.string().trim().min(1).optional(),
|
|
13109
|
+
trafficControl: z5.string().trim().min(1).optional(),
|
|
12601
13110
|
tree: z5.string().trim().min(1).optional(),
|
|
12602
13111
|
water: z5.string().trim().min(1),
|
|
12603
13112
|
waterName: z5.string().trim().min(1),
|
|
@@ -12633,6 +13142,7 @@ var fieldBindingsSchema = z5.object({
|
|
|
12633
13142
|
elevationFeet: z5.string().trim().min(1),
|
|
12634
13143
|
expressway: z5.string().trim().min(1),
|
|
12635
13144
|
foot: z5.string().trim().min(1),
|
|
13145
|
+
hedge: z5.string().trim().min(1).optional(),
|
|
12636
13146
|
height: z5.string().trim().min(1),
|
|
12637
13147
|
hasParts: z5.string().trim().min(1),
|
|
12638
13148
|
hide3d: z5.string().trim().min(1),
|
|
@@ -12803,6 +13313,14 @@ var marineSchema = z5.union([
|
|
|
12803
13313
|
nautical: z5.union([z5.literal(false), marineSourceSchema]).optional()
|
|
12804
13314
|
}).strict()
|
|
12805
13315
|
]);
|
|
13316
|
+
var boundaryAreasSchema = z5.union([
|
|
13317
|
+
z5.literal(false),
|
|
13318
|
+
z5.object({
|
|
13319
|
+
family: z5.enum(["boundaries"]),
|
|
13320
|
+
promoteId: z5.literal("ref").optional(),
|
|
13321
|
+
url: publicVectorUrlSchema.optional()
|
|
13322
|
+
}).strict()
|
|
13323
|
+
]);
|
|
12806
13324
|
var directVectorDataSchema = z5.object({
|
|
12807
13325
|
...vectorDataShape,
|
|
12808
13326
|
bounds: z5.tuple([
|
|
@@ -13077,10 +13595,32 @@ var terrainSchema = z5.union([
|
|
|
13077
13595
|
encoding: z5.enum(["mapbox", "terrarium"]).optional(),
|
|
13078
13596
|
exaggeration: z5.number().finite().min(0).max(10).optional(),
|
|
13079
13597
|
hillshade: terrainHillshadeSchema.optional(),
|
|
13598
|
+
tiles: z5.array(
|
|
13599
|
+
z5.string().refine(
|
|
13600
|
+
isSafeTileflowDemUrlTemplate,
|
|
13601
|
+
"Expected an HTTP(S) DEM template containing {z}, {x}, and {y}"
|
|
13602
|
+
)
|
|
13603
|
+
).min(1).max(8).optional(),
|
|
13604
|
+
tileSize: z5.union([z5.literal(256), z5.literal(512)]).optional(),
|
|
13605
|
+
minZoom: z5.number().int().min(0).max(24).optional(),
|
|
13606
|
+
maxZoom: z5.number().int().min(0).max(24).optional(),
|
|
13080
13607
|
mode: z5.enum(["none", "hillshade", "3d"]).optional(),
|
|
13081
13608
|
sourceId: z5.string().trim().min(1).optional(),
|
|
13082
13609
|
url: z5.string().trim().min(1).optional()
|
|
13083
|
-
}).strict()
|
|
13610
|
+
}).strict().superRefine((value, context) => {
|
|
13611
|
+
if (value.tiles && value.url)
|
|
13612
|
+
context.addIssue({
|
|
13613
|
+
code: "custom",
|
|
13614
|
+
message: "Terrain accepts tiles or url, not both",
|
|
13615
|
+
path: ["tiles"]
|
|
13616
|
+
});
|
|
13617
|
+
if ((value.minZoom ?? 0) > (value.maxZoom ?? 24))
|
|
13618
|
+
context.addIssue({
|
|
13619
|
+
code: "custom",
|
|
13620
|
+
message: "Terrain maxZoom must be at least minZoom",
|
|
13621
|
+
path: ["maxZoom"]
|
|
13622
|
+
});
|
|
13623
|
+
})
|
|
13084
13624
|
]);
|
|
13085
13625
|
var atmosphereColorLiteralSchema = z5.string().refine((value) => Color3.parse(value)?.a === 1, "Expected an opaque color");
|
|
13086
13626
|
var atmosphereColorSchema = z5.union([
|
|
@@ -13133,6 +13673,7 @@ var themesSchema = z5.record(tileflowThemeNameSchema, tileflowThemeSchema).super
|
|
|
13133
13673
|
var systemThemesSchema = z5.object({ dark: tileflowThemeNameSchema, light: tileflowThemeNameSchema }).strict();
|
|
13134
13674
|
var resolvedTileflowMapSchema = z5.object({
|
|
13135
13675
|
atmosphere: atmosphereSchema.optional(),
|
|
13676
|
+
boundaryAreas: boundaryAreasSchema.optional(),
|
|
13136
13677
|
data: dataSchema.optional(),
|
|
13137
13678
|
defaultTheme: tileflowThemeNameSchema,
|
|
13138
13679
|
fonts: fontDirectoriesSchema.optional(),
|
|
@@ -13557,7 +14098,10 @@ function resolveRaster(terrain, apiBaseUrl, encoding) {
|
|
|
13557
14098
|
...terrain.attribution ? { attribution: terrain.attribution } : {},
|
|
13558
14099
|
encoding,
|
|
13559
14100
|
type: "raster-dem",
|
|
13560
|
-
url
|
|
14101
|
+
...terrain.tiles ? { tiles: [...terrain.tiles] } : { url },
|
|
14102
|
+
...terrain.minZoom === void 0 ? {} : { minzoom: terrain.minZoom },
|
|
14103
|
+
...terrain.maxZoom === void 0 ? {} : { maxzoom: terrain.maxZoom },
|
|
14104
|
+
...terrain.tileSize === void 0 ? {} : { tileSize: terrain.tileSize }
|
|
13561
14105
|
}
|
|
13562
14106
|
};
|
|
13563
14107
|
}
|
|
@@ -13645,6 +14189,91 @@ var tileflowCompilationPlannerStages = Object.freeze([
|
|
|
13645
14189
|
"lowering"
|
|
13646
14190
|
]);
|
|
13647
14191
|
|
|
14192
|
+
// src/cartography/design-decisions.ts
|
|
14193
|
+
function describeTileflowDesignDecisions(input) {
|
|
14194
|
+
const { map, modules, theme, buildings: buildings2 } = input;
|
|
14195
|
+
const decisions = input.colors.map(
|
|
14196
|
+
({ role, source, value }) => ({
|
|
14197
|
+
path: `themes.${theme}.tokens.color.${role}`,
|
|
14198
|
+
source,
|
|
14199
|
+
value,
|
|
14200
|
+
reason: source === "theme" ? "The selected theme defines this role, including any authored token expression." : "The compiler derives this optional role from the selected theme color roles.",
|
|
14201
|
+
override: `Set tokens.color[${JSON.stringify(role)}] in defineTheme(); an explicit role replaces this derivation.`
|
|
14202
|
+
})
|
|
14203
|
+
);
|
|
14204
|
+
const policy = (domain, key, value, reason) => {
|
|
14205
|
+
const path = `modules.${domain}.${key}`;
|
|
14206
|
+
const authored = key.split(".").reduce(
|
|
14207
|
+
(node2, part) => node2 && typeof node2 === "object" ? node2[part] : void 0,
|
|
14208
|
+
map.modules?.[domain]
|
|
14209
|
+
);
|
|
14210
|
+
decisions.push({
|
|
14211
|
+
path,
|
|
14212
|
+
value,
|
|
14213
|
+
source: authored === void 0 ? "default" : "map",
|
|
14214
|
+
reason,
|
|
14215
|
+
override: `Set ${path} with ${domain}(...) or refine(...); reset() restores the SDK default. Render-stack refinements apply after module policies.`
|
|
14216
|
+
});
|
|
14217
|
+
};
|
|
14218
|
+
if (modules.roads.enabled !== false) {
|
|
14219
|
+
const roads2 = resolveRoads(modules.roads);
|
|
14220
|
+
policy(
|
|
14221
|
+
"roads",
|
|
14222
|
+
"rampOrder",
|
|
14223
|
+
roads2.rampOrder,
|
|
14224
|
+
"Below-roads draws ramp fills before receiving carriageways; by-class keeps each fill with its road class."
|
|
14225
|
+
);
|
|
14226
|
+
policy(
|
|
14227
|
+
"roads",
|
|
14228
|
+
"detail",
|
|
14229
|
+
roads2.detail,
|
|
14230
|
+
"Road detail selects the initial classes; explicit class settings and disabled classes refine that selection."
|
|
14231
|
+
);
|
|
14232
|
+
}
|
|
14233
|
+
if (modules.labels.enabled !== false) {
|
|
14234
|
+
const labels2 = resolveLabels(modules.labels);
|
|
14235
|
+
policy(
|
|
14236
|
+
"labels",
|
|
14237
|
+
"roadPlacement",
|
|
14238
|
+
labels2.roadPlacement,
|
|
14239
|
+
"Ground names draw below buildings and yield in collisions to foreground symbols; foreground names draw above buildings."
|
|
14240
|
+
);
|
|
14241
|
+
policy(
|
|
14242
|
+
"labels",
|
|
14243
|
+
"collisionPriority",
|
|
14244
|
+
labels2.collisionPriority,
|
|
14245
|
+
"Navigation prioritizes structural road classes within their drawing stack; it does not decouple collision priority from depth."
|
|
14246
|
+
);
|
|
14247
|
+
}
|
|
14248
|
+
if (buildings2) {
|
|
14249
|
+
policy(
|
|
14250
|
+
"buildings",
|
|
14251
|
+
"threeDimensional.edgeRadius",
|
|
14252
|
+
buildings2.edgeRadius,
|
|
14253
|
+
"Radius in metres rounds building corners; zero retains sharp corners."
|
|
14254
|
+
);
|
|
14255
|
+
policy(
|
|
14256
|
+
"buildings",
|
|
14257
|
+
"threeDimensional.lighting",
|
|
14258
|
+
buildings2.lighting,
|
|
14259
|
+
"Sun lighting shares scene illumination; style lighting uses the alternate building shading mode."
|
|
14260
|
+
);
|
|
14261
|
+
policy(
|
|
14262
|
+
"buildings",
|
|
14263
|
+
"threeDimensional.shadows",
|
|
14264
|
+
buildings2.shadows,
|
|
14265
|
+
"Cast shadows follow scene light; false disables them independently of contact occlusion."
|
|
14266
|
+
);
|
|
14267
|
+
policy(
|
|
14268
|
+
"buildings",
|
|
14269
|
+
"threeDimensional.occlusion",
|
|
14270
|
+
buildings2.occlusion,
|
|
14271
|
+
"Contact occlusion separates buildings from the ground and shades lower walls; false disables it independently."
|
|
14272
|
+
);
|
|
14273
|
+
}
|
|
14274
|
+
return decisions;
|
|
14275
|
+
}
|
|
14276
|
+
|
|
13648
14277
|
// src/cartography/graph.ts
|
|
13649
14278
|
function assembleTileflowLayerFamilies(families, extraConstraints = []) {
|
|
13650
14279
|
const slotOrder = resolveSlotOrder(extraConstraints);
|
|
@@ -14127,7 +14756,7 @@ function diagnoseFractionalLayoutZoomSteps(layers) {
|
|
|
14127
14756
|
function fractionalZoomSteps(value) {
|
|
14128
14757
|
if (!Array.isArray(value)) return [];
|
|
14129
14758
|
const stops = [];
|
|
14130
|
-
if (value[0] === "step" &&
|
|
14759
|
+
if (value[0] === "step" && isZoom2(value[1])) {
|
|
14131
14760
|
for (let index = 3; index < value.length; index += 2) {
|
|
14132
14761
|
const stop = value[index];
|
|
14133
14762
|
if (typeof stop === "number" && !Number.isInteger(stop)) stops.push(stop);
|
|
@@ -14136,7 +14765,7 @@ function fractionalZoomSteps(value) {
|
|
|
14136
14765
|
for (const entry of value) stops.push(...fractionalZoomSteps(entry));
|
|
14137
14766
|
return stops;
|
|
14138
14767
|
}
|
|
14139
|
-
function
|
|
14768
|
+
function isZoom2(value) {
|
|
14140
14769
|
return Array.isArray(value) && value.length === 1 && value[0] === "zoom";
|
|
14141
14770
|
}
|
|
14142
14771
|
|
|
@@ -14265,6 +14894,7 @@ function canMergeRoadLines(layers) {
|
|
|
14265
14894
|
function mergeRoadLineGroup(key, layers) {
|
|
14266
14895
|
const first = layers[0];
|
|
14267
14896
|
const conditions = layers.map((layer) => asFilter(layer.selector));
|
|
14897
|
+
if (conditions.some(containsZoom)) return void 0;
|
|
14268
14898
|
const paint = {};
|
|
14269
14899
|
const paintKeys = new Set(layers.flatMap((layer) => Object.keys(layer.style.appearance ?? {})));
|
|
14270
14900
|
for (const key2 of paintKeys) {
|
|
@@ -14911,7 +15541,7 @@ function sameLayerSource(left, right) {
|
|
|
14911
15541
|
}
|
|
14912
15542
|
function sameTopLevelProperties(left, right, additionallyIgnored = []) {
|
|
14913
15543
|
const ignored = new Set(additionallyIgnored);
|
|
14914
|
-
const
|
|
15544
|
+
const select2 = (layer) => ({
|
|
14915
15545
|
annotations: layer.annotations,
|
|
14916
15546
|
feature: layer.feature,
|
|
14917
15547
|
properties: layer.properties,
|
|
@@ -14920,7 +15550,7 @@ function sameTopLevelProperties(left, right, additionallyIgnored = []) {
|
|
|
14920
15550
|
),
|
|
14921
15551
|
renderer: layer.renderer
|
|
14922
15552
|
});
|
|
14923
|
-
return allEqual([
|
|
15553
|
+
return allEqual([select2(left), select2(right)]);
|
|
14924
15554
|
}
|
|
14925
15555
|
function isOrderedRenderPass(layer) {
|
|
14926
15556
|
return layer.origins.some(({ operations }) => operations.some(({ kind }) => kind === "pass"));
|
|
@@ -14987,6 +15617,7 @@ function tileflowBuildings3DDesignOf(module, context, layers, buildingModels, ti
|
|
|
14987
15617
|
const config = isRecord8(module) ? module : {};
|
|
14988
15618
|
if (config.enabled === false) return void 0;
|
|
14989
15619
|
const style = config.threeDimensional ?? {};
|
|
15620
|
+
const defaults4 = tileflowDefaultBuildings3DDesign;
|
|
14990
15621
|
let color2;
|
|
14991
15622
|
let verticalGradient = true;
|
|
14992
15623
|
for (const [id2, property] of colourSources) {
|
|
@@ -15009,15 +15640,16 @@ function tileflowBuildings3DDesignOf(module, context, layers, buildingModels, ti
|
|
|
15009
15640
|
...context.data.schema.layers.poi ? { places: context.data.schema.layers.poi } : {}
|
|
15010
15641
|
},
|
|
15011
15642
|
color: color2 ?? context.colors.buildings.generic,
|
|
15012
|
-
lighting: style.lighting === "
|
|
15643
|
+
lighting: style.lighting === "style" ? "style" : defaults4.lighting,
|
|
15013
15644
|
colorVariation: number(style.colorVariation, 0),
|
|
15014
15645
|
verticalGradient,
|
|
15015
15646
|
edges: number(style.edges, 0),
|
|
15647
|
+
edgeRadius: number(style.edgeRadius, defaults4.edgeRadius),
|
|
15016
15648
|
...typeof edgeColor === "string" ? { edgeColor } : {},
|
|
15017
15649
|
nightLight: number(style.nightLight, 0),
|
|
15018
15650
|
windows: style.windows === true,
|
|
15019
|
-
shadows: style.shadows ===
|
|
15020
|
-
occlusion: style.occlusion ===
|
|
15651
|
+
shadows: typeof style.shadows === "boolean" ? style.shadows : defaults4.shadows,
|
|
15652
|
+
occlusion: typeof style.occlusion === "boolean" ? style.occlusion : defaults4.occlusion,
|
|
15021
15653
|
...placedModels(style.models, buildingModels),
|
|
15022
15654
|
...modelColours(style.modelColors),
|
|
15023
15655
|
...landmarksOf(style.landmarks, tileflowLandmarks)
|
|
@@ -15064,7 +15696,7 @@ function tileflowStreetLightsDesignOf(module, context) {
|
|
|
15064
15696
|
const config = isRecord8(module) ? module : {};
|
|
15065
15697
|
const layers = context.data.schema.layers;
|
|
15066
15698
|
const style = config.streetLights ?? {};
|
|
15067
|
-
if (config.enabled === false || style.enabled
|
|
15699
|
+
if (config.enabled === false || style.enabled !== true || !layers.streetFurniture)
|
|
15068
15700
|
return void 0;
|
|
15069
15701
|
const color2 = style.color === void 0 ? void 0 : toMapLibreStyleValue(style.color);
|
|
15070
15702
|
const intensity = style.intensity === void 0 ? void 0 : toMapLibreStyleValue(style.intensity);
|
|
@@ -15137,6 +15769,10 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15137
15769
|
const data = runCompilationPhase("data", () => resolveTileflowData(config.data, { apiBaseUrl }));
|
|
15138
15770
|
const marine = runCompilationPhase("data", () => resolveMarine(config.marine, apiBaseUrl));
|
|
15139
15771
|
const terrain = runCompilationPhase("data", () => resolveTerrain(config.terrain, apiBaseUrl));
|
|
15772
|
+
const boundaryAreas = runCompilationPhase(
|
|
15773
|
+
"data",
|
|
15774
|
+
() => resolveBoundaryAreas(config.boundaryAreas, apiBaseUrl)
|
|
15775
|
+
);
|
|
15140
15776
|
const terrainSources = [terrain?.raster, terrain?.contours].filter(
|
|
15141
15777
|
(source) => source !== void 0
|
|
15142
15778
|
);
|
|
@@ -15177,11 +15813,17 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15177
15813
|
}
|
|
15178
15814
|
marineSourceIds.add(source.sourceId);
|
|
15179
15815
|
}
|
|
15816
|
+
if (boundaryAreas && (boundaryAreas.sourceId === data.sourceId || terrainSourceIds.has(boundaryAreas.sourceId) || marineSourceIds.has(boundaryAreas.sourceId))) {
|
|
15817
|
+
throw new Error(
|
|
15818
|
+
`Boundaries source ID "${boundaryAreas.sourceId}" conflicts with another Tileflow source.`
|
|
15819
|
+
);
|
|
15820
|
+
}
|
|
15180
15821
|
});
|
|
15181
15822
|
const platformSourceIds = [
|
|
15182
15823
|
data.sourceId,
|
|
15183
15824
|
...terrainSources.map(({ sourceId }) => sourceId),
|
|
15184
|
-
...marineSources.map(({ sourceId }) => sourceId)
|
|
15825
|
+
...marineSources.map(({ sourceId }) => sourceId),
|
|
15826
|
+
...boundaryAreas ? [boundaryAreas.sourceId] : []
|
|
15185
15827
|
];
|
|
15186
15828
|
const hostedSources = runCompilationPhase(
|
|
15187
15829
|
"data",
|
|
@@ -15209,7 +15851,14 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15209
15851
|
"data",
|
|
15210
15852
|
() => createSemanticDataView(semanticData)
|
|
15211
15853
|
);
|
|
15212
|
-
const
|
|
15854
|
+
const colorDecisions = [];
|
|
15855
|
+
const colors = runCompilationPhase(
|
|
15856
|
+
"theme",
|
|
15857
|
+
() => resolveThemeColors(
|
|
15858
|
+
selected.theme,
|
|
15859
|
+
onReport ? (decision) => colorDecisions.push(decision) : void 0
|
|
15860
|
+
)
|
|
15861
|
+
);
|
|
15213
15862
|
const images = runCompilationPhase("theme", () => resolveThemeImages(selected.theme));
|
|
15214
15863
|
const typography = resolvedTheme.typography;
|
|
15215
15864
|
const context = {
|
|
@@ -15278,8 +15927,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15278
15927
|
() => insertTileflowOverlays({
|
|
15279
15928
|
basemapLayers,
|
|
15280
15929
|
mapId: config.id,
|
|
15281
|
-
overlays: config.overlays,
|
|
15282
|
-
sourceIds:
|
|
15930
|
+
overlays: validateBoundaryChoroplethOverlays(config.id, config.overlays, boundaryAreas),
|
|
15931
|
+
sourceIds: [
|
|
15932
|
+
...Object.keys(hostedSources.definitions),
|
|
15933
|
+
...boundaryAreas ? [boundaryAreas.sourceId] : []
|
|
15934
|
+
]
|
|
15283
15935
|
})
|
|
15284
15936
|
);
|
|
15285
15937
|
const plannedLayers = loweredOverlays.layers;
|
|
@@ -15436,6 +16088,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15436
16088
|
...marine?.nautical ? { [marine.nautical.sourceId]: marine.nautical.source } : {},
|
|
15437
16089
|
...terrain?.raster ? { [terrain.raster.sourceId]: terrain.raster.source } : {},
|
|
15438
16090
|
...terrain?.contours ? { [terrain.contours.sourceId]: terrain.contours.source } : {},
|
|
16091
|
+
...boundaryAreas ? { [boundaryAreas.sourceId]: boundaryAreas.source } : {},
|
|
15439
16092
|
...hostedSources.definitions
|
|
15440
16093
|
};
|
|
15441
16094
|
const sourceIdentities = {
|
|
@@ -15443,6 +16096,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15443
16096
|
...marine?.bathymetry?.vector ? { [marine.bathymetry.vector.sourceId]: marine.bathymetry.vector.identity } : {},
|
|
15444
16097
|
...marine?.bathymetry?.relief ? { [marine.bathymetry.relief.sourceId]: marine.bathymetry.relief.identity } : {},
|
|
15445
16098
|
...marine?.nautical ? { [marine.nautical.sourceId]: marine.nautical.identity } : {},
|
|
16099
|
+
...boundaryAreas ? { [boundaryAreas.sourceId]: boundaryAreas.identity } : {},
|
|
15446
16100
|
...hostedSources.identities
|
|
15447
16101
|
};
|
|
15448
16102
|
const sourceRequirements = runCompilationPhase(
|
|
@@ -15527,6 +16181,15 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
|
|
|
15527
16181
|
};
|
|
15528
16182
|
const report = {
|
|
15529
16183
|
...partialReport,
|
|
16184
|
+
...onReport ? {
|
|
16185
|
+
decisions: describeTileflowDesignDecisions({
|
|
16186
|
+
map: input,
|
|
16187
|
+
modules,
|
|
16188
|
+
theme: selected.name,
|
|
16189
|
+
colors: colorDecisions,
|
|
16190
|
+
...buildings3D ? { buildings: buildings3D } : {}
|
|
16191
|
+
})
|
|
16192
|
+
} : {},
|
|
15530
16193
|
requirements: sourceRequirements
|
|
15531
16194
|
};
|
|
15532
16195
|
onReport?.(report);
|
|
@@ -16330,29 +16993,6 @@ function checkShotTiming(shot, context) {
|
|
|
16330
16993
|
}
|
|
16331
16994
|
}
|
|
16332
16995
|
|
|
16333
|
-
// src/maps/operations.ts
|
|
16334
|
-
var resetValue = deepFreeze({ $tileflow: "reset" });
|
|
16335
|
-
var disableValue = deepFreeze({ op: "disable" });
|
|
16336
|
-
function isTileflowReset(value) {
|
|
16337
|
-
if (!isPlainRecord3(value)) return false;
|
|
16338
|
-
return value.$tileflow === "reset" && Object.keys(value).length === 1;
|
|
16339
|
-
}
|
|
16340
|
-
function isTileflowModuleOperation(value) {
|
|
16341
|
-
if (!isPlainRecord3(value) || typeof value.op !== "string") return false;
|
|
16342
|
-
return value.op === "disable" || value.op === "refine";
|
|
16343
|
-
}
|
|
16344
|
-
function deepFreeze(value) {
|
|
16345
|
-
if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
16346
|
-
Object.freeze(value);
|
|
16347
|
-
for (const child of Object.values(value)) deepFreeze(child);
|
|
16348
|
-
return value;
|
|
16349
|
-
}
|
|
16350
|
-
function isPlainRecord3(value) {
|
|
16351
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
16352
|
-
const prototype = Object.getPrototypeOf(value);
|
|
16353
|
-
return prototype === Object.prototype || prototype === null;
|
|
16354
|
-
}
|
|
16355
|
-
|
|
16356
16996
|
// src/maps/resolve.ts
|
|
16357
16997
|
var tileflowMapDefaultMaxDepth = 64;
|
|
16358
16998
|
var TileflowMapResolutionError = class extends Error {
|
|
@@ -16369,6 +17009,7 @@ var TileflowMapResolutionError = class extends Error {
|
|
|
16369
17009
|
};
|
|
16370
17010
|
var tileflowMapMergeStrategies = {
|
|
16371
17011
|
atmosphere: "atomic",
|
|
17012
|
+
boundaryAreas: "atomic",
|
|
16372
17013
|
data: "atomic",
|
|
16373
17014
|
defaultTheme: "atomic",
|
|
16374
17015
|
extends: "lineage",
|