@chrtco/sdk 0.10.47 → 0.10.49

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/index.js CHANGED
@@ -25386,15 +25386,46 @@ function getLabelConfig(layerName, opts = {}) {
25386
25386
  // UWTROC/OBSTRN/WRECKS are dominance-PARTICIPATING classes —
25387
25387
  // exactly one band's copy renders at any zoom, so anchor-shifting
25388
25388
  // can't split a cross-band duplicate pair into two visible labels.
25389
- // Shallowest (most dangerous) hazard wins within-class contests;
25390
- // VALSOU-less hazards (awash / unsounded) rank first of all.
25391
25389
  layout: {
25392
25390
  "text-variable-anchor": ["top-left", "bottom-left", "top-right", "bottom-right"],
25393
25391
  "text-radial-offset": 0.9,
25394
25392
  "text-justify": "auto",
25395
- "text-allow-overlap": false,
25393
+ // ⚠ A DANGER DEPTH IS NEVER DROPPED (owner, 2026-10-06). Before,
25394
+ // a callout with no free corner was culled and the rock, wreck or
25395
+ // obstruction drew with no depth beside it: 15 of 136 at Boston
25396
+ // z12.3. ALLOW-OVERLAP ONLY, and that is the whole point:
25397
+ // - the variable anchors still fan out first — MapLibre 5.24
25398
+ // tries every corner without overlap and overlaps, at the
25399
+ // first corner, only when none is free (`placement.ts`,
25400
+ // `placementPasses`), so only the callouts that used to vanish
25401
+ // draw over something;
25402
+ // - `text-ignore-placement` stays false, so a callout keeps its
25403
+ // collision space and blocks what it blocked before. Ignoring
25404
+ // placement as well freed that space for buoy and place names,
25405
+ // which then pushed shallow soundings off: 11 shoals uncovered
25406
+ // at z12.3 on the bench. This form: 0.
25407
+ // The census and its numbers: `scripts/chart-bench/
25408
+ // danger-depth-census.ts` and `.chrt/standards/s-52.md` §8a.
25409
+ "text-allow-overlap": true,
25396
25410
  "text-optional": true,
25397
25411
  "text-padding": 2,
25412
+ // ⚠ THE DEPTH SORT KEY IS LOAD-BEARING — do not "optimise" it to a
25413
+ // constant. It costs draw calls: MapLibre 5.24 draws an overlapping
25414
+ // layer with a data-driven key one segment per distinct key, twice
25415
+ // with a halo over 1 px (`drawLayerSymbols`, `hasSortKey &&
25416
+ // bucket.canOverlap`), so at 100 % the chart issues +14 % draw calls
25417
+ // at z12.3 against 0.10.3 (R6's 0.10.4 gate). But the key is also
25418
+ // the PLACEMENT order, and a callout keeps its collision box, so the
25419
+ // order decides which corner each callout takes and therefore which
25420
+ // soundings and buoy names survive beneath it. Measured with each
25421
+ // form booted fresh (2026-10-06, Boston and Salem Sound, z11.5–16,
25422
+ // at 1280×800 and the Pi's 1920×1185): a constant key, 3/4/5 depth
25423
+ // bands and whole metres ALL uncover shoals that 0.10.3 drew (a
25424
+ // constant key: 5 at z12.3 on the Pi's canvas, a 0.6 m one at
25425
+ // 1280×800) and change buoy names; only this key matches 0.10.4's
25426
+ // census. `danger-depth-census.ts --form <json>` re-runs that gate
25427
+ // for any candidate. A 1 px halo (one pass, not two) halves the
25428
+ // cost with placement byte-identical — a portrayal call, not taken.
25398
25429
  "symbol-sort-key": labelSortKey(100, ["*", 10, valsou])
25399
25430
  }
25400
25431
  };
@@ -28678,65 +28709,364 @@ function iconExpressionForFamily(objectClass2, family) {
28678
28709
  }
28679
28710
  }
28680
28711
 
28681
- // src/atlas/depth-label-size.ts
28682
- var DEPTH_LABEL_SIZES = ["standard", "large"];
28683
- var DEFAULT_DEPTH_LABEL_SIZE = "standard";
28684
- function parseDepthLabelSize(raw) {
28685
- if (!raw) return null;
28686
- const lower = raw.toLowerCase();
28687
- return DEPTH_LABEL_SIZES.find((s) => s === lower) ?? null;
28688
- }
28689
- var DEPTH_LABEL_CLASSES = /* @__PURE__ */ new Set([
28690
- "SOUNDG",
28691
- "DEPCNT",
28692
- "UWTROC",
28693
- "OBSTRN",
28694
- "WRECKS"
28695
- ]);
28696
- var SEMIBOLD = ["Open Sans Semibold"];
28697
- var HAZARD_DEPTH = {
28698
- textSize: ["interpolate", ["linear"], ["zoom"], 12, 12, 16, 15],
28699
- haloWidth: 1.4,
28700
- font: SEMIBOLD
28712
+ // src/atlas/chart-symbol-sizing.ts
28713
+ var CHART_SYMBOL_SIZES = ["landLabels", "waterLabels", "depthNumbers", "waypoints", "chartIcons"];
28714
+ var CHART_SYMBOL_SIZE_WIRE = {
28715
+ landLabels: "chart.landLabelScale",
28716
+ waterLabels: "chart.waterLabelScale",
28717
+ /** Proposed for CHRTKit; aligned with sail's ChartSymbolSizing before release. */
28718
+ depthNumbers: "chart.depthNumberScale",
28719
+ waypoints: "chart.waypointScale",
28720
+ chartIcons: "chart.chartIconScale"
28701
28721
  };
28702
- var LARGE = {
28703
- // Standard: 7 / 8 / 10 / 12 at z8 / 11 / 14 / 16, halo 1.0, Semibold.
28704
- SOUNDG: {
28705
- textSize: ["interpolate", ["linear"], ["zoom"], 8, 10, 11, 11, 14, 14, 16, 17],
28706
- haloWidth: 1.2,
28707
- font: SEMIBOLD
28708
- },
28709
- // Standard: 8 / 10 at z10 / 14, halo 1.2, Regular.
28710
- //
28711
- // ⚠ ~1.2x, NOT 1.4x, AND SEMIBOLD CARRIES THE REST. A contour value rides
28712
- // its line (`symbol-placement: line`) and needs a straight-enough run to fit;
28713
- // it also shares rank 300 with the soundings, which place first. At 1.4x the
28714
- // Boston harbour view kept 15 of Standard's 20 contour labels; at 1.2x, 18.
28715
- DEPCNT: {
28716
- textSize: ["interpolate", ["linear"], ["zoom"], 10, 10, 14, 12],
28717
- haloWidth: 1.2,
28718
- font: SEMIBOLD
28719
- },
28720
- UWTROC: HAZARD_DEPTH,
28721
- OBSTRN: HAZARD_DEPTH,
28722
- WRECKS: HAZARD_DEPTH
28722
+ var CHART_SYMBOL_SIZE_LABELS = {
28723
+ landLabels: "Land labels",
28724
+ waterLabels: "Water labels",
28725
+ depthNumbers: "Depth numbers",
28726
+ waypoints: "Waypoints",
28727
+ chartIcons: "Chart icons"
28723
28728
  };
28724
- var LARGE_SOUNDING_PADDING_PX = 3.5;
28725
- function applyDepthLabelSize(objectClass2, layer, size) {
28726
- if (size !== "large") return layer;
28727
- const large = LARGE[objectClass2];
28728
- if (large === void 0) return layer;
28729
- const layout = {
28730
- ...layer.layout ?? {},
28731
- "text-size": large.textSize,
28732
- "text-font": [...large.font]
28733
- };
28734
- if (objectClass2 === "SOUNDG") layout["text-padding"] = LARGE_SOUNDING_PADDING_PX;
28735
- return {
28736
- ...layer,
28737
- layout,
28738
- paint: { ...layer.paint, "text-halo-width": large.haloWidth }
28739
- };
28729
+ var CHART_SYMBOL_SIZE_REACH = {
28730
+ landLabels: "Towns, landmarks, buildings and land areas.",
28731
+ waterLabels: "Sea, channel, lake and river names, and clearances.",
28732
+ depthNumbers: "Soundings, contour values, and the depths beside rocks, wrecks and obstructions.",
28733
+ waypoints: "Your waypoints and route marks, and how far a tap reaches them.",
28734
+ chartIcons: "Buoys, beacons, lights, wrecks, rocks and their names."
28735
+ };
28736
+ var CHART_SYMBOL_SCALE = {
28737
+ min: 50,
28738
+ max: 200,
28739
+ standard: 100,
28740
+ /** The − / + step. */
28741
+ buttonStep: 10,
28742
+ /** The slider's snap. */
28743
+ sliderStep: 5
28744
+ };
28745
+ var STANDARD_CHART_SYMBOL_SCALES = {
28746
+ landLabels: 100,
28747
+ waterLabels: 100,
28748
+ depthNumbers: 100,
28749
+ waypoints: 100,
28750
+ chartIcons: 100
28751
+ };
28752
+ function clampChartSymbolScale(percent) {
28753
+ if (!Number.isFinite(percent)) return CHART_SYMBOL_SCALE.standard;
28754
+ return Math.min(Math.max(Math.round(percent), CHART_SYMBOL_SCALE.min), CHART_SYMBOL_SCALE.max);
28755
+ }
28756
+ function snapChartSymbolScale(value) {
28757
+ if (!Number.isFinite(value)) return CHART_SYMBOL_SCALE.standard;
28758
+ const step = CHART_SYMBOL_SCALE.sliderStep;
28759
+ return clampChartSymbolScale(Math.round(value / step) * step);
28760
+ }
28761
+ function stepChartSymbolScale(percent, presses) {
28762
+ return clampChartSymbolScale(percent + presses * CHART_SYMBOL_SCALE.buttonStep);
28763
+ }
28764
+ function decodeChartSymbolScale(raw) {
28765
+ if (typeof raw === "number") return Number.isFinite(raw) ? clampChartSymbolScale(raw) : null;
28766
+ if (typeof raw === "string" && /^\s*-?\d+(\.\d+)?\s*$/.test(raw)) return clampChartSymbolScale(Number(raw));
28767
+ return null;
28768
+ }
28769
+ var LAND_TEXT_CLASSES = /* @__PURE__ */ new Set([
28770
+ "BUAARE",
28771
+ "LNDARE",
28772
+ "LNDRGN",
28773
+ "AIRARE",
28774
+ "PRDARE",
28775
+ "LNDMRK",
28776
+ "BUISGL",
28777
+ "SLCONS",
28778
+ "HRBFAC",
28779
+ "BRIDGE"
28780
+ ]);
28781
+ var WATER_TEXT_CLASSES = /* @__PURE__ */ new Set([
28782
+ "SEAARE",
28783
+ "DRGNOTE",
28784
+ "SBDARE",
28785
+ "WEDKLP",
28786
+ "ACHARE",
28787
+ "LAKARE",
28788
+ "RIVERS",
28789
+ "CANALS",
28790
+ "CBLOHD",
28791
+ "PIPOHD"
28792
+ ]);
28793
+ var SYMBOL_ANNOTATION_TEXT_CLASSES = /* @__PURE__ */ new Set([
28794
+ "LIGHTS",
28795
+ "FOGSIG",
28796
+ "BOYCAR",
28797
+ "BOYINB",
28798
+ "BOYISD",
28799
+ "BOYLAT",
28800
+ "BOYSAW",
28801
+ "BOYSPP",
28802
+ "BCNCAR",
28803
+ "BCNISD",
28804
+ "BCNLAT",
28805
+ "BCNSAW",
28806
+ "BCNSPP",
28807
+ "MORFAC",
28808
+ "PILPNT"
28809
+ ]);
28810
+ var BASEMAP_LAND_TEXT = /* @__PURE__ */ new Set([
28811
+ "CHRT_LAND/land_town",
28812
+ "CHRT_LAND/land_summit",
28813
+ "CHRT_LAND/land_poi",
28814
+ "CHRT_BASE/state_label"
28815
+ ]);
28816
+ var BASEMAP_WATER_TEXT = /* @__PURE__ */ new Set([
28817
+ "CHRT_BASE/inland_water_label",
28818
+ "CHRT_BASE/inland_water_label_detail"
28819
+ ]);
28820
+ var BASEMAP_SOURCES = /* @__PURE__ */ new Set(["CHRT_LAND", "CHRT_BASE"]);
28821
+ var HAZARD_LABEL_PRIORITY = 100;
28822
+ var DEPTH_FIGURE_CLASSES = /* @__PURE__ */ new Set(["SOUNDG", "DEPCNT", "UWTROC", "OBSTRN", "WRECKS"]);
28823
+ function contourValueFactor(depthNumbersFactor) {
28824
+ return 1 + (depthNumbersFactor - 1) / 2;
28825
+ }
28826
+ var SERVED_FONT_STACKS = ["Open Sans Regular", "Open Sans Italic", "Open Sans Semibold"];
28827
+ function semiboldFontStack(font) {
28828
+ if (!Array.isArray(font) || !font.every((face) => typeof face === "string")) return font;
28829
+ return font.map((face) => {
28830
+ const semibold = face.replace(/ Regular$/, " Semibold");
28831
+ return SERVED_FONT_STACKS.includes(semibold) ? semibold : face;
28832
+ });
28833
+ }
28834
+ function factsOf(layer) {
28835
+ const l = layer;
28836
+ const source = typeof l.source === "string" ? l.source : void 0;
28837
+ const sourceLayer = typeof l["source-layer"] === "string" ? l["source-layer"] : void 0;
28838
+ const meta = l.metadata ?? {};
28839
+ const isBasemap = source !== void 0 && BASEMAP_SOURCES.has(source);
28840
+ const cls = meta["s52:objectClass"];
28841
+ const objectClass2 = isBasemap ? void 0 : (typeof cls === "string" ? cls : sourceLayer)?.toUpperCase();
28842
+ const p = meta["chrt:labelPriority"];
28843
+ return { source, sourceLayer, objectClass: objectClass2, labelPriority: typeof p === "number" ? p : void 0 };
28844
+ }
28845
+ function textSliderFor(layer) {
28846
+ const f = factsOf(layer);
28847
+ if (f.source !== void 0 && BASEMAP_SOURCES.has(f.source)) {
28848
+ const key = `${f.source}/${f.sourceLayer ?? ""}`;
28849
+ if (BASEMAP_LAND_TEXT.has(key)) return "landLabels";
28850
+ if (BASEMAP_WATER_TEXT.has(key)) return "waterLabels";
28851
+ return null;
28852
+ }
28853
+ const cls = f.objectClass;
28854
+ if (cls === void 0) return null;
28855
+ if (DEPTH_FIGURE_CLASSES.has(cls)) return "depthNumbers";
28856
+ if (LAND_TEXT_CLASSES.has(cls)) return "landLabels";
28857
+ if (WATER_TEXT_CLASSES.has(cls)) return "waterLabels";
28858
+ if (SYMBOL_ANNOTATION_TEXT_CLASSES.has(cls)) return "chartIcons";
28859
+ return null;
28860
+ }
28861
+ function iconSliderFor(layer) {
28862
+ const f = factsOf(layer);
28863
+ if (f.source !== void 0 && BASEMAP_SOURCES.has(f.source)) return null;
28864
+ return f.objectClass === void 0 ? null : "chartIcons";
28865
+ }
28866
+ function mentionsZoom(v) {
28867
+ if (Array.isArray(v)) {
28868
+ if (v.length === 1 && v[0] === "zoom") return true;
28869
+ return v.some(mentionsZoom);
28870
+ }
28871
+ if (v !== null && typeof v === "object") return Object.values(v).some(mentionsZoom);
28872
+ return false;
28873
+ }
28874
+ function scaledOutput(v, factor) {
28875
+ return typeof v === "number" ? round(v * factor) : ["*", v, factor];
28876
+ }
28877
+ function round(n) {
28878
+ return Math.round(n * 1e3) / 1e3;
28879
+ }
28880
+ function scaleSizeExpression(value, factor, fallback) {
28881
+ if (!Number.isFinite(factor) || factor <= 0) return null;
28882
+ if (value === void 0) return round(fallback * factor);
28883
+ if (factor === 1) return value;
28884
+ if (typeof value === "number") return round(value * factor);
28885
+ if (Array.isArray(value)) {
28886
+ const op = value[0];
28887
+ if (typeof op !== "string") return null;
28888
+ if (op === "interpolate" || op === "interpolate-hcl" || op === "interpolate-lab") {
28889
+ if (value.length < 5 || value.length % 2 !== 1) return null;
28890
+ const out = [...value];
28891
+ for (let i = 4; i < out.length; i += 2) out[i] = scaledOutput(out[i], factor);
28892
+ return out;
28893
+ }
28894
+ if (op === "step") {
28895
+ if (value.length < 3 || value.length % 2 !== 1) return null;
28896
+ const out = [...value];
28897
+ for (let i = 2; i < out.length; i += 2) out[i] = scaledOutput(out[i], factor);
28898
+ return out;
28899
+ }
28900
+ return mentionsZoom(value) ? null : ["*", value, factor];
28901
+ }
28902
+ if (value !== null && typeof value === "object") {
28903
+ const stops = value.stops;
28904
+ if (!Array.isArray(stops)) return null;
28905
+ const outStops = [];
28906
+ for (const stop of stops) {
28907
+ if (!Array.isArray(stop) || stop.length !== 2 || typeof stop[1] !== "number") return null;
28908
+ outStops.push([stop[0], round(stop[1] * factor)]);
28909
+ }
28910
+ return { ...value, stops: outStops };
28911
+ }
28912
+ return null;
28913
+ }
28914
+ function haloFactor(factor) {
28915
+ return factor > 1 ? 1 + (factor - 1) / 2 : 1;
28916
+ }
28917
+ var SERVED_SOUNDING_PADDING_PX = 6;
28918
+ var SOUNDING_PADDING_PER_FACTOR = 6.25;
28919
+ function soundingPadding(factor, served = SERVED_SOUNDING_PADDING_PX) {
28920
+ if (factor <= 1) return served;
28921
+ return round(Math.max(0, served - SOUNDING_PADDING_PER_FACTOR * (factor - 1)));
28922
+ }
28923
+ var MAPLIBRE_DEFAULT_TEXT_PADDING_PX = 2;
28924
+ function soundingPaddingValue(served, factor) {
28925
+ if (factor <= 1) return served;
28926
+ if (served === void 0) return soundingPadding(factor, MAPLIBRE_DEFAULT_TEXT_PADDING_PX);
28927
+ if (typeof served === "number") return soundingPadding(factor, served);
28928
+ if (!Array.isArray(served) || typeof served[0] !== "string") return served;
28929
+ const first = served[0] === "interpolate" ? 4 : served[0] === "step" ? 2 : null;
28930
+ if (first === null) return served;
28931
+ const out = [...served];
28932
+ for (let i = first; i < out.length; i += 2) {
28933
+ if (typeof out[i] !== "number") return served;
28934
+ out[i] = soundingPadding(factor, out[i]);
28935
+ }
28936
+ return out;
28937
+ }
28938
+ function factorOf(scales, size) {
28939
+ return clampChartSymbolScale(scales[size]) / 100;
28940
+ }
28941
+ var OUTRANK_LABELS = { kind: "outrankLabels" };
28942
+ var CHART_SOUNDINGS_RULE = { kind: "zoomSplit", harbourFrom: 14 };
28943
+ var KEPT_ABOVE_SOUNDINGS_CLASSES = /* @__PURE__ */ new Set(["SEAARE", "LNDARE", "LNDRGN"]);
28944
+ function keptAboveSoundings(layer, splittingLights = false) {
28945
+ const facts = factsOf(layer);
28946
+ if (splittingLights && facts.objectClass === "LIGHTS") return false;
28947
+ if (facts.labelPriority === HAZARD_LABEL_PRIORITY) return true;
28948
+ if (facts.source !== void 0 && BASEMAP_SOURCES.has(facts.source)) return true;
28949
+ return KEPT_ABOVE_SOUNDINGS_CLASSES.has(facts.objectClass ?? "");
28950
+ }
28951
+ function fromZoom(z, value) {
28952
+ return ["step", ["zoom"], value ?? false, z, true];
28953
+ }
28954
+ function labelSlidersOffStandard(scales) {
28955
+ return ["landLabels", "waterLabels", "depthNumbers", "chartIcons"].some((s) => clampChartSymbolScale(scales[s]) !== 100);
28956
+ }
28957
+ function applyChartSymbolScalesToLayer(layer, scales, yieldText = false) {
28958
+ if (layer.type !== "symbol") return layer;
28959
+ const layout = layer.layout ?? {};
28960
+ const hasText = layout["text-field"] !== void 0;
28961
+ const hasIcon = layout["icon-image"] !== void 0;
28962
+ if (!hasText && !hasIcon) return layer;
28963
+ const facts = factsOf(layer);
28964
+ const nextLayout = { ...layout };
28965
+ let nextPaint = null;
28966
+ let touched = false;
28967
+ if (hasText) {
28968
+ const slider = textSliderFor(layer);
28969
+ const isDepthFigure = slider === "depthNumbers";
28970
+ const sliderFactor = slider === null ? 1 : factorOf(scales, slider);
28971
+ const f = isDepthFigure && facts.objectClass === "DEPCNT" ? contourValueFactor(sliderFactor) : sliderFactor;
28972
+ const size = f === 1 ? null : scaleSizeExpression(layout["text-size"], f, 16);
28973
+ if (size !== null) {
28974
+ touched = true;
28975
+ nextLayout["text-size"] = size;
28976
+ if (f > 1) {
28977
+ if (isDepthFigure) {
28978
+ const semibold = semiboldFontStack(layout["text-font"]);
28979
+ if (JSON.stringify(semibold) !== JSON.stringify(layout["text-font"])) nextLayout["text-font"] = semibold;
28980
+ if (facts.objectClass === "SOUNDG") {
28981
+ const padding = soundingPaddingValue(layout["text-padding"], f);
28982
+ if (padding !== void 0) nextLayout["text-padding"] = padding;
28983
+ } else if (facts.labelPriority === HAZARD_LABEL_PRIORITY) {
28984
+ nextLayout["text-ignore-placement"] = true;
28985
+ nextLayout["text-allow-overlap"] = true;
28986
+ }
28987
+ } else {
28988
+ nextLayout["text-ignore-placement"] = true;
28989
+ if (facts.labelPriority === HAZARD_LABEL_PRIORITY) nextLayout["text-allow-overlap"] = true;
28990
+ }
28991
+ const halo = layer.paint?.["text-halo-width"];
28992
+ if (isDepthFigure && halo !== void 0) {
28993
+ const wider = scaleSizeExpression(halo, haloFactor(sliderFactor), 0);
28994
+ if (wider !== null) nextPaint = { ...layer.paint ?? {}, "text-halo-width": wider };
28995
+ }
28996
+ }
28997
+ }
28998
+ if (yieldText && facts.objectClass !== "SOUNDG") {
28999
+ touched = true;
29000
+ nextLayout["text-ignore-placement"] = true;
29001
+ }
29002
+ }
29003
+ if (hasIcon) {
29004
+ const slider = iconSliderFor(layer);
29005
+ const f = slider === null ? 1 : factorOf(scales, slider);
29006
+ const size = f === 1 ? null : scaleSizeExpression(layout["icon-size"], f, 1);
29007
+ if (size !== null) {
29008
+ touched = true;
29009
+ nextLayout["icon-size"] = size;
29010
+ if (f > 1) nextLayout["icon-ignore-placement"] = true;
29011
+ }
29012
+ }
29013
+ if (!touched) return layer;
29014
+ return { ...layer, layout: nextLayout, ...nextPaint ? { paint: nextPaint } : {} };
29015
+ }
29016
+ var isSoundingText = (l) => l.type === "symbol" && l.layout?.["text-field"] !== void 0 && factsOf(l).objectClass === "SOUNDG";
29017
+ function applyChartSymbolScales(layers, scales, rule = CHART_SOUNDINGS_RULE) {
29018
+ if (scales === void 0 || ["landLabels", "waterLabels", "depthNumbers", "chartIcons"].every((s) => clampChartSymbolScale(scales[s]) === 100)) {
29019
+ return layers;
29020
+ }
29021
+ const outrank = labelSlidersOffStandard(scales);
29022
+ const soundingsAt = layers.findIndex(isSoundingText);
29023
+ const split = rule.kind === "zoomSplit" ? rule : null;
29024
+ const demoted = [];
29025
+ const kept = [];
29026
+ layers.forEach((l, i) => {
29027
+ const above = outrank && soundingsAt !== -1 && i > soundingsAt;
29028
+ const hasText = l.type === "symbol" && l.layout?.["text-field"] !== void 0;
29029
+ if (split !== null && above && hasText && !isSoundingText(l) && !keptAboveSoundings(l, split.splitLights === true)) {
29030
+ const own = applyChartSymbolScalesToLayer(l, scales, false);
29031
+ const layout = own.layout ?? {};
29032
+ demoted.push({
29033
+ ...own,
29034
+ layout: {
29035
+ ...layout,
29036
+ "text-allow-overlap": fromZoom(split.harbourFrom, layout["text-allow-overlap"]),
29037
+ "text-ignore-placement": fromZoom(split.harbourFrom, layout["text-ignore-placement"])
29038
+ }
29039
+ });
29040
+ return;
29041
+ }
29042
+ kept.push(applyChartSymbolScalesToLayer(l, scales, above));
29043
+ });
29044
+ if (demoted.length === 0) return kept;
29045
+ const at = kept.findIndex(isSoundingText);
29046
+ return [...kept.slice(0, at), ...demoted, ...kept.slice(at)];
29047
+ }
29048
+ function demotedBelowSoundings(layers, scales, rule = CHART_SOUNDINGS_RULE) {
29049
+ if (scales === void 0 || rule.kind !== "zoomSplit" || !labelSlidersOffStandard(scales)) return [];
29050
+ const soundingsAt = layers.findIndex(isSoundingText);
29051
+ if (soundingsAt === -1) return [];
29052
+ return layers.filter((l, i) => i > soundingsAt && l.type === "symbol" && l.layout?.["text-field"] !== void 0 && !isSoundingText(l) && !keptAboveSoundings(l, rule.splitLights === true)).map((l) => l.id);
29053
+ }
29054
+ function chartSymbolSizingCensus(layers) {
29055
+ const unclassified = [];
29056
+ const unscalable = [];
29057
+ for (const l of layers) {
29058
+ if (l.type !== "symbol") continue;
29059
+ const layout = l.layout ?? {};
29060
+ if (layout["text-field"] !== void 0) {
29061
+ if (textSliderFor(l) === null) unclassified.push(`${l.id} text`);
29062
+ else if (scaleSizeExpression(layout["text-size"], 2, 16) === null) unscalable.push(`${l.id} text`);
29063
+ }
29064
+ if (layout["icon-image"] !== void 0) {
29065
+ if (iconSliderFor(l) === null) unclassified.push(`${l.id} icon`);
29066
+ else if (scaleSizeExpression(layout["icon-size"], 2, 1) === null) unscalable.push(`${l.id} icon`);
29067
+ }
29068
+ }
29069
+ return { unclassified, unscalable };
28740
29070
  }
28741
29071
 
28742
29072
  // src/atlas/base-bundle.ts
@@ -30079,7 +30409,7 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30079
30409
  if (getLabelBehavior(objectClass2) === "orientation-cross-band") {
30080
30410
  layer.filter = LABEL_ANCHOR_FILTER;
30081
30411
  }
30082
- return applyDepthLabelSize(objectClass2, layer, labelOpts.depthLabelSize);
30412
+ return layer;
30083
30413
  }
30084
30414
  function getGeomTypesForClass(objectClass2, defaultType) {
30085
30415
  if (SYMBOL_MAP[objectClass2] && defaultType === "symbol") {
@@ -31234,8 +31564,7 @@ function generateStyle(palette = "DAY", opts = {}) {
31234
31564
  label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
31235
31565
  safetyContour: opts.safetyContour,
31236
31566
  twoShadeSoundings: opts.twoShadeSoundings,
31237
- soundingFontWeight: opts.soundingFontWeight,
31238
- depthLabelSize: opts.depthLabelSize
31567
+ soundingFontWeight: opts.soundingFontWeight
31239
31568
  })
31240
31569
  });
31241
31570
  }
@@ -31660,7 +31989,9 @@ function generateStyle(palette = "DAY", opts = {}) {
31660
31989
  layers: finalLayers
31661
31990
  };
31662
31991
  const familyStyle = symbolSet === "s101" ? applyS101SymbolSet(style) : symbolSet === "chrt" ? applyChrtSymbolSet(style, palette) : symbolSet === "raster" ? applyRasterSymbolSet(style) : style;
31663
- return applyEinkPortrayal(familyStyle, palette, { safetyContour: opts.safetyContour });
31992
+ const portrayed = applyEinkPortrayal(familyStyle, palette, { safetyContour: opts.safetyContour });
31993
+ const sized = applyChartSymbolScales(portrayed.layers, opts.symbolScales, opts.soundingsRule);
31994
+ return sized === portrayed.layers ? portrayed : { ...portrayed, layers: sized };
31664
31995
  }
31665
31996
  function generateStyleJSON(palette = "DAY", opts = {}) {
31666
31997
  return JSON.stringify(generateStyle(palette, opts), null, 2);
@@ -32566,6 +32897,12 @@ export {
32566
32897
  CHART_BAND_CODES,
32567
32898
  CHART_CASCADE_CODES,
32568
32899
  CHART_SECTIONS,
32900
+ CHART_SOUNDINGS_RULE,
32901
+ CHART_SYMBOL_SCALE,
32902
+ CHART_SYMBOL_SIZES,
32903
+ CHART_SYMBOL_SIZE_LABELS,
32904
+ CHART_SYMBOL_SIZE_REACH,
32905
+ CHART_SYMBOL_SIZE_WIRE,
32569
32906
  CHRTRASTER_REF_CROPS,
32570
32907
  CHRTRASTER_SVGS,
32571
32908
  CHRT_ATON_COLORS,
@@ -32579,7 +32916,6 @@ export {
32579
32916
  ChrtError,
32580
32917
  DEFAULT_CLICK_TOLERANCE,
32581
32918
  DEFAULT_CUSTOM_STYLE_CONFIG,
32582
- DEFAULT_DEPTH_LABEL_SIZE,
32583
32919
  DEFAULT_INSPECT_CLASS_RANK,
32584
32920
  DEFAULT_LABEL_SETTINGS,
32585
32921
  DEFAULT_NAV_AID_SETTINGS,
@@ -32591,14 +32927,14 @@ export {
32591
32927
  DEFAULT_WATER_BODY_SETTINGS,
32592
32928
  DEPARE_FILL_BRANCH_TOKENS,
32593
32929
  DEPARE_FILL_FALLBACK_TOKEN,
32594
- DEPTH_LABEL_CLASSES,
32595
- DEPTH_LABEL_SIZES,
32930
+ DEPTH_FIGURE_CLASSES,
32596
32931
  DRAPEABLE_LAYER_TYPES,
32597
32932
  EINK_COLOR_TABLES,
32598
32933
  EINK_DANGER_PATTERN,
32599
32934
  EINK_DOTS_PATTERN,
32600
32935
  EINK_HATCH_PATTERN,
32601
32936
  EINK_MONO_GREYS,
32937
+ HAZARD_LABEL_PRIORITY,
32602
32938
  INSPECT_CLASS_RANK,
32603
32939
  INSPECT_RANK_DEVIATIONS,
32604
32940
  InteractClient,
@@ -32614,7 +32950,8 @@ export {
32614
32950
  LAND_ATTRIBUTION,
32615
32951
  LAND_BUILD_MAXZOOM,
32616
32952
  LAND_REGION,
32617
- LARGE_SOUNDING_PADDING_PX,
32953
+ LAND_TEXT_CLASSES,
32954
+ MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
32618
32955
  NAUTICAL_SYMBOLS,
32619
32956
  NAV_AID_LAYER_GROUPS,
32620
32957
  NAV_AID_QUERY_LAYERS,
@@ -32623,6 +32960,7 @@ export {
32623
32960
  NON_SOLAS_DISCLAIMER,
32624
32961
  NON_SOLAS_DISCLAIMER_SHORT,
32625
32962
  OPAQUE_SPLIT_OF,
32963
+ OUTRANK_LABELS,
32626
32964
  OfflineError,
32627
32965
  OfflineStyleError,
32628
32966
  PLACENAME_LAND_CLASSES,
@@ -32657,9 +32995,14 @@ export {
32657
32995
  S52_LOOKUP,
32658
32996
  S52_NIGHT,
32659
32997
  S52_PATTERN_SPRITE_PREFIX,
32998
+ SERVED_FONT_STACKS,
32999
+ SERVED_SOUNDING_PADDING_PX,
32660
33000
  SIGPER_TOLERANCE_SECONDS,
32661
33001
  SIGSEQ_MAX_TERMS,
32662
33002
  SOUNDING_LAYERS,
33003
+ SOUNDING_PADDING_PER_FACTOR,
33004
+ STANDARD_CHART_SYMBOL_SCALES,
33005
+ SYMBOL_ANNOTATION_TEXT_CLASSES,
32663
33006
  SYMBOL_FAMILIES,
32664
33007
  SYMBOL_FAMILY_META,
32665
33008
  SYMBOL_GROUPS,
@@ -32667,12 +33010,14 @@ export {
32667
33010
  TileAuth,
32668
33011
  TileTokenError,
32669
33012
  WATER_BODY_LAYER_GROUPS,
33013
+ WATER_TEXT_CLASSES,
32670
33014
  WeatherEndpoint,
32671
33015
  applyBaseBundle,
33016
+ applyChartSymbolScales,
33017
+ applyChartSymbolScalesToLayer,
32672
33018
  applyChrtSymbolSet,
32673
33019
  applyColorTheme,
32674
33020
  applyCustomStyleSettings,
32675
- applyDepthLabelSize,
32676
33021
  applyEinkPortrayal,
32677
33022
  applyRasterSymbolSet,
32678
33023
  applyS101SymbolSet,
@@ -32699,19 +33044,24 @@ export {
32699
33044
  capLuminance,
32700
33045
  cellBandCharacters,
32701
33046
  cellBandMatch,
33047
+ chartSymbolSizingCensus,
32702
33048
  chrtAtonTintExpression,
32703
33049
  chrtHouseColor,
32704
33050
  chrtLightTintExpression,
33051
+ clampChartSymbolScale,
32705
33052
  collectCandidates,
32706
33053
  collectClickCandidates,
32707
33054
  combineWithBandFilter,
33055
+ contourValueFactor,
32708
33056
  contrastRatio,
32709
33057
  daymarkColor,
32710
33058
  decimateFeatures,
32711
33059
  decodeAreaFeatureProps,
32712
33060
  decodeBridgeProps,
32713
33061
  decodeCandidate,
33062
+ decodeChartSymbolScale,
32714
33063
  decodeNavAidProps,
33064
+ demotedBelowSoundings,
32715
33065
  depareColor,
32716
33066
  depareFillExpression,
32717
33067
  depthAreaClassLayerId,
@@ -32721,6 +33071,7 @@ export {
32721
33071
  drapedFirst,
32722
33072
  extractNavAidDesignation,
32723
33073
  featureIdentity,
33074
+ fromZoom,
32724
33075
  generateCustomStyle,
32725
33076
  generateSpriteManifest,
32726
33077
  generateSpriteSheet,
@@ -32748,9 +33099,11 @@ export {
32748
33099
  getThemedS101Svg,
32749
33100
  getThemedS52Svg,
32750
33101
  getTopmarkMeaning,
33102
+ haloFactor,
32751
33103
  hazardColor,
32752
33104
  hazardFillExpression,
32753
33105
  iconExpressionForFamily,
33106
+ iconSliderFor,
32754
33107
  isDepthAreaClassLayerId,
32755
33108
  isDrapeable,
32756
33109
  isEinkColorTable,
@@ -32762,6 +33115,7 @@ export {
32762
33115
  isSoundingLayerVisible,
32763
33116
  isVisibleInMode,
32764
33117
  isWaterBodyLayerVisible,
33118
+ keptAboveSoundings,
32765
33119
  labelSortKey,
32766
33120
  lampRhythmColour,
32767
33121
  lampRhythmOpacity,
@@ -32774,7 +33128,6 @@ export {
32774
33128
  normalizeTheme,
32775
33129
  opaqueSplitBaseOf,
32776
33130
  paginate,
32777
- parseDepthLabelSize,
32778
33131
  parseListCodes,
32779
33132
  parseSigseqTerms,
32780
33133
  parseSymbolFamily,
@@ -32797,12 +33150,19 @@ export {
32797
33150
  safetyContourValues,
32798
33151
  sanitizeCustomStyleConfig,
32799
33152
  scaleLinearLight,
33153
+ scaleSizeExpression,
32800
33154
  scaleSizeValue,
32801
33155
  scaminToMinZoom,
32802
33156
  sectorLimitToArcBearing,
33157
+ semiboldFontStack,
33158
+ snapChartSymbolScale,
33159
+ soundingPadding,
33160
+ soundingPaddingValue,
32803
33161
  splitOpaqueDepthAreas,
33162
+ stepChartSymbolScale,
32804
33163
  styleModeBand,
32805
33164
  terrainStackCount,
33165
+ textSliderFor,
32806
33166
  themeChrtGlyph,
32807
33167
  tileTemplate,
32808
33168
  uncoveredSectorArcs,