From e17ee9cc20194de13b99c63232596801c943a9f2 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 13 Feb 2026 19:01:58 +0100 Subject: [PATCH 1/5] refactor(maps): migrate from Leaflet to MapLibre GL JS Replace Leaflet + leaflet-gpx with MapLibre GL JS for WebGL-based rendering, vector tile support, and PMTiles support. - Add GPX parsing helper using @tmcw/togeojson with haversine distance and elevation gain/loss calculation - Support raster tiles (default OSM), PMTiles with lazy protocol registration, and full style override via mapStyle config - Add configurable tile attribution via tileLayerAttribution - Fix bug where imperial units were displayed with metric labels - Update CSP to allow MapLibre web workers and tile fetching - Update e2e tests with new selectors and additional coverage --- dev/docker/csp.yaml | 6 + packages/web-app-maps/README.md | 62 +++- packages/web-app-maps/leaflet.d.ts | 21 -- packages/web-app-maps/package.json | 8 +- .../web-app-maps/src/components/GpxMap.vue | 117 ++++--- .../src/components/LocationFolderView.vue | 17 +- .../src/components/LocationPanel.vue | 17 +- .../web-app-maps/src/composables/index.ts | 2 +- .../src/composables/useLeaflet.ts | 61 ---- .../web-app-maps/src/composables/useMap.ts | 116 +++++++ .../src/composables/useMapPins.ts | 32 +- packages/web-app-maps/src/helpers/gpx.ts | 111 +++++++ packages/web-app-maps/src/index.ts | 3 +- packages/web-app-maps/src/styles.css | 8 +- packages/web-app-maps/src/types.ts | 3 +- .../web-app-maps/tests/e2e/app-maps.spec.ts | 68 ++++- pnpm-lock.yaml | 286 +++++++++++++++--- 17 files changed, 715 insertions(+), 223 deletions(-) delete mode 100644 packages/web-app-maps/leaflet.d.ts delete mode 100644 packages/web-app-maps/src/composables/useLeaflet.ts create mode 100644 packages/web-app-maps/src/composables/useMap.ts create mode 100644 packages/web-app-maps/src/helpers/gpx.ts diff --git a/dev/docker/csp.yaml b/dev/docker/csp.yaml index 36657287..29989c34 100644 --- a/dev/docker/csp.yaml +++ b/dev/docker/csp.yaml @@ -1,9 +1,15 @@ directives: child-src: - "'self'" + - 'blob:' + worker-src: + - "'self'" + - 'blob:' connect-src: - "'self'" - 'blob:' + - 'https://tile.openstreetmap.org/' + - 'https://protomaps.github.io/' default-src: - "'none'" font-src: diff --git a/packages/web-app-maps/README.md b/packages/web-app-maps/README.md index ecf1e104..b086b185 100644 --- a/packages/web-app-maps/README.md +++ b/packages/web-app-maps/README.md @@ -6,23 +6,75 @@ OpenCloud Maps app can display `.gpx` files and show geo location data for singl In `apps.yaml` you can override configuration like this: +### Raster tiles (default) + ```yaml maps: config: tileLayerUrlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' + tileLayerAttribution: 'OpenStreetMap' tileLayerOptions: maxZoom: 19 - attribution: '© OpenStreetMap' ``` -`tileLayerUrlTemplate` and `tileLayerOptions` have the above as default values, you can override it if you want to use another tile layer provider. +`tileLayerUrlTemplate` defaults to OpenStreetMap. `tileLayerAttribution` defaults to OpenStreetMap when using raster tiles. -To enable seamless integration of traffic to the OpenStreetMap servers, the Content Security Policy of OpenCloud has to be adopted. +### PMTiles (vector tiles) -In the file `csp.yaml`, add the entry `- 'https://tile.openstreetmap.org/'` in the `img-src:` section. +For self-hosted vector tile maps using [PMTiles](https://protomaps.com/docs/pmtiles), point `tileLayerUrlTemplate` to a `.pmtiles` file: -Please respect the work of [OpenStreetMap](https://openstreetmap.org) and read the [Tile Usage Policy](https://operations.osmfoundation.org/policies/tiles/). +```yaml +maps: + config: + tileLayerUrlTemplate: 'https://your-server.example.com/tiles/region.pmtiles' + tileLayerAttribution: 'Protomaps | OpenStreetMap' +``` + +Vector tile labels require font glyphs. By default, fonts are loaded from `protomaps.github.io`. To use self-hosted fonts instead, set `tileLayerGlyphs`: + +```yaml +maps: + config: + tileLayerUrlTemplate: 'https://your-server.example.com/tiles/region.pmtiles' + tileLayerGlyphs: 'https://your-server.example.com/assets/fonts/{fontstack}/{range}.pbf' +``` + +### Full style override + +For complete control over the map style, provide a URL to a [MapLibre Style JSON](https://maplibre.org/maplibre-style-spec/): + +```yaml +maps: + config: + mapStyle: 'https://your-server.example.com/style.json' +``` + +### Content Security Policy + +To enable seamless integration of traffic to tile servers, the Content Security Policy of OpenCloud has to be adopted. + +In the file `csp.yaml`, add the tile server URL(s) to the `connect-src:` section. MapLibre also requires web workers, so `worker-src` and `child-src` must allow `blob:`: + +```yaml +directives: + worker-src: + - "'self'" + - 'blob:' + child-src: + - "'self'" + - 'blob:' + connect-src: + - "'self'" + - 'blob:' + - 'https://tile.openstreetmap.org/' +``` + +When using PMTiles with the default font configuration, also add `https://protomaps.github.io/` to `connect-src`. ## Privacy Notice The rendered maps are loaded from OpenStreetMap (by default). This allows them to do at least some basic kind of tracking, simply because files are loaded from their servers by your browser. + +When using PMTiles with the default font configuration, font glyphs are loaded from `protomaps.github.io`. + +Please respect the work of [OpenStreetMap](https://openstreetmap.org) and read the [Tile Usage Policy](https://operations.osmfoundation.org/policies/tiles/). diff --git a/packages/web-app-maps/leaflet.d.ts b/packages/web-app-maps/leaflet.d.ts deleted file mode 100644 index 269bc6de..00000000 --- a/packages/web-app-maps/leaflet.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Augmentation for @types/leaflet-gpx to fix incomplete type definitions -// The official types are missing the 'markers' property and 'clickable' in marker_options -import 'leaflet' - -declare module 'leaflet' { - interface GPXOptions { - // The official types are missing this separate 'markers' property - markers?: { - startIcon?: string - endIcon?: string - wptIcons?: { - [key: string]: string - } - } - } - - interface GPXMarkerOptions { - // The official types are missing 'clickable' property - clickable?: boolean - } -} diff --git a/packages/web-app-maps/package.json b/packages/web-app-maps/package.json index 15ef9120..3a33c91b 100644 --- a/packages/web-app-maps/package.json +++ b/packages/web-app-maps/package.json @@ -12,14 +12,14 @@ "test:unit": "NODE_OPTIONS=--unhandled-rejections=throw vitest" }, "devDependencies": { - "@types/leaflet": "^1.9.21", - "@types/leaflet-gpx": "^1.3.8", "vue": "^3.4.21", "vue3-gettext": "^2.4.0" }, "dependencies": { - "leaflet": "1.9.4", - "leaflet-gpx": "2.2.0", + "@protomaps/basemaps": "^5.7.0", + "@tmcw/togeojson": "^6.0.0", + "maplibre-gl": "^5.1.1", + "pmtiles": "^4.0.1", "zod": "^4.0.17" }, "peerDependencies": { diff --git a/packages/web-app-maps/src/components/GpxMap.vue b/packages/web-app-maps/src/components/GpxMap.vue index f69a4876..1495a584 100644 --- a/packages/web-app-maps/src/components/GpxMap.vue +++ b/packages/web-app-maps/src/components/GpxMap.vue @@ -1,6 +1,6 @@ -
+