@chrtco/sdk 0.7.5 → 0.7.6

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.cjs CHANGED
@@ -3248,18 +3248,37 @@ function depareFillExpression(ct = "DAY") {
3248
3248
  resolveColor("DEPDW", ct)
3249
3249
  ];
3250
3250
  }
3251
- function colourCodeCase(cases, fallback) {
3252
- const colourStr = ["to-string", ["coalesce", ["get", "COLOUR"], ["get", "colour"], ""]];
3251
+ function listAttrString(attr) {
3252
+ return ["to-string", ["coalesce", ["get", attr], ["get", attr.toLowerCase()], ""]];
3253
+ }
3254
+ function listAttrCase(attr, cases, fallback) {
3255
+ const attrStr = listAttrString(attr);
3253
3256
  const expr = ["case"];
3254
3257
  for (const [code, value] of cases) {
3255
- expr.push([">=", ["index-of", `"${code}"`, colourStr], 0], value);
3258
+ expr.push([">=", ["index-of", `"${code}"`, attrStr], 0], value);
3259
+ }
3260
+ for (const [code, value] of cases) {
3261
+ expr.push(["==", attrStr, code], value);
3256
3262
  }
3257
3263
  for (const [code, value] of cases) {
3258
- expr.push(["==", colourStr, code], value);
3264
+ expr.push(["==", attrStr, `[${code}]`], value);
3259
3265
  }
3260
3266
  expr.push(fallback);
3261
3267
  return expr;
3262
3268
  }
3269
+ function listAttrHas(attr, codes) {
3270
+ const attrStr = listAttrString(attr);
3271
+ const clauses = [];
3272
+ for (const code of codes) {
3273
+ clauses.push([">=", ["index-of", `"${code}"`, attrStr], 0]);
3274
+ clauses.push(["==", attrStr, code]);
3275
+ clauses.push(["==", attrStr, `[${code}]`]);
3276
+ }
3277
+ return ["any", ...clauses];
3278
+ }
3279
+ function colourCodeCase(cases, fallback) {
3280
+ return listAttrCase("COLOUR", cases, fallback);
3281
+ }
3263
3282
  function lightColor(props, ct = "DAY") {
3264
3283
  const raw = props.COLOUR ?? props.colour;
3265
3284
  const code = Array.isArray(raw) ? Number(raw[0]) : typeof raw === "string" && raw.startsWith("[") ? Number(JSON.parse(raw)[0]) : Number(raw ?? 0);
@@ -5298,7 +5317,7 @@ var NAUTICAL_SYMBOLS = [
5298
5317
  description: "Church spire or steeple, conspicuous from seaward. Useful for visual fixing.",
5299
5318
  s52PlRef: "LNDMRK17",
5300
5319
  attributeKey: "CATLMK",
5301
- attributeValue: 17,
5320
+ attributeValue: 20,
5302
5321
  renderSize: "32x32",
5303
5322
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Spire, building, position circle" }],
5304
5323
  lineStyles: [{ element: "Building outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5315,7 +5334,7 @@ var NAUTICAL_SYMBOLS = [
5315
5334
  description: "Tower conspicuous from seaward. General-purpose landmark symbol.",
5316
5335
  s52PlRef: "LNDMRK20",
5317
5336
  attributeKey: "CATLMK",
5318
- attributeValue: 20,
5337
+ attributeValue: 17,
5319
5338
  renderSize: "32x32",
5320
5339
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower body, cap, position circle" }],
5321
5340
  lineStyles: [{ element: "Tower outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5332,7 +5351,7 @@ var NAUTICAL_SYMBOLS = [
5332
5351
  description: "Industrial chimney or smokestack visible from the sea.",
5333
5352
  s52PlRef: "LNDMRK01",
5334
5353
  attributeKey: "CATLMK",
5335
- attributeValue: 1,
5354
+ attributeValue: 3,
5336
5355
  renderSize: "32x32",
5337
5356
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Chimney body, smoke, position circle" }],
5338
5357
  lineStyles: [{ element: "Body outline", width: 0.8, color: "#000000" }, { element: "Smoke plume", width: 1, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5349,7 +5368,7 @@ var NAUTICAL_SYMBOLS = [
5349
5368
  description: "Flagstaff or flagpole conspicuous from seaward.",
5350
5369
  s52PlRef: "LNDMRK03",
5351
5370
  attributeKey: "CATLMK",
5352
- attributeValue: 3,
5371
+ attributeValue: 5,
5353
5372
  renderSize: "32x32",
5354
5373
  colors: [
5355
5374
  { token: "CHBLK", hex: "#000000", usage: "Pole and position circle" },
@@ -5369,7 +5388,7 @@ var NAUTICAL_SYMBOLS = [
5369
5388
  description: "Monument or memorial conspicuous from the sea.",
5370
5389
  s52PlRef: "LNDMRK05",
5371
5390
  attributeKey: "CATLMK",
5372
- attributeValue: 5,
5391
+ attributeValue: 9,
5373
5392
  renderSize: "32x32",
5374
5393
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Monument body, base, position circle" }],
5375
5394
  lineStyles: [{ element: "Body outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5384,9 +5403,9 @@ var NAUTICAL_SYMBOLS = [
5384
5403
  getSvg: landmarkWindmill,
5385
5404
  group: "landmarks",
5386
5405
  description: "Traditional windmill with rotating vanes. Conspicuous landmark.",
5387
- s52PlRef: "LNDMRK21",
5406
+ s52PlRef: "WNDMIL12",
5388
5407
  attributeKey: "CATLMK",
5389
- attributeValue: 7,
5408
+ attributeValue: 18,
5390
5409
  renderSize: "32x32",
5391
5410
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower, vanes, hub, position circle" }],
5392
5411
  lineStyles: [{ element: "Tower", width: 1.5, color: "#000000" }, { element: "Vane arms", width: 1.2, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5401,9 +5420,9 @@ var NAUTICAL_SYMBOLS = [
5401
5420
  getSvg: landmarkWindTurbine,
5402
5421
  group: "landmarks",
5403
5422
  description: "Modern 3-blade wind turbine. Increasingly common offshore landmark.",
5404
- s52PlRef: "LNDMRK22",
5423
+ s52PlRef: "WIMCON11",
5405
5424
  attributeKey: "CATLMK",
5406
- attributeValue: 26,
5425
+ attributeValue: 19,
5407
5426
  renderSize: "32x32",
5408
5427
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower, blades, hub, position circle" }],
5409
5428
  lineStyles: [{ element: "Tower", width: 1.5, color: "#000000" }, { element: "Blades", width: 1.2, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5420,7 +5439,7 @@ var NAUTICAL_SYMBOLS = [
5420
5439
  description: "Dome or cupola (e.g., observatory, mosque) visible from the sea.",
5421
5440
  s52PlRef: "LNDMRK03",
5422
5441
  attributeKey: "CATLMK",
5423
- attributeValue: 6,
5442
+ attributeValue: 15,
5424
5443
  renderSize: "32x32",
5425
5444
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Dome, base, position circle" }],
5426
5445
  lineStyles: [{ element: "Base outline", width: 0.8, color: "#000000" }, { element: "Dome curve", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5437,7 +5456,7 @@ var NAUTICAL_SYMBOLS = [
5437
5456
  description: "Radar scanner antenna conspicuous from seaward.",
5438
5457
  s52PlRef: "LNDMRK12",
5439
5458
  attributeKey: "CATLMK",
5440
- attributeValue: 9,
5459
+ attributeValue: 16,
5441
5460
  renderSize: "32x32",
5442
5461
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower, scanner dish, position circle" }],
5443
5462
  lineStyles: [{ element: "Tower", width: 1.5, color: "#000000" }, { element: "Scanner frame", width: 1.5, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5454,7 +5473,7 @@ var NAUTICAL_SYMBOLS = [
5454
5473
  description: "Satellite dish or parabolic antenna visible from the sea.",
5455
5474
  s52PlRef: "LNDMRK12",
5456
5475
  attributeKey: "CATLMK",
5457
- attributeValue: 12,
5476
+ attributeValue: 4,
5458
5477
  renderSize: "32x32",
5459
5478
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tower, dish, position circle" }],
5460
5479
  lineStyles: [{ element: "Tower", width: 1.5, color: "#000000" }, { element: "Dish curve", width: 1.5, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5469,9 +5488,9 @@ var NAUTICAL_SYMBOLS = [
5469
5488
  getSvg: landmarkWaterTower,
5470
5489
  group: "landmarks",
5471
5490
  description: "Water tower. Elevated tank on supporting structure, conspicuous landmark.",
5472
- s52PlRef: "LNDMRK19",
5473
- attributeKey: "CATLMK",
5474
- attributeValue: 15,
5491
+ s52PlRef: "TOWERS12",
5492
+ attributeKey: "CATSIL",
5493
+ attributeValue: 4,
5475
5494
  renderSize: "32x32",
5476
5495
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Tank, legs, position circle" }],
5477
5496
  lineStyles: [{ element: "Leg outlines", width: 1.2, color: "#000000" }, { element: "Tank outline", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5488,7 +5507,7 @@ var NAUTICAL_SYMBOLS = [
5488
5507
  description: "Radio or television transmission mast with horizontal cross-arms.",
5489
5508
  s52PlRef: "LNDMRK10",
5490
5509
  attributeKey: "CATLMK",
5491
- attributeValue: 8,
5510
+ attributeValue: 7,
5492
5511
  renderSize: "32x32",
5493
5512
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Mast, cross-arms, top, position circle" }],
5494
5513
  lineStyles: [{ element: "Main mast", width: 1.5, color: "#000000" }, { element: "Cross-arms", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5505,7 +5524,7 @@ var NAUTICAL_SYMBOLS = [
5505
5524
  description: "Religious cross or calvary conspicuous from seaward.",
5506
5525
  s52PlRef: "LNDMRK05",
5507
5526
  attributeKey: "CATLMK",
5508
- attributeValue: 10,
5527
+ attributeValue: 14,
5509
5528
  renderSize: "32x32",
5510
5529
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Cross arms and position circle" }],
5511
5530
  lineStyles: [{ element: "Vertical arm", width: 2, color: "#000000" }, { element: "Horizontal arm", width: 2, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5522,7 +5541,7 @@ var NAUTICAL_SYMBOLS = [
5522
5541
  description: "General mast (not radio/TV). Pole with spherical top.",
5523
5542
  s52PlRef: "LNDMRK10",
5524
5543
  attributeKey: "CATLMK",
5525
- attributeValue: 14,
5544
+ attributeValue: 7,
5526
5545
  renderSize: "32x32",
5527
5546
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Mast pole, top ball, position circle" }],
5528
5547
  lineStyles: [{ element: "Mast pole", width: 2, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5537,9 +5556,9 @@ var NAUTICAL_SYMBOLS = [
5537
5556
  getSvg: landmarkSilo,
5538
5557
  group: "landmarks",
5539
5558
  description: "Silo, grain elevator, or storage tank conspicuous from the sea.",
5540
- s52PlRef: "LNDMRK17",
5541
- attributeKey: "CATLMK",
5542
- attributeValue: 33,
5559
+ s52PlRef: "SILBUI01",
5560
+ attributeKey: "CATSIL",
5561
+ attributeValue: 1,
5543
5562
  renderSize: "32x32",
5544
5563
  colors: [{ token: "CHBLK", hex: "#000000", usage: "Body, dome top, position circle" }],
5545
5564
  lineStyles: [{ element: "Body outline", width: 0.8, color: "#000000" }, { element: "Dome curve", width: 0.8, color: "#000000" }, { element: "Position circle", width: 0.6, pattern: "2, 2", color: "#000000" }],
@@ -5941,27 +5960,18 @@ function getIconExpression(layerName) {
5941
5960
  ];
5942
5961
  }
5943
5962
  if (layerName === "LNDMRK") {
5944
- return [
5945
- "match",
5946
- ["coalesce", ["get", "CATLMK"], ["get", "catlmk"], 0],
5947
- 1,
5948
- "CAIRNS11",
5949
- // Cairn
5950
- 17,
5951
- "BCNTOW01",
5952
- // Tower
5953
- 18,
5954
- "LNDMRK18",
5963
+ return listAttrCase("CATLMK", [
5964
+ ["18", "LNDMRK18"],
5955
5965
  // Windmill (4-vane)
5956
- 19,
5957
- "LNDMRK19",
5966
+ ["19", "LNDMRK19"],
5958
5967
  // Wind motor / turbine (3-blade)
5959
- 20,
5960
- "BCNTOW01",
5968
+ ["1", "CAIRNS11"],
5969
+ // Cairn
5970
+ ["17", "BCNTOW01"],
5971
+ // Tower
5972
+ ["20", "BCNTOW01"]
5961
5973
  // Spire → tower
5962
- "BCNTOW01"
5963
- // Default: tower
5964
- ];
5974
+ ], "BCNTOW01");
5965
5975
  }
5966
5976
  if (layerName === "DAYMAR") {
5967
5977
  return [
@@ -6101,18 +6111,12 @@ function getIconExpression(layerName) {
6101
6111
  ];
6102
6112
  }
6103
6113
  if (layerName === "LIGHTS") {
6104
- return [
6105
- "match",
6106
- ["coalesce", ["get", "CATLIT"], ["get", "catlit"], 0],
6107
- 8,
6108
- "LIGHTS82",
6109
- // Aero light
6110
- 9,
6111
- "LIGHTS82",
6112
- // Air obstruction light
6113
- "LIGHTS11"
6114
- // Default: major light
6115
- ];
6114
+ return listAttrCase("CATLIT", [
6115
+ ["8", "LIGHTS82"],
6116
+ // Floodlight
6117
+ ["9", "LIGHTS82"]
6118
+ // Strip light
6119
+ ], "LIGHTS11");
6116
6120
  }
6117
6121
  return SYMBOL_MAP[layerName] ?? "";
6118
6122
  }
@@ -6804,6 +6808,11 @@ var LABEL_BEHAVIOR = {
6804
6808
  function getLabelBehavior(layerName) {
6805
6809
  return LABEL_BEHAVIOR[layerName] ?? "feature-band-gated";
6806
6810
  }
6811
+ var LABEL_ANCHOR_FILTER = [
6812
+ "!=",
6813
+ ["get", "_label_anchor"],
6814
+ false
6815
+ ];
6807
6816
  function getLabelConfig(layerName, opts = {}) {
6808
6817
  const ct = opts.colorTable ?? "DAY";
6809
6818
  switch (layerName) {
@@ -7595,6 +7604,18 @@ function preprocessSoundings(fc) {
7595
7604
  }
7596
7605
  return { type: "FeatureCollection", features };
7597
7606
  }
7607
+ function firstListCode(raw) {
7608
+ if (raw == null) return NaN;
7609
+ if (Array.isArray(raw)) return Number(raw[0]);
7610
+ if (typeof raw === "string" && raw.startsWith("[")) {
7611
+ try {
7612
+ const arr = JSON.parse(raw);
7613
+ if (Array.isArray(arr)) return Number(arr[0]);
7614
+ } catch {
7615
+ }
7616
+ }
7617
+ return Number(raw);
7618
+ }
7598
7619
  function preprocessLights(fc) {
7599
7620
  return {
7600
7621
  type: "FeatureCollection",
@@ -7603,7 +7624,7 @@ function preprocessLights(fc) {
7603
7624
  const parts = [];
7604
7625
  const litchr = Number(props.LITCHR);
7605
7626
  if (litchr && LITCHR_ABBR[litchr]) parts.push(LITCHR_ABBR[litchr]);
7606
- const colour = Number(props.COLOUR);
7627
+ const colour = firstListCode(props.COLOUR);
7607
7628
  if (colour && COLOUR_ABBR[colour]) parts.push(COLOUR_ABBR[colour]);
7608
7629
  const sigper = Number(props.SIGPER);
7609
7630
  if (sigper && sigper > 0) parts.push(sigper + "s");
@@ -7677,7 +7698,7 @@ function preprocessNavAids(fc) {
7677
7698
  const match = objnam.match(/(\d+[A-Z]?)\s*$/);
7678
7699
  if (!match) return f;
7679
7700
  const designation = match[1];
7680
- const colourCode = Number(props.COLOUR);
7701
+ const colourCode = firstListCode(props.COLOUR);
7681
7702
  const prefix = NAV_COLOUR_PREFIX[colourCode];
7682
7703
  const label = prefix ? `${prefix} "${designation}"` : `"${designation}"`;
7683
7704
  return {
@@ -9756,23 +9777,30 @@ function buildLayersForClass(objectClass, ct, sourceId = DEFAULT_REGION) {
9756
9777
  }
9757
9778
  if (objectClass === "LNDMRK" && resolved.type === "symbol") {
9758
9779
  const baseFilterParts = layer.filter && layer.filter[0] === "all" ? layer.filter.slice(1) : layer.filter ? [layer.filter] : [];
9759
- const catlmk = ["coalesce", ["get", "CATLMK"], ["get", "catlmk"], 0];
9760
- const isWind = ["any", ["==", catlmk, 18], ["==", catlmk, 19]];
9761
- const isLighthouse = ["==", catlmk, 17];
9780
+ const isWind = listAttrHas("CATLMK", ["18", "19"]);
9781
+ const isLighthouse = listAttrHas("CATLMK", ["17"]);
9762
9782
  const scaminFloor = scaminToMinZoom(entry?.scamin);
9763
9783
  const windLayer = {
9764
9784
  ...layer,
9765
9785
  id: `s52-${objectClass}-wind-symbol`,
9766
- filter: ["all", ...baseFilterParts, isWind]
9786
+ filter: ["all", ...baseFilterParts, isWind],
9787
+ // Explicit, not inherited: turbines share exact coordinates with
9788
+ // their aviation-light LIGHTS feature; without allow-overlap the
9789
+ // turbine icon loses collision to the LIGHTS flare and vanishes.
9790
+ // (The value currently also arrives via the entry-layout
9791
+ // spread-last chain, but that chain is fragile — see s52-lookup
9792
+ // LNDMRK entry — so the intent is pinned here.)
9793
+ layout: { ...layer.layout ?? {}, "icon-allow-overlap": true }
9767
9794
  };
9768
9795
  delete windLayer.minzoom;
9769
9796
  const lighthouseLayer = {
9770
9797
  ...layer,
9771
9798
  id: `s52-${objectClass}-lighthouse-symbol`,
9772
- filter: ["all", ...baseFilterParts, isLighthouse],
9799
+ filter: ["all", ...baseFilterParts, isLighthouse, ["!", isWind]],
9773
9800
  layout: {
9774
9801
  ...layer.layout ?? {},
9775
- "icon-image": "LIGHTHOUSE01"
9802
+ "icon-image": "LIGHTHOUSE01",
9803
+ "icon-allow-overlap": true
9776
9804
  }
9777
9805
  };
9778
9806
  delete lighthouseLayer.minzoom;
@@ -10653,7 +10681,7 @@ function buildLabelLayer2(objectClass, ct, sourceId = DEFAULT_REGION, depthUnits
10653
10681
  delete layer.filter;
10654
10682
  }
10655
10683
  if (getLabelBehavior(objectClass) === "orientation-cross-band") {
10656
- delete layer.filter;
10684
+ layer.filter = LABEL_ANCHOR_FILTER;
10657
10685
  }
10658
10686
  return layer;
10659
10687
  }