Skip to content

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 keyRequired pluginEffect once the plugin is loaded
ui.showCacheButton@geoleaf-plugins/offline-uiShows the offline cache management button
modules.editor.showAddPoi@geoleaf-plugins/editorShows 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/editorMakes the field editable, per layer
(no key)@geoleaf-plugins/connectorfetch interceptor — enabled on import
(no key)@geoleaf-plugins/file-importEnables the GeoLeaf.FileImport.* API
(no key)@geoleaf-plugins/flatgeobufEnables the GeoLeaf.FlatGeobuf.* API
(no key)@geoleaf-plugins/cogEnables the GeoLeaf.COG.* API
(no key)@geoleaf-plugins/websocketReal-time connection over WebSocket (GeoLeaf.Ws)
(no key)@geoleaf-plugins/realtime-layerAuto-refreshing layers
modules.geocoding@geoleaf-plugins/geocodingAddress search pill
modules.measure@geoleaf-plugins/measureMeasurement tools in the toolbar
modules.print@geoleaf-plugins/printPrint / export button
modules.routing@geoleaf-plugins/routingItinerary panel
modules.navigation@geoleaf-plugins/navigationGuidance along a computed itinerary
modules.position-share@geoleaf-plugins/position-shareShares the user's position — off by default
modules.isochrone@geoleaf-plugins/isochroneReachable-zone window in the toolbar

Plugin Storage — @geoleaf-plugins/offline-ui ​

Enable the cache button ​

In ui.json → the ui section:

json
{
    "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.

json
{
    "enabled": true,
    "cache": {
        "enableProfileCache": true,
        "enableTileCache": true
    },
    "dataOrigins": [
        {
            "origin": "https://tiles.example.com",
            "roles": ["tiles"],
            "cacheable": true,
            "prefetch": true
        }
    ]
}
KeyTypeDefaultDescription
enabledbooleanfalseLoads the offline engine (requires modules.pwa.enabled)
cache.enableProfileCachebooleantrueEnables the profile download — configuration files and layer data
cache.enableTileCachebooleantrueDownloads the tiles of offline basemaps. A veto: false wins over any selection
dataOriginsarray[]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.enabledbooleantrueMounts the sync bar at the top of the application. false mounts nothing; the queue is unaffected
drain.pollIntervalMsnumber60000Period 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: true and prefetch: 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 says prefetch: 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 in preparation.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:

json
{
    "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
            }
        }
    }
}
KeyDescription
offlineMakes the basemap downloadable — from a declared origin (above)
cacheMinZoomLowest zoom level to pre-fetch
cacheMaxZoomHighest zoom level to pre-fetch
offlineBoundsGeographic 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:

json
{
    "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 ​

DANGER

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.

OldNew
poiAddConfig.enabledno key any more — the plugin is loaded lazily when the button is pressed
poiAddConfig.defaultPositionmodules.editor.poiAddDefaultPosition (in config/plugins/editor.json)

Plugin Connector — @geoleaf-plugins/connector ​

Installation ​

bash
npm install @geoleaf-plugins/connector

Usage ​

js
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 ​

ParameterTypeDescription
baseUrlstringRequests 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
getTokenfunctionMode 1 — returns the token, or a promise of it, at each request; null sends none
auth.endpointstringMode 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.uibooleanMode 2 — the plugin renders the login window

⚠️ getToken and auth are mutually exclusive, and there is no third mode. An API key is handed over by getToken and travels as Authorization: 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 through getToken with 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.token and auth.headerName. None of these options has ever existed: such a configuration fails with auth.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 ​

bash
npm install @geoleaf-plugins/file-import

Main API ​

js
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 ​

FormatExtensionNotes
GPX.gpxTracks, routes, waypoints → GeoJSON
KML/KMZ.kml, .kmzKMZ is decompressed automatically
CSV.csvConfigurable lat/lon columns
TopoJSON.json, .topojsonAutomatic conversion → GeoJSON

importAsLayer options ​

OptionTypeDefaultDescription
layerIdstringgeneratedIdentifier of the resulting layer
styleobjectdefaultMapLibre GL JS style applied to the layer
typestring"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 ​

bash
npm install @geoleaf-plugins/flatgeobuf

Declarative 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:

KeyTypeDefaultDescription
urlstring—URL of the .fgb file (relative to the profile root, or absolute)
bbox[W, S, E, N] (4 numbers)absentSpatial filter: only features inside the bbox are transferred over HTTP Range
limitnumber100 000Maximum number of features to load (anti-DoS protection)
autoRefreshbooleanfalseReloads the features on every map move (moveend)
debounceMsnumber300Auto-refresh debounce delay, in ms

Example — layer with bbox and auto-refresh (zones_desserte):

json
{
    "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):

json
{
    "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 ​

js
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 ​

js
// 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 ​

bash
# public npm registry
npm install @geoleaf-plugins/cog

Main API ​

js
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 ​

OptionTypeDefaultDescription
idstringgeneratedIdentifier of the MapLibre layer and source
bandsnumber[]first three bandsThree 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
opacitynumber1Layer opacity (0–1)
nodatanumberfrom file metadatanodata 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) ​

KeyTypeRequiredDescription
enabledbooleanyesStarts the RT layer at boot. When false, the block stays descriptive.
source"polling" | "websocket" | "sse"yesTransport type.
decoder"json" | "gtfs-rt" | <custom>yesDecoder applied to the received payload. Custom decoders via registerDecoder().
urlstringwhen source = polling/sseURL of the remote endpoint.
intervalMsnumberno (default 30 000)Polling period. Polling only.
fallbackUrlstringnoFallback 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.
channelstringwhen source = websocketChannel consumed through GeoLeaf.Ws.subscribe().
updateMode"upsert" | "replace" | "merge"no (default "upsert")Strategy used to apply updates.
idFieldstringrequired for upsert / mergeProperty used as the stable feature identifier.
staleTimeoutMsnumbernoDelay 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().
mappingobjectnoHints for the GTFS-RT decoder (idField, delayField, targetLayerId).

Example — GeoJSON polling with a CDN fallback (USGS) ​

json
{
    "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 ​

json
{
    "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 ​

ts
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 ​

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 ​

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) ​

js
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 ​

KeyTypeDefaultDescription
enabledbooleantrueEnables or disables the table module entirely.
defaultVisiblebooleanfalseOpens the table panel when the profile loads.
pageSizenumber—Deprecated, inert. There is no pagination; the renderer windows by scroll.
maxRowsPerLayernumber30000Rows kept per layer; the cut warns the user once per layer and per session, and 0 means zero. Does not affect Table.exportLayer().
enableExportButtonbooleantrueShows 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 ;.
csvIncludeGeometrybooleanfalseIncludes a __geometry column (WKT/GeoJSON) in the CSV export.
resizablebooleantrueAllows vertical resizing of the table panel.
defaultHeightstring"40%"Initial height of the panel.
minHeightstring"20%"Minimum height when resizing.
maxHeightstring"60%"Maximum height when resizing.

Example — restrict the formats and force the ; separator ​

json
{
    "modules": {
        "table": {
            "exportFormats": ["geojson", "csv", "excel"],
            "csvSeparator": ";",
            "csvIncludeGeometry": false,
            "maxRowsPerLayer": 10000
        }
    }
}

Available export formats ​

FormatKeyExtra weightNotes
GeoJSONgeojson0 (already loaded)Default format.
CSVcsv~0 KBUTF-8 BOM, configurable separator, optional __geometry column.
KMLkml~0 KBNative XML, no external dependency. Properties in CDATA. Works with Google Earth / QGIS.
GPXgpx~0 KBNative XML. <wpt> for Points, <trk> for LineStrings, <rte> for Polygons.
Excel (.xlsx)excel~150 KB gzipLoaded lazily (SheetJS) on the first click only — no impact on the initial bundle.

Extended public API ​

ts
// `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:

EventPayloadTrigger
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 ​

js
// 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 / false

See 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.md of @geoleaf-plugins/connector), not with the core: it documents the plugin
  • PROFILE_JSON_REFERENCE.md — clustering configuration (modules.cluster); the poiConfig key was removed in v3
  • ui/PERMALINK.md — permalink configuration
  • GETTING_STARTED.html — quick-start guide

Released under the MIT License.