// Navi-Erst-Fix bei RUNDEN: der Startindex darf nicht ans Track-Ende springen. // // Spiegelt die _closestIdx-Erst-Fix-Logik aus js/pages/routes.js (_startNav). An einem // Start/Ende-Knoten einer Runde ist der ENDPUNKT oft ein paar Meter näher als der // Startpunkt; die alte globale Suche sprang dann sofort ans Track-Ende → 100 % / 0 km ab // Sekunde 1 (Angie, Deining-Runde 09.06.2026). Bei Änderung BEIDE Stellen anpassen. // // Hinweis: bewusst eine Nachbildung — die echte Funktion ist eine Closure in _startNav // und nicht exportierbar, ohne routes.js umzubauen. const _haversineKm = (lat1, lon1, lat2, lon2) => { const R = 6371, dLat = (lat2 - lat1) * Math.PI / 180, dLon = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2; return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); }; // Erst-Fix-Index für gegebenen track + Userposition (1:1 aus routes.js). function firstFixIdx(track, lat, lon) { const search = (from, to) => { let best = from, bestD = Infinity; for (let i = from; i <= to; i++) { const d = _haversineKm(lat, lon, track[i].lat, track[i].lon); if (d < bestD) { bestD = d; best = i; } } return { best, bestD }; }; const isLoop = track.length > 2 && _haversineKm(track[0].lat, track[0].lon, track[track.length - 1].lat, track[track.length - 1].lon) < 0.06; const g = search(0, track.length - 1); if (isLoop) { const win = Math.min(track.length - 1, Math.max(30, Math.floor(track.length * 0.15))); const s = search(0, win); return { idx: s.bestD < 0.15 ? s.best : g.best, isLoop }; } const s = search(0, Math.min(track.length - 1, 30)); return { idx: (s.bestD - g.bestD) * 1000 < 25 ? s.best : g.best, isLoop }; } // Die ALTE Logik (vor dem Fix) — nur zum Beweis, dass der Fix wirklich etwas ändert. function firstFixIdxOld(track, lat, lon) { const search = (from, to) => { let best = from, bestD = Infinity; for (let i = from; i <= to; i++) { const d = _haversineKm(lat, lon, track[i].lat, track[i].lon); if (d < bestD) { bestD = d; best = i; } } return { best, bestD }; }; const g = search(0, track.length - 1); const s = search(0, Math.min(track.length - 1, 30)); return (s.bestD - g.bestD) * 1000 < 25 ? s.best : g.best; } // --- Synthetische Deining-artige Runde ------------------------------------- const C = { lat: 48.07, lon: 11.50 }; const mLat = m => m / 111320; const mLon = (m, lat) => m / (111320 * Math.cos(lat * Math.PI / 180)); // Punkt auf einem Kreis: Winkel von Nord, im Uhrzeigersinn. const onCircle = (deg, r) => { const rad = deg * Math.PI / 180; return { lat: C.lat + mLat(r * Math.cos(rad)), lon: C.lon + mLon(r * Math.sin(rad), C.lat) }; }; const N = 40, R = 80; // 40 Punkte auf 80-m-Kreis, lange Runde von 0°→329° const track = []; for (let i = 0; i < N; i++) track.push(onCircle(i / (N - 1) * 329, R)); // User steht 3 m außerhalb des ENDpunkts (329°) → näher am Ende als am Start. const user = onCircle(329, R + 3); const startEndM = _haversineKm(track[0].lat, track[0].lon, track[N - 1].lat, track[N - 1].lon) * 1000; const dStart = _haversineKm(user.lat, user.lon, track[0].lat, track[0].lon) * 1000; const dEnd = _haversineKm(user.lat, user.lon, track[N - 1].lat, track[N - 1].lon) * 1000; console.log(`Runde: Start↔Ende ${startEndM.toFixed(0)} m | User→Start ${dStart.toFixed(0)} m, User→Ende ${dEnd.toFixed(0)} m`); // 1. Loop wird erkannt (Start ≈ Ende < 60 m) const res = firstFixIdx(track, user.lat, user.lon); if (!res.isLoop) throw new Error('Runde nicht als Loop erkannt'); // 2. Erst-Fix landet im STARTbereich, NICHT am Track-Ende console.log('Erst-Fix-Index:', res.idx, '(von', N - 1 + ')'); if (res.idx > Math.floor(N * 0.15)) throw new Error(`Erst-Fix sprang weg vom Start (idx ${res.idx})`); // 3. Beweis: die alte Logik wäre hier ans Ende gesprungen (100 %) const old = firstFixIdxOld(track, user.lat, user.lon); console.log('Alte Logik-Index:', old); if (old !== N - 1) throw new Error('Erwartet: alte Logik springt ans Ende — Testfall trifft den Bug nicht mehr'); // 4. Punkt-zu-Punkt-Route (kein Loop): User am Start → 0 %, am Ende → bleibt sinnvoll const ptp = []; for (let i = 0; i < N; i++) ptp.push({ lat: C.lat + mLat(i * 25), lon: C.lon }); // 25-m-Schritte nach Norden const ptpRes = firstFixIdx(ptp, ptp[0].lat, ptp[0].lon); if (ptpRes.isLoop) throw new Error('Gerade Route fälschlich als Loop erkannt'); if (ptpRes.idx !== 0) throw new Error(`Punkt-zu-Punkt am Start sollte idx 0 sein, war ${ptpRes.idx}`); console.log('\nALLE NAV-LOOP-TESTS BESTANDEN');