@chrtco/sdk 0.10.45 → 0.10.47
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/CHANGELOG.md +35 -0
- package/README.md +29 -6
- package/dist/atlas/browser.cjs +478 -15
- package/dist/atlas/browser.cjs.map +1 -1
- package/dist/atlas/browser.d.cts +8 -3
- package/dist/atlas/browser.d.ts +8 -3
- package/dist/atlas/browser.js +478 -15
- package/dist/atlas/browser.js.map +1 -1
- package/dist/index.cjs +2293 -1603
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1272 -1045
- package/dist/index.d.ts +1272 -1045
- package/dist/index.js +2274 -1603
- package/dist/index.js.map +1 -1
- package/dist/symbol-families-De7sN-p5.d.cts +93 -0
- package/dist/symbol-families-De7sN-p5.d.ts +93 -0
- package/package.json +1 -1
- package/dist/symbol-families-D2Kx20HJ.d.cts +0 -47
- package/dist/symbol-families-D2Kx20HJ.d.ts +0 -47
package/dist/index.js
CHANGED
|
@@ -2057,6 +2057,19 @@ function s57AbbrTable(acronym) {
|
|
|
2057
2057
|
}
|
|
2058
2058
|
|
|
2059
2059
|
// src/atlas/s52-colors.ts
|
|
2060
|
+
var EINK_COLOR_TABLES = { color: "EINK_COLOR", mono: "EINK_MONO", night: "EINK_NIGHT" };
|
|
2061
|
+
function isEinkColorTable(ct) {
|
|
2062
|
+
return ct === "EINK_COLOR" || ct === "EINK_MONO" || ct === "EINK_NIGHT";
|
|
2063
|
+
}
|
|
2064
|
+
function isEinkGreyTable(ct) {
|
|
2065
|
+
return ct === "EINK_MONO" || ct === "EINK_NIGHT";
|
|
2066
|
+
}
|
|
2067
|
+
var EINK_MONO_GREYS = {
|
|
2068
|
+
INK: "#000000",
|
|
2069
|
+
DARK: "#555555",
|
|
2070
|
+
MID: "#aaaaaa",
|
|
2071
|
+
PAPER: "#ffffff"
|
|
2072
|
+
};
|
|
2060
2073
|
var S52_DAY = {
|
|
2061
2074
|
// Depth
|
|
2062
2075
|
NODTA: "#a4a4a4",
|
|
@@ -2337,11 +2350,235 @@ var S52_NIGHT = {
|
|
|
2337
2350
|
RES02: "#383028",
|
|
2338
2351
|
RES03: "#383028"
|
|
2339
2352
|
};
|
|
2353
|
+
var S52_EINK_COLOR = {
|
|
2354
|
+
NODTA: "#b4b4b4",
|
|
2355
|
+
DEPIT: "#4f8f3a",
|
|
2356
|
+
DEPVS: "#74ade0",
|
|
2357
|
+
DEPMS: "#bad6ef",
|
|
2358
|
+
DEPMD: "#ffffff",
|
|
2359
|
+
DEPDW: "#ffffff",
|
|
2360
|
+
LANDA: "#f2e2b0",
|
|
2361
|
+
LANDF: "#c49a4a",
|
|
2362
|
+
CHBRN: "#8b5a2b",
|
|
2363
|
+
CHGRF: "#9a9a9a",
|
|
2364
|
+
CHGRD: "#444444",
|
|
2365
|
+
CHBLK: "#000000",
|
|
2366
|
+
CSTLN: "#000000",
|
|
2367
|
+
OUTLW: "#000000",
|
|
2368
|
+
BKAJ1: "#333333",
|
|
2369
|
+
TRFCD: "#c800c8",
|
|
2370
|
+
TRFCF: "#c800c8",
|
|
2371
|
+
RESBL: "#0050c8",
|
|
2372
|
+
APTS1: "#c800c8",
|
|
2373
|
+
DNGHL: "#e00000",
|
|
2374
|
+
ISHDG: "#e00000",
|
|
2375
|
+
LITRD: "#e00000",
|
|
2376
|
+
LITGN: "#00a000",
|
|
2377
|
+
LITYW: "#f0c000",
|
|
2378
|
+
LITWT: "#ffffff",
|
|
2379
|
+
LITBL: "#0050c8",
|
|
2380
|
+
LITOR: "#f07000",
|
|
2381
|
+
RADHI: "#00a000",
|
|
2382
|
+
RADLO: "#006000",
|
|
2383
|
+
PLRTE: "#e00000",
|
|
2384
|
+
ARPAT: "#008040",
|
|
2385
|
+
ISDNG: "#c800c8",
|
|
2386
|
+
CHRED: "#e00000",
|
|
2387
|
+
CHGRN: "#00a000",
|
|
2388
|
+
CHYLW: "#f0c000",
|
|
2389
|
+
CHMGD: "#c800c8",
|
|
2390
|
+
CHMGF: "#c800c8",
|
|
2391
|
+
CHWHT: "#ffffff",
|
|
2392
|
+
CHCOR: "#f07000",
|
|
2393
|
+
// Deeper-than-safety soundings: dark grey, the one secondary ink. Shoal
|
|
2394
|
+
// soundings are black — the louder numeral, as in every other table.
|
|
2395
|
+
SNDG1: "#555555",
|
|
2396
|
+
SNDG2: "#000000",
|
|
2397
|
+
DEPSC: "#000000",
|
|
2398
|
+
DEPCN: "#555555",
|
|
2399
|
+
UINFD: "#000000",
|
|
2400
|
+
UINFF: "#555555",
|
|
2401
|
+
UINFR: "#e00000",
|
|
2402
|
+
UINFG: "#00a000",
|
|
2403
|
+
UINFO: "#f07000",
|
|
2404
|
+
UINFB: "#0050c8",
|
|
2405
|
+
UINFM: "#c800c8",
|
|
2406
|
+
UIBCK: "#ffffff",
|
|
2407
|
+
UIBDR: "#555555",
|
|
2408
|
+
UIAFD: "#74ade0",
|
|
2409
|
+
UIAFF: "#c49a4a",
|
|
2410
|
+
PIPLN: "#008000",
|
|
2411
|
+
OUTLL: "#c49a4a",
|
|
2412
|
+
SCLBR: "#f07000",
|
|
2413
|
+
NINFO: "#f07000",
|
|
2414
|
+
ADINF: "#b08800",
|
|
2415
|
+
RESGR: "#555555",
|
|
2416
|
+
SHIPS: "#000000",
|
|
2417
|
+
PSTRK: "#000000",
|
|
2418
|
+
SYTRK: "#555555",
|
|
2419
|
+
APLRT: "#f07000",
|
|
2420
|
+
CURSR: "#f07000",
|
|
2421
|
+
BKAJ2: "#000000",
|
|
2422
|
+
RES01: "#808080",
|
|
2423
|
+
RES02: "#808080",
|
|
2424
|
+
RES03: "#808080"
|
|
2425
|
+
};
|
|
2426
|
+
var M = EINK_MONO_GREYS;
|
|
2427
|
+
var S52_EINK_MONO = {
|
|
2428
|
+
// No data is DARK, never the land tone: a no-data edge in the land tone
|
|
2429
|
+
// reads as a coastline (seen on the Boston render, 2026-10-05).
|
|
2430
|
+
NODTA: M.DARK,
|
|
2431
|
+
DEPIT: M.PAPER,
|
|
2432
|
+
DEPVS: M.PAPER,
|
|
2433
|
+
DEPMS: M.PAPER,
|
|
2434
|
+
DEPMD: M.PAPER,
|
|
2435
|
+
DEPDW: M.PAPER,
|
|
2436
|
+
LANDA: M.MID,
|
|
2437
|
+
LANDF: M.DARK,
|
|
2438
|
+
CHBRN: M.DARK,
|
|
2439
|
+
CHGRF: M.MID,
|
|
2440
|
+
CHGRD: M.DARK,
|
|
2441
|
+
CHBLK: M.INK,
|
|
2442
|
+
CSTLN: M.INK,
|
|
2443
|
+
OUTLW: M.INK,
|
|
2444
|
+
BKAJ1: M.DARK,
|
|
2445
|
+
TRFCD: M.INK,
|
|
2446
|
+
TRFCF: M.INK,
|
|
2447
|
+
RESBL: M.INK,
|
|
2448
|
+
APTS1: M.INK,
|
|
2449
|
+
DNGHL: M.INK,
|
|
2450
|
+
ISHDG: M.INK,
|
|
2451
|
+
LITRD: M.INK,
|
|
2452
|
+
LITGN: M.INK,
|
|
2453
|
+
LITYW: M.INK,
|
|
2454
|
+
LITWT: M.PAPER,
|
|
2455
|
+
LITBL: M.INK,
|
|
2456
|
+
LITOR: M.INK,
|
|
2457
|
+
RADHI: M.INK,
|
|
2458
|
+
RADLO: M.DARK,
|
|
2459
|
+
PLRTE: M.INK,
|
|
2460
|
+
ARPAT: M.INK,
|
|
2461
|
+
ISDNG: M.INK,
|
|
2462
|
+
CHRED: M.DARK,
|
|
2463
|
+
CHGRN: M.MID,
|
|
2464
|
+
CHYLW: M.MID,
|
|
2465
|
+
CHMGD: M.INK,
|
|
2466
|
+
CHMGF: M.INK,
|
|
2467
|
+
CHWHT: M.PAPER,
|
|
2468
|
+
CHCOR: M.MID,
|
|
2469
|
+
SNDG1: M.DARK,
|
|
2470
|
+
SNDG2: M.INK,
|
|
2471
|
+
DEPSC: M.INK,
|
|
2472
|
+
DEPCN: M.DARK,
|
|
2473
|
+
UINFD: M.INK,
|
|
2474
|
+
UINFF: M.DARK,
|
|
2475
|
+
UINFR: M.INK,
|
|
2476
|
+
UINFG: M.INK,
|
|
2477
|
+
UINFO: M.INK,
|
|
2478
|
+
UINFB: M.INK,
|
|
2479
|
+
UINFM: M.INK,
|
|
2480
|
+
UIBCK: M.PAPER,
|
|
2481
|
+
UIBDR: M.DARK,
|
|
2482
|
+
UIAFD: M.MID,
|
|
2483
|
+
UIAFF: M.MID,
|
|
2484
|
+
PIPLN: M.INK,
|
|
2485
|
+
OUTLL: M.DARK,
|
|
2486
|
+
SCLBR: M.INK,
|
|
2487
|
+
NINFO: M.INK,
|
|
2488
|
+
ADINF: M.DARK,
|
|
2489
|
+
RESGR: M.DARK,
|
|
2490
|
+
SHIPS: M.INK,
|
|
2491
|
+
PSTRK: M.INK,
|
|
2492
|
+
SYTRK: M.DARK,
|
|
2493
|
+
APLRT: M.INK,
|
|
2494
|
+
CURSR: M.INK,
|
|
2495
|
+
BKAJ2: M.INK,
|
|
2496
|
+
RES01: M.DARK,
|
|
2497
|
+
RES02: M.DARK,
|
|
2498
|
+
RES03: M.DARK
|
|
2499
|
+
};
|
|
2500
|
+
var N = { GROUND: M.INK, DIM: M.DARK, LINE: M.MID, BRIGHT: M.PAPER };
|
|
2501
|
+
var S52_EINK_NIGHT = {
|
|
2502
|
+
NODTA: N.GROUND,
|
|
2503
|
+
DEPIT: N.GROUND,
|
|
2504
|
+
DEPVS: N.GROUND,
|
|
2505
|
+
DEPMS: N.GROUND,
|
|
2506
|
+
DEPMD: N.GROUND,
|
|
2507
|
+
DEPDW: N.GROUND,
|
|
2508
|
+
LANDA: N.DIM,
|
|
2509
|
+
LANDF: N.LINE,
|
|
2510
|
+
CHBRN: N.LINE,
|
|
2511
|
+
CHGRF: N.DIM,
|
|
2512
|
+
CHGRD: N.LINE,
|
|
2513
|
+
CHBLK: N.LINE,
|
|
2514
|
+
CSTLN: N.LINE,
|
|
2515
|
+
OUTLW: N.LINE,
|
|
2516
|
+
BKAJ1: N.DIM,
|
|
2517
|
+
TRFCD: N.LINE,
|
|
2518
|
+
TRFCF: N.LINE,
|
|
2519
|
+
RESBL: N.LINE,
|
|
2520
|
+
APTS1: N.LINE,
|
|
2521
|
+
DNGHL: N.BRIGHT,
|
|
2522
|
+
ISHDG: N.BRIGHT,
|
|
2523
|
+
LITRD: N.BRIGHT,
|
|
2524
|
+
LITGN: N.BRIGHT,
|
|
2525
|
+
LITYW: N.BRIGHT,
|
|
2526
|
+
LITWT: N.BRIGHT,
|
|
2527
|
+
LITBL: N.BRIGHT,
|
|
2528
|
+
LITOR: N.BRIGHT,
|
|
2529
|
+
RADHI: N.LINE,
|
|
2530
|
+
RADLO: N.DIM,
|
|
2531
|
+
PLRTE: N.LINE,
|
|
2532
|
+
ARPAT: N.LINE,
|
|
2533
|
+
ISDNG: N.BRIGHT,
|
|
2534
|
+
// Mark bodies by tone, as in mono, so they stay apart from each other and
|
|
2535
|
+
// from the black water; shape still says which side.
|
|
2536
|
+
CHRED: N.DIM,
|
|
2537
|
+
CHGRN: N.LINE,
|
|
2538
|
+
CHYLW: N.LINE,
|
|
2539
|
+
CHMGD: N.LINE,
|
|
2540
|
+
CHMGF: N.LINE,
|
|
2541
|
+
CHWHT: N.BRIGHT,
|
|
2542
|
+
CHCOR: N.LINE,
|
|
2543
|
+
SNDG1: N.LINE,
|
|
2544
|
+
SNDG2: N.BRIGHT,
|
|
2545
|
+
DEPSC: N.BRIGHT,
|
|
2546
|
+
DEPCN: N.LINE,
|
|
2547
|
+
UINFD: N.LINE,
|
|
2548
|
+
UINFF: N.DIM,
|
|
2549
|
+
UINFR: N.BRIGHT,
|
|
2550
|
+
UINFG: N.LINE,
|
|
2551
|
+
UINFO: N.LINE,
|
|
2552
|
+
UINFB: N.LINE,
|
|
2553
|
+
UINFM: N.LINE,
|
|
2554
|
+
UIBCK: N.GROUND,
|
|
2555
|
+
UIBDR: N.DIM,
|
|
2556
|
+
UIAFD: N.DIM,
|
|
2557
|
+
UIAFF: N.DIM,
|
|
2558
|
+
PIPLN: N.LINE,
|
|
2559
|
+
OUTLL: N.DIM,
|
|
2560
|
+
SCLBR: N.LINE,
|
|
2561
|
+
NINFO: N.LINE,
|
|
2562
|
+
ADINF: N.DIM,
|
|
2563
|
+
RESGR: N.DIM,
|
|
2564
|
+
SHIPS: N.BRIGHT,
|
|
2565
|
+
PSTRK: N.LINE,
|
|
2566
|
+
SYTRK: N.DIM,
|
|
2567
|
+
APLRT: N.BRIGHT,
|
|
2568
|
+
CURSR: N.BRIGHT,
|
|
2569
|
+
BKAJ2: N.GROUND,
|
|
2570
|
+
RES01: N.DIM,
|
|
2571
|
+
RES02: N.DIM,
|
|
2572
|
+
RES03: N.DIM
|
|
2573
|
+
};
|
|
2340
2574
|
var S52_COLORS = S52_DAY;
|
|
2341
2575
|
var COLOR_TABLES = {
|
|
2342
2576
|
DAY: S52_DAY,
|
|
2343
2577
|
DUSK: S52_DUSK,
|
|
2344
|
-
NIGHT: S52_NIGHT
|
|
2578
|
+
NIGHT: S52_NIGHT,
|
|
2579
|
+
EINK_COLOR: S52_EINK_COLOR,
|
|
2580
|
+
EINK_MONO: S52_EINK_MONO,
|
|
2581
|
+
EINK_NIGHT: S52_EINK_NIGHT
|
|
2345
2582
|
};
|
|
2346
2583
|
function getColorTable(name) {
|
|
2347
2584
|
return COLOR_TABLES[name];
|
|
@@ -4988,6 +5225,254 @@ var InteractClient = class {
|
|
|
4988
5225
|
}
|
|
4989
5226
|
};
|
|
4990
5227
|
|
|
5228
|
+
// src/atlas/chrt-svgs-generated.ts
|
|
5229
|
+
var CHRT_GLYPH_SVGS = {
|
|
5230
|
+
"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>',
|
|
5231
|
+
"badge-sound": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="7" cy="16" r="2.2" fill="#D200D2"/><path d="M12 10a9 9 0 0 1 0 12M16.5 6.5a15 15 0 0 1 0 19M21 3a21 21 0 0 1 0 26" stroke="#D200D2" stroke-width="2.4" fill="none" stroke-linecap="round"/></svg>',
|
|
5232
|
+
"beacon-cairn": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="11" r="4" fill="#fff"/><circle cx="11.5" cy="19" r="4.6" fill="#fff"/><circle cx="20.5" cy="19" r="4.6" fill="#fff"/><path d="M7 28h18" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5233
|
+
"beacon-generic": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 25V12" stroke="#fff" stroke-width="2.6" stroke-linecap="round"/><path d="M16 4l6 8H10Z" fill="#fff"/><path d="M8 28h16M11 25h10" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5234
|
+
"beacon-lattice": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M11 25 13 6h6l2 19M12 20h8M12.6 13h6.8M11 25l9-12M21 25l-9-12" stroke="#fff" stroke-width="1.8" stroke-linecap="round" fill="none"/><path d="M8 28h16" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5235
|
+
"beacon-stake": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 25V6" stroke="#fff" stroke-width="2.8" stroke-linecap="round"/><path d="M9 28h14" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5236
|
+
"beacon-tower": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M12 6h8l1.6 19H10.4Z" fill="#fff"/><path d="M10 6h12" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><path d="M8 28h16" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5237
|
+
"buoy-barrel": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect x="7" y="13" width="18" height="11" rx="5.5" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5238
|
+
"buoy-can": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M9 12h14v11a2 2 0 0 1-2 2H11a2 2 0 0 1-2-2Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5239
|
+
"buoy-conical": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 8 24.5 25H7.5Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5240
|
+
"buoy-pillar": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M13 6h6l1.4 19H11.6Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5241
|
+
"buoy-spar": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M14.5 5h3l1 20h-5Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5242
|
+
"buoy-spherical": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="17.5" r="8.5" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5243
|
+
"buoy-super": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M7 17h18l-2.5 8h-13Z" fill="#fff"/><path d="M16 17V8" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><circle cx="16" cy="6.5" r="2" fill="#fff"/><path d="M5 28h22" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5244
|
+
"cardinal-e": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 1.5 20 6h-8Z" fill="#101418"/><path d="M16 11 12 6.5h8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5245
|
+
"cardinal-n": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 1.5 20 6h-8Z" fill="#101418"/><path d="M16 6.5 20 11h-8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5246
|
+
"cardinal-s": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 11 12 6.5h8Z" fill="#101418"/><path d="M16 6 12 1.5h8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5247
|
+
"cardinal-w": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 6 12 1.5h8Z" fill="#101418"/><path d="M16 6.5 20 11h-8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5248
|
+
"daymark-square": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect x="6" y="6" width="20" height="20" rx="1.5" fill="#fff"/></svg>',
|
|
5249
|
+
"daymark-triangle": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 5 28 26H4Z" fill="#fff"/></svg>',
|
|
5250
|
+
"eink-danger": '<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M-2 2 L2 -2 M0 8 L8 0 M6 10 L10 6" stroke="#000000" stroke-width="1" fill="none"/><path d="M-2 6 L2 10 M0 0 L8 8 M6 -2 L10 2" stroke="#000000" stroke-width="1" fill="none"/></svg>',
|
|
5251
|
+
"eink-dots": '<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10"><circle cx="2.5" cy="2.5" r="0.9" fill="#000000"/><circle cx="7.5" cy="7.5" r="0.9" fill="#000000"/></svg>',
|
|
5252
|
+
"eink-hatch": '<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M-2 2 L2 -2 M0 8 L8 0 M6 10 L10 6" stroke="#000000" stroke-width="0.9" fill="none"/></svg>',
|
|
5253
|
+
"hatch-nocoverage": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-3 9 L9 -3 M-3 21 L21 -3 M3 27 L27 3 M15 27 L27 15" stroke="#8A97A0" stroke-width="0.8" fill="none"/><path d="M-3 15 L15 33 M-3 3 L21 27 M3 -3 L27 21 M15 -3 L27 9" stroke="#8A97A0" stroke-width="0.8" fill="none"/></svg>',
|
|
5254
|
+
"hatch-uncharted": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-3 9 L9 -3 M-3 21 L21 -3 M3 27 L27 3 M15 27 L27 15" stroke="#5B7383" stroke-width="0.65" fill="none"/></svg>',
|
|
5255
|
+
"isolated-danger": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="3.5" r="2.4" fill="#101418"/><circle cx="16" cy="9.5" r="2.4" fill="#101418"/><path d="M13 13h6l1 12H12Z" fill="#101418"/><rect x="12.4" y="17.5" width="7.2" height="3.4" fill="#E5484D"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5256
|
+
"light-flare": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="11" cy="11" r="3.2" fill="#fff"/><path d="M13.4 13.4 24 19.5a2.6 2.6 0 0 1-4.5 4.5L13.4 13.4Z" fill="#fff"/></svg>',
|
|
5257
|
+
"lightship": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M6 20l3-5h14l3 5-3.5 4h-13Z" fill="#fff"/><path d="M16 15V8" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><circle cx="16" cy="6" r="2.2" fill="#fff"/><path d="M5 28h22" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
5258
|
+
"safe-water": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="6" r="2.4" fill="#E5484D"/><circle cx="16" cy="17.5" r="8" fill="#F8FAFC"/><path d="M12.2 10.5a8 8 0 0 0 0 14M19.8 10.5a8 8 0 0 1 0 14" stroke="#E5484D" stroke-width="3" fill="none"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>'
|
|
5259
|
+
};
|
|
5260
|
+
|
|
5261
|
+
// src/atlas/s52-pattern.ts
|
|
5262
|
+
var S52_PATTERN_SPRITE_PREFIX = "AP:";
|
|
5263
|
+
function patternSpriteId(patternName) {
|
|
5264
|
+
return `${S52_PATTERN_SPRITE_PREFIX}${patternName}`;
|
|
5265
|
+
}
|
|
5266
|
+
|
|
5267
|
+
// src/atlas/s52-conditional.ts
|
|
5268
|
+
var DEFAULT_SAFETY_CONTOUR_M = 10;
|
|
5269
|
+
var DEPARE_DEEP_CUT_M = 18.2;
|
|
5270
|
+
var DEPARE_SHALLOW_CUT_M = 2;
|
|
5271
|
+
function effectiveSafetyContour(safetyContourM) {
|
|
5272
|
+
if (!Number.isFinite(safetyContourM)) return DEFAULT_SAFETY_CONTOUR_M;
|
|
5273
|
+
return Math.min(Math.max(safetyContourM, DEPARE_SHALLOW_CUT_M), DEPARE_DEEP_CUT_M);
|
|
5274
|
+
}
|
|
5275
|
+
function depareColor(props, ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5276
|
+
const drval1 = Number(props.DRVAL1 ?? props.drval1 ?? -1);
|
|
5277
|
+
const drval2 = Number(props.DRVAL2 ?? props.drval2 ?? drval1);
|
|
5278
|
+
const safety = effectiveSafetyContour(safetyContourM);
|
|
5279
|
+
if (drval1 < 0 && drval2 <= 0) return resolveColor("DEPIT", ct);
|
|
5280
|
+
if (drval1 < DEPARE_SHALLOW_CUT_M) return resolveColor("DEPVS", ct);
|
|
5281
|
+
if (drval1 < safety) return resolveColor("DEPMS", ct);
|
|
5282
|
+
if (drval1 < DEPARE_DEEP_CUT_M) return resolveColor("DEPMD", ct);
|
|
5283
|
+
return resolveColor("DEPDW", ct);
|
|
5284
|
+
}
|
|
5285
|
+
var DEPARE_FILL_BRANCH_TOKENS = ["DEPIT", "DEPVS", "DEPMS", "DEPMD"];
|
|
5286
|
+
var DEPARE_FILL_FALLBACK_TOKEN = "DEPDW";
|
|
5287
|
+
function depareFillExpression(ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5288
|
+
const safety = effectiveSafetyContour(safetyContourM);
|
|
5289
|
+
const [it, vs, ms, md] = DEPARE_FILL_BRANCH_TOKENS;
|
|
5290
|
+
return [
|
|
5291
|
+
"case",
|
|
5292
|
+
[
|
|
5293
|
+
"all",
|
|
5294
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], 0],
|
|
5295
|
+
["<=", ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0], 0]
|
|
5296
|
+
],
|
|
5297
|
+
resolveColor(it, ct),
|
|
5298
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_SHALLOW_CUT_M],
|
|
5299
|
+
resolveColor(vs, ct),
|
|
5300
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], safety],
|
|
5301
|
+
resolveColor(ms, ct),
|
|
5302
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_DEEP_CUT_M],
|
|
5303
|
+
resolveColor(md, ct),
|
|
5304
|
+
resolveColor(DEPARE_FILL_FALLBACK_TOKEN, ct)
|
|
5305
|
+
];
|
|
5306
|
+
}
|
|
5307
|
+
function listAttrString(attr) {
|
|
5308
|
+
return ["to-string", ["coalesce", ["get", attr], ["get", attr.toLowerCase()], ""]];
|
|
5309
|
+
}
|
|
5310
|
+
function listAttrCase(attr, cases, fallback) {
|
|
5311
|
+
const attrStr = listAttrString(attr);
|
|
5312
|
+
const expr = ["case"];
|
|
5313
|
+
for (const [code, value] of cases) {
|
|
5314
|
+
expr.push([">=", ["index-of", `"${code}"`, attrStr], 0], value);
|
|
5315
|
+
}
|
|
5316
|
+
for (const [code, value] of cases) {
|
|
5317
|
+
expr.push(["==", attrStr, code], value);
|
|
5318
|
+
}
|
|
5319
|
+
for (const [code, value] of cases) {
|
|
5320
|
+
expr.push(["==", attrStr, `[${code}]`], value);
|
|
5321
|
+
}
|
|
5322
|
+
expr.push(fallback);
|
|
5323
|
+
return expr;
|
|
5324
|
+
}
|
|
5325
|
+
function listAttrHas(attr, codes) {
|
|
5326
|
+
const attrStr = listAttrString(attr);
|
|
5327
|
+
const clauses = [];
|
|
5328
|
+
for (const code of codes) {
|
|
5329
|
+
clauses.push([">=", ["index-of", `"${code}"`, attrStr], 0]);
|
|
5330
|
+
clauses.push(["==", attrStr, code]);
|
|
5331
|
+
clauses.push(["==", attrStr, `[${code}]`]);
|
|
5332
|
+
}
|
|
5333
|
+
return ["any", ...clauses];
|
|
5334
|
+
}
|
|
5335
|
+
function colourCodeCase(cases, fallback) {
|
|
5336
|
+
return listAttrCase("COLOUR", cases, fallback);
|
|
5337
|
+
}
|
|
5338
|
+
function lightColor(props, ct = "DAY") {
|
|
5339
|
+
const raw = props.COLOUR ?? props.colour;
|
|
5340
|
+
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5341
|
+
switch (code) {
|
|
5342
|
+
case 1:
|
|
5343
|
+
return resolveColor("LITWT", ct);
|
|
5344
|
+
case 3:
|
|
5345
|
+
return resolveColor("LITRD", ct);
|
|
5346
|
+
case 4:
|
|
5347
|
+
return resolveColor("LITGN", ct);
|
|
5348
|
+
case 5:
|
|
5349
|
+
return resolveColor("LITBL", ct);
|
|
5350
|
+
case 6:
|
|
5351
|
+
return resolveColor("LITYW", ct);
|
|
5352
|
+
case 11:
|
|
5353
|
+
return resolveColor("LITOR", ct);
|
|
5354
|
+
default:
|
|
5355
|
+
return resolveColor("LITYW", ct);
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
function lightFillExpression(ct = "DAY") {
|
|
5359
|
+
return colourCodeCase(
|
|
5360
|
+
[
|
|
5361
|
+
["3", resolveColor("LITRD", ct)],
|
|
5362
|
+
["4", resolveColor("LITGN", ct)],
|
|
5363
|
+
["6", resolveColor("LITYW", ct)],
|
|
5364
|
+
["11", resolveColor("LITOR", ct)],
|
|
5365
|
+
["5", resolveColor("LITBL", ct)],
|
|
5366
|
+
["1", resolveColor("LITWT", ct)]
|
|
5367
|
+
],
|
|
5368
|
+
resolveColor("LITYW", ct)
|
|
5369
|
+
);
|
|
5370
|
+
}
|
|
5371
|
+
function lateralColor(props, ct = "DAY") {
|
|
5372
|
+
const catlam = Number(props.CATLAM ?? props.catlam ?? 0);
|
|
5373
|
+
if (catlam === 1) return resolveColor("LITRD", ct);
|
|
5374
|
+
if (catlam === 2) return resolveColor("LITGN", ct);
|
|
5375
|
+
return resolveColor("LITYW", ct);
|
|
5376
|
+
}
|
|
5377
|
+
function lateralFillExpression(ct = "DAY") {
|
|
5378
|
+
return [
|
|
5379
|
+
"match",
|
|
5380
|
+
["coalesce", ["get", "CATLAM"], ["get", "catlam"], 0],
|
|
5381
|
+
1,
|
|
5382
|
+
resolveColor("LITRD", ct),
|
|
5383
|
+
2,
|
|
5384
|
+
resolveColor("LITGN", ct),
|
|
5385
|
+
resolveColor("LITYW", ct)
|
|
5386
|
+
];
|
|
5387
|
+
}
|
|
5388
|
+
function hazardColor(props, ct = "DAY") {
|
|
5389
|
+
const watlev = Number(props.WATLEV ?? props.watlev ?? 0);
|
|
5390
|
+
const valsou = props.VALSOU ?? props.valsou;
|
|
5391
|
+
if (watlev === 1 || watlev === 2) return resolveColor("DNGHL", ct);
|
|
5392
|
+
if (valsou != null && Number(valsou) < 2) return resolveColor("DNGHL", ct);
|
|
5393
|
+
return resolveColor("RESBL", ct);
|
|
5394
|
+
}
|
|
5395
|
+
function hazardFillExpression(ct = "DAY") {
|
|
5396
|
+
return [
|
|
5397
|
+
"case",
|
|
5398
|
+
[
|
|
5399
|
+
"any",
|
|
5400
|
+
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 1],
|
|
5401
|
+
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 2]
|
|
5402
|
+
],
|
|
5403
|
+
resolveColor("DNGHL", ct),
|
|
5404
|
+
[
|
|
5405
|
+
"all",
|
|
5406
|
+
["has", "VALSOU"],
|
|
5407
|
+
["<", ["get", "VALSOU"], 2]
|
|
5408
|
+
],
|
|
5409
|
+
resolveColor("DNGHL", ct),
|
|
5410
|
+
resolveColor("RESBL", ct)
|
|
5411
|
+
];
|
|
5412
|
+
}
|
|
5413
|
+
function vegatnPatternExpression() {
|
|
5414
|
+
return [
|
|
5415
|
+
"match",
|
|
5416
|
+
["coalesce", ["get", "CATVEG"], ["get", "catveg"], 0],
|
|
5417
|
+
1,
|
|
5418
|
+
patternSpriteId("VEGATN03"),
|
|
5419
|
+
// grass
|
|
5420
|
+
3,
|
|
5421
|
+
patternSpriteId("VEGATN03"),
|
|
5422
|
+
// bush
|
|
5423
|
+
4,
|
|
5424
|
+
patternSpriteId("VEGATN04"),
|
|
5425
|
+
// deciduous wood
|
|
5426
|
+
5,
|
|
5427
|
+
patternSpriteId("VEGATN04"),
|
|
5428
|
+
// coniferous wood
|
|
5429
|
+
6,
|
|
5430
|
+
patternSpriteId("VEGATN04"),
|
|
5431
|
+
// wood (mixed)
|
|
5432
|
+
7,
|
|
5433
|
+
patternSpriteId("MARSHES1"),
|
|
5434
|
+
// mangroves
|
|
5435
|
+
10,
|
|
5436
|
+
patternSpriteId("VEGATN03"),
|
|
5437
|
+
// scrub
|
|
5438
|
+
11,
|
|
5439
|
+
patternSpriteId("VEGATN04"),
|
|
5440
|
+
// tree
|
|
5441
|
+
12,
|
|
5442
|
+
patternSpriteId("VEGATN04"),
|
|
5443
|
+
// evergreen
|
|
5444
|
+
16,
|
|
5445
|
+
patternSpriteId("MARSHES1"),
|
|
5446
|
+
// swamp
|
|
5447
|
+
17,
|
|
5448
|
+
patternSpriteId("MARSHES1"),
|
|
5449
|
+
// marsh
|
|
5450
|
+
21,
|
|
5451
|
+
patternSpriteId("VEGATN04"),
|
|
5452
|
+
// savannah
|
|
5453
|
+
patternSpriteId("VEGATN03")
|
|
5454
|
+
// default: grass pattern
|
|
5455
|
+
];
|
|
5456
|
+
}
|
|
5457
|
+
function daymarkColor(props, ct = "DAY") {
|
|
5458
|
+
const raw = props.COLOUR ?? props.colour;
|
|
5459
|
+
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5460
|
+
switch (code) {
|
|
5461
|
+
case 1:
|
|
5462
|
+
return resolveColor("LITWT", ct);
|
|
5463
|
+
case 3:
|
|
5464
|
+
return resolveColor("LITRD", ct);
|
|
5465
|
+
case 4:
|
|
5466
|
+
return resolveColor("LITGN", ct);
|
|
5467
|
+
case 6:
|
|
5468
|
+
return resolveColor("LITYW", ct);
|
|
5469
|
+
case 11:
|
|
5470
|
+
return resolveColor("LITOR", ct);
|
|
5471
|
+
default:
|
|
5472
|
+
return resolveColor("LITGN", ct);
|
|
5473
|
+
}
|
|
5474
|
+
}
|
|
5475
|
+
|
|
4991
5476
|
// src/atlas/palette-light.ts
|
|
4992
5477
|
var HEX6 = /^#?([0-9a-f]{6})$/i;
|
|
4993
5478
|
function channels(hex) {
|
|
@@ -5026,8 +5511,971 @@ function capLuminance(hex, ceiling) {
|
|
|
5026
5511
|
}
|
|
5027
5512
|
var NIGHT_INK = S52_NIGHT.CHBLK;
|
|
5028
5513
|
var NIGHT_MUTED_INK = scaleLinearLight(S52_NIGHT.CHBLK, 0.43);
|
|
5029
|
-
|
|
5030
|
-
|
|
5514
|
+
function einkMonoGrey(hex) {
|
|
5515
|
+
const l = relativeLuminance(hex);
|
|
5516
|
+
const g2 = EINK_MONO_GREYS;
|
|
5517
|
+
const steps = [g2.INK, g2.DARK, g2.MID, g2.PAPER].map((c) => [c, relativeLuminance(c)]);
|
|
5518
|
+
let best = steps[0];
|
|
5519
|
+
for (const step of steps) if (Math.abs(step[1] - l) < Math.abs(best[1] - l)) best = step;
|
|
5520
|
+
return best[0];
|
|
5521
|
+
}
|
|
5522
|
+
function einkNightGrey(hex) {
|
|
5523
|
+
const l = relativeLuminance(hex);
|
|
5524
|
+
const g2 = EINK_MONO_GREYS;
|
|
5525
|
+
const steps = [g2.DARK, g2.MID, g2.PAPER].map((c) => [c, relativeLuminance(c)]);
|
|
5526
|
+
let best = steps[0];
|
|
5527
|
+
for (const step of steps) if (Math.abs(step[1] - l) < Math.abs(best[1] - l)) best = step;
|
|
5528
|
+
return best[0];
|
|
5529
|
+
}
|
|
5530
|
+
|
|
5531
|
+
// src/atlas/chrt-style.ts
|
|
5532
|
+
var CHRT_ICON_PREFIX = "chrt:";
|
|
5533
|
+
var CHRT_SDF_GLYPHS = /* @__PURE__ */ new Set([
|
|
5534
|
+
"buoy-can",
|
|
5535
|
+
"buoy-conical",
|
|
5536
|
+
"buoy-spherical",
|
|
5537
|
+
"buoy-pillar",
|
|
5538
|
+
"buoy-spar",
|
|
5539
|
+
"buoy-barrel",
|
|
5540
|
+
"buoy-super",
|
|
5541
|
+
"beacon-generic",
|
|
5542
|
+
"beacon-tower",
|
|
5543
|
+
"beacon-lattice",
|
|
5544
|
+
"beacon-stake",
|
|
5545
|
+
"beacon-cairn",
|
|
5546
|
+
"light-flare",
|
|
5547
|
+
"lightship",
|
|
5548
|
+
"daymark-triangle",
|
|
5549
|
+
"daymark-square"
|
|
5550
|
+
]);
|
|
5551
|
+
var CHRT_GLYPH_NAMES = new Set(Object.keys(CHRT_GLYPH_SVGS));
|
|
5552
|
+
var CHRT_ATON_COLORS = {
|
|
5553
|
+
"1": "#F8FAFC",
|
|
5554
|
+
// white
|
|
5555
|
+
"2": "#101418",
|
|
5556
|
+
// black
|
|
5557
|
+
"3": "#E5484D",
|
|
5558
|
+
// red
|
|
5559
|
+
"4": "#30A46C",
|
|
5560
|
+
// green
|
|
5561
|
+
"5": "#3E63DD",
|
|
5562
|
+
// blue
|
|
5563
|
+
"6": "#FFC805",
|
|
5564
|
+
// yellow
|
|
5565
|
+
"7": "#8B9298",
|
|
5566
|
+
// grey
|
|
5567
|
+
"8": "#A66B3A",
|
|
5568
|
+
// brown
|
|
5569
|
+
"9": "#FFB224",
|
|
5570
|
+
// amber
|
|
5571
|
+
"10": "#7E4CCB",
|
|
5572
|
+
// violet
|
|
5573
|
+
"11": "#F76B15",
|
|
5574
|
+
// orange
|
|
5575
|
+
"12": "#D671C1"
|
|
5576
|
+
// pink (magenta-ish)
|
|
5577
|
+
};
|
|
5578
|
+
var CHRT_UNKNOWN_TINT = "#8B9298";
|
|
5579
|
+
var HOUSE_BLACK = "#101418";
|
|
5580
|
+
function chrtHouseColor(dayHex, palette = "DAY") {
|
|
5581
|
+
if (palette === "DAY" || palette === "EINK_COLOR") return dayHex;
|
|
5582
|
+
if (palette === "EINK_MONO") return einkMonoGrey(dayHex);
|
|
5583
|
+
if (palette === "EINK_NIGHT") return dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase() || dayHex.toLowerCase() === "#000000" ? "#aaaaaa" : einkNightGrey(dayHex);
|
|
5584
|
+
const isBlack = dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase();
|
|
5585
|
+
if (palette === "DUSK") {
|
|
5586
|
+
return isBlack ? dayHex : capLuminance(dayHex, relativeLuminance(resolveColor("CHBLK", "DUSK")));
|
|
5587
|
+
}
|
|
5588
|
+
return isBlack ? NIGHT_INK : capLuminance(dayHex, relativeLuminance(NIGHT_MUTED_INK));
|
|
5589
|
+
}
|
|
5590
|
+
function themeChrtGlyph(svg3, palette = "DAY") {
|
|
5591
|
+
if (palette === "DAY" || palette === "EINK_COLOR") return svg3;
|
|
5592
|
+
return svg3.replace(
|
|
5593
|
+
/(fill|stroke)="(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})"/g,
|
|
5594
|
+
(_m, attr, hex) => {
|
|
5595
|
+
const six = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
5596
|
+
return `${attr}="${chrtHouseColor(six, palette)}"`;
|
|
5597
|
+
}
|
|
5598
|
+
);
|
|
5599
|
+
}
|
|
5600
|
+
function chrtAtonTintExpression(palette = "DAY") {
|
|
5601
|
+
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
5602
|
+
return colourCodeCase(
|
|
5603
|
+
[
|
|
5604
|
+
["3", c("3")],
|
|
5605
|
+
["4", c("4")],
|
|
5606
|
+
["6", c("6")],
|
|
5607
|
+
["11", c("11")],
|
|
5608
|
+
["9", c("9")],
|
|
5609
|
+
["5", c("5")],
|
|
5610
|
+
["1", c("1")],
|
|
5611
|
+
["2", c("2")],
|
|
5612
|
+
["8", c("8")],
|
|
5613
|
+
["10", c("10")],
|
|
5614
|
+
["12", c("12")],
|
|
5615
|
+
["7", c("7")]
|
|
5616
|
+
],
|
|
5617
|
+
chrtHouseColor(CHRT_UNKNOWN_TINT, palette)
|
|
5618
|
+
);
|
|
5619
|
+
}
|
|
5620
|
+
function chrtLightTintExpression(palette = "DAY") {
|
|
5621
|
+
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
5622
|
+
return colourCodeCase(
|
|
5623
|
+
[
|
|
5624
|
+
["3", c("3")],
|
|
5625
|
+
["4", c("4")],
|
|
5626
|
+
["6", c("6")],
|
|
5627
|
+
["11", c("11")],
|
|
5628
|
+
["9", c("9")],
|
|
5629
|
+
["5", c("5")],
|
|
5630
|
+
["1", c("1")]
|
|
5631
|
+
],
|
|
5632
|
+
c("1")
|
|
5633
|
+
);
|
|
5634
|
+
}
|
|
5635
|
+
function scalar(attr) {
|
|
5636
|
+
return ["coalesce", ["get", attr], ["get", attr.toLowerCase()], 0];
|
|
5637
|
+
}
|
|
5638
|
+
var g = (name) => `${CHRT_ICON_PREFIX}${name}`;
|
|
5639
|
+
function buoyBodyExpression() {
|
|
5640
|
+
return [
|
|
5641
|
+
"match",
|
|
5642
|
+
scalar("BOYSHP"),
|
|
5643
|
+
1,
|
|
5644
|
+
g("buoy-conical"),
|
|
5645
|
+
2,
|
|
5646
|
+
g("buoy-can"),
|
|
5647
|
+
3,
|
|
5648
|
+
g("buoy-spherical"),
|
|
5649
|
+
4,
|
|
5650
|
+
g("buoy-pillar"),
|
|
5651
|
+
5,
|
|
5652
|
+
g("buoy-spar"),
|
|
5653
|
+
6,
|
|
5654
|
+
g("buoy-barrel"),
|
|
5655
|
+
7,
|
|
5656
|
+
g("buoy-super"),
|
|
5657
|
+
8,
|
|
5658
|
+
g("buoy-super"),
|
|
5659
|
+
g("buoy-pillar")
|
|
5660
|
+
];
|
|
5661
|
+
}
|
|
5662
|
+
function beaconBodyExpression() {
|
|
5663
|
+
return [
|
|
5664
|
+
"match",
|
|
5665
|
+
scalar("BCNSHP"),
|
|
5666
|
+
1,
|
|
5667
|
+
g("beacon-stake"),
|
|
5668
|
+
2,
|
|
5669
|
+
g("beacon-stake"),
|
|
5670
|
+
3,
|
|
5671
|
+
g("beacon-tower"),
|
|
5672
|
+
4,
|
|
5673
|
+
g("beacon-lattice"),
|
|
5674
|
+
5,
|
|
5675
|
+
g("beacon-stake"),
|
|
5676
|
+
6,
|
|
5677
|
+
g("beacon-cairn"),
|
|
5678
|
+
g("beacon-generic")
|
|
5679
|
+
];
|
|
5680
|
+
}
|
|
5681
|
+
function cardinalExpression() {
|
|
5682
|
+
return [
|
|
5683
|
+
"match",
|
|
5684
|
+
scalar("CATCAM"),
|
|
5685
|
+
1,
|
|
5686
|
+
g("cardinal-n"),
|
|
5687
|
+
2,
|
|
5688
|
+
g("cardinal-e"),
|
|
5689
|
+
3,
|
|
5690
|
+
g("cardinal-s"),
|
|
5691
|
+
4,
|
|
5692
|
+
g("cardinal-w"),
|
|
5693
|
+
g("cardinal-n")
|
|
5694
|
+
];
|
|
5695
|
+
}
|
|
5696
|
+
var CHRT_ICON_EXPRESSIONS = {
|
|
5697
|
+
BOYLAT: buoyBodyExpression,
|
|
5698
|
+
BOYSPP: buoyBodyExpression,
|
|
5699
|
+
BOYINB: buoyBodyExpression,
|
|
5700
|
+
BOYCAR: cardinalExpression,
|
|
5701
|
+
BOYISD: () => g("isolated-danger"),
|
|
5702
|
+
BOYSAW: () => g("safe-water"),
|
|
5703
|
+
BCNLAT: beaconBodyExpression,
|
|
5704
|
+
BCNSPP: beaconBodyExpression,
|
|
5705
|
+
BCNCAR: cardinalExpression,
|
|
5706
|
+
BCNISD: () => g("isolated-danger"),
|
|
5707
|
+
BCNSAW: () => g("safe-water"),
|
|
5708
|
+
LIGHTS: () => g("light-flare"),
|
|
5709
|
+
LITFLT: () => g("lightship"),
|
|
5710
|
+
LITVES: () => g("lightship"),
|
|
5711
|
+
FOGSIG: () => g("badge-sound"),
|
|
5712
|
+
RTPBCN: () => g("badge-radar"),
|
|
5713
|
+
RDOSTA: () => g("badge-radar"),
|
|
5714
|
+
DAYMAR: () => [
|
|
5715
|
+
"match",
|
|
5716
|
+
scalar("TOPSHP"),
|
|
5717
|
+
1,
|
|
5718
|
+
g("daymark-triangle"),
|
|
5719
|
+
2,
|
|
5720
|
+
g("daymark-triangle"),
|
|
5721
|
+
5,
|
|
5722
|
+
g("daymark-triangle"),
|
|
5723
|
+
g("daymark-square")
|
|
5724
|
+
]
|
|
5725
|
+
};
|
|
5726
|
+
var TINTED_CLASSES = /* @__PURE__ */ new Set([
|
|
5727
|
+
"BOYLAT",
|
|
5728
|
+
"BOYSPP",
|
|
5729
|
+
"BOYINB",
|
|
5730
|
+
"BCNLAT",
|
|
5731
|
+
"BCNSPP",
|
|
5732
|
+
"DAYMAR"
|
|
5733
|
+
]);
|
|
5734
|
+
var CHRT_HALO = { color: HOUSE_BLACK, width: 1 };
|
|
5735
|
+
var CHRT_LINE_DASH = {
|
|
5736
|
+
CBLSUB: { color: "CHMGD", width: 0.8, dash: [6, 3] },
|
|
5737
|
+
PIPSOL: { color: "PIPLN", width: 0.8, dash: [6, 3] }
|
|
5738
|
+
};
|
|
5739
|
+
function applyChrtSymbolSet(style, palette = "DAY") {
|
|
5740
|
+
for (const layer of style.layers) {
|
|
5741
|
+
if (layer.type === "line") {
|
|
5742
|
+
const cls = layer.metadata?.["s52:objectClass"];
|
|
5743
|
+
const dash = cls ? CHRT_LINE_DASH[cls] : void 0;
|
|
5744
|
+
const paint2 = layer.paint ?? {};
|
|
5745
|
+
if (dash && paint2["line-pattern"] != null) {
|
|
5746
|
+
delete paint2["line-pattern"];
|
|
5747
|
+
paint2["line-color"] = resolveColor(dash.color, palette);
|
|
5748
|
+
paint2["line-width"] = dash.width;
|
|
5749
|
+
paint2["line-dasharray"] = dash.dash;
|
|
5750
|
+
layer.paint = paint2;
|
|
5751
|
+
}
|
|
5752
|
+
continue;
|
|
5753
|
+
}
|
|
5754
|
+
if (layer.type !== "symbol") continue;
|
|
5755
|
+
const objectClass2 = layer.metadata?.["s52:objectClass"];
|
|
5756
|
+
if (!objectClass2 || !CHRT_ICON_EXPRESSIONS[objectClass2]) continue;
|
|
5757
|
+
const layout = layer.layout;
|
|
5758
|
+
if (!layout || layout["icon-image"] == null) continue;
|
|
5759
|
+
layout["icon-image"] = CHRT_ICON_EXPRESSIONS[objectClass2]();
|
|
5760
|
+
const paint = layer.paint ?? {};
|
|
5761
|
+
const haloColor = palette === "EINK_MONO" ? chrtHouseColor(HOUSE_BLACK, palette) : palette === "EINK_NIGHT" ? "#000000" : CHRT_HALO.color;
|
|
5762
|
+
if (TINTED_CLASSES.has(objectClass2)) {
|
|
5763
|
+
paint["icon-color"] = chrtAtonTintExpression(palette);
|
|
5764
|
+
paint["icon-halo-color"] = haloColor;
|
|
5765
|
+
paint["icon-halo-width"] = CHRT_HALO.width;
|
|
5766
|
+
} else if (objectClass2 === "LIGHTS") {
|
|
5767
|
+
paint["icon-color"] = chrtLightTintExpression(palette);
|
|
5768
|
+
paint["icon-halo-color"] = haloColor;
|
|
5769
|
+
paint["icon-halo-width"] = CHRT_HALO.width;
|
|
5770
|
+
}
|
|
5771
|
+
layer.paint = paint;
|
|
5772
|
+
}
|
|
5773
|
+
const metadata = style.metadata;
|
|
5774
|
+
metadata["chrt:symbol-set"] = "chrt";
|
|
5775
|
+
return style;
|
|
5776
|
+
}
|
|
5777
|
+
|
|
5778
|
+
// src/atlas/base-layers.ts
|
|
5779
|
+
var NOCOVERAGE_PATTERN = "chrt:hatch-nocoverage";
|
|
5780
|
+
var UNCHARTED_PATTERN = "chrt:hatch-uncharted";
|
|
5781
|
+
function inlandWaterColor(palette) {
|
|
5782
|
+
return (BASE_THEMES[palette] ?? BASE_THEMES.DAY).inlandWater;
|
|
5783
|
+
}
|
|
5784
|
+
var BASE_THEMES = {
|
|
5785
|
+
DAY: {
|
|
5786
|
+
// ── `earth` is the jurisdiction tan DIMMED (~0.55), not a blue-grey ──
|
|
5787
|
+
// Outside the jurisdiction the map still owes the reader "what is land".
|
|
5788
|
+
// State 3 in the header is "the geography, under a crosshatch", and until
|
|
5789
|
+
// 2026-08-31 that geography was unreadable: earth #2A3439 against sea
|
|
5790
|
+
// #1B2328 measured 1.25:1, so Hispaniola showed charted boxes with tan
|
|
5791
|
+
// land inside and nothing recognisable around them (owner report: "no
|
|
5792
|
+
// land painting down near the DR at some zooms"). Land is warm and sea is
|
|
5793
|
+
// cool because that is what land and sea are; dimming the JURISDICTION
|
|
5794
|
+
// tan rather than inventing a colour says the true thing — this is the
|
|
5795
|
+
// same land, we simply do not carry charts for it. Held against BOTH
|
|
5796
|
+
// neighbours: legible over sea, and never mistakable for the full tan of
|
|
5797
|
+
// a country we do carry. DAY 3.59:1 vs sea, 3.17:1 vs tan.
|
|
5798
|
+
earth: "#807761",
|
|
5799
|
+
water: "#1B2328",
|
|
5800
|
+
river: "#24313A",
|
|
5801
|
+
boundary: "#4A575E",
|
|
5802
|
+
jurisdiction: "#D4C69D",
|
|
5803
|
+
// CHRTland's own tan — one land colour
|
|
5804
|
+
inlandWater: "#B9CFDD",
|
|
5805
|
+
// muted, unmistakably not a charted blue
|
|
5806
|
+
// The uncharted hatch's stripe, held to 2.3:1 against `inlandWater` in
|
|
5807
|
+
// every palette (`inland-hatch-contrast.test.ts`) — the hatch says "we have
|
|
5808
|
+
// not charted this water", and stripes the eye cannot find say nothing.
|
|
5809
|
+
// DAY 3.08:1, darker than its ground. Also the colour the artwork file
|
|
5810
|
+
// itself is drawn in.
|
|
5811
|
+
inlandHatch: "#5B7383",
|
|
5812
|
+
// State furniture. These three were referenced by the layers but missing
|
|
5813
|
+
// from this table (and its type — the long-standing TS2339 trio), so the
|
|
5814
|
+
// lines shipped on MapLibre's DEFAULT black. Now deliberate: a hairline
|
|
5815
|
+
// a shade deeper than the tan, quiet caps, tan halo.
|
|
5816
|
+
stateLine: "#B1A077",
|
|
5817
|
+
stateLabel: "#8E8468",
|
|
5818
|
+
stateHalo: "#D4C69D",
|
|
5819
|
+
// Rough-lake furniture (owner ask 2026-08-28: "the lakes need labels,
|
|
5820
|
+
// and shorelines drawn"): a shoreline a shade deeper than the rough
|
|
5821
|
+
// water, and a name in the water's own voice, haloed by the water.
|
|
5822
|
+
inlandShore: "#8FA9BA",
|
|
5823
|
+
inlandLabel: "#54707F",
|
|
5824
|
+
inlandHalo: "#B9CFDD"
|
|
5825
|
+
},
|
|
5826
|
+
DUSK: {
|
|
5827
|
+
// ── Re-derived 2026-09-01: the basemap sits UNDER the chart ──────────
|
|
5828
|
+
// These were the DAY tones scaled by a constant, never measured against
|
|
5829
|
+
// the DUSK chart — so `jurisdiction` came out at L=0.506 against a chart
|
|
5830
|
+
// whose own land (LANDA #8a7d5e) is L=0.209, i.e. the context layer was
|
|
5831
|
+
// 2.4x brighter than the data layer. Now capped at the chart's own land.
|
|
5832
|
+
// The rest of the block rides the same 0.666 channel factor so every
|
|
5833
|
+
// relationship the palette already chose is preserved, only moved down.
|
|
5834
|
+
// `water` is deliberately NOT scaled: it is the reference the land/sea
|
|
5835
|
+
// floor is measured against.
|
|
5836
|
+
earth: "#544E3E",
|
|
5837
|
+
water: "#161D21",
|
|
5838
|
+
river: "#141B20",
|
|
5839
|
+
boundary: "#293135",
|
|
5840
|
+
jurisdiction: "#867D64",
|
|
5841
|
+
inlandWater: "#62717A",
|
|
5842
|
+
// Owner's choice, 2026-10-04: dark stripes, 2.37:1. The DAY stripe here
|
|
5843
|
+
// was 1.02:1 — invisible, so an uncharted lake read as plain water.
|
|
5844
|
+
inlandHatch: "#2E383F",
|
|
5845
|
+
// Label stays DARK here — the halo is still mid-toned, so 3:1 is reachable
|
|
5846
|
+
// downward and a bright label would be a new light source at dusk.
|
|
5847
|
+
stateLine: "#B5A987",
|
|
5848
|
+
stateLabel: "#38342A",
|
|
5849
|
+
stateHalo: "#867D64",
|
|
5850
|
+
inlandShore: "#4D5B67",
|
|
5851
|
+
inlandLabel: "#212629",
|
|
5852
|
+
inlandHalo: "#62717A"
|
|
5853
|
+
},
|
|
5854
|
+
NIGHT: {
|
|
5855
|
+
// ── Re-derived 2026-09-01 — the owner-reported night defect ──────────
|
|
5856
|
+
// `jurisdiction` was L=0.240 against a chart whose own land (LANDA
|
|
5857
|
+
// #3a3020) is L=0.031: the world basemap was 7.7x BRIGHTER than the chart
|
|
5858
|
+
// it sits under, so at z<11 the uncharted interior read as a pale
|
|
5859
|
+
// continent-sized slab on a night chart. Capped at the chart's own land.
|
|
5860
|
+
//
|
|
5861
|
+
// ⚠ `earth` AND `jurisdiction` CONVERGE HERE, and that is forced, not
|
|
5862
|
+
// sloppy. Holding earth 2:1 above the ocean AND 2:1 below the jurisdiction
|
|
5863
|
+
// tan requires jurisdiction >= L 0.174 — 5.6x the chart's own land. At
|
|
5864
|
+
// night the range between the ocean (L 0.006) and the cap (L 0.031) is too
|
|
5865
|
+
// compressed to carry two land tones. COVERAGE IS STILL SAID, by the
|
|
5866
|
+
// `chrt:hatch-nocoverage` crosshatch, which is a stronger signal than tone
|
|
5867
|
+
// and is the one thing that cannot be lost to palette compression.
|
|
5868
|
+
//
|
|
5869
|
+
// Labels invert to light-on-dark: 3:1 downward is unreachable off a halo
|
|
5870
|
+
// this close to black, and CHRTland's own night town labels already sit at
|
|
5871
|
+
// L=0.177 for exactly this reason.
|
|
5872
|
+
earth: "#333026",
|
|
5873
|
+
water: "#10161A",
|
|
5874
|
+
river: "#090D0F",
|
|
5875
|
+
boundary: "#131719",
|
|
5876
|
+
jurisdiction: "#353128",
|
|
5877
|
+
inlandWater: "#232A30",
|
|
5878
|
+
// The DAY stripe, lighter than this ground: 2.92:1, and L 0.16, under the
|
|
5879
|
+
// NIGHT muted-ink ceiling (`night-luminance.test.ts`). Red night draws the
|
|
5880
|
+
// NIGHT chart, so this is its stripe too.
|
|
5881
|
+
inlandHatch: "#5B7383",
|
|
5882
|
+
stateLine: "#484236",
|
|
5883
|
+
stateLabel: "#837963",
|
|
5884
|
+
stateHalo: "#353128",
|
|
5885
|
+
inlandShore: "#1C2227",
|
|
5886
|
+
inlandLabel: "#617484",
|
|
5887
|
+
inlandHalo: "#232A30"
|
|
5888
|
+
},
|
|
5889
|
+
// ── E-ink: uncharted is DARK, never paper ─────────────────────────────
|
|
5890
|
+
// On an e-ink chart, charted safe water is paper. Water outside coverage
|
|
5891
|
+
// must never read as that, so it is the darkest fill on the page — the
|
|
5892
|
+
// e-ink twin of DAY's dark carto — with the no-coverage crosshatch over it
|
|
5893
|
+
// (invariant 5: the coverage portrayal survives). Land outside coverage
|
|
5894
|
+
// keeps the chart's own land tone.
|
|
5895
|
+
EINK_COLOR: {
|
|
5896
|
+
earth: "#d8ccb0",
|
|
5897
|
+
water: "#4f5a62",
|
|
5898
|
+
river: "#4f5a62",
|
|
5899
|
+
boundary: "#000000",
|
|
5900
|
+
jurisdiction: "#f2e2b0",
|
|
5901
|
+
inlandWater: "#c8d6e0",
|
|
5902
|
+
// The house stripe, 3.4:1 on this rough water — strong enough for the
|
|
5903
|
+
// 150 ppi colour layer.
|
|
5904
|
+
inlandHatch: "#5B7383",
|
|
5905
|
+
stateLine: "#555555",
|
|
5906
|
+
stateLabel: "#000000",
|
|
5907
|
+
stateHalo: "#ffffff",
|
|
5908
|
+
inlandShore: "#555555",
|
|
5909
|
+
inlandLabel: "#000000",
|
|
5910
|
+
inlandHalo: "#ffffff"
|
|
5911
|
+
},
|
|
5912
|
+
// Mono: the same rule in the four greys. Uncharted water DARK under a MID
|
|
5913
|
+
// crosshatch; land MID; rough inland water PAPER under the uncharted hatch.
|
|
5914
|
+
EINK_MONO: {
|
|
5915
|
+
earth: "#aaaaaa",
|
|
5916
|
+
water: "#555555",
|
|
5917
|
+
river: "#555555",
|
|
5918
|
+
boundary: "#000000",
|
|
5919
|
+
jurisdiction: "#aaaaaa",
|
|
5920
|
+
inlandWater: "#ffffff",
|
|
5921
|
+
inlandHatch: "#555555",
|
|
5922
|
+
stateLine: "#555555",
|
|
5923
|
+
stateLabel: "#000000",
|
|
5924
|
+
stateHalo: "#ffffff",
|
|
5925
|
+
inlandShore: "#555555",
|
|
5926
|
+
inlandLabel: "#000000",
|
|
5927
|
+
inlandHalo: "#ffffff"
|
|
5928
|
+
},
|
|
5929
|
+
// Night: the inverse. Water outside coverage is the black ground UNDER the
|
|
5930
|
+
// light crosshatch — charted safe water is plain black, so the hatch is what
|
|
5931
|
+
// tells them apart, here as everywhere. Land the DIM grey; labels the line
|
|
5932
|
+
// grey on black.
|
|
5933
|
+
EINK_NIGHT: {
|
|
5934
|
+
earth: "#555555",
|
|
5935
|
+
water: "#000000",
|
|
5936
|
+
river: "#000000",
|
|
5937
|
+
boundary: "#aaaaaa",
|
|
5938
|
+
jurisdiction: "#555555",
|
|
5939
|
+
inlandWater: "#000000",
|
|
5940
|
+
inlandHatch: "#aaaaaa",
|
|
5941
|
+
stateLine: "#aaaaaa",
|
|
5942
|
+
stateLabel: "#aaaaaa",
|
|
5943
|
+
stateHalo: "#000000",
|
|
5944
|
+
inlandShore: "#aaaaaa",
|
|
5945
|
+
inlandLabel: "#aaaaaa",
|
|
5946
|
+
inlandHalo: "#000000"
|
|
5947
|
+
}
|
|
5948
|
+
};
|
|
5949
|
+
function baseLayers(opts = {}) {
|
|
5950
|
+
const palette = opts.palette ?? "DAY";
|
|
5951
|
+
const T = BASE_THEMES[palette] ?? BASE_THEMES.DAY;
|
|
5952
|
+
const src = opts.sourceId ?? BASE_REGION;
|
|
5953
|
+
const L = (o) => o;
|
|
5954
|
+
return [
|
|
5955
|
+
// 1. Geography — the world as it is, in the quiet dark voice. The OCEAN
|
|
5956
|
+
// is a background, not a polygon: the archive ships land and lakes,
|
|
5957
|
+
// and "everything else is sea" is exactly what a background expresses
|
|
5958
|
+
// — geometry-free, gap-free at every antimeridian and pole. Without
|
|
5959
|
+
// it the sea outside coverage was bare S-52 NODTA, a light grey that
|
|
5960
|
+
// read as "charted ground" with a hatch on it.
|
|
5961
|
+
L({
|
|
5962
|
+
id: "chrtbase-ocean",
|
|
5963
|
+
type: "background",
|
|
5964
|
+
paint: { "background-color": T.water }
|
|
5965
|
+
}),
|
|
5966
|
+
L({
|
|
5967
|
+
id: "chrtbase-earth",
|
|
5968
|
+
type: "fill",
|
|
5969
|
+
source: src,
|
|
5970
|
+
"source-layer": "base_earth",
|
|
5971
|
+
paint: { "fill-color": T.earth }
|
|
5972
|
+
}),
|
|
5973
|
+
L({
|
|
5974
|
+
id: "chrtbase-water",
|
|
5975
|
+
type: "fill",
|
|
5976
|
+
source: src,
|
|
5977
|
+
"source-layer": "base_water",
|
|
5978
|
+
paint: { "fill-color": T.water }
|
|
5979
|
+
}),
|
|
5980
|
+
L({
|
|
5981
|
+
id: "chrtbase-river",
|
|
5982
|
+
type: "line",
|
|
5983
|
+
source: src,
|
|
5984
|
+
"source-layer": "base_river",
|
|
5985
|
+
minzoom: 9,
|
|
5986
|
+
paint: {
|
|
5987
|
+
"line-color": T.river,
|
|
5988
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.6, 14, 1.6]
|
|
5989
|
+
}
|
|
5990
|
+
}),
|
|
5991
|
+
L({
|
|
5992
|
+
id: "chrtbase-boundary",
|
|
5993
|
+
type: "line",
|
|
5994
|
+
source: src,
|
|
5995
|
+
"source-layer": "base_boundary",
|
|
5996
|
+
paint: { "line-color": T.boundary, "line-width": 0.6, "line-opacity": 0.8 }
|
|
5997
|
+
}),
|
|
5998
|
+
// 2. THE DEFAULT: no coverage, everywhere, as a geometry-free background.
|
|
5999
|
+
// Constant opacity by design — see the header. What kills it is being
|
|
6000
|
+
// painted OVER, never fading.
|
|
6001
|
+
L({
|
|
6002
|
+
id: "chrtbase-nocoverage",
|
|
6003
|
+
type: "background",
|
|
6004
|
+
paint: { "background-pattern": NOCOVERAGE_PATTERN, "background-opacity": 0.55 }
|
|
6005
|
+
}),
|
|
6006
|
+
// 3. The jurisdiction: countries whose charts CHRT carries go tan — the
|
|
6007
|
+
// same tan as CHRTland, so "land in our world" is ONE colour. Opaque,
|
|
6008
|
+
// so it takes the crosshatch off its ground entirely.
|
|
6009
|
+
L({
|
|
6010
|
+
id: "chrtbase-jurisdiction",
|
|
6011
|
+
type: "fill",
|
|
6012
|
+
source: src,
|
|
6013
|
+
"source-layer": "jurisdiction",
|
|
6014
|
+
paint: { "fill-color": T.jurisdiction }
|
|
6015
|
+
}),
|
|
6016
|
+
// 4. States — internal boundaries and names, inside the jurisdiction
|
|
6017
|
+
// only (owner decision 2026-08-28). A hairline a shade deeper than the
|
|
6018
|
+
// tan, and quiet letter-spaced caps that fade as harbour detail takes
|
|
6019
|
+
// over. The one deliberate exception to no-names-on-the-basemap:
|
|
6020
|
+
// ADMIN furniture, nothing else — see the fence test.
|
|
6021
|
+
L({
|
|
6022
|
+
id: "chrtbase-state-boundary",
|
|
6023
|
+
type: "line",
|
|
6024
|
+
source: src,
|
|
6025
|
+
"source-layer": "state_boundary",
|
|
6026
|
+
minzoom: 4,
|
|
6027
|
+
paint: {
|
|
6028
|
+
"line-color": T.stateLine,
|
|
6029
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1],
|
|
6030
|
+
"line-dasharray": [3, 2],
|
|
6031
|
+
"line-opacity": 0.8
|
|
6032
|
+
}
|
|
6033
|
+
}),
|
|
6034
|
+
// 5. Country borders, drawn to be HOISTED above the whole chart by the
|
|
6035
|
+
// generator (owner, 2026-08-31: "where there is charted land, can we
|
|
6036
|
+
// add country borders on top of everything? I want it clear where
|
|
6037
|
+
// there is a boarder."). Sourced from NE boundary_lines_land — land
|
|
6038
|
+
// borders only, never coastline — so riding on top cannot stroke a
|
|
6039
|
+
// coast. Dashed so it reads as an administrative line, not chart
|
|
6040
|
+
// linework.
|
|
6041
|
+
L({
|
|
6042
|
+
id: "chrtbase-country-boundary",
|
|
6043
|
+
type: "line",
|
|
6044
|
+
source: src,
|
|
6045
|
+
"source-layer": "country_boundary",
|
|
6046
|
+
paint: {
|
|
6047
|
+
"line-color": T.boundary,
|
|
6048
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.8, 8, 1.4, 12, 2.2],
|
|
6049
|
+
"line-dasharray": [4, 2],
|
|
6050
|
+
"line-opacity": 0.9
|
|
6051
|
+
}
|
|
6052
|
+
}),
|
|
6053
|
+
L({
|
|
6054
|
+
id: "chrtbase-state-label",
|
|
6055
|
+
type: "symbol",
|
|
6056
|
+
source: src,
|
|
6057
|
+
"source-layer": "state_label",
|
|
6058
|
+
minzoom: 4,
|
|
6059
|
+
maxzoom: 10,
|
|
6060
|
+
layout: {
|
|
6061
|
+
"text-field": ["upcase", ["get", "name"]],
|
|
6062
|
+
"text-font": ["Open Sans Regular"],
|
|
6063
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 4, 9, 8, 12],
|
|
6064
|
+
"text-letter-spacing": 0.25,
|
|
6065
|
+
"text-allow-overlap": false,
|
|
6066
|
+
"text-padding": 8
|
|
6067
|
+
},
|
|
6068
|
+
paint: {
|
|
6069
|
+
"text-color": T.stateLabel,
|
|
6070
|
+
"text-halo-color": T.stateHalo,
|
|
6071
|
+
"text-halo-width": 1.2,
|
|
6072
|
+
"text-opacity": ["interpolate", ["linear"], ["zoom"], 4, 0.7, 8, 0.85, 10, 0.4]
|
|
6073
|
+
}
|
|
6074
|
+
})
|
|
6075
|
+
];
|
|
6076
|
+
}
|
|
6077
|
+
function baseInlandWaterLayers(opts = {}) {
|
|
6078
|
+
const palette = opts.palette ?? "DAY";
|
|
6079
|
+
const T = BASE_THEMES[palette] ?? BASE_THEMES.DAY;
|
|
6080
|
+
const src = opts.sourceId ?? BASE_REGION;
|
|
6081
|
+
const L = (o) => o;
|
|
6082
|
+
const NE_TIER = ["!", ["has", "kind"]];
|
|
6083
|
+
const PLANET_TIER = ["has", "kind"];
|
|
6084
|
+
const HANDOVER = 9;
|
|
6085
|
+
return [
|
|
6086
|
+
// ── The fill pair, SPLIT BY TIER at the handover ────────────────────────
|
|
6087
|
+
//
|
|
6088
|
+
// Both tiers share `inland_water` (tile-join concatenates the same layer
|
|
6089
|
+
// name), and an unsplit fill draws BOTH — which means the NE polygon is
|
|
6090
|
+
// painted over the planet one at every zoom. That is not a cosmetic
|
|
6091
|
+
// double-draw: NE 10m carries no interior rings, so its blob has no
|
|
6092
|
+
// ISLANDS in it, and painting it over the planet's holed polygons buries
|
|
6093
|
+
// every island in the lake under "uncharted water". Owner-reported at
|
|
6094
|
+
// Winnipesaukee z11.7 ("there is land (and islands) being covered by the
|
|
6095
|
+
// 'no chart here' boundary"), and measured: rendering the planet tier
|
|
6096
|
+
// alone brings back the whole archipelago — Rattlesnake, Bear, Long
|
|
6097
|
+
// Island, Diamond, the Barndoors — while the NE tier alone is a faceted
|
|
6098
|
+
// blob whose edges bear no relation to the shoreline drawn beneath it.
|
|
6099
|
+
//
|
|
6100
|
+
// So each tier owns its own zoom range, the same handover the shoreline
|
|
6101
|
+
// below uses. Known residue, measured over the whole archive: of the 141
|
|
6102
|
+
// NE lakes we still carry, 138 have planet water beneath them; the
|
|
6103
|
+
// exceptions are two unnamed western-Alaska tundra lakes and a 2e-5 deg²
|
|
6104
|
+
// Lake Michigan sliver, which lose their rough fill above z9 because the
|
|
6105
|
+
// planet has no `water` polygon there at all (checked against the raw
|
|
6106
|
+
// pre-clip NDJSON — not a clip artefact).
|
|
6107
|
+
L({
|
|
6108
|
+
id: "chrtbase-inland-water",
|
|
6109
|
+
type: "fill",
|
|
6110
|
+
source: src,
|
|
6111
|
+
"source-layer": "inland_water",
|
|
6112
|
+
filter: NE_TIER,
|
|
6113
|
+
maxzoom: HANDOVER,
|
|
6114
|
+
paint: { "fill-color": T.inlandWater }
|
|
6115
|
+
}),
|
|
6116
|
+
L({
|
|
6117
|
+
id: "chrtbase-inland-hatch",
|
|
6118
|
+
type: "fill",
|
|
6119
|
+
source: src,
|
|
6120
|
+
"source-layer": "inland_water",
|
|
6121
|
+
filter: NE_TIER,
|
|
6122
|
+
maxzoom: HANDOVER,
|
|
6123
|
+
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
6124
|
+
}),
|
|
6125
|
+
L({
|
|
6126
|
+
id: "chrtbase-inland-water-detail",
|
|
6127
|
+
type: "fill",
|
|
6128
|
+
source: src,
|
|
6129
|
+
"source-layer": "inland_water",
|
|
6130
|
+
filter: PLANET_TIER,
|
|
6131
|
+
minzoom: HANDOVER,
|
|
6132
|
+
paint: { "fill-color": T.inlandWater }
|
|
6133
|
+
}),
|
|
6134
|
+
L({
|
|
6135
|
+
id: "chrtbase-inland-hatch-detail",
|
|
6136
|
+
type: "fill",
|
|
6137
|
+
source: src,
|
|
6138
|
+
"source-layer": "inland_water",
|
|
6139
|
+
filter: PLANET_TIER,
|
|
6140
|
+
minzoom: HANDOVER,
|
|
6141
|
+
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
6142
|
+
}),
|
|
6143
|
+
// The rough shoreline (owner ask 2026-08-28). Drawn ABOVE the hatch so
|
|
6144
|
+
// the edge stays crisp through the texture, and BELOW the charted-water
|
|
6145
|
+
// repaint clones the generator splices in after it — so wherever the
|
|
6146
|
+
// chart repaints its own water, the rough NE10m line yields to the
|
|
6147
|
+
// chart's COALNE instead of double-striking it.
|
|
6148
|
+
//
|
|
6149
|
+
// NE-tier geometry ONLY (`kind` absent): the planet detail tier joins
|
|
6150
|
+
// the same source-layer from z9 as MANY polygons whose chunk-clip seams
|
|
6151
|
+
// rejoin invisibly as FILLS — but a line layer strokes every seam, and
|
|
6152
|
+
// Winnipesaukee rendered as a mesh of internal edges. Planet features
|
|
6153
|
+
// carry `kind` (the INLAND_KINDS filter's own field); NE's carry only
|
|
6154
|
+
// `name`, so the absence of `kind` IS the tier discriminator.
|
|
6155
|
+
L({
|
|
6156
|
+
id: "chrtbase-inland-shore",
|
|
6157
|
+
type: "line",
|
|
6158
|
+
source: src,
|
|
6159
|
+
"source-layer": "inland_water",
|
|
6160
|
+
filter: NE_TIER,
|
|
6161
|
+
// Hands the shoreline over to the planet tier at ITS floor. Both
|
|
6162
|
+
// tiers live in `inland_water` from z9 up (tile-join concatenates the
|
|
6163
|
+
// same layer name), and their FILLS merge invisibly — but two
|
|
6164
|
+
// outlines do not: NE 10m generalises Winnipesaukee's shape, so its
|
|
6165
|
+
// outline ran straight across the middle of the lake the planet had
|
|
6166
|
+
// drawn correctly around. The coarse line is right only while it is
|
|
6167
|
+
// the only shape we have.
|
|
6168
|
+
maxzoom: HANDOVER,
|
|
6169
|
+
paint: {
|
|
6170
|
+
"line-color": T.inlandShore,
|
|
6171
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1.1, 14, 1.6],
|
|
6172
|
+
"line-opacity": 0.9
|
|
6173
|
+
}
|
|
6174
|
+
}),
|
|
6175
|
+
// The PLANET tier's shoreline (owner ask 2026-08-31: "uncharted small
|
|
6176
|
+
// lakes render with no outline"). It reads its own `inland_shore` layer,
|
|
6177
|
+
// NOT `inland_water`, and that indirection is the whole fix: the mesh
|
|
6178
|
+
// that forced the `!has kind` fence above is a CLIP artefact — an MVT
|
|
6179
|
+
// feature arrives already cut to its own z10 tile, so stroking the
|
|
6180
|
+
// polygon strokes every tile boundary it crosses. The build now walks
|
|
6181
|
+
// those rings, drops the axis-aligned runs that sit on the read zoom's
|
|
6182
|
+
// tile grid, and emits what is left as lines (measured on one New
|
|
6183
|
+
// England chunk: 61 long axis-aligned mesh edges → 5, and the 5 survivors
|
|
6184
|
+
// are 0.02–0.5 tiles off the grid, i.e. real straight water edges).
|
|
6185
|
+
//
|
|
6186
|
+
// Thinner and quieter than the NE line: this is finer geometry describing
|
|
6187
|
+
// water nobody has surveyed, and approximate data should look
|
|
6188
|
+
// approximate.
|
|
6189
|
+
L({
|
|
6190
|
+
id: "chrtbase-inland-shore-detail",
|
|
6191
|
+
type: "line",
|
|
6192
|
+
source: src,
|
|
6193
|
+
"source-layer": "inland_shore",
|
|
6194
|
+
minzoom: HANDOVER,
|
|
6195
|
+
paint: {
|
|
6196
|
+
"line-color": T.inlandShore,
|
|
6197
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.5, 12, 0.9, 15, 1.2],
|
|
6198
|
+
"line-opacity": 0.85
|
|
6199
|
+
}
|
|
6200
|
+
}),
|
|
6201
|
+
// The lake's name (owner ask 2026-08-28) — the second scoped exception
|
|
6202
|
+
// to no-names-on-the-basemap, after the states: WATER-BODY names only.
|
|
6203
|
+
// No maxzoom: an uncharted lake keeps its name at every zoom; over
|
|
6204
|
+
// charted lakes the chart's own SEAARE label takes over at detail zooms
|
|
6205
|
+
// and collision culls the echo.
|
|
6206
|
+
//
|
|
6207
|
+
// The archive's `inland_water_label` anchors (v3+). v3 shipped ONE
|
|
6208
|
+
// PointOnSurface anchor per lake, and a big lake's single anchor pans
|
|
6209
|
+
// off-viewport — Winnipesaukee at z11.7 read nameless with its name two
|
|
6210
|
+
// tiles west of the screen (owner-reported). Labelling the polygons
|
|
6211
|
+
// instead is NOT the fix: tippecanoe's coalescing drops the NE polygon
|
|
6212
|
+
// from some z11 tiles (measured: 11/619/747 carries only planet pieces),
|
|
6213
|
+
// so a polygon-sourced label vanishes tile by tile. v4 therefore bakes a
|
|
6214
|
+
// deterministic anchor GRID into big lakes — the paper-chart convention
|
|
6215
|
+
// of repeating a name along a large water body — and this one layer
|
|
6216
|
+
// renders however many anchors are in view; collision culls the rest.
|
|
6217
|
+
// Planet-tier water-body NAMES (owner ask 2026-08-31: NE 10m names only
|
|
6218
|
+
// 113 US lakes). Emitted BELOW the NE layer on purpose — MapLibre places
|
|
6219
|
+
// symbols topmost-layer-first, so the big NE lake wins collision against
|
|
6220
|
+
// the pond beside it, and both lose to the chart's own labels (every ENC
|
|
6221
|
+
// class is emitted after this pair).
|
|
6222
|
+
//
|
|
6223
|
+
// The size threshold is OSM's own `min_zoom`, carried through the build:
|
|
6224
|
+
// Lake Winnipesaukee reads 10, Squam Lake 12, so a pond simply is not in
|
|
6225
|
+
// the filter until the user is close enough for it to be the answer to
|
|
6226
|
+
// "what is that water". `["zoom"]` in a filter evaluates at integer zoom
|
|
6227
|
+
// levels and removes the feature from placement entirely — verified
|
|
6228
|
+
// against maplibre-gl-native rather than assumed, because a text-size-0
|
|
6229
|
+
// gate would still claim a collision slot.
|
|
6230
|
+
//
|
|
6231
|
+
// The tiers are separate LAYERS, not a filtered split of one — see the
|
|
6232
|
+
// builder for why: an archive that a not-yet-deployed style would
|
|
6233
|
+
// mis-render cannot be promoted first, and this one has to be.
|
|
6234
|
+
L({
|
|
6235
|
+
id: "chrtbase-inland-label-detail",
|
|
6236
|
+
type: "symbol",
|
|
6237
|
+
source: src,
|
|
6238
|
+
"source-layer": "inland_water_label_detail",
|
|
6239
|
+
minzoom: HANDOVER,
|
|
6240
|
+
filter: [">=", ["zoom"], ["get", "min_zoom"]],
|
|
6241
|
+
layout: {
|
|
6242
|
+
"text-field": ["get", "name"],
|
|
6243
|
+
"text-font": ["Open Sans Regular"],
|
|
6244
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 9, 9, 12, 10.5, 15, 11],
|
|
6245
|
+
"text-max-width": 8,
|
|
6246
|
+
"text-allow-overlap": false,
|
|
6247
|
+
"text-padding": 6,
|
|
6248
|
+
// Bigger water first within the tier — min_zoom IS the size rank.
|
|
6249
|
+
"symbol-sort-key": ["get", "min_zoom"]
|
|
6250
|
+
},
|
|
6251
|
+
paint: {
|
|
6252
|
+
"text-color": T.inlandLabel,
|
|
6253
|
+
"text-halo-color": T.inlandHalo,
|
|
6254
|
+
"text-halo-width": 1,
|
|
6255
|
+
"text-opacity": 0.9
|
|
6256
|
+
}
|
|
6257
|
+
}),
|
|
6258
|
+
L({
|
|
6259
|
+
id: "chrtbase-inland-label",
|
|
6260
|
+
type: "symbol",
|
|
6261
|
+
source: src,
|
|
6262
|
+
"source-layer": "inland_water_label",
|
|
6263
|
+
minzoom: 5,
|
|
6264
|
+
layout: {
|
|
6265
|
+
"text-field": ["get", "name"],
|
|
6266
|
+
"text-font": ["Open Sans Regular"],
|
|
6267
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 5, 9.5, 10, 12],
|
|
6268
|
+
"text-max-width": 7,
|
|
6269
|
+
"text-allow-overlap": false,
|
|
6270
|
+
// Zoom-scaled spacing for the anchor GRID: at z8.9 the 0.08-deg
|
|
6271
|
+
// anchors sit ~54 px apart and a flat padding let six copies of
|
|
6272
|
+
// "Lake Ontario" crowd one screen (owner-reported 2026-08-30).
|
|
6273
|
+
// Wide collision boxes at low zoom thin the grid to the repeat-
|
|
6274
|
+
// by-convention cadence; by z12 the anchors are ~850 px apart and
|
|
6275
|
+
// the padding relaxes to normal.
|
|
6276
|
+
"text-padding": ["interpolate", ["linear"], ["zoom"], 5, 80, 9, 64, 12, 16]
|
|
6277
|
+
},
|
|
6278
|
+
paint: {
|
|
6279
|
+
"text-color": T.inlandLabel,
|
|
6280
|
+
"text-halo-color": T.inlandHalo,
|
|
6281
|
+
"text-halo-width": 1.1
|
|
6282
|
+
}
|
|
6283
|
+
})
|
|
6284
|
+
];
|
|
6285
|
+
}
|
|
6286
|
+
|
|
6287
|
+
// src/atlas/eink-portrayal.ts
|
|
6288
|
+
var EINK_DOTS_PATTERN = `${CHRT_ICON_PREFIX}eink-dots`;
|
|
6289
|
+
var EINK_HATCH_PATTERN = `${CHRT_ICON_PREFIX}eink-hatch`;
|
|
6290
|
+
var EINK_DANGER_PATTERN = `${CHRT_ICON_PREFIX}eink-danger`;
|
|
6291
|
+
var EINK_LINE_SCALE = 1.5;
|
|
6292
|
+
var EINK_LINE_MIN_PX = 1;
|
|
6293
|
+
var EINK_TEXT_SCALE = 1.15;
|
|
6294
|
+
var EINK_HALO_MIN_PX = 1.5;
|
|
6295
|
+
var EINK_DANGER_ICON_SCALE = 1.35;
|
|
6296
|
+
var EINK_WASH_CUTOFF = 0.3;
|
|
6297
|
+
var EINK_SAFETY_CONTOUR_WIDTH = [2, 3.5];
|
|
6298
|
+
var EINK_DANGER_CLASSES = /* @__PURE__ */ new Set(["OBSTRN", "WRECKS", "UWTROC"]);
|
|
6299
|
+
var METRIC_CONTOUR_LADDER_M = [0, 2, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1e3];
|
|
6300
|
+
var IMPERIAL_CONTOUR_LADDER_M = [
|
|
6301
|
+
1.8,
|
|
6302
|
+
3.6,
|
|
6303
|
+
5.4,
|
|
6304
|
+
9.1,
|
|
6305
|
+
18.2,
|
|
6306
|
+
27.4,
|
|
6307
|
+
36.5,
|
|
6308
|
+
54.8,
|
|
6309
|
+
91.4,
|
|
6310
|
+
182.8,
|
|
6311
|
+
365.7,
|
|
6312
|
+
914.4
|
|
6313
|
+
];
|
|
6314
|
+
function snapToLadder(safetyM, ladder) {
|
|
6315
|
+
for (const rung of ladder) if (rung >= safetyM - 1e-6) return rung;
|
|
6316
|
+
return null;
|
|
6317
|
+
}
|
|
6318
|
+
function safetyContourValues(safetyContourM) {
|
|
6319
|
+
const s = effectiveSafetyContour(safetyContourM);
|
|
6320
|
+
const out = [snapToLadder(s, METRIC_CONTOUR_LADDER_M), snapToLadder(s, IMPERIAL_CONTOUR_LADDER_M)];
|
|
6321
|
+
return [...new Set(out.filter((v) => v !== null))];
|
|
6322
|
+
}
|
|
6323
|
+
function isExpr(v) {
|
|
6324
|
+
return Array.isArray(v) && typeof v[0] === "string";
|
|
6325
|
+
}
|
|
6326
|
+
function scaleNumeric(v, k, min = 0) {
|
|
6327
|
+
if (typeof v === "number") return Math.max(v * k, v > 0 ? min : 0);
|
|
6328
|
+
if (!isExpr(v)) return v;
|
|
6329
|
+
const op = v[0];
|
|
6330
|
+
if (op === "interpolate" || op === "interpolate-hcl" || op === "interpolate-lab") {
|
|
6331
|
+
const out = v.slice(0, 3);
|
|
6332
|
+
for (let i = 3; i < v.length; i += 2) out.push(v[i], scaleNumeric(v[i + 1], k, min));
|
|
6333
|
+
return out;
|
|
6334
|
+
}
|
|
6335
|
+
if (op === "step") {
|
|
6336
|
+
const out = [op, v[1], scaleNumeric(v[2], k, min)];
|
|
6337
|
+
for (let i = 3; i < v.length; i += 2) out.push(v[i], scaleNumeric(v[i + 1], k, min));
|
|
6338
|
+
return out;
|
|
6339
|
+
}
|
|
6340
|
+
if (op === "literal") return v;
|
|
6341
|
+
return ["max", ["*", v, k], min];
|
|
6342
|
+
}
|
|
6343
|
+
function objectClass(layer) {
|
|
6344
|
+
const md = layer.metadata;
|
|
6345
|
+
const c = md?.["s52:objectClass"];
|
|
6346
|
+
return typeof c === "string" ? c : null;
|
|
6347
|
+
}
|
|
6348
|
+
function scaffoldId(prefix, baseId) {
|
|
6349
|
+
return `${prefix}${baseId.replace(/-band(\d)/g, "-b$1")}`;
|
|
6350
|
+
}
|
|
6351
|
+
var DRVAL1 = ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1];
|
|
6352
|
+
var DRVAL2 = ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0];
|
|
6353
|
+
var VALDCO = ["to-number", ["coalesce", ["get", "VALDCO"], ["get", "valdco"], -1], -1];
|
|
6354
|
+
function withFilter(base, extra) {
|
|
6355
|
+
return base === void 0 ? extra : ["all", base, extra];
|
|
6356
|
+
}
|
|
6357
|
+
function applyEinkPortrayal(style, palette, opts = {}) {
|
|
6358
|
+
if (palette !== "EINK_COLOR" && palette !== "EINK_MONO" && palette !== "EINK_NIGHT") return style;
|
|
6359
|
+
const mono = isEinkGreyTable(palette);
|
|
6360
|
+
const night = palette === "EINK_NIGHT";
|
|
6361
|
+
const safetyInk = resolveColor("DEPSC", palette);
|
|
6362
|
+
const groundInk = resolveColor("UIBCK", palette);
|
|
6363
|
+
const safety = effectiveSafetyContour(opts.safetyContour ?? DEFAULT_SAFETY_CONTOUR_M);
|
|
6364
|
+
const safetyValues = safetyContourValues(safety);
|
|
6365
|
+
const out = [];
|
|
6366
|
+
for (const original of style.layers) {
|
|
6367
|
+
const id = original.id;
|
|
6368
|
+
const type = original.type;
|
|
6369
|
+
if (type === "hillshade" || id.startsWith("chrtland-fade-")) continue;
|
|
6370
|
+
const layer = structuredClone(original);
|
|
6371
|
+
const paint = layer.paint ??= {};
|
|
6372
|
+
const layout = layer.layout;
|
|
6373
|
+
const cls = objectClass(layer);
|
|
6374
|
+
if (type === "line") {
|
|
6375
|
+
if (paint["line-width"] !== void 0) {
|
|
6376
|
+
paint["line-width"] = scaleNumeric(paint["line-width"], EINK_LINE_SCALE, EINK_LINE_MIN_PX);
|
|
6377
|
+
} else {
|
|
6378
|
+
paint["line-width"] = EINK_LINE_SCALE;
|
|
6379
|
+
}
|
|
6380
|
+
const o = paint["line-opacity"];
|
|
6381
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["line-opacity"] = 1;
|
|
6382
|
+
if (paint["line-blur"] !== void 0) paint["line-blur"] = 0;
|
|
6383
|
+
}
|
|
6384
|
+
if (type === "fill") {
|
|
6385
|
+
const o = paint["fill-opacity"];
|
|
6386
|
+
if (typeof o === "number" && o > 0 && o < 1) {
|
|
6387
|
+
if (cls && EINK_DANGER_CLASSES.has(cls)) {
|
|
6388
|
+
paint["fill-pattern"] = EINK_DANGER_PATTERN;
|
|
6389
|
+
paint["fill-opacity"] = 1;
|
|
6390
|
+
} else {
|
|
6391
|
+
paint["fill-opacity"] = o < EINK_WASH_CUTOFF ? 0 : 1;
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
if (paint["fill-antialias"] === void 0) paint["fill-antialias"] = true;
|
|
6395
|
+
}
|
|
6396
|
+
if (type === "background" && night && id === "chrt-background") {
|
|
6397
|
+
paint["background-pattern"] = NOCOVERAGE_PATTERN;
|
|
6398
|
+
}
|
|
6399
|
+
if (type === "background") {
|
|
6400
|
+
const o = paint["background-opacity"];
|
|
6401
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["background-opacity"] = 1;
|
|
6402
|
+
}
|
|
6403
|
+
if (type === "circle") {
|
|
6404
|
+
const blur = paint["circle-blur"];
|
|
6405
|
+
if (typeof blur === "number" && blur >= 0.5) {
|
|
6406
|
+
paint["circle-opacity"] = 0;
|
|
6407
|
+
} else {
|
|
6408
|
+
if (blur !== void 0) paint["circle-blur"] = 0;
|
|
6409
|
+
const o = paint["circle-opacity"];
|
|
6410
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["circle-opacity"] = 1;
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
if (type === "symbol") {
|
|
6414
|
+
if (layout?.["text-field"] !== void 0) {
|
|
6415
|
+
if (layout["text-size"] !== void 0) {
|
|
6416
|
+
layout["text-size"] = scaleNumeric(layout["text-size"], EINK_TEXT_SCALE);
|
|
6417
|
+
}
|
|
6418
|
+
if (paint["text-opacity"] !== void 0) paint["text-opacity"] = 1;
|
|
6419
|
+
if (typeof paint["text-halo-color"] === "string") paint["text-halo-color"] = groundInk;
|
|
6420
|
+
const hw = paint["text-halo-width"];
|
|
6421
|
+
paint["text-halo-width"] = typeof hw === "number" ? Math.max(hw, EINK_HALO_MIN_PX) : hw ?? EINK_HALO_MIN_PX;
|
|
6422
|
+
if (paint["text-halo-blur"] !== void 0) paint["text-halo-blur"] = 0;
|
|
6423
|
+
}
|
|
6424
|
+
const io = paint["icon-opacity"];
|
|
6425
|
+
if (typeof io === "number" && io > 0 && io < 1) paint["icon-opacity"] = 1;
|
|
6426
|
+
if (cls && EINK_DANGER_CLASSES.has(cls) && layout?.["icon-image"] !== void 0) {
|
|
6427
|
+
layout["icon-size"] = scaleNumeric(layout["icon-size"] ?? 1, EINK_DANGER_ICON_SCALE);
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
out.push(layer);
|
|
6431
|
+
if (type === "fill" && cls === "DEPARE" && mono) {
|
|
6432
|
+
const drying = ["all", ["<", DRVAL1, 0], ["<=", DRVAL2, 0]];
|
|
6433
|
+
out.push({
|
|
6434
|
+
...structuredClone(original),
|
|
6435
|
+
id: scaffoldId("chrt-eink-depth-of-", id),
|
|
6436
|
+
filter: withFilter(original.filter, ["<", DRVAL1, safety]),
|
|
6437
|
+
paint: {
|
|
6438
|
+
"fill-pattern": ["case", drying, EINK_HATCH_PATTERN, EINK_DOTS_PATTERN]
|
|
6439
|
+
}
|
|
6440
|
+
});
|
|
6441
|
+
}
|
|
6442
|
+
if (type === "line" && cls === "DEPCNT") {
|
|
6443
|
+
const isSafety = ["any", ...safetyValues.map((v) => ["<", ["abs", ["-", VALDCO, v]], 0.05])];
|
|
6444
|
+
out.push({
|
|
6445
|
+
...structuredClone(original),
|
|
6446
|
+
id: scaffoldId("chrt-eink-safety-of-", id),
|
|
6447
|
+
filter: withFilter(original.filter, isSafety),
|
|
6448
|
+
paint: {
|
|
6449
|
+
"line-color": safetyInk,
|
|
6450
|
+
"line-width": [
|
|
6451
|
+
"interpolate",
|
|
6452
|
+
["linear"],
|
|
6453
|
+
["zoom"],
|
|
6454
|
+
8,
|
|
6455
|
+
EINK_SAFETY_CONTOUR_WIDTH[0],
|
|
6456
|
+
14,
|
|
6457
|
+
EINK_SAFETY_CONTOUR_WIDTH[1]
|
|
6458
|
+
]
|
|
6459
|
+
},
|
|
6460
|
+
layout: { "line-join": "round", "line-cap": "round" }
|
|
6461
|
+
});
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
return {
|
|
6465
|
+
...style,
|
|
6466
|
+
// No transitions: a property change is one repaint, never an animation.
|
|
6467
|
+
...{ transition: { duration: 0, delay: 0 } },
|
|
6468
|
+
metadata: { ...style.metadata, "chrt:eink": night ? "night" : mono ? "mono" : "color" },
|
|
6469
|
+
layers: out
|
|
6470
|
+
};
|
|
6471
|
+
}
|
|
6472
|
+
|
|
6473
|
+
// src/atlas/map-live.ts
|
|
6474
|
+
function isMapLive(map) {
|
|
6475
|
+
return map._removed !== true;
|
|
6476
|
+
}
|
|
6477
|
+
|
|
6478
|
+
// src/atlas/s52-linestyles-generated.ts
|
|
5031
6479
|
var S52_LINESTYLES = {
|
|
5032
6480
|
ACHARE51: {
|
|
5033
6481
|
name: "ACHARE51",
|
|
@@ -5653,252 +7101,37 @@ function houseWidth(meta) {
|
|
|
5653
7101
|
}
|
|
5654
7102
|
var S52_LINE_SPRITE_PREFIX = "LC:";
|
|
5655
7103
|
function lineSpriteId(lineStyleName) {
|
|
5656
|
-
return `${S52_LINE_SPRITE_PREFIX}${lineStyleName}`;
|
|
5657
|
-
}
|
|
5658
|
-
function lineStylePaint(name) {
|
|
5659
|
-
const meta = S52_LINESTYLES[name];
|
|
5660
|
-
if (!meta) {
|
|
5661
|
-
throw new Error(
|
|
5662
|
-
`Unknown S-52 linestyle "${name}". Known: ${Object.keys(S52_LINESTYLES).length} styles (regenerate with: bun run scripts/extract-opencpn-linestyles.ts)`
|
|
5663
|
-
);
|
|
5664
|
-
}
|
|
5665
|
-
return {
|
|
5666
|
-
"line-pattern": lineSpriteId(meta.name),
|
|
5667
|
-
"line-width": houseWidth(meta)
|
|
5668
|
-
};
|
|
5669
|
-
}
|
|
5670
|
-
function lineStylePaintCase(cases, fallback) {
|
|
5671
|
-
const resolve = (name) => {
|
|
5672
|
-
const meta = S52_LINESTYLES[name];
|
|
5673
|
-
if (!meta) throw new Error(`Unknown S-52 linestyle "${name}"`);
|
|
5674
|
-
return meta;
|
|
5675
|
-
};
|
|
5676
|
-
const fallbackMeta = resolve(fallback);
|
|
5677
|
-
const pattern = ["case"];
|
|
5678
|
-
const width = ["case"];
|
|
5679
|
-
for (const [condition, name] of cases) {
|
|
5680
|
-
const meta = resolve(name);
|
|
5681
|
-
pattern.push(condition, lineSpriteId(meta.name));
|
|
5682
|
-
width.push(condition, houseWidth(meta));
|
|
5683
|
-
}
|
|
5684
|
-
pattern.push(lineSpriteId(fallbackMeta.name));
|
|
5685
|
-
width.push(houseWidth(fallbackMeta));
|
|
5686
|
-
return { "line-pattern": pattern, "line-width": width };
|
|
5687
|
-
}
|
|
5688
|
-
|
|
5689
|
-
// src/atlas/s52-pattern.ts
|
|
5690
|
-
var S52_PATTERN_SPRITE_PREFIX = "AP:";
|
|
5691
|
-
function patternSpriteId(patternName) {
|
|
5692
|
-
return `${S52_PATTERN_SPRITE_PREFIX}${patternName}`;
|
|
5693
|
-
}
|
|
5694
|
-
|
|
5695
|
-
// src/atlas/s52-conditional.ts
|
|
5696
|
-
var DEFAULT_SAFETY_CONTOUR_M = 10;
|
|
5697
|
-
var DEPARE_DEEP_CUT_M = 18.2;
|
|
5698
|
-
var DEPARE_SHALLOW_CUT_M = 2;
|
|
5699
|
-
function effectiveSafetyContour(safetyContourM) {
|
|
5700
|
-
if (!Number.isFinite(safetyContourM)) return DEFAULT_SAFETY_CONTOUR_M;
|
|
5701
|
-
return Math.min(Math.max(safetyContourM, DEPARE_SHALLOW_CUT_M), DEPARE_DEEP_CUT_M);
|
|
5702
|
-
}
|
|
5703
|
-
function depareColor(props, ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5704
|
-
const drval1 = Number(props.DRVAL1 ?? props.drval1 ?? -1);
|
|
5705
|
-
const drval2 = Number(props.DRVAL2 ?? props.drval2 ?? drval1);
|
|
5706
|
-
const safety = effectiveSafetyContour(safetyContourM);
|
|
5707
|
-
if (drval1 < 0 && drval2 <= 0) return resolveColor("DEPIT", ct);
|
|
5708
|
-
if (drval1 < DEPARE_SHALLOW_CUT_M) return resolveColor("DEPVS", ct);
|
|
5709
|
-
if (drval1 < safety) return resolveColor("DEPMS", ct);
|
|
5710
|
-
if (drval1 < DEPARE_DEEP_CUT_M) return resolveColor("DEPMD", ct);
|
|
5711
|
-
return resolveColor("DEPDW", ct);
|
|
5712
|
-
}
|
|
5713
|
-
var DEPARE_FILL_BRANCH_TOKENS = ["DEPIT", "DEPVS", "DEPMS", "DEPMD"];
|
|
5714
|
-
var DEPARE_FILL_FALLBACK_TOKEN = "DEPDW";
|
|
5715
|
-
function depareFillExpression(ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5716
|
-
const safety = effectiveSafetyContour(safetyContourM);
|
|
5717
|
-
const [it, vs, ms, md] = DEPARE_FILL_BRANCH_TOKENS;
|
|
5718
|
-
return [
|
|
5719
|
-
"case",
|
|
5720
|
-
[
|
|
5721
|
-
"all",
|
|
5722
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], 0],
|
|
5723
|
-
["<=", ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0], 0]
|
|
5724
|
-
],
|
|
5725
|
-
resolveColor(it, ct),
|
|
5726
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_SHALLOW_CUT_M],
|
|
5727
|
-
resolveColor(vs, ct),
|
|
5728
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], safety],
|
|
5729
|
-
resolveColor(ms, ct),
|
|
5730
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_DEEP_CUT_M],
|
|
5731
|
-
resolveColor(md, ct),
|
|
5732
|
-
resolveColor(DEPARE_FILL_FALLBACK_TOKEN, ct)
|
|
5733
|
-
];
|
|
5734
|
-
}
|
|
5735
|
-
function listAttrString(attr) {
|
|
5736
|
-
return ["to-string", ["coalesce", ["get", attr], ["get", attr.toLowerCase()], ""]];
|
|
5737
|
-
}
|
|
5738
|
-
function listAttrCase(attr, cases, fallback) {
|
|
5739
|
-
const attrStr = listAttrString(attr);
|
|
5740
|
-
const expr = ["case"];
|
|
5741
|
-
for (const [code, value] of cases) {
|
|
5742
|
-
expr.push([">=", ["index-of", `"${code}"`, attrStr], 0], value);
|
|
5743
|
-
}
|
|
5744
|
-
for (const [code, value] of cases) {
|
|
5745
|
-
expr.push(["==", attrStr, code], value);
|
|
5746
|
-
}
|
|
5747
|
-
for (const [code, value] of cases) {
|
|
5748
|
-
expr.push(["==", attrStr, `[${code}]`], value);
|
|
5749
|
-
}
|
|
5750
|
-
expr.push(fallback);
|
|
5751
|
-
return expr;
|
|
5752
|
-
}
|
|
5753
|
-
function listAttrHas(attr, codes) {
|
|
5754
|
-
const attrStr = listAttrString(attr);
|
|
5755
|
-
const clauses = [];
|
|
5756
|
-
for (const code of codes) {
|
|
5757
|
-
clauses.push([">=", ["index-of", `"${code}"`, attrStr], 0]);
|
|
5758
|
-
clauses.push(["==", attrStr, code]);
|
|
5759
|
-
clauses.push(["==", attrStr, `[${code}]`]);
|
|
5760
|
-
}
|
|
5761
|
-
return ["any", ...clauses];
|
|
5762
|
-
}
|
|
5763
|
-
function colourCodeCase(cases, fallback) {
|
|
5764
|
-
return listAttrCase("COLOUR", cases, fallback);
|
|
5765
|
-
}
|
|
5766
|
-
function lightColor(props, ct = "DAY") {
|
|
5767
|
-
const raw = props.COLOUR ?? props.colour;
|
|
5768
|
-
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5769
|
-
switch (code) {
|
|
5770
|
-
case 1:
|
|
5771
|
-
return resolveColor("LITWT", ct);
|
|
5772
|
-
case 3:
|
|
5773
|
-
return resolveColor("LITRD", ct);
|
|
5774
|
-
case 4:
|
|
5775
|
-
return resolveColor("LITGN", ct);
|
|
5776
|
-
case 5:
|
|
5777
|
-
return resolveColor("LITBL", ct);
|
|
5778
|
-
case 6:
|
|
5779
|
-
return resolveColor("LITYW", ct);
|
|
5780
|
-
case 11:
|
|
5781
|
-
return resolveColor("LITOR", ct);
|
|
5782
|
-
default:
|
|
5783
|
-
return resolveColor("LITYW", ct);
|
|
5784
|
-
}
|
|
5785
|
-
}
|
|
5786
|
-
function lightFillExpression(ct = "DAY") {
|
|
5787
|
-
return colourCodeCase(
|
|
5788
|
-
[
|
|
5789
|
-
["3", resolveColor("LITRD", ct)],
|
|
5790
|
-
["4", resolveColor("LITGN", ct)],
|
|
5791
|
-
["6", resolveColor("LITYW", ct)],
|
|
5792
|
-
["11", resolveColor("LITOR", ct)],
|
|
5793
|
-
["5", resolveColor("LITBL", ct)],
|
|
5794
|
-
["1", resolveColor("LITWT", ct)]
|
|
5795
|
-
],
|
|
5796
|
-
resolveColor("LITYW", ct)
|
|
5797
|
-
);
|
|
5798
|
-
}
|
|
5799
|
-
function lateralColor(props, ct = "DAY") {
|
|
5800
|
-
const catlam = Number(props.CATLAM ?? props.catlam ?? 0);
|
|
5801
|
-
if (catlam === 1) return resolveColor("LITRD", ct);
|
|
5802
|
-
if (catlam === 2) return resolveColor("LITGN", ct);
|
|
5803
|
-
return resolveColor("LITYW", ct);
|
|
5804
|
-
}
|
|
5805
|
-
function lateralFillExpression(ct = "DAY") {
|
|
5806
|
-
return [
|
|
5807
|
-
"match",
|
|
5808
|
-
["coalesce", ["get", "CATLAM"], ["get", "catlam"], 0],
|
|
5809
|
-
1,
|
|
5810
|
-
resolveColor("LITRD", ct),
|
|
5811
|
-
2,
|
|
5812
|
-
resolveColor("LITGN", ct),
|
|
5813
|
-
resolveColor("LITYW", ct)
|
|
5814
|
-
];
|
|
5815
|
-
}
|
|
5816
|
-
function hazardColor(props, ct = "DAY") {
|
|
5817
|
-
const watlev = Number(props.WATLEV ?? props.watlev ?? 0);
|
|
5818
|
-
const valsou = props.VALSOU ?? props.valsou;
|
|
5819
|
-
if (watlev === 1 || watlev === 2) return resolveColor("DNGHL", ct);
|
|
5820
|
-
if (valsou != null && Number(valsou) < 2) return resolveColor("DNGHL", ct);
|
|
5821
|
-
return resolveColor("RESBL", ct);
|
|
5822
|
-
}
|
|
5823
|
-
function hazardFillExpression(ct = "DAY") {
|
|
5824
|
-
return [
|
|
5825
|
-
"case",
|
|
5826
|
-
[
|
|
5827
|
-
"any",
|
|
5828
|
-
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 1],
|
|
5829
|
-
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 2]
|
|
5830
|
-
],
|
|
5831
|
-
resolveColor("DNGHL", ct),
|
|
5832
|
-
[
|
|
5833
|
-
"all",
|
|
5834
|
-
["has", "VALSOU"],
|
|
5835
|
-
["<", ["get", "VALSOU"], 2]
|
|
5836
|
-
],
|
|
5837
|
-
resolveColor("DNGHL", ct),
|
|
5838
|
-
resolveColor("RESBL", ct)
|
|
5839
|
-
];
|
|
5840
|
-
}
|
|
5841
|
-
function vegatnPatternExpression() {
|
|
5842
|
-
return [
|
|
5843
|
-
"match",
|
|
5844
|
-
["coalesce", ["get", "CATVEG"], ["get", "catveg"], 0],
|
|
5845
|
-
1,
|
|
5846
|
-
patternSpriteId("VEGATN03"),
|
|
5847
|
-
// grass
|
|
5848
|
-
3,
|
|
5849
|
-
patternSpriteId("VEGATN03"),
|
|
5850
|
-
// bush
|
|
5851
|
-
4,
|
|
5852
|
-
patternSpriteId("VEGATN04"),
|
|
5853
|
-
// deciduous wood
|
|
5854
|
-
5,
|
|
5855
|
-
patternSpriteId("VEGATN04"),
|
|
5856
|
-
// coniferous wood
|
|
5857
|
-
6,
|
|
5858
|
-
patternSpriteId("VEGATN04"),
|
|
5859
|
-
// wood (mixed)
|
|
5860
|
-
7,
|
|
5861
|
-
patternSpriteId("MARSHES1"),
|
|
5862
|
-
// mangroves
|
|
5863
|
-
10,
|
|
5864
|
-
patternSpriteId("VEGATN03"),
|
|
5865
|
-
// scrub
|
|
5866
|
-
11,
|
|
5867
|
-
patternSpriteId("VEGATN04"),
|
|
5868
|
-
// tree
|
|
5869
|
-
12,
|
|
5870
|
-
patternSpriteId("VEGATN04"),
|
|
5871
|
-
// evergreen
|
|
5872
|
-
16,
|
|
5873
|
-
patternSpriteId("MARSHES1"),
|
|
5874
|
-
// swamp
|
|
5875
|
-
17,
|
|
5876
|
-
patternSpriteId("MARSHES1"),
|
|
5877
|
-
// marsh
|
|
5878
|
-
21,
|
|
5879
|
-
patternSpriteId("VEGATN04"),
|
|
5880
|
-
// savannah
|
|
5881
|
-
patternSpriteId("VEGATN03")
|
|
5882
|
-
// default: grass pattern
|
|
5883
|
-
];
|
|
5884
|
-
}
|
|
5885
|
-
function daymarkColor(props, ct = "DAY") {
|
|
5886
|
-
const raw = props.COLOUR ?? props.colour;
|
|
5887
|
-
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5888
|
-
switch (code) {
|
|
5889
|
-
case 1:
|
|
5890
|
-
return resolveColor("LITWT", ct);
|
|
5891
|
-
case 3:
|
|
5892
|
-
return resolveColor("LITRD", ct);
|
|
5893
|
-
case 4:
|
|
5894
|
-
return resolveColor("LITGN", ct);
|
|
5895
|
-
case 6:
|
|
5896
|
-
return resolveColor("LITYW", ct);
|
|
5897
|
-
case 11:
|
|
5898
|
-
return resolveColor("LITOR", ct);
|
|
5899
|
-
default:
|
|
5900
|
-
return resolveColor("LITGN", ct);
|
|
7104
|
+
return `${S52_LINE_SPRITE_PREFIX}${lineStyleName}`;
|
|
7105
|
+
}
|
|
7106
|
+
function lineStylePaint(name) {
|
|
7107
|
+
const meta = S52_LINESTYLES[name];
|
|
7108
|
+
if (!meta) {
|
|
7109
|
+
throw new Error(
|
|
7110
|
+
`Unknown S-52 linestyle "${name}". Known: ${Object.keys(S52_LINESTYLES).length} styles (regenerate with: bun run scripts/extract-opencpn-linestyles.ts)`
|
|
7111
|
+
);
|
|
7112
|
+
}
|
|
7113
|
+
return {
|
|
7114
|
+
"line-pattern": lineSpriteId(meta.name),
|
|
7115
|
+
"line-width": houseWidth(meta)
|
|
7116
|
+
};
|
|
7117
|
+
}
|
|
7118
|
+
function lineStylePaintCase(cases, fallback) {
|
|
7119
|
+
const resolve = (name) => {
|
|
7120
|
+
const meta = S52_LINESTYLES[name];
|
|
7121
|
+
if (!meta) throw new Error(`Unknown S-52 linestyle "${name}"`);
|
|
7122
|
+
return meta;
|
|
7123
|
+
};
|
|
7124
|
+
const fallbackMeta = resolve(fallback);
|
|
7125
|
+
const pattern = ["case"];
|
|
7126
|
+
const width = ["case"];
|
|
7127
|
+
for (const [condition, name] of cases) {
|
|
7128
|
+
const meta = resolve(name);
|
|
7129
|
+
pattern.push(condition, lineSpriteId(meta.name));
|
|
7130
|
+
width.push(condition, houseWidth(meta));
|
|
5901
7131
|
}
|
|
7132
|
+
pattern.push(lineSpriteId(fallbackMeta.name));
|
|
7133
|
+
width.push(houseWidth(fallbackMeta));
|
|
7134
|
+
return { "line-pattern": pattern, "line-width": width };
|
|
5902
7135
|
}
|
|
5903
7136
|
|
|
5904
7137
|
// src/atlas/s52-lookup.ts
|
|
@@ -19791,9 +21024,9 @@ function compileAtonExpression(lookup, mapSymbol = (name) => name) {
|
|
|
19791
21024
|
expr.push(fallback);
|
|
19792
21025
|
return expr;
|
|
19793
21026
|
}
|
|
19794
|
-
function atonIconExpression(
|
|
19795
|
-
const lookup = S52_ATON_LOOKUPS[
|
|
19796
|
-
if (!lookup) throw new Error(`s52-aton-expressions: no generated lookup for ${
|
|
21027
|
+
function atonIconExpression(objectClass2) {
|
|
21028
|
+
const lookup = S52_ATON_LOOKUPS[objectClass2];
|
|
21029
|
+
if (!lookup) throw new Error(`s52-aton-expressions: no generated lookup for ${objectClass2}`);
|
|
19797
21030
|
return compileAtonExpression(lookup);
|
|
19798
21031
|
}
|
|
19799
21032
|
function lightsIconExpression() {
|
|
@@ -22878,7 +24111,9 @@ var LABEL_TINT = {
|
|
|
22878
24111
|
};
|
|
22879
24112
|
function tintFor(tint, ct) {
|
|
22880
24113
|
const day = LABEL_TINT[tint].day;
|
|
22881
|
-
if (ct === "DAY") return day;
|
|
24114
|
+
if (ct === "DAY" || ct === "EINK_COLOR") return day;
|
|
24115
|
+
if (ct === "EINK_MONO") return einkMonoGrey(day);
|
|
24116
|
+
if (ct === "EINK_NIGHT") return einkNightGrey(day);
|
|
22882
24117
|
return capLuminance(day, relativeLuminance(resolveColor("CHBLK", ct)));
|
|
22883
24118
|
}
|
|
22884
24119
|
function nightText(tint) {
|
|
@@ -22889,10 +24124,14 @@ function nightText(tint) {
|
|
|
22889
24124
|
}
|
|
22890
24125
|
function onHalo(tint, ct) {
|
|
22891
24126
|
if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
|
|
24127
|
+
if (ct === "EINK_NIGHT") return { textColor: EINK_MONO_GREYS.MID, textHaloColor: EINK_MONO_GREYS.INK };
|
|
24128
|
+
if (isEinkColorTable(ct)) return { textColor: EINK_MONO_GREYS.INK, textHaloColor: EINK_MONO_GREYS.PAPER };
|
|
22892
24129
|
return { textColor: DARK_BG, textHaloColor: tintFor(tint, ct) };
|
|
22893
24130
|
}
|
|
22894
24131
|
function onKnockout(tint, ct) {
|
|
22895
24132
|
if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
|
|
24133
|
+
if (ct === "EINK_NIGHT") return { textColor: EINK_MONO_GREYS.MID, textHaloColor: EINK_MONO_GREYS.INK };
|
|
24134
|
+
if (isEinkColorTable(ct)) return { textColor: EINK_MONO_GREYS.INK, textHaloColor: EINK_MONO_GREYS.PAPER };
|
|
22896
24135
|
return { textColor: tintFor(tint, ct), textHaloColor: DARK_BG };
|
|
22897
24136
|
}
|
|
22898
24137
|
var NAV_AID_LABEL_TEXT_SIZE = [
|
|
@@ -27078,409 +28317,145 @@ function applyColorTheme(svgString, colorTable = "DAY") {
|
|
|
27078
28317
|
}
|
|
27079
28318
|
);
|
|
27080
28319
|
}
|
|
27081
|
-
function getAvailablePointSymbols() {
|
|
27082
|
-
return Object.keys(S52_POINT_SVGS);
|
|
27083
|
-
}
|
|
27084
|
-
function getAvailablePatterns() {
|
|
27085
|
-
return Object.keys(S52_PATTERN_SVGS);
|
|
27086
|
-
}
|
|
27087
|
-
function getAvailableLineSymbols() {
|
|
27088
|
-
return Object.keys(S52_LINE_SVGS);
|
|
27089
|
-
}
|
|
27090
|
-
function getS52SymbolStats() {
|
|
27091
|
-
return {
|
|
27092
|
-
pointCount: Object.keys(S52_POINT_SVGS).length,
|
|
27093
|
-
patternCount: Object.keys(S52_PATTERN_SVGS).length,
|
|
27094
|
-
lineCount: Object.keys(S52_LINE_SVGS).length,
|
|
27095
|
-
totalCount: Object.keys(ALL_S52_SVGS).length
|
|
27096
|
-
};
|
|
27097
|
-
}
|
|
27098
|
-
|
|
27099
|
-
// src/atlas/s101-style.ts
|
|
27100
|
-
var S101_ICON_PREFIX = "s101:";
|
|
27101
|
-
var S101_SYMBOL_NAMES = new Set(Object.keys(S101_POINT_SVGS));
|
|
27102
|
-
var S101_NO_OFFICIAL_COUNTERPART = /* @__PURE__ */ new Set([
|
|
27103
|
-
"DNGCIR01",
|
|
27104
|
-
"RADRFL03",
|
|
27105
|
-
"UWTROC01",
|
|
27106
|
-
"LNDMRK18",
|
|
27107
|
-
"LNDMRK19",
|
|
27108
|
-
"LIGHTHOUSE01",
|
|
27109
|
-
"SISTAT02",
|
|
27110
|
-
"WATTUR01"
|
|
27111
|
-
]);
|
|
27112
|
-
var S101_TABLES = {
|
|
27113
|
-
DAY: S101_DAY,
|
|
27114
|
-
DUSK: S101_DUSK,
|
|
27115
|
-
NIGHT: S101_NIGHT
|
|
27116
|
-
};
|
|
27117
|
-
var warnedTokens = /* @__PURE__ */ new Set();
|
|
27118
|
-
function reportUnresolvedToken(token, table) {
|
|
27119
|
-
const key = `${table}:${token}`;
|
|
27120
|
-
if (warnedTokens.has(key)) return;
|
|
27121
|
-
warnedTokens.add(key);
|
|
27122
|
-
console.warn(
|
|
27123
|
-
`[chrt/s101] colour token "${token}" is not defined in the official S-101 ${table} profile. The symbol using it will draw with a substituted colour, which on a chart changes what the symbol MEANS. Re-check the vendored assets/s101/css/${table.toLowerCase()}SvgStyle.css against the pinned catalogue commit.`
|
|
27124
|
-
);
|
|
27125
|
-
}
|
|
27126
|
-
function resolveS101Color(token, table = "DAY") {
|
|
27127
|
-
const hex = S101_TABLES[table][token];
|
|
27128
|
-
if (hex) return hex;
|
|
27129
|
-
reportUnresolvedToken(token, table);
|
|
27130
|
-
return "#000000";
|
|
27131
|
-
}
|
|
27132
|
-
function getThemedS101Svg(symbolName, table = "DAY") {
|
|
27133
|
-
const raw = S101_POINT_SVGS[symbolName];
|
|
27134
|
-
if (!raw) return null;
|
|
27135
|
-
return raw.replace(/(fill|stroke)="([A-Z][A-Z0-9]{3,5})"/g, (match, attr, token) => {
|
|
27136
|
-
if (token === "none") return match;
|
|
27137
|
-
const hex = S101_TABLES[table][token];
|
|
27138
|
-
if (hex) return `${attr}="${hex}"`;
|
|
27139
|
-
reportUnresolvedToken(token, table);
|
|
27140
|
-
return match;
|
|
27141
|
-
});
|
|
27142
|
-
}
|
|
27143
|
-
function s101ArtworkProvenance(symbolName) {
|
|
27144
|
-
if (!symbolName) return "chrt-drawn";
|
|
27145
|
-
if (S101_SYMBOL_NAMES.has(symbolName)) return "official";
|
|
27146
|
-
if (getS52SvgString(symbolName) !== null) return "s52-substitute";
|
|
27147
|
-
return "chrt-drawn";
|
|
27148
|
-
}
|
|
27149
|
-
function rewriteIconToS101(node) {
|
|
27150
|
-
if (typeof node === "string") {
|
|
27151
|
-
return S101_SYMBOL_NAMES.has(node) ? `${S101_ICON_PREFIX}${node}` : node;
|
|
27152
|
-
}
|
|
27153
|
-
if (Array.isArray(node)) return node.map(rewriteIconToS101);
|
|
27154
|
-
return node;
|
|
27155
|
-
}
|
|
27156
|
-
var S101_AREA_FILL_NAMES = /* @__PURE__ */ new Set();
|
|
27157
|
-
var S101_LINE_STYLE_NAMES = /* @__PURE__ */ new Set();
|
|
27158
|
-
function rewritePatternToS101(node, available) {
|
|
27159
|
-
if (typeof node === "string") {
|
|
27160
|
-
return available.has(node) ? `${S101_ICON_PREFIX}${node}` : node;
|
|
27161
|
-
}
|
|
27162
|
-
if (Array.isArray(node)) return node.map((n) => rewritePatternToS101(n, available));
|
|
27163
|
-
return node;
|
|
27164
|
-
}
|
|
27165
|
-
function collectLiterals(node, out) {
|
|
27166
|
-
if (typeof node === "string") {
|
|
27167
|
-
out.add(node);
|
|
27168
|
-
return;
|
|
27169
|
-
}
|
|
27170
|
-
if (Array.isArray(node)) for (const n of node) collectLiterals(n, out);
|
|
27171
|
-
}
|
|
27172
|
-
function applyS101SymbolSet(style) {
|
|
27173
|
-
const seen = { icon: /* @__PURE__ */ new Set(), fill: /* @__PURE__ */ new Set(), line: /* @__PURE__ */ new Set() };
|
|
27174
|
-
for (const layer of style.layers) {
|
|
27175
|
-
const layout = layer.layout;
|
|
27176
|
-
const paint = layer.paint;
|
|
27177
|
-
if (layer.type === "symbol" && layout && layout["icon-image"] != null) {
|
|
27178
|
-
layout["icon-image"] = rewriteIconToS101(layout["icon-image"]);
|
|
27179
|
-
collectLiterals(layout["icon-image"], seen.icon);
|
|
27180
|
-
}
|
|
27181
|
-
for (const bag of [layout, paint]) {
|
|
27182
|
-
if (!bag) continue;
|
|
27183
|
-
if (bag["fill-pattern"] != null) {
|
|
27184
|
-
bag["fill-pattern"] = rewritePatternToS101(bag["fill-pattern"], S101_AREA_FILL_NAMES);
|
|
27185
|
-
collectLiterals(bag["fill-pattern"], seen.fill);
|
|
27186
|
-
}
|
|
27187
|
-
if (bag["line-pattern"] != null) {
|
|
27188
|
-
bag["line-pattern"] = rewritePatternToS101(bag["line-pattern"], S101_LINE_STYLE_NAMES);
|
|
27189
|
-
collectLiterals(bag["line-pattern"], seen.line);
|
|
27190
|
-
}
|
|
27191
|
-
}
|
|
27192
|
-
}
|
|
27193
|
-
const official = (names) => [...names].filter((n) => n.startsWith(S101_ICON_PREFIX)).length;
|
|
27194
|
-
const iconSubs = [...seen.icon].filter((n) => S101_NO_OFFICIAL_COUNTERPART.has(n)).length;
|
|
27195
|
-
const patternSubs = (names) => [...names].filter((n) => /^(AP|LC):/.test(n)).length;
|
|
27196
|
-
const coverage = {
|
|
27197
|
-
pointSymbols: { official: official(seen.icon), s52Substitute: iconSubs },
|
|
27198
|
-
areaFills: { official: official(seen.fill), s52Substitute: patternSubs(seen.fill) },
|
|
27199
|
-
lineStyles: { official: official(seen.line), s52Substitute: patternSubs(seen.line) }
|
|
27200
|
-
};
|
|
27201
|
-
const metadata = style.metadata;
|
|
27202
|
-
metadata["chrt:symbol-set"] = "s101";
|
|
27203
|
-
metadata["chrt:s101-source-commit"] = S101_SOURCE_COMMIT;
|
|
27204
|
-
metadata["chrt:s101-coverage"] = {
|
|
27205
|
-
...coverage,
|
|
27206
|
-
/** True only when NOTHING in this document is an S-52 substitute. A
|
|
27207
|
-
* consumer that wants "S-101 or nothing" branches on this one boolean. */
|
|
27208
|
-
complete: Object.values(coverage).every((c) => c.s52Substitute === 0)
|
|
27209
|
-
};
|
|
27210
|
-
return style;
|
|
27211
|
-
}
|
|
27212
|
-
|
|
27213
|
-
// src/atlas/chrt-svgs-generated.ts
|
|
27214
|
-
var CHRT_GLYPH_SVGS = {
|
|
27215
|
-
"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>',
|
|
27216
|
-
"badge-sound": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="7" cy="16" r="2.2" fill="#D200D2"/><path d="M12 10a9 9 0 0 1 0 12M16.5 6.5a15 15 0 0 1 0 19M21 3a21 21 0 0 1 0 26" stroke="#D200D2" stroke-width="2.4" fill="none" stroke-linecap="round"/></svg>',
|
|
27217
|
-
"beacon-cairn": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="11" r="4" fill="#fff"/><circle cx="11.5" cy="19" r="4.6" fill="#fff"/><circle cx="20.5" cy="19" r="4.6" fill="#fff"/><path d="M7 28h18" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27218
|
-
"beacon-generic": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 25V12" stroke="#fff" stroke-width="2.6" stroke-linecap="round"/><path d="M16 4l6 8H10Z" fill="#fff"/><path d="M8 28h16M11 25h10" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27219
|
-
"beacon-lattice": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M11 25 13 6h6l2 19M12 20h8M12.6 13h6.8M11 25l9-12M21 25l-9-12" stroke="#fff" stroke-width="1.8" stroke-linecap="round" fill="none"/><path d="M8 28h16" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27220
|
-
"beacon-stake": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 25V6" stroke="#fff" stroke-width="2.8" stroke-linecap="round"/><path d="M9 28h14" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27221
|
-
"beacon-tower": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M12 6h8l1.6 19H10.4Z" fill="#fff"/><path d="M10 6h12" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><path d="M8 28h16" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27222
|
-
"buoy-barrel": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect x="7" y="13" width="18" height="11" rx="5.5" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27223
|
-
"buoy-can": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M9 12h14v11a2 2 0 0 1-2 2H11a2 2 0 0 1-2-2Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27224
|
-
"buoy-conical": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 8 24.5 25H7.5Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27225
|
-
"buoy-pillar": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M13 6h6l1.4 19H11.6Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27226
|
-
"buoy-spar": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M14.5 5h3l1 20h-5Z" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27227
|
-
"buoy-spherical": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="17.5" r="8.5" fill="#fff"/><path d="M6 28h20" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27228
|
-
"buoy-super": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M7 17h18l-2.5 8h-13Z" fill="#fff"/><path d="M16 17V8" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><circle cx="16" cy="6.5" r="2" fill="#fff"/><path d="M5 28h22" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27229
|
-
"cardinal-e": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 1.5 20 6h-8Z" fill="#101418"/><path d="M16 11 12 6.5h8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27230
|
-
"cardinal-n": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 1.5 20 6h-8Z" fill="#101418"/><path d="M16 6.5 20 11h-8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27231
|
-
"cardinal-s": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 11 12 6.5h8Z" fill="#101418"/><path d="M16 6 12 1.5h8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27232
|
-
"cardinal-w": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 6 12 1.5h8Z" fill="#101418"/><path d="M16 6.5 20 11h-8Z" fill="#101418"/><path d="M13 12h6l1 13H12Z" fill="#FFC805"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27233
|
-
"daymark-square": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect x="6" y="6" width="20" height="20" rx="1.5" fill="#fff"/></svg>',
|
|
27234
|
-
"daymark-triangle": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 5 28 26H4Z" fill="#fff"/></svg>',
|
|
27235
|
-
"hatch-nocoverage": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-3 9 L9 -3 M-3 21 L21 -3 M3 27 L27 3 M15 27 L27 15" stroke="#8A97A0" stroke-width="0.8" fill="none"/><path d="M-3 15 L15 33 M-3 3 L21 27 M3 -3 L27 21 M15 -3 L27 9" stroke="#8A97A0" stroke-width="0.8" fill="none"/></svg>',
|
|
27236
|
-
"hatch-uncharted": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-3 9 L9 -3 M-3 21 L21 -3 M3 27 L27 3 M15 27 L27 15" stroke="#5B7383" stroke-width="0.65" fill="none"/></svg>',
|
|
27237
|
-
"isolated-danger": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="3.5" r="2.4" fill="#101418"/><circle cx="16" cy="9.5" r="2.4" fill="#101418"/><path d="M13 13h6l1 12H12Z" fill="#101418"/><rect x="12.4" y="17.5" width="7.2" height="3.4" fill="#E5484D"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27238
|
-
"light-flare": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="11" cy="11" r="3.2" fill="#fff"/><path d="M13.4 13.4 24 19.5a2.6 2.6 0 0 1-4.5 4.5L13.4 13.4Z" fill="#fff"/></svg>',
|
|
27239
|
-
"lightship": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M6 20l3-5h14l3 5-3.5 4h-13Z" fill="#fff"/><path d="M16 15V8" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/><circle cx="16" cy="6" r="2.2" fill="#fff"/><path d="M5 28h22" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
27240
|
-
"safe-water": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="6" r="2.4" fill="#E5484D"/><circle cx="16" cy="17.5" r="8" fill="#F8FAFC"/><path d="M12.2 10.5a8 8 0 0 0 0 14M19.8 10.5a8 8 0 0 1 0 14" stroke="#E5484D" stroke-width="3" fill="none"/><path d="M6 28h20" stroke="#101418" stroke-width="2.4" stroke-linecap="round"/></svg>'
|
|
27241
|
-
};
|
|
27242
|
-
|
|
27243
|
-
// src/atlas/chrt-style.ts
|
|
27244
|
-
var CHRT_ICON_PREFIX = "chrt:";
|
|
27245
|
-
var CHRT_SDF_GLYPHS = /* @__PURE__ */ new Set([
|
|
27246
|
-
"buoy-can",
|
|
27247
|
-
"buoy-conical",
|
|
27248
|
-
"buoy-spherical",
|
|
27249
|
-
"buoy-pillar",
|
|
27250
|
-
"buoy-spar",
|
|
27251
|
-
"buoy-barrel",
|
|
27252
|
-
"buoy-super",
|
|
27253
|
-
"beacon-generic",
|
|
27254
|
-
"beacon-tower",
|
|
27255
|
-
"beacon-lattice",
|
|
27256
|
-
"beacon-stake",
|
|
27257
|
-
"beacon-cairn",
|
|
27258
|
-
"light-flare",
|
|
27259
|
-
"lightship",
|
|
27260
|
-
"daymark-triangle",
|
|
27261
|
-
"daymark-square"
|
|
27262
|
-
]);
|
|
27263
|
-
var CHRT_GLYPH_NAMES = new Set(Object.keys(CHRT_GLYPH_SVGS));
|
|
27264
|
-
var CHRT_ATON_COLORS = {
|
|
27265
|
-
"1": "#F8FAFC",
|
|
27266
|
-
// white
|
|
27267
|
-
"2": "#101418",
|
|
27268
|
-
// black
|
|
27269
|
-
"3": "#E5484D",
|
|
27270
|
-
// red
|
|
27271
|
-
"4": "#30A46C",
|
|
27272
|
-
// green
|
|
27273
|
-
"5": "#3E63DD",
|
|
27274
|
-
// blue
|
|
27275
|
-
"6": "#FFC805",
|
|
27276
|
-
// yellow
|
|
27277
|
-
"7": "#8B9298",
|
|
27278
|
-
// grey
|
|
27279
|
-
"8": "#A66B3A",
|
|
27280
|
-
// brown
|
|
27281
|
-
"9": "#FFB224",
|
|
27282
|
-
// amber
|
|
27283
|
-
"10": "#7E4CCB",
|
|
27284
|
-
// violet
|
|
27285
|
-
"11": "#F76B15",
|
|
27286
|
-
// orange
|
|
27287
|
-
"12": "#D671C1"
|
|
27288
|
-
// pink (magenta-ish)
|
|
27289
|
-
};
|
|
27290
|
-
var CHRT_UNKNOWN_TINT = "#8B9298";
|
|
27291
|
-
var HOUSE_BLACK = "#101418";
|
|
27292
|
-
function chrtHouseColor(dayHex, palette = "DAY") {
|
|
27293
|
-
if (palette === "DAY") return dayHex;
|
|
27294
|
-
const isBlack = dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase();
|
|
27295
|
-
if (palette === "DUSK") {
|
|
27296
|
-
return isBlack ? dayHex : capLuminance(dayHex, relativeLuminance(resolveColor("CHBLK", "DUSK")));
|
|
27297
|
-
}
|
|
27298
|
-
return isBlack ? NIGHT_INK : capLuminance(dayHex, relativeLuminance(NIGHT_MUTED_INK));
|
|
27299
|
-
}
|
|
27300
|
-
function themeChrtGlyph(svg3, palette = "DAY") {
|
|
27301
|
-
if (palette === "DAY") return svg3;
|
|
27302
|
-
return svg3.replace(
|
|
27303
|
-
/(fill|stroke)="(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})"/g,
|
|
27304
|
-
(_m, attr, hex) => {
|
|
27305
|
-
const six = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
27306
|
-
return `${attr}="${chrtHouseColor(six, palette)}"`;
|
|
27307
|
-
}
|
|
27308
|
-
);
|
|
27309
|
-
}
|
|
27310
|
-
function chrtAtonTintExpression(palette = "DAY") {
|
|
27311
|
-
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
27312
|
-
return colourCodeCase(
|
|
27313
|
-
[
|
|
27314
|
-
["3", c("3")],
|
|
27315
|
-
["4", c("4")],
|
|
27316
|
-
["6", c("6")],
|
|
27317
|
-
["11", c("11")],
|
|
27318
|
-
["9", c("9")],
|
|
27319
|
-
["5", c("5")],
|
|
27320
|
-
["1", c("1")],
|
|
27321
|
-
["2", c("2")],
|
|
27322
|
-
["8", c("8")],
|
|
27323
|
-
["10", c("10")],
|
|
27324
|
-
["12", c("12")],
|
|
27325
|
-
["7", c("7")]
|
|
27326
|
-
],
|
|
27327
|
-
chrtHouseColor(CHRT_UNKNOWN_TINT, palette)
|
|
27328
|
-
);
|
|
28320
|
+
function getAvailablePointSymbols() {
|
|
28321
|
+
return Object.keys(S52_POINT_SVGS);
|
|
27329
28322
|
}
|
|
27330
|
-
function
|
|
27331
|
-
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
|
|
27337
|
-
|
|
27338
|
-
|
|
27339
|
-
|
|
27340
|
-
|
|
27341
|
-
|
|
27342
|
-
|
|
28323
|
+
function getAvailablePatterns() {
|
|
28324
|
+
return Object.keys(S52_PATTERN_SVGS);
|
|
28325
|
+
}
|
|
28326
|
+
function getAvailableLineSymbols() {
|
|
28327
|
+
return Object.keys(S52_LINE_SVGS);
|
|
28328
|
+
}
|
|
28329
|
+
function getS52SymbolStats() {
|
|
28330
|
+
return {
|
|
28331
|
+
pointCount: Object.keys(S52_POINT_SVGS).length,
|
|
28332
|
+
patternCount: Object.keys(S52_PATTERN_SVGS).length,
|
|
28333
|
+
lineCount: Object.keys(S52_LINE_SVGS).length,
|
|
28334
|
+
totalCount: Object.keys(ALL_S52_SVGS).length
|
|
28335
|
+
};
|
|
28336
|
+
}
|
|
28337
|
+
|
|
28338
|
+
// src/atlas/s101-style.ts
|
|
28339
|
+
var S101_ICON_PREFIX = "s101:";
|
|
28340
|
+
var S101_SYMBOL_NAMES = new Set(Object.keys(S101_POINT_SVGS));
|
|
28341
|
+
var S101_NO_OFFICIAL_COUNTERPART = /* @__PURE__ */ new Set([
|
|
28342
|
+
"DNGCIR01",
|
|
28343
|
+
"RADRFL03",
|
|
28344
|
+
"UWTROC01",
|
|
28345
|
+
"LNDMRK18",
|
|
28346
|
+
"LNDMRK19",
|
|
28347
|
+
"LIGHTHOUSE01",
|
|
28348
|
+
"SISTAT02",
|
|
28349
|
+
"WATTUR01"
|
|
28350
|
+
]);
|
|
28351
|
+
function s101EinkTable(einkS52, fallback) {
|
|
28352
|
+
const out = {};
|
|
28353
|
+
for (const [token, dayHex] of Object.entries(S101_DAY)) {
|
|
28354
|
+
out[token] = einkS52[token] ?? fallback(dayHex);
|
|
28355
|
+
}
|
|
28356
|
+
return out;
|
|
28357
|
+
}
|
|
28358
|
+
var S101_TABLES = {
|
|
28359
|
+
DAY: S101_DAY,
|
|
28360
|
+
DUSK: S101_DUSK,
|
|
28361
|
+
NIGHT: S101_NIGHT,
|
|
28362
|
+
EINK_COLOR: s101EinkTable(S52_EINK_COLOR, (hex) => hex),
|
|
28363
|
+
EINK_MONO: s101EinkTable(S52_EINK_MONO, einkMonoGrey),
|
|
28364
|
+
EINK_NIGHT: s101EinkTable(S52_EINK_NIGHT, einkNightGrey)
|
|
28365
|
+
};
|
|
28366
|
+
var warnedTokens = /* @__PURE__ */ new Set();
|
|
28367
|
+
function reportUnresolvedToken(token, table) {
|
|
28368
|
+
const key = `${table}:${token}`;
|
|
28369
|
+
if (warnedTokens.has(key)) return;
|
|
28370
|
+
warnedTokens.add(key);
|
|
28371
|
+
console.warn(
|
|
28372
|
+
`[chrt/s101] colour token "${token}" is not defined in the official S-101 ${table} profile. The symbol using it will draw with a substituted colour, which on a chart changes what the symbol MEANS. Re-check the vendored assets/s101/css/${table.toLowerCase()}SvgStyle.css against the pinned catalogue commit.`
|
|
27343
28373
|
);
|
|
27344
28374
|
}
|
|
27345
|
-
function
|
|
27346
|
-
|
|
28375
|
+
function resolveS101Color(token, table = "DAY") {
|
|
28376
|
+
const hex = S101_TABLES[table][token];
|
|
28377
|
+
if (hex) return hex;
|
|
28378
|
+
reportUnresolvedToken(token, table);
|
|
28379
|
+
return "#000000";
|
|
27347
28380
|
}
|
|
27348
|
-
|
|
27349
|
-
|
|
27350
|
-
return
|
|
27351
|
-
|
|
27352
|
-
|
|
27353
|
-
|
|
27354
|
-
|
|
27355
|
-
|
|
27356
|
-
|
|
27357
|
-
|
|
27358
|
-
g("buoy-spherical"),
|
|
27359
|
-
4,
|
|
27360
|
-
g("buoy-pillar"),
|
|
27361
|
-
5,
|
|
27362
|
-
g("buoy-spar"),
|
|
27363
|
-
6,
|
|
27364
|
-
g("buoy-barrel"),
|
|
27365
|
-
7,
|
|
27366
|
-
g("buoy-super"),
|
|
27367
|
-
8,
|
|
27368
|
-
g("buoy-super"),
|
|
27369
|
-
g("buoy-pillar")
|
|
27370
|
-
];
|
|
28381
|
+
function getThemedS101Svg(symbolName, table = "DAY") {
|
|
28382
|
+
const raw = S101_POINT_SVGS[symbolName];
|
|
28383
|
+
if (!raw) return null;
|
|
28384
|
+
return raw.replace(/(fill|stroke)="([A-Z][A-Z0-9]{3,5})"/g, (match, attr, token) => {
|
|
28385
|
+
if (token === "none") return match;
|
|
28386
|
+
const hex = S101_TABLES[table][token];
|
|
28387
|
+
if (hex) return `${attr}="${hex}"`;
|
|
28388
|
+
reportUnresolvedToken(token, table);
|
|
28389
|
+
return match;
|
|
28390
|
+
});
|
|
27371
28391
|
}
|
|
27372
|
-
function
|
|
27373
|
-
return
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
g("beacon-stake"),
|
|
27378
|
-
2,
|
|
27379
|
-
g("beacon-stake"),
|
|
27380
|
-
3,
|
|
27381
|
-
g("beacon-tower"),
|
|
27382
|
-
4,
|
|
27383
|
-
g("beacon-lattice"),
|
|
27384
|
-
5,
|
|
27385
|
-
g("beacon-stake"),
|
|
27386
|
-
6,
|
|
27387
|
-
g("beacon-cairn"),
|
|
27388
|
-
g("beacon-generic")
|
|
27389
|
-
];
|
|
28392
|
+
function s101ArtworkProvenance(symbolName) {
|
|
28393
|
+
if (!symbolName) return "chrt-drawn";
|
|
28394
|
+
if (S101_SYMBOL_NAMES.has(symbolName)) return "official";
|
|
28395
|
+
if (getS52SvgString(symbolName) !== null) return "s52-substitute";
|
|
28396
|
+
return "chrt-drawn";
|
|
27390
28397
|
}
|
|
27391
|
-
function
|
|
27392
|
-
|
|
27393
|
-
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
27397
|
-
2,
|
|
27398
|
-
g("cardinal-e"),
|
|
27399
|
-
3,
|
|
27400
|
-
g("cardinal-s"),
|
|
27401
|
-
4,
|
|
27402
|
-
g("cardinal-w"),
|
|
27403
|
-
g("cardinal-n")
|
|
27404
|
-
];
|
|
28398
|
+
function rewriteIconToS101(node) {
|
|
28399
|
+
if (typeof node === "string") {
|
|
28400
|
+
return S101_SYMBOL_NAMES.has(node) ? `${S101_ICON_PREFIX}${node}` : node;
|
|
28401
|
+
}
|
|
28402
|
+
if (Array.isArray(node)) return node.map(rewriteIconToS101);
|
|
28403
|
+
return node;
|
|
27405
28404
|
}
|
|
27406
|
-
var
|
|
27407
|
-
|
|
27408
|
-
|
|
27409
|
-
|
|
27410
|
-
|
|
27411
|
-
|
|
27412
|
-
|
|
27413
|
-
|
|
27414
|
-
|
|
27415
|
-
|
|
27416
|
-
|
|
27417
|
-
|
|
27418
|
-
|
|
27419
|
-
|
|
27420
|
-
|
|
27421
|
-
|
|
27422
|
-
|
|
27423
|
-
|
|
27424
|
-
DAYMAR: () => [
|
|
27425
|
-
"match",
|
|
27426
|
-
scalar("TOPSHP"),
|
|
27427
|
-
1,
|
|
27428
|
-
g("daymark-triangle"),
|
|
27429
|
-
2,
|
|
27430
|
-
g("daymark-triangle"),
|
|
27431
|
-
5,
|
|
27432
|
-
g("daymark-triangle"),
|
|
27433
|
-
g("daymark-square")
|
|
27434
|
-
]
|
|
27435
|
-
};
|
|
27436
|
-
var TINTED_CLASSES = /* @__PURE__ */ new Set([
|
|
27437
|
-
"BOYLAT",
|
|
27438
|
-
"BOYSPP",
|
|
27439
|
-
"BOYINB",
|
|
27440
|
-
"BCNLAT",
|
|
27441
|
-
"BCNSPP",
|
|
27442
|
-
"DAYMAR"
|
|
27443
|
-
]);
|
|
27444
|
-
var CHRT_HALO = { color: HOUSE_BLACK, width: 1 };
|
|
27445
|
-
var CHRT_LINE_DASH = {
|
|
27446
|
-
CBLSUB: { color: "CHMGD", width: 0.8, dash: [6, 3] },
|
|
27447
|
-
PIPSOL: { color: "PIPLN", width: 0.8, dash: [6, 3] }
|
|
27448
|
-
};
|
|
27449
|
-
function applyChrtSymbolSet(style, palette = "DAY") {
|
|
28405
|
+
var S101_AREA_FILL_NAMES = /* @__PURE__ */ new Set();
|
|
28406
|
+
var S101_LINE_STYLE_NAMES = /* @__PURE__ */ new Set();
|
|
28407
|
+
function rewritePatternToS101(node, available) {
|
|
28408
|
+
if (typeof node === "string") {
|
|
28409
|
+
return available.has(node) ? `${S101_ICON_PREFIX}${node}` : node;
|
|
28410
|
+
}
|
|
28411
|
+
if (Array.isArray(node)) return node.map((n) => rewritePatternToS101(n, available));
|
|
28412
|
+
return node;
|
|
28413
|
+
}
|
|
28414
|
+
function collectLiterals(node, out) {
|
|
28415
|
+
if (typeof node === "string") {
|
|
28416
|
+
out.add(node);
|
|
28417
|
+
return;
|
|
28418
|
+
}
|
|
28419
|
+
if (Array.isArray(node)) for (const n of node) collectLiterals(n, out);
|
|
28420
|
+
}
|
|
28421
|
+
function applyS101SymbolSet(style) {
|
|
28422
|
+
const seen = { icon: /* @__PURE__ */ new Set(), fill: /* @__PURE__ */ new Set(), line: /* @__PURE__ */ new Set() };
|
|
27450
28423
|
for (const layer of style.layers) {
|
|
27451
|
-
if (layer.type === "line") {
|
|
27452
|
-
const cls = layer.metadata?.["s52:objectClass"];
|
|
27453
|
-
const dash = cls ? CHRT_LINE_DASH[cls] : void 0;
|
|
27454
|
-
const paint2 = layer.paint ?? {};
|
|
27455
|
-
if (dash && paint2["line-pattern"] != null) {
|
|
27456
|
-
delete paint2["line-pattern"];
|
|
27457
|
-
paint2["line-color"] = resolveColor(dash.color, palette);
|
|
27458
|
-
paint2["line-width"] = dash.width;
|
|
27459
|
-
paint2["line-dasharray"] = dash.dash;
|
|
27460
|
-
layer.paint = paint2;
|
|
27461
|
-
}
|
|
27462
|
-
continue;
|
|
27463
|
-
}
|
|
27464
|
-
if (layer.type !== "symbol") continue;
|
|
27465
|
-
const objectClass = layer.metadata?.["s52:objectClass"];
|
|
27466
|
-
if (!objectClass || !CHRT_ICON_EXPRESSIONS[objectClass]) continue;
|
|
27467
28424
|
const layout = layer.layout;
|
|
27468
|
-
|
|
27469
|
-
layout["icon-image"]
|
|
27470
|
-
|
|
27471
|
-
|
|
27472
|
-
|
|
27473
|
-
|
|
27474
|
-
|
|
27475
|
-
|
|
27476
|
-
|
|
27477
|
-
|
|
27478
|
-
|
|
28425
|
+
const paint = layer.paint;
|
|
28426
|
+
if (layer.type === "symbol" && layout && layout["icon-image"] != null) {
|
|
28427
|
+
layout["icon-image"] = rewriteIconToS101(layout["icon-image"]);
|
|
28428
|
+
collectLiterals(layout["icon-image"], seen.icon);
|
|
28429
|
+
}
|
|
28430
|
+
for (const bag of [layout, paint]) {
|
|
28431
|
+
if (!bag) continue;
|
|
28432
|
+
if (bag["fill-pattern"] != null) {
|
|
28433
|
+
bag["fill-pattern"] = rewritePatternToS101(bag["fill-pattern"], S101_AREA_FILL_NAMES);
|
|
28434
|
+
collectLiterals(bag["fill-pattern"], seen.fill);
|
|
28435
|
+
}
|
|
28436
|
+
if (bag["line-pattern"] != null) {
|
|
28437
|
+
bag["line-pattern"] = rewritePatternToS101(bag["line-pattern"], S101_LINE_STYLE_NAMES);
|
|
28438
|
+
collectLiterals(bag["line-pattern"], seen.line);
|
|
28439
|
+
}
|
|
27479
28440
|
}
|
|
27480
|
-
layer.paint = paint;
|
|
27481
28441
|
}
|
|
28442
|
+
const official = (names) => [...names].filter((n) => n.startsWith(S101_ICON_PREFIX)).length;
|
|
28443
|
+
const iconSubs = [...seen.icon].filter((n) => S101_NO_OFFICIAL_COUNTERPART.has(n)).length;
|
|
28444
|
+
const patternSubs = (names) => [...names].filter((n) => /^(AP|LC):/.test(n)).length;
|
|
28445
|
+
const coverage = {
|
|
28446
|
+
pointSymbols: { official: official(seen.icon), s52Substitute: iconSubs },
|
|
28447
|
+
areaFills: { official: official(seen.fill), s52Substitute: patternSubs(seen.fill) },
|
|
28448
|
+
lineStyles: { official: official(seen.line), s52Substitute: patternSubs(seen.line) }
|
|
28449
|
+
};
|
|
27482
28450
|
const metadata = style.metadata;
|
|
27483
|
-
metadata["chrt:symbol-set"] = "
|
|
28451
|
+
metadata["chrt:symbol-set"] = "s101";
|
|
28452
|
+
metadata["chrt:s101-source-commit"] = S101_SOURCE_COMMIT;
|
|
28453
|
+
metadata["chrt:s101-coverage"] = {
|
|
28454
|
+
...coverage,
|
|
28455
|
+
/** True only when NOTHING in this document is an S-52 substitute. A
|
|
28456
|
+
* consumer that wants "S-101 or nothing" branches on this one boolean. */
|
|
28457
|
+
complete: Object.values(coverage).every((c) => c.s52Substitute === 0)
|
|
28458
|
+
};
|
|
27484
28459
|
return style;
|
|
27485
28460
|
}
|
|
27486
28461
|
|
|
@@ -27637,9 +28612,9 @@ var RASTER_ATON_CLASSES = Object.keys(
|
|
|
27637
28612
|
if (!s52) return true;
|
|
27638
28613
|
return JSON.stringify({ r: paper.rows, d: paper.defaultSymbol }) !== JSON.stringify({ r: s52.rows, d: s52.defaultSymbol });
|
|
27639
28614
|
});
|
|
27640
|
-
function rasterAtonIconExpression(
|
|
27641
|
-
const lookup = S52_ATON_LOOKUPS_PAPER[
|
|
27642
|
-
if (!lookup) throw new Error(`raster-style: no paper lookup for ${
|
|
28615
|
+
function rasterAtonIconExpression(objectClass2) {
|
|
28616
|
+
const lookup = S52_ATON_LOOKUPS_PAPER[objectClass2];
|
|
28617
|
+
if (!lookup) throw new Error(`raster-style: no paper lookup for ${objectClass2}`);
|
|
27643
28618
|
return compileAtonExpression(lookup, resolveRasterImage);
|
|
27644
28619
|
}
|
|
27645
28620
|
function getRasterSvg(name, table = "DAY") {
|
|
@@ -27653,9 +28628,9 @@ function applyRasterSymbolSet(style) {
|
|
|
27653
28628
|
const layout = layer.layout;
|
|
27654
28629
|
if (!layout || layout["icon-image"] == null) continue;
|
|
27655
28630
|
const metadata2 = layer.metadata;
|
|
27656
|
-
const
|
|
27657
|
-
if (typeof
|
|
27658
|
-
layout["icon-image"] = rasterAtonIconExpression(
|
|
28631
|
+
const objectClass2 = metadata2?.["s52:objectClass"];
|
|
28632
|
+
if (typeof objectClass2 !== "string" || !classes.has(objectClass2)) continue;
|
|
28633
|
+
layout["icon-image"] = rasterAtonIconExpression(objectClass2);
|
|
27659
28634
|
}
|
|
27660
28635
|
const metadata = style.metadata;
|
|
27661
28636
|
metadata["chrt:symbol-set"] = "raster";
|
|
@@ -27687,22 +28662,83 @@ function parseSymbolFamily(raw) {
|
|
|
27687
28662
|
const lower = raw.toLowerCase();
|
|
27688
28663
|
return SYMBOL_FAMILIES.find((f) => f === lower) ?? null;
|
|
27689
28664
|
}
|
|
27690
|
-
function iconExpressionForFamily(
|
|
27691
|
-
const s52 = getIconExpression(
|
|
28665
|
+
function iconExpressionForFamily(objectClass2, family) {
|
|
28666
|
+
const s52 = getIconExpression(objectClass2);
|
|
27692
28667
|
switch (family) {
|
|
27693
28668
|
case "s52":
|
|
27694
28669
|
return s52;
|
|
27695
28670
|
case "s101":
|
|
27696
28671
|
return rewriteIconToS101(s52);
|
|
27697
28672
|
case "chrt": {
|
|
27698
|
-
const build = CHRT_ICON_EXPRESSIONS[
|
|
28673
|
+
const build = CHRT_ICON_EXPRESSIONS[objectClass2];
|
|
27699
28674
|
return build ? build() : s52;
|
|
27700
28675
|
}
|
|
27701
28676
|
case "raster":
|
|
27702
|
-
return RASTER_ATON_CLASSES.includes(
|
|
28677
|
+
return RASTER_ATON_CLASSES.includes(objectClass2) ? rasterAtonIconExpression(objectClass2) : s52;
|
|
27703
28678
|
}
|
|
27704
28679
|
}
|
|
27705
28680
|
|
|
28681
|
+
// src/atlas/depth-label-size.ts
|
|
28682
|
+
var DEPTH_LABEL_SIZES = ["standard", "large"];
|
|
28683
|
+
var DEFAULT_DEPTH_LABEL_SIZE = "standard";
|
|
28684
|
+
function parseDepthLabelSize(raw) {
|
|
28685
|
+
if (!raw) return null;
|
|
28686
|
+
const lower = raw.toLowerCase();
|
|
28687
|
+
return DEPTH_LABEL_SIZES.find((s) => s === lower) ?? null;
|
|
28688
|
+
}
|
|
28689
|
+
var DEPTH_LABEL_CLASSES = /* @__PURE__ */ new Set([
|
|
28690
|
+
"SOUNDG",
|
|
28691
|
+
"DEPCNT",
|
|
28692
|
+
"UWTROC",
|
|
28693
|
+
"OBSTRN",
|
|
28694
|
+
"WRECKS"
|
|
28695
|
+
]);
|
|
28696
|
+
var SEMIBOLD = ["Open Sans Semibold"];
|
|
28697
|
+
var HAZARD_DEPTH = {
|
|
28698
|
+
textSize: ["interpolate", ["linear"], ["zoom"], 12, 12, 16, 15],
|
|
28699
|
+
haloWidth: 1.4,
|
|
28700
|
+
font: SEMIBOLD
|
|
28701
|
+
};
|
|
28702
|
+
var LARGE = {
|
|
28703
|
+
// Standard: 7 / 8 / 10 / 12 at z8 / 11 / 14 / 16, halo 1.0, Semibold.
|
|
28704
|
+
SOUNDG: {
|
|
28705
|
+
textSize: ["interpolate", ["linear"], ["zoom"], 8, 10, 11, 11, 14, 14, 16, 17],
|
|
28706
|
+
haloWidth: 1.2,
|
|
28707
|
+
font: SEMIBOLD
|
|
28708
|
+
},
|
|
28709
|
+
// Standard: 8 / 10 at z10 / 14, halo 1.2, Regular.
|
|
28710
|
+
//
|
|
28711
|
+
// ⚠ ~1.2x, NOT 1.4x, AND SEMIBOLD CARRIES THE REST. A contour value rides
|
|
28712
|
+
// its line (`symbol-placement: line`) and needs a straight-enough run to fit;
|
|
28713
|
+
// it also shares rank 300 with the soundings, which place first. At 1.4x the
|
|
28714
|
+
// Boston harbour view kept 15 of Standard's 20 contour labels; at 1.2x, 18.
|
|
28715
|
+
DEPCNT: {
|
|
28716
|
+
textSize: ["interpolate", ["linear"], ["zoom"], 10, 10, 14, 12],
|
|
28717
|
+
haloWidth: 1.2,
|
|
28718
|
+
font: SEMIBOLD
|
|
28719
|
+
},
|
|
28720
|
+
UWTROC: HAZARD_DEPTH,
|
|
28721
|
+
OBSTRN: HAZARD_DEPTH,
|
|
28722
|
+
WRECKS: HAZARD_DEPTH
|
|
28723
|
+
};
|
|
28724
|
+
var LARGE_SOUNDING_PADDING_PX = 3.5;
|
|
28725
|
+
function applyDepthLabelSize(objectClass2, layer, size) {
|
|
28726
|
+
if (size !== "large") return layer;
|
|
28727
|
+
const large = LARGE[objectClass2];
|
|
28728
|
+
if (large === void 0) return layer;
|
|
28729
|
+
const layout = {
|
|
28730
|
+
...layer.layout ?? {},
|
|
28731
|
+
"text-size": large.textSize,
|
|
28732
|
+
"text-font": [...large.font]
|
|
28733
|
+
};
|
|
28734
|
+
if (objectClass2 === "SOUNDG") layout["text-padding"] = LARGE_SOUNDING_PADDING_PX;
|
|
28735
|
+
return {
|
|
28736
|
+
...layer,
|
|
28737
|
+
layout,
|
|
28738
|
+
paint: { ...layer.paint, "text-halo-width": large.haloWidth }
|
|
28739
|
+
};
|
|
28740
|
+
}
|
|
28741
|
+
|
|
27706
28742
|
// src/atlas/base-bundle.ts
|
|
27707
28743
|
var BASE_BUNDLE_ID = BASE_REGION;
|
|
27708
28744
|
var BASE_BUNDLE_SCHEMA = "chrt.basemap-bundle/1";
|
|
@@ -27892,47 +28928,47 @@ function buildLightLabelExpression() {
|
|
|
27892
28928
|
];
|
|
27893
28929
|
return ["concat", litchrExpr, siggrpExpr, colourExpr, sigperExpr, valnmrExpr];
|
|
27894
28930
|
}
|
|
27895
|
-
function buildLayersForClass(
|
|
27896
|
-
if (!S52_LOOKUP[
|
|
27897
|
-
const entry = S52_LOOKUP[
|
|
28931
|
+
function buildLayersForClass(objectClass2, ct, sourceId = DEFAULT_REGION, opts = {}) {
|
|
28932
|
+
if (!S52_LOOKUP[objectClass2]) return [];
|
|
28933
|
+
const entry = S52_LOOKUP[objectClass2];
|
|
27898
28934
|
const layers = [];
|
|
27899
|
-
const geomTypes = getGeomTypesForClass(
|
|
28935
|
+
const geomTypes = getGeomTypesForClass(objectClass2, entry?.style.type);
|
|
27900
28936
|
for (const geomType of geomTypes) {
|
|
27901
|
-
const resolved = resolveStyle(
|
|
27902
|
-
const layerId = `s52-${
|
|
28937
|
+
const resolved = resolveStyle(objectClass2, geomType, ct, { safetyContour: opts.safetyContour });
|
|
28938
|
+
const layerId = `s52-${objectClass2}-${resolved.type}`;
|
|
27903
28939
|
const layer = {
|
|
27904
28940
|
id: layerId,
|
|
27905
28941
|
type: resolved.type,
|
|
27906
28942
|
source: sourceId,
|
|
27907
|
-
"source-layer":
|
|
28943
|
+
"source-layer": objectClass2,
|
|
27908
28944
|
paint: resolved.paint,
|
|
27909
28945
|
metadata: {
|
|
27910
|
-
"s52:objectClass":
|
|
28946
|
+
"s52:objectClass": objectClass2,
|
|
27911
28947
|
"s52:displayCategory": entry?.displayCategory ?? "OTHER",
|
|
27912
28948
|
"s52:category": entry?.category ?? "Meta"
|
|
27913
28949
|
}
|
|
27914
28950
|
};
|
|
27915
|
-
if (resolved.type === "line" && LINE_FROM_POLYGON_LAYERS.has(
|
|
28951
|
+
if (resolved.type === "line" && LINE_FROM_POLYGON_LAYERS.has(objectClass2)) {
|
|
27916
28952
|
layer.filter = GEOM_FILTERS.fill;
|
|
27917
28953
|
} else if (GEOM_FILTERS[resolved.type]) {
|
|
27918
28954
|
layer.filter = GEOM_FILTERS[resolved.type];
|
|
27919
28955
|
}
|
|
27920
|
-
if (SCAMIN_DECLUTTER_CLASSES.has(
|
|
28956
|
+
if (SCAMIN_DECLUTTER_CLASSES.has(objectClass2)) {
|
|
27921
28957
|
layer.filter = layer.filter ? ["all", layer.filter, scaminZoomGateFilter()] : ["all", scaminZoomGateFilter()];
|
|
27922
28958
|
}
|
|
27923
28959
|
if (resolved.minzoom) {
|
|
27924
28960
|
layer.minzoom = resolved.minzoom;
|
|
27925
28961
|
}
|
|
27926
|
-
if (resolved.type === "fill" && OPAQUE_FILLS.has(
|
|
28962
|
+
if (resolved.type === "fill" && OPAQUE_FILLS.has(objectClass2)) {
|
|
27927
28963
|
layer.paint["fill-opacity"] = 1;
|
|
27928
28964
|
}
|
|
27929
|
-
if (resolved.type === "line" && ZOOM_SCALED_LINES2[
|
|
27930
|
-
const [z1, w1, z2, w2] = ZOOM_SCALED_LINES2[
|
|
28965
|
+
if (resolved.type === "line" && ZOOM_SCALED_LINES2[objectClass2]) {
|
|
28966
|
+
const [z1, w1, z2, w2] = ZOOM_SCALED_LINES2[objectClass2];
|
|
27931
28967
|
layer.paint["line-width"] = ["interpolate", ["linear"], ["zoom"], z1, w1, z2, w2];
|
|
27932
28968
|
}
|
|
27933
28969
|
if (resolved.type === "symbol") {
|
|
27934
|
-
const iconExpr = getIconExpression(
|
|
27935
|
-
const childOffset = CHILD_ICON_OFFSETS[
|
|
28970
|
+
const iconExpr = getIconExpression(objectClass2);
|
|
28971
|
+
const childOffset = CHILD_ICON_OFFSETS[objectClass2];
|
|
27936
28972
|
const isSafetyCritical = SAFETY_GROUPS.has(entry?.viewingGroup ?? 0);
|
|
27937
28973
|
const safetyCriticalIconSize = [
|
|
27938
28974
|
"interpolate",
|
|
@@ -28055,7 +29091,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28055
29091
|
]
|
|
28056
29092
|
};
|
|
28057
29093
|
}
|
|
28058
|
-
if (
|
|
29094
|
+
if (objectClass2 === "LIGHTS") {
|
|
28059
29095
|
layer.layout = {
|
|
28060
29096
|
...layer.layout,
|
|
28061
29097
|
"icon-allow-overlap": true,
|
|
@@ -28097,14 +29133,14 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28097
29133
|
} else if (resolved.layout) {
|
|
28098
29134
|
layer.layout = resolved.layout;
|
|
28099
29135
|
}
|
|
28100
|
-
if (
|
|
29136
|
+
if (objectClass2 === "LNDMRK" && resolved.type === "symbol") {
|
|
28101
29137
|
const baseFilterParts = layer.filter && layer.filter[0] === "all" ? layer.filter.slice(1) : layer.filter ? [layer.filter] : [];
|
|
28102
29138
|
const isWind = listAttrHas("CATLMK", ["18", "19"]);
|
|
28103
29139
|
const isLighthouse = listAttrHas("CATLMK", ["17"]);
|
|
28104
29140
|
const scaminFloor = scaminToMinZoom(entry?.scamin);
|
|
28105
29141
|
const windLayer = {
|
|
28106
29142
|
...layer,
|
|
28107
|
-
id: `s52-${
|
|
29143
|
+
id: `s52-${objectClass2}-wind-symbol`,
|
|
28108
29144
|
filter: ["all", ...baseFilterParts, isWind],
|
|
28109
29145
|
// Explicit, not inherited: turbines share exact coordinates with
|
|
28110
29146
|
// their aviation-light LIGHTS feature; without allow-overlap the
|
|
@@ -28117,7 +29153,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28117
29153
|
delete windLayer.minzoom;
|
|
28118
29154
|
const lighthouseLayer = {
|
|
28119
29155
|
...layer,
|
|
28120
|
-
id: `s52-${
|
|
29156
|
+
id: `s52-${objectClass2}-lighthouse-symbol`,
|
|
28121
29157
|
filter: ["all", ...baseFilterParts, isLighthouse, ["!", isWind]],
|
|
28122
29158
|
layout: {
|
|
28123
29159
|
...layer.layout ?? {},
|
|
@@ -28128,7 +29164,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28128
29164
|
delete lighthouseLayer.minzoom;
|
|
28129
29165
|
const otherLayer = {
|
|
28130
29166
|
...layer,
|
|
28131
|
-
id: `s52-${
|
|
29167
|
+
id: `s52-${objectClass2}-other-symbol`,
|
|
28132
29168
|
filter: ["all", ...baseFilterParts, ["!", isWind], ["!", isLighthouse]]
|
|
28133
29169
|
};
|
|
28134
29170
|
if (scaminFloor !== void 0) {
|
|
@@ -28143,10 +29179,10 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28143
29179
|
const valsouNum = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 999], 999];
|
|
28144
29180
|
const watlevNum = ["to-number", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 0];
|
|
28145
29181
|
layers.push({
|
|
28146
|
-
id: `s52-${
|
|
29182
|
+
id: `s52-${objectClass2}-danger-ring`,
|
|
28147
29183
|
type: "symbol",
|
|
28148
29184
|
source: sourceId,
|
|
28149
|
-
"source-layer":
|
|
29185
|
+
"source-layer": objectClass2,
|
|
28150
29186
|
filter: [
|
|
28151
29187
|
"all",
|
|
28152
29188
|
GEOM_FILTERS.symbol,
|
|
@@ -28179,48 +29215,48 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28179
29215
|
},
|
|
28180
29216
|
paint: {},
|
|
28181
29217
|
minzoom: 13,
|
|
28182
|
-
metadata: { "s52:objectClass":
|
|
29218
|
+
metadata: { "s52:objectClass": objectClass2, "s52:role": "isolated-danger-ring" }
|
|
28183
29219
|
});
|
|
28184
29220
|
}
|
|
28185
|
-
if (resolved.type === "fill" && FILL_OUTLINE_LAYERS.has(
|
|
28186
|
-
const outlineColor =
|
|
29221
|
+
if (resolved.type === "fill" && FILL_OUTLINE_LAYERS.has(objectClass2)) {
|
|
29222
|
+
const outlineColor = objectClass2 === "DEPARE" ? resolveColor("CHGRD", ct) : resolveColor("CHGRF", ct);
|
|
28187
29223
|
layers.push({
|
|
28188
|
-
id: `s52-${
|
|
29224
|
+
id: `s52-${objectClass2}-outline`,
|
|
28189
29225
|
type: "line",
|
|
28190
29226
|
source: sourceId,
|
|
28191
|
-
"source-layer":
|
|
29227
|
+
"source-layer": objectClass2,
|
|
28192
29228
|
filter: GEOM_FILTERS.fill,
|
|
28193
29229
|
paint: {
|
|
28194
29230
|
"line-color": outlineColor,
|
|
28195
29231
|
"line-width": 0.3,
|
|
28196
29232
|
"line-opacity": 0.4
|
|
28197
29233
|
},
|
|
28198
|
-
metadata: { "s52:objectClass":
|
|
29234
|
+
metadata: { "s52:objectClass": objectClass2, "s52:role": "outline" }
|
|
28199
29235
|
});
|
|
28200
29236
|
}
|
|
28201
|
-
if (resolved.type === "fill" && REGULATED_AREA_BOUNDARY_LAYERS[
|
|
28202
|
-
const lineStyle = REGULATED_AREA_BOUNDARY_LINESTYLES[
|
|
29237
|
+
if (resolved.type === "fill" && REGULATED_AREA_BOUNDARY_LAYERS[objectClass2]) {
|
|
29238
|
+
const lineStyle = REGULATED_AREA_BOUNDARY_LINESTYLES[objectClass2];
|
|
28203
29239
|
const boundaryPaint = lineStyle ? lineStylePaint(lineStyle) : {
|
|
28204
|
-
"line-color": resolveColor(REGULATED_AREA_BOUNDARY_LAYERS[
|
|
29240
|
+
"line-color": resolveColor(REGULATED_AREA_BOUNDARY_LAYERS[objectClass2], ct),
|
|
28205
29241
|
"line-width": 1,
|
|
28206
29242
|
"line-dasharray": [4, 2],
|
|
28207
29243
|
"line-opacity": 0.85
|
|
28208
29244
|
};
|
|
28209
29245
|
layers.push({
|
|
28210
|
-
id: `s52-${
|
|
29246
|
+
id: `s52-${objectClass2}-boundary`,
|
|
28211
29247
|
type: "line",
|
|
28212
29248
|
source: sourceId,
|
|
28213
|
-
"source-layer":
|
|
29249
|
+
"source-layer": objectClass2,
|
|
28214
29250
|
filter: GEOM_FILTERS.fill,
|
|
28215
29251
|
paint: boundaryPaint,
|
|
28216
29252
|
metadata: {
|
|
28217
|
-
"s52:objectClass":
|
|
29253
|
+
"s52:objectClass": objectClass2,
|
|
28218
29254
|
"s52:role": "boundary",
|
|
28219
29255
|
...lineStyle ? { "s52:lineStyle": lineStyle } : {}
|
|
28220
29256
|
}
|
|
28221
29257
|
});
|
|
28222
29258
|
}
|
|
28223
|
-
if (
|
|
29259
|
+
if (objectClass2 === "ACHARE" && resolved.type === "fill") {
|
|
28224
29260
|
layers.push({
|
|
28225
29261
|
id: "s52-ACHARE-boundary",
|
|
28226
29262
|
type: "line",
|
|
@@ -28236,7 +29272,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28236
29272
|
metadata: { "s52:objectClass": "ACHARE", "s52:role": "boundary" }
|
|
28237
29273
|
});
|
|
28238
29274
|
}
|
|
28239
|
-
if (
|
|
29275
|
+
if (objectClass2 === "DRGARE" && resolved.type === "fill") {
|
|
28240
29276
|
layers.push({
|
|
28241
29277
|
id: "s52-DRGARE-boundary",
|
|
28242
29278
|
type: "line",
|
|
@@ -28252,7 +29288,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28252
29288
|
metadata: { "s52:objectClass": "DRGARE", "s52:role": "boundary" }
|
|
28253
29289
|
});
|
|
28254
29290
|
}
|
|
28255
|
-
if (
|
|
29291
|
+
if (objectClass2 === "ACHARE" && resolved.type === "fill") {
|
|
28256
29292
|
layers.push({
|
|
28257
29293
|
id: "s52-ACHARE-anchor-icon",
|
|
28258
29294
|
type: "symbol",
|
|
@@ -28281,7 +29317,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28281
29317
|
metadata: { "s52:objectClass": "ACHARE", "s52:role": "centroid-icon" }
|
|
28282
29318
|
});
|
|
28283
29319
|
}
|
|
28284
|
-
if (
|
|
29320
|
+
if (objectClass2 === "LNDARE" && resolved.type === "fill") {
|
|
28285
29321
|
layers.push({
|
|
28286
29322
|
id: `s52-LNDARE-point`,
|
|
28287
29323
|
type: "circle",
|
|
@@ -28297,7 +29333,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28297
29333
|
metadata: { "s52:objectClass": "LNDARE", "s52:role": "point" }
|
|
28298
29334
|
});
|
|
28299
29335
|
}
|
|
28300
|
-
if (
|
|
29336
|
+
if (objectClass2 === "LNDARE" && resolved.type === "fill") {
|
|
28301
29337
|
layers.push({
|
|
28302
29338
|
id: `s52-LNDARE-line`,
|
|
28303
29339
|
type: "line",
|
|
@@ -28311,7 +29347,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28311
29347
|
metadata: { "s52:objectClass": "LNDARE", "s52:role": "line" }
|
|
28312
29348
|
});
|
|
28313
29349
|
}
|
|
28314
|
-
if (
|
|
29350
|
+
if (objectClass2 === "BUAARE" && resolved.type === "fill") {
|
|
28315
29351
|
layers.push({
|
|
28316
29352
|
id: `s52-BUAARE-point`,
|
|
28317
29353
|
type: "circle",
|
|
@@ -28327,7 +29363,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28327
29363
|
metadata: { "s52:objectClass": "BUAARE", "s52:role": "point" }
|
|
28328
29364
|
});
|
|
28329
29365
|
}
|
|
28330
|
-
if (
|
|
29366
|
+
if (objectClass2 === "RIVERS" && resolved.type === "fill") {
|
|
28331
29367
|
layers.push({
|
|
28332
29368
|
id: `s52-RIVERS-line`,
|
|
28333
29369
|
type: "line",
|
|
@@ -28341,7 +29377,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28341
29377
|
metadata: { "s52:objectClass": "RIVERS", "s52:role": "line" }
|
|
28342
29378
|
});
|
|
28343
29379
|
}
|
|
28344
|
-
if (
|
|
29380
|
+
if (objectClass2 === "BUISGL" && resolved.type === "circle") {
|
|
28345
29381
|
layers.push({
|
|
28346
29382
|
id: `s52-BUISGL-polygon`,
|
|
28347
29383
|
type: "fill",
|
|
@@ -28367,7 +29403,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28367
29403
|
metadata: { "s52:objectClass": "BUISGL", "s52:role": "polygon-outline" }
|
|
28368
29404
|
});
|
|
28369
29405
|
}
|
|
28370
|
-
if (
|
|
29406
|
+
if (objectClass2 === "BRIDGE" && resolved.type === "line") {
|
|
28371
29407
|
layers.push({
|
|
28372
29408
|
id: `s52-BRIDGE-polygon`,
|
|
28373
29409
|
type: "fill",
|
|
@@ -28393,7 +29429,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28393
29429
|
metadata: { "s52:objectClass": "BRIDGE", "s52:role": "polygon-outline" }
|
|
28394
29430
|
});
|
|
28395
29431
|
}
|
|
28396
|
-
if (
|
|
29432
|
+
if (objectClass2 === "PYLONS" && resolved.type === "fill") {
|
|
28397
29433
|
layers.push({
|
|
28398
29434
|
id: `s52-PYLONS-point`,
|
|
28399
29435
|
type: "circle",
|
|
@@ -28409,7 +29445,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28409
29445
|
metadata: { "s52:objectClass": "PYLONS", "s52:role": "point" }
|
|
28410
29446
|
});
|
|
28411
29447
|
}
|
|
28412
|
-
if (
|
|
29448
|
+
if (objectClass2 === "WEDKLP" && resolved.type === "fill") {
|
|
28413
29449
|
layers.push({
|
|
28414
29450
|
id: `s52-WEDKLP-point`,
|
|
28415
29451
|
type: "circle",
|
|
@@ -28426,7 +29462,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28426
29462
|
metadata: { "s52:objectClass": "WEDKLP", "s52:role": "point" }
|
|
28427
29463
|
});
|
|
28428
29464
|
}
|
|
28429
|
-
if (
|
|
29465
|
+
if (objectClass2 === "SLOGRD" && resolved.type === "fill") {
|
|
28430
29466
|
layers.push({
|
|
28431
29467
|
id: `s52-SLOGRD-point`,
|
|
28432
29468
|
type: "circle",
|
|
@@ -28443,7 +29479,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28443
29479
|
metadata: { "s52:objectClass": "SLOGRD", "s52:role": "point" }
|
|
28444
29480
|
});
|
|
28445
29481
|
}
|
|
28446
|
-
if (
|
|
29482
|
+
if (objectClass2 === "RUNWAY" && resolved.type === "fill") {
|
|
28447
29483
|
layers.push({
|
|
28448
29484
|
id: `s52-RUNWAY-line`,
|
|
28449
29485
|
type: "line",
|
|
@@ -28457,7 +29493,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28457
29493
|
metadata: { "s52:objectClass": "RUNWAY", "s52:role": "line" }
|
|
28458
29494
|
});
|
|
28459
29495
|
}
|
|
28460
|
-
if (
|
|
29496
|
+
if (objectClass2 === "FORSTC" && resolved.type === "fill") {
|
|
28461
29497
|
layers.push({
|
|
28462
29498
|
id: `s52-FORSTC-line`,
|
|
28463
29499
|
type: "line",
|
|
@@ -28471,7 +29507,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28471
29507
|
metadata: { "s52:objectClass": "FORSTC", "s52:role": "line" }
|
|
28472
29508
|
});
|
|
28473
29509
|
}
|
|
28474
|
-
if (
|
|
29510
|
+
if (objectClass2 === "FSHFAC" && resolved.type === "fill") {
|
|
28475
29511
|
layers.push({
|
|
28476
29512
|
id: `s52-FSHFAC-point`,
|
|
28477
29513
|
type: "circle",
|
|
@@ -28487,7 +29523,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28487
29523
|
metadata: { "s52:objectClass": "FSHFAC", "s52:role": "point" }
|
|
28488
29524
|
});
|
|
28489
29525
|
}
|
|
28490
|
-
if (
|
|
29526
|
+
if (objectClass2 === "OBSTRN" && resolved.type === "symbol") {
|
|
28491
29527
|
layers.push({
|
|
28492
29528
|
id: `s52-OBSTRN-polygon`,
|
|
28493
29529
|
type: "fill",
|
|
@@ -28527,7 +29563,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28527
29563
|
metadata: { "s52:objectClass": "OBSTRN", "s52:role": "line" }
|
|
28528
29564
|
});
|
|
28529
29565
|
}
|
|
28530
|
-
if (
|
|
29566
|
+
if (objectClass2 === "SLCONS" && resolved.type === "line") {
|
|
28531
29567
|
layers.push({
|
|
28532
29568
|
id: `s52-SLCONS-polygon`,
|
|
28533
29569
|
type: "fill",
|
|
@@ -28567,7 +29603,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28567
29603
|
metadata: { "s52:objectClass": "SLCONS", "s52:role": "point" }
|
|
28568
29604
|
});
|
|
28569
29605
|
}
|
|
28570
|
-
if (
|
|
29606
|
+
if (objectClass2 === "PRDARE" && resolved.type === "line") {
|
|
28571
29607
|
layers.push({
|
|
28572
29608
|
id: `s52-PRDARE-point`,
|
|
28573
29609
|
type: "circle",
|
|
@@ -28584,7 +29620,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28584
29620
|
metadata: { "s52:objectClass": "PRDARE", "s52:role": "point" }
|
|
28585
29621
|
});
|
|
28586
29622
|
}
|
|
28587
|
-
if (
|
|
29623
|
+
if (objectClass2 === "CTNARE" && resolved.type === "line") {
|
|
28588
29624
|
layers.push({
|
|
28589
29625
|
id: `s52-CTNARE-point`,
|
|
28590
29626
|
type: "circle",
|
|
@@ -28600,7 +29636,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28600
29636
|
metadata: { "s52:objectClass": "CTNARE", "s52:role": "point" }
|
|
28601
29637
|
});
|
|
28602
29638
|
}
|
|
28603
|
-
if (
|
|
29639
|
+
if (objectClass2 === "PONTON" && resolved.type === "line") {
|
|
28604
29640
|
layers.push({
|
|
28605
29641
|
id: `s52-PONTON-polygon`,
|
|
28606
29642
|
type: "fill",
|
|
@@ -28626,7 +29662,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28626
29662
|
metadata: { "s52:objectClass": "PONTON", "s52:role": "polygon-outline" }
|
|
28627
29663
|
});
|
|
28628
29664
|
}
|
|
28629
|
-
if (
|
|
29665
|
+
if (objectClass2 === "WRECKS" && resolved.type === "symbol") {
|
|
28630
29666
|
layers.push({
|
|
28631
29667
|
id: `s52-WRECKS-polygon`,
|
|
28632
29668
|
type: "fill",
|
|
@@ -28653,7 +29689,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28653
29689
|
metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon-outline" }
|
|
28654
29690
|
});
|
|
28655
29691
|
}
|
|
28656
|
-
if (
|
|
29692
|
+
if (objectClass2 === "WATTUR" && resolved.type === "symbol") {
|
|
28657
29693
|
layers.push({
|
|
28658
29694
|
id: `s52-WATTUR-polygon`,
|
|
28659
29695
|
type: "fill",
|
|
@@ -28667,7 +29703,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28667
29703
|
metadata: { "s52:objectClass": "WATTUR", "s52:role": "polygon" }
|
|
28668
29704
|
});
|
|
28669
29705
|
}
|
|
28670
|
-
if (
|
|
29706
|
+
if (objectClass2 === "MAGVAR" && resolved.type === "line") {
|
|
28671
29707
|
layers.push({
|
|
28672
29708
|
id: "s52-MAGVAR-point",
|
|
28673
29709
|
type: "circle",
|
|
@@ -28684,7 +29720,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28684
29720
|
metadata: { "s52:objectClass": "MAGVAR", "s52:role": "point" }
|
|
28685
29721
|
});
|
|
28686
29722
|
}
|
|
28687
|
-
if (
|
|
29723
|
+
if (objectClass2 === "DMPGRD" && resolved.type === "fill") {
|
|
28688
29724
|
layers.push({
|
|
28689
29725
|
id: `s52-DMPGRD-point`,
|
|
28690
29726
|
type: "circle",
|
|
@@ -28701,7 +29737,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28701
29737
|
metadata: { "s52:objectClass": "DMPGRD", "s52:role": "point" }
|
|
28702
29738
|
});
|
|
28703
29739
|
}
|
|
28704
|
-
if (
|
|
29740
|
+
if (objectClass2 === "DAMCON" && resolved.type === "line") {
|
|
28705
29741
|
layers.push({
|
|
28706
29742
|
id: `s52-DAMCON-polygon`,
|
|
28707
29743
|
type: "fill",
|
|
@@ -28727,7 +29763,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28727
29763
|
metadata: { "s52:objectClass": "DAMCON", "s52:role": "outline" }
|
|
28728
29764
|
});
|
|
28729
29765
|
}
|
|
28730
|
-
if (
|
|
29766
|
+
if (objectClass2 === "GATCON" && resolved.type === "line") {
|
|
28731
29767
|
layers.push({
|
|
28732
29768
|
id: `s52-GATCON-polygon`,
|
|
28733
29769
|
type: "fill",
|
|
@@ -28753,7 +29789,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28753
29789
|
metadata: { "s52:objectClass": "GATCON", "s52:role": "outline" }
|
|
28754
29790
|
});
|
|
28755
29791
|
}
|
|
28756
|
-
if (
|
|
29792
|
+
if (objectClass2 === "CANALS" && resolved.type === "line") {
|
|
28757
29793
|
layers.push({
|
|
28758
29794
|
id: `s52-CANALS-polygon`,
|
|
28759
29795
|
type: "fill",
|
|
@@ -28767,7 +29803,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28767
29803
|
metadata: { "s52:objectClass": "CANALS", "s52:role": "polygon" }
|
|
28768
29804
|
});
|
|
28769
29805
|
}
|
|
28770
|
-
if (
|
|
29806
|
+
if (objectClass2 === "PILBOP" && resolved.type === "symbol") {
|
|
28771
29807
|
layers.push({
|
|
28772
29808
|
id: `s52-PILBOP-polygon`,
|
|
28773
29809
|
type: "fill",
|
|
@@ -28794,7 +29830,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28794
29830
|
metadata: { "s52:objectClass": "PILBOP", "s52:role": "outline" }
|
|
28795
29831
|
});
|
|
28796
29832
|
}
|
|
28797
|
-
if (
|
|
29833
|
+
if (objectClass2 === "HRBFAC" && resolved.type === "circle") {
|
|
28798
29834
|
layers.push({
|
|
28799
29835
|
id: `s52-HRBFAC-polygon`,
|
|
28800
29836
|
type: "fill",
|
|
@@ -28808,7 +29844,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28808
29844
|
metadata: { "s52:objectClass": "HRBFAC", "s52:role": "polygon" }
|
|
28809
29845
|
});
|
|
28810
29846
|
}
|
|
28811
|
-
if (
|
|
29847
|
+
if (objectClass2 === "MARCUL" && resolved.type === "fill") {
|
|
28812
29848
|
layers.push({
|
|
28813
29849
|
id: `s52-MARCUL-point`,
|
|
28814
29850
|
type: "circle",
|
|
@@ -28838,7 +29874,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28838
29874
|
metadata: { "s52:objectClass": "MARCUL", "s52:role": "line" }
|
|
28839
29875
|
});
|
|
28840
29876
|
}
|
|
28841
|
-
if (
|
|
29877
|
+
if (objectClass2 === "RECTRC" && resolved.type === "line") {
|
|
28842
29878
|
layers.push({
|
|
28843
29879
|
id: `s52-RECTRC-polygon`,
|
|
28844
29880
|
type: "fill",
|
|
@@ -28852,7 +29888,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28852
29888
|
metadata: { "s52:objectClass": "RECTRC", "s52:role": "polygon" }
|
|
28853
29889
|
});
|
|
28854
29890
|
}
|
|
28855
|
-
if (
|
|
29891
|
+
if (objectClass2 === "PONTON" && resolved.type === "fill") {
|
|
28856
29892
|
layers.push({
|
|
28857
29893
|
id: `s52-PONTON-line`,
|
|
28858
29894
|
type: "line",
|
|
@@ -28866,7 +29902,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28866
29902
|
metadata: { "s52:objectClass": "PONTON", "s52:role": "line" }
|
|
28867
29903
|
});
|
|
28868
29904
|
}
|
|
28869
|
-
if (
|
|
29905
|
+
if (objectClass2 === "SILTNK" && resolved.type === "symbol") {
|
|
28870
29906
|
layers.push({
|
|
28871
29907
|
id: `s52-SILTNK-symbol-polygon`,
|
|
28872
29908
|
type: "symbol",
|
|
@@ -28884,7 +29920,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28884
29920
|
metadata: { "s52:objectClass": "SILTNK", "s52:role": "symbol-polygon" }
|
|
28885
29921
|
});
|
|
28886
29922
|
}
|
|
28887
|
-
if (
|
|
29923
|
+
if (objectClass2 === "ACHBRT" && resolved.type === "symbol") {
|
|
28888
29924
|
layers.push({
|
|
28889
29925
|
id: `s52-ACHBRT-symbol-polygon`,
|
|
28890
29926
|
type: "symbol",
|
|
@@ -28902,7 +29938,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28902
29938
|
metadata: { "s52:objectClass": "ACHBRT", "s52:role": "symbol-polygon" }
|
|
28903
29939
|
});
|
|
28904
29940
|
}
|
|
28905
|
-
if (
|
|
29941
|
+
if (objectClass2 === "BERTHS" && resolved.type === "line") {
|
|
28906
29942
|
layers.push({
|
|
28907
29943
|
id: `s52-BERTHS-circle`,
|
|
28908
29944
|
type: "circle",
|
|
@@ -28918,888 +29954,451 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28918
29954
|
metadata: { "s52:objectClass": "BERTHS", "s52:role": "point" }
|
|
28919
29955
|
});
|
|
28920
29956
|
}
|
|
28921
|
-
if (
|
|
29957
|
+
if (objectClass2 === "TUNNEL" && resolved.type === "line") {
|
|
28922
29958
|
layers.push({
|
|
28923
29959
|
// NOT `s52-TUNNEL-line` — that id is already taken by the main layer
|
|
28924
29960
|
// (`s52-${objectClass}-${resolved.type}`), and duplicate layer ids
|
|
28925
29961
|
// are a Mapbox style-spec violation (caught by style-integrity.test).
|
|
28926
29962
|
id: `s52-TUNNEL-linestring`,
|
|
28927
29963
|
type: "line",
|
|
28928
|
-
source: sourceId,
|
|
28929
|
-
"source-layer": "TUNNEL",
|
|
28930
|
-
filter: GEOM_FILTERS.line,
|
|
28931
|
-
paint: {
|
|
28932
|
-
"line-color": resolveColor("CHGRD", ct),
|
|
28933
|
-
"line-width": 1,
|
|
28934
|
-
"line-dasharray": [2, 2]
|
|
28935
|
-
},
|
|
28936
|
-
metadata: { "s52:objectClass": "TUNNEL", "s52:role": "line" }
|
|
28937
|
-
});
|
|
28938
|
-
}
|
|
28939
|
-
}
|
|
28940
|
-
return layers;
|
|
28941
|
-
}
|
|
28942
|
-
function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
|
|
28943
|
-
if (!LABELED_LAYERS.has(objectClass)) return null;
|
|
28944
|
-
const config = getLabelConfig(objectClass, {
|
|
28945
|
-
colorTable: ct,
|
|
28946
|
-
safetyContour: labelOpts.safetyContour,
|
|
28947
|
-
twoShadeSoundings: labelOpts.twoShadeSoundings,
|
|
28948
|
-
soundingFontWeight: labelOpts.soundingFontWeight
|
|
28949
|
-
});
|
|
28950
|
-
if (!config) return null;
|
|
28951
|
-
let textField = config.textField;
|
|
28952
|
-
if (objectClass === "LIGHTS") {
|
|
28953
|
-
textField = buildLightLabelExpression();
|
|
28954
|
-
}
|
|
28955
|
-
if (typeof textField !== "string" && Array.isArray(textField)) {
|
|
28956
|
-
const flat = JSON.stringify(textField);
|
|
28957
|
-
if (flat.includes("_navAidLabel")) {
|
|
28958
|
-
textField = buildNavAidLabelExpression();
|
|
28959
|
-
}
|
|
28960
|
-
}
|
|
28961
|
-
if (depthUnits === "feet") {
|
|
28962
|
-
if (objectClass === "SOUNDG") {
|
|
28963
|
-
textField = [
|
|
28964
|
-
"concat",
|
|
28965
|
-
[
|
|
28966
|
-
"round",
|
|
28967
|
-
[
|
|
28968
|
-
"*",
|
|
28969
|
-
[
|
|
28970
|
-
"to-number",
|
|
28971
|
-
["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
|
|
28972
|
-
0
|
|
28973
|
-
],
|
|
28974
|
-
3.28084
|
|
28975
|
-
]
|
|
28976
|
-
],
|
|
28977
|
-
""
|
|
28978
|
-
];
|
|
28979
|
-
} else if (objectClass === "DEPCNT") {
|
|
28980
|
-
textField = [
|
|
28981
|
-
"concat",
|
|
28982
|
-
["round", ["*", ["to-number", ["get", "VALDCO"]], 3.28084]],
|
|
28983
|
-
"ft"
|
|
28984
|
-
];
|
|
28985
|
-
} else if (objectClass === "UWTROC" || objectClass === "OBSTRN" || objectClass === "WRECKS") {
|
|
28986
|
-
const valsouFt = [
|
|
28987
|
-
"to-number",
|
|
28988
|
-
["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0],
|
|
28989
|
-
0
|
|
28990
|
-
];
|
|
28991
|
-
textField = [
|
|
28992
|
-
"case",
|
|
28993
|
-
[">", valsouFt, 0],
|
|
28994
|
-
["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
|
|
28995
|
-
""
|
|
28996
|
-
];
|
|
28997
|
-
}
|
|
28998
|
-
}
|
|
28999
|
-
const labelPriority = getLabelPriority(objectClass);
|
|
29000
|
-
const layer = {
|
|
29001
|
-
id: `s52-${objectClass}-label`,
|
|
29002
|
-
type: "symbol",
|
|
29003
|
-
source: sourceId,
|
|
29004
|
-
"source-layer": objectClass,
|
|
29005
|
-
filter: GEOM_FILTERS.symbol,
|
|
29006
|
-
layout: {
|
|
29007
|
-
"text-field": textField,
|
|
29008
|
-
"text-size": config.textSize,
|
|
29009
|
-
"text-font": config.textFont ?? ["Open Sans Regular"],
|
|
29010
|
-
"text-offset": config.textOffset,
|
|
29011
|
-
"text-anchor": "top",
|
|
29012
|
-
"text-allow-overlap": false,
|
|
29013
|
-
"text-optional": true,
|
|
29014
|
-
"text-padding": 4,
|
|
29015
|
-
"symbol-sort-key": labelPriority * 1e3,
|
|
29016
|
-
...config.layout ?? {}
|
|
29017
|
-
},
|
|
29018
|
-
paint: {
|
|
29019
|
-
"text-color": config.textColor,
|
|
29020
|
-
"text-halo-color": config.textHaloColor,
|
|
29021
|
-
"text-halo-width": config.textHaloWidth
|
|
29022
|
-
},
|
|
29023
|
-
metadata: {
|
|
29024
|
-
"s52:objectClass": objectClass,
|
|
29025
|
-
"chrt:labelPriority": labelPriority
|
|
29026
|
-
}
|
|
29027
|
-
};
|
|
29028
|
-
if (config.minzoom) {
|
|
29029
|
-
layer.minzoom = config.minzoom;
|
|
29030
|
-
}
|
|
29031
|
-
if (objectClass === "DEPCNT") {
|
|
29032
|
-
layer.filter = GEOM_FILTERS.line;
|
|
29033
|
-
}
|
|
29034
|
-
if (objectClass === "CBLOHD" || objectClass === "PIPOHD") {
|
|
29035
|
-
layer.filter = GEOM_FILTERS.line;
|
|
29036
|
-
}
|
|
29037
|
-
if (objectClass === "SBDARE") {
|
|
29038
|
-
delete layer.filter;
|
|
29039
|
-
}
|
|
29040
|
-
if (objectClass === "MAGVAR") {
|
|
29041
|
-
delete layer.filter;
|
|
29042
|
-
}
|
|
29043
|
-
if (getLabelBehavior(objectClass) === "orientation-cross-band") {
|
|
29044
|
-
layer.filter = LABEL_ANCHOR_FILTER;
|
|
29045
|
-
}
|
|
29046
|
-
return layer;
|
|
29047
|
-
}
|
|
29048
|
-
function getGeomTypesForClass(objectClass, defaultType) {
|
|
29049
|
-
if (SYMBOL_MAP[objectClass] && defaultType === "symbol") {
|
|
29050
|
-
return ["symbol"];
|
|
29051
|
-
}
|
|
29052
|
-
if (objectClass === "SOUNDG") {
|
|
29053
|
-
return ["circle"];
|
|
29054
|
-
}
|
|
29055
|
-
if (defaultType) {
|
|
29056
|
-
return [defaultType];
|
|
29057
|
-
}
|
|
29058
|
-
return ["circle"];
|
|
29059
|
-
}
|
|
29060
|
-
function getOrderedClasses() {
|
|
29061
|
-
const ordered = [...S52_DRAW_ORDER];
|
|
29062
|
-
for (const key of Object.keys(S52_LOOKUP)) {
|
|
29063
|
-
if (!ordered.includes(key)) {
|
|
29064
|
-
const insertIdx = ordered.indexOf("SOUNDG");
|
|
29065
|
-
if (insertIdx >= 0) {
|
|
29066
|
-
ordered.splice(insertIdx, 0, key);
|
|
29067
|
-
} else {
|
|
29068
|
-
ordered.push(key);
|
|
29069
|
-
}
|
|
29070
|
-
}
|
|
29071
|
-
}
|
|
29072
|
-
return ordered;
|
|
29073
|
-
}
|
|
29074
|
-
|
|
29075
|
-
// src/atlas/terrain-order.ts
|
|
29076
|
-
var DRAPEABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
29077
|
-
"background",
|
|
29078
|
-
"fill",
|
|
29079
|
-
"line",
|
|
29080
|
-
"raster",
|
|
29081
|
-
"hillshade",
|
|
29082
|
-
"color-relief"
|
|
29083
|
-
]);
|
|
29084
|
-
function isDrapeable(layer) {
|
|
29085
|
-
return DRAPEABLE_LAYER_TYPES.has(layer.type);
|
|
29086
|
-
}
|
|
29087
|
-
function drapedFirst(layers) {
|
|
29088
|
-
const draped = [];
|
|
29089
|
-
const direct = [];
|
|
29090
|
-
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29091
|
-
return [...draped, ...direct];
|
|
29092
|
-
}
|
|
29093
|
-
function terrainStackCount(layers) {
|
|
29094
|
-
let stacks = 0;
|
|
29095
|
-
let prevDraped = false;
|
|
29096
|
-
for (const l of layers) {
|
|
29097
|
-
const d = isDrapeable(l);
|
|
29098
|
-
if (d && !prevDraped) stacks++;
|
|
29099
|
-
prevDraped = d;
|
|
29100
|
-
}
|
|
29101
|
-
return stacks;
|
|
29102
|
-
}
|
|
29103
|
-
function drapeHalves(layers) {
|
|
29104
|
-
const draped = [];
|
|
29105
|
-
const direct = [];
|
|
29106
|
-
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29107
|
-
return { draped, direct };
|
|
29108
|
-
}
|
|
29109
|
-
|
|
29110
|
-
// src/atlas/lamps.ts
|
|
29111
|
-
var LAMP_GLOW_LAYER_ID = "s52-LIGHTS-lamp-glow";
|
|
29112
|
-
var LAMP_CORE_LAYER_ID = "s52-LIGHTS-lamp-core";
|
|
29113
|
-
var LAMP_LAYER_IDS = [LAMP_GLOW_LAYER_ID, LAMP_CORE_LAYER_ID];
|
|
29114
|
-
var RANGE_NM = ["to-number", ["coalesce", ["get", "VALNMR"], ["get", "valnmr"]], 6];
|
|
29115
|
-
var UNSECTORED_POINTS = [
|
|
29116
|
-
"all",
|
|
29117
|
-
["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]],
|
|
29118
|
-
["!", ["has", "SECTR1"]],
|
|
29119
|
-
// …and not lights with a charted rhythm either: those flash, and the
|
|
29120
|
-
// flashing is computed by the app from `SIGSEQ` (light-sigseq.ts). What is
|
|
29121
|
-
// left here is exactly the set that is steady from every bearing.
|
|
29122
|
-
["!", ["has", "SIGSEQ"]]
|
|
29123
|
-
];
|
|
29124
|
-
function buildLampLayers(sourceId, ct) {
|
|
29125
|
-
const colour = lightFillExpression(ct);
|
|
29126
|
-
const common = {
|
|
29127
|
-
type: "circle",
|
|
29128
|
-
source: sourceId,
|
|
29129
|
-
"source-layer": "LIGHTS",
|
|
29130
|
-
filter: UNSECTORED_POINTS,
|
|
29131
|
-
layout: { visibility: "none" }
|
|
29132
|
-
};
|
|
29133
|
-
const metadata = { "chrt:role": "lamp", "s52:objectClass": "LIGHTS" };
|
|
29134
|
-
return [
|
|
29135
|
-
{
|
|
29136
|
-
...common,
|
|
29137
|
-
id: LAMP_GLOW_LAYER_ID,
|
|
29138
|
-
paint: {
|
|
29139
|
-
"circle-color": colour,
|
|
29140
|
-
// 4 nm → 7 px, 12 nm → 12 px, 25 nm → 20 px; capped, because a
|
|
29141
|
-
// primary seacoast light is not a searchlight.
|
|
29142
|
-
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 5, 4, 7, 12, 12, 25, 20],
|
|
29143
|
-
"circle-blur": 1,
|
|
29144
|
-
"circle-opacity": 0.55,
|
|
29145
|
-
// A lamp is a point of light: it does not grow as it comes near, and
|
|
29146
|
-
// it does not lie down with the chart.
|
|
29147
|
-
"circle-pitch-alignment": "viewport",
|
|
29148
|
-
"circle-pitch-scale": "viewport"
|
|
29149
|
-
},
|
|
29150
|
-
metadata: { ...metadata, "chrt:lamp": "glow" }
|
|
29151
|
-
},
|
|
29152
|
-
{
|
|
29153
|
-
...common,
|
|
29154
|
-
id: LAMP_CORE_LAYER_ID,
|
|
29155
|
-
paint: {
|
|
29156
|
-
"circle-color": colour,
|
|
29157
|
-
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 1.5, 12, 2.5, 25, 3.5],
|
|
29158
|
-
"circle-blur": 0.25,
|
|
29159
|
-
"circle-opacity": 1,
|
|
29160
|
-
"circle-pitch-alignment": "viewport",
|
|
29161
|
-
"circle-pitch-scale": "viewport"
|
|
29162
|
-
},
|
|
29163
|
-
metadata: { ...metadata, "chrt:lamp": "core" }
|
|
29964
|
+
source: sourceId,
|
|
29965
|
+
"source-layer": "TUNNEL",
|
|
29966
|
+
filter: GEOM_FILTERS.line,
|
|
29967
|
+
paint: {
|
|
29968
|
+
"line-color": resolveColor("CHGRD", ct),
|
|
29969
|
+
"line-width": 1,
|
|
29970
|
+
"line-dasharray": [2, 2]
|
|
29971
|
+
},
|
|
29972
|
+
metadata: { "s52:objectClass": "TUNNEL", "s52:role": "line" }
|
|
29973
|
+
});
|
|
29164
29974
|
}
|
|
29165
|
-
];
|
|
29166
|
-
}
|
|
29167
|
-
|
|
29168
|
-
// src/atlas/band-priority.ts
|
|
29169
|
-
var BAND_PRIORITY_RANGES = {
|
|
29170
|
-
"1": { minzoom: 0 },
|
|
29171
|
-
"2": { minzoom: 5 },
|
|
29172
|
-
"3": { minzoom: 7 },
|
|
29173
|
-
"4": { minzoom: 9 },
|
|
29174
|
-
"5": { minzoom: 11 },
|
|
29175
|
-
"6": { minzoom: 11 }
|
|
29176
|
-
};
|
|
29177
|
-
var BAND_DATA_MINZOOM = {
|
|
29178
|
-
"1": 0,
|
|
29179
|
-
"2": 5,
|
|
29180
|
-
"3": 7,
|
|
29181
|
-
"4": 9,
|
|
29182
|
-
"5": 11,
|
|
29183
|
-
"6": 13
|
|
29184
|
-
};
|
|
29185
|
-
function dominatedVariantMaxzoom(D) {
|
|
29186
|
-
return Math.max(BAND_PRIORITY_RANGES[D].minzoom, BAND_DATA_MINZOOM[D]);
|
|
29187
|
-
}
|
|
29188
|
-
var DOMINANCE_EXEMPT_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29189
|
-
"LNDARE",
|
|
29190
|
-
"DEPARE",
|
|
29191
|
-
"UNSARE",
|
|
29192
|
-
"LAKARE",
|
|
29193
|
-
"DRGARE",
|
|
29194
|
-
"SEAARE"
|
|
29195
|
-
]);
|
|
29196
|
-
function isExemptOpaqueFill(layer) {
|
|
29197
|
-
if (layer.type !== "fill") return false;
|
|
29198
|
-
for (const cls of DOMINANCE_EXEMPT_FILL_CLASSES) {
|
|
29199
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
29200
|
-
}
|
|
29201
|
-
return false;
|
|
29202
|
-
}
|
|
29203
|
-
var GROUND_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29204
|
-
...DOMINANCE_EXEMPT_FILL_CLASSES,
|
|
29205
|
-
"RIVERS"
|
|
29206
|
-
]);
|
|
29207
|
-
function isGroundFill(layer) {
|
|
29208
|
-
if (layer.type !== "fill") return false;
|
|
29209
|
-
for (const cls of GROUND_FILL_CLASSES) {
|
|
29210
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
29211
|
-
}
|
|
29212
|
-
return false;
|
|
29213
|
-
}
|
|
29214
|
-
var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
29215
|
-
"1",
|
|
29216
|
-
"2",
|
|
29217
|
-
"3"
|
|
29218
|
-
]);
|
|
29219
|
-
function bandVariantMinzoom(layer, B) {
|
|
29220
|
-
const classMinzoom = layer.minzoom ?? 0;
|
|
29221
|
-
if (isGroundFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
29222
|
-
return 0;
|
|
29223
|
-
}
|
|
29224
|
-
if (isGroundFill(layer)) {
|
|
29225
|
-
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
29226
|
-
}
|
|
29227
|
-
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
29228
|
-
}
|
|
29229
|
-
var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29230
|
-
"BUAARE",
|
|
29231
|
-
// 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
|
|
29232
|
-
// Captain of the Port Zone" RESARE exists at bands 2/3 as a generalized
|
|
29233
|
-
// shore-hugging polygon whose point-sampled dominance tag MISSED (the
|
|
29234
|
-
// Providence-BUAARE failure mode), so its magenta dashed boundary drew
|
|
29235
|
-
// OFFSET over the band-5 detail at z11.5 — read in the field as "a
|
|
29236
|
-
// magenta shoreline bleeding down from a less coarse band". The same
|
|
29237
|
-
// tile carried untagged band-2 ADMARE and CTNARE giants in the same
|
|
29238
|
-
// state. All three are area classes whose coarse-band boundaries hug
|
|
29239
|
-
// shorelines and whose fine-band copies are authoritative.
|
|
29240
|
-
"RESARE",
|
|
29241
|
-
"CTNARE",
|
|
29242
|
-
"ADMARE"
|
|
29243
|
-
]);
|
|
29244
|
-
var GENERALISED_AREA_MAX_BAND = 3;
|
|
29245
|
-
function generalisedAreaCeiling(layer, B) {
|
|
29246
|
-
if (layer.type !== "fill" && layer.type !== "line") return null;
|
|
29247
|
-
if (parseInt(B, 10) > GENERALISED_AREA_MAX_BAND) return null;
|
|
29248
|
-
for (const cls of GENERALISED_AREA_FILL_CLASSES) {
|
|
29249
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return BAND_PRIORITY_RANGES["5"].minzoom;
|
|
29250
|
-
}
|
|
29251
|
-
return null;
|
|
29252
|
-
}
|
|
29253
|
-
var BAND_DRAW_ORDER = ["1", "2", "3", "4", "5", "6"];
|
|
29254
|
-
var CHART_BAND_CODES = {
|
|
29255
|
-
Overview: ["1"],
|
|
29256
|
-
General: ["2"],
|
|
29257
|
-
Coastal: ["3"],
|
|
29258
|
-
Approach: ["4"],
|
|
29259
|
-
Harbor: ["5", "6"]
|
|
29260
|
-
};
|
|
29261
|
-
var CHART_CASCADE_CODES = {
|
|
29262
|
-
Overview: ["1"],
|
|
29263
|
-
General: ["1", "2"],
|
|
29264
|
-
Coastal: ["1", "2", "3"],
|
|
29265
|
-
Approach: ["1", "2", "3", "4"],
|
|
29266
|
-
Harbor: ["1", "2", "3", "4", "5", "6"]
|
|
29267
|
-
};
|
|
29268
|
-
var CHRT_STYLE_MODES = [
|
|
29269
|
-
"continuous",
|
|
29270
|
-
...Object.keys(CHART_BAND_CODES).map(
|
|
29271
|
-
(band) => `chart:${band}`
|
|
29272
|
-
)
|
|
29273
|
-
];
|
|
29274
|
-
function normalizeStyleMode(raw) {
|
|
29275
|
-
if (raw === null || raw === void 0 || raw === "") return "continuous";
|
|
29276
|
-
const lower = raw.toLowerCase();
|
|
29277
|
-
return CHRT_STYLE_MODES.find((mode) => mode.toLowerCase() === lower) ?? null;
|
|
29278
|
-
}
|
|
29279
|
-
function styleModeBand(mode) {
|
|
29280
|
-
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
29281
|
-
}
|
|
29282
|
-
function bandFilterExpression(codes) {
|
|
29283
|
-
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
29284
|
-
}
|
|
29285
|
-
var BAND_CODE_ALIASES = {
|
|
29286
|
-
"5": ["7"]
|
|
29287
|
-
};
|
|
29288
|
-
function cellBandCharacters(code) {
|
|
29289
|
-
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
29290
|
-
}
|
|
29291
|
-
function cellBandMatch(cellBandExpr, codes) {
|
|
29292
|
-
const list = Array.isArray(codes) ? codes : [codes];
|
|
29293
|
-
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
29294
|
-
if (chars.length === 1) {
|
|
29295
|
-
return ["==", cellBandExpr, chars[0]];
|
|
29296
29975
|
}
|
|
29297
|
-
return
|
|
29976
|
+
return layers;
|
|
29298
29977
|
}
|
|
29299
|
-
function
|
|
29300
|
-
if (!
|
|
29301
|
-
|
|
29302
|
-
|
|
29978
|
+
function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
|
|
29979
|
+
if (!LABELED_LAYERS.has(objectClass2)) return null;
|
|
29980
|
+
const config = getLabelConfig(objectClass2, {
|
|
29981
|
+
colorTable: ct,
|
|
29982
|
+
safetyContour: labelOpts.safetyContour,
|
|
29983
|
+
twoShadeSoundings: labelOpts.twoShadeSoundings,
|
|
29984
|
+
soundingFontWeight: labelOpts.soundingFontWeight
|
|
29985
|
+
});
|
|
29986
|
+
if (!config) return null;
|
|
29987
|
+
let textField = config.textField;
|
|
29988
|
+
if (objectClass2 === "LIGHTS") {
|
|
29989
|
+
textField = buildLightLabelExpression();
|
|
29303
29990
|
}
|
|
29304
|
-
|
|
29305
|
-
|
|
29306
|
-
|
|
29307
|
-
|
|
29308
|
-
|
|
29309
|
-
["!=", ["get", "_dominated"], true]
|
|
29310
|
-
];
|
|
29311
|
-
function dominatedByMinFilter(D) {
|
|
29312
|
-
const dInt = parseInt(D, 10);
|
|
29313
|
-
if (D === "6") {
|
|
29314
|
-
return [
|
|
29315
|
-
"any",
|
|
29316
|
-
["==", ["get", "_dominated_by_min"], dInt],
|
|
29317
|
-
["==", ["get", "_dominated"], true]
|
|
29318
|
-
];
|
|
29991
|
+
if (typeof textField !== "string" && Array.isArray(textField)) {
|
|
29992
|
+
const flat = JSON.stringify(textField);
|
|
29993
|
+
if (flat.includes("_navAidLabel")) {
|
|
29994
|
+
textField = buildNavAidLabelExpression();
|
|
29995
|
+
}
|
|
29319
29996
|
}
|
|
29320
|
-
|
|
29321
|
-
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
const [e, n] = bbox.ne;
|
|
29325
|
-
return [
|
|
29326
|
-
"within",
|
|
29327
|
-
{
|
|
29328
|
-
type: "Polygon",
|
|
29329
|
-
coordinates: [
|
|
29997
|
+
if (depthUnits === "feet") {
|
|
29998
|
+
if (objectClass2 === "SOUNDG") {
|
|
29999
|
+
textField = [
|
|
30000
|
+
"concat",
|
|
29330
30001
|
[
|
|
29331
|
-
|
|
29332
|
-
[
|
|
29333
|
-
|
|
29334
|
-
|
|
29335
|
-
|
|
29336
|
-
|
|
29337
|
-
|
|
30002
|
+
"round",
|
|
30003
|
+
[
|
|
30004
|
+
"*",
|
|
30005
|
+
[
|
|
30006
|
+
"to-number",
|
|
30007
|
+
["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
|
|
30008
|
+
0
|
|
30009
|
+
],
|
|
30010
|
+
3.28084
|
|
30011
|
+
]
|
|
30012
|
+
],
|
|
30013
|
+
""
|
|
30014
|
+
];
|
|
30015
|
+
} else if (objectClass2 === "DEPCNT") {
|
|
30016
|
+
textField = [
|
|
30017
|
+
"concat",
|
|
30018
|
+
["round", ["*", ["to-number", ["get", "VALDCO"]], 3.28084]],
|
|
30019
|
+
"ft"
|
|
30020
|
+
];
|
|
30021
|
+
} else if (objectClass2 === "UWTROC" || objectClass2 === "OBSTRN" || objectClass2 === "WRECKS") {
|
|
30022
|
+
const valsouFt = [
|
|
30023
|
+
"to-number",
|
|
30024
|
+
["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0],
|
|
30025
|
+
0
|
|
30026
|
+
];
|
|
30027
|
+
textField = [
|
|
30028
|
+
"case",
|
|
30029
|
+
[">", valsouFt, 0],
|
|
30030
|
+
["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
|
|
30031
|
+
""
|
|
30032
|
+
];
|
|
30033
|
+
}
|
|
30034
|
+
}
|
|
30035
|
+
const labelPriority = getLabelPriority(objectClass2);
|
|
30036
|
+
const layer = {
|
|
30037
|
+
id: `s52-${objectClass2}-label`,
|
|
30038
|
+
type: "symbol",
|
|
30039
|
+
source: sourceId,
|
|
30040
|
+
"source-layer": objectClass2,
|
|
30041
|
+
filter: GEOM_FILTERS.symbol,
|
|
30042
|
+
layout: {
|
|
30043
|
+
"text-field": textField,
|
|
30044
|
+
"text-size": config.textSize,
|
|
30045
|
+
"text-font": config.textFont ?? ["Open Sans Regular"],
|
|
30046
|
+
"text-offset": config.textOffset,
|
|
30047
|
+
"text-anchor": "top",
|
|
30048
|
+
"text-allow-overlap": false,
|
|
30049
|
+
"text-optional": true,
|
|
30050
|
+
"text-padding": 4,
|
|
30051
|
+
"symbol-sort-key": labelPriority * 1e3,
|
|
30052
|
+
...config.layout ?? {}
|
|
30053
|
+
},
|
|
30054
|
+
paint: {
|
|
30055
|
+
"text-color": config.textColor,
|
|
30056
|
+
"text-halo-color": config.textHaloColor,
|
|
30057
|
+
"text-halo-width": config.textHaloWidth
|
|
30058
|
+
},
|
|
30059
|
+
metadata: {
|
|
30060
|
+
"s52:objectClass": objectClass2,
|
|
30061
|
+
"chrt:labelPriority": labelPriority
|
|
29338
30062
|
}
|
|
29339
|
-
];
|
|
29340
|
-
}
|
|
29341
|
-
function worldMinusBboxPolygon(bbox) {
|
|
29342
|
-
const [w, s] = bbox.sw;
|
|
29343
|
-
const [e, n] = bbox.ne;
|
|
29344
|
-
return {
|
|
29345
|
-
type: "Polygon",
|
|
29346
|
-
coordinates: [
|
|
29347
|
-
[
|
|
29348
|
-
[-180, -85],
|
|
29349
|
-
[180, -85],
|
|
29350
|
-
[180, 85],
|
|
29351
|
-
[-180, 85],
|
|
29352
|
-
[-180, -85]
|
|
29353
|
-
],
|
|
29354
|
-
// Inner ring (hole) — winding opposite to outer ring per GeoJSON
|
|
29355
|
-
// spec. MapLibre is permissive but we follow strict winding.
|
|
29356
|
-
[
|
|
29357
|
-
[w, s],
|
|
29358
|
-
[w, n],
|
|
29359
|
-
[e, n],
|
|
29360
|
-
[e, s],
|
|
29361
|
-
[w, s]
|
|
29362
|
-
]
|
|
29363
|
-
]
|
|
29364
30063
|
};
|
|
30064
|
+
if (config.minzoom) {
|
|
30065
|
+
layer.minzoom = config.minzoom;
|
|
30066
|
+
}
|
|
30067
|
+
if (objectClass2 === "DEPCNT") {
|
|
30068
|
+
layer.filter = GEOM_FILTERS.line;
|
|
30069
|
+
}
|
|
30070
|
+
if (objectClass2 === "CBLOHD" || objectClass2 === "PIPOHD") {
|
|
30071
|
+
layer.filter = GEOM_FILTERS.line;
|
|
30072
|
+
}
|
|
30073
|
+
if (objectClass2 === "SBDARE") {
|
|
30074
|
+
delete layer.filter;
|
|
30075
|
+
}
|
|
30076
|
+
if (objectClass2 === "MAGVAR") {
|
|
30077
|
+
delete layer.filter;
|
|
30078
|
+
}
|
|
30079
|
+
if (getLabelBehavior(objectClass2) === "orientation-cross-band") {
|
|
30080
|
+
layer.filter = LABEL_ANCHOR_FILTER;
|
|
30081
|
+
}
|
|
30082
|
+
return applyDepthLabelSize(objectClass2, layer, labelOpts.depthLabelSize);
|
|
29365
30083
|
}
|
|
29366
|
-
|
|
29367
|
-
|
|
29368
|
-
|
|
29369
|
-
var UNCHARTED_PATTERN = "chrt:hatch-uncharted";
|
|
29370
|
-
function inlandWaterColor(palette) {
|
|
29371
|
-
return (BASE_THEMES[palette] ?? BASE_THEMES.DAY).inlandWater;
|
|
29372
|
-
}
|
|
29373
|
-
var BASE_THEMES = {
|
|
29374
|
-
DAY: {
|
|
29375
|
-
// ── `earth` is the jurisdiction tan DIMMED (~0.55), not a blue-grey ──
|
|
29376
|
-
// Outside the jurisdiction the map still owes the reader "what is land".
|
|
29377
|
-
// State 3 in the header is "the geography, under a crosshatch", and until
|
|
29378
|
-
// 2026-08-31 that geography was unreadable: earth #2A3439 against sea
|
|
29379
|
-
// #1B2328 measured 1.25:1, so Hispaniola showed charted boxes with tan
|
|
29380
|
-
// land inside and nothing recognisable around them (owner report: "no
|
|
29381
|
-
// land painting down near the DR at some zooms"). Land is warm and sea is
|
|
29382
|
-
// cool because that is what land and sea are; dimming the JURISDICTION
|
|
29383
|
-
// tan rather than inventing a colour says the true thing — this is the
|
|
29384
|
-
// same land, we simply do not carry charts for it. Held against BOTH
|
|
29385
|
-
// neighbours: legible over sea, and never mistakable for the full tan of
|
|
29386
|
-
// a country we do carry. DAY 3.59:1 vs sea, 3.17:1 vs tan.
|
|
29387
|
-
earth: "#807761",
|
|
29388
|
-
water: "#1B2328",
|
|
29389
|
-
river: "#24313A",
|
|
29390
|
-
boundary: "#4A575E",
|
|
29391
|
-
jurisdiction: "#D4C69D",
|
|
29392
|
-
// CHRTland's own tan — one land colour
|
|
29393
|
-
inlandWater: "#B9CFDD",
|
|
29394
|
-
// muted, unmistakably not a charted blue
|
|
29395
|
-
// State furniture. These three were referenced by the layers but missing
|
|
29396
|
-
// from this table (and its type — the long-standing TS2339 trio), so the
|
|
29397
|
-
// lines shipped on MapLibre's DEFAULT black. Now deliberate: a hairline
|
|
29398
|
-
// a shade deeper than the tan, quiet caps, tan halo.
|
|
29399
|
-
stateLine: "#B1A077",
|
|
29400
|
-
stateLabel: "#8E8468",
|
|
29401
|
-
stateHalo: "#D4C69D",
|
|
29402
|
-
// Rough-lake furniture (owner ask 2026-08-28: "the lakes need labels,
|
|
29403
|
-
// and shorelines drawn"): a shoreline a shade deeper than the rough
|
|
29404
|
-
// water, and a name in the water's own voice, haloed by the water.
|
|
29405
|
-
inlandShore: "#8FA9BA",
|
|
29406
|
-
inlandLabel: "#54707F",
|
|
29407
|
-
inlandHalo: "#B9CFDD"
|
|
29408
|
-
},
|
|
29409
|
-
DUSK: {
|
|
29410
|
-
// ── Re-derived 2026-09-01: the basemap sits UNDER the chart ──────────
|
|
29411
|
-
// These were the DAY tones scaled by a constant, never measured against
|
|
29412
|
-
// the DUSK chart — so `jurisdiction` came out at L=0.506 against a chart
|
|
29413
|
-
// whose own land (LANDA #8a7d5e) is L=0.209, i.e. the context layer was
|
|
29414
|
-
// 2.4x brighter than the data layer. Now capped at the chart's own land.
|
|
29415
|
-
// The rest of the block rides the same 0.666 channel factor so every
|
|
29416
|
-
// relationship the palette already chose is preserved, only moved down.
|
|
29417
|
-
// `water` is deliberately NOT scaled: it is the reference the land/sea
|
|
29418
|
-
// floor is measured against.
|
|
29419
|
-
earth: "#544E3E",
|
|
29420
|
-
water: "#161D21",
|
|
29421
|
-
river: "#141B20",
|
|
29422
|
-
boundary: "#293135",
|
|
29423
|
-
jurisdiction: "#867D64",
|
|
29424
|
-
inlandWater: "#62717A",
|
|
29425
|
-
// Label stays DARK here — the halo is still mid-toned, so 3:1 is reachable
|
|
29426
|
-
// downward and a bright label would be a new light source at dusk.
|
|
29427
|
-
stateLine: "#B5A987",
|
|
29428
|
-
stateLabel: "#38342A",
|
|
29429
|
-
stateHalo: "#867D64",
|
|
29430
|
-
inlandShore: "#4D5B67",
|
|
29431
|
-
inlandLabel: "#212629",
|
|
29432
|
-
inlandHalo: "#62717A"
|
|
29433
|
-
},
|
|
29434
|
-
NIGHT: {
|
|
29435
|
-
// ── Re-derived 2026-09-01 — the owner-reported night defect ──────────
|
|
29436
|
-
// `jurisdiction` was L=0.240 against a chart whose own land (LANDA
|
|
29437
|
-
// #3a3020) is L=0.031: the world basemap was 7.7x BRIGHTER than the chart
|
|
29438
|
-
// it sits under, so at z<11 the uncharted interior read as a pale
|
|
29439
|
-
// continent-sized slab on a night chart. Capped at the chart's own land.
|
|
29440
|
-
//
|
|
29441
|
-
// ⚠ `earth` AND `jurisdiction` CONVERGE HERE, and that is forced, not
|
|
29442
|
-
// sloppy. Holding earth 2:1 above the ocean AND 2:1 below the jurisdiction
|
|
29443
|
-
// tan requires jurisdiction >= L 0.174 — 5.6x the chart's own land. At
|
|
29444
|
-
// night the range between the ocean (L 0.006) and the cap (L 0.031) is too
|
|
29445
|
-
// compressed to carry two land tones. COVERAGE IS STILL SAID, by the
|
|
29446
|
-
// `chrt:hatch-nocoverage` crosshatch, which is a stronger signal than tone
|
|
29447
|
-
// and is the one thing that cannot be lost to palette compression.
|
|
29448
|
-
//
|
|
29449
|
-
// Labels invert to light-on-dark: 3:1 downward is unreachable off a halo
|
|
29450
|
-
// this close to black, and CHRTland's own night town labels already sit at
|
|
29451
|
-
// L=0.177 for exactly this reason.
|
|
29452
|
-
earth: "#333026",
|
|
29453
|
-
water: "#10161A",
|
|
29454
|
-
river: "#090D0F",
|
|
29455
|
-
boundary: "#131719",
|
|
29456
|
-
jurisdiction: "#353128",
|
|
29457
|
-
inlandWater: "#232A30",
|
|
29458
|
-
stateLine: "#484236",
|
|
29459
|
-
stateLabel: "#837963",
|
|
29460
|
-
stateHalo: "#353128",
|
|
29461
|
-
inlandShore: "#1C2227",
|
|
29462
|
-
inlandLabel: "#617484",
|
|
29463
|
-
inlandHalo: "#232A30"
|
|
30084
|
+
function getGeomTypesForClass(objectClass2, defaultType) {
|
|
30085
|
+
if (SYMBOL_MAP[objectClass2] && defaultType === "symbol") {
|
|
30086
|
+
return ["symbol"];
|
|
29464
30087
|
}
|
|
29465
|
-
|
|
29466
|
-
|
|
29467
|
-
|
|
29468
|
-
|
|
29469
|
-
|
|
29470
|
-
|
|
29471
|
-
return [
|
|
29472
|
-
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
29477
|
-
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
|
|
29481
|
-
|
|
29482
|
-
}),
|
|
29483
|
-
L({
|
|
29484
|
-
id: "chrtbase-earth",
|
|
29485
|
-
type: "fill",
|
|
29486
|
-
source: src,
|
|
29487
|
-
"source-layer": "base_earth",
|
|
29488
|
-
paint: { "fill-color": T.earth }
|
|
29489
|
-
}),
|
|
29490
|
-
L({
|
|
29491
|
-
id: "chrtbase-water",
|
|
29492
|
-
type: "fill",
|
|
29493
|
-
source: src,
|
|
29494
|
-
"source-layer": "base_water",
|
|
29495
|
-
paint: { "fill-color": T.water }
|
|
29496
|
-
}),
|
|
29497
|
-
L({
|
|
29498
|
-
id: "chrtbase-river",
|
|
29499
|
-
type: "line",
|
|
29500
|
-
source: src,
|
|
29501
|
-
"source-layer": "base_river",
|
|
29502
|
-
minzoom: 9,
|
|
29503
|
-
paint: {
|
|
29504
|
-
"line-color": T.river,
|
|
29505
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.6, 14, 1.6]
|
|
29506
|
-
}
|
|
29507
|
-
}),
|
|
29508
|
-
L({
|
|
29509
|
-
id: "chrtbase-boundary",
|
|
29510
|
-
type: "line",
|
|
29511
|
-
source: src,
|
|
29512
|
-
"source-layer": "base_boundary",
|
|
29513
|
-
paint: { "line-color": T.boundary, "line-width": 0.6, "line-opacity": 0.8 }
|
|
29514
|
-
}),
|
|
29515
|
-
// 2. THE DEFAULT: no coverage, everywhere, as a geometry-free background.
|
|
29516
|
-
// Constant opacity by design — see the header. What kills it is being
|
|
29517
|
-
// painted OVER, never fading.
|
|
29518
|
-
L({
|
|
29519
|
-
id: "chrtbase-nocoverage",
|
|
29520
|
-
type: "background",
|
|
29521
|
-
paint: { "background-pattern": NOCOVERAGE_PATTERN, "background-opacity": 0.55 }
|
|
29522
|
-
}),
|
|
29523
|
-
// 3. The jurisdiction: countries whose charts CHRT carries go tan — the
|
|
29524
|
-
// same tan as CHRTland, so "land in our world" is ONE colour. Opaque,
|
|
29525
|
-
// so it takes the crosshatch off its ground entirely.
|
|
29526
|
-
L({
|
|
29527
|
-
id: "chrtbase-jurisdiction",
|
|
29528
|
-
type: "fill",
|
|
29529
|
-
source: src,
|
|
29530
|
-
"source-layer": "jurisdiction",
|
|
29531
|
-
paint: { "fill-color": T.jurisdiction }
|
|
29532
|
-
}),
|
|
29533
|
-
// 4. States — internal boundaries and names, inside the jurisdiction
|
|
29534
|
-
// only (owner decision 2026-08-28). A hairline a shade deeper than the
|
|
29535
|
-
// tan, and quiet letter-spaced caps that fade as harbour detail takes
|
|
29536
|
-
// over. The one deliberate exception to no-names-on-the-basemap:
|
|
29537
|
-
// ADMIN furniture, nothing else — see the fence test.
|
|
29538
|
-
L({
|
|
29539
|
-
id: "chrtbase-state-boundary",
|
|
29540
|
-
type: "line",
|
|
29541
|
-
source: src,
|
|
29542
|
-
"source-layer": "state_boundary",
|
|
29543
|
-
minzoom: 4,
|
|
29544
|
-
paint: {
|
|
29545
|
-
"line-color": T.stateLine,
|
|
29546
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1],
|
|
29547
|
-
"line-dasharray": [3, 2],
|
|
29548
|
-
"line-opacity": 0.8
|
|
29549
|
-
}
|
|
29550
|
-
}),
|
|
29551
|
-
// 5. Country borders, drawn to be HOISTED above the whole chart by the
|
|
29552
|
-
// generator (owner, 2026-08-31: "where there is charted land, can we
|
|
29553
|
-
// add country borders on top of everything? I want it clear where
|
|
29554
|
-
// there is a boarder."). Sourced from NE boundary_lines_land — land
|
|
29555
|
-
// borders only, never coastline — so riding on top cannot stroke a
|
|
29556
|
-
// coast. Dashed so it reads as an administrative line, not chart
|
|
29557
|
-
// linework.
|
|
29558
|
-
L({
|
|
29559
|
-
id: "chrtbase-country-boundary",
|
|
29560
|
-
type: "line",
|
|
29561
|
-
source: src,
|
|
29562
|
-
"source-layer": "country_boundary",
|
|
29563
|
-
paint: {
|
|
29564
|
-
"line-color": T.boundary,
|
|
29565
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.8, 8, 1.4, 12, 2.2],
|
|
29566
|
-
"line-dasharray": [4, 2],
|
|
29567
|
-
"line-opacity": 0.9
|
|
29568
|
-
}
|
|
29569
|
-
}),
|
|
29570
|
-
L({
|
|
29571
|
-
id: "chrtbase-state-label",
|
|
29572
|
-
type: "symbol",
|
|
29573
|
-
source: src,
|
|
29574
|
-
"source-layer": "state_label",
|
|
29575
|
-
minzoom: 4,
|
|
29576
|
-
maxzoom: 10,
|
|
29577
|
-
layout: {
|
|
29578
|
-
"text-field": ["upcase", ["get", "name"]],
|
|
29579
|
-
"text-font": ["Open Sans Regular"],
|
|
29580
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 4, 9, 8, 12],
|
|
29581
|
-
"text-letter-spacing": 0.25,
|
|
29582
|
-
"text-allow-overlap": false,
|
|
29583
|
-
"text-padding": 8
|
|
29584
|
-
},
|
|
29585
|
-
paint: {
|
|
29586
|
-
"text-color": T.stateLabel,
|
|
29587
|
-
"text-halo-color": T.stateHalo,
|
|
29588
|
-
"text-halo-width": 1.2,
|
|
29589
|
-
"text-opacity": ["interpolate", ["linear"], ["zoom"], 4, 0.7, 8, 0.85, 10, 0.4]
|
|
30088
|
+
if (objectClass2 === "SOUNDG") {
|
|
30089
|
+
return ["circle"];
|
|
30090
|
+
}
|
|
30091
|
+
if (defaultType) {
|
|
30092
|
+
return [defaultType];
|
|
30093
|
+
}
|
|
30094
|
+
return ["circle"];
|
|
30095
|
+
}
|
|
30096
|
+
function getOrderedClasses() {
|
|
30097
|
+
const ordered = [...S52_DRAW_ORDER];
|
|
30098
|
+
for (const key of Object.keys(S52_LOOKUP)) {
|
|
30099
|
+
if (!ordered.includes(key)) {
|
|
30100
|
+
const insertIdx = ordered.indexOf("SOUNDG");
|
|
30101
|
+
if (insertIdx >= 0) {
|
|
30102
|
+
ordered.splice(insertIdx, 0, key);
|
|
30103
|
+
} else {
|
|
30104
|
+
ordered.push(key);
|
|
29590
30105
|
}
|
|
29591
|
-
}
|
|
29592
|
-
|
|
30106
|
+
}
|
|
30107
|
+
}
|
|
30108
|
+
return ordered;
|
|
29593
30109
|
}
|
|
29594
|
-
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
29598
|
-
|
|
29599
|
-
|
|
29600
|
-
|
|
29601
|
-
|
|
30110
|
+
|
|
30111
|
+
// src/atlas/terrain-order.ts
|
|
30112
|
+
var DRAPEABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
30113
|
+
"background",
|
|
30114
|
+
"fill",
|
|
30115
|
+
"line",
|
|
30116
|
+
"raster",
|
|
30117
|
+
"hillshade",
|
|
30118
|
+
"color-relief"
|
|
30119
|
+
]);
|
|
30120
|
+
function isDrapeable(layer) {
|
|
30121
|
+
return DRAPEABLE_LAYER_TYPES.has(layer.type);
|
|
30122
|
+
}
|
|
30123
|
+
function drapedFirst(layers) {
|
|
30124
|
+
const draped = [];
|
|
30125
|
+
const direct = [];
|
|
30126
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
30127
|
+
return [...draped, ...direct];
|
|
30128
|
+
}
|
|
30129
|
+
function terrainStackCount(layers) {
|
|
30130
|
+
let stacks = 0;
|
|
30131
|
+
let prevDraped = false;
|
|
30132
|
+
for (const l of layers) {
|
|
30133
|
+
const d = isDrapeable(l);
|
|
30134
|
+
if (d && !prevDraped) stacks++;
|
|
30135
|
+
prevDraped = d;
|
|
30136
|
+
}
|
|
30137
|
+
return stacks;
|
|
30138
|
+
}
|
|
30139
|
+
function drapeHalves(layers) {
|
|
30140
|
+
const draped = [];
|
|
30141
|
+
const direct = [];
|
|
30142
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
30143
|
+
return { draped, direct };
|
|
30144
|
+
}
|
|
30145
|
+
|
|
30146
|
+
// src/atlas/lamps.ts
|
|
30147
|
+
var LAMP_GLOW_LAYER_ID = "s52-LIGHTS-lamp-glow";
|
|
30148
|
+
var LAMP_CORE_LAYER_ID = "s52-LIGHTS-lamp-core";
|
|
30149
|
+
var LAMP_LAYER_IDS = [LAMP_GLOW_LAYER_ID, LAMP_CORE_LAYER_ID];
|
|
30150
|
+
var RANGE_NM = ["to-number", ["coalesce", ["get", "VALNMR"], ["get", "valnmr"]], 6];
|
|
30151
|
+
var UNSECTORED_POINTS = [
|
|
30152
|
+
"all",
|
|
30153
|
+
["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]],
|
|
30154
|
+
["!", ["has", "SECTR1"]],
|
|
30155
|
+
// …and not lights with a charted rhythm either: those flash, and the
|
|
30156
|
+
// flashing is computed by the app from `SIGSEQ` (light-sigseq.ts). What is
|
|
30157
|
+
// left here is exactly the set that is steady from every bearing.
|
|
30158
|
+
["!", ["has", "SIGSEQ"]]
|
|
30159
|
+
];
|
|
30160
|
+
function buildLampLayers(sourceId, ct) {
|
|
30161
|
+
const colour = lightFillExpression(ct);
|
|
30162
|
+
const common = {
|
|
30163
|
+
type: "circle",
|
|
30164
|
+
source: sourceId,
|
|
30165
|
+
"source-layer": "LIGHTS",
|
|
30166
|
+
filter: UNSECTORED_POINTS,
|
|
30167
|
+
layout: { visibility: "none" }
|
|
30168
|
+
};
|
|
30169
|
+
const metadata = { "chrt:role": "lamp", "s52:objectClass": "LIGHTS" };
|
|
29602
30170
|
return [
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29606
|
-
// name), and an unsplit fill draws BOTH — which means the NE polygon is
|
|
29607
|
-
// painted over the planet one at every zoom. That is not a cosmetic
|
|
29608
|
-
// double-draw: NE 10m carries no interior rings, so its blob has no
|
|
29609
|
-
// ISLANDS in it, and painting it over the planet's holed polygons buries
|
|
29610
|
-
// every island in the lake under "uncharted water". Owner-reported at
|
|
29611
|
-
// Winnipesaukee z11.7 ("there is land (and islands) being covered by the
|
|
29612
|
-
// 'no chart here' boundary"), and measured: rendering the planet tier
|
|
29613
|
-
// alone brings back the whole archipelago — Rattlesnake, Bear, Long
|
|
29614
|
-
// Island, Diamond, the Barndoors — while the NE tier alone is a faceted
|
|
29615
|
-
// blob whose edges bear no relation to the shoreline drawn beneath it.
|
|
29616
|
-
//
|
|
29617
|
-
// So each tier owns its own zoom range, the same handover the shoreline
|
|
29618
|
-
// below uses. Known residue, measured over the whole archive: of the 141
|
|
29619
|
-
// NE lakes we still carry, 138 have planet water beneath them; the
|
|
29620
|
-
// exceptions are two unnamed western-Alaska tundra lakes and a 2e-5 deg²
|
|
29621
|
-
// Lake Michigan sliver, which lose their rough fill above z9 because the
|
|
29622
|
-
// planet has no `water` polygon there at all (checked against the raw
|
|
29623
|
-
// pre-clip NDJSON — not a clip artefact).
|
|
29624
|
-
L({
|
|
29625
|
-
id: "chrtbase-inland-water",
|
|
29626
|
-
type: "fill",
|
|
29627
|
-
source: src,
|
|
29628
|
-
"source-layer": "inland_water",
|
|
29629
|
-
filter: NE_TIER,
|
|
29630
|
-
maxzoom: HANDOVER,
|
|
29631
|
-
paint: { "fill-color": T.inlandWater }
|
|
29632
|
-
}),
|
|
29633
|
-
L({
|
|
29634
|
-
id: "chrtbase-inland-hatch",
|
|
29635
|
-
type: "fill",
|
|
29636
|
-
source: src,
|
|
29637
|
-
"source-layer": "inland_water",
|
|
29638
|
-
filter: NE_TIER,
|
|
29639
|
-
maxzoom: HANDOVER,
|
|
29640
|
-
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
29641
|
-
}),
|
|
29642
|
-
L({
|
|
29643
|
-
id: "chrtbase-inland-water-detail",
|
|
29644
|
-
type: "fill",
|
|
29645
|
-
source: src,
|
|
29646
|
-
"source-layer": "inland_water",
|
|
29647
|
-
filter: PLANET_TIER,
|
|
29648
|
-
minzoom: HANDOVER,
|
|
29649
|
-
paint: { "fill-color": T.inlandWater }
|
|
29650
|
-
}),
|
|
29651
|
-
L({
|
|
29652
|
-
id: "chrtbase-inland-hatch-detail",
|
|
29653
|
-
type: "fill",
|
|
29654
|
-
source: src,
|
|
29655
|
-
"source-layer": "inland_water",
|
|
29656
|
-
filter: PLANET_TIER,
|
|
29657
|
-
minzoom: HANDOVER,
|
|
29658
|
-
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
29659
|
-
}),
|
|
29660
|
-
// The rough shoreline (owner ask 2026-08-28). Drawn ABOVE the hatch so
|
|
29661
|
-
// the edge stays crisp through the texture, and BELOW the charted-water
|
|
29662
|
-
// repaint clones the generator splices in after it — so wherever the
|
|
29663
|
-
// chart repaints its own water, the rough NE10m line yields to the
|
|
29664
|
-
// chart's COALNE instead of double-striking it.
|
|
29665
|
-
//
|
|
29666
|
-
// NE-tier geometry ONLY (`kind` absent): the planet detail tier joins
|
|
29667
|
-
// the same source-layer from z9 as MANY polygons whose chunk-clip seams
|
|
29668
|
-
// rejoin invisibly as FILLS — but a line layer strokes every seam, and
|
|
29669
|
-
// Winnipesaukee rendered as a mesh of internal edges. Planet features
|
|
29670
|
-
// carry `kind` (the INLAND_KINDS filter's own field); NE's carry only
|
|
29671
|
-
// `name`, so the absence of `kind` IS the tier discriminator.
|
|
29672
|
-
L({
|
|
29673
|
-
id: "chrtbase-inland-shore",
|
|
29674
|
-
type: "line",
|
|
29675
|
-
source: src,
|
|
29676
|
-
"source-layer": "inland_water",
|
|
29677
|
-
filter: NE_TIER,
|
|
29678
|
-
// Hands the shoreline over to the planet tier at ITS floor. Both
|
|
29679
|
-
// tiers live in `inland_water` from z9 up (tile-join concatenates the
|
|
29680
|
-
// same layer name), and their FILLS merge invisibly — but two
|
|
29681
|
-
// outlines do not: NE 10m generalises Winnipesaukee's shape, so its
|
|
29682
|
-
// outline ran straight across the middle of the lake the planet had
|
|
29683
|
-
// drawn correctly around. The coarse line is right only while it is
|
|
29684
|
-
// the only shape we have.
|
|
29685
|
-
maxzoom: HANDOVER,
|
|
29686
|
-
paint: {
|
|
29687
|
-
"line-color": T.inlandShore,
|
|
29688
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1.1, 14, 1.6],
|
|
29689
|
-
"line-opacity": 0.9
|
|
29690
|
-
}
|
|
29691
|
-
}),
|
|
29692
|
-
// The PLANET tier's shoreline (owner ask 2026-08-31: "uncharted small
|
|
29693
|
-
// lakes render with no outline"). It reads its own `inland_shore` layer,
|
|
29694
|
-
// NOT `inland_water`, and that indirection is the whole fix: the mesh
|
|
29695
|
-
// that forced the `!has kind` fence above is a CLIP artefact — an MVT
|
|
29696
|
-
// feature arrives already cut to its own z10 tile, so stroking the
|
|
29697
|
-
// polygon strokes every tile boundary it crosses. The build now walks
|
|
29698
|
-
// those rings, drops the axis-aligned runs that sit on the read zoom's
|
|
29699
|
-
// tile grid, and emits what is left as lines (measured on one New
|
|
29700
|
-
// England chunk: 61 long axis-aligned mesh edges → 5, and the 5 survivors
|
|
29701
|
-
// are 0.02–0.5 tiles off the grid, i.e. real straight water edges).
|
|
29702
|
-
//
|
|
29703
|
-
// Thinner and quieter than the NE line: this is finer geometry describing
|
|
29704
|
-
// water nobody has surveyed, and approximate data should look
|
|
29705
|
-
// approximate.
|
|
29706
|
-
L({
|
|
29707
|
-
id: "chrtbase-inland-shore-detail",
|
|
29708
|
-
type: "line",
|
|
29709
|
-
source: src,
|
|
29710
|
-
"source-layer": "inland_shore",
|
|
29711
|
-
minzoom: HANDOVER,
|
|
29712
|
-
paint: {
|
|
29713
|
-
"line-color": T.inlandShore,
|
|
29714
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.5, 12, 0.9, 15, 1.2],
|
|
29715
|
-
"line-opacity": 0.85
|
|
29716
|
-
}
|
|
29717
|
-
}),
|
|
29718
|
-
// The lake's name (owner ask 2026-08-28) — the second scoped exception
|
|
29719
|
-
// to no-names-on-the-basemap, after the states: WATER-BODY names only.
|
|
29720
|
-
// No maxzoom: an uncharted lake keeps its name at every zoom; over
|
|
29721
|
-
// charted lakes the chart's own SEAARE label takes over at detail zooms
|
|
29722
|
-
// and collision culls the echo.
|
|
29723
|
-
//
|
|
29724
|
-
// The archive's `inland_water_label` anchors (v3+). v3 shipped ONE
|
|
29725
|
-
// PointOnSurface anchor per lake, and a big lake's single anchor pans
|
|
29726
|
-
// off-viewport — Winnipesaukee at z11.7 read nameless with its name two
|
|
29727
|
-
// tiles west of the screen (owner-reported). Labelling the polygons
|
|
29728
|
-
// instead is NOT the fix: tippecanoe's coalescing drops the NE polygon
|
|
29729
|
-
// from some z11 tiles (measured: 11/619/747 carries only planet pieces),
|
|
29730
|
-
// so a polygon-sourced label vanishes tile by tile. v4 therefore bakes a
|
|
29731
|
-
// deterministic anchor GRID into big lakes — the paper-chart convention
|
|
29732
|
-
// of repeating a name along a large water body — and this one layer
|
|
29733
|
-
// renders however many anchors are in view; collision culls the rest.
|
|
29734
|
-
// Planet-tier water-body NAMES (owner ask 2026-08-31: NE 10m names only
|
|
29735
|
-
// 113 US lakes). Emitted BELOW the NE layer on purpose — MapLibre places
|
|
29736
|
-
// symbols topmost-layer-first, so the big NE lake wins collision against
|
|
29737
|
-
// the pond beside it, and both lose to the chart's own labels (every ENC
|
|
29738
|
-
// class is emitted after this pair).
|
|
29739
|
-
//
|
|
29740
|
-
// The size threshold is OSM's own `min_zoom`, carried through the build:
|
|
29741
|
-
// Lake Winnipesaukee reads 10, Squam Lake 12, so a pond simply is not in
|
|
29742
|
-
// the filter until the user is close enough for it to be the answer to
|
|
29743
|
-
// "what is that water". `["zoom"]` in a filter evaluates at integer zoom
|
|
29744
|
-
// levels and removes the feature from placement entirely — verified
|
|
29745
|
-
// against maplibre-gl-native rather than assumed, because a text-size-0
|
|
29746
|
-
// gate would still claim a collision slot.
|
|
29747
|
-
//
|
|
29748
|
-
// The tiers are separate LAYERS, not a filtered split of one — see the
|
|
29749
|
-
// builder for why: an archive that a not-yet-deployed style would
|
|
29750
|
-
// mis-render cannot be promoted first, and this one has to be.
|
|
29751
|
-
L({
|
|
29752
|
-
id: "chrtbase-inland-label-detail",
|
|
29753
|
-
type: "symbol",
|
|
29754
|
-
source: src,
|
|
29755
|
-
"source-layer": "inland_water_label_detail",
|
|
29756
|
-
minzoom: HANDOVER,
|
|
29757
|
-
filter: [">=", ["zoom"], ["get", "min_zoom"]],
|
|
29758
|
-
layout: {
|
|
29759
|
-
"text-field": ["get", "name"],
|
|
29760
|
-
"text-font": ["Open Sans Regular"],
|
|
29761
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 9, 9, 12, 10.5, 15, 11],
|
|
29762
|
-
"text-max-width": 8,
|
|
29763
|
-
"text-allow-overlap": false,
|
|
29764
|
-
"text-padding": 6,
|
|
29765
|
-
// Bigger water first within the tier — min_zoom IS the size rank.
|
|
29766
|
-
"symbol-sort-key": ["get", "min_zoom"]
|
|
29767
|
-
},
|
|
30171
|
+
{
|
|
30172
|
+
...common,
|
|
30173
|
+
id: LAMP_GLOW_LAYER_ID,
|
|
29768
30174
|
paint: {
|
|
29769
|
-
"
|
|
29770
|
-
|
|
29771
|
-
|
|
29772
|
-
"
|
|
29773
|
-
|
|
29774
|
-
|
|
29775
|
-
|
|
29776
|
-
|
|
29777
|
-
|
|
29778
|
-
|
|
29779
|
-
"source-layer": "inland_water_label",
|
|
29780
|
-
minzoom: 5,
|
|
29781
|
-
layout: {
|
|
29782
|
-
"text-field": ["get", "name"],
|
|
29783
|
-
"text-font": ["Open Sans Regular"],
|
|
29784
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 5, 9.5, 10, 12],
|
|
29785
|
-
"text-max-width": 7,
|
|
29786
|
-
"text-allow-overlap": false,
|
|
29787
|
-
// Zoom-scaled spacing for the anchor GRID: at z8.9 the 0.08-deg
|
|
29788
|
-
// anchors sit ~54 px apart and a flat padding let six copies of
|
|
29789
|
-
// "Lake Ontario" crowd one screen (owner-reported 2026-08-30).
|
|
29790
|
-
// Wide collision boxes at low zoom thin the grid to the repeat-
|
|
29791
|
-
// by-convention cadence; by z12 the anchors are ~850 px apart and
|
|
29792
|
-
// the padding relaxes to normal.
|
|
29793
|
-
"text-padding": ["interpolate", ["linear"], ["zoom"], 5, 80, 9, 64, 12, 16]
|
|
30175
|
+
"circle-color": colour,
|
|
30176
|
+
// 4 nm → 7 px, 12 nm → 12 px, 25 nm → 20 px; capped, because a
|
|
30177
|
+
// primary seacoast light is not a searchlight.
|
|
30178
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 5, 4, 7, 12, 12, 25, 20],
|
|
30179
|
+
"circle-blur": 1,
|
|
30180
|
+
"circle-opacity": 0.55,
|
|
30181
|
+
// A lamp is a point of light: it does not grow as it comes near, and
|
|
30182
|
+
// it does not lie down with the chart.
|
|
30183
|
+
"circle-pitch-alignment": "viewport",
|
|
30184
|
+
"circle-pitch-scale": "viewport"
|
|
29794
30185
|
},
|
|
30186
|
+
metadata: { ...metadata, "chrt:lamp": "glow" }
|
|
30187
|
+
},
|
|
30188
|
+
{
|
|
30189
|
+
...common,
|
|
30190
|
+
id: LAMP_CORE_LAYER_ID,
|
|
29795
30191
|
paint: {
|
|
29796
|
-
"
|
|
29797
|
-
"
|
|
29798
|
-
"
|
|
29799
|
-
|
|
29800
|
-
|
|
30192
|
+
"circle-color": colour,
|
|
30193
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 1.5, 12, 2.5, 25, 3.5],
|
|
30194
|
+
"circle-blur": 0.25,
|
|
30195
|
+
"circle-opacity": 1,
|
|
30196
|
+
"circle-pitch-alignment": "viewport",
|
|
30197
|
+
"circle-pitch-scale": "viewport"
|
|
30198
|
+
},
|
|
30199
|
+
metadata: { ...metadata, "chrt:lamp": "core" }
|
|
30200
|
+
}
|
|
30201
|
+
];
|
|
30202
|
+
}
|
|
30203
|
+
|
|
30204
|
+
// src/atlas/band-priority.ts
|
|
30205
|
+
var BAND_PRIORITY_RANGES = {
|
|
30206
|
+
"1": { minzoom: 0 },
|
|
30207
|
+
"2": { minzoom: 5 },
|
|
30208
|
+
"3": { minzoom: 7 },
|
|
30209
|
+
"4": { minzoom: 9 },
|
|
30210
|
+
"5": { minzoom: 11 },
|
|
30211
|
+
"6": { minzoom: 11 }
|
|
30212
|
+
};
|
|
30213
|
+
var BAND_DATA_MINZOOM = {
|
|
30214
|
+
"1": 0,
|
|
30215
|
+
"2": 5,
|
|
30216
|
+
"3": 7,
|
|
30217
|
+
"4": 9,
|
|
30218
|
+
"5": 11,
|
|
30219
|
+
"6": 13
|
|
30220
|
+
};
|
|
30221
|
+
function dominatedVariantMaxzoom(D) {
|
|
30222
|
+
return Math.max(BAND_PRIORITY_RANGES[D].minzoom, BAND_DATA_MINZOOM[D]);
|
|
30223
|
+
}
|
|
30224
|
+
var DOMINANCE_EXEMPT_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30225
|
+
"LNDARE",
|
|
30226
|
+
"DEPARE",
|
|
30227
|
+
"UNSARE",
|
|
30228
|
+
"LAKARE",
|
|
30229
|
+
"DRGARE",
|
|
30230
|
+
"SEAARE"
|
|
30231
|
+
]);
|
|
30232
|
+
function isExemptOpaqueFill(layer) {
|
|
30233
|
+
if (layer.type !== "fill") return false;
|
|
30234
|
+
for (const cls of DOMINANCE_EXEMPT_FILL_CLASSES) {
|
|
30235
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
30236
|
+
}
|
|
30237
|
+
return false;
|
|
30238
|
+
}
|
|
30239
|
+
var GROUND_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30240
|
+
...DOMINANCE_EXEMPT_FILL_CLASSES,
|
|
30241
|
+
"RIVERS"
|
|
30242
|
+
]);
|
|
30243
|
+
function isGroundFill(layer) {
|
|
30244
|
+
if (layer.type !== "fill") return false;
|
|
30245
|
+
for (const cls of GROUND_FILL_CLASSES) {
|
|
30246
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
30247
|
+
}
|
|
30248
|
+
return false;
|
|
30249
|
+
}
|
|
30250
|
+
var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
30251
|
+
"1",
|
|
30252
|
+
"2",
|
|
30253
|
+
"3"
|
|
30254
|
+
]);
|
|
30255
|
+
function bandVariantMinzoom(layer, B) {
|
|
30256
|
+
const classMinzoom = layer.minzoom ?? 0;
|
|
30257
|
+
if (isGroundFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
30258
|
+
return 0;
|
|
30259
|
+
}
|
|
30260
|
+
if (isGroundFill(layer)) {
|
|
30261
|
+
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
30262
|
+
}
|
|
30263
|
+
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
30264
|
+
}
|
|
30265
|
+
var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30266
|
+
"BUAARE",
|
|
30267
|
+
// 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
|
|
30268
|
+
// Captain of the Port Zone" RESARE exists at bands 2/3 as a generalized
|
|
30269
|
+
// shore-hugging polygon whose point-sampled dominance tag MISSED (the
|
|
30270
|
+
// Providence-BUAARE failure mode), so its magenta dashed boundary drew
|
|
30271
|
+
// OFFSET over the band-5 detail at z11.5 — read in the field as "a
|
|
30272
|
+
// magenta shoreline bleeding down from a less coarse band". The same
|
|
30273
|
+
// tile carried untagged band-2 ADMARE and CTNARE giants in the same
|
|
30274
|
+
// state. All three are area classes whose coarse-band boundaries hug
|
|
30275
|
+
// shorelines and whose fine-band copies are authoritative.
|
|
30276
|
+
"RESARE",
|
|
30277
|
+
"CTNARE",
|
|
30278
|
+
"ADMARE"
|
|
30279
|
+
]);
|
|
30280
|
+
var GENERALISED_AREA_MAX_BAND = 3;
|
|
30281
|
+
function generalisedAreaCeiling(layer, B) {
|
|
30282
|
+
if (layer.type !== "fill" && layer.type !== "line") return null;
|
|
30283
|
+
if (parseInt(B, 10) > GENERALISED_AREA_MAX_BAND) return null;
|
|
30284
|
+
for (const cls of GENERALISED_AREA_FILL_CLASSES) {
|
|
30285
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return BAND_PRIORITY_RANGES["5"].minzoom;
|
|
30286
|
+
}
|
|
30287
|
+
return null;
|
|
30288
|
+
}
|
|
30289
|
+
var BAND_DRAW_ORDER = ["1", "2", "3", "4", "5", "6"];
|
|
30290
|
+
var CHART_BAND_CODES = {
|
|
30291
|
+
Overview: ["1"],
|
|
30292
|
+
General: ["2"],
|
|
30293
|
+
Coastal: ["3"],
|
|
30294
|
+
Approach: ["4"],
|
|
30295
|
+
Harbor: ["5", "6"]
|
|
30296
|
+
};
|
|
30297
|
+
var CHART_CASCADE_CODES = {
|
|
30298
|
+
Overview: ["1"],
|
|
30299
|
+
General: ["1", "2"],
|
|
30300
|
+
Coastal: ["1", "2", "3"],
|
|
30301
|
+
Approach: ["1", "2", "3", "4"],
|
|
30302
|
+
Harbor: ["1", "2", "3", "4", "5", "6"]
|
|
30303
|
+
};
|
|
30304
|
+
var CHRT_STYLE_MODES = [
|
|
30305
|
+
"continuous",
|
|
30306
|
+
...Object.keys(CHART_BAND_CODES).map(
|
|
30307
|
+
(band) => `chart:${band}`
|
|
30308
|
+
)
|
|
30309
|
+
];
|
|
30310
|
+
function normalizeStyleMode(raw) {
|
|
30311
|
+
if (raw === null || raw === void 0 || raw === "") return "continuous";
|
|
30312
|
+
const lower = raw.toLowerCase();
|
|
30313
|
+
return CHRT_STYLE_MODES.find((mode) => mode.toLowerCase() === lower) ?? null;
|
|
30314
|
+
}
|
|
30315
|
+
function styleModeBand(mode) {
|
|
30316
|
+
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
30317
|
+
}
|
|
30318
|
+
function bandFilterExpression(codes) {
|
|
30319
|
+
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
30320
|
+
}
|
|
30321
|
+
var BAND_CODE_ALIASES = {
|
|
30322
|
+
"5": ["7"]
|
|
30323
|
+
};
|
|
30324
|
+
function cellBandCharacters(code) {
|
|
30325
|
+
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
30326
|
+
}
|
|
30327
|
+
function cellBandMatch(cellBandExpr, codes) {
|
|
30328
|
+
const list = Array.isArray(codes) ? codes : [codes];
|
|
30329
|
+
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
30330
|
+
if (chars.length === 1) {
|
|
30331
|
+
return ["==", cellBandExpr, chars[0]];
|
|
30332
|
+
}
|
|
30333
|
+
return ["in", cellBandExpr, ["literal", chars]];
|
|
30334
|
+
}
|
|
30335
|
+
function combineWithBandFilter(baseFilter, bandFilter) {
|
|
30336
|
+
if (!baseFilter) return bandFilter;
|
|
30337
|
+
if (baseFilter[0] === "all") {
|
|
30338
|
+
return ["all", ...baseFilter.slice(1), bandFilter];
|
|
30339
|
+
}
|
|
30340
|
+
return ["all", baseFilter, bandFilter];
|
|
30341
|
+
}
|
|
30342
|
+
var NOT_DOMINATED_FILTER = [
|
|
30343
|
+
"all",
|
|
30344
|
+
["!", ["has", "_dominated_by_min"]],
|
|
30345
|
+
["!=", ["get", "_dominated"], true]
|
|
30346
|
+
];
|
|
30347
|
+
function dominatedByMinFilter(D) {
|
|
30348
|
+
const dInt = parseInt(D, 10);
|
|
30349
|
+
if (D === "6") {
|
|
30350
|
+
return [
|
|
30351
|
+
"any",
|
|
30352
|
+
["==", ["get", "_dominated_by_min"], dInt],
|
|
30353
|
+
["==", ["get", "_dominated"], true]
|
|
30354
|
+
];
|
|
30355
|
+
}
|
|
30356
|
+
return ["==", ["get", "_dominated_by_min"], dInt];
|
|
30357
|
+
}
|
|
30358
|
+
function withinBboxFilter(bbox) {
|
|
30359
|
+
const [w, s] = bbox.sw;
|
|
30360
|
+
const [e, n] = bbox.ne;
|
|
30361
|
+
return [
|
|
30362
|
+
"within",
|
|
30363
|
+
{
|
|
30364
|
+
type: "Polygon",
|
|
30365
|
+
coordinates: [
|
|
30366
|
+
[
|
|
30367
|
+
[w, s],
|
|
30368
|
+
[e, s],
|
|
30369
|
+
[e, n],
|
|
30370
|
+
[w, n],
|
|
30371
|
+
[w, s]
|
|
30372
|
+
]
|
|
30373
|
+
]
|
|
30374
|
+
}
|
|
29801
30375
|
];
|
|
29802
30376
|
}
|
|
30377
|
+
function worldMinusBboxPolygon(bbox) {
|
|
30378
|
+
const [w, s] = bbox.sw;
|
|
30379
|
+
const [e, n] = bbox.ne;
|
|
30380
|
+
return {
|
|
30381
|
+
type: "Polygon",
|
|
30382
|
+
coordinates: [
|
|
30383
|
+
[
|
|
30384
|
+
[-180, -85],
|
|
30385
|
+
[180, -85],
|
|
30386
|
+
[180, 85],
|
|
30387
|
+
[-180, 85],
|
|
30388
|
+
[-180, -85]
|
|
30389
|
+
],
|
|
30390
|
+
// Inner ring (hole) — winding opposite to outer ring per GeoJSON
|
|
30391
|
+
// spec. MapLibre is permissive but we follow strict winding.
|
|
30392
|
+
[
|
|
30393
|
+
[w, s],
|
|
30394
|
+
[w, n],
|
|
30395
|
+
[e, n],
|
|
30396
|
+
[e, s],
|
|
30397
|
+
[w, s]
|
|
30398
|
+
]
|
|
30399
|
+
]
|
|
30400
|
+
};
|
|
30401
|
+
}
|
|
29803
30402
|
|
|
29804
30403
|
// src/atlas/tile-number.ts
|
|
29805
30404
|
function tileNumber(property, fallback) {
|
|
@@ -29870,6 +30469,57 @@ var LAND_THEMES = {
|
|
|
29870
30469
|
// skipper is navigating by. Left at the shipped value rather than
|
|
29871
30470
|
// lowered, because lowering it is a separate decision from this one.
|
|
29872
30471
|
exaggeration: 0.45
|
|
30472
|
+
},
|
|
30473
|
+
// ── E-ink: paper land, no relief ──────────────────────────────────────
|
|
30474
|
+
// Hillshade is a continuous grey ramp — exactly what an e-ink panel cannot
|
|
30475
|
+
// show (and a moving one costs a refresh), so both e-ink palettes switch it
|
|
30476
|
+
// off (`exaggeration: 0`). Land is the chart's own LANDA in that table, so
|
|
30477
|
+
// charted and CHRTland land are one tone; labels are black on paper.
|
|
30478
|
+
EINK_COLOR: {
|
|
30479
|
+
base: "#f2e2b0",
|
|
30480
|
+
cover: { wood: "#d6e0b0", wetland: "#d0dccc", urban: "#e0dacb", sand: "#f2e2b0" },
|
|
30481
|
+
contour: "#a08a5a",
|
|
30482
|
+
contourIndex: "#6b5a3e",
|
|
30483
|
+
road: "#d8c08a",
|
|
30484
|
+
roadMajor: "#c49a4a",
|
|
30485
|
+
label: "#000000",
|
|
30486
|
+
halo: "#ffffff",
|
|
30487
|
+
shadow: "#6b5a3e",
|
|
30488
|
+
highlight: "#ffffff",
|
|
30489
|
+
fade: "#ffffff",
|
|
30490
|
+
exaggeration: 0
|
|
30491
|
+
},
|
|
30492
|
+
// Mono: one land tone (MID). Land cover is not drawn as tone — four greys
|
|
30493
|
+
// cannot spare one for "wood" — so every cover class is the land tone.
|
|
30494
|
+
EINK_MONO: {
|
|
30495
|
+
base: "#aaaaaa",
|
|
30496
|
+
cover: { wood: "#aaaaaa", wetland: "#aaaaaa", urban: "#aaaaaa", sand: "#aaaaaa" },
|
|
30497
|
+
contour: "#555555",
|
|
30498
|
+
contourIndex: "#555555",
|
|
30499
|
+
road: "#ffffff",
|
|
30500
|
+
roadMajor: "#ffffff",
|
|
30501
|
+
label: "#000000",
|
|
30502
|
+
halo: "#ffffff",
|
|
30503
|
+
shadow: "#555555",
|
|
30504
|
+
highlight: "#ffffff",
|
|
30505
|
+
fade: "#ffffff",
|
|
30506
|
+
exaggeration: 0
|
|
30507
|
+
},
|
|
30508
|
+
// Night: land is the DIM grey (the chart's EINK_NIGHT LANDA), drawn on in
|
|
30509
|
+
// black so the land adds no lit lines of its own; labels the night line grey.
|
|
30510
|
+
EINK_NIGHT: {
|
|
30511
|
+
base: "#555555",
|
|
30512
|
+
cover: { wood: "#555555", wetland: "#555555", urban: "#555555", sand: "#555555" },
|
|
30513
|
+
contour: "#000000",
|
|
30514
|
+
contourIndex: "#000000",
|
|
30515
|
+
road: "#000000",
|
|
30516
|
+
roadMajor: "#000000",
|
|
30517
|
+
label: "#aaaaaa",
|
|
30518
|
+
halo: "#000000",
|
|
30519
|
+
shadow: "#000000",
|
|
30520
|
+
highlight: "#555555",
|
|
30521
|
+
fade: "#000000",
|
|
30522
|
+
exaggeration: 0
|
|
29873
30523
|
}
|
|
29874
30524
|
};
|
|
29875
30525
|
var FITS = ["<=", tileNumber("min_z", 0), ["zoom"]];
|
|
@@ -30252,7 +30902,10 @@ var META_LAYERS = /* @__PURE__ */ new Set([
|
|
|
30252
30902
|
var PALETTE_NAMES = {
|
|
30253
30903
|
DAY: "DAY_BRIGHT",
|
|
30254
30904
|
DUSK: "DUSK",
|
|
30255
|
-
NIGHT: "NIGHT"
|
|
30905
|
+
NIGHT: "NIGHT",
|
|
30906
|
+
EINK_COLOR: "EINK_COLOR",
|
|
30907
|
+
EINK_MONO: "EINK_MONO",
|
|
30908
|
+
EINK_NIGHT: "EINK_NIGHT"
|
|
30256
30909
|
};
|
|
30257
30910
|
var SYMBOL_SET_LABELS = {
|
|
30258
30911
|
s52: "S-52",
|
|
@@ -30364,7 +31017,7 @@ var ORIENTATION_OPACITY_SCALE = {
|
|
|
30364
31017
|
};
|
|
30365
31018
|
var ORIENTATION_OPACITY_MAX = 0.92;
|
|
30366
31019
|
var ORIENTATION_BOOST_MINZOOM = 11;
|
|
30367
|
-
function fanOrientationLabelByCellBand(label, bandFan,
|
|
31020
|
+
function fanOrientationLabelByCellBand(label, bandFan, objectClass2) {
|
|
30368
31021
|
const cellBandExpr = [
|
|
30369
31022
|
"slice",
|
|
30370
31023
|
["coalesce", ["get", "_cell_id"], "??9???"],
|
|
@@ -30372,7 +31025,7 @@ function fanOrientationLabelByCellBand(label, bandFan, objectClass) {
|
|
|
30372
31025
|
3
|
|
30373
31026
|
];
|
|
30374
31027
|
const variants = [];
|
|
30375
|
-
const scale =
|
|
31028
|
+
const scale = objectClass2 ? ORIENTATION_OPACITY_SCALE[objectClass2] ?? 1 : 1;
|
|
30376
31029
|
for (const code of BAND_DRAW_ORDER) {
|
|
30377
31030
|
const range = BAND_PRIORITY_RANGES[code];
|
|
30378
31031
|
const curve = CELL_BAND_OPACITY_CURVE[code];
|
|
@@ -30571,17 +31224,18 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30571
31224
|
const bandFan = opts.bandFan ?? "collapsed";
|
|
30572
31225
|
const lockedBounds = opts.chartMode?.lockedBounds;
|
|
30573
31226
|
const bundles = [];
|
|
30574
|
-
for (const
|
|
30575
|
-
if (META_LAYERS.has(
|
|
31227
|
+
for (const objectClass2 of orderedClasses) {
|
|
31228
|
+
if (META_LAYERS.has(objectClass2)) continue;
|
|
30576
31229
|
bundles.push({
|
|
30577
|
-
objectClass,
|
|
30578
|
-
featureLayers: buildLayersForClass(
|
|
31230
|
+
objectClass: objectClass2,
|
|
31231
|
+
featureLayers: buildLayersForClass(objectClass2, palette, sourceId, {
|
|
30579
31232
|
safetyContour: opts.safetyContour
|
|
30580
31233
|
}),
|
|
30581
|
-
label: buildLabelLayer2(
|
|
31234
|
+
label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
|
|
30582
31235
|
safetyContour: opts.safetyContour,
|
|
30583
31236
|
twoShadeSoundings: opts.twoShadeSoundings,
|
|
30584
|
-
soundingFontWeight: opts.soundingFontWeight
|
|
31237
|
+
soundingFontWeight: opts.soundingFontWeight,
|
|
31238
|
+
depthLabelSize: opts.depthLabelSize
|
|
30585
31239
|
})
|
|
30586
31240
|
});
|
|
30587
31241
|
}
|
|
@@ -30722,17 +31376,17 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30722
31376
|
if (opts.labels === "placename") {
|
|
30723
31377
|
labelLayers.push(...buildPlaceNameLayers(sourceId, palette));
|
|
30724
31378
|
} else {
|
|
30725
|
-
for (const { objectClass, label } of bundles) {
|
|
31379
|
+
for (const { objectClass: objectClass2, label } of bundles) {
|
|
30726
31380
|
if (!label) continue;
|
|
30727
|
-
if (getLabelBehavior(
|
|
30728
|
-
if (
|
|
31381
|
+
if (getLabelBehavior(objectClass2) === "orientation-cross-band") {
|
|
31382
|
+
if (objectClass2 === "LNDARE" || objectClass2 === "SEAARE") {
|
|
30729
31383
|
const Z = ISLAND_LABEL_HOIST_MINZOOM;
|
|
30730
|
-
const { hoist, watermark } =
|
|
30731
|
-
fanOrientationLabelByCellBand(label, bandFan,
|
|
31384
|
+
const { hoist, watermark } = objectClass2 === "LNDARE" ? splitOrientationFan(
|
|
31385
|
+
fanOrientationLabelByCellBand(label, bandFan, objectClass2),
|
|
30732
31386
|
[">=", ["zoom"], Z],
|
|
30733
31387
|
["<", ["zoom"], Z]
|
|
30734
31388
|
) : splitOrientationFan(
|
|
30735
|
-
fanOrientationLabelByCellBand(label, bandFan,
|
|
31389
|
+
fanOrientationLabelByCellBand(label, bandFan, objectClass2),
|
|
30736
31390
|
["<=", buildSeaAreaTierExpression(), MAJOR_WATER_TIER_MAX],
|
|
30737
31391
|
[">", buildSeaAreaTierExpression(), MAJOR_WATER_TIER_MAX],
|
|
30738
31392
|
{ "text-padding": MAJOR_WATER_TEXT_PADDING }
|
|
@@ -30741,7 +31395,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30741
31395
|
labelLayers.push(...watermark);
|
|
30742
31396
|
continue;
|
|
30743
31397
|
}
|
|
30744
|
-
labelLayers.push(...fanOrientationLabelByCellBand(label, bandFan,
|
|
31398
|
+
labelLayers.push(...fanOrientationLabelByCellBand(label, bandFan, objectClass2));
|
|
30745
31399
|
}
|
|
30746
31400
|
}
|
|
30747
31401
|
}
|
|
@@ -31005,10 +31659,8 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
31005
31659
|
glyphs: `${glyphsBase}/{fontstack}/{range}.pbf`,
|
|
31006
31660
|
layers: finalLayers
|
|
31007
31661
|
};
|
|
31008
|
-
|
|
31009
|
-
|
|
31010
|
-
if (symbolSet === "raster") return applyRasterSymbolSet(style);
|
|
31011
|
-
return style;
|
|
31662
|
+
const familyStyle = symbolSet === "s101" ? applyS101SymbolSet(style) : symbolSet === "chrt" ? applyChrtSymbolSet(style, palette) : symbolSet === "raster" ? applyRasterSymbolSet(style) : style;
|
|
31663
|
+
return applyEinkPortrayal(familyStyle, palette, { safetyContour: opts.safetyContour });
|
|
31012
31664
|
}
|
|
31013
31665
|
function generateStyleJSON(palette = "DAY", opts = {}) {
|
|
31014
31666
|
return JSON.stringify(generateStyle(palette, opts), null, 2);
|
|
@@ -31152,7 +31804,7 @@ var DEFAULT_CUSTOM_STYLE_CONFIG = {
|
|
|
31152
31804
|
water_body_settings: DEFAULT_WATER_BODY_SETTINGS,
|
|
31153
31805
|
visible_layers: null
|
|
31154
31806
|
};
|
|
31155
|
-
var COLOR_TABLE_NAMES = /* @__PURE__ */ new Set(["DAY", "DUSK", "NIGHT"]);
|
|
31807
|
+
var COLOR_TABLE_NAMES = /* @__PURE__ */ new Set(["DAY", "DUSK", "NIGHT", "EINK_COLOR", "EINK_MONO", "EINK_NIGHT"]);
|
|
31156
31808
|
var DISPLAY_MODES = /* @__PURE__ */ new Set(["BASE", "STANDARD", "ALL"]);
|
|
31157
31809
|
var LAYER_NAME_RE = /^[A-Za-z0-9_$]{2,16}$/;
|
|
31158
31810
|
var MAX_VISIBLE_LAYERS = 512;
|
|
@@ -31259,12 +31911,12 @@ function sanitizeCustomStyleConfig(raw) {
|
|
|
31259
31911
|
}
|
|
31260
31912
|
function normalizeTheme(raw) {
|
|
31261
31913
|
if (!raw) return null;
|
|
31262
|
-
const upper = raw.trim().toUpperCase();
|
|
31914
|
+
const upper = raw.trim().toUpperCase().replace(/-/g, "_");
|
|
31263
31915
|
return COLOR_TABLE_NAMES.has(upper) ? upper : null;
|
|
31264
31916
|
}
|
|
31265
31917
|
function resolveCustomTheme(config, requested) {
|
|
31266
31918
|
if (!requested) return { palette: config.default_color_table, themeFellBack: false };
|
|
31267
|
-
if (config.enabled_color_tables.includes(requested)) {
|
|
31919
|
+
if (isEinkColorTable(requested) || config.enabled_color_tables.includes(requested)) {
|
|
31268
31920
|
return { palette: requested, themeFellBack: false };
|
|
31269
31921
|
}
|
|
31270
31922
|
return { palette: config.default_color_table, themeFellBack: true };
|
|
@@ -31927,6 +32579,7 @@ export {
|
|
|
31927
32579
|
ChrtError,
|
|
31928
32580
|
DEFAULT_CLICK_TOLERANCE,
|
|
31929
32581
|
DEFAULT_CUSTOM_STYLE_CONFIG,
|
|
32582
|
+
DEFAULT_DEPTH_LABEL_SIZE,
|
|
31930
32583
|
DEFAULT_INSPECT_CLASS_RANK,
|
|
31931
32584
|
DEFAULT_LABEL_SETTINGS,
|
|
31932
32585
|
DEFAULT_NAV_AID_SETTINGS,
|
|
@@ -31938,7 +32591,14 @@ export {
|
|
|
31938
32591
|
DEFAULT_WATER_BODY_SETTINGS,
|
|
31939
32592
|
DEPARE_FILL_BRANCH_TOKENS,
|
|
31940
32593
|
DEPARE_FILL_FALLBACK_TOKEN,
|
|
32594
|
+
DEPTH_LABEL_CLASSES,
|
|
32595
|
+
DEPTH_LABEL_SIZES,
|
|
31941
32596
|
DRAPEABLE_LAYER_TYPES,
|
|
32597
|
+
EINK_COLOR_TABLES,
|
|
32598
|
+
EINK_DANGER_PATTERN,
|
|
32599
|
+
EINK_DOTS_PATTERN,
|
|
32600
|
+
EINK_HATCH_PATTERN,
|
|
32601
|
+
EINK_MONO_GREYS,
|
|
31942
32602
|
INSPECT_CLASS_RANK,
|
|
31943
32603
|
INSPECT_RANK_DEVIATIONS,
|
|
31944
32604
|
InteractClient,
|
|
@@ -31954,6 +32614,7 @@ export {
|
|
|
31954
32614
|
LAND_ATTRIBUTION,
|
|
31955
32615
|
LAND_BUILD_MAXZOOM,
|
|
31956
32616
|
LAND_REGION,
|
|
32617
|
+
LARGE_SOUNDING_PADDING_PX,
|
|
31957
32618
|
NAUTICAL_SYMBOLS,
|
|
31958
32619
|
NAV_AID_LAYER_GROUPS,
|
|
31959
32620
|
NAV_AID_QUERY_LAYERS,
|
|
@@ -31990,6 +32651,9 @@ export {
|
|
|
31990
32651
|
S52_DAY,
|
|
31991
32652
|
S52_DRAW_ORDER,
|
|
31992
32653
|
S52_DUSK,
|
|
32654
|
+
S52_EINK_COLOR,
|
|
32655
|
+
S52_EINK_MONO,
|
|
32656
|
+
S52_EINK_NIGHT,
|
|
31993
32657
|
S52_LOOKUP,
|
|
31994
32658
|
S52_NIGHT,
|
|
31995
32659
|
S52_PATTERN_SPRITE_PREFIX,
|
|
@@ -32008,6 +32672,8 @@ export {
|
|
|
32008
32672
|
applyChrtSymbolSet,
|
|
32009
32673
|
applyColorTheme,
|
|
32010
32674
|
applyCustomStyleSettings,
|
|
32675
|
+
applyDepthLabelSize,
|
|
32676
|
+
applyEinkPortrayal,
|
|
32011
32677
|
applyRasterSymbolSet,
|
|
32012
32678
|
applyS101SymbolSet,
|
|
32013
32679
|
atonIconExpression,
|
|
@@ -32087,7 +32753,10 @@ export {
|
|
|
32087
32753
|
iconExpressionForFamily,
|
|
32088
32754
|
isDepthAreaClassLayerId,
|
|
32089
32755
|
isDrapeable,
|
|
32756
|
+
isEinkColorTable,
|
|
32757
|
+
isEinkGreyTable,
|
|
32090
32758
|
isLabelLayerVisible,
|
|
32759
|
+
isMapLive,
|
|
32091
32760
|
isNavAidLayer,
|
|
32092
32761
|
isNavAidLayerVisible,
|
|
32093
32762
|
isSoundingLayerVisible,
|
|
@@ -32105,6 +32774,7 @@ export {
|
|
|
32105
32774
|
normalizeTheme,
|
|
32106
32775
|
opaqueSplitBaseOf,
|
|
32107
32776
|
paginate,
|
|
32777
|
+
parseDepthLabelSize,
|
|
32108
32778
|
parseListCodes,
|
|
32109
32779
|
parseSigseqTerms,
|
|
32110
32780
|
parseSymbolFamily,
|
|
@@ -32124,6 +32794,7 @@ export {
|
|
|
32124
32794
|
resolveTargetClass,
|
|
32125
32795
|
rhythmProperties,
|
|
32126
32796
|
s101ArtworkProvenance,
|
|
32797
|
+
safetyContourValues,
|
|
32127
32798
|
sanitizeCustomStyleConfig,
|
|
32128
32799
|
scaleLinearLight,
|
|
32129
32800
|
scaleSizeValue,
|