diff --git a/config.example.json b/config.example.json index c5de20a0..1a094dbe 100644 --- a/config.example.json +++ b/config.example.json @@ -78,7 +78,7 @@ "darkDefault": "carto-dark", "lightDefault": "carto-light", "providers": { - "_comment_carto": "Carto is the default provider. Since 2026-08 Carto requires an API key - without one the tiles come back stamped 'API KEY REQUIRED'. Get a free key at https://carto.com/basemaps/apikey and put it in 'key'. WARNING: the key is sent to the browser; restrict it by domain in the Carto dashboard. Optional: specify 'domain' for Carto enterprise (e.g. 'mycompany' for 'https://{s}.mycompany.cartocdn.com').", + "_comment_carto": "Carto is the default provider. Since 2026-08 CARTO requires an API key on its raster basemaps: without one every tile is served stamped 'API KEY REQUIRED' with a 200 status, so nothing errors and no healthcheck fires - verify by looking at a tile, not at a status code. Get a free key (no account, no card, 5M tile requests/month) at https://carto.com/basemaps/apikey and put it in 'key'. The query parameter is 'key'; 'api_key' is silently ignored and still serves the watermarked tile. WARNING: the key is sent to the browser; restrict it by domain in the CARTO dashboard. Optional: specify 'domain' for Carto enterprise (e.g. 'mycompany' for 'https://{s}.mycompany.cartocdn.com').", "carto": { "enabled": true, "key": "", diff --git a/docs/deployment.md b/docs/deployment.md index a7c15a0c..211ad025 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -188,6 +188,44 @@ See `config.example.json` in the repository for all available options including: Map tile providers are enabled and configured via the `config.json` file. You can provide your custom API credentials (e.g. `osm_url`, `stamen_api_key`, `mapbox_api_key`) to activate external tile services. Once configured on the server, users can select their preferred tile provider from the Customizer UI on the client, and their choice will be persisted automatically. +#### CARTO now requires an API key + +CARTO's raster basemaps (`carto-dark`, `carto-light`, `carto-voyager`, +`carto-voyager-dark`, `positron-dark`) require an API key. Without one, tiles +are still returned with a **200 status** but every one of them is stamped +`API KEY REQUIRED` — so nothing errors, no healthcheck fires, and the map just +quietly looks broken. + +Get a free key at — no CARTO account, no +credit card, emailed back immediately, 5 million tile requests per calendar +month across raster and vector. Then set it as `key`: + +```json +"map": { + "tiles": { + "providers": { + "carto": { "enabled": true, "key": "YOUR_CARTO_KEY" } + } + } +} +``` + +Notes: + +- The query parameter is `key`. **`api_key` is silently ignored** and still + serves the watermarked tile. +- The key is sent to the browser, so treat it as public. CARTO asks for a domain + when the key is issued, but whether that restriction is enforced on every + request has not been verified here. Assume anyone can read the key from + devtools and spend your quota. The 5M/month fair-use ceiling makes that + low-impact for most deployments, but it is worth knowing. +- A wrong or expired key fails the same silent way as no key at all. Verify by + **looking at a tile**, not by checking for a 200. +- CARTO has said it is considering freezing data updates to the raster + basemaps. If you would rather not depend on them, set + `"carto": { "enabled": false }` and use another provider — `esri` needs no + key at all, and `osm`/`stamen` accept their own tokens. + --- ## MQTT Setup diff --git a/public/customize-v2.js b/public/customize-v2.js index d1d844cf..71c73853 100644 --- a/public/customize-v2.js +++ b/public/customize-v2.js @@ -1835,7 +1835,9 @@ var modalClosingLine = null; _gfModalMap = L.map(mapDiv, { zoomControl: true }); - L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { + L.tileLayer(window.MC_tileUrlById + ? window.MC_tileUrlById('carto-light', 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png') + : 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap © CartoDB', maxZoom: 19 }).addTo(_gfModalMap); @@ -2044,7 +2046,9 @@ if (!mapEl || typeof L === 'undefined') return; _gfMap = L.map(mapEl, { zoomControl: false, dragging: false, scrollWheelZoom: false, doubleClickZoom: false, touchZoom: false }); - L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { + L.tileLayer(window.MC_tileUrlById + ? window.MC_tileUrlById('carto-light', 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png') + : 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap © CartoDB', maxZoom: 19 }).addTo(_gfMap); diff --git a/public/geofilter-builder.html b/public/geofilter-builder.html index 2a55ca84..3ea8e54f 100644 --- a/public/geofilter-builder.html +++ b/public/geofilter-builder.html @@ -85,14 +85,30 @@ +