diff --git a/frontend/js/main.js b/frontend/js/main.js index 8d47408..5b8b215 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -3603,25 +3603,54 @@ function _offsetLatLon(lat, lon, brDeg, d) { // sits on the route (<=150 m), else the current position's nearest route point. Target = a // route point ~1.5 km further along; on failure step further (up to the route end). // Returns { points:[{lat,lon}], target:{lat,lon} } or null. +// Reroute back onto the active route PAST a blockage ahead. Only acts when a nogo +// actually sits on the route ahead of the current progress point (in the travel +// direction) — otherwise returns {noBlockage:true} and builds nothing (pressing it +// with no blockage used to route to a bogus far point). Returns {points,target} on +// success, null if a detour couldn't be built, {noBlockage:true} if nothing to do. async function rejoinReroute(cur, bearingDeg, routePts, nogos) { if (!cur || !routePts || routePts.length < 2) return null; let cum = [0]; for (let i = 1; i < routePts.length; i++) cum[i] = cum[i-1] + GPS.distance(routePts[i-1].lat, routePts[i-1].lon, routePts[i].lat, routePts[i].lon); - let nearestRouteDist = (pt) => { let d = Infinity; for (let p of routePts) { let dd = GPS.distance(pt.lat, pt.lon, p.lat, p.lon); if (dd < d) d = dd; } return d; }; - // reference point of the blockage - let ref = cur; - if (nogos && nogos.length) { - let best = null, bestD = Infinity; - for (let n of nogos) if (nearestRouteDist(n) <= 150) { let dc = GPS.distance(cur.lat, cur.lon, n.lat, n.lon); if (dc < bestD) { bestD = dc; best = n; } } - if (best) ref = best; + let inNogo = (p) => nogos && nogos.some(n => GPS.distance(p.lat, p.lon, n.lat, n.lon) <= (n.radius || 25) + 15); + + // current progress = nearest route point to the current position + let curIdx = 0, curD = Infinity; + for (let i = 0; i < routePts.length; i++) { let d = GPS.distance(cur.lat, cur.lon, routePts[i].lat, routePts[i].lon); if (d < curD) { curD = d; curIdx = i; } } + + // travel direction along the route: increasing index unless the compass bearing + // clearly points the other way (handles routes ridden in reverse / loops). + let step = 1; + if (bearingDeg != null && !isNaN(bearingDeg)) { + let ja = Math.min(routePts.length - 1, curIdx + 1), jb = Math.max(0, curIdx - 1); + if (ja !== jb) { + let fb = GPS.bearing(routePts[jb].lat, routePts[jb].lon, routePts[ja].lat, routePts[ja].lon); + let diff = Math.abs(((bearingDeg - fb + 540) % 360) - 180); // 0 = aligned w/ increasing idx, 180 = opposite + if (diff > 90) step = -1; // travelling against increasing index → forward is decreasing idx + } } - let refIdx = 0, refD = Infinity; - for (let i = 0; i < routePts.length; i++) { let d = GPS.distance(ref.lat, ref.lon, routePts[i].lat, routePts[i].lon); if (d < refD) { refD = d; refIdx = i; } } + + // blockage: first route point within a nogo AHEAD, within a lookahead window + const LOOKAHEAD = 2500; + let blocked = null; + for (let i = curIdx; i >= 0 && i < routePts.length; i += step) { + if (Math.abs(cum[i] - cum[curIdx]) > LOOKAHEAD) break; + if (inNogo(routePts[i])) { blocked = i; break; } + } + if (blocked == null) return { noBlockage: true }; + + // rejoin target: first route point clear of every nogo AND >= margin past the blockage + const CLEAR_MARGIN = 300; + let ti = blocked; + while (ti >= 0 && ti < routePts.length) { + if (!inNogo(routePts[ti]) && Math.abs(cum[ti] - cum[blocked]) >= CLEAR_MARGIN) break; + ti += step; + } + if (ti < 0 || ti >= routePts.length) ti = step > 0 ? routePts.length - 1 : 0; + let helper = (bearingDeg != null && !isNaN(bearingDeg)) ? _offsetLatLon(cur.lat, cur.lon, bearingDeg, 40) : null; - let skip = 1500; - for (let attempt = 0; attempt < 8; attempt++) { - let want = cum[refIdx] + skip, ti = refIdx; - while (ti < routePts.length - 1 && cum[ti] < want) ti++; + let jump = Math.max(1, Math.round(routePts.length / 50)); + for (let attempt = 0; attempt < 6; attempt++) { let target = routePts[ti]; let straight = GPS.distance(cur.lat, cur.lon, target.lat, target.lon); let wps = helper ? [[cur.lon, cur.lat], [helper.lon, helper.lat], [target.lon, target.lat]] : [[cur.lon, cur.lat], [target.lon, target.lat]]; @@ -3633,8 +3662,9 @@ async function rejoinReroute(cur, bearingDeg, routePts, nogos) { if (rlen <= Math.max(500, straight * 3)) return { points: rp.map(p => ({ lat: p.lat, lon: p.lon })), target: { lat: target.lat, lon: target.lon } }; } } catch (e) { /* try a further rejoin point */ } - if (ti >= routePts.length - 1) break; - skip += 1500; + let next = ti + step * jump; + if (next < 0 || next >= routePts.length) break; + ti = next; } return null; } @@ -5132,6 +5162,7 @@ async function renderRecordingInline(container) { let ng = await getNogosForRouter(); let bearing = (typeof smoothedHeading === "number") ? smoothedHeading : null; let res = await rejoinReroute(cur, bearing, route.points, ng); + if (res && res.noBlockage) { showToast("Впереди затыка нет (нет nogo на маршруте)"); rerouteBtn.classList.remove("active"); return; } if (!res) { showToast("Объезд не найден"); rerouteBtn.classList.remove("active"); return; } if (rerouteLayer) { map.removeLayer(rerouteLayer); rerouteLayer = null; } let ll = res.points.map(p => [p.lat, p.lon]);