Skip to content

Commit 3ea9b6a

Browse files
authored
feat: add onExport handler so hosts can save GeoJSON exports (#5)
* feat: add onExport handler so hosts can save GeoJSON exports The layer export button downloads GeoJSON via an anchor element, which works in browsers but not in desktop webviews (e.g. Tauri) that have no download manager, so the button appears to do nothing there. Add an optional `onExport(filename, data)` control option. When set, the control delegates the export to it instead of triggering the browser download, letting a host application save the file with its own dialog. Default behavior (browser download) is unchanged when no handler is set. * Address CodeRabbit review feedback - Guard the host onExport callback in exportLayer with try/catch: a throwing host handler no longer breaks the click path. On failure we surface a notice, set the error state, and return null so UI feedback stays consistent.
1 parent 6967bb2 commit 3ea9b6a

5 files changed

Lines changed: 41 additions & 6 deletions

File tree

geolibre-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "maplibre-gl-overture-maps",
33
"name": "MapLibre GL Overture Maps",
4-
"version": "0.2.0",
4+
"version": "0.3.0",
55
"entry": "dist/index.js",
66
"description": "Visualize Overture Maps PMTiles themes with a MapLibre control.",
77
"style": "dist/style.css"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "maplibre-gl-overture-maps",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"description": "A MapLibre GL JS plugin for visualizing Overture Maps PMTiles themes",
55
"type": "module",
66
"main": "./dist/index.cjs",

src/geolibre.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function isOvertureMapsState(
8282
export const plugin: GeoLibrePlugin = {
8383
id: "maplibre-gl-overture-maps",
8484
name: "MapLibre GL Overture Maps",
85-
version: "0.2.0",
85+
version: "0.3.0",
8686
activate(app) {
8787
control = control ?? createControl();
8888
const added = app.addMapControl(control, position);

src/lib/core/OvertureMapsControl.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,15 @@ import { ensurePmtilesProtocol } from './pmtilesProtocol';
3737
* Default options for the OvertureMapsControl
3838
*/
3939
const DEFAULT_OPTIONS: Required<
40-
Omit<OvertureMapsControlOptions, 'release' | 'themeColors' | 'themeOpacity'>
40+
Omit<
41+
OvertureMapsControlOptions,
42+
'release' | 'themeColors' | 'themeOpacity' | 'onExport'
43+
>
4144
> &
42-
Pick<OvertureMapsControlOptions, 'release' | 'themeColors' | 'themeOpacity'> = {
45+
Pick<
46+
OvertureMapsControlOptions,
47+
'release' | 'themeColors' | 'themeOpacity' | 'onExport'
48+
> = {
4349
collapsed: true,
4450
position: 'top-right',
4551
title: 'Overture Maps',
@@ -54,6 +60,7 @@ const DEFAULT_OPTIONS: Required<
5460
visibleThemes: ['buildings', 'transportation', 'places'],
5561
themeColors: undefined,
5662
themeOpacity: undefined,
63+
onExport: undefined,
5764
};
5865

5966
const DEFAULT_OPACITY = 0.8;
@@ -700,7 +707,23 @@ export class OvertureMapsControl implements IControl {
700707
return null;
701708
}
702709

703-
this._downloadGeoJSON(`overture-${theme}-${sourceLayer}.geojson`, collection);
710+
const filename = `overture-${theme}-${sourceLayer}.geojson`;
711+
if (this._options.onExport) {
712+
// Let the host save the file (e.g. a native dialog in a desktop webview
713+
// where anchor-based downloads do not work). Guard against a host
714+
// handler throwing so the click path stays consistent.
715+
try {
716+
this._options.onExport(filename, collection);
717+
} catch (error) {
718+
this._notify(`Failed to export ${label}.`);
719+
this._setError(
720+
`Export failed (${error instanceof Error ? error.message : 'unknown error'}).`
721+
);
722+
return null;
723+
}
724+
} else {
725+
this._downloadGeoJSON(filename, collection);
726+
}
704727
const count = collection.features.length;
705728
this._notify(`Exported ${count} ${label} feature${count === 1 ? '' : 's'}.`);
706729
return collection;

src/lib/core/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FeatureCollection } from 'geojson';
12
import type { Map } from 'maplibre-gl';
23
import type { OvertureTheme } from './themes';
34

@@ -96,6 +97,17 @@ export interface OvertureMapsControlOptions {
9697
* @default 0.8 for every theme
9798
*/
9899
themeOpacity?: Partial<Record<OvertureTheme, number>>;
100+
101+
/**
102+
* Custom handler for exporting a layer to GeoJSON. When provided, it is
103+
* called instead of the built-in browser download, letting a host
104+
* application save the file its own way (e.g. a native save dialog in a
105+
* desktop webview where anchor downloads do not work).
106+
*
107+
* @param filename - The suggested file name (e.g. `overture-buildings-building.geojson`)
108+
* @param data - The exported FeatureCollection
109+
*/
110+
onExport?: (filename: string, data: FeatureCollection) => void;
99111
}
100112

101113
/**

0 commit comments

Comments
 (0)