@chrtco/sdk 0.10.38 → 0.10.40
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 +134 -0
- package/dist/index.cjs +995 -651
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +529 -17
- package/dist/index.d.ts +529 -17
- package/dist/index.js +964 -651
- package/dist/index.js.map +1 -1
- package/package.json +3 -20
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}`;
|
|
@@ -638,14 +673,15 @@ function buildSources(opts = {}) {
|
|
|
638
673
|
type: "vector",
|
|
639
674
|
tiles: [tileTemplate(base, BASE_REGION, "mvt", buildFor(BASE_REGION))],
|
|
640
675
|
minzoom: BASE_MINZOOM,
|
|
641
|
-
maxzoom: BASE_MAXZOOM
|
|
676
|
+
maxzoom: BASE_MAXZOOM,
|
|
677
|
+
attribution: BASE_ATTRIBUTION
|
|
642
678
|
};
|
|
643
679
|
}
|
|
644
680
|
if (opts.relief) {
|
|
645
681
|
sources[RELIEF_REGION] = {
|
|
646
682
|
type: "raster-dem",
|
|
647
683
|
tiles: [tileTemplate(base, RELIEF_REGION, "png", buildFor(RELIEF_REGION))],
|
|
648
|
-
encoding:
|
|
684
|
+
encoding: RELIEF_ENCODING,
|
|
649
685
|
tileSize: RELIEF_TILE_SIZE,
|
|
650
686
|
minzoom: RELIEF_MINZOOM,
|
|
651
687
|
maxzoom: RELIEF_MAXZOOM
|
|
@@ -671,6 +707,7 @@ var TilesEndpoint = class {
|
|
|
671
707
|
if (opts.mode) params.set("mode", opts.mode);
|
|
672
708
|
if (opts.land) params.set("land", "1");
|
|
673
709
|
if (opts.relief) params.set("relief", "1");
|
|
710
|
+
if (opts.units === "imperial") params.set("units", "imperial");
|
|
674
711
|
const query2 = params.size > 0 ? `?${params.toString()}` : "";
|
|
675
712
|
return `${this.tileBaseUrl}/v1/styles/${this.region}/style.json${query2}`;
|
|
676
713
|
}
|
|
@@ -796,6 +833,9 @@ var WeatherEndpoint = class {
|
|
|
796
833
|
*
|
|
797
834
|
* `unavailable` names every field the station did not report, so absence is
|
|
798
835
|
* stated rather than inferred from a missing key.
|
|
836
|
+
*
|
|
837
|
+
* Pass `hours` for the station's recent readings as `series` — NDBC's own
|
|
838
|
+
* readings at NDBC's own instants, never interpolated.
|
|
799
839
|
*/
|
|
800
840
|
async conditions(options) {
|
|
801
841
|
return this.http.request(
|
|
@@ -804,7 +844,8 @@ var WeatherEndpoint = class {
|
|
|
804
844
|
lat: options.lat,
|
|
805
845
|
lon: options.lon,
|
|
806
846
|
units: options.units,
|
|
807
|
-
station: options.station
|
|
847
|
+
station: options.station,
|
|
848
|
+
hours: options.hours
|
|
808
849
|
},
|
|
809
850
|
options.signal
|
|
810
851
|
);
|
|
@@ -1268,10 +1309,10 @@ async function readManifest(manifestPath, fs) {
|
|
|
1268
1309
|
if (!manifestPath) return emptyManifest();
|
|
1269
1310
|
if (!await fs.exists(manifestPath)) return emptyManifest();
|
|
1270
1311
|
const raw = await fs.readFile(manifestPath);
|
|
1271
|
-
const
|
|
1312
|
+
const text2 = new TextDecoder().decode(raw);
|
|
1272
1313
|
let parsed;
|
|
1273
1314
|
try {
|
|
1274
|
-
parsed = JSON.parse(
|
|
1315
|
+
parsed = JSON.parse(text2);
|
|
1275
1316
|
} catch {
|
|
1276
1317
|
throw new OfflineError(
|
|
1277
1318
|
"unexpected_response",
|
|
@@ -1295,8 +1336,8 @@ async function readManifest(manifestPath, fs) {
|
|
|
1295
1336
|
};
|
|
1296
1337
|
}
|
|
1297
1338
|
async function writeManifest(manifestPath, fs, manifest) {
|
|
1298
|
-
const
|
|
1299
|
-
const bytes = new TextEncoder().encode(
|
|
1339
|
+
const text2 = JSON.stringify(manifest, null, 2);
|
|
1340
|
+
const bytes = new TextEncoder().encode(text2);
|
|
1300
1341
|
await fs.writeFile(manifestPath, bytes);
|
|
1301
1342
|
}
|
|
1302
1343
|
function usedBytes(manifest, excludeRegion) {
|
|
@@ -2261,110 +2302,438 @@ function s57AbbrTable(acronym) {
|
|
|
2261
2302
|
return cached;
|
|
2262
2303
|
}
|
|
2263
2304
|
|
|
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
|
-
|
|
2305
|
+
// src/atlas/s52-colors.ts
|
|
2306
|
+
var S52_DAY = {
|
|
2307
|
+
// Depth
|
|
2308
|
+
NODTA: "#a4a4a4",
|
|
2309
|
+
DEPIT: "#009678",
|
|
2310
|
+
DEPVS: "#7fbfdf",
|
|
2311
|
+
DEPMS: "#a7d4e8",
|
|
2312
|
+
DEPMD: "#cae7f3",
|
|
2313
|
+
DEPDW: "#ecf7fc",
|
|
2314
|
+
// Land / topography
|
|
2315
|
+
LANDA: "#e6d1a1",
|
|
2316
|
+
LANDF: "#c8b078",
|
|
2317
|
+
CHBRN: "#ab7948",
|
|
2318
|
+
CHGRF: "#878787",
|
|
2319
|
+
CHGRD: "#555555",
|
|
2320
|
+
// Chart symbology
|
|
2321
|
+
CHBLK: "#000000",
|
|
2322
|
+
CSTLN: "#000000",
|
|
2323
|
+
OUTLW: "#000000",
|
|
2324
|
+
BKAJ1: "#4d4d4d",
|
|
2325
|
+
// Navigation / traffic
|
|
2326
|
+
TRFCD: "#d200d2",
|
|
2327
|
+
TRFCF: "#e680e6",
|
|
2328
|
+
RESBL: "#0064b4",
|
|
2329
|
+
APTS1: "#d200d2",
|
|
2330
|
+
// Danger / hazard
|
|
2331
|
+
DNGHL: "#ff0000",
|
|
2332
|
+
ISHDG: "#cc0000",
|
|
2333
|
+
// Lights
|
|
2334
|
+
LITRD: "#ee0000",
|
|
2335
|
+
LITGN: "#00c800",
|
|
2336
|
+
LITYW: "#fddc00",
|
|
2337
|
+
LITWT: "#ffffff",
|
|
2338
|
+
LITBL: "#0064ff",
|
|
2339
|
+
LITOR: "#ff8c00",
|
|
2340
|
+
// Buoys / marks
|
|
2341
|
+
RADHI: "#68e456",
|
|
2342
|
+
RADLO: "#3f8a34",
|
|
2343
|
+
PLRTE: "#dc4025",
|
|
2344
|
+
ARPAT: "#3fa56f",
|
|
2345
|
+
ISDNG: "#c545c3",
|
|
2346
|
+
// Chart colours
|
|
2347
|
+
CHRED: "#f15469",
|
|
2348
|
+
CHGRN: "#68e456",
|
|
2349
|
+
CHYLW: "#f4da48",
|
|
2350
|
+
CHMGD: "#c545c3",
|
|
2351
|
+
CHMGF: "#d3a6e9",
|
|
2352
|
+
CHWHT: "#d4eaee",
|
|
2353
|
+
CHCOR: "#eb7d36",
|
|
2354
|
+
// Soundings
|
|
2355
|
+
SNDG1: "#7d898c",
|
|
2356
|
+
SNDG2: "#070707",
|
|
2357
|
+
DEPSC: "#525a5c",
|
|
2358
|
+
DEPCN: "#555555",
|
|
2359
|
+
// Utility / infrastructure
|
|
2360
|
+
UINFD: "#070707",
|
|
2361
|
+
UINFF: "#7d898c",
|
|
2362
|
+
UINFR: "#f15469",
|
|
2363
|
+
UINFG: "#68e456",
|
|
2364
|
+
UINFO: "#eb7d36",
|
|
2365
|
+
UINFB: "#3a78f0",
|
|
2366
|
+
UINFM: "#c545c3",
|
|
2367
|
+
UIBCK: "#d4eaee",
|
|
2368
|
+
UIBDR: "#7d898c",
|
|
2369
|
+
UIAFD: "#73b6ef",
|
|
2370
|
+
UIAFF: "#c9b97a",
|
|
2371
|
+
PIPLN: "#55aa00",
|
|
2372
|
+
// Outlines
|
|
2373
|
+
OUTLL: "#c9b97a",
|
|
2374
|
+
// Miscellaneous
|
|
2375
|
+
SCLBR: "#eb7d36",
|
|
2376
|
+
NINFO: "#eb7d36",
|
|
2377
|
+
ADINF: "#b29f34",
|
|
2378
|
+
RESGR: "#7d898c",
|
|
2379
|
+
SHIPS: "#070707",
|
|
2380
|
+
PSTRK: "#070707",
|
|
2381
|
+
SYTRK: "#7d898c",
|
|
2382
|
+
APLRT: "#eb7d36",
|
|
2383
|
+
CURSR: "#eb7d36",
|
|
2384
|
+
BKAJ2: "#232728",
|
|
2385
|
+
RES01: "#a3b4b7",
|
|
2386
|
+
RES02: "#a3b4b7",
|
|
2387
|
+
RES03: "#a3b4b7"
|
|
2289
2388
|
};
|
|
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
|
-
|
|
2389
|
+
var S52_DUSK = {
|
|
2390
|
+
NODTA: "#6b6b6b",
|
|
2391
|
+
DEPIT: "#006650",
|
|
2392
|
+
DEPVS: "#487085",
|
|
2393
|
+
DEPMS: "#608a9d",
|
|
2394
|
+
DEPMD: "#7ba6b8",
|
|
2395
|
+
DEPDW: "#95bfd0",
|
|
2396
|
+
LANDA: "#8a7d5e",
|
|
2397
|
+
LANDF: "#7a6a48",
|
|
2398
|
+
CHBRN: "#6e4e30",
|
|
2399
|
+
CHGRF: "#585858",
|
|
2400
|
+
CHGRD: "#3a3a3a",
|
|
2401
|
+
CHBLK: "#b0b0b0",
|
|
2402
|
+
CSTLN: "#b0b0b0",
|
|
2403
|
+
OUTLW: "#b0b0b0",
|
|
2404
|
+
BKAJ1: "#707070",
|
|
2405
|
+
TRFCD: "#a000a0",
|
|
2406
|
+
TRFCF: "#b060b0",
|
|
2407
|
+
RESBL: "#004a88",
|
|
2408
|
+
APTS1: "#a000a0",
|
|
2409
|
+
DNGHL: "#cc0000",
|
|
2410
|
+
ISHDG: "#990000",
|
|
2411
|
+
LITRD: "#bb0000",
|
|
2412
|
+
LITGN: "#009900",
|
|
2413
|
+
LITYW: "#c8b000",
|
|
2414
|
+
LITWT: "#d0d0d0",
|
|
2415
|
+
LITBL: "#004abb",
|
|
2416
|
+
LITOR: "#cc7000",
|
|
2417
|
+
RADHI: "#009940",
|
|
2418
|
+
RADLO: "#2d6330",
|
|
2419
|
+
PLRTE: "#a03020",
|
|
2420
|
+
ARPAT: "#306040",
|
|
2421
|
+
ISDNG: "#903090",
|
|
2422
|
+
CHRED: "#b05060",
|
|
2423
|
+
CHGRN: "#509040",
|
|
2424
|
+
CHYLW: "#b0a030",
|
|
2425
|
+
CHMGD: "#903090",
|
|
2426
|
+
CHMGF: "#905090",
|
|
2427
|
+
CHWHT: "#90a0a0",
|
|
2428
|
+
CHCOR: "#a06030",
|
|
2429
|
+
SNDG1: "#606060",
|
|
2430
|
+
SNDG2: "#b0b0b0",
|
|
2431
|
+
DEPSC: "#606060",
|
|
2432
|
+
DEPCN: "#2E2E2E",
|
|
2433
|
+
UINFD: "#b0b0b0",
|
|
2434
|
+
UINFF: "#606060",
|
|
2435
|
+
UINFR: "#b05060",
|
|
2436
|
+
UINFG: "#509040",
|
|
2437
|
+
UINFO: "#a06030",
|
|
2438
|
+
UINFB: "#304090",
|
|
2439
|
+
UINFM: "#903090",
|
|
2440
|
+
UIBCK: "#1a1a1a",
|
|
2441
|
+
UIBDR: "#606060",
|
|
2442
|
+
UIAFD: "#405060",
|
|
2443
|
+
UIAFF: "#605840",
|
|
2444
|
+
PIPLN: "#3d7a00",
|
|
2445
|
+
OUTLL: "#605840",
|
|
2446
|
+
SCLBR: "#a06030",
|
|
2447
|
+
NINFO: "#a06030",
|
|
2448
|
+
ADINF: "#807020",
|
|
2449
|
+
RESGR: "#606060",
|
|
2450
|
+
SHIPS: "#b0b0b0",
|
|
2451
|
+
PSTRK: "#b0b0b0",
|
|
2452
|
+
SYTRK: "#606060",
|
|
2453
|
+
APLRT: "#a06030",
|
|
2454
|
+
CURSR: "#cc0000",
|
|
2455
|
+
BKAJ2: "#252525",
|
|
2456
|
+
RES01: "#606060",
|
|
2457
|
+
RES02: "#606060",
|
|
2458
|
+
RES03: "#606060"
|
|
2336
2459
|
};
|
|
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
|
-
"
|
|
2460
|
+
var S52_NIGHT = {
|
|
2461
|
+
NODTA: "#3a3a32",
|
|
2462
|
+
DEPIT: "#003828",
|
|
2463
|
+
DEPVS: "#356577",
|
|
2464
|
+
DEPMS: "#265066",
|
|
2465
|
+
DEPMD: "#102e44",
|
|
2466
|
+
DEPDW: "#051a28",
|
|
2467
|
+
LANDA: "#3a3020",
|
|
2468
|
+
LANDF: "#302818",
|
|
2469
|
+
CHBRN: "#382410",
|
|
2470
|
+
CHGRF: "#2a2a22",
|
|
2471
|
+
CHGRD: "#1e1e18",
|
|
2472
|
+
CHBLK: "#c8aa78",
|
|
2473
|
+
CSTLN: "#c8aa78",
|
|
2474
|
+
OUTLW: "#c8aa78",
|
|
2475
|
+
BKAJ1: "#504830",
|
|
2476
|
+
TRFCD: "#6e006e",
|
|
2477
|
+
TRFCF: "#803880",
|
|
2478
|
+
RESBL: "#002a50",
|
|
2479
|
+
APTS1: "#6e006e",
|
|
2480
|
+
DNGHL: "#a00000",
|
|
2481
|
+
ISHDG: "#700000",
|
|
2482
|
+
LITRD: "#880000",
|
|
2483
|
+
LITGN: "#006600",
|
|
2484
|
+
LITYW: "#887800",
|
|
2485
|
+
LITWT: "#a09878",
|
|
2486
|
+
LITBL: "#003088",
|
|
2487
|
+
LITOR: "#884800",
|
|
2488
|
+
RADHI: "#005528",
|
|
2489
|
+
RADLO: "#1a3a20",
|
|
2490
|
+
PLRTE: "#6e2018",
|
|
2491
|
+
ARPAT: "#1a3028",
|
|
2492
|
+
ISDNG: "#481848",
|
|
2493
|
+
CHRED: "#682030",
|
|
2494
|
+
CHGRN: "#285020",
|
|
2495
|
+
CHYLW: "#685828",
|
|
2496
|
+
CHMGD: "#481848",
|
|
2497
|
+
CHMGF: "#482848",
|
|
2498
|
+
CHWHT: "#484840",
|
|
2499
|
+
CHCOR: "#683818",
|
|
2500
|
+
SNDG1: "#383028",
|
|
2501
|
+
SNDG2: "#c8aa78",
|
|
2502
|
+
DEPSC: "#383028",
|
|
2503
|
+
// ⚠ DELIBERATE DEVIATION from the published S-52 NIGHT value (#1f2223),
|
|
2504
|
+
// recorded here because `docs/validation/s52-spec-gap-matrix.json` tracks
|
|
2505
|
+
// this token as `value-differs` and the CHRTstyles programme is bringing
|
|
2506
|
+
// colours TO spec — this one moves further away, on purpose.
|
|
2507
|
+
//
|
|
2508
|
+
// Spec NIGHT DEPCN is near-black, which measures ~1.1:1 against this
|
|
2509
|
+
// palette's own deep water (#051a28). S-52's NIGHT table assumes a
|
|
2510
|
+
// dark-adapted eye in a darkened wheelhouse; on a phone or a laptop at
|
|
2511
|
+
// anchor it is not dim, it is absent — reported as "can't see bottom
|
|
2512
|
+
// contours in night mode only". A contour nobody can see fails the
|
|
2513
|
+
// standard's intent while matching its number.
|
|
2514
|
+
//
|
|
2515
|
+
// The palette already makes this trade elsewhere and in the same
|
|
2516
|
+
// direction: NIGHT's CHBLK/CSTLN/OUTLW are #c8aa78, light warm linework on
|
|
2517
|
+
// dark water. This value is that family, dimmed to stay subordinate to the
|
|
2518
|
+
// coastline. Revisit with the CHRTstyles programme, which owns the
|
|
2519
|
+
// spec-vs-legibility call across all 63 tokens.
|
|
2520
|
+
DEPCN: "#A68E84",
|
|
2521
|
+
UINFD: "#c8aa78",
|
|
2522
|
+
UINFF: "#383028",
|
|
2523
|
+
UINFR: "#682030",
|
|
2524
|
+
UINFG: "#285020",
|
|
2525
|
+
UINFO: "#683818",
|
|
2526
|
+
UINFB: "#182848",
|
|
2527
|
+
UINFM: "#481848",
|
|
2528
|
+
UIBCK: "#0a0a08",
|
|
2529
|
+
UIBDR: "#383028",
|
|
2530
|
+
UIAFD: "#182838",
|
|
2531
|
+
UIAFF: "#302818",
|
|
2532
|
+
PIPLN: "#284800",
|
|
2533
|
+
OUTLL: "#302818",
|
|
2534
|
+
SCLBR: "#683818",
|
|
2535
|
+
NINFO: "#683818",
|
|
2536
|
+
ADINF: "#483818",
|
|
2537
|
+
RESGR: "#383028",
|
|
2538
|
+
SHIPS: "#c8aa78",
|
|
2539
|
+
PSTRK: "#c8aa78",
|
|
2540
|
+
SYTRK: "#484030",
|
|
2541
|
+
APLRT: "#683818",
|
|
2542
|
+
CURSR: "#a00000",
|
|
2543
|
+
BKAJ2: "#0a0a08",
|
|
2544
|
+
RES01: "#383028",
|
|
2545
|
+
RES02: "#383028",
|
|
2546
|
+
RES03: "#383028"
|
|
2547
|
+
};
|
|
2548
|
+
var S52_COLORS = S52_DAY;
|
|
2549
|
+
var COLOR_TABLES = {
|
|
2550
|
+
DAY: S52_DAY,
|
|
2551
|
+
DUSK: S52_DUSK,
|
|
2552
|
+
NIGHT: S52_NIGHT
|
|
2553
|
+
};
|
|
2554
|
+
function getColorTable(name) {
|
|
2555
|
+
return COLOR_TABLES[name];
|
|
2556
|
+
}
|
|
2557
|
+
function resolveColor(token, table = "DAY") {
|
|
2558
|
+
const t = COLOR_TABLES[table];
|
|
2559
|
+
return t[token] ?? (table === "NIGHT" ? "#c8aa78" : "#000000");
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// src/interact/light-sigseq.ts
|
|
2563
|
+
var SIGSEQ_MAX_TERMS = 24;
|
|
2564
|
+
var SIGPER_TOLERANCE_SECONDS = 0.06;
|
|
2565
|
+
var LAMP_TICK_MS = 100;
|
|
2566
|
+
var TERM = /^\d+(?:\.\d+)?$/;
|
|
2567
|
+
function text(v) {
|
|
2568
|
+
if (v === null || v === void 0) return null;
|
|
2569
|
+
const s = String(v).trim();
|
|
2570
|
+
return s === "" ? null : s;
|
|
2571
|
+
}
|
|
2572
|
+
function parseSigseqTerms(sigseq, sigper) {
|
|
2573
|
+
const raw = text(sigseq);
|
|
2574
|
+
if (raw === null) return { refusal: "absent" };
|
|
2575
|
+
const terms = raw.split("+");
|
|
2576
|
+
if (terms.length === 0 || terms.length > SIGSEQ_MAX_TERMS) return { refusal: "malformed" };
|
|
2577
|
+
const phases = [];
|
|
2578
|
+
for (const term of terms) {
|
|
2579
|
+
const t = term.trim();
|
|
2580
|
+
if (t === "") return { refusal: "malformed" };
|
|
2581
|
+
const isEclipse = t.startsWith("(") && t.endsWith(")");
|
|
2582
|
+
const body = (isEclipse ? t.slice(1, -1) : t).trim();
|
|
2583
|
+
if (!TERM.test(body)) return { refusal: "malformed" };
|
|
2584
|
+
const seconds = Number(body);
|
|
2585
|
+
if (!(seconds > 0) || !Number.isFinite(seconds)) return { refusal: "nonPositiveDuration" };
|
|
2586
|
+
phases.push({ seconds, isOn: !isEclipse });
|
|
2587
|
+
}
|
|
2588
|
+
const sum = phases.reduce((t, p) => t + p.seconds, 0);
|
|
2589
|
+
if (!(sum > 0)) return { refusal: "nonPositiveDuration" };
|
|
2590
|
+
if (sigper !== null && sigper > 0) {
|
|
2591
|
+
if (Math.abs(sum - sigper) > SIGPER_TOLERANCE_SECONDS) return { refusal: "sigperMismatch" };
|
|
2592
|
+
return { phases, periodSeconds: sigper, checkedAgainstSigper: true };
|
|
2593
|
+
}
|
|
2594
|
+
return { phases, periodSeconds: sum, checkedAgainstSigper: false };
|
|
2595
|
+
}
|
|
2596
|
+
function rhythmProperties(parsed, onColoursHex) {
|
|
2597
|
+
if (parsed === null || parsed.phases.length === 0) return { rp: 0, rn: 0 };
|
|
2598
|
+
const out = { rp: parsed.periodSeconds, rn: parsed.phases.length };
|
|
2599
|
+
let end = 0;
|
|
2600
|
+
let onIndex = 0;
|
|
2601
|
+
parsed.phases.forEach((p, i) => {
|
|
2602
|
+
end += p.seconds;
|
|
2603
|
+
const k = i + 1;
|
|
2604
|
+
out[`r${k}`] = Math.round(end * 1e3) / 1e3;
|
|
2605
|
+
out[`o${k}`] = p.isOn ? 1 : 0;
|
|
2606
|
+
if (p.isOn) {
|
|
2607
|
+
out[`c${k}`] = onColoursHex[onIndex % Math.max(1, onColoursHex.length)] ?? "#ffffff";
|
|
2608
|
+
onIndex += 1;
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2611
|
+
return out;
|
|
2612
|
+
}
|
|
2613
|
+
function phaseExpr(nowSeconds2) {
|
|
2614
|
+
return ["%", nowSeconds2, ["max", ["get", "rp"], 1e-3]];
|
|
2615
|
+
}
|
|
2616
|
+
function lampRhythmOpacity(nowSeconds2, base) {
|
|
2617
|
+
const cases = ["case", ["<=", ["get", "rn"], 0], base];
|
|
2618
|
+
for (let k = 1; k <= SIGSEQ_MAX_TERMS; k++) {
|
|
2619
|
+
cases.push(["<", ["var", "ph"], ["coalesce", ["get", `r${k}`], 1e9]], ["*", base, ["coalesce", ["get", `o${k}`], 1]]);
|
|
2620
|
+
}
|
|
2621
|
+
cases.push(0);
|
|
2622
|
+
return ["let", "ph", phaseExpr(nowSeconds2), cases];
|
|
2623
|
+
}
|
|
2624
|
+
function lampRhythmColour(nowSeconds2, fallback) {
|
|
2625
|
+
const cases = ["case", ["<=", ["get", "rn"], 0], fallback];
|
|
2626
|
+
for (let k = 1; k <= SIGSEQ_MAX_TERMS; k++) {
|
|
2627
|
+
cases.push(["<", ["var", "ph"], ["coalesce", ["get", `r${k}`], 1e9]], ["coalesce", ["get", `c${k}`], fallback]);
|
|
2628
|
+
}
|
|
2629
|
+
cases.push(fallback);
|
|
2630
|
+
return ["let", "ph", phaseExpr(nowSeconds2), cases];
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
// src/interact/decode.ts
|
|
2634
|
+
function nameAbbrTable(acronym) {
|
|
2635
|
+
const names = s57NameTable(acronym);
|
|
2636
|
+
const abbrs = s57AbbrTable(acronym);
|
|
2637
|
+
const out = {};
|
|
2638
|
+
for (const [code, name] of Object.entries(names)) {
|
|
2639
|
+
const abbr = abbrs[Number(code)];
|
|
2640
|
+
if (abbr !== void 0) out[Number(code)] = { name, abbr };
|
|
2641
|
+
}
|
|
2642
|
+
return out;
|
|
2643
|
+
}
|
|
2644
|
+
var COLOUR_HEX = {
|
|
2645
|
+
1: "#ffffff",
|
|
2646
|
+
2: "#333333",
|
|
2647
|
+
3: "#ef4444",
|
|
2648
|
+
4: "#22c55e",
|
|
2649
|
+
5: "#3b82f6",
|
|
2650
|
+
6: "#eab308",
|
|
2651
|
+
7: "#9ca3af",
|
|
2652
|
+
8: "#92400e",
|
|
2653
|
+
9: "#f59e0b",
|
|
2654
|
+
10: "#8b5cf6",
|
|
2655
|
+
11: "#f97316",
|
|
2656
|
+
12: "#d946ef",
|
|
2657
|
+
13: "#f472b6"
|
|
2658
|
+
};
|
|
2659
|
+
var LITCHR_TABLE = nameAbbrTable("LITCHR");
|
|
2660
|
+
var COLOUR_TABLE = Object.fromEntries(
|
|
2661
|
+
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] }])
|
|
2662
|
+
);
|
|
2663
|
+
var BOYSHP_TABLE = s57NameTable("BOYSHP");
|
|
2664
|
+
var CATLAM_TABLE = s57NameTable("CATLAM");
|
|
2665
|
+
var CATCAM_TABLE = s57NameTable("CATCAM");
|
|
2666
|
+
var CATLMK_TABLE = s57NameTable("CATLMK");
|
|
2667
|
+
var CATFOG_TABLE = s57NameTable("CATFOG");
|
|
2668
|
+
var CATFOG_ABBR_TABLE = s57AbbrTable("CATFOG");
|
|
2669
|
+
var CATFOG_NAUTOPHONE = Object.values(CATFOG_CODES).find(
|
|
2670
|
+
(c) => c.name.toLowerCase() === "nautophone"
|
|
2671
|
+
)?.code;
|
|
2672
|
+
var CATLIT_TABLE = s57NameTable("CATLIT");
|
|
2673
|
+
var TOPSHP_TABLE = s57NameTable("TOPSHP");
|
|
2674
|
+
var STATUS_TABLE = s57NameTable("STATUS");
|
|
2675
|
+
var MARSYS_TABLE = s57NameTable("MARSYS");
|
|
2676
|
+
var LAYER_TYPE_MAP = {
|
|
2677
|
+
BOYLAT: "Lateral Buoy",
|
|
2678
|
+
BOYCAR: "Cardinal Buoy",
|
|
2679
|
+
BOYSPP: "Special Purpose Buoy",
|
|
2680
|
+
BOYISD: "Isolated Danger Buoy",
|
|
2681
|
+
BOYSAW: "Safe Water Buoy",
|
|
2682
|
+
BCNLAT: "Lateral Beacon",
|
|
2683
|
+
BCNCAR: "Cardinal Beacon",
|
|
2684
|
+
BCNSPP: "Special Purpose Beacon",
|
|
2685
|
+
BCNISD: "Isolated Danger Beacon",
|
|
2686
|
+
BCNSAW: "Safe Water Beacon",
|
|
2687
|
+
LIGHTS: "Light",
|
|
2688
|
+
DAYMAR: "Daymark",
|
|
2689
|
+
FOGSIG: "Fog Signal",
|
|
2690
|
+
RTPBCN: "Radar Transponder Beacon",
|
|
2691
|
+
RDOSTA: "Radio/Radar Station",
|
|
2692
|
+
TOPMAR: "Topmark",
|
|
2693
|
+
MORFAC: "Mooring Facility",
|
|
2694
|
+
LNDMRK: "Landmark",
|
|
2695
|
+
SISTAT: "Signal Station",
|
|
2696
|
+
PILBOP: "Pilot Boarding Place",
|
|
2697
|
+
HRBFAC: "Harbour Facility",
|
|
2698
|
+
SMCFAC: "Small Craft Facility",
|
|
2699
|
+
CRANES: "Crane",
|
|
2700
|
+
OFSPLF: "Offshore Platform",
|
|
2701
|
+
ACHBRT: "Anchor Berth",
|
|
2702
|
+
DISMAR: "Distance Mark",
|
|
2703
|
+
LITVES: "Light Vessel",
|
|
2704
|
+
LITFLT: "Light Float"
|
|
2705
|
+
};
|
|
2706
|
+
var NAV_AID_QUERY_LAYERS = [
|
|
2707
|
+
"enc-BOYLAT",
|
|
2708
|
+
"enc-BOYCAR",
|
|
2709
|
+
"enc-BOYSPP",
|
|
2710
|
+
"enc-BOYISD",
|
|
2711
|
+
"enc-BOYSAW",
|
|
2712
|
+
"enc-BCNLAT",
|
|
2713
|
+
"enc-BCNCAR",
|
|
2714
|
+
"enc-BCNSPP",
|
|
2715
|
+
"enc-BCNISD",
|
|
2716
|
+
"enc-BCNSAW",
|
|
2717
|
+
"enc-LIGHTS",
|
|
2718
|
+
"enc-LIGHTS-point",
|
|
2719
|
+
"enc-DAYMAR",
|
|
2720
|
+
"enc-FOGSIG",
|
|
2721
|
+
"enc-RTPBCN",
|
|
2722
|
+
"enc-RDOSTA",
|
|
2723
|
+
"enc-TOPMAR",
|
|
2724
|
+
"enc-MORFAC",
|
|
2725
|
+
"enc-MORFAC-point",
|
|
2726
|
+
"enc-LNDMRK",
|
|
2727
|
+
"enc-LNDMRK-point",
|
|
2728
|
+
"enc-SISTAT",
|
|
2729
|
+
"enc-SISTAT-point",
|
|
2730
|
+
"enc-PILBOP",
|
|
2731
|
+
"enc-PILBOP-point",
|
|
2732
|
+
"enc-HRBFAC",
|
|
2733
|
+
"enc-SMCFAC",
|
|
2734
|
+
"enc-CRANES",
|
|
2735
|
+
"enc-CRANES-point",
|
|
2736
|
+
"enc-OFSPLF",
|
|
2368
2737
|
"enc-OFSPLF-point",
|
|
2369
2738
|
"enc-ACHBRT",
|
|
2370
2739
|
"enc-DISMAR",
|
|
@@ -3167,14 +3536,8 @@ function decodeNavAidProps(layerName, props, coords) {
|
|
|
3167
3536
|
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3168
3537
|
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3169
3538
|
if (isSectorLight) {
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
sectorEnd: sectr2,
|
|
3173
|
-
colour: colourEntry?.name ?? "White",
|
|
3174
|
-
colourHex: colourEntry?.hex ?? "#ffffff",
|
|
3175
|
-
notation,
|
|
3176
|
-
nominalRange
|
|
3177
|
-
});
|
|
3539
|
+
const sec = sectorFromProps(props, sectr1, sectr2, "DAY", false);
|
|
3540
|
+
if (sec) sectors.push({ ...sec, notation });
|
|
3178
3541
|
}
|
|
3179
3542
|
return {
|
|
3180
3543
|
name,
|
|
@@ -3192,6 +3555,7 @@ function decodeNavAidProps(layerName, props, coords) {
|
|
|
3192
3555
|
lightCharacterFull: litchrEntry?.name ?? null,
|
|
3193
3556
|
period: sigper,
|
|
3194
3557
|
group,
|
|
3558
|
+
sigseq: props.SIGSEQ != null && String(props.SIGSEQ).trim() !== "" ? String(props.SIGSEQ).trim() : null,
|
|
3195
3559
|
fogSignal: fogSignal2,
|
|
3196
3560
|
fogSignalAbbr,
|
|
3197
3561
|
radarReflector,
|
|
@@ -3222,6 +3586,18 @@ function buildNotation(litchrAbbr, colourAbbrs, sigper, group) {
|
|
|
3222
3586
|
function buildFlashPattern(decoded) {
|
|
3223
3587
|
const litchr = decoded.lightCharacter;
|
|
3224
3588
|
if (!litchr) return null;
|
|
3589
|
+
const charted = parseSigseqTerms(decoded.sigseq, decoded.period);
|
|
3590
|
+
if (!("refusal" in charted)) {
|
|
3591
|
+
const colours2 = decoded.colourHexList.length > 0 ? decoded.colourHexList : [decoded.lightColourHex];
|
|
3592
|
+
let onIndex = 0;
|
|
3593
|
+
const chartedPhases = charted.phases.map((p) => {
|
|
3594
|
+
if (!p.isOn) return { on: false, duration: p.seconds * 1e3 };
|
|
3595
|
+
const color = colours2[onIndex % colours2.length];
|
|
3596
|
+
onIndex += 1;
|
|
3597
|
+
return { on: true, duration: p.seconds * 1e3, color };
|
|
3598
|
+
});
|
|
3599
|
+
return { phases: chartedPhases, totalCycle: charted.periodSeconds * 1e3, notation: decoded.notation, timing: "charted" };
|
|
3600
|
+
}
|
|
3225
3601
|
const period = decoded.period && decoded.period > 0 ? decoded.period * 1e3 : 6e3;
|
|
3226
3602
|
const groups = decoded.group ?? [1];
|
|
3227
3603
|
const totalFlashes = groups.reduce((a, b) => a + b, 0);
|
|
@@ -3390,7 +3766,7 @@ function buildFlashPattern(decoded) {
|
|
|
3390
3766
|
}
|
|
3391
3767
|
}
|
|
3392
3768
|
const totalCycle = phases.reduce((sum, p) => sum + p.duration, 0);
|
|
3393
|
-
return { phases, totalCycle, notation };
|
|
3769
|
+
return { timing: "invented", phases, totalCycle, notation };
|
|
3394
3770
|
}
|
|
3395
3771
|
function uncoveredSectorArcs(sectors) {
|
|
3396
3772
|
const norm = (d) => (d % 360 + 360) % 360;
|
|
@@ -3429,7 +3805,70 @@ function uncoveredSectorArcs(sectors) {
|
|
|
3429
3805
|
}
|
|
3430
3806
|
return gaps.filter(([a, b]) => Math.abs(b - a) >= 0.05 || b < a).map(([a, b]) => ({ start: norm(a), end: norm(b) }));
|
|
3431
3807
|
}
|
|
3432
|
-
function
|
|
3808
|
+
function parseCodes(val) {
|
|
3809
|
+
if (val == null) return [];
|
|
3810
|
+
const str = String(val).trim();
|
|
3811
|
+
let parts;
|
|
3812
|
+
if (str.startsWith("[")) {
|
|
3813
|
+
try {
|
|
3814
|
+
const arr = JSON.parse(str);
|
|
3815
|
+
parts = Array.isArray(arr) ? arr.map(String) : [];
|
|
3816
|
+
} catch {
|
|
3817
|
+
parts = str.split(/[,;]/);
|
|
3818
|
+
}
|
|
3819
|
+
} else {
|
|
3820
|
+
parts = str.split(/[,;]/);
|
|
3821
|
+
}
|
|
3822
|
+
return parts.map((p) => Number(p.trim())).filter((n) => !isNaN(n));
|
|
3823
|
+
}
|
|
3824
|
+
var OBSCURED_LITVIS = /* @__PURE__ */ new Set([3, 7, 8]);
|
|
3825
|
+
function sectorArcColourToken(colourCodes, obscured) {
|
|
3826
|
+
if (obscured) return "CHBRN";
|
|
3827
|
+
const has = (c) => colourCodes.includes(c);
|
|
3828
|
+
if (colourCodes.length === 1) {
|
|
3829
|
+
if (has(3)) return "LITRD";
|
|
3830
|
+
if (has(4)) return "LITGN";
|
|
3831
|
+
if (has(1) || has(6) || has(11)) return "LITYW";
|
|
3832
|
+
return "CHMGD";
|
|
3833
|
+
}
|
|
3834
|
+
if (colourCodes.length === 2) {
|
|
3835
|
+
if (has(1) && has(3)) return "LITRD";
|
|
3836
|
+
if (has(1) && has(4)) return "LITGN";
|
|
3837
|
+
return "CHMGD";
|
|
3838
|
+
}
|
|
3839
|
+
return "CHMGD";
|
|
3840
|
+
}
|
|
3841
|
+
function sectorFromProps(props, s1, s2, ct, allRound) {
|
|
3842
|
+
const colourCodes = parseCodes(props.COLOUR);
|
|
3843
|
+
const entries = colourCodes.map((c) => COLOUR_TABLE[c]).filter((e) => e != null);
|
|
3844
|
+
if (allRound && entries.length === 0) return null;
|
|
3845
|
+
const obscured = parseCodes(props.LITVIS).some((v) => OBSCURED_LITVIS.has(v));
|
|
3846
|
+
const litchrCode = parseCode(props.LITCHR);
|
|
3847
|
+
const le = litchrCode ? LITCHR_TABLE[litchrCode] : null;
|
|
3848
|
+
const sigper = props.SIGPER != null ? Number(props.SIGPER) : null;
|
|
3849
|
+
const group = parseGroup(props.SIGGRP);
|
|
3850
|
+
const base = buildNotation(le?.abbr ?? null, entries.length > 0 ? [entries.map((e) => e.abbr).join("")] : [], sigper, group);
|
|
3851
|
+
const notation = obscured && base ? `${base} (obsc)` : base;
|
|
3852
|
+
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3853
|
+
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3854
|
+
const arcColourToken = sectorArcColourToken(colourCodes, obscured);
|
|
3855
|
+
return {
|
|
3856
|
+
sectorStart: s1,
|
|
3857
|
+
sectorEnd: s2,
|
|
3858
|
+
colour: entries[0]?.name ?? "White",
|
|
3859
|
+
colourHex: entries[0]?.hex ?? "#ffffff",
|
|
3860
|
+
notation,
|
|
3861
|
+
nominalRange,
|
|
3862
|
+
colourCodes,
|
|
3863
|
+
arcColourToken,
|
|
3864
|
+
arcColourHex: resolveColor(arcColourToken, ct),
|
|
3865
|
+
obscured,
|
|
3866
|
+
sigseq: props.SIGSEQ != null && String(props.SIGSEQ).trim() !== "" ? String(props.SIGSEQ).trim() : null,
|
|
3867
|
+
sigper: sigper != null && !isNaN(sigper) && sigper > 0 ? sigper : null,
|
|
3868
|
+
...allRound ? { allRound: true } : {}
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
function buildSectorsFromFeatures(features, ct = "DAY") {
|
|
3433
3872
|
const sectors = [];
|
|
3434
3873
|
const allRound = [];
|
|
3435
3874
|
for (const f of features) {
|
|
@@ -3440,47 +3879,15 @@ function buildSectorsFromFeatures(features) {
|
|
|
3440
3879
|
if (props.SECTR1 == null && props.SECTR2 == null && parseCode(props.COLOUR) != null) allRound.push(props);
|
|
3441
3880
|
continue;
|
|
3442
3881
|
}
|
|
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
|
-
});
|
|
3882
|
+
const sec = sectorFromProps(props, s1, s2, ct, false);
|
|
3883
|
+
if (sec) sectors.push(sec);
|
|
3460
3884
|
}
|
|
3461
3885
|
if (sectors.length > 0 && allRound.length > 0) {
|
|
3462
3886
|
const gaps = uncoveredSectorArcs(sectors);
|
|
3463
3887
|
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
3888
|
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
|
-
});
|
|
3889
|
+
const sec = sectorFromProps(props, gap.start, gap.end, ct, true);
|
|
3890
|
+
if (sec) sectors.push(sec);
|
|
3484
3891
|
}
|
|
3485
3892
|
}
|
|
3486
3893
|
}
|
|
@@ -3530,12 +3937,16 @@ function buildSectorArcGeoJSON(center, startBearing, endBearing, radiusNm = 0.5,
|
|
|
3530
3937
|
}
|
|
3531
3938
|
function buildSectorArcsFC(center, sectors, radiusNm = 0.4, innerRadiusFrac = 0.92) {
|
|
3532
3939
|
const innerNm = innerRadiusFrac > 0 ? radiusNm * innerRadiusFrac : 0;
|
|
3533
|
-
const
|
|
3940
|
+
const drawn = sectors.filter((sec) => !sec.allRound);
|
|
3941
|
+
const features = drawn.map((sec, i) => {
|
|
3534
3942
|
const arc = buildSectorArcGeoJSON(center, sec.sectorStart, sec.sectorEnd, radiusNm, innerNm);
|
|
3535
3943
|
arc.properties = {
|
|
3536
3944
|
sectorIndex: i,
|
|
3537
3945
|
colour: sec.colour,
|
|
3538
3946
|
colourHex: sec.colourHex,
|
|
3947
|
+
arcColourHex: sec.arcColourHex,
|
|
3948
|
+
arcColourToken: sec.arcColourToken,
|
|
3949
|
+
obscured: sec.obscured,
|
|
3539
3950
|
notation: sec.notation,
|
|
3540
3951
|
sectorStart: sec.sectorStart,
|
|
3541
3952
|
sectorEnd: sec.sectorEnd,
|
|
@@ -3548,7 +3959,7 @@ function buildSectorArcsFC(center, sectors, radiusNm = 0.4, innerRadiusFrac = 0.
|
|
|
3548
3959
|
const seenBearings = /* @__PURE__ */ new Set();
|
|
3549
3960
|
const TICK_OUTER_OVERHANG = 1.05;
|
|
3550
3961
|
const TICK_INNER_OVERHANG = 0.93;
|
|
3551
|
-
for (const sec of
|
|
3962
|
+
for (const sec of drawn) {
|
|
3552
3963
|
for (const bearing of [sec.sectorStart, sec.sectorEnd]) {
|
|
3553
3964
|
const rad = sectorLimitToArcBearing(bearing) * Math.PI / 180;
|
|
3554
3965
|
const radiusDeg = radiusNm / 60;
|
|
@@ -4418,10 +4829,10 @@ function ensureStyles(doc) {
|
|
|
4418
4829
|
doc.head.appendChild(style);
|
|
4419
4830
|
styleInjected = true;
|
|
4420
4831
|
}
|
|
4421
|
-
function el(doc, tag, className,
|
|
4832
|
+
function el(doc, tag, className, text2) {
|
|
4422
4833
|
const node = doc.createElement(tag);
|
|
4423
4834
|
node.className = className;
|
|
4424
|
-
if (
|
|
4835
|
+
if (text2 != null) node.textContent = text2;
|
|
4425
4836
|
return node;
|
|
4426
4837
|
}
|
|
4427
4838
|
function animateDot(dot, pattern) {
|
|
@@ -4587,460 +4998,203 @@ ${notice.dataCurrency}`;
|
|
|
4587
4998
|
this.place();
|
|
4588
4999
|
this.map.on("move", this.reposition);
|
|
4589
5000
|
}
|
|
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"
|
|
5001
|
+
place() {
|
|
5002
|
+
if (!this.root || !this.lngLat) return;
|
|
5003
|
+
const { x, y } = this.map.project(this.lngLat);
|
|
5004
|
+
this.root.style.left = `${x}px`;
|
|
5005
|
+
this.root.style.top = `${y}px`;
|
|
5006
|
+
}
|
|
5007
|
+
close() {
|
|
5008
|
+
if (this.stopAnimation) {
|
|
5009
|
+
this.stopAnimation();
|
|
5010
|
+
this.stopAnimation = null;
|
|
5011
|
+
}
|
|
5012
|
+
if (this.root) {
|
|
5013
|
+
this.map.off("move", this.reposition);
|
|
5014
|
+
this.root.remove();
|
|
5015
|
+
this.root = null;
|
|
5016
|
+
}
|
|
5017
|
+
this.lngLat = null;
|
|
5018
|
+
}
|
|
5030
5019
|
};
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5020
|
+
|
|
5021
|
+
// src/interact/index.ts
|
|
5022
|
+
var InteractClient = class {
|
|
5023
|
+
constructor(http) {
|
|
5024
|
+
this.http = http;
|
|
5025
|
+
this.entitlementClient = new EntitlementClient(http);
|
|
5026
|
+
}
|
|
5027
|
+
entitlementClient;
|
|
5028
|
+
/**
|
|
5029
|
+
* Check the CHRTinteract entitlement for this API key (server-side, plan
|
|
5030
|
+
* derived, cached per client). Throws `InteractError` with code
|
|
5031
|
+
* `interact_not_enabled` for Free-plan teams.
|
|
5032
|
+
*/
|
|
5033
|
+
async entitlement() {
|
|
5034
|
+
return this.entitlementClient.check();
|
|
5035
|
+
}
|
|
5036
|
+
/**
|
|
5037
|
+
* Decode a chart feature (from `queryRenderedFeatures`, the features API,
|
|
5038
|
+
* or plain `{ layer, properties }`) into a typed {@link InspectResult}.
|
|
5039
|
+
*
|
|
5040
|
+
* The first call performs the server-side entitlement check. With
|
|
5041
|
+
* `{ enrich: true }` the result is upgraded with full S-57 properties and
|
|
5042
|
+
* source-cell provenance (edition / update / issue date) via the features
|
|
5043
|
+
* API — metered against the key's `featureCalls` quota.
|
|
5044
|
+
*
|
|
5045
|
+
* @example
|
|
5046
|
+
* const result = await chrt.interact.inspect(feature);
|
|
5047
|
+
* if (result.kind === "navaid") console.log(result.characteristic); // "Fl R 4s"
|
|
5048
|
+
*/
|
|
5049
|
+
async inspect(feature, opts) {
|
|
5050
|
+
await this.entitlementClient.check();
|
|
5051
|
+
const result = buildInspectResult(feature, opts);
|
|
5052
|
+
if (opts?.enrich) {
|
|
5053
|
+
return enrichResult(this.http, result, opts.signal);
|
|
5054
|
+
}
|
|
5055
|
+
return result;
|
|
5056
|
+
}
|
|
5057
|
+
/**
|
|
5058
|
+
* Collect EVERY inspectable feature under a point on a CHRT map —
|
|
5059
|
+
* deduplicated across tile boundaries by S-57 identity (`LNAM`) and
|
|
5060
|
+
* ordered by class priority (point targets > lines > areas, safety
|
|
5061
|
+
* classes first within each band). Area features never preempt the buoy
|
|
5062
|
+
* on top of them but always stay selectable: a click on open water
|
|
5063
|
+
* resolves to its DEPARE.
|
|
5064
|
+
*
|
|
5065
|
+
* `point` is a screen pixel point (`e.point` from a map click) or
|
|
5066
|
+
* `[lng, lat]`. Point/line targets are picked up within
|
|
5067
|
+
* `opts.tolerance` px (default 8); polygon candidates require the exact
|
|
5068
|
+
* point to fall inside them.
|
|
5069
|
+
*
|
|
5070
|
+
* The first call performs the server-side entitlement check.
|
|
5071
|
+
*
|
|
5072
|
+
* @example
|
|
5073
|
+
* const { candidates, select } = await chrt.interact.inspectAll(map, e.point);
|
|
5074
|
+
* const result = await select(candidates[0]); // full decode (+ merge)
|
|
5075
|
+
*/
|
|
5076
|
+
async inspectAll(map, point, opts = {}) {
|
|
5077
|
+
if (!map || typeof map.queryRenderedFeatures !== "function") {
|
|
5078
|
+
throw new InteractError(
|
|
5079
|
+
"unexpected_response",
|
|
5080
|
+
"inspectAll(): expected a MapLibre / Mapbox GL map instance"
|
|
5081
|
+
);
|
|
5082
|
+
}
|
|
5083
|
+
await this.entitlementClient.check();
|
|
5084
|
+
const screen = Array.isArray(point) ? map.project([point[0], point[1]]) : point;
|
|
5085
|
+
const lngLat = Array.isArray(point) ? { lng: point[0], lat: point[1] } : map.unproject?.([point.x, point.y]) ?? null;
|
|
5086
|
+
const at = { point: screen, lngLat };
|
|
5087
|
+
const candidates = collectClickCandidates(map, screen, opts);
|
|
5088
|
+
const http = this.http;
|
|
5089
|
+
return {
|
|
5090
|
+
candidates,
|
|
5091
|
+
select: async (candidate, selectOpts) => {
|
|
5092
|
+
const result = decodeCandidate(map, candidate, at, opts);
|
|
5093
|
+
if (selectOpts?.enrich) {
|
|
5094
|
+
return enrichResult(http, result, selectOpts.signal);
|
|
5095
|
+
}
|
|
5096
|
+
return result;
|
|
5097
|
+
}
|
|
5098
|
+
};
|
|
5099
|
+
}
|
|
5100
|
+
/**
|
|
5101
|
+
* Attach click-to-inspect to a MapLibre / Mapbox GL map rendering CHRT
|
|
5102
|
+
* tiles. Resolves once the server-side entitlement check passes; rejects
|
|
5103
|
+
* with `InteractError` (`interact_not_enabled` on Free plans) otherwise —
|
|
5104
|
+
* no listeners are wired on denial.
|
|
5105
|
+
*
|
|
5106
|
+
* Headless-first: pass `onInspect` to receive every decoded
|
|
5107
|
+
* {@link InspectResult} and render your own UI. Without callbacks, the
|
|
5108
|
+
* built-in minimal popup is shown (opt back in explicitly with
|
|
5109
|
+
* `popup: true` to have both).
|
|
5110
|
+
*
|
|
5111
|
+
* A click that hits SEVERAL features (deduplicated by S-57 identity)
|
|
5112
|
+
* disambiguates instead of silently taking the topmost:
|
|
5113
|
+
* - `onCandidates` provided → it receives the priority-ordered list;
|
|
5114
|
+
* resolve a choice with `ctx.select(candidate)`.
|
|
5115
|
+
* - built-in popup → shows a candidate list; picking a row opens the
|
|
5116
|
+
* result card.
|
|
5117
|
+
* - `onInspect` only → the top candidate is inspected immediately, and
|
|
5118
|
+
* clicking the SAME spot again cycles to the next candidate — so an
|
|
5119
|
+
* area feature under a buoy is reachable with a second click.
|
|
5120
|
+
*
|
|
5121
|
+
* @example
|
|
5122
|
+
* const handle = await chrt.interact.attach(map, {
|
|
5123
|
+
* onInspect: (result) => console.log(result.title, result.characteristic),
|
|
5124
|
+
* });
|
|
5125
|
+
* // later: handle.detach();
|
|
5126
|
+
*/
|
|
5127
|
+
async attach(map, opts = {}) {
|
|
5128
|
+
if (!map || typeof map.queryRenderedFeatures !== "function" || typeof map.on !== "function") {
|
|
5129
|
+
throw new InteractError(
|
|
5130
|
+
"unexpected_response",
|
|
5131
|
+
"attach(): expected a MapLibre / Mapbox GL map instance"
|
|
5132
|
+
);
|
|
5133
|
+
}
|
|
5134
|
+
await this.entitlementClient.check();
|
|
5135
|
+
const showPopup = opts.popup ?? (!opts.onInspect && !opts.onCandidates);
|
|
5136
|
+
const popup = showPopup ? new InteractPopup(map) : null;
|
|
5137
|
+
const http = this.http;
|
|
5138
|
+
let cycle = null;
|
|
5139
|
+
const samePoint = (a, b) => {
|
|
5140
|
+
const pad = opts.tolerance ?? DEFAULT_CLICK_TOLERANCE;
|
|
5141
|
+
return Math.abs(a.x - b.x) <= pad && Math.abs(a.y - b.y) <= pad;
|
|
5142
|
+
};
|
|
5143
|
+
const select = (candidate, e) => {
|
|
5144
|
+
const result = decodeCandidate(map, candidate, e, opts);
|
|
5145
|
+
const finish = (r) => {
|
|
5146
|
+
popup?.show(r, r.coordinates ?? [e.lngLat.lng, e.lngLat.lat]);
|
|
5147
|
+
opts.onInspect?.(r, { lngLat: e.lngLat, point: e.point, feature: candidate.feature });
|
|
5148
|
+
return r;
|
|
5149
|
+
};
|
|
5150
|
+
if (opts.enrich) {
|
|
5151
|
+
return enrichResult(http, result).then(finish).catch(() => finish(result));
|
|
5152
|
+
}
|
|
5153
|
+
return Promise.resolve(finish(result));
|
|
5154
|
+
};
|
|
5155
|
+
const clickHandler = (e) => {
|
|
5156
|
+
const candidates = collectClickCandidates(map, e.point, opts);
|
|
5157
|
+
if (candidates.length === 0) {
|
|
5158
|
+
cycle = null;
|
|
5159
|
+
popup?.close();
|
|
5160
|
+
opts.onMiss?.({ lngLat: e.lngLat });
|
|
5161
|
+
return;
|
|
5162
|
+
}
|
|
5163
|
+
if (candidates.length === 1) {
|
|
5164
|
+
cycle = null;
|
|
5165
|
+
void select(candidates[0], e);
|
|
5166
|
+
return;
|
|
5167
|
+
}
|
|
5168
|
+
if (opts.onCandidates || popup) {
|
|
5169
|
+
cycle = null;
|
|
5170
|
+
if (popup) {
|
|
5171
|
+
popup.showList(candidates, [e.lngLat.lng, e.lngLat.lat], (c) => void select(c, e));
|
|
5172
|
+
}
|
|
5173
|
+
opts.onCandidates?.(candidates, {
|
|
5174
|
+
lngLat: e.lngLat,
|
|
5175
|
+
point: e.point,
|
|
5176
|
+
select: (c) => select(c, e)
|
|
5177
|
+
});
|
|
5178
|
+
return;
|
|
5179
|
+
}
|
|
5180
|
+
const key = candidates.map((c) => c.identity ?? c.layerId).join("/");
|
|
5181
|
+
if (cycle && cycle.key === key && samePoint(cycle.point, e.point)) {
|
|
5182
|
+
cycle.index = (cycle.index + 1) % candidates.length;
|
|
5183
|
+
cycle.point = e.point;
|
|
5184
|
+
} else {
|
|
5185
|
+
cycle = { key, point: e.point, index: 0 };
|
|
5186
|
+
}
|
|
5187
|
+
void select(candidates[cycle.index], e);
|
|
5188
|
+
};
|
|
5189
|
+
map.on("click", clickHandler);
|
|
5190
|
+
return {
|
|
5191
|
+
detach: () => {
|
|
5192
|
+
map.off("click", clickHandler);
|
|
5193
|
+
popup?.close();
|
|
5194
|
+
}
|
|
5195
|
+
};
|
|
5196
|
+
}
|
|
5036
5197
|
};
|
|
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
5198
|
|
|
5045
5199
|
// src/atlas/s52-linestyles-generated.ts
|
|
5046
5200
|
var S52_LINESTYLES = {
|
|
@@ -27691,6 +27845,35 @@ function iconExpressionForFamily(objectClass, family) {
|
|
|
27691
27845
|
}
|
|
27692
27846
|
}
|
|
27693
27847
|
|
|
27848
|
+
// src/atlas/base-bundle.ts
|
|
27849
|
+
var BASE_BUNDLE_ID = BASE_REGION;
|
|
27850
|
+
var BASE_BUNDLE_SCHEMA = "chrt.basemap-bundle/1";
|
|
27851
|
+
var BASE_BUNDLE_MAXZOOM = 6;
|
|
27852
|
+
var BASE_BUNDLE_SOURCE_LAYERS = [
|
|
27853
|
+
"base_earth",
|
|
27854
|
+
"base_water",
|
|
27855
|
+
"base_boundary",
|
|
27856
|
+
"country_boundary"
|
|
27857
|
+
];
|
|
27858
|
+
var BASE_BUNDLE_ATTRIBUTION = "\xA9 CHRT \xB7 Made with Natural Earth";
|
|
27859
|
+
var BASE_CLIENT_RASTER_BEFORE_ID = "chrtbase-nocoverage";
|
|
27860
|
+
function applyBaseBundle(style, opts) {
|
|
27861
|
+
const keep = new Set(BASE_BUNDLE_SOURCE_LAYERS);
|
|
27862
|
+
style.layers = style.layers.filter((raw) => {
|
|
27863
|
+
const layer = raw;
|
|
27864
|
+
if (layer.source !== BASE_REGION) return true;
|
|
27865
|
+
return typeof layer["source-layer"] === "string" && keep.has(layer["source-layer"]);
|
|
27866
|
+
});
|
|
27867
|
+
style.sources[BASE_REGION] = {
|
|
27868
|
+
type: "vector",
|
|
27869
|
+
tiles: [opts.tiles],
|
|
27870
|
+
minzoom: BASE_MINZOOM,
|
|
27871
|
+
maxzoom: BASE_BUNDLE_MAXZOOM,
|
|
27872
|
+
attribution: BASE_BUNDLE_ATTRIBUTION
|
|
27873
|
+
};
|
|
27874
|
+
return style;
|
|
27875
|
+
}
|
|
27876
|
+
|
|
27694
27877
|
// src/atlas/layer-builder.ts
|
|
27695
27878
|
var SCAMIN_DECLUTTER_CLASSES = /* @__PURE__ */ new Set([
|
|
27696
27879
|
"OFSPLF",
|
|
@@ -29031,6 +29214,99 @@ function getOrderedClasses() {
|
|
|
29031
29214
|
return ordered;
|
|
29032
29215
|
}
|
|
29033
29216
|
|
|
29217
|
+
// src/atlas/terrain-order.ts
|
|
29218
|
+
var DRAPEABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
29219
|
+
"background",
|
|
29220
|
+
"fill",
|
|
29221
|
+
"line",
|
|
29222
|
+
"raster",
|
|
29223
|
+
"hillshade",
|
|
29224
|
+
"color-relief"
|
|
29225
|
+
]);
|
|
29226
|
+
function isDrapeable(layer) {
|
|
29227
|
+
return DRAPEABLE_LAYER_TYPES.has(layer.type);
|
|
29228
|
+
}
|
|
29229
|
+
function drapedFirst(layers) {
|
|
29230
|
+
const draped = [];
|
|
29231
|
+
const direct = [];
|
|
29232
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29233
|
+
return [...draped, ...direct];
|
|
29234
|
+
}
|
|
29235
|
+
function terrainStackCount(layers) {
|
|
29236
|
+
let stacks = 0;
|
|
29237
|
+
let prevDraped = false;
|
|
29238
|
+
for (const l of layers) {
|
|
29239
|
+
const d = isDrapeable(l);
|
|
29240
|
+
if (d && !prevDraped) stacks++;
|
|
29241
|
+
prevDraped = d;
|
|
29242
|
+
}
|
|
29243
|
+
return stacks;
|
|
29244
|
+
}
|
|
29245
|
+
function drapeHalves(layers) {
|
|
29246
|
+
const draped = [];
|
|
29247
|
+
const direct = [];
|
|
29248
|
+
for (const l of layers) (isDrapeable(l) ? draped : direct).push(l);
|
|
29249
|
+
return { draped, direct };
|
|
29250
|
+
}
|
|
29251
|
+
|
|
29252
|
+
// src/atlas/lamps.ts
|
|
29253
|
+
var LAMP_GLOW_LAYER_ID = "s52-LIGHTS-lamp-glow";
|
|
29254
|
+
var LAMP_CORE_LAYER_ID = "s52-LIGHTS-lamp-core";
|
|
29255
|
+
var LAMP_LAYER_IDS = [LAMP_GLOW_LAYER_ID, LAMP_CORE_LAYER_ID];
|
|
29256
|
+
var RANGE_NM = ["to-number", ["coalesce", ["get", "VALNMR"], ["get", "valnmr"]], 6];
|
|
29257
|
+
var UNSECTORED_POINTS = [
|
|
29258
|
+
"all",
|
|
29259
|
+
["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]],
|
|
29260
|
+
["!", ["has", "SECTR1"]],
|
|
29261
|
+
// …and not lights with a charted rhythm either: those flash, and the
|
|
29262
|
+
// flashing is computed by the app from `SIGSEQ` (light-sigseq.ts). What is
|
|
29263
|
+
// left here is exactly the set that is steady from every bearing.
|
|
29264
|
+
["!", ["has", "SIGSEQ"]]
|
|
29265
|
+
];
|
|
29266
|
+
function buildLampLayers(sourceId, ct) {
|
|
29267
|
+
const colour = lightFillExpression(ct);
|
|
29268
|
+
const common = {
|
|
29269
|
+
type: "circle",
|
|
29270
|
+
source: sourceId,
|
|
29271
|
+
"source-layer": "LIGHTS",
|
|
29272
|
+
filter: UNSECTORED_POINTS,
|
|
29273
|
+
layout: { visibility: "none" }
|
|
29274
|
+
};
|
|
29275
|
+
const metadata = { "chrt:role": "lamp", "s52:objectClass": "LIGHTS" };
|
|
29276
|
+
return [
|
|
29277
|
+
{
|
|
29278
|
+
...common,
|
|
29279
|
+
id: LAMP_GLOW_LAYER_ID,
|
|
29280
|
+
paint: {
|
|
29281
|
+
"circle-color": colour,
|
|
29282
|
+
// 4 nm → 7 px, 12 nm → 12 px, 25 nm → 20 px; capped, because a
|
|
29283
|
+
// primary seacoast light is not a searchlight.
|
|
29284
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 5, 4, 7, 12, 12, 25, 20],
|
|
29285
|
+
"circle-blur": 1,
|
|
29286
|
+
"circle-opacity": 0.55,
|
|
29287
|
+
// A lamp is a point of light: it does not grow as it comes near, and
|
|
29288
|
+
// it does not lie down with the chart.
|
|
29289
|
+
"circle-pitch-alignment": "viewport",
|
|
29290
|
+
"circle-pitch-scale": "viewport"
|
|
29291
|
+
},
|
|
29292
|
+
metadata: { ...metadata, "chrt:lamp": "glow" }
|
|
29293
|
+
},
|
|
29294
|
+
{
|
|
29295
|
+
...common,
|
|
29296
|
+
id: LAMP_CORE_LAYER_ID,
|
|
29297
|
+
paint: {
|
|
29298
|
+
"circle-color": colour,
|
|
29299
|
+
"circle-radius": ["interpolate", ["linear"], RANGE_NM, 0, 1.5, 12, 2.5, 25, 3.5],
|
|
29300
|
+
"circle-blur": 0.25,
|
|
29301
|
+
"circle-opacity": 1,
|
|
29302
|
+
"circle-pitch-alignment": "viewport",
|
|
29303
|
+
"circle-pitch-scale": "viewport"
|
|
29304
|
+
},
|
|
29305
|
+
metadata: { ...metadata, "chrt:lamp": "core" }
|
|
29306
|
+
}
|
|
29307
|
+
];
|
|
29308
|
+
}
|
|
29309
|
+
|
|
29034
29310
|
// src/atlas/band-priority.ts
|
|
29035
29311
|
var BAND_PRIORITY_RANGES = {
|
|
29036
29312
|
"1": { minzoom: 0 },
|
|
@@ -29066,6 +29342,17 @@ function isExemptOpaqueFill(layer) {
|
|
|
29066
29342
|
}
|
|
29067
29343
|
return false;
|
|
29068
29344
|
}
|
|
29345
|
+
var GROUND_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
29346
|
+
...DOMINANCE_EXEMPT_FILL_CLASSES,
|
|
29347
|
+
"RIVERS"
|
|
29348
|
+
]);
|
|
29349
|
+
function isGroundFill(layer) {
|
|
29350
|
+
if (layer.type !== "fill") return false;
|
|
29351
|
+
for (const cls of GROUND_FILL_CLASSES) {
|
|
29352
|
+
if (layer.id.startsWith(`s52-${cls}-`)) return true;
|
|
29353
|
+
}
|
|
29354
|
+
return false;
|
|
29355
|
+
}
|
|
29069
29356
|
var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
29070
29357
|
"1",
|
|
29071
29358
|
"2",
|
|
@@ -29073,10 +29360,10 @@ var FILL_FLOOR_FREE_BANDS = /* @__PURE__ */ new Set([
|
|
|
29073
29360
|
]);
|
|
29074
29361
|
function bandVariantMinzoom(layer, B) {
|
|
29075
29362
|
const classMinzoom = layer.minzoom ?? 0;
|
|
29076
|
-
if (
|
|
29077
|
-
return
|
|
29363
|
+
if (isGroundFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
29364
|
+
return 0;
|
|
29078
29365
|
}
|
|
29079
|
-
if (
|
|
29366
|
+
if (isGroundFill(layer)) {
|
|
29080
29367
|
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
29081
29368
|
}
|
|
29082
29369
|
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
@@ -29135,11 +29422,21 @@ function styleModeBand(mode) {
|
|
|
29135
29422
|
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
29136
29423
|
}
|
|
29137
29424
|
function bandFilterExpression(codes) {
|
|
29425
|
+
return cellBandMatch(["slice", ["get", "_cell_id"], 2, 3], codes);
|
|
29426
|
+
}
|
|
29427
|
+
var BAND_CODE_ALIASES = {
|
|
29428
|
+
"5": ["7"]
|
|
29429
|
+
};
|
|
29430
|
+
function cellBandCharacters(code) {
|
|
29431
|
+
return [code, ...BAND_CODE_ALIASES[code] ?? []];
|
|
29432
|
+
}
|
|
29433
|
+
function cellBandMatch(cellBandExpr, codes) {
|
|
29138
29434
|
const list = Array.isArray(codes) ? codes : [codes];
|
|
29139
|
-
|
|
29140
|
-
|
|
29435
|
+
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
29436
|
+
if (chars.length === 1) {
|
|
29437
|
+
return ["==", cellBandExpr, chars[0]];
|
|
29141
29438
|
}
|
|
29142
|
-
return ["in",
|
|
29439
|
+
return ["in", cellBandExpr, ["literal", chars]];
|
|
29143
29440
|
}
|
|
29144
29441
|
function combineWithBandFilter(baseFilter, bandFilter) {
|
|
29145
29442
|
if (!baseFilter) return bandFilter;
|
|
@@ -30223,7 +30520,8 @@ function fanOrientationLabelByCellBand(label, bandFan, objectClass) {
|
|
|
30223
30520
|
}
|
|
30224
30521
|
const cellFiltered = combineWithBandFilter(
|
|
30225
30522
|
label.filter,
|
|
30226
|
-
|
|
30523
|
+
// Aliases included (USACE `7` rides with band 5 — #269).
|
|
30524
|
+
cellBandMatch(cellBandExpr, code)
|
|
30227
30525
|
);
|
|
30228
30526
|
const minzoom = Math.max(
|
|
30229
30527
|
label.minzoom ?? range.minzoom,
|
|
@@ -30428,6 +30726,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30428
30726
|
(b) => !TOP_STACK_LIGHT_CLASSES.has(b.objectClass) && !BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30429
30727
|
);
|
|
30430
30728
|
const topLightBundles = bundles.filter((b) => TOP_STACK_LIGHT_CLASSES.has(b.objectClass));
|
|
30729
|
+
const lampLayers = buildLampLayers(sourceId, palette);
|
|
30431
30730
|
const waterBaseBundles = bundles.filter(
|
|
30432
30731
|
(b) => BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30433
30732
|
);
|
|
@@ -30461,6 +30760,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30461
30760
|
emitChartFeatureVariants(waterBaseBundles);
|
|
30462
30761
|
emitChartFeatureVariants(regularBundles);
|
|
30463
30762
|
emitChartFeatureVariants(topLightBundles);
|
|
30763
|
+
layers.push(...lampLayers);
|
|
30464
30764
|
const chartLabelBundles = byLabelPriorityBottomUp(
|
|
30465
30765
|
bundles.filter(
|
|
30466
30766
|
(b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
|
|
@@ -30538,6 +30838,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30538
30838
|
}
|
|
30539
30839
|
}
|
|
30540
30840
|
}
|
|
30841
|
+
layers.push(...lampLayers);
|
|
30541
30842
|
{
|
|
30542
30843
|
const TERMINAL_FILL_RE = new RegExp(
|
|
30543
30844
|
`^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
|
|
@@ -30626,13 +30927,18 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30626
30927
|
const glyphsBase = `${opts.tileBaseUrl ?? "https://tiles.chrt.co"}/v1/glyphs`;
|
|
30627
30928
|
const isLndareFill = (l) => /^s52-LNDARE-fill-band\d+$/.test(l.id);
|
|
30628
30929
|
const isWaterBaseFill = (l) => /^s52-(DEPARE|DRGARE|UNSARE)-fill-band\d+$/.test(l.id);
|
|
30930
|
+
const isLakareFill = (l) => /^s52-LAKARE-fill-band\d+$/.test(l.id);
|
|
30629
30931
|
const bandOf = (l) => {
|
|
30630
30932
|
const m = /-band(\d+)$/.exec(l.id);
|
|
30631
30933
|
return m ? Number(m[1]) : 0;
|
|
30632
30934
|
};
|
|
30633
30935
|
const emitted = opts.land ? (() => {
|
|
30634
|
-
const relocated = layers.filter(
|
|
30635
|
-
|
|
30936
|
+
const relocated = layers.filter(
|
|
30937
|
+
(l) => isLndareFill(l) || isWaterBaseFill(l) || isLakareFill(l)
|
|
30938
|
+
);
|
|
30939
|
+
const others = layers.filter(
|
|
30940
|
+
(l) => !isLndareFill(l) && !isWaterBaseFill(l) && !isLakareFill(l)
|
|
30941
|
+
);
|
|
30636
30942
|
const isSeaareFill = (l) => /^s52-SEAARE-fill-band\d+$/.test(l.id);
|
|
30637
30943
|
const seaare = others.filter(isSeaareFill);
|
|
30638
30944
|
const rest = others.filter((l) => !isSeaareFill(l));
|
|
@@ -30656,40 +30962,43 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30656
30962
|
for (const b of bands) {
|
|
30657
30963
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isWaterBaseFill(l)));
|
|
30658
30964
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLndareFill(l)).map(asBaseTan));
|
|
30965
|
+
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLakareFill(l)));
|
|
30659
30966
|
}
|
|
30660
30967
|
const findRest = (id) => restAbove.findIndex((l) => l.id === id);
|
|
30661
|
-
const
|
|
30662
|
-
const pairAt = shoreAt !== -1 ? shoreAt : findRest("chrtbase-inland-hatch");
|
|
30968
|
+
const pairAt = findRest("chrtbase-inland-water");
|
|
30663
30969
|
let restFinal = restAbove;
|
|
30664
30970
|
if (pairAt !== -1) {
|
|
30665
30971
|
const repaint = [];
|
|
30666
|
-
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
|
|
30972
|
+
const pushCascadeClones = (l, b) => {
|
|
30973
|
+
const src = l;
|
|
30974
|
+
const meta = { ...src.metadata ?? {}, "chrt:role": "charted-water-repaint" };
|
|
30975
|
+
const notDom = {
|
|
30976
|
+
...l,
|
|
30977
|
+
id: `${src.id}-overpair`,
|
|
30978
|
+
filter: ["all", src.filter, NOT_DOMINATED_FILTER],
|
|
30979
|
+
metadata: meta
|
|
30980
|
+
};
|
|
30981
|
+
delete notDom.maxzoom;
|
|
30982
|
+
delete notDom.minzoom;
|
|
30983
|
+
repaint.push(notDom);
|
|
30984
|
+
for (const D of BAND_DRAW_ORDER) {
|
|
30985
|
+
if (parseInt(D, 10) <= b) continue;
|
|
30986
|
+
const dMaxzoom = dominatedVariantMaxzoom(D);
|
|
30987
|
+
if (dMaxzoom <= 0) continue;
|
|
30988
|
+
const domVariant = {
|
|
30671
30989
|
...l,
|
|
30672
|
-
id: `${src.id}-overpair`,
|
|
30673
|
-
filter: ["all", src.filter,
|
|
30674
|
-
metadata: meta
|
|
30990
|
+
id: `${src.id}-overpair-domBy${D}`,
|
|
30991
|
+
filter: ["all", src.filter, dominatedByMinFilter(D)],
|
|
30992
|
+
metadata: meta,
|
|
30993
|
+
maxzoom: dMaxzoom
|
|
30675
30994
|
};
|
|
30676
|
-
delete
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
|
|
30683
|
-
const domVariant = {
|
|
30684
|
-
...l,
|
|
30685
|
-
id: `${src.id}-overpair-domBy${D}`,
|
|
30686
|
-
filter: ["all", src.filter, dominatedByMinFilter(D)],
|
|
30687
|
-
metadata: meta,
|
|
30688
|
-
maxzoom: dMaxzoom
|
|
30689
|
-
};
|
|
30690
|
-
delete domVariant.minzoom;
|
|
30691
|
-
repaint.push(domVariant);
|
|
30692
|
-
}
|
|
30995
|
+
delete domVariant.minzoom;
|
|
30996
|
+
repaint.push(domVariant);
|
|
30997
|
+
}
|
|
30998
|
+
};
|
|
30999
|
+
for (const b of bands) {
|
|
31000
|
+
for (const l of relocated.filter((x) => bandOf(x) === b && isWaterBaseFill(x))) {
|
|
31001
|
+
pushCascadeClones(l, b);
|
|
30693
31002
|
}
|
|
30694
31003
|
if (parseInt(b, 10) >= FINE_LAND_REPAINT_MIN_BAND) {
|
|
30695
31004
|
for (const l of relocated.filter(
|
|
@@ -30705,6 +31014,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30705
31014
|
repaint.push(landClone);
|
|
30706
31015
|
}
|
|
30707
31016
|
}
|
|
31017
|
+
for (const l of relocated.filter((x) => bandOf(x) === b && isLakareFill(x))) {
|
|
31018
|
+
pushCascadeClones(l, b);
|
|
31019
|
+
}
|
|
30708
31020
|
}
|
|
30709
31021
|
const isCoalneLine = (l) => /^s52-COALNE-line-band\d(-domBy\d)?$/.test(l.id);
|
|
30710
31022
|
for (const l of layers.filter(isCoalneLine)) {
|
|
@@ -30728,9 +31040,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30728
31040
|
repaint.push(terminal);
|
|
30729
31041
|
}
|
|
30730
31042
|
restFinal = [
|
|
30731
|
-
...restAbove.slice(0, pairAt
|
|
31043
|
+
...restAbove.slice(0, pairAt),
|
|
30732
31044
|
...repaint,
|
|
30733
|
-
...restAbove.slice(pairAt
|
|
31045
|
+
...restAbove.slice(pairAt)
|
|
30734
31046
|
];
|
|
30735
31047
|
}
|
|
30736
31048
|
{
|
|
@@ -30779,6 +31091,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30779
31091
|
emitted.push(layer);
|
|
30780
31092
|
}
|
|
30781
31093
|
}
|
|
31094
|
+
const finalLayers = drapedFirst(emitted);
|
|
30782
31095
|
const style = {
|
|
30783
31096
|
version: 8,
|
|
30784
31097
|
name: `CHRT ${SYMBOL_SET_LABELS[symbolSet]} ${PALETTE_NAMES[palette]}${opts.chartMode ? ` \u2014 chart:${opts.chartMode.band}` : ""}`,
|
|
@@ -30787,7 +31100,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30787
31100
|
"chrt:symbol-set": symbolSet,
|
|
30788
31101
|
"chrt:color-table": palette,
|
|
30789
31102
|
"chrt:generated-at": (/* @__PURE__ */ new Date()).toISOString(),
|
|
30790
|
-
"chrt:layer-count":
|
|
31103
|
+
"chrt:layer-count": finalLayers.length,
|
|
30791
31104
|
"chrt:land": Boolean(opts.land),
|
|
30792
31105
|
"chrt:relief": Boolean(opts.relief),
|
|
30793
31106
|
"chrt:base": Boolean(opts.base),
|
|
@@ -30827,7 +31140,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30827
31140
|
// upstream glyph byte-identical and composite only those six in from
|
|
30828
31141
|
// Noto Sans; see scripts/generate-glyphs.mjs.
|
|
30829
31142
|
glyphs: `${glyphsBase}/{fontstack}/{range}.pbf`,
|
|
30830
|
-
layers:
|
|
31143
|
+
layers: finalLayers
|
|
30831
31144
|
};
|
|
30832
31145
|
if (symbolSet === "s101") return applyS101SymbolSet(style);
|
|
30833
31146
|
if (symbolSet === "chrt") return applyChrtSymbolSet(style, palette);
|
|
@@ -31656,8 +31969,16 @@ var Chrt = class {
|
|
|
31656
31969
|
ALL_WATER_BODY_LAYERS,
|
|
31657
31970
|
AREA_QUERY_LAYERS,
|
|
31658
31971
|
ATON_EXPRESSION_CLASSES,
|
|
31972
|
+
BAND_CODE_ALIASES,
|
|
31659
31973
|
BAND_DRAW_ORDER,
|
|
31660
31974
|
BAND_PRIORITY_RANGES,
|
|
31975
|
+
BASE_ATTRIBUTION,
|
|
31976
|
+
BASE_BUNDLE_ATTRIBUTION,
|
|
31977
|
+
BASE_BUNDLE_ID,
|
|
31978
|
+
BASE_BUNDLE_MAXZOOM,
|
|
31979
|
+
BASE_BUNDLE_SCHEMA,
|
|
31980
|
+
BASE_BUNDLE_SOURCE_LAYERS,
|
|
31981
|
+
BASE_CLIENT_RASTER_BEFORE_ID,
|
|
31661
31982
|
BASE_MAXZOOM,
|
|
31662
31983
|
BASE_MINZOOM,
|
|
31663
31984
|
BASE_REGION,
|
|
@@ -31690,6 +32011,7 @@ var Chrt = class {
|
|
|
31690
32011
|
DEFAULT_TILE_BASE_URL,
|
|
31691
32012
|
DEFAULT_TILE_REGION,
|
|
31692
32013
|
DEFAULT_WATER_BODY_SETTINGS,
|
|
32014
|
+
DRAPEABLE_LAYER_TYPES,
|
|
31693
32015
|
INSPECT_CLASS_RANK,
|
|
31694
32016
|
INSPECT_RANK_DEVIATIONS,
|
|
31695
32017
|
InteractClient,
|
|
@@ -31698,6 +32020,11 @@ var Chrt = class {
|
|
|
31698
32020
|
LABEL_BEHAVIOR,
|
|
31699
32021
|
LABEL_LAYER_GROUPS,
|
|
31700
32022
|
LABEL_PRIORITY,
|
|
32023
|
+
LAMP_CORE_LAYER_ID,
|
|
32024
|
+
LAMP_GLOW_LAYER_ID,
|
|
32025
|
+
LAMP_LAYER_IDS,
|
|
32026
|
+
LAMP_TICK_MS,
|
|
32027
|
+
LAND_ATTRIBUTION,
|
|
31701
32028
|
LAND_BUILD_MAXZOOM,
|
|
31702
32029
|
LAND_REGION,
|
|
31703
32030
|
NAUTICAL_SYMBOLS,
|
|
@@ -31713,7 +32040,10 @@ var Chrt = class {
|
|
|
31713
32040
|
RASTER_ATON_CLASSES,
|
|
31714
32041
|
RASTER_GLYPH_NAMES,
|
|
31715
32042
|
RASTER_ICON_PREFIX,
|
|
32043
|
+
RELIEF_ATTRIBUTION,
|
|
32044
|
+
RELIEF_ENCODING,
|
|
31716
32045
|
RELIEF_REGION,
|
|
32046
|
+
RELIEF_TILE_SIZE,
|
|
31717
32047
|
S101_AREA_FILL_NAMES,
|
|
31718
32048
|
S101_CHART_SECTIONS,
|
|
31719
32049
|
S101_FEATURES,
|
|
@@ -31733,6 +32063,8 @@ var Chrt = class {
|
|
|
31733
32063
|
S52_LOOKUP,
|
|
31734
32064
|
S52_NIGHT,
|
|
31735
32065
|
S52_PATTERN_SPRITE_PREFIX,
|
|
32066
|
+
SIGPER_TOLERANCE_SECONDS,
|
|
32067
|
+
SIGSEQ_MAX_TERMS,
|
|
31736
32068
|
SOUNDING_LAYERS,
|
|
31737
32069
|
SYMBOL_FAMILIES,
|
|
31738
32070
|
SYMBOL_FAMILY_META,
|
|
@@ -31742,6 +32074,7 @@ var Chrt = class {
|
|
|
31742
32074
|
TileTokenError,
|
|
31743
32075
|
WATER_BODY_LAYER_GROUPS,
|
|
31744
32076
|
WeatherEndpoint,
|
|
32077
|
+
applyBaseBundle,
|
|
31745
32078
|
applyChrtSymbolSet,
|
|
31746
32079
|
applyColorTheme,
|
|
31747
32080
|
applyCustomStyleSettings,
|
|
@@ -31754,6 +32087,7 @@ var Chrt = class {
|
|
|
31754
32087
|
buildFlashPattern,
|
|
31755
32088
|
buildLabelLayer,
|
|
31756
32089
|
buildLakeTierExpression,
|
|
32090
|
+
buildLampLayers,
|
|
31757
32091
|
buildLandAreaTierExpression,
|
|
31758
32092
|
buildLandRegionTierExpression,
|
|
31759
32093
|
buildLayersForClass,
|
|
@@ -31766,6 +32100,8 @@ var Chrt = class {
|
|
|
31766
32100
|
buildSectorsFromFeatures,
|
|
31767
32101
|
buildSources,
|
|
31768
32102
|
candidateRank,
|
|
32103
|
+
cellBandCharacters,
|
|
32104
|
+
cellBandMatch,
|
|
31769
32105
|
chrtAtonTintExpression,
|
|
31770
32106
|
chrtLightTintExpression,
|
|
31771
32107
|
collectCandidates,
|
|
@@ -31780,6 +32116,8 @@ var Chrt = class {
|
|
|
31780
32116
|
depareColor,
|
|
31781
32117
|
depareFillExpression,
|
|
31782
32118
|
deriveMarinerName,
|
|
32119
|
+
drapeHalves,
|
|
32120
|
+
drapedFirst,
|
|
31783
32121
|
extractNavAidDesignation,
|
|
31784
32122
|
featureIdentity,
|
|
31785
32123
|
generateCustomStyle,
|
|
@@ -31812,6 +32150,7 @@ var Chrt = class {
|
|
|
31812
32150
|
hazardColor,
|
|
31813
32151
|
hazardFillExpression,
|
|
31814
32152
|
iconExpressionForFamily,
|
|
32153
|
+
isDrapeable,
|
|
31815
32154
|
isLabelLayerVisible,
|
|
31816
32155
|
isNavAidLayer,
|
|
31817
32156
|
isNavAidLayerVisible,
|
|
@@ -31819,6 +32158,8 @@ var Chrt = class {
|
|
|
31819
32158
|
isVisibleInMode,
|
|
31820
32159
|
isWaterBodyLayerVisible,
|
|
31821
32160
|
labelSortKey,
|
|
32161
|
+
lampRhythmColour,
|
|
32162
|
+
lampRhythmOpacity,
|
|
31822
32163
|
lateralColor,
|
|
31823
32164
|
lateralFillExpression,
|
|
31824
32165
|
lightColor,
|
|
@@ -31828,6 +32169,7 @@ var Chrt = class {
|
|
|
31828
32169
|
normalizeTheme,
|
|
31829
32170
|
paginate,
|
|
31830
32171
|
parseListCodes,
|
|
32172
|
+
parseSigseqTerms,
|
|
31831
32173
|
parseSymbolFamily,
|
|
31832
32174
|
parseWebhook,
|
|
31833
32175
|
patternSpriteId,
|
|
@@ -31842,12 +32184,14 @@ var Chrt = class {
|
|
|
31842
32184
|
resolveS101Color,
|
|
31843
32185
|
resolveStyle,
|
|
31844
32186
|
resolveTargetClass,
|
|
32187
|
+
rhythmProperties,
|
|
31845
32188
|
s101ArtworkProvenance,
|
|
31846
32189
|
sanitizeCustomStyleConfig,
|
|
31847
32190
|
scaleSizeValue,
|
|
31848
32191
|
scaminToMinZoom,
|
|
31849
32192
|
sectorLimitToArcBearing,
|
|
31850
32193
|
styleModeBand,
|
|
32194
|
+
terrainStackCount,
|
|
31851
32195
|
tileTemplate,
|
|
31852
32196
|
uncoveredSectorArcs,
|
|
31853
32197
|
withinBboxFilter,
|