docs: review the docs against the code and fix drift (#3008)

* docs: review the docs against the code and fix drift

Check every docs page against the current code and correct what no
longer matches: menu paths and labels, plugin placement (most plugins
now open in a docked panel), Cesium ion tokens being optional, desktop
OAuth sign-in for sharing, browser project save/open, project schema
fields, server and collaboration endpoints, Python and MCP APIs, and
stale paths in the source-code deep dives.

Fix the Spatial SQL tutorial's ST_Area_Spheroid query, which needs
ST_FlipCoordinates for lat/lon axis order. Document the Settings
sections that were listed but never described (Language, Appearance,
Interface, Geocoding, AI Providers), and add the USGS NLDI and ArcGIS
feature editing pages to the nav.

* docs: keep the NLDI and ArcGIS editing pages out of the nav

* Address CodeRabbit review feedback

- collaboration.md: give both snapshot-cap defaults, 10 MB for the
  relay and 1 MB in the Docker Compose setup.
- features.md, cesium-renderer.md: without an Ion token the globe still
  gets terrain from keyless Terrarium elevation (or a configured COG),
  not a bare ellipsoid (cesium-engine.ts enableWorldTerrain).

* Address CodeRabbit review feedback

- project-format.md: type blankBackgroundColor and selectedLayerId as
  string | null, matching ProjectFile in packages/core/src/types.ts.
This commit is contained in:
Qiusheng Wu authored and GitHub committed 2026-10-06 19:03:19 -04:00
1 parent 49f083b334
commit 38ed979d3b
67 files changed
+679 -418

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@ Before opening a pull request:
```bash
pre-commit run --files path/to/changed.ts path/to/other.tsx # list each file you changed
npm run ci:web # frontend-only changes: lint, i18n check, type check, unit tests
npm run ci:web # frontend-only changes: lint, drift checks, type checks, unit tests
npm run ci # the full gate CI runs (also needs Rust and Python)
```
+1
View File
@@ -15,6 +15,7 @@ many transitive dependencies they rely on).
### Mapping and rendering
- [MapLibre GL JS](https://maplibre.org/) — the core map rendering engine.
- [CesiumJS](https://cesium.com/platform/cesiumjs/) — the 3D globe rendering engine.
- [deck.gl](https://deck.gl/) and the [vis.gl](https://vis.gl/) / Open Visualization toolkit — raster, point-cloud, and 3D overlays.
- [Three.js](https://threejs.org/) — 3D rendering used by globe and atmosphere effects.
- [PMTiles](https://github.com/protomaps/PMTiles) and [Protomaps](https://protomaps.com/) — single-file tile archives.
+14 -11
View File
@@ -28,19 +28,22 @@ Vector tools (Turf.js / in-browser GeoPandas via Pyodide), the SQL Workspace
Tools that depend on a **local desktop process** are hidden on mobile, because
Android has no Python sidecar or local helper binaries:
- Processing → GeoLibre Toolbox → **Raster**, **Conversion**, **AI Segmentation**
(all need the Python sidecar)
- Add Data → **PostgreSQL** (served by the local Martin tile server)
- Processing → GeoLibre Toolbox → **Conversion**, **AI Segmentation**, and the
sidecar-backed **Raster** tools (all need the Python sidecar; the Raster
submenu keeps only the client-side **Download Global DEM** tool)
- Add Data → **PostgreSQL** (served by the local Martin tile server) and
**SQL Server**
These are gated by a user-agent `isMobile()` check, so the Add Data menu and the
Layer panel's add-data group never offer them. Everything else runs client-side.
These are gated by a user-agent `isMobile()` check, so the Processing menu, the
Add Data menu, and the Layer panel's add-data group never offer them. Everything
else runs client-side.
PostgreSQL has one entry point that check does not cover: the Browser panel
keeps its **Databases** section on every platform for discovery, so its + still
opens the PostgreSQL dialog. The dialog gates on `isDesktopRuntime()`
(`isTauri() && !isMobile()`) rather than on `isTauri()` alone, so on Android it
shows the "requires GeoLibre Desktop" notice and disables Connect instead of
calling a sidecar that cannot exist (GeoLibre#2091).
The database sources have one entry point that check does not cover: the Browser
panel keeps its **Databases** section on every platform for discovery, so its +
still opens the connection dialog. The dialogs gate on `isDesktopRuntime()`
(`isTauri() && !isMobile()`) rather than on `isTauri()` alone, so on Android they
show the "requires GeoLibre Desktop" notice and disable Connect instead of
calling a helper that cannot exist (GeoLibre#2091).
## Toolchain setup (one time)
+4 -4
View File
@@ -3,11 +3,11 @@
GeoLibre can save feature additions, attribute and geometry updates, and deletions
back to an editable ArcGIS Feature Service in the web and desktop apps.
1. Choose **Add Data > ArcGIS Layer**, select **Feature layer**, and enter the
1. Choose **Add Data → ArcGIS Layer**, select **Feature layer**, and enter the
service layer URL (or portal item). Supply an access token for a protected service.
2. Edit attributes in the attribute table or use **Layer actions > Edit geometry**.
2. Edit attributes in the attribute table or use **Layer actions → Edit geometry**.
Finish the geometry editing session before saving.
3. Choose **Layer actions > Save edits to ArcGIS service**. The status reports
3. Choose **Layer actions → Save edits to ArcGIS service**. The status reports
inserted, updated, and deleted records, followed by any individual failures.
The save action appears when the service advertises supported editing operations
@@ -43,7 +43,7 @@ an existing token expires.
## Signing in with ArcGIS
In Add Data, ArcGIS, set **Authentication** to **Sign in with ArcGIS** instead of
In the **Add Data → ArcGIS Layer** dialog, set **Authentication** to **Sign in with ArcGIS** instead of
pasting a token. GeoLibre opens the portal's own sign-in page (OAuth 2.0
Authorization Code with PKCE), so it never sees your password, MFA code or
identity-provider credentials, and the layer is added with your own permissions.
+1 -1
View File
@@ -179,7 +179,7 @@ override draws Esri World Imagery instead.
feature is painted onto the capture rather than drawn on the live map, and
a fixed scale is corrected up to three times, aiming for 0.5 % of the
requested one, since the SDK rounds the zoom it is given.
- **Plugins → Layer Control** toggles the native ArcGIS layer list, enabled by
- **Controls → Layer Control** toggles the native ArcGIS layer list, enabled by
default on the primary map like the shared plugin. Its visibility
toggles update the project and the sidebar, and sidebar changes update the
list. Mixed-geometry GeoJSON records have one entry for all their parts.
+3 -2
View File
@@ -30,6 +30,7 @@ flowchart LR
| `@geolibre/processing` | Client-side algorithm registry |
| `@geolibre/plugins` | Plugin interface and built-in plugins |
| `@geolibre/embed` | Typed, dependency-free client for the iframe embed API, published to npm |
| `@geolibre/collab-core` | Transport-neutral collaboration relay core (wire protocol, permissions, payload validators) shared by the `workers/collab` and `workers/collab-node` relays |
| `geolibre-desktop` | Shell layout, Tauri I/O, composition |
## State flow
@@ -86,7 +87,7 @@ The default MapLibre map can be joined by — or replaced with — a 3D globe re
- **Camera sync.** `packages/map/src/cesium-camera.ts` converts between MapLibre's Web-Mercator `MapViewState` (zoom, nadir-referenced pitch, bearing) and Cesium's camera (metric range, horizon-referenced pitch, heading), matched by **ground resolution** (metres per pixel) so the on-screen scale stays in step even when the panes differ in height. `CesiumCanvas` seeds its camera from the shared `mapView`, applies store changes to the globe, and writes the globe's own moves back — bidirectional, like the 2D panes — with a tolerance check that suppresses the apply→`moveEnd` echo so there is no jitter loop.
- **Layer sync.** `CesiumLayerSync` (`packages/map/src/cesium-layer-sync.ts`) reconciles the store's `GeoLibreLayer[]` onto the globe the way `MapController.syncLayers` does for MapLibre, reusing the same per-pane visibility overrides and group effects as `SecondaryMapCanvas`. It renders the kinds where Cesium is the natural fit — GeoJSON (a draped `GeoJsonDataSource` whose entities are styled per feature by `createFeatureStyleResolver` (`packages/map/src/feature-style.ts`, issue #2278): it evaluates the same MapLibre expressions `@geolibre/core` builds for the 2D map — single, categorized, graduated, rule-based with per-rule symbol overrides, and expression modes, simplestyle properties, proportional sizing, metre-unit strokes — with the style-spec engine, so polygon fills and outlines, line widths and arrow decorations, circle radii, classified marker sprites, fill patterns, and the layer's zoom range (a shared `DistanceDisplayCondition`) match the 2D map; points draw as circles unless the layer renders markers; a point-only layer with `pointRenderer: "cluster"` clusters through the data source's `EntityCluster` with bubbles and abbreviated counts styled like the 2D cluster layers and switched off past `clusterMaxZoom`, and a point-only layer above 50 000 features bypasses entities for one `PointPrimitiveCollection` whose primitives carry the feature reference on `id` so picking, highlighting, and filters still work (`packages/map/src/cesium-points.ts`, issue #2282; the heatmap renderer has no globe form and draws as plain circles); with field or expression labels, halos, and label zoom limits), XYZ/raster/WMTS, WMS, ArcGIS MapServer (`ArcGisMapServerImageryProvider`), and georeferenced image overlays (`SingleTileImageryProvider`) (as `ImageryLayer`s), and 3D Tiles (a `Cesium3DTileset` primitive that consumes the layer's tileset URL, request headers, and altitude offset directly, and whose features are classified by `cesium-tileset-style.ts` (issue #2290): the layer's colour expression and composed feature filter are translated from MapLibre expressions into the 3D Tiles styling language and applied as a `Cesium3DTileStyle`, so a tileset categorizes, graduates, or follows a rule tree the way an extruded vector layer does, and the layer opacity reaches it as a white multiply that fades textured tiles without tinting them — an untranslatable colour or filter leaves the tileset drawing its own colours and showing every feature rather than guessing; the attribute names the Style panel lists come from the first rendered tile, published onto `metadata.fields` because a tileset carries its schema in the tiles rather than in the layer record; an ArcGIS I3S scene layer goes through Cesium's own `I3SDataProvider` instead of loaders.gl, a Gaussian-splat layer whose asset is a 3D Tiles tileset renders through the same branch because Cesium draws `KHR_gaussian_splatting` tiles natively, and a point cloud in 3D Tiles form gets `pointCloudShading` eye-dome lighting), plus COPC point clouds decoded in the browser with the `copc` package into a bounded `PointPrimitiveCollection` preview reprojected through the archive's WKT (`packages/map/src/cesium-point-cloud.ts`, issue #2285) — with live visibility/opacity, rebuild-on-source-change, and removal. Tile sources that reach the 2D map through a MapLibre custom protocol — COG tiles from the WASM tiler, raster PMTiles, local MBTiles, the desktop's native XYZ/WMS fetcher, KML super-overlays — render through `ProtocolImageryProvider` (`packages/map/src/cesium-protocol-imagery.ts`, issue #2283), a Cesium `ImageryProvider` that expands the `{z}/{x}/{y}` template the way MapLibre does, hands each tile URL to the handler in `maplibregl.config.REGISTERED_PROTOCOLS` (process-wide, so it works with no MapLibre map mounted), and decodes the bytes into an `ImageBitmap` flipped the way Cesium expects; an unregistered scheme is reported as a layer error rather than drawn blank. COG layers open the same `cog-tiler-wasm` source the raster control uses and render from the persisted `metadata.rasterState` (`cesium-cog-imagery.ts`), so a COG looks the same on both renderers; a COG restored from a project with only a desktop file path stays 2D-only until the raster control has reopened it. Zarr layers draw through zarr-cesium's `ZarrLayerProvider` (`cesium-zarr-imagery.ts`, issue #2261), fed from the same record the 2D Zarr control and the ArcGIS renderer read (the URL, a registered store or kerchunk manifest, the variable, selector, colour limits, and ramp); a Time Slider step or restyle updates the live provider and swaps in a fresh `ImageryLayer` over it rather than reopening the store, and a grid in a CRS other than EPSG:4326/3857 is reported as a layer error. zarr-cesium imports the `cesium` wrapper, which Vite aliases to `@cesium/engine` so the globe keeps one engine instance. The raster symbology (brightness, contrast, saturation, hue) maps onto `ImageryLayer`'s own adjustLine truncated
- **Globe interaction parity.** The primary globe publishes ground cursor coordinates and optional terrain elevation to the shared status bar, clearing them on pointer exit and teardown. Projected scene modes report a Mercator projection. Home, scene-mode, and fullscreen widgets participate in shared control positioning without remounting their DOM. In the Controls menu they answer to the existing entries: **Compass** governs the Home (reset view) button and **Globe** governs the scene-mode picker, as the globe's counterparts of the 2D reset-bearing and projection controls, and the toolbar replays those choices onto a freshly mounted globe.
- **Environment plugins.** The Sun simulation, Atmospheric Effects, Clouds, and Precipitation plugins declare MapLibre and Cesium (the Flight Simulator declares all three) and branch on `app.getCesiumScene()` (issue #2287), a typed handle to the primary globe's namespace, widget, scene, camera, and clock that `CesiumEngine.getCesiumScene()` exposes as the globe's counterpart to `getMap()`. On the globe the Sun drives a native `SunLight`, `globe.enableLighting`, and the scene clock (the night-side depth maps onto `globe.vertexShadowDarkness`); Effects toggles the sky box and atmosphere and re-tints `SkyAtmosphere` by hue/saturation/brightness shifts from the halo settings; the Flight Simulator shares its keyboard, physics, and HUD with both 2D maps behind a camera adapter — `camera.setView` each frame with `screenSpaceCameraController.enableInputs` suspended on the globe, `setFreeCameraOptions` on Mapbox, `calculateCameraOptionsFromCameraLngLatAltRotation` on MapLibre; the weather overlays are store tile layers and render through `CesiumLayerSync` unchanged. The Spinning Globe control needs no branch: it drives the store through the control host's MapLibre facade. `DesktopShell` re-attaches these plugins after either engine mounts, so a renderer swap rebinds them the way a MapLibre re-init does.
- **Environment plugins.** The Sun simulation, Atmospheric Effects, Clouds, Precipitation, and Flight Simulator plugins declare Cesium alongside the 2D engines (Sun, Effects, and the Flight Simulator list MapLibre, Mapbox, and Cesium; Clouds and Precipitation also list ArcGIS) and branch on `app.getCesiumScene()` (issue #2287), a typed handle to the primary globe's namespace, widget, scene, camera, and clock that `CesiumEngine.getCesiumScene()` exposes as the globe's counterpart to `getMap()`. On the globe the Sun drives a native `SunLight`, `globe.enableLighting`, and the scene clock (the night-side depth maps onto `globe.vertexShadowDarkness`); Effects toggles the sky box and atmosphere and re-tints `SkyAtmosphere` by hue/saturation/brightness shifts from the halo settings; the Flight Simulator shares its keyboard, physics, and HUD with both 2D maps behind a camera adapter — `camera.setView` each frame with `screenSpaceCameraController.enableInputs` suspended on the globe, `setFreeCameraOptions` on Mapbox, `calculateCameraOptionsFromCameraLngLatAltRotation` on MapLibre; the weather overlays are store tile layers and render through `CesiumLayerSync` unchanged. The Spinning Globe control needs no branch: it drives the store through the control host's MapLibre facade. `DesktopShell` re-attaches these plugins after either engine mounts, so a renderer swap rebinds them the way a MapLibre re-init does.
- **Persistence.** A pane's `viewKind` is part of `SecondaryMapView` and round-trips through the `.geolibre.json` project format (`normalizeSecondaryMapViews`), so a project saved with a globe pane reopens as the globe, token or not. The workspace's own choice persists as the top-level `primaryRenderer` (`normalizePrimaryRenderer`), written only when it is not the default — so a MapLibre project is byte-identical to one saved before the setting existed, and a 3D-first project reopens directly on the globe.
## DuckDB-WASM
@@ -128,7 +129,7 @@ A third Vector engine, **Python (Pyodide)**, runs the same GeoPandas/Shapely cod
The **SQL Workspace** offers an **Apache Sedona** engine alongside DuckDB and PostGIS. It runs Sedona spatial SQL on [SedonaDB](https://sedona.apache.org/sedonadb/), the single-node Rust (DataFusion + Arrow) engine, through the sidecar's `/sql` endpoints (`/sql/status`, `/sql/run`) backed by the optional `sedona` extra (`apache-sedona[db]`). In the browser — and on desktop when the extra is not installed — the same engine runs entirely client-side on [CereusDB](https://github.com/tobilg/cereusdb), a WebAssembly build of SedonaDB; the workspace prefers the sidecar when reachable and falls back to CereusDB automatically. The CereusDB bundle is large, so it is dynamically imported into its own chunk and only downloaded when a Sedona query first runs.
The sidecar has since grown past conversion: `raster.py` runs the Raster toolbox on rasterio/numpy/contourpy, `vector.py` backs the optional GeoPandas vector engine, and `ml.py` reverse-proxies AI Segmentation to the `samgeo-api` server (SamGeo/SAM 3), which is kept out of process because SAM 3 needs a multi-gigabyte GPU Torch build. Leafmap and GeoAI workflows are still expected to follow the same pattern.
The sidecar has since grown past conversion. Its routers live in `backend/geolibre_server/geolibre_server/app/`: `raster.py` runs the Raster toolbox on rasterio/numpy/contourpy, `vector.py` backs the optional GeoPandas vector engine, `sql.py` backs the Sedona engine above, `postgis.py` and `mssql.py` load PostGIS and SQL Server tables as editable layers and write edits back, `pointcloud.py` writes point cloud annotator labels into a whole LAS/LAZ/COPC file, and `ml.py` reverse-proxies AI Segmentation to the `samgeo-api` server (SamGeo/SAM 3), which is kept out of process because SAM 3 needs a multi-gigabyte GPU Torch build. See the [Server API reference](server-api.md) for every endpoint. Leafmap and GeoAI workflows are still expected to follow the same pattern.
## Offline support (PWA)
+6 -4
View File
@@ -7,10 +7,12 @@ other three engines. Primary and pane choices are saved with the project.
## Credentials
The renderer requires a Cesium ion token. The hosted web version bundles a
demo token, so Cesium works there out of the box; the desktop and mobile apps
need your own token. The token enables Cesium World Terrain, Ion World Imagery
as a fallback, and access to ion-hosted assets.
The globe works without a Cesium ion token: it drapes the project basemap as
its base imagery, uses keyless Terrarium elevation (or a configured COG) for
terrain, and shows a hint that links to the token setting. A token adds
Cesium World Terrain, Ion World Imagery as the fallback for a basemap with no
raster form, and access to ion-hosted assets. The hosted web version bundles a
demo token; the desktop and mobile apps need your own for those features.
Enter the token under **Settings → Environment Variables → Cesium Ion token**.
It is stored on the current device rather than in the project file. Developers
+27 -12
View File
@@ -19,6 +19,8 @@ small teams.
Bounded recent history is relayed to late joiners; never written to a project.
- **Permissions** — the host's per-participant view-only / can-edit overrides
(#754), broadcast on the participant list as `editOverride`.
- **Comments** — map comment add / reply / resolve / delete actions, relayed as
`comment-mutation` frames and applied to each peer's store.
## Architecture
@@ -55,9 +57,12 @@ Limitations).
## Sync protocol
All frames are JSON. `CollabMessage` is a discriminated union on `type`. See
`apps/geolibre-desktop/src/lib/collab-protocol.ts` for the authoritative types
(shared by client and worker).
All frames are JSON, and each direction is a discriminated union on `type`
(`ClientMessage` and `ServerMessage`). The relays' types live in
`packages/collab-core/src/protocol.ts` (`@geolibre/collab-core/protocol`, with
`project` typed as `unknown`); the frontend keeps a parallel copy in
`apps/geolibre-desktop/src/lib/collab-protocol.ts` with `project` typed as
`GeoLibreProject`. Keep the `type` discriminants and field names in sync.
Client → server:
@@ -75,6 +80,7 @@ Client → server:
| `kick-participant` | `clientId, reason?` | host only; disconnect a participant |
| `block-participant` | `clientId, reason?` | host only; disconnect and bar that participant key from rejoining (see the caveat under *Moderation*) |
| `chat` | `text, coordinate?` | a chat message, with an optional attached map coordinate (#754) |
| `comment-mutation` | `action` | add / reply / toggle-resolve / delete a map comment; validated and rate-limited by the relay |
Server → client:
@@ -91,7 +97,8 @@ Server → client:
| `layer-locks` | `lockedLayerIds` | broadcast when host locks/unlocks layer IDs |
| `kicked` | `reason` | sent to a participant who was kicked or blocked by the host |
| `chat` | `message` | fan-out of a chat message (echoed to the sender, so order is server-authoritative) (#754) |
| `error` | `code, message` | e.g. `forbidden`, `too-large`, `identity-required`, `identity-unavailable`, `layer-locked` |
| `comment-mutation` | `action` | fan-out of a peer's comment change |
| `error` | `code, message` | `forbidden`, `too-large`, `bad-message`, `not-found`, `identity-required`, `identity-unavailable`, or `layer-locked` |
### Echo / feedback-loop prevention
@@ -113,10 +120,15 @@ accepted MVP limitation; a coalesced-history option is a v2 item.
## Durable Object (`workers/collab`)
- `POST /sessions` — host creates a session: generates a short base32 code, mints
a host token, stores `{ mode, hostToken }`, returns `{ sessionId, hostToken,
mode }` to the host only.
a host token, stores `{ mode, hostToken, requireIdentity }`, returns
`{ sessionId, hostToken, mode, requireIdentity }` to the host only. A
disallowed origin gets `403` and a rate-limited caller `429` (see the operator
note under *Chat*).
- `GET /sessions/:id/ws` — WebSocket upgrade, routed to
`env.COLLAB_SESSION.get(idFromName(id))`.
- `GET /sessions/:id/log` / `DELETE /sessions/:id/log` — host-only download or
clear of the session log (`Authorization: Bearer <hostToken>`).
- `GET /health` (also `/`) — `{ ok, service, identitySupported }`.
`CollabSession` uses the **WebSocket Hibernation API** so idle sessions evict
from memory while keeping sockets open. Per-socket participant metadata is kept
@@ -137,7 +149,7 @@ of the database.
## Frontend
- `lib/collab-protocol.ts` — shared message types.
- `lib/collab-protocol.ts` — the frontend copy of the message types.
- `lib/collab-client.ts` — WebSocket transport, `resolveCollabBaseUrl()` (wss/loopback
validation, returns `null` when unset), exponential-backoff reconnect.
- `hooks/useCollaboration.ts` — orchestration: subscribes to the store
@@ -260,8 +272,8 @@ ephemeral and never written to a project file.
> hosted origins (`geolibre.app`, `web.geolibre.app`, its legacy
> `viewer.geolibre.app` alias, `studio.geolibre.app`, and
> `collab.geolibre.app`), single-label HTTPS deployment hosts under
> `*.geolibre-preview.pages.dev`, loopback hosts (`localhost` and
> `127.0.0.1`), and `tauri://localhost`). Nested or custom-port preview hosts
> `*.geolibre-preview.pages.dev`, loopback hosts (`localhost`, `*.localhost`,
> and `127.0.0.1`), and `tauri://localhost`). Nested or custom-port preview hosts
> and look-alike domains are rejected; the shared `opengeos.org` GitHub Pages
> preview origin is deliberately not trusted) as browser-origin filtering
> and defense-in-depth (not authentication or a general server-side access gate)
@@ -308,7 +320,8 @@ core:
- `workers/collab-node` is the self-hostable Node/SQLite relay. It is included
as `geolibre-collab` in the root `docker-compose.yml`; its persistent database
is mounted at `/data/collab.sqlite`. Configure `COLLAB_MAX_SNAPSHOT_BYTES`
(default 10,000,000) and `COLLAB_IDLE_TTL_MS` (default two hours) when needed.
(relay default 10,000,000; the compose file defaults it to 1,000,000) and
`COLLAB_IDLE_TTL_MS` (default two hours) when needed.
Both relay implementations accept `COLLAB_MAX_SNAPSHOT_BYTES` as a positive
integer number of bytes. For the Cloudflare relay, set it as a Worker variable,
@@ -347,8 +360,10 @@ environment. Until that env var is set, the feature stays dark.
wins and the slower edit is overwritten. Presence helps users avoid colliding.
- **Payload size**: layers can embed `FeatureCollection`s. `projectFromStore`
already strips redundant `geojson` for URL-backed layers, but a large
in-memory/local-file layer can exceed the ~1 MiB frame cap and is rejected with
a clear error (share via URL instead). v2: diff / chunked layer sync.
in-memory/local-file layer can exceed the snapshot cap
(`COLLAB_MAX_SNAPSHOT_BYTES`: 10 MB by default for the relay, 1 MB in the
Docker Compose setup) and is rejected with
`error: too-large` (share via URL instead). v2: diff / chunked layer sync.
- **Undo**: a remote apply clears local undo (see above).
- v2 directions: per-action mutation or CRDT transport, coalesced remote-apply
history, richer permission/identity (tie to share.geolibre.app accounts).
+8 -9
View File
@@ -91,9 +91,9 @@ you pick the right tool for a given job, not to declare a winner.
| **Cloud-native raster** | COG, Zarr, Cloud-Optimized NetCDF/HDF, kerchunk, MBTiles | COG, Zarr (GDAL's built-in driver) | COG; Zarr as a native multidimensional raster type | Hosted imagery layers | COG and GeoTIFF, loaded into warehouse tables (RaQuet spec) | GeoTIFF upload | — |
| **STAC** | Built-in catalog browser + STAC Index discovery | Built-in STAC connections (Browser panel and Data Source Manager) | Built-in STAC connections and the Explore STAC pane | Living Atlas (not STAC) | — | — | — |
| **OGC services** | XYZ; WMS/WFS/WMTS discovered via GetCapabilities; OGC API Features/Tiles from a landing page, collection, or items URL | Full OGC support | Full OGC support | Yes | Limited | Limited | XYZ / vector tiles |
| **Esri services and geodatabases** | ArcGIS FeatureServer, VectorTileServer, I3S, plus local `.gdb` File Geodatabase folders (desktop) | Yes | Native | Hosted feature, tile, imagery, and scene layers | One-way ArcGIS Online / Portal migration tooling only | Some ArcGIS layers | — |
| **Databases** | PostGIS browsing, DuckDB, in-browser PGlite/PostGIS | PostGIS, SpatiaLite, Oracle, SQL Server, SAP HANA | Enterprise geodatabases (SDE) | Hosted only | The core product — BigQuery, Snowflake, Redshift, Databricks, PostgreSQL/PostGIS, Oracle Spatial, all queried live | PostGIS / Snowflake connections (higher plans) | — |
| **3D and point clouds** | LiDAR, 3D Tiles, I3S, Gaussian splats, glTF/GLB, Cesium globe pane | Point clouds, 3D map view, tiled scene layers | **Deepest 3D** — scenes, I3S, mesh, LAS | Scene Viewer | 3D map view, extrusion, Google Photorealistic 3D Tiles basemap. No point clouds or LiDAR | — | Extrusions and hexbins only |
| **Esri services and geodatabases** | ArcGIS FeatureServer, MapServer, ImageServer, VectorTileServer, I3S, plus local `.gdb` File Geodatabase folders (desktop) | Yes | Native | Hosted feature, tile, imagery, and scene layers | One-way ArcGIS Online / Portal migration tooling only | Some ArcGIS layers | — |
| **Databases** | PostGIS browsing, SQL Server / Azure SQL (desktop), DuckDB, Apache Iceberg, in-browser PGlite/PostGIS | PostGIS, SpatiaLite, Oracle, SQL Server, SAP HANA | Enterprise geodatabases (SDE) | Hosted only | The core product — BigQuery, Snowflake, Redshift, Databricks, PostgreSQL/PostGIS, Oracle Spatial, all queried live | PostGIS / Snowflake connections (higher plans) | — |
| **3D and point clouds** | LiDAR, 3D Tiles, I3S, Gaussian splats, glTF/GLB, CesiumJS as a rendering engine | Point clouds, 3D map view, tiled scene layers | **Deepest 3D** — scenes, I3S, mesh, LAS | Scene Viewer | 3D map view, extrusion, Google Photorealistic 3D Tiles basemap. No point clouds or LiDAR | — | Extrusions and hexbins only |
| **Planetary basemaps** | Moon, Mars, Mercury, Venus, Galilean moons, Titan, Pluto, Charon — with a per-project ellipsoid driving measurements | Via plugins | Limited | — | — | — | — |
## Analysis and processing
@@ -105,7 +105,7 @@ you pick the right tool for a given job, not to declare a winner.
| **Vector analysis** | Buffer, overlay, dissolve, joins, selection, topology checks — Turf.js, or GeoPandas via Pyodide/sidecar | Comprehensive | Comprehensive | Common tools | Buffer, overlay, spatial join, clip, Voronoi/Delaunay, KNN, trade areas — as warehouse SQL | Common tools | Spatial joins via SQL or the AI assistant |
| **Raster analysis** | Hillshade, slope, contour, zonal/focal stats, raster calculator, reclassify, mosaic — rasterio sidecar with a browser fallback | Comprehensive (GDAL/GRASS/SAGA) | Comprehensive; Spatial Analyst extension | Limited | Narrow — zonal statistics and band value extraction | — | — |
| **Spatial SQL** | DuckDB Spatial, PGlite/PostGIS, and Apache Sedona — **all in the browser** | Virtual layers, DB Manager, PostGIS connections | SQL against enterprise geodatabases | — | **The native interface** — warehouse SQL everywhere: query sources, SQL parameters, Workflows, SQL API | SQL on connected sources (higher plans) | DuckDB SQL Data Explorer over loaded data and remote URLs |
| **Model / batch chaining** | Batch runner with model and pipeline chaining | Graphical Model Designer | ModelBuilder | — | **Workflows** — visual DAG builder with scheduling, version history, and SQL export | — | — |
| **Model / batch chaining** | Visual Model Builder, plus a batch runner with model and pipeline chaining | Graphical Model Designer | ModelBuilder | — | **Workflows** — visual DAG builder with scheduling, version history, and SQL export | — | — |
| **Spatial statistics** | Toolbox including Emerging Hot Spot Analysis | Via plugins and processing providers | Full Spatial Statistics toolbox | Some | Deep — Getis-Ord, Moran's I, space-time hotspots, GWR, kriging/IDW, composite scores, twin areas | — | — |
| **Network analysis** | Isochrones, service areas, OD cost matrices, routing | QGIS Network Analysis, plugins (ORS, pgRouting) | Network Analyst extension | Routing services (credits) | Isolines, routes, and routing matrices via Location Data Services (credit-metered) | — | — |
| **AI / ML** | AI Segmentation (SamGeo/SAM 3), in-browser ONNX/YOLO object detection | Via plugins | `arcgis.learn` deep-learning toolset | Pretrained models (credits) | BigQuery ML and Snowflake ML components; embeddings and geospatial foundation models | — | — |
@@ -118,7 +118,7 @@ you pick the right tool for a given job, not to declare a winner.
| **Renderers** | Single, categorized, graduated, rule-based, expression, heatmap, cluster, proportional symbols, diagrams | **Richest** — plus every symbol layer type | Very rich | Smart-mapping styles | Point, line, polygon, grid, H3, raster — plus heatmap and cluster aggregation; quantile/quantize/log/custom scales | Simple, well-designed defaults | Visualization-oriented |
| **Data-defined everything** | Expression Builder wired into filters, labels, styling, field calculation, selection | Yes, pervasive | Attribute-driven symbology (Arcade) | Arcade | Attribute-driven styling per channel; custom **SQL** aggregation expressions in place of an expression language | — | — |
| **Labeling** | Data-defined engine with placement, offset, rotation, wrap, dedup | Best-in-class placement engine | Maplex label engine | Basic | Basic | Basic | Basic |
| **Print layout** | Print Layout composer with legend, scale, title block, atlas / map series, PNG and PDF | Full print composer with atlas | Full layout view with map series | Basic print / export | Export to high-res image and PDF | Export to image and PDF | Image export |
| **Print layout** | Print Layout composer with legend, scale, title block, atlas / map series, PNG, PDF, and editable SVG | Full print composer with atlas | Full layout view with map series | Basic print / export | Export to high-res image and PDF | Export to image and PDF | Image export |
| **Style interchange** | Imports and exports **OGC SLD, QGIS QML, and Mapbox GL JSON** | QML, SLD | `.lyrx`, limited SLD | — | — | — | — |
| **Project import** | Reads **QGIS `.qgs`/`.qgz` and ArcGIS Pro `.aprx`/`.mapx`** | Reads its own | Reads its own (and `.mxd`) | — | — | — | — |
| **Story maps** | Built-in story map builder with presenter view and standalone HTML export | Via plugins | — | ArcGIS StoryMaps (separate product) | Built-in skill for storymaps app development | Shareable maps, not chaptered stories | — |
@@ -130,7 +130,7 @@ you pick the right tool for a given job, not to declare a winner.
|---|---|---|---|---|---|---|---|
| **Scripting** | Python Console, `geolibre` Python package, docked Jupyter notebook beside the map | PyQGIS, `qgis_process` headless CLI | `arcpy`, notebooks | ArcGIS API for Python / JavaScript | CARTO + deck.gl + CARTO CLI + REST APIs | REST API + Python client | JS library |
| **Notebook integration** | The **whole app** embeds as a Jupyter anywidget with two-way project sync | Bindings, not the UI | Notebooks in Pro drive `arcpy` | Hosted notebooks | `pydeck-carto` widget in Jupyter or Colab; Analytics Toolbox SQL from any warehouse notebook | — | Widget |
| **Plugin ecosystem** | TypeScript plugin API, built-ins, zip installs, bundled drop-ins | **Largest** — thousands of Python plugins | .NET add-ins, Python toolboxes | Configurable app templates | Workflows extension packages | None (API only) | Fork the library |
| **Plugin ecosystem** | TypeScript plugin API, built-ins, a registry marketplace, zip installs, bundled drop-ins | **Largest** — thousands of Python plugins | .NET add-ins, Python toolboxes | Configurable app templates | Workflows extension packages | None (API only) | Fork the library |
| **AI assistant** | Natural-language assistant that turns plain English into auditable, undoable operations; bring your own API key | Via plugins | Copilot features | Arcade assistant (beta) and the AI assistants family | **AI Agents** on a map — semantic model, configurable tools, bring your own LLM — plus a hosted **MCP server**, CLI, and agent skills | AI-assisted map making | Yes — OpenAI, Gemini, DeepSeek, or local Ollama; bring your own key |
| **Real-time collaboration** | Yes (MVP) — hosted relay, optionally self-hosted — plus anchored review comments | No (Mergin Maps for sync) | No | Shared editing of hosted layers | Asynchronous plus real-time map-anchored comments | **Best-in-class** multiplayer editing and comments | — |
| **Embedding** | `maponly` and `layout=viewer` URL modes, a versioned `postMessage` API, and the typed `@geolibre/embed` npm client | QGIS Server / QGIS2Web export | — | Embeddable web apps | Embeddable private maps with URL parameters and a `postMessage` API | Embeddable maps | Embeddable |
@@ -183,7 +183,7 @@ planetary-scale raster analysis — and is out of scope here.
| **Historical imagery** | Historical Imagery plugin over Esri World Imagery snapshots, plus Timelapse annual basemaps | A timeline of the capture dates available for a place, plus historical Street View on the web | Historical imagery slider |
| **Photorealistic 3D and Street View** | Google Photorealistic 3D Tiles and Street View with your own API key; plus 3D Tiles, I3S, LiDAR, COPC, and Gaussian splats | Native, no key, best-in-class | 3D buildings and Street View |
| **Presentation output** | Story maps, print layout with atlas, camera tour recorded to video, dashboards, one-file offline HTML export | Shareable Drive projects with a slide-style present mode | Movie Maker HD video and high-resolution image export |
| **Project file** | `.geolibre.json` — open, documented, diffable, local | Hosted project in Drive; KML export | `.kmz` / `.kml` |
| **Project file** | `.geolibre` / `.geolibre.json` — open, documented, diffable, local | Hosted project in Drive; KML export | `.kmz` / `.kml` |
| **Automation** | Python package, MCP server, Jupyter widget, TypeScript plugin API, embed API | — | — |
### Where they overlap
@@ -228,7 +228,7 @@ top of it — and keeps that data on your machine rather than in a Google accoun
**Choose GeoLibre when** you want a real GIS in a browser tab with nothing to
install, your data has to stay on your machine, you work with cloud-native
formats (COG, GeoParquet, PMTiles, Zarr, STAC), you want the same app on desktop,
Android, and inside a notebook, or you are embedding a map workspace into your
Android, iOS, and inside a notebook, or you are embedding a map workspace into your
own product and want an open, documented project format and license.
**Choose QGIS when** you need the widest possible format support through
@@ -300,7 +300,6 @@ here.
<!--
Entries go here, one line each, alphabetical by tool name:
- [<Tool> vs GeoLibre](<https URL>), maintained by <the project or author>
Delete the "none submitted yet" line below once the first one lands.
-->
- [Maptitude vs GeoLibre](https://www.caliper.com/maptitude/solutions/maptitude-vs-geolibre.htm), maintained by Caliper Corporation
+12 -3
View File
@@ -73,9 +73,15 @@ packages/map # MapLibre integration and layer sync
packages/ui # Tailwind + shadcn/ui primitives
packages/plugins # Plugin API and built-in plugins
packages/processing # Client-side algorithm registry
packages/embed # Typed iframe embed client (@geolibre/embed)
packages/collab-core # Shared collaboration code for the collab workers
workers/ # viewer, collab, collab-node, tiles and ai-proxy workers
backend/geolibre_server # Optional FastAPI conversion sidecar (Python)
docs/ # This documentation site (MkDocs)
backend/geolibre_server_api # Reference projects and identity API (Python)
python/ # The geolibre Python package (Jupyter widget, MCP server)
e2e/ # Playwright end-to-end tests
tests/ # Frontend unit and component tests
docs/ # This documentation site (Zensical, mkdocs.yml)
```
See the [Architecture](architecture.md) reference for how these fit together,
@@ -133,7 +139,8 @@ runs the full production build once per invocation; if you already built, add
`SKIP=npm-build` in front of the command.
`npm run ci:web` is the quick gate for changes that only touch the web app and
its packages: lint, the i18n catalog check, `typecheck:fast`, the
its packages: lint, the shared dependency, i18n catalog, gallery and plugin docs
checks, `typecheck:fast`, the
[test type check](maintenance.md#test-type-check-ratchet), and the frontend
unit tests. It needs only Node.
@@ -143,6 +150,7 @@ order:
| Step | Command | Covers |
| ------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Lint | `npm run lint` | ESLint over `apps/`, `packages/`, `workers/` and `tests/` |
| Shared deps check | `npm run check:shared-deps` | Dependencies declared by both the app and `packages/plugins` carry the same range (see [Shared dependency ranges](maintenance.md#shared-dependency-ranges)) |
| i18n catalog check | `npm run i18n:tools:check` | The processing-tool strings in `en.json` match the tool registries (regenerate with `npm run i18n:tools`) |
| Gallery check | `npm run gallery:check` | `docs/gallery.md` and the `docs/demos.md` teaser match `scripts/demo-gallery.json` (regenerate with `npm run gallery`) |
| Plugin docs check | `npm run plugins:docs:check` | The built-in plugin table in `docs/user-guide/plugins.md` matches the plugin registry (regenerate with `npm run plugins:docs`) |
@@ -161,7 +169,8 @@ and `npm run ci:backend` (backend and Docker tests; needs Python and the npm CLI
to run the script, but no `npm install`). CI runs lint, `ci:frontend`,
`ci:backend` and `check:rust` as separate, parallel jobs in
`.github/workflows/ci.yml` rather than one after another, with
`typecheck:tests` running next to lint in the "Lint and type check" job, so if
`check:shared-deps` and `typecheck:tests` running next to lint in the "Lint and
type check" job, so if
you add a step to `npm run ci`, add it to one of those groups and CI picks it
up.
+7 -6
View File
@@ -17,11 +17,11 @@ workflow and [Projects](user-guide/projects.md) for saving and reopening data.
| Shapefile | `.shp` with companion files, or `.zip` | Vector Layer | Keep `.shx`, `.dbf`, and `.prj` companions together; use a ZIP for browser imports. |
| GML | `.gml` | Vector Layer | Imported through the vector reader. |
| MapInfo TAB | `.tab` and companion files | Vector Layer | Requires access to the dataset's companion files. |
| KML / KMZ | `.kml`, `.kmz` | KML / KMZ Layer or Vector Layer | Supports folders, styles, ground overlays, embedded models, and Super-Overlays; behavior depends on the renderer. |
| KML / KMZ | `.kml`, `.kmz` | KML / KMZ or Vector Layer | Supports folders, styles, ground overlays, embedded models, and Super-Overlays; behavior depends on the renderer. |
| GPX | `.gpx` | GPX Layer | Waypoints, tracks, and routes can become separate layers. |
| LandXML | `.xml`, `.landxml`, LandXML URL | LandXML Layer | Imports TIN surfaces, horizontal alignments, vertical profile metadata, and survey points; projected data requires a source CRS. |
| Delimited text | CSV, TSV, and custom-delimited text | Delimited Text Layer | Map coordinate columns, specify their CRS, or geocode address columns. CSV without coordinates can also be loaded as a table. |
| Excel | Excel workbooks | File import | Select a worksheet and coordinate columns to create point features. |
| Excel | Excel workbooks | Delimited Text Layer | Select a worksheet and coordinate columns to create point features. |
| AutoCAD | `.dxf`, `.dwg` | CAD (DXF/DWG) Layer | Select drawing layers and the source CRS. Coordinate Z values are kept and rendered in 3D unless you turn that off. |
| Esri File Geodatabase | `.gdb` folder | File Geodatabase (GDB) | Desktop folder access; select a feature class. |
| OpenStreetMap PBF | `.osm.pbf` | OSM PBF Layer | Reads an extract in the browser and adds the selected features. |
@@ -76,9 +76,9 @@ are described in [Web Services](user-guide/web-services.md) and
| LiDAR point clouds | LiDAR Layer | LAS/LAZ and cloud-optimized point-cloud data. The layer menu exports a cloud to LAS, LAZ or COPC, and Whitebox LiDAR tool outputs load as new point cloud layers. LAS, LAZ and COPC files can also be dropped on the map. |
| Gaussian splats | Gaussian Splatting | Point-based scene rendering. |
| OGC 3D Tiles | 3D Tiles Layer | Streamed 3D tilesets; authenticated tilesets can use request headers. |
| ArcGIS I3S | I3S integration | Integrated Mesh and 3D Object scene layers. |
| Cesium ion assets | Cesium ion | Assets hosted on Cesium ion; requires the Cesium renderer. |
| CZML | CZML Layer | Time-dependent scene descriptions; requires the Cesium renderer. |
| ArcGIS I3S | 3D Tiles Layer (SceneServer URL) | Integrated Mesh and 3D Object scene layers. |
| Cesium ion assets | Cesium Ion Asset | 3D Tiles tilesets or imagery hosted on Cesium ion; requires the Cesium renderer. |
| CZML | CZML Dynamic 3D Scene | Time-dependent scene descriptions; requires the Cesium renderer. |
| glTF / GLB | 3D Model (glTF) | Place a local or remote model at coordinates with scale, rotation, and altitude. |
| Georeferenced video | Video Layer | Video draped over four map corner coordinates. |
| Geotagged photos | Geotagged Photos | EXIF GPS locations with thumbnails; photos without coordinates can be placed manually. |
@@ -90,6 +90,7 @@ are described in [Web Services](user-guide/web-services.md) and
| --- | --- | --- |
| DuckDB / DuckDB Spatial | DuckDB Layer or SQL Workspace | Query local files, remote data, or a database and add spatial results to the map. |
| PostgreSQL / PostGIS | PostgreSQL Layer or Browser panel | Desktop database connections use a local tile server. |
| SQL Server / Azure SQL | SQL Server Layer or Browser panel | Desktop only; spatial tables load as editable layers. |
| Apache Iceberg | Apache Iceberg Layer | Read spatial tables through DuckDB. |
| PostGIS via PGlite | SQL Workspace | In-browser spatial SQL engine. |
| Apache Sedona | SQL Workspace | An additional spatial query engine. |
@@ -98,7 +99,7 @@ are described in [Web Services](user-guide/web-services.md) and
| Format | Entry point | Notes |
| --- | --- | --- |
| GeoLibre project (`.geolibre.json`) | Project > Open | Layers, styles, groups, and map state; remote sources and local file references still need to be accessible. |
| GeoLibre project (`.geolibre.json`) | Project > Open From | Layers, styles, groups, and map state; remote sources and local file references still need to be accessible. |
| QGIS project (`.qgs`, `.qgz`) | Project > Import | Imports supported layers and settings; reports skipped sources. Local paths require desktop access. |
| ArcGIS Pro project or map (`.aprx`, `.mapx`) | Project > Import | Imports supported layers and settings; see the [project import guide](user-guide/projects.md#importing-an-arcgis-pro-project) for requirements. |
+1 -1
View File
@@ -44,7 +44,7 @@ measurements match the body you are mapping. Switch bodies from the planet
switcher in the Layers panel.
The deep-space starfield behind each globe comes from the
[Atmosphere Effects plugin](features.md#plugins).
[Atmospheric Effects plugin](features.md#plugins).
<table>
<tr>
+8 -8
View File
@@ -44,7 +44,7 @@ withheld is never on offer, whatever the profile says.
| Capability | Grants |
| --- | --- |
| `project:edit` | Authoring the project: New, Open, Open Recent, Import, Project History, Save, Save As, Duplicate, Save as Template, Collaborate, StoryMap; Undo/Redo (the menu items **and** the Ctrl/Cmd+Z and Ctrl+Y shortcuts); Export Selection; adding a review comment; the embed API's `loadProject`. |
| `project:edit` | Authoring the project: New, Open, Open Recent, Import, Project History, Save, Save As, Duplicate, Save as Template, Collaborate, StoryMap; Undo/Redo (the menu items **and** the Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z, and Ctrl+Y shortcuts); Export Selection; adding a review comment; the embed API's `loadProject`. |
| `data:add` | Bringing data in: the whole Add Data menu, dragging a file onto the map (browser and desktop), and the embed API's `addLayer` and `addData`. |
| `processing:run` | The whole Processing menu — Whitebox, SQL, Python, the AI assistant, geocoding, Model Builder, conversion/vector/raster tools — and the embed API's `openTool`. |
| `export:data` | Getting data or a rendering back out: Share, Export HTML, Print, Print Layout, Offline Basemap, and the embed API's `exportImage`. |
@@ -65,10 +65,10 @@ Docker runtime override:
```bash
docker run --rm -p 8080:80 \
-e GEOLIBRE_CAPABILITIES=data:add,export:data \
geolibre-policy:local
ghcr.io/opengeos/geolibre:latest
```
Build `geolibre-policy:local` from merged `main` as described in
Runtime policy support ships in the published image from v3.3.0 on; see
[Deployment Policy](deployment-policy.md#docker). `none` grants nothing with
the environment variable; JSON uses `"capabilities":[]` for the same empty
grant. Omitted sources grant the default full set.
@@ -107,13 +107,13 @@ do not configure Docker nginx route enforcement.
A kiosk or exhibit terminal, with no optional capabilities:
```bash
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=none geolibre-policy:local
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=none ghcr.io/opengeos/geolibre:latest
```
Allow visitors to export an image:
```bash
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=export:data geolibre-policy:local
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=export:data ghcr.io/opengeos/geolibre:latest
```
A classroom instance with project authoring, data and processing tools, and
@@ -122,12 +122,12 @@ export, but no plugin installs or settings:
```bash
docker run --rm -p 8080:80 \
-e GEOLIBRE_CAPABILITIES=project:edit,data:add,processing:run,export:data \
geolibre-policy:local
ghcr.io/opengeos/geolibre:latest
```
An embedded map can use an empty grant to refuse commands such as
`loadProject`, `addLayer`, `addData`, `openTool`, and `exportImage`, while
`setView`, `highlight`, and layer-visibility commands remain available.
`setView`, `highlightFeature`, and layer-visibility commands remain available.
## Embed API behavior
@@ -135,7 +135,7 @@ A denied command rejects rather than silently doing nothing, so the host page
can tell the difference between "refused" and "no effect":
```js
await map.addData({ url: "https://example.com/data.geojson" });
await map.addData("https://example.com/data.geojson");
// Error: Missing data:add capability
```
+12 -12
View File
@@ -28,10 +28,10 @@ overrides field by field, and writes the generated public policy on every boot
(see [Docker](#docker)). The client then resolves settings in this order:
final policy, `window.__GEOLIBRE_DEPLOYMENT_ENV__`, then build environment.
**Availability:** Runtime policy delivery and Docker enforcement live on
`main`. Published Docker images and desktop releases may predate them. If a
Docker image does not honour `deployment.json`, build the image from `main`.
For desktop, use a build that includes this support.
**Availability:** Runtime policy delivery, desktop config-dir loading, and
Docker enforcement first shipped in v3.3.0. Older Docker images and desktop
releases ignore `deployment.json`; use v3.3.0 or later (the
`ghcr.io/opengeos/geolibre:latest` image is rebuilt from `main`).
The previous `admin-profile.json` and `VITE_GEOLIBRE_CAPABILITIES` inputs are
**legacy, still honoured**. They remain fallbacks where the primary policy does
@@ -82,7 +82,7 @@ For capability details and Docker route enforcement, see
"hiddenDataSources": ["arcgis"],
"hiddenPlugins": ["plugin-a"],
"hiddenMenus": ["help"],
"hiddenMenuItems": ["file.print"]
"hiddenMenuItems": ["project.print"]
},
"plugins": {
"registryUrl": "https://plugins.example.com/registry.json",
@@ -166,15 +166,15 @@ An array of the capability names from
| Field | Type | Meaning |
| --- | --- | --- |
| `shareUrl` | string | Projects server URL (`http(s)://…`), or `off` to remove Share and the Gallery. |
| `collabUrl` | string | Live collaboration relay (`ws(s)://…`). |
| `shareUrl` | string | Projects server URL (`https://…`; plain `http://` only on `localhost`/`127.0.0.1`), or `off` to remove Share and the Gallery. |
| `collabUrl` | string | Live collaboration relay (`wss://…`; plain `ws://` only on loopback). |
| `embedOrigins` | string[] | Origins allowed to drive a framed app (`https://host`), or `*` for any. |
### `geolens`
| Field | Type | Meaning |
| --- | --- | --- |
| `url` | string | Default GeoLens server (`http(s)://…`), `same-origin`, or `off`. |
| `url` | string | Default GeoLens server (`https://…`; plain `http://` only on loopback in the container), `same-origin`, or `off`. |
### `ai`
@@ -197,6 +197,7 @@ mean opposite things. Omitted means "no restriction from this file"; `[]` means
"nothing is granted/allowed".
## Plugin precedence
An id in `blocked` is never loaded, even if it is also in `allowed`. When
`allowed` is present, any non-bundled external id not in it is not loaded.
@@ -318,16 +319,15 @@ blank values count as unset. Invalid input stops boot and cannot be repaired by
an override.
```bash
docker build -t geolibre-policy:local .
docker run --rm -p 8080:80 \
-v "$PWD/deployment.json:/etc/geolibre/deployment.json:ro" \
-e GEOLIBRE_DEPLOYMENT_FILE=/etc/geolibre/deployment.json \
-e GEOLIBRE_CAPABILITIES=data:add,export:data \
geolibre-policy:local
ghcr.io/opengeos/geolibre:latest
```
Build the image from merged `main` to obtain runtime policy support until a
containing release is verified; configuration changes do not require rebuilding.
Configuration changes take effect on the next container start; they do not
require rebuilding the image.
| Variable | Policy field |
| --- | --- |
+3 -1
View File
@@ -31,10 +31,11 @@ older versions.
Release builds are produced for:
- Linux x64: Debian package, RPM package, and AppImage
- Windows x64: unsigned desktop binary
- Windows x64: unsigned `.msi` and `-setup.exe` installers, a portable `.zip`, and the bare `.exe`
- macOS Apple Silicon: Developer ID signed and notarized DMG and app bundle (v1.4.1+)
- macOS Intel: Developer ID signed and notarized DMG and app bundle (v1.4.1+)
- Android: signed APKs, one per ABI (`geolibre-android-arm64.apk` and friends)
- Chrome extension: a packaged ZIP (`geolibre-chrome-<version>.zip`)
The Windows GitHub Release build is unsigned and may require a platform-specific
trust prompt; the [Microsoft Store](#windows-installation) build is signed and
@@ -160,6 +161,7 @@ shipping them broken. Here is how the two macOS builds compare:
| SQL Workspace (DuckDB-WASM, PGlite/PostGIS, in-browser Apache Sedona on CereusDB) | Yes | Yes |
| Python sidecar engines (GeoPandas vector, rasterio raster, GDAL conversion, SamGeo segmentation, the SedonaDB sidecar behind the Apache Sedona engine) | Yes | No |
| Add Data → PostgreSQL / PostGIS (martin tile server) | Yes | No |
| Add Data → SQL Server, File Geodatabase (sidecar-backed) | Yes | No |
| Notebook panel on a local JupyterLab server | Yes | JupyterLite only |
| Installing external plugins from a zip or the registry | Yes | Built-in and bundled plugins only |
| Earth Engine sign-in | Yes | No |
+22 -12
View File
@@ -4,12 +4,12 @@ A feature-by-feature list of what GeoLibre can do today — the latest release p
what has landed on `main` since (see [Recently added](index.md#recently-added)).
For task-oriented walkthroughs, see the [User Guide](user-guide/interface.md) and
the [Tutorials](tutorials/index.md); for what is planned next, see the
[Roadmap](roadmap.md). For how this stacks up against QGIS, ArcGIS, Felt, and
kepler.gl, see the [Comparison](comparison.md).
[Roadmap](roadmap.md). For how this stacks up against QGIS, ArcGIS, CARTO, Felt,
kepler.gl, and Google Earth, see the [Comparison](comparison.md).
## Platforms and interface
- Runs across desktop (Tauri), web (browser), native Android (Tauri v2 mobile), and mobile or small screens
- Runs across desktop (Tauri), web (browser), native Android and iOS (Tauri v2 mobile), and mobile or small screens
- Responsive, touch-friendly layout that adapts menus, dialogs, and panels; on phones the Layers and Style panels overlay the map as slide-over sheets
- Per-panel visibility through Layout settings
- Command palette (`Ctrl`/`Cmd` + `K`) that searches and runs menu and toolbar actions across Add Data, Processing, Controls, Plugins, and Help
@@ -19,10 +19,11 @@ kepler.gl, see the [Comparison](comparison.md).
- Customizable UI profiles that tailor which menus, panels, and data sources are visible, so a deployment can present a focused subset of the app to its users. See [UI Profiles](ui-profiles.md)
- Internationalization framework with react-i18next and 20 complete per-build translation catalogs — including right-to-left Arabic, Persian, and Hebrew with a fully mirrored interface, Persian set in Vazirmatn — plus a `?locale`/`?lang` query parameter to set the embed language
- Plugin display names resolve through one place, so a plugin reads the same in the Plugins menu, the command palette, Settings, and Manage Plugins, and every processing tool name, description, group label, parameter, and select option is generated into the English catalog for translators to work from. See [Internationalization](i18n.md)
- The Whitebox toolbox is translated too: its 1066 tool names, 45 subcategory labels, and unlocked tool parameters are generated into the catalogs and rendered through `t()` in the Processing menu, the tool dialog, and Model Builder search, with Simplified Chinese complete and other locales falling back to English per string
- The Whitebox toolbox is translated too: its 1068 tool names, 45 subcategory labels, and unlocked tool parameters are generated into the catalogs and rendered through `t()` in the Processing menu, the tool dialog, and Model Builder search, with Simplified Chinese complete and other locales falling back to English per string
- Accessibility pass with axe-checked screens, keyboard navigation, and screen-reader labels
- App-wide, section, and plugin React error boundaries that contain failures and keep the rest of the workspace usable
- Undo/redo for layer and style operations
- **View → Color vision preview** that simulates protanopia, deuteranopia, tritanopia, and achromatopsia on the map, and an Examples section of starter projects in the New Project dialog
- A design system of the app's own: self-hosted IBM Plex Sans and IBM Plex Mono (no CDN, so the desktop build renders offline under its CSP) instead of whatever the platform's UI font happens to be, a dark-mode elevation ladder that separates canvas, panels, dialogs, and menus by surface rather than by a 1px border, theme-aware shadows that stay visible in dark mode, and one translucent map-glass treatment shared by every piece of chrome that floats over the map
## Map workspace and basemaps
@@ -48,9 +49,9 @@ kepler.gl, see the [Comparison](comparison.md).
## Rendering engines and 3D globe
- **Four interchangeable rendering engines**: **View → Rendering engine** switches the primary workspace among MapLibre GL JS, Mapbox GL JS, CesiumJS, and the ArcGIS Maps SDK for JavaScript. Every split pane can independently use any of the four. Engine choices are saved with the project; the camera, layers, groups, visibility, opacity, and supported styles survive a switch. Menus, tools, and plugins are gated on the active engine's capabilities. See [Rendering engines](user-guide/rendering-engines.md).
- **Engine-specific strengths**: MapLibre is the default and has the broadest plugin support; Mapbox supplies Mapbox styles, globe, and terrain; Cesium is the native globe for terrain, 3D Tiles, CZML, ion, and I3S; ArcGIS renders native ArcGIS services in 2D maps and 3D scenes. Mapbox requires an access token; Cesium requires a Cesium ion token (the web version bundles a demo token, desktop and mobile need your own); ArcGIS works without credentials and uses an optional API key for Esri basemap styles. See the [Mapbox](mapbox-renderer.md) and [ArcGIS](arcgis-renderer.md) references.
- **Engine-specific strengths**: MapLibre is the default and has the broadest plugin support; Mapbox supplies Mapbox styles, globe, and terrain; Cesium is the native globe for terrain, 3D Tiles, CZML, ion, and I3S; ArcGIS renders native ArcGIS services in 2D maps and 3D scenes. Mapbox requires an access token; Cesium works without a token, and a Cesium ion token adds World Terrain and ion assets (the web version bundles a demo token, desktop and mobile need your own); ArcGIS works without credentials and uses an optional API key for Esri basemap styles. See the [Mapbox](mapbox-renderer.md) and [ArcGIS](arcgis-renderer.md) references.
- **Cesium as the primary rendering engine**: selecting Cesium replaces the whole workspace with its 3D globe; the previous canvas is unmounted rather than hidden. See [Architecture](architecture.md#3d-globe-view-cesiumjs).
- A Cesium Ion token is optional: without one the globe draws the project's basemap on a bare ellipsoid, and with one it adds Cesium World Terrain and Ion imagery. See [Optional 3D globe credentials](getting-started.md#optional-3d-globe-credentials-cesium-ion)
- A Cesium Ion token is optional: without one the globe draws the project's basemap and takes terrain from keyless Terrarium elevation or a configured COG, and with one it adds Cesium World Terrain and Ion imagery, and any quantized-mesh Cesium Ion terrain asset available to the account can be used as the terrain source. See [Optional 3D globe credentials](getting-started.md#optional-3d-globe-credentials-cesium-ion)
- **Globe chrome**: Reset view, fullscreen, and a 3D / 2D / Columbus scene-mode picker (flat modes in Web Mercator to match the project map), movable between corners; a native basemap and terrain gallery with Ion imagery plus keyless Esri and OpenStreetMap styles, with a Project basemap option; cursor coordinates, elevation, and the active projection in the shared status bar
- **Vector layers at parity with the 2D map**: per-feature single, categorized, graduated, rule-based, expression, proportional, and marker symbology compiled from the same paint expressions; field and expression labels; point clustering and batched rendering for large point layers; polygon extrusion and true Z elevation; layer filters, the Time Slider clock, and story-map opacity; click popups, hover tooltips, identify, and highlighting
- **Rasters, tiles, and 3D content**: COG, raster PMTiles, local MBTiles, and native tile fetchers through a protocol bridge; vector tiles, vector PMTiles, and vector MBTiles draped through a hidden MapLibre renderer so styles match exactly; ArcGIS MapServer, WMTS, and image overlays; I3S scene layers, LiDAR point clouds, and Gaussian splat tilesets loaded natively
@@ -66,13 +67,14 @@ kepler.gl, see the [Comparison](comparison.md).
- Large local vector layers render through client-side vector tiling, with a warning before loading very large files
- Add Data menu covering every remote and cloud-native source:
- **URL deep links**: open GeoJSON, GeoParquet, PMTiles, a REST endpoint returning a GeoJSON FeatureCollection, a COG, a LiDAR point cloud (LAS/LAZ/COPC/EPT, with `&dataType=lidar` for an extensionless API endpoint), or a ZIP/REST response containing multiple GeoJSON files with `?data=`, repeated as many times as you have sources; optionally apply vector or raster style JSON with `?style=`, automatically fit the layer extent, and associate per-file ZIP styles by filename stem; `?stac=` opens a STAC catalog or API collection
- **Tile and map services**: XYZ tiles; WMS and WFS, with layers and feature types discovered from the service's GetCapabilities so you pick from a populated dropdown; vector tiles, including OGC API - Tiles services; and ArcGIS FeatureServer, VectorTileServer, MapServer, and ImageServer layers. The last two load as ordinary raster layers, so opacity, the Style panel's brightness/contrast/saturation, reordering, and project save all apply, drawing from the service's own fused cache when it was built on the standard Web Mercator scheme and from `/export` or `/exportImage` otherwise. MapServer sublayers and ImageServer raster functions are browsed and picked from each service's advertised list, with custom raster function JSON still available for advanced ImageServer rules
- **Tile and map services**: XYZ tiles; WMS and WFS, with layers and feature types discovered from the service's GetCapabilities so you pick from a populated dropdown; vector tiles, including OGC API - Tiles services; and ArcGIS FeatureServer, VectorTileServer, MapServer, and ImageServer layers. The last two load as ordinary raster layers, so opacity, the Style panel's brightness/contrast/saturation, reordering, and project save all apply, drawing from the service's own fused cache when it was built on the standard Web Mercator scheme and from `/export` or `/exportImage` otherwise. MapServer sublayers and ImageServer raster functions are browsed and picked from each service's advertised list, with custom raster function JSON still available for advanced ImageServer rules. Secured ArcGIS services take a manual access token or **Sign in with ArcGIS** (OAuth with PKCE against ArcGIS Online or an Enterprise portal), with the session held in memory and never saved to the project. A WMS layer's legend is shown in its Style panel and can be added to the map as an HTML control
- **Feature services and feeds**: GeoJSON URLs; GeoRSS feeds from a URL or file; and OGC API - Features collections added as vector layers from whatever URL you have in hand — a landing page, `/collections`, a collection, or a full items URL
- **Raster**: COG and GeoTIFF; Cloud-Optimized NetCDF/HDF via kerchunk references, plus local HDF5 and NetCDF-4 files; and MBTiles
- **Cloud-native archives**: PMTiles, and Zarr from a remote store, an Icechunk repository, or a folder on disk, with variable and dimension pickers that offer the store's real coordinate values rather than raw indices. A tiled archive carrying several source layers is added as a group of them — one named, individually styleable layer per source layer — instead of a single opaque row
- **Files with pickers**: multi-layer GeoPackages, with a layer picker so only the chosen feature tables load; Excel workbooks imported as point layers from their X and Y columns, with a worksheet picker; delimited text with a source CRS field so projected easting/northing columns reproject correctly, or an Addresses mode that concatenates the columns you pick and geocodes each row through the project's provider (unmatched rows are kept as null-geometry features so they stay visible and fixable in the attribute table); CAD drawings (DXF/DWG) with a drawing-layer picker and CRS selector; and Esri File Geodatabases (`.gdb` folders, desktop) with a feature-class picker and automatic reprojection
- **3D and media**: LiDAR; 3D Tiles, including authenticated tilesets via custom request headers; ArcGIS I3S scene layers (Integrated Mesh and 3D Object, rendered on deck.gl); Gaussian splats; glTF/GLB 3D models placed at coordinates, from a URL or a local file embedded in the project; georeferenced video overlays; and geotagged photos imported as a point layer from their EXIF GPS, with manual placement and drag for photos lacking coordinates and a true native-resolution photo viewer
- **[WCS raster subsets](data-formats.md#wcs-raster-subsets)**: discover WCS 1.0.0 coverages and download GeoTIFF subsets in EPSG:4326 or EPSG:3857 with explicit extent and pixel dimensions, preserving numerical values for raster inspection and analysis
- **Administrative boundaries**: a country's ADM0 to ADM5 boundaries (depending on the country) from the open geoBoundaries release, in simplified or full geometry
- **Catalog search**: an OGC CSW 2.0.2 source that searches a catalog's records by keyword, saves the connection to the service library, adds GeoJSON resources directly, and hands WMS, WFS, and ArcGIS results to their prefilled Add Data forms
- **Throughout**: a fully internationalized dialog, comma decimal support, drag-and-dropped CSV coordinate files, sample-data dropdowns on every upstream-backed panel for loading ready-made example datasets, and a saved service library for storing and re-adding frequently used web-service endpoints
- Encoded polylines as a first-class format: a codec for Google and OSRM precision 5 and Valhalla and Mapbox precision 6, with an interactive preview and coordinate inspector in Add Data, batch parsing with custom delimiters, processing tools for encoding and decoding, layer export at either precision, and matching support in the Python package
@@ -89,7 +91,7 @@ kepler.gl, see the [Comparison](comparison.md).
- ArcGIS Hub, Socrata, and CKAN (Humanitarian Data Exchange) open-data browsers under Plugins → Web Services: search public dataset catalogs by keyword (or restrict the ArcGIS Hub search to the current map area), page through results, and add a dataset to the map or download it
- OSM Downloader under Plugins → Web Services: query buildings, roads, amenities, waterways, land use, all tagged features, or a custom tag from the current view or typed bounds through Overpass, then add the result as GeoJSON or save it to disk
- Drag and drop vector and GeoTIFF/COG raster files onto the map to add them as layers
- **Open data in GeoLibre**, a [Chrome extension](https://chromewebstore.google.com/detail/open-data-in-geolibre/joinecgbfoldanidcoakpjgkbaceaooj) on the Chrome Web Store, that collects the supported dataset links and map services (WMS, WMTS, WFS, OGC API - Features, ArcGIS Feature Services, XYZ/TMS, and vector tiles) on the page you are viewing and opens the ones you pick together on one GeoLibre map
- **Open data in GeoLibre**, a [Chrome extension](https://chromewebstore.google.com/detail/open-data-in-geolibre/joinecgbfoldanidcoakpjgkbaceaooj) on the Chrome Web Store, that collects the supported dataset links and map services (WMS, WMTS, WFS, OGC API - Features, ArcGIS Feature Services, XYZ/TMS, and vector tiles) on the page you are viewing and opens the ones you pick together on one GeoLibre map; an options page points it at a self-hosted GeoLibre instead of the hosted app
## Layers, styling, and labels
@@ -100,7 +102,6 @@ kepler.gl, see the [Comparison](comparison.md).
- A Search places box in the footer that geocodes to a location, flies straight to a typed coordinate in decimal degrees, DMS, or DDM, or flies to an H3 cell index typed as either a hexadecimal string or a 64-bit integer (framing and outlining the cell) — all without leaving the panel
- The same box also searches the loaded vector layers' own attributes, so a parcel id, a station code, or a well name is found in the data you just added. Matches are grouped by layer above the place results and ranked exact match first, then prefix, then contains; picking one selects that feature on its layer and flies to it with the attribute table's highlight. Fields marked hidden or excluded are skipped, as are hidden layers and tile-backed layers that hold no local features
- Nested layer groups that give the layer stack a real hierarchy. See [Layer groups](user-guide/layers.md#layer-groups)
- Per-layer capabilities that mark a layer read-only or restrict it to creating, updating, or deleting features, enforced in the UI and again on the backend, so a reference layer in a shared project stays as its author left it
- Create a group from scratch, from the current layer, or from a multi-selection
- Move one layer or a whole selection into a group in one step, or add new data straight into a group
- Set a group-level opacity, collapse and expand groups, and reorder them
@@ -108,6 +109,7 @@ kepler.gl, see the [Comparison](comparison.md).
- Sort a group's contents A to Z or Z to A
- Hiding a group hides its layers, and a layer suppressed that way is marked as such rather than looking like one you switched off
- The on-map Layer Control shows the same group tree, with per-group visibility, opacity, and collapse kept in sync with the Layers panel, and drag or context-menu reordering that keeps a layer inside its group
- Per-layer capabilities that mark a layer read-only or restrict it to creating, updating, or deleting features, enforced in the UI and again on the backend, so a reference layer in a shared project stays as its author left it
- Auto-generated on-map Legend panel derived from the visible layers' symbology
- Per-class rows for graduated, categorized, rule-based, and expression styling; gradient bars for heatmaps and continuous raster colormaps; proportional-symbol size ramps; diagram fields; and land-cover labels from a Raster Attribute Table
- An edit mode for renaming, hiding, and reordering entries, adding a section from a color dictionary, choosing a corner, collapsing sections, resizing the panel, and exporting the rendered legend as JSON
@@ -116,7 +118,7 @@ kepler.gl, see the [Comparison](comparison.md).
- Live style panel
- **Style suggestions**: a dismissible strip offering up to three one-click renderers derived from the layer's own attributes — categorize by a low-cardinality label, graduate by the numeric column with the most spread, or a heatmap for a dense point layer — shown while the layer still carries its as-added symbology
- **Vector-tile classification**: PMTiles, MBTiles, and remote vector-tile layers carry no local features, so the panel samples the features the viewport has already loaded to fill in the field list and the values graduated and categorized styling need, and 3D extrusion defaults to a real height property
- **Renderers**: single, categorized, graduated, expression, and rule-based (filter-driven) symbology over fill, stroke, opacity, and circle radius, plus proportional symbols, fill patterns, a built-in marker library, and point heatmap (with a color ramp and optional numeric attribute weighting) and clustering renderers — all including for Add Vector Layer point layers
- **Renderers**: single, categorized, graduated, expression, and rule-based (filter-driven) symbology over fill, stroke, opacity, and circle radius, with graduated classes by equal interval, quantile, natural breaks, standard deviation, geometric interval, or manual breaks (the method is named in the legend), plus proportional symbols, fill patterns, a built-in marker library, custom point markers from an uploaded SVG, PNG, JPEG, or GIF image, and point heatmap (with a color ramp and optional numeric attribute weighting) and clustering renderers — all including for Add Vector Layer point layers
- **Cartographic blend modes** (Multiply, Screen, Lighten, Add) that composite a layer with the map beneath it instead of just fading it, so colour or imagery over a hillshade keeps the relief visible at full saturation; applied inside MapLibre's render loop, so it works on vector and raster layers alike, in the right z-position, with labels left legible. See [Blend modes](user-guide/layers.md#blend-modes)
- **Color**: an inline color ramp picker that previews each colormap's gradient on the trigger and beside every option, plus a transparent (no fill / no outline) option in the color picker
- **Rule-based renderer**: per-rule symbol properties, scale-dependent visibility, nested rules, and per-rule toggles, and it can hide features matching no rule
@@ -150,7 +152,7 @@ kepler.gl, see the [Comparison](comparison.md).
## Attribute data and expressions
- Attribute table
- **Browsing**: filtering, sorting, resize controls, feature highlighting with Ctrl- and Shift-click multi-row selection, optional zoom to selected features, and virtualized rows for large layers
- **Browsing**: filtering, sorting, resize controls, feature highlighting with Ctrl- and Shift-click multi-row selection, optional zoom to selected features, virtualized rows for large layers, and a Refresh button that reloads features from a refreshable source so other clients' edits show up
- **Editing and derived fields**: add-field and field-calculator tools (including geometry length and area calculation), virtual fields (expression-backed computed columns that update with the data), persistent attribute joins configured in layer properties, and automatic editor tracking (created by/at and edited by/at)
- **Forms**: an attribute form designer with edit widgets, validation constraints, and conditional field visibility
- **Analysis**: a Charts panel (histogram, scatter, bar, line, box) and a field statistics summary panel, scoped to the whole layer or to just the selected features
@@ -188,6 +190,7 @@ kepler.gl, see the [Comparison](comparison.md).
- Controls menu
- Measure (including terrain-aware 3D measurements and a heading readout — a true great-circle initial bearing with a 16-point compass label, plus a final bearing on lines long enough for the great circle to converge), Bookmark, Minimap, View State, and a Search panel
- Colorbar, Legend, HTML, and Image map controls; the Image control places one or more collapsible images from a URL in a chosen map corner, sized by width, width and height, or width and aspect ratio, and is saved with the project
- Map annotation tools that draw text, arrows, and highlights on the map, saved with the project, on a toolbar that collapses to a single button when the drawing tools are not in use
- Persistent mode banners for the Directions and Reverse Geocode tools
- A Camera Tour recorder that captures an animated keyframe tour to video, with per-keyframe recapture, per-keyframe hold and transition duration controls, and saving or loading a named tour setup as JSON
@@ -303,16 +306,18 @@ kepler.gl, see the [Comparison](comparison.md).
- Reusable project templates saved to a personal library, with an option to keep the basemap, groups, styles, legend, widgets, and layout while stripping the data layer content
- Startup preferences on the desktop app: open the default workspace, reopen the last local project, or always open one chosen project, and choose whether an empty workspace starts as a 3D globe or a Mercator map. Remote share links are never replayed on launch, a project URL in the address bar takes precedence, and a startup project that has gone missing falls back to the default workspace with an explanation instead of an error. See [Settings](user-guide/settings.md#startup)
- A configurable startup map view — longitude, latitude, and zoom — so an empty workspace opens over the area you work in rather than on the whole world. See [Settings](user-guide/settings.md#startup)
- A Map Preferences option to stop zooming to newly added layers, so adding data or running a tool keeps the current view
## Plugins
- Built-in plugins for the map surface: basemap, layer control, MapLibre components, and swipe
- Web Services plugins dock in the shared panel host rather than floating over the map, so a catalog browser resizes, collapses, and sits alongside the Layers and Style panels while keeping its own MapLibre control lifecycle and layer sync
- **God's Eye View** turns the Cesium globe into a live situation room: USGS earthquakes and CelesTrak satellite orbits as live CZML, keyless NASA FIRMS active fires, public transit (Entur and others), public CCTV and traffic cameras (Austin, New South Wales, Ontario, DriveBC), TomTom live traffic flow, and AIS vessels, with API key entry for the feeds that need one
- **God's Eye View** turns the Cesium globe into a live situation room: USGS earthquakes and CelesTrak satellite orbits as live CZML, keyless NASA FIRMS active fires, public transit (Entur and others), public CCTV and traffic cameras (Austin, New South Wales, Ontario, DriveBC), TomTom live traffic flow, AIS vessels, and radio stations, with API key entry for the feeds that need one and per-feed options saved with the project (a category filter for radio stations, on-map snapshot previews for CCTV cameras)
- Imagery and street level: street view, Mapillary coverage and street-level image viewer, OpenAerialMap open-aerial-imagery search, and Historical Imagery
- Data catalog browsers:
- **Natural Earth** and **Source Cooperative**, including opening or streaming large GeoParquet from Source Cooperative
- **STAC catalogs**, which discovers catalogs from STAC Index, connects to both static catalogs and STAC APIs, searches a collection's items, and adds any visualizable asset as a layer (COG, PMTiles, GeoParquet, and Zarr with a variable picker), including an Icechunk repository read through its own manifest and Azure-hosted assets signed at add time so private Planetary Computer containers open like public ones. A static catalog can also be walked as a tree, opening a folder to read it and starting a search from whatever you picked
- **ArcGIS Portal**, which lets a signed-in ArcGIS user browse their own content, favorites, groups, organization, or the whole portal and add items to the map, sharing the Add Data sign-in session
- **Earthdata GIS**, which searches NASA's EOSDIS ArcGIS portal and renders its imagery, map, and feature services and published web maps as first-class layers
- **Planet Open Data**, which opens Planet's disaster releases through the STAC browser with the public catalog preselected
- **[Portolan](https://www.portolan-sdi.org/)**, available under Plugins → Web Services → Portolan, opens the public Portolan Registry with searchable catalog discovery. Select a catalog or enter a custom catalog URL, browse its collections, then double-click a collection to show assets that can be added to the map using the STAC browser's existing loaders
@@ -331,6 +336,7 @@ kepler.gl, see the [Comparison](comparison.md).
- Elevation Profile charts a drawn line or the line features currently selected on a layer, so a route already on the map does not have to be traced again
- USGS LiDAR clips a point cloud to an area of interest and downloads the result as COPC
- IGN LiDAR HD browses and loads France's national LiDAR HD point clouds
- Whitebox LiDAR tool outputs load on the map as point cloud layers, and a LiDAR layer exports as LAS, LAZ, or COPC from its layer menu
- Point Cloud Annotation labels LiDAR points on the map with ASPRS or custom classes, instances, polygon-to-point lifting, Whitebox pre-labels, oriented 3D boxes, and 3D vectors, saves labels with the project, and exports a labelled LAS/LAZ, NumPy arrays, or GeoJSON. See [Point cloud annotation](user-guide/point-cloud-annotation.md)
- The GeoEditor can pull the vector features currently visible in the map view into the editor for editing without re-importing the source, and write edits back to their origin, including GeoPackage and GeoJSON files and PostGIS database tables
- GeoEditor sketches export as an ordinary project layer, carrying their style, opacity, and visibility, so drawn work leaves the editor without being redrawn or re-imported
@@ -357,6 +363,10 @@ kepler.gl, see the [Comparison](comparison.md).
- DGGS plugins under Plugins → DGGS for three more discrete global grid systems — **A5**, **DGGRID**, and **DGGAL** — each rendering its grid over the current view at a chosen or automatic resolution with a cell-count guard, identifying a cell to read its id, parents, children, neighbors, and center, and adding the grid or the selection to the map as a layer or exporting it as GeoJSON
- Atmosphere Effects plugin that renders a deep-space backdrop, parallax starfield, comets, and an atmospheric halo around the globe at low zoom (technique adapted from [Leonel Dias](https://leoneljdias.github.io/posts/globe-atmosphere-halo-comets/)), with a Spinning Globe panel and customizable atmosphere halo and deep-space colors
- Directions plugin for interactive routing via [maplibre-gl-directions](https://github.com/maplibre/maplibre-gl-directions): click the map to add waypoints, drag to reposition, and click a waypoint to remove it (uses the public OSRM demo server, driving only)
- Install, update, and remove external plugins from the registry marketplace, with
installs and updates verified against the published bundle hash, plugins the
registry blocklists refused even when already installed, and external plugins
grouped under **Plugins → Installed**
- Install external plugins from uploaded zips and local development directories,
subject to the client deployment plugin policy. Bundled drop-ins load
automatically unless their ids are blocked by that policy.
+14 -15
View File
@@ -52,7 +52,7 @@ The Linux app runs in WebKitGTK. At startup it picks the WebKitGTK renderer sett
| NVIDIA GPU, WebKitGTK 2.48 to 2.51 | `WEBKIT_DISABLE_DMABUF_RENDERER=1` (NVIDIA's buffer allocation fails, and there is no faster fallback yet) |
| NVIDIA GPU, WebKitGTK 2.52 or newer | `WEBKIT_DISABLE_DMABUF_RENDERER=0` and `WEBKIT_DMABUF_RENDERER_FORCE_SHM=1` (a shared-memory fallback that avoids the blank window without the slow legacy renderer) |
| Anything else | Nothing; WebKitGTK's default DMA-BUF renderer is used |
| x86-64 CPU without AVX | `JSC_useWasmOSR=false` and `JSC_useBBQTierUpChecks=false` (WebAssembly tier-up crashes the renderer on these CPUs; WebAssembly-heavy work runs slower) |
| x86-64 CPU without AVX | `JSC_useWasmOSR=false` and `JSC_useBBQTierUpChecks=false` (WebAssembly tier-up crashes the renderer on these CPUs; WebAssembly-heavy work runs slower; see [CPUs without AVX](#linux-desktop-a-blank-window-on-a-cpu-without-avx)) |
The app treats the GPU as NVIDIA when the boot display is driven by the `nvidia` driver, when `__NV_PRIME_RENDER_OFFLOAD` is set to anything but `0`, or when `__GLX_VENDOR_LIBRARY_NAME=nvidia`. It also sets `GTK_USE_PORTAL=1` so file dialogs go through the desktop portal.
@@ -145,7 +145,7 @@ cd GeoLibre
npm install
```
Bun users can run `bun install`. The root `trustedDependencies` list allows the known install scripts for `core-js`, `@google/genai`, and `protobufjs`.
Bun users can run `bun install`. The root `trustedDependencies` list allows the known install scripts for `@google/genai`, `core-js`, `esbuild`, `protobufjs`, `sharp`, and `workerd`.
### Update
@@ -495,10 +495,10 @@ capabilities rather than rebuilding the client:
```bash
docker run --rm -p 8080:80 \
-e GEOLIBRE_CAPABILITIES=project:edit,data:add,processing:run,export:data \
geolibre-policy:local
ghcr.io/opengeos/geolibre:latest
```
Build the local image from merged `main` as described in
Runtime policy enforcement needs image v3.3.0 or later; see
[Deployment Policy](deployment-policy.md#docker). The legacy
`VITE_GEOLIBRE_CAPABILITIES` build input is still honoured as a client fallback.
`none` grants no capabilities. Client gates remove menus, command palette
@@ -623,8 +623,9 @@ Production web callbacks require HTTPS and must end in
`geolibre-desktop` registration to the same JSON array when desktop sign-in is
required. The web app signs in through a popup to the server's consent page
using this registration, so the `geolibre-web` callback URL must match the web
app's own origin (including any base path). Desktop sign-in (the system
browser flow) is still pending; desktop users paste a personal API token. See
app's own origin (including any base path). The desktop app opens the consent
page in the system browser and receives the callback through the OS protocol
handler; a personal API token still works as a fallback. See
the [server API OAuth contract](server-api.md#oauth-20-sign-in-authorization-code-s256-pkce)
for the flow and lifetime settings.
@@ -883,7 +884,7 @@ Restart `npm run dev` or `npm run tauri:dev` after changing environment variable
## Optional basemap credentials
The **New map** dialog offers [Protomaps](https://protomaps.com) basemaps (Light, Dark, White, Grayscale, Black) when a Protomaps API key is configured. Without a key these options are hidden, and you can still use the OpenFreeMap basemaps or a custom style URL.
The **New project** dialog offers [Protomaps](https://protomaps.com) basemaps (Light, Dark, White, Grayscale, Black) when a Protomaps API key is configured. Without a key these options are hidden, and you can still use the OpenFreeMap basemaps or a custom style URL.
Use your own key — create one in the [Protomaps dashboard](https://protomaps.com). Set it one of two ways:
@@ -923,7 +924,7 @@ VITE_AMAZON_LOCATION_AWS_REGION=us-east-1 # optional; omit to
The Basemaps control also carries **Protomaps** and **Stadia Maps** (including Stadia x Stamen) style basemaps. Both authenticate with your own key:
```env
VITE_PROTOMAPS_API_KEY=your_protomaps_api_key # same key as the New map dialog's Protomaps basemaps
VITE_PROTOMAPS_API_KEY=your_protomaps_api_key # same key as the New project dialog's Protomaps basemaps
VITE_STADIA_API_KEY=your_stadia_api_key # https://client.stadiamaps.com
```
@@ -937,7 +938,7 @@ GeoLibre's default basemaps (OpenFreeMap, Protomaps) are hosted outside mainland
### The Regional section (no key)
**New project** and **Change basemap** both carry a collapsed **Regional → China (中国)** section with five keyless basemaps: 高德地图, 高德卫星, 高德混合 (Amap street, satellite, and satellite-with-labels) and 腾讯地图, 腾讯深色 (Tencent street and dark). Pick one and it applies like any other basemap. Nothing to configure.
**New project** and **Change background** both carry a collapsed **Regional → China (中国)** section with five keyless basemaps: 高德地图, 高德卫星, 高德混合 (Amap street, satellite, and satellite-with-labels) and 腾讯地图, 腾讯深色 (Tencent street and dark). Pick one and it applies like any other basemap. Nothing to configure.
### The Basemaps control (adds Tianditu)
@@ -967,7 +968,7 @@ Keys set via **Settings → Environment Variables**, or typed directly into the
## Optional 3D globe credentials (Cesium Ion)
The optional **Cesium 3D-globe renderer** can own the primary map or any pane in a mixed-engine split layout. It requires a [Cesium Ion](https://ion.cesium.com/) access token. The hosted web version bundles a demo token, so the globe works there out of the box, but the desktop and mobile apps need your own. The token enables Cesium World Terrain (relief on tilted views) plus Ion World Imagery as the fallback for a basemap that has no raster form. To get one, create a free Ion account, copy your default access token, and set it at build time:
The optional **Cesium 3D-globe renderer** can own the primary map or any pane in a mixed-engine split layout. It works without a token, draping the project basemap as its imagery, but a [Cesium Ion](https://ion.cesium.com/) access token unlocks more. The hosted web version bundles a demo token; the desktop and mobile apps need your own. The token enables Cesium World Terrain (relief on tilted views) plus Ion World Imagery as the fallback for a basemap that has no raster form. To get one, create a free Ion account, copy your default access token, and set it at build time:
```env
CESIUM_TOKEN=your_cesium_ion_access_token
@@ -1041,9 +1042,7 @@ extra is unavailable. See [Processing Tools](user-guide/processing.md) for what
each engine does, and [AI Segmentation](user-guide/segmentation.md) for the
separate `samgeo-api` model server.
## Linux desktop troubleshooting
### A blank window on a CPU without AVX
## Linux desktop: a blank window on a CPU without AVX
On x86-64 CPUs with no AVX (Intel Celeron and Pentium N-series, Atom, and
anything older than Sandy Bridge), WebKitGTK can kill its own renderer as soon
@@ -1071,7 +1070,7 @@ The two options only work as a pair, so GeoLibre treats them as one decision.
Turning either one back on is the opt-out and leaves both alone:
```bash
JSC_useWasmOSR=true geolibre
JSC_useWasmOSR=true geolibre-desktop
```
Setting one of them to `false` yourself is not an opt-out: GeoLibre keeps your
@@ -1082,7 +1081,7 @@ Or, if a renderer crash persists, fall back to disabling the WebAssembly JIT
entirely:
```bash
JSC_useBBQJIT=false geolibre
JSC_useBBQJIT=false geolibre-desktop
```
The check is a runtime CPU feature test, not a model or release-date list, so
+16 -9
View File
@@ -34,7 +34,12 @@ apps/geolibre-desktop/src/i18n/
├── id.json # Indonesian
├── hi.json # Hindi
├── th.json # Thai
└── ar.json # Arabic (right-to-left)
├── vi.json # Vietnamese
├── az.json # Azerbaijani
├── ka.json # Georgian
├── ar.json # Arabic (right-to-left)
├── he.json # Hebrew (right-to-left)
└── fa.json # Persian (right-to-left)
```
The non-English catalogs were seeded with machine-assisted translations and are
@@ -74,8 +79,8 @@ On startup the initial language is resolved in priority order
4. the default, `en`.
Only languages that ship a catalog are honored; anything else falls through to
the next rule. The in-app selector (the Settings menu → Language, or the Layout
tab of the Settings dialog) changes the
the next rule. The in-app selector (the Settings menu → Language, or the
Language section of the Settings dialog) changes the
language live and records the choice so it survives reloads.
The `?locale` parameter makes embeds language-aware, e.g.:
@@ -127,8 +132,8 @@ names/extensions, or persisted data values.
3. If the code isn't already in `LANGUAGE_NAMES` (`languages.ts`), add an entry
so the selector shows a friendly native name. (Optional — it falls back to
the raw code.)
4. That's it. The language appears in **Settings → Language** (and the Layout
tab of the Settings dialog) and works with `?locale=<code>`.
4. That's it. The language appears in **Settings → Language** (and the
Language section of the Settings dialog) and works with `?locale=<code>`.
## Keeping catalogs complete
@@ -141,7 +146,8 @@ bundled catalog against `en.json`:
- **Missing keys are reported, not enforced.** The test logs each locale's
missing keys. Plural keys are compared by their base, so a locale carries the
plural forms its language needs (`_other` only for zh/ja/ko/id/th/vi;
`_few`/`_many` for ru; all six for ar) rather than en's `_one`/`_other` pair.
`_two` for he; `_few`/`_many` for ru; all six for ar) rather than en's
`_one`/`_other` pair.
So a change that adds a `t()` key, or a `plugin.<id>.*` key a built-in plugin
resolves through `app.translate`, only needs to add it to `en.json`. The other
@@ -152,8 +158,8 @@ change).
## Right-to-left languages
Arabic (`ar`) is the first right-to-left catalog, and the layout direction is
wired so further RTL locales are additive:
Arabic (`ar`), Hebrew (`he`) and Persian (`fa`) ship as right-to-left
catalogs, and the layout direction is wired so further RTL locales are additive:
- **Direction registry.** `RTL_LANGUAGES` in `languages.ts` lists the
right-to-left base subtags (`ar`, `he`, `fa`, `ur`);
@@ -275,7 +281,8 @@ gives it `getLocale()`, `onLocaleChange(listener)`,
`translate(key, defaultValue, params)` and `registerTranslations(resources)`
instead, and toolbar menu labels and panel titles accept getter functions so
they can call `translate` and be re-read after a language change. See
`docs/plugin-api.md` ("Following the app language").
[Following the app language](plugin-api.md#following-the-app-language) in the
plugin API reference.
Built-in plugins keep their strings in the bundled catalogs under
`plugin.<pluginId>.*` and resolve them with `createPluginTranslator(app, id)`
+16 -16
View File
@@ -104,7 +104,7 @@ Build interactive maps in RStudio, Quarto, R Markdown, and Shiny with the [`geol
<div class="feature-card" markdown>
### AI Assistant
Chat with your data: a natural-language [assistant](user-guide/ai-assistant.md) that turns plain-English requests into GeoLibre operations — Spatial SQL, symbology, add or remove data, and map control — applied through the app so they stay auditable and undoable. Provider-pluggable (Google Gemini, Anthropic, OpenAI) with your own API key, disabled until configured.
Chat with your data: a natural-language [assistant](user-guide/ai-assistant.md) that turns plain-English requests into GeoLibre operations — Spatial SQL, symbology, add or remove data, and map control — applied through the app so they stay auditable and undoable. Provider-pluggable (Google Gemini, Anthropic, OpenAI, OpenRouter, Amazon Bedrock, a local Ollama, or any OpenAI-compatible endpoint) with your own credentials, disabled until configured.
</div>
<div class="feature-card" markdown>
@@ -144,7 +144,7 @@ Open a project by passing a public `.geolibre.json` URL with the `url` query par
https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json
```
You can also open hosted data directly. `data` accepts GeoJSON, GeoParquet, PMTiles, REST endpoints returning GeoJSON or ZIP, ZIP archives containing multiple GeoJSON files, and COGs. Add `style` to apply hosted vector or raster symbology:
You can also open hosted data directly. `data` accepts GeoJSON, GeoParquet, PMTiles, COGs, LiDAR point clouds (LAS, LAZ, COPC, or an EPT `ept.json`), REST endpoints returning GeoJSON or ZIP, and ZIP archives containing multiple GeoJSON files. Add `style` to apply hosted vector or raster symbology:
```text
https://web.geolibre.app/?data=https://assets.geolibre.app/data/places.geojson&style=https://assets.geolibre.app/data/sample.style.json
@@ -152,7 +152,7 @@ https://web.geolibre.app/?data=https://assets.geolibre.app/data/places.geojson&s
Vector layers can produce a compatible file from **Layer actions → Styles → Export GeoLibre URL style**, and apply it again with **Import style from file (GeoLibre URL / Mapbox GL / SLD / QML)…**, or with **Import style from text…** by pasting the style itself.
For narrow embeds, add `?layout=compact` to the demo URL to use icon-only toolbar buttons and hide project metadata:
For narrow embeds, add `&layout=compact` to the demo URL to use icon-only toolbar buttons and hide project metadata:
```text
https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json&layout=compact
@@ -213,19 +213,19 @@ see the [Roadmap](roadmap.md).
### Recently added
Newest capabilities, still settling in: autosave with crash recovery and a
browsable [project history](user-guide/projects.md#project-history-and-crash-recovery),
[QGIS](user-guide/projects.md#importing-a-qgis-project) and
[ArcGIS Pro project import](user-guide/projects.md#importing-an-arcgis-pro-project), nested
[layer groups](user-guide/layers.md#layer-groups) with multi-select moves,
anchored [review comments](user-guide/map-controls.md#review-comments) that sync
through a collaboration session, an
[Elements panel](user-guide/map-controls.md#annotations-and-the-elements-panel)
for map annotations, [ArcGIS Hub and open-data catalog
browsers](user-guide/adding-data.md#more-data-sources), address geocoding of
delimited text at import time, KML Super-Overlay support, a Dashboard selector
widget that cross-filters the other widgets, and
[NMEA receiver support](user-guide/map-controls.md#gps-tracking) in GPS Tracking.
Newest capabilities, still settling in. The v3.3 release added interactive
[line of sight](user-guide/map-controls.md), MGRS/USNG grids and coordinate
readouts, SQL Server and Azure SQL sources on desktop, a
[project history](user-guide/projects.md#project-history-and-crash-recovery)
that compares snapshots and restores single layers, editable layer metadata
with STAC Item export, glTF, OBJ, and STL export of 3D-extruded layers, and
plugin panels that dock in the side panel. Since then, `main` has added an
[ArcGIS Portal](user-guide/web-services.md#arcgis-portal) browser and a
[Sentinel-2 Explorer](user-guide/web-services.md#sentinel-2-explorer) under Web
Services, Sign in with ArcGIS for secured services, administrative boundaries
from geoBoundaries, Cesium Ion terrain assets, custom point markers from PNG,
JPEG, and GIF images, standard deviation and geometric interval
classification, and LAS/LAZ/COPC export of LiDAR tool outputs.
The [roadmap](roadmap.md) tracks every release, version by version.
+25 -16
View File
@@ -45,21 +45,29 @@ Tools that depend on a **local desktop process** are hidden on mobile because
iOS has no Python sidecar or local helper binaries and its sandbox forbids
spawning subprocesses:
- Processing → GeoLibre Toolbox → **Raster**, **Conversion**, **AI Segmentation**
(all need the Python sidecar). The Whitebox geoprocessing toolbox runs in
WebAssembly, needs no sidecar, and stays available.
- Add Data → **PostgreSQL** (served by the local Martin tile server)
- Processing → GeoLibre Toolbox → **Conversion**, **AI Segmentation**, and the
sidecar-backed **Raster** tools (all need the Python sidecar; the Raster
submenu keeps only the client-side **Download Global DEM** tool). The Whitebox
geoprocessing toolbox runs in WebAssembly, needs no sidecar, and stays
available.
- Add Data → **PostgreSQL** (served by the local Martin tile server) and
**SQL Server**
These are gated by a user-agent `isMobile()` check (which already matches
iPhone/iPad), so the Add Data menu and the Layer panel's add-data group never
offer them. Everything else runs client-side.
iPhone/iPad), so the Processing menu, the Add Data menu, and the Layer panel's
add-data group never offer them. Everything else runs client-side.
PostgreSQL has one entry point that check does not cover: the Browser panel
keeps its **Databases** section on every platform for discovery, so its + still
opens the PostgreSQL dialog. The dialog gates on `isDesktopRuntime()`
(`isTauri() && !isMobile()`) rather than on `isTauri()` alone, so on iOS it
shows the "requires GeoLibre Desktop" notice and disables Connect instead of
calling a sidecar that cannot exist (GeoLibre#2091).
The database sources have one entry point that check does not cover: the Browser
panel keeps its **Databases** section on every platform for discovery, so its +
still opens the connection dialog. The dialogs gate on `isDesktopRuntime()`
(`isTauri() && !isMobile()`) rather than on `isTauri()` alone, so on iOS they
show the "requires GeoLibre Desktop" notice and disable Connect instead of
calling a helper that cannot exist (GeoLibre#2091).
Unlike Android, the App Store build also hides **Earth Engine**: signing in from
the packaged app needs a loopback OAuth listener, which the Apple App Store
builds compile out (`src-tauri/src/lib.rs`) so the app binds no listening
socket.
## Location permission (required)
@@ -282,12 +290,12 @@ not free to choose — see the Xcode floor below.
on a published release, as the `GeoLibre_<version>_ios_app-store.ipa` release
asset, which outlives the artifact's 14-day retention:
- `APPLE_IOS_CERTIFICATE_BASE64` — `base64 -i dist.p12`
- `APPLE_IOS_CERTIFICATE_PASSWORD`
- `APPLE_IOS_CERTIFICATE_PASSWORD` — only if the `.p12` is password-protected
- `APPLE_IOS_PROVISIONING_PROFILE_BASE64` — `base64 -i profile.mobileprovision`
- `APPLE_TEAM_ID` — **reused** from the existing macOS/Homebrew signing
secrets; the Team ID is account-wide.
- **Without them**, it falls back to a no-signing **compile check**
(`cargo build --lib --target aarch64-apple-ios`) so CI still catches iOS build
(`cargo build --lib --release --target aarch64-apple-ios`) so CI still catches iOS build
breakage; it just can't produce an installable `.ipa`. A release run in that
state attaches nothing and logs a warning saying which secrets were missing,
rather than failing the release.
@@ -445,7 +453,8 @@ Most mirror Android:
- The **Download Offline Area** tool relies on a service worker, which the Tauri
builds don't use — it's a PWA feature. Native offline basemap caching is future
work.
- Earth Engine OAuth uses a desktop loopback/multi-window flow; a mobile
deep-link redirect (an iOS URL scheme / universal link) is future work.
- Earth Engine is hidden in the App Store build because its OAuth uses a
desktop loopback flow; a mobile deep-link redirect (an iOS URL scheme /
universal link) is future work.
- iPadOS multitasking (Split View / Stage Manager) hasn't been tuned; the
responsive layout should adapt, but verify on a real iPad before release.
+5 -4
View File
@@ -12,8 +12,8 @@ is produced and why it differs.
GeoLibre Desktop ships on macOS two ways:
- **Developer ID** (the default): the notarized `.dmg` published by
`release.yml` on every GitHub release and distributed through the website,
Homebrew, and winget-style channels. Full feature set, hardened runtime, no
`release.yml` on every GitHub release and distributed through the website
and Homebrew. Full feature set, hardened runtime, no
App Sandbox.
- **Mac App Store (MAS)**: a sandboxed variant built by the
`mas-store.yml` workflow (or `npm run tauri:build:mas` locally). This page
@@ -41,8 +41,9 @@ Tauri commands with stubs, and `GEOLIBRE_MAS_BUILD=1` hides the matching UI:
- **Jupyter server** (the Notebook panel falls back to the bundled
JupyterLite, which the web build already uses; Pyodide executes inside
WebKit, which App Review permits).
- **martin tile server** and its GitHub binary download (Add Data →
PostgreSQL is hidden).
- **martin tile server** and its GitHub binary download. Add Data hides the
sources with no client-side engine: **PostgreSQL** (martin), plus **SQL
Server** and **File Geodatabase** (both sidecar-backed).
- **External plugin installation** (zip archives and registry installs).
Built-in and bundled drop-in plugins keep working.
- **In-app update checks** (`GEOLIBRE_STORE_BUILD=1`, same as the Microsoft
+4 -3
View File
@@ -477,7 +477,7 @@ rejects the preflight a JSON `POST /search` needs, so the library's own
goes out as a `GET /search`, and the collection list falls back to the bundled
`planetary-computer-collections.json` when the live one cannot be read. The
subclass reuses the private `fetch` / `abortController` fields, and
`maplibre-gl-planetary-computer.ts` swaps it into the control's private
`maplibre-planetary-computer.ts` swaps it into the control's private
`_stacClient` field before the control is added (collections load in `onAdd`).
If upstream renames those fields, loads collections in its constructor, or adds
@@ -1072,8 +1072,9 @@ the bundle report makes a second copy easy to spot.
Dependencies are watched two ways: **Dependabot** (`.github/dependabot.yml`) opens
grouped weekly update PRs for npm, pip (backend + `python/`), cargo, and Actions,
and the CI **`audit` job** runs `npm run audit:ci` (blocking) plus a non-blocking
`pip-audit` of the resolved backend environment.
and CI runs `npm run audit:ci` (blocking, in the "Lint and type check" job) plus a
non-blocking `pip-audit` of the resolved backend environment (in the "Backend
tests" job).
`audit:ci` is `scripts/audit-check.mjs`, a thin wrapper over `npm audit
--omit=dev` that still fails on every high/critical advisory _except_ the ones
+11 -8
View File
@@ -9,8 +9,8 @@ Paste your token into **Settings → Environment Variables → Mapbox token** an
click **Save Settings**. Like the Cesium token, it is stored on this device,
outside the project file. Existing enabled Mapbox environment-variable rows
move into this field when settings are saved; Cancel leaves them unchanged.
Alternatively, launch the development server with `MAPBOX_TOKEN` in its environment. Token changes
recreate Mapbox maps. A missing token displays setup instructions; map loading
Alternatively, launch the development server with `MAPBOX_TOKEN` in its
environment. Token changes recreate Mapbox maps. A missing token displays setup instructions; map loading
errors redact access tokens. Use a public Mapbox token appropriate for your
application. Mapbox use is associated with that token's account and is subject
to Mapbox's terms and usage pricing.
@@ -161,8 +161,8 @@ browser against an authenticated Mapbox map):
- **Layer Swipe** for native style layers. The control drives both maps only
through the surface the two engines share, so the one map it constructed
itself — the clipped comparison pane, until now always a MapLibre one — comes
from `maplibre-gl-swipe` 0.13.0's `createMap`, fed mapbox-gl's `Map`. Two Mapbox specifics come with it: the
pane is handed the access token explicitly (mapbox-gl reads its token from a
from `maplibre-gl-swipe`'s `createMap` option (added in 0.13.0), fed
mapbox-gl's `Map`. Two Mapbox specifics come with it: the pane is handed the access token explicitly (mapbox-gl reads its token from a
global the app never sets, so a second map built without it renders nothing),
and the basemap grouping is seeded with `basemapLayerIds` because a
`mapbox://` style URL cannot be fetched — the same reason the layer control
@@ -247,15 +247,18 @@ has no `addProtocol` hook, but its source already tiles the data in a worker
with geojson-vt. With 200,000 points, adding the layer took about 2.1 s on
Mapbox against 1.5 s on MapLibre, a restyle 0.9 s against 0.8 s, and a data
edit 2.1 s against 1.2 s, with a shorter longest main-thread stall on Mapbox
(0.8 s against 0.85 s). Mapbox Standard is loaded as a local style import with a shared opacity setting.
The Background card fades its land and water colors, labels (including ocean labels),
3D objects, and atmosphere while preserving project layers and Standard's configuration.
(0.8 s against 0.85 s).
Mapbox Standard is loaded as a local style import with a shared opacity
setting. The Background card fades its land and water colors, labels
(including ocean labels), 3D objects, and atmosphere while preserving project
layers and Standard's configuration.
## License and terms
GeoLibre itself is MIT licensed, but the Mapbox renderer depends on
[Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) v3 (`mapbox-gl`
3.30.0 at the time of writing), which is **not** open source. Mapbox GL JS v3
3.32.0 at the time of writing), which is **not** open source. Mapbox GL JS v3
is distributed under the
[Mapbox Terms of Service](https://www.mapbox.com/legal/tos) and its
[license](https://github.com/mapbox/mapbox-gl-js/blob/main/LICENSE.txt);
+3 -2
View File
@@ -34,7 +34,8 @@ geolibre-mcp --root ~/maps
The server speaks MCP over stdio, which is what desktop clients spawn. The
`--root` flag is repeatable, and `GEOLIBRE_MCP_ROOTS` (`:`-separated, `;` on
Windows) does the same job from the environment. With neither set, the workspace
is the current directory.
is the current directory. `--transport` switches from the default `stdio` to
`streamable-http` or `sse` for clients that connect over HTTP.
### Client configuration
@@ -243,7 +244,7 @@ project.
Use `set_renderer(path, "cesium")` to open a project on the globe.
`set_map_layout(path, 1, 2, view_kinds=["cesium", "maplibre"])` creates a mixed
grid and returns the secondary pane IDs. Pass one as `pane_id` to `set_renderer`
grid and returns the secondary panes, with their IDs, as `secondaryMapViews`. Pass one as `pane_id` to `set_renderer`
to change only that pane. Camera tools continue to use longitude/latitude and
the shared zoom, bearing, and pitch convention. The accepted renderer names are
`maplibre`, `mapbox`, `cesium`, and `arcgis`.
+12 -5
View File
@@ -13,6 +13,8 @@ both the web and desktop builds, with a different Python runtime behind the same
from a uv-managed Python environment (the same mechanism as the FastAPI
sidecar) on `127.0.0.1:8766`, token-authenticated, and embeds it. This gives
full CPython with the native geospatial stack (geopandas, rasterio, GDAL, …).
The Mac App Store build is the exception: it cannot spawn the server, so it
embeds the JupyterLite site like the web build.
This is distinct from the [`geolibre` Python package](python.md), which does the
inverse — embedding the *whole GeoLibre app* inside a notebook cell.
@@ -158,9 +160,11 @@ instead (GeoLibre#1851, GeoLibre#1658). Install the deps above and rebuild.
Two builds therefore treat a missing CLI as fatal rather than skippable, because
they serve the site and cannot degrade: the Mac App Store build, and any build
that sets `GEOLIBRE_JUPYTERLITE_REQUIRED=1` (the Docker image does). The desktop
(Tauri) dev and build paths skip it entirely (they use the real JupyterLab
server), so the static site never bloats the installer.
that sets `GEOLIBRE_JUPYTERLITE_REQUIRED=1` (the Docker image does). The Mac
App Store build cannot spawn a JupyterLab server, so its Notebook panel embeds
the JupyterLite site instead. Every other desktop (Tauri) dev and build path
skips it entirely (they use the real JupyterLab server), so the static site
never bloats the installer.
The Docker image builds and serves the site, and gives `/jupyterlite/` its own
block in `docker/nginx.conf`:
@@ -175,8 +179,11 @@ block in `docker/nginx.conf`:
prefix match would otherwise take away from them.
Build config lives in `apps/geolibre-desktop/jupyterlite/` (a
`jupyter_lite_config.json`, the build `requirements.txt`, and a starter
`files/Welcome.ipynb`). The generated directory is excluded from the PWA
`jupyter_lite_config.json`, a `jupyter-lite.json`, and the build
`requirements.txt`). The build stages the kernel client and the starter
`Welcome.ipynb` into its `files/` from their canonical copies under
`backend/geolibre_server/` (`notebook_client.py`,
`notebook_examples/Welcome.ipynb`). The generated directory is excluded from the PWA
precache (see `pwaPlugin` in `apps/geolibre-desktop/vite.config.ts`).
## Desktop server
+73 -18
View File
@@ -22,6 +22,7 @@ export type GeoLibreBuiltInMapControl =
| "scale"
| "attribution"
| "logo"
| "maptoolkit-logo"
| "layer-control";
export interface GeoLibrePlugin {
@@ -43,7 +44,9 @@ export interface GeoLibrePlugin {
exclusiveGroup?: string;
/** At least one name is required for handleUrlParameters to be called. */
urlParameterNames?: string[];
activate: (app: GeoLibreAppAPI) => boolean | void;
// Return (or resolve) false to refuse activation; the host then rolls back
// the optimistic active state.
activate: (app: GeoLibreAppAPI) => boolean | void | Promise<boolean | void>;
deactivate: (app: GeoLibreAppAPI) => void;
handleUrlParameters?: (
app: GeoLibreAppAPI,
@@ -56,6 +59,18 @@ export interface GeoLibrePlugin {
) => boolean | void;
getProjectState?: () => unknown;
applyProjectState?: (app: GeoLibreAppAPI, state: unknown) => boolean | void;
/**
* The plugin saves its own panel open/collapsed state through
* getProjectState/applyProjectState, so the project-restore pass must not
* collapse its control.
*/
restoresPanelCollapseState?: boolean;
/**
* The plugin's project state is project data, not a preference: every
* project load calls applyProjectState, with undefined when the file has no
* state for it, so the previous project's data is not carried over.
*/
clearsStateOnProjectLoad?: boolean;
/**
* A workspace tool, not part of a project (e.g. the S3 Browser panel):
* project loads and map swaps leave it running, and it is never saved in
@@ -69,6 +84,7 @@ export interface GeoLibrePlugin {
export interface GeoLibreDeckGL {
core: typeof import("@deck.gl/core");
layers: typeof import("@deck.gl/layers");
aggregationLayers: typeof import("@deck.gl/aggregation-layers");
geoLayers: typeof import("@deck.gl/geo-layers");
meshLayers: typeof import("@deck.gl/mesh-layers");
mapbox: typeof import("@deck.gl/mapbox");
@@ -115,6 +131,8 @@ export interface GeoLibreRasterWindowReading {
overviewLevel: number;
}
// An excerpt: the full interface, with every optional method, is in
// packages/plugins/src/types.ts.
export interface GeoLibreAppAPI {
setBasemap: (styleUrl: string) => void;
addGeoJsonLayer: (
@@ -198,6 +216,16 @@ export interface GeoLibreAppAPI {
moveLayersToGroup?: (layerIds: string[], groupId: string | null) => void;
moveLayerGroupToGroup?: (id: string, parentId: string | null) => void;
removeLayerGroup?: (id: string) => void;
// Time Slider adapters (see "Driving a layer's own time dimension" below).
registerTemporalLayer?: (
layerId: string,
adapter: TemporalLayerAdapter,
options?: { bind?: boolean }
) => () => void;
unregisterTemporalLayer?: (layerId: string) => void;
// See "Activating and deactivating other plugins" below.
activatePlugin?: (pluginId: string, state?: unknown) => Promise<boolean>;
deactivatePlugin?: (pluginId: string) => boolean;
getActiveBasemap: () => string;
onBasemapChange: (callback: (styleUrl: string) => void) => () => void;
fetchArrayBuffer?: (url: string) => Promise<ArrayBuffer>;
@@ -216,6 +244,11 @@ export interface GeoLibreAppAPI {
// falls back to this when getMap() is null — see "Supporting the Mapbox
// renderer" below.
getMapboxMap?: () => import("mapbox-gl").Map | null;
// The mapbox-gl namespace and the access token, only while Mapbox is the
// primary renderer, for a plugin that must construct Mapbox classes or a
// second Mapbox map.
getMapboxGl?: () => typeof import("mapbox-gl").default | null;
getMapboxAccessToken?: () => string | null;
// The primary ArcGIS MapView or SceneView, or null on another renderer.
// The shared deck overlay hosts flat maps and local scenes only.
getArcgisView?: () => ReturnType<
@@ -290,11 +323,18 @@ export interface GeoLibreAppAPI {
openFloatingPanel?: (id: string) => boolean;
closeFloatingPanel?: (id: string) => void;
getOpenFloatingPanels?: () => string[];
// AI Assistant tools and guidance (see "Assistant tools" below).
registerAssistantTool?: (tool: Tool) => () => void;
registerAssistantToolSpec?: (spec: AssistantToolSpec) => () => void;
registerAssistantGuidance?: (text: string) => () => void;
}
// A getter re-reads the label on every render (see "Toolbar menus" below).
export type GeoLibreToolbarLabel = string | (() => string);
export interface GeoLibreToolbarMenu {
id: string;
label: string;
label: GeoLibreToolbarLabel;
icon?: string; // URL or data: URI
items: GeoLibreToolbarMenuItem[];
}
@@ -303,7 +343,7 @@ export type GeoLibreToolbarMenuItem =
| {
type?: "action";
id: string;
label: string;
label: GeoLibreToolbarLabel;
icon?: string;
disabled?: boolean;
onSelect: () => void;
@@ -311,7 +351,7 @@ export type GeoLibreToolbarMenuItem =
| {
type: "submenu";
id: string;
label: string;
label: GeoLibreToolbarLabel;
icon?: string;
items: GeoLibreToolbarMenuItem[];
}
@@ -338,6 +378,8 @@ export interface GeoLibreFloatingPanelRegistration {
title: string | (() => string);
icon?: string; // URL or data: URI
defaultWidth?: number;
defaultHeight?: number; // omitted: the card sizes to its content
position?: GeoLibreMapControlPosition; // corner it first opens at, default "top-left"
render: (container: HTMLElement) => void | (() => void);
onOpen?: () => void;
onClose?: () => void;
@@ -370,11 +412,15 @@ export interface GeoLibreRightPanelRegistration {
icon?: string;
/** Preferred expanded width in px (desktop only; host-clamped). */
defaultWidth?: number;
/** Deactivate the owning plugin when the user closes the panel. */
deactivatePluginOnClose?: boolean;
/** Fill the panel body with your own DOM. May return a cleanup function. */
render: (container: HTMLElement) => void | (() => void);
onOpen?: () => void;
onCollapse?: () => void;
onClose?: () => void;
/** Called only on an explicit close, not when another panel displaces it. */
onExplicitClose?: () => void;
}
```
@@ -424,17 +470,19 @@ GeoLibre. To add one to this repository:
export { myPlugin } from "./plugins/my-plugin";
```
3. Register it in `apps/geolibre-desktop/src/hooks/usePlugins.ts`.
3. Register it in `apps/geolibre-desktop/src/hooks/usePlugins.ts` by adding it
to the `BUILT_IN_PLUGINS` array, which is passed to `manager.registerAll`.
```typescript
import { myPlugin } from "@geolibre/plugins";
manager.registerAll([
const BUILT_IN_PLUGINS: GeoLibrePlugin[] = [
maplibreLayerControlPlugin,
maplibreGeoAgentPlugin,
maplibreGeoEditorPlugin,
// ...
myPlugin,
]);
];
manager.registerAll(BUILT_IN_PLUGINS);
```
For a MapLibre control plugin, add the package dependency, then call
@@ -482,14 +530,16 @@ default control button CSS can override their flex centering:
}
```
Run `npm run build` and `pre-commit run --all-files` before submitting the
change. If you also touched pages under `docs/`, build the site — CI runs
Run `npm run build` and `pre-commit run --files <changed files>` before
submitting the change. If you also touched pages under `docs/`, build the site — CI runs
`zensical build --strict`, so a broken link or a page missing from the
`mkdocs.yml` `nav` fails the build. See
[Contributing](contributing.md#documentation) for both gates in full.
## Built-in plugins
A selection of the built-in plugins:
| ID | Description |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `osm-basemap` | OpenFreeMap Liberty style |
@@ -499,7 +549,7 @@ change. If you also touched pages under `docs/`, build the site — CI runs
| `maplibre-gl-geo-editor` | Adds GeoEditor drawing controls |
| `maplibre-gl-dimensions` | Adds Dimension tools (linear/angular CAD-style dimension lines, with optional vertex snapping) |
| `maplibre-gl-geoagent` | Adds GeoAgent map assistant controls |
| `maplibre-gl-lidar` | Adds LiDAR controls |
| `maplibre-gl-usgs-lidar` | Searches and streams USGS 3DEP LiDAR point clouds |
| `geolibre-ign-lidar-hd` | Searches IGN LiDAR HD tile coverage (WFS) and downloads point cloud (COPC LAZ) files. Its live-network test is opt-in via `RUN_LIVE_TESTS` (see `tests/ign-lidar-hd.test.ts`) |
| `maplibre-gl-streetview` | Adds street view controls |
| `maplibre-gl-swipe` | Adds map swipe controls |
@@ -526,7 +576,7 @@ Map control plugins can optionally expose `getMapControlPosition()` and `setMapC
Plugins with serializable runtime settings can expose `getProjectState()` and `applyProjectState()` so GeoLibre can save and restore those settings in the project file. A wrapper should use these hooks to adapt upstream control APIs such as `getState()` without requiring every upstream package to implement a GeoLibre-specific interface.
Plugins that render with deck.gl should call `app.getDeckGL()` (returns a promise) to obtain GeoLibre's own deck.gl modules — `core`, `layers`, `geoLayers`, `meshLayers`, and `mapbox` (use `mapbox.MapboxOverlay` for interleaved MapLibre rendering). Render on the host's single deck.gl instance rather than bundling a second copy: deck.gl and luma.gl throw on a version mismatch and share global singletons, so a bundled copy fails to render. Call it with optional chaining (`app.getDeckGL?.()`) since a host variant may not ship deck.gl.
Plugins that render with deck.gl should call `app.getDeckGL()` (returns a promise) to obtain GeoLibre's own deck.gl modules — `core`, `layers`, `aggregationLayers`, `geoLayers`, `meshLayers`, and `mapbox` (use `mapbox.MapboxOverlay` for interleaved MapLibre rendering). Render on the host's single deck.gl instance rather than bundling a second copy: deck.gl and luma.gl throw on a version mismatch and share global singletons, so a bundled copy fails to render. Call it with optional chaining (`app.getDeckGL?.()`) since a host variant may not ship deck.gl.
### Shared projection library
@@ -796,6 +846,7 @@ export interface GeoLibreWfsLayerOptions {
}
export interface GeoLibreCogLayerOptions {
engine?: "maplibre-gl-raster" | "cog-tiler-wasm" | "titiler" | "auto"; // see below
bands?: string; // "1" (single band) or "1,2,3" (RGB)
colormap?: string; // named colormap for a single-band COG, e.g. "terrain"
rescaleMin?: number;
@@ -1016,6 +1067,8 @@ export interface TemporalLayerAdapter {
getTimeValues: () => ReadonlyArray<Date | number | string>; // the time coordinate, in index order
setTime: (date: Date) => void | Promise<void>; // apply a date to the layer
dimension?: string; // the axis name, default "time"
granularity?: "hour" | "day" | "month" | "year"; // stepping unit; derived from the axis span when omitted
displayUnits?: ("hour" | "day" | "month" | "year")[]; // units offered by the slider's granularity controls
}
```
@@ -1327,7 +1380,7 @@ const user = await (await http(`${server}/api/v1/users/current`)).json(); // sen
## Floating panels
A floating panel is a draggable, closeable card the host overlays on the map's top-left corner. Unlike a dockable right panel (one active panel docked at a fixed position), several floating panels can be open at once and they do not shrink the map. The render contract is the same plain-DOM `render(container)` as right panels.
A floating panel is a draggable, closeable card the host overlays on the map, opening at the corner named by `position` (top-left by default). Unlike a dockable right panel (one active panel docked at a fixed position), several floating panels can be open at once and they do not shrink the map. The render contract is the same plain-DOM `render(container)` as right panels.
```typescript
const unregister = app.registerFloatingPanel?.({
@@ -1403,7 +1456,7 @@ project or persisted state carrying `activePluginIds` overrides the default.
The flag is honored **only** for bundled drop-ins; it does not bypass a blocked
plugin policy.
If instead you want a plugin compiled into the main JS bundle (no `plugin.json`, no fetch), register it as a built-in plugin (see "Add a plugin" in the repository README).
If instead you want a plugin compiled into the main JS bundle (no `plugin.json`, no fetch), register it as a built-in plugin (see [Add a built-in plugin](#add-a-built-in-plugin-in-this-repository)).
```json
{
@@ -1417,7 +1470,7 @@ If instead you want a plugin compiled into the main JS bundle (no `plugin.json`,
}
```
The `entry` file must export a `GeoLibrePlugin` as either the default export or a named `plugin` export. The exported plugin `id`, `name`, and `version` must match `plugin.json`. The entry must be a self-contained `.js` or `.mjs` bundle because relative module imports inside the zip are not resolved by this first loader. The optional `engines` array declares which of GeoLibre's four map renderers the plugin supports (`"maplibre" | "mapbox" | "cesium" | "arcgis"`, defaulting to `["maplibre"]`). Plugins supporting the native globe add `"cesium"`; plugins that stay on the Style Spec surface can add `"mapbox"` (see "Supporting the Mapbox renderer"); and plugins with an ArcGIS-native adapter can add `"arcgis"` (see the [ArcGIS renderer](arcgis-renderer.md)). The host suspends a plugin when the selected engine is not in this list and restores it when a compatible engine becomes active.
The `entry` file must export a `GeoLibrePlugin` as either the default export or a named `plugin` export. The exported plugin `id`, `name`, and `version` must match `plugin.json`. The entry must be a self-contained `.js` or `.mjs` bundle because relative module imports inside the zip are not resolved by this first loader. The optional `engines` array declares which of GeoLibre's four map renderers the plugin supports (`"maplibre" | "mapbox" | "cesium" | "arcgis"`, defaulting to `["maplibre"]`). Plugins supporting the native globe add `"cesium"`; plugins that stay on the Style Spec surface can add `"mapbox"` (see [Supporting the Mapbox renderer](#supporting-the-mapbox-renderer)); and plugins with an ArcGIS-native adapter can add `"arcgis"` (see the [ArcGIS renderer](arcgis-renderer.md)). The host suspends a plugin when the selected engine is not in this list and restores it when a compatible engine becomes active.
External plugin entries are executed with `import(URL.createObjectURL(...))`, which is why the desktop CSP in `tauri.conf.json` includes `blob:` in `script-src`. Removing `blob:` from `script-src` breaks external plugin loading. Combined with `'unsafe-eval'`, this means code that can create a blob URL can execute scripts, which is acceptable because external plugins are trusted local files installed by the user.
@@ -1428,8 +1481,8 @@ boundary. GeoLibre keeps them in a trusted local save only when the user
explicitly chooses to retain credentials, and removes the entire
`plugins.settings` object from shares, standalone HTML exports, embed
snapshots, and collaboration snapshots. The exception is state a plugin's
registry entry declares publishable (see `publishableSettings` under "Plugin
marketplace"), which is kept and still scrubbed for credentials. Store portable, non-secret identifiers
registry entry declares publishable (see `publishableSettings` under [Plugin
marketplace](#plugin-marketplace)), which is kept and still scrubbed for credentials. Store portable, non-secret identifiers
such as broker references in layer source or metadata instead when recipients
need them.
@@ -1632,6 +1685,8 @@ callbacks omits the method. The assistant recomposes its system prompt together
with its tools before the next prompt, keeping the conversation history.
Feature-detect the method for older hosts.
## Map renderers
The host exposes `app.getMapRenderer()` to read the current primary renderer.
Engine declarations are enforced by the plugin manager for activation, URL
parameters, project restoration, and delayed control registration, as well as
@@ -1724,7 +1779,7 @@ whose results land out of order.
### Supporting the Mapbox renderer
The Mapbox renderer (`docs/mapbox-renderer.md`) draws with Mapbox GL JS, whose
The [Mapbox renderer](mapbox-renderer.md) draws with Mapbox GL JS, whose
runtime API is the Style Spec surface MapLibre grew out of: sources and style
layers (`addSource`, `addLayer`, `setPaintProperty`, `setLayoutProperty`,
`setFilter`, `getSource`, `getLayer`, `getStyle`, `moveLayer`), images
+5 -1
View File
@@ -1,6 +1,6 @@
# Privacy Policy
_Last updated: October 4, 2026_
_Last updated: October 6, 2026_
GeoLibre Desktop ("GeoLibre", "the app") is an open-source desktop GIS
application developed by the OpenGeos community. This policy explains how the app
@@ -50,6 +50,10 @@ the request you make, and are governed by their own privacy policies:
server operated by the OpenGeos project (currently hosted on Cloudflare) and
shared with the other participants. The relay holds the latest project snapshot
so that later joiners can load the session, and discards it when the session ends.
- **Project sharing**: if you use Project → Share, the project file is uploaded
to share.geolibre.app, operated by the OpenGeos project. Vector layers added
from local files are embedded in the uploaded file; other local files are not
uploaded.
GeoLibre does not control these third-party services; please review their privacy
policies for how they handle data.
+23 -5
View File
@@ -26,24 +26,34 @@ file contents do not change.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `version` | string | Format version (`0.1.0`) |
| `version` | string | Format version: the app writes `0.2.0`; older files and the Python package write `0.1.0` |
| `name` | string | Project display name |
| `mapView` | object | `center`, `zoom`, `bearing`, `pitch`, optional `bbox` |
| `basemapStyleUrl` | string | MapLibre style JSON URL, or an empty string for a blank background |
| `basemapVisible` | boolean | Whether the Background layer is visible |
| `basemapOpacity` | number | Background layer opacity from `0` to `1` |
| `blankBackgroundColor` | string \| null | Optional custom colour for the Blank background; `null` uses the theme default |
| `layers` | array | Layer definitions (see below) |
| `selectedLayerId` | string \| null | Optional layer selected in the Layers panel when the project was saved; `null` restores no active layer |
| `layerGroups` | array | Optional Layers-panel folders (`id`, `name`, `parentId`, `collapsed`, `visible`, `opacity`); omitted when empty |
| `styles` | object | Map of layer id → `LayerStyle` |
| `preferences` | object | Project map preferences, environment variables, and geocoding settings (see [Credential redaction](#credential-redaction)) |
| `plugins` | object | Optional external plugin manifest URLs, active plugin IDs, plugin map-control positions, and plugin settings |
| `legend` | object | Optional Print Layout legend customizations (title, grouping, ordering, per-item rename/hide) |
| `printLayout` | object | Optional Print Layout composer settings (title, page size, orientation, blocks, atlas); omitted when default |
| `storymap` | object | Optional scroll-driven story map (chapters and presentation settings); omitted when there are no chapters |
| `models` | array | Optional saved processing models (Model Builder pipelines) |
| `processingHistory` | array | Optional record of processing tool runs (Processing History) |
| `widgets` | array | Optional Dashboard panel chart widgets (see below); omitted when there are none |
| `dashboardColumns`| number | Optional Dashboard widget-grid column count (1-6, default 2); omitted when default |
| `mapLayout` | object | Optional multi-map grid (`rows`, `cols`, `syncView`); omitted for a single map |
| `secondaryMapViews` | array | Optional panes past the primary one, each with its own camera and layer visibility; omitted for a single map |
| `primaryMapLabel` | string | Optional label for the primary pane of a multi-map grid |
| `bookmarks` | array | Optional saved map views from the Bookmarks panel (see below); omitted when there are none |
| `bookmarkGroups` | array | Optional folders the bookmarks are organized into; omitted when there are none |
| `styleLibrary` | array | Optional project-scoped Style Manager entries (name, tags, kind, `LayerStyle` subset); omitted when empty |
| `primaryRenderer` | string | Optional engine for the primary map area: `"maplibre"` (2D, the default), `"mapbox"` (Mapbox GL JS), `"arcgis"` (ArcGIS Maps SDK for JavaScript) or `"cesium"` (3D globe); omitted when default |
| `comments` | array | Optional review comments anchored to map points or features, with replies |
| `interaction` | object | Optional startup Identify target and control visibility (see below); omitted by default |
| `metadata` | object | Free-form project metadata |
@@ -197,9 +207,13 @@ self-contained HTML page for static hosting.
```
Each widget binds a chart to a layer's attributes. `type` is one of `histogram`,
`scatter`, `bar`, `line`, `box`, or `pie`. Which other keys apply depends on the
type: `field` (histogram/line/box), `xField`/`yField` (scatter), `category` +
`aggregation` + `valueField` (bar/pie), `bins` (histogram). Bar `aggregation` is
`scatter`, `bar`, `line`, `box`, `pie`, `indicator`, `selector`, or `list`.
Which other keys apply depends on the type: `field` (histogram/line/box, and
indicator unless it counts), `xField`/`yField` (scatter), `category` +
`aggregation` + `valueField` (bar/pie), `bins` (histogram),
`indicatorAggregation` (`count`/`sum`/`mean`/`min`/`max`/`median`) + `prefix` +
`suffix` (indicator), `category` + `multiple` (selector), and `listFields` +
`sortBy` + `sortDir` + `limit` (list). Bar `aggregation` is
`count`/`sum`/`mean`; pie is `count`/`sum` only. `title` is an optional label and
`color` an optional hex (`#rgb`/`#rrggbb`) for the chart's marks (the series
color for single-series charts; the base of a monochromatic ramp for bar/pie).
@@ -630,6 +644,7 @@ under `geolibre:metadata`, beside the `geo` key.
| `geojson` | Supported for imported files and GeoJSON URLs |
| `xyz` | Supported for raster tile templates |
| `wms` | Supported as tiled WMS GetMap layers |
| `wmts` | Supported for WMTS raster tile templates |
| `raster` | Supported for raster tile templates; with `source.ionAssetId` and `metadata.sourceKind: "cesium-ion"`, a Cesium Ion imagery asset the 3D globe loads with the app's Ion token (3D only) |
| `vector-tiles` | Supported for MapLibre vector tile sources |
| `mbtiles` | Supported in the desktop app through a local MapLibre protocol |
@@ -642,7 +657,10 @@ under `geolibre:metadata`, beside the `geo` key.
| `gaussian-splat` | Supported through the Components plugin |
| `geoparquet` | Imported as GeoJSON via DuckDB-WASM |
| `duckdb-query` | Supported for SQL query-result layers |
| `3d-tiles` | Supported through the `maplibre-gl-3d-tiles` plugin; with `source.ionAssetId` and `metadata.sourceKind: "cesium-ion"`, a Cesium Ion tileset the 3D globe loads with the app's Ion token (3D only) |
| `deckgl-viz` | Supported through the Deck.gl Layer plugin |
| `video` | Supported for georeferenced video overlays |
| `image` | Supported for georeferenced image overlays (KML ground overlays, the Georeferencer, NetCDF grids) |
| `3d-tiles` | Supported through the `maplibre-gl-3d-tiles` package; with `source.ionAssetId` and `metadata.sourceKind: "cesium-ion"`, a Cesium Ion tileset the 3D globe loads with the app's Ion token (3D only) |
## API
+10 -8
View File
@@ -270,6 +270,8 @@ m.on_layer_change(lambda e: print("layers", e["layerIds"]))
| `set_layer_visibility(layer, visible)` / `set_layer_opacity(layer, opacity)` | Change a layer by id, name, or handle. |
| `list_algorithms()` | Available processing algorithms (`id`, `parameters`, …). |
| `run_algorithm(id, parameters=None, timeout=)` | Run an algorithm; returns `{logs, resultLayerIds}`. |
| `run_model_builder(graph, timeout=)` | Run a serialized Model Builder graph in the displayed app. |
| `list_whitebox_tools()` / `run_whitebox_tool(tool_id, parameters=None, timeout=)` | List, or run in the browser via WASM, the bundled Whitebox tools; `run_whitebox_tool` returns `{logs, resultLayerIds}`. |
| `to_image(path=None, timeout=)` | Capture the map as PNG bytes, or write to `path`. |
| `to_html(path=None, title=, width=, height=, app_url=)` | Export a standalone HTML page that embeds the current project (credentials redacted) in this map's `layout` and `theme`; returns the HTML or writes to `path`. |
| `on(event, cb)` / `on_click` / `on_selection_change` / `on_layer_change` | Register event callbacks; returns an unsubscribe function. |
@@ -279,7 +281,7 @@ m.on_layer_change(lambda e: print("layers", e["layerIds"]))
| Method | Description |
| --- | --- |
| `Map(center, zoom, basemap=, height=, layout=, theme=)` | Create a map. |
| `Map(center, zoom, basemap=, renderer=, height=, layout=, theme=, server_proxy=)` | Create a map. |
| `add_geojson(data, name=, **style)` | Add GeoJSON from a dict, file path, URL, JSON string, or GeoDataFrame. |
| `add_gdf(gdf, name=, column=None, **style)` | Add a GeoDataFrame, optionally as a choropleth. |
| `add_csv(data, x="longitude", y="latitude", name=, **style)` / `add_xy_data(...)` | Add points from a CSV path, URL, text, DataFrame, or row mappings. |
@@ -295,19 +297,19 @@ m.on_layer_change(lambda e: print("layers", e["layerIds"]))
| `add_flatgeobuf(data, name=, **style)` | Add a FlatGeobuf dataset (URL or local file). |
| `add_shp(data, name=, **style)` | Add a Shapefile (zipped URL or local `.shp`). |
| `add_kml(data, name=, **style)` / `add_gpkg(data, name=, layer=None, **style)` | Add KML/KMZ or GeoPackage data. |
| `add_polyline(polyline, name="Polyline", precision=5, **style)` | Add an Encoded Polyline layer from a string or list of strings (precision 5 or 6). |
| `add_polyline(polyline, name="Polyline", precision=5, unescape=False, **style)` | Add an Encoded Polyline layer from a string or list of strings (precision 5 or 6). |
| `add_vector_tiles(url, name=, source_layers=, source_layer=, **style)` | Add a vector tile layer from a TileJSON endpoint. |
| `add_pmtiles(url, name=, tile_type=, source_layers=, **style)` | Add a PMTiles archive (vector or raster). |
| `add_tile_layer(url, name=, tile_size=, attribution=)` | Add a raster XYZ tile layer. |
| `add_tile_layer(url, name=, tile_size=, attribution=, bounds=, **style)` | Add a raster XYZ tile layer. |
| `add_ee_layer(ee_object, vis_params=, name=, shown=, opacity=)` | Add an authenticated Google Earth Engine object as raster tiles (needs `earthengine-api`). |
| `add_wms(endpoint, layers, name=, styles=, image_format=, transparent=, tile_size=, version=, crs=, bounds=, **style)` | Add a WMS layer (GetMap, tiled raster). `bounds` is `[west, south, east, north]`, needed for zoom-to-layer. `crs` defaults to `EPSG:3857`; for a server without Web Mercator pass a CRS it lists: preferably a geographic one (`EPSG:4326`, `EPSG:4258`, `EPSG:6706`, or `CRS:84` with `version="1.3.0"`), otherwise a projected `EPSG:<code>` such as `EPSG:25832`, which the desktop app warps from its EPSG tables. Only the desktop app redraws those tiles into Web Mercator: the web build and `export_html` pages still send the Web Mercator BBOX, so such a layer stays blank there. |
| `add_wms(endpoint, layers, name=, styles=, image_format=, transparent=, tile_size=, version=, crs=, bounds=, **style)` | Add a WMS layer (GetMap, tiled raster). `bounds` is `[west, south, east, north]`, needed for zoom-to-layer. `crs` defaults to `EPSG:3857`; for a server without Web Mercator pass a CRS it lists: preferably a geographic one (`EPSG:4326`, `EPSG:4258`, `EPSG:6706`, or `CRS:84` with `version="1.3.0"`), otherwise a projected `EPSG:<code>` such as `EPSG:25832`, which the desktop app warps from its EPSG tables. Only the desktop app redraws those tiles into Web Mercator: the web build and `to_html` pages still send the Web Mercator BBOX, so such a layer stays blank there. |
| `add_wmts(url, name=, tile_size=, bounds=, **style)` | Add a WMTS layer from a tile URL template. |
| `add_wfs(endpoint, type_name, name=, version=, output_format=, srs_name=, max_features=, **style)` | Add a WFS layer (GetFeature GeoJSON, fetched and inlined). |
| `add_cog(url, name=, bands=, colormap=, rescale=, **style)` | Add a Cloud Optimized GeoTIFF (URL or a kernel-side local GeoTIFF path). |
| `add_raster(source, name=, bands=, colormap=, rescale=, array_args=, **style)` | Add a COG/GeoTIFF URL or path, or an xarray DataArray/Dataset (xarray needs `geolibre[raster]`). |
| `add_raster(source, name=, url=, bands=, colormap=, rescale=, array_args=, **style)` | Add a COG/GeoTIFF URL or path, or an xarray DataArray/Dataset (xarray needs `geolibre[raster]`). |
| `add_lidar(url, name=None, **style)` | Add a LAS, LAZ, COPC or EPT point cloud by URL (COPC/EPT stream by level of detail). |
| `point_cloud_annotations()` | Read the point labels, instance ids, custom classes, 3D boxes and 3D vectors saved by the app's [point cloud annotator](user-guide/point-cloud-annotation.md). Labels and instance ids are keyed by source URL, then node key and point index; `geolibre.project.apply_point_labels` writes a whole-file source's labels onto `laspy` classification. |
| `prelabel_point_cloud(url, input_file, tool="ground")` | Run the annotator's Whitebox pre-label on a local copy of a LiDAR layer and save the changed classes as its labels (`ground` or `ground-vegetation`; needs `geolibre[pointcloud]`). |
| `prelabel_point_cloud(url, input_file, tool="ground", only_unclassified=True)` | Run the annotator's Whitebox pre-label on a local copy of a LiDAR layer and save the changed classes as its labels (`ground` or `ground-vegetation`; needs `geolibre[pointcloud]`). |
| `write_labeled_point_cloud(url, input_file, output_file)` | Write a local copy of a LiDAR layer's LAS/LAZ/COPC file with the saved labels and instance ids applied, streaming files larger than memory (needs `geolibre[pointcloud]`). |
| `set_point_cloud_classes(classes)` | Define the annotator's custom classes, e.g. `[{"code": 64, "name": "Car", "color": "#e11d48"}]` (codes 19-255); the LiDAR layer draws them in their colour and names them in its legend. |
| `add_3d_tiles(url=None, name=, ion_asset_id=, altitude_offset=, request_headers=, **style)` | Add a 3D Tiles `tileset.json` URL, or a Cesium Ion tileset by asset id (3D globe only). |
@@ -315,7 +317,7 @@ m.on_layer_change(lambda e: print("layers", e["layerIds"]))
| `add_czml(url=None, name=, data=, source_path=, **style)` | Add a CZML (Cesium Language) dynamic 3D scene by URL or inline packets: orbits, vehicle tracks, moving models. Renders on the 3D globe, which follows the document's clock. |
| `add_cesium_kml(url=None, name=, data=, source_path=, **style)` | Native KML/KMZ on the globe with document styles and overlays. Supply a URL, inline XML, or a KMZ data URL; use `add_kml` for vector conversion. |
| `add_video(urls, coordinates, name=, **style)` | Add a georeferenced video (four `[lng, lat]` corners). |
| `add_basemap(basemap)` | Set the background basemap. |
| `add_basemap(basemap)` / `set_basemap(basemap)` | Set the background basemap. |
| `split_map(left_layers=None, right_layers=None, orientation=, position=, control_position=)` | Add a swipe (split-map) comparison slider between two layer sets. |
| `set_map_legend(title=None, position=, group_by_layer=, visible=, collapsed=)` | Show the map legend (Controls → Legend), whose rows come from each visible layer's symbology. One per map; calling it again updates it. |
| `add_legend(title=None, legend_dict=, labels=, colors=, builtin=, position=, shape=)` | Add a legend from a `{label: color}` dict, parallel `labels`/`colors`, or a `builtin` preset (`"nlcd"`, `"esa_worldcover"`). |
@@ -328,7 +330,7 @@ m.on_layer_change(lambda e: print("layers", e["layerIds"]))
| `rename_layer(layer, name)` / `move_layer(layer, index)` / `duplicate_layer(layer, name=)` / `show_layer(layer)` / `hide_layer(layer)` | Manage layers by id, name, or `Layer` handle. |
| `layer_properties(layer)` / `column_values(layer, column)` / `describe()` | Inspect inlined data and summarize a project without a browser round trip. |
| `remove_layer(layer_id)` / `clear_layers()` | Remove one layer by id, name, or handle, or remove all layers. |
| `set_popup(layer, fields=None, click=, hover=, title=, title_expression=, body_expression=, show_feature_id=, tooltip=, merge=False)` | Choose what a click popup shows for a layer, and how each value is formatted. |
| `set_popup(layer, fields=None, click=, hover=, title=, title_expression=, body_expression=, show_feature_id=, max_width=, image_height=, tooltip=, merge=False)` | Choose what a click popup shows for a layer, and how each value is formatted. |
| `set_tooltip(layer, fields=True)` / `clear_popup(layer)` | Turn a hover tooltip on (or off), or drop the popup config and restore the default popup. |
| `set_layer_metadata(layer, merge=False, title=, abstract=, keywords=, license=, attribution=, contact=, lineage=, temporal_extent=, links=)` / `clear_layer_metadata(layer)` | Describe a layer for catalogs (the app's Metadata dialog, its STAC Item export and GeoParquet export metadata). Emails, dates (ISO 8601) and link URLs are validated. `Layer.descriptive_metadata` reads it back. |
| `set_layer_filter(layer, expression)` | Hide a layer's features that do not match a boolean MapLibre expression, e.g. `[">=", ["get", "pop"], 100000]` (the filter Select by Expression → Filter layer saves). `None` clears it. |
+2 -2
View File
@@ -221,7 +221,7 @@
- [x] Customizable UI profiles that tailor which menus, panels, and data sources are visible, so a deployment can present a focused subset of the app to its users (see [UI Profiles](ui-profiles.md))
- [x] Bookmarks now capture the active layers alongside the camera, with selectable export, a resizable and reorderable panel, and a save-as name prompt
- [x] Sequential route (directions) network tool under Processing for computing routes through an ordered set of waypoints
- [x] Protomaps basemaps in the New map dialog, support for stacking multiple raster basemaps, and basemap element visibility presets in the layer control
- [x] Protomaps basemaps in the New project dialog, support for stacking multiple raster basemaps, and basemap element visibility presets in the layer control
- [x] Spinning Globe panel under Atmospheric Effects and customizable atmosphere halo and deep-space colors for the globe view
- [x] Notebook panel can split the workspace 50/50 with the map and auto-collapse the Style panel for more room, and the attribute table gains a column explorer for finding and toggling fields in wide tables
- [x] USGS LiDAR plugin replaces the previous LiDAR Viewer for browsing and loading USGS 3DEP point-cloud data, imported KML and KMZ layers honor their embedded symbology, and vector strokes can be sized in scale-proportional meters
@@ -565,7 +565,7 @@
- [x] **An interactive histogram for the raster display stretch**, so you can see the distribution while adjusting the min and max
- [x] **S3 projects**: open projects straight from the S3 Browser, which stays open across project loads and can open at startup. See [Cloud Storage](user-guide/cloud-storage.md)
- [x] **A calmer, clearer interface**: unified toast notifications with a Report issue action, layer-failure and tile-health toasts on the Cesium and ArcGIS engines, a command palette that reaches individual tools, a color vision preview, and starter projects in New Project
- [x] **A plugin ecosystem that docks and verifies**: Historical Imagery, Timelapse, Overture, Basemaps, GeoAgent, Layer Swipe, Elevation Profile, and USGS LiDAR panels dock in the side panel and follow the dark theme; registry installs are verified against the published bundle hash and honor the registry blocklist; plugins can add items to built-in toolbar menus, keep project state through credential stripping, use the host proj4 and WFS APIs, and get a native fetch with a cookie jar on desktop; a `?plugin=` link opens registry plugins behind a trust prompt; external plugins group under Plugins → Installed. A new USGS Elevation (DEM) plugin joins the catalog. See [Plugin API](plugin-api.md)
- [x] **A plugin ecosystem that docks and verifies**: Historical Imagery, Timelapse, Overture, Basemaps, GeoAgent, Layer Swipe, Elevation Profile, and USGS LiDAR panels dock in the side panel and follow the dark theme; registry installs are verified against the published bundle hash and honor the registry blocklist; plugins can add items to built-in toolbar menus, keep project state through credential stripping, use the host proj4 and WFS APIs, and get a native fetch with a cookie jar on desktop; a `?plugin=` link opens registry plugins behind a trust prompt; external plugins group under Plugins → Installed. A new USGS 3DEP elevation (DEM) plugin joins the catalog. See [Plugin API](plugin-api.md)
- [x] **For Python and AI agents**: `geolibre-mcp` can drive the open desktop map, author filters, labels, plugin state, and story maps, and run live processing. See [MCP server](mcp.md) and [Python API](python.md)
- [x] **For deployments and servers**: deployment.json loads before first paint and from the desktop config directory, the Docker image generates a validated deployment.json at boot and enforces sidecar, AI, and plugin policy, and the server API gains shares with link role, expiry, and password, per-org OIDC federation and trusted-header sign-in, per-org security policy, and SCIM 2.0 provisioning. See [Deployment Policy](deployment-policy.md) and [Server API](server-api.md)
- [x] **Complete catalogs, and a zoom preference**: every locale catalog is complete and plugins can ship their own translations, and a Map Preferences option turns off zooming to newly added layers. See [Internationalization](i18n.md)
+6 -4
View File
@@ -150,7 +150,7 @@ generated final policy; build-only grants do not configure its guards.
| --- | --- | --- |
| `GEOLIBRE_DEPLOYMENT_FILE` | unset, or mounted source policy | Validated at boot; generated public file is `/usr/share/nginx/html/deployment.json`, served at `/deployment.json`. |
| `GEOLIBRE_CAPABILITIES` | unset, or capabilities to grant | Runtime field override; `none` grants none. Unknown names stop boot. Final generated policy controls nginx sidecar guards and sidecar startup. |
| `GEOLIBRE_AI_URL` / `GEOLIBRE_AI_PROXY_URL` / `GEOLIBRE_AI_PROXY_TOKEN` | unset, or approved proxy trio | `GEOLIBRE_AI_URL=/ai` overrides policy to enable AI. URL must be an HTTPS origin without credentials/path/query/fragment; token only `[A-Za-z0-9._-]`. Missing or invalid configuration stops boot. Keep upstream URL and token private. |
| `GEOLIBRE_AI_URL` / `GEOLIBRE_AI_PROXY_URL` / `GEOLIBRE_AI_PROXY_TOKEN` | unset, or approved proxy trio | `GEOLIBRE_AI_URL=/ai` overrides policy to enable AI. The proxy URL must be an HTTPS origin without credentials/path/query/fragment; token only `[A-Za-z0-9._-]`. Missing or invalid configuration stops boot. Keep upstream URL and token private. |
| `VITE_GEOLIBRE_CAPABILITIES` (legacy build arg; still honoured) | unset, or capabilities to grant | Client fallback only; does not configure nginx guards. |
| `GEOLIBRE_AI_MODEL` | optional, with `GEOLIBRE_AI_URL` | Overrides policy model only when `GEOLIBRE_AI_URL` is set. |
| `GEOLIBRE_COLLAB_URL` | unset, or your own relay | Unset leaves [live collaboration](collaboration.md) dark. Set it to a `wss://` relay you run if you want multiplayer editing without the hosted relay. |
@@ -168,6 +168,7 @@ generated final policy; build-only grants do not configure its guards.
| `GEOLIBRE_NO_EXTERNAL_CDN` (build arg) | `1` for restricted deployments | Strips GeoLibre's own references to external CDNs (`unpkg.com`, `cdn.jsdelivr.net`) from the build output. Features whose assets are only available from a CDN are disabled or degraded: storymap HTML export, built-in object detection models, ONNX WASM, 3D Tiles Draco/KTX2 decoders, and gdal3.js export. Pyodide is not hard-disabled — the flag drops only its default index URL, so setting `VITE_PYODIDE_INDEX_URL` to an approved mirror keeps it working. Also forces `GEOLIBRE_PGLITE_CDN=0`, `GEOLIBRE_CEREUS_CDN=0`, `GEOLIBRE_GDAL_CDN=0`, and `GEOLIBRE_DUCKDB_WASM_CDN=0` — so PGlite/PostGIS, CereusDB, and DuckDB-WASM stay **available**, vendored into the build under `/assets/` (at a larger build size) rather than fetched. Note that some third-party packages (DuckDB-WASM, loaders.gl, maplibre-gl-3d-tiles) carry their own internal CDN URLs that this flag cannot remove; see [architecture.md](architecture.md) for the details. Intended for deployments that cannot reference untrusted external CDNs (e.g. enterprise environments with strict CSP requirements). |
| `GEOLIBRE_APP_NAME` | optional, e.g. `Acme Maps` | Replaces "GeoLibre" at the start of the toolbar and in the browser tab title. Whitespace runs collapse to one space and the name is capped at 60 characters. `VITE_GEOLIBRE_APP_NAME` is the equivalent build arg. |
| `VITE_WELCOME_DISABLED=1` (build arg) | optional | Skips the first-launch wizard for every visitor. |
See [Getting Started](getting-started.md#run-with-docker) for the full list.
!!! warning "Before exposing the image publicly"
@@ -192,6 +193,7 @@ least one listed capability must be granted.
| `/sidecar/ml` | `processing:run` |
| `/sidecar/sql` | `processing:run` |
| `/sidecar/postgis` | `data:add` |
| `/sidecar/mssql` | `data:add` |
| `/sidecar/conversion` | `processing:run` **or** `data:add` |
A denied route returns HTTP 403 with an `application/json` body containing
@@ -504,11 +506,11 @@ the public internet:
| Feature | Default | How to keep it internal |
| --- | --- | --- |
| Basemaps | OpenFreeMap / CARTO tiles | Use the Basemaps plugin's **custom style URL** and serve your own style plus a PMTiles basemap from your server, or use a blank background. Add the host to the CSP if it is not your own origin. |
| Basemaps | OpenFreeMap / CARTO tiles | Use the Basemaps plugin's **custom style URL** and serve your own style plus a PMTiles basemap from your server, or use a blank background. Any HTTPS host is already allowed by the CSP; a plain `http://` host needs a CSP change. |
| Geocoding | Public Nominatim | Point it at a self-hosted Nominatim or Pelias (see [Data Integrations](user-guide/data-integrations.md#geocoding)). |
| Routing and isochrones | Public FOSSGIS Valhalla (`valhalla1.openstreetmap.de`) | Set `VITE_ROUTING_ENDPOINT` to your own Valhalla server. This covers Processing → GeoLibre Toolbox → Network and the **Drive time** / **Walk time** [quick actions](user-guide/map-controls.md#quick-analysis-from-a-clicked-point). Add the host to the CSP. |
| Routing and isochrones | Public FOSSGIS Valhalla (`valhalla1.openstreetmap.de`) | Set `VITE_ROUTING_ENDPOINT` to your own Valhalla server. This covers Processing → GeoLibre Toolbox → Network and the **Drive time** / **Walk time** [quick actions](user-guide/map-controls.md#quick-analysis-from-a-clicked-point). It is read at build time, or per browser from Settings → Environment variables; the Docker image has no build arg or runtime variable for it. An HTTPS endpoint needs no CSP change. |
| Pointer elevation readout | Public Open-Meteo elevation API, whenever 3D terrain has no sample for the point | Leave the readout off (it is off by default), or decline the consent prompt GeoLibre shows before the first remote lookup — that is the gate the resolver checks. Enabling 3D terrain makes the remote call rare but does not rule it out, since a point terrain cannot answer still falls through. |
| Python (Pyodide) vector engine | Loads Pyodide from jsDelivr | Set `VITE_PYODIDE_INDEX_URL` to a mirrored copy of the Pyodide distribution. |
| Python (Pyodide) vector engine | Loads Pyodide from jsDelivr | Set `VITE_PYODIDE_INDEX_URL` to a mirrored copy of the Pyodide distribution (at build time, or per browser in Settings → Environment variables). Serve the mirror from your own origin, or add it to the CSP `script-src`, which pins only the jsDelivr Pyodide path. |
| AI assistant | Off unless configured | Leave `GEOLIBRE_AI_URL` unset, or route it through your own proxy. |
| Project sharing | `share.geolibre.app` | `GEOLIBRE_SHARE_URL=off`, or your own [projects server](server-api.md). |
| Collaboration | Off unless configured | Leave `GEOLIBRE_COLLAB_URL` unset, or run `workers/collab-node` yourself. |
+29 -16
View File
@@ -87,7 +87,7 @@ deployment.
| Field | Limit |
| --- | ---: |
| project title (derived from the uploaded project) | 100 Unicode code points |
| username | 3–39 lowercase ASCII letters, digits, or hyphens |
| username | 3–39 lowercase ASCII letters, digits, or hyphens, not starting or ending with a hyphen |
| slug | 1–100 lowercase ASCII letters, digits, or hyphens |
| description | 2,000 Unicode code points |
| tags | 20 tags, 40 Unicode code points each |
@@ -96,7 +96,9 @@ deployment.
| `limit` | default 24, maximum 100 |
Servers may configure a smaller upload limit, but must return `413` and an
`error` explaining that limit.
`error` explaining that limit. The reference server reads its document and
thumbnail limits from `GEOLIBRE_MAX_PROJECT_BYTES` and
`GEOLIBRE_MAX_THUMBNAIL_BYTES`.
## Visibility
@@ -128,7 +130,10 @@ three project scopes. Omitting `expiresInDays` preserves the v1 delete-only
token lifecycle (the token does not expire); the accepted explicit lifetime is
1–365 days. An unknown or empty `scopes` list returns `400`
`{"error": "invalid_scope"}`; an `expiresInDays` outside 1–365 returns `400`
`{"error": "invalid_request"}`.
`{"error": "invalid_request"}`. `email` is optional, trimmed and normalized to
lowercase, validated, and unique when present. A password shorter than 8
characters or a malformed username is `422`; a taken username or email is
`409`.
```json
{
@@ -157,9 +162,7 @@ Response `201`:
Exchanges account credentials for a personal API token. It accepts the same
optional policy fields (but not `email`) and returns the same shape as account
creation. Tokens are opaque and stored only as SHA-256 digests. `email` is
optional at account creation, trimmed and normalized to lowercase, validated,
and unique when present.
creation. Tokens are opaque and stored only as SHA-256 digests.
| Status | `error` |
| --- | --- |
@@ -168,16 +171,19 @@ and unique when present.
| 403 | `password expired` (change it with `POST /api/account/password`) |
| 403 | `single sign-on required` (an organization of the account [disallows built-in accounts](#organization-identity-provider)) |
### `PATCH /api/account`
### `GET /api/account` and `PATCH /api/account`
Requires `write:projects`. `{"email":"ada@example.org"}` sets the signed-in
`GET /api/account` returns `{"account": <account>}` for any valid credential.
`PATCH` requires `write:projects`. `{"email":"ada@example.org"}` sets the signed-in
account's validated, normalized email; `{"email":null}` clears it. A duplicate
email is `409`. The response is `{"account": <account>}` and uses
`Cache-Control: private, no-store`.
### `DELETE /api/auth/token`
Revokes the presented Bearer token. Response: `204`.
Revokes the presented Bearer token: a personal token is deleted, and an OAuth
access token revokes its whole session family. Response: `204`.
### `GET /api/users/me`
@@ -382,7 +388,8 @@ ownership, and an owner cannot leave until ownership is transferred.
Routes:
- `GET /api/groups/mine` lists accepted memberships; `GET /api/groups/{id}`
returns group detail to a signed-in caller.
returns group detail to a signed-in caller. `PATCH /api/groups/{id}` changes
`name`, `description`, or `joinPolicy`; owner or manager only.
- `DELETE /api/groups/{id}` deletes the group with its memberships,
invitations, and thumbnail; owner only. Projects shared with the group are
kept and lose only that group target.
@@ -393,7 +400,7 @@ Routes:
`{username}=me` leaves.
- `POST /api/groups/{id}/invitations` creates a pending invitation for exactly
one `username` or `email`. The creation response includes its opaque token;
manager listings omit the token and retain pending, accepted, and revoked
manager listings (`GET` on the same path) omit the token and retain pending, accepted, and revoked
rows. `DELETE .../invitations/{invitationId}` changes a pending invitation to
`revoked`, and `POST /api/groups/invitations/{token}/accept` changes it to
`accepted` while adding the signed-in target account.
@@ -850,6 +857,9 @@ value the provider puts in that claim (for Entra ID, the UPN in
"versionCount": 1,
"featured": false,
"deleteProtected": false,
"role": "edit",
"expiresAt": null,
"hasPassword": false,
"createdAt": "2026-08-03T12:00:00Z",
"updatedAt": "2026-08-03T12:00:00Z",
"tags": [],
@@ -1123,7 +1133,8 @@ optional `slug`:
Responds `201` with `{"transfer": <transfer>, "project": <project>}`.
- An **organization** target requires that the caller administers the
organization and is applied immediately (the transfer is stored as
`accepted`). It responds `201` with the moved `project`.
`accepted`). It responds `201` with `{"transfer": <transfer>, "project":
<project>}`, the project already moved.
Refusals use stable phrases so clients can explain them:
@@ -1252,7 +1263,8 @@ validated callback with `error`, `iss`, and the exact `state` value when supplie
browser-binding cookie, CSRF value, same-origin `Origin` or `Referer`, and
account credentials. Approval returns `303` to the exact callback with a
single-use code, `state`, and `iss`; cancellation returns `access_denied`.
Authorization codes expire after 60 seconds by default.
Authorization codes expire after 60 seconds by default
(`GEOLIBRE_OAUTH_CODE_TTL_SECONDS`).
Production HTTPS uses a host-only `Secure` browser-binding cookie; permitted
loopback HTTP development uses a host-only non-`Secure` cookie so Safari can
submit the consent form.
@@ -1299,11 +1311,12 @@ management token only in memory and discard it when session management closes,
the project session changes, or the grant expires. A `401` on a management
request requires a new consent; it must not sign the project session out.
Access tokens expire after 600 seconds by default and never outlive their
family. Refresh tokens are single-use and rotate on every use. Reusing a
Access tokens expire after 600 seconds by default
(`GEOLIBRE_OAUTH_ACCESS_TTL_SECONDS`) and never outlive their family. Refresh tokens are single-use and rotate on every use. Reusing a
consumed refresh token revokes the entire family, including tokens minted by
the successful rotation. A family expires at issuance plus the configured
refresh TTL (30 days by default); rotation never extends it.
refresh TTL (`GEOLIBRE_OAUTH_REFRESH_TTL_SECONDS`, 30 days by default);
rotation never extends it.
An enabled server deletes bounded batches of expired interactions, access
tokens, and families at startup, during OAuth requests, and every five minutes
+2 -2
View File
@@ -2,8 +2,8 @@
GeoLibre is free and open-source software, released under the
[MIT License](https://github.com/opengeos/GeoLibre/blob/main/LICENSE) and
developed in the open. There is no paid tier, no seat license, and no telemetry —
everything on this site is available to everyone, forever.
developed in the open. There is no paid tier, no seat license, and no telemetry in
the app — everything on this site is available to everyone, forever.
That model only works if the work behind it is sustainable. If GeoLibre saves you
or your team time, sponsorship is the most direct way to keep it moving.
+2 -2
View File
@@ -35,8 +35,8 @@ A Cloud-Optimized GeoTIFF is a regular GeoTIFF organized so clients can read jus
Use **Processing → GeoLibre Toolbox → Conversion** to write cloud-native files. See [Processing Tools](../user-guide/processing.md#conversion).
- **Vector to GeoParquet** and **CSV to GeoParquet** run in the browser with DuckDB-WASM.
- **Vector to FlatGeobuf**, **Vector to PMTiles**, and **Raster to COG** run on the Python sidecar (desktop app).
- In the browser build, **Vector to GeoParquet** and **CSV to GeoParquet** run on DuckDB-WASM, and **Vector to FlatGeobuf**, **Vector to PMTiles**, and **Raster to COG** run on WebAssembly (`geolibre-wasm`), so none of them needs a sidecar.
- The desktop app runs these on the Python sidecar instead, which reads more input formats (Raster to COG in the browser reads GeoTIFF only) and preserves data types the browser writers cannot.
For example, to publish a local GeoJSON as GeoParquet:
+1 -1
View File
@@ -38,7 +38,7 @@ See [Adding Data](../user-guide/adding-data.md) for every supported source.
## 5. Save or share
- In the desktop app, use **Project → Save** to write a `.geolibre.json` file.
- Use **Project → Save** to write a `.geolibre.json` file. The desktop app saves to disk; the browser build uses the browser's save dialog where it has one and otherwise downloads the file. See [Projects](../user-guide/projects.md#save-and-save-as).
- Anywhere, use **Project → Share** to upload the project and get a public link. See [Sharing & Embedding](sharing-embedding.md).
## Next steps
+1 -1
View File
@@ -5,7 +5,7 @@ These tutorials walk through common GeoLibre workflows end to end. Each one is s
## Before you start
- You can follow most tutorials in the **live viewer** at [web.geolibre.app](https://web.geolibre.app/), which is the browser build of GeoLibre. No installation required.
- A few steps need the **desktop app**: opening and saving project files, reading local MBTiles and rasters, and the Python sidecar tools (raster processing and sidecar conversions). These are called out where they appear. See [Downloads](../downloads.md) to install. The Whitebox geoprocessing toolbox is not among them — its 1,000+ tools run in the browser on WebAssembly.
- A few steps need the **desktop app**: reading local MBTiles and rasters, and the Python sidecar tools (raster tools without a browser engine, such as Contour). These are called out where they appear. See [Downloads](../downloads.md) to install. The Whitebox geoprocessing toolbox is not among them — its 1,000+ tools run in the browser on WebAssembly.
- The sample dataset used in several tutorials is a public GeoParquet file of world countries: `https://data.source.coop/giswqs/opengeos/countries.parquet`.
## The tutorials
+2 -1
View File
@@ -7,7 +7,8 @@ Once you have a map you like, you can publish it as a public link and embed it i
Sharing uploads to `share.geolibre.app`.
- **Web app**: open **Project → Share...** and click **Sign in to share.geolibre.app**. Approve the access request in the popup that opens — you are connected for the browser session.
- **GeoLibre Desktop**: paste a personal API token into **Settings → Environment Variables → Share.GeoLibre API token**. Create one under Settings → API tokens at [share.geolibre.app/settings](https://share.geolibre.app/settings). You only need to do this once.
- **GeoLibre Desktop**: click the same **Sign in to share.geolibre.app** button. The sign-in opens in your system browser, and the desktop app keeps the session in your OS credential store.
- **Fallback (any build)**: paste a personal API token into **Settings → Environment Variables → Share.GeoLibre API token**. Create one under Settings → API tokens at [share.geolibre.app/settings](https://share.geolibre.app/settings).
## 2. Share the project
+31 -30
View File
@@ -31,7 +31,7 @@ The repository is an npm workspaces monorepo with 7 packages plus a desktop appl
![Supported formats](https://assets.geolibre.app/images/add-data-formats.webp)
The KML case is particularly illustrative. `docs/architecture.md:61` states it clearly: KML is handled by a **custom-built parser** to **preserve embedded styling**, outputting simplestyle-spec properties (`fill`, `stroke`, `stroke-width`) so that styled KML looks the same in GeoLibre as it does in Google Earth. Only when the custom parser fails does it fall back to DuckDB Spatial, **at the cost of losing style information**.
The KML case is particularly illustrative. The DuckDB-WASM section of `docs/architecture.md` states it clearly: KML is handled by a **custom-built parser** to **preserve embedded styling**, outputting simplestyle-spec properties (`fill`, `stroke`, `stroke-width`) so that styled KML looks the same in GeoLibre as it does in Google Earth. Only when the custom parser fails does it fall back to DuckDB Spatial, **at the cost of losing style information**.
> **Key insight**: General-purpose libraries inevitably discard format-specific information when unifying data models. And that information is often exactly what users care about most.
@@ -39,18 +39,18 @@ The same pattern repeats for Shapefile: **try shpjs first, only fall back to Duc
### 1.2 Spatial Computation: Four Engine Tiers, Defaulting to the Lightest
This area is easy to misunderstand. Tracing through the source (per `docs/architecture.md:75-79`):
This area is easy to misunderstand. Tracing through the source (per the Python sidecar section of `docs/architecture.md`):
| Engine | Where It Runs | Positioning |
|---|---|---|
| **Turf.js** (`@turf/*`, ~20 sub-packages) | Browser, pure JS | **Default engine for vector tools**, zero dependencies, zero backend |
| **Turf.js** (`@turf/*`, ~25 sub-packages) | Browser, pure JS | **Default engine for vector tools**, zero dependencies, zero backend |
| **GeoPandas / Shapely** | Python sidecar | Upgrade path when **projection-aware** results are needed |
| **GeoPandas / Shapely** | Browser via Pyodide | **Same codebase**, usable in the web version |
| **DuckDB / PGlite+PostGIS / SedonaDB** | Browser or sidecar | Three engines for the SQL Workspace |
Note that Turf is imported **per sub-package** (`@turf/buffer`, `@turf/intersect`, etc.), not as a whole. This is important — importing all of Turf at once is substantial; per-package imports are what make it practical.
The "one codebase, two runtimes" design is documented at `docs/architecture.md:77`: the geometry logic lives in a **framework-free module** `backend/geolibre_server/geolibre_server/vector_ops.py`. A Vite plugin (`vite-plugins/copy-vector-ops.ts`) copies it into the frontend package, and the browser side uses a classic Web Worker to load Pyodide, install `geopandas`, and call `run_vector_tool` across a JSON string boundary.
The "one codebase, two runtimes" design is documented in the same section: the geometry logic lives in a **framework-free module** `backend/geolibre_server/geolibre_server/vector_ops.py`. A Vite plugin (`vite-plugins/copy-vector-ops.ts`) copies it into the frontend package, and the browser side uses a classic Web Worker to load Pyodide, install `geopandas`, and call `run_vector_tool` across a JSON string boundary.
> **Real-world lesson**: Anyone who has worked with GIS knows this pitfall — compute a buffer with Turf.js on the frontend, compute the same buffer with PostGIS on the backend, and the areas differ by 0.3%. Two days later you discover it's because the default segment count differs between the two implementations.
@@ -58,8 +58,8 @@ The "one codebase, two runtimes" design is documented at `docs/architecture.md:7
| Library | Role |
|---|---|
| **`maplibre-gl`** 5.24 | Primary map |
| **`deck.gl`** 9.3 (core/layers/geo-layers/mesh-layers/aggregation-layers/mapbox) | COG, 3D Tiles, I3S, visualization layers, interleaved into the MapLibre canvas |
| **`maplibre-gl`** 6.11 | Primary map |
| **`deck.gl`** 9.4 (core/layers/geo-layers/mesh-layers/aggregation-layers/mapbox) | COG, 3D Tiles, I3S, visualization layers, interleaved into the MapLibre canvas |
| **`maplibre-gl-3d-tiles` / `-lidar` / `-splat` / `-raster` / `-vector`** | **Without switching engines, directly add 3D Tiles, point clouds, and Gaussian splats onto MapLibre** |
| **`@developmentseed/deck.gl-geotiff` / `-raster`** | COG rendering |
| **`@carbonplan/zarr-layer`** | Zarr scientific data |
@@ -67,7 +67,7 @@ The "one codebase, two runtimes" design is documented at `docs/architecture.md:7
| **`@geoman-io/maplibre-geoman-free`** | Drawing and editing |
| **`maplibre-gl-time-slider` / `-swipe` / `-layer-control` / `-basemap-control`** | Interactive controls |
| **`@tanstack/react-virtual`** | Attribute table virtualization |
| **`cesium`** 1.143 | Optional 3D globe split-view, **lazy-loaded ~4.8 MB in a separate chunk** |
| **`cesium`** 1.146 | Optional 3D globe split-view, **lazy-loaded ~4.7 MB in a separate chunk** |
![3D Tiles, vectors, glTF, and Gaussian splats intermixed in a single layer list](https://assets.geolibre.app/images/3dtiles.webp)
@@ -86,7 +86,7 @@ The "one codebase, two runtimes" design is documented at `docs/architecture.md:7
This section is the core of this article. **If you only read one thing, read this.**
First, let's cover WebAssembly in one sentence: the GeoLibre repository has 315 hits for the `wasm` identifier. **Database, language runtime, native toolchain, codec, and machine learning** — all five capability categories are powered by WASM engines: DuckDB, sql.js (SQLite), PGlite + PostGIS, CereusDB (SedonaDB), Pyodide, `geolibre-wasm` (a WASI build of Whitebox), gdal3.js, `cog-tiler-wasm`, h5wasm, onnxruntime-web. **When you trace the native languages, the picture becomes clear: C, C++, Rust. WASM here is not about accelerating JavaScript — it's about bringing decades of accumulated native GIS ecosystem into the browser wholesale.**
First, let's cover WebAssembly in one sentence: the identifier `wasm` turns up well over a thousand times in the GeoLibre source. **Database, language runtime, native toolchain, codec, and machine learning** — all five capability categories are powered by WASM engines: DuckDB, sql.js (SQLite), PGlite + PostGIS, CereusDB (SedonaDB), Pyodide, `geolibre-wasm` (the Whitebox engine compiled to WASI, plus GeoLibre's own Rust tools), gdal3.js, `cog-tiler-wasm`, h5wasm, onnxruntime-web. **When you trace the native languages, the picture becomes clear: C, C++, Rust. WASM here is not about accelerating JavaScript — it's about bringing decades of accumulated native GIS ecosystem into the browser wholesale.**
![Processing menu: a row of WASM engines behind it](https://assets.geolibre.app/images/processing-tools-menu.webp)
@@ -141,12 +141,14 @@ const MANUAL_BUNDLES: duckdb.DuckDBBundles = {
mvp: { mainModule: duckdbWasmMvp, mainWorker: mvpWorker },
eh: { mainModule: duckdbWasmEh, mainWorker: ehWorker },
};
export function selectDuckDbBundle() {
return duckdb.selectBundle(MANUAL_BUNDLES);
export async function selectDuckDbBundle() {
// Imported here so DuckDB-WASM's JS stays off the startup path
const { selectBundle } = await import("@duckdb/duckdb-wasm");
return selectBundle(MANUAL_BUNDLES);
}
```
**Manually listing bundles rather than using the default CDN resolution ensures Vite produces content-hashed local artifacts for the WASM and worker** — which in turn enables safe caching by the Service Worker's CacheFirst strategy (see Section 5).
**Manually listing bundles rather than using the default CDN resolution ensures Vite produces content-hashed local artifacts for the WASM and worker** — which in turn enables safe caching by the Service Worker's CacheFirst strategy (see Section 5). (The `npm run lite:build` variant swaps in `duckdb-wasm-bundles.cdn.ts`, which loads the same pinned version from jsDelivr for hosts with a per-file size cap.)
**Second, extension loading must be "per-instance, run-once."** `INSTALL spatial` goes over the network, and `LOAD` has state; concurrent calls will conflict. GeoLibre's approach is worth studying:
@@ -190,7 +192,7 @@ GeoLibre counters this with a two-pronged approach:
**First, warming up.** The `beforeLoad` hook on `ensureSpatialExtension` is exactly for this — before `LOAD spatial`, it runs `SELECT 1 FROM read_parquet(…) LIMIT 0` against whatever remote reader this query uses. **`LIMIT 0` only fetches the Parquet footer, incurring almost no additional traffic.** If this particular query has no remote Parquet, it falls back to reading a small public sample file.
**Second, rebuilding.** If the bug still strikes (e.g., the warm-up failed previously), `runSqlQuery` catches `stoi: no conversion`, calls `resetSqlDatabase(poisoned)` to replace the entire instance, and then **retries once**. The rebuild goes through the warm-up again, so the second attempt is clean.
**Second, rebuilding.** If the bug still strikes (e.g., the warm-up failed previously), `runSqlQuery` catches `stoi: no conversion`, calls `resetSqlDatabase(db)` to replace the entire instance, and then **retries once**. The rebuild goes through the warm-up again, so the second attempt is clean.
Two additional defensive details: the retry is triggered only when the statement **actually contains a remote reader call** (URLs appearing inside string literals don't count), and `resetSqlDatabase` confirms that "the instance to replace is still the current instance" before acting.
@@ -247,11 +249,11 @@ Threshold constants in the source (all verifiable):
| Constant | Value | Location | What It Protects |
|---|---|---|---|
| `LARGE_VECTOR_FEATURE_THRESHOLD` | **50,000** | `core/src/types.ts:670` | Main-thread GeoJSON parsing |
| `LARGE_VECTOR_FEATURE_THRESHOLD` | **50,000** | `core/src/types.ts:783` | Main-thread GeoJSON parsing |
| `maxHistoryFeatureCount` | 500,000 | `core/src/history.ts:29` | Undo stack memory |
| `DUCKDB_VECTOR_FEATURE_WARN_COUNT` | 100,000 | `core/src/types.ts:1838` | Result materialization memory |
| `DUCKDB_VECTOR_FEATURE_WARN_COUNT` | 100,000 | `core/src/types.ts:2540` | Result materialization memory |
| `MAX_CEREUS_FEATURES` | 50,000 | `lib/sedona-workspace.ts:25` | WASM heap |
| `MAX_DERIVED_FEATURES` | 50,000 | `map/src/derived-geometry.ts:37` | Derived geometry computation |
| `MAX_DERIVED_FEATURES` | 50,000 | `map/src/derived-geometry.ts:42` | Derived geometry computation |
| `historyCoalesceMs` | 400 ms | `core/src/history.ts:6` | Undo record explosion |
| Remote files | 2 GiB | `plugins/remote-file-formats.ts` | DuckDB-WASM 32-bit |
@@ -261,19 +263,20 @@ Threshold constants in the source (all verifiable):
### 3.2 Client-Side Tiling: On-the-Fly Tile Generation Above 50K Features
The full pipeline is in `packages/map/src/geojson-vt-protocol.ts`, step by step:
The pipeline lives in `packages/map/src/geojson-vt-protocol.ts` (protocol and registry) and `geojson-vt-index.ts` (index building and encoding), step by step:
- Indexing uses **`@maplibre/geojson-vt`** (note: this is MapLibre's fork; **Supercluster is included in this package** — the comments say it's "the same engine MapLibre uses internally")
- Point layers use `Supercluster` for indexing; everything else uses `GeoJSONVT`
- Encoding uses `@maplibre/vt-pbf`'s `fromGeojsonVt`
- Fed to MapLibre via a custom protocol `geolibre-gjvt`
- `TILE_EXTENT = 4096`, `TILE_MAX_ZOOM = 16` (beyond this, let MapLibre over-zoom)
- Index building and tile encoding run on a Web Worker (`geojson-vt.worker.ts`), so a large load no longer freezes the main thread; where no Worker is available, the same code runs inline
![Large vector data loading](https://assets.geolibre.app/demos/vector-data-demo.gif)
Two details worth highlighting individually.
**First, tile indexes live in a module-level Map, not in the store.** The source comment puts it this way:
**First, the per-layer tile registry lives in a module-level Map, not in the store.** The source comment puts it this way:
> Keyed by layer id. Module-level rather than on the Zustand record because **tile indexes are large, non-serializable objects that must not enter app state or be written to `.geolibre.json`**.
@@ -282,8 +285,8 @@ Two details worth highlighting individually.
**Second, check the abort signal before encoding.** One line:
```ts
// packages/map/src/geojson-vt-protocol.ts:150
if (abortController?.signal.aborted) return { data: new ArrayBuffer(0) };
// packages/map/src/geojson-vt-protocol.ts
if (signal?.aborted) return empty;
```
MapLibre cancels tile requests that scroll off-screen, and the result would be discarded anyway — so don't waste the computation.
@@ -295,7 +298,7 @@ MapLibre cancels tile requests that scroll off-screen, and the result would be d
### 3.3 Undo Stack: Three Refinements Worth Studying
The file `packages/core/src/history.ts` is worth opening directly — about a hundred lines, high density. It uses `zundo` (Zustand's time-travel middleware).
The file `packages/core/src/history.ts` is worth opening directly — under two hundred lines, high density. It uses `zundo` (Zustand's time-travel middleware).
The problem: **Every snapshot holds the full GeoJSON of layers.** Repeated editing pins multiple copies in memory (the comments reference issue #341).
@@ -311,7 +314,7 @@ let total = distinctFeatureCount(pastStates[lastIndex], seen);
**"When memory is tight, clear the undo stack" is many projects' approach — but that means the user, at the very moment they most need undo (right after a large edit), has exactly no undo available.**
**Treatment 3: Deduplicate by object reference.** `distinctFeatureCount` uses a `Set<object>` to track seen payloads: unchanged layers share the same reference across snapshots and are counted only once. **So "keeping many small-layer snapshots" has almost no additional memory cost.**
**Treatment 3: Deduplicate by object reference.** `distinctFeatureCount` uses a `Set` to track seen payloads: unchanged layers share the same reference across snapshots and are counted only once. **So "keeping many small-layer snapshots" has almost no additional memory cost.**
One more easily overlooked detail: that 400 ms is not ordinary debouncing — it's **leading-edge debounce**, serving as zundo's `handleSet`:
@@ -327,17 +330,15 @@ This sounds modest, but it's a high-frequency bug: many virtual list implementat
### 3.5 Bundle Size: How Far Does Lazy Loading Go?
The `manualChunks` configuration in `apps/geolibre-desktop/vite.config.ts` is the core of this approach. Take the most representative example — Cesium:
The code-splitting configuration in `apps/geolibre-desktop/vite.config.ts` (`manualChunks` plus the Rolldown `CODE_SPLITTING_GROUPS`) is the core of this approach. Take the most representative example — Cesium. The source comments record that when Cesium's chunk once swallowed shared helpers, "the whole ~4.7 MB Cesium chunk was modulepreloaded on every boot"; it now sits in its own group because **"Cesium only loads when a pane switches to the 3D globe."**
> CesiumJS (~4.8 MB) for the 3D-globe view. Lazily imported only when a pane switches to the globe……kept in its own build chunk and **off the 2D boot path**.
**"Off the boot path" is the key phrase.** For web GIS, this is the easiest performance win: the users who actually need 3D may only be 20%; there is no reason for 100% of users to pay the first-screen cost for it.
**Keeping it off the boot path is the key point.** For web GIS, this is the easiest performance win: the users who actually need 3D may only be 20%; there is no reason for 100% of users to pay the first-screen cost for it.
A detailed size breakdown from the source comments, organized here:
| Heavy Resource | Size | Strategy |
|---|---|---|
| CesiumJS | ~4.8 MB | Separate chunk, `import()` only when switching to globe view |
| CesiumJS | ~4.7 MB | Separate chunk, `import()` only when switching to globe view |
| PGlite + PostGIS | ~25 MB (bundling into desktop would add **~22 MB of nearly incompressible size**) | Default via jsDelivr CDN, not included in the build |
| gdal3.js | WASM ~28 MB + data ~12 MB | **Never bundled**, always from CDN; disabling CDN disables the feature |
| Pyodide / CereusDB | Tens of MB each | CDN-loaded; PGlite and CereusDB can be bundled-in via build switches |
@@ -383,7 +384,7 @@ This section is pure web engineering, less GIS-specific, but has the lowest migr
![Layer panel: all parameters modify the store](https://assets.geolibre.app/images/raster-style-panel.webp)
**Pattern 1: Constant array as single source of truth.** The 20 layer types at `packages/core/src/types.ts:62`:
**Pattern 1: Constant array as single source of truth.** The 20 layer types at `packages/core/src/types.ts:63`:
```ts
export const LAYER_TYPES = [
@@ -413,7 +414,7 @@ This minimalism is deliberate. The smaller the state, the greater the confidence
**Pattern 3: Cross-language boundaries marked with `SYNC:`.** This was the most surprising discovery from reading through the source.
The 17 vector file extensions exist as `VECTOR_FILE_DIALOG_EXTENSIONS` in TypeScript (`lib/tauri-io.ts:154`) and as `RESTORABLE_VECTOR_EXTENSIONS: [&str; 17]` in Rust (`src-tauri/src/lib.rs:418`) — **two copies, because constants can't be shared across languages**. Their solution is to annotate both sides. Quoted verbatim from `lib/tauri-io.ts:150-153`:
The 17 vector file extensions exist as `VECTOR_FILE_DIALOG_EXTENSIONS` in TypeScript (`lib/file-io/paths.ts:39`) and as `RESTORABLE_VECTOR_EXTENSIONS: [&str; 17]` in Rust (`src-tauri/src/lib.rs:800`) — **two copies, because constants can't be shared across languages**. Their solution is to annotate both sides. Quoted verbatim from `lib/file-io/paths.ts:35-38`:
> SYNC: RESTORABLE_VECTOR_EXTENSIONS in src-tauri/src/lib.rs must list the same extensions, or a format added here would be rejected by the Rust restore guard on every project reopen (**the bug this PR fixes**). Grep "SYNC:" to find the partner list.
@@ -425,7 +426,7 @@ Note the parenthetical "the bug this PR fixes" — it refers to the pull request
## 5. Offline Capability: Workbox Three-Tier Caching Strategy
This section stands alone because it's especially relevant for intranet, offline, and government deployment scenarios, and `docs/architecture.md:83-100` documents it comprehensively.
This section stands alone because it's especially relevant for intranet, offline, and government deployment scenarios, and the "Offline support (PWA)" section of `docs/architecture.md` documents it comprehensively.
The web build is an installable PWA using `vite-plugin-pwa` + Workbox. Caching is **deliberately split into three tiers**:
@@ -547,7 +548,7 @@ One more detail worth remembering: Source Cooperative's unknown API paths don't
COG's ability to be read via Range requests depends on **internal tiling and pyramid overviews within the file**. A striped GeoTIFF can technically be range-read too — its strip offsets and byte counts are in the header — but a strip spans the full image width, so fetching a small map extent drags in far more bytes than it needs, and with no overviews there is no coarse level to zoom out against. Naming it `.tif` changes none of that, and GeoLibre's client-side readers require internal tiles outright.
GeoLibre's handling is instructive: the panel **first reads the file header with a Range request** to determine whether internal tiles exist; if not, it prompts the user to go through client-side conversion (the gdal3.js path) before loading. **A few KB of header for a definitive answer, rather than making the user wait through an inevitably slow load.**
GeoLibre's handling is instructive: the panel **first reads the file header with a Range request** to determine whether internal tiles exist; if not, it offers to convert the file to a COG in the browser (geolibre-wasm's `CogBuilder`, in `packages/processing/src/cog-convert.ts`) before loading. **A few KB of header for a definitive answer, rather than making the user wait through an inevitably slow load.**
The trade-offs among the three raster engines are also documented in the source: `cog-tiler-wasm` (default, browser WASM, **at the cost of only using built-in color ramps** — custom classification is lost), `maplibre-gl-raster` (GPU, full symbology), `titiler` (server-side). **The default is chosen for stability, not feature completeness.**
+31 -30
View File
@@ -34,7 +34,7 @@
![支持格式](https://assets.geolibre.app/images/add-data-formats.webp)
KML 那条尤其能说明取舍。`docs/architecture.md:61` 写得很直白:KML 由**自研解析器**读取,为的是**保留内嵌符号化**,输出 simplestyle-spec 属性(`fill`、`stroke`、`stroke-width`),这样带样式的 KML 在 GeoLibre 里和在 Google Earth 里长得一样;解析器读不了的才退回 DuckDB Spatial,**代价是丢掉样式**。
KML 那条尤其能说明取舍。`docs/architecture.md` 的 DuckDB-WASM 一节写得很直白:KML 由**自研解析器**读取,为的是**保留内嵌符号化**,输出 simplestyle-spec 属性(`fill`、`stroke`、`stroke-width`),这样带样式的 KML 在 GeoLibre 里和在 Google Earth 里长得一样;解析器读不了的才退回 DuckDB Spatial,**代价是丢掉样式**。
> **关键启发:** 通用库为了统一数据模型,一定会丢掉格式特有的信息。而那些信息往往正是用户最在意的部分。
@@ -42,18 +42,18 @@ KML 那条尤其能说明取舍。`docs/architecture.md:61` 写得很直白:KM
### 1.2 空间计算:四层引擎,默认最轻
这块的分工很容易看混,按源码捋一遍(依据 `docs/architecture.md:75-79`):
这块的分工很容易看混,按源码捋一遍(依据 `docs/architecture.md` 的 Python sidecar 一节):
| 引擎 | 在哪跑 | 定位 |
|---|---|---|
| **Turf.js**(`@turf/*` 二十来个子包) | 浏览器纯 JS | **矢量工具的默认引擎**,零依赖零后端 |
| **Turf.js**(`@turf/*` 二十五个左右子包) | 浏览器纯 JS | **矢量工具的默认引擎**,零依赖零后端 |
| **GeoPandas / Shapely** | Python sidecar | 需要**投影感知**结果时的升级项 |
| **GeoPandas / Shapely** | 浏览器 Pyodide | **同一份代码**,Web 版也能用 |
| **DuckDB / PGlite+PostGIS / SedonaDB** | 浏览器 或 sidecar | SQL Workspace 的三个引擎 |
注意 Turf 是**按需引入子包**的(`@turf/buffer`、`@turf/intersect`……),不是整包 import。这个习惯很重要——Turf 全量引入是很大一坨,按子包引才有意义。
那条「一份代码两处运行」的设计写在 `docs/architecture.md:77`:几何逻辑是一个**无框架模块** `backend/geolibre_server/geolibre_server/vector_ops.py`,一个 Vite 插件(`vite-plugins/copy-vector-ops.ts`)把它复制进前端包,浏览器侧用一个经典 Web Worker 加载 Pyodide、装 `geopandas`、通过 JSON 字符串边界调 `run_vector_tool`。
那条「一份代码两处运行」的设计也写在同一节:几何逻辑是一个**无框架模块** `backend/geolibre_server/geolibre_server/vector_ops.py`,一个 Vite 插件(`vite-plugins/copy-vector-ops.ts`)把它复制进前端包,浏览器侧用一个经典 Web Worker 加载 Pyodide、装 `geopandas`、通过 JSON 字符串边界调 `run_vector_tool`。
> **实际教训:** 做过 GIS 的都知道这个坑——前端 turf 算缓冲区,后端 PostGIS 算缓冲区,面积差 0.3%,追两天发现是分段数默认值不同。
@@ -61,8 +61,8 @@ KML 那条尤其能说明取舍。`docs/architecture.md:61` 写得很直白:KM
| 库 | 负责 |
|---|---|
| **`maplibre-gl`** 5.24 | 主地图 |
| **`deck.gl`** 9.3(core/layers/geo-layers/mesh-layers/aggregation-layers/mapbox) | COG、3D Tiles、I3S、可视化图层,交织进 MapLibre 画布 |
| **`maplibre-gl`** 6.11 | 主地图 |
| **`deck.gl`** 9.4(core/layers/geo-layers/mesh-layers/aggregation-layers/mapbox) | COG、3D Tiles、I3S、可视化图层,交织进 MapLibre 画布 |
| **`maplibre-gl-3d-tiles` / `-lidar` / `-splat` / `-raster` / `-vector`** | **不换引擎,MapLibre 上直接加 3D Tiles、点云、高斯泼溅** |
| **`@developmentseed/deck.gl-geotiff` / `-raster`** | COG 渲染 |
| **`@carbonplan/zarr-layer`** | Zarr 科学数据 |
@@ -70,7 +70,7 @@ KML 那条尤其能说明取舍。`docs/architecture.md:61` 写得很直白:KM
| **`@geoman-io/maplibre-geoman-free`** | 绘制与编辑 |
| **`maplibre-gl-time-slider` / `-swipe` / `-layer-control` / `-basemap-control`** | 交互控件 |
| **`@tanstack/react-virtual`** | 属性表虚拟化 |
| **`cesium`** 1.143 | 可选的三维球分屏,**懒加载 ~4.8MB 独立 chunk** |
| **`cesium`** 1.146 | 可选的三维球分屏,**懒加载 ~4.7MB 独立 chunk** |
![3D Tiles、矢量、glTF、高斯泼溅混排在同一个图层列表](https://assets.geolibre.app/images/3dtiles.webp)
@@ -89,7 +89,7 @@ KML 那条尤其能说明取舍。`docs/architecture.md:61` 写得很直白:KM
这一节是这篇的重点。**如果只看一件事,看这个。**
先把 WebAssembly 这件事一句话说完:GeoLibre 仓库里带 `wasm` 的标识符有 315 处命中,**数据库、语言运行时、原生工具链、编解码、机器学习**五类能力全部由 WASM 引擎承担——DuckDB、sql.js(SQLite)、PGlite + PostGIS、CereusDB(SedonaDB)、Pyodide、`geolibre-wasm`(Whitebox 的 WASI 构建)、gdal3.js、`cog-tiler-wasm`、h5wasm、onnxruntime-web。**原生语言连起来看就明白了:C、C++、Rust。WASM 在这里不是给 JS 加速,是把 GIS 领域几十年积累的原生生态整体搬进浏览器。**
先把 WebAssembly 这件事一句话说完:`wasm` 这个标识符在 GeoLibre 源码里出现了一千多次,**数据库、语言运行时、原生工具链、编解码、机器学习**五类能力全部由 WASM 引擎承担——DuckDB、sql.js(SQLite)、PGlite + PostGIS、CereusDB(SedonaDB)、Pyodide、`geolibre-wasm`(编译成 WASI 的 Whitebox 引擎,外加 GeoLibre 自己的 Rust 工具)、gdal3.js、`cog-tiler-wasm`、h5wasm、onnxruntime-web。**原生语言连起来看就明白了:C、C++、Rust。WASM 在这里不是给 JS 加速,是把 GIS 领域几十年积累的原生生态整体搬进浏览器。**
![数据处理菜单:背后是一排 WASM 引擎](https://assets.geolibre.app/images/processing-tools-menu.webp)
@@ -144,12 +144,14 @@ const MANUAL_BUNDLES: duckdb.DuckDBBundles = {
mvp: { mainModule: duckdbWasmMvp, mainWorker: mvpWorker },
eh: { mainModule: duckdbWasmEh, mainWorker: ehWorker },
};
export function selectDuckDbBundle() {
return duckdb.selectBundle(MANUAL_BUNDLES);
export async function selectDuckDbBundle() {
// 在这里才 import,让 DuckDB-WASM 的 JS 不进启动路径
const { selectBundle } = await import("@duckdb/duckdb-wasm");
return selectBundle(MANUAL_BUNDLES);
}
```
**手动列 bundle 而不是用默认 CDN 解析,是为了让 Vite 把 wasm 和 worker 打成带哈希的本地产物**——这样才能被 Service Worker 的 CacheFirst 安全缓存(见第五节)。
**手动列 bundle 而不是用默认 CDN 解析,是为了让 Vite 把 wasm 和 worker 打成带哈希的本地产物**——这样才能被 Service Worker 的 CacheFirst 安全缓存(见第五节)。(`npm run lite:build` 那个变体会换成 `duckdb-wasm-bundles.cdn.ts`,从 jsDelivr 加载同一个锁定版本,给有单文件体积上限的托管平台用。)
**第二,扩展加载必须做成「按实例只跑一次」。** `INSTALL spatial` 会走网络,`LOAD` 有状态,并发调用会互相打架。GeoLibre 的做法很值得借鉴:
@@ -191,7 +193,7 @@ GeoLibre 的应对是两手:
**一是预热。** `ensureSpatialExtension` 那个 `beforeLoad` 钩子就是干这个的——在 `LOAD spatial` 之前,先拿这条查询自己的远程 reader 跑一次 `SELECT 1 FROM read_parquet(…) LIMIT 0`。**`LIMIT 0` 只会取 Parquet 的 footer,几乎不产生额外流量。**如果这条查询本身没有远程 Parquet,就退回读一个公开的小样本文件兜底。
**二是重建。** 万一还是中招了(比如上一次预热失败),`runSqlQuery` 会捕获 `stoi: no conversion`、调 `resetSqlDatabase(poisoned)` 把那个实例整个换掉、然后**重试一次**。重建时会重新走一遍预热,所以第二次是干净的。
**二是重建。** 万一还是中招了(比如上一次预热失败),`runSqlQuery` 会捕获 `stoi: no conversion`、调 `resetSqlDatabase(db)` 把那个实例整个换掉、然后**重试一次**。重建时会重新走一遍预热,所以第二次是干净的。
这里还有两个防御细节:重试只在语句里**确实有远程 reader 调用**时才触发(字符串字面量里出现的 URL 不算),并且 `resetSqlDatabase` 会先确认「要换掉的实例仍是当前实例」再动手。
@@ -248,11 +250,11 @@ compile_error!("the `mas` (Mac App Store) build must not enable `native-duckdb`:
| 常量 | 值 | 位置 | 保护什么 |
|---|---|---|---|
| `LARGE_VECTOR_FEATURE_THRESHOLD` | **50,000** | `core/src/types.ts:670` | 主线程 GeoJSON 解析 |
| `LARGE_VECTOR_FEATURE_THRESHOLD` | **50,000** | `core/src/types.ts:783` | 主线程 GeoJSON 解析 |
| `maxHistoryFeatureCount` | 500,000 | `core/src/history.ts:29` | 撤销栈内存 |
| `DUCKDB_VECTOR_FEATURE_WARN_COUNT` | 100,000 | `core/src/types.ts:1838` | 结果物化内存 |
| `DUCKDB_VECTOR_FEATURE_WARN_COUNT` | 100,000 | `core/src/types.ts:2540` | 结果物化内存 |
| `MAX_CEREUS_FEATURES` | 50,000 | `lib/sedona-workspace.ts:25` | WASM 堆 |
| `MAX_DERIVED_FEATURES` | 50,000 | `map/src/derived-geometry.ts:37` | 派生几何计算 |
| `MAX_DERIVED_FEATURES` | 50,000 | `map/src/derived-geometry.ts:42` | 派生几何计算 |
| `historyCoalesceMs` | 400 ms | `core/src/history.ts:6` | 撤销记录爆炸 |
| 远程文件 | 2 GiB | `plugins/remote-file-formats.ts` | DuckDB-WASM 32 位 |
@@ -262,19 +264,20 @@ compile_error!("the `mas` (Mac App Store) build must not enable `native-duckdb`:
### 3.2 客户端切片:超过 5 万要素就现场切瓦片
完整流水线在 `packages/map/src/geojson-vt-protocol.ts`,一步步是这样的:
流水线在 `packages/map/src/geojson-vt-protocol.ts`(协议和注册表)和 `geojson-vt-index.ts`(建索引和编码)里,一步步是这样的:
- 索引用 **`@maplibre/geojson-vt`**(注意是 MapLibre 的 fork,**Supercluster 也在这个包里**,注释说它就是「the same engine MapLibre uses internally」)
- 点图层用 `Supercluster` 索引,其它用 `GeoJSONVT`
- 编码用 `@maplibre/vt-pbf` 的 `fromGeojsonVt`
- 通过自定义协议 `geolibre-gjvt` 喂给 MapLibre
- `TILE_EXTENT = 4096`,`TILE_MAX_ZOOM = 16`(超过就让 MapLibre over-zoom)
- 建索引和瓦片编码跑在 Web Worker(`geojson-vt.worker.ts`)里,大图层加载时不再卡主线程;没有 Worker 的环境下,同一套代码直接在主线程跑
![大数据量矢量加载](https://assets.geolibre.app/demos/vector-data-demo.gif)
两个细节值得单独说。
**第一,瓦片索引放模块级 Map,不进 store。** 源码注释是这么写的:
**第一,按图层存的瓦片注册表放模块级 Map,不进 store。** 源码注释是这么写的:
> Keyed by layer id. Module-level rather than on the Zustand record because **tile indexes are large, non-serializable objects that must not enter app state or be written to `.geolibre.json`**.
@@ -283,8 +286,8 @@ compile_error!("the `mas` (Mac App Store) build must not enable `native-duckdb`:
**第二,编码前先看中止信号。** 就一行:
```ts
// packages/map/src/geojson-vt-protocol.ts:150
if (abortController?.signal.aborted) return { data: new ArrayBuffer(0) };
// packages/map/src/geojson-vt-protocol.ts
if (signal?.aborted) return empty;
```
因为 MapLibre 会取消滚出屏幕的瓦片请求,结果反正会被丢掉,那就别算。
@@ -296,7 +299,7 @@ if (abortController?.signal.aborted) return { data: new ArrayBuffer(0) };
### 3.3 撤销栈:三个精细到值得借鉴的处理
`packages/core/src/history.ts` 这个文件值得直接打开看,一百多行,密度很高。用的是 `zundo`(Zustand 的时间旅行中间件)。
`packages/core/src/history.ts` 这个文件值得直接打开看,不到两百行,密度很高。用的是 `zundo`(Zustand 的时间旅行中间件)。
问题:**每个快照都持有图层的完整 GeoJSON**,反复编辑会把好几份副本钉在内存里(注释里点了 issue #341)。
@@ -312,7 +315,7 @@ let total = distinctFeatureCount(pastStates[lastIndex], seen);
**「内存不够就清空撤销栈」是很多项目的做法,但那意味着用户在最需要撤销的那一刻——刚做完一次大编辑——恰好没有撤销可用。**
**处理三,按对象引用去重。** `distinctFeatureCount` 用一个 `Set<object>` 记住见过的 payload:没改动的图层在多个快照间共享同一个引用,只算一次。**所以「保留很多个小图层的快照」几乎没有额外内存开销。**
**处理三,按对象引用去重。** `distinctFeatureCount` 用一个 `Set` 记住见过的 payload:没改动的图层在多个快照间共享同一个引用,只算一次。**所以「保留很多个小图层的快照」几乎没有额外内存开销。**
还有一个容易忽略的细节:那个 400ms 不是普通防抖,是**前沿防抖(leading-edge debounce)**,作为 zundo 的 `handleSet`:
@@ -328,17 +331,15 @@ let total = distinctFeatureCount(pastStates[lastIndex], seen);
### 3.5 体积:懒加载做到什么程度
`apps/geolibre-desktop/vite.config.ts` 里的 `manualChunks` 是这套东西的核心。举个最有代表性的例子——Cesium:
`apps/geolibre-desktop/vite.config.ts` 里的分包配置(`manualChunks` 加上 Rolldown 的 `CODE_SPLITTING_GROUPS`)是这套东西的核心。举个最有代表性的例子——Cesium。源码注释记录过一次教训:Cesium 的 chunk 曾经吞进了共享的辅助模块,结果「the whole ~4.7 MB Cesium chunk was modulepreloaded on every boot」;现在它单独成组,因为 **「Cesium only loads when a pane switches to the 3D globe.」**
> CesiumJS (~4.8 MB) for the 3D-globe view. Lazily imported only when a pane switches to the globe……kept in its own build chunk and **off the 2D boot path**.
**「off the boot path」是关键词。** 对 Web 端 GIS 来说,最容易获得的性能提升就是这个:真正需要三维的用户可能只有 20%,没理由让 100% 的人为它承担首屏加载开销。
**让它不进启动路径,是关键。** 对 Web 端 GIS 来说,最容易获得的性能提升就是这个:真正需要三维的用户可能只有 20%,没理由让 100% 的人为它承担首屏加载开销。
源码注释中有一份详细的体积分析,整理如下:
| 重资源 | 体积 | 处理方式 |
|---|---|---|
| CesiumJS | ~4.8 MB | 独立 chunk,切到球面视图才 `import()` |
| CesiumJS | ~4.7 MB | 独立 chunk,切到球面视图才 `import()` |
| PGlite + PostGIS | ~25 MB(打进桌面端会多 **~22 MB 几乎不可压缩的体积**) | 默认走 jsDelivr CDN,不进构建 |
| gdal3.js | wasm ~28 MB + data ~12 MB | **从不打包**,只从 CDN 取;关掉 CDN 就是关掉这个功能 |
| Pyodide / CereusDB | 各数十 MB | CDN 加载,PGlite 和 CereusDB 可用构建开关改成内置 |
@@ -384,7 +385,7 @@ let total = distinctFeatureCount(pastStates[lastIndex], seen);
![图层面板:所有参数改的都是 store](https://assets.geolibre.app/images/raster-style-panel.webp)
**模式一,常量数组当单一来源。** `packages/core/src/types.ts:62` 那 20 个图层类型:
**模式一,常量数组当单一来源。** `packages/core/src/types.ts:63` 那 20 个图层类型:
```ts
export const LAYER_TYPES = [
@@ -414,7 +415,7 @@ export interface MapViewState {
**模式三,跨语言边界用 `SYNC:` 标记。** 这是翻阅源码过程中最意外的收获。
那 17 个矢量扩展名,在 TS 里是 `VECTOR_FILE_DIALOG_EXTENSIONS`(`lib/tauri-io.ts:154`),在 Rust 里是 `RESTORABLE_VECTOR_EXTENSIONS: [&str; 17]`(`src-tauri/src/lib.rs:418`)——**两份,因为跨语言没法共享常量**。他们的处理是在两边都写注释,下面这段原样引自 `lib/tauri-io.ts:150-153`:
那 17 个矢量扩展名,在 TS 里是 `VECTOR_FILE_DIALOG_EXTENSIONS`(`lib/file-io/paths.ts:39`),在 Rust 里是 `RESTORABLE_VECTOR_EXTENSIONS: [&str; 17]`(`src-tauri/src/lib.rs:800`)——**两份,因为跨语言没法共享常量**。他们的处理是在两边都写注释,下面这段原样引自 `lib/file-io/paths.ts:35-38`:
> SYNC: RESTORABLE_VECTOR_EXTENSIONS in src-tauri/src/lib.rs must list the same extensions, or a format added here would be rejected by the Rust restore guard on every project reopen (**the bug this PR fixes**). Grep "SYNC:" to find the partner list.
@@ -426,7 +427,7 @@ export interface MapViewState {
## 五、离线能力:Workbox 三层缓存策略
这节单独拎出来,因为国内做内网、离线、政务项目的人特别需要,而 `docs/architecture.md:83-100` 把这套写得非常完整。
这节单独拎出来,因为国内做内网、离线、政务项目的人特别需要,而 `docs/architecture.md` 的「Offline support (PWA)」一节把这套写得非常完整。
Web 构建是一个可安装的 PWA,用 `vite-plugin-pwa` + Workbox。缓存**刻意分成三层**:
@@ -548,7 +549,7 @@ Web 构建是一个可安装的 PWA,用 `vite-plugin-pwa` + Workbox。缓存**
COG 能被 range 直读,靠的是**文件内部有瓦片切分和金字塔(overviews)**。条带式 GeoTIFF 严格说也能 range 读——条带的偏移和字节数就写在头里——但一个条带横跨整幅影像的宽度,取一小块地图范围要连带拉回远超所需的字节,而且没有 overviews 就没有可供缩小时使用的粗级别。后缀叫不叫 `.tif` 都改变不了这一点,况且 GeoLibre 的客户端读取器直接要求文件内部有瓦片。
GeoLibre 的处理值得借鉴:面板**先用一个 Range 请求把文件头读回来**,判断有没有内部瓦片;没有就提示走客户端转换(gdal3.js 那条路)再加载。**用几 KB 的头部换一个明确判断,而不是让用户等一次必然很慢的加载。**
GeoLibre 的处理值得借鉴:面板**先用一个 Range 请求把文件头读回来**,判断有没有内部瓦片;没有就提示在浏览器里先转成 COG(geolibre-wasm 的 `CogBuilder`,见 `packages/processing/src/cog-convert.ts`)再加载。**用几 KB 的头部换一个明确判断,而不是让用户等一次必然很慢的加载。**
三个栅格引擎的取舍源码里也写清楚了:`cog-tiler-wasm`(默认,浏览器 WASM,**代价是只能用内置色带**,自定义分级失效)、`maplibre-gl-raster`(GPU,符号化完整)、`titiler`(服务端)。**默认值选的是最稳的那个,不是功能最全的。**
+3 -2
View File
@@ -35,7 +35,8 @@ The spatial extension is loaded, so `ST_*` functions are available. For example,
```sql
-- area is in square degrees because the data is in EPSG:4326;
-- the ranking is valid, but use ST_Area_Spheroid(geom) / 1e6 for km2.
-- the ranking is valid, but for km2 use
-- ST_Area_Spheroid(ST_FlipCoordinates(geom)) / 1e6 (it expects lat/lon order).
SELECT NAME, ST_Area(geom) AS area, geom
FROM https://data.source.coop/giswqs/opengeos/countries.parquet
WHERE CONTINENT = 'Africa'
@@ -51,7 +52,7 @@ When a query returns a geometry column, click **Add as layer** to create a new l
Export the query result as **CSV** or **GeoParquet** straight from the workspace — the **Export CSV** and **Export GeoParquet** buttons sit beside **Add as layer**. See [SQL Workspace](../user-guide/sql-workspace.md).
!!! tip "Sample queries and history"
Use the **Sample queries** menus to start from a working query, and the **history** to rerun a previous one.
Use the **Sample queries…** menu to start from a working query, and **History…** to rerun a previous one.
## Next steps
+17 -17
View File
@@ -15,7 +15,7 @@ Before listing formats, it's important to clarify "where it runs," since many fo
| Runtime | How | Notes |
|---|---|---|
| Browser | Open `web.geolibre.app` | No installation needed; works offline after initial load |
| Desktop | Tauri v2 native application | Windows / macOS / Linux; available via Microsoft Store, Homebrew, winget, AUR, Flatpak |
| Desktop | Tauri v2 native application | Windows / macOS / Linux; available via Microsoft Store, Mac App Store, Homebrew, winget, AUR, Flatpak |
| Android | Google Play native app | ~40 MB per ABI |
| iOS | App Store native app | iPhone and iPad, same codebase via Tauri v2 mobile |
| Jupyter | `pip install geolibre` | The entire application embedded in a notebook cell |
@@ -50,6 +50,7 @@ What's truly interesting is **which engine reads each format behind the scenes**
| **GML** | `.gml` | DuckDB `ST_Read` | — |
| **GPX** | `.gpx` | Pure JS | **Auto-splits into three layers**: waypoints / tracks / routes |
| **CSV / TSV** | `.csv` `.tsv` `.txt` `.dat` | Custom + DuckDB fallback | Auto-detects delimiter and lat/lon columns; WKT geometry columns go through DuckDB; dialog allows specifying source CRS |
| **Excel** | `.xlsx` `.xls` | SheetJS (`@e965/xlsx`) | Opened from the Delimited Text panel: pick a worksheet, then the coordinate columns |
| **CAD (DXF/DWG)** | `.dxf` `.dwg` | DuckDB `ST_Read` | Presents a layer list for selection; **CAD files lack coordinate systems — EPSG must be selected manually** |
| **MapInfo TAB** | `.tab` | `ST_Read` | — |
| **Esri File Geodatabase** | `.gdb` **folder** | Python sidecar | Desktop-only, requires sidecar; hidden in Mac App Store builds |
@@ -57,7 +58,7 @@ What's truly interesting is **which engine reads each format behind the scenes**
| **GeoRSS** | `.xml` `.rss` `.atom` | Pure JS | Supports RSS 2.0 / Atom / RDF, GeoRSS Simple + GML geometries |
| **Geotagged photos** | `.jpg` `.jpeg` `.png` `.tif` `.tiff` `.webp` `.heic` `.heif` | exifr | **Reads EXIF GPS data to directly generate a point layer** — very practical for UAV/drone photos |
_Explicitly unsupported: `.xlsx` / `.xls` (zero hits in a full-repo search), raw `.osm` XML (PBF only). If you need Excel data, save as CSV first._
_Explicitly unsupported: raw `.osm` XML (PBF only)._
!!! tip "KML `<Model>` 3D Models"
The KML `<Model>` handling is worth noting: it loads embedded COLLADA `.dae` files with three.js, then exports them as GLB into the map. The project pulled in three.js just for this one edge case.
@@ -94,11 +95,11 @@ _Note the asymmetry: `.img`, `.vrt`, `.asc`, `.jp2`, `.hgt` are only recognized
| Item | Support | Notes |
|---|---|---|
| **LiDAR layer** | COPC / LAZ (via URL) | Rendered through `maplibre-gl-lidar` + deck.gl |
| **USGS 3DEP** | Online point cloud streaming | Standalone plugin; comes with a 3DEP elevation index WMS overlay |
| **LiDAR layer** | LAS / LAZ / COPC (URL or local file), EPT (`ept.json` URL) | Rendered through `maplibre-gl-lidar` + deck.gl |
| **USGS 3DEP** | Online point cloud streaming | Standalone plugin built on `maplibre-gl-usgs-lidar`; comes with a 3DEP elevation index WMS overlay |
| **Whitebox LiDAR tools** | `.las .laz .zlidar .copc .e57 .ply`, output `.laz` | The only place in the entire repo where `.e57` / `.ply` appear |
_The LiDAR layer panel's extension allowlist is **not in this repository** — it's defined in upstream npm packages. The only direct evidence in the repo is a single `.copc.laz` example URL. LAS/LAZ/COPC/EPT are very likely supported but cannot be confirmed 100% from the source._
_The LiDAR file picker itself lives in the upstream `maplibre-gl-lidar` package; GeoLibre's wrapper (`packages/plugins/src/plugins/components/lidar.ts`) documents LAS/LAZ/COPC files and EPT `ept.json` URLs, and a dropped local COPC streams by octree node without being copied._
---
@@ -111,7 +112,7 @@ The opening scenario — "received some 3D Tiles, just want to take a quick look
| **OGC 3D Tiles** | tileset URL | `maplibre-gl-3d-tiles` + deck.gl `Tile3DLayer` | **Supports custom request headers** — authenticated tilesets work |
| **Google Photorealistic 3D Tiles** | Built-in URL | Same as above | Requires Google Maps API key; passed via request headers, never stored to disk |
| **ArcGIS I3S Scene Layer** | `…/SceneServer` URL | deck.gl + loaders.gl `I3SLoader` | Both integrated mesh and 3D object layers are supported |
| **glTF / GLB** | **URL only** | deck.gl `ScenegraphLayer` | No local file picker — this is the most visible gap currently |
| **glTF / GLB** | URL or local file | deck.gl `ScenegraphLayer` | Added through the deck.gl visualization layer's scenegraph type |
| **COLLADA `.dae`** | Only via KML `<Model>` embedding | three.js → GLB | — |
| **Gaussian Splats** | URL | `maplibre-gl-splat` | Storage layer type is `gaussian-splat` |
@@ -119,7 +120,7 @@ The opening scenario — "received some 3D Tiles, just want to take a quick look
This screenshot is quite telling: **in the layer panel on the left, 3D Tiles, vectors, XYZ, glTF models, and Gaussian splats are all stacked in the same list** — and the rendering result is right there on the right. "Loading 3D Tiles requires setting up a server and writing a page" becomes **paste a URL** here.
_What's absent: `.obj` is completely unsupported (zero hits). `.b3dm`/`.pnts`/`.cmpt` — these 3D Tiles internal formats also have zero hits in the repo; they're transparently handled by upstream loaders, so you don't need to worry about them. The Gaussian splat extension list is likewise in upstream packages._
_What's absent: `.obj` cannot be imported (it appears only as an output of the extruded-layer 3D model export, alongside GLB and STL). `.b3dm`/`.pnts`/`.cmpt` — these 3D Tiles internal formats also have zero hits in the repo; they're transparently handled by upstream loaders, so you don't need to worry about them. The Gaussian splat extension list is likewise in upstream packages._
---
@@ -143,8 +144,7 @@ What you can fill in under "Add Data → Web Services":
| **WFS** | GetCapabilities fetches typeName; optional auto-refresh |
| **OGC API - Features** | Landing page / `/collections` / single collection / full `/items` URL are all recognized; auto-follows `next` links; defaults to 1,000 features |
| **OGC API - Tiles (Vector)** | TileJSON or MVT template; can optionally provide a Mapbox style URL to resolve `source-layer` names |
| **ArcGIS** | Only **two** in the dialog: FeatureServer (fetched as `f=geojson`) and VectorTileServer |
| **ArcGIS MapServer / ImageServer** | Not in the Add Data dialog; only accessible indirectly via plugins like NASA Earthdata GIS or EnviroAtlas |
| **ArcGIS** | Four layer types: FeatureServer (downloaded page by page as GeoJSON), VectorTileServer, MapServer, and ImageServer (the last two as raster layers); supports portal item IDs and ArcGIS sign-in |
| **MBTiles** | `.mbtiles` local file, custom protocol + Rust backend reads. **Desktop-only** |
| **PMTiles** | `.pmtiles`; vector or raster both work; auto-sniffs file header |
| **PostgreSQL / PostGIS** | Connect → select table → outputs MVT via built-in Martin service. **Desktop-only** |
@@ -227,7 +227,7 @@ Right-click a layer to export — **all done in the browser, no backend needed**
| Tool | Input | Output |
|---|---|---|
| Vector → Vector | `geojson geojsonl json parquet geoparquet fgb gpkg shp zip kml gml gpx` | Desktop: 14 drivers — GeoJSON, GeoJSONSeq, FlatGeobuf, GPKG, Shapefile, GML, KML, CSV, SQLite, GMT, DXF, MapInfo, JML, GPX |
| Vector → GeoParquet | Same as above | `.parquet`; compression: `zstd / snappy / gzip / lz4 / uncompressed` |
| Vector → GeoParquet | `parquet geoparquet geojson json shp gpkg fgb gml kml` | `.parquet`; compression: `zstd / snappy / gzip / lz4 / uncompressed` |
| Vector → FlatGeobuf | Same as above | `.fgb` |
| Vector → Shapefile | Same as above | `.zip` |
| Vector → GeoPackage | Same as above | `.gpkg` |
@@ -254,7 +254,7 @@ Mentioned piecemeal above, consolidated here. **This is where things most easily
| Limitation | Impact |
|---|---|
| **Desktop (Tauri) exclusive** | Native file/folder dialogs, local MBTiles, local raster reads, Shapefile companion file auto-discovery, PostGIS/Martin, file geodatabase, local file watch reload |
| **Requires Python sidecar** | File geodatabase, all desktop conversion tools (preferred path), raster tools (rasterio), AI segmentation, PostGIS, Sedona |
| **Requires Python sidecar** | File geodatabase, all desktop conversion tools (preferred path), raster tools without a browser engine (rasterio), AI segmentation, PostGIS, Sedona |
| **Mac App Store build** | No Python sidecar: hides PostgreSQL and GDB data sources, hides AI segmentation; Whitebox, conversion, raster, and vector tools all fall back to their browser/WASM engines; Shapefile companion files must be manually multi-selected |
| **Android / iOS (mobile)** | Hides raster tools, conversion tools, AI segmentation, PostgreSQL — all sidecar-backed. The Whitebox toolbox is WASM-backed and stays available |
| **Browser** | No local MBTiles/GDB/PostGIS; conversion output is subset; vector conversion doesn't accept `.zip`; raster-to-COG only accepts GeoTIFF; Zarr local folders unavailable in Firefox/Safari |
@@ -291,7 +291,7 @@ can replace layers owned by an earlier one.
A noteworthy detail: **Cesium's camera sync is not aligned by zoom level, but by ground resolution (meters/pixel)**, so split-screen panels at different heights maintain the same on-screen scale.
**Five compute engines**, all under the same UI: DuckDB-WASM Spatial (the workhorse, running in a dedicated Worker), PGlite + PostGIS, Apache Sedona (sidecar or browser WASM version), Pyodide (running GeoPandas/Shapely in the browser), Whitebox WASM (700+ tools).
**Five compute engines**, all under the same UI: DuckDB-WASM Spatial (the workhorse, running in a dedicated Worker), PGlite + PostGIS, Apache Sedona (sidecar or browser WASM version), Pyodide (running GeoPandas/Shapely in the browser), Whitebox WASM (1,000+ tools).
!["Processing" menu expanded: Whitebox, Conversion, Hydrology, LiDAR, Network, Projection, Raster, Remote Sensing, Terrain, Vector — with an alphabetical tool list on the right](https://assets.geolibre.app/images/processing-tools-menu.webp)
@@ -303,7 +303,7 @@ A noteworthy detail: **Cesium's camera sync is not aligned by zoom level, but by
A quick overview, all verifiable numbers from the source — not estimates.
**How large vectors are handled.** **Above 50,000 features, it stops using MapLibre's native GeoJSON source and instead tiles on-the-fly client-side** — geojson-vt generates tiles (point layers use Supercluster for aggregation), vt-pbf encodes to MVT, then feeds MapLibre through a custom protocol. Max zoom 16, 4096 extent.
**How large vectors are handled.** **Above 50,000 features, it stops using MapLibre's native GeoJSON source and instead tiles on-the-fly client-side** — on a Web Worker, geojson-vt generates tiles (point layers use Supercluster for aggregation), vt-pbf encodes to MVT, then feeds MapLibre through a custom protocol. Max zoom 16, 4096 extent.
Two details that show engineering care: tile index objects are **deliberately excluded from the store** (too large, non-serializable, can't be written into a project file); encoding checks an abort signal before proceeding, since MapLibre cancels tile requests that scroll off-screen.
@@ -340,13 +340,13 @@ Having covered the strengths, let's discuss the limitations.
**1. The feature scope is deliberately narrow.** It focuses on browser workflows, local processing, cloud-native formats, spatial SQL, modern visualization, and portability. Complex professional workflows still belong in QGIS.
**2. The iteration speed is a double-edged sword.** From 0 to 2.4.0 in just over two months; 2.0.0 to 2.1.0 was separated by roughly 19 hours. If you're considering it as a long-term production dependency, factor in this churn risk.
**2. The iteration speed is a double-edged sword.** When the original article was written, it had gone from 0 to 2.4.0 in just over two months; 2.0.0 to 2.1.0 was separated by roughly 19 hours. If you're considering it as a long-term production dependency, factor in this churn risk.
**3. Several clear format gaps.** glTF/GLB has no local file picker (URL only), `.obj` is completely unsupported, Excel is unsupported, HDF4 is unsupported, raw `.osm` XML is unsupported.
**3. Several clear format gaps.** `.obj` cannot be imported, HDF4 is unsupported, raw `.osm` XML is unsupported.
**4. Platform capability asymmetry.** See the table in Section 10. Don't extrapolate the browser version's experience to represent the whole.
**5. Cesium 3D Globe requires an ion token outside the web version.** The hosted web version bundles a demo token, but the desktop and mobile apps need your own. The free tier is sufficient for individual use; teams need to budget for quotas and pricing.
**5. Cesium terrain and imagery need an ion token outside the web version.** The hosted web version bundles a demo token, but the desktop and mobile apps need your own; without one the globe still works, just without Cesium World Terrain and Ion imagery. The free tier is sufficient for individual use; teams need to budget for quotas and pricing.
**6. The China-specific environment.** Default sources for basemaps, terrain, and Photorealistic 3D Tiles are all outside the firewall; coordinates use standard WGS84 — **GCJ-02 offsets must be handled separately**. These two issues need to be addressed first for serious use. Reliable first-hand data on this is unavailable; further input from actual users is welcome.
@@ -364,7 +364,7 @@ Usage paths by scenario:
1. **Just want to take a look** — Open `web.geolibre.app` directly; no installation, no registration
2. **Need to get real work done** — Download the desktop version from GitHub Releases, or via Microsoft Store / Homebrew / winget; a two-minute process
3. **Python users** — `pip install geolibre`; for GeoPandas support, `pip install "geolibre[all]"`; requires Python 3.10+
3. **Python users** — `pip install geolibre`; for GeoPandas support, `pip install "geolibre[all]"`; requires Python 3.11+
4. **Intranet / offline environments** — `VITE_PYODIDE_INDEX_URL` and `VITE_DUCKDB_SPATIAL_EXTENSION_PATH` can point Pyodide and the DuckDB spatial extension to internal mirrors, **no rebuild required**; an official Docker image is also available at `ghcr.io/opengeos/geolibre:latest`
5. **Secondary development** — npm workspaces monorepo, so use **npm** (the repo tracks `package-lock.json`) on **Node 22+**; main application in `apps/geolibre-desktop`; MIT licensed
+17 -17
View File
@@ -17,7 +17,7 @@
| 形态 | 怎么跑 | 说明 |
|---|---|---|
| 浏览器 | 打开 `web.geolibre.app` | 什么都不用装,加载完可离线用 |
| 桌面 | Tauri v2 原生应用 | Windows / macOS / Linux,微软商店、Homebrew、winget、AUR、Flatpak 都有 |
| 桌面 | Tauri v2 原生应用 | Windows / macOS / Linux,微软商店、Mac App Store、Homebrew、winget、AUR、Flatpak 都有 |
| 安卓 | Google Play 原生 App | 每 ABI 约 40MB |
| iOS | App Store 原生 App | iPhone 与 iPad,同一套代码经 Tauri v2 mobile 构建 |
| Jupyter | `pip install geolibre` | 整个应用嵌进 notebook 单元格 |
@@ -52,6 +52,7 @@ csv, tsv, kml, kmz, gml, gpx, dxf, tab, shp, zip
| **GML** | `.gml` | DuckDB `ST_Read` | — |
| **GPX** | `.gpx` | 纯 JS | **自动拆成三个图层**:航点 / 轨迹 / 路线 |
| **CSV / TSV** | `.csv` `.tsv` `.txt` `.dat` | 自研 + DuckDB 兜底 | 自动识别分隔符和经纬度列;WKT 几何列走 DuckDB;对话框可指定源坐标系 |
| **Excel** | `.xlsx` `.xls` | SheetJS(`@e965/xlsx`) | 从分隔文本面板打开:先选工作表,再选坐标列 |
| **CAD(DXF/DWG)** | `.dxf` `.dwg` | DuckDB `ST_Read` | 会读出图层清单让你挑;**CAD 不带坐标系,需要手动选 EPSG** |
| **MapInfo TAB** | `.tab` | `ST_Read` | — |
| **Esri 文件地理数据库** | `.gdb` **文件夹** | Python sidecar | 桌面端专属,且要 sidecar;Mac App Store 版本里是隐藏的 |
@@ -59,7 +60,7 @@ csv, tsv, kml, kmz, gml, gpx, dxf, tab, shp, zip
| **GeoRSS** | `.xml` `.rss` `.atom` | 纯 JS | RSS 2.0 / Atom / RDF 都吃,GeoRSS Simple + GML 几何 |
| **地理标记照片** | `.jpg` `.jpeg` `.png` `.tif` `.tiff` `.webp` `.heic` `.heif` | exifr | **读 EXIF GPS 直接生成点图层**,无人机照片很实用 |
_明确不支持的:`.xlsx` / `.xls`(经全库搜索,零命中)、原始 `.osm` XML(只认 PBF)。要用 Excel 的先另存为 CSV。_
_明确不支持的:原始 `.osm` XML(只认 PBF)。_
!!! tip "KML `<Model>` 的三维模型"
KML 的 `<Model>` 这条值得关注:它会把内嵌的 COLLADA `.dae` 用 three.js 加载再导出成 GLB 加载到地图中。为了这一个边缘场景引入了 three.js。
@@ -96,11 +97,11 @@ _注意这个不对等:`.img`、`.vrt`、`.asc`、`.jp2`、`.hgt` 这些只在
| 项 | 支持 | 说明 |
|---|---|---|
| **LiDAR 图层** | COPC / LAZ(URL 方式) | 走 `maplibre-gl-lidar` + deck.gl 渲染 |
| **USGS 3DEP** | 在线点云流式加载 | 独立插件,会附带 3DEP 高程索引 WMS 覆盖图 |
| **LiDAR 图层** | LAS / LAZ / COPC(URL 或本地文件)、EPT(`ept.json` URL) | 走 `maplibre-gl-lidar` + deck.gl 渲染 |
| **USGS 3DEP** | 在线点云流式加载 | 基于 `maplibre-gl-usgs-lidar` 的独立插件,会附带 3DEP 高程索引 WMS 覆盖图 |
| **Whitebox LiDAR 工具** | `.las .laz .zlidar .copc .e57 .ply`,输出 `.laz` | 这是全库唯一出现 `.e57` / `.ply` 的地方 |
_LiDAR 图层面板本身的扩展名白名单**不在这个仓库里**,定义在上游 npm 包中。仓库里唯一的直接证据是一个 `.copc.laz` 的示例 URL。LAS/LAZ/COPC/EPT 大概率都支持,但从源码无法百分百确认。_
_LiDAR 的文件选择器本身在上游 `maplibre-gl-lidar` 包里;GeoLibre 的封装(`packages/plugins/src/plugins/components/lidar.ts`)写明支持 LAS/LAZ/COPC 文件和 EPT 的 `ept.json` URL,拖进来的本地 COPC 会按八叉树节点流式加载,不整份复制。_
---
@@ -113,7 +114,7 @@ _LiDAR 图层面板本身的扩展名白名单**不在这个仓库里**,定义
| **OGC 3D Tiles** | tileset URL | `maplibre-gl-3d-tiles` + deck.gl `Tile3DLayer` | **支持自定义请求头**,带鉴权的切片也能加 |
| **Google 照片级 3D Tiles** | 内置 URL | 同上 | 需要 Google Maps API key,走请求头传,不落盘 |
| **ArcGIS I3S 场景图层** | `…/SceneServer` URL | deck.gl + loaders.gl `I3SLoader` | 整合网格和三维对象图层都支持 |
| **glTF / GLB** | **只能填 URL** | deck.gl `ScenegraphLayer` | 没有本地文件选择器,这是目前最明显的缺口 |
| **glTF / GLB** | URL 或本地文件 | deck.gl `ScenegraphLayer` | 通过 deck.gl 可视化图层的 scenegraph 类型添加 |
| **COLLADA `.dae`** | 只能通过 KML `<Model>` 内嵌 | three.js → GLB | — |
| **高斯泼溅** | URL | `maplibre-gl-splat` | 存储层类型是 `gaussian-splat` |
@@ -121,7 +122,7 @@ _LiDAR 图层面板本身的扩展名白名单**不在这个仓库里**,定义
上面这张图挺能说明问题:**左侧图层面板里 3D Tiles、矢量、XYZ、glTF 模型、高斯泼溅是叠在同一个列表里的**,右边直接就是渲染结果。「加载 3D Tiles 要起服务器写页面」这件事在这儿就是**粘贴一个 URL**。
_没找到的:`.obj` 完全不支持(零命中)。`.b3dm`/`.pnts`/`.cmpt` 这些 3D Tiles 内部格式在仓库里也搜不到——它们被上游加载器透明处理了,不用你操心。高斯泼溅的扩展名清单同样在上游包里。_
_没找到的:`.obj` 不能导入(它只作为拉伸图层三维模型导出的一种输出格式出现,和 GLB、STL 并列)。`.b3dm`/`.pnts`/`.cmpt` 这些 3D Tiles 内部格式在仓库里也搜不到——它们被上游加载器透明处理了,不用你操心。高斯泼溅的扩展名清单同样在上游包里。_
---
@@ -145,8 +146,7 @@ geoparquet, duckdb-query, deckgl-viz, video, image
| **WFS** | GetCapabilities 拉 typeName;可选自动刷新 |
| **OGC API - Features** | 落地页 / `/collections` / 单个集合 / 完整 `/items` URL 都认,自动翻 `next` 链接,默认取 1000 条 |
| **OGC API - Tiles(矢量)** | TileJSON 或 MVT 模板,可另填 Mapbox style URL 来解析 `source-layer` 名 |
| **ArcGIS** | 对话框里**只有两个**:FeatureServer(以 `f=geojson` 拉)和 VectorTileServer |
| **ArcGIS MapServer / ImageServer** | 不在添加数据对话框里,只能通过 NASA Earthdata GIS、EnviroAtlas 这类插件间接用 |
| **ArcGIS** | 四种图层类型:FeatureServer(按页下载成 GeoJSON)、VectorTileServer、MapServer 和 ImageServer(后两种作为栅格图层);支持门户项目 ID 和 ArcGIS 登录 |
| **MBTiles** | `.mbtiles` 本地文件,自定义协议 + Rust 后端读取。**桌面端专属** |
| **PMTiles** | `.pmtiles`,矢量栅格都行,自动嗅探文件头 |
| **PostgreSQL / PostGIS** | 连接 → 选表 → 出 MVT,靠内置的 Martin 服务。**桌面端专属** |
@@ -229,7 +229,7 @@ geoparquet, duckdb-query, deckgl-viz, video, image
| 工具 | 输入 | 输出 |
|---|---|---|
| 矢量 → 矢量 | `geojson geojsonl json parquet geoparquet fgb gpkg shp zip kml gml gpx` | 桌面端 14 种驱动:GeoJSON、GeoJSONSeq、FlatGeobuf、GPKG、Shapefile、GML、KML、CSV、SQLite、GMT、DXF、MapInfo、JML、GPX |
| 矢量 → GeoParquet | 同上 | `.parquet`,压缩可选 `zstd / snappy / gzip / lz4 / 不压缩` |
| 矢量 → GeoParquet | `parquet geoparquet geojson json shp gpkg fgb gml kml` | `.parquet`,压缩可选 `zstd / snappy / gzip / lz4 / 不压缩` |
| 矢量 → FlatGeobuf | 同上 | `.fgb` |
| 矢量 → Shapefile | 同上 | `.zip` |
| 矢量 → GeoPackage | 同上 | `.gpkg` |
@@ -256,7 +256,7 @@ _浏览器端输出格式是子集:geojson / json / csv / parquet / geoparquet
| 限制 | 影响什么 |
|---|---|
| **桌面端(Tauri)专属** | 原生文件/文件夹对话框、本地 MBTiles、本地栅格读取、Shapefile 同名文件自动发现、PostGIS/Martin、文件地理数据库、本地文件监听重载 |
| **需要 Python sidecar** | 文件地理数据库、桌面端的全部转换工具(首选路径)、栅格工具(rasterio)、AI 分割、PostGIS、Sedona |
| **需要 Python sidecar** | 文件地理数据库、桌面端的全部转换工具(首选路径)、没有浏览器引擎的栅格工具(rasterio)、AI 分割、PostGIS、Sedona |
| **Mac App Store 版本** | 不带 Python sidecar:隐藏 PostgreSQL 和 GDB 数据源、隐藏 AI 分割;Whitebox、转换、栅格、矢量工具全部退回浏览器/WASM 引擎;Shapefile companion 文件要手动多选 |
| **安卓 / iOS 移动端** | 隐藏栅格工具、转换工具、AI 分割、PostgreSQL——这些都依赖 sidecar。Whitebox 工具箱走 WASM,依然可用 |
| **浏览器端** | 无本地 MBTiles/GDB/PostGIS;转换输出是子集;矢量转换不收 `.zip`;栅格转 COG 只收 GeoTIFF;Zarr 本地文件夹在 Firefox/Safari 不可用 |
@@ -286,7 +286,7 @@ _浏览器端输出格式是子集:geojson / json / csv / parquet / geoparquet
一个值得注意的细节:**Cesium 的相机同步不是按 zoom 级别对齐的,而是按地面分辨率(米/像素)**,所以不同高度的分屏面板能保持同样的屏幕比例尺。
**计算引擎有五套**,都挂在同一套 UI 下:DuckDB-WASM Spatial(主力,跑在独立 Worker 里)、PGlite + PostGIS、Apache Sedona(sidecar 或浏览器 WASM 版)、Pyodide(浏览器里跑 GeoPandas/Shapely)、Whitebox WASM(700+ 工具)。
**计算引擎有五套**,都挂在同一套 UI 下:DuckDB-WASM Spatial(主力,跑在独立 Worker 里)、PGlite + PostGIS、Apache Sedona(sidecar 或浏览器 WASM 版)、Pyodide(浏览器里跑 GeoPandas/Shapely)、Whitebox WASM(1,000+ 工具)。
!["处理"菜单展开的样子:Whitebox、转换、水文、LiDAR、网络、投影、栅格、遥感、地形、矢量,右边是按字母排的工具清单](https://assets.geolibre.app/images/processing-tools-menu.webp)
@@ -298,7 +298,7 @@ _浏览器端输出格式是子集:geojson / json / csv / parquet / geoparquet
也简单说几个,都是源码里能查到的实数,不是估算的。
**大矢量的处理方式。** **超过 5 万要素,就不再走 MapLibre 原生 GeoJSON 源,而是在客户端现场切成矢量瓦片**——geojson-vt 生成瓦片(点图层用 Supercluster 聚合),vt-pbf 编码成 MVT,再通过一个自定义协议喂给 MapLibre。最大 16 级,4096 extent。
**大矢量的处理方式。** **超过 5 万要素,就不再走 MapLibre 原生 GeoJSON 源,而是在客户端现场切成矢量瓦片**——在 Web Worker 里由 geojson-vt 生成瓦片(点图层用 Supercluster 聚合),vt-pbf 编码成 MVT,再通过一个自定义协议喂给 MapLibre。最大 16 级,4096 extent。
两个细节能看出功力:瓦片索引对象**故意不放进 store**(太大、不可序列化,不能写进工程文件);编码前会检查中止信号,因为 MapLibre 会取消滚出屏幕的瓦片请求。
@@ -335,13 +335,13 @@ _浏览器端输出格式是子集:geojson / json / csv / parquet / geoparquet
**一、功能范围是故意收窄的。** 它聚焦在浏览器工作流、本地处理、云原生格式、空间 SQL、现代可视化和可移植性。复杂的专业流程该用 QGIS 还得用 QGIS。
**二、迭代速度是双刃剑。** 两个多月从 0 到 2.4.0,2.0.0 到 2.1.0 只隔了大概 19 小时。拿它做生产环境的长期依赖,得想清楚这个 churn 风险。
**二、迭代速度是双刃剑。** 原文写作时,它两个多月就从 0 到了 2.4.0,2.0.0 到 2.1.0 只隔了大概 19 小时。拿它做生产环境的长期依赖,得想清楚这个 churn 风险。
**三、几个明确的格式缺口。** glTF/GLB 没有本地文件选择器(只能填 URL)、`.obj` 完全不支持、Excel 不支持、HDF4 不支持、原始 `.osm` XML 不支持。
**三、几个明确的格式缺口。** `.obj` 不能导入、HDF4 不支持、原始 `.osm` XML 不支持。
**四、平台能力不对等。** 见第十节那张表。别拿浏览器版的体验去代表全部。
**五、Cesium 3D 球在网页版之外需要 Ion token。** 网页版内置了演示 token,桌面版和移动版需要自己的 token。免费额度够个人玩,团队用要算账。
**五、Cesium 的地形和影像在网页版之外需要 Ion token。** 网页版内置了演示 token,桌面版和移动版需要自己的 token;没有 token 三维球照样能用,只是没有 Cesium World Terrain 和 Ion 影像。免费额度够个人玩,团队用要算账。
**六、国内环境。** 底图、地形、Photorealistic 3D Tiles 这些默认源都在墙外;坐标系走标准 WGS84,**GCJ-02 偏移得自己处理**。想认真用得先解决这两件事。这部分暂无可靠的实测信息,留给实际使用者补充。
@@ -359,7 +359,7 @@ GeoLibre 真正的价值在于——不在于它比 QGIS 强(它不强),
1. **只想看看** —— 直接开 `web.geolibre.app`,不用装不用注册
2. **要真干活** —— GitHub Releases 下桌面版,或走微软商店 / Homebrew / winget,两分钟的事
3. **Python 用户** —— `pip install geolibre`,要 GeoPandas 支持就 `pip install "geolibre[all]"`,需要 Python 3.10+
3. **Python 用户** —— `pip install geolibre`,要 GeoPandas 支持就 `pip install "geolibre[all]"`,需要 Python 3.11+
4. **内网 / 离线环境** —— `VITE_PYODIDE_INDEX_URL` 和 `VITE_DUCKDB_SPATIAL_EXTENSION_PATH` 可以把 Pyodide 和 DuckDB 空间扩展指到内部镜像,**不用重新构建**;官方也有 Docker 镜像 `ghcr.io/opengeos/geolibre:latest`
5. **二次开发** —— npm workspaces 单体仓库,所以要用 **npm**(仓库跟踪 `package-lock.json`)配 **Node 22+**;主应用在 `apps/geolibre-desktop`,MIT 许可
+1 -1
View File
@@ -7,7 +7,7 @@ This tutorial derives terrain products from a digital elevation model (DEM): a h
## 1. Load a DEM
Add an elevation raster as a layer, for example a GeoTIFF or COG DEM (see [Adding Data](../user-guide/adding-data.md)). The raster tools take a file path in and write a file path out, so a local or accessible raster works best.
Add an elevation raster as a layer, for example a GeoTIFF or COG DEM (see [Adding Data](../user-guide/adding-data.md)). The raster tools read a GeoTIFF file, so a local or accessible raster works best. Tools with a browser engine (Hillshade, Slope, Aspect, Clip by extent) can also use **Use a layer on the map** to pick a raster layer you have already added.
## 2. Hillshade
+6 -4
View File
@@ -43,8 +43,9 @@ Open **Settings → Interface** to:
- Pick an **experience level**, which fills the checklists from each item's
complexity and takes effect at once.
- Check or uncheck individual **data sources**, **plugins**, whole **menus**
(Project, Edit, Add Data, Processing, Controls, Plugins, Help), and the items
within the Project, Edit, Processing, Controls, Settings, and Help menus.
(Project, Edit, View, Add Data, Processing, Controls, Plugins, Help), and the
items within the Project, Edit, View, Processing, Controls, Settings, and Help
menus.
Editing any item switches the selector to **Custom**.
The **Settings** menu itself, and its Language / Layout / Interface entries, are
@@ -105,13 +106,14 @@ these profile fields; the legacy file stores them at top level.
| `lock` | boolean | When `true`, users cannot change the profile from Settings. To release the lock on the next launch, remove or clear `interface.lock` in the selected deployment policy, or the top-level `lock` in the legacy profile. |
| `hiddenDataSources` | string[] | Explicit data-source ids to hide. Overrides the preset when present. |
| `hiddenPlugins` | string[] | Explicit plugin ids to hide. Overrides the preset when present. |
| `hiddenMenus` | string[] | Top-level menu ids to hide (`project`, `edit`, `addData`, `processing`, `controls`, `plugins`, `help`). |
| `hiddenMenus` | string[] | Top-level menu ids to hide (`project`, `edit`, `view`, `addData`, `processing`, `controls`, `plugins`, `help`). |
| `hiddenMenuItems` | string[] | Menu-item ids to hide (e.g. `processing.raster`, `help.diagnostics`, `controls.minimap`). |
Data-source ids are the catalog ids in
`apps/geolibre-desktop/src/lib/ui-profile.ts` (e.g. `vector`, `xyz`, `mbtiles`,
`postgres`). Plugin ids are the stable ids defined in
`packages/plugins/src/plugins/*` (e.g. `maplibre-gl-geoagent`). Menu and
`packages/plugins/src/plugins/*` and `packages/plugins/src/plugin-ids.ts`
(e.g. `maplibre-gl-geoagent`). Menu and
menu-item ids are the catalog ids in the same `ui-profile.ts`
(`TOP_LEVEL_MENUS`, `MENU_ITEM_CATALOG`).
+24 -11
View File
@@ -14,9 +14,9 @@ To collect supported dataset links from a catalog or other webpage and open seve
| --- | --- |
| **Vector Layer** | Opens the Add Vector panel (backed by `maplibre-gl-vector`). Loads GeoJSON, GeoParquet, FlatGeobuf, zipped Shapefile, GeoPackage, KML/KMZ, GML, and other vector formats from a file or URL. |
| **Raster Layer** | Opens the Add Raster panel (backed by `maplibre-gl-raster`). Loads GeoTIFF and Cloud-Optimized GeoTIFF (COG) from a file or URL. |
| **Delimited Text Layer** | Loads CSV/TSV from a file or URL, using longitude and latitude columns to build point features, or by geocoding one or more address columns (see [Geocoding](data-integrations.md#geocoding)). |
| **Delimited Text Layer** | Loads CSV/TSV from a file or URL, using longitude and latitude columns to build point features, or by geocoding one or more address columns (see [Geocoding](data-integrations.md#geocoding)). A file with no coordinates can be added as a non-spatial attribute table. |
| **CAD (DXF/DWG) Layer** | Loads AutoCAD drawings, converting their entities to vector features. Coordinate Z values (contours, 3D polylines, surveyed points) are kept and rendered in 3D unless **Render Z values in 3D** is unchecked. |
| **File Geodatabase (GDB)** | Opens an Esri file geodatabase and adds one of its feature classes as a layer. |
| **File Geodatabase (GDB)** | Opens an Esri file geodatabase (`.gdb` folder) and adds one of its feature classes as a layer. It is read by the local GeoLibre server, so it needs the desktop app. |
| **Geotagged Photos** | Reads the EXIF GPS tags from a set of photos and places each one on the map as a point with a thumbnail. |
| **GPX Layer** | Loads a GPX file or URL and splits it into separate waypoint, track, and route layers. |
| **LandXML Layer** | Loads a LandXML file or URL and imports selected TIN surfaces, horizontal alignments, vertical profile metadata, and survey points. Projected coordinates are reprojected from the selected or embedded source CRS. |
@@ -64,6 +64,7 @@ GeoParquet opens across its variants: 1.0 and 1.1 files (including one carrying
| **XYZ Layer** | A raster or vector tile service using a `{z}/{x}/{y}` URL template. |
| **[WCS Layer](../data-formats.md#wcs-raster-subsets)** | Downloads numerical GeoTIFF subsets from WCS 1.0.0 services. |
| **WMS Layer** | A Web Map Service layer, with click-to-identify through GetFeatureInfo where supported. In the desktop app, once the layers are retrieved, a layer can be requested in any coordinate reference system it offers; the tiles are reprojected to Web Mercator. |
| **CSW Catalog** | Searches an OGC Catalogue Service (CSW) for datasets and adds their supported linked resources (GeoJSON, WMS, WFS, ArcGIS) to the map. |
| **WFS Layer** | A Web Feature Service layer, with optional automatic refresh. GeoLibre asks for GeoJSON first and falls back to GML when the server offers no GeoJSON output, so GML-only services (MapServer, most INSPIRE services) load too. GML in WGS84, ETRS89, NAD83 or Web Mercator is read directly, and GML in any other EPSG coordinate system (a national grid, a UTM zone) is reprojected; a system GeoLibre does not know is reported as an error rather than drawn in the wrong place. In the desktop app, WFS requests go through the native HTTP client, so services that send no CORS headers load too. |
| **WMTS Layer** | A Web Map Tile Service layer. |
| **OGC API - Features** | An OGC API - Features endpoint; pick a collection and add it as a vector layer. |
@@ -78,7 +79,9 @@ GeoParquet opens across its variants: 1.0 and 1.1 files (including one carrying
### ArcGIS services
Pick the **Layer type** that matches the service, then give it a service URL or a
portal item ID (with an access token for a secured service).
portal item ID. For a secured service, set **Authentication** to **Sign in with
ArcGIS** or paste an access token; see
[Signing in with ArcGIS](../arcgis-editing.md#signing-in-with-arcgis).
| Layer type | Service | How it loads |
| --- | --- | --- |
@@ -122,6 +125,9 @@ were rendered before the choice existed and cannot honor it.
| **LiDAR Layer** | Point-cloud visualization, rendered with deck.gl. |
| **Gaussian Splatting** | Gaussian splat scenes. |
| **3D Tiles Layer** | OGC 3D Tiles, restored when reopening a project. Includes a Google Photorealistic 3D Tiles sample that reads `VITE_GOOGLE_MAPS_API_KEY` or `GOOGLE_MAPS_API_KEY` from the runtime environment. |
| **Cesium Ion Asset** | Adds a 3D Tiles tileset or imagery from Cesium ion by asset ID, using your Cesium ion token. Cesium engine only; the entry is disabled on the 2D engines. |
| **CZML Dynamic 3D Scene** | Loads a CZML document of orbits, trajectories, vehicle paths, and other time-varying 3D scenes. Cesium engine only. |
| **KML / KMZ** | Loads KML or KMZ with native globe styling, overlays, and network links on the Cesium globe, and through the regular KML importer on the 2D engines. |
| **3D Model (glTF)** | Places a glTF/GLB model at a coordinate, with scale, rotation, and altitude controls. |
## Databases
@@ -135,7 +141,12 @@ were rendered before the choice existed and cannot honor it.
## Drag and drop
Drag a vector file (GeoJSON, zipped Shapefile, KMZ, and similar) or a GeoTIFF/COG raster directly onto the map to add it as a layer. GPX files dropped on the map are split into named waypoint, track, and route layers.
Drag a vector file (GeoJSON, zipped Shapefile, KMZ, and similar) or a GeoTIFF/COG raster directly onto the map to add it as a layer. GPX files dropped on the map are split into named waypoint, track, and route layers. Other files are routed by type:
- **OSM PBF** (`.osm.pbf`) extracts are parsed into point, line, and polygon layers.
- **LAS, LAZ, and COPC** point clouds load into the LiDAR control; a LAS 1.4 file the viewer cannot read whole is converted to COPC first.
- **Geotagged photos** (JPEG, PNG, WebP, HEIC) become a point layer placed from their EXIF GPS tags.
- A single **`.geolibre.json`** project file opens as the current project.
## The Browser panel
@@ -145,10 +156,12 @@ The **Browser** tab on the left edge of the window opens a QGIS-style Data Sourc
| Section | What it holds |
| --- | --- |
| **Favorites** | Services, folders, and files you starred, listed first for quick access. Appears once you have favorited something. |
| **My Data** | Your personal layer library. **Layer actions → Save to My Data** stores a fully configured layer — source, style, labels, filters, joins, virtual fields, and attribute form — and one click here re-adds it to any later project. Import and export the library with the buttons on the section header. |
| **Services** | Saved map services, grouped by kind (XYZ, WMS, WFS, WMTS, ArcGIS). GeoLibre ships a starter set; the **+** on a group adds a new connection of that kind. Expand a service to browse its layers and add one. Self-hosted deployments can add read-only organization-wide services here, marked with a *config* badge (see [Getting Started](../getting-started.md#deployment-service-library)); they are shared with every user, cannot be edited or deleted, and are never stored in your own service library. |
| **Recent** | The sources you added most recently, so a repeat is one click. |
| **Services** | Saved map services, grouped by kind (WMS, WFS, WMTS, XYZ, ArcGIS, CSW). GeoLibre ships a starter set; the **+** on a group adds a new connection of that kind. Expand a service to browse its layers and add one. Self-hosted deployments can add read-only organization-wide services here, marked with a *config* badge (see [Getting Started](../getting-started.md#deployment-service-library)); they are shared with every user, cannot be edited or deleted, and are never stored in your own service library. |
| **Recent** | The projects you opened most recently, so reopening one is one click. |
| **Databases** | PostgreSQL/PostGIS and SQL Server connections, each in its own engine group with a **+** for a new connection. Expand a connection to browse its schemas and spatial tables; the Add Data form retains each engine's table options. The trash icon forgets a saved connection and removes its saved credential; on desktop, if the system keychain cannot delete the credential right away, GeoLibre shows a persistent warning with the password-masked connection label and retries the next time it starts. Failures for different connections are reported separately. |
| **Files** | Desktop app only. Folders you pinned with **Add folder** (the **+** on the section), each expandable to its subfolders and the loadable files inside, so you can add a file without a file dialog. |
Choose the **+** on the PostgreSQL or SQL Server group to open that engine's connection form; the Databases section has no shared connection action.
@@ -160,13 +173,13 @@ If desktop database browsing reports a processing server from a previous session
## Basemaps
The basemap sits at the bottom of the [Layers panel](layers.md) as the **Background** entry. **Double-click that row** to open the **Change basemap** dialog, or activate the **Basemaps** plugin from the [Plugins menu](plugins.md) to switch between OpenFreeMap styles (Liberty, Liberty 3D, Positron, Bright, Dark, Fiord), a collapsible **Regional** group, a blank background, or a custom style URL. You can toggle basemap visibility and adjust its opacity from the Layers panel.
The basemap sits at the bottom of the [Layers panel](layers.md) as the **Background** entry. **Double-click that row** to open the **Change background** dialog, or activate the **Basemaps** plugin from the [Plugins menu](plugins.md) to switch between OpenFreeMap styles (Liberty, Liberty 3D, Positron, Bright, Dark, Fiord), Protomaps styles (when the build offers them), a collapsible **Regional** group, a blank background, or a custom style URL. You can toggle basemap visibility and adjust its opacity from the Layers panel.
![The Change basemap dialog, with OpenFreeMap styles at the top and Moon, Mars, and other celestial-body sections below](https://assets.geolibre.app/images/geolibre-change-basemap.webp)
![The Change background dialog, with OpenFreeMap styles at the top and Moon, Mars, and other celestial-body sections below](https://assets.geolibre.app/images/geolibre-change-basemap.webp)
### Other celestial bodies
GeoLibre can map worlds beyond Earth. The **Change basemap** dialog and the **New project** dialog group planetary basemaps into sections for **The Moon**, **Mars**, and a collapsible **Other celestial bodies** section covering **Mercury, Venus, the Galilean moons** (Io, Europa, Ganymede, Callisto), **Titan, Pluto,** and **Charon**. The Moon and Mars mosaics come from [OpenPlanetaryMap](https://www.openplanetary.org/opm); the other bodies come from [USGS Astrogeology](https://astrogeology.usgs.gov/) and are reprojected to Web Mercator on the fly so MapLibre can render them.
GeoLibre can map worlds beyond Earth. The **Change background** dialog and the **New project** dialog group planetary basemaps into sections for **The Moon**, **Mars**, and a collapsible **Other celestial bodies** section covering **Mercury, Venus, the Galilean moons** (Io, Europa, Ganymede, Callisto), **Titan, Pluto,** and **Charon**. The Moon and Mars mosaics come from [OpenPlanetaryMap](https://www.openplanetary.org/opm); the other bodies come from [USGS Astrogeology](https://astrogeology.usgs.gov/) and are reprojected to Web Mercator on the fly so MapLibre can render them.
For quick switching, use the **planet switcher** (the orbit icon) in the Layers panel header. Selecting a body sets the project's **ellipsoid**, so distance, area, and scale-bar measurements use that body's radius instead of Earth's.
@@ -182,7 +195,7 @@ Three of them, grouped under **Plugins → Web Services**, search public dataset
| **Socrata** | Public Socrata open-data catalogs, adding their GeoJSON datasets. |
| **CKAN** | The Humanitarian Data Exchange CKAN catalog, adding its available GeoJSON resources. |
Each panel shows how many of the total results you are looking at and offers **Load more** to page further. See [Web Services](web-services.md) for the other fourteen browsers in that submenu.
Each panel shows how many of the total results you are looking at and offers **Load more** to page further. See [Web Services](web-services.md) for the other browsers in that submenu.
!!! note "Browser vs desktop"
URL-based sources work in both the browser and the desktop app. Local file dialogs, local MBTiles, local raster reads, and PostgreSQL require the desktop app. See [Getting Started](../getting-started.md).
URL-based sources work in both the browser and the desktop app. Local file dialogs, local MBTiles, local raster reads, File Geodatabases, PostgreSQL, and SQL Server require the desktop app. See [Getting Started](../getting-started.md).
+1 -1
View File
@@ -28,7 +28,7 @@ providers in **Settings → AI Providers**:
| --- | --- | --- |
| Google Gemini | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | `gemini-3.6-flash` |
| Anthropic | `ANTHROPIC_API_KEY` | `claude-opus-5` |
| OpenAI | `OPENAI_API_KEY` | Select from the current GPT models |
| OpenAI | `OPENAI_API_KEY` | `gpt-5.6` |
| **OpenRouter** | `OPENROUTER_API_KEY` (+ optional `OPENROUTER_MODEL`) | `openai/gpt-5.6-luna` |
| **Ollama** (local) | `OLLAMA_BASE_URL` (e.g. `http://localhost:11434`) | `gemma4` |
| **Amazon Bedrock** | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (+ `AWS_REGION`, optional `AWS_SESSION_TOKEN`) | `global.anthropic.claude-opus-5` |
+4 -3
View File
@@ -26,6 +26,7 @@ memory.
| **Statistics** | Summary statistics for one field. See [Field statistics](#field-statistics). |
| **Charts** | Chart one or two fields. See [Charts](#charts). |
| **Dashboard** | Open the [Dashboard](processing.md#dashboard) with this layer preselected. |
| **Refresh** | Re-read the features from the source to pick up changes made elsewhere, such as edits by other clients of a service. Shown for layers that can be [refreshed](layers.md#refreshing-live-layers); on desktop, a layer read from a local file is reloaded from disk. Disabled while an ArcGIS layer has unsaved edits. |
| **Export** | Write the layer to a file. See [Exporting](#exporting). |
| **Search attributes...** | Show only rows whose values (or id) contain the text, ignoring case. |
| **Zoom to selection** | Zoom the map to each new selection. |
@@ -275,9 +276,9 @@ that holds an object must contain valid JSON before you can save.
Layer layers.
- `Ctrl`/`Cmd` + `Z` undoes saved layer changes (not while you are typing in a
cell).
- To write edits back to the file or service the layer came from, use the
layer's **Save edits to source file** (or the ArcGIS and PostGIS
equivalents).
- To write edits back to the file, service, or database the layer came from,
use the layer's **Save edits to source file** (or the ArcGIS, PostGIS, and
SQL Server equivalents).
Combine this with the **GeoEditor** plugin to edit geometry and attributes
together. See [Managing Layers](layers.md).
+2 -2
View File
@@ -33,11 +33,11 @@ A manually installed copy does not update itself. To update it, download and ext
1. Visit a webpage or data catalog containing geospatial file links.
2. Select the GeoLibre icon in the Chrome toolbar.
3. Use **All**, **Vector**, or **Raster** to filter the discovered datasets.
3. Use **All**, **Vector**, **Raster**, or **LiDAR** to filter the discovered datasets.
4. Check one or more datasets. Nothing is selected by default.
5. Select **Open in GeoLibre** to load the selected datasets on the same map.
The extension recognizes GeoJSON and spatial JSON, GeoParquet and Parquet, PMTiles, GeoTIFF and Cloud-Optimized GeoTIFF, ZIP archives containing GeoJSON, JSON-LD download metadata, and existing GeoLibre data links. On Source Cooperative repository pages, it also reads the embedded inventory so datasets outside the currently visible list can be selected.
The extension recognizes GeoJSON and spatial JSON, GeoParquet and Parquet, PMTiles, GeoTIFF and Cloud-Optimized GeoTIFF, LiDAR point clouds (LAS, LAZ, COPC, and EPT `ept.json`), ZIP archives containing GeoJSON, JSON-LD download metadata, and existing GeoLibre data links. A neighboring `name.style.json` or `name.geolibre.style.json` link is paired with the matching dataset. It also offers the map services the page's own maps requested: WMS, WMTS, WFS, OGC API - Features, ArcGIS feature services, XYZ/TMS image tiles, and vector tiles. On Source Cooperative repository pages, it also reads the embedded inventory so datasets outside the currently visible list can be selected.
## Use a different GeoLibre instance
+3
View File
@@ -77,10 +77,13 @@ Temporary credentials are refreshed shortly before they expire.
- Type `s3://bucket/prefix/` (or a bucket name, or an S3 HTTPS URL) and press **Go**.
- **List buckets** lists every bucket the selected connection's credentials can see. On the web this needs the S3 service endpoint to allow the page's origin, which AWS does not, so use the desktop app or type the bucket name.
- **Add** puts a COG/GeoTIFF, GeoParquet, GeoJSON, FlatGeobuf, GeoPackage, CSV, PMTiles, or COPC/LAZ/LAS point cloud file on the map through the same code paths as Add Data. Once a file is on the map its button reads **Added**; remove the layer and it turns back into **Add**. **Copy URI** copies the `s3://` URI for use elsewhere, such as the SQL Workspace.
- A `.geolibre` or `.geolibre.json` file shows **Open project** instead, which opens that project from the bucket.
- To add several files at once, tick their checkboxes (or **Select all**) and choose **Add selected**. They are added one after another, with progress shown under the buttons.
- **Up** goes to the parent folder; **Set as default** makes the current folder the one the browser opens at.
- The line under the location box shows whether the bucket is read with a connection's credentials or anonymously.
To have the S3 Browser open every time GeoLibre starts, turn on **Open the S3 Browser at startup** under [Settings → Startup Settings](settings.md#startup).
## Where credentials are stored
Connections are device-local and never saved in a project file.
+5 -4
View File
@@ -42,7 +42,7 @@ single sign-on layer.
## Web Services
The **Web Services** submenu of the [Plugins menu](plugins.md) bundles seventeen catalog and service browsers, from the United States federal sources below to general-purpose STAC, ArcGIS Hub, Source Cooperative, and Hugging Face browsers. All seventeen are documented on the **[Web Services](web-services.md)** page.
The **Web Services** submenu of the [Plugins menu](plugins.md) bundles more than thirty catalog and service browsers, from the United States federal sources below to general-purpose STAC, ArcGIS Hub, Source Cooperative, and Hugging Face browsers. All of them are documented on the **[Web Services](web-services.md)** page.
| Service | Data |
| --- | --- |
@@ -59,7 +59,8 @@ The **Web Services** submenu of the [Plugins menu](plugins.md) bundles seventeen
| **Historical Imagery** | Plugins menu | Browse historical Esri World Imagery snapshots. |
| **Vantor Open Data** | Plugins → Web Services | Search disaster-event satellite imagery, filter pre/post-event scenes by map extent, visualize Cloud-Optimized GeoTIFFs, and download selected scenes. |
| **Planet Open Data** | Plugins → Web Services | Browse Planet Labs PBC disaster data releases through the STAC Catalogs interface, with the public disaster catalog selected by default. |
| **Street View** | Plugins menu | View Google Street View and Mapillary street-level imagery. Needs provider credentials (see [Getting Started](../getting-started.md#optional-imagery-credentials)). |
| **Street View** | Plugins menu | View Google Street View or Mapillary street-level imagery at a clicked point. Needs provider credentials (see [Getting Started](../getting-started.md#optional-imagery-credentials)). |
| **Mapillary** | Plugins menu | Browse Mapillary street-level imagery in its own viewer. Needs a Mapillary access token (see [Getting Started](../getting-started.md#optional-imagery-credentials)). |
## Time series and comparison
@@ -74,7 +75,7 @@ The **Web Services** submenu of the [Plugins menu](plugins.md) bundles seventeen
| --- | --- |
| **GeoAgent** | AI-assisted geospatial analysis. |
The plugins under **Imagery and street-level**, **Time series and comparison**, and **AI analysis** are activated from the [Plugins menu](plugins.md), where you can also set their on-map position (the integrations further up this page are reached from the Processing and Add Data menus instead). The Web Services panels are the exception, including **Vantor Open Data** and **Planet Open Data** above: they dock beside the Layers panel instead of floating over the map, so they offer no position choice.
The plugins under **Imagery and street-level**, **Time series and comparison**, and **AI analysis** are activated from the [Plugins menu](plugins.md) (the integrations further up this page are reached from the Processing and Add Data menus instead). Most of them, like the Web Services panels, open docked beside the Layers panel; only **Street View** and **Mapillary** place a control on the map, so only they offer a position choice in their Plugins submenu.
## Geocoding
@@ -82,7 +83,7 @@ GeoLibre can turn addresses into points and points into addresses. Both run thro
| Tool | Where | What it does |
| --- | --- | --- |
| **Geocode Addresses** | Processing menu | Pick a CSV with an address column and geocode each row into a point layer. Each matched row keeps its original columns plus `geocode_lat`, `geocode_lon`, `geocode_display_name`, and `geocode_importance` (a match score). A per-run provider picker lets you switch backend for that batch. |
| **Geocode Addresses** | Processing → GeoLibre Toolbox | Pick a CSV with an address column and geocode each row into a point layer. Each matched row keeps its original columns plus `geocode_lat`, `geocode_lon`, `geocode_display_name`, and `geocode_importance` (a match score). A per-run provider picker lets you switch backend for that batch. |
| **Delimited Text Layer → Addresses** | Add Data | Geocode a CSV/TSV at import time instead of a separate step: choose "Addresses" as the import mode, pick one or more columns to concatenate into the address (e.g. street, city, state), and each row is geocoded through the project's configured provider. Matched rows become points; rows with no match are kept (not dropped) with `geocode_status: "unmatched"` so they stay visible and fixable in the attribute table. |
| **Reverse Geocode** | Controls menu | A toggle. While on, click anywhere on the map to look up the address at that point, shown in a popup with a copy button. The popup follows the app's light or dark theme. |
+14 -6
View File
@@ -26,6 +26,7 @@ A chrome-free `maponly` embed shows only the map, as in this shared 3D Tiles pro
| `loading` | `loading=true` | Exposes screenshot readiness on the document element. Accepts a bare flag, `true`, `1`, `yes`, or `on`; disabled by default. See below. |
| `data` | `data=https://assets.geolibre.app/data/places.geojson` | Loads public GeoJSON, GeoParquet, PMTiles, a COG, or a ZIP/REST response containing multiple GeoJSON files. |
| `style` | `style=https://assets.geolibre.app/data/sample.style.json` | Applies a GeoLibre/MapLibre vector style or raster-style JSON to the data loaded by `data`. |
| `dataType` | `dataType=lidar` | Marks a `data` URL whose path has no file extension as a LiDAR point cloud; pairs with `data` by position. See [Open remote data](#open-remote-data). |
| `stac` | `stac=https://earth-search.aws.element84.com/v1/collections/naip` | Opens the STAC Catalogs browser connected to a STAC catalog, API, or API collection. A collection URL is searched on load, so its item footprints appear as a layer. See [Open a STAC catalog](#open-a-stac-catalog). |
| `layout` | `layout=viewer` | `viewer` provides read-only chrome: Layers, View, Controls, basemaps, search/identify, Help, and any quick filters the project's layers carry, with authoring UI hidden. `compact` is the icon-only full-app layout; `embed` and `iframe` are aliases. |
| `toolbar` | `toolbar=none` | Hides the top toolbar while keeping panels and the status bar. Use `icons` for icon-only buttons; `icon` and `icon-only` are aliases. `hidden`, `hide`, and `off` are aliases for `none`. |
@@ -208,8 +209,8 @@ Read-only covers the keyboard too: the
project shortcuts (Ctrl/Cmd+N, +O, +S) and the command palette (Ctrl/Cmd+K) go
with the menus they belong to, while the View shortcuts (`[`, `]`, `n`, `u`,
`r`) keep working since the View menu stays. Dropping a file onto the map
imports nothing, and the plugins whose on-map control writes to the project —
the geometry editor, Annotations, and GeoAgent — cannot be active, even if the
imports nothing, and the plugins that write to the project — the geometry
editor, Annotations, and GeoAgent — cannot be active, even if the
loaded project saved them that way.
So an embed cannot be steered into authoring by a key press, a drag, or a
project file. Display plugins (layer control, basemaps, time slider, legend and
@@ -407,7 +408,7 @@ For Docker, `GEOLIBRE_EMBED_ORIGINS` overrides the policy at container startup:
```bash
docker run --rm -p 8080:80 \
-e GEOLIBRE_EMBED_ORIGINS="https://portal.example.com,https://erp.example.com" \
geolibre-policy:local
ghcr.io/opengeos/geolibre:latest
```
For a static build, the legacy `VITE_GEOLIBRE_EMBED_ORIGINS` build setting is
@@ -476,6 +477,8 @@ other frame or origin is ignored. Pass the *app's* origin, not your own.
| `setLayerVisibility(layerId, visible)` | `void` | Shows or hides a project layer. |
| `listLayers()` | `LayerSummary[]` | `{ id, name, type, visible, opacity }` per layer. |
| `setFilter(layerId, expression)` | `void` | A MapLibre filter expression, or `null` to clear it. |
| `setRenderer(renderer)` | `void` | See [Switching renderers](#switching-renderers). |
| `getRenderer()` | the active renderer | `"maplibre"`, `"mapbox"`, `"cesium"`, or `"arcgis"`. |
| `getViewport()` | `Viewport` | `{ bbox, center, zoom, bearing, pitch }`. |
| `addLayer(spec)` | the new layer's `id` | Takes a project-format layer specification. |
| `addData(url, options?)` | the new layer `id`s | Loads remote data like `?data=`; options are `{ styleUrl, fit }`. |
@@ -524,6 +527,8 @@ them out of the other `postMessage` traffic on your page.
| `listLayers` | `{}` | Returns layer summaries in the acknowledgement's `result`. |
| `setFilter` | `{ layerId, expression }` | Applies a MapLibre filter expression; send `null` to clear it. |
| `getViewport` | `{}` | Returns the current camera and bounds in `result`. |
| `setRenderer` | `{ renderer }` | Switches the map renderer. See [Switching renderers](#switching-renderers). |
| `getRenderer` | `{}` | Returns the active renderer in `result`. |
| `addLayer` | `{ spec }` | Adds a project-format layer specification at runtime. |
| `addData` | `{ url, styleUrl?, fit? }` | Loads GeoJSON/API, ZIP, GeoParquet, PMTiles, or COG data without reloading the iframe. |
| `exportImage` | `{}` | Returns the rendered map as a PNG data URL in `result`. |
@@ -560,6 +565,7 @@ reporting whether it worked.
| `ack` | `{ requestId, ok, error, result }` | A message you sent with a `requestId` was applied (or rejected). |
| `projectLoaded` | `{ url, name, layerIds }` | A project finished loading, whoever started it. |
| `selectionChanged` | `{ layerId, featureIds }` | The user (or your `highlightFeature`) changed the selection. |
| `rendererchange` | `{ renderer }` | The map renderer changed. |
| `viewChanged` | `{ bbox, center, zoom, bearing, pitch }` | The camera moved (throttled to about four events a second). |
| `toolCompleted` | `{ id, name, status, engine, durationMs, outputLayerNames }` | A processing run finished, successfully or not. |
| `serverFileWritten` | `{ path, toolId }` | A file-based tool wrote an output (conversion and raster tools). |
@@ -623,10 +629,12 @@ See the [Sharing & Embedding tutorial](../tutorials/sharing-embedding.md) for a
## Switching renderers
With the [typed client](#the-typed-client):
```javascript
client.on("rendererchange", ({ renderer }) => console.log(renderer));
await client.setRenderer("cesium");
const renderer = await client.getRenderer();
map.on("rendererchange", ({ renderer }) => console.log(renderer));
await map.setRenderer("cesium");
const renderer = await map.getRenderer();
```
Both methods accept or return `"maplibre"`, `"mapbox"`, `"cesium"`, or
+7 -5
View File
@@ -17,8 +17,8 @@ The toolbar across the top of the window groups every action into nine menus:
| **Processing** | Run vector, raster, conversion, Whitebox, and SQL tools, plus the [AI Assistant](ai-assistant.md). The menu holds [two separate toolboxes](processing.md#two-toolboxes-in-one-menu), so some category names appear twice. See [Processing Tools](processing.md) and [SQL Workspace](sql-workspace.md). |
| **Controls** | Toggle map controls and component panels (Measure, Bookmark, Minimap, and more). See [Map Controls & Tools](map-controls.md). |
| **Plugins** | Activate built-in plugins and set their on-map position. See [Plugins & Marketplace](plugins.md). |
| **Settings** | Map preferences, layout, environment variables, project settings, and Manage Plugins. See [Settings & Preferences](settings.md). |
| **Help** | The command palette, keyboard shortcuts, **Simplify Interface...**, diagnostics, feedback, update checks, and the About dialog. |
| **Settings** | Language, map preferences, layout, appearance, interface profile, geocoding, AI providers, environment variables, cloud storage, updates, startup project, Manage Plugins, and the Style Manager. See [Settings & Preferences](settings.md). |
| **Help** | The command palette, keyboard shortcuts, **Simplify Interface...**, links to the website and GitHub repository, diagnostics, feedback, update checks, and the About dialog. |
On the right side of the toolbar are the light/dark theme toggle and the editable project name.
@@ -57,6 +57,8 @@ The built-in global shortcuts are:
| `Ctrl`/`Cmd` + `O` | Open project from file |
| `Ctrl`/`Cmd` + `S` | Save project |
| `Ctrl`/`Cmd` + `Shift` + `S` | Save project as… |
| `Ctrl`/`Cmd` + `Z` | Undo |
| `Ctrl`/`Cmd` + `Shift` + `Z` (or `Ctrl` + `Y`) | Redo |
| `C` | Toggle the [review comment](map-controls.md#review-comments) tool, then click the map to place the pin |
| `N` | Reset bearing (north up) |
| `U` | Reset pitch (top-down view) |
@@ -91,7 +93,7 @@ Four dockable panels surround the map, plus the attribute table along the bottom
| **Comments** | Right | Anchored review notes and their threads. See [Review comments](map-controls.md#review-comments). |
| **Attribute table** | Bottom | The attributes of the selected vector or DuckDB layer, with its own explorer, statistics, chart, and export tools. Expand it from the status bar. See [Attribute Table](attribute-table.md). |
The Layers, Style, and Attribute panels can each be shown or hidden from **Settings → Layout**, and panels auto-hide on small screens. Resize the Layers and Style panels by dragging their inner edge, and the attribute table by dragging its top edge.
The Browser, Layers, Style, Comments, and Attribute panels can each be shown or hidden from **Settings → Layout**, and panels auto-hide on small screens. Resize the Layers and Style panels by dragging their inner edge, and the attribute table by dragging its top edge.
## The map
@@ -166,8 +168,8 @@ Messages about what just happened (a layer that failed to load, a file that was
## Theme
Use the sun/moon button on the toolbar to switch between light and dark themes. The theme also follows your operating system preference by default, and you can set it for embeds with the `theme=dark` or `theme=light` URL parameter. See [Embedding & Sharing](embedding.md).
Use the sun/moon button on the toolbar to switch between light and dark themes, and **Settings → Appearance** to pick an accent color. The theme follows your operating system preference by default, and you can set it for embeds with the `theme=dark` or `theme=light` URL parameter. See [Embedding & Sharing](embedding.md).
## Desktop and browser
The same UI runs as an installed desktop app (built with Tauri) and as a web app in the browser. The browser build covers most workflows, but features that need the local filesystem (file dialogs, local MBTiles and raster reads, project save/open, and the Python sidecar tools) require the desktop app. Each affected page notes these differences. See [Getting Started](../getting-started.md) for installation and [Downloads](../downloads.md) for installers.
The same UI runs as an installed desktop app (built with Tauri) and as a web app in the browser. The browser build covers most workflows, but features that need direct access to the local filesystem (native file dialogs, local MBTiles and raster reads, reopening referenced local files, and the Python sidecar tools, unless a self-hosted web deployment proxies the sidecar) require the desktop app. In the browser, projects open through the browser's file picker and save through its save dialog or as a download. Each affected page notes these differences. See [Getting Started](../getting-started.md) for installation and [Downloads](../downloads.md) for installers.
+7 -3
View File
@@ -96,7 +96,7 @@ Selecting a layer expands a row of icon buttons on its card:
| **Identify features** | Click features on the map to see their attributes in a popup. On a raster layer this reads the pixel value instead, and on a multiband raster it also builds a [spectral profile](styling.md#spectral-profile). On an editable vector layer, each result also offers **Edit geometry** (opens the GeoEditor with that feature selected) and **Edit attributes** (opens the attribute table in edit mode on that feature's row). |
| **Open Style panel** | Select the layer and open its [styling controls](styling.md). |
| **Layer actions** | The full menu, below. |
| **Metadata** | Inspect the layer's source and configuration. |
| **Metadata** | Inspect the layer's source and configuration, and edit its descriptive metadata (abstract, keywords, license, contact, lineage, temporal extent, and links). **Export as STAC Item** writes that metadata out as a STAC Item. |
| **Remove layer** | Delete the layer from the project. |
The **Layer actions** menu (the `…` button) holds everything else:
@@ -115,11 +115,15 @@ The **Layer actions** menu (the `…` button) holds everything else:
| **Select features** | The interactive selection modes: by click, rectangle, polygon, freehand, or radius, plus **Clear Selection**. Hold `Shift` to add, `Alt` to remove, `Shift`+`Alt` to intersect, and `Esc` to cancel. |
| **Select by Expression…** / **Select by Location…** | Build a selection from an attribute expression or a spatial relationship. Select by Expression can also apply the expression as a persistent layer filter, hiding non-matching features without creating a new layer. Both are also on the [Edit menu](interface.md#the-top-toolbar). |
| **Bind to Time Slider…** | Drive the Time Slider from one of this layer's date or number fields. |
| **Export** | Write the layer out as GeoJSON, GeoParquet, GeoPackage, KML, KMZ, zipped Shapefile, or CSV (attributes only). A polygon layer drawn as a 3D extrusion can also be exported as a 3D model (glTF `.glb`, OBJ, or STL) for Blender and other 3D tools. See [3D model export](#3d-model-export). |
| **Export** | Write the layer out as GeoJSON, GeoParquet, GeoPackage, KML, KMZ, zipped Shapefile, or CSV (attributes only). On a raster layer it offers **GeoTIFF (COG)** and **Extract subset…**, and on a LiDAR layer LAS, LAZ, or COPC. A polygon layer drawn as a 3D extrusion can also be exported as a 3D model (glTF `.glb`, OBJ, or STL) for Blender and other 3D tools. See [3D model export](#3d-model-export). |
| **Styles** | Import and export symbology — see [below](#importing-and-exporting-styles). |
| **Save to My Data** | Store the fully configured layer in your personal library, ready to re-add from the [Browser panel](adding-data.md#the-browser-panel) in any later project. |
| **Copy style** / **Paste style** | Carry symbology from one layer to another. |
| **Refresh** / **Auto refresh** | Reload the source now, or on an interval — see [Refreshing live layers](#refreshing-live-layers). |
| **Reload from disk** / **Watch file for changes** | Desktop app, layers read from a local file: re-read the file now, or reload it whenever it changes on disk. These replace **Refresh** / **Auto refresh** on such layers. |
| **Save edits to source file** | Desktop app: write attribute and geometry edits back to the layer's GeoJSON or GeoPackage file. On an ArcGIS feature layer, a PostGIS table, or a SQL Server table it reads **Save edits to ArcGIS service**, **Save edits to PostGIS table**, or **Save edits to SQL Server table**. |
| **Materialize to editable layer** | DuckDB layers: copy the query result into an editable GeoJSON layer. |
| **Edit query in SQL Workspace** | SQL query layers: reopen the layer's query in the [SQL Workspace](sql-workspace.md). |
Some entries are unavailable on layers they do not apply to: **Select by Location** needs a second layer to compare against, and **Paste style** needs a style on the clipboard.
@@ -178,7 +182,7 @@ Layers added from a [DuckDB source](adding-data.md#databases) or produced by the
## The basemap
The **Background** entry at the bottom of the panel is the basemap. Toggle its visibility and adjust its opacity here. To change which basemap is shown, use the **Basemaps** plugin from the [Plugins menu](plugins.md). See [Adding Data](adding-data.md#basemaps).
The **Background** entry at the bottom of the panel is the basemap. Toggle its visibility and adjust its opacity here. To change which basemap is shown, double-click the row to open the **Change background** dialog, or use the **Basemaps** plugin from the [Plugins menu](plugins.md). See [Adding Data](adding-data.md#basemaps).
!!! tip "Editing geometry"
To draw or edit features directly on the map, activate the **GeoEditor** plugin from the [Plugins menu](plugins.md). It adds drawing, vertex editing, and deletion tools for GeoJSON layers.
+2 -1
View File
@@ -31,6 +31,7 @@ These are interactive panels provided by the MapLibre components plugin:
| **Colorbar** | Display a continuous color scale for raster values. |
| **Legend** | Show a legend describing the layers on the map. |
| **HTML** | Display custom HTML content in an on-map panel. |
| **Image** | Place images from `https://` URLs on the map, such as a logo, a north arrow, or a legend, in any corner. Each image has a title bar that folds it away, and a map can hold up to 20. |
| **Measure** | Measure distances and areas interactively, with heading and terrain-aware 3D readouts. See [Measuring distance, area, and heading](#measuring-distance-area-and-heading). |
| **Bookmark** | Save named map views and jump back to them. Bookmarks and their folders are saved in the project, so they travel with the file (bookmarks saved by older versions in this browser are copied into the first project you open that has none and saved there; the old browser copy is left in place but no longer updated). |
| **Minimap** | Show an overview map of the current extent. |
@@ -49,7 +50,7 @@ The **Print Layout** composer lives under the Project menu; see [Print Layout](p
**Exact segment** is the keyboard route to the same thing: type a length and a bearing and click **Add** to extend the measurement by a segment of exactly those dimensions, instead of clicking for it. Useful for retracing a deed description or a survey traverse.
Beyond distance and area, the panel adds two further sections:
Beyond distance and area, the panel adds further readouts:
- **Heading** — the direction of the measured line, as degrees plus a 16-point compass label (for example `310° NW`). This is a true great-circle initial bearing, not the angle the line makes on screen: on a Mercator map Washington to London *looks* due east, while the real initial bearing is about 50°. On a path with more than two points, the heading is the overall first-to-last direction rather than one row per segment.
- **Final heading** — the bearing arriving at the far end. A great circle changes direction along its length, so this row appears only on lines long enough for the convergence to reach a degree; short measurements stay a single row.
+4 -3
View File
@@ -8,7 +8,7 @@ The **Plugins** menu lists every available plugin under **Activate plugin**. Cli
![The Plugins menu, listing every built-in plugin under Activate plugin](https://assets.geolibre.app/images/geolibre-plugins-menu.webp)
Most entries open a submenu that **activates** the plugin and **positions** its on-map control in any corner: top left, top right, bottom left, or bottom right. A few behave differently: **Flight Simulator**, **SamGeo**, and **Point Cloud Annotation** toggle directly with no submenu, and **Web Services** and **DGGS** open a list of their sub-plugins instead.
Most entries toggle their plugin directly. The plugins that place a control on the map (**GeoEditor**, **Annotations**, **Dimensions**, **Street View**, and **Mapillary**) open a submenu instead, which **activates** the plugin and **positions** its on-map control in any corner: top left, top right, bottom left, or bottom right. **Web Services** and **DGGS** open a list of their sub-plugins, and plugins you install from the marketplace are listed under **Installed**, which also links to **Manage Plugins…**.
![A plugin submenu, with Activate above the four map-corner positions](https://assets.geolibre.app/images/geolibre-plugin-position-menu.webp)
@@ -78,7 +78,7 @@ GeoLibre registers 68 built-in plugins.
| **OLC** | Plugins → DGGS | `olc` | The Open Location Code (plus code) grid over the current view; identify a cell and export the grid or selection. |
| **Geohash** | Plugins → DGGS | `geohash` | The Geohash grid over the current view; identify a cell and export the grid or selection. |
| **Tilecode** | Plugins → DGGS | `tilecode` | The web-map tile grid (z/x/y tile codes) over the current view; identify a cell and export the grid or selection. |
| **Basemaps** | Plugins | `basemap-control` | A basemap gallery for switching the background map, from the same catalog as the Change basemap dialog. |
| **Basemaps** | Plugins | `basemap-control` | A basemap gallery for switching the background map, from the same catalog as the Change background dialog. |
| **Historical Imagery** | Plugins | `esri-wayback` | Browse historical aerial and satellite imagery (Esri World Imagery Wayback) for a location. |
| **Time Slider** | Plugins | `time-slider` | Filter a temporal layer by a date or number field, or step a time-enabled raster or Zarr cube through its time axis. |
| **Timelapse** | Plugins | `timelapse` | Animate annual cloudless basemaps (EOX Sentinel-2, NASA GIBS Landsat/WELD and MODIS land cover) and record them to video. |
@@ -176,7 +176,7 @@ Open **Settings → Manage Plugins** to browse the marketplace. The dialog is mo
- **Search** the registry and **Install** an entry with one click. Installation records the plugin's manifest URL and registers it immediately, with no restart.
- **Update** appears when a newer version is published; it re-fetches and re-registers the plugin in place, keeping the old version if the update fails.
- **Uninstall** (after a confirmation) unregisters the plugin at runtime and tears down any active control.
- The **Settings** section manages additional plugin sources: extra local directories and manual manifest URLs.
- The **Settings** section installs a packaged plugin (`.zip`) with **Install from file** and manages additional plugin sources: extra local directories (desktop only) and manual manifest URLs.
Compatibility is checked against each entry's `minGeoLibreVersion`, so incompatible plugins are flagged rather than installed.
@@ -188,6 +188,7 @@ Compatibility is checked against each entry's `minGeoLibreVersion`, so incompati
- **Curated registry**: the marketplace fetches a versioned JSON registry, hosted by default at `plugins.geolibre.app`. The registry and plugin bundles live in the [opengeos/geolibre-plugins](https://github.com/opengeos/geolibre-plugins) repository.
- **Manifest URL**: point the Settings section at any `plugin.json` manifest URL.
- **Local directory**: load a plugin from a local folder (desktop app).
- **Plugin archive**: install a packaged `.zip` from **Install from file**; in the browser it is stored locally and reloads on your next visit.
- **Bundled drop-ins**: plugins placed in `public/plugins/<id>/` load automatically in a build.
## Writing your own plugin
+3 -2
View File
@@ -91,8 +91,9 @@ elevation-filtered points are skipped, as in a drawn selection.
## Pre-labelling with Whitebox
**Pre-label (Whitebox)** runs a Whitebox LiDAR classifier on the session's
points in the browser (the same WebAssembly build as **Processing → Whitebox**)
and applies its classes as one undoable edit:
points in the browser (the same WebAssembly build as the **Processing →
Whitebox Toolbox**) when you press **Run pre-label**, and applies its classes
as one undoable edit:
- **Ground (improved ground point filter)** separates ground (2) from
everything else (1).
+4 -4
View File
@@ -159,7 +159,7 @@ GeoLibre's own tools, under **Processing → GeoLibre Toolbox**.
#### Engines
Every vector tool can run on one of three engines, selectable in the dialog:
The vector tools run on one of three engines, selectable in the dialog (a few, such as Reproject, need the Sidecar or Python engine):
- **Client (Turf.js)**: runs entirely in the browser. No setup, works offline, and operates on the layer's GeoJSON.
- **Sidecar (GeoPandas)**: runs on the optional Python sidecar for projection-aware results, backed by GeoPandas and Shapely. The dialog falls back to the client engine when the sidecar's optional `vector` extra is not installed.
@@ -221,7 +221,7 @@ Eight of them also offer a **Client (browser)** engine that computes on the load
| **Mosaic / merge** | Combine several rasters into one. |
| **Focal statistics** | Compute a moving-window statistic (mean, min, max, …) over a raster. |
**Georeferencing** sits at the bottom of the same submenu: it pins a non-georeferenced image to the map with ground control points using a least-squares affine fit, reporting per-GCP and RMS residuals.
**Download Global DEM** sits at the top of the same submenu: it downloads a DEM for the current map view or a box drawn on the map from the public AWS Terrain Tiles dataset (no API key needed), and opens in the Whitebox Toolbox dialog rather than the Raster tools dialog. **Georeferencing** sits at the bottom: it pins a non-georeferenced image to the map with ground control points using a least-squares affine fit, reporting per-GCP and RMS residuals.
See the [Terrain Analysis tutorial](../tutorials/terrain-analysis.md).
@@ -269,7 +269,7 @@ The sidecar can also confine conversion inputs and outputs to an allowlist of di
| **Getis-Ord Gi\* hotspots** | Per-feature hot and cold spots with significance levels. |
| **Average nearest neighbor** | Whether a point pattern is more clustered or dispersed than random. |
| **Kernel density (heatmap)** | A continuous density surface from a point layer. |
| **Emerging Hot Spot** | Hot-spot trends over time from a space-time cube. |
| **Emerging Hot Spot (space-time cube)** | Hot-spot trends over time from a space-time cube. |
| **Composite score (suitability index)** | Normalize, weight, and combine several numeric fields into one index (0-100 by default, or 0-1), styled on the map by the score. |
@@ -344,7 +344,7 @@ Either way the tool list is the same; only the executing engine changes.
- **Search** by name at the top of the tool list, or narrow with the **category** and **source** dropdowns.
- **Browse by category** without opening the dialog at all: the Processing menu has a submenu per category (Conversion, Hydrology, LiDAR, Network, Projection, Raster, Remote Sensing, Terrain, Vector) with nested subcategory submenus, including the `GeoLibre (WASM)` subheading for GeoLibre's own tools. Picking a tool opens the dialog with it preselected. The catalog is bundled offline, so the menu works with no network.
- **Fill in the form** — the dialog builds it from the tool's own parameter manifest, with a file picker for path inputs and an output-format dropdown for vector outputs. Parameters that are ground distances get a metric unit picker.
- **Run**, and the output is added to the map. Raster outputs are Cloud Optimized GeoTIFFs. A LiDAR output from the in-browser (WASM) engine, such as a classified point cloud, is downloaded as a LAS file; load it with **Add Data → LiDAR Layer**.
- **Run**, and the output is added to the map. Raster outputs are Cloud Optimized GeoTIFFs. A LiDAR output from the in-browser (WASM) engine, such as a classified point cloud, is added to the map as a point cloud layer (converted to COPC first when needed), and is downloaded as a file only if that fails. A LiDAR layer's **Export** menu saves it as LAS, LAZ, or COPC.
![The Whitebox Toolbox dialog, with the tool search on the left and the selected tool's generated form on the right](https://assets.geolibre.app/images/geolibre-whitebox-toolbox.webp)
+13 -8
View File
@@ -11,7 +11,7 @@ A GeoLibre project captures your whole workspace in a single `.geolibre.json` fi
**Project → New...** starts a fresh project. GeoLibre offers to save the current project first, then resets the layers, map view, controls, and plugin state to defaults.
The **New project** dialog names the project and picks its starting basemap: the OpenFreeMap styles, a **Regional** group, sections for the Moon, Mars, and other celestial bodies, a blank background, or a custom MapLibre style or PMTiles URL.
The **New project** dialog names the project and picks its starting basemap: the OpenFreeMap styles, the Protomaps styles (when the build offers them), a **Regional** group, sections for the Moon, Mars, and other celestial bodies, a blank background, or a custom MapLibre style or PMTiles URL. Templates you saved with **Save as template...** appear above the basemaps under **Saved Templates**.
![The New project dialog, with a project name field and the basemap gallery](https://assets.geolibre.app/images/geolibre-new-project.webp)
@@ -19,13 +19,13 @@ The collapsible **Examples** section lists a few curated starter projects from t
## Open
**Project → Open From** has two sources:
**Project → Open From** has three sources:
- **File...** opens a `.geolibre.json` file from disk (desktop app).
- **File...** opens a `.geolibre.json` file from disk. The browser build uses the browser's file picker.
- **URL...** loads a public `.geolibre.json` from an HTTP or HTTPS URL. This works in the browser too and adds the project to your recent list.
- **Gallery...** browses the shared project gallery and opens any entry with one click.
**Project → Open Recent** lists the projects you have opened before, each with its name, path, and the time you last opened it. Click an entry to reopen it, use the small remove button to drop a single entry, or choose **Clear Recent Projects** to empty the list. On the desktop app the recent list persists across sessions; in the browser it tracks URL-based projects.
**Project → Open Recent** lists the projects you have opened before, each with its name, path, and the time you last opened it. Click an entry to reopen it, use the small remove button to drop a single entry, or choose **Clear Recent Projects** to empty the list. On the desktop app the recent list persists across sessions. In the browser, a URL-based entry reopens directly, while an entry for a local file asks you to pick the file again, because a browser cannot reopen a file on disk by itself.
!!! note "Loading a project at startup"
You can open a project directly by passing its URL with the `url` query parameter, for example `?url=https://share.geolibre.app/you/project.geolibre.json`. See [Embedding & Sharing](embedding.md).
@@ -37,7 +37,7 @@ The collapsible **Examples** section lists a few curated starter projects from t
- **Save** writes back to the project's existing file path.
- **Save As...** prompts for a new name and location.
Both capture the current map view, basemap, layers, styles, preferences, and plugin state at the moment you save. Projects that were opened from a URL have no writable local path, so both Save and Save As fall back to the save dialog. Saving requires the desktop app.
Both capture the current map view, basemap, layers, styles, preferences, and plugin state at the moment you save. Projects that were opened from a URL have no writable local path, so both Save and Save As fall back to the save dialog. In the browser build, saving uses the browser's save dialog where it has one (Chromium-based browsers); elsewhere it asks for a file name and downloads the project to your downloads folder.
**Project → Duplicate project** copies the open project into a new, unsaved one, so you can branch off an experiment without touching the original file.
@@ -78,8 +78,9 @@ An ArcGIS Pro project can contain several maps; GeoLibre imports its first 2D ma
Connecting your account depends on the build:
- **Web app**: click **Sign in** in the Share dialog (or Settings → Environment Variables). A popup opens `share.geolibre.app`'s consent page; approving it connects the app, and the sign-in is kept for the browser session. Session expired prompts offer a one-click re-sign-in.
- **GeoLibre Desktop** (and as a fallback everywhere): paste a personal API token — created under Settings → API tokens at [share.geolibre.app/settings](https://share.geolibre.app/settings) — into the **Share.GeoLibre API token** field in **Settings → Environment Variables**.
- **Web app**: click **Sign in** in the Share dialog (or under **Share.GeoLibre account** in **Settings → Environment Variables**). A popup opens `share.geolibre.app`'s consent page; approving it connects the app, and the sign-in is kept for the browser session. Session expired prompts offer a one-click re-sign-in.
- **GeoLibre Desktop**: sign in the same way; the consent page opens in your system browser and returns to the app. The sign-in is kept in your system keychain, so it survives restarts. Once signed in, **Settings → Environment Variables** also lets you review and revoke your sessions.
- **Mobile apps, notebook embeds, and as a fallback everywhere**: paste a personal API token — created under Settings → API tokens at [share.geolibre.app/settings](https://share.geolibre.app/settings) — into the **Share.GeoLibre API token** field in **Settings → Environment Variables**.
### Share-readiness check
@@ -105,7 +106,11 @@ The dialog warns even before a share token is configured, because uploading a sa
## Export as HTML
**Project → Export as HTML...** writes the whole project to a single standalone HTML file that runs offline with no server. Host it anywhere, or open it straight from disk.
**Project → Export → Export as HTML...** writes the whole project to a single standalone HTML file that runs offline with no server. Host it anywhere, or open it straight from disk.
## Layer styles
**Project → Export → Export Layer Styles...** writes every layer's style to one JSON file keyed by layer name. **Project → Import → Import Layer Styles...** applies such a file to the open project, restyling each layer whose name matches in one undoable step, and reports any styles that matched no layer. To apply a styles file automatically, choose it under **Default layer styles** in [Settings → Startup](settings.md#startup): each layer you add whose name matches is styled as it arrives, while layers in opened projects keep their saved styles.
## Collaborate
+5 -5
View File
@@ -9,13 +9,13 @@ JavaScript.
| --- | --- | --- |
| **MapLibre GL JS** | The broadest GeoLibre feature and plugin support; vector styles, raster data, terrain, and lightweight 3D content | No credential required; bundled with GeoLibre |
| **Mapbox GL JS** | Mapbox styles and services, Mapbox globe and terrain, and a Style-Spec-compatible alternative to MapLibre | A Mapbox access token is required; the engine is loaded only when selected |
| **CesiumJS** | A native 3D globe, terrain, 3D Tiles, CZML, Cesium ion assets, I3S, and globe-oriented analysis | A Cesium ion token is required; the web version bundles a demo token, desktop and mobile need your own; loaded only when selected |
| **CesiumJS** | A native 3D globe, terrain, 3D Tiles, CZML, Cesium ion assets, I3S, and globe-oriented analysis | Works without a token; a Cesium ion token adds World Terrain and ion imagery and assets (the web version bundles a demo token, desktop and mobile need your own); loaded only when selected |
| **ArcGIS Maps SDK for JavaScript** | Native ArcGIS services, Esri basemaps, and ArcGIS 2D maps or 3D scenes | Works without a key; an ArcGIS API key adds Esri basemap styles; SDK modules load from Esri's CDN when first selected |
## Switch the primary map
Choose **View → Rendering engine**, then select **MapLibre**, **Mapbox**,
**Cesium**, or **ArcGIS**. GeoLibre replaces the active canvas; it does not keep
**ArcGIS**, or **Cesium**. GeoLibre replaces the active canvas; it does not keep
the previous engine hidden and consuming graphics resources.
The project store is independent of any renderer, so the camera, layers,
@@ -41,9 +41,9 @@ current device and are not written to the shared project file.
- MapLibre needs no credential.
- Mapbox requires an access token and is subject to Mapbox's terms and usage
pricing.
- Cesium requires a Cesium ion token. The web version bundles a demo token;
the desktop and mobile apps need your own. The token enables Cesium World
Terrain and ion-hosted imagery and assets.
- Cesium runs without a token, using the project basemap as imagery. A Cesium
ion token enables Cesium World Terrain and ion-hosted imagery and assets. The
web version bundles a demo token; the desktop and mobile apps need your own.
- ArcGIS works without a key by translating the project basemap. A key enables
Esri basemap styles. The renderer itself still needs access to
`js.arcgis.com` on its first load because the SDK is not bundled.
+1
View File
@@ -164,6 +164,7 @@ The sidecar exposes these endpoints (proxied to `samgeo-api`):
| Method | Endpoint | Description |
| --- | --- | --- |
| GET | `/ml/status` | Backend availability, default model, version, and model list. |
| GET | `/ml/models` | The backend's model catalog (available and loaded models). |
| POST | `/ml/segment/text` | Text-prompt segmentation (`prompt`, `confidence_threshold`). |
| POST | `/ml/segment/automatic` | Automatic mask generation. |
| POST | `/ml/segment/predict` | Box/point prompt segmentation (`boxes`, `point_coords`, `point_labels`, `point_crs`). |
+39 -15
View File
@@ -1,6 +1,6 @@
# Settings & Preferences
The **Settings** menu holds the workspace preferences: how the map behaves, which panels are visible, runtime environment variables, project settings, and the entry point to [Manage Plugins](plugins.md).
The **Settings** menu holds the workspace preferences: how the map behaves, which panels are visible, the interface language and appearance, geocoding and AI providers, runtime environment variables, and the entry point to [Manage Plugins](plugins.md).
![The Settings menu](https://assets.geolibre.app/images/geolibre-settings-menu.webp)
@@ -8,19 +8,25 @@ The Settings dialog is organized into these sections:
| Section | What it covers |
| --- | --- |
| **Language** | The interface language. See [Internationalization](../i18n.md). |
| **Language** | The interface language and optional Whitebox language packs. See [Language](#language). |
| **Map** | Navigation constraints, celestial body, scale units, and coordinate format. See [Map Preferences](#map-preferences). |
| **Layout** | Which panels and toolbar labels are shown. See [Layout](#layout). |
| **Appearance** | Light or dark mode and the accent color applied on top of it. |
| **Interface** | The [UI profile](../ui-profiles.md) — an experience level (Beginner, Intermediate, Advanced, or Custom) that simplifies the menus, data sources, and plugins on offer. Nothing is removed permanently; you can switch levels at any time. |
| **Geocoding** | The address-search provider. See [Data Integrations](data-integrations.md#geocoding). |
| **AI Providers** | Model and credentials for the [AI Assistant](ai-assistant.md). |
| **Appearance** | Light or dark mode and the accent color applied on top of it. See [Appearance](#appearance). |
| **Interface** | The [UI profile](../ui-profiles.md) — an experience level (Beginner, Intermediate, Advanced, or Custom) that simplifies the menus, data sources, and plugins on offer. Nothing is removed permanently; you can switch levels at any time. See [Interface](#interface). |
| **Geocoding** | The address-search provider. See [Geocoding](#geocoding). |
| **AI Providers** | Model and credentials for the [AI Assistant](ai-assistant.md). See [AI Providers](#ai-providers). |
| **Environment** | The share token and runtime key-value pairs. See [Environment Variables](#environment-variables). |
| **Cloud Storage** | S3 connections for private buckets, and the S3 Browser's default location. See [Cloud Storage](#cloud-storage). |
| **Updates** | Update checks (desktop only). See [Updates](#updates). |
| **Startup** | Which project the app opens with (desktop only). See [Startup](#startup). |
| **Startup** | The untitled workspace's projection and view, and (desktop only) which project the app opens with. See [Startup](#startup). |
| **Style Manager** | Your saved symbology presets, reachable here and from a layer's **Layer actions → Styles → Saved styles**. See [Styling Layers](styling.md). |
## Language
**Settings → Language** sets the **Interface language**. Each option is shown in its own script, with the English name alongside. Right-to-left languages mirror the whole layout. See [Internationalization](../i18n.md) for the supported languages.
The same section manages optional **Whitebox language packs**, which translate Whitebox tool names, parameters, and help text without making every installation larger. Choose **Download pack** to fetch the official pack for the selected language, or **Import file** to install a pack file. **Remove pack** returns the Whitebox metadata to English. Packs are stored only on the current device. Downloading one sends the locale code, but no project data, to the pack host. Builds that disable downloads offer only **Import file**.
## Map Preferences
**Settings → Map Preferences** controls how the map can be navigated:
@@ -45,20 +51,36 @@ Use **Use Current View** to set the bounds from where the map is now, or **Reset
## Layout
**Settings → Layout** toggles the chrome around the map:
**Settings → Layout Settings** toggles the chrome around the map:
- **Show toolbar labels**: text labels next to toolbar buttons, or icon-only.
- **Show project info**: the project name and path in the toolbar.
- **Show Layers panel**, **Show Style panel**, **Show Attribute panel**: per-panel visibility.
- **Show Layers panel**, **Show Style panel**, **Show Browser panel**, **Show Comments panel**: per-panel visibility.
Panels also auto-hide on small screens for a responsive layout.
## Appearance
**Settings → Appearance** picks the **Mode** (Light or Dark) and an **Accent color** applied on top of it: Blue, Violet, Emerald, Rose, Amber, or **Custom**, which takes any color from a picker or a hex code.
## Interface
**Settings → Interface** chooses an **Experience level** that simplifies the interface: **Beginner**, **Intermediate**, and **Advanced** apply a curated layout at once. Below the levels you can switch individual **Data sources**, **Plugins**, and **Menus** on or off. Editing any of them switches the level to **Custom**. Nothing is removed permanently, and **Reset** restores the defaults. When an administrator manages the profile, this section is read-only. See [UI Profiles](../ui-profiles.md#settings-interface).
## Geocoding
**Settings → Geocoding** chooses the **Provider** used by Geocode Addresses, address import, and Reverse Geocode. Nominatim is the default and needs no key. For providers that need one, enter an **API key**. You can also override the **Forward endpoint** and **Reverse endpoint**, for example to point at a self-hosted instance, and give Nominatim a **Contact email**. The section notes where keys are stored: in the system keychain when it is available, otherwise in the project file. Keys are sent as query parameters, so they appear in request URLs. See [Data Integrations → Geocoding](data-integrations.md#geocoding) for the provider list.
## AI Providers
**Settings → AI Providers** configures the credentials for the [AI Assistant](ai-assistant.md). Each configuration is a named **profile**, such as *Work Gemini* or *Local Ollama*. Choose **Add profile**, pick a provider, fill in the fields it asks for, then **Save profile**. Mark one profile **Set as default** to use it when the assistant opens; one configured profile is enough to start chatting. Credentials are saved on the current device, never in the project file. When a system keychain is available, the secret fields go there. On the desktop app, a blank key field falls back to the matching operating-system environment variable. See [AI Assistant → Setup](ai-assistant.md#setup-choose-an-ai-provider).
## Environment Variables
**Settings → Environment Variables** (the **Environment** tab in the Settings dialog) holds the share account connection and the runtime key-value pairs that GeoLibre and its plugins read, such as API keys:
- **Share.GeoLibre account** (web app): sign in to `share.geolibre.app` through a popup, or sign out, used by **Project → Share** and the Project Gallery. See [Projects](projects.md#share).
- **Share.GeoLibre API token**: the personal API token used by **Project → Share** — the normal path on the desktop app, and a fallback on the web. See [Projects](projects.md#share).
- **Share.GeoLibre account**: sign in to `share.geolibre.app` (a popup on the web, your system browser on the desktop app), or sign out. Used by **Project → Share** and the Project Gallery. See [Projects](projects.md#share).
- **Share.GeoLibre API token**: a personal API token for **Project → Share**, the fallback when sign-in is not available (for example on mobile or in a notebook embed). See [Projects](projects.md#share).
- **Environment variables**: named key-value pairs (for example, API keys for Earth Engine, Street View, and other integrations). You can enable or disable individual variables, and secret values are masked. Variable names must start with a letter or underscore and contain only letters, numbers, and underscores.
!!! tip "Where credentials go"
@@ -68,7 +90,7 @@ Panels also auto-hide on small screens for a responsive layout.
On the desktop app, the [AI Assistant](ai-assistant.md) also reads its own allowlisted keys (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, and the other provider variables) straight from your operating system's environment variables — so you can keep API keys out of the saved project file entirely. A value entered here always takes precedence over the OS environment. See [AI Assistant → Reading keys from your system environment](ai-assistant.md#reading-keys-from-your-system-environment-desktop) for the full list.
!!! tip "Protomaps basemaps"
To use the [Protomaps](https://protomaps.com) basemaps in the **New map** dialog, add an environment variable named `VITE_PROTOMAPS_API_KEY` with your own Protomaps API key. The Protomaps options appear in the dialog as soon as the key is enabled — no restart needed. When no key is set, the Protomaps section is hidden. See [Getting Started](../getting-started.md#optional-basemap-credentials) for setting the key at build time for a self-hosted deployment.
To use the [Protomaps](https://protomaps.com) basemaps in the **New project** dialog, add an environment variable named `VITE_PROTOMAPS_API_KEY` with your own Protomaps API key. The Protomaps options appear in the dialog as soon as the key is enabled — no restart needed. When no key is set, the Protomaps section is hidden. See [Getting Started](../getting-started.md#optional-basemap-credentials) for setting the key at build time for a self-hosted deployment.
## Cloud Storage
@@ -80,7 +102,7 @@ The project name is edited in place on the right of the toolbar, and it is saved
## Startup
**Settings → Startup** chooses how GeoLibre opens a new session. The installed desktop app also offers project restoration modes:
**Settings → Startup Settings** chooses how GeoLibre opens a new session. The installed desktop app also offers project restoration modes:
| Mode | Behavior |
| --- | --- |
@@ -90,6 +112,8 @@ The project name is edited in place on the right of the toolbar, and it is saved
**Enable 3D globe by default** controls the projection of the new, untitled workspace shown when no project is provided. Turn it off to start that workspace in Mercator. **Default map view** sets the center longitude, center latitude, and zoom level for the same workspace. Choose **Use Current View** to copy the center and zoom from the map canvas. A restored project or project link always uses the projection and camera saved in that project instead.
**Open the S3 Browser at startup** shows the [S3 Browser](cloud-storage.md) panel each time GeoLibre starts; it stays open when you open a project. **Default layer styles** takes a layer styles file (**Project → Export → Export Layer Styles**): each layer you add whose name matches a style in it is styled automatically, while layers in opened projects keep their saved styles. GeoLibre keeps a copy of the file, so choose it again after editing it.
If the startup project has been moved or deleted, GeoLibre opens the default workspace instead, says so in a banner, and drops the missing file from the recent-projects list.
!!! note "Project restoration is desktop only"
@@ -101,13 +125,13 @@ Two deliberate limits are worth knowing:
- **A URL always wins.** Launching with a project or `?data=` parameter in the URL skips the startup restore entirely, and so does opening your own project before the restore finishes.
!!! note "Android reopens its own copy"
Android identifies a project picked from device storage by a temporary reference that stops working once the app's process ends — which is exactly when the startup restore runs. So on Android GeoLibre keeps a copy of the startup project in its own private storage and reopens that copy, refreshing it every time you open or save the project. Two consequences worth knowing: a project edited in another app after you last saved it in GeoLibre reopens as GeoLibre last saw it (open it again from **File → Open** to pick the newer contents back up), and a project deleted from the device still reopens from GeoLibre's copy rather than dropping out of the startup preference, because Android reports a deleted file and an expired reference the same way.
Android identifies a project picked from device storage by a temporary reference that stops working once the app's process ends — which is exactly when the startup restore runs. So on Android GeoLibre keeps a copy of the startup project in its own private storage and reopens that copy, refreshing it every time you open or save the project. Two consequences worth knowing: a project edited in another app after you last saved it in GeoLibre reopens as GeoLibre last saw it (open it again from **Project → Open From → File...** to pick the newer contents back up), and a project deleted from the device still reopens from GeoLibre's copy rather than dropping out of the startup preference, because Android reports a deleted file and an expired reference the same way.
Saving a project you opened from device storage asks you where to save it, once — Android does not grant write access to a file you only picked to read. If your startup project is that project, the preference follows it to the file that save creates, so it keeps opening the copy you are actually working in.
## Updates
**Settings → Updates** (desktop only) controls the update check: whether GeoLibre checks for a newer version at startup, and which kinds of releases raise a notification. Turn the check off for a fully offline workflow.
**Settings → Update Settings** (desktop only) controls the update check: whether GeoLibre checks for a newer version at startup, and which kinds of releases raise a notification. Turn the check off for a fully offline workflow.
## Style Manager
+5 -5
View File
@@ -6,7 +6,7 @@ The **SQL Workspace** runs DuckDB Spatial SQL right in the app, against your loa
## Querying loaded layers
Every loaded vector layer is exposed as a queryable table; the dialog lists the available table names at the top. Write a query in the editor and click **Run** to see the results.
Every loaded vector layer is exposed as a queryable table; the workspace lists the available table names under **Queryable layers**. Write a query in the editor and click **Run** (or press `Ctrl/Cmd+Enter`) to see the results. `Tab` or `Ctrl+Space` autocompletes.
```sql
SELECT NAME, CONTINENT, POP_EST
@@ -50,7 +50,7 @@ The bare `FROM s3://…` form works too — the workspace wraps it in the matchi
## Choosing a SQL engine
The **Engine** menu (top right) selects which SQL engine runs your query. All three load a spatial extension, so `ST_*` functions are available in each.
The **SQL engine** menu (top right) selects which SQL engine runs your query. All three load a spatial extension, so `ST_*` functions are available in each.
- **DuckDB** (default) — DuckDB Spatial, in-browser. Queries loaded layers, local files, and remote URLs (including `s3://`/`gs://`/`az://` public data). Works offline after first use.
- **PostGIS** — PGlite + PostGIS, in-browser. Queries loaded layers using full PostGIS SQL. The first run loads a ~19 MB engine.
@@ -64,12 +64,12 @@ The **Engine** menu (top right) selects which SQL engine runs your query. All th
## Sample queries and history
- **Sample queries** and **Sample query for layer** menus drop ready-made queries into the editor to get you started.
- Your previous queries are kept in a **history** so you can rerun them.
- The **Sample queries…** and **Sample query for layer…** menus drop ready-made queries into the editor to get you started.
- Your previous queries are kept under **History…** so you can rerun them.
## Using the results
When a query returns geometry, you can **add the result to the map** as a new layer (with an optional layer name). The result layer behaves like any vector layer, with [identify, selection, and the attribute table](attribute-table.md). You can also **export** results as CSV or GeoParquet.
When a query returns geometry, **Add as layer** adds the result to the map as a new layer (with an optional layer name). The result layer behaves like any vector layer, with [identify, selection, and the attribute table](attribute-table.md). With the DuckDB engine, **Add as query layer** instead adds a live layer that re-runs the SQL when you refresh it from the Layers panel. **Export CSV** and **Export GeoParquet** save the results to a file (GeoParquet needs a geometry column).
!!! tip "Multiple result layers"
You can add several DuckDB query-result layers to the same project and keep them all open at once.
+6 -2
View File
@@ -10,7 +10,7 @@ For vector layers the Style panel covers fill, stroke, points, labels, and 3D ex
- **Fill**: fill color and fill opacity for polygons.
- **Stroke**: line color and width for lines and polygon outlines.
- **Points**: circle radius for point layers.
- **Points**: circle radius for point layers, or **Use marker icon** to draw a marker instead: a built-in shape (circle, square, triangle, diamond, star, cross, or pin) with a color and size, or a **Custom image**. **Upload image…** accepts SVG, PNG, JPEG, or GIF; raster images draw as they are, so the marker color and per-class colors apply only to SVG markers.
- **Labels**: text color, size, halo color, and halo width.
- **3D extrusion**: turn polygons into extruded blocks, with a height field, height scale, base height, and color. Advanced expressions are available for both height and color.
@@ -122,6 +122,8 @@ The selected vector layer's **Layer actions → Styles** submenu imports and exp
See [Managing Layers](layers.md#importing-and-exporting-styles) for the menu workflow and [Embedding & Sharing](embedding.md#open-remote-data) for the JSON conventions.
To restyle many layers at once, **Project → Export → Export Layer Styles...** writes every layer's style to one file keyed by layer name, and **Project → Import → Import Layer Styles...** applies it to the layers whose names match. See [Projects](projects.md#layer-styles).
!!! tip "Choropleth maps"
To make a choropleth, select **Graduated**, pick a numeric attribute, choose a colormap, and click **Apply style type**. See the [Your First Map tutorial](../tutorials/first-map.md).
@@ -147,10 +149,12 @@ For raster layers the Style panel exposes image adjustments:
- **Brightness** (minimum and maximum)
- **Saturation**
- **Contrast**
- **Hue rotation** (in degrees)
- **Hue Rotate** (in degrees)
These let you tune the look of GeoTIFF, COG, and tile-based raster layers without changing the underlying data.
A WMS layer also gets a **Legend** section. **Get legend** shows the service's legend image, from the capabilities `LegendURL` when one is advertised or a `GetLegendGraphic` request otherwise; if the service has none, you can enter a legend image URL instead. **Add to map** places the legend on the map in an HTML control.
### Raster symbology and histogram stretch
GeoTIFF and COG layers add a **Raster symbology** section with a **Render mode**:
+3 -2
View File
@@ -202,8 +202,9 @@ that is the first thing to try if the window is blank or the map flickers. See
If a plugin's **Activate** action seems to do nothing, first rule out the
stale-cache cause above with a hard refresh or a private window. Once you are on
the latest build, an active plugin shows a checkmark next to its entry in the
**Plugins** menu, and map controls (when the plugin provides one) appear at the
configured corner of the map. A greyed-out plugin does not support the current
**Plugins** menu, and its panel opens in the side panel, or, for the few
plugins with an on-map control (such as GeoEditor or Annotations), the control
appears at the configured corner of the map. A greyed-out plugin does not support the current
rendering engine; hover over it for the reason. A plugin that fails to start is
reported as a notification and in Diagnostics.
+5 -5
View File
@@ -5,7 +5,7 @@ Hydrography Dataset (NHD) network and displaying the result on the map.
## Trace a map point
1. Open **Plugins → USGS NLDI**.
1. Open **Plugins → Web Services → USGS NLDI**.
2. Choose **Complete flowline**, **Upstream only**, or **Downstream only**.
3. Click a point on the map.
@@ -25,10 +25,10 @@ streamgages (`ca_gages` or `nwissite`), groundwater wells (`nwisgw`),
Geospatial Fabric points (`gfv11_pois`), HUC12 pour points (`huc12pp`),
New Mexico water sites (`nmwdi-st`), or another catalog returned by NLDI.
Choose **Plot another navigation layer** to draw the selected features on the
map; flowlines are purple and point catalogs are also rendered by the
navigation layer. These are chained
workflows: the selected map point feeds hydrolocation, its COMID feeds the
basin or navigation request, and the result is rendered on the map.
map; navigation results, flowlines and points alike, are drawn in purple.
These are chained workflows: the selected map point feeds hydrolocation, its
COMID feeds the basin or navigation request, and the result is rendered on the
map.
Use **Export rendered results to GeoJSON** to save the selected point,
flowline, raindrop path, basin, and any plotted navigation features as one
+23 -3
View File
@@ -2,7 +2,7 @@
**Plugins → Web Services** is a submenu of catalog and service browsers. Each entry connects to one public (or self-hosted) data provider, searches it, and adds what you pick to the map as a normal GeoLibre layer.
They are grouped together because they behave the same way, not because they share a data source: every one of them opens a **docked side panel** rather than a floating on-map control, so it sits alongside the Layers and Style panels, resizes with them, and can be collapsed. That is also why these entries have no "position" submenu — unlike most plugins, there is no on-map control to place in a corner.
They are grouped together because they behave the same way, not because they share a data source: every one of them opens a **docked side panel** rather than a floating on-map control, so it sits alongside the Layers and Style panels, resizes with them, and can be collapsed. That is also why these entries have no "position" submenu: there is no on-map control to place in a corner.
![The Plugins menu with the Web Services submenu open, listing the catalog and service browsers](https://assets.geolibre.app/images/web-services-menu.webp)
@@ -10,8 +10,8 @@ They are grouped together because they behave the same way, not because they sha
- **Activating** an entry opens its panel; closing the panel deactivates the plugin. A check mark next to **Web Services** in the Plugins menu means at least one of them is active.
- **Layers you add are real layers.** Whatever a panel puts on the map is mirrored into the GeoLibre layer store, so it appears in the [Layers panel](layers.md), can be reordered, hidden, restyled, and removed there, and is saved into the `.geolibre.json` [project file](projects.md). Reopening the project restores the layer and hands it back to its panel.
- **The catalog browsers are mutually exclusive.** STAC Catalogs and Planet Open Data share panel state, so activating one deactivates the other; if the switch fails, the plugin that was displaced comes back.
- **Nothing here needs an account** except Hugging Face uploads (a user access token) and GeoLens private datasets (an API key).
- **The catalog browsers are mutually exclusive.** STAC Catalogs, Planet Open Data, and Portolan share panel state, so activating one deactivates the other; if the switch fails, the plugin that was displaced comes back.
- **Nothing here needs an account** except ArcGIS Portal (an ArcGIS sign-in), private S3 buckets (an S3 connection), Hugging Face uploads (a user access token), and GeoLens private datasets (an API key).
## At a glance
@@ -23,11 +23,15 @@ They are grouped together because they behave the same way, not because they sha
| [USGS National Map](#usgs-national-map) | USGS | Topo, imagery, hydrography, elevation, and index services |
| [USGS NLDI](#usgs-nldi) | USGS | Flowline tracing, hydrolocation, basins, and network navigation |
| [USGS 3DEP](#usgs-3dep) | USGS | 3DEP digital elevation models (1 m, 1/3 and 1 arc-second, and more) |
| [USGS LiDAR](#usgs-lidar) | USGS | 3DEP LiDAR point clouds clipped to an area of interest, as COPC |
| [Vantor Open Data](#vantor-open-data) | Vantor | Disaster-event satellite imagery (COG) |
| [Planet Open Data](#planet-open-data) | Planet Labs | Planet's disaster data releases, through the STAC browser |
| [Portolan](#portolan) | Portolan Registry | Registered geospatial catalogs, or a publisher's catalog URL, through the STAC browser |
| [Earthdata GIS](#earthdata-gis) | NASA EOSDIS | ArcGIS image, map, and feature services, and published web maps |
| [OpenAerialMap](#openaerialmap) | OpenAerialMap | Openly licensed drone and aerial imagery |
| [OSM Downloader](#osm-downloader) | OpenStreetMap / Overpass | Buildings, roads, amenities, waterways, land use, or custom OSM tags |
| [IGN LiDAR HD](#ign-lidar-hd) | IGN (France) | LiDAR HD tile coverage and COPC point clouds |
| [ArcGIS Portal](#arcgis-portal) | Esri | The content of your ArcGIS Online organization or Enterprise portal, after signing in |
| [ArcGIS Hub](#arcgis-hub) | Esri | Public datasets published to ArcGIS Hub |
| [Tennessee GIS](#tennessee-gis) | State of Tennessee | The geodata.tn.gov open GIS data portal |
| [US Federal GIS](#us-federal-gis) | US federal agencies | The public GIS portals of 24 federal agencies, from the Census Bureau and NOAA to USGS and FEMA |
@@ -108,6 +112,10 @@ Searches The National Map for [3D Elevation Program](https://www.usgs.gov/3d-ele
- Footprints render on the map and in the Layers panel; click one to select its result, or export them all to GeoJSON.
- **Load on Map** streams a GeoTIFF DEM through GeoLibre's raster path; **Download** saves the source file (IMG products are download-only).
## USGS LiDAR
Clips a USGS [3D Elevation Program](https://www.usgs.gov/3d-elevation-program) LiDAR point cloud to an area of interest and downloads the result as COPC. While the panel is open, a 3DEP Elevation Index layer shows where point clouds exist, and the map switches to the Mercator projection (your previous projection is restored when you close it), since the streamed point cloud does not render on the globe.
## Vantor Open Data
A STAC explorer for [Vantor's](https://www.vantor.com/) open disaster imagery releases.
@@ -121,6 +129,10 @@ A STAC explorer for [Vantor's](https://www.vantor.com/) open disaster imagery re
The same panel as [STAC Catalogs](#stac-catalogs), pinned to [Planet Labs PBC's](https://www.planet.com/disasterdata/) continuously updated disaster data releases so the catalog is already selected when it opens. Everything below about searching, filtering, and adding assets applies here too.
## Portolan
The same panel as [STAC Catalogs](#stac-catalogs), opened on the public [Portolan Registry](https://github.com/portolan-sdi/portolan-registry) of catalogs. Pick a registered catalog, or enter a publisher's catalog URL to connect to it directly, then add assets as in STAC Catalogs.
## Earthdata GIS
Searches NASA's [Earthdata GIS portal](https://gis.earthdata.nasa.gov) — the ArcGIS services EOSDIS publishes for its DAACs and disaster responses.
@@ -153,6 +165,14 @@ Public Overpass instances are intended for bounded interactive queries. Zoom to
The panel identifies the source as © OpenStreetMap contributors and notes the Open Database License (ODbL); keep the required attribution when publishing derived maps or data.
## IGN LiDAR HD
Searches the tile coverage of France's [IGN LiDAR HD](https://geoservices.ign.fr/lidarhd) program and adds its COPC point clouds.
- **Use map extent** and **Search tiles** list the LiDAR HD tiles in the area; their footprints are drawn as one entry in the Layers panel, and hovering a tile highlights its footprint.
- **Add to map** streams a tile as a point cloud layer (MapLibre, Mapbox, or ArcGIS renderer), and **Download** saves the COPC file. Check several tiles and choose **Add selected to map** to add them in one go.
- The data is © IGN, published under the Licence Ouverte 2.0.
## ArcGIS Portal
Browses the content of your own ArcGIS Online organization or ArcGIS Enterprise
+1 -1
View File
@@ -169,7 +169,7 @@
"description": "The web-map tile grid (z/x/y tile codes) over the current view; identify a cell and export the grid or selection."
},
"maplibre-gl-basemap-control": {
"description": "A basemap gallery for switching the background map, from the same catalog as the Change basemap dialog."
"description": "A basemap gallery for switching the background map, from the same catalog as the Change background dialog."
},
"maplibre-gl-esri-wayback": {
"description": "Browse historical aerial and satellite imagery (Esri World Imagery Wayback) for a location."