@geolibre/core 3.1.0 → 3.3.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/dist/index.d.mts CHANGED
@@ -1,2616 +1,6 @@
1
+ import { $ as BLANK_BASEMAP, $n as QuickFilterKind, $t as LayerPopupConfig, A as PlanetaryBasemap, An as OPENFREEMAP_BASEMAPS, Ar as isDefaultPrintLayout, At as FieldVisibility, B as getEllipsoid, Bn as ProcessingModelGraph, Br as Point, Bt as LabelDedupe, C as ELLIPSOIDS, Cn as ModelGraphNode, Cr as styleValue, Ct as DashboardWidget, D as PLANETARY_BASEMAP_GROUPS, Dn as ModelToolProvider, Dr as PrintLayoutLegendEntry, Dt as DiagramSizeMode, E as PLANETARY_BASEMAPS, En as ModelPortKind, Er as PrintLayoutCorner, Et as DiagramField, F as earthLengthToBody, Fn as PopupDateFormat, Fr as Feature, Ft as GeometryGeneratorType, G as AddTileLayerOptions, Gn as ProcessingRunStatus, Gr as availableCesiumBasemap, Gt as LayerDescriptiveMetadata, H as getPlanetaryBasemapByStyleUrl, Hn as ProcessingRerunRequest, Hr as CESIUM_BASEMAPS, Ht as LabelTransform, I as getActiveBodyRadiusRatio, In as PopupFieldConfig, Ir as FeatureCollection, It as IndicatorAggregation, J as AppRole, Jn as ProjectPluginControlPosition, Jr as sameCesiumImagery, Jt as LayerJoinStats, K as AppCapabilities, Kn as ProjectComment, Kr as basemapToCesiumImagery, Kt as LayerGroup, L as getActiveEllipsoid, Ln as PopupFieldFormat, Lr as Geometry, Lt as LARGE_VECTOR_FEATURE_THRESHOLD, M as PlanetaryBasemapSectionId, Mn as PROTOMAPS_BASEMAPS, Mr as printLayoutConfigsEqual, Mt as GeoLibreLayer, N as bodyLengthToEarth, Nn as ParticipantIdentity, Nr as scrubPrintLayoutForLayers, Nt as GeoLibreProject, O as PLANETARY_BASEMAP_SENTINEL_PREFIX, On as NETCDF_IMAGE_SOURCE_KIND, Or as PrintLayoutPageFilter, Ot as DiagramType, P as earthAreaToBody, Pn as PointRenderer, Pr as scrubPrintLayoutForRemovedLayers, Pt as GeocodingPreferences, Q as AttributeFormWidget, Qn as QuickFilterDateKind, Qt as LayerMetadataTemporalExtent, R as getActiveMeanRadiusMeters, Rn as PopupFieldKind, Rr as LineString, Rt as LAYER_TYPES, S as EARTH_MEAN_RADIUS_METERS, Sn as ModelGraphEdge, Sr as shouldUseTiledRendering, St as DUCKDB_VECTOR_ROUTE_BYTES, T as EllipsoidId, Tn as ModelGraphPort, Tr as PrintLayoutConfig, Tt as DashboardWidgetType, U as meanRadiusMeters, Un as ProcessingRun, Ur as CesiumBasemapId, Ut as LayerCapabilities, V as getPlanetaryBasemapById, Vn as ProcessingModelStep, Vr as Polygon, Vt as LabelStyle, W as setActiveEllipsoidId, Wn as ProcessingRunKind, Wr as CesiumBasemapImagery, Wt as LayerConnection, X as AttributeFormFieldConfig, Xn as ProjectPreferences, Xt as LayerMetadataContact, Y as AttributeFormConfig, Yn as ProjectPluginState, Yt as LayerLibraryEntry, Z as AttributeFormValueMapEntry, Zn as ProjectTemplateEntry, Zt as LayerMetadataLink, _ as unescapePolyline, _n as MapProjection, _r as VectorRule, _t as DEFAULT_MAP_GRID_LAYOUT, a as collectTransitiveJoinSourceIds, an as LegendCustomEntry, ar as StoryActiveSlideMode, at as CollaborationParticipant, b as transformGeojsonElevation, bn as MapViewState, br as detectNonGeographicCoordinates, bt as DEFAULT_STORY_MAP, c as stripJoinFields, cn as LegendItemOverride, cr as StoryChapterAnimation, ct as CollaborationState, d as batchDecodePolylines, dn as MAX_DASHBOARD_COLUMNS, dr as StoryLayerOpacityChange, dt as CommentReply, en as LayerQuickFilter, er as QuickFilterTextOperator, et as BLEND_MODES, f as decodePolyline, fn as MAX_MAP_GRID_DIM, fr as StoryMap, ft as DEFAULT_BASEMAP, g as polylineStrToGeoJSON, gn as MapPreferences, gr as StyleLibraryEntryKind, gt as DEFAULT_LEGEND_CONFIG, h as geoJSONToPolylineStr, hn as MapGridLayout, hr as StyleLibraryEntry, ht as DEFAULT_LAYER_STYLE, i as cascadeLayerJoinRefresh, in as LegendConfig, ir as SecondaryMapView, it as CollaborationMode, j as PlanetaryBasemapGroup, jn as PROJECT_VERSION, jr as normalizePrintLayoutConfig, jt as FillPattern, k as PLANET_SWITCHER_OPTIONS, kn as NonGeographicCoordinates, kr as createDefaultPrintLayout, kt as EditorTrackingConfig, l as PolylineBatchOptions, ln as LegendPanelPosition, lr as StoryChapterLocation, lt as CommentAnchor, m as encodePolyline, mn as MIN_DASHBOARD_COLUMNS, mr as StrokeWidthUnit, mt as DEFAULT_DASHBOARD_COLUMNS, n as applyJoinsToLayer, nn as LayerType, nr as RuntimeEnvironmentVariable, nt as CollabInvite, o as layerJoinKey, on as LegendCustomItem, or as StoryChapter, ot as CollaborationPresence, p as decodePolylineDetailed, pn as MAX_PROCESSING_HISTORY, pr as StorySlideMode, pt as DEFAULT_BLEND_MODE, q as AppPrivilege, qn as ProjectInteraction, qr as normalizeCesiumBasemap, qt as LayerJoin, r as applyLayerJoins, rn as LayerVirtualField, rr as SQL_QUERY_SOURCE_KIND, rt as CollaborationChatMessage, s as reapplyLayerJoins, sn as LegendCustomShape, sr as StoryChapterAlignment, st as CollaborationRole, t as ApplyLayerJoinsResult, tn as LayerStyle, tr as RecentProjectEntry, tt as BlendMode, u as PolylineDecodeResult, un as LineDecoration, ur as StoryInsetPosition, ut as CommentAuthor, v as geojsonHasZCoordinates, vn as MapRendererKind, vr as VectorStyleMode, vt as DEFAULT_PRIMARY_RENDERER, w as Ellipsoid, wn as ModelGraphNodeKind, wr as DEFAULT_PRINT_LAYOUT, wt as DashboardWidgetAggregation, x as DEFAULT_ELLIPSOID_ID, xn as MarkerShape, xr as isDuckDBQueryLayer, xt as DUCKDB_VECTOR_FEATURE_WARN_COUNT, y as horizontalBbox, yn as MapScaleUnit, yr as VectorStyleStop, yt as DEFAULT_PROJECT_PREFERENCES, z as getActiveSemiMajorAxisMeters, zn as ProcessingModel, zr as MultiPolygon, zt as LabelAnchor } from "./joins-CRZZcLqX.mjs";
1
2
  import "zustand";
2
3
  import "zundo";
3
- //#region src/cesium-imagery.d.ts
4
- /**
5
- * Maps the project's basemap onto imagery the Cesium globe can render.
6
- *
7
- * The 2D panes render `basemapStyleUrl` through MapLibre, which understands
8
- * vector GL styles and GeoLibre's `geolibre://` sentinels. Cesium understands
9
- * neither: it draws imagery from tile providers only. So a globe pane that
10
- * simply mounted the store basemap would show nothing, which is why the pane
11
- * used to hardcode Ion World Imagery (with a token) or OpenStreetMap (without)
12
- * and ignore the user's choice entirely — leaving a bright globe beside dark 2D
13
- * panes and breaking the "same map, different projection" promise the multi-map
14
- * grid makes.
15
- *
16
- * This module is the translation. It is deliberately **engine-free** — it
17
- * returns a plain descriptor and never imports Cesium — so it lives in
18
- * `@geolibre/core` beside the basemap catalogs it reads, the 2D and 3D
19
- * renderers share one source of truth, and the Cesium chunk boundary in
20
- * `vite.config.ts` stays intact.
21
- *
22
- * Three outcomes are possible, and the caller has to handle all three (see
23
- * {@link CesiumBasemapImagery}): tiles to draw, nothing to draw, or "no raster
24
- * equivalent exists — pick your own default".
25
- */
26
- /** Stable project IDs and the corresponding Cesium bundled thumbnails / ion assets. */
27
- export declare const CESIUM_BASEMAPS: readonly [{
28
- readonly id: "project";
29
- readonly name: "Project basemap";
30
- readonly icon: "naturalEarthII.png";
31
- }, {
32
- readonly id: "natural-earth";
33
- readonly name: "Natural Earth II";
34
- readonly icon: "naturalEarthII.png";
35
- }, {
36
- readonly id: "bing-aerial";
37
- readonly name: "Bing Maps Aerial";
38
- readonly icon: "bingAerial.png";
39
- readonly assetId: 2;
40
- }, {
41
- readonly id: "bing-labels";
42
- readonly name: "Bing Maps Aerial with Labels";
43
- readonly icon: "bingAerialLabels.png";
44
- readonly assetId: 3;
45
- }, {
46
- readonly id: "bing-roads";
47
- readonly name: "Bing Maps Roads";
48
- readonly icon: "bingRoads.png";
49
- readonly assetId: 4;
50
- }, {
51
- readonly id: "sentinel-2";
52
- readonly name: "Sentinel-2";
53
- readonly icon: "sentinel-2.png";
54
- readonly assetId: 3954;
55
- }, {
56
- readonly id: "blue-marble";
57
- readonly name: "Blue Marble";
58
- readonly icon: "blueMarble.png";
59
- readonly assetId: 3845;
60
- }, {
61
- readonly id: "earth-at-night";
62
- readonly name: "Earth at night";
63
- readonly icon: "earthAtNight.png";
64
- readonly assetId: 3812;
65
- }, {
66
- readonly id: "google-satellite";
67
- readonly name: "Google Maps Satellite";
68
- readonly icon: "googleSatellite.png";
69
- readonly assetId: 3830182;
70
- }, {
71
- readonly id: "google-labels";
72
- readonly name: "Google Maps Satellite with Labels";
73
- readonly icon: "googleSatelliteLabels.png";
74
- readonly assetId: 3830183;
75
- }, {
76
- readonly id: "google-roads";
77
- readonly name: "Google Maps Roadmap";
78
- readonly icon: "googleRoadmap.png";
79
- readonly assetId: 3830184;
80
- }, {
81
- readonly id: "google-contour";
82
- readonly name: "Google Maps Contour";
83
- readonly icon: "googleContour.png";
84
- readonly assetId: 3830186;
85
- }, {
86
- readonly id: "azure-aerial";
87
- readonly name: "Azure Maps Aerial";
88
- readonly icon: "azureAerial.png";
89
- readonly assetId: 3891168;
90
- }, {
91
- readonly id: "azure-roads";
92
- readonly name: "Azure Maps Roads";
93
- readonly icon: "azureRoads.png";
94
- readonly assetId: 3891169;
95
- }, {
96
- readonly id: "esri-imagery";
97
- readonly name: "ArcGIS World Imagery";
98
- readonly icon: "ArcGisMapServiceWorldImagery.png";
99
- readonly category: "Other";
100
- readonly service: "World_Imagery";
101
- }, {
102
- readonly id: "esri-hillshade";
103
- readonly name: "ArcGIS World Hillshade";
104
- readonly icon: "ArcGisMapServiceWorldHillshade.png";
105
- readonly category: "Other";
106
- readonly service: "Elevation/World_Hillshade";
107
- }, {
108
- readonly id: "esri-ocean";
109
- readonly name: "Esri World Ocean";
110
- readonly icon: "ArcGisMapServiceWorldOcean.png";
111
- readonly category: "Other";
112
- readonly service: "Ocean/World_Ocean_Base";
113
- }, {
114
- readonly id: "osm";
115
- readonly name: "OpenStreetMap";
116
- readonly icon: "openStreetMap.png";
117
- readonly category: "Other";
118
- }];
119
- export type CesiumBasemapId = (typeof CESIUM_BASEMAPS)[number]["id"];
120
- export declare function normalizeCesiumBasemap(value: unknown): CesiumBasemapId;
121
- /**
122
- * Missing credentials fall back to keyless imagery without changing the saved
123
- * choice.
124
- *
125
- * The fallback is ArcGIS World Imagery rather than the project background: a
126
- * globe wants to look like the Earth, and the project's own basemap is usually
127
- * a vector street style whose raster analogue is mostly empty ocean from
128
- * orbit. It needs no key, so the globe looks the same with or without an Ion
129
- * token — only terrain and the Ion-only basemaps are lost.
130
- */
131
- export declare function availableCesiumBasemap(value: unknown, hasIonToken: boolean): CesiumBasemapId;
132
- /**
133
- * What the globe should draw underneath the project's data layers.
134
- *
135
- * - `xyz` — a tile template to render, with the credit and zoom bounds that go
136
- * with it.
137
- * - `none` — the user chose the blank basemap; draw a bare ellipsoid.
138
- * - `default` — this basemap has no raster equivalent (a custom style URL, a
139
- * provider style, an offline archive). The renderer falls back to whatever it
140
- * can offer, which is Ion World Imagery when a token is configured and
141
- * OpenStreetMap otherwise.
142
- */
143
- export type CesiumBasemapImagery = {
144
- kind: "none";
145
- } | {
146
- kind: "default";
147
- } | {
148
- kind: "ion";
149
- assetId: number;
150
- } | {
151
- kind: "natural-earth";
152
- } | {
153
- kind: "arcgis";
154
- url: string;
155
- } | {
156
- kind: "xyz";
157
- /** Tile template with `{z}`/`{x}`/`{y}` placeholders. */
158
- template: string;
159
- /** Credit to show on the globe, as the HTML the 2D map already uses. */
160
- attribution: string;
161
- /** Max native zoom of the source, so the globe overzooms rather than 404s. */
162
- maximumLevel?: number;
163
- /**
164
- * Tile row ordering. `"tms"` numbers rows from the bottom; absent means
165
- * standard XYZ. Cesium has no `scheme` option, so a TMS template needs
166
- * `{y}` swapped for its `{reverseY}` placeholder at render time.
167
- */
168
- scheme?: "tms";
169
- /**
170
- * A transparent overlay drawn directly above {@link template} — the
171
- * roads-and-labels tiles a hybrid basemap (Amap Hybrid) bakes in, so one
172
- * basemap selection still yields a labelled satellite globe.
173
- */
174
- overlayTemplate?: string;
175
- };
176
- /**
177
- * Whether two descriptors would draw the same thing.
178
- *
179
- * Descriptors are built fresh on every call, so two basemaps resolving to the
180
- * same analogue — OpenFreeMap `liberty` and `bright` are both the streets
181
- * tone — yield equal but distinct objects. A renderer that remembers the
182
- * descriptor it drew (to skip redundant work) must compare by value: a
183
- * reference check would miss that case and tear down and re-add an identical
184
- * tile provider, costing a flash and a round of re-requested tiles for no
185
- * visible change.
186
- */
187
- export declare function sameCesiumImagery(a: CesiumBasemapImagery, b: CesiumBasemapImagery): boolean;
188
- /**
189
- * Translate a basemap style URL into imagery for the Cesium globe.
190
- *
191
- * Planetary and regional basemaps are already raster tile sets, so they carry
192
- * straight over — the globe shows the same Mars/Moon/Amap tiles the 2D panes
193
- * do. GeoLibre's own vector styles map to a keyless raster basemap of matching
194
- * tone, so switching the project to a dark basemap darkens the globe too.
195
- * Everything else reports `default`, leaving the choice of fallback imagery to
196
- * the renderer (which knows whether an Ion token is configured).
197
- *
198
- * A `geolibre://` sentinel that no longer resolves is treated as the default
199
- * basemap, mirroring what `resolveMapStyle` does for the 2D map rather than
200
- * handing the sentinel onward as if it were fetchable.
201
- *
202
- * @param styleUrl - The project's `basemapStyleUrl`.
203
- * @returns What the globe should draw beneath the data layers.
204
- */
205
- export declare function basemapToCesiumImagery(styleUrl: string | undefined, cesiumBasemap?: CesiumBasemapId): CesiumBasemapImagery;
206
- //#endregion
207
- //#region ../../node_modules/@types/geojson/index.d.ts
208
- /**
209
- * The value values for the "type" property of GeoJSON Objects.
210
- * https://tools.ietf.org/html/rfc7946#section-1.4
211
- */
212
- type GeoJsonTypes = GeoJSON$1["type"];
213
- /**
214
- * Bounding box
215
- * https://tools.ietf.org/html/rfc7946#section-5
216
- */
217
- type BBox = [number, number, number, number] | [number, number, number, number, number, number];
218
- /**
219
- * A Position is an array of coordinates.
220
- * https://tools.ietf.org/html/rfc7946#section-3.1.1
221
- * Array should contain between two and three elements.
222
- * The previous GeoJSON specification allowed more elements (e.g., which could be used to represent M values),
223
- * but the current specification only allows X, Y, and (optionally) Z to be defined.
224
- *
225
- * Note: the type will not be narrowed down to `[number, number] | [number, number, number]` due to
226
- * marginal benefits and the large impact of breaking change.
227
- *
228
- * See previous discussions on the type narrowing:
229
- * - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21590|Nov 2017}
230
- * - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/67773|Dec 2023}
231
- * - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/71441| Dec 2024}
232
- *
233
- * One can use a
234
- * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|user-defined type guard that returns a type predicate}
235
- * to determine if a position is a 2D or 3D position.
236
- *
237
- * @example
238
- * import type { Position } from 'geojson';
239
- *
240
- * type StrictPosition = [x: number, y: number] | [x: number, y: number, z: number]
241
- *
242
- * function isStrictPosition(position: Position): position is StrictPosition {
243
- * return position.length === 2 || position.length === 3
244
- * };
245
- *
246
- * let position: Position = [-116.91, 45.54];
247
- *
248
- * let x: number;
249
- * let y: number;
250
- * let z: number | undefined;
251
- *
252
- * if (isStrictPosition(position)) {
253
- * // `tsc` would throw an error if we tried to destructure a fourth parameter
254
- * [x, y, z] = position;
255
- * } else {
256
- * throw new TypeError("Position is not a 2D or 3D point");
257
- * }
258
- */
259
- type Position = number[];
260
- /**
261
- * The base GeoJSON object.
262
- * https://tools.ietf.org/html/rfc7946#section-3
263
- * The GeoJSON specification also allows foreign members
264
- * (https://tools.ietf.org/html/rfc7946#section-6.1)
265
- * Developers should use "&" type in TypeScript or extend the interface
266
- * to add these foreign members.
267
- */
268
- interface GeoJsonObject {
269
- // Don't include foreign members directly into this type def.
270
- // in order to preserve type safety.
271
- // [key: string]: any;
272
- /**
273
- * Specifies the type of GeoJSON object.
274
- */
275
- type: GeoJsonTypes;
276
- /**
277
- * Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.
278
- * The value of the bbox member is an array of length 2*n where n is the number of dimensions
279
- * represented in the contained geometries, with all axes of the most southwesterly point
280
- * followed by all axes of the more northeasterly point.
281
- * The axes order of a bbox follows the axes order of geometries.
282
- * https://tools.ietf.org/html/rfc7946#section-5
283
- */
284
- bbox?: BBox | undefined;
285
- }
286
- /**
287
- * Union of GeoJSON objects.
288
- */
289
- type GeoJSON$1<G extends Geometry | null = Geometry, P = GeoJsonProperties> = G | Feature<G, P> | FeatureCollection<G, P>;
290
- /**
291
- * Geometry object.
292
- * https://tools.ietf.org/html/rfc7946#section-3
293
- */
294
- type Geometry = Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection;
295
- /**
296
- * Point geometry object.
297
- * https://tools.ietf.org/html/rfc7946#section-3.1.2
298
- */
299
- interface Point extends GeoJsonObject {
300
- type: "Point";
301
- coordinates: Position;
302
- }
303
- /**
304
- * MultiPoint geometry object.
305
- * https://tools.ietf.org/html/rfc7946#section-3.1.3
306
- */
307
- interface MultiPoint extends GeoJsonObject {
308
- type: "MultiPoint";
309
- coordinates: Position[];
310
- }
311
- /**
312
- * LineString geometry object.
313
- * https://tools.ietf.org/html/rfc7946#section-3.1.4
314
- */
315
- interface LineString extends GeoJsonObject {
316
- type: "LineString";
317
- coordinates: Position[];
318
- }
319
- /**
320
- * MultiLineString geometry object.
321
- * https://tools.ietf.org/html/rfc7946#section-3.1.5
322
- */
323
- interface MultiLineString extends GeoJsonObject {
324
- type: "MultiLineString";
325
- coordinates: Position[][];
326
- }
327
- /**
328
- * Polygon geometry object.
329
- * https://tools.ietf.org/html/rfc7946#section-3.1.6
330
- */
331
- interface Polygon extends GeoJsonObject {
332
- type: "Polygon";
333
- coordinates: Position[][];
334
- }
335
- /**
336
- * MultiPolygon geometry object.
337
- * https://tools.ietf.org/html/rfc7946#section-3.1.7
338
- */
339
- interface MultiPolygon extends GeoJsonObject {
340
- type: "MultiPolygon";
341
- coordinates: Position[][][];
342
- }
343
- /**
344
- * Geometry Collection
345
- * https://tools.ietf.org/html/rfc7946#section-3.1.8
346
- */
347
- interface GeometryCollection<G extends Geometry = Geometry> extends GeoJsonObject {
348
- type: "GeometryCollection";
349
- geometries: G[];
350
- }
351
- type GeoJsonProperties = {
352
- [name: string]: any;
353
- } | null;
354
- /**
355
- * A feature object which contains a geometry and associated properties.
356
- * https://tools.ietf.org/html/rfc7946#section-3.2
357
- */
358
- interface Feature<G extends Geometry | null = Geometry, P = GeoJsonProperties> extends GeoJsonObject {
359
- type: "Feature";
360
- /**
361
- * The feature's geometry
362
- */
363
- geometry: G;
364
- /**
365
- * A value that uniquely identifies this feature in a
366
- * https://tools.ietf.org/html/rfc7946#section-3.2.
367
- */
368
- id?: string | number | undefined;
369
- /**
370
- * Properties associated with this feature.
371
- */
372
- properties: P;
373
- }
374
- /**
375
- * A collection of feature objects.
376
- * https://tools.ietf.org/html/rfc7946#section-3.3
377
- */
378
- interface FeatureCollection<G extends Geometry | null = Geometry, P = GeoJsonProperties> extends GeoJsonObject {
379
- type: "FeatureCollection";
380
- features: Array<Feature<G, P>>;
381
- }
382
- //#endregion
383
- //#region src/print-layout-config.d.ts
384
- /**
385
- * Print Layout composer settings that belong to a project.
386
- *
387
- * The Print Layout dialog composes a printable page out of the current map:
388
- * a title block, page size and orientation, the cartographic furniture
389
- * (legend, scale bar, north arrow), optional data blocks, and an atlas /
390
- * map-series definition. All of that describes the *project's* map document,
391
- * so it round-trips through `.geolibre.json` (GeoLibre discussion #1992 —
392
- * before this, reopening a project lost the title, page format and
393
- * orientation, and the dialog kept showing the previous project's settings).
394
- *
395
- * Deliberately **not** in here: everything that is per-session rather than
396
- * part of the document — the captured map image, export progress, error and
397
- * clipboard notices, the current atlas page, and the dialog's own chrome
398
- * (panel widths, dialog size).
399
- *
400
- * The unions below are the storage contract. Their app-side counterparts
401
- * (`PaperSizeId`, `Orientation`, `BodyCorner`, ... in
402
- * `apps/geolibre-desktop/src/lib/`) are checked against these by ordinary
403
- * assignment in the dialog's snapshot/restore paths, so adding a paper size or
404
- * a corner on one side without the other fails the build.
405
- */
406
- /** A page corner an overlay block can be pinned to. */
407
- export type PrintLayoutCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
408
- /** Which features a data block covers when the atlas paginates the map. */
409
- export type PrintLayoutPageFilter = "all" | "contained" | "intersecting";
410
- /** One hand-authored swatch in the composer's user-defined legend. */
411
- export interface PrintLayoutLegendEntry {
412
- id: string;
413
- label: string;
414
- color: string;
415
- }
416
- export interface PrintLayoutConfig {
417
- title: string;
418
- subtitle: string;
419
- titlePlacement: "outside" | "inside";
420
- titleAlign: "left" | "center" | "right";
421
- paperSize: "a4" | "a3" | "letter" | "legal" | "tabloid" | "fullhd" | "hd" | "uhd4k" | "square" | "custom";
422
- orientation: "portrait" | "landscape";
423
- customWidth: number;
424
- customHeight: number;
425
- customUnit: "mm" | "px";
426
- pageMargin: "normal" | "narrow" | "none";
427
- showPageBorder: boolean;
428
- pageBorderColor: string;
429
- pageBorderWidth: number;
430
- mapBorderColor: string;
431
- mapBorderWidth: number;
432
- mapBackground: string;
433
- showTitle: boolean;
434
- showSubtitle: boolean;
435
- showLegend: boolean;
436
- showScaleBar: boolean;
437
- showNorthArrow: boolean;
438
- navigationGrouped: boolean;
439
- showFooter: boolean;
440
- footerText: string;
441
- showDate: boolean;
442
- /** Blank means "today", resolved when the page is drawn. */
443
- dateText: string;
444
- showAttribution: boolean;
445
- showColorbar: boolean;
446
- colorbarRamp: string;
447
- /** Kept as typed text so a half-entered value survives a round trip. */
448
- colorbarMin: string;
449
- colorbarMax: string;
450
- colorbarLabel: string;
451
- colorbarOrientation: "vertical" | "horizontal";
452
- colorbarLength: number;
453
- colorbarPosition: PrintLayoutCorner;
454
- showCustomLegend: boolean;
455
- customLegendTitle: string;
456
- customLegendEntries: PrintLayoutLegendEntry[];
457
- customLegendPosition: PrintLayoutCorner;
458
- showDataTable: boolean;
459
- tableLayerId: string;
460
- tableTitle: string;
461
- tableColumns: string[];
462
- tableSortField: string;
463
- tableSortDesc: boolean;
464
- tableMaxRows: number;
465
- tableFitRows: boolean;
466
- tablePosition: PrintLayoutCorner;
467
- tablePageFilter: PrintLayoutPageFilter;
468
- tableFilterToAtlasFeature: boolean;
469
- showDataChart: boolean;
470
- chartLayerId: string;
471
- chartTitle: string;
472
- chartType: "bar" | "pie" | "line";
473
- chartCategoryField: string;
474
- chartAggregation: "count" | "sum" | "mean";
475
- chartValueField: string;
476
- chartPosition: PrintLayoutCorner;
477
- chartPageFilter: PrintLayoutPageFilter;
478
- showInfoBlock: boolean;
479
- author: string;
480
- projectNumber: string;
481
- crs: string;
482
- revision: string;
483
- captureMode: "viewport" | "extent";
484
- /** Drawn print extent as `[west, south, east, north]`; null when unset. */
485
- extentBbox: [number, number, number, number] | null;
486
- atlasEnabled: boolean;
487
- atlasLayerId: string;
488
- atlasCoverage: "features" | "line";
489
- atlasSegmentKm: string;
490
- atlasNameField: string;
491
- atlasExtentMode: "margin" | "scale";
492
- atlasMarginPct: number;
493
- atlasMaskEnabled: boolean;
494
- atlasScale: string;
495
- atlasSortField: string;
496
- atlasSortDescending: boolean;
497
- atlasFilter: string;
498
- atlasFilenamePattern: string;
499
- }
500
- /**
501
- * Settings a project starts with, and the shape every partial or hand-edited
502
- * `printLayout` is filled out against. Also the dialog's initial state, so
503
- * "new project" and "a project saved before this feature" compose identically.
504
- */
505
- export declare const DEFAULT_PRINT_LAYOUT: PrintLayoutConfig;
506
- /** A fresh copy of the defaults, safe to hand to a store or a component. */
507
- export declare function createDefaultPrintLayout(): PrintLayoutConfig;
508
- /**
509
- * Coerce an untrusted (possibly hand-edited, possibly partial) `printLayout`
510
- * into a complete {@link PrintLayoutConfig}, filling every missing or
511
- * malformed field from {@link DEFAULT_PRINT_LAYOUT}.
512
- *
513
- * @param value - The raw `printLayout` value read from a project file.
514
- * @returns The normalized config, or null when the project carries none, so
515
- * callers can tell "absent" from "explicitly default".
516
- */
517
- export declare function normalizePrintLayoutConfig(value: unknown): PrintLayoutConfig | null;
518
- /**
519
- * Whether a config is still the untouched default, so a project that never
520
- * opened the composer serializes without a `printLayout` key and stays
521
- * byte-identical to one saved before this feature.
522
- */
523
- export declare function isDefaultPrintLayout(config: PrintLayoutConfig): boolean;
524
- /**
525
- * Structural equality for two configs. Used to skip no-op store writes, so
526
- * merely opening the Print Layout dialog never marks a project dirty.
527
- */
528
- export declare function printLayoutConfigsEqual(a: PrintLayoutConfig, b: PrintLayoutConfig): boolean;
529
- /**
530
- * Drop references to layers the project no longer carries, so a composer that
531
- * pointed at a since-deleted layer opens with the block cleared instead of
532
- * rendering nothing from a dangling id. Mirrors the widget/comment/legend
533
- * scrubbing in `applyProjectToStore`.
534
- *
535
- * @param config - The config to scrub.
536
- * @param existingLayerIds - Ids of the layers present in the project.
537
- * @returns The same object when nothing referenced a missing layer.
538
- */
539
- export declare function scrubPrintLayoutForLayers(config: PrintLayoutConfig, existingLayerIds: ReadonlySet<string>): PrintLayoutConfig;
540
- /**
541
- * The delete-time counterpart, matching `scrubLegendForRemovedLayers` and its
542
- * siblings: called when layers are removed from the open project so the saved
543
- * file never carries a block pointing at a layer that is already gone.
544
- *
545
- * @param config - The config to scrub.
546
- * @param layerIds - The removed layer id, or ids.
547
- * @returns The same object when no block referenced a removed layer.
548
- */
549
- export declare function scrubPrintLayoutForRemovedLayers(config: PrintLayoutConfig, layerIds: string | Iterable<string>): PrintLayoutConfig;
550
- //#endregion
551
- //#region src/types.d.ts
552
- export declare const OPENFREEMAP_BASEMAPS: readonly [{
553
- readonly id: "liberty";
554
- readonly name: "Liberty";
555
- readonly styleUrl: "https://tiles.openfreemap.org/styles/liberty";
556
- }, {
557
- readonly id: "liberty-3d";
558
- readonly name: "Liberty 3D";
559
- readonly styleUrl: "https://tiles.openfreemap.org/styles/liberty";
560
- }, {
561
- readonly id: "positron";
562
- readonly name: "Positron";
563
- readonly styleUrl: "https://tiles.openfreemap.org/styles/positron";
564
- }, {
565
- readonly id: "bright";
566
- readonly name: "Bright";
567
- readonly styleUrl: "https://tiles.openfreemap.org/styles/bright";
568
- }, {
569
- readonly id: "dark";
570
- readonly name: "Dark";
571
- readonly styleUrl: "https://tiles.openfreemap.org/styles/dark";
572
- }, {
573
- readonly id: "fiord";
574
- readonly name: "Fiord";
575
- readonly styleUrl: "https://tiles.openfreemap.org/styles/fiord";
576
- }];
577
- /**
578
- * Protomaps v5 basemap flavors. These are resolved to full style URLs at use
579
- * time by `getProtomapsStyleUrl`, which injects the `VITE_PROTOMAPS_API_KEY`
580
- * runtime env var. The key is only present in builds configured with it (e.g.
581
- * the GitHub Pages web demo), so consumers should hide these options when no
582
- * key is available.
583
- */
584
- export declare const PROTOMAPS_BASEMAPS: readonly [{
585
- readonly id: "protomaps-light";
586
- readonly name: "Light";
587
- readonly flavor: "light";
588
- }, {
589
- readonly id: "protomaps-dark";
590
- readonly name: "Dark";
591
- readonly flavor: "dark";
592
- }, {
593
- readonly id: "protomaps-white";
594
- readonly name: "White";
595
- readonly flavor: "white";
596
- }, {
597
- readonly id: "protomaps-grayscale";
598
- readonly name: "Grayscale";
599
- readonly flavor: "grayscale";
600
- }, {
601
- readonly id: "protomaps-black";
602
- readonly name: "Black";
603
- readonly flavor: "black";
604
- }];
605
- export declare const DEFAULT_BASEMAP = "https://tiles.openfreemap.org/styles/liberty";
606
- export declare const BLANK_BASEMAP = "";
607
- export declare const PROJECT_VERSION = "0.2.0";
608
- /**
609
- * Every layer type, as a runtime list so untrusted input (an imported Layer
610
- * Library bundle, a hand-edited project) can be validated against it.
611
- * {@link LayerType} is derived from this array, so the two cannot drift.
612
- */
613
- export declare const LAYER_TYPES: readonly ["geojson", "raster", "wms", "wmts", "xyz", "vector-tiles", "arcgis", "pmtiles", "mbtiles", "zarr", "lidar", "gaussian-splat", "3d-tiles", "cog", "flatgeobuf", "geoparquet", "duckdb-query", "deckgl-viz", "video", "image"];
614
- export type LayerType = (typeof LAYER_TYPES)[number];
615
- export type VectorStyleMode = "single" | "graduated" | "categorized" | "rule-based" | "expression";
616
- /**
617
- * One entry in a {@link LayerStyle.vectorRules} rule-based renderer. Each rule
618
- * pairs a MapLibre filter expression (a boolean expression array, serialized as
619
- * JSON) with a symbol color. Rules are evaluated top to bottom; the first whose
620
- * filter matches wins. The catch-all rule (`isElse`) has no filter and supplies
621
- * the color for features no other rule matched. Mirrors the QGIS/ArcGIS
622
- * rule-based renderer.
623
- */
624
- export interface VectorRule {
625
- id: string;
626
- /** Human-readable label shown in the editor and the legend. */
627
- label: string;
628
- /**
629
- * A MapLibre boolean filter expression serialized as JSON, e.g.
630
- * `["==", ["get", "TYPE"], "park"]`. Ignored when {@link isElse} is set or
631
- * the JSON is invalid.
632
- */
633
- filter: string;
634
- /** The symbol fill/circle color for features this rule matches (6-digit hex). */
635
- color: string;
636
- /**
637
- * When true this is the catch-all "else" rule: {@link filter} is unused and
638
- * the rule is an unconditional fallback for features no other rule matched,
639
- * so the {@link minZoom}/{@link maxZoom} and {@link parentId} fields are
640
- * ignored on it. Its symbol overrides and {@link enabled} toggle do apply.
641
- */
642
- isElse: boolean;
643
- /**
644
- * Whether the rule participates in rendering. `false` temporarily disables
645
- * the rule (and, for a group, its whole subtree) without deleting it,
646
- * mirroring the QGIS rule checkbox. Absent means enabled.
647
- */
648
- enabled?: boolean;
649
- /**
650
- * Lowest zoom (inclusive) the rule applies at, mirroring MapLibre's layer
651
- * `minzoom` convention. Absent means no lower bound.
652
- */
653
- minZoom?: number;
654
- /**
655
- * Zoom the rule stops applying at (exclusive, like MapLibre's layer
656
- * `maxzoom`). Absent means no upper bound. Together with {@link minZoom}
657
- * this is the QGIS per-rule "scale range".
658
- */
659
- maxZoom?: number;
660
- /**
661
- * The id of the parent rule when this rule is nested inside a group (QGIS
662
- * rule tree). A child rule matches only features that also match every
663
- * ancestor's filter, and inherits the intersection of the ancestors' zoom
664
- * ranges and enabled state. A rule that has children acts as a group: its
665
- * own symbol is not rendered; only leaf rules draw. Absent or dangling
666
- * means a top-level rule.
667
- */
668
- parentId?: string;
669
- /**
670
- * Per-rule stroke/outline color override (6-digit hex): the polygon outline
671
- * and circle stroke color for matching features. Absent inherits the layer
672
- * {@link LayerStyle.strokeColor}.
673
- */
674
- strokeColor?: string;
675
- /**
676
- * Per-rule stroke width override in pixels: the line width (lines and
677
- * polygon outlines) and circle stroke width for matching features. Absent
678
- * inherits the layer {@link LayerStyle.strokeWidth}.
679
- */
680
- strokeWidth?: number;
681
- /**
682
- * Per-rule fill/circle opacity override (0..1) for matching features.
683
- * Absent inherits the layer {@link LayerStyle.fillOpacity}.
684
- */
685
- fillOpacity?: number;
686
- /**
687
- * Per-rule circle radius override in pixels for matching point features.
688
- * Absent inherits the layer {@link LayerStyle.circleRadius}.
689
- */
690
- circleRadius?: number;
691
- }
692
- /**
693
- * The fill pattern applied to polygon layers. `"none"` keeps a flat fill; the
694
- * named patterns are generated as recolorable sprite tiles; `"svg"` rasterizes
695
- * the user-supplied markup in {@link LayerStyle.fillPatternSvg}.
696
- */
697
- export type FillPattern = "none" | "hatch" | "cross-hatch" | "horizontal" | "vertical" | "dots" | "svg";
698
- /**
699
- * The built-in marker shape for a point layer, or `"custom"` to rasterize the
700
- * user-supplied SVG in {@link LayerStyle.markerSvg}. Built-in shapes are drawn
701
- * on a sprite tile and recolored via {@link LayerStyle.markerColor}.
702
- */
703
- export type MarkerShape = "circle" | "square" | "triangle" | "diamond" | "star" | "cross" | "pin" | "custom";
704
- /**
705
- * How a point layer is rendered: as individual markers, a density heatmap, or
706
- * clustered bubbles. Only applies to point geometry.
707
- */
708
- export type PointRenderer = "single" | "heatmap" | "cluster";
709
- /**
710
- * The repeated decoration symbol drawn along line features (and polygon
711
- * outlines), or `"none"` when decorations are off. Mirrors the QGIS
712
- * marker-line / arrow symbol layers: `"arrow"` renders directional arrowheads
713
- * that follow the line, the other shapes render as repeated markers.
714
- */
715
- export type LineDecoration = "none" | "arrow" | "triangle" | "circle" | "square";
716
- /**
717
- * The per-feature derived geometry rendered by the geometry generator, or
718
- * `"none"` when the generator is off. Mirrors QGIS geometry-generator symbol
719
- * layers: each feature's derived geometry (its centroid, bounding box, convex
720
- * hull, or a buffer) is drawn as an extra symbol over the layer's normal
721
- * symbology. `"centroid"` derives points; the rest derive polygons.
722
- */
723
- export type GeometryGeneratorType = "none" | "centroid" | "bounding-box" | "convex-hull" | "buffer";
724
- /**
725
- * Unit a stroke/line width is measured in. `"pixels"` is constant screen space;
726
- * `"meters"` is ground distance, so the rendered width scales with the map
727
- * scale (zoom). See {@link LayerStyle.strokeWidthUnit}.
728
- */
729
- export type StrokeWidthUnit = "pixels" | "meters";
730
- /**
731
- * The chart drawn on top of each feature by the diagram renderer (QGIS-style
732
- * diagram symbology), or `"none"` when diagrams are off. Diagrams visualize
733
- * several numeric attributes per feature at once — e.g. election results by
734
- * party per county — and render through the shared deck.gl overlay on the
735
- * feature's point location or polygon centroid.
736
- */
737
- export type DiagramType = "none" | "pie" | "donut" | "bar" | "stacked-bar";
738
- /**
739
- * How the overall diagram size is determined.
740
- *
741
- * - `"fixed"`: every diagram renders at {@link LayerStyle.diagramSize} pixels.
742
- * - `"sum"`: scaled by the sum of the mapped attribute values, so the largest
743
- * total renders at {@link LayerStyle.diagramSize} pixels (area-true square
744
- * root scaling).
745
- * - `"attribute"`: scaled the same way by the single numeric attribute in
746
- * {@link LayerStyle.diagramSizeProperty}.
747
- */
748
- export type DiagramSizeMode = "fixed" | "sum" | "attribute";
749
- /** One attribute rendered as a slice/bar of a feature diagram. */
750
- export interface DiagramField {
751
- /** Numeric feature property visualized by this slice/bar. */
752
- property: string;
753
- /** Slice/bar color (6-digit hex). */
754
- color: string;
755
- }
756
- export interface VectorStyleStop {
757
- value: string | number;
758
- color: string;
759
- label?: string;
760
- }
761
- /** Attribute-driven labeling for a vector layer (rendered as a MapLibre symbol layer). */
762
- export interface LabelStyle {
763
- /** Whether labels are shown for the layer. */
764
- enabled: boolean;
765
- /** Attribute field whose value becomes the label text. */
766
- field: string;
767
- /**
768
- * Optional MapLibre expression (JSON string) for the label text, which
769
- * overrides {@link field} when non-empty (e.g. concatenating several fields).
770
- */
771
- expression: string;
772
- /** `"point"` labels at the feature/centroid; `"line"` places them along lines. */
773
- placement: "point" | "line";
774
- /** Label text size in pixels. */
775
- size: number;
776
- /** CSS color string for the label text. */
777
- color: string;
778
- /** CSS color string for the text halo drawn behind the label. */
779
- haloColor: string;
780
- /** Width of the text halo in pixels. */
781
- haloWidth: number;
782
- /** Scale range for labels; `0` / `24` inherit the layer's own zoom range. */
783
- minZoom: number;
784
- maxZoom: number;
785
- /** Let labels overlap instead of hiding colliding ones. */
786
- allowOverlap: boolean;
787
- /**
788
- * Where the label sits relative to its anchor point (MapLibre `text-anchor`),
789
- * e.g. `"top"` places the text above the point. Ignored for line placement.
790
- */
791
- anchor: LabelAnchor;
792
- /** Horizontal label offset in ems (MapLibre `text-offset` x). */
793
- offsetX: number;
794
- /** Vertical label offset in ems (MapLibre `text-offset` y; positive is down). */
795
- offsetY: number;
796
- /** Label rotation in degrees clockwise (MapLibre `text-rotate`). */
797
- rotation: number;
798
- /** Maximum line width in ems before the label wraps (MapLibre `text-max-width`). */
799
- maxWidth: number;
800
- /** Letter-case transform applied to the label text (MapLibre `text-transform`). */
801
- transform: LabelTransform;
802
- /**
803
- * Render a numeric {@link field} with the locale's thousands and decimal
804
- * separators (issue #2336), so `1234567.5` labels as `1,234,567.5` instead
805
- * of running together. Non-numeric values are unaffected, and it is not
806
- * applied to {@link expression}, which formats its own output (MapLibre's
807
- * `number-format`, offered in the Expression Builder).
808
- */
809
- numberFormatEnabled: boolean;
810
- /** Decimal places kept while {@link numberFormatEnabled} is on (0-10). */
811
- numberDecimals: number;
812
- /**
813
- * BCP 47 tag picking the separators for {@link numberFormatEnabled}, from
814
- * {@link LABEL_NUMBER_LOCALES}. Empty (the default) follows the app's own
815
- * language, the way popup number fields do.
816
- */
817
- numberLocale: string;
818
- /**
819
- * How to handle features that share a label.
820
- *
821
- * - `"off"`: every feature is labeled (the historical behavior).
822
- * - `"unique"`: features stacked at the same point are collapsed to a single
823
- * label, so co-located points (e.g. several antennas at one cell site) do not
824
- * stack overlapping text.
825
- * - `"concatenate"`: co-located points are merged into one label that joins
826
- * their distinct {@link field} values, one per line.
827
- *
828
- * Applies to point layers rendered through the inline GeoJSON path and uses
829
- * {@link field} (not {@link expression}) as the label value.
830
- */
831
- dedupe: LabelDedupe;
832
- /**
833
- * Data-defined override for {@link size}: a MapLibre expression (JSON
834
- * string) producing a number, e.g. sizing labels by population. Empty means
835
- * "use the literal {@link size}". Like the other data-defined overrides
836
- * below, it reads source feature attributes, so it is skipped while
837
- * {@link dedupe} collapsing is active (the aggregated features carry only
838
- * the label value).
839
- */
840
- sizeExpression: string;
841
- /**
842
- * Data-defined override for {@link color}: a MapLibre expression (JSON
843
- * string) producing a color, e.g. coloring labels by category.
844
- */
845
- colorExpression: string;
846
- /**
847
- * Data-defined label opacity: a MapLibre expression (JSON string) producing
848
- * a number in 0..1. When set it replaces the layer-wide opacity for labels
849
- * (wrapping it would invalidate top-level `["zoom"]` interpolations).
850
- */
851
- opacityExpression: string;
852
- /**
853
- * Per-feature label visibility: a MapLibre expression (JSON string)
854
- * producing a boolean. Features evaluating false get no label (e.g. hide
855
- * labels below an attribute threshold). Combined with the layer's other
856
- * feature filters.
857
- */
858
- visibilityExpression: string;
859
- /**
860
- * Per-feature placement priority: a MapLibre expression (JSON string)
861
- * producing a number, applied as `symbol-sort-key`. Labels with lower
862
- * values are placed first, so they win when space is tight.
863
- */
864
- priorityExpression: string;
865
- }
866
- /** MapLibre `text-anchor` positions offered for {@link LabelStyle.anchor}. */
867
- export type LabelAnchor = "center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
868
- /** Letter-case transform offered for {@link LabelStyle.transform}. */
869
- export type LabelTransform = "none" | "uppercase" | "lowercase";
870
- /** Duplicate-label handling offered for {@link LabelStyle.dedupe}. */
871
- export type LabelDedupe = "off" | "unique" | "concatenate";
872
- /**
873
- * Cartographic blend modes offered for {@link LayerStyle.blendMode}.
874
- *
875
- * The list is bounded by what WebGL *fixed-function* blending can express --
876
- * one `blendFunc` factor pair plus one `blendEquation` -- because that is the
877
- * only blend state MapLibre's renderer exposes, and it applies to colour and
878
- * alpha together. Two consequences shaped this list:
879
- *
880
- * - Modes needing the destination colour inside a fragment shader (`overlay`,
881
- * `color-dodge`, `soft-light`, `difference`, and the non-separable HSL modes)
882
- * are absent. MapLibre draws straight into the map framebuffer, which a
883
- * shader cannot sample, so computing them would need an extra copy pass.
884
- * - `darken` and `subtract` are absent even though `MIN` and
885
- * `FUNC_REVERSE_SUBTRACT` look like they would serve. Both also apply to the
886
- * alpha channel, where they drive the map canvas transparent: `MIN` takes the
887
- * whole canvas to `min(0, dst) = 0` everywhere the layer does not cover, and
888
- * `FUNC_REVERSE_SUBTRACT` leaves `dstA - srcA` inside it. Correcting either
889
- * needs `blendEquationSeparate` / `blendFuncSeparate`, which MapLibre's state
890
- * tracker does not offer.
891
- *
892
- * Every mode here leaves the canvas opaque and leaves the map untouched
893
- * wherever the layer contributes nothing. See `docs/user-guide/layers.md`.
894
- */
895
- export declare const BLEND_MODES: readonly ["normal", "multiply", "screen", "lighten", "add"];
896
- /**
897
- * How a layer's pixels combine with the map beneath it. `"normal"` is ordinary
898
- * alpha compositing (the default, and what every layer did before blend modes
899
- * existed). See {@link BLEND_MODES}.
900
- */
901
- export type BlendMode = (typeof BLEND_MODES)[number];
902
- /**
903
- * The mode a layer renders with unless it says otherwise. Declared separately
904
- * from {@link DEFAULT_LAYER_STYLE} so consumers get a non-optional
905
- * {@link BlendMode} to fall back to: `blendMode` is optional on
906
- * {@link LayerStyle}, so `DEFAULT_LAYER_STYLE.blendMode` is typed
907
- * `BlendMode | undefined` however it is initialized.
908
- */
909
- export declare const DEFAULT_BLEND_MODE: BlendMode;
910
- export interface LayerStyle {
911
- minZoom: number;
912
- maxZoom: number;
913
- fillColor: string;
914
- strokeColor: string;
915
- strokeWidth: number;
916
- /**
917
- * Unit the {@link strokeWidth} value is expressed in.
918
- *
919
- * - `"pixels"` (default): a constant screen-space width that never changes
920
- * with zoom — the historical behavior.
921
- * - `"meters"`: a ground-distance width, so the rendered line stays
922
- * proportional to the map scale (thicker zoomed in, thinner zoomed out),
923
- * matching QGIS "map units". Only affects line/polygon-outline rendering;
924
- * point/circle outlines remain pixel-based.
925
- */
926
- strokeWidthUnit: StrokeWidthUnit;
927
- fillOpacity: number;
928
- circleRadius: number;
929
- textColor: string;
930
- textHaloColor: string;
931
- textHaloWidth: number;
932
- textSize: number;
933
- labels: LabelStyle;
934
- extrusionEnabled: boolean;
935
- extrusionColor: string;
936
- extrusionOpacity: number;
937
- extrusionHeightProperty: string;
938
- extrusionHeightScale: number;
939
- extrusionBase: number;
940
- extrusionAdvancedStyleEnabled: boolean;
941
- extrusionColorExpression: string;
942
- extrusionHeightExpression: string;
943
- /**
944
- * When true, a vector layer whose coordinates carry Z values (e.g. a GPX
945
- * track with elevations) renders in true 3D through the shared deck.gl
946
- * overlay instead of MapLibre's flat 2D layers, so features sit at their
947
- * own altitude. Orthogonal to {@link extrusionEnabled}, which extrudes flat
948
- * polygons by an attribute; only one of the two should be on at a time.
949
- */
950
- elevation3dEnabled: boolean;
951
- /** Multiplier applied to each coordinate's Z value (vertical exaggeration). */
952
- elevation3dVerticalScale: number;
953
- /** Constant altitude offset in meters added after the vertical scale. */
954
- elevation3dOffset: number;
955
- vectorStyleMode: VectorStyleMode;
956
- vectorStyleProperty: string;
957
- vectorStyleClassCount: number;
958
- vectorStyleColorRamp: string;
959
- vectorStyleClassificationScheme: string;
960
- vectorStyleStops: VectorStyleStop[];
961
- vectorStyleExpression: string;
962
- /**
963
- * Ordered rules for the `"rule-based"` {@link vectorStyleMode}. Compiled to a
964
- * MapLibre `case` color expression (first matching filter wins, catch-all
965
- * last). See {@link VectorRule}.
966
- */
967
- vectorRules: VectorRule[];
968
- /**
969
- * When true, the point circle radius (or line width) is sized by a numeric
970
- * field via an `interpolate` between {@link proportionalSizeMinValue} ..
971
- * {@link proportionalSizeMaxValue} mapped onto {@link proportionalSizeMinRadius}
972
- * .. {@link proportionalSizeMaxRadius} (QGIS "graduated → size" / proportional
973
- * symbols). Orthogonal to the color {@link vectorStyleMode}.
974
- */
975
- proportionalSizeEnabled: boolean;
976
- proportionalSizeProperty: string;
977
- proportionalSizeMinValue: number;
978
- proportionalSizeMaxValue: number;
979
- proportionalSizeMinRadius: number;
980
- proportionalSizeMaxRadius: number;
981
- /**
982
- * Polygon fill pattern. `"none"` keeps the flat fill; other values render a
983
- * recolorable sprite tile ({@link fillPatternColor}); `"svg"` rasterizes
984
- * {@link fillPatternSvg}. See {@link FillPattern}.
985
- */
986
- fillPattern: FillPattern;
987
- fillPatternColor: string;
988
- /** Raw SVG markup (or a data URL) used when {@link fillPattern} is `"svg"`. */
989
- fillPatternSvg: string;
990
- /**
991
- * When true, point features render as a marker icon ({@link markerShape})
992
- * instead of a plain circle. Built-in shapes are recolored via
993
- * {@link markerColor}; `"custom"` rasterizes {@link markerSvg}.
994
- */
995
- markerEnabled: boolean;
996
- markerShape: MarkerShape;
997
- markerColor: string;
998
- markerSize: number;
999
- /** Raw SVG markup (or a data URL) used when {@link markerShape} is `"custom"`. */
1000
- markerSvg: string;
1001
- /**
1002
- * When true, per-feature [simplestyle-spec](https://github.com/mapbox/simplestyle-spec)
1003
- * properties (`fill`, `fill-opacity`, `stroke`, `stroke-width`,
1004
- * `stroke-opacity`, `marker-color`) override the flat layer style on a
1005
- * per-feature basis. Set automatically when a GeoJSON layer is added whose
1006
- * features carry these properties (e.g. styled KML/KMZ), so embedded
1007
- * symbology renders without manual configuration.
1008
- */
1009
- simpleStyleEnabled: boolean;
1010
- /**
1011
- * Per-feature chart symbology (QGIS-style diagrams). `"none"` disables it;
1012
- * any other value renders one {@link DiagramType} chart per feature over the
1013
- * layer's normal symbology, built from the numeric attributes in
1014
- * {@link diagramFields}. See `@geolibre/core`'s `diagram.ts` helpers.
1015
- */
1016
- diagramType: DiagramType;
1017
- /** Attributes (and their colors) charted by the diagram renderer, in order. */
1018
- diagramFields: DiagramField[];
1019
- /** How the per-feature diagram size is determined. */
1020
- diagramSizeMode: DiagramSizeMode;
1021
- /**
1022
- * Diagram size in pixels: the rendered diameter/height for `"fixed"` sizing,
1023
- * or the diameter/height of the largest feature for scaled sizing.
1024
- */
1025
- diagramSize: number;
1026
- /** Numeric attribute driving `"attribute"` sizing (see {@link DiagramSizeMode}). */
1027
- diagramSizeProperty: string;
1028
- /** Minimum zoom at which diagrams are drawn, to avoid clutter when zoomed out. */
1029
- diagramMinZoom: number;
1030
- /**
1031
- * When true, diagrams that would overlap an already-placed diagram on screen
1032
- * are skipped (largest first), decluttering dense areas. Recomputed as the
1033
- * view changes.
1034
- */
1035
- diagramDeclutter: boolean;
1036
- pointRenderer: PointRenderer;
1037
- heatmapRadius: number;
1038
- heatmapIntensity: number;
1039
- /** Built-in color ramp used by the heatmap density renderer. */
1040
- heatmapColorRamp: string;
1041
- /** Numeric feature property used as heatmap weight; blank gives every point equal weight. */
1042
- heatmapWeightProperty: string;
1043
- clusterRadius: number;
1044
- clusterMaxZoom: number;
1045
- /**
1046
- * When true, the polygon fill renders *inverted*: the area outside the
1047
- * features is filled (with {@link fillColor}/{@link fillOpacity}) and the
1048
- * features themselves become holes, mirroring the QGIS "Inverted polygons"
1049
- * renderer. Feature outlines still render normally. Only applies to layers
1050
- * with polygon geometry; ignored while {@link extrusionEnabled} is on.
1051
- */
1052
- invertedFillEnabled: boolean;
1053
- /**
1054
- * Repeated decoration symbol drawn along line features and polygon outlines
1055
- * (QGIS marker-line / arrow lines). `"none"` disables it. See
1056
- * {@link LineDecoration}.
1057
- */
1058
- lineDecoration: LineDecoration;
1059
- /**
1060
- * Decoration symbol color (6-digit hex). An empty string inherits
1061
- * {@link strokeColor} so decorations follow the stroke by default.
1062
- */
1063
- lineDecorationColor: string;
1064
- /** Decoration symbol size in pixels. */
1065
- lineDecorationSize: number;
1066
- /** Distance between consecutive decoration symbols in pixels. */
1067
- lineDecorationSpacing: number;
1068
- /**
1069
- * Per-feature derived geometry drawn over the layer's normal symbology
1070
- * (QGIS geometry generator). `"none"` disables it. See
1071
- * {@link GeometryGeneratorType}.
1072
- */
1073
- geometryGenerator: GeometryGeneratorType;
1074
- /** Buffer distance in meters for the `"buffer"` generator. */
1075
- geometryGeneratorBufferDistance: number;
1076
- /**
1077
- * Attribute driving the `"buffer"` generator's distance, so each feature is
1078
- * buffered by its own value in meters (QGIS data-defined override on a
1079
- * geometry-generator symbol). An empty string buffers every feature by the
1080
- * flat {@link geometryGeneratorBufferDistance}, which also stands in for
1081
- * features whose value is missing or non-numeric.
1082
- */
1083
- geometryGeneratorBufferProperty: string;
1084
- /** Fill color (6-digit hex) for generated polygons and centroid points. */
1085
- geometryGeneratorFillColor: string;
1086
- /** Outline color (6-digit hex) for generated geometry. */
1087
- geometryGeneratorStrokeColor: string;
1088
- /** Outline width in pixels for generated geometry. */
1089
- geometryGeneratorStrokeWidth: number;
1090
- /** Fill opacity (0..1) for generated polygons and centroid points. */
1091
- geometryGeneratorOpacity: number;
1092
- /** Circle radius in pixels for generated centroid points. */
1093
- geometryGeneratorCircleRadius: number;
1094
- /**
1095
- * Attribute driving the radius of generated centroid points, scaling them
1096
- * between {@link geometryGeneratorSizeMinRadius} and
1097
- * {@link geometryGeneratorSizeMaxRadius} across
1098
- * {@link geometryGeneratorSizeMinValue} ..
1099
- * {@link geometryGeneratorSizeMaxValue} (proportional symbols on the derived
1100
- * centroids). An empty string draws every centroid at the flat
1101
- * {@link geometryGeneratorCircleRadius}.
1102
- *
1103
- * Deliberately separate from {@link proportionalSizeProperty}: that one also
1104
- * drives line width, so reusing it here would resize a polygon layer's
1105
- * outlines as a side effect of sizing its centroids.
1106
- */
1107
- geometryGeneratorSizeProperty: string;
1108
- geometryGeneratorSizeMinValue: number;
1109
- geometryGeneratorSizeMaxValue: number;
1110
- geometryGeneratorSizeMinRadius: number;
1111
- geometryGeneratorSizeMaxRadius: number;
1112
- rasterBrightnessMin: number;
1113
- rasterBrightnessMax: number;
1114
- rasterSaturation: number;
1115
- rasterContrast: number;
1116
- rasterHueRotate: number;
1117
- /**
1118
- * How this layer composites onto whatever is drawn beneath it. `"normal"`
1119
- * (the default) is ordinary alpha compositing; `"multiply"` is the classic
1120
- * cartographic case of laying colour over a hillshade so the relief still
1121
- * reads through. Applies to the layer's own rendered geometry and raster
1122
- * tiles, not to its labels, which stay legible on top. See
1123
- * {@link BLEND_MODES} for the available modes and why the list is what it is.
1124
- */
1125
- blendMode?: BlendMode;
1126
- }
1127
- export declare const DEFAULT_LAYER_STYLE: LayerStyle;
1128
- /**
1129
- * Read a layer style property, falling back to the shared default when the
1130
- * layer does not define it. Shared by `@geolibre/map` and the desktop app so
1131
- * the two consumers cannot drift.
1132
- */
1133
- export declare function styleValue<K extends keyof LayerStyle>(style: LayerStyle, key: K): LayerStyle[K];
1134
- /**
1135
- * Feature-count threshold above which a local vector (GeoJSON) layer is rendered
1136
- * through client-side vector tiles (geojson-vt / supercluster served by a custom
1137
- * MapLibre protocol) instead of one in-memory geojson source pushed via
1138
- * `setData`. Small layers stay on the simpler inline path. Mirrors the
1139
- * `MAX_CEREUS_FEATURES` precedent in the desktop SQL engine.
1140
- */
1141
- export declare const LARGE_VECTOR_FEATURE_THRESHOLD = 50000;
1142
- /**
1143
- * Decide whether a GeoJSON layer should use the tiled rendering path.
1144
- *
1145
- * @param geojson - The layer's feature collection (may be undefined for
1146
- * non-vector layers).
1147
- * @returns `true` when the collection exceeds
1148
- * {@link LARGE_VECTOR_FEATURE_THRESHOLD} features.
1149
- */
1150
- export declare function shouldUseTiledRendering(geojson: GeoJSON.FeatureCollection | undefined): boolean;
1151
- /**
1152
- * Match statistics from the last time a {@link LayerJoin} was applied, shown in
1153
- * the Joins UI so silent key mismatches are visible (mirrors QGIS's join
1154
- * feedback). Recomputed on every apply; persisted harmlessly with the join.
1155
- */
1156
- export interface LayerJoinStats {
1157
- /** Target features whose key matched a join-table row. */
1158
- matchedCount: number;
1159
- /** Target features with no matching join-table row (their joined columns are null). */
1160
- unmatchedTargetCount: number;
1161
- /** Join-table rows (with a non-empty key) that matched no target feature. */
1162
- unmatchedJoinCount: number;
1163
- }
1164
- /**
1165
- * A persistent attribute join attached to a layer (QGIS Layer Properties →
1166
- * Joins): a live left join that augments the layer's attribute table with
1167
- * columns from another layer (typically a geometry-less table added via
1168
- * Delimited Text with no coordinate fields) matched on a key field. Unlike the
1169
- * Processing → Vector attribute join, the layer keeps its identity — styles,
1170
- * labels, and position — and the joined columns refresh whenever the join
1171
- * table's data changes. Definitions persist in `.geolibre.json` and re-resolve
1172
- * on project load. See `joins.ts` for the engine.
1173
- */
1174
- export interface LayerJoin {
1175
- /** Stable id for list edits. */
1176
- id: string;
1177
- /** Id of the layer providing the joined columns (its geometry is ignored). */
1178
- joinLayerId: string;
1179
- /** Key field on the layer that owns this join. */
1180
- targetField: string;
1181
- /** Key field on the join layer. */
1182
- joinField: string;
1183
- /**
1184
- * Join-layer fields to bring over; `undefined` brings every field except the
1185
- * key. Names are the join layer's own (pre-prefix) field names.
1186
- */
1187
- fields?: string[];
1188
- /** Optional prefix prepended to every joined column name (as in QGIS). */
1189
- prefix?: string;
1190
- /** `false` detaches the joined columns without deleting the definition. */
1191
- enabled?: boolean;
1192
- /**
1193
- * Bookkeeping written by the engine: the output column names this join added
1194
- * to the layer's features. Applying joins strips these first, which makes
1195
- * re-application idempotent without keeping a duplicate copy of the base
1196
- * data (base columns always win a name collision, so a joined column never
1197
- * shadows one). Not user-editable.
1198
- */
1199
- addedFields?: string[];
1200
- /** Last-run match statistics; see {@link LayerJoinStats}. */
1201
- stats?: LayerJoinStats;
1202
- }
1203
- /** Edit-widget kinds the Attribute Form designer can assign to a field. */
1204
- export type AttributeFormWidget = "text" | "number" | "range" | "checkbox" | "date" | "valueMap";
1205
- /** One selectable entry of a `valueMap` widget (stored value + display label). */
1206
- export interface AttributeFormValueMapEntry {
1207
- /** The value written to the feature property (compared as a string). */
1208
- value: string;
1209
- /** Human-readable label shown in the dropdown; defaults to {@link value}. */
1210
- label?: string;
1211
- }
1212
- /**
1213
- * Per-field configuration authored in the Attribute Form designer (layer
1214
- * properties → Attributes Form, QGIS-style): which edit widget the attribute
1215
- * editing surfaces render for the field, plus optional expression-based
1216
- * constraints and conditional visibility. Consumed by the attribute table's
1217
- * inline editor and the Field Collection capture form; helpers live in
1218
- * `attribute-form.ts`.
1219
- */
1220
- export interface AttributeFormFieldConfig {
1221
- /** Feature property key this configuration applies to. */
1222
- field: string;
1223
- widget: AttributeFormWidget;
1224
- /** Display label override shown in forms instead of the raw field name. */
1225
- alias?: string;
1226
- /**
1227
- * When true, a null/empty value fails validation. Checkbox widgets are
1228
- * exempt: unchecked is a valid state, not a missing value.
1229
- */
1230
- required?: boolean;
1231
- /** Dropdown entries for the `valueMap` widget. */
1232
- valueMap?: AttributeFormValueMapEntry[];
1233
- /** Lower bound for `number`/`range` widgets (inclusive). */
1234
- min?: number;
1235
- /** Upper bound for `number`/`range` widgets (inclusive). */
1236
- max?: number;
1237
- /** Step for the `range` widget's input. */
1238
- step?: number;
1239
- /**
1240
- * Boolean MapLibre expression that must evaluate to `true` against the
1241
- * feature's (candidate) properties for the value to be accepted, e.g.
1242
- * `[">", ["get", "population"], 0]`. Stored as the expression source string
1243
- * the Expression Builder edits.
1244
- */
1245
- constraintExpression?: string;
1246
- /** Human-readable message shown when the constraint fails. */
1247
- constraintDescription?: string;
1248
- /**
1249
- * Boolean MapLibre expression controlling whether the field is shown in
1250
- * attribute forms; `false` hides the field (and skips its validation).
1251
- * Empty/invalid expressions fail open so a typo cannot hide data entry.
1252
- */
1253
- visibilityExpression?: string;
1254
- }
1255
- /** The Attribute Form designer's whole per-layer configuration. */
1256
- export interface AttributeFormConfig {
1257
- fields: AttributeFormFieldConfig[];
1258
- }
1259
- /**
1260
- * How a popup renders one field's value (issue #2113). `"auto"` reproduces the
1261
- * untyped rendering the Identify popup has always done — sanitized KML
1262
- * `description` markup, an inline `data:image/*;base64` value as a thumbnail,
1263
- * anything else stringified. The remaining kinds are explicit author choices
1264
- * so a popup never has to guess from the value.
1265
- */
1266
- export type PopupFieldKind = "auto" | "text" | "number" | "date" | "link" | "image";
1267
- /** How a `"date"` field's value is written out. */
1268
- export type PopupDateFormat = "date" | "datetime" | "time" | "iso" | "year";
1269
- /** Value formatting for one popup field. Every part is optional. */
1270
- export interface PopupFieldFormat {
1271
- /** Fixed number of decimals for a `"number"` field. */
1272
- decimals?: number;
1273
- /** Group thousands with the locale's separator (`"number"` fields). */
1274
- thousands?: boolean;
1275
- /** Date rendering for a `"date"` field; defaults to `"date"`. */
1276
- dateFormat?: PopupDateFormat;
1277
- /** Text placed before the formatted value. */
1278
- prefix?: string;
1279
- /** Text placed after the formatted value, e.g. a unit suffix. */
1280
- suffix?: string;
1281
- /** Link text for a `"link"` field; the value itself is used when unset. */
1282
- linkLabel?: string;
1283
- }
1284
- /** One field's entry in a layer's popup configuration. */
1285
- export interface PopupFieldConfig {
1286
- /** Feature property key. */
1287
- field: string;
1288
- /** Display label shown instead of the raw field name. */
1289
- label?: string;
1290
- /** How the value renders; defaults to `"auto"`. */
1291
- kind?: PopupFieldKind;
1292
- format?: PopupFieldFormat;
1293
- /** Include this field in the hover tooltip's short subset. */
1294
- hover?: boolean;
1295
- }
1296
- /**
1297
- * Per-layer configuration for what a viewer sees when they interact with a
1298
- * feature (issue #2113): which fields the Identify popup shows, in what order,
1299
- * under what labels and formatting, plus an optional hover tooltip.
1300
- *
1301
- * A layer with no `popup` block behaves exactly as it did before this existed:
1302
- * the layer name as the heading, then every property as a key/value row.
1303
- * {@link GeoLibreLayer.fieldVisibility} stays authoritative — a `"hidden"` or
1304
- * `"excluded"` field is dropped even when a popup config names it.
1305
- */
1306
- export interface LayerPopupConfig {
1307
- /** `false` suppresses the Identify popup for this layer. Defaults to `true`. */
1308
- click?: boolean;
1309
- /** `true` shows a hover tooltip built from the `hover` fields. Defaults to `false`. */
1310
- hover?: boolean;
1311
- /** Field whose value titles the popup instead of the layer name. */
1312
- titleField?: string;
1313
- /**
1314
- * MapLibre expression source producing the popup title. Wins over
1315
- * {@link titleField}; when it fails or produces nothing the title falls
1316
- * through to `titleField`, and only then to the layer name.
1317
- */
1318
- titleExpression?: string;
1319
- /**
1320
- * MapLibre expression source producing the whole popup body as text, for
1321
- * authors who want a sentence rather than a table. When it evaluates, it
1322
- * replaces the field rows.
1323
- */
1324
- bodyExpression?: string;
1325
- /** `false` drops the synthetic `id` row. Defaults to `true`. */
1326
- showFeatureId?: boolean;
1327
- /**
1328
- * Widest the click popup may grow, in CSS pixels. Unset keeps the default
1329
- * (520px, or 420px for a popup carrying an image). Clamped to the range
1330
- * `resolvePopupMaxWidth` enforces and always capped by the viewport, so a
1331
- * value wider than the window still leaves the map usable.
1332
- */
1333
- maxWidth?: number;
1334
- /**
1335
- * Tallest an `"image"` field's thumbnail may draw inside the popup, in CSS
1336
- * pixels. Unset keeps the default (`min(50vh, 420px)`). Clamped by
1337
- * `resolvePopupImageHeight`. Pair it with {@link maxWidth} for a
1338
- * bigger picture: the thumbnail keeps its aspect ratio, so widening the
1339
- * popup is what lets a landscape photo use the extra height.
1340
- */
1341
- imageHeight?: number;
1342
- /**
1343
- * The fields to show and their order. An empty or absent list keeps today's
1344
- * behavior: every visible property, in the feature's own key order.
1345
- */
1346
- fields?: PopupFieldConfig[];
1347
- }
1348
- /**
1349
- * A virtual field attached to a vector layer (QGIS Field Calculator → "Create
1350
- * virtual field", issue #1321): a column defined by a MapLibre expression that
1351
- * recomputes live instead of being written once as static values. The engine
1352
- * (`virtual-fields.ts`) materializes the computed values into the layer's
1353
- * feature properties — so the attribute table, Expression Builder,
1354
- * data-driven styling, labels, and selection all see the column with no
1355
- * further wiring — and re-derives them whenever the layer's data (or its
1356
- * joins) change. Definitions persist in `.geolibre.json` and re-resolve on
1357
- * project load; the expression is a declarative MapLibre expression (never
1358
- * arbitrary code), so re-evaluating it from a shared project file is safe.
1359
- */
1360
- export interface LayerVirtualField {
1361
- /** Stable id for list edits. */
1362
- id: string;
1363
- /** The output column name. A name already taken by a base column is skipped. */
1364
- name: string;
1365
- /**
1366
- * MapLibre expression source (JSON text, e.g. `["/", ["get", "pop"],
1367
- * ["get", "area_km2"]]`) evaluated against each feature.
1368
- */
1369
- expression: string;
1370
- /** `false` detaches the computed column without deleting the definition. */
1371
- enabled?: boolean;
1372
- /**
1373
- * Bookkeeping written by the engine: the column name actually materialized
1374
- * on the last apply, absent when the field was disabled, failed to compile,
1375
- * or was skipped because the name collided with an existing column.
1376
- * Applying virtual fields strips these first, which makes re-application
1377
- * idempotent (an existing column is never shadowed, so stripping exactly
1378
- * restores the pre-apply properties). Not user-editable.
1379
- */
1380
- addedField?: string;
1381
- /** Compile error from the last apply, when the expression failed to parse. */
1382
- error?: string;
1383
- /**
1384
- * Features whose evaluation threw at runtime on the last apply (their cell
1385
- * is null). Surfaced so the UI can warn without one bad feature aborting
1386
- * the whole column.
1387
- */
1388
- errorCount?: number;
1389
- }
1390
- /**
1391
- * Quick filters (issue #2114): the data-driven filter controls a layer offers
1392
- * in its Quick Filters section. What persists is the *control state* — field,
1393
- * kind, chosen values — not the compiled output; `quick-filters.ts` compiles it
1394
- * to a MapLibre filter at sync time so a saved filter can always be reopened
1395
- * and edited.
1396
- */
1397
- /** Which control a quick filter renders, and how it compiles. */
1398
- export type QuickFilterKind = "categorical" | "range" | "date" | "text";
1399
- /** The comparison a `text` quick filter applies (always case-insensitive). */
1400
- export type QuickFilterTextOperator = "contains" | "startsWith" | "equals";
1401
- /**
1402
- * How a date field stores its values, deciding whether a `date` quick filter
1403
- * compares ISO text or epoch numbers. `iso` covers both `YYYY-MM-DD` and full
1404
- * `YYYY-MM-DDTHH:MM:SSZ` timestamps: only the leading `YYYY-MM-DD` slice is
1405
- * compared, so a trailing time, a milliseconds fraction, or a `Z` cannot break
1406
- * a boundary. Mirrors (deliberately, in a smaller form) the value kinds the
1407
- * Time Slider's `detectValueKind` reports.
1408
- */
1409
- export type QuickFilterDateKind = "iso" | "epochMs" | "epochS";
1410
- /** A single filter control persisted on a layer. */
1411
- export interface LayerQuickFilter {
1412
- /** Stable id, used as the React key and for edits/removal. */
1413
- id: string;
1414
- /** The feature property this control narrows. */
1415
- field: string;
1416
- kind: QuickFilterKind;
1417
- /**
1418
- * `false` keeps the control configured but inert, so a filter can be muted
1419
- * without losing the values chosen for it. Defaults to enabled.
1420
- */
1421
- enabled?: boolean;
1422
- /**
1423
- * `categorical`: the chosen values. An empty (or omitted) selection means
1424
- * "every value" and compiles to nothing — unchecking the last box clears the
1425
- * filter rather than emptying the map.
1426
- */
1427
- values?: (string | number | boolean)[];
1428
- /** `range`: inclusive bounds. `null`/omitted leaves that side open. */
1429
- min?: number | null;
1430
- max?: number | null;
1431
- /**
1432
- * `date`: inclusive `YYYY-MM-DD` bounds. The end day is included in full, so
1433
- * a timestamp field filtered to a single day keeps that whole day.
1434
- */
1435
- start?: string | null;
1436
- end?: string | null;
1437
- /** `date`: how the field stores its values. Defaults to `iso`. */
1438
- dateKind?: QuickFilterDateKind;
1439
- /** `text`: the comparison. Defaults to `contains`. */
1440
- operator?: QuickFilterTextOperator;
1441
- /** `text`: the needle. Blank means no constraint. */
1442
- text?: string;
1443
- }
1444
- /** Persisted refresh policy and most recent synchronization result for a layer. */
1445
- export interface LayerConnection {
1446
- /** Owning layer id. Repeated here so records remain self-describing when exported. */
1447
- layerId: string;
1448
- /** Automatic refresh cadence in seconds, or null for manual synchronization only. */
1449
- interval: number | null;
1450
- /** ISO timestamp of the most recent successful synchronization. */
1451
- lastSyncedAt: string | null;
1452
- /** Most recent synchronization error. Cleared by a successful synchronization. */
1453
- lastError: string | null;
1454
- /** Whether a failed synchronization retains the last good data or clears it. */
1455
- onFailure: "keep-last" | "clear";
1456
- }
1457
- /**
1458
- * Configuration for automatic feature editor tracking (Issue #1677).
1459
- * Maintains `created_by`, `created_at`, `edited_by`, `edited_at` fields
1460
- * automatically when features are created or updated.
1461
- */
1462
- export interface EditorTrackingConfig {
1463
- /** `true` enables automatic creation/edit timestamp and author stamping. */
1464
- enabled: boolean;
1465
- /** Field name for creation author (default `"created_by"`). */
1466
- createdByField?: string;
1467
- /** Field name for creation timestamp (default `"created_at"`). */
1468
- createdAtField?: string;
1469
- /** Field name for last-edit author (default `"edited_by"`). */
1470
- editedByField?: string;
1471
- /** Field name for last-edit timestamp (default `"edited_at"`). */
1472
- editedAtField?: string;
1473
- }
1474
- /**
1475
- * Visibility of a layer's attribute field.
1476
- * - "hidden": Not shown in the attribute table, identify popup, tooltips, or field pickers, but remains in the data.
1477
- * - "excluded": Removed entirely from the data when the project is shared or exported.
1478
- */
1479
- export type FieldVisibility = "hidden" | "excluded";
1480
- /**
1481
- * Explicit capability flags defining what actions a user/session may perform on a layer.
1482
- * Any omitted capability falls back to the inferred default behavior for that layer's source.
1483
- */
1484
- export interface LayerCapabilities {
1485
- /** Can the layer's features or attributes be queried / identified. */
1486
- query?: boolean;
1487
- /** Can new features be created/added to the layer. */
1488
- create?: boolean;
1489
- /** Can existing features/attributes be modified. */
1490
- update?: boolean;
1491
- /** Can features be deleted from the layer. */
1492
- delete?: boolean;
1493
- /** Can the layer data/symbology be exported/downloaded. */
1494
- export?: boolean;
1495
- }
1496
- /**
1497
- * Application privilege identifiers defining discrete capabilities in GeoLibre.
1498
- */
1499
- export type AppPrivilege = "layers:edit" | "layers:add-remote" | "layers:add-local" | "processing:run" | "processing:sidecar" | "project:save" | "project:share" | "project:share-public" | "plugins:install" | "assistant:use" | "connections:manage" | "export:data" | "export:image" | "settings:manage";
1500
- /**
1501
- * Standard named roles bundling application privileges.
1502
- */
1503
- export type AppRole = "viewer" | "editor" | "publisher" | "administrator" | "custom";
1504
- /**
1505
- * Ephemeral application capabilities state defining the active role, effective privileges,
1506
- * and optional restriction reason for the current session/deployment.
1507
- */
1508
- export interface AppCapabilities {
1509
- /**
1510
- * The assigned application role. `custom` once an ad-hoc `grantAppPrivilege` /
1511
- * `revokeAppPrivilege` has moved the set away from the bundle a named role
1512
- * defines, so the role never claims a shape the privileges do not have.
1513
- */
1514
- role: AppRole;
1515
- /** List of granted privileges for the active role or custom configuration. */
1516
- privileges: AppPrivilege[];
1517
- /**
1518
- * Optional human-readable reason covering the whole set (e.g. "Action disabled
1519
- * by deployment policy"), used for any privilege without its own.
1520
- */
1521
- reason?: string;
1522
- /**
1523
- * Per-privilege reasons, and why they exist: two privileges can be withheld by
1524
- * different causes — a role bundle plus a licence limit, say — and a single
1525
- * `reason` would make the second revocation relabel the first, so every gate
1526
- * would explain itself with whichever cause happened to be recorded last.
1527
- * Takes precedence over `reason` for the privileges it names.
1528
- */
1529
- privilegeReasons?: Partial<Record<AppPrivilege, string>>;
1530
- }
1531
- export interface GeoLibreLayer {
1532
- id: string;
1533
- name: string;
1534
- type: LayerType;
1535
- source: Record<string, unknown>;
1536
- visible: boolean;
1537
- opacity: number;
1538
- style: LayerStyle;
1539
- metadata: Record<string, unknown>;
1540
- beforeId?: string;
1541
- geojson?: FeatureCollection;
1542
- /**
1543
- * Explicit capability set for the layer (query, create, update, delete, export).
1544
- * Unset capabilities default to the inferred behavior for the layer's source kind.
1545
- */
1546
- capabilities?: LayerCapabilities;
1547
- /**
1548
- * Automatic editor tracking configuration for feature creation/updates.
1549
- */
1550
- editorTracking?: EditorTrackingConfig;
1551
- /**
1552
- * Field-level visibility overrides. Fields marked as "excluded" are physically
1553
- * removed from the data during export and sharing.
1554
- */
1555
- fieldVisibility?: Record<string, FieldVisibility>;
1556
- /**
1557
- * Per-field edit-widget, constraint, and visibility configuration authored
1558
- * in the Attribute Form designer. Applied by the attribute editing surfaces
1559
- * (attribute table inline editor, Field Collection capture form); persists
1560
- * with the project like {@link joins}.
1561
- */
1562
- attributeForm?: AttributeFormConfig;
1563
- /**
1564
- * Popup and hover-tooltip design for this layer, authored in the Style
1565
- * panel's Popup section. Absent means the default full-property dump.
1566
- */
1567
- popup?: LayerPopupConfig;
1568
- /**
1569
- * Persistent attribute joins applied to this layer's features, in order.
1570
- * The joined columns are materialized into `geojson` feature properties (so
1571
- * the attribute table, Expression Builder, styling, and labels all see them)
1572
- * and re-derived whenever the layer's or a join table's data changes.
1573
- */
1574
- joins?: LayerJoin[];
1575
- /**
1576
- * Expression-backed virtual fields computed for this layer's features, in
1577
- * order. Applied after joins (so an expression can read joined columns) and
1578
- * materialized into `geojson` feature properties; re-derived whenever the
1579
- * layer's data changes. See {@link LayerVirtualField}.
1580
- */
1581
- virtualFields?: LayerVirtualField[];
1582
- /**
1583
- * Transient MapLibre filter expression applied on top of every rendered
1584
- * sub-layer's geometry filter. The Time Slider plugin sets this on a bound
1585
- * vector layer so scrubbing the timeline narrows the visible features to the
1586
- * current time window, while the layer's own styling and opacity stay
1587
- * untouched. It is derived from the slider's current date, so it is NOT
1588
- * persisted (stripped by `prepareLayerForSave`); the binding config lives in
1589
- * `metadata.timeBinding` and the filter is recomputed live on the next
1590
- * activation. `undefined` means no time filter is applied.
1591
- */
1592
- timeFilter?: unknown[];
1593
- /** Transient MapLibre expression applied by the iframe embed API. */
1594
- embedFilter?: unknown[];
1595
- /**
1596
- * Project-persisted boolean MapLibre expression that narrows the features
1597
- * rendered for this layer. Unlike a selection, this leaves the source data
1598
- * intact and keeps non-matching features hidden until the filter is cleared.
1599
- * It is composed with transient filters, quick filters, and rule visibility
1600
- * by the map renderers.
1601
- */
1602
- filterExpression?: unknown[];
1603
- /**
1604
- * Data-driven filter controls authored in the layer's Quick Filters section
1605
- * (issue #2114). Unlike {@link timeFilter} and {@link embedFilter} this is
1606
- * persisted control *state*, not a compiled expression: `@geolibre/map`
1607
- * compiles it at sync time (see `compileQuickFilters`) and combines the
1608
- * result with the transient filters and the rule-based visibility filter, so
1609
- * a host page's filter and a user's filter narrow the layer together.
1610
- */
1611
- quickFilters?: LayerQuickFilter[];
1612
- sourcePath?: string;
1613
- /**
1614
- * Id of the {@link LayerGroup} this layer belongs to, or `undefined` when the
1615
- * layer sits at the top level of the layer panel. Layers sharing a `groupId`
1616
- * are kept contiguous in the store's flat `layers` array so the group renders
1617
- * as one block; see `@geolibre/core`'s `layer-groups` helpers.
1618
- */
1619
- groupId?: string;
1620
- /**
1621
- * Project-persisted connection policy for reloadable layers. Runtime timers
1622
- * are reconstructed from this record when a project opens.
1623
- */
1624
- connection?: LayerConnection;
1625
- }
1626
- /**
1627
- * Options for {@link AppState.addTileLayer}: a native raster tile layer (XYZ,
1628
- * WMS, or WMTS) that appears in the Layers panel and persists with the project,
1629
- * just like a layer added through the Add Data dialog. Mirrors the raster
1630
- * `source` fields MapLibre understands so an external plugin can register tile
1631
- * layers without touching the map directly.
1632
- */
1633
- export interface AddTileLayerOptions {
1634
- /**
1635
- * One or more XYZ tile URL templates (with `{x}`/`{y}`/`{z}` placeholders).
1636
- * At least one non-empty template is required, or the layer renders nothing.
1637
- */
1638
- tiles: string[];
1639
- /**
1640
- * Layer discriminator, controlling how the layer is labelled and (for WMS)
1641
- * dev-server proxied. Defaults to `"xyz"`. The layer's `source.type` is
1642
- * always `"raster"`, so any other value (such as `"vector-tiles"` from an
1643
- * untyped JS caller) throws rather than persisting a mislabelled source.
1644
- */
1645
- type?: "xyz" | "wms" | "wmts" | "raster";
1646
- /** Service or base URL recorded on the source for display and restore. */
1647
- url?: string;
1648
- /** Tile size in pixels (default 256). */
1649
- tileSize?: number;
1650
- /** Attribution string shown in the map's attribution control. */
1651
- attribution?: string;
1652
- /** Visible extent as `[west, south, east, north]` in WGS84 degrees. */
1653
- bounds?: [number, number, number, number];
1654
- /** Minimum zoom at which tiles are requested. */
1655
- minzoom?: number;
1656
- /** Maximum zoom at which tiles are requested. */
1657
- maxzoom?: number;
1658
- /** Tile y-axis scheme; `"tms"` flips the y origin. Defaults to `"xyz"`. */
1659
- scheme?: "xyz" | "tms";
1660
- /** Initial visibility (default true). */
1661
- visible?: boolean;
1662
- /** Initial opacity in [0, 1] (default 1). */
1663
- opacity?: number;
1664
- /**
1665
- * Extra source fields merged onto the layer's `source` (e.g. the WMS
1666
- * `layers`/`styles`/`format` recorded for restore). The required `type`,
1667
- * `tiles`, and `tileSize` always win over keys supplied here.
1668
- */
1669
- source?: Record<string, unknown>;
1670
- /** Extra metadata merged onto the layer record. */
1671
- metadata?: Record<string, unknown>;
1672
- }
1673
- /**
1674
- * A named, collapsible folder in the layer panel that organizes a contiguous
1675
- * run of layers. Groups may be nested by referencing another group as their
1676
- * parent; the root groups omit `parentId`.
1677
- *
1678
- * The group's `visible` flag and `opacity` multiplier are folded into each
1679
- * child layer's effective render state by `applyGroupEffects` before the map
1680
- * syncs, so children keep their own stored `visible`/`opacity` values.
1681
- */
1682
- export interface LayerGroup {
1683
- id: string;
1684
- name: string;
1685
- /** Parent folder id, or undefined when this folder is at the panel root. */
1686
- parentId?: string;
1687
- /** When true, the group's children are hidden in the panel (not on the map). */
1688
- collapsed: boolean;
1689
- /** Group-level visibility; ANDed with each child layer's own visibility. */
1690
- visible: boolean;
1691
- /** Group-level opacity in [0, 1]; multiplied into each child's opacity. */
1692
- opacity: number;
1693
- }
1694
- /**
1695
- * Metadata `sourceKind` marking a live SQL query layer: a GeoJSON-backed layer
1696
- * created from a SQL Workspace result whose DuckDB statement is stored on the
1697
- * layer metadata and re-executed on refresh. Defined here so the desktop app
1698
- * (which owns the query/refresh logic) and `@geolibre/plugins` (which excludes
1699
- * these layers from in-place geometry editing) share one value.
1700
- */
1701
- export declare const SQL_QUERY_SOURCE_KIND = "sql-query";
1702
- /**
1703
- * Metadata `sourceKind` marking a NetCDF/HDF grid baked into an `image` overlay,
1704
- * as opposed to the KML ground overlays that otherwise use that layer type.
1705
- *
1706
- * Defined here so `@geolibre/map` (which must not run its feature-query identify
1707
- * on these) and the desktop app (which owns the symbology panel, the pixel
1708
- * readout, and the spectral profile) share one value.
1709
- */
1710
- export declare const NETCDF_IMAGE_SOURCE_KIND = "netcdf-image";
1711
- /**
1712
- * Detect a DuckDB query layer rendered through the plugin's external deck.gl
1713
- * overlay. Shared by `@geolibre/map`, `@geolibre/plugins`, and the desktop
1714
- * app so the detection criteria cannot drift.
1715
- */
1716
- export declare function isDuckDBQueryLayer(layer: Pick<GeoLibreLayer, "metadata" | "type"> | undefined): boolean;
1717
- export interface MapViewState {
1718
- center: [number, number];
1719
- zoom: number;
1720
- bearing: number;
1721
- pitch: number;
1722
- bbox?: [number, number, number, number];
1723
- }
1724
- /**
1725
- * Multi-map (split/grid) support. The workspace can show several map panes in a
1726
- * `rows x cols` grid. Pane 0 is always the *primary* map: it keeps the existing
1727
- * single-map wiring (the global `mapView` + `basemap*` fields, the layer/style
1728
- * panels, plugins, deck.gl). Panes 1..N are *secondary* maps described by
1729
- * `SecondaryMapView` records: every pane shares the same basemap and the same
1730
- * `layers`, but each secondary pane may override which layers are visible so
1731
- * different panes can show different layers. When `syncView` is on,
1732
- * panning/zooming any pane mirrors the camera to every other pane.
1733
- */
1734
- export interface MapGridLayout {
1735
- /** Grid rows (>= 1). */
1736
- rows: number;
1737
- /** Grid columns (>= 1). `rows * cols` is the total number of panes. */
1738
- cols: number;
1739
- /** When true, all panes share a synchronized camera. */
1740
- syncView: boolean;
1741
- }
1742
- /**
1743
- * Which engine draws a map pane.
1744
- *
1745
- * `"maplibre"` is the 2D MapLibre GL map that owns the app's plugin, styling,
1746
- * and deck.gl integrations. `"cesium"` is the 3D globe (see `CesiumCanvas`),
1747
- * which renders the same shared store state — camera, basemap, layers, group
1748
- * effects — through CesiumJS. `"mapbox"` is Mapbox GL JS and `"arcgis"` the
1749
- * ArcGIS Maps SDK for JavaScript, loaded from Esri's CDN at runtime (see
1750
- * `ArcgisCanvas`); both draw the same store state through their own engines.
1751
- *
1752
- * Used both for secondary panes ({@link SecondaryMapView.viewKind}) and for the
1753
- * primary workspace ({@link GeoLibreProject.primaryRenderer}), so the two never
1754
- * drift apart.
1755
- */
1756
- export type MapRendererKind = "maplibre" | "cesium" | "mapbox" | "arcgis";
1757
- /**
1758
- * The engine that draws the primary map area when a project says nothing. The
1759
- * 2D map: it is the renderer every tool, plugin, and panel is wired to, so an
1760
- * existing project (and a new one) opens exactly as it always did.
1761
- */
1762
- export declare const DEFAULT_PRIMARY_RENDERER: MapRendererKind;
1763
- /**
1764
- * A non-primary map pane: shares the primary map's basemap and layers, with its
1765
- * own camera and per-layer visibility overrides.
1766
- */
1767
- export interface SecondaryMapView {
1768
- /** Stable id, used as the React key and the sync-group registration id. */
1769
- id: string;
1770
- view: MapViewState;
1771
- /** Optional user-entered label shown on the pane (e.g. a date or scenario). */
1772
- label?: string;
1773
- /**
1774
- * Which rendering engine draws this pane. Defaults to `"maplibre"` (the 2D
1775
- * map) when absent, so existing projects and panes are unchanged. `"cesium"`
1776
- * renders a 3D globe (see {@link CesiumCanvas}) over the same shared layers.
1777
- */
1778
- viewKind?: MapRendererKind;
1779
- /**
1780
- * Per-layer visibility overrides keyed by layer id. A layer absent from this
1781
- * map inherits the primary map's visibility (`layer.visible`); an entry forces
1782
- * the layer visible (`true`) or hidden (`false`) in this pane only.
1783
- */
1784
- layerVisibility: Record<string, boolean>;
1785
- }
1786
- /** The default single-map grid (one pane, sync enabled so it turns on cleanly). */
1787
- export declare const DEFAULT_MAP_GRID_LAYOUT: MapGridLayout;
1788
- /** Maximum rows or columns in the map grid (so at most a 4x4 = 16-pane grid). */
1789
- export declare const MAX_MAP_GRID_DIM = 4;
1790
- /**
1791
- * Live multi-user collaboration (issue #307). These types describe the
1792
- * *ephemeral* session state the store holds while a live session is active. It
1793
- * is intentionally never written to the `.geolibre.json` project file (the
1794
- * `project.ts` serializers never read it) and never tracked in undo history (the
1795
- * store's `partialize` never lists it), so it resets cleanly on reload.
1796
- */
1797
- export type CollaborationRole = "host" | "guest";
1798
- /** Whether guests may edit (`co-edit`) or only watch (`view-only`). */
1799
- export type CollaborationMode = "view-only" | "co-edit";
1800
- export interface ParticipantIdentity {
1801
- provider: string;
1802
- userId: string;
1803
- username: string;
1804
- }
1805
- export interface CollabInvite {
1806
- token: string;
1807
- role: CollaborationMode;
1808
- createdAt: number;
1809
- maxUses?: number;
1810
- useCount: number;
1811
- revoked: boolean;
1812
- }
1813
- export interface CollaborationParticipant {
1814
- clientId: string;
1815
- displayName: string;
1816
- color: string;
1817
- role: CollaborationRole;
1818
- /**
1819
- * Host-set per-participant edit override (#754). `null` follows the session
1820
- * `mode`; `true`/`false` pins this participant to can-edit / view-only. Always
1821
- * `null` for the host (the host can always edit).
1822
- */
1823
- editOverride: boolean | null;
1824
- /** Optional account identity when signed-in identity binding is enabled. */
1825
- identity?: ParticipantIdentity | null;
1826
- }
1827
- /** A remote participant's live cursor + viewport, used to render presence. */
1828
- export interface CollaborationPresence {
1829
- displayName: string;
1830
- color: string;
1831
- cursor?: {
1832
- lng: number;
1833
- lat: number;
1834
- } | null;
1835
- view?: MapViewState | null;
1836
- }
1837
- /** One in-session chat message (#754). Ephemeral; never persisted to a project. */
1838
- export interface CollaborationChatMessage {
1839
- /** Server-assigned id (stable React key / dedupe). */
1840
- id: string;
1841
- /** clientId of the author. */
1842
- clientId: string;
1843
- displayName: string;
1844
- color: string;
1845
- text: string;
1846
- /** Optional map coordinate the author attached; clickable to recenter. */
1847
- coordinate?: {
1848
- lng: number;
1849
- lat: number;
1850
- } | null;
1851
- /** Server-assigned epoch-ms timestamp. */
1852
- ts: number;
1853
- }
1854
- export interface CollaborationState {
1855
- /** True once connected and joined to a session. */
1856
- isActive: boolean;
1857
- /** True while connecting/reconnecting (UI shows a spinner). */
1858
- connecting: boolean;
1859
- sessionId: string | null;
1860
- clientId: string | null;
1861
- role: CollaborationRole | null;
1862
- mode: CollaborationMode;
1863
- selfName: string;
1864
- selfColor: string;
1865
- participants: CollaborationParticipant[];
1866
- /** Remote presence keyed by participant clientId (never includes self). */
1867
- presence: Record<string, CollaborationPresence>;
1868
- /** When true, this participant's camera follows the host's viewport. */
1869
- followHost: boolean;
1870
- /** Recent session chat, oldest first, capped to a bounded window (#754). */
1871
- chat: CollaborationChatMessage[];
1872
- /** Session flag requiring participants to be signed in. */
1873
- requireIdentity: boolean;
1874
- /**
1875
- * Whether the connected relay has an identity issuer configured. False (the
1876
- * default) means it cannot verify a sign-in, so the host UI hides the
1877
- * "require a signed-in account" toggle instead of offering a gate that would
1878
- * lock every guest out.
1879
- */
1880
- identitySupported: boolean;
1881
- /** Layer IDs marked locked by the host. */
1882
- lockedLayerIds: string[];
1883
- /** Active session invites minted by host. */
1884
- invites: CollabInvite[];
1885
- /** Last human-readable error, surfaced in the Collaborate dialog. */
1886
- error: string | null;
1887
- }
1888
- /** Map projection the renderer uses. Mirrors the GlobeControl toggle. */
1889
- export type MapProjection = "globe" | "mercator";
1890
- /**
1891
- * Unit system the scale bar reports distances in. `"metric"` uses m/km,
1892
- * `"imperial"` uses ft/mi, and `"nautical"` uses nautical miles.
1893
- */
1894
- export type MapScaleUnit = "metric" | "imperial" | "nautical";
1895
- export interface MapPreferences {
1896
- restrictBounds: boolean;
1897
- bounds: [number, number, number, number];
1898
- minZoom: number;
1899
- maxZoom: number;
1900
- maxPitch: number;
1901
- renderWorldCopies: boolean;
1902
- projection: MapProjection;
1903
- /**
1904
- * Celestial body / ellipsoid the project's coordinates describe (keys into the
1905
- * ellipsoid registry in `@geolibre/core`). Drives measurement radii and pairs
1906
- * with planetary basemaps. Defaults to `"earth"` (WGS 84).
1907
- */
1908
- ellipsoidId: string;
1909
- /**
1910
- * Unit system the scale bar displays. Defaults to `"metric"`; switch to
1911
- * `"imperial"` for feet/miles or `"nautical"` for nautical miles.
1912
- */
1913
- scaleUnit: MapScaleUnit;
1914
- /**
1915
- * Whether the status bar resolves and shows the ground elevation under the
1916
- * pointer (issue #1813). **Defaults to `false`**: with 3D terrain off the
1917
- * lookup falls back to the public Open-Meteo service, so hovering would send
1918
- * coordinates off the device for a readout the user never asked for. Toggled
1919
- * from Controls -> Elevation.
1920
- */
1921
- showPointerElevation: boolean;
1922
- /** Whether the built-in 3D terrain control and terrain surface are enabled. */
1923
- terrainEnabled: boolean;
1924
- /** Mapbox-only style. New projects use Streets; absent follows the shared basemap. */
1925
- mapboxStyleUrl?: string;
1926
- /**
1927
- * ArcGIS-only basemap: an Esri basemap style id (`arcgis/streets`,
1928
- * `arcgis/imagery`, `osm/standard`, ...). New projects use Streets. Absent
1929
- * follows the shared basemap, translated to tiles the SDK can draw; the id
1930
- * is also set aside when no ArcGIS API key is configured, since Esri's
1931
- * basemap styles service requires one.
1932
- */
1933
- arcgisBasemap?: string;
1934
- /** Cesium imagery override; absent follows the shared project basemap. */
1935
- cesiumBasemap?: CesiumBasemapId;
1936
- /**
1937
- * Notation the status bar reports the pointer coordinate in: `"dd"` decimal
1938
- * degrees (default), `"dms"` degrees/minutes/seconds, `"ddm"` degrees and
1939
- * decimal minutes, or `"utm"` zone easting/northing. Stored as a string
1940
- * rather than a union so `@geolibre/core` does not have to depend on the
1941
- * formatter, which lives with the app's DMS helpers and the Gridlines
1942
- * plugin's UTM projection; the app normalises unknown values to `"dd"`.
1943
- */
1944
- coordinateFormat: string;
1945
- }
1946
- export interface RuntimeEnvironmentVariable {
1947
- key: string;
1948
- value: string;
1949
- enabled: boolean;
1950
- }
1951
- declare global {
1952
- interface Window {
1953
- __GEOLIBRE_RUNTIME_ENV__?: Record<string, string>;
1954
- }
1955
- }
1956
- /**
1957
- * Geocoding backend selection persisted in the project. The provider id keys
1958
- * into the geocoding registry in `@geolibre/core`; API keys are stored per
1959
- * provider so switching backends does not discard the others' keys. Empty
1960
- * endpoint overrides fall back to the provider's default endpoints.
1961
- */
1962
- export interface GeocodingPreferences {
1963
- providerId: string;
1964
- /** Per-provider API key / access token, keyed by provider id. */
1965
- apiKeys: Record<string, string>;
1966
- /** Optional custom forward endpoint (else the provider default). */
1967
- forwardEndpoint?: string;
1968
- /** Optional custom reverse endpoint (else the provider default). */
1969
- reverseEndpoint?: string;
1970
- /** Contact email sent to identify the client (used by Nominatim). */
1971
- email?: string;
1972
- }
1973
- export interface ProjectPreferences {
1974
- map: MapPreferences;
1975
- environmentVariables: RuntimeEnvironmentVariable[];
1976
- geocoding: GeocodingPreferences;
1977
- }
1978
- export type ProjectPluginControlPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
1979
- export interface ProjectPluginState {
1980
- manifestUrls: string[];
1981
- activePluginIds: string[];
1982
- mapControlPositions: Record<string, ProjectPluginControlPosition>;
1983
- settings: Record<string, unknown>;
1984
- }
1985
- export declare const DEFAULT_PROJECT_PREFERENCES: ProjectPreferences;
1986
- /**
1987
- * A single user override for one legend item, keyed in {@link LegendConfig.overrides}
1988
- * by a stable item key (a layer id for a whole entry, or `${layerId}::${index}`
1989
- * for an individual class within a graduated/categorized entry).
1990
- */
1991
- export interface LegendItemOverride {
1992
- /** User-supplied label that replaces the auto-generated one. */
1993
- label?: string;
1994
- /** When true, the item is omitted from the rendered legend. */
1995
- hidden?: boolean;
1996
- }
1997
- /** Swatch shape for a hand-authored legend item. */
1998
- export type LegendCustomShape = "square" | "circle" | "line";
1999
- /** One hand-authored legend item: a color swatch plus its label. */
2000
- export interface LegendCustomItem {
2001
- /** Display label (e.g. an NLCD class name). */
2002
- label: string;
2003
- /** Swatch color as a CSS color (typically `#rrggbb`). */
2004
- color: string;
2005
- /** Swatch shape; defaults to `"square"`. */
2006
- shape?: LegendCustomShape;
2007
- /**
2008
- * Symbol size in map pixels (circle radius / line width). Carried so that
2009
- * customizing a proportional-symbol entry keeps the graduated sizes it was
2010
- * seeded from instead of flattening every symbol to one size.
2011
- */
2012
- size?: number;
2013
- }
2014
- /**
2015
- * A hand-authored legend entry. Keyed by layer id it REPLACES that layer's
2016
- * auto-derived classes (the fallback when automatic derivation is wrong or
2017
- * impossible, e.g. a paletted land-cover raster like NLCD); keyed by a
2018
- * `custom:` id it renders as a standalone section not tied to any layer.
2019
- */
2020
- export interface LegendCustomEntry {
2021
- /** Optional heading; a layer-keyed entry falls back to the layer name. */
2022
- title?: string;
2023
- /** The items to render, top to bottom. */
2024
- items: LegendCustomItem[];
2025
- }
2026
- /** Map corner the on-map legend panel docks to. */
2027
- export type LegendPanelPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
2028
- /**
2029
- * User customizations for the legend (the on-map Legend panel and the Print
2030
- * Layout legend). The legend itself is always derived from the visible layers'
2031
- * symbology; this record only stores the edits layered on top (title, ordering,
2032
- * per-item rename/hide, hand-authored entries), so it survives layer additions
2033
- * and removals and is persisted in the `.geolibre.json` project.
2034
- */
2035
- export interface LegendConfig {
2036
- /** Heading drawn above the legend entries. */
2037
- title: string;
2038
- /** When true, classes are grouped under a per-layer heading. */
2039
- groupByLayer: boolean;
2040
- /**
2041
- * Custom top-level entry order by layer id, top-first. Layer ids not listed
2042
- * keep their default order after the listed ones.
2043
- */
2044
- order: string[];
2045
- /** Per-item overrides keyed by stable item key. */
2046
- overrides: Record<string, LegendItemOverride>;
2047
- /**
2048
- * Hand-authored entries keyed by layer id (replacing that layer's
2049
- * auto-derived classes) or by a standalone `custom:` id. See
2050
- * {@link LegendCustomEntry}.
2051
- */
2052
- customEntries?: Record<string, LegendCustomEntry>;
2053
- /** Whether the on-map Legend panel is open (persisted with the project). */
2054
- panelVisible?: boolean;
2055
- /** Whether the open panel is collapsed to just its header bar. */
2056
- panelCollapsed?: boolean;
2057
- /** Map corner the on-map Legend panel docks to; defaults to `"top-left"`. */
2058
- panelPosition?: LegendPanelPosition;
2059
- /**
2060
- * User-resized panel width in px (via the corner drag handles). Absent means
2061
- * the default width; height absent means auto-fit to content within the map.
2062
- */
2063
- panelWidth?: number;
2064
- /** User-resized panel height in px. See {@link LegendConfig.panelWidth}. */
2065
- panelHeight?: number;
2066
- }
2067
- export declare const DEFAULT_LEGEND_CONFIG: LegendConfig;
2068
- /** Camera target captured for a story chapter. */
2069
- export interface StoryChapterLocation {
2070
- center: [number, number];
2071
- zoom: number;
2072
- pitch: number;
2073
- bearing: number;
2074
- }
2075
- /** Where a chapter's text panel sits over the map. */
2076
- export type StoryChapterAlignment = "left" | "center" | "right" | "full";
2077
- /** How the map transitions to a chapter's location. */
2078
- export type StoryChapterAnimation = "flyTo" | "easeTo" | "jumpTo";
2079
- /** A layer opacity change triggered when a chapter is entered or exited. */
2080
- export interface StoryLayerOpacityChange {
2081
- /** Stable identity for React list keys; optional for older project files. */
2082
- id?: string;
2083
- /** GeoLibre store layer id whose opacity should change. */
2084
- layerId: string;
2085
- opacity: number;
2086
- /** Transition duration in milliseconds. */
2087
- duration?: number;
2088
- }
2089
- /** A single scene in a scroll-driven story map. */
2090
- export interface StoryChapter {
2091
- id: string;
2092
- title: string;
2093
- description: string;
2094
- /** Optional image shown in the chapter panel (URL or data URI). */
2095
- image?: string;
2096
- alignment: StoryChapterAlignment;
2097
- /** Hide the text panel while still transitioning the map. */
2098
- hidden: boolean;
2099
- location: StoryChapterLocation;
2100
- mapAnimation: StoryChapterAnimation;
2101
- /** Slowly rotate the camera once the transition settles. */
2102
- rotateAnimation: boolean;
2103
- onChapterEnter: StoryLayerOpacityChange[];
2104
- onChapterExit: StoryLayerOpacityChange[];
2105
- }
2106
- export type StoryInsetPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
2107
- /**
2108
- * A non-chapter intro/outro slide shown before the first or after the last
2109
- * chapter (#998). `"none"` disables it. The remaining modes are:
2110
- * - `"blank"` a solid screen in the panel theme color,
2111
- * - `"black"` a solid black screen,
2112
- * - `"global"` a zoomed-out global view of the map with no text,
2113
- * - `"adjacent"` the camera of the neighboring chapter (the first chapter for
2114
- * the start slide, the last chapter for the closing slide) with all text and
2115
- * media hidden, so the presenter can zoom into the full content next.
2116
- */
2117
- export type StorySlideMode = "none" | "blank" | "black" | "global" | "adjacent";
2118
- /** A start/closing slide that is actually shown (every mode except `"none"`). */
2119
- export type StoryActiveSlideMode = Exclude<StorySlideMode, "none">;
2120
- /** Scroll-driven story map authored on top of a GeoLibre project. */
2121
- export interface StoryMap {
2122
- title: string;
2123
- subtitle: string;
2124
- byline: string;
2125
- footer: string;
2126
- theme: "light" | "dark";
2127
- showMarkers: boolean;
2128
- markerColor: string;
2129
- inset: boolean;
2130
- insetPosition: StoryInsetPosition;
2131
- /**
2132
- * Start the presentation with the chapter itinerary/navigation panel hidden,
2133
- * revealing chapters one at a time instead of listing every upcoming location
2134
- * up front (#995). The presenter still offers a toggle to open the list.
2135
- */
2136
- hideChapterNav: boolean;
2137
- /** Optional intro slide shown before the first chapter (#998). */
2138
- startSlide: StorySlideMode;
2139
- /** Optional closing slide shown after the last chapter (#998). */
2140
- endSlide: StorySlideMode;
2141
- chapters: StoryChapter[];
2142
- }
2143
- export declare const DEFAULT_STORY_MAP: StoryMap;
2144
- /**
2145
- * One step in a {@link ProcessingModel}: a processing tool invoked with a fixed
2146
- * set of parameters. The runner chains steps by feeding each step's output layer
2147
- * into the next step's input layer parameter (`inputParam`, default `"layer"`),
2148
- * so a step's stored `parameters` for that input is ignored for every step after
2149
- * the first.
2150
- */
2151
- export interface ProcessingModelStep {
2152
- /** Stable id, unique within the model (used as the React key and run label). */
2153
- id: string;
2154
- /** The processing tool's registry id (e.g. `"buffer"`). */
2155
- toolId: string;
2156
- /** Parameter values keyed by the tool's parameter ids. */
2157
- parameters: Record<string, unknown>;
2158
- /**
2159
- * Which `type: "layer"` parameter receives the previous step's output. Defaults
2160
- * to `"layer"`; set it for tools whose primary input is named differently.
2161
- */
2162
- inputParam?: string;
2163
- }
2164
- /**
2165
- * What flows along a model edge. Vector nodes exchange FeatureCollections;
2166
- * raster nodes exchange GeoTIFF bytes. A port declaring `"any"` accepts either
2167
- * and is resolved to a concrete kind at run time by whatever is wired into it.
2168
- */
2169
- export type ModelPortKind = "vector" | "raster" | "any";
2170
- /** One connection point on a {@link ModelGraphNode}. */
2171
- export interface ModelGraphPort {
2172
- /**
2173
- * Port id, unique within its node and direction. For a tool node's inputs
2174
- * this is the underlying tool parameter id, so wiring an edge and setting the
2175
- * parameter by hand are the same operation.
2176
- */
2177
- id: string;
2178
- label: string;
2179
- kind: ModelPortKind;
2180
- /** Inputs only: the run fails when nothing is wired in and no value is set. */
2181
- required?: boolean;
2182
- }
2183
- /**
2184
- * What a node does. `input` sources an existing project layer, `tool` runs a
2185
- * processing algorithm, and `output` names a result to add back to the map.
2186
- */
2187
- export type ModelGraphNodeKind = "input" | "tool" | "output";
2188
- /** One node on the Model Builder canvas. */
2189
- export interface ModelGraphNode {
2190
- /** Stable id, unique within the graph; referenced by {@link ModelGraphEdge}. */
2191
- id: string;
2192
- kind: ModelGraphNodeKind;
2193
- /** Canvas position in graph coordinates (unscaled by zoom). */
2194
- x: number;
2195
- y: number;
2196
- /** `input` nodes: the project layer id this node sources. */
2197
- layerId?: string;
2198
- /**
2199
- * `tool` nodes: the tool's id within {@link provider}'s registry. Kept
2200
- * separate from the provider so the same short id can exist in both.
2201
- */
2202
- toolId?: string;
2203
- /** `tool` nodes: which registry resolves {@link toolId}. */
2204
- provider?: ModelToolProvider;
2205
- /** `tool` nodes: parameter values for everything not supplied by an edge. */
2206
- parameters?: Record<string, unknown>;
2207
- /** `output` nodes: the layer name given to the result added to the map. */
2208
- name?: string;
2209
- }
2210
- /**
2211
- * A directed connection from one node's output port to another node's input
2212
- * port. Ports are named, so a tool with several inputs (Clip's target and
2213
- * overlay, say) wires each one unambiguously.
2214
- */
2215
- export interface ModelGraphEdge {
2216
- id: string;
2217
- from: string;
2218
- fromPort: string;
2219
- to: string;
2220
- toPort: string;
2221
- }
2222
- /** The node-and-edge graph authored on the Model Builder canvas. */
2223
- export interface ProcessingModelGraph {
2224
- nodes: ModelGraphNode[];
2225
- edges: ModelGraphEdge[];
2226
- }
2227
- /** Which registry a {@link ModelGraphNode.toolId} is resolved against. */
2228
- export type ModelToolProvider = "vector" | "whitebox";
2229
- /**
2230
- * A reusable processing pipeline ("model" in QGIS Graphical Modeler / ArcGIS
2231
- * ModelBuilder terms), saved in the project file so it can be reloaded and
2232
- * re-run.
2233
- *
2234
- * Two shapes coexist. {@link steps} is the original strictly linear chain, and
2235
- * remains the only thing older builds understand. {@link graph} is the
2236
- * Model Builder's directed graph, which supports multi-input tools, branches
2237
- * and merges. When both are present `graph` wins; a model saved by the canvas
2238
- * also writes a `steps` projection whenever its graph happens to be a single
2239
- * chain, so older builds can still run it.
2240
- */
2241
- export interface ProcessingModel {
2242
- id: string;
2243
- name: string;
2244
- steps: ProcessingModelStep[];
2245
- graph?: ProcessingModelGraph;
2246
- }
2247
- /**
2248
- * Which dialog family executed a {@link ProcessingRun}. Drives the History
2249
- * panel's re-run routing: each kind maps to the dialog (and store open-flag)
2250
- * that can be reopened pre-filled with the run's parameters.
2251
- */
2252
- export type ProcessingRunKind = "vector" | "statistics" | "network" | "whitebox" | "raster" | "conversion" | "algorithm";
2253
- export type ProcessingRunStatus = "success" | "error";
2254
- /** Upper bound on persisted processing-history entries (oldest are dropped). */
2255
- export declare const MAX_PROCESSING_HISTORY = 100;
2256
- /**
2257
- * One recorded processing tool run (issue #1292). Appended by every processing
2258
- * dialog when a run finishes and persisted in the project file, so a saved
2259
- * project documents how its derived layers were produced. Parameter values are
2260
- * stored exactly as the dialog dispatched them (layer parameters hold layer
2261
- * ids), which is what re-run pre-fills and "Copy as Python" emit.
2262
- */
2263
- export interface ProcessingRun {
2264
- /** Stable id, unique within the project (React key and update key). */
2265
- id: string;
2266
- kind: ProcessingRunKind;
2267
- /** The tool's registry id (e.g. `"buffer"`, `"slope"`). */
2268
- toolId: string;
2269
- /** Human-readable tool name captured at run time. */
2270
- toolName: string;
2271
- /** Engine that executed the run: `client`, `wasm`, `sidecar`, `pyodide`, `browser`. */
2272
- engine: string;
2273
- /** Parameter values keyed by the tool's parameter ids. */
2274
- parameters: Record<string, unknown>;
2275
- /** Names of input layers referenced by layer parameters, keyed by layer id. */
2276
- inputLayerNames?: Record<string, string>;
2277
- /** Names of layers the run added to the map. */
2278
- outputLayerNames?: string[];
2279
- /** Input file path/name, for file-based tools (raster/conversion). */
2280
- inputPath?: string;
2281
- /** Output file path/name, for file-based tools (raster/conversion). */
2282
- outputPath?: string;
2283
- /** ISO timestamp of when the run started. */
2284
- startedAt: string;
2285
- /** Wall-clock duration of the run in milliseconds. */
2286
- durationMs?: number;
2287
- status: ProcessingRunStatus;
2288
- /** Error message when `status` is `"error"`. */
2289
- error?: string;
2290
- }
2291
- /**
2292
- * A pending "re-run from History" request. The History panel writes it to the
2293
- * store and opens the target dialog; the dialog consumes it (pre-filling its
2294
- * parameter form) and clears it. `autoRun` asks the dialog to start the run
2295
- * immediately after pre-filling (plain Re-run vs Edit & re-run).
2296
- */
2297
- export interface ProcessingRerunRequest {
2298
- kind: ProcessingRunKind;
2299
- toolId: string;
2300
- parameters: Record<string, unknown>;
2301
- /** Engine to preselect, when the dialog offers a choice. */
2302
- engine?: string;
2303
- autoRun?: boolean;
2304
- }
2305
- /** Column-count bounds for the Dashboard panel's widget grid. */
2306
- export declare const MIN_DASHBOARD_COLUMNS = 1;
2307
- export declare const MAX_DASHBOARD_COLUMNS = 6;
2308
- export declare const DEFAULT_DASHBOARD_COLUMNS = 2;
2309
- /** The chart a {@link DashboardWidget} draws. Mirrors the attribute Charts
2310
- * panel's types so a widget reuses the same rendering. The `"indicator"` type
2311
- * is a non-chart KPI tile (issue #1381). */
2312
- export type DashboardWidgetType = "histogram" | "scatter" | "bar" | "line" | "box" | "pie" | "indicator" | "selector" | "list";
2313
- /** How a bar widget reduces its category groups. */
2314
- export type DashboardWidgetAggregation = "count" | "sum" | "mean";
2315
- /**
2316
- * One chart in the Dashboard panel: a chart type bound to a layer and the
2317
- * field(s) it plots. Which `field*`/`category`/`aggregation` keys apply depends
2318
- * on `type` (histogram/line/box use `field`; scatter uses `xField`/`yField`;
2319
- * bar uses `category` + `aggregation` and, for sum/mean, `valueField`). Unused
2320
- * keys are simply ignored, so the record stays flat and easy to hand-edit.
2321
- * Saved in the project file so a dashboard reopens with its widgets intact.
2322
- */
2323
- export interface DashboardWidget {
2324
- /** Stable id, unique within the project (React key and store key). */
2325
- id: string;
2326
- /** The layer whose features feed this widget. */
2327
- layerId: string;
2328
- /** The chart to draw. */
2329
- type: DashboardWidgetType;
2330
- /** Optional custom title; the panel derives a label from the fields if absent. */
2331
- title?: string;
2332
- /** Optional hex color (`#rgb`/`#rrggbb`) for the chart's marks. Single-series
2333
- * charts use it as the series color; bar/pie use it as the base of a
2334
- * monochromatic ramp. Defaults to the theme primary / multi-color palette. */
2335
- color?: string;
2336
- /** Value field for histogram/line/box. */
2337
- field?: string;
2338
- /** X-axis field for scatter. */
2339
- xField?: string;
2340
- /** Y-axis field for scatter. */
2341
- yField?: string;
2342
- /** Number of bins for a histogram. */
2343
- bins?: number;
2344
- /** Category field for a bar chart. */
2345
- category?: string;
2346
- /** Aggregation for a bar chart (default `count`). */
2347
- aggregation?: DashboardWidgetAggregation;
2348
- /** Value field a bar chart's sum/mean reduces (ignored for `count`). */
2349
- valueField?: string;
2350
- /** Indicator widget: aggregation function for the KPI value (issue #1381).
2351
- * Extends bar aggregation with min, max, and median. */
2352
- indicatorAggregation?: IndicatorAggregation;
2353
- /** Indicator widget: optional prefix (e.g. "€", "$"). */
2354
- prefix?: string;
2355
- /** Indicator widget: optional suffix (e.g. " kg", " ha"). */
2356
- suffix?: string;
2357
- /** Selector widget: whether multiple values can be picked (default false). */
2358
- multiple?: boolean;
2359
- /** List widget: columns to display. */
2360
- listFields?: string[];
2361
- /** List widget: field to sort by. */
2362
- sortBy?: string;
2363
- /** List widget: sort direction (default "desc"). */
2364
- sortDir?: "asc" | "desc";
2365
- /** List widget: max rows to show (default 20). */
2366
- limit?: number;
2367
- }
2368
- /** Aggregation functions for indicator widgets (issue #1381). Extends the bar
2369
- * widget aggregation with min, max, and median. */
2370
- export type IndicatorAggregation = "count" | "sum" | "mean" | "min" | "max" | "median";
2371
- /**
2372
- * What slice of a layer's styling a Style Manager entry captures (issue #1294).
2373
- *
2374
- * - `"style"`: the layer's complete {@link LayerStyle} snapshot.
2375
- * - `"symbol"`: fill/stroke/marker/pattern symbology only, so it can restyle a
2376
- * layer without touching its labels or renderer configuration.
2377
- * - `"labels"`: the {@link LabelStyle} block only.
2378
- * - `"ramp"`: the color ramp + classification settings only (ramp name, class
2379
- * count, classification scheme), independent of the attribute the target
2380
- * layer classifies on.
2381
- */
2382
- export type StyleLibraryEntryKind = "style" | "symbol" | "labels" | "ramp";
2383
- /**
2384
- * One saved, reusable style in the Style Manager library (issue #1294). The
2385
- * payload is a {@link LayerStyle} subset chosen by {@link kind}; applying an
2386
- * entry merges that subset onto the target layer's style. Entries live either
2387
- * in the app-level library (persisted across projects) or embedded in a
2388
- * project file's `styleLibrary` array.
2389
- */
2390
- export interface StyleLibraryEntry {
2391
- /** Stable id, used as the IndexedDB/store key; upserts overwrite by id. */
2392
- id: string;
2393
- /** Display name shown in the Style Manager. */
2394
- name: string;
2395
- /** Which style subset {@link style} carries. */
2396
- kind: StyleLibraryEntryKind;
2397
- /** Free-form tags for filtering the library. */
2398
- tags: string[];
2399
- /** The saved {@link LayerStyle} subset (see {@link kind}). */
2400
- style: Partial<LayerStyle>;
2401
- /** ISO timestamp of the last save; empty for built-in presets. */
2402
- updatedAt: string;
2403
- }
2404
- /**
2405
- * One saved, re-addable layer in the Layer Library (issue #1520) — the "My
2406
- * Data" section of the Browser panel. Stores the layer's **source
2407
- * specification** plus its full presentation state (style, labels, filters,
2408
- * joins, virtual fields, attribute form), deliberately *not* the data, so the
2409
- * library stays small and an entry always reflects the current contents of its
2410
- * source.
2411
- *
2412
- * The exception is a layer whose features exist only in memory (drawn features,
2413
- * processing output) or in a local file: those have no re-fetchable source, so
2414
- * their features are embedded in {@link geojson} behind a size cap. See
2415
- * `layer-library.ts` for how an entry is captured and re-added.
2416
- */
2417
- export interface LayerLibraryEntry {
2418
- /** Stable id, used as the IndexedDB/store key; upserts overwrite by id. */
2419
- id: string;
2420
- /** Display name shown in the Browser panel's My Data section. */
2421
- name: string;
2422
- /** ISO timestamp of the last save. */
2423
- addedAt: string;
2424
- /** The layer type to recreate ({@link GeoLibreLayer.type}). */
2425
- layerType: LayerType;
2426
- /** The MapLibre/plugin source spec to recreate the layer from. */
2427
- source: Record<string, unknown>;
2428
- /** The saved layer's complete {@link LayerStyle} (labels included). */
2429
- style: LayerStyle;
2430
- /** Layer opacity in [0, 1]. */
2431
- opacity: number;
2432
- /** The layer metadata the renderers and plugin sync modules key off. */
2433
- metadata: Record<string, unknown>;
2434
- /** Absolute local path, for a layer read from a file on disk. */
2435
- sourcePath?: string;
2436
- /** Persistent attribute joins to reapply. */
2437
- joins?: LayerJoin[];
2438
- /** Expression-backed virtual fields to reapply. */
2439
- virtualFields?: LayerVirtualField[];
2440
- /** Per-field edit-widget/constraint configuration to reapply. */
2441
- attributeForm?: AttributeFormConfig;
2442
- /** Popup/tooltip design to reapply. */
2443
- popup?: LayerPopupConfig;
2444
- /**
2445
- * Embedded features, present only for a layer whose source cannot be
2446
- * re-read (in-memory features) or whose local file may be unavailable.
2447
- */
2448
- geojson?: FeatureCollection;
2449
- /**
2450
- * True when the entry can only be re-added by a host that can read
2451
- * {@link sourcePath} — i.e. its features were too large to embed, so the
2452
- * desktop app must re-read the file and the browser build cannot.
2453
- */
2454
- needsLocalFile?: boolean;
2455
- }
2456
- /** One saved template in the Template Library. */
2457
- export interface ProjectTemplateEntry {
2458
- id: string;
2459
- name: string;
2460
- description?: string;
2461
- project: GeoLibreProject;
2462
- createdAt: string;
2463
- updatedAt: string;
2464
- }
2465
- export interface GeoLibreProject {
2466
- id?: string;
2467
- version: string;
2468
- name: string;
2469
- mapView: MapViewState;
2470
- basemapStyleUrl: string;
2471
- basemapVisible: boolean;
2472
- basemapOpacity: number;
2473
- /** Custom colour for the Blank background. Null uses the current theme default. */
2474
- blankBackgroundColor?: string | null;
2475
- layers: GeoLibreLayer[];
2476
- /**
2477
- * Layer selected in the Layers panel when the project was saved. Omitted by
2478
- * legacy projects; `null` deliberately restores no active layer.
2479
- */
2480
- selectedLayerId?: string | null;
2481
- /** Named folders that organize the flat `layers` list in the layer panel. */
2482
- layerGroups?: LayerGroup[];
2483
- styles: Record<string, LayerStyle>;
2484
- preferences: ProjectPreferences;
2485
- plugins?: ProjectPluginState;
2486
- /** User customizations for the Print Layout legend. */
2487
- legend?: LegendConfig;
2488
- /**
2489
- * Print Layout composer settings (title, page size, orientation, blocks,
2490
- * atlas). Omitted while the composer is untouched, so projects that never
2491
- * opened it are unaffected (GeoLibre discussion #1992).
2492
- */
2493
- printLayout?: PrintLayoutConfig;
2494
- storymap?: StoryMap;
2495
- /** Saved processing pipelines (batch/model chaining; issue #344). */
2496
- models?: ProcessingModel[];
2497
- /** Recorded processing tool runs (Processing History; issue #1292). */
2498
- processingHistory?: ProcessingRun[];
2499
- /** Saved Dashboard panel chart widgets (issue #401). */
2500
- widgets?: DashboardWidget[];
2501
- /** Number of columns in the Dashboard widget grid; omitted when default. */
2502
- dashboardColumns?: number;
2503
- /**
2504
- * Multi-map grid layout; omitted (single 1x1 pane) for default projects so
2505
- * legacy readers and single-map files are unaffected.
2506
- */
2507
- mapLayout?: MapGridLayout;
2508
- /**
2509
- * Secondary map panes (everything past the primary pane). Omitted when the
2510
- * grid is a single pane. The primary pane uses the top-level `mapView` /
2511
- * `basemap*` fields.
2512
- */
2513
- secondaryMapViews?: SecondaryMapView[];
2514
- /** User-entered label for the primary pane; omitted when empty. */
2515
- primaryMapLabel?: string;
2516
- /**
2517
- * Which engine draws the primary map area (issue #2217). Omitted for the
2518
- * default 2D map, so every project written before this existed — and every
2519
- * project that never leaves MapLibre — is byte-identical to before. A project
2520
- * saved as `"cesium"` reopens directly on the 3D globe.
2521
- *
2522
- * This is independent of {@link mapLayout}: a 1x1 workspace can be either
2523
- * renderer, and a multi-pane grid can still mix the two through each pane's
2524
- * {@link SecondaryMapView.viewKind}.
2525
- */
2526
- primaryRenderer?: MapRendererKind;
2527
- /**
2528
- * Project-scoped Style Manager entries (issue #1294), so a project can carry
2529
- * its reusable styles to teammates. Omitted when empty; the app-level
2530
- * library is persisted outside the project file and never serialized here.
2531
- */
2532
- styleLibrary?: StyleLibraryEntry[];
2533
- /** Anchored review comments on map points or features (issue #1518). */
2534
- comments?: ProjectComment[];
2535
- metadata: Record<string, unknown>;
2536
- }
2537
- export type CommentAnchor = {
2538
- type: "point";
2539
- lngLat: [number, number];
2540
- } | {
2541
- type: "feature";
2542
- layerId: string;
2543
- featureId: string | number;
2544
- lngLat?: [number, number];
2545
- };
2546
- export interface CommentAuthor {
2547
- name: string;
2548
- color: string;
2549
- }
2550
- export interface CommentReply {
2551
- id: string;
2552
- author: CommentAuthor;
2553
- body: string;
2554
- createdAt: string;
2555
- }
2556
- export interface ProjectComment {
2557
- id: string;
2558
- anchor: CommentAnchor;
2559
- author: CommentAuthor;
2560
- body: string;
2561
- createdAt: string;
2562
- resolved: boolean;
2563
- replies: CommentReply[];
2564
- }
2565
- export interface RecentProjectEntry {
2566
- path: string;
2567
- name: string;
2568
- openedAt: string;
2569
- }
2570
- /**
2571
- * Size at or above which a local vector file is read through DuckDB instead of
2572
- * the in-memory JavaScript readers, and the feature count above which the user
2573
- * is asked before every feature is materialized as GeoJSON.
2574
- *
2575
- * These live in core so the desktop loaders (`duckdb-vector-guard.ts`) and the
2576
- * Add Vector Layer panel (`@geolibre/plugins`, which configures the
2577
- * `maplibre-gl-vector` control's `autoThreshold`) switch strategy at the *same*
2578
- * numbers. Before they were unified the two entry points disagreed — the panel
2579
- * tiled at 25 MB / 50k while drag-and-drop stayed in memory to 100 MB — so the
2580
- * same file behaved differently depending on how it was added, and no single
2581
- * number could be documented.
2582
- */
2583
- export declare const DUCKDB_VECTOR_ROUTE_BYTES: number;
2584
- export declare const DUCKDB_VECTOR_FEATURE_WARN_COUNT = 100000;
2585
- /** A collection whose coordinates cannot be WGS84 longitude/latitude. */
2586
- export interface NonGeographicCoordinates {
2587
- /** How many coordinates were inspected. */
2588
- sampled: number;
2589
- /** The largest |x| seen — a longitude may not exceed 180. */
2590
- maxAbsX: number;
2591
- /** The largest |y| seen — a latitude may not exceed 90. */
2592
- maxAbsY: number;
2593
- }
2594
- /**
2595
- * Detect a collection that declares (or is assumed to be) WGS84 but carries
2596
- * projected coordinates — the failure mode where a layer loads cleanly, appears
2597
- * in the Layers panel, and renders nowhere because its "longitude" is a easting
2598
- * in metres or feet.
2599
- *
2600
- * GeoLibre honours whatever CRS a file declares, so a file that declares
2601
- * `CRS84`/`GCS_WGS_1984` while holding State Plane or Albers coordinates is
2602
- * passed through untouched and lands off the map with no error. Callers use
2603
- * this to warn instead of failing silently; it does not guess the true CRS,
2604
- * which only the user knows.
2605
- *
2606
- * Sampling stops at `sampleLimit` coordinates: out-of-range values are a
2607
- * property of the whole file, so a prefix is enough and a 3-million-coordinate
2608
- * collection is not walked twice.
2609
- *
2610
- * @returns Details when a coordinate is out of geographic range, else null.
2611
- */
2612
- export declare function detectNonGeographicCoordinates(geojson: GeoJSON.FeatureCollection | undefined, sampleLimit?: number): NonGeographicCoordinates | null;
2613
- //#endregion
2614
4
  //#region src/diagram.d.ts
2615
5
  /**
2616
6
  * Hard cap on how many features get a diagram. Each diagram is rasterized into
@@ -2779,280 +169,6 @@ export declare const PHOTO_PROPERTY = "photo";
2779
169
  */
2780
170
  export declare const PHOTO_FULL_PROPERTY = "photo_full";
2781
171
  //#endregion
2782
- //#region src/ellipsoids.d.ts
2783
- /**
2784
- * Celestial-body ellipsoids and the planetary basemaps that pair with them.
2785
- *
2786
- * GeoLibre is Earth-centric by construction: MapLibre only renders Web Mercator
2787
- * and all vector data is kept as WGS84 lon/lat GeoJSON. This module does *not*
2788
- * change that — MapLibre still treats the planet as a unit sphere and there is
2789
- * no true multi-CRS rendering (see maplibre-gl-js#168). What it adds is a
2790
- * per-project notion of *which body* the coordinates describe, so that:
2791
- *
2792
- * - distance / area / scale measurements use that body's radius instead of a
2793
- * hardcoded Earth radius, and
2794
- * - Moon / Mars basemaps (published in a Web-Mercator tiling scheme) can be
2795
- * selected like any other basemap.
2796
- *
2797
- * The active ellipsoid is a lightweight module-level singleton kept in sync with
2798
- * the project's map preferences (see the store's `setPreferences`). Measurement
2799
- * helpers read it lazily at call time, so callers in other packages don't need
2800
- * the value threaded through their signatures — a deliberate prototype-friendly
2801
- * shortcut over a full CRS/context refactor.
2802
- */
2803
- /** A biaxial (rotational) ellipsoid describing a celestial body. */
2804
- export interface Ellipsoid {
2805
- /** Stable id persisted in the project (`map.ellipsoidId`). */
2806
- id: string;
2807
- /** Human-readable name shown in the UI. */
2808
- name: string;
2809
- /** Equatorial (semi-major) radius in metres — the "web mercator" radius. */
2810
- semiMajorAxisMeters: number;
2811
- /**
2812
- * Inverse flattening `1/f`. `0` denotes a perfect sphere (Moon), in which case
2813
- * the polar radius equals {@link semiMajorAxisMeters}.
2814
- */
2815
- inverseFlattening: number;
2816
- }
2817
- /**
2818
- * Built-in ellipsoids. Earth is WGS 84; every other body uses IAU-adopted
2819
- * figures. Only Earth and Mars are appreciably oblate; the rest are modelled as
2820
- * spheres (`inverseFlattening: 0`), which is how the IAU/USGS simple-cylindrical
2821
- * products treat them and is well within the accuracy MapLibre's unit-sphere
2822
- * rendering can represent anyway.
2823
- */
2824
- export declare const ELLIPSOIDS: readonly [{
2825
- readonly id: "earth";
2826
- readonly name: "Earth (WGS 84)";
2827
- readonly semiMajorAxisMeters: 6378137;
2828
- readonly inverseFlattening: 298.257223563;
2829
- }, {
2830
- readonly id: "moon";
2831
- readonly name: "Moon";
2832
- readonly semiMajorAxisMeters: 1737400;
2833
- readonly inverseFlattening: 0;
2834
- }, {
2835
- readonly id: "mars";
2836
- readonly name: "Mars (IAU 2000)";
2837
- readonly semiMajorAxisMeters: 3396190;
2838
- readonly inverseFlattening: 169.894447;
2839
- }, {
2840
- readonly id: "mercury";
2841
- readonly name: "Mercury";
2842
- readonly semiMajorAxisMeters: 2439400;
2843
- readonly inverseFlattening: 0;
2844
- }, {
2845
- readonly id: "venus";
2846
- readonly name: "Venus";
2847
- readonly semiMajorAxisMeters: 6051800;
2848
- readonly inverseFlattening: 0;
2849
- }, {
2850
- readonly id: "io";
2851
- readonly name: "Io";
2852
- readonly semiMajorAxisMeters: 1821600;
2853
- readonly inverseFlattening: 0;
2854
- }, {
2855
- readonly id: "europa";
2856
- readonly name: "Europa";
2857
- readonly semiMajorAxisMeters: 1560800;
2858
- readonly inverseFlattening: 0;
2859
- }, {
2860
- readonly id: "ganymede";
2861
- readonly name: "Ganymede";
2862
- readonly semiMajorAxisMeters: 2631200;
2863
- readonly inverseFlattening: 0;
2864
- }, {
2865
- readonly id: "callisto";
2866
- readonly name: "Callisto";
2867
- readonly semiMajorAxisMeters: 2410300;
2868
- readonly inverseFlattening: 0;
2869
- }, {
2870
- readonly id: "titan";
2871
- readonly name: "Titan";
2872
- readonly semiMajorAxisMeters: 2574730;
2873
- readonly inverseFlattening: 0;
2874
- }, {
2875
- readonly id: "pluto";
2876
- readonly name: "Pluto";
2877
- readonly semiMajorAxisMeters: 1188300;
2878
- readonly inverseFlattening: 0;
2879
- }, {
2880
- readonly id: "charon";
2881
- readonly name: "Charon";
2882
- readonly semiMajorAxisMeters: 606000;
2883
- readonly inverseFlattening: 0;
2884
- }];
2885
- export type EllipsoidId = (typeof ELLIPSOIDS)[number]["id"];
2886
- export declare const DEFAULT_ELLIPSOID_ID: EllipsoidId;
2887
- /** Look an ellipsoid up by id, falling back to Earth for unknown ids. */
2888
- export declare function getEllipsoid(id: string | undefined): Ellipsoid;
2889
- /**
2890
- * Mean radius `R = (2a + b) / 3` in metres, where `b` is the polar radius
2891
- * derived from the inverse flattening. This is the radius used for spherical
2892
- * (haversine) distance and area math.
2893
- */
2894
- export declare function meanRadiusMeters(ellipsoid: Ellipsoid): number;
2895
- /** Earth's mean radius in metres — the radius Turf.js and MapLibre bake in. */
2896
- export declare const EARTH_MEAN_RADIUS_METERS: number;
2897
- /**
2898
- * Point the measurement helpers at a body. Unknown ids fall back to Earth so a
2899
- * malformed project can never break measurements. Safe to call on every
2900
- * preferences change; it is a cheap assignment.
2901
- */
2902
- export declare function setActiveEllipsoidId(id: string | undefined): void;
2903
- export declare function getActiveEllipsoid(): Ellipsoid;
2904
- /** Mean radius (metres) of the active body — for haversine distance/area. */
2905
- export declare function getActiveMeanRadiusMeters(): number;
2906
- /** Semi-major axis (metres) of the active body — for its Web-Mercator scale. */
2907
- export declare function getActiveSemiMajorAxisMeters(): number;
2908
- /**
2909
- * The active body's mean radius as a fraction of Earth's — `R_body / R_earth`,
2910
- * and exactly `1` on Earth.
2911
- *
2912
- * Everything GeoLibre measures ultimately comes from lon/lat *angles*, and the
2913
- * only thing that turns an angle into a ground distance is the radius it is
2914
- * multiplied by. Turf.js hardcodes Earth's, with no per-call override (that is
2915
- * the "Turf is Earth-locked" half of GeoLibre#1128), so rather than patch or
2916
- * replace Turf we post-scale what it returns and pre-scale what we hand it. The
2917
- * result is exact for a sphere, which is how every non-Earth body GeoLibre
2918
- * offers is modelled anyway, and within Mars' 0.6% flattening at worst.
2919
- *
2920
- * Suggested by @thareUSGS (USGS Astrogeology) on GeoLibre#1128 as the practical
2921
- * workaround planetary web maps have long used on Earth-locked engines.
2922
- */
2923
- export declare function getActiveBodyRadiusRatio(): number;
2924
- /**
2925
- * Convert a length Turf.js measured on Earth into the active body's true ground
2926
- * length. A no-op on Earth.
2927
- *
2928
- * Use on anything Turf *returns* — a distance, a length, a perimeter.
2929
- *
2930
- * The correction is a dimensionless ratio, so it is unit-agnostic: pass the
2931
- * length in whatever unit Turf gave it back (metres, kilometres, miles) and the
2932
- * result is in that same unit. There is no hidden unit conversion here.
2933
- */
2934
- export declare function earthLengthToBody(length: number): number;
2935
- /**
2936
- * Convert a true ground length on the active body into the Earth-equivalent
2937
- * length to hand Turf.js. A no-op on Earth.
2938
- *
2939
- * Use on anything passed *into* Turf as a distance — a buffer width, a circle
2940
- * or sector radius, a search threshold — so the resulting geometry spans that
2941
- * distance on this body rather than on Earth.
2942
- *
2943
- * Unit-agnostic like {@link earthLengthToBody}: pass the length in whatever
2944
- * unit you are about to hand Turf alongside it, and the result is in that unit.
2945
- */
2946
- export declare function bodyLengthToEarth(length: number): number;
2947
- /**
2948
- * Convert an area Turf.js measured on Earth into the active body's true ground
2949
- * area. A no-op on Earth.
2950
- *
2951
- * Areas scale with the *square* of the radius ratio, so this is not the same
2952
- * correction as {@link earthLengthToBody}. Unit-agnostic in the same way, for
2953
- * whatever squared unit the area is expressed in.
2954
- */
2955
- export declare function earthAreaToBody(area: number): number;
2956
- /**
2957
- * A raster basemap for a celestial body — the Moon and Mars mosaics, plus the
2958
- * Earth satellite imagery the planet switcher pairs with Earth. The tiles are
2959
- * XYZ (or TMS, see {@link scheme}) images in that body's Web-Mercator scheme, so
2960
- * MapLibre renders them directly. The `styleUrl` is a `geolibre://basemap/<id>`
2961
- * sentinel; the map controller expands it into a raster style at apply time (it
2962
- * is not a fetchable URL).
2963
- */
2964
- export interface PlanetaryBasemap {
2965
- id: string;
2966
- name: string;
2967
- /** Sentinel stored as the basemap style URL. */
2968
- styleUrl: string;
2969
- /** XYZ (or TMS, see {@link scheme}) tile template. */
2970
- tileUrl: string;
2971
- /**
2972
- * Tile row ordering. OpenPlanetaryMap's single-layer raster mosaics (the S3
2973
- * datasets) are published as **TMS** (tile origin bottom-left, Y flipped),
2974
- * whereas the CARTO `opmbuilder` named maps are standard **XYZ**. Omit for
2975
- * XYZ; MapLibre defaults to `"xyz"` when `scheme` is absent.
2976
- */
2977
- scheme?: "tms";
2978
- /** Max native zoom of the source (MapLibre overzooms beyond this). */
2979
- maxZoom: number;
2980
- /** Attribution shown on the map. */
2981
- attribution: string;
2982
- /** The body this basemap depicts, so selecting it can set the ellipsoid. */
2983
- ellipsoidId: EllipsoidId;
2984
- }
2985
- export declare const PLANETARY_BASEMAP_SENTINEL_PREFIX = "geolibre://basemap/";
2986
- export declare const PLANETARY_BASEMAPS: readonly PlanetaryBasemap[];
2987
- /**
2988
- * The section a planetary basemap is grouped under in the New Project and
2989
- * Change Basemap pickers. The Moon and Mars keep dedicated sections (they are
2990
- * the most-used bodies and each has several mosaics); every other body is
2991
- * collapsed into a single "other" section so the picker isn't a wall of
2992
- * one-item headings.
2993
- */
2994
- export type PlanetaryBasemapSectionId = "moon" | "mars" | "other";
2995
- /** A picker section paired with the planetary basemaps it lists. */
2996
- export interface PlanetaryBasemapGroup {
2997
- id: PlanetaryBasemapSectionId;
2998
- basemaps: readonly PlanetaryBasemap[];
2999
- }
3000
- /**
3001
- * {@link PLANETARY_BASEMAPS} grouped for the pickers: one section each for the
3002
- * Moon and Mars, then a single "Other celestial bodies" section for the rest,
3003
- * in {@link PLANETARY_BASEMAPS} order (grouped by body). Empty sections are
3004
- * dropped.
3005
- */
3006
- export declare const PLANETARY_BASEMAP_GROUPS: readonly PlanetaryBasemapGroup[];
3007
- /** Resolve a `geolibre://basemap/<id>` sentinel to its planetary basemap. */
3008
- export declare function getPlanetaryBasemapByStyleUrl(styleUrl: string | undefined): PlanetaryBasemap | undefined;
3009
- /** Look a planetary basemap up by its stable id. */
3010
- export declare function getPlanetaryBasemapById(id: string): PlanetaryBasemap | undefined;
3011
- /**
3012
- * The celestial bodies offered by the Layers-panel planet switcher, in menu
3013
- * order (like Google Earth's planet dropdown). Each body is paired with the
3014
- * basemap the switcher applies for it — USGS satellite imagery for Earth, the
3015
- * OpenPlanetaryMap Hillshaded Albedo mosaic for the Moon, and the OpenPlanetaryMap
3016
- * Viking mosaic for Mars.
3017
- */
3018
- export declare const PLANET_SWITCHER_OPTIONS: readonly [{
3019
- readonly ellipsoidId: "earth";
3020
- readonly basemapId: "earth-usgs-imagery";
3021
- }, {
3022
- readonly ellipsoidId: "moon";
3023
- readonly basemapId: "moon-hillshaded-albedo";
3024
- }, {
3025
- readonly ellipsoidId: "mars";
3026
- readonly basemapId: "mars-viking-mdim21";
3027
- }, {
3028
- readonly ellipsoidId: "callisto";
3029
- readonly basemapId: "callisto-galileo-voyager";
3030
- }, {
3031
- readonly ellipsoidId: "charon";
3032
- readonly basemapId: "charon-mosaic";
3033
- }, {
3034
- readonly ellipsoidId: "europa";
3035
- readonly basemapId: "europa-galileo-voyager";
3036
- }, {
3037
- readonly ellipsoidId: "ganymede";
3038
- readonly basemapId: "ganymede-galileo-voyager";
3039
- }, {
3040
- readonly ellipsoidId: "io";
3041
- readonly basemapId: "io-galileo-color";
3042
- }, {
3043
- readonly ellipsoidId: "mercury";
3044
- readonly basemapId: "mercury-messenger-color";
3045
- }, {
3046
- readonly ellipsoidId: "pluto";
3047
- readonly basemapId: "pluto-mosaic";
3048
- }, {
3049
- readonly ellipsoidId: "titan";
3050
- readonly basemapId: "titan-cassini";
3051
- }, {
3052
- readonly ellipsoidId: "venus";
3053
- readonly basemapId: "venus-magellan-color";
3054
- }];
3055
- //#endregion
3056
172
  //#region src/regional-basemaps.d.ts
3057
173
  /**
3058
174
  * Raster basemaps for regions the default catalog does not serve well.
@@ -3148,57 +264,6 @@ export declare function getRegionalBasemapById(id: string | undefined): Regional
3148
264
  /** Whether a style URL is a regional-basemap sentinel, resolvable or not. */
3149
265
  export declare function isRegionalBasemapSentinel(styleUrl: string | undefined): boolean;
3150
266
  //#endregion
3151
- //#region src/geojson-z.d.ts
3152
- /**
3153
- * Helpers for vector data whose coordinates carry a Z value (elevation), e.g.
3154
- * GPX tracks with `<ele>` readings or LineStringZ/PointZ geometries. MapLibre's
3155
- * 2D style layers ignore the third coordinate, so layers that want to render
3156
- * their Z values use these helpers to detect and rescale elevations before
3157
- * handing the data to a deck.gl overlay. The same data can carry its elevation
3158
- * in a `bbox` member too, which `horizontalBbox` trims back to two dimensions.
3159
- */
3160
- /**
3161
- * Reduces a bounding box to its horizontal extent, `[west, south, east, north]`.
3162
- *
3163
- * RFC 7946 §5 lets a `bbox` member carry elevation, in which case it holds six
3164
- * values — `[west, south, minAltitude, east, north, maxAltitude]` — and
3165
- * `@turf/bbox` returns a collection's own member verbatim rather than
3166
- * recomputing it. A consumer reading such a box as four values takes the
3167
- * altitude for a longitude and the east edge for a latitude; that is how a 3D
3168
- * feed reached MapLibre as an out-of-range latitude (#2358).
3169
- *
3170
- * Returns null when there is no usable horizontal extent: a box of any other
3171
- * length, or one carrying a non-finite value anywhere — an empty collection, or
3172
- * one whose features all have a null geometry, bboxes to ±Infinity, and a box
3173
- * whose altitudes are not finite is not one to trust the rest of.
3174
- *
3175
- * @param box - A GeoJSON or Turf bounding box, of four or six values.
3176
- */
3177
- export declare function horizontalBbox(box: readonly number[] | null | undefined): [number, number, number, number] | null;
3178
- /**
3179
- * Returns true when any coordinate in the GeoJSON carries a finite, non-zero
3180
- * Z value. All-zero Z arrays are treated as flat data because rendering them
3181
- * "in 3D" would be indistinguishable from the 2D result. The result is
3182
- * cached per GeoJSON object, so repeated calls on the same data are free.
3183
- *
3184
- * @param geojson - Any GeoJSON object (geometry, feature, or collection).
3185
- * @param includeZero - Accept zero elevations when explicit absolute-height placement matters.
3186
- */
3187
- export declare function geojsonHasZCoordinates(geojson: GeoJSON$1 | null | undefined, includeZero?: boolean): boolean;
3188
- /**
3189
- * Returns a copy of the GeoJSON with every coordinate's Z value mapped to
3190
- * `z * verticalScale + offset` (missing or non-finite Z treated as 0). Used
3191
- * to apply vertical exaggeration and a constant altitude offset before 3D
3192
- * rendering. The identity transform (`verticalScale === 1 && offset === 0`)
3193
- * still produces a copy so the non-finite-Z sanitization applies on every
3194
- * path — WebGL does not handle NaN positions gracefully.
3195
- *
3196
- * @param geojson - The feature collection to transform.
3197
- * @param verticalScale - Multiplier applied to each Z value.
3198
- * @param offset - Constant altitude in meters added after scaling.
3199
- */
3200
- export declare function transformGeojsonElevation(geojson: FeatureCollection, verticalScale: number, offset: number): FeatureCollection;
3201
- //#endregion
3202
267
  //#region src/color-ramp.d.ts
3203
268
  /**
3204
269
  * Color-ramp definitions and classification-break helpers shared by the
@@ -3561,131 +626,41 @@ export declare function routeResponseToFeatures(response: unknown, points: Routi
3561
626
  */
3562
627
  export declare function compareSequenceValues(a: unknown, b: unknown): number;
3563
628
  /**
3564
- * Error thrown when a Valhalla request returns a non-2xx response. Carries the
3565
- * HTTP status so callers can branch on it (e.g. a 4xx rejection) without
3566
- * pattern-matching the message string.
3567
- */
3568
- export declare class RoutingRequestError extends Error {
3569
- readonly status: number;
3570
- constructor(status: number, statusText: string);
3571
- }
3572
- /**
3573
- * Requests an isochrone from the Valhalla server.
3574
- *
3575
- * @param endpoint - The Valhalla base URL.
3576
- * @param body - The request body from {@link buildIsochroneRequest}.
3577
- * @param signal - Optional abort signal.
3578
- * @returns The Valhalla GeoJSON response.
3579
- */
3580
- export declare function requestIsochrone(endpoint: string, body: IsochroneRequestBody, signal?: AbortSignal): Promise<unknown>;
3581
- /**
3582
- * Requests an OD cost matrix from the Valhalla server.
3583
- *
3584
- * @param endpoint - The Valhalla base URL.
3585
- * @param body - The request body from {@link buildMatrixRequest}.
3586
- * @param signal - Optional abort signal.
3587
- * @returns The Valhalla matrix response.
3588
- */
3589
- export declare function requestMatrix(endpoint: string, body: MatrixRequestBody, signal?: AbortSignal): Promise<unknown>;
3590
- /**
3591
- * Requests a route through ordered waypoints from the Valhalla server.
3592
- *
3593
- * @param endpoint - The Valhalla base URL.
3594
- * @param body - The request body from {@link buildRouteRequest}.
3595
- * @param signal - Optional abort signal.
3596
- * @returns The Valhalla `/route` response.
3597
- */
3598
- export declare function requestRoute(endpoint: string, body: RouteRequestBody, signal?: AbortSignal): Promise<unknown>;
3599
- //#endregion
3600
- //#region src/polyline.d.ts
3601
- /**
3602
- * Pure TypeScript Google Encoded Polyline algorithm codec.
3603
- *
3604
- * Supports arbitrary precision:
3605
- * - precision = 5: Google Maps standard, OSRM, HERE (factor 1e5, ~1.1m precision)
3606
- * - precision = 6: Valhalla, Mapbox, Open Source Routing Machine 6 (factor 1e6, ~0.11m precision)
3607
- *
3608
- * Coordinates are represented as `[lon, lat]` pairs in GeoJSON format.
3609
- */
3610
- /**
3611
- * Unescapes backslashes in a polyline string.
3612
- *
3613
- * When polyline strings are copied from JSON payloads, log files, or source code,
3614
- * backslashes are frequently double-escaped as `\\` or formatted with escape sequences.
3615
- *
3616
- * @param str - Input string with potentially escaped backslashes.
3617
- * @returns Cleaned polyline string.
3618
- */
3619
- export declare function unescapePolyline(str: string): string;
3620
- export interface PolylineDecodeResult {
3621
- coordinates: [number, number][];
3622
- complete: boolean;
3623
- }
3624
- /**
3625
- * Decodes an encoded polyline string into an array of `[lon, lat]` coordinates,
3626
- * along with a `complete` flag indicating whether the entire string was consumed
3627
- * as complete coordinate pairs without truncation or malformed characters.
3628
- *
3629
- * @param encoded - The ASCII-encoded polyline string.
3630
- * @param precision - Number of decimal digits (default: 5).
3631
- * @param unescape - Whether to unescape double-escaped backslashes before decoding (default: false).
3632
- * @returns Object with decoded `coordinates` and `complete` boolean status.
3633
- */
3634
- export declare function decodePolylineDetailed(encoded: string, precision?: number, unescape?: boolean): PolylineDecodeResult;
3635
- /**
3636
- * Decodes an encoded polyline string into an array of `[lon, lat]` coordinates.
3637
- *
3638
- * @param encoded - The ASCII-encoded polyline string.
3639
- * @param precision - Number of decimal digits (default: 5).
3640
- * @param unescape - Whether to unescape double-escaped backslashes before decoding (default: false).
3641
- * @returns Array of `[lon, lat]` coordinate pairs in GeoJSON order.
3642
- */
3643
- export declare function decodePolyline(encoded: string, precision?: number, unescape?: boolean): [number, number][];
3644
- /**
3645
- * Encodes an array of `[lon, lat]` coordinates into a Google Encoded Polyline string.
3646
- *
3647
- * @param coordinates - Array of `[lon, lat]` coordinate pairs.
3648
- * @param precision - Number of decimal digits (default: 5).
3649
- * @returns The encoded polyline string.
629
+ * Error thrown when a Valhalla request returns a non-2xx response. Carries the
630
+ * HTTP status so callers can branch on it (e.g. a 4xx rejection) without
631
+ * pattern-matching the message string.
3650
632
  */
3651
- export declare function encodePolyline(coordinates: [number, number][], precision?: number): string;
633
+ export declare class RoutingRequestError extends Error {
634
+ readonly status: number;
635
+ constructor(status: number, statusText: string);
636
+ }
3652
637
  /**
3653
- * Decodes an encoded polyline string into a GeoJSON `Feature<LineString>`.
638
+ * Requests an isochrone from the Valhalla server.
3654
639
  *
3655
- * @param encoded - The polyline string.
3656
- * @param precision - Coordinate precision (default: 5).
3657
- * @param properties - Optional feature properties.
3658
- * @returns A GeoJSON LineString Feature.
640
+ * @param endpoint - The Valhalla base URL.
641
+ * @param body - The request body from {@link buildIsochroneRequest}.
642
+ * @param signal - Optional abort signal.
643
+ * @returns The Valhalla GeoJSON response.
3659
644
  */
3660
- export declare function polylineStrToGeoJSON(encoded: string, precision?: number, properties?: GeoJsonProperties): Feature<LineString>;
645
+ export declare function requestIsochrone(endpoint: string, body: IsochroneRequestBody, signal?: AbortSignal): Promise<unknown>;
3661
646
  /**
3662
- * Encodes a GeoJSON LineString, MultiLineString, Feature, or FeatureCollection into encoded polyline string(s).
647
+ * Requests an OD cost matrix from the Valhalla server.
3663
648
  *
3664
- * @param geojson - The GeoJSON input geometry or feature.
3665
- * @param precision - Coordinate precision (default: 5).
3666
- * @returns An encoded polyline string for LineString, or an array of strings for MultiLineString / FeatureCollection.
649
+ * @param endpoint - The Valhalla base URL.
650
+ * @param body - The request body from {@link buildMatrixRequest}.
651
+ * @param signal - Optional abort signal.
652
+ * @returns The Valhalla matrix response.
3667
653
  */
3668
- export declare function geoJSONToPolylineStr(geojson: LineString | MultiLineString | Feature<LineString | MultiLineString> | FeatureCollection<LineString | MultiLineString>, precision?: number): string | string[];
3669
- export interface PolylineBatchOptions {
3670
- /** Coordinate precision (default: 5) */
3671
- precision?: number;
3672
- /** Whether to unescape `\\` backslashes (default: true) */
3673
- unescape?: boolean;
3674
- /** Custom delimiter string or RegExp separating individual polylines (default: newline) */
3675
- delimiter?: string | RegExp;
3676
- /** Whether to merge lines into a single MultiLineString feature (default: false) */
3677
- asMultiLine?: boolean;
3678
- /** Optional base properties to assign to output features */
3679
- baseProperties?: GeoJsonProperties;
3680
- }
654
+ export declare function requestMatrix(endpoint: string, body: MatrixRequestBody, signal?: AbortSignal): Promise<unknown>;
3681
655
  /**
3682
- * Parses and batch-decodes multiple polylines from a text block or file content.
656
+ * Requests a route through ordered waypoints from the Valhalla server.
3683
657
  *
3684
- * @param input - Multiline string or delimited text containing encoded polylines.
3685
- * @param options - Decoding and parsing configuration.
3686
- * @returns A FeatureCollection containing LineString (or MultiLineString) features.
658
+ * @param endpoint - The Valhalla base URL.
659
+ * @param body - The request body from {@link buildRouteRequest}.
660
+ * @param signal - Optional abort signal.
661
+ * @returns The Valhalla `/route` response.
3687
662
  */
3688
- export declare function batchDecodePolylines(input: string, options?: PolylineBatchOptions): FeatureCollection<LineString | MultiLineString>;
663
+ export declare function requestRoute(endpoint: string, body: RouteRequestBody, signal?: AbortSignal): Promise<unknown>;
3689
664
  //#endregion
3690
665
  //#region src/vector-color.d.ts
3691
666
  /**
@@ -4259,6 +1234,47 @@ export declare function inferFieldTypes(features: ReadonlyArray<{
4259
1234
  properties?: Record<string, unknown> | null;
4260
1235
  }>, names: string[]): Record<string, ExpressionFieldType>;
4261
1236
  //#endregion
1237
+ //#region src/style-layer-evaluator.d.ts
1238
+ /** The geometry kinds a MapLibre style expression tells apart (`["geometry-type"]`). */
1239
+ export type StyleGeometryKind = "Point" | "LineString" | "Polygon";
1240
+ /** A feature as a style layer's expressions read it. */
1241
+ export interface StyleFeatureInput {
1242
+ id?: string | number;
1243
+ properties: Record<string, unknown> | null | undefined;
1244
+ kind: StyleGeometryKind;
1245
+ }
1246
+ /** A style layer's filter and property values, evaluated as MapLibre would. */
1247
+ export interface StyleLayerEvaluator {
1248
+ /** Whether the layer's `filter` keeps the feature at `zoom`. */
1249
+ passes: (zoom: number, feature: StyleFeatureInput) => boolean;
1250
+ /**
1251
+ * A paint or layout property's value for a feature at `zoom`: the layer's
1252
+ * value (a literal, a legacy function or an expression) or the spec's
1253
+ * default. A color comes back as `[r, g, b, a]` with channels 0-255 and
1254
+ * alpha 0-1; formatted text as its plain string. `undefined` for a property
1255
+ * the spec does not know or an expression that does not compile.
1256
+ */
1257
+ value: (group: "paint" | "layout", name: string, zoom: number, feature: StyleFeatureInput) => unknown;
1258
+ }
1259
+ interface StyleLayerInput {
1260
+ id?: string;
1261
+ type: string;
1262
+ filter?: unknown;
1263
+ paint?: Record<string, unknown>;
1264
+ layout?: Record<string, unknown>;
1265
+ }
1266
+ /**
1267
+ * Compile a MapLibre style layer's filter and properties for evaluation
1268
+ * outside MapLibre, for a renderer that draws the layer itself (the ArcGIS
1269
+ * renderer drawing a plugin's own GeoJSON overlay, say). Uses the style spec's
1270
+ * own evaluator, so data-driven and zoom expressions, legacy functions and
1271
+ * defaults behave as they do on MapLibre. Each property compiles once.
1272
+ *
1273
+ * @param layer - The style layer (its `type`, `filter`, `paint`, `layout`).
1274
+ * @returns The layer's evaluator.
1275
+ */
1276
+ export declare function createStyleLayerEvaluator(layer: StyleLayerInput): StyleLayerEvaluator;
1277
+ //#endregion
4262
1278
  //#region src/document-locale.d.ts
4263
1279
  /**
4264
1280
  * The document language, so formatted numbers and dates follow the UI locale.
@@ -4447,6 +1463,21 @@ export declare function supportsBridgedOpacity(layerId: string): boolean;
4447
1463
  export declare function rendererAppliesOpacity(layer: {
4448
1464
  type: string;
4449
1465
  }, renderer: string | undefined): boolean;
1466
+ /**
1467
+ * Whether `layer` is an Add Vector Layer layer GeoLibre adopted
1468
+ * (`maplibre-gl-vector-adopted`, ADOPTED_VECTOR_SOURCE_KIND in
1469
+ * @geolibre/plugins) that has no features yet. A project saves a URL- or
1470
+ * path-backed adopted layer without them, and the vector control reads them
1471
+ * again on reopen. Until then its `source.url` may name GeoParquet or
1472
+ * GeoPackage, so a renderer must wait rather than load that URL as GeoJSON.
1473
+ *
1474
+ * @param layer - A store layer.
1475
+ * @returns True while the adopted layer awaits its features.
1476
+ */
1477
+ export declare function isAdoptedVectorAwaitingFeatures(layer: {
1478
+ metadata: Record<string, unknown>;
1479
+ geojson?: unknown;
1480
+ }): boolean;
4450
1481
  //#endregion
4451
1482
  //#region src/attribute-form.d.ts
4452
1483
  /** Options threaded to expression evaluation (constraints, visibility). */
@@ -4520,6 +1551,160 @@ export declare function validateAttributeFormField(config: AttributeFormFieldCon
4520
1551
  */
4521
1552
  export declare function validateAttributeFormValues(form: AttributeFormConfig | undefined, properties: Record<string, unknown>, options?: AttributeFormEvalOptions): AttributeFormValidation;
4522
1553
  //#endregion
1554
+ //#region src/layer-descriptive-metadata.d.ts
1555
+ /** STAC specification version the Item export targets. */
1556
+ export declare const STAC_VERSION = "1.0.0";
1557
+ /** Schema URL of the STAC processing extension, declared for `processing:lineage`. */
1558
+ export declare const STAC_PROCESSING_EXTENSION = "https://stac-extensions.github.io/processing/v1.1.0/schema.json";
1559
+ /**
1560
+ * Split comma-separated keyword text into a clean keyword list.
1561
+ *
1562
+ * @param text - Text such as `"roads, transport, osm"`.
1563
+ * @returns The trimmed, non-empty keywords with case-insensitive duplicates removed.
1564
+ */
1565
+ export declare function parseMetadataKeywords(text: string): string[];
1566
+ /**
1567
+ * Normalize a raw descriptive-metadata value (from a project file, a plugin or
1568
+ * the Metadata dialog): keep only string fields, trim them, and drop empty
1569
+ * fields, empty keywords and links without an address.
1570
+ *
1571
+ * Values are not validated here — a malformed email or date in a hand-edited
1572
+ * file is kept so the dialog can show and fix it rather than silently lose it.
1573
+ *
1574
+ * @param value - Any value.
1575
+ * @returns The cleaned record, or `undefined` when nothing is left, so an
1576
+ * empty record is never written to a project.
1577
+ */
1578
+ export declare function normalizeLayerDescriptiveMetadata(value: unknown): LayerDescriptiveMetadata | undefined;
1579
+ /**
1580
+ * Whether a string is a plausible email address (`local@domain.tld`).
1581
+ *
1582
+ * @param value - The candidate address.
1583
+ * @returns `true` when it has the shape of an email address.
1584
+ */
1585
+ export declare function isValidMetadataEmail(value: string): boolean;
1586
+ /**
1587
+ * Whether a string is an absolute URL with a scheme a metadata link may use
1588
+ * (http, https, ftp, s3, gs, mailto). Script and data URLs are rejected.
1589
+ *
1590
+ * @param value - The candidate URL.
1591
+ * @returns `true` when it parses as an allowed absolute URL.
1592
+ */
1593
+ export declare function isValidMetadataUrl(value: string): boolean;
1594
+ /**
1595
+ * Whether a string is an ISO 8601 date (`YYYY-MM-DD`) or date-time
1596
+ * (`YYYY-MM-DDTHH:MM[:SS[.fff]][Z|±HH:MM]`) naming a real instant.
1597
+ *
1598
+ * @param value - The candidate date.
1599
+ * @returns `true` when it is a valid ISO 8601 date or date-time.
1600
+ */
1601
+ export declare function isValidMetadataDate(value: string): boolean;
1602
+ /**
1603
+ * Convert a validated metadata date to an RFC 3339 UTC date-time, as STAC
1604
+ * requires. A date-only bound covers its whole day: a start becomes the first
1605
+ * second of the day, an end the last. A date-time without an offset is read
1606
+ * as UTC so the export does not depend on the exporting machine's time zone.
1607
+ *
1608
+ * @param value - A date accepted by {@link isValidMetadataDate}.
1609
+ * @param bound - Whether the value starts or ends an interval.
1610
+ * @returns The RFC 3339 date-time, or `null` for an invalid date.
1611
+ */
1612
+ export declare function metadataDateToRfc3339(value: string, bound: "start" | "end"): string | null;
1613
+ /** Why a metadata field failed {@link validateLayerDescriptiveMetadata}. */
1614
+ export type LayerMetadataIssueCode = "email" | "url" | "date" | "dateOrder";
1615
+ /** One validation failure, addressed by a dotted field path. */
1616
+ export interface LayerMetadataIssue {
1617
+ /**
1618
+ * The failing field: `contact.email`, `temporalExtent.start`,
1619
+ * `temporalExtent.end`, or `links.<index>.href`.
1620
+ */
1621
+ field: string;
1622
+ code: LayerMetadataIssueCode;
1623
+ }
1624
+ /**
1625
+ * Validate the fields of a descriptive-metadata record that have a format:
1626
+ * the contact email, the temporal-extent dates (and their order), and the
1627
+ * link URLs. Free-text fields are not checked.
1628
+ *
1629
+ * @param metadata - The record to check (normalized or a dialog draft).
1630
+ * @returns Every failure found; empty when the record is valid.
1631
+ */
1632
+ export declare function validateLayerDescriptiveMetadata(metadata: LayerDescriptiveMetadata | undefined): LayerMetadataIssue[];
1633
+ /** A STAC 1.0 link object. */
1634
+ export interface StacLink {
1635
+ href: string;
1636
+ rel: string;
1637
+ type?: string;
1638
+ title?: string;
1639
+ }
1640
+ /** A STAC 1.0 asset object. */
1641
+ export interface StacAsset {
1642
+ href: string;
1643
+ type?: string;
1644
+ title?: string;
1645
+ roles?: string[];
1646
+ }
1647
+ /** A STAC 1.0 provider object. */
1648
+ export interface StacProvider {
1649
+ name: string;
1650
+ description?: string;
1651
+ roles?: string[];
1652
+ url?: string;
1653
+ }
1654
+ /** The STAC 1.0 Item {@link buildLayerStacItem} produces. */
1655
+ export interface StacItem {
1656
+ type: "Feature";
1657
+ stac_version: string;
1658
+ stac_extensions: string[];
1659
+ id: string;
1660
+ geometry: Record<string, unknown> | null;
1661
+ bbox?: [number, number, number, number];
1662
+ properties: Record<string, unknown> & {
1663
+ datetime: string | null;
1664
+ };
1665
+ links: StacLink[];
1666
+ assets: Record<string, StacAsset>;
1667
+ }
1668
+ /** Options for {@link buildLayerStacItem}. */
1669
+ export interface LayerStacItemOptions {
1670
+ /** The layer's extent in EPSG:4326 as `[west, south, east, north]`. */
1671
+ bbox?: readonly number[] | null;
1672
+ /** Where the layer's data can be fetched from, when it has a source URL. */
1673
+ assetHref?: string | null;
1674
+ /** Clock used for `datetime` when no temporal extent is recorded. */
1675
+ now?: Date;
1676
+ }
1677
+ /**
1678
+ * Guess the media type of a layer's data asset from the URL's extension, or
1679
+ * the layer type when the URL has none (a COG is flagged cloud-optimized).
1680
+ *
1681
+ * @param href - The asset URL.
1682
+ * @param layerType - The layer's type.
1683
+ * @returns The media type, or `undefined` when it cannot be told.
1684
+ */
1685
+ export declare function stacAssetMediaType(href: string, layerType: GeoLibreLayer["type"]): string | undefined;
1686
+ /**
1687
+ * Build a STAC 1.0 Item describing a layer from its descriptive metadata.
1688
+ *
1689
+ * Mapping: `id` is the layer id; `geometry`/`bbox` come from the WGS84 extent
1690
+ * (a `null` geometry, as STAC allows, when the extent is unknown); `title`
1691
+ * falls back to the layer name; `abstract` becomes `description`; the contact
1692
+ * becomes a `producer` provider; `lineage` becomes `processing:lineage` (with
1693
+ * the processing extension declared); the temporal extent becomes `datetime`
1694
+ * or `start_datetime`/`end_datetime` (with `datetime` set to the export time
1695
+ * when none is recorded, since STAC requires one); links keep their `rel`
1696
+ * (default `related`); and a source URL becomes the `data` asset.
1697
+ *
1698
+ * A license that is not an SPDX-style identifier (STAC 1.0's pattern) is
1699
+ * exported as `proprietary`, with the original text kept in
1700
+ * `geolibre:license`; the attribution is kept in `geolibre:attribution`.
1701
+ *
1702
+ * @param layer - The layer to describe.
1703
+ * @param options - Extent, asset URL and clock; see {@link LayerStacItemOptions}.
1704
+ * @returns The STAC Item, ready to `JSON.stringify`.
1705
+ */
1706
+ export declare function buildLayerStacItem(layer: Pick<GeoLibreLayer, "id" | "name" | "type" | "descriptiveMetadata">, options?: LayerStacItemOptions): StacItem;
1707
+ //#endregion
4523
1708
  //#region src/popup.d.ts
4524
1709
  /** One resolved popup row, ready for the renderer. */
4525
1710
  export interface PopupRow {
@@ -4685,87 +1870,6 @@ export declare function isInlineImageValue(value: unknown): value is string;
4685
1870
  */
4686
1871
  export declare function isSafePopupUrl(value: unknown, allowDataImage?: boolean): value is string;
4687
1872
  //#endregion
4688
- //#region src/joins.d.ts
4689
- /**
4690
- * Match key for a persistent join: empty values (null/undefined/NaN/empty
4691
- * string) never match a row, mirroring a SQL/pandas NaN join key. Non-empty
4692
- * values are keyed by their string form, so a numeric `5` and the string `"5"`
4693
- * join while a zero-padded code like `"01001"` only matches another `"01001"`.
4694
- * Kept in sync with the Processing attribute join's `attributeJoinKey`.
4695
- */
4696
- export declare function layerJoinKey(value: unknown): string | null;
4697
- /**
4698
- * Remove every column previously added by `joins` (per their `addedFields`
4699
- * bookkeeping) from a copy of `features`, restoring the pre-join properties.
4700
- * Features without any tracked column are returned unchanged (same reference).
4701
- */
4702
- export declare function stripJoinFields(features: Feature[], joins: LayerJoin[] | undefined): Feature[];
4703
- /** Result of {@link applyLayerJoins}: joined features plus refreshed join records. */
4704
- export interface ApplyLayerJoinsResult {
4705
- features: Feature[];
4706
- /** Input joins with `addedFields` and `stats` recomputed for this run. */
4707
- joins: LayerJoin[];
4708
- }
4709
- /**
4710
- * Apply `joins` in order to base (already-stripped) `features`, returning new
4711
- * feature objects with the joined columns merged into their properties.
4712
- *
4713
- * Semantics per join: left join, first matching join row wins, and joined
4714
- * columns are null-filled on unmatched features so the schema stays
4715
- * consistent. An output name (prefix + field) that collides with an existing
4716
- * column — from the base data or an earlier join — drops that one column;
4717
- * the join's other columns still apply. A disabled join, or one whose source
4718
- * cannot be resolved, contributes nothing and gets empty `addedFields` and no
4719
- * stats.
4720
- *
4721
- * @param features - The layer's base features (strip previous joins first).
4722
- * @param joins - Join definitions in application order.
4723
- * @param resolveSource - Maps a join's `joinLayerId` to that layer's current
4724
- * feature collection, or `undefined` when the layer is gone (or is the
4725
- * target itself; self-joins are refused by the caller).
4726
- */
4727
- export declare function applyLayerJoins(features: Feature[], joins: LayerJoin[] | undefined, resolveSource: (joinLayerId: string) => FeatureCollection | undefined): ApplyLayerJoinsResult;
4728
- /**
4729
- * Strip-and-reapply this layer's derived columns — persistent joins, then
4730
- * virtual fields (so an expression can read joined columns) — resolving join
4731
- * sources from `allLayers`. Pass `nextJoins`/`nextVirtualFields` to replace
4732
- * the definitions (the layer's current bookkeeping still drives the strip, so
4733
- * removed definitions clean up after themselves); omit them to refresh the
4734
- * existing definitions against current data. Layers without a feature
4735
- * collection are returned unchanged.
4736
- */
4737
- export declare function applyJoinsToLayer(layer: GeoLibreLayer, allLayers: GeoLibreLayer[], nextJoins?: LayerJoin[], nextVirtualFields?: LayerVirtualField[]): GeoLibreLayer;
4738
- /**
4739
- * Ids of every layer whose data (directly or transitively) feeds `layerId`'s
4740
- * joins, following `joinLayerId` edges through each source's own joins.
4741
- * Disabled joins count too: re-enabling one must not spring a cycle. The Joins
4742
- * UI uses this to keep a layer that already consumes the target — however
4743
- * indirectly — out of the join-source picker, so circular joins cannot be
4744
- * authored (the refresh cascade still breaks them defensively for hand-edited
4745
- * projects).
4746
- */
4747
- export declare function collectTransitiveJoinSourceIds(layers: GeoLibreLayer[], layerId: string): Set<string>;
4748
- /**
4749
- * Refresh every layer whose joins (directly or transitively) consume the
4750
- * layer identified by `seedId`, after that layer's data changed — or after it
4751
- * was removed (a gone source resolves to nothing, so its frozen columns strip
4752
- * away). The affected layers re-derive in topological dependency order, so a
4753
- * layer joining two other affected layers sees both of them fresh regardless
4754
- * of their positions in the layer panel. The seed itself is not touched.
4755
- */
4756
- export declare function cascadeLayerJoinRefresh(layers: GeoLibreLayer[], seedId: string): GeoLibreLayer[];
4757
- /**
4758
- * Re-resolve every layer's persistent joins against the freshly loaded layer
4759
- * set (project open). Layers re-derive in topological order of their join
4760
- * dependencies, so a chain (`C joins A, A joins B`) resolves A before C; a
4761
- * hand-edited cycle is broken by forcing one of its members through first
4762
- * (see {@link topologicalJoinOrder}), so consumers downstream of the cycle
4763
- * still re-derive after it. Layers with virtual fields but no joins re-derive
4764
- * too (their expressions run against the freshly loaded features). Layer sets
4765
- * without joins or virtual fields pass through by reference.
4766
- */
4767
- export declare function reapplyLayerJoins(layers: GeoLibreLayer[]): GeoLibreLayer[];
4768
- //#endregion
4769
1873
  //#region src/virtual-fields.d.ts
4770
1874
  /**
4771
1875
  * Virtual fields (QGIS Field Calculator → "Create virtual field", issue
@@ -5141,14 +2245,23 @@ export declare class ElevationFetchError extends Error {
5141
2245
  }
5142
2246
  /** A `fetch`-compatible function, so tests can inject a stub. */
5143
2247
  export type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
2248
+ /**
2249
+ * Normalize longitude into [-180, 180] degrees (handles unwrapped coordinates
2250
+ * from antimeridian cross-overs and world-copy pannings).
2251
+ */
2252
+ export declare function normalizeLongitude(lng: number): number;
2253
+ /**
2254
+ * Clamp latitude into [-90, 90] degrees.
2255
+ */
2256
+ export declare function clampLatitude(lat: number): number;
5144
2257
  /**
5145
2258
  * Fetch elevations (in meters) for an ordered list of coordinates.
5146
2259
  *
5147
2260
  * @param points - Coordinates as `[lng, lat]`, at most {@link MAX_POINTS_PER_REQUEST}
5148
2261
  * @param fetchImpl - Optional `fetch` implementation; defaults to the global `fetch`
5149
2262
  * @returns The elevation in meters for each input coordinate, in the same order
5150
- * @throws {ElevationFetchError} On too many points, a network error, a non-2xx
5151
- * response, a malformed body, or a length mismatch
2263
+ * @throws {ElevationFetchError} On too many points, a non-finite coordinate, a
2264
+ * network error, a non-2xx response, a malformed body, or a length mismatch
5152
2265
  */
5153
2266
  export declare function fetchElevations(points: LngLat[], fetchImpl?: FetchLike): Promise<number[]>;
5154
2267
  /** The slice of the MapLibre map the terrain sampler needs (stubbed in tests). */
@@ -5315,6 +2428,27 @@ export declare function createProjectLayerSerializationCache(): ProjectLayerSeri
5315
2428
  * @returns The same text {@link serializeProject} returns for `project`.
5316
2429
  */
5317
2430
  export declare function serializeProjectWithLayerCache(project: GeoLibreProject, layerSources: readonly object[], cache: ProjectLayerSerializationCache): string;
2431
+ /**
2432
+ * {@link serializeProjectWithLayerCache} that hands the main thread back
2433
+ * between layers, so autosave on a project with several large layers is many
2434
+ * short tasks instead of one 300-400 ms freeze (GeoLibre#2869).
2435
+ *
2436
+ * The cache makes a camera move free, so the cost lands on the first autosave
2437
+ * and on edits to a big layer: each layer that misses the cache is serialized
2438
+ * in its own slice, and the thread is released once a slice has run longer than
2439
+ * a few milliseconds. A single huge layer is still one slice. The result is
2440
+ * identical to the synchronous call. `project` and `layerSources` must not be
2441
+ * mutated while this runs; the store replaces records rather than editing them,
2442
+ * so a snapshot taken from it stays valid.
2443
+ *
2444
+ * @param project Project to serialize, as built from `layerSources`.
2445
+ * @param layerSources Immutable source record for each entry of
2446
+ * `project.layers`, in the same order, used as the cache key.
2447
+ * @param cache Cache shared between calls.
2448
+ * @param yieldFn Hands the thread back; replaced in tests.
2449
+ * @returns The same text {@link serializeProject} returns for `project`.
2450
+ */
2451
+ export declare function serializeProjectWithLayerCacheAsync(project: GeoLibreProject, layerSources: readonly object[], cache: ProjectLayerSerializationCache, yieldFn?: () => Promise<void>): Promise<string>;
5318
2452
  export declare function parseProject(json: string): GeoLibreProject;
5319
2453
  /**
5320
2454
  * Validate and coerce a story map loaded from an untrusted project file.
@@ -5426,6 +2560,18 @@ export declare function normalizeWidgets(value: unknown): DashboardWidget[] | nu
5426
2560
  * @returns An integer column count within [MIN, MAX].
5427
2561
  */
5428
2562
  export declare function normalizeDashboardColumns(value: unknown): number;
2563
+ /**
2564
+ * Coerce an untrusted `interaction` block into a {@link ProjectInteraction}.
2565
+ *
2566
+ * Layer ids are kept even when no such layer exists: the block is replayed
2567
+ * against whatever layers the project has when it opens, and the loader drops
2568
+ * the ones it cannot find. Control names are kept as written for the same
2569
+ * reason; the app ignores names it does not know.
2570
+ *
2571
+ * @param value - Raw `interaction` value from the project JSON or the store.
2572
+ * @returns The normalized block, or null when it carries nothing to apply.
2573
+ */
2574
+ export declare function normalizeProjectInteraction(value: unknown): ProjectInteraction | null;
5429
2575
  export declare function isAllowedPluginManifestUrl(url: string): boolean;
5430
2576
  export declare function normalizeProjectComments(rawComments: unknown): ProjectComment[];
5431
2577
  export declare function projectFromStore(state: {
@@ -5454,6 +2600,8 @@ export declare function projectFromStore(state: {
5454
2600
  /** Project-scoped Style Manager entries (the store's `projectStyleLibrary`). */
5455
2601
  styleLibrary?: StyleLibraryEntry[] | null;
5456
2602
  comments?: ProjectComment[] | null;
2603
+ /** Startup interaction state loaded with the project, written back as-is. */
2604
+ interaction?: ProjectInteraction | null;
5457
2605
  metadata: Record<string, unknown>;
5458
2606
  }): GeoLibreProject;
5459
2607
  /**
@@ -5466,6 +2614,20 @@ export declare function projectFromStore(state: {
5466
2614
  * @returns The unwrapped template, or `tile` itself.
5467
2615
  */
5468
2616
  export declare function portableWmsTileUrl(tile: unknown): unknown;
2617
+ /**
2618
+ * The store form of one project layer: its style completed from
2619
+ * {@link DEFAULT_LAYER_STYLE} and the project's top-level `styles` entry.
2620
+ *
2621
+ * Legacy and externally-authored projects can carry a partial top-level style
2622
+ * alongside newer fields on the layer itself. Those layer fields are kept,
2623
+ * while the top-level copy stays authoritative where it explicitly supplies a
2624
+ * value.
2625
+ *
2626
+ * @param project - The project the layer belongs to (for its `styles` map).
2627
+ * @param layer - One of `project.layers`.
2628
+ * @returns A new layer record ready for the store.
2629
+ */
2630
+ export declare function hydrateProjectLayer(project: Pick<GeoLibreProject, "styles">, layer: GeoLibreLayer): GeoLibreLayer;
5469
2631
  export declare function applyProjectToStore(project: GeoLibreProject): {
5470
2632
  projectName: string;
5471
2633
  mapView: MapViewState;
@@ -5490,6 +2652,7 @@ export declare function applyProjectToStore(project: GeoLibreProject): {
5490
2652
  primaryRenderer: MapRendererKind;
5491
2653
  projectStyleLibrary: StyleLibraryEntry[];
5492
2654
  comments: ProjectComment[];
2655
+ projectInteraction: ProjectInteraction | null;
5493
2656
  metadata: Record<string, unknown>;
5494
2657
  };
5495
2658
  /**
@@ -5509,6 +2672,160 @@ export declare function createProjectTemplate(project: GeoLibreProject, options?
5509
2672
  stripDataLayers?: boolean;
5510
2673
  }): GeoLibreProject;
5511
2674
  //#endregion
2675
+ //#region src/project-diff.d.ts
2676
+ /** One changed value, with bounded human-readable previews of each side. */
2677
+ export interface ProjectValueChange {
2678
+ /** Dotted key path relative to the section, e.g. `fillColor` or `labels.field`. */
2679
+ path: string;
2680
+ /** Preview of the earlier value; `undefined` when the key was absent. */
2681
+ before?: string;
2682
+ /** Preview of the later value; `undefined` when the key is now absent. */
2683
+ after?: string;
2684
+ }
2685
+ /** Counts of embedded GeoJSON feature changes in one layer. */
2686
+ export interface FeatureChangeSummary {
2687
+ added: number;
2688
+ removed: number;
2689
+ modified: number;
2690
+ unchanged: number;
2691
+ /**
2692
+ * How features were matched: `"id"` when every feature carries a unique
2693
+ * `id`, `"geometry"` when none does (matched by geometry hash), `"mixed"`
2694
+ * otherwise.
2695
+ */
2696
+ matchedBy: "id" | "geometry" | "mixed";
2697
+ }
2698
+ /** Changes to one layer between the two projects. */
2699
+ export interface LayerDiff {
2700
+ id: string;
2701
+ /** Layer name in the later project (or the earlier one when removed). */
2702
+ name: string;
2703
+ /** Layer type in the later project (or the earlier one when removed). */
2704
+ type: string;
2705
+ status: "added" | "removed" | "changed";
2706
+ /** Set when the layer was renamed. */
2707
+ renamed?: {
2708
+ before: string;
2709
+ after: string;
2710
+ };
2711
+ /**
2712
+ * Set when the layer changed position relative to the layers present in
2713
+ * both projects (bottom-to-top indexes in each project's `layers`).
2714
+ */
2715
+ moved?: {
2716
+ before: number;
2717
+ after: number;
2718
+ };
2719
+ visibility?: {
2720
+ before: boolean;
2721
+ after: boolean;
2722
+ };
2723
+ opacity?: {
2724
+ before: number;
2725
+ after: number;
2726
+ };
2727
+ /** Style keys other than labels. */
2728
+ style: ProjectValueChange[];
2729
+ /** Label style keys (`style.labels.*`), without the `labels.` prefix. */
2730
+ labels: ProjectValueChange[];
2731
+ /** `filterExpression` and `quickFilters`. */
2732
+ filter: ProjectValueChange[];
2733
+ /** `type`, `source.*` and `sourcePath`. */
2734
+ source: ProjectValueChange[];
2735
+ /** Any other persisted layer field (popup, joins, metadata.*, ...). */
2736
+ other: ProjectValueChange[];
2737
+ /** Embedded GeoJSON feature changes; absent when the data is unchanged. */
2738
+ features?: FeatureChangeSummary;
2739
+ /** Feature count for an added or removed layer with embedded GeoJSON. */
2740
+ featureCount?: number;
2741
+ }
2742
+ /** Changes to one plugin's project state. */
2743
+ export interface PluginDiff {
2744
+ id: string;
2745
+ /** `added`/`removed` mean the plugin was activated/deactivated. */
2746
+ status: "added" | "removed" | "changed";
2747
+ changes: ProjectValueChange[];
2748
+ }
2749
+ /** The full structured difference between two projects. */
2750
+ export interface ProjectDiff {
2751
+ layers: {
2752
+ added: LayerDiff[];
2753
+ removed: LayerDiff[];
2754
+ changed: LayerDiff[];
2755
+ /** True when any layer present in both projects changed position. */
2756
+ reordered: boolean;
2757
+ };
2758
+ camera: ProjectValueChange[];
2759
+ basemap: ProjectValueChange[];
2760
+ /** Map projection (`preferences.map.projection`). */
2761
+ projection: ProjectValueChange[];
2762
+ /** Other project preferences (`preferences.*` minus the projection). */
2763
+ preferences: ProjectValueChange[];
2764
+ plugins: PluginDiff[];
2765
+ /** Plugin manifest URLs added to or removed from the project. */
2766
+ pluginManifests: {
2767
+ added: string[];
2768
+ removed: string[];
2769
+ };
2770
+ /** Project name (`name`), `metadata.description` and other metadata keys. */
2771
+ metadata: ProjectValueChange[];
2772
+ /** Other top-level sections that changed (`legend`, `storymap`, ...). */
2773
+ sections: string[];
2774
+ /** Total number of reported changes; 0 means the projects are equivalent. */
2775
+ changeCount: number;
2776
+ }
2777
+ /** Options for {@link diffProjects}. */
2778
+ export interface ProjectDiffOptions {
2779
+ /** Maximum characters of a before/after preview (default 80). */
2780
+ maxPreviewLength?: number;
2781
+ }
2782
+ /**
2783
+ * Deep structural equality for JSON-like values. A key whose value is
2784
+ * `undefined` is treated as absent, so an in-memory record compares equal to
2785
+ * its JSON round trip.
2786
+ *
2787
+ * @param a - First value.
2788
+ * @param b - Second value.
2789
+ * @returns True when the two values are structurally equal.
2790
+ */
2791
+ export declare function jsonValuesEqual(a: unknown, b: unknown): boolean;
2792
+ /**
2793
+ * Render a bounded JSON-ish preview of a value without serializing more of it
2794
+ * than the preview can show, so a large array or object costs no more than
2795
+ * `maxLength` characters of work.
2796
+ *
2797
+ * @param value - The value to preview.
2798
+ * @param maxLength - Maximum preview length; longer output ends with `…`.
2799
+ * @returns The preview, or `undefined` for `undefined`.
2800
+ */
2801
+ export declare function previewValue(value: unknown, maxLength?: number): string | undefined;
2802
+ /**
2803
+ * Count the features added, removed and modified between two feature
2804
+ * collections. Features are matched by `id` when they carry one; id-less
2805
+ * features are matched by geometry hash, so a feature whose properties
2806
+ * changed but whose geometry did not counts as modified, while a moved
2807
+ * id-less feature counts as one removal plus one addition.
2808
+ *
2809
+ * @param before - Earlier collection (absent counts as empty).
2810
+ * @param after - Later collection (absent counts as empty).
2811
+ * @returns The change counts.
2812
+ */
2813
+ export declare function diffFeatureCollections(before: FeatureCollection | null | undefined, after: FeatureCollection | null | undefined): FeatureChangeSummary;
2814
+ /**
2815
+ * Compute a structured summary of what changed from `before` to `after`.
2816
+ *
2817
+ * Layers are matched by id. Embedded GeoJSON is compared per feature (by id,
2818
+ * falling back to geometry hash) and each feature is hashed once per object,
2819
+ * so diffing the same parsed project against several others reuses its
2820
+ * hashes; callers keep and pass the same parsed project objects to benefit.
2821
+ *
2822
+ * @param before - The earlier project (e.g. an autosave snapshot).
2823
+ * @param after - The later project (e.g. the current project).
2824
+ * @param options - Preview length and other tuning.
2825
+ * @returns The grouped change summary.
2826
+ */
2827
+ export declare function diffProjects(before: GeoLibreProject, after: GeoLibreProject, options?: ProjectDiffOptions): ProjectDiff;
2828
+ //#endregion
5512
2829
  //#region src/style-library.d.ts
5513
2830
  /**
5514
2831
  * Layer types whose symbology is driven by the vector {@link LayerStyle}
@@ -5750,6 +3067,7 @@ export interface LayerLibraryConfigPatch {
5750
3067
  virtualFields?: LayerVirtualField[];
5751
3068
  attributeForm?: AttributeFormConfig;
5752
3069
  popup?: LayerPopupConfig;
3070
+ descriptiveMetadata?: LayerDescriptiveMetadata;
5753
3071
  }
5754
3072
  /** How an entry should be re-added to the current project. */
5755
3073
  export type LayerLibraryAddPlan =
@@ -6032,6 +3350,101 @@ export declare function extractCopiedLayerStyle(layer: GeoLibreLayer): CopiedLay
6032
3350
  */
6033
3351
  export declare function applyCopiedLayerStyle(target: GeoLibreLayer, copied: CopiedLayerStyle): Partial<GeoLibreLayer> | null;
6034
3352
  //#endregion
3353
+ //#region src/layer-style-file.d.ts
3354
+ /** `type` tag identifying a layer styles file. */
3355
+ export declare const LAYER_STYLES_FILE_TYPE = "geolibre-layer-styles";
3356
+ /** Format version written by {@link serializeLayerStylesFile}. */
3357
+ export declare const LAYER_STYLES_FILE_VERSION = 1;
3358
+ /** One layer's style in a layer styles file, keyed by the layer's name. */
3359
+ export type LayerStyleFileEntry = {
3360
+ /** Name of the layer the style was exported from; the match key. */
3361
+ layerName: string;
3362
+ kind: "vector";
3363
+ /** The full vector {@link LayerStyle} bag. */
3364
+ style: LayerStyle;
3365
+ } | {
3366
+ layerName: string;
3367
+ kind: "raster";
3368
+ /** Raster visualization state (`metadata.rasterState`). */
3369
+ rasterState?: Record<string, unknown>;
3370
+ /** Raster classification symbology (`metadata.rasterSymbology`). */
3371
+ rasterSymbology?: Record<string, unknown>;
3372
+ };
3373
+ /** The on-disk layer styles document. */
3374
+ export interface LayerStylesFile {
3375
+ type: typeof LAYER_STYLES_FILE_TYPE;
3376
+ version: typeof LAYER_STYLES_FILE_VERSION;
3377
+ styles: LayerStyleFileEntry[];
3378
+ }
3379
+ /**
3380
+ * Capture the style of every layer that has one. Layers without copyable
3381
+ * symbology (basemap tiles, 3D tiles, ...) are skipped. Layers that share a
3382
+ * name are all written, in stack order; the first one wins when the file is
3383
+ * applied (see {@link findLayerStyleEntryIndex}).
3384
+ *
3385
+ * @param layers - The layers to export, in store (bottom-to-top) order.
3386
+ * @returns The style entries.
3387
+ */
3388
+ export declare function extractLayerStyleEntries(layers: readonly GeoLibreLayer[]): LayerStyleFileEntry[];
3389
+ /**
3390
+ * Serialize style entries into the JSON written by Export Layer Styles.
3391
+ *
3392
+ * @param entries - The entries to write.
3393
+ * @returns Pretty-printed file content.
3394
+ */
3395
+ export declare function serializeLayerStylesFile(entries: readonly LayerStyleFileEntry[]): string;
3396
+ /**
3397
+ * Coerce untrusted entries (a hand-edited file, or a copy kept in settings)
3398
+ * into clean ones. An entry without a name or a known kind is dropped; a
3399
+ * vector style is sanitized and completed against the default style, and a
3400
+ * raster state keeps only well-typed appearance keys, so applying an entry
3401
+ * never hands a renderer an unknown, missing or wrong-typed field.
3402
+ *
3403
+ * @param value - The raw `styles` array.
3404
+ * @returns The usable entries.
3405
+ */
3406
+ export declare function normalizeLayerStyleEntries(value: unknown): LayerStyleFileEntry[];
3407
+ /**
3408
+ * Parse a file written by {@link serializeLayerStylesFile}.
3409
+ *
3410
+ * @param json - The file content.
3411
+ * @returns The normalized entries.
3412
+ * @throws Error when the content is not a layer styles file, comes from a
3413
+ * newer format version, or holds no usable entries.
3414
+ */
3415
+ export declare function parseLayerStylesFile(json: string): LayerStyleFileEntry[];
3416
+ /**
3417
+ * Find the entry for a layer name: an exact match first, then one that differs
3418
+ * only in case or surrounding whitespace, so `Roads` still picks up a style
3419
+ * saved for `roads`. The first match in file order wins.
3420
+ *
3421
+ * @param entries - The style entries.
3422
+ * @param layerName - The layer name to look up.
3423
+ * @param kind - When given, only entries of this style family are considered,
3424
+ * so a raster style saved under a shared name does not shadow the vector one.
3425
+ * @returns The matching entry's index in `entries`, or -1.
3426
+ */
3427
+ export declare function findLayerStyleEntryIndex(entries: readonly LayerStyleFileEntry[], layerName: string, kind?: LayerStyleClipboardKind): number;
3428
+ /** A layer restyled from a style entry, and the entry it took. */
3429
+ export interface LayerStyleMatch {
3430
+ /** Index of the applied entry in the entries array. */
3431
+ entryIndex: number;
3432
+ /** The patch that applies it. */
3433
+ patch: Partial<GeoLibreLayer>;
3434
+ }
3435
+ /**
3436
+ * Build the patch that restyles a layer from the entry matching its name, with
3437
+ * the same semantics as pasting a copied style: a vector style replaces the
3438
+ * whole style bag, a raster style merges its appearance keys and keeps the
3439
+ * layer's own band selection, and layer opacity is left alone.
3440
+ *
3441
+ * @param layer - The layer to restyle.
3442
+ * @param entries - The style entries.
3443
+ * @returns The matched entry and patch, or `null` when no entry of the layer's
3444
+ * style family matches its name (or the layer has no stylable symbology).
3445
+ */
3446
+ export declare function matchLayerStyleEntry(layer: GeoLibreLayer, entries: readonly LayerStyleFileEntry[]): LayerStyleMatch | null;
3447
+ //#endregion
6035
3448
  //#region src/layer-groups.d.ts
6036
3449
  /** Opacity a freshly created {@link LayerGroup} starts at (fully opaque). */
6037
3450
  export declare const DEFAULT_LAYER_GROUP_OPACITY = 1;
@@ -6323,6 +3736,236 @@ export declare function layerGroupSortability(layers: GeoLibreLayer[], groups: L
6323
3736
  */
6324
3737
  export declare function formatPixelValue(value: number): string;
6325
3738
  //#endregion
3739
+ //#region src/s3.d.ts
3740
+ /**
3741
+ * Amazon S3 (and S3-compatible) object URLs: parsing, public HTTPS mapping,
3742
+ * SigV4 query-string presigning, and the registry the app uses to plug
3743
+ * credentialed signing into every reader.
3744
+ *
3745
+ * Readers in GeoLibre take a plain HTTPS URL (geotiff.js, cog-tiler-wasm,
3746
+ * pmtiles, DuckDB-WASM's HTTP reader, the vector control). A presigned URL is
3747
+ * the one form of authenticated S3 access all of them accept without a custom
3748
+ * fetch hook, so private objects are read by signing the URL first and handing
3749
+ * the reader the result. Layers keep the unsigned source URL (`s3://…` or the
3750
+ * plain object URL); the signed one is minted again on every load, so a saved
3751
+ * project never carries a signature or a session token.
3752
+ */
3753
+ /** A bucket and object key, with the endpoint the URL named, if any. */
3754
+ export interface S3ObjectLocation {
3755
+ bucket: string;
3756
+ /** The raw (decoded) object key, without a leading slash. */
3757
+ key: string;
3758
+ /** The region an AWS HTTPS URL named in its host, when it named one. */
3759
+ region?: string;
3760
+ }
3761
+ /** Where and how a bucket is addressed. */
3762
+ export interface S3EndpointConfig {
3763
+ /** Signing region, e.g. `us-east-1`. S3-compatible stores often accept `auto`. */
3764
+ region: string;
3765
+ /**
3766
+ * Custom endpoint origin for S3-compatible stores (MinIO, Cloudflare R2,
3767
+ * Wasabi, Ceph), e.g. `https://minio.example.com:9000`. Empty for AWS.
3768
+ */
3769
+ endpoint?: string;
3770
+ /** Address the bucket in the path instead of the host name. */
3771
+ pathStyle?: boolean;
3772
+ }
3773
+ /** AWS credentials used to sign a request. */
3774
+ export interface S3Credentials {
3775
+ accessKeyId: string;
3776
+ secretAccessKey: string;
3777
+ sessionToken?: string;
3778
+ /** Epoch milliseconds at which temporary credentials stop working. */
3779
+ expiresAt?: number;
3780
+ }
3781
+ /** A presigned URL and the moment it stops working. */
3782
+ export interface S3SignedUrl {
3783
+ href: string;
3784
+ /** Epoch milliseconds. */
3785
+ expiresAt: number;
3786
+ }
3787
+ /** SigV4 allows a presigned URL to live at most seven days. */
3788
+ export declare const S3_MAX_PRESIGN_SECONDS: number;
3789
+ /**
3790
+ * Parses an `s3://bucket/key` URI or an AWS S3 HTTPS object URL
3791
+ * (virtual-hosted or path-style, any region) into its bucket and key.
3792
+ * Query strings and fragments are ignored, so a presigned URL parses to the
3793
+ * object it signs.
3794
+ *
3795
+ * @param url The URL to parse.
3796
+ * @returns The bucket and key, or null when the URL is not an S3 object URL.
3797
+ */
3798
+ export declare function parseS3Url(url: string): S3ObjectLocation | null;
3799
+ /** Whether `url` is an `s3://` URI (as opposed to an HTTPS object URL). */
3800
+ export declare function isS3Uri(url: string): boolean;
3801
+ /** Formats a location as an `s3://bucket/key` URI. */
3802
+ export declare function formatS3Uri(location: S3ObjectLocation): string;
3803
+ /**
3804
+ * URI-encodes a key the way SigV4 canonicalizes an S3 path: every byte except
3805
+ * the unreserved set and `/` is percent-encoded, once.
3806
+ */
3807
+ export declare function encodeS3Key(key: string): string;
3808
+ /**
3809
+ * Builds the HTTPS URL of an object.
3810
+ *
3811
+ * AWS buckets whose name contains a dot are addressed path-style, since the
3812
+ * `*.s3.amazonaws.com` wildcard certificate covers one label only.
3813
+ *
3814
+ * @param location The bucket and key.
3815
+ * @param config The endpoint; without one the global AWS endpoint is used.
3816
+ * @returns The unsigned object URL.
3817
+ */
3818
+ export declare function s3ObjectHttpsUrl(location: S3ObjectLocation, config?: Partial<S3EndpointConfig>): string;
3819
+ export interface PresignS3Options {
3820
+ /** The unsigned HTTPS object URL (see {@link s3ObjectHttpsUrl}). */
3821
+ url: string;
3822
+ region: string;
3823
+ credentials: S3Credentials;
3824
+ /** Lifetime in seconds, clamped to 1…{@link S3_MAX_PRESIGN_SECONDS}. */
3825
+ expiresIn: number;
3826
+ /** HTTP method the URL authorizes. Defaults to GET. */
3827
+ method?: string;
3828
+ /** Signing time; defaults to now. Exposed for tests. */
3829
+ now?: Date;
3830
+ }
3831
+ /**
3832
+ * Presigns an S3 request with AWS Signature Version 4 in the query string.
3833
+ * Runs on WebCrypto, so it works in the browser, the Tauri webview, and Node.
3834
+ *
3835
+ * The URL authorizes exactly one method: a GET URL cannot answer a HEAD, so
3836
+ * readers that probe with HEAD need a GET range request instead.
3837
+ *
3838
+ * @param options The object URL, region, credentials, and lifetime.
3839
+ * @returns The presigned URL.
3840
+ */
3841
+ export declare function presignS3Url(options: PresignS3Options): Promise<string>;
3842
+ /** One signed S3 GET: an object read, or a bucket-level call such as ListObjectsV2. */
3843
+ export interface S3PresignRequest {
3844
+ bucket: string;
3845
+ /** Object key; empty for a bucket-level request. */
3846
+ key: string;
3847
+ /** Extra query parameters to sign (e.g. `list-type=2`). */
3848
+ query?: Record<string, string>;
3849
+ /** Region an AWS URL named, used ahead of the connection's default. */
3850
+ region?: string;
3851
+ /**
3852
+ * Sign with this connection instead of the one matched by bucket name (the
3853
+ * S3 browser listing a chosen connection's buckets, where `bucket` is empty).
3854
+ */
3855
+ connectionId?: string;
3856
+ }
3857
+ /** A configured connection, as the S3 browser lists it. No secrets. */
3858
+ export interface S3ConnectionSummary {
3859
+ id: string;
3860
+ name: string;
3861
+ /** Bucket patterns the connection covers; empty means every bucket. */
3862
+ buckets: string[];
3863
+ }
3864
+ /**
3865
+ * Presigns S3 requests with the app's configured credentials. `presign`
3866
+ * resolves to null when no configured connection covers the bucket, in which
3867
+ * case the object is read anonymously.
3868
+ */
3869
+ export interface S3UrlSigner {
3870
+ /** Whether a configured connection covers `bucket`. Must be synchronous. */
3871
+ covers(bucket: string): boolean;
3872
+ /** Presigns a GET; rejects when the connection's credentials cannot be resolved. */
3873
+ presign(request: S3PresignRequest, signal?: AbortSignal): Promise<S3SignedUrl | null>;
3874
+ /** The configured connections, for pickers. */
3875
+ connections(): S3ConnectionSummary[];
3876
+ /**
3877
+ * Fetches a presigned bucket-level URL (listing). The desktop app routes it
3878
+ * through its native HTTP client, which needs no bucket CORS rule.
3879
+ */
3880
+ fetchText?(url: string, signal?: AbortSignal): Promise<{
3881
+ status: number;
3882
+ body: string;
3883
+ }>;
3884
+ /** The persisted location (`s3://bucket/prefix/`) the S3 browser opens at, or "". */
3885
+ defaultLocation?(): string;
3886
+ /** Persists the S3 browser's default location; "" clears it. */
3887
+ setDefaultLocation?(location: string): void;
3888
+ }
3889
+ /** Installs (or with null, removes) the app's S3 signer. */
3890
+ export declare function registerS3UrlSigner(signer: S3UrlSigner | null): void;
3891
+ /**
3892
+ * Whether reading `url` needs the S3 signer: it names an S3 object in a bucket
3893
+ * a configured connection covers.
3894
+ */
3895
+ export declare function isCredentialedS3Url(url: string): boolean;
3896
+ /**
3897
+ * The unsigned URL a presigned one was minted for by
3898
+ * {@link resolveReadableUrl}, or `url` itself. Layer sync runs every URL a
3899
+ * control reports through this, so the store (and a saved project) keeps the
3900
+ * `s3://` source rather than an expiring signature.
3901
+ */
3902
+ export declare function unsignedSourceUrl(url: string): string;
3903
+ export declare function unsignedSourceUrl(url: string | undefined): string | undefined;
3904
+ /**
3905
+ * Resolves a data URL into one a plain HTTP reader can fetch:
3906
+ *
3907
+ * - an S3 object in a bucket a configured connection covers → a presigned URL;
3908
+ * - any other `s3://` URI → the public HTTPS object URL (anonymous read);
3909
+ * - anything else → unchanged.
3910
+ *
3911
+ * @param url The URL a layer or query names.
3912
+ * @param signal Aborts a pending credential lookup.
3913
+ * @returns The URL to hand the reader.
3914
+ * @throws When the bucket is covered but its credentials cannot be resolved.
3915
+ */
3916
+ export declare function resolveReadableUrl(url: string, signal?: AbortSignal): Promise<string>;
3917
+ /** The installed signer, for the S3 browser. */
3918
+ export declare function getS3UrlSigner(): S3UrlSigner | null;
3919
+ /** One page of a ListObjectsV2 response. */
3920
+ export interface S3ListPage {
3921
+ /** Common prefixes ("folders") under the requested prefix. */
3922
+ prefixes: string[];
3923
+ objects: Array<{
3924
+ key: string;
3925
+ size: number;
3926
+ lastModified?: string;
3927
+ }>;
3928
+ /** Present when more results follow. */
3929
+ nextContinuationToken?: string;
3930
+ }
3931
+ /**
3932
+ * Parses a ListObjectsV2 XML body. S3's listing XML is flat and predictable,
3933
+ * so this reads it with patterns rather than an XML DOM, which keeps it usable
3934
+ * in workers and Node tests.
3935
+ */
3936
+ export declare function parseS3ListObjects(xml: string): S3ListPage;
3937
+ /** The error code and message of an S3 XML error body, when it is one. */
3938
+ export declare function parseS3Error(xml: string): {
3939
+ code: string;
3940
+ message: string;
3941
+ region?: string;
3942
+ } | null;
3943
+ /** Bucket names from a ListBuckets (`GET /` on the service) XML body. */
3944
+ export declare function parseS3BucketList(xml: string): string[];
3945
+ /** A read the bucket's CORS configuration blocked. */
3946
+ export declare class S3CorsError extends Error {
3947
+ readonly bucket: string;
3948
+ readonly origin: string;
3949
+ constructor(message: string, bucket: string, origin: string, cause: unknown);
3950
+ }
3951
+ /** Translates `key`, falling back to `defaultValue` (an app API's `translate`). */
3952
+ export type S3ErrorTranslate = (key: string, defaultValue: string, params?: Record<string, string | number>) => string;
3953
+ /**
3954
+ * Explains why reading an S3 object failed when the browser only said
3955
+ * "Failed to fetch". A browser reports a request the bucket's CORS rules
3956
+ * blocked exactly like a network outage, so this repeats the request twice:
3957
+ * normally, where success means the failure was transient; then in `no-cors`
3958
+ * mode, which CORS cannot block. Only when the first fails and the second gets
3959
+ * an answer is the bucket's CORS configuration the cause.
3960
+ *
3961
+ * @param url The `s3://` URI or object URL that was being read.
3962
+ * @param error What the reader threw.
3963
+ * @param translate Turns the message into the UI language.
3964
+ * @returns An {@link S3CorsError} naming the bucket and the origin to allow,
3965
+ * or `error` unchanged when it is not a CORS refusal of an S3 read.
3966
+ */
3967
+ export declare function explainS3ReadError(url: string, error: unknown, translate?: S3ErrorTranslate): Promise<unknown>;
3968
+ //#endregion
6326
3969
  //#region src/storymap-sample.d.ts
6327
3970
  /**
6328
3971
  * Build a ready-to-play sample story map.
@@ -6379,6 +4022,20 @@ export declare function isStoryHiddenLayer(opacities: Record<string, number> | u
6379
4022
  * @param opacities The store's `ui.storymapLayerOpacity` record.
6380
4023
  */
6381
4024
  export declare function storyVisibleLayers(layers: GeoLibreLayer[], presenting: boolean, opacities: Record<string, number> | undefined): GeoLibreLayer[];
4025
+ /**
4026
+ * Resolve a story chapter location to a full camera view.
4027
+ *
4028
+ * A location's `pitch` and `bearing` are optional in memory (a project file
4029
+ * fills them with 0 on load). An absent value keeps the camera's current one,
4030
+ * the same as MapLibre's camera methods do for an omitted option, so engines
4031
+ * whose camera API needs every field (Cesium, `MapEngine.applyView`) behave
4032
+ * like the MapLibre map.
4033
+ *
4034
+ * @param location The chapter's camera target.
4035
+ * @param current The camera's current view, used for an absent pitch/bearing.
4036
+ * @returns The location with `pitch` and `bearing` filled in.
4037
+ */
4038
+ export declare function storyLocationView(location: StoryChapterLocation, current: Pick<MapViewState, "pitch" | "bearing">): MapViewState;
6382
4039
  //#endregion
6383
4040
  //#region src/layer-ref-scrub.d.ts
6384
4041
  /**
@@ -6566,6 +4223,22 @@ interface LayersSlice {
6566
4223
  * style was applied, so callers can skip the confirmation on a no-op.
6567
4224
  */
6568
4225
  pasteLayerStyle: (id: string) => boolean;
4226
+ /**
4227
+ * Restyle layers from layer styles file entries matched by layer name
4228
+ * (Import Layer Styles, and the Startup setting's auto-apply). Each matched
4229
+ * layer is patched the way {@link pasteLayerStyle} would patch it, all in a
4230
+ * single store update so the import is one undo step.
4231
+ *
4232
+ * @param entries - The parsed style entries.
4233
+ * @param layerIds - Restrict the restyle to these layers; every layer when
4234
+ * omitted.
4235
+ * @returns Each restyled layer's id with the index of the entry it took, in
4236
+ * stack order.
4237
+ */
4238
+ applyLayerStyleEntries: (entries: readonly LayerStyleFileEntry[], layerIds?: readonly string[]) => {
4239
+ layerId: string;
4240
+ entryIndex: number;
4241
+ }[];
6569
4242
  /**
6570
4243
  * Replace a layer's persistent attribute joins and immediately re-derive its
6571
4244
  * joined columns (strip what the previous joins added, apply the new list).
@@ -6584,6 +4257,13 @@ interface LayersSlice {
6584
4257
  * `undefined` to restore the default full-property dump.
6585
4258
  */
6586
4259
  setLayerPopup: (id: string, popup: LayerPopupConfig | undefined) => void;
4260
+ /**
4261
+ * Replace the layer's user-authored catalog metadata (title, abstract,
4262
+ * keywords, license, contact, lineage, temporal extent, links). The record
4263
+ * is normalized first; one that cleans to nothing (or `undefined`) removes
4264
+ * the metadata entirely. One call is one undo step.
4265
+ */
4266
+ setLayerDescriptiveMetadata: (id: string, metadata: LayerDescriptiveMetadata | undefined) => void;
6587
4267
  /**
6588
4268
  * Replace the layer's editor tracking configuration (whether creation/edit
6589
4269
  * author and timestamp columns are maintained, and under which names). Pass
@@ -6827,6 +4507,12 @@ interface ProjectContentSlice {
6827
4507
  dashboardColumns: number;
6828
4508
  /** Anchored review comments on map points or features (issue #1518). */
6829
4509
  comments: ProjectComment[];
4510
+ /**
4511
+ * The loaded project's startup `interaction` block (issue #2688). Applied
4512
+ * once on load and written back unchanged on save; live Identify and control
4513
+ * changes do not edit it.
4514
+ */
4515
+ projectInteraction: ProjectInteraction | null;
6830
4516
  /** Append a new dashboard widget. */
6831
4517
  addWidget: (widget: DashboardWidget) => void;
6832
4518
  /** Patch an existing dashboard widget by id (no-op if absent). Merges, so an
@@ -6915,7 +4601,45 @@ interface GpsStatusFix {
6915
4601
  }
6916
4602
  /** Reserved Identify target for querying every visible queryable layer at once. */
6917
4603
  export declare const IDENTIFY_ALL_LAYERS_ID = "__geolibre_identify_all_layers__";
4604
+ /** The Identify fields of the store, as set together by an Identify change. */
4605
+ interface IdentifyState {
4606
+ identifyLayerId: string | null;
4607
+ identifyLayerIds: string[] | null;
4608
+ }
4609
+ /**
4610
+ * Resolve an Identify target against the layers that exist.
4611
+ *
4612
+ * Shared by the scripting `setIdentify` command and by project load, which
4613
+ * applies a project's saved `interaction.identify` (issue #2688).
4614
+ *
4615
+ * @param target - A layer id, `"all"`, a list of layer ids, or null/undefined.
4616
+ * @param layerIds - Ids of the layers currently in the project.
4617
+ * @returns The store fields to set. Ids that name no layer are dropped; a list
4618
+ * that keeps one id becomes a single-layer target, and one that keeps several
4619
+ * becomes the all-layers mode restricted to those ids.
4620
+ */
4621
+ export declare function resolveIdentifyTarget(target: string | readonly string[] | null | undefined, layerIds: Iterable<string>): IdentifyState;
4622
+ /**
4623
+ * Whether the all-layers Identify mode may query a layer.
4624
+ *
4625
+ * @param layerId - The candidate layer's id.
4626
+ * @param identifyLayerIds - The store's `identifyLayerIds` restriction.
4627
+ * @returns True when there is no restriction or the layer is in it.
4628
+ */
4629
+ export declare function identifyAllIncludes(layerId: string, identifyLayerIds: readonly string[] | null | undefined): boolean;
4630
+ /**
4631
+ * Drop removed layers from the Identify state.
4632
+ *
4633
+ * @param state - The current Identify fields.
4634
+ * @param removed - Ids of the layers being removed.
4635
+ * @returns The Identify fields with those layers gone. A restricted all-layers
4636
+ * mode left with one layer narrows to it, and with none turns Identify off.
4637
+ */
4638
+ export declare function identifyStateWithoutLayers(state: IdentifyState, removed: ReadonlySet<string>): IdentifyState;
6918
4639
  interface SessionSlice {
4640
+ /** Local viewing switch; saved layer popup settings are left untouched. */
4641
+ hoverTooltipsEnabled: boolean;
4642
+ setHoverTooltipsEnabled: (enabled: boolean) => void;
6919
4643
  selectedLayerId: string | null;
6920
4644
  selectedFeatureId: string | null;
6921
4645
  /**
@@ -6932,6 +4656,14 @@ interface SessionSlice {
6932
4656
  * DuckDB query, WMS, COG, NetCDF image and time-slider raster alike.
6933
4657
  */
6934
4658
  identifyLayerId: string | null;
4659
+ /**
4660
+ * Layers the all-layers Identify mode is limited to, or null for every
4661
+ * visible queryable layer. Set only alongside {@link IDENTIFY_ALL_LAYERS_ID},
4662
+ * by a script or project that names several layers (issue #2688), and
4663
+ * cleared by every `setIdentifyLayer` call so the in-app buttons keep their
4664
+ * plain meaning.
4665
+ */
4666
+ identifyLayerIds: string[] | null;
6935
4667
  pointerCoords: [number, number] | null;
6936
4668
  /**
6937
4669
  * Ground elevation in true metres under the pointer, for the status bar
@@ -6965,6 +4697,8 @@ interface SessionSlice {
6965
4697
  */
6966
4698
  selectFeatures: (ids: string[], anchorId?: string | null) => void;
6967
4699
  setIdentifyLayer: (id: string | null) => void;
4700
+ /** Set both Identify fields at once (see {@link resolveIdentifyTarget}). */
4701
+ setIdentifyState: (state: IdentifyState) => void;
6968
4702
  setAttributeFilter: (filter: string) => void;
6969
4703
  }
6970
4704
  //#endregion
@@ -7011,6 +4745,12 @@ interface UiSlice {
7011
4745
  notebookOpen: boolean;
7012
4746
  assistantOpen: boolean;
7013
4747
  attributeTableOpen: boolean;
4748
+ /**
4749
+ * Pending "open the attribute table in edit mode" request for this layer,
4750
+ * written by Identify's Edit attributes action (#2932). The table consumes
4751
+ * and clears it. Null when idle.
4752
+ */
4753
+ attributeTableEditLayerId: string | null;
7014
4754
  /** Whether the Raster Attribute Table bottom panel is open (issue #1307). */
7015
4755
  rasterAttributeTableOpen: boolean;
7016
4756
  dashboardOpen: boolean;
@@ -7070,6 +4810,11 @@ interface UiSlice {
7070
4810
  setNotebookOpen: (open: boolean) => void;
7071
4811
  setAssistantOpen: (open: boolean) => void;
7072
4812
  setAttributeTableOpen: (open: boolean) => void;
4813
+ /**
4814
+ * Ask the attribute table to open in edit mode on `layerId` (opening the
4815
+ * table too), or clear a pending request with `null`.
4816
+ */
4817
+ requestAttributeTableEdit: (layerId: string | null) => void;
7073
4818
  setRasterAttributeTableOpen: (open: boolean) => void;
7074
4819
  setDashboardOpen: (open: boolean) => void;
7075
4820
  setStorymapPanelOpen: (open: boolean) => void;
@@ -7130,6 +4875,14 @@ export declare function clearHistory(): void;
7130
4875
  export declare const useAppStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<AppState>, "temporal"> & {
7131
4876
  temporal: import("zustand").StoreApi<import("zundo").TemporalState<HistoryState>>;
7132
4877
  }>;
4878
+ /**
4879
+ * Whether the map should fit to data the user just added, per the project's
4880
+ * Map Preferences. Add-data paths consult this before an automatic fit; an
4881
+ * explicit "Zoom to layer" request ignores it.
4882
+ *
4883
+ * @returns `true` unless the user turned off zooming to new layers.
4884
+ */
4885
+ export declare function shouldZoomToNewLayers(): boolean;
7133
4886
  /**
7134
4887
  * React hook for consuming application capability state for a specific privilege.
7135
4888
  *
@@ -7944,6 +5697,89 @@ export declare function resolveThreeDTilesRequestHeaders(url: string, headers: R
7944
5697
  */
7945
5698
  export declare function persistedThreeDTilesRequestHeaders(url: string, headers: Record<string, string> | undefined): Record<string, string> | undefined;
7946
5699
  //#endregion
5700
+ //#region src/project-credential-refs.d.ts
5701
+ export declare function geocodingApiKeyAccount(providerId: string): string;
5702
+ export declare function environmentVariableAccount(key: string): string;
5703
+ /** Rows are secret unless explicitly marked `secret: false`. */
5704
+ export declare function isSecretEnvironmentVariable(variable: RuntimeEnvironmentVariable): boolean;
5705
+ type ProjectCredentialLookup = (account: string) => string | undefined;
5706
+ /** Install (or clear, with `null`) the source of stored project credentials. */
5707
+ export declare function setProjectCredentialLookup(lookup: ProjectCredentialLookup | null): void;
5708
+ /** The stored value for `account`, or `undefined` when none (or no lookup). */
5709
+ export declare function lookupProjectCredential(account: string): string | undefined;
5710
+ /** Fill empty geocoding API keys from the stored ones. */
5711
+ export declare function overlayStoredGeocodingApiKeys(geocoding: GeocodingPreferences): GeocodingPreferences;
5712
+ /** Fill empty secret environment variable values from the stored ones. */
5713
+ export declare function overlayStoredEnvironmentVariables(variables: RuntimeEnvironmentVariable[]): RuntimeEnvironmentVariable[];
5714
+ /** Preferences with every empty stored credential filled in. */
5715
+ export declare function overlayStoredPreferenceCredentials(preferences: ProjectPreferences): ProjectPreferences;
5716
+ /**
5717
+ * The credential changes an edited Settings draft makes, keyed by account
5718
+ * (`""` deletes).
5719
+ *
5720
+ * - `seeded`: the overlaid preferences the dialog opened with.
5721
+ * - `next`: the normalized draft being saved.
5722
+ *
5723
+ * Only values the user changed are returned, so an untouched session override
5724
+ * (plaintext from the opened file) stays out of the keychain and opening a
5725
+ * file never writes it. Removing, renaming, or un-secreting a variable never
5726
+ * deletes its stored value: names are shared by every project on the device.
5727
+ * Clearing a field deletes the stored value only when the field showed that
5728
+ * stored value; clearing a file override that differed from it just drops the
5729
+ * override, so the shared value applies again and other projects keep it.
5730
+ */
5731
+ export declare function changedPreferenceCredentials(seeded: ProjectPreferences, next: ProjectPreferences): Record<string, string>;
5732
+ /**
5733
+ * Move unambiguous geocoding API keys and secret environment variable values
5734
+ * out of `project` (a copy; the input is untouched) into `secrets`, keyed by
5735
+ * account. Secret environment rows that have no unique name remain in the
5736
+ * project for an explicit keep/strip choice. Layers pass through: their
5737
+ * request headers reference variables as `${NAME}` rather than holding
5738
+ * secrets that need moving.
5739
+ */
5740
+ export declare function splitProjectCredentials(project: GeoLibreProject): {
5741
+ project: GeoLibreProject;
5742
+ secrets: Record<string, string>;
5743
+ };
5744
+ /**
5745
+ * `headers` with every `${NAME}` resolved from the project's enabled
5746
+ * Environment Variables (stored secret values filled in). Only those rows
5747
+ * count, not the OS environment or build-time values that share the runtime
5748
+ * env. A header referencing an unset variable is dropped.
5749
+ */
5750
+ export declare function resolveProjectHeaderReferences(headers: Record<string, string> | undefined): Record<string, string> | undefined;
5751
+ //#endregion
5752
+ //#region src/header-references.d.ts
5753
+ /**
5754
+ * `${NAME}` references in request header values.
5755
+ *
5756
+ * A header such as `Authorization: Bearer ${TILES_TOKEN}` is saved as typed
5757
+ * and resolved from the project's Environment Variables when a request is
5758
+ * made, so the secret itself never sits on the layer. This module is pure (no
5759
+ * store import): `credentials.ts` depends on it, and the store depends on
5760
+ * `credentials.ts`.
5761
+ */
5762
+ export declare function hasHeaderReferences(headers: Record<string, string> | undefined): boolean;
5763
+ /**
5764
+ * Whether `value` carries no secret of its own: at most a scheme word such as
5765
+ * `Bearer` followed by a single `${NAME}`. Redaction keeps such values.
5766
+ */
5767
+ export declare function isHeaderReferenceOnly(value: string): boolean;
5768
+ /**
5769
+ * Whether credential-bearing request headers may be sent to `url`: HTTPS, or
5770
+ * plain HTTP to loopback so a local dev server still works. The scheme is read
5771
+ * off a parsed URL, so an unusually cased `HTTPS://` is not misread as
5772
+ * plaintext; a relative or unparseable URL is refused.
5773
+ */
5774
+ export declare function allowsCredentialHeaders(url: string): boolean;
5775
+ /**
5776
+ * `headers` with every `${NAME}` replaced by `values[NAME]`. A header that
5777
+ * references an unset (or empty) variable is omitted rather than sent with a
5778
+ * hole in it. Returns the input unchanged when nothing references a variable,
5779
+ * and `undefined` when no header remains.
5780
+ */
5781
+ export declare function resolveHeaderReferences(headers: Record<string, string> | undefined, values: Readonly<Record<string, string>>): Record<string, string> | undefined;
5782
+ //#endregion
7947
5783
  //#region src/credentials.d.ts
7948
5784
  /**
7949
5785
  * Project fields whose values are credentials. Keep this registry as the
@@ -8007,11 +5843,29 @@ interface CredentialRedactionResult {
8007
5843
  export declare const MAX_REDACT_DEPTH = 12;
8008
5844
  /** Whether an object key in layer/plugin configuration holds a credential. */
8009
5845
  export declare function isCredentialFieldName(name: string): boolean;
5846
+ /**
5847
+ * Whether a URL query-parameter name carries a credential. This is the wider of
5848
+ * the two registries (see `URL_CREDENTIAL_PARAMS`). Exported so a caller that
5849
+ * must leave the parameter in place — the native diagnostics log rewrites its
5850
+ * value to `[redacted]` rather than dropping the pair — shares this registry
5851
+ * instead of keeping a second, narrower list that can drift from it.
5852
+ */
5853
+ export declare function isCredentialUrlParam(name: string): boolean;
8010
5854
  /**
8011
5855
  * Remove credentials from URL-shaped values without encoding tile-template
8012
5856
  * placeholders such as `{z}/{x}/{y}`.
8013
5857
  */
8014
5858
  export declare function redactUrlCredentials(value: string): string;
5859
+ /**
5860
+ * Replace the registry-declared publishable plugin settings.
5861
+ *
5862
+ * Built-in ids in {@link PUBLISHABLE_PLUGIN_SETTINGS} always win, so a registry
5863
+ * entry cannot widen a first-party plugin's allowlist.
5864
+ *
5865
+ * @param entries Plugin ids mapped to the settings keys to keep, or `null` to
5866
+ * keep the whole blob. Ids that declare nothing should be omitted.
5867
+ */
5868
+ export declare function setRegistryPublishableSettings(entries: Iterable<readonly [string, readonly string[] | null]>): void;
8015
5869
  /**
8016
5870
  * Return a detached project safe for any external egress.
8017
5871
  *
@@ -8024,9 +5878,22 @@ export declare function redactUrlCredentials(value: string): string;
8024
5878
  * and carries no credentials, and dropping it stripped the legend, colorbar,
8025
5879
  * and swipe from the exported map. Manifest URLs, activation, and control
8026
5880
  * positions stay intact so recipients can still load and configure the plugin
8027
- * themselves.
5881
+ * themselves. A registry-listed external plugin can opt in through the
5882
+ * `publishableSettings` field of its registry entry (see
5883
+ * {@link setRegistryPublishableSettings}); what it keeps is still swept below.
8028
5884
  */
8029
5885
  export declare function redactProjectCredentials(project: GeoLibreProject): CredentialRedactionResult;
5886
+ /**
5887
+ * Remove credentials from a single configuration value (a layer `source`,
5888
+ * `metadata`, or `sourcePath`) with the same rules the project redaction pass
5889
+ * applies to layer configuration: credential-named fields and non-reference
5890
+ * header values are dropped, and credential URL parameters are stripped from
5891
+ * every URL-shaped string.
5892
+ *
5893
+ * @param value - The configuration value to scrub. Not mutated.
5894
+ * @returns A detached, redacted copy.
5895
+ */
5896
+ export declare function redactConfigurationCredentials<T>(value: T): T;
8030
5897
  /** Convenience wrapper for callers that only need the safe project. */
8031
5898
  export declare function redactCredentials(project: GeoLibreProject): GeoLibreProject;
8032
5899
  //#endregion
@@ -8189,5 +6056,5 @@ export declare function localFileName(path: string): string;
8189
6056
  /** Keep imported layers distinguishable, including within a multi-file import. */
8190
6057
  export declare function uniqueImportedLayerName(name: string, names: Iterable<string>): string;
8191
6058
  //#endregion
8192
- export type { AppState, CesiumIonAssetKind, CesiumIonLayerOptions, CesiumIonQuickPick, CesiumIonQuickPickGroup, CesiumKmlLayerOptions, ConversionToolKind, CredentialRedactionResult, CzmlDocument, CzmlLayerOptions, CzmlPacket, CzmlSource, GeocodeMatch, GeocodeRequest, GeocodeStatus, GeocoderConfig, GeocodingPreferenceInput, GeocodingProvider, GeocodingProviderId, GpsStatusFix, LayerSummary, NetworkToolKind, NominatimForwardResult, NominatimReverseResult, RasterToolKind, ReverseGeocodeDisplay, StatisticsToolKind, VectorToolKind };
6059
+ export { AddTileLayerOptions, AppCapabilities, AppPrivilege, AppRole, type AppState, ApplyLayerJoinsResult, AttributeFormConfig, AttributeFormFieldConfig, AttributeFormValueMapEntry, AttributeFormWidget, BLANK_BASEMAP, BLEND_MODES, BlendMode, CESIUM_BASEMAPS, CesiumBasemapId, CesiumBasemapImagery, type CesiumIonAssetKind, type CesiumIonLayerOptions, type CesiumIonQuickPick, type CesiumIonQuickPickGroup, type CesiumKmlLayerOptions, CollabInvite, CollaborationChatMessage, CollaborationMode, CollaborationParticipant, CollaborationPresence, CollaborationRole, CollaborationState, CommentAnchor, CommentAuthor, CommentReply, type ConversionToolKind, type CredentialRedactionResult, type CzmlDocument, type CzmlLayerOptions, type CzmlPacket, type CzmlSource, DEFAULT_BASEMAP, DEFAULT_BLEND_MODE, DEFAULT_DASHBOARD_COLUMNS, DEFAULT_ELLIPSOID_ID, DEFAULT_LAYER_STYLE, DEFAULT_LEGEND_CONFIG, DEFAULT_MAP_GRID_LAYOUT, DEFAULT_PRIMARY_RENDERER, DEFAULT_PRINT_LAYOUT, DEFAULT_PROJECT_PREFERENCES, DEFAULT_STORY_MAP, DUCKDB_VECTOR_FEATURE_WARN_COUNT, DUCKDB_VECTOR_ROUTE_BYTES, DashboardWidget, DashboardWidgetAggregation, DashboardWidgetType, DiagramField, DiagramSizeMode, DiagramType, EARTH_MEAN_RADIUS_METERS, ELLIPSOIDS, EditorTrackingConfig, Ellipsoid, EllipsoidId, FieldVisibility, FillPattern, GeoLibreLayer, GeoLibreProject, type GeocodeMatch, type GeocodeRequest, type GeocodeStatus, type GeocoderConfig, type GeocodingPreferenceInput, GeocodingPreferences, type GeocodingProvider, type GeocodingProviderId, GeometryGeneratorType, type GpsStatusFix, type IdentifyState, IndicatorAggregation, LARGE_VECTOR_FEATURE_THRESHOLD, LAYER_TYPES, LabelAnchor, LabelDedupe, LabelStyle, LabelTransform, LayerCapabilities, LayerConnection, LayerDescriptiveMetadata, LayerGroup, LayerJoin, LayerJoinStats, LayerLibraryEntry, LayerMetadataContact, LayerMetadataLink, LayerMetadataTemporalExtent, LayerPopupConfig, LayerQuickFilter, LayerStyle, type LayerSummary, LayerType, LayerVirtualField, LegendConfig, LegendCustomEntry, LegendCustomItem, LegendCustomShape, LegendItemOverride, LegendPanelPosition, LineDecoration, MAX_DASHBOARD_COLUMNS, MAX_MAP_GRID_DIM, MAX_PROCESSING_HISTORY, MIN_DASHBOARD_COLUMNS, MapGridLayout, MapPreferences, MapProjection, MapRendererKind, MapScaleUnit, MapViewState, MarkerShape, ModelGraphEdge, ModelGraphNode, ModelGraphNodeKind, ModelGraphPort, ModelPortKind, ModelToolProvider, NETCDF_IMAGE_SOURCE_KIND, type NetworkToolKind, type NominatimForwardResult, type NominatimReverseResult, NonGeographicCoordinates, OPENFREEMAP_BASEMAPS, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANET_SWITCHER_OPTIONS, PROJECT_VERSION, PROTOMAPS_BASEMAPS, ParticipantIdentity, PlanetaryBasemap, PlanetaryBasemapGroup, PlanetaryBasemapSectionId, PointRenderer, PolylineBatchOptions, PolylineDecodeResult, PopupDateFormat, PopupFieldConfig, PopupFieldFormat, PopupFieldKind, PrintLayoutConfig, PrintLayoutCorner, PrintLayoutLegendEntry, PrintLayoutPageFilter, ProcessingModel, ProcessingModelGraph, ProcessingModelStep, ProcessingRerunRequest, ProcessingRun, ProcessingRunKind, ProcessingRunStatus, ProjectComment, type ProjectCredentialLookup, ProjectInteraction, ProjectPluginControlPosition, ProjectPluginState, ProjectPreferences, ProjectTemplateEntry, QuickFilterDateKind, QuickFilterKind, QuickFilterTextOperator, type RasterToolKind, RecentProjectEntry, type ReverseGeocodeDisplay, RuntimeEnvironmentVariable, SQL_QUERY_SOURCE_KIND, SecondaryMapView, type StatisticsToolKind, StoryActiveSlideMode, StoryChapter, StoryChapterAlignment, StoryChapterAnimation, StoryChapterLocation, StoryInsetPosition, StoryLayerOpacityChange, StoryMap, StorySlideMode, StrokeWidthUnit, StyleLibraryEntry, StyleLibraryEntryKind, VectorRule, VectorStyleMode, VectorStyleStop, type VectorToolKind, applyJoinsToLayer, applyLayerJoins, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, bodyLengthToEarth, cascadeLayerJoinRefresh, collectTransitiveJoinSourceIds, createDefaultPrintLayout, decodePolyline, decodePolylineDetailed, detectNonGeographicCoordinates, earthAreaToBody, earthLengthToBody, encodePolyline, geoJSONToPolylineStr, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getEllipsoid, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, horizontalBbox, isDefaultPrintLayout, isDuckDBQueryLayer, layerJoinKey, meanRadiusMeters, normalizeCesiumBasemap, normalizePrintLayoutConfig, polylineStrToGeoJSON, printLayoutConfigsEqual, reapplyLayerJoins, sameCesiumImagery, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, setActiveEllipsoidId, shouldUseTiledRendering, stripJoinFields, styleValue, transformGeojsonElevation, unescapePolyline };
8193
6060
  //# sourceMappingURL=index.d.mts.map