fix: Kartenkacheln von OpenFreeMap statt CARTO
CARTO liefert die Kacheln inzwischen mit einem eingebrannten "API KEY REQUIRED" quer ueber der Karte — und zwar mit HTTP 200, weshalb es wie ein Darstellungsfehler aussah und nicht wie eine Sperre. Der Schluessel aus PUBLIC_CARTO_API_KEY aenderte daran nichts: Die Kachel kam byteidentisch zurueck wie ohne ihn, als key=, api_key= und apikey=. OpenFreeMap braucht keinen Schluessel und kennt kein Limit; die Stile heissen dort positron und dark und sind genau die, die CARTO als Positron und Dark Matter gepraegt hat. Weil das Vektorkacheln sind, tauscht der Themenwechsel jetzt den ganzen Stil statt nur der Kachel-URLs. Ein Stilwechsel wirft Quellen und Layer weg, deshalb legt style.load die Trail-Linie neu an — die Marker nicht, die haengen als DOM-Elemente an der Karte und nicht am Stil. PUBLIC_CARTO_API_KEY wird nicht mehr gelesen und kann aus .env und der Deployment-Konfiguration verschwinden. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P32KoesVtABd6xWsqMKzhr
This commit is contained in:
parent
69588528e8
commit
c817e208cd
1 changed files with 73 additions and 64 deletions
|
|
@ -25,7 +25,6 @@
|
||||||
import { nearestOnLine, distanceAlong, positionAtDistance } from '$lib/gpx'
|
import { nearestOnLine, distanceAlong, positionAtDistance } from '$lib/gpx'
|
||||||
import FlagIcon from './FlagIcon.svelte'
|
import FlagIcon from './FlagIcon.svelte'
|
||||||
import { trailStatusColor } from '$lib/trailStatus'
|
import { trailStatusColor } from '$lib/trailStatus'
|
||||||
import { env } from '$env/dynamic/public'
|
|
||||||
import { mode } from 'mode-watcher'
|
import { mode } from 'mode-watcher'
|
||||||
|
|
||||||
maplibregl.setWorkerUrl(workerUrl)
|
maplibregl.setWorkerUrl(workerUrl)
|
||||||
|
|
@ -84,29 +83,24 @@
|
||||||
} = $props()
|
} = $props()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basiskarten von CARTO statt der Standard-OSM-Kacheln: Sie brauchen
|
* Basiskarten von OpenFreeMap: zurückhaltend gezeichnet — die Trail-Linie
|
||||||
* keinen Schlüssel, sind zurückhaltend gezeichnet — die Trail-Linie liegt
|
* liegt dadurch deutlich sichtbarer darüber — und in einer echten dunklen
|
||||||
* dadurch deutlich sichtbarer darüber — und es gibt sie in einer echten
|
* Fassung. Ein nachträglich abgedunkeltes OSM-Bild wäre nur ein dunkleres
|
||||||
* dunklen Fassung. Ein nachträglich abgedunkeltes OSM-Bild wäre nur ein
|
* Bild, keine dunkle Karte.
|
||||||
* dunkleres Bild, keine dunkle Karte.
|
|
||||||
*
|
*
|
||||||
* Mehrere Subdomains, weil Browser die Verbindungen pro Host begrenzen.
|
* Vorher lagen hier die Rasterkacheln von CARTO. Die kommen inzwischen
|
||||||
|
* mit einem eingebrannten „API KEY REQUIRED" quer über der Karte — mit
|
||||||
|
* HTTP 200, weshalb es wie ein Darstellungsfehler aussah und nicht wie
|
||||||
|
* eine Sperre. Der Schlüssel aus PUBLIC_CARTO_API_KEY änderte daran
|
||||||
|
* nichts: Die Kachel kam byteidentisch zurück wie ohne ihn.
|
||||||
|
*
|
||||||
|
* OpenFreeMap braucht keinen Schlüssel und kennt kein Limit; die Stile
|
||||||
|
* heißen dort positron und dark und sind genau die, die CARTO als
|
||||||
|
* Positron und Dark Matter geprägt hat. Die Quellenangabe steckt in der
|
||||||
|
* Stildatei, sie muss hier nicht mehr von Hand gesetzt werden.
|
||||||
*/
|
*/
|
||||||
// Der Schlüssel muss den Browser erreichen — er kommt deshalb aus den
|
const STYLE_LIGHT = 'https://tiles.openfreemap.org/styles/positron'
|
||||||
// PUBLIC_-Variablen. Über $env/dynamic/public statt static, weil die
|
const STYLE_DARK = 'https://tiles.openfreemap.org/styles/dark'
|
||||||
// Kacheln auch ohne Schlüssel ausgeliefert werden: Fehlt die Variable,
|
|
||||||
// soll der Build nicht abbrechen.
|
|
||||||
const CARTO_KEY = env.PUBLIC_CARTO_API_KEY ?? ''
|
|
||||||
const KEY_PARAM = CARTO_KEY ? `?key=${CARTO_KEY}` : ''
|
|
||||||
|
|
||||||
const cartoTiles = (style: 'light_all' | 'dark_all') =>
|
|
||||||
['a', 'b', 'c'].map(
|
|
||||||
(sub) => `https://${sub}.basemaps.cartocdn.com/${style}/{z}/{x}/{y}.png${KEY_PARAM}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
const TILES_LIGHT = cartoTiles('light_all')
|
|
||||||
const TILES_DARK = cartoTiles('dark_all')
|
|
||||||
const ATTRIBUTION = '© OpenStreetMap-Mitwirkende, © CARTO'
|
|
||||||
|
|
||||||
const isDark = $derived(mode.current === 'dark')
|
const isDark = $derived(mode.current === 'dark')
|
||||||
|
|
||||||
|
|
@ -143,18 +137,7 @@
|
||||||
function build() {
|
function build() {
|
||||||
const instance = new maplibregl.Map({
|
const instance = new maplibregl.Map({
|
||||||
container,
|
container,
|
||||||
style: {
|
style: isDark ? STYLE_DARK : STYLE_LIGHT,
|
||||||
version: 8,
|
|
||||||
sources: {
|
|
||||||
osm: {
|
|
||||||
type: 'raster',
|
|
||||||
tiles: isDark ? TILES_DARK : TILES_LIGHT,
|
|
||||||
tileSize: 256,
|
|
||||||
attribution: ATTRIBUTION,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
layers: [{ id: 'osm', type: 'raster', source: 'osm' }],
|
|
||||||
},
|
|
||||||
center: FALLBACK_CENTER,
|
center: FALLBACK_CENTER,
|
||||||
zoom: 11,
|
zoom: 11,
|
||||||
attributionControl: controls ? undefined : false,
|
attributionControl: controls ? undefined : false,
|
||||||
|
|
@ -166,8 +149,13 @@
|
||||||
instance.addControl(new maplibregl.ScaleControl(), 'bottom-left')
|
instance.addControl(new maplibregl.ScaleControl(), 'bottom-left')
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.on('load', () => {
|
// `style.load` statt `load`: Es feuert auch nach jedem
|
||||||
|
// setStyle. Ein Stilwechsel wirft Quellen und Layer weg, die
|
||||||
|
// Trail-Linie muss danach neu entstehen — die Marker nicht, die
|
||||||
|
// haengen als DOM-Elemente an der Karte und nicht am Stil.
|
||||||
|
instance.on('style.load', () => {
|
||||||
ready = true
|
ready = true
|
||||||
|
drawTrail(instance)
|
||||||
})
|
})
|
||||||
|
|
||||||
cleanupMap = () => {
|
cleanupMap = () => {
|
||||||
|
|
@ -252,18 +240,26 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trail-Linie zeichnen bzw. aktualisieren
|
/**
|
||||||
$effect(() => {
|
* Die layerbezogenen Listener bleiben am Kartenobjekt haengen, auch wenn
|
||||||
if (!map || !ready) return
|
* ihr Layer beim Stilwechsel verschwindet und gleich darauf wieder
|
||||||
|
* entsteht. Ein zweites bindTrailEvents wuerde sie deshalb nur
|
||||||
|
* verdoppeln.
|
||||||
|
*/
|
||||||
|
let trailEventsBound = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trail-Linie zeichnen bzw. aktualisieren. Zwei Aufrufer: der Effekt, wenn
|
||||||
|
* sich Strecke oder Zustand aendern, und `style.load` nach jedem
|
||||||
|
* Stilwechsel — dann ist die Quelle fort und muss neu angelegt werden.
|
||||||
|
*/
|
||||||
|
function drawTrail(instance: MapLibreMap) {
|
||||||
const data = geojson
|
const data = geojson
|
||||||
const color = trailStatusColor(status)
|
const color = trailStatusColor(status)
|
||||||
const src = map.getSource('trail') as maplibregl.GeoJSONSource | undefined
|
const src = instance.getSource('trail') as maplibregl.GeoJSONSource | undefined
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
if (src) {
|
src?.setData({ type: 'FeatureCollection', features: [] })
|
||||||
src.setData({ type: 'FeatureCollection', features: [] })
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,33 +267,46 @@
|
||||||
|
|
||||||
if (src) {
|
if (src) {
|
||||||
src.setData(feature)
|
src.setData(feature)
|
||||||
map.setPaintProperty('trail-line', 'line-color', color)
|
instance.setPaintProperty('trail-line', 'line-color', color)
|
||||||
} else {
|
return
|
||||||
map.addSource('trail', { type: 'geojson', data: feature })
|
}
|
||||||
map.addLayer({
|
|
||||||
|
instance.addSource('trail', { type: 'geojson', data: feature })
|
||||||
|
instance.addLayer({
|
||||||
id: 'trail-line',
|
id: 'trail-line',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
source: 'trail',
|
source: 'trail',
|
||||||
layout: { 'line-join': 'round', 'line-cap': 'round' },
|
layout: { 'line-join': 'round', 'line-cap': 'round' },
|
||||||
paint: { 'line-color': color, 'line-width': 4 },
|
paint: { 'line-color': color, 'line-width': 4 },
|
||||||
})
|
})
|
||||||
map.addLayer({
|
instance.addLayer({
|
||||||
id: 'trail-hit',
|
id: 'trail-hit',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
source: 'trail',
|
source: 'trail',
|
||||||
layout: { 'line-join': 'round', 'line-cap': 'round' },
|
layout: { 'line-join': 'round', 'line-cap': 'round' },
|
||||||
paint: { 'line-color': color, 'line-width': 20, 'line-opacity': 0 },
|
paint: { 'line-color': color, 'line-width': 20, 'line-opacity': 0 },
|
||||||
})
|
})
|
||||||
bindTrailEvents(map)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Beim Themenwechsel die Kachelquelle austauschen. */
|
if (!trailEventsBound) {
|
||||||
|
bindTrailEvents(instance)
|
||||||
|
trailEventsBound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trail-Linie zeichnen bzw. aktualisieren
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!map || !ready) return
|
if (!map || !ready) return
|
||||||
|
drawTrail(map)
|
||||||
|
})
|
||||||
|
|
||||||
const src = map.getSource('osm') as maplibregl.RasterTileSource | undefined
|
/**
|
||||||
src?.setTiles(isDark ? TILES_DARK : TILES_LIGHT)
|
* Beim Themenwechsel den ganzen Stil tauschen. Kacheln allein tun es
|
||||||
|
* nicht: Hell und dunkel unterscheiden sich hier nicht im Bild, sondern
|
||||||
|
* in der Zeichenvorschrift.
|
||||||
|
*/
|
||||||
|
$effect(() => {
|
||||||
|
if (!map || !ready) return
|
||||||
|
map.setStyle(isDark ? STYLE_DARK : STYLE_LIGHT)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Kartenausschnitt auf den Track setzen
|
// Kartenausschnitt auf den Track setzen
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue