feat: add per-trip use_gpx and autoconnect toggles
Adds two configurable toggles to the trip blueprint (Admin2 Trip tab): - use_gpx: show/hide GPX tracks on all maps (default: enabled) - autoconnect: draw connector lines between markers (default: enabled) When use_gpx is off, GPX files are not fetched or rendered on any map (home, map, trip, dailies). The stats panel in trip.html.twig still reads GPX_URLS directly and is unaffected. When autoconnect is off, buildJourneySegments suppresses all auto-connectors; only entries with force_connect:true still draw a line — making force_connect behaviour independent of both settings. Also refactors the inline Promise.all in trip.html.twig to use the shared renderGpxJourney utility (reducing duplication). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
This commit is contained in:
@@ -55,6 +55,30 @@ form:
|
|||||||
placeholder: '6 weeks from Venice to Sicily by train'
|
placeholder: '6 weeks from Venice to Sicily by train'
|
||||||
help: 'Short description shown on homepage highlight cards'
|
help: 'Short description shown on homepage highlight cards'
|
||||||
|
|
||||||
|
header.use_gpx:
|
||||||
|
type: toggle
|
||||||
|
label: Show GPX tracks
|
||||||
|
help: 'Display GPX route files on the map'
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: 'Yes'
|
||||||
|
0: 'No'
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
header.autoconnect:
|
||||||
|
type: toggle
|
||||||
|
label: Connect markers
|
||||||
|
help: 'Draw connector lines between location markers (suppressed where GPX covers the route)'
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: 'Yes'
|
||||||
|
0: 'No'
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
|
||||||
publishing:
|
publishing:
|
||||||
type: tab
|
type: tab
|
||||||
title: Publishing
|
title: Publishing
|
||||||
|
|||||||
@@ -223,8 +223,13 @@
|
|||||||
* - GPX present, pair NOT covered by any single file → connector drawn
|
* - GPX present, pair NOT covered by any single file → connector drawn
|
||||||
* - force_connect on arriving entry → always draw connector
|
* - force_connect on arriving entry → always draw connector
|
||||||
*/
|
*/
|
||||||
function buildJourneySegments(entries, allTrackpoints, thresholdKm) {
|
/*
|
||||||
thresholdKm = thresholdKm || 10;
|
* opts.autoconnect (bool, default true): when false, only entries with
|
||||||
|
* force_connect:true are connected — all other pairs are left unconnected.
|
||||||
|
*/
|
||||||
|
function buildJourneySegments(entries, allTrackpoints, thresholdKm, opts) {
|
||||||
|
thresholdKm = thresholdKm || 10;
|
||||||
|
var autoconnect = !opts || opts.autoconnect !== false;
|
||||||
var hasGpx = allTrackpoints && allTrackpoints.length > 0;
|
var hasGpx = allTrackpoints && allTrackpoints.length > 0;
|
||||||
var segments = [];
|
var segments = [];
|
||||||
var current = [];
|
var current = [];
|
||||||
@@ -241,7 +246,11 @@
|
|||||||
var prev = entries[i - 1];
|
var prev = entries[i - 1];
|
||||||
var connect;
|
var connect;
|
||||||
|
|
||||||
if (!hasGpx || e.force_connect) {
|
if (e.force_connect) {
|
||||||
|
connect = true;
|
||||||
|
} else if (!autoconnect) {
|
||||||
|
connect = false;
|
||||||
|
} else if (!hasGpx) {
|
||||||
connect = true;
|
connect = true;
|
||||||
} else {
|
} else {
|
||||||
var pLat = parseFloat(prev.lat);
|
var pLat = parseFloat(prev.lat);
|
||||||
@@ -295,7 +304,7 @@
|
|||||||
* When gpxUrls is empty, Promise.all resolves immediately → no GPX layers,
|
* When gpxUrls is empty, Promise.all resolves immediately → no GPX layers,
|
||||||
* buildJourneySegments draws a full connector line between all entries.
|
* buildJourneySegments draws a full connector line between all entries.
|
||||||
*/
|
*/
|
||||||
function renderGpxJourney(map, gpxUrls, entries, gpxSourcePrefix, journeySourceId) {
|
function renderGpxJourney(map, gpxUrls, entries, gpxSourcePrefix, journeySourceId, opts) {
|
||||||
Promise.all(gpxUrls.map(function (url, idx) {
|
Promise.all(gpxUrls.map(function (url, idx) {
|
||||||
return fetch(url)
|
return fetch(url)
|
||||||
.then(function (r) { return r.text(); })
|
.then(function (r) { return r.text(); })
|
||||||
@@ -314,7 +323,7 @@
|
|||||||
.catch(function (err) { console.warn('GPX load failed:', url, err); return []; });
|
.catch(function (err) { console.warn('GPX load failed:', url, err); return []; });
|
||||||
})).then(function (allTrackpoints) {
|
})).then(function (allTrackpoints) {
|
||||||
var valid = allTrackpoints.filter(function (tp) { return tp.length > 0; });
|
var valid = allTrackpoints.filter(function (tp) { return tp.length > 0; });
|
||||||
var segments = buildJourneySegments(entries, valid, 10);
|
var segments = buildJourneySegments(entries, valid, 10, opts);
|
||||||
addJourneySegments(map, segments, journeySourceId);
|
addJourneySegments(map, segments, journeySourceId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,8 @@
|
|||||||
<script>
|
<script>
|
||||||
var FEED_ENTRIES = {{ map_entries|json_encode|raw }};
|
var FEED_ENTRIES = {{ map_entries|json_encode|raw }};
|
||||||
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
||||||
|
var USE_GPX = {{ trip_page.header.use_gpx ?? true ? 'true' : 'false' }};
|
||||||
|
var AUTOCONNECT = {{ trip_page.header.autoconnect ?? true ? 'true' : 'false' }};
|
||||||
|
|
||||||
var feedMap = new maplibregl.Map({
|
var feedMap = new maplibregl.Map({
|
||||||
container: 'feed-map',
|
container: 'feed-map',
|
||||||
@@ -87,7 +89,7 @@ feedMap.on('load', function () {
|
|||||||
feedMap.fitBounds(bounds, { padding: 60, maxZoom: 11 });
|
feedMap.fitBounds(bounds, { padding: 60, maxZoom: 11 });
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(GPX_URLS.map(function (url, idx) {
|
Promise.all((USE_GPX ? GPX_URLS : []).map(function (url) {
|
||||||
return fetch(url)
|
return fetch(url)
|
||||||
.then(function (r) { return r.text(); })
|
.then(function (r) { return r.text(); })
|
||||||
.then(function (text) {
|
.then(function (text) {
|
||||||
@@ -101,7 +103,7 @@ feedMap.on('load', function () {
|
|||||||
});
|
});
|
||||||
})).then(function (allTrackpoints) {
|
})).then(function (allTrackpoints) {
|
||||||
var validTrackpoints = allTrackpoints.filter(function (tp) { return tp.length > 0; });
|
var validTrackpoints = allTrackpoints.filter(function (tp) { return tp.length > 0; });
|
||||||
var segments = MapUtils.buildJourneySegments(FEED_ENTRIES, validTrackpoints, 10);
|
var segments = MapUtils.buildJourneySegments(FEED_ENTRIES, validTrackpoints, 10, { autoconnect: AUTOCONNECT });
|
||||||
MapUtils.addJourneySegments(feedMap, segments, 'feed-journey');
|
MapUtils.addJourneySegments(feedMap, segments, 'feed-journey');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,6 +151,8 @@
|
|||||||
<script>
|
<script>
|
||||||
var HOME_ENTRIES = {{ map_entries|json_encode|raw }};
|
var HOME_ENTRIES = {{ map_entries|json_encode|raw }};
|
||||||
var HOME_GPX_URLS = {{ home_gpx_urls|json_encode|raw }};
|
var HOME_GPX_URLS = {{ home_gpx_urls|json_encode|raw }};
|
||||||
|
var USE_GPX = {{ trip and trip.header.use_gpx is not null ? (trip.header.use_gpx ? 'true' : 'false') : 'true' }};
|
||||||
|
var AUTOCONNECT = {{ trip and trip.header.autoconnect is not null ? (trip.header.autoconnect ? 'true' : 'false') : 'true' }};
|
||||||
|
|
||||||
var homeMap = new maplibregl.Map({
|
var homeMap = new maplibregl.Map({
|
||||||
container: 'home-map',
|
container: 'home-map',
|
||||||
@@ -192,7 +194,7 @@ homeMap.on('load', function () {
|
|||||||
|
|
||||||
setTimeout(function () { homeMap.resize(); }, 100);
|
setTimeout(function () { homeMap.resize(); }, 100);
|
||||||
|
|
||||||
MapUtils.renderGpxJourney(homeMap, HOME_GPX_URLS, HOME_ENTRIES, 'home-gpx', 'home-journey');
|
MapUtils.renderGpxJourney(homeMap, USE_GPX ? HOME_GPX_URLS : [], HOME_ENTRIES, 'home-gpx', 'home-journey', { autoconnect: AUTOCONNECT });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -42,8 +42,10 @@
|
|||||||
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var ENTRIES = {{ map_entries|json_encode|raw }};
|
var ENTRIES = {{ map_entries|json_encode|raw }};
|
||||||
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
||||||
|
var USE_GPX = {{ trip_page.header.use_gpx ?? true ? 'true' : 'false' }};
|
||||||
|
var AUTOCONNECT = {{ trip_page.header.autoconnect ?? true ? 'true' : 'false' }};
|
||||||
|
|
||||||
var map = new maplibregl.Map({
|
var map = new maplibregl.Map({
|
||||||
container: 'trip-map',
|
container: 'trip-map',
|
||||||
@@ -92,7 +94,7 @@ map.on('load', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── GPX tracks + journey segments ─────────────────────────── */
|
/* ── GPX tracks + journey segments ─────────────────────────── */
|
||||||
MapUtils.renderGpxJourney(map, GPX_URLS, ENTRIES, 'gpx', 'journey');
|
MapUtils.renderGpxJourney(map, USE_GPX ? GPX_URLS : [], ENTRIES, 'gpx', 'journey', { autoconnect: AUTOCONNECT });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -298,7 +298,9 @@
|
|||||||
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
var TRIP_ENTRIES = {{ map_entries|json_encode|raw }};
|
var TRIP_ENTRIES = {{ map_entries|json_encode|raw }};
|
||||||
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
||||||
|
var USE_GPX = {{ page.header.use_gpx ?? true ? 'true' : 'false' }};
|
||||||
|
var AUTOCONNECT = {{ page.header.autoconnect ?? true ? 'true' : 'false' }};
|
||||||
|
|
||||||
var tripMap = new maplibregl.Map({
|
var tripMap = new maplibregl.Map({
|
||||||
container: 'trip-map',
|
container: 'trip-map',
|
||||||
@@ -349,30 +351,7 @@ tripMap.on('load', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── GPX tracks + journey segments ─────────────────────────── */
|
/* ── GPX tracks + journey segments ─────────────────────────── */
|
||||||
Promise.all(GPX_URLS.map(function (url, idx) {
|
MapUtils.renderGpxJourney(tripMap, USE_GPX ? GPX_URLS : [], TRIP_ENTRIES, 'gpx', 'trip-journey', { autoconnect: AUTOCONNECT });
|
||||||
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;
|
|
||||||
tripMap.addSource(sid, { type: 'geojson', data: geojson });
|
|
||||||
tripMap.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(TRIP_ENTRIES, validTrackpoints, 10);
|
|
||||||
MapUtils.addJourneySegments(tripMap, segments, 'trip-journey');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
setTimeout(function () { tripMap.resize(); }, 100);
|
setTimeout(function () { tripMap.resize(); }, 100);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user