@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.js
CHANGED
|
@@ -1307,6 +1307,32 @@ var S52_LOOKUP = {
|
|
|
1307
1307
|
viewingGroup: 13050,
|
|
1308
1308
|
scamin: 45e3
|
|
1309
1309
|
},
|
|
1310
|
+
// Unsurveyed area — a polygon where no reliable hydrographic survey
|
|
1311
|
+
// exists. The IHO S-52 PresLib portrayal is the hatched pattern
|
|
1312
|
+
// UNSURV03 (diagonal lines over a faint grey wash); we don't carry
|
|
1313
|
+
// that sprite, so we lean on the existing OPAQUE_FILLS treatment in
|
|
1314
|
+
// `layer-builder.ts` to force a fully-opaque grey fill that visibly
|
|
1315
|
+
// suppresses anything underneath. The "no data here — don't assume
|
|
1316
|
+
// safe water" signal is the entire point; UNSARE rendering is a BASE
|
|
1317
|
+
// display-category obligation, not an optional cue.
|
|
1318
|
+
//
|
|
1319
|
+
// Note: `UNSARE` is already a member of `OPAQUE_FILLS` and
|
|
1320
|
+
// `OPAQUE_FILL_LAYERS` in `layer-builder.ts` and `style-resolver.ts`
|
|
1321
|
+
// — half-wired before this entry landed. Adding the lookup entry +
|
|
1322
|
+
// draw-order slot completes the wire-up. Surfaced by the
|
|
1323
|
+
// `Audit S-57 Layer Coverage (Weekly)` workflow (issue #575); 5
|
|
1324
|
+
// features in the NOAA corpus, all in remote-Alaska coverage gaps
|
|
1325
|
+
// where survey is genuinely absent.
|
|
1326
|
+
UNSARE: {
|
|
1327
|
+
objectClass: "UNSARE",
|
|
1328
|
+
name: "Unsurveyed Area",
|
|
1329
|
+
category: "Depths",
|
|
1330
|
+
displayCategory: "BASE",
|
|
1331
|
+
int1Ref: "I27",
|
|
1332
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 1 } },
|
|
1333
|
+
viewingGroup: 13050,
|
|
1334
|
+
scamin: 0
|
|
1335
|
+
},
|
|
1310
1336
|
// ── Land & Shoreline (INT1 Section C) ───────────────────
|
|
1311
1337
|
LNDARE: {
|
|
1312
1338
|
objectClass: "LNDARE",
|
|
@@ -1459,6 +1485,51 @@ var S52_LOOKUP = {
|
|
|
1459
1485
|
viewingGroup: 12050,
|
|
1460
1486
|
scamin: 45e3
|
|
1461
1487
|
},
|
|
1488
|
+
// Rapids — direct navigation hazard in narrows / river constrictions. S-52
|
|
1489
|
+
// INT1 C24 portrayal: a stylised wavy line representing turbulent water.
|
|
1490
|
+
// We use a blue dashed line (DEPVS) so the feature reads as a water
|
|
1491
|
+
// disturbance — same colour family as the depth tints, distinct from the
|
|
1492
|
+
// black coastal-construction line family. Surfaced by the weekly layer-
|
|
1493
|
+
// coverage audit (issue #575); 4 features in the NOAA corpus, all in
|
|
1494
|
+
// freshwater approaches where a small craft could be swept onto rocks.
|
|
1495
|
+
RAPIDS: {
|
|
1496
|
+
objectClass: "RAPIDS",
|
|
1497
|
+
name: "Rapids",
|
|
1498
|
+
category: "Hazards",
|
|
1499
|
+
displayCategory: "STANDARD",
|
|
1500
|
+
int1Ref: "C24",
|
|
1501
|
+
style: { type: "line", paint: { "line-color": "DEPVS", "line-width": 1, "line-dasharray": [3, 2] } },
|
|
1502
|
+
viewingGroup: 24020,
|
|
1503
|
+
scamin: 45e3
|
|
1504
|
+
},
|
|
1505
|
+
// Dyke — physical earth/stone embankment that confines water. Same
|
|
1506
|
+
// black-line family as SLCONS / FNCLNE / CAUSWY: solid CHBLK so the
|
|
1507
|
+
// structure reads as a hard man-made shoreline modification. INT1 F8.
|
|
1508
|
+
// Surfaced by the weekly layer-coverage audit (issue #575); 13 features
|
|
1509
|
+
// in the NOAA corpus.
|
|
1510
|
+
DYKCON: {
|
|
1511
|
+
objectClass: "DYKCON",
|
|
1512
|
+
name: "Dyke",
|
|
1513
|
+
category: "Land",
|
|
1514
|
+
displayCategory: "STANDARD",
|
|
1515
|
+
int1Ref: "F8",
|
|
1516
|
+
style: { type: "line", paint: { "line-color": "CHBLK", "line-width": 1 } },
|
|
1517
|
+
viewingGroup: 12410,
|
|
1518
|
+
scamin: 22e3
|
|
1519
|
+
},
|
|
1520
|
+
// Causeway — raised road or rail bed across tidal flats or shallow water
|
|
1521
|
+
// (Cape Cod, the Florida Keys, etc.). Black-line family alongside DYKCON.
|
|
1522
|
+
// INT1 F3. Surfaced by the weekly layer-coverage audit (issue #575).
|
|
1523
|
+
CAUSWY: {
|
|
1524
|
+
objectClass: "CAUSWY",
|
|
1525
|
+
name: "Causeway",
|
|
1526
|
+
category: "Infrastructure",
|
|
1527
|
+
displayCategory: "STANDARD",
|
|
1528
|
+
int1Ref: "F3",
|
|
1529
|
+
style: { type: "line", paint: { "line-color": "CHBLK", "line-width": 1 } },
|
|
1530
|
+
viewingGroup: 12410,
|
|
1531
|
+
scamin: 22e3
|
|
1532
|
+
},
|
|
1462
1533
|
TUNNEL: {
|
|
1463
1534
|
objectClass: "TUNNEL",
|
|
1464
1535
|
name: "Tunnel",
|
|
@@ -1656,6 +1727,13 @@ var S52_LOOKUP = {
|
|
|
1656
1727
|
scamin: 9e4
|
|
1657
1728
|
},
|
|
1658
1729
|
LNDELV: {
|
|
1730
|
+
// LineString variant (land contours) renders normally with the line
|
|
1731
|
+
// style below. Point variant ≈ spot elevation ("12m") on land — per
|
|
1732
|
+
// S-52 PresLib convention, marine displays suppress land spot heights
|
|
1733
|
+
// so they don't compete with safety-critical hydrography. The
|
|
1734
|
+
// `auditIgnore` flag silences the weekly layer-coverage audit for the
|
|
1735
|
+
// Point geom-type only; it does not disable the line rendering. See
|
|
1736
|
+
// #575 triage matrix for the full rationale.
|
|
1659
1737
|
objectClass: "LNDELV",
|
|
1660
1738
|
name: "Land Elevation",
|
|
1661
1739
|
category: "Land",
|
|
@@ -1663,7 +1741,8 @@ var S52_LOOKUP = {
|
|
|
1663
1741
|
int1Ref: "C10",
|
|
1664
1742
|
style: { type: "line", paint: { "line-color": "CHBRN", "line-width": 0.5 } },
|
|
1665
1743
|
viewingGroup: 12070,
|
|
1666
|
-
scamin: 22e3
|
|
1744
|
+
scamin: 22e3,
|
|
1745
|
+
auditIgnore: true
|
|
1667
1746
|
},
|
|
1668
1747
|
// ── Hazards (INT1 Section K) ────────────────────────────
|
|
1669
1748
|
OBSTRN: {
|
|
@@ -1749,7 +1828,15 @@ var S52_LOOKUP = {
|
|
|
1749
1828
|
int1Ref: "J1",
|
|
1750
1829
|
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0.15 } },
|
|
1751
1830
|
viewingGroup: 14010,
|
|
1752
|
-
scamin: 22e3
|
|
1831
|
+
scamin: 22e3,
|
|
1832
|
+
// Point variant (NOAA encodes 2,709 SBDARE Points) is rendered as a
|
|
1833
|
+
// seabed-quality text label ("S"/"M"/"R" per S-52 INT1 J) via the
|
|
1834
|
+
// labels.ts path — see layer-builder.ts SBDARE special-case routing.
|
|
1835
|
+
// The layer-coverage audit script only inspects MapLibre layer filters
|
|
1836
|
+
// from buildLayersForClass(), so it can't see that the Point variant
|
|
1837
|
+
// is in fact rendered. Matches the SOUNDG precedent (auditIgnore +
|
|
1838
|
+
// text-via-labels.ts rendering — see s52-lookup.ts SOUNDG entry).
|
|
1839
|
+
auditIgnore: true
|
|
1753
1840
|
},
|
|
1754
1841
|
WEDKLP: {
|
|
1755
1842
|
// Translucent green fill suggests kelp/vegetation; Point variant (the
|
|
@@ -1766,6 +1853,23 @@ var S52_LOOKUP = {
|
|
|
1766
1853
|
viewingGroup: 14010,
|
|
1767
1854
|
scamin: 22e3
|
|
1768
1855
|
},
|
|
1856
|
+
// Oil barrier — a deployed containment boom corralling spilled oil or
|
|
1857
|
+
// marking the perimeter of a spill-response operation. Operational
|
|
1858
|
+
// hazard: small craft must avoid crossing the line (and the polluted
|
|
1859
|
+
// water it contains). S-52 has no formal INT1 entry, but PresLib
|
|
1860
|
+
// convention for spill-response gear is magenta — matches the rest of
|
|
1861
|
+
// the regulated/operational-area portrayal family. Dashed line so it
|
|
1862
|
+
// reads as a temporary/operational marker rather than a permanent
|
|
1863
|
+
// structure. Surfaced by the weekly layer-coverage audit (issue #575).
|
|
1864
|
+
OILBAR: {
|
|
1865
|
+
objectClass: "OILBAR",
|
|
1866
|
+
name: "Oil Barrier",
|
|
1867
|
+
category: "Hazards",
|
|
1868
|
+
displayCategory: "STANDARD",
|
|
1869
|
+
style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 1, "line-dasharray": [4, 2] } },
|
|
1870
|
+
viewingGroup: 24020,
|
|
1871
|
+
scamin: 45e3
|
|
1872
|
+
},
|
|
1769
1873
|
// ── Regulated Areas (INT1 Section N) ────────────────────
|
|
1770
1874
|
ACHARE: {
|
|
1771
1875
|
// Anchorages are conventionally rendered as a subtle filled region in
|
|
@@ -1939,6 +2043,196 @@ var S52_LOOKUP = {
|
|
|
1939
2043
|
viewingGroup: 26050,
|
|
1940
2044
|
scamin: 9e4
|
|
1941
2045
|
},
|
|
2046
|
+
// ── Jurisdictional zones (INT1 Section N) ───────────────
|
|
2047
|
+
// ADMARE / COSARE / CONZNE / EXEZNE / TESARE share an S-52 portrayal
|
|
2048
|
+
// family: magenta dashed boundary, no fill. These are vast offshore
|
|
2049
|
+
// polygons (EEZ extends 200nm, contiguous zone 24nm) where a flood fill
|
|
2050
|
+
// — even at 0.05 opacity — would visibly tint half the chart and stack
|
|
2051
|
+
// with overlapping jurisdictions into a purple wash. Same boundary-only
|
|
2052
|
+
// pattern as TESARE / RESARE / CTNARE via LINE_FROM_POLYGON_LAYERS.
|
|
2053
|
+
// Surfaced by the weekly layer-coverage audit (issue #575 rows 8–15).
|
|
2054
|
+
ADMARE: {
|
|
2055
|
+
// Administration area — state/national waters, the broadest
|
|
2056
|
+
// jurisdictional polygon class. INT1 N40 maps it into the same
|
|
2057
|
+
// magenta-dashed family as territorial-sea / EEZ / contiguous-zone.
|
|
2058
|
+
objectClass: "ADMARE",
|
|
2059
|
+
name: "Administration Area",
|
|
2060
|
+
category: "Regulated Areas",
|
|
2061
|
+
displayCategory: "STANDARD",
|
|
2062
|
+
int1Ref: "N40",
|
|
2063
|
+
style: {
|
|
2064
|
+
type: "line",
|
|
2065
|
+
paint: {
|
|
2066
|
+
"line-color": "CHMGD",
|
|
2067
|
+
"line-width": 1,
|
|
2068
|
+
"line-dasharray": [6, 4],
|
|
2069
|
+
"line-opacity": 0.6
|
|
2070
|
+
}
|
|
2071
|
+
},
|
|
2072
|
+
viewingGroup: 26010,
|
|
2073
|
+
scamin: 9e4
|
|
2074
|
+
},
|
|
2075
|
+
COSARE: {
|
|
2076
|
+
// Coastal area — jurisdictional polygon delineating a coastal-state
|
|
2077
|
+
// administrative region. Previously suppressed via META_LAYERS in
|
|
2078
|
+
// style-generator.ts; promoted here to render as a magenta dashed
|
|
2079
|
+
// boundary alongside the rest of the N-section jurisdiction family.
|
|
2080
|
+
objectClass: "COSARE",
|
|
2081
|
+
name: "Coastal Area",
|
|
2082
|
+
category: "Regulated Areas",
|
|
2083
|
+
displayCategory: "STANDARD",
|
|
2084
|
+
int1Ref: "N40",
|
|
2085
|
+
style: {
|
|
2086
|
+
type: "line",
|
|
2087
|
+
paint: {
|
|
2088
|
+
"line-color": "CHMGD",
|
|
2089
|
+
"line-width": 1,
|
|
2090
|
+
"line-dasharray": [6, 4],
|
|
2091
|
+
"line-opacity": 0.6
|
|
2092
|
+
}
|
|
2093
|
+
},
|
|
2094
|
+
viewingGroup: 26010,
|
|
2095
|
+
scamin: 9e4
|
|
2096
|
+
},
|
|
2097
|
+
CONZNE: {
|
|
2098
|
+
// Contiguous Zone — 24nm offshore boundary. Same magenta dashed
|
|
2099
|
+
// portrayal as territorial sea; boundary-only because the polygon is
|
|
2100
|
+
// vast and a flood fill would obscure huge swathes of chart data.
|
|
2101
|
+
objectClass: "CONZNE",
|
|
2102
|
+
name: "Contiguous Zone",
|
|
2103
|
+
category: "Regulated Areas",
|
|
2104
|
+
displayCategory: "STANDARD",
|
|
2105
|
+
int1Ref: "N44",
|
|
2106
|
+
style: {
|
|
2107
|
+
type: "line",
|
|
2108
|
+
paint: {
|
|
2109
|
+
"line-color": "CHMGD",
|
|
2110
|
+
"line-width": 1,
|
|
2111
|
+
"line-dasharray": [6, 4],
|
|
2112
|
+
"line-opacity": 0.6
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
viewingGroup: 26010,
|
|
2116
|
+
scamin: 9e4
|
|
2117
|
+
},
|
|
2118
|
+
EXEZNE: {
|
|
2119
|
+
// Exclusive Economic Zone — 200nm offshore boundary. Largest of the
|
|
2120
|
+
// jurisdictional polygons; flood-filling even at 0.05 opacity would
|
|
2121
|
+
// tint half the chart at coastal zooms. Boundary-only is the only
|
|
2122
|
+
// sensible portrayal at scale.
|
|
2123
|
+
objectClass: "EXEZNE",
|
|
2124
|
+
name: "Exclusive Economic Zone",
|
|
2125
|
+
category: "Regulated Areas",
|
|
2126
|
+
displayCategory: "STANDARD",
|
|
2127
|
+
int1Ref: "N47",
|
|
2128
|
+
style: {
|
|
2129
|
+
type: "line",
|
|
2130
|
+
paint: {
|
|
2131
|
+
"line-color": "CHMGD",
|
|
2132
|
+
"line-width": 1,
|
|
2133
|
+
"line-dasharray": [6, 4],
|
|
2134
|
+
"line-opacity": 0.6
|
|
2135
|
+
}
|
|
2136
|
+
},
|
|
2137
|
+
viewingGroup: 26010,
|
|
2138
|
+
scamin: 9e4
|
|
2139
|
+
},
|
|
2140
|
+
// ── IMO routing measures (INT1 Section M) ───────────────
|
|
2141
|
+
// TSEZNE / DWRTPT / DWRTCL / TWRTPT are IMO-adopted routing measures
|
|
2142
|
+
// (Traffic Separation Scheme zones, deepwater routes, two-way routes).
|
|
2143
|
+
// S-52 portrayal: magenta dashed boundary for polygons, dashed centerline
|
|
2144
|
+
// for the LineString. The INT1 M-section spec calls for additional
|
|
2145
|
+
// diagonal hatching (TSSCRS01 sprite) on TSEZNE and "DW" centroid text
|
|
2146
|
+
// on DWRTPT — both are deferred follow-ups (TSSCRS01 not in the atlas
|
|
2147
|
+
// sprite set; centroid text belongs in labels.ts). Boundary visibility
|
|
2148
|
+
// is the priority cue here; routing-text augmentation comes later.
|
|
2149
|
+
TSEZNE: {
|
|
2150
|
+
// Traffic Separation Zone — the buffer band between opposing lanes of
|
|
2151
|
+
// a TSS. S-52 INT1 M14 calls for T-pattern diagonal hatching
|
|
2152
|
+
// (TSSCRS01) but that sprite isn't in our generated atlas; fall back
|
|
2153
|
+
// to a magenta dashed boundary so the zone is at least visible.
|
|
2154
|
+
objectClass: "TSEZNE",
|
|
2155
|
+
name: "Traffic Separation Zone",
|
|
2156
|
+
category: "Regulated Areas",
|
|
2157
|
+
displayCategory: "STANDARD",
|
|
2158
|
+
int1Ref: "M14",
|
|
2159
|
+
style: {
|
|
2160
|
+
type: "line",
|
|
2161
|
+
paint: {
|
|
2162
|
+
"line-color": "CHMGD",
|
|
2163
|
+
"line-width": 1.2,
|
|
2164
|
+
"line-dasharray": [4, 3],
|
|
2165
|
+
"line-opacity": 0.7
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2168
|
+
viewingGroup: 26010,
|
|
2169
|
+
scamin: 9e4
|
|
2170
|
+
},
|
|
2171
|
+
DWRTPT: {
|
|
2172
|
+
// Deepwater Route Part — polygon segment of an IMO-adopted deepwater
|
|
2173
|
+
// route. S-52 INT1 M27 portrayal includes "DW" centroid text; that's
|
|
2174
|
+
// a labels.ts addition deferred to a follow-up. Boundary-only here.
|
|
2175
|
+
objectClass: "DWRTPT",
|
|
2176
|
+
name: "Deepwater Route Part",
|
|
2177
|
+
category: "Regulated Areas",
|
|
2178
|
+
displayCategory: "STANDARD",
|
|
2179
|
+
int1Ref: "M27",
|
|
2180
|
+
style: {
|
|
2181
|
+
type: "line",
|
|
2182
|
+
paint: {
|
|
2183
|
+
"line-color": "CHMGD",
|
|
2184
|
+
"line-width": 1.2,
|
|
2185
|
+
"line-dasharray": [4, 3],
|
|
2186
|
+
"line-opacity": 0.7
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2189
|
+
viewingGroup: 26010,
|
|
2190
|
+
scamin: 9e4
|
|
2191
|
+
},
|
|
2192
|
+
DWRTCL: {
|
|
2193
|
+
// Deepwater Route Centerline — LineString feature (not a polygon
|
|
2194
|
+
// boundary) running down the middle of a DWRTPT. Same magenta dashed
|
|
2195
|
+
// style as MAGVAR isogonic lines but slightly heavier so the
|
|
2196
|
+
// route-axis cue reads against busy water.
|
|
2197
|
+
objectClass: "DWRTCL",
|
|
2198
|
+
name: "Deepwater Route Centerline",
|
|
2199
|
+
category: "Regulated Areas",
|
|
2200
|
+
displayCategory: "STANDARD",
|
|
2201
|
+
int1Ref: "M27",
|
|
2202
|
+
style: {
|
|
2203
|
+
type: "line",
|
|
2204
|
+
paint: {
|
|
2205
|
+
"line-color": "CHMGD",
|
|
2206
|
+
"line-width": 0.8,
|
|
2207
|
+
"line-dasharray": [6, 3],
|
|
2208
|
+
"line-opacity": 0.7
|
|
2209
|
+
}
|
|
2210
|
+
},
|
|
2211
|
+
viewingGroup: 26010,
|
|
2212
|
+
scamin: 9e4
|
|
2213
|
+
},
|
|
2214
|
+
TWRTPT: {
|
|
2215
|
+
// Two-Way Route Part — polygon segment of an IMO-adopted two-way
|
|
2216
|
+
// route. S-52 INT1 M28 portrayal includes paired arrow centroid
|
|
2217
|
+
// symbols (TWRTPT52 / TWRTPT53 sprites exist in the atlas but are a
|
|
2218
|
+
// labels.ts follow-up). Boundary-only here.
|
|
2219
|
+
objectClass: "TWRTPT",
|
|
2220
|
+
name: "Two-Way Route Part",
|
|
2221
|
+
category: "Regulated Areas",
|
|
2222
|
+
displayCategory: "STANDARD",
|
|
2223
|
+
int1Ref: "M28",
|
|
2224
|
+
style: {
|
|
2225
|
+
type: "line",
|
|
2226
|
+
paint: {
|
|
2227
|
+
"line-color": "CHMGD",
|
|
2228
|
+
"line-width": 1.2,
|
|
2229
|
+
"line-dasharray": [4, 3],
|
|
2230
|
+
"line-opacity": 0.7
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2233
|
+
viewingGroup: 26010,
|
|
2234
|
+
scamin: 9e4
|
|
2235
|
+
},
|
|
1942
2236
|
// ── Infrastructure (INT1 Sections D, F, L) ──────────────
|
|
1943
2237
|
BRIDGE: {
|
|
1944
2238
|
objectClass: "BRIDGE",
|
|
@@ -2247,6 +2541,56 @@ var S52_LOOKUP = {
|
|
|
2247
2541
|
viewingGroup: 12410,
|
|
2248
2542
|
scamin: 22e3
|
|
2249
2543
|
},
|
|
2544
|
+
// Coast Guard station — emergency-response context. Mariners look for
|
|
2545
|
+
// these when navigating unfamiliar coastline or planning a distress-call
|
|
2546
|
+
// contingency. INT1 F62. Symbolised with the same generic tower glyph
|
|
2547
|
+
// (BCNTOW01) used by BUISGL and the CATLMK=17 LNDMRK variant — a
|
|
2548
|
+
// dedicated CGUSTA SVG is a reasonable follow-up but not required for
|
|
2549
|
+
// the structural-presence cue. Surfaced by the weekly layer-coverage
|
|
2550
|
+
// audit (issue #575); 17 Point features in the NOAA corpus.
|
|
2551
|
+
CGUSTA: {
|
|
2552
|
+
objectClass: "CGUSTA",
|
|
2553
|
+
name: "Coast Guard Station",
|
|
2554
|
+
category: "Infrastructure",
|
|
2555
|
+
displayCategory: "OTHER",
|
|
2556
|
+
int1Ref: "F62",
|
|
2557
|
+
style: { type: "symbol", paint: {}, layout: { "icon-size": 0.6, "icon-allow-overlap": true }, iconImage: "BCNTOW01" },
|
|
2558
|
+
viewingGroup: 12410,
|
|
2559
|
+
scamin: 22e3
|
|
2560
|
+
},
|
|
2561
|
+
// "New Object" — S-57 placeholder class for features the issuing HO has
|
|
2562
|
+
// identified but not yet classified into a more specific class. S-52
|
|
2563
|
+
// PresLib does NOT formally define a portrayal because the class is by
|
|
2564
|
+
// construction not-yet-portrayable, so any distinctive symbol is
|
|
2565
|
+
// permissible — we use a vivid magenta-filled circle with a bold black
|
|
2566
|
+
// stroke so a mariner sees clearly that "there is something unclassified
|
|
2567
|
+
// here." Hiding this class would amount to hiding the HO's own
|
|
2568
|
+
// uncertainty marker, which is the exact opposite of what a chart user
|
|
2569
|
+
// needs. displayCategory: STANDARD (not OTHER) so a default-display
|
|
2570
|
+
// mariner cannot accidentally suppress it; scamin: 0 so the marker
|
|
2571
|
+
// stays visible at every scale band. Surfaced by the weekly layer-
|
|
2572
|
+
// coverage audit (issue #575); 8 Point features in the NOAA corpus.
|
|
2573
|
+
// SAFETY CONTRACT: any future "clean up unused magenta point classes"
|
|
2574
|
+
// pass MUST NOT delete or hide this entry — read the comment block on
|
|
2575
|
+
// issue #575 row 22 before touching.
|
|
2576
|
+
NEWOBJ: {
|
|
2577
|
+
objectClass: "NEWOBJ",
|
|
2578
|
+
name: "New Object (Unclassified)",
|
|
2579
|
+
category: "Hazards",
|
|
2580
|
+
displayCategory: "STANDARD",
|
|
2581
|
+
style: {
|
|
2582
|
+
type: "circle",
|
|
2583
|
+
paint: {
|
|
2584
|
+
"circle-color": "CHMGD",
|
|
2585
|
+
"circle-radius": 5,
|
|
2586
|
+
"circle-stroke-color": "CHBLK",
|
|
2587
|
+
"circle-stroke-width": 1.2,
|
|
2588
|
+
"circle-opacity": 0.85
|
|
2589
|
+
}
|
|
2590
|
+
},
|
|
2591
|
+
viewingGroup: 24020,
|
|
2592
|
+
scamin: 0
|
|
2593
|
+
},
|
|
2250
2594
|
// ── Overhead & Underground (INT1 Section L) ─────────────
|
|
2251
2595
|
// Overhead cable — S-52 INT1 L31 portrayal: solid thin magenta line
|
|
2252
2596
|
// (not dashed) because overhead cables are an in-air navigation hazard
|
|
@@ -2298,6 +2642,11 @@ var S52_LOOKUP = {
|
|
|
2298
2642
|
// when the sprite image is missing; `fill-pattern` here is the default
|
|
2299
2643
|
// used if the conditional expression isn't applied for some reason
|
|
2300
2644
|
// (e.g. a consumer that bypasses `resolvePaint`).
|
|
2645
|
+
//
|
|
2646
|
+
// Polygon (vegetation fill) is the primary variant. The Point variant
|
|
2647
|
+
// ≈ a single marker tree; sub-marine-relevance, so `auditIgnore`
|
|
2648
|
+
// silences the audit for the Point geom-type without touching the
|
|
2649
|
+
// Polygon rendering. See #575 triage matrix.
|
|
2301
2650
|
style: {
|
|
2302
2651
|
type: "fill",
|
|
2303
2652
|
paint: { "fill-color": "LANDF", "fill-opacity": 0.6, "fill-pattern": "VEGATN03" }
|
|
@@ -2305,7 +2654,8 @@ var S52_LOOKUP = {
|
|
|
2305
2654
|
hasConditional: true,
|
|
2306
2655
|
conditionalAttributes: ["CATVEG"],
|
|
2307
2656
|
viewingGroup: 12050,
|
|
2308
|
-
scamin: 22e3
|
|
2657
|
+
scamin: 22e3,
|
|
2658
|
+
auditIgnore: true
|
|
2309
2659
|
},
|
|
2310
2660
|
SEAARE: {
|
|
2311
2661
|
// Cross-band water base. SEAARE polygons tile the sea below the
|
|
@@ -2484,6 +2834,42 @@ var S52_LOOKUP = {
|
|
|
2484
2834
|
viewingGroup: 23010,
|
|
2485
2835
|
scamin: 0
|
|
2486
2836
|
},
|
|
2837
|
+
// ── Audit-ignored stubs (non-metadata) ──────────────────
|
|
2838
|
+
// Classes whose non-rendering is a deliberate S-52 portrayal choice
|
|
2839
|
+
// rather than a real gap, but which aren't S-57 metadata. Each is
|
|
2840
|
+
// present here only so the weekly layer-coverage audit (#575) stops
|
|
2841
|
+
// flagging it as missing. Paint is a transparent fill — no visible
|
|
2842
|
+
// output. Swapping `auditIgnore: true` for a real style is a one-line
|
|
2843
|
+
// change if the call ever needs to be reversed.
|
|
2844
|
+
SLOTOP: {
|
|
2845
|
+
// Top of slope — land terrain feature, low marine relevance.
|
|
2846
|
+
objectClass: "SLOTOP",
|
|
2847
|
+
name: "Top of Slope",
|
|
2848
|
+
category: "Land",
|
|
2849
|
+
displayCategory: "OTHER",
|
|
2850
|
+
int1Ref: "C12",
|
|
2851
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2852
|
+
auditIgnore: true
|
|
2853
|
+
},
|
|
2854
|
+
SNDWAV: {
|
|
2855
|
+
// Sand waves — niche bathymetric texture feature, not a hazard.
|
|
2856
|
+
objectClass: "SNDWAV",
|
|
2857
|
+
name: "Sand Waves",
|
|
2858
|
+
category: "Depths",
|
|
2859
|
+
displayCategory: "OTHER",
|
|
2860
|
+
int1Ref: "J14",
|
|
2861
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2862
|
+
auditIgnore: true
|
|
2863
|
+
},
|
|
2864
|
+
CTRPNT: {
|
|
2865
|
+
// Centerline point — geometric helper, not a feature on the chart.
|
|
2866
|
+
objectClass: "CTRPNT",
|
|
2867
|
+
name: "Centerline Point",
|
|
2868
|
+
category: "Meta",
|
|
2869
|
+
displayCategory: "OTHER",
|
|
2870
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2871
|
+
auditIgnore: true
|
|
2872
|
+
},
|
|
2487
2873
|
// ── Meta (hidden by default) ────────────────────────────
|
|
2488
2874
|
// M_* and DSID are S-57 metadata classes — they describe the dataset
|
|
2489
2875
|
// (coverage extent, survey reliability, source publication, etc.) and
|
|
@@ -2522,6 +2908,17 @@ var S52_LOOKUP = {
|
|
|
2522
2908
|
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2523
2909
|
auditIgnore: true
|
|
2524
2910
|
},
|
|
2911
|
+
M_SDAT: {
|
|
2912
|
+
// Sounding Datum — S-57 metadata describing the vertical datum used
|
|
2913
|
+
// for soundings (typically LAT). Click-query attribute carrier; no
|
|
2914
|
+
// visible shape on its own.
|
|
2915
|
+
objectClass: "M_SDAT",
|
|
2916
|
+
name: "Sounding Datum",
|
|
2917
|
+
category: "Meta",
|
|
2918
|
+
displayCategory: "META",
|
|
2919
|
+
style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
|
|
2920
|
+
auditIgnore: true
|
|
2921
|
+
},
|
|
2525
2922
|
M_SREL: {
|
|
2526
2923
|
objectClass: "M_SREL",
|
|
2527
2924
|
name: "Survey Reliability",
|
|
@@ -2561,6 +2958,16 @@ var S52_DRAW_ORDER = [
|
|
|
2561
2958
|
// paired paint-spec change (fill-opacity 0 → 1, color → DEPDW).
|
|
2562
2959
|
"SEAARE",
|
|
2563
2960
|
"DEPARE",
|
|
2961
|
+
// 2a. Unsurveyed area — opaque grey wash painted ON TOP of any DEPARE
|
|
2962
|
+
// tile within its extent so depth-tier colours can never imply the area
|
|
2963
|
+
// is surveyed-and-safe. If both DEPARE and UNSARE cover the same pixel,
|
|
2964
|
+
// UNSARE wins on purpose: the hydrographic office classified the polygon
|
|
2965
|
+
// as unsurveyed, so any depth data within it is suspect at best. Sits
|
|
2966
|
+
// below dredged / swept (which represent active survey work) so those
|
|
2967
|
+
// appear if they overlap UNSARE — but in practice they don't, and the
|
|
2968
|
+
// ordering is the safer fallback. Surfaced by the weekly layer-coverage
|
|
2969
|
+
// audit (issue #575); see s52-lookup.ts UNSARE entry for context.
|
|
2970
|
+
"UNSARE",
|
|
2564
2971
|
// 3. Dredged / swept areas (fill)
|
|
2565
2972
|
"DRGARE",
|
|
2566
2973
|
"SWPARE",
|
|
@@ -2598,6 +3005,17 @@ var S52_DRAW_ORDER = [
|
|
|
2598
3005
|
"LOGPON",
|
|
2599
3006
|
"CBLARE",
|
|
2600
3007
|
"PIPARE",
|
|
3008
|
+
// 6a. Jurisdictional zones + IMO routing measures (boundary-only via
|
|
3009
|
+
// LINE_FROM_POLYGON_LAYERS). Slotted at the end of the regulated-area
|
|
3010
|
+
// band so the dashed magenta boundaries paint above the fill family
|
|
3011
|
+
// but below shoreline / depth-contour lines. Closes #575 rows 8–15.
|
|
3012
|
+
"ADMARE",
|
|
3013
|
+
"COSARE",
|
|
3014
|
+
"CONZNE",
|
|
3015
|
+
"EXEZNE",
|
|
3016
|
+
"TSEZNE",
|
|
3017
|
+
"DWRTPT",
|
|
3018
|
+
"TWRTPT",
|
|
2601
3019
|
// 7. Infrastructure areas (fill)
|
|
2602
3020
|
"PONTON",
|
|
2603
3021
|
"HULKES",
|
|
@@ -2607,8 +3025,13 @@ var S52_DRAW_ORDER = [
|
|
|
2607
3025
|
"PYLONS",
|
|
2608
3026
|
"FORSTC",
|
|
2609
3027
|
// 8. Coastline / shore (line)
|
|
3028
|
+
// DYKCON (dyke) and CAUSWY (causeway) join the SLCONS family — solid
|
|
3029
|
+
// black lines representing man-made shoreline modifications. Drawn in
|
|
3030
|
+
// the same band so they layer consistently with adjacent SLCONS edges.
|
|
2610
3031
|
"COALNE",
|
|
2611
3032
|
"SLCONS",
|
|
3033
|
+
"DYKCON",
|
|
3034
|
+
"CAUSWY",
|
|
2612
3035
|
"FNCLNE",
|
|
2613
3036
|
// 9. Depth contours (line)
|
|
2614
3037
|
"DEPCNT",
|
|
@@ -2619,13 +3042,20 @@ var S52_DRAW_ORDER = [
|
|
|
2619
3042
|
// routing and aids-to-nav win the visual hierarchy).
|
|
2620
3043
|
"MAGVAR",
|
|
2621
3044
|
// 10. Navigation lines (line)
|
|
3045
|
+
// RAPIDS and OILBAR are direct/operational water hazards. RAPIDS uses
|
|
3046
|
+
// the depth-blue palette (DEPVS dashed) so the feature reads as a
|
|
3047
|
+
// water-surface disturbance; OILBAR uses CHMGD to fit the operational-
|
|
3048
|
+
// hazard family alongside cables / pipelines.
|
|
2622
3049
|
"NAVLNE",
|
|
2623
3050
|
"TSSBND",
|
|
3051
|
+
"DWRTCL",
|
|
2624
3052
|
"ROADWY",
|
|
2625
3053
|
"RAILWY",
|
|
2626
3054
|
"TUNNEL",
|
|
2627
3055
|
"FERYRT",
|
|
2628
3056
|
"RECTRC",
|
|
3057
|
+
"RAPIDS",
|
|
3058
|
+
"OILBAR",
|
|
2629
3059
|
"PIPSOL",
|
|
2630
3060
|
"PIPOHD",
|
|
2631
3061
|
"CBLSUB",
|
|
@@ -2634,6 +3064,11 @@ var S52_DRAW_ORDER = [
|
|
|
2634
3064
|
"DAMCON",
|
|
2635
3065
|
"BERTHS",
|
|
2636
3066
|
// 11. Point features
|
|
3067
|
+
// CGUSTA (coast guard station, emergency-context) sits with the building
|
|
3068
|
+
// / landmark family — a structural-presence cue rather than a routing
|
|
3069
|
+
// signal. NEWOBJ ("something here we haven't classified yet") goes at
|
|
3070
|
+
// the END of the point list so the distinctive magenta marker stays on
|
|
3071
|
+
// top of neighbouring point features and cannot be visually swallowed.
|
|
2637
3072
|
"SOUNDG",
|
|
2638
3073
|
"MORFAC",
|
|
2639
3074
|
"PILPNT",
|
|
@@ -2647,6 +3082,8 @@ var S52_DRAW_ORDER = [
|
|
|
2647
3082
|
"CRANES",
|
|
2648
3083
|
"DISMAR",
|
|
2649
3084
|
"ACHBRT",
|
|
3085
|
+
"CGUSTA",
|
|
3086
|
+
"NEWOBJ",
|
|
2650
3087
|
// 12. Nav aids (on top)
|
|
2651
3088
|
"FOGSIG",
|
|
2652
3089
|
"RTPBCN",
|
|
@@ -5264,6 +5701,7 @@ var SYMBOL_MAP = {
|
|
|
5264
5701
|
PILBOP: "PILBOP02",
|
|
5265
5702
|
SISTAT: "SISTAT02",
|
|
5266
5703
|
SMCFAC: "ACHARE02",
|
|
5704
|
+
CGUSTA: "BCNTOW01",
|
|
5267
5705
|
// Land infrastructure
|
|
5268
5706
|
SILTNK: "TNKCON02",
|
|
5269
5707
|
BUISGL: "BCNTOW01",
|
|
@@ -5920,6 +6358,237 @@ function buildSeabedLabelExpression() {
|
|
|
5920
6358
|
const natsurJoined = ["slice", natsurParts, 1];
|
|
5921
6359
|
return ["concat", natquaPrefix, natsurJoined];
|
|
5922
6360
|
}
|
|
6361
|
+
function underseaFeatureNameMatch(objnam) {
|
|
6362
|
+
const terms = [
|
|
6363
|
+
"Canyon",
|
|
6364
|
+
"Seamount",
|
|
6365
|
+
"Ridge",
|
|
6366
|
+
"Basin",
|
|
6367
|
+
"Swell",
|
|
6368
|
+
"Knoll",
|
|
6369
|
+
"Shoal",
|
|
6370
|
+
"Ledge",
|
|
6371
|
+
"Bank",
|
|
6372
|
+
"Valley",
|
|
6373
|
+
"Spur",
|
|
6374
|
+
"Escarpment"
|
|
6375
|
+
];
|
|
6376
|
+
return ["any", ...terms.map((t) => [">=", ["index-of", t, objnam], 0])];
|
|
6377
|
+
}
|
|
6378
|
+
var CATSEA_RELIEF_CODES = [
|
|
6379
|
+
2,
|
|
6380
|
+
// gat
|
|
6381
|
+
3,
|
|
6382
|
+
// bank
|
|
6383
|
+
4,
|
|
6384
|
+
// deep
|
|
6385
|
+
6,
|
|
6386
|
+
// trench
|
|
6387
|
+
7,
|
|
6388
|
+
// basin
|
|
6389
|
+
8,
|
|
6390
|
+
// mud flats
|
|
6391
|
+
9,
|
|
6392
|
+
// reef
|
|
6393
|
+
10,
|
|
6394
|
+
// ledge
|
|
6395
|
+
11,
|
|
6396
|
+
// canyon
|
|
6397
|
+
13,
|
|
6398
|
+
// shoal
|
|
6399
|
+
14,
|
|
6400
|
+
// knoll
|
|
6401
|
+
15,
|
|
6402
|
+
// ridge
|
|
6403
|
+
16,
|
|
6404
|
+
// seamount
|
|
6405
|
+
17,
|
|
6406
|
+
// pinnacle
|
|
6407
|
+
18,
|
|
6408
|
+
// abyssal plain
|
|
6409
|
+
19,
|
|
6410
|
+
// plateau
|
|
6411
|
+
20,
|
|
6412
|
+
// spur
|
|
6413
|
+
21,
|
|
6414
|
+
// shelf
|
|
6415
|
+
22,
|
|
6416
|
+
// trough
|
|
6417
|
+
23,
|
|
6418
|
+
// saddle
|
|
6419
|
+
24,
|
|
6420
|
+
// abyssal hills
|
|
6421
|
+
25,
|
|
6422
|
+
// apron
|
|
6423
|
+
26,
|
|
6424
|
+
// archipelagic apron
|
|
6425
|
+
27,
|
|
6426
|
+
// borderland
|
|
6427
|
+
28,
|
|
6428
|
+
// continental margin
|
|
6429
|
+
29,
|
|
6430
|
+
// continental rise
|
|
6431
|
+
30,
|
|
6432
|
+
// escarpment
|
|
6433
|
+
31,
|
|
6434
|
+
// fan
|
|
6435
|
+
32,
|
|
6436
|
+
// fracture zone
|
|
6437
|
+
33,
|
|
6438
|
+
// gap
|
|
6439
|
+
34,
|
|
6440
|
+
// guyot
|
|
6441
|
+
35,
|
|
6442
|
+
// hill
|
|
6443
|
+
36,
|
|
6444
|
+
// hole
|
|
6445
|
+
37,
|
|
6446
|
+
// hollow
|
|
6447
|
+
39,
|
|
6448
|
+
// levee
|
|
6449
|
+
40,
|
|
6450
|
+
// median valley
|
|
6451
|
+
41,
|
|
6452
|
+
// moat
|
|
6453
|
+
42,
|
|
6454
|
+
// mountains
|
|
6455
|
+
43,
|
|
6456
|
+
// peak
|
|
6457
|
+
44,
|
|
6458
|
+
// province
|
|
6459
|
+
45,
|
|
6460
|
+
// rise
|
|
6461
|
+
47,
|
|
6462
|
+
// seamount chain
|
|
6463
|
+
48,
|
|
6464
|
+
// shelf-edge
|
|
6465
|
+
49,
|
|
6466
|
+
// sill
|
|
6467
|
+
50,
|
|
6468
|
+
// slope
|
|
6469
|
+
51,
|
|
6470
|
+
// terrace
|
|
6471
|
+
52
|
|
6472
|
+
// valley
|
|
6473
|
+
];
|
|
6474
|
+
function buildSeaAreaTierExpression() {
|
|
6475
|
+
const catsea = ["to-number", ["coalesce", ["get", "CATSEA"], ["get", "catsea"], 0]];
|
|
6476
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6477
|
+
return [
|
|
6478
|
+
"case",
|
|
6479
|
+
// Tier 4 — Anchorage detail (before all promotions so "Boston Harbor,
|
|
6480
|
+
// MA …" anchorage compound names don't bubble up).
|
|
6481
|
+
[">=", ["index-of", "Anchorage Area", objnam], 0],
|
|
6482
|
+
4,
|
|
6483
|
+
// Tier 5 — undersea relief, by authoritative CATSEA code first
|
|
6484
|
+
// (Georges Bank=3, Georges Shoal=13, …), then by name for
|
|
6485
|
+
// uncategorised features. Before promotion shims; see
|
|
6486
|
+
// underseaFeatureNameMatch.
|
|
6487
|
+
["match", catsea, CATSEA_RELIEF_CODES, true, false],
|
|
6488
|
+
5,
|
|
6489
|
+
underseaFeatureNameMatch(objnam),
|
|
6490
|
+
5,
|
|
6491
|
+
// Tier 3 by authoritative CATSEA: bay / narrows.
|
|
6492
|
+
["match", catsea, [5, 12], true, false],
|
|
6493
|
+
3,
|
|
6494
|
+
// OBJNAM promotion shims (CATSEA has no ocean/sea/gulf/sound/strait
|
|
6495
|
+
// codes, so names are the only signal for the headline tiers):
|
|
6496
|
+
[">=", ["index-of", "Atlantic Ocean", objnam], 0],
|
|
6497
|
+
1,
|
|
6498
|
+
[">=", ["index-of", "Pacific Ocean", objnam], 0],
|
|
6499
|
+
1,
|
|
6500
|
+
[">=", ["index-of", "Arctic Ocean", objnam], 0],
|
|
6501
|
+
1,
|
|
6502
|
+
[">=", ["index-of", "Gulf of", objnam], 0],
|
|
6503
|
+
2,
|
|
6504
|
+
[">=", ["index-of", " Bay", objnam], 0],
|
|
6505
|
+
3,
|
|
6506
|
+
[">=", ["index-of", " Sound", objnam], 0],
|
|
6507
|
+
3,
|
|
6508
|
+
[">=", ["index-of", " Strait", objnam], 0],
|
|
6509
|
+
3,
|
|
6510
|
+
[">=", ["index-of", " Sea", objnam], 0],
|
|
6511
|
+
2,
|
|
6512
|
+
5
|
|
6513
|
+
];
|
|
6514
|
+
}
|
|
6515
|
+
function buildBuiltUpAreaTierExpression() {
|
|
6516
|
+
const catbua = ["coalesce", ["get", "CATBUA"], ["get", "catbua"], 0];
|
|
6517
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6518
|
+
const nameLen = ["length", objnam];
|
|
6519
|
+
return [
|
|
6520
|
+
"case",
|
|
6521
|
+
["==", ["to-number", catbua], 1],
|
|
6522
|
+
1,
|
|
6523
|
+
["==", ["to-number", catbua], 5],
|
|
6524
|
+
2,
|
|
6525
|
+
["<=", nameLen, 8],
|
|
6526
|
+
1,
|
|
6527
|
+
["<=", nameLen, 14],
|
|
6528
|
+
2,
|
|
6529
|
+
3
|
|
6530
|
+
];
|
|
6531
|
+
}
|
|
6532
|
+
function buildLandRegionTierExpression() {
|
|
6533
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6534
|
+
const nameLen = ["length", objnam];
|
|
6535
|
+
return [
|
|
6536
|
+
"case",
|
|
6537
|
+
// NOAA also encodes undersea relief names (e.g. "Block Canyon", 12
|
|
6538
|
+
// chars, no comma) as LNDRGN — without this demotion they'd qualify
|
|
6539
|
+
// as tier-1 "major region" and paint at overview zoom.
|
|
6540
|
+
underseaFeatureNameMatch(objnam),
|
|
6541
|
+
2,
|
|
6542
|
+
["all", ["<=", nameLen, 12], ["==", ["index-of", ",", objnam], -1]],
|
|
6543
|
+
1,
|
|
6544
|
+
2
|
|
6545
|
+
];
|
|
6546
|
+
}
|
|
6547
|
+
function buildLandAreaTierExpression() {
|
|
6548
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6549
|
+
return [
|
|
6550
|
+
"case",
|
|
6551
|
+
["<=", ["length", objnam], 12],
|
|
6552
|
+
1,
|
|
6553
|
+
2
|
|
6554
|
+
];
|
|
6555
|
+
}
|
|
6556
|
+
function buildLakeTierExpression() {
|
|
6557
|
+
const catlak = ["coalesce", ["get", "CATLAK"], ["get", "catlak"], 0];
|
|
6558
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6559
|
+
return [
|
|
6560
|
+
"case",
|
|
6561
|
+
["==", ["to-number", catlak], 1],
|
|
6562
|
+
1,
|
|
6563
|
+
["any", ["==", ["to-number", catlak], 2], ["==", ["to-number", catlak], 3]],
|
|
6564
|
+
2,
|
|
6565
|
+
["<=", ["length", objnam], 12],
|
|
6566
|
+
1,
|
|
6567
|
+
2
|
|
6568
|
+
];
|
|
6569
|
+
}
|
|
6570
|
+
function buildRiverTierExpression() {
|
|
6571
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6572
|
+
return [
|
|
6573
|
+
"case",
|
|
6574
|
+
[
|
|
6575
|
+
"all",
|
|
6576
|
+
["<=", ["length", objnam], 15],
|
|
6577
|
+
["==", ["index-of", ",", objnam], -1]
|
|
6578
|
+
],
|
|
6579
|
+
1,
|
|
6580
|
+
2
|
|
6581
|
+
];
|
|
6582
|
+
}
|
|
6583
|
+
function buildAirportTierExpression() {
|
|
6584
|
+
const objnam = ["coalesce", ["get", "OBJNAM"], ""];
|
|
6585
|
+
return [
|
|
6586
|
+
"case",
|
|
6587
|
+
["<=", ["length", objnam], 25],
|
|
6588
|
+
1,
|
|
6589
|
+
2
|
|
6590
|
+
];
|
|
6591
|
+
}
|
|
5923
6592
|
var COLOR = {
|
|
5924
6593
|
WATER: "#9ec1d8",
|
|
5925
6594
|
WATER_DEEP: "#8fb8d0",
|
|
@@ -5940,6 +6609,60 @@ var ORIENTATION_BAND_SORT_KEY = [
|
|
|
5940
6609
|
["slice", ["coalesce", ["get", "_cell_id"], "??9???"], 2, 3],
|
|
5941
6610
|
9
|
|
5942
6611
|
];
|
|
6612
|
+
function orientationLabelLayout(extras = {}) {
|
|
6613
|
+
return {
|
|
6614
|
+
"text-anchor": "center",
|
|
6615
|
+
"text-allow-overlap": false,
|
|
6616
|
+
"text-ignore-placement": false,
|
|
6617
|
+
"text-optional": true,
|
|
6618
|
+
"text-padding": 15,
|
|
6619
|
+
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY,
|
|
6620
|
+
...extras
|
|
6621
|
+
};
|
|
6622
|
+
}
|
|
6623
|
+
function tieredSizeCurve(tierExpr, stops) {
|
|
6624
|
+
const withHolds = [];
|
|
6625
|
+
for (let i = 0; i < stops.length; i++) {
|
|
6626
|
+
if (i > 0) {
|
|
6627
|
+
const [pz, pv] = stops[i - 1];
|
|
6628
|
+
const [nz, nv] = stops[i];
|
|
6629
|
+
if (pv.some((v, t) => v === 0 && nv[t] > 0)) {
|
|
6630
|
+
const hz = Math.round((nz - 0.2) * 10) / 10;
|
|
6631
|
+
if (hz > pz) {
|
|
6632
|
+
const f = (hz - pz) / (nz - pz);
|
|
6633
|
+
withHolds.push([
|
|
6634
|
+
hz,
|
|
6635
|
+
pv.map(
|
|
6636
|
+
(v, t) => v === 0 && nv[t] > 0 ? 0 : Math.round((v + (nv[t] - v) * f) * 10) / 10
|
|
6637
|
+
)
|
|
6638
|
+
]);
|
|
6639
|
+
}
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
withHolds.push(stops[i]);
|
|
6643
|
+
}
|
|
6644
|
+
const expr = ["interpolate", ["linear"], ["zoom"]];
|
|
6645
|
+
for (const [z, vals] of withHolds) {
|
|
6646
|
+
const m = ["match", tierExpr];
|
|
6647
|
+
for (let t = 0; t < vals.length - 1; t++) m.push(t + 1, vals[t]);
|
|
6648
|
+
m.push(vals[vals.length - 1]);
|
|
6649
|
+
expr.push(z, m);
|
|
6650
|
+
}
|
|
6651
|
+
return expr;
|
|
6652
|
+
}
|
|
6653
|
+
var ORIENTATION_HALO_WIDTH = [
|
|
6654
|
+
"interpolate",
|
|
6655
|
+
["linear"],
|
|
6656
|
+
["zoom"],
|
|
6657
|
+
8,
|
|
6658
|
+
1.6,
|
|
6659
|
+
11,
|
|
6660
|
+
1.5,
|
|
6661
|
+
14,
|
|
6662
|
+
1,
|
|
6663
|
+
16,
|
|
6664
|
+
0.7
|
|
6665
|
+
];
|
|
5943
6666
|
var LABEL_BEHAVIOR = {
|
|
5944
6667
|
// Orientation labels — render once across all bands.
|
|
5945
6668
|
SEAARE: "orientation-cross-band",
|
|
@@ -6054,35 +6777,34 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6054
6777
|
}
|
|
6055
6778
|
};
|
|
6056
6779
|
}
|
|
6057
|
-
case "LNDRGN":
|
|
6780
|
+
case "LNDRGN": {
|
|
6781
|
+
const tierExpr = buildLandRegionTierExpression();
|
|
6058
6782
|
return {
|
|
6059
6783
|
type: "symbol",
|
|
6060
6784
|
textField: ["get", "OBJNAM"],
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6785
|
+
// Tier-1 entry moved from z5 to z7: undersea-relief names without a
|
|
6786
|
+
// generic keyword ("Three Dory", "Outer Fall") can't be demoted by
|
|
6787
|
+
// pattern, so the overview/general bands stay clean by holding ALL
|
|
6788
|
+
// LNDRGN labels until coastal zoom. The Carto basemap already labels
|
|
6789
|
+
// major land regions at z5-z6. Columns: t1 major region, t2 local
|
|
6790
|
+
// point (also the default).
|
|
6791
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
6792
|
+
[5, [0, 0]],
|
|
6793
|
+
[7, [11, 0]],
|
|
6794
|
+
[9, [12, 0]],
|
|
6795
|
+
[11, [12, 9]],
|
|
6796
|
+
[13, [11, 11]],
|
|
6797
|
+
[16, [10, 10]]
|
|
6798
|
+
]),
|
|
6072
6799
|
textColor: DARK_BG,
|
|
6073
6800
|
textHaloColor: COLOR.LAND_REGION,
|
|
6074
|
-
textHaloWidth:
|
|
6801
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6075
6802
|
textOffset: [0, 0],
|
|
6076
6803
|
textFont: ["Open Sans Regular"],
|
|
6077
|
-
minzoom:
|
|
6078
|
-
layout:
|
|
6079
|
-
"text-anchor": "center",
|
|
6080
|
-
"text-allow-overlap": false,
|
|
6081
|
-
"text-padding": 6,
|
|
6082
|
-
"text-optional": true,
|
|
6083
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6084
|
-
}
|
|
6804
|
+
minzoom: 5,
|
|
6805
|
+
layout: orientationLabelLayout()
|
|
6085
6806
|
};
|
|
6807
|
+
}
|
|
6086
6808
|
case "LNDMRK":
|
|
6087
6809
|
return {
|
|
6088
6810
|
type: "symbol",
|
|
@@ -6211,97 +6933,97 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6211
6933
|
}
|
|
6212
6934
|
};
|
|
6213
6935
|
}
|
|
6214
|
-
case "SEAARE":
|
|
6936
|
+
case "SEAARE": {
|
|
6937
|
+
const tierExpr = buildSeaAreaTierExpression();
|
|
6215
6938
|
return {
|
|
6216
6939
|
type: "symbol",
|
|
6217
6940
|
textField: ["get", "OBJNAM"],
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
14,
|
|
6229
|
-
|
|
6230
|
-
|
|
6941
|
+
// Default tier (CATSEA=0, uncategorised: canyons, seamounts, fishing
|
|
6942
|
+
// banks etc.) is suppressed at overview/general/coastal scales because
|
|
6943
|
+
// it floods the overview with hundreds of small italic labels that
|
|
6944
|
+
// crowd out the headline Ocean/Sea/Gulf names. It becomes visible only
|
|
6945
|
+
// at approach scale (z9) when the user has zoomed in enough that
|
|
6946
|
+
// individual banks are useful context. Columns: t1 ocean, t2 sea/gulf,
|
|
6947
|
+
// t3 bay/sound, t4 anchorage, default.
|
|
6948
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
6949
|
+
[3, [14, 8, 0, 0, 0]],
|
|
6950
|
+
[5, [16, 13, 9, 0, 0]],
|
|
6951
|
+
[7, [14, 14, 12, 0, 0]],
|
|
6952
|
+
[9, [12, 13, 14, 0, 9]],
|
|
6953
|
+
[11, [10, 12, 13, 0, 11]],
|
|
6954
|
+
[13, [9, 11, 12, 10, 11]],
|
|
6955
|
+
[16, [9, 10, 11, 11, 10]]
|
|
6956
|
+
]),
|
|
6231
6957
|
textColor: DARK_BG,
|
|
6232
6958
|
textHaloColor: COLOR.WATER_DEEP,
|
|
6233
|
-
textHaloWidth:
|
|
6959
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6234
6960
|
textOffset: [0, 0],
|
|
6235
6961
|
textFont: ["Open Sans Italic"],
|
|
6236
6962
|
minzoom: 5,
|
|
6237
|
-
layout: {
|
|
6238
|
-
"text-anchor": "center",
|
|
6239
|
-
"text-allow-overlap": false,
|
|
6240
|
-
"text-padding": 10,
|
|
6241
|
-
"text-optional": true,
|
|
6963
|
+
layout: orientationLabelLayout({
|
|
6242
6964
|
"text-letter-spacing": 0.15,
|
|
6243
6965
|
"text-max-width": 9,
|
|
6244
|
-
|
|
6245
|
-
|
|
6966
|
+
// SEAARE is always-paint, unlike the other orientation classes.
|
|
6967
|
+
// MapLibre places symbols topmost-layer-first, so with
|
|
6968
|
+
// allow-overlap:false the dense sounding field (drawn above)
|
|
6969
|
+
// collision-starves big-water names — observed on staging at z8:
|
|
6970
|
+
// only 2 SEAARE labels survived viewport-wide, with Cape Cod Bay
|
|
6971
|
+
// and Gulf of Maine dropped entirely. Always-paint is safe for
|
|
6972
|
+
// this class because (a) the dominance composition in
|
|
6973
|
+
// fanOrientationLabelByCellBand suppresses cross-band duplicate
|
|
6974
|
+
// copies, and (b) tier sizing zeroes relief/anchorage names at
|
|
6975
|
+
// the zooms where density is a problem. Rivers/lakes/land keep
|
|
6976
|
+
// collision-dedup — their within-band multi-polygon stacking was
|
|
6977
|
+
// the original complaint, and they lack big-water headline value.
|
|
6978
|
+
"text-allow-overlap": true,
|
|
6979
|
+
"text-ignore-placement": true,
|
|
6980
|
+
"text-optional": false,
|
|
6981
|
+
"text-padding": 2
|
|
6982
|
+
})
|
|
6246
6983
|
};
|
|
6247
|
-
|
|
6984
|
+
}
|
|
6985
|
+
case "LAKARE": {
|
|
6986
|
+
const tierExpr = buildLakeTierExpression();
|
|
6248
6987
|
return {
|
|
6249
6988
|
type: "symbol",
|
|
6250
6989
|
textField: ["get", "OBJNAM"],
|
|
6251
|
-
textSize: [
|
|
6252
|
-
|
|
6253
|
-
[
|
|
6254
|
-
[
|
|
6255
|
-
|
|
6256
|
-
10
|
|
6257
|
-
|
|
6258
|
-
12,
|
|
6259
|
-
15,
|
|
6260
|
-
14
|
|
6261
|
-
],
|
|
6990
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
6991
|
+
[7, [9, 0]],
|
|
6992
|
+
[9, [11, 0]],
|
|
6993
|
+
[11, [11, 9]],
|
|
6994
|
+
[13, [10, 11]],
|
|
6995
|
+
[16, [9, 10]]
|
|
6996
|
+
]),
|
|
6262
6997
|
textColor: DARK_BG,
|
|
6263
6998
|
textHaloColor: COLOR.WATER,
|
|
6264
|
-
textHaloWidth:
|
|
6999
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6265
7000
|
textOffset: [0, 0],
|
|
6266
7001
|
textFont: ["Open Sans Italic"],
|
|
6267
|
-
minzoom:
|
|
6268
|
-
layout:
|
|
6269
|
-
"text-anchor": "center",
|
|
6270
|
-
"text-allow-overlap": false,
|
|
6271
|
-
"text-padding": 6,
|
|
6272
|
-
"text-optional": true,
|
|
6273
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6274
|
-
}
|
|
7002
|
+
minzoom: 7,
|
|
7003
|
+
layout: orientationLabelLayout()
|
|
6275
7004
|
};
|
|
6276
|
-
|
|
7005
|
+
}
|
|
7006
|
+
case "RIVERS": {
|
|
7007
|
+
const tierExpr = buildRiverTierExpression();
|
|
6277
7008
|
return {
|
|
6278
7009
|
type: "symbol",
|
|
6279
7010
|
textField: ["get", "OBJNAM"],
|
|
6280
|
-
textSize: [
|
|
6281
|
-
|
|
6282
|
-
[
|
|
6283
|
-
[
|
|
6284
|
-
|
|
6285
|
-
9,
|
|
6286
|
-
|
|
6287
|
-
11,
|
|
6288
|
-
15,
|
|
6289
|
-
13
|
|
6290
|
-
],
|
|
7011
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7012
|
+
[7, [9, 0]],
|
|
7013
|
+
[9, [11, 0]],
|
|
7014
|
+
[11, [11, 9]],
|
|
7015
|
+
[13, [10, 10]],
|
|
7016
|
+
[16, [9, 10]]
|
|
7017
|
+
]),
|
|
6291
7018
|
textColor: DARK_BG,
|
|
6292
7019
|
textHaloColor: COLOR.WATER,
|
|
6293
|
-
textHaloWidth:
|
|
7020
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6294
7021
|
textOffset: [0, 0],
|
|
6295
7022
|
textFont: ["Open Sans Italic"],
|
|
6296
|
-
minzoom:
|
|
6297
|
-
layout:
|
|
6298
|
-
"text-anchor": "center",
|
|
6299
|
-
"text-allow-overlap": false,
|
|
6300
|
-
"text-padding": 4,
|
|
6301
|
-
"text-optional": true,
|
|
6302
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6303
|
-
}
|
|
7023
|
+
minzoom: 7,
|
|
7024
|
+
layout: orientationLabelLayout()
|
|
6304
7025
|
};
|
|
7026
|
+
}
|
|
6305
7027
|
case "CANALS":
|
|
6306
7028
|
return {
|
|
6307
7029
|
type: "symbol",
|
|
@@ -6311,83 +7033,67 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6311
7033
|
["linear"],
|
|
6312
7034
|
["zoom"],
|
|
6313
7035
|
11,
|
|
7036
|
+
10,
|
|
7037
|
+
13,
|
|
6314
7038
|
9,
|
|
6315
|
-
|
|
6316
|
-
|
|
7039
|
+
16,
|
|
7040
|
+
9
|
|
6317
7041
|
],
|
|
6318
7042
|
textColor: DARK_BG,
|
|
6319
7043
|
textHaloColor: COLOR.WATER,
|
|
6320
|
-
textHaloWidth:
|
|
7044
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6321
7045
|
textOffset: [0, 0],
|
|
6322
7046
|
textFont: ["Open Sans Italic"],
|
|
6323
7047
|
minzoom: 11,
|
|
6324
|
-
layout:
|
|
6325
|
-
"text-anchor": "center",
|
|
6326
|
-
"text-allow-overlap": false,
|
|
6327
|
-
"text-padding": 4,
|
|
6328
|
-
"text-optional": true,
|
|
6329
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6330
|
-
}
|
|
7048
|
+
layout: orientationLabelLayout()
|
|
6331
7049
|
};
|
|
6332
|
-
case "LNDARE":
|
|
7050
|
+
case "LNDARE": {
|
|
7051
|
+
const tierExpr = buildLandAreaTierExpression();
|
|
6333
7052
|
return {
|
|
6334
7053
|
type: "symbol",
|
|
6335
7054
|
textField: ["get", "OBJNAM"],
|
|
6336
|
-
textSize: [
|
|
6337
|
-
|
|
6338
|
-
[
|
|
6339
|
-
[
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
15,
|
|
6345
|
-
13
|
|
6346
|
-
],
|
|
7055
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7056
|
+
[5, [8, 0]],
|
|
7057
|
+
[7, [10, 0]],
|
|
7058
|
+
[9, [11, 0]],
|
|
7059
|
+
[11, [11, 9]],
|
|
7060
|
+
[13, [10, 10]],
|
|
7061
|
+
[16, [9, 10]]
|
|
7062
|
+
]),
|
|
6347
7063
|
textColor: DARK_BG,
|
|
6348
7064
|
textHaloColor: COLOR.LAND_NATURAL,
|
|
6349
|
-
textHaloWidth:
|
|
7065
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6350
7066
|
textOffset: [0, 0],
|
|
6351
7067
|
textFont: ["Open Sans Regular"],
|
|
6352
|
-
minzoom:
|
|
6353
|
-
layout:
|
|
6354
|
-
"text-anchor": "center",
|
|
6355
|
-
"text-allow-overlap": false,
|
|
6356
|
-
"text-padding": 6,
|
|
6357
|
-
"text-optional": true,
|
|
6358
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6359
|
-
}
|
|
7068
|
+
minzoom: 5,
|
|
7069
|
+
layout: orientationLabelLayout()
|
|
6360
7070
|
};
|
|
6361
|
-
|
|
7071
|
+
}
|
|
7072
|
+
case "BUAARE": {
|
|
7073
|
+
const tierExpr = buildBuiltUpAreaTierExpression();
|
|
6362
7074
|
return {
|
|
6363
7075
|
type: "symbol",
|
|
6364
7076
|
textField: ["get", "OBJNAM"],
|
|
6365
|
-
textSize: [
|
|
6366
|
-
|
|
6367
|
-
[
|
|
6368
|
-
[
|
|
6369
|
-
|
|
6370
|
-
11,
|
|
6371
|
-
10,
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
15
|
|
6375
|
-
],
|
|
7077
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7078
|
+
[3, [11, 0, 0]],
|
|
7079
|
+
[5, [13, 0, 0]],
|
|
7080
|
+
[7, [13, 9, 0]],
|
|
7081
|
+
[9, [12, 12, 0]],
|
|
7082
|
+
[11, [11, 12, 10]],
|
|
7083
|
+
[13, [10, 11, 12]],
|
|
7084
|
+
[16, [10, 10, 11]]
|
|
7085
|
+
]),
|
|
6376
7086
|
textColor: DARK_BG,
|
|
6377
7087
|
textHaloColor: COLOR.CITY,
|
|
6378
|
-
textHaloWidth:
|
|
7088
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6379
7089
|
textOffset: [0, 0],
|
|
6380
7090
|
textFont: ["Open Sans Regular"],
|
|
6381
|
-
minzoom:
|
|
6382
|
-
layout: {
|
|
6383
|
-
"text-
|
|
6384
|
-
|
|
6385
|
-
"text-padding": 8,
|
|
6386
|
-
"text-optional": true,
|
|
6387
|
-
"text-letter-spacing": 0.05,
|
|
6388
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6389
|
-
}
|
|
7091
|
+
minzoom: 3,
|
|
7092
|
+
layout: orientationLabelLayout({
|
|
7093
|
+
"text-letter-spacing": 0.05
|
|
7094
|
+
})
|
|
6390
7095
|
};
|
|
7096
|
+
}
|
|
6391
7097
|
case "BRIDGE":
|
|
6392
7098
|
return {
|
|
6393
7099
|
type: "symbol",
|
|
@@ -6455,33 +7161,27 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6455
7161
|
"text-optional": true
|
|
6456
7162
|
}
|
|
6457
7163
|
};
|
|
6458
|
-
case "AIRARE":
|
|
7164
|
+
case "AIRARE": {
|
|
7165
|
+
const tierExpr = buildAirportTierExpression();
|
|
6459
7166
|
return {
|
|
6460
7167
|
type: "symbol",
|
|
6461
7168
|
textField: ["get", "OBJNAM"],
|
|
6462
|
-
textSize: [
|
|
6463
|
-
|
|
6464
|
-
[
|
|
6465
|
-
[
|
|
6466
|
-
|
|
6467
|
-
9,
|
|
6468
|
-
|
|
6469
|
-
11
|
|
6470
|
-
],
|
|
7169
|
+
textSize: tieredSizeCurve(tierExpr, [
|
|
7170
|
+
[7, [9, 0]],
|
|
7171
|
+
[9, [11, 0]],
|
|
7172
|
+
[11, [11, 9]],
|
|
7173
|
+
[13, [10, 10]],
|
|
7174
|
+
[16, [9, 9]]
|
|
7175
|
+
]),
|
|
6471
7176
|
textColor: DARK_BG,
|
|
6472
7177
|
textHaloColor: COLOR.AIRPORT,
|
|
6473
|
-
textHaloWidth:
|
|
7178
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6474
7179
|
textOffset: [0, 0],
|
|
6475
7180
|
textFont: ["Open Sans Regular"],
|
|
6476
|
-
minzoom:
|
|
6477
|
-
layout:
|
|
6478
|
-
"text-anchor": "center",
|
|
6479
|
-
"text-allow-overlap": false,
|
|
6480
|
-
"text-padding": 5,
|
|
6481
|
-
"text-optional": true,
|
|
6482
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6483
|
-
}
|
|
7181
|
+
minzoom: 7,
|
|
7182
|
+
layout: orientationLabelLayout()
|
|
6484
7183
|
};
|
|
7184
|
+
}
|
|
6485
7185
|
case "SBDARE":
|
|
6486
7186
|
return {
|
|
6487
7187
|
type: "symbol",
|
|
@@ -6554,26 +7254,20 @@ function getLabelConfig(layerName, opts = {}) {
|
|
|
6554
7254
|
"interpolate",
|
|
6555
7255
|
["linear"],
|
|
6556
7256
|
["zoom"],
|
|
7257
|
+
11,
|
|
7258
|
+
9,
|
|
7259
|
+
13,
|
|
6557
7260
|
8,
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
12,
|
|
6561
|
-
15,
|
|
6562
|
-
14
|
|
7261
|
+
16,
|
|
7262
|
+
8
|
|
6563
7263
|
],
|
|
6564
7264
|
textColor: "#c545c3",
|
|
6565
7265
|
textHaloColor: DARK_BG,
|
|
6566
|
-
textHaloWidth:
|
|
7266
|
+
textHaloWidth: ORIENTATION_HALO_WIDTH,
|
|
6567
7267
|
textOffset: [0, 0],
|
|
6568
7268
|
textFont: ["Open Sans Italic"],
|
|
6569
7269
|
minzoom: 8,
|
|
6570
|
-
layout:
|
|
6571
|
-
"text-anchor": "center",
|
|
6572
|
-
"text-allow-overlap": false,
|
|
6573
|
-
"text-padding": 6,
|
|
6574
|
-
"text-optional": true,
|
|
6575
|
-
"symbol-sort-key": ORIENTATION_BAND_SORT_KEY
|
|
6576
|
-
}
|
|
7270
|
+
layout: orientationLabelLayout()
|
|
6577
7271
|
};
|
|
6578
7272
|
case "WEDKLP":
|
|
6579
7273
|
return {
|
|
@@ -8680,7 +9374,21 @@ var LINE_FROM_POLYGON_LAYERS = /* @__PURE__ */ new Set([
|
|
|
8680
9374
|
"MIPARE",
|
|
8681
9375
|
"TESARE",
|
|
8682
9376
|
"MAGVAR",
|
|
8683
|
-
"PRDARE"
|
|
9377
|
+
"PRDARE",
|
|
9378
|
+
"TUNNEL",
|
|
9379
|
+
// Jurisdictional zones (INT1 N40–N47) and IMO routing measures
|
|
9380
|
+
// (INT1 M14, M27, M28) — magenta dashed boundary, no fill. Vast
|
|
9381
|
+
// offshore polygons would tint half the chart if flood-filled, so
|
|
9382
|
+
// every member here renders as boundary-only. DWRTCL is not listed
|
|
9383
|
+
// because it's a native LineString class, not a polygon-as-line.
|
|
9384
|
+
// Closes issue #575 rows 8–15.
|
|
9385
|
+
"ADMARE",
|
|
9386
|
+
"COSARE",
|
|
9387
|
+
"CONZNE",
|
|
9388
|
+
"EXEZNE",
|
|
9389
|
+
"TSEZNE",
|
|
9390
|
+
"DWRTPT",
|
|
9391
|
+
"TWRTPT"
|
|
8684
9392
|
]);
|
|
8685
9393
|
var ZOOM_SCALED_LINES2 = {
|
|
8686
9394
|
DEPCNT: [8, 0.3, 14, 0.8],
|
|
@@ -9668,6 +10376,90 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
|
|
|
9668
10376
|
metadata: { "s52:objectClass": "RECTRC", "s52:role": "polygon" }
|
|
9669
10377
|
});
|
|
9670
10378
|
}
|
|
10379
|
+
if (objectClass === "PONTON" && resolved.type === "fill") {
|
|
10380
|
+
layers.push({
|
|
10381
|
+
id: `s52-PONTON-line`,
|
|
10382
|
+
type: "line",
|
|
10383
|
+
source: sourceId,
|
|
10384
|
+
"source-layer": "PONTON",
|
|
10385
|
+
filter: GEOM_FILTERS.line,
|
|
10386
|
+
paint: {
|
|
10387
|
+
"line-color": resolveColor("CHGRF", ct),
|
|
10388
|
+
"line-width": 1.5
|
|
10389
|
+
},
|
|
10390
|
+
metadata: { "s52:objectClass": "PONTON", "s52:role": "line" }
|
|
10391
|
+
});
|
|
10392
|
+
}
|
|
10393
|
+
if (objectClass === "SILTNK" && resolved.type === "symbol") {
|
|
10394
|
+
layers.push({
|
|
10395
|
+
id: `s52-SILTNK-symbol-polygon`,
|
|
10396
|
+
type: "symbol",
|
|
10397
|
+
source: sourceId,
|
|
10398
|
+
"source-layer": "SILTNK",
|
|
10399
|
+
filter: GEOM_FILTERS.fill,
|
|
10400
|
+
layout: {
|
|
10401
|
+
"icon-image": "TNKCON02",
|
|
10402
|
+
"icon-size": 0.6,
|
|
10403
|
+
"icon-allow-overlap": true,
|
|
10404
|
+
"icon-ignore-placement": false,
|
|
10405
|
+
"symbol-placement": "point"
|
|
10406
|
+
},
|
|
10407
|
+
paint: {},
|
|
10408
|
+
metadata: { "s52:objectClass": "SILTNK", "s52:role": "symbol-polygon" }
|
|
10409
|
+
});
|
|
10410
|
+
}
|
|
10411
|
+
if (objectClass === "ACHBRT" && resolved.type === "symbol") {
|
|
10412
|
+
layers.push({
|
|
10413
|
+
id: `s52-ACHBRT-symbol-polygon`,
|
|
10414
|
+
type: "symbol",
|
|
10415
|
+
source: sourceId,
|
|
10416
|
+
"source-layer": "ACHBRT",
|
|
10417
|
+
filter: GEOM_FILTERS.fill,
|
|
10418
|
+
layout: {
|
|
10419
|
+
"icon-image": "ACHARE02",
|
|
10420
|
+
"icon-size": 0.5,
|
|
10421
|
+
"icon-allow-overlap": true,
|
|
10422
|
+
"icon-ignore-placement": false,
|
|
10423
|
+
"symbol-placement": "point"
|
|
10424
|
+
},
|
|
10425
|
+
paint: {},
|
|
10426
|
+
metadata: { "s52:objectClass": "ACHBRT", "s52:role": "symbol-polygon" }
|
|
10427
|
+
});
|
|
10428
|
+
}
|
|
10429
|
+
if (objectClass === "BERTHS" && resolved.type === "line") {
|
|
10430
|
+
layers.push({
|
|
10431
|
+
id: `s52-BERTHS-circle`,
|
|
10432
|
+
type: "circle",
|
|
10433
|
+
source: sourceId,
|
|
10434
|
+
"source-layer": "BERTHS",
|
|
10435
|
+
filter: GEOM_FILTERS.circle,
|
|
10436
|
+
paint: {
|
|
10437
|
+
"circle-color": resolveColor("CHBLK", ct),
|
|
10438
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.5, 14, 2.2, 17, 3],
|
|
10439
|
+
"circle-stroke-color": resolveColor("CHBLK", ct),
|
|
10440
|
+
"circle-stroke-width": 0.5
|
|
10441
|
+
},
|
|
10442
|
+
metadata: { "s52:objectClass": "BERTHS", "s52:role": "point" }
|
|
10443
|
+
});
|
|
10444
|
+
}
|
|
10445
|
+
if (objectClass === "TUNNEL" && resolved.type === "line") {
|
|
10446
|
+
layers.push({
|
|
10447
|
+
// NOT `s52-TUNNEL-line` — that id is already taken by the main layer
|
|
10448
|
+
// (`s52-${objectClass}-${resolved.type}`), and duplicate layer ids
|
|
10449
|
+
// are a Mapbox style-spec violation (caught by style-integrity.test).
|
|
10450
|
+
id: `s52-TUNNEL-linestring`,
|
|
10451
|
+
type: "line",
|
|
10452
|
+
source: sourceId,
|
|
10453
|
+
"source-layer": "TUNNEL",
|
|
10454
|
+
filter: GEOM_FILTERS.line,
|
|
10455
|
+
paint: {
|
|
10456
|
+
"line-color": resolveColor("CHGRD", ct),
|
|
10457
|
+
"line-width": 1,
|
|
10458
|
+
"line-dasharray": [2, 2]
|
|
10459
|
+
},
|
|
10460
|
+
metadata: { "s52:objectClass": "TUNNEL", "s52:role": "line" }
|
|
10461
|
+
});
|
|
10462
|
+
}
|
|
9671
10463
|
}
|
|
9672
10464
|
return layers;
|
|
9673
10465
|
}
|
|
@@ -9903,9 +10695,21 @@ var META_LAYERS = /* @__PURE__ */ new Set([
|
|
|
9903
10695
|
"M_CSCL",
|
|
9904
10696
|
"TS_FEB",
|
|
9905
10697
|
"TS_TIS",
|
|
9906
|
-
"COSARE",
|
|
9907
|
-
"NEWOBJ",
|
|
9908
10698
|
"LOCMAG"
|
|
10699
|
+
// COSARE was previously suppressed here as meta. The weekly layer-
|
|
10700
|
+
// coverage audit (issue #575) classified it as a jurisdictional polygon
|
|
10701
|
+
// (S-52 INT1 N40, same family as ADMARE / TESARE). It now has an
|
|
10702
|
+
// S52_LOOKUP entry and renders as a magenta dashed boundary; suppressing
|
|
10703
|
+
// it as meta would defeat that wire-up.
|
|
10704
|
+
//
|
|
10705
|
+
// NEWOBJ was likewise previously suppressed here. That was the source
|
|
10706
|
+
// of audit gap row 22: NEWOBJ is the S-57 placeholder for features the
|
|
10707
|
+
// HO has identified but not yet classified. Skipping it silently hid
|
|
10708
|
+
// the HO's own "we don't know what this is, but it is here" signal —
|
|
10709
|
+
// the opposite of what mariners need. The class now renders via its
|
|
10710
|
+
// S52_LOOKUP entry as a vivid magenta circle, displayCategory STANDARD,
|
|
10711
|
+
// scamin 0. Do not put it back here — see the lookup entry for the
|
|
10712
|
+
// safety contract.
|
|
9909
10713
|
]);
|
|
9910
10714
|
var PALETTE_NAMES = {
|
|
9911
10715
|
DAY: "DAY_BRIGHT",
|
|
@@ -9996,21 +10800,58 @@ function makeBandVariants(layer, B) {
|
|
|
9996
10800
|
}
|
|
9997
10801
|
return variants;
|
|
9998
10802
|
}
|
|
9999
|
-
|
|
10803
|
+
var CELL_BAND_OPACITY_CURVE = {
|
|
10804
|
+
"1": [[3, 0.5], [5, 0.3], [16, 0.15]],
|
|
10805
|
+
"2": [[5, 0.5], [7, 0.3], [16, 0.15]],
|
|
10806
|
+
"3": [[7, 0.55], [9, 0.35], [16, 0.18]],
|
|
10807
|
+
"4": [[9, 0.55], [11, 0.35], [16, 0.2]],
|
|
10808
|
+
"5": [[11, 0.6], [13, 0.35], [18, 0.25]],
|
|
10809
|
+
"6": [[13, 0.75], [16, 0.55], [22, 0.45]]
|
|
10810
|
+
};
|
|
10811
|
+
function fanOrientationLabelByCellBand(label) {
|
|
10812
|
+
const cellBandExpr = [
|
|
10813
|
+
"slice",
|
|
10814
|
+
["coalesce", ["get", "_cell_id"], "??9???"],
|
|
10815
|
+
2,
|
|
10816
|
+
3
|
|
10817
|
+
];
|
|
10000
10818
|
const variants = [];
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10819
|
+
for (const code of BAND_DRAW_ORDER) {
|
|
10820
|
+
const range = BAND_PRIORITY_RANGES[code];
|
|
10821
|
+
const curve = CELL_BAND_OPACITY_CURVE[code];
|
|
10822
|
+
const opacityExpr = ["interpolate", ["linear"], ["zoom"]];
|
|
10823
|
+
for (const [z, o] of curve) opacityExpr.push(z, o);
|
|
10824
|
+
const cellFiltered = combineWithBandFilter(
|
|
10825
|
+
label.filter,
|
|
10826
|
+
["==", cellBandExpr, code]
|
|
10827
|
+
);
|
|
10828
|
+
const minzoom = Math.max(
|
|
10829
|
+
label.minzoom ?? range.minzoom,
|
|
10830
|
+
range.minzoom
|
|
10831
|
+
);
|
|
10832
|
+
const paint = {
|
|
10833
|
+
...label.paint ?? {},
|
|
10834
|
+
"text-opacity": opacityExpr
|
|
10835
|
+
};
|
|
10008
10836
|
variants.push({
|
|
10009
10837
|
...label,
|
|
10010
|
-
id: `${label.id}-
|
|
10011
|
-
filter: combineWithBandFilter(
|
|
10012
|
-
|
|
10838
|
+
id: `${label.id}-cell${code}-notdom`,
|
|
10839
|
+
filter: combineWithBandFilter(cellFiltered, NOT_DOMINATED_FILTER),
|
|
10840
|
+
minzoom,
|
|
10841
|
+
paint
|
|
10013
10842
|
});
|
|
10843
|
+
for (const D of BAND_DRAW_ORDER) {
|
|
10844
|
+
const dom = BAND_PRIORITY_RANGES[D];
|
|
10845
|
+
if (dom.minzoom <= minzoom) continue;
|
|
10846
|
+
variants.push({
|
|
10847
|
+
...label,
|
|
10848
|
+
id: `${label.id}-cell${code}-domBy${D}`,
|
|
10849
|
+
filter: combineWithBandFilter(cellFiltered, dominatedByMinFilter(D)),
|
|
10850
|
+
minzoom,
|
|
10851
|
+
maxzoom: dom.minzoom,
|
|
10852
|
+
paint
|
|
10853
|
+
});
|
|
10854
|
+
}
|
|
10014
10855
|
}
|
|
10015
10856
|
return variants;
|
|
10016
10857
|
}
|
|
@@ -10134,7 +10975,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
10134
10975
|
for (const { objectClass, label } of bundles) {
|
|
10135
10976
|
if (!label) continue;
|
|
10136
10977
|
if (getLabelBehavior(objectClass) === "orientation-cross-band") {
|
|
10137
|
-
labelLayers.push(...
|
|
10978
|
+
labelLayers.push(...fanOrientationLabelByCellBand(label));
|
|
10138
10979
|
}
|
|
10139
10980
|
}
|
|
10140
10981
|
for (const code of BAND_DRAW_ORDER) {
|