@nika-js/onlymap 0.7.5 → 0.8.0

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -2
  3. package/dist/{LercDecode.es-DYwn8YTS.js → LercDecode.es--DH6_OFE.js} +1 -1
  4. package/dist/{basemap-CtEc5BCi.js → basemap-2ZZ_9gFn.js} +528 -502
  5. package/dist/basemap.d.ts +15 -0
  6. package/dist/cartograph/api.d.ts +45 -0
  7. package/dist/cartograph/atlas.d.ts +50 -0
  8. package/dist/cartograph/core-loader.d.ts +35 -0
  9. package/dist/cartograph/cvd.d.ts +106 -0
  10. package/dist/cartograph/element-base.d.ts +26 -0
  11. package/dist/cartograph/elements/om-atlas.d.ts +47 -0
  12. package/dist/cartograph/elements/om-cartograph.d.ts +76 -0
  13. package/dist/cartograph/elements/om-frame.d.ts +111 -0
  14. package/dist/cartograph/elements/om-graticule.d.ts +24 -0
  15. package/dist/cartograph/elements/om-image.d.ts +12 -0
  16. package/dist/cartograph/elements/om-legend.d.ts +16 -0
  17. package/dist/cartograph/elements/om-north.d.ts +11 -0
  18. package/dist/cartograph/elements/om-scalebar.d.ts +13 -0
  19. package/dist/cartograph/elements/om-shape.d.ts +16 -0
  20. package/dist/cartograph/elements/om-text.d.ts +9 -0
  21. package/dist/cartograph/georef.d.ts +126 -0
  22. package/dist/cartograph/graticule.d.ts +70 -0
  23. package/dist/cartograph/html-data.d.ts +27 -0
  24. package/dist/cartograph/index.d.ts +1 -0
  25. package/dist/cartograph/layout.d.ts +23 -0
  26. package/dist/cartograph/legend.d.ts +75 -0
  27. package/dist/cartograph/live-frame.d.ts +69 -0
  28. package/dist/cartograph/paint.d.ts +54 -0
  29. package/dist/cartograph/refs.d.ts +33 -0
  30. package/dist/cartograph/render.d.ts +64 -0
  31. package/dist/cartograph/scalebar.d.ts +91 -0
  32. package/dist/cartograph/schema.d.ts +38 -0
  33. package/dist/cartograph/standalone.d.ts +1 -0
  34. package/dist/cartograph/textlayout.d.ts +45 -0
  35. package/dist/cartograph/tokens.d.ts +45 -0
  36. package/dist/cartograph/url-actions.d.ts +26 -0
  37. package/dist/cartograph/validate.d.ts +38 -0
  38. package/dist/cartograph/zip.d.ts +22 -0
  39. package/dist/cartograph.css +1 -0
  40. package/dist/cartograph.js +2768 -0
  41. package/dist/cartograph.standalone.js +7640 -0
  42. package/dist/crs-DsDQ4Q4i.js +71 -0
  43. package/dist/download.d.ts +6 -0
  44. package/dist/feature-access.d.ts +14 -0
  45. package/dist/field-access.d.ts +1 -10
  46. package/dist/{geoparquet-RBC0ZJNm.js → geoparquet-DDZzee5Z.js} +1 -1
  47. package/dist/{index-DkimKgOm.js → index-BGOblzWN.js} +5141 -5070
  48. package/dist/index-CcLC9jE5.js +4798 -0
  49. package/dist/{index-CiGUw6GD.js → index-CeoVzd6t.js} +1 -1
  50. package/dist/{index-CJ0pib8I.js → index-Cz1VsWzO.js} +2 -2
  51. package/dist/{index-Bwu1DFHP.js → index-DkxFFflv.js} +1 -1
  52. package/dist/{index-HrEJtZoQ.js → index-DphcuoPv.js} +1 -1
  53. package/dist/index.d.ts +1 -1
  54. package/dist/{lerc-BIQMg0FV.js → lerc-D7TfTy6l.js} +2 -2
  55. package/dist/onlymap.standalone.js +8827 -8730
  56. package/dist/onlymapjs.js +12 -11
  57. package/dist/{raster-Dsugvobw.js → raster-D_3YKFZz.js} +3 -3
  58. package/dist/{raster-pipeline-rqJJRCEo.js → raster-pipeline-D1yeoJo2.js} +1 -1
  59. package/dist/runtime-core.d.ts +30 -0
  60. package/dist/snapshot.d.ts +17 -0
  61. package/dist/units.d.ts +9 -0
  62. package/dist/version.d.ts +1 -1
  63. package/dist/{zarr-CRysSFhD.js → zarr-DH5Ntt5U.js} +2 -2
  64. package/docs/cartograph.md +396 -0
  65. package/llms.txt +2 -1
  66. package/onlymapjs.attributes.json +1 -0
  67. package/onlymapjs.html-data.json +974 -0
  68. package/package.json +13 -2
  69. package/skills/onlymapjs/SKILL.md +2 -2
  70. package/skills/onlymapjs/references/syntax.md +28 -2
package/dist/basemap.d.ts CHANGED
@@ -103,6 +103,21 @@ export declare class MapLibreBasemapAdapter {
103
103
  * fire without one. The ecosystem's standard user-vs-programmatic signal.
104
104
  */
105
105
  onMove(cb: (origin: ViewOrigin) => void): void;
106
+ /** Set while a capture-resolution override resizes the canvases — see onMove. */
107
+ private moveSuppressed;
108
+ /**
109
+ * Re-rasterize both canvases at `ratio` captured pixels per CSS pixel, or
110
+ * restore the display default with null (spec: "Snapshot API" — the
111
+ * `snapshot({scale})` seam). MapLibre's own `setPixelRatio` drives the
112
+ * basemap canvas; the overlay's deck takes the same number through
113
+ * `useDevicePixels`, which luma applies to the drawing buffer immediately.
114
+ *
115
+ * The idle latch is cleared deliberately: the caller must re-await
116
+ * `whenSettled()` before capturing, because a re-rasterized basemap has
117
+ * tiles and glyphs to redraw and `map.once("render")` alone would capture
118
+ * the first, unconverged frame.
119
+ */
120
+ setCapturePixelRatio(ratio: number | null): void;
106
121
  setLayers(layers: DeckProps["layers"]): void;
107
122
  /**
108
123
  * Scene lighting (spec: "Scene Lighting") — MapboxOverlay accepts full
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The cartograph API surface — elements, geometry, validation, export.
3
+ *
4
+ * Shared by BOTH build entries. Deliberately contains no core-loader
5
+ * registration: the import expression that reaches the map runtime differs
6
+ * per entry (package name vs relative URL) and lives in index.ts /
7
+ * standalone.ts. Importing this module registers the elements and pulls the
8
+ * stylesheet.
9
+ */
10
+ import "./cartograph.css";
11
+ export { OmCartographElement } from "./elements/om-cartograph";
12
+ export { OmTextElement } from "./elements/om-text";
13
+ export { OmFrameElement, type FrameMode, type FrameErrorReason } from "./elements/om-frame";
14
+ export { OmScalebarElement } from "./elements/om-scalebar";
15
+ export { OmNorthElement } from "./elements/om-north";
16
+ export { OmShapeElement, type ShapeKind } from "./elements/om-shape";
17
+ export { OmImageElement } from "./elements/om-image";
18
+ export { OmLegendElement } from "./elements/om-legend";
19
+ export { OmGraticuleElement } from "./elements/om-graticule";
20
+ export { OmAtlasElement } from "./elements/om-atlas";
21
+ export { CARTOGRAPH_FORMAT, CARTOGRAPH_THEMES, CARTOGRAPH_TAG_NAMES, CARTOGRAPH_ELEMENT_ATTRS, INERT_RESERVED_CARTOGRAPH_ATTRS, PAGE_SIZE_NAMES, PLACED_ATTR_NAMES, } from "./schema";
22
+ export { PAGE_PRESETS, PX_PER_MM, resolvePageSize, type PageSizeMm } from "./layout";
23
+ export { createFrameGeoref, cornersFromBounds, parseCorners, normalizeCrs, builtinCrsForward, resolveCrsForward, northBearingDegrees, scaleVariation, utmDef, mercatorForward, mercatorInverse, type FrameGeoref, type CrsForward, } from "./georef";
24
+ export { buildScalebar, buildScalebarDrawing, niceNumberBelow, formatScaleDenominator, SCALE_GROUP_SEPARATOR, type ScalebarSpec, type ScalebarDrawing, type ScalebarStyle, type ScalebarUnits, } from "./scalebar";
25
+ export { resolveTokens, resolveToken, tokensIn, applyTokens, isoDate, type TokenContext, type FrameTokenValues } from "./tokens";
26
+ export { layoutText, canvasMeasurer, alignOffset, type TextLayout, type TextLine } from "./textlayout";
27
+ export { validateCartograph, validateCartographString, validateCartographDeep } from "./validate";
28
+ export { deriveLegendRows, readOverrides, readLiteralRows, swatchShapeFor, rowColorFor, MAX_CATEGORY_ENTRIES, type LegendRow, type LegendRowOverride, type SwatchShape, } from "./legend";
29
+ export { buildGraticule, niceInterval, formatDms, GEOGRAPHIC_PLANE, type Graticule, type GridPlane } from "./graticule";
30
+ export { lintLegendColours, simulate, deltaE00, parseColor, ensureCvdFilters, applyCvdToCanvas, resolveCvdType, CVD_TYPES, CVD_VALUES, CVD_MATRICES, COLLAPSE_THRESHOLD, type CvdType, type LegendColourLint, type ColourCollapse, } from "./cvd";
31
+ export { loadCore, setCoreLoader, peekCore, isCoreLoaded, type CoreModule } from "./core-loader";
32
+ export { cornersFromCamera, readCamera, sanitizeAdoptedMap, type FrameCamera, type Corners } from "./live-frame";
33
+ export { renderCartograph, renderCartographAtlas, MAX_CANVAS_AREA, MAX_CANVAS_DIMENSION, type RenderCartographOptions, type RenderCartographResult, type RenderAtlasOptions, type RenderAtlasResult, } from "./render";
34
+ export { buildPages, featureBounds, padBounds, boundsArea, atlasFields, fieldOf, parseSort, sanitizeFilename, resolveFilenames, rejectionFor, type AtlasFeature, type AtlasRejection, } from "./atlas";
35
+ export { createZip, type ZipFile } from "./zip";
36
+ export { applyUrlActions, whenPageReady, installUrlActions, URL_ACTION_TIMEOUT_MS } from "./url-actions";
37
+ export { boxMm, paintEnvForDpi, type PaintEnv, type CartographPainter } from "./paint";
38
+ export { findFrame, allFrames, referencedFrame, ownerCartograph, clearUnlinked } from "./refs";
39
+ /**
40
+ * Explicit registration — importing the entry already registers everything
41
+ * (defineElement is idempotent and a no-op outside a browser); this exists
42
+ * for hosts that tree-shake side effects or want registration at a chosen
43
+ * time.
44
+ */
45
+ export declare function defineCartographElements(): void;
@@ -0,0 +1,50 @@
1
+ /** A row as the atlas sees it — flat object, GeoJSON feature, or columnar slice. */
2
+ export type AtlasFeature = Record<string, unknown>;
3
+ export interface AtlasLayerInput {
4
+ id: string;
5
+ type?: string;
6
+ data?: unknown;
7
+ tileData?: unknown;
8
+ asset?: unknown;
9
+ live?: boolean;
10
+ filter?: unknown;
11
+ categoryFilter?: unknown;
12
+ props?: Record<string, unknown>;
13
+ }
14
+ /** Why a layer cannot drive an atlas — each maps to a validator message. */
15
+ export type AtlasRejection = "tiled" | "asset" | "empty";
16
+ export declare function rejectionFor(layer: AtlasLayerInput | undefined): AtlasRejection | null;
17
+ /** Read a field from a row, looking inside GeoJSON `properties` when present. */
18
+ export declare function fieldOf(feature: AtlasFeature, name: string): unknown;
19
+ /** Every field a page's `{{atlas.<field>}}` tokens can resolve, flattened. */
20
+ export declare function atlasFields(feature: AtlasFeature): Record<string, string>;
21
+ export type Bounds = [number, number, number, number];
22
+ /**
23
+ * A feature's lon/lat bounding box, walking whatever geometry it carries.
24
+ * Null when the feature has no coordinates to fit to.
25
+ */
26
+ export declare function featureBounds(feature: AtlasFeature): Bounds | null;
27
+ /** Grow a box by a fraction of its own size, so a fitted feature is not flush to the frame edge. */
28
+ export declare function padBounds(bounds: Bounds, margin: number): Bounds;
29
+ /** Bounding-box area — the tie-break for "the largest feature" in overlap checks. */
30
+ export declare const boundsArea: ([west, south, east, north]: Bounds) => number;
31
+ export interface BuildPagesOptions {
32
+ features: readonly AtlasFeature[];
33
+ /** Compiled predicate from the `filter=` expression; absent keeps everything. */
34
+ filter?: (feature: AtlasFeature) => boolean;
35
+ /** `sort="field"` / `"-field"`, comma-separated for several keys. */
36
+ sort?: string | null;
37
+ }
38
+ /** Apply filter and sort to produce the page order. */
39
+ export declare function buildPages({ features, filter, sort }: BuildPagesOptions): AtlasFeature[];
40
+ export declare function parseSort(sort: string | null | undefined): {
41
+ field: string;
42
+ descending: boolean;
43
+ }[];
44
+ /** Strip everything a filesystem (or a zip reader) would object to. */
45
+ export declare function sanitizeFilename(name: string): string;
46
+ /**
47
+ * Resolve one filename per page from a token pattern, suffixing duplicates so
48
+ * two features with the same name cannot silently overwrite each other.
49
+ */
50
+ export declare function resolveFilenames(pattern: string | null | undefined, pages: readonly AtlasFeature[], extension: string): string[];
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The one place the cartograph reaches the map runtime.
3
+ *
4
+ * THE LAZY-CORE RULE: `src/cartograph/` never imports the core entry at module
5
+ * scope, so a static-only cartograph — the qgis2carto output, a survey sheet
6
+ * in a mail preview — loads no deck.gl, no MapLibre, nothing. The core arrives
7
+ * only when a live `<om-frame>` actually mounts. `dev/assert-bundle-cdn-safe.mjs`
8
+ * fails the build if that ever regresses.
9
+ *
10
+ * The import EXPRESSION differs per build entry and cannot be written here:
11
+ * - the bundler entry imports the package by name ("@nika-js/onlymap"),
12
+ * which its Vite config marks external so consumers share one core
13
+ * instance (the deck.gl class-identity rule);
14
+ * - the standalone entry imports a sibling file by relative URL, because a
15
+ * CDN-hosted single file has no package resolution.
16
+ * So each entry injects its own loader and this module owns the caching,
17
+ * the in-flight de-duplication, and the "you forgot" error.
18
+ */
19
+ /** The slice of the core the cartograph uses. Type-only — erased at compile time. */
20
+ export type CoreModule = typeof import("../index");
21
+ type CoreLoader = () => Promise<unknown>;
22
+ /** Called once by each entry (index.ts / standalone.ts) at import time. */
23
+ export declare function setCoreLoader(fn: CoreLoader): void;
24
+ /** True once the core has finished loading — lets callers stay synchronous. */
25
+ export declare function isCoreLoaded(): boolean;
26
+ /** The loaded core, or null if it has not been loaded yet. */
27
+ export declare function peekCore(): CoreModule | null;
28
+ /**
29
+ * Load the map runtime. Concurrent callers share one import; repeat callers
30
+ * get the cached module.
31
+ */
32
+ export declare function loadCore(): Promise<CoreModule>;
33
+ /** Test seam: forget the cached core so a suite can install a different loader. */
34
+ export declare function resetCoreLoaderForTest(): void;
35
+ export {};
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Colour-vision-deficiency simulation and legend colour linting
3
+ * (NikaGeospatial/onlymap-js#42).
4
+ *
5
+ * Two jobs, one set of matrices:
6
+ * - `cvd=` / `?cvd=` renders the whole page as a viewer with that condition
7
+ * would see it — a review aid, applied to the live document and never
8
+ * persisted by an editor.
9
+ * - `lintLegendColours()` answers the question that actually matters before
10
+ * printing: would any two legend entries become indistinguishable? A map
11
+ * whose categories collapse under deuteranopia is unreadable to roughly
12
+ * 1 in 12 men, and paper offers no way to toggle it back.
13
+ *
14
+ * Matrices are Viénot, Brettel & Mollon (1999), applied in LINEAR RGB.
15
+ *
16
+ * That combination is load-bearing and was got wrong once: the widely-copied
17
+ * "colour blindness matrices" that operate on sRGB directly are INVERTIBLE —
18
+ * their red/green block has a non-zero determinant — so no two distinct
19
+ * colours ever map to the same output and a lint built on them can never
20
+ * report a collapse. The Viénot matrices project onto the dichromat's own
21
+ * two-dimensional colour plane, so rows 1 and 2 (protan/deutan) or 2 and 3
22
+ * (tritan) are IDENTICAL by construction: the confusion axis genuinely
23
+ * disappears, which is the whole point of asking the question.
24
+ */
25
+ export type CvdType = "protanopia" | "deuteranopia" | "tritanopia" | "achromatopsia";
26
+ export declare const CVD_TYPES: readonly CvdType[];
27
+ /** The simulation an authored `cvd=` / `?cvd=` value asks for; null for "none" or a typo. */
28
+ export declare function resolveCvdType(raw: string | null | undefined): CvdType | null;
29
+ /** Every accepted `cvd=` spelling, for validator messages and IntelliSense. */
30
+ export declare const CVD_VALUES: readonly string[];
31
+ /**
32
+ * Row-major 3×3 transforms in LINEAR RGB (not sRGB — see the module comment).
33
+ * Note the repeated rows: that singularity IS the simulation.
34
+ */
35
+ export declare const CVD_MATRICES: Record<CvdType, readonly number[]>;
36
+ export interface Rgb {
37
+ r: number;
38
+ g: number;
39
+ b: number;
40
+ }
41
+ /** Parse `#rgb`, `#rrggbb`, or `rgb()/rgba()`. Null for anything else (a gradient, a keyword we do not resolve). */
42
+ export declare function parseColor(input: string): Rgb | null;
43
+ /**
44
+ * Apply a CVD matrix to a colour. The transform happens in LINEAR light,
45
+ * because that is the space the matrices were derived in — running them on
46
+ * gamma-encoded sRGB gives visibly wrong results and, worse, a model that
47
+ * never collapses anything.
48
+ */
49
+ export declare function simulate(color: Rgb, type: CvdType): Rgb;
50
+ /**
51
+ * CIEDE2000 colour difference. Below ~10 two colours are hard to tell apart
52
+ * at legend-swatch size; below ~2.3 they are a "just noticeable difference".
53
+ */
54
+ export declare function deltaE00(a: Rgb, b: Rgb): number;
55
+ /** Colours that collapse under a CVD type are indistinguishable below this ΔE00. */
56
+ export declare const COLLAPSE_THRESHOLD = 10;
57
+ export interface LegendColourEntry {
58
+ label: string;
59
+ color: string;
60
+ /**
61
+ * The layer this row came from. Rows are only compared WITHIN a layer
62
+ * (NikaGeospatial/onlymap-js#42 §3): two layers may legitimately share a colour — a reader
63
+ * tells their swatches apart by which block of the legend they sit in, not
64
+ * by hue — so pairing across them reports collapses nobody can act on.
65
+ * Literal rows carry no layer and are compared as one group.
66
+ */
67
+ layer?: string;
68
+ }
69
+ export interface ColourCollapse {
70
+ type: CvdType;
71
+ a: LegendColourEntry;
72
+ b: LegendColourEntry;
73
+ /** ΔE00 after simulation — smaller is worse. */
74
+ distance: number;
75
+ }
76
+ export interface LegendColourLint {
77
+ collapses: ColourCollapse[];
78
+ /** The CVD type that damages this palette most, if any. */
79
+ worstType: CvdType | null;
80
+ }
81
+ /**
82
+ * Find legend entries that become indistinguishable under colour-vision
83
+ * deficiency. Pairs that are ALREADY indistinguishable to normal vision are
84
+ * skipped — that is a palette bug of a different kind, not a CVD finding.
85
+ */
86
+ export declare function lintLegendColours(entries: readonly LegendColourEntry[], threshold?: number): LegendColourLint;
87
+ /**
88
+ * Apply a CVD simulation to a 2D canvas in place — the EXPORT half of `cvd=`
89
+ * (NikaGeospatial/onlymap-js#42). The screen and print paths use the SVG filter above; a raster export
90
+ * has no filter stage, so the same matrices are run over the pixels here,
91
+ * which is what keeps "what I reviewed" and "what I sent" the same image.
92
+ *
93
+ * Returns false when the pixels cannot be read — a cross-origin raster taints
94
+ * the canvas, and an unfiltered export is a better answer than a thrown one,
95
+ * as long as the caller says so.
96
+ */
97
+ export declare function applyCvdToCanvas(ctx: CanvasRenderingContext2D, width: number, height: number, type: CvdType): boolean;
98
+ /** The `filter` value applied to a page under `cvd=` — an SVG filter this module injects. */
99
+ export declare const CVD_FILTER_ID: (type: CvdType) => string;
100
+ /**
101
+ * Ensure the SVG colour-matrix filters exist in the document, once. CSS
102
+ * cannot express a colour matrix, so simulation needs a real filter element;
103
+ * it is injected next to the page rather than shipped in the stylesheet
104
+ * because a document with no `cvd=` should carry none of it.
105
+ */
106
+ export declare function ensureCvdFilters(doc: Document): void;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared base for the cartograph elements: attribute reflection with
3
+ * microtask-coalesced re-render (the om-widget observedAttributes pattern,
4
+ * batched — an editor writing x, y, w, h in one gesture triggers ONE
5
+ * render), and placement application.
6
+ *
7
+ * Placement contract (design §3.1): the ATTRIBUTES (`x y w h rotation z`,
8
+ * mm) are the single authored source of truth; the runtime re-asserts the
9
+ * equivalent inline mm styles from them on every change. Producers also
10
+ * write those inline styles at export time so the no-JS fallback positions
11
+ * exactly; the validator (Phase 1) errors when authored styles disagree
12
+ * with the attributes.
13
+ */
14
+ import { HTMLElementBase } from "../env";
15
+ export declare abstract class CartographElementBase extends HTMLElementBase {
16
+ private renderQueued;
17
+ connectedCallback(): void;
18
+ attributeChangedCallback(): void;
19
+ /** Ask for a re-render — used when something this element DEPENDS on changed. */
20
+ requestCartographRender(): void;
21
+ protected scheduleRender(): void;
22
+ /** One coalesced render per microtask — subclasses re-read their attributes here. */
23
+ protected abstract renderNow(): void;
24
+ /** Write the placed-element inline styles from the mm placement attributes. */
25
+ protected applyPlacement(): void;
26
+ }
@@ -0,0 +1,47 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type AtlasFeature, type AtlasRejection } from "../atlas";
3
+ import type { OmFrameElement } from "./om-frame";
4
+ export declare class OmAtlasElement extends CartographElementBase {
5
+ static readonly observedAttributes: string[];
6
+ /**
7
+ * Pages are SNAPSHOTTED when iteration first starts. A streaming or polling
8
+ * layer would otherwise change the page count halfway through an export,
9
+ * producing an archive whose contents do not match its own index.
10
+ */
11
+ private pages;
12
+ private currentIndex;
13
+ private rejection;
14
+ /** The frame whose layer drives the iteration — the same `for=` rule every furniture element follows. */
15
+ get frame(): OmFrameElement | null;
16
+ get count(): number;
17
+ get index(): number;
18
+ get current(): AtlasFeature | null;
19
+ /** Why this atlas cannot run, if it cannot. */
20
+ get problem(): AtlasRejection | null;
21
+ /** Export filenames, one per page, already de-duplicated. */
22
+ filenames(extension?: string): string[];
23
+ /** Human page labels, for an editor's stepper and PDF bookmarks. */
24
+ pageName(index: number): string;
25
+ /**
26
+ * Recompute the page list from the layer as it stands now, and apply the
27
+ * current page. Applying is part of refreshing because the page list first
28
+ * becomes non-empty when the frame mounts its map: without it, page 1 would
29
+ * never be applied and a cartograph would print `{{atlas.*}}` literally with
30
+ * the authored camera, until something else happened to seek.
31
+ */
32
+ refresh(): void;
33
+ private ensurePages;
34
+ /**
35
+ * Rows from the layer, with the layer's OWN declarative filters applied —
36
+ * the coherence rule. An atlas that paged over unfiltered rows would print
37
+ * sheets for features the map itself is hiding.
38
+ */
39
+ private materialize;
40
+ /** `filter=` uses the CORE expression language — the syntax authors already know from om-layer. */
41
+ private compileFilter;
42
+ /** Move to a page: refit the following frames, re-resolve tokens, announce it. */
43
+ seek(index: number): void;
44
+ next(): void;
45
+ private applyPage;
46
+ protected renderNow(): void;
47
+ }
@@ -0,0 +1,76 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type PageSizeMm } from "../layout";
3
+ import { type TokenContext } from "../tokens";
4
+ import type { OmAtlasElement } from "./om-atlas";
5
+ export declare class OmCartographElement extends CartographElementBase {
6
+ static readonly observedAttributes: string[];
7
+ private frameListenerBound;
8
+ /** Fields of the atlas page currently displayed, for {{atlas.<field>}}. */
9
+ private atlasFields;
10
+ private creditObserver;
11
+ private unsubscribeLicense;
12
+ /** The resolved page box, mm — furniture and export read this, never the attributes. */
13
+ get pageSize(): PageSizeMm;
14
+ /** Bleed in mm (0 when unset) — extends the printed sheet past the trim. */
15
+ get bleedMm(): number;
16
+ connectedCallback(): void;
17
+ disconnectedCallback(): void;
18
+ /**
19
+ * The free-tier credit (issue #41 §6, decided 2026-09-11): a cartograph is
20
+ * a print/export artifact, and `snapshot()` deliberately excludes the map
21
+ * badge — so without this line a free-plan sheet would ship credit-free.
22
+ * Suppressed when the author placed their OWN OnlyMap credit in any
23
+ * `om-text` (the licensing condition is the credit, not this element), or
24
+ * when a loaded core reports a paid plan (`hideBadge`). A static-only page
25
+ * never loads the core and keeps the credit — key verification is the map
26
+ * runtime's job (issue #41 §9: the om-map carries the key).
27
+ */
28
+ private renderCredit;
29
+ /** Give the runtime-owned DOM credit the same canvas paint contract as authored furniture. */
30
+ private armCreditPainter;
31
+ /**
32
+ * Print this page at `dpi`.
33
+ *
34
+ * A live map is a WebGL canvas, and WebGL canvases print blank or at screen
35
+ * resolution — the drawing buffer is not preserved, and the print rasterizer
36
+ * never re-renders it. So every live frame is swapped for a print-resolution
37
+ * capture first, and restored on `afterprint`. This is also why an
38
+ * unassisted Ctrl+P cannot produce a high-DPI page: the print dialog opens
39
+ * synchronously and captures are asynchronous.
40
+ */
41
+ print(options?: {
42
+ dpi?: number;
43
+ }): Promise<void>;
44
+ /** Render the whole page to one image, for `flatten`. */
45
+ private buildFlattenedImage;
46
+ /** The page's atlas controller, if it has one. */
47
+ get atlas(): OmAtlasElement | null;
48
+ /** Called by the atlas when it seeks — the source for {{atlas.<field>}}. */
49
+ setAtlasFields(fields: Record<string, string> | undefined): void;
50
+ /** Re-render every furniture element — called when a frame's georeference appears. */
51
+ refreshFurniture(): void;
52
+ /** Values every `{{token}}` on this page resolves against. */
53
+ tokenContext(): TokenContext;
54
+ /** Re-resolve `{{tokens}}` in every text block on the page. */
55
+ refreshTokens(): void;
56
+ protected renderNow(): void;
57
+ /**
58
+ * Crop marks and the safe-zone guide.
59
+ *
60
+ * Crop marks tell a trimmer where the sheet is cut, so they live OUTSIDE the
61
+ * trim box, in the bleed. The safe zone is the opposite: an on-screen guide
62
+ * showing where content is at risk of being trimmed off, and it must never
63
+ * reach paper or an exported image — a guide printed on the deliverable is
64
+ * worse than no guide.
65
+ */
66
+ private renderPrintMarks;
67
+ /**
68
+ * Colour-vision simulation over the whole page — a review aid, so an author
69
+ * can see what a red/green categorical scheme does to roughly 1 in 12 male
70
+ * readers BEFORE it is printed, when nothing can be toggled back.
71
+ *
72
+ * CSS cannot express a colour matrix, so the filters are real SVG elements,
73
+ * injected only when a page actually asks for one.
74
+ */
75
+ private applyCvd;
76
+ }
@@ -0,0 +1,111 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ import { type FrameGeoref } from "../georef";
4
+ export type FrameMode = "live" | "static";
5
+ export type FrameErrorReason = "no-raster" | "bad-corners" | "unknown-crs" | "raster-decode" | "no-source" | "load-failed" | "no-camera" | "degenerate-view";
6
+ export declare class OmFrameElement extends CartographElementBase implements CartographPainter {
7
+ static readonly observedAttributes: string[];
8
+ /** Resolved non-builtin CRS planes, so a re-render does not re-await proj4. */
9
+ private resolvedForward;
10
+ private resolvedForwardKey;
11
+ private resolveToken;
12
+ private lastError;
13
+ private readyFired;
14
+ private readySettled;
15
+ private readyPromise;
16
+ private readyResolve;
17
+ /**
18
+ * Settles when the frame has either rendered its map or given up on it —
19
+ * mirroring `<om-map>`'s own `ready`. It RESOLVES on error rather than
20
+ * rejecting, because every caller (export, print, the URL triggers) wants
21
+ * "stop waiting", and a frame that failed still shows a visible placeholder
22
+ * that belongs on the page.
23
+ */
24
+ get ready(): Promise<void>;
25
+ private settleReady;
26
+ /** Live-mode state. */
27
+ private liveMap;
28
+ private liveCorners;
29
+ private mountToken;
30
+ private mounting;
31
+ private mountingSource;
32
+ private mountedSource;
33
+ private liveMapOwned;
34
+ /** A capture standing in for the live map during export/print. */
35
+ private captureImage;
36
+ private printing;
37
+ get mode(): FrameMode;
38
+ /** The mounted `<om-map>`, once a live frame has one. */
39
+ get mapElement(): HTMLElement | null;
40
+ /**
41
+ * The frame's georeference, or null when it has none yet (malformed corners,
42
+ * a CRS still resolving, a live map not yet mounted, a view too pitched to
43
+ * georeference). Recomputed on demand — the fit is an 8×8 solve, far cheaper
44
+ * than caching it correctly.
45
+ */
46
+ get georef(): FrameGeoref | null;
47
+ /** The raster a static frame draws — its first <img> child, if any. */
48
+ get raster(): HTMLImageElement | null;
49
+ disconnectedCallback(): void;
50
+ /**
51
+ * Bump the generation token so a mount already in flight cannot adopt its
52
+ * map into this frame when it lands. The token is the whole cancellation
53
+ * protocol, so it lives in one place — three copies of it is three chances
54
+ * for a stale map to arrive after a src= change.
55
+ */
56
+ private abandonMount;
57
+ protected renderNow(): void;
58
+ private renderStatic;
59
+ private renderLive;
60
+ /** Inline maps are identified by node identity; fetched maps by their src=. */
61
+ private liveSource;
62
+ /** Leave live mode without deleting an authored inline map. */
63
+ private suspendLiveMode;
64
+ private releaseLiveMap;
65
+ private updateLiveCorners;
66
+ private mountLive;
67
+ private sizeLiveMap;
68
+ /**
69
+ * Draw another frame's footprint on this one. The outline is DENSIFIED —
70
+ * sampled along each edge and projected point by point — because a
71
+ * rectangle in one projection is a curve in another, and four straight
72
+ * segments would visibly cut the corners on a wide-extent inset.
73
+ */
74
+ private renderOverview;
75
+ private signalStaticReady;
76
+ private signalReady;
77
+ /**
78
+ * Frames fail loudly and visibly. A silent empty box on a printed page is
79
+ * the worst outcome — it looks like a design choice.
80
+ */
81
+ private showPlaceholder;
82
+ private clearPlaceholder;
83
+ /**
84
+ * Capture this frame's map at `scale` captured pixels per CSS pixel.
85
+ * Static frames need no capture (their raster IS the picture); a live frame
86
+ * re-renders at the requested resolution through the core.
87
+ */
88
+ captureAt(scale: number): Promise<HTMLImageElement | null>;
89
+ /**
90
+ * Point this frame's camera at a lon/lat box — the atlas's per-page refit.
91
+ * Solves for the zoom that fits the box in the frame's own pixel size, in
92
+ * the Mercator plane (where the projection is linear), then writes the
93
+ * camera attributes so everything downstream — corners, scale bar, tokens —
94
+ * follows from the same source as any authored camera.
95
+ */
96
+ fitBounds(bounds: [number, number, number, number]): void;
97
+ /** Export hook: capture before the page paints (see render.ts). */
98
+ prepareCartograph(env: PaintEnv): Promise<void>;
99
+ /** Export hook: forget the capture, so a failed pre-pass never reuses it. */
100
+ discardCartograph(): void;
101
+ /**
102
+ * Swap the live canvas for a print-resolution bitmap. A WebGL canvas prints
103
+ * blank or at screen resolution — the drawing buffer is not preserved and
104
+ * the print rasterizer does not re-render it — so the printed page shows a
105
+ * captured `<img>` instead, restored afterwards.
106
+ */
107
+ enterPrintMode(dpi: number): Promise<void>;
108
+ exitPrintMode(): void;
109
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
110
+ private paintOverview;
111
+ }
@@ -0,0 +1,24 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ import { type Graticule } from "../graticule";
4
+ export declare class OmGraticuleElement extends CartographElementBase implements CartographPainter {
5
+ static readonly observedAttributes: string[];
6
+ private resolvedPlane;
7
+ private resolvedPlaneKey;
8
+ private resolveToken;
9
+ private get kind();
10
+ private get labelPlacement();
11
+ /** The grid this element currently draws, or null when it cannot yet. */
12
+ get graticule(): Graticule | null;
13
+ /**
14
+ * The coordinate system the lattice is walked in. Geographic needs nothing;
15
+ * a projected grid needs proj4, which resolves asynchronously and triggers
16
+ * a re-render when it lands.
17
+ */
18
+ private gridPlane;
19
+ protected renderNow(): void;
20
+ private renderSvg;
21
+ /** Intersections of this line with the perpendicular family. */
22
+ private tickPoints;
23
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
24
+ }
@@ -0,0 +1,12 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ declare const FITS: readonly ["contain", "cover", "fill"];
4
+ type Fit = (typeof FITS)[number];
5
+ export declare class OmImageElement extends CartographElementBase implements CartographPainter {
6
+ static readonly observedAttributes: string[];
7
+ get fit(): Fit;
8
+ get image(): HTMLImageElement | null;
9
+ protected renderNow(): void;
10
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
11
+ }
12
+ export {};
@@ -0,0 +1,16 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ import { type LegendRow } from "../legend";
4
+ export declare class OmLegendElement extends CartographElementBase implements CartographPainter {
5
+ static readonly observedAttributes: string[];
6
+ get derived(): boolean;
7
+ /** The rows this legend currently shows — recomputed on demand, never stored. */
8
+ get rows(): LegendRow[];
9
+ protected renderNow(): void;
10
+ private renderRows;
11
+ private buildRow;
12
+ /** The swatch geometry carries meaning: an area, a line and a point read differently. */
13
+ private buildSwatch;
14
+ private buildSymbology;
15
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
16
+ }
@@ -0,0 +1,11 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ export declare class OmNorthElement extends CartographElementBase implements CartographPainter {
4
+ static readonly observedAttributes: string[];
5
+ /** Degrees clockwise from the page's "up", or null when unknowable. */
6
+ get bearing(): number | null;
7
+ private get indicatorKind();
8
+ protected renderNow(): void;
9
+ private buildGeometry;
10
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
11
+ }
@@ -0,0 +1,13 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ import { type ScalebarDrawing } from "../scalebar";
4
+ export declare class OmScalebarElement extends CartographElementBase implements CartographPainter {
5
+ static readonly observedAttributes: string[];
6
+ /** The geometry currently drawn — reused by the canvas exporter. */
7
+ get drawing(): ScalebarDrawing | null;
8
+ private get barStyle();
9
+ private get unitSystem();
10
+ protected renderNow(): void;
11
+ private renderSvg;
12
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
13
+ }
@@ -0,0 +1,16 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ export type ShapeKind = "rect" | "ellipse" | "line" | "arrow";
4
+ export declare class OmShapeElement extends CartographElementBase implements CartographPainter {
5
+ static readonly observedAttributes: string[];
6
+ get kind(): ShapeKind;
7
+ private get fill();
8
+ private get stroke();
9
+ private get strokeWidthMm();
10
+ private get fillOpacity();
11
+ private get radiusMm();
12
+ protected renderNow(): void;
13
+ /** A line from the box's top-left to its bottom-right, with a solid head. */
14
+ private arrowPath;
15
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
16
+ }
@@ -0,0 +1,9 @@
1
+ import { CartographElementBase } from "../element-base";
2
+ import { type CartographPainter, type PaintEnv } from "../paint";
3
+ export declare class OmTextElement extends CartographElementBase implements CartographPainter {
4
+ static readonly observedAttributes: string[];
5
+ protected renderNow(): void;
6
+ /** Re-run token substitution from the authored templates. */
7
+ resolveTokens(): void;
8
+ paintCartograph(ctx: CanvasRenderingContext2D, env: PaintEnv): void;
9
+ }