@chrtco/sdk 0.10.39 → 0.10.41
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 +146 -0
- package/dist/index.cjs +991 -656
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +531 -17
- package/dist/index.d.ts +531 -17
- package/dist/index.js +960 -656
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,8 +24,16 @@ __export(index_exports, {
|
|
|
24
24
|
ALL_WATER_BODY_LAYERS: () => ALL_WATER_BODY_LAYERS,
|
|
25
25
|
AREA_QUERY_LAYERS: () => AREA_QUERY_LAYERS,
|
|
26
26
|
ATON_EXPRESSION_CLASSES: () => ATON_EXPRESSION_CLASSES,
|
|
27
|
+
BAND_CODE_ALIASES: () => BAND_CODE_ALIASES,
|
|
27
28
|
BAND_DRAW_ORDER: () => BAND_DRAW_ORDER,
|
|
28
29
|
BAND_PRIORITY_RANGES: () => BAND_PRIORITY_RANGES,
|
|
30
|
+
BASE_ATTRIBUTION: () => BASE_ATTRIBUTION,
|
|
31
|
+
BASE_BUNDLE_ATTRIBUTION: () => BASE_BUNDLE_ATTRIBUTION,
|
|
32
|
+
BASE_BUNDLE_ID: () => BASE_BUNDLE_ID,
|
|
33
|
+
BASE_BUNDLE_MAXZOOM: () => BASE_BUNDLE_MAXZOOM,
|
|
34
|
+
BASE_BUNDLE_SCHEMA: () => BASE_BUNDLE_SCHEMA,
|
|
35
|
+
BASE_BUNDLE_SOURCE_LAYERS: () => BASE_BUNDLE_SOURCE_LAYERS,
|
|
36
|
+
BASE_CLIENT_RASTER_BEFORE_ID: () => BASE_CLIENT_RASTER_BEFORE_ID,
|
|
29
37
|
BASE_MAXZOOM: () => BASE_MAXZOOM,
|
|
30
38
|
BASE_MINZOOM: () => BASE_MINZOOM,
|
|
31
39
|
BASE_REGION: () => BASE_REGION,
|
|
@@ -58,6 +66,7 @@ __export(index_exports, {
|
|
|
58
66
|
DEFAULT_TILE_BASE_URL: () => DEFAULT_TILE_BASE_URL,
|
|
59
67
|
DEFAULT_TILE_REGION: () => DEFAULT_TILE_REGION,
|
|
60
68
|
DEFAULT_WATER_BODY_SETTINGS: () => DEFAULT_WATER_BODY_SETTINGS,
|
|
69
|
+
DRAPEABLE_LAYER_TYPES: () => DRAPEABLE_LAYER_TYPES,
|
|
61
70
|
INSPECT_CLASS_RANK: () => INSPECT_CLASS_RANK,
|
|
62
71
|
INSPECT_RANK_DEVIATIONS: () => INSPECT_RANK_DEVIATIONS,
|
|
63
72
|
InteractClient: () => InteractClient,
|
|
@@ -66,6 +75,11 @@ __export(index_exports, {
|
|
|
66
75
|
LABEL_BEHAVIOR: () => LABEL_BEHAVIOR,
|
|
67
76
|
LABEL_LAYER_GROUPS: () => LABEL_LAYER_GROUPS,
|
|
68
77
|
LABEL_PRIORITY: () => LABEL_PRIORITY,
|
|
78
|
+
LAMP_CORE_LAYER_ID: () => LAMP_CORE_LAYER_ID,
|
|
79
|
+
LAMP_GLOW_LAYER_ID: () => LAMP_GLOW_LAYER_ID,
|
|
80
|
+
LAMP_LAYER_IDS: () => LAMP_LAYER_IDS,
|
|
81
|
+
LAMP_TICK_MS: () => LAMP_TICK_MS,
|
|
82
|
+
LAND_ATTRIBUTION: () => LAND_ATTRIBUTION,
|
|
69
83
|
LAND_BUILD_MAXZOOM: () => LAND_BUILD_MAXZOOM,
|
|
70
84
|
LAND_REGION: () => LAND_REGION,
|
|
71
85
|
NAUTICAL_SYMBOLS: () => NAUTICAL_SYMBOLS,
|
|
@@ -81,7 +95,10 @@ __export(index_exports, {
|
|
|
81
95
|
RASTER_ATON_CLASSES: () => RASTER_ATON_CLASSES,
|
|
82
96
|
RASTER_GLYPH_NAMES: () => RASTER_GLYPH_NAMES,
|
|
83
97
|
RASTER_ICON_PREFIX: () => RASTER_ICON_PREFIX,
|
|
98
|
+
RELIEF_ATTRIBUTION: () => RELIEF_ATTRIBUTION,
|
|
99
|
+
RELIEF_ENCODING: () => RELIEF_ENCODING,
|
|
84
100
|
RELIEF_REGION: () => RELIEF_REGION,
|
|
101
|
+
RELIEF_TILE_SIZE: () => RELIEF_TILE_SIZE,
|
|
85
102
|
S101_AREA_FILL_NAMES: () => S101_AREA_FILL_NAMES,
|
|
86
103
|
S101_CHART_SECTIONS: () => S101_CHART_SECTIONS,
|
|
87
104
|
S101_FEATURES: () => S101_FEATURES,
|
|
@@ -101,6 +118,8 @@ __export(index_exports, {
|
|
|
101
118
|
S52_LOOKUP: () => S52_LOOKUP,
|
|
102
119
|
S52_NIGHT: () => S52_NIGHT,
|
|
103
120
|
S52_PATTERN_SPRITE_PREFIX: () => S52_PATTERN_SPRITE_PREFIX,
|
|
121
|
+
SIGPER_TOLERANCE_SECONDS: () => SIGPER_TOLERANCE_SECONDS,
|
|
122
|
+
SIGSEQ_MAX_TERMS: () => SIGSEQ_MAX_TERMS,
|
|
104
123
|
SOUNDING_LAYERS: () => SOUNDING_LAYERS,
|
|
105
124
|
SYMBOL_FAMILIES: () => SYMBOL_FAMILIES,
|
|
106
125
|
SYMBOL_FAMILY_META: () => SYMBOL_FAMILY_META,
|
|
@@ -110,6 +129,7 @@ __export(index_exports, {
|
|
|
110
129
|
TileTokenError: () => TileTokenError,
|
|
111
130
|
WATER_BODY_LAYER_GROUPS: () => WATER_BODY_LAYER_GROUPS,
|
|
112
131
|
WeatherEndpoint: () => WeatherEndpoint,
|
|
132
|
+
applyBaseBundle: () => applyBaseBundle,
|
|
113
133
|
applyChrtSymbolSet: () => applyChrtSymbolSet,
|
|
114
134
|
applyColorTheme: () => applyColorTheme,
|
|
115
135
|
applyCustomStyleSettings: () => applyCustomStyleSettings,
|
|
@@ -122,6 +142,7 @@ __export(index_exports, {
|
|
|
122
142
|
buildFlashPattern: () => buildFlashPattern,
|
|
123
143
|
buildLabelLayer: () => buildLabelLayer,
|
|
124
144
|
buildLakeTierExpression: () => buildLakeTierExpression,
|
|
145
|
+
buildLampLayers: () => buildLampLayers,
|
|
125
146
|
buildLandAreaTierExpression: () => buildLandAreaTierExpression,
|
|
126
147
|
buildLandRegionTierExpression: () => buildLandRegionTierExpression,
|
|
127
148
|
buildLayersForClass: () => buildLayersForClass,
|
|
@@ -134,6 +155,8 @@ __export(index_exports, {
|
|
|
134
155
|
buildSectorsFromFeatures: () => buildSectorsFromFeatures,
|
|
135
156
|
buildSources: () => buildSources,
|
|
136
157
|
candidateRank: () => candidateRank,
|
|
158
|
+
cellBandCharacters: () => cellBandCharacters,
|
|
159
|
+
cellBandMatch: () => cellBandMatch,
|
|
137
160
|
chrtAtonTintExpression: () => chrtAtonTintExpression,
|
|
138
161
|
chrtLightTintExpression: () => chrtLightTintExpression,
|
|
139
162
|
collectCandidates: () => collectCandidates,
|
|
@@ -148,6 +171,8 @@ __export(index_exports, {
|
|
|
148
171
|
depareColor: () => depareColor,
|
|
149
172
|
depareFillExpression: () => depareFillExpression,
|
|
150
173
|
deriveMarinerName: () => deriveMarinerName,
|
|
174
|
+
drapeHalves: () => drapeHalves,
|
|
175
|
+
drapedFirst: () => drapedFirst,
|
|
151
176
|
extractNavAidDesignation: () => extractNavAidDesignation,
|
|
152
177
|
featureIdentity: () => featureIdentity,
|
|
153
178
|
generateCustomStyle: () => generateCustomStyle,
|
|
@@ -180,6 +205,7 @@ __export(index_exports, {
|
|
|
180
205
|
hazardColor: () => hazardColor,
|
|
181
206
|
hazardFillExpression: () => hazardFillExpression,
|
|
182
207
|
iconExpressionForFamily: () => iconExpressionForFamily,
|
|
208
|
+
isDrapeable: () => isDrapeable,
|
|
183
209
|
isLabelLayerVisible: () => isLabelLayerVisible,
|
|
184
210
|
isNavAidLayer: () => isNavAidLayer,
|
|
185
211
|
isNavAidLayerVisible: () => isNavAidLayerVisible,
|
|
@@ -187,6 +213,8 @@ __export(index_exports, {
|
|
|
187
213
|
isVisibleInMode: () => isVisibleInMode,
|
|
188
214
|
isWaterBodyLayerVisible: () => isWaterBodyLayerVisible,
|
|
189
215
|
labelSortKey: () => labelSortKey,
|
|
216
|
+
lampRhythmColour: () => lampRhythmColour,
|
|
217
|
+
lampRhythmOpacity: () => lampRhythmOpacity,
|
|
190
218
|
lateralColor: () => lateralColor,
|
|
191
219
|
lateralFillExpression: () => lateralFillExpression,
|
|
192
220
|
lightColor: () => lightColor,
|
|
@@ -196,6 +224,7 @@ __export(index_exports, {
|
|
|
196
224
|
normalizeTheme: () => normalizeTheme,
|
|
197
225
|
paginate: () => paginate,
|
|
198
226
|
parseListCodes: () => parseListCodes,
|
|
227
|
+
parseSigseqTerms: () => parseSigseqTerms,
|
|
199
228
|
parseSymbolFamily: () => parseSymbolFamily,
|
|
200
229
|
parseWebhook: () => parseWebhook,
|
|
201
230
|
patternSpriteId: () => patternSpriteId,
|
|
@@ -210,12 +239,14 @@ __export(index_exports, {
|
|
|
210
239
|
resolveS101Color: () => resolveS101Color,
|
|
211
240
|
resolveStyle: () => resolveStyle,
|
|
212
241
|
resolveTargetClass: () => resolveTargetClass,
|
|
242
|
+
rhythmProperties: () => rhythmProperties,
|
|
213
243
|
s101ArtworkProvenance: () => s101ArtworkProvenance,
|
|
214
244
|
sanitizeCustomStyleConfig: () => sanitizeCustomStyleConfig,
|
|
215
245
|
scaleSizeValue: () => scaleSizeValue,
|
|
216
246
|
scaminToMinZoom: () => scaminToMinZoom,
|
|
217
247
|
sectorLimitToArcBearing: () => sectorLimitToArcBearing,
|
|
218
248
|
styleModeBand: () => styleModeBand,
|
|
249
|
+
terrainStackCount: () => terrainStackCount,
|
|
219
250
|
tileTemplate: () => tileTemplate,
|
|
220
251
|
uncoveredSectorArcs: () => uncoveredSectorArcs,
|
|
221
252
|
withinBboxFilter: () => withinBboxFilter,
|
|
@@ -592,9 +623,13 @@ var RELIEF_REGION = "CHRT_RELIEF";
|
|
|
592
623
|
var BASE_REGION = "CHRT_BASE";
|
|
593
624
|
var BASE_MINZOOM = 0;
|
|
594
625
|
var BASE_MAXZOOM = 11;
|
|
626
|
+
var BASE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth \xB7 \xA9 OpenStreetMap contributors";
|
|
627
|
+
var LAND_ATTRIBUTION = "\xA9 OpenStreetMap contributors";
|
|
628
|
+
var RELIEF_ATTRIBUTION = "Elevation: USGS 3D Elevation Program";
|
|
595
629
|
var RELIEF_MINZOOM = 9;
|
|
596
630
|
var RELIEF_MAXZOOM = 13;
|
|
597
631
|
var RELIEF_TILE_SIZE = 512;
|
|
632
|
+
var RELIEF_ENCODING = "terrarium";
|
|
598
633
|
var BUILD_PARAM = "v";
|
|
599
634
|
function tileTemplate(base, region, extension, buildId) {
|
|
600
635
|
const url = `${base}/v1/tiles/${region}/{z}/{x}/{y}.${extension}`;
|
|
@@ -630,7 +665,8 @@ function buildSources(opts = {}) {
|
|
|
630
665
|
type: "vector",
|
|
631
666
|
tiles: [tileTemplate(base, LAND_REGION, "mvt", buildFor(LAND_REGION))],
|
|
632
667
|
minzoom: BUILD_MINZOOM,
|
|
633
|
-
maxzoom: LAND_BUILD_MAXZOOM
|
|
668
|
+
maxzoom: LAND_BUILD_MAXZOOM,
|
|
669
|
+
attribution: LAND_ATTRIBUTION
|
|
634
670
|
};
|
|
635
671
|
}
|
|
636
672
|
if (opts.base) {
|
|
@@ -638,17 +674,19 @@ function buildSources(opts = {}) {
|
|
|
638
674
|
type: "vector",
|
|
639
675
|
tiles: [tileTemplate(base, BASE_REGION, "mvt", buildFor(BASE_REGION))],
|
|
640
676
|
minzoom: BASE_MINZOOM,
|
|
641
|
-
maxzoom: BASE_MAXZOOM
|
|
677
|
+
maxzoom: BASE_MAXZOOM,
|
|
678
|
+
attribution: BASE_ATTRIBUTION
|
|
642
679
|
};
|
|
643
680
|
}
|
|
644
681
|
if (opts.relief) {
|
|
645
682
|
sources[RELIEF_REGION] = {
|
|
646
683
|
type: "raster-dem",
|
|
647
684
|
tiles: [tileTemplate(base, RELIEF_REGION, "png", buildFor(RELIEF_REGION))],
|
|
648
|
-
encoding:
|
|
685
|
+
encoding: RELIEF_ENCODING,
|
|
649
686
|
tileSize: RELIEF_TILE_SIZE,
|
|
650
687
|
minzoom: RELIEF_MINZOOM,
|
|
651
|
-
maxzoom: RELIEF_MAXZOOM
|
|
688
|
+
maxzoom: RELIEF_MAXZOOM,
|
|
689
|
+
attribution: RELIEF_ATTRIBUTION
|
|
652
690
|
};
|
|
653
691
|
}
|
|
654
692
|
return sources;
|
|
@@ -671,6 +709,7 @@ var TilesEndpoint = class {
|
|
|
671
709
|
if (opts.mode) params.set("mode", opts.mode);
|
|
672
710
|
if (opts.land) params.set("land", "1");
|
|
673
711
|
if (opts.relief) params.set("relief", "1");
|
|
712
|
+
if (opts.units === "imperial") params.set("units", "imperial");
|
|
674
713
|
const query2 = params.size > 0 ? `?${params.toString()}` : "";
|
|
675
714
|
return `${this.tileBaseUrl}/v1/styles/${this.region}/style.json${query2}`;
|
|
676
715
|
}
|
|
@@ -796,6 +835,9 @@ var WeatherEndpoint = class {
|
|
|
796
835
|
*
|
|
797
836
|
* `unavailable` names every field the station did not report, so absence is
|
|
798
837
|
* stated rather than inferred from a missing key.
|
|
838
|
+
*
|
|
839
|
+
* Pass `hours` for the station's recent readings as `series` — NDBC's own
|
|
840
|
+
* readings at NDBC's own instants, never interpolated.
|
|
799
841
|
*/
|
|
800
842
|
async conditions(options) {
|
|
801
843
|
return this.http.request(
|
|
@@ -804,7 +846,8 @@ var WeatherEndpoint = class {
|
|
|
804
846
|
lat: options.lat,
|
|
805
847
|
lon: options.lon,
|
|
806
848
|
units: options.units,
|
|
807
|
-
station: options.station
|
|
849
|
+
station: options.station,
|
|
850
|
+
hours: options.hours
|
|
808
851
|
},
|
|
809
852
|
options.signal
|
|
810
853
|
);
|
|
@@ -1268,10 +1311,10 @@ async function readManifest(manifestPath, fs) {
|
|
|
1268
1311
|
if (!manifestPath) return emptyManifest();
|
|
1269
1312
|
if (!await fs.exists(manifestPath)) return emptyManifest();
|
|
1270
1313
|
const raw = await fs.readFile(manifestPath);
|
|
1271
|
-
const
|
|
1314
|
+
const text2 = new TextDecoder().decode(raw);
|
|
1272
1315
|
let parsed;
|
|
1273
1316
|
try {
|
|
1274
|
-
parsed = JSON.parse(
|
|
1317
|
+
parsed = JSON.parse(text2);
|
|
1275
1318
|
} catch {
|
|
1276
1319
|
throw new OfflineError(
|
|
1277
1320
|
"unexpected_response",
|
|
@@ -1295,8 +1338,8 @@ async function readManifest(manifestPath, fs) {
|
|
|
1295
1338
|
};
|
|
1296
1339
|
}
|
|
1297
1340
|
async function writeManifest(manifestPath, fs, manifest) {
|
|
1298
|
-
const
|
|
1299
|
-
const bytes = new TextEncoder().encode(
|
|
1341
|
+
const text2 = JSON.stringify(manifest, null, 2);
|
|
1342
|
+
const bytes = new TextEncoder().encode(text2);
|
|
1300
1343
|
await fs.writeFile(manifestPath, bytes);
|
|
1301
1344
|
}
|
|
1302
1345
|
function usedBytes(manifest, excludeRegion) {
|
|
@@ -2261,110 +2304,438 @@ function s57AbbrTable(acronym) {
|
|
|
2261
2304
|
return cached;
|
|
2262
2305
|
}
|
|
2263
2306
|
|
|
2264
|
-
// src/
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2307
|
+
// src/atlas/s52-colors.ts
|
|
2308
|
+
var S52_DAY = {
|
|
2309
|
+
// Depth
|
|
2310
|
+
NODTA: "#a4a4a4",
|
|
2311
|
+
DEPIT: "#009678",
|
|
2312
|
+
DEPVS: "#7fbfdf",
|
|
2313
|
+
DEPMS: "#a7d4e8",
|
|
2314
|
+
DEPMD: "#cae7f3",
|
|
2315
|
+
DEPDW: "#ecf7fc",
|
|
2316
|
+
// Land / topography
|
|
2317
|
+
LANDA: "#e6d1a1",
|
|
2318
|
+
LANDF: "#c8b078",
|
|
2319
|
+
CHBRN: "#ab7948",
|
|
2320
|
+
CHGRF: "#878787",
|
|
2321
|
+
CHGRD: "#555555",
|
|
2322
|
+
// Chart symbology
|
|
2323
|
+
CHBLK: "#000000",
|
|
2324
|
+
CSTLN: "#000000",
|
|
2325
|
+
OUTLW: "#000000",
|
|
2326
|
+
BKAJ1: "#4d4d4d",
|
|
2327
|
+
// Navigation / traffic
|
|
2328
|
+
TRFCD: "#d200d2",
|
|
2329
|
+
TRFCF: "#e680e6",
|
|
2330
|
+
RESBL: "#0064b4",
|
|
2331
|
+
APTS1: "#d200d2",
|
|
2332
|
+
// Danger / hazard
|
|
2333
|
+
DNGHL: "#ff0000",
|
|
2334
|
+
ISHDG: "#cc0000",
|
|
2335
|
+
// Lights
|
|
2336
|
+
LITRD: "#ee0000",
|
|
2337
|
+
LITGN: "#00c800",
|
|
2338
|
+
LITYW: "#fddc00",
|
|
2339
|
+
LITWT: "#ffffff",
|
|
2340
|
+
LITBL: "#0064ff",
|
|
2341
|
+
LITOR: "#ff8c00",
|
|
2342
|
+
// Buoys / marks
|
|
2343
|
+
RADHI: "#68e456",
|
|
2344
|
+
RADLO: "#3f8a34",
|
|
2345
|
+
PLRTE: "#dc4025",
|
|
2346
|
+
ARPAT: "#3fa56f",
|
|
2347
|
+
ISDNG: "#c545c3",
|
|
2348
|
+
// Chart colours
|
|
2349
|
+
CHRED: "#f15469",
|
|
2350
|
+
CHGRN: "#68e456",
|
|
2351
|
+
CHYLW: "#f4da48",
|
|
2352
|
+
CHMGD: "#c545c3",
|
|
2353
|
+
CHMGF: "#d3a6e9",
|
|
2354
|
+
CHWHT: "#d4eaee",
|
|
2355
|
+
CHCOR: "#eb7d36",
|
|
2356
|
+
// Soundings
|
|
2357
|
+
SNDG1: "#7d898c",
|
|
2358
|
+
SNDG2: "#070707",
|
|
2359
|
+
DEPSC: "#525a5c",
|
|
2360
|
+
DEPCN: "#555555",
|
|
2361
|
+
// Utility / infrastructure
|
|
2362
|
+
UINFD: "#070707",
|
|
2363
|
+
UINFF: "#7d898c",
|
|
2364
|
+
UINFR: "#f15469",
|
|
2365
|
+
UINFG: "#68e456",
|
|
2366
|
+
UINFO: "#eb7d36",
|
|
2367
|
+
UINFB: "#3a78f0",
|
|
2368
|
+
UINFM: "#c545c3",
|
|
2369
|
+
UIBCK: "#d4eaee",
|
|
2370
|
+
UIBDR: "#7d898c",
|
|
2371
|
+
UIAFD: "#73b6ef",
|
|
2372
|
+
UIAFF: "#c9b97a",
|
|
2373
|
+
PIPLN: "#55aa00",
|
|
2374
|
+
// Outlines
|
|
2375
|
+
OUTLL: "#c9b97a",
|
|
2376
|
+
// Miscellaneous
|
|
2377
|
+
SCLBR: "#eb7d36",
|
|
2378
|
+
NINFO: "#eb7d36",
|
|
2379
|
+
ADINF: "#b29f34",
|
|
2380
|
+
RESGR: "#7d898c",
|
|
2381
|
+
SHIPS: "#070707",
|
|
2382
|
+
PSTRK: "#070707",
|
|
2383
|
+
SYTRK: "#7d898c",
|
|
2384
|
+
APLRT: "#eb7d36",
|
|
2385
|
+
CURSR: "#eb7d36",
|
|
2386
|
+
BKAJ2: "#232728",
|
|
2387
|
+
RES01: "#a3b4b7",
|
|
2388
|
+
RES02: "#a3b4b7",
|
|
2389
|
+
RES03: "#a3b4b7"
|
|
2289
2390
|
};
|
|
2290
|
-
var
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2391
|
+
var S52_DUSK = {
|
|
2392
|
+
NODTA: "#6b6b6b",
|
|
2393
|
+
DEPIT: "#006650",
|
|
2394
|
+
DEPVS: "#487085",
|
|
2395
|
+
DEPMS: "#608a9d",
|
|
2396
|
+
DEPMD: "#7ba6b8",
|
|
2397
|
+
DEPDW: "#95bfd0",
|
|
2398
|
+
LANDA: "#8a7d5e",
|
|
2399
|
+
LANDF: "#7a6a48",
|
|
2400
|
+
CHBRN: "#6e4e30",
|
|
2401
|
+
CHGRF: "#585858",
|
|
2402
|
+
CHGRD: "#3a3a3a",
|
|
2403
|
+
CHBLK: "#b0b0b0",
|
|
2404
|
+
CSTLN: "#b0b0b0",
|
|
2405
|
+
OUTLW: "#b0b0b0",
|
|
2406
|
+
BKAJ1: "#707070",
|
|
2407
|
+
TRFCD: "#a000a0",
|
|
2408
|
+
TRFCF: "#b060b0",
|
|
2409
|
+
RESBL: "#004a88",
|
|
2410
|
+
APTS1: "#a000a0",
|
|
2411
|
+
DNGHL: "#cc0000",
|
|
2412
|
+
ISHDG: "#990000",
|
|
2413
|
+
LITRD: "#bb0000",
|
|
2414
|
+
LITGN: "#009900",
|
|
2415
|
+
LITYW: "#c8b000",
|
|
2416
|
+
LITWT: "#d0d0d0",
|
|
2417
|
+
LITBL: "#004abb",
|
|
2418
|
+
LITOR: "#cc7000",
|
|
2419
|
+
RADHI: "#009940",
|
|
2420
|
+
RADLO: "#2d6330",
|
|
2421
|
+
PLRTE: "#a03020",
|
|
2422
|
+
ARPAT: "#306040",
|
|
2423
|
+
ISDNG: "#903090",
|
|
2424
|
+
CHRED: "#b05060",
|
|
2425
|
+
CHGRN: "#509040",
|
|
2426
|
+
CHYLW: "#b0a030",
|
|
2427
|
+
CHMGD: "#903090",
|
|
2428
|
+
CHMGF: "#905090",
|
|
2429
|
+
CHWHT: "#90a0a0",
|
|
2430
|
+
CHCOR: "#a06030",
|
|
2431
|
+
SNDG1: "#606060",
|
|
2432
|
+
SNDG2: "#b0b0b0",
|
|
2433
|
+
DEPSC: "#606060",
|
|
2434
|
+
DEPCN: "#2E2E2E",
|
|
2435
|
+
UINFD: "#b0b0b0",
|
|
2436
|
+
UINFF: "#606060",
|
|
2437
|
+
UINFR: "#b05060",
|
|
2438
|
+
UINFG: "#509040",
|
|
2439
|
+
UINFO: "#a06030",
|
|
2440
|
+
UINFB: "#304090",
|
|
2441
|
+
UINFM: "#903090",
|
|
2442
|
+
UIBCK: "#1a1a1a",
|
|
2443
|
+
UIBDR: "#606060",
|
|
2444
|
+
UIAFD: "#405060",
|
|
2445
|
+
UIAFF: "#605840",
|
|
2446
|
+
PIPLN: "#3d7a00",
|
|
2447
|
+
OUTLL: "#605840",
|
|
2448
|
+
SCLBR: "#a06030",
|
|
2449
|
+
NINFO: "#a06030",
|
|
2450
|
+
ADINF: "#807020",
|
|
2451
|
+
RESGR: "#606060",
|
|
2452
|
+
SHIPS: "#b0b0b0",
|
|
2453
|
+
PSTRK: "#b0b0b0",
|
|
2454
|
+
SYTRK: "#606060",
|
|
2455
|
+
APLRT: "#a06030",
|
|
2456
|
+
CURSR: "#cc0000",
|
|
2457
|
+
BKAJ2: "#252525",
|
|
2458
|
+
RES01: "#606060",
|
|
2459
|
+
RES02: "#606060",
|
|
2460
|
+
RES03: "#606060"
|
|
2336
2461
|
};
|
|
2337
|
-
var
|
|
2338
|
-
"
|
|
2339
|
-
"
|
|
2340
|
-
"
|
|
2341
|
-
"
|
|
2342
|
-
"
|
|
2343
|
-
"
|
|
2344
|
-
"
|
|
2345
|
-
"
|
|
2346
|
-
"
|
|
2347
|
-
"
|
|
2348
|
-
"
|
|
2349
|
-
"
|
|
2350
|
-
"
|
|
2351
|
-
"
|
|
2352
|
-
"
|
|
2353
|
-
"
|
|
2354
|
-
"
|
|
2355
|
-
"
|
|
2356
|
-
"
|
|
2357
|
-
"
|
|
2358
|
-
"
|
|
2359
|
-
"
|
|
2360
|
-
"
|
|
2361
|
-
"
|
|
2362
|
-
"
|
|
2363
|
-
"
|
|
2364
|
-
"
|
|
2365
|
-
"
|
|
2366
|
-
"
|
|
2367
|
-
"
|
|
2462
|
+
var S52_NIGHT = {
|
|
2463
|
+
NODTA: "#3a3a32",
|
|
2464
|
+
DEPIT: "#003828",
|
|
2465
|
+
DEPVS: "#356577",
|
|
2466
|
+
DEPMS: "#265066",
|
|
2467
|
+
DEPMD: "#102e44",
|
|
2468
|
+
DEPDW: "#051a28",
|
|
2469
|
+
LANDA: "#3a3020",
|
|
2470
|
+
LANDF: "#302818",
|
|
2471
|
+
CHBRN: "#382410",
|
|
2472
|
+
CHGRF: "#2a2a22",
|
|
2473
|
+
CHGRD: "#1e1e18",
|
|
2474
|
+
CHBLK: "#c8aa78",
|
|
2475
|
+
CSTLN: "#c8aa78",
|
|
2476
|
+
OUTLW: "#c8aa78",
|
|
2477
|
+
BKAJ1: "#504830",
|
|
2478
|
+
TRFCD: "#6e006e",
|
|
2479
|
+
TRFCF: "#803880",
|
|
2480
|
+
RESBL: "#002a50",
|
|
2481
|
+
APTS1: "#6e006e",
|
|
2482
|
+
DNGHL: "#a00000",
|
|
2483
|
+
ISHDG: "#700000",
|
|
2484
|
+
LITRD: "#880000",
|
|
2485
|
+
LITGN: "#006600",
|
|
2486
|
+
LITYW: "#887800",
|
|
2487
|
+
LITWT: "#a09878",
|
|
2488
|
+
LITBL: "#003088",
|
|
2489
|
+
LITOR: "#884800",
|
|
2490
|
+
RADHI: "#005528",
|
|
2491
|
+
RADLO: "#1a3a20",
|
|
2492
|
+
PLRTE: "#6e2018",
|
|
2493
|
+
ARPAT: "#1a3028",
|
|
2494
|
+
ISDNG: "#481848",
|
|
2495
|
+
CHRED: "#682030",
|
|
2496
|
+
CHGRN: "#285020",
|
|
2497
|
+
CHYLW: "#685828",
|
|
2498
|
+
CHMGD: "#481848",
|
|
2499
|
+
CHMGF: "#482848",
|
|
2500
|
+
CHWHT: "#484840",
|
|
2501
|
+
CHCOR: "#683818",
|
|
2502
|
+
SNDG1: "#383028",
|
|
2503
|
+
SNDG2: "#c8aa78",
|
|
2504
|
+
DEPSC: "#383028",
|
|
2505
|
+
// ⚠ DELIBERATE DEVIATION from the published S-52 NIGHT value (#1f2223),
|
|
2506
|
+
// recorded here because `docs/validation/s52-spec-gap-matrix.json` tracks
|
|
2507
|
+
// this token as `value-differs` and the CHRTstyles programme is bringing
|
|
2508
|
+
// colours TO spec — this one moves further away, on purpose.
|
|
2509
|
+
//
|
|
2510
|
+
// Spec NIGHT DEPCN is near-black, which measures ~1.1:1 against this
|
|
2511
|
+
// palette's own deep water (#051a28). S-52's NIGHT table assumes a
|
|
2512
|
+
// dark-adapted eye in a darkened wheelhouse; on a phone or a laptop at
|
|
2513
|
+
// anchor it is not dim, it is absent — reported as "can't see bottom
|
|
2514
|
+
// contours in night mode only". A contour nobody can see fails the
|
|
2515
|
+
// standard's intent while matching its number.
|
|
2516
|
+
//
|
|
2517
|
+
// The palette already makes this trade elsewhere and in the same
|
|
2518
|
+
// direction: NIGHT's CHBLK/CSTLN/OUTLW are #c8aa78, light warm linework on
|
|
2519
|
+
// dark water. This value is that family, dimmed to stay subordinate to the
|
|
2520
|
+
// coastline. Revisit with the CHRTstyles programme, which owns the
|
|
2521
|
+
// spec-vs-legibility call across all 63 tokens.
|
|
2522
|
+
DEPCN: "#A68E84",
|
|
2523
|
+
UINFD: "#c8aa78",
|
|
2524
|
+
UINFF: "#383028",
|
|
2525
|
+
UINFR: "#682030",
|
|
2526
|
+
UINFG: "#285020",
|
|
2527
|
+
UINFO: "#683818",
|
|
2528
|
+
UINFB: "#182848",
|
|
2529
|
+
UINFM: "#481848",
|
|
2530
|
+
UIBCK: "#0a0a08",
|
|
2531
|
+
UIBDR: "#383028",
|
|
2532
|
+
UIAFD: "#182838",
|
|
2533
|
+
UIAFF: "#302818",
|
|
2534
|
+
PIPLN: "#284800",
|
|
2535
|
+
OUTLL: "#302818",
|
|
2536
|
+
SCLBR: "#683818",
|
|
2537
|
+
NINFO: "#683818",
|
|
2538
|
+
ADINF: "#483818",
|
|
2539
|
+
RESGR: "#383028",
|
|
2540
|
+
SHIPS: "#c8aa78",
|
|
2541
|
+
PSTRK: "#c8aa78",
|
|
2542
|
+
SYTRK: "#484030",
|
|
2543
|
+
APLRT: "#683818",
|
|
2544
|
+
CURSR: "#a00000",
|
|
2545
|
+
BKAJ2: "#0a0a08",
|
|
2546
|
+
RES01: "#383028",
|
|
2547
|
+
RES02: "#383028",
|
|
2548
|
+
RES03: "#383028"
|
|
2549
|
+
};
|
|
2550
|
+
var S52_COLORS = S52_DAY;
|
|
2551
|
+
var COLOR_TABLES = {
|
|
2552
|
+
DAY: S52_DAY,
|
|
2553
|
+
DUSK: S52_DUSK,
|
|
2554
|
+
NIGHT: S52_NIGHT
|
|
2555
|
+
};
|
|
2556
|
+
function getColorTable(name) {
|
|
2557
|
+
return COLOR_TABLES[name];
|
|
2558
|
+
}
|
|
2559
|
+
function resolveColor(token, table = "DAY") {
|
|
2560
|
+
const t = COLOR_TABLES[table];
|
|
2561
|
+
return t[token] ?? (table === "NIGHT" ? "#c8aa78" : "#000000");
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// src/interact/light-sigseq.ts
|
|
2565
|
+
var SIGSEQ_MAX_TERMS = 24;
|
|
2566
|
+
var SIGPER_TOLERANCE_SECONDS = 0.06;
|
|
2567
|
+
var LAMP_TICK_MS = 100;
|
|
2568
|
+
var TERM = /^\d+(?:\.\d+)?$/;
|
|
2569
|
+
function text(v) {
|
|
2570
|
+
if (v === null || v === void 0) return null;
|
|
2571
|
+
const s = String(v).trim();
|
|
2572
|
+
return s === "" ? null : s;
|
|
2573
|
+
}
|
|
2574
|
+
function parseSigseqTerms(sigseq, sigper) {
|
|
2575
|
+
const raw = text(sigseq);
|
|
2576
|
+
if (raw === null) return { refusal: "absent" };
|
|
2577
|
+
const terms = raw.split("+");
|
|
2578
|
+
if (terms.length === 0 || terms.length > SIGSEQ_MAX_TERMS) return { refusal: "malformed" };
|
|
2579
|
+
const phases = [];
|
|
2580
|
+
for (const term of terms) {
|
|
2581
|
+
const t = term.trim();
|
|
2582
|
+
if (t === "") return { refusal: "malformed" };
|
|
2583
|
+
const isEclipse = t.startsWith("(") && t.endsWith(")");
|
|
2584
|
+
const body = (isEclipse ? t.slice(1, -1) : t).trim();
|
|
2585
|
+
if (!TERM.test(body)) return { refusal: "malformed" };
|
|
2586
|
+
const seconds = Number(body);
|
|
2587
|
+
if (!(seconds > 0) || !Number.isFinite(seconds)) return { refusal: "nonPositiveDuration" };
|
|
2588
|
+
phases.push({ seconds, isOn: !isEclipse });
|
|
2589
|
+
}
|
|
2590
|
+
const sum = phases.reduce((t, p) => t + p.seconds, 0);
|
|
2591
|
+
if (!(sum > 0)) return { refusal: "nonPositiveDuration" };
|
|
2592
|
+
if (sigper !== null && sigper > 0) {
|
|
2593
|
+
if (Math.abs(sum - sigper) > SIGPER_TOLERANCE_SECONDS) return { refusal: "sigperMismatch" };
|
|
2594
|
+
return { phases, periodSeconds: sigper, checkedAgainstSigper: true };
|
|
2595
|
+
}
|
|
2596
|
+
return { phases, periodSeconds: sum, checkedAgainstSigper: false };
|
|
2597
|
+
}
|
|
2598
|
+
function rhythmProperties(parsed, onColoursHex) {
|
|
2599
|
+
if (parsed === null || parsed.phases.length === 0) return { rp: 0, rn: 0 };
|
|
2600
|
+
const out = { rp: parsed.periodSeconds, rn: parsed.phases.length };
|
|
2601
|
+
let end = 0;
|
|
2602
|
+
let onIndex = 0;
|
|
2603
|
+
parsed.phases.forEach((p, i) => {
|
|
2604
|
+
end += p.seconds;
|
|
2605
|
+
const k = i + 1;
|
|
2606
|
+
out[`r${k}`] = Math.round(end * 1e3) / 1e3;
|
|
2607
|
+
out[`o${k}`] = p.isOn ? 1 : 0;
|
|
2608
|
+
if (p.isOn) {
|
|
2609
|
+
out[`c${k}`] = onColoursHex[onIndex % Math.max(1, onColoursHex.length)] ?? "#ffffff";
|
|
2610
|
+
onIndex += 1;
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
return out;
|
|
2614
|
+
}
|
|
2615
|
+
function phaseExpr(nowSeconds2) {
|
|
2616
|
+
return ["%", nowSeconds2, ["max", ["get", "rp"], 1e-3]];
|
|
2617
|
+
}
|
|
2618
|
+
function lampRhythmOpacity(nowSeconds2, base) {
|
|
2619
|
+
const cases = ["case", ["<=", ["get", "rn"], 0], base];
|
|
2620
|
+
for (let k = 1; k <= SIGSEQ_MAX_TERMS; k++) {
|
|
2621
|
+
cases.push(["<", ["var", "ph"], ["coalesce", ["get", `r${k}`], 1e9]], ["*", base, ["coalesce", ["get", `o${k}`], 1]]);
|
|
2622
|
+
}
|
|
2623
|
+
cases.push(0);
|
|
2624
|
+
return ["let", "ph", phaseExpr(nowSeconds2), cases];
|
|
2625
|
+
}
|
|
2626
|
+
function lampRhythmColour(nowSeconds2, fallback) {
|
|
2627
|
+
const cases = ["case", ["<=", ["get", "rn"], 0], fallback];
|
|
2628
|
+
for (let k = 1; k <= SIGSEQ_MAX_TERMS; k++) {
|
|
2629
|
+
cases.push(["<", ["var", "ph"], ["coalesce", ["get", `r${k}`], 1e9]], ["coalesce", ["get", `c${k}`], fallback]);
|
|
2630
|
+
}
|
|
2631
|
+
cases.push(fallback);
|
|
2632
|
+
return ["let", "ph", phaseExpr(nowSeconds2), cases];
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// src/interact/decode.ts
|
|
2636
|
+
function nameAbbrTable(acronym) {
|
|
2637
|
+
const names = s57NameTable(acronym);
|
|
2638
|
+
const abbrs = s57AbbrTable(acronym);
|
|
2639
|
+
const out = {};
|
|
2640
|
+
for (const [code, name] of Object.entries(names)) {
|
|
2641
|
+
const abbr = abbrs[Number(code)];
|
|
2642
|
+
if (abbr !== void 0) out[Number(code)] = { name, abbr };
|
|
2643
|
+
}
|
|
2644
|
+
return out;
|
|
2645
|
+
}
|
|
2646
|
+
var COLOUR_HEX = {
|
|
2647
|
+
1: "#ffffff",
|
|
2648
|
+
2: "#333333",
|
|
2649
|
+
3: "#ef4444",
|
|
2650
|
+
4: "#22c55e",
|
|
2651
|
+
5: "#3b82f6",
|
|
2652
|
+
6: "#eab308",
|
|
2653
|
+
7: "#9ca3af",
|
|
2654
|
+
8: "#92400e",
|
|
2655
|
+
9: "#f59e0b",
|
|
2656
|
+
10: "#8b5cf6",
|
|
2657
|
+
11: "#f97316",
|
|
2658
|
+
12: "#d946ef",
|
|
2659
|
+
13: "#f472b6"
|
|
2660
|
+
};
|
|
2661
|
+
var LITCHR_TABLE = nameAbbrTable("LITCHR");
|
|
2662
|
+
var COLOUR_TABLE = Object.fromEntries(
|
|
2663
|
+
Object.values(COLOUR_CODES).filter((c) => c.abbr !== void 0 && COLOUR_HEX[c.code] !== void 0).map((c) => [c.code, { name: c.name, abbr: c.abbr, hex: COLOUR_HEX[c.code] }])
|
|
2664
|
+
);
|
|
2665
|
+
var BOYSHP_TABLE = s57NameTable("BOYSHP");
|
|
2666
|
+
var CATLAM_TABLE = s57NameTable("CATLAM");
|
|
2667
|
+
var CATCAM_TABLE = s57NameTable("CATCAM");
|
|
2668
|
+
var CATLMK_TABLE = s57NameTable("CATLMK");
|
|
2669
|
+
var CATFOG_TABLE = s57NameTable("CATFOG");
|
|
2670
|
+
var CATFOG_ABBR_TABLE = s57AbbrTable("CATFOG");
|
|
2671
|
+
var CATFOG_NAUTOPHONE = Object.values(CATFOG_CODES).find(
|
|
2672
|
+
(c) => c.name.toLowerCase() === "nautophone"
|
|
2673
|
+
)?.code;
|
|
2674
|
+
var CATLIT_TABLE = s57NameTable("CATLIT");
|
|
2675
|
+
var TOPSHP_TABLE = s57NameTable("TOPSHP");
|
|
2676
|
+
var STATUS_TABLE = s57NameTable("STATUS");
|
|
2677
|
+
var MARSYS_TABLE = s57NameTable("MARSYS");
|
|
2678
|
+
var LAYER_TYPE_MAP = {
|
|
2679
|
+
BOYLAT: "Lateral Buoy",
|
|
2680
|
+
BOYCAR: "Cardinal Buoy",
|
|
2681
|
+
BOYSPP: "Special Purpose Buoy",
|
|
2682
|
+
BOYISD: "Isolated Danger Buoy",
|
|
2683
|
+
BOYSAW: "Safe Water Buoy",
|
|
2684
|
+
BCNLAT: "Lateral Beacon",
|
|
2685
|
+
BCNCAR: "Cardinal Beacon",
|
|
2686
|
+
BCNSPP: "Special Purpose Beacon",
|
|
2687
|
+
BCNISD: "Isolated Danger Beacon",
|
|
2688
|
+
BCNSAW: "Safe Water Beacon",
|
|
2689
|
+
LIGHTS: "Light",
|
|
2690
|
+
DAYMAR: "Daymark",
|
|
2691
|
+
FOGSIG: "Fog Signal",
|
|
2692
|
+
RTPBCN: "Radar Transponder Beacon",
|
|
2693
|
+
RDOSTA: "Radio/Radar Station",
|
|
2694
|
+
TOPMAR: "Topmark",
|
|
2695
|
+
MORFAC: "Mooring Facility",
|
|
2696
|
+
LNDMRK: "Landmark",
|
|
2697
|
+
SISTAT: "Signal Station",
|
|
2698
|
+
PILBOP: "Pilot Boarding Place",
|
|
2699
|
+
HRBFAC: "Harbour Facility",
|
|
2700
|
+
SMCFAC: "Small Craft Facility",
|
|
2701
|
+
CRANES: "Crane",
|
|
2702
|
+
OFSPLF: "Offshore Platform",
|
|
2703
|
+
ACHBRT: "Anchor Berth",
|
|
2704
|
+
DISMAR: "Distance Mark",
|
|
2705
|
+
LITVES: "Light Vessel",
|
|
2706
|
+
LITFLT: "Light Float"
|
|
2707
|
+
};
|
|
2708
|
+
var NAV_AID_QUERY_LAYERS = [
|
|
2709
|
+
"enc-BOYLAT",
|
|
2710
|
+
"enc-BOYCAR",
|
|
2711
|
+
"enc-BOYSPP",
|
|
2712
|
+
"enc-BOYISD",
|
|
2713
|
+
"enc-BOYSAW",
|
|
2714
|
+
"enc-BCNLAT",
|
|
2715
|
+
"enc-BCNCAR",
|
|
2716
|
+
"enc-BCNSPP",
|
|
2717
|
+
"enc-BCNISD",
|
|
2718
|
+
"enc-BCNSAW",
|
|
2719
|
+
"enc-LIGHTS",
|
|
2720
|
+
"enc-LIGHTS-point",
|
|
2721
|
+
"enc-DAYMAR",
|
|
2722
|
+
"enc-FOGSIG",
|
|
2723
|
+
"enc-RTPBCN",
|
|
2724
|
+
"enc-RDOSTA",
|
|
2725
|
+
"enc-TOPMAR",
|
|
2726
|
+
"enc-MORFAC",
|
|
2727
|
+
"enc-MORFAC-point",
|
|
2728
|
+
"enc-LNDMRK",
|
|
2729
|
+
"enc-LNDMRK-point",
|
|
2730
|
+
"enc-SISTAT",
|
|
2731
|
+
"enc-SISTAT-point",
|
|
2732
|
+
"enc-PILBOP",
|
|
2733
|
+
"enc-PILBOP-point",
|
|
2734
|
+
"enc-HRBFAC",
|
|
2735
|
+
"enc-SMCFAC",
|
|
2736
|
+
"enc-CRANES",
|
|
2737
|
+
"enc-CRANES-point",
|
|
2738
|
+
"enc-OFSPLF",
|
|
2368
2739
|
"enc-OFSPLF-point",
|
|
2369
2740
|
"enc-ACHBRT",
|
|
2370
2741
|
"enc-DISMAR",
|
|
@@ -3167,14 +3538,8 @@ function decodeNavAidProps(layerName, props, coords) {
|
|
|
3167
3538
|
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3168
3539
|
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3169
3540
|
if (isSectorLight) {
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
sectorEnd: sectr2,
|
|
3173
|
-
colour: colourEntry?.name ?? "White",
|
|
3174
|
-
colourHex: colourEntry?.hex ?? "#ffffff",
|
|
3175
|
-
notation,
|
|
3176
|
-
nominalRange
|
|
3177
|
-
});
|
|
3541
|
+
const sec = sectorFromProps(props, sectr1, sectr2, "DAY", false);
|
|
3542
|
+
if (sec) sectors.push({ ...sec, notation });
|
|
3178
3543
|
}
|
|
3179
3544
|
return {
|
|
3180
3545
|
name,
|
|
@@ -3192,6 +3557,7 @@ function decodeNavAidProps(layerName, props, coords) {
|
|
|
3192
3557
|
lightCharacterFull: litchrEntry?.name ?? null,
|
|
3193
3558
|
period: sigper,
|
|
3194
3559
|
group,
|
|
3560
|
+
sigseq: props.SIGSEQ != null && String(props.SIGSEQ).trim() !== "" ? String(props.SIGSEQ).trim() : null,
|
|
3195
3561
|
fogSignal: fogSignal2,
|
|
3196
3562
|
fogSignalAbbr,
|
|
3197
3563
|
radarReflector,
|
|
@@ -3222,6 +3588,18 @@ function buildNotation(litchrAbbr, colourAbbrs, sigper, group) {
|
|
|
3222
3588
|
function buildFlashPattern(decoded) {
|
|
3223
3589
|
const litchr = decoded.lightCharacter;
|
|
3224
3590
|
if (!litchr) return null;
|
|
3591
|
+
const charted = parseSigseqTerms(decoded.sigseq, decoded.period);
|
|
3592
|
+
if (!("refusal" in charted)) {
|
|
3593
|
+
const colours2 = decoded.colourHexList.length > 0 ? decoded.colourHexList : [decoded.lightColourHex];
|
|
3594
|
+
let onIndex = 0;
|
|
3595
|
+
const chartedPhases = charted.phases.map((p) => {
|
|
3596
|
+
if (!p.isOn) return { on: false, duration: p.seconds * 1e3 };
|
|
3597
|
+
const color = colours2[onIndex % colours2.length];
|
|
3598
|
+
onIndex += 1;
|
|
3599
|
+
return { on: true, duration: p.seconds * 1e3, color };
|
|
3600
|
+
});
|
|
3601
|
+
return { phases: chartedPhases, totalCycle: charted.periodSeconds * 1e3, notation: decoded.notation, timing: "charted" };
|
|
3602
|
+
}
|
|
3225
3603
|
const period = decoded.period && decoded.period > 0 ? decoded.period * 1e3 : 6e3;
|
|
3226
3604
|
const groups = decoded.group ?? [1];
|
|
3227
3605
|
const totalFlashes = groups.reduce((a, b) => a + b, 0);
|
|
@@ -3390,7 +3768,7 @@ function buildFlashPattern(decoded) {
|
|
|
3390
3768
|
}
|
|
3391
3769
|
}
|
|
3392
3770
|
const totalCycle = phases.reduce((sum, p) => sum + p.duration, 0);
|
|
3393
|
-
return { phases, totalCycle, notation };
|
|
3771
|
+
return { timing: "invented", phases, totalCycle, notation };
|
|
3394
3772
|
}
|
|
3395
3773
|
function uncoveredSectorArcs(sectors) {
|
|
3396
3774
|
const norm = (d) => (d % 360 + 360) % 360;
|
|
@@ -3429,7 +3807,70 @@ function uncoveredSectorArcs(sectors) {
|
|
|
3429
3807
|
}
|
|
3430
3808
|
return gaps.filter(([a, b]) => Math.abs(b - a) >= 0.05 || b < a).map(([a, b]) => ({ start: norm(a), end: norm(b) }));
|
|
3431
3809
|
}
|
|
3432
|
-
function
|
|
3810
|
+
function parseCodes(val) {
|
|
3811
|
+
if (val == null) return [];
|
|
3812
|
+
const str = String(val).trim();
|
|
3813
|
+
let parts;
|
|
3814
|
+
if (str.startsWith("[")) {
|
|
3815
|
+
try {
|
|
3816
|
+
const arr = JSON.parse(str);
|
|
3817
|
+
parts = Array.isArray(arr) ? arr.map(String) : [];
|
|
3818
|
+
} catch {
|
|
3819
|
+
parts = str.split(/[,;]/);
|
|
3820
|
+
}
|
|
3821
|
+
} else {
|
|
3822
|
+
parts = str.split(/[,;]/);
|
|
3823
|
+
}
|
|
3824
|
+
return parts.map((p) => Number(p.trim())).filter((n) => !isNaN(n));
|
|
3825
|
+
}
|
|
3826
|
+
var OBSCURED_LITVIS = /* @__PURE__ */ new Set([3, 7, 8]);
|
|
3827
|
+
function sectorArcColourToken(colourCodes, obscured) {
|
|
3828
|
+
if (obscured) return "CHBRN";
|
|
3829
|
+
const has = (c) => colourCodes.includes(c);
|
|
3830
|
+
if (colourCodes.length === 1) {
|
|
3831
|
+
if (has(3)) return "LITRD";
|
|
3832
|
+
if (has(4)) return "LITGN";
|
|
3833
|
+
if (has(1) || has(6) || has(11)) return "LITYW";
|
|
3834
|
+
return "CHMGD";
|
|
3835
|
+
}
|
|
3836
|
+
if (colourCodes.length === 2) {
|
|
3837
|
+
if (has(1) && has(3)) return "LITRD";
|
|
3838
|
+
if (has(1) && has(4)) return "LITGN";
|
|
3839
|
+
return "CHMGD";
|
|
3840
|
+
}
|
|
3841
|
+
return "CHMGD";
|
|
3842
|
+
}
|
|
3843
|
+
function sectorFromProps(props, s1, s2, ct, allRound) {
|
|
3844
|
+
const colourCodes = parseCodes(props.COLOUR);
|
|
3845
|
+
const entries = colourCodes.map((c) => COLOUR_TABLE[c]).filter((e) => e != null);
|
|
3846
|
+
if (allRound && entries.length === 0) return null;
|
|
3847
|
+
const obscured = parseCodes(props.LITVIS).some((v) => OBSCURED_LITVIS.has(v));
|
|
3848
|
+
const litchrCode = parseCode(props.LITCHR);
|
|
3849
|
+
const le = litchrCode ? LITCHR_TABLE[litchrCode] : null;
|
|
3850
|
+
const sigper = props.SIGPER != null ? Number(props.SIGPER) : null;
|
|
3851
|
+
const group = parseGroup(props.SIGGRP);
|
|
3852
|
+
const base = buildNotation(le?.abbr ?? null, entries.length > 0 ? [entries.map((e) => e.abbr).join("")] : [], sigper, group);
|
|
3853
|
+
const notation = obscured && base ? `${base} (obsc)` : base;
|
|
3854
|
+
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3855
|
+
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3856
|
+
const arcColourToken = sectorArcColourToken(colourCodes, obscured);
|
|
3857
|
+
return {
|
|
3858
|
+
sectorStart: s1,
|
|
3859
|
+
sectorEnd: s2,
|
|
3860
|
+
colour: entries[0]?.name ?? "White",
|
|
3861
|
+
colourHex: entries[0]?.hex ?? "#ffffff",
|
|
3862
|
+
notation,
|
|
3863
|
+
nominalRange,
|
|
3864
|
+
colourCodes,
|
|
3865
|
+
arcColourToken,
|
|
3866
|
+
arcColourHex: resolveColor(arcColourToken, ct),
|
|
3867
|
+
obscured,
|
|
3868
|
+
sigseq: props.SIGSEQ != null && String(props.SIGSEQ).trim() !== "" ? String(props.SIGSEQ).trim() : null,
|
|
3869
|
+
sigper: sigper != null && !isNaN(sigper) && sigper > 0 ? sigper : null,
|
|
3870
|
+
...allRound ? { allRound: true } : {}
|
|
3871
|
+
};
|
|
3872
|
+
}
|
|
3873
|
+
function buildSectorsFromFeatures(features, ct = "DAY") {
|
|
3433
3874
|
const sectors = [];
|
|
3434
3875
|
const allRound = [];
|
|
3435
3876
|
for (const f of features) {
|
|
@@ -3440,47 +3881,15 @@ function buildSectorsFromFeatures(features) {
|
|
|
3440
3881
|
if (props.SECTR1 == null && props.SECTR2 == null && parseCode(props.COLOUR) != null) allRound.push(props);
|
|
3441
3882
|
continue;
|
|
3442
3883
|
}
|
|
3443
|
-
const
|
|
3444
|
-
|
|
3445
|
-
const litchrCode = parseCode(props.LITCHR);
|
|
3446
|
-
const le = litchrCode ? LITCHR_TABLE[litchrCode] : null;
|
|
3447
|
-
const sigper = props.SIGPER != null ? Number(props.SIGPER) : null;
|
|
3448
|
-
const group = parseGroup(props.SIGGRP);
|
|
3449
|
-
const notation = buildNotation(le?.abbr ?? null, ce ? [ce.abbr] : [], sigper, group);
|
|
3450
|
-
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3451
|
-
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3452
|
-
sectors.push({
|
|
3453
|
-
sectorStart: s1,
|
|
3454
|
-
sectorEnd: s2,
|
|
3455
|
-
colour: ce?.name ?? "White",
|
|
3456
|
-
colourHex: ce?.hex ?? "#ffffff",
|
|
3457
|
-
notation,
|
|
3458
|
-
nominalRange
|
|
3459
|
-
});
|
|
3884
|
+
const sec = sectorFromProps(props, s1, s2, ct, false);
|
|
3885
|
+
if (sec) sectors.push(sec);
|
|
3460
3886
|
}
|
|
3461
3887
|
if (sectors.length > 0 && allRound.length > 0) {
|
|
3462
3888
|
const gaps = uncoveredSectorArcs(sectors);
|
|
3463
3889
|
for (const props of allRound) {
|
|
3464
|
-
const colourCode = parseCode(props.COLOUR);
|
|
3465
|
-
const ce = colourCode ? COLOUR_TABLE[colourCode] : null;
|
|
3466
|
-
if (!ce) continue;
|
|
3467
|
-
const litchrCode = parseCode(props.LITCHR);
|
|
3468
|
-
const le = litchrCode ? LITCHR_TABLE[litchrCode] : null;
|
|
3469
|
-
const sigper = props.SIGPER != null ? Number(props.SIGPER) : null;
|
|
3470
|
-
const group = parseGroup(props.SIGGRP);
|
|
3471
|
-
const notation = buildNotation(le?.abbr ?? null, [ce.abbr], sigper, group);
|
|
3472
|
-
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3473
|
-
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3474
3890
|
for (const gap of gaps) {
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
sectorEnd: gap.end,
|
|
3478
|
-
colour: ce.name,
|
|
3479
|
-
colourHex: ce.hex,
|
|
3480
|
-
notation,
|
|
3481
|
-
nominalRange,
|
|
3482
|
-
allRound: true
|
|
3483
|
-
});
|
|
3891
|
+
const sec = sectorFromProps(props, gap.start, gap.end, ct, true);
|
|
3892
|
+
if (sec) sectors.push(sec);
|
|
3484
3893
|
}
|
|
3485
3894
|
}
|
|
3486
3895
|
}
|
|
@@ -3530,12 +3939,16 @@ function buildSectorArcGeoJSON(center, startBearing, endBearing, radiusNm = 0.5,
|
|
|
3530
3939
|
}
|
|
3531
3940
|
function buildSectorArcsFC(center, sectors, radiusNm = 0.4, innerRadiusFrac = 0.92) {
|
|
3532
3941
|
const innerNm = innerRadiusFrac > 0 ? radiusNm * innerRadiusFrac : 0;
|
|
3533
|
-
const
|
|
3942
|
+
const drawn = sectors.filter((sec) => !sec.allRound);
|
|
3943
|
+
const features = drawn.map((sec, i) => {
|
|
3534
3944
|
const arc = buildSectorArcGeoJSON(center, sec.sectorStart, sec.sectorEnd, radiusNm, innerNm);
|
|
3535
3945
|
arc.properties = {
|
|
3536
3946
|
sectorIndex: i,
|
|
3537
3947
|
colour: sec.colour,
|
|
3538
3948
|
colourHex: sec.colourHex,
|
|
3949
|
+
arcColourHex: sec.arcColourHex,
|
|
3950
|
+
arcColourToken: sec.arcColourToken,
|
|
3951
|
+
obscured: sec.obscured,
|
|
3539
3952
|
notation: sec.notation,
|
|
3540
3953
|
sectorStart: sec.sectorStart,
|
|
3541
3954
|
sectorEnd: sec.sectorEnd,
|
|
@@ -3548,7 +3961,7 @@ function buildSectorArcsFC(center, sectors, radiusNm = 0.4, innerRadiusFrac = 0.
|
|
|
3548
3961
|
const seenBearings = /* @__PURE__ */ new Set();
|
|
3549
3962
|
const TICK_OUTER_OVERHANG = 1.05;
|
|
3550
3963
|
const TICK_INNER_OVERHANG = 0.93;
|
|
3551
|
-
for (const sec of
|
|
3964
|
+
for (const sec of drawn) {
|
|
3552
3965
|
for (const bearing of [sec.sectorStart, sec.sectorEnd]) {
|
|
3553
3966
|
const rad = sectorLimitToArcBearing(bearing) * Math.PI / 180;
|
|
3554
3967
|
const radiusDeg = radiusNm / 60;
|
|
@@ -4418,10 +4831,10 @@ function ensureStyles(doc) {
|
|
|
4418
4831
|
doc.head.appendChild(style);
|
|
4419
4832
|
styleInjected = true;
|
|
4420
4833
|
}
|
|
4421
|
-
function el(doc, tag, className,
|
|
4834
|
+
function el(doc, tag, className, text2) {
|
|
4422
4835
|
const node = doc.createElement(tag);
|
|
4423
4836
|
node.className = className;
|
|
4424
|
-
if (
|
|
4837
|
+
if (text2 != null) node.textContent = text2;
|
|
4425
4838
|
return node;
|
|
4426
4839
|
}
|
|
4427
4840
|
function animateDot(dot, pattern) {
|
|
@@ -4581,466 +4994,209 @@ ${notice.dataCurrency}`;
|
|
|
4581
4994
|
if (position === "static" || position === "") {
|
|
4582
4995
|
container.style.position = "relative";
|
|
4583
4996
|
}
|
|
4584
|
-
container.appendChild(root);
|
|
4585
|
-
this.root = root;
|
|
4586
|
-
this.lngLat = lngLat;
|
|
4587
|
-
this.place();
|
|
4588
|
-
this.map.on("move", this.reposition);
|
|
4589
|
-
}
|
|
4590
|
-
place() {
|
|
4591
|
-
if (!this.root || !this.lngLat) return;
|
|
4592
|
-
const { x, y } = this.map.project(this.lngLat);
|
|
4593
|
-
this.root.style.left = `${x}px`;
|
|
4594
|
-
this.root.style.top = `${y}px`;
|
|
4595
|
-
}
|
|
4596
|
-
close() {
|
|
4597
|
-
if (this.stopAnimation) {
|
|
4598
|
-
this.stopAnimation();
|
|
4599
|
-
this.stopAnimation = null;
|
|
4600
|
-
}
|
|
4601
|
-
if (this.root) {
|
|
4602
|
-
this.map.off("move", this.reposition);
|
|
4603
|
-
this.root.remove();
|
|
4604
|
-
this.root = null;
|
|
4605
|
-
}
|
|
4606
|
-
this.lngLat = null;
|
|
4607
|
-
}
|
|
4608
|
-
};
|
|
4609
|
-
|
|
4610
|
-
// src/interact/index.ts
|
|
4611
|
-
var InteractClient = class {
|
|
4612
|
-
constructor(http) {
|
|
4613
|
-
this.http = http;
|
|
4614
|
-
this.entitlementClient = new EntitlementClient(http);
|
|
4615
|
-
}
|
|
4616
|
-
entitlementClient;
|
|
4617
|
-
/**
|
|
4618
|
-
* Check the CHRTinteract entitlement for this API key (server-side, plan
|
|
4619
|
-
* derived, cached per client). Throws `InteractError` with code
|
|
4620
|
-
* `interact_not_enabled` for Free-plan teams.
|
|
4621
|
-
*/
|
|
4622
|
-
async entitlement() {
|
|
4623
|
-
return this.entitlementClient.check();
|
|
4624
|
-
}
|
|
4625
|
-
/**
|
|
4626
|
-
* Decode a chart feature (from `queryRenderedFeatures`, the features API,
|
|
4627
|
-
* or plain `{ layer, properties }`) into a typed {@link InspectResult}.
|
|
4628
|
-
*
|
|
4629
|
-
* The first call performs the server-side entitlement check. With
|
|
4630
|
-
* `{ enrich: true }` the result is upgraded with full S-57 properties and
|
|
4631
|
-
* source-cell provenance (edition / update / issue date) via the features
|
|
4632
|
-
* API — metered against the key's `featureCalls` quota.
|
|
4633
|
-
*
|
|
4634
|
-
* @example
|
|
4635
|
-
* const result = await chrt.interact.inspect(feature);
|
|
4636
|
-
* if (result.kind === "navaid") console.log(result.characteristic); // "Fl R 4s"
|
|
4637
|
-
*/
|
|
4638
|
-
async inspect(feature, opts) {
|
|
4639
|
-
await this.entitlementClient.check();
|
|
4640
|
-
const result = buildInspectResult(feature, opts);
|
|
4641
|
-
if (opts?.enrich) {
|
|
4642
|
-
return enrichResult(this.http, result, opts.signal);
|
|
4643
|
-
}
|
|
4644
|
-
return result;
|
|
4645
|
-
}
|
|
4646
|
-
/**
|
|
4647
|
-
* Collect EVERY inspectable feature under a point on a CHRT map —
|
|
4648
|
-
* deduplicated across tile boundaries by S-57 identity (`LNAM`) and
|
|
4649
|
-
* ordered by class priority (point targets > lines > areas, safety
|
|
4650
|
-
* classes first within each band). Area features never preempt the buoy
|
|
4651
|
-
* on top of them but always stay selectable: a click on open water
|
|
4652
|
-
* resolves to its DEPARE.
|
|
4653
|
-
*
|
|
4654
|
-
* `point` is a screen pixel point (`e.point` from a map click) or
|
|
4655
|
-
* `[lng, lat]`. Point/line targets are picked up within
|
|
4656
|
-
* `opts.tolerance` px (default 8); polygon candidates require the exact
|
|
4657
|
-
* point to fall inside them.
|
|
4658
|
-
*
|
|
4659
|
-
* The first call performs the server-side entitlement check.
|
|
4660
|
-
*
|
|
4661
|
-
* @example
|
|
4662
|
-
* const { candidates, select } = await chrt.interact.inspectAll(map, e.point);
|
|
4663
|
-
* const result = await select(candidates[0]); // full decode (+ merge)
|
|
4664
|
-
*/
|
|
4665
|
-
async inspectAll(map, point, opts = {}) {
|
|
4666
|
-
if (!map || typeof map.queryRenderedFeatures !== "function") {
|
|
4667
|
-
throw new InteractError(
|
|
4668
|
-
"unexpected_response",
|
|
4669
|
-
"inspectAll(): expected a MapLibre / Mapbox GL map instance"
|
|
4670
|
-
);
|
|
4671
|
-
}
|
|
4672
|
-
await this.entitlementClient.check();
|
|
4673
|
-
const screen = Array.isArray(point) ? map.project([point[0], point[1]]) : point;
|
|
4674
|
-
const lngLat = Array.isArray(point) ? { lng: point[0], lat: point[1] } : map.unproject?.([point.x, point.y]) ?? null;
|
|
4675
|
-
const at = { point: screen, lngLat };
|
|
4676
|
-
const candidates = collectClickCandidates(map, screen, opts);
|
|
4677
|
-
const http = this.http;
|
|
4678
|
-
return {
|
|
4679
|
-
candidates,
|
|
4680
|
-
select: async (candidate, selectOpts) => {
|
|
4681
|
-
const result = decodeCandidate(map, candidate, at, opts);
|
|
4682
|
-
if (selectOpts?.enrich) {
|
|
4683
|
-
return enrichResult(http, result, selectOpts.signal);
|
|
4684
|
-
}
|
|
4685
|
-
return result;
|
|
4686
|
-
}
|
|
4687
|
-
};
|
|
4688
|
-
}
|
|
4689
|
-
/**
|
|
4690
|
-
* Attach click-to-inspect to a MapLibre / Mapbox GL map rendering CHRT
|
|
4691
|
-
* tiles. Resolves once the server-side entitlement check passes; rejects
|
|
4692
|
-
* with `InteractError` (`interact_not_enabled` on Free plans) otherwise —
|
|
4693
|
-
* no listeners are wired on denial.
|
|
4694
|
-
*
|
|
4695
|
-
* Headless-first: pass `onInspect` to receive every decoded
|
|
4696
|
-
* {@link InspectResult} and render your own UI. Without callbacks, the
|
|
4697
|
-
* built-in minimal popup is shown (opt back in explicitly with
|
|
4698
|
-
* `popup: true` to have both).
|
|
4699
|
-
*
|
|
4700
|
-
* A click that hits SEVERAL features (deduplicated by S-57 identity)
|
|
4701
|
-
* disambiguates instead of silently taking the topmost:
|
|
4702
|
-
* - `onCandidates` provided → it receives the priority-ordered list;
|
|
4703
|
-
* resolve a choice with `ctx.select(candidate)`.
|
|
4704
|
-
* - built-in popup → shows a candidate list; picking a row opens the
|
|
4705
|
-
* result card.
|
|
4706
|
-
* - `onInspect` only → the top candidate is inspected immediately, and
|
|
4707
|
-
* clicking the SAME spot again cycles to the next candidate — so an
|
|
4708
|
-
* area feature under a buoy is reachable with a second click.
|
|
4709
|
-
*
|
|
4710
|
-
* @example
|
|
4711
|
-
* const handle = await chrt.interact.attach(map, {
|
|
4712
|
-
* onInspect: (result) => console.log(result.title, result.characteristic),
|
|
4713
|
-
* });
|
|
4714
|
-
* // later: handle.detach();
|
|
4715
|
-
*/
|
|
4716
|
-
async attach(map, opts = {}) {
|
|
4717
|
-
if (!map || typeof map.queryRenderedFeatures !== "function" || typeof map.on !== "function") {
|
|
4718
|
-
throw new InteractError(
|
|
4719
|
-
"unexpected_response",
|
|
4720
|
-
"attach(): expected a MapLibre / Mapbox GL map instance"
|
|
4721
|
-
);
|
|
4722
|
-
}
|
|
4723
|
-
await this.entitlementClient.check();
|
|
4724
|
-
const showPopup = opts.popup ?? (!opts.onInspect && !opts.onCandidates);
|
|
4725
|
-
const popup = showPopup ? new InteractPopup(map) : null;
|
|
4726
|
-
const http = this.http;
|
|
4727
|
-
let cycle = null;
|
|
4728
|
-
const samePoint = (a, b) => {
|
|
4729
|
-
const pad = opts.tolerance ?? DEFAULT_CLICK_TOLERANCE;
|
|
4730
|
-
return Math.abs(a.x - b.x) <= pad && Math.abs(a.y - b.y) <= pad;
|
|
4731
|
-
};
|
|
4732
|
-
const select = (candidate, e) => {
|
|
4733
|
-
const result = decodeCandidate(map, candidate, e, opts);
|
|
4734
|
-
const finish = (r) => {
|
|
4735
|
-
popup?.show(r, r.coordinates ?? [e.lngLat.lng, e.lngLat.lat]);
|
|
4736
|
-
opts.onInspect?.(r, { lngLat: e.lngLat, point: e.point, feature: candidate.feature });
|
|
4737
|
-
return r;
|
|
4738
|
-
};
|
|
4739
|
-
if (opts.enrich) {
|
|
4740
|
-
return enrichResult(http, result).then(finish).catch(() => finish(result));
|
|
4741
|
-
}
|
|
4742
|
-
return Promise.resolve(finish(result));
|
|
4743
|
-
};
|
|
4744
|
-
const clickHandler = (e) => {
|
|
4745
|
-
const candidates = collectClickCandidates(map, e.point, opts);
|
|
4746
|
-
if (candidates.length === 0) {
|
|
4747
|
-
cycle = null;
|
|
4748
|
-
popup?.close();
|
|
4749
|
-
opts.onMiss?.({ lngLat: e.lngLat });
|
|
4750
|
-
return;
|
|
4751
|
-
}
|
|
4752
|
-
if (candidates.length === 1) {
|
|
4753
|
-
cycle = null;
|
|
4754
|
-
void select(candidates[0], e);
|
|
4755
|
-
return;
|
|
4756
|
-
}
|
|
4757
|
-
if (opts.onCandidates || popup) {
|
|
4758
|
-
cycle = null;
|
|
4759
|
-
if (popup) {
|
|
4760
|
-
popup.showList(candidates, [e.lngLat.lng, e.lngLat.lat], (c) => void select(c, e));
|
|
4761
|
-
}
|
|
4762
|
-
opts.onCandidates?.(candidates, {
|
|
4763
|
-
lngLat: e.lngLat,
|
|
4764
|
-
point: e.point,
|
|
4765
|
-
select: (c) => select(c, e)
|
|
4766
|
-
});
|
|
4767
|
-
return;
|
|
4768
|
-
}
|
|
4769
|
-
const key = candidates.map((c) => c.identity ?? c.layerId).join("/");
|
|
4770
|
-
if (cycle && cycle.key === key && samePoint(cycle.point, e.point)) {
|
|
4771
|
-
cycle.index = (cycle.index + 1) % candidates.length;
|
|
4772
|
-
cycle.point = e.point;
|
|
4773
|
-
} else {
|
|
4774
|
-
cycle = { key, point: e.point, index: 0 };
|
|
4775
|
-
}
|
|
4776
|
-
void select(candidates[cycle.index], e);
|
|
4777
|
-
};
|
|
4778
|
-
map.on("click", clickHandler);
|
|
4779
|
-
return {
|
|
4780
|
-
detach: () => {
|
|
4781
|
-
map.off("click", clickHandler);
|
|
4782
|
-
popup?.close();
|
|
4783
|
-
}
|
|
4784
|
-
};
|
|
4785
|
-
}
|
|
4786
|
-
};
|
|
4787
|
-
|
|
4788
|
-
// src/atlas/s52-colors.ts
|
|
4789
|
-
var S52_DAY = {
|
|
4790
|
-
// Depth
|
|
4791
|
-
NODTA: "#a4a4a4",
|
|
4792
|
-
DEPIT: "#009678",
|
|
4793
|
-
DEPVS: "#7fbfdf",
|
|
4794
|
-
DEPMS: "#a7d4e8",
|
|
4795
|
-
DEPMD: "#cae7f3",
|
|
4796
|
-
DEPDW: "#ecf7fc",
|
|
4797
|
-
// Land / topography
|
|
4798
|
-
LANDA: "#e6d1a1",
|
|
4799
|
-
LANDF: "#c8b078",
|
|
4800
|
-
CHBRN: "#ab7948",
|
|
4801
|
-
CHGRF: "#878787",
|
|
4802
|
-
CHGRD: "#555555",
|
|
4803
|
-
// Chart symbology
|
|
4804
|
-
CHBLK: "#000000",
|
|
4805
|
-
CSTLN: "#000000",
|
|
4806
|
-
OUTLW: "#000000",
|
|
4807
|
-
BKAJ1: "#4d4d4d",
|
|
4808
|
-
// Navigation / traffic
|
|
4809
|
-
TRFCD: "#d200d2",
|
|
4810
|
-
TRFCF: "#e680e6",
|
|
4811
|
-
RESBL: "#0064b4",
|
|
4812
|
-
APTS1: "#d200d2",
|
|
4813
|
-
// Danger / hazard
|
|
4814
|
-
DNGHL: "#ff0000",
|
|
4815
|
-
ISHDG: "#cc0000",
|
|
4816
|
-
// Lights
|
|
4817
|
-
LITRD: "#ee0000",
|
|
4818
|
-
LITGN: "#00c800",
|
|
4819
|
-
LITYW: "#fddc00",
|
|
4820
|
-
LITWT: "#ffffff",
|
|
4821
|
-
LITBL: "#0064ff",
|
|
4822
|
-
LITOR: "#ff8c00",
|
|
4823
|
-
// Buoys / marks
|
|
4824
|
-
RADHI: "#68e456",
|
|
4825
|
-
RADLO: "#3f8a34",
|
|
4826
|
-
PLRTE: "#dc4025",
|
|
4827
|
-
ARPAT: "#3fa56f",
|
|
4828
|
-
ISDNG: "#c545c3",
|
|
4829
|
-
// Chart colours
|
|
4830
|
-
CHRED: "#f15469",
|
|
4831
|
-
CHGRN: "#68e456",
|
|
4832
|
-
CHYLW: "#f4da48",
|
|
4833
|
-
CHMGD: "#c545c3",
|
|
4834
|
-
CHMGF: "#d3a6e9",
|
|
4835
|
-
CHWHT: "#d4eaee",
|
|
4836
|
-
CHCOR: "#eb7d36",
|
|
4837
|
-
// Soundings
|
|
4838
|
-
SNDG1: "#7d898c",
|
|
4839
|
-
SNDG2: "#070707",
|
|
4840
|
-
DEPSC: "#525a5c",
|
|
4841
|
-
DEPCN: "#555555",
|
|
4842
|
-
// Utility / infrastructure
|
|
4843
|
-
UINFD: "#070707",
|
|
4844
|
-
UINFF: "#7d898c",
|
|
4845
|
-
UINFR: "#f15469",
|
|
4846
|
-
UINFG: "#68e456",
|
|
4847
|
-
UINFO: "#eb7d36",
|
|
4848
|
-
UINFB: "#3a78f0",
|
|
4849
|
-
UINFM: "#c545c3",
|
|
4850
|
-
UIBCK: "#d4eaee",
|
|
4851
|
-
UIBDR: "#7d898c",
|
|
4852
|
-
UIAFD: "#73b6ef",
|
|
4853
|
-
UIAFF: "#c9b97a",
|
|
4854
|
-
PIPLN: "#55aa00",
|
|
4855
|
-
// Outlines
|
|
4856
|
-
OUTLL: "#c9b97a",
|
|
4857
|
-
// Miscellaneous
|
|
4858
|
-
SCLBR: "#eb7d36",
|
|
4859
|
-
NINFO: "#eb7d36",
|
|
4860
|
-
ADINF: "#b29f34",
|
|
4861
|
-
RESGR: "#7d898c",
|
|
4862
|
-
SHIPS: "#070707",
|
|
4863
|
-
PSTRK: "#070707",
|
|
4864
|
-
SYTRK: "#7d898c",
|
|
4865
|
-
APLRT: "#eb7d36",
|
|
4866
|
-
CURSR: "#eb7d36",
|
|
4867
|
-
BKAJ2: "#232728",
|
|
4868
|
-
RES01: "#a3b4b7",
|
|
4869
|
-
RES02: "#a3b4b7",
|
|
4870
|
-
RES03: "#a3b4b7"
|
|
4871
|
-
};
|
|
4872
|
-
var S52_DUSK = {
|
|
4873
|
-
NODTA: "#6b6b6b",
|
|
4874
|
-
DEPIT: "#006650",
|
|
4875
|
-
DEPVS: "#487085",
|
|
4876
|
-
DEPMS: "#608a9d",
|
|
4877
|
-
DEPMD: "#7ba6b8",
|
|
4878
|
-
DEPDW: "#95bfd0",
|
|
4879
|
-
LANDA: "#8a7d5e",
|
|
4880
|
-
LANDF: "#7a6a48",
|
|
4881
|
-
CHBRN: "#6e4e30",
|
|
4882
|
-
CHGRF: "#585858",
|
|
4883
|
-
CHGRD: "#3a3a3a",
|
|
4884
|
-
CHBLK: "#b0b0b0",
|
|
4885
|
-
CSTLN: "#b0b0b0",
|
|
4886
|
-
OUTLW: "#b0b0b0",
|
|
4887
|
-
BKAJ1: "#707070",
|
|
4888
|
-
TRFCD: "#a000a0",
|
|
4889
|
-
TRFCF: "#b060b0",
|
|
4890
|
-
RESBL: "#004a88",
|
|
4891
|
-
APTS1: "#a000a0",
|
|
4892
|
-
DNGHL: "#cc0000",
|
|
4893
|
-
ISHDG: "#990000",
|
|
4894
|
-
LITRD: "#bb0000",
|
|
4895
|
-
LITGN: "#009900",
|
|
4896
|
-
LITYW: "#c8b000",
|
|
4897
|
-
LITWT: "#d0d0d0",
|
|
4898
|
-
LITBL: "#004abb",
|
|
4899
|
-
LITOR: "#cc7000",
|
|
4900
|
-
RADHI: "#009940",
|
|
4901
|
-
RADLO: "#2d6330",
|
|
4902
|
-
PLRTE: "#a03020",
|
|
4903
|
-
ARPAT: "#306040",
|
|
4904
|
-
ISDNG: "#903090",
|
|
4905
|
-
CHRED: "#b05060",
|
|
4906
|
-
CHGRN: "#509040",
|
|
4907
|
-
CHYLW: "#b0a030",
|
|
4908
|
-
CHMGD: "#903090",
|
|
4909
|
-
CHMGF: "#905090",
|
|
4910
|
-
CHWHT: "#90a0a0",
|
|
4911
|
-
CHCOR: "#a06030",
|
|
4912
|
-
SNDG1: "#606060",
|
|
4913
|
-
SNDG2: "#b0b0b0",
|
|
4914
|
-
DEPSC: "#606060",
|
|
4915
|
-
DEPCN: "#2E2E2E",
|
|
4916
|
-
UINFD: "#b0b0b0",
|
|
4917
|
-
UINFF: "#606060",
|
|
4918
|
-
UINFR: "#b05060",
|
|
4919
|
-
UINFG: "#509040",
|
|
4920
|
-
UINFO: "#a06030",
|
|
4921
|
-
UINFB: "#304090",
|
|
4922
|
-
UINFM: "#903090",
|
|
4923
|
-
UIBCK: "#1a1a1a",
|
|
4924
|
-
UIBDR: "#606060",
|
|
4925
|
-
UIAFD: "#405060",
|
|
4926
|
-
UIAFF: "#605840",
|
|
4927
|
-
PIPLN: "#3d7a00",
|
|
4928
|
-
OUTLL: "#605840",
|
|
4929
|
-
SCLBR: "#a06030",
|
|
4930
|
-
NINFO: "#a06030",
|
|
4931
|
-
ADINF: "#807020",
|
|
4932
|
-
RESGR: "#606060",
|
|
4933
|
-
SHIPS: "#b0b0b0",
|
|
4934
|
-
PSTRK: "#b0b0b0",
|
|
4935
|
-
SYTRK: "#606060",
|
|
4936
|
-
APLRT: "#a06030",
|
|
4937
|
-
CURSR: "#cc0000",
|
|
4938
|
-
BKAJ2: "#252525",
|
|
4939
|
-
RES01: "#606060",
|
|
4940
|
-
RES02: "#606060",
|
|
4941
|
-
RES03: "#606060"
|
|
4942
|
-
};
|
|
4943
|
-
var S52_NIGHT = {
|
|
4944
|
-
NODTA: "#3a3a32",
|
|
4945
|
-
DEPIT: "#003828",
|
|
4946
|
-
DEPVS: "#356577",
|
|
4947
|
-
DEPMS: "#265066",
|
|
4948
|
-
DEPMD: "#102e44",
|
|
4949
|
-
DEPDW: "#051a28",
|
|
4950
|
-
LANDA: "#3a3020",
|
|
4951
|
-
LANDF: "#302818",
|
|
4952
|
-
CHBRN: "#382410",
|
|
4953
|
-
CHGRF: "#2a2a22",
|
|
4954
|
-
CHGRD: "#1e1e18",
|
|
4955
|
-
CHBLK: "#c8aa78",
|
|
4956
|
-
CSTLN: "#c8aa78",
|
|
4957
|
-
OUTLW: "#c8aa78",
|
|
4958
|
-
BKAJ1: "#504830",
|
|
4959
|
-
TRFCD: "#6e006e",
|
|
4960
|
-
TRFCF: "#803880",
|
|
4961
|
-
RESBL: "#002a50",
|
|
4962
|
-
APTS1: "#6e006e",
|
|
4963
|
-
DNGHL: "#a00000",
|
|
4964
|
-
ISHDG: "#700000",
|
|
4965
|
-
LITRD: "#880000",
|
|
4966
|
-
LITGN: "#006600",
|
|
4967
|
-
LITYW: "#887800",
|
|
4968
|
-
LITWT: "#a09878",
|
|
4969
|
-
LITBL: "#003088",
|
|
4970
|
-
LITOR: "#884800",
|
|
4971
|
-
RADHI: "#005528",
|
|
4972
|
-
RADLO: "#1a3a20",
|
|
4973
|
-
PLRTE: "#6e2018",
|
|
4974
|
-
ARPAT: "#1a3028",
|
|
4975
|
-
ISDNG: "#481848",
|
|
4976
|
-
CHRED: "#682030",
|
|
4977
|
-
CHGRN: "#285020",
|
|
4978
|
-
CHYLW: "#685828",
|
|
4979
|
-
CHMGD: "#481848",
|
|
4980
|
-
CHMGF: "#482848",
|
|
4981
|
-
CHWHT: "#484840",
|
|
4982
|
-
CHCOR: "#683818",
|
|
4983
|
-
SNDG1: "#383028",
|
|
4984
|
-
SNDG2: "#c8aa78",
|
|
4985
|
-
DEPSC: "#383028",
|
|
4986
|
-
// ⚠ DELIBERATE DEVIATION from the published S-52 NIGHT value (#1f2223),
|
|
4987
|
-
// recorded here because `docs/validation/s52-spec-gap-matrix.json` tracks
|
|
4988
|
-
// this token as `value-differs` and the CHRTstyles programme is bringing
|
|
4989
|
-
// colours TO spec — this one moves further away, on purpose.
|
|
4990
|
-
//
|
|
4991
|
-
// Spec NIGHT DEPCN is near-black, which measures ~1.1:1 against this
|
|
4992
|
-
// palette's own deep water (#051a28). S-52's NIGHT table assumes a
|
|
4993
|
-
// dark-adapted eye in a darkened wheelhouse; on a phone or a laptop at
|
|
4994
|
-
// anchor it is not dim, it is absent — reported as "can't see bottom
|
|
4995
|
-
// contours in night mode only". A contour nobody can see fails the
|
|
4996
|
-
// standard's intent while matching its number.
|
|
4997
|
-
//
|
|
4998
|
-
// The palette already makes this trade elsewhere and in the same
|
|
4999
|
-
// direction: NIGHT's CHBLK/CSTLN/OUTLW are #c8aa78, light warm linework on
|
|
5000
|
-
// dark water. This value is that family, dimmed to stay subordinate to the
|
|
5001
|
-
// coastline. Revisit with the CHRTstyles programme, which owns the
|
|
5002
|
-
// spec-vs-legibility call across all 63 tokens.
|
|
5003
|
-
DEPCN: "#A68E84",
|
|
5004
|
-
UINFD: "#c8aa78",
|
|
5005
|
-
UINFF: "#383028",
|
|
5006
|
-
UINFR: "#682030",
|
|
5007
|
-
UINFG: "#285020",
|
|
5008
|
-
UINFO: "#683818",
|
|
5009
|
-
UINFB: "#182848",
|
|
5010
|
-
UINFM: "#481848",
|
|
5011
|
-
UIBCK: "#0a0a08",
|
|
5012
|
-
UIBDR: "#383028",
|
|
5013
|
-
UIAFD: "#182838",
|
|
5014
|
-
UIAFF: "#302818",
|
|
5015
|
-
PIPLN: "#284800",
|
|
5016
|
-
OUTLL: "#302818",
|
|
5017
|
-
SCLBR: "#683818",
|
|
5018
|
-
NINFO: "#683818",
|
|
5019
|
-
ADINF: "#483818",
|
|
5020
|
-
RESGR: "#383028",
|
|
5021
|
-
SHIPS: "#c8aa78",
|
|
5022
|
-
PSTRK: "#c8aa78",
|
|
5023
|
-
SYTRK: "#484030",
|
|
5024
|
-
APLRT: "#683818",
|
|
5025
|
-
CURSR: "#a00000",
|
|
5026
|
-
BKAJ2: "#0a0a08",
|
|
5027
|
-
RES01: "#383028",
|
|
5028
|
-
RES02: "#383028",
|
|
5029
|
-
RES03: "#383028"
|
|
4997
|
+
container.appendChild(root);
|
|
4998
|
+
this.root = root;
|
|
4999
|
+
this.lngLat = lngLat;
|
|
5000
|
+
this.place();
|
|
5001
|
+
this.map.on("move", this.reposition);
|
|
5002
|
+
}
|
|
5003
|
+
place() {
|
|
5004
|
+
if (!this.root || !this.lngLat) return;
|
|
5005
|
+
const { x, y } = this.map.project(this.lngLat);
|
|
5006
|
+
this.root.style.left = `${x}px`;
|
|
5007
|
+
this.root.style.top = `${y}px`;
|
|
5008
|
+
}
|
|
5009
|
+
close() {
|
|
5010
|
+
if (this.stopAnimation) {
|
|
5011
|
+
this.stopAnimation();
|
|
5012
|
+
this.stopAnimation = null;
|
|
5013
|
+
}
|
|
5014
|
+
if (this.root) {
|
|
5015
|
+
this.map.off("move", this.reposition);
|
|
5016
|
+
this.root.remove();
|
|
5017
|
+
this.root = null;
|
|
5018
|
+
}
|
|
5019
|
+
this.lngLat = null;
|
|
5020
|
+
}
|
|
5030
5021
|
};
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5022
|
+
|
|
5023
|
+
// src/interact/index.ts
|
|
5024
|
+
var InteractClient = class {
|
|
5025
|
+
constructor(http) {
|
|
5026
|
+
this.http = http;
|
|
5027
|
+
this.entitlementClient = new EntitlementClient(http);
|
|
5028
|
+
}
|
|
5029
|
+
entitlementClient;
|
|
5030
|
+
/**
|
|
5031
|
+
* Check the CHRTinteract entitlement for this API key (server-side, plan
|
|
5032
|
+
* derived, cached per client). Throws `InteractError` with code
|
|
5033
|
+
* `interact_not_enabled` for Free-plan teams.
|
|
5034
|
+
*/
|
|
5035
|
+
async entitlement() {
|
|
5036
|
+
return this.entitlementClient.check();
|
|
5037
|
+
}
|
|
5038
|
+
/**
|
|
5039
|
+
* Decode a chart feature (from `queryRenderedFeatures`, the features API,
|
|
5040
|
+
* or plain `{ layer, properties }`) into a typed {@link InspectResult}.
|
|
5041
|
+
*
|
|
5042
|
+
* The first call performs the server-side entitlement check. With
|
|
5043
|
+
* `{ enrich: true }` the result is upgraded with full S-57 properties and
|
|
5044
|
+
* source-cell provenance (edition / update / issue date) via the features
|
|
5045
|
+
* API — metered against the key's `featureCalls` quota.
|
|
5046
|
+
*
|
|
5047
|
+
* @example
|
|
5048
|
+
* const result = await chrt.interact.inspect(feature);
|
|
5049
|
+
* if (result.kind === "navaid") console.log(result.characteristic); // "Fl R 4s"
|
|
5050
|
+
*/
|
|
5051
|
+
async inspect(feature, opts) {
|
|
5052
|
+
await this.entitlementClient.check();
|
|
5053
|
+
const result = buildInspectResult(feature, opts);
|
|
5054
|
+
if (opts?.enrich) {
|
|
5055
|
+
return enrichResult(this.http, result, opts.signal);
|
|
5056
|
+
}
|
|
5057
|
+
return result;
|
|
5058
|
+
}
|
|
5059
|
+
/**
|
|
5060
|
+
* Collect EVERY inspectable feature under a point on a CHRT map —
|
|
5061
|
+
* deduplicated across tile boundaries by S-57 identity (`LNAM`) and
|
|
5062
|
+
* ordered by class priority (point targets > lines > areas, safety
|
|
5063
|
+
* classes first within each band). Area features never preempt the buoy
|
|
5064
|
+
* on top of them but always stay selectable: a click on open water
|
|
5065
|
+
* resolves to its DEPARE.
|
|
5066
|
+
*
|
|
5067
|
+
* `point` is a screen pixel point (`e.point` from a map click) or
|
|
5068
|
+
* `[lng, lat]`. Point/line targets are picked up within
|
|
5069
|
+
* `opts.tolerance` px (default 8); polygon candidates require the exact
|
|
5070
|
+
* point to fall inside them.
|
|
5071
|
+
*
|
|
5072
|
+
* The first call performs the server-side entitlement check.
|
|
5073
|
+
*
|
|
5074
|
+
* @example
|
|
5075
|
+
* const { candidates, select } = await chrt.interact.inspectAll(map, e.point);
|
|
5076
|
+
* const result = await select(candidates[0]); // full decode (+ merge)
|
|
5077
|
+
*/
|
|
5078
|
+
async inspectAll(map, point, opts = {}) {
|
|
5079
|
+
if (!map || typeof map.queryRenderedFeatures !== "function") {
|
|
5080
|
+
throw new InteractError(
|
|
5081
|
+
"unexpected_response",
|
|
5082
|
+
"inspectAll(): expected a MapLibre / Mapbox GL map instance"
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
5085
|
+
await this.entitlementClient.check();
|
|
5086
|
+
const screen = Array.isArray(point) ? map.project([point[0], point[1]]) : point;
|
|
5087
|
+
const lngLat = Array.isArray(point) ? { lng: point[0], lat: point[1] } : map.unproject?.([point.x, point.y]) ?? null;
|
|
5088
|
+
const at = { point: screen, lngLat };
|
|
5089
|
+
const candidates = collectClickCandidates(map, screen, opts);
|
|
5090
|
+
const http = this.http;
|
|
5091
|
+
return {
|
|
5092
|
+
candidates,
|
|
5093
|
+
select: async (candidate, selectOpts) => {
|
|
5094
|
+
const result = decodeCandidate(map, candidate, at, opts);
|
|
5095
|
+
if (selectOpts?.enrich) {
|
|
5096
|
+
return enrichResult(http, result, selectOpts.signal);
|
|
5097
|
+
}
|
|
5098
|
+
return result;
|
|
5099
|
+
}
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
/**
|
|
5103
|
+
* Attach click-to-inspect to a MapLibre / Mapbox GL map rendering CHRT
|
|
5104
|
+
* tiles. Resolves once the server-side entitlement check passes; rejects
|
|
5105
|
+
* with `InteractError` (`interact_not_enabled` on Free plans) otherwise —
|
|
5106
|
+
* no listeners are wired on denial.
|
|
5107
|
+
*
|
|
5108
|
+
* Headless-first: pass `onInspect` to receive every decoded
|
|
5109
|
+
* {@link InspectResult} and render your own UI. Without callbacks, the
|
|
5110
|
+
* built-in minimal popup is shown (opt back in explicitly with
|
|
5111
|
+
* `popup: true` to have both).
|
|
5112
|
+
*
|
|
5113
|
+
* A click that hits SEVERAL features (deduplicated by S-57 identity)
|
|
5114
|
+
* disambiguates instead of silently taking the topmost:
|
|
5115
|
+
* - `onCandidates` provided → it receives the priority-ordered list;
|
|
5116
|
+
* resolve a choice with `ctx.select(candidate)`.
|
|
5117
|
+
* - built-in popup → shows a candidate list; picking a row opens the
|
|
5118
|
+
* result card.
|
|
5119
|
+
* - `onInspect` only → the top candidate is inspected immediately, and
|
|
5120
|
+
* clicking the SAME spot again cycles to the next candidate — so an
|
|
5121
|
+
* area feature under a buoy is reachable with a second click.
|
|
5122
|
+
*
|
|
5123
|
+
* @example
|
|
5124
|
+
* const handle = await chrt.interact.attach(map, {
|
|
5125
|
+
* onInspect: (result) => console.log(result.title, result.characteristic),
|
|
5126
|
+
* });
|
|
5127
|
+
* // later: handle.detach();
|
|
5128
|
+
*/
|
|
5129
|
+
async attach(map, opts = {}) {
|
|
5130
|
+
if (!map || typeof map.queryRenderedFeatures !== "function" || typeof map.on !== "function") {
|
|
5131
|
+
throw new InteractError(
|
|
5132
|
+
"unexpected_response",
|
|
5133
|
+
"attach(): expected a MapLibre / Mapbox GL map instance"
|
|
5134
|
+
);
|
|
5135
|
+
}
|
|
5136
|
+
await this.entitlementClient.check();
|
|
5137
|
+
const showPopup = opts.popup ?? (!opts.onInspect && !opts.onCandidates);
|
|
5138
|
+
const popup = showPopup ? new InteractPopup(map) : null;
|
|
5139
|
+
const http = this.http;
|
|
5140
|
+
let cycle = null;
|
|
5141
|
+
const samePoint = (a, b) => {
|
|
5142
|
+
const pad = opts.tolerance ?? DEFAULT_CLICK_TOLERANCE;
|
|
5143
|
+
return Math.abs(a.x - b.x) <= pad && Math.abs(a.y - b.y) <= pad;
|
|
5144
|
+
};
|
|
5145
|
+
const select = (candidate, e) => {
|
|
5146
|
+
const result = decodeCandidate(map, candidate, e, opts);
|
|
5147
|
+
const finish = (r) => {
|
|
5148
|
+
popup?.show(r, r.coordinates ?? [e.lngLat.lng, e.lngLat.lat]);
|
|
5149
|
+
opts.onInspect?.(r, { lngLat: e.lngLat, point: e.point, feature: candidate.feature });
|
|
5150
|
+
return r;
|
|
5151
|
+
};
|
|
5152
|
+
if (opts.enrich) {
|
|
5153
|
+
return enrichResult(http, result).then(finish).catch(() => finish(result));
|
|
5154
|
+
}
|
|
5155
|
+
return Promise.resolve(finish(result));
|
|
5156
|
+
};
|
|
5157
|
+
const clickHandler = (e) => {
|
|
5158
|
+
const candidates = collectClickCandidates(map, e.point, opts);
|
|
5159
|
+
if (candidates.length === 0) {
|
|
5160
|
+
cycle = null;
|
|
5161
|
+
popup?.close();
|
|
5162
|
+
opts.onMiss?.({ lngLat: e.lngLat });
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5165
|
+
if (candidates.length === 1) {
|
|
5166
|
+
cycle = null;
|
|
5167
|
+
void select(candidates[0], e);
|
|
5168
|
+
return;
|
|
5169
|
+
}
|
|
5170
|
+
if (opts.onCandidates || popup) {
|
|
5171
|
+
cycle = null;
|
|
5172
|
+
if (popup) {
|
|
5173
|
+
popup.showList(candidates, [e.lngLat.lng, e.lngLat.lat], (c) => void select(c, e));
|
|
5174
|
+
}
|
|
5175
|
+
opts.onCandidates?.(candidates, {
|
|
5176
|
+
lngLat: e.lngLat,
|
|
5177
|
+
point: e.point,
|
|
5178
|
+
select: (c) => select(c, e)
|
|
5179
|
+
});
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
const key = candidates.map((c) => c.identity ?? c.layerId).join("/");
|
|
5183
|
+
if (cycle && cycle.key === key && samePoint(cycle.point, e.point)) {
|
|
5184
|
+
cycle.index = (cycle.index + 1) % candidates.length;
|
|
5185
|
+
cycle.point = e.point;
|
|
5186
|
+
} else {
|
|
5187
|
+
cycle = { key, point: e.point, index: 0 };
|
|
5188
|
+
}
|
|
5189
|
+
void select(candidates[cycle.index], e);
|
|
5190
|
+
};
|
|
5191
|
+
map.on("click", clickHandler);
|
|
5192
|
+
return {
|
|
5193
|
+
detach: () => {
|
|
5194
|
+
map.off("click", clickHandler);
|
|
5195
|
+
popup?.close();
|
|
5196
|
+
}
|
|
5197
|
+
};
|
|
5198
|
+
}
|
|
5036
5199
|
};
|
|
5037
|
-
function getColorTable(name) {
|
|
5038
|
-
return COLOR_TABLES[name];
|
|
5039
|
-
}
|
|
5040
|
-
function resolveColor(token, table = "DAY") {
|
|
5041
|
-
const t = COLOR_TABLES[table];
|
|
5042
|
-
return t[token] ?? (table === "NIGHT" ? "#c8aa78" : "#000000");
|
|
5043
|
-
}
|
|
5044
5200
|
|
|
5045
5201
|
// src/atlas/s52-linestyles-generated.ts
|
|
5046
5202
|
var S52_LINESTYLES = {
|
|
@@ -27691,6 +27847,35 @@ function iconExpressionForFamily(objectClass, family) {
|
|
|
27691
27847
|
}
|
|
27692
27848
|
}
|
|
27693
27849
|
|
|
27850
|
+
// src/atlas/base-bundle.ts
|
|
27851
|
+
var BASE_BUNDLE_ID = BASE_REGION;
|
|
27852
|
+
var BASE_BUNDLE_SCHEMA = "chrt.basemap-bundle/1";
|
|
27853
|
+
var BASE_BUNDLE_MAXZOOM = 6;
|
|
27854
|
+
var BASE_BUNDLE_SOURCE_LAYERS = [
|
|
27855
|
+
"base_earth",
|
|
27856
|
+
"base_water",
|
|
27857
|
+
"base_boundary",
|
|
27858
|
+
"country_boundary"
|
|
27859
|
+
];
|
|
27860
|
+
var BASE_BUNDLE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth";
|
|
27861
|
+
var BASE_CLIENT_RASTER_BEFORE_ID = "chrtbase-nocoverage";
|
|
27862
|
+
function applyBaseBundle(style, opts) {
|
|
27863
|
+
const keep = new Set(BASE_BUNDLE_SOURCE_LAYERS);
|
|
27864
|
+
style.layers = style.layers.filter((raw) => {
|
|
27865
|
+
const layer = raw;
|
|
27866
|
+
if (layer.source !== BASE_REGION) return true;
|
|
27867
|
+
return typeof layer["source-layer"] === "string" && keep.has(layer["source-layer"]);
|
|
27868
|
+
});
|
|
27869
|
+
style.sources[BASE_REGION] = {
|
|
27870
|
+
type: "vector",
|
|
27871
|
+
tiles: [opts.tiles],
|
|
27872
|
+
minzoom: BASE_MINZOOM,
|
|
27873
|
+
maxzoom: BASE_BUNDLE_MAXZOOM,
|
|
27874
|
+
attribution: BASE_BUNDLE_ATTRIBUTION
|
|
27875
|
+
};
|
|
27876
|
+
return style;
|
|
27877
|
+
}
|
|
27878
|
+
|
|
27694
27879
|
// src/atlas/layer-builder.ts
|
|
27695
27880
|
var SCAMIN_DECLUTTER_CLASSES = /* @__PURE__ */ new Set([
|
|
27696
27881
|
"OFSPLF",
|
|
@@ -29031,6 +29216,99 @@ function getOrderedClasses() {
|
|
|
29031
29216
|
return ordered;
|
|
29032
29217
|
}
|
|
29033
29218
|
|
|
29219
|
+
// src/atlas/terrain-order.ts
|
|
29220
|
+
var DRAPEABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
29221
|
+
"background",
|
|
29222
|
+
"fill",
|
|
29223
|
+
"line",
|
|
29224
|
+
"raster",
|
|
29225
|
+
"hillshade",
|
|
29226
|
+
"color-relief"
|
|
29227
|
+
]);
|
|
29228
|
+
function isDrapeable(layer) {
|
|
29229
|
+
return DRAPEABLE_LAYER_TYPES.has(layer.type);
|
|
29230
|
+
}
|
|
29231
|
+
function drapedFirst(layers) {
|
|
29232
|
+
const draped = [];
|
|
29233
|
+
const direct = [];
|
|
29234
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29235
|
+
return [...draped, ...direct];
|
|
29236
|
+
}
|
|
29237
|
+
function terrainStackCount(layers) {
|
|
29238
|
+
let stacks = 0;
|
|
29239
|
+
let prevDraped = false;
|
|
29240
|
+
for (const l of layers) {
|
|
29241
|
+
const d = isDrapeable(l);
|
|
29242
|
+
if (d && !prevDraped) stacks++;
|
|
29243
|
+
prevDraped = d;
|
|
29244
|
+
}
|
|
29245
|
+
return stacks;
|
|
29246
|
+
}
|
|
29247
|
+
function drapeHalves(layers) {
|
|
29248
|
+
const draped = [];
|
|
29249
|
+
const direct = [];
|
|
29250
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29251
|
+
return { draped, direct };
|
|
29252
|
+
}
|
|
29253
|
+
|
|
29254
|
+
// src/atlas/lamps.ts
|
|
29255
|
+
var LAMP_GLOW_LAYER_ID = "s52-LIGHTS-lamp-glow";
|
|
29256
|
+
var LAMP_CORE_LAYER_ID = "s52-LIGHTS-lamp-core";
|
|
29257
|
+
var LAMP_LAYER_IDS = [LAMP_GLOW_LAYER_ID, LAMP_CORE_LAYER_ID];
|
|
29258
|
+
var RANGE_NM = ["to-number", ["coalesce", ["get", "VALNMR"], ["get", "valnmr"]], 6];
|
|
29259
|
+
var UNSECTORED_POINTS = [
|
|
29260
|
+
"all",
|
|
29261
|
+
["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]],
|
|
29262
|
+
["!", ["has", "SECTR1"]],
|
|
29263
|
+
// …and not lights with a charted rhythm either: those flash, and the
|
|
29264
|
+
// flashing is computed by the app from `SIGSEQ` (light-sigseq.ts). What is
|
|
29265
|
+
// left here is exactly the set that is steady from every bearing.
|
|
29266
|
+
["!", ["has", "SIGSEQ"]]
|
|
29267
|
+
];
|
|
29268
|
+
function buildLampLayers(sourceId, ct) {
|
|
29269
|
+
const colour = lightFillExpression(ct);
|
|
29270
|
+
const common = {
|
|
29271
|
+
type: "circle",
|
|
29272
|
+
source: sourceId,
|
|
29273
|
+
"source-layer": "LIGHTS",
|
|
29274
|
+
filter: UNSECTORED_POINTS,
|
|
29275
|
+
layout: { visibility: "none" }
|
|
29276
|
+
};
|
|
29277
|
+
const metadata = { "chrt:role": "lamp", "s52:objectClass": "LIGHTS" };
|
|
29278
|
+
return [
|
|
29279
|
+
{
|
|
29280
|
+
...common,
|
|
29281
|
+
id: LAMP_GLOW_LAYER_ID,
|
|
29282
|
+
paint: {
|
|
29283
|
+
"circle-color": colour,
|
|
29284
|
+
// 4 nm → 7 px, 12 nm → 12 px, 25 nm → 20 px; capped, because a
|
|
29285
|
+
// primary seacoast light is not a searchlight.
|
|
29286
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 5, 4, 7, 12, 12, 25, 20],
|
|
29287
|
+
"circle-blur": 1,
|
|
29288
|
+
"circle-opacity": 0.55,
|
|
29289
|
+
// A lamp is a point of light: it does not grow as it comes near, and
|
|
29290
|
+
// it does not lie down with the chart.
|
|
29291
|
+
"circle-pitch-alignment": "viewport",
|
|
29292
|
+
"circle-pitch-scale": "viewport"
|
|
29293
|
+
},
|
|
29294
|
+
metadata: { ...metadata, "chrt:lamp": "glow" }
|
|
29295
|
+
},
|
|
29296
|
+
{
|
|
29297
|
+
...common,
|
|
29298
|
+
id: LAMP_CORE_LAYER_ID,
|
|
29299
|
+
paint: {
|
|
29300
|
+
"circle-color": colour,
|
|
29301
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 1.5, 12, 2.5, 25, 3.5],
|
|
29302
|
+
"circle-blur": 0.25,
|
|
29303
|
+
"circle-opacity": 1,
|
|
29304
|
+
"circle-pitch-alignment": "viewport",
|
|
29305
|
+
"circle-pitch-scale": "viewport"
|
|
29306
|
+
},
|
|
29307
|
+
metadata: { ...metadata, "chrt:lamp": "core" }
|
|
29308
|
+
}
|
|
29309
|
+
];
|
|
29310
|
+
}
|
|
29311
|
+
|
|
29034
29312
|
// src/atlas/band-priority.ts
|
|
29035
29313
|
var BAND_PRIORITY_RANGES = {
|
|
29036
29314
|
"1": { minzoom: 0 },
|
|
@@ -29146,11 +29424,21 @@ function styleModeBand(mode) {
|
|
|
29146
29424
|
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
29147
29425
|
}
|
|
29148
29426
|
function bandFilterExpression(codes) {
|
|
29427
|
+
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
29428
|
+
}
|
|
29429
|
+
var BAND_CODE_ALIASES = {
|
|
29430
|
+
"5": ["7"]
|
|
29431
|
+
};
|
|
29432
|
+
function cellBandCharacters(code) {
|
|
29433
|
+
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
29434
|
+
}
|
|
29435
|
+
function cellBandMatch(cellBandExpr, codes) {
|
|
29149
29436
|
const list = Array.isArray(codes) ? codes : [codes];
|
|
29150
|
-
|
|
29151
|
-
|
|
29437
|
+
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
29438
|
+
if (chars.length === 1) {
|
|
29439
|
+
return ["==", cellBandExpr, chars[0]];
|
|
29152
29440
|
}
|
|
29153
|
-
return ["in",
|
|
29441
|
+
return ["in", cellBandExpr, ["literal", chars]];
|
|
29154
29442
|
}
|
|
29155
29443
|
function combineWithBandFilter(baseFilter, bandFilter) {
|
|
29156
29444
|
if (!baseFilter) return bandFilter;
|
|
@@ -30234,7 +30522,8 @@ function fanOrientationLabelByCellBand(label, bandFan, objectClass) {
|
|
|
30234
30522
|
}
|
|
30235
30523
|
const cellFiltered = combineWithBandFilter(
|
|
30236
30524
|
label.filter,
|
|
30237
|
-
|
|
30525
|
+
// Aliases included (USACE `7` rides with band 5 — #269).
|
|
30526
|
+
cellBandMatch(cellBandExpr, code)
|
|
30238
30527
|
);
|
|
30239
30528
|
const minzoom = Math.max(
|
|
30240
30529
|
label.minzoom ?? range.minzoom,
|
|
@@ -30439,6 +30728,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30439
30728
|
(b) => !TOP_STACK_LIGHT_CLASSES.has(b.objectClass) && !BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30440
30729
|
);
|
|
30441
30730
|
const topLightBundles = bundles.filter((b) => TOP_STACK_LIGHT_CLASSES.has(b.objectClass));
|
|
30731
|
+
const lampLayers = buildLampLayers(sourceId, palette);
|
|
30442
30732
|
const waterBaseBundles = bundles.filter(
|
|
30443
30733
|
(b) => BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30444
30734
|
);
|
|
@@ -30472,6 +30762,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30472
30762
|
emitChartFeatureVariants(waterBaseBundles);
|
|
30473
30763
|
emitChartFeatureVariants(regularBundles);
|
|
30474
30764
|
emitChartFeatureVariants(topLightBundles);
|
|
30765
|
+
layers.push(...lampLayers);
|
|
30475
30766
|
const chartLabelBundles = byLabelPriorityBottomUp(
|
|
30476
30767
|
bundles.filter(
|
|
30477
30768
|
(b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
|
|
@@ -30549,6 +30840,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30549
30840
|
}
|
|
30550
30841
|
}
|
|
30551
30842
|
}
|
|
30843
|
+
layers.push(...lampLayers);
|
|
30552
30844
|
{
|
|
30553
30845
|
const TERMINAL_FILL_RE = new RegExp(
|
|
30554
30846
|
`^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
|
|
@@ -30637,13 +30929,18 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30637
30929
|
const glyphsBase = `${opts.tileBaseUrl ?? "https://tiles.chrt.co"}/v1/glyphs`;
|
|
30638
30930
|
const isLndareFill = (l) => /^s52-LNDARE-fill-band\d+$/.test(l.id);
|
|
30639
30931
|
const isWaterBaseFill = (l) => /^s52-(DEPARE|DRGARE|UNSARE)-fill-band\d+$/.test(l.id);
|
|
30932
|
+
const isLakareFill = (l) => /^s52-LAKARE-fill-band\d+$/.test(l.id);
|
|
30640
30933
|
const bandOf = (l) => {
|
|
30641
30934
|
const m = /-band(\d+)$/.exec(l.id);
|
|
30642
30935
|
return m ? Number(m[1]) : 0;
|
|
30643
30936
|
};
|
|
30644
30937
|
const emitted = opts.land ? (() => {
|
|
30645
|
-
const relocated = layers.filter(
|
|
30646
|
-
|
|
30938
|
+
const relocated = layers.filter(
|
|
30939
|
+
(l) => isLndareFill(l) || isWaterBaseFill(l) || isLakareFill(l)
|
|
30940
|
+
);
|
|
30941
|
+
const others = layers.filter(
|
|
30942
|
+
(l) => !isLndareFill(l) && !isWaterBaseFill(l) && !isLakareFill(l)
|
|
30943
|
+
);
|
|
30647
30944
|
const isSeaareFill = (l) => /^s52-SEAARE-fill-band\d+$/.test(l.id);
|
|
30648
30945
|
const seaare = others.filter(isSeaareFill);
|
|
30649
30946
|
const rest = others.filter((l) => !isSeaareFill(l));
|
|
@@ -30667,40 +30964,43 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30667
30964
|
for (const b of bands) {
|
|
30668
30965
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isWaterBaseFill(l)));
|
|
30669
30966
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLndareFill(l)).map(asBaseTan));
|
|
30967
|
+
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLakareFill(l)));
|
|
30670
30968
|
}
|
|
30671
30969
|
const findRest = (id) => restAbove.findIndex((l) => l.id === id);
|
|
30672
|
-
const
|
|
30673
|
-
const pairAt = shoreAt !== -1 ? shoreAt : findRest("chrtbase-inland-hatch");
|
|
30970
|
+
const pairAt = findRest("chrtbase-inland-water");
|
|
30674
30971
|
let restFinal = restAbove;
|
|
30675
30972
|
if (pairAt !== -1) {
|
|
30676
30973
|
const repaint = [];
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30974
|
+
const pushCascadeClones = (l, b) => {
|
|
30975
|
+
const src = l;
|
|
30976
|
+
const meta = { ...src.metadata ?? {}, "chrt:role": "charted-water-repaint" };
|
|
30977
|
+
const notDom = {
|
|
30978
|
+
...l,
|
|
30979
|
+
id: `${src.id}-overpair`,
|
|
30980
|
+
filter: ["all", src.filter, NOT_DOMINATED_FILTER],
|
|
30981
|
+
metadata: meta
|
|
30982
|
+
};
|
|
30983
|
+
delete notDom.maxzoom;
|
|
30984
|
+
delete notDom.minzoom;
|
|
30985
|
+
repaint.push(notDom);
|
|
30986
|
+
for (const D of BAND_DRAW_ORDER) {
|
|
30987
|
+
if (parseInt(D, 10) <= b) continue;
|
|
30988
|
+
const dMaxzoom = dominatedVariantMaxzoom(D);
|
|
30989
|
+
if (dMaxzoom <= 0) continue;
|
|
30990
|
+
const domVariant = {
|
|
30682
30991
|
...l,
|
|
30683
|
-
id: `${src.id}-overpair`,
|
|
30684
|
-
filter: ["all", src.filter,
|
|
30685
|
-
metadata: meta
|
|
30992
|
+
id: `${src.id}-overpair-domBy${D}`,
|
|
30993
|
+
filter: ["all", src.filter, dominatedByMinFilter(D)],
|
|
30994
|
+
metadata: meta,
|
|
30995
|
+
maxzoom: dMaxzoom
|
|
30686
30996
|
};
|
|
30687
|
-
delete
|
|
30688
|
-
|
|
30689
|
-
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
|
|
30693
|
-
|
|
30694
|
-
const domVariant = {
|
|
30695
|
-
...l,
|
|
30696
|
-
id: `${src.id}-overpair-domBy${D}`,
|
|
30697
|
-
filter: ["all", src.filter, dominatedByMinFilter(D)],
|
|
30698
|
-
metadata: meta,
|
|
30699
|
-
maxzoom: dMaxzoom
|
|
30700
|
-
};
|
|
30701
|
-
delete domVariant.minzoom;
|
|
30702
|
-
repaint.push(domVariant);
|
|
30703
|
-
}
|
|
30997
|
+
delete domVariant.minzoom;
|
|
30998
|
+
repaint.push(domVariant);
|
|
30999
|
+
}
|
|
31000
|
+
};
|
|
31001
|
+
for (const b of bands) {
|
|
31002
|
+
for (const l of relocated.filter((x) => bandOf(x) === b && isWaterBaseFill(x))) {
|
|
31003
|
+
pushCascadeClones(l, b);
|
|
30704
31004
|
}
|
|
30705
31005
|
if (parseInt(b, 10) >= FINE_LAND_REPAINT_MIN_BAND) {
|
|
30706
31006
|
for (const l of relocated.filter(
|
|
@@ -30716,6 +31016,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30716
31016
|
repaint.push(landClone);
|
|
30717
31017
|
}
|
|
30718
31018
|
}
|
|
31019
|
+
for (const l of relocated.filter((x) => bandOf(x) === b && isLakareFill(x))) {
|
|
31020
|
+
pushCascadeClones(l, b);
|
|
31021
|
+
}
|
|
30719
31022
|
}
|
|
30720
31023
|
const isCoalneLine = (l) => /^s52-COALNE-line-band\d(-domBy\d)?$/.test(l.id);
|
|
30721
31024
|
for (const l of layers.filter(isCoalneLine)) {
|
|
@@ -30739,9 +31042,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30739
31042
|
repaint.push(terminal);
|
|
30740
31043
|
}
|
|
30741
31044
|
restFinal = [
|
|
30742
|
-
...restAbove.slice(0, pairAt
|
|
31045
|
+
...restAbove.slice(0, pairAt),
|
|
30743
31046
|
...repaint,
|
|
30744
|
-
...restAbove.slice(pairAt
|
|
31047
|
+
...restAbove.slice(pairAt)
|
|
30745
31048
|
];
|
|
30746
31049
|
}
|
|
30747
31050
|
{
|
|
@@ -30790,6 +31093,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30790
31093
|
emitted.push(layer);
|
|
30791
31094
|
}
|
|
30792
31095
|
}
|
|
31096
|
+
const finalLayers = drapedFirst(emitted);
|
|
30793
31097
|
const style = {
|
|
30794
31098
|
version: 8,
|
|
30795
31099
|
name: `CHRT ${SYMBOL_SET_LABELS[symbolSet]} ${PALETTE_NAMES[palette]}${opts.chartMode ? ` \u2014 chart:${opts.chartMode.band}` : ""}`,
|
|
@@ -30798,7 +31102,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30798
31102
|
"chrt:symbol-set": symbolSet,
|
|
30799
31103
|
"chrt:color-table": palette,
|
|
30800
31104
|
"chrt:generated-at": (/* @__PURE__ */ new Date()).toISOString(),
|
|
30801
|
-
"chrt:layer-count":
|
|
31105
|
+
"chrt:layer-count": finalLayers.length,
|
|
30802
31106
|
"chrt:land": Boolean(opts.land),
|
|
30803
31107
|
"chrt:relief": Boolean(opts.relief),
|
|
30804
31108
|
"chrt:base": Boolean(opts.base),
|
|
@@ -30838,7 +31142,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30838
31142
|
// upstream glyph byte-identical and composite only those six in from
|
|
30839
31143
|
// Noto Sans; see scripts/generate-glyphs.mjs.
|
|
30840
31144
|
glyphs: `${glyphsBase}/{fontstack}/{range}.pbf`,
|
|
30841
|
-
layers:
|
|
31145
|
+
layers: finalLayers
|
|
30842
31146
|
};
|
|
30843
31147
|
if (symbolSet === "s101") return applyS101SymbolSet(style);
|
|
30844
31148
|
if (symbolSet === "chrt") return applyChrtSymbolSet(style, palette);
|
|
@@ -31667,8 +31971,16 @@ var Chrt = class {
|
|
|
31667
31971
|
ALL_WATER_BODY_LAYERS,
|
|
31668
31972
|
AREA_QUERY_LAYERS,
|
|
31669
31973
|
ATON_EXPRESSION_CLASSES,
|
|
31974
|
+
BAND_CODE_ALIASES,
|
|
31670
31975
|
BAND_DRAW_ORDER,
|
|
31671
31976
|
BAND_PRIORITY_RANGES,
|
|
31977
|
+
BASE_ATTRIBUTION,
|
|
31978
|
+
BASE_BUNDLE_ATTRIBUTION,
|
|
31979
|
+
BASE_BUNDLE_ID,
|
|
31980
|
+
BASE_BUNDLE_MAXZOOM,
|
|
31981
|
+
BASE_BUNDLE_SCHEMA,
|
|
31982
|
+
BASE_BUNDLE_SOURCE_LAYERS,
|
|
31983
|
+
BASE_CLIENT_RASTER_BEFORE_ID,
|
|
31672
31984
|
BASE_MAXZOOM,
|
|
31673
31985
|
BASE_MINZOOM,
|
|
31674
31986
|
BASE_REGION,
|
|
@@ -31701,6 +32013,7 @@ var Chrt = class {
|
|
|
31701
32013
|
DEFAULT_TILE_BASE_URL,
|
|
31702
32014
|
DEFAULT_TILE_REGION,
|
|
31703
32015
|
DEFAULT_WATER_BODY_SETTINGS,
|
|
32016
|
+
DRAPEABLE_LAYER_TYPES,
|
|
31704
32017
|
INSPECT_CLASS_RANK,
|
|
31705
32018
|
INSPECT_RANK_DEVIATIONS,
|
|
31706
32019
|
InteractClient,
|
|
@@ -31709,6 +32022,11 @@ var Chrt = class {
|
|
|
31709
32022
|
LABEL_BEHAVIOR,
|
|
31710
32023
|
LABEL_LAYER_GROUPS,
|
|
31711
32024
|
LABEL_PRIORITY,
|
|
32025
|
+
LAMP_CORE_LAYER_ID,
|
|
32026
|
+
LAMP_GLOW_LAYER_ID,
|
|
32027
|
+
LAMP_LAYER_IDS,
|
|
32028
|
+
LAMP_TICK_MS,
|
|
32029
|
+
LAND_ATTRIBUTION,
|
|
31712
32030
|
LAND_BUILD_MAXZOOM,
|
|
31713
32031
|
LAND_REGION,
|
|
31714
32032
|
NAUTICAL_SYMBOLS,
|
|
@@ -31724,7 +32042,10 @@ var Chrt = class {
|
|
|
31724
32042
|
RASTER_ATON_CLASSES,
|
|
31725
32043
|
RASTER_GLYPH_NAMES,
|
|
31726
32044
|
RASTER_ICON_PREFIX,
|
|
32045
|
+
RELIEF_ATTRIBUTION,
|
|
32046
|
+
RELIEF_ENCODING,
|
|
31727
32047
|
RELIEF_REGION,
|
|
32048
|
+
RELIEF_TILE_SIZE,
|
|
31728
32049
|
S101_AREA_FILL_NAMES,
|
|
31729
32050
|
S101_CHART_SECTIONS,
|
|
31730
32051
|
S101_FEATURES,
|
|
@@ -31744,6 +32065,8 @@ var Chrt = class {
|
|
|
31744
32065
|
S52_LOOKUP,
|
|
31745
32066
|
S52_NIGHT,
|
|
31746
32067
|
S52_PATTERN_SPRITE_PREFIX,
|
|
32068
|
+
SIGPER_TOLERANCE_SECONDS,
|
|
32069
|
+
SIGSEQ_MAX_TERMS,
|
|
31747
32070
|
SOUNDING_LAYERS,
|
|
31748
32071
|
SYMBOL_FAMILIES,
|
|
31749
32072
|
SYMBOL_FAMILY_META,
|
|
@@ -31753,6 +32076,7 @@ var Chrt = class {
|
|
|
31753
32076
|
TileTokenError,
|
|
31754
32077
|
WATER_BODY_LAYER_GROUPS,
|
|
31755
32078
|
WeatherEndpoint,
|
|
32079
|
+
applyBaseBundle,
|
|
31756
32080
|
applyChrtSymbolSet,
|
|
31757
32081
|
applyColorTheme,
|
|
31758
32082
|
applyCustomStyleSettings,
|
|
@@ -31765,6 +32089,7 @@ var Chrt = class {
|
|
|
31765
32089
|
buildFlashPattern,
|
|
31766
32090
|
buildLabelLayer,
|
|
31767
32091
|
buildLakeTierExpression,
|
|
32092
|
+
buildLampLayers,
|
|
31768
32093
|
buildLandAreaTierExpression,
|
|
31769
32094
|
buildLandRegionTierExpression,
|
|
31770
32095
|
buildLayersForClass,
|
|
@@ -31777,6 +32102,8 @@ var Chrt = class {
|
|
|
31777
32102
|
buildSectorsFromFeatures,
|
|
31778
32103
|
buildSources,
|
|
31779
32104
|
candidateRank,
|
|
32105
|
+
cellBandCharacters,
|
|
32106
|
+
cellBandMatch,
|
|
31780
32107
|
chrtAtonTintExpression,
|
|
31781
32108
|
chrtLightTintExpression,
|
|
31782
32109
|
collectCandidates,
|
|
@@ -31791,6 +32118,8 @@ var Chrt = class {
|
|
|
31791
32118
|
depareColor,
|
|
31792
32119
|
depareFillExpression,
|
|
31793
32120
|
deriveMarinerName,
|
|
32121
|
+
drapeHalves,
|
|
32122
|
+
drapedFirst,
|
|
31794
32123
|
extractNavAidDesignation,
|
|
31795
32124
|
featureIdentity,
|
|
31796
32125
|
generateCustomStyle,
|
|
@@ -31823,6 +32152,7 @@ var Chrt = class {
|
|
|
31823
32152
|
hazardColor,
|
|
31824
32153
|
hazardFillExpression,
|
|
31825
32154
|
iconExpressionForFamily,
|
|
32155
|
+
isDrapeable,
|
|
31826
32156
|
isLabelLayerVisible,
|
|
31827
32157
|
isNavAidLayer,
|
|
31828
32158
|
isNavAidLayerVisible,
|
|
@@ -31830,6 +32160,8 @@ var Chrt = class {
|
|
|
31830
32160
|
isVisibleInMode,
|
|
31831
32161
|
isWaterBodyLayerVisible,
|
|
31832
32162
|
labelSortKey,
|
|
32163
|
+
lampRhythmColour,
|
|
32164
|
+
lampRhythmOpacity,
|
|
31833
32165
|
lateralColor,
|
|
31834
32166
|
lateralFillExpression,
|
|
31835
32167
|
lightColor,
|
|
@@ -31839,6 +32171,7 @@ var Chrt = class {
|
|
|
31839
32171
|
normalizeTheme,
|
|
31840
32172
|
paginate,
|
|
31841
32173
|
parseListCodes,
|
|
32174
|
+
parseSigseqTerms,
|
|
31842
32175
|
parseSymbolFamily,
|
|
31843
32176
|
parseWebhook,
|
|
31844
32177
|
patternSpriteId,
|
|
@@ -31853,12 +32186,14 @@ var Chrt = class {
|
|
|
31853
32186
|
resolveS101Color,
|
|
31854
32187
|
resolveStyle,
|
|
31855
32188
|
resolveTargetClass,
|
|
32189
|
+
rhythmProperties,
|
|
31856
32190
|
s101ArtworkProvenance,
|
|
31857
32191
|
sanitizeCustomStyleConfig,
|
|
31858
32192
|
scaleSizeValue,
|
|
31859
32193
|
scaminToMinZoom,
|
|
31860
32194
|
sectorLimitToArcBearing,
|
|
31861
32195
|
styleModeBand,
|
|
32196
|
+
terrainStackCount,
|
|
31862
32197
|
tileTemplate,
|
|
31863
32198
|
uncoveredSectorArcs,
|
|
31864
32199
|
withinBboxFilter,
|