@tileflow/core 0.1.0-alpha.36 → 0.1.0-alpha.38

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/dist/build.js CHANGED
@@ -45,6 +45,112 @@ function compileTileflowSky(options) {
45
45
  };
46
46
  }
47
47
 
48
+ // src/models-3d-model.ts
49
+ var tileflowModelParts = Object.freeze({
50
+ surface: 0,
51
+ secondary: 1,
52
+ glass: 2,
53
+ accent: 3,
54
+ roof: 4
55
+ });
56
+ var tileflowModelLightingRoles = Object.freeze({
57
+ unclassified: 0,
58
+ wall: 1,
59
+ door: 2,
60
+ roof: 3,
61
+ window: 4,
62
+ lamp: 5,
63
+ logo: 6
64
+ });
65
+ var tileflowModelDefaultLightingRoles = Object.freeze([1, 1, 4, 1, 3]);
66
+ var tileflowModelLimits = Object.freeze({
67
+ /** Levels of detail, nearest first. */
68
+ levels: 3,
69
+ /** Vertices of one level. */
70
+ vertices: 2e5,
71
+ /** Triangles of one level. */
72
+ triangles: 2e5,
73
+ /** Points of the footprint's rings, together. */
74
+ footprintPoints: 4096,
75
+ /** Half the model's widest extent, in metres. */
76
+ radius: 2e3,
77
+ /** The model's height, in metres. */
78
+ height: 1e3,
79
+ /** Models in one prepared file, as a landmark tile holds them. */
80
+ models: 512,
81
+ /** Bytes of one prepared file. */
82
+ bytes: 16 * 1024 * 1024
83
+ });
84
+
85
+ // src/building-illumination.ts
86
+ var defaultBuildingFloodLight = Object.freeze({
87
+ intensity: 0,
88
+ brightnessScale: 1,
89
+ color: "#f5cfa8",
90
+ groundRadius: [30, 100],
91
+ minGroundHeight: 30,
92
+ minWallHeight: 200,
93
+ wallHeightRatio: 1 / 3
94
+ });
95
+ var defaultModelEmissionGradient = [0, 1, 1, 1, 0];
96
+ function readBuildingFloodLight(value) {
97
+ if (!record(value)) return void 0;
98
+ const defaults4 = defaultBuildingFloodLight;
99
+ const radius = value.groundRadius;
100
+ return {
101
+ intensity: bounded(value.intensity, 0, 1, 0),
102
+ brightnessScale: bounded(value.brightnessScale, 0, 16, 1),
103
+ color: typeof value.color === "string" ? value.color : defaults4.color,
104
+ groundRadius: Array.isArray(radius) && radius.length === 2 && radius.every((v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 2048) && radius[0] <= radius[1] ? [radius[0], radius[1]] : defaults4.groundRadius,
105
+ minGroundHeight: bounded(value.minGroundHeight, 0, 6e3, defaults4.minGroundHeight),
106
+ minWallHeight: bounded(value.minWallHeight, 0, 6e3, defaults4.minWallHeight),
107
+ wallHeightRatio: bounded(value.wallHeightRatio, 0, 1, defaults4.wallHeightRatio)
108
+ };
109
+ }
110
+ function readModelRoleLighting(value) {
111
+ if (!record(value)) return void 0;
112
+ const result = {};
113
+ for (const role of Object.keys(tileflowModelLightingRoles)) {
114
+ const entry = value[role];
115
+ if (!record(entry)) continue;
116
+ const gradient = entry.emissiveHeight;
117
+ result[role] = {
118
+ emissiveStrength: bounded(entry.emissiveStrength, 0, 5, 0),
119
+ ...entry.emissiveVariation === void 0 ? {} : { emissiveVariation: bounded(entry.emissiveVariation, 0, 1, 0) },
120
+ ...typeof entry.emissiveColor === "string" ? { emissiveColor: entry.emissiveColor } : {},
121
+ ...typeof entry.color === "string" ? { color: entry.color } : {},
122
+ ...entry.colorMix === void 0 ? {} : { colorMix: bounded(entry.colorMix, 0, 1, 1) },
123
+ ...entry.metallic === void 0 ? {} : { metallic: bounded(entry.metallic, 0, 1, 0) },
124
+ ...entry.roughness === void 0 ? {} : { roughness: bounded(entry.roughness, 0, 1, 1) },
125
+ ...readRoleColorVariation(entry.colorVariation),
126
+ emissiveHeight: Array.isArray(gradient) && gradient.length === 5 && gradient.every(
127
+ (v, i) => typeof v === "number" && Number.isFinite(v) && v <= 1 && v >= (i === 4 ? -1 : 0)
128
+ ) ? gradient : defaultModelEmissionGradient
129
+ };
130
+ }
131
+ return Object.keys(result).length ? result : void 0;
132
+ }
133
+ function readRoleColorVariation(value) {
134
+ if (!record(value)) return {};
135
+ for (const [key, max] of [
136
+ ["hue", 360],
137
+ ["saturation", 100],
138
+ ["lightness", 100]
139
+ ]) {
140
+ const range2 = value[key];
141
+ if (!Array.isArray(range2) || range2.length !== 2 || range2[0] > range2[1] || !range2.every((v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= max))
142
+ return {};
143
+ }
144
+ if (value.space !== void 0 && value.space !== "srgb" && value.space !== "linear") return {};
145
+ return { colorVariation: value };
146
+ }
147
+ function bounded(value, min, max, fallback) {
148
+ return typeof value === "number" && Number.isFinite(value) && value >= min && value <= max ? value : fallback;
149
+ }
150
+ function record(value) {
151
+ return typeof value === "object" && value !== null && !Array.isArray(value);
152
+ }
153
+
48
154
  // src/buildings-3d-design.ts
49
155
  var tileflowBuildingsMetadataKey = "tileflow:buildings";
50
156
  var tileflowDefaultBuildings3DDesign = Object.freeze({
@@ -59,11 +165,22 @@ var tileflowDefaultBuildings3DDesign = Object.freeze({
59
165
  nightLight: 0,
60
166
  windows: false,
61
167
  shadows: true,
62
- occlusion: true
168
+ occlusion: true,
169
+ modelOcclusion: 0.75
63
170
  });
64
171
  function tileflowBuildingsMetadata(design) {
65
172
  return { version: 1, ...design };
66
173
  }
174
+ function readTileflowBuildingsEntrance(value) {
175
+ if (!isRecord(value)) return {};
176
+ const band = (entry) => Array.isArray(entry) && entry.length === 2 && entry.every((zoom3) => typeof zoom3 === "number" && zoom3 >= 0 && zoom3 <= 24) && entry[0] < entry[1] ? [entry[0], entry[1]] : void 0;
177
+ const buildings2 = band(value.buildings);
178
+ const models = band(value.models);
179
+ return buildings2 || models ? { entrance: { ...buildings2 ? { buildings: buildings2 } : {}, ...models ? { models } : {} } } : {};
180
+ }
181
+ function isRecord(value) {
182
+ return typeof value === "object" && value !== null && !Array.isArray(value);
183
+ }
67
184
 
68
185
  // src/data/world-generation.ts
69
186
  var tileflowWorldGeneration = "v1";
@@ -183,26 +300,26 @@ var canonicalFields = {
183
300
  };
184
301
  var worldAttribution = '<a href="https://tileflow.dev/copyright">\xA9 Tileflow</a> <a href="https://www.openmaptiles.org/">\xA9 OpenMapTiles</a> <a href="https://www.openstreetmap.org/copyright">\xA9 OpenStreetMap</a>';
185
302
  function openMapTiles(options = {}) {
186
- const layers = { ...canonicalLayers, ...options.layers };
187
- const fields = { ...canonicalFields, ...options.fields };
188
- if (options.capabilities?.businessCorridor === false) delete layers.businessCorridor;
303
+ const layers2 = { ...canonicalLayers, ...options.layers };
304
+ const fields2 = { ...canonicalFields, ...options.fields };
305
+ if (options.capabilities?.businessCorridor === false) delete layers2.businessCorridor;
189
306
  if (options.capabilities?.bathymetry === true) {
190
- layers.bathymetry ??= "bathymetry";
191
- fields.bathymetryMinDepth ??= "min_depth";
192
- fields.bathymetrySortKey ??= "sort_key";
307
+ layers2.bathymetry ??= "bathymetry";
308
+ fields2.bathymetryMinDepth ??= "min_depth";
309
+ fields2.bathymetrySortKey ??= "sort_key";
193
310
  } else {
194
- delete layers.bathymetry;
195
- delete fields.bathymetryMinDepth;
196
- delete fields.bathymetrySortKey;
311
+ delete layers2.bathymetry;
312
+ delete fields2.bathymetryMinDepth;
313
+ delete fields2.bathymetrySortKey;
197
314
  }
198
- if (options.capabilities?.canopy === false) delete layers.canopy;
199
- if (options.capabilities?.globalLandcover === false) delete layers.globalLandcover;
200
- if (options.capabilities?.tree === false) delete layers.tree;
315
+ if (options.capabilities?.canopy === false) delete layers2.canopy;
316
+ if (options.capabilities?.globalLandcover === false) delete layers2.globalLandcover;
317
+ if (options.capabilities?.tree === false) delete layers2.tree;
201
318
  return {
202
319
  type: "openmaptiles",
203
320
  contractVersion: openMapTilesContractVersion,
204
- fields,
205
- layers,
321
+ fields: fields2,
322
+ layers: layers2,
206
323
  semantics: {
207
324
  parkLayer: options.semantics?.parkLayer ?? "mixed"
208
325
  }
@@ -228,6 +345,7 @@ function tileflowWorldV1Schema(options = {}) {
228
345
  sidewalk: "sidewalk",
229
346
  streetFurniture: "street_furniture",
230
347
  trafficControl: "traffic_control",
348
+ transportationOutline: "transportation_outline",
231
349
  ...options.layers
232
350
  },
233
351
  semantics: { ...options.semantics, parkLayer: "protected-only" }
@@ -496,25 +614,25 @@ function inferTileflowDataRequirements(style, options = {}) {
496
614
  for (const [index, layer] of (style.layers ?? []).entries()) {
497
615
  if (layer.source !== sourceId || typeof layer["source-layer"] !== "string") continue;
498
616
  const sourceLayer = layer["source-layer"];
499
- const fields = getOrCreate(fieldsByLayer, sourceLayer);
617
+ const fields2 = getOrCreate(fieldsByLayer, sourceLayer);
500
618
  const filter = layer.filter === void 0 ? void 0 : convertFilter(layer.filter);
501
- collectFieldAccesses(filter, fields, dynamicAccesses, `layers[${index}].filter`);
619
+ collectFieldAccesses(filter, fields2, dynamicAccesses, `layers[${index}].filter`);
502
620
  for (const key of ["layout", "paint"]) {
503
- collectFieldAccesses(layer[key], fields, dynamicAccesses, `layers[${index}].${key}`);
621
+ collectFieldAccesses(layer[key], fields2, dynamicAccesses, `layers[${index}].${key}`);
504
622
  }
505
623
  }
506
624
  for (const requirement of options.additional ?? []) {
507
625
  requirePortableName(requirement.id, "additional source layer");
508
- const fields = getOrCreate(fieldsByLayer, requirement.id);
626
+ const fields2 = getOrCreate(fieldsByLayer, requirement.id);
509
627
  for (const field2 of requirement.fields) {
510
628
  requirePortableName(field2.name, `additional field on ${requirement.id}`);
511
- const existing = fields.get(field2.name);
629
+ const existing = fields2.get(field2.name);
512
630
  if (existing !== void 0 && field2.type !== void 0 && existing !== field2.type) {
513
631
  throw new TypeError(
514
632
  `Conflicting manual data types for ${requirement.id}.${field2.name}: ${existing} and ${field2.type}.`
515
633
  );
516
634
  }
517
- fields.set(field2.name, field2.type ?? existing);
635
+ fields2.set(field2.name, field2.type ?? existing);
518
636
  }
519
637
  }
520
638
  return {
@@ -522,8 +640,8 @@ function inferTileflowDataRequirements(style, options = {}) {
522
640
  dynamicAccesses: [...dynamicAccesses].sort(compareCodeUnits),
523
641
  schemaVersion: 1,
524
642
  sourceId,
525
- sourceLayers: [...fieldsByLayer].sort(([left], [right]) => compareCodeUnits(left, right)).map(([id2, fields]) => ({
526
- fields: [...fields].sort(([left], [right]) => compareCodeUnits(left, right)).map(([name, type]) => ({ name, ...type === void 0 ? {} : { type } })),
643
+ sourceLayers: [...fieldsByLayer].sort(([left], [right]) => compareCodeUnits(left, right)).map(([id2, fields2]) => ({
644
+ fields: [...fields2].sort(([left], [right]) => compareCodeUnits(left, right)).map(([name, type]) => ({ name, ...type === void 0 ? {} : { type } })),
527
645
  id: id2
528
646
  }))
529
647
  };
@@ -598,40 +716,40 @@ function validateTileflowDataCompatibility(requirements, contract) {
598
716
  }
599
717
  return issues;
600
718
  }
601
- function collectFieldAccesses(value, fields, dynamicAccesses, path) {
719
+ function collectFieldAccesses(value, fields2, dynamicAccesses, path) {
602
720
  if (Array.isArray(value)) {
603
721
  const operator = value[0];
604
722
  if (operator === "literal") return;
605
723
  if (operator === "get" || operator === "has") {
606
724
  const featureProperties = value.length === 2 || isPropertiesExpression(value[2]);
607
725
  if (featureProperties) {
608
- if (typeof value[1] === "string") fields.set(value[1], fields.get(value[1]));
726
+ if (typeof value[1] === "string") fields2.set(value[1], fields2.get(value[1]));
609
727
  else dynamicAccesses.add(`${path}[1]`);
610
728
  }
611
729
  if (typeof value[1] !== "string") {
612
- collectFieldAccesses(value[1], fields, dynamicAccesses, `${path}[1]`);
730
+ collectFieldAccesses(value[1], fields2, dynamicAccesses, `${path}[1]`);
613
731
  }
614
732
  if (!featureProperties) {
615
- collectFieldAccesses(value[2], fields, dynamicAccesses, `${path}[2]`);
733
+ collectFieldAccesses(value[2], fields2, dynamicAccesses, `${path}[2]`);
616
734
  }
617
735
  for (let index = 3; index < value.length; index += 1) {
618
- collectFieldAccesses(value[index], fields, dynamicAccesses, `${path}[${index}]`);
736
+ collectFieldAccesses(value[index], fields2, dynamicAccesses, `${path}[${index}]`);
619
737
  }
620
738
  return;
621
739
  }
622
740
  if (operator === "properties") dynamicAccesses.add(path);
623
741
  for (let index = 1; index < value.length; index += 1) {
624
- collectFieldAccesses(value[index], fields, dynamicAccesses, `${path}[${index}]`);
742
+ collectFieldAccesses(value[index], fields2, dynamicAccesses, `${path}[${index}]`);
625
743
  }
626
744
  return;
627
745
  }
628
746
  if (!value || typeof value !== "object") return;
629
- const record3 = value;
630
- if (typeof record3.property === "string" && Array.isArray(record3.stops)) {
631
- fields.set(record3.property, fields.get(record3.property));
747
+ const record4 = value;
748
+ if (typeof record4.property === "string" && Array.isArray(record4.stops)) {
749
+ fields2.set(record4.property, fields2.get(record4.property));
632
750
  }
633
- for (const [key, child] of Object.entries(record3)) {
634
- collectFieldAccesses(child, fields, dynamicAccesses, `${path}.${key}`);
751
+ for (const [key, child] of Object.entries(record4)) {
752
+ collectFieldAccesses(child, fields2, dynamicAccesses, `${path}.${key}`);
635
753
  }
636
754
  }
637
755
  function isPropertiesExpression(value) {
@@ -1002,26 +1120,26 @@ function toMapLibreStyleValue(value) {
1002
1120
  return cloneJson2(value);
1003
1121
  }
1004
1122
  function isTileflowExpression(value) {
1005
- return isRecord(value) && value.kind === "expression" && Array.isArray(value.value);
1123
+ return isRecord2(value) && value.kind === "expression" && Array.isArray(value.value);
1006
1124
  }
1007
1125
  function isTileflowZoomValue(value) {
1008
- return isRecord(value) && value.kind === "zoom" && Array.isArray(value.stops);
1126
+ return isRecord2(value) && value.kind === "zoom" && Array.isArray(value.stops);
1009
1127
  }
1010
1128
  function isTileflowThemeTokenReference(value) {
1011
- return isRecord(value) && hasExactOwnKeys(value, ["category", "kind", "token"]) && value.kind === "theme-token" && typeof value.token === "string" && /^[A-Za-z][A-Za-z0-9_-]*(?:\.[A-Za-z][A-Za-z0-9_-]*)*$/.test(value.token) && tileflowThemeTokenCategories.includes(value.category);
1129
+ return isRecord2(value) && hasExactOwnKeys(value, ["category", "kind", "token"]) && value.kind === "theme-token" && typeof value.token === "string" && /^[A-Za-z][A-Za-z0-9_-]*(?:\.[A-Za-z][A-Za-z0-9_-]*)*$/.test(value.token) && tileflowThemeTokenCategories.includes(value.category);
1012
1130
  }
1013
1131
  function isTileflowFixedValue(value) {
1014
- return isRecord(value) && hasExactOwnKeys(value, ["kind", "reason", "value"]) && value.kind === "theme-fixed" && typeof value.reason === "string" && Boolean(value.reason.trim()) && Object.hasOwn(value, "value");
1132
+ return isRecord2(value) && hasExactOwnKeys(value, ["kind", "reason", "value"]) && value.kind === "theme-fixed" && typeof value.reason === "string" && Boolean(value.reason.trim()) && Object.hasOwn(value, "value");
1015
1133
  }
1016
1134
  function isTileflowThemeColorOperation(value) {
1017
- if (!isRecord(value) || value.kind !== "theme-color") return false;
1135
+ if (!isRecord2(value) || value.kind !== "theme-color") return false;
1018
1136
  if (value.operation === "alpha") {
1019
1137
  return hasExactOwnKeys(value, ["color", "kind", "opacity", "operation"]) && Object.hasOwn(value, "color") && Object.hasOwn(value, "opacity");
1020
1138
  }
1021
1139
  return value.operation === "mix" && hasExactOwnKeys(value, ["amount", "from", "kind", "operation", "space", "to"]) && value.space === "oklch" && Object.hasOwn(value, "amount") && Object.hasOwn(value, "from") && Object.hasOwn(value, "to");
1022
1140
  }
1023
1141
  function isTileflowThemeValueNode(value) {
1024
- return isRecord(value) && (value.kind === "theme-token" || value.kind === "theme-fixed" || value.kind === "theme-color");
1142
+ return isRecord2(value) && (value.kind === "theme-token" || value.kind === "theme-fixed" || value.kind === "theme-color");
1025
1143
  }
1026
1144
  function createZoomValue(interpolation, stops, base) {
1027
1145
  if (stops.length === 0) {
@@ -1061,7 +1179,7 @@ function cloneJson2(value) {
1061
1179
  }
1062
1180
  return JSON.parse(serialized);
1063
1181
  }
1064
- function isRecord(value) {
1182
+ function isRecord2(value) {
1065
1183
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1066
1184
  }
1067
1185
  function hasExactOwnKeys(value, expectedKeys) {
@@ -1094,7 +1212,7 @@ function applyLineStyle(layer, style) {
1094
1212
  const layout = { ...asRecord(layer.layout) };
1095
1213
  setPaint(paint, "line-blur", style.blur);
1096
1214
  setPaint(paint, "line-color", style.color);
1097
- setPaint(paint, "line-dasharray", style.dash);
1215
+ if (!isSolidDash(style.dash)) setPaint(paint, "line-dasharray", style.dash);
1098
1216
  setPaint(paint, "line-gap-width", style.gapWidth);
1099
1217
  setPaint(paint, "line-offset", style.offset);
1100
1218
  setPaint(paint, "line-opacity", style.opacity);
@@ -1237,20 +1355,20 @@ function applySymbolStyle(layer, style) {
1237
1355
  return applyLayerRange(result, range2);
1238
1356
  }
1239
1357
  function createAreaLayers(base, style) {
1240
- const layers = [];
1358
+ const layers2 = [];
1241
1359
  if (style.fill && style.fill.visible !== false) {
1242
- layers.push({
1360
+ layers2.push({
1243
1361
  layer: applyFillStyle({ ...base, type: "fill" }, style.fill),
1244
1362
  phase: "fill"
1245
1363
  });
1246
1364
  }
1247
1365
  if (style.outline && style.outline.visible !== false) {
1248
- layers.push({
1366
+ layers2.push({
1249
1367
  layer: applyLineStyle({ ...base, id: `${base.id}-outline`, type: "line" }, style.outline),
1250
1368
  phase: "outline"
1251
1369
  });
1252
1370
  }
1253
- return layers;
1371
+ return layers2;
1254
1372
  }
1255
1373
  function applyCircleStyle(layer, style) {
1256
1374
  const paint = { ...asRecord(layer.paint) };
@@ -1338,6 +1456,12 @@ function withoutGovernedAllowOverlap(layout) {
1338
1456
  }
1339
1457
  return result;
1340
1458
  }
1459
+ function isSolidDash(value) {
1460
+ if (value === void 0) return false;
1461
+ const dash = toMapLibreStyleValue(value);
1462
+ const array = Array.isArray(dash) && dash[0] === "literal" ? dash[1] : dash;
1463
+ return Array.isArray(array) && array.length === 2 && array[0] === 1 && array[1] === 0;
1464
+ }
1341
1465
  function setPaint(paint, property, value) {
1342
1466
  if (value !== void 0) paint[property] = toMapLibreStyleValue(value);
1343
1467
  }
@@ -1382,10 +1506,10 @@ function mergeTileflowDesign(base, ...overlays) {
1382
1506
  }
1383
1507
  function mergeValue(base, overlay) {
1384
1508
  if (overlay === void 0) return cloneJson3(base);
1385
- if (Array.isArray(overlay) || isTileflowExpression(overlay) || isTileflowThemeValueNode(overlay) || isTileflowZoomValue(overlay) || !isRecord2(overlay)) {
1509
+ if (Array.isArray(overlay) || isTileflowExpression(overlay) || isTileflowThemeValueNode(overlay) || isTileflowZoomValue(overlay) || !isRecord3(overlay)) {
1386
1510
  return cloneJson3(overlay);
1387
1511
  }
1388
- const baseRecord = isRecord2(base) ? base : {};
1512
+ const baseRecord = isRecord3(base) ? base : {};
1389
1513
  const result = {};
1390
1514
  for (const key of /* @__PURE__ */ new Set([...Object.keys(baseRecord), ...Object.keys(overlay)])) {
1391
1515
  const overlayValue = overlay[key];
@@ -1401,7 +1525,7 @@ function cloneJson3(value) {
1401
1525
  if (value === void 0) return value;
1402
1526
  return JSON.parse(JSON.stringify(value));
1403
1527
  }
1404
- function isRecord2(value) {
1528
+ function isRecord3(value) {
1405
1529
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1406
1530
  }
1407
1531
 
@@ -1860,22 +1984,37 @@ function compileBuildings(request, context) {
1860
1984
  }
1861
1985
  return contributions;
1862
1986
  }
1987
+ var buildingTargetsByMode = {
1988
+ flat: ["buildings.flat.fill", "buildings.flat.outline"],
1989
+ "3d": ["buildings.extrusion"]
1990
+ };
1991
+ function buildingTargetsOfOtherModes(request) {
1992
+ const activeMode = request?.mode ?? "flat";
1993
+ const targets = /* @__PURE__ */ new Map();
1994
+ for (const [mode, modeTargets] of Object.entries(buildingTargetsByMode)) {
1995
+ if (mode === activeMode) continue;
1996
+ for (const target of modeTargets) {
1997
+ targets.set(target, { activeMode, mode, owner: "buildings" });
1998
+ }
1999
+ }
2000
+ return targets;
2001
+ }
1863
2002
  function buildingColorExpression(context) {
1864
- const fields = context.data.schema.fields;
2003
+ const fields2 = context.data.schema.fields;
1865
2004
  const colors = context.colors.buildings;
1866
2005
  return [
1867
2006
  "case",
1868
- ["==", ["coalesce", ["get", fields.buildingTone], ""], "active"],
2007
+ ["==", ["coalesce", ["get", fields2.buildingTone], ""], "active"],
1869
2008
  colors.active,
1870
- ["==", ["coalesce", ["get", fields.buildingTone], ""], "destination"],
2009
+ ["==", ["coalesce", ["get", fields2.buildingTone], ""], "destination"],
1871
2010
  colors.destination,
1872
- ["==", ["coalesce", ["get", fields.buildingTone], ""], "commercial"],
2011
+ ["==", ["coalesce", ["get", fields2.buildingTone], ""], "commercial"],
1873
2012
  colors.commercial,
1874
2013
  // Without a tone, World's use class: housing, shops and offices, public buildings and works
1875
2014
  // each take their theme colour; a shed, a garage or an unknown use the generic one.
1876
2015
  [
1877
2016
  "match",
1878
- ["coalesce", ["get", fields.class], ""],
2017
+ ["coalesce", ["get", fields2.class], ""],
1879
2018
  "residential",
1880
2019
  colors.residential,
1881
2020
  "commercial",
@@ -1899,7 +2038,7 @@ function businessCorridorVisibilityFilter(context) {
1899
2038
  ];
1900
2039
  }
1901
2040
  function buildingHeightExpression(context) {
1902
- const fields = context.data.schema.fields;
2041
+ const fields2 = context.data.schema.fields;
1903
2042
  return [
1904
2043
  "max",
1905
2044
  0,
@@ -1907,7 +2046,7 @@ function buildingHeightExpression(context) {
1907
2046
  "to-number",
1908
2047
  [
1909
2048
  "coalesce",
1910
- ["get", fields.renderHeight],
2049
+ ["get", fields2.renderHeight],
1911
2050
  // V8.14 materializes this fallback in the tile. Keep the defensive
1912
2051
  // value only for malformed or legacy third-party sources.
1913
2052
  5
@@ -1917,11 +2056,11 @@ function buildingHeightExpression(context) {
1917
2056
  ];
1918
2057
  }
1919
2058
  function buildingBaseExpression(context, height) {
1920
- const fields = context.data.schema.fields;
2059
+ const fields2 = context.data.schema.fields;
1921
2060
  return [
1922
2061
  "max",
1923
2062
  0,
1924
- ["min", ["to-number", ["coalesce", ["get", fields.renderMinHeight], 0], 0], height]
2063
+ ["min", ["to-number", ["coalesce", ["get", fields2.renderMinHeight], 0], 0], height]
1925
2064
  ];
1926
2065
  }
1927
2066
 
@@ -1976,30 +2115,24 @@ var tileflowRoadConstructionClasses = [
1976
2115
  function isTileflowPathRoadClass(roadClass) {
1977
2116
  return tileflowPathRoadClasses.includes(roadClass);
1978
2117
  }
1979
- function tileflowRoadClassFilter(fields, roadClass) {
2118
+ function tileflowRoadClassFilter(fields2, roadClass) {
2119
+ const classes = tileflowRoadClassValues(roadClass);
1980
2120
  if (isTileflowPathRoadClass(roadClass)) {
1981
2121
  return [
1982
2122
  "all",
1983
- ["match", ["get", fields.class], ["path", ...constructionClasses[roadClass]], true, false],
1984
- ["match", ["get", fields.subclass], pathSubclasses[roadClass], true, false]
2123
+ ["match", ["get", fields2.class], classes, true, false],
2124
+ ["match", ["get", fields2.subclass], pathSubclasses[roadClass], true, false]
1985
2125
  ];
1986
2126
  }
1987
- const classes = roadClass === "minor" ? [
1988
- "minor",
1989
- "residential",
1990
- "unclassified",
1991
- ...compatibleRoadClasses[roadClass] ?? [],
1992
- ...constructionClasses[roadClass]
1993
- ] : roadClass === "service" ? [
1994
- "service",
1995
- ...compatibleRoadClasses[roadClass] ?? [],
1996
- ...constructionClasses[roadClass]
1997
- ] : [
1998
- roadClass,
2127
+ return ["match", ["get", fields2.class], classes, true, false];
2128
+ }
2129
+ function tileflowRoadClassValues(roadClass) {
2130
+ if (isTileflowPathRoadClass(roadClass)) return ["path", ...constructionClasses[roadClass]];
2131
+ return [
2132
+ ...roadClass === "minor" ? ["minor", "residential", "unclassified"] : [roadClass],
1999
2133
  ...compatibleRoadClasses[roadClass] ?? [],
2000
2134
  ...constructionClasses[roadClass]
2001
2135
  ];
2002
- return ["match", ["get", fields.class], classes, true, false];
2003
2136
  }
2004
2137
 
2005
2138
  // src/modules/roads/index.ts
@@ -2157,25 +2290,25 @@ function labelNameFields(language, context) {
2157
2290
  return labelFieldExpression(language, context).slice(1).map((entry) => entry[1]);
2158
2291
  }
2159
2292
  function labelFieldExpression(language, context) {
2160
- const fields = context.data.schema.fields;
2293
+ const fields2 = context.data.schema.fields;
2161
2294
  if (language === "local") {
2162
2295
  return [
2163
2296
  "coalesce",
2164
- ["get", fields.name],
2165
- ["get", fields.nameLatin],
2166
- ["get", fields.nameEnglish]
2297
+ ["get", fields2.name],
2298
+ ["get", fields2.nameLatin],
2299
+ ["get", fields2.nameEnglish]
2167
2300
  ];
2168
2301
  }
2169
2302
  if (language === "auto") {
2170
2303
  return [
2171
2304
  "coalesce",
2172
- ["get", fields.nameLatin],
2173
- ["get", fields.name],
2174
- ["get", fields.nameEnglish]
2305
+ ["get", fields2.nameLatin],
2306
+ ["get", fields2.name],
2307
+ ["get", fields2.nameEnglish]
2175
2308
  ];
2176
2309
  }
2177
- const requestedField = language === "en" ? fields.nameEnglish : `name:${language}`;
2178
- return ["coalesce", ["get", requestedField], ["get", fields.nameLatin], ["get", fields.name]];
2310
+ const requestedField = language === "en" ? fields2.nameEnglish : `name:${language}`;
2311
+ return ["coalesce", ["get", requestedField], ["get", fields2.nameLatin], ["get", fields2.name]];
2179
2312
  }
2180
2313
 
2181
2314
  // src/modules/labels/compiler.ts
@@ -2712,8 +2845,8 @@ function classFilter(field2, classes) {
2712
2845
  function aerodromeLabelExpression(detail, language, context) {
2713
2846
  const name = labelFieldExpression(language, context);
2714
2847
  if (detail === "none") return name;
2715
- const fields = context.data.schema.fields;
2716
- const code = detail === "all" ? ["coalesce", ["get", fields.iata], ["get", fields.icao], ""] : ["coalesce", ["get", fields.iata], ""];
2848
+ const fields2 = context.data.schema.fields;
2849
+ const code = detail === "all" ? ["coalesce", ["get", fields2.iata], ["get", fields2.icao], ""] : ["coalesce", ["get", fields2.iata], ""];
2717
2850
  return [
2718
2851
  "case",
2719
2852
  ["!=", ["to-string", code], ""],
@@ -2801,8 +2934,8 @@ function placePriority(rankField, base, capitals = false, capitalField = "capita
2801
2934
  function capitalFilter(capitalField) {
2802
2935
  return [">", ["to-number", ["get", capitalField], 0], 0];
2803
2936
  }
2804
- function roadClassesFilter(fields, roadClasses) {
2805
- return roadClasses.length === 1 ? tileflowRoadClassFilter(fields, roadClasses[0]) : ["any", ...roadClasses.map((roadClass) => tileflowRoadClassFilter(fields, roadClass))];
2937
+ function roadClassesFilter(fields2, roadClasses) {
2938
+ return roadClasses.length === 1 ? tileflowRoadClassFilter(fields2, roadClasses[0]) : ["any", ...roadClasses.map((roadClass) => tileflowRoadClassFilter(fields2, roadClass))];
2806
2939
  }
2807
2940
  function symbolContribution(target, localOrder, layer, slot = "symbols", family) {
2808
2941
  return {
@@ -3251,11 +3384,11 @@ function compileLandforms(request, language, context) {
3251
3384
  ];
3252
3385
  });
3253
3386
  }
3254
- function landformFilter(fields, classes) {
3255
- const classFilter4 = classes.length === 1 ? ["==", ["get", fields.class], classes[0]] : ["match", ["get", fields.class], classes, true, false];
3387
+ function landformFilter(fields2, classes) {
3388
+ const classFilter4 = classes.length === 1 ? ["==", ["get", fields2.class], classes[0]] : ["match", ["get", fields2.class], classes, true, false];
3256
3389
  const classZoom = [
3257
3390
  "match",
3258
- ["get", fields.class],
3391
+ ["get", fields2.class],
3259
3392
  "volcano",
3260
3393
  [">=", ["zoom"], 9],
3261
3394
  "peak",
@@ -3269,10 +3402,10 @@ function landformFilter(fields, classes) {
3269
3402
  return [
3270
3403
  "all",
3271
3404
  ["==", ["geometry-type"], "Point"],
3272
- ["has", fields.name],
3405
+ ["has", fields2.name],
3273
3406
  classFilter4,
3274
3407
  classZoom,
3275
- ["<=", ["to-number", ["get", fields.rank], 99], ["step", ["zoom"], 3, 12, 10, 14, 99]]
3408
+ ["<=", ["to-number", ["get", fields2.rank], 99], ["step", ["zoom"], 3, 12, 10, 14, 99]]
3276
3409
  ];
3277
3410
  }
3278
3411
  function landformContribution(target, localOrder, layer) {
@@ -3287,6 +3420,25 @@ function landformContribution(target, localOrder, layer) {
3287
3420
  }
3288
3421
 
3289
3422
  // src/modules/nautical/index.ts
3423
+ var tileflowNauticalImageRoles = Object.freeze({
3424
+ aids: Object.freeze({ default: "nautical.aid" }),
3425
+ beacons: Object.freeze({ default: "nautical.beacon" }),
3426
+ buoys: Object.freeze({
3427
+ cardinal: "nautical.buoy.cardinal",
3428
+ default: "nautical.buoy",
3429
+ port: "nautical.buoy.port",
3430
+ starboard: "nautical.buoy.starboard"
3431
+ }),
3432
+ harbours: Object.freeze({ default: "nautical.harbour" }),
3433
+ lighthouses: Object.freeze({ default: "nautical.lighthouse" }),
3434
+ lights: Object.freeze({ default: "nautical.light" }),
3435
+ ports: Object.freeze({ default: "nautical.port" }),
3436
+ wrecks: Object.freeze({ default: "nautical.wreck" })
3437
+ });
3438
+ var tileflowNauticalNotice = Object.freeze({
3439
+ id: "nautical-not-for-navigation",
3440
+ text: "Not for navigation"
3441
+ });
3290
3442
  function nautical(options = {}) {
3291
3443
  return { type: "nautical", ...cloneJson11(options) };
3292
3444
  }
@@ -3332,30 +3484,51 @@ var tileflowBathymetryV1Schema = Object.freeze({
3332
3484
  });
3333
3485
  var tileflowNauticalV1Schema = Object.freeze({
3334
3486
  fields: Object.freeze({
3487
+ category: "category",
3335
3488
  cell: "cell",
3336
3489
  character: "character",
3337
3490
  class: "class",
3338
3491
  color: "color",
3492
+ colorPattern: "color_pattern",
3493
+ country: "country",
3339
3494
  coverage: "coverage",
3340
3495
  depth: "depth",
3341
- direction: "direction",
3342
3496
  edition: "edition",
3497
+ families: "families",
3498
+ group: "group",
3499
+ heightM: "height_m",
3500
+ label: "label",
3343
3501
  licence: "licence",
3502
+ lit: "lit",
3503
+ minZoom: "min_zoom",
3344
3504
  name: "name",
3345
- provider: "provider",
3505
+ orientation: "orientation",
3506
+ periodS: "period_s",
3507
+ position: "position",
3346
3508
  provenance: "provenance",
3347
- quality: "quality",
3509
+ provider: "provider",
3348
3510
  rangeNm: "range_nm",
3511
+ rank: "rank",
3512
+ reference: "reference",
3513
+ restriction: "restriction",
3349
3514
  scale: "scale",
3515
+ sectorEnd: "sector_end",
3516
+ sectorStart: "sector_start",
3517
+ shape: "shape",
3518
+ size: "size",
3350
3519
  subclass: "subclass",
3351
- update: "update"
3520
+ system: "system",
3521
+ topmark: "topmark",
3522
+ unlocode: "unlocode",
3523
+ update: "update",
3524
+ waterLevel: "water_level"
3352
3525
  }),
3353
3526
  geometryTypes: Object.freeze({
3354
3527
  aids: Object.freeze(["Point"]),
3355
3528
  coverage: Object.freeze(["Polygon"]),
3356
- hazards: Object.freeze(["Point", "Polygon"]),
3529
+ hazards: Object.freeze(["Point", "LineString", "Polygon"]),
3357
3530
  lights: Object.freeze(["Point"]),
3358
- navigationAreas: Object.freeze(["Polygon"]),
3531
+ navigationAreas: Object.freeze(["Point", "LineString", "Polygon"]),
3359
3532
  reefs: Object.freeze(["Polygon"]),
3360
3533
  soundings: Object.freeze(["Point"]),
3361
3534
  wrecks: Object.freeze(["Point", "Polygon"])
@@ -3526,490 +3699,832 @@ function cloneJson12(value) {
3526
3699
  }
3527
3700
 
3528
3701
  // src/modules/nautical/compiler.ts
3702
+ var { fields, layers } = tileflowNauticalV1Schema;
3703
+ var markColours = Object.freeze({
3704
+ amber: "#F2A10A",
3705
+ black: "#1F2328",
3706
+ blue: "#2D6CDF",
3707
+ brown: "#8A5A2E",
3708
+ green: "#1E9E57",
3709
+ grey: "#8C939A",
3710
+ magenta: "#C2398F",
3711
+ orange: "#EF7D1A",
3712
+ pink: "#EE8AB8",
3713
+ red: "#D3313A",
3714
+ violet: "#8456C9",
3715
+ white: "#FFFFFF",
3716
+ yellow: "#F5C518"
3717
+ });
3718
+ var lightColours = Object.freeze({
3719
+ ...markColours,
3720
+ white: "#F7D046"
3721
+ });
3722
+ var floatingAidClasses = ["buoy", "light-float", "light-vessel"];
3723
+ var fixedAidClasses = ["beacon", "daymark"];
3724
+ var portLateralCategories = ["port", "preferred-channel-port", "waterway-left"];
3725
+ var starboardLateralCategories = [
3726
+ "preferred-channel-starboard",
3727
+ "starboard",
3728
+ "waterway-right"
3729
+ ];
3730
+ var exposedWaterLevels = ["above-mean-water", "awash", "covers", "dry"];
3731
+ var dangerousWreckClasses = ["dangerous", "hull-showing", "mast-showing"];
3732
+ var operationalAreaClasses = [
3733
+ "anchor-berth",
3734
+ "anchorage",
3735
+ "dumping-ground",
3736
+ "military",
3737
+ "restricted",
3738
+ "traffic-separation"
3739
+ ];
3740
+ var routeClasses = ["route", "traffic-separation"];
3741
+ var cableClasses = ["cable", "cable-area", "pipeline", "pipeline-area"];
3742
+ var portSizes = ["large", "medium", "small", "very-small"];
3529
3743
  function compileNautical(request, context) {
3530
3744
  const nauticalSource = context.marine?.nautical;
3531
3745
  if (!nauticalSource || request?.enabled === false) return [];
3532
3746
  const colors = context.colors;
3533
- const { fields, layers } = tileflowNauticalV1Schema;
3747
+ const ink = colors.hydro.label;
3748
+ const halo = colors.hydro.water;
3749
+ const accent = colors.poi.transport;
3534
3750
  const chartText = {
3535
3751
  ...typographyTextStyle(context.typography.water),
3536
- color: colors.hydro.label,
3537
- haloColor: colors.hydro.water,
3752
+ color: ink,
3753
+ haloColor: halo,
3538
3754
  haloWidth: 1.2
3539
3755
  };
3540
- const config = mergeTileflowDesign(
3541
- {
3542
- type: "nautical",
3543
- enabled: true,
3544
- soundings: {
3545
- minZoom: 12,
3546
- placement: "point",
3547
- priority: 92,
3756
+ const markText = {
3757
+ ...typographyTextStyle(context.typography.poi),
3758
+ color: ink,
3759
+ haloColor: halo,
3760
+ haloWidth: 1.2,
3761
+ transform: "none"
3762
+ };
3763
+ const images = themedImages(context);
3764
+ const rankPriority = {
3765
+ priority: expression(["coalesce", ["get", fields.rank], 9]),
3766
+ priorityOrder: "lower-first"
3767
+ };
3768
+ const nameFrom = (minZoom) => expression(["step", ["zoom"], "", minZoom, ["coalesce", ["get", fields.name], ""]]);
3769
+ const sideLabel = { anchor: "left", offset: [0.85, 0], optional: true };
3770
+ const belowLabel = { anchor: "top", offset: [0, 0.75], optional: true };
3771
+ const markColour = expression(colourMatch(firstColour(fields.color), markColours, ink));
3772
+ const markStroke = expression(colourMatch(secondColour(fields.color), markColours, ink));
3773
+ const flareColour = expression(
3774
+ colourMatch(firstColour(fields.color), lightColours, lightColours.white)
3775
+ );
3776
+ const iconPaint = { color: markColour, haloColor: ink, haloWidth: 1 };
3777
+ const defaults4 = {
3778
+ type: "nautical",
3779
+ enabled: true,
3780
+ // Coverage cells are square tiles; a faint seamless tint before marks appear avoids a grid.
3781
+ coverage: {
3782
+ fill: {
3783
+ antialias: false,
3784
+ color: ink,
3785
+ maxZoom: 8,
3786
+ opacity: zoom.linear([
3787
+ [0, 0.06],
3788
+ [5, 0.045],
3789
+ [8, 0]
3790
+ ])
3791
+ }
3792
+ },
3793
+ navigationAreas: {
3794
+ fill: {
3795
+ color: accent,
3796
+ minZoom: 7,
3797
+ opacity: expression([
3798
+ "match",
3799
+ ["get", fields.class],
3800
+ "traffic-separation",
3801
+ 0.08,
3802
+ ["military", "restricted", "dumping-ground"],
3803
+ 0.05,
3804
+ 0
3805
+ ])
3806
+ },
3807
+ outline: {
3808
+ color: expression([
3809
+ "match",
3810
+ ["get", fields.class],
3811
+ [...operationalAreaClasses],
3812
+ accent,
3813
+ ink
3814
+ ]),
3815
+ dash: [4, 2],
3816
+ minZoom: 7,
3817
+ opacity: expression([
3818
+ "interpolate",
3819
+ ["linear"],
3820
+ ["zoom"],
3821
+ 7,
3822
+ ["match", ["get", fields.class], [...operationalAreaClasses], 0.55, 0],
3823
+ 10,
3824
+ ["match", ["get", fields.class], [...operationalAreaClasses], 0.75, 0],
3825
+ 12,
3826
+ ["match", ["get", fields.class], [...operationalAreaClasses], 0.85, 0.4]
3827
+ ]),
3828
+ width: zoom.linear([
3829
+ [7, 0.6],
3830
+ [14, 1.2]
3831
+ ])
3832
+ }
3833
+ },
3834
+ reefs: {
3835
+ fill: { color: colors.landcover.sand, minZoom: 6, opacity: 0.32 },
3836
+ outline: { color: ink, dash: [1.5, 1.5], minZoom: 6, opacity: 0.6, width: 0.8 }
3837
+ },
3838
+ cables: {
3839
+ color: accent,
3840
+ dash: [2, 2],
3841
+ minZoom: 8,
3842
+ opacity: zoom.linear([
3843
+ [8, 0.3],
3844
+ [12, 0.6]
3845
+ ]),
3846
+ width: zoom.linear([
3847
+ [8, 0.6],
3848
+ [14, 1]
3849
+ ])
3850
+ },
3851
+ routes: {
3852
+ color: expression([
3853
+ "match",
3854
+ ["get", fields.class],
3855
+ "traffic-separation",
3856
+ accent,
3857
+ ink
3858
+ ]),
3859
+ dash: [5, 3],
3860
+ minZoom: 8,
3861
+ opacity: 0.72,
3862
+ width: zoom.linear([
3863
+ [8, 0.6],
3864
+ [14, 1.3]
3865
+ ])
3866
+ },
3867
+ hazardAreas: {
3868
+ fill: { color: accent, minZoom: 10, opacity: 0.08 },
3869
+ outline: { color: accent, dash: [1, 1], minZoom: 10, opacity: 0.7, width: 0.8 }
3870
+ },
3871
+ wreckAreas: {
3872
+ fill: { color: ink, minZoom: 11, opacity: 0.08 },
3873
+ outline: { color: ink, dash: [2, 1], minZoom: 11, opacity: 0.6, width: 0.75 }
3874
+ },
3875
+ soundings: {
3876
+ minZoom: 12,
3877
+ placement: "point",
3878
+ text: {
3879
+ ...chartText,
3880
+ allowOverlap: false,
3881
+ field: expression(["to-string", ["get", fields.depth]]),
3882
+ optional: false,
3883
+ padding: 5,
3884
+ size: zoom.linear([
3885
+ [12, 9],
3886
+ [16, 11]
3887
+ ]),
3888
+ transform: "none"
3889
+ }
3890
+ },
3891
+ hazards: {
3892
+ minZoom: 8,
3893
+ placement: "point",
3894
+ marker: {
3895
+ color: expression([
3896
+ "match",
3897
+ ["get", fields.class],
3898
+ "offshore-platform",
3899
+ ink,
3900
+ "rgba(0, 0, 0, 0)"
3901
+ ]),
3902
+ radius: zoom.linear([
3903
+ [8, 2.2],
3904
+ [16, 3.6]
3905
+ ]),
3906
+ strokeColor: ink,
3907
+ strokeWidth: 1.1
3908
+ },
3909
+ text: {
3910
+ ...markText,
3911
+ field: expression([
3912
+ "match",
3913
+ ["get", fields.waterLevel],
3914
+ [...exposedWaterLevels],
3915
+ "*",
3916
+ "+"
3917
+ ]),
3918
+ haloWidth: 1,
3919
+ offset: expression([
3920
+ "match",
3921
+ ["get", fields.waterLevel],
3922
+ [...exposedWaterLevels],
3923
+ ["literal", [0, 0.25]],
3924
+ ["literal", [0, 0]]
3925
+ ]),
3926
+ optional: false,
3927
+ padding: 1,
3928
+ size: zoom.linear([
3929
+ [12, 11],
3930
+ [16, 14]
3931
+ ])
3932
+ }
3933
+ },
3934
+ wrecks: {
3935
+ minZoom: 11,
3936
+ placement: "point",
3937
+ ...images.wrecks ? {
3938
+ icon: {
3939
+ color: ink,
3940
+ haloColor: halo,
3941
+ haloWidth: 1,
3942
+ image: images.wrecks,
3943
+ opacity: expression([
3944
+ "match",
3945
+ ["get", fields.class],
3946
+ [...dangerousWreckClasses],
3947
+ 1,
3948
+ 0.65
3949
+ ]),
3950
+ size: zoom.linear([
3951
+ [11, 1],
3952
+ [16, 1.4]
3953
+ ])
3954
+ },
3955
+ text: { ...markText, ...sideLabel, field: nameFrom(14), padding: 4, size: 9 }
3956
+ } : {
3548
3957
  text: {
3549
- ...chartText,
3550
- allowOverlap: false,
3551
- field: expression(["to-string", ["get", fields.depth]]),
3958
+ ...markText,
3959
+ color: expression([
3960
+ "match",
3961
+ ["get", fields.class],
3962
+ [...dangerousWreckClasses],
3963
+ accent,
3964
+ ink
3965
+ ]),
3966
+ field: "Wk",
3552
3967
  optional: false,
3553
- padding: 5,
3968
+ padding: 3,
3969
+ size: 10
3970
+ }
3971
+ }
3972
+ },
3973
+ harbours: {
3974
+ minZoom: 11,
3975
+ placement: "point",
3976
+ ...images.harbours ? {
3977
+ icon: {
3978
+ color: ink,
3979
+ haloColor: halo,
3980
+ haloWidth: 1,
3981
+ image: images.harbours,
3554
3982
  size: zoom.linear([
3555
- [12, 9],
3556
- [16, 11]
3983
+ [11, 0.9],
3984
+ [16, 1.3]
3557
3985
  ])
3558
3986
  }
3987
+ } : {
3988
+ marker: {
3989
+ color: ink,
3990
+ radius: zoom.linear([
3991
+ [11, 2.2],
3992
+ [16, 3.6]
3993
+ ]),
3994
+ strokeColor: halo,
3995
+ strokeWidth: 1
3996
+ }
3559
3997
  },
3560
- aids: {
3561
- minZoom: 10,
3562
- placement: "point",
3563
- priority: 98,
3998
+ text: { ...markText, ...belowLabel, field: nameFrom(12), padding: 4, size: 10 }
3999
+ },
4000
+ aids: {
4001
+ minZoom: 11,
4002
+ placement: "point",
4003
+ ...rankPriority,
4004
+ ...images.aids ? {
4005
+ icon: {
4006
+ ...iconPaint,
4007
+ image: images.aids,
4008
+ size: zoom.linear([
4009
+ [11, 0.8],
4010
+ [16, 1.2]
4011
+ ])
4012
+ }
4013
+ } : {
3564
4014
  marker: {
3565
- color: colors.hydro.water,
4015
+ color: halo,
3566
4016
  radius: zoom.linear([
3567
- [10, 2.6],
3568
- [16, 4.2]
4017
+ [11, 1.8],
4018
+ [16, 3]
3569
4019
  ]),
3570
- strokeColor: colors.hydro.label,
3571
- strokeWidth: 1.2
3572
- },
3573
- text: {
3574
- ...chartText,
3575
- field: expression([
3576
- "coalesce",
3577
- ["get", fields.name],
3578
- ["get", fields.subclass],
3579
- ["get", fields.class]
4020
+ strokeColor: ink,
4021
+ strokeWidth: 1
4022
+ }
4023
+ },
4024
+ text: { ...markText, ...sideLabel, field: nameFrom(15), padding: 3, size: 9 }
4025
+ },
4026
+ beacons: {
4027
+ minZoom: 11,
4028
+ placement: "point",
4029
+ ...rankPriority,
4030
+ ...images.beacons ? {
4031
+ icon: {
4032
+ ...iconPaint,
4033
+ image: images.beacons,
4034
+ size: zoom.linear([
4035
+ [11, 0.8],
4036
+ [16, 1.2]
4037
+ ])
4038
+ }
4039
+ } : {
4040
+ marker: {
4041
+ color: markColour,
4042
+ radius: zoom.linear([
4043
+ [11, 1.8],
4044
+ [16, 3.4]
3580
4045
  ]),
3581
- offset: [0, 1.1],
3582
- optional: true,
3583
- padding: 4,
3584
- size: 10
4046
+ strokeColor: markStroke,
4047
+ strokeWidth: 1
3585
4048
  }
3586
4049
  },
3587
- lighthouses: {
3588
- minZoom: 10,
3589
- placement: "point",
3590
- priority: 99,
4050
+ text: { ...markText, ...sideLabel, field: nameFrom(14), padding: 3, size: 9 }
4051
+ },
4052
+ lights: {
4053
+ minZoom: 6,
4054
+ placement: "point",
4055
+ ...rankPriority,
4056
+ ...images.lights ? {
4057
+ icon: {
4058
+ allowOverlap: true,
4059
+ color: flareColour,
4060
+ haloColor: halo,
4061
+ haloWidth: 0.5,
4062
+ ignorePlacement: true,
4063
+ image: images.lights,
4064
+ opacity: 0.9,
4065
+ size: byClassAndZoom([
4066
+ [6, 0.9, 0.65],
4067
+ [14, 1.6, 1.15]
4068
+ ])
4069
+ }
4070
+ } : {
3591
4071
  marker: {
3592
- color: colors.hydro.water,
4072
+ color: flareColour,
4073
+ opacity: 0.55,
4074
+ radius: byClassAndZoom([
4075
+ [6, 3.2, 1.8],
4076
+ [14, 7, 4.5]
4077
+ ])
4078
+ }
4079
+ },
4080
+ text: {
4081
+ ...markText,
4082
+ ...sideLabel,
4083
+ field: expression([
4084
+ "step",
4085
+ ["zoom"],
4086
+ "",
4087
+ 10,
4088
+ [
4089
+ "case",
4090
+ ["==", ["get", fields.class], "major"],
4091
+ ["coalesce", ["get", fields.label], ""],
4092
+ ""
4093
+ ],
4094
+ 13,
4095
+ ["coalesce", ["get", fields.label], ""]
4096
+ ]),
4097
+ padding: 3,
4098
+ size: zoom.linear([
4099
+ [10, 9],
4100
+ [16, 10.5]
4101
+ ])
4102
+ }
4103
+ },
4104
+ buoys: {
4105
+ minZoom: 8,
4106
+ placement: "point",
4107
+ ...rankPriority,
4108
+ ...images.buoys ? {
4109
+ icon: {
4110
+ ...iconPaint,
4111
+ // Dense harbours thin out by rank at low zooms instead of piling up.
4112
+ image: images.buoys,
4113
+ padding: 1,
4114
+ size: zoom.linear([
4115
+ [11, 1],
4116
+ [16, 1.5]
4117
+ ])
4118
+ }
4119
+ } : {
4120
+ marker: {
4121
+ color: markColour,
3593
4122
  radius: zoom.linear([
3594
- [10, 3],
4123
+ [8, 2.4],
3595
4124
  [16, 4.6]
3596
4125
  ]),
3597
- strokeColor: colors.hydro.label,
4126
+ strokeColor: markStroke,
3598
4127
  strokeWidth: 1.4
3599
- },
3600
- text: {
3601
- ...chartText,
3602
- field: expression([
3603
- "case",
3604
- ["has", fields.name],
3605
- ["to-string", ["get", fields.name]],
3606
- "Lighthouse"
3607
- ]),
3608
- offset: [0, 1.2],
3609
- optional: true,
3610
- padding: 4,
3611
- size: 10
3612
4128
  }
3613
4129
  },
3614
- lights: {
3615
- minZoom: 11,
3616
- placement: "point",
3617
- priority: 96,
4130
+ text: { ...markText, ...sideLabel, field: nameFrom(14), padding: 3, size: 9 }
4131
+ },
4132
+ lighthouses: {
4133
+ minZoom: 8,
4134
+ placement: "point",
4135
+ ...rankPriority,
4136
+ ...images.lighthouses ? {
4137
+ icon: {
4138
+ allowOverlap: true,
4139
+ color: ink,
4140
+ haloColor: halo,
4141
+ haloWidth: 1,
4142
+ image: images.lighthouses,
4143
+ size: zoom.linear([
4144
+ [8, 1],
4145
+ [16, 1.6]
4146
+ ])
4147
+ }
4148
+ } : {
3618
4149
  marker: {
3619
- color: colors.poi.transport,
3620
- opacity: 0.24,
4150
+ color: ink,
3621
4151
  radius: zoom.linear([
3622
- [11, 4],
3623
- [16, 7]
4152
+ [8, 2.6],
4153
+ [16, 4.8]
3624
4154
  ]),
3625
- strokeColor: colors.poi.transport,
3626
- strokeWidth: 1
3627
- },
3628
- text: {
3629
- ...chartText,
3630
- color: colors.poi.transport,
3631
- field: lightLabel(fields),
3632
- offset: [0, 1.25],
3633
- optional: true,
3634
- padding: 5,
3635
- size: 9
4155
+ strokeColor: halo,
4156
+ strokeWidth: 1.2
4157
+ }
4158
+ },
4159
+ text: {
4160
+ ...markText,
4161
+ ...belowLabel,
4162
+ field: nameFrom(10),
4163
+ padding: 4,
4164
+ size: zoom.linear([
4165
+ [10, 9.5],
4166
+ [16, 11.5]
4167
+ ])
4168
+ }
4169
+ },
4170
+ ports: {
4171
+ minZoom: 3,
4172
+ placement: "point",
4173
+ ...rankPriority,
4174
+ ...images.ports ? {
4175
+ icon: {
4176
+ color: ink,
4177
+ haloColor: halo,
4178
+ haloWidth: 1,
4179
+ image: images.ports,
4180
+ size: byPortSizeAndZoom([
4181
+ [3, [1.05, 0.95, 0.85, 0.75]],
4182
+ [12, [1.45, 1.35, 1.25, 1.15]]
4183
+ ])
4184
+ }
4185
+ } : {
4186
+ marker: {
4187
+ color: ink,
4188
+ radius: byPortSizeAndZoom([
4189
+ [3, [3.2, 2.8, 2.4, 2]],
4190
+ [12, [5, 4.5, 4, 3.5]]
4191
+ ]),
4192
+ strokeColor: halo,
4193
+ strokeWidth: 1.2
3636
4194
  }
3637
4195
  },
4196
+ text: {
4197
+ ...markText,
4198
+ ...belowLabel,
4199
+ field: expression(["coalesce", ["get", fields.name], ""]),
4200
+ padding: 6,
4201
+ size: byPortSizeAndZoom([
4202
+ [3, [11, 10, 9.5, 9]],
4203
+ [12, [12.5, 11.5, 11, 10.5]]
4204
+ ])
4205
+ }
4206
+ },
4207
+ labels: {
4208
+ coverage: {
4209
+ maxZoom: 9,
4210
+ minZoom: 4,
4211
+ placement: "point",
4212
+ visible: false,
4213
+ text: { ...chartText, field: expression(["get", fields.provider]), size: 8 }
4214
+ },
3638
4215
  hazards: {
3639
4216
  minZoom: 11,
3640
4217
  placement: "point",
3641
- priority: 95,
3642
- text: {
3643
- ...chartText,
3644
- color: colors.poi.transport,
3645
- field: nameAndDepthWithPrefix(fields.name, fields.depth, "X"),
3646
- optional: false,
3647
- padding: 5,
3648
- size: 12
3649
- }
4218
+ text: { ...markText, color: accent, field: expression(["get", fields.name]) }
3650
4219
  },
3651
- wrecks: {
4220
+ navigationAreas: {
3652
4221
  minZoom: 11,
3653
4222
  placement: "point",
3654
- priority: 94,
3655
4223
  text: {
3656
- ...chartText,
3657
- field: nameAndDepthWithPrefix(fields.name, fields.depth, "Wrk"),
3658
- optional: false,
3659
- padding: 5,
3660
- size: 9
4224
+ ...markText,
4225
+ color: expression([
4226
+ "match",
4227
+ ["get", fields.class],
4228
+ [...operationalAreaClasses],
4229
+ accent,
4230
+ ink
4231
+ ]),
4232
+ field: expression(["get", fields.name]),
4233
+ optional: true,
4234
+ padding: 8,
4235
+ size: 9.5
3661
4236
  }
3662
4237
  },
3663
4238
  reefs: {
3664
- fill: { color: colors.landcover.sand, minZoom: 7, opacity: 0.34 },
3665
- outline: {
3666
- color: colors.hydro.label,
3667
- dash: [1.5, 1.5],
3668
- minZoom: 7,
3669
- opacity: 0.62,
3670
- width: 0.8
3671
- }
3672
- },
3673
- hazardAreas: {
3674
- fill: { color: colors.poi.transport, minZoom: 11, opacity: 0.1 },
3675
- outline: {
3676
- color: colors.poi.transport,
3677
- dash: [1, 1],
3678
- minZoom: 11,
3679
- opacity: 0.72,
3680
- width: 0.8
3681
- }
3682
- },
3683
- wreckAreas: {
3684
- fill: { color: colors.hydro.label, minZoom: 11, opacity: 0.08 },
3685
- outline: {
3686
- color: colors.hydro.label,
3687
- dash: [2, 1],
3688
- minZoom: 11,
3689
- opacity: 0.62,
3690
- width: 0.75
3691
- }
3692
- },
3693
- navigationAreas: {
3694
- fill: { color: colors.poi.transport, minZoom: 8, opacity: 0.06 },
3695
- outline: {
3696
- color: colors.poi.transport,
3697
- dash: [4, 2],
3698
- minZoom: 8,
3699
- opacity: 0.62,
3700
- width: 0.8
3701
- }
4239
+ minZoom: 11,
4240
+ placement: "point",
4241
+ text: { ...chartText, field: expression(["get", fields.name]), size: 9 }
3702
4242
  },
3703
- coverage: {
3704
- outline: {
3705
- color: colors.poi.transport,
3706
- dash: [2, 3],
3707
- maxZoom: 9,
3708
- minZoom: 4,
3709
- opacity: 0.34,
3710
- width: 0.6
3711
- }
3712
- },
3713
- labels: {
3714
- coverage: {
3715
- maxZoom: 9,
3716
- minZoom: 4,
3717
- placement: "point",
3718
- priority: 70,
3719
- text: {
3720
- ...chartText,
3721
- color: colors.poi.transport,
3722
- field: expression(["coalesce", ["get", fields.name], ["get", fields.provider]]),
3723
- optional: true,
3724
- padding: 12,
3725
- size: 8
3726
- }
3727
- },
3728
- hazards: {
3729
- minZoom: 11,
3730
- placement: "point",
3731
- priority: 93,
3732
- text: {
3733
- ...chartText,
3734
- color: colors.poi.transport,
3735
- field: nameAndDepthWithPrefix(fields.name, fields.depth, "X"),
3736
- optional: true,
3737
- padding: 6,
3738
- size: 9
3739
- }
3740
- },
3741
- navigationAreas: {
3742
- minZoom: 10,
3743
- placement: "point",
3744
- priority: 78,
3745
- text: {
3746
- ...chartText,
3747
- color: colors.poi.transport,
3748
- field: expression(["coalesce", ["get", fields.name], ["get", fields.class]]),
3749
- optional: true,
3750
- padding: 8,
3751
- size: 9
3752
- }
3753
- },
3754
- reefs: {
3755
- minZoom: 12,
3756
- placement: "point",
3757
- priority: 89,
3758
- text: {
3759
- ...chartText,
3760
- field: nameAndDepthWithPrefix(fields.name, fields.depth, "Reef"),
3761
- optional: true,
3762
- padding: 7,
3763
- size: 9
3764
- }
3765
- },
3766
- wrecks: {
3767
- minZoom: 11,
3768
- placement: "point",
3769
- priority: 92,
3770
- text: {
3771
- ...chartText,
3772
- field: nameAndDepthWithPrefix(fields.name, fields.depth, "Wrk"),
3773
- optional: true,
3774
- padding: 6,
3775
- size: 9
3776
- }
3777
- }
4243
+ wrecks: {
4244
+ minZoom: 12,
4245
+ placement: "point",
4246
+ text: { ...markText, field: expression(["get", fields.name]), size: 9 }
3778
4247
  }
3779
- },
4248
+ }
4249
+ };
4250
+ const config = mergeTileflowDesign(
4251
+ defaults4,
3780
4252
  request
3781
4253
  );
3782
4254
  const source = nauticalSource.sourceId;
3783
4255
  const contributions = [];
3784
- appendAreaFeature(contributions, {
4256
+ const add2 = (options) => appendNauticalLayer(contributions, { ...options, source });
4257
+ add2({
4258
+ // Only coverage that speaks for every family; lists of lights alone would double the tint.
4259
+ filter: ["all", polygons, ["in", layers.aids, ["coalesce", ["get", fields.families], ""]]],
3785
4260
  id: "tileflow-nautical-coverage",
3786
4261
  localOrder: 380,
3787
- source,
3788
4262
  sourceLayer: layers.coverage,
3789
4263
  style: config.coverage,
3790
4264
  target: "nautical.coverage"
3791
4265
  });
3792
- appendAreaFeature(contributions, {
4266
+ add2({
4267
+ filter: polygons,
3793
4268
  id: "tileflow-nautical-navigation-areas",
3794
4269
  localOrder: 390,
3795
- source,
3796
4270
  sourceLayer: layers.navigationAreas,
3797
4271
  style: config.navigationAreas,
3798
4272
  target: "nautical.navigationAreas"
3799
4273
  });
3800
- appendAreaFeature(contributions, {
4274
+ add2({
4275
+ filter: polygons,
3801
4276
  id: "tileflow-nautical-reefs",
3802
4277
  localOrder: 400,
3803
- source,
3804
4278
  sourceLayer: layers.reefs,
3805
4279
  style: config.reefs,
3806
4280
  target: "nautical.reefs"
3807
4281
  });
3808
- appendAreaFeature(contributions, {
4282
+ add2({
4283
+ filter: linesOf(cableClasses),
4284
+ id: "tileflow-nautical-cables",
4285
+ line: true,
4286
+ localOrder: 404,
4287
+ sourceLayer: layers.navigationAreas,
4288
+ style: config.cables,
4289
+ target: "nautical.cables"
4290
+ });
4291
+ add2({
4292
+ filter: linesOf(routeClasses),
4293
+ id: "tileflow-nautical-routes",
4294
+ line: true,
4295
+ localOrder: 406,
4296
+ sourceLayer: layers.navigationAreas,
4297
+ style: config.routes,
4298
+ target: "nautical.routes"
4299
+ });
4300
+ add2({
4301
+ filter: polygons,
3809
4302
  id: "tileflow-nautical-hazard-areas",
3810
4303
  localOrder: 410,
3811
- source,
4304
+ outlineFilter: ["match", ["geometry-type"], ["LineString", "Polygon"], true, false],
3812
4305
  sourceLayer: layers.hazards,
3813
4306
  style: config.hazardAreas,
3814
4307
  target: "nautical.hazardAreas"
3815
4308
  });
3816
- appendAreaFeature(contributions, {
4309
+ add2({
4310
+ filter: polygons,
3817
4311
  id: "tileflow-nautical-wreck-areas",
3818
4312
  localOrder: 420,
3819
- source,
3820
4313
  sourceLayer: layers.wrecks,
3821
4314
  style: config.wreckAreas,
3822
4315
  target: "nautical.wreckAreas"
3823
4316
  });
3824
- appendPolygonLabel(contributions, {
3825
- featureFilter: ["any", ["has", fields.name], ["has", fields.provider]],
3826
- id: "tileflow-nautical-coverage-labels",
3827
- localOrder: 1060,
3828
- source,
3829
- sourceLayer: layers.coverage,
3830
- style: config.labels?.coverage,
3831
- target: "nautical.labels.coverage"
3832
- });
3833
- appendPolygonLabel(contributions, {
3834
- featureFilter: ["any", ["has", fields.name], ["has", fields.class]],
3835
- id: "tileflow-nautical-navigation-area-labels",
3836
- localOrder: 1070,
3837
- source,
3838
- sourceLayer: layers.navigationAreas,
3839
- style: config.labels?.navigationAreas,
3840
- target: "nautical.labels.navigationAreas"
3841
- });
3842
- appendPolygonLabel(contributions, {
3843
- id: "tileflow-nautical-reef-labels",
3844
- localOrder: 1080,
3845
- source,
3846
- sourceLayer: layers.reefs,
3847
- style: config.labels?.reefs,
3848
- target: "nautical.labels.reefs"
4317
+ for (const [key, id2, sourceLayer, localOrder] of [
4318
+ ["coverage", "tileflow-nautical-coverage-labels", layers.coverage, 560],
4319
+ ["navigationAreas", "tileflow-nautical-navigation-area-labels", layers.navigationAreas, 562],
4320
+ ["reefs", "tileflow-nautical-reef-labels", layers.reefs, 564],
4321
+ ["hazards", "tileflow-nautical-hazard-area-labels", layers.hazards, 566],
4322
+ ["wrecks", "tileflow-nautical-wreck-area-labels", layers.wrecks, 568]
4323
+ ]) {
4324
+ add2({
4325
+ filter: ["all", polygons, ["has", key === "coverage" ? fields.provider : fields.name]],
4326
+ id: id2,
4327
+ localOrder,
4328
+ sourceLayer,
4329
+ style: config.labels?.[key],
4330
+ target: `nautical.labels.${key}`
4331
+ });
4332
+ }
4333
+ add2({
4334
+ filter: ["all", points, ["has", fields.depth]],
4335
+ id: "tileflow-nautical-soundings",
4336
+ localOrder: 570,
4337
+ sourceLayer: layers.soundings,
4338
+ style: config.soundings,
4339
+ target: "nautical.soundings"
3849
4340
  });
3850
- appendPolygonLabel(contributions, {
3851
- id: "tileflow-nautical-hazard-area-labels",
3852
- localOrder: 1090,
3853
- source,
4341
+ add2({
4342
+ filter: points,
4343
+ id: "tileflow-nautical-hazards",
4344
+ localOrder: 574,
4345
+ markerFilter: ["!=", ["get", fields.class], "rock"],
3854
4346
  sourceLayer: layers.hazards,
3855
- style: config.labels?.hazards,
3856
- target: "nautical.labels.hazards"
4347
+ style: config.hazards,
4348
+ symbolFilter: ["==", ["get", fields.class], "rock"],
4349
+ target: "nautical.hazards"
3857
4350
  });
3858
- appendPolygonLabel(contributions, {
3859
- id: "tileflow-nautical-wreck-area-labels",
3860
- localOrder: 1095,
3861
- source,
4351
+ add2({
4352
+ filter: points,
4353
+ id: "tileflow-nautical-wrecks",
4354
+ localOrder: 578,
3862
4355
  sourceLayer: layers.wrecks,
3863
- style: config.labels?.wrecks,
3864
- target: "nautical.labels.wrecks"
4356
+ style: config.wrecks,
4357
+ target: "nautical.wrecks"
3865
4358
  });
3866
- appendPointFeature(contributions, {
3867
- id: "tileflow-nautical-soundings",
3868
- localOrder: 1100,
3869
- source,
3870
- sourceLayer: layers.soundings,
3871
- style: config.soundings,
3872
- target: "nautical.soundings",
3873
- filter: ["all", ["==", ["geometry-type"], "Point"], ["has", fields.depth]]
4359
+ add2({
4360
+ filter: ["all", points, ["==", ["get", fields.class], "harbour"]],
4361
+ id: "tileflow-nautical-harbours",
4362
+ localOrder: 582,
4363
+ sourceLayer: layers.navigationAreas,
4364
+ style: config.harbours,
4365
+ target: "nautical.harbours"
3874
4366
  });
3875
- appendPointFeature(contributions, {
4367
+ add2({
4368
+ filter: [
4369
+ "all",
4370
+ points,
4371
+ [
4372
+ "match",
4373
+ ["get", fields.class],
4374
+ [...floatingAidClasses, ...fixedAidClasses, "lighthouse"],
4375
+ false,
4376
+ true
4377
+ ]
4378
+ ],
3876
4379
  id: "tileflow-nautical-aids",
3877
- localOrder: 1110,
3878
- source,
4380
+ localOrder: 586,
3879
4381
  sourceLayer: layers.aids,
3880
4382
  style: config.aids,
3881
- target: "nautical.aids",
3882
- filter: [
3883
- "all",
3884
- ["==", ["geometry-type"], "Point"],
3885
- ["!=", ["get", fields.class], "lighthouse"]
3886
- ]
4383
+ target: "nautical.aids"
3887
4384
  });
3888
- appendPointFeature(contributions, {
3889
- id: "tileflow-nautical-lighthouses",
3890
- localOrder: 1115,
3891
- source,
4385
+ add2({
4386
+ filter: ["all", points, ["match", ["get", fields.class], [...fixedAidClasses], true, false]],
4387
+ id: "tileflow-nautical-beacons",
4388
+ localOrder: 590,
3892
4389
  sourceLayer: layers.aids,
3893
- style: config.lighthouses,
3894
- target: "nautical.lighthouses",
3895
- filter: [
3896
- "all",
3897
- ["==", ["geometry-type"], "Point"],
3898
- ["==", ["get", fields.class], "lighthouse"]
3899
- ]
4390
+ style: config.beacons,
4391
+ target: "nautical.beacons"
3900
4392
  });
3901
- appendPointFeature(contributions, {
4393
+ add2({
4394
+ filter: points,
3902
4395
  id: "tileflow-nautical-lights",
3903
- localOrder: 1120,
3904
- source,
4396
+ localOrder: 594,
3905
4397
  sourceLayer: layers.lights,
3906
4398
  style: config.lights,
3907
- target: "nautical.lights",
3908
- filter: ["==", ["geometry-type"], "Point"]
4399
+ target: "nautical.lights"
3909
4400
  });
3910
- appendPointFeature(contributions, {
3911
- id: "tileflow-nautical-hazards",
3912
- localOrder: 1130,
3913
- source,
3914
- sourceLayer: layers.hazards,
3915
- style: config.hazards,
3916
- target: "nautical.hazards",
3917
- filter: ["==", ["geometry-type"], "Point"]
4401
+ add2({
4402
+ filter: ["all", points, ["match", ["get", fields.class], [...floatingAidClasses], true, false]],
4403
+ id: "tileflow-nautical-buoys",
4404
+ localOrder: 598,
4405
+ sourceLayer: layers.aids,
4406
+ style: config.buoys,
4407
+ target: "nautical.buoys"
3918
4408
  });
3919
- appendPointFeature(contributions, {
3920
- id: "tileflow-nautical-wrecks",
3921
- localOrder: 1140,
3922
- source,
3923
- sourceLayer: layers.wrecks,
3924
- style: config.wrecks,
3925
- target: "nautical.wrecks",
3926
- filter: ["==", ["geometry-type"], "Point"]
4409
+ add2({
4410
+ filter: ["all", points, ["==", ["get", fields.class], "lighthouse"]],
4411
+ id: "tileflow-nautical-lighthouses",
4412
+ localOrder: 602,
4413
+ sourceLayer: layers.aids,
4414
+ style: config.lighthouses,
4415
+ target: "nautical.lighthouses"
4416
+ });
4417
+ add2({
4418
+ filter: ["all", points, ["==", ["get", fields.class], "port"]],
4419
+ id: "tileflow-nautical-ports",
4420
+ localOrder: 606,
4421
+ sourceLayer: layers.navigationAreas,
4422
+ style: config.ports,
4423
+ target: "nautical.ports"
3927
4424
  });
3928
4425
  return contributions;
3929
4426
  }
3930
- function appendPolygonLabel(contributions, options) {
3931
- const style = options.style;
3932
- if (!style || style.visible === false || (!style.text || style.text.visible === false) && (!style.icon || style.icon.visible === false)) {
3933
- return;
3934
- }
3935
- contributions.push(
3936
- nauticalContribution(
3937
- options.target,
3938
- options.localOrder,
3939
- applySymbolStyle(
3940
- {
3941
- id: options.id,
3942
- type: "symbol",
3943
- source: options.source,
3944
- "source-layer": options.sourceLayer,
3945
- filter: options.featureFilter ? ["all", ["==", ["geometry-type"], "Polygon"], options.featureFilter] : ["==", ["geometry-type"], "Polygon"]
3946
- },
3947
- style
3948
- ),
3949
- "symbols"
3950
- )
3951
- );
4427
+ var points = ["==", ["geometry-type"], "Point"];
4428
+ var polygons = ["==", ["geometry-type"], "Polygon"];
4429
+ function linesOf(classes) {
4430
+ return [
4431
+ "all",
4432
+ ["==", ["geometry-type"], "LineString"],
4433
+ ["match", ["get", fields.class], [...classes], true, false]
4434
+ ];
3952
4435
  }
3953
- function appendAreaFeature(contributions, options) {
3954
- if (!options.style) return;
3955
- for (const area of createAreaLayers(
3956
- {
3957
- id: options.id,
3958
- type: "fill",
3959
- source: options.source,
3960
- "source-layer": options.sourceLayer,
3961
- filter: ["==", ["geometry-type"], "Polygon"]
3962
- },
3963
- options.style
3964
- )) {
4436
+ var fromFeatureMinZoom = [">=", ["zoom"], ["to-number", ["get", fields.minZoom], 0]];
4437
+ function appendNauticalLayer(contributions, options) {
4438
+ const style = options.style;
4439
+ if (!style) return;
4440
+ const base = (id2, type, filter) => ({
4441
+ id: id2,
4442
+ type,
4443
+ source: options.source,
4444
+ "source-layer": options.sourceLayer,
4445
+ filter: ["all", filter, fromFeatureMinZoom]
4446
+ });
4447
+ if (options.line) {
4448
+ const line = style;
4449
+ if (line.visible === false) return;
3965
4450
  contributions.push(
3966
4451
  nauticalContribution(
3967
- `${options.target}.${area.phase}`,
3968
- options.localOrder + (area.phase === "fill" ? 0 : 1),
3969
- area.layer,
4452
+ options.target,
4453
+ options.localOrder,
4454
+ applyLineStyle(base(options.id, "line", options.filter), line),
3970
4455
  "hydro"
3971
4456
  )
3972
4457
  );
4458
+ return;
3973
4459
  }
3974
- }
3975
- function appendPointFeature(contributions, options) {
3976
- const style = options.style;
3977
- if (!style || style.visible === false) return;
3978
- const symbolStyle2 = withoutMarker(style);
3979
- const base = {
3980
- id: options.id,
3981
- type: "symbol",
3982
- source: options.source,
3983
- "source-layer": options.sourceLayer,
3984
- filter: options.filter
3985
- };
3986
- if (style.marker && style.marker.visible !== false) {
4460
+ if ("fill" in style || "outline" in style) {
4461
+ const area = style;
4462
+ if (area.fill && area.fill.visible !== false) {
4463
+ contributions.push(
4464
+ nauticalContribution(
4465
+ `${options.target}.fill`,
4466
+ options.localOrder,
4467
+ applyFillStyle(base(options.id, "fill", options.filter), area.fill),
4468
+ "hydro"
4469
+ )
4470
+ );
4471
+ }
4472
+ if (area.outline && area.outline.visible !== false) {
4473
+ contributions.push(
4474
+ nauticalContribution(
4475
+ `${options.target}.outline`,
4476
+ options.localOrder + 1,
4477
+ applyLineStyle(
4478
+ base(`${options.id}-outline`, "line", options.outlineFilter ?? options.filter),
4479
+ area.outline
4480
+ ),
4481
+ "hydro"
4482
+ )
4483
+ );
4484
+ }
4485
+ return;
4486
+ }
4487
+ const point = style;
4488
+ if (point.visible === false) return;
4489
+ const isLabel = options.target.startsWith("nautical.labels.");
4490
+ if (!isLabel && point.marker && point.marker.visible !== false) {
3987
4491
  contributions.push(
3988
4492
  nauticalContribution(
3989
4493
  `${options.target}.marker`,
3990
4494
  options.localOrder,
3991
4495
  applyCircleStyle(
3992
- { ...base, id: `${options.id}-marker`, type: "circle" },
4496
+ base(
4497
+ `${options.id}-marker`,
4498
+ "circle",
4499
+ options.markerFilter ? ["all", options.filter, options.markerFilter] : options.filter
4500
+ ),
3993
4501
  mergeTileflowDesign(
3994
4502
  {
3995
- ...style.maxZoom === void 0 ? {} : { maxZoom: style.maxZoom },
3996
- ...style.minZoom === void 0 ? {} : { minZoom: style.minZoom }
4503
+ ...point.maxZoom === void 0 ? {} : { maxZoom: point.maxZoom },
4504
+ ...point.minZoom === void 0 ? {} : { minZoom: point.minZoom }
3997
4505
  },
3998
- style.marker
4506
+ point.marker
3999
4507
  )
4000
4508
  ),
4001
4509
  "symbols"
4002
4510
  )
4003
4511
  );
4004
4512
  }
4005
- const hasText = Boolean(style.text && style.text.visible !== false);
4006
- const hasIcon = Boolean(style.icon && style.icon.visible !== false);
4513
+ const hasText = Boolean(point.text && point.text.visible !== false);
4514
+ const hasIcon = Boolean(point.icon && point.icon.visible !== false);
4007
4515
  if (!hasText && !hasIcon) return;
4008
4516
  contributions.push(
4009
4517
  nauticalContribution(
4010
- `${options.target}.symbol`,
4011
- options.localOrder + 1,
4012
- applySymbolStyle(base, symbolStyle2),
4518
+ isLabel ? options.target : `${options.target}.symbol`,
4519
+ options.localOrder + (isLabel ? 0 : 1),
4520
+ applySymbolStyle(
4521
+ base(
4522
+ options.id,
4523
+ "symbol",
4524
+ options.symbolFilter ? ["all", options.filter, options.symbolFilter] : options.filter
4525
+ ),
4526
+ withoutMarker(point)
4527
+ ),
4013
4528
  "symbols"
4014
4529
  )
4015
4530
  );
@@ -4022,31 +4537,107 @@ function withoutMarker(style) {
4022
4537
  function nauticalContribution(target, localOrder, layer, slot) {
4023
4538
  return { kind: "layer", layer, localOrder, owner: "nautical", slot, target };
4024
4539
  }
4025
- function nameAndDepthWithPrefix(nameField, depthField, prefix) {
4540
+ function themedImages(context) {
4541
+ const roles = tileflowNauticalImageRoles;
4542
+ const images = {};
4543
+ for (const group of Object.keys(roles)) {
4544
+ const image = context.images[roles[group].default];
4545
+ if (image) images[group] = image;
4546
+ }
4547
+ const buoy = images.buoys;
4548
+ if (typeof buoy === "string") {
4549
+ const port = context.images[roles.buoys.port] ?? buoy;
4550
+ const starboard = context.images[roles.buoys.starboard] ?? buoy;
4551
+ const cardinal = context.images[roles.buoys.cardinal] ?? buoy;
4552
+ images.buoys = expression([
4553
+ "case",
4554
+ ["==", ["get", fields.subclass], "cardinal"],
4555
+ cardinal,
4556
+ [
4557
+ "match",
4558
+ ["get", fields.category],
4559
+ [...portLateralCategories],
4560
+ port,
4561
+ [...starboardLateralCategories],
4562
+ starboard,
4563
+ buoy
4564
+ ]
4565
+ ]);
4566
+ }
4567
+ return images;
4568
+ }
4569
+ function firstColour(field2) {
4570
+ return [
4571
+ "let",
4572
+ "nauticalColours",
4573
+ ["to-string", ["coalesce", ["get", field2], ""]],
4574
+ leadingName(["var", "nauticalColours"])
4575
+ ];
4576
+ }
4577
+ function secondColour(field2) {
4578
+ return [
4579
+ "let",
4580
+ "nauticalColours",
4581
+ ["to-string", ["coalesce", ["get", field2], ""]],
4582
+ [
4583
+ "let",
4584
+ "nauticalComma",
4585
+ ["index-of", ",", ["var", "nauticalColours"]],
4586
+ [
4587
+ "case",
4588
+ ["<", ["var", "nauticalComma"], 0],
4589
+ "",
4590
+ [
4591
+ "let",
4592
+ "nauticalRest",
4593
+ ["slice", ["var", "nauticalColours"], ["+", ["var", "nauticalComma"], 1]],
4594
+ leadingName(["var", "nauticalRest"])
4595
+ ]
4596
+ ]
4597
+ ]
4598
+ ];
4599
+ }
4600
+ function leadingName(list) {
4601
+ return [
4602
+ "let",
4603
+ "nauticalEnd",
4604
+ ["index-of", ",", list],
4605
+ ["case", ["<", ["var", "nauticalEnd"], 0], list, ["slice", list, 0, ["var", "nauticalEnd"]]]
4606
+ ];
4607
+ }
4608
+ function colourMatch(name, palette, fallback) {
4609
+ return [
4610
+ "match",
4611
+ name,
4612
+ ...Object.entries(palette).flatMap(([colourName, value]) => [colourName, value]),
4613
+ fallback
4614
+ ];
4615
+ }
4616
+ function byClassAndZoom(stops) {
4026
4617
  return expression([
4027
- "concat",
4028
- prefix,
4029
- ["case", ["has", nameField], ["concat", " ", ["to-string", ["get", nameField]]], ""],
4030
- ["case", ["has", depthField], ["concat", " ", ["to-string", ["get", depthField]], "m"], ""]
4618
+ "interpolate",
4619
+ ["linear"],
4620
+ ["zoom"],
4621
+ ...stops.flatMap(([stop, major, minor]) => [
4622
+ stop,
4623
+ ["match", ["get", fields.class], "major", major, minor]
4624
+ ])
4031
4625
  ]);
4032
4626
  }
4033
- function lightLabel(fields) {
4627
+ function byPortSizeAndZoom(stops) {
4034
4628
  return expression([
4035
- "concat",
4036
- ["case", ["has", fields.character], ["to-string", ["get", fields.character]], "Lt"],
4037
- [
4038
- "case",
4039
- ["has", fields.rangeNm],
4040
- ["concat", " ", ["to-string", ["get", fields.rangeNm]], "M"],
4041
- ""
4042
- ],
4043
- [
4044
- "case",
4045
- ["has", fields.direction],
4046
- ["concat", " ", ["to-string", ["get", fields.direction]], "\xB0"],
4047
- ""
4048
- ],
4049
- ["case", ["has", fields.name], ["concat", " ", ["to-string", ["get", fields.name]]], ""]
4629
+ "interpolate",
4630
+ ["linear"],
4631
+ ["zoom"],
4632
+ ...stops.flatMap(([stop, values]) => [
4633
+ stop,
4634
+ [
4635
+ "match",
4636
+ ["get", fields.size],
4637
+ ...portSizes.flatMap((size, index) => [size, values[index]]),
4638
+ values[3]
4639
+ ]
4640
+ ])
4050
4641
  ]);
4051
4642
  }
4052
4643
 
@@ -4211,12 +4802,12 @@ function mergePoiCategoryLayers(entries, options) {
4211
4802
  };
4212
4803
  for (const part of ["layout", "paint"]) {
4213
4804
  const records = entries.map(({ layer }) => asRecord2(layer[part]));
4214
- const properties2 = [...new Set(records.flatMap((record3) => Object.keys(record3)))].sort();
4805
+ const properties2 = [...new Set(records.flatMap((record4) => Object.keys(record4)))].sort();
4215
4806
  const merged = {};
4216
4807
  for (const property of properties2) {
4217
4808
  const fallback = Object.hasOwn(varying, property) ? varying[property] : tileflowPoiLayerPropertyDefaults[property];
4218
4809
  const values = records.map(
4219
- (record3) => Object.hasOwn(record3, property) ? record3[property] : fallback
4810
+ (record4) => Object.hasOwn(record4, property) ? record4[property] : fallback
4220
4811
  );
4221
4812
  if (values.every((value) => sameJson(value, values[0]))) {
4222
4813
  if (values[0] !== void 0) merged[property] = values[0];
@@ -4575,11 +5166,11 @@ function compileCategory(category, request, semantics, context, language, select
4575
5166
  return parts;
4576
5167
  }
4577
5168
  function createPoiSelection(semantics, context, language) {
4578
- const { fields } = context.data.schema;
5169
+ const { fields: fields2 } = context.data.schema;
4579
5170
  const shown = /* @__PURE__ */ new Map();
4580
5171
  const iconOnlyTypes = [...new Set(semantics.placement.iconOnlyTypes)];
4581
- const iconOnly = () => iconOnlyTypes.length ? ["match", ["get", fields.poiType], iconOnlyTypes, true, false] : ["literal", false];
4582
- const inCategories = (categories) => categories.length === 1 ? ["==", ["get", fields.poiCategory], categories[0]] : ["match", ["get", fields.poiCategory], [...categories], true, false];
5172
+ const iconOnly = () => iconOnlyTypes.length ? ["match", ["get", fields2.poiType], iconOnlyTypes, true, false] : ["literal", false];
5173
+ const inCategories = (categories) => categories.length === 1 ? ["==", ["get", fields2.poiCategory], categories[0]] : ["match", ["get", fields2.poiCategory], [...categories], true, false];
4583
5174
  const named = () => [
4584
5175
  "any",
4585
5176
  ...labelNameFields(language, context).map((field2) => ["has", field2])
@@ -4592,22 +5183,22 @@ function createPoiSelection(semantics, context, language) {
4592
5183
  if (!iconOnlyTypes.length || field2 === void 0) return field2;
4593
5184
  return expression([
4594
5185
  "match",
4595
- ["get", fields.poiType],
5186
+ ["get", fields2.poiType],
4596
5187
  iconOnlyTypes,
4597
5188
  "",
4598
5189
  fieldValue(field2)
4599
5190
  ]);
4600
5191
  },
4601
5192
  filter(categories, representation) {
4602
- const filterRank = numericPoiField(fields.poiFilterRank, 6);
4603
- const sizeRank = numericPoiField(fields.poiSizeRank, 17);
5193
+ const filterRank = numericPoiField(fields2.poiFilterRank, 6);
5194
+ const sizeRank = numericPoiField(fields2.poiSizeRank, 17);
4604
5195
  const result = [
4605
5196
  "all",
4606
5197
  inCategories(categories),
4607
- ["has", fields.poiFilterRank],
5198
+ ["has", fields2.poiFilterRank],
4608
5199
  [">=", filterRank, 0],
4609
5200
  ["<=", filterRank, poiRankThreshold(semantics.density)],
4610
- ["has", fields.poiSizeRank],
5201
+ ["has", fields2.poiSizeRank],
4611
5202
  [">=", sizeRank, 0],
4612
5203
  ["<=", sizeRank, 16]
4613
5204
  ];
@@ -4830,9 +5421,19 @@ var visualNumberProperties = /* @__PURE__ */ new Set([
4830
5421
  "angle",
4831
5422
  "base",
4832
5423
  "blur",
5424
+ "brightnessScale",
4833
5425
  "crownScale",
4834
5426
  "dash",
4835
5427
  "exaggeration",
5428
+ "emissiveStrength",
5429
+ "emissiveVariation",
5430
+ "emissiveHeight",
5431
+ "colorMix",
5432
+ "metallic",
5433
+ "roughness",
5434
+ "hue",
5435
+ "saturation",
5436
+ "lightness",
4836
5437
  "fadeDuration",
4837
5438
  "gapWidth",
4838
5439
  "haloBlur",
@@ -4846,6 +5447,7 @@ var visualNumberProperties = /* @__PURE__ */ new Set([
4846
5447
  "maxAngle",
4847
5448
  "maxWidth",
4848
5449
  "miterLimit",
5450
+ "modelOcclusion",
4849
5451
  "offset",
4850
5452
  "opacity",
4851
5453
  "padding",
@@ -4941,8 +5543,11 @@ var visualNumberProperties = /* @__PURE__ */ new Set([
4941
5543
  "text-translate",
4942
5544
  "symbol-spacing"
4943
5545
  ]);
5546
+ function isTileflowVisualColorCollection(property) {
5547
+ return property === "colors" || property.endsWith("Colors") || property.endsWith("-colors");
5548
+ }
4944
5549
  function classifyTileflowVisualProperty(property, _value) {
4945
- if (property === "color" || property === "colors" || property.endsWith("Color") || property.endsWith("Colors") || property.endsWith("-color") || property.endsWith("-colors")) {
5550
+ if (property === "color" || property.endsWith("Color") || property.endsWith("-color") || isTileflowVisualColorCollection(property)) {
4946
5551
  return "color";
4947
5552
  }
4948
5553
  if (property === "font" || property === "fallbacks" || property.endsWith("-font")) return "font";
@@ -5046,7 +5651,7 @@ function auditTileflowThemeValues(value, path = "value", context = inferAuditCon
5046
5651
  return diagnostics.sort(compareAuditDiagnostics);
5047
5652
  }
5048
5653
  function auditTileflowMapThemeValues(map) {
5049
- const modules = isRecord3(map.modules) ? map.modules : void 0;
5654
+ const modules = isRecord4(map.modules) ? map.modules : void 0;
5050
5655
  const moduleDiagnostics = modules ? Object.keys(modules).sort(compareCodeUnits2).flatMap(
5051
5656
  (owner) => auditTileflowThemeValues(modules[owner], `modules.${owner}`, {
5052
5657
  owner,
@@ -5149,7 +5754,7 @@ function isThemePrimitiveForCategory(value, category) {
5149
5754
  return category === "number" ? typeof value === "number" && Number.isFinite(value) || Array.isArray(value) && value.every((entry) => typeof entry === "number" && Number.isFinite(entry)) : typeof value === "string" && value.length > 0;
5150
5755
  }
5151
5756
  function isThemeValueCandidate(value) {
5152
- return isRecord3(value) && (value.kind === "theme-token" || value.kind === "theme-fixed" || value.kind === "theme-color");
5757
+ return isRecord4(value) && (value.kind === "theme-token" || value.kind === "theme-fixed" || value.kind === "theme-color");
5153
5758
  }
5154
5759
  function visitAuditableValue(value, path, category, state, intentRequired = false) {
5155
5760
  if (isThemeStructuralPath(path)) category = void 0;
@@ -5165,7 +5770,7 @@ function visitAuditableValue(value, path, category, state, intentRequired = fals
5165
5770
  visitAuditableArray(value, path, category, state, false, intentRequired);
5166
5771
  return;
5167
5772
  }
5168
- if (!isRecord3(value)) return;
5773
+ if (!isRecord4(value)) return;
5169
5774
  if (value.kind === "expression" && Array.isArray(value.value)) {
5170
5775
  visitAuditableArray(value.value, `${path}.value`, category, state, true, intentRequired);
5171
5776
  return;
@@ -5185,15 +5790,19 @@ function visitAuditableValue(value, path, category, state, intentRequired = fals
5185
5790
  }
5186
5791
  return;
5187
5792
  }
5793
+ const collectionCategory = isTileflowVisualColorCollection(lastPathKey(path)) ? category : void 0;
5188
5794
  for (const key of Object.keys(value).sort(compareCodeUnits2)) {
5189
5795
  visitAuditableValue(
5190
5796
  value[key],
5191
5797
  `${path}.${key}`,
5192
- classifyTileflowVisualProperty(key, value[key]),
5798
+ classifyTileflowVisualProperty(key, value[key]) ?? collectionCategory,
5193
5799
  state
5194
5800
  );
5195
5801
  }
5196
5802
  }
5803
+ function lastPathKey(path) {
5804
+ return path.endsWith("]") ? "" : path.slice(path.lastIndexOf(".") + 1);
5805
+ }
5197
5806
  function visitAuditableArray(value, path, category, state, forceExpression = false, intentRequired = false) {
5198
5807
  const operator = typeof value[0] === "string" ? value[0] : void 0;
5199
5808
  const isExpression2 = forceExpression || isMapLibreExpressionOperator(operator);
@@ -5480,7 +6089,7 @@ var ThemeValueResolver = class {
5480
6089
  if (Array.isArray(value)) {
5481
6090
  return value.map((entry, index) => this.resolveTree(entry, `${path}[${index}]`, category));
5482
6091
  }
5483
- if (!isRecord3(value)) return value;
6092
+ if (!isRecord4(value)) return value;
5484
6093
  if (value.kind === "expression" && Array.isArray(value.value)) {
5485
6094
  return {
5486
6095
  ...value,
@@ -5694,13 +6303,13 @@ function assertThemeIdentity(theme) {
5694
6303
  }
5695
6304
  }
5696
6305
  function assertThemeDocument(theme) {
5697
- const record3 = assertPlainThemeRecord(theme, "theme");
5698
- assertExactThemeKeys(record3, "theme", themeDocumentKeys);
5699
- assertRequiredThemeKeys(record3, "theme", themeDocumentKeys);
6306
+ const record4 = assertPlainThemeRecord(theme, "theme");
6307
+ assertExactThemeKeys(record4, "theme", themeDocumentKeys);
6308
+ assertRequiredThemeKeys(record4, "theme", themeDocumentKeys);
5700
6309
  assertThemeIdentity(theme);
5701
- assertThemeTokens(record3.tokens, "theme.tokens", true);
5702
- assertThemeTypography(record3.typography, "theme.typography");
5703
- assertThemeLighting(record3.lighting, "theme.lighting");
6310
+ assertThemeTokens(record4.tokens, "theme.tokens", true);
6311
+ assertThemeTypography(record4.typography, "theme.typography");
6312
+ assertThemeLighting(record4.lighting, "theme.lighting");
5704
6313
  cloneJson14(theme);
5705
6314
  }
5706
6315
  function assertThemeTokens(value, path, complete) {
@@ -5794,7 +6403,7 @@ function assertThemeLighting(value, path, allowReset = false) {
5794
6403
  }
5795
6404
  }
5796
6405
  function assertThemeCategoryValue(value, expectedCategory, path) {
5797
- if (!isRecord3(value)) {
6406
+ if (!isRecord4(value)) {
5798
6407
  assertTokenPrimitive(expectedCategory, value, path);
5799
6408
  return;
5800
6409
  }
@@ -6146,11 +6755,11 @@ function cloneJson14(value) {
6146
6755
  if (serialized === void 0) throw new Error("Tileflow themes must be JSON-serializable.");
6147
6756
  return JSON.parse(serialized);
6148
6757
  }
6149
- function isRecord3(value) {
6758
+ function isRecord4(value) {
6150
6759
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6151
6760
  }
6152
6761
  function isPlainRecord2(value) {
6153
- if (!isRecord3(value)) return false;
6762
+ if (!isRecord4(value)) return false;
6154
6763
  const prototype = Object.getPrototypeOf(value);
6155
6764
  return prototype === Object.prototype || prototype === null;
6156
6765
  }
@@ -6656,12 +7265,12 @@ function rgbToHex(r, g, b) {
6656
7265
 
6657
7266
  // src/modules/roads/roundabouts.ts
6658
7267
  function compileRoundabouts(request, surfaces, context, contributions) {
6659
- const { fields, layers } = context.data.schema;
6660
- if (!layers.circularFeature) return;
7268
+ const { fields: fields2, layers: layers2 } = context.data.schema;
7269
+ if (!layers2.circularFeature) return;
6661
7270
  const drawn = surfaces.filter(({ fill }) => fill && fill.visible !== false);
6662
7271
  if (!drawn.length) return;
6663
7272
  const values = (resolve) => drawn.map((surface) => ({
6664
- condition: tileflowRoadClassFilter(fields, surface.roadClass),
7273
+ condition: tileflowRoadClassFilter(fields2, surface.roadClass),
6665
7274
  value: toMapLibreStyleValue(resolve(surface))
6666
7275
  }));
6667
7276
  const widths = values(({ fill }) => fill?.width ?? 1);
@@ -6669,10 +7278,10 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6669
7278
  const gaps = values(({ casing }) => casing?.gapWidth ?? 0);
6670
7279
  const fallbackWidth = request?.fallbackWidth === void 0 ? void 0 : toMapLibreStyleValue(request.fallbackWidth);
6671
7280
  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];
7281
+ const radiusAt15 = ["to-number", ["get", fields2.circularRadiusAtZoom15], 0];
7282
+ const radiusMeters = ["to-number", ["get", fields2.circularRadiusMeters], 0];
7283
+ const innerMeters = ["to-number", ["get", fields2.circularInnerRadiusMeters], 0];
7284
+ const outerMeters = ["to-number", ["get", fields2.circularOuterRadiusMeters], 0];
6676
7285
  const physical = [
6677
7286
  "all",
6678
7287
  [">", radiusMeters, 0],
@@ -6702,35 +7311,37 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6702
7311
  }),
6703
7312
  0
6704
7313
  ) : 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
- ]);
7314
+ const geometry = (casing, width) => expression(
7315
+ bindRepeated([
7316
+ "interpolate",
7317
+ ["exponential", 2],
7318
+ ["zoom"],
7319
+ ...geometryLevels.flatMap((level) => {
7320
+ const centerline = ["*", radiusAt15, 2 ** (level - 15)];
7321
+ const fallback = fallbackWidth === void 0 ? select(
7322
+ widths.map(({ condition, value }) => ({ condition, value: atZoom(value, level) })),
7323
+ 0
7324
+ ) : atZoom(fallbackWidth, level);
7325
+ const inner = [
7326
+ "case",
7327
+ physical,
7328
+ ["*", centerline, ["/", innerMeters, radiusMeters]],
7329
+ ["-", centerline, ["/", fallback, 2]]
7330
+ ];
7331
+ const fillWidth = [
7332
+ "case",
7333
+ physical,
7334
+ ["*", centerline, ["/", ["-", outerMeters, innerMeters], radiusMeters]],
7335
+ fallback
7336
+ ];
7337
+ const border = casing ? borderAt(level) : 0;
7338
+ return [
7339
+ level,
7340
+ width ? ["max", 0, ["+", fillWidth, border]] : ["max", 0, ["-", inner, ["/", border, 2]]]
7341
+ ];
7342
+ })
7343
+ ])
7344
+ );
6734
7345
  const phaseStyle = (phase) => ({
6735
7346
  color: "rgba(0, 0, 0, 0)",
6736
7347
  minZoom: 15,
@@ -6740,7 +7351,7 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6740
7351
  strokeWidth: geometry(phase === "casing", true),
6741
7352
  strokeColor: phase === "casing" ? context.colors.roads.casing : expression([
6742
7353
  "match",
6743
- ["coalesce", ["get", fields.class], "minor"],
7354
+ ["coalesce", ["get", fields2.class], "minor"],
6744
7355
  "motorway",
6745
7356
  context.colors.roads.motorway,
6746
7357
  "trunk",
@@ -6751,7 +7362,7 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6751
7362
  context.colors.roads.secondary,
6752
7363
  context.colors.roads.minor
6753
7364
  ]),
6754
- strokeOpacity: phaseVisibility(drawn, phase, fields)
7365
+ strokeOpacity: phaseVisibility(drawn, phase, fields2)
6755
7366
  });
6756
7367
  for (const phase of ["casing", "fill"]) {
6757
7368
  const style = mergeTileflowDesign(phaseStyle(phase), request?.[phase]);
@@ -6767,13 +7378,13 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6767
7378
  id: `tileflow-road-circular-${phase}`,
6768
7379
  type: "circle",
6769
7380
  source: context.data.sourceId,
6770
- "source-layer": layers.circularFeature,
7381
+ "source-layer": layers2.circularFeature,
6771
7382
  filter: [
6772
7383
  "all",
6773
- ["==", ["get", fields.circularKind], "road_ring"],
7384
+ ["==", ["get", fields2.circularKind], "road_ring"],
6774
7385
  [
6775
7386
  "any",
6776
- ...eligible.map((surface) => tileflowRoadClassFilter(fields, surface.roadClass))
7387
+ ...eligible.map((surface) => tileflowRoadClassFilter(fields2, surface.roadClass))
6777
7388
  ]
6778
7389
  ]
6779
7390
  },
@@ -6787,13 +7398,70 @@ function compileRoundabouts(request, surfaces, context, contributions) {
6787
7398
  });
6788
7399
  }
6789
7400
  }
7401
+ var bindable = /* @__PURE__ */ new Set([
7402
+ "case",
7403
+ "match",
7404
+ "all",
7405
+ "any",
7406
+ "!",
7407
+ "==",
7408
+ "!=",
7409
+ "<",
7410
+ "<=",
7411
+ ">",
7412
+ ">=",
7413
+ "has",
7414
+ "get",
7415
+ "to-number",
7416
+ "coalesce",
7417
+ "+",
7418
+ "-",
7419
+ "*",
7420
+ "/",
7421
+ "max",
7422
+ "min"
7423
+ ]);
7424
+ function bindRepeated(curve) {
7425
+ let tree = curve;
7426
+ for (let index = 0; index < 2; index += 1) {
7427
+ const json = /* @__PURE__ */ new Map();
7428
+ const found = /* @__PURE__ */ new Map();
7429
+ const visit = (value) => {
7430
+ if (!Array.isArray(value) || value[0] === "literal")
7431
+ return { text: JSON.stringify(value), free: true };
7432
+ const parts = value.map(visit);
7433
+ const text = `[${parts.map((part) => part.text).join(",")}]`;
7434
+ json.set(value, text);
7435
+ const free = value[0] !== "zoom" && value[0] !== "var" && parts.every((part) => part.free);
7436
+ if (free && typeof value[0] === "string" && bindable.has(value[0]))
7437
+ found.set(text, (found.get(text) ?? 0) + 1);
7438
+ return { text, free };
7439
+ };
7440
+ visit(tree);
7441
+ let best;
7442
+ let saving = 256;
7443
+ for (const [text, count] of found) {
7444
+ const saved = (count - 1) * text.length;
7445
+ if (count > 1 && saved > saving) [best, saving] = [text, saved];
7446
+ }
7447
+ if (best === void 0) break;
7448
+ const name = `tileflow_${index}`;
7449
+ const replace = (value) => {
7450
+ if (!Array.isArray(value) || value[0] === "literal") return value;
7451
+ if (json.get(value) === best) return ["var", name];
7452
+ return value.map(replace);
7453
+ };
7454
+ tree = ["let", name, JSON.parse(best), replace(tree)];
7455
+ }
7456
+ return tree;
7457
+ }
6790
7458
  function select(values, fallback) {
6791
7459
  if (!values.length) return fallback;
6792
7460
  const serialized = JSON.stringify(values[0].value);
6793
7461
  if (values.every(({ value }) => JSON.stringify(value) === serialized)) return values[0].value;
6794
7462
  return ["case", ...values.flatMap(({ condition, value }) => [condition, value]), fallback];
6795
7463
  }
6796
- function phaseVisibility(surfaces, phase, fields) {
7464
+ function phaseVisibility(surfaces, phase, fields2) {
6797
7465
  const levels = [
6798
7466
  ...new Set(
6799
7467
  surfaces.flatMap((surface) => {
@@ -6809,7 +7477,7 @@ function phaseVisibility(surfaces, phase, fields) {
6809
7477
  surfaces.map((surface) => {
6810
7478
  const style = surface[phase];
6811
7479
  return {
6812
- condition: tileflowRoadClassFilter(fields, surface.roadClass),
7480
+ condition: tileflowRoadClassFilter(fields2, surface.roadClass),
6813
7481
  value: (style?.minZoom === void 0 || level >= style.minZoom) && (style?.maxZoom === void 0 || level < style.maxZoom) ? 1 : 0
6814
7482
  };
6815
7483
  }),
@@ -6898,13 +7566,49 @@ function atZoom(value, level) {
6898
7566
  stops.push({ level: value[index], value: value[index + 1] });
6899
7567
  }
6900
7568
  const exact = stops.find((stop) => stop.level === level);
6901
- if (exact) return exact.value;
7569
+ if (exact) return foldConstants(exact.value);
6902
7570
  const lower = [...stops].reverse().find((stop) => stop.level < level);
6903
7571
  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];
7572
+ if (value[0] === "step") return foldConstants(lower?.value ?? value[2]);
7573
+ if (!lower) return foldConstants(upper?.value ?? 0);
7574
+ if (!upper) return foldConstants(lower.value);
7575
+ const at = interpolationFactor2(
7576
+ value[1],
7577
+ level - lower.level,
7578
+ upper.level - lower.level
7579
+ );
7580
+ const folded = Number.isFinite(at) ? interpolated(foldConstants(lower.value), foldConstants(upper.value), at) : void 0;
7581
+ return folded ?? ["interpolate", value[1], level, lower.level, lower.value, upper.level, upper.value];
7582
+ }
7583
+ function foldConstants(value) {
7584
+ if (!Array.isArray(value) || value[0] === "literal") return value;
7585
+ const folded = value.map(foldConstants);
7586
+ const [operator, ...operands] = folded;
7587
+ if (!operands.length || !operands.every((operand) => typeof operand === "number")) return folded;
7588
+ const numbers = operands;
7589
+ const result = operator === "*" ? numbers.reduce((product, number) => product * number, 1) : operator === "+" ? numbers.reduce((sum, number) => sum + number, 0) : operator === "-" ? numbers.length === 1 ? -numbers[0] : numbers[0] - numbers[1] : operator === "/" && numbers.length === 2 ? numbers[0] / numbers[1] : operator === "max" ? Math.max(...numbers) : operator === "min" ? Math.min(...numbers) : void 0;
7590
+ return result !== void 0 && Number.isFinite(result) ? result : folded;
7591
+ }
7592
+ function interpolated(lower, upper, at) {
7593
+ if (typeof lower === "number" && typeof upper === "number") return lower + (upper - lower) * at;
7594
+ if (JSON.stringify(lower) === JSON.stringify(upper)) return lower;
7595
+ if (!Array.isArray(lower) || !Array.isArray(upper) || lower.length !== upper.length || lower[0] !== "case" && lower[0] !== "match" || lower[0] !== upper[0])
7596
+ return void 0;
7597
+ const first = lower[0] === "case" ? 1 : 2;
7598
+ const result = lower.slice(0, first);
7599
+ if (JSON.stringify(result) !== JSON.stringify(upper.slice(0, first))) return void 0;
7600
+ for (let index = first; index < lower.length; index += 1) {
7601
+ const output = (index - first) % 2 === 1 || index === lower.length - 1;
7602
+ if (!output) {
7603
+ if (JSON.stringify(lower[index]) !== JSON.stringify(upper[index])) return void 0;
7604
+ result.push(lower[index]);
7605
+ continue;
7606
+ }
7607
+ const value = interpolated(lower[index], upper[index], at);
7608
+ if (value === void 0) return void 0;
7609
+ result.push(value);
7610
+ }
7611
+ return result;
6908
7612
  }
6909
7613
 
6910
7614
  // src/modules/roads/compiler.ts
@@ -6975,6 +7679,10 @@ function compileRoads(request, context) {
6975
7679
  request?.classes?.[roadClass]
6976
7680
  );
6977
7681
  const treatments = resolveRoadTreatments(request, schema.fields, roadClass);
7682
+ const drawn = {
7683
+ field: schema.fields.class,
7684
+ classes: new Set(tileflowRoadClassValues(roadClass))
7685
+ };
6978
7686
  const roundaboutSurface = { roadClass };
6979
7687
  roundaboutSurfaces.push(roundaboutSurface);
6980
7688
  for (const structure of ["tunnel", "surface", "bridge"]) {
@@ -6997,7 +7705,8 @@ function compileRoads(request, context) {
6997
7705
  treatments,
6998
7706
  structure,
6999
7707
  phase,
7000
- phaseDefaults.color
7708
+ phaseDefaults.color,
7709
+ drawn
7001
7710
  );
7002
7711
  const contribution = {
7003
7712
  family: roadLinePhysicalFamily(roadClass, structure, phase),
@@ -7060,7 +7769,8 @@ function compileRoads(request, context) {
7060
7769
  treatments,
7061
7770
  structure,
7062
7771
  "fill",
7063
- roadColor(context, roadClass)
7772
+ roadColor(context, roadClass),
7773
+ drawn
7064
7774
  );
7065
7775
  const size = hatch.size === void 0 ? scaleStyleValue(treatedFill.width ?? 1, 0.68) : toMapLibreStyleValue(hatch.size);
7066
7776
  const minZoom = hatch.minZoom ?? treatedFill.minZoom;
@@ -7181,31 +7891,24 @@ function compileRoads(request, context) {
7181
7891
  const areaTargets = [
7182
7892
  {
7183
7893
  id: "tileflow-road-area",
7184
- filter: ["==", ["geometry-type"], "Polygon"],
7894
+ kind: void 0,
7185
7895
  name: "road",
7186
7896
  style: areaStyles.road
7187
7897
  },
7188
7898
  {
7189
7899
  id: "tileflow-road-pedestrian-area",
7190
- filter: [
7191
- "all",
7192
- ["==", ["geometry-type"], "Polygon"],
7193
- tileflowRoadClassFilter(schema.fields, "pedestrian")
7194
- ],
7900
+ kind: tileflowRoadClassFilter(schema.fields, "pedestrian"),
7195
7901
  name: "pedestrian",
7196
7902
  style: areaStyles.pedestrian
7197
7903
  },
7198
7904
  {
7199
7905
  id: "tileflow-road-pier-area",
7200
- filter: [
7201
- "all",
7202
- ["==", ["geometry-type"], "Polygon"],
7203
- ["==", ["get", schema.fields.class], "pier"]
7204
- ],
7906
+ kind: ["==", ["get", schema.fields.class], "pier"],
7205
7907
  name: "pier",
7206
7908
  style: areaStyles.pier
7207
7909
  }
7208
7910
  ];
7911
+ const polygons2 = ["==", ["geometry-type"], "Polygon"];
7209
7912
  let areaOrder = 900;
7210
7913
  for (const target of areaTargets) {
7211
7914
  if (!target.style) continue;
@@ -7215,13 +7918,19 @@ function compileRoads(request, context) {
7215
7918
  type: "fill",
7216
7919
  source,
7217
7920
  "source-layer": schema.layers.road,
7218
- filter: target.filter
7921
+ filter: target.kind ? ["all", polygons2, target.kind] : polygons2
7219
7922
  },
7220
7923
  target.style
7221
7924
  )) {
7925
+ const { filter: _polygonFilter, ...unfiltered } = area.layer;
7926
+ const outline = area.phase === "outline" && schema.layers.transportationOutline ? {
7927
+ ...unfiltered,
7928
+ "source-layer": schema.layers.transportationOutline,
7929
+ ...target.kind ? { filter: target.kind } : {}
7930
+ } : area.layer;
7222
7931
  contributions.push({
7223
7932
  kind: "layer",
7224
- layer: area.layer,
7933
+ layer: outline,
7225
7934
  localOrder: areaOrder++,
7226
7935
  owner: "roads",
7227
7936
  slot: "transport-areas",
@@ -7299,8 +8008,8 @@ function roadPhysicalCohort(roadClass) {
7299
8008
  return "path";
7300
8009
  }
7301
8010
  function compileSidewalks(request, pedestrianAreaColor, context, contributions) {
7302
- const { fields, layers } = context.data.schema;
7303
- if (!layers.sidewalk) return;
8011
+ const { fields: fields2, layers: layers2 } = context.data.schema;
8012
+ if (!layers2.sidewalk) return;
7304
8013
  const config = mergeTileflowDesign(
7305
8014
  {
7306
8015
  surface: {
@@ -7314,11 +8023,11 @@ function compileSidewalks(request, pedestrianAreaColor, context, contributions)
7314
8023
  const base = {
7315
8024
  type: "fill",
7316
8025
  source: context.data.sourceId,
7317
- "source-layer": layers.sidewalk,
8026
+ "source-layer": layers2.sidewalk,
7318
8027
  filter: [
7319
8028
  "all",
7320
8029
  ["==", ["geometry-type"], "Polygon"],
7321
- ["match", ["get", fields.class], ["sidewalk", "pedestrian"], true, false]
8030
+ ["match", ["get", fields2.class], ["sidewalk", "pedestrian"], true, false]
7322
8031
  ]
7323
8032
  };
7324
8033
  if (config.surface?.visible !== false) {
@@ -7356,8 +8065,8 @@ function compileSidewalks(request, pedestrianAreaColor, context, contributions)
7356
8065
  }
7357
8066
  }
7358
8067
  function compileCrossings(request, context, contributions) {
7359
- const { fields, layers } = context.data.schema;
7360
- if (!request || !layers.streetFurniture) return;
8068
+ const { fields: fields2, layers: layers2 } = context.data.schema;
8069
+ if (!request || !layers2.streetFurniture) return;
7361
8070
  const config = mergeTileflowDesign(
7362
8071
  {
7363
8072
  allowOverlap: true,
@@ -7370,7 +8079,7 @@ function compileCrossings(request, context, contributions) {
7370
8079
  ]),
7371
8080
  padding: 0,
7372
8081
  pitchAlignment: "map",
7373
- rotate: expression(["+", ["to-number", ["get", fields.direction], 0], 90]),
8082
+ rotate: expression(["+", ["to-number", ["get", fields2.direction], 0], 90]),
7374
8083
  rotationAlignment: "map",
7375
8084
  size: zoom.linear([
7376
8085
  [15, 0.22],
@@ -7393,14 +8102,14 @@ function compileCrossings(request, context, contributions) {
7393
8102
  id: "tileflow-road-crossing",
7394
8103
  type: "symbol",
7395
8104
  source: context.data.sourceId,
7396
- "source-layer": layers.streetFurniture,
8105
+ "source-layer": layers2.streetFurniture,
7397
8106
  filter: [
7398
8107
  "all",
7399
8108
  ["==", ["geometry-type"], "Point"],
7400
- ["==", ["get", fields.subclass], "crossing"],
7401
- ["!=", ["get", fields.crossing], "no"],
7402
- ["has", fields.markings],
7403
- ["match", ["get", fields.markings], ["no", "none", "unmarked"], false, true]
8109
+ ["==", ["get", fields2.subclass], "crossing"],
8110
+ ["!=", ["get", fields2.crossing], "no"],
8111
+ ["has", fields2.markings],
8112
+ ["match", ["get", fields2.markings], ["no", "none", "unmarked"], false, true]
7404
8113
  ]
7405
8114
  },
7406
8115
  config
@@ -7476,43 +8185,44 @@ function substituteZoom(value, level) {
7476
8185
  if (value.length === 1 && value[0] === "zoom") return level;
7477
8186
  return value.map((entry) => substituteZoom(entry, level));
7478
8187
  }
7479
- function resolveRoadTreatments(request, fields, roadClass) {
8188
+ function resolveRoadTreatments(request, fields2, roadClass) {
7480
8189
  const entries = [];
7481
8190
  const add2 = (style, condition) => {
7482
8191
  if (style && style.enabled !== false) entries.push({ condition, style });
7483
8192
  };
7484
8193
  add2(request?.modifiers?.construction, [
7485
8194
  "match",
7486
- ["get", fields.class],
8195
+ ["get", fields2.class],
7487
8196
  tileflowRoadConstructionClasses,
7488
8197
  true,
7489
8198
  false
7490
8199
  ]);
7491
- add2(request?.restrictions?.bicycle, restrictedAccessFilter(fields.bicycle));
7492
- add2(request?.restrictions?.foot, restrictedAccessFilter(fields.foot));
7493
- add2(request?.restrictions?.horse, restrictedAccessFilter(fields.horse));
7494
- add2(request?.restrictions?.access, restrictedAccessFilter(fields.access));
7495
- add2(request?.restrictions?.toll, flagFilter(fields.toll));
7496
- add2(request?.modifiers?.expressway, flagFilter(fields.expressway));
7497
- add2(request?.modifiers?.ramp, flagFilter(fields.ramp));
7498
- add2(request?.modifiers?.unpaved, ["==", ["get", fields.surface], "unpaved"]);
7499
- add2(request?.modifiers?.indoor, flagFilter(fields.indoor));
7500
- add2(request?.modifiers?.official, flagFilter(fields.official));
7501
- for (const [scale, style] of Object.entries(request?.mountainBike ?? {})) {
7502
- add2(style, ["==", ["to-string", ["get", fields.mtbScale]], scale]);
7503
- }
8200
+ add2(request?.restrictions?.bicycle, restrictedAccessFilter(fields2.bicycle));
8201
+ add2(request?.restrictions?.foot, restrictedAccessFilter(fields2.foot));
8202
+ add2(request?.restrictions?.horse, restrictedAccessFilter(fields2.horse));
8203
+ add2(request?.restrictions?.access, restrictedAccessFilter(fields2.access));
8204
+ add2(request?.restrictions?.toll, flagFilter(fields2.toll));
8205
+ add2(request?.modifiers?.expressway, flagFilter(fields2.expressway));
8206
+ add2(request?.modifiers?.ramp, flagFilter(fields2.ramp));
8207
+ add2(request?.modifiers?.unpaved, ["==", ["get", fields2.surface], "unpaved"]);
8208
+ add2(request?.modifiers?.indoor, flagFilter(fields2.indoor));
8209
+ add2(request?.modifiers?.official, flagFilter(fields2.official));
8210
+ if (isTileflowPathRoadClass(roadClass) || roadClass === "track")
8211
+ for (const [scale, style] of Object.entries(request?.mountainBike ?? {})) {
8212
+ add2(style, ["==", ["to-string", ["get", fields2.mtbScale]], scale]);
8213
+ }
7504
8214
  if (roadClass === "service") {
7505
8215
  for (const [serviceType, value] of Object.entries(serviceTypeValues)) {
7506
8216
  add2(request?.serviceTypes?.[serviceType], [
7507
8217
  "==",
7508
- ["get", fields.service],
8218
+ ["get", fields2.service],
7509
8219
  value
7510
8220
  ]);
7511
8221
  }
7512
8222
  }
7513
8223
  return entries;
7514
8224
  }
7515
- function applyRoadTreatments(base, treatments, structure, phase, fallbackColor) {
8225
+ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor, drawn) {
7516
8226
  const result = { ...base };
7517
8227
  const paintProperties = [
7518
8228
  "blur",
@@ -7529,12 +8239,12 @@ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor)
7529
8239
  return value === void 0 ? [] : [
7530
8240
  {
7531
8241
  condition,
7532
- resolve: () => expressionOutput(toMapLibreStyleValue(value))
8242
+ resolve: () => expressionOutput(withinClasses(toMapLibreStyleValue(value), drawn))
7533
8243
  }
7534
8244
  ];
7535
8245
  });
7536
8246
  if (branches.length > 0) {
7537
- if (property === "dash" && isSolidRoadDash(result[property] ?? treatmentPaintDefaults.dash) && branches.every(({ resolve }) => isSolidRoadDash(resolve()))) {
8247
+ if (property === "dash" && isSolidRoadDash(withinClasses(result[property] ?? treatmentPaintDefaults.dash, drawn)) && branches.every(({ resolve }) => isSolidRoadDash(resolve()))) {
7538
8248
  delete result[property];
7539
8249
  continue;
7540
8250
  }
@@ -7564,6 +8274,49 @@ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor)
7564
8274
  }
7565
8275
  return result;
7566
8276
  }
8277
+ function withinClasses(value, drawn) {
8278
+ const decided = (condition) => {
8279
+ if (!Array.isArray(condition)) return typeof condition === "boolean" ? condition : void 0;
8280
+ const [operator, ...operands] = condition;
8281
+ const isClass = (operand) => Array.isArray(operand) && operand.length === 2 && operand[0] === "get" && (operand[1] === drawn.field || typeof operand[1] === "object" && operand[1] !== null && operand[1].kind === "tileflow-data-field" && operand[1].name === "class");
8282
+ const among = (labels2) => {
8283
+ const hits = [...drawn.classes].filter((value2) => labels2.includes(value2)).length;
8284
+ return hits === 0 ? false : hits === drawn.classes.size ? true : void 0;
8285
+ };
8286
+ if ((operator === "==" || operator === "!=") && operands.length === 2) {
8287
+ const label = isClass(operands[0]) ? operands[1] : isClass(operands[1]) ? operands[0] : null;
8288
+ if (typeof label !== "string") return void 0;
8289
+ const equal2 = among([label]);
8290
+ return equal2 === void 0 ? void 0 : operator === "==" ? equal2 : !equal2;
8291
+ }
8292
+ if (operator === "match" && operands.length === 4 && isClass(operands[0]) && typeof operands[2] === "boolean" && typeof operands[3] === "boolean") {
8293
+ const labels2 = Array.isArray(operands[1]) ? operands[1] : [operands[1]];
8294
+ const matched = among(labels2);
8295
+ return matched === void 0 ? void 0 : matched ? operands[2] : operands[3];
8296
+ }
8297
+ if (operator === "!" && operands.length === 1) {
8298
+ const inner = decided(operands[0]);
8299
+ return inner === void 0 ? void 0 : !inner;
8300
+ }
8301
+ if (operator === "all" || operator === "any") {
8302
+ const results = operands.map(decided);
8303
+ if (results.includes(operator === "all" ? false : true)) return operator !== "all";
8304
+ return results.every((result) => result !== void 0) ? operator === "all" : void 0;
8305
+ }
8306
+ return void 0;
8307
+ };
8308
+ if (!Array.isArray(value) || value[0] !== "case") return value;
8309
+ const branches = [];
8310
+ for (let index = 1; index < value.length - 1; index += 2) {
8311
+ const holds = decided(value[index]);
8312
+ if (holds === false) continue;
8313
+ const output = withinClasses(value[index + 1], drawn);
8314
+ if (holds === true) return branches.length ? ["case", ...branches, output] : output;
8315
+ branches.push(value[index], output);
8316
+ }
8317
+ const fallback = withinClasses(value.at(-1), drawn);
8318
+ return branches.length ? ["case", ...branches, fallback] : fallback;
8319
+ }
7567
8320
  function isSolidRoadDash(value) {
7568
8321
  const output = expressionOutput(value);
7569
8322
  const dash = Array.isArray(output) && output[0] === "literal" ? output[1] : output;
@@ -8308,7 +9061,7 @@ function compileWater(request, context, language = "auto") {
8308
9061
  );
8309
9062
  if (config.enabled === false) return [];
8310
9063
  const source = context.data.sourceId;
8311
- const { fields, layers } = context.data.schema;
9064
+ const { fields: fields2, layers: layers2 } = context.data.schema;
8312
9065
  const contributions = [];
8313
9066
  if (config.bodies) {
8314
9067
  for (const area of createAreaLayers(
@@ -8316,8 +9069,8 @@ function compileWater(request, context, language = "auto") {
8316
9069
  id: "tileflow-water",
8317
9070
  type: "fill",
8318
9071
  source,
8319
- "source-layer": layers.water,
8320
- filter: ["!=", ["get", fields.intermittent], 1]
9072
+ "source-layer": layers2.water,
9073
+ filter: ["!=", ["get", fields2.intermittent], 1]
8321
9074
  },
8322
9075
  config.bodies
8323
9076
  )) {
@@ -8337,8 +9090,8 @@ function compileWater(request, context, language = "auto") {
8337
9090
  id: "tileflow-water-intermittent",
8338
9091
  type: "fill",
8339
9092
  source,
8340
- "source-layer": layers.water,
8341
- filter: ["==", ["get", fields.intermittent], 1]
9093
+ "source-layer": layers2.water,
9094
+ filter: ["==", ["get", fields2.intermittent], 1]
8342
9095
  },
8343
9096
  config.intermittent.bodies
8344
9097
  )) {
@@ -8356,9 +9109,9 @@ function compileWater(request, context, language = "auto") {
8356
9109
  const sidecarBathymetry = bathymetryProduct?.vector;
8357
9110
  const usePrimaryBathymetryFallback = context.marine === void 0;
8358
9111
  const bathymetrySource = sidecarBathymetry?.sourceId ?? source;
8359
- const bathymetryLayer = sidecarBathymetry ? "bathymetry" : usePrimaryBathymetryFallback ? layers.bathymetry : void 0;
8360
- const bathymetryDepthField = sidecarBathymetry ? "min_depth" : usePrimaryBathymetryFallback ? fields.bathymetryMinDepth : void 0;
8361
- const bathymetrySortField = sidecarBathymetry ? "sort_key" : usePrimaryBathymetryFallback ? fields.bathymetrySortKey : void 0;
9112
+ const bathymetryLayer = sidecarBathymetry ? "bathymetry" : usePrimaryBathymetryFallback ? layers2.bathymetry : void 0;
9113
+ const bathymetryDepthField = sidecarBathymetry ? "min_depth" : usePrimaryBathymetryFallback ? fields2.bathymetryMinDepth : void 0;
9114
+ const bathymetrySortField = sidecarBathymetry ? "sort_key" : usePrimaryBathymetryFallback ? fields2.bathymetrySortKey : void 0;
8362
9115
  if (bathymetryLayer && bathymetryDepthField && bathymetrySortField) {
8363
9116
  const depth = context.colors.hydro.depth;
8364
9117
  const depthStops = bathymetryDepthStops(depth);
@@ -8558,11 +9311,11 @@ function compileWater(request, context, language = "auto") {
8558
9311
  id: `tileflow-waterway-${name}`,
8559
9312
  type: "line",
8560
9313
  source,
8561
- "source-layer": layers.waterway,
9314
+ "source-layer": layers2.waterway,
8562
9315
  filter: [
8563
9316
  "all",
8564
- classFilter3(fields.class, classes),
8565
- ["!=", ["get", fields.intermittent], 1]
9317
+ classFilter3(fields2.class, classes),
9318
+ ["!=", ["get", fields2.intermittent], 1]
8566
9319
  ]
8567
9320
  },
8568
9321
  style
@@ -8587,11 +9340,11 @@ function compileWater(request, context, language = "auto") {
8587
9340
  id: `tileflow-waterway-${name}-intermittent`,
8588
9341
  type: "line",
8589
9342
  source,
8590
- "source-layer": layers.waterway,
9343
+ "source-layer": layers2.waterway,
8591
9344
  filter: [
8592
9345
  "all",
8593
- classFilter3(fields.class, classes),
8594
- ["==", ["get", fields.intermittent], 1]
9346
+ classFilter3(fields2.class, classes),
9347
+ ["==", ["get", fields2.intermittent], 1]
8595
9348
  ]
8596
9349
  },
8597
9350
  mergeTileflowDesign(style, intermittent)
@@ -8606,8 +9359,8 @@ function compileWater(request, context, language = "auto") {
8606
9359
  }
8607
9360
  function appendBathymetryNames(contributions, options) {
8608
9361
  const { context, language } = options;
8609
- const { fields } = tileflowBathymetryV1Schema;
8610
- const localName = ["to-string", ["coalesce", ["get", fields.name], ""]];
9362
+ const { fields: fields2 } = tileflowBathymetryV1Schema;
9363
+ const localName = ["to-string", ["coalesce", ["get", fields2.name], ""]];
8611
9364
  const name = language === "auto" || language === "local" ? localName : [
8612
9365
  "case",
8613
9366
  ["!=", ["to-string", ["coalesce", ["get", `name:${language}`], ""]], ""],
@@ -8618,7 +9371,7 @@ function appendBathymetryNames(contributions, options) {
8618
9371
  {
8619
9372
  minZoom: 0,
8620
9373
  placement: "point",
8621
- priority: expression(["to-number", ["get", fields.sortKey], 0]),
9374
+ priority: expression(["to-number", ["get", fields2.sortKey], 0]),
8622
9375
  priorityOrder: "lower-first",
8623
9376
  text: {
8624
9377
  ...typographyTextStyle(context.typography.water),
@@ -8636,8 +9389,8 @@ function appendBathymetryNames(contributions, options) {
8636
9389
  options.style
8637
9390
  );
8638
9391
  if (style.visible === false || style.text?.visible === false) return;
8639
- const sourceMinimum = ["to-number", ["coalesce", ["get", fields.minZoom], 0], 0];
8640
- const sourceMaximum = ["to-number", ["coalesce", ["get", fields.maxZoom], 24], 24];
9392
+ const sourceMinimum = ["to-number", ["coalesce", ["get", fields2.minZoom], 0], 0];
9393
+ const sourceMaximum = ["to-number", ["coalesce", ["get", fields2.maxZoom], 24], 24];
8641
9394
  const ranges = options.availability === true ? void 0 : [...options.availability.zoomRanges].sort(
8642
9395
  (left, right) => left.minZoom - right.minZoom || left.maxZoom - right.maxZoom
8643
9396
  );
@@ -9078,8 +9831,8 @@ function assertProvidedServices(domain, services) {
9078
9831
  }
9079
9832
  }
9080
9833
  function checkedModuleRecord(entries) {
9081
- const record3 = Object.fromEntries(entries);
9082
- const actual = Object.keys(record3).sort(compareCodeUnits3);
9834
+ const record4 = Object.fromEntries(entries);
9835
+ const actual = Object.keys(record4).sort(compareCodeUnits3);
9083
9836
  if (actual.length !== tileflowSemanticDomainRegistry.length || actual.some(
9084
9837
  (name, index) => name !== [...tileflowSemanticDomainRegistry.map(({ name: name2 }) => name2)].sort(compareCodeUnits3)[index]
9085
9838
  )) {
@@ -9087,7 +9840,7 @@ function checkedModuleRecord(entries) {
9087
9840
  "Tileflow semantic domain registry did not produce one value for every module."
9088
9841
  );
9089
9842
  }
9090
- return record3;
9843
+ return record4;
9091
9844
  }
9092
9845
  function assertRegistryIsValid() {
9093
9846
  const names = /* @__PURE__ */ new Set();
@@ -9146,12 +9899,12 @@ function readTileflowCompilerProvenance(layer) {
9146
9899
  const target = metadata[tileflowCompilerMetadataKeys.target];
9147
9900
  return owner && slot && typeof target === "string" && tileflowSemanticTargetPattern.test(target) ? [{ operations: [], owner, slot, target }] : [];
9148
9901
  }
9149
- function createTileflowStyleInspection(map, theme, layers) {
9902
+ function createTileflowStyleInspection(map, theme, layers2) {
9150
9903
  return {
9151
9904
  schemaVersion: tileflowStyleInspectionSchemaVersion,
9152
9905
  map,
9153
9906
  theme,
9154
- layers: layers.map((layer, index) => ({
9907
+ layers: layers2.map((layer, index) => ({
9155
9908
  id: typeof layer.id === "string" ? layer.id : "",
9156
9909
  index,
9157
9910
  type: typeof layer.type === "string" ? layer.type : "",
@@ -9251,12 +10004,12 @@ var classicManifestSchemaOperations = {
9251
10004
 
9252
10005
  // src/portable-identity-schema.ts
9253
10006
  function createPortableIdentitySchemas(operations) {
9254
- const bounded = operations.maxLength(
10007
+ const bounded2 = operations.maxLength(
9255
10008
  operations.minLength(operations.string(), 1),
9256
10009
  tileflowPortableIdMaximumLength
9257
10010
  );
9258
10011
  const pattern = operations.regex(
9259
- bounded,
10012
+ bounded2,
9260
10013
  tileflowPortableIdPattern,
9261
10014
  "Expected lowercase kebab-case beginning with a letter"
9262
10015
  );
@@ -9370,7 +10123,7 @@ function insertTileflowOverlays(input) {
9370
10123
  available.length > 0 ? `Use one of: ${available.join(", ")}.` : "Declare the source first."
9371
10124
  );
9372
10125
  }
9373
- const layers2 = overlay.layers.map((layer, index) => {
10126
+ const layers3 = overlay.layers.map((layer, index) => {
9374
10127
  if (layerIds.has(layer.id)) {
9375
10128
  throw new TileflowOverlayCompilationError(
9376
10129
  "TF_OVERLAY_LAYER_ID_DUPLICATE",
@@ -9388,21 +10141,21 @@ function insertTileflowOverlays(input) {
9388
10141
  }
9389
10142
  };
9390
10143
  });
9391
- groups.set(overlay.placement, [...groups.get(overlay.placement) ?? [], ...layers2]);
10144
+ groups.set(overlay.placement, [...groups.get(overlay.placement) ?? [], ...layers3]);
9392
10145
  identities[overlayId] = Object.freeze({
9393
10146
  layers: Object.freeze(overlay.layers.map(({ id: id2 }) => id2)),
9394
10147
  placement: overlay.placement,
9395
10148
  source: overlay.source
9396
10149
  });
9397
10150
  }
9398
- let layers = input.basemapLayers.map(cloneJson18);
10151
+ let layers2 = input.basemapLayers.map(cloneJson18);
9399
10152
  for (const placement of tileflowOverlayPlacements) {
9400
10153
  const overlayLayers = groups.get(placement);
9401
10154
  if (!overlayLayers?.length) continue;
9402
- const index = tileflowOverlayInsertionIndex(layers, placement);
9403
- layers = [...layers.slice(0, index), ...overlayLayers, ...layers.slice(index)];
10155
+ const index = tileflowOverlayInsertionIndex(layers2, placement);
10156
+ layers2 = [...layers2.slice(0, index), ...overlayLayers, ...layers2.slice(index)];
9404
10157
  }
9405
- return { identities, layers };
10158
+ return { identities, layers: layers2 };
9406
10159
  }
9407
10160
  function isSafeLocalPmtilesPath(value) {
9408
10161
  if (typeof value !== "string" || value.length > 512 || !value.endsWith(".pmtiles") || value.includes("\\") || /[\p{Cc}?#]/u.test(value)) {
@@ -9426,8 +10179,8 @@ function cloneJson18(value) {
9426
10179
  Object.entries(value).map(([key, child]) => [key, cloneJson18(child)])
9427
10180
  );
9428
10181
  }
9429
- function tileflowOverlayInsertionIndex(layers, placement) {
9430
- const ranks = layers.map((layer) => {
10182
+ function tileflowOverlayInsertionIndex(layers2, placement) {
10183
+ const ranks = layers2.map((layer) => {
9431
10184
  const slots = readTileflowCompilerProvenance(layer).map(
9432
10185
  ({ slot }) => tileflowLayerSlots.indexOf(slot)
9433
10186
  );
@@ -9634,6 +10387,14 @@ function validateBoundaryChoroplethOverlays(mapId, overlays, boundaryAreas) {
9634
10387
  return overlays;
9635
10388
  }
9636
10389
 
10390
+ // src/notices.ts
10391
+ var tileflowNoticesMetadataKey = "tileflow:notices";
10392
+ var tileflowNoticeLimits = Object.freeze({
10393
+ maxNotices: 8,
10394
+ maxSources: 16,
10395
+ maxTextLength: 120
10396
+ });
10397
+
9637
10398
  // src/resolved-map-schema.ts
9638
10399
  import { Color as Color3 } from "@maplibre/maplibre-gl-style-spec";
9639
10400
  import { z as z5 } from "zod";
@@ -10271,12 +11032,12 @@ function dataSource() {
10271
11032
  return Object.freeze({ kind: "tileflow-data-source", name: "primary" });
10272
11033
  }
10273
11034
  function createSemanticDataView(data) {
10274
- const fields = Object.fromEntries(
11035
+ const fields2 = Object.fromEntries(
10275
11036
  Object.entries(data.schema.fields).flatMap(
10276
11037
  ([name, value]) => value === void 0 ? [] : [[name, field(name)]]
10277
11038
  )
10278
11039
  );
10279
- const layers = Object.fromEntries(
11040
+ const layers2 = Object.fromEntries(
10280
11041
  Object.entries(data.schema.layers).flatMap(
10281
11042
  ([name, value]) => value === void 0 ? [] : [[name, dataLayer(name)]]
10282
11043
  )
@@ -10284,7 +11045,7 @@ function createSemanticDataView(data) {
10284
11045
  return {
10285
11046
  ...data,
10286
11047
  sourceId: dataSource(),
10287
- schema: { ...data.schema, fields, layers }
11048
+ schema: { ...data.schema, fields: fields2, layers: layers2 }
10288
11049
  };
10289
11050
  }
10290
11051
  function bindSemanticReferences(value, data) {
@@ -10306,21 +11067,21 @@ function bindSemanticValue(value, schema, sourceId) {
10306
11067
  }
10307
11068
  if (isDataSourceReference(value)) return sourceId;
10308
11069
  if (Array.isArray(value)) return value.map((item) => bindSemanticValue(item, schema, sourceId));
10309
- if (!isRecord4(value)) return value;
11070
+ if (!isRecord5(value)) return value;
10310
11071
  return Object.fromEntries(
10311
11072
  Object.entries(value).map(([key, item]) => [key, bindSemanticValue(item, schema, sourceId)])
10312
11073
  );
10313
11074
  }
10314
11075
  function isDataFieldReference2(value) {
10315
- return isRecord4(value) && value.kind === "tileflow-data-field" && typeof value.name === "string";
11076
+ return isRecord5(value) && value.kind === "tileflow-data-field" && typeof value.name === "string";
10316
11077
  }
10317
11078
  function isDataLayerReference(value) {
10318
- return isRecord4(value) && value.kind === "tileflow-data-layer" && typeof value.name === "string";
11079
+ return isRecord5(value) && value.kind === "tileflow-data-layer" && typeof value.name === "string";
10319
11080
  }
10320
11081
  function isDataSourceReference(value) {
10321
- return isRecord4(value) && value.kind === "tileflow-data-source" && value.name === "primary";
11082
+ return isRecord5(value) && value.kind === "tileflow-data-source" && value.name === "primary";
10322
11083
  }
10323
- function isRecord4(value) {
11084
+ function isRecord5(value) {
10324
11085
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10325
11086
  }
10326
11087
  function compareCodeUnits5(left, right) {
@@ -10424,8 +11185,8 @@ function createTileflowLayerTemplateIR(layer, semanticKey) {
10424
11185
  renderer: type,
10425
11186
  ...filter === void 0 ? {} : { selector: cloneJson20(filter) },
10426
11187
  style: Object.freeze({
10427
- ...isRecord5(layout) ? { placement: Object.freeze(cloneJson20(layout)) } : {},
10428
- ...isRecord5(paint) ? { appearance: Object.freeze(cloneJson20(paint)) } : {}
11188
+ ...isRecord6(layout) ? { placement: Object.freeze(cloneJson20(layout)) } : {},
11189
+ ...isRecord6(paint) ? { appearance: Object.freeze(cloneJson20(paint)) } : {}
10429
11190
  })
10430
11191
  });
10431
11192
  }
@@ -10448,12 +11209,12 @@ function materializeTileflowLayerFamilyIR(template, semantics) {
10448
11209
  });
10449
11210
  }
10450
11211
  function lowerTileflowDomainIR(families, data, resolvePhysicalId = physicalLayerIdForSemanticKey) {
10451
- const layers = families.map(
11212
+ const layers2 = families.map(
10452
11213
  (family) => bindSemanticReferences(emitLayer(family, resolvePhysicalId), data)
10453
11214
  );
10454
- assertNoSemanticReferences(layers);
11215
+ assertNoSemanticReferences(layers2);
10455
11216
  const ids = /* @__PURE__ */ new Set();
10456
- for (const layer of layers) {
11217
+ for (const layer of layers2) {
10457
11218
  if (ids.has(layer.id)) {
10458
11219
  throw new TileflowDomainIRError(
10459
11220
  "TILEFLOW_DOMAIN_IR_DUPLICATE_PHYSICAL_ID",
@@ -10462,7 +11223,7 @@ function lowerTileflowDomainIR(families, data, resolvePhysicalId = physicalLayer
10462
11223
  }
10463
11224
  ids.add(layer.id);
10464
11225
  }
10465
- return Object.freeze({ layers: Object.freeze(layers) });
11226
+ return Object.freeze({ layers: Object.freeze(layers2) });
10466
11227
  }
10467
11228
  var TileflowDomainIRError = class extends Error {
10468
11229
  code;
@@ -10616,7 +11377,11 @@ var exactPhysicalLayerIds = Object.freeze({
10616
11377
  });
10617
11378
  var nauticalPhysicalBases = Object.freeze({
10618
11379
  "nautical.aids": "tileflow-nautical-aids",
11380
+ "nautical.beacons": "tileflow-nautical-beacons",
11381
+ "nautical.buoys": "tileflow-nautical-buoys",
11382
+ "nautical.cables": "tileflow-nautical-cables",
10619
11383
  "nautical.coverage": "tileflow-nautical-coverage",
11384
+ "nautical.harbours": "tileflow-nautical-harbours",
10620
11385
  "nautical.hazardAreas": "tileflow-nautical-hazard-areas",
10621
11386
  "nautical.hazards": "tileflow-nautical-hazards",
10622
11387
  "nautical.labels.coverage": "tileflow-nautical-coverage-labels",
@@ -10627,7 +11392,9 @@ var nauticalPhysicalBases = Object.freeze({
10627
11392
  "nautical.lighthouses": "tileflow-nautical-lighthouses",
10628
11393
  "nautical.lights": "tileflow-nautical-lights",
10629
11394
  "nautical.navigationAreas": "tileflow-nautical-navigation-areas",
11395
+ "nautical.ports": "tileflow-nautical-ports",
10630
11396
  "nautical.reefs": "tileflow-nautical-reefs",
11397
+ "nautical.routes": "tileflow-nautical-routes",
10631
11398
  "nautical.soundings": "tileflow-nautical-soundings",
10632
11399
  "nautical.wreckAreas": "tileflow-nautical-wreck-areas",
10633
11400
  "nautical.wrecks": "tileflow-nautical-wrecks"
@@ -10679,21 +11446,21 @@ function visitReferences(value, counts, visited = /* @__PURE__ */ new WeakSet())
10679
11446
  }
10680
11447
  }
10681
11448
  function isSemanticFieldReference(value) {
10682
- return isRecord5(value) && value.kind === "tileflow-data-field";
11449
+ return isRecord6(value) && value.kind === "tileflow-data-field";
10683
11450
  }
10684
11451
  function isSemanticLayerReference(value) {
10685
- return isRecord5(value) && value.kind === "tileflow-data-layer";
11452
+ return isRecord6(value) && value.kind === "tileflow-data-layer";
10686
11453
  }
10687
11454
  function isSemanticSourceReference(value) {
10688
- return isRecord5(value) && value.kind === "tileflow-data-source";
11455
+ return isRecord6(value) && value.kind === "tileflow-data-source";
10689
11456
  }
10690
11457
  function cloneJson20(value) {
10691
11458
  return JSON.parse(JSON.stringify(value));
10692
11459
  }
10693
11460
  function asRecord4(value) {
10694
- return isRecord5(value) ? value : {};
11461
+ return isRecord6(value) ? value : {};
10695
11462
  }
10696
- function isRecord5(value) {
11463
+ function isRecord6(value) {
10697
11464
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10698
11465
  }
10699
11466
 
@@ -10745,7 +11512,7 @@ function validateTileflowRenderSelectorConstraints(input) {
10745
11512
  };
10746
11513
  while (pending.length > 0) {
10747
11514
  const current = pending.pop();
10748
- if (!isRecord6(current.selector) || typeof current.selector.kind !== "string") {
11515
+ if (!isRecord7(current.selector) || typeof current.selector.kind !== "string") {
10749
11516
  addIssue(
10750
11517
  "invalid-selector",
10751
11518
  "a render selector must be a semantic selector node",
@@ -10806,7 +11573,7 @@ function validateTileflowRenderSelectorConstraints(input) {
10806
11573
  }
10807
11574
  for (const [index, branch] of current.selector.branches.entries()) {
10808
11575
  const branchPath = [...branchesPath, index];
10809
- if (!isRecord6(branch)) {
11576
+ if (!isRecord7(branch)) {
10810
11577
  addIssue("invalid-selector", "match selector branch must be an object", branchPath);
10811
11578
  continue;
10812
11579
  }
@@ -10837,14 +11604,14 @@ function validateTileflowRenderSelectorConstraints(input) {
10837
11604
  for (let index = current.selector.stops.length - 1; index >= 0; index -= 1) {
10838
11605
  const stop = current.selector.stops[index];
10839
11606
  const stopPath = [...stopsPath, index];
10840
- if (!isRecord6(stop)) {
11607
+ if (!isRecord7(stop)) {
10841
11608
  addIssue("invalid-selector", "step selector stop must be an object", stopPath);
10842
11609
  continue;
10843
11610
  }
10844
11611
  pushChild(stop.selector, current.depth + 1, [...stopPath, "selector"]);
10845
11612
  }
10846
11613
  for (const [index, stop] of current.selector.stops.entries()) {
10847
- if (!isRecord6(stop)) continue;
11614
+ if (!isRecord7(stop)) continue;
10848
11615
  const zoom3 = stop.zoom;
10849
11616
  const zoomPath = [...stopsPath, index, "zoom"];
10850
11617
  if (typeof zoom3 !== "number" || !Number.isFinite(zoom3) || zoom3 < 0 || zoom3 > 24) {
@@ -10908,18 +11675,18 @@ function compileRenderSelector(selector, data) {
10908
11675
  assertRenderSelectorConstraints(selector);
10909
11676
  return lowerSelector(selector, data);
10910
11677
  }
10911
- function applyCompiledRenderStacks(input, operations) {
10912
- let layers = input.map(cloneJson21);
11678
+ function applyCompiledRenderStacks(input, operations, targetsOfOtherModes = /* @__PURE__ */ new Map()) {
11679
+ let layers2 = input.map(cloneJson21);
10913
11680
  const passes = operations.filter(
10914
11681
  (operation) => operation.kind === "layer"
10915
11682
  );
10916
11683
  const refinements = operations.filter(
10917
11684
  (operation) => operation.kind === "refinement"
10918
11685
  );
10919
- requireUniqueCompiledLayers(layers, passes);
11686
+ requireUniqueCompiledLayers(layers2, passes);
10920
11687
  const passTargets = new Set(passes.map(({ target }) => target));
10921
- const knownTargets = /* @__PURE__ */ new Set([...layers.map(({ target }) => target), ...passTargets]);
10922
- const activeOwners = new Set(layers.map(({ owner }) => owner));
11688
+ const knownTargets = /* @__PURE__ */ new Set([...layers2.map(({ target }) => target), ...passTargets]);
11689
+ const activeOwners = new Set(layers2.map(({ owner }) => owner));
10923
11690
  const drawn = /* @__PURE__ */ new Set();
10924
11691
  for (let changed = true; changed; ) {
10925
11692
  changed = false;
@@ -10937,11 +11704,12 @@ function applyCompiledRenderStacks(input, operations) {
10937
11704
  (refinement) => refinement.requirements.every((requirement) => activeOwners.has(requirement))
10938
11705
  ).sort(compareCompiledOperations);
10939
11706
  for (const refinement of activeRefinements) {
10940
- const index = findTargetIndex(layers, refinement.target);
11707
+ const index = findTargetIndex(layers2, refinement.target);
10941
11708
  if (index < 0 && !passTargets.has(refinement.target)) {
11709
+ const other = targetsOfOtherModes.get(refinement.target);
10942
11710
  throw renderStackError(
10943
11711
  "unknown-refinement-target",
10944
- `Render refinement targets unknown semantic contribution: ${refinement.target}.`
11712
+ other === void 0 ? `Render refinement targets unknown semantic contribution: ${refinement.target}.` : `Render refinement targets ${refinement.target}, ${describeOtherMode(other)}`
10945
11713
  );
10946
11714
  }
10947
11715
  if (refinement.placement !== void 0 && placementAnchor(refinement.placement) === refinement.target) {
@@ -10963,20 +11731,21 @@ function applyCompiledRenderStacks(input, operations) {
10963
11731
  for (const operation of pending) {
10964
11732
  const anchor = placementAnchor(operation.placement);
10965
11733
  if (anchor !== void 0 && !knownTargets.has(anchor)) {
11734
+ const other = targetsOfOtherModes.get(anchor);
10966
11735
  throw renderStackError(
10967
11736
  "unknown-or-circular-anchor",
10968
- `${operation.target} is placed beside unknown semantic target ${anchor}.`
11737
+ other === void 0 ? `${operation.target} is placed beside unknown semantic target ${anchor}.` : `${operation.target} is placed beside ${anchor}, ${describeOtherMode(other)}`
10969
11738
  );
10970
11739
  }
10971
11740
  }
10972
11741
  const lastPlaced = /* @__PURE__ */ new Map();
10973
11742
  const isMovePending = (target) => pending.some((operation) => operation.kind === "refinement" && operation.target === target);
10974
11743
  const isReady = (operation) => {
10975
- if (operation.kind === "refinement" && findTargetIndex(layers, operation.target) < 0) {
11744
+ if (operation.kind === "refinement" && findTargetIndex(layers2, operation.target) < 0) {
10976
11745
  return false;
10977
11746
  }
10978
11747
  const anchor = placementAnchor(operation.placement);
10979
- return anchor === void 0 || findTargetIndex(layers, anchor) >= 0 && !isMovePending(anchor);
11748
+ return anchor === void 0 || findTargetIndex(layers2, anchor) >= 0 && !isMovePending(anchor);
10980
11749
  };
10981
11750
  while (pending.length > 0) {
10982
11751
  const first = pending.find(isReady);
@@ -10994,34 +11763,34 @@ function applyCompiledRenderStacks(input, operations) {
10994
11763
  const moved = /* @__PURE__ */ new Map();
10995
11764
  for (const operation of group) {
10996
11765
  if (operation.kind !== "refinement") continue;
10997
- const index2 = findTargetIndex(layers, operation.target);
10998
- moved.set(operation.target, requireRefinementOwner(layers[index2], operation));
10999
- layers = [...layers.slice(0, index2), ...layers.slice(index2 + 1)];
11766
+ const index2 = findTargetIndex(layers2, operation.target);
11767
+ moved.set(operation.target, requireRefinementOwner(layers2[index2], operation));
11768
+ layers2 = [...layers2.slice(0, index2), ...layers2.slice(index2 + 1)];
11000
11769
  for (const [placed, target] of lastPlaced) {
11001
11770
  if (target === operation.target) lastPlaced.delete(placed);
11002
11771
  }
11003
11772
  }
11004
11773
  const placement = first.placement;
11005
11774
  const anchor = placementAnchor(placement);
11006
- const anchorLayer = anchor === void 0 ? void 0 : layers[findTargetIndex(layers, anchor)];
11775
+ const anchorLayer = anchor === void 0 ? void 0 : layers2[findTargetIndex(layers2, anchor)];
11007
11776
  const slot = anchorLayer === void 0 ? tileflowBandPlacementSlot(placement) : requireSemanticSlot(anchorLayer, anchor);
11008
11777
  const block = group.map(
11009
11778
  (operation) => operation.kind === "layer" ? materializeRenderLayer(operation, anchorLayer, slot) : moveLayer(moved.get(operation.target), operation, slot, anchorLayer)
11010
11779
  );
11011
11780
  const previous = lastPlaced.get(key);
11012
- const previousIndex = previous === void 0 ? -1 : findTargetIndex(layers, previous);
11013
- const index = anchorLayer !== void 0 ? findTargetIndex(layers, anchor) + (key.startsWith("above:") ? 1 : 0) : previousIndex >= 0 ? previousIndex + 1 : tileflowBandPlacementIndex(
11014
- layers.map((candidate) => tileflowLayerSlots.indexOf(candidate.slot)),
11781
+ const previousIndex = previous === void 0 ? -1 : findTargetIndex(layers2, previous);
11782
+ const index = anchorLayer !== void 0 ? findTargetIndex(layers2, anchor) + (key.startsWith("above:") ? 1 : 0) : previousIndex >= 0 ? previousIndex + 1 : tileflowBandPlacementIndex(
11783
+ layers2.map((candidate) => tileflowLayerSlots.indexOf(candidate.slot)),
11015
11784
  placement
11016
11785
  );
11017
- layers = [...layers.slice(0, index), ...block, ...layers.slice(index)];
11786
+ layers2 = [...layers2.slice(0, index), ...block, ...layers2.slice(index)];
11018
11787
  lastPlaced.set(key, block.at(-1).target);
11019
11788
  }
11020
11789
  for (const refinement of activeRefinements) {
11021
11790
  if (refinement.patch === void 0) continue;
11022
- const index = findTargetIndex(layers, refinement.target);
11791
+ const index = findTargetIndex(layers2, refinement.target);
11023
11792
  if (index < 0) continue;
11024
- const previous = requireRefinementOwner(layers[index], refinement);
11793
+ const previous = requireRefinementOwner(layers2[index], refinement);
11025
11794
  const expectedType = rendererLayerType(refinement.renderer);
11026
11795
  if (previous.renderer !== expectedType) {
11027
11796
  throw renderStackError(
@@ -11029,9 +11798,9 @@ function applyCompiledRenderStacks(input, operations) {
11029
11798
  `Render refinement ${refinement.target} expects ${expectedType}, found ${String(previous.renderer)}.`
11030
11799
  );
11031
11800
  }
11032
- layers[index] = applyRefinement(previous, { ...refinement, patch: refinement.patch });
11801
+ layers2[index] = applyRefinement(previous, { ...refinement, patch: refinement.patch });
11033
11802
  }
11034
- return layers;
11803
+ return layers2;
11035
11804
  }
11036
11805
  function requireRefinementOwner(layer, refinement) {
11037
11806
  if (layer.owner !== refinement.owner) {
@@ -11169,7 +11938,7 @@ function applyRendererStyle(layer, renderer, style) {
11169
11938
  };
11170
11939
  }
11171
11940
  function lowerSelector(selector, data) {
11172
- if (!isRecord6(selector) || typeof selector.kind !== "string") {
11941
+ if (!isRecord7(selector) || typeof selector.kind !== "string") {
11173
11942
  throw renderStackError(
11174
11943
  "invalid-selector",
11175
11944
  "A render selector must be a semantic selector node."
@@ -11372,7 +12141,7 @@ function requireRenderer(value) {
11372
12141
  function requirePlacement(value) {
11373
12142
  if (typeof value === "string") {
11374
12143
  if (tileflowBandPlacements.includes(value)) return;
11375
- } else if (isRecord6(value)) {
12144
+ } else if (isRecord7(value)) {
11376
12145
  const keys = Object.keys(value);
11377
12146
  if (keys.length === 1 && (keys[0] === "above" || keys[0] === "below")) {
11378
12147
  requirePortableTarget(value[keys[0]], "render placement target");
@@ -11399,7 +12168,7 @@ function validateRefinementShape(operation) {
11399
12168
  validateStyle(operation.renderer, operation.style);
11400
12169
  }
11401
12170
  function validateStyle(renderer, style) {
11402
- if (!isRecord6(style)) {
12171
+ if (!isRecord7(style)) {
11403
12172
  throw renderStackError(
11404
12173
  "invalid-style",
11405
12174
  `A ${renderer} render operation requires a style object.`
@@ -11501,7 +12270,7 @@ function normalizeRequirements(requirements) {
11501
12270
  return [...requirements];
11502
12271
  }
11503
12272
  function requireRenderStackEntries(stack) {
11504
- if (!isRecord6(stack)) {
12273
+ if (!isRecord7(stack)) {
11505
12274
  throw renderStackError("invalid-stack", "A render stack must be a named operation record.");
11506
12275
  }
11507
12276
  const entries = Object.entries(stack);
@@ -11550,9 +12319,9 @@ function requireOperationName(name) {
11550
12319
  );
11551
12320
  }
11552
12321
  }
11553
- function requireUniqueCompiledLayers(layers, operations) {
11554
- const keys = new Set(layers.map(({ key }) => key));
11555
- const targets = new Set(layers.map(({ target }) => target));
12322
+ function requireUniqueCompiledLayers(layers2, operations) {
12323
+ const keys = new Set(layers2.map(({ key }) => key));
12324
+ const targets = new Set(layers2.map(({ target }) => target));
11556
12325
  for (const operation of operations) {
11557
12326
  if (keys.has(operation.template.key)) {
11558
12327
  throw renderStackError(
@@ -11612,8 +12381,8 @@ function requireSemanticSlot(layer, target) {
11612
12381
  }
11613
12382
  return layer.slot;
11614
12383
  }
11615
- function findTargetIndex(layers, target) {
11616
- return layers.findIndex((layer) => layer.target === target);
12384
+ function findTargetIndex(layers2, target) {
12385
+ return layers2.findIndex((layer) => layer.target === target);
11617
12386
  }
11618
12387
  function requireScalar(value, description) {
11619
12388
  if (typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string" || typeof value === "number" && !Number.isFinite(value)) {
@@ -11638,6 +12407,9 @@ function assertOnlyKeys(input, allowed, description) {
11638
12407
  );
11639
12408
  }
11640
12409
  }
12410
+ function describeOtherMode({ activeMode, mode, owner }) {
12411
+ return `which ${owner} draws only in mode '${mode}'; this map draws ${owner} in mode '${activeMode}'. Place the operation beside a target drawn in mode '${activeMode}' or at a band edge, or remove it; refine({mode: '${activeMode}', renderStack: reset()}) removes an inherited ${owner} render stack.`;
12412
+ }
11641
12413
  function renderStackError(code, message) {
11642
12414
  return Object.assign(new Error(message), { code, name: "TileflowRenderStackError" });
11643
12415
  }
@@ -11648,9 +12420,9 @@ function cloneJson21(value) {
11648
12420
  return JSON.parse(JSON.stringify(value));
11649
12421
  }
11650
12422
  function asRecord5(value) {
11651
- return isRecord6(value) ? value : {};
12423
+ return isRecord7(value) ? value : {};
11652
12424
  }
11653
- function isRecord6(value) {
12425
+ function isRecord7(value) {
11654
12426
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
11655
12427
  }
11656
12428
 
@@ -12721,6 +13493,29 @@ function boundedThemeNumberSchema(min, max) {
12721
13493
  }).strict()
12722
13494
  ]);
12723
13495
  }
13496
+ var entranceBandSchema = z5.tuple([z5.number().finite().min(0).max(24), z5.number().finite().min(0).max(24)]).refine(([from, to]) => from < to, "A band starts at a lower zoom than it ends.");
13497
+ var modelRoleLightingSchema = z5.object({
13498
+ emissiveStrength: boundedThemeNumberSchema(0, 5).optional(),
13499
+ emissiveVariation: boundedThemeNumberSchema(0, 1).optional(),
13500
+ emissiveColor: themeColorValueSchema.optional(),
13501
+ color: themeColorValueSchema.optional(),
13502
+ colorMix: boundedThemeNumberSchema(0, 1).optional(),
13503
+ metallic: boundedThemeNumberSchema(0, 1).optional(),
13504
+ roughness: boundedThemeNumberSchema(0, 1).optional(),
13505
+ colorVariation: z5.object({
13506
+ hue: z5.tuple([boundedThemeNumberSchema(0, 360), boundedThemeNumberSchema(0, 360)]),
13507
+ saturation: z5.tuple([boundedThemeNumberSchema(0, 100), boundedThemeNumberSchema(0, 100)]),
13508
+ lightness: z5.tuple([boundedThemeNumberSchema(0, 100), boundedThemeNumberSchema(0, 100)]),
13509
+ space: z5.enum(["srgb", "linear"]).optional()
13510
+ }).strict().optional(),
13511
+ emissiveHeight: z5.tuple([
13512
+ boundedThemeNumberSchema(0, 1),
13513
+ boundedThemeNumberSchema(0, 1),
13514
+ boundedThemeNumberSchema(0, 1),
13515
+ boundedThemeNumberSchema(0, 1),
13516
+ boundedThemeNumberSchema(-1, 1)
13517
+ ]).optional()
13518
+ }).strict();
12724
13519
  var buildingsModuleSchema = z5.object({
12725
13520
  type: z5.literal("buildings"),
12726
13521
  businessCorridor: areaStyleSchema.optional(),
@@ -12736,7 +13531,27 @@ var buildingsModuleSchema = z5.object({
12736
13531
  edgeRadius: z5.number().min(0).max(1).optional(),
12737
13532
  edgeColor: themeColorValueSchema.optional(),
12738
13533
  nightLight: boundedThemeNumberSchema(0, 2).optional(),
13534
+ floodLight: z5.object({
13535
+ intensity: boundedThemeNumberSchema(0, 1).optional(),
13536
+ brightnessScale: boundedThemeNumberSchema(0, 16).optional(),
13537
+ color: themeColorValueSchema.optional(),
13538
+ groundRadius: z5.tuple([z5.number().finite().min(0).max(2048), z5.number().finite().min(0).max(2048)]).refine(([min, max]) => min <= max, "The minimum radius must not exceed the maximum.").optional(),
13539
+ minGroundHeight: z5.number().finite().min(0).max(6e3).optional(),
13540
+ minWallHeight: z5.number().finite().min(0).max(6e3).optional(),
13541
+ wallHeightRatio: z5.number().finite().min(0).max(1).optional()
13542
+ }).strict().optional(),
13543
+ modelLighting: z5.object({
13544
+ unclassified: modelRoleLightingSchema.optional(),
13545
+ wall: modelRoleLightingSchema.optional(),
13546
+ door: modelRoleLightingSchema.optional(),
13547
+ roof: modelRoleLightingSchema.optional(),
13548
+ window: modelRoleLightingSchema.optional(),
13549
+ lamp: modelRoleLightingSchema.optional(),
13550
+ logo: modelRoleLightingSchema.optional()
13551
+ }).strict().optional(),
13552
+ entrance: z5.object({ buildings: entranceBandSchema.optional(), models: entranceBandSchema.optional() }).strict().optional(),
12739
13553
  occlusion: z5.boolean().optional(),
13554
+ modelOcclusion: boundedThemeNumberSchema(0, 1).optional(),
12740
13555
  shadows: z5.boolean().optional(),
12741
13556
  windows: z5.boolean().optional(),
12742
13557
  models: z5.array(
@@ -12973,8 +13788,12 @@ var poiModuleSchema = z5.object({
12973
13788
  var nauticalModuleSchema = z5.object({
12974
13789
  type: z5.literal("nautical"),
12975
13790
  aids: markerSymbolStyleSchema.optional(),
13791
+ beacons: markerSymbolStyleSchema.optional(),
13792
+ buoys: markerSymbolStyleSchema.optional(),
13793
+ cables: lineStyleSchema.optional(),
12976
13794
  coverage: areaStyleSchema.optional(),
12977
13795
  enabled: z5.boolean().optional(),
13796
+ harbours: markerSymbolStyleSchema.optional(),
12978
13797
  hazardAreas: areaStyleSchema.optional(),
12979
13798
  hazards: markerSymbolStyleSchema.optional(),
12980
13799
  labels: z5.object({
@@ -12987,8 +13806,10 @@ var nauticalModuleSchema = z5.object({
12987
13806
  lighthouses: markerSymbolStyleSchema.optional(),
12988
13807
  lights: markerSymbolStyleSchema.optional(),
12989
13808
  navigationAreas: areaStyleSchema.optional(),
13809
+ ports: markerSymbolStyleSchema.optional(),
12990
13810
  reefs: areaStyleSchema.optional(),
12991
13811
  renderStack: renderStackSchema.optional(),
13812
+ routes: lineStyleSchema.optional(),
12992
13813
  soundings: markerSymbolStyleSchema.optional(),
12993
13814
  wreckAreas: areaStyleSchema.optional(),
12994
13815
  wrecks: markerSymbolStyleSchema.optional()
@@ -13108,6 +13929,7 @@ var layerBindingsSchema = z5.object({
13108
13929
  streetFurniture: z5.string().trim().min(1).optional(),
13109
13930
  trafficControl: z5.string().trim().min(1).optional(),
13110
13931
  tree: z5.string().trim().min(1).optional(),
13932
+ transportationOutline: z5.string().trim().min(1).optional(),
13111
13933
  water: z5.string().trim().min(1),
13112
13934
  waterName: z5.string().trim().min(1),
13113
13935
  waterway: z5.string().trim().min(1)
@@ -13847,8 +14669,8 @@ function isSafeGlyphUrl(value) {
13847
14669
  function validateZoomRanges(value, context, path = []) {
13848
14670
  if (!value || typeof value !== "object") return;
13849
14671
  if (!Array.isArray(value)) {
13850
- const record3 = value;
13851
- if (typeof record3.minZoom === "number" && typeof record3.maxZoom === "number" && record3.minZoom > record3.maxZoom) {
14672
+ const record4 = value;
14673
+ if (typeof record4.minZoom === "number" && typeof record4.maxZoom === "number" && record4.minZoom > record4.maxZoom) {
13852
14674
  context.addIssue({
13853
14675
  code: "custom",
13854
14676
  path: [...path, "maxZoom"],
@@ -14470,7 +15292,7 @@ function applyTileflowIconCapabilities(style, icons) {
14470
15292
  "line-pattern",
14471
15293
  "background-pattern"
14472
15294
  ]) {
14473
- const pattern = record(layer.paint)[property];
15295
+ const pattern = record2(layer.paint)[property];
14474
15296
  if (pattern !== void 0 && [...outputs(pattern, ids)].some((id2) => appearances[id2]?.representation === "sdf")) {
14475
15297
  throw Object.assign(new Error(`Layer "${layer.id}" cannot use an SDF icon as a pattern.`), {
14476
15298
  code: "TF_ICON_PATTERN_SDF",
@@ -14479,7 +15301,7 @@ function applyTileflowIconCapabilities(style, icons) {
14479
15301
  });
14480
15302
  }
14481
15303
  }
14482
- const image = record(layer.layout)["icon-image"];
15304
+ const image = record2(layer.layout)["icon-image"];
14483
15305
  if (image === void 0) continue;
14484
15306
  if (typeof image === "string" && /\{[^{}]+\}/u.test(image) || typeof image !== "string" && !Array.isArray(image)) {
14485
15307
  throw Object.assign(new Error(`Layer "${layer.id}" has an unproven legacy icon selection.`), {
@@ -14501,7 +15323,7 @@ function applyTileflowIconCapabilities(style, icons) {
14501
15323
  }
14502
15324
  );
14503
15325
  }
14504
- const paint = { ...record(layer.paint) };
15326
+ const paint = { ...record2(layer.paint) };
14505
15327
  for (const [property, limit] of [
14506
15328
  ["icon-halo-width", 2],
14507
15329
  ["icon-halo-blur", 1]
@@ -14531,7 +15353,7 @@ function applyTileflowIconCapabilities(style, icons) {
14531
15353
  layer.paint = paint;
14532
15354
  }
14533
15355
  }
14534
- function record(value) {
15356
+ function record2(value) {
14535
15357
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
14536
15358
  }
14537
15359
  function outputs(value, ids) {
@@ -14620,8 +15442,8 @@ function defaultPaint(image, key, names, appearances, ids) {
14620
15442
  // src/cartography/interaction-manifest.ts
14621
15443
  var tileflowInteractionManifestMetadataKey = "tileflow:interaction-manifest";
14622
15444
  var tileflowInteractionManifestVersion = 3;
14623
- function createTileflowInteractionManifest(layers, fields) {
14624
- const poiLayers = layers.flatMap((layer, priority) => {
15445
+ function createTileflowInteractionManifest(layers2, fields2) {
15446
+ const poiLayers = layers2.flatMap((layer, priority) => {
14625
15447
  const id2 = typeof layer.id === "string" ? layer.id : void 0;
14626
15448
  const source = typeof layer.source === "string" ? layer.source : void 0;
14627
15449
  const sourceLayer = typeof layer["source-layer"] === "string" ? layer["source-layer"] : void 0;
@@ -14650,7 +15472,7 @@ function createTileflowInteractionManifest(layers, fields) {
14650
15472
  identity: ["source", "source-layer", "feature-id"],
14651
15473
  representationPriority: ["marker", "icon", "combined", "label"]
14652
15474
  },
14653
- fields: { ...fields },
15475
+ fields: { ...fields2 },
14654
15476
  hitTesting: { frequency: "animation-frame", order: "rendered-topmost" },
14655
15477
  identity: "maplibre-feature-id-if-present",
14656
15478
  layers: poiLayers
@@ -14659,13 +15481,13 @@ function createTileflowInteractionManifest(layers, fields) {
14659
15481
  version: tileflowInteractionManifestVersion
14660
15482
  };
14661
15483
  }
14662
- function assertTileflowInteractionManifestLayers(manifest, layers) {
15484
+ function assertTileflowInteractionManifestLayers(manifest, layers2) {
14663
15485
  const poiLayers = manifest?.domains.poi?.layers ?? [];
14664
15486
  if (poiLayers.length > 256) {
14665
15487
  throw new Error("Tileflow interaction manifest exceeds the 256-layer POI lookup limit.");
14666
15488
  }
14667
15489
  const layersById = new Map(
14668
- layers.flatMap((layer) => typeof layer.id === "string" ? [[layer.id, layer]] : [])
15490
+ layers2.flatMap((layer) => typeof layer.id === "string" ? [[layer.id, layer]] : [])
14669
15491
  );
14670
15492
  const poiNamespaces = new Set(
14671
15493
  poiLayers.map((layer) => `${layer.source}\0${layer.sourceLayer}`)
@@ -14714,11 +15536,11 @@ function isTileflowPoiCategory(value) {
14714
15536
  return typeof value === "string" && tileflowPoiCategories.includes(value);
14715
15537
  }
14716
15538
  function semanticTarget(layer) {
14717
- if (!isRecord7(layer.metadata)) return void 0;
15539
+ if (!isRecord8(layer.metadata)) return void 0;
14718
15540
  const value = layer.metadata[tileflowCompilerMetadataKeys.target];
14719
15541
  return typeof value === "string" ? value : void 0;
14720
15542
  }
14721
- function isRecord7(value) {
15543
+ function isRecord8(value) {
14722
15544
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
14723
15545
  }
14724
15546
  function isSafeArtifactName(value) {
@@ -14729,9 +15551,9 @@ function isSafeArtifactName(value) {
14729
15551
  }
14730
15552
 
14731
15553
  // src/cartography/layout-zoom-diagnostics.ts
14732
- function diagnoseFractionalLayoutZoomSteps(layers) {
15554
+ function diagnoseFractionalLayoutZoomSteps(layers2) {
14733
15555
  const diagnostics = [];
14734
- for (const layer of layers) {
15556
+ for (const layer of layers2) {
14735
15557
  const layout = layer.layout;
14736
15558
  if (layout === null || typeof layout !== "object") continue;
14737
15559
  for (const [property, value] of Object.entries(layout)) {
@@ -14772,11 +15594,11 @@ function isZoom2(value) {
14772
15594
  // src/cartography/overlay-placement-manifest.ts
14773
15595
  var tileflowOverlayPlacementManifestMetadataKey = "tileflow:overlay-placement-manifest";
14774
15596
  var tileflowOverlayPlacementManifestSchemaVersion = 1;
14775
- function createTileflowOverlayPlacementManifest(layers) {
15597
+ function createTileflowOverlayPlacementManifest(layers2) {
14776
15598
  const anchors = Object.fromEntries(
14777
15599
  tileflowOverlayPlacements.map((placement) => {
14778
- const index = tileflowOverlayInsertionIndex(layers, placement);
14779
- const id2 = index < layers.length ? layers[index]?.id : void 0;
15600
+ const index = tileflowOverlayInsertionIndex(layers2, placement);
15601
+ const id2 = index < layers2.length ? layers2[index]?.id : void 0;
14780
15602
  if (id2 !== void 0 && typeof id2 !== "string") {
14781
15603
  throw new Error(`Tileflow overlay placement ${placement} resolved an invalid layer ID.`);
14782
15604
  }
@@ -14788,7 +15610,7 @@ function createTileflowOverlayPlacementManifest(layers) {
14788
15610
  schemaVersion: tileflowOverlayPlacementManifestSchemaVersion
14789
15611
  };
14790
15612
  }
14791
- function assertTileflowOverlayPlacementManifestLayers(manifest, layers) {
15613
+ function assertTileflowOverlayPlacementManifestLayers(manifest, layers2) {
14792
15614
  if (manifest.schemaVersion !== tileflowOverlayPlacementManifestSchemaVersion) {
14793
15615
  throw new Error("Tileflow overlay placement manifest schema version is unsupported.");
14794
15616
  }
@@ -14797,14 +15619,14 @@ function assertTileflowOverlayPlacementManifestLayers(manifest, layers) {
14797
15619
  throw new Error("Tileflow overlay placement manifest keys are invalid.");
14798
15620
  }
14799
15621
  const layerIndexes = new Map(
14800
- layers.flatMap(
15622
+ layers2.flatMap(
14801
15623
  (layer, index) => typeof layer.id === "string" ? [[layer.id, index]] : []
14802
15624
  )
14803
15625
  );
14804
15626
  let previous = -1;
14805
15627
  for (const placement of tileflowOverlayPlacements) {
14806
15628
  const anchor = manifest.anchors[placement];
14807
- const index = anchor === null ? layers.length : layerIndexes.get(anchor);
15629
+ const index = anchor === null ? layers2.length : layerIndexes.get(anchor);
14808
15630
  if (index === void 0) {
14809
15631
  throw new Error(`Tileflow overlay placement manifest references missing layer: ${anchor}`);
14810
15632
  }
@@ -14836,18 +15658,18 @@ var lineLayoutDefaults = {
14836
15658
  "line-round-limit": 1.05
14837
15659
  };
14838
15660
  function planTileflowLayerFamilies(input) {
14839
- let layers = input.map(cloneJson23);
14840
- layers = consolidateRoadLines(layers);
14841
- layers = consolidateRoadHatches(layers);
14842
- layers = consolidateRoadLabels(layers);
14843
- layers = consolidateFillFamily(layers, "land.landcover", "land.cohorts.landcover");
14844
- layers = consolidateFillFamily(layers, "land.landuse", "land.cohorts.landuse");
14845
- layers = consolidateWaterways(layers);
14846
- return layers;
14847
- }
14848
- function consolidateRoadLines(layers) {
15661
+ let layers2 = input.map(cloneJson23);
15662
+ layers2 = consolidateRoadLines(layers2);
15663
+ layers2 = consolidateRoadHatches(layers2);
15664
+ layers2 = consolidateRoadLabels(layers2);
15665
+ layers2 = consolidateFillFamily(layers2, "land.landcover", "land.cohorts.landcover");
15666
+ layers2 = consolidateFillFamily(layers2, "land.landuse", "land.cohorts.landuse");
15667
+ layers2 = consolidateWaterways(layers2);
15668
+ return layers2;
15669
+ }
15670
+ function consolidateRoadLines(layers2) {
14849
15671
  const groups = /* @__PURE__ */ new Map();
14850
- for (const [index, layer] of layers.entries()) {
15672
+ for (const [index, layer] of layers2.entries()) {
14851
15673
  if (layer.renderer !== "line" || layer.family?.kind !== "road-line") continue;
14852
15674
  const key = layer.family.group;
14853
15675
  const group = groups.get(key) ?? [];
@@ -14862,7 +15684,7 @@ function consolidateRoadLines(layers) {
14862
15684
  continue;
14863
15685
  }
14864
15686
  const mergedId = eligible[0].layer.family.outputKey;
14865
- if (hasGeneratedKeyCollision(layers, mergedId, eligible)) continue;
15687
+ if (hasGeneratedKeyCollision(layers2, mergedId, eligible)) continue;
14866
15688
  const merged = mergeRoadLineGroup(
14867
15689
  mergedId,
14868
15690
  eligible.map(({ layer }) => layer)
@@ -14876,10 +15698,10 @@ function consolidateRoadLines(layers) {
14876
15698
  }
14877
15699
  replacements.set(insertAt, [...replacements.get(insertAt) ?? [], merged]);
14878
15700
  }
14879
- return applyReplacements(layers, replacements);
15701
+ return applyReplacements(layers2, replacements);
14880
15702
  }
14881
- function canMergeRoadLines(layers) {
14882
- const common = layers[0];
15703
+ function canMergeRoadLines(layers2) {
15704
+ const common = layers2[0];
14883
15705
  if (!common) return false;
14884
15706
  const supportedPaint = /* @__PURE__ */ new Set([...Object.keys(linePaintDefaults), "line-pattern"]);
14885
15707
  const supportedLayout = /* @__PURE__ */ new Set([
@@ -14887,18 +15709,20 @@ function canMergeRoadLines(layers) {
14887
15709
  "line-sort-key",
14888
15710
  "visibility"
14889
15711
  ]);
14890
- return layers.every(
15712
+ return layers2.every(
14891
15713
  (layer) => sameLayerSource(layer, common) && Object.keys(layer.style.appearance ?? {}).every((key) => supportedPaint.has(key)) && Object.keys(layer.style.placement ?? {}).every((key) => supportedLayout.has(key)) && layer.style.appearance?.["line-pattern"] === void 0 && layer.style.placement?.visibility !== "none" && !isOrderedRenderPass(layer) && sameTopLevelProperties(layer, common, ["minZoom"])
14892
15714
  );
14893
15715
  }
14894
- function mergeRoadLineGroup(key, layers) {
14895
- const first = layers[0];
14896
- const conditions = layers.map((layer) => asFilter(layer.selector));
15716
+ function mergeRoadLineGroup(key, layers2) {
15717
+ const first = layers2[0];
15718
+ const conditions = layers2.map((layer) => asFilter(layer.selector));
14897
15719
  if (conditions.some(containsZoom)) return void 0;
14898
15720
  const paint = {};
14899
- const paintKeys = new Set(layers.flatMap((layer) => Object.keys(layer.style.appearance ?? {})));
15721
+ const paintKeys = new Set(layers2.flatMap((layer) => Object.keys(layer.style.appearance ?? {})));
14900
15722
  for (const key2 of paintKeys) {
14901
- const values = layers.map((layer) => layer.style.appearance?.[key2]);
15723
+ const values = layers2.map((layer) => layer.style.appearance?.[key2]);
15724
+ if (key2 === "line-dasharray" && values.every((value) => isSolidDash(value ?? linePaintDefaults[key2])))
15725
+ continue;
14902
15726
  const combined = combineConditionalValues(
14903
15727
  values.map((value, index) => ({
14904
15728
  condition: conditions[index],
@@ -14912,7 +15736,7 @@ function mergeRoadLineGroup(key, layers) {
14912
15736
  }
14913
15737
  const layout = {};
14914
15738
  for (const key2 of Object.keys(lineLayoutDefaults)) {
14915
- const values = layers.map((layer, index) => ({
15739
+ const values = layers2.map((layer, index) => ({
14916
15740
  condition: conditions[index],
14917
15741
  value: layer.style.placement?.[key2] ?? lineLayoutDefaults[key2]
14918
15742
  }));
@@ -14925,7 +15749,7 @@ function mergeRoadLineGroup(key, layers) {
14925
15749
  }
14926
15750
  }
14927
15751
  const sortKey = combineConditionalValues(
14928
- layers.map((layer, index) => ({
15752
+ layers2.map((layer, index) => ({
14929
15753
  condition: conditions[index],
14930
15754
  value: rankedSortKey(layer.style.placement?.["line-sort-key"] ?? 0, index)
14931
15755
  })),
@@ -14942,16 +15766,16 @@ function mergeRoadLineGroup(key, layers) {
14942
15766
  selector: unionFilters(conditions),
14943
15767
  range: {
14944
15768
  minZoom: roadHighZoom,
14945
- ...finiteMaximumZoom(layers) === void 0 ? {} : { maxZoom: finiteMaximumZoom(layers) }
15769
+ ...finiteMaximumZoom(layers2) === void 0 ? {} : { maxZoom: finiteMaximumZoom(layers2) }
14946
15770
  },
14947
15771
  style: { appearance: paint, placement: layout }
14948
15772
  },
14949
- layers
15773
+ layers2
14950
15774
  );
14951
15775
  }
14952
- function consolidateRoadHatches(layers) {
15776
+ function consolidateRoadHatches(layers2) {
14953
15777
  const groups = /* @__PURE__ */ new Map();
14954
- for (const [index, layer] of layers.entries()) {
15778
+ for (const [index, layer] of layers2.entries()) {
14955
15779
  if (layer.renderer !== "symbol" && layer.renderer !== "line" || layer.family?.kind !== "road-hatch") {
14956
15780
  continue;
14957
15781
  }
@@ -14971,7 +15795,7 @@ function consolidateRoadHatches(layers) {
14971
15795
  continue;
14972
15796
  }
14973
15797
  const mergedId = first.family.outputKey;
14974
- if (hasGeneratedKeyCollision(layers, mergedId, entries)) continue;
15798
+ if (hasGeneratedKeyCollision(layers2, mergedId, entries)) continue;
14975
15799
  const conditions = entries.map(({ layer }) => asFilter(layer.selector));
14976
15800
  const sortKeyProperty = first.renderer === "line" ? "line-sort-key" : "symbol-sort-key";
14977
15801
  const layoutKeys = new Set(
@@ -15076,17 +15900,17 @@ function consolidateRoadHatches(layers) {
15076
15900
  for (const { index } of entries) replacements.set(index, []);
15077
15901
  replacements.set(entries.at(-1).index, [merged]);
15078
15902
  }
15079
- return applyReplacements(layers, replacements);
15903
+ return applyReplacements(layers2, replacements);
15080
15904
  }
15081
- function consolidateRoadLabels(layers) {
15082
- const candidates = layers.map((layer, index) => ({
15905
+ function consolidateRoadLabels(layers2) {
15906
+ const candidates = layers2.map((layer, index) => ({
15083
15907
  index,
15084
15908
  layer,
15085
15909
  roadClass: layer.family?.kind === "road-label" ? layer.family.member : void 0
15086
15910
  })).filter(
15087
15911
  ({ layer, roadClass }) => layer.renderer === "symbol" && roadClass !== void 0 && layer.feature?.dataLayer !== void 0
15088
15912
  );
15089
- if (candidates.some(({ layer }) => isOrderedRenderPass(layer))) return layers;
15913
+ if (candidates.some(({ layer }) => isOrderedRenderPass(layer))) return layers2;
15090
15914
  const groups = /* @__PURE__ */ new Map();
15091
15915
  for (const candidate of candidates) {
15092
15916
  const { layer } = candidate;
@@ -15108,7 +15932,7 @@ function consolidateRoadLabels(layers) {
15108
15932
  if (!group.every(({ layer }) => sameTopLevelProperties(layer, first))) continue;
15109
15933
  const conditions = group.map(({ layer }) => asFilter(layer.selector));
15110
15934
  const key = group.some(({ roadClass }) => roadClass === "motorway") ? "labels.cohorts.roads.major" : group.some(({ roadClass }) => roadClass === "service") ? "labels.cohorts.roads.local" : `labels.cohorts.roads.cohort${groupNumber++}`;
15111
- if (hasGeneratedKeyCollision(layers, key, group)) continue;
15935
+ if (hasGeneratedKeyCollision(layers2, key, group)) continue;
15112
15936
  const layout = { ...first.style.placement ?? {} };
15113
15937
  const sortKeyValues = group.map(({ layer }, index) => {
15114
15938
  const original = layer.style.placement?.["symbol-sort-key"] ?? 0;
@@ -15133,13 +15957,13 @@ function consolidateRoadLabels(layers) {
15133
15957
  for (const { index } of group) replacements.set(index, []);
15134
15958
  replacements.set(group.at(-1).index, [merged]);
15135
15959
  }
15136
- return applyReplacements(layers, replacements);
15960
+ return applyReplacements(layers2, replacements);
15137
15961
  }
15138
- function consolidateFillFamily(layers, group, mergedId) {
15139
- const candidates = layers.map((layer, index) => ({ index, layer })).filter(
15962
+ function consolidateFillFamily(layers2, group, mergedId) {
15963
+ const candidates = layers2.map((layer, index) => ({ index, layer })).filter(
15140
15964
  ({ layer }) => layer.renderer === "fill" && layer.family?.kind === "fill" && layer.family.group === group
15141
15965
  );
15142
- if (candidates.some(({ layer }) => isOrderedRenderPass(layer))) return layers;
15966
+ if (candidates.some(({ layer }) => isOrderedRenderPass(layer))) return layers2;
15143
15967
  const groups = /* @__PURE__ */ new Map();
15144
15968
  for (const candidate of candidates) {
15145
15969
  const { layer } = candidate;
@@ -15211,7 +16035,7 @@ function consolidateFillFamily(layers, group, mergedId) {
15211
16035
  if (!sortKey.ok) continue;
15212
16036
  layout["fill-sort-key"] = sortKey.value;
15213
16037
  const key = suffix === 0 ? mergedId : `${mergedId}.cohort${suffix}`;
15214
- if (hasGeneratedKeyCollision(layers, key, group2)) continue;
16038
+ if (hasGeneratedKeyCollision(layers2, key, group2)) continue;
15215
16039
  const merged = withMergedOrigins(
15216
16040
  {
15217
16041
  ...first,
@@ -15226,20 +16050,20 @@ function consolidateFillFamily(layers, group, mergedId) {
15226
16050
  replacements.set(group2.at(-1).index, [merged]);
15227
16051
  }
15228
16052
  }
15229
- return applyReplacements(layers, replacements);
16053
+ return applyReplacements(layers2, replacements);
15230
16054
  }
15231
- function consolidateWaterways(layers) {
16055
+ function consolidateWaterways(layers2) {
15232
16056
  const replacements = /* @__PURE__ */ new Map();
15233
16057
  for (const name of ["river", "canal", "stream", "other"]) {
15234
- const regularIndex = layers.findIndex(
16058
+ const regularIndex = layers2.findIndex(
15235
16059
  (layer) => layer.family?.kind === "waterway" && layer.family.group === name && layer.family.variant === "regular"
15236
16060
  );
15237
- const intermittentIndex = layers.findIndex(
16061
+ const intermittentIndex = layers2.findIndex(
15238
16062
  (layer) => layer.family?.kind === "waterway" && layer.family.group === name && layer.family.variant === "intermittent"
15239
16063
  );
15240
16064
  if (regularIndex < 0 || intermittentIndex < 0) continue;
15241
- const regular = layers[regularIndex];
15242
- const intermittent = layers[intermittentIndex];
16065
+ const regular = layers2[regularIndex];
16066
+ const intermittent = layers2[intermittentIndex];
15243
16067
  if (!sameLayerSource(regular, intermittent) || isOrderedRenderPass(regular) || isOrderedRenderPass(intermittent) || intermittentIndex !== regularIndex + 1 || minimumZoom(regular) !== minimumZoom(intermittent) || maximumZoom(regular) !== maximumZoom(intermittent) || !allEqual([regular.style.placement ?? {}, intermittent.style.placement ?? {}]) || !sameTopLevelProperties(regular, intermittent)) {
15244
16068
  continue;
15245
16069
  }
@@ -15298,7 +16122,7 @@ function consolidateWaterways(layers) {
15298
16122
  replacements.set(regularIndex, []);
15299
16123
  replacements.set(intermittentIndex, [merged]);
15300
16124
  }
15301
- return applyReplacements(layers, replacements);
16125
+ return applyReplacements(layers2, replacements);
15302
16126
  }
15303
16127
  function combineConditionalValues(entries, fallback, minimumRelevantZoom) {
15304
16128
  if (entries.length === 0) return { ok: true, value: fallback };
@@ -15533,8 +16357,8 @@ function unionFilters(filters) {
15533
16357
  function asFilter(value) {
15534
16358
  return Array.isArray(value) ? value : ["literal", true];
15535
16359
  }
15536
- function applyReplacements(layers, replacements) {
15537
- return layers.flatMap((layer, index) => replacements.get(index) ?? [layer]);
16360
+ function applyReplacements(layers2, replacements) {
16361
+ return layers2.flatMap((layer, index) => replacements.get(index) ?? [layer]);
15538
16362
  }
15539
16363
  function sameLayerSource(left, right) {
15540
16364
  return allEqual([left.feature, right.feature]);
@@ -15572,9 +16396,9 @@ function contiguousRuns(entries) {
15572
16396
  }
15573
16397
  return runs;
15574
16398
  }
15575
- function hasGeneratedKeyCollision(layers, key, entries) {
16399
+ function hasGeneratedKeyCollision(layers2, key, entries) {
15576
16400
  const members = new Set(entries.map(({ layer }) => layer));
15577
- return layers.some((layer) => layer.key === key && !members.has(layer));
16401
+ return layers2.some((layer) => layer.key === key && !members.has(layer));
15578
16402
  }
15579
16403
  function withMaximumZoom(layer, maxzoom) {
15580
16404
  return {
@@ -15588,8 +16412,8 @@ function minimumZoom(layer) {
15588
16412
  function maximumZoom(layer) {
15589
16413
  return typeof layer.range?.maxZoom === "number" ? layer.range.maxZoom : Infinity;
15590
16414
  }
15591
- function finiteMaximumZoom(layers) {
15592
- const maximum2 = Math.max(...layers.map(maximumZoom));
16415
+ function finiteMaximumZoom(layers2) {
16416
+ const maximum2 = Math.max(...layers2.map(maximumZoom));
15593
16417
  return Number.isFinite(maximum2) ? maximum2 : void 0;
15594
16418
  }
15595
16419
  function cloneJson23(value) {
@@ -15613,18 +16437,18 @@ var colourSources = [
15613
16437
  ["tileflow-buildings-3d", "fill-extrusion-color"],
15614
16438
  ["tileflow-buildings-fill", "fill-color"]
15615
16439
  ];
15616
- function tileflowBuildings3DDesignOf(module, context, layers, buildingModels, tileflowLandmarks) {
15617
- const config = isRecord8(module) ? module : {};
16440
+ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, tileflowLandmarks) {
16441
+ const config = isRecord9(module) ? module : {};
15618
16442
  if (config.enabled === false) return void 0;
15619
16443
  const style = config.threeDimensional ?? {};
15620
16444
  const defaults4 = tileflowDefaultBuildings3DDesign;
15621
16445
  let color2;
15622
16446
  let verticalGradient = true;
15623
16447
  for (const [id2, property] of colourSources) {
15624
- const value = layers.find((layer) => layer.id === id2)?.paint?.[property];
16448
+ const value = layers2.find((layer) => layer.id === id2)?.paint?.[property];
15625
16449
  if (typeof value === "string" || Array.isArray(value)) {
15626
16450
  color2 = value;
15627
- verticalGradient = layers.find((layer) => layer.id === id2)?.paint?.["fill-extrusion-vertical-gradient"] !== false;
16451
+ verticalGradient = layers2.find((layer) => layer.id === id2)?.paint?.["fill-extrusion-vertical-gradient"] !== false;
15628
16452
  break;
15629
16453
  }
15630
16454
  }
@@ -15647,9 +16471,54 @@ function tileflowBuildings3DDesignOf(module, context, layers, buildingModels, ti
15647
16471
  edgeRadius: number(style.edgeRadius, defaults4.edgeRadius),
15648
16472
  ...typeof edgeColor === "string" ? { edgeColor } : {},
15649
16473
  nightLight: number(style.nightLight, 0),
16474
+ ...isRecord9(style.floodLight) ? {
16475
+ floodLight: readBuildingFloodLight({
16476
+ ...style.floodLight,
16477
+ intensity: number(style.floodLight.intensity, 0),
16478
+ brightnessScale: number(style.floodLight.brightnessScale, 1),
16479
+ color: style.floodLight.color === void 0 ? void 0 : toMapLibreStyleValue(style.floodLight.color)
16480
+ })
16481
+ } : {},
16482
+ ...isRecord9(style.modelLighting) ? {
16483
+ modelLighting: readModelRoleLighting(
16484
+ Object.fromEntries(
16485
+ Object.entries(style.modelLighting).filter(([, entry]) => isRecord9(entry)).map(([role, entry]) => {
16486
+ const own = entry;
16487
+ return [
16488
+ role,
16489
+ {
16490
+ ...own,
16491
+ emissiveStrength: number(own.emissiveStrength, 0),
16492
+ ...Object.fromEntries(
16493
+ ["emissiveVariation", "colorMix", "metallic", "roughness"].filter((key) => own[key] !== void 0).map((key) => [key, number(own[key], 0)])
16494
+ ),
16495
+ ...Array.isArray(own.emissiveHeight) ? { emissiveHeight: own.emissiveHeight.map((v) => number(v, 0)) } : {},
16496
+ ...isRecord9(own.colorVariation) ? {
16497
+ colorVariation: {
16498
+ ...own.colorVariation,
16499
+ ...Object.fromEntries(
16500
+ ["hue", "saturation", "lightness"].map((key) => [
16501
+ key,
16502
+ Array.isArray(
16503
+ own.colorVariation[key]
16504
+ ) ? own.colorVariation[key].map((v) => number(v, 0)) : []
16505
+ ])
16506
+ )
16507
+ }
16508
+ } : {},
16509
+ color: own.color === void 0 ? void 0 : toMapLibreStyleValue(own.color),
16510
+ emissiveColor: own.emissiveColor === void 0 ? void 0 : toMapLibreStyleValue(own.emissiveColor)
16511
+ }
16512
+ ];
16513
+ })
16514
+ )
16515
+ )
16516
+ } : {},
15650
16517
  windows: style.windows === true,
16518
+ ...readTileflowBuildingsEntrance(style.entrance),
15651
16519
  shadows: typeof style.shadows === "boolean" ? style.shadows : defaults4.shadows,
15652
16520
  occlusion: typeof style.occlusion === "boolean" ? style.occlusion : defaults4.occlusion,
16521
+ modelOcclusion: number(style.modelOcclusion, defaults4.modelOcclusion),
15653
16522
  ...placedModels(style.models, buildingModels),
15654
16523
  ...modelColours(style.modelColors),
15655
16524
  ...landmarksOf(style.landmarks, tileflowLandmarks)
@@ -15657,7 +16526,7 @@ function tileflowBuildings3DDesignOf(module, context, layers, buildingModels, ti
15657
16526
  }
15658
16527
  function landmarksOf(value, tileflowLandmarks) {
15659
16528
  if (value === false) return {};
15660
- if (isRecord8(value) && typeof value.url === "string") return { landmarks: { url: value.url } };
16529
+ if (isRecord9(value) && typeof value.url === "string") return { landmarks: { url: value.url } };
15661
16530
  return tileflowLandmarks ? { landmarks: { url: tileflowLandmarks } } : {};
15662
16531
  }
15663
16532
  function placedModels(value, served) {
@@ -15684,7 +16553,7 @@ function placedModels(value, served) {
15684
16553
  };
15685
16554
  }
15686
16555
  function modelColours(value) {
15687
- if (!isRecord8(value)) return {};
16556
+ if (!isRecord9(value)) return {};
15688
16557
  const colours = {};
15689
16558
  for (const [part, colour] of Object.entries(value)) {
15690
16559
  const resolved = colour === void 0 ? void 0 : toMapLibreStyleValue(colour);
@@ -15693,10 +16562,10 @@ function modelColours(value) {
15693
16562
  return Object.keys(colours).length > 0 ? { modelColors: colours } : {};
15694
16563
  }
15695
16564
  function tileflowStreetLightsDesignOf(module, context) {
15696
- const config = isRecord8(module) ? module : {};
15697
- const layers = context.data.schema.layers;
16565
+ const config = isRecord9(module) ? module : {};
16566
+ const layers2 = context.data.schema.layers;
15698
16567
  const style = config.streetLights ?? {};
15699
- if (config.enabled === false || style.enabled !== true || !layers.streetFurniture)
16568
+ if (config.enabled === false || style.enabled !== true || !layers2.streetFurniture)
15700
16569
  return void 0;
15701
16570
  const color2 = style.color === void 0 ? void 0 : toMapLibreStyleValue(style.color);
15702
16571
  const intensity = style.intensity === void 0 ? void 0 : toMapLibreStyleValue(style.intensity);
@@ -15704,16 +16573,16 @@ function tileflowStreetLightsDesignOf(module, context) {
15704
16573
  return {
15705
16574
  source: context.data.sourceId,
15706
16575
  layers: {
15707
- streetFurniture: layers.streetFurniture,
15708
- transportation: layers.road,
15709
- ...layers.sidewalk ? { sidewalk: layers.sidewalk } : {}
16576
+ streetFurniture: layers2.streetFurniture,
16577
+ transportation: layers2.road,
16578
+ ...layers2.sidewalk ? { sidewalk: layers2.sidewalk } : {}
15710
16579
  },
15711
16580
  estimate: estimate === "streets" || estimate === "none" || estimate === "lit" ? estimate : "lit",
15712
16581
  color: typeof color2 === "string" ? color2 : tileflowDefaultStreetLightsDesign.color,
15713
16582
  intensity: typeof intensity === "number" ? intensity : 1
15714
16583
  };
15715
16584
  }
15716
- function isRecord8(value) {
16585
+ function isRecord9(value) {
15717
16586
  return typeof value === "object" && value !== null && !Array.isArray(value);
15718
16587
  }
15719
16588
 
@@ -15885,7 +16754,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
15885
16754
  )
15886
16755
  );
15887
16756
  const renderStackModules = Object.values(modules).filter(
15888
- (module) => isRecord9(module) && module.enabled !== false && "renderStack" in module && isRecord9(module.renderStack)
16757
+ (module) => isRecord10(module) && module.enabled !== false && "renderStack" in module && isRecord10(module.renderStack)
15889
16758
  );
15890
16759
  const renderOperations = runCompilationPhase(
15891
16760
  "render-stack",
@@ -15911,7 +16780,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
15911
16780
  const requestedRenderOperations = domainIR.renderOperations;
15912
16781
  const stackedFamilies = runCompilationPhase(
15913
16782
  "render-stack",
15914
- () => applyCompiledRenderStacks(assembledFamilies, requestedRenderOperations)
16783
+ () => applyCompiledRenderStacks(
16784
+ assembledFamilies,
16785
+ requestedRenderOperations,
16786
+ buildingTargetsOfOtherModes(modules.buildings)
16787
+ )
15915
16788
  );
15916
16789
  const plannedFamilies = runCompilationPhase(
15917
16790
  "physical-planner",
@@ -16047,11 +16920,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16047
16920
  "finalization",
16048
16921
  () => createTileflowOverlayPlacementManifest(plannedLayers)
16049
16922
  );
16050
- const layers = runCompilationPhase("finalization", () => finalizeTileflowLayers(plannedLayers));
16923
+ const layers2 = runCompilationPhase("finalization", () => finalizeTileflowLayers(plannedLayers));
16051
16924
  const buildings3D = tileflowBuildings3DDesignOf(
16052
16925
  modules.buildings,
16053
16926
  context,
16054
- layers,
16927
+ layers2,
16055
16928
  options.preparedAssets?.buildingModels,
16056
16929
  data.kind === "tileflow-world" ? `${apiBaseUrl}/landmarks/tiles.json` : void 0
16057
16930
  );
@@ -16061,11 +16934,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16061
16934
  );
16062
16935
  runCompilationPhase(
16063
16936
  "finalization",
16064
- () => assertTileflowInteractionManifestLayers(interactionManifest, layers)
16937
+ () => assertTileflowInteractionManifestLayers(interactionManifest, layers2)
16065
16938
  );
16066
16939
  runCompilationPhase(
16067
16940
  "finalization",
16068
- () => assertTileflowOverlayPlacementManifestLayers(overlayPlacementManifest, layers)
16941
+ () => assertTileflowOverlayPlacementManifestLayers(overlayPlacementManifest, layers2)
16069
16942
  );
16070
16943
  const glyphs = runCompilationPhase("assets", () => resolveGlyphs(config));
16071
16944
  const sprite = options.preparedAssets?.icons?.sprite;
@@ -16105,10 +16978,10 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16105
16978
  version: 8,
16106
16979
  name: config.name ?? "Streets",
16107
16980
  sources,
16108
- layers
16981
+ layers: layers2
16109
16982
  })
16110
16983
  );
16111
- const flatVegetationLayers = layers.filter((layer) => {
16984
+ const flatVegetationLayers = layers2.filter((layer) => {
16112
16985
  const metadata = layer.metadata;
16113
16986
  return metadata?.["tileflow:vegetation-mode"] !== void 0 || metadata?.["tileflow:domain"] === "vegetation";
16114
16987
  });
@@ -16127,6 +17000,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16127
17000
  layer.layout = { ...layer.layout, visibility: "none" };
16128
17001
  }
16129
17002
  const atmosphere = resolveTileflowAtmosphere(config.atmosphere);
17003
+ const notices = compiledNotices(layers2, marine);
16130
17004
  const style = {
16131
17005
  ...atmosphere ? { sky: compileTileflowSky(atmosphere) } : {},
16132
17006
  version: 8,
@@ -16136,7 +17010,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16136
17010
  ...config.projection ? { projection: { type: config.projection } } : {},
16137
17011
  ...sprite ? { sprite } : {},
16138
17012
  sources,
16139
- layers,
17013
+ layers: layers2,
16140
17014
  ...terrain?.mode === "3d" && terrain.raster ? { terrain: { exaggeration: terrain.exaggeration, source: terrain.raster.sourceId } } : {},
16141
17015
  metadata: {
16142
17016
  ...mapMetadata ? {
@@ -16175,8 +17049,9 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16175
17049
  "tileflow:sourceRequirements": sourceRequirements,
16176
17050
  ...interactionManifest ? { [tileflowInteractionManifestMetadataKey]: interactionManifest } : {},
16177
17051
  [tileflowOverlayPlacementManifestMetadataKey]: overlayPlacementManifest,
16178
- "tileflow:modules": Object.entries(modules).filter(([, module]) => !isRecord9(module) || module.enabled !== false).map(([name]) => name).sort(),
16179
- ...config.view ? { "tileflow:view": config.view } : {}
17052
+ "tileflow:modules": Object.entries(modules).filter(([, module]) => !isRecord10(module) || module.enabled !== false).map(([name]) => name).sort(),
17053
+ ...config.view ? { "tileflow:view": config.view } : {},
17054
+ ...notices.length > 0 ? { [tileflowNoticesMetadataKey]: notices } : {}
16180
17055
  }
16181
17056
  };
16182
17057
  const report = {
@@ -16236,7 +17111,7 @@ function finalizeTileflowLayers(input) {
16236
17111
  if (typeof minimum2 === "number" && typeof maximum2 === "number" && minimum2 > maximum2) {
16237
17112
  throw new Error(`Compiled Tileflow layer ${id2} requires minzoom <= maxzoom.`);
16238
17113
  }
16239
- const metadata = isRecord9(layer.metadata) ? { ...layer.metadata } : void 0;
17114
+ const metadata = isRecord10(layer.metadata) ? { ...layer.metadata } : void 0;
16240
17115
  if (metadata) {
16241
17116
  const owner = metadata[tileflowCompilerMetadataKeys.owner];
16242
17117
  delete metadata[tileflowCompilerProvenanceMetadataKey];
@@ -16270,9 +17145,14 @@ function assertMapLibreStyle(style, mapId) {
16270
17145
  }
16271
17146
  );
16272
17147
  }
16273
- function isRecord9(value) {
17148
+ function isRecord10(value) {
16274
17149
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16275
17150
  }
17151
+ function compiledNotices(layers2, marine) {
17152
+ const nautical2 = marine?.nautical?.sourceId;
17153
+ if (!nautical2 || !layers2.some((layer) => layer.source === nautical2)) return [];
17154
+ return [{ ...tileflowNauticalNotice, sources: [nautical2] }];
17155
+ }
16276
17156
  function resolveGlyphs(config) {
16277
17157
  if (config.fonts !== void 0) return void 0;
16278
17158
  return config.glyphs?.url;
@@ -16310,8 +17190,8 @@ function assertPreparedIconReferences(style, config, prepared) {
16310
17190
  function collectStyleImageReferences(style) {
16311
17191
  const result = /* @__PURE__ */ new Set();
16312
17192
  for (const layer of style.layers) {
16313
- const layout = isRecord9(layer.layout) ? layer.layout : {};
16314
- const paint = isRecord9(layer.paint) ? layer.paint : {};
17193
+ const layout = isRecord10(layer.layout) ? layer.layout : {};
17194
+ const paint = isRecord10(layer.paint) ? layer.paint : {};
16315
17195
  const layerId = typeof layer.id === "string" ? layer.id : "<unknown>";
16316
17196
  for (const [property, value] of [
16317
17197
  ["icon-image", layout["icon-image"]],
@@ -16400,7 +17280,7 @@ function assertGlyphFontStacks(style, config) {
16400
17280
  const declared = new Set(config.glyphs.fontStacks);
16401
17281
  const missing = /* @__PURE__ */ new Set();
16402
17282
  for (const layer of style.layers) {
16403
- const layout = isRecord9(layer.layout) ? layer.layout : void 0;
17283
+ const layout = isRecord10(layer.layout) ? layer.layout : void 0;
16404
17284
  const font = layout?.["text-font"];
16405
17285
  if (!Array.isArray(font) || !font.every((entry) => typeof entry === "string")) continue;
16406
17286
  const stack = font.join(",");
@@ -16416,7 +17296,7 @@ function assertGlyphFontStacks(style, config) {
16416
17296
  function assertTextAssets(style, config) {
16417
17297
  const textLayers = [];
16418
17298
  for (const layer of style.layers) {
16419
- const layout = isRecord9(layer.layout) ? layer.layout : void 0;
17299
+ const layout = isRecord10(layer.layout) ? layer.layout : void 0;
16420
17300
  if (layout?.["text-field"] === void 0) continue;
16421
17301
  textLayers.push(typeof layer.id === "string" ? layer.id : "<unknown>");
16422
17302
  const font = layout["text-font"];
@@ -16429,7 +17309,7 @@ function assertTextAssets(style, config) {
16429
17309
  if (textLayers.length === 0) return;
16430
17310
  if (config.fonts !== void 0) {
16431
17311
  const backed = style.layers.find((layer) => {
16432
- const font = isRecord9(layer.layout) ? layer.layout["text-font"] : void 0;
17312
+ const font = isRecord10(layer.layout) ? layer.layout["text-font"] : void 0;
16433
17313
  return Array.isArray(font) && font.length === 1 && typeof font[0] === "string" && parseTileflowLineBackgroundFontStack(font[0]) !== void 0;
16434
17314
  });
16435
17315
  if (backed) {
@@ -16455,8 +17335,8 @@ function runCompilationPhase(phase, operation) {
16455
17335
  }
16456
17336
  }
16457
17337
  function withCompilationPhase(error, phase) {
16458
- const record3 = isRecord9(error) ? error : {};
16459
- if (typeof record3.phase === "string") return error;
17338
+ const record4 = isRecord10(error) ? error : {};
17339
+ if (typeof record4.phase === "string") return error;
16460
17340
  if (error instanceof Error && Object.isExtensible(error)) {
16461
17341
  Object.defineProperty(error, "phase", { configurable: true, enumerable: true, value: phase });
16462
17342
  return error;
@@ -16472,7 +17352,7 @@ function withCompilationPhase(error, phase) {
16472
17352
  "severity",
16473
17353
  "target"
16474
17354
  ]) {
16475
- if (record3[key] !== void 0) Object.assign(wrapped, { [key]: record3[key] });
17355
+ if (record4[key] !== void 0) Object.assign(wrapped, { [key]: record4[key] });
16476
17356
  }
16477
17357
  Object.assign(wrapped, { phase });
16478
17358
  return wrapped;
@@ -17206,6 +18086,9 @@ function applyModuleOperation(resolved, moduleName, operation, mapId, path) {
17206
18086
  assertNoReset(patch, mapId, `${path}/patches/${index}`, true);
17207
18087
  next = applyModulePatch(next, patch, mapId, `${path}/patches/${index}`);
17208
18088
  }
18089
+ if (isPlainRecord4(next.renderStack) && Object.keys(next.renderStack).length === 0) {
18090
+ delete next.renderStack;
18091
+ }
17209
18092
  assertModuleOwner(next, moduleName, mapId, path);
17210
18093
  resolved[moduleName] = next;
17211
18094
  return;
@@ -17368,11 +18251,11 @@ function cloneDesign(value) {
17368
18251
  function describeMap(value) {
17369
18252
  return typeof value.id === "string" && value.id ? value.id : "<anonymous>";
17370
18253
  }
17371
- function isRecord10(value) {
18254
+ function isRecord11(value) {
17372
18255
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
17373
18256
  }
17374
18257
  function isPlainRecord4(value) {
17375
- if (!isRecord10(value)) return false;
18258
+ if (!isRecord11(value)) return false;
17376
18259
  const prototype = Object.getPrototypeOf(value);
17377
18260
  return prototype === Object.prototype || prototype === null;
17378
18261
  }
@@ -18251,9 +19134,9 @@ var tileflowNativeBuildRecordSchema = strictNativeObject({
18251
19134
  engines: strictNativeObject({ android: z13.literal("13.2.0"), ios: z13.literal("6.26.0") }),
18252
19135
  buildManifestSha256: hashSchema,
18253
19136
  transformations: z13.array(tileflowNativeStyleTransformationSchema).max(4096)
18254
- }).superRefine((record3, context) => {
19137
+ }).superRefine((record4, context) => {
18255
19138
  let previous = "";
18256
- for (const [index, item] of record3.transformations.entries()) {
19139
+ for (const [index, item] of record4.transformations.entries()) {
18257
19140
  const name = `${item.map}/${item.theme}`;
18258
19141
  if (name <= previous)
18259
19142
  context.addIssue({
@@ -26956,13 +27839,13 @@ function validateNativeStyle(input, options, stage) {
26956
27839
  issues.push(createTileflowNativeDiagnostic(code, path));
26957
27840
  }
26958
27841
  };
26959
- if (!record2(options) || !plainOptions(options)) {
27842
+ if (!record3(options) || !plainOptions(options)) {
26960
27843
  return [createTileflowNativeDiagnostic("NATIVE_RENDERER_UNSUPPORTED", "/profile")];
26961
27844
  }
26962
27845
  if (options.profile !== void 0 && options.profile !== "native-v1" || !expressionDefinitions) {
26963
27846
  return [createTileflowNativeDiagnostic("NATIVE_RENDERER_UNSUPPORTED", "/profile")];
26964
27847
  }
26965
- if (!isBoundedNativeJson(input, stage) || !record2(input)) {
27848
+ if (!isBoundedNativeJson(input, stage) || !record3(input)) {
26966
27849
  return [createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE")];
26967
27850
  }
26968
27851
  const style = input;
@@ -26976,11 +27859,11 @@ function validateNativeStyle(input, options, stage) {
26976
27859
  }
26977
27860
  if (style.projection !== void 0) {
26978
27861
  const projection = style.projection;
26979
- if (!record2(projection) || projection.type !== "mercator" || Object.keys(projection).length !== 1) {
27862
+ if (!record3(projection) || projection.type !== "mercator" || Object.keys(projection).length !== 1) {
26980
27863
  add2("NATIVE_UNSUPPORTED_STYLE", "/projection");
26981
27864
  }
26982
27865
  }
26983
- if (record2(style.metadata)) {
27866
+ if (record3(style.metadata)) {
26984
27867
  for (const key of Object.keys(style.metadata).sort()) {
26985
27868
  if (/^tileflow:(?:contour|pmtiles)/iu.test(key)) {
26986
27869
  add2("NATIVE_UNSUPPORTED_SOURCE", nativePointer("/metadata", key));
@@ -27023,14 +27906,14 @@ function validateNativeStyle(input, options, stage) {
27023
27906
  add2("NATIVE_FONT_UNAVAILABLE", "/glyphs");
27024
27907
  } else checkUrl(style.glyphs, "/glyphs", "glyphs");
27025
27908
  }
27026
- const sources = record2(style.sources) ? style.sources : {};
27909
+ const sources = record3(style.sources) ? style.sources : {};
27027
27910
  if (Object.keys(sources).length > tileflowNativeProfileLimits.maximumSources) {
27028
27911
  add2("NATIVE_UNSUPPORTED_SOURCE", "/sources");
27029
27912
  }
27030
27913
  for (const id2 of Object.keys(sources).sort().slice(0, tileflowNativeProfileLimits.maximumSources)) {
27031
27914
  const source = sources[id2];
27032
27915
  const path = nativePointer("/sources", id2);
27033
- if (!record2(source) || typeof source.type !== "string" || !sourceKinds.has(source.type)) {
27916
+ if (!record3(source) || typeof source.type !== "string" || !sourceKinds.has(source.type)) {
27034
27917
  add2("NATIVE_UNSUPPORTED_SOURCE", path);
27035
27918
  continue;
27036
27919
  }
@@ -27042,7 +27925,7 @@ function validateNativeStyle(input, options, stage) {
27042
27925
  "NATIVE_UNSUPPORTED_SOURCE"
27043
27926
  );
27044
27927
  if (source.clusterProperties !== void 0) {
27045
- if (record2(source.clusterProperties)) {
27928
+ if (record3(source.clusterProperties)) {
27046
27929
  for (const key of Object.keys(source.clusterProperties).sort()) {
27047
27930
  const values = source.clusterProperties[key];
27048
27931
  if (Array.isArray(values))
@@ -27068,7 +27951,7 @@ function validateNativeStyle(input, options, stage) {
27068
27951
  if (faces !== void 0) {
27069
27952
  const support = spec.$root?.["font-faces"]?.["sdk-support"]?.["basic functionality"];
27070
27953
  if (!support || !supportsBoth(support)) add2("NATIVE_FONT_UNAVAILABLE", "/font-faces");
27071
- if (!record2(faces) || Object.keys(faces).length > tileflowNativeProfileLimits.maximumFontFaces) {
27954
+ if (!record3(faces) || Object.keys(faces).length > tileflowNativeProfileLimits.maximumFontFaces) {
27072
27955
  add2("NATIVE_FONT_UNAVAILABLE", "/font-faces");
27073
27956
  } else {
27074
27957
  for (const name of Object.keys(faces).sort()) {
@@ -27080,19 +27963,19 @@ function validateNativeStyle(input, options, stage) {
27080
27963
  }
27081
27964
  }
27082
27965
  }
27083
- const layers = Array.isArray(style.layers) ? style.layers : [];
27084
- if (layers.length > tileflowNativeProfileLimits.maximumLayers)
27966
+ const layers2 = Array.isArray(style.layers) ? style.layers : [];
27967
+ if (layers2.length > tileflowNativeProfileLimits.maximumLayers)
27085
27968
  add2("NATIVE_UNSUPPORTED_STYLE", "/layers");
27086
- layers.slice(0, tileflowNativeProfileLimits.maximumLayers).forEach((layer, i) => {
27969
+ layers2.slice(0, tileflowNativeProfileLimits.maximumLayers).forEach((layer, i) => {
27087
27970
  const path = `/layers/${i}`;
27088
- if (!record2(layer) || typeof layer.type !== "string" || !layerKinds.has(layer.type) || layer.ref !== void 0) {
27971
+ if (!record3(layer) || typeof layer.type !== "string" || !layerKinds.has(layer.type) || layer.ref !== void 0) {
27089
27972
  add2("NATIVE_UNSUPPORTED_STYLE", path);
27090
27973
  return;
27091
27974
  }
27092
27975
  properties(layer, spec.layer, path, add2);
27093
27976
  for (const group of ["layout", "paint"]) {
27094
27977
  const values = layer[group];
27095
- if (!record2(values)) continue;
27978
+ if (!record3(values)) continue;
27096
27979
  properties(
27097
27980
  values,
27098
27981
  spec[`${group}_${layer.type}`],
@@ -27106,7 +27989,7 @@ function validateNativeStyle(input, options, stage) {
27106
27989
  }
27107
27990
  }
27108
27991
  expressions(layer.filter, `${path}/filter`, add2);
27109
- if (!options.deferFontClosure && record2(layer.layout) && layer.layout["text-field"] !== void 0) {
27992
+ if (!options.deferFontClosure && record3(layer.layout) && layer.layout["text-field"] !== void 0) {
27110
27993
  const fonts = layer.layout["text-font"];
27111
27994
  if (style.glyphs === void 0) {
27112
27995
  if (!fontStackAvailable(fonts, faces))
@@ -27116,7 +27999,7 @@ function validateNativeStyle(input, options, stage) {
27116
27999
  }
27117
28000
  });
27118
28001
  for (const key of ["light", "transition"]) {
27119
- if (record2(style[key])) {
28002
+ if (record3(style[key])) {
27120
28003
  properties(style[key], spec[key], `/${key}`, add2);
27121
28004
  for (const [property, value] of Object.entries(style[key]))
27122
28005
  expressions(value, nativePointer(`/${key}`, property), add2);
@@ -27149,7 +28032,7 @@ function properties(values, definitions, path, add2, code = "NATIVE_UNSUPPORTED_
27149
28032
  const basic = definition["sdk-support"]?.["basic functionality"];
27150
28033
  if (basic && !supportsBoth(basic)) add2(code, nativePointer(path, key));
27151
28034
  const value = values[key];
27152
- if (checkFeatureSupport && record2(value) && Object.hasOwn(value, "stops"))
28035
+ if (checkFeatureSupport && record3(value) && Object.hasOwn(value, "stops"))
27153
28036
  add2(code, nativePointer(path, key));
27154
28037
  if (typeof value === "string") {
27155
28038
  const enumSupport = definition.values?.[value]?.["sdk-support"]?.["basic functionality"];
@@ -27175,7 +28058,7 @@ function expressions(value, path, add2) {
27175
28058
  if (index === 0) return;
27176
28059
  if (operator === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0) return;
27177
28060
  if (operator.startsWith("interpolate") && index === 1) return;
27178
- if (record2(child)) {
28061
+ if (record3(child)) {
27179
28062
  for (const key of Object.keys(child).sort())
27180
28063
  expressions(child[key], nativePointer(`${path}/${index}`, key), add2);
27181
28064
  } else expressions(child, `${path}/${index}`, add2);
@@ -27183,14 +28066,14 @@ function expressions(value, path, add2) {
27183
28066
  }
27184
28067
  function fontStackAvailable(value, faces) {
27185
28068
  const stack = Array.isArray(value) && value.length === 2 && value[0] === "literal" ? value[1] : value;
27186
- return Array.isArray(stack) && stack.length > 0 && record2(faces) && stack.every((name) => typeof name === "string" && Object.hasOwn(faces, name));
28069
+ return Array.isArray(stack) && stack.length > 0 && record3(faces) && stack.every((name) => typeof name === "string" && Object.hasOwn(faces, name));
27187
28070
  }
27188
28071
  function inlineFontStacks(value, path, faces, add2) {
27189
28072
  if (!Array.isArray(value) || !isExpression(value) || value[0] === "literal") return;
27190
28073
  value.forEach((child, index) => {
27191
28074
  if (index === 0 || value[0] === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0)
27192
28075
  return;
27193
- if (value[0] === "format" && record2(child) && child["text-font"] !== void 0 && !fontStackAvailable(child["text-font"], faces)) {
28076
+ if (value[0] === "format" && record3(child) && child["text-font"] !== void 0 && !fontStackAvailable(child["text-font"], faces)) {
27194
28077
  add2("NATIVE_FONT_UNAVAILABLE", `${path}/${index}/text-font`);
27195
28078
  }
27196
28079
  inlineFontStacks(child, `${path}/${index}`, faces, add2);
@@ -27207,7 +28090,7 @@ function parserIssuePointer(key, style) {
27207
28090
  path = nativePointer(path, match3[1]);
27208
28091
  value = value[Number(match3[1])];
27209
28092
  rest = rest.slice(match3[0].length).replace(/^\./u, "");
27210
- } else if (record2(value)) {
28093
+ } else if (record3(value)) {
27211
28094
  const matches = Object.keys(value).filter(
27212
28095
  (name2) => rest === name2 || rest.startsWith(`${name2}.`) || rest.startsWith(`${name2}[`)
27213
28096
  );
@@ -27238,7 +28121,7 @@ function plainOptions(value) {
27238
28121
  return false;
27239
28122
  }
27240
28123
  }
27241
- function record2(value) {
28124
+ function record3(value) {
27242
28125
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
27243
28126
  }
27244
28127
  function normalizeNativeDiagnostics(input) {
@@ -27528,15 +28411,15 @@ function semanticIdentity(style) {
27528
28411
  const candidate = object(style.metadata)?.["tileflow:interaction-manifest"];
27529
28412
  if (candidate === void 0) return void 0;
27530
28413
  const manifest = semanticSchema.parse(candidate);
27531
- const layers = style.layers.map((value) => {
28414
+ const layers2 = style.layers.map((value) => {
27532
28415
  const layer = object(value);
27533
28416
  if (!layer) throw new Error();
27534
28417
  return layer;
27535
28418
  });
27536
- assertTileflowInteractionManifestLayers(manifest, layers);
28419
+ assertTileflowInteractionManifestLayers(manifest, layers2);
27537
28420
  const seen = /* @__PURE__ */ new Set();
27538
28421
  for (const layer of manifest.domains.poi?.layers ?? []) {
27539
- if (seen.has(layer.layerId) || layers[layer.priority]?.id !== layer.layerId) throw new Error();
28422
+ if (seen.has(layer.layerId) || layers2[layer.priority]?.id !== layer.layerId) throw new Error();
27540
28423
  seen.add(layer.layerId);
27541
28424
  }
27542
28425
  const poi2 = manifest.domains.poi;