@chrtco/sdk 0.10.51 → 0.10.52

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.cjs CHANGED
@@ -103,6 +103,8 @@ __export(index_exports, {
103
103
  LAND_BUILD_MAXZOOM: () => LAND_BUILD_MAXZOOM,
104
104
  LAND_REGION: () => LAND_REGION,
105
105
  LAND_TEXT_CLASSES: () => LAND_TEXT_CLASSES,
106
+ LIGHTHOUSE_NAME_FILTER: () => LIGHTHOUSE_NAME_FILTER,
107
+ LIGHTHOUSE_NAME_LABEL_PRIORITY: () => LIGHTHOUSE_NAME_LABEL_PRIORITY,
106
108
  LIGHT_LABEL_FEET_PER_METRE: () => LIGHT_LABEL_FEET_PER_METRE,
107
109
  MAPLIBRE_DEFAULT_TEXT_PADDING_PX: () => MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
108
110
  NAUTICAL_SYMBOLS: () => NAUTICAL_SYMBOLS,
@@ -148,6 +150,14 @@ __export(index_exports, {
148
150
  S52_LOOKUP: () => S52_LOOKUP,
149
151
  S52_NIGHT: () => S52_NIGHT,
150
152
  S52_PATTERN_SPRITE_PREFIX: () => S52_PATTERN_SPRITE_PREFIX,
153
+ SEAFLOOR_ATTRIBUTION: () => SEAFLOOR_ATTRIBUTION,
154
+ SEAFLOOR_BAKE: () => SEAFLOOR_BAKE,
155
+ SEAFLOOR_LAYER_ID: () => SEAFLOOR_LAYER_ID,
156
+ SEAFLOOR_MAXZOOM: () => SEAFLOOR_MAXZOOM,
157
+ SEAFLOOR_MINZOOM: () => SEAFLOOR_MINZOOM,
158
+ SEAFLOOR_REGION: () => SEAFLOOR_REGION,
159
+ SEAFLOOR_THEME_PAINT: () => SEAFLOOR_THEME_PAINT,
160
+ SEAFLOOR_TILE_SIZE: () => SEAFLOOR_TILE_SIZE,
151
161
  SECTOR_ARC_LAYER_IDS: () => SECTOR_ARC_LAYER_IDS,
152
162
  SECTOR_LEG_LAYER_IDS: () => SECTOR_LEG_LAYER_IDS,
153
163
  SECTOR_LIGHT_LAYER_IDS: () => SECTOR_LIGHT_LAYER_IDS,
@@ -327,6 +337,7 @@ __export(index_exports, {
327
337
  scaleSizeExpression: () => scaleSizeExpression,
328
338
  scaleSizeValue: () => scaleSizeValue,
329
339
  scaminToMinZoom: () => scaminToMinZoom,
340
+ seafloorLayers: () => seafloorLayers,
330
341
  sectorLimitToArcBearing: () => sectorLimitToArcBearing,
331
342
  semiboldFontStack: () => semiboldFontStack,
332
343
  snapChartSymbolScale: () => snapChartSymbolScale,
@@ -481,7 +492,7 @@ var TileTokenError = class extends Error {
481
492
  this.status = status;
482
493
  }
483
494
  };
484
- var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png)(?:\?.*)?$/i;
495
+ var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png|webp)(?:\?.*)?$/i;
485
496
  var STYLE_URL_PATH = /\/v1\/styles\/[A-Za-z0-9_]+\/style\.json(?:\?.*)?$/i;
486
497
  var REFRESH_MARGIN_SECONDS = 30;
487
498
  var STALE_GRACE_SECONDS = 1;
@@ -712,6 +723,11 @@ var LAND_BUILD_MAXZOOM = 14;
712
723
  var LAND_REGION = "CHRT_LAND";
713
724
  var RELIEF_REGION = "CHRT_RELIEF";
714
725
  var BASE_REGION = "CHRT_BASE";
726
+ var SEAFLOOR_REGION = "CHRT_SEAFLOOR";
727
+ var SEAFLOOR_ATTRIBUTION = "Seabed: NOAA OCS BlueTopo, NOAA NCEI CUDEM and Great Lakes Bathymetry, GEBCO Compilation Group (2026)";
728
+ var SEAFLOOR_MINZOOM = 4;
729
+ var SEAFLOOR_MAXZOOM = 13;
730
+ var SEAFLOOR_TILE_SIZE = 512;
715
731
  var BASE_MINZOOM = 0;
716
732
  var BASE_MAXZOOM = 11;
717
733
  var BASE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth \xB7 \xA9 OpenStreetMap contributors";
@@ -780,6 +796,16 @@ function buildSources(opts = {}) {
780
796
  attribution: RELIEF_ATTRIBUTION
781
797
  };
782
798
  }
799
+ if (opts.seafloor) {
800
+ sources[SEAFLOOR_REGION] = {
801
+ type: "raster",
802
+ tiles: [tileTemplate(base, SEAFLOOR_REGION, "webp", buildFor(SEAFLOOR_REGION))],
803
+ tileSize: SEAFLOOR_TILE_SIZE,
804
+ minzoom: SEAFLOOR_MINZOOM,
805
+ maxzoom: SEAFLOOR_MAXZOOM,
806
+ attribution: SEAFLOOR_ATTRIBUTION
807
+ };
808
+ }
783
809
  return sources;
784
810
  }
785
811
 
@@ -2245,6 +2271,70 @@ var CONVIS_CODES = {
2245
2271
  1: { code: 1, name: "Visually conspicuous", abbr: "conspic" },
2246
2272
  2: { code: 2, name: "Not visually conspicuous" }
2247
2273
  };
2274
+ var HORDAT_CODES = {
2275
+ 1: { code: 1, name: "WGS 72" },
2276
+ 2: { code: 2, name: "WGS 84" },
2277
+ 3: { code: 3, name: "European 1950" },
2278
+ 4: { code: 4, name: "Potsdam Datum" },
2279
+ 5: { code: 5, name: "Adindan" },
2280
+ 6: { code: 6, name: "Afgooye" },
2281
+ 7: { code: 7, name: "Ain el Abd 1970" },
2282
+ 8: { code: 8, name: "Anna 1 Astro 1965" },
2283
+ 9: { code: 9, name: "Antigua Island Astro 1943" },
2284
+ 10: { code: 10, name: "Arc 1950" },
2285
+ 11: { code: 11, name: "Arc 1960" },
2286
+ 12: { code: 12, name: "Ascension Island 1958" },
2287
+ 13: { code: 13, name: "Astro beacon \\E\\ 1945" },
2288
+ 14: { code: 14, name: "Astro DOS 71/4" },
2289
+ 15: { code: 15, name: "Astro Tern Island (FRIG) 1961" },
2290
+ 16: { code: 16, name: "Astronimical Station 1952" },
2291
+ 17: { code: 17, name: "Australian Geodetic 1966" },
2292
+ 18: { code: 18, name: "Australian Geodetic 1984" },
2293
+ 19: { code: 19, name: "Ayabelle Lighthouse" },
2294
+ 20: { code: 20, name: "Bellevue (IGN)" },
2295
+ 21: { code: 21, name: "Bermuda 1957" },
2296
+ 22: { code: 22, name: "Bissau" },
2297
+ 23: { code: 23, name: "Bogota Observatory" },
2298
+ 24: { code: 24, name: "Bukit Rimpah" },
2299
+ 25: { code: 25, name: "Camp Area Astro" },
2300
+ 26: { code: 26, name: "Campo Inchauspe 1969" },
2301
+ 27: { code: 27, name: "Canton Astro 1966" },
2302
+ 28: { code: 28, name: "Cape" },
2303
+ 29: { code: 29, name: "Cape Canaveral" },
2304
+ 30: { code: 30, name: "Carthage" },
2305
+ 31: { code: 31, name: "Chatam Island Astro 1971" },
2306
+ 32: { code: 32, name: "Chua Astro" },
2307
+ 33: { code: 33, name: "Corrego Alegre" },
2308
+ 34: { code: 34, name: "Dabola" },
2309
+ 35: { code: 35, name: "Djakarta (Batavia)" },
2310
+ 36: { code: 36, name: "DOS 1968" },
2311
+ 37: { code: 37, name: "Easter Island 1967" },
2312
+ 38: { code: 38, name: "European 1979" },
2313
+ 39: { code: 39, name: "Fort Thomas 1955" },
2314
+ 40: { code: 40, name: "Gan 1970" },
2315
+ 41: { code: 41, name: "Geodetic Datum 1949" },
2316
+ 42: { code: 42, name: "Graciosa Base SW 1948" },
2317
+ 43: { code: 43, name: "Guam 1963" },
2318
+ 44: { code: 44, name: "Ganung Segara" },
2319
+ 45: { code: 45, name: "GUX 1 Astro" },
2320
+ 46: { code: 46, name: "Herat North" },
2321
+ 47: { code: 47, name: "Hjorsey 1955" },
2322
+ 48: { code: 48, name: "Hong Kong 1963" },
2323
+ 49: { code: 49, name: "Hu-Tzu-Shan" },
2324
+ 50: { code: 50, name: "Indian" },
2325
+ 51: { code: 51, name: "Indian 1954" },
2326
+ 52: { code: 52, name: "Indian 1975" },
2327
+ 53: { code: 53, name: "Ireland 1965" },
2328
+ 54: { code: 54, name: "ISTS 061 Astro 1968" },
2329
+ 55: { code: 55, name: "ISTS 073 Astro 1969" },
2330
+ 56: { code: 56, name: "Johnston Island 1961" },
2331
+ 57: { code: 57, name: "Kandawala" },
2332
+ 58: { code: 58, name: "Kerguelen Island 1949" },
2333
+ 59: { code: 59, name: "Kertau 1948" },
2334
+ 60: { code: 60, name: "Kusaie Astro 1951" },
2335
+ 99: { code: 99, name: "South Asia" },
2336
+ 100: { code: 100, name: "Tananarive Observatory 1925" }
2337
+ };
2248
2338
  var LITCHR_CODES = {
2249
2339
  1: { code: 1, name: "Fixed", abbr: "F" },
2250
2340
  2: { code: 2, name: "Flashing", abbr: "Fl" },
@@ -2414,6 +2504,37 @@ var TRAFIC_CODES = {
2414
2504
  3: { code: 3, name: "One-way" },
2415
2505
  4: { code: 4, name: "Two-way" }
2416
2506
  };
2507
+ var VERDAT_CODES = {
2508
+ 1: { code: 1, name: "Mean low water springs" },
2509
+ 2: { code: 2, name: "Mean lower low water springs" },
2510
+ 3: { code: 3, name: "Mean sea level" },
2511
+ 4: { code: 4, name: "Lowest low water" },
2512
+ 5: { code: 5, name: "Mean low water" },
2513
+ 6: { code: 6, name: "Lowest low water springs" },
2514
+ 7: { code: 7, name: "Approximate mean low water springs" },
2515
+ 8: { code: 8, name: "Indian spring low water" },
2516
+ 9: { code: 9, name: "Low water springs" },
2517
+ 10: { code: 10, name: "Approximate lowest astronomical tide" },
2518
+ 11: { code: 11, name: "Nearly lowest low water" },
2519
+ 12: { code: 12, name: "Mean lower low water" },
2520
+ 13: { code: 13, name: "Low water" },
2521
+ 14: { code: 14, name: "Approximate mean low water" },
2522
+ 15: { code: 15, name: "Approximate mean lower low water" },
2523
+ 16: { code: 16, name: "Mean high water" },
2524
+ 17: { code: 17, name: "Mean high water springs" },
2525
+ 18: { code: 18, name: "High water" },
2526
+ 19: { code: 19, name: "Approximate mean sea level" },
2527
+ 20: { code: 20, name: "High water springs" },
2528
+ 21: { code: 21, name: "Mean higher high water" },
2529
+ 22: { code: 22, name: "Equinoctial spring low water" },
2530
+ 23: { code: 23, name: "Lowest astronomical tide" },
2531
+ 24: { code: 24, name: "Local datum" },
2532
+ 25: { code: 25, name: "International Great Lakes Datum 1985" },
2533
+ 26: { code: 26, name: "Mean water level" },
2534
+ 27: { code: 27, name: "Lower low water large tide" },
2535
+ 28: { code: 28, name: "Higher high water large tide" },
2536
+ 29: { code: 29, name: "Nearly highest high water" }
2537
+ };
2417
2538
  var WATLEV_CODES = {
2418
2539
  1: { code: 1, name: "Partly submerged at high water" },
2419
2540
  2: { code: 2, name: "Always dry" },
@@ -2449,6 +2570,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2449
2570
  ["COLPAT", COLPAT_CODES],
2450
2571
  ["CONDTN", CONDTN_CODES],
2451
2572
  ["CONVIS", CONVIS_CODES],
2573
+ ["HORDAT", HORDAT_CODES],
2452
2574
  ["LITCHR", LITCHR_CODES],
2453
2575
  ["MARSYS", MARSYS_CODES],
2454
2576
  ["NATCON", NATCON_CODES],
@@ -2459,6 +2581,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2459
2581
  ["STATUS", STATUS_CODES],
2460
2582
  ["TOPSHP", TOPSHP_CODES],
2461
2583
  ["TRAFIC", TRAFIC_CODES],
2584
+ ["VERDAT", VERDAT_CODES],
2462
2585
  ["WATLEV", WATLEV_CODES]
2463
2586
  ]);
2464
2587
  var S57_DOMAINS = new Map(
@@ -5691,7 +5814,7 @@ var EntitlementClient = class {
5691
5814
  }
5692
5815
  if (!res.ok) {
5693
5816
  const code = mapDenialCode(res.body?.error, res.status);
5694
- 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})`;
5817
+ 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})`;
5695
5818
  const plan = typeof res.body?.plan === "string" ? res.body.plan : void 0;
5696
5819
  throw new InteractError(code, message, { status: res.status, plan });
5697
5820
  }
@@ -6260,6 +6383,32 @@ var InteractClient = class {
6260
6383
  }
6261
6384
  };
6262
6385
 
6386
+ // src/atlas/seafloor-layers.ts
6387
+ var SEAFLOOR_LAYER_ID = "chrtseafloor-shade";
6388
+ var SEAFLOOR_BAKE = Object.freeze({
6389
+ rgb: Object.freeze([47, 72, 88]),
6390
+ kneeAlpha: 16,
6391
+ protectedSafetyContourM: 10,
6392
+ alphaMax: 90
6393
+ });
6394
+ var SEAFLOOR_THEME_PAINT = Object.freeze({
6395
+ DAY: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 1 },
6396
+ DUSK: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 0.8 },
6397
+ NIGHT: { "raster-brightness-min": 0.55, "raster-brightness-max": 1, "raster-opacity": 0.55 }
6398
+ });
6399
+ function seafloorLayers(palette = "DAY") {
6400
+ if (palette !== "DAY" && palette !== "DUSK" && palette !== "NIGHT") return [];
6401
+ const p = SEAFLOOR_THEME_PAINT[palette];
6402
+ const layer = {
6403
+ id: SEAFLOOR_LAYER_ID,
6404
+ type: "raster",
6405
+ source: SEAFLOOR_REGION,
6406
+ minzoom: SEAFLOOR_MINZOOM,
6407
+ paint: { ...p }
6408
+ };
6409
+ return [layer];
6410
+ }
6411
+
6263
6412
  // src/atlas/chrt-svgs-generated.ts
6264
6413
  var CHRT_GLYPH_SVGS = {
6265
6414
  "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>',
@@ -7734,7 +7883,7 @@ function applyEinkPortrayal(style, palette, opts = {}) {
7734
7883
  for (const original of style.layers) {
7735
7884
  const id = original.id;
7736
7885
  const type = original.type;
7737
- if (type === "hillshade" || id.startsWith("chrtland-fade-")) continue;
7886
+ if (type === "hillshade" || id.startsWith("chrtland-fade-") || id === SEAFLOOR_LAYER_ID) continue;
7738
7887
  const layer = structuredClone(original);
7739
7888
  const paint = layer.paint ??= {};
7740
7889
  const layout = layer.layout;
@@ -25998,6 +26147,8 @@ var ORIENTATION_LABEL_PRIORITY = 900;
25998
26147
  var MAJOR_WATER_TIER_MAX = 3;
25999
26148
  var MAJOR_WATER_LABEL_PRIORITY = 280;
26000
26149
  var ISLAND_LABEL_HOIST_MINZOOM = 12;
26150
+ var LIGHTHOUSE_NAME_LABEL_PRIORITY = 270;
26151
+ var LIGHTHOUSE_NAME_FILTER = ["all", listAttrHas("FUNCTN", ["33"]), HAS_OBJNAM];
26001
26152
  function getLabelPriority(layerName) {
26002
26153
  if (getLabelBehavior(layerName) === "orientation-cross-band") {
26003
26154
  return ORIENTATION_LABEL_PRIORITY;
@@ -26975,7 +27126,7 @@ function preprocessSoundings(fc) {
26975
27126
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26976
27127
  properties: {
26977
27128
  ...f.properties,
26978
- depth: Math.round(depth * 10) / 10 + ""
27129
+ depth: Math.floor(depth * 10) / 10 + ""
26979
27130
  }
26980
27131
  });
26981
27132
  }
@@ -26988,7 +27139,7 @@ function preprocessSoundings(fc) {
26988
27139
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26989
27140
  properties: {
26990
27141
  ...f.properties,
26991
- depth: Math.round(depth * 10) / 10 + ""
27142
+ depth: Math.floor(depth * 10) / 10 + ""
26992
27143
  }
26993
27144
  });
26994
27145
  }
@@ -27273,7 +27424,7 @@ function dredgeNoteSize() {
27273
27424
  return ["interpolate", ["exponential", 2], ["zoom"], ...stops];
27274
27425
  }
27275
27426
  function noteDepthText(prop, depthUnits) {
27276
- return depthUnits === "feet" ? ["concat", ["to-string", ["round", ["*", ["to-number", ["get", prop]], 3.28084]]], " FT"] : ["concat", ["to-string", ["get", prop]], " M"];
27427
+ return depthUnits === "feet" ? ["concat", ["to-string", ["floor", ["*", ["to-number", ["get", prop]], 3.28084]]], " FT"] : ["concat", ["to-string", ["get", prop]], " M"];
27277
27428
  }
27278
27429
  function buildDredgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters") {
27279
27430
  const widthProp = depthUnits === "feet" ? "width_ft" : "width_m";
@@ -27439,11 +27590,11 @@ function buildBridgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters")
27439
27590
  const unit = imperial ? " FT" : " M";
27440
27591
  const measure = (prop) => imperial ? [
27441
27592
  "concat",
27442
- ["to-string", ["floor", ["+", 0.5, ["*", 3.28084, ["to-number", ["get", prop]]]]]],
27593
+ ["to-string", ["floor", ["*", 3.28084, ["to-number", ["get", prop]]]]],
27443
27594
  unit
27444
27595
  ] : [
27445
27596
  "concat",
27446
- ["to-string", ["/", ["floor", ["+", 0.5, ["*", 10, ["to-number", ["get", prop]]]]], 10]],
27597
+ ["to-string", ["/", ["floor", ["*", 10, ["to-number", ["get", prop]]]], 10]],
27447
27598
  unit
27448
27599
  ];
27449
27600
  const clearance = (prop, prefix, suffix = "") => [
@@ -30449,6 +30600,15 @@ function buildLayersForClass(objectClass2, ct, sourceId = DEFAULT_REGION, opts =
30449
30600
  }
30450
30601
  return layers;
30451
30602
  }
30603
+ function dangerDepthMetresDown() {
30604
+ const valsou = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0], 0];
30605
+ return [
30606
+ "case",
30607
+ [">", valsou, 0],
30608
+ ["concat", ["to-string", ["/", ["floor", ["+", ["*", valsou, 10], 1e-6]], 10]], "m"],
30609
+ ""
30610
+ ];
30611
+ }
30452
30612
  function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
30453
30613
  if (!LABELED_LAYERS.has(objectClass2)) return null;
30454
30614
  const config = getLabelConfig(objectClass2, {
@@ -30473,7 +30633,7 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30473
30633
  textField = [
30474
30634
  "concat",
30475
30635
  [
30476
- "round",
30636
+ "floor",
30477
30637
  [
30478
30638
  "*",
30479
30639
  [
@@ -30501,10 +30661,16 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30501
30661
  textField = [
30502
30662
  "case",
30503
30663
  [">", valsouFt, 0],
30504
- ["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
30664
+ [
30665
+ "concat",
30666
+ ["to-string", [labelOpts.dangerDepthRounding === "down" ? "floor" : "round", ["*", valsouFt, 3.28084]]],
30667
+ "ft"
30668
+ ],
30505
30669
  ""
30506
30670
  ];
30507
30671
  }
30672
+ } else if (labelOpts.dangerDepthRounding === "down" && (objectClass2 === "UWTROC" || objectClass2 === "OBSTRN" || objectClass2 === "WRECKS")) {
30673
+ textField = dangerDepthMetresDown();
30508
30674
  }
30509
30675
  const labelPriority = getLabelPriority(objectClass2);
30510
30676
  const layer = {
@@ -31374,7 +31540,14 @@ var DEPTH_FIGURE_CLASSES = /* @__PURE__ */ new Set(["SOUNDG", "DEPCNT", "UWTROC"
31374
31540
  function contourValueFactor(depthNumbersFactor) {
31375
31541
  return 1 + (depthNumbersFactor - 1) / 2;
31376
31542
  }
31377
- var SERVED_FONT_STACKS = ["Open Sans Regular", "Open Sans Italic", "Open Sans Semibold"];
31543
+ var SERVED_FONT_STACKS = [
31544
+ "Open Sans Regular",
31545
+ "Open Sans Italic",
31546
+ "Open Sans Semibold",
31547
+ "Old Standard TT Regular",
31548
+ "Old Standard TT Italic",
31549
+ "Old Standard TT Bold"
31550
+ ];
31378
31551
  function semiboldFontStack(font) {
31379
31552
  if (!Array.isArray(font) || !font.every((face) => typeof face === "string")) return font;
31380
31553
  return font.map((face) => {
@@ -32303,7 +32476,7 @@ function landLayers(opts = {}) {
32303
32476
  minzoom: 11.5,
32304
32477
  filter: ["all", FITS, [">=", tileNumber("peak_m", 0), 40]],
32305
32478
  layout: {
32306
- "text-field": ["concat", ["get", "name"], "\n", ["to-string", ["get", "peak_m"]], " m"],
32479
+ "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"],
32307
32480
  "text-font": FONT,
32308
32481
  "text-size": ["interpolate", ["linear"], ["zoom"], 11.5, 8.5, 14, 11],
32309
32482
  "text-offset": anchorOffset(0.8),
@@ -32668,6 +32841,23 @@ function spliceByLabelPriority(labelLayers, incoming, priority) {
32668
32841
  if (at === -1) labelLayers.push(...incoming);
32669
32842
  else labelLayers.splice(at, 0, ...incoming);
32670
32843
  }
32844
+ function hoistLighthouseNames(labelLayers) {
32845
+ const hoisted = [];
32846
+ for (const l of labelLayers) {
32847
+ if (!l.id.startsWith("s52-LNDMRK-label")) continue;
32848
+ hoisted.push({
32849
+ ...l,
32850
+ // `s52-LNDMRK-light-label…`, keeping any `-band{B}` tail last: the shape
32851
+ // of the danger-depth placers (`s52-UWTROC-placer-label`), which
32852
+ // custom-style's bandFloor() and the band-collapse id checks read.
32853
+ id: l.id.replace("s52-LNDMRK-label", "s52-LNDMRK-light-label"),
32854
+ filter: combineWithBandFilter(l.filter, LIGHTHOUSE_NAME_FILTER),
32855
+ metadata: { ...l.metadata ?? {}, "chrt:labelPriority": LIGHTHOUSE_NAME_LABEL_PRIORITY }
32856
+ });
32857
+ l.filter = combineWithBandFilter(l.filter, ["!", LIGHTHOUSE_NAME_FILTER]);
32858
+ }
32859
+ spliceByLabelPriority(labelLayers, hoisted, LIGHTHOUSE_NAME_LABEL_PRIORITY);
32860
+ }
32671
32861
  function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32672
32862
  const parts = [bandFilterExpression(B)];
32673
32863
  if (!isDominanceExempt(layer)) {
@@ -32680,6 +32870,9 @@ function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequ
32680
32870
  }
32681
32871
  return parts;
32682
32872
  }
32873
+ function strictCascade(finest) {
32874
+ return BAND_DRAW_ORDER.slice(0, BAND_DRAW_ORDER.indexOf(finest) + 1);
32875
+ }
32683
32876
  function chartModeFilterForBand(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32684
32877
  const [first, ...rest] = chartModeBandClauses(
32685
32878
  layer,
@@ -32713,7 +32906,8 @@ function makeCollapsedChartLabel(label, cascade, requestedSet, lockedBounds) {
32713
32906
  return collapsed;
32714
32907
  }
32715
32908
  function generateStyle(palette = "DAY", opts = {}) {
32716
- const sources = buildSources(opts);
32909
+ const seafloor = Boolean(opts.seafloor && opts.land) && seafloorLayers(palette).length > 0;
32910
+ const sources = buildSources({ ...opts, seafloor });
32717
32911
  const sourceId = opts.region ?? DEFAULT_REGION;
32718
32912
  const orderedClasses = getOrderedClasses();
32719
32913
  const backgroundLayer = {
@@ -32755,7 +32949,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32755
32949
  if (opts.land) {
32756
32950
  layers.push(...landLayers({
32757
32951
  palette,
32758
- ...opts.relief ? { relief: true } : {}
32952
+ ...opts.relief ? { relief: true } : {},
32953
+ ...opts.depthUnits === "feet" ? { heightUnits: "feet" } : {}
32759
32954
  }));
32760
32955
  }
32761
32956
  if (opts.base) {
@@ -32763,8 +32958,9 @@ function generateStyle(palette = "DAY", opts = {}) {
32763
32958
  }
32764
32959
  const labelLayers = [];
32765
32960
  const majorWaterLayers = [];
32766
- const chartCascade = opts.chartMode ? CHART_CASCADE_CODES[opts.chartMode.band] : null;
32767
- const chartRequestedCodes = opts.chartMode ? CHART_BAND_CODES[opts.chartMode.band] : null;
32961
+ const finestBand = opts.chartMode?.finestBand;
32962
+ const chartCascade = opts.chartMode ? finestBand ? strictCascade(finestBand) : CHART_CASCADE_CODES[opts.chartMode.band] : null;
32963
+ const chartRequestedCodes = opts.chartMode ? finestBand ? [finestBand] : CHART_BAND_CODES[opts.chartMode.band] : null;
32768
32964
  const bandFan = opts.bandFan ?? "collapsed";
32769
32965
  const lockedBounds = opts.chartMode?.lockedBounds;
32770
32966
  const bundles = [];
@@ -32778,7 +32974,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32778
32974
  label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
32779
32975
  safetyContour: opts.safetyContour,
32780
32976
  twoShadeSoundings: opts.twoShadeSoundings,
32781
- soundingFontWeight: opts.soundingFontWeight
32977
+ soundingFontWeight: opts.soundingFontWeight,
32978
+ ...opts.dangerDepthRounding ? { dangerDepthRounding: opts.dangerDepthRounding } : {}
32782
32979
  })
32783
32980
  });
32784
32981
  }
@@ -32821,7 +33018,12 @@ function generateStyle(palette = "DAY", opts = {}) {
32821
33018
  emitChartFeatureVariants(waterBaseBundles);
32822
33019
  emitChartFeatureVariants(regularBundles);
32823
33020
  emitChartFeatureVariants(topLightBundles);
32824
- layers.push(...sectorLightLayers, ...lampLayers);
33021
+ const cascadeGate = finestBand ? bandFilterExpression(chartCascade) : null;
33022
+ layers.push(
33023
+ ...[...sectorLightLayers, ...lampLayers].map(
33024
+ (l) => cascadeGate ? { ...l, filter: combineWithBandFilter(l.filter, cascadeGate) } : l
33025
+ )
33026
+ );
32825
33027
  const chartLabelBundles = byLabelPriorityBottomUp(
32826
33028
  bundles.filter(
32827
33029
  (b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
@@ -32875,6 +33077,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32875
33077
  ),
32876
33078
  BRGNOTE_LABEL_PRIORITY
32877
33079
  );
33080
+ hoistLighthouseNames(labelLayers);
32878
33081
  layers.push(...labelLayers);
32879
33082
  } else {
32880
33083
  const emitBand = (base, code) => bandFan === "legacy" ? makeBandVariants(base, code) : makeCollapsedBandVariant(base, code, isDominanceExempt(base));
@@ -32981,6 +33184,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32981
33184
  ),
32982
33185
  BRGNOTE_LABEL_PRIORITY
32983
33186
  );
33187
+ hoistLighthouseNames(labelLayers);
32984
33188
  layers.push(...labelLayers);
32985
33189
  }
32986
33190
  const symbolSet = opts.symbolSet ?? "s52";
@@ -33152,6 +33356,14 @@ function generateStyle(palette = "DAY", opts = {}) {
33152
33356
  emitted.push(layer);
33153
33357
  }
33154
33358
  }
33359
+ if (seafloor) {
33360
+ const idAt = (i) => emitted[i].id;
33361
+ let lastDepthFill = -1;
33362
+ for (let i = 0; i < emitted.length; i++) {
33363
+ if (/^s52-(DEPARE|DRGARE)-fill-band\d+/.test(idAt(i))) lastDepthFill = i;
33364
+ }
33365
+ if (lastDepthFill !== -1) emitted.splice(lastDepthFill + 1, 0, ...seafloorLayers(palette));
33366
+ }
33155
33367
  const finalLayers = drapedFirst(emitted);
33156
33368
  const style = {
33157
33369
  version: 8,
@@ -33165,6 +33377,7 @@ function generateStyle(palette = "DAY", opts = {}) {
33165
33377
  "chrt:land": Boolean(opts.land),
33166
33378
  "chrt:relief": Boolean(opts.relief),
33167
33379
  "chrt:base": Boolean(opts.base),
33380
+ "chrt:seafloor": seafloor,
33168
33381
  "chrt:render-mode": opts.chartMode ? `chart:${opts.chartMode.band}` : "continuous",
33169
33382
  "chrt:description": "IHO S-52 portrayal rules for Electronic Navigational Charts (ENCs). Generated from the CHRT atlas engine."
33170
33383
  },
@@ -34179,6 +34392,8 @@ var Chrt = class {
34179
34392
  LAND_BUILD_MAXZOOM,
34180
34393
  LAND_REGION,
34181
34394
  LAND_TEXT_CLASSES,
34395
+ LIGHTHOUSE_NAME_FILTER,
34396
+ LIGHTHOUSE_NAME_LABEL_PRIORITY,
34182
34397
  LIGHT_LABEL_FEET_PER_METRE,
34183
34398
  MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
34184
34399
  NAUTICAL_SYMBOLS,
@@ -34224,6 +34439,14 @@ var Chrt = class {
34224
34439
  S52_LOOKUP,
34225
34440
  S52_NIGHT,
34226
34441
  S52_PATTERN_SPRITE_PREFIX,
34442
+ SEAFLOOR_ATTRIBUTION,
34443
+ SEAFLOOR_BAKE,
34444
+ SEAFLOOR_LAYER_ID,
34445
+ SEAFLOOR_MAXZOOM,
34446
+ SEAFLOOR_MINZOOM,
34447
+ SEAFLOOR_REGION,
34448
+ SEAFLOOR_THEME_PAINT,
34449
+ SEAFLOOR_TILE_SIZE,
34227
34450
  SECTOR_ARC_LAYER_IDS,
34228
34451
  SECTOR_LEG_LAYER_IDS,
34229
34452
  SECTOR_LIGHT_LAYER_IDS,
@@ -34403,6 +34626,7 @@ var Chrt = class {
34403
34626
  scaleSizeExpression,
34404
34627
  scaleSizeValue,
34405
34628
  scaminToMinZoom,
34629
+ seafloorLayers,
34406
34630
  sectorLimitToArcBearing,
34407
34631
  semiboldFontStack,
34408
34632
  snapChartSymbolScale,