@chrtco/sdk 0.10.25 → 0.10.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/index.cjs +100 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -6
- package/dist/index.d.ts +57 -6
- package/dist/index.js +98 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to `@chrtco/sdk` will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Sector-light arcs were drawn on the wrong side of the light.**
|
|
9
|
+
`buildSectorArcGeoJSON()` / `buildSectorArcsFC()` painted each sector at the
|
|
10
|
+
raw `SECTR1`/`SECTR2` bearing measured from the light. S-57 charts those
|
|
11
|
+
limits as bearings FROM SEAWARD (the light as seen from the vessel), so every
|
|
12
|
+
arc — and its limit ticks — landed on the reciprocal side: a Maine light
|
|
13
|
+
charted white 000°–180° / red 180°–000° showed white over the island and red
|
|
14
|
+
over the channel. Both builders now apply the 180° reciprocal (new export
|
|
15
|
+
`sectorLimitToArcBearing()`); bearing labels still print the charted value.
|
|
16
|
+
`SectorInfo.sectorStart`/`sectorEnd` are documented as charted from-seaward
|
|
17
|
+
values and are unchanged.
|
|
18
|
+
- **All-round lights co-located with sector lights were not drawn.**
|
|
19
|
+
`buildSectorsFromFeatures()` skipped any `LIGHTS` feature without
|
|
20
|
+
`SECTR1`/`SECTR2`, so a light like Isle au Haut (`Fl R 4s` all-round plus a
|
|
21
|
+
white 034°–060° sector) showed the white arc and no red at all. Per S-52
|
|
22
|
+
LIGHTS05 the all-round light is now emitted over the arc its sectored
|
|
23
|
+
siblings leave uncovered (new export `uncoveredSectorArcs()`; entries carry
|
|
24
|
+
`SectorInfo.allRound: true`). A lone all-round light still adds no arc.
|
|
25
|
+
|
|
7
26
|
### Added
|
|
8
27
|
- **`StyleGeneratorOptions.labels`** — `"legacy"` (default) or `"placename"`.
|
|
9
28
|
`"placename"` swaps the 9 orientation classes' 54-layer per-band label fan
|
package/dist/index.cjs
CHANGED
|
@@ -213,8 +213,10 @@ __export(index_exports, {
|
|
|
213
213
|
sanitizeCustomStyleConfig: () => sanitizeCustomStyleConfig,
|
|
214
214
|
scaleSizeValue: () => scaleSizeValue,
|
|
215
215
|
scaminToMinZoom: () => scaminToMinZoom,
|
|
216
|
+
sectorLimitToArcBearing: () => sectorLimitToArcBearing,
|
|
216
217
|
styleModeBand: () => styleModeBand,
|
|
217
218
|
tileTemplate: () => tileTemplate,
|
|
219
|
+
uncoveredSectorArcs: () => uncoveredSectorArcs,
|
|
218
220
|
withinBboxFilter: () => withinBboxFilter,
|
|
219
221
|
worldMinusBboxPolygon: () => worldMinusBboxPolygon
|
|
220
222
|
});
|
|
@@ -3388,13 +3390,54 @@ function buildFlashPattern(decoded) {
|
|
|
3388
3390
|
const totalCycle = phases.reduce((sum, p) => sum + p.duration, 0);
|
|
3389
3391
|
return { phases, totalCycle, notation };
|
|
3390
3392
|
}
|
|
3393
|
+
function uncoveredSectorArcs(sectors) {
|
|
3394
|
+
const norm = (d) => (d % 360 + 360) % 360;
|
|
3395
|
+
const flat = [];
|
|
3396
|
+
for (const sec of sectors) {
|
|
3397
|
+
const a = norm(sec.sectorStart);
|
|
3398
|
+
let sweep = norm(sec.sectorEnd) - a;
|
|
3399
|
+
if (sweep <= 0) sweep += 360;
|
|
3400
|
+
if (sweep >= 360) return [];
|
|
3401
|
+
const b = a + sweep;
|
|
3402
|
+
if (b <= 360) flat.push([a, b]);
|
|
3403
|
+
else {
|
|
3404
|
+
flat.push([a, 360]);
|
|
3405
|
+
flat.push([0, b - 360]);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (flat.length === 0) return [{ start: 0, end: 0 }];
|
|
3409
|
+
flat.sort((x, y) => x[0] - y[0]);
|
|
3410
|
+
const merged = [];
|
|
3411
|
+
for (const [a, b] of flat) {
|
|
3412
|
+
const last = merged[merged.length - 1];
|
|
3413
|
+
if (last && a <= last[1]) last[1] = Math.max(last[1], b);
|
|
3414
|
+
else merged.push([a, b]);
|
|
3415
|
+
}
|
|
3416
|
+
const gaps = [];
|
|
3417
|
+
let cursor = 0;
|
|
3418
|
+
for (const [a, b] of merged) {
|
|
3419
|
+
if (a > cursor) gaps.push([cursor, a]);
|
|
3420
|
+
cursor = Math.max(cursor, b);
|
|
3421
|
+
}
|
|
3422
|
+
if (cursor < 360) gaps.push([cursor, 360]);
|
|
3423
|
+
if (gaps.length >= 2 && gaps[0][0] === 0 && gaps[gaps.length - 1][1] === 360) {
|
|
3424
|
+
const first = gaps.shift();
|
|
3425
|
+
const last = gaps.pop();
|
|
3426
|
+
gaps.push([last[0], first[1]]);
|
|
3427
|
+
}
|
|
3428
|
+
return gaps.filter(([a, b]) => Math.abs(b - a) >= 0.05 || b < a).map(([a, b]) => ({ start: norm(a), end: norm(b) }));
|
|
3429
|
+
}
|
|
3391
3430
|
function buildSectorsFromFeatures(features) {
|
|
3392
3431
|
const sectors = [];
|
|
3432
|
+
const allRound = [];
|
|
3393
3433
|
for (const f of features) {
|
|
3394
3434
|
const props = f.properties ?? {};
|
|
3395
3435
|
const s1 = props.SECTR1 != null ? Number(props.SECTR1) : null;
|
|
3396
3436
|
const s2 = props.SECTR2 != null ? Number(props.SECTR2) : null;
|
|
3397
|
-
if (s1 == null || s2 == null || isNaN(s1) || isNaN(s2))
|
|
3437
|
+
if (s1 == null || s2 == null || isNaN(s1) || isNaN(s2)) {
|
|
3438
|
+
if (props.SECTR1 == null && props.SECTR2 == null && parseCode(props.COLOUR) != null) allRound.push(props);
|
|
3439
|
+
continue;
|
|
3440
|
+
}
|
|
3398
3441
|
const colourCode = parseCode(props.COLOUR);
|
|
3399
3442
|
const ce = colourCode ? COLOUR_TABLE[colourCode] : null;
|
|
3400
3443
|
const litchrCode = parseCode(props.LITCHR);
|
|
@@ -3413,15 +3456,44 @@ function buildSectorsFromFeatures(features) {
|
|
|
3413
3456
|
nominalRange
|
|
3414
3457
|
});
|
|
3415
3458
|
}
|
|
3459
|
+
if (sectors.length > 0 && allRound.length > 0) {
|
|
3460
|
+
const gaps = uncoveredSectorArcs(sectors);
|
|
3461
|
+
for (const props of allRound) {
|
|
3462
|
+
const colourCode = parseCode(props.COLOUR);
|
|
3463
|
+
const ce = colourCode ? COLOUR_TABLE[colourCode] : null;
|
|
3464
|
+
if (!ce) continue;
|
|
3465
|
+
const litchrCode = parseCode(props.LITCHR);
|
|
3466
|
+
const le = litchrCode ? LITCHR_TABLE[litchrCode] : null;
|
|
3467
|
+
const sigper = props.SIGPER != null ? Number(props.SIGPER) : null;
|
|
3468
|
+
const group = parseGroup(props.SIGGRP);
|
|
3469
|
+
const notation = buildNotation(le?.abbr ?? null, [ce.abbr], sigper, group);
|
|
3470
|
+
const valnmr = props.VALNMR != null ? Number(props.VALNMR) : null;
|
|
3471
|
+
const nominalRange = valnmr != null && !isNaN(valnmr) && valnmr > 0 ? valnmr : null;
|
|
3472
|
+
for (const gap of gaps) {
|
|
3473
|
+
sectors.push({
|
|
3474
|
+
sectorStart: gap.start,
|
|
3475
|
+
sectorEnd: gap.end,
|
|
3476
|
+
colour: ce.name,
|
|
3477
|
+
colourHex: ce.hex,
|
|
3478
|
+
notation,
|
|
3479
|
+
nominalRange,
|
|
3480
|
+
allRound: true
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3416
3485
|
return sectors;
|
|
3417
3486
|
}
|
|
3487
|
+
function sectorLimitToArcBearing(limitDegFromSeaward) {
|
|
3488
|
+
return ((limitDegFromSeaward + 180) % 360 + 360) % 360;
|
|
3489
|
+
}
|
|
3418
3490
|
function buildSectorArcGeoJSON(center, startBearing, endBearing, radiusNm = 0.5, innerRadiusNm = 0, steps = 64) {
|
|
3419
3491
|
const radiusDeg = radiusNm / 60;
|
|
3420
3492
|
const innerDeg = innerRadiusNm > 0 ? innerRadiusNm / 60 : 0;
|
|
3421
3493
|
const [lng, lat] = center;
|
|
3422
3494
|
const cosLat = Math.cos(lat * Math.PI / 180);
|
|
3423
|
-
const s = (startBearing
|
|
3424
|
-
const e = (endBearing
|
|
3495
|
+
const s = sectorLimitToArcBearing(startBearing);
|
|
3496
|
+
const e = sectorLimitToArcBearing(endBearing);
|
|
3425
3497
|
let sweep = e - s;
|
|
3426
3498
|
if (sweep <= 0) sweep += 360;
|
|
3427
3499
|
const project = (bearingDeg, rDeg) => {
|
|
@@ -3476,7 +3548,7 @@ function buildSectorArcsFC(center, sectors, radiusNm = 0.4, innerRadiusFrac = 0.
|
|
|
3476
3548
|
const TICK_INNER_OVERHANG = 0.93;
|
|
3477
3549
|
for (const sec of sectors) {
|
|
3478
3550
|
for (const bearing of [sec.sectorStart, sec.sectorEnd]) {
|
|
3479
|
-
const rad = bearing * Math.PI / 180;
|
|
3551
|
+
const rad = sectorLimitToArcBearing(bearing) * Math.PI / 180;
|
|
3480
3552
|
const radiusDeg = radiusNm / 60;
|
|
3481
3553
|
const innerRadiusDeg = innerNm > 0 ? innerNm / 60 : 0;
|
|
3482
3554
|
const cosLat = Math.cos(center[1] * Math.PI / 180);
|
|
@@ -28637,6 +28709,9 @@ function bandVariantMinzoom(layer, B) {
|
|
|
28637
28709
|
if (isExemptOpaqueFill(layer) && FILL_FLOOR_FREE_BANDS.has(B)) {
|
|
28638
28710
|
return classMinzoom;
|
|
28639
28711
|
}
|
|
28712
|
+
if (isExemptOpaqueFill(layer)) {
|
|
28713
|
+
return BAND_PRIORITY_RANGES[B].minzoom;
|
|
28714
|
+
}
|
|
28640
28715
|
return Math.max(classMinzoom, BAND_PRIORITY_RANGES[B].minzoom);
|
|
28641
28716
|
}
|
|
28642
28717
|
var GENERALISED_AREA_FILL_CLASSES = /* @__PURE__ */ new Set([
|
|
@@ -30037,7 +30112,9 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30037
30112
|
}
|
|
30038
30113
|
}
|
|
30039
30114
|
{
|
|
30040
|
-
const TERMINAL_FILL_RE =
|
|
30115
|
+
const TERMINAL_FILL_RE = new RegExp(
|
|
30116
|
+
`^s52-(?:${[...DOMINANCE_EXEMPT_FILL_CLASSES].join("|")})-fill-band[456]$`
|
|
30117
|
+
);
|
|
30041
30118
|
for (const l of layers) {
|
|
30042
30119
|
const lay = l;
|
|
30043
30120
|
if (!TERMINAL_FILL_RE.test(lay.id)) continue;
|
|
@@ -30175,7 +30252,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30175
30252
|
)) {
|
|
30176
30253
|
const src = l;
|
|
30177
30254
|
const landClone = {
|
|
30178
|
-
...l,
|
|
30255
|
+
...asBaseTan(l),
|
|
30179
30256
|
id: `${src.id}-overpair`,
|
|
30180
30257
|
metadata: { ...src.metadata ?? {}, "chrt:role": "charted-land-repaint" }
|
|
30181
30258
|
};
|
|
@@ -30211,6 +30288,21 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
30211
30288
|
...restAbove.slice(pairAt + 1)
|
|
30212
30289
|
];
|
|
30213
30290
|
}
|
|
30291
|
+
{
|
|
30292
|
+
const hsAt = restFinal.findIndex(
|
|
30293
|
+
(l) => l.type === "hillshade"
|
|
30294
|
+
);
|
|
30295
|
+
let lastRepaint = -1;
|
|
30296
|
+
restFinal.forEach((l, i) => {
|
|
30297
|
+
if (/-overpair(-|$)/.test(l.id)) lastRepaint = i;
|
|
30298
|
+
});
|
|
30299
|
+
if (hsAt !== -1 && lastRepaint > hsAt) {
|
|
30300
|
+
const reordered = [...restFinal];
|
|
30301
|
+
const [hs] = reordered.splice(hsAt, 1);
|
|
30302
|
+
reordered.splice(lastRepaint, 0, hs);
|
|
30303
|
+
restFinal = reordered;
|
|
30304
|
+
}
|
|
30305
|
+
}
|
|
30214
30306
|
return [restFinal[0], ...baseBlock, ...seaare, ...ordered, ...restFinal.slice(1)];
|
|
30215
30307
|
})() : layers;
|
|
30216
30308
|
for (const id of [
|
|
@@ -31292,8 +31384,10 @@ var Chrt = class {
|
|
|
31292
31384
|
sanitizeCustomStyleConfig,
|
|
31293
31385
|
scaleSizeValue,
|
|
31294
31386
|
scaminToMinZoom,
|
|
31387
|
+
sectorLimitToArcBearing,
|
|
31295
31388
|
styleModeBand,
|
|
31296
31389
|
tileTemplate,
|
|
31390
|
+
uncoveredSectorArcs,
|
|
31297
31391
|
withinBboxFilter,
|
|
31298
31392
|
worldMinusBboxPolygon
|
|
31299
31393
|
});
|