@chrtco/sdk 0.6.0 → 0.7.1

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/index.js CHANGED
@@ -1281,9 +1281,11 @@ var S52_LOOKUP = {
1281
1281
  category: "Depths",
1282
1282
  displayCategory: "BASE",
1283
1283
  int1Ref: "I10",
1284
+ // Rendered as depth-number text via labels.ts, not as a visible dot.
1284
1285
  style: { type: "circle", paint: { "circle-color": "CHBLK", "circle-radius": 0, "circle-opacity": 0, "circle-stroke-width": 0 } },
1285
1286
  viewingGroup: 13010,
1286
- scamin: 0
1287
+ scamin: 0,
1288
+ auditIgnore: true
1287
1289
  },
1288
1290
  DRGARE: {
1289
1291
  objectClass: "DRGARE",
@@ -1344,8 +1346,28 @@ var S52_LOOKUP = {
1344
1346
  category: "Land",
1345
1347
  displayCategory: "OTHER",
1346
1348
  int1Ref: "C20",
1349
+ // Rendered as region NAME label only via labels.ts — no visible shape.
1350
+ // scamin raised from 45_000 → 90_000 so place-name labels (Provincetown,
1351
+ // Plymouth, Marblehead, …) surface at coastal scale (z10) instead of
1352
+ // waiting for z11. NOAA paper charts show major place names at this
1353
+ // scale; one-step-down was masking landfall context for mariners.
1347
1354
  style: { type: "circle", paint: { "circle-color": "CHBRN", "circle-radius": 0, "circle-opacity": 0 } },
1348
1355
  viewingGroup: 12070,
1356
+ scamin: 9e4,
1357
+ auditIgnore: true
1358
+ },
1359
+ SLOGRD: {
1360
+ // Sloping ground — beach gradient / sloping terrain at landfall. Subtle
1361
+ // LANDA tint so it reads as a beach-context cue without competing with
1362
+ // safety-critical layers. Polygon main + Point variant (NOAA encodes 7
1363
+ // Point and 8 Polygon SLOGRD features).
1364
+ objectClass: "SLOGRD",
1365
+ name: "Sloping Ground",
1366
+ category: "Land",
1367
+ displayCategory: "OTHER",
1368
+ int1Ref: "C8",
1369
+ style: { type: "fill", paint: { "fill-color": "LANDA", "fill-opacity": 0.3 } },
1370
+ viewingGroup: 12070,
1349
1371
  scamin: 45e3
1350
1372
  },
1351
1373
  BUAARE: {
@@ -1354,9 +1376,13 @@ var S52_LOOKUP = {
1354
1376
  category: "Land",
1355
1377
  displayCategory: "STANDARD",
1356
1378
  int1Ref: "D1",
1379
+ // scamin dropped (was 45_000 → z11): built-up areas are infrequent at
1380
+ // low zooms and the label config's text-size interpolation already keeps
1381
+ // them readable. Without this drop, town labels were hidden until z11,
1382
+ // one step too aggressive for coastal-scale orientation.
1357
1383
  style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0.25 } },
1358
1384
  viewingGroup: 12210,
1359
- scamin: 45e3
1385
+ scamin: 0
1360
1386
  },
1361
1387
  BUISGL: {
1362
1388
  objectClass: "BUISGL",
@@ -1368,6 +1394,21 @@ var S52_LOOKUP = {
1368
1394
  viewingGroup: 12210,
1369
1395
  scamin: 22e3
1370
1396
  },
1397
+ FORSTC: {
1398
+ // Fortified structure — coastal fortifications and historic forts. Useful
1399
+ // for visual orientation at landfall. Treated like a building footprint
1400
+ // (CHBRN fill + darker outline) per S-52 F13 portrayal. NOAA encodes 2
1401
+ // Line and 2 Polygon FORSTC features; the Line variant is added in
1402
+ // layer-builder.ts following the LNDARE Line pattern.
1403
+ objectClass: "FORSTC",
1404
+ name: "Fortified Structure",
1405
+ category: "Land",
1406
+ displayCategory: "OTHER",
1407
+ int1Ref: "F13",
1408
+ style: { type: "fill", paint: { "fill-color": "CHBRN", "fill-opacity": 0.4 } },
1409
+ viewingGroup: 12410,
1410
+ scamin: 22e3
1411
+ },
1371
1412
  LAKARE: {
1372
1413
  objectClass: "LAKARE",
1373
1414
  name: "Lake",
@@ -1631,7 +1672,10 @@ var S52_LOOKUP = {
1631
1672
  category: "Hazards",
1632
1673
  displayCategory: "STANDARD",
1633
1674
  int1Ref: "K40",
1634
- style: { type: "symbol", paint: {}, layout: { "icon-size": 0.6, "icon-allow-overlap": true }, iconImage: "OBSTRN01" },
1675
+ // Step expression hides rocks below zoom 12 (where dense coastal fields render
1676
+ // as solid black masses), then scales linearly through harbor-approach zooms.
1677
+ // Aligns with typical NOAA ENC SCAMIN of 22000 (≈ zoom 12) for these features.
1678
+ style: { type: "symbol", paint: {}, layout: { "icon-size": ["interpolate", ["linear"], ["zoom"], 11.99, 0, 12, 0.3, 14, 0.5, 16, 0.7], "icon-allow-overlap": true }, iconImage: "OBSTRN01" },
1635
1679
  hasConditional: true,
1636
1680
  conditionalAttributes: ["WATLEV", "VALSOU"],
1637
1681
  viewingGroup: 24020,
@@ -1643,7 +1687,7 @@ var S52_LOOKUP = {
1643
1687
  category: "Hazards",
1644
1688
  displayCategory: "STANDARD",
1645
1689
  int1Ref: "K12",
1646
- style: { type: "symbol", paint: {}, layout: { "icon-size": 0.6, "icon-allow-overlap": true }, iconImage: "UWTROC03" },
1690
+ style: { type: "symbol", paint: {}, layout: { "icon-size": ["interpolate", ["linear"], ["zoom"], 11.99, 0, 12, 0.3, 14, 0.5, 16, 0.7], "icon-allow-overlap": true }, iconImage: "UWTROC03" },
1647
1691
  hasConditional: true,
1648
1692
  conditionalAttributes: ["WATLEV", "VALSOU"],
1649
1693
  viewingGroup: 24020,
@@ -1661,6 +1705,42 @@ var S52_LOOKUP = {
1661
1705
  viewingGroup: 24020,
1662
1706
  scamin: 0
1663
1707
  },
1708
+ WATTUR: {
1709
+ // Water turbulence (tide rips, eddies, overfalls, breakers, bombora) per
1710
+ // S-52 PresLib H44. Safety-relevant for small craft — these features can
1711
+ // swamp a boat that didn't expect them. NOAA paper-chart convention is a
1712
+ // small wavy-line cluster with the abbreviation noted in the label
1713
+ // (`Br` for breakers, `Eddies`, `Overfalls`, `Tide rips`).
1714
+ //
1715
+ // Symbol layer drives the dominant Point geometry; the Polygon variant is
1716
+ // emitted from layer-builder via the same multi-geom pattern as PR #388.
1717
+ objectClass: "WATTUR",
1718
+ name: "Water Turbulence",
1719
+ category: "Hazards",
1720
+ displayCategory: "STANDARD",
1721
+ int1Ref: "H44",
1722
+ style: { type: "symbol", paint: {}, layout: { "icon-size": 0.7, "icon-allow-overlap": true }, iconImage: "WATTUR01" },
1723
+ hasConditional: true,
1724
+ conditionalAttributes: ["CATWAT"],
1725
+ viewingGroup: 24020,
1726
+ scamin: 22e3
1727
+ },
1728
+ FSHFAC: {
1729
+ // Fishing facility — weirs, stake fences, fish traps. Small-craft hazard,
1730
+ // so safety-critical viewing group 24020 (matches OBSTRN/UWTROC/WRECKS).
1731
+ // Subtle water-feature tint (DEPVS @0.25) + Point variant for stake-fence
1732
+ // markers. NOAA encodes 1 Point and 3 Polygon FSHFAC features.
1733
+ objectClass: "FSHFAC",
1734
+ name: "Fishing Facility",
1735
+ category: "Hazards",
1736
+ displayCategory: "STANDARD",
1737
+ int1Ref: "K44",
1738
+ style: { type: "fill", paint: { "fill-color": "DEPVS", "fill-opacity": 0.25 } },
1739
+ hasConditional: true,
1740
+ conditionalAttributes: ["CATFIF"],
1741
+ viewingGroup: 24020,
1742
+ scamin: 22e3
1743
+ },
1664
1744
  SBDARE: {
1665
1745
  objectClass: "SBDARE",
1666
1746
  name: "Seabed Area",
@@ -1671,8 +1751,32 @@ var S52_LOOKUP = {
1671
1751
  viewingGroup: 14010,
1672
1752
  scamin: 22e3
1673
1753
  },
1754
+ WEDKLP: {
1755
+ // Translucent green fill suggests kelp/vegetation; Point variant (the
1756
+ // dominant geometry — 85 of 88 features) is emitted by layer-builder as
1757
+ // a circle, mirroring the LNDARE Point precedent from PR #388.
1758
+ objectClass: "WEDKLP",
1759
+ name: "Weed / Kelp",
1760
+ category: "Land",
1761
+ displayCategory: "STANDARD",
1762
+ int1Ref: "K43",
1763
+ style: { type: "fill", paint: { "fill-color": "CHGRN", "fill-opacity": 0.35 } },
1764
+ hasConditional: true,
1765
+ conditionalAttributes: ["CATWED", "WATLEV"],
1766
+ viewingGroup: 14010,
1767
+ scamin: 22e3
1768
+ },
1674
1769
  // ── Regulated Areas (INT1 Section N) ────────────────────
1675
1770
  ACHARE: {
1771
+ // Anchorages are conventionally rendered as a subtle filled region in
1772
+ // S-52 with an anchor symbol at centroid and a dashed magenta boundary
1773
+ // (PresLib N12, NOAA Chart No. 1 §N). The fill stays low-opacity so
1774
+ // multiple overlapping anchorages can't compound into a flood — see
1775
+ // comments on CTNARE / RESARE below — but 0.04 left the area effectively
1776
+ // invisible. 0.10 produces a faint NOAA-style tint that still reads
1777
+ // against a busy harbor without stacking into purple wash; the dashed
1778
+ // magenta boundary and centroid anchor icon are emitted as companion
1779
+ // layers in layer-builder.ts so the "anchorage here" cue is unambiguous.
1676
1780
  objectClass: "ACHARE",
1677
1781
  name: "Anchorage Area",
1678
1782
  category: "Regulated Areas",
@@ -1683,32 +1787,71 @@ var S52_LOOKUP = {
1683
1787
  scamin: 9e4
1684
1788
  },
1685
1789
  RESARE: {
1790
+ // S-52 spec: restricted areas render as a solid magenta boundary line
1791
+ // (often with T-marks pointing inward). The previous flood-fill at
1792
+ // opacity 0.12 was a major source of the Boston Harbor purple wash —
1793
+ // Logan Airport's aircraft-vicinity restricted zones are RESARE
1794
+ // polygons that overlap the security-zone CTNARE polygons; with both
1795
+ // as flood fills, the harbor inner waters disappeared under stacked
1796
+ // magenta. Same boundary-only treatment as CTNARE.
1686
1797
  objectClass: "RESARE",
1687
1798
  name: "Restricted Area",
1688
1799
  category: "Regulated Areas",
1689
1800
  displayCategory: "STANDARD",
1690
1801
  int1Ref: "N2",
1691
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.12 } },
1802
+ style: {
1803
+ type: "line",
1804
+ paint: {
1805
+ "line-color": "TRFCD",
1806
+ "line-width": 1.2,
1807
+ "line-opacity": 0.7
1808
+ }
1809
+ },
1692
1810
  viewingGroup: 26040,
1693
1811
  scamin: 9e4
1694
1812
  },
1695
1813
  CTNARE: {
1814
+ // S-52 IHO INT1 N30 spec: caution areas render as a magenta dashed
1815
+ // boundary + centroid "!" symbol — NOT a flood fill. Our previous
1816
+ // `type: "fill"` with `fill-opacity: 0.08` looked harmless in
1817
+ // isolation but Boston-area Harbor cells have many overlapping CTNARE
1818
+ // features (Logan Airport security zones, harbor traffic regulated
1819
+ // areas, etc.). Each at 8% stacks: 5+ overlapping features → a 35%+
1820
+ // magenta wash that obliterates the underlying depth shading.
1821
+ //
1822
+ // Boundary-only (line) is the correct S-52 portrayal and removes the
1823
+ // stacking-flood-fill problem entirely. Hit-testing for the polygon
1824
+ // interior is lost — if we want it back, add a separate invisible
1825
+ // fill layer with `fill-opacity: 0`. Not worth the cost yet.
1696
1826
  objectClass: "CTNARE",
1697
1827
  name: "Caution Area",
1698
1828
  category: "Regulated Areas",
1699
1829
  displayCategory: "STANDARD",
1700
1830
  int1Ref: "N30",
1701
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.08 } },
1831
+ style: {
1832
+ type: "line",
1833
+ paint: {
1834
+ "line-color": "TRFCD",
1835
+ "line-width": 1.2,
1836
+ "line-dasharray": [4, 3],
1837
+ "line-opacity": 0.7
1838
+ }
1839
+ },
1702
1840
  viewingGroup: 26050,
1703
1841
  scamin: 9e4
1704
1842
  },
1705
1843
  FAIRWY: {
1844
+ // Fairways stay as a subtle fill (S-52 convention: very low-opacity
1845
+ // tint so ships can see the bounds of the navigable channel without
1846
+ // obscuring soundings). Reduced from 0.06 → 0.03 to limit stacking.
1847
+ // Long-term: fairway color should arguably be a neutral grey rather
1848
+ // than TRFCD magenta — separate followup.
1706
1849
  objectClass: "FAIRWY",
1707
1850
  name: "Fairway",
1708
1851
  category: "Regulated Areas",
1709
1852
  displayCategory: "STANDARD",
1710
1853
  int1Ref: "M18",
1711
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.06 } },
1854
+ style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.03 } },
1712
1855
  viewingGroup: 25020,
1713
1856
  scamin: 9e4
1714
1857
  },
@@ -1723,12 +1866,15 @@ var S52_LOOKUP = {
1723
1866
  scamin: 22e3
1724
1867
  },
1725
1868
  TSSLPT: {
1869
+ // Traffic separation lane. Light fill so the directional arrows (rendered
1870
+ // separately) read against it; reduced from 0.08 → 0.04 to avoid stacking
1871
+ // with adjacent TSSLPT/TSSRON polygons.
1726
1872
  objectClass: "TSSLPT",
1727
1873
  name: "TSS Lane Part",
1728
1874
  category: "Regulated Areas",
1729
1875
  displayCategory: "STANDARD",
1730
1876
  int1Ref: "M25",
1731
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.08 } },
1877
+ style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.04 } },
1732
1878
  viewingGroup: 25010,
1733
1879
  scamin: 9e4
1734
1880
  },
@@ -1738,7 +1884,7 @@ var S52_LOOKUP = {
1738
1884
  category: "Regulated Areas",
1739
1885
  displayCategory: "STANDARD",
1740
1886
  int1Ref: "M26",
1741
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.08 } },
1887
+ style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.04 } },
1742
1888
  viewingGroup: 25010,
1743
1889
  scamin: 9e4
1744
1890
  },
@@ -1753,22 +1899,43 @@ var S52_LOOKUP = {
1753
1899
  scamin: 9e4
1754
1900
  },
1755
1901
  TESARE: {
1902
+ // S-52 spec: territorial sea boundaries render as a magenta dashed
1903
+ // line (not a fill). Same reasoning as CTNARE/RESARE — even at 0.04
1904
+ // opacity, multiple territorial-sea lobes can stack.
1756
1905
  objectClass: "TESARE",
1757
1906
  name: "Territorial Sea Area",
1758
1907
  category: "Regulated Areas",
1759
1908
  displayCategory: "OTHER",
1760
1909
  int1Ref: "N40",
1761
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.04 } },
1910
+ style: {
1911
+ type: "line",
1912
+ paint: {
1913
+ "line-color": "TRFCD",
1914
+ "line-width": 1,
1915
+ "line-dasharray": [6, 4],
1916
+ "line-opacity": 0.6
1917
+ }
1918
+ },
1762
1919
  viewingGroup: 26050,
1763
1920
  scamin: 9e4
1764
1921
  },
1765
1922
  MIPARE: {
1923
+ // S-52 spec: military practice area renders as a magenta dashed
1924
+ // boundary + centroid symbol — boundary-only here, matches CTNARE.
1766
1925
  objectClass: "MIPARE",
1767
1926
  name: "Military Practice Area",
1768
1927
  category: "Regulated Areas",
1769
1928
  displayCategory: "OTHER",
1770
1929
  int1Ref: "N30",
1771
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.1 } },
1930
+ style: {
1931
+ type: "line",
1932
+ paint: {
1933
+ "line-color": "TRFCD",
1934
+ "line-width": 1.2,
1935
+ "line-dasharray": [4, 3],
1936
+ "line-opacity": 0.7
1937
+ }
1938
+ },
1772
1939
  viewingGroup: 26050,
1773
1940
  scamin: 9e4
1774
1941
  },
@@ -1785,45 +1952,101 @@ var S52_LOOKUP = {
1785
1952
  hasConditional: true,
1786
1953
  conditionalAttributes: ["CATBRG", "VERCLR"]
1787
1954
  },
1955
+ PYLONS: {
1956
+ objectClass: "PYLONS",
1957
+ name: "Pylon / Bridge Support",
1958
+ category: "Infrastructure",
1959
+ displayCategory: "STANDARD",
1960
+ int1Ref: "D27",
1961
+ // Default fill style — layer-builder emits the Point variant via the
1962
+ // same multi-geom pattern from PR #388 (LNDARE Point fix).
1963
+ style: { type: "fill", paint: { "fill-color": "CHBRN", "fill-opacity": 0.5 } },
1964
+ hasConditional: true,
1965
+ conditionalAttributes: ["CATPYL", "VERLEN"],
1966
+ viewingGroup: 12410,
1967
+ scamin: 22e3
1968
+ },
1969
+ // Submarine cable — S-52 INT1 L30 portrayal: thin dashed CHMGD line.
1970
+ // Matches NOAA ENC Online's portrayal — a clean continuous dashed line,
1971
+ // no periodic pictogram overlays (those proved visually noisy at z14+ when
1972
+ // many parallel cables run in a corridor). Previous TRFCD colour was wrong:
1973
+ // submarine cables are charted in CHMGD per S-52.
1974
+ // scamin 90_000 (→ z10) matches the regulated-area family (CTNARE, FAIRWY,
1975
+ // RESARE, PRDARE) so cables appear at Approach scale alongside the other
1976
+ // navigation hazards mariners route around. Previous 22_000 (→ z12) hid
1977
+ // them at Approach band even though the data is in tiles from z7.
1788
1978
  CBLSUB: {
1789
1979
  objectClass: "CBLSUB",
1790
1980
  name: "Submarine Cable",
1791
1981
  category: "Infrastructure",
1792
- displayCategory: "OTHER",
1982
+ displayCategory: "STANDARD",
1793
1983
  int1Ref: "L30",
1794
- style: { type: "line", paint: { "line-color": "TRFCD", "line-width": 1, "line-dasharray": [5, 3] } },
1795
- viewingGroup: 24010,
1796
- scamin: 22e3
1984
+ style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 0.8, "line-dasharray": [6, 3] } },
1985
+ hasConditional: true,
1986
+ conditionalAttributes: ["CATCBL", "STATUS"],
1987
+ viewingGroup: 24090,
1988
+ scamin: 9e4
1797
1989
  },
1798
1990
  CBLARE: {
1799
1991
  objectClass: "CBLARE",
1800
1992
  name: "Cable Area",
1801
- category: "Infrastructure",
1802
- displayCategory: "OTHER",
1803
- int1Ref: "L31",
1804
- style: { type: "fill", paint: { "fill-color": "TRFCD", "fill-opacity": 0.08 } },
1805
- viewingGroup: 24010,
1806
- scamin: 22e3
1993
+ category: "Regulated Areas",
1994
+ displayCategory: "STANDARD",
1995
+ int1Ref: "L32",
1996
+ // S-52 PresLib L32: dashed magenta boundary + diagonal hatching pattern.
1997
+ // The CHMGD fill is a low-opacity wash under the pattern; layer-builder
1998
+ // emits a dashed CHMGD boundary line for the polygon edge (see
1999
+ // REGULATED_AREA_BOUNDARY_LAYERS).
2000
+ style: { type: "fill", paint: { "fill-color": "CHMGD", "fill-opacity": 0.08, "fill-pattern": "CBLARE01" } },
2001
+ hasConditional: true,
2002
+ conditionalAttributes: ["CATCBL", "STATUS"],
2003
+ viewingGroup: 26010,
2004
+ scamin: 9e4
1807
2005
  },
2006
+ // Submarine pipeline — S-52 INT1 L40 portrayal: thin dashed PIPLN line.
2007
+ // Same family as CBLSUB but PIPLN-coloured. Clean continuous dashed line
2008
+ // without periodic pictograms — matches NOAA portrayal.
1808
2009
  PIPSOL: {
1809
2010
  objectClass: "PIPSOL",
1810
- name: "Pipeline, Submarine",
2011
+ name: "Submarine Pipeline",
1811
2012
  category: "Infrastructure",
1812
- displayCategory: "OTHER",
2013
+ displayCategory: "STANDARD",
1813
2014
  int1Ref: "L40",
1814
- style: { type: "line", paint: { "line-color": "PIPLN", "line-width": 1, "line-dasharray": [6, 3] } },
1815
- viewingGroup: 24010,
1816
- scamin: 22e3
2015
+ style: { type: "line", paint: { "line-color": "PIPLN", "line-width": 0.8, "line-dasharray": [6, 3] } },
2016
+ hasConditional: true,
2017
+ conditionalAttributes: ["CATPIP", "STATUS"],
2018
+ viewingGroup: 24090,
2019
+ scamin: 9e4
2020
+ },
2021
+ // Overhead pipeline — S-52 INT1 L41 portrayal: solid thin PIPLN line.
2022
+ // VERCLR clearance label rendered along the line via labels.ts.
2023
+ // Previously absent from the lookup entirely — the 2 NOAA LineString
2024
+ // features were silently dropped.
2025
+ PIPOHD: {
2026
+ objectClass: "PIPOHD",
2027
+ name: "Overhead Pipeline",
2028
+ category: "Infrastructure",
2029
+ displayCategory: "STANDARD",
2030
+ int1Ref: "L41",
2031
+ style: { type: "line", paint: { "line-color": "PIPLN", "line-width": 1 } },
2032
+ hasConditional: true,
2033
+ conditionalAttributes: ["CATPIP", "VERCLR", "STATUS"],
2034
+ viewingGroup: 24070,
2035
+ scamin: 9e4
1817
2036
  },
1818
2037
  PIPARE: {
1819
2038
  objectClass: "PIPARE",
1820
2039
  name: "Pipeline Area",
1821
- category: "Infrastructure",
1822
- displayCategory: "OTHER",
1823
- int1Ref: "L41",
1824
- style: { type: "fill", paint: { "fill-color": "PIPLN", "fill-opacity": 0.08 } },
1825
- viewingGroup: 24010,
1826
- scamin: 22e3
2040
+ category: "Regulated Areas",
2041
+ displayCategory: "STANDARD",
2042
+ int1Ref: "L42",
2043
+ // S-52 PresLib L42: dashed pipeline-colour boundary + diagonal hatching.
2044
+ // PIPLN wash under the pattern; layer-builder emits the dashed boundary.
2045
+ style: { type: "fill", paint: { "fill-color": "PIPLN", "fill-opacity": 0.08, "fill-pattern": "PIPARE01" } },
2046
+ hasConditional: true,
2047
+ conditionalAttributes: ["CATPIP", "STATUS"],
2048
+ viewingGroup: 26010,
2049
+ scamin: 9e4
1827
2050
  },
1828
2051
  MORFAC: {
1829
2052
  objectClass: "MORFAC",
@@ -1911,7 +2134,9 @@ var S52_LOOKUP = {
1911
2134
  category: "Land",
1912
2135
  displayCategory: "OTHER",
1913
2136
  int1Ref: "E33",
1914
- style: { type: "circle", paint: { "circle-color": "CHBLK", "circle-radius": 3, "circle-stroke-width": 0 } },
2137
+ style: { type: "symbol", paint: {}, layout: { "icon-size": 0.6, "icon-allow-overlap": true }, iconImage: "TNKCON02" },
2138
+ hasConditional: true,
2139
+ conditionalAttributes: ["CATSIL", "CONVIS"],
1915
2140
  viewingGroup: 12210,
1916
2141
  scamin: 22e3
1917
2142
  },
@@ -2023,15 +2248,22 @@ var S52_LOOKUP = {
2023
2248
  scamin: 22e3
2024
2249
  },
2025
2250
  // ── Overhead & Underground (INT1 Section L) ─────────────
2251
+ // Overhead cable — S-52 INT1 L31 portrayal: solid thin magenta line
2252
+ // (not dashed) because overhead cables are an in-air navigation hazard
2253
+ // (mast clearance), not a submerged one. The VERCLR clearance label is
2254
+ // emitted along the line via labels.ts. Previous CHBLK + dash pattern
2255
+ // was wrong.
2026
2256
  CBLOHD: {
2027
2257
  objectClass: "CBLOHD",
2028
- name: "Cable, Overhead",
2258
+ name: "Overhead Cable",
2029
2259
  category: "Infrastructure",
2030
2260
  displayCategory: "STANDARD",
2031
- int1Ref: "D26",
2032
- style: { type: "line", paint: { "line-color": "CHBLK", "line-width": 1, "line-dasharray": [4, 2] } },
2033
- viewingGroup: 24010,
2034
- scamin: 22e3
2261
+ int1Ref: "L31",
2262
+ style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 1 } },
2263
+ hasConditional: true,
2264
+ conditionalAttributes: ["CATCBL", "VERCLR", "STATUS"],
2265
+ viewingGroup: 24070,
2266
+ scamin: 9e4
2035
2267
  },
2036
2268
  // ── Routes (INT1 Section M) ─────────────────────────────
2037
2269
  FERYRT: {
@@ -2061,19 +2293,41 @@ var S52_LOOKUP = {
2061
2293
  category: "Land",
2062
2294
  displayCategory: "OTHER",
2063
2295
  int1Ref: "C31",
2064
- style: { type: "fill", paint: { "fill-color": "LANDF", "fill-opacity": 0.2 } },
2296
+ // Pattern fill driven by CATVEG (resolved in `style-resolver.ts` via
2297
+ // `vegatnPatternExpression`). `fill-color` is the MapLibre fallback for
2298
+ // when the sprite image is missing; `fill-pattern` here is the default
2299
+ // used if the conditional expression isn't applied for some reason
2300
+ // (e.g. a consumer that bypasses `resolvePaint`).
2301
+ style: {
2302
+ type: "fill",
2303
+ paint: { "fill-color": "LANDF", "fill-opacity": 0.6, "fill-pattern": "VEGATN03" }
2304
+ },
2305
+ hasConditional: true,
2306
+ conditionalAttributes: ["CATVEG"],
2065
2307
  viewingGroup: 12050,
2066
2308
  scamin: 22e3
2067
2309
  },
2068
2310
  SEAARE: {
2311
+ // Cross-band water base. SEAARE polygons tile the sea below the
2312
+ // detailed DEPARE depth-area features, so where DEPARE has coverage
2313
+ // the depth bins win on top; where it doesn't (the coverage hole
2314
+ // between Coastal and Approach cells, etc.), SEAARE paints the
2315
+ // "deep water" colour instead of leaving the basemap to bleed
2316
+ // through. Drawn before DEPARE — see `draw-order.ts`. Opaque,
2317
+ // forced via `OPAQUE_FILL_LAYERS` in `style-resolver.ts`.
2318
+ //
2319
+ // `scamin: 0` so the base fill renders at every continuous zoom,
2320
+ // gated only by the band-priority window. The label config in
2321
+ // `labels.ts` keeps its own `minzoom: 5` so the OBJNAM doesn't
2322
+ // splash at low zooms.
2069
2323
  objectClass: "SEAARE",
2070
2324
  name: "Sea Area",
2071
2325
  category: "Depths",
2072
2326
  displayCategory: "OTHER",
2073
2327
  int1Ref: "O1",
2074
- style: { type: "fill", paint: { "fill-color": "DEPMD", "fill-opacity": 0 } },
2328
+ style: { type: "fill", paint: { "fill-color": "DEPDW", "fill-opacity": 1 } },
2075
2329
  viewingGroup: 12070,
2076
- scamin: 9e4
2330
+ scamin: 0
2077
2331
  },
2078
2332
  MARCUL: {
2079
2333
  objectClass: "MARCUL",
@@ -2135,6 +2389,27 @@ var S52_LOOKUP = {
2135
2389
  viewingGroup: 25010,
2136
2390
  scamin: 9e4
2137
2391
  },
2392
+ PRDARE: {
2393
+ // Production Area — offshore oil/gas platforms, offshore wind farms (or
2394
+ // their exclusion zones), aquaculture, and other industrial production
2395
+ // areas mariners must not enter. Safety-critical: a vessel transiting
2396
+ // Boston-NYC should see Block Island / Vineyard Wind exclusion zones.
2397
+ //
2398
+ // Boundary-only dashed magenta — matches RESARE/CTNARE/MIPARE/TESARE
2399
+ // convention via LINE_FROM_POLYGON_LAYERS (see layer-builder.ts). Avoids
2400
+ // the flood-fill stacking problem where multiple regulated areas overlap
2401
+ // and obliterate underlying depth shading.
2402
+ objectClass: "PRDARE",
2403
+ name: "Production Area",
2404
+ category: "Regulated Areas",
2405
+ displayCategory: "STANDARD",
2406
+ int1Ref: "L5",
2407
+ style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 1.4, "line-dasharray": [4, 2] } },
2408
+ hasConditional: true,
2409
+ conditionalAttributes: ["CATPRA", "STATUS"],
2410
+ viewingGroup: 26010,
2411
+ scamin: 9e4
2412
+ },
2138
2413
  OSPARE: {
2139
2414
  objectClass: "OSPARE",
2140
2415
  name: "Offshore Production Area",
@@ -2165,6 +2440,21 @@ var S52_LOOKUP = {
2165
2440
  viewingGroup: 12210,
2166
2441
  scamin: 22e3
2167
2442
  },
2443
+ RUNWAY: {
2444
+ // Airport runway near coast — navigation reference, not a navigable
2445
+ // feature. Distinct from ROADWY (which is `type: "line"`) — runways are
2446
+ // typically polygon-encoded showing the actual paved extent. NOAA encodes
2447
+ // 1 Line and 5 Polygon RUNWAY features; the Line variant is added in
2448
+ // layer-builder.ts so neither geometry type is silently dropped.
2449
+ objectClass: "RUNWAY",
2450
+ name: "Runway",
2451
+ category: "Infrastructure",
2452
+ displayCategory: "OTHER",
2453
+ int1Ref: "D20",
2454
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0.3 } },
2455
+ viewingGroup: 12250,
2456
+ scamin: 22e3
2457
+ },
2168
2458
  DAMCON: {
2169
2459
  objectClass: "DAMCON",
2170
2460
  name: "Dam",
@@ -2175,48 +2465,78 @@ var S52_LOOKUP = {
2175
2465
  viewingGroup: 12410,
2176
2466
  scamin: 22e3
2177
2467
  },
2468
+ // ── Magnetic Variation (INT1 Section B) ─────────────────
2469
+ // Polygon features (~136 in NOAA corpus) are isogonic zones — areas of
2470
+ // constant magnetic variation. The line style here is drawn as the
2471
+ // polygon BOUNDARY (LINE_FROM_POLYGON_LAYERS in layer-builder.ts), giving
2472
+ // the faint dashed magenta isogonic lines mariners read for compass
2473
+ // correction. Point features (~58) get their own circle layer in
2474
+ // layer-builder.ts. The VALMAG label is rendered via labels.ts.
2475
+ MAGVAR: {
2476
+ objectClass: "MAGVAR",
2477
+ name: "Magnetic Variation",
2478
+ category: "Meta",
2479
+ displayCategory: "OTHER",
2480
+ int1Ref: "B68",
2481
+ style: { type: "line", paint: { "line-color": "CHMGD", "line-width": 0.5, "line-opacity": 0.5, "line-dasharray": [4, 2] } },
2482
+ hasConditional: true,
2483
+ conditionalAttributes: ["VALMAG", "VALACM", "RYRMGV"],
2484
+ viewingGroup: 23010,
2485
+ scamin: 0
2486
+ },
2178
2487
  // ── Meta (hidden by default) ────────────────────────────
2488
+ // M_* and DSID are S-57 metadata classes — they describe the dataset
2489
+ // (coverage extent, survey reliability, source publication, etc.) and
2490
+ // carry attributes for click-query lookups, but never draw a visible
2491
+ // shape. `auditIgnore` keeps the layer-coverage audit from flagging
2492
+ // their geometry-type rows as gaps.
2179
2493
  M_COVR: {
2180
2494
  objectClass: "M_COVR",
2181
2495
  name: "Coverage",
2182
2496
  category: "Meta",
2183
2497
  displayCategory: "META",
2184
- style: { type: "line", paint: { "line-color": "CHGRF", "line-width": 0.5, "line-opacity": 0.3 } }
2498
+ style: { type: "line", paint: { "line-color": "CHGRF", "line-width": 0.5, "line-opacity": 0.3 } },
2499
+ auditIgnore: true
2185
2500
  },
2186
2501
  M_NPUB: {
2187
2502
  objectClass: "M_NPUB",
2188
2503
  name: "Nautical Publication",
2189
2504
  category: "Meta",
2190
2505
  displayCategory: "META",
2191
- style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } }
2506
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
2507
+ auditIgnore: true
2192
2508
  },
2193
2509
  M_NSYS: {
2194
2510
  objectClass: "M_NSYS",
2195
2511
  name: "Nav System of Marks",
2196
2512
  category: "Meta",
2197
2513
  displayCategory: "META",
2198
- style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } }
2514
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
2515
+ auditIgnore: true
2199
2516
  },
2200
2517
  M_QUAL: {
2201
2518
  objectClass: "M_QUAL",
2202
2519
  name: "Quality of Data",
2203
2520
  category: "Meta",
2204
2521
  displayCategory: "META",
2205
- style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } }
2522
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
2523
+ auditIgnore: true
2206
2524
  },
2207
2525
  M_SREL: {
2208
2526
  objectClass: "M_SREL",
2209
2527
  name: "Survey Reliability",
2210
2528
  category: "Meta",
2211
2529
  displayCategory: "META",
2212
- style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } }
2530
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
2531
+ auditIgnore: true
2213
2532
  },
2214
2533
  DSID: {
2215
2534
  objectClass: "DSID",
2216
2535
  name: "Dataset ID",
2217
2536
  category: "Meta",
2218
2537
  displayCategory: "META",
2219
- style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } }
2538
+ style: { type: "fill", paint: { "fill-color": "CHGRF", "fill-opacity": 0 } },
2539
+ auditIgnore: true
2220
2540
  }
2221
2541
  };
2222
2542
 
@@ -2229,25 +2549,38 @@ var S52_DRAW_ORDER = [
2229
2549
  "M_NSYS",
2230
2550
  "M_QUAL",
2231
2551
  "M_SREL",
2232
- // 2. Depth areas – deepest first (fill)
2233
- "DEPARE",
2552
+ // 2. Depth areas – broadest first (fill).
2553
+ //
2554
+ // SEAARE goes BEFORE DEPARE so its uniform "deep water" base fill paints
2555
+ // under DEPARE's depth-tier bins. Where DEPARE has coverage, the bin
2556
+ // colour wins on top; where DEPARE doesn't (cross-band coverage gaps —
2557
+ // e.g. between Coastal and Approach cell extents), SEAARE provides a
2558
+ // chart-coloured base instead of the basemap bleeding through. This is
2559
+ // the visual-coherence fix for hard charcoal-vs-vector-blue seams at
2560
+ // scale-band boundaries — see `s52-lookup.ts`'s SEAARE entry for the
2561
+ // paired paint-spec change (fill-opacity 0 → 1, color → DEPDW).
2234
2562
  "SEAARE",
2563
+ "DEPARE",
2235
2564
  // 3. Dredged / swept areas (fill)
2236
2565
  "DRGARE",
2237
2566
  "SWPARE",
2238
2567
  "SBDARE",
2568
+ "WEDKLP",
2239
2569
  // 4. Land (fill)
2240
2570
  "LNDARE",
2571
+ "SLOGRD",
2241
2572
  "BUAARE",
2242
2573
  "LAKARE",
2243
2574
  "RIVERS",
2244
2575
  "CANALS",
2245
2576
  "VEGATN",
2246
2577
  "AIRARE",
2578
+ "RUNWAY",
2247
2579
  // 5. Area hazards (fill)
2248
2580
  "OBSTRN",
2249
2581
  "UWTROC",
2250
2582
  "MARCUL",
2583
+ "FSHFAC",
2251
2584
  // 6. Regulated areas (fill)
2252
2585
  "TESARE",
2253
2586
  "MIPARE",
@@ -2260,6 +2593,7 @@ var S52_DRAW_ORDER = [
2260
2593
  "ACHARE",
2261
2594
  "RESARE",
2262
2595
  "CTNARE",
2596
+ "PRDARE",
2263
2597
  "OSPARE",
2264
2598
  "LOGPON",
2265
2599
  "CBLARE",
@@ -2270,6 +2604,8 @@ var S52_DRAW_ORDER = [
2270
2604
  "DRYDOC",
2271
2605
  "FLODOC",
2272
2606
  "BRIDGE",
2607
+ "PYLONS",
2608
+ "FORSTC",
2273
2609
  // 8. Coastline / shore (line)
2274
2610
  "COALNE",
2275
2611
  "SLCONS",
@@ -2277,6 +2613,11 @@ var S52_DRAW_ORDER = [
2277
2613
  // 9. Depth contours (line)
2278
2614
  "DEPCNT",
2279
2615
  "LNDELV",
2616
+ // 9a. Magnetic-variation isogonic lines (background reference annotations,
2617
+ // drawn above depth contours so the magenta dashes read against the
2618
+ // depth-fill ladder, but below nav lines / nav aids so safety-critical
2619
+ // routing and aids-to-nav win the visual hierarchy).
2620
+ "MAGVAR",
2280
2621
  // 10. Navigation lines (line)
2281
2622
  "NAVLNE",
2282
2623
  "TSSBND",
@@ -2286,6 +2627,7 @@ var S52_DRAW_ORDER = [
2286
2627
  "FERYRT",
2287
2628
  "RECTRC",
2288
2629
  "PIPSOL",
2630
+ "PIPOHD",
2289
2631
  "CBLSUB",
2290
2632
  "CBLOHD",
2291
2633
  "GATCON",
@@ -2328,7 +2670,8 @@ var S52_DRAW_ORDER = [
2328
2670
  "LITFLT",
2329
2671
  "LIGHTS",
2330
2672
  // 13. Hazard points (very top)
2331
- "WRECKS"
2673
+ "WRECKS",
2674
+ "WATTUR"
2332
2675
  ];
2333
2676
  function getDrawPriority(layerName) {
2334
2677
  const idx = S52_DRAW_ORDER.indexOf(layerName);
@@ -2363,9 +2706,22 @@ function depareFillExpression(ct = "DAY") {
2363
2706
  resolveColor("DEPDW", ct)
2364
2707
  ];
2365
2708
  }
2709
+ function colourCodeCase(cases, fallback) {
2710
+ const colourStr = ["to-string", ["coalesce", ["get", "COLOUR"], ["get", "colour"], ""]];
2711
+ const expr = ["case"];
2712
+ for (const [code, value] of cases) {
2713
+ expr.push([">=", ["index-of", `"${code}"`, colourStr], 0], value);
2714
+ }
2715
+ for (const [code, value] of cases) {
2716
+ expr.push(["==", colourStr, code], value);
2717
+ }
2718
+ expr.push(fallback);
2719
+ return expr;
2720
+ }
2366
2721
  function lightColor(props, ct = "DAY") {
2367
- const colour = Number(props.COLOUR ?? props.colour ?? 0);
2368
- switch (colour) {
2722
+ const raw = props.COLOUR ?? props.colour;
2723
+ const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
2724
+ switch (code) {
2369
2725
  case 1:
2370
2726
  return resolveColor("LITWT", ct);
2371
2727
  case 3:
@@ -2383,23 +2739,17 @@ function lightColor(props, ct = "DAY") {
2383
2739
  }
2384
2740
  }
2385
2741
  function lightFillExpression(ct = "DAY") {
2386
- return [
2387
- "match",
2388
- ["coalesce", ["get", "COLOUR"], ["get", "colour"], 0],
2389
- 1,
2390
- resolveColor("LITWT", ct),
2391
- 3,
2392
- resolveColor("LITRD", ct),
2393
- 4,
2394
- resolveColor("LITGN", ct),
2395
- 5,
2396
- resolveColor("LITBL", ct),
2397
- 6,
2398
- resolveColor("LITYW", ct),
2399
- 11,
2400
- resolveColor("LITOR", ct),
2742
+ return colourCodeCase(
2743
+ [
2744
+ ["3", resolveColor("LITRD", ct)],
2745
+ ["4", resolveColor("LITGN", ct)],
2746
+ ["6", resolveColor("LITYW", ct)],
2747
+ ["11", resolveColor("LITOR", ct)],
2748
+ ["5", resolveColor("LITBL", ct)],
2749
+ ["1", resolveColor("LITWT", ct)]
2750
+ ],
2401
2751
  resolveColor("LITYW", ct)
2402
- ];
2752
+ );
2403
2753
  }
2404
2754
  function lateralColor(props, ct = "DAY") {
2405
2755
  const catlam = Number(props.CATLAM ?? props.catlam ?? 0);
@@ -2443,9 +2793,54 @@ function hazardFillExpression(ct = "DAY") {
2443
2793
  resolveColor("RESBL", ct)
2444
2794
  ];
2445
2795
  }
2796
+ function vegatnPatternExpression() {
2797
+ return [
2798
+ "match",
2799
+ ["coalesce", ["get", "CATVEG"], ["get", "catveg"], 0],
2800
+ 1,
2801
+ "VEGATN03",
2802
+ // grass
2803
+ 3,
2804
+ "VEGATN03",
2805
+ // bush
2806
+ 4,
2807
+ "VEGATN04",
2808
+ // deciduous wood
2809
+ 5,
2810
+ "VEGATN04",
2811
+ // coniferous wood
2812
+ 6,
2813
+ "VEGATN04",
2814
+ // wood (mixed)
2815
+ 7,
2816
+ "MARSHES1",
2817
+ // mangroves
2818
+ 10,
2819
+ "VEGATN03",
2820
+ // scrub
2821
+ 11,
2822
+ "VEGATN04",
2823
+ // tree
2824
+ 12,
2825
+ "VEGATN04",
2826
+ // evergreen
2827
+ 16,
2828
+ "MARSHES1",
2829
+ // swamp
2830
+ 17,
2831
+ "MARSHES1",
2832
+ // marsh
2833
+ 21,
2834
+ "VEGATN04",
2835
+ // savannah
2836
+ "VEGATN03"
2837
+ // default: grass pattern
2838
+ ];
2839
+ }
2446
2840
  function daymarkColor(props, ct = "DAY") {
2447
- const colour = Number(props.COLOUR ?? props.colour ?? 0);
2448
- switch (colour) {
2841
+ const raw = props.COLOUR ?? props.colour;
2842
+ const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
2843
+ switch (code) {
2449
2844
  case 1:
2450
2845
  return resolveColor("LITWT", ct);
2451
2846
  case 3:
@@ -2732,14 +3127,14 @@ function topmarkXShape() {
2732
3127
  }
2733
3128
  function lightMajor() {
2734
3129
  return svg(`
2735
- <circle cx="16" cy="22" r="3" fill="#d200d2"/>
2736
- <path d="M16,22 L12.5,12 Q16,2 19.5,12 Z" fill="#d200d2"/>
3130
+ <circle cx="16" cy="16" r="2.5" fill="#d200d2"/>
3131
+ <path d="M16,16 L20.6,25.5 Q30,30 25.5,20.6 Z" fill="#d200d2"/>
2737
3132
  `);
2738
3133
  }
2739
3134
  function lightMinor() {
2740
3135
  return svg(`
2741
- <circle cx="16" cy="22" r="2.5" fill="#d200d2"/>
2742
- <path d="M16,22 L14,14 Q16,7 18,14 Z" fill="#d200d2"/>
3136
+ <circle cx="16" cy="16" r="2" fill="#d200d2"/>
3137
+ <path d="M16,16 L20.3,23.1 Q26.6,26.6 23.1,20.3 Z" fill="#d200d2"/>
2743
3138
  `);
2744
3139
  }
2745
3140
  function lightSector() {
@@ -4857,6 +5252,7 @@ var SYMBOL_MAP = {
4857
5252
  WRECKS: "WRECKS04",
4858
5253
  OBSTRN: "OBSTRN01",
4859
5254
  UWTROC: "UWTROC03",
5255
+ WATTUR: "WATTUR01",
4860
5256
  // Landmarks
4861
5257
  LNDMRK: "BCNTOW01",
4862
5258
  // Infrastructure
@@ -4869,7 +5265,7 @@ var SYMBOL_MAP = {
4869
5265
  SISTAT: "SISTAT02",
4870
5266
  SMCFAC: "ACHARE02",
4871
5267
  // Land infrastructure
4872
- SILTNK: "BCNTOW01",
5268
+ SILTNK: "TNKCON02",
4873
5269
  BUISGL: "BCNTOW01",
4874
5270
  HRBFAC: "ACHARE02",
4875
5271
  // Areas
@@ -4957,20 +5353,69 @@ function getIconExpression(layerName) {
4957
5353
  // Default: submerged/dangerous
4958
5354
  ];
4959
5355
  }
5356
+ if (layerName === "SILTNK") {
5357
+ const catsil = ["coalesce", ["get", "CATSIL"], ["get", "catsil"], 0];
5358
+ const convis = ["coalesce", ["get", "CONVIS"], ["get", "convis"], 0];
5359
+ return [
5360
+ "case",
5361
+ ["==", convis, 1],
5362
+ [
5363
+ "match",
5364
+ catsil,
5365
+ 1,
5366
+ "SILBUI11",
5367
+ // Silo, conspicuous
5368
+ 2,
5369
+ "TNKCON12",
5370
+ // Tank, conspicuous
5371
+ 3,
5372
+ "TOWERS03",
5373
+ // Grain elevator, conspicuous
5374
+ 4,
5375
+ "TOWERS12",
5376
+ // Water tower, conspicuous
5377
+ "TNKCON12"
5378
+ // CONVIS=1 but no CATSIL → conspicuous tank
5379
+ ],
5380
+ [
5381
+ "match",
5382
+ catsil,
5383
+ 1,
5384
+ "SILBUI01",
5385
+ // Silo
5386
+ 2,
5387
+ "TNKCON02",
5388
+ // Tank
5389
+ 3,
5390
+ "TOWERS01",
5391
+ // Grain elevator
5392
+ 4,
5393
+ "TOWERS02",
5394
+ // Water tower
5395
+ "TNKCON02"
5396
+ // Default: non-conspicuous tank
5397
+ ]
5398
+ ];
5399
+ }
4960
5400
  if (layerName === "LNDMRK") {
4961
5401
  return [
4962
5402
  "match",
4963
5403
  ["coalesce", ["get", "CATLMK"], ["get", "catlmk"], 0],
4964
- // Most LNDMRK types → generic tower beacon (best available S-52 vector)
4965
5404
  1,
4966
5405
  "CAIRNS11",
4967
5406
  // Cairn
4968
5407
  17,
4969
5408
  "BCNTOW01",
4970
- // Spire → tower
5409
+ // Tower
5410
+ 18,
5411
+ "LNDMRK18",
5412
+ // Windmill (4-vane)
5413
+ 19,
5414
+ "LNDMRK19",
5415
+ // Wind motor / turbine (3-blade)
4971
5416
  20,
4972
5417
  "BCNTOW01",
4973
- // Tower
5418
+ // Spire → tower
4974
5419
  "BCNTOW01"
4975
5420
  // Default: tower
4976
5421
  ];
@@ -5045,13 +5490,13 @@ function getIconExpression(layerName) {
5045
5490
  "match",
5046
5491
  ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0],
5047
5492
  4,
5048
- "UWTROC03",
5049
- // Awash
5493
+ "UWTROC01",
5494
+ // Covers and uncovers — dotted-circle + cross (K11)
5050
5495
  5,
5051
- "UWTROC04",
5052
- // Covers and uncovers
5496
+ "UWTROC03",
5497
+ // Awash at chart datum — solid-circle + cross (K12)
5053
5498
  "UWTROC04"
5054
- // Default: underwater
5499
+ // Default / always submerged — asterisk (K13)
5055
5500
  ];
5056
5501
  }
5057
5502
  if (layerName === "OBSTRN") {
@@ -5090,7 +5535,27 @@ function getIconExpression(layerName) {
5090
5535
  ];
5091
5536
  }
5092
5537
  if (layerName === "BRIDGE") {
5093
- return "BRIDGE01";
5538
+ return [
5539
+ "match",
5540
+ ["coalesce", ["get", "CATBRG"], ["get", "catbrg"], 1],
5541
+ 1,
5542
+ "BRIDGE01",
5543
+ // Fixed bridge (D20)
5544
+ 2,
5545
+ "BRIDGE02",
5546
+ // Opening bridge — generic movable (D21)
5547
+ 3,
5548
+ "BRIDGE03",
5549
+ // Swing bridge (D23.3)
5550
+ 4,
5551
+ "BRIDGE04",
5552
+ // Lifting bridge (D23.1)
5553
+ 5,
5554
+ "BRIDGE05",
5555
+ // Bascule bridge (D23.2)
5556
+ "BRIDGE01"
5557
+ // Default: fixed
5558
+ ];
5094
5559
  }
5095
5560
  if (layerName === "LIGHTS") {
5096
5561
  return [
@@ -5195,7 +5660,7 @@ function scaminToMinZoom(scamin) {
5195
5660
  return void 0;
5196
5661
  }
5197
5662
  var SAFETY_CRITICAL_GROUPS = /* @__PURE__ */ new Set([24020, 27010, 27070, 27080]);
5198
- var OPAQUE_FILL_LAYERS = /* @__PURE__ */ new Set(["DEPARE", "LNDARE", "UNSARE", "LAKARE", "DRGARE"]);
5663
+ var OPAQUE_FILL_LAYERS = /* @__PURE__ */ new Set(["DEPARE", "LNDARE", "UNSARE", "LAKARE", "DRGARE", "SEAARE"]);
5199
5664
  var SCAMIN_OVERRIDE_LAYERS = /* @__PURE__ */ new Set([
5200
5665
  "BOYLAT",
5201
5666
  "BOYCAR",
@@ -5243,6 +5708,8 @@ function resolvePaint(layerName, paint, geomType, ct = "DAY") {
5243
5708
  resolved["circle-color"] = lateralFillExpression(ct);
5244
5709
  } else if ((layerName === "OBSTRN" || layerName === "UWTROC" || layerName === "WRECKS") && geomType === "circle") {
5245
5710
  resolved["circle-color"] = hazardFillExpression(ct);
5711
+ } else if (layerName === "VEGATN" && geomType === "fill") {
5712
+ resolved["fill-pattern"] = vegatnPatternExpression();
5246
5713
  }
5247
5714
  return resolved;
5248
5715
  }
@@ -5263,7 +5730,7 @@ function resolveStyle(layerName, geomKind, ct = "DAY") {
5263
5730
  paint: {},
5264
5731
  layout: {
5265
5732
  "icon-image": iconExpr,
5266
- "icon-size": ["interpolate", ["linear"], ["zoom"], 8, 0.4, 14, 0.8],
5733
+ "icon-size": ["interpolate", ["linear"], ["zoom"], 6, 0.32, 13, 0.32, 14, 0.36, 18, 1.04],
5267
5734
  "icon-allow-overlap": true,
5268
5735
  "icon-ignore-placement": false,
5269
5736
  ...entry.style.layout ?? {}
@@ -5344,13 +5811,200 @@ var COLOUR_ABBR = {
5344
5811
  6: "Y",
5345
5812
  11: "Or"
5346
5813
  };
5814
+ var NATSUR_ABBR = [
5815
+ [1, "M"],
5816
+ // mud
5817
+ [2, "Cy"],
5818
+ // clay
5819
+ [3, "Si"],
5820
+ // silt
5821
+ [4, "S"],
5822
+ // sand
5823
+ [5, "St"],
5824
+ // stones
5825
+ [6, "G"],
5826
+ // gravel
5827
+ [7, "P"],
5828
+ // pebbles
5829
+ [8, "Cb"],
5830
+ // cobbles
5831
+ [9, "R"],
5832
+ // rock
5833
+ [11, "La"],
5834
+ // lava
5835
+ [14, "Co"],
5836
+ // coral
5837
+ [17, "Sh"],
5838
+ // shells
5839
+ [18, "Bo"]
5840
+ // boulder
5841
+ ];
5842
+ var NATQUA_ABBR = [
5843
+ [1, "f"],
5844
+ // fine
5845
+ [2, "m"],
5846
+ // medium
5847
+ [3, "c"],
5848
+ // coarse
5849
+ [4, "bk"],
5850
+ // broken
5851
+ [5, "sy"],
5852
+ // sticky
5853
+ [6, "so"],
5854
+ // soft
5855
+ [7, "sf"],
5856
+ // stiff
5857
+ [8, "v"],
5858
+ // volcanic
5859
+ [9, "ca"],
5860
+ // calcareous
5861
+ [10, "h"]
5862
+ // hard
5863
+ ];
5347
5864
  var DARK_BG = "#1a1d23";
5348
- function getLabelConfig(layerName) {
5865
+ var NAV_AID_LABEL_TEXT_SIZE = [
5866
+ "interpolate",
5867
+ ["linear"],
5868
+ ["zoom"],
5869
+ 10,
5870
+ 8,
5871
+ 14,
5872
+ 10,
5873
+ 18,
5874
+ 13
5875
+ ];
5876
+ var NAV_AID_LABEL_FONT = ["Open Sans Semibold"];
5877
+ function listAttrHasCode(stringified, code) {
5878
+ return [
5879
+ "any",
5880
+ [">=", ["index-of", `"${code}"`, stringified], 0],
5881
+ [
5882
+ ">=",
5883
+ ["index-of", `,${code},`, ["concat", ",", stringified, ","]],
5884
+ 0
5885
+ ]
5886
+ ];
5887
+ }
5888
+ function buildSeabedLabelExpression() {
5889
+ const natsurStr = [
5890
+ "to-string",
5891
+ ["coalesce", ["get", "NATSUR"], ["get", "natsur"], ""]
5892
+ ];
5893
+ const natquaStr = [
5894
+ "to-string",
5895
+ ["coalesce", ["get", "NATQUA"], ["get", "natqua"], ""]
5896
+ ];
5897
+ const natquaPrefix = ["case"];
5898
+ for (const [code, abbr] of NATQUA_ABBR) {
5899
+ natquaPrefix.push(listAttrHasCode(natquaStr, code), abbr);
5900
+ }
5901
+ natquaPrefix.push("");
5902
+ const natsurParts = ["concat"];
5903
+ for (const [code, abbr] of NATSUR_ABBR) {
5904
+ natsurParts.push([
5905
+ "case",
5906
+ listAttrHasCode(natsurStr, code),
5907
+ `.${abbr}`,
5908
+ ""
5909
+ ]);
5910
+ }
5911
+ const natsurJoined = ["slice", natsurParts, 1];
5912
+ return ["concat", natquaPrefix, natsurJoined];
5913
+ }
5914
+ var COLOR = {
5915
+ WATER: "#9ec1d8",
5916
+ WATER_DEEP: "#8fb8d0",
5917
+ LAND_REGION: "#e8d8b8",
5918
+ LAND_NATURAL: "#e0d8c8",
5919
+ CITY: "#f1e7d2",
5920
+ AIRPORT: "#d4c9b0",
5921
+ STRUCTURE: "#bfa88a",
5922
+ STRUCTURE_LIGHT: "#c8c0b0",
5923
+ SOUNDING: "#c8c8c8",
5924
+ NAV_NUMBER: "#ffffff",
5925
+ NAV_CARDINAL: "#fdf0b0",
5926
+ NAV_SPECIAL: "#eab308",
5927
+ LIGHT: "#fdf0b0"
5928
+ };
5929
+ var ORIENTATION_BAND_SORT_KEY = [
5930
+ "to-number",
5931
+ ["slice", ["coalesce", ["get", "_cell_id"], "??9???"], 2, 3],
5932
+ 9
5933
+ ];
5934
+ var LABEL_BEHAVIOR = {
5935
+ // Orientation labels — render once across all bands.
5936
+ SEAARE: "orientation-cross-band",
5937
+ LNDRGN: "orientation-cross-band",
5938
+ LAKARE: "orientation-cross-band",
5939
+ RIVERS: "orientation-cross-band",
5940
+ CANALS: "orientation-cross-band",
5941
+ BUAARE: "orientation-cross-band",
5942
+ AIRARE: "orientation-cross-band",
5943
+ LNDARE: "orientation-cross-band",
5944
+ // Production areas (offshore wind farms, oil/gas platforms) — rare but
5945
+ // named. Use polygon-centroid placement across bands so "Block Island
5946
+ // Wind Farm" / "Vineyard Wind" labels show when the area renders.
5947
+ PRDARE: "orientation-cross-band",
5948
+ // Feature-specific labels — fan by band so only the dominant band shows.
5949
+ SOUNDG: "feature-band-gated",
5950
+ DEPCNT: "feature-band-gated",
5951
+ LIGHTS: "feature-band-gated",
5952
+ BOYLAT: "feature-band-gated",
5953
+ BCNLAT: "feature-band-gated",
5954
+ BOYCAR: "feature-band-gated",
5955
+ BCNCAR: "feature-band-gated",
5956
+ BOYSPP: "feature-band-gated",
5957
+ BCNSPP: "feature-band-gated",
5958
+ BOYISD: "feature-band-gated",
5959
+ BOYSAW: "feature-band-gated",
5960
+ BRIDGE: "feature-band-gated",
5961
+ PILPNT: "feature-band-gated",
5962
+ MORFAC: "feature-band-gated",
5963
+ SLCONS: "feature-band-gated",
5964
+ BUISGL: "feature-band-gated",
5965
+ LNDMRK: "feature-band-gated"
5966
+ };
5967
+ function getLabelBehavior(layerName) {
5968
+ return LABEL_BEHAVIOR[layerName] ?? "feature-band-gated";
5969
+ }
5970
+ function getLabelConfig(layerName, opts = {}) {
5349
5971
  switch (layerName) {
5350
- case "SOUNDG":
5972
+ case "SOUNDG": {
5973
+ const ct = opts.colorTable ?? "DAY";
5974
+ const safetyContour = opts.safetyContour ?? 10;
5975
+ const twoShade = opts.twoShadeSoundings ?? true;
5976
+ const weight = opts.soundingFontWeight ?? "semibold";
5977
+ const screenColors = ct === "DAY" ? { shoaler: DARK_BG, deeper: "#4a5560", halo: "#ffffff" } : { shoaler: resolveColor("SNDG2", ct), deeper: resolveColor("SNDG1", ct), halo: resolveColor("UIBCK", ct) };
5978
+ const depthExpr = [
5979
+ "to-number",
5980
+ ["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
5981
+ 9999
5982
+ ];
5983
+ const textColor = twoShade ? [
5984
+ "case",
5985
+ ["<", depthExpr, safetyContour],
5986
+ screenColors.shoaler,
5987
+ screenColors.deeper
5988
+ ] : screenColors.shoaler;
5989
+ const haloColor = screenColors.halo;
5990
+ const fontStack = weight === "semibold" ? ["Open Sans Semibold", "Open Sans Regular"] : ["Open Sans Regular"];
5351
5991
  return {
5352
5992
  type: "symbol",
5353
- textField: ["get", "depth"],
5993
+ // Property name varies by source path:
5994
+ // GeoJSON path → preprocessSoundings adds lowercase `depth` (string)
5995
+ // PMTiles path → baremetal ingest writes uppercase `DEPTH` (number)
5996
+ // raw S-57 → `VALSOU` last-resort fallback
5997
+ textField: [
5998
+ "coalesce",
5999
+ ["get", "depth"],
6000
+ ["get", "DEPTH"],
6001
+ ["get", "VALSOU"]
6002
+ ],
6003
+ // Compact ladder tuned for screen DPI — small but legible against
6004
+ // dense depth-contour fields. 0.5 px halo proved too thin (text
6005
+ // disappeared into contours); 2.0+ px proved too thick (fuzzy
6006
+ // blob). Settled here: 7/8/10/12 across z8/z11/z14/z16 with a
6007
+ // flat 1.0 px halo — enough cut-out for separation without bulk.
5354
6008
  textSize: [
5355
6009
  "interpolate",
5356
6010
  ["linear"],
@@ -5358,39 +6012,67 @@ function getLabelConfig(layerName) {
5358
6012
  8,
5359
6013
  7,
5360
6014
  11,
5361
- 9,
6015
+ 8,
5362
6016
  14,
5363
- 11
6017
+ 10,
6018
+ 16,
6019
+ 12
5364
6020
  ],
5365
- textColor: "#c8c8c8",
5366
- textHaloColor: DARK_BG,
5367
- textHaloWidth: 1.2,
6021
+ textColor,
6022
+ textHaloColor: haloColor,
6023
+ textHaloWidth: 1,
5368
6024
  textOffset: [0, 0],
5369
- textFont: ["Open Sans Regular", "Arial Unicode MS Regular"],
6025
+ textFont: fontStack,
5370
6026
  minzoom: 8,
5371
6027
  layout: {
5372
6028
  "text-anchor": "center",
5373
6029
  "text-allow-overlap": false,
5374
- "text-padding": 5,
6030
+ // Slightly more breathing room around the (now larger) glyphs.
6031
+ "text-padding": 6,
5375
6032
  "text-optional": true,
6033
+ // Small positive tracking opens up dense numerals at small
6034
+ // sizes — established cartographic typography practice.
6035
+ "text-letter-spacing": 0.02,
5376
6036
  // Shallowest-wins: Mapbox GL renders lower sort keys first and
5377
6037
  // gives them collision priority. Using depth directly ensures
5378
6038
  // the shallowest sounding in a cluster is always the one shown,
5379
6039
  // even when labels are hidden due to overlap.
5380
- "symbol-sort-key": ["to-number", ["get", "depth"], 999]
6040
+ "symbol-sort-key": [
6041
+ "to-number",
6042
+ ["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
6043
+ 999
6044
+ ]
5381
6045
  }
5382
6046
  };
6047
+ }
5383
6048
  case "LNDRGN":
5384
6049
  return {
5385
6050
  type: "symbol",
5386
6051
  textField: ["get", "OBJNAM"],
5387
- textSize: 11,
5388
- textColor: "#e0d8c8",
6052
+ textSize: [
6053
+ "interpolate",
6054
+ ["linear"],
6055
+ ["zoom"],
6056
+ 8,
6057
+ 10,
6058
+ 12,
6059
+ 12,
6060
+ 15,
6061
+ 14
6062
+ ],
6063
+ textColor: COLOR.LAND_REGION,
5389
6064
  textHaloColor: DARK_BG,
5390
- textHaloWidth: 1.5,
6065
+ textHaloWidth: 1.4,
5391
6066
  textOffset: [0, 0],
5392
- textFont: ["Open Sans Italic", "Arial Unicode MS Regular"],
5393
- minzoom: 9
6067
+ textFont: ["Open Sans Regular"],
6068
+ minzoom: 8,
6069
+ layout: {
6070
+ "text-anchor": "center",
6071
+ "text-allow-overlap": false,
6072
+ "text-padding": 6,
6073
+ "text-optional": true,
6074
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
6075
+ }
5394
6076
  };
5395
6077
  case "LNDMRK":
5396
6078
  return {
@@ -5401,166 +6083,300 @@ function getLabelConfig(layerName) {
5401
6083
  textHaloColor: DARK_BG,
5402
6084
  textHaloWidth: 1.5,
5403
6085
  textOffset: [0, 1.2],
5404
- minzoom: 10
6086
+ minzoom: 9
5405
6087
  };
5406
6088
  case "BOYLAT":
5407
6089
  return {
5408
6090
  type: "symbol",
5409
6091
  textField: ["coalesce", ["get", "_navAidLabel"], ["get", "OBJNAM"]],
5410
- textSize: 8,
5411
- textColor: "#ffffff",
5412
- textHaloColor: DARK_BG,
6092
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6093
+ textFont: NAV_AID_LABEL_FONT,
6094
+ textColor: DARK_BG,
6095
+ textHaloColor: "#ffffff",
5413
6096
  textHaloWidth: 1.5,
5414
6097
  textOffset: [0, 1.5],
5415
- minzoom: 12
6098
+ minzoom: 10
5416
6099
  };
5417
6100
  case "BCNLAT":
5418
6101
  return {
5419
6102
  type: "symbol",
5420
6103
  textField: ["coalesce", ["get", "_navAidLabel"], ["get", "OBJNAM"]],
5421
- textSize: 8,
5422
- textColor: "#ffffff",
5423
- textHaloColor: DARK_BG,
6104
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6105
+ textFont: NAV_AID_LABEL_FONT,
6106
+ textColor: DARK_BG,
6107
+ textHaloColor: "#ffffff",
5424
6108
  textHaloWidth: 1.5,
5425
6109
  textOffset: [0, 1.5],
5426
- minzoom: 12
6110
+ minzoom: 10
5427
6111
  };
5428
6112
  case "BOYCAR":
5429
6113
  case "BCNCAR":
5430
6114
  return {
5431
6115
  type: "symbol",
5432
6116
  textField: ["coalesce", ["get", "_navAidLabel"], ["get", "OBJNAM"]],
5433
- textSize: 8,
5434
- textColor: "#fdf0b0",
5435
- textHaloColor: DARK_BG,
6117
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6118
+ textFont: NAV_AID_LABEL_FONT,
6119
+ textColor: DARK_BG,
6120
+ textHaloColor: "#fdf0b0",
5436
6121
  textHaloWidth: 1.5,
5437
6122
  textOffset: [0, 1.5],
5438
- minzoom: 12
6123
+ minzoom: 10
5439
6124
  };
5440
6125
  case "BOYSPP":
5441
6126
  case "BCNSPP":
5442
6127
  return {
5443
6128
  type: "symbol",
5444
6129
  textField: ["coalesce", ["get", "_navAidLabel"], ["get", "OBJNAM"]],
5445
- textSize: 8,
5446
- textColor: "#eab308",
5447
- textHaloColor: DARK_BG,
6130
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6131
+ textFont: NAV_AID_LABEL_FONT,
6132
+ textColor: DARK_BG,
6133
+ textHaloColor: "#eab308",
5448
6134
  textHaloWidth: 1.5,
5449
6135
  textOffset: [0, 1.5],
5450
- minzoom: 12
6136
+ minzoom: 10
5451
6137
  };
5452
6138
  case "BOYISD":
5453
6139
  case "BOYSAW":
5454
6140
  return {
5455
6141
  type: "symbol",
5456
6142
  textField: ["coalesce", ["get", "_navAidLabel"], ["get", "OBJNAM"]],
5457
- textSize: 8,
5458
- textColor: "#ffffff",
5459
- textHaloColor: DARK_BG,
6143
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6144
+ textFont: NAV_AID_LABEL_FONT,
6145
+ textColor: DARK_BG,
6146
+ textHaloColor: "#ffffff",
5460
6147
  textHaloWidth: 1.5,
5461
6148
  textOffset: [0, 1.5],
5462
- minzoom: 12
6149
+ minzoom: 10
5463
6150
  };
5464
6151
  case "LIGHTS":
5465
6152
  return {
5466
6153
  type: "symbol",
5467
6154
  textField: ["get", "_lightLabel"],
5468
- textSize: 8,
5469
- textColor: "#fdf0b0",
5470
- textHaloColor: DARK_BG,
6155
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6156
+ textFont: NAV_AID_LABEL_FONT,
6157
+ textColor: DARK_BG,
6158
+ textHaloColor: "#fdf0b0",
6159
+ textHaloWidth: 1.5,
6160
+ textOffset: [2, 0],
6161
+ minzoom: 9,
6162
+ layout: {
6163
+ "text-anchor": "left"
6164
+ }
6165
+ };
6166
+ case "FOGSIG": {
6167
+ const catfogExpr = [
6168
+ "match",
6169
+ ["to-number", ["coalesce", ["get", "CATFOG"], 0], 0],
6170
+ 2,
6171
+ "DIA",
6172
+ 3,
6173
+ "SIREN",
6174
+ 4,
6175
+ "NAUTO",
6176
+ 5,
6177
+ "BELL",
6178
+ 6,
6179
+ "WHIS",
6180
+ 7,
6181
+ "REED",
6182
+ 8,
6183
+ "GONG",
6184
+ 9,
6185
+ "HORN",
6186
+ ""
6187
+ ];
6188
+ return {
6189
+ type: "symbol",
6190
+ textField: catfogExpr,
6191
+ textSize: NAV_AID_LABEL_TEXT_SIZE,
6192
+ textFont: NAV_AID_LABEL_FONT,
6193
+ textColor: DARK_BG,
6194
+ textHaloColor: "#b0c8e8",
5471
6195
  textHaloWidth: 1.5,
5472
- textOffset: [1.5, 0],
5473
- minzoom: 11
6196
+ textOffset: [2, 1.2],
6197
+ minzoom: 11,
6198
+ layout: {
6199
+ "text-anchor": "left",
6200
+ "text-allow-overlap": false,
6201
+ "text-optional": true
6202
+ }
5474
6203
  };
6204
+ }
5475
6205
  case "SEAARE":
5476
6206
  return {
5477
6207
  type: "symbol",
5478
6208
  textField: ["get", "OBJNAM"],
5479
- textSize: 13,
5480
- textColor: "#8fb8d0",
6209
+ textSize: [
6210
+ "interpolate",
6211
+ ["linear"],
6212
+ ["zoom"],
6213
+ 5,
6214
+ 11,
6215
+ 8,
6216
+ 13,
6217
+ 11,
6218
+ 15,
6219
+ 14,
6220
+ 17
6221
+ ],
6222
+ textColor: COLOR.WATER_DEEP,
5481
6223
  textHaloColor: DARK_BG,
5482
6224
  textHaloWidth: 1.8,
5483
6225
  textOffset: [0, 0],
5484
- textFont: ["Open Sans Italic", "Arial Unicode MS Regular"],
5485
- minzoom: 6,
6226
+ textFont: ["Open Sans Italic"],
6227
+ minzoom: 5,
5486
6228
  layout: {
5487
6229
  "text-anchor": "center",
5488
6230
  "text-allow-overlap": false,
5489
- "text-padding": 8,
6231
+ "text-padding": 10,
5490
6232
  "text-optional": true,
5491
- "text-letter-spacing": 0.15
6233
+ "text-letter-spacing": 0.15,
6234
+ "text-max-width": 9,
6235
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
5492
6236
  }
5493
6237
  };
5494
6238
  case "LAKARE":
5495
6239
  return {
5496
6240
  type: "symbol",
5497
6241
  textField: ["get", "OBJNAM"],
5498
- textSize: 11,
5499
- textColor: "#8fb8d0",
6242
+ textSize: [
6243
+ "interpolate",
6244
+ ["linear"],
6245
+ ["zoom"],
6246
+ 8,
6247
+ 10,
6248
+ 12,
6249
+ 12,
6250
+ 15,
6251
+ 14
6252
+ ],
6253
+ textColor: COLOR.WATER,
5500
6254
  textHaloColor: DARK_BG,
5501
6255
  textHaloWidth: 1.5,
5502
6256
  textOffset: [0, 0],
5503
- textFont: ["Open Sans Italic", "Arial Unicode MS Regular"],
5504
- minzoom: 9,
6257
+ textFont: ["Open Sans Italic"],
6258
+ minzoom: 8,
5505
6259
  layout: {
5506
6260
  "text-anchor": "center",
5507
6261
  "text-allow-overlap": false,
5508
6262
  "text-padding": 6,
5509
- "text-optional": true
6263
+ "text-optional": true,
6264
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
5510
6265
  }
5511
6266
  };
5512
6267
  case "RIVERS":
5513
6268
  return {
5514
6269
  type: "symbol",
5515
6270
  textField: ["get", "OBJNAM"],
5516
- textSize: 10,
5517
- textColor: "#8fb8d0",
6271
+ textSize: [
6272
+ "interpolate",
6273
+ ["linear"],
6274
+ ["zoom"],
6275
+ 9,
6276
+ 9,
6277
+ 12,
6278
+ 11,
6279
+ 15,
6280
+ 13
6281
+ ],
6282
+ textColor: COLOR.WATER,
5518
6283
  textHaloColor: DARK_BG,
5519
- textHaloWidth: 1.5,
6284
+ textHaloWidth: 1.4,
5520
6285
  textOffset: [0, 0],
5521
- textFont: ["Open Sans Italic", "Arial Unicode MS Regular"],
5522
- minzoom: 10,
6286
+ textFont: ["Open Sans Italic"],
6287
+ minzoom: 9,
5523
6288
  layout: {
5524
6289
  "text-anchor": "center",
5525
6290
  "text-allow-overlap": false,
5526
6291
  "text-padding": 4,
5527
- "text-optional": true
6292
+ "text-optional": true,
6293
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
5528
6294
  }
5529
6295
  };
5530
6296
  case "CANALS":
5531
6297
  return {
5532
6298
  type: "symbol",
5533
6299
  textField: ["get", "OBJNAM"],
5534
- textSize: 9,
5535
- textColor: "#8fb8d0",
6300
+ textSize: [
6301
+ "interpolate",
6302
+ ["linear"],
6303
+ ["zoom"],
6304
+ 11,
6305
+ 9,
6306
+ 14,
6307
+ 11
6308
+ ],
6309
+ textColor: COLOR.WATER,
5536
6310
  textHaloColor: DARK_BG,
5537
- textHaloWidth: 1.5,
6311
+ textHaloWidth: 1.3,
5538
6312
  textOffset: [0, 0],
5539
- textFont: ["Open Sans Italic", "Arial Unicode MS Regular"],
6313
+ textFont: ["Open Sans Italic"],
5540
6314
  minzoom: 11,
5541
6315
  layout: {
5542
6316
  "text-anchor": "center",
5543
6317
  "text-allow-overlap": false,
5544
6318
  "text-padding": 4,
5545
- "text-optional": true
6319
+ "text-optional": true,
6320
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
5546
6321
  }
5547
6322
  };
5548
6323
  case "LNDARE":
5549
6324
  return {
5550
6325
  type: "symbol",
5551
6326
  textField: ["get", "OBJNAM"],
5552
- textSize: 10,
5553
- textColor: "#e0d8c8",
6327
+ textSize: [
6328
+ "interpolate",
6329
+ ["linear"],
6330
+ ["zoom"],
6331
+ 8,
6332
+ 9,
6333
+ 12,
6334
+ 11,
6335
+ 15,
6336
+ 13
6337
+ ],
6338
+ textColor: COLOR.LAND_NATURAL,
5554
6339
  textHaloColor: DARK_BG,
5555
- textHaloWidth: 1.5,
6340
+ textHaloWidth: 1.4,
5556
6341
  textOffset: [0, 0],
5557
- textFont: ["Open Sans Regular", "Arial Unicode MS Regular"],
5558
- minzoom: 10,
6342
+ textFont: ["Open Sans Regular"],
6343
+ minzoom: 8,
5559
6344
  layout: {
5560
6345
  "text-anchor": "center",
5561
6346
  "text-allow-overlap": false,
5562
6347
  "text-padding": 6,
5563
- "text-optional": true
6348
+ "text-optional": true,
6349
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
6350
+ }
6351
+ };
6352
+ case "BUAARE":
6353
+ return {
6354
+ type: "symbol",
6355
+ textField: ["get", "OBJNAM"],
6356
+ textSize: [
6357
+ "interpolate",
6358
+ ["linear"],
6359
+ ["zoom"],
6360
+ 7,
6361
+ 11,
6362
+ 10,
6363
+ 13,
6364
+ 13,
6365
+ 15
6366
+ ],
6367
+ textColor: COLOR.CITY,
6368
+ textHaloColor: DARK_BG,
6369
+ textHaloWidth: 1.6,
6370
+ textOffset: [0, 0],
6371
+ textFont: ["Open Sans Regular"],
6372
+ minzoom: 7,
6373
+ layout: {
6374
+ "text-anchor": "center",
6375
+ "text-allow-overlap": false,
6376
+ "text-padding": 8,
6377
+ "text-optional": true,
6378
+ "text-letter-spacing": 0.05,
6379
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
5564
6380
  }
5565
6381
  };
5566
6382
  case "BRIDGE":
@@ -5634,19 +6450,236 @@ function getLabelConfig(layerName) {
5634
6450
  return {
5635
6451
  type: "symbol",
5636
6452
  textField: ["get", "OBJNAM"],
5637
- textSize: 10,
5638
- textColor: "#d4c9b0",
6453
+ textSize: [
6454
+ "interpolate",
6455
+ ["linear"],
6456
+ ["zoom"],
6457
+ 9,
6458
+ 9,
6459
+ 13,
6460
+ 11
6461
+ ],
6462
+ textColor: COLOR.AIRPORT,
5639
6463
  textHaloColor: DARK_BG,
5640
- textHaloWidth: 1.5,
6464
+ textHaloWidth: 1.4,
6465
+ textOffset: [0, 0],
6466
+ textFont: ["Open Sans Regular"],
6467
+ minzoom: 9,
6468
+ layout: {
6469
+ "text-anchor": "center",
6470
+ "text-allow-overlap": false,
6471
+ "text-padding": 5,
6472
+ "text-optional": true,
6473
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
6474
+ }
6475
+ };
6476
+ case "SBDARE":
6477
+ return {
6478
+ type: "symbol",
6479
+ textField: buildSeabedLabelExpression(),
6480
+ textSize: [
6481
+ "interpolate",
6482
+ ["linear"],
6483
+ ["zoom"],
6484
+ 12,
6485
+ 9,
6486
+ 16,
6487
+ 11
6488
+ ],
6489
+ textColor: COLOR.SOUNDING,
6490
+ textHaloColor: DARK_BG,
6491
+ textHaloWidth: 1,
6492
+ textOffset: [0, 0],
6493
+ textFont: ["Open Sans Italic"],
6494
+ minzoom: 12,
6495
+ layout: {
6496
+ "symbol-placement": "point",
6497
+ "text-anchor": "center",
6498
+ "text-allow-overlap": false,
6499
+ "text-padding": 4,
6500
+ "text-optional": true,
6501
+ "text-letter-spacing": 0.02
6502
+ }
6503
+ };
6504
+ case "MAGVAR": {
6505
+ const valmag = ["coalesce", ["get", "VALMAG"], 0];
6506
+ const variationText = [
6507
+ "case",
6508
+ [">=", valmag, 0],
6509
+ ["concat", ["to-string", ["floor", valmag]], "\xB0E"],
6510
+ ["concat", ["to-string", ["floor", ["abs", valmag]]], "\xB0W"]
6511
+ ];
6512
+ return {
6513
+ type: "symbol",
6514
+ textField: variationText,
6515
+ textSize: [
6516
+ "interpolate",
6517
+ ["linear"],
6518
+ ["zoom"],
6519
+ 10,
6520
+ 9,
6521
+ 14,
6522
+ 11
6523
+ ],
6524
+ textColor: COLOR.SOUNDING,
6525
+ textHaloColor: DARK_BG,
6526
+ textHaloWidth: 1,
5641
6527
  textOffset: [0, 0],
6528
+ textFont: ["Open Sans Italic"],
5642
6529
  minzoom: 10,
5643
6530
  layout: {
6531
+ "symbol-placement": "point",
5644
6532
  "text-anchor": "center",
5645
6533
  "text-allow-overlap": false,
5646
6534
  "text-padding": 6,
5647
- "text-optional": true
6535
+ "text-optional": true,
6536
+ "text-letter-spacing": 0.02
6537
+ }
6538
+ };
6539
+ }
6540
+ case "PRDARE":
6541
+ return {
6542
+ type: "symbol",
6543
+ textField: ["get", "OBJNAM"],
6544
+ textSize: [
6545
+ "interpolate",
6546
+ ["linear"],
6547
+ ["zoom"],
6548
+ 8,
6549
+ 10,
6550
+ 12,
6551
+ 12,
6552
+ 15,
6553
+ 14
6554
+ ],
6555
+ textColor: "#c545c3",
6556
+ textHaloColor: DARK_BG,
6557
+ textHaloWidth: 1.4,
6558
+ textOffset: [0, 0],
6559
+ textFont: ["Open Sans Italic"],
6560
+ minzoom: 8,
6561
+ layout: {
6562
+ "text-anchor": "center",
6563
+ "text-allow-overlap": false,
6564
+ "text-padding": 6,
6565
+ "text-optional": true,
6566
+ "symbol-sort-key": ORIENTATION_BAND_SORT_KEY
6567
+ }
6568
+ };
6569
+ case "WEDKLP":
6570
+ return {
6571
+ type: "symbol",
6572
+ textField: "Wd",
6573
+ textSize: [
6574
+ "interpolate",
6575
+ ["linear"],
6576
+ ["zoom"],
6577
+ 12,
6578
+ 9,
6579
+ 16,
6580
+ 11
6581
+ ],
6582
+ textColor: "#3f8a34",
6583
+ textHaloColor: DARK_BG,
6584
+ textHaloWidth: 1,
6585
+ textOffset: [0, 0],
6586
+ textFont: ["Open Sans Italic"],
6587
+ minzoom: 12,
6588
+ layout: {
6589
+ "symbol-placement": "point",
6590
+ "text-anchor": "center",
6591
+ "text-allow-overlap": false,
6592
+ "text-padding": 4,
6593
+ "text-optional": true,
6594
+ "text-letter-spacing": 0.02
6595
+ }
6596
+ };
6597
+ case "UWTROC":
6598
+ case "OBSTRN":
6599
+ case "WRECKS": {
6600
+ const ct = opts.colorTable ?? "DAY";
6601
+ const valsou = [
6602
+ "to-number",
6603
+ ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 0],
6604
+ 0
6605
+ ];
6606
+ return {
6607
+ type: "symbol",
6608
+ textField: [
6609
+ "case",
6610
+ [">", valsou, 0],
6611
+ [
6612
+ "concat",
6613
+ ["to-string", ["/", ["round", ["*", valsou, 10]], 10]],
6614
+ "m"
6615
+ ],
6616
+ ""
6617
+ ],
6618
+ textSize: [
6619
+ "interpolate",
6620
+ ["linear"],
6621
+ ["zoom"],
6622
+ 12,
6623
+ 9,
6624
+ 16,
6625
+ 11
6626
+ ],
6627
+ textColor: resolveColor("CHBLK", ct),
6628
+ textHaloColor: resolveColor("CHWHT", ct),
6629
+ textHaloWidth: 1.2,
6630
+ // Place to the upper-right of the symbol so it doesn't sit on top
6631
+ // of the asterisk / cross. Anchor top-left so the offset grows
6632
+ // away from the symbol as text-size grows.
6633
+ textOffset: [0.8, -0.4],
6634
+ textFont: ["Open Sans Regular"],
6635
+ minzoom: 12,
6636
+ layout: {
6637
+ "text-anchor": "top-left",
6638
+ "text-allow-overlap": false,
6639
+ "text-optional": true,
6640
+ "text-padding": 2
6641
+ }
6642
+ };
6643
+ }
6644
+ case "CBLOHD":
6645
+ case "PIPOHD": {
6646
+ const isCBL = layerName === "CBLOHD";
6647
+ return {
6648
+ type: "symbol",
6649
+ textField: [
6650
+ "case",
6651
+ ["has", "VERCLR"],
6652
+ ["concat", ["to-string", ["get", "VERCLR"]], "m"],
6653
+ ""
6654
+ ],
6655
+ textSize: [
6656
+ "interpolate",
6657
+ ["linear"],
6658
+ ["zoom"],
6659
+ 11,
6660
+ 8,
6661
+ 14,
6662
+ 10,
6663
+ 17,
6664
+ 11
6665
+ ],
6666
+ textColor: isCBL ? "#c545c3" : "#7fbf3f",
6667
+ textHaloColor: DARK_BG,
6668
+ textHaloWidth: 1.4,
6669
+ textOffset: [0, 0],
6670
+ textFont: ["Open Sans Regular"],
6671
+ minzoom: 11,
6672
+ layout: {
6673
+ "symbol-placement": "line",
6674
+ "text-anchor": "center",
6675
+ "symbol-spacing": 250,
6676
+ "text-rotation-alignment": "map",
6677
+ "text-allow-overlap": false,
6678
+ "text-optional": true,
6679
+ "text-letter-spacing": 0.02
5648
6680
  }
5649
6681
  };
6682
+ }
5650
6683
  case "DEPCNT":
5651
6684
  return {
5652
6685
  type: "symbol",
@@ -5660,12 +6693,12 @@ function getLabelConfig(layerName) {
5660
6693
  14,
5661
6694
  10
5662
6695
  ],
5663
- textColor: "#c8c8c8",
5664
- textHaloColor: DARK_BG,
6696
+ textColor: DARK_BG,
6697
+ textHaloColor: "#c8c8c8",
5665
6698
  textHaloWidth: 1.2,
5666
6699
  textOffset: [0, 0],
5667
- textFont: ["Open Sans Regular", "Arial Unicode MS Regular"],
5668
- minzoom: 10,
6700
+ textFont: ["Open Sans Regular"],
6701
+ minzoom: 9,
5669
6702
  layout: {
5670
6703
  "symbol-placement": "line",
5671
6704
  "text-anchor": "center",
@@ -5696,7 +6729,7 @@ function buildLabelLayer(layerId, sourceId, config, visible, overrides) {
5696
6729
  visibility: visible ? "visible" : "none",
5697
6730
  "text-field": config.textField,
5698
6731
  "text-size": textSize,
5699
- "text-font": config.textFont ?? ["Open Sans Regular", "Arial Unicode MS Regular"],
6732
+ "text-font": config.textFont ?? ["Open Sans Regular"],
5700
6733
  "text-offset": config.textOffset,
5701
6734
  "text-anchor": "top",
5702
6735
  "text-allow-overlap": overrides?.allowOverlap ?? false,
@@ -5779,6 +6812,7 @@ var LABELED_LAYERS = /* @__PURE__ */ new Set([
5779
6812
  "LNDRGN",
5780
6813
  "LNDMRK",
5781
6814
  "LNDARE",
6815
+ "BUAARE",
5782
6816
  "BOYLAT",
5783
6817
  "BCNLAT",
5784
6818
  "BOYCAR",
@@ -5788,6 +6822,7 @@ var LABELED_LAYERS = /* @__PURE__ */ new Set([
5788
6822
  "BCNCAR",
5789
6823
  "BCNSPP",
5790
6824
  "LIGHTS",
6825
+ "FOGSIG",
5791
6826
  "SEAARE",
5792
6827
  "LAKARE",
5793
6828
  "RIVERS",
@@ -5798,7 +6833,16 @@ var LABELED_LAYERS = /* @__PURE__ */ new Set([
5798
6833
  "SLCONS",
5799
6834
  "BUISGL",
5800
6835
  "AIRARE",
5801
- "DEPCNT"
6836
+ "DEPCNT",
6837
+ "SBDARE",
6838
+ "MAGVAR",
6839
+ "PRDARE",
6840
+ "WEDKLP",
6841
+ "UWTROC",
6842
+ "OBSTRN",
6843
+ "WRECKS",
6844
+ "CBLOHD",
6845
+ "PIPOHD"
5802
6846
  ]);
5803
6847
  var NAV_AID_LABEL_LAYERS = /* @__PURE__ */ new Set([
5804
6848
  "BOYLAT",
@@ -7114,7 +8158,7 @@ var S52_POINT_SVGS = {
7114
8158
  "BOYDEF03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 21.8 10.8 L 22.4 9.6 L 22.9 8.6 L 23.6 7.9 L 24.3 7.2 L 25.4 6.8 L 26.4 6.9 L 27.6 7.2 L 28.8 7.5 L 29.8 8.9 L 30.5 9.7 L 31.0 10.6 L 31.0 11.7 L 30.5 12.7 L 30.0 13.5 L 29.1 14.5 L 28.2 15.6 L 27.3 16.6 L 26.8 17.4 L 26.3 18.1 L 26.2 19.1 L 26.2 20.0 L 26.2 20.7 L 26.3 21.6 L 26.3 21.7" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 25.4 25.2 L 27.6 25.2" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="9.45" cy="16.19" r="8.45" fill="none" stroke="CHGRD" stroke-width="0.8" />\n <path d="M 9.4 16.2 Z" fill="CHGRD" />\n <circle cx="9.45" cy="16.19" r="8.45" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <circle cx="9.45" cy="16.19" r="0.62" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 9.4 16.2 Z" fill="OUTLW" />\n</svg>\n',
7115
8159
  "BOYGEN03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 22.4 10.5 L 22.9 9.4 L 23.4 8.4 L 24.1 7.7 L 24.7 7.1 L 25.7 6.7 L 26.6 6.8 L 27.8 7.1 L 28.9 7.4 L 29.8 8.7 L 30.5 9.5 L 31.0 10.3 L 31.0 11.3 L 30.5 12.2 L 30.0 13.1 L 29.2 14.0 L 28.3 15.0 L 27.5 16.0 L 27.0 16.7 L 26.6 17.4 L 26.5 18.3 L 26.5 19.2 L 26.5 19.8 L 26.6 20.7 L 26.6 20.8" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 25.7 24.1 L 27.8 24.1" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="11.24" cy="14.16" r="6.78" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="11.31" cy="23.14" r="2.16" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 13.5 23.1 L 21.6 23.1" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 23.1 L 9.1 23.1" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7116
8160
  "BOYINB01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.02" cy="22.46" r="2.28" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="16.02" cy="9.75" r="2.49" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 1.0 22.5 L 13.6 22.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.1 22.5 L 31.0 22.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 2.6 22.5 L 6.0 12.5 L 26.0 12.5 L 29.4 22.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7117
- "BOYISD12": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="20.52" cy="7.07" r="6.07" fill="none" stroke="CHRED" stroke-width="0.8" />\n <path d="M 20.5 7.1 Z" fill="CHRED" />\n <circle cx="11.48" cy="24.93" r="6.07" fill="none" stroke="CHRED" stroke-width="0.8" />\n <path d="M 11.5 24.9 Z" fill="CHRED" />\n <circle cx="20.52" cy="7.07" r="6.07" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <circle cx="11.48" cy="24.93" r="6.07" fill="none" stroke="OUTLW" stroke-width="0.8" />\n</svg>\n',
8161
+ "BOYISD12": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 27.0 L 12.5 27.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" />\n <path d="M 19.5 27.0 L 31.0 27.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" />\n <path d="M 10.5 27.0 L 12.5 15.0 L 19.5 15.0 L 21.5 27.0 Z" fill="CHBLK" stroke="CHBLK" stroke-width="0.8" stroke-linejoin="round" />\n <rect x="11.4" y="19.0" width="9.2" height="3.5" fill="LITRD" />\n <line x1="16" y1="15.0" x2="16" y2="11.0" stroke="CHBLK" stroke-width="0.8" />\n <circle cx="16" cy="8.5" r="2.0" fill="CHBLK" />\n <circle cx="16" cy="3.5" r="2.0" fill="CHBLK" />\n</svg>\n',
7118
8162
  "BOYLAT13": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0 Z" fill="CHGRN" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="CHGRN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="OUTLW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="22.32" cy="21.30" r="1.03" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 22.3 21.3 Z" fill="OUTLW" />\n</svg>\n',
7119
8163
  "BOYLAT14": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0 Z" fill="CHRED" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="CHRED" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="OUTLW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="22.32" cy="21.30" r="1.03" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 22.3 21.3 Z" fill="OUTLW" />\n</svg>\n',
7120
8164
  "BOYLAT23": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 27.3 L 9.6 4.7 L 31.0 4.7 L 23.8 27.3 L 1.0 27.3 Z" fill="CHGRN" />\n <path d="M 1.0 27.3 L 9.6 4.7 L 31.0 4.7 L 23.8 27.3 L 1.0 27.3" fill="none" stroke="CHGRN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 27.3 L 9.6 4.7 L 31.0 4.7 L 23.8 27.3 L 1.0 27.3" fill="none" stroke="OUTLW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.63" cy="15.52" r="0.85" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 16.6 15.5 Z" fill="OUTLW" />\n</svg>\n',
@@ -7125,7 +8169,7 @@ var S52_POINT_SVGS = {
7125
8169
  "BOYMOR03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="14.88" cy="26.37" r="3.17" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="19.41" cy="6.11" r="3.65" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 1.0 26.4 L 11.5 26.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 17.8 26.4 L 28.7 26.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.3 26.4 L 7.9 10.2 L 31.0 10.2 L 26.6 26.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7126
8170
  "BOYMOR11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.30" cy="11.89" r="2.81" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 16.3 11.9 Z" fill="CHBLK" />\n <path d="M 1.0 22.9 L 3.6 15.1 L 28.4 15.1 L 31.0 22.9 L 1.0 22.9 Z" fill="CHBLK" />\n <path d="M 1.0 22.9 L 3.6 15.1 L 28.4 15.1 L 31.0 22.9 L 1.0 22.9" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7127
8171
  "BOYPIL01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.43" cy="25.37" r="2.96" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 1.0 25.4 L 13.6 25.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.1 25.4 L 31.0 25.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.3 25.4 L 15.3 13.6 L 18.6 3.7 L 23.5 3.7 L 23.5 15.6 L 28.2 25.4" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7128
- "BOYSAW12": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="14.78" fill="none" stroke="CHRED" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="CHRED" />\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <circle cx="16.00" cy="16.00" r="1.11" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="OUTLW" />\n</svg>\n',
8172
+ "BOYSAW12": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 27.0 L 12.5 27.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" />\n <path d="M 19.5 27.0 L 31.0 27.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" />\n <path d="M 10.5 27.0 L 12.5 14.0 L 19.5 14.0 L 21.5 27.0 Z" fill="LITWT" stroke="CHBLK" stroke-width="0.8" stroke-linejoin="round" />\n <rect x="13.2" y="14.0" width="2.0" height="13.0" fill="LITRD" />\n <rect x="16.8" y="14.0" width="2.0" height="13.0" fill="LITRD" />\n <line x1="16" y1="14.0" x2="16" y2="8.0" stroke="CHBLK" stroke-width="0.8" />\n <circle cx="16" cy="5.5" r="2.5" fill="LITRD" stroke="CHBLK" stroke-width="0.6" />\n</svg>\n',
7129
8173
  "BOYSPH01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="15.76" cy="26.22" r="2.81" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 18.7 26.2 L 31.0 26.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.1 26.2 L 2.6 22.6 L 2.2 19.6 L 1.8 16.4 L 2.5 12.9 L 3.5 10.5 L 5.3 8.1 L 7.3 6.1 L 9.8 4.5 L 12.1 3.7 L 14.9 3.0 L 17.9 3.0 L 20.9 3.7 L 24.2 5.1 L 26.4 6.8 L 27.9 9.0 L 29.7 11.8 L 30.7 14.6 L 30.8 17.7 L 30.5 21.5 L 29.4 24.2 L 28.3 26.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 26.2 L 12.9 26.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7130
8174
  "BOYSPP11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CHYLW" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="CHYLW" />\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <circle cx="16.00" cy="16.00" r="1.13" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="OUTLW" />\n</svg>\n',
7131
8175
  "BOYSPP15": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0 Z" fill="CHYLW" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="CHYLW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 30.8 31.0 L 30.8 1.0 L 1.2 31.0 L 30.8 31.0" fill="none" stroke="OUTLW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="20.95" cy="21.30" r="1.03" fill="none" stroke="OUTLW" stroke-width="0.8" />\n <path d="M 21.0 21.3 Z" fill="OUTLW" />\n</svg>\n',
@@ -7134,7 +8178,11 @@ var S52_POINT_SVGS = {
7134
8178
  "BOYSUP01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.02" cy="23.18" r="2.28" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 1.0 23.2 L 13.6 23.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.1 23.2 L 31.0 23.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 2.6 23.2 L 6.0 13.2 L 26.0 13.2 L 29.4 23.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 13.2 L 16.0 6.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7135
8179
  "BOYSUP02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.1 25.3 L 4.9 14.1 L 27.2 14.1 L 31.0 25.3" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.1 14.1 L 16.1 6.7" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 25.2 L 4.9 14.0 L 27.2 14.1 L 31.0 25.2 L 1.0 25.2 Z" fill="CHBLK" />\n <path d="M 1.0 25.2 L 4.9 14.0 L 27.2 14.1 L 31.0 25.2 L 1.0 25.2" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7136
8180
  "BOYSUP03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.12" cy="24.98" r="2.07" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 16.1 9.9 L 16.1 5.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 6.6 L 19.3 9.9" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 9.9 L 19.3 6.6" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 25.0 L 14.0 25.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.0 25.0 L 31.0 25.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 2.6 25.0 L 6.0 14.9 L 26.0 14.9 L 29.4 25.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.1 14.9 L 16.1 8.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7137
- "BRIDGE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CHMGD" stroke-width="0.8" />\n <circle cx="16.07" cy="15.70" r="8.63" fill="none" stroke="CHMGD" stroke-width="0.8" />\n</svg>\n',
8181
+ "BRIDGE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="4" y1="14" x2="28" y2="14" stroke="CHBLK" stroke-width="2" />\n <line x1="8" y1="14" x2="8" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="24" y1="14" x2="24" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <path d="M8,14 Q16,6 24,14" fill="none" stroke="CHBLK" stroke-width="1.5" />\n</svg>\n',
8182
+ "BRIDGE02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="4" y1="14" x2="28" y2="14" stroke="CHBLK" stroke-width="2" />\n <line x1="8" y1="14" x2="8" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="24" y1="14" x2="24" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <path d="M8,14 Q16,6 24,14" fill="none" stroke="TRFCD" stroke-width="1.5" stroke-dasharray="3,2" />\n</svg>\n',
8183
+ "BRIDGE03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="4" y1="14" x2="28" y2="14" stroke="CHBLK" stroke-width="2" />\n <line x1="8" y1="14" x2="8" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="24" y1="14" x2="24" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <circle cx="16" cy="14" r="3" fill="none" stroke="TRFCD" stroke-width="1.5" />\n <circle cx="16" cy="14" r="1" fill="TRFCD" />\n</svg>\n',
8184
+ "BRIDGE04": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="4" y1="16" x2="28" y2="16" stroke="CHBLK" stroke-width="2" />\n <line x1="8" y1="16" x2="8" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="24" y1="16" x2="24" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="12" y1="16" x2="12" y2="6" stroke="TRFCD" stroke-width="1.5" />\n <polygon points="12,4 9,8 15,8" fill="TRFCD" />\n <line x1="20" y1="16" x2="20" y2="6" stroke="TRFCD" stroke-width="1.5" />\n <polygon points="20,4 17,8 23,8" fill="TRFCD" />\n</svg>\n',
8185
+ "BRIDGE05": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="4" y1="14" x2="28" y2="14" stroke="CHBLK" stroke-width="2" />\n <line x1="8" y1="14" x2="8" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="24" y1="14" x2="24" y2="24" stroke="CHBLK" stroke-width="1.8" />\n <line x1="14" y1="14" x2="20" y2="6" stroke="TRFCD" stroke-width="2" />\n <circle cx="14" cy="14" r="1.5" fill="TRFCD" />\n</svg>\n',
7138
8186
  "BRTHNO01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CHMGD" stroke-width="0.8" />\n</svg>\n',
7139
8187
  "BUAARE02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="LANDF" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="LANDF" />\n</svg>\n',
7140
8188
  "BUIREL01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 8.5 1.0 L 23.5 1.0 L 16.0 8.5 L 8.5 1.0 Z" fill="LANDF" />\n <path d="M 8.5 1.0 L 23.5 1.0 L 16.0 8.5 L 8.5 1.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 31.0 8.5 L 31.0 23.5 L 23.5 16.0 L 31.0 8.5 Z" fill="LANDF" />\n <path d="M 31.0 8.5 L 31.0 23.5 L 23.5 16.0 L 31.0 8.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.5 31.0 L 23.5 31.0 L 16.0 23.5 L 8.5 31.0 Z" fill="LANDF" />\n <path d="M 8.5 31.0 L 23.5 31.0 L 16.0 23.5 L 8.5 31.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 8.5 L 1.0 23.5 L 8.5 16.0 L 1.0 8.5 Z" fill="LANDF" />\n <path d="M 1.0 8.5 L 1.0 23.5 L 8.5 16.0 L 1.0 8.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.5 16.0 L 23.5 16.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 8.5 L 16.0 23.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.5 1.0 L 23.5 1.0 L 16.0 8.5 L 8.5 1.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 31.0 8.5 L 31.0 23.5 L 23.5 16.0 L 31.0 8.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 23.5 31.0 L 8.5 31.0 L 16.0 23.5 L 23.5 31.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 8.5 L 1.0 23.5 L 8.5 16.0 L 1.0 8.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7145,6 +8193,8 @@ var S52_POINT_SVGS = {
7145
8193
  "BUIREL15": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 4.8 1.0 L 6.5 3.2 L 8.4 4.9 L 10.6 6.0 L 12.8 6.9 L 15.2 7.2 L 17.1 7.1 L 19.1 6.9 L 21.3 6.2 L 23.0 5.1 L 24.5 4.2 L 25.8 2.8 L 26.7 1.7 L 27.2 1.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.48" cy="24.63" r="6.37" fill="none" stroke="CHBLK" stroke-width="0.8" />\n <path d="M 16.5 17.9 L 16.5 7.1" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.61" cy="24.56" r="1.29" fill="none" stroke="CHBLK" stroke-width="0.8" />\n <path d="M 16.6 24.6 Z" fill="CHBLK" />\n</svg>\n',
7146
8194
  "BUISGL01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0 Z" fill="CHBRN" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="CHBRN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="LANDF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7147
8195
  "BUISGL11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0 Z" fill="LANDF" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8196
+ "CABLE01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 10 2 L 5 8 L 9 8 L 6 14 L 11 6 L 7 6 Z" fill="CHMGD" stroke="CHMGD" stroke-width="0.4" stroke-linejoin="round" />\n</svg>\n',
8197
+ "CABOHD01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 3 5 L 8 11 L 13 5" fill="none" stroke="CHMGD" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7148
8198
  "CAIRNS01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 17.5 1.1 L 20.1 1.9 L 21.7 2.7 L 23.2 4.6 L 23.8 6.2 L 24.2 7.4 L 24.2 9.4 L 23.4 10.9 L 22.9 12.1 L 21.8 13.5 L 21.3 14.1 L 21.8 12.5 L 22.7 10.9 L 22.9 9.8 L 22.9 7.8 L 22.2 5.8 L 21.7 4.6 L 20.7 3.5 L 19.1 2.1 L 17.5 1.1 Z" fill="LANDF" />\n <path d="M 17.5 1.1 L 20.1 1.9 L 21.7 2.7 L 23.2 4.6 L 23.8 6.2 L 24.2 7.4 L 24.2 9.4 L 23.4 10.9 L 22.9 12.1 L 21.8 13.5 L 21.3 14.1 L 21.8 12.5 L 22.7 10.9 L 22.9 9.8 L 22.9 7.8 L 22.2 5.8 L 21.7 4.6 L 20.7 3.5 L 19.1 2.1 L 17.5 1.1" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 11.7 13.7 L 13.2 15.1 L 14.8 16.3 L 15.4 17.2 L 15.9 18.8 L 16.4 20.4 L 16.4 21.6 L 15.6 23.5 L 15.0 25.1 L 14.0 26.3 L 12.2 27.7 L 13.2 26.1 L 14.0 24.5 L 14.8 21.4 L 14.8 19.8 L 14.0 17.9 L 13.4 16.7 L 12.2 14.7 L 11.7 13.7 Z" fill="LANDF" />\n <path d="M 11.7 13.7 L 13.2 15.1 L 14.8 16.3 L 15.4 17.2 L 15.9 18.8 L 16.4 20.4 L 16.4 21.6 L 15.6 23.5 L 15.0 25.1 L 14.0 26.3 L 12.2 27.7 L 13.2 26.1 L 14.0 24.5 L 14.8 21.4 L 14.8 19.8 L 14.0 17.9 L 13.4 16.7 L 12.2 14.7 L 11.7 13.7" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 24.4 14.5 L 26.0 14.7 L 27.6 15.3 L 29.1 16.8 L 29.7 17.9 L 30.5 19.4 L 30.7 21.0 L 30.5 22.6 L 30.1 24.0 L 29.5 25.1 L 28.5 26.1 L 27.4 27.7 L 28.5 25.5 L 29.1 23.5 L 29.5 21.4 L 29.0 18.8 L 27.9 17.2 L 26.6 15.7 L 25.4 15.1 L 24.4 14.5 Z" fill="LANDF" />\n <path d="M 24.4 14.5 L 26.0 14.7 L 27.6 15.3 L 29.1 16.8 L 29.7 17.9 L 30.5 19.4 L 30.7 21.0 L 30.5 22.6 L 30.1 24.0 L 29.5 25.1 L 28.5 26.1 L 27.4 27.7 L 28.5 25.5 L 29.1 23.5 L 29.5 21.4 L 29.0 18.8 L 27.9 17.2 L 26.6 15.7 L 25.4 15.1 L 24.4 14.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.38" cy="28.67" r="2.33" fill="none" stroke="LANDF" stroke-width="1.2" />\n <circle cx="8.70" cy="21.00" r="7.42" fill="none" stroke="LANDF" stroke-width="1.2" />\n <circle cx="16.75" cy="8.23" r="7.23" fill="none" stroke="LANDF" stroke-width="1.2" />\n <circle cx="23.80" cy="21.38" r="6.86" fill="none" stroke="LANDF" stroke-width="1.2" />\n <path d="M 2.4 28.7 L 13.8 28.7" fill="none" stroke="LANDF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.7 28.7 L 29.7 28.7" fill="none" stroke="LANDF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7149
8199
  "CAIRNS11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 17.5 1.1 L 20.1 1.9 L 21.7 2.7 L 23.2 4.6 L 23.8 6.2 L 24.2 7.4 L 24.2 9.4 L 23.4 10.9 L 22.9 12.1 L 21.8 13.5 L 21.3 14.1 L 21.8 12.5 L 22.7 10.9 L 22.9 9.8 L 22.9 7.8 L 22.2 5.8 L 21.7 4.6 L 20.7 3.5 L 19.1 2.1 L 17.5 1.1 Z" fill="CHBLK" />\n <path d="M 17.5 1.1 L 20.1 1.9 L 21.7 2.7 L 23.2 4.6 L 23.8 6.2 L 24.2 7.4 L 24.2 9.4 L 23.4 10.9 L 22.9 12.1 L 21.8 13.5 L 21.3 14.1 L 21.8 12.5 L 22.7 10.9 L 22.9 9.8 L 22.9 7.8 L 22.2 5.8 L 21.7 4.6 L 20.7 3.5 L 19.1 2.1 L 17.5 1.1" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 11.7 13.7 L 13.2 15.1 L 14.8 16.3 L 15.4 17.2 L 15.9 18.8 L 16.4 20.4 L 16.4 21.6 L 15.6 23.5 L 15.0 25.1 L 14.0 26.3 L 12.2 27.7 L 13.2 26.1 L 14.0 24.5 L 14.8 21.4 L 14.8 19.8 L 14.0 17.9 L 13.4 16.7 L 12.2 14.7 L 11.7 13.7 Z" fill="CHBLK" />\n <path d="M 11.7 13.7 L 13.2 15.1 L 14.8 16.3 L 15.4 17.2 L 15.9 18.8 L 16.4 20.4 L 16.4 21.6 L 15.6 23.5 L 15.0 25.1 L 14.0 26.3 L 12.2 27.7 L 13.2 26.1 L 14.0 24.5 L 14.8 21.4 L 14.8 19.8 L 14.0 17.9 L 13.4 16.7 L 12.2 14.7 L 11.7 13.7" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 24.4 14.5 L 26.0 14.7 L 27.6 15.3 L 29.1 16.8 L 29.7 17.9 L 30.5 19.4 L 30.7 21.0 L 30.5 22.6 L 30.1 24.0 L 29.5 25.1 L 28.5 26.1 L 27.4 27.7 L 28.5 25.5 L 29.1 23.5 L 29.5 21.4 L 29.0 18.8 L 27.9 17.2 L 26.6 15.7 L 25.4 15.1 L 24.4 14.5 Z" fill="CHBLK" />\n <path d="M 24.4 14.5 L 26.0 14.7 L 27.6 15.3 L 29.1 16.8 L 29.7 17.9 L 30.5 19.4 L 30.7 21.0 L 30.5 22.6 L 30.1 24.0 L 29.5 25.1 L 28.5 26.1 L 27.4 27.7 L 28.5 25.5 L 29.1 23.5 L 29.5 21.4 L 29.0 18.8 L 27.9 17.2 L 26.6 15.7 L 25.4 15.1 L 24.4 14.5" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.38" cy="28.67" r="2.33" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="8.70" cy="21.00" r="7.42" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="16.75" cy="8.23" r="7.23" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <circle cx="23.80" cy="21.38" r="6.86" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 2.4 28.7 L 13.8 28.7" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.7 28.7 L 29.7 28.7" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7150
8200
  "CBLARE51": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 19.5 1.0 L 11.0 14.5 L 21.0 18.1 L 13.0 31.0" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7183,6 +8233,18 @@ var S52_POINT_SVGS = {
7183
8233
  "DIRBOYB1": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 9.0 20.9 L 2.2 20.9 L 16.0 7.1 L 29.7 20.9 L 22.7 20.9 L 22.7 25.5 L 9.0 25.5 L 9.0 20.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="4.48" cy="10.02" r="3.48" fill="none" stroke="CHGRN" stroke-width="1.8" />\n <circle cx="27.59" cy="10.02" r="3.41" fill="none" stroke="CHRED" stroke-width="1.8" />\n</svg>\n',
7184
8234
  "DISMAR03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CHMGD" stroke-width="1.2" />\n</svg>\n',
7185
8235
  "DISMAR04": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 5.1 L 1.0 26.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.4 13.3 L 1.0 21.5" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.5 18.7 L 9.2 26.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.6 13.3 L 14.6 26.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.6 15.5 L 16.5 13.5 L 18.2 13.0 L 19.3 13.0 L 21.6 14.1 L 22.8 15.7 L 22.8 26.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 22.8 16.0 L 23.9 13.8 L 26.1 12.8 L 27.5 12.8 L 29.7 13.8 L 31.0 16.0 L 31.0 26.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8236
+ "DNGCIR01": `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
8237
+ <!-- S-52 PresLib \xA7CSYM02 isolated-danger ring. Per IHO S-52 spec, hazards
8238
+ (UWTROC / OBSTRN / WRECKS) whose VALSOU is shallower than the
8239
+ mariner's safety contour, OR that cover-and-uncover with the tide
8240
+ (always dangerous regardless of sounded depth), get a thin DASHED
8241
+ MAGENTA RING overlaid on top of the base hazard icon.
8242
+ Thin 1.0-px stroke (no fill) at r=14 surrounds the 18-unit base
8243
+ hazard asterisks/crosses with ~3 px of breathing room at z16,
8244
+ reading as the standard NOAA / ECDIS isolated-danger marker. -->
8245
+ <circle cx="16" cy="16" r="14" fill="none" stroke="CHMGD" stroke-width="1.0" stroke-dasharray="2,2" />
8246
+ </svg>
8247
+ `,
7186
8248
  "DNGHILIT": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0 Z" fill="DNGHL" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="DNGHL" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 31.0 L 31.0 31.0 L 31.0 1.0 L 1.0 1.0 L 1.0 31.0" fill="none" stroke="DNGHL" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7187
8249
  "DOMES001": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="15.73" cy="25.99" r="2.56" fill="none" stroke="LANDF" stroke-width="0.8" />\n <path d="M 13.2 25.8 L 3.4 25.8" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.4 25.8 L 2.8 24.9 L 2.0 22.8 L 1.2 20.7 L 1.0 19.0 L 1.0 16.6 L 1.6 14.1 L 2.7 11.2 L 3.6 9.4 L 5.3 7.6 L 7.1 6.1 L 8.8 5.2 L 11.6 4.2 L 14.0 3.7 L 16.6 3.4 L 19.5 3.9 L 22.3 4.8 L 25.0 6.5 L 27.5 8.9 L 28.9 10.8 L 30.2 13.3 L 30.9 16.4 L 31.0 20.1 L 30.4 22.6 L 29.6 24.6 L 28.7 25.6" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.2 25.8 L 28.6 25.8 L 28.8 25.3" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7188
8250
  "DOMES011": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="15.73" cy="25.99" r="2.56" fill="none" stroke="CHBLK" stroke-width="0.8" />\n <path d="M 13.2 25.8 L 3.4 25.8" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.4 25.8 L 2.8 24.9 L 2.0 22.8 L 1.2 20.7 L 1.0 19.0 L 1.0 16.6 L 1.6 14.1 L 2.7 11.2 L 3.6 9.4 L 5.3 7.6 L 7.1 6.1 L 8.8 5.2 L 11.6 4.2 L 14.0 3.7 L 16.6 3.4 L 19.5 3.9 L 22.3 4.8 L 25.0 6.5 L 27.5 8.9 L 28.9 10.8 L 30.2 13.3 L 30.9 16.4 L 31.0 20.1 L 30.4 22.6 L 29.6 24.6 L 28.7 25.6" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.2 25.8 L 28.6 25.8 L 28.8 25.3" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7227,9 +8289,10 @@ var S52_POINT_SVGS = {
7227
8289
  "ISODGR01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.0 1.0 L 26.7 5.3 L 31.0 16.0 L 26.7 26.7 L 16.0 31.0 L 5.3 26.7 L 1.0 16.0 L 5.3 5.3 L 16.0 1.0 L 16.0 12.8 L 21.9 6.9 L 25.1 10.1 L 19.2 16.0 L 25.1 21.9 L 21.9 25.1 L 16.0 19.2 L 10.1 25.1 L 6.9 21.9 L 12.8 16.0 L 6.9 10.1 L 10.1 6.9 L 16.0 12.8 L 16.0 1.0 Z" fill="ISDNG" />\n <path d="M 16.0 1.0 L 26.7 5.3 L 31.0 16.0 L 26.7 26.7 L 16.0 31.0 L 5.3 26.7 L 1.0 16.0 L 5.3 5.3 L 16.0 1.0 L 16.0 12.8 L 21.9 6.9 L 25.1 10.1 L 19.2 16.0 L 25.1 21.9 L 21.9 25.1 L 16.0 19.2 L 10.1 25.1 L 6.9 21.9 L 12.8 16.0 L 6.9 10.1 L 10.1 6.9 L 16.0 12.8 L 16.0 1.0" fill="none" stroke="ISDNG" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 16.0 L 5.3 5.3 L 16.0 1.0 L 26.7 5.3 L 31.0 16.0 L 26.7 26.7 L 16.0 31.0 L 5.3 26.7 L 1.0 16.0" fill="none" stroke="ISDNG" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7228
8290
  "ITZARE51": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 4.7 27.8 L 4.7 4.2" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 23.1 4.3 L 23.1 27.8" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 4.3 L 8.7 4.3" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.3 27.7 L 8.7 27.7" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 15.1 4.3 L 31.0 4.3" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7229
8291
  "LIGHTDEF": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 31.0 L 10.9 6.9 L 10.9 5.8 L 11.2 4.2 L 12.0 2.8 L 12.8 2.1 L 14.1 1.0 L 16.0 1.0 L 17.6 1.0 L 18.7 1.3 L 20.3 2.3 L 20.8 3.9 L 21.1 5.3 L 21.1 6.6 L 16.3 31.0 Z" fill="CHMGD" />\n <path d="M 16.3 31.0 L 10.9 6.9 L 10.9 5.8 L 11.2 4.2 L 12.0 2.8 L 12.8 2.1 L 14.1 1.0 L 16.0 1.0 L 17.6 1.0 L 18.7 1.3 L 20.3 2.3 L 20.8 3.9 L 21.1 5.3 L 21.1 6.6 L 16.3 31.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 31.0 L 10.9 6.4 L 10.9 4.7 L 11.4 3.1 L 12.3 2.3 L 13.6 1.3 L 15.2 1.0 L 16.3 1.0 L 17.6 1.0 L 18.9 1.5 L 20.0 2.6 L 20.8 4.2 L 21.1 5.3 L 20.8 7.7 L 16.3 31.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7230
- "LIGHTS11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0 Z" fill="LITRD" />\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0" fill="none" stroke="LITRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 21.3 5.2 L 21.0 6.6 L 16.3 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 28.0 L 11.5 6.6 L 11.8 5.2 L 12.1 4.1 L 12.9 3.2 L 14.1 2.1 L 15.4 1.9 L 16.3 1.9 L 17.4 2.1 L 18.8 2.7 L 19.6 3.5 L 20.2 4.6 L 20.4 5.7 L 20.2 7.7 L 16.3 28.0" fill="none" stroke="LITRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 31.0 L 10.7 6.0 L 11.5 3.5 L 13.8 1.6 L 16.3 1.0 L 18.5 1.9 L 19.9 2.7 L 21.0 4.9" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8292
+ "LIGHTHOUSE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <rect x="6" y="26" width="20" height="4" fill="CHBLK" />\n <path d="M 9 26 L 12 8 L 20 8 L 23 26 Z" fill="CHBLK" />\n <rect x="13" y="4" width="6" height="4" fill="CHBLK" />\n <path d="M 12 4 L 16 1 L 20 4 Z" fill="CHBLK" />\n</svg>\n',
8293
+ "LIGHTS11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <g transform="translate(10 10) rotate(135) scale(0.9) translate(-16.3 -28)" fill="LITRD">\n <path d="M 16.3 28 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3 L 14.3 2.1 L 16 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28 Z" />\n </g>\n</svg>\n',
7231
8294
  "LIGHTS12": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0 Z" fill="LITGN" />\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0" fill="none" stroke="LITGN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 21.3 5.2 L 21.0 6.6 L 16.3 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 28.0 L 11.5 6.6 L 11.8 5.2 L 12.1 4.1 L 12.9 3.2 L 14.1 2.1 L 15.4 1.9 L 16.3 1.9 L 17.4 2.1 L 18.8 2.7 L 19.6 3.5 L 20.2 4.6 L 20.4 5.7 L 20.2 7.7 L 16.3 28.0" fill="none" stroke="LITGN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 31.0 L 10.7 6.0 L 11.5 3.5 L 13.8 1.6 L 16.3 1.0 L 18.5 1.9 L 19.9 2.7 L 21.0 4.9" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7232
- "LIGHTS13": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0 Z" fill="LITYW" />\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0" fill="none" stroke="LITYW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 21.3 5.2 L 21.0 6.6 L 16.3 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 28.0 L 11.5 6.6 L 11.8 5.2 L 12.1 4.1 L 12.9 3.2 L 14.1 2.1 L 15.4 1.9 L 16.3 1.9 L 17.4 2.1 L 18.8 2.7 L 19.6 3.5 L 20.2 4.6 L 20.4 5.7 L 20.2 7.7 L 16.3 28.0" fill="none" stroke="LITYW" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 31.0 L 10.7 6.0 L 11.5 3.5 L 13.8 1.6 L 16.3 1.0 L 18.5 1.9 L 19.9 2.7 L 21.0 4.9" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8295
+ "LIGHTS13": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <g transform="translate(10 10) rotate(135) scale(0.9) translate(-16.3 -28)" fill="LITYW">\n <path d="M 16.3 28 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3 L 14.3 2.1 L 16 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28 Z" />\n </g>\n</svg>\n',
7233
8296
  "LIGHTS14": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0 Z" fill="CHMGD" />\n <path d="M 16.3 28.0 L 11.8 7.1 L 11.8 6.3 L 11.8 4.9 L 12.4 3.5 L 13.2 3.0 L 14.3 2.1 L 16.0 1.9 L 17.4 2.1 L 18.5 2.4 L 19.6 3.2 L 20.2 4.6 L 20.4 5.7 L 20.4 6.9 L 16.3 28.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 21.3 5.2 L 21.0 6.6 L 16.3 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 28.0 L 11.5 6.6 L 11.8 5.2 L 12.1 4.1 L 12.9 3.2 L 14.1 2.1 L 15.4 1.9 L 16.3 1.9 L 17.4 2.1 L 18.8 2.7 L 19.6 3.5 L 20.2 4.6 L 20.4 5.7 L 20.2 7.7 L 16.3 28.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.3 31.0 L 10.7 6.0 L 11.5 3.5 L 13.8 1.6 L 16.3 1.0 L 18.5 1.9 L 19.9 2.7 L 21.0 4.9" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7234
8297
  "LIGHTS81": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 25.9 L 6.9 5.9 L 12.9 26.0 L 19.0 5.9 L 25.0 26.1 L 31.0 5.9" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7235
8298
  "LIGHTS82": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="6.96" cy="17.80" r="5.96" fill="none" stroke="CHMGD" stroke-width="1.2" />\n <path d="M 14.9 23.1 L 25.9 23.5" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.3 12.1 L 28.0 8.2" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 15.8 17.5 L 31.0 16.0" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7239,6 +8302,8 @@ var S52_POINT_SVGS = {
7239
8302
  "LITVES01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="24.83" r="2.33" fill="none" stroke="CHBLK" stroke-width="1.2" />\n <path d="M 1.0 24.8 L 12.7 24.8" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 6.0 24.8 L 2.7 16.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 26.0 24.8 L 29.3 16.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 19.0 L 16.0 9.8" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 9.3 19.0 L 9.3 13.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 22.7 19.0 L 22.7 13.2" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 9.8 L 16.0 4.8" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 6.5 L 19.3 9.8" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 9.8 L 19.3 6.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.7 21.5 L 10.2 21.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 21.8 21.5 L 27.3 21.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 2.7 16.5 L 9.2 19.0 L 16.0 19.0 L 22.7 19.0 L 29.3 16.5" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.3 24.8 L 31.0 24.8" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7240
8303
  "LITVES02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 5.9 23.3 L 1.0 15.1 L 31.0 15.1 L 25.6 23.3 L 5.9 23.3 Z" fill="CHBLK" />\n <path d="M 5.9 23.3 L 1.0 15.1 L 31.0 15.1 L 25.6 23.3 L 5.9 23.3" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 15.1 L 16.0 8.7" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7241
8304
  "LNDARE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="13.54" fill="none" stroke="LANDA" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="LANDA" />\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CSTLN" stroke-width="1.2" />\n</svg>\n',
8305
+ "LNDMRK18": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="16" y1="28" x2="16" y2="10" stroke="CHBLK" stroke-width="1.5"/>\n <circle cx="16" cy="10" r="2" fill="CHBLK"/>\n <line x1="16" y1="10" x2="6" y2="2" stroke="CHBLK" stroke-width="1.2"/>\n <line x1="16" y1="10" x2="26" y2="2" stroke="CHBLK" stroke-width="1.2"/>\n <line x1="16" y1="10" x2="6" y2="18" stroke="CHBLK" stroke-width="1.2"/>\n <line x1="16" y1="10" x2="26" y2="18" stroke="CHBLK" stroke-width="1.2"/>\n <circle cx="16" cy="16" r="7" fill="none" stroke="CHBLK" stroke-width="0.6" stroke-dasharray="2,2"/>\n</svg>\n',
8306
+ "LNDMRK19": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <line x1="16" y1="28" x2="16" y2="10" stroke="CHBLK" stroke-width="1.5"/>\n <circle cx="16" cy="10" r="1.5" fill="CHBLK"/>\n <line x1="16" y1="10" x2="16" y2="2" stroke="CHBLK" stroke-width="1.2"/>\n <line x1="16" y1="10" x2="9" y2="17" stroke="CHBLK" stroke-width="1.2"/>\n <line x1="16" y1="10" x2="23" y2="17" stroke="CHBLK" stroke-width="1.2"/>\n <circle cx="16" cy="16" r="7" fill="none" stroke="CHBLK" stroke-width="0.6" stroke-dasharray="2,2"/>\n</svg>\n',
7242
8307
  "LOCMAG01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 18.9 22.5 L 18.9 31.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.9 23.4 L 18.9 1.0 L 13.1 17.9 L 14.2 18.3 L 15.1 18.7 L 15.9 19.4 L 16.4 20.0 L 17.0 20.6 L 17.8 21.6 L 18.3 22.4 L 18.8 23.3" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7243
8308
  "LOCMAG51": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 18.9 23.6 L 18.9 1.0 L 13.0 18.1 L 14.1 18.5 L 15.0 18.9 L 15.8 19.6 L 16.4 20.2 L 17.0 20.8 L 17.7 21.8 L 18.3 22.6 L 18.8 23.5" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.0 23.5 L 19.0 31.0" fill="none" stroke="CHMGF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7244
8309
  "LOWACC01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 4.5 10.2 L 5.8 10.2" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 5.0 8.0 L 5.0 6.6 L 5.0 6.1 L 5.4 5.6 L 5.7 5.2 L 6.3 4.5 L 6.6 3.9 L 6.8 3.2 L 6.6 2.5 L 6.3 2.0 L 5.8 1.5 L 5.3 1.1 L 4.8 1.1 L 4.2 1.0 L 3.7 1.1 L 3.2 1.4 L 2.7 1.9 L 2.4 2.4 L 2.3 2.8 L 2.3 3.3 L 2.4 3.7 L 2.5 3.8" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 6.8 8.1 L 29.7 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7261,9 +8326,19 @@ var S52_POINT_SVGS = {
7261
8326
  "NMKRCD04": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 16.0 L 16.0 1.0 L 16.0 31.0 L 1.0 16.0 Z" fill="CHGRN" />\n <path d="M 1.0 16.0 L 16.0 1.0 L 16.0 31.0 L 1.0 16.0" fill="none" stroke="CHGRN" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 31.0 16.0 L 16.0 31.0 L 16.0 1.0 L 31.0 16.0 Z" fill="CHWHT" />\n <path d="M 31.0 16.0 L 16.0 31.0 L 16.0 1.0 L 31.0 16.0" fill="none" stroke="CHWHT" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 16.0 L 16.0 1.0 L 31.0 16.0 L 16.0 31.0 L 1.0 16.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7262
8327
  "NORTHAR1": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.3 31.0 L 16.3 1.0" fill="none" stroke="SCLBR" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.8 20.7 L 12.8 14.1 L 19.6 20.7 L 19.6 14.2" fill="none" stroke="SCLBR" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.4 8.9 L 19.6 8.9 L 16.2 1.1 L 12.4 8.9 Z" fill="SCLBR" />\n <path d="M 12.4 8.9 L 19.6 8.9 L 16.2 1.1 L 12.4 8.9" fill="none" stroke="SCLBR" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7263
8328
  "NOTBRD11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 8.5 31.0 L 23.5 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.0 31.0 L 16.0 16.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.8 16.0 L 27.3 16.0 L 27.3 1.0 L 4.8 1.0 L 4.8 16.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7264
- "OBSTRN01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="DEPVS" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="DEPVS" />\n</svg>\n',
8329
+ "OBSTRN01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <!-- S-52 PresLib OBSTRN01 / INT1 K40: obstruction, position-fixed (fish weir,\n snag, sunken object, foul ground point). NOAA portrayal: bold "+" cross\n inside a dashed oval. The dashed boundary (longer dashes than the dotted\n K41 variant) signals "area of obstruction" \u2014 mariners should give the\n feature a wide berth even when its precise extent is unclear. 18-unit\n cross with 1.6 stroke for harbor-zoom visibility. -->\n <ellipse cx="16" cy="16" rx="13" ry="10" fill="none" stroke="CHBLK" stroke-width="1.0" stroke-dasharray="1.8,1.4" />\n <path d="M 16 7 L 16 25" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 7 16 L 25 16" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n</svg>\n',
7265
8330
  "OBSTRN02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"></svg>\n',
7266
- "OBSTRN03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="DEPIT" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="DEPIT" />\n</svg>\n',
8331
+ "OBSTRN03": `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
8332
+ <!-- S-52 PresLib OBSTRN03 / INT1 K41: obstruction always submerged with
8333
+ sounded depth (WATLEV=3). NOAA portrayal: bold "+" cross inside a
8334
+ DOTTED oval (shorter, tighter dashes than OBSTRN01's dashed style).
8335
+ Depth rendered separately via VALSOU label. 18-unit cross matching
8336
+ UWTROC bold weight for visual hazard consistency. -->
8337
+ <ellipse cx="16" cy="16" rx="13" ry="10" fill="none" stroke="CHBLK" stroke-width="0.9" stroke-dasharray="1.0,1.2" />
8338
+ <path d="M 16 7 L 16 25" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />
8339
+ <path d="M 7 16 L 25 16" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />
8340
+ </svg>
8341
+ `,
7267
8342
  "OBSTRN18": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 1.0 L 1.0 31.0 L 31.0 31.0 L 30.8 1.0 L 1.0 1.0 Z" fill="LANDA" />\n <path d="M 1.0 1.0 L 1.0 31.0 L 31.0 31.0 L 30.8 1.0 L 1.0 1.0" fill="none" stroke="LANDA" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 1.0 L 1.0 31.0 L 31.0 31.0 L 30.8 1.0 L 1.0 1.0" fill="none" stroke="CSTLN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7268
8343
  "OFSPLF01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="3.75" fill="none" stroke="CHBLK" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="CHBLK" />\n <path d="M 1.0 1.0 L 31.0 1.0 L 31.0 31.0 L 1.0 31.0 L 1.0 1.0" fill="none" stroke="CHBLK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7269
8344
  "OSPONE02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 16.0 L 31.0 16.0" fill="none" stroke="SHIPS" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7274,6 +8349,8 @@ var S52_POINT_SVGS = {
7274
8349
  "PASTRK02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 16.0 L 31.0 16.0" fill="none" stroke="SYTRK" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7275
8350
  "PILBOP02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 16.0 1.0 L 10.2 16.2 L 16.0 31.0 L 22.1 16.2 L 16.0 1.0 Z" fill="CHMGD" />\n <path d="M 16.0 1.0 L 10.2 16.2 L 16.0 31.0 L 22.1 16.2 L 16.0 1.0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <circle cx="16.00" cy="16.17" r="14.77" fill="none" stroke="CHMGD" stroke-width="0.8" />\n</svg>\n',
7276
8351
  "PILPNT02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="CHBLK" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="CHBLK" />\n</svg>\n',
8352
+ "PIPLIN01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <circle cx="8" cy="8" r="3.5" fill="none" stroke="PIPLN" stroke-width="1.6" />\n</svg>\n',
8353
+ "PIPOHD01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 3 12 L 8 5 L 13 12" fill="none" stroke="PIPLN" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />\n <line x1="3" y1="13" x2="5" y2="13" stroke="PIPLN" stroke-width="1.4" stroke-linecap="round" />\n <line x1="11" y1="13" x2="13" y2="13" stroke="PIPLN" stroke-width="1.4" stroke-linecap="round" />\n</svg>\n',
7277
8354
  "PLNPOS01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 15.9 L 1.4 14.3 L 3.4 12.5 L 6.7 11.2 L 10.2 10.6 L 15.6 10.2 L 21.2 10.6 L 25.0 11.2 L 28.2 12.5 L 30.3 14.3 L 31.0 15.9 L 30.8 18.0 L 28.2 19.7 L 25.0 20.8 L 21.2 21.8 L 15.6 21.8 L 10.2 21.8 L 6.7 21.0 L 2.9 19.7 L 1.1 18.1 L 1.0 15.9" fill="none" stroke="PLRTE" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7278
8355
  "PLNPOS02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 16.0 L 31.0 16.0" fill="none" stroke="PLRTE" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7279
8356
  "PLNSPD03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 26.1 L 1.1 5.8 L 31.0 5.8 L 31.0 26.2 L 1.0 26.1" fill="none" stroke="PLRTE" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7387,12 +8464,14 @@ var S52_POINT_SVGS = {
7387
8464
  "TWRTPT53": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 13.3 4.5 L 16.0 1.0 L 18.6 4.5" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 11.3 7.0 L 9.6 9.5 L 12.8 9.5" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.9 9.4 L 22.4 9.4 L 20.4 6.8" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 26.9 L 12.7 31.0 L 19.2 31.0 L 19.2 27.0" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 23.7 L 12.7 20.3" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 12.7 13.0 L 12.7 16.2" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.6 12.8 L 19.6 15.9" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.5 20.1 L 19.5 23.4" fill="none" stroke="TRFCD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7388
8465
  "UNITFTH1": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 6.3 14.0 L 25.7 14.0" fill="none" stroke="SCLBR" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 25.8 1.0 L 6.3 1.0 L 6.2 31.0" fill="none" stroke="SCLBR" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7389
8466
  "UNITMTR1": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 3.1 31.0 L 3.1 1.0 L 16.1 25.2 L 28.9 1.4 L 28.9 30.6" fill="none" stroke="SCLBR" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7390
- "UWTROC03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="DEPVS" stroke-width="0.8" />\n <path d="M 16.0 16.0 Z" fill="DEPVS" />\n <path d="M 16.1 6.4 L 16.1 27.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 5.9 16.4 L 26.2 16.4" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7391
- "UWTROC04": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 31.0 16.1 L 1.0 16.1" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 24.8 3.9 L 7.1 28.2" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 24.8 27.7 L 6.8 3.8" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8467
+ "UWTROC01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <!-- S-52 PresLib UWTROC01 / INT1 K11: rock that covers and uncovers\n (WATLEV=4). Per IHO S-52 PresLib Edition 4.0 Annex A, the symbol is a\n plain 4-line asterisk \u2014 geometrically identical to UWTROC04 (K13).\n The K11/K13 distinction is conveyed by attribute context: K11 has no\n VALSOU (depth varies with tide so no fixed sounding), K13 carries a\n VALSOU label rendered separately. 18-unit asterisk, 1.6 stroke. -->\n <path d="M 16 7 L 16 25" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 7 16 L 25 16" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 9.5 9.5 L 22.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 22.5 9.5 L 9.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n</svg>\n',
8468
+ "UWTROC03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <!-- S-52 PresLib UWTROC03 / INT1 K12: rock awash at chart datum (WATLEV=5).\n NOAA portrayal: bold 4-line asterisk enclosed in a dotted oval \u2014 the\n oval signals "at chart datum, dangerous" and visually distinguishes\n awash rocks from sounded submerged rocks (UWTROC04). 18-unit asterisk\n inside a 28\xD722 dotted oval. -->\n <ellipse cx="16" cy="16" rx="14" ry="11" fill="none" stroke="CHBLK" stroke-width="1.0" stroke-dasharray="1.0,1.2" />\n <path d="M 16 7 L 16 25" fill="none" stroke="CHBLK" stroke-width="1.5" stroke-linecap="round" />\n <path d="M 7 16 L 25 16" fill="none" stroke="CHBLK" stroke-width="1.5" stroke-linecap="round" />\n <path d="M 9.5 9.5 L 22.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.5" stroke-linecap="round" />\n <path d="M 22.5 9.5 L 9.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.5" stroke-linecap="round" />\n</svg>\n',
8469
+ "UWTROC04": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <!-- S-52 PresLib UWTROC04 / INT1 K13: underwater rock always submerged with\n sounded depth (WATLEV=3, dangerous to surface navigation). NOAA portrayal:\n bold 4-line asterisk, depth value rendered separately by VALSOU label.\n 18-unit span with 1.6 stroke for clear visibility at harbor zoom \u2014 these\n are safety-critical hazards and need to read like NOAA paper-chart\n asterisks (~2 mm at 1:5000 \u2248 18 px on screen at z16). -->\n <path d="M 16 7 L 16 25" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 7 16 L 25 16" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 9.5 9.5 L 22.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n <path d="M 22.5 9.5 L 9.5 22.5" fill="none" stroke="CHBLK" stroke-width="1.6" stroke-linecap="round" />\n</svg>\n',
7392
8470
  "VECGND01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 7.5 26.4 L 15.9 15.8 L 24.6 26.6" fill="none" stroke="SHIPS" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 23.3 L 15.8 5.4 L 31.0 23.4" fill="none" stroke="SHIPS" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7393
8471
  "VECGND21": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 7.5 26.4 L 15.9 15.8 L 24.6 26.6" fill="none" stroke="ARPAT" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 23.3 L 15.8 5.4 L 31.0 23.4" fill="none" stroke="ARPAT" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7394
8472
  "VECWTR01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 24.9 L 15.8 7.0 L 31.0 25.0" fill="none" stroke="SHIPS" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7395
8473
  "VECWTR21": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 24.9 L 15.8 7.0 L 31.0 25.0" fill="none" stroke="ARPAT" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8474
+ "WATTUR01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 8 11 Q 12 9 16 11 T 24 11" fill="none" stroke="CHBLK" stroke-width="1.4" stroke-linecap="round" />\n <path d="M 8 16 Q 12 14 16 16 T 24 16" fill="none" stroke="CHBLK" stroke-width="1.4" stroke-linecap="round" />\n <path d="M 8 21 Q 12 19 16 21 T 24 21" fill="none" stroke="CHBLK" stroke-width="1.4" stroke-linecap="round" />\n</svg>\n',
7396
8475
  "WATTUR02": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 1.0 18.9 L 2.8 18.3 L 5.0 16.8 L 7.0 13.9 L 9.0 12.9 L 11.2 13.1 L 9.0 15.1 L 9.0 17.0 L 11.3 19.1 L 13.2 18.3 L 15.0 17.1 L 17.1 13.9 L 19.3 13.0 L 21.1 13.1 L 19.0 15.1 L 18.9 17.2 L 21.3 19.1 L 23.3 18.2 L 25.0 17.1 L 27.0 14.1 L 29.3 13.0 L 31.0 13.2 L 29.0 15.7 L 29.1 17.2 L 30.9 19.1" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7397
8476
  "WAYPNT01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="PLRTE" stroke-width="1.8" />\n</svg>\n',
7398
8477
  "WAYPNT03": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <circle cx="16.00" cy="16.00" r="15.00" fill="none" stroke="APLRT" stroke-width="1.2" />\n</svg>\n',
@@ -7410,6 +8489,7 @@ var S52_POINT_SVGS = {
7410
8489
  };
7411
8490
  var S52_PATTERN_SVGS = {
7412
8491
  "AIRARE02": '<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">\n <path d="M 8.9 25.1 L 18.8 25.1 L 15.9 21.8 L 15.9 17.6 L 27.0 17.6 L 16.8 10.7 L 16.8 5.6 L 14.3 2.9 L 11.3 5.6 L 11.3 10.7 L 1.0 17.3 L 11.5 17.4 L 11.5 22.0 L 8.9 25.1" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8492
+ "CBLARE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 0 32 L 32 0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-opacity="0.7" />\n <path d="M -16 32 L 16 0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-opacity="0.7" />\n <path d="M 16 32 L 48 0" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-opacity="0.7" />\n</svg>\n',
7413
8493
  "DIAMOND1": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 8.2 1.0 L 23.8 31.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.2 31.0 L 23.8 1.0" fill="none" stroke="CHBLK" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7414
8494
  "DQUALA11": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 22.4 9.2 L 27.7 9.2" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 26.7 7.1 L 23.5 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 23.5 7.1 L 26.7 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.1 9.2 L 18.5 9.2" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 17.4 7.1 L 14.2 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.2 7.1 L 17.4 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.8 9.3 L 9.1 9.3" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.1 7.2 L 4.9 11.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.9 7.2 L 8.1 11.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.8 15.5 L 14.1 15.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.0 13.4 L 9.8 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 9.9 13.4 L 13.0 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.1 15.4 L 23.4 15.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 22.4 13.3 L 19.2 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.2 13.3 L 22.4 17.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.7 21.4 L 19.0 21.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.0 19.3 L 14.8 23.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.8 19.3 L 18.0 23.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.5 9.0 L 1.0 7.6 L 1.2 6.6 L 1.5 5.9 L 2.4 5.6 L 2.9 5.5 L 28.1 5.5 L 29.6 5.7 L 30.5 6.1 L 30.8 6.6 L 31.0 7.1 L 31.0 7.5 L 31.0 8.2 L 30.7 8.7 L 18.5 24.9 L 18.0 25.4 L 17.6 26.0 L 17.1 26.3 L 16.8 26.3 L 16.4 26.5 L 15.8 26.4 L 15.5 26.3 L 14.8 26.1 L 14.4 25.8 L 14.1 25.4 L 13.7 25.0 L 1.5 9.0" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7415
8495
  "DQUALA21": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 22.4 9.2 L 27.7 9.2" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 26.7 7.1 L 23.5 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 23.5 7.1 L 26.7 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.1 9.2 L 18.5 9.2" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 17.4 7.1 L 14.2 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.2 7.1 L 17.4 11.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.8 9.3 L 9.1 9.3" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.1 7.2 L 4.9 11.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.9 7.2 L 8.1 11.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 8.8 15.5 L 14.1 15.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.0 13.4 L 9.8 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 9.9 13.4 L 13.0 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.1 15.4 L 23.4 15.4" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 22.4 13.3 L 19.2 17.6" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 19.2 13.3 L 22.4 17.5" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.5 9.0 L 1.0 7.6 L 1.2 6.6 L 1.5 5.9 L 2.4 5.6 L 2.9 5.5 L 28.1 5.5 L 29.6 5.7 L 30.5 6.1 L 30.8 6.6 L 31.0 7.1 L 31.0 7.5 L 31.0 8.2 L 30.7 8.7 L 18.5 24.9 L 18.0 25.4 L 17.6 26.0 L 17.1 26.3 L 16.8 26.3 L 16.4 26.5 L 15.8 26.4 L 15.5 26.3 L 14.8 26.1 L 14.4 25.8 L 14.1 25.4 L 13.7 25.0 L 1.5 9.0" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7427,6 +8507,7 @@ var S52_PATTERN_SVGS = {
7427
8507
  "MARSHES1": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 8.0 10.4 L 8.0 1.1" fill="none" stroke="CHBRN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 3.7 14.9 L 12.4 14.9" fill="none" stroke="CHBRN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 1.0 12.0 L 15.0 12.0" fill="none" stroke="CHBRN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 5.0 11.6 L 2.5 5.8" fill="none" stroke="CHBRN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 10.8 11.6 L 13.3 6.0" fill="none" stroke="CHBRN" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7428
8508
  "NODATA03": '<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">\n <path d="M 1.0 14.1 L 27.0 13.9" fill="none" stroke="CHGRD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7429
8509
  "OVERSC01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 8.0 1.0 L 8.0 15.0" fill="none" stroke="CHGRD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
8510
+ "PIPARE01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 0 32 L 32 0" fill="none" stroke="PIPLN" stroke-width="0.8" stroke-opacity="0.7" />\n <path d="M -16 32 L 16 0" fill="none" stroke="PIPLN" stroke-width="0.8" stroke-opacity="0.7" />\n <path d="M 16 32 L 48 0" fill="none" stroke="PIPLN" stroke-width="0.8" stroke-opacity="0.7" />\n</svg>\n',
7430
8511
  "PRTSUR01": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">\n <path d="M 1.0 8.0 L 15.0 8.0" fill="none" stroke="CHGRD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7431
8512
  "QUESMRK1": '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">\n <path d="M 5.5 4.9 L 6.1 3.8 L 6.6 2.7 L 7.3 2.1 L 8.0 1.4 L 9.0 1.0 L 10.0 1.1 L 11.2 1.4 L 12.3 1.7 L 13.3 3.1 L 14.0 3.8 L 14.5 4.7 L 14.5 5.8 L 14.0 6.7 L 13.5 7.6 L 12.6 8.6 L 11.7 9.6 L 10.9 10.6 L 10.3 11.4 L 9.9 12.1 L 9.8 13.0 L 9.8 13.9 L 9.8 14.6 L 9.9 15.5 L 9.9 15.6" fill="none" stroke="CHMGD" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 9.0 19.0 L 11.2 19.0" fill="none" stroke="CHMGD" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
7432
8513
  "RCKLDG01": '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">\n <path d="M 5.6 29.0 L 9.8 25.7" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 4.2 7.2 L 6.2 13.2" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 16.1 14.7 L 13.0 19.2" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.2 7.8 L 14.7 3.1" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 17.9 22.2 L 18.9 27.8" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 28.8 21.5 L 31.0 16.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 29.1 9.8 L 24.2 8.2" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 6.2 13.1 L 1.0 10.4" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 13.2 19.1 L 12.7 13.9" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 5.5 29.0 L 6.0 24.0" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 18.0 22.2 L 22.1 25.5" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 30.9 16.5 L 25.7 18.7" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 24.3 8.3 L 28.3 5.6" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M 14.7 3.0 L 16.8 7.6" fill="none" stroke="LANDF" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n',
@@ -7577,7 +8658,20 @@ var FILL_OUTLINE_LAYERS = /* @__PURE__ */ new Set([
7577
8658
  "FAIRWY",
7578
8659
  "TESARE",
7579
8660
  "MIPARE",
7580
- "PRCARE"
8661
+ "PRCARE",
8662
+ "PYLONS"
8663
+ ]);
8664
+ var REGULATED_AREA_BOUNDARY_LAYERS = {
8665
+ CBLARE: "CHMGD",
8666
+ PIPARE: "PIPLN"
8667
+ };
8668
+ var LINE_FROM_POLYGON_LAYERS = /* @__PURE__ */ new Set([
8669
+ "CTNARE",
8670
+ "RESARE",
8671
+ "MIPARE",
8672
+ "TESARE",
8673
+ "MAGVAR",
8674
+ "PRDARE"
7581
8675
  ]);
7582
8676
  var ZOOM_SCALED_LINES2 = {
7583
8677
  DEPCNT: [8, 0.3, 14, 0.8],
@@ -7587,8 +8681,66 @@ var ZOOM_SCALED_LINES2 = {
7587
8681
  BRIDGE: [8, 1, 14, 2],
7588
8682
  DAMCON: [8, 1, 14, 2]
7589
8683
  };
7590
- var OPAQUE_FILLS = /* @__PURE__ */ new Set(["DEPARE", "LNDARE", "UNSARE", "LAKARE", "DRGARE"]);
8684
+ var OPAQUE_FILLS = /* @__PURE__ */ new Set(["DEPARE", "LNDARE", "UNSARE", "LAKARE", "DRGARE", "SEAARE"]);
7591
8685
  var SAFETY_GROUPS = /* @__PURE__ */ new Set([24020, 27010, 27070, 27080]);
8686
+ var HAZARD_GROUPS = /* @__PURE__ */ new Set([24020]);
8687
+ var CHILD_ICON_OFFSETS = {
8688
+ FOGSIG: [-22, 18],
8689
+ // lower-left quadrant of the buoy symbol
8690
+ RTPBCN: [22, 18],
8691
+ // lower-right quadrant
8692
+ TOPMAR: [0, -24],
8693
+ // directly above the buoy symbol
8694
+ DAYMAR: [0, -20],
8695
+ // directly above (like TOPMAR)
8696
+ LIGHTS: [14, 14]
8697
+ // lower-right, off the buoy hull so the flare is visible
8698
+ };
8699
+ var SAFETY_CRITICAL_LABELS = /* @__PURE__ */ new Set([
8700
+ "LIGHTS",
8701
+ "SOUNDG",
8702
+ "BOYLAT",
8703
+ "BCNLAT",
8704
+ "BOYCAR",
8705
+ "BCNCAR",
8706
+ "BOYSPP",
8707
+ "BCNSPP",
8708
+ "BOYISD",
8709
+ "BOYSAW",
8710
+ "FOGSIG",
8711
+ "RTPBCN"
8712
+ ]);
8713
+ var HIGH_DENSITY_NAV_AIDS = /* @__PURE__ */ new Set([
8714
+ "BOYLAT",
8715
+ "BOYCAR",
8716
+ "BOYSPP",
8717
+ "BOYISD",
8718
+ "BOYSAW",
8719
+ "BOYINB",
8720
+ "BCNLAT",
8721
+ "BCNCAR",
8722
+ "BCNSPP",
8723
+ "BCNISD",
8724
+ "BCNSAW"
8725
+ ]);
8726
+ function buildNavAidLabelExpression() {
8727
+ const colourPrefix = colourCodeCase(
8728
+ [
8729
+ ["3", "R "],
8730
+ ["4", "G "],
8731
+ ["6", "Y "]
8732
+ ],
8733
+ ""
8734
+ );
8735
+ return [
8736
+ "case",
8737
+ ["has", "_navAidLabel"],
8738
+ ["get", "_navAidLabel"],
8739
+ ["has", "OBJNAM"],
8740
+ ["concat", colourPrefix, ["get", "OBJNAM"]],
8741
+ ""
8742
+ ];
8743
+ }
7592
8744
  function buildLightLabelExpression() {
7593
8745
  const litchrExpr = [
7594
8746
  "match",
@@ -7617,21 +8769,26 @@ function buildLightLabelExpression() {
7617
8769
  "UQ",
7618
8770
  ""
7619
8771
  ];
7620
- const colourExpr = [
7621
- "match",
7622
- ["coalesce", ["get", "COLOUR"], 0],
7623
- 1,
7624
- " W",
7625
- 3,
7626
- " R",
7627
- 4,
7628
- " G",
7629
- 5,
7630
- " Bu",
7631
- 6,
7632
- " Y",
7633
- 11,
7634
- " Or",
8772
+ const colourExpr = colourCodeCase(
8773
+ [
8774
+ ["3", " R"],
8775
+ ["4", " G"],
8776
+ ["6", " Y"],
8777
+ ["11", " Or"],
8778
+ ["5", " Bu"],
8779
+ ["1", " W"]
8780
+ ],
8781
+ ""
8782
+ );
8783
+ const siggrpExpr = [
8784
+ "case",
8785
+ [
8786
+ "all",
8787
+ ["has", "SIGGRP"],
8788
+ ["!=", ["coalesce", ["get", "SIGGRP"], ""], ""],
8789
+ ["!=", ["coalesce", ["get", "SIGGRP"], "(1)"], "(1)"]
8790
+ ],
8791
+ ["to-string", ["get", "SIGGRP"]],
7635
8792
  ""
7636
8793
  ];
7637
8794
  const sigperExpr = [
@@ -7640,7 +8797,7 @@ function buildLightLabelExpression() {
7640
8797
  ["concat", " ", ["to-string", ["get", "SIGPER"]], "s"],
7641
8798
  ""
7642
8799
  ];
7643
- return ["concat", litchrExpr, colourExpr, sigperExpr];
8800
+ return ["concat", litchrExpr, siggrpExpr, colourExpr, sigperExpr];
7644
8801
  }
7645
8802
  function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
7646
8803
  if (!S52_LOOKUP[objectClass]) return [];
@@ -7662,7 +8819,9 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
7662
8819
  "s52:category": entry?.category ?? "Meta"
7663
8820
  }
7664
8821
  };
7665
- if (GEOM_FILTERS[resolved.type]) {
8822
+ if (resolved.type === "line" && LINE_FROM_POLYGON_LAYERS.has(objectClass)) {
8823
+ layer.filter = GEOM_FILTERS.fill;
8824
+ } else if (GEOM_FILTERS[resolved.type]) {
7666
8825
  layer.filter = GEOM_FILTERS[resolved.type];
7667
8826
  }
7668
8827
  if (resolved.minzoom) {
@@ -7677,17 +8836,180 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
7677
8836
  }
7678
8837
  if (resolved.type === "symbol") {
7679
8838
  const iconExpr = getIconExpression(objectClass);
8839
+ const childOffset = CHILD_ICON_OFFSETS[objectClass];
8840
+ const isSafetyCritical = SAFETY_GROUPS.has(entry?.viewingGroup ?? 0);
8841
+ const safetyCriticalIconSize = [
8842
+ "interpolate",
8843
+ ["linear"],
8844
+ ["zoom"],
8845
+ 6,
8846
+ 0.4,
8847
+ 12,
8848
+ 0.45,
8849
+ 14,
8850
+ 0.6,
8851
+ 18,
8852
+ 1.1
8853
+ ];
8854
+ const defaultIconSize = [
8855
+ "interpolate",
8856
+ ["linear"],
8857
+ ["zoom"],
8858
+ 6,
8859
+ 0.32,
8860
+ 13,
8861
+ 0.32,
8862
+ 14,
8863
+ 0.36,
8864
+ 18,
8865
+ 1.04
8866
+ ];
7680
8867
  layer.layout = {
7681
8868
  "icon-image": iconExpr,
7682
- "icon-size": ["interpolate", ["linear"], ["zoom"], 8, 0.4, 14, 0.8],
7683
- "icon-allow-overlap": SAFETY_GROUPS.has(entry?.viewingGroup ?? 0),
7684
- "icon-optional": !SAFETY_GROUPS.has(entry?.viewingGroup ?? 0),
7685
- ...resolved.layout ?? {}
8869
+ "icon-size": defaultIconSize,
8870
+ "icon-allow-overlap": isSafetyCritical,
8871
+ "icon-optional": !isSafetyCritical,
8872
+ ...childOffset ? { "icon-offset": childOffset } : {},
8873
+ ...resolved.layout ?? {},
8874
+ // Safety-critical icon-size MUST be applied AFTER the resolved.layout
8875
+ // spread. Otherwise the static "icon-size" baked into safety-critical
8876
+ // lookup entries (LIGHTS 0.7, BOYLAT 0.7, BCNLAT 0.6, BCNSPP 0.6,
8877
+ // FOGSIG 0.6, RTPBCN 0.6) suppresses the coastal-scale curve and the
8878
+ // icons stay invisibly small at z9–z11 next to their large labels.
8879
+ // Non-safety classes keep the static-or-default cascade from above.
8880
+ ...isSafetyCritical ? { "icon-size": safetyCriticalIconSize } : {}
7686
8881
  };
8882
+ if (HAZARD_GROUPS.has(entry?.viewingGroup ?? 0)) {
8883
+ const watlev = ["to-number", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 0];
8884
+ const valsou = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 99], 99];
8885
+ const hazardIconSize = [
8886
+ "interpolate",
8887
+ ["linear"],
8888
+ ["zoom"],
8889
+ 10,
8890
+ 0.3,
8891
+ 12,
8892
+ 0.5,
8893
+ 13,
8894
+ 0.65,
8895
+ 14,
8896
+ 0.9,
8897
+ 16,
8898
+ 1.2,
8899
+ 18,
8900
+ 1.5
8901
+ ];
8902
+ layer.layout = {
8903
+ ...layer.layout,
8904
+ "icon-size": hazardIconSize,
8905
+ "icon-allow-overlap": false,
8906
+ "icon-optional": true,
8907
+ // Pad each hazard's collision box so adjacent rocks don't stack
8908
+ // visually. At z12–13 a 2 px pad roughly doubles the dedupe
8909
+ // radius relative to the rendered icon, which is the difference
8910
+ // between a black mass and individually-legible hazards.
8911
+ "icon-padding": 2,
8912
+ "symbol-sort-key": [
8913
+ "case",
8914
+ ["==", watlev, 4],
8915
+ 1,
8916
+ ["==", watlev, 5],
8917
+ 2,
8918
+ // Submerged rocks: sounded depth determines priority. Shallower
8919
+ // = smaller sort-key = wins collisions. Offset by 10 so any
8920
+ // submerged rock still loses to a covers-and-uncovers / awash
8921
+ // hazard at the same point, but ranks dangerously-shallow
8922
+ // submerged rocks above deeper ones.
8923
+ ["+", 10, valsou]
8924
+ ]
8925
+ };
8926
+ }
8927
+ if (objectClass === "LIGHTS") {
8928
+ layer.paint["icon-color"] = lightFillExpression(ct);
8929
+ layer.paint["icon-halo-color"] = resolveColor("CHBLK", ct);
8930
+ layer.paint["icon-halo-width"] = 1;
8931
+ }
7687
8932
  } else if (resolved.layout) {
7688
8933
  layer.layout = resolved.layout;
7689
8934
  }
8935
+ if (objectClass === "LNDMRK" && resolved.type === "symbol") {
8936
+ const baseFilterParts = layer.filter && layer.filter[0] === "all" ? layer.filter.slice(1) : layer.filter ? [layer.filter] : [];
8937
+ const catlmk = ["coalesce", ["get", "CATLMK"], ["get", "catlmk"], 0];
8938
+ const isWind = ["any", ["==", catlmk, 18], ["==", catlmk, 19]];
8939
+ const isLighthouse = ["==", catlmk, 17];
8940
+ const scaminFloor = scaminToMinZoom(entry?.scamin);
8941
+ const windLayer = {
8942
+ ...layer,
8943
+ id: `s52-${objectClass}-wind-symbol`,
8944
+ filter: ["all", ...baseFilterParts, isWind]
8945
+ };
8946
+ delete windLayer.minzoom;
8947
+ const lighthouseLayer = {
8948
+ ...layer,
8949
+ id: `s52-${objectClass}-lighthouse-symbol`,
8950
+ filter: ["all", ...baseFilterParts, isLighthouse],
8951
+ layout: {
8952
+ ...layer.layout ?? {},
8953
+ "icon-image": "LIGHTHOUSE01"
8954
+ }
8955
+ };
8956
+ delete lighthouseLayer.minzoom;
8957
+ const otherLayer = {
8958
+ ...layer,
8959
+ id: `s52-${objectClass}-other-symbol`,
8960
+ filter: ["all", ...baseFilterParts, ["!", isWind], ["!", isLighthouse]]
8961
+ };
8962
+ if (scaminFloor !== void 0) {
8963
+ otherLayer.minzoom = scaminFloor;
8964
+ }
8965
+ layers.push(windLayer, lighthouseLayer, otherLayer);
8966
+ continue;
8967
+ }
7690
8968
  layers.push(layer);
8969
+ if (HAZARD_GROUPS.has(entry?.viewingGroup ?? 0) && resolved.type === "symbol") {
8970
+ const HAZARD_SAFETY_CONTOUR_M = 9;
8971
+ const valsouNum = ["to-number", ["coalesce", ["get", "VALSOU"], ["get", "valsou"], 999], 999];
8972
+ const watlevNum = ["to-number", ["coalesce", ["get", "WATLEV"], ["get", "watlev"], 0], 0];
8973
+ layers.push({
8974
+ id: `s52-${objectClass}-danger-ring`,
8975
+ type: "symbol",
8976
+ source: sourceId,
8977
+ "source-layer": objectClass,
8978
+ filter: [
8979
+ "all",
8980
+ GEOM_FILTERS.symbol,
8981
+ [
8982
+ "any",
8983
+ ["<", valsouNum, HAZARD_SAFETY_CONTOUR_M],
8984
+ ["==", watlevNum, 4]
8985
+ ]
8986
+ ],
8987
+ layout: {
8988
+ "icon-image": "DNGCIR01",
8989
+ "icon-size": [
8990
+ "interpolate",
8991
+ ["linear"],
8992
+ ["zoom"],
8993
+ 12.99,
8994
+ 0,
8995
+ 13,
8996
+ 0.55,
8997
+ 14,
8998
+ 0.85,
8999
+ 16,
9000
+ 1.15,
9001
+ 18,
9002
+ 1.45
9003
+ ],
9004
+ "icon-allow-overlap": true,
9005
+ "icon-ignore-placement": true,
9006
+ "icon-optional": true
9007
+ },
9008
+ paint: {},
9009
+ minzoom: 13,
9010
+ metadata: { "s52:objectClass": objectClass, "s52:role": "isolated-danger-ring" }
9011
+ });
9012
+ }
7691
9013
  if (resolved.type === "fill" && FILL_OUTLINE_LAYERS.has(objectClass)) {
7692
9014
  const outlineColor = objectClass === "DEPARE" ? resolveColor("CHGRD", ct) : resolveColor("CHGRF", ct);
7693
9015
  layers.push({
@@ -7704,12 +9026,650 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
7704
9026
  metadata: { "s52:objectClass": objectClass, "s52:role": "outline" }
7705
9027
  });
7706
9028
  }
9029
+ if (resolved.type === "fill" && REGULATED_AREA_BOUNDARY_LAYERS[objectClass]) {
9030
+ const boundaryColor = resolveColor(REGULATED_AREA_BOUNDARY_LAYERS[objectClass], ct);
9031
+ layers.push({
9032
+ id: `s52-${objectClass}-boundary`,
9033
+ type: "line",
9034
+ source: sourceId,
9035
+ "source-layer": objectClass,
9036
+ filter: GEOM_FILTERS.fill,
9037
+ paint: {
9038
+ "line-color": boundaryColor,
9039
+ "line-width": 1,
9040
+ "line-dasharray": [4, 2],
9041
+ "line-opacity": 0.85
9042
+ },
9043
+ metadata: { "s52:objectClass": objectClass, "s52:role": "boundary" }
9044
+ });
9045
+ }
9046
+ if (objectClass === "ACHARE" && resolved.type === "fill") {
9047
+ layers.push({
9048
+ id: "s52-ACHARE-boundary",
9049
+ type: "line",
9050
+ source: sourceId,
9051
+ "source-layer": "ACHARE",
9052
+ filter: GEOM_FILTERS.fill,
9053
+ paint: {
9054
+ "line-color": resolveColor("CHMGD", ct),
9055
+ "line-width": 0.8,
9056
+ "line-dasharray": [3, 2],
9057
+ "line-opacity": 0.7
9058
+ },
9059
+ metadata: { "s52:objectClass": "ACHARE", "s52:role": "boundary" }
9060
+ });
9061
+ }
9062
+ if (objectClass === "ACHARE" && resolved.type === "fill") {
9063
+ layers.push({
9064
+ id: "s52-ACHARE-anchor-icon",
9065
+ type: "symbol",
9066
+ source: sourceId,
9067
+ "source-layer": "ACHARE",
9068
+ filter: GEOM_FILTERS.fill,
9069
+ layout: {
9070
+ "icon-image": "ACHARE02",
9071
+ "icon-size": ["interpolate", ["linear"], ["zoom"], 10, 0.5, 14, 0.8, 17, 1.1],
9072
+ "icon-allow-overlap": true,
9073
+ "icon-ignore-placement": false,
9074
+ "symbol-placement": "point",
9075
+ "text-field": ["case", ["has", "OBJNAM"], ["get", "OBJNAM"], ""],
9076
+ "text-size": ["interpolate", ["linear"], ["zoom"], 11, 9, 16, 11],
9077
+ "text-anchor": "top",
9078
+ "text-offset": [0, 1.4],
9079
+ "text-optional": true,
9080
+ "text-font": ["Open Sans Italic"],
9081
+ "text-allow-overlap": false
9082
+ },
9083
+ paint: {
9084
+ "text-color": resolveColor("CHMGD", ct),
9085
+ "text-halo-color": resolveColor("CHWHT", ct),
9086
+ "text-halo-width": 1
9087
+ },
9088
+ metadata: { "s52:objectClass": "ACHARE", "s52:role": "centroid-icon" }
9089
+ });
9090
+ }
9091
+ if (objectClass === "LNDARE" && resolved.type === "fill") {
9092
+ layers.push({
9093
+ id: `s52-LNDARE-point`,
9094
+ type: "circle",
9095
+ source: sourceId,
9096
+ "source-layer": "LNDARE",
9097
+ filter: GEOM_FILTERS.circle,
9098
+ paint: {
9099
+ "circle-color": resolveColor("LANDA", ct),
9100
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.8, 14, 2.4, 17, 3.6],
9101
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9102
+ "circle-stroke-width": 0.8
9103
+ },
9104
+ metadata: { "s52:objectClass": "LNDARE", "s52:role": "point" }
9105
+ });
9106
+ }
9107
+ if (objectClass === "LNDARE" && resolved.type === "fill") {
9108
+ layers.push({
9109
+ id: `s52-LNDARE-line`,
9110
+ type: "line",
9111
+ source: sourceId,
9112
+ "source-layer": "LNDARE",
9113
+ filter: GEOM_FILTERS.line,
9114
+ paint: {
9115
+ "line-color": resolveColor("CSTLN", ct),
9116
+ "line-width": ["interpolate", ["linear"], ["zoom"], 8, 0.6, 14, 1.4]
9117
+ },
9118
+ metadata: { "s52:objectClass": "LNDARE", "s52:role": "line" }
9119
+ });
9120
+ }
9121
+ if (objectClass === "BUAARE" && resolved.type === "fill") {
9122
+ layers.push({
9123
+ id: `s52-BUAARE-point`,
9124
+ type: "circle",
9125
+ source: sourceId,
9126
+ "source-layer": "BUAARE",
9127
+ filter: GEOM_FILTERS.circle,
9128
+ paint: {
9129
+ "circle-color": resolveColor("CHGRF", ct),
9130
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 8, 1.5, 12, 2.5, 16, 4],
9131
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9132
+ "circle-stroke-width": 0.6
9133
+ },
9134
+ metadata: { "s52:objectClass": "BUAARE", "s52:role": "point" }
9135
+ });
9136
+ }
9137
+ if (objectClass === "RIVERS" && resolved.type === "fill") {
9138
+ layers.push({
9139
+ id: `s52-RIVERS-line`,
9140
+ type: "line",
9141
+ source: sourceId,
9142
+ "source-layer": "RIVERS",
9143
+ filter: GEOM_FILTERS.line,
9144
+ paint: {
9145
+ "line-color": resolveColor("DEPMD", ct),
9146
+ "line-width": ["interpolate", ["linear"], ["zoom"], 8, 0.5, 14, 1.2]
9147
+ },
9148
+ metadata: { "s52:objectClass": "RIVERS", "s52:role": "line" }
9149
+ });
9150
+ }
9151
+ if (objectClass === "BUISGL" && resolved.type === "circle") {
9152
+ layers.push({
9153
+ id: `s52-BUISGL-polygon`,
9154
+ type: "fill",
9155
+ source: sourceId,
9156
+ "source-layer": "BUISGL",
9157
+ filter: GEOM_FILTERS.fill,
9158
+ paint: {
9159
+ "fill-color": resolveColor("CHBRN", ct),
9160
+ "fill-opacity": 0.55
9161
+ },
9162
+ metadata: { "s52:objectClass": "BUISGL", "s52:role": "polygon" }
9163
+ });
9164
+ layers.push({
9165
+ id: `s52-BUISGL-polygon-outline`,
9166
+ type: "line",
9167
+ source: sourceId,
9168
+ "source-layer": "BUISGL",
9169
+ filter: GEOM_FILTERS.fill,
9170
+ paint: {
9171
+ "line-color": resolveColor("CHBLK", ct),
9172
+ "line-width": 0.4
9173
+ },
9174
+ metadata: { "s52:objectClass": "BUISGL", "s52:role": "polygon-outline" }
9175
+ });
9176
+ }
9177
+ if (objectClass === "BRIDGE" && resolved.type === "line") {
9178
+ layers.push({
9179
+ id: `s52-BRIDGE-polygon`,
9180
+ type: "fill",
9181
+ source: sourceId,
9182
+ "source-layer": "BRIDGE",
9183
+ filter: GEOM_FILTERS.fill,
9184
+ paint: {
9185
+ "fill-color": resolveColor("CHBRN", ct),
9186
+ "fill-opacity": 0.35
9187
+ },
9188
+ metadata: { "s52:objectClass": "BRIDGE", "s52:role": "polygon" }
9189
+ });
9190
+ layers.push({
9191
+ id: `s52-BRIDGE-polygon-outline`,
9192
+ type: "line",
9193
+ source: sourceId,
9194
+ "source-layer": "BRIDGE",
9195
+ filter: GEOM_FILTERS.fill,
9196
+ paint: {
9197
+ "line-color": resolveColor("CHBLK", ct),
9198
+ "line-width": 1
9199
+ },
9200
+ metadata: { "s52:objectClass": "BRIDGE", "s52:role": "polygon-outline" }
9201
+ });
9202
+ }
9203
+ if (objectClass === "PYLONS" && resolved.type === "fill") {
9204
+ layers.push({
9205
+ id: `s52-PYLONS-point`,
9206
+ type: "circle",
9207
+ source: sourceId,
9208
+ "source-layer": "PYLONS",
9209
+ filter: GEOM_FILTERS.circle,
9210
+ paint: {
9211
+ "circle-color": resolveColor("CHBRN", ct),
9212
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.8, 14, 2.6, 17, 3.8],
9213
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9214
+ "circle-stroke-width": 0.8
9215
+ },
9216
+ metadata: { "s52:objectClass": "PYLONS", "s52:role": "point" }
9217
+ });
9218
+ }
9219
+ if (objectClass === "WEDKLP" && resolved.type === "fill") {
9220
+ layers.push({
9221
+ id: `s52-WEDKLP-point`,
9222
+ type: "circle",
9223
+ source: sourceId,
9224
+ "source-layer": "WEDKLP",
9225
+ filter: GEOM_FILTERS.circle,
9226
+ paint: {
9227
+ "circle-color": resolveColor("CHGRN", ct),
9228
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 2.5, 17, 4],
9229
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9230
+ "circle-stroke-width": 0.5,
9231
+ "circle-opacity": 0.7
9232
+ },
9233
+ metadata: { "s52:objectClass": "WEDKLP", "s52:role": "point" }
9234
+ });
9235
+ }
9236
+ if (objectClass === "SLOGRD" && resolved.type === "fill") {
9237
+ layers.push({
9238
+ id: `s52-SLOGRD-point`,
9239
+ type: "circle",
9240
+ source: sourceId,
9241
+ "source-layer": "SLOGRD",
9242
+ filter: GEOM_FILTERS.circle,
9243
+ paint: {
9244
+ "circle-color": resolveColor("CHBRN", ct),
9245
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.4, 14, 2, 17, 3],
9246
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9247
+ "circle-stroke-width": 0.4,
9248
+ "circle-opacity": 0.7
9249
+ },
9250
+ metadata: { "s52:objectClass": "SLOGRD", "s52:role": "point" }
9251
+ });
9252
+ }
9253
+ if (objectClass === "RUNWAY" && resolved.type === "fill") {
9254
+ layers.push({
9255
+ id: `s52-RUNWAY-line`,
9256
+ type: "line",
9257
+ source: sourceId,
9258
+ "source-layer": "RUNWAY",
9259
+ filter: GEOM_FILTERS.line,
9260
+ paint: {
9261
+ "line-color": resolveColor("CHGRF", ct),
9262
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.6, 14, 1.2, 17, 2]
9263
+ },
9264
+ metadata: { "s52:objectClass": "RUNWAY", "s52:role": "line" }
9265
+ });
9266
+ }
9267
+ if (objectClass === "FORSTC" && resolved.type === "fill") {
9268
+ layers.push({
9269
+ id: `s52-FORSTC-line`,
9270
+ type: "line",
9271
+ source: sourceId,
9272
+ "source-layer": "FORSTC",
9273
+ filter: GEOM_FILTERS.line,
9274
+ paint: {
9275
+ "line-color": resolveColor("CHBLK", ct),
9276
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.6, 14, 1.2, 17, 1.8]
9277
+ },
9278
+ metadata: { "s52:objectClass": "FORSTC", "s52:role": "line" }
9279
+ });
9280
+ }
9281
+ if (objectClass === "FSHFAC" && resolved.type === "fill") {
9282
+ layers.push({
9283
+ id: `s52-FSHFAC-point`,
9284
+ type: "circle",
9285
+ source: sourceId,
9286
+ "source-layer": "FSHFAC",
9287
+ filter: GEOM_FILTERS.circle,
9288
+ paint: {
9289
+ "circle-color": resolveColor("CHBLK", ct),
9290
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.4, 14, 2, 17, 2.8],
9291
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9292
+ "circle-stroke-width": 0.4
9293
+ },
9294
+ metadata: { "s52:objectClass": "FSHFAC", "s52:role": "point" }
9295
+ });
9296
+ }
9297
+ if (objectClass === "OBSTRN" && resolved.type === "symbol") {
9298
+ layers.push({
9299
+ id: `s52-OBSTRN-polygon`,
9300
+ type: "fill",
9301
+ source: sourceId,
9302
+ "source-layer": "OBSTRN",
9303
+ filter: GEOM_FILTERS.fill,
9304
+ paint: {
9305
+ "fill-color": resolveColor("CHBLK", ct),
9306
+ "fill-opacity": 0.1
9307
+ },
9308
+ metadata: { "s52:objectClass": "OBSTRN", "s52:role": "polygon" }
9309
+ });
9310
+ layers.push({
9311
+ id: `s52-OBSTRN-polygon-outline`,
9312
+ type: "line",
9313
+ source: sourceId,
9314
+ "source-layer": "OBSTRN",
9315
+ filter: GEOM_FILTERS.fill,
9316
+ paint: {
9317
+ "line-color": resolveColor("CHBLK", ct),
9318
+ "line-width": ["interpolate", ["linear"], ["zoom"], 8, 0.8, 14, 1.4],
9319
+ "line-dasharray": [3, 2]
9320
+ },
9321
+ metadata: { "s52:objectClass": "OBSTRN", "s52:role": "polygon-outline" }
9322
+ });
9323
+ layers.push({
9324
+ id: `s52-OBSTRN-line`,
9325
+ type: "line",
9326
+ source: sourceId,
9327
+ "source-layer": "OBSTRN",
9328
+ filter: GEOM_FILTERS.line,
9329
+ paint: {
9330
+ "line-color": resolveColor("CHBLK", ct),
9331
+ "line-width": 1.2,
9332
+ "line-dasharray": [3, 2]
9333
+ },
9334
+ metadata: { "s52:objectClass": "OBSTRN", "s52:role": "line" }
9335
+ });
9336
+ }
9337
+ if (objectClass === "SLCONS" && resolved.type === "line") {
9338
+ layers.push({
9339
+ id: `s52-SLCONS-polygon`,
9340
+ type: "fill",
9341
+ source: sourceId,
9342
+ "source-layer": "SLCONS",
9343
+ filter: GEOM_FILTERS.fill,
9344
+ paint: {
9345
+ "fill-color": resolveColor("CHBRN", ct),
9346
+ "fill-opacity": 0.5
9347
+ },
9348
+ metadata: { "s52:objectClass": "SLCONS", "s52:role": "polygon" }
9349
+ });
9350
+ layers.push({
9351
+ id: `s52-SLCONS-polygon-outline`,
9352
+ type: "line",
9353
+ source: sourceId,
9354
+ "source-layer": "SLCONS",
9355
+ filter: GEOM_FILTERS.fill,
9356
+ paint: {
9357
+ "line-color": resolveColor("CHBLK", ct),
9358
+ "line-width": 0.6
9359
+ },
9360
+ metadata: { "s52:objectClass": "SLCONS", "s52:role": "polygon-outline" }
9361
+ });
9362
+ layers.push({
9363
+ id: `s52-SLCONS-point`,
9364
+ type: "circle",
9365
+ source: sourceId,
9366
+ "source-layer": "SLCONS",
9367
+ filter: GEOM_FILTERS.circle,
9368
+ paint: {
9369
+ "circle-color": resolveColor("CHBRN", ct),
9370
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.6, 14, 2.2, 17, 3.2],
9371
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9372
+ "circle-stroke-width": 0.6
9373
+ },
9374
+ metadata: { "s52:objectClass": "SLCONS", "s52:role": "point" }
9375
+ });
9376
+ }
9377
+ if (objectClass === "PRDARE" && resolved.type === "line") {
9378
+ layers.push({
9379
+ id: `s52-PRDARE-point`,
9380
+ type: "circle",
9381
+ source: sourceId,
9382
+ "source-layer": "PRDARE",
9383
+ filter: GEOM_FILTERS.circle,
9384
+ paint: {
9385
+ "circle-color": resolveColor("CHMGD", ct),
9386
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 8, 2, 14, 3.5, 17, 5],
9387
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9388
+ "circle-stroke-width": 0.8,
9389
+ "circle-opacity": 0.35
9390
+ },
9391
+ metadata: { "s52:objectClass": "PRDARE", "s52:role": "point" }
9392
+ });
9393
+ }
9394
+ if (objectClass === "CTNARE" && resolved.type === "line") {
9395
+ layers.push({
9396
+ id: `s52-CTNARE-point`,
9397
+ type: "circle",
9398
+ source: sourceId,
9399
+ "source-layer": "CTNARE",
9400
+ filter: GEOM_FILTERS.circle,
9401
+ paint: {
9402
+ "circle-color": resolveColor("LITYW", ct),
9403
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.8, 14, 2.6, 17, 4],
9404
+ "circle-stroke-color": resolveColor("CHMGD", ct),
9405
+ "circle-stroke-width": 1.2
9406
+ },
9407
+ metadata: { "s52:objectClass": "CTNARE", "s52:role": "point" }
9408
+ });
9409
+ }
9410
+ if (objectClass === "PONTON" && resolved.type === "line") {
9411
+ layers.push({
9412
+ id: `s52-PONTON-polygon`,
9413
+ type: "fill",
9414
+ source: sourceId,
9415
+ "source-layer": "PONTON",
9416
+ filter: GEOM_FILTERS.fill,
9417
+ paint: {
9418
+ "fill-color": resolveColor("CHBRN", ct),
9419
+ "fill-opacity": 0.5
9420
+ },
9421
+ metadata: { "s52:objectClass": "PONTON", "s52:role": "polygon" }
9422
+ });
9423
+ layers.push({
9424
+ id: `s52-PONTON-polygon-outline`,
9425
+ type: "line",
9426
+ source: sourceId,
9427
+ "source-layer": "PONTON",
9428
+ filter: GEOM_FILTERS.fill,
9429
+ paint: {
9430
+ "line-color": resolveColor("CHBLK", ct),
9431
+ "line-width": 0.6
9432
+ },
9433
+ metadata: { "s52:objectClass": "PONTON", "s52:role": "polygon-outline" }
9434
+ });
9435
+ }
9436
+ if (objectClass === "WRECKS" && resolved.type === "symbol") {
9437
+ layers.push({
9438
+ id: `s52-WRECKS-polygon`,
9439
+ type: "fill",
9440
+ source: sourceId,
9441
+ "source-layer": "WRECKS",
9442
+ filter: GEOM_FILTERS.fill,
9443
+ paint: {
9444
+ "fill-color": resolveColor("CHBLK", ct),
9445
+ "fill-opacity": 0.08
9446
+ },
9447
+ metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon" }
9448
+ });
9449
+ layers.push({
9450
+ id: `s52-WRECKS-polygon-outline`,
9451
+ type: "line",
9452
+ source: sourceId,
9453
+ "source-layer": "WRECKS",
9454
+ filter: GEOM_FILTERS.fill,
9455
+ paint: {
9456
+ "line-color": resolveColor("CHBLK", ct),
9457
+ "line-width": 1,
9458
+ "line-dasharray": [3, 2]
9459
+ },
9460
+ metadata: { "s52:objectClass": "WRECKS", "s52:role": "polygon-outline" }
9461
+ });
9462
+ }
9463
+ if (objectClass === "WATTUR" && resolved.type === "symbol") {
9464
+ layers.push({
9465
+ id: `s52-WATTUR-polygon`,
9466
+ type: "fill",
9467
+ source: sourceId,
9468
+ "source-layer": "WATTUR",
9469
+ filter: GEOM_FILTERS.fill,
9470
+ paint: {
9471
+ "fill-color": resolveColor("CHBLK", ct),
9472
+ "fill-opacity": 0.05
9473
+ },
9474
+ metadata: { "s52:objectClass": "WATTUR", "s52:role": "polygon" }
9475
+ });
9476
+ }
9477
+ if (objectClass === "MAGVAR" && resolved.type === "line") {
9478
+ layers.push({
9479
+ id: "s52-MAGVAR-point",
9480
+ type: "circle",
9481
+ source: sourceId,
9482
+ "source-layer": "MAGVAR",
9483
+ filter: GEOM_FILTERS.circle,
9484
+ paint: {
9485
+ "circle-color": resolveColor("CHMGD", ct),
9486
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 1, 12, 2],
9487
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9488
+ "circle-stroke-width": 0.4,
9489
+ "circle-opacity": 0.4
9490
+ },
9491
+ metadata: { "s52:objectClass": "MAGVAR", "s52:role": "point" }
9492
+ });
9493
+ }
9494
+ if (objectClass === "DMPGRD" && resolved.type === "fill") {
9495
+ layers.push({
9496
+ id: `s52-DMPGRD-point`,
9497
+ type: "circle",
9498
+ source: sourceId,
9499
+ "source-layer": "DMPGRD",
9500
+ filter: GEOM_FILTERS.circle,
9501
+ paint: {
9502
+ "circle-color": resolveColor("LITYW", ct),
9503
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 2.5, 17, 3.6],
9504
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9505
+ "circle-stroke-width": 0.5,
9506
+ "circle-opacity": 0.7
9507
+ },
9508
+ metadata: { "s52:objectClass": "DMPGRD", "s52:role": "point" }
9509
+ });
9510
+ }
9511
+ if (objectClass === "DAMCON" && resolved.type === "line") {
9512
+ layers.push({
9513
+ id: `s52-DAMCON-polygon`,
9514
+ type: "fill",
9515
+ source: sourceId,
9516
+ "source-layer": "DAMCON",
9517
+ filter: GEOM_FILTERS.fill,
9518
+ paint: {
9519
+ "fill-color": resolveColor("CHBRN", ct),
9520
+ "fill-opacity": 0.4
9521
+ },
9522
+ metadata: { "s52:objectClass": "DAMCON", "s52:role": "polygon" }
9523
+ });
9524
+ layers.push({
9525
+ id: `s52-DAMCON-polygon-outline`,
9526
+ type: "line",
9527
+ source: sourceId,
9528
+ "source-layer": "DAMCON",
9529
+ filter: GEOM_FILTERS.fill,
9530
+ paint: {
9531
+ "line-color": resolveColor("CHBLK", ct),
9532
+ "line-width": 0.6
9533
+ },
9534
+ metadata: { "s52:objectClass": "DAMCON", "s52:role": "outline" }
9535
+ });
9536
+ }
9537
+ if (objectClass === "GATCON" && resolved.type === "line") {
9538
+ layers.push({
9539
+ id: `s52-GATCON-polygon`,
9540
+ type: "fill",
9541
+ source: sourceId,
9542
+ "source-layer": "GATCON",
9543
+ filter: GEOM_FILTERS.fill,
9544
+ paint: {
9545
+ "fill-color": resolveColor("CHBRN", ct),
9546
+ "fill-opacity": 0.4
9547
+ },
9548
+ metadata: { "s52:objectClass": "GATCON", "s52:role": "polygon" }
9549
+ });
9550
+ layers.push({
9551
+ id: `s52-GATCON-polygon-outline`,
9552
+ type: "line",
9553
+ source: sourceId,
9554
+ "source-layer": "GATCON",
9555
+ filter: GEOM_FILTERS.fill,
9556
+ paint: {
9557
+ "line-color": resolveColor("CHBLK", ct),
9558
+ "line-width": 0.6
9559
+ },
9560
+ metadata: { "s52:objectClass": "GATCON", "s52:role": "outline" }
9561
+ });
9562
+ }
9563
+ if (objectClass === "CANALS" && resolved.type === "line") {
9564
+ layers.push({
9565
+ id: `s52-CANALS-polygon`,
9566
+ type: "fill",
9567
+ source: sourceId,
9568
+ "source-layer": "CANALS",
9569
+ filter: GEOM_FILTERS.fill,
9570
+ paint: {
9571
+ "fill-color": resolveColor("DEPMS", ct),
9572
+ "fill-opacity": 0.5
9573
+ },
9574
+ metadata: { "s52:objectClass": "CANALS", "s52:role": "polygon" }
9575
+ });
9576
+ }
9577
+ if (objectClass === "PILBOP" && resolved.type === "symbol") {
9578
+ layers.push({
9579
+ id: `s52-PILBOP-polygon`,
9580
+ type: "fill",
9581
+ source: sourceId,
9582
+ "source-layer": "PILBOP",
9583
+ filter: GEOM_FILTERS.fill,
9584
+ paint: {
9585
+ "fill-color": resolveColor("CHMGD", ct),
9586
+ "fill-opacity": 0.1
9587
+ },
9588
+ metadata: { "s52:objectClass": "PILBOP", "s52:role": "polygon" }
9589
+ });
9590
+ layers.push({
9591
+ id: `s52-PILBOP-polygon-outline`,
9592
+ type: "line",
9593
+ source: sourceId,
9594
+ "source-layer": "PILBOP",
9595
+ filter: GEOM_FILTERS.fill,
9596
+ paint: {
9597
+ "line-color": resolveColor("CHMGD", ct),
9598
+ "line-width": 0.8,
9599
+ "line-dasharray": [4, 2]
9600
+ },
9601
+ metadata: { "s52:objectClass": "PILBOP", "s52:role": "outline" }
9602
+ });
9603
+ }
9604
+ if (objectClass === "HRBFAC" && resolved.type === "circle") {
9605
+ layers.push({
9606
+ id: `s52-HRBFAC-polygon`,
9607
+ type: "fill",
9608
+ source: sourceId,
9609
+ "source-layer": "HRBFAC",
9610
+ filter: GEOM_FILTERS.fill,
9611
+ paint: {
9612
+ "fill-color": resolveColor("TRFCD", ct),
9613
+ "fill-opacity": 0.15
9614
+ },
9615
+ metadata: { "s52:objectClass": "HRBFAC", "s52:role": "polygon" }
9616
+ });
9617
+ }
9618
+ if (objectClass === "MARCUL" && resolved.type === "fill") {
9619
+ layers.push({
9620
+ id: `s52-MARCUL-point`,
9621
+ type: "circle",
9622
+ source: sourceId,
9623
+ "source-layer": "MARCUL",
9624
+ filter: GEOM_FILTERS.circle,
9625
+ paint: {
9626
+ "circle-color": resolveColor("CHBRN", ct),
9627
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 12, 1.2, 14, 1.8, 17, 2.6],
9628
+ "circle-stroke-color": resolveColor("CHBLK", ct),
9629
+ "circle-stroke-width": 0.4,
9630
+ "circle-opacity": 0.6
9631
+ },
9632
+ metadata: { "s52:objectClass": "MARCUL", "s52:role": "point" }
9633
+ });
9634
+ layers.push({
9635
+ id: `s52-MARCUL-line`,
9636
+ type: "line",
9637
+ source: sourceId,
9638
+ "source-layer": "MARCUL",
9639
+ filter: GEOM_FILTERS.line,
9640
+ paint: {
9641
+ "line-color": resolveColor("CHBLK", ct),
9642
+ "line-width": 0.5,
9643
+ "line-dasharray": [2, 2]
9644
+ },
9645
+ metadata: { "s52:objectClass": "MARCUL", "s52:role": "line" }
9646
+ });
9647
+ }
9648
+ if (objectClass === "RECTRC" && resolved.type === "line") {
9649
+ layers.push({
9650
+ id: `s52-RECTRC-polygon`,
9651
+ type: "fill",
9652
+ source: sourceId,
9653
+ "source-layer": "RECTRC",
9654
+ filter: GEOM_FILTERS.fill,
9655
+ paint: {
9656
+ "fill-color": resolveColor("TRFCD", ct),
9657
+ "fill-opacity": 0.05
9658
+ },
9659
+ metadata: { "s52:objectClass": "RECTRC", "s52:role": "polygon" }
9660
+ });
9661
+ }
7707
9662
  }
7708
9663
  return layers;
7709
9664
  }
7710
- function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION) {
9665
+ function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION, depthUnits = "meters", labelOpts = {}) {
7711
9666
  if (!LABELED_LAYERS.has(objectClass)) return null;
7712
- const config = getLabelConfig(objectClass);
9667
+ const config = getLabelConfig(objectClass, {
9668
+ colorTable: ct,
9669
+ safetyContour: labelOpts.safetyContour,
9670
+ twoShadeSoundings: labelOpts.twoShadeSoundings,
9671
+ soundingFontWeight: labelOpts.soundingFontWeight
9672
+ });
7713
9673
  if (!config) return null;
7714
9674
  let textField = config.textField;
7715
9675
  if (objectClass === "LIGHTS") {
@@ -7718,9 +9678,36 @@ function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION) {
7718
9678
  if (typeof textField !== "string" && Array.isArray(textField)) {
7719
9679
  const flat = JSON.stringify(textField);
7720
9680
  if (flat.includes("_navAidLabel")) {
7721
- textField = ["get", "OBJNAM"];
9681
+ textField = buildNavAidLabelExpression();
7722
9682
  }
7723
9683
  }
9684
+ if (depthUnits === "feet") {
9685
+ if (objectClass === "SOUNDG") {
9686
+ textField = [
9687
+ "concat",
9688
+ [
9689
+ "round",
9690
+ [
9691
+ "*",
9692
+ [
9693
+ "to-number",
9694
+ ["coalesce", ["get", "depth"], ["get", "DEPTH"], ["get", "VALSOU"]],
9695
+ 0
9696
+ ],
9697
+ 3.28084
9698
+ ]
9699
+ ],
9700
+ ""
9701
+ ];
9702
+ } else if (objectClass === "DEPCNT") {
9703
+ textField = [
9704
+ "concat",
9705
+ ["round", ["*", ["to-number", ["get", "VALDCO"]], 3.28084]],
9706
+ "ft"
9707
+ ];
9708
+ }
9709
+ }
9710
+ const isSafetyCritical = SAFETY_CRITICAL_LABELS.has(objectClass);
7724
9711
  const layer = {
7725
9712
  id: `s52-${objectClass}-label`,
7726
9713
  type: "symbol",
@@ -7730,12 +9717,12 @@ function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION) {
7730
9717
  layout: {
7731
9718
  "text-field": textField,
7732
9719
  "text-size": config.textSize,
7733
- "text-font": config.textFont ?? ["Open Sans Regular", "Arial Unicode MS Regular"],
9720
+ "text-font": config.textFont ?? ["Open Sans Regular"],
7734
9721
  "text-offset": config.textOffset,
7735
9722
  "text-anchor": "top",
7736
- "text-allow-overlap": false,
7737
- "text-optional": true,
7738
- "text-padding": 4,
9723
+ "text-allow-overlap": isSafetyCritical,
9724
+ "text-optional": !isSafetyCritical,
9725
+ "text-padding": isSafetyCritical ? 0 : 4,
7739
9726
  ...config.layout ?? {}
7740
9727
  },
7741
9728
  paint: {
@@ -7750,6 +9737,18 @@ function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION) {
7750
9737
  if (objectClass === "DEPCNT") {
7751
9738
  layer.filter = GEOM_FILTERS.line;
7752
9739
  }
9740
+ if (objectClass === "CBLOHD" || objectClass === "PIPOHD") {
9741
+ layer.filter = GEOM_FILTERS.line;
9742
+ }
9743
+ if (objectClass === "SBDARE") {
9744
+ delete layer.filter;
9745
+ }
9746
+ if (objectClass === "MAGVAR") {
9747
+ delete layer.filter;
9748
+ }
9749
+ if (getLabelBehavior(objectClass) === "orientation-cross-band") {
9750
+ delete layer.filter;
9751
+ }
7753
9752
  return layer;
7754
9753
  }
7755
9754
  function getGeomTypesForClass(objectClass, defaultType) {
@@ -7779,6 +9778,105 @@ function getOrderedClasses() {
7779
9778
  return ordered;
7780
9779
  }
7781
9780
 
9781
+ // src/atlas/band-priority.ts
9782
+ var BAND_PRIORITY_RANGES = {
9783
+ "1": { minzoom: 3 },
9784
+ "2": { minzoom: 5 },
9785
+ "3": { minzoom: 7 },
9786
+ "4": { minzoom: 9 },
9787
+ "5": { minzoom: 11 },
9788
+ "6": { minzoom: 11 }
9789
+ };
9790
+ var BAND_DRAW_ORDER = ["1", "2", "3", "4", "5", "6"];
9791
+ var CHART_BAND_CODES = {
9792
+ Overview: ["1"],
9793
+ General: ["2"],
9794
+ Coastal: ["3"],
9795
+ Approach: ["4"],
9796
+ Harbor: ["5", "6"]
9797
+ };
9798
+ var CHART_CASCADE_CODES = {
9799
+ Overview: ["1"],
9800
+ General: ["1", "2"],
9801
+ Coastal: ["1", "2", "3"],
9802
+ Approach: ["1", "2", "3", "4"],
9803
+ Harbor: ["1", "2", "3", "4", "5", "6"]
9804
+ };
9805
+ function bandFilterExpression(codes) {
9806
+ const list = Array.isArray(codes) ? codes : [codes];
9807
+ if (list.length === 1) {
9808
+ return ["==", ["slice", ["get", "_cell_id"], 2, 3], list[0]];
9809
+ }
9810
+ return ["in", ["slice", ["get", "_cell_id"], 2, 3], ["literal", [...list]]];
9811
+ }
9812
+ function combineWithBandFilter(baseFilter, bandFilter) {
9813
+ if (!baseFilter) return bandFilter;
9814
+ if (baseFilter[0] === "all") {
9815
+ return ["all", ...baseFilter.slice(1), bandFilter];
9816
+ }
9817
+ return ["all", baseFilter, bandFilter];
9818
+ }
9819
+ var NOT_DOMINATED_FILTER = [
9820
+ "all",
9821
+ ["!", ["has", "_dominated_by_min"]],
9822
+ ["!=", ["get", "_dominated"], true]
9823
+ ];
9824
+ function dominatedByMinFilter(D) {
9825
+ const dInt = parseInt(D, 10);
9826
+ if (D === "6") {
9827
+ return [
9828
+ "any",
9829
+ ["==", ["get", "_dominated_by_min"], dInt],
9830
+ ["==", ["get", "_dominated"], true]
9831
+ ];
9832
+ }
9833
+ return ["==", ["get", "_dominated_by_min"], dInt];
9834
+ }
9835
+ function withinBboxFilter(bbox) {
9836
+ const [w, s] = bbox.sw;
9837
+ const [e, n] = bbox.ne;
9838
+ return [
9839
+ "within",
9840
+ {
9841
+ type: "Polygon",
9842
+ coordinates: [
9843
+ [
9844
+ [w, s],
9845
+ [e, s],
9846
+ [e, n],
9847
+ [w, n],
9848
+ [w, s]
9849
+ ]
9850
+ ]
9851
+ }
9852
+ ];
9853
+ }
9854
+ function worldMinusBboxPolygon(bbox) {
9855
+ const [w, s] = bbox.sw;
9856
+ const [e, n] = bbox.ne;
9857
+ return {
9858
+ type: "Polygon",
9859
+ coordinates: [
9860
+ [
9861
+ [-180, -85],
9862
+ [180, -85],
9863
+ [180, 85],
9864
+ [-180, 85],
9865
+ [-180, -85]
9866
+ ],
9867
+ // Inner ring (hole) — winding opposite to outer ring per GeoJSON
9868
+ // spec. MapLibre is permissive but we follow strict winding.
9869
+ [
9870
+ [w, s],
9871
+ [w, n],
9872
+ [e, n],
9873
+ [e, s],
9874
+ [w, s]
9875
+ ]
9876
+ ]
9877
+ };
9878
+ }
9879
+
7782
9880
  // src/atlas/style-generator.ts
7783
9881
  var META_LAYERS = /* @__PURE__ */ new Set([
7784
9882
  "DSID",
@@ -7805,28 +9903,251 @@ var PALETTE_NAMES = {
7805
9903
  DUSK: "DUSK",
7806
9904
  NIGHT: "NIGHT"
7807
9905
  };
9906
+ var DOMINANCE_EXEMPT_CLASSES = /* @__PURE__ */ new Set([
9907
+ "LNDARE",
9908
+ // Safety-critical nav-aid point classes (S-52 viewing groups 24020,
9909
+ // 27010, 27070, 27080). User-reported: offshore Mass Bay BOYSAW /
9910
+ // BOYLAT etc. disappeared at z >= 12 because the dominance index
9911
+ // tagged the coastal-band feature `_dominated_by_min` for a harbor
9912
+ // cell whose M_COVR centroid-covered the buoy but whose feature data
9913
+ // didn't actually include it — no harbor-band copy ever takes over,
9914
+ // the dominated coastal variant times out at the dominator's
9915
+ // minzoom, and the buoy vanishes. Force these classes to always
9916
+ // render their not-dominated variant regardless of `_dominated_by_min`.
9917
+ // Trade-off: where both coastal AND harbor cells genuinely have the
9918
+ // same buoy, both copies render stacked — visually indistinguishable
9919
+ // from one buoy. Safety wins over the cosmetic dedupe.
9920
+ "BOYLAT",
9921
+ "BOYCAR",
9922
+ "BOYSPP",
9923
+ "BOYISD",
9924
+ "BOYSAW",
9925
+ "BOYINB",
9926
+ "BCNLAT",
9927
+ "BCNCAR",
9928
+ "BCNSPP",
9929
+ "BCNISD",
9930
+ "BCNSAW",
9931
+ "LIGHTS",
9932
+ "LITFLT",
9933
+ "LITVES",
9934
+ "FOGSIG",
9935
+ "RTPBCN",
9936
+ "RDOSTA"
9937
+ ]);
9938
+ function isDominanceExempt(layer) {
9939
+ for (const cls of DOMINANCE_EXEMPT_CLASSES) {
9940
+ if (layer.id.startsWith(`s52-${cls}-`)) return true;
9941
+ }
9942
+ return false;
9943
+ }
9944
+ function makeBandVariants(layer, B) {
9945
+ const range = BAND_PRIORITY_RANGES[B];
9946
+ const classMinzoom = layer.minzoom ?? 0;
9947
+ const minzoom = Math.max(classMinzoom, range.minzoom);
9948
+ const variants = [];
9949
+ if (isDominanceExempt(layer)) {
9950
+ const exempt = {
9951
+ ...layer,
9952
+ id: `${layer.id}-band${B}`,
9953
+ filter: combineWithBandFilter(layer.filter, bandFilterExpression(B)),
9954
+ minzoom
9955
+ };
9956
+ delete exempt.maxzoom;
9957
+ return [exempt];
9958
+ }
9959
+ const notDom = {
9960
+ ...layer,
9961
+ id: `${layer.id}-band${B}`,
9962
+ filter: combineWithBandFilter(
9963
+ layer.filter,
9964
+ ["all", bandFilterExpression(B), NOT_DOMINATED_FILTER]
9965
+ ),
9966
+ minzoom
9967
+ };
9968
+ delete notDom.maxzoom;
9969
+ variants.push(notDom);
9970
+ const bInt = parseInt(B, 10);
9971
+ for (const D of BAND_DRAW_ORDER) {
9972
+ const dInt = parseInt(D, 10);
9973
+ if (dInt <= bInt) continue;
9974
+ const dRange = BAND_PRIORITY_RANGES[D];
9975
+ if (dRange.minzoom <= minzoom) continue;
9976
+ const domVariant = {
9977
+ ...layer,
9978
+ id: `${layer.id}-band${B}-domBy${D}`,
9979
+ filter: combineWithBandFilter(
9980
+ layer.filter,
9981
+ ["all", bandFilterExpression(B), dominatedByMinFilter(D)]
9982
+ ),
9983
+ minzoom,
9984
+ maxzoom: dRange.minzoom
9985
+ };
9986
+ variants.push(domVariant);
9987
+ }
9988
+ return variants;
9989
+ }
9990
+ function fanOrientationLabelByDominator(label) {
9991
+ const variants = [];
9992
+ variants.push({
9993
+ ...label,
9994
+ id: `${label.id}-notdom`,
9995
+ filter: combineWithBandFilter(label.filter, NOT_DOMINATED_FILTER)
9996
+ });
9997
+ for (const D of BAND_DRAW_ORDER) {
9998
+ if (D === "1") continue;
9999
+ variants.push({
10000
+ ...label,
10001
+ id: `${label.id}-domBy${D}`,
10002
+ filter: combineWithBandFilter(label.filter, dominatedByMinFilter(D)),
10003
+ maxzoom: BAND_PRIORITY_RANGES[D].minzoom
10004
+ });
10005
+ }
10006
+ return variants;
10007
+ }
10008
+ function applyHarborLabelCollisionDiscipline(variant, objectClass, code) {
10009
+ if (!HIGH_DENSITY_NAV_AIDS.has(objectClass)) return variant;
10010
+ if (code !== "5" && code !== "6") return variant;
10011
+ const oldLayout = variant.layout ?? {};
10012
+ return {
10013
+ ...variant,
10014
+ layout: {
10015
+ ...oldLayout,
10016
+ "text-allow-overlap": false,
10017
+ "text-optional": true,
10018
+ // Wider padding so even allowed labels space visually instead of
10019
+ // touching. 6 is enough to read individual buoy designations clearly
10020
+ // at z12-14 over Boston Harbor.
10021
+ "text-padding": 6,
10022
+ // Deterministic collision arbitration: prefer features with
10023
+ // longer-range nav-aid significance (VALNMR — visible nautical
10024
+ // miles), then shorter OBJNAM (tighter labels win), then FIDN as
10025
+ // tiebreaker. Lower sort-key wins in MapLibre.
10026
+ "symbol-sort-key": [
10027
+ "case",
10028
+ ["has", "VALNMR"],
10029
+ ["-", 0, ["coalesce", ["to-number", ["get", "VALNMR"]], 0]],
10030
+ ["length", ["coalesce", ["get", "OBJNAM"], ""]]
10031
+ ]
10032
+ }
10033
+ };
10034
+ }
10035
+ function chartModeFilterForBand(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
10036
+ let filter = combineWithBandFilter(layer.filter, bandFilterExpression(B));
10037
+ if (!isDominanceExempt(layer)) {
10038
+ for (const D of higherCascadeBands) {
10039
+ filter = combineWithBandFilter(filter, ["!", dominatedByMinFilter(D)]);
10040
+ }
10041
+ }
10042
+ if (lockedBounds && isRequestedBand) {
10043
+ filter = combineWithBandFilter(filter, withinBboxFilter(lockedBounds));
10044
+ }
10045
+ return filter;
10046
+ }
7808
10047
  function generateStyle(palette = "DAY", opts = {}) {
7809
10048
  const sources = buildSources(opts);
7810
10049
  const sourceId = opts.region ?? DEFAULT_REGION;
7811
10050
  const orderedClasses = getOrderedClasses();
7812
10051
  const layers = [];
7813
10052
  const labelLayers = [];
10053
+ const chartCascade = opts.chartMode ? CHART_CASCADE_CODES[opts.chartMode.band] : null;
10054
+ const chartRequestedCodes = opts.chartMode ? CHART_BAND_CODES[opts.chartMode.band] : null;
10055
+ const lockedBounds = opts.chartMode?.lockedBounds;
10056
+ const bundles = [];
7814
10057
  for (const objectClass of orderedClasses) {
7815
10058
  if (META_LAYERS.has(objectClass)) continue;
7816
- const featureLayers = buildLayersForClass(objectClass, palette, sourceId);
7817
- layers.push(...featureLayers);
7818
- const label = buildLabelLayer2(objectClass, palette, sourceId);
7819
- if (label) labelLayers.push(label);
10059
+ bundles.push({
10060
+ objectClass,
10061
+ featureLayers: buildLayersForClass(objectClass, palette, sourceId),
10062
+ label: buildLabelLayer2(objectClass, palette, sourceId, opts.depthUnits ?? "meters", {
10063
+ safetyContour: opts.safetyContour,
10064
+ twoShadeSoundings: opts.twoShadeSoundings,
10065
+ soundingFontWeight: opts.soundingFontWeight
10066
+ })
10067
+ });
10068
+ }
10069
+ if (chartCascade && chartRequestedCodes) {
10070
+ const requestedSet = new Set(chartRequestedCodes);
10071
+ for (let i = 0; i < chartCascade.length; i++) {
10072
+ const B = chartCascade[i];
10073
+ const higher = chartCascade.slice(i + 1);
10074
+ const isRequested = requestedSet.has(B);
10075
+ for (const { featureLayers } of bundles) {
10076
+ for (const base of featureLayers) {
10077
+ const filter = chartModeFilterForBand(
10078
+ base,
10079
+ B,
10080
+ higher,
10081
+ lockedBounds,
10082
+ isRequested
10083
+ );
10084
+ const variant = {
10085
+ ...base,
10086
+ id: `${base.id}-band${B}`,
10087
+ filter
10088
+ };
10089
+ delete variant.maxzoom;
10090
+ layers.push(variant);
10091
+ }
10092
+ }
10093
+ }
10094
+ for (let i = 0; i < chartCascade.length; i++) {
10095
+ const B = chartCascade[i];
10096
+ const higher = chartCascade.slice(i + 1);
10097
+ const isRequested = requestedSet.has(B);
10098
+ for (const { label } of bundles) {
10099
+ if (!label) continue;
10100
+ const filter = chartModeFilterForBand(
10101
+ label,
10102
+ B,
10103
+ higher,
10104
+ lockedBounds,
10105
+ isRequested
10106
+ );
10107
+ const variant = {
10108
+ ...label,
10109
+ id: `${label.id}-band${B}`,
10110
+ filter
10111
+ };
10112
+ delete variant.maxzoom;
10113
+ labelLayers.push(variant);
10114
+ }
10115
+ }
10116
+ layers.push(...labelLayers);
10117
+ } else {
10118
+ for (const code of BAND_DRAW_ORDER) {
10119
+ for (const { featureLayers } of bundles) {
10120
+ for (const base of featureLayers) {
10121
+ layers.push(...makeBandVariants(base, code));
10122
+ }
10123
+ }
10124
+ }
10125
+ for (const { objectClass, label } of bundles) {
10126
+ if (!label) continue;
10127
+ if (getLabelBehavior(objectClass) === "orientation-cross-band") {
10128
+ labelLayers.push(...fanOrientationLabelByDominator(label));
10129
+ }
10130
+ }
10131
+ for (const code of BAND_DRAW_ORDER) {
10132
+ for (const { objectClass, label } of bundles) {
10133
+ if (!label) continue;
10134
+ if (getLabelBehavior(objectClass) === "orientation-cross-band") continue;
10135
+ for (const variant of makeBandVariants(label, code)) {
10136
+ labelLayers.push(applyHarborLabelCollisionDiscipline(variant, objectClass, code));
10137
+ }
10138
+ }
10139
+ }
10140
+ layers.push(...labelLayers);
7820
10141
  }
7821
- layers.push(...labelLayers);
7822
10142
  return {
7823
10143
  version: 8,
7824
- name: `CHRT S-52 ${PALETTE_NAMES[palette]}`,
10144
+ name: `CHRT S-52 ${PALETTE_NAMES[palette]}${opts.chartMode ? ` \u2014 chart:${opts.chartMode.band}` : ""}`,
7825
10145
  metadata: {
7826
10146
  "chrt:s52-edition": "4.0",
7827
10147
  "chrt:color-table": palette,
7828
10148
  "chrt:generated-at": (/* @__PURE__ */ new Date()).toISOString(),
7829
10149
  "chrt:layer-count": layers.length,
10150
+ "chrt:render-mode": opts.chartMode ? `chart:${opts.chartMode.band}` : "continuous",
7830
10151
  "chrt:description": "IHO S-52 portrayal rules for Electronic Navigational Charts (ENCs). Generated from the CHRT atlas engine."
7831
10152
  },
7832
10153
  sources,
@@ -7835,7 +10156,7 @@ function generateStyle(palette = "DAY", opts = {}) {
7835
10156
  // we're decommissioning Mapbox MTS. When the page is repurposed to
7836
10157
  // generate a MapLibre-native style for self-hosted consumers, wire the
7837
10158
  // `sprite` field back in against a CHRT-hosted sprite route (see
7838
- // `.lovable/` follow-ups). Runtime maps load S-52 symbols via
10159
+ // `.chrt/` follow-ups). Runtime maps load S-52 symbols via
7839
10160
  // `loadS52SymbolImages()` — no sprite fetch happens at runtime.
7840
10161
  glyphs: "https://fonts.openmaptiles.org/{fontstack}/{range}.pbf",
7841
10162
  layers
@@ -8006,14 +10327,19 @@ var Chrt = class {
8006
10327
  }
8007
10328
  };
8008
10329
  export {
10330
+ BAND_DRAW_ORDER,
10331
+ BAND_PRIORITY_RANGES,
8009
10332
  BUILD_MAXZOOM,
8010
10333
  BUILD_MINZOOM,
10334
+ CHART_BAND_CODES,
10335
+ CHART_CASCADE_CODES,
8011
10336
  CHART_SECTIONS,
8012
10337
  Chrt,
8013
10338
  ChrtError,
8014
10339
  DEFAULT_REGION,
8015
10340
  DEFAULT_TILE_BASE_URL,
8016
10341
  LABELED_LAYERS,
10342
+ LABEL_BEHAVIOR,
8017
10343
  NAUTICAL_SYMBOLS,
8018
10344
  OfflineError,
8019
10345
  OfflineStyleError,
@@ -8030,10 +10356,12 @@ export {
8030
10356
  SYMBOL_GROUPS,
8031
10357
  SYMBOL_MAP,
8032
10358
  applyColorTheme,
10359
+ bandFilterExpression,
8033
10360
  buildLabelLayer,
8034
10361
  buildLayersForClass,
8035
10362
  buildOfflineStyle,
8036
10363
  buildSources,
10364
+ combineWithBandFilter,
8037
10365
  daymarkColor,
8038
10366
  decimateFeatures,
8039
10367
  depareColor,
@@ -8050,6 +10378,7 @@ export {
8050
10378
  getDrawPriority,
8051
10379
  getGridSizeForZoom,
8052
10380
  getIconExpression,
10381
+ getLabelBehavior,
8053
10382
  getLabelConfig,
8054
10383
  getLookupEntry,
8055
10384
  getMaxCellsForZoom,
@@ -8075,6 +10404,8 @@ export {
8075
10404
  preprocessWaterBodies,
8076
10405
  resolveColor,
8077
10406
  resolveStyle,
8078
- scaminToMinZoom
10407
+ scaminToMinZoom,
10408
+ withinBboxFilter,
10409
+ worldMinusBboxPolygon
8079
10410
  };
8080
10411
  //# sourceMappingURL=index.js.map