@tileflow/core 0.1.0-alpha.37 → 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
@@ -171,6 +171,16 @@ var tileflowDefaultBuildings3DDesign = Object.freeze({
171
171
  function tileflowBuildingsMetadata(design) {
172
172
  return { version: 1, ...design };
173
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
+ }
174
184
 
175
185
  // src/data/world-generation.ts
176
186
  var tileflowWorldGeneration = "v1";
@@ -335,6 +345,7 @@ function tileflowWorldV1Schema(options = {}) {
335
345
  sidewalk: "sidewalk",
336
346
  streetFurniture: "street_furniture",
337
347
  trafficControl: "traffic_control",
348
+ transportationOutline: "transportation_outline",
338
349
  ...options.layers
339
350
  },
340
351
  semantics: { ...options.semantics, parkLayer: "protected-only" }
@@ -1109,26 +1120,26 @@ function toMapLibreStyleValue(value) {
1109
1120
  return cloneJson2(value);
1110
1121
  }
1111
1122
  function isTileflowExpression(value) {
1112
- return isRecord(value) && value.kind === "expression" && Array.isArray(value.value);
1123
+ return isRecord2(value) && value.kind === "expression" && Array.isArray(value.value);
1113
1124
  }
1114
1125
  function isTileflowZoomValue(value) {
1115
- return isRecord(value) && value.kind === "zoom" && Array.isArray(value.stops);
1126
+ return isRecord2(value) && value.kind === "zoom" && Array.isArray(value.stops);
1116
1127
  }
1117
1128
  function isTileflowThemeTokenReference(value) {
1118
- 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);
1119
1130
  }
1120
1131
  function isTileflowFixedValue(value) {
1121
- 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");
1122
1133
  }
1123
1134
  function isTileflowThemeColorOperation(value) {
1124
- if (!isRecord(value) || value.kind !== "theme-color") return false;
1135
+ if (!isRecord2(value) || value.kind !== "theme-color") return false;
1125
1136
  if (value.operation === "alpha") {
1126
1137
  return hasExactOwnKeys(value, ["color", "kind", "opacity", "operation"]) && Object.hasOwn(value, "color") && Object.hasOwn(value, "opacity");
1127
1138
  }
1128
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");
1129
1140
  }
1130
1141
  function isTileflowThemeValueNode(value) {
1131
- 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");
1132
1143
  }
1133
1144
  function createZoomValue(interpolation, stops, base) {
1134
1145
  if (stops.length === 0) {
@@ -1168,7 +1179,7 @@ function cloneJson2(value) {
1168
1179
  }
1169
1180
  return JSON.parse(serialized);
1170
1181
  }
1171
- function isRecord(value) {
1182
+ function isRecord2(value) {
1172
1183
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1173
1184
  }
1174
1185
  function hasExactOwnKeys(value, expectedKeys) {
@@ -1201,7 +1212,7 @@ function applyLineStyle(layer, style) {
1201
1212
  const layout = { ...asRecord(layer.layout) };
1202
1213
  setPaint(paint, "line-blur", style.blur);
1203
1214
  setPaint(paint, "line-color", style.color);
1204
- setPaint(paint, "line-dasharray", style.dash);
1215
+ if (!isSolidDash(style.dash)) setPaint(paint, "line-dasharray", style.dash);
1205
1216
  setPaint(paint, "line-gap-width", style.gapWidth);
1206
1217
  setPaint(paint, "line-offset", style.offset);
1207
1218
  setPaint(paint, "line-opacity", style.opacity);
@@ -1445,6 +1456,12 @@ function withoutGovernedAllowOverlap(layout) {
1445
1456
  }
1446
1457
  return result;
1447
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
+ }
1448
1465
  function setPaint(paint, property, value) {
1449
1466
  if (value !== void 0) paint[property] = toMapLibreStyleValue(value);
1450
1467
  }
@@ -1489,10 +1506,10 @@ function mergeTileflowDesign(base, ...overlays) {
1489
1506
  }
1490
1507
  function mergeValue(base, overlay) {
1491
1508
  if (overlay === void 0) return cloneJson3(base);
1492
- 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)) {
1493
1510
  return cloneJson3(overlay);
1494
1511
  }
1495
- const baseRecord = isRecord2(base) ? base : {};
1512
+ const baseRecord = isRecord3(base) ? base : {};
1496
1513
  const result = {};
1497
1514
  for (const key of /* @__PURE__ */ new Set([...Object.keys(baseRecord), ...Object.keys(overlay)])) {
1498
1515
  const overlayValue = overlay[key];
@@ -1508,7 +1525,7 @@ function cloneJson3(value) {
1508
1525
  if (value === void 0) return value;
1509
1526
  return JSON.parse(JSON.stringify(value));
1510
1527
  }
1511
- function isRecord2(value) {
1528
+ function isRecord3(value) {
1512
1529
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1513
1530
  }
1514
1531
 
@@ -1967,6 +1984,21 @@ function compileBuildings(request, context) {
1967
1984
  }
1968
1985
  return contributions;
1969
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
+ }
1970
2002
  function buildingColorExpression(context) {
1971
2003
  const fields2 = context.data.schema.fields;
1972
2004
  const colors = context.colors.buildings;
@@ -2084,29 +2116,23 @@ function isTileflowPathRoadClass(roadClass) {
2084
2116
  return tileflowPathRoadClasses.includes(roadClass);
2085
2117
  }
2086
2118
  function tileflowRoadClassFilter(fields2, roadClass) {
2119
+ const classes = tileflowRoadClassValues(roadClass);
2087
2120
  if (isTileflowPathRoadClass(roadClass)) {
2088
2121
  return [
2089
2122
  "all",
2090
- ["match", ["get", fields2.class], ["path", ...constructionClasses[roadClass]], true, false],
2123
+ ["match", ["get", fields2.class], classes, true, false],
2091
2124
  ["match", ["get", fields2.subclass], pathSubclasses[roadClass], true, false]
2092
2125
  ];
2093
2126
  }
2094
- const classes = roadClass === "minor" ? [
2095
- "minor",
2096
- "residential",
2097
- "unclassified",
2098
- ...compatibleRoadClasses[roadClass] ?? [],
2099
- ...constructionClasses[roadClass]
2100
- ] : roadClass === "service" ? [
2101
- "service",
2102
- ...compatibleRoadClasses[roadClass] ?? [],
2103
- ...constructionClasses[roadClass]
2104
- ] : [
2105
- 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],
2106
2133
  ...compatibleRoadClasses[roadClass] ?? [],
2107
2134
  ...constructionClasses[roadClass]
2108
2135
  ];
2109
- return ["match", ["get", fields2.class], classes, true, false];
2110
2136
  }
2111
2137
 
2112
2138
  // src/modules/roads/index.ts
@@ -5517,8 +5543,11 @@ var visualNumberProperties = /* @__PURE__ */ new Set([
5517
5543
  "text-translate",
5518
5544
  "symbol-spacing"
5519
5545
  ]);
5546
+ function isTileflowVisualColorCollection(property) {
5547
+ return property === "colors" || property.endsWith("Colors") || property.endsWith("-colors");
5548
+ }
5520
5549
  function classifyTileflowVisualProperty(property, _value) {
5521
- 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)) {
5522
5551
  return "color";
5523
5552
  }
5524
5553
  if (property === "font" || property === "fallbacks" || property.endsWith("-font")) return "font";
@@ -5622,7 +5651,7 @@ function auditTileflowThemeValues(value, path = "value", context = inferAuditCon
5622
5651
  return diagnostics.sort(compareAuditDiagnostics);
5623
5652
  }
5624
5653
  function auditTileflowMapThemeValues(map) {
5625
- const modules = isRecord3(map.modules) ? map.modules : void 0;
5654
+ const modules = isRecord4(map.modules) ? map.modules : void 0;
5626
5655
  const moduleDiagnostics = modules ? Object.keys(modules).sort(compareCodeUnits2).flatMap(
5627
5656
  (owner) => auditTileflowThemeValues(modules[owner], `modules.${owner}`, {
5628
5657
  owner,
@@ -5725,7 +5754,7 @@ function isThemePrimitiveForCategory(value, category) {
5725
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;
5726
5755
  }
5727
5756
  function isThemeValueCandidate(value) {
5728
- 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");
5729
5758
  }
5730
5759
  function visitAuditableValue(value, path, category, state, intentRequired = false) {
5731
5760
  if (isThemeStructuralPath(path)) category = void 0;
@@ -5741,7 +5770,7 @@ function visitAuditableValue(value, path, category, state, intentRequired = fals
5741
5770
  visitAuditableArray(value, path, category, state, false, intentRequired);
5742
5771
  return;
5743
5772
  }
5744
- if (!isRecord3(value)) return;
5773
+ if (!isRecord4(value)) return;
5745
5774
  if (value.kind === "expression" && Array.isArray(value.value)) {
5746
5775
  visitAuditableArray(value.value, `${path}.value`, category, state, true, intentRequired);
5747
5776
  return;
@@ -5761,15 +5790,19 @@ function visitAuditableValue(value, path, category, state, intentRequired = fals
5761
5790
  }
5762
5791
  return;
5763
5792
  }
5793
+ const collectionCategory = isTileflowVisualColorCollection(lastPathKey(path)) ? category : void 0;
5764
5794
  for (const key of Object.keys(value).sort(compareCodeUnits2)) {
5765
5795
  visitAuditableValue(
5766
5796
  value[key],
5767
5797
  `${path}.${key}`,
5768
- classifyTileflowVisualProperty(key, value[key]),
5798
+ classifyTileflowVisualProperty(key, value[key]) ?? collectionCategory,
5769
5799
  state
5770
5800
  );
5771
5801
  }
5772
5802
  }
5803
+ function lastPathKey(path) {
5804
+ return path.endsWith("]") ? "" : path.slice(path.lastIndexOf(".") + 1);
5805
+ }
5773
5806
  function visitAuditableArray(value, path, category, state, forceExpression = false, intentRequired = false) {
5774
5807
  const operator = typeof value[0] === "string" ? value[0] : void 0;
5775
5808
  const isExpression2 = forceExpression || isMapLibreExpressionOperator(operator);
@@ -6056,7 +6089,7 @@ var ThemeValueResolver = class {
6056
6089
  if (Array.isArray(value)) {
6057
6090
  return value.map((entry, index) => this.resolveTree(entry, `${path}[${index}]`, category));
6058
6091
  }
6059
- if (!isRecord3(value)) return value;
6092
+ if (!isRecord4(value)) return value;
6060
6093
  if (value.kind === "expression" && Array.isArray(value.value)) {
6061
6094
  return {
6062
6095
  ...value,
@@ -6370,7 +6403,7 @@ function assertThemeLighting(value, path, allowReset = false) {
6370
6403
  }
6371
6404
  }
6372
6405
  function assertThemeCategoryValue(value, expectedCategory, path) {
6373
- if (!isRecord3(value)) {
6406
+ if (!isRecord4(value)) {
6374
6407
  assertTokenPrimitive(expectedCategory, value, path);
6375
6408
  return;
6376
6409
  }
@@ -6722,11 +6755,11 @@ function cloneJson14(value) {
6722
6755
  if (serialized === void 0) throw new Error("Tileflow themes must be JSON-serializable.");
6723
6756
  return JSON.parse(serialized);
6724
6757
  }
6725
- function isRecord3(value) {
6758
+ function isRecord4(value) {
6726
6759
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6727
6760
  }
6728
6761
  function isPlainRecord2(value) {
6729
- if (!isRecord3(value)) return false;
6762
+ if (!isRecord4(value)) return false;
6730
6763
  const prototype = Object.getPrototypeOf(value);
6731
6764
  return prototype === Object.prototype || prototype === null;
6732
6765
  }
@@ -7278,35 +7311,37 @@ function compileRoundabouts(request, surfaces, context, contributions) {
7278
7311
  }),
7279
7312
  0
7280
7313
  ) : atZoom(borderWidth, level);
7281
- const geometry = (casing, width) => expression([
7282
- "interpolate",
7283
- ["exponential", 2],
7284
- ["zoom"],
7285
- ...geometryLevels.flatMap((level) => {
7286
- const centerline = ["*", radiusAt15, 2 ** (level - 15)];
7287
- const fallback = fallbackWidth === void 0 ? select(
7288
- widths.map(({ condition, value }) => ({ condition, value: atZoom(value, level) })),
7289
- 0
7290
- ) : atZoom(fallbackWidth, level);
7291
- const inner = [
7292
- "case",
7293
- physical,
7294
- ["*", centerline, ["/", innerMeters, radiusMeters]],
7295
- ["-", centerline, ["/", fallback, 2]]
7296
- ];
7297
- const fillWidth = [
7298
- "case",
7299
- physical,
7300
- ["*", centerline, ["/", ["-", outerMeters, innerMeters], radiusMeters]],
7301
- fallback
7302
- ];
7303
- const border = casing ? borderAt(level) : 0;
7304
- return [
7305
- level,
7306
- width ? ["max", 0, ["+", fillWidth, border]] : ["max", 0, ["-", inner, ["/", border, 2]]]
7307
- ];
7308
- })
7309
- ]);
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
+ );
7310
7345
  const phaseStyle = (phase) => ({
7311
7346
  color: "rgba(0, 0, 0, 0)",
7312
7347
  minZoom: 15,
@@ -7363,6 +7398,63 @@ function compileRoundabouts(request, surfaces, context, contributions) {
7363
7398
  });
7364
7399
  }
7365
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
+ }
7366
7458
  function select(values, fallback) {
7367
7459
  if (!values.length) return fallback;
7368
7460
  const serialized = JSON.stringify(values[0].value);
@@ -7474,13 +7566,49 @@ function atZoom(value, level) {
7474
7566
  stops.push({ level: value[index], value: value[index + 1] });
7475
7567
  }
7476
7568
  const exact = stops.find((stop) => stop.level === level);
7477
- if (exact) return exact.value;
7569
+ if (exact) return foldConstants(exact.value);
7478
7570
  const lower = [...stops].reverse().find((stop) => stop.level < level);
7479
7571
  const upper = stops.find((stop) => stop.level > level);
7480
- if (value[0] === "step") return lower?.value ?? value[2];
7481
- if (!lower) return upper?.value ?? 0;
7482
- if (!upper) return lower.value;
7483
- 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;
7484
7612
  }
7485
7613
 
7486
7614
  // src/modules/roads/compiler.ts
@@ -7551,6 +7679,10 @@ function compileRoads(request, context) {
7551
7679
  request?.classes?.[roadClass]
7552
7680
  );
7553
7681
  const treatments = resolveRoadTreatments(request, schema.fields, roadClass);
7682
+ const drawn = {
7683
+ field: schema.fields.class,
7684
+ classes: new Set(tileflowRoadClassValues(roadClass))
7685
+ };
7554
7686
  const roundaboutSurface = { roadClass };
7555
7687
  roundaboutSurfaces.push(roundaboutSurface);
7556
7688
  for (const structure of ["tunnel", "surface", "bridge"]) {
@@ -7573,7 +7705,8 @@ function compileRoads(request, context) {
7573
7705
  treatments,
7574
7706
  structure,
7575
7707
  phase,
7576
- phaseDefaults.color
7708
+ phaseDefaults.color,
7709
+ drawn
7577
7710
  );
7578
7711
  const contribution = {
7579
7712
  family: roadLinePhysicalFamily(roadClass, structure, phase),
@@ -7636,7 +7769,8 @@ function compileRoads(request, context) {
7636
7769
  treatments,
7637
7770
  structure,
7638
7771
  "fill",
7639
- roadColor(context, roadClass)
7772
+ roadColor(context, roadClass),
7773
+ drawn
7640
7774
  );
7641
7775
  const size = hatch.size === void 0 ? scaleStyleValue(treatedFill.width ?? 1, 0.68) : toMapLibreStyleValue(hatch.size);
7642
7776
  const minZoom = hatch.minZoom ?? treatedFill.minZoom;
@@ -7757,31 +7891,24 @@ function compileRoads(request, context) {
7757
7891
  const areaTargets = [
7758
7892
  {
7759
7893
  id: "tileflow-road-area",
7760
- filter: ["==", ["geometry-type"], "Polygon"],
7894
+ kind: void 0,
7761
7895
  name: "road",
7762
7896
  style: areaStyles.road
7763
7897
  },
7764
7898
  {
7765
7899
  id: "tileflow-road-pedestrian-area",
7766
- filter: [
7767
- "all",
7768
- ["==", ["geometry-type"], "Polygon"],
7769
- tileflowRoadClassFilter(schema.fields, "pedestrian")
7770
- ],
7900
+ kind: tileflowRoadClassFilter(schema.fields, "pedestrian"),
7771
7901
  name: "pedestrian",
7772
7902
  style: areaStyles.pedestrian
7773
7903
  },
7774
7904
  {
7775
7905
  id: "tileflow-road-pier-area",
7776
- filter: [
7777
- "all",
7778
- ["==", ["geometry-type"], "Polygon"],
7779
- ["==", ["get", schema.fields.class], "pier"]
7780
- ],
7906
+ kind: ["==", ["get", schema.fields.class], "pier"],
7781
7907
  name: "pier",
7782
7908
  style: areaStyles.pier
7783
7909
  }
7784
7910
  ];
7911
+ const polygons2 = ["==", ["geometry-type"], "Polygon"];
7785
7912
  let areaOrder = 900;
7786
7913
  for (const target of areaTargets) {
7787
7914
  if (!target.style) continue;
@@ -7791,13 +7918,19 @@ function compileRoads(request, context) {
7791
7918
  type: "fill",
7792
7919
  source,
7793
7920
  "source-layer": schema.layers.road,
7794
- filter: target.filter
7921
+ filter: target.kind ? ["all", polygons2, target.kind] : polygons2
7795
7922
  },
7796
7923
  target.style
7797
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;
7798
7931
  contributions.push({
7799
7932
  kind: "layer",
7800
- layer: area.layer,
7933
+ layer: outline,
7801
7934
  localOrder: areaOrder++,
7802
7935
  owner: "roads",
7803
7936
  slot: "transport-areas",
@@ -8074,9 +8207,10 @@ function resolveRoadTreatments(request, fields2, roadClass) {
8074
8207
  add2(request?.modifiers?.unpaved, ["==", ["get", fields2.surface], "unpaved"]);
8075
8208
  add2(request?.modifiers?.indoor, flagFilter(fields2.indoor));
8076
8209
  add2(request?.modifiers?.official, flagFilter(fields2.official));
8077
- for (const [scale, style] of Object.entries(request?.mountainBike ?? {})) {
8078
- add2(style, ["==", ["to-string", ["get", fields2.mtbScale]], scale]);
8079
- }
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
+ }
8080
8214
  if (roadClass === "service") {
8081
8215
  for (const [serviceType, value] of Object.entries(serviceTypeValues)) {
8082
8216
  add2(request?.serviceTypes?.[serviceType], [
@@ -8088,7 +8222,7 @@ function resolveRoadTreatments(request, fields2, roadClass) {
8088
8222
  }
8089
8223
  return entries;
8090
8224
  }
8091
- function applyRoadTreatments(base, treatments, structure, phase, fallbackColor) {
8225
+ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor, drawn) {
8092
8226
  const result = { ...base };
8093
8227
  const paintProperties = [
8094
8228
  "blur",
@@ -8105,12 +8239,12 @@ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor)
8105
8239
  return value === void 0 ? [] : [
8106
8240
  {
8107
8241
  condition,
8108
- resolve: () => expressionOutput(toMapLibreStyleValue(value))
8242
+ resolve: () => expressionOutput(withinClasses(toMapLibreStyleValue(value), drawn))
8109
8243
  }
8110
8244
  ];
8111
8245
  });
8112
8246
  if (branches.length > 0) {
8113
- 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()))) {
8114
8248
  delete result[property];
8115
8249
  continue;
8116
8250
  }
@@ -8140,6 +8274,49 @@ function applyRoadTreatments(base, treatments, structure, phase, fallbackColor)
8140
8274
  }
8141
8275
  return result;
8142
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
+ }
8143
8320
  function isSolidRoadDash(value) {
8144
8321
  const output = expressionOutput(value);
8145
8322
  const dash = Array.isArray(output) && output[0] === "literal" ? output[1] : output;
@@ -10890,21 +11067,21 @@ function bindSemanticValue(value, schema, sourceId) {
10890
11067
  }
10891
11068
  if (isDataSourceReference(value)) return sourceId;
10892
11069
  if (Array.isArray(value)) return value.map((item) => bindSemanticValue(item, schema, sourceId));
10893
- if (!isRecord4(value)) return value;
11070
+ if (!isRecord5(value)) return value;
10894
11071
  return Object.fromEntries(
10895
11072
  Object.entries(value).map(([key, item]) => [key, bindSemanticValue(item, schema, sourceId)])
10896
11073
  );
10897
11074
  }
10898
11075
  function isDataFieldReference2(value) {
10899
- 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";
10900
11077
  }
10901
11078
  function isDataLayerReference(value) {
10902
- 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";
10903
11080
  }
10904
11081
  function isDataSourceReference(value) {
10905
- return isRecord4(value) && value.kind === "tileflow-data-source" && value.name === "primary";
11082
+ return isRecord5(value) && value.kind === "tileflow-data-source" && value.name === "primary";
10906
11083
  }
10907
- function isRecord4(value) {
11084
+ function isRecord5(value) {
10908
11085
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10909
11086
  }
10910
11087
  function compareCodeUnits5(left, right) {
@@ -11008,8 +11185,8 @@ function createTileflowLayerTemplateIR(layer, semanticKey) {
11008
11185
  renderer: type,
11009
11186
  ...filter === void 0 ? {} : { selector: cloneJson20(filter) },
11010
11187
  style: Object.freeze({
11011
- ...isRecord5(layout) ? { placement: Object.freeze(cloneJson20(layout)) } : {},
11012
- ...isRecord5(paint) ? { appearance: Object.freeze(cloneJson20(paint)) } : {}
11188
+ ...isRecord6(layout) ? { placement: Object.freeze(cloneJson20(layout)) } : {},
11189
+ ...isRecord6(paint) ? { appearance: Object.freeze(cloneJson20(paint)) } : {}
11013
11190
  })
11014
11191
  });
11015
11192
  }
@@ -11269,21 +11446,21 @@ function visitReferences(value, counts, visited = /* @__PURE__ */ new WeakSet())
11269
11446
  }
11270
11447
  }
11271
11448
  function isSemanticFieldReference(value) {
11272
- return isRecord5(value) && value.kind === "tileflow-data-field";
11449
+ return isRecord6(value) && value.kind === "tileflow-data-field";
11273
11450
  }
11274
11451
  function isSemanticLayerReference(value) {
11275
- return isRecord5(value) && value.kind === "tileflow-data-layer";
11452
+ return isRecord6(value) && value.kind === "tileflow-data-layer";
11276
11453
  }
11277
11454
  function isSemanticSourceReference(value) {
11278
- return isRecord5(value) && value.kind === "tileflow-data-source";
11455
+ return isRecord6(value) && value.kind === "tileflow-data-source";
11279
11456
  }
11280
11457
  function cloneJson20(value) {
11281
11458
  return JSON.parse(JSON.stringify(value));
11282
11459
  }
11283
11460
  function asRecord4(value) {
11284
- return isRecord5(value) ? value : {};
11461
+ return isRecord6(value) ? value : {};
11285
11462
  }
11286
- function isRecord5(value) {
11463
+ function isRecord6(value) {
11287
11464
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
11288
11465
  }
11289
11466
 
@@ -11335,7 +11512,7 @@ function validateTileflowRenderSelectorConstraints(input) {
11335
11512
  };
11336
11513
  while (pending.length > 0) {
11337
11514
  const current = pending.pop();
11338
- if (!isRecord6(current.selector) || typeof current.selector.kind !== "string") {
11515
+ if (!isRecord7(current.selector) || typeof current.selector.kind !== "string") {
11339
11516
  addIssue(
11340
11517
  "invalid-selector",
11341
11518
  "a render selector must be a semantic selector node",
@@ -11396,7 +11573,7 @@ function validateTileflowRenderSelectorConstraints(input) {
11396
11573
  }
11397
11574
  for (const [index, branch] of current.selector.branches.entries()) {
11398
11575
  const branchPath = [...branchesPath, index];
11399
- if (!isRecord6(branch)) {
11576
+ if (!isRecord7(branch)) {
11400
11577
  addIssue("invalid-selector", "match selector branch must be an object", branchPath);
11401
11578
  continue;
11402
11579
  }
@@ -11427,14 +11604,14 @@ function validateTileflowRenderSelectorConstraints(input) {
11427
11604
  for (let index = current.selector.stops.length - 1; index >= 0; index -= 1) {
11428
11605
  const stop = current.selector.stops[index];
11429
11606
  const stopPath = [...stopsPath, index];
11430
- if (!isRecord6(stop)) {
11607
+ if (!isRecord7(stop)) {
11431
11608
  addIssue("invalid-selector", "step selector stop must be an object", stopPath);
11432
11609
  continue;
11433
11610
  }
11434
11611
  pushChild(stop.selector, current.depth + 1, [...stopPath, "selector"]);
11435
11612
  }
11436
11613
  for (const [index, stop] of current.selector.stops.entries()) {
11437
- if (!isRecord6(stop)) continue;
11614
+ if (!isRecord7(stop)) continue;
11438
11615
  const zoom3 = stop.zoom;
11439
11616
  const zoomPath = [...stopsPath, index, "zoom"];
11440
11617
  if (typeof zoom3 !== "number" || !Number.isFinite(zoom3) || zoom3 < 0 || zoom3 > 24) {
@@ -11498,7 +11675,7 @@ function compileRenderSelector(selector, data) {
11498
11675
  assertRenderSelectorConstraints(selector);
11499
11676
  return lowerSelector(selector, data);
11500
11677
  }
11501
- function applyCompiledRenderStacks(input, operations) {
11678
+ function applyCompiledRenderStacks(input, operations, targetsOfOtherModes = /* @__PURE__ */ new Map()) {
11502
11679
  let layers2 = input.map(cloneJson21);
11503
11680
  const passes = operations.filter(
11504
11681
  (operation) => operation.kind === "layer"
@@ -11529,9 +11706,10 @@ function applyCompiledRenderStacks(input, operations) {
11529
11706
  for (const refinement of activeRefinements) {
11530
11707
  const index = findTargetIndex(layers2, refinement.target);
11531
11708
  if (index < 0 && !passTargets.has(refinement.target)) {
11709
+ const other = targetsOfOtherModes.get(refinement.target);
11532
11710
  throw renderStackError(
11533
11711
  "unknown-refinement-target",
11534
- `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)}`
11535
11713
  );
11536
11714
  }
11537
11715
  if (refinement.placement !== void 0 && placementAnchor(refinement.placement) === refinement.target) {
@@ -11553,9 +11731,10 @@ function applyCompiledRenderStacks(input, operations) {
11553
11731
  for (const operation of pending) {
11554
11732
  const anchor = placementAnchor(operation.placement);
11555
11733
  if (anchor !== void 0 && !knownTargets.has(anchor)) {
11734
+ const other = targetsOfOtherModes.get(anchor);
11556
11735
  throw renderStackError(
11557
11736
  "unknown-or-circular-anchor",
11558
- `${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)}`
11559
11738
  );
11560
11739
  }
11561
11740
  }
@@ -11759,7 +11938,7 @@ function applyRendererStyle(layer, renderer, style) {
11759
11938
  };
11760
11939
  }
11761
11940
  function lowerSelector(selector, data) {
11762
- if (!isRecord6(selector) || typeof selector.kind !== "string") {
11941
+ if (!isRecord7(selector) || typeof selector.kind !== "string") {
11763
11942
  throw renderStackError(
11764
11943
  "invalid-selector",
11765
11944
  "A render selector must be a semantic selector node."
@@ -11962,7 +12141,7 @@ function requireRenderer(value) {
11962
12141
  function requirePlacement(value) {
11963
12142
  if (typeof value === "string") {
11964
12143
  if (tileflowBandPlacements.includes(value)) return;
11965
- } else if (isRecord6(value)) {
12144
+ } else if (isRecord7(value)) {
11966
12145
  const keys = Object.keys(value);
11967
12146
  if (keys.length === 1 && (keys[0] === "above" || keys[0] === "below")) {
11968
12147
  requirePortableTarget(value[keys[0]], "render placement target");
@@ -11989,7 +12168,7 @@ function validateRefinementShape(operation) {
11989
12168
  validateStyle(operation.renderer, operation.style);
11990
12169
  }
11991
12170
  function validateStyle(renderer, style) {
11992
- if (!isRecord6(style)) {
12171
+ if (!isRecord7(style)) {
11993
12172
  throw renderStackError(
11994
12173
  "invalid-style",
11995
12174
  `A ${renderer} render operation requires a style object.`
@@ -12091,7 +12270,7 @@ function normalizeRequirements(requirements) {
12091
12270
  return [...requirements];
12092
12271
  }
12093
12272
  function requireRenderStackEntries(stack) {
12094
- if (!isRecord6(stack)) {
12273
+ if (!isRecord7(stack)) {
12095
12274
  throw renderStackError("invalid-stack", "A render stack must be a named operation record.");
12096
12275
  }
12097
12276
  const entries = Object.entries(stack);
@@ -12228,6 +12407,9 @@ function assertOnlyKeys(input, allowed, description) {
12228
12407
  );
12229
12408
  }
12230
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
+ }
12231
12413
  function renderStackError(code, message) {
12232
12414
  return Object.assign(new Error(message), { code, name: "TileflowRenderStackError" });
12233
12415
  }
@@ -12238,9 +12420,9 @@ function cloneJson21(value) {
12238
12420
  return JSON.parse(JSON.stringify(value));
12239
12421
  }
12240
12422
  function asRecord5(value) {
12241
- return isRecord6(value) ? value : {};
12423
+ return isRecord7(value) ? value : {};
12242
12424
  }
12243
- function isRecord6(value) {
12425
+ function isRecord7(value) {
12244
12426
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
12245
12427
  }
12246
12428
 
@@ -13311,6 +13493,7 @@ function boundedThemeNumberSchema(min, max) {
13311
13493
  }).strict()
13312
13494
  ]);
13313
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.");
13314
13497
  var modelRoleLightingSchema = z5.object({
13315
13498
  emissiveStrength: boundedThemeNumberSchema(0, 5).optional(),
13316
13499
  emissiveVariation: boundedThemeNumberSchema(0, 1).optional(),
@@ -13366,6 +13549,7 @@ var buildingsModuleSchema = z5.object({
13366
13549
  lamp: modelRoleLightingSchema.optional(),
13367
13550
  logo: modelRoleLightingSchema.optional()
13368
13551
  }).strict().optional(),
13552
+ entrance: z5.object({ buildings: entranceBandSchema.optional(), models: entranceBandSchema.optional() }).strict().optional(),
13369
13553
  occlusion: z5.boolean().optional(),
13370
13554
  modelOcclusion: boundedThemeNumberSchema(0, 1).optional(),
13371
13555
  shadows: z5.boolean().optional(),
@@ -13745,6 +13929,7 @@ var layerBindingsSchema = z5.object({
13745
13929
  streetFurniture: z5.string().trim().min(1).optional(),
13746
13930
  trafficControl: z5.string().trim().min(1).optional(),
13747
13931
  tree: z5.string().trim().min(1).optional(),
13932
+ transportationOutline: z5.string().trim().min(1).optional(),
13748
13933
  water: z5.string().trim().min(1),
13749
13934
  waterName: z5.string().trim().min(1),
13750
13935
  waterway: z5.string().trim().min(1)
@@ -15351,11 +15536,11 @@ function isTileflowPoiCategory(value) {
15351
15536
  return typeof value === "string" && tileflowPoiCategories.includes(value);
15352
15537
  }
15353
15538
  function semanticTarget(layer) {
15354
- if (!isRecord7(layer.metadata)) return void 0;
15539
+ if (!isRecord8(layer.metadata)) return void 0;
15355
15540
  const value = layer.metadata[tileflowCompilerMetadataKeys.target];
15356
15541
  return typeof value === "string" ? value : void 0;
15357
15542
  }
15358
- function isRecord7(value) {
15543
+ function isRecord8(value) {
15359
15544
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
15360
15545
  }
15361
15546
  function isSafeArtifactName(value) {
@@ -15536,6 +15721,8 @@ function mergeRoadLineGroup(key, layers2) {
15536
15721
  const paintKeys = new Set(layers2.flatMap((layer) => Object.keys(layer.style.appearance ?? {})));
15537
15722
  for (const key2 of paintKeys) {
15538
15723
  const values = layers2.map((layer) => layer.style.appearance?.[key2]);
15724
+ if (key2 === "line-dasharray" && values.every((value) => isSolidDash(value ?? linePaintDefaults[key2])))
15725
+ continue;
15539
15726
  const combined = combineConditionalValues(
15540
15727
  values.map((value, index) => ({
15541
15728
  condition: conditions[index],
@@ -16251,7 +16438,7 @@ var colourSources = [
16251
16438
  ["tileflow-buildings-fill", "fill-color"]
16252
16439
  ];
16253
16440
  function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, tileflowLandmarks) {
16254
- const config = isRecord8(module) ? module : {};
16441
+ const config = isRecord9(module) ? module : {};
16255
16442
  if (config.enabled === false) return void 0;
16256
16443
  const style = config.threeDimensional ?? {};
16257
16444
  const defaults4 = tileflowDefaultBuildings3DDesign;
@@ -16284,7 +16471,7 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
16284
16471
  edgeRadius: number(style.edgeRadius, defaults4.edgeRadius),
16285
16472
  ...typeof edgeColor === "string" ? { edgeColor } : {},
16286
16473
  nightLight: number(style.nightLight, 0),
16287
- ...isRecord8(style.floodLight) ? {
16474
+ ...isRecord9(style.floodLight) ? {
16288
16475
  floodLight: readBuildingFloodLight({
16289
16476
  ...style.floodLight,
16290
16477
  intensity: number(style.floodLight.intensity, 0),
@@ -16292,10 +16479,10 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
16292
16479
  color: style.floodLight.color === void 0 ? void 0 : toMapLibreStyleValue(style.floodLight.color)
16293
16480
  })
16294
16481
  } : {},
16295
- ...isRecord8(style.modelLighting) ? {
16482
+ ...isRecord9(style.modelLighting) ? {
16296
16483
  modelLighting: readModelRoleLighting(
16297
16484
  Object.fromEntries(
16298
- Object.entries(style.modelLighting).filter(([, entry]) => isRecord8(entry)).map(([role, entry]) => {
16485
+ Object.entries(style.modelLighting).filter(([, entry]) => isRecord9(entry)).map(([role, entry]) => {
16299
16486
  const own = entry;
16300
16487
  return [
16301
16488
  role,
@@ -16306,7 +16493,7 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
16306
16493
  ["emissiveVariation", "colorMix", "metallic", "roughness"].filter((key) => own[key] !== void 0).map((key) => [key, number(own[key], 0)])
16307
16494
  ),
16308
16495
  ...Array.isArray(own.emissiveHeight) ? { emissiveHeight: own.emissiveHeight.map((v) => number(v, 0)) } : {},
16309
- ...isRecord8(own.colorVariation) ? {
16496
+ ...isRecord9(own.colorVariation) ? {
16310
16497
  colorVariation: {
16311
16498
  ...own.colorVariation,
16312
16499
  ...Object.fromEntries(
@@ -16328,6 +16515,7 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
16328
16515
  )
16329
16516
  } : {},
16330
16517
  windows: style.windows === true,
16518
+ ...readTileflowBuildingsEntrance(style.entrance),
16331
16519
  shadows: typeof style.shadows === "boolean" ? style.shadows : defaults4.shadows,
16332
16520
  occlusion: typeof style.occlusion === "boolean" ? style.occlusion : defaults4.occlusion,
16333
16521
  modelOcclusion: number(style.modelOcclusion, defaults4.modelOcclusion),
@@ -16338,7 +16526,7 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
16338
16526
  }
16339
16527
  function landmarksOf(value, tileflowLandmarks) {
16340
16528
  if (value === false) return {};
16341
- 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 } };
16342
16530
  return tileflowLandmarks ? { landmarks: { url: tileflowLandmarks } } : {};
16343
16531
  }
16344
16532
  function placedModels(value, served) {
@@ -16365,7 +16553,7 @@ function placedModels(value, served) {
16365
16553
  };
16366
16554
  }
16367
16555
  function modelColours(value) {
16368
- if (!isRecord8(value)) return {};
16556
+ if (!isRecord9(value)) return {};
16369
16557
  const colours = {};
16370
16558
  for (const [part, colour] of Object.entries(value)) {
16371
16559
  const resolved = colour === void 0 ? void 0 : toMapLibreStyleValue(colour);
@@ -16374,7 +16562,7 @@ function modelColours(value) {
16374
16562
  return Object.keys(colours).length > 0 ? { modelColors: colours } : {};
16375
16563
  }
16376
16564
  function tileflowStreetLightsDesignOf(module, context) {
16377
- const config = isRecord8(module) ? module : {};
16565
+ const config = isRecord9(module) ? module : {};
16378
16566
  const layers2 = context.data.schema.layers;
16379
16567
  const style = config.streetLights ?? {};
16380
16568
  if (config.enabled === false || style.enabled !== true || !layers2.streetFurniture)
@@ -16394,7 +16582,7 @@ function tileflowStreetLightsDesignOf(module, context) {
16394
16582
  intensity: typeof intensity === "number" ? intensity : 1
16395
16583
  };
16396
16584
  }
16397
- function isRecord8(value) {
16585
+ function isRecord9(value) {
16398
16586
  return typeof value === "object" && value !== null && !Array.isArray(value);
16399
16587
  }
16400
16588
 
@@ -16566,7 +16754,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16566
16754
  )
16567
16755
  );
16568
16756
  const renderStackModules = Object.values(modules).filter(
16569
- (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)
16570
16758
  );
16571
16759
  const renderOperations = runCompilationPhase(
16572
16760
  "render-stack",
@@ -16592,7 +16780,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16592
16780
  const requestedRenderOperations = domainIR.renderOperations;
16593
16781
  const stackedFamilies = runCompilationPhase(
16594
16782
  "render-stack",
16595
- () => applyCompiledRenderStacks(assembledFamilies, requestedRenderOperations)
16783
+ () => applyCompiledRenderStacks(
16784
+ assembledFamilies,
16785
+ requestedRenderOperations,
16786
+ buildingTargetsOfOtherModes(modules.buildings)
16787
+ )
16596
16788
  );
16597
16789
  const plannedFamilies = runCompilationPhase(
16598
16790
  "physical-planner",
@@ -16857,7 +17049,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
16857
17049
  "tileflow:sourceRequirements": sourceRequirements,
16858
17050
  ...interactionManifest ? { [tileflowInteractionManifestMetadataKey]: interactionManifest } : {},
16859
17051
  [tileflowOverlayPlacementManifestMetadataKey]: overlayPlacementManifest,
16860
- "tileflow:modules": Object.entries(modules).filter(([, module]) => !isRecord9(module) || module.enabled !== false).map(([name]) => name).sort(),
17052
+ "tileflow:modules": Object.entries(modules).filter(([, module]) => !isRecord10(module) || module.enabled !== false).map(([name]) => name).sort(),
16861
17053
  ...config.view ? { "tileflow:view": config.view } : {},
16862
17054
  ...notices.length > 0 ? { [tileflowNoticesMetadataKey]: notices } : {}
16863
17055
  }
@@ -16919,7 +17111,7 @@ function finalizeTileflowLayers(input) {
16919
17111
  if (typeof minimum2 === "number" && typeof maximum2 === "number" && minimum2 > maximum2) {
16920
17112
  throw new Error(`Compiled Tileflow layer ${id2} requires minzoom <= maxzoom.`);
16921
17113
  }
16922
- const metadata = isRecord9(layer.metadata) ? { ...layer.metadata } : void 0;
17114
+ const metadata = isRecord10(layer.metadata) ? { ...layer.metadata } : void 0;
16923
17115
  if (metadata) {
16924
17116
  const owner = metadata[tileflowCompilerMetadataKeys.owner];
16925
17117
  delete metadata[tileflowCompilerProvenanceMetadataKey];
@@ -16953,7 +17145,7 @@ function assertMapLibreStyle(style, mapId) {
16953
17145
  }
16954
17146
  );
16955
17147
  }
16956
- function isRecord9(value) {
17148
+ function isRecord10(value) {
16957
17149
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16958
17150
  }
16959
17151
  function compiledNotices(layers2, marine) {
@@ -16998,8 +17190,8 @@ function assertPreparedIconReferences(style, config, prepared) {
16998
17190
  function collectStyleImageReferences(style) {
16999
17191
  const result = /* @__PURE__ */ new Set();
17000
17192
  for (const layer of style.layers) {
17001
- const layout = isRecord9(layer.layout) ? layer.layout : {};
17002
- const paint = isRecord9(layer.paint) ? layer.paint : {};
17193
+ const layout = isRecord10(layer.layout) ? layer.layout : {};
17194
+ const paint = isRecord10(layer.paint) ? layer.paint : {};
17003
17195
  const layerId = typeof layer.id === "string" ? layer.id : "<unknown>";
17004
17196
  for (const [property, value] of [
17005
17197
  ["icon-image", layout["icon-image"]],
@@ -17088,7 +17280,7 @@ function assertGlyphFontStacks(style, config) {
17088
17280
  const declared = new Set(config.glyphs.fontStacks);
17089
17281
  const missing = /* @__PURE__ */ new Set();
17090
17282
  for (const layer of style.layers) {
17091
- const layout = isRecord9(layer.layout) ? layer.layout : void 0;
17283
+ const layout = isRecord10(layer.layout) ? layer.layout : void 0;
17092
17284
  const font = layout?.["text-font"];
17093
17285
  if (!Array.isArray(font) || !font.every((entry) => typeof entry === "string")) continue;
17094
17286
  const stack = font.join(",");
@@ -17104,7 +17296,7 @@ function assertGlyphFontStacks(style, config) {
17104
17296
  function assertTextAssets(style, config) {
17105
17297
  const textLayers = [];
17106
17298
  for (const layer of style.layers) {
17107
- const layout = isRecord9(layer.layout) ? layer.layout : void 0;
17299
+ const layout = isRecord10(layer.layout) ? layer.layout : void 0;
17108
17300
  if (layout?.["text-field"] === void 0) continue;
17109
17301
  textLayers.push(typeof layer.id === "string" ? layer.id : "<unknown>");
17110
17302
  const font = layout["text-font"];
@@ -17117,7 +17309,7 @@ function assertTextAssets(style, config) {
17117
17309
  if (textLayers.length === 0) return;
17118
17310
  if (config.fonts !== void 0) {
17119
17311
  const backed = style.layers.find((layer) => {
17120
- const font = isRecord9(layer.layout) ? layer.layout["text-font"] : void 0;
17312
+ const font = isRecord10(layer.layout) ? layer.layout["text-font"] : void 0;
17121
17313
  return Array.isArray(font) && font.length === 1 && typeof font[0] === "string" && parseTileflowLineBackgroundFontStack(font[0]) !== void 0;
17122
17314
  });
17123
17315
  if (backed) {
@@ -17143,7 +17335,7 @@ function runCompilationPhase(phase, operation) {
17143
17335
  }
17144
17336
  }
17145
17337
  function withCompilationPhase(error, phase) {
17146
- const record4 = isRecord9(error) ? error : {};
17338
+ const record4 = isRecord10(error) ? error : {};
17147
17339
  if (typeof record4.phase === "string") return error;
17148
17340
  if (error instanceof Error && Object.isExtensible(error)) {
17149
17341
  Object.defineProperty(error, "phase", { configurable: true, enumerable: true, value: phase });
@@ -17894,6 +18086,9 @@ function applyModuleOperation(resolved, moduleName, operation, mapId, path) {
17894
18086
  assertNoReset(patch, mapId, `${path}/patches/${index}`, true);
17895
18087
  next = applyModulePatch(next, patch, mapId, `${path}/patches/${index}`);
17896
18088
  }
18089
+ if (isPlainRecord4(next.renderStack) && Object.keys(next.renderStack).length === 0) {
18090
+ delete next.renderStack;
18091
+ }
17897
18092
  assertModuleOwner(next, moduleName, mapId, path);
17898
18093
  resolved[moduleName] = next;
17899
18094
  return;
@@ -18056,11 +18251,11 @@ function cloneDesign(value) {
18056
18251
  function describeMap(value) {
18057
18252
  return typeof value.id === "string" && value.id ? value.id : "<anonymous>";
18058
18253
  }
18059
- function isRecord10(value) {
18254
+ function isRecord11(value) {
18060
18255
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
18061
18256
  }
18062
18257
  function isPlainRecord4(value) {
18063
- if (!isRecord10(value)) return false;
18258
+ if (!isRecord11(value)) return false;
18064
18259
  const prototype = Object.getPrototypeOf(value);
18065
18260
  return prototype === Object.prototype || prototype === null;
18066
18261
  }