@chrtco/sdk 0.7.3 → 0.7.5
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/dist/atlas/browser.cjs.map +1 -1
- package/dist/atlas/browser.js.map +1 -1
- package/dist/index.cjs +1029 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1029 -188
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1412,6 +1412,32 @@ var S52_LOOKUP = {
|
|
|
1412
1412
|
viewingGroup: 13050,
|
|
1413
1413
|
scamin: 45e3
|
|
1414
1414
|
},
|
|
1415
|
+
// Unsurveyed area — a polygon where no reliable hydrographic survey
|
|
1416
|
+
// exists. The IHO S-52 PresLib portrayal is the hatched pattern
|
|
1417
|
+
// UNSURV03 (diagonal lines over a faint grey wash); we don't carry
|
|
1418
|
+
// that sprite, so we lean on the existing OPAQUE_FILLS treatment in
|
|
1419
|
+
// `layer-builder.ts` to force a fully-opaque grey fill that visibly
|
|
1420
|
+
// suppresses anything underneath. The "no data here — don't assume
|
|
1421
|
+
// safe water" signal is the entire point; UNSARE rendering is a BASE
|
|
1422
|
+
// display-category obligation, not an optional cue.
|
|
1423
|
+
//
|
|
1424
|
+
// Note: `UNSARE` is already a member of `OPAQUE_FILLS` and
|
|
1425
|
+
// `OPAQUE_FILL_LAYERS` in `layer-builder.ts` and `style-resolver.ts`
|
|
1426
|
+
// — half-wired before this entry landed. Adding the lookup entry +
|
|
1427
|
+
// draw-order slot completes the wire-up. Surfaced by the
|
|
1428
|
+
// `Audit S-57 Layer Coverage (Weekly)` workflow (issue #575); 5
|
|
1429
|
+
// features in the NOAA corpus, all in remote-Alaska coverage gaps
|
|
1430
|
+
// where survey is genuinely absent.
|
|
1431
|
+
UNSARE: {
|
|
1432
|
+
objectClass: "UNSARE",
|
|
1433
|
+
name: "Unsurveyed Area",
|
|
1434
|
+
category: "Depths",
|
|
1435
|
+
displayCategory: "BASE",
|
|
1436
|
+
int1Ref: "I27",
|
|
1437
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 1 } },
|
|
1438
|
+
viewingGroup: 13050,
|
|
1439
|
+
scamin: 0
|
|
1440
|
+
},
|
|
1415
1441
|
// ── Land & Shoreline (INT1 Section C) ───────────────────
|
|
1416
1442
|
LNDARE: {
|
|
1417
1443
|
objectClass: "LNDARE",
|
|
@@ -1564,6 +1590,51 @@ var S52_LOOKUP = {
|
|
|
1564
1590
|
viewingGroup: 12050,
|
|
1565
1591
|
scamin: 45e3
|
|
1566
1592
|
},
|
|
1593
|
+
// Rapids — direct navigation hazard in narrows / river constrictions. S-52
|
|
1594
|
+
// INT1 C24 portrayal: a stylised wavy line representing turbulent water.
|
|
1595
|
+
// We use a blue dashed line (DEPVS) so the feature reads as a water
|
|
1596
|
+
// disturbance — same colour family as the depth tints, distinct from the
|
|
1597
|
+
// black coastal-construction line family. Surfaced by the weekly layer-
|
|
1598
|
+
// coverage audit (issue #575); 4 features in the NOAA corpus, all in
|
|
1599
|
+
// freshwater approaches where a small craft could be swept onto rocks.
|
|
1600
|
+
RAPIDS: {
|
|
1601
|
+
objectClass: "RAPIDS",
|
|
1602
|
+
name: "Rapids",
|
|
1603
|
+
category: "Hazards",
|
|
1604
|
+
displayCategory: "STANDARD",
|
|
1605
|
+
int1Ref: "C24",
|
|
1606
|
+
style: { type: "line", paint: { "line-color": "DEPVS", "line-width": 1, "line-dasharray": [3, 2] } },
|
|
1607
|
+
viewingGroup: 24020,
|
|
1608
|
+
scamin: 45e3
|
|
1609
|
+
},
|
|
1610
|
+
// Dyke — physical earth/stone embankment that confines water. Same
|
|
1611
|
+
// black-line family as SLCONS / FNCLNE / CAUSWY: solid CHBLK so the
|
|
1612
|
+
// structure reads as a hard man-made shoreline modification. INT1 F8.
|
|
1613
|
+
// Surfaced by the weekly layer-coverage audit (issue #575); 13 features
|
|
1614
|
+
// in the NOAA corpus.
|
|
1615
|
+
DYKCON: {
|
|
1616
|
+
objectClass: "DYKCON",
|
|
1617
|
+
name: "Dyke",
|
|
1618
|
+
category: "Land",
|
|
1619
|
+
displayCategory: "STANDARD",
|
|
1620
|
+
int1Ref: "F8",
|
|
1621
|
+
style: { type: "line", paint: { "line-color": "CHBLK", "line-width": 1 } },
|
|
1622
|
+
viewingGroup: 12410,
|
|
1623
|
+
scamin: 22e3
|
|
1624
|
+
},
|
|
1625
|
+
// Causeway — raised road or rail bed across tidal flats or shallow water
|
|
1626
|
+
// (Cape Cod, the Florida Keys, etc.). Black-line family alongside DYKCON.
|
|
1627
|
+
// INT1 F3. Surfaced by the weekly layer-coverage audit (issue #575).
|
|
1628
|
+
CAUSWY: {
|
|
1629
|
+
objectClass: "CAUSWY",
|
|
1630
|
+
name: "Causeway",
|
|
1631
|
+
category: "Infrastructure",
|
|
1632
|
+
displayCategory: "STANDARD",
|
|
1633
|
+
int1Ref: "F3",
|
|
1634
|
+
style: { type: "line", paint: { "line-color": "CHBLK", "line-width": 1 } },
|
|
1635
|
+
viewingGroup: 12410,
|
|
1636
|
+
scamin: 22e3
|
|
1637
|
+
},
|
|
1567
1638
|
TUNNEL: {
|
|
1568
1639
|
objectClass: "TUNNEL",
|
|
1569
1640
|
name: "Tunnel",
|
|
@@ -1761,6 +1832,13 @@ var S52_LOOKUP = {
|
|
|
1761
1832
|
scamin: 9e4
|
|
1762
1833
|
},
|
|
1763
1834
|
LNDELV: {
|
|
1835
|
+
// LineString variant (land contours) renders normally with the line
|
|
1836
|
+
// style below. Point variant ≈ spot elevation ("12m") on land — per
|
|
1837
|
+
// S-52 PresLib convention, marine displays suppress land spot heights
|
|
1838
|
+
// so they don't compete with safety-critical hydrography. The
|
|
1839
|
+
// `auditIgnore` flag silences the weekly layer-coverage audit for the
|
|
1840
|
+
// Point geom-type only; it does not disable the line rendering. See
|
|
1841
|
+
// #575 triage matrix for the full rationale.
|
|
1764
1842
|
objectClass: "LNDELV",
|
|
1765
1843
|
name: "Land Elevation",
|
|
1766
1844
|
category: "Land",
|
|
@@ -1768,7 +1846,8 @@ var S52_LOOKUP = {
|
|
|
1768
1846
|
int1Ref: "C10",
|
|
1769
1847
|
style: { type: "line", paint: { "line-color": "CHBRN", "line-width": 0.5 } },
|
|
1770
1848
|
viewingGroup: 12070,
|
|
1771
|
-
scamin: 22e3
|
|
1849
|
+
scamin: 22e3,
|
|
1850
|
+
auditIgnore: true
|
|
1772
1851
|
},
|
|
1773
1852
|
// ── Hazards (INT1 Section K) ────────────────────────────
|
|
1774
1853
|
OBSTRN: {
|
|
@@ -1854,7 +1933,15 @@ var S52_LOOKUP = {
|
|
|
1854
1933
|
int1Ref: "J1",
|
|
1855
1934
|
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0.15 } },
|
|
1856
1935
|
viewingGroup: 14010,
|
|
1857
|
-
scamin: 22e3
|
|
1936
|
+
scamin: 22e3,
|
|
1937
|
+
// Point variant (NOAA encodes 2,709 SBDARE Points) is rendered as a
|
|
1938
|
+
// seabed-quality text label ("S"/"M"/"R" per S-52 INT1 J) via the
|
|
1939
|
+
// labels.ts path — see layer-builder.ts SBDARE special-case routing.
|
|
1940
|
+
// The layer-coverage audit script only inspects MapLibre layer filters
|
|
1941
|
+
// from buildLayersForClass(), so it can't see that the Point variant
|
|
1942
|
+
// is in fact rendered. Matches the SOUNDG precedent (auditIgnore +
|
|
1943
|
+
// text-via-labels.ts rendering — see s52-lookup.ts SOUNDG entry).
|
|
1944
|
+
auditIgnore: true
|
|
1858
1945
|
},
|
|
1859
1946
|
WEDKLP: {
|
|
1860
1947
|
// Translucent green fill suggests kelp/vegetation; Point variant (the
|
|
@@ -1871,6 +1958,23 @@ var S52_LOOKUP = {
|
|
|
1871
1958
|
viewingGroup: 14010,
|
|
1872
1959
|
scamin: 22e3
|
|
1873
1960
|
},
|
|
1961
|
+
// Oil barrier — a deployed containment boom corralling spilled oil or
|
|
1962
|
+
// marking the perimeter of a spill-response operation. Operational
|
|
1963
|
+
// hazard: small craft must avoid crossing the line (and the polluted
|
|
1964
|
+
// water it contains). S-52 has no formal INT1 entry, but PresLib
|
|
1965
|
+
// convention for spill-response gear is magenta — matches the rest of
|
|
1966
|
+
// the regulated/operational-area portrayal family. Dashed line so it
|
|
1967
|
+
// reads as a temporary/operational marker rather than a permanent
|
|
1968
|
+
// structure. Surfaced by the weekly layer-coverage audit (issue #575).
|
|
1969
|
+
OILBAR: {
|
|
1970
|
+
objectClass: "OILBAR",
|
|
1971
|
+
name: "Oil Barrier",
|
|
1972
|
+
category: "Hazards",
|
|
1973
|
+
displayCategory: "STANDARD",
|
|
1974
|
+
style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 1, "line-dasharray": [4, 2] } },
|
|
1975
|
+
viewingGroup: 24020,
|
|
1976
|
+
scamin: 45e3
|
|
1977
|
+
},
|
|
1874
1978
|
// ── Regulated Areas (INT1 Section N) ────────────────────
|
|
1875
1979
|
ACHARE: {
|
|
1876
1980
|
// Anchorages are conventionally rendered as a subtle filled region in
|
|
@@ -2044,6 +2148,196 @@ var S52_LOOKUP = {
|
|
|
2044
2148
|
viewingGroup: 26050,
|
|
2045
2149
|
scamin: 9e4
|
|
2046
2150
|
},
|
|
2151
|
+
// ── Jurisdictional zones (INT1 Section N) ───────────────
|
|
2152
|
+
// ADMARE / COSARE / CONZNE / EXEZNE / TESARE share an S-52 portrayal
|
|
2153
|
+
// family: magenta dashed boundary, no fill. These are vast offshore
|
|
2154
|
+
// polygons (EEZ extends 200nm, contiguous zone 24nm) where a flood fill
|
|
2155
|
+
// — even at 0.05 opacity — would visibly tint half the chart and stack
|
|
2156
|
+
// with overlapping jurisdictions into a purple wash. Same boundary-only
|
|
2157
|
+
// pattern as TESARE / RESARE / CTNARE via LINE_FROM_POLYGON_LAYERS.
|
|
2158
|
+
// Surfaced by the weekly layer-coverage audit (issue #575 rows 8–15).
|
|
2159
|
+
ADMARE: {
|
|
2160
|
+
// Administration area — state/national waters, the broadest
|
|
2161
|
+
// jurisdictional polygon class. INT1 N40 maps it into the same
|
|
2162
|
+
// magenta-dashed family as territorial-sea / EEZ / contiguous-zone.
|
|
2163
|
+
objectClass: "ADMARE",
|
|
2164
|
+
name: "Administration Area",
|
|
2165
|
+
category: "Regulated Areas",
|
|
2166
|
+
displayCategory: "STANDARD",
|
|
2167
|
+
int1Ref: "N40",
|
|
2168
|
+
style: {
|
|
2169
|
+
type: "line",
|
|
2170
|
+
paint: {
|
|
2171
|
+
"line-color": "CHMGD",
|
|
2172
|
+
"line-width": 1,
|
|
2173
|
+
"line-dasharray": [6, 4],
|
|
2174
|
+
"line-opacity": 0.6
|
|
2175
|
+
}
|
|
2176
|
+
},
|
|
2177
|
+
viewingGroup: 26010,
|
|
2178
|
+
scamin: 9e4
|
|
2179
|
+
},
|
|
2180
|
+
COSARE: {
|
|
2181
|
+
// Coastal area — jurisdictional polygon delineating a coastal-state
|
|
2182
|
+
// administrative region. Previously suppressed via META_LAYERS in
|
|
2183
|
+
// style-generator.ts; promoted here to render as a magenta dashed
|
|
2184
|
+
// boundary alongside the rest of the N-section jurisdiction family.
|
|
2185
|
+
objectClass: "COSARE",
|
|
2186
|
+
name: "Coastal Area",
|
|
2187
|
+
category: "Regulated Areas",
|
|
2188
|
+
displayCategory: "STANDARD",
|
|
2189
|
+
int1Ref: "N40",
|
|
2190
|
+
style: {
|
|
2191
|
+
type: "line",
|
|
2192
|
+
paint: {
|
|
2193
|
+
"line-color": "CHMGD",
|
|
2194
|
+
"line-width": 1,
|
|
2195
|
+
"line-dasharray": [6, 4],
|
|
2196
|
+
"line-opacity": 0.6
|
|
2197
|
+
}
|
|
2198
|
+
},
|
|
2199
|
+
viewingGroup: 26010,
|
|
2200
|
+
scamin: 9e4
|
|
2201
|
+
},
|
|
2202
|
+
CONZNE: {
|
|
2203
|
+
// Contiguous Zone — 24nm offshore boundary. Same magenta dashed
|
|
2204
|
+
// portrayal as territorial sea; boundary-only because the polygon is
|
|
2205
|
+
// vast and a flood fill would obscure huge swathes of chart data.
|
|
2206
|
+
objectClass: "CONZNE",
|
|
2207
|
+
name: "Contiguous Zone",
|
|
2208
|
+
category: "Regulated Areas",
|
|
2209
|
+
displayCategory: "STANDARD",
|
|
2210
|
+
int1Ref: "N44",
|
|
2211
|
+
style: {
|
|
2212
|
+
type: "line",
|
|
2213
|
+
paint: {
|
|
2214
|
+
"line-color": "CHMGD",
|
|
2215
|
+
"line-width": 1,
|
|
2216
|
+
"line-dasharray": [6, 4],
|
|
2217
|
+
"line-opacity": 0.6
|
|
2218
|
+
}
|
|
2219
|
+
},
|
|
2220
|
+
viewingGroup: 26010,
|
|
2221
|
+
scamin: 9e4
|
|
2222
|
+
},
|
|
2223
|
+
EXEZNE: {
|
|
2224
|
+
// Exclusive Economic Zone — 200nm offshore boundary. Largest of the
|
|
2225
|
+
// jurisdictional polygons; flood-filling even at 0.05 opacity would
|
|
2226
|
+
// tint half the chart at coastal zooms. Boundary-only is the only
|
|
2227
|
+
// sensible portrayal at scale.
|
|
2228
|
+
objectClass: "EXEZNE",
|
|
2229
|
+
name: "Exclusive Economic Zone",
|
|
2230
|
+
category: "Regulated Areas",
|
|
2231
|
+
displayCategory: "STANDARD",
|
|
2232
|
+
int1Ref: "N47",
|
|
2233
|
+
style: {
|
|
2234
|
+
type: "line",
|
|
2235
|
+
paint: {
|
|
2236
|
+
"line-color": "CHMGD",
|
|
2237
|
+
"line-width": 1,
|
|
2238
|
+
"line-dasharray": [6, 4],
|
|
2239
|
+
"line-opacity": 0.6
|
|
2240
|
+
}
|
|
2241
|
+
},
|
|
2242
|
+
viewingGroup: 26010,
|
|
2243
|
+
scamin: 9e4
|
|
2244
|
+
},
|
|
2245
|
+
// ── IMO routing measures (INT1 Section M) ───────────────
|
|
2246
|
+
// TSEZNE / DWRTPT / DWRTCL / TWRTPT are IMO-adopted routing measures
|
|
2247
|
+
// (Traffic Separation Scheme zones, deepwater routes, two-way routes).
|
|
2248
|
+
// S-52 portrayal: magenta dashed boundary for polygons, dashed centerline
|
|
2249
|
+
// for the LineString. The INT1 M-section spec calls for additional
|
|
2250
|
+
// diagonal hatching (TSSCRS01 sprite) on TSEZNE and "DW" centroid text
|
|
2251
|
+
// on DWRTPT — both are deferred follow-ups (TSSCRS01 not in the atlas
|
|
2252
|
+
// sprite set; centroid text belongs in labels.ts). Boundary visibility
|
|
2253
|
+
// is the priority cue here; routing-text augmentation comes later.
|
|
2254
|
+
TSEZNE: {
|
|
2255
|
+
// Traffic Separation Zone — the buffer band between opposing lanes of
|
|
2256
|
+
// a TSS. S-52 INT1 M14 calls for T-pattern diagonal hatching
|
|
2257
|
+
// (TSSCRS01) but that sprite isn't in our generated atlas; fall back
|
|
2258
|
+
// to a magenta dashed boundary so the zone is at least visible.
|
|
2259
|
+
objectClass: "TSEZNE",
|
|
2260
|
+
name: "Traffic Separation Zone",
|
|
2261
|
+
category: "Regulated Areas",
|
|
2262
|
+
displayCategory: "STANDARD",
|
|
2263
|
+
int1Ref: "M14",
|
|
2264
|
+
style: {
|
|
2265
|
+
type: "line",
|
|
2266
|
+
paint: {
|
|
2267
|
+
"line-color": "CHMGD",
|
|
2268
|
+
"line-width": 1.2,
|
|
2269
|
+
"line-dasharray": [4, 3],
|
|
2270
|
+
"line-opacity": 0.7
|
|
2271
|
+
}
|
|
2272
|
+
},
|
|
2273
|
+
viewingGroup: 26010,
|
|
2274
|
+
scamin: 9e4
|
|
2275
|
+
},
|
|
2276
|
+
DWRTPT: {
|
|
2277
|
+
// Deepwater Route Part — polygon segment of an IMO-adopted deepwater
|
|
2278
|
+
// route. S-52 INT1 M27 portrayal includes "DW" centroid text; that's
|
|
2279
|
+
// a labels.ts addition deferred to a follow-up. Boundary-only here.
|
|
2280
|
+
objectClass: "DWRTPT",
|
|
2281
|
+
name: "Deepwater Route Part",
|
|
2282
|
+
category: "Regulated Areas",
|
|
2283
|
+
displayCategory: "STANDARD",
|
|
2284
|
+
int1Ref: "M27",
|
|
2285
|
+
style: {
|
|
2286
|
+
type: "line",
|
|
2287
|
+
paint: {
|
|
2288
|
+
"line-color": "CHMGD",
|
|
2289
|
+
"line-width": 1.2,
|
|
2290
|
+
"line-dasharray": [4, 3],
|
|
2291
|
+
"line-opacity": 0.7
|
|
2292
|
+
}
|
|
2293
|
+
},
|
|
2294
|
+
viewingGroup: 26010,
|
|
2295
|
+
scamin: 9e4
|
|
2296
|
+
},
|
|
2297
|
+
DWRTCL: {
|
|
2298
|
+
// Deepwater Route Centerline — LineString feature (not a polygon
|
|
2299
|
+
// boundary) running down the middle of a DWRTPT. Same magenta dashed
|
|
2300
|
+
// style as MAGVAR isogonic lines but slightly heavier so the
|
|
2301
|
+
// route-axis cue reads against busy water.
|
|
2302
|
+
objectClass: "DWRTCL",
|
|
2303
|
+
name: "Deepwater Route Centerline",
|
|
2304
|
+
category: "Regulated Areas",
|
|
2305
|
+
displayCategory: "STANDARD",
|
|
2306
|
+
int1Ref: "M27",
|
|
2307
|
+
style: {
|
|
2308
|
+
type: "line",
|
|
2309
|
+
paint: {
|
|
2310
|
+
"line-color": "CHMGD",
|
|
2311
|
+
"line-width": 0.8,
|
|
2312
|
+
"line-dasharray": [6, 3],
|
|
2313
|
+
"line-opacity": 0.7
|
|
2314
|
+
}
|
|
2315
|
+
},
|
|
2316
|
+
viewingGroup: 26010,
|
|
2317
|
+
scamin: 9e4
|
|
2318
|
+
},
|
|
2319
|
+
TWRTPT: {
|
|
2320
|
+
// Two-Way Route Part — polygon segment of an IMO-adopted two-way
|
|
2321
|
+
// route. S-52 INT1 M28 portrayal includes paired arrow centroid
|
|
2322
|
+
// symbols (TWRTPT52 / TWRTPT53 sprites exist in the atlas but are a
|
|
2323
|
+
// labels.ts follow-up). Boundary-only here.
|
|
2324
|
+
objectClass: "TWRTPT",
|
|
2325
|
+
name: "Two-Way Route Part",
|
|
2326
|
+
category: "Regulated Areas",
|
|
2327
|
+
displayCategory: "STANDARD",
|
|
2328
|
+
int1Ref: "M28",
|
|
2329
|
+
style: {
|
|
2330
|
+
type: "line",
|
|
2331
|
+
paint: {
|
|
2332
|
+
"line-color": "CHMGD",
|
|
2333
|
+
"line-width": 1.2,
|
|
2334
|
+
"line-dasharray": [4, 3],
|
|
2335
|
+
"line-opacity": 0.7
|
|
2336
|
+
}
|
|
2337
|
+
},
|
|
2338
|
+
viewingGroup: 26010,
|
|
2339
|
+
scamin: 9e4
|
|
2340
|
+
},
|
|
2047
2341
|
// ── Infrastructure (INT1 Sections D, F, L) ──────────────
|
|
2048
2342
|
BRIDGE: {
|
|
2049
2343
|
objectClass: "BRIDGE",
|
|
@@ -2352,6 +2646,56 @@ var S52_LOOKUP = {
|
|
|
2352
2646
|
viewingGroup: 12410,
|
|
2353
2647
|
scamin: 22e3
|
|
2354
2648
|
},
|
|
2649
|
+
// Coast Guard station — emergency-response context. Mariners look for
|
|
2650
|
+
// these when navigating unfamiliar coastline or planning a distress-call
|
|
2651
|
+
// contingency. INT1 F62. Symbolised with the same generic tower glyph
|
|
2652
|
+
// (BCNTOW01) used by BUISGL and the CATLMK=17 LNDMRK variant — a
|
|
2653
|
+
// dedicated CGUSTA SVG is a reasonable follow-up but not required for
|
|
2654
|
+
// the structural-presence cue. Surfaced by the weekly layer-coverage
|
|
2655
|
+
// audit (issue #575); 17 Point features in the NOAA corpus.
|
|
2656
|
+
CGUSTA: {
|
|
2657
|
+
objectClass: "CGUSTA",
|
|
2658
|
+
name: "Coast Guard Station",
|
|
2659
|
+
category: "Infrastructure",
|
|
2660
|
+
displayCategory: "OTHER",
|
|
2661
|
+
int1Ref: "F62",
|
|
2662
|
+
style: { type: "symbol", paint: {}, layout: { "icon-size": 0.6, "icon-allow-overlap": true }, iconImage: "BCNTOW01" },
|
|
2663
|
+
viewingGroup: 12410,
|
|
2664
|
+
scamin: 22e3
|
|
2665
|
+
},
|
|
2666
|
+
// "New Object" — S-57 placeholder class for features the issuing HO has
|
|
2667
|
+
// identified but not yet classified into a more specific class. S-52
|
|
2668
|
+
// PresLib does NOT formally define a portrayal because the class is by
|
|
2669
|
+
// construction not-yet-portrayable, so any distinctive symbol is
|
|
2670
|
+
// permissible — we use a vivid magenta-filled circle with a bold black
|
|
2671
|
+
// stroke so a mariner sees clearly that "there is something unclassified
|
|
2672
|
+
// here." Hiding this class would amount to hiding the HO's own
|
|
2673
|
+
// uncertainty marker, which is the exact opposite of what a chart user
|
|
2674
|
+
// needs. displayCategory: STANDARD (not OTHER) so a default-display
|
|
2675
|
+
// mariner cannot accidentally suppress it; scamin: 0 so the marker
|
|
2676
|
+
// stays visible at every scale band. Surfaced by the weekly layer-
|
|
2677
|
+
// coverage audit (issue #575); 8 Point features in the NOAA corpus.
|
|
2678
|
+
// SAFETY CONTRACT: any future "clean up unused magenta point classes"
|
|
2679
|
+
// pass MUST NOT delete or hide this entry — read the comment block on
|
|
2680
|
+
// issue #575 row 22 before touching.
|
|
2681
|
+
NEWOBJ: {
|
|
2682
|
+
objectClass: "NEWOBJ",
|
|
2683
|
+
name: "New Object (Unclassified)",
|
|
2684
|
+
category: "Hazards",
|
|
2685
|
+
displayCategory: "STANDARD",
|
|
2686
|
+
style: {
|
|
2687
|
+
type: "circle",
|
|
2688
|
+
paint: {
|
|
2689
|
+
"circle-color": "CHMGD",
|
|
2690
|
+
"circle-radius": 5,
|
|
2691
|
+
"circle-stroke-color": "CHBLK",
|
|
2692
|
+
"circle-stroke-width": 1.2,
|
|
2693
|
+
"circle-opacity": 0.85
|
|
2694
|
+
}
|
|
2695
|
+
},
|
|
2696
|
+
viewingGroup: 24020,
|
|
2697
|
+
scamin: 0
|
|
2698
|
+
},
|
|
2355
2699
|
// ── Overhead & Underground (INT1 Section L) ─────────────
|
|
2356
2700
|
// Overhead cable — S-52 INT1 L31 portrayal: solid thin magenta line
|
|
2357
2701
|
// (not dashed) because overhead cables are an in-air navigation hazard
|
|
@@ -2403,6 +2747,11 @@ var S52_LOOKUP = {
|
|
|
2403
2747
|
// when the sprite image is missing; `fill-pattern` here is the default
|
|
2404
2748
|
// used if the conditional expression isn't applied for some reason
|
|
2405
2749
|
// (e.g. a consumer that bypasses `resolvePaint`).
|
|
2750
|
+
//
|
|
2751
|
+
// Polygon (vegetation fill) is the primary variant. The Point variant
|
|
2752
|
+
// ≈ a single marker tree; sub-marine-relevance, so `auditIgnore`
|
|
2753
|
+
// silences the audit for the Point geom-type without touching the
|
|
2754
|
+
// Polygon rendering. See #575 triage matrix.
|
|
2406
2755
|
style: {
|
|
2407
2756
|
type: "fill",
|
|
2408
2757
|
paint: { "fill-color": "LANDF", "fill-opacity": 0.6, "fill-pattern": "VEGATN03" }
|
|
@@ -2410,7 +2759,8 @@ var S52_LOOKUP = {
|
|
|
2410
2759
|
hasConditional: true,
|
|
2411
2760
|
conditionalAttributes: ["CATVEG"],
|
|
2412
2761
|
viewingGroup: 12050,
|
|
2413
|
-
scamin: 22e3
|
|
2762
|
+
scamin: 22e3,
|
|
2763
|
+
auditIgnore: true
|
|
2414
2764
|
},
|
|
2415
2765
|
SEAARE: {
|
|
2416
2766
|
// Cross-band water base. SEAARE polygons tile the sea below the
|
|
@@ -2589,6 +2939,42 @@ var S52_LOOKUP = {
|
|
|
2589
2939
|
viewingGroup: 23010,
|
|
2590
2940
|
scamin: 0
|
|
2591
2941
|
},
|
|
2942
|
+
// ── Audit-ignored stubs (non-metadata) ──────────────────
|
|
2943
|
+
// Classes whose non-rendering is a deliberate S-52 portrayal choice
|
|
2944
|
+
// rather than a real gap, but which aren't S-57 metadata. Each is
|
|
2945
|
+
// present here only so the weekly layer-coverage audit (#575) stops
|
|
2946
|
+
// flagging it as missing. Paint is a transparent fill — no visible
|
|
2947
|
+
// output. Swapping `auditIgnore: true` for a real style is a one-line
|
|
2948
|
+
// change if the call ever needs to be reversed.
|
|
2949
|
+
SLOTOP: {
|
|
2950
|
+
// Top of slope — land terrain feature, low marine relevance.
|
|
2951
|
+
objectClass: "SLOTOP",
|
|
2952
|
+
name: "Top of Slope",
|
|
2953
|
+
category: "Land",
|
|
2954
|
+
displayCategory: "OTHER",
|
|
2955
|
+
int1Ref: "C12",
|
|
2956
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2957
|
+
auditIgnore: true
|
|
2958
|
+
},
|
|
2959
|
+
SNDWAV: {
|
|
2960
|
+
// Sand waves — niche bathymetric texture feature, not a hazard.
|
|
2961
|
+
objectClass: "SNDWAV",
|
|
2962
|
+
name: "Sand Waves",
|
|
2963
|
+
category: "Depths",
|
|
2964
|
+
displayCategory: "OTHER",
|
|
2965
|
+
int1Ref: "J14",
|
|
2966
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2967
|
+
auditIgnore: true
|
|
2968
|
+
},
|
|
2969
|
+
CTRPNT: {
|
|
2970
|
+
// Centerline point — geometric helper, not a feature on the chart.
|
|
2971
|
+
objectClass: "CTRPNT",
|
|
2972
|
+
name: "Centerline Point",
|
|
2973
|
+
category: "Meta",
|
|
2974
|
+
displayCategory: "OTHER",
|
|
2975
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2976
|
+
auditIgnore: true
|
|
2977
|
+
},
|
|
2592
2978
|
// ── Meta (hidden by default) ────────────────────────────
|
|
2593
2979
|
// M_* and DSID are S-57 metadata classes — they describe the dataset
|
|
2594
2980
|
// (coverage extent, survey reliability, source publication, etc.) and
|
|
@@ -2627,6 +3013,17 @@ var S52_LOOKUP = {
|
|
|
2627
3013
|
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2628
3014
|
auditIgnore: true
|
|
2629
3015
|
},
|
|
3016
|
+
M_SDAT: {
|
|
3017
|
+
// Sounding Datum — S-57 metadata describing the vertical datum used
|
|
3018
|
+
// for soundings (typically LAT). Click-query attribute carrier; no
|
|
3019
|
+
// visible shape on its own.
|
|
3020
|
+
objectClass: "M_SDAT",
|
|
3021
|
+
name: "Sounding Datum",
|
|
3022
|
+
category: "Meta",
|
|
3023
|
+
displayCategory: "META",
|
|
3024
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
3025
|
+
auditIgnore: true
|
|
3026
|
+
},
|
|
2630
3027
|
M_SREL: {
|
|
2631
3028
|
objectClass: "M_SREL",
|
|
2632
3029
|
name: "Survey Reliability",
|
|
@@ -2666,6 +3063,16 @@ var S52_DRAW_ORDER = [
|
|
|
2666
3063
|
// paired paint-spec change (fill-opacity 0 → 1, color → DEPDW).
|
|
2667
3064
|
"SEAARE",
|
|
2668
3065
|
"DEPARE",
|
|
3066
|
+
// 2a. Unsurveyed area — opaque grey wash painted ON TOP of any DEPARE
|
|
3067
|
+
// tile within its extent so depth-tier colours can never imply the area
|
|
3068
|
+
// is surveyed-and-safe. If both DEPARE and UNSARE cover the same pixel,
|
|
3069
|
+
// UNSARE wins on purpose: the hydrographic office classified the polygon
|
|
3070
|
+
// as unsurveyed, so any depth data within it is suspect at best. Sits
|
|
3071
|
+
// below dredged / swept (which represent active survey work) so those
|
|
3072
|
+
// appear if they overlap UNSARE — but in practice they don't, and the
|
|
3073
|
+
// ordering is the safer fallback. Surfaced by the weekly layer-coverage
|
|
3074
|
+
// audit (issue #575); see s52-lookup.ts UNSARE entry for context.
|
|
3075
|
+
"UNSARE",
|
|
2669
3076
|
// 3. Dredged / swept areas (fill)
|
|
2670
3077
|
"DRGARE",
|
|
2671
3078
|
"SWPARE",
|
|
@@ -2703,6 +3110,17 @@ var S52_DRAW_ORDER = [
|
|
|
2703
3110
|
"LOGPON",
|
|
2704
3111
|
"CBLARE",
|
|
2705
3112
|
"PIPARE",
|
|
3113
|
+
// 6a. Jurisdictional zones + IMO routing measures (boundary-only via
|
|
3114
|
+
// LINE_FROM_POLYGON_LAYERS). Slotted at the end of the regulated-area
|
|
3115
|
+
// band so the dashed magenta boundaries paint above the fill family
|
|
3116
|
+
// but below shoreline / depth-contour lines. Closes #575 rows 8–15.
|
|
3117
|
+
"ADMARE",
|
|
3118
|
+
"COSARE",
|
|
3119
|
+
"CONZNE",
|
|
3120
|
+
"EXEZNE",
|
|
3121
|
+
"TSEZNE",
|
|
3122
|
+
"DWRTPT",
|
|
3123
|
+
"TWRTPT",
|
|
2706
3124
|
// 7. Infrastructure areas (fill)
|
|
2707
3125
|
"PONTON",
|
|
2708
3126
|
"HULKES",
|
|
@@ -2712,8 +3130,13 @@ var S52_DRAW_ORDER = [
|
|
|
2712
3130
|
"PYLONS",
|
|
2713
3131
|
"FORSTC",
|
|
2714
3132
|
// 8. Coastline / shore (line)
|
|
3133
|
+
// DYKCON (dyke) and CAUSWY (causeway) join the SLCONS family — solid
|
|
3134
|
+
// black lines representing man-made shoreline modifications. Drawn in
|
|
3135
|
+
// the same band so they layer consistently with adjacent SLCONS edges.
|
|
2715
3136
|
"COALNE",
|
|
2716
3137
|
"SLCONS",
|
|
3138
|
+
"DYKCON",
|
|
3139
|
+
"CAUSWY",
|
|
2717
3140
|
"FNCLNE",
|
|
2718
3141
|
// 9. Depth contours (line)
|
|
2719
3142
|
"DEPCNT",
|
|
@@ -2724,13 +3147,20 @@ var S52_DRAW_ORDER = [
|
|
|
2724
3147
|
// routing and aids-to-nav win the visual hierarchy).
|
|
2725
3148
|
"MAGVAR",
|
|
2726
3149
|
// 10. Navigation lines (line)
|
|
3150
|
+
// RAPIDS and OILBAR are direct/operational water hazards. RAPIDS uses
|
|
3151
|
+
// the depth-blue palette (DEPVS dashed) so the feature reads as a
|
|
3152
|
+
// water-surface disturbance; OILBAR uses CHMGD to fit the operational-
|
|
3153
|
+
// hazard family alongside cables / pipelines.
|
|
2727
3154
|
"NAVLNE",
|
|
2728
3155
|
"TSSBND",
|
|
3156
|
+
"DWRTCL",
|
|
2729
3157
|
"ROADWY",
|
|
2730
3158
|
"RAILWY",
|
|
2731
3159
|
"TUNNEL",
|
|
2732
3160
|
"FERYRT",
|
|
2733
3161
|
"RECTRC",
|
|
3162
|
+
"RAPIDS",
|
|
3163
|
+
"OILBAR",
|
|
2734
3164
|
"PIPSOL",
|
|
2735
3165
|
"PIPOHD",
|
|
2736
3166
|
"CBLSUB",
|
|
@@ -2739,6 +3169,11 @@ var S52_DRAW_ORDER = [
|
|
|
2739
3169
|
"DAMCON",
|
|
2740
3170
|
"BERTHS",
|
|
2741
3171
|
// 11. Point features
|
|
3172
|
+
// CGUSTA (coast guard station, emergency-context) sits with the building
|
|
3173
|
+
// / landmark family — a structural-presence cue rather than a routing
|
|
3174
|
+
// signal. NEWOBJ ("something here we haven't classified yet") goes at
|
|
3175
|
+
// the END of the point list so the distinctive magenta marker stays on
|
|
3176
|
+
// top of neighbouring point features and cannot be visually swallowed.
|
|
2742
3177
|
"SOUNDG",
|
|
2743
3178
|
"MORFAC",
|
|
2744
3179
|
"PILPNT",
|
|
@@ -2752,6 +3187,8 @@ var S52_DRAW_ORDER = [
|
|
|
2752
3187
|
"CRANES",
|
|
2753
3188
|
"DISMAR",
|
|
2754
3189
|
"ACHBRT",
|
|
3190
|
+
"CGUSTA",
|
|
3191
|
+
"NEWOBJ",
|
|
2755
3192
|
// 12. Nav aids (on top)
|
|
2756
3193
|
"FOGSIG",
|
|
2757
3194
|
"RTPBCN",
|
|
@@ -5369,6 +5806,7 @@ var SYMBOL_MAP = {
|
|
|
5369
5806
|
PILBOP: "PILBOP02",
|
|
5370
5807
|
SISTAT: "SISTAT02",
|
|
5371
5808
|
SMCFAC: "ACHARE02",
|
|
5809
|
+
CGUSTA: "BCNTOW01",
|
|
5372
5810
|
// Land infrastructure
|
|
5373
5811
|
SILTNK: "TNKCON02",
|
|
5374
5812
|
BUISGL: "BCNTOW01",
|
|
@@ -6025,6 +6463,237 @@ function buildSeabedLabelExpression() {
|
|
|
6025
6463
|
const natsurJoined = ["slice", natsurParts, 1];
|
|
6026
6464
|
return ["concat", natquaPrefix, natsurJoined];
|
|
6027
6465
|
}
|
|
6466
|
+
function underseaFeatureNameMatch(objnam) {
|
|
6467
|
+
const terms = [
|
|
6468
|
+
"Canyon",
|
|
6469
|
+
"Seamount",
|
|
6470
|
+
"Ridge",
|
|
6471
|
+
"Basin",
|
|
6472
|
+
"Swell",
|
|
6473
|
+
"Knoll",
|
|
6474
|
+
"Shoal",
|
|
6475
|
+
"Ledge",
|
|
6476
|
+
"Bank",
|
|
6477
|
+
"Valley",
|
|
6478
|
+
"Spur",
|
|
6479
|
+
"Escarpment"
|
|
6480
|
+
];
|
|
6481
|
+
return ["any", ...terms.map((t) => [">=", ["index-of", t, objnam], 0])];
|
|
6482
|
+
}
|
|
6483
|
+
var CATSEA_RELIEF_CODES = [
|
|
6484
|
+
2,
|
|
6485
|
+
// gat
|
|
6486
|
+
3,
|
|
6487
|
+
// bank
|
|
6488
|
+
4,
|
|
6489
|
+
// deep
|
|
6490
|
+
6,
|
|
6491
|
+
// trench
|
|
6492
|
+
7,
|
|
6493
|
+
// basin
|
|
6494
|
+
8,
|
|
6495
|
+
// mud flats
|
|
6496
|
+
9,
|
|
6497
|
+
// reef
|
|
6498
|
+
10,
|
|
6499
|
+
// ledge
|
|
6500
|
+
11,
|
|
6501
|
+
// canyon
|
|
6502
|
+
13,
|
|
6503
|
+
// shoal
|
|
6504
|
+
14,
|
|
6505
|
+
// knoll
|
|
6506
|
+
15,
|
|
6507
|
+
// ridge
|
|
6508
|
+
16,
|
|
6509
|
+
// seamount
|
|
6510
|
+
17,
|
|
6511
|
+
// pinnacle
|
|
6512
|
+
18,
|
|
6513
|
+
// abyssal plain
|
|
6514
|
+
19,
|
|
6515
|
+
// plateau
|
|
6516
|
+
20,
|
|
6517
|
+
// spur
|
|
6518
|
+
21,
|
|
6519
|
+
// shelf
|
|
6520
|
+
22,
|
|
6521
|
+
// trough
|
|
6522
|
+
23,
|
|
6523
|
+
// saddle
|
|
6524
|
+
24,
|
|
6525
|
+
// abyssal hills
|
|
6526
|
+
25,
|
|
6527
|
+
// apron
|
|
6528
|
+
26,
|
|
6529
|
+
// archipelagic apron
|
|
6530
|
+
27,
|
|
6531
|
+
// borderland
|
|
6532
|
+
28,
|
|
6533
|
+
// continental margin
|
|
6534
|
+
29,
|
|
6535
|
+
// continental rise
|
|
6536
|
+
30,
|
|
6537
|
+
// escarpment
|
|
6538
|
+
31,
|
|
6539
|
+
// fan
|
|
6540
|
+
32,
|
|
6541
|
+
// fracture zone
|
|
6542
|
+
33,
|
|
6543
|
+
// gap
|
|
6544
|
+
34,
|
|
6545
|
+
// guyot
|
|
6546
|
+
35,
|
|
6547
|
+
// hill
|
|
6548
|
+
36,
|
|
6549
|
+
// hole
|
|
6550
|
+
37,
|
|
6551
|
+
// hollow
|
|
6552
|
+
39,
|
|
6553
|
+
// levee
|
|
6554
|
+
40,
|
|
6555
|
+
// median valley
|
|
6556
|
+
41,
|
|
6557
|
+
// moat
|
|
6558
|
+
42,
|
|
6559
|
+
// mountains
|
|
6560
|
+
43,
|
|
6561
|
+
// peak
|
|
6562
|
+
44,
|
|
6563
|
+
// province
|
|
6564
|
+
45,
|
|
6565
|
+
// rise
|
|
6566
|
+
47,
|
|
6567
|
+
// seamount chain
|
|
6568
|
+
48,
|
|
6569
|
+
// shelf-edge
|
|
6570
|
+
49,
|
|
6571
|
+
// sill
|
|
6572
|
+
50,
|
|
6573
|
+
// slope
|
|
6574
|
+
51,
|
|
6575
|
+
// terrace
|
|
6576
|
+
52
|
|
6577
|
+
// valley
|
|
6578
|
+
];
|
|
6579
|
+
function buildSeaAreaTierExpression() {
|
|
6580
|
+
const catsea = ["to-number", ["coalesce", ["get", "CATSEA"], ["get", "catsea"], 0]];
|
|
6581
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6582
|
+
return [
|
|
6583
|
+
"case",
|
|
6584
|
+
// Tier 4 — Anchorage detail (before all promotions so "Boston Harbor,
|
|
6585
|
+
// MA …" anchorage compound names don't bubble up).
|
|
6586
|
+
[">=", ["index-of", "Anchorage Area", objnam], 0],
|
|
6587
|
+
4,
|
|
6588
|
+
// Tier 5 — undersea relief, by authoritative CATSEA code first
|
|
6589
|
+
// (Georges Bank=3, Georges Shoal=13, …), then by name for
|
|
6590
|
+
// uncategorised features. Before promotion shims; see
|
|
6591
|
+
// underseaFeatureNameMatch.
|
|
6592
|
+
["match", catsea, CATSEA_RELIEF_CODES, true, false],
|
|
6593
|
+
5,
|
|
6594
|
+
underseaFeatureNameMatch(objnam),
|
|
6595
|
+
5,
|
|
6596
|
+
// Tier 3 by authoritative CATSEA: bay / narrows.
|
|
6597
|
+
["match", catsea, [5, 12], true, false],
|
|
6598
|
+
3,
|
|
6599
|
+
// OBJNAM promotion shims (CATSEA has no ocean/sea/gulf/sound/strait
|
|
6600
|
+
// codes, so names are the only signal for the headline tiers):
|
|
6601
|
+
[">=", ["index-of", "Atlantic Ocean", objnam], 0],
|
|
6602
|
+
1,
|
|
6603
|
+
[">=", ["index-of", "Pacific Ocean", objnam], 0],
|
|
6604
|
+
1,
|
|
6605
|
+
[">=", ["index-of", "Arctic Ocean", objnam], 0],
|
|
6606
|
+
1,
|
|
6607
|
+
[">=", ["index-of", "Gulf of", objnam], 0],
|
|
6608
|
+
2,
|
|
6609
|
+
[">=", ["index-of", " Bay", objnam], 0],
|
|
6610
|
+
3,
|
|
6611
|
+
[">=", ["index-of", " Sound", objnam], 0],
|
|
6612
|
+
3,
|
|
6613
|
+
[">=", ["index-of", " Strait", objnam], 0],
|
|
6614
|
+
3,
|
|
6615
|
+
[">=", ["index-of", " Sea", objnam], 0],
|
|
6616
|
+
2,
|
|
6617
|
+
5
|
|
6618
|
+
];
|
|
6619
|
+
}
|
|
6620
|
+
function buildBuiltUpAreaTierExpression() {
|
|
6621
|
+
const catbua = ["coalesce", ["get", "CATBUA"], ["get", "catbua"], 0];
|
|
6622
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6623
|
+
const nameLen = ["length", objnam];
|
|
6624
|
+
return [
|
|
6625
|
+
"case",
|
|
6626
|
+
["==", ["to-number", catbua], 1],
|
|
6627
|
+
1,
|
|
6628
|
+
["==", ["to-number", catbua], 5],
|
|
6629
|
+
2,
|
|
6630
|
+
["<=", nameLen, 8],
|
|
6631
|
+
1,
|
|
6632
|
+
["<=", nameLen, 14],
|
|
6633
|
+
2,
|
|
6634
|
+
3
|
|
6635
|
+
];
|
|
6636
|
+
}
|
|
6637
|
+
function buildLandRegionTierExpression() {
|
|
6638
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6639
|
+
const nameLen = ["length", objnam];
|
|
6640
|
+
return [
|
|
6641
|
+
"case",
|
|
6642
|
+
// NOAA also encodes undersea relief names (e.g. "Block Canyon", 12
|
|
6643
|
+
// chars, no comma) as LNDRGN — without this demotion they'd qualify
|
|
6644
|
+
// as tier-1 "major region" and paint at overview zoom.
|
|
6645
|
+
underseaFeatureNameMatch(objnam),
|
|
6646
|
+
2,
|
|
6647
|
+
["all", ["<=", nameLen, 12], ["==", ["index-of", ",", objnam], -1]],
|
|
6648
|
+
1,
|
|
6649
|
+
2
|
|
6650
|
+
];
|
|
6651
|
+
}
|
|
6652
|
+
function buildLandAreaTierExpression() {
|
|
6653
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6654
|
+
return [
|
|
6655
|
+
"case",
|
|
6656
|
+
["<=", ["length", objnam], 12],
|
|
6657
|
+
1,
|
|
6658
|
+
2
|
|
6659
|
+
];
|
|
6660
|
+
}
|
|
6661
|
+
function buildLakeTierExpression() {
|
|
6662
|
+
const catlak = ["coalesce", ["get", "CATLAK"], ["get", "catlak"], 0];
|
|
6663
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6664
|
+
return [
|
|
6665
|
+
"case",
|
|
6666
|
+
["==", ["to-number", catlak], 1],
|
|
6667
|
+
1,
|
|
6668
|
+
["any", ["==", ["to-number", catlak], 2], ["==", ["to-number", catlak], 3]],
|
|
6669
|
+
2,
|
|
6670
|
+
["<=", ["length", objnam], 12],
|
|
6671
|
+
1,
|
|
6672
|
+
2
|
|
6673
|
+
];
|
|
6674
|
+
}
|
|
6675
|
+
function buildRiverTierExpression() {
|
|
6676
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6677
|
+
return [
|
|
6678
|
+
"case",
|
|
6679
|
+
[
|
|
6680
|
+
"all",
|
|
6681
|
+
["<=", ["length", objnam], 15],
|
|
6682
|
+
["==", ["index-of", ",", objnam], -1]
|
|
6683
|
+
],
|
|
6684
|
+
1,
|
|
6685
|
+
2
|
|
6686
|
+
];
|
|
6687
|
+
}
|
|
6688
|
+
function buildAirportTierExpression() {
|
|
6689
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6690
|
+
return [
|
|
6691
|
+
"case",
|
|
6692
|
+
["<=", ["length", objnam], 25],
|
|
6693
|
+
1,
|
|
6694
|
+
2
|
|
6695
|
+
];
|
|
6696
|
+
}
|
|
6028
6697
|
var COLOR = {
|
|
6029
6698
|
WATER: "#9ec1d8",
|
|
6030
6699
|
WATER_DEEP: "#8fb8d0",
|
|
@@ -6045,6 +6714,60 @@ var ORIENTATION_BAND_SORT_KEY = [
|
|
|
6045
6714
|
["slice", ["coalesce", ["get", "_cell_id"], "??9???"], 2, 3],
|
|
6046
6715
|
9
|
|
6047
6716
|
];
|
|
6717
|
+
function orientationLabelLayout(extras = {}) {
|
|
6718
|
+
return {
|
|
6719
|
+
"text-anchor": "center",
|
|
6720
|
+
"text-allow-overlap": false,
|
|
6721
|
+
"text-ignore-placement": false,
|
|
6722
|
+
"text-optional": true,
|
|
6723
|
+
"text-padding": 15,
|
|
6724
|
+
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY,
|
|
6725
|
+
...extras
|
|
6726
|
+
};
|
|
6727
|
+
}
|
|
6728
|
+
function tieredSizeCurve(tierExpr, stops) {
|
|
6729
|
+
const withHolds = [];
|
|
6730
|
+
for (let i = 0; i < stops.length; i++) {
|
|
6731
|
+
if (i > 0) {
|
|
6732
|
+
const [pz, pv] = stops[i - 1];
|
|
6733
|
+
const [nz, nv] = stops[i];
|
|
6734
|
+
if (pv.some((v, t) => v === 0 && nv[t] > 0)) {
|
|
6735
|
+
const hz = Math.round((nz - 0.2) * 10) / 10;
|
|
6736
|
+
if (hz > pz) {
|
|
6737
|
+
const f = (hz - pz) / (nz - pz);
|
|
6738
|
+
withHolds.push([
|
|
6739
|
+
hz,
|
|
6740
|
+
pv.map(
|
|
6741
|
+
(v, t) => v === 0 && nv[t] > 0 ? 0 : Math.round((v + (nv[t] - v) * f) * 10) / 10
|
|
6742
|
+
)
|
|
6743
|
+
]);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
withHolds.push(stops[i]);
|
|
6748
|
+
}
|
|
6749
|
+
const expr = ["interpolate", ["linear"], ["zoom"]];
|
|
6750
|
+
for (const [z, vals] of withHolds) {
|
|
6751
|
+
const m = ["match", tierExpr];
|
|
6752
|
+
for (let t = 0; t < vals.length - 1; t++) m.push(t + 1, vals[t]);
|
|
6753
|
+
m.push(vals[vals.length - 1]);
|
|
6754
|
+
expr.push(z, m);
|
|
6755
|
+
}
|
|
6756
|
+
return expr;
|
|
6757
|
+
}
|
|
6758
|
+
var ORIENTATION_HALO_WIDTH = [
|
|
6759
|
+
"interpolate",
|
|
6760
|
+
["linear"],
|
|
6761
|
+
["zoom"],
|
|
6762
|
+
8,
|
|
6763
|
+
1.6,
|
|
6764
|
+
11,
|
|
6765
|
+
1.5,
|
|
6766
|
+
14,
|
|
6767
|
+
1,
|
|
6768
|
+
16,
|
|
6769
|
+
0.7
|
|
6770
|
+
];
|
|
6048
6771
|
var LABEL_BEHAVIOR = {
|
|
6049
6772
|
// Orientation labels — render once across all bands.
|
|
6050
6773
|
SEAARE: "orientation-cross-band",
|
|
@@ -6159,35 +6882,34 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6159
6882
|
}
|
|
6160
6883
|
};
|
|
6161
6884
|
}
|
|
6162
|
-
case "LNDRGN":
|
|
6885
|
+
case "LNDRGN": {
|
|
6886
|
+
const tierExpr = buildLandRegionTierExpression();
|
|
6163
6887
|
return {
|
|
6164
6888
|
type: "symbol",
|
|
6165
6889
|
textField: ["get", "OBJNAM"],
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6890
|
+
// Tier-1 entry moved from z5 to z7: undersea-relief names without a
|
|
6891
|
+
// generic keyword ("Three Dory", "Outer Fall") can't be demoted by
|
|
6892
|
+
// pattern, so the overview/general bands stay clean by holding ALL
|
|
6893
|
+
// LNDRGN labels until coastal zoom. The Carto basemap already labels
|
|
6894
|
+
// major land regions at z5-z6. Columns: t1 major region, t2 local
|
|
6895
|
+
// point (also the default).
|
|
6896
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
6897
|
+
[5, [0, 0]],
|
|
6898
|
+
[7, [11, 0]],
|
|
6899
|
+
[9, [12, 0]],
|
|
6900
|
+
[11, [12, 9]],
|
|
6901
|
+
[13, [11, 11]],
|
|
6902
|
+
[16, [10, 10]]
|
|
6903
|
+
]),
|
|
6177
6904
|
textColor: DARK_BG,
|
|
6178
6905
|
textHaloColor: COLOR.LAND_REGION,
|
|
6179
|
-
textHaloWidth:
|
|
6906
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6180
6907
|
textOffset: [0, 0],
|
|
6181
6908
|
textFont: ["Open Sans Regular"],
|
|
6182
|
-
minzoom:
|
|
6183
|
-
layout:
|
|
6184
|
-
"text-anchor": "center",
|
|
6185
|
-
"text-allow-overlap": false,
|
|
6186
|
-
"text-padding": 6,
|
|
6187
|
-
"text-optional": true,
|
|
6188
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6189
|
-
}
|
|
6909
|
+
minzoom: 5,
|
|
6910
|
+
layout: orientationLabelLayout()
|
|
6190
6911
|
};
|
|
6912
|
+
}
|
|
6191
6913
|
case "LNDMRK":
|
|
6192
6914
|
return {
|
|
6193
6915
|
type: "symbol",
|
|
@@ -6316,97 +7038,97 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6316
7038
|
}
|
|
6317
7039
|
};
|
|
6318
7040
|
}
|
|
6319
|
-
case "SEAARE":
|
|
7041
|
+
case "SEAARE": {
|
|
7042
|
+
const tierExpr = buildSeaAreaTierExpression();
|
|
6320
7043
|
return {
|
|
6321
7044
|
type: "symbol",
|
|
6322
7045
|
textField: ["get", "OBJNAM"],
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
14,
|
|
6334
|
-
|
|
6335
|
-
|
|
7046
|
+
// Default tier (CATSEA=0, uncategorised: canyons, seamounts, fishing
|
|
7047
|
+
// banks etc.) is suppressed at overview/general/coastal scales because
|
|
7048
|
+
// it floods the overview with hundreds of small italic labels that
|
|
7049
|
+
// crowd out the headline Ocean/Sea/Gulf names. It becomes visible only
|
|
7050
|
+
// at approach scale (z9) when the user has zoomed in enough that
|
|
7051
|
+
// individual banks are useful context. Columns: t1 ocean, t2 sea/gulf,
|
|
7052
|
+
// t3 bay/sound, t4 anchorage, default.
|
|
7053
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7054
|
+
[3, [14, 8, 0, 0, 0]],
|
|
7055
|
+
[5, [16, 13, 9, 0, 0]],
|
|
7056
|
+
[7, [14, 14, 12, 0, 0]],
|
|
7057
|
+
[9, [12, 13, 14, 0, 9]],
|
|
7058
|
+
[11, [10, 12, 13, 0, 11]],
|
|
7059
|
+
[13, [9, 11, 12, 10, 11]],
|
|
7060
|
+
[16, [9, 10, 11, 11, 10]]
|
|
7061
|
+
]),
|
|
6336
7062
|
textColor: DARK_BG,
|
|
6337
7063
|
textHaloColor: COLOR.WATER_DEEP,
|
|
6338
|
-
textHaloWidth:
|
|
7064
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6339
7065
|
textOffset: [0, 0],
|
|
6340
7066
|
textFont: ["Open Sans Italic"],
|
|
6341
7067
|
minzoom: 5,
|
|
6342
|
-
layout: {
|
|
6343
|
-
"text-anchor": "center",
|
|
6344
|
-
"text-allow-overlap": false,
|
|
6345
|
-
"text-padding": 10,
|
|
6346
|
-
"text-optional": true,
|
|
7068
|
+
layout: orientationLabelLayout({
|
|
6347
7069
|
"text-letter-spacing": 0.15,
|
|
6348
7070
|
"text-max-width": 9,
|
|
6349
|
-
|
|
6350
|
-
|
|
7071
|
+
// SEAARE is always-paint, unlike the other orientation classes.
|
|
7072
|
+
// MapLibre places symbols topmost-layer-first, so with
|
|
7073
|
+
// allow-overlap:false the dense sounding field (drawn above)
|
|
7074
|
+
// collision-starves big-water names — observed on staging at z8:
|
|
7075
|
+
// only 2 SEAARE labels survived viewport-wide, with Cape Cod Bay
|
|
7076
|
+
// and Gulf of Maine dropped entirely. Always-paint is safe for
|
|
7077
|
+
// this class because (a) the dominance composition in
|
|
7078
|
+
// fanOrientationLabelByCellBand suppresses cross-band duplicate
|
|
7079
|
+
// copies, and (b) tier sizing zeroes relief/anchorage names at
|
|
7080
|
+
// the zooms where density is a problem. Rivers/lakes/land keep
|
|
7081
|
+
// collision-dedup — their within-band multi-polygon stacking was
|
|
7082
|
+
// the original complaint, and they lack big-water headline value.
|
|
7083
|
+
"text-allow-overlap": true,
|
|
7084
|
+
"text-ignore-placement": true,
|
|
7085
|
+
"text-optional": false,
|
|
7086
|
+
"text-padding": 2
|
|
7087
|
+
})
|
|
6351
7088
|
};
|
|
6352
|
-
|
|
7089
|
+
}
|
|
7090
|
+
case "LAKARE": {
|
|
7091
|
+
const tierExpr = buildLakeTierExpression();
|
|
6353
7092
|
return {
|
|
6354
7093
|
type: "symbol",
|
|
6355
7094
|
textField: ["get", "OBJNAM"],
|
|
6356
|
-
textSize: [
|
|
6357
|
-
|
|
6358
|
-
[
|
|
6359
|
-
[
|
|
6360
|
-
|
|
6361
|
-
10
|
|
6362
|
-
|
|
6363
|
-
12,
|
|
6364
|
-
15,
|
|
6365
|
-
14
|
|
6366
|
-
],
|
|
7095
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7096
|
+
[7, [9, 0]],
|
|
7097
|
+
[9, [11, 0]],
|
|
7098
|
+
[11, [11, 9]],
|
|
7099
|
+
[13, [10, 11]],
|
|
7100
|
+
[16, [9, 10]]
|
|
7101
|
+
]),
|
|
6367
7102
|
textColor: DARK_BG,
|
|
6368
7103
|
textHaloColor: COLOR.WATER,
|
|
6369
|
-
textHaloWidth:
|
|
7104
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6370
7105
|
textOffset: [0, 0],
|
|
6371
7106
|
textFont: ["Open Sans Italic"],
|
|
6372
|
-
minzoom:
|
|
6373
|
-
layout:
|
|
6374
|
-
"text-anchor": "center",
|
|
6375
|
-
"text-allow-overlap": false,
|
|
6376
|
-
"text-padding": 6,
|
|
6377
|
-
"text-optional": true,
|
|
6378
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6379
|
-
}
|
|
7107
|
+
minzoom: 7,
|
|
7108
|
+
layout: orientationLabelLayout()
|
|
6380
7109
|
};
|
|
6381
|
-
|
|
7110
|
+
}
|
|
7111
|
+
case "RIVERS": {
|
|
7112
|
+
const tierExpr = buildRiverTierExpression();
|
|
6382
7113
|
return {
|
|
6383
7114
|
type: "symbol",
|
|
6384
7115
|
textField: ["get", "OBJNAM"],
|
|
6385
|
-
textSize: [
|
|
6386
|
-
|
|
6387
|
-
[
|
|
6388
|
-
[
|
|
6389
|
-
|
|
6390
|
-
9,
|
|
6391
|
-
|
|
6392
|
-
11,
|
|
6393
|
-
15,
|
|
6394
|
-
13
|
|
6395
|
-
],
|
|
7116
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7117
|
+
[7, [9, 0]],
|
|
7118
|
+
[9, [11, 0]],
|
|
7119
|
+
[11, [11, 9]],
|
|
7120
|
+
[13, [10, 10]],
|
|
7121
|
+
[16, [9, 10]]
|
|
7122
|
+
]),
|
|
6396
7123
|
textColor: DARK_BG,
|
|
6397
7124
|
textHaloColor: COLOR.WATER,
|
|
6398
|
-
textHaloWidth:
|
|
7125
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6399
7126
|
textOffset: [0, 0],
|
|
6400
7127
|
textFont: ["Open Sans Italic"],
|
|
6401
|
-
minzoom:
|
|
6402
|
-
layout:
|
|
6403
|
-
"text-anchor": "center",
|
|
6404
|
-
"text-allow-overlap": false,
|
|
6405
|
-
"text-padding": 4,
|
|
6406
|
-
"text-optional": true,
|
|
6407
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6408
|
-
}
|
|
7128
|
+
minzoom: 7,
|
|
7129
|
+
layout: orientationLabelLayout()
|
|
6409
7130
|
};
|
|
7131
|
+
}
|
|
6410
7132
|
case "CANALS":
|
|
6411
7133
|
return {
|
|
6412
7134
|
type: "symbol",
|
|
@@ -6416,83 +7138,67 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6416
7138
|
["linear"],
|
|
6417
7139
|
["zoom"],
|
|
6418
7140
|
11,
|
|
7141
|
+
10,
|
|
7142
|
+
13,
|
|
6419
7143
|
9,
|
|
6420
|
-
|
|
6421
|
-
|
|
7144
|
+
16,
|
|
7145
|
+
9
|
|
6422
7146
|
],
|
|
6423
7147
|
textColor: DARK_BG,
|
|
6424
7148
|
textHaloColor: COLOR.WATER,
|
|
6425
|
-
textHaloWidth:
|
|
7149
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6426
7150
|
textOffset: [0, 0],
|
|
6427
7151
|
textFont: ["Open Sans Italic"],
|
|
6428
7152
|
minzoom: 11,
|
|
6429
|
-
layout:
|
|
6430
|
-
"text-anchor": "center",
|
|
6431
|
-
"text-allow-overlap": false,
|
|
6432
|
-
"text-padding": 4,
|
|
6433
|
-
"text-optional": true,
|
|
6434
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6435
|
-
}
|
|
7153
|
+
layout: orientationLabelLayout()
|
|
6436
7154
|
};
|
|
6437
|
-
case "LNDARE":
|
|
7155
|
+
case "LNDARE": {
|
|
7156
|
+
const tierExpr = buildLandAreaTierExpression();
|
|
6438
7157
|
return {
|
|
6439
7158
|
type: "symbol",
|
|
6440
7159
|
textField: ["get", "OBJNAM"],
|
|
6441
|
-
textSize: [
|
|
6442
|
-
|
|
6443
|
-
[
|
|
6444
|
-
[
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
15,
|
|
6450
|
-
13
|
|
6451
|
-
],
|
|
7160
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7161
|
+
[5, [8, 0]],
|
|
7162
|
+
[7, [10, 0]],
|
|
7163
|
+
[9, [11, 0]],
|
|
7164
|
+
[11, [11, 9]],
|
|
7165
|
+
[13, [10, 10]],
|
|
7166
|
+
[16, [9, 10]]
|
|
7167
|
+
]),
|
|
6452
7168
|
textColor: DARK_BG,
|
|
6453
7169
|
textHaloColor: COLOR.LAND_NATURAL,
|
|
6454
|
-
textHaloWidth:
|
|
7170
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6455
7171
|
textOffset: [0, 0],
|
|
6456
7172
|
textFont: ["Open Sans Regular"],
|
|
6457
|
-
minzoom:
|
|
6458
|
-
layout:
|
|
6459
|
-
"text-anchor": "center",
|
|
6460
|
-
"text-allow-overlap": false,
|
|
6461
|
-
"text-padding": 6,
|
|
6462
|
-
"text-optional": true,
|
|
6463
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6464
|
-
}
|
|
7173
|
+
minzoom: 5,
|
|
7174
|
+
layout: orientationLabelLayout()
|
|
6465
7175
|
};
|
|
6466
|
-
|
|
7176
|
+
}
|
|
7177
|
+
case "BUAARE": {
|
|
7178
|
+
const tierExpr = buildBuiltUpAreaTierExpression();
|
|
6467
7179
|
return {
|
|
6468
7180
|
type: "symbol",
|
|
6469
7181
|
textField: ["get", "OBJNAM"],
|
|
6470
|
-
textSize: [
|
|
6471
|
-
|
|
6472
|
-
[
|
|
6473
|
-
[
|
|
6474
|
-
|
|
6475
|
-
11,
|
|
6476
|
-
10,
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
15
|
|
6480
|
-
],
|
|
7182
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7183
|
+
[3, [11, 0, 0]],
|
|
7184
|
+
[5, [13, 0, 0]],
|
|
7185
|
+
[7, [13, 9, 0]],
|
|
7186
|
+
[9, [12, 12, 0]],
|
|
7187
|
+
[11, [11, 12, 10]],
|
|
7188
|
+
[13, [10, 11, 12]],
|
|
7189
|
+
[16, [10, 10, 11]]
|
|
7190
|
+
]),
|
|
6481
7191
|
textColor: DARK_BG,
|
|
6482
7192
|
textHaloColor: COLOR.CITY,
|
|
6483
|
-
textHaloWidth:
|
|
7193
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6484
7194
|
textOffset: [0, 0],
|
|
6485
7195
|
textFont: ["Open Sans Regular"],
|
|
6486
|
-
minzoom:
|
|
6487
|
-
layout: {
|
|
6488
|
-
"text-
|
|
6489
|
-
|
|
6490
|
-
"text-padding": 8,
|
|
6491
|
-
"text-optional": true,
|
|
6492
|
-
"text-letter-spacing": 0.05,
|
|
6493
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6494
|
-
}
|
|
7196
|
+
minzoom: 3,
|
|
7197
|
+
layout: orientationLabelLayout({
|
|
7198
|
+
"text-letter-spacing": 0.05
|
|
7199
|
+
})
|
|
6495
7200
|
};
|
|
7201
|
+
}
|
|
6496
7202
|
case "BRIDGE":
|
|
6497
7203
|
return {
|
|
6498
7204
|
type: "symbol",
|
|
@@ -6560,33 +7266,27 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6560
7266
|
"text-optional": true
|
|
6561
7267
|
}
|
|
6562
7268
|
};
|
|
6563
|
-
case "AIRARE":
|
|
7269
|
+
case "AIRARE": {
|
|
7270
|
+
const tierExpr = buildAirportTierExpression();
|
|
6564
7271
|
return {
|
|
6565
7272
|
type: "symbol",
|
|
6566
7273
|
textField: ["get", "OBJNAM"],
|
|
6567
|
-
textSize: [
|
|
6568
|
-
|
|
6569
|
-
[
|
|
6570
|
-
[
|
|
6571
|
-
|
|
6572
|
-
9,
|
|
6573
|
-
|
|
6574
|
-
11
|
|
6575
|
-
],
|
|
7274
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7275
|
+
[7, [9, 0]],
|
|
7276
|
+
[9, [11, 0]],
|
|
7277
|
+
[11, [11, 9]],
|
|
7278
|
+
[13, [10, 10]],
|
|
7279
|
+
[16, [9, 9]]
|
|
7280
|
+
]),
|
|
6576
7281
|
textColor: DARK_BG,
|
|
6577
7282
|
textHaloColor: COLOR.AIRPORT,
|
|
6578
|
-
textHaloWidth:
|
|
7283
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6579
7284
|
textOffset: [0, 0],
|
|
6580
7285
|
textFont: ["Open Sans Regular"],
|
|
6581
|
-
minzoom:
|
|
6582
|
-
layout:
|
|
6583
|
-
"text-anchor": "center",
|
|
6584
|
-
"text-allow-overlap": false,
|
|
6585
|
-
"text-padding": 5,
|
|
6586
|
-
"text-optional": true,
|
|
6587
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6588
|
-
}
|
|
7286
|
+
minzoom: 7,
|
|
7287
|
+
layout: orientationLabelLayout()
|
|
6589
7288
|
};
|
|
7289
|
+
}
|
|
6590
7290
|
case "SBDARE":
|
|
6591
7291
|
return {
|
|
6592
7292
|
type: "symbol",
|
|
@@ -6659,26 +7359,20 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6659
7359
|
"interpolate",
|
|
6660
7360
|
["linear"],
|
|
6661
7361
|
["zoom"],
|
|
7362
|
+
11,
|
|
7363
|
+
9,
|
|
7364
|
+
13,
|
|
6662
7365
|
8,
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
12,
|
|
6666
|
-
15,
|
|
6667
|
-
14
|
|
7366
|
+
16,
|
|
7367
|
+
8
|
|
6668
7368
|
],
|
|
6669
7369
|
textColor: "#c545c3",
|
|
6670
7370
|
textHaloColor: DARK_BG,
|
|
6671
|
-
textHaloWidth:
|
|
7371
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6672
7372
|
textOffset: [0, 0],
|
|
6673
7373
|
textFont: ["Open Sans Italic"],
|
|
6674
7374
|
minzoom: 8,
|
|
6675
|
-
layout:
|
|
6676
|
-
"text-anchor": "center",
|
|
6677
|
-
"text-allow-overlap": false,
|
|
6678
|
-
"text-padding": 6,
|
|
6679
|
-
"text-optional": true,
|
|
6680
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6681
|
-
}
|
|
7375
|
+
layout: orientationLabelLayout()
|
|
6682
7376
|
};
|
|
6683
7377
|
case "WEDKLP":
|
|
6684
7378
|
return {
|
|
@@ -8785,7 +9479,21 @@ var LINE_FROM_POLYGON_LAYERS = /* @__PURE__ */ new Set([
|
|
|
8785
9479
|
"MIPARE",
|
|
8786
9480
|
"TESARE",
|
|
8787
9481
|
"MAGVAR",
|
|
8788
|
-
"PRDARE"
|
|
9482
|
+
"PRDARE",
|
|
9483
|
+
"TUNNEL",
|
|
9484
|
+
// Jurisdictional zones (INT1 N40–N47) and IMO routing measures
|
|
9485
|
+
// (INT1 M14, M27, M28) — magenta dashed boundary, no fill. Vast
|
|
9486
|
+
// offshore polygons would tint half the chart if flood-filled, so
|
|
9487
|
+
// every member here renders as boundary-only. DWRTCL is not listed
|
|
9488
|
+
// because it's a native LineString class, not a polygon-as-line.
|
|
9489
|
+
// Closes issue #575 rows 8–15.
|
|
9490
|
+
"ADMARE",
|
|
9491
|
+
"COSARE",
|
|
9492
|
+
"CONZNE",
|
|
9493
|
+
"EXEZNE",
|
|
9494
|
+
"TSEZNE",
|
|
9495
|
+
"DWRTPT",
|
|
9496
|
+
"TWRTPT"
|
|
8789
9497
|
]);
|
|
8790
9498
|
var ZOOM_SCALED_LINES2 = {
|
|
8791
9499
|
DEPCNT: [8, 0.3, 14, 0.8],
|
|
@@ -9773,6 +10481,90 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
|
|
|
9773
10481
|
metadata: { "s52:objectClass": "RECTRC", "s52:role": "polygon" }
|
|
9774
10482
|
});
|
|
9775
10483
|
}
|
|
10484
|
+
if (objectClass === "PONTON" && resolved.type === "fill") {
|
|
10485
|
+
layers.push({
|
|
10486
|
+
id: `s52-PONTON-line`,
|
|
10487
|
+
type: "line",
|
|
10488
|
+
source: sourceId,
|
|
10489
|
+
"source-layer": "PONTON",
|
|
10490
|
+
filter: GEOM_FILTERS.line,
|
|
10491
|
+
paint: {
|
|
10492
|
+
"line-color": resolveColor("CHGRF", ct),
|
|
10493
|
+
"line-width": 1.5
|
|
10494
|
+
},
|
|
10495
|
+
metadata: { "s52:objectClass": "PONTON", "s52:role": "line" }
|
|
10496
|
+
});
|
|
10497
|
+
}
|
|
10498
|
+
if (objectClass === "SILTNK" && resolved.type === "symbol") {
|
|
10499
|
+
layers.push({
|
|
10500
|
+
id: `s52-SILTNK-symbol-polygon`,
|
|
10501
|
+
type: "symbol",
|
|
10502
|
+
source: sourceId,
|
|
10503
|
+
"source-layer": "SILTNK",
|
|
10504
|
+
filter: GEOM_FILTERS.fill,
|
|
10505
|
+
layout: {
|
|
10506
|
+
"icon-image": "TNKCON02",
|
|
10507
|
+
"icon-size": 0.6,
|
|
10508
|
+
"icon-allow-overlap": true,
|
|
10509
|
+
"icon-ignore-placement": false,
|
|
10510
|
+
"symbol-placement": "point"
|
|
10511
|
+
},
|
|
10512
|
+
paint: {},
|
|
10513
|
+
metadata: { "s52:objectClass": "SILTNK", "s52:role": "symbol-polygon" }
|
|
10514
|
+
});
|
|
10515
|
+
}
|
|
10516
|
+
if (objectClass === "ACHBRT" && resolved.type === "symbol") {
|
|
10517
|
+
layers.push({
|
|
10518
|
+
id: `s52-ACHBRT-symbol-polygon`,
|
|
10519
|
+
type: "symbol",
|
|
10520
|
+
source: sourceId,
|
|
10521
|
+
"source-layer": "ACHBRT",
|
|
10522
|
+
filter: GEOM_FILTERS.fill,
|
|
10523
|
+
layout: {
|
|
10524
|
+
"icon-image": "ACHARE02",
|
|
10525
|
+
"icon-size": 0.5,
|
|
10526
|
+
"icon-allow-overlap": true,
|
|
10527
|
+
"icon-ignore-placement": false,
|
|
10528
|
+
"symbol-placement": "point"
|
|
10529
|
+
},
|
|
10530
|
+
paint: {},
|
|
10531
|
+
metadata: { "s52:objectClass": "ACHBRT", "s52:role": "symbol-polygon" }
|
|
10532
|
+
});
|
|
10533
|
+
}
|
|
10534
|
+
if (objectClass === "BERTHS" && resolved.type === "line") {
|
|
10535
|
+
layers.push({
|
|
10536
|
+
id: `s52-BERTHS-circle`,
|
|
10537
|
+
type: "circle",
|
|
10538
|
+
source: sourceId,
|
|
10539
|
+
"source-layer": "BERTHS",
|
|
10540
|
+
filter: GEOM_FILTERS.circle,
|
|
10541
|
+
paint: {
|
|
10542
|
+
"circle-color": resolveColor("CHBLK", ct),
|
|
10543
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.5, 14, 2.2, 17, 3],
|
|
10544
|
+
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
10545
|
+
"circle-stroke-width": 0.5
|
|
10546
|
+
},
|
|
10547
|
+
metadata: { "s52:objectClass": "BERTHS", "s52:role": "point" }
|
|
10548
|
+
});
|
|
10549
|
+
}
|
|
10550
|
+
if (objectClass === "TUNNEL" && resolved.type === "line") {
|
|
10551
|
+
layers.push({
|
|
10552
|
+
// NOT `s52-TUNNEL-line` — that id is already taken by the main layer
|
|
10553
|
+
// (`s52-${objectClass}-${resolved.type}`), and duplicate layer ids
|
|
10554
|
+
// are a Mapbox style-spec violation (caught by style-integrity.test).
|
|
10555
|
+
id: `s52-TUNNEL-linestring`,
|
|
10556
|
+
type: "line",
|
|
10557
|
+
source: sourceId,
|
|
10558
|
+
"source-layer": "TUNNEL",
|
|
10559
|
+
filter: GEOM_FILTERS.line,
|
|
10560
|
+
paint: {
|
|
10561
|
+
"line-color": resolveColor("CHGRD", ct),
|
|
10562
|
+
"line-width": 1,
|
|
10563
|
+
"line-dasharray": [2, 2]
|
|
10564
|
+
},
|
|
10565
|
+
metadata: { "s52:objectClass": "TUNNEL", "s52:role": "line" }
|
|
10566
|
+
});
|
|
10567
|
+
}
|
|
9776
10568
|
}
|
|
9777
10569
|
return layers;
|
|
9778
10570
|
}
|
|
@@ -10008,9 +10800,21 @@ var META_LAYERS = /* @__PURE__ */ new Set([
|
|
|
10008
10800
|
"M_CSCL",
|
|
10009
10801
|
"TS_FEB",
|
|
10010
10802
|
"TS_TIS",
|
|
10011
|
-
"COSARE",
|
|
10012
|
-
"NEWOBJ",
|
|
10013
10803
|
"LOCMAG"
|
|
10804
|
+
// COSARE was previously suppressed here as meta. The weekly layer-
|
|
10805
|
+
// coverage audit (issue #575) classified it as a jurisdictional polygon
|
|
10806
|
+
// (S-52 INT1 N40, same family as ADMARE / TESARE). It now has an
|
|
10807
|
+
// S52_LOOKUP entry and renders as a magenta dashed boundary; suppressing
|
|
10808
|
+
// it as meta would defeat that wire-up.
|
|
10809
|
+
//
|
|
10810
|
+
// NEWOBJ was likewise previously suppressed here. That was the source
|
|
10811
|
+
// of audit gap row 22: NEWOBJ is the S-57 placeholder for features the
|
|
10812
|
+
// HO has identified but not yet classified. Skipping it silently hid
|
|
10813
|
+
// the HO's own "we don't know what this is, but it is here" signal —
|
|
10814
|
+
// the opposite of what mariners need. The class now renders via its
|
|
10815
|
+
// S52_LOOKUP entry as a vivid magenta circle, displayCategory STANDARD,
|
|
10816
|
+
// scamin 0. Do not put it back here — see the lookup entry for the
|
|
10817
|
+
// safety contract.
|
|
10014
10818
|
]);
|
|
10015
10819
|
var PALETTE_NAMES = {
|
|
10016
10820
|
DAY: "DAY_BRIGHT",
|
|
@@ -10101,21 +10905,58 @@ function makeBandVariants(layer, B) {
|
|
|
10101
10905
|
}
|
|
10102
10906
|
return variants;
|
|
10103
10907
|
}
|
|
10104
|
-
|
|
10908
|
+
var CELL_BAND_OPACITY_CURVE = {
|
|
10909
|
+
"1": [[3, 0.5], [5, 0.3], [16, 0.15]],
|
|
10910
|
+
"2": [[5, 0.5], [7, 0.3], [16, 0.15]],
|
|
10911
|
+
"3": [[7, 0.55], [9, 0.35], [16, 0.18]],
|
|
10912
|
+
"4": [[9, 0.55], [11, 0.35], [16, 0.2]],
|
|
10913
|
+
"5": [[11, 0.6], [13, 0.35], [18, 0.25]],
|
|
10914
|
+
"6": [[13, 0.75], [16, 0.55], [22, 0.45]]
|
|
10915
|
+
};
|
|
10916
|
+
function fanOrientationLabelByCellBand(label) {
|
|
10917
|
+
const cellBandExpr = [
|
|
10918
|
+
"slice",
|
|
10919
|
+
["coalesce", ["get", "_cell_id"], "??9???"],
|
|
10920
|
+
2,
|
|
10921
|
+
3
|
|
10922
|
+
];
|
|
10105
10923
|
const variants = [];
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10924
|
+
for (const code of BAND_DRAW_ORDER) {
|
|
10925
|
+
const range = BAND_PRIORITY_RANGES[code];
|
|
10926
|
+
const curve = CELL_BAND_OPACITY_CURVE[code];
|
|
10927
|
+
const opacityExpr = ["interpolate", ["linear"], ["zoom"]];
|
|
10928
|
+
for (const [z, o] of curve) opacityExpr.push(z, o);
|
|
10929
|
+
const cellFiltered = combineWithBandFilter(
|
|
10930
|
+
label.filter,
|
|
10931
|
+
["==", cellBandExpr, code]
|
|
10932
|
+
);
|
|
10933
|
+
const minzoom = Math.max(
|
|
10934
|
+
label.minzoom ?? range.minzoom,
|
|
10935
|
+
range.minzoom
|
|
10936
|
+
);
|
|
10937
|
+
const paint = {
|
|
10938
|
+
...label.paint ?? {},
|
|
10939
|
+
"text-opacity": opacityExpr
|
|
10940
|
+
};
|
|
10113
10941
|
variants.push({
|
|
10114
10942
|
...label,
|
|
10115
|
-
id: `${label.id}-
|
|
10116
|
-
filter: combineWithBandFilter(
|
|
10117
|
-
|
|
10943
|
+
id: `${label.id}-cell${code}-notdom`,
|
|
10944
|
+
filter: combineWithBandFilter(cellFiltered, NOT_DOMINATED_FILTER),
|
|
10945
|
+
minzoom,
|
|
10946
|
+
paint
|
|
10118
10947
|
});
|
|
10948
|
+
for (const D of BAND_DRAW_ORDER) {
|
|
10949
|
+
const dom = BAND_PRIORITY_RANGES[D];
|
|
10950
|
+
if (dom.minzoom <= minzoom) continue;
|
|
10951
|
+
variants.push({
|
|
10952
|
+
...label,
|
|
10953
|
+
id: `${label.id}-cell${code}-domBy${D}`,
|
|
10954
|
+
filter: combineWithBandFilter(cellFiltered, dominatedByMinFilter(D)),
|
|
10955
|
+
minzoom,
|
|
10956
|
+
maxzoom: dom.minzoom,
|
|
10957
|
+
paint
|
|
10958
|
+
});
|
|
10959
|
+
}
|
|
10119
10960
|
}
|
|
10120
10961
|
return variants;
|
|
10121
10962
|
}
|
|
@@ -10239,7 +11080,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
10239
11080
|
for (const { objectClass, label } of bundles) {
|
|
10240
11081
|
if (!label) continue;
|
|
10241
11082
|
if (getLabelBehavior(objectClass) === "orientation-cross-band") {
|
|
10242
|
-
labelLayers.push(...
|
|
11083
|
+
labelLayers.push(...fanOrientationLabelByCellBand(label));
|
|
10243
11084
|
}
|
|
10244
11085
|
}
|
|
10245
11086
|
for (const code of BAND_DRAW_ORDER) {
|