From 5bdc84e2077e13d53e6d0080bef2705f7283ea5b Mon Sep 17 00:00:00 2001 From: Johan Cwiklinski Date: Mon, 31 Aug 2026 16:19:08 +0200 Subject: [PATCH 1/2] Replace CARTO basemaps with OpenFreeMap vector tiles CARTO now watermarks anonymous basemap tiles with "API KEY REQUIRED", while still answering 200 with a valid PNG, so nothing on our side could detect it. OpenFreeMap needs no key, no registration, and can be self-hosted, which a distributed plugin can rely on. The positron style keeps the grey background light_all provided, so the Galette markers stay readable. Vector tiles mean maplibre-gl, bridged into Leaflet by maplibre-gl-leaflet: the markers, clusters, popups and controls all stay where they are. maplibre-gl is pinned to 5.x because 6.x dropped the UMD build, and it gets its own bundle because uglify-js cannot parse its already minified dist. Attribution is no longer hardcoded: the bridge reads it from the source TileJSON and feeds the Leaflet control. Browsers without WebGL 2 fall back to raster OpenStreetMap tiles rather than to a blank map. --- README.md | 2 + gulpfile.js | 26 +- package-lock.json | 274 ++++++++++++++++++++- package.json | 4 +- templates/default/common_scripts.html.twig | 30 ++- templates/default/headers.html.twig | 1 + 6 files changed, 328 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fd524ce..d33f80b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A [Galette](https://galette.eu) plugin to geolocalize members. Map display uses [Leaflet project](https://leafletjs.com/) while search is provided by [Nominatim](https://nominatim.openstreetmap.org/) API. +Background tiles are the [OpenFreeMap](https://openfreemap.org/) vector tiles, rendered with [MapLibre GL](https://maplibre.org/): no API key, no registration, and the whole thing can be self-hosted. * website: https://galette.eu - https://doc.galette.eu/en/master/plugins/maps.html * bugs and features: http://bugs.galette.eu/projects/galette-plugin-maps @@ -25,6 +26,7 @@ To use Galette Maps plugin, you'll need a reliable Galette version, and of cours Un plugin [Galette](https://galette.eu) pour gérer paiments de cotisation et de dons via Maps. Les cartes sont affichées par le biais du [projet Leaflet](https://leafletjs.com/) tandis que la recherche est assurée par l'API [Nominatim](https://nominatim.openstreetmap.org/). +Le fond de carte provient des tuiles vectorielles [OpenFreeMap](https://openfreemap.org/), rendues par [MapLibre GL](https://maplibre.org/) : sans clé d'API, sans inscription, et auto-hébergeable. * site web : https://galette.eu - https://doc.galette.eu/fr/master/plugins/maps.html * bogues et fonctionnalités : http://bugs.galette.eu/projects/galette-plugin-maps diff --git a/gulpfile.js b/gulpfile.js index 81d232d..5b11870 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -37,6 +37,19 @@ const main_scripts = [ './node_modules/leaflet-legend/leaflet-legend.js' ]; +const gl_styles = [ + './node_modules/maplibre-gl/dist/maplibre-gl.css' +]; + +// maplibre-gl is pinned to 5.x on purpose: 6.x dropped the UMD build and ships +// ES modules only, which cannot be concatenated into a classic script. The 5.x +// dist is already minified and uses syntax uglify-js cannot parse, so this +// bundle is concatenated as is, never piped through uglify. +const gl_scripts = [ + './node_modules/maplibre-gl/dist/maplibre-gl.js', + './node_modules/@maplibre/maplibre-gl-leaflet/leaflet-maplibre-gl.js' +]; + const main_assets = [ { 'src': './node_modules/leaflet/dist/images/*', @@ -81,7 +94,12 @@ function styles() { .pipe(concat('maps-locate.bundle.min.css')) .pipe(gulp.dest(plugin.public)); - return merge(main, locate); + gl = gulp.src(gl_styles) + .pipe(cleancss()) + .pipe(concat('maps-gl.bundle.min.css')) + .pipe(gulp.dest(plugin.public)); + + return merge(main, locate, gl); }; function scripts() { @@ -97,7 +115,11 @@ function scripts() { .pipe(uglify()) .pipe(gulp.dest(plugin.public)); - return merge(main, locate); + gl = gulp.src(gl_scripts) + .pipe(concat('maps-gl.bundle.min.js')) + .pipe(gulp.dest(plugin.public)); + + return merge(main, locate, gl); }; function assets() { diff --git a/package-lock.json b/package-lock.json index 4661b41..17df4fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,15 @@ "version": "2.0.0", "license": "GPL-3.0-or-later", "dependencies": { + "@maplibre/maplibre-gl-leaflet": "^0.1.4", "leaflet": "^1.8.0", "leaflet-control-geocoder": "^4.0.0", "leaflet-gesture-handling": "^1.2.2", "leaflet-legend": "^1.0.2", "leaflet.fullscreen": "^5.3.3", "leaflet.locatecontrol": "^0.90.1", - "leaflet.markercluster": "^1.5.3" + "leaflet.markercluster": "^1.5.3", + "maplibre-gl": "^5.24.0" }, "devDependencies": { "del": "^6.1.1", @@ -50,6 +52,130 @@ "node": ">=10.13.0" } }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", + "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==", + "license": "MIT", + "engines": { + "node": ">= 22" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz", + "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz", + "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^4.0.2" + } + }, + "node_modules/@mapbox/vector-tile/node_modules/pbf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/geojson-vt": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz", + "integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.1.0" + } + }, + "node_modules/@maplibre/maplibre-gl-leaflet": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-leaflet/-/maplibre-gl-leaflet-0.1.4.tgz", + "integrity": "sha512-k57wcndZIvq3m08g2je4pjUPGr43ffNW3j+gXbymt8Vi2l1lUERUi1UmdTZcTPN8LuDDYOMWRNBaSLwpAu02ew==", + "license": "ISC", + "peerDependencies": { + "@types/leaflet": "^1.9.0", + "leaflet": "^1.9.3", + "maplibre-gl": "^2.4.0 || ^3.3.1 || ^4.3.2 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz", + "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^1.0.0", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^3.0.0", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/mlt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.2.0.tgz", + "integrity": "sha512-g45M8gEI4sMO3X9ib4K7n3ZKFf0qQOL+NMkHCnEK51lOrYFHiEssOuZncx1+miIgi1IEE2NcbRMcq8RBkL+QHA==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0" + } + }, + "node_modules/@maplibre/vt-pbf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz", + "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^5.1.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -95,6 +221,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/leaflet": { + "version": "1.9.22", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.22.tgz", + "integrity": "sha512-h3lhECYEKDasG7LFHu+GiHqAvsgLuQvlJvVZzJDGONo3sEL+wUOqSFLnwkZlK0qVxnxbuGFW8iBlJNYs5wgndA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -697,6 +839,12 @@ "node": ">= 10.13.0" } }, + "node_modules/earcut": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.3.tgz", + "integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==", + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -953,6 +1101,12 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1681,6 +1835,18 @@ "url": "https://bevry.me/fund" } }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, + "node_modules/kdbush": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", + "license": "ISC" + }, "node_modules/last-run": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/last-run/-/last-run-2.0.0.tgz", @@ -1812,6 +1978,52 @@ "node": ">=0.10.0" } }, + "node_modules/maplibre-gl": { + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz", + "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/tiny-sdf": "^2.1.0", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^2.0.4", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/geojson-vt": "^6.1.0", + "@maplibre/maplibre-gl-style-spec": "^24.8.1", + "@maplibre/mlt": "^1.1.8", + "@maplibre/vt-pbf": "^4.3.0", + "@types/geojson": "^7946.0.16", + "earcut": "^3.0.2", + "gl-matrix": "^3.4.4", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^4.0.1", + "potpack": "^2.1.0", + "quickselect": "^3.0.0", + "tinyqueue": "^3.0.0" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/maplibre-gl/node_modules/pbf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1849,6 +2061,21 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, "node_modules/mute-stdout": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", @@ -2042,6 +2269,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", @@ -2111,6 +2350,12 @@ "node": ">=0.10.0" } }, + "node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2118,6 +2363,12 @@ "dev": true, "license": "MIT" }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", + "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2139,6 +2390,12 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -2285,6 +2542,15 @@ "node": ">= 10.13.0" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -2572,6 +2838,12 @@ "readable-stream": "2 || 3" } }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 0c15a79..04979ed 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ "ordered-read-streams": "^2.0.0" }, "dependencies": { + "@maplibre/maplibre-gl-leaflet": "^0.1.4", "leaflet": "^1.8.0", "leaflet-control-geocoder": "^4.0.0", "leaflet-gesture-handling": "^1.2.2", "leaflet-legend": "^1.0.2", "leaflet.fullscreen": "^5.3.3", "leaflet.locatecontrol": "^0.90.1", - "leaflet.markercluster": "^1.5.3" + "leaflet.markercluster": "^1.5.3", + "maplibre-gl": "^5.24.0" } } diff --git a/templates/default/common_scripts.html.twig b/templates/default/common_scripts.html.twig index 2cf662b..9477c91 100644 --- a/templates/default/common_scripts.html.twig +++ b/templates/default/common_scripts.html.twig @@ -6,6 +6,7 @@ {% if cur_route starts with 'maps_' %} + {% endif %} {% if cur_route == 'maps_localize_member' or cur_route == 'maps_mymap' %} @@ -30,6 +31,17 @@ shadowSize: [41, 41] }); + /** + * Vector tiles need WebGL 2; without it we fall back to raster tiles + */ + function _hasWebGL2(){ + try { + return !!(window.WebGL2RenderingContext && document.createElement('canvas').getContext('webgl2')); + } catch (err) { + return false; + } + } + {% if cur_route != 'maps_map' %} function _iLiveHere(_id){ $('#' + _id).click(function(e, f,g){ @@ -77,7 +89,8 @@ var _lon = {% if town is defined %}{{ town['longitude'] }}{% else %}2.4609375{% endif %}; var map = L.map( 'map', { - gestureHandling: true + gestureHandling: true, + maxZoom: 19 } ).setView([_lat, _lon], {% if town is defined %}12{% else %}6{% endif %}); @@ -118,10 +131,17 @@ }).addTo(map); {% endif %} - L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', { - maxZoom: 18, - attribution: '{{ _T("Map data (c)", "maps")|e("js") }} {{ _T("OpenStreetMap contributors", "maps")|e("js") }}, {{ _T("Imagery (c)", "maps")|e("js") }} CartoDB' - }).addTo(map); + if (_hasWebGL2()) { + {# Attribution comes from the style sources, the Leaflet control picks it up on load #} + L.maplibreGL({ + style: 'https://tiles.openfreemap.org/styles/positron' + }).addTo(map); + } else { + L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: '{{ _T("Map data (c)", "maps")|e("js") }} {{ _T("OpenStreetMap contributors", "maps")|e("js") }}' + }).addTo(map); + } try { _mapsBinded(map); diff --git a/templates/default/headers.html.twig b/templates/default/headers.html.twig index 3cde5d6..c22873c 100644 --- a/templates/default/headers.html.twig +++ b/templates/default/headers.html.twig @@ -6,6 +6,7 @@ {% if cur_route starts with 'maps_' %} + {% endif %} {% if cur_route == 'maps_localize_member' or cur_route == 'maps_mymap' %} From c99ba4ca7c67ea66b2a6cbe81efe0f926f660ca8 Mon Sep 17 00:00:00 2001 From: Johan Cwiklinski Date: Mon, 31 Aug 2026 16:38:08 +0200 Subject: [PATCH 2/2] Add a tile provider quick fix note (api key required) Issue will be fixed in next version; but current users will have issues. Here a quickfix for them! --- README.tiles.md | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 README.tiles.md diff --git a/README.tiles.md b/README.tiles.md new file mode 100644 index 0000000..5da5ac6 --- /dev/null +++ b/README.tiles.md @@ -0,0 +1,145 @@ +# Quick fix — "API KEY REQUIRED" tiles + +## Symptom + +Every map in the plugin shows the **"API KEY REQUIRED — +carto.com/basemaps/apikey"** watermark across the tiles. + +## Cause + +The background map comes from CARTO (`basemaps.cartocdn.com`), which now +requires an API key for its basemaps. + +## Fix + +A single file to edit: + +``` +galette/plugins/plugin-maps/templates/default/common_scripts.html.twig +``` + +### The diff + +```diff +--- a/templates/default/common_scripts.html.twig ++++ b/templates/default/common_scripts.html.twig +@@ -132,9 +132,9 @@ + }).addTo(map); + {% endif %} + +- L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', { +- maxZoom: 18, +- attribution: '{{ _T("Map data (c)", "maps")|e("js") }} {{ _T("OpenStreetMap contributors", "maps")|e("js") }}, {{ _T("Imagery (c)", "maps")|e("js") }} CartoDB' ++ L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { ++ maxZoom: 19, ++ attribution: '{{ _T("Map data (c)", "maps")|e("js") }} {{ _T("OpenStreetMap contributors", "maps")|e("js") }}' + }).addTo(map); + + try { +``` + +Copy the diff above into a file (`tiles-quickfix.diff` for example) at plugin top directory, and apply it from there with: + +``` +patch -p1 < tiles-quickfix.diff +``` + +### Clearing the Twig cache — mandatory + +**Without this step, the change has no effect**: Galette serves compiled +templates and does not notice that a `.twig` file has changed. + +Back at Galette top directory: + +``` +rm -rf galette/data/cache/*/templates/ +``` + +The directory usually belongs to the web server user (`www-data`, `apache`), so +the command has to be run as `root` or through `sudo`. It is rebuilt on its own +on the next visit; neither a restart nor a reload of the web server is needed. + +## Other possible background maps + +Every URL below has been tested and answers without an API key. The +`attribution` block is **mandatory**: keeping it is not a formality, it is the +condition of the data licence. + +### OpenStreetMap — the standard rendering + +The one used in the fix above. The simplest, the best known. It renders in +colour, where CARTO `light_all` was light grey: the Galette markers stand out +slightly less. + +```js +L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: '© OpenStreetMap' +}).addTo(map); +``` + +Worth knowing: the OSMF usage policy does cover low traffic sites — which an +association's Galette instance is — but explicitly asks *not* to hardcode the +tile URL in distributed software. Which is exactly the trap CARTO has just +sprung on us. + +### OpenStreetMap France + +Servers of the OSM-FR association, French rendering, one more zoom level. + +```js +L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', { + maxZoom: 20, + subdomains: 'abc', + attribution: '© OpenStreetMap — OSM-FR tiles' +}).addTo(map); +``` + +### Humanitarian OSM Team + +Same host as OSM-FR, more contrasted style, roads and facilities are very +readable. + +```js +L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', { + maxZoom: 20, + subdomains: 'abc', + attribution: '© OpenStreetMapHOT tiles' +}).addTo(map); +``` + +### OpenStreetMap.de + +German rendering, local names favoured. Beware, it stops at zoom 18: beyond +that the server answers a 404 (checked). + +```js +L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', { + maxZoom: 18, + attribution: '© OpenStreetMap' +}).addTo(map); +``` + +### Esri World Light Gray Base + +Visually the closest to what the plugin used to display: very light grey, almost +without colour, the markers stand out perfectly. Note the unusual `{z}/{y}/{x}` +order, and the JPEG format. + +```js +L.tileLayer('https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}', { + maxZoom: 16, + attribution: 'Tiles © Esri — Esri, DeLorme, NAVTEQ' +}).addTo(map); +``` + +One reservation: this is a commercial vendor's courtesy service, with no +availability commitment. Nothing guarantees it will not do tomorrow what CARTO +has just done. + +### Staying on CARTO + +That is possible, but it now means opening an account on + and adding the key to the URL. The exact form of +the URL with a key is given by their interface — we do not reproduce it here, it +is specific to each account.