@chrtco/sdk 0.10.51 → 0.10.53

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
@@ -134,7 +134,7 @@ var TileTokenError = class extends Error {
134
134
  this.status = status;
135
135
  }
136
136
  };
137
- var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png)(?:\?.*)?$/i;
137
+ var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png|webp)(?:\?.*)?$/i;
138
138
  var STYLE_URL_PATH = /\/v1\/styles\/[A-Za-z0-9_]+\/style\.json(?:\?.*)?$/i;
139
139
  var REFRESH_MARGIN_SECONDS = 30;
140
140
  var STALE_GRACE_SECONDS = 1;
@@ -365,6 +365,11 @@ var LAND_BUILD_MAXZOOM = 14;
365
365
  var LAND_REGION = "CHRT_LAND";
366
366
  var RELIEF_REGION = "CHRT_RELIEF";
367
367
  var BASE_REGION = "CHRT_BASE";
368
+ var SEAFLOOR_REGION = "CHRT_SEAFLOOR";
369
+ var SEAFLOOR_ATTRIBUTION = "Seabed: NOAA OCS BlueTopo, NOAA NCEI CUDEM and Great Lakes Bathymetry, GEBCO Compilation Group (2026)";
370
+ var SEAFLOOR_MINZOOM = 4;
371
+ var SEAFLOOR_MAXZOOM = 13;
372
+ var SEAFLOOR_TILE_SIZE = 512;
368
373
  var BASE_MINZOOM = 0;
369
374
  var BASE_MAXZOOM = 11;
370
375
  var BASE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth \xB7 \xA9 OpenStreetMap contributors";
@@ -433,6 +438,16 @@ function buildSources(opts = {}) {
433
438
  attribution: RELIEF_ATTRIBUTION
434
439
  };
435
440
  }
441
+ if (opts.seafloor) {
442
+ sources[SEAFLOOR_REGION] = {
443
+ type: "raster",
444
+ tiles: [tileTemplate(base, SEAFLOOR_REGION, "webp", buildFor(SEAFLOOR_REGION))],
445
+ tileSize: SEAFLOOR_TILE_SIZE,
446
+ minzoom: SEAFLOOR_MINZOOM,
447
+ maxzoom: SEAFLOOR_MAXZOOM,
448
+ attribution: SEAFLOOR_ATTRIBUTION
449
+ };
450
+ }
436
451
  return sources;
437
452
  }
438
453
 
@@ -1898,6 +1913,70 @@ var CONVIS_CODES = {
1898
1913
  1: { code: 1, name: "Visually conspicuous", abbr: "conspic" },
1899
1914
  2: { code: 2, name: "Not visually conspicuous" }
1900
1915
  };
1916
+ var HORDAT_CODES = {
1917
+ 1: { code: 1, name: "WGS 72" },
1918
+ 2: { code: 2, name: "WGS 84" },
1919
+ 3: { code: 3, name: "European 1950" },
1920
+ 4: { code: 4, name: "Potsdam Datum" },
1921
+ 5: { code: 5, name: "Adindan" },
1922
+ 6: { code: 6, name: "Afgooye" },
1923
+ 7: { code: 7, name: "Ain el Abd 1970" },
1924
+ 8: { code: 8, name: "Anna 1 Astro 1965" },
1925
+ 9: { code: 9, name: "Antigua Island Astro 1943" },
1926
+ 10: { code: 10, name: "Arc 1950" },
1927
+ 11: { code: 11, name: "Arc 1960" },
1928
+ 12: { code: 12, name: "Ascension Island 1958" },
1929
+ 13: { code: 13, name: "Astro beacon \\E\\ 1945" },
1930
+ 14: { code: 14, name: "Astro DOS 71/4" },
1931
+ 15: { code: 15, name: "Astro Tern Island (FRIG) 1961" },
1932
+ 16: { code: 16, name: "Astronimical Station 1952" },
1933
+ 17: { code: 17, name: "Australian Geodetic 1966" },
1934
+ 18: { code: 18, name: "Australian Geodetic 1984" },
1935
+ 19: { code: 19, name: "Ayabelle Lighthouse" },
1936
+ 20: { code: 20, name: "Bellevue (IGN)" },
1937
+ 21: { code: 21, name: "Bermuda 1957" },
1938
+ 22: { code: 22, name: "Bissau" },
1939
+ 23: { code: 23, name: "Bogota Observatory" },
1940
+ 24: { code: 24, name: "Bukit Rimpah" },
1941
+ 25: { code: 25, name: "Camp Area Astro" },
1942
+ 26: { code: 26, name: "Campo Inchauspe 1969" },
1943
+ 27: { code: 27, name: "Canton Astro 1966" },
1944
+ 28: { code: 28, name: "Cape" },
1945
+ 29: { code: 29, name: "Cape Canaveral" },
1946
+ 30: { code: 30, name: "Carthage" },
1947
+ 31: { code: 31, name: "Chatam Island Astro 1971" },
1948
+ 32: { code: 32, name: "Chua Astro" },
1949
+ 33: { code: 33, name: "Corrego Alegre" },
1950
+ 34: { code: 34, name: "Dabola" },
1951
+ 35: { code: 35, name: "Djakarta (Batavia)" },
1952
+ 36: { code: 36, name: "DOS 1968" },
1953
+ 37: { code: 37, name: "Easter Island 1967" },
1954
+ 38: { code: 38, name: "European 1979" },
1955
+ 39: { code: 39, name: "Fort Thomas 1955" },
1956
+ 40: { code: 40, name: "Gan 1970" },
1957
+ 41: { code: 41, name: "Geodetic Datum 1949" },
1958
+ 42: { code: 42, name: "Graciosa Base SW 1948" },
1959
+ 43: { code: 43, name: "Guam 1963" },
1960
+ 44: { code: 44, name: "Ganung Segara" },
1961
+ 45: { code: 45, name: "GUX 1 Astro" },
1962
+ 46: { code: 46, name: "Herat North" },
1963
+ 47: { code: 47, name: "Hjorsey 1955" },
1964
+ 48: { code: 48, name: "Hong Kong 1963" },
1965
+ 49: { code: 49, name: "Hu-Tzu-Shan" },
1966
+ 50: { code: 50, name: "Indian" },
1967
+ 51: { code: 51, name: "Indian 1954" },
1968
+ 52: { code: 52, name: "Indian 1975" },
1969
+ 53: { code: 53, name: "Ireland 1965" },
1970
+ 54: { code: 54, name: "ISTS 061 Astro 1968" },
1971
+ 55: { code: 55, name: "ISTS 073 Astro 1969" },
1972
+ 56: { code: 56, name: "Johnston Island 1961" },
1973
+ 57: { code: 57, name: "Kandawala" },
1974
+ 58: { code: 58, name: "Kerguelen Island 1949" },
1975
+ 59: { code: 59, name: "Kertau 1948" },
1976
+ 60: { code: 60, name: "Kusaie Astro 1951" },
1977
+ 99: { code: 99, name: "South Asia" },
1978
+ 100: { code: 100, name: "Tananarive Observatory 1925" }
1979
+ };
1901
1980
  var LITCHR_CODES = {
1902
1981
  1: { code: 1, name: "Fixed", abbr: "F" },
1903
1982
  2: { code: 2, name: "Flashing", abbr: "Fl" },
@@ -2067,6 +2146,37 @@ var TRAFIC_CODES = {
2067
2146
  3: { code: 3, name: "One-way" },
2068
2147
  4: { code: 4, name: "Two-way" }
2069
2148
  };
2149
+ var VERDAT_CODES = {
2150
+ 1: { code: 1, name: "Mean low water springs" },
2151
+ 2: { code: 2, name: "Mean lower low water springs" },
2152
+ 3: { code: 3, name: "Mean sea level" },
2153
+ 4: { code: 4, name: "Lowest low water" },
2154
+ 5: { code: 5, name: "Mean low water" },
2155
+ 6: { code: 6, name: "Lowest low water springs" },
2156
+ 7: { code: 7, name: "Approximate mean low water springs" },
2157
+ 8: { code: 8, name: "Indian spring low water" },
2158
+ 9: { code: 9, name: "Low water springs" },
2159
+ 10: { code: 10, name: "Approximate lowest astronomical tide" },
2160
+ 11: { code: 11, name: "Nearly lowest low water" },
2161
+ 12: { code: 12, name: "Mean lower low water" },
2162
+ 13: { code: 13, name: "Low water" },
2163
+ 14: { code: 14, name: "Approximate mean low water" },
2164
+ 15: { code: 15, name: "Approximate mean lower low water" },
2165
+ 16: { code: 16, name: "Mean high water" },
2166
+ 17: { code: 17, name: "Mean high water springs" },
2167
+ 18: { code: 18, name: "High water" },
2168
+ 19: { code: 19, name: "Approximate mean sea level" },
2169
+ 20: { code: 20, name: "High water springs" },
2170
+ 21: { code: 21, name: "Mean higher high water" },
2171
+ 22: { code: 22, name: "Equinoctial spring low water" },
2172
+ 23: { code: 23, name: "Lowest astronomical tide" },
2173
+ 24: { code: 24, name: "Local datum" },
2174
+ 25: { code: 25, name: "International Great Lakes Datum 1985" },
2175
+ 26: { code: 26, name: "Mean water level" },
2176
+ 27: { code: 27, name: "Lower low water large tide" },
2177
+ 28: { code: 28, name: "Higher high water large tide" },
2178
+ 29: { code: 29, name: "Nearly highest high water" }
2179
+ };
2070
2180
  var WATLEV_CODES = {
2071
2181
  1: { code: 1, name: "Partly submerged at high water" },
2072
2182
  2: { code: 2, name: "Always dry" },
@@ -2102,6 +2212,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2102
2212
  ["COLPAT", COLPAT_CODES],
2103
2213
  ["CONDTN", CONDTN_CODES],
2104
2214
  ["CONVIS", CONVIS_CODES],
2215
+ ["HORDAT", HORDAT_CODES],
2105
2216
  ["LITCHR", LITCHR_CODES],
2106
2217
  ["MARSYS", MARSYS_CODES],
2107
2218
  ["NATCON", NATCON_CODES],
@@ -2112,6 +2223,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2112
2223
  ["STATUS", STATUS_CODES],
2113
2224
  ["TOPSHP", TOPSHP_CODES],
2114
2225
  ["TRAFIC", TRAFIC_CODES],
2226
+ ["VERDAT", VERDAT_CODES],
2115
2227
  ["WATLEV", WATLEV_CODES]
2116
2228
  ]);
2117
2229
  var S57_DOMAINS = new Map(
@@ -5344,7 +5456,7 @@ var EntitlementClient = class {
5344
5456
  }
5345
5457
  if (!res.ok) {
5346
5458
  const code = mapDenialCode(res.body?.error, res.status);
5347
- const message = typeof res.body?.message === "string" ? res.body.message : code === "interact_not_enabled" ? "CHRTinteract requires the Dev plan or above. Upgrade at https://chrt.co/pricing." : `entitlement check failed (HTTP ${res.status})`;
5459
+ const message = typeof res.body?.message === "string" ? res.body.message : code === "interact_not_enabled" ? "CHRTinteract requires the Dev plan or above. Upgrade at https://platform.chrt.co/plans." : `entitlement check failed (HTTP ${res.status})`;
5348
5460
  const plan = typeof res.body?.plan === "string" ? res.body.plan : void 0;
5349
5461
  throw new InteractError(code, message, { status: res.status, plan });
5350
5462
  }
@@ -5913,6 +6025,32 @@ var InteractClient = class {
5913
6025
  }
5914
6026
  };
5915
6027
 
6028
+ // src/atlas/seafloor-layers.ts
6029
+ var SEAFLOOR_LAYER_ID = "chrtseafloor-shade";
6030
+ var SEAFLOOR_BAKE = Object.freeze({
6031
+ rgb: Object.freeze([47, 72, 88]),
6032
+ kneeAlpha: 16,
6033
+ protectedSafetyContourM: 10,
6034
+ alphaMax: 90
6035
+ });
6036
+ var SEAFLOOR_THEME_PAINT = Object.freeze({
6037
+ DAY: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 1 },
6038
+ DUSK: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 0.8 },
6039
+ NIGHT: { "raster-brightness-min": 0.55, "raster-brightness-max": 1, "raster-opacity": 0.55 }
6040
+ });
6041
+ function seafloorLayers(palette = "DAY") {
6042
+ if (palette !== "DAY" && palette !== "DUSK" && palette !== "NIGHT") return [];
6043
+ const p = SEAFLOOR_THEME_PAINT[palette];
6044
+ const layer = {
6045
+ id: SEAFLOOR_LAYER_ID,
6046
+ type: "raster",
6047
+ source: SEAFLOOR_REGION,
6048
+ minzoom: SEAFLOOR_MINZOOM,
6049
+ paint: { ...p }
6050
+ };
6051
+ return [layer];
6052
+ }
6053
+
5916
6054
  // src/atlas/chrt-svgs-generated.ts
5917
6055
  var CHRT_GLYPH_SVGS = {
5918
6056
  "badge-radar": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="19" r="2.6" fill="#D200D2"/><path d="M9 19a7 7 0 0 1 14 0M5 19a11 11 0 0 1 22 0" stroke="#D200D2" stroke-width="2.4" fill="none" stroke-linecap="round" stroke-dasharray="4 3"/></svg>',
@@ -7387,7 +7525,7 @@ function applyEinkPortrayal(style, palette, opts = {}) {
7387
7525
  for (const original of style.layers) {
7388
7526
  const id = original.id;
7389
7527
  const type = original.type;
7390
- if (type === "hillshade" || id.startsWith("chrtland-fade-")) continue;
7528
+ if (type === "hillshade" || id.startsWith("chrtland-fade-") || id === SEAFLOOR_LAYER_ID) continue;
7391
7529
  const layer = structuredClone(original);
7392
7530
  const paint = layer.paint ??= {};
7393
7531
  const layout = layer.layout;
@@ -25651,6 +25789,8 @@ var ORIENTATION_LABEL_PRIORITY = 900;
25651
25789
  var MAJOR_WATER_TIER_MAX = 3;
25652
25790
  var MAJOR_WATER_LABEL_PRIORITY = 280;
25653
25791
  var ISLAND_LABEL_HOIST_MINZOOM = 12;
25792
+ var LIGHTHOUSE_NAME_LABEL_PRIORITY = 270;
25793
+ var LIGHTHOUSE_NAME_FILTER = ["all", listAttrHas("FUNCTN", ["33"]), HAS_OBJNAM];
25654
25794
  function getLabelPriority(layerName) {
25655
25795
  if (getLabelBehavior(layerName) === "orientation-cross-band") {
25656
25796
  return ORIENTATION_LABEL_PRIORITY;
@@ -26628,7 +26768,7 @@ function preprocessSoundings(fc) {
26628
26768
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26629
26769
  properties: {
26630
26770
  ...f.properties,
26631
- depth: Math.round(depth * 10) / 10 + ""
26771
+ depth: Math.floor(depth * 10) / 10 + ""
26632
26772
  }
26633
26773
  });
26634
26774
  }
@@ -26641,7 +26781,7 @@ function preprocessSoundings(fc) {
26641
26781
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26642
26782
  properties: {
26643
26783
  ...f.properties,
26644
- depth: Math.round(depth * 10) / 10 + ""
26784
+ depth: Math.floor(depth * 10) / 10 + ""
26645
26785
  }
26646
26786
  });
26647
26787
  }
@@ -26926,7 +27066,7 @@ function dredgeNoteSize() {
26926
27066
  return ["interpolate", ["exponential", 2], ["zoom"], ...stops];
26927
27067
  }
26928
27068
  function noteDepthText(prop, depthUnits) {
26929
- return depthUnits === "feet" ? ["concat", ["to-string", ["round", ["*", ["to-number", ["get", prop]], 3.28084]]], " FT"] : ["concat", ["to-string", ["get", prop]], " M"];
27069
+ return depthUnits === "feet" ? ["concat", ["to-string", ["floor", ["*", ["to-number", ["get", prop]], 3.28084]]], " FT"] : ["concat", ["to-string", ["get", prop]], " M"];
26930
27070
  }
26931
27071
  function buildDredgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters") {
26932
27072
  const widthProp = depthUnits === "feet" ? "width_ft" : "width_m";
@@ -27092,11 +27232,11 @@ function buildBridgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters")
27092
27232
  const unit = imperial ? " FT" : " M";
27093
27233
  const measure = (prop) => imperial ? [
27094
27234
  "concat",
27095
- ["to-string", ["floor", ["+", 0.5, ["*", 3.28084, ["to-number", ["get", prop]]]]]],
27235
+ ["to-string", ["floor", ["*", 3.28084, ["to-number", ["get", prop]]]]],
27096
27236
  unit
27097
27237
  ] : [
27098
27238
  "concat",
27099
- ["to-string", ["/", ["floor", ["+", 0.5, ["*", 10, ["to-number", ["get", prop]]]]], 10]],
27239
+ ["to-string", ["/", ["floor", ["*", 10, ["to-number", ["get", prop]]]], 10]],
27100
27240
  unit
27101
27241
  ];
27102
27242
  const clearance = (prop, prefix, suffix = "") => [
@@ -30102,6 +30242,15 @@ function buildLayersForClass(objectClass2, ct, sourceId = DEFAULT_REGION, opts =
30102
30242
  }
30103
30243
  return layers;
30104
30244
  }
30245
+ function dangerDepthMetresDown() {
30246
+ const valsou = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0], 0];
30247
+ return [
30248
+ "case",
30249
+ [">", valsou, 0],
30250
+ ["concat", ["to-string", ["/", ["floor", ["+", ["*", valsou, 10], 1e-6]], 10]], "m"],
30251
+ ""
30252
+ ];
30253
+ }
30105
30254
  function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
30106
30255
  if (!LABELED_LAYERS.has(objectClass2)) return null;
30107
30256
  const config = getLabelConfig(objectClass2, {
@@ -30126,7 +30275,7 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30126
30275
  textField = [
30127
30276
  "concat",
30128
30277
  [
30129
- "round",
30278
+ "floor",
30130
30279
  [
30131
30280
  "*",
30132
30281
  [
@@ -30154,10 +30303,16 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30154
30303
  textField = [
30155
30304
  "case",
30156
30305
  [">", valsouFt, 0],
30157
- ["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
30306
+ [
30307
+ "concat",
30308
+ ["to-string", [labelOpts.dangerDepthRounding === "down" ? "floor" : "round", ["*", valsouFt, 3.28084]]],
30309
+ "ft"
30310
+ ],
30158
30311
  ""
30159
30312
  ];
30160
30313
  }
30314
+ } else if (labelOpts.dangerDepthRounding === "down" && (objectClass2 === "UWTROC" || objectClass2 === "OBSTRN" || objectClass2 === "WRECKS")) {
30315
+ textField = dangerDepthMetresDown();
30161
30316
  }
30162
30317
  const labelPriority = getLabelPriority(objectClass2);
30163
30318
  const layer = {
@@ -31027,7 +31182,14 @@ var DEPTH_FIGURE_CLASSES = /* @__PURE__ */ new Set(["SOUNDG", "DEPCNT", "UWTROC"
31027
31182
  function contourValueFactor(depthNumbersFactor) {
31028
31183
  return 1 + (depthNumbersFactor - 1) / 2;
31029
31184
  }
31030
- var SERVED_FONT_STACKS = ["Open Sans Regular", "Open Sans Italic", "Open Sans Semibold"];
31185
+ var SERVED_FONT_STACKS = [
31186
+ "Open Sans Regular",
31187
+ "Open Sans Italic",
31188
+ "Open Sans Semibold",
31189
+ "Old Standard TT Regular",
31190
+ "Old Standard TT Italic",
31191
+ "Old Standard TT Bold"
31192
+ ];
31031
31193
  function semiboldFontStack(font) {
31032
31194
  if (!Array.isArray(font) || !font.every((face) => typeof face === "string")) return font;
31033
31195
  return font.map((face) => {
@@ -31457,8 +31619,54 @@ function bandVariantMinzoom(layer, B) {
31457
31619
  if (isGroundFill(layer)) {
31458
31620
  return BAND_PRIORITY_RANGES[B].minzoom;
31459
31621
  }
31622
+ if (COARSE_MARK_BANDS.has(B) && isCoarseNonAidMark(layer)) {
31623
+ return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom, COARSE_MARK_FLOOR);
31624
+ }
31460
31625
  return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
31461
31626
  }
31627
+ var AID_TO_NAVIGATION_CLASSES = /* @__PURE__ */ new Set([
31628
+ "BOYLAT",
31629
+ "BOYCAR",
31630
+ "BOYSPP",
31631
+ "BOYISD",
31632
+ "BOYSAW",
31633
+ "BOYINB",
31634
+ "BCNLAT",
31635
+ "BCNCAR",
31636
+ "BCNSPP",
31637
+ "BCNISD",
31638
+ "BCNSAW",
31639
+ "LIGHTS",
31640
+ "LITFLT",
31641
+ "LITVES",
31642
+ "FOGSIG",
31643
+ "TOPMAR",
31644
+ "DAYMAR",
31645
+ "RTPBCN",
31646
+ "RDOSTA",
31647
+ "RADRFL",
31648
+ "RETRFL"
31649
+ ]);
31650
+ var COARSE_MARK_BANDS = /* @__PURE__ */ new Set(["1", "2"]);
31651
+ var COARSE_MARK_HELD_HAZARDS = /* @__PURE__ */ new Set(["WRECKS", "OBSTRN", "UWTROC"]);
31652
+ var COARSE_MARK_FLOOR = BAND_PRIORITY_RANGES["3"].minzoom;
31653
+ function bandFloorGate() {
31654
+ return [
31655
+ "any",
31656
+ ...BAND_DRAW_ORDER.map((B) => {
31657
+ const floor = BAND_PRIORITY_RANGES[B].minzoom;
31658
+ return floor > 0 ? ["all", bandFilterExpression(B), [">=", ["zoom"], floor]] : bandFilterExpression(B);
31659
+ })
31660
+ ];
31661
+ }
31662
+ function isCoarseNonAidMark(layer) {
31663
+ if (layer.type !== "symbol" && layer.type !== "circle") return false;
31664
+ const cls = /^s52-([A-Za-z]{6})(?:-|$)/.exec(layer.id)?.[1]?.toUpperCase();
31665
+ if (!cls || cls === "SOUNDG" || AID_TO_NAVIGATION_CLASSES.has(cls) || COARSE_MARK_HELD_HAZARDS.has(cls)) return false;
31666
+ if (layer.type === "circle") return true;
31667
+ const layout = layer.layout;
31668
+ return typeof layout === "object" && layout !== null && "icon-image" in layout;
31669
+ }
31462
31670
  var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
31463
31671
  "BUAARE",
31464
31672
  // 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
@@ -31956,7 +32164,7 @@ function landLayers(opts = {}) {
31956
32164
  minzoom: 11.5,
31957
32165
  filter: ["all", FITS, [">=", tileNumber("peak_m", 0), 40]],
31958
32166
  layout: {
31959
- "text-field": ["concat", ["get", "name"], "\n", ["to-string", ["get", "peak_m"]], " m"],
32167
+ "text-field": opts.heightUnits === "feet" ? ["concat", ["get", "name"], "\n", ["to-string", ["round", ["*", ["to-number", ["get", "peak_m"], 0], 3.28084]]], " ft"] : ["concat", ["get", "name"], "\n", ["to-string", ["get", "peak_m"]], " m"],
31960
32168
  "text-font": FONT,
31961
32169
  "text-size": ["interpolate", ["linear"], ["zoom"], 11.5, 8.5, 14, 11],
31962
32170
  "text-offset": anchorOffset(0.8),
@@ -32321,6 +32529,23 @@ function spliceByLabelPriority(labelLayers, incoming, priority) {
32321
32529
  if (at === -1) labelLayers.push(...incoming);
32322
32530
  else labelLayers.splice(at, 0, ...incoming);
32323
32531
  }
32532
+ function hoistLighthouseNames(labelLayers) {
32533
+ const hoisted = [];
32534
+ for (const l of labelLayers) {
32535
+ if (!l.id.startsWith("s52-LNDMRK-label")) continue;
32536
+ hoisted.push({
32537
+ ...l,
32538
+ // `s52-LNDMRK-light-label…`, keeping any `-band{B}` tail last: the shape
32539
+ // of the danger-depth placers (`s52-UWTROC-placer-label`), which
32540
+ // custom-style's bandFloor() and the band-collapse id checks read.
32541
+ id: l.id.replace("s52-LNDMRK-label", "s52-LNDMRK-light-label"),
32542
+ filter: combineWithBandFilter(l.filter, LIGHTHOUSE_NAME_FILTER),
32543
+ metadata: { ...l.metadata ?? {}, "chrt:labelPriority": LIGHTHOUSE_NAME_LABEL_PRIORITY }
32544
+ });
32545
+ l.filter = combineWithBandFilter(l.filter, ["!", LIGHTHOUSE_NAME_FILTER]);
32546
+ }
32547
+ spliceByLabelPriority(labelLayers, hoisted, LIGHTHOUSE_NAME_LABEL_PRIORITY);
32548
+ }
32324
32549
  function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32325
32550
  const parts = [bandFilterExpression(B)];
32326
32551
  if (!isDominanceExempt(layer)) {
@@ -32333,6 +32558,9 @@ function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequ
32333
32558
  }
32334
32559
  return parts;
32335
32560
  }
32561
+ function strictCascade(finest) {
32562
+ return BAND_DRAW_ORDER.slice(0, BAND_DRAW_ORDER.indexOf(finest) + 1);
32563
+ }
32336
32564
  function chartModeFilterForBand(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32337
32565
  const [first, ...rest] = chartModeBandClauses(
32338
32566
  layer,
@@ -32366,7 +32594,8 @@ function makeCollapsedChartLabel(label, cascade, requestedSet, lockedBounds) {
32366
32594
  return collapsed;
32367
32595
  }
32368
32596
  function generateStyle(palette = "DAY", opts = {}) {
32369
- const sources = buildSources(opts);
32597
+ const seafloor = Boolean(opts.seafloor && opts.land) && seafloorLayers(palette).length > 0;
32598
+ const sources = buildSources({ ...opts, seafloor });
32370
32599
  const sourceId = opts.region ?? DEFAULT_REGION;
32371
32600
  const orderedClasses = getOrderedClasses();
32372
32601
  const backgroundLayer = {
@@ -32408,7 +32637,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32408
32637
  if (opts.land) {
32409
32638
  layers.push(...landLayers({
32410
32639
  palette,
32411
- ...opts.relief ? { relief: true } : {}
32640
+ ...opts.relief ? { relief: true } : {},
32641
+ ...opts.depthUnits === "feet" ? { heightUnits: "feet" } : {}
32412
32642
  }));
32413
32643
  }
32414
32644
  if (opts.base) {
@@ -32416,8 +32646,9 @@ function generateStyle(palette = "DAY", opts = {}) {
32416
32646
  }
32417
32647
  const labelLayers = [];
32418
32648
  const majorWaterLayers = [];
32419
- const chartCascade = opts.chartMode ? CHART_CASCADE_CODES[opts.chartMode.band] : null;
32420
- const chartRequestedCodes = opts.chartMode ? CHART_BAND_CODES[opts.chartMode.band] : null;
32649
+ const finestBand = opts.chartMode?.finestBand;
32650
+ const chartCascade = opts.chartMode ? finestBand ? strictCascade(finestBand) : CHART_CASCADE_CODES[opts.chartMode.band] : null;
32651
+ const chartRequestedCodes = opts.chartMode ? finestBand ? [finestBand] : CHART_BAND_CODES[opts.chartMode.band] : null;
32421
32652
  const bandFan = opts.bandFan ?? "collapsed";
32422
32653
  const lockedBounds = opts.chartMode?.lockedBounds;
32423
32654
  const bundles = [];
@@ -32431,7 +32662,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32431
32662
  label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
32432
32663
  safetyContour: opts.safetyContour,
32433
32664
  twoShadeSoundings: opts.twoShadeSoundings,
32434
- soundingFontWeight: opts.soundingFontWeight
32665
+ soundingFontWeight: opts.soundingFontWeight,
32666
+ ...opts.dangerDepthRounding ? { dangerDepthRounding: opts.dangerDepthRounding } : {}
32435
32667
  })
32436
32668
  });
32437
32669
  }
@@ -32474,7 +32706,12 @@ function generateStyle(palette = "DAY", opts = {}) {
32474
32706
  emitChartFeatureVariants(waterBaseBundles);
32475
32707
  emitChartFeatureVariants(regularBundles);
32476
32708
  emitChartFeatureVariants(topLightBundles);
32477
- layers.push(...sectorLightLayers, ...lampLayers);
32709
+ const cascadeGate = finestBand ? bandFilterExpression(chartCascade) : null;
32710
+ layers.push(
32711
+ ...[...sectorLightLayers, ...lampLayers].map(
32712
+ (l) => cascadeGate ? { ...l, filter: combineWithBandFilter(l.filter, cascadeGate) } : l
32713
+ )
32714
+ );
32478
32715
  const chartLabelBundles = byLabelPriorityBottomUp(
32479
32716
  bundles.filter(
32480
32717
  (b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
@@ -32528,6 +32765,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32528
32765
  ),
32529
32766
  BRGNOTE_LABEL_PRIORITY
32530
32767
  );
32768
+ hoistLighthouseNames(labelLayers);
32531
32769
  layers.push(...labelLayers);
32532
32770
  } else {
32533
32771
  const emitBand = (base, code) => bandFan === "legacy" ? makeBandVariants(base, code) : makeCollapsedBandVariant(base, code, isDominanceExempt(base));
@@ -32552,7 +32790,11 @@ function generateStyle(palette = "DAY", opts = {}) {
32552
32790
  }
32553
32791
  }
32554
32792
  }
32555
- layers.push(...sectorLightLayers, ...lampLayers);
32793
+ const sectorGate = bandFloorGate();
32794
+ layers.push(
32795
+ ...sectorLightLayers.map((l) => ({ ...l, filter: combineWithBandFilter(l.filter, sectorGate) })),
32796
+ ...lampLayers
32797
+ );
32556
32798
  {
32557
32799
  const TERMINAL_FILL_RE = new RegExp(
32558
32800
  `^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
@@ -32634,6 +32876,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32634
32876
  ),
32635
32877
  BRGNOTE_LABEL_PRIORITY
32636
32878
  );
32879
+ hoistLighthouseNames(labelLayers);
32637
32880
  layers.push(...labelLayers);
32638
32881
  }
32639
32882
  const symbolSet = opts.symbolSet ?? "s52";
@@ -32805,6 +33048,14 @@ function generateStyle(palette = "DAY", opts = {}) {
32805
33048
  emitted.push(layer);
32806
33049
  }
32807
33050
  }
33051
+ if (seafloor) {
33052
+ const idAt = (i) => emitted[i].id;
33053
+ let lastDepthFill = -1;
33054
+ for (let i = 0; i < emitted.length; i++) {
33055
+ if (/^s52-(DEPARE|DRGARE)-fill-band\d+/.test(idAt(i))) lastDepthFill = i;
33056
+ }
33057
+ if (lastDepthFill !== -1) emitted.splice(lastDepthFill + 1, 0, ...seafloorLayers(palette));
33058
+ }
32808
33059
  const finalLayers = drapedFirst(emitted);
32809
33060
  const style = {
32810
33061
  version: 8,
@@ -32818,6 +33069,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32818
33069
  "chrt:land": Boolean(opts.land),
32819
33070
  "chrt:relief": Boolean(opts.relief),
32820
33071
  "chrt:base": Boolean(opts.base),
33072
+ "chrt:seafloor": seafloor,
32821
33073
  "chrt:render-mode": opts.chartMode ? `chart:${opts.chartMode.band}` : "continuous",
32822
33074
  "chrt:description": "IHO S-52 portrayal rules for Electronic Navigational Charts (ENCs). Generated from the CHRT atlas engine."
32823
33075
  },
@@ -33748,6 +34000,7 @@ var Chrt = class {
33748
34000
  }
33749
34001
  };
33750
34002
  export {
34003
+ AID_TO_NAVIGATION_CLASSES,
33751
34004
  ALL_NAV_AID_LAYERS,
33752
34005
  ALL_WATER_BODY_LAYERS,
33753
34006
  AREA_QUERY_LAYERS,
@@ -33787,6 +34040,8 @@ export {
33787
34040
  CHRT_ICON_PREFIX,
33788
34041
  CHRT_SDF_GLYPHS,
33789
34042
  CHRT_STYLE_MODES,
34043
+ COARSE_MARK_FLOOR,
34044
+ COARSE_MARK_HELD_HAZARDS,
33790
34045
  Chrt,
33791
34046
  ChrtError,
33792
34047
  DANGER_DEPTH_LABEL_ID,
@@ -33831,6 +34086,8 @@ export {
33831
34086
  LAND_BUILD_MAXZOOM,
33832
34087
  LAND_REGION,
33833
34088
  LAND_TEXT_CLASSES,
34089
+ LIGHTHOUSE_NAME_FILTER,
34090
+ LIGHTHOUSE_NAME_LABEL_PRIORITY,
33834
34091
  LIGHT_LABEL_FEET_PER_METRE,
33835
34092
  MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
33836
34093
  NAUTICAL_SYMBOLS,
@@ -33876,6 +34133,14 @@ export {
33876
34133
  S52_LOOKUP,
33877
34134
  S52_NIGHT,
33878
34135
  S52_PATTERN_SPRITE_PREFIX,
34136
+ SEAFLOOR_ATTRIBUTION,
34137
+ SEAFLOOR_BAKE,
34138
+ SEAFLOOR_LAYER_ID,
34139
+ SEAFLOOR_MAXZOOM,
34140
+ SEAFLOOR_MINZOOM,
34141
+ SEAFLOOR_REGION,
34142
+ SEAFLOOR_THEME_PAINT,
34143
+ SEAFLOOR_TILE_SIZE,
33879
34144
  SECTOR_ARC_LAYER_IDS,
33880
34145
  SECTOR_LEG_LAYER_IDS,
33881
34146
  SECTOR_LIGHT_LAYER_IDS,
@@ -33909,6 +34174,7 @@ export {
33909
34174
  applyS101SymbolSet,
33910
34175
  atonIconExpression,
33911
34176
  bandFilterExpression,
34177
+ bandFloorGate,
33912
34178
  buildAirportTierExpression,
33913
34179
  buildBuiltUpAreaTierExpression,
33914
34180
  buildFlashPattern,
@@ -34055,6 +34321,7 @@ export {
34055
34321
  scaleSizeExpression,
34056
34322
  scaleSizeValue,
34057
34323
  scaminToMinZoom,
34324
+ seafloorLayers,
34058
34325
  sectorLimitToArcBearing,
34059
34326
  semiboldFontStack,
34060
34327
  snapChartSymbolScale,