@chrtco/sdk 0.10.15 → 0.10.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7483,6 +7483,16 @@ var S52_DRAW_ORDER = [
7483
7483
  // the visual-coherence fix for hard charcoal-vs-vector-blue seams at
7484
7484
  // scale-band boundaries — see `s52-lookup.ts`'s SEAARE entry for the
7485
7485
  // paired paint-spec change (fill-opacity 0 → 1, color → DEPDW).
7486
+ //
7487
+ // ⚠ 2026-08-23: within-band ordering alone was NOT enough. Under the
7488
+ // band-major interleave, band-N's SEAARE painted ABOVE band-(N-1)'s
7489
+ // DEPARE, and NOAA's named-bay SEAARE polygons outrun (and out-survive,
7490
+ // through tile simplification) their own band's DEPARE — the live
7491
+ // "white blobs" over Narragansett/Quincy/Hingham Bays. The style
7492
+ // generator now hoists every band's SEAARE fill below the whole
7493
+ // cascade (BOTTOM_STACK_WATER_BASE_CLASSES in style-generator.ts);
7494
+ // this within-band slot remains for the label layer and as the record
7495
+ // of the original intent.
7486
7496
  "SEAARE",
7487
7497
  "DEPARE",
7488
7498
  // 2a. Unsurveyed area — opaque grey wash painted ON TOP of any DEPARE
@@ -27012,11 +27022,13 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
27012
27022
  ["linear"],
27013
27023
  ["zoom"],
27014
27024
  10,
27015
- 0.3,
27025
+ 0.16,
27026
+ 11,
27027
+ 0.22,
27016
27028
  12,
27017
- 0.5,
27029
+ 0.3,
27018
27030
  13,
27019
- 0.65,
27031
+ 0.5,
27020
27032
  14,
27021
27033
  0.9,
27022
27034
  16,
@@ -27027,12 +27039,45 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
27027
27039
  layer.layout = {
27028
27040
  ...layer.layout,
27029
27041
  "icon-size": hazardIconSize,
27030
- "icon-allow-overlap": false,
27031
- "icon-optional": true,
27032
- // Pad each hazard's collision box so adjacent rocks don't stack
27033
- // visually. At z12–13 a 2 px pad roughly doubles the dedupe
27034
- // radius relative to the rendered icon, which is the difference
27035
- // between a black mass and individually-legible hazards.
27042
+ // ── A HAZARD IS NEVER SUPPRESSED BY COLLISION ────────────────────
27043
+ //
27044
+ // This was `allow-overlap: false, icon-optional: true`, chosen to stop
27045
+ // adjacent rocks stacking into a black mass — a real problem, and the
27046
+ // reasoning above it only ever considered rock-against-rock.
27047
+ //
27048
+ // The same two flags also made hazards lose against EVERY other
27049
+ // class, and the nav aids they lose to are `allow-overlap: true` and
27050
+ // so can never lose in return. A rock beside a buoy was dropped;
27051
+ // `icon-optional: true` means an icon that collides is simply not
27052
+ // drawn, and a hazard symbol carries no label to fall back to, so it
27053
+ // vanished with nothing left behind to notice. Observed at Sculpin
27054
+ // Ledge (42°39.6'N 070°36.9'W) at Harbor scale.
27055
+ //
27056
+ // Which hazard wins was also decided by an accident of stack
27057
+ // position: UWTROC sits at draw index 802, below the nav aids at
27058
+ // ~947, while WRECKS sits at 950 above them — so rocks beat buoys and
27059
+ // wrecks lost to them, for no reason anyone chose.
27060
+ //
27061
+ // Deconflicting hazards against each other stays; it just no longer
27062
+ // runs through suppression:
27063
+ // - `symbol-sort-key` (below) still ranks by danger, and with
27064
+ // overlap allowed it now sets DRAW ORDER, so the worst hazard is
27065
+ // the one painted on top rather than the one that survives.
27066
+ // - the icon-size curve above is what keeps a dense ledge legible
27067
+ // at the zoomed-out end of Harbor. That is now the only thing
27068
+ // doing it, which is why its low end was lowered in the same
27069
+ // change.
27070
+ //
27071
+ // ⚠ `icon-ignore-placement` is deliberately LEFT AT ITS DEFAULT
27072
+ // (false). Hazards no longer yield to anything, but they still
27073
+ // occupy the collision index, so a label that would sit on top of a
27074
+ // rock moves instead of covering it.
27075
+ "icon-allow-overlap": true,
27076
+ "icon-optional": false,
27077
+ // Retained, with a narrower job than it had. With overlap allowed
27078
+ // this no longer suppresses a neighbouring hazard; it pads the box
27079
+ // hazards present to OTHER classes' placement, keeping labels off
27080
+ // them.
27036
27081
  "icon-padding": 2,
27037
27082
  "symbol-sort-key": [
27038
27083
  "case",
@@ -28544,6 +28589,7 @@ function isDominanceExempt(layer) {
28544
28589
  return false;
28545
28590
  }
28546
28591
  var TOP_STACK_LIGHT_CLASSES = /* @__PURE__ */ new Set(["LIGHTS"]);
28592
+ var BOTTOM_STACK_WATER_BASE_CLASSES = /* @__PURE__ */ new Set(["SEAARE"]);
28547
28593
  function makeBandVariants(layer, B) {
28548
28594
  const range = BAND_PRIORITY_RANGES[B];
28549
28595
  const minzoom = bandVariantMinzoom(layer, B);
@@ -28743,8 +28789,13 @@ function generateStyle(palette = "DAY", opts = {}) {
28743
28789
  })
28744
28790
  });
28745
28791
  }
28746
- const regularBundles = bundles.filter((b) => !TOP_STACK_LIGHT_CLASSES.has(b.objectClass));
28792
+ const regularBundles = bundles.filter(
28793
+ (b) => !TOP_STACK_LIGHT_CLASSES.has(b.objectClass) && !BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
28794
+ );
28747
28795
  const topLightBundles = bundles.filter((b) => TOP_STACK_LIGHT_CLASSES.has(b.objectClass));
28796
+ const waterBaseBundles = bundles.filter(
28797
+ (b) => BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
28798
+ );
28748
28799
  if (chartCascade && chartRequestedCodes) {
28749
28800
  const requestedSet = new Set(chartRequestedCodes);
28750
28801
  const emitChartFeatureVariants = (bundleSet) => {
@@ -28772,6 +28823,7 @@ function generateStyle(palette = "DAY", opts = {}) {
28772
28823
  }
28773
28824
  }
28774
28825
  };
28826
+ emitChartFeatureVariants(waterBaseBundles);
28775
28827
  emitChartFeatureVariants(regularBundles);
28776
28828
  emitChartFeatureVariants(topLightBundles);
28777
28829
  const chartLabelBundles = byLabelPriorityBottomUp(
@@ -28812,6 +28864,13 @@ function generateStyle(palette = "DAY", opts = {}) {
28812
28864
  layers.push(...labelLayers);
28813
28865
  } else {
28814
28866
  const emitBand = (base, code) => bandFan === "legacy" ? makeBandVariants(base, code) : makeCollapsedBandVariant(base, code, isDominanceExempt(base));
28867
+ for (const code of BAND_DRAW_ORDER) {
28868
+ for (const { featureLayers } of waterBaseBundles) {
28869
+ for (const base of featureLayers) {
28870
+ layers.push(...emitBand(base, code));
28871
+ }
28872
+ }
28873
+ }
28815
28874
  for (const code of BAND_DRAW_ORDER) {
28816
28875
  for (const { featureLayers } of regularBundles) {
28817
28876
  for (const base of featureLayers) {
@@ -28855,10 +28914,12 @@ function generateStyle(palette = "DAY", opts = {}) {
28855
28914
  const symbolSet = opts.symbolSet ?? "s52";
28856
28915
  const spriteBase = `${opts.tileBaseUrl ?? "https://tiles.chrt.co"}/v1/sprites`;
28857
28916
  const isLndareFill = (l) => /^s52-LNDARE-fill-band\d+$/.test(l.id);
28917
+ const isSeaareFill = (l) => /^s52-SEAARE-fill-band\d+$/.test(l.id);
28858
28918
  const emitted = opts.land ? (() => {
28859
- const fills = layers.filter(isLndareFill);
28860
- const rest = layers.filter((l) => !isLndareFill(l));
28861
- return [rest[0], ...fills, ...rest.slice(1)];
28919
+ const lndareFills = layers.filter(isLndareFill);
28920
+ const seaareFills = layers.filter(isSeaareFill);
28921
+ const rest = layers.filter((l) => !isLndareFill(l) && !isSeaareFill(l));
28922
+ return [rest[0], ...seaareFills, ...lndareFills, ...rest.slice(1)];
28862
28923
  })() : layers;
28863
28924
  const style = {
28864
28925
  version: 8,