@chrtco/sdk 0.10.1 → 0.10.2
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 +87 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -10
- package/dist/index.d.ts +27 -10
- package/dist/index.js +87 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2600,20 +2600,37 @@ declare function worldMinusBboxPolygon(bbox: BBox): GeoJSON.Polygon;
|
|
|
2600
2600
|
* `band-collapse.test.ts` re-proves it on every CI run by differentially
|
|
2601
2601
|
* evaluating legacy-vs-collapsed through MapLibre's real three gates.
|
|
2602
2602
|
*
|
|
2603
|
-
* **Scope.**
|
|
2604
|
-
*
|
|
2605
|
-
*
|
|
2606
|
-
*
|
|
2603
|
+
* **Scope.** For FEATURE layers (fills, lines, feature symbols) this is
|
|
2604
|
+
* axis 1 ONLY: the per-band layers survive, with identical ids and identical
|
|
2605
|
+
* positions, so the band-major draw order is untouched. "Axis 2" (collapsing
|
|
2606
|
+
* the per-band copies into one layer per class) was evaluated and **KILLED**
|
|
2607
|
+
* for feature layers — it is unsound for the opaque-fill classes
|
|
2607
2608
|
* (`DOMINANCE_EXEMPT_FILL_CLASSES` ≡ `OPAQUE_FILL_LAYERS`), where band-major
|
|
2608
2609
|
* order is the ONLY thing separating the bands, and 15/15 class pairs are
|
|
2609
|
-
* provably unsatisfiable with one layer each. Do not reintroduce it.
|
|
2610
|
+
* provably unsatisfiable with one layer each. Do not reintroduce it there.
|
|
2610
2611
|
* Evidence: `.chrt/perf/2026-07-14-p8-d4-spike.md`.
|
|
2611
2612
|
*
|
|
2612
|
-
*
|
|
2613
|
-
*
|
|
2614
|
-
*
|
|
2615
|
-
*
|
|
2616
|
-
*
|
|
2613
|
+
* LABEL layers are different (CHRTsail #121 — MapLibre Native iOS silently
|
|
2614
|
+
* collapses its whole render pass above 482 symbol layers, and the served
|
|
2615
|
+
* style carried 575): `makeCollapsedCrossBandLabel` folds a label's ×6 band
|
|
2616
|
+
* fan into ONE layer per class, and `collapsedOrientationGate` folds the
|
|
2617
|
+
* orientation fan's per-dominator variants into its per-cell layers. That is
|
|
2618
|
+
* NOT the killed axis 2, because what the fill argument protects — paint
|
|
2619
|
+
* order as the sole band separator — does not apply to labels: their
|
|
2620
|
+
* load-bearing order is the cross-class P4 priority ladder (each class keeps
|
|
2621
|
+
* exactly one layer in its ladder slot), and within-class placement priority
|
|
2622
|
+
* rides the `rank*1000 + within-key` symbol-sort-key that labels.ts
|
|
2623
|
+
* documents as designed to survive precisely this merge.
|
|
2624
|
+
*
|
|
2625
|
+
* **Load-bearing invariant.** A collapsed per-band FEATURE layer MUST keep
|
|
2626
|
+
* the `-band{B}` id segment: `custom-style.ts` `bandFloor()` parses
|
|
2627
|
+
* `/-band(\d)/` to clamp customer minzoom/scamin overrides to the band's
|
|
2628
|
+
* data floor, on the live public Style API path. Renaming the id silently
|
|
2629
|
+
* unclamps those overrides. Cross-band LABEL layers are the deliberate
|
|
2630
|
+
* exception: they carry every band floor INSIDE the filter (tile-zoom
|
|
2631
|
+
* clauses a customer override cannot touch), which supersedes the clamp —
|
|
2632
|
+
* `bandFloor()` returning 0 for a bare label id is correct, not a gap.
|
|
2633
|
+
* Both halves asserted in `band-collapse.test.ts`.
|
|
2617
2634
|
*/
|
|
2618
2635
|
|
|
2619
2636
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -2600,20 +2600,37 @@ declare function worldMinusBboxPolygon(bbox: BBox): GeoJSON.Polygon;
|
|
|
2600
2600
|
* `band-collapse.test.ts` re-proves it on every CI run by differentially
|
|
2601
2601
|
* evaluating legacy-vs-collapsed through MapLibre's real three gates.
|
|
2602
2602
|
*
|
|
2603
|
-
* **Scope.**
|
|
2604
|
-
*
|
|
2605
|
-
*
|
|
2606
|
-
*
|
|
2603
|
+
* **Scope.** For FEATURE layers (fills, lines, feature symbols) this is
|
|
2604
|
+
* axis 1 ONLY: the per-band layers survive, with identical ids and identical
|
|
2605
|
+
* positions, so the band-major draw order is untouched. "Axis 2" (collapsing
|
|
2606
|
+
* the per-band copies into one layer per class) was evaluated and **KILLED**
|
|
2607
|
+
* for feature layers — it is unsound for the opaque-fill classes
|
|
2607
2608
|
* (`DOMINANCE_EXEMPT_FILL_CLASSES` ≡ `OPAQUE_FILL_LAYERS`), where band-major
|
|
2608
2609
|
* order is the ONLY thing separating the bands, and 15/15 class pairs are
|
|
2609
|
-
* provably unsatisfiable with one layer each. Do not reintroduce it.
|
|
2610
|
+
* provably unsatisfiable with one layer each. Do not reintroduce it there.
|
|
2610
2611
|
* Evidence: `.chrt/perf/2026-07-14-p8-d4-spike.md`.
|
|
2611
2612
|
*
|
|
2612
|
-
*
|
|
2613
|
-
*
|
|
2614
|
-
*
|
|
2615
|
-
*
|
|
2616
|
-
*
|
|
2613
|
+
* LABEL layers are different (CHRTsail #121 — MapLibre Native iOS silently
|
|
2614
|
+
* collapses its whole render pass above 482 symbol layers, and the served
|
|
2615
|
+
* style carried 575): `makeCollapsedCrossBandLabel` folds a label's ×6 band
|
|
2616
|
+
* fan into ONE layer per class, and `collapsedOrientationGate` folds the
|
|
2617
|
+
* orientation fan's per-dominator variants into its per-cell layers. That is
|
|
2618
|
+
* NOT the killed axis 2, because what the fill argument protects — paint
|
|
2619
|
+
* order as the sole band separator — does not apply to labels: their
|
|
2620
|
+
* load-bearing order is the cross-class P4 priority ladder (each class keeps
|
|
2621
|
+
* exactly one layer in its ladder slot), and within-class placement priority
|
|
2622
|
+
* rides the `rank*1000 + within-key` symbol-sort-key that labels.ts
|
|
2623
|
+
* documents as designed to survive precisely this merge.
|
|
2624
|
+
*
|
|
2625
|
+
* **Load-bearing invariant.** A collapsed per-band FEATURE layer MUST keep
|
|
2626
|
+
* the `-band{B}` id segment: `custom-style.ts` `bandFloor()` parses
|
|
2627
|
+
* `/-band(\d)/` to clamp customer minzoom/scamin overrides to the band's
|
|
2628
|
+
* data floor, on the live public Style API path. Renaming the id silently
|
|
2629
|
+
* unclamps those overrides. Cross-band LABEL layers are the deliberate
|
|
2630
|
+
* exception: they carry every band floor INSIDE the filter (tile-zoom
|
|
2631
|
+
* clauses a customer override cannot touch), which supersedes the clamp —
|
|
2632
|
+
* `bandFloor()` returning 0 for a bare label id is correct, not a gap.
|
|
2633
|
+
* Both halves asserted in `band-collapse.test.ts`.
|
|
2617
2634
|
*/
|
|
2618
2635
|
|
|
2619
2636
|
/**
|
package/dist/index.js
CHANGED
|
@@ -27282,6 +27282,34 @@ function makeCollapsedBandVariant(layer, B, isExempt) {
|
|
|
27282
27282
|
delete collapsed.maxzoom;
|
|
27283
27283
|
return [collapsed];
|
|
27284
27284
|
}
|
|
27285
|
+
function collapsedOrientationGate(minzoom) {
|
|
27286
|
+
const clauses = [NOT_DOMINATED_FILTER];
|
|
27287
|
+
for (const D of BAND_DRAW_ORDER) {
|
|
27288
|
+
const dMaxzoom = dominatedVariantMaxzoom(D);
|
|
27289
|
+
if (dMaxzoom <= minzoom) continue;
|
|
27290
|
+
clauses.push(["all", dominatedByMinFilter(D), ["<", ["zoom"], dMaxzoom]]);
|
|
27291
|
+
}
|
|
27292
|
+
return ["any", ...clauses];
|
|
27293
|
+
}
|
|
27294
|
+
function makeCollapsedCrossBandLabel(label, isExempt) {
|
|
27295
|
+
const classMinzoom = label.minzoom ?? 0;
|
|
27296
|
+
const clauses = [];
|
|
27297
|
+
for (const B of BAND_DRAW_ORDER) {
|
|
27298
|
+
const floor = BAND_PRIORITY_RANGES[B].minzoom;
|
|
27299
|
+
const parts = [bandFilterExpression(B)];
|
|
27300
|
+
if (floor > 0) parts.push([">=", ["zoom"], floor]);
|
|
27301
|
+
if (!isExempt) {
|
|
27302
|
+
parts.push(collapsedDominanceGate(B, Math.max(classMinzoom, floor)));
|
|
27303
|
+
}
|
|
27304
|
+
clauses.push(parts.length === 1 ? parts[0] : ["all", ...parts]);
|
|
27305
|
+
}
|
|
27306
|
+
const collapsed = {
|
|
27307
|
+
...label,
|
|
27308
|
+
filter: combineWithBandFilter(label.filter, ["any", ...clauses])
|
|
27309
|
+
};
|
|
27310
|
+
delete collapsed.maxzoom;
|
|
27311
|
+
return collapsed;
|
|
27312
|
+
}
|
|
27285
27313
|
|
|
27286
27314
|
// src/atlas/style-generator.ts
|
|
27287
27315
|
var META_LAYERS = /* @__PURE__ */ new Set([
|
|
@@ -27433,7 +27461,7 @@ var CELL_BAND_OPACITY_CURVE = {
|
|
|
27433
27461
|
"5": [[11, 0.6], [13, 0.35], [18, 0.25]],
|
|
27434
27462
|
"6": [[13, 0.75], [16, 0.55], [22, 0.45]]
|
|
27435
27463
|
};
|
|
27436
|
-
function fanOrientationLabelByCellBand(label) {
|
|
27464
|
+
function fanOrientationLabelByCellBand(label, bandFan) {
|
|
27437
27465
|
const cellBandExpr = [
|
|
27438
27466
|
"slice",
|
|
27439
27467
|
["coalesce", ["get", "_cell_id"], "??9???"],
|
|
@@ -27458,6 +27486,16 @@ function fanOrientationLabelByCellBand(label) {
|
|
|
27458
27486
|
...label.paint ?? {},
|
|
27459
27487
|
"text-opacity": opacityExpr
|
|
27460
27488
|
};
|
|
27489
|
+
if (bandFan === "collapsed") {
|
|
27490
|
+
variants.push({
|
|
27491
|
+
...label,
|
|
27492
|
+
id: `${label.id}-cell${code}-notdom`,
|
|
27493
|
+
filter: combineWithBandFilter(cellFiltered, collapsedOrientationGate(minzoom)),
|
|
27494
|
+
minzoom,
|
|
27495
|
+
paint
|
|
27496
|
+
});
|
|
27497
|
+
continue;
|
|
27498
|
+
}
|
|
27461
27499
|
variants.push({
|
|
27462
27500
|
...label,
|
|
27463
27501
|
id: `${label.id}-cell${code}-notdom`,
|
|
@@ -27487,18 +27525,50 @@ function byLabelPriorityBottomUp(bundles) {
|
|
|
27487
27525
|
(a, b) => getLabelPriority(b.objectClass) - getLabelPriority(a.objectClass)
|
|
27488
27526
|
);
|
|
27489
27527
|
}
|
|
27490
|
-
function
|
|
27491
|
-
|
|
27528
|
+
function chartModeBandClauses(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
|
|
27529
|
+
const parts = [bandFilterExpression(B)];
|
|
27492
27530
|
if (!isDominanceExempt(layer)) {
|
|
27493
27531
|
for (const D of higherCascadeBands) {
|
|
27494
|
-
|
|
27532
|
+
parts.push(["!", dominatedByMinFilter(D)]);
|
|
27495
27533
|
}
|
|
27496
27534
|
}
|
|
27497
27535
|
if (lockedBounds && isRequestedBand) {
|
|
27498
|
-
|
|
27536
|
+
parts.push(withinBboxFilter(lockedBounds));
|
|
27537
|
+
}
|
|
27538
|
+
return parts;
|
|
27539
|
+
}
|
|
27540
|
+
function chartModeFilterForBand(layer, B, higherCascadeBands, lockedBounds, isRequestedBand) {
|
|
27541
|
+
const [first, ...rest] = chartModeBandClauses(
|
|
27542
|
+
layer,
|
|
27543
|
+
B,
|
|
27544
|
+
higherCascadeBands,
|
|
27545
|
+
lockedBounds,
|
|
27546
|
+
isRequestedBand
|
|
27547
|
+
);
|
|
27548
|
+
let filter = combineWithBandFilter(layer.filter, first);
|
|
27549
|
+
for (const part of rest) {
|
|
27550
|
+
filter = combineWithBandFilter(filter, part);
|
|
27499
27551
|
}
|
|
27500
27552
|
return filter;
|
|
27501
27553
|
}
|
|
27554
|
+
function makeCollapsedChartLabel(label, cascade, requestedSet, lockedBounds) {
|
|
27555
|
+
const clauses = cascade.map((B, i) => {
|
|
27556
|
+
const parts = chartModeBandClauses(
|
|
27557
|
+
label,
|
|
27558
|
+
B,
|
|
27559
|
+
cascade.slice(i + 1),
|
|
27560
|
+
lockedBounds,
|
|
27561
|
+
requestedSet.has(B)
|
|
27562
|
+
);
|
|
27563
|
+
return parts.length === 1 ? parts[0] : ["all", ...parts];
|
|
27564
|
+
});
|
|
27565
|
+
const collapsed = {
|
|
27566
|
+
...label,
|
|
27567
|
+
filter: combineWithBandFilter(label.filter, ["any", ...clauses])
|
|
27568
|
+
};
|
|
27569
|
+
delete collapsed.maxzoom;
|
|
27570
|
+
return collapsed;
|
|
27571
|
+
}
|
|
27502
27572
|
function generateStyle(palette = "DAY", opts = {}) {
|
|
27503
27573
|
const sources = buildSources(opts);
|
|
27504
27574
|
const sourceId = opts.region ?? DEFAULT_REGION;
|
|
@@ -27514,6 +27584,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
27514
27584
|
const labelLayers = [];
|
|
27515
27585
|
const chartCascade = opts.chartMode ? CHART_CASCADE_CODES[opts.chartMode.band] : null;
|
|
27516
27586
|
const chartRequestedCodes = opts.chartMode ? CHART_BAND_CODES[opts.chartMode.band] : null;
|
|
27587
|
+
const bandFan = opts.bandFan ?? "collapsed";
|
|
27517
27588
|
const lockedBounds = opts.chartMode?.lockedBounds;
|
|
27518
27589
|
const bundles = [];
|
|
27519
27590
|
for (const objectClass of orderedClasses) {
|
|
@@ -27567,6 +27638,12 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
27567
27638
|
)
|
|
27568
27639
|
);
|
|
27569
27640
|
for (const { label } of chartLabelBundles) {
|
|
27641
|
+
if (bandFan === "collapsed") {
|
|
27642
|
+
labelLayers.push(
|
|
27643
|
+
makeCollapsedChartLabel(label, chartCascade, requestedSet, lockedBounds)
|
|
27644
|
+
);
|
|
27645
|
+
continue;
|
|
27646
|
+
}
|
|
27570
27647
|
for (let i = 0; i < chartCascade.length; i++) {
|
|
27571
27648
|
const B = chartCascade[i];
|
|
27572
27649
|
const higher = chartCascade.slice(i + 1);
|
|
@@ -27589,7 +27666,6 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
27589
27666
|
}
|
|
27590
27667
|
layers.push(...labelLayers);
|
|
27591
27668
|
} else {
|
|
27592
|
-
const bandFan = opts.bandFan ?? "collapsed";
|
|
27593
27669
|
const emitBand = (base, code) => bandFan === "legacy" ? makeBandVariants(base, code) : makeCollapsedBandVariant(base, code, isDominanceExempt(base));
|
|
27594
27670
|
for (const code of BAND_DRAW_ORDER) {
|
|
27595
27671
|
for (const { featureLayers } of regularBundles) {
|
|
@@ -27608,7 +27684,7 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
27608
27684
|
for (const { objectClass, label } of bundles) {
|
|
27609
27685
|
if (!label) continue;
|
|
27610
27686
|
if (getLabelBehavior(objectClass) === "orientation-cross-band") {
|
|
27611
|
-
labelLayers.push(...fanOrientationLabelByCellBand(label));
|
|
27687
|
+
labelLayers.push(...fanOrientationLabelByCellBand(label, bandFan));
|
|
27612
27688
|
}
|
|
27613
27689
|
}
|
|
27614
27690
|
const featureLabelBundles = byLabelPriorityBottomUp(
|
|
@@ -27617,6 +27693,10 @@ function generateStyle(palette = "DAY", opts = {}) {
|
|
|
27617
27693
|
)
|
|
27618
27694
|
);
|
|
27619
27695
|
for (const { label } of featureLabelBundles) {
|
|
27696
|
+
if (bandFan === "collapsed") {
|
|
27697
|
+
labelLayers.push(makeCollapsedCrossBandLabel(label, isDominanceExempt(label)));
|
|
27698
|
+
continue;
|
|
27699
|
+
}
|
|
27620
27700
|
for (const code of BAND_DRAW_ORDER) {
|
|
27621
27701
|
labelLayers.push(...emitBand(label, code));
|
|
27622
27702
|
}
|