@geolibre/core 3.4.0 → 3.5.1

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.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import { $ as isHeaderReferenceOnly, $n as geoJSONToPolylineStr, $r as MAX_DASHBOARD_COLUMNS, $t as layerGroupSortability, A as normalizeSecondaryMapViews, An as metadataDateToRfc3339, Ar as getActiveEllipsoid, At as normalizeStyleLibraryEntries, B as storyMapHasContent, Bn as isStyleSpecColor, Br as DEFAULT_BASEMAP, Bt as scrubPrintLayoutForRemovedLayers, C as normalizeModelGraph, Cn as stripVirtualFieldColumns, Cr as PLANETARY_BASEMAP_GROUPS, Ct as RAMP_STYLE_KEYS, D as normalizeProcessingHistory, Dn as isValidMetadataDate, Dr as earthAreaToBody, Dt as createStyleLibraryEntryId, E as normalizePrimaryRenderer, En as buildLayerStacItem, Er as bodyLengthToEarth, Et as SYMBOL_STYLE_KEYS, F as projectFromStore, Fn as EXPRESSION_FUNCTION_CATEGORIES, Fr as getPlanetaryBasemapByStyleUrl, Ft as createDefaultPrintLayout, G as isCredentialUrlParam, Gn as SELECTION_MODES, Gr as DEFAULT_MAP_GRID_LAYOUT, Gt as DEFAULT_LAYER_GROUP_OPACITY, H as PROJECT_CREDENTIAL_FIELDS, Hn as removeTrailingJsonCommas, Hr as DEFAULT_DASHBOARD_COLUMNS, Ht as scrubCommentsForRemovedLayers, I as resolveMapGrid, In as compileFeatureExpression, Ir as meanRadiusMeters, It as isDefaultPrintLayout, J as redactProjectCredentials, Jn as invertSelection, Jr as DEFAULT_STORY_MAP, Jt as buildLayerTree, K as redactConfigurationCredentials, Kn as applySelectionMode, Kr as DEFAULT_PRIMARY_RENDERER, Kt as applyGroupEffects, L as serializeProject, Ln as evaluateMapExpression, Lr as setActiveEllipsoidId, Lt as normalizePrintLayoutConfig, M as normalizeWidgets, Mn as parseMetadataKeywords, Mr as getActiveSemiMajorAxisMeters, Mt as sanitizeLayerStylePatch, N as parseProject, Nn as stacAssetMediaType, Nr as getEllipsoid, Nt as serializeStyleLibrary, O as normalizeProjectComments, On as isValidMetadataEmail, Or as earthLengthToBody, Ot as extractStyleLibraryStyle, P as portableWmsTileUrl, Pn as validateLayerDescriptiveMetadata, Pr as getPlanetaryBasemapById, Pt as DEFAULT_PRINT_LAYOUT, Q as hasHeaderReferences, Qn as encodePolyline, Qr as LAYER_TYPES, Qt as layerGroupMoveability, R as serializeProjectWithLayerCache, Rn as formatExpressionPreviewValue, Rr as BLANK_BASEMAP, Rt as printLayoutConfigsEqual, S as normalizeMapViewState, Sn as applyLayerVirtualFields, Sr as PLANETARY_BASEMAPS, St as BUILT_IN_STYLE_PRESETS, T as normalizeObiaWorkbench, Tn as STAC_VERSION, Tr as PLANET_SWITCHER_OPTIONS, Tt as STYLE_LIBRARY_BUNDLE_VERSION, U as PUBLISHABLE_PLUGIN_SETTINGS, Un as substituteExpressionVariables, Ur as DEFAULT_LAYER_STYLE, Ut as scrubLegendForRemovedLayers, V as MAX_REDACT_DEPTH, Vn as matchFeaturesByExpression, Vr as DEFAULT_BLEND_MODE, Vt as removedLayerIdSet, W as isCredentialFieldName, Wn as validateMapExpression, Wr as DEFAULT_LEGEND_CONFIG, Wt as scrubWidgetsForRemovedLayers, X as setRegistryPublishableSettings, Xn as decodePolyline, Xr as DUCKDB_VECTOR_ROUTE_BYTES, Xt as groupMemberIndices, Y as redactUrlCredentials, Yn as batchDecodePolylines, Yr as DUCKDB_VECTOR_FEATURE_WARN_COUNT, Yt as effectiveLayerRenderState, Z as allowsCredentialHeaders, Zn as decodePolylineDetailed, Zr as LARGE_VECTOR_FEATURE_THRESHOLD, Zt as layerGroupDepth, _ as normalizeBookmarkGroups, _n as cascadeLayerJoinRefresh, _r as getRegionalBasemapByStyleUrl, _t as CZML_SOURCE_KIND, a as DEFAULT_PROJECT_NAME, ai as PROJECT_VERSION, an as ROLE_PRIVILEGES, ar as ARCGIS_PROJECTION_PRESETS, at as CESIUM_OSM_BUILDINGS_ASSET_ID, b as normalizeLegendConfig, bn as reapplyLayerJoins, br as EARTH_MEAN_RADIUS_METERS, bt as isCzmlLayer, c as createDefaultMapView, ci as detectNonGeographicCoordinates, cn as hasAppPrivilege, cr as availableCesiumBasemap, ct as createCesiumIonLayer, d as createProjectTemplate, di as styleValue, dn as normalizeAppPrivileges, dr as sameCesiumImagery, dt as parseCesiumIonAssetId, ei as MAX_MAP_GRID_DIM, en as layerPanelGroupHeaders, er as polylineStrToGeoJSON, et as resolveHeaderReferences, f as detachProjectCopy, fn as normalizeLayerCapabilities, fr as CHINA_BASEMAPS, ft as CESIUM_KML_SOURCE_KIND, g as normalizeBlankBackgroundColor, gn as applyLayerJoins, gr as getRegionalBasemapById, gt as CZML_QUICK_PICKS, h as isSessionOnlyLayer, hn as applyJoinsToLayer, hr as REGIONAL_BASEMAP_SENTINEL_PREFIX, ht as isCesiumKmlLayer, i as previewValue, ii as OPENFREEMAP_BASEMAPS, in as ALL_APP_PRIVILEGES, ir as transformGeojsonElevation, it as CESIUM_ION_SOURCE_KIND, j as normalizeStoryMap, jn as normalizeLayerDescriptiveMetadata, jr as getActiveMeanRadiusMeters, jt as parseStyleLibrary, k as normalizeProjectInteraction, kn as isValidMetadataUrl, kr as getActiveBodyRadiusRatio, kt as isStyleLibraryTargetLayer, l as createEmptyProject, li as isDuckDBQueryLayer, ln as inferLayerCapabilities, lr as basemapToCesiumImagery, lt as isCesiumIonLayer, m as isAllowedPluginManifestUrl, mn as resolveRolePrivileges, mr as REGIONAL_BASEMAP_GROUPS, mt as createCesiumKmlLayer, n as diffProjects, ni as MIN_DASHBOARD_COLUMNS, nn as reorderLayerGroupInPanel, nr as geojsonHasZCoordinates, nt as CESIUM_GOOGLE_PHOTOREALISTIC_ASSET_ID, o as MAX_PROJECT_BOOKMARKS, oi as PROTOMAPS_BASEMAPS, on as appPrivilegeReason, or as normalizeArcgisWkid, ot as cesiumIonAssetId, p as hydrateProjectLayer, pn as resolveLayerCapabilities, pr as REGIONAL_BASEMAPS, pt as cesiumKmlSource, q as redactCredentials, qn as featureSelectionId, qr as DEFAULT_PROJECT_PREFERENCES, qt as buildLayerPanelUnits, r as jsonValuesEqual, ri as NETCDF_IMAGE_SOURCE_KIND, rn as sortLayerGroupInPanel, rr as horizontalBbox, rt as CESIUM_ION_QUICK_PICKS, s as applyProjectToStore, si as SQL_QUERY_SOURCE_KIND, sn as createDefaultAppCapabilities, sr as CESIUM_BASEMAPS, st as cesiumIonAssetKind, t as diffFeatureCollections, ti as MAX_PROCESSING_HISTORY, tn as normalizeGroupContiguity, tr as unescapePolyline, tt as CESIUM_BING_AERIAL_ASSET_ID, u as createProjectLayerSerializationCache, ui as shouldUseTiledRendering, un as intersectPrivileges, ur as normalizeCesiumBasemap, ut as isCesiumOnlyLayer, v as normalizeBookmarks, vn as collectTransitiveJoinSourceIds, vr as isRegionalBasemapSentinel, vt as createCzmlLayer, w as normalizeModels, wn as STAC_PROCESSING_EXTENSION, wr as PLANETARY_BASEMAP_SENTINEL_PREFIX, wt as STYLE_LIBRARY_BUNDLE_TYPE, x as normalizeMapLayout, xn as stripJoinFields, xr as ELLIPSOIDS, xt as parseCzml, y as normalizeDashboardColumns, yn as layerJoinKey, yr as DEFAULT_ELLIPSOID_ID, yt as czmlSource, z as serializeProjectWithLayerCacheAsync, zn as inferFieldTypes, zr as BLEND_MODES, zt as scrubPrintLayoutForLayers } from "./project-diff-Bua7fBME.mjs";
1
+ import { $ as GEOCODING_PROVIDERS, $i as getPlanetaryBasemapById, $n as groupMemberIndices, $r as getBuildEnvironment, $t as CESIUM_BING_AERIAL_ASSET_ID, A as normalizeSecondaryMapViews, Aa as styleValue, Ai as sameCesiumImagery, An as createStyleLibraryEntryId, Ar as isValidMetadataDate, At as selectedGeocodingLocator, B as storyMapHasContent, Bi as ELLIPSOIDS, Bn as normalizePrintLayoutConfig, Br as evaluateMapExpression, Bt as PROJECT_CREDENTIAL_FIELDS, C as normalizeModelGraph, Ca as OPENFREEMAP_BASEMAPS, Ci as parseMapCrsEpsgCode, Cn as isGeoArrowBatch, Cr as reapplyLayerJoins, Ct as normalizeArcGISLocatorUrl, D as normalizeProcessingHistory, Da as detectNonGeographicCoordinates, Di as availableCesiumBasemap, Dn as STYLE_LIBRARY_BUNDLE_TYPE, Dr as STAC_PROCESSING_EXTENSION, Dt as parseArcGISSuggestions, E as normalizePrimaryRenderer, Ea as SQL_QUERY_SOURCE_KIND, Ei as CESIUM_BASEMAPS, En as RAMP_STYLE_KEYS, Er as stripVirtualFieldColumns, Et as parseArcGISLocatorInfo, F as projectFromStore, Fi as getRegionalBasemapById, Fn as sanitizeLayerStylePatch, Fr as parseMetadataKeywords, Ft as DEFAULT_STORY_CHAPTER_DURATION_SECONDS, G as DEFAULT_REVERSE_GEOCODE_ENDPOINT, Gi as bodyLengthToEarth, Gn as scrubCommentsForRemovedLayers, Gr as removeTrailingJsonCommas, Gt as redactCredentials, H as CARTOCIUDAD_PUBLIC_HOST, Hi as PLANETARY_BASEMAP_GROUPS, Hn as scrubPrintLayoutForLayers, Hr as inferFieldTypes, Ht as isCredentialFieldName, I as resolveMapGrid, Ii as getRegionalBasemapByStyleUrl, In as serializeStyleLibrary, Ir as stacAssetMediaType, It as MAX_STORY_CHAPTER_DURATION_SECONDS, J as GEOCODE_LAT_KEY, Ji as getActiveBodyRadiusRatio, Jn as DEFAULT_LAYER_GROUP_OPACITY, Jr as SELECTION_MODES, Jt as setRegistryPublishableSettings, K as GEOCODE_ATTRIBUTE_PREFIX, Ki as earthAreaToBody, Kn as scrubLegendForRemovedLayers, Kr as substituteExpressionVariables, Kt as redactProjectCredentials, L as serializeProject, Li as isRegionalBasemapSentinel, Ln as DEFAULT_PRINT_LAYOUT, Lr as validateLayerDescriptiveMetadata, Lt as MIN_STORY_CHAPTER_DURATION_SECONDS, M as normalizeWidgets, Mi as REGIONAL_BASEMAPS, Mn as isStyleLibraryTargetLayer, Mr as isValidMetadataUrl, Mt as setGeocodingTokenProvider, N as parseProject, Ni as REGIONAL_BASEMAP_GROUPS, Nn as normalizeStyleLibraryEntries, Nr as metadataDateToRfc3339, Nt as shouldThrottle, O as normalizeProjectComments, Oa as isDuckDBQueryLayer, Oi as basemapToCesiumImagery, On as STYLE_LIBRARY_BUNDLE_VERSION, Or as STAC_VERSION, Ot as resolveGeocoderConfig, P as portableWmsTileUrl, Pi as REGIONAL_BASEMAP_SENTINEL_PREFIX, Pn as parseStyleLibrary, Pr as normalizeLayerDescriptiveMetadata, Pt as unmatchedGeocodeFeature, Q as GEOCODE_STATUS_KEY, Qi as getEllipsoid, Qn as effectiveLayerRenderState, Qr as getArcgisApiKey, Qt as resolveHeaderReferences, R as serializeProjectWithLayerCache, Ri as DEFAULT_ELLIPSOID_ID, Rn as createDefaultPrintLayout, Rr as EXPRESSION_FUNCTION_CATEGORIES, Rt as normalizeStoryChapterDurationSeconds, S as normalizeMapViewState, Sa as NETCDF_IMAGE_SOURCE_KIND, Si as normalizeMapCrs, Sn as geoArrowToFeatureCollection, Sr as layerJoinKey, St as nominatimReverseResultToDisplay, T as normalizeObiaWorkbench, Ta as PROTOMAPS_BASEMAPS, Ti as normalizeArcgisWkid, Tn as BUILT_IN_STYLE_PRESETS, Tr as applyLayerVirtualFields, Tt as normalizeGeocodingProviderId, U as DEFAULT_FORWARD_GEOCODE_ENDPOINT, Ui as PLANETARY_BASEMAP_SENTINEL_PREFIX, Un as scrubPrintLayoutForRemovedLayers, Ur as isStyleSpecColor, Ut as isCredentialUrlParam, V as CARTOCIUDAD_MIN_INTERVAL_MS, Vi as PLANETARY_BASEMAPS, Vn as printLayoutConfigsEqual, Vr as formatExpressionPreviewValue, Vt as PUBLISHABLE_PLUGIN_SETTINGS, W as DEFAULT_GEOCODING_PROVIDER_ID, Wi as PLANET_SWITCHER_OPTIONS, Wn as removedLayerIdSet, Wr as matchFeaturesByExpression, Wt as redactConfigurationCredentials, X as GEOCODE_PROVIDER_KEY, Xi as getActiveMeanRadiusMeters, Xn as buildLayerPanelUnits, Xr as featureSelectionId, Xt as hasHeaderReferences, Y as GEOCODE_LON_KEY, Yi as getActiveEllipsoid, Yn as applyGroupEffects, Yr as applySelectionMode, Yt as allowsCredentialHeaders, Z as GEOCODE_SCORE_KEY, Zi as getActiveSemiMajorAxisMeters, Zn as buildLayerTree, Zr as invertSelection, Zt as isHeaderReferenceOnly, _ as normalizeBookmarkGroups, _a as LAYER_TYPES, _i as MAP_CRS_CHANGE_EVENT, _n as czmlSource, _r as resolveRolePrivileges, _t as geocoderSupportsSuggest, a as DEFAULT_PROJECT_NAME, aa as DEFAULT_BASEMAP, ai as getRuntimeEnvironment, an as cesiumIonAssetKind, ar as reorderLayerGroupInPanel, at as arcgisLocatorInputField, b as normalizeLegendConfig, ba as MAX_PROCESSING_HISTORY, bi as deriveTileMatrixSet, bn as computeGeoArrowBBox, br as cascadeLayerJoinRefresh, bt as nextDelayMs, c as createDefaultMapView, ca as DEFAULT_LAYER_STYLE, ci as decodePolyline, cn as isCesiumOnlyLayer, cr as ROLE_PRIVILEGES, ct as csvRowsToGeocodeRequests, d as createProjectTemplate, da as DEFAULT_PRIMARY_RENDERER, di as geoJSONToPolylineStr, dn as cesiumKmlSource, dr as hasAppPrivilege, dt as geocodeMatchToFeature, ea as getPlanetaryBasemapByStyleUrl, ei as getCesiumIonToken, en as CESIUM_GOOGLE_PHOTOREALISTIC_ASSET_ID, er as layerGroupDepth, et as GeocoderServiceError, f as detachProjectCopy, fa as DEFAULT_PROJECT_PREFERENCES, fi as polylineStrToGeoJSON, fn as createCesiumKmlLayer, fr as inferLayerCapabilities, ft as geocodeReverse, g as normalizeBlankBackgroundColor, ga as LARGE_VECTOR_FEATURE_THRESHOLD, gi as transformGeojsonElevation, gn as createCzmlLayer, gr as resolveLayerCapabilities, gt as geocoderSupportsReverse, h as isSessionOnlyLayer, ha as DUCKDB_VECTOR_ROUTE_BYTES, hi as horizontalBbox, hn as CZML_SOURCE_KIND, hr as normalizeLayerCapabilities, ht as geocoderNeedsApiKey, i as previewValue, ia as BLEND_MODES, ii as getProtomapsStyleUrl, in as cesiumIonAssetId, ir as normalizeGroupContiguity, it as PUBLIC_GEOCODE_ROW_CAP, j as normalizeStoryMap, ji as CHINA_BASEMAPS, jn as extractStyleLibraryStyle, jr as isValidMetadataEmail, jt as setGeocodingFetch, k as normalizeProjectInteraction, ka as shouldUseTiledRendering, ki as normalizeCesiumBasemap, kn as SYMBOL_STYLE_KEYS, kr as buildLayerStacItem, kt as rowCap, l as createEmptyProject, la as DEFAULT_LEGEND_CONFIG, li as decodePolylineDetailed, ln as parseCesiumIonAssetId, lr as appPrivilegeReason, lt as fetchArcGISLocatorInfo, m as isAllowedPluginManifestUrl, ma as DUCKDB_VECTOR_FEATURE_WARN_COUNT, mi as geojsonHasZCoordinates, mn as CZML_QUICK_PICKS, mr as normalizeAppPrivileges, mt as geocoderMinIntervalMs, n as diffProjects, na as setActiveEllipsoidId, ni as getMapboxAccessToken, nn as CESIUM_ION_SOURCE_KIND, nr as layerGroupSortability, nt as NOMINATIM_MIN_INTERVAL_MS, o as MAX_PROJECT_BOOKMARKS, oa as DEFAULT_BLEND_MODE, oi as getSpatialExtensionPath, on as createCesiumIonLayer, or as sortLayerGroupInPanel, ot as buildForwardGeocodeUrl, p as hydrateProjectLayer, pa as DEFAULT_STORY_MAP, pi as unescapePolyline, pn as isCesiumKmlLayer, pr as intersectPrivileges, pt as geocodeSuggest, q as GEOCODE_DISPLAY_NAME_KEY, qi as earthLengthToBody, qn as scrubWidgetsForRemovedLayers, qr as validateMapExpression, qt as redactUrlCredentials, r as jsonValuesEqual, ra as BLANK_BASEMAP, ri as getProtomapsApiKey, rn as CESIUM_OSM_BUILDINGS_ASSET_ID, rr as layerPanelGroupHeaders, rt as NOMINATIM_PUBLIC_HOST, s as applyProjectToStore, sa as DEFAULT_DASHBOARD_COLUMNS, si as batchDecodePolylines, sn as isCesiumIonLayer, sr as ALL_APP_PRIVILEGES, st as buildReverseGeocodeUrl, t as diffFeatureCollections, ta as meanRadiusMeters, ti as getGoogleMapsApiKey, tn as CESIUM_ION_QUICK_PICKS, tr as layerGroupMoveability, tt as GeocoderUnsupportedError, u as createProjectLayerSerializationCache, ua as DEFAULT_MAP_GRID_LAYOUT, ui as encodePolyline, un as CESIUM_KML_SOURCE_KIND, ur as createDefaultAppCapabilities, ut as geocodeForward, v as normalizeBookmarks, va as MAX_DASHBOARD_COLUMNS, vi as MAP_CRS_PRESETS, vn as isCzmlLayer, vr as applyJoinsToLayer, vt as getGeocoderConfig, w as normalizeModels, wa as PROJECT_VERSION, wi as ARCGIS_PROJECTION_PRESETS, wn as withRasterSourceDefaults, wr as stripJoinFields, wt as normalizeGeocodingLocator, x as normalizeMapLayout, xa as MIN_DASHBOARD_COLUMNS, xi as getMapCrsPreset, xn as featureCollectionToGeoArrow, xr as collectTransitiveJoinSourceIds, xt as nominatimResultToFeature, y as normalizeDashboardColumns, ya as MAX_MAP_GRID_DIM, yi as adaptBboxTemplateToCrs, yn as parseCzml, yr as applyLayerJoins, yt as getGeocodingProvider, z as serializeProjectWithLayerCacheAsync, zi as EARTH_MEAN_RADIUS_METERS, zn as isDefaultPrintLayout, zr as compileFeatureExpression, zt as MAX_REDACT_DEPTH } from "./project-diff-CSG1AgSK.mjs";
2
2
  import { Color, featureFilter, latest, normalizePropertyExpression } from "@maplibre/maplibre-gl-style-spec";
3
3
  import { create } from "zustand";
4
4
  import { temporal } from "zundo";
5
5
  import { v4 } from "uuid";
6
6
  import { shallow } from "zustand/shallow";
7
7
  import { useShallow } from "zustand/react/shallow";
8
- //#region src/diagram.ts
8
+ // src/diagram.ts
9
9
  /**
10
10
  * Hard cap on how many features get a diagram. Each diagram is rasterized into
11
11
  * an icon atlas, so an unbounded layer (e.g. a million-point parquet) would
@@ -210,8 +210,7 @@ function diagramPixelSize(datum, style, maxSizeValue) {
210
210
  if (maxSizeValue <= 0 || datum.sizeValue <= 0) return 8;
211
211
  return Math.max(8, size * Math.sqrt(datum.sizeValue / maxSizeValue));
212
212
  }
213
- //#endregion
214
- //#region src/marker-shape.ts
213
+ // src/marker-shape.ts
215
214
  /**
216
215
  * Trace the outline of a built-in marker {@link MarkerShape} onto a 2D canvas
217
216
  * path, centered in a `size`×`size` box. The caller supplies the fill/stroke;
@@ -372,8 +371,7 @@ function resolveSvgSource(markup) {
372
371
  if (trimmed.startsWith("data:")) return trimmed;
373
372
  return null;
374
373
  }
375
- //#endregion
376
- //#region src/hyperlink.ts
374
+ // src/hyperlink.ts
377
375
  /**
378
376
  * Return a whole-value HTTP(S) URL that is safe to expose as an attribute link.
379
377
  *
@@ -394,8 +392,7 @@ function attributeLinkUrl(value) {
394
392
  return null;
395
393
  }
396
394
  }
397
- //#endregion
398
- //#region src/photo.ts
395
+ // src/photo.ts
399
396
  /**
400
397
  * Feature-property keys for inline photo images, shared by the app and the map
401
398
  * package so neither hardcodes a second copy that could drift. These keys are
@@ -413,8 +410,7 @@ const PHOTO_PROPERTY = "photo";
413
410
  * format a browser cannot display at native size (TIFF/HEIC).
414
411
  */
415
412
  const PHOTO_FULL_PROPERTY = "photo_full";
416
- //#endregion
417
- //#region src/color-ramp.ts
413
+ // src/color-ramp.ts
418
414
  /** The built-in color ramps offered by the style panels. */
419
415
  const VECTOR_COLOR_RAMPS = [
420
416
  {
@@ -942,8 +938,7 @@ function naturalClassBreaks(values, count) {
942
938
  breaks[0] = sorted[0];
943
939
  return breaks;
944
940
  }
945
- //#endregion
946
- //#region src/paths.ts
941
+ // src/paths.ts
947
942
  const PATH_TRAVERSAL = /(?:^|[/\\])\.\.(?:[/\\]|$)/;
948
943
  /**
949
944
  * Whether a path contains a `..` directory-traversal segment. Used to reject a
@@ -975,178 +970,7 @@ function hasPathTraversal(path) {
975
970
  function isAbsoluteFilesystemPath(path) {
976
971
  return path.startsWith("/") || /^[a-z]:[\\/]/i.test(path);
977
972
  }
978
- //#endregion
979
- //#region src/runtime-env.ts
980
- const buildEnv = typeof __GEOLIBRE_BUILD_ENV__ === "undefined" ? {} : __GEOLIBRE_BUILD_ENV__;
981
- /**
982
- * The allowlisted build-time environment, with no runtime overlay.
983
- *
984
- * For settings that must be fixed by the build and NOT overridable by a
985
- * `.geolibre.json` a user opened — the auth gate, the deployment profile, the
986
- * embed origin allowlist. Routing those through {@link getRuntimeEnvironment}
987
- * would let project-supplied `__GEOLIBRE_RUNTIME_ENV__` values relax them.
988
- *
989
- * Callers should prefer this over reading `import.meta.env` directly: a
990
- * whole-object read defeats Vite's per-key replacement and inlines every
991
- * `VITE_` var on the build machine into the chunk.
992
- *
993
- * @returns The build-time environment record.
994
- */
995
- function getBuildEnvironment() {
996
- return buildEnv;
997
- }
998
- /**
999
- * Merges build-time env with project runtime env (the latter wins). Falls back
1000
- * to build-time env alone outside a browser (e.g. in tests).
1001
- *
1002
- * @returns The resolved environment variables.
1003
- */
1004
- function getRuntimeEnvironment() {
1005
- if (typeof window === "undefined") return buildEnv;
1006
- return {
1007
- ...buildEnv,
1008
- ...window.__GEOLIBRE_RUNTIME_ENV__ ?? {}
1009
- };
1010
- }
1011
- /**
1012
- * Resolves a local DuckDB spatial extension path from the runtime environment.
1013
- *
1014
- * When `VITE_DUCKDB_SPATIAL_EXTENSION_PATH` is set, DuckDB consumers (the
1015
- * desktop app's own loader and the Add Vector panel's maplibre-gl-vector
1016
- * control) load the spatial extension from this path with `LOAD '<path>'`
1017
- * instead of installing it from the remote repository, which hangs in
1018
- * sandboxed or firewalled environments. Lives in `@geolibre/core` so every
1019
- * consumer shares one implementation.
1020
- *
1021
- * @param env - Environment record (defaults to the runtime environment);
1022
- * injectable for testing.
1023
- * @returns The trimmed extension path, or undefined when unset.
1024
- */
1025
- function getSpatialExtensionPath(env) {
1026
- return (env ?? getRuntimeEnvironment()).VITE_DUCKDB_SPATIAL_EXTENSION_PATH?.trim() || void 0;
1027
- }
1028
- /**
1029
- * Resolves the Protomaps API key from the runtime environment.
1030
- *
1031
- * Protomaps' hosted styles require an API key embedded in the style URL. The
1032
- * key is supplied via `VITE_PROTOMAPS_API_KEY` (baked in at build time for the
1033
- * web demo; see the deploy workflow). When unset, the Protomaps basemaps are
1034
- * unavailable and should be hidden from the UI.
1035
- *
1036
- * @param env - Environment record (defaults to the runtime environment);
1037
- * injectable for testing.
1038
- * @returns The trimmed API key, or undefined when unset.
1039
- */
1040
- function getProtomapsApiKey(env) {
1041
- return (env ?? getRuntimeEnvironment()).VITE_PROTOMAPS_API_KEY?.trim() || void 0;
1042
- }
1043
- /**
1044
- * Resolves the Google Maps API key from the runtime environment.
1045
- *
1046
- * GeoLibre's browser-facing builds normally use `VITE_GOOGLE_MAPS_API_KEY`.
1047
- * The bare `GOOGLE_MAPS_API_KEY` fallback is reached two ways: (1) the desktop
1048
- * Vite config copies it to `VITE_GOOGLE_MAPS_API_KEY` at build time for local
1049
- * shell testing (`vite.config.ts`'s `envPrefix` does not include the bare
1050
- * name), and (2) a project's own runtime environment variables
1051
- * (`window.__GEOLIBRE_RUNTIME_ENV__`), which are not subject to Vite's
1052
- * envPrefix allowlist at all.
1053
- *
1054
- * @param env - Environment record (defaults to the runtime environment);
1055
- * injectable for testing.
1056
- * @returns The trimmed API key, or undefined when unset.
1057
- */
1058
- function getGoogleMapsApiKey(env) {
1059
- const runtimeEnv = env ?? getRuntimeEnvironment();
1060
- return runtimeEnv.VITE_GOOGLE_MAPS_API_KEY?.trim() || runtimeEnv.GOOGLE_MAPS_API_KEY?.trim() || void 0;
1061
- }
1062
- /**
1063
- * Resolves the Mapbox access token from the runtime environment.
1064
- *
1065
- * The basemap control's Mapbox styles authenticate with the user's own token.
1066
- * It is supplied via `VITE_MAPBOX_ACCESS_TOKEN` (baked in at build time from the
1067
- * bare `MAPBOX_TOKEN` env var, which `vite.config.ts` copies into the prefixed
1068
- * name — the spelling Mapbox's own tooling uses) or set at runtime through
1069
- * Settings → Environment variables (`window.__GEOLIBRE_RUNTIME_ENV__`, which
1070
- * bypasses Vite's envPrefix allowlist, so a bare `MAPBOX_TOKEN` entry works
1071
- * there too). When unset, the Mapbox basemaps prompt for a token in the basemap
1072
- * panel's API keys view instead.
1073
- *
1074
- * Note the precedence, shared with {@link getGoogleMapsApiKey} and
1075
- * {@link getCesiumIonToken}: the prefixed name always wins over the bare one,
1076
- * and `getRuntimeEnvironment` merges build-time and runtime vars into one
1077
- * record. So a build that baked in `VITE_MAPBOX_ACCESS_TOKEN` is overridden at
1078
- * runtime by a Settings entry under that *same* prefixed name; a bare
1079
- * `MAPBOX_TOKEN` entry is a fallback for when nothing was baked in, not a way
1080
- * to override a baked token.
1081
- *
1082
- * @param env - Environment record (defaults to the runtime environment);
1083
- * injectable for testing.
1084
- * @returns The trimmed token, or undefined when unset.
1085
- */
1086
- function getMapboxAccessToken(env) {
1087
- const runtimeEnv = env ?? getRuntimeEnvironment();
1088
- return runtimeEnv.VITE_MAPBOX_ACCESS_TOKEN?.trim() || runtimeEnv.MAPBOX_TOKEN?.trim() || void 0;
1089
- }
1090
- /**
1091
- * Resolves the ArcGIS API key from the runtime environment.
1092
- *
1093
- * The ArcGIS renderer draws the translated project basemap and non-Esri layers
1094
- * without a key; Esri's basemap styles and location services require one
1095
- * (an ArcGIS Location Platform or ArcGIS Online API key credential). It is
1096
- * supplied via `VITE_ARCGIS_API_KEY` (baked in at build time from the bare
1097
- * `ARCGIS_API_KEY` env var, which `vite.config.ts` copies into the prefixed
1098
- * name) or set at runtime through Settings → Environment variables
1099
- * (`window.__GEOLIBRE_RUNTIME_ENV__`, so a bare `ARCGIS_API_KEY` entry works
1100
- * there too). Same precedence as {@link getMapboxAccessToken}.
1101
- *
1102
- * @param env - Environment record (defaults to the runtime environment);
1103
- * injectable for testing.
1104
- * @returns The trimmed key, or undefined when unset.
1105
- */
1106
- function getArcgisApiKey(env) {
1107
- const runtimeEnv = env ?? getRuntimeEnvironment();
1108
- return runtimeEnv.VITE_ARCGIS_API_KEY?.trim() || runtimeEnv.ARCGIS_API_KEY?.trim() || void 0;
1109
- }
1110
- /**
1111
- * Resolves the Cesium Ion access token from the runtime environment.
1112
- *
1113
- * Cesium World Terrain and Ion World Imagery need a Cesium Ion token. It is
1114
- * supplied via `VITE_CESIUM_TOKEN` (baked in at build time from the bare
1115
- * `CESIUM_TOKEN` env var, which `vite.config.ts` copies into the prefixed name)
1116
- * or set at runtime through Settings → Environment variables
1117
- * (`window.__GEOLIBRE_RUNTIME_ENV__`, which bypasses Vite's envPrefix allowlist,
1118
- * so a bare `CESIUM_TOKEN` entry works there too).
1119
- *
1120
- * The 3D-globe view itself does **not** need one: it draws the project basemap
1121
- * as its base imagery whenever that basemap has a raster form (see
1122
- * {@link basemapToCesiumImagery}), and falls back to Ion World Imagery — or, with
1123
- * no token, to OpenStreetMap — for the ones that do not. So an absent token
1124
- * costs terrain and the quality of that fallback, not the view.
1125
- *
1126
- * @param env - Environment record (defaults to the runtime environment);
1127
- * injectable for testing.
1128
- * @returns The trimmed token, or undefined when unset.
1129
- */
1130
- function getCesiumIonToken(env) {
1131
- const runtimeEnv = env ?? getRuntimeEnvironment();
1132
- return runtimeEnv.VITE_CESIUM_TOKEN?.trim() || runtimeEnv.CESIUM_TOKEN?.trim() || void 0;
1133
- }
1134
- /**
1135
- * Builds a full Protomaps v5 style URL for a flavor, injecting the API key.
1136
- *
1137
- * @param flavor - The Protomaps flavor name (e.g. `light`, `dark`, `white`,
1138
- * `grayscale`, `black`).
1139
- * @param env - Environment record (defaults to the runtime environment);
1140
- * injectable for testing.
1141
- * @returns The resolved style URL, or undefined when no API key is configured.
1142
- */
1143
- function getProtomapsStyleUrl(flavor, env) {
1144
- const key = getProtomapsApiKey(env);
1145
- if (!key) return void 0;
1146
- return `https://api.protomaps.com/styles/v5/${encodeURIComponent(flavor)}/en.json?key=${encodeURIComponent(key)}`;
1147
- }
1148
- //#endregion
1149
- //#region src/routing.ts
973
+ // src/routing.ts
1150
974
  /**
1151
975
  * Routing / network-analysis client and pure helpers backing the Processing →
1152
976
  * Network tools (isochrones / service areas, OD cost matrices).
@@ -1470,8 +1294,7 @@ function requestRoute(endpoint, body, signal) {
1470
1294
  function requestNavigationRoute(endpoint, body, signal) {
1471
1295
  return postJson(`${stripTrailingSlash(endpoint)}/route`, body, signal);
1472
1296
  }
1473
- //#endregion
1474
- //#region src/vector-color.ts
1297
+ // src/vector-color.ts
1475
1298
  /** Whether a color value is a data-driven expression rather than a flat color. */
1476
1299
  function isVectorColorExpression(value) {
1477
1300
  return Array.isArray(value);
@@ -2252,8 +2075,7 @@ function extrusionColorValue(style) {
2252
2075
  if (vectorExpression !== flat) return vectorExpression;
2253
2076
  return parseJsonExpression(styleValue(style, "extrusionAdvancedStyleEnabled") ? styleValue(style, "extrusionColorExpression") : "") ?? flat;
2254
2077
  }
2255
- //#endregion
2256
- //#region src/style-layer-evaluator.ts
2078
+ // src/style-layer-evaluator.ts
2257
2079
  const GEOMETRY_TYPE = {
2258
2080
  Point: 1,
2259
2081
  LineString: 2,
@@ -2332,8 +2154,7 @@ function createStyleLayerEvaluator(layer) {
2332
2154
  }
2333
2155
  };
2334
2156
  }
2335
- //#endregion
2336
- //#region src/document-locale.ts
2157
+ // src/document-locale.ts
2337
2158
  /**
2338
2159
  * The document language, so formatted numbers and dates follow the UI locale.
2339
2160
  *
@@ -2345,8 +2166,7 @@ function createStyleLayerEvaluator(layer) {
2345
2166
  function documentLocale() {
2346
2167
  return (typeof document !== "undefined" ? document.documentElement.lang.trim() : "") || void 0;
2347
2168
  }
2348
- //#endregion
2349
- //#region src/label-number-format.ts
2169
+ // src/label-number-format.ts
2350
2170
  /**
2351
2171
  * Locale-aware number formatting for map labels (issue #2336).
2352
2172
  *
@@ -2562,8 +2382,7 @@ function labelFieldTextField(labels, fallbackLocale) {
2562
2382
  function formatLabelNumberSample(locale, decimals) {
2563
2383
  return labelNumberFormatter(resolveLabelNumberLocale(locale), decimals).format(LABEL_NUMBER_SAMPLE);
2564
2384
  }
2565
- //#endregion
2566
- //#region src/external-native-paint.ts
2385
+ // src/external-native-paint.ts
2567
2386
  /**
2568
2387
  * Whether a layer is drawn by a **custom render layer** its control created — a
2569
2388
  * deck.gl overlay, 3D Tiles, or Add Vector Layer's own MapLibre layers — rather
@@ -2646,8 +2465,7 @@ function rendererAppliesOpacity(layer, renderer) {
2646
2465
  function isAdoptedVectorAwaitingFeatures(layer) {
2647
2466
  return layer.metadata.sourceKind === "maplibre-gl-vector-adopted" && !layer.geojson;
2648
2467
  }
2649
- //#endregion
2650
- //#region src/attribute-form.ts
2468
+ // src/attribute-form.ts
2651
2469
  /** Look up the configuration for a field, if any. */
2652
2470
  function getAttributeFormField(form, field) {
2653
2471
  return form?.fields.find((entry) => entry.field === field);
@@ -2881,8 +2699,50 @@ function featureForEvaluation(properties, feature) {
2881
2699
  properties
2882
2700
  };
2883
2701
  }
2884
- //#endregion
2885
- //#region src/popup.ts
2702
+ // src/map-click-claims.ts
2703
+ /**
2704
+ * Map tools that own the map's clicks while they draw (the Measure control
2705
+ * placing points, the Geo Editor sketching a shape) register a claim here, and
2706
+ * the Identify click handlers stand down while any claim holds. Without it a
2707
+ * click meant as a measure vertex also opens an Identify popup over the layer
2708
+ * underneath (issue #3237).
2709
+ *
2710
+ * A claim is a predicate read at click time rather than a flag the tool sets,
2711
+ * so a tool whose drawing state changes without an event (Measure stops
2712
+ * drawing silently when its panel collapses) never leaves a stale claim.
2713
+ */
2714
+ const claims = /* @__PURE__ */ new Set();
2715
+ /**
2716
+ * Register a tool whose drawing state claims the map's clicks.
2717
+ *
2718
+ * Args:
2719
+ * isClaiming: Returns true while the tool is drawing and owns map clicks.
2720
+ *
2721
+ * Returns:
2722
+ * A function that removes the claim.
2723
+ */
2724
+ function registerMapClickClaim(isClaiming) {
2725
+ const entry = () => isClaiming();
2726
+ claims.add(entry);
2727
+ return () => {
2728
+ claims.delete(entry);
2729
+ };
2730
+ }
2731
+ /**
2732
+ * Whether a registered map tool currently owns the map's clicks.
2733
+ *
2734
+ * Returns:
2735
+ * True when any registered claim reports that its tool is drawing.
2736
+ */
2737
+ function isMapClickClaimed() {
2738
+ for (const isClaiming of claims) try {
2739
+ if (isClaiming()) return true;
2740
+ } catch (error) {
2741
+ console.warn("[GeoLibre] A map click claim could not be read", error);
2742
+ }
2743
+ return false;
2744
+ }
2745
+ // src/popup.ts
2886
2746
  /** Match an inline base64 raster image (excludes SVG, which can carry scripts). */
2887
2747
  const INLINE_IMAGE_DATA_URL = /^data:image\/(?!svg)[\w.+-]+;base64,/i;
2888
2748
  /**
@@ -3225,8 +3085,7 @@ function isSafePopupUrl(value, allowDataImage = false) {
3225
3085
  if (/^https?:\/\//i.test(trimmed)) return true;
3226
3086
  return allowDataImage && INLINE_IMAGE_DATA_URL.test(trimmed);
3227
3087
  }
3228
- //#endregion
3229
- //#region src/quick-filters.ts
3088
+ // src/quick-filters.ts
3230
3089
  /**
3231
3090
  * Quick filters (issue #2114): per-layer, data-driven filter controls that
3232
3091
  * compile down to the MapLibre filter a layer already supports.
@@ -3465,8 +3324,7 @@ function clearQuickFilterValues(filters) {
3465
3324
  text: filter.kind === "text" ? "" : filter.text
3466
3325
  }));
3467
3326
  }
3468
- //#endregion
3469
- //#region src/layer-filters.ts
3327
+ // src/layer-filters.ts
3470
3328
  /**
3471
3329
  * Return a layer's persisted expression filter when it has a usable array
3472
3330
  * shape, otherwise return null.
@@ -3501,8 +3359,7 @@ function compileLayerFilters(layer) {
3501
3359
  function hasActiveLayerFilter(layer) {
3502
3360
  return compileLayerFilters(layer) !== null;
3503
3361
  }
3504
- //#endregion
3505
- //#region src/deployment-capabilities.ts
3362
+ // src/deployment-capabilities.ts
3506
3363
  /** Every capability, in a stable order for docs and tests. */
3507
3364
  const DEPLOYMENT_CAPABILITIES = [
3508
3365
  "project:edit",
@@ -3555,8 +3412,7 @@ function parseDeploymentCapabilities(raw) {
3555
3412
  }
3556
3413
  return granted;
3557
3414
  }
3558
- //#endregion
3559
- //#region src/store/capabilities-slice.ts
3415
+ // src/store/capabilities-slice.ts
3560
3416
  /**
3561
3417
  * What this deployment and this session may do: deployment capabilities
3562
3418
  * (issue #1673) and the application role / privilege model (issue #1672).
@@ -3612,8 +3468,7 @@ const createCapabilitiesSlice = (set, get) => ({
3612
3468
  return hasAppPrivilege(get().capabilities, privilege);
3613
3469
  }
3614
3470
  });
3615
- //#endregion
3616
- //#region src/store/collaboration-slice.ts
3471
+ // src/store/collaboration-slice.ts
3617
3472
  /**
3618
3473
  * A fresh, inactive collaboration slice (no live session). Frozen (like
3619
3474
  * DEFAULT_LEGEND_CONFIG) to guard against accidental in-place mutation; store
@@ -3671,8 +3526,7 @@ const createCollaborationSlice = (set) => ({
3671
3526
  }
3672
3527
  }))
3673
3528
  });
3674
- //#endregion
3675
- //#region src/store/session-slice.ts
3529
+ // src/store/session-slice.ts
3676
3530
  /** Reserved Identify target for querying every visible queryable layer at once. */
3677
3531
  const IDENTIFY_ALL_LAYERS_ID = "__geolibre_identify_all_layers__";
3678
3532
  /**
@@ -3801,8 +3655,7 @@ const createSessionSlice = (set) => ({
3801
3655
  }),
3802
3656
  setAttributeFilter: (filter) => set({ attributeFilter: filter })
3803
3657
  });
3804
- //#endregion
3805
- //#region src/store/layer-removal.ts
3658
+ // src/store/layer-removal.ts
3806
3659
  /**
3807
3660
  * Reference scrubbing shared by the actions that delete layers (`removeLayer`
3808
3661
  * and `removeLayerGroup` with `removeChildren`), so a deleted layer never
@@ -3867,8 +3720,7 @@ function cascadeJoinRefreshForRemoved(layers, layerIds) {
3867
3720
  for (const id of removedLayerIdSet(layerIds)) current = cascadeLayerJoinRefresh(current, id);
3868
3721
  return current;
3869
3722
  }
3870
- //#endregion
3871
- //#region src/store/layer-groups-slice.ts
3723
+ // src/store/layer-groups-slice.ts
3872
3724
  /**
3873
3725
  * Layer-panel folders: create, rename, nest, reorder, sort and delete groups,
3874
3726
  * and move layers between them. `layerGroups` is tracked by undo history
@@ -4059,8 +3911,7 @@ const createLayerGroupsSlice = (set, get) => ({
4059
3911
  };
4060
3912
  })
4061
3913
  });
4062
- //#endregion
4063
- //#region src/file-name.ts
3914
+ // src/file-name.ts
4064
3915
  /**
4065
3916
  * A bare Android SAF document ID, as the browser `File.name` carries after a
4066
3917
  * native import: a known volume prefix (`primary:`, `home:`, `raw:`, `msf:`, or a
@@ -4098,8 +3949,7 @@ function uniqueImportedLayerName(name, names) {
4098
3949
  while (existing.has(candidate)) candidate = `${name}_${suffix++}`;
4099
3950
  return candidate;
4100
3951
  }
4101
- //#endregion
4102
- //#region src/layer-defaults.ts
3952
+ // src/layer-defaults.ts
4103
3953
  /**
4104
3954
  * Initial styling for a newly added vector layer (issue #1519).
4105
3955
  *
@@ -4288,8 +4138,7 @@ function isInitialLayerStyle(style, geojson) {
4288
4138
  };
4289
4139
  return style.fillOpacity === expected.fillOpacity && style.strokeWidth === expected.strokeWidth && style.circleRadius === expected.circleRadius;
4290
4140
  }
4291
- //#endregion
4292
- //#region src/layer-style-clipboard.ts
4141
+ // src/layer-style-clipboard.ts
4293
4142
  /**
4294
4143
  * `metadata.sourceKind` marking a deck.gl COG raster managed by the
4295
4144
  * maplibre-gl-raster control. Mirrors `RASTER_SOURCE_KIND` in
@@ -4400,8 +4249,7 @@ function rescaleEntryCount(state) {
4400
4249
  if (state.mode === "rgb") return Array.isArray(state.bands) ? state.bands.length : 3;
4401
4250
  return 1;
4402
4251
  }
4403
- //#endregion
4404
- //#region src/layer-style-file.ts
4252
+ // src/layer-style-file.ts
4405
4253
  /** `type` tag identifying a layer styles file. */
4406
4254
  const LAYER_STYLES_FILE_TYPE = "geolibre-layer-styles";
4407
4255
  /** Format version written by {@link serializeLayerStylesFile}. */
@@ -4596,8 +4444,7 @@ function matchLayerStyleEntry(layer, entries) {
4596
4444
  patch
4597
4445
  } : null;
4598
4446
  }
4599
- //#endregion
4600
- //#region src/store/layers-slice.ts
4447
+ // src/store/layers-slice.ts
4601
4448
  /**
4602
4449
  * The layer types {@link AppState.addTileLayer} accepts. Each renders through
4603
4450
  * the raster tile path, which is why the layer's `source.type` is always
@@ -4612,7 +4459,8 @@ const RASTER_TILE_LAYER_TYPES = /* @__PURE__ */ new Set([
4612
4459
  const createLayersSlice = (set, get) => ({
4613
4460
  layers: [],
4614
4461
  copiedLayerStyle: null,
4615
- addLayer: (layer, beforeLayerId = null) => set((s) => {
4462
+ addLayer: (inputLayer, beforeLayerId = null) => set((s) => {
4463
+ let layer = withRasterSourceDefaults(inputLayer);
4616
4464
  const layers = [...s.layers];
4617
4465
  const { sourcePath } = layer;
4618
4466
  if (sourcePath && (!/^[a-z][a-z0-9+.-]*:\/\//i.test(sourcePath) || /^(content|file):\/\//i.test(sourcePath)) && isSourceDerivedLayerName(layer.name, sourcePath)) layer = {
@@ -4656,10 +4504,19 @@ const createLayersSlice = (set, get) => ({
4656
4504
  isDirty: true
4657
4505
  })),
4658
4506
  updateLayer: (id, patch) => set((s) => {
4659
- let layers = s.layers.map((l) => l.id === id ? {
4660
- ...l,
4661
- ...patch
4662
- } : l);
4507
+ let layers = s.layers.map((l) => {
4508
+ if (l.id !== id) return l;
4509
+ const updated = {
4510
+ ...l,
4511
+ ...patch
4512
+ };
4513
+ if (patch.geojson !== void 0 && !("geoarrow" in patch)) delete updated.geoarrow;
4514
+ else if ("geoarrow" in patch) {
4515
+ if (patch.geoarrow === null || patch.geoarrow === void 0) delete updated.geoarrow;
4516
+ else updated.geoarrow = patch.geoarrow;
4517
+ }
4518
+ return updated;
4519
+ });
4663
4520
  if (patch.geojson !== void 0) {
4664
4521
  if (patch.joins === void 0 && patch.virtualFields === void 0) layers = layers.map((l) => l.id === id && (l.joins?.length || l.virtualFields?.length) ? applyJoinsToLayer(l, layers) : l);
4665
4522
  layers = cascadeLayerJoinRefresh(layers, id);
@@ -4793,8 +4650,14 @@ const createLayersSlice = (set, get) => ({
4793
4650
  isDirty: true
4794
4651
  };
4795
4652
  }),
4796
- addGeoJsonLayer: (name, geojson, sourcePath, beforeLayerId = null) => {
4653
+ addGeoJsonLayer: (name, geojson, sourcePath, beforeLayerId = null, extra) => {
4797
4654
  const id = v4();
4655
+ let geoarrow = extra?.geoarrow;
4656
+ if (!geoarrow && shouldUseTiledRendering(geojson)) try {
4657
+ geoarrow = featureCollectionToGeoArrow(geojson) ?? void 0;
4658
+ } catch (err) {
4659
+ console.warn("[GeoLibre] GeoArrow batch conversion failed; falling back to GeoJSON", err);
4660
+ }
4798
4661
  const layer = {
4799
4662
  id,
4800
4663
  name,
@@ -4807,8 +4670,9 @@ const createLayersSlice = (set, get) => ({
4807
4670
  layers: get().layers,
4808
4671
  overrides: { simpleStyleEnabled: hasSimpleStyleProperties(geojson) }
4809
4672
  }),
4810
- metadata: {},
4673
+ metadata: { ...extra?.metadata },
4811
4674
  geojson,
4675
+ ...geoarrow ? { geoarrow } : {},
4812
4676
  sourcePath
4813
4677
  };
4814
4678
  get().addLayer(layer, beforeLayerId);
@@ -4871,8 +4735,7 @@ const createLayersSlice = (set, get) => ({
4871
4735
  return id;
4872
4736
  }
4873
4737
  });
4874
- //#endregion
4875
- //#region src/layer-library.ts
4738
+ // src/layer-library.ts
4876
4739
  /** `type` discriminator of an exported Layer Library bundle file. */
4877
4740
  const LAYER_LIBRARY_BUNDLE_TYPE = "geolibre-layer-library";
4878
4741
  /** Current bundle format version, for forward-compatible readers. */
@@ -5532,8 +5395,7 @@ function createLayerLibraryEntryId() {
5532
5395
  if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
5533
5396
  return `layer-lib-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
5534
5397
  }
5535
- //#endregion
5536
- //#region src/store/libraries-slice.ts
5398
+ // src/store/libraries-slice.ts
5537
5399
  /**
5538
5400
  * The Style Manager, Layer Library and Template Library. The app-level lists
5539
5401
  * live outside the project lifecycle (persisted by the desktop app); only the
@@ -5576,8 +5438,7 @@ const createLibrariesSlice = (set) => ({
5576
5438
  saveTemplateEntry: (entry) => set((s) => ({ templateLibrary: s.templateLibrary.some((t) => t.id === entry.id) ? s.templateLibrary.map((t) => t.id === entry.id ? entry : t) : [...s.templateLibrary, entry] })),
5577
5439
  deleteTemplateEntry: (id) => set((s) => ({ templateLibrary: s.templateLibrary.filter((t) => t.id !== id) }))
5578
5440
  });
5579
- //#endregion
5580
- //#region src/store/map-view-slice.ts
5441
+ // src/store/map-view-slice.ts
5581
5442
  /**
5582
5443
  * The map workspace: the primary camera, the basemap, project preferences, the
5583
5444
  * multi-map grid and its secondary panes, and which engine draws the primary
@@ -5825,8 +5686,7 @@ const createMapViewSlice = (set) => ({
5825
5686
  isDirty: true
5826
5687
  })
5827
5688
  });
5828
- //#endregion
5829
- //#region src/store/processing-slice.ts
5689
+ // src/store/processing-slice.ts
5830
5690
  /**
5831
5691
  * Saved processing models (issue #344) and the Processing History log
5832
5692
  * (issue #1292). Both are saved with the project.
@@ -5872,8 +5732,7 @@ const createProcessingSlice = (set) => ({
5872
5732
  isDirty: true
5873
5733
  } : { obiaWorkbench })
5874
5734
  });
5875
- //#endregion
5876
- //#region src/store/project-content-slice.ts
5735
+ // src/store/project-content-slice.ts
5877
5736
  /**
5878
5737
  * Project content that sits beside the layers: review comments (issue #1518),
5879
5738
  * Dashboard widgets (issue #401) and the story map. All of it is saved with the
@@ -6049,8 +5908,7 @@ const createProjectContentSlice = (set) => ({
6049
5908
  };
6050
5909
  })
6051
5910
  });
6052
- //#endregion
6053
- //#region src/history.ts
5911
+ // src/history.ts
6054
5912
  /**
6055
5913
  * Runtime-settable window (ms) used to coalesce rapid history-producing changes
6056
5914
  * (e.g. a continuous opacity/style slider drag) into a single undo entry. Tests
@@ -6178,8 +6036,7 @@ function leadingDebounce(fn, getWait) {
6178
6036
  };
6179
6037
  return wrapped;
6180
6038
  }
6181
- //#endregion
6182
- //#region src/store/undo-history.ts
6039
+ // src/store/undo-history.ts
6183
6040
  /**
6184
6041
  * Undo/redo for the app store. zundo's `temporal` middleware tracks only the
6185
6042
  * project-data fields that {@link partializeHistory} lists (layers, groups,
@@ -6438,8 +6295,7 @@ function clearHistory() {
6438
6295
  notifyProjectRestoreHistory();
6439
6296
  }
6440
6297
  }
6441
- //#endregion
6442
- //#region src/store/project-slice.ts
6298
+ // src/store/project-slice.ts
6443
6299
  /**
6444
6300
  * Project identity and lifecycle: name, path, dirty flag, recent projects, and
6445
6301
  * the project-level settings that have no richer home (plugins, legend, print
@@ -6588,8 +6444,7 @@ const createProjectSlice = (set, get) => ({
6588
6444
  });
6589
6445
  }
6590
6446
  });
6591
- //#endregion
6592
- //#region src/store/ui-slice.ts
6447
+ // src/store/ui-slice.ts
6593
6448
  const createUiSlice = (set) => ({
6594
6449
  ui: {
6595
6450
  processingOpen: false,
@@ -6785,8 +6640,7 @@ const createUiSlice = (set) => ({
6785
6640
  styleManagerOpen: open
6786
6641
  } }))
6787
6642
  });
6788
- //#endregion
6789
- //#region src/store.ts
6643
+ // src/store.ts
6790
6644
  /**
6791
6645
  * The app store: one Zustand store wrapped in zundo's undo history, composed
6792
6646
  * from the slices under `./store/` (zustand's slices pattern). The split is
@@ -6844,8 +6698,7 @@ function useAppCapability(privilege) {
6844
6698
  reason: appPrivilegeReason(capabilities, privilege)
6845
6699
  };
6846
6700
  }
6847
- //#endregion
6848
- //#region src/selection-actions.ts
6701
+ // src/selection-actions.ts
6849
6702
  /**
6850
6703
  * Store-touching selection helpers. Kept out of `selection.ts` so that module
6851
6704
  * stays a pure, directly testable set of functions over id arrays.
@@ -6873,8 +6726,7 @@ function applyMatchedSelection(targetLayerId, matchedIds, mode) {
6873
6726
  store.selectFeatures(next);
6874
6727
  return next.length;
6875
6728
  }
6876
- //#endregion
6877
- //#region src/scale-units.ts
6729
+ // src/scale-units.ts
6878
6730
  /** Conversion constants shared by every scale-bar denomination. */
6879
6731
  const FEET_PER_METER = 3.2808398950131235;
6880
6732
  const METERS_PER_FOOT = .3048;
@@ -6938,8 +6790,7 @@ function scaleSpan(maxMeters, unit) {
6938
6790
  label
6939
6791
  };
6940
6792
  }
6941
- //#endregion
6942
- //#region src/elevation.ts
6793
+ // src/elevation.ts
6943
6794
  /** Open-Meteo accepts at most 100 coordinates per elevation request. */
6944
6795
  const MAX_POINTS_PER_REQUEST = 100;
6945
6796
  /** Abort an elevation request that has not responded within this window. */
@@ -7137,8 +6988,7 @@ function createPointerElevationResolver(options) {
7137
6988
  dispose: invalidate
7138
6989
  };
7139
6990
  }
7140
- //#endregion
7141
- //#region src/camera-altitude.ts
6991
+ // src/camera-altitude.ts
7142
6992
  /**
7143
6993
  * Camera altitude ("Eye alt") for the status bar — issue #1816.
7144
6994
  *
@@ -7185,8 +7035,7 @@ function formatCameraAltitude(meters, unit) {
7185
7035
  maximumFractionDigits: digits
7186
7036
  })} ${label}`;
7187
7037
  }
7188
- //#endregion
7189
- //#region src/layers-file.ts
7038
+ // src/layers-file.ts
7190
7039
  /** `type` tag identifying a layers file. */
7191
7040
  const LAYERS_FILE_TYPE = "geolibre-layers";
7192
7041
  /** Format version written by {@link serializeLayersFile}. */
@@ -7392,8 +7241,7 @@ function addLayersToProject(project, content) {
7392
7241
  layerGroups: [...groups, ...structuredClone(layerGroups)]
7393
7242
  };
7394
7243
  }
7395
- //#endregion
7396
- //#region src/pixel-format.ts
7244
+ // src/pixel-format.ts
7397
7245
  /**
7398
7246
  * Trim a float sample to something readable. A 32-bit raster value decoded to a
7399
7247
  * JS double prints all 17 digits of its binary representation
@@ -7409,8 +7257,7 @@ function formatPixelValue(value) {
7409
7257
  if (Number.isInteger(value)) return String(value);
7410
7258
  return String(Number(value.toPrecision(6)));
7411
7259
  }
7412
- //#endregion
7413
- //#region src/s3.ts
7260
+ // src/s3.ts
7414
7261
  /** SigV4 allows a presigned URL to live at most seven days. */
7415
7262
  const S3_MAX_PRESIGN_SECONDS = 604800;
7416
7263
  const S3_SCHEME = /^s3a?:\/\//i;
@@ -7811,8 +7658,7 @@ async function explainS3ReadError(url, error, translate) {
7811
7658
  origin
7812
7659
  }) : fallback, location.bucket, origin, error);
7813
7660
  }
7814
- //#endregion
7815
- //#region src/storymap-sample.ts
7661
+ // src/storymap-sample.ts
7816
7662
  const SAMPLE_ASSET_BASE = "https://opengeos.org/maplibre-gl-storymaps/assets";
7817
7663
  /**
7818
7664
  * Build a ready-to-play sample story map.
@@ -7931,8 +7777,7 @@ function createSampleStoryMap() {
7931
7777
  ]
7932
7778
  };
7933
7779
  }
7934
- //#endregion
7935
- //#region src/storymap-playback.ts
7780
+ // src/storymap-playback.ts
7936
7781
  /** The clamped opacity recorded for a layer, or undefined when untouched. */
7937
7782
  function recordedStoryOpacity(opacities, layerId) {
7938
7783
  const value = opacities?.[layerId];
@@ -8018,8 +7863,7 @@ function storyLocationView(location, current) {
8018
7863
  bearing: location.bearing ?? current.bearing
8019
7864
  };
8020
7865
  }
8021
- //#endregion
8022
- //#region src/storymap-io.ts
7866
+ // src/storymap-io.ts
8023
7867
  /**
8024
7868
  * Serialize a story map to pretty-printed JSON for export.
8025
7869
  *
@@ -8062,7 +7906,8 @@ const CSV_HEADERS = [
8062
7906
  "mapAnimation",
8063
7907
  "rotateAnimation",
8064
7908
  "onChapterEnter",
8065
- "onChapterExit"
7909
+ "onChapterExit",
7910
+ "durationSeconds"
8066
7911
  ];
8067
7912
  /**
8068
7913
  * Serialize a story map's chapters to CSV (one row per chapter).
@@ -8090,7 +7935,8 @@ function serializeStoryMapCsv(storymap) {
8090
7935
  c.mapAnimation,
8091
7936
  String(c.rotateAnimation),
8092
7937
  JSON.stringify(c.onChapterEnter),
8093
- JSON.stringify(c.onChapterExit)
7938
+ JSON.stringify(c.onChapterExit),
7939
+ String(c.durationSeconds ?? "")
8094
7940
  ].map(csvEscape).join(","));
8095
7941
  return rows.join("\n") + "\n";
8096
7942
  }
@@ -8145,6 +7991,7 @@ function parseStoryMapCsv(text, base) {
8145
7991
  },
8146
7992
  mapAnimation: get("mapanimation").trim() || "flyTo",
8147
7993
  rotateAnimation: bool("rotateanimation"),
7994
+ durationSeconds: get("durationseconds").trim() ? Number(get("durationseconds")) : void 0,
8148
7995
  onChapterEnter: jsonArray("onchapterenter"),
8149
7996
  onChapterExit: jsonArray("onchapterexit")
8150
7997
  };
@@ -8194,8 +8041,7 @@ function parseCsvRows(text) {
8194
8041
  }
8195
8042
  return rows;
8196
8043
  }
8197
- //#endregion
8198
- //#region src/layer-selectors.ts
8044
+ // src/layer-selectors.ts
8199
8045
  /**
8200
8046
  * Narrow subscriptions to the store's `layers` array.
8201
8047
  *
@@ -8336,816 +8182,7 @@ function useLayerSummaries() {
8336
8182
  function useLayersWhen(active) {
8337
8183
  return useAppStore(selectLayersWhen(active));
8338
8184
  }
8339
- //#endregion
8340
- //#region src/geocoding.ts
8341
- /**
8342
- * Geocoding client and pure helpers shared by the batch-geocode dialog and the
8343
- * reverse-geocode plugin.
8344
- *
8345
- * This module lives in `@geolibre/core` rather than `@geolibre/processing`
8346
- * because `@geolibre/plugins` depends on core but not on processing, and the
8347
- * reverse-geocode plugin needs the same client. The pure helpers (URL builders,
8348
- * result mappers, pacing) carry no React or MapLibre dependency so they can be
8349
- * unit-tested without a browser or network.
8350
- *
8351
- * Providers: geocoding is dispatched through a small {@link GeocodingProvider}
8352
- * registry so alternatives to Nominatim (ArcGIS World Geocoder, CartoCiudad, Mapbox,
8353
- * Pelias, Google) can be selected per project. Each provider builds its own request
8354
- * URLs and normalizes its response into a {@link GeocodeMatch} (forward) or a
8355
- * {@link ReverseGeocodeDisplay} (reverse), so the dialog and plugin stay
8356
- * provider-agnostic. Nominatim is the default.
8357
- *
8358
- * The 1 request/second throttle and the row cap are part of Nominatim's public
8359
- * usage policy and are therefore applied ONLY to the default public host; a
8360
- * self-hosted endpoint relaxes both, and keyed providers (Mapbox/ArcGIS/Google)
8361
- * are paced by their own quotas, not by us.
8362
- *
8363
- * Browser fetch cannot set `User-Agent`/`Referer`, so the app is identified to
8364
- * Nominatim via the optional `email` query parameter plus the automatically
8365
- * sent `Referer`. The desktop build overrides this with a native (Tauri) fetch
8366
- * via {@link setGeocodingFetch} — see the note there. See
8367
- * docs/user-guide/data-integrations.md#geocoding.
8368
- */
8369
- const DEFAULT_FORWARD_GEOCODE_ENDPOINT = "https://nominatim.openstreetmap.org/search";
8370
- const DEFAULT_REVERSE_GEOCODE_ENDPOINT = "https://nominatim.openstreetmap.org/reverse";
8371
- /** Host whose public usage policy (1 req/sec, bulk limits) we must respect. */
8372
- const NOMINATIM_PUBLIC_HOST = "nominatim.openstreetmap.org";
8373
- /** Minimum spacing between requests to the public Nominatim endpoint. */
8374
- const NOMINATIM_MIN_INTERVAL_MS = 1100;
8375
- /** Host of the free, unkeyed public CartoCiudad geocoder (IGN/CNIG). */
8376
- const CARTOCIUDAD_PUBLIC_HOST = "cartociudad.es";
8377
- /**
8378
- * Courtesy spacing (~5 req/sec) between requests to the public CartoCiudad
8379
- * endpoint. Unlike {@link NOMINATIM_MIN_INTERVAL_MS} this is not a published
8380
- * policy — IGN documents no rate limit — it is our own conservative default so
8381
- * a large batch does not burst at a free public government service. See
8382
- * {@link geocoderMinIntervalMs}.
8383
- */
8384
- const CARTOCIUDAD_MIN_INTERVAL_MS = 200;
8385
- /** Max rows a single batch run will geocode against the public endpoint. */
8386
- const PUBLIC_GEOCODE_ROW_CAP = 1e3;
8387
- /** Property keys added to each geocoded feature. */
8388
- const GEOCODE_LAT_KEY = "geocode_lat";
8389
- const GEOCODE_LON_KEY = "geocode_lon";
8390
- const GEOCODE_DISPLAY_NAME_KEY = "geocode_display_name";
8391
- const GEOCODE_SCORE_KEY = "geocode_importance";
8392
- /** Provider id that produced (or failed to produce) a match, when stamped. */
8393
- const GEOCODE_PROVIDER_KEY = "geocode_provider";
8394
- /** Whether a row matched, when stamped. See {@link GeocodeStatus}. */
8395
- const GEOCODE_STATUS_KEY = "geocode_status";
8396
- /** The provider used when none is configured. */
8397
- const DEFAULT_GEOCODING_PROVIDER_ID = "nominatim";
8398
- function coerceScore(value) {
8399
- if (value === void 0 || value === null || value === "") return null;
8400
- const num = Number(value);
8401
- return Number.isFinite(num) ? num : null;
8402
- }
8403
- /** Append `base` to `existing`, suffixing `_2`, `_3`, ... if the key collides. */
8404
- function uniqueKey(base, existing) {
8405
- if (!(base in existing)) return base;
8406
- let suffix = 2;
8407
- while (`${base}_${suffix}` in existing) suffix += 1;
8408
- return `${base}_${suffix}`;
8409
- }
8410
- /**
8411
- * Coerce an address-parts record to `Record<string, string>`. Provider address
8412
- * objects can carry numbers (ArcGIS `Score`/`X`/`Y`, Pelias coordinates), so
8413
- * values are stringified rather than unsafely cast; null/undefined are dropped.
8414
- */
8415
- function stringifyParts(obj) {
8416
- const parts = {};
8417
- for (const [key, value] of Object.entries(obj)) {
8418
- if (value === null || value === void 0) continue;
8419
- parts[key] = typeof value === "string" ? value : String(value);
8420
- }
8421
- return parts;
8422
- }
8423
- /** Read a string property off an unknown object, or undefined. */
8424
- function readString(obj, key) {
8425
- if (obj && typeof obj === "object" && key in obj) {
8426
- const value = obj[key];
8427
- if (typeof value === "string") return value;
8428
- if (typeof value === "number") return String(value);
8429
- }
8430
- }
8431
- /** Build a Nominatim forward-geocoding URL (jsonv2, address details on). */
8432
- function buildForwardGeocodeUrl(endpoint, query, options = {}) {
8433
- const url = new URL(endpoint);
8434
- url.searchParams.set("q", query);
8435
- url.searchParams.set("format", "jsonv2");
8436
- url.searchParams.set("addressdetails", "1");
8437
- url.searchParams.set("limit", String(options.limit ?? 1));
8438
- if (options.email) url.searchParams.set("email", options.email);
8439
- return url.toString();
8440
- }
8441
- /** Build a Nominatim reverse-geocoding URL (jsonv2, address details on). */
8442
- function buildReverseGeocodeUrl(endpoint, lon, lat, options = {}) {
8443
- const url = new URL(endpoint);
8444
- url.searchParams.set("lat", String(lat));
8445
- url.searchParams.set("lon", String(lon));
8446
- url.searchParams.set("format", "jsonv2");
8447
- url.searchParams.set("addressdetails", "1");
8448
- if (options.zoom !== void 0) url.searchParams.set("zoom", String(options.zoom));
8449
- if (options.email) url.searchParams.set("email", options.email);
8450
- return url.toString();
8451
- }
8452
- /** Map one Nominatim forward result onto a normalized match, or null. */
8453
- function nominatimForwardResultToMatch(result) {
8454
- const lat = Number(result.lat);
8455
- const lon = Number(result.lon);
8456
- if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
8457
- return {
8458
- lat,
8459
- lon,
8460
- displayName: result.display_name ?? "",
8461
- score: coerceScore(result.importance)
8462
- };
8463
- }
8464
- /**
8465
- * Convert a normalized {@link GeocodeMatch} into a point Feature whose
8466
- * properties carry the original CSV row plus `geocode_lat`/`geocode_lon`/
8467
- * `geocode_display_name`/`geocode_importance`. The added keys are de-duplicated
8468
- * against the original columns so an existing `geocode_lat` is not clobbered.
8469
- * Geometry coordinates are `[lon, lat]`. Returns null when the match has no
8470
- * finite coordinates.
8471
- *
8472
- * `extra.providerId`, when passed, also stamps `geocode_provider` and
8473
- * `geocode_status: "matched"` (dropped when omitted, so the existing
8474
- * `GeocodeDialog` call site keeps its current output unchanged).
8475
- */
8476
- function geocodeMatchToFeature(match, originalRow = {}, extra) {
8477
- if (!Number.isFinite(match.lat) || !Number.isFinite(match.lon)) return null;
8478
- const properties = { ...originalRow };
8479
- const added = {
8480
- [GEOCODE_LAT_KEY]: match.lat,
8481
- [GEOCODE_LON_KEY]: match.lon,
8482
- [GEOCODE_DISPLAY_NAME_KEY]: match.displayName ?? "",
8483
- [GEOCODE_SCORE_KEY]: match.score
8484
- };
8485
- if (extra?.providerId) {
8486
- added[GEOCODE_PROVIDER_KEY] = extra.providerId;
8487
- added[GEOCODE_STATUS_KEY] = "matched";
8488
- }
8489
- for (const [key, value] of Object.entries(added)) properties[uniqueKey(key, properties)] = value;
8490
- return {
8491
- type: "Feature",
8492
- geometry: {
8493
- type: "Point",
8494
- coordinates: [match.lon, match.lat]
8495
- },
8496
- properties
8497
- };
8498
- }
8499
- /**
8500
- * Build a null-geometry Feature for a CSV row that could not be geocoded, so
8501
- * it is kept (not silently dropped) alongside the matched point features.
8502
- * Mirrors the null-geometry shape {@link parseDelimitedTextLayer} already uses
8503
- * for a non-spatial attribute table. Stamps `geocode_status: "unmatched"` and
8504
- * `geocode_provider`, de-duplicated against the original columns the same way
8505
- * {@link geocodeMatchToFeature} does.
8506
- */
8507
- function unmatchedGeocodeFeature(originalRow, providerId) {
8508
- const properties = { ...originalRow };
8509
- const added = {
8510
- [GEOCODE_STATUS_KEY]: "unmatched",
8511
- [GEOCODE_PROVIDER_KEY]: providerId
8512
- };
8513
- for (const [key, value] of Object.entries(added)) properties[uniqueKey(key, properties)] = value;
8514
- return {
8515
- type: "Feature",
8516
- geometry: null,
8517
- properties
8518
- };
8519
- }
8520
- /**
8521
- * Convert a Nominatim forward result into a point Feature. Retained as a thin
8522
- * wrapper over {@link geocodeMatchToFeature} for callers and tests that work
8523
- * directly with Nominatim results. Returns null when the result has no finite
8524
- * coordinates.
8525
- */
8526
- function nominatimResultToFeature(result, originalRow = {}) {
8527
- const match = nominatimForwardResultToMatch(result);
8528
- return match ? geocodeMatchToFeature(match, originalRow) : null;
8529
- }
8530
- /**
8531
- * Resolve a Nominatim reverse result to a display string and address parts, or
8532
- * null when the point could not be reverse-geocoded.
8533
- */
8534
- function nominatimReverseResultToDisplay(result) {
8535
- if (!result || result.error) return null;
8536
- const displayName = result.display_name?.trim();
8537
- if (!displayName) return null;
8538
- return {
8539
- displayName,
8540
- parts: result.address ?? {}
8541
- };
8542
- }
8543
- const nominatimProvider = {
8544
- id: "nominatim",
8545
- label: "Nominatim (OpenStreetMap)",
8546
- forward: true,
8547
- reverse: true,
8548
- requiresApiKey: false,
8549
- acceptsApiKey: false,
8550
- defaultForwardEndpoint: DEFAULT_FORWARD_GEOCODE_ENDPOINT,
8551
- defaultReverseEndpoint: DEFAULT_REVERSE_GEOCODE_ENDPOINT,
8552
- buildForwardUrl: (config, query, options) => buildForwardGeocodeUrl(config.forwardEndpoint, query, {
8553
- email: options.email,
8554
- limit: options.limit
8555
- }),
8556
- parseForward: (data) => Array.isArray(data) ? data.map(nominatimForwardResultToMatch).filter((m) => m !== null) : [],
8557
- buildReverseUrl: (config, lon, lat, options) => buildReverseGeocodeUrl(config.reverseEndpoint, lon, lat, {
8558
- email: options.email,
8559
- zoom: options.zoom
8560
- }),
8561
- parseReverse: (data) => nominatimReverseResultToDisplay(data && typeof data === "object" ? data : null)
8562
- };
8563
- /** Read a GeoJSON FeatureCollection's features array, or []. */
8564
- function geojsonFeatures(data) {
8565
- if (data && typeof data === "object" && Array.isArray(data.features)) return data.features.filter((f) => !!f && typeof f === "object");
8566
- return [];
8567
- }
8568
- /** Pull `[lon, lat]` from a GeoJSON Point geometry, or null. */
8569
- function pointCoords(feature) {
8570
- const coords = feature.geometry?.coordinates;
8571
- if (Array.isArray(coords) && coords.length >= 2) {
8572
- const lon = Number(coords[0]);
8573
- const lat = Number(coords[1]);
8574
- if (Number.isFinite(lon) && Number.isFinite(lat)) return [lon, lat];
8575
- }
8576
- return null;
8577
- }
8578
- const peliasProvider = {
8579
- id: "pelias",
8580
- label: "Pelias",
8581
- forward: true,
8582
- reverse: true,
8583
- requiresApiKey: false,
8584
- acceptsApiKey: true,
8585
- defaultForwardEndpoint: "https://api.geocode.earth/v1/search",
8586
- defaultReverseEndpoint: "https://api.geocode.earth/v1/reverse",
8587
- buildForwardUrl: (config, query, options) => {
8588
- const url = new URL(config.forwardEndpoint);
8589
- url.searchParams.set("text", query);
8590
- if (options.limit) url.searchParams.set("size", String(options.limit));
8591
- if (config.apiKey) url.searchParams.set("api_key", config.apiKey);
8592
- return url.toString();
8593
- },
8594
- parseForward: (data) => geojsonFeatures(data).map((feature) => {
8595
- const coords = pointCoords(feature);
8596
- if (!coords) return null;
8597
- const props = feature.properties ?? {};
8598
- return {
8599
- lon: coords[0],
8600
- lat: coords[1],
8601
- displayName: readString(props, "label") ?? "",
8602
- score: coerceScore(props.confidence)
8603
- };
8604
- }).filter((m) => m !== null),
8605
- buildReverseUrl: (config, lon, lat) => {
8606
- const url = new URL(config.reverseEndpoint);
8607
- url.searchParams.set("point.lat", String(lat));
8608
- url.searchParams.set("point.lon", String(lon));
8609
- if (config.apiKey) url.searchParams.set("api_key", config.apiKey);
8610
- return url.toString();
8611
- },
8612
- parseReverse: (data) => {
8613
- const feature = geojsonFeatures(data)[0];
8614
- if (!feature) return null;
8615
- const props = feature.properties ?? {};
8616
- const displayName = readString(props, "label")?.trim();
8617
- return displayName ? {
8618
- displayName,
8619
- parts: stringifyParts(props)
8620
- } : null;
8621
- }
8622
- };
8623
- const arcgisProvider = {
8624
- id: "arcgis",
8625
- label: "ArcGIS World Geocoder",
8626
- forward: true,
8627
- reverse: true,
8628
- requiresApiKey: true,
8629
- acceptsApiKey: true,
8630
- defaultForwardEndpoint: "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates",
8631
- defaultReverseEndpoint: "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode",
8632
- buildForwardUrl: (config, query, options) => {
8633
- const url = new URL(config.forwardEndpoint);
8634
- url.searchParams.set("SingleLine", query);
8635
- url.searchParams.set("f", "json");
8636
- url.searchParams.set("outFields", "Match_addr");
8637
- if (options.limit) url.searchParams.set("maxLocations", String(options.limit));
8638
- if (config.apiKey) url.searchParams.set("token", config.apiKey);
8639
- return url.toString();
8640
- },
8641
- parseForward: (data) => {
8642
- return (data && typeof data === "object" && Array.isArray(data.candidates) ? data.candidates : []).map((candidate) => {
8643
- const location = candidate.location;
8644
- const lon = Number(location?.x);
8645
- const lat = Number(location?.y);
8646
- if (!Number.isFinite(lon) || !Number.isFinite(lat)) return null;
8647
- return {
8648
- lon,
8649
- lat,
8650
- displayName: readString(candidate, "address") ?? "",
8651
- score: coerceScore(candidate.score)
8652
- };
8653
- }).filter((m) => m !== null);
8654
- },
8655
- buildReverseUrl: (config, lon, lat) => {
8656
- const url = new URL(config.reverseEndpoint);
8657
- url.searchParams.set("location", `${lon},${lat}`);
8658
- url.searchParams.set("f", "json");
8659
- if (config.apiKey) url.searchParams.set("token", config.apiKey);
8660
- return url.toString();
8661
- },
8662
- parseReverse: (data) => {
8663
- const address = data && typeof data === "object" ? data.address : void 0;
8664
- if (!address || typeof address !== "object") return null;
8665
- const displayName = (readString(address, "LongLabel") ?? readString(address, "Match_addr"))?.trim();
8666
- return displayName ? {
8667
- displayName,
8668
- parts: stringifyParts(address)
8669
- } : null;
8670
- }
8671
- };
8672
- const mapboxProvider = {
8673
- id: "mapbox",
8674
- label: "Mapbox",
8675
- forward: true,
8676
- reverse: true,
8677
- requiresApiKey: true,
8678
- acceptsApiKey: true,
8679
- defaultForwardEndpoint: "https://api.mapbox.com/geocoding/v5/mapbox.places",
8680
- defaultReverseEndpoint: "https://api.mapbox.com/geocoding/v5/mapbox.places",
8681
- buildForwardUrl: (config, query, options) => {
8682
- const base = config.forwardEndpoint.replace(/\/+$/, "");
8683
- const url = new URL(`${base}/${encodeURIComponent(query)}.json`);
8684
- if (options.limit) url.searchParams.set("limit", String(options.limit));
8685
- if (config.apiKey) url.searchParams.set("access_token", config.apiKey);
8686
- return url.toString();
8687
- },
8688
- parseForward: (data) => geojsonFeatures(data).map((feature) => {
8689
- const center = feature.center;
8690
- let coords = null;
8691
- if (Array.isArray(center) && center.length >= 2) {
8692
- const lon = Number(center[0]);
8693
- const lat = Number(center[1]);
8694
- if (Number.isFinite(lon) && Number.isFinite(lat)) coords = [lon, lat];
8695
- }
8696
- coords ??= pointCoords(feature);
8697
- if (!coords) return null;
8698
- return {
8699
- lon: coords[0],
8700
- lat: coords[1],
8701
- displayName: readString(feature, "place_name") ?? "",
8702
- score: coerceScore(feature.relevance)
8703
- };
8704
- }).filter((m) => m !== null),
8705
- buildReverseUrl: (config, lon, lat) => {
8706
- const base = config.reverseEndpoint.replace(/\/+$/, "");
8707
- const url = new URL(`${base}/${lon},${lat}.json`);
8708
- if (config.apiKey) url.searchParams.set("access_token", config.apiKey);
8709
- return url.toString();
8710
- },
8711
- parseReverse: (data) => {
8712
- const feature = geojsonFeatures(data)[0];
8713
- if (!feature) return null;
8714
- const displayName = readString(feature, "place_name")?.trim();
8715
- return displayName ? {
8716
- displayName,
8717
- parts: stringifyParts(feature.properties ?? {})
8718
- } : null;
8719
- }
8720
- };
8721
- /**
8722
- * Detect a Google Geocoding API error embedded in an HTTP 200 body. Google v3
8723
- * returns REQUEST_DENIED / OVER_QUERY_LIMIT / INVALID_REQUEST and friends with
8724
- * a 200 status and the failure only in the `status` field, so `response.ok`
8725
- * alone would silently turn those into empty results. Returns null for OK and
8726
- * ZERO_RESULTS (a successful empty match).
8727
- */
8728
- function googleErrorMessage(data) {
8729
- const status = readString(data, "status");
8730
- if (!status || status === "OK" || status === "ZERO_RESULTS") return null;
8731
- const detail = readString(data, "error_message");
8732
- return detail ? `Google geocoder error: ${status} - ${detail}` : `Google geocoder error: ${status}`;
8733
- }
8734
- const googleProvider = {
8735
- id: "google",
8736
- label: "Google",
8737
- forward: true,
8738
- reverse: true,
8739
- requiresApiKey: true,
8740
- acceptsApiKey: true,
8741
- browserCorsRestricted: true,
8742
- defaultForwardEndpoint: "https://maps.googleapis.com/maps/api/geocode/json",
8743
- defaultReverseEndpoint: "https://maps.googleapis.com/maps/api/geocode/json",
8744
- buildForwardUrl: (config, query) => {
8745
- const url = new URL(config.forwardEndpoint);
8746
- url.searchParams.set("address", query);
8747
- if (config.apiKey) url.searchParams.set("key", config.apiKey);
8748
- return url.toString();
8749
- },
8750
- parseForward: (data) => {
8751
- const error = googleErrorMessage(data);
8752
- if (error) throw new Error(error);
8753
- return parseGoogleResults(data);
8754
- },
8755
- buildReverseUrl: (config, lon, lat) => {
8756
- const url = new URL(config.reverseEndpoint);
8757
- url.searchParams.set("latlng", `${lat},${lon}`);
8758
- if (config.apiKey) url.searchParams.set("key", config.apiKey);
8759
- return url.toString();
8760
- },
8761
- parseReverse: (data) => {
8762
- const error = googleErrorMessage(data);
8763
- if (error) throw new Error(error);
8764
- const displayName = readString((data && typeof data === "object" && Array.isArray(data.results) ? data.results : [])[0], "formatted_address")?.trim();
8765
- return displayName ? {
8766
- displayName,
8767
- parts: {}
8768
- } : null;
8769
- }
8770
- };
8771
- /** Map a Google Geocoding API `results` array onto normalized matches. */
8772
- function parseGoogleResults(data) {
8773
- return (data && typeof data === "object" && Array.isArray(data.results) ? data.results : []).map((result) => {
8774
- const location = result.geometry?.location;
8775
- const lat = Number(location?.lat);
8776
- const lon = Number(location?.lng);
8777
- if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
8778
- return {
8779
- lat,
8780
- lon,
8781
- displayName: readString(result, "formatted_address") ?? "",
8782
- score: null
8783
- };
8784
- }).filter((m) => m !== null);
8785
- }
8786
- /**
8787
- * Read a CartoCiudad `lat`/`lng` field as a finite number, or null when the
8788
- * field is absent, blank, or not numeric (`"NaN"` and `"invalid"` included).
8789
- * Guarding the raw value matters because `Number(null)` is 0, which would turn
8790
- * a coordinate-less response into a match in the Gulf of Guinea.
8791
- */
8792
- function cartociudadCoordinate(value) {
8793
- if (typeof value === "number") return Number.isFinite(value) ? value : null;
8794
- if (typeof value !== "string" || !value.trim()) return null;
8795
- const parsed = Number(value);
8796
- return Number.isFinite(parsed) ? parsed : null;
8797
- }
8798
- /** Validated WGS84 position for a result, or null when it carries none. */
8799
- function cartociudadCoords(r) {
8800
- const lat = cartociudadCoordinate(r.lat);
8801
- const lon = cartociudadCoordinate(r.lng);
8802
- if (lat === null || lon === null) return null;
8803
- if (lat < -90 || lat > 90 || lon < -180 || lon > 180) return null;
8804
- return {
8805
- lat,
8806
- lon
8807
- };
8808
- }
8809
- /** Default endpoints for the public CartoCiudad REST API (no auth required). */
8810
- const CARTOCIUDAD_FORWARD_ENDPOINT = "https://www.cartociudad.es/geocoder/api/geocoder/find";
8811
- const CARTOCIUDAD_REVERSE_ENDPOINT = "https://www.cartociudad.es/geocoder/api/geocoder/reverseGeocode";
8812
- /**
8813
- * Build a human-readable display name from CartoCiudad address fields.
8814
- * Format: "Tip Via Address Num, CP Poblacion (Province)"
8815
- */
8816
- function cartociudadDisplayName(r) {
8817
- const parts = [];
8818
- const streetParts = [r.tip_via, r.address].filter((s) => s && String(s).trim()).map((s) => String(s).trim());
8819
- if (streetParts.length) {
8820
- let street = streetParts.join(" ");
8821
- const portalNumber = r.portalNumber === void 0 || r.portalNumber === null ? "" : String(r.portalNumber).trim();
8822
- if (portalNumber) street += ` ${portalNumber}`;
8823
- parts.push(street);
8824
- }
8825
- const localityName = [r.poblacion, r.muni].find((s) => s && String(s).trim());
8826
- const locality = [r.postalCode, localityName].filter((s) => s && String(s).trim()).map((s) => String(s).trim()).join(" ");
8827
- if (locality) parts.push(locality);
8828
- const province = r.province ? String(r.province).trim() : "";
8829
- if (province) parts.push(`(${province})`);
8830
- return parts.join(", ") || r.address || r.poblacion || r.muni || "";
8831
- }
8832
- /** All selectable geocoding providers, Nominatim first (the default). */
8833
- const GEOCODING_PROVIDERS = [
8834
- nominatimProvider,
8835
- peliasProvider,
8836
- arcgisProvider,
8837
- mapboxProvider,
8838
- googleProvider,
8839
- {
8840
- id: "cartociudad",
8841
- label: "CartoCiudad (IGN España)",
8842
- forward: true,
8843
- reverse: true,
8844
- requiresApiKey: false,
8845
- acceptsApiKey: false,
8846
- defaultForwardEndpoint: CARTOCIUDAD_FORWARD_ENDPOINT,
8847
- defaultReverseEndpoint: CARTOCIUDAD_REVERSE_ENDPOINT,
8848
- buildForwardUrl: (config, query) => {
8849
- const url = new URL(config.forwardEndpoint);
8850
- url.searchParams.set("q", query);
8851
- return url.toString();
8852
- },
8853
- parseForward: (data) => {
8854
- if (!data || typeof data !== "object" || Array.isArray(data)) return [];
8855
- const r = data;
8856
- const coords = cartociudadCoords(r);
8857
- if (!coords) return [];
8858
- const displayName = cartociudadDisplayName(r).trim();
8859
- if (!displayName) return [];
8860
- return [{
8861
- lat: coords.lat,
8862
- lon: coords.lon,
8863
- displayName,
8864
- score: null
8865
- }];
8866
- },
8867
- buildReverseUrl: (config, lon, lat) => {
8868
- const url = new URL(config.reverseEndpoint);
8869
- url.searchParams.set("lat", String(lat));
8870
- url.searchParams.set("lon", String(lon));
8871
- return url.toString();
8872
- },
8873
- parseReverse: (data) => {
8874
- if (!data || typeof data !== "object" || Array.isArray(data)) return null;
8875
- const r = data;
8876
- if (!cartociudadCoords(r)) return null;
8877
- const displayName = cartociudadDisplayName(r).trim();
8878
- if (!displayName) return null;
8879
- const parts = {};
8880
- for (const [key, value] of Object.entries({
8881
- type: r.type,
8882
- tip_via: r.tip_via,
8883
- address: r.address,
8884
- portalNumber: r.portalNumber,
8885
- postalCode: r.postalCode,
8886
- poblacion: r.poblacion,
8887
- muni: r.muni,
8888
- province: r.province,
8889
- comunidadAutonoma: r.comunidadAutonoma,
8890
- refCatastral: r.refCatastral
8891
- })) if (value !== void 0 && value !== null && value !== "") parts[key] = String(value);
8892
- return {
8893
- displayName,
8894
- parts
8895
- };
8896
- }
8897
- }
8898
- ];
8899
- const PROVIDERS_BY_ID = new Map(GEOCODING_PROVIDERS.map((provider) => [provider.id, provider]));
8900
- /** Coerce an arbitrary string to a known provider id, defaulting to Nominatim. */
8901
- function normalizeGeocodingProviderId(value) {
8902
- const id = value?.trim();
8903
- return id && PROVIDERS_BY_ID.has(id) ? id : DEFAULT_GEOCODING_PROVIDER_ID;
8904
- }
8905
- /** Look up a provider by id, falling back to Nominatim for unknown ids. */
8906
- function getGeocodingProvider(id) {
8907
- return PROVIDERS_BY_ID.get(normalizeGeocodingProviderId(id)) ?? nominatimProvider;
8908
- }
8909
- /**
8910
- * Resolve the geocoder configuration from runtime env, falling back to the
8911
- * selected provider's default endpoints. `VITE_GEOCODER_PROVIDER` selects the
8912
- * backend; `VITE_GEOCODER_ENDPOINT`/`VITE_GEOCODER_REVERSE_ENDPOINT` override
8913
- * the endpoints; `VITE_GEOCODER_EMAIL` supplies the contact email; and
8914
- * `VITE_GEOCODER_API_KEY` supplies the API key for keyed providers.
8915
- */
8916
- function getGeocoderConfig() {
8917
- const env = getRuntimeEnvironment();
8918
- const provider = getGeocodingProvider(env.VITE_GEOCODER_PROVIDER);
8919
- return {
8920
- providerId: provider.id,
8921
- forwardEndpoint: env.VITE_GEOCODER_ENDPOINT?.trim() || provider.defaultForwardEndpoint,
8922
- reverseEndpoint: env.VITE_GEOCODER_REVERSE_ENDPOINT?.trim() || provider.defaultReverseEndpoint,
8923
- email: env.VITE_GEOCODER_EMAIL?.trim() || void 0,
8924
- apiKey: env.VITE_GEOCODER_API_KEY?.trim() || void 0
8925
- };
8926
- }
8927
- /**
8928
- * Build a {@link GeocoderConfig} from structured project preferences. Used by
8929
- * the geocode dialog so a per-run provider choice resolves the right endpoints
8930
- * and API key without round-tripping through runtime env.
8931
- */
8932
- function resolveGeocoderConfig(input) {
8933
- const provider = getGeocodingProvider(input.providerId);
8934
- return {
8935
- providerId: provider.id,
8936
- forwardEndpoint: input.forwardEndpoint?.trim() || provider.defaultForwardEndpoint,
8937
- reverseEndpoint: input.reverseEndpoint?.trim() || provider.defaultReverseEndpoint,
8938
- email: input.email?.trim() || void 0,
8939
- apiKey: input.apiKeys?.[provider.id]?.trim() || void 0
8940
- };
8941
- }
8942
- /** Hostname of `endpoint`, or null when it does not parse as a URL. */
8943
- function geocoderHostname(endpoint) {
8944
- try {
8945
- return new URL(endpoint).hostname;
8946
- } catch {
8947
- return null;
8948
- }
8949
- }
8950
- /** Whether `hostname` is the public CartoCiudad service or a subdomain of it. */
8951
- function isCartociudadHost(hostname) {
8952
- if (hostname === null) return false;
8953
- return hostname === "cartociudad.es" || hostname.endsWith(`.cartociudad.es`);
8954
- }
8955
- /**
8956
- * Whether requests to `endpoint` must be throttled AND row-capped. True for the
8957
- * public Nominatim host (its usage policy applies) and, defensively, for any
8958
- * endpoint that does not parse as a URL. Every other host (a self-hosted
8959
- * Nominatim or a keyed provider) returns false.
8960
- *
8961
- * Note this is the *Nominatim policy* gate, not "is this host paced at all" —
8962
- * see {@link geocoderMinIntervalMs}, which paces CartoCiudad more gently
8963
- * without capping its row count.
8964
- */
8965
- function shouldThrottle(endpoint) {
8966
- const hostname = geocoderHostname(endpoint);
8967
- return hostname === null || hostname === "nominatim.openstreetmap.org";
8968
- }
8969
- /**
8970
- * The row cap to apply for `endpoint`: a finite cap for the public Nominatim
8971
- * host, else Infinity. CartoCiudad is uncapped — IGN publishes no row limit and
8972
- * its own bulk address geocoder processes up to 60,000 records per run, so a
8973
- * cap here would be an invented constraint. It is still paced; see
8974
- * {@link geocoderMinIntervalMs}.
8975
- */
8976
- function rowCap(endpoint) {
8977
- return shouldThrottle(endpoint) ? PUBLIC_GEOCODE_ROW_CAP : Number.POSITIVE_INFINITY;
8978
- }
8979
- /**
8980
- * Minimum spacing (ms) between requests for `endpoint`. Gated on the hostname,
8981
- * not the selected provider, so pointing any provider at the public Nominatim
8982
- * host still honors its 1 req/sec policy.
8983
- *
8984
- * CartoCiudad gets a gentler pace of its own. IGN documents no rate limit for
8985
- * the single-lookup `find`/`reverseGeocode` endpoints either way, so the honest
8986
- * position is that unbounded bursting is unverified rather than sanctioned:
8987
- * a large CSV run against a free public government service with zero spacing
8988
- * risks getting the caller's IP throttled and degrading the service for
8989
- * everyone. Nominatim's 1 req/sec is that provider's published policy and is
8990
- * not borrowed here. Keyed providers and self-hosted endpoints are paced by
8991
- * their own quotas, not by us.
8992
- */
8993
- function geocoderMinIntervalMs(endpoint) {
8994
- if (shouldThrottle(endpoint)) return NOMINATIM_MIN_INTERVAL_MS;
8995
- return isCartociudadHost(geocoderHostname(endpoint)) ? 200 : 0;
8996
- }
8997
- /** Hosted Pelias service that rejects keyless requests. */
8998
- const GEOCODE_EARTH_HOST_SUFFIX = "geocode.earth";
8999
- /**
9000
- * Whether `config` cannot geocode without an API key. True for the keyed
9001
- * providers (ArcGIS, Mapbox, Google) and for Pelias only when it targets the
9002
- * hosted geocode.earth endpoint (a self-hosted Pelias needs no key). Drives the
9003
- * batch dialog's "missing key" warning and disabled run.
9004
- */
9005
- function geocoderNeedsApiKey(config) {
9006
- const provider = getGeocodingProvider(config.providerId);
9007
- if (provider.requiresApiKey) return true;
9008
- if (provider.id === "pelias") try {
9009
- const host = new URL(config.forwardEndpoint).hostname;
9010
- return host === GEOCODE_EARTH_HOST_SUFFIX || host.endsWith(`.${GEOCODE_EARTH_HOST_SUFFIX}`);
9011
- } catch {
9012
- return false;
9013
- }
9014
- return false;
9015
- }
9016
- /**
9017
- * Milliseconds to wait before starting the next request so consecutive
9018
- * requests are spaced at least `intervalMs` apart, measured from the previous
9019
- * request's start time (not its completion) so a slow network does not double
9020
- * the wait. Returns 0 for the first request or when enough time has elapsed.
9021
- */
9022
- function nextDelayMs(lastStartedAt, now, intervalMs) {
9023
- if (lastStartedAt === null) return 0;
9024
- return Math.max(0, intervalMs - (now - lastStartedAt));
9025
- }
9026
- /**
9027
- * Build geocoding requests from parsed CSV rows. The address for each row is
9028
- * the selected columns trimmed and joined with ", " (so multi-part addresses
9029
- * like street/city/country can be combined). Rows whose composed address is
9030
- * empty are skipped.
9031
- */
9032
- function csvRowsToGeocodeRequests(rows, addressColumns) {
9033
- const requests = [];
9034
- rows.forEach((row, index) => {
9035
- const address = addressColumns.map((column) => (row[column] ?? "").trim()).filter(Boolean).join(", ").trim();
9036
- if (!address) return;
9037
- requests.push({
9038
- index,
9039
- address,
9040
- row
9041
- });
9042
- });
9043
- return requests;
9044
- }
9045
- /**
9046
- * The fetch implementation used by {@link geocodeForward}/{@link geocodeReverse}.
9047
- * Null means "use the global browser `fetch`" (the default for the web and
9048
- * embedded builds).
9049
- */
9050
- let geocodingFetch = null;
9051
- /**
9052
- * Override the fetch used for geocoding requests, or pass null to restore the
9053
- * global browser `fetch`.
9054
- *
9055
- * The desktop shell sets this to Tauri's native HTTP fetch so geocoding requests
9056
- * are made from the Rust side, which:
9057
- * - bypasses the WebView's CORS enforcement. Public Nominatim's CDN
9058
- * intermittently omits the `Access-Control-Allow-Origin` header on cached
9059
- * responses, which the browser then rejects — surfacing to the user as
9060
- * "Search failed. Try again." (the symptom that failed Microsoft Store
9061
- * certification); and
9062
- * - can send a `User-Agent` identifying the app, as Nominatim's usage policy
9063
- * requires (browser fetch cannot set that header).
9064
- *
9065
- * Safe to call multiple times; only the most recent implementation is used.
9066
- */
9067
- function setGeocodingFetch(fetchImpl) {
9068
- geocodingFetch = fetchImpl;
9069
- }
9070
- /** The active geocoding fetch: the injected override, else the global fetch. */
9071
- function geocodeFetch() {
9072
- return geocodingFetch ?? fetch;
9073
- }
9074
- /**
9075
- * Read a geocoder response body as JSON, treating an empty one as "no results".
9076
- *
9077
- * CartoCiudad answers an unmatched query with HTTP 204 and an empty body rather
9078
- * than a JSON null (verified against the live `find` and `reverseGeocode`
9079
- * endpoints). `response.ok` is true for 204, so parsing unconditionally would
9080
- * throw a JSON syntax error and surface a nonsense address as "Search failed"
9081
- * instead of "no match". Returning undefined lets each provider's parser report
9082
- * no results the way it already does for a null body.
9083
- */
9084
- async function readGeocodeJson(response) {
9085
- if (response.status === 204) return void 0;
9086
- const text = await response.text();
9087
- if (!text.trim()) return void 0;
9088
- return JSON.parse(text);
9089
- }
9090
- /**
9091
- * Normalize a longitude into [-180, 180], or null when it is not finite.
9092
- *
9093
- * MapLibre reports `lngLat.lng` unwrapped once the map is zoomed out far enough
9094
- * to show repeated world copies, so a click on a non-primary copy arrives as
9095
- * e.g. 210 rather than -150. Rejecting those outright would silently drop a
9096
- * legitimate reverse-geocode click for every provider, so wrap instead.
9097
- *
9098
- * An in-range longitude returns untouched: the modulo round-trip is not exact
9099
- * in floating point (-3.7 comes back as -3.7000000000000455), and the common
9100
- * case should not be perturbed to handle the rare one.
9101
- */
9102
- function wrapLongitude(lon) {
9103
- if (!Number.isFinite(lon)) return null;
9104
- if (lon >= -180 && lon <= 180) return lon;
9105
- return ((lon + 180) % 360 + 360) % 360 - 180;
9106
- }
9107
- /**
9108
- * Forward-geocode a single query through the configured provider, returning
9109
- * normalized matches.
9110
- */
9111
- async function geocodeForward(query, options = {}) {
9112
- const config = options.config ?? getGeocoderConfig();
9113
- const provider = getGeocodingProvider(config.providerId);
9114
- const url = provider.buildForwardUrl(config, query, {
9115
- email: config.email,
9116
- limit: options.limit
9117
- });
9118
- const response = await geocodeFetch()(url, {
9119
- signal: options.signal,
9120
- headers: { Accept: "application/json" }
9121
- });
9122
- if (!response.ok) throw new Error(`Geocoder returned HTTP ${response.status}`);
9123
- const data = await readGeocodeJson(response);
9124
- return provider.parseForward(data);
9125
- }
9126
- /**
9127
- * Reverse-geocode a single point ([lon, lat]) through the configured provider,
9128
- * returning a normalized display result or null when no address was found.
9129
- */
9130
- async function geocodeReverse(lon, lat, options = {}) {
9131
- const wrappedLon = wrapLongitude(lon);
9132
- if (wrappedLon === null || !Number.isFinite(lat) || lat < -90 || lat > 90) return null;
9133
- const config = options.config ?? getGeocoderConfig();
9134
- const provider = getGeocodingProvider(config.providerId);
9135
- const url = provider.buildReverseUrl(config, wrappedLon, lat, {
9136
- email: config.email,
9137
- zoom: options.zoom
9138
- });
9139
- const response = await geocodeFetch()(url, {
9140
- signal: options.signal,
9141
- headers: { Accept: "application/json" }
9142
- });
9143
- if (!response.ok) throw new Error(`Geocoder returned HTTP ${response.status}`);
9144
- const data = await readGeocodeJson(response);
9145
- return provider.parseReverse(data);
9146
- }
9147
- //#endregion
9148
- //#region src/platform.ts
8185
+ // src/platform.ts
9149
8186
  /**
9150
8187
  * Platform detection shared across the workspace. Carries no React/MapLibre
9151
8188
  * dependency so callers stay unit-testable.
@@ -9171,8 +8208,7 @@ async function geocodeReverse(lon, lat, options = {}) {
9171
8208
  function isIpadDesktopUserAgent(userAgent, maxTouchPoints) {
9172
8209
  return /Macintosh/.test(userAgent) && (maxTouchPoints ?? 0) > 1;
9173
8210
  }
9174
- //#endregion
9175
- //#region src/three-d-tiles.ts
8211
+ // src/three-d-tiles.ts
9176
8212
  /** Google Photorealistic 3D Tiles root host + path. */
9177
8213
  const GOOGLE_PHOTOREALISTIC_TILES_HOST = "tile.googleapis.com";
9178
8214
  const GOOGLE_PHOTOREALISTIC_TILES_PATH = "/v1/3dtiles/root.json";
@@ -9239,8 +8275,7 @@ function persistedThreeDTilesRequestHeaders(url, headers) {
9239
8275
  if (!isGooglePhotorealisticTilesetUrl(url)) return headers;
9240
8276
  return nonEmptyRecord(stripGoogleMapsApiKeyHeader(headers));
9241
8277
  }
9242
- //#endregion
9243
- //#region src/project-credential-refs.ts
8278
+ // src/project-credential-refs.ts
9244
8279
  /**
9245
8280
  * Credentials a project refers to by name rather than carrying inline.
9246
8281
  *
@@ -9417,8 +8452,7 @@ function resolveProjectHeaderReferences(headers) {
9417
8452
  }
9418
8453
  return resolveHeaderReferences(headers, values);
9419
8454
  }
9420
- //#endregion
9421
- //#region src/visibility.ts
8455
+ // src/visibility.ts
9422
8456
  /**
9423
8457
  * Returns a new FeatureCollection with properties marked as "excluded" removed.
9424
8458
  */
@@ -9450,8 +8484,9 @@ function excludeHiddenFieldsFromProject(project) {
9450
8484
  const strippedGeojson = excludeHiddenFieldsFromGeojson(layer.geojson, layer.fieldVisibility);
9451
8485
  if (strippedGeojson !== layer.geojson) {
9452
8486
  changed = true;
8487
+ const { geoarrow: _staleGeoarrow, ...rest } = updatedLayer;
9453
8488
  updatedLayer = {
9454
- ...updatedLayer,
8489
+ ...rest,
9455
8490
  geojson: strippedGeojson
9456
8491
  };
9457
8492
  }
@@ -9476,8 +8511,7 @@ function excludeHiddenFieldsFromProject(project) {
9476
8511
  layers
9477
8512
  } : project;
9478
8513
  }
9479
- //#endregion
9480
- //#region src/editor-tracking.ts
8514
+ // src/editor-tracking.ts
9481
8515
  /** Default field names for maintained editor tracking columns. */
9482
8516
  const DEFAULT_EDITOR_TRACKING_CONFIG = {
9483
8517
  enabled: false,
@@ -9647,8 +8681,7 @@ function stampFeatureCollectionEditorTracking(collection, action, options) {
9647
8681
  features: collection.features.map((feat) => stampFeatureEditorTracking(feat, action, opts))
9648
8682
  };
9649
8683
  }
9650
- //#endregion
9651
- //#region src/editor-identity.ts
8684
+ // src/editor-identity.ts
9652
8685
  /**
9653
8686
  * Runtime resolution of "who is editing", for the editor tracking stamps.
9654
8687
  *
@@ -9696,8 +8729,7 @@ function currentEditorIdentity() {
9696
8729
  const { isActive, selfName } = useAppStore.getState().collaboration;
9697
8730
  return pickEditorIdentity(isActive ? selfName : void 0, readStoredAuthorName());
9698
8731
  }
9699
- //#endregion
9700
- //#region src/datum-shift.ts
8732
+ // src/datum-shift.ts
9701
8733
  /** Inclusive ranges of codes, as a list. */
9702
8734
  const range = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => from + i);
9703
8735
  const SHIFTS = [
@@ -9847,8 +8879,7 @@ function withGeoKeysDatumShift(definition, geoKeys) {
9847
8879
  function installCogTilerDatumShift(module) {
9848
8880
  module.setSourceCrsResolver?.((def, geoKeys) => def.trimStart().startsWith("+") ? withGeoKeysDatumShift(def, geoKeys) : def);
9849
8881
  }
9850
- //#endregion
9851
- //#region src/cog-jpeg-tables.ts
8882
+ // src/cog-jpeg-tables.ts
9852
8883
  const DEFAULT_COEFFICIENTS = [
9853
8884
  .299,
9854
8885
  .587,
@@ -9978,7 +9009,6 @@ function withJpegTablesPatch(module) {
9978
9009
  openCog: (async (input) => patchJpegCogSource(await module.openCog(input)))
9979
9010
  };
9980
9011
  }
9981
- //#endregion
9982
- export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, ARCGIS_PROJECTION_PRESETS, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CESIUM_BASEMAPS, CESIUM_BING_AERIAL_ASSET_ID, CESIUM_GOOGLE_PHOTOREALISTIC_ASSET_ID, CESIUM_ION_QUICK_PICKS, CESIUM_ION_SOURCE_KIND, CESIUM_KML_SOURCE_KIND, CESIUM_OSM_BUILDINGS_ASSET_ID, CHINA_BASEMAPS, CZML_QUICK_PICKS, CZML_SOURCE_KIND, DEFAULT_BASEMAP, DEFAULT_BLEND_MODE, DEFAULT_COLLABORATION_STATE, DEFAULT_DASHBOARD_COLUMNS, DEFAULT_EDITOR_IDENTITY, DEFAULT_EDITOR_TRACKING_CONFIG, DEFAULT_ELLIPSOID_ID, DEFAULT_FORWARD_GEOCODE_ENDPOINT, DEFAULT_GEOCODING_PROVIDER_ID, DEFAULT_LAYER_GROUP_OPACITY, DEFAULT_LAYER_STYLE, DEFAULT_LEGEND_CONFIG, DEFAULT_MAP_GRID_LAYOUT, DEFAULT_PRIMARY_RENDERER, DEFAULT_PRINT_LAYOUT, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_PREFERENCES, DEFAULT_REVERSE_GEOCODE_ENDPOINT, DEFAULT_ROUTING_ENDPOINT, DEFAULT_STORY_MAP, DEPLOYMENT_CAPABILITIES, DUCKDB_VECTOR_FEATURE_WARN_COUNT, DUCKDB_VECTOR_ROUTE_BYTES, EARTH_MEAN_RADIUS_METERS, EDITOR_IDENTITY_STORAGE_KEY, ELEVATION_REQUEST_TIMEOUT_MS, ELLIPSOIDS, EXPRESSION_FUNCTION_CATEGORIES, ElevationFetchError, FEET_PER_METER, FEET_PER_MILE, GEOCODE_DISPLAY_NAME_KEY, GEOCODE_LAT_KEY, GEOCODE_LON_KEY, GEOCODE_PROVIDER_KEY, GEOCODE_SCORE_KEY, GEOCODE_STATUS_KEY, GEOCODING_PROVIDERS, GOOGLE_MAPS_API_KEY_HEADER, IDENTIFY_ALL_LAYERS_ID, LABEL_NUMBER_LOCALES, LABEL_NUMBER_SAMPLE, LARGE_VECTOR_FEATURE_THRESHOLD, LAYERS_FILE_TYPE, LAYERS_FILE_VERSION, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_STYLES_FILE_TYPE, LAYER_STYLES_FILE_VERSION, LAYER_TYPES, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYERS_FILE_BYTES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_PROJECT_BOOKMARKS, MAX_REDACT_DEPTH, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, NO_LAYERS, OPENFREEMAP_BASEMAPS, PHOTO_FULL_PROPERTY, PHOTO_PROPERTY, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANET_SWITCHER_OPTIONS, POINTER_ELEVATION_CACHE_LIMIT, POINTER_ELEVATION_DEBOUNCE_MS, POPUP_IMAGE_HEIGHT_RANGE, POPUP_MAX_WIDTH_RANGE, PROJECT_CREDENTIAL_FIELDS, PROJECT_VERSION, PROTOMAPS_BASEMAPS, PUBLIC_GEOCODE_ROW_CAP, PUBLISHABLE_PLUGIN_SETTINGS, RAMP_STYLE_KEYS, RASTER_APPEARANCE_STATE_KEYS, REGIONAL_BASEMAPS, REGIONAL_BASEMAP_GROUPS, REGIONAL_BASEMAP_SENTINEL_PREFIX, ROLE_PRIVILEGES, RoutingRequestError, S3CorsError, S3_MAX_PRESIGN_SECONDS, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STAC_PROCESSING_EXTENSION, STAC_VERSION, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeLayerFilterExpression, activeQuickFilters, addLayersToProject, allowsCredentialHeaders, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, applyStoryLayerOpacity, attributeFormFieldLabel, attributeLinkUrl, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerStacItem, buildLayerTree, buildMatrixRequest, buildReverseGeocodeUrl, buildRouteRequest, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, cesiumIonAssetId, cesiumIonAssetKind, cesiumKmlSource, changedPreferenceCredentials, circleRadiusValue, clampLabelDecimals, clampLatitude, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileLayerFilters, compileQuickFilter, compileQuickFilters, controlRendersLayer, convertTiffYCbCrToRgb, copyableLayerStyleKind, createCesiumIonLayer, createCesiumKmlLayer, createCzmlLayer, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectLayerSerializationCache, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLayerEvaluator, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, czmlSource, darkenHex, datumShiftFor, decodePolyline, decodePolylineDetailed, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, diffFeatureCollections, diffProjects, documentLocale, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, encodeS3Key, ensureEditorTrackingFields, environmentVariableAccount, evaluateMapExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, explainS3ReadError, extractCopiedLayerStyle, extractLayerStyleEntries, extractLayersFileContent, extractStyleLibraryStyle, extrusionColorValue, extrusionHeightValue, featureSelectionId, fetchElevations, findLayerStyleEntryIndex, formatCameraAltitude, formatExpressionPreviewValue, formatLabelNumber, formatLabelNumberSample, formatPixelValue, formatPopupValue, formatRoundNum, formatS3Uri, generatorCircleRadiusValue, generatorSizeRange, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocoderMinIntervalMs, geocoderNeedsApiKey, geocodingApiKeyAccount, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getArcgisApiKey, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getS3UrlSigner, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, hasActiveLayerFilter, hasActiveQuickFilter, hasAppPrivilege, hasHeaderReferences, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, heatmapRampColors, horizontalBbox, hydrateProjectLayer, identifyAllIncludes, identifyStateWithoutLayers, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, installCogTilerDatumShift, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbbreviatedJpegCompression, isAbsoluteFilesystemPath, isAdoptedVectorAwaitingFeatures, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isCesiumIonLayer, isCesiumKmlLayer, isCesiumOnlyLayer, isCredentialFieldName, isCredentialUrlParam, isCredentialedS3Url, isCzmlLayer, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGooglePhotorealisticTilesetUrl, isHeaderReferenceOnly, isHexColor, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isMaintainedEditorTrackingField, isPopupClickEnabled, isPopupHoverEnabled, isRasterMarkerSource, isReferencedLayer, isRegionalBasemapSentinel, isS3Uri, isSafePopupUrl, isSecretEnvironmentVariable, isSessionOnlyLayer, isStoryHiddenLayer, isStyleLibraryTargetLayer, isStyleSpecColor, isSvgMarkerResponse, isValidMetadataDate, isValidMetadataEmail, isValidMetadataUrl, isVectorColorExpression, isochroneResponseToFeatures, jsonValuesEqual, labelFieldTextField, layerGroupDepth, layerGroupMoveability, layerGroupSortability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerLocalPath, layerPanelGroupHeaders, lineWidthValue, localFileName, lookupProjectCredential, mapZoomStepOutputs, matchFeaturesByExpression, matchLayerStyleEntry, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, mergeAttributeFormConfigs, metadataDateToRfc3339, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeArcgisWkid, normalizeBlankBackgroundColor, normalizeBookmarkGroups, normalizeBookmarks, normalizeCesiumBasemap, normalizeDashboardColumns, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerDescriptiveMetadata, normalizeLayerLibraryEntries, normalizeLayerStyleEntries, normalizeLayersFileContent, normalizeLegendConfig, normalizeLongitude, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizeObiaWorkbench, normalizePrimaryRenderer, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeProjectInteraction, normalizeSecondaryMapViews, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, overlayStoredEnvironmentVariables, overlayStoredGeocodingApiKeys, overlayStoredPreferenceCredentials, parseCesiumIonAssetId, parseContours, parseCzml, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseLayerStylesFile, parseLayersFile, parseMetadataKeywords, parseProject, parseS3BucketList, parseS3Error, parseS3ListObjects, parseS3Url, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, patchJpegCogSource, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, portableWmsTileUrl, presignS3Url, previewValue, printLayoutConfigsEqual, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, readStoredAuthorName, reapplyLayerJoins, redactConfigurationCredentials, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerProjectRestoreHistory, registerS3UrlSigner, removeTrailingJsonCommas, rendererAppliesOpacity, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestNavigationRoute, requestRoute, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveHeaderReferences, resolveIdentifyTarget, resolveLabelNumberLocale, resolveLayerCapabilities, resolveMapGrid, resolvePopupBody, resolvePopupImageHeight, resolvePopupMaxWidth, resolvePopupRows, resolvePopupTitle, resolveProjectHeaderReferences, resolveReadableUrl, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, s3ObjectHttpsUrl, sameCesiumImagery, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, selectLayerById, selectLayerIds, selectLayerSummaries, selectLayersWhen, serializeLayerLibrary, serializeLayerStylesFile, serializeLayersFile, serializeProject, serializeProjectWithLayerCache, serializeProjectWithLayerCacheAsync, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setProjectCredentialLookup, setRegistryPublishableSettings, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, shouldZoomToNewLayers, simpleStyleNumberValue, sortLayerGroupInPanel, splitProjectCredentials, stacAssetMediaType, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyLayerOpacityFactor, storyLocationView, storyMapHasContent, storyVisibleLayers, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, uniqueImportedLayerName, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, unsignedSourceUrl, useAppCapability, useAppStore, useLayer, useLayerIds, useLayerSummaries, useLayersWhen, validateAttributeFormField, validateAttributeFormValues, validateLayerDescriptiveMetadata, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visiblePopupFields, visiblePopupProperties, withDatumShift, withGeoKeysDatumShift, withJpegTablesPatch, withoutHistory };
9012
+ export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, ARCGIS_PROJECTION_PRESETS, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CESIUM_BASEMAPS, CESIUM_BING_AERIAL_ASSET_ID, CESIUM_GOOGLE_PHOTOREALISTIC_ASSET_ID, CESIUM_ION_QUICK_PICKS, CESIUM_ION_SOURCE_KIND, CESIUM_KML_SOURCE_KIND, CESIUM_OSM_BUILDINGS_ASSET_ID, CHINA_BASEMAPS, CZML_QUICK_PICKS, CZML_SOURCE_KIND, DEFAULT_BASEMAP, DEFAULT_BLEND_MODE, DEFAULT_COLLABORATION_STATE, DEFAULT_DASHBOARD_COLUMNS, DEFAULT_EDITOR_IDENTITY, DEFAULT_EDITOR_TRACKING_CONFIG, DEFAULT_ELLIPSOID_ID, DEFAULT_FORWARD_GEOCODE_ENDPOINT, DEFAULT_GEOCODING_PROVIDER_ID, DEFAULT_LAYER_GROUP_OPACITY, DEFAULT_LAYER_STYLE, DEFAULT_LEGEND_CONFIG, DEFAULT_MAP_GRID_LAYOUT, DEFAULT_PRIMARY_RENDERER, DEFAULT_PRINT_LAYOUT, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_PREFERENCES, DEFAULT_REVERSE_GEOCODE_ENDPOINT, DEFAULT_ROUTING_ENDPOINT, DEFAULT_STORY_CHAPTER_DURATION_SECONDS, DEFAULT_STORY_MAP, DEPLOYMENT_CAPABILITIES, DUCKDB_VECTOR_FEATURE_WARN_COUNT, DUCKDB_VECTOR_ROUTE_BYTES, EARTH_MEAN_RADIUS_METERS, EDITOR_IDENTITY_STORAGE_KEY, ELEVATION_REQUEST_TIMEOUT_MS, ELLIPSOIDS, EXPRESSION_FUNCTION_CATEGORIES, ElevationFetchError, FEET_PER_METER, FEET_PER_MILE, GEOCODE_ATTRIBUTE_PREFIX, GEOCODE_DISPLAY_NAME_KEY, GEOCODE_LAT_KEY, GEOCODE_LON_KEY, GEOCODE_PROVIDER_KEY, GEOCODE_SCORE_KEY, GEOCODE_STATUS_KEY, GEOCODING_PROVIDERS, GOOGLE_MAPS_API_KEY_HEADER, GeocoderServiceError, GeocoderUnsupportedError, IDENTIFY_ALL_LAYERS_ID, LABEL_NUMBER_LOCALES, LABEL_NUMBER_SAMPLE, LARGE_VECTOR_FEATURE_THRESHOLD, LAYERS_FILE_TYPE, LAYERS_FILE_VERSION, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_STYLES_FILE_TYPE, LAYER_STYLES_FILE_VERSION, LAYER_TYPES, MAP_CRS_CHANGE_EVENT, MAP_CRS_PRESETS, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYERS_FILE_BYTES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_PROJECT_BOOKMARKS, MAX_REDACT_DEPTH, MAX_STORY_CHAPTER_DURATION_SECONDS, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, MIN_STORY_CHAPTER_DURATION_SECONDS, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, NO_LAYERS, OPENFREEMAP_BASEMAPS, PHOTO_FULL_PROPERTY, PHOTO_PROPERTY, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANET_SWITCHER_OPTIONS, POINTER_ELEVATION_CACHE_LIMIT, POINTER_ELEVATION_DEBOUNCE_MS, POPUP_IMAGE_HEIGHT_RANGE, POPUP_MAX_WIDTH_RANGE, PROJECT_CREDENTIAL_FIELDS, PROJECT_VERSION, PROTOMAPS_BASEMAPS, PUBLIC_GEOCODE_ROW_CAP, PUBLISHABLE_PLUGIN_SETTINGS, RAMP_STYLE_KEYS, RASTER_APPEARANCE_STATE_KEYS, REGIONAL_BASEMAPS, REGIONAL_BASEMAP_GROUPS, REGIONAL_BASEMAP_SENTINEL_PREFIX, ROLE_PRIVILEGES, RoutingRequestError, S3CorsError, S3_MAX_PRESIGN_SECONDS, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STAC_PROCESSING_EXTENSION, STAC_VERSION, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeLayerFilterExpression, activeQuickFilters, adaptBboxTemplateToCrs, addLayersToProject, allowsCredentialHeaders, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, applyStoryLayerOpacity, arcgisLocatorInputField, attributeFormFieldLabel, attributeLinkUrl, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerStacItem, buildLayerTree, buildMatrixRequest, buildReverseGeocodeUrl, buildRouteRequest, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, cesiumIonAssetId, cesiumIonAssetKind, cesiumKmlSource, changedPreferenceCredentials, circleRadiusValue, clampLabelDecimals, clampLatitude, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileLayerFilters, compileQuickFilter, compileQuickFilters, computeGeoArrowBBox, controlRendersLayer, convertTiffYCbCrToRgb, copyableLayerStyleKind, createCesiumIonLayer, createCesiumKmlLayer, createCzmlLayer, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectLayerSerializationCache, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLayerEvaluator, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, czmlSource, darkenHex, datumShiftFor, decodePolyline, decodePolylineDetailed, deriveTileMatrixSet, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, diffFeatureCollections, diffProjects, documentLocale, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, encodeS3Key, ensureEditorTrackingFields, environmentVariableAccount, evaluateMapExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, explainS3ReadError, extractCopiedLayerStyle, extractLayerStyleEntries, extractLayersFileContent, extractStyleLibraryStyle, extrusionColorValue, extrusionHeightValue, featureCollectionToGeoArrow, featureSelectionId, fetchArcGISLocatorInfo, fetchElevations, findLayerStyleEntryIndex, formatCameraAltitude, formatExpressionPreviewValue, formatLabelNumber, formatLabelNumberSample, formatPixelValue, formatPopupValue, formatRoundNum, formatS3Uri, generatorCircleRadiusValue, generatorSizeRange, geoArrowToFeatureCollection, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocodeSuggest, geocoderMinIntervalMs, geocoderNeedsApiKey, geocoderSupportsReverse, geocoderSupportsSuggest, geocodingApiKeyAccount, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getArcgisApiKey, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapCrsPreset, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getS3UrlSigner, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, hasActiveLayerFilter, hasActiveQuickFilter, hasAppPrivilege, hasHeaderReferences, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, heatmapRampColors, horizontalBbox, hydrateProjectLayer, identifyAllIncludes, identifyStateWithoutLayers, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, installCogTilerDatumShift, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbbreviatedJpegCompression, isAbsoluteFilesystemPath, isAdoptedVectorAwaitingFeatures, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isCesiumIonLayer, isCesiumKmlLayer, isCesiumOnlyLayer, isCredentialFieldName, isCredentialUrlParam, isCredentialedS3Url, isCzmlLayer, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGeoArrowBatch, isGooglePhotorealisticTilesetUrl, isHeaderReferenceOnly, isHexColor, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isMaintainedEditorTrackingField, isMapClickClaimed, isPopupClickEnabled, isPopupHoverEnabled, isRasterMarkerSource, isReferencedLayer, isRegionalBasemapSentinel, isS3Uri, isSafePopupUrl, isSecretEnvironmentVariable, isSessionOnlyLayer, isStoryHiddenLayer, isStyleLibraryTargetLayer, isStyleSpecColor, isSvgMarkerResponse, isValidMetadataDate, isValidMetadataEmail, isValidMetadataUrl, isVectorColorExpression, isochroneResponseToFeatures, jsonValuesEqual, labelFieldTextField, layerGroupDepth, layerGroupMoveability, layerGroupSortability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerLocalPath, layerPanelGroupHeaders, lineWidthValue, localFileName, lookupProjectCredential, mapZoomStepOutputs, matchFeaturesByExpression, matchLayerStyleEntry, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, mergeAttributeFormConfigs, metadataDateToRfc3339, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeArcGISLocatorUrl, normalizeArcgisWkid, normalizeBlankBackgroundColor, normalizeBookmarkGroups, normalizeBookmarks, normalizeCesiumBasemap, normalizeDashboardColumns, normalizeGeocodingLocator, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerDescriptiveMetadata, normalizeLayerLibraryEntries, normalizeLayerStyleEntries, normalizeLayersFileContent, normalizeLegendConfig, normalizeLongitude, normalizeMapCrs, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizeObiaWorkbench, normalizePrimaryRenderer, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeProjectInteraction, normalizeSecondaryMapViews, normalizeStoryChapterDurationSeconds, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, overlayStoredEnvironmentVariables, overlayStoredGeocodingApiKeys, overlayStoredPreferenceCredentials, parseArcGISLocatorInfo, parseArcGISSuggestions, parseCesiumIonAssetId, parseContours, parseCzml, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseLayerStylesFile, parseLayersFile, parseMapCrsEpsgCode, parseMetadataKeywords, parseProject, parseS3BucketList, parseS3Error, parseS3ListObjects, parseS3Url, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, patchJpegCogSource, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, portableWmsTileUrl, presignS3Url, previewValue, printLayoutConfigsEqual, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, readStoredAuthorName, reapplyLayerJoins, redactConfigurationCredentials, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerMapClickClaim, registerProjectRestoreHistory, registerS3UrlSigner, removeTrailingJsonCommas, rendererAppliesOpacity, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestNavigationRoute, requestRoute, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveHeaderReferences, resolveIdentifyTarget, resolveLabelNumberLocale, resolveLayerCapabilities, resolveMapGrid, resolvePopupBody, resolvePopupImageHeight, resolvePopupMaxWidth, resolvePopupRows, resolvePopupTitle, resolveProjectHeaderReferences, resolveReadableUrl, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, s3ObjectHttpsUrl, sameCesiumImagery, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, selectLayerById, selectLayerIds, selectLayerSummaries, selectLayersWhen, selectedGeocodingLocator, serializeLayerLibrary, serializeLayerStylesFile, serializeLayersFile, serializeProject, serializeProjectWithLayerCache, serializeProjectWithLayerCacheAsync, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setGeocodingTokenProvider, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setProjectCredentialLookup, setRegistryPublishableSettings, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, shouldZoomToNewLayers, simpleStyleNumberValue, sortLayerGroupInPanel, splitProjectCredentials, stacAssetMediaType, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyLayerOpacityFactor, storyLocationView, storyMapHasContent, storyVisibleLayers, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, uniqueImportedLayerName, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, unsignedSourceUrl, useAppCapability, useAppStore, useLayer, useLayerIds, useLayerSummaries, useLayersWhen, validateAttributeFormField, validateAttributeFormValues, validateLayerDescriptiveMetadata, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visiblePopupFields, visiblePopupProperties, withDatumShift, withGeoKeysDatumShift, withJpegTablesPatch, withoutHistory };
9983
9013
 
9984
9014
  //# sourceMappingURL=index.mjs.map