From 9809950347cce3c9f29f6aee81502ccdb3163875 Mon Sep 17 00:00:00 2001 From: Mischa Date: Sun, 21 Jun 2026 11:38:50 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20simplify=20connector=20logic=20?= =?UTF-8?q?=E2=80=94=20remove=20GPX=20proximity=20suppression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit autoconnect:true now connects every consecutive entry pair in chronological order; the old proximity check (suppress where GPX covers the route) is removed entirely. - buildJourneySegments: drops allTrackpoints/thresholdKm params; logic is now force_connect || autoconnect (binary, no GPX math) - renderGpxJourney: no longer extracts trackpoints; just renders visual GPX layers then calls buildJourneySegments - dailies.html.twig: removes GPX URL collection, toGeoJSON CDN load, and the Promise.all — connectors are now synchronous - extractTrackpoints/isNearTrack/haversineKm removed (dead code) - blueprint help text updated Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr --- themes/intotheeast/blueprints/trip.yaml | 2 +- themes/intotheeast/js/maplibre-utils.js | 141 +++--------------- .../intotheeast/templates/dailies.html.twig | 29 +--- 3 files changed, 25 insertions(+), 147 deletions(-) diff --git a/themes/intotheeast/blueprints/trip.yaml b/themes/intotheeast/blueprints/trip.yaml index 471c26c..1ff7576 100644 --- a/themes/intotheeast/blueprints/trip.yaml +++ b/themes/intotheeast/blueprints/trip.yaml @@ -70,7 +70,7 @@ form: header.autoconnect: type: toggle label: Connect markers - help: 'Draw connector lines between location markers (suppressed where GPX covers the route)' + help: 'Draw connector lines between all location markers in chronological order' highlight: 1 default: 1 options: diff --git a/themes/intotheeast/js/maplibre-utils.js b/themes/intotheeast/js/maplibre-utils.js index a46f0a8..4db9c57 100644 --- a/themes/intotheeast/js/maplibre-utils.js +++ b/themes/intotheeast/js/maplibre-utils.js @@ -152,127 +152,35 @@ return el; } - /* ── GPX connector algorithm ────────────────────────────────────────── */ - - /* Haversine distance in km between two [lat, lng] points */ - function haversineKm(lat1, lng1, lat2, lng2) { - var R = 6371; - var dLat = (lat2 - lat1) * Math.PI / 180; - var dLng = (lng2 - lng1) * Math.PI / 180; - var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * - Math.sin(dLng / 2) * Math.sin(dLng / 2); - return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - } - /* - * Extract trackpoints from a toGeoJSON output. - * Returns [[lat, lng], ...] — latitude first (internal convention). - * GeoJSON coordinates are [lng, lat]; we flip them here. - */ - function extractTrackpoints(geojson) { - var points = []; - (geojson.features || []).forEach(function (feat) { - var coords = []; - if (feat.geometry.type === 'LineString') { - coords = feat.geometry.coordinates; - } else if (feat.geometry.type === 'MultiLineString') { - feat.geometry.coordinates.forEach(function (line) { - coords = coords.concat(line); - }); - } - coords.forEach(function (c) { points.push([c[1], c[0]]); }); // [lng,lat] → [lat,lng] - }); - return points; - } - - /* - * Check whether a marker is within thresholdKm of any trackpoint in the array. - * trackpoints: [[lat, lng], ...] (internal convention, latitude first). - * Samples every 10th point for performance; always checks the last point. - */ - function isNearTrack(markerLat, markerLng, trackpoints, thresholdKm) { - if (!trackpoints || trackpoints.length === 0) return false; - var degLat = thresholdKm / 111; - var degLng = thresholdKm / (111 * Math.cos(markerLat * Math.PI / 180)); - for (var i = 0; i < trackpoints.length; i += 10) { - var pt = trackpoints[i]; - if (Math.abs(pt[0] - markerLat) > degLat || Math.abs(pt[1] - markerLng) > degLng) continue; - if (haversineKm(markerLat, markerLng, pt[0], pt[1]) <= thresholdKm) return true; - } - // Always check the last point (may be skipped by stride=10). - // Note: per-point degree pre-filter in the loop is functionally equivalent - // to a per-file bounding-box skip at this data scale. - var last = trackpoints[trackpoints.length - 1]; - return haversineKm(markerLat, markerLng, last[0], last[1]) <= thresholdKm; - } - - /* - * Build journey line segments from entries and GPX trackpoints. + * Build journey line segments from entries. * - * entries: [{lat, lng, force_connect}, ...] in chronological order - * allTrackpoints: [ [[lat,lng],...], ... ] — one sub-array per GPX file - * thresholdKm: proximity radius (default 10) + * entries: [{lat, lng, force_connect?}, ...] in chronological order + * opts.autoconnect (bool, default true): + * true → connect every consecutive pair (simple chronological line) + * false → connect only entries where force_connect:true * - * Returns array of segments, each segment being [[lng, lat], ...] in MapLibre - * coordinate order. A segment with < 2 points is omitted. - * - * Rules: - * - No GPX files → all adjacent pairs connected (one segment) - * - GPX present, pair covered by same file → connector suppressed - * - GPX present, pair NOT covered by any single file → connector drawn - * - force_connect on arriving entry → always draw connector + * Returns array of segments [[lng, lat], ...] in MapLibre coordinate order. + * Segments with < 2 points are omitted. */ - /* - * 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; + function buildJourneySegments(entries, opts) { var autoconnect = !opts || opts.autoconnect !== false; - var hasGpx = allTrackpoints && allTrackpoints.length > 0; - var segments = []; - var current = []; + var segments = []; + var current = []; for (var i = 0; i < entries.length; i++) { var e = entries[i]; - var lngLat = [parseFloat(e.lng), parseFloat(e.lat)]; // MapLibre: [lng, lat] + var lngLat = [parseFloat(e.lng), parseFloat(e.lat)]; - if (i === 0) { - current.push(lngLat); - continue; - } + if (i === 0) { current.push(lngLat); continue; } - var prev = entries[i - 1]; - var connect; - - if (e.force_connect) { - connect = true; - } else if (!autoconnect) { - connect = false; - } else if (!hasGpx) { - connect = true; - } else { - var pLat = parseFloat(prev.lat); - var pLng = parseFloat(prev.lng); - var cLat = parseFloat(e.lat); - var cLng = parseFloat(e.lng); - var covered = false; - for (var f = 0; f < allTrackpoints.length; f++) { - if (isNearTrack(pLat, pLng, allTrackpoints[f], thresholdKm) && - isNearTrack(cLat, cLng, allTrackpoints[f], thresholdKm)) { - covered = true; - break; - } - } - connect = !covered; - } + var connect = e.force_connect || autoconnect; if (connect) { current.push(lngLat); } else { if (current.length >= 2) segments.push(current); - current = [lngLat]; // start new segment from this point + current = [lngLat]; } } @@ -293,16 +201,14 @@ } /* - * Fetch GPX files, render their raw tracks, then draw journey connector lines - * only between entries not covered by any GPX file (connector suppression). + * Fetch GPX files and render their raw tracks, then draw journey connector + * lines between entries per opts.autoconnect / force_connect. * - * gpxUrls: array of GPX file URLs to fetch - * entries: [{lat, lng, force_connect?}, ...] in chronological order + * gpxUrls: array of GPX file URLs to fetch (empty → no tracks, connectors only) + * 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. + * opts: forwarded to buildJourneySegments (opts.autoconnect) */ function renderGpxJourney(map, gpxUrls, entries, gpxSourcePrefix, journeySourceId, opts) { Promise.all(gpxUrls.map(function (url, idx) { @@ -318,12 +224,10 @@ 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, opts); + .catch(function (err) { console.warn('GPX load failed:', url, err); }); + })).then(function () { + var segments = buildJourneySegments(entries, opts); addJourneySegments(map, segments, journeySourceId); }); } @@ -334,7 +238,6 @@ addJourneyLine: addJourneyLine, addJourneySegments: addJourneySegments, buildJourneySegments: buildJourneySegments, - extractTrackpoints: extractTrackpoints, renderGpxJourney: renderGpxJourney, createDotMarker: createDotMarker, createStoryMarker: createStoryMarker diff --git a/themes/intotheeast/templates/dailies.html.twig b/themes/intotheeast/templates/dailies.html.twig index 6602a43..5948e02 100644 --- a/themes/intotheeast/templates/dailies.html.twig +++ b/themes/intotheeast/templates/dailies.html.twig @@ -31,14 +31,7 @@ {% endif %} {% endfor %} -{# Collect GPX URLs from parent trip page for connector algorithm #} {% set trip_page = page.parent() %} -{% set gpx_urls = [] %} -{% for name, media in trip_page.media.all %} - {% if name|split('.')|last == 'gpx' %} - {% set gpx_urls = gpx_urls|merge([trip_page.url ~ '/' ~ name]) %} - {% endif %} -{% endfor %} {% if map_entries|length > 0 %}
@@ -48,12 +41,9 @@ - {% endif %}