@chrtco/sdk 0.10.46 → 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 +22 -0
- package/README.md +29 -6
- package/dist/atlas/browser.cjs +339 -5
- package/dist/atlas/browser.cjs.map +1 -1
- package/dist/atlas/browser.d.cts +1 -1
- package/dist/atlas/browser.d.ts +1 -1
- package/dist/atlas/browser.js +339 -5
- package/dist/atlas/browser.js.map +1 -1
- package/dist/index.cjs +2344 -1748
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1236 -1163
- package/dist/index.d.ts +1236 -1163
- package/dist/index.js +2332 -1748
- 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.cjs
CHANGED
|
@@ -72,6 +72,11 @@ __export(index_exports, {
|
|
|
72
72
|
DEPTH_LABEL_CLASSES: () => DEPTH_LABEL_CLASSES,
|
|
73
73
|
DEPTH_LABEL_SIZES: () => DEPTH_LABEL_SIZES,
|
|
74
74
|
DRAPEABLE_LAYER_TYPES: () => DRAPEABLE_LAYER_TYPES,
|
|
75
|
+
EINK_COLOR_TABLES: () => EINK_COLOR_TABLES,
|
|
76
|
+
EINK_DANGER_PATTERN: () => EINK_DANGER_PATTERN,
|
|
77
|
+
EINK_DOTS_PATTERN: () => EINK_DOTS_PATTERN,
|
|
78
|
+
EINK_HATCH_PATTERN: () => EINK_HATCH_PATTERN,
|
|
79
|
+
EINK_MONO_GREYS: () => EINK_MONO_GREYS,
|
|
75
80
|
INSPECT_CLASS_RANK: () => INSPECT_CLASS_RANK,
|
|
76
81
|
INSPECT_RANK_DEVIATIONS: () => INSPECT_RANK_DEVIATIONS,
|
|
77
82
|
InteractClient: () => InteractClient,
|
|
@@ -124,6 +129,9 @@ __export(index_exports, {
|
|
|
124
129
|
S52_DAY: () => S52_DAY,
|
|
125
130
|
S52_DRAW_ORDER: () => S52_DRAW_ORDER,
|
|
126
131
|
S52_DUSK: () => S52_DUSK,
|
|
132
|
+
S52_EINK_COLOR: () => S52_EINK_COLOR,
|
|
133
|
+
S52_EINK_MONO: () => S52_EINK_MONO,
|
|
134
|
+
S52_EINK_NIGHT: () => S52_EINK_NIGHT,
|
|
127
135
|
S52_LOOKUP: () => S52_LOOKUP,
|
|
128
136
|
S52_NIGHT: () => S52_NIGHT,
|
|
129
137
|
S52_PATTERN_SPRITE_PREFIX: () => S52_PATTERN_SPRITE_PREFIX,
|
|
@@ -143,6 +151,7 @@ __export(index_exports, {
|
|
|
143
151
|
applyColorTheme: () => applyColorTheme,
|
|
144
152
|
applyCustomStyleSettings: () => applyCustomStyleSettings,
|
|
145
153
|
applyDepthLabelSize: () => applyDepthLabelSize,
|
|
154
|
+
applyEinkPortrayal: () => applyEinkPortrayal,
|
|
146
155
|
applyRasterSymbolSet: () => applyRasterSymbolSet,
|
|
147
156
|
applyS101SymbolSet: () => applyS101SymbolSet,
|
|
148
157
|
atonIconExpression: () => atonIconExpression,
|
|
@@ -222,6 +231,8 @@ __export(index_exports, {
|
|
|
222
231
|
iconExpressionForFamily: () => iconExpressionForFamily,
|
|
223
232
|
isDepthAreaClassLayerId: () => isDepthAreaClassLayerId,
|
|
224
233
|
isDrapeable: () => isDrapeable,
|
|
234
|
+
isEinkColorTable: () => isEinkColorTable,
|
|
235
|
+
isEinkGreyTable: () => isEinkGreyTable,
|
|
225
236
|
isLabelLayerVisible: () => isLabelLayerVisible,
|
|
226
237
|
isMapLive: () => isMapLive,
|
|
227
238
|
isNavAidLayer: () => isNavAidLayer,
|
|
@@ -261,6 +272,7 @@ __export(index_exports, {
|
|
|
261
272
|
resolveTargetClass: () => resolveTargetClass,
|
|
262
273
|
rhythmProperties: () => rhythmProperties,
|
|
263
274
|
s101ArtworkProvenance: () => s101ArtworkProvenance,
|
|
275
|
+
safetyContourValues: () => safetyContourValues,
|
|
264
276
|
sanitizeCustomStyleConfig: () => sanitizeCustomStyleConfig,
|
|
265
277
|
scaleLinearLight: () => scaleLinearLight,
|
|
266
278
|
scaleSizeValue: () => scaleSizeValue,
|
|
@@ -2336,6 +2348,19 @@ function s57AbbrTable(acronym) {
|
|
|
2336
2348
|
}
|
|
2337
2349
|
|
|
2338
2350
|
// src/atlas/s52-colors.ts
|
|
2351
|
+
var EINK_COLOR_TABLES = { color: "EINK_COLOR", mono: "EINK_MONO", night: "EINK_NIGHT" };
|
|
2352
|
+
function isEinkColorTable(ct) {
|
|
2353
|
+
return ct === "EINK_COLOR" || ct === "EINK_MONO" || ct === "EINK_NIGHT";
|
|
2354
|
+
}
|
|
2355
|
+
function isEinkGreyTable(ct) {
|
|
2356
|
+
return ct === "EINK_MONO" || ct === "EINK_NIGHT";
|
|
2357
|
+
}
|
|
2358
|
+
var EINK_MONO_GREYS = {
|
|
2359
|
+
INK: "#000000",
|
|
2360
|
+
DARK: "#555555",
|
|
2361
|
+
MID: "#aaaaaa",
|
|
2362
|
+
PAPER: "#ffffff"
|
|
2363
|
+
};
|
|
2339
2364
|
var S52_DAY = {
|
|
2340
2365
|
// Depth
|
|
2341
2366
|
NODTA: "#a4a4a4",
|
|
@@ -2616,11 +2641,235 @@ var S52_NIGHT = {
|
|
|
2616
2641
|
RES02: "#383028",
|
|
2617
2642
|
RES03: "#383028"
|
|
2618
2643
|
};
|
|
2644
|
+
var S52_EINK_COLOR = {
|
|
2645
|
+
NODTA: "#b4b4b4",
|
|
2646
|
+
DEPIT: "#4f8f3a",
|
|
2647
|
+
DEPVS: "#74ade0",
|
|
2648
|
+
DEPMS: "#bad6ef",
|
|
2649
|
+
DEPMD: "#ffffff",
|
|
2650
|
+
DEPDW: "#ffffff",
|
|
2651
|
+
LANDA: "#f2e2b0",
|
|
2652
|
+
LANDF: "#c49a4a",
|
|
2653
|
+
CHBRN: "#8b5a2b",
|
|
2654
|
+
CHGRF: "#9a9a9a",
|
|
2655
|
+
CHGRD: "#444444",
|
|
2656
|
+
CHBLK: "#000000",
|
|
2657
|
+
CSTLN: "#000000",
|
|
2658
|
+
OUTLW: "#000000",
|
|
2659
|
+
BKAJ1: "#333333",
|
|
2660
|
+
TRFCD: "#c800c8",
|
|
2661
|
+
TRFCF: "#c800c8",
|
|
2662
|
+
RESBL: "#0050c8",
|
|
2663
|
+
APTS1: "#c800c8",
|
|
2664
|
+
DNGHL: "#e00000",
|
|
2665
|
+
ISHDG: "#e00000",
|
|
2666
|
+
LITRD: "#e00000",
|
|
2667
|
+
LITGN: "#00a000",
|
|
2668
|
+
LITYW: "#f0c000",
|
|
2669
|
+
LITWT: "#ffffff",
|
|
2670
|
+
LITBL: "#0050c8",
|
|
2671
|
+
LITOR: "#f07000",
|
|
2672
|
+
RADHI: "#00a000",
|
|
2673
|
+
RADLO: "#006000",
|
|
2674
|
+
PLRTE: "#e00000",
|
|
2675
|
+
ARPAT: "#008040",
|
|
2676
|
+
ISDNG: "#c800c8",
|
|
2677
|
+
CHRED: "#e00000",
|
|
2678
|
+
CHGRN: "#00a000",
|
|
2679
|
+
CHYLW: "#f0c000",
|
|
2680
|
+
CHMGD: "#c800c8",
|
|
2681
|
+
CHMGF: "#c800c8",
|
|
2682
|
+
CHWHT: "#ffffff",
|
|
2683
|
+
CHCOR: "#f07000",
|
|
2684
|
+
// Deeper-than-safety soundings: dark grey, the one secondary ink. Shoal
|
|
2685
|
+
// soundings are black — the louder numeral, as in every other table.
|
|
2686
|
+
SNDG1: "#555555",
|
|
2687
|
+
SNDG2: "#000000",
|
|
2688
|
+
DEPSC: "#000000",
|
|
2689
|
+
DEPCN: "#555555",
|
|
2690
|
+
UINFD: "#000000",
|
|
2691
|
+
UINFF: "#555555",
|
|
2692
|
+
UINFR: "#e00000",
|
|
2693
|
+
UINFG: "#00a000",
|
|
2694
|
+
UINFO: "#f07000",
|
|
2695
|
+
UINFB: "#0050c8",
|
|
2696
|
+
UINFM: "#c800c8",
|
|
2697
|
+
UIBCK: "#ffffff",
|
|
2698
|
+
UIBDR: "#555555",
|
|
2699
|
+
UIAFD: "#74ade0",
|
|
2700
|
+
UIAFF: "#c49a4a",
|
|
2701
|
+
PIPLN: "#008000",
|
|
2702
|
+
OUTLL: "#c49a4a",
|
|
2703
|
+
SCLBR: "#f07000",
|
|
2704
|
+
NINFO: "#f07000",
|
|
2705
|
+
ADINF: "#b08800",
|
|
2706
|
+
RESGR: "#555555",
|
|
2707
|
+
SHIPS: "#000000",
|
|
2708
|
+
PSTRK: "#000000",
|
|
2709
|
+
SYTRK: "#555555",
|
|
2710
|
+
APLRT: "#f07000",
|
|
2711
|
+
CURSR: "#f07000",
|
|
2712
|
+
BKAJ2: "#000000",
|
|
2713
|
+
RES01: "#808080",
|
|
2714
|
+
RES02: "#808080",
|
|
2715
|
+
RES03: "#808080"
|
|
2716
|
+
};
|
|
2717
|
+
var M = EINK_MONO_GREYS;
|
|
2718
|
+
var S52_EINK_MONO = {
|
|
2719
|
+
// No data is DARK, never the land tone: a no-data edge in the land tone
|
|
2720
|
+
// reads as a coastline (seen on the Boston render, 2026-10-05).
|
|
2721
|
+
NODTA: M.DARK,
|
|
2722
|
+
DEPIT: M.PAPER,
|
|
2723
|
+
DEPVS: M.PAPER,
|
|
2724
|
+
DEPMS: M.PAPER,
|
|
2725
|
+
DEPMD: M.PAPER,
|
|
2726
|
+
DEPDW: M.PAPER,
|
|
2727
|
+
LANDA: M.MID,
|
|
2728
|
+
LANDF: M.DARK,
|
|
2729
|
+
CHBRN: M.DARK,
|
|
2730
|
+
CHGRF: M.MID,
|
|
2731
|
+
CHGRD: M.DARK,
|
|
2732
|
+
CHBLK: M.INK,
|
|
2733
|
+
CSTLN: M.INK,
|
|
2734
|
+
OUTLW: M.INK,
|
|
2735
|
+
BKAJ1: M.DARK,
|
|
2736
|
+
TRFCD: M.INK,
|
|
2737
|
+
TRFCF: M.INK,
|
|
2738
|
+
RESBL: M.INK,
|
|
2739
|
+
APTS1: M.INK,
|
|
2740
|
+
DNGHL: M.INK,
|
|
2741
|
+
ISHDG: M.INK,
|
|
2742
|
+
LITRD: M.INK,
|
|
2743
|
+
LITGN: M.INK,
|
|
2744
|
+
LITYW: M.INK,
|
|
2745
|
+
LITWT: M.PAPER,
|
|
2746
|
+
LITBL: M.INK,
|
|
2747
|
+
LITOR: M.INK,
|
|
2748
|
+
RADHI: M.INK,
|
|
2749
|
+
RADLO: M.DARK,
|
|
2750
|
+
PLRTE: M.INK,
|
|
2751
|
+
ARPAT: M.INK,
|
|
2752
|
+
ISDNG: M.INK,
|
|
2753
|
+
CHRED: M.DARK,
|
|
2754
|
+
CHGRN: M.MID,
|
|
2755
|
+
CHYLW: M.MID,
|
|
2756
|
+
CHMGD: M.INK,
|
|
2757
|
+
CHMGF: M.INK,
|
|
2758
|
+
CHWHT: M.PAPER,
|
|
2759
|
+
CHCOR: M.MID,
|
|
2760
|
+
SNDG1: M.DARK,
|
|
2761
|
+
SNDG2: M.INK,
|
|
2762
|
+
DEPSC: M.INK,
|
|
2763
|
+
DEPCN: M.DARK,
|
|
2764
|
+
UINFD: M.INK,
|
|
2765
|
+
UINFF: M.DARK,
|
|
2766
|
+
UINFR: M.INK,
|
|
2767
|
+
UINFG: M.INK,
|
|
2768
|
+
UINFO: M.INK,
|
|
2769
|
+
UINFB: M.INK,
|
|
2770
|
+
UINFM: M.INK,
|
|
2771
|
+
UIBCK: M.PAPER,
|
|
2772
|
+
UIBDR: M.DARK,
|
|
2773
|
+
UIAFD: M.MID,
|
|
2774
|
+
UIAFF: M.MID,
|
|
2775
|
+
PIPLN: M.INK,
|
|
2776
|
+
OUTLL: M.DARK,
|
|
2777
|
+
SCLBR: M.INK,
|
|
2778
|
+
NINFO: M.INK,
|
|
2779
|
+
ADINF: M.DARK,
|
|
2780
|
+
RESGR: M.DARK,
|
|
2781
|
+
SHIPS: M.INK,
|
|
2782
|
+
PSTRK: M.INK,
|
|
2783
|
+
SYTRK: M.DARK,
|
|
2784
|
+
APLRT: M.INK,
|
|
2785
|
+
CURSR: M.INK,
|
|
2786
|
+
BKAJ2: M.INK,
|
|
2787
|
+
RES01: M.DARK,
|
|
2788
|
+
RES02: M.DARK,
|
|
2789
|
+
RES03: M.DARK
|
|
2790
|
+
};
|
|
2791
|
+
var N = { GROUND: M.INK, DIM: M.DARK, LINE: M.MID, BRIGHT: M.PAPER };
|
|
2792
|
+
var S52_EINK_NIGHT = {
|
|
2793
|
+
NODTA: N.GROUND,
|
|
2794
|
+
DEPIT: N.GROUND,
|
|
2795
|
+
DEPVS: N.GROUND,
|
|
2796
|
+
DEPMS: N.GROUND,
|
|
2797
|
+
DEPMD: N.GROUND,
|
|
2798
|
+
DEPDW: N.GROUND,
|
|
2799
|
+
LANDA: N.DIM,
|
|
2800
|
+
LANDF: N.LINE,
|
|
2801
|
+
CHBRN: N.LINE,
|
|
2802
|
+
CHGRF: N.DIM,
|
|
2803
|
+
CHGRD: N.LINE,
|
|
2804
|
+
CHBLK: N.LINE,
|
|
2805
|
+
CSTLN: N.LINE,
|
|
2806
|
+
OUTLW: N.LINE,
|
|
2807
|
+
BKAJ1: N.DIM,
|
|
2808
|
+
TRFCD: N.LINE,
|
|
2809
|
+
TRFCF: N.LINE,
|
|
2810
|
+
RESBL: N.LINE,
|
|
2811
|
+
APTS1: N.LINE,
|
|
2812
|
+
DNGHL: N.BRIGHT,
|
|
2813
|
+
ISHDG: N.BRIGHT,
|
|
2814
|
+
LITRD: N.BRIGHT,
|
|
2815
|
+
LITGN: N.BRIGHT,
|
|
2816
|
+
LITYW: N.BRIGHT,
|
|
2817
|
+
LITWT: N.BRIGHT,
|
|
2818
|
+
LITBL: N.BRIGHT,
|
|
2819
|
+
LITOR: N.BRIGHT,
|
|
2820
|
+
RADHI: N.LINE,
|
|
2821
|
+
RADLO: N.DIM,
|
|
2822
|
+
PLRTE: N.LINE,
|
|
2823
|
+
ARPAT: N.LINE,
|
|
2824
|
+
ISDNG: N.BRIGHT,
|
|
2825
|
+
// Mark bodies by tone, as in mono, so they stay apart from each other and
|
|
2826
|
+
// from the black water; shape still says which side.
|
|
2827
|
+
CHRED: N.DIM,
|
|
2828
|
+
CHGRN: N.LINE,
|
|
2829
|
+
CHYLW: N.LINE,
|
|
2830
|
+
CHMGD: N.LINE,
|
|
2831
|
+
CHMGF: N.LINE,
|
|
2832
|
+
CHWHT: N.BRIGHT,
|
|
2833
|
+
CHCOR: N.LINE,
|
|
2834
|
+
SNDG1: N.LINE,
|
|
2835
|
+
SNDG2: N.BRIGHT,
|
|
2836
|
+
DEPSC: N.BRIGHT,
|
|
2837
|
+
DEPCN: N.LINE,
|
|
2838
|
+
UINFD: N.LINE,
|
|
2839
|
+
UINFF: N.DIM,
|
|
2840
|
+
UINFR: N.BRIGHT,
|
|
2841
|
+
UINFG: N.LINE,
|
|
2842
|
+
UINFO: N.LINE,
|
|
2843
|
+
UINFB: N.LINE,
|
|
2844
|
+
UINFM: N.LINE,
|
|
2845
|
+
UIBCK: N.GROUND,
|
|
2846
|
+
UIBDR: N.DIM,
|
|
2847
|
+
UIAFD: N.DIM,
|
|
2848
|
+
UIAFF: N.DIM,
|
|
2849
|
+
PIPLN: N.LINE,
|
|
2850
|
+
OUTLL: N.DIM,
|
|
2851
|
+
SCLBR: N.LINE,
|
|
2852
|
+
NINFO: N.LINE,
|
|
2853
|
+
ADINF: N.DIM,
|
|
2854
|
+
RESGR: N.DIM,
|
|
2855
|
+
SHIPS: N.BRIGHT,
|
|
2856
|
+
PSTRK: N.LINE,
|
|
2857
|
+
SYTRK: N.DIM,
|
|
2858
|
+
APLRT: N.BRIGHT,
|
|
2859
|
+
CURSR: N.BRIGHT,
|
|
2860
|
+
BKAJ2: N.GROUND,
|
|
2861
|
+
RES01: N.DIM,
|
|
2862
|
+
RES02: N.DIM,
|
|
2863
|
+
RES03: N.DIM
|
|
2864
|
+
};
|
|
2619
2865
|
var S52_COLORS = S52_DAY;
|
|
2620
2866
|
var COLOR_TABLES = {
|
|
2621
2867
|
DAY: S52_DAY,
|
|
2622
2868
|
DUSK: S52_DUSK,
|
|
2623
|
-
NIGHT: S52_NIGHT
|
|
2869
|
+
NIGHT: S52_NIGHT,
|
|
2870
|
+
EINK_COLOR: S52_EINK_COLOR,
|
|
2871
|
+
EINK_MONO: S52_EINK_MONO,
|
|
2872
|
+
EINK_NIGHT: S52_EINK_NIGHT
|
|
2624
2873
|
};
|
|
2625
2874
|
function getColorTable(name) {
|
|
2626
2875
|
return COLOR_TABLES[name];
|
|
@@ -5267,6 +5516,254 @@ var InteractClient = class {
|
|
|
5267
5516
|
}
|
|
5268
5517
|
};
|
|
5269
5518
|
|
|
5519
|
+
// src/atlas/chrt-svgs-generated.ts
|
|
5520
|
+
var CHRT_GLYPH_SVGS = {
|
|
5521
|
+
"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>',
|
|
5522
|
+
"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>',
|
|
5523
|
+
"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>',
|
|
5524
|
+
"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>',
|
|
5525
|
+
"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>',
|
|
5526
|
+
"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>',
|
|
5527
|
+
"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>',
|
|
5528
|
+
"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>',
|
|
5529
|
+
"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>',
|
|
5530
|
+
"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>',
|
|
5531
|
+
"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>',
|
|
5532
|
+
"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>',
|
|
5533
|
+
"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>',
|
|
5534
|
+
"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>',
|
|
5535
|
+
"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>',
|
|
5536
|
+
"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>',
|
|
5537
|
+
"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>',
|
|
5538
|
+
"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>',
|
|
5539
|
+
"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>',
|
|
5540
|
+
"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>',
|
|
5541
|
+
"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>',
|
|
5542
|
+
"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>',
|
|
5543
|
+
"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>',
|
|
5544
|
+
"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>',
|
|
5545
|
+
"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>',
|
|
5546
|
+
"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>',
|
|
5547
|
+
"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>',
|
|
5548
|
+
"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>',
|
|
5549
|
+
"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>'
|
|
5550
|
+
};
|
|
5551
|
+
|
|
5552
|
+
// src/atlas/s52-pattern.ts
|
|
5553
|
+
var S52_PATTERN_SPRITE_PREFIX = "AP:";
|
|
5554
|
+
function patternSpriteId(patternName) {
|
|
5555
|
+
return `${S52_PATTERN_SPRITE_PREFIX}${patternName}`;
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
// src/atlas/s52-conditional.ts
|
|
5559
|
+
var DEFAULT_SAFETY_CONTOUR_M = 10;
|
|
5560
|
+
var DEPARE_DEEP_CUT_M = 18.2;
|
|
5561
|
+
var DEPARE_SHALLOW_CUT_M = 2;
|
|
5562
|
+
function effectiveSafetyContour(safetyContourM) {
|
|
5563
|
+
if (!Number.isFinite(safetyContourM)) return DEFAULT_SAFETY_CONTOUR_M;
|
|
5564
|
+
return Math.min(Math.max(safetyContourM, DEPARE_SHALLOW_CUT_M), DEPARE_DEEP_CUT_M);
|
|
5565
|
+
}
|
|
5566
|
+
function depareColor(props, ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5567
|
+
const drval1 = Number(props.DRVAL1 ?? props.drval1 ?? -1);
|
|
5568
|
+
const drval2 = Number(props.DRVAL2 ?? props.drval2 ?? drval1);
|
|
5569
|
+
const safety = effectiveSafetyContour(safetyContourM);
|
|
5570
|
+
if (drval1 < 0 && drval2 <= 0) return resolveColor("DEPIT", ct);
|
|
5571
|
+
if (drval1 < DEPARE_SHALLOW_CUT_M) return resolveColor("DEPVS", ct);
|
|
5572
|
+
if (drval1 < safety) return resolveColor("DEPMS", ct);
|
|
5573
|
+
if (drval1 < DEPARE_DEEP_CUT_M) return resolveColor("DEPMD", ct);
|
|
5574
|
+
return resolveColor("DEPDW", ct);
|
|
5575
|
+
}
|
|
5576
|
+
var DEPARE_FILL_BRANCH_TOKENS = ["DEPIT", "DEPVS", "DEPMS", "DEPMD"];
|
|
5577
|
+
var DEPARE_FILL_FALLBACK_TOKEN = "DEPDW";
|
|
5578
|
+
function depareFillExpression(ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5579
|
+
const safety = effectiveSafetyContour(safetyContourM);
|
|
5580
|
+
const [it, vs, ms, md] = DEPARE_FILL_BRANCH_TOKENS;
|
|
5581
|
+
return [
|
|
5582
|
+
"case",
|
|
5583
|
+
[
|
|
5584
|
+
"all",
|
|
5585
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], 0],
|
|
5586
|
+
["<=", ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0], 0]
|
|
5587
|
+
],
|
|
5588
|
+
resolveColor(it, ct),
|
|
5589
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_SHALLOW_CUT_M],
|
|
5590
|
+
resolveColor(vs, ct),
|
|
5591
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], safety],
|
|
5592
|
+
resolveColor(ms, ct),
|
|
5593
|
+
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_DEEP_CUT_M],
|
|
5594
|
+
resolveColor(md, ct),
|
|
5595
|
+
resolveColor(DEPARE_FILL_FALLBACK_TOKEN, ct)
|
|
5596
|
+
];
|
|
5597
|
+
}
|
|
5598
|
+
function listAttrString(attr) {
|
|
5599
|
+
return ["to-string", ["coalesce", ["get", attr], ["get", attr.toLowerCase()], ""]];
|
|
5600
|
+
}
|
|
5601
|
+
function listAttrCase(attr, cases, fallback) {
|
|
5602
|
+
const attrStr = listAttrString(attr);
|
|
5603
|
+
const expr = ["case"];
|
|
5604
|
+
for (const [code, value] of cases) {
|
|
5605
|
+
expr.push([">=", ["index-of", `"${code}"`, attrStr], 0], value);
|
|
5606
|
+
}
|
|
5607
|
+
for (const [code, value] of cases) {
|
|
5608
|
+
expr.push(["==", attrStr, code], value);
|
|
5609
|
+
}
|
|
5610
|
+
for (const [code, value] of cases) {
|
|
5611
|
+
expr.push(["==", attrStr, `[${code}]`], value);
|
|
5612
|
+
}
|
|
5613
|
+
expr.push(fallback);
|
|
5614
|
+
return expr;
|
|
5615
|
+
}
|
|
5616
|
+
function listAttrHas(attr, codes) {
|
|
5617
|
+
const attrStr = listAttrString(attr);
|
|
5618
|
+
const clauses = [];
|
|
5619
|
+
for (const code of codes) {
|
|
5620
|
+
clauses.push([">=", ["index-of", `"${code}"`, attrStr], 0]);
|
|
5621
|
+
clauses.push(["==", attrStr, code]);
|
|
5622
|
+
clauses.push(["==", attrStr, `[${code}]`]);
|
|
5623
|
+
}
|
|
5624
|
+
return ["any", ...clauses];
|
|
5625
|
+
}
|
|
5626
|
+
function colourCodeCase(cases, fallback) {
|
|
5627
|
+
return listAttrCase("COLOUR", cases, fallback);
|
|
5628
|
+
}
|
|
5629
|
+
function lightColor(props, ct = "DAY") {
|
|
5630
|
+
const raw = props.COLOUR ?? props.colour;
|
|
5631
|
+
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5632
|
+
switch (code) {
|
|
5633
|
+
case 1:
|
|
5634
|
+
return resolveColor("LITWT", ct);
|
|
5635
|
+
case 3:
|
|
5636
|
+
return resolveColor("LITRD", ct);
|
|
5637
|
+
case 4:
|
|
5638
|
+
return resolveColor("LITGN", ct);
|
|
5639
|
+
case 5:
|
|
5640
|
+
return resolveColor("LITBL", ct);
|
|
5641
|
+
case 6:
|
|
5642
|
+
return resolveColor("LITYW", ct);
|
|
5643
|
+
case 11:
|
|
5644
|
+
return resolveColor("LITOR", ct);
|
|
5645
|
+
default:
|
|
5646
|
+
return resolveColor("LITYW", ct);
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
function lightFillExpression(ct = "DAY") {
|
|
5650
|
+
return colourCodeCase(
|
|
5651
|
+
[
|
|
5652
|
+
["3", resolveColor("LITRD", ct)],
|
|
5653
|
+
["4", resolveColor("LITGN", ct)],
|
|
5654
|
+
["6", resolveColor("LITYW", ct)],
|
|
5655
|
+
["11", resolveColor("LITOR", ct)],
|
|
5656
|
+
["5", resolveColor("LITBL", ct)],
|
|
5657
|
+
["1", resolveColor("LITWT", ct)]
|
|
5658
|
+
],
|
|
5659
|
+
resolveColor("LITYW", ct)
|
|
5660
|
+
);
|
|
5661
|
+
}
|
|
5662
|
+
function lateralColor(props, ct = "DAY") {
|
|
5663
|
+
const catlam = Number(props.CATLAM ?? props.catlam ?? 0);
|
|
5664
|
+
if (catlam === 1) return resolveColor("LITRD", ct);
|
|
5665
|
+
if (catlam === 2) return resolveColor("LITGN", ct);
|
|
5666
|
+
return resolveColor("LITYW", ct);
|
|
5667
|
+
}
|
|
5668
|
+
function lateralFillExpression(ct = "DAY") {
|
|
5669
|
+
return [
|
|
5670
|
+
"match",
|
|
5671
|
+
["coalesce", ["get", "CATLAM"], ["get", "catlam"], 0],
|
|
5672
|
+
1,
|
|
5673
|
+
resolveColor("LITRD", ct),
|
|
5674
|
+
2,
|
|
5675
|
+
resolveColor("LITGN", ct),
|
|
5676
|
+
resolveColor("LITYW", ct)
|
|
5677
|
+
];
|
|
5678
|
+
}
|
|
5679
|
+
function hazardColor(props, ct = "DAY") {
|
|
5680
|
+
const watlev = Number(props.WATLEV ?? props.watlev ?? 0);
|
|
5681
|
+
const valsou = props.VALSOU ?? props.valsou;
|
|
5682
|
+
if (watlev === 1 || watlev === 2) return resolveColor("DNGHL", ct);
|
|
5683
|
+
if (valsou != null && Number(valsou) < 2) return resolveColor("DNGHL", ct);
|
|
5684
|
+
return resolveColor("RESBL", ct);
|
|
5685
|
+
}
|
|
5686
|
+
function hazardFillExpression(ct = "DAY") {
|
|
5687
|
+
return [
|
|
5688
|
+
"case",
|
|
5689
|
+
[
|
|
5690
|
+
"any",
|
|
5691
|
+
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 1],
|
|
5692
|
+
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 2]
|
|
5693
|
+
],
|
|
5694
|
+
resolveColor("DNGHL", ct),
|
|
5695
|
+
[
|
|
5696
|
+
"all",
|
|
5697
|
+
["has", "VALSOU"],
|
|
5698
|
+
["<", ["get", "VALSOU"], 2]
|
|
5699
|
+
],
|
|
5700
|
+
resolveColor("DNGHL", ct),
|
|
5701
|
+
resolveColor("RESBL", ct)
|
|
5702
|
+
];
|
|
5703
|
+
}
|
|
5704
|
+
function vegatnPatternExpression() {
|
|
5705
|
+
return [
|
|
5706
|
+
"match",
|
|
5707
|
+
["coalesce", ["get", "CATVEG"], ["get", "catveg"], 0],
|
|
5708
|
+
1,
|
|
5709
|
+
patternSpriteId("VEGATN03"),
|
|
5710
|
+
// grass
|
|
5711
|
+
3,
|
|
5712
|
+
patternSpriteId("VEGATN03"),
|
|
5713
|
+
// bush
|
|
5714
|
+
4,
|
|
5715
|
+
patternSpriteId("VEGATN04"),
|
|
5716
|
+
// deciduous wood
|
|
5717
|
+
5,
|
|
5718
|
+
patternSpriteId("VEGATN04"),
|
|
5719
|
+
// coniferous wood
|
|
5720
|
+
6,
|
|
5721
|
+
patternSpriteId("VEGATN04"),
|
|
5722
|
+
// wood (mixed)
|
|
5723
|
+
7,
|
|
5724
|
+
patternSpriteId("MARSHES1"),
|
|
5725
|
+
// mangroves
|
|
5726
|
+
10,
|
|
5727
|
+
patternSpriteId("VEGATN03"),
|
|
5728
|
+
// scrub
|
|
5729
|
+
11,
|
|
5730
|
+
patternSpriteId("VEGATN04"),
|
|
5731
|
+
// tree
|
|
5732
|
+
12,
|
|
5733
|
+
patternSpriteId("VEGATN04"),
|
|
5734
|
+
// evergreen
|
|
5735
|
+
16,
|
|
5736
|
+
patternSpriteId("MARSHES1"),
|
|
5737
|
+
// swamp
|
|
5738
|
+
17,
|
|
5739
|
+
patternSpriteId("MARSHES1"),
|
|
5740
|
+
// marsh
|
|
5741
|
+
21,
|
|
5742
|
+
patternSpriteId("VEGATN04"),
|
|
5743
|
+
// savannah
|
|
5744
|
+
patternSpriteId("VEGATN03")
|
|
5745
|
+
// default: grass pattern
|
|
5746
|
+
];
|
|
5747
|
+
}
|
|
5748
|
+
function daymarkColor(props, ct = "DAY") {
|
|
5749
|
+
const raw = props.COLOUR ?? props.colour;
|
|
5750
|
+
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
5751
|
+
switch (code) {
|
|
5752
|
+
case 1:
|
|
5753
|
+
return resolveColor("LITWT", ct);
|
|
5754
|
+
case 3:
|
|
5755
|
+
return resolveColor("LITRD", ct);
|
|
5756
|
+
case 4:
|
|
5757
|
+
return resolveColor("LITGN", ct);
|
|
5758
|
+
case 6:
|
|
5759
|
+
return resolveColor("LITYW", ct);
|
|
5760
|
+
case 11:
|
|
5761
|
+
return resolveColor("LITOR", ct);
|
|
5762
|
+
default:
|
|
5763
|
+
return resolveColor("LITGN", ct);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
|
|
5270
5767
|
// src/atlas/palette-light.ts
|
|
5271
5768
|
var HEX6 = /^#?([0-9a-f]{6})$/i;
|
|
5272
5769
|
function channels(hex) {
|
|
@@ -5305,8 +5802,966 @@ function capLuminance(hex, ceiling) {
|
|
|
5305
5802
|
}
|
|
5306
5803
|
var NIGHT_INK = S52_NIGHT.CHBLK;
|
|
5307
5804
|
var NIGHT_MUTED_INK = scaleLinearLight(S52_NIGHT.CHBLK, 0.43);
|
|
5308
|
-
|
|
5309
|
-
|
|
5805
|
+
function einkMonoGrey(hex) {
|
|
5806
|
+
const l = relativeLuminance(hex);
|
|
5807
|
+
const g2 = EINK_MONO_GREYS;
|
|
5808
|
+
const steps = [g2.INK, g2.DARK, g2.MID, g2.PAPER].map((c) => [c, relativeLuminance(c)]);
|
|
5809
|
+
let best = steps[0];
|
|
5810
|
+
for (const step of steps) if (Math.abs(step[1] - l) < Math.abs(best[1] - l)) best = step;
|
|
5811
|
+
return best[0];
|
|
5812
|
+
}
|
|
5813
|
+
function einkNightGrey(hex) {
|
|
5814
|
+
const l = relativeLuminance(hex);
|
|
5815
|
+
const g2 = EINK_MONO_GREYS;
|
|
5816
|
+
const steps = [g2.DARK, g2.MID, g2.PAPER].map((c) => [c, relativeLuminance(c)]);
|
|
5817
|
+
let best = steps[0];
|
|
5818
|
+
for (const step of steps) if (Math.abs(step[1] - l) < Math.abs(best[1] - l)) best = step;
|
|
5819
|
+
return best[0];
|
|
5820
|
+
}
|
|
5821
|
+
|
|
5822
|
+
// src/atlas/chrt-style.ts
|
|
5823
|
+
var CHRT_ICON_PREFIX = "chrt:";
|
|
5824
|
+
var CHRT_SDF_GLYPHS = /* @__PURE__ */ new Set([
|
|
5825
|
+
"buoy-can",
|
|
5826
|
+
"buoy-conical",
|
|
5827
|
+
"buoy-spherical",
|
|
5828
|
+
"buoy-pillar",
|
|
5829
|
+
"buoy-spar",
|
|
5830
|
+
"buoy-barrel",
|
|
5831
|
+
"buoy-super",
|
|
5832
|
+
"beacon-generic",
|
|
5833
|
+
"beacon-tower",
|
|
5834
|
+
"beacon-lattice",
|
|
5835
|
+
"beacon-stake",
|
|
5836
|
+
"beacon-cairn",
|
|
5837
|
+
"light-flare",
|
|
5838
|
+
"lightship",
|
|
5839
|
+
"daymark-triangle",
|
|
5840
|
+
"daymark-square"
|
|
5841
|
+
]);
|
|
5842
|
+
var CHRT_GLYPH_NAMES = new Set(Object.keys(CHRT_GLYPH_SVGS));
|
|
5843
|
+
var CHRT_ATON_COLORS = {
|
|
5844
|
+
"1": "#F8FAFC",
|
|
5845
|
+
// white
|
|
5846
|
+
"2": "#101418",
|
|
5847
|
+
// black
|
|
5848
|
+
"3": "#E5484D",
|
|
5849
|
+
// red
|
|
5850
|
+
"4": "#30A46C",
|
|
5851
|
+
// green
|
|
5852
|
+
"5": "#3E63DD",
|
|
5853
|
+
// blue
|
|
5854
|
+
"6": "#FFC805",
|
|
5855
|
+
// yellow
|
|
5856
|
+
"7": "#8B9298",
|
|
5857
|
+
// grey
|
|
5858
|
+
"8": "#A66B3A",
|
|
5859
|
+
// brown
|
|
5860
|
+
"9": "#FFB224",
|
|
5861
|
+
// amber
|
|
5862
|
+
"10": "#7E4CCB",
|
|
5863
|
+
// violet
|
|
5864
|
+
"11": "#F76B15",
|
|
5865
|
+
// orange
|
|
5866
|
+
"12": "#D671C1"
|
|
5867
|
+
// pink (magenta-ish)
|
|
5868
|
+
};
|
|
5869
|
+
var CHRT_UNKNOWN_TINT = "#8B9298";
|
|
5870
|
+
var HOUSE_BLACK = "#101418";
|
|
5871
|
+
function chrtHouseColor(dayHex, palette = "DAY") {
|
|
5872
|
+
if (palette === "DAY" || palette === "EINK_COLOR") return dayHex;
|
|
5873
|
+
if (palette === "EINK_MONO") return einkMonoGrey(dayHex);
|
|
5874
|
+
if (palette === "EINK_NIGHT") return dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase() || dayHex.toLowerCase() === "#000000" ? "#aaaaaa" : einkNightGrey(dayHex);
|
|
5875
|
+
const isBlack = dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase();
|
|
5876
|
+
if (palette === "DUSK") {
|
|
5877
|
+
return isBlack ? dayHex : capLuminance(dayHex, relativeLuminance(resolveColor("CHBLK", "DUSK")));
|
|
5878
|
+
}
|
|
5879
|
+
return isBlack ? NIGHT_INK : capLuminance(dayHex, relativeLuminance(NIGHT_MUTED_INK));
|
|
5880
|
+
}
|
|
5881
|
+
function themeChrtGlyph(svg3, palette = "DAY") {
|
|
5882
|
+
if (palette === "DAY" || palette === "EINK_COLOR") return svg3;
|
|
5883
|
+
return svg3.replace(
|
|
5884
|
+
/(fill|stroke)="(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})"/g,
|
|
5885
|
+
(_m, attr, hex) => {
|
|
5886
|
+
const six = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
5887
|
+
return `${attr}="${chrtHouseColor(six, palette)}"`;
|
|
5888
|
+
}
|
|
5889
|
+
);
|
|
5890
|
+
}
|
|
5891
|
+
function chrtAtonTintExpression(palette = "DAY") {
|
|
5892
|
+
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
5893
|
+
return colourCodeCase(
|
|
5894
|
+
[
|
|
5895
|
+
["3", c("3")],
|
|
5896
|
+
["4", c("4")],
|
|
5897
|
+
["6", c("6")],
|
|
5898
|
+
["11", c("11")],
|
|
5899
|
+
["9", c("9")],
|
|
5900
|
+
["5", c("5")],
|
|
5901
|
+
["1", c("1")],
|
|
5902
|
+
["2", c("2")],
|
|
5903
|
+
["8", c("8")],
|
|
5904
|
+
["10", c("10")],
|
|
5905
|
+
["12", c("12")],
|
|
5906
|
+
["7", c("7")]
|
|
5907
|
+
],
|
|
5908
|
+
chrtHouseColor(CHRT_UNKNOWN_TINT, palette)
|
|
5909
|
+
);
|
|
5910
|
+
}
|
|
5911
|
+
function chrtLightTintExpression(palette = "DAY") {
|
|
5912
|
+
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
5913
|
+
return colourCodeCase(
|
|
5914
|
+
[
|
|
5915
|
+
["3", c("3")],
|
|
5916
|
+
["4", c("4")],
|
|
5917
|
+
["6", c("6")],
|
|
5918
|
+
["11", c("11")],
|
|
5919
|
+
["9", c("9")],
|
|
5920
|
+
["5", c("5")],
|
|
5921
|
+
["1", c("1")]
|
|
5922
|
+
],
|
|
5923
|
+
c("1")
|
|
5924
|
+
);
|
|
5925
|
+
}
|
|
5926
|
+
function scalar(attr) {
|
|
5927
|
+
return ["coalesce", ["get", attr], ["get", attr.toLowerCase()], 0];
|
|
5928
|
+
}
|
|
5929
|
+
var g = (name) => `${CHRT_ICON_PREFIX}${name}`;
|
|
5930
|
+
function buoyBodyExpression() {
|
|
5931
|
+
return [
|
|
5932
|
+
"match",
|
|
5933
|
+
scalar("BOYSHP"),
|
|
5934
|
+
1,
|
|
5935
|
+
g("buoy-conical"),
|
|
5936
|
+
2,
|
|
5937
|
+
g("buoy-can"),
|
|
5938
|
+
3,
|
|
5939
|
+
g("buoy-spherical"),
|
|
5940
|
+
4,
|
|
5941
|
+
g("buoy-pillar"),
|
|
5942
|
+
5,
|
|
5943
|
+
g("buoy-spar"),
|
|
5944
|
+
6,
|
|
5945
|
+
g("buoy-barrel"),
|
|
5946
|
+
7,
|
|
5947
|
+
g("buoy-super"),
|
|
5948
|
+
8,
|
|
5949
|
+
g("buoy-super"),
|
|
5950
|
+
g("buoy-pillar")
|
|
5951
|
+
];
|
|
5952
|
+
}
|
|
5953
|
+
function beaconBodyExpression() {
|
|
5954
|
+
return [
|
|
5955
|
+
"match",
|
|
5956
|
+
scalar("BCNSHP"),
|
|
5957
|
+
1,
|
|
5958
|
+
g("beacon-stake"),
|
|
5959
|
+
2,
|
|
5960
|
+
g("beacon-stake"),
|
|
5961
|
+
3,
|
|
5962
|
+
g("beacon-tower"),
|
|
5963
|
+
4,
|
|
5964
|
+
g("beacon-lattice"),
|
|
5965
|
+
5,
|
|
5966
|
+
g("beacon-stake"),
|
|
5967
|
+
6,
|
|
5968
|
+
g("beacon-cairn"),
|
|
5969
|
+
g("beacon-generic")
|
|
5970
|
+
];
|
|
5971
|
+
}
|
|
5972
|
+
function cardinalExpression() {
|
|
5973
|
+
return [
|
|
5974
|
+
"match",
|
|
5975
|
+
scalar("CATCAM"),
|
|
5976
|
+
1,
|
|
5977
|
+
g("cardinal-n"),
|
|
5978
|
+
2,
|
|
5979
|
+
g("cardinal-e"),
|
|
5980
|
+
3,
|
|
5981
|
+
g("cardinal-s"),
|
|
5982
|
+
4,
|
|
5983
|
+
g("cardinal-w"),
|
|
5984
|
+
g("cardinal-n")
|
|
5985
|
+
];
|
|
5986
|
+
}
|
|
5987
|
+
var CHRT_ICON_EXPRESSIONS = {
|
|
5988
|
+
BOYLAT: buoyBodyExpression,
|
|
5989
|
+
BOYSPP: buoyBodyExpression,
|
|
5990
|
+
BOYINB: buoyBodyExpression,
|
|
5991
|
+
BOYCAR: cardinalExpression,
|
|
5992
|
+
BOYISD: () => g("isolated-danger"),
|
|
5993
|
+
BOYSAW: () => g("safe-water"),
|
|
5994
|
+
BCNLAT: beaconBodyExpression,
|
|
5995
|
+
BCNSPP: beaconBodyExpression,
|
|
5996
|
+
BCNCAR: cardinalExpression,
|
|
5997
|
+
BCNISD: () => g("isolated-danger"),
|
|
5998
|
+
BCNSAW: () => g("safe-water"),
|
|
5999
|
+
LIGHTS: () => g("light-flare"),
|
|
6000
|
+
LITFLT: () => g("lightship"),
|
|
6001
|
+
LITVES: () => g("lightship"),
|
|
6002
|
+
FOGSIG: () => g("badge-sound"),
|
|
6003
|
+
RTPBCN: () => g("badge-radar"),
|
|
6004
|
+
RDOSTA: () => g("badge-radar"),
|
|
6005
|
+
DAYMAR: () => [
|
|
6006
|
+
"match",
|
|
6007
|
+
scalar("TOPSHP"),
|
|
6008
|
+
1,
|
|
6009
|
+
g("daymark-triangle"),
|
|
6010
|
+
2,
|
|
6011
|
+
g("daymark-triangle"),
|
|
6012
|
+
5,
|
|
6013
|
+
g("daymark-triangle"),
|
|
6014
|
+
g("daymark-square")
|
|
6015
|
+
]
|
|
6016
|
+
};
|
|
6017
|
+
var TINTED_CLASSES = /* @__PURE__ */ new Set([
|
|
6018
|
+
"BOYLAT",
|
|
6019
|
+
"BOYSPP",
|
|
6020
|
+
"BOYINB",
|
|
6021
|
+
"BCNLAT",
|
|
6022
|
+
"BCNSPP",
|
|
6023
|
+
"DAYMAR"
|
|
6024
|
+
]);
|
|
6025
|
+
var CHRT_HALO = { color: HOUSE_BLACK, width: 1 };
|
|
6026
|
+
var CHRT_LINE_DASH = {
|
|
6027
|
+
CBLSUB: { color: "CHMGD", width: 0.8, dash: [6, 3] },
|
|
6028
|
+
PIPSOL: { color: "PIPLN", width: 0.8, dash: [6, 3] }
|
|
6029
|
+
};
|
|
6030
|
+
function applyChrtSymbolSet(style, palette = "DAY") {
|
|
6031
|
+
for (const layer of style.layers) {
|
|
6032
|
+
if (layer.type === "line") {
|
|
6033
|
+
const cls = layer.metadata?.["s52:objectClass"];
|
|
6034
|
+
const dash = cls ? CHRT_LINE_DASH[cls] : void 0;
|
|
6035
|
+
const paint2 = layer.paint ?? {};
|
|
6036
|
+
if (dash && paint2["line-pattern"] != null) {
|
|
6037
|
+
delete paint2["line-pattern"];
|
|
6038
|
+
paint2["line-color"] = resolveColor(dash.color, palette);
|
|
6039
|
+
paint2["line-width"] = dash.width;
|
|
6040
|
+
paint2["line-dasharray"] = dash.dash;
|
|
6041
|
+
layer.paint = paint2;
|
|
6042
|
+
}
|
|
6043
|
+
continue;
|
|
6044
|
+
}
|
|
6045
|
+
if (layer.type !== "symbol") continue;
|
|
6046
|
+
const objectClass2 = layer.metadata?.["s52:objectClass"];
|
|
6047
|
+
if (!objectClass2 || !CHRT_ICON_EXPRESSIONS[objectClass2]) continue;
|
|
6048
|
+
const layout = layer.layout;
|
|
6049
|
+
if (!layout || layout["icon-image"] == null) continue;
|
|
6050
|
+
layout["icon-image"] = CHRT_ICON_EXPRESSIONS[objectClass2]();
|
|
6051
|
+
const paint = layer.paint ?? {};
|
|
6052
|
+
const haloColor = palette === "EINK_MONO" ? chrtHouseColor(HOUSE_BLACK, palette) : palette === "EINK_NIGHT" ? "#000000" : CHRT_HALO.color;
|
|
6053
|
+
if (TINTED_CLASSES.has(objectClass2)) {
|
|
6054
|
+
paint["icon-color"] = chrtAtonTintExpression(palette);
|
|
6055
|
+
paint["icon-halo-color"] = haloColor;
|
|
6056
|
+
paint["icon-halo-width"] = CHRT_HALO.width;
|
|
6057
|
+
} else if (objectClass2 === "LIGHTS") {
|
|
6058
|
+
paint["icon-color"] = chrtLightTintExpression(palette);
|
|
6059
|
+
paint["icon-halo-color"] = haloColor;
|
|
6060
|
+
paint["icon-halo-width"] = CHRT_HALO.width;
|
|
6061
|
+
}
|
|
6062
|
+
layer.paint = paint;
|
|
6063
|
+
}
|
|
6064
|
+
const metadata = style.metadata;
|
|
6065
|
+
metadata["chrt:symbol-set"] = "chrt";
|
|
6066
|
+
return style;
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
// src/atlas/base-layers.ts
|
|
6070
|
+
var NOCOVERAGE_PATTERN = "chrt:hatch-nocoverage";
|
|
6071
|
+
var UNCHARTED_PATTERN = "chrt:hatch-uncharted";
|
|
6072
|
+
function inlandWaterColor(palette) {
|
|
6073
|
+
return (BASE_THEMES[palette] ?? BASE_THEMES.DAY).inlandWater;
|
|
6074
|
+
}
|
|
6075
|
+
var BASE_THEMES = {
|
|
6076
|
+
DAY: {
|
|
6077
|
+
// ── `earth` is the jurisdiction tan DIMMED (~0.55), not a blue-grey ──
|
|
6078
|
+
// Outside the jurisdiction the map still owes the reader "what is land".
|
|
6079
|
+
// State 3 in the header is "the geography, under a crosshatch", and until
|
|
6080
|
+
// 2026-08-31 that geography was unreadable: earth #2A3439 against sea
|
|
6081
|
+
// #1B2328 measured 1.25:1, so Hispaniola showed charted boxes with tan
|
|
6082
|
+
// land inside and nothing recognisable around them (owner report: "no
|
|
6083
|
+
// land painting down near the DR at some zooms"). Land is warm and sea is
|
|
6084
|
+
// cool because that is what land and sea are; dimming the JURISDICTION
|
|
6085
|
+
// tan rather than inventing a colour says the true thing — this is the
|
|
6086
|
+
// same land, we simply do not carry charts for it. Held against BOTH
|
|
6087
|
+
// neighbours: legible over sea, and never mistakable for the full tan of
|
|
6088
|
+
// a country we do carry. DAY 3.59:1 vs sea, 3.17:1 vs tan.
|
|
6089
|
+
earth: "#807761",
|
|
6090
|
+
water: "#1B2328",
|
|
6091
|
+
river: "#24313A",
|
|
6092
|
+
boundary: "#4A575E",
|
|
6093
|
+
jurisdiction: "#D4C69D",
|
|
6094
|
+
// CHRTland's own tan — one land colour
|
|
6095
|
+
inlandWater: "#B9CFDD",
|
|
6096
|
+
// muted, unmistakably not a charted blue
|
|
6097
|
+
// The uncharted hatch's stripe, held to 2.3:1 against `inlandWater` in
|
|
6098
|
+
// every palette (`inland-hatch-contrast.test.ts`) — the hatch says "we have
|
|
6099
|
+
// not charted this water", and stripes the eye cannot find say nothing.
|
|
6100
|
+
// DAY 3.08:1, darker than its ground. Also the colour the artwork file
|
|
6101
|
+
// itself is drawn in.
|
|
6102
|
+
inlandHatch: "#5B7383",
|
|
6103
|
+
// State furniture. These three were referenced by the layers but missing
|
|
6104
|
+
// from this table (and its type — the long-standing TS2339 trio), so the
|
|
6105
|
+
// lines shipped on MapLibre's DEFAULT black. Now deliberate: a hairline
|
|
6106
|
+
// a shade deeper than the tan, quiet caps, tan halo.
|
|
6107
|
+
stateLine: "#B1A077",
|
|
6108
|
+
stateLabel: "#8E8468",
|
|
6109
|
+
stateHalo: "#D4C69D",
|
|
6110
|
+
// Rough-lake furniture (owner ask 2026-08-28: "the lakes need labels,
|
|
6111
|
+
// and shorelines drawn"): a shoreline a shade deeper than the rough
|
|
6112
|
+
// water, and a name in the water's own voice, haloed by the water.
|
|
6113
|
+
inlandShore: "#8FA9BA",
|
|
6114
|
+
inlandLabel: "#54707F",
|
|
6115
|
+
inlandHalo: "#B9CFDD"
|
|
6116
|
+
},
|
|
6117
|
+
DUSK: {
|
|
6118
|
+
// ── Re-derived 2026-09-01: the basemap sits UNDER the chart ──────────
|
|
6119
|
+
// These were the DAY tones scaled by a constant, never measured against
|
|
6120
|
+
// the DUSK chart — so `jurisdiction` came out at L=0.506 against a chart
|
|
6121
|
+
// whose own land (LANDA #8a7d5e) is L=0.209, i.e. the context layer was
|
|
6122
|
+
// 2.4x brighter than the data layer. Now capped at the chart's own land.
|
|
6123
|
+
// The rest of the block rides the same 0.666 channel factor so every
|
|
6124
|
+
// relationship the palette already chose is preserved, only moved down.
|
|
6125
|
+
// `water` is deliberately NOT scaled: it is the reference the land/sea
|
|
6126
|
+
// floor is measured against.
|
|
6127
|
+
earth: "#544E3E",
|
|
6128
|
+
water: "#161D21",
|
|
6129
|
+
river: "#141B20",
|
|
6130
|
+
boundary: "#293135",
|
|
6131
|
+
jurisdiction: "#867D64",
|
|
6132
|
+
inlandWater: "#62717A",
|
|
6133
|
+
// Owner's choice, 2026-10-04: dark stripes, 2.37:1. The DAY stripe here
|
|
6134
|
+
// was 1.02:1 — invisible, so an uncharted lake read as plain water.
|
|
6135
|
+
inlandHatch: "#2E383F",
|
|
6136
|
+
// Label stays DARK here — the halo is still mid-toned, so 3:1 is reachable
|
|
6137
|
+
// downward and a bright label would be a new light source at dusk.
|
|
6138
|
+
stateLine: "#B5A987",
|
|
6139
|
+
stateLabel: "#38342A",
|
|
6140
|
+
stateHalo: "#867D64",
|
|
6141
|
+
inlandShore: "#4D5B67",
|
|
6142
|
+
inlandLabel: "#212629",
|
|
6143
|
+
inlandHalo: "#62717A"
|
|
6144
|
+
},
|
|
6145
|
+
NIGHT: {
|
|
6146
|
+
// ── Re-derived 2026-09-01 — the owner-reported night defect ──────────
|
|
6147
|
+
// `jurisdiction` was L=0.240 against a chart whose own land (LANDA
|
|
6148
|
+
// #3a3020) is L=0.031: the world basemap was 7.7x BRIGHTER than the chart
|
|
6149
|
+
// it sits under, so at z<11 the uncharted interior read as a pale
|
|
6150
|
+
// continent-sized slab on a night chart. Capped at the chart's own land.
|
|
6151
|
+
//
|
|
6152
|
+
// ⚠ `earth` AND `jurisdiction` CONVERGE HERE, and that is forced, not
|
|
6153
|
+
// sloppy. Holding earth 2:1 above the ocean AND 2:1 below the jurisdiction
|
|
6154
|
+
// tan requires jurisdiction >= L 0.174 — 5.6x the chart's own land. At
|
|
6155
|
+
// night the range between the ocean (L 0.006) and the cap (L 0.031) is too
|
|
6156
|
+
// compressed to carry two land tones. COVERAGE IS STILL SAID, by the
|
|
6157
|
+
// `chrt:hatch-nocoverage` crosshatch, which is a stronger signal than tone
|
|
6158
|
+
// and is the one thing that cannot be lost to palette compression.
|
|
6159
|
+
//
|
|
6160
|
+
// Labels invert to light-on-dark: 3:1 downward is unreachable off a halo
|
|
6161
|
+
// this close to black, and CHRTland's own night town labels already sit at
|
|
6162
|
+
// L=0.177 for exactly this reason.
|
|
6163
|
+
earth: "#333026",
|
|
6164
|
+
water: "#10161A",
|
|
6165
|
+
river: "#090D0F",
|
|
6166
|
+
boundary: "#131719",
|
|
6167
|
+
jurisdiction: "#353128",
|
|
6168
|
+
inlandWater: "#232A30",
|
|
6169
|
+
// The DAY stripe, lighter than this ground: 2.92:1, and L 0.16, under the
|
|
6170
|
+
// NIGHT muted-ink ceiling (`night-luminance.test.ts`). Red night draws the
|
|
6171
|
+
// NIGHT chart, so this is its stripe too.
|
|
6172
|
+
inlandHatch: "#5B7383",
|
|
6173
|
+
stateLine: "#484236",
|
|
6174
|
+
stateLabel: "#837963",
|
|
6175
|
+
stateHalo: "#353128",
|
|
6176
|
+
inlandShore: "#1C2227",
|
|
6177
|
+
inlandLabel: "#617484",
|
|
6178
|
+
inlandHalo: "#232A30"
|
|
6179
|
+
},
|
|
6180
|
+
// ── E-ink: uncharted is DARK, never paper ─────────────────────────────
|
|
6181
|
+
// On an e-ink chart, charted safe water is paper. Water outside coverage
|
|
6182
|
+
// must never read as that, so it is the darkest fill on the page — the
|
|
6183
|
+
// e-ink twin of DAY's dark carto — with the no-coverage crosshatch over it
|
|
6184
|
+
// (invariant 5: the coverage portrayal survives). Land outside coverage
|
|
6185
|
+
// keeps the chart's own land tone.
|
|
6186
|
+
EINK_COLOR: {
|
|
6187
|
+
earth: "#d8ccb0",
|
|
6188
|
+
water: "#4f5a62",
|
|
6189
|
+
river: "#4f5a62",
|
|
6190
|
+
boundary: "#000000",
|
|
6191
|
+
jurisdiction: "#f2e2b0",
|
|
6192
|
+
inlandWater: "#c8d6e0",
|
|
6193
|
+
// The house stripe, 3.4:1 on this rough water — strong enough for the
|
|
6194
|
+
// 150 ppi colour layer.
|
|
6195
|
+
inlandHatch: "#5B7383",
|
|
6196
|
+
stateLine: "#555555",
|
|
6197
|
+
stateLabel: "#000000",
|
|
6198
|
+
stateHalo: "#ffffff",
|
|
6199
|
+
inlandShore: "#555555",
|
|
6200
|
+
inlandLabel: "#000000",
|
|
6201
|
+
inlandHalo: "#ffffff"
|
|
6202
|
+
},
|
|
6203
|
+
// Mono: the same rule in the four greys. Uncharted water DARK under a MID
|
|
6204
|
+
// crosshatch; land MID; rough inland water PAPER under the uncharted hatch.
|
|
6205
|
+
EINK_MONO: {
|
|
6206
|
+
earth: "#aaaaaa",
|
|
6207
|
+
water: "#555555",
|
|
6208
|
+
river: "#555555",
|
|
6209
|
+
boundary: "#000000",
|
|
6210
|
+
jurisdiction: "#aaaaaa",
|
|
6211
|
+
inlandWater: "#ffffff",
|
|
6212
|
+
inlandHatch: "#555555",
|
|
6213
|
+
stateLine: "#555555",
|
|
6214
|
+
stateLabel: "#000000",
|
|
6215
|
+
stateHalo: "#ffffff",
|
|
6216
|
+
inlandShore: "#555555",
|
|
6217
|
+
inlandLabel: "#000000",
|
|
6218
|
+
inlandHalo: "#ffffff"
|
|
6219
|
+
},
|
|
6220
|
+
// Night: the inverse. Water outside coverage is the black ground UNDER the
|
|
6221
|
+
// light crosshatch — charted safe water is plain black, so the hatch is what
|
|
6222
|
+
// tells them apart, here as everywhere. Land the DIM grey; labels the line
|
|
6223
|
+
// grey on black.
|
|
6224
|
+
EINK_NIGHT: {
|
|
6225
|
+
earth: "#555555",
|
|
6226
|
+
water: "#000000",
|
|
6227
|
+
river: "#000000",
|
|
6228
|
+
boundary: "#aaaaaa",
|
|
6229
|
+
jurisdiction: "#555555",
|
|
6230
|
+
inlandWater: "#000000",
|
|
6231
|
+
inlandHatch: "#aaaaaa",
|
|
6232
|
+
stateLine: "#aaaaaa",
|
|
6233
|
+
stateLabel: "#aaaaaa",
|
|
6234
|
+
stateHalo: "#000000",
|
|
6235
|
+
inlandShore: "#aaaaaa",
|
|
6236
|
+
inlandLabel: "#aaaaaa",
|
|
6237
|
+
inlandHalo: "#000000"
|
|
6238
|
+
}
|
|
6239
|
+
};
|
|
6240
|
+
function baseLayers(opts = {}) {
|
|
6241
|
+
const palette = opts.palette ?? "DAY";
|
|
6242
|
+
const T = BASE_THEMES[palette] ?? BASE_THEMES.DAY;
|
|
6243
|
+
const src = opts.sourceId ?? BASE_REGION;
|
|
6244
|
+
const L = (o) => o;
|
|
6245
|
+
return [
|
|
6246
|
+
// 1. Geography — the world as it is, in the quiet dark voice. The OCEAN
|
|
6247
|
+
// is a background, not a polygon: the archive ships land and lakes,
|
|
6248
|
+
// and "everything else is sea" is exactly what a background expresses
|
|
6249
|
+
// — geometry-free, gap-free at every antimeridian and pole. Without
|
|
6250
|
+
// it the sea outside coverage was bare S-52 NODTA, a light grey that
|
|
6251
|
+
// read as "charted ground" with a hatch on it.
|
|
6252
|
+
L({
|
|
6253
|
+
id: "chrtbase-ocean",
|
|
6254
|
+
type: "background",
|
|
6255
|
+
paint: { "background-color": T.water }
|
|
6256
|
+
}),
|
|
6257
|
+
L({
|
|
6258
|
+
id: "chrtbase-earth",
|
|
6259
|
+
type: "fill",
|
|
6260
|
+
source: src,
|
|
6261
|
+
"source-layer": "base_earth",
|
|
6262
|
+
paint: { "fill-color": T.earth }
|
|
6263
|
+
}),
|
|
6264
|
+
L({
|
|
6265
|
+
id: "chrtbase-water",
|
|
6266
|
+
type: "fill",
|
|
6267
|
+
source: src,
|
|
6268
|
+
"source-layer": "base_water",
|
|
6269
|
+
paint: { "fill-color": T.water }
|
|
6270
|
+
}),
|
|
6271
|
+
L({
|
|
6272
|
+
id: "chrtbase-river",
|
|
6273
|
+
type: "line",
|
|
6274
|
+
source: src,
|
|
6275
|
+
"source-layer": "base_river",
|
|
6276
|
+
minzoom: 9,
|
|
6277
|
+
paint: {
|
|
6278
|
+
"line-color": T.river,
|
|
6279
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.6, 14, 1.6]
|
|
6280
|
+
}
|
|
6281
|
+
}),
|
|
6282
|
+
L({
|
|
6283
|
+
id: "chrtbase-boundary",
|
|
6284
|
+
type: "line",
|
|
6285
|
+
source: src,
|
|
6286
|
+
"source-layer": "base_boundary",
|
|
6287
|
+
paint: { "line-color": T.boundary, "line-width": 0.6, "line-opacity": 0.8 }
|
|
6288
|
+
}),
|
|
6289
|
+
// 2. THE DEFAULT: no coverage, everywhere, as a geometry-free background.
|
|
6290
|
+
// Constant opacity by design — see the header. What kills it is being
|
|
6291
|
+
// painted OVER, never fading.
|
|
6292
|
+
L({
|
|
6293
|
+
id: "chrtbase-nocoverage",
|
|
6294
|
+
type: "background",
|
|
6295
|
+
paint: { "background-pattern": NOCOVERAGE_PATTERN, "background-opacity": 0.55 }
|
|
6296
|
+
}),
|
|
6297
|
+
// 3. The jurisdiction: countries whose charts CHRT carries go tan — the
|
|
6298
|
+
// same tan as CHRTland, so "land in our world" is ONE colour. Opaque,
|
|
6299
|
+
// so it takes the crosshatch off its ground entirely.
|
|
6300
|
+
L({
|
|
6301
|
+
id: "chrtbase-jurisdiction",
|
|
6302
|
+
type: "fill",
|
|
6303
|
+
source: src,
|
|
6304
|
+
"source-layer": "jurisdiction",
|
|
6305
|
+
paint: { "fill-color": T.jurisdiction }
|
|
6306
|
+
}),
|
|
6307
|
+
// 4. States — internal boundaries and names, inside the jurisdiction
|
|
6308
|
+
// only (owner decision 2026-08-28). A hairline a shade deeper than the
|
|
6309
|
+
// tan, and quiet letter-spaced caps that fade as harbour detail takes
|
|
6310
|
+
// over. The one deliberate exception to no-names-on-the-basemap:
|
|
6311
|
+
// ADMIN furniture, nothing else — see the fence test.
|
|
6312
|
+
L({
|
|
6313
|
+
id: "chrtbase-state-boundary",
|
|
6314
|
+
type: "line",
|
|
6315
|
+
source: src,
|
|
6316
|
+
"source-layer": "state_boundary",
|
|
6317
|
+
minzoom: 4,
|
|
6318
|
+
paint: {
|
|
6319
|
+
"line-color": T.stateLine,
|
|
6320
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1],
|
|
6321
|
+
"line-dasharray": [3, 2],
|
|
6322
|
+
"line-opacity": 0.8
|
|
6323
|
+
}
|
|
6324
|
+
}),
|
|
6325
|
+
// 5. Country borders, drawn to be HOISTED above the whole chart by the
|
|
6326
|
+
// generator (owner, 2026-08-31: "where there is charted land, can we
|
|
6327
|
+
// add country borders on top of everything? I want it clear where
|
|
6328
|
+
// there is a boarder."). Sourced from NE boundary_lines_land — land
|
|
6329
|
+
// borders only, never coastline — so riding on top cannot stroke a
|
|
6330
|
+
// coast. Dashed so it reads as an administrative line, not chart
|
|
6331
|
+
// linework.
|
|
6332
|
+
L({
|
|
6333
|
+
id: "chrtbase-country-boundary",
|
|
6334
|
+
type: "line",
|
|
6335
|
+
source: src,
|
|
6336
|
+
"source-layer": "country_boundary",
|
|
6337
|
+
paint: {
|
|
6338
|
+
"line-color": T.boundary,
|
|
6339
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.8, 8, 1.4, 12, 2.2],
|
|
6340
|
+
"line-dasharray": [4, 2],
|
|
6341
|
+
"line-opacity": 0.9
|
|
6342
|
+
}
|
|
6343
|
+
}),
|
|
6344
|
+
L({
|
|
6345
|
+
id: "chrtbase-state-label",
|
|
6346
|
+
type: "symbol",
|
|
6347
|
+
source: src,
|
|
6348
|
+
"source-layer": "state_label",
|
|
6349
|
+
minzoom: 4,
|
|
6350
|
+
maxzoom: 10,
|
|
6351
|
+
layout: {
|
|
6352
|
+
"text-field": ["upcase", ["get", "name"]],
|
|
6353
|
+
"text-font": ["Open Sans Regular"],
|
|
6354
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 4, 9, 8, 12],
|
|
6355
|
+
"text-letter-spacing": 0.25,
|
|
6356
|
+
"text-allow-overlap": false,
|
|
6357
|
+
"text-padding": 8
|
|
6358
|
+
},
|
|
6359
|
+
paint: {
|
|
6360
|
+
"text-color": T.stateLabel,
|
|
6361
|
+
"text-halo-color": T.stateHalo,
|
|
6362
|
+
"text-halo-width": 1.2,
|
|
6363
|
+
"text-opacity": ["interpolate", ["linear"], ["zoom"], 4, 0.7, 8, 0.85, 10, 0.4]
|
|
6364
|
+
}
|
|
6365
|
+
})
|
|
6366
|
+
];
|
|
6367
|
+
}
|
|
6368
|
+
function baseInlandWaterLayers(opts = {}) {
|
|
6369
|
+
const palette = opts.palette ?? "DAY";
|
|
6370
|
+
const T = BASE_THEMES[palette] ?? BASE_THEMES.DAY;
|
|
6371
|
+
const src = opts.sourceId ?? BASE_REGION;
|
|
6372
|
+
const L = (o) => o;
|
|
6373
|
+
const NE_TIER = ["!", ["has", "kind"]];
|
|
6374
|
+
const PLANET_TIER = ["has", "kind"];
|
|
6375
|
+
const HANDOVER = 9;
|
|
6376
|
+
return [
|
|
6377
|
+
// ── The fill pair, SPLIT BY TIER at the handover ────────────────────────
|
|
6378
|
+
//
|
|
6379
|
+
// Both tiers share `inland_water` (tile-join concatenates the same layer
|
|
6380
|
+
// name), and an unsplit fill draws BOTH — which means the NE polygon is
|
|
6381
|
+
// painted over the planet one at every zoom. That is not a cosmetic
|
|
6382
|
+
// double-draw: NE 10m carries no interior rings, so its blob has no
|
|
6383
|
+
// ISLANDS in it, and painting it over the planet's holed polygons buries
|
|
6384
|
+
// every island in the lake under "uncharted water". Owner-reported at
|
|
6385
|
+
// Winnipesaukee z11.7 ("there is land (and islands) being covered by the
|
|
6386
|
+
// 'no chart here' boundary"), and measured: rendering the planet tier
|
|
6387
|
+
// alone brings back the whole archipelago — Rattlesnake, Bear, Long
|
|
6388
|
+
// Island, Diamond, the Barndoors — while the NE tier alone is a faceted
|
|
6389
|
+
// blob whose edges bear no relation to the shoreline drawn beneath it.
|
|
6390
|
+
//
|
|
6391
|
+
// So each tier owns its own zoom range, the same handover the shoreline
|
|
6392
|
+
// below uses. Known residue, measured over the whole archive: of the 141
|
|
6393
|
+
// NE lakes we still carry, 138 have planet water beneath them; the
|
|
6394
|
+
// exceptions are two unnamed western-Alaska tundra lakes and a 2e-5 deg²
|
|
6395
|
+
// Lake Michigan sliver, which lose their rough fill above z9 because the
|
|
6396
|
+
// planet has no `water` polygon there at all (checked against the raw
|
|
6397
|
+
// pre-clip NDJSON — not a clip artefact).
|
|
6398
|
+
L({
|
|
6399
|
+
id: "chrtbase-inland-water",
|
|
6400
|
+
type: "fill",
|
|
6401
|
+
source: src,
|
|
6402
|
+
"source-layer": "inland_water",
|
|
6403
|
+
filter: NE_TIER,
|
|
6404
|
+
maxzoom: HANDOVER,
|
|
6405
|
+
paint: { "fill-color": T.inlandWater }
|
|
6406
|
+
}),
|
|
6407
|
+
L({
|
|
6408
|
+
id: "chrtbase-inland-hatch",
|
|
6409
|
+
type: "fill",
|
|
6410
|
+
source: src,
|
|
6411
|
+
"source-layer": "inland_water",
|
|
6412
|
+
filter: NE_TIER,
|
|
6413
|
+
maxzoom: HANDOVER,
|
|
6414
|
+
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
6415
|
+
}),
|
|
6416
|
+
L({
|
|
6417
|
+
id: "chrtbase-inland-water-detail",
|
|
6418
|
+
type: "fill",
|
|
6419
|
+
source: src,
|
|
6420
|
+
"source-layer": "inland_water",
|
|
6421
|
+
filter: PLANET_TIER,
|
|
6422
|
+
minzoom: HANDOVER,
|
|
6423
|
+
paint: { "fill-color": T.inlandWater }
|
|
6424
|
+
}),
|
|
6425
|
+
L({
|
|
6426
|
+
id: "chrtbase-inland-hatch-detail",
|
|
6427
|
+
type: "fill",
|
|
6428
|
+
source: src,
|
|
6429
|
+
"source-layer": "inland_water",
|
|
6430
|
+
filter: PLANET_TIER,
|
|
6431
|
+
minzoom: HANDOVER,
|
|
6432
|
+
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
6433
|
+
}),
|
|
6434
|
+
// The rough shoreline (owner ask 2026-08-28). Drawn ABOVE the hatch so
|
|
6435
|
+
// the edge stays crisp through the texture, and BELOW the charted-water
|
|
6436
|
+
// repaint clones the generator splices in after it — so wherever the
|
|
6437
|
+
// chart repaints its own water, the rough NE10m line yields to the
|
|
6438
|
+
// chart's COALNE instead of double-striking it.
|
|
6439
|
+
//
|
|
6440
|
+
// NE-tier geometry ONLY (`kind` absent): the planet detail tier joins
|
|
6441
|
+
// the same source-layer from z9 as MANY polygons whose chunk-clip seams
|
|
6442
|
+
// rejoin invisibly as FILLS — but a line layer strokes every seam, and
|
|
6443
|
+
// Winnipesaukee rendered as a mesh of internal edges. Planet features
|
|
6444
|
+
// carry `kind` (the INLAND_KINDS filter's own field); NE's carry only
|
|
6445
|
+
// `name`, so the absence of `kind` IS the tier discriminator.
|
|
6446
|
+
L({
|
|
6447
|
+
id: "chrtbase-inland-shore",
|
|
6448
|
+
type: "line",
|
|
6449
|
+
source: src,
|
|
6450
|
+
"source-layer": "inland_water",
|
|
6451
|
+
filter: NE_TIER,
|
|
6452
|
+
// Hands the shoreline over to the planet tier at ITS floor. Both
|
|
6453
|
+
// tiers live in `inland_water` from z9 up (tile-join concatenates the
|
|
6454
|
+
// same layer name), and their FILLS merge invisibly — but two
|
|
6455
|
+
// outlines do not: NE 10m generalises Winnipesaukee's shape, so its
|
|
6456
|
+
// outline ran straight across the middle of the lake the planet had
|
|
6457
|
+
// drawn correctly around. The coarse line is right only while it is
|
|
6458
|
+
// the only shape we have.
|
|
6459
|
+
maxzoom: HANDOVER,
|
|
6460
|
+
paint: {
|
|
6461
|
+
"line-color": T.inlandShore,
|
|
6462
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1.1, 14, 1.6],
|
|
6463
|
+
"line-opacity": 0.9
|
|
6464
|
+
}
|
|
6465
|
+
}),
|
|
6466
|
+
// The PLANET tier's shoreline (owner ask 2026-08-31: "uncharted small
|
|
6467
|
+
// lakes render with no outline"). It reads its own `inland_shore` layer,
|
|
6468
|
+
// NOT `inland_water`, and that indirection is the whole fix: the mesh
|
|
6469
|
+
// that forced the `!has kind` fence above is a CLIP artefact — an MVT
|
|
6470
|
+
// feature arrives already cut to its own z10 tile, so stroking the
|
|
6471
|
+
// polygon strokes every tile boundary it crosses. The build now walks
|
|
6472
|
+
// those rings, drops the axis-aligned runs that sit on the read zoom's
|
|
6473
|
+
// tile grid, and emits what is left as lines (measured on one New
|
|
6474
|
+
// England chunk: 61 long axis-aligned mesh edges → 5, and the 5 survivors
|
|
6475
|
+
// are 0.02–0.5 tiles off the grid, i.e. real straight water edges).
|
|
6476
|
+
//
|
|
6477
|
+
// Thinner and quieter than the NE line: this is finer geometry describing
|
|
6478
|
+
// water nobody has surveyed, and approximate data should look
|
|
6479
|
+
// approximate.
|
|
6480
|
+
L({
|
|
6481
|
+
id: "chrtbase-inland-shore-detail",
|
|
6482
|
+
type: "line",
|
|
6483
|
+
source: src,
|
|
6484
|
+
"source-layer": "inland_shore",
|
|
6485
|
+
minzoom: HANDOVER,
|
|
6486
|
+
paint: {
|
|
6487
|
+
"line-color": T.inlandShore,
|
|
6488
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.5, 12, 0.9, 15, 1.2],
|
|
6489
|
+
"line-opacity": 0.85
|
|
6490
|
+
}
|
|
6491
|
+
}),
|
|
6492
|
+
// The lake's name (owner ask 2026-08-28) — the second scoped exception
|
|
6493
|
+
// to no-names-on-the-basemap, after the states: WATER-BODY names only.
|
|
6494
|
+
// No maxzoom: an uncharted lake keeps its name at every zoom; over
|
|
6495
|
+
// charted lakes the chart's own SEAARE label takes over at detail zooms
|
|
6496
|
+
// and collision culls the echo.
|
|
6497
|
+
//
|
|
6498
|
+
// The archive's `inland_water_label` anchors (v3+). v3 shipped ONE
|
|
6499
|
+
// PointOnSurface anchor per lake, and a big lake's single anchor pans
|
|
6500
|
+
// off-viewport — Winnipesaukee at z11.7 read nameless with its name two
|
|
6501
|
+
// tiles west of the screen (owner-reported). Labelling the polygons
|
|
6502
|
+
// instead is NOT the fix: tippecanoe's coalescing drops the NE polygon
|
|
6503
|
+
// from some z11 tiles (measured: 11/619/747 carries only planet pieces),
|
|
6504
|
+
// so a polygon-sourced label vanishes tile by tile. v4 therefore bakes a
|
|
6505
|
+
// deterministic anchor GRID into big lakes — the paper-chart convention
|
|
6506
|
+
// of repeating a name along a large water body — and this one layer
|
|
6507
|
+
// renders however many anchors are in view; collision culls the rest.
|
|
6508
|
+
// Planet-tier water-body NAMES (owner ask 2026-08-31: NE 10m names only
|
|
6509
|
+
// 113 US lakes). Emitted BELOW the NE layer on purpose — MapLibre places
|
|
6510
|
+
// symbols topmost-layer-first, so the big NE lake wins collision against
|
|
6511
|
+
// the pond beside it, and both lose to the chart's own labels (every ENC
|
|
6512
|
+
// class is emitted after this pair).
|
|
6513
|
+
//
|
|
6514
|
+
// The size threshold is OSM's own `min_zoom`, carried through the build:
|
|
6515
|
+
// Lake Winnipesaukee reads 10, Squam Lake 12, so a pond simply is not in
|
|
6516
|
+
// the filter until the user is close enough for it to be the answer to
|
|
6517
|
+
// "what is that water". `["zoom"]` in a filter evaluates at integer zoom
|
|
6518
|
+
// levels and removes the feature from placement entirely — verified
|
|
6519
|
+
// against maplibre-gl-native rather than assumed, because a text-size-0
|
|
6520
|
+
// gate would still claim a collision slot.
|
|
6521
|
+
//
|
|
6522
|
+
// The tiers are separate LAYERS, not a filtered split of one — see the
|
|
6523
|
+
// builder for why: an archive that a not-yet-deployed style would
|
|
6524
|
+
// mis-render cannot be promoted first, and this one has to be.
|
|
6525
|
+
L({
|
|
6526
|
+
id: "chrtbase-inland-label-detail",
|
|
6527
|
+
type: "symbol",
|
|
6528
|
+
source: src,
|
|
6529
|
+
"source-layer": "inland_water_label_detail",
|
|
6530
|
+
minzoom: HANDOVER,
|
|
6531
|
+
filter: [">=", ["zoom"], ["get", "min_zoom"]],
|
|
6532
|
+
layout: {
|
|
6533
|
+
"text-field": ["get", "name"],
|
|
6534
|
+
"text-font": ["Open Sans Regular"],
|
|
6535
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 9, 9, 12, 10.5, 15, 11],
|
|
6536
|
+
"text-max-width": 8,
|
|
6537
|
+
"text-allow-overlap": false,
|
|
6538
|
+
"text-padding": 6,
|
|
6539
|
+
// Bigger water first within the tier — min_zoom IS the size rank.
|
|
6540
|
+
"symbol-sort-key": ["get", "min_zoom"]
|
|
6541
|
+
},
|
|
6542
|
+
paint: {
|
|
6543
|
+
"text-color": T.inlandLabel,
|
|
6544
|
+
"text-halo-color": T.inlandHalo,
|
|
6545
|
+
"text-halo-width": 1,
|
|
6546
|
+
"text-opacity": 0.9
|
|
6547
|
+
}
|
|
6548
|
+
}),
|
|
6549
|
+
L({
|
|
6550
|
+
id: "chrtbase-inland-label",
|
|
6551
|
+
type: "symbol",
|
|
6552
|
+
source: src,
|
|
6553
|
+
"source-layer": "inland_water_label",
|
|
6554
|
+
minzoom: 5,
|
|
6555
|
+
layout: {
|
|
6556
|
+
"text-field": ["get", "name"],
|
|
6557
|
+
"text-font": ["Open Sans Regular"],
|
|
6558
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 5, 9.5, 10, 12],
|
|
6559
|
+
"text-max-width": 7,
|
|
6560
|
+
"text-allow-overlap": false,
|
|
6561
|
+
// Zoom-scaled spacing for the anchor GRID: at z8.9 the 0.08-deg
|
|
6562
|
+
// anchors sit ~54 px apart and a flat padding let six copies of
|
|
6563
|
+
// "Lake Ontario" crowd one screen (owner-reported 2026-08-30).
|
|
6564
|
+
// Wide collision boxes at low zoom thin the grid to the repeat-
|
|
6565
|
+
// by-convention cadence; by z12 the anchors are ~850 px apart and
|
|
6566
|
+
// the padding relaxes to normal.
|
|
6567
|
+
"text-padding": ["interpolate", ["linear"], ["zoom"], 5, 80, 9, 64, 12, 16]
|
|
6568
|
+
},
|
|
6569
|
+
paint: {
|
|
6570
|
+
"text-color": T.inlandLabel,
|
|
6571
|
+
"text-halo-color": T.inlandHalo,
|
|
6572
|
+
"text-halo-width": 1.1
|
|
6573
|
+
}
|
|
6574
|
+
})
|
|
6575
|
+
];
|
|
6576
|
+
}
|
|
6577
|
+
|
|
6578
|
+
// src/atlas/eink-portrayal.ts
|
|
6579
|
+
var EINK_DOTS_PATTERN = `${CHRT_ICON_PREFIX}eink-dots`;
|
|
6580
|
+
var EINK_HATCH_PATTERN = `${CHRT_ICON_PREFIX}eink-hatch`;
|
|
6581
|
+
var EINK_DANGER_PATTERN = `${CHRT_ICON_PREFIX}eink-danger`;
|
|
6582
|
+
var EINK_LINE_SCALE = 1.5;
|
|
6583
|
+
var EINK_LINE_MIN_PX = 1;
|
|
6584
|
+
var EINK_TEXT_SCALE = 1.15;
|
|
6585
|
+
var EINK_HALO_MIN_PX = 1.5;
|
|
6586
|
+
var EINK_DANGER_ICON_SCALE = 1.35;
|
|
6587
|
+
var EINK_WASH_CUTOFF = 0.3;
|
|
6588
|
+
var EINK_SAFETY_CONTOUR_WIDTH = [2, 3.5];
|
|
6589
|
+
var EINK_DANGER_CLASSES = /* @__PURE__ */ new Set(["OBSTRN", "WRECKS", "UWTROC"]);
|
|
6590
|
+
var METRIC_CONTOUR_LADDER_M = [0, 2, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1e3];
|
|
6591
|
+
var IMPERIAL_CONTOUR_LADDER_M = [
|
|
6592
|
+
1.8,
|
|
6593
|
+
3.6,
|
|
6594
|
+
5.4,
|
|
6595
|
+
9.1,
|
|
6596
|
+
18.2,
|
|
6597
|
+
27.4,
|
|
6598
|
+
36.5,
|
|
6599
|
+
54.8,
|
|
6600
|
+
91.4,
|
|
6601
|
+
182.8,
|
|
6602
|
+
365.7,
|
|
6603
|
+
914.4
|
|
6604
|
+
];
|
|
6605
|
+
function snapToLadder(safetyM, ladder) {
|
|
6606
|
+
for (const rung of ladder) if (rung >= safetyM - 1e-6) return rung;
|
|
6607
|
+
return null;
|
|
6608
|
+
}
|
|
6609
|
+
function safetyContourValues(safetyContourM) {
|
|
6610
|
+
const s = effectiveSafetyContour(safetyContourM);
|
|
6611
|
+
const out = [snapToLadder(s, METRIC_CONTOUR_LADDER_M), snapToLadder(s, IMPERIAL_CONTOUR_LADDER_M)];
|
|
6612
|
+
return [...new Set(out.filter((v) => v !== null))];
|
|
6613
|
+
}
|
|
6614
|
+
function isExpr(v) {
|
|
6615
|
+
return Array.isArray(v) && typeof v[0] === "string";
|
|
6616
|
+
}
|
|
6617
|
+
function scaleNumeric(v, k, min = 0) {
|
|
6618
|
+
if (typeof v === "number") return Math.max(v * k, v > 0 ? min : 0);
|
|
6619
|
+
if (!isExpr(v)) return v;
|
|
6620
|
+
const op = v[0];
|
|
6621
|
+
if (op === "interpolate" || op === "interpolate-hcl" || op === "interpolate-lab") {
|
|
6622
|
+
const out = v.slice(0, 3);
|
|
6623
|
+
for (let i = 3; i < v.length; i += 2) out.push(v[i], scaleNumeric(v[i + 1], k, min));
|
|
6624
|
+
return out;
|
|
6625
|
+
}
|
|
6626
|
+
if (op === "step") {
|
|
6627
|
+
const out = [op, v[1], scaleNumeric(v[2], k, min)];
|
|
6628
|
+
for (let i = 3; i < v.length; i += 2) out.push(v[i], scaleNumeric(v[i + 1], k, min));
|
|
6629
|
+
return out;
|
|
6630
|
+
}
|
|
6631
|
+
if (op === "literal") return v;
|
|
6632
|
+
return ["max", ["*", v, k], min];
|
|
6633
|
+
}
|
|
6634
|
+
function objectClass(layer) {
|
|
6635
|
+
const md = layer.metadata;
|
|
6636
|
+
const c = md?.["s52:objectClass"];
|
|
6637
|
+
return typeof c === "string" ? c : null;
|
|
6638
|
+
}
|
|
6639
|
+
function scaffoldId(prefix, baseId) {
|
|
6640
|
+
return `${prefix}${baseId.replace(/-band(\d)/g, "-b$1")}`;
|
|
6641
|
+
}
|
|
6642
|
+
var DRVAL1 = ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1];
|
|
6643
|
+
var DRVAL2 = ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0];
|
|
6644
|
+
var VALDCO = ["to-number", ["coalesce", ["get", "VALDCO"], ["get", "valdco"], -1], -1];
|
|
6645
|
+
function withFilter(base, extra) {
|
|
6646
|
+
return base === void 0 ? extra : ["all", base, extra];
|
|
6647
|
+
}
|
|
6648
|
+
function applyEinkPortrayal(style, palette, opts = {}) {
|
|
6649
|
+
if (palette !== "EINK_COLOR" && palette !== "EINK_MONO" && palette !== "EINK_NIGHT") return style;
|
|
6650
|
+
const mono = isEinkGreyTable(palette);
|
|
6651
|
+
const night = palette === "EINK_NIGHT";
|
|
6652
|
+
const safetyInk = resolveColor("DEPSC", palette);
|
|
6653
|
+
const groundInk = resolveColor("UIBCK", palette);
|
|
6654
|
+
const safety = effectiveSafetyContour(opts.safetyContour ?? DEFAULT_SAFETY_CONTOUR_M);
|
|
6655
|
+
const safetyValues = safetyContourValues(safety);
|
|
6656
|
+
const out = [];
|
|
6657
|
+
for (const original of style.layers) {
|
|
6658
|
+
const id = original.id;
|
|
6659
|
+
const type = original.type;
|
|
6660
|
+
if (type === "hillshade" || id.startsWith("chrtland-fade-")) continue;
|
|
6661
|
+
const layer = structuredClone(original);
|
|
6662
|
+
const paint = layer.paint ??= {};
|
|
6663
|
+
const layout = layer.layout;
|
|
6664
|
+
const cls = objectClass(layer);
|
|
6665
|
+
if (type === "line") {
|
|
6666
|
+
if (paint["line-width"] !== void 0) {
|
|
6667
|
+
paint["line-width"] = scaleNumeric(paint["line-width"], EINK_LINE_SCALE, EINK_LINE_MIN_PX);
|
|
6668
|
+
} else {
|
|
6669
|
+
paint["line-width"] = EINK_LINE_SCALE;
|
|
6670
|
+
}
|
|
6671
|
+
const o = paint["line-opacity"];
|
|
6672
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["line-opacity"] = 1;
|
|
6673
|
+
if (paint["line-blur"] !== void 0) paint["line-blur"] = 0;
|
|
6674
|
+
}
|
|
6675
|
+
if (type === "fill") {
|
|
6676
|
+
const o = paint["fill-opacity"];
|
|
6677
|
+
if (typeof o === "number" && o > 0 && o < 1) {
|
|
6678
|
+
if (cls && EINK_DANGER_CLASSES.has(cls)) {
|
|
6679
|
+
paint["fill-pattern"] = EINK_DANGER_PATTERN;
|
|
6680
|
+
paint["fill-opacity"] = 1;
|
|
6681
|
+
} else {
|
|
6682
|
+
paint["fill-opacity"] = o < EINK_WASH_CUTOFF ? 0 : 1;
|
|
6683
|
+
}
|
|
6684
|
+
}
|
|
6685
|
+
if (paint["fill-antialias"] === void 0) paint["fill-antialias"] = true;
|
|
6686
|
+
}
|
|
6687
|
+
if (type === "background" && night && id === "chrt-background") {
|
|
6688
|
+
paint["background-pattern"] = NOCOVERAGE_PATTERN;
|
|
6689
|
+
}
|
|
6690
|
+
if (type === "background") {
|
|
6691
|
+
const o = paint["background-opacity"];
|
|
6692
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["background-opacity"] = 1;
|
|
6693
|
+
}
|
|
6694
|
+
if (type === "circle") {
|
|
6695
|
+
const blur = paint["circle-blur"];
|
|
6696
|
+
if (typeof blur === "number" && blur >= 0.5) {
|
|
6697
|
+
paint["circle-opacity"] = 0;
|
|
6698
|
+
} else {
|
|
6699
|
+
if (blur !== void 0) paint["circle-blur"] = 0;
|
|
6700
|
+
const o = paint["circle-opacity"];
|
|
6701
|
+
if (typeof o === "number" && o > 0 && o < 1) paint["circle-opacity"] = 1;
|
|
6702
|
+
}
|
|
6703
|
+
}
|
|
6704
|
+
if (type === "symbol") {
|
|
6705
|
+
if (layout?.["text-field"] !== void 0) {
|
|
6706
|
+
if (layout["text-size"] !== void 0) {
|
|
6707
|
+
layout["text-size"] = scaleNumeric(layout["text-size"], EINK_TEXT_SCALE);
|
|
6708
|
+
}
|
|
6709
|
+
if (paint["text-opacity"] !== void 0) paint["text-opacity"] = 1;
|
|
6710
|
+
if (typeof paint["text-halo-color"] === "string") paint["text-halo-color"] = groundInk;
|
|
6711
|
+
const hw = paint["text-halo-width"];
|
|
6712
|
+
paint["text-halo-width"] = typeof hw === "number" ? Math.max(hw, EINK_HALO_MIN_PX) : hw ?? EINK_HALO_MIN_PX;
|
|
6713
|
+
if (paint["text-halo-blur"] !== void 0) paint["text-halo-blur"] = 0;
|
|
6714
|
+
}
|
|
6715
|
+
const io = paint["icon-opacity"];
|
|
6716
|
+
if (typeof io === "number" && io > 0 && io < 1) paint["icon-opacity"] = 1;
|
|
6717
|
+
if (cls && EINK_DANGER_CLASSES.has(cls) && layout?.["icon-image"] !== void 0) {
|
|
6718
|
+
layout["icon-size"] = scaleNumeric(layout["icon-size"] ?? 1, EINK_DANGER_ICON_SCALE);
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
out.push(layer);
|
|
6722
|
+
if (type === "fill" && cls === "DEPARE" && mono) {
|
|
6723
|
+
const drying = ["all", ["<", DRVAL1, 0], ["<=", DRVAL2, 0]];
|
|
6724
|
+
out.push({
|
|
6725
|
+
...structuredClone(original),
|
|
6726
|
+
id: scaffoldId("chrt-eink-depth-of-", id),
|
|
6727
|
+
filter: withFilter(original.filter, ["<", DRVAL1, safety]),
|
|
6728
|
+
paint: {
|
|
6729
|
+
"fill-pattern": ["case", drying, EINK_HATCH_PATTERN, EINK_DOTS_PATTERN]
|
|
6730
|
+
}
|
|
6731
|
+
});
|
|
6732
|
+
}
|
|
6733
|
+
if (type === "line" && cls === "DEPCNT") {
|
|
6734
|
+
const isSafety = ["any", ...safetyValues.map((v) => ["<", ["abs", ["-", VALDCO, v]], 0.05])];
|
|
6735
|
+
out.push({
|
|
6736
|
+
...structuredClone(original),
|
|
6737
|
+
id: scaffoldId("chrt-eink-safety-of-", id),
|
|
6738
|
+
filter: withFilter(original.filter, isSafety),
|
|
6739
|
+
paint: {
|
|
6740
|
+
"line-color": safetyInk,
|
|
6741
|
+
"line-width": [
|
|
6742
|
+
"interpolate",
|
|
6743
|
+
["linear"],
|
|
6744
|
+
["zoom"],
|
|
6745
|
+
8,
|
|
6746
|
+
EINK_SAFETY_CONTOUR_WIDTH[0],
|
|
6747
|
+
14,
|
|
6748
|
+
EINK_SAFETY_CONTOUR_WIDTH[1]
|
|
6749
|
+
]
|
|
6750
|
+
},
|
|
6751
|
+
layout: { "line-join": "round", "line-cap": "round" }
|
|
6752
|
+
});
|
|
6753
|
+
}
|
|
6754
|
+
}
|
|
6755
|
+
return {
|
|
6756
|
+
...style,
|
|
6757
|
+
// No transitions: a property change is one repaint, never an animation.
|
|
6758
|
+
...{ transition: { duration: 0, delay: 0 } },
|
|
6759
|
+
metadata: { ...style.metadata, "chrt:eink": night ? "night" : mono ? "mono" : "color" },
|
|
6760
|
+
layers: out
|
|
6761
|
+
};
|
|
6762
|
+
}
|
|
6763
|
+
|
|
6764
|
+
// src/atlas/map-live.ts
|
|
5310
6765
|
function isMapLive(map) {
|
|
5311
6766
|
return map._removed !== true;
|
|
5312
6767
|
}
|
|
@@ -5941,248 +7396,33 @@ function lineSpriteId(lineStyleName) {
|
|
|
5941
7396
|
}
|
|
5942
7397
|
function lineStylePaint(name) {
|
|
5943
7398
|
const meta = S52_LINESTYLES[name];
|
|
5944
|
-
if (!meta) {
|
|
5945
|
-
throw new Error(
|
|
5946
|
-
`Unknown S-52 linestyle "${name}". Known: ${Object.keys(S52_LINESTYLES).length} styles (regenerate with: bun run scripts/extract-opencpn-linestyles.ts)`
|
|
5947
|
-
);
|
|
5948
|
-
}
|
|
5949
|
-
return {
|
|
5950
|
-
"line-pattern": lineSpriteId(meta.name),
|
|
5951
|
-
"line-width": houseWidth(meta)
|
|
5952
|
-
};
|
|
5953
|
-
}
|
|
5954
|
-
function lineStylePaintCase(cases, fallback) {
|
|
5955
|
-
const resolve = (name) => {
|
|
5956
|
-
const meta = S52_LINESTYLES[name];
|
|
5957
|
-
if (!meta) throw new Error(`Unknown S-52 linestyle "${name}"`);
|
|
5958
|
-
return meta;
|
|
5959
|
-
};
|
|
5960
|
-
const fallbackMeta = resolve(fallback);
|
|
5961
|
-
const pattern = ["case"];
|
|
5962
|
-
const width = ["case"];
|
|
5963
|
-
for (const [condition, name] of cases) {
|
|
5964
|
-
const meta = resolve(name);
|
|
5965
|
-
pattern.push(condition, lineSpriteId(meta.name));
|
|
5966
|
-
width.push(condition, houseWidth(meta));
|
|
5967
|
-
}
|
|
5968
|
-
pattern.push(lineSpriteId(fallbackMeta.name));
|
|
5969
|
-
width.push(houseWidth(fallbackMeta));
|
|
5970
|
-
return { "line-pattern": pattern, "line-width": width };
|
|
5971
|
-
}
|
|
5972
|
-
|
|
5973
|
-
// src/atlas/s52-pattern.ts
|
|
5974
|
-
var S52_PATTERN_SPRITE_PREFIX = "AP:";
|
|
5975
|
-
function patternSpriteId(patternName) {
|
|
5976
|
-
return `${S52_PATTERN_SPRITE_PREFIX}${patternName}`;
|
|
5977
|
-
}
|
|
5978
|
-
|
|
5979
|
-
// src/atlas/s52-conditional.ts
|
|
5980
|
-
var DEFAULT_SAFETY_CONTOUR_M = 10;
|
|
5981
|
-
var DEPARE_DEEP_CUT_M = 18.2;
|
|
5982
|
-
var DEPARE_SHALLOW_CUT_M = 2;
|
|
5983
|
-
function effectiveSafetyContour(safetyContourM) {
|
|
5984
|
-
if (!Number.isFinite(safetyContourM)) return DEFAULT_SAFETY_CONTOUR_M;
|
|
5985
|
-
return Math.min(Math.max(safetyContourM, DEPARE_SHALLOW_CUT_M), DEPARE_DEEP_CUT_M);
|
|
5986
|
-
}
|
|
5987
|
-
function depareColor(props, ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
5988
|
-
const drval1 = Number(props.DRVAL1 ?? props.drval1 ?? -1);
|
|
5989
|
-
const drval2 = Number(props.DRVAL2 ?? props.drval2 ?? drval1);
|
|
5990
|
-
const safety = effectiveSafetyContour(safetyContourM);
|
|
5991
|
-
if (drval1 < 0 && drval2 <= 0) return resolveColor("DEPIT", ct);
|
|
5992
|
-
if (drval1 < DEPARE_SHALLOW_CUT_M) return resolveColor("DEPVS", ct);
|
|
5993
|
-
if (drval1 < safety) return resolveColor("DEPMS", ct);
|
|
5994
|
-
if (drval1 < DEPARE_DEEP_CUT_M) return resolveColor("DEPMD", ct);
|
|
5995
|
-
return resolveColor("DEPDW", ct);
|
|
5996
|
-
}
|
|
5997
|
-
var DEPARE_FILL_BRANCH_TOKENS = ["DEPIT", "DEPVS", "DEPMS", "DEPMD"];
|
|
5998
|
-
var DEPARE_FILL_FALLBACK_TOKEN = "DEPDW";
|
|
5999
|
-
function depareFillExpression(ct = "DAY", safetyContourM = DEFAULT_SAFETY_CONTOUR_M) {
|
|
6000
|
-
const safety = effectiveSafetyContour(safetyContourM);
|
|
6001
|
-
const [it, vs, ms, md] = DEPARE_FILL_BRANCH_TOKENS;
|
|
6002
|
-
return [
|
|
6003
|
-
"case",
|
|
6004
|
-
[
|
|
6005
|
-
"all",
|
|
6006
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], 0],
|
|
6007
|
-
["<=", ["coalesce", ["get", "DRVAL2"], ["get", "drval2"], 0], 0]
|
|
6008
|
-
],
|
|
6009
|
-
resolveColor(it, ct),
|
|
6010
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_SHALLOW_CUT_M],
|
|
6011
|
-
resolveColor(vs, ct),
|
|
6012
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], safety],
|
|
6013
|
-
resolveColor(ms, ct),
|
|
6014
|
-
["<", ["coalesce", ["get", "DRVAL1"], ["get", "drval1"], -1], DEPARE_DEEP_CUT_M],
|
|
6015
|
-
resolveColor(md, ct),
|
|
6016
|
-
resolveColor(DEPARE_FILL_FALLBACK_TOKEN, ct)
|
|
6017
|
-
];
|
|
6018
|
-
}
|
|
6019
|
-
function listAttrString(attr) {
|
|
6020
|
-
return ["to-string", ["coalesce", ["get", attr], ["get", attr.toLowerCase()], ""]];
|
|
6021
|
-
}
|
|
6022
|
-
function listAttrCase(attr, cases, fallback) {
|
|
6023
|
-
const attrStr = listAttrString(attr);
|
|
6024
|
-
const expr = ["case"];
|
|
6025
|
-
for (const [code, value] of cases) {
|
|
6026
|
-
expr.push([">=", ["index-of", `"${code}"`, attrStr], 0], value);
|
|
6027
|
-
}
|
|
6028
|
-
for (const [code, value] of cases) {
|
|
6029
|
-
expr.push(["==", attrStr, code], value);
|
|
6030
|
-
}
|
|
6031
|
-
for (const [code, value] of cases) {
|
|
6032
|
-
expr.push(["==", attrStr, `[${code}]`], value);
|
|
6033
|
-
}
|
|
6034
|
-
expr.push(fallback);
|
|
6035
|
-
return expr;
|
|
6036
|
-
}
|
|
6037
|
-
function listAttrHas(attr, codes) {
|
|
6038
|
-
const attrStr = listAttrString(attr);
|
|
6039
|
-
const clauses = [];
|
|
6040
|
-
for (const code of codes) {
|
|
6041
|
-
clauses.push([">=", ["index-of", `"${code}"`, attrStr], 0]);
|
|
6042
|
-
clauses.push(["==", attrStr, code]);
|
|
6043
|
-
clauses.push(["==", attrStr, `[${code}]`]);
|
|
6044
|
-
}
|
|
6045
|
-
return ["any", ...clauses];
|
|
6046
|
-
}
|
|
6047
|
-
function colourCodeCase(cases, fallback) {
|
|
6048
|
-
return listAttrCase("COLOUR", cases, fallback);
|
|
6049
|
-
}
|
|
6050
|
-
function lightColor(props, ct = "DAY") {
|
|
6051
|
-
const raw = props.COLOUR ?? props.colour;
|
|
6052
|
-
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
6053
|
-
switch (code) {
|
|
6054
|
-
case 1:
|
|
6055
|
-
return resolveColor("LITWT", ct);
|
|
6056
|
-
case 3:
|
|
6057
|
-
return resolveColor("LITRD", ct);
|
|
6058
|
-
case 4:
|
|
6059
|
-
return resolveColor("LITGN", ct);
|
|
6060
|
-
case 5:
|
|
6061
|
-
return resolveColor("LITBL", ct);
|
|
6062
|
-
case 6:
|
|
6063
|
-
return resolveColor("LITYW", ct);
|
|
6064
|
-
case 11:
|
|
6065
|
-
return resolveColor("LITOR", ct);
|
|
6066
|
-
default:
|
|
6067
|
-
return resolveColor("LITYW", ct);
|
|
6068
|
-
}
|
|
6069
|
-
}
|
|
6070
|
-
function lightFillExpression(ct = "DAY") {
|
|
6071
|
-
return colourCodeCase(
|
|
6072
|
-
[
|
|
6073
|
-
["3", resolveColor("LITRD", ct)],
|
|
6074
|
-
["4", resolveColor("LITGN", ct)],
|
|
6075
|
-
["6", resolveColor("LITYW", ct)],
|
|
6076
|
-
["11", resolveColor("LITOR", ct)],
|
|
6077
|
-
["5", resolveColor("LITBL", ct)],
|
|
6078
|
-
["1", resolveColor("LITWT", ct)]
|
|
6079
|
-
],
|
|
6080
|
-
resolveColor("LITYW", ct)
|
|
6081
|
-
);
|
|
6082
|
-
}
|
|
6083
|
-
function lateralColor(props, ct = "DAY") {
|
|
6084
|
-
const catlam = Number(props.CATLAM ?? props.catlam ?? 0);
|
|
6085
|
-
if (catlam === 1) return resolveColor("LITRD", ct);
|
|
6086
|
-
if (catlam === 2) return resolveColor("LITGN", ct);
|
|
6087
|
-
return resolveColor("LITYW", ct);
|
|
6088
|
-
}
|
|
6089
|
-
function lateralFillExpression(ct = "DAY") {
|
|
6090
|
-
return [
|
|
6091
|
-
"match",
|
|
6092
|
-
["coalesce", ["get", "CATLAM"], ["get", "catlam"], 0],
|
|
6093
|
-
1,
|
|
6094
|
-
resolveColor("LITRD", ct),
|
|
6095
|
-
2,
|
|
6096
|
-
resolveColor("LITGN", ct),
|
|
6097
|
-
resolveColor("LITYW", ct)
|
|
6098
|
-
];
|
|
6099
|
-
}
|
|
6100
|
-
function hazardColor(props, ct = "DAY") {
|
|
6101
|
-
const watlev = Number(props.WATLEV ?? props.watlev ?? 0);
|
|
6102
|
-
const valsou = props.VALSOU ?? props.valsou;
|
|
6103
|
-
if (watlev === 1 || watlev === 2) return resolveColor("DNGHL", ct);
|
|
6104
|
-
if (valsou != null && Number(valsou) < 2) return resolveColor("DNGHL", ct);
|
|
6105
|
-
return resolveColor("RESBL", ct);
|
|
6106
|
-
}
|
|
6107
|
-
function hazardFillExpression(ct = "DAY") {
|
|
6108
|
-
return [
|
|
6109
|
-
"case",
|
|
6110
|
-
[
|
|
6111
|
-
"any",
|
|
6112
|
-
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 1],
|
|
6113
|
-
["==", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 2]
|
|
6114
|
-
],
|
|
6115
|
-
resolveColor("DNGHL", ct),
|
|
6116
|
-
[
|
|
6117
|
-
"all",
|
|
6118
|
-
["has", "VALSOU"],
|
|
6119
|
-
["<", ["get", "VALSOU"], 2]
|
|
6120
|
-
],
|
|
6121
|
-
resolveColor("DNGHL", ct),
|
|
6122
|
-
resolveColor("RESBL", ct)
|
|
6123
|
-
];
|
|
6124
|
-
}
|
|
6125
|
-
function vegatnPatternExpression() {
|
|
6126
|
-
return [
|
|
6127
|
-
"match",
|
|
6128
|
-
["coalesce", ["get", "CATVEG"], ["get", "catveg"], 0],
|
|
6129
|
-
1,
|
|
6130
|
-
patternSpriteId("VEGATN03"),
|
|
6131
|
-
// grass
|
|
6132
|
-
3,
|
|
6133
|
-
patternSpriteId("VEGATN03"),
|
|
6134
|
-
// bush
|
|
6135
|
-
4,
|
|
6136
|
-
patternSpriteId("VEGATN04"),
|
|
6137
|
-
// deciduous wood
|
|
6138
|
-
5,
|
|
6139
|
-
patternSpriteId("VEGATN04"),
|
|
6140
|
-
// coniferous wood
|
|
6141
|
-
6,
|
|
6142
|
-
patternSpriteId("VEGATN04"),
|
|
6143
|
-
// wood (mixed)
|
|
6144
|
-
7,
|
|
6145
|
-
patternSpriteId("MARSHES1"),
|
|
6146
|
-
// mangroves
|
|
6147
|
-
10,
|
|
6148
|
-
patternSpriteId("VEGATN03"),
|
|
6149
|
-
// scrub
|
|
6150
|
-
11,
|
|
6151
|
-
patternSpriteId("VEGATN04"),
|
|
6152
|
-
// tree
|
|
6153
|
-
12,
|
|
6154
|
-
patternSpriteId("VEGATN04"),
|
|
6155
|
-
// evergreen
|
|
6156
|
-
16,
|
|
6157
|
-
patternSpriteId("MARSHES1"),
|
|
6158
|
-
// swamp
|
|
6159
|
-
17,
|
|
6160
|
-
patternSpriteId("MARSHES1"),
|
|
6161
|
-
// marsh
|
|
6162
|
-
21,
|
|
6163
|
-
patternSpriteId("VEGATN04"),
|
|
6164
|
-
// savannah
|
|
6165
|
-
patternSpriteId("VEGATN03")
|
|
6166
|
-
// default: grass pattern
|
|
6167
|
-
];
|
|
6168
|
-
}
|
|
6169
|
-
function daymarkColor(props, ct = "DAY") {
|
|
6170
|
-
const raw = props.COLOUR ?? props.colour;
|
|
6171
|
-
const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
|
|
6172
|
-
switch (code) {
|
|
6173
|
-
case 1:
|
|
6174
|
-
return resolveColor("LITWT", ct);
|
|
6175
|
-
case 3:
|
|
6176
|
-
return resolveColor("LITRD", ct);
|
|
6177
|
-
case 4:
|
|
6178
|
-
return resolveColor("LITGN", ct);
|
|
6179
|
-
case 6:
|
|
6180
|
-
return resolveColor("LITYW", ct);
|
|
6181
|
-
case 11:
|
|
6182
|
-
return resolveColor("LITOR", ct);
|
|
6183
|
-
default:
|
|
6184
|
-
return resolveColor("LITGN", ct);
|
|
7399
|
+
if (!meta) {
|
|
7400
|
+
throw new Error(
|
|
7401
|
+
`Unknown S-52 linestyle "${name}". Known: ${Object.keys(S52_LINESTYLES).length} styles (regenerate with: bun run scripts/extract-opencpn-linestyles.ts)`
|
|
7402
|
+
);
|
|
7403
|
+
}
|
|
7404
|
+
return {
|
|
7405
|
+
"line-pattern": lineSpriteId(meta.name),
|
|
7406
|
+
"line-width": houseWidth(meta)
|
|
7407
|
+
};
|
|
7408
|
+
}
|
|
7409
|
+
function lineStylePaintCase(cases, fallback) {
|
|
7410
|
+
const resolve = (name) => {
|
|
7411
|
+
const meta = S52_LINESTYLES[name];
|
|
7412
|
+
if (!meta) throw new Error(`Unknown S-52 linestyle "${name}"`);
|
|
7413
|
+
return meta;
|
|
7414
|
+
};
|
|
7415
|
+
const fallbackMeta = resolve(fallback);
|
|
7416
|
+
const pattern = ["case"];
|
|
7417
|
+
const width = ["case"];
|
|
7418
|
+
for (const [condition, name] of cases) {
|
|
7419
|
+
const meta = resolve(name);
|
|
7420
|
+
pattern.push(condition, lineSpriteId(meta.name));
|
|
7421
|
+
width.push(condition, houseWidth(meta));
|
|
6185
7422
|
}
|
|
7423
|
+
pattern.push(lineSpriteId(fallbackMeta.name));
|
|
7424
|
+
width.push(houseWidth(fallbackMeta));
|
|
7425
|
+
return { "line-pattern": pattern, "line-width": width };
|
|
6186
7426
|
}
|
|
6187
7427
|
|
|
6188
7428
|
// src/atlas/s52-lookup.ts
|
|
@@ -20075,9 +21315,9 @@ function compileAtonExpression(lookup, mapSymbol = (name) => name) {
|
|
|
20075
21315
|
expr.push(fallback);
|
|
20076
21316
|
return expr;
|
|
20077
21317
|
}
|
|
20078
|
-
function atonIconExpression(
|
|
20079
|
-
const lookup = S52_ATON_LOOKUPS[
|
|
20080
|
-
if (!lookup) throw new Error(`s52-aton-expressions: no generated lookup for ${
|
|
21318
|
+
function atonIconExpression(objectClass2) {
|
|
21319
|
+
const lookup = S52_ATON_LOOKUPS[objectClass2];
|
|
21320
|
+
if (!lookup) throw new Error(`s52-aton-expressions: no generated lookup for ${objectClass2}`);
|
|
20081
21321
|
return compileAtonExpression(lookup);
|
|
20082
21322
|
}
|
|
20083
21323
|
function lightsIconExpression() {
|
|
@@ -23162,7 +24402,9 @@ var LABEL_TINT = {
|
|
|
23162
24402
|
};
|
|
23163
24403
|
function tintFor(tint, ct) {
|
|
23164
24404
|
const day = LABEL_TINT[tint].day;
|
|
23165
|
-
if (ct === "DAY") return day;
|
|
24405
|
+
if (ct === "DAY" || ct === "EINK_COLOR") return day;
|
|
24406
|
+
if (ct === "EINK_MONO") return einkMonoGrey(day);
|
|
24407
|
+
if (ct === "EINK_NIGHT") return einkNightGrey(day);
|
|
23166
24408
|
return capLuminance(day, relativeLuminance(resolveColor("CHBLK", ct)));
|
|
23167
24409
|
}
|
|
23168
24410
|
function nightText(tint) {
|
|
@@ -23173,10 +24415,14 @@ function nightText(tint) {
|
|
|
23173
24415
|
}
|
|
23174
24416
|
function onHalo(tint, ct) {
|
|
23175
24417
|
if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
|
|
24418
|
+
if (ct === "EINK_NIGHT") return { textColor: EINK_MONO_GREYS.MID, textHaloColor: EINK_MONO_GREYS.INK };
|
|
24419
|
+
if (isEinkColorTable(ct)) return { textColor: EINK_MONO_GREYS.INK, textHaloColor: EINK_MONO_GREYS.PAPER };
|
|
23176
24420
|
return { textColor: DARK_BG, textHaloColor: tintFor(tint, ct) };
|
|
23177
24421
|
}
|
|
23178
24422
|
function onKnockout(tint, ct) {
|
|
23179
24423
|
if (ct === "NIGHT") return { textColor: nightText(tint), textHaloColor: NIGHT_KNOCKOUT };
|
|
24424
|
+
if (ct === "EINK_NIGHT") return { textColor: EINK_MONO_GREYS.MID, textHaloColor: EINK_MONO_GREYS.INK };
|
|
24425
|
+
if (isEinkColorTable(ct)) return { textColor: EINK_MONO_GREYS.INK, textHaloColor: EINK_MONO_GREYS.PAPER };
|
|
23180
24426
|
return { textColor: tintFor(tint, ct), textHaloColor: DARK_BG };
|
|
23181
24427
|
}
|
|
23182
24428
|
var NAV_AID_LABEL_TEXT_SIZE = [
|
|
@@ -27393,10 +28639,20 @@ var S101_NO_OFFICIAL_COUNTERPART = /* @__PURE__ */ new Set([
|
|
|
27393
28639
|
"SISTAT02",
|
|
27394
28640
|
"WATTUR01"
|
|
27395
28641
|
]);
|
|
28642
|
+
function s101EinkTable(einkS52, fallback) {
|
|
28643
|
+
const out = {};
|
|
28644
|
+
for (const [token, dayHex] of Object.entries(S101_DAY)) {
|
|
28645
|
+
out[token] = einkS52[token] ?? fallback(dayHex);
|
|
28646
|
+
}
|
|
28647
|
+
return out;
|
|
28648
|
+
}
|
|
27396
28649
|
var S101_TABLES = {
|
|
27397
28650
|
DAY: S101_DAY,
|
|
27398
28651
|
DUSK: S101_DUSK,
|
|
27399
|
-
NIGHT: S101_NIGHT
|
|
28652
|
+
NIGHT: S101_NIGHT,
|
|
28653
|
+
EINK_COLOR: s101EinkTable(S52_EINK_COLOR, (hex) => hex),
|
|
28654
|
+
EINK_MONO: s101EinkTable(S52_EINK_MONO, einkMonoGrey),
|
|
28655
|
+
EINK_NIGHT: s101EinkTable(S52_EINK_NIGHT, einkNightGrey)
|
|
27400
28656
|
};
|
|
27401
28657
|
var warnedTokens = /* @__PURE__ */ new Set();
|
|
27402
28658
|
function reportUnresolvedToken(token, table) {
|
|
@@ -27443,328 +28699,54 @@ function rewritePatternToS101(node, available) {
|
|
|
27443
28699
|
if (typeof node === "string") {
|
|
27444
28700
|
return available.has(node) ? `${S101_ICON_PREFIX}${node}` : node;
|
|
27445
28701
|
}
|
|
27446
|
-
if (Array.isArray(node)) return node.map((n) => rewritePatternToS101(n, available));
|
|
27447
|
-
return node;
|
|
27448
|
-
}
|
|
27449
|
-
function collectLiterals(node, out) {
|
|
27450
|
-
if (typeof node === "string") {
|
|
27451
|
-
out.add(node);
|
|
27452
|
-
return;
|
|
27453
|
-
}
|
|
27454
|
-
if (Array.isArray(node)) for (const n of node) collectLiterals(n, out);
|
|
27455
|
-
}
|
|
27456
|
-
function applyS101SymbolSet(style) {
|
|
27457
|
-
const seen = { icon: /* @__PURE__ */ new Set(), fill: /* @__PURE__ */ new Set(), line: /* @__PURE__ */ new Set() };
|
|
27458
|
-
for (const layer of style.layers) {
|
|
27459
|
-
const layout = layer.layout;
|
|
27460
|
-
const paint = layer.paint;
|
|
27461
|
-
if (layer.type === "symbol" && layout && layout["icon-image"] != null) {
|
|
27462
|
-
layout["icon-image"] = rewriteIconToS101(layout["icon-image"]);
|
|
27463
|
-
collectLiterals(layout["icon-image"], seen.icon);
|
|
27464
|
-
}
|
|
27465
|
-
for (const bag of [layout, paint]) {
|
|
27466
|
-
if (!bag) continue;
|
|
27467
|
-
if (bag["fill-pattern"] != null) {
|
|
27468
|
-
bag["fill-pattern"] = rewritePatternToS101(bag["fill-pattern"], S101_AREA_FILL_NAMES);
|
|
27469
|
-
collectLiterals(bag["fill-pattern"], seen.fill);
|
|
27470
|
-
}
|
|
27471
|
-
if (bag["line-pattern"] != null) {
|
|
27472
|
-
bag["line-pattern"] = rewritePatternToS101(bag["line-pattern"], S101_LINE_STYLE_NAMES);
|
|
27473
|
-
collectLiterals(bag["line-pattern"], seen.line);
|
|
27474
|
-
}
|
|
27475
|
-
}
|
|
27476
|
-
}
|
|
27477
|
-
const official = (names) => [...names].filter((n) => n.startsWith(S101_ICON_PREFIX)).length;
|
|
27478
|
-
const iconSubs = [...seen.icon].filter((n) => S101_NO_OFFICIAL_COUNTERPART.has(n)).length;
|
|
27479
|
-
const patternSubs = (names) => [...names].filter((n) => /^(AP|LC):/.test(n)).length;
|
|
27480
|
-
const coverage = {
|
|
27481
|
-
pointSymbols: { official: official(seen.icon), s52Substitute: iconSubs },
|
|
27482
|
-
areaFills: { official: official(seen.fill), s52Substitute: patternSubs(seen.fill) },
|
|
27483
|
-
lineStyles: { official: official(seen.line), s52Substitute: patternSubs(seen.line) }
|
|
27484
|
-
};
|
|
27485
|
-
const metadata = style.metadata;
|
|
27486
|
-
metadata["chrt:symbol-set"] = "s101";
|
|
27487
|
-
metadata["chrt:s101-source-commit"] = S101_SOURCE_COMMIT;
|
|
27488
|
-
metadata["chrt:s101-coverage"] = {
|
|
27489
|
-
...coverage,
|
|
27490
|
-
/** True only when NOTHING in this document is an S-52 substitute. A
|
|
27491
|
-
* consumer that wants "S-101 or nothing" branches on this one boolean. */
|
|
27492
|
-
complete: Object.values(coverage).every((c) => c.s52Substitute === 0)
|
|
27493
|
-
};
|
|
27494
|
-
return style;
|
|
27495
|
-
}
|
|
27496
|
-
|
|
27497
|
-
// src/atlas/chrt-svgs-generated.ts
|
|
27498
|
-
var CHRT_GLYPH_SVGS = {
|
|
27499
|
-
"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>',
|
|
27500
|
-
"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>',
|
|
27501
|
-
"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>',
|
|
27502
|
-
"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>',
|
|
27503
|
-
"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>',
|
|
27504
|
-
"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>',
|
|
27505
|
-
"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>',
|
|
27506
|
-
"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>',
|
|
27507
|
-
"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>',
|
|
27508
|
-
"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>',
|
|
27509
|
-
"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>',
|
|
27510
|
-
"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>',
|
|
27511
|
-
"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>',
|
|
27512
|
-
"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>',
|
|
27513
|
-
"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>',
|
|
27514
|
-
"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>',
|
|
27515
|
-
"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>',
|
|
27516
|
-
"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>',
|
|
27517
|
-
"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>',
|
|
27518
|
-
"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>',
|
|
27519
|
-
"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>',
|
|
27520
|
-
"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>',
|
|
27521
|
-
"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>',
|
|
27522
|
-
"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>',
|
|
27523
|
-
"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>',
|
|
27524
|
-
"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>'
|
|
27525
|
-
};
|
|
27526
|
-
|
|
27527
|
-
// src/atlas/chrt-style.ts
|
|
27528
|
-
var CHRT_ICON_PREFIX = "chrt:";
|
|
27529
|
-
var CHRT_SDF_GLYPHS = /* @__PURE__ */ new Set([
|
|
27530
|
-
"buoy-can",
|
|
27531
|
-
"buoy-conical",
|
|
27532
|
-
"buoy-spherical",
|
|
27533
|
-
"buoy-pillar",
|
|
27534
|
-
"buoy-spar",
|
|
27535
|
-
"buoy-barrel",
|
|
27536
|
-
"buoy-super",
|
|
27537
|
-
"beacon-generic",
|
|
27538
|
-
"beacon-tower",
|
|
27539
|
-
"beacon-lattice",
|
|
27540
|
-
"beacon-stake",
|
|
27541
|
-
"beacon-cairn",
|
|
27542
|
-
"light-flare",
|
|
27543
|
-
"lightship",
|
|
27544
|
-
"daymark-triangle",
|
|
27545
|
-
"daymark-square"
|
|
27546
|
-
]);
|
|
27547
|
-
var CHRT_GLYPH_NAMES = new Set(Object.keys(CHRT_GLYPH_SVGS));
|
|
27548
|
-
var CHRT_ATON_COLORS = {
|
|
27549
|
-
"1": "#F8FAFC",
|
|
27550
|
-
// white
|
|
27551
|
-
"2": "#101418",
|
|
27552
|
-
// black
|
|
27553
|
-
"3": "#E5484D",
|
|
27554
|
-
// red
|
|
27555
|
-
"4": "#30A46C",
|
|
27556
|
-
// green
|
|
27557
|
-
"5": "#3E63DD",
|
|
27558
|
-
// blue
|
|
27559
|
-
"6": "#FFC805",
|
|
27560
|
-
// yellow
|
|
27561
|
-
"7": "#8B9298",
|
|
27562
|
-
// grey
|
|
27563
|
-
"8": "#A66B3A",
|
|
27564
|
-
// brown
|
|
27565
|
-
"9": "#FFB224",
|
|
27566
|
-
// amber
|
|
27567
|
-
"10": "#7E4CCB",
|
|
27568
|
-
// violet
|
|
27569
|
-
"11": "#F76B15",
|
|
27570
|
-
// orange
|
|
27571
|
-
"12": "#D671C1"
|
|
27572
|
-
// pink (magenta-ish)
|
|
27573
|
-
};
|
|
27574
|
-
var CHRT_UNKNOWN_TINT = "#8B9298";
|
|
27575
|
-
var HOUSE_BLACK = "#101418";
|
|
27576
|
-
function chrtHouseColor(dayHex, palette = "DAY") {
|
|
27577
|
-
if (palette === "DAY") return dayHex;
|
|
27578
|
-
const isBlack = dayHex.toLowerCase() === HOUSE_BLACK.toLowerCase();
|
|
27579
|
-
if (palette === "DUSK") {
|
|
27580
|
-
return isBlack ? dayHex : capLuminance(dayHex, relativeLuminance(resolveColor("CHBLK", "DUSK")));
|
|
27581
|
-
}
|
|
27582
|
-
return isBlack ? NIGHT_INK : capLuminance(dayHex, relativeLuminance(NIGHT_MUTED_INK));
|
|
27583
|
-
}
|
|
27584
|
-
function themeChrtGlyph(svg3, palette = "DAY") {
|
|
27585
|
-
if (palette === "DAY") return svg3;
|
|
27586
|
-
return svg3.replace(
|
|
27587
|
-
/(fill|stroke)="(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})"/g,
|
|
27588
|
-
(_m, attr, hex) => {
|
|
27589
|
-
const six = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
27590
|
-
return `${attr}="${chrtHouseColor(six, palette)}"`;
|
|
27591
|
-
}
|
|
27592
|
-
);
|
|
27593
|
-
}
|
|
27594
|
-
function chrtAtonTintExpression(palette = "DAY") {
|
|
27595
|
-
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
27596
|
-
return colourCodeCase(
|
|
27597
|
-
[
|
|
27598
|
-
["3", c("3")],
|
|
27599
|
-
["4", c("4")],
|
|
27600
|
-
["6", c("6")],
|
|
27601
|
-
["11", c("11")],
|
|
27602
|
-
["9", c("9")],
|
|
27603
|
-
["5", c("5")],
|
|
27604
|
-
["1", c("1")],
|
|
27605
|
-
["2", c("2")],
|
|
27606
|
-
["8", c("8")],
|
|
27607
|
-
["10", c("10")],
|
|
27608
|
-
["12", c("12")],
|
|
27609
|
-
["7", c("7")]
|
|
27610
|
-
],
|
|
27611
|
-
chrtHouseColor(CHRT_UNKNOWN_TINT, palette)
|
|
27612
|
-
);
|
|
27613
|
-
}
|
|
27614
|
-
function chrtLightTintExpression(palette = "DAY") {
|
|
27615
|
-
const c = (code) => chrtHouseColor(CHRT_ATON_COLORS[code], palette);
|
|
27616
|
-
return colourCodeCase(
|
|
27617
|
-
[
|
|
27618
|
-
["3", c("3")],
|
|
27619
|
-
["4", c("4")],
|
|
27620
|
-
["6", c("6")],
|
|
27621
|
-
["11", c("11")],
|
|
27622
|
-
["9", c("9")],
|
|
27623
|
-
["5", c("5")],
|
|
27624
|
-
["1", c("1")]
|
|
27625
|
-
],
|
|
27626
|
-
c("1")
|
|
27627
|
-
);
|
|
27628
|
-
}
|
|
27629
|
-
function scalar(attr) {
|
|
27630
|
-
return ["coalesce", ["get", attr], ["get", attr.toLowerCase()], 0];
|
|
27631
|
-
}
|
|
27632
|
-
var g = (name) => `${CHRT_ICON_PREFIX}${name}`;
|
|
27633
|
-
function buoyBodyExpression() {
|
|
27634
|
-
return [
|
|
27635
|
-
"match",
|
|
27636
|
-
scalar("BOYSHP"),
|
|
27637
|
-
1,
|
|
27638
|
-
g("buoy-conical"),
|
|
27639
|
-
2,
|
|
27640
|
-
g("buoy-can"),
|
|
27641
|
-
3,
|
|
27642
|
-
g("buoy-spherical"),
|
|
27643
|
-
4,
|
|
27644
|
-
g("buoy-pillar"),
|
|
27645
|
-
5,
|
|
27646
|
-
g("buoy-spar"),
|
|
27647
|
-
6,
|
|
27648
|
-
g("buoy-barrel"),
|
|
27649
|
-
7,
|
|
27650
|
-
g("buoy-super"),
|
|
27651
|
-
8,
|
|
27652
|
-
g("buoy-super"),
|
|
27653
|
-
g("buoy-pillar")
|
|
27654
|
-
];
|
|
27655
|
-
}
|
|
27656
|
-
function beaconBodyExpression() {
|
|
27657
|
-
return [
|
|
27658
|
-
"match",
|
|
27659
|
-
scalar("BCNSHP"),
|
|
27660
|
-
1,
|
|
27661
|
-
g("beacon-stake"),
|
|
27662
|
-
2,
|
|
27663
|
-
g("beacon-stake"),
|
|
27664
|
-
3,
|
|
27665
|
-
g("beacon-tower"),
|
|
27666
|
-
4,
|
|
27667
|
-
g("beacon-lattice"),
|
|
27668
|
-
5,
|
|
27669
|
-
g("beacon-stake"),
|
|
27670
|
-
6,
|
|
27671
|
-
g("beacon-cairn"),
|
|
27672
|
-
g("beacon-generic")
|
|
27673
|
-
];
|
|
28702
|
+
if (Array.isArray(node)) return node.map((n) => rewritePatternToS101(n, available));
|
|
28703
|
+
return node;
|
|
27674
28704
|
}
|
|
27675
|
-
function
|
|
27676
|
-
|
|
27677
|
-
|
|
27678
|
-
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
2,
|
|
27682
|
-
g("cardinal-e"),
|
|
27683
|
-
3,
|
|
27684
|
-
g("cardinal-s"),
|
|
27685
|
-
4,
|
|
27686
|
-
g("cardinal-w"),
|
|
27687
|
-
g("cardinal-n")
|
|
27688
|
-
];
|
|
28705
|
+
function collectLiterals(node, out) {
|
|
28706
|
+
if (typeof node === "string") {
|
|
28707
|
+
out.add(node);
|
|
28708
|
+
return;
|
|
28709
|
+
}
|
|
28710
|
+
if (Array.isArray(node)) for (const n of node) collectLiterals(n, out);
|
|
27689
28711
|
}
|
|
27690
|
-
|
|
27691
|
-
|
|
27692
|
-
BOYSPP: buoyBodyExpression,
|
|
27693
|
-
BOYINB: buoyBodyExpression,
|
|
27694
|
-
BOYCAR: cardinalExpression,
|
|
27695
|
-
BOYISD: () => g("isolated-danger"),
|
|
27696
|
-
BOYSAW: () => g("safe-water"),
|
|
27697
|
-
BCNLAT: beaconBodyExpression,
|
|
27698
|
-
BCNSPP: beaconBodyExpression,
|
|
27699
|
-
BCNCAR: cardinalExpression,
|
|
27700
|
-
BCNISD: () => g("isolated-danger"),
|
|
27701
|
-
BCNSAW: () => g("safe-water"),
|
|
27702
|
-
LIGHTS: () => g("light-flare"),
|
|
27703
|
-
LITFLT: () => g("lightship"),
|
|
27704
|
-
LITVES: () => g("lightship"),
|
|
27705
|
-
FOGSIG: () => g("badge-sound"),
|
|
27706
|
-
RTPBCN: () => g("badge-radar"),
|
|
27707
|
-
RDOSTA: () => g("badge-radar"),
|
|
27708
|
-
DAYMAR: () => [
|
|
27709
|
-
"match",
|
|
27710
|
-
scalar("TOPSHP"),
|
|
27711
|
-
1,
|
|
27712
|
-
g("daymark-triangle"),
|
|
27713
|
-
2,
|
|
27714
|
-
g("daymark-triangle"),
|
|
27715
|
-
5,
|
|
27716
|
-
g("daymark-triangle"),
|
|
27717
|
-
g("daymark-square")
|
|
27718
|
-
]
|
|
27719
|
-
};
|
|
27720
|
-
var TINTED_CLASSES = /* @__PURE__ */ new Set([
|
|
27721
|
-
"BOYLAT",
|
|
27722
|
-
"BOYSPP",
|
|
27723
|
-
"BOYINB",
|
|
27724
|
-
"BCNLAT",
|
|
27725
|
-
"BCNSPP",
|
|
27726
|
-
"DAYMAR"
|
|
27727
|
-
]);
|
|
27728
|
-
var CHRT_HALO = { color: HOUSE_BLACK, width: 1 };
|
|
27729
|
-
var CHRT_LINE_DASH = {
|
|
27730
|
-
CBLSUB: { color: "CHMGD", width: 0.8, dash: [6, 3] },
|
|
27731
|
-
PIPSOL: { color: "PIPLN", width: 0.8, dash: [6, 3] }
|
|
27732
|
-
};
|
|
27733
|
-
function applyChrtSymbolSet(style, palette = "DAY") {
|
|
28712
|
+
function applyS101SymbolSet(style) {
|
|
28713
|
+
const seen = { icon: /* @__PURE__ */ new Set(), fill: /* @__PURE__ */ new Set(), line: /* @__PURE__ */ new Set() };
|
|
27734
28714
|
for (const layer of style.layers) {
|
|
27735
|
-
if (layer.type === "line") {
|
|
27736
|
-
const cls = layer.metadata?.["s52:objectClass"];
|
|
27737
|
-
const dash = cls ? CHRT_LINE_DASH[cls] : void 0;
|
|
27738
|
-
const paint2 = layer.paint ?? {};
|
|
27739
|
-
if (dash && paint2["line-pattern"] != null) {
|
|
27740
|
-
delete paint2["line-pattern"];
|
|
27741
|
-
paint2["line-color"] = resolveColor(dash.color, palette);
|
|
27742
|
-
paint2["line-width"] = dash.width;
|
|
27743
|
-
paint2["line-dasharray"] = dash.dash;
|
|
27744
|
-
layer.paint = paint2;
|
|
27745
|
-
}
|
|
27746
|
-
continue;
|
|
27747
|
-
}
|
|
27748
|
-
if (layer.type !== "symbol") continue;
|
|
27749
|
-
const objectClass = layer.metadata?.["s52:objectClass"];
|
|
27750
|
-
if (!objectClass || !CHRT_ICON_EXPRESSIONS[objectClass]) continue;
|
|
27751
28715
|
const layout = layer.layout;
|
|
27752
|
-
|
|
27753
|
-
layout["icon-image"]
|
|
27754
|
-
|
|
27755
|
-
|
|
27756
|
-
|
|
27757
|
-
|
|
27758
|
-
|
|
27759
|
-
|
|
27760
|
-
|
|
27761
|
-
|
|
27762
|
-
|
|
28716
|
+
const paint = layer.paint;
|
|
28717
|
+
if (layer.type === "symbol" && layout && layout["icon-image"] != null) {
|
|
28718
|
+
layout["icon-image"] = rewriteIconToS101(layout["icon-image"]);
|
|
28719
|
+
collectLiterals(layout["icon-image"], seen.icon);
|
|
28720
|
+
}
|
|
28721
|
+
for (const bag of [layout, paint]) {
|
|
28722
|
+
if (!bag) continue;
|
|
28723
|
+
if (bag["fill-pattern"] != null) {
|
|
28724
|
+
bag["fill-pattern"] = rewritePatternToS101(bag["fill-pattern"], S101_AREA_FILL_NAMES);
|
|
28725
|
+
collectLiterals(bag["fill-pattern"], seen.fill);
|
|
28726
|
+
}
|
|
28727
|
+
if (bag["line-pattern"] != null) {
|
|
28728
|
+
bag["line-pattern"] = rewritePatternToS101(bag["line-pattern"], S101_LINE_STYLE_NAMES);
|
|
28729
|
+
collectLiterals(bag["line-pattern"], seen.line);
|
|
28730
|
+
}
|
|
27763
28731
|
}
|
|
27764
|
-
layer.paint = paint;
|
|
27765
28732
|
}
|
|
28733
|
+
const official = (names) => [...names].filter((n) => n.startsWith(S101_ICON_PREFIX)).length;
|
|
28734
|
+
const iconSubs = [...seen.icon].filter((n) => S101_NO_OFFICIAL_COUNTERPART.has(n)).length;
|
|
28735
|
+
const patternSubs = (names) => [...names].filter((n) => /^(AP|LC):/.test(n)).length;
|
|
28736
|
+
const coverage = {
|
|
28737
|
+
pointSymbols: { official: official(seen.icon), s52Substitute: iconSubs },
|
|
28738
|
+
areaFills: { official: official(seen.fill), s52Substitute: patternSubs(seen.fill) },
|
|
28739
|
+
lineStyles: { official: official(seen.line), s52Substitute: patternSubs(seen.line) }
|
|
28740
|
+
};
|
|
27766
28741
|
const metadata = style.metadata;
|
|
27767
|
-
metadata["chrt:symbol-set"] = "
|
|
28742
|
+
metadata["chrt:symbol-set"] = "s101";
|
|
28743
|
+
metadata["chrt:s101-source-commit"] = S101_SOURCE_COMMIT;
|
|
28744
|
+
metadata["chrt:s101-coverage"] = {
|
|
28745
|
+
...coverage,
|
|
28746
|
+
/** True only when NOTHING in this document is an S-52 substitute. A
|
|
28747
|
+
* consumer that wants "S-101 or nothing" branches on this one boolean. */
|
|
28748
|
+
complete: Object.values(coverage).every((c) => c.s52Substitute === 0)
|
|
28749
|
+
};
|
|
27768
28750
|
return style;
|
|
27769
28751
|
}
|
|
27770
28752
|
|
|
@@ -27921,9 +28903,9 @@ var RASTER_ATON_CLASSES = Object.keys(
|
|
|
27921
28903
|
if (!s52) return true;
|
|
27922
28904
|
return JSON.stringify({ r: paper.rows, d: paper.defaultSymbol }) !== JSON.stringify({ r: s52.rows, d: s52.defaultSymbol });
|
|
27923
28905
|
});
|
|
27924
|
-
function rasterAtonIconExpression(
|
|
27925
|
-
const lookup = S52_ATON_LOOKUPS_PAPER[
|
|
27926
|
-
if (!lookup) throw new Error(`raster-style: no paper lookup for ${
|
|
28906
|
+
function rasterAtonIconExpression(objectClass2) {
|
|
28907
|
+
const lookup = S52_ATON_LOOKUPS_PAPER[objectClass2];
|
|
28908
|
+
if (!lookup) throw new Error(`raster-style: no paper lookup for ${objectClass2}`);
|
|
27927
28909
|
return compileAtonExpression(lookup, resolveRasterImage);
|
|
27928
28910
|
}
|
|
27929
28911
|
function getRasterSvg(name, table = "DAY") {
|
|
@@ -27937,9 +28919,9 @@ function applyRasterSymbolSet(style) {
|
|
|
27937
28919
|
const layout = layer.layout;
|
|
27938
28920
|
if (!layout || layout["icon-image"] == null) continue;
|
|
27939
28921
|
const metadata2 = layer.metadata;
|
|
27940
|
-
const
|
|
27941
|
-
if (typeof
|
|
27942
|
-
layout["icon-image"] = rasterAtonIconExpression(
|
|
28922
|
+
const objectClass2 = metadata2?.["s52:objectClass"];
|
|
28923
|
+
if (typeof objectClass2 !== "string" || !classes.has(objectClass2)) continue;
|
|
28924
|
+
layout["icon-image"] = rasterAtonIconExpression(objectClass2);
|
|
27943
28925
|
}
|
|
27944
28926
|
const metadata = style.metadata;
|
|
27945
28927
|
metadata["chrt:symbol-set"] = "raster";
|
|
@@ -27971,19 +28953,19 @@ function parseSymbolFamily(raw) {
|
|
|
27971
28953
|
const lower = raw.toLowerCase();
|
|
27972
28954
|
return SYMBOL_FAMILIES.find((f) => f === lower) ?? null;
|
|
27973
28955
|
}
|
|
27974
|
-
function iconExpressionForFamily(
|
|
27975
|
-
const s52 = getIconExpression(
|
|
28956
|
+
function iconExpressionForFamily(objectClass2, family) {
|
|
28957
|
+
const s52 = getIconExpression(objectClass2);
|
|
27976
28958
|
switch (family) {
|
|
27977
28959
|
case "s52":
|
|
27978
28960
|
return s52;
|
|
27979
28961
|
case "s101":
|
|
27980
28962
|
return rewriteIconToS101(s52);
|
|
27981
28963
|
case "chrt": {
|
|
27982
|
-
const build = CHRT_ICON_EXPRESSIONS[
|
|
28964
|
+
const build = CHRT_ICON_EXPRESSIONS[objectClass2];
|
|
27983
28965
|
return build ? build() : s52;
|
|
27984
28966
|
}
|
|
27985
28967
|
case "raster":
|
|
27986
|
-
return RASTER_ATON_CLASSES.includes(
|
|
28968
|
+
return RASTER_ATON_CLASSES.includes(objectClass2) ? rasterAtonIconExpression(objectClass2) : s52;
|
|
27987
28969
|
}
|
|
27988
28970
|
}
|
|
27989
28971
|
|
|
@@ -28031,16 +29013,16 @@ var LARGE = {
|
|
|
28031
29013
|
WRECKS: HAZARD_DEPTH
|
|
28032
29014
|
};
|
|
28033
29015
|
var LARGE_SOUNDING_PADDING_PX = 3.5;
|
|
28034
|
-
function applyDepthLabelSize(
|
|
29016
|
+
function applyDepthLabelSize(objectClass2, layer, size) {
|
|
28035
29017
|
if (size !== "large") return layer;
|
|
28036
|
-
const large = LARGE[
|
|
29018
|
+
const large = LARGE[objectClass2];
|
|
28037
29019
|
if (large === void 0) return layer;
|
|
28038
29020
|
const layout = {
|
|
28039
29021
|
...layer.layout ?? {},
|
|
28040
29022
|
"text-size": large.textSize,
|
|
28041
29023
|
"text-font": [...large.font]
|
|
28042
29024
|
};
|
|
28043
|
-
if (
|
|
29025
|
+
if (objectClass2 === "SOUNDG") layout["text-padding"] = LARGE_SOUNDING_PADDING_PX;
|
|
28044
29026
|
return {
|
|
28045
29027
|
...layer,
|
|
28046
29028
|
layout,
|
|
@@ -28237,47 +29219,47 @@ function buildLightLabelExpression() {
|
|
|
28237
29219
|
];
|
|
28238
29220
|
return ["concat", litchrExpr, siggrpExpr, colourExpr, sigperExpr, valnmrExpr];
|
|
28239
29221
|
}
|
|
28240
|
-
function buildLayersForClass(
|
|
28241
|
-
if (!S52_LOOKUP[
|
|
28242
|
-
const entry = S52_LOOKUP[
|
|
29222
|
+
function buildLayersForClass(objectClass2, ct, sourceId = DEFAULT_REGION, opts = {}) {
|
|
29223
|
+
if (!S52_LOOKUP[objectClass2]) return [];
|
|
29224
|
+
const entry = S52_LOOKUP[objectClass2];
|
|
28243
29225
|
const layers = [];
|
|
28244
|
-
const geomTypes = getGeomTypesForClass(
|
|
29226
|
+
const geomTypes = getGeomTypesForClass(objectClass2, entry?.style.type);
|
|
28245
29227
|
for (const geomType of geomTypes) {
|
|
28246
|
-
const resolved = resolveStyle(
|
|
28247
|
-
const layerId = `s52-${
|
|
29228
|
+
const resolved = resolveStyle(objectClass2, geomType, ct, { safetyContour: opts.safetyContour });
|
|
29229
|
+
const layerId = `s52-${objectClass2}-${resolved.type}`;
|
|
28248
29230
|
const layer = {
|
|
28249
29231
|
id: layerId,
|
|
28250
29232
|
type: resolved.type,
|
|
28251
29233
|
source: sourceId,
|
|
28252
|
-
"source-layer":
|
|
29234
|
+
"source-layer": objectClass2,
|
|
28253
29235
|
paint: resolved.paint,
|
|
28254
29236
|
metadata: {
|
|
28255
|
-
"s52:objectClass":
|
|
29237
|
+
"s52:objectClass": objectClass2,
|
|
28256
29238
|
"s52:displayCategory": entry?.displayCategory ?? "OTHER",
|
|
28257
29239
|
"s52:category": entry?.category ?? "Meta"
|
|
28258
29240
|
}
|
|
28259
29241
|
};
|
|
28260
|
-
if (resolved.type === "line" && LINE_FROM_POLYGON_LAYERS.has(
|
|
29242
|
+
if (resolved.type === "line" && LINE_FROM_POLYGON_LAYERS.has(objectClass2)) {
|
|
28261
29243
|
layer.filter = GEOM_FILTERS.fill;
|
|
28262
29244
|
} else if (GEOM_FILTERS[resolved.type]) {
|
|
28263
29245
|
layer.filter = GEOM_FILTERS[resolved.type];
|
|
28264
29246
|
}
|
|
28265
|
-
if (SCAMIN_DECLUTTER_CLASSES.has(
|
|
29247
|
+
if (SCAMIN_DECLUTTER_CLASSES.has(objectClass2)) {
|
|
28266
29248
|
layer.filter = layer.filter ? ["all", layer.filter, scaminZoomGateFilter()] : ["all", scaminZoomGateFilter()];
|
|
28267
29249
|
}
|
|
28268
29250
|
if (resolved.minzoom) {
|
|
28269
29251
|
layer.minzoom = resolved.minzoom;
|
|
28270
29252
|
}
|
|
28271
|
-
if (resolved.type === "fill" && OPAQUE_FILLS.has(
|
|
29253
|
+
if (resolved.type === "fill" && OPAQUE_FILLS.has(objectClass2)) {
|
|
28272
29254
|
layer.paint["fill-opacity"] = 1;
|
|
28273
29255
|
}
|
|
28274
|
-
if (resolved.type === "line" && ZOOM_SCALED_LINES2[
|
|
28275
|
-
const [z1, w1, z2, w2] = ZOOM_SCALED_LINES2[
|
|
29256
|
+
if (resolved.type === "line" && ZOOM_SCALED_LINES2[objectClass2]) {
|
|
29257
|
+
const [z1, w1, z2, w2] = ZOOM_SCALED_LINES2[objectClass2];
|
|
28276
29258
|
layer.paint["line-width"] = ["interpolate", ["linear"], ["zoom"], z1, w1, z2, w2];
|
|
28277
29259
|
}
|
|
28278
29260
|
if (resolved.type === "symbol") {
|
|
28279
|
-
const iconExpr = getIconExpression(
|
|
28280
|
-
const childOffset = CHILD_ICON_OFFSETS[
|
|
29261
|
+
const iconExpr = getIconExpression(objectClass2);
|
|
29262
|
+
const childOffset = CHILD_ICON_OFFSETS[objectClass2];
|
|
28281
29263
|
const isSafetyCritical = SAFETY_GROUPS.has(entry?.viewingGroup ?? 0);
|
|
28282
29264
|
const safetyCriticalIconSize = [
|
|
28283
29265
|
"interpolate",
|
|
@@ -28400,7 +29382,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28400
29382
|
]
|
|
28401
29383
|
};
|
|
28402
29384
|
}
|
|
28403
|
-
if (
|
|
29385
|
+
if (objectClass2 === "LIGHTS") {
|
|
28404
29386
|
layer.layout = {
|
|
28405
29387
|
...layer.layout,
|
|
28406
29388
|
"icon-allow-overlap": true,
|
|
@@ -28442,14 +29424,14 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28442
29424
|
} else if (resolved.layout) {
|
|
28443
29425
|
layer.layout = resolved.layout;
|
|
28444
29426
|
}
|
|
28445
|
-
if (
|
|
29427
|
+
if (objectClass2 === "LNDMRK" && resolved.type === "symbol") {
|
|
28446
29428
|
const baseFilterParts = layer.filter && layer.filter[0] === "all" ? layer.filter.slice(1) : layer.filter ? [layer.filter] : [];
|
|
28447
29429
|
const isWind = listAttrHas("CATLMK", ["18", "19"]);
|
|
28448
29430
|
const isLighthouse = listAttrHas("CATLMK", ["17"]);
|
|
28449
29431
|
const scaminFloor = scaminToMinZoom(entry?.scamin);
|
|
28450
29432
|
const windLayer = {
|
|
28451
29433
|
...layer,
|
|
28452
|
-
id: `s52-${
|
|
29434
|
+
id: `s52-${objectClass2}-wind-symbol`,
|
|
28453
29435
|
filter: ["all", ...baseFilterParts, isWind],
|
|
28454
29436
|
// Explicit, not inherited: turbines share exact coordinates with
|
|
28455
29437
|
// their aviation-light LIGHTS feature; without allow-overlap the
|
|
@@ -28462,7 +29444,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28462
29444
|
delete windLayer.minzoom;
|
|
28463
29445
|
const lighthouseLayer = {
|
|
28464
29446
|
...layer,
|
|
28465
|
-
id: `s52-${
|
|
29447
|
+
id: `s52-${objectClass2}-lighthouse-symbol`,
|
|
28466
29448
|
filter: ["all", ...baseFilterParts, isLighthouse, ["!", isWind]],
|
|
28467
29449
|
layout: {
|
|
28468
29450
|
...layer.layout ?? {},
|
|
@@ -28473,7 +29455,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28473
29455
|
delete lighthouseLayer.minzoom;
|
|
28474
29456
|
const otherLayer = {
|
|
28475
29457
|
...layer,
|
|
28476
|
-
id: `s52-${
|
|
29458
|
+
id: `s52-${objectClass2}-other-symbol`,
|
|
28477
29459
|
filter: ["all", ...baseFilterParts, ["!", isWind], ["!", isLighthouse]]
|
|
28478
29460
|
};
|
|
28479
29461
|
if (scaminFloor !== void 0) {
|
|
@@ -28488,10 +29470,10 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28488
29470
|
const valsouNum = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 999], 999];
|
|
28489
29471
|
const watlevNum = ["to-number", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 0];
|
|
28490
29472
|
layers.push({
|
|
28491
|
-
id: `s52-${
|
|
29473
|
+
id: `s52-${objectClass2}-danger-ring`,
|
|
28492
29474
|
type: "symbol",
|
|
28493
29475
|
source: sourceId,
|
|
28494
|
-
"source-layer":
|
|
29476
|
+
"source-layer": objectClass2,
|
|
28495
29477
|
filter: [
|
|
28496
29478
|
"all",
|
|
28497
29479
|
GEOM_FILTERS.symbol,
|
|
@@ -28524,48 +29506,48 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28524
29506
|
},
|
|
28525
29507
|
paint: {},
|
|
28526
29508
|
minzoom: 13,
|
|
28527
|
-
metadata: { "s52:objectClass":
|
|
29509
|
+
metadata: { "s52:objectClass": objectClass2, "s52:role": "isolated-danger-ring" }
|
|
28528
29510
|
});
|
|
28529
29511
|
}
|
|
28530
|
-
if (resolved.type === "fill" && FILL_OUTLINE_LAYERS.has(
|
|
28531
|
-
const outlineColor =
|
|
29512
|
+
if (resolved.type === "fill" && FILL_OUTLINE_LAYERS.has(objectClass2)) {
|
|
29513
|
+
const outlineColor = objectClass2 === "DEPARE" ? resolveColor("CHGRD", ct) : resolveColor("CHGRF", ct);
|
|
28532
29514
|
layers.push({
|
|
28533
|
-
id: `s52-${
|
|
29515
|
+
id: `s52-${objectClass2}-outline`,
|
|
28534
29516
|
type: "line",
|
|
28535
29517
|
source: sourceId,
|
|
28536
|
-
"source-layer":
|
|
29518
|
+
"source-layer": objectClass2,
|
|
28537
29519
|
filter: GEOM_FILTERS.fill,
|
|
28538
29520
|
paint: {
|
|
28539
29521
|
"line-color": outlineColor,
|
|
28540
29522
|
"line-width": 0.3,
|
|
28541
29523
|
"line-opacity": 0.4
|
|
28542
29524
|
},
|
|
28543
|
-
metadata: { "s52:objectClass":
|
|
29525
|
+
metadata: { "s52:objectClass": objectClass2, "s52:role": "outline" }
|
|
28544
29526
|
});
|
|
28545
29527
|
}
|
|
28546
|
-
if (resolved.type === "fill" && REGULATED_AREA_BOUNDARY_LAYERS[
|
|
28547
|
-
const lineStyle = REGULATED_AREA_BOUNDARY_LINESTYLES[
|
|
29528
|
+
if (resolved.type === "fill" && REGULATED_AREA_BOUNDARY_LAYERS[objectClass2]) {
|
|
29529
|
+
const lineStyle = REGULATED_AREA_BOUNDARY_LINESTYLES[objectClass2];
|
|
28548
29530
|
const boundaryPaint = lineStyle ? lineStylePaint(lineStyle) : {
|
|
28549
|
-
"line-color": resolveColor(REGULATED_AREA_BOUNDARY_LAYERS[
|
|
29531
|
+
"line-color": resolveColor(REGULATED_AREA_BOUNDARY_LAYERS[objectClass2], ct),
|
|
28550
29532
|
"line-width": 1,
|
|
28551
29533
|
"line-dasharray": [4, 2],
|
|
28552
29534
|
"line-opacity": 0.85
|
|
28553
29535
|
};
|
|
28554
29536
|
layers.push({
|
|
28555
|
-
id: `s52-${
|
|
29537
|
+
id: `s52-${objectClass2}-boundary`,
|
|
28556
29538
|
type: "line",
|
|
28557
29539
|
source: sourceId,
|
|
28558
|
-
"source-layer":
|
|
29540
|
+
"source-layer": objectClass2,
|
|
28559
29541
|
filter: GEOM_FILTERS.fill,
|
|
28560
29542
|
paint: boundaryPaint,
|
|
28561
29543
|
metadata: {
|
|
28562
|
-
"s52:objectClass":
|
|
29544
|
+
"s52:objectClass": objectClass2,
|
|
28563
29545
|
"s52:role": "boundary",
|
|
28564
29546
|
...lineStyle ? { "s52:lineStyle": lineStyle } : {}
|
|
28565
29547
|
}
|
|
28566
29548
|
});
|
|
28567
29549
|
}
|
|
28568
|
-
if (
|
|
29550
|
+
if (objectClass2 === "ACHARE" && resolved.type === "fill") {
|
|
28569
29551
|
layers.push({
|
|
28570
29552
|
id: "s52-ACHARE-boundary",
|
|
28571
29553
|
type: "line",
|
|
@@ -28581,7 +29563,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28581
29563
|
metadata: { "s52:objectClass": "ACHARE", "s52:role": "boundary" }
|
|
28582
29564
|
});
|
|
28583
29565
|
}
|
|
28584
|
-
if (
|
|
29566
|
+
if (objectClass2 === "DRGARE" && resolved.type === "fill") {
|
|
28585
29567
|
layers.push({
|
|
28586
29568
|
id: "s52-DRGARE-boundary",
|
|
28587
29569
|
type: "line",
|
|
@@ -28597,7 +29579,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28597
29579
|
metadata: { "s52:objectClass": "DRGARE", "s52:role": "boundary" }
|
|
28598
29580
|
});
|
|
28599
29581
|
}
|
|
28600
|
-
if (
|
|
29582
|
+
if (objectClass2 === "ACHARE" && resolved.type === "fill") {
|
|
28601
29583
|
layers.push({
|
|
28602
29584
|
id: "s52-ACHARE-anchor-icon",
|
|
28603
29585
|
type: "symbol",
|
|
@@ -28626,7 +29608,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28626
29608
|
metadata: { "s52:objectClass": "ACHARE", "s52:role": "centroid-icon" }
|
|
28627
29609
|
});
|
|
28628
29610
|
}
|
|
28629
|
-
if (
|
|
29611
|
+
if (objectClass2 === "LNDARE" && resolved.type === "fill") {
|
|
28630
29612
|
layers.push({
|
|
28631
29613
|
id: `s52-LNDARE-point`,
|
|
28632
29614
|
type: "circle",
|
|
@@ -28642,7 +29624,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28642
29624
|
metadata: { "s52:objectClass": "LNDARE", "s52:role": "point" }
|
|
28643
29625
|
});
|
|
28644
29626
|
}
|
|
28645
|
-
if (
|
|
29627
|
+
if (objectClass2 === "LNDARE" && resolved.type === "fill") {
|
|
28646
29628
|
layers.push({
|
|
28647
29629
|
id: `s52-LNDARE-line`,
|
|
28648
29630
|
type: "line",
|
|
@@ -28656,7 +29638,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28656
29638
|
metadata: { "s52:objectClass": "LNDARE", "s52:role": "line" }
|
|
28657
29639
|
});
|
|
28658
29640
|
}
|
|
28659
|
-
if (
|
|
29641
|
+
if (objectClass2 === "BUAARE" && resolved.type === "fill") {
|
|
28660
29642
|
layers.push({
|
|
28661
29643
|
id: `s52-BUAARE-point`,
|
|
28662
29644
|
type: "circle",
|
|
@@ -28672,7 +29654,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28672
29654
|
metadata: { "s52:objectClass": "BUAARE", "s52:role": "point" }
|
|
28673
29655
|
});
|
|
28674
29656
|
}
|
|
28675
|
-
if (
|
|
29657
|
+
if (objectClass2 === "RIVERS" && resolved.type === "fill") {
|
|
28676
29658
|
layers.push({
|
|
28677
29659
|
id: `s52-RIVERS-line`,
|
|
28678
29660
|
type: "line",
|
|
@@ -28686,7 +29668,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28686
29668
|
metadata: { "s52:objectClass": "RIVERS", "s52:role": "line" }
|
|
28687
29669
|
});
|
|
28688
29670
|
}
|
|
28689
|
-
if (
|
|
29671
|
+
if (objectClass2 === "BUISGL" && resolved.type === "circle") {
|
|
28690
29672
|
layers.push({
|
|
28691
29673
|
id: `s52-BUISGL-polygon`,
|
|
28692
29674
|
type: "fill",
|
|
@@ -28712,7 +29694,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28712
29694
|
metadata: { "s52:objectClass": "BUISGL", "s52:role": "polygon-outline" }
|
|
28713
29695
|
});
|
|
28714
29696
|
}
|
|
28715
|
-
if (
|
|
29697
|
+
if (objectClass2 === "BRIDGE" && resolved.type === "line") {
|
|
28716
29698
|
layers.push({
|
|
28717
29699
|
id: `s52-BRIDGE-polygon`,
|
|
28718
29700
|
type: "fill",
|
|
@@ -28738,7 +29720,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28738
29720
|
metadata: { "s52:objectClass": "BRIDGE", "s52:role": "polygon-outline" }
|
|
28739
29721
|
});
|
|
28740
29722
|
}
|
|
28741
|
-
if (
|
|
29723
|
+
if (objectClass2 === "PYLONS" && resolved.type === "fill") {
|
|
28742
29724
|
layers.push({
|
|
28743
29725
|
id: `s52-PYLONS-point`,
|
|
28744
29726
|
type: "circle",
|
|
@@ -28754,7 +29736,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28754
29736
|
metadata: { "s52:objectClass": "PYLONS", "s52:role": "point" }
|
|
28755
29737
|
});
|
|
28756
29738
|
}
|
|
28757
|
-
if (
|
|
29739
|
+
if (objectClass2 === "WEDKLP" && resolved.type === "fill") {
|
|
28758
29740
|
layers.push({
|
|
28759
29741
|
id: `s52-WEDKLP-point`,
|
|
28760
29742
|
type: "circle",
|
|
@@ -28771,7 +29753,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28771
29753
|
metadata: { "s52:objectClass": "WEDKLP", "s52:role": "point" }
|
|
28772
29754
|
});
|
|
28773
29755
|
}
|
|
28774
|
-
if (
|
|
29756
|
+
if (objectClass2 === "SLOGRD" && resolved.type === "fill") {
|
|
28775
29757
|
layers.push({
|
|
28776
29758
|
id: `s52-SLOGRD-point`,
|
|
28777
29759
|
type: "circle",
|
|
@@ -28788,7 +29770,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28788
29770
|
metadata: { "s52:objectClass": "SLOGRD", "s52:role": "point" }
|
|
28789
29771
|
});
|
|
28790
29772
|
}
|
|
28791
|
-
if (
|
|
29773
|
+
if (objectClass2 === "RUNWAY" && resolved.type === "fill") {
|
|
28792
29774
|
layers.push({
|
|
28793
29775
|
id: `s52-RUNWAY-line`,
|
|
28794
29776
|
type: "line",
|
|
@@ -28802,7 +29784,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28802
29784
|
metadata: { "s52:objectClass": "RUNWAY", "s52:role": "line" }
|
|
28803
29785
|
});
|
|
28804
29786
|
}
|
|
28805
|
-
if (
|
|
29787
|
+
if (objectClass2 === "FORSTC" && resolved.type === "fill") {
|
|
28806
29788
|
layers.push({
|
|
28807
29789
|
id: `s52-FORSTC-line`,
|
|
28808
29790
|
type: "line",
|
|
@@ -28816,7 +29798,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28816
29798
|
metadata: { "s52:objectClass": "FORSTC", "s52:role": "line" }
|
|
28817
29799
|
});
|
|
28818
29800
|
}
|
|
28819
|
-
if (
|
|
29801
|
+
if (objectClass2 === "FSHFAC" && resolved.type === "fill") {
|
|
28820
29802
|
layers.push({
|
|
28821
29803
|
id: `s52-FSHFAC-point`,
|
|
28822
29804
|
type: "circle",
|
|
@@ -28832,7 +29814,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28832
29814
|
metadata: { "s52:objectClass": "FSHFAC", "s52:role": "point" }
|
|
28833
29815
|
});
|
|
28834
29816
|
}
|
|
28835
|
-
if (
|
|
29817
|
+
if (objectClass2 === "OBSTRN" && resolved.type === "symbol") {
|
|
28836
29818
|
layers.push({
|
|
28837
29819
|
id: `s52-OBSTRN-polygon`,
|
|
28838
29820
|
type: "fill",
|
|
@@ -28872,7 +29854,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28872
29854
|
metadata: { "s52:objectClass": "OBSTRN", "s52:role": "line" }
|
|
28873
29855
|
});
|
|
28874
29856
|
}
|
|
28875
|
-
if (
|
|
29857
|
+
if (objectClass2 === "SLCONS" && resolved.type === "line") {
|
|
28876
29858
|
layers.push({
|
|
28877
29859
|
id: `s52-SLCONS-polygon`,
|
|
28878
29860
|
type: "fill",
|
|
@@ -28912,7 +29894,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28912
29894
|
metadata: { "s52:objectClass": "SLCONS", "s52:role": "point" }
|
|
28913
29895
|
});
|
|
28914
29896
|
}
|
|
28915
|
-
if (
|
|
29897
|
+
if (objectClass2 === "PRDARE" && resolved.type === "line") {
|
|
28916
29898
|
layers.push({
|
|
28917
29899
|
id: `s52-PRDARE-point`,
|
|
28918
29900
|
type: "circle",
|
|
@@ -28929,7 +29911,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28929
29911
|
metadata: { "s52:objectClass": "PRDARE", "s52:role": "point" }
|
|
28930
29912
|
});
|
|
28931
29913
|
}
|
|
28932
|
-
if (
|
|
29914
|
+
if (objectClass2 === "CTNARE" && resolved.type === "line") {
|
|
28933
29915
|
layers.push({
|
|
28934
29916
|
id: `s52-CTNARE-point`,
|
|
28935
29917
|
type: "circle",
|
|
@@ -28945,7 +29927,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28945
29927
|
metadata: { "s52:objectClass": "CTNARE", "s52:role": "point" }
|
|
28946
29928
|
});
|
|
28947
29929
|
}
|
|
28948
|
-
if (
|
|
29930
|
+
if (objectClass2 === "PONTON" && resolved.type === "line") {
|
|
28949
29931
|
layers.push({
|
|
28950
29932
|
id: `s52-PONTON-polygon`,
|
|
28951
29933
|
type: "fill",
|
|
@@ -28971,7 +29953,7 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28971
29953
|
metadata: { "s52:objectClass": "PONTON", "s52:role": "polygon-outline" }
|
|
28972
29954
|
});
|
|
28973
29955
|
}
|
|
28974
|
-
if (
|
|
29956
|
+
if (objectClass2 === "WRECKS" && resolved.type === "symbol") {
|
|
28975
29957
|
layers.push({
|
|
28976
29958
|
id: `s52-WRECKS-polygon`,
|
|
28977
29959
|
type: "fill",
|
|
@@ -28985,1179 +29967,729 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION, opts =
|
|
|
28985
29967
|
metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon" }
|
|
28986
29968
|
});
|
|
28987
29969
|
layers.push({
|
|
28988
|
-
id: `s52-WRECKS-polygon-outline`,
|
|
28989
|
-
type: "line",
|
|
28990
|
-
source: sourceId,
|
|
28991
|
-
"source-layer": "WRECKS",
|
|
28992
|
-
filter: GEOM_FILTERS.fill,
|
|
28993
|
-
paint: {
|
|
28994
|
-
"line-color": resolveColor("CHBLK", ct),
|
|
28995
|
-
"line-width": 1,
|
|
28996
|
-
"line-dasharray": [3, 2]
|
|
28997
|
-
},
|
|
28998
|
-
metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon-outline" }
|
|
28999
|
-
});
|
|
29000
|
-
}
|
|
29001
|
-
if (objectClass === "WATTUR" && resolved.type === "symbol") {
|
|
29002
|
-
layers.push({
|
|
29003
|
-
id: `s52-WATTUR-polygon`,
|
|
29004
|
-
type: "fill",
|
|
29005
|
-
source: sourceId,
|
|
29006
|
-
"source-layer": "WATTUR",
|
|
29007
|
-
filter: GEOM_FILTERS.fill,
|
|
29008
|
-
paint: {
|
|
29009
|
-
"fill-color": resolveColor("CHBLK", ct),
|
|
29010
|
-
"fill-opacity": 0.05
|
|
29011
|
-
},
|
|
29012
|
-
metadata: { "s52:objectClass": "WATTUR", "s52:role": "polygon" }
|
|
29013
|
-
});
|
|
29014
|
-
}
|
|
29015
|
-
if (objectClass === "MAGVAR" && resolved.type === "line") {
|
|
29016
|
-
layers.push({
|
|
29017
|
-
id: "s52-MAGVAR-point",
|
|
29018
|
-
type: "circle",
|
|
29019
|
-
source: sourceId,
|
|
29020
|
-
"source-layer": "MAGVAR",
|
|
29021
|
-
filter: GEOM_FILTERS.circle,
|
|
29022
|
-
paint: {
|
|
29023
|
-
"circle-color": resolveColor("CHMGD", ct),
|
|
29024
|
-
"circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 1, 12, 2],
|
|
29025
|
-
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
29026
|
-
"circle-stroke-width": 0.4,
|
|
29027
|
-
"circle-opacity": 0.4
|
|
29028
|
-
},
|
|
29029
|
-
metadata: { "s52:objectClass": "MAGVAR", "s52:role": "point" }
|
|
29030
|
-
});
|
|
29031
|
-
}
|
|
29032
|
-
if (objectClass === "DMPGRD" && resolved.type === "fill") {
|
|
29033
|
-
layers.push({
|
|
29034
|
-
id: `s52-DMPGRD-point`,
|
|
29035
|
-
type: "circle",
|
|
29036
|
-
source: sourceId,
|
|
29037
|
-
"source-layer": "DMPGRD",
|
|
29038
|
-
filter: GEOM_FILTERS.circle,
|
|
29039
|
-
paint: {
|
|
29040
|
-
"circle-color": resolveColor("LITYW", ct),
|
|
29041
|
-
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 2.5, 17, 3.6],
|
|
29042
|
-
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
29043
|
-
"circle-stroke-width": 0.5,
|
|
29044
|
-
"circle-opacity": 0.7
|
|
29045
|
-
},
|
|
29046
|
-
metadata: { "s52:objectClass": "DMPGRD", "s52:role": "point" }
|
|
29047
|
-
});
|
|
29048
|
-
}
|
|
29049
|
-
if (objectClass === "DAMCON" && resolved.type === "line") {
|
|
29050
|
-
layers.push({
|
|
29051
|
-
id: `s52-DAMCON-polygon`,
|
|
29052
|
-
type: "fill",
|
|
29053
|
-
source: sourceId,
|
|
29054
|
-
"source-layer": "DAMCON",
|
|
29055
|
-
filter: GEOM_FILTERS.fill,
|
|
29056
|
-
paint: {
|
|
29057
|
-
"fill-color": resolveColor("CHBRN", ct),
|
|
29058
|
-
"fill-opacity": 0.4
|
|
29059
|
-
},
|
|
29060
|
-
metadata: { "s52:objectClass": "DAMCON", "s52:role": "polygon" }
|
|
29061
|
-
});
|
|
29062
|
-
layers.push({
|
|
29063
|
-
id: `s52-DAMCON-polygon-outline`,
|
|
29064
|
-
type: "line",
|
|
29065
|
-
source: sourceId,
|
|
29066
|
-
"source-layer": "DAMCON",
|
|
29067
|
-
filter: GEOM_FILTERS.fill,
|
|
29068
|
-
paint: {
|
|
29069
|
-
"line-color": resolveColor("CHBLK", ct),
|
|
29070
|
-
"line-width": 0.6
|
|
29071
|
-
},
|
|
29072
|
-
metadata: { "s52:objectClass": "DAMCON", "s52:role": "outline" }
|
|
29073
|
-
});
|
|
29074
|
-
}
|
|
29075
|
-
if (objectClass === "GATCON" && resolved.type === "line") {
|
|
29076
|
-
layers.push({
|
|
29077
|
-
id: `s52-GATCON-polygon`,
|
|
29078
|
-
type: "fill",
|
|
29079
|
-
source: sourceId,
|
|
29080
|
-
"source-layer": "GATCON",
|
|
29081
|
-
filter: GEOM_FILTERS.fill,
|
|
29082
|
-
paint: {
|
|
29083
|
-
"fill-color": resolveColor("CHBRN", ct),
|
|
29084
|
-
"fill-opacity": 0.4
|
|
29085
|
-
},
|
|
29086
|
-
metadata: { "s52:objectClass": "GATCON", "s52:role": "polygon" }
|
|
29087
|
-
});
|
|
29088
|
-
layers.push({
|
|
29089
|
-
id: `s52-GATCON-polygon-outline`,
|
|
29090
|
-
type: "line",
|
|
29091
|
-
source: sourceId,
|
|
29092
|
-
"source-layer": "GATCON",
|
|
29093
|
-
filter: GEOM_FILTERS.fill,
|
|
29094
|
-
paint: {
|
|
29095
|
-
"line-color": resolveColor("CHBLK", ct),
|
|
29096
|
-
"line-width": 0.6
|
|
29097
|
-
},
|
|
29098
|
-
metadata: { "s52:objectClass": "GATCON", "s52:role": "outline" }
|
|
29099
|
-
});
|
|
29100
|
-
}
|
|
29101
|
-
if (objectClass === "CANALS" && resolved.type === "line") {
|
|
29102
|
-
layers.push({
|
|
29103
|
-
id: `s52-CANALS-polygon`,
|
|
29104
|
-
type: "fill",
|
|
29105
|
-
source: sourceId,
|
|
29106
|
-
"source-layer": "CANALS",
|
|
29107
|
-
filter: GEOM_FILTERS.fill,
|
|
29108
|
-
paint: {
|
|
29109
|
-
"fill-color": resolveColor("DEPMS", ct),
|
|
29110
|
-
"fill-opacity": 0.5
|
|
29111
|
-
},
|
|
29112
|
-
metadata: { "s52:objectClass": "CANALS", "s52:role": "polygon" }
|
|
29113
|
-
});
|
|
29114
|
-
}
|
|
29115
|
-
if (objectClass === "PILBOP" && resolved.type === "symbol") {
|
|
29116
|
-
layers.push({
|
|
29117
|
-
id: `s52-PILBOP-polygon`,
|
|
29118
|
-
type: "fill",
|
|
29119
|
-
source: sourceId,
|
|
29120
|
-
"source-layer": "PILBOP",
|
|
29121
|
-
filter: GEOM_FILTERS.fill,
|
|
29122
|
-
paint: {
|
|
29123
|
-
"fill-color": resolveColor("CHMGD", ct),
|
|
29124
|
-
"fill-opacity": 0.1
|
|
29125
|
-
},
|
|
29126
|
-
metadata: { "s52:objectClass": "PILBOP", "s52:role": "polygon" }
|
|
29127
|
-
});
|
|
29128
|
-
layers.push({
|
|
29129
|
-
id: `s52-PILBOP-polygon-outline`,
|
|
29970
|
+
id: `s52-WRECKS-polygon-outline`,
|
|
29130
29971
|
type: "line",
|
|
29131
29972
|
source: sourceId,
|
|
29132
|
-
"source-layer": "
|
|
29973
|
+
"source-layer": "WRECKS",
|
|
29133
29974
|
filter: GEOM_FILTERS.fill,
|
|
29134
29975
|
paint: {
|
|
29135
|
-
"line-color": resolveColor("
|
|
29136
|
-
"line-width":
|
|
29137
|
-
"line-dasharray": [
|
|
29976
|
+
"line-color": resolveColor("CHBLK", ct),
|
|
29977
|
+
"line-width": 1,
|
|
29978
|
+
"line-dasharray": [3, 2]
|
|
29138
29979
|
},
|
|
29139
|
-
metadata: { "s52:objectClass": "
|
|
29980
|
+
metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon-outline" }
|
|
29140
29981
|
});
|
|
29141
29982
|
}
|
|
29142
|
-
if (
|
|
29983
|
+
if (objectClass2 === "WATTUR" && resolved.type === "symbol") {
|
|
29143
29984
|
layers.push({
|
|
29144
|
-
id: `s52-
|
|
29985
|
+
id: `s52-WATTUR-polygon`,
|
|
29145
29986
|
type: "fill",
|
|
29146
29987
|
source: sourceId,
|
|
29147
|
-
"source-layer": "
|
|
29988
|
+
"source-layer": "WATTUR",
|
|
29148
29989
|
filter: GEOM_FILTERS.fill,
|
|
29149
29990
|
paint: {
|
|
29150
|
-
"fill-color": resolveColor("
|
|
29151
|
-
"fill-opacity": 0.
|
|
29991
|
+
"fill-color": resolveColor("CHBLK", ct),
|
|
29992
|
+
"fill-opacity": 0.05
|
|
29152
29993
|
},
|
|
29153
|
-
metadata: { "s52:objectClass": "
|
|
29994
|
+
metadata: { "s52:objectClass": "WATTUR", "s52:role": "polygon" }
|
|
29154
29995
|
});
|
|
29155
29996
|
}
|
|
29156
|
-
if (
|
|
29997
|
+
if (objectClass2 === "MAGVAR" && resolved.type === "line") {
|
|
29157
29998
|
layers.push({
|
|
29158
|
-
id:
|
|
29999
|
+
id: "s52-MAGVAR-point",
|
|
29159
30000
|
type: "circle",
|
|
29160
30001
|
source: sourceId,
|
|
29161
|
-
"source-layer": "
|
|
30002
|
+
"source-layer": "MAGVAR",
|
|
29162
30003
|
filter: GEOM_FILTERS.circle,
|
|
29163
30004
|
paint: {
|
|
29164
|
-
"circle-color": resolveColor("
|
|
29165
|
-
"circle-radius": ["interpolate", ["linear"], ["zoom"],
|
|
30005
|
+
"circle-color": resolveColor("CHMGD", ct),
|
|
30006
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 1, 12, 2],
|
|
29166
30007
|
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
29167
30008
|
"circle-stroke-width": 0.4,
|
|
29168
|
-
"circle-opacity": 0.
|
|
30009
|
+
"circle-opacity": 0.4
|
|
29169
30010
|
},
|
|
29170
|
-
metadata: { "s52:objectClass": "
|
|
30011
|
+
metadata: { "s52:objectClass": "MAGVAR", "s52:role": "point" }
|
|
29171
30012
|
});
|
|
30013
|
+
}
|
|
30014
|
+
if (objectClass2 === "DMPGRD" && resolved.type === "fill") {
|
|
29172
30015
|
layers.push({
|
|
29173
|
-
id: `s52-
|
|
29174
|
-
type: "
|
|
30016
|
+
id: `s52-DMPGRD-point`,
|
|
30017
|
+
type: "circle",
|
|
29175
30018
|
source: sourceId,
|
|
29176
|
-
"source-layer": "
|
|
29177
|
-
filter: GEOM_FILTERS.
|
|
30019
|
+
"source-layer": "DMPGRD",
|
|
30020
|
+
filter: GEOM_FILTERS.circle,
|
|
29178
30021
|
paint: {
|
|
29179
|
-
"
|
|
29180
|
-
"
|
|
29181
|
-
"
|
|
30022
|
+
"circle-color": resolveColor("LITYW", ct),
|
|
30023
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 2.5, 17, 3.6],
|
|
30024
|
+
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
30025
|
+
"circle-stroke-width": 0.5,
|
|
30026
|
+
"circle-opacity": 0.7
|
|
29182
30027
|
},
|
|
29183
|
-
metadata: { "s52:objectClass": "
|
|
30028
|
+
metadata: { "s52:objectClass": "DMPGRD", "s52:role": "point" }
|
|
29184
30029
|
});
|
|
29185
30030
|
}
|
|
29186
|
-
if (
|
|
30031
|
+
if (objectClass2 === "DAMCON" && resolved.type === "line") {
|
|
29187
30032
|
layers.push({
|
|
29188
|
-
id: `s52-
|
|
30033
|
+
id: `s52-DAMCON-polygon`,
|
|
29189
30034
|
type: "fill",
|
|
29190
30035
|
source: sourceId,
|
|
29191
|
-
"source-layer": "
|
|
30036
|
+
"source-layer": "DAMCON",
|
|
29192
30037
|
filter: GEOM_FILTERS.fill,
|
|
29193
30038
|
paint: {
|
|
29194
|
-
"fill-color": resolveColor("
|
|
29195
|
-
"fill-opacity": 0.
|
|
30039
|
+
"fill-color": resolveColor("CHBRN", ct),
|
|
30040
|
+
"fill-opacity": 0.4
|
|
29196
30041
|
},
|
|
29197
|
-
metadata: { "s52:objectClass": "
|
|
30042
|
+
metadata: { "s52:objectClass": "DAMCON", "s52:role": "polygon" }
|
|
29198
30043
|
});
|
|
29199
|
-
}
|
|
29200
|
-
if (objectClass === "PONTON" && resolved.type === "fill") {
|
|
29201
30044
|
layers.push({
|
|
29202
|
-
id: `s52-
|
|
30045
|
+
id: `s52-DAMCON-polygon-outline`,
|
|
29203
30046
|
type: "line",
|
|
29204
30047
|
source: sourceId,
|
|
29205
|
-
"source-layer": "
|
|
29206
|
-
filter: GEOM_FILTERS.
|
|
30048
|
+
"source-layer": "DAMCON",
|
|
30049
|
+
filter: GEOM_FILTERS.fill,
|
|
29207
30050
|
paint: {
|
|
29208
|
-
"line-color": resolveColor("
|
|
29209
|
-
"line-width":
|
|
30051
|
+
"line-color": resolveColor("CHBLK", ct),
|
|
30052
|
+
"line-width": 0.6
|
|
29210
30053
|
},
|
|
29211
|
-
metadata: { "s52:objectClass": "
|
|
30054
|
+
metadata: { "s52:objectClass": "DAMCON", "s52:role": "outline" }
|
|
29212
30055
|
});
|
|
29213
30056
|
}
|
|
29214
|
-
if (
|
|
30057
|
+
if (objectClass2 === "GATCON" && resolved.type === "line") {
|
|
29215
30058
|
layers.push({
|
|
29216
|
-
id: `s52-
|
|
29217
|
-
type: "
|
|
30059
|
+
id: `s52-GATCON-polygon`,
|
|
30060
|
+
type: "fill",
|
|
29218
30061
|
source: sourceId,
|
|
29219
|
-
"source-layer": "
|
|
30062
|
+
"source-layer": "GATCON",
|
|
29220
30063
|
filter: GEOM_FILTERS.fill,
|
|
29221
|
-
|
|
29222
|
-
"
|
|
29223
|
-
"
|
|
29224
|
-
"icon-allow-overlap": true,
|
|
29225
|
-
"icon-ignore-placement": false,
|
|
29226
|
-
"symbol-placement": "point"
|
|
30064
|
+
paint: {
|
|
30065
|
+
"fill-color": resolveColor("CHBRN", ct),
|
|
30066
|
+
"fill-opacity": 0.4
|
|
29227
30067
|
},
|
|
29228
|
-
|
|
29229
|
-
metadata: { "s52:objectClass": "SILTNK", "s52:role": "symbol-polygon" }
|
|
30068
|
+
metadata: { "s52:objectClass": "GATCON", "s52:role": "polygon" }
|
|
29230
30069
|
});
|
|
29231
|
-
}
|
|
29232
|
-
if (objectClass === "ACHBRT" && resolved.type === "symbol") {
|
|
29233
30070
|
layers.push({
|
|
29234
|
-
id: `s52-
|
|
29235
|
-
type: "
|
|
30071
|
+
id: `s52-GATCON-polygon-outline`,
|
|
30072
|
+
type: "line",
|
|
29236
30073
|
source: sourceId,
|
|
29237
|
-
"source-layer": "
|
|
30074
|
+
"source-layer": "GATCON",
|
|
29238
30075
|
filter: GEOM_FILTERS.fill,
|
|
29239
|
-
layout: {
|
|
29240
|
-
"icon-image": "ACHARE02",
|
|
29241
|
-
"icon-size": 0.5,
|
|
29242
|
-
"icon-allow-overlap": true,
|
|
29243
|
-
"icon-ignore-placement": false,
|
|
29244
|
-
"symbol-placement": "point"
|
|
29245
|
-
},
|
|
29246
|
-
paint: {},
|
|
29247
|
-
metadata: { "s52:objectClass": "ACHBRT", "s52:role": "symbol-polygon" }
|
|
29248
|
-
});
|
|
29249
|
-
}
|
|
29250
|
-
if (objectClass === "BERTHS" && resolved.type === "line") {
|
|
29251
|
-
layers.push({
|
|
29252
|
-
id: `s52-BERTHS-circle`,
|
|
29253
|
-
type: "circle",
|
|
29254
|
-
source: sourceId,
|
|
29255
|
-
"source-layer": "BERTHS",
|
|
29256
|
-
filter: GEOM_FILTERS.circle,
|
|
29257
30076
|
paint: {
|
|
29258
|
-
"
|
|
29259
|
-
"
|
|
29260
|
-
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
29261
|
-
"circle-stroke-width": 0.5
|
|
30077
|
+
"line-color": resolveColor("CHBLK", ct),
|
|
30078
|
+
"line-width": 0.6
|
|
29262
30079
|
},
|
|
29263
|
-
metadata: { "s52:objectClass": "
|
|
30080
|
+
metadata: { "s52:objectClass": "GATCON", "s52:role": "outline" }
|
|
29264
30081
|
});
|
|
29265
30082
|
}
|
|
29266
|
-
if (
|
|
30083
|
+
if (objectClass2 === "CANALS" && resolved.type === "line") {
|
|
29267
30084
|
layers.push({
|
|
29268
|
-
|
|
29269
|
-
|
|
29270
|
-
// are a Mapbox style-spec violation (caught by style-integrity.test).
|
|
29271
|
-
id: `s52-TUNNEL-linestring`,
|
|
29272
|
-
type: "line",
|
|
30085
|
+
id: `s52-CANALS-polygon`,
|
|
30086
|
+
type: "fill",
|
|
29273
30087
|
source: sourceId,
|
|
29274
|
-
"source-layer": "
|
|
29275
|
-
filter: GEOM_FILTERS.
|
|
30088
|
+
"source-layer": "CANALS",
|
|
30089
|
+
filter: GEOM_FILTERS.fill,
|
|
29276
30090
|
paint: {
|
|
29277
|
-
"
|
|
29278
|
-
"
|
|
29279
|
-
"line-dasharray": [2, 2]
|
|
30091
|
+
"fill-color": resolveColor("DEPMS", ct),
|
|
30092
|
+
"fill-opacity": 0.5
|
|
29280
30093
|
},
|
|
29281
|
-
metadata: { "s52:objectClass": "
|
|
29282
|
-
});
|
|
29283
|
-
}
|
|
29284
|
-
}
|
|
29285
|
-
return layers;
|
|
29286
|
-
}
|
|
29287
|
-
function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
|
|
29288
|
-
if (!LABELED_LAYERS.has(objectClass)) return null;
|
|
29289
|
-
const config = getLabelConfig(objectClass, {
|
|
29290
|
-
colorTable: ct,
|
|
29291
|
-
safetyContour: labelOpts.safetyContour,
|
|
29292
|
-
twoShadeSoundings: labelOpts.twoShadeSoundings,
|
|
29293
|
-
soundingFontWeight: labelOpts.soundingFontWeight
|
|
29294
|
-
});
|
|
29295
|
-
if (!config) return null;
|
|
29296
|
-
let textField = config.textField;
|
|
29297
|
-
if (objectClass === "LIGHTS") {
|
|
29298
|
-
textField = buildLightLabelExpression();
|
|
29299
|
-
}
|
|
29300
|
-
if (typeof textField !== "string" && Array.isArray(textField)) {
|
|
29301
|
-
const flat = JSON.stringify(textField);
|
|
29302
|
-
if (flat.includes("_navAidLabel")) {
|
|
29303
|
-
textField = buildNavAidLabelExpression();
|
|
29304
|
-
}
|
|
29305
|
-
}
|
|
29306
|
-
if (depthUnits === "feet") {
|
|
29307
|
-
if (objectClass === "SOUNDG") {
|
|
29308
|
-
textField = [
|
|
29309
|
-
"concat",
|
|
29310
|
-
[
|
|
29311
|
-
"round",
|
|
29312
|
-
[
|
|
29313
|
-
"*",
|
|
29314
|
-
[
|
|
29315
|
-
"to-number",
|
|
29316
|
-
["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
|
|
29317
|
-
0
|
|
29318
|
-
],
|
|
29319
|
-
3.28084
|
|
29320
|
-
]
|
|
29321
|
-
],
|
|
29322
|
-
""
|
|
29323
|
-
];
|
|
29324
|
-
} else if (objectClass === "DEPCNT") {
|
|
29325
|
-
textField = [
|
|
29326
|
-
"concat",
|
|
29327
|
-
["round", ["*", ["to-number", ["get", "VALDCO"]], 3.28084]],
|
|
29328
|
-
"ft"
|
|
29329
|
-
];
|
|
29330
|
-
} else if (objectClass === "UWTROC" || objectClass === "OBSTRN" || objectClass === "WRECKS") {
|
|
29331
|
-
const valsouFt = [
|
|
29332
|
-
"to-number",
|
|
29333
|
-
["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0],
|
|
29334
|
-
0
|
|
29335
|
-
];
|
|
29336
|
-
textField = [
|
|
29337
|
-
"case",
|
|
29338
|
-
[">", valsouFt, 0],
|
|
29339
|
-
["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
|
|
29340
|
-
""
|
|
29341
|
-
];
|
|
29342
|
-
}
|
|
29343
|
-
}
|
|
29344
|
-
const labelPriority = getLabelPriority(objectClass);
|
|
29345
|
-
const layer = {
|
|
29346
|
-
id: `s52-${objectClass}-label`,
|
|
29347
|
-
type: "symbol",
|
|
29348
|
-
source: sourceId,
|
|
29349
|
-
"source-layer": objectClass,
|
|
29350
|
-
filter: GEOM_FILTERS.symbol,
|
|
29351
|
-
layout: {
|
|
29352
|
-
"text-field": textField,
|
|
29353
|
-
"text-size": config.textSize,
|
|
29354
|
-
"text-font": config.textFont ?? ["Open Sans Regular"],
|
|
29355
|
-
"text-offset": config.textOffset,
|
|
29356
|
-
"text-anchor": "top",
|
|
29357
|
-
"text-allow-overlap": false,
|
|
29358
|
-
"text-optional": true,
|
|
29359
|
-
"text-padding": 4,
|
|
29360
|
-
"symbol-sort-key": labelPriority * 1e3,
|
|
29361
|
-
...config.layout ?? {}
|
|
29362
|
-
},
|
|
29363
|
-
paint: {
|
|
29364
|
-
"text-color": config.textColor,
|
|
29365
|
-
"text-halo-color": config.textHaloColor,
|
|
29366
|
-
"text-halo-width": config.textHaloWidth
|
|
29367
|
-
},
|
|
29368
|
-
metadata: {
|
|
29369
|
-
"s52:objectClass": objectClass,
|
|
29370
|
-
"chrt:labelPriority": labelPriority
|
|
29371
|
-
}
|
|
29372
|
-
};
|
|
29373
|
-
if (config.minzoom) {
|
|
29374
|
-
layer.minzoom = config.minzoom;
|
|
29375
|
-
}
|
|
29376
|
-
if (objectClass === "DEPCNT") {
|
|
29377
|
-
layer.filter = GEOM_FILTERS.line;
|
|
29378
|
-
}
|
|
29379
|
-
if (objectClass === "CBLOHD" || objectClass === "PIPOHD") {
|
|
29380
|
-
layer.filter = GEOM_FILTERS.line;
|
|
29381
|
-
}
|
|
29382
|
-
if (objectClass === "SBDARE") {
|
|
29383
|
-
delete layer.filter;
|
|
29384
|
-
}
|
|
29385
|
-
if (objectClass === "MAGVAR") {
|
|
29386
|
-
delete layer.filter;
|
|
29387
|
-
}
|
|
29388
|
-
if (getLabelBehavior(objectClass) === "orientation-cross-band") {
|
|
29389
|
-
layer.filter = LABEL_ANCHOR_FILTER;
|
|
29390
|
-
}
|
|
29391
|
-
return applyDepthLabelSize(objectClass, layer, labelOpts.depthLabelSize);
|
|
29392
|
-
}
|
|
29393
|
-
function getGeomTypesForClass(objectClass, defaultType) {
|
|
29394
|
-
if (SYMBOL_MAP[objectClass] && defaultType === "symbol") {
|
|
29395
|
-
return ["symbol"];
|
|
29396
|
-
}
|
|
29397
|
-
if (objectClass === "SOUNDG") {
|
|
29398
|
-
return ["circle"];
|
|
29399
|
-
}
|
|
29400
|
-
if (defaultType) {
|
|
29401
|
-
return [defaultType];
|
|
29402
|
-
}
|
|
29403
|
-
return ["circle"];
|
|
29404
|
-
}
|
|
29405
|
-
function getOrderedClasses() {
|
|
29406
|
-
const ordered = [...S52_DRAW_ORDER];
|
|
29407
|
-
for (const key of Object.keys(S52_LOOKUP)) {
|
|
29408
|
-
if (!ordered.includes(key)) {
|
|
29409
|
-
const insertIdx = ordered.indexOf("SOUNDG");
|
|
29410
|
-
if (insertIdx >= 0) {
|
|
29411
|
-
ordered.splice(insertIdx, 0, key);
|
|
29412
|
-
} else {
|
|
29413
|
-
ordered.push(key);
|
|
29414
|
-
}
|
|
30094
|
+
metadata: { "s52:objectClass": "CANALS", "s52:role": "polygon" }
|
|
30095
|
+
});
|
|
29415
30096
|
}
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
|
|
29419
|
-
|
|
29420
|
-
|
|
29421
|
-
|
|
29422
|
-
|
|
29423
|
-
|
|
29424
|
-
|
|
29425
|
-
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
29431
|
-
|
|
29432
|
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29437
|
-
|
|
29438
|
-
|
|
29439
|
-
|
|
29440
|
-
|
|
29441
|
-
|
|
29442
|
-
|
|
29443
|
-
if (
|
|
29444
|
-
|
|
29445
|
-
|
|
29446
|
-
|
|
29447
|
-
|
|
29448
|
-
|
|
29449
|
-
|
|
29450
|
-
|
|
29451
|
-
|
|
29452
|
-
|
|
29453
|
-
}
|
|
29454
|
-
|
|
29455
|
-
|
|
29456
|
-
|
|
29457
|
-
|
|
29458
|
-
|
|
29459
|
-
|
|
29460
|
-
|
|
29461
|
-
|
|
29462
|
-
|
|
29463
|
-
|
|
29464
|
-
|
|
29465
|
-
|
|
29466
|
-
|
|
29467
|
-
|
|
29468
|
-
|
|
29469
|
-
|
|
29470
|
-
|
|
29471
|
-
|
|
29472
|
-
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
29477
|
-
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
|
|
29481
|
-
|
|
29482
|
-
|
|
29483
|
-
|
|
29484
|
-
"
|
|
29485
|
-
|
|
29486
|
-
|
|
29487
|
-
|
|
29488
|
-
|
|
29489
|
-
|
|
29490
|
-
|
|
29491
|
-
|
|
29492
|
-
"
|
|
29493
|
-
|
|
29494
|
-
|
|
29495
|
-
|
|
29496
|
-
|
|
29497
|
-
|
|
29498
|
-
|
|
29499
|
-
|
|
29500
|
-
|
|
29501
|
-
|
|
29502
|
-
|
|
29503
|
-
|
|
29504
|
-
"
|
|
29505
|
-
|
|
29506
|
-
"
|
|
29507
|
-
|
|
29508
|
-
|
|
30097
|
+
if (objectClass2 === "PILBOP" && resolved.type === "symbol") {
|
|
30098
|
+
layers.push({
|
|
30099
|
+
id: `s52-PILBOP-polygon`,
|
|
30100
|
+
type: "fill",
|
|
30101
|
+
source: sourceId,
|
|
30102
|
+
"source-layer": "PILBOP",
|
|
30103
|
+
filter: GEOM_FILTERS.fill,
|
|
30104
|
+
paint: {
|
|
30105
|
+
"fill-color": resolveColor("CHMGD", ct),
|
|
30106
|
+
"fill-opacity": 0.1
|
|
30107
|
+
},
|
|
30108
|
+
metadata: { "s52:objectClass": "PILBOP", "s52:role": "polygon" }
|
|
30109
|
+
});
|
|
30110
|
+
layers.push({
|
|
30111
|
+
id: `s52-PILBOP-polygon-outline`,
|
|
30112
|
+
type: "line",
|
|
30113
|
+
source: sourceId,
|
|
30114
|
+
"source-layer": "PILBOP",
|
|
30115
|
+
filter: GEOM_FILTERS.fill,
|
|
30116
|
+
paint: {
|
|
30117
|
+
"line-color": resolveColor("CHMGD", ct),
|
|
30118
|
+
"line-width": 0.8,
|
|
30119
|
+
"line-dasharray": [4, 2]
|
|
30120
|
+
},
|
|
30121
|
+
metadata: { "s52:objectClass": "PILBOP", "s52:role": "outline" }
|
|
30122
|
+
});
|
|
30123
|
+
}
|
|
30124
|
+
if (objectClass2 === "HRBFAC" && resolved.type === "circle") {
|
|
30125
|
+
layers.push({
|
|
30126
|
+
id: `s52-HRBFAC-polygon`,
|
|
30127
|
+
type: "fill",
|
|
30128
|
+
source: sourceId,
|
|
30129
|
+
"source-layer": "HRBFAC",
|
|
30130
|
+
filter: GEOM_FILTERS.fill,
|
|
30131
|
+
paint: {
|
|
30132
|
+
"fill-color": resolveColor("TRFCD", ct),
|
|
30133
|
+
"fill-opacity": 0.15
|
|
30134
|
+
},
|
|
30135
|
+
metadata: { "s52:objectClass": "HRBFAC", "s52:role": "polygon" }
|
|
30136
|
+
});
|
|
30137
|
+
}
|
|
30138
|
+
if (objectClass2 === "MARCUL" && resolved.type === "fill") {
|
|
30139
|
+
layers.push({
|
|
30140
|
+
id: `s52-MARCUL-point`,
|
|
30141
|
+
type: "circle",
|
|
30142
|
+
source: sourceId,
|
|
30143
|
+
"source-layer": "MARCUL",
|
|
30144
|
+
filter: GEOM_FILTERS.circle,
|
|
30145
|
+
paint: {
|
|
30146
|
+
"circle-color": resolveColor("CHBRN", ct),
|
|
30147
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.2, 14, 1.8, 17, 2.6],
|
|
30148
|
+
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
30149
|
+
"circle-stroke-width": 0.4,
|
|
30150
|
+
"circle-opacity": 0.6
|
|
30151
|
+
},
|
|
30152
|
+
metadata: { "s52:objectClass": "MARCUL", "s52:role": "point" }
|
|
30153
|
+
});
|
|
30154
|
+
layers.push({
|
|
30155
|
+
id: `s52-MARCUL-line`,
|
|
30156
|
+
type: "line",
|
|
30157
|
+
source: sourceId,
|
|
30158
|
+
"source-layer": "MARCUL",
|
|
30159
|
+
filter: GEOM_FILTERS.line,
|
|
30160
|
+
paint: {
|
|
30161
|
+
"line-color": resolveColor("CHBLK", ct),
|
|
30162
|
+
"line-width": 0.5,
|
|
30163
|
+
"line-dasharray": [2, 2]
|
|
30164
|
+
},
|
|
30165
|
+
metadata: { "s52:objectClass": "MARCUL", "s52:role": "line" }
|
|
30166
|
+
});
|
|
30167
|
+
}
|
|
30168
|
+
if (objectClass2 === "RECTRC" && resolved.type === "line") {
|
|
30169
|
+
layers.push({
|
|
30170
|
+
id: `s52-RECTRC-polygon`,
|
|
30171
|
+
type: "fill",
|
|
30172
|
+
source: sourceId,
|
|
30173
|
+
"source-layer": "RECTRC",
|
|
30174
|
+
filter: GEOM_FILTERS.fill,
|
|
30175
|
+
paint: {
|
|
30176
|
+
"fill-color": resolveColor("TRFCD", ct),
|
|
30177
|
+
"fill-opacity": 0.05
|
|
30178
|
+
},
|
|
30179
|
+
metadata: { "s52:objectClass": "RECTRC", "s52:role": "polygon" }
|
|
30180
|
+
});
|
|
30181
|
+
}
|
|
30182
|
+
if (objectClass2 === "PONTON" && resolved.type === "fill") {
|
|
30183
|
+
layers.push({
|
|
30184
|
+
id: `s52-PONTON-line`,
|
|
30185
|
+
type: "line",
|
|
30186
|
+
source: sourceId,
|
|
30187
|
+
"source-layer": "PONTON",
|
|
30188
|
+
filter: GEOM_FILTERS.line,
|
|
30189
|
+
paint: {
|
|
30190
|
+
"line-color": resolveColor("CHGRF", ct),
|
|
30191
|
+
"line-width": 1.5
|
|
30192
|
+
},
|
|
30193
|
+
metadata: { "s52:objectClass": "PONTON", "s52:role": "line" }
|
|
30194
|
+
});
|
|
30195
|
+
}
|
|
30196
|
+
if (objectClass2 === "SILTNK" && resolved.type === "symbol") {
|
|
30197
|
+
layers.push({
|
|
30198
|
+
id: `s52-SILTNK-symbol-polygon`,
|
|
30199
|
+
type: "symbol",
|
|
30200
|
+
source: sourceId,
|
|
30201
|
+
"source-layer": "SILTNK",
|
|
30202
|
+
filter: GEOM_FILTERS.fill,
|
|
30203
|
+
layout: {
|
|
30204
|
+
"icon-image": "TNKCON02",
|
|
30205
|
+
"icon-size": 0.6,
|
|
30206
|
+
"icon-allow-overlap": true,
|
|
30207
|
+
"icon-ignore-placement": false,
|
|
30208
|
+
"symbol-placement": "point"
|
|
30209
|
+
},
|
|
30210
|
+
paint: {},
|
|
30211
|
+
metadata: { "s52:objectClass": "SILTNK", "s52:role": "symbol-polygon" }
|
|
30212
|
+
});
|
|
30213
|
+
}
|
|
30214
|
+
if (objectClass2 === "ACHBRT" && resolved.type === "symbol") {
|
|
30215
|
+
layers.push({
|
|
30216
|
+
id: `s52-ACHBRT-symbol-polygon`,
|
|
30217
|
+
type: "symbol",
|
|
30218
|
+
source: sourceId,
|
|
30219
|
+
"source-layer": "ACHBRT",
|
|
30220
|
+
filter: GEOM_FILTERS.fill,
|
|
30221
|
+
layout: {
|
|
30222
|
+
"icon-image": "ACHARE02",
|
|
30223
|
+
"icon-size": 0.5,
|
|
30224
|
+
"icon-allow-overlap": true,
|
|
30225
|
+
"icon-ignore-placement": false,
|
|
30226
|
+
"symbol-placement": "point"
|
|
30227
|
+
},
|
|
30228
|
+
paint: {},
|
|
30229
|
+
metadata: { "s52:objectClass": "ACHBRT", "s52:role": "symbol-polygon" }
|
|
30230
|
+
});
|
|
30231
|
+
}
|
|
30232
|
+
if (objectClass2 === "BERTHS" && resolved.type === "line") {
|
|
30233
|
+
layers.push({
|
|
30234
|
+
id: `s52-BERTHS-circle`,
|
|
30235
|
+
type: "circle",
|
|
30236
|
+
source: sourceId,
|
|
30237
|
+
"source-layer": "BERTHS",
|
|
30238
|
+
filter: GEOM_FILTERS.circle,
|
|
30239
|
+
paint: {
|
|
30240
|
+
"circle-color": resolveColor("CHBLK", ct),
|
|
30241
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.5, 14, 2.2, 17, 3],
|
|
30242
|
+
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
30243
|
+
"circle-stroke-width": 0.5
|
|
30244
|
+
},
|
|
30245
|
+
metadata: { "s52:objectClass": "BERTHS", "s52:role": "point" }
|
|
30246
|
+
});
|
|
30247
|
+
}
|
|
30248
|
+
if (objectClass2 === "TUNNEL" && resolved.type === "line") {
|
|
30249
|
+
layers.push({
|
|
30250
|
+
// NOT `s52-TUNNEL-line` — that id is already taken by the main layer
|
|
30251
|
+
// (`s52-${objectClass}-${resolved.type}`), and duplicate layer ids
|
|
30252
|
+
// are a Mapbox style-spec violation (caught by style-integrity.test).
|
|
30253
|
+
id: `s52-TUNNEL-linestring`,
|
|
30254
|
+
type: "line",
|
|
30255
|
+
source: sourceId,
|
|
30256
|
+
"source-layer": "TUNNEL",
|
|
30257
|
+
filter: GEOM_FILTERS.line,
|
|
30258
|
+
paint: {
|
|
30259
|
+
"line-color": resolveColor("CHGRD", ct),
|
|
30260
|
+
"line-width": 1,
|
|
30261
|
+
"line-dasharray": [2, 2]
|
|
30262
|
+
},
|
|
30263
|
+
metadata: { "s52:objectClass": "TUNNEL", "s52:role": "line" }
|
|
30264
|
+
});
|
|
29509
30265
|
}
|
|
29510
|
-
];
|
|
29511
|
-
}
|
|
29512
|
-
|
|
29513
|
-
// src/atlas/band-priority.ts
|
|
29514
|
-
var BAND_PRIORITY_RANGES = {
|
|
29515
|
-
"1": { minzoom: 0 },
|
|
29516
|
-
"2": { minzoom: 5 },
|
|
29517
|
-
"3": { minzoom: 7 },
|
|
29518
|
-
"4": { minzoom: 9 },
|
|
29519
|
-
"5": { minzoom: 11 },
|
|
29520
|
-
"6": { minzoom: 11 }
|
|
29521
|
-
};
|
|
29522
|
-
var BAND_DATA_MINZOOM = {
|
|
29523
|
-
"1": 0,
|
|
29524
|
-
"2": 5,
|
|
29525
|
-
"3": 7,
|
|
29526
|
-
"4": 9,
|
|
29527
|
-
"5": 11,
|
|
29528
|
-
"6": 13
|
|
29529
|
-
};
|
|
29530
|
-
function dominatedVariantMaxzoom(D) {
|
|
29531
|
-
return Math.max(BAND_PRIORITY_RANGES[D].minzoom, BAND_DATA_MINZOOM[D]);
|
|
29532
|
-
}
|
|
29533
|
-
var DOMINANCE_EXEMPT_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29534
|
-
"LNDARE",
|
|
29535
|
-
"DEPARE",
|
|
29536
|
-
"UNSARE",
|
|
29537
|
-
"LAKARE",
|
|
29538
|
-
"DRGARE",
|
|
29539
|
-
"SEAARE"
|
|
29540
|
-
]);
|
|
29541
|
-
function isExemptOpaqueFill(layer) {
|
|
29542
|
-
if (layer.type !== "fill") return false;
|
|
29543
|
-
for (const cls of DOMINANCE_EXEMPT_FILL_CLASSES) {
|
|
29544
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
29545
|
-
}
|
|
29546
|
-
return false;
|
|
29547
|
-
}
|
|
29548
|
-
var GROUND_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29549
|
-
...DOMINANCE_EXEMPT_FILL_CLASSES,
|
|
29550
|
-
"RIVERS"
|
|
29551
|
-
]);
|
|
29552
|
-
function isGroundFill(layer) {
|
|
29553
|
-
if (layer.type !== "fill") return false;
|
|
29554
|
-
for (const cls of GROUND_FILL_CLASSES) {
|
|
29555
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
29556
|
-
}
|
|
29557
|
-
return false;
|
|
29558
|
-
}
|
|
29559
|
-
var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
29560
|
-
"1",
|
|
29561
|
-
"2",
|
|
29562
|
-
"3"
|
|
29563
|
-
]);
|
|
29564
|
-
function bandVariantMinzoom(layer, B) {
|
|
29565
|
-
const classMinzoom = layer.minzoom ?? 0;
|
|
29566
|
-
if (isGroundFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
29567
|
-
return 0;
|
|
29568
|
-
}
|
|
29569
|
-
if (isGroundFill(layer)) {
|
|
29570
|
-
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
29571
|
-
}
|
|
29572
|
-
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
29573
|
-
}
|
|
29574
|
-
var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29575
|
-
"BUAARE",
|
|
29576
|
-
// 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
|
|
29577
|
-
// Captain of the Port Zone" RESARE exists at bands 2/3 as a generalized
|
|
29578
|
-
// shore-hugging polygon whose point-sampled dominance tag MISSED (the
|
|
29579
|
-
// Providence-BUAARE failure mode), so its magenta dashed boundary drew
|
|
29580
|
-
// OFFSET over the band-5 detail at z11.5 — read in the field as "a
|
|
29581
|
-
// magenta shoreline bleeding down from a less coarse band". The same
|
|
29582
|
-
// tile carried untagged band-2 ADMARE and CTNARE giants in the same
|
|
29583
|
-
// state. All three are area classes whose coarse-band boundaries hug
|
|
29584
|
-
// shorelines and whose fine-band copies are authoritative.
|
|
29585
|
-
"RESARE",
|
|
29586
|
-
"CTNARE",
|
|
29587
|
-
"ADMARE"
|
|
29588
|
-
]);
|
|
29589
|
-
var GENERALISED_AREA_MAX_BAND = 3;
|
|
29590
|
-
function generalisedAreaCeiling(layer, B) {
|
|
29591
|
-
if (layer.type !== "fill" && layer.type !== "line") return null;
|
|
29592
|
-
if (parseInt(B, 10) > GENERALISED_AREA_MAX_BAND) return null;
|
|
29593
|
-
for (const cls of GENERALISED_AREA_FILL_CLASSES) {
|
|
29594
|
-
if (layer.id.startsWith(`s52-${cls}-`)) return BAND_PRIORITY_RANGES["5"].minzoom;
|
|
29595
|
-
}
|
|
29596
|
-
return null;
|
|
29597
|
-
}
|
|
29598
|
-
var BAND_DRAW_ORDER = ["1", "2", "3", "4", "5", "6"];
|
|
29599
|
-
var CHART_BAND_CODES = {
|
|
29600
|
-
Overview: ["1"],
|
|
29601
|
-
General: ["2"],
|
|
29602
|
-
Coastal: ["3"],
|
|
29603
|
-
Approach: ["4"],
|
|
29604
|
-
Harbor: ["5", "6"]
|
|
29605
|
-
};
|
|
29606
|
-
var CHART_CASCADE_CODES = {
|
|
29607
|
-
Overview: ["1"],
|
|
29608
|
-
General: ["1", "2"],
|
|
29609
|
-
Coastal: ["1", "2", "3"],
|
|
29610
|
-
Approach: ["1", "2", "3", "4"],
|
|
29611
|
-
Harbor: ["1", "2", "3", "4", "5", "6"]
|
|
29612
|
-
};
|
|
29613
|
-
var CHRT_STYLE_MODES = [
|
|
29614
|
-
"continuous",
|
|
29615
|
-
...Object.keys(CHART_BAND_CODES).map(
|
|
29616
|
-
(band) => `chart:${band}`
|
|
29617
|
-
)
|
|
29618
|
-
];
|
|
29619
|
-
function normalizeStyleMode(raw) {
|
|
29620
|
-
if (raw === null || raw === void 0 || raw === "") return "continuous";
|
|
29621
|
-
const lower = raw.toLowerCase();
|
|
29622
|
-
return CHRT_STYLE_MODES.find((mode) => mode.toLowerCase() === lower) ?? null;
|
|
29623
|
-
}
|
|
29624
|
-
function styleModeBand(mode) {
|
|
29625
|
-
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
29626
|
-
}
|
|
29627
|
-
function bandFilterExpression(codes) {
|
|
29628
|
-
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
29629
|
-
}
|
|
29630
|
-
var BAND_CODE_ALIASES = {
|
|
29631
|
-
"5": ["7"]
|
|
29632
|
-
};
|
|
29633
|
-
function cellBandCharacters(code) {
|
|
29634
|
-
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
29635
|
-
}
|
|
29636
|
-
function cellBandMatch(cellBandExpr, codes) {
|
|
29637
|
-
const list = Array.isArray(codes) ? codes : [codes];
|
|
29638
|
-
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
29639
|
-
if (chars.length === 1) {
|
|
29640
|
-
return ["==", cellBandExpr, chars[0]];
|
|
29641
30266
|
}
|
|
29642
|
-
return
|
|
30267
|
+
return layers;
|
|
29643
30268
|
}
|
|
29644
|
-
function
|
|
29645
|
-
if (!
|
|
29646
|
-
|
|
29647
|
-
|
|
30269
|
+
function buildLabelLayer2(objectClass2, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
|
|
30270
|
+
if (!LABELED_LAYERS.has(objectClass2)) return null;
|
|
30271
|
+
const config = getLabelConfig(objectClass2, {
|
|
30272
|
+
colorTable: ct,
|
|
30273
|
+
safetyContour: labelOpts.safetyContour,
|
|
30274
|
+
twoShadeSoundings: labelOpts.twoShadeSoundings,
|
|
30275
|
+
soundingFontWeight: labelOpts.soundingFontWeight
|
|
30276
|
+
});
|
|
30277
|
+
if (!config) return null;
|
|
30278
|
+
let textField = config.textField;
|
|
30279
|
+
if (objectClass2 === "LIGHTS") {
|
|
30280
|
+
textField = buildLightLabelExpression();
|
|
29648
30281
|
}
|
|
29649
|
-
|
|
29650
|
-
|
|
29651
|
-
|
|
29652
|
-
|
|
29653
|
-
|
|
29654
|
-
["!=", ["get", "_dominated"], true]
|
|
29655
|
-
];
|
|
29656
|
-
function dominatedByMinFilter(D) {
|
|
29657
|
-
const dInt = parseInt(D, 10);
|
|
29658
|
-
if (D === "6") {
|
|
29659
|
-
return [
|
|
29660
|
-
"any",
|
|
29661
|
-
["==", ["get", "_dominated_by_min"], dInt],
|
|
29662
|
-
["==", ["get", "_dominated"], true]
|
|
29663
|
-
];
|
|
30282
|
+
if (typeof textField !== "string" && Array.isArray(textField)) {
|
|
30283
|
+
const flat = JSON.stringify(textField);
|
|
30284
|
+
if (flat.includes("_navAidLabel")) {
|
|
30285
|
+
textField = buildNavAidLabelExpression();
|
|
30286
|
+
}
|
|
29664
30287
|
}
|
|
29665
|
-
|
|
29666
|
-
|
|
29667
|
-
|
|
29668
|
-
|
|
29669
|
-
const [e, n] = bbox.ne;
|
|
29670
|
-
return [
|
|
29671
|
-
"within",
|
|
29672
|
-
{
|
|
29673
|
-
type: "Polygon",
|
|
29674
|
-
coordinates: [
|
|
30288
|
+
if (depthUnits === "feet") {
|
|
30289
|
+
if (objectClass2 === "SOUNDG") {
|
|
30290
|
+
textField = [
|
|
30291
|
+
"concat",
|
|
29675
30292
|
[
|
|
29676
|
-
|
|
29677
|
-
[
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
|
|
29682
|
-
|
|
30293
|
+
"round",
|
|
30294
|
+
[
|
|
30295
|
+
"*",
|
|
30296
|
+
[
|
|
30297
|
+
"to-number",
|
|
30298
|
+
["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
|
|
30299
|
+
0
|
|
30300
|
+
],
|
|
30301
|
+
3.28084
|
|
30302
|
+
]
|
|
30303
|
+
],
|
|
30304
|
+
""
|
|
30305
|
+
];
|
|
30306
|
+
} else if (objectClass2 === "DEPCNT") {
|
|
30307
|
+
textField = [
|
|
30308
|
+
"concat",
|
|
30309
|
+
["round", ["*", ["to-number", ["get", "VALDCO"]], 3.28084]],
|
|
30310
|
+
"ft"
|
|
30311
|
+
];
|
|
30312
|
+
} else if (objectClass2 === "UWTROC" || objectClass2 === "OBSTRN" || objectClass2 === "WRECKS") {
|
|
30313
|
+
const valsouFt = [
|
|
30314
|
+
"to-number",
|
|
30315
|
+
["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0],
|
|
30316
|
+
0
|
|
30317
|
+
];
|
|
30318
|
+
textField = [
|
|
30319
|
+
"case",
|
|
30320
|
+
[">", valsouFt, 0],
|
|
30321
|
+
["concat", ["to-string", ["round", ["*", valsouFt, 3.28084]]], "ft"],
|
|
30322
|
+
""
|
|
30323
|
+
];
|
|
30324
|
+
}
|
|
30325
|
+
}
|
|
30326
|
+
const labelPriority = getLabelPriority(objectClass2);
|
|
30327
|
+
const layer = {
|
|
30328
|
+
id: `s52-${objectClass2}-label`,
|
|
30329
|
+
type: "symbol",
|
|
30330
|
+
source: sourceId,
|
|
30331
|
+
"source-layer": objectClass2,
|
|
30332
|
+
filter: GEOM_FILTERS.symbol,
|
|
30333
|
+
layout: {
|
|
30334
|
+
"text-field": textField,
|
|
30335
|
+
"text-size": config.textSize,
|
|
30336
|
+
"text-font": config.textFont ?? ["Open Sans Regular"],
|
|
30337
|
+
"text-offset": config.textOffset,
|
|
30338
|
+
"text-anchor": "top",
|
|
30339
|
+
"text-allow-overlap": false,
|
|
30340
|
+
"text-optional": true,
|
|
30341
|
+
"text-padding": 4,
|
|
30342
|
+
"symbol-sort-key": labelPriority * 1e3,
|
|
30343
|
+
...config.layout ?? {}
|
|
30344
|
+
},
|
|
30345
|
+
paint: {
|
|
30346
|
+
"text-color": config.textColor,
|
|
30347
|
+
"text-halo-color": config.textHaloColor,
|
|
30348
|
+
"text-halo-width": config.textHaloWidth
|
|
30349
|
+
},
|
|
30350
|
+
metadata: {
|
|
30351
|
+
"s52:objectClass": objectClass2,
|
|
30352
|
+
"chrt:labelPriority": labelPriority
|
|
29683
30353
|
}
|
|
29684
|
-
];
|
|
29685
|
-
}
|
|
29686
|
-
function worldMinusBboxPolygon(bbox) {
|
|
29687
|
-
const [w, s] = bbox.sw;
|
|
29688
|
-
const [e, n] = bbox.ne;
|
|
29689
|
-
return {
|
|
29690
|
-
type: "Polygon",
|
|
29691
|
-
coordinates: [
|
|
29692
|
-
[
|
|
29693
|
-
[-180, -85],
|
|
29694
|
-
[180, -85],
|
|
29695
|
-
[180, 85],
|
|
29696
|
-
[-180, 85],
|
|
29697
|
-
[-180, -85]
|
|
29698
|
-
],
|
|
29699
|
-
// Inner ring (hole) — winding opposite to outer ring per GeoJSON
|
|
29700
|
-
// spec. MapLibre is permissive but we follow strict winding.
|
|
29701
|
-
[
|
|
29702
|
-
[w, s],
|
|
29703
|
-
[w, n],
|
|
29704
|
-
[e, n],
|
|
29705
|
-
[e, s],
|
|
29706
|
-
[w, s]
|
|
29707
|
-
]
|
|
29708
|
-
]
|
|
29709
30354
|
};
|
|
30355
|
+
if (config.minzoom) {
|
|
30356
|
+
layer.minzoom = config.minzoom;
|
|
30357
|
+
}
|
|
30358
|
+
if (objectClass2 === "DEPCNT") {
|
|
30359
|
+
layer.filter = GEOM_FILTERS.line;
|
|
30360
|
+
}
|
|
30361
|
+
if (objectClass2 === "CBLOHD" || objectClass2 === "PIPOHD") {
|
|
30362
|
+
layer.filter = GEOM_FILTERS.line;
|
|
30363
|
+
}
|
|
30364
|
+
if (objectClass2 === "SBDARE") {
|
|
30365
|
+
delete layer.filter;
|
|
30366
|
+
}
|
|
30367
|
+
if (objectClass2 === "MAGVAR") {
|
|
30368
|
+
delete layer.filter;
|
|
30369
|
+
}
|
|
30370
|
+
if (getLabelBehavior(objectClass2) === "orientation-cross-band") {
|
|
30371
|
+
layer.filter = LABEL_ANCHOR_FILTER;
|
|
30372
|
+
}
|
|
30373
|
+
return applyDepthLabelSize(objectClass2, layer, labelOpts.depthLabelSize);
|
|
29710
30374
|
}
|
|
29711
|
-
|
|
29712
|
-
|
|
29713
|
-
|
|
29714
|
-
var UNCHARTED_PATTERN = "chrt:hatch-uncharted";
|
|
29715
|
-
function inlandWaterColor(palette) {
|
|
29716
|
-
return (BASE_THEMES[palette] ?? BASE_THEMES.DAY).inlandWater;
|
|
29717
|
-
}
|
|
29718
|
-
var BASE_THEMES = {
|
|
29719
|
-
DAY: {
|
|
29720
|
-
// ── `earth` is the jurisdiction tan DIMMED (~0.55), not a blue-grey ──
|
|
29721
|
-
// Outside the jurisdiction the map still owes the reader "what is land".
|
|
29722
|
-
// State 3 in the header is "the geography, under a crosshatch", and until
|
|
29723
|
-
// 2026-08-31 that geography was unreadable: earth #2A3439 against sea
|
|
29724
|
-
// #1B2328 measured 1.25:1, so Hispaniola showed charted boxes with tan
|
|
29725
|
-
// land inside and nothing recognisable around them (owner report: "no
|
|
29726
|
-
// land painting down near the DR at some zooms"). Land is warm and sea is
|
|
29727
|
-
// cool because that is what land and sea are; dimming the JURISDICTION
|
|
29728
|
-
// tan rather than inventing a colour says the true thing — this is the
|
|
29729
|
-
// same land, we simply do not carry charts for it. Held against BOTH
|
|
29730
|
-
// neighbours: legible over sea, and never mistakable for the full tan of
|
|
29731
|
-
// a country we do carry. DAY 3.59:1 vs sea, 3.17:1 vs tan.
|
|
29732
|
-
earth: "#807761",
|
|
29733
|
-
water: "#1B2328",
|
|
29734
|
-
river: "#24313A",
|
|
29735
|
-
boundary: "#4A575E",
|
|
29736
|
-
jurisdiction: "#D4C69D",
|
|
29737
|
-
// CHRTland's own tan — one land colour
|
|
29738
|
-
inlandWater: "#B9CFDD",
|
|
29739
|
-
// muted, unmistakably not a charted blue
|
|
29740
|
-
// The uncharted hatch's stripe, held to 2.3:1 against `inlandWater` in
|
|
29741
|
-
// every palette (`inland-hatch-contrast.test.ts`) — the hatch says "we have
|
|
29742
|
-
// not charted this water", and stripes the eye cannot find say nothing.
|
|
29743
|
-
// DAY 3.08:1, darker than its ground. Also the colour the artwork file
|
|
29744
|
-
// itself is drawn in.
|
|
29745
|
-
inlandHatch: "#5B7383",
|
|
29746
|
-
// State furniture. These three were referenced by the layers but missing
|
|
29747
|
-
// from this table (and its type — the long-standing TS2339 trio), so the
|
|
29748
|
-
// lines shipped on MapLibre's DEFAULT black. Now deliberate: a hairline
|
|
29749
|
-
// a shade deeper than the tan, quiet caps, tan halo.
|
|
29750
|
-
stateLine: "#B1A077",
|
|
29751
|
-
stateLabel: "#8E8468",
|
|
29752
|
-
stateHalo: "#D4C69D",
|
|
29753
|
-
// Rough-lake furniture (owner ask 2026-08-28: "the lakes need labels,
|
|
29754
|
-
// and shorelines drawn"): a shoreline a shade deeper than the rough
|
|
29755
|
-
// water, and a name in the water's own voice, haloed by the water.
|
|
29756
|
-
inlandShore: "#8FA9BA",
|
|
29757
|
-
inlandLabel: "#54707F",
|
|
29758
|
-
inlandHalo: "#B9CFDD"
|
|
29759
|
-
},
|
|
29760
|
-
DUSK: {
|
|
29761
|
-
// ── Re-derived 2026-09-01: the basemap sits UNDER the chart ──────────
|
|
29762
|
-
// These were the DAY tones scaled by a constant, never measured against
|
|
29763
|
-
// the DUSK chart — so `jurisdiction` came out at L=0.506 against a chart
|
|
29764
|
-
// whose own land (LANDA #8a7d5e) is L=0.209, i.e. the context layer was
|
|
29765
|
-
// 2.4x brighter than the data layer. Now capped at the chart's own land.
|
|
29766
|
-
// The rest of the block rides the same 0.666 channel factor so every
|
|
29767
|
-
// relationship the palette already chose is preserved, only moved down.
|
|
29768
|
-
// `water` is deliberately NOT scaled: it is the reference the land/sea
|
|
29769
|
-
// floor is measured against.
|
|
29770
|
-
earth: "#544E3E",
|
|
29771
|
-
water: "#161D21",
|
|
29772
|
-
river: "#141B20",
|
|
29773
|
-
boundary: "#293135",
|
|
29774
|
-
jurisdiction: "#867D64",
|
|
29775
|
-
inlandWater: "#62717A",
|
|
29776
|
-
// Owner's choice, 2026-10-04: dark stripes, 2.37:1. The DAY stripe here
|
|
29777
|
-
// was 1.02:1 — invisible, so an uncharted lake read as plain water.
|
|
29778
|
-
inlandHatch: "#2E383F",
|
|
29779
|
-
// Label stays DARK here — the halo is still mid-toned, so 3:1 is reachable
|
|
29780
|
-
// downward and a bright label would be a new light source at dusk.
|
|
29781
|
-
stateLine: "#B5A987",
|
|
29782
|
-
stateLabel: "#38342A",
|
|
29783
|
-
stateHalo: "#867D64",
|
|
29784
|
-
inlandShore: "#4D5B67",
|
|
29785
|
-
inlandLabel: "#212629",
|
|
29786
|
-
inlandHalo: "#62717A"
|
|
29787
|
-
},
|
|
29788
|
-
NIGHT: {
|
|
29789
|
-
// ── Re-derived 2026-09-01 — the owner-reported night defect ──────────
|
|
29790
|
-
// `jurisdiction` was L=0.240 against a chart whose own land (LANDA
|
|
29791
|
-
// #3a3020) is L=0.031: the world basemap was 7.7x BRIGHTER than the chart
|
|
29792
|
-
// it sits under, so at z<11 the uncharted interior read as a pale
|
|
29793
|
-
// continent-sized slab on a night chart. Capped at the chart's own land.
|
|
29794
|
-
//
|
|
29795
|
-
// ⚠ `earth` AND `jurisdiction` CONVERGE HERE, and that is forced, not
|
|
29796
|
-
// sloppy. Holding earth 2:1 above the ocean AND 2:1 below the jurisdiction
|
|
29797
|
-
// tan requires jurisdiction >= L 0.174 — 5.6x the chart's own land. At
|
|
29798
|
-
// night the range between the ocean (L 0.006) and the cap (L 0.031) is too
|
|
29799
|
-
// compressed to carry two land tones. COVERAGE IS STILL SAID, by the
|
|
29800
|
-
// `chrt:hatch-nocoverage` crosshatch, which is a stronger signal than tone
|
|
29801
|
-
// and is the one thing that cannot be lost to palette compression.
|
|
29802
|
-
//
|
|
29803
|
-
// Labels invert to light-on-dark: 3:1 downward is unreachable off a halo
|
|
29804
|
-
// this close to black, and CHRTland's own night town labels already sit at
|
|
29805
|
-
// L=0.177 for exactly this reason.
|
|
29806
|
-
earth: "#333026",
|
|
29807
|
-
water: "#10161A",
|
|
29808
|
-
river: "#090D0F",
|
|
29809
|
-
boundary: "#131719",
|
|
29810
|
-
jurisdiction: "#353128",
|
|
29811
|
-
inlandWater: "#232A30",
|
|
29812
|
-
// The DAY stripe, lighter than this ground: 2.92:1, and L 0.16, under the
|
|
29813
|
-
// NIGHT muted-ink ceiling (`night-luminance.test.ts`). Red night draws the
|
|
29814
|
-
// NIGHT chart, so this is its stripe too.
|
|
29815
|
-
inlandHatch: "#5B7383",
|
|
29816
|
-
stateLine: "#484236",
|
|
29817
|
-
stateLabel: "#837963",
|
|
29818
|
-
stateHalo: "#353128",
|
|
29819
|
-
inlandShore: "#1C2227",
|
|
29820
|
-
inlandLabel: "#617484",
|
|
29821
|
-
inlandHalo: "#232A30"
|
|
30375
|
+
function getGeomTypesForClass(objectClass2, defaultType) {
|
|
30376
|
+
if (SYMBOL_MAP[objectClass2] && defaultType === "symbol") {
|
|
30377
|
+
return ["symbol"];
|
|
29822
30378
|
}
|
|
29823
|
-
|
|
29824
|
-
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
return [
|
|
29830
|
-
|
|
29831
|
-
|
|
29832
|
-
|
|
29833
|
-
|
|
29834
|
-
|
|
29835
|
-
|
|
29836
|
-
|
|
29837
|
-
|
|
29838
|
-
|
|
29839
|
-
|
|
29840
|
-
}),
|
|
29841
|
-
L({
|
|
29842
|
-
id: "chrtbase-earth",
|
|
29843
|
-
type: "fill",
|
|
29844
|
-
source: src,
|
|
29845
|
-
"source-layer": "base_earth",
|
|
29846
|
-
paint: { "fill-color": T.earth }
|
|
29847
|
-
}),
|
|
29848
|
-
L({
|
|
29849
|
-
id: "chrtbase-water",
|
|
29850
|
-
type: "fill",
|
|
29851
|
-
source: src,
|
|
29852
|
-
"source-layer": "base_water",
|
|
29853
|
-
paint: { "fill-color": T.water }
|
|
29854
|
-
}),
|
|
29855
|
-
L({
|
|
29856
|
-
id: "chrtbase-river",
|
|
29857
|
-
type: "line",
|
|
29858
|
-
source: src,
|
|
29859
|
-
"source-layer": "base_river",
|
|
29860
|
-
minzoom: 9,
|
|
29861
|
-
paint: {
|
|
29862
|
-
"line-color": T.river,
|
|
29863
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.6, 14, 1.6]
|
|
29864
|
-
}
|
|
29865
|
-
}),
|
|
29866
|
-
L({
|
|
29867
|
-
id: "chrtbase-boundary",
|
|
29868
|
-
type: "line",
|
|
29869
|
-
source: src,
|
|
29870
|
-
"source-layer": "base_boundary",
|
|
29871
|
-
paint: { "line-color": T.boundary, "line-width": 0.6, "line-opacity": 0.8 }
|
|
29872
|
-
}),
|
|
29873
|
-
// 2. THE DEFAULT: no coverage, everywhere, as a geometry-free background.
|
|
29874
|
-
// Constant opacity by design — see the header. What kills it is being
|
|
29875
|
-
// painted OVER, never fading.
|
|
29876
|
-
L({
|
|
29877
|
-
id: "chrtbase-nocoverage",
|
|
29878
|
-
type: "background",
|
|
29879
|
-
paint: { "background-pattern": NOCOVERAGE_PATTERN, "background-opacity": 0.55 }
|
|
29880
|
-
}),
|
|
29881
|
-
// 3. The jurisdiction: countries whose charts CHRT carries go tan — the
|
|
29882
|
-
// same tan as CHRTland, so "land in our world" is ONE colour. Opaque,
|
|
29883
|
-
// so it takes the crosshatch off its ground entirely.
|
|
29884
|
-
L({
|
|
29885
|
-
id: "chrtbase-jurisdiction",
|
|
29886
|
-
type: "fill",
|
|
29887
|
-
source: src,
|
|
29888
|
-
"source-layer": "jurisdiction",
|
|
29889
|
-
paint: { "fill-color": T.jurisdiction }
|
|
29890
|
-
}),
|
|
29891
|
-
// 4. States — internal boundaries and names, inside the jurisdiction
|
|
29892
|
-
// only (owner decision 2026-08-28). A hairline a shade deeper than the
|
|
29893
|
-
// tan, and quiet letter-spaced caps that fade as harbour detail takes
|
|
29894
|
-
// over. The one deliberate exception to no-names-on-the-basemap:
|
|
29895
|
-
// ADMIN furniture, nothing else — see the fence test.
|
|
29896
|
-
L({
|
|
29897
|
-
id: "chrtbase-state-boundary",
|
|
29898
|
-
type: "line",
|
|
29899
|
-
source: src,
|
|
29900
|
-
"source-layer": "state_boundary",
|
|
29901
|
-
minzoom: 4,
|
|
29902
|
-
paint: {
|
|
29903
|
-
"line-color": T.stateLine,
|
|
29904
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1],
|
|
29905
|
-
"line-dasharray": [3, 2],
|
|
29906
|
-
"line-opacity": 0.8
|
|
29907
|
-
}
|
|
29908
|
-
}),
|
|
29909
|
-
// 5. Country borders, drawn to be HOISTED above the whole chart by the
|
|
29910
|
-
// generator (owner, 2026-08-31: "where there is charted land, can we
|
|
29911
|
-
// add country borders on top of everything? I want it clear where
|
|
29912
|
-
// there is a boarder."). Sourced from NE boundary_lines_land — land
|
|
29913
|
-
// borders only, never coastline — so riding on top cannot stroke a
|
|
29914
|
-
// coast. Dashed so it reads as an administrative line, not chart
|
|
29915
|
-
// linework.
|
|
29916
|
-
L({
|
|
29917
|
-
id: "chrtbase-country-boundary",
|
|
29918
|
-
type: "line",
|
|
29919
|
-
source: src,
|
|
29920
|
-
"source-layer": "country_boundary",
|
|
29921
|
-
paint: {
|
|
29922
|
-
"line-color": T.boundary,
|
|
29923
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.8, 8, 1.4, 12, 2.2],
|
|
29924
|
-
"line-dasharray": [4, 2],
|
|
29925
|
-
"line-opacity": 0.9
|
|
29926
|
-
}
|
|
29927
|
-
}),
|
|
29928
|
-
L({
|
|
29929
|
-
id: "chrtbase-state-label",
|
|
29930
|
-
type: "symbol",
|
|
29931
|
-
source: src,
|
|
29932
|
-
"source-layer": "state_label",
|
|
29933
|
-
minzoom: 4,
|
|
29934
|
-
maxzoom: 10,
|
|
29935
|
-
layout: {
|
|
29936
|
-
"text-field": ["upcase", ["get", "name"]],
|
|
29937
|
-
"text-font": ["Open Sans Regular"],
|
|
29938
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 4, 9, 8, 12],
|
|
29939
|
-
"text-letter-spacing": 0.25,
|
|
29940
|
-
"text-allow-overlap": false,
|
|
29941
|
-
"text-padding": 8
|
|
29942
|
-
},
|
|
29943
|
-
paint: {
|
|
29944
|
-
"text-color": T.stateLabel,
|
|
29945
|
-
"text-halo-color": T.stateHalo,
|
|
29946
|
-
"text-halo-width": 1.2,
|
|
29947
|
-
"text-opacity": ["interpolate", ["linear"], ["zoom"], 4, 0.7, 8, 0.85, 10, 0.4]
|
|
30379
|
+
if (objectClass2 === "SOUNDG") {
|
|
30380
|
+
return ["circle"];
|
|
30381
|
+
}
|
|
30382
|
+
if (defaultType) {
|
|
30383
|
+
return [defaultType];
|
|
30384
|
+
}
|
|
30385
|
+
return ["circle"];
|
|
30386
|
+
}
|
|
30387
|
+
function getOrderedClasses() {
|
|
30388
|
+
const ordered = [...S52_DRAW_ORDER];
|
|
30389
|
+
for (const key of Object.keys(S52_LOOKUP)) {
|
|
30390
|
+
if (!ordered.includes(key)) {
|
|
30391
|
+
const insertIdx = ordered.indexOf("SOUNDG");
|
|
30392
|
+
if (insertIdx >= 0) {
|
|
30393
|
+
ordered.splice(insertIdx, 0, key);
|
|
30394
|
+
} else {
|
|
30395
|
+
ordered.push(key);
|
|
29948
30396
|
}
|
|
29949
|
-
}
|
|
29950
|
-
|
|
30397
|
+
}
|
|
30398
|
+
}
|
|
30399
|
+
return ordered;
|
|
29951
30400
|
}
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
|
|
29955
|
-
|
|
29956
|
-
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
|
|
30401
|
+
|
|
30402
|
+
// src/atlas/terrain-order.ts
|
|
30403
|
+
var DRAPEABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
30404
|
+
"background",
|
|
30405
|
+
"fill",
|
|
30406
|
+
"line",
|
|
30407
|
+
"raster",
|
|
30408
|
+
"hillshade",
|
|
30409
|
+
"color-relief"
|
|
30410
|
+
]);
|
|
30411
|
+
function isDrapeable(layer) {
|
|
30412
|
+
return DRAPEABLE_LAYER_TYPES.has(layer.type);
|
|
30413
|
+
}
|
|
30414
|
+
function drapedFirst(layers) {
|
|
30415
|
+
const draped = [];
|
|
30416
|
+
const direct = [];
|
|
30417
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
30418
|
+
return [...draped, ...direct];
|
|
30419
|
+
}
|
|
30420
|
+
function terrainStackCount(layers) {
|
|
30421
|
+
let stacks = 0;
|
|
30422
|
+
let prevDraped = false;
|
|
30423
|
+
for (const l of layers) {
|
|
30424
|
+
const d = isDrapeable(l);
|
|
30425
|
+
if (d && !prevDraped) stacks++;
|
|
30426
|
+
prevDraped = d;
|
|
30427
|
+
}
|
|
30428
|
+
return stacks;
|
|
30429
|
+
}
|
|
30430
|
+
function drapeHalves(layers) {
|
|
30431
|
+
const draped = [];
|
|
30432
|
+
const direct = [];
|
|
30433
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
30434
|
+
return { draped, direct };
|
|
30435
|
+
}
|
|
30436
|
+
|
|
30437
|
+
// src/atlas/lamps.ts
|
|
30438
|
+
var LAMP_GLOW_LAYER_ID = "s52-LIGHTS-lamp-glow";
|
|
30439
|
+
var LAMP_CORE_LAYER_ID = "s52-LIGHTS-lamp-core";
|
|
30440
|
+
var LAMP_LAYER_IDS = [LAMP_GLOW_LAYER_ID, LAMP_CORE_LAYER_ID];
|
|
30441
|
+
var RANGE_NM = ["to-number", ["coalesce", ["get", "VALNMR"], ["get", "valnmr"]], 6];
|
|
30442
|
+
var UNSECTORED_POINTS = [
|
|
30443
|
+
"all",
|
|
30444
|
+
["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]],
|
|
30445
|
+
["!", ["has", "SECTR1"]],
|
|
30446
|
+
// …and not lights with a charted rhythm either: those flash, and the
|
|
30447
|
+
// flashing is computed by the app from `SIGSEQ` (light-sigseq.ts). What is
|
|
30448
|
+
// left here is exactly the set that is steady from every bearing.
|
|
30449
|
+
["!", ["has", "SIGSEQ"]]
|
|
30450
|
+
];
|
|
30451
|
+
function buildLampLayers(sourceId, ct) {
|
|
30452
|
+
const colour = lightFillExpression(ct);
|
|
30453
|
+
const common = {
|
|
30454
|
+
type: "circle",
|
|
30455
|
+
source: sourceId,
|
|
30456
|
+
"source-layer": "LIGHTS",
|
|
30457
|
+
filter: UNSECTORED_POINTS,
|
|
30458
|
+
layout: { visibility: "none" }
|
|
30459
|
+
};
|
|
30460
|
+
const metadata = { "chrt:role": "lamp", "s52:objectClass": "LIGHTS" };
|
|
29960
30461
|
return [
|
|
29961
|
-
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
// name), and an unsplit fill draws BOTH — which means the NE polygon is
|
|
29965
|
-
// painted over the planet one at every zoom. That is not a cosmetic
|
|
29966
|
-
// double-draw: NE 10m carries no interior rings, so its blob has no
|
|
29967
|
-
// ISLANDS in it, and painting it over the planet's holed polygons buries
|
|
29968
|
-
// every island in the lake under "uncharted water". Owner-reported at
|
|
29969
|
-
// Winnipesaukee z11.7 ("there is land (and islands) being covered by the
|
|
29970
|
-
// 'no chart here' boundary"), and measured: rendering the planet tier
|
|
29971
|
-
// alone brings back the whole archipelago — Rattlesnake, Bear, Long
|
|
29972
|
-
// Island, Diamond, the Barndoors — while the NE tier alone is a faceted
|
|
29973
|
-
// blob whose edges bear no relation to the shoreline drawn beneath it.
|
|
29974
|
-
//
|
|
29975
|
-
// So each tier owns its own zoom range, the same handover the shoreline
|
|
29976
|
-
// below uses. Known residue, measured over the whole archive: of the 141
|
|
29977
|
-
// NE lakes we still carry, 138 have planet water beneath them; the
|
|
29978
|
-
// exceptions are two unnamed western-Alaska tundra lakes and a 2e-5 deg²
|
|
29979
|
-
// Lake Michigan sliver, which lose their rough fill above z9 because the
|
|
29980
|
-
// planet has no `water` polygon there at all (checked against the raw
|
|
29981
|
-
// pre-clip NDJSON — not a clip artefact).
|
|
29982
|
-
L({
|
|
29983
|
-
id: "chrtbase-inland-water",
|
|
29984
|
-
type: "fill",
|
|
29985
|
-
source: src,
|
|
29986
|
-
"source-layer": "inland_water",
|
|
29987
|
-
filter: NE_TIER,
|
|
29988
|
-
maxzoom: HANDOVER,
|
|
29989
|
-
paint: { "fill-color": T.inlandWater }
|
|
29990
|
-
}),
|
|
29991
|
-
L({
|
|
29992
|
-
id: "chrtbase-inland-hatch",
|
|
29993
|
-
type: "fill",
|
|
29994
|
-
source: src,
|
|
29995
|
-
"source-layer": "inland_water",
|
|
29996
|
-
filter: NE_TIER,
|
|
29997
|
-
maxzoom: HANDOVER,
|
|
29998
|
-
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
29999
|
-
}),
|
|
30000
|
-
L({
|
|
30001
|
-
id: "chrtbase-inland-water-detail",
|
|
30002
|
-
type: "fill",
|
|
30003
|
-
source: src,
|
|
30004
|
-
"source-layer": "inland_water",
|
|
30005
|
-
filter: PLANET_TIER,
|
|
30006
|
-
minzoom: HANDOVER,
|
|
30007
|
-
paint: { "fill-color": T.inlandWater }
|
|
30008
|
-
}),
|
|
30009
|
-
L({
|
|
30010
|
-
id: "chrtbase-inland-hatch-detail",
|
|
30011
|
-
type: "fill",
|
|
30012
|
-
source: src,
|
|
30013
|
-
"source-layer": "inland_water",
|
|
30014
|
-
filter: PLANET_TIER,
|
|
30015
|
-
minzoom: HANDOVER,
|
|
30016
|
-
paint: { "fill-pattern": UNCHARTED_PATTERN, "fill-opacity": 0.7 }
|
|
30017
|
-
}),
|
|
30018
|
-
// The rough shoreline (owner ask 2026-08-28). Drawn ABOVE the hatch so
|
|
30019
|
-
// the edge stays crisp through the texture, and BELOW the charted-water
|
|
30020
|
-
// repaint clones the generator splices in after it — so wherever the
|
|
30021
|
-
// chart repaints its own water, the rough NE10m line yields to the
|
|
30022
|
-
// chart's COALNE instead of double-striking it.
|
|
30023
|
-
//
|
|
30024
|
-
// NE-tier geometry ONLY (`kind` absent): the planet detail tier joins
|
|
30025
|
-
// the same source-layer from z9 as MANY polygons whose chunk-clip seams
|
|
30026
|
-
// rejoin invisibly as FILLS — but a line layer strokes every seam, and
|
|
30027
|
-
// Winnipesaukee rendered as a mesh of internal edges. Planet features
|
|
30028
|
-
// carry `kind` (the INLAND_KINDS filter's own field); NE's carry only
|
|
30029
|
-
// `name`, so the absence of `kind` IS the tier discriminator.
|
|
30030
|
-
L({
|
|
30031
|
-
id: "chrtbase-inland-shore",
|
|
30032
|
-
type: "line",
|
|
30033
|
-
source: src,
|
|
30034
|
-
"source-layer": "inland_water",
|
|
30035
|
-
filter: NE_TIER,
|
|
30036
|
-
// Hands the shoreline over to the planet tier at ITS floor. Both
|
|
30037
|
-
// tiers live in `inland_water` from z9 up (tile-join concatenates the
|
|
30038
|
-
// same layer name), and their FILLS merge invisibly — but two
|
|
30039
|
-
// outlines do not: NE 10m generalises Winnipesaukee's shape, so its
|
|
30040
|
-
// outline ran straight across the middle of the lake the planet had
|
|
30041
|
-
// drawn correctly around. The coarse line is right only while it is
|
|
30042
|
-
// the only shape we have.
|
|
30043
|
-
maxzoom: HANDOVER,
|
|
30044
|
-
paint: {
|
|
30045
|
-
"line-color": T.inlandShore,
|
|
30046
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.5, 10, 1.1, 14, 1.6],
|
|
30047
|
-
"line-opacity": 0.9
|
|
30048
|
-
}
|
|
30049
|
-
}),
|
|
30050
|
-
// The PLANET tier's shoreline (owner ask 2026-08-31: "uncharted small
|
|
30051
|
-
// lakes render with no outline"). It reads its own `inland_shore` layer,
|
|
30052
|
-
// NOT `inland_water`, and that indirection is the whole fix: the mesh
|
|
30053
|
-
// that forced the `!has kind` fence above is a CLIP artefact — an MVT
|
|
30054
|
-
// feature arrives already cut to its own z10 tile, so stroking the
|
|
30055
|
-
// polygon strokes every tile boundary it crosses. The build now walks
|
|
30056
|
-
// those rings, drops the axis-aligned runs that sit on the read zoom's
|
|
30057
|
-
// tile grid, and emits what is left as lines (measured on one New
|
|
30058
|
-
// England chunk: 61 long axis-aligned mesh edges → 5, and the 5 survivors
|
|
30059
|
-
// are 0.02–0.5 tiles off the grid, i.e. real straight water edges).
|
|
30060
|
-
//
|
|
30061
|
-
// Thinner and quieter than the NE line: this is finer geometry describing
|
|
30062
|
-
// water nobody has surveyed, and approximate data should look
|
|
30063
|
-
// approximate.
|
|
30064
|
-
L({
|
|
30065
|
-
id: "chrtbase-inland-shore-detail",
|
|
30066
|
-
type: "line",
|
|
30067
|
-
source: src,
|
|
30068
|
-
"source-layer": "inland_shore",
|
|
30069
|
-
minzoom: HANDOVER,
|
|
30070
|
-
paint: {
|
|
30071
|
-
"line-color": T.inlandShore,
|
|
30072
|
-
"line-width": ["interpolate", ["linear"], ["zoom"], 9, 0.5, 12, 0.9, 15, 1.2],
|
|
30073
|
-
"line-opacity": 0.85
|
|
30074
|
-
}
|
|
30075
|
-
}),
|
|
30076
|
-
// The lake's name (owner ask 2026-08-28) — the second scoped exception
|
|
30077
|
-
// to no-names-on-the-basemap, after the states: WATER-BODY names only.
|
|
30078
|
-
// No maxzoom: an uncharted lake keeps its name at every zoom; over
|
|
30079
|
-
// charted lakes the chart's own SEAARE label takes over at detail zooms
|
|
30080
|
-
// and collision culls the echo.
|
|
30081
|
-
//
|
|
30082
|
-
// The archive's `inland_water_label` anchors (v3+). v3 shipped ONE
|
|
30083
|
-
// PointOnSurface anchor per lake, and a big lake's single anchor pans
|
|
30084
|
-
// off-viewport — Winnipesaukee at z11.7 read nameless with its name two
|
|
30085
|
-
// tiles west of the screen (owner-reported). Labelling the polygons
|
|
30086
|
-
// instead is NOT the fix: tippecanoe's coalescing drops the NE polygon
|
|
30087
|
-
// from some z11 tiles (measured: 11/619/747 carries only planet pieces),
|
|
30088
|
-
// so a polygon-sourced label vanishes tile by tile. v4 therefore bakes a
|
|
30089
|
-
// deterministic anchor GRID into big lakes — the paper-chart convention
|
|
30090
|
-
// of repeating a name along a large water body — and this one layer
|
|
30091
|
-
// renders however many anchors are in view; collision culls the rest.
|
|
30092
|
-
// Planet-tier water-body NAMES (owner ask 2026-08-31: NE 10m names only
|
|
30093
|
-
// 113 US lakes). Emitted BELOW the NE layer on purpose — MapLibre places
|
|
30094
|
-
// symbols topmost-layer-first, so the big NE lake wins collision against
|
|
30095
|
-
// the pond beside it, and both lose to the chart's own labels (every ENC
|
|
30096
|
-
// class is emitted after this pair).
|
|
30097
|
-
//
|
|
30098
|
-
// The size threshold is OSM's own `min_zoom`, carried through the build:
|
|
30099
|
-
// Lake Winnipesaukee reads 10, Squam Lake 12, so a pond simply is not in
|
|
30100
|
-
// the filter until the user is close enough for it to be the answer to
|
|
30101
|
-
// "what is that water". `["zoom"]` in a filter evaluates at integer zoom
|
|
30102
|
-
// levels and removes the feature from placement entirely — verified
|
|
30103
|
-
// against maplibre-gl-native rather than assumed, because a text-size-0
|
|
30104
|
-
// gate would still claim a collision slot.
|
|
30105
|
-
//
|
|
30106
|
-
// The tiers are separate LAYERS, not a filtered split of one — see the
|
|
30107
|
-
// builder for why: an archive that a not-yet-deployed style would
|
|
30108
|
-
// mis-render cannot be promoted first, and this one has to be.
|
|
30109
|
-
L({
|
|
30110
|
-
id: "chrtbase-inland-label-detail",
|
|
30111
|
-
type: "symbol",
|
|
30112
|
-
source: src,
|
|
30113
|
-
"source-layer": "inland_water_label_detail",
|
|
30114
|
-
minzoom: HANDOVER,
|
|
30115
|
-
filter: [">=", ["zoom"], ["get", "min_zoom"]],
|
|
30116
|
-
layout: {
|
|
30117
|
-
"text-field": ["get", "name"],
|
|
30118
|
-
"text-font": ["Open Sans Regular"],
|
|
30119
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 9, 9, 12, 10.5, 15, 11],
|
|
30120
|
-
"text-max-width": 8,
|
|
30121
|
-
"text-allow-overlap": false,
|
|
30122
|
-
"text-padding": 6,
|
|
30123
|
-
// Bigger water first within the tier — min_zoom IS the size rank.
|
|
30124
|
-
"symbol-sort-key": ["get", "min_zoom"]
|
|
30125
|
-
},
|
|
30462
|
+
{
|
|
30463
|
+
...common,
|
|
30464
|
+
id: LAMP_GLOW_LAYER_ID,
|
|
30126
30465
|
paint: {
|
|
30127
|
-
"
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
"
|
|
30131
|
-
|
|
30132
|
-
|
|
30133
|
-
|
|
30134
|
-
|
|
30135
|
-
|
|
30136
|
-
|
|
30137
|
-
"source-layer": "inland_water_label",
|
|
30138
|
-
minzoom: 5,
|
|
30139
|
-
layout: {
|
|
30140
|
-
"text-field": ["get", "name"],
|
|
30141
|
-
"text-font": ["Open Sans Regular"],
|
|
30142
|
-
"text-size": ["interpolate", ["linear"], ["zoom"], 5, 9.5, 10, 12],
|
|
30143
|
-
"text-max-width": 7,
|
|
30144
|
-
"text-allow-overlap": false,
|
|
30145
|
-
// Zoom-scaled spacing for the anchor GRID: at z8.9 the 0.08-deg
|
|
30146
|
-
// anchors sit ~54 px apart and a flat padding let six copies of
|
|
30147
|
-
// "Lake Ontario" crowd one screen (owner-reported 2026-08-30).
|
|
30148
|
-
// Wide collision boxes at low zoom thin the grid to the repeat-
|
|
30149
|
-
// by-convention cadence; by z12 the anchors are ~850 px apart and
|
|
30150
|
-
// the padding relaxes to normal.
|
|
30151
|
-
"text-padding": ["interpolate", ["linear"], ["zoom"], 5, 80, 9, 64, 12, 16]
|
|
30466
|
+
"circle-color": colour,
|
|
30467
|
+
// 4 nm → 7 px, 12 nm → 12 px, 25 nm → 20 px; capped, because a
|
|
30468
|
+
// primary seacoast light is not a searchlight.
|
|
30469
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 5, 4, 7, 12, 12, 25, 20],
|
|
30470
|
+
"circle-blur": 1,
|
|
30471
|
+
"circle-opacity": 0.55,
|
|
30472
|
+
// A lamp is a point of light: it does not grow as it comes near, and
|
|
30473
|
+
// it does not lie down with the chart.
|
|
30474
|
+
"circle-pitch-alignment": "viewport",
|
|
30475
|
+
"circle-pitch-scale": "viewport"
|
|
30152
30476
|
},
|
|
30477
|
+
metadata: { ...metadata, "chrt:lamp": "glow" }
|
|
30478
|
+
},
|
|
30479
|
+
{
|
|
30480
|
+
...common,
|
|
30481
|
+
id: LAMP_CORE_LAYER_ID,
|
|
30153
30482
|
paint: {
|
|
30154
|
-
"
|
|
30155
|
-
"
|
|
30156
|
-
"
|
|
30157
|
-
|
|
30158
|
-
|
|
30483
|
+
"circle-color": colour,
|
|
30484
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 1.5, 12, 2.5, 25, 3.5],
|
|
30485
|
+
"circle-blur": 0.25,
|
|
30486
|
+
"circle-opacity": 1,
|
|
30487
|
+
"circle-pitch-alignment": "viewport",
|
|
30488
|
+
"circle-pitch-scale": "viewport"
|
|
30489
|
+
},
|
|
30490
|
+
metadata: { ...metadata, "chrt:lamp": "core" }
|
|
30491
|
+
}
|
|
30492
|
+
];
|
|
30493
|
+
}
|
|
30494
|
+
|
|
30495
|
+
// src/atlas/band-priority.ts
|
|
30496
|
+
var BAND_PRIORITY_RANGES = {
|
|
30497
|
+
"1": { minzoom: 0 },
|
|
30498
|
+
"2": { minzoom: 5 },
|
|
30499
|
+
"3": { minzoom: 7 },
|
|
30500
|
+
"4": { minzoom: 9 },
|
|
30501
|
+
"5": { minzoom: 11 },
|
|
30502
|
+
"6": { minzoom: 11 }
|
|
30503
|
+
};
|
|
30504
|
+
var BAND_DATA_MINZOOM = {
|
|
30505
|
+
"1": 0,
|
|
30506
|
+
"2": 5,
|
|
30507
|
+
"3": 7,
|
|
30508
|
+
"4": 9,
|
|
30509
|
+
"5": 11,
|
|
30510
|
+
"6": 13
|
|
30511
|
+
};
|
|
30512
|
+
function dominatedVariantMaxzoom(D) {
|
|
30513
|
+
return Math.max(BAND_PRIORITY_RANGES[D].minzoom, BAND_DATA_MINZOOM[D]);
|
|
30514
|
+
}
|
|
30515
|
+
var DOMINANCE_EXEMPT_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30516
|
+
"LNDARE",
|
|
30517
|
+
"DEPARE",
|
|
30518
|
+
"UNSARE",
|
|
30519
|
+
"LAKARE",
|
|
30520
|
+
"DRGARE",
|
|
30521
|
+
"SEAARE"
|
|
30522
|
+
]);
|
|
30523
|
+
function isExemptOpaqueFill(layer) {
|
|
30524
|
+
if (layer.type !== "fill") return false;
|
|
30525
|
+
for (const cls of DOMINANCE_EXEMPT_FILL_CLASSES) {
|
|
30526
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
30527
|
+
}
|
|
30528
|
+
return false;
|
|
30529
|
+
}
|
|
30530
|
+
var GROUND_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30531
|
+
...DOMINANCE_EXEMPT_FILL_CLASSES,
|
|
30532
|
+
"RIVERS"
|
|
30533
|
+
]);
|
|
30534
|
+
function isGroundFill(layer) {
|
|
30535
|
+
if (layer.type !== "fill") return false;
|
|
30536
|
+
for (const cls of GROUND_FILL_CLASSES) {
|
|
30537
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
30538
|
+
}
|
|
30539
|
+
return false;
|
|
30540
|
+
}
|
|
30541
|
+
var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
30542
|
+
"1",
|
|
30543
|
+
"2",
|
|
30544
|
+
"3"
|
|
30545
|
+
]);
|
|
30546
|
+
function bandVariantMinzoom(layer, B) {
|
|
30547
|
+
const classMinzoom = layer.minzoom ?? 0;
|
|
30548
|
+
if (isGroundFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
30549
|
+
return 0;
|
|
30550
|
+
}
|
|
30551
|
+
if (isGroundFill(layer)) {
|
|
30552
|
+
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
30553
|
+
}
|
|
30554
|
+
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
30555
|
+
}
|
|
30556
|
+
var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
30557
|
+
"BUAARE",
|
|
30558
|
+
// 2026-08-28, New York Harbor: the "New York Marine Inspection Zone and
|
|
30559
|
+
// Captain of the Port Zone" RESARE exists at bands 2/3 as a generalized
|
|
30560
|
+
// shore-hugging polygon whose point-sampled dominance tag MISSED (the
|
|
30561
|
+
// Providence-BUAARE failure mode), so its magenta dashed boundary drew
|
|
30562
|
+
// OFFSET over the band-5 detail at z11.5 — read in the field as "a
|
|
30563
|
+
// magenta shoreline bleeding down from a less coarse band". The same
|
|
30564
|
+
// tile carried untagged band-2 ADMARE and CTNARE giants in the same
|
|
30565
|
+
// state. All three are area classes whose coarse-band boundaries hug
|
|
30566
|
+
// shorelines and whose fine-band copies are authoritative.
|
|
30567
|
+
"RESARE",
|
|
30568
|
+
"CTNARE",
|
|
30569
|
+
"ADMARE"
|
|
30570
|
+
]);
|
|
30571
|
+
var GENERALISED_AREA_MAX_BAND = 3;
|
|
30572
|
+
function generalisedAreaCeiling(layer, B) {
|
|
30573
|
+
if (layer.type !== "fill" && layer.type !== "line") return null;
|
|
30574
|
+
if (parseInt(B, 10) > GENERALISED_AREA_MAX_BAND) return null;
|
|
30575
|
+
for (const cls of GENERALISED_AREA_FILL_CLASSES) {
|
|
30576
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return BAND_PRIORITY_RANGES["5"].minzoom;
|
|
30577
|
+
}
|
|
30578
|
+
return null;
|
|
30579
|
+
}
|
|
30580
|
+
var BAND_DRAW_ORDER = ["1", "2", "3", "4", "5", "6"];
|
|
30581
|
+
var CHART_BAND_CODES = {
|
|
30582
|
+
Overview: ["1"],
|
|
30583
|
+
General: ["2"],
|
|
30584
|
+
Coastal: ["3"],
|
|
30585
|
+
Approach: ["4"],
|
|
30586
|
+
Harbor: ["5", "6"]
|
|
30587
|
+
};
|
|
30588
|
+
var CHART_CASCADE_CODES = {
|
|
30589
|
+
Overview: ["1"],
|
|
30590
|
+
General: ["1", "2"],
|
|
30591
|
+
Coastal: ["1", "2", "3"],
|
|
30592
|
+
Approach: ["1", "2", "3", "4"],
|
|
30593
|
+
Harbor: ["1", "2", "3", "4", "5", "6"]
|
|
30594
|
+
};
|
|
30595
|
+
var CHRT_STYLE_MODES = [
|
|
30596
|
+
"continuous",
|
|
30597
|
+
...Object.keys(CHART_BAND_CODES).map(
|
|
30598
|
+
(band) => `chart:${band}`
|
|
30599
|
+
)
|
|
30600
|
+
];
|
|
30601
|
+
function normalizeStyleMode(raw) {
|
|
30602
|
+
if (raw === null || raw === void 0 || raw === "") return "continuous";
|
|
30603
|
+
const lower = raw.toLowerCase();
|
|
30604
|
+
return CHRT_STYLE_MODES.find((mode) => mode.toLowerCase() === lower) ?? null;
|
|
30605
|
+
}
|
|
30606
|
+
function styleModeBand(mode) {
|
|
30607
|
+
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
30608
|
+
}
|
|
30609
|
+
function bandFilterExpression(codes) {
|
|
30610
|
+
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
30611
|
+
}
|
|
30612
|
+
var BAND_CODE_ALIASES = {
|
|
30613
|
+
"5": ["7"]
|
|
30614
|
+
};
|
|
30615
|
+
function cellBandCharacters(code) {
|
|
30616
|
+
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
30617
|
+
}
|
|
30618
|
+
function cellBandMatch(cellBandExpr, codes) {
|
|
30619
|
+
const list = Array.isArray(codes) ? codes : [codes];
|
|
30620
|
+
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
30621
|
+
if (chars.length === 1) {
|
|
30622
|
+
return ["==", cellBandExpr, chars[0]];
|
|
30623
|
+
}
|
|
30624
|
+
return ["in", cellBandExpr, ["literal", chars]];
|
|
30625
|
+
}
|
|
30626
|
+
function combineWithBandFilter(baseFilter, bandFilter) {
|
|
30627
|
+
if (!baseFilter) return bandFilter;
|
|
30628
|
+
if (baseFilter[0] === "all") {
|
|
30629
|
+
return ["all", ...baseFilter.slice(1), bandFilter];
|
|
30630
|
+
}
|
|
30631
|
+
return ["all", baseFilter, bandFilter];
|
|
30632
|
+
}
|
|
30633
|
+
var NOT_DOMINATED_FILTER = [
|
|
30634
|
+
"all",
|
|
30635
|
+
["!", ["has", "_dominated_by_min"]],
|
|
30636
|
+
["!=", ["get", "_dominated"], true]
|
|
30637
|
+
];
|
|
30638
|
+
function dominatedByMinFilter(D) {
|
|
30639
|
+
const dInt = parseInt(D, 10);
|
|
30640
|
+
if (D === "6") {
|
|
30641
|
+
return [
|
|
30642
|
+
"any",
|
|
30643
|
+
["==", ["get", "_dominated_by_min"], dInt],
|
|
30644
|
+
["==", ["get", "_dominated"], true]
|
|
30645
|
+
];
|
|
30646
|
+
}
|
|
30647
|
+
return ["==", ["get", "_dominated_by_min"], dInt];
|
|
30648
|
+
}
|
|
30649
|
+
function withinBboxFilter(bbox) {
|
|
30650
|
+
const [w, s] = bbox.sw;
|
|
30651
|
+
const [e, n] = bbox.ne;
|
|
30652
|
+
return [
|
|
30653
|
+
"within",
|
|
30654
|
+
{
|
|
30655
|
+
type: "Polygon",
|
|
30656
|
+
coordinates: [
|
|
30657
|
+
[
|
|
30658
|
+
[w, s],
|
|
30659
|
+
[e, s],
|
|
30660
|
+
[e, n],
|
|
30661
|
+
[w, n],
|
|
30662
|
+
[w, s]
|
|
30663
|
+
]
|
|
30664
|
+
]
|
|
30665
|
+
}
|
|
30159
30666
|
];
|
|
30160
30667
|
}
|
|
30668
|
+
function worldMinusBboxPolygon(bbox) {
|
|
30669
|
+
const [w, s] = bbox.sw;
|
|
30670
|
+
const [e, n] = bbox.ne;
|
|
30671
|
+
return {
|
|
30672
|
+
type: "Polygon",
|
|
30673
|
+
coordinates: [
|
|
30674
|
+
[
|
|
30675
|
+
[-180, -85],
|
|
30676
|
+
[180, -85],
|
|
30677
|
+
[180, 85],
|
|
30678
|
+
[-180, 85],
|
|
30679
|
+
[-180, -85]
|
|
30680
|
+
],
|
|
30681
|
+
// Inner ring (hole) — winding opposite to outer ring per GeoJSON
|
|
30682
|
+
// spec. MapLibre is permissive but we follow strict winding.
|
|
30683
|
+
[
|
|
30684
|
+
[w, s],
|
|
30685
|
+
[w, n],
|
|
30686
|
+
[e, n],
|
|
30687
|
+
[e, s],
|
|
30688
|
+
[w, s]
|
|
30689
|
+
]
|
|
30690
|
+
]
|
|
30691
|
+
};
|
|
30692
|
+
}
|
|
30161
30693
|
|
|
30162
30694
|
// src/atlas/tile-number.ts
|
|
30163
30695
|
function tileNumber(property, fallback) {
|
|
@@ -30228,6 +30760,57 @@ var LAND_THEMES = {
|
|
|
30228
30760
|
// skipper is navigating by. Left at the shipped value rather than
|
|
30229
30761
|
// lowered, because lowering it is a separate decision from this one.
|
|
30230
30762
|
exaggeration: 0.45
|
|
30763
|
+
},
|
|
30764
|
+
// ── E-ink: paper land, no relief ──────────────────────────────────────
|
|
30765
|
+
// Hillshade is a continuous grey ramp — exactly what an e-ink panel cannot
|
|
30766
|
+
// show (and a moving one costs a refresh), so both e-ink palettes switch it
|
|
30767
|
+
// off (`exaggeration: 0`). Land is the chart's own LANDA in that table, so
|
|
30768
|
+
// charted and CHRTland land are one tone; labels are black on paper.
|
|
30769
|
+
EINK_COLOR: {
|
|
30770
|
+
base: "#f2e2b0",
|
|
30771
|
+
cover: { wood: "#d6e0b0", wetland: "#d0dccc", urban: "#e0dacb", sand: "#f2e2b0" },
|
|
30772
|
+
contour: "#a08a5a",
|
|
30773
|
+
contourIndex: "#6b5a3e",
|
|
30774
|
+
road: "#d8c08a",
|
|
30775
|
+
roadMajor: "#c49a4a",
|
|
30776
|
+
label: "#000000",
|
|
30777
|
+
halo: "#ffffff",
|
|
30778
|
+
shadow: "#6b5a3e",
|
|
30779
|
+
highlight: "#ffffff",
|
|
30780
|
+
fade: "#ffffff",
|
|
30781
|
+
exaggeration: 0
|
|
30782
|
+
},
|
|
30783
|
+
// Mono: one land tone (MID). Land cover is not drawn as tone — four greys
|
|
30784
|
+
// cannot spare one for "wood" — so every cover class is the land tone.
|
|
30785
|
+
EINK_MONO: {
|
|
30786
|
+
base: "#aaaaaa",
|
|
30787
|
+
cover: { wood: "#aaaaaa", wetland: "#aaaaaa", urban: "#aaaaaa", sand: "#aaaaaa" },
|
|
30788
|
+
contour: "#555555",
|
|
30789
|
+
contourIndex: "#555555",
|
|
30790
|
+
road: "#ffffff",
|
|
30791
|
+
roadMajor: "#ffffff",
|
|
30792
|
+
label: "#000000",
|
|
30793
|
+
halo: "#ffffff",
|
|
30794
|
+
shadow: "#555555",
|
|
30795
|
+
highlight: "#ffffff",
|
|
30796
|
+
fade: "#ffffff",
|
|
30797
|
+
exaggeration: 0
|
|
30798
|
+
},
|
|
30799
|
+
// Night: land is the DIM grey (the chart's EINK_NIGHT LANDA), drawn on in
|
|
30800
|
+
// black so the land adds no lit lines of its own; labels the night line grey.
|
|
30801
|
+
EINK_NIGHT: {
|
|
30802
|
+
base: "#555555",
|
|
30803
|
+
cover: { wood: "#555555", wetland: "#555555", urban: "#555555", sand: "#555555" },
|
|
30804
|
+
contour: "#000000",
|
|
30805
|
+
contourIndex: "#000000",
|
|
30806
|
+
road: "#000000",
|
|
30807
|
+
roadMajor: "#000000",
|
|
30808
|
+
label: "#aaaaaa",
|
|
30809
|
+
halo: "#000000",
|
|
30810
|
+
shadow: "#000000",
|
|
30811
|
+
highlight: "#555555",
|
|
30812
|
+
fade: "#000000",
|
|
30813
|
+
exaggeration: 0
|
|
30231
30814
|
}
|
|
30232
30815
|
};
|
|
30233
30816
|
var FITS = ["<=", tileNumber("min_z", 0), ["zoom"]];
|
|
@@ -30610,7 +31193,10 @@ var META_LAYERS = /* @__PURE__ */ new Set([
|
|
|
30610
31193
|
var PALETTE_NAMES = {
|
|
30611
31194
|
DAY: "DAY_BRIGHT",
|
|
30612
31195
|
DUSK: "DUSK",
|
|
30613
|
-
NIGHT: "NIGHT"
|
|
31196
|
+
NIGHT: "NIGHT",
|
|
31197
|
+
EINK_COLOR: "EINK_COLOR",
|
|
31198
|
+
EINK_MONO: "EINK_MONO",
|
|
31199
|
+
EINK_NIGHT: "EINK_NIGHT"
|
|
30614
31200
|
};
|
|
30615
31201
|
var SYMBOL_SET_LABELS = {
|
|
30616
31202
|
s52: "S-52",
|
|
@@ -30722,7 +31308,7 @@ var ORIENTATION_OPACITY_SCALE = {
|
|
|
30722
31308
|
};
|
|
30723
31309
|
var ORIENTATION_OPACITY_MAX = 0.92;
|
|
30724
31310
|
var ORIENTATION_BOOST_MINZOOM = 11;
|
|
30725
|
-
function fanOrientationLabelByCellBand(label, bandFan,
|
|
31311
|
+
function fanOrientationLabelByCellBand(label, bandFan, objectClass2) {
|
|
30726
31312
|
const cellBandExpr = [
|
|
30727
31313
|
"slice",
|
|
30728
31314
|
["coalesce", ["get", "_cell_id"], "??9???"],
|
|
@@ -30730,7 +31316,7 @@ function fanOrientationLabelByCellBand(label, bandFan, objectClass) {
|
|
|
30730
31316
|
3
|
|
30731
31317
|
];
|
|
30732
31318
|
const variants = [];
|
|
30733
|
-
const scale =
|
|
31319
|
+
const scale = objectClass2 ? ORIENTATION_OPACITY_SCALE[objectClass2] ?? 1 : 1;
|
|
30734
31320
|
for (const code of BAND_DRAW_ORDER) {
|
|
30735
31321
|
const range = BAND_PRIORITY_RANGES[code];
|
|
30736
31322
|
const curve = CELL_BAND_OPACITY_CURVE[code];
|
|
@@ -30929,14 +31515,14 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30929
31515
|
const bandFan = opts.bandFan ?? "collapsed";
|
|
30930
31516
|
const lockedBounds = opts.chartMode?.lockedBounds;
|
|
30931
31517
|
const bundles = [];
|
|
30932
|
-
for (const
|
|
30933
|
-
if (META_LAYERS.has(
|
|
31518
|
+
for (const objectClass2 of orderedClasses) {
|
|
31519
|
+
if (META_LAYERS.has(objectClass2)) continue;
|
|
30934
31520
|
bundles.push({
|
|
30935
|
-
objectClass,
|
|
30936
|
-
featureLayers: buildLayersForClass(
|
|
31521
|
+
objectClass: objectClass2,
|
|
31522
|
+
featureLayers: buildLayersForClass(objectClass2, palette, sourceId, {
|
|
30937
31523
|
safetyContour: opts.safetyContour
|
|
30938
31524
|
}),
|
|
30939
|
-
label: buildLabelLayer2(
|
|
31525
|
+
label: buildLabelLayer2(objectClass2, palette, sourceId, opts.depthUnits ?? "meters", {
|
|
30940
31526
|
safetyContour: opts.safetyContour,
|
|
30941
31527
|
twoShadeSoundings: opts.twoShadeSoundings,
|
|
30942
31528
|
soundingFontWeight: opts.soundingFontWeight,
|
|
@@ -31081,17 +31667,17 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
31081
31667
|
if (opts.labels === "placename") {
|
|
31082
31668
|
labelLayers.push(...buildPlaceNameLayers(sourceId, palette));
|
|
31083
31669
|
} else {
|
|
31084
|
-
for (const { objectClass, label } of bundles) {
|
|
31670
|
+
for (const { objectClass: objectClass2, label } of bundles) {
|
|
31085
31671
|
if (!label) continue;
|
|
31086
|
-
if (getLabelBehavior(
|
|
31087
|
-
if (
|
|
31672
|
+
if (getLabelBehavior(objectClass2) === "orientation-cross-band") {
|
|
31673
|
+
if (objectClass2 === "LNDARE" || objectClass2 === "SEAARE") {
|
|
31088
31674
|
const Z = ISLAND_LABEL_HOIST_MINZOOM;
|
|
31089
|
-
const { hoist, watermark } =
|
|
31090
|
-
fanOrientationLabelByCellBand(label, bandFan,
|
|
31675
|
+
const { hoist, watermark } = objectClass2 === "LNDARE" ? splitOrientationFan(
|
|
31676
|
+
fanOrientationLabelByCellBand(label, bandFan, objectClass2),
|
|
31091
31677
|
[">=", ["zoom"], Z],
|
|
31092
31678
|
["<", ["zoom"], Z]
|
|
31093
31679
|
) : splitOrientationFan(
|
|
31094
|
-
fanOrientationLabelByCellBand(label, bandFan,
|
|
31680
|
+
fanOrientationLabelByCellBand(label, bandFan, objectClass2),
|
|
31095
31681
|
["<=", buildSeaAreaTierExpression(), MAJOR_WATER_TIER_MAX],
|
|
31096
31682
|
[">", buildSeaAreaTierExpression(), MAJOR_WATER_TIER_MAX],
|
|
31097
31683
|
{ "text-padding": MAJOR_WATER_TEXT_PADDING }
|
|
@@ -31100,7 +31686,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
31100
31686
|
labelLayers.push(...watermark);
|
|
31101
31687
|
continue;
|
|
31102
31688
|
}
|
|
31103
|
-
labelLayers.push(...fanOrientationLabelByCellBand(label, bandFan,
|
|
31689
|
+
labelLayers.push(...fanOrientationLabelByCellBand(label, bandFan, objectClass2));
|
|
31104
31690
|
}
|
|
31105
31691
|
}
|
|
31106
31692
|
}
|
|
@@ -31364,10 +31950,8 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
31364
31950
|
glyphs: `${glyphsBase}/{fontstack}/{range}.pbf`,
|
|
31365
31951
|
layers: finalLayers
|
|
31366
31952
|
};
|
|
31367
|
-
|
|
31368
|
-
|
|
31369
|
-
if (symbolSet === "raster") return applyRasterSymbolSet(style);
|
|
31370
|
-
return style;
|
|
31953
|
+
const familyStyle = symbolSet === "s101" ? applyS101SymbolSet(style) : symbolSet === "chrt" ? applyChrtSymbolSet(style, palette) : symbolSet === "raster" ? applyRasterSymbolSet(style) : style;
|
|
31954
|
+
return applyEinkPortrayal(familyStyle, palette, { safetyContour: opts.safetyContour });
|
|
31371
31955
|
}
|
|
31372
31956
|
function generateStyleJSON(palette = "DAY", opts = {}) {
|
|
31373
31957
|
return JSON.stringify(generateStyle(palette, opts), null, 2);
|
|
@@ -31511,7 +32095,7 @@ var DEFAULT_CUSTOM_STYLE_CONFIG = {
|
|
|
31511
32095
|
water_body_settings: DEFAULT_WATER_BODY_SETTINGS,
|
|
31512
32096
|
visible_layers: null
|
|
31513
32097
|
};
|
|
31514
|
-
var COLOR_TABLE_NAMES = /* @__PURE__ */ new Set(["DAY", "DUSK", "NIGHT"]);
|
|
32098
|
+
var COLOR_TABLE_NAMES = /* @__PURE__ */ new Set(["DAY", "DUSK", "NIGHT", "EINK_COLOR", "EINK_MONO", "EINK_NIGHT"]);
|
|
31515
32099
|
var DISPLAY_MODES = /* @__PURE__ */ new Set(["BASE", "STANDARD", "ALL"]);
|
|
31516
32100
|
var LAYER_NAME_RE = /^[A-Za-z0-9_$]{2,16}$/;
|
|
31517
32101
|
var MAX_VISIBLE_LAYERS = 512;
|
|
@@ -31618,12 +32202,12 @@ function sanitizeCustomStyleConfig(raw) {
|
|
|
31618
32202
|
}
|
|
31619
32203
|
function normalizeTheme(raw) {
|
|
31620
32204
|
if (!raw) return null;
|
|
31621
|
-
const upper = raw.trim().toUpperCase();
|
|
32205
|
+
const upper = raw.trim().toUpperCase().replace(/-/g, "_");
|
|
31622
32206
|
return COLOR_TABLE_NAMES.has(upper) ? upper : null;
|
|
31623
32207
|
}
|
|
31624
32208
|
function resolveCustomTheme(config, requested) {
|
|
31625
32209
|
if (!requested) return { palette: config.default_color_table, themeFellBack: false };
|
|
31626
|
-
if (config.enabled_color_tables.includes(requested)) {
|
|
32210
|
+
if (isEinkColorTable(requested) || config.enabled_color_tables.includes(requested)) {
|
|
31627
32211
|
return { palette: requested, themeFellBack: false };
|
|
31628
32212
|
}
|
|
31629
32213
|
return { palette: config.default_color_table, themeFellBack: true };
|
|
@@ -32302,6 +32886,11 @@ var Chrt = class {
|
|
|
32302
32886
|
DEPTH_LABEL_CLASSES,
|
|
32303
32887
|
DEPTH_LABEL_SIZES,
|
|
32304
32888
|
DRAPEABLE_LAYER_TYPES,
|
|
32889
|
+
EINK_COLOR_TABLES,
|
|
32890
|
+
EINK_DANGER_PATTERN,
|
|
32891
|
+
EINK_DOTS_PATTERN,
|
|
32892
|
+
EINK_HATCH_PATTERN,
|
|
32893
|
+
EINK_MONO_GREYS,
|
|
32305
32894
|
INSPECT_CLASS_RANK,
|
|
32306
32895
|
INSPECT_RANK_DEVIATIONS,
|
|
32307
32896
|
InteractClient,
|
|
@@ -32354,6 +32943,9 @@ var Chrt = class {
|
|
|
32354
32943
|
S52_DAY,
|
|
32355
32944
|
S52_DRAW_ORDER,
|
|
32356
32945
|
S52_DUSK,
|
|
32946
|
+
S52_EINK_COLOR,
|
|
32947
|
+
S52_EINK_MONO,
|
|
32948
|
+
S52_EINK_NIGHT,
|
|
32357
32949
|
S52_LOOKUP,
|
|
32358
32950
|
S52_NIGHT,
|
|
32359
32951
|
S52_PATTERN_SPRITE_PREFIX,
|
|
@@ -32373,6 +32965,7 @@ var Chrt = class {
|
|
|
32373
32965
|
applyColorTheme,
|
|
32374
32966
|
applyCustomStyleSettings,
|
|
32375
32967
|
applyDepthLabelSize,
|
|
32968
|
+
applyEinkPortrayal,
|
|
32376
32969
|
applyRasterSymbolSet,
|
|
32377
32970
|
applyS101SymbolSet,
|
|
32378
32971
|
atonIconExpression,
|
|
@@ -32452,6 +33045,8 @@ var Chrt = class {
|
|
|
32452
33045
|
iconExpressionForFamily,
|
|
32453
33046
|
isDepthAreaClassLayerId,
|
|
32454
33047
|
isDrapeable,
|
|
33048
|
+
isEinkColorTable,
|
|
33049
|
+
isEinkGreyTable,
|
|
32455
33050
|
isLabelLayerVisible,
|
|
32456
33051
|
isMapLive,
|
|
32457
33052
|
isNavAidLayer,
|
|
@@ -32491,6 +33086,7 @@ var Chrt = class {
|
|
|
32491
33086
|
resolveTargetClass,
|
|
32492
33087
|
rhythmProperties,
|
|
32493
33088
|
s101ArtworkProvenance,
|
|
33089
|
+
safetyContourValues,
|
|
32494
33090
|
sanitizeCustomStyleConfig,
|
|
32495
33091
|
scaleLinearLight,
|
|
32496
33092
|
scaleSizeValue,
|