fix: share GPX+journey rendering via MapUtils.renderGpxJourney

The home map was drawing an initial addJourneyLine, then trying to remove
layer 'home-journey' in the Promise.all callback — but addJourneyLine names
the layer 'home-journey-line', so removeLayer was a no-op and removeSource
failed (layer still referencing the source), leaving a ghost line on top of
the GPX tracks.

Extract the Promise.all → GPX tracks → buildJourneySegments → addJourneySegments
pattern into MapUtils.renderGpxJourney() and replace both map.html.twig and
home.html.twig with the shared call. No upfront journey line is drawn — the
function handles the no-GPX case correctly via Promise.all([]).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
This commit is contained in:
2026-06-21 10:37:56 +02:00
parent 04e4fa3dcd
commit 6eaa00d612
3 changed files with 39 additions and 52 deletions
+37
View File
@@ -283,6 +283,42 @@
});
}
/*
* Fetch GPX files, render their raw tracks, then draw journey connector lines
* only between entries not covered by any GPX file (connector suppression).
*
* gpxUrls: array of GPX file URLs to fetch
* entries: [{lat, lng, force_connect?}, ...] in chronological order
* gpxSourcePrefix: source/layer ID prefix for raw GPX tracks (e.g. 'gpx', 'home-gpx')
* journeySourceId: base source ID for connector segments (e.g. 'journey', 'home-journey')
*
* When gpxUrls is empty, Promise.all resolves immediately → no GPX layers,
* buildJourneySegments draws a full connector line between all entries.
*/
function renderGpxJourney(map, gpxUrls, entries, gpxSourcePrefix, journeySourceId) {
Promise.all(gpxUrls.map(function (url, idx) {
return fetch(url)
.then(function (r) { return r.text(); })
.then(function (text) {
var xml = new DOMParser().parseFromString(text, 'text/xml');
var geojson = toGeoJSON.gpx(xml);
var sid = gpxSourcePrefix + '-' + idx;
map.addSource(sid, { type: 'geojson', data: geojson });
map.addLayer({
id: sid + '-line', type: 'line', source: sid,
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': ACCENT, 'line-width': 2, 'line-opacity': 0.7 }
});
return extractTrackpoints(geojson);
})
.catch(function (err) { console.warn('GPX load failed:', url, err); return []; });
})).then(function (allTrackpoints) {
var valid = allTrackpoints.filter(function (tp) { return tp.length > 0; });
var segments = buildJourneySegments(entries, valid, 10);
addJourneySegments(map, segments, journeySourceId);
});
}
global.MapUtils = {
MAP_STYLE: MAP_STYLE,
ACCENT: ACCENT,
@@ -290,6 +326,7 @@
addJourneySegments: addJourneySegments,
buildJourneySegments: buildJourneySegments,
extractTrackpoints: extractTrackpoints,
renderGpxJourney: renderGpxJourney,
createDotMarker: createDotMarker,
createStoryMarker: createStoryMarker
};
+1 -28
View File
@@ -184,9 +184,6 @@ homeMap.on('load', function () {
new maplibregl.Marker({ element: el }).setLngLat(lngLat).addTo(homeMap);
});
/* Draw simple journey line immediately; replaced below if GPX is present */
MapUtils.addJourneyLine(homeMap, coords, 'home-journey');
if (HOME_ENTRIES.length === 1) {
homeMap.jumpTo({ center: coords[0], zoom: 10 });
} else {
@@ -195,31 +192,7 @@ homeMap.on('load', function () {
setTimeout(function () { homeMap.resize(); }, 100);
if (HOME_GPX_URLS.length > 0) {
Promise.all(HOME_GPX_URLS.map(function (url, idx) {
return fetch(url)
.then(function (r) { return r.text(); })
.then(function (text) {
var xml = new DOMParser().parseFromString(text, 'text/xml');
var geojson = toGeoJSON.gpx(xml);
var sid = 'home-gpx-' + idx;
homeMap.addSource(sid, { type: 'geojson', data: geojson });
homeMap.addLayer({
id: sid + '-line', type: 'line', source: sid,
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': MapUtils.ACCENT, 'line-width': 2, 'line-opacity': 0.7 }
});
return MapUtils.extractTrackpoints(geojson);
})
.catch(function (err) { console.warn('GPX load failed:', url, err); return []; });
})).then(function (allTrackpoints) {
if (homeMap.getLayer('home-journey')) homeMap.removeLayer('home-journey');
if (homeMap.getSource('home-journey')) homeMap.removeSource('home-journey');
var valid = allTrackpoints.filter(function (tp) { return tp.length > 0; });
var segments = MapUtils.buildJourneySegments(HOME_ENTRIES, valid, 10);
MapUtils.addJourneySegments(homeMap, segments, 'home-journey');
});
}
MapUtils.renderGpxJourney(homeMap, HOME_GPX_URLS, HOME_ENTRIES, 'home-gpx', 'home-journey');
});
</script>
{% endif %}
+1 -24
View File
@@ -92,30 +92,7 @@ map.on('load', function () {
}
/* ── GPX tracks + journey segments ─────────────────────────── */
Promise.all(GPX_URLS.map(function (url, idx) {
return fetch(url)
.then(function (r) { return r.text(); })
.then(function (text) {
var xml = new DOMParser().parseFromString(text, 'text/xml');
var geojson = toGeoJSON.gpx(xml);
var sid = 'gpx-' + idx;
map.addSource(sid, { type: 'geojson', data: geojson });
map.addLayer({
id: sid + '-line', type: 'line', source: sid,
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': MapUtils.ACCENT, 'line-width': 2, 'line-opacity': 0.7 }
});
return MapUtils.extractTrackpoints(geojson);
})
.catch(function (err) {
console.warn('GPX load failed:', url, err);
return [];
});
})).then(function (allTrackpoints) {
var validTrackpoints = allTrackpoints.filter(function (tp) { return tp.length > 0; });
var segments = MapUtils.buildJourneySegments(ENTRIES, validTrackpoints, 10);
MapUtils.addJourneySegments(map, segments, 'journey');
});
MapUtils.renderGpxJourney(map, GPX_URLS, ENTRIES, 'gpx', 'journey');
});
</script>
{% endblock %}