@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.
- package/CHANGELOG.md +18 -0
- package/README.md +4 -2
- package/dist/{LercDecode.es-DYwn8YTS.js → LercDecode.es--DH6_OFE.js} +1 -1
- package/dist/{basemap-CtEc5BCi.js → basemap-2ZZ_9gFn.js} +528 -502
- package/dist/basemap.d.ts +15 -0
- package/dist/cartograph/api.d.ts +45 -0
- package/dist/cartograph/atlas.d.ts +50 -0
- package/dist/cartograph/core-loader.d.ts +35 -0
- package/dist/cartograph/cvd.d.ts +106 -0
- package/dist/cartograph/element-base.d.ts +26 -0
- package/dist/cartograph/elements/om-atlas.d.ts +47 -0
- package/dist/cartograph/elements/om-cartograph.d.ts +76 -0
- package/dist/cartograph/elements/om-frame.d.ts +111 -0
- package/dist/cartograph/elements/om-graticule.d.ts +24 -0
- package/dist/cartograph/elements/om-image.d.ts +12 -0
- package/dist/cartograph/elements/om-legend.d.ts +16 -0
- package/dist/cartograph/elements/om-north.d.ts +11 -0
- package/dist/cartograph/elements/om-scalebar.d.ts +13 -0
- package/dist/cartograph/elements/om-shape.d.ts +16 -0
- package/dist/cartograph/elements/om-text.d.ts +9 -0
- package/dist/cartograph/georef.d.ts +126 -0
- package/dist/cartograph/graticule.d.ts +70 -0
- package/dist/cartograph/html-data.d.ts +27 -0
- package/dist/cartograph/index.d.ts +1 -0
- package/dist/cartograph/layout.d.ts +23 -0
- package/dist/cartograph/legend.d.ts +75 -0
- package/dist/cartograph/live-frame.d.ts +69 -0
- package/dist/cartograph/paint.d.ts +54 -0
- package/dist/cartograph/refs.d.ts +33 -0
- package/dist/cartograph/render.d.ts +64 -0
- package/dist/cartograph/scalebar.d.ts +91 -0
- package/dist/cartograph/schema.d.ts +38 -0
- package/dist/cartograph/standalone.d.ts +1 -0
- package/dist/cartograph/textlayout.d.ts +45 -0
- package/dist/cartograph/tokens.d.ts +45 -0
- package/dist/cartograph/url-actions.d.ts +26 -0
- package/dist/cartograph/validate.d.ts +38 -0
- package/dist/cartograph/zip.d.ts +22 -0
- package/dist/cartograph.css +1 -0
- package/dist/cartograph.js +2768 -0
- package/dist/cartograph.standalone.js +7640 -0
- package/dist/crs-DsDQ4Q4i.js +71 -0
- package/dist/download.d.ts +6 -0
- package/dist/feature-access.d.ts +14 -0
- package/dist/field-access.d.ts +1 -10
- package/dist/{geoparquet-RBC0ZJNm.js → geoparquet-DDZzee5Z.js} +1 -1
- package/dist/{index-DkimKgOm.js → index-BGOblzWN.js} +5141 -5070
- package/dist/index-CcLC9jE5.js +4798 -0
- package/dist/{index-CiGUw6GD.js → index-CeoVzd6t.js} +1 -1
- package/dist/{index-CJ0pib8I.js → index-Cz1VsWzO.js} +2 -2
- package/dist/{index-Bwu1DFHP.js → index-DkxFFflv.js} +1 -1
- package/dist/{index-HrEJtZoQ.js → index-DphcuoPv.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/{lerc-BIQMg0FV.js → lerc-D7TfTy6l.js} +2 -2
- package/dist/onlymap.standalone.js +8827 -8730
- package/dist/onlymapjs.js +12 -11
- package/dist/{raster-Dsugvobw.js → raster-D_3YKFZz.js} +3 -3
- package/dist/{raster-pipeline-rqJJRCEo.js → raster-pipeline-D1yeoJo2.js} +1 -1
- package/dist/runtime-core.d.ts +30 -0
- package/dist/snapshot.d.ts +17 -0
- package/dist/units.d.ts +9 -0
- package/dist/version.d.ts +1 -1
- package/dist/{zarr-CRysSFhD.js → zarr-DH5Ntt5U.js} +2 -2
- package/docs/cartograph.md +396 -0
- package/llms.txt +2 -1
- package/onlymapjs.attributes.json +1 -0
- package/onlymapjs.html-data.json +974 -0
- package/package.json +13 -2
- package/skills/onlymapjs/SKILL.md +2 -2
- 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
|
+
}
|