Map tiles: switch to Esri World Light Gray (OSM 403s file://)

tiles="OpenStreetMap" returned HTTP 403 "referer is required by tile usage
policy" in the target browser — OSM graylists refererless requests and a
karte.html opened via file:// sends no Referer. Esri's Canvas/World_Light_Gray
base + reference layers need neither a key nor a Referer and keep the muted
look. New MAP_TILES / MAP_TILES_LABELS / MAP_TILES_ATTR constants; generate_map
adds them as explicit TileLayers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 22:37:41 +02:00
parent 5b85596a46
commit 5eb60ec2d3
6 changed files with 62 additions and 30 deletions
+18 -5
View File
@@ -61,9 +61,16 @@ GEOCODE_REGIONS = ["Baden-Württemberg", "Hessen"]
MAP_DEFAULT_CENTER = [49.0, 9.0]
MAP_DEFAULT_ZOOM = 8
#: Kartenkacheln. "OpenStreetMap" braucht keinen API-Schlüssel.
#: (Die früher genutzten "CartoDB positron"-Kacheln verlangen seit 2024 einen Schlüssel.)
MAP_TILES = "OpenStreetMap"
#: Kartenkacheln Esri "World Light Gray" (heller, ruhiger Hintergrund).
#: Braucht keinen API-Schlüssel und keinen Referer (anders als tile.openstreetmap.org,
#: das lokale Dateien mit 403 blockt, und "CartoDB positron", das einen Schlüssel will).
#: Alternativen ohne Schlüssel als Ein-Zeilen-Tausch:
#: Esri Straßenkarte: …/World_Street_Map/MapServer/tile/{z}/{y}/{x}
#: Esri Topo: …/World_Topo_Map/MapServer/tile/{z}/{y}/{x}
_ESRI = "https://server.arcgisonline.com/ArcGIS/rest/services"
MAP_TILES = f"{_ESRI}/Canvas/World_Light_Gray_Base/MapServer/tile/{{z}}/{{y}}/{{x}}"
MAP_TILES_LABELS = f"{_ESRI}/Canvas/World_Light_Gray_Reference/MapServer/tile/{{z}}/{{y}}/{{x}}"
MAP_TILES_ATTR = "Kacheln © Esri"
log = logging.getLogger("arbeitsorte")
@@ -421,9 +428,15 @@ def generate_map(store: DataStore) -> Path:
raise ValueError("Keine Koordinaten vorhanden. Bitte zuerst Einträge speichern.")
m = folium.Map(
location=MAP_DEFAULT_CENTER, zoom_start=MAP_DEFAULT_ZOOM,
tiles=MAP_TILES,
location=MAP_DEFAULT_CENTER, zoom_start=MAP_DEFAULT_ZOOM, tiles=None,
)
folium.TileLayer(
MAP_TILES, attr=MAP_TILES_ATTR, name="Karte", control=False,
).add_to(m)
folium.TileLayer(
MAP_TILES_LABELS, attr=MAP_TILES_ATTR, name="Beschriftung",
control=False, overlay=True,
).add_to(m)
max_count = int(freq["count"].max())
for _, row in freq.iterrows():
+1 -1
View File
@@ -112,6 +112,6 @@ This is the file to ask for when diagnosing a problem on the user's laptop.
|------------|-----------|-------------------|
| Nominatim (OSM) | geocoding new/edited entries | entries save with blank coords, shown as `⚠ nicht gefunden` |
| CDN (jsdelivr, jquery, cloudflare) | rendering `karte.html` | map page loads blank / unstyled |
| OpenStreetMap tiles (`tile.openstreetmap.org`) | map background | blank/grey tiles |
| Esri tiles (`server.arcgisonline.com`) | map background | blank/grey tiles |
Existing entries that already have coordinates do **not** need the network.
+10
View File
@@ -2,6 +2,16 @@
Notable changes to the app. Newest first.
## 2026-09-07 — map tiles → Esri (OSM 403s local files)
The switch to `tiles="OpenStreetMap"` worked on the dev machine but the target
laptop's browser got **HTTP 403 "referer is required by tile usage policy"**
OSM's CDN blocks refererless requests, and a `karte.html` opened as `file://`
sends no `Referer`. Switched to **Esri "World Light Gray"** (base + labels
overlay, `server.arcgisonline.com`): no key, no `Referer` check, muted look
similar to the old positron. Constants `MAP_TILES` / `MAP_TILES_LABELS` /
`MAP_TILES_ATTR`.
## 2026-09-07 — install split for non-admin users
The target account has no sudo, so install is two scripts:
+18 -11
View File
@@ -36,19 +36,26 @@ Numeric parsing is deliberately deferred to `get_map_data` (`pd.to_numeric(...,
errors="coerce")`). Don't "fix" columns to float on load — blank coordinates and
leading-zero PLZ both depend on the string representation.
## CartoDB "positron" tiles now require an API key (2026-09-07)
## Map tiles: Carto needs a key, OSM 403s file:// — use Esri (2026-09-07)
The original `folium.Map(tiles="CartoDB positron")` started rendering an
"API KEY REQUIRED / carto.com/basemaps/apikey" watermark Carto moved its
basemaps behind a key. Switched to `tiles="OpenStreetMap"` (constant
`MAP_TILES`), which needs no key. Trade-off: OSM standard tiles are more
colourful than positron's muted grey. Other no-key options if a lighter look is
wanted: OSM Germany (`tile.openstreetmap.de`), OSM France Hot
(`{s}.tile.openstreetmap.fr/hot`) both still colourful. Truly muted
key-free basemaps are hard to find now.
Two dead ends before landing on Esri:
Don't assume a "free" third-party tile source stays free pin the provider in
one constant so swapping it is a one-line change.
1. `folium.Map(tiles="CartoDB positron")` → "API KEY REQUIRED" watermark; Carto
moved basemaps behind a key.
2. `tiles="OpenStreetMap"` → worked in `curl` from the dev machine but the
**user's browser got HTTP 403 "referer is required by tile usage policy"**.
OSM's tile CDN graylists refererless requests, and a `karte.html` opened as a
local `file://` sends no `Referer`. A browser can't add one. Dead end for
this app's "open a local HTML file" model.
Now: **Esri "World Light Gray"** base + reference (labels) overlay
(`server.arcgisonline.com/ArcGIS/rest/services/Canvas/…`). No key, no `Referer`
check, light/muted look close to the old positron. `MAP_TILES` /
`MAP_TILES_LABELS` constants; comment lists Esri Street/Topo as swaps.
Lesson: a tile source that works from `curl` or an `http://` page can still fail
from `file://`. Test the map by **opening the generated `karte.html` directly**,
not just by checking the URL in the HTML.
## Nominatim query order matters
+5 -5
View File
@@ -163,11 +163,11 @@ or at least skip exact duplicates by default.
## 9. Offline map 🟢
`karte.html` loads Leaflet/jQuery/Bootstrap from CDNs and tiles from
`tile.openstreetmap.org` (`MAP_TILES`; switched away from CartoDB positron, which
now needs an API key). The map is blank without internet even though all the
data is local. If offline use matters, vendor the Leaflet JS/CSS into `data/`
and post-process the Folium output to point at local files (tiles would still
need caching or an offline tile pack — larger effort).
`server.arcgisonline.com` (Esri; `MAP_TILES` — Carto needs a key, OSM 403s
`file://`). The map is blank without internet even though all the data is local.
If offline use matters, vendor the Leaflet JS/CSS into `data/` and post-process
the Folium output to point at local files (tiles would still need caching or an
offline tile pack — larger effort).
## 10. Small code-quality items 🟢 — partly done
+10 -8
View File
@@ -65,9 +65,10 @@ A table view of everything in `orte.csv`.
Generated by `generate_map()`:
- Base layer: **OpenStreetMap** standard tiles (`MAP_TILES`) no API key.
Initial view centred on `[49.0, 9.0]`, zoom 8 (roughly Baden-Württemberg);
the view auto-fits to the markers when there is more than one.
- Base layer: **Esri "World Light Gray"** (`MAP_TILES` + a labels overlay) a
light, muted basemap that needs no API key and no `Referer`. Initial view
centred on `[49.0, 9.0]`, zoom 8; auto-fits to the markers when there is more
than one.
- One **`CircleMarker`** per unique `(city, lat, lon)` group. Radius scales
linearly with the visit count for that location
(`5 + count / max_count * 20` px), colour is DRK red (`#CC0000`).
@@ -77,11 +78,12 @@ Generated by `generate_map()`:
bar shows a warning).
> The generated HTML pulls Leaflet, jQuery and Bootstrap from CDNs, and the map
> tiles from `tile.openstreetmap.org`, so the map needs an internet connection to
> render even though the data is local. To change the look, set `MAP_TILES` near
> the top of `app.py` to another no-key provider Folium knows
> (`"OpenStreetMap"`, `"CartoDB positron"` *now needs a key*, or a custom
> `folium.TileLayer` URL such as OSM Germany / OSM France).
> tiles from `server.arcgisonline.com`, so the map needs an internet connection
> to render even though the data is local. To change the look, edit `MAP_TILES` /
> `MAP_TILES_LABELS` near the top of `app.py` — the comment there lists other
> no-key Esri layers (Street, Topo). `tile.openstreetmap.org` was tried first
> but its tile-usage policy **403s** pages opened from `file://` (no `Referer`);
> `CartoDB positron` needs an API key.
## Geocoding behaviour