@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.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AID_TO_NAVIGATION_CLASSES: () => AID_TO_NAVIGATION_CLASSES,
23
24
  ALL_NAV_AID_LAYERS: () => ALL_NAV_AID_LAYERS,
24
25
  ALL_WATER_BODY_LAYERS: () => ALL_WATER_BODY_LAYERS,
25
26
  AREA_QUERY_LAYERS: () => AREA_QUERY_LAYERS,
@@ -59,6 +60,8 @@ __export(index_exports, {
59
60
  CHRT_ICON_PREFIX: () => CHRT_ICON_PREFIX,
60
61
  CHRT_SDF_GLYPHS: () => CHRT_SDF_GLYPHS,
61
62
  CHRT_STYLE_MODES: () => CHRT_STYLE_MODES,
63
+ COARSE_MARK_FLOOR: () => COARSE_MARK_FLOOR,
64
+ COARSE_MARK_HELD_HAZARDS: () => COARSE_MARK_HELD_HAZARDS,
62
65
  Chrt: () => Chrt,
63
66
  ChrtError: () => ChrtError,
64
67
  DANGER_DEPTH_LABEL_ID: () => DANGER_DEPTH_LABEL_ID,
@@ -103,6 +106,8 @@ __export(index_exports, {
103
106
  LAND_BUILD_MAXZOOM: () => LAND_BUILD_MAXZOOM,
104
107
  LAND_REGION: () => LAND_REGION,
105
108
  LAND_TEXT_CLASSES: () => LAND_TEXT_CLASSES,
109
+ LIGHTHOUSE_NAME_FILTER: () => LIGHTHOUSE_NAME_FILTER,
110
+ LIGHTHOUSE_NAME_LABEL_PRIORITY: () => LIGHTHOUSE_NAME_LABEL_PRIORITY,
106
111
  LIGHT_LABEL_FEET_PER_METRE: () => LIGHT_LABEL_FEET_PER_METRE,
107
112
  MAPLIBRE_DEFAULT_TEXT_PADDING_PX: () => MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
108
113
  NAUTICAL_SYMBOLS: () => NAUTICAL_SYMBOLS,
@@ -148,6 +153,14 @@ __export(index_exports, {
148
153
  S52_LOOKUP: () => S52_LOOKUP,
149
154
  S52_NIGHT: () => S52_NIGHT,
150
155
  S52_PATTERN_SPRITE_PREFIX: () => S52_PATTERN_SPRITE_PREFIX,
156
+ SEAFLOOR_ATTRIBUTION: () => SEAFLOOR_ATTRIBUTION,
157
+ SEAFLOOR_BAKE: () => SEAFLOOR_BAKE,
158
+ SEAFLOOR_LAYER_ID: () => SEAFLOOR_LAYER_ID,
159
+ SEAFLOOR_MAXZOOM: () => SEAFLOOR_MAXZOOM,
160
+ SEAFLOOR_MINZOOM: () => SEAFLOOR_MINZOOM,
161
+ SEAFLOOR_REGION: () => SEAFLOOR_REGION,
162
+ SEAFLOOR_THEME_PAINT: () => SEAFLOOR_THEME_PAINT,
163
+ SEAFLOOR_TILE_SIZE: () => SEAFLOOR_TILE_SIZE,
151
164
  SECTOR_ARC_LAYER_IDS: () => SECTOR_ARC_LAYER_IDS,
152
165
  SECTOR_LEG_LAYER_IDS: () => SECTOR_LEG_LAYER_IDS,
153
166
  SECTOR_LIGHT_LAYER_IDS: () => SECTOR_LIGHT_LAYER_IDS,
@@ -181,6 +194,7 @@ __export(index_exports, {
181
194
  applyS101SymbolSet: () => applyS101SymbolSet,
182
195
  atonIconExpression: () => atonIconExpression,
183
196
  bandFilterExpression: () => bandFilterExpression,
197
+ bandFloorGate: () => bandFloorGate,
184
198
  buildAirportTierExpression: () => buildAirportTierExpression,
185
199
  buildBuiltUpAreaTierExpression: () => buildBuiltUpAreaTierExpression,
186
200
  buildFlashPattern: () => buildFlashPattern,
@@ -327,6 +341,7 @@ __export(index_exports, {
327
341
  scaleSizeExpression: () => scaleSizeExpression,
328
342
  scaleSizeValue: () => scaleSizeValue,
329
343
  scaminToMinZoom: () => scaminToMinZoom,
344
+ seafloorLayers: () => seafloorLayers,
330
345
  sectorLimitToArcBearing: () => sectorLimitToArcBearing,
331
346
  semiboldFontStack: () => semiboldFontStack,
332
347
  snapChartSymbolScale: () => snapChartSymbolScale,
@@ -481,7 +496,7 @@ var TileTokenError = class extends Error {
481
496
  this.status = status;
482
497
  }
483
498
  };
484
- var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png)(?:\?.*)?$/i;
499
+ var TILE_URL_PATH = /\/v1\/tiles\/[A-Za-z0-9_]+(?:\/[a-z][a-z0-9-]*)?\/\d+\/\d+\/\d+\.(?:mvt|png|webp)(?:\?.*)?$/i;
485
500
  var STYLE_URL_PATH = /\/v1\/styles\/[A-Za-z0-9_]+\/style\.json(?:\?.*)?$/i;
486
501
  var REFRESH_MARGIN_SECONDS = 30;
487
502
  var STALE_GRACE_SECONDS = 1;
@@ -712,6 +727,11 @@ var LAND_BUILD_MAXZOOM = 14;
712
727
  var LAND_REGION = "CHRT_LAND";
713
728
  var RELIEF_REGION = "CHRT_RELIEF";
714
729
  var BASE_REGION = "CHRT_BASE";
730
+ var SEAFLOOR_REGION = "CHRT_SEAFLOOR";
731
+ var SEAFLOOR_ATTRIBUTION = "Seabed: NOAA OCS BlueTopo, NOAA NCEI CUDEM and Great Lakes Bathymetry, GEBCO Compilation Group (2026)";
732
+ var SEAFLOOR_MINZOOM = 4;
733
+ var SEAFLOOR_MAXZOOM = 13;
734
+ var SEAFLOOR_TILE_SIZE = 512;
715
735
  var BASE_MINZOOM = 0;
716
736
  var BASE_MAXZOOM = 11;
717
737
  var BASE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth \xB7 \xA9 OpenStreetMap contributors";
@@ -780,6 +800,16 @@ function buildSources(opts = {}) {
780
800
  attribution: RELIEF_ATTRIBUTION
781
801
  };
782
802
  }
803
+ if (opts.seafloor) {
804
+ sources[SEAFLOOR_REGION] = {
805
+ type: "raster",
806
+ tiles: [tileTemplate(base, SEAFLOOR_REGION, "webp", buildFor(SEAFLOOR_REGION))],
807
+ tileSize: SEAFLOOR_TILE_SIZE,
808
+ minzoom: SEAFLOOR_MINZOOM,
809
+ maxzoom: SEAFLOOR_MAXZOOM,
810
+ attribution: SEAFLOOR_ATTRIBUTION
811
+ };
812
+ }
783
813
  return sources;
784
814
  }
785
815
 
@@ -2245,6 +2275,70 @@ var CONVIS_CODES = {
2245
2275
  1: { code: 1, name: "Visually conspicuous", abbr: "conspic" },
2246
2276
  2: { code: 2, name: "Not visually conspicuous" }
2247
2277
  };
2278
+ var HORDAT_CODES = {
2279
+ 1: { code: 1, name: "WGS 72" },
2280
+ 2: { code: 2, name: "WGS 84" },
2281
+ 3: { code: 3, name: "European 1950" },
2282
+ 4: { code: 4, name: "Potsdam Datum" },
2283
+ 5: { code: 5, name: "Adindan" },
2284
+ 6: { code: 6, name: "Afgooye" },
2285
+ 7: { code: 7, name: "Ain el Abd 1970" },
2286
+ 8: { code: 8, name: "Anna 1 Astro 1965" },
2287
+ 9: { code: 9, name: "Antigua Island Astro 1943" },
2288
+ 10: { code: 10, name: "Arc 1950" },
2289
+ 11: { code: 11, name: "Arc 1960" },
2290
+ 12: { code: 12, name: "Ascension Island 1958" },
2291
+ 13: { code: 13, name: "Astro beacon \\E\\ 1945" },
2292
+ 14: { code: 14, name: "Astro DOS 71/4" },
2293
+ 15: { code: 15, name: "Astro Tern Island (FRIG) 1961" },
2294
+ 16: { code: 16, name: "Astronimical Station 1952" },
2295
+ 17: { code: 17, name: "Australian Geodetic 1966" },
2296
+ 18: { code: 18, name: "Australian Geodetic 1984" },
2297
+ 19: { code: 19, name: "Ayabelle Lighthouse" },
2298
+ 20: { code: 20, name: "Bellevue (IGN)" },
2299
+ 21: { code: 21, name: "Bermuda 1957" },
2300
+ 22: { code: 22, name: "Bissau" },
2301
+ 23: { code: 23, name: "Bogota Observatory" },
2302
+ 24: { code: 24, name: "Bukit Rimpah" },
2303
+ 25: { code: 25, name: "Camp Area Astro" },
2304
+ 26: { code: 26, name: "Campo Inchauspe 1969" },
2305
+ 27: { code: 27, name: "Canton Astro 1966" },
2306
+ 28: { code: 28, name: "Cape" },
2307
+ 29: { code: 29, name: "Cape Canaveral" },
2308
+ 30: { code: 30, name: "Carthage" },
2309
+ 31: { code: 31, name: "Chatam Island Astro 1971" },
2310
+ 32: { code: 32, name: "Chua Astro" },
2311
+ 33: { code: 33, name: "Corrego Alegre" },
2312
+ 34: { code: 34, name: "Dabola" },
2313
+ 35: { code: 35, name: "Djakarta (Batavia)" },
2314
+ 36: { code: 36, name: "DOS 1968" },
2315
+ 37: { code: 37, name: "Easter Island 1967" },
2316
+ 38: { code: 38, name: "European 1979" },
2317
+ 39: { code: 39, name: "Fort Thomas 1955" },
2318
+ 40: { code: 40, name: "Gan 1970" },
2319
+ 41: { code: 41, name: "Geodetic Datum 1949" },
2320
+ 42: { code: 42, name: "Graciosa Base SW 1948" },
2321
+ 43: { code: 43, name: "Guam 1963" },
2322
+ 44: { code: 44, name: "Ganung Segara" },
2323
+ 45: { code: 45, name: "GUX 1 Astro" },
2324
+ 46: { code: 46, name: "Herat North" },
2325
+ 47: { code: 47, name: "Hjorsey 1955" },
2326
+ 48: { code: 48, name: "Hong Kong 1963" },
2327
+ 49: { code: 49, name: "Hu-Tzu-Shan" },
2328
+ 50: { code: 50, name: "Indian" },
2329
+ 51: { code: 51, name: "Indian 1954" },
2330
+ 52: { code: 52, name: "Indian 1975" },
2331
+ 53: { code: 53, name: "Ireland 1965" },
2332
+ 54: { code: 54, name: "ISTS 061 Astro 1968" },
2333
+ 55: { code: 55, name: "ISTS 073 Astro 1969" },
2334
+ 56: { code: 56, name: "Johnston Island 1961" },
2335
+ 57: { code: 57, name: "Kandawala" },
2336
+ 58: { code: 58, name: "Kerguelen Island 1949" },
2337
+ 59: { code: 59, name: "Kertau 1948" },
2338
+ 60: { code: 60, name: "Kusaie Astro 1951" },
2339
+ 99: { code: 99, name: "South Asia" },
2340
+ 100: { code: 100, name: "Tananarive Observatory 1925" }
2341
+ };
2248
2342
  var LITCHR_CODES = {
2249
2343
  1: { code: 1, name: "Fixed", abbr: "F" },
2250
2344
  2: { code: 2, name: "Flashing", abbr: "Fl" },
@@ -2414,6 +2508,37 @@ var TRAFIC_CODES = {
2414
2508
  3: { code: 3, name: "One-way" },
2415
2509
  4: { code: 4, name: "Two-way" }
2416
2510
  };
2511
+ var VERDAT_CODES = {
2512
+ 1: { code: 1, name: "Mean low water springs" },
2513
+ 2: { code: 2, name: "Mean lower low water springs" },
2514
+ 3: { code: 3, name: "Mean sea level" },
2515
+ 4: { code: 4, name: "Lowest low water" },
2516
+ 5: { code: 5, name: "Mean low water" },
2517
+ 6: { code: 6, name: "Lowest low water springs" },
2518
+ 7: { code: 7, name: "Approximate mean low water springs" },
2519
+ 8: { code: 8, name: "Indian spring low water" },
2520
+ 9: { code: 9, name: "Low water springs" },
2521
+ 10: { code: 10, name: "Approximate lowest astronomical tide" },
2522
+ 11: { code: 11, name: "Nearly lowest low water" },
2523
+ 12: { code: 12, name: "Mean lower low water" },
2524
+ 13: { code: 13, name: "Low water" },
2525
+ 14: { code: 14, name: "Approximate mean low water" },
2526
+ 15: { code: 15, name: "Approximate mean lower low water" },
2527
+ 16: { code: 16, name: "Mean high water" },
2528
+ 17: { code: 17, name: "Mean high water springs" },
2529
+ 18: { code: 18, name: "High water" },
2530
+ 19: { code: 19, name: "Approximate mean sea level" },
2531
+ 20: { code: 20, name: "High water springs" },
2532
+ 21: { code: 21, name: "Mean higher high water" },
2533
+ 22: { code: 22, name: "Equinoctial spring low water" },
2534
+ 23: { code: 23, name: "Lowest astronomical tide" },
2535
+ 24: { code: 24, name: "Local datum" },
2536
+ 25: { code: 25, name: "International Great Lakes Datum 1985" },
2537
+ 26: { code: 26, name: "Mean water level" },
2538
+ 27: { code: 27, name: "Lower low water large tide" },
2539
+ 28: { code: 28, name: "Higher high water large tide" },
2540
+ 29: { code: 29, name: "Nearly highest high water" }
2541
+ };
2417
2542
  var WATLEV_CODES = {
2418
2543
  1: { code: 1, name: "Partly submerged at high water" },
2419
2544
  2: { code: 2, name: "Always dry" },
@@ -2449,6 +2574,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2449
2574
  ["COLPAT", COLPAT_CODES],
2450
2575
  ["CONDTN", CONDTN_CODES],
2451
2576
  ["CONVIS", CONVIS_CODES],
2577
+ ["HORDAT", HORDAT_CODES],
2452
2578
  ["LITCHR", LITCHR_CODES],
2453
2579
  ["MARSYS", MARSYS_CODES],
2454
2580
  ["NATCON", NATCON_CODES],
@@ -2459,6 +2585,7 @@ var S57_CODELISTS = /* @__PURE__ */ new Map([
2459
2585
  ["STATUS", STATUS_CODES],
2460
2586
  ["TOPSHP", TOPSHP_CODES],
2461
2587
  ["TRAFIC", TRAFIC_CODES],
2588
+ ["VERDAT", VERDAT_CODES],
2462
2589
  ["WATLEV", WATLEV_CODES]
2463
2590
  ]);
2464
2591
  var S57_DOMAINS = new Map(
@@ -5691,7 +5818,7 @@ var EntitlementClient = class {
5691
5818
  }
5692
5819
  if (!res.ok) {
5693
5820
  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})`;
5821
+ 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
5822
  const plan = typeof res.body?.plan === "string" ? res.body.plan : void 0;
5696
5823
  throw new InteractError(code, message, { status: res.status, plan });
5697
5824
  }
@@ -6260,6 +6387,32 @@ var InteractClient = class {
6260
6387
  }
6261
6388
  };
6262
6389
 
6390
+ // src/atlas/seafloor-layers.ts
6391
+ var SEAFLOOR_LAYER_ID = "chrtseafloor-shade";
6392
+ var SEAFLOOR_BAKE = Object.freeze({
6393
+ rgb: Object.freeze([47, 72, 88]),
6394
+ kneeAlpha: 16,
6395
+ protectedSafetyContourM: 10,
6396
+ alphaMax: 90
6397
+ });
6398
+ var SEAFLOOR_THEME_PAINT = Object.freeze({
6399
+ DAY: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 1 },
6400
+ DUSK: { "raster-brightness-min": 0, "raster-brightness-max": 1, "raster-opacity": 0.8 },
6401
+ NIGHT: { "raster-brightness-min": 0.55, "raster-brightness-max": 1, "raster-opacity": 0.55 }
6402
+ });
6403
+ function seafloorLayers(palette = "DAY") {
6404
+ if (palette !== "DAY" && palette !== "DUSK" && palette !== "NIGHT") return [];
6405
+ const p = SEAFLOOR_THEME_PAINT[palette];
6406
+ const layer = {
6407
+ id: SEAFLOOR_LAYER_ID,
6408
+ type: "raster",
6409
+ source: SEAFLOOR_REGION,
6410
+ minzoom: SEAFLOOR_MINZOOM,
6411
+ paint: { ...p }
6412
+ };
6413
+ return [layer];
6414
+ }
6415
+
6263
6416
  // src/atlas/chrt-svgs-generated.ts
6264
6417
  var CHRT_GLYPH_SVGS = {
6265
6418
  "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 +7887,7 @@ function applyEinkPortrayal(style, palette, opts = {}) {
7734
7887
  for (const original of style.layers) {
7735
7888
  const id = original.id;
7736
7889
  const type = original.type;
7737
- if (type === "hillshade" || id.startsWith("chrtland-fade-")) continue;
7890
+ if (type === "hillshade" || id.startsWith("chrtland-fade-") || id === SEAFLOOR_LAYER_ID) continue;
7738
7891
  const layer = structuredClone(original);
7739
7892
  const paint = layer.paint ??= {};
7740
7893
  const layout = layer.layout;
@@ -25998,6 +26151,8 @@ var ORIENTATION_LABEL_PRIORITY = 900;
25998
26151
  var MAJOR_WATER_TIER_MAX = 3;
25999
26152
  var MAJOR_WATER_LABEL_PRIORITY = 280;
26000
26153
  var ISLAND_LABEL_HOIST_MINZOOM = 12;
26154
+ var LIGHTHOUSE_NAME_LABEL_PRIORITY = 270;
26155
+ var LIGHTHOUSE_NAME_FILTER = ["all", listAttrHas("FUNCTN", ["33"]), HAS_OBJNAM];
26001
26156
  function getLabelPriority(layerName) {
26002
26157
  if (getLabelBehavior(layerName) === "orientation-cross-band") {
26003
26158
  return ORIENTATION_LABEL_PRIORITY;
@@ -26975,7 +27130,7 @@ function preprocessSoundings(fc) {
26975
27130
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26976
27131
  properties: {
26977
27132
  ...f.properties,
26978
- depth: Math.round(depth * 10) / 10 + ""
27133
+ depth: Math.floor(depth * 10) / 10 + ""
26979
27134
  }
26980
27135
  });
26981
27136
  }
@@ -26988,7 +27143,7 @@ function preprocessSoundings(fc) {
26988
27143
  geometry: { type: "Point", coordinates: [coords[0], coords[1]] },
26989
27144
  properties: {
26990
27145
  ...f.properties,
26991
- depth: Math.round(depth * 10) / 10 + ""
27146
+ depth: Math.floor(depth * 10) / 10 + ""
26992
27147
  }
26993
27148
  });
26994
27149
  }
@@ -27273,7 +27428,7 @@ function dredgeNoteSize() {
27273
27428
  return ["interpolate", ["exponential", 2], ["zoom"], ...stops];
27274
27429
  }
27275
27430
  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"];
27431
+ return depthUnits === "feet" ? ["concat", ["to-string", ["floor", ["*", ["to-number", ["get", prop]], 3.28084]]], " FT"] : ["concat", ["to-string", ["get", prop]], " M"];
27277
27432
  }
27278
27433
  function buildDredgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters") {
27279
27434
  const widthProp = depthUnits === "feet" ? "width_ft" : "width_m";
@@ -27439,11 +27594,11 @@ function buildBridgeNoteLayers(sourceId, palette = "DAY", depthUnits = "meters")
27439
27594
  const unit = imperial ? " FT" : " M";
27440
27595
  const measure = (prop) => imperial ? [
27441
27596
  "concat",
27442
- ["to-string", ["floor", ["+", 0.5, ["*", 3.28084, ["to-number", ["get", prop]]]]]],
27597
+ ["to-string", ["floor", ["*", 3.28084, ["to-number", ["get", prop]]]]],
27443
27598
  unit
27444
27599
  ] : [
27445
27600
  "concat",
27446
- ["to-string", ["/", ["floor", ["+", 0.5, ["*", 10, ["to-number", ["get", prop]]]]], 10]],
27601
+ ["to-string", ["/", ["floor", ["*", 10, ["to-number", ["get", prop]]]], 10]],
27447
27602
  unit
27448
27603
  ];
27449
27604
  const clearance = (prop, prefix, suffix = "") => [
@@ -30449,6 +30604,15 @@ function buildLayersForClass(objectClass2, ct, sourceId = DEFAULT_REGION, opts =
30449
30604
  }
30450
30605
  return layers;
30451
30606
  }
30607
+ function dangerDepthMetresDown() {
30608
+ const valsou = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0], 0];
30609
+ return [
30610
+ "case",
30611
+ [">", valsou, 0],
30612
+ ["concat", ["to-string", ["/", ["floor", ["+", ["*", valsou, 10], 1e-6]], 10]], "m"],
30613
+ ""
30614
+ ];
30615
+ }
30452
30616
  function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
30453
30617
  if (!LABELED_LAYERS.has(objectClass2)) return null;
30454
30618
  const config = getLabelConfig(objectClass2, {
@@ -30473,7 +30637,7 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30473
30637
  textField = [
30474
30638
  "concat",
30475
30639
  [
30476
- "round",
30640
+ "floor",
30477
30641
  [
30478
30642
  "*",
30479
30643
  [
@@ -30501,10 +30665,16 @@ function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnit
30501
30665
  textField = [
30502
30666
  "case",
30503
30667
  [">", valsouFt, 0],
30504
- ["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
30668
+ [
30669
+ "concat",
30670
+ ["to-string", [labelOpts.dangerDepthRounding === "down" ? "floor" : "round", ["*", valsouFt, 3.28084]]],
30671
+ "ft"
30672
+ ],
30505
30673
  ""
30506
30674
  ];
30507
30675
  }
30676
+ } else if (labelOpts.dangerDepthRounding === "down" && (objectClass2 === "UWTROC" || objectClass2 === "OBSTRN" || objectClass2 === "WRECKS")) {
30677
+ textField = dangerDepthMetresDown();
30508
30678
  }
30509
30679
  const labelPriority = getLabelPriority(objectClass2);
30510
30680
  const layer = {
@@ -31374,7 +31544,14 @@ var DEPTH_FIGURE_CLASSES = /* @__PURE__ */ new Set(["SOUNDG", "DEPCNT", "UWTROC"
31374
31544
  function contourValueFactor(depthNumbersFactor) {
31375
31545
  return 1 + (depthNumbersFactor - 1) / 2;
31376
31546
  }
31377
- var SERVED_FONT_STACKS = ["Open Sans Regular", "Open Sans Italic", "Open Sans Semibold"];
31547
+ var SERVED_FONT_STACKS = [
31548
+ "Open Sans Regular",
31549
+ "Open Sans Italic",
31550
+ "Open Sans Semibold",
31551
+ "Old Standard TT Regular",
31552
+ "Old Standard TT Italic",
31553
+ "Old Standard TT Bold"
31554
+ ];
31378
31555
  function semiboldFontStack(font) {
31379
31556
  if (!Array.isArray(font) || !font.every((face) => typeof face === "string")) return font;
31380
31557
  return font.map((face) => {
@@ -31804,8 +31981,54 @@ function bandVariantMinzoom(layer, B) {
31804
31981
  if (isGroundFill(layer)) {
31805
31982
  return BAND_PRIORITY_RANGES[B].minzoom;
31806
31983
  }
31984
+ if (COARSE_MARK_BANDS.has(B) && isCoarseNonAidMark(layer)) {
31985
+ return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom, COARSE_MARK_FLOOR);
31986
+ }
31807
31987
  return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
31808
31988
  }
31989
+ var AID_TO_NAVIGATION_CLASSES = /* @__PURE__ */ new Set([
31990
+ "BOYLAT",
31991
+ "BOYCAR",
31992
+ "BOYSPP",
31993
+ "BOYISD",
31994
+ "BOYSAW",
31995
+ "BOYINB",
31996
+ "BCNLAT",
31997
+ "BCNCAR",
31998
+ "BCNSPP",
31999
+ "BCNISD",
32000
+ "BCNSAW",
32001
+ "LIGHTS",
32002
+ "LITFLT",
32003
+ "LITVES",
32004
+ "FOGSIG",
32005
+ "TOPMAR",
32006
+ "DAYMAR",
32007
+ "RTPBCN",
32008
+ "RDOSTA",
32009
+ "RADRFL",
32010
+ "RETRFL"
32011
+ ]);
32012
+ var COARSE_MARK_BANDS = /* @__PURE__ */ new Set(["1", "2"]);
32013
+ var COARSE_MARK_HELD_HAZARDS = /* @__PURE__ */ new Set(["WRECKS", "OBSTRN", "UWTROC"]);
32014
+ var COARSE_MARK_FLOOR = BAND_PRIORITY_RANGES["3"].minzoom;
32015
+ function bandFloorGate() {
32016
+ return [
32017
+ "any",
32018
+ ...BAND_DRAW_ORDER.map((B) => {
32019
+ const floor = BAND_PRIORITY_RANGES[B].minzoom;
32020
+ return floor > 0 ? ["all", bandFilterExpression(B), [">=", ["zoom"], floor]] : bandFilterExpression(B);
32021
+ })
32022
+ ];
32023
+ }
32024
+ function isCoarseNonAidMark(layer) {
32025
+ if (layer.type !== "symbol" && layer.type !== "circle") return false;
32026
+ const cls = /^s52-([A-Za-z]{6})(?:-|$)/.exec(layer.id)?.[1]?.toUpperCase();
32027
+ if (!cls || cls === "SOUNDG" || AID_TO_NAVIGATION_CLASSES.has(cls) || COARSE_MARK_HELD_HAZARDS.has(cls)) return false;
32028
+ if (layer.type === "circle") return true;
32029
+ const layout = layer.layout;
32030
+ return typeof layout === "object" && layout !== null && "icon-image" in layout;
32031
+ }
31809
32032
  var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
31810
32033
  "BUAARE",
31811
32034
  // 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
@@ -32303,7 +32526,7 @@ function landLayers(opts = {}) {
32303
32526
  minzoom: 11.5,
32304
32527
  filter: ["all", FITS, [">=", tileNumber("peak_m", 0), 40]],
32305
32528
  layout: {
32306
- "text-field": ["concat", ["get", "name"], "\n", ["to-string", ["get", "peak_m"]], " m"],
32529
+ "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
32530
  "text-font": FONT,
32308
32531
  "text-size": ["interpolate", ["linear"], ["zoom"], 11.5, 8.5, 14, 11],
32309
32532
  "text-offset": anchorOffset(0.8),
@@ -32668,6 +32891,23 @@ function spliceByLabelPriority(labelLayers, incoming, priority) {
32668
32891
  if (at === -1) labelLayers.push(...incoming);
32669
32892
  else labelLayers.splice(at, 0, ...incoming);
32670
32893
  }
32894
+ function hoistLighthouseNames(labelLayers) {
32895
+ const hoisted = [];
32896
+ for (const l of labelLayers) {
32897
+ if (!l.id.startsWith("s52-LNDMRK-label")) continue;
32898
+ hoisted.push({
32899
+ ...l,
32900
+ // `s52-LNDMRK-light-label…`, keeping any `-band{B}` tail last: the shape
32901
+ // of the danger-depth placers (`s52-UWTROC-placer-label`), which
32902
+ // custom-style's bandFloor() and the band-collapse id checks read.
32903
+ id: l.id.replace("s52-LNDMRK-label", "s52-LNDMRK-light-label"),
32904
+ filter: combineWithBandFilter(l.filter, LIGHTHOUSE_NAME_FILTER),
32905
+ metadata: { ...l.metadata ?? {}, "chrt:labelPriority": LIGHTHOUSE_NAME_LABEL_PRIORITY }
32906
+ });
32907
+ l.filter = combineWithBandFilter(l.filter, ["!", LIGHTHOUSE_NAME_FILTER]);
32908
+ }
32909
+ spliceByLabelPriority(labelLayers, hoisted, LIGHTHOUSE_NAME_LABEL_PRIORITY);
32910
+ }
32671
32911
  function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32672
32912
  const parts = [bandFilterExpression(B)];
32673
32913
  if (!isDominanceExempt(layer)) {
@@ -32680,6 +32920,9 @@ function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequ
32680
32920
  }
32681
32921
  return parts;
32682
32922
  }
32923
+ function strictCascade(finest) {
32924
+ return BAND_DRAW_ORDER.slice(0, BAND_DRAW_ORDER.indexOf(finest) + 1);
32925
+ }
32683
32926
  function chartModeFilterForBand(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
32684
32927
  const [first, ...rest] = chartModeBandClauses(
32685
32928
  layer,
@@ -32713,7 +32956,8 @@ function makeCollapsedChartLabel(label, cascade, requestedSet, lockedBounds) {
32713
32956
  return collapsed;
32714
32957
  }
32715
32958
  function generateStyle(palette = "DAY", opts = {}) {
32716
- const sources = buildSources(opts);
32959
+ const seafloor = Boolean(opts.seafloor && opts.land) && seafloorLayers(palette).length > 0;
32960
+ const sources = buildSources({ ...opts, seafloor });
32717
32961
  const sourceId = opts.region ?? DEFAULT_REGION;
32718
32962
  const orderedClasses = getOrderedClasses();
32719
32963
  const backgroundLayer = {
@@ -32755,7 +32999,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32755
32999
  if (opts.land) {
32756
33000
  layers.push(...landLayers({
32757
33001
  palette,
32758
- ...opts.relief ? { relief: true } : {}
33002
+ ...opts.relief ? { relief: true } : {},
33003
+ ...opts.depthUnits === "feet" ? { heightUnits: "feet" } : {}
32759
33004
  }));
32760
33005
  }
32761
33006
  if (opts.base) {
@@ -32763,8 +33008,9 @@ function generateStyle(palette = "DAY", opts = {}) {
32763
33008
  }
32764
33009
  const labelLayers = [];
32765
33010
  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;
33011
+ const finestBand = opts.chartMode?.finestBand;
33012
+ const chartCascade = opts.chartMode ? finestBand ? strictCascade(finestBand) : CHART_CASCADE_CODES[opts.chartMode.band] : null;
33013
+ const chartRequestedCodes = opts.chartMode ? finestBand ? [finestBand] : CHART_BAND_CODES[opts.chartMode.band] : null;
32768
33014
  const bandFan = opts.bandFan ?? "collapsed";
32769
33015
  const lockedBounds = opts.chartMode?.lockedBounds;
32770
33016
  const bundles = [];
@@ -32778,7 +33024,8 @@ function generateStyle(palette = "DAY", opts = {}) {
32778
33024
  label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
32779
33025
  safetyContour: opts.safetyContour,
32780
33026
  twoShadeSoundings: opts.twoShadeSoundings,
32781
- soundingFontWeight: opts.soundingFontWeight
33027
+ soundingFontWeight: opts.soundingFontWeight,
33028
+ ...opts.dangerDepthRounding ? { dangerDepthRounding: opts.dangerDepthRounding } : {}
32782
33029
  })
32783
33030
  });
32784
33031
  }
@@ -32821,7 +33068,12 @@ function generateStyle(palette = "DAY", opts = {}) {
32821
33068
  emitChartFeatureVariants(waterBaseBundles);
32822
33069
  emitChartFeatureVariants(regularBundles);
32823
33070
  emitChartFeatureVariants(topLightBundles);
32824
- layers.push(...sectorLightLayers, ...lampLayers);
33071
+ const cascadeGate = finestBand ? bandFilterExpression(chartCascade) : null;
33072
+ layers.push(
33073
+ ...[...sectorLightLayers, ...lampLayers].map(
33074
+ (l) => cascadeGate ? { ...l, filter: combineWithBandFilter(l.filter, cascadeGate) } : l
33075
+ )
33076
+ );
32825
33077
  const chartLabelBundles = byLabelPriorityBottomUp(
32826
33078
  bundles.filter(
32827
33079
  (b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
@@ -32875,6 +33127,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32875
33127
  ),
32876
33128
  BRGNOTE_LABEL_PRIORITY
32877
33129
  );
33130
+ hoistLighthouseNames(labelLayers);
32878
33131
  layers.push(...labelLayers);
32879
33132
  } else {
32880
33133
  const emitBand = (base, code) => bandFan === "legacy" ? makeBandVariants(base, code) : makeCollapsedBandVariant(base, code, isDominanceExempt(base));
@@ -32899,7 +33152,11 @@ function generateStyle(palette = "DAY", opts = {}) {
32899
33152
  }
32900
33153
  }
32901
33154
  }
32902
- layers.push(...sectorLightLayers, ...lampLayers);
33155
+ const sectorGate = bandFloorGate();
33156
+ layers.push(
33157
+ ...sectorLightLayers.map((l) => ({ ...l, filter: combineWithBandFilter(l.filter, sectorGate) })),
33158
+ ...lampLayers
33159
+ );
32903
33160
  {
32904
33161
  const TERMINAL_FILL_RE = new RegExp(
32905
33162
  `^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
@@ -32981,6 +33238,7 @@ function generateStyle(palette = "DAY", opts = {}) {
32981
33238
  ),
32982
33239
  BRGNOTE_LABEL_PRIORITY
32983
33240
  );
33241
+ hoistLighthouseNames(labelLayers);
32984
33242
  layers.push(...labelLayers);
32985
33243
  }
32986
33244
  const symbolSet = opts.symbolSet ?? "s52";
@@ -33152,6 +33410,14 @@ function generateStyle(palette = "DAY", opts = {}) {
33152
33410
  emitted.push(layer);
33153
33411
  }
33154
33412
  }
33413
+ if (seafloor) {
33414
+ const idAt = (i) => emitted[i].id;
33415
+ let lastDepthFill = -1;
33416
+ for (let i = 0; i < emitted.length; i++) {
33417
+ if (/^s52-(DEPARE|DRGARE)-fill-band\d+/.test(idAt(i))) lastDepthFill = i;
33418
+ }
33419
+ if (lastDepthFill !== -1) emitted.splice(lastDepthFill + 1, 0, ...seafloorLayers(palette));
33420
+ }
33155
33421
  const finalLayers = drapedFirst(emitted);
33156
33422
  const style = {
33157
33423
  version: 8,
@@ -33165,6 +33431,7 @@ function generateStyle(palette = "DAY", opts = {}) {
33165
33431
  "chrt:land": Boolean(opts.land),
33166
33432
  "chrt:relief": Boolean(opts.relief),
33167
33433
  "chrt:base": Boolean(opts.base),
33434
+ "chrt:seafloor": seafloor,
33168
33435
  "chrt:render-mode": opts.chartMode ? `chart:${opts.chartMode.band}` : "continuous",
33169
33436
  "chrt:description": "IHO S-52 portrayal rules for Electronic Navigational Charts (ENCs). Generated from the CHRT atlas engine."
33170
33437
  },
@@ -34096,6 +34363,7 @@ var Chrt = class {
34096
34363
  };
34097
34364
  // Annotate the CommonJS export names for ESM import in node:
34098
34365
  0 && (module.exports = {
34366
+ AID_TO_NAVIGATION_CLASSES,
34099
34367
  ALL_NAV_AID_LAYERS,
34100
34368
  ALL_WATER_BODY_LAYERS,
34101
34369
  AREA_QUERY_LAYERS,
@@ -34135,6 +34403,8 @@ var Chrt = class {
34135
34403
  CHRT_ICON_PREFIX,
34136
34404
  CHRT_SDF_GLYPHS,
34137
34405
  CHRT_STYLE_MODES,
34406
+ COARSE_MARK_FLOOR,
34407
+ COARSE_MARK_HELD_HAZARDS,
34138
34408
  Chrt,
34139
34409
  ChrtError,
34140
34410
  DANGER_DEPTH_LABEL_ID,
@@ -34179,6 +34449,8 @@ var Chrt = class {
34179
34449
  LAND_BUILD_MAXZOOM,
34180
34450
  LAND_REGION,
34181
34451
  LAND_TEXT_CLASSES,
34452
+ LIGHTHOUSE_NAME_FILTER,
34453
+ LIGHTHOUSE_NAME_LABEL_PRIORITY,
34182
34454
  LIGHT_LABEL_FEET_PER_METRE,
34183
34455
  MAPLIBRE_DEFAULT_TEXT_PADDING_PX,
34184
34456
  NAUTICAL_SYMBOLS,
@@ -34224,6 +34496,14 @@ var Chrt = class {
34224
34496
  S52_LOOKUP,
34225
34497
  S52_NIGHT,
34226
34498
  S52_PATTERN_SPRITE_PREFIX,
34499
+ SEAFLOOR_ATTRIBUTION,
34500
+ SEAFLOOR_BAKE,
34501
+ SEAFLOOR_LAYER_ID,
34502
+ SEAFLOOR_MAXZOOM,
34503
+ SEAFLOOR_MINZOOM,
34504
+ SEAFLOOR_REGION,
34505
+ SEAFLOOR_THEME_PAINT,
34506
+ SEAFLOOR_TILE_SIZE,
34227
34507
  SECTOR_ARC_LAYER_IDS,
34228
34508
  SECTOR_LEG_LAYER_IDS,
34229
34509
  SECTOR_LIGHT_LAYER_IDS,
@@ -34257,6 +34537,7 @@ var Chrt = class {
34257
34537
  applyS101SymbolSet,
34258
34538
  atonIconExpression,
34259
34539
  bandFilterExpression,
34540
+ bandFloorGate,
34260
34541
  buildAirportTierExpression,
34261
34542
  buildBuiltUpAreaTierExpression,
34262
34543
  buildFlashPattern,
@@ -34403,6 +34684,7 @@ var Chrt = class {
34403
34684
  scaleSizeExpression,
34404
34685
  scaleSizeValue,
34405
34686
  scaminToMinZoom,
34687
+ seafloorLayers,
34406
34688
  sectorLimitToArcBearing,
34407
34689
  semiboldFontStack,
34408
34690
  snapChartSymbolScale,