@georeferencing/plugins 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TobiLG <github@tobilg.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,321 @@
1
+ # @georeferencing/plugins
2
+
3
+ Optional local export formats for `@georeferencing/core`: GeoTIFF, JPEG, PDF,
4
+ normalized-image world files, JSON sessions, QGIS control points and accuracy
5
+ reports. Each format is enabled explicitly through controller configuration.
6
+ Heavy implementations load when the user requests an export.
7
+
8
+ This package has no React or OpenLayers dependency. The optional PDF map contract
9
+ is structural and accepts an OpenLayers map. The React editor reads the same
10
+ controller registry to display only configured export actions.
11
+
12
+ ## Installation and package selection
13
+
14
+ ```sh
15
+ pnpm add @georeferencing/core @georeferencing/plugins
16
+ # Add the UI only if needed:
17
+ pnpm add @georeferencing/react react@19 react-dom@19 ol@10
18
+ ```
19
+
20
+ The package ships ESM, TypeScript declarations and codec-worker assets. Node.js
21
+ 22.12+ is the declared tooling requirement; repository development uses pnpm 12.x.
22
+ **Install the same version of `@georeferencing/plugins`, `@georeferencing/core`
23
+ and (if used) `@georeferencing/react`**, and upgrade them together. Plugins depend
24
+ on core; a mismatched version can install a second copy of core whose projection
25
+ registry and worker code are separate from the controller's. See
26
+ [keep package versions aligned](https://georeferencing-api-docs.gh.tobilg.com/Getting_started/#keep-package-versions-aligned).
27
+
28
+ Core alone enables no exports. Import only the factories your application uses.
29
+ The root plugin barrel exports factories and types; per-format imports give
30
+ bundlers a narrower dependency graph. Unused formats can be excluded from the
31
+ browser bundle. Installing this package also installs its `pdf-lib` dependency,
32
+ but PDF code is downloaded by the application only when its lazy export path runs.
33
+
34
+ ## Pure serializers
35
+
36
+ `@georeferencing/plugins/serializers` exposes `exportPoints(document, definitions?)`,
37
+ `worldFile(fit, workingCrs, outputCrs)` and `accuracyReport(document, fit)` without
38
+ loading a codec worker or UI. These return text/placement values; the `/data`
39
+ factories register full controller exports and downloadable artifacts.
40
+
41
+ ```ts
42
+ import { importPoints, parseSession } from "@georeferencing/core";
43
+ import { exportPoints, accuracyReport, worldFile } from "@georeferencing/plugins/serializers";
44
+ ```
45
+
46
+ Session validation and `.points` importing live in core. The `worldFile()`
47
+ factory registered from `@georeferencing/plugins/data` (and the plugin root) is a
48
+ separate controller export format.
49
+
50
+ ## Configure export formats
51
+
52
+ ```ts
53
+ import { GeoreferencerController } from "@georeferencing/core";
54
+ import { createWorkerEngine } from "@georeferencing/core/engine";
55
+ import { geoTiff } from "@georeferencing/plugins/geotiff";
56
+ import { jpeg } from "@georeferencing/plugins/jpeg";
57
+ import { pdf } from "@georeferencing/plugins/pdf";
58
+
59
+ export function createExportEditor(workingCrs: string) {
60
+ const engine = createWorkerEngine();
61
+ const controller = new GeoreferencerController({
62
+ workingCrs,
63
+ engine,
64
+ exports: [
65
+ geoTiff(),
66
+ jpeg({ quality: 0.92, background: [255, 255, 255] }),
67
+ pdf({ paper: "A4", landscape: true, attribution: "Host-provided attribution" }),
68
+ ],
69
+ });
70
+ return { controller, engine };
71
+ }
72
+ ```
73
+
74
+ The caller loads an image and supplies GCPs through the controller or React UI.
75
+ Raster/report exports require a current valid fit. Dispose the controller and
76
+ its host-owned engine when the session is permanently finished.
77
+
78
+ | Factory | Import suffix | Format ID | Files / eligibility |
79
+ | --- | --- | --- | --- |
80
+ | `geoTiff()` | `/geotiff` | `geotiff` | Georeferenced TIFF; valid fit |
81
+ | `jpeg()` | `/jpeg` | `jpeg` | JPEG, `.jgw`, `.crs.json`; valid fit |
82
+ | `pdf()` | `/pdf` | `pdf` | PDF alignment/map report; valid fit and preview |
83
+ | `worldFile()` | `/data` | `world-file` | Original-resolution normalized PNG, `.pgw`, CRS sidecar; Linear/Helmert without reprojection |
84
+ | `session()` | `/data` | `session` | Complete session JSON; fit may be unfinished |
85
+ | `points(definitions?)` | `/data` | `points` | QGIS `.points`; fit may be unfinished |
86
+ | `accuracy()` | `/data` | `accuracy` | Full-precision JSON diagnostics; valid fit |
87
+
88
+ All controller exports currently require loaded source image bytes. Register data
89
+ exports only when needed:
90
+
91
+ ```ts
92
+ import type { Definitions, GeoreferencerController } from "@georeferencing/core";
93
+ import { accuracy, points, session, worldFile } from "@georeferencing/plugins/data";
94
+
95
+ export function enableDataExports(
96
+ controller: GeoreferencerController,
97
+ definitions: Definitions,
98
+ ) {
99
+ controller.setExportFormats([session(), points(definitions), accuracy(), worldFile()]);
100
+ }
101
+ ```
102
+
103
+ `setExportFormats` replaces the entire registry and cancels pending export work;
104
+ it does not append formats. IDs must be unique. Configuration is runtime state,
105
+ separate from saved session JSON. Removing all formats leaves alignment and host
106
+ feature/draft saving available.
107
+
108
+ ## Run an export and consume every artifact
109
+
110
+ ```ts
111
+ import type { ExportResult, GeoreferencerController } from "@georeferencing/core";
112
+
113
+ export async function exportSelected(
114
+ controller: GeoreferencerController,
115
+ id: string,
116
+ consume: (result: ExportResult) => Promise<void>,
117
+ ) {
118
+ const unavailable = controller.getExportUnavailable(id);
119
+ if (unavailable) throw new Error(unavailable);
120
+ const result = await controller.export(id);
121
+ if (result) await consume(result);
122
+ return result;
123
+ }
124
+ ```
125
+
126
+ `ExportResult` contains:
127
+
128
+ | Field | Meaning |
129
+ | --- | --- |
130
+ | `format` | Registered format ID |
131
+ | `document` | Frozen document revision actually processed |
132
+ | `blob` | Primary encoded artifact, also `files[0].blob` |
133
+ | `files` | Suggested filename and Blob for every artifact, including required sidecars |
134
+ | `raster` | Optional full-resolution, pre-encoding RGBA raster, bounds and CRS |
135
+ | `worldFile` | Optional original-pixel placement text and explicit CRS |
136
+
137
+ Save/download each `files` entry with its filename. The React component's
138
+ `onExport(result)` intercepts this same result instead of default downloads.
139
+ Hosts that create object URLs must revoke them after use. A completed export is
140
+ not a persistence acknowledgement and does not clear the dirty flag.
141
+
142
+ Missing formats, invalid eligibility and concurrent exports reject before work
143
+ starts. Lazy-load/processing failures return `null` and populate snapshot errors;
144
+ the operation can be retried. Cancellation and stale work also return `null`.
145
+ Use `cancelExport()` to abort. Image replacement, re-alignment, suspension and
146
+ format-registry changes discard obsolete outputs. Newer drawing edits can remain
147
+ in the draft while the result retains the exact earlier document snapshot.
148
+
149
+ `exportRaster()` is an alias for `export("geotiff")`, and `exportWorldFile()`
150
+ aliases `export("world-file")`. Their matching plugins must be registered.
151
+
152
+ ## GeoTIFF output
153
+
154
+ `geoTiff()` uses the core full-resolution warp followed by a dedicated codec
155
+ worker. Configure spatial and creation settings through `controller.setOutput`:
156
+
157
+ ```ts
158
+ import type { GeoreferencerController } from "@georeferencing/core";
159
+
160
+ export function configureTiff(controller: GeoreferencerController) {
161
+ controller.setOutput({
162
+ ...controller.getSnapshot().document.output,
163
+ crs: "EPSG:3857",
164
+ resolution: [2, 2], // two output-CRS units per pixel; choose for your data
165
+ resampler: "bilinear",
166
+ compression: "deflate",
167
+ rowsPerStrip: 256,
168
+ predictor: 2,
169
+ });
170
+ }
171
+ ```
172
+
173
+ | Setting | Behavior |
174
+ | --- | --- |
175
+ | `crs` | Explicit output CRS; register required definitions with the engine |
176
+ | `bounds` | Optional outer-edge crop extent in output CRS |
177
+ | `resolution` | Positive x/y pixel sizes in output units; omitted means estimated resolution |
178
+ | `resampler` | `nearest`, `bilinear`, `cubic`, `cubicSpline` or `lanczos` |
179
+ | `compression` | `none`, `deflate` or `packbits` |
180
+ | `rowsPerStrip` | Integer 1–4096; default bounded by 256 and raster height |
181
+ | `predictor` | 1 or 2; horizontal predictor 2 requires Deflate |
182
+ | `sourceNoData` | Source byte or RGB triple excluded before interpolation |
183
+ | `noData` | Optional output byte 0–255 with a GDAL no-data tag; otherwise retain alpha |
184
+
185
+ Output is north-up, PixelIsArea, 8-bit RGB or unassociated RGBA. Bounds locate
186
+ outer pixel edges; the first row is at maxY. Numeric no-data cannot preserve
187
+ partial alpha; choose a value that does not collide with valid RGB data. Deflate
188
+ requires browser CompressionStream support.
189
+
190
+ This encoder writes classic TIFF below 4 GiB and requires an EPSG code below
191
+ 32767. BigTIFF, COG layout, arbitrary-WKT GeoKeys, scientific sample preservation
192
+ and multipage output are outside the supported envelope. See the
193
+ [capabilities and limits](https://github.com/tobilg/georeferencing/blob/main/packages/documentation/guides/capabilities.md)
194
+ for the supported output envelope and QGIS compatibility boundaries.
195
+
196
+ ## JPEG output
197
+
198
+ `jpeg({ quality, background })` shares the same full-resolution warp and output
199
+ grid, then encodes a lossy JPEG. `quality` is 0–1, default 0.92. `background` is an
200
+ RGB byte tuple, default white; transparent pixels are composited onto it.
201
+ TIFF compression and predictor settings do not affect JPEG.
202
+
203
+ JPEG has no embedded CRS. Keep all three files together: the image, `.jgw` world
204
+ file and `.crs.json` metadata. The world file uses the upper-left pixel centre and
205
+ negative y pixel size. JSON records CRS, bounds, dimensions, source identity,
206
+ revision and encoding options. `result.raster` retains the unflattened RGBA warp;
207
+ decoded JPEG pixels can differ because of alpha composition and lossy encoding.
208
+
209
+ ## PDF and supplementary exports
210
+
211
+ `pdf()` creates a report from the aligned preview, including GCPs/residuals,
212
+ parameters, source/revision information and an embedded full-precision JSON
213
+ attachment. It is a report, not a geospatial PDF raster export.
214
+
215
+ Options include `paper` (`A4`, `A3`, `Letter`), `landscape`, `margin` in PDF points,
216
+ projection `definitions`, `attribution` and an optional `map` or map accessor.
217
+
218
+ ```ts
219
+ import { pdf, type ReportMap } from "@georeferencing/plugins/pdf";
220
+
221
+ export function mapReport(currentMap: () => ReportMap | undefined) {
222
+ return pdf({ map: currentMap, paper: "A4", margin: 40 });
223
+ }
224
+ ```
225
+
226
+ The accessor resolves the current map at export time. Capture uses currently
227
+ loaded canvas layers without moving the view or waiting for pending tiles. Map
228
+ sources must permit CORS canvas export. WebGL-only renderers and arbitrary DOM
229
+ overlays are outside this capture contract. Omit `map` for an aligned-raster-only
230
+ report. PDF cancellation discards a late result but cannot interrupt synchronous
231
+ PDF generation or undo a module download.
232
+
233
+ The `worldFile()` plugin exports orientation-normalized original-resolution PNG
234
+ pixels with matching placement; a world file itself contains no CRS. QGIS
235
+ `.points` exports use the QGIS source-y convention and placeholder residual
236
+ columns; use `accuracy()` for computed diagnostics. A session download preserves
237
+ metadata and features, but not original file bytes, undo history or credentials.
238
+ Neither a downloaded session nor a report counts as a host save.
239
+
240
+ ## Worker assets and memory
241
+
242
+ With Vite, use `worker: { format: "es" }`. Default codec URLs resolve relative to
243
+ the installed plugin module, including packed consumers. Each codec can override
244
+ its worker independently:
245
+
246
+ ```ts
247
+ import { geoTiff } from "@georeferencing/plugins/geotiff";
248
+ import { jpeg } from "@georeferencing/plugins/jpeg";
249
+
250
+ export const formats = [
251
+ geoTiff({ workerUrl: "/my-app/assets/geotiff.js" }),
252
+ jpeg({ workerUrl: "/my-app/assets/jpeg.js", quality: 0.9 }),
253
+ ];
254
+ ```
255
+
256
+ Copy the matching `dist/workers/geotiff.js` or `jpeg.js`, adjacent legal notices
257
+ and `dist/licenses` when deploying assets yourself. Alternatively supply a
258
+ `workerFactory` returning a fresh dedicated module worker. Vite supports asset
259
+ imports from `@georeferencing/plugins/geotiff-worker?worker` and
260
+ `@georeferencing/plugins/jpeg-worker?worker`. Respect the deployment base and CSP.
261
+
262
+ Raster codecs inherit engine limits, projection definitions, scheduling and
263
+ cancellation. Temporary raster buffers transfer to the encoder and back; the
264
+ editor preview is never transferred. Low-level callers must treat submitted
265
+ buffers as detached until the returned raster arrives. Aborting terminates a
266
+ codec worker. Core defaults are 24MP input/output and 768 MiB estimated reservation;
267
+ compression and buffers may require a smaller practical output. These estimates
268
+ are not measured process-memory caps or streaming guarantees.
269
+
270
+ ## Custom exporters and low-level APIs
271
+
272
+ A custom descriptor implements core's `ExportFormat` contract; it does not need
273
+ to depend on this package. Respect the supplied AbortSignal and frozen document.
274
+ The first file's Blob must be the primary returned Blob.
275
+
276
+ ```ts
277
+ import type { ExportFormat } from "@georeferencing/core";
278
+
279
+ export const provenance: ExportFormat = {
280
+ id: "provenance",
281
+ label: "Export provenance",
282
+ requiresFit: false,
283
+ load: async () => ({
284
+ async run({ document, signal }) {
285
+ signal.throwIfAborted();
286
+ const blob = new Blob([JSON.stringify({
287
+ documentId: document.id,
288
+ revision: document.documentRevision,
289
+ source: document.sourceImage,
290
+ })], { type: "application/json" });
291
+ return { blob, files: [{ name: "provenance.json", blob }] };
292
+ },
293
+ }),
294
+ };
295
+ ```
296
+
297
+ For a larger implementation, have `load` dynamically import your own module.
298
+ Use `raster: true` to request shared raster output controls, and `unavailable` for
299
+ additional pure eligibility checks. All exporters share the controller's revision
300
+ and cancellation contract.
301
+
302
+ `@georeferencing/plugins/tiff` exposes low-level TIFF encoders and option
303
+ validation. `/report` exposes `createPdfReport`, `captureMap` and structural map
304
+ interfaces. Bypassing the controller makes the caller responsible for matching
305
+ revisions, limits, cancellation and resource cleanup. Custom codec workers can
306
+ use `installEncoderWorker` from `@georeferencing/core/encoder-worker`.
307
+
308
+ ## Troubleshooting and license
309
+
310
+ | Symptom | Check |
311
+ | --- | --- |
312
+ | No export buttons / `EXPORT_DISABLED` | Register the intended format on the controller |
313
+ | Lazy module or codec load fails | Bundler output, asset base URL and CSP; retry after correcting deployment |
314
+ | JPEG appears unreferenced in another app | Keep both sidecars and configure/read the recorded CRS |
315
+ | PDF cannot capture a map | Loaded canvas layers, visible map size and CORS; omit optional map capture if unsupported |
316
+ | TIFF option or memory error | Compatible compression/predictor/no-data settings and output size |
317
+
318
+ MIT licensed, with bundled third-party notices in `dist/licenses`. Public TypeDoc
319
+ comments ship in declarations and are published as the
320
+ [API documentation](https://georeferencing-api-docs.gh.tobilg.com). Source, issues and contribution notes are in the
321
+ [GitHub repository](https://github.com/tobilg/georeferencing).
@@ -0,0 +1,9 @@
1
+ import type { Definitions, Exporter } from "@georeferencing/core";
2
+ /** @internal */
3
+ export declare const sessionExporter: Exporter;
4
+ /** @internal */
5
+ export declare function pointsExporter(definitions: Definitions): Exporter;
6
+ /** @internal */
7
+ export declare const accuracyExporter: Exporter;
8
+ /** @internal */
9
+ export declare const worldFileExporter: Exporter;
@@ -0,0 +1,57 @@
1
+ import { accuracyReport, exportPoints, worldFile } from "./serializers.js";
2
+ const textFile = (name, text, type = "application/json") => ({
3
+ name,
4
+ blob: new Blob([text], { type }),
5
+ });
6
+ /** @internal */
7
+ export const sessionExporter = {
8
+ async run({ document }) {
9
+ const file = textFile("session.json", JSON.stringify(document, null, 2));
10
+ return { blob: file.blob, files: [file] };
11
+ },
12
+ };
13
+ /** @internal */
14
+ export function pointsExporter(definitions) {
15
+ return {
16
+ async run({ document }) {
17
+ const file = textFile("image.points", exportPoints(document, definitions), "text/plain");
18
+ return { blob: file.blob, files: [file] };
19
+ },
20
+ };
21
+ }
22
+ /** @internal */
23
+ export const accuracyExporter = {
24
+ async run({ document, fit }) {
25
+ if (!fit)
26
+ throw Error("A valid alignment is required.");
27
+ const file = textFile("accuracy-report.json", accuracyReport(document, fit));
28
+ return { blob: file.blob, files: [file] };
29
+ },
30
+ };
31
+ /** @internal */
32
+ export const worldFileExporter = {
33
+ async run({ document, fit, file, engine, tag, signal, onProgress }) {
34
+ if (!fit || !file || !document.sourceImage)
35
+ throw Error("A matching image and fit are required.");
36
+ const placement = worldFile(fit, document.workingCrs, document.output.crs);
37
+ const result = await engine.run({ kind: "normalize", file, metadata: document.sourceImage }, tag, { signal, onProgress });
38
+ signal.throwIfAborted();
39
+ if (!result.blob)
40
+ throw Error("Image normalization did not return PNG bytes.");
41
+ return {
42
+ blob: result.blob,
43
+ worldFile: placement,
44
+ files: [
45
+ { name: "image.png", blob: result.blob },
46
+ textFile("image.pgw", placement.text, "text/plain"),
47
+ textFile("image.crs.json", JSON.stringify({
48
+ crs: placement.crs,
49
+ image: document.sourceImage,
50
+ documentId: document.id,
51
+ revision: document.documentRevision,
52
+ note: "Applies to the accompanying orientation-normalized original-resolution image.png",
53
+ }, null, 2)),
54
+ ],
55
+ };
56
+ },
57
+ };
package/dist/data.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional session, QGIS points, world-file and accuracy exports.
3
+ * @module data
4
+ */
5
+ import type { Definitions, ExportFormat } from "@georeferencing/core";
6
+ /** Register a complete version-1 JSON session download, even before a valid fit. */
7
+ export declare function session(): ExportFormat;
8
+ /** Register QGIS .points download, projecting targets with the supplied definitions. */
9
+ export declare function points(definitions?: Definitions): ExportFormat;
10
+ /** Register a full-precision JSON alignment report with training residual formulas. */
11
+ export declare function accuracy(): ExportFormat;
12
+ /** Register original-resolution normalized PNG, world file and explicit CRS sidecar. */
13
+ export declare function worldFile(): ExportFormat;
package/dist/data.js ADDED
@@ -0,0 +1,38 @@
1
+ /** Register a complete version-1 JSON session download, even before a valid fit. */
2
+ export function session() {
3
+ return {
4
+ id: "session",
5
+ label: "Export session",
6
+ requiresFit: false,
7
+ load: async () => (await import("./data-export.js")).sessionExporter,
8
+ };
9
+ }
10
+ /** Register QGIS .points download, projecting targets with the supplied definitions. */
11
+ export function points(definitions = {}) {
12
+ return {
13
+ id: "points",
14
+ label: "Export .points",
15
+ requiresFit: false,
16
+ load: async () => (await import("./data-export.js")).pointsExporter(definitions),
17
+ };
18
+ }
19
+ /** Register a full-precision JSON alignment report with training residual formulas. */
20
+ export function accuracy() {
21
+ return {
22
+ id: "accuracy",
23
+ label: "Accuracy report",
24
+ load: async () => (await import("./data-export.js")).accuracyExporter,
25
+ };
26
+ }
27
+ /** Register original-resolution normalized PNG, world file and explicit CRS sidecar. */
28
+ export function worldFile() {
29
+ return {
30
+ id: "world-file",
31
+ label: "World file & CRS",
32
+ unavailable: (doc) => ["linear", "helmert"].includes(doc.model) &&
33
+ doc.workingCrs === doc.output.crs
34
+ ? null
35
+ : "World-file output requires Linear/Helmert without reprojection.",
36
+ load: async () => (await import("./data-export.js")).worldFileExporter,
37
+ };
38
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional GeoTIFF export; import from `@georeferencing/plugins/geotiff`.
3
+ * @module geotiff
4
+ */
5
+ import type { ExportFormat } from "@georeferencing/core";
6
+ import type { RasterPluginOptions } from "./types.js";
7
+ export type { RasterPluginOptions } from "./types.js";
8
+ /**
9
+ * Register lazy GeoTIFF export. Encoding runs in a dedicated plugin worker using
10
+ * the controller engine's limits, projections, scheduler and cancellation.
11
+ * No workers or heavy encoder code load until the user requests this format.
12
+ */
13
+ export declare function geoTiff(options?: RasterPluginOptions): ExportFormat;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Register lazy GeoTIFF export. Encoding runs in a dedicated plugin worker using
3
+ * the controller engine's limits, projections, scheduler and cancellation.
4
+ * No workers or heavy encoder code load until the user requests this format.
5
+ */
6
+ export function geoTiff(options = {}) {
7
+ return {
8
+ id: "geotiff",
9
+ label: "Export GeoTIFF",
10
+ raster: true,
11
+ load: async () => {
12
+ const { renderAndEncode } = await import("./raster-export.js");
13
+ return {
14
+ run: (context) => renderAndEncode(context, "geotiff", {
15
+ ...options,
16
+ workerFactory: options.workerFactory ??
17
+ (options.workerUrl
18
+ ? undefined
19
+ : () => new Worker(new URL("./workers/geotiff.js", import.meta.url), { type: "module" })),
20
+ }),
21
+ };
22
+ },
23
+ };
24
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Optional lazy export formats. Prefer per-format imports to give bundlers the
3
+ * narrowest dependency graph. Register descriptors with ControllerOptions.exports.
4
+ * @module plugins
5
+ */
6
+ export { accuracy, points, session, worldFile } from "./data.js";
7
+ export { geoTiff } from "./geotiff.js";
8
+ export { jpeg } from "./jpeg.js";
9
+ export type { PdfOptions, ReportMap, ReportOptions, ReportView, } from "./pdf.js";
10
+ export { pdf } from "./pdf.js";
11
+ export type { JpegOptions, RasterPluginOptions } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Optional lazy export formats. Prefer per-format imports to give bundlers the
3
+ * narrowest dependency graph. Register descriptors with ControllerOptions.exports.
4
+ * @module plugins
5
+ */
6
+ export { accuracy, points, session, worldFile } from "./data.js";
7
+ export { geoTiff } from "./geotiff.js";
8
+ export { jpeg } from "./jpeg.js";
9
+ export { pdf } from "./pdf.js";
package/dist/jpeg.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional georeferenced JPEG export; import from `@georeferencing/plugins/jpeg`.
3
+ * @module jpeg
4
+ */
5
+ import type { ExportFormat } from "@georeferencing/core";
6
+ import type { JpegOptions } from "./types.js";
7
+ export type { JpegOptions } from "./types.js";
8
+ /**
9
+ * Register lazy JPEG export with a pixel-centre world file and explicit CRS/source
10
+ * JSON sidecar. Transparency is composited over the configured background.
11
+ * @throws GeoreferenceError For an invalid quality or RGB background.
12
+ */
13
+ export declare function jpeg(options?: JpegOptions): ExportFormat;
package/dist/jpeg.js ADDED
@@ -0,0 +1,33 @@
1
+ import { GeoreferenceError } from "@georeferencing/core";
2
+ /**
3
+ * Register lazy JPEG export with a pixel-centre world file and explicit CRS/source
4
+ * JSON sidecar. Transparency is composited over the configured background.
5
+ * @throws GeoreferenceError For an invalid quality or RGB background.
6
+ */
7
+ export function jpeg(options = {}) {
8
+ const quality = options.quality ?? 0.92;
9
+ const background = options.background ?? [255, 255, 255];
10
+ if (!Number.isFinite(quality) ||
11
+ quality < 0 ||
12
+ quality > 1 ||
13
+ background.length !== 3 ||
14
+ background.some((v) => !Number.isInteger(v) || v < 0 || v > 255))
15
+ throw new GeoreferenceError("JPEG_OPTIONS", "JPEG quality must be 0–1 and background must contain three RGB bytes.");
16
+ return {
17
+ id: "jpeg",
18
+ label: "Export JPEG",
19
+ raster: true,
20
+ load: async () => {
21
+ const { renderAndEncode } = await import("./raster-export.js");
22
+ return {
23
+ run: (context) => renderAndEncode(context, "jpeg", {
24
+ ...options,
25
+ workerFactory: options.workerFactory ??
26
+ (options.workerUrl
27
+ ? undefined
28
+ : () => new Worker(new URL("./workers/jpeg.js", import.meta.url), { type: "module" })),
29
+ }, { quality, background: [...background] }),
30
+ };
31
+ },
32
+ };
33
+ }