GeoLeaf — Plugin configuration in a profile
Applies to: @geoleaf/core v3.x
Overview
Some configuration keys in profile.json and ui.json only take effect once the matching plugin is loaded. Without the plugin, the key is read without error and silently ignored. This design makes it possible to declare configurations up front and load plugins optionally, depending on the environment.
| Profile key | Required plugin | Effect once the plugin is loaded |
|---|---|---|
ui.showCacheButton | @geoleaf-plugins/offline-ui | Shows the offline cache management button |
modules.editor.showAddPoi | @geoleaf-plugins/editor | Shows the add-POI button |
modules.offline | (in-core engine; its interface is @geoleaf-plugins/offline-ui) | Configures the offline cache (tiles, profile, declared origins) |
layer.attributes.fields[].edit | @geoleaf-plugins/editor | Makes the field editable, per layer |
| (no key) | @geoleaf-plugins/connector | fetch interceptor — enabled on import |
| (no key) | @geoleaf-plugins/file-import | Enables the GeoLeaf.FileImport.* API |
| (no key) | @geoleaf-plugins/flatgeobuf | Enables the GeoLeaf.FlatGeobuf.* API |
| (no key) | @geoleaf-plugins/cog | Enables the GeoLeaf.COG.* API |
| (no key) | @geoleaf-plugins/websocket | Real-time connection over WebSocket (GeoLeaf.Ws) |
| (no key) | @geoleaf-plugins/realtime-layer | Auto-refreshing layers |
modules.geocoding | @geoleaf-plugins/geocoding | Address search pill |
modules.measure | @geoleaf-plugins/measure | Measurement tools in the toolbar |
modules.print | @geoleaf-plugins/print | Print / export button |
modules.routing | @geoleaf-plugins/routing | Itinerary panel |
modules.navigation | @geoleaf-plugins/navigation | Guidance along a computed itinerary |
modules.position-share | @geoleaf-plugins/position-share | Shares the user's position — off by default |
modules.isochrone | @geoleaf-plugins/isochrone | Reachable-zone window in the toolbar |
Plugin Storage — @geoleaf-plugins/offline-ui
Enable the cache button
In ui.json → the ui section:
{
"ui": {
"showCacheButton": true
}
}Without this flag set to true, the button does not appear, even when the plugin is loaded.
The modules.offline block — config/plugins/offline.json
Configures the offline cache. The file is referenced by Files.modules.offline in profile.json, and its content becomes modules.offline. The offline engine is in the core, opt-in, and requires modules.pwa.enabled; this plugin provides its interface. The engine also holds the queue of edits made offline: what a layer declares for it, and how the queue drains, is the offline write cycle.
{
"enabled": true,
"cache": {
"enableProfileCache": true,
"enableTileCache": true
},
"dataOrigins": [
{
"origin": "https://tiles.example.com",
"roles": ["tiles"],
"cacheable": true,
"prefetch": true
}
]
}| Key | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Loads the offline engine (requires modules.pwa.enabled) |
cache.enableProfileCache | boolean | true | Enables the profile download — configuration files and layer data |
cache.enableTileCache | boolean | true | Downloads the tiles of offline basemaps. A veto: false wins over any selection |
dataOrigins | array | [] | Declared origins — prefetch: true lets the preparation download tiles from that origin; once one is declared, a layer's entities, its direct url, its fetchOptions lists and the sprite come only from a declared origin or the page's own |
banner.enabled | boolean | true | Mounts the sync bar at the top of the application. false mounts nothing; the queue is unaffected |
drain.pollIntervalMs | number | 60000 | Period of the queue's retry tick, in ms. 0 turns it off: an entry waiting after a failure then waits for the network to come back or the tab to be shown again |
⚠️ A tile from another origin is only downloaded when that origin is declared
cacheable: trueandprefetch: true. Downloading ahead of use is not viewing, and several free providers forbid it — OpenStreetMap: « Offline use is not permitted on tile.openstreetmap.org ». Declare an origin only if you operate it or its terms allow it. Tiles served by the application's own origin need no declaration — but a declaration prevails: once that origin is declared (for a data API it also serves, say), its tiles are prepared only if the declaration saysprefetch: true. A basemap refused by this rule is skipped, with a warning that names its origin.GeoLeaf.Storage.prefetchVerdict(url)answers the same rule before the download, with the reason of a refusal, and a download reports what it left out inpreparation.refusedOrigins.The network indicator is no longer part of this block: it is
modules.pwa.offlineDetector.enabled.
Per-layer cache (in basemaps.json)
Cache configuration can be refined for each basemap:
{
"basemaps": {
"own-tiles": {
"label": "Our tile server",
"type": "tile",
"url": "https://tiles.example.com/{z}/{x}/{y}.png",
"offline": true,
"cacheMinZoom": 8,
"cacheMaxZoom": 16,
"offlineBounds": {
"north": 48.9,
"south": 48.8,
"east": 2.4,
"west": 2.3
}
}
}
}| Key | Description |
|---|---|
offline | Makes the basemap downloadable — from a declared origin (above) |
cacheMinZoom | Lowest zoom level to pre-fetch |
cacheMaxZoom | Highest zoom level to pre-fetch |
offlineBounds | Geographic extent to cache (north, south, east, west) |
Plugin Editor — @geoleaf-plugins/editor
Enable the add button
In config/plugins/editor.json — the file holds the modules.editor block itself:
{
"showAddPoi": true
}WARNING
This key used to live under ui.showAddPoi. It has been removed from the ui schema (additionalProperties: false), so writing it there now makes npm run validate:profiles fail. Its default has changed as well: false (opt-in) became true (opt-out).
The poiAddConfig block in profile.json — REMOVED
poiAddConfig block in profile.jsonDANGER
This block no longer exists, and copying it makes npm run validate:profiles FAIL. It configured the POI add form of the addpoi plugin, since merged into @geoleaf-plugins/editor. profile.schema.json is additionalProperties: false and declares only ten root keys ($schema, id, label, displayLabel, icon, description, version, Files, map, modules): an unknown root key is rejected, not ignored.
| Old | New |
|---|---|
poiAddConfig.enabled | no key any more — the plugin is loaded lazily when the button is pressed |
poiAddConfig.defaultPosition | modules.editor.poiAddDefaultPosition (in config/plugins/editor.json) |
Plugin Connector — @geoleaf-plugins/connector
Installation
npm install @geoleaf-plugins/connectorUsage
import "@geoleaf/core";
import "@geoleaf-plugins/connector"; // mounts GeoLeaf.Connector — nothing is intercepted before configure()
// Mode 1 — the page owns the session and hands the token over at each request
await GeoLeaf.Connector.configure({
baseUrl: "https://api.example.com",
getToken: async () => identityClient.currentToken(),
});
// Mode 2 — a login route on the server, with the plugin's login window
await GeoLeaf.Connector.configure({
baseUrl: "https://api.example.com",
auth: { endpoint: "https://api.example.com/auth/login", ui: true },
});Configuration parameters
| Parameter | Type | Description |
|---|---|---|
baseUrl | string | Requests under this URL carry Authorization: Bearer <token>. HTTPS; http:// is tolerated only when the URL itself targets a development host — localhost, *.localhost, the loopback addresses, *.test — whatever host serves the page |
getToken | function | Mode 1 — returns the token, or a promise of it, at each request; null sends none |
auth.endpoint | string | Mode 2 — the login route: POST { login, password } answered by { token, expiresIn }; renewal at {endpoint}/refresh. Held to the same HTTPS rule as baseUrl: it receives the password |
auth.ui | boolean | Mode 2 — the plugin renders the login window |
⚠️
getTokenandauthare mutually exclusive, and there is no third mode. An API key is handed over bygetTokenand travels asAuthorization: Bearer <key>; no option changes the header. What the server must answer — sign-in, renewal, every status — is written once, in §3 of the server contract. A server that speaks another dialect — an OAuth 2 token endpoint, a key in a header of its own — goes throughgetTokenwith its own client, or through a thin adapter on its side: §7.6 of the security guide.This section used to document
auth.type("bearer"/"apikey"/"cookie"),auth.tokenandauth.headerName. None of these options has ever existed: such a configuration fails withauth.endpoint must be a non-empty string.
Plugin File Import — @geoleaf-plugins/file-import
Status: available (~70 KB gzip)
Imports geographic files on the client side (GPX, KML/KMZ, CSV, TopoJSON) and converts them to GeoJSON. Imported data can be displayed directly as a map layer.
Installation
npm install @geoleaf-plugins/file-importMain API
import "@geoleaf/core";
import "@geoleaf-plugins/file-import";
// Convert a File object into GeoJSON — the format is detected from the file extension
const { data, warnings } = await GeoLeaf.FileImport.convert(file);
// Import and display as a map layer — resolves to the id of the layer
const layerId = await GeoLeaf.FileImport.importAsLayer(file, {
layerId: "imported-data",
layerName: "Imported data",
});Supported formats
| Format | Extension | Notes |
|---|---|---|
| GPX | .gpx | Tracks, routes, waypoints → GeoJSON |
| KML/KMZ | .kml, .kmz | KMZ is decompressed automatically |
| CSV | .csv | Configurable lat/lon columns |
| TopoJSON | .json, .topojson | Automatic conversion → GeoJSON |
importAsLayer options
| Option | Type | Default | Description |
|---|---|---|---|
layerId | string | generated | Identifier of the resulting layer |
style | object | default | MapLibre GL JS style applied to the layer |
type | string | "auto" | Forces the format ("gpx", "kml", "csv", and so on) |
Plugin FlatGeobuf — @geoleaf-plugins/flatgeobuf
Status: available (~20 KB gzip)
Streaming load of FlatGeobuf files with spatial bounding-box filtering (HTTP Range + R-tree index). Suited to large datasets served without an intermediate server.
Installation
npm install @geoleaf-plugins/flatgeobufDeclarative configuration in a layer config
Each FlatGeobuf layer is declared in a <layer>_config.json file with "plugin": "flatgeobuf". The entry in layers.json is identical to any other layer.
Schema of the data block:
| Key | Type | Default | Description |
|---|---|---|---|
url | string | — | URL of the .fgb file (relative to the profile root, or absolute) |
bbox | [W, S, E, N] (4 numbers) | absent | Spatial filter: only features inside the bbox are transferred over HTTP Range |
limit | number | 100 000 | Maximum number of features to load (anti-DoS protection) |
autoRefresh | boolean | false | Reloads the features on every map move (moveend) |
debounceMs | number | 300 | Auto-refresh debounce delay, in ms |
Example — layer with bbox and auto-refresh (zones_desserte):
{
"id": "zones_desserte",
"label": "Zones de desserte SNCF",
"plugin": "flatgeobuf",
"zIndex": 30,
"geometry": "polygon",
"data": {
"url": "data/zones_desserte_sncf.fgb",
"bbox": [2.225, 41.362, 8.227, 51.089],
"limit": 1000,
"autoRefresh": true,
"debounceMs": 500
},
"styles": {
"directory": "styles",
"default": "defaut.json",
"available": [{ "id": "defaut", "label": "défaut", "file": "defaut.json" }]
},
"table": { "enabled": false },
"clustering": { "enabled": false }
}Example — local file layer without bbox (eco_regions_fgb):
{
"id": "eco_regions_fgb",
"label": "Éco-régions (FlatGeobuf)",
"plugin": "flatgeobuf",
"zIndex": 51,
"geometry": "polygon",
"data": {
"url": "layers/eco_regions_fgb/data/eco_regions.fgb",
"limit": 50000,
"autoRefresh": false
}
}Loading from initialisation code
import "@geoleaf/core";
import "@geoleaf-plugins/flatgeobuf";
// Read the config from the profile, then load the layer:
const layerId = await GeoLeaf.FlatGeobuf.loadLayerFromConfig(layerConfig);
// If layerConfig.data.bbox is set → loadBboxAsLayer (HTTP Range)
// Otherwise → loadAsLayer (whole file)Low-level API
// Full load → GeoJSON FeatureCollection
const result = await GeoLeaf.FlatGeobuf.load("https://example.com/data.fgb");
// Load by bbox (HTTP Range — only the features inside the bbox are downloaded)
const result = await GeoLeaf.FlatGeobuf.loadBbox("https://example.com/data.fgb", {
minX: 2.2,
minY: 48.8,
maxX: 2.5,
maxY: 49.0,
});
// Add straight to the map as a layer
const layerId = await GeoLeaf.FlatGeobuf.loadAsLayer("https://example.com/data.fgb", {
layerId: "ma-couche",
layerName: "My data",
visible: true,
});WARNING
Server prerequisite: the server hosting the .fgb files must support HTTP Range requests (Content-Range header) for bbox filtering to work. Nginx, Apache, Amazon S3 and GitHub Pages support it by default.
Plugin COG — @geoleaf-plugins/cog
Status: available (~156 KB gzip)
Reads and displays Cloud Optimized GeoTIFF (COG) directly in MapLibre GL JS. Supports multi-band images, LUT colour maps and custom raster source injection.
Installation
# public npm registry
npm install @geoleaf-plugins/cogMain API
import "@geoleaf/core";
import "@geoleaf-plugins/cog";
// The plugin draws on the native MapLibre map
const map = GeoLeaf.Core.getMap().getNativeMap();
// Add a COG layer to the map — resolves to a handle
const handle = await GeoLeaf.COG.addLayer("https://example.com/ortho.tif", map, {
id: "ortho",
bands: [1, 2, 3],
opacity: 0.85,
});
// Remove the layer
handle.remove();
// or, by id
GeoLeaf.COG.removeLayer(map, "ortho");addLayer options
| Option | Type | Default | Description |
|---|---|---|---|
id | string | generated | Identifier of the MapLibre layer and source |
bands | number[] | first three bands | Three bands for RGB, or one band rendered as grayscale (1-based) |
colorMap | [r, g, b, a][] | — | Lookup table of 256 RGBA entries, for single-band rendering |
opacity | number | 1 | Layer opacity (0–1) |
nodata | number | from file metadata | nodata value to mask out |
Plugin WebSocket — @geoleaf-plugins/websocket
WebSocket transport plugin: a real-time connection with automatic reconnection, offline buffering and built-in metrics, behind GeoLeaf.Ws. It reads no profile key — it is configured by GeoLeaf.Ws.init(). Its API and options are described in its README.
Plugin Realtime Layer — @geoleaf-plugins/realtime-layer
Status: available — MIT plugin for auto-refreshing layers.
This plugin enables auto-refreshing layers over HTTP polling, WebSocket or Server-Sent Events. The configuration lives in the data.realtime block of each <layer>_config.json.
Supported keys (data.realtime block)
| Key | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Starts the RT layer at boot. When false, the block stays descriptive. |
source | "polling" | "websocket" | "sse" | yes | Transport type. |
decoder | "json" | "gtfs-rt" | <custom> | yes | Decoder applied to the received payload. Custom decoders via registerDecoder(). |
url | string | when source = polling/sse | URL of the remote endpoint. |
intervalMs | number | no (default 30 000) | Polling period. Polling only. |
fallbackUrl | string | no | Fallback URL served when url returns a non-2xx status or fails (network). Polling only. The snapshot is emitted once per outage; the primary is used again on its first success. |
channel | string | when source = websocket | Channel consumed through GeoLeaf.Ws.subscribe(). |
updateMode | "upsert" | "replace" | "merge" | no (default "upsert") | Strategy used to apply updates. |
idField | string | required for upsert / merge | Property used as the stable feature identifier. |
staleTimeoutMs | number | no | Delay after which a feature that has not been refreshed becomes stale. |
staleAction | "remove" | "dim" | <custom> | no (default "remove") | Action applied to stale features. Custom actions via registerStaleAction(). |
mapping | object | no | Hints for the GTFS-RT decoder (idField, delayField, targetLayerId). |
Example — GeoJSON polling with a CDN fallback (USGS)
{
"id": "epicentres_seismes",
"data": {
"dataUrl": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_week.geojson",
"realtime": {
"enabled": true,
"source": "polling",
"url": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_week.geojson",
"intervalMs": 60000,
"decoder": "json",
"updateMode": "upsert",
"idField": "id",
"fallbackUrl": "data/epicentres_seismes_snapshot.geojson"
}
}
}Example — SNCF GTFS-RT with a protobuf fallback
{
"id": "gares_voyageurs",
"data": {
"dataUrl": "https://ressources.data.sncf.com/.../geojson",
"realtime": {
"enabled": true,
"source": "polling",
"url": "https://proxy.transport.data.gouv.fr/resource/sncf-gtfs-rt-trip-updates",
"intervalMs": 120000,
"decoder": "gtfs-rt",
"updateMode": "merge",
"idField": "code_uic",
"mapping": {
"idField": "stop_id",
"delayField": "delay",
"targetLayerId": "gares_voyageurs"
},
"staleTimeoutMs": 300000,
"staleAction": "dim",
"fallbackUrl": "data/gares_voyageurs_gtfsrt_snapshot.pb"
}
}
}Public API
GeoLeaf.RealtimeLayer.start(layerId: string): void;
GeoLeaf.RealtimeLayer.stop(layerId: string): void;
GeoLeaf.RealtimeLayer.stopAll(): void;
GeoLeaf.RealtimeLayer.getStatus(layerId: string): { active, source, lastUpdateAt, staleCount };
GeoLeaf.RealtimeLayer.registerDecoder(name: string, decoder: IDecoder): void;
GeoLeaf.RealtimeLayer.registerStaleAction(name: string, handler: StaleActionHandler): void;Layers with data.realtime.enabled: true start automatically on the geoleaf:app:ready event.
Example: a profile with both plugins enabled
profile.json
{
"id": "mon-profil",
"label": "Mon Profil",
"version": "1.0.0",
"map": {
"center": [48.8566, 2.3522],
"zoom": 12
},
"Files": {
"layersFile": "config/core/layers.json",
"basemapsFile": "config/core/basemaps.json",
"uiFile": "config/core/ui.json",
"modules": {
"cluster": "config/plugins/cluster.json",
"offline": "config/plugins/offline.json"
}
}
}config/plugins/offline.json carries the modules.offline block shown above.
ui.json
{
"ui": {
"theme": "auto",
"language": "en",
"showLayerManager": true,
"showFilterPanel": true,
"showLegend": true,
"enableGeolocation": true,
"showCacheButton": true,
"permalink": {
"enabled": true,
"mode": "hash"
}
},
"search": {
"title": "Filtrer",
"searchPlaceholder": "Rechercher...",
"filters": [
{
"id": "searchText",
"type": "search",
"label": "Recherche textuelle",
"placeholder": "Nom...",
"searchFields": ["properties.name"]
}
]
}
}Loading the plugins (ESM)
import "@geoleaf/core";
import "@geoleaf-plugins/connector"; // optional — when an authenticated API is required
import "@geoleaf-plugins/offline-ui"; // unlocks showCacheButton, the offline cache interface
import "@geoleaf-plugins/editor"; // unlocks modules.editor.* (POI editing and capture)
GeoLeaf.boot({
config: {
data: {
activeProfile: "mon-profil",
profilesBasePath: "./profiles/",
},
},
});WARNING
Import order: plugins must be imported after @geoleaf/core. See PLUGIN_DEVELOPMENT_GUIDE.md → section "Load order".
Table — modules.table (plugin @geoleaf-plugins/table)
INFO
The data table has been moved out of the core into the MIT plugin @geoleaf-plugins/table. See the plugin README for installation, configuration (modules.table.*) and migration.
The table is now the MIT plugin @geoleaf-plugins/table. Its configuration lives under modules.table.* (file config/plugins/table.json + Files.modules.table), no longer under the root key tableConfig. The keys below describe the table behaviour, in particular the available export formats. The GeoLeaf.Table.* API stays valid once the plugin is loaded.
modules.table keys
| Key | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enables or disables the table module entirely. |
defaultVisible | boolean | false | Opens the table panel when the profile loads. |
pageSize | number | — | Deprecated, inert. There is no pagination; the renderer windows by scroll. |
maxRowsPerLayer | number | 30000 | Rows kept per layer; the cut warns the user once per layer and per session, and 0 means zero. Does not affect Table.exportLayer(). |
enableExportButton | boolean | true | Shows the export buttons (selection + layer) in the toolbar. |
exportFormats | ('geojson'|'csv'|'kml'|'gpx'|'excel')[] | ["geojson", "csv", "kml", "gpx", "excel"] | Formats offered in the export dropdowns, in this order. |
csvSeparator | ',' | ';' | "," | Separator used for CSV export. Useful for Excel compatibility in locales that use ;. |
csvIncludeGeometry | boolean | false | Includes a __geometry column (WKT/GeoJSON) in the CSV export. |
resizable | boolean | true | Allows vertical resizing of the table panel. |
defaultHeight | string | "40%" | Initial height of the panel. |
minHeight | string | "20%" | Minimum height when resizing. |
maxHeight | string | "60%" | Maximum height when resizing. |
Example — restrict the formats and force the ; separator
{
"modules": {
"table": {
"exportFormats": ["geojson", "csv", "excel"],
"csvSeparator": ";",
"csvIncludeGeometry": false,
"maxRowsPerLayer": 10000
}
}
}Available export formats
| Format | Key | Extra weight | Notes |
|---|---|---|---|
| GeoJSON | geojson | 0 (already loaded) | Default format. |
| CSV | csv | ~0 KB | UTF-8 BOM, configurable separator, optional __geometry column. |
| KML | kml | ~0 KB | Native XML, no external dependency. Properties in CDATA. Works with Google Earth / QGIS. |
| GPX | gpx | ~0 KB | Native XML. <wpt> for Points, <trk> for LineStrings, <rte> for Polygons. |
| Excel (.xlsx) | excel | ~150 KB gzip | Loaded lazily (SheetJS) on the first click only — no impact on the initial bundle. |
Extended public API
// `GeoLeaf.Table` is mounted by the plugin: it is absent from a page that does not load it,
// hence the `?.`.
// Export the current selection
GeoLeaf.Table?.exportSelection(); // GeoJSON (default)
GeoLeaf.Table?.exportSelection("csv"); // CSV
GeoLeaf.Table?.exportSelection("csv", { csvSeparator: ";" }); // CSV with a custom separator
// Export the whole active layer (ignores the maxRowsPerLayer limit)
GeoLeaf.Table?.exportLayer(); // GeoJSON (default)
GeoLeaf.Table?.exportLayer("kml");
GeoLeaf.Table?.exportLayer("excel");Emitted events
The table emits ten events, all named geoleaf:table:… — two of them on export:
| Event | Payload | Trigger |
|---|---|---|
geoleaf:table:exportSelection | { layerId, format, selectedIds, rows } | Table.exportSelection() |
geoleaf:table:exportLayer | { layerId, format, count } | Table.exportLayer() |
The eight others, and their payloads, are listed in the Table guide.
Plugin Geocoding — @geoleaf-plugins/geocoding
Address search: the user types an address or a place name in a floating pill, and the plugin asks a provider — Addok / BAN by default, Nominatim, Photon, a custom HTTPS endpoint, or the map's own features (layers), alone or as an ordered list. Its configuration lives under modules.geocoding; the keys and the providers are described in its README.
Plugin Measure — @geoleaf-plugins/measure
Measurement tools: distances, surfaces (rectangle, circle, polygon), a GPS track and georeferenced annotations, exported as GeoJSON — and composed on the printed page when @geoleaf-plugins/print is loaded. Its configuration lives under modules.measure, every key optional; see its README.
Plugin Print — @geoleaf-plugins/print
Exports the map at a controlled scale to a paper format (A4, A3), as a PDF or a JPG at 300 DPI. Its configuration lives under modules.print, every key optional. The former root key printConfig is no longer read: a profile that still carries it gets the defaults. See its README.
Plugin Routing — @geoleaf-plugins/routing
Route computation between two or more points, from an itinerary panel that the « Route to here » action of a feature sheet opens. It asks an external engine — Valhalla by default, OSRM built in, others registered with GeoLeaf.Routing.registerProvider(). Its configuration lives under modules.routing; its toolbar entry point is hidden unless showButton is true. See its README.
Plugin Navigation — @geoleaf-plugins/navigation
Turn-by-turn guidance along a route computed by @geoleaf-plugins/routing: it follows the user's position, announces the next manoeuvre and recomputes when the user leaves the path. Guidance is entered from the itinerary panel of routing. Its configuration lives under modules.navigation; see its README.
Plugin Position Share — @geoleaf-plugins/position-share
Sends the user's GPS position to a server over an interchangeable transport — http, websocket, or a registered one — and can show where the other users are. It is off until the profile turns it on: modules.position-share.enabled defaults to false. See its README.
Plugin Isochrone — @geoleaf-plugins/isochrone
The zones reachable from a point within a given time, asked of a routing engine and stacked on the map from a floating window. Its configuration lives under modules.isochrone — modes, durations, rank of the zones, palette. 🛑 No engine key is ever written in a profile: a profile is a served file, and a key is given at runtime with GeoLeaf.Isochrone.configure(). See its README.
Core rule: silent degradation
Only keys actually declared in the schema degrade silently. poiAddConfig and ui.showAddPoi are no longer declared; fixed-shape objects are additionalProperties: false, so writing them makes npm run validate:profiles fail instead of being ignored. Of the keys listed above, ui.showCacheButton is the one still declared.
Plugins with no profile key (connector, file-import, flatgeobuf, cog) need no JSON configuration at all — they activate on import. When the matching plugin is not loaded:
- No error is raised
- The key is read and ignored
- The button or the feature does not appear
This behaviour is intentional: it allows a single profile to serve several environments, with or without the optional plugins.
Check plugin status at runtime
// List of loaded plugins
GeoLeaf.plugins.getLoadedPlugins();
// → ["core", "connector", "offline-ui", "editor", "file-import", "flatgeobuf", "cog"]
// Check one specific plugin
GeoLeaf.plugins.isLoaded("offline-ui"); // → true / false
GeoLeaf.plugins.isLoaded("file-import"); // → true / false
GeoLeaf.plugins.isLoaded("flatgeobuf"); // → true / false
GeoLeaf.plugins.isLoaded("cog"); // → true / falseSee also
- PLUGIN_DEVELOPMENT_GUIDE.md — the plugin pattern, for contributors to the repository (plugins are first-party)
CONNECTOR_GUIDE.md— HTTP authentication with@geoleaf-plugins/connector. The guide ships with the plugin package (docs/CONNECTOR_GUIDE.mdof@geoleaf-plugins/connector), not with the core: it documents the plugin- PROFILE_JSON_REFERENCE.md — clustering configuration (
modules.cluster); thepoiConfigkey was removed in v3 - ui/PERMALINK.md — permalink configuration
- GETTING_STARTED.html — quick-start guide
