@chrtco/sdk 0.10.39 → 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 +987 -654
- 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 +956 -654
- 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) {
|
|
@@ -4581,466 +4992,209 @@ ${notice.dataCurrency}`;
|
|
|
4581
4992
|
if (position === "static" || position === "") {
|
|
4582
4993
|
container.style.position = "relative";
|
|
4583
4994
|
}
|
|
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"
|
|
4995
|
+
container.appendChild(root);
|
|
4996
|
+
this.root = root;
|
|
4997
|
+
this.lngLat = lngLat;
|
|
4998
|
+
this.place();
|
|
4999
|
+
this.map.on("move", this.reposition);
|
|
5000
|
+
}
|
|
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 },
|
|
@@ -29146,11 +29422,21 @@ function styleModeBand(mode) {
|
|
|
29146
29422
|
return mode === "continuous" ? null : mode.slice("chart:".length);
|
|
29147
29423
|
}
|
|
29148
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) {
|
|
29149
29434
|
const list = Array.isArray(codes) ? codes : [codes];
|
|
29150
|
-
|
|
29151
|
-
|
|
29435
|
+
const chars = [...new Set(list.flatMap((c) => cellBandCharacters(c)))];
|
|
29436
|
+
if (chars.length === 1) {
|
|
29437
|
+
return ["==", cellBandExpr, chars[0]];
|
|
29152
29438
|
}
|
|
29153
|
-
return ["in",
|
|
29439
|
+
return ["in", cellBandExpr, ["literal", chars]];
|
|
29154
29440
|
}
|
|
29155
29441
|
function combineWithBandFilter(baseFilter, bandFilter) {
|
|
29156
29442
|
if (!baseFilter) return bandFilter;
|
|
@@ -30234,7 +30520,8 @@ function fanOrientationLabelByCellBand(label, bandFan, objectClass) {
|
|
|
30234
30520
|
}
|
|
30235
30521
|
const cellFiltered = combineWithBandFilter(
|
|
30236
30522
|
label.filter,
|
|
30237
|
-
|
|
30523
|
+
// Aliases included (USACE `7` rides with band 5 — #269).
|
|
30524
|
+
cellBandMatch(cellBandExpr, code)
|
|
30238
30525
|
);
|
|
30239
30526
|
const minzoom = Math.max(
|
|
30240
30527
|
label.minzoom ?? range.minzoom,
|
|
@@ -30439,6 +30726,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30439
30726
|
(b) => !TOP_STACK_LIGHT_CLASSES.has(b.objectClass) && !BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30440
30727
|
);
|
|
30441
30728
|
const topLightBundles = bundles.filter((b) => TOP_STACK_LIGHT_CLASSES.has(b.objectClass));
|
|
30729
|
+
const lampLayers = buildLampLayers(sourceId, palette);
|
|
30442
30730
|
const waterBaseBundles = bundles.filter(
|
|
30443
30731
|
(b) => BOTTOM_STACK_WATER_BASE_CLASSES.has(b.objectClass)
|
|
30444
30732
|
);
|
|
@@ -30472,6 +30760,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30472
30760
|
emitChartFeatureVariants(waterBaseBundles);
|
|
30473
30761
|
emitChartFeatureVariants(regularBundles);
|
|
30474
30762
|
emitChartFeatureVariants(topLightBundles);
|
|
30763
|
+
layers.push(...lampLayers);
|
|
30475
30764
|
const chartLabelBundles = byLabelPriorityBottomUp(
|
|
30476
30765
|
bundles.filter(
|
|
30477
30766
|
(b) => b.label !== null && (opts.labels !== "placename" || getLabelBehavior(b.objectClass) !== "orientation-cross-band")
|
|
@@ -30549,6 +30838,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30549
30838
|
}
|
|
30550
30839
|
}
|
|
30551
30840
|
}
|
|
30841
|
+
layers.push(...lampLayers);
|
|
30552
30842
|
{
|
|
30553
30843
|
const TERMINAL_FILL_RE = new RegExp(
|
|
30554
30844
|
`^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
|
|
@@ -30637,13 +30927,18 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30637
30927
|
const glyphsBase = `${opts.tileBaseUrl ?? "https://tiles.chrt.co"}/v1/glyphs`;
|
|
30638
30928
|
const isLndareFill = (l) => /^s52-LNDARE-fill-band\d+$/.test(l.id);
|
|
30639
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);
|
|
30640
30931
|
const bandOf = (l) => {
|
|
30641
30932
|
const m = /-band(\d+)$/.exec(l.id);
|
|
30642
30933
|
return m ? Number(m[1]) : 0;
|
|
30643
30934
|
};
|
|
30644
30935
|
const emitted = opts.land ? (() => {
|
|
30645
|
-
const relocated = layers.filter(
|
|
30646
|
-
|
|
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
|
+
);
|
|
30647
30942
|
const isSeaareFill = (l) => /^s52-SEAARE-fill-band\d+$/.test(l.id);
|
|
30648
30943
|
const seaare = others.filter(isSeaareFill);
|
|
30649
30944
|
const rest = others.filter((l) => !isSeaareFill(l));
|
|
@@ -30667,40 +30962,43 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30667
30962
|
for (const b of bands) {
|
|
30668
30963
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isWaterBaseFill(l)));
|
|
30669
30964
|
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLndareFill(l)).map(asBaseTan));
|
|
30965
|
+
ordered.push(...relocated.filter((l) => bandOf(l) === b && isLakareFill(l)));
|
|
30670
30966
|
}
|
|
30671
30967
|
const findRest = (id) => restAbove.findIndex((l) => l.id === id);
|
|
30672
|
-
const
|
|
30673
|
-
const pairAt = shoreAt !== -1 ? shoreAt : findRest("chrtbase-inland-hatch");
|
|
30968
|
+
const pairAt = findRest("chrtbase-inland-water");
|
|
30674
30969
|
let restFinal = restAbove;
|
|
30675
30970
|
if (pairAt !== -1) {
|
|
30676
30971
|
const repaint = [];
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
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 = {
|
|
30682
30989
|
...l,
|
|
30683
|
-
id: `${src.id}-overpair`,
|
|
30684
|
-
filter: ["all", src.filter,
|
|
30685
|
-
metadata: meta
|
|
30990
|
+
id: `${src.id}-overpair-domBy${D}`,
|
|
30991
|
+
filter: ["all", src.filter, dominatedByMinFilter(D)],
|
|
30992
|
+
metadata: meta,
|
|
30993
|
+
maxzoom: dMaxzoom
|
|
30686
30994
|
};
|
|
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
|
-
}
|
|
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);
|
|
30704
31002
|
}
|
|
30705
31003
|
if (parseInt(b, 10) >= FINE_LAND_REPAINT_MIN_BAND) {
|
|
30706
31004
|
for (const l of relocated.filter(
|
|
@@ -30716,6 +31014,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30716
31014
|
repaint.push(landClone);
|
|
30717
31015
|
}
|
|
30718
31016
|
}
|
|
31017
|
+
for (const l of relocated.filter((x) => bandOf(x) === b && isLakareFill(x))) {
|
|
31018
|
+
pushCascadeClones(l, b);
|
|
31019
|
+
}
|
|
30719
31020
|
}
|
|
30720
31021
|
const isCoalneLine = (l) => /^s52-COALNE-line-band\d(-domBy\d)?$/.test(l.id);
|
|
30721
31022
|
for (const l of layers.filter(isCoalneLine)) {
|
|
@@ -30739,9 +31040,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30739
31040
|
repaint.push(terminal);
|
|
30740
31041
|
}
|
|
30741
31042
|
restFinal = [
|
|
30742
|
-
...restAbove.slice(0, pairAt
|
|
31043
|
+
...restAbove.slice(0, pairAt),
|
|
30743
31044
|
...repaint,
|
|
30744
|
-
...restAbove.slice(pairAt
|
|
31045
|
+
...restAbove.slice(pairAt)
|
|
30745
31046
|
];
|
|
30746
31047
|
}
|
|
30747
31048
|
{
|
|
@@ -30790,6 +31091,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30790
31091
|
emitted.push(layer);
|
|
30791
31092
|
}
|
|
30792
31093
|
}
|
|
31094
|
+
const finalLayers = drapedFirst(emitted);
|
|
30793
31095
|
const style = {
|
|
30794
31096
|
version: 8,
|
|
30795
31097
|
name: `CHRT ${SYMBOL_SET_LABELS[symbolSet]} ${PALETTE_NAMES[palette]}${opts.chartMode ? ` \u2014 chart:${opts.chartMode.band}` : ""}`,
|
|
@@ -30798,7 +31100,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30798
31100
|
"chrt:symbol-set": symbolSet,
|
|
30799
31101
|
"chrt:color-table": palette,
|
|
30800
31102
|
"chrt:generated-at": (/* @__PURE__ */ new Date()).toISOString(),
|
|
30801
|
-
"chrt:layer-count":
|
|
31103
|
+
"chrt:layer-count": finalLayers.length,
|
|
30802
31104
|
"chrt:land": Boolean(opts.land),
|
|
30803
31105
|
"chrt:relief": Boolean(opts.relief),
|
|
30804
31106
|
"chrt:base": Boolean(opts.base),
|
|
@@ -30838,7 +31140,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30838
31140
|
// upstream glyph byte-identical and composite only those six in from
|
|
30839
31141
|
// Noto Sans; see scripts/generate-glyphs.mjs.
|
|
30840
31142
|
glyphs: `${glyphsBase}/{fontstack}/{range}.pbf`,
|
|
30841
|
-
layers:
|
|
31143
|
+
layers: finalLayers
|
|
30842
31144
|
};
|
|
30843
31145
|
if (symbolSet === "s101") return applyS101SymbolSet(style);
|
|
30844
31146
|
if (symbolSet === "chrt") return applyChrtSymbolSet(style, palette);
|
|
@@ -31667,8 +31969,16 @@ var Chrt = class {
|
|
|
31667
31969
|
ALL_WATER_BODY_LAYERS,
|
|
31668
31970
|
AREA_QUERY_LAYERS,
|
|
31669
31971
|
ATON_EXPRESSION_CLASSES,
|
|
31972
|
+
BAND_CODE_ALIASES,
|
|
31670
31973
|
BAND_DRAW_ORDER,
|
|
31671
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,
|
|
31672
31982
|
BASE_MAXZOOM,
|
|
31673
31983
|
BASE_MINZOOM,
|
|
31674
31984
|
BASE_REGION,
|
|
@@ -31701,6 +32011,7 @@ var Chrt = class {
|
|
|
31701
32011
|
DEFAULT_TILE_BASE_URL,
|
|
31702
32012
|
DEFAULT_TILE_REGION,
|
|
31703
32013
|
DEFAULT_WATER_BODY_SETTINGS,
|
|
32014
|
+
DRAPEABLE_LAYER_TYPES,
|
|
31704
32015
|
INSPECT_CLASS_RANK,
|
|
31705
32016
|
INSPECT_RANK_DEVIATIONS,
|
|
31706
32017
|
InteractClient,
|
|
@@ -31709,6 +32020,11 @@ var Chrt = class {
|
|
|
31709
32020
|
LABEL_BEHAVIOR,
|
|
31710
32021
|
LABEL_LAYER_GROUPS,
|
|
31711
32022
|
LABEL_PRIORITY,
|
|
32023
|
+
LAMP_CORE_LAYER_ID,
|
|
32024
|
+
LAMP_GLOW_LAYER_ID,
|
|
32025
|
+
LAMP_LAYER_IDS,
|
|
32026
|
+
LAMP_TICK_MS,
|
|
32027
|
+
LAND_ATTRIBUTION,
|
|
31712
32028
|
LAND_BUILD_MAXZOOM,
|
|
31713
32029
|
LAND_REGION,
|
|
31714
32030
|
NAUTICAL_SYMBOLS,
|
|
@@ -31724,7 +32040,10 @@ var Chrt = class {
|
|
|
31724
32040
|
RASTER_ATON_CLASSES,
|
|
31725
32041
|
RASTER_GLYPH_NAMES,
|
|
31726
32042
|
RASTER_ICON_PREFIX,
|
|
32043
|
+
RELIEF_ATTRIBUTION,
|
|
32044
|
+
RELIEF_ENCODING,
|
|
31727
32045
|
RELIEF_REGION,
|
|
32046
|
+
RELIEF_TILE_SIZE,
|
|
31728
32047
|
S101_AREA_FILL_NAMES,
|
|
31729
32048
|
S101_CHART_SECTIONS,
|
|
31730
32049
|
S101_FEATURES,
|
|
@@ -31744,6 +32063,8 @@ var Chrt = class {
|
|
|
31744
32063
|
S52_LOOKUP,
|
|
31745
32064
|
S52_NIGHT,
|
|
31746
32065
|
S52_PATTERN_SPRITE_PREFIX,
|
|
32066
|
+
SIGPER_TOLERANCE_SECONDS,
|
|
32067
|
+
SIGSEQ_MAX_TERMS,
|
|
31747
32068
|
SOUNDING_LAYERS,
|
|
31748
32069
|
SYMBOL_FAMILIES,
|
|
31749
32070
|
SYMBOL_FAMILY_META,
|
|
@@ -31753,6 +32074,7 @@ var Chrt = class {
|
|
|
31753
32074
|
TileTokenError,
|
|
31754
32075
|
WATER_BODY_LAYER_GROUPS,
|
|
31755
32076
|
WeatherEndpoint,
|
|
32077
|
+
applyBaseBundle,
|
|
31756
32078
|
applyChrtSymbolSet,
|
|
31757
32079
|
applyColorTheme,
|
|
31758
32080
|
applyCustomStyleSettings,
|
|
@@ -31765,6 +32087,7 @@ var Chrt = class {
|
|
|
31765
32087
|
buildFlashPattern,
|
|
31766
32088
|
buildLabelLayer,
|
|
31767
32089
|
buildLakeTierExpression,
|
|
32090
|
+
buildLampLayers,
|
|
31768
32091
|
buildLandAreaTierExpression,
|
|
31769
32092
|
buildLandRegionTierExpression,
|
|
31770
32093
|
buildLayersForClass,
|
|
@@ -31777,6 +32100,8 @@ var Chrt = class {
|
|
|
31777
32100
|
buildSectorsFromFeatures,
|
|
31778
32101
|
buildSources,
|
|
31779
32102
|
candidateRank,
|
|
32103
|
+
cellBandCharacters,
|
|
32104
|
+
cellBandMatch,
|
|
31780
32105
|
chrtAtonTintExpression,
|
|
31781
32106
|
chrtLightTintExpression,
|
|
31782
32107
|
collectCandidates,
|
|
@@ -31791,6 +32116,8 @@ var Chrt = class {
|
|
|
31791
32116
|
depareColor,
|
|
31792
32117
|
depareFillExpression,
|
|
31793
32118
|
deriveMarinerName,
|
|
32119
|
+
drapeHalves,
|
|
32120
|
+
drapedFirst,
|
|
31794
32121
|
extractNavAidDesignation,
|
|
31795
32122
|
featureIdentity,
|
|
31796
32123
|
generateCustomStyle,
|
|
@@ -31823,6 +32150,7 @@ var Chrt = class {
|
|
|
31823
32150
|
hazardColor,
|
|
31824
32151
|
hazardFillExpression,
|
|
31825
32152
|
iconExpressionForFamily,
|
|
32153
|
+
isDrapeable,
|
|
31826
32154
|
isLabelLayerVisible,
|
|
31827
32155
|
isNavAidLayer,
|
|
31828
32156
|
isNavAidLayerVisible,
|
|
@@ -31830,6 +32158,8 @@ var Chrt = class {
|
|
|
31830
32158
|
isVisibleInMode,
|
|
31831
32159
|
isWaterBodyLayerVisible,
|
|
31832
32160
|
labelSortKey,
|
|
32161
|
+
lampRhythmColour,
|
|
32162
|
+
lampRhythmOpacity,
|
|
31833
32163
|
lateralColor,
|
|
31834
32164
|
lateralFillExpression,
|
|
31835
32165
|
lightColor,
|
|
@@ -31839,6 +32169,7 @@ var Chrt = class {
|
|
|
31839
32169
|
normalizeTheme,
|
|
31840
32170
|
paginate,
|
|
31841
32171
|
parseListCodes,
|
|
32172
|
+
parseSigseqTerms,
|
|
31842
32173
|
parseSymbolFamily,
|
|
31843
32174
|
parseWebhook,
|
|
31844
32175
|
patternSpriteId,
|
|
@@ -31853,12 +32184,14 @@ var Chrt = class {
|
|
|
31853
32184
|
resolveS101Color,
|
|
31854
32185
|
resolveStyle,
|
|
31855
32186
|
resolveTargetClass,
|
|
32187
|
+
rhythmProperties,
|
|
31856
32188
|
s101ArtworkProvenance,
|
|
31857
32189
|
sanitizeCustomStyleConfig,
|
|
31858
32190
|
scaleSizeValue,
|
|
31859
32191
|
scaminToMinZoom,
|
|
31860
32192
|
sectorLimitToArcBearing,
|
|
31861
32193
|
styleModeBand,
|
|
32194
|
+
terrainStackCount,
|
|
31862
32195
|
tileTemplate,
|
|
31863
32196
|
uncoveredSectorArcs,
|
|
31864
32197
|
withinBboxFilter,
|