baldav/docs/plans/2026-10-26-elevation-slope-brouter.md

10 KiB
Raw Blame History

План: точность высот/уклона в предпросмотре + безопасность маршрутов (brouter)

Context

Пять замечаний по предпросмотру маршрута и маршрутизации (baldav):

  1. Режим «уклон» врёт: рисует крутой подъём там, где его нет (и наоборот). Высоты из DEM квантуются по ячейкам 30м, точки внутри ячейки имеют одну высоту, на границе - скачок, и вся Δ падает на один короткий сегмент = ложный крутой уклон. (Второе - «не рисует где есть» - предел разрешения SRTM 30м, не чинится без более точного DEM.)
  2. Спуски красить иначе: сейчас уклон красится по МОДУЛЮ (вверх и вниз одинаково). Спуски по направлению движения должны уходить из зелёного в голубой -> navy.
  3. brouter трижды завёл к бродам/ледяной воде (ford - теги OSM в данных brouter, не SRTM). Нужно: избегать бродов/сложных троп/крутых спусков по умолчанию, ПОКАЗЫВАТЬ оставшиеся опасные участки на предпросмотре заранее, и дать опцию «разрешить (испытать удачу)». Полный объём.
  4. Кнопка объезда ↪ на записи срабатывает всегда и ведёт «к первому вейпоинту»: нет проверки, что впереди реально есть затык, а цель выбирается по глобально ближайшей точке маршрута (ломается на петлях и вдали от маршрута).
  5. Уточнять высоты по многим проездам. Решение пользователя: база - SRTM, медиана высот из треков берётся ТОЛЬКО как поправка вверх/вниз к SRTM (не замена), там где катал.

Общее: раскраска уклон/высота и расчёт уклона - в ОДНИХ функциях, используются в предпросмотре маршрута, детали записанного трека, редакторе и на экране записи. Правка общих функций чинит везде; проверить надо каждое место (WT4: починил A - проверь B и C в той же области).

Критичные файлы и функции

  • frontend/js/gps.js: GPS.routeElevStats (расчёт grades[]/smoothed[]), GPS.filterAndSmoothAlt (сглаживание высот - сейчас по ТОЧКАМ, окно 5), GPS.fillGaps/get_ride_graph (сетка ~20м - база для п.5).
  • frontend/js/main.js: routeSlopeColor + ROUTE_SLOPE_BANDS + slopeLegendHtml (п.2), drawRoutePolyline/showRouteDetail (предпросмотр), rejoinReroute (п.4), fetchElevations (п.5).
  • src-tauri/gen/android/app/src/main/assets/brouter/profiles2/trekking.brf (п.3, избегание) + копия в tools/brouter-web-staging/profiles2/trekking.brf (единый источник - править обе).
  • MainActivity.kt routeBrouter (п.3, вытащить теги путей из OsmTrack.messageList рядом с GPX).
  • src-tauri/src/lib.rs get_ride_graph/новый агрегат высот (п.5).

Подход по пунктам

П.1 - уклон по дистанционному окну (вместо ступенек ячейки)

В GPS.routeElevStats считать grades[i] как центральную разность высоты по РАССТОЯНИЮ окном ~30м (одна ячейка DEM), а не между соседними точками: для точки i найти вперёд j и назад k так, чтобы суммарная дистанция >= W (конфиг gradeWindowM, дефолт 30), grade = (smoothed[j]-smoothed[k])/dist. Так одиночная ступенька ячейки размазывается по всей ячейке. maxGrade и профиль - по тем же сглаженным. «Не рисует где есть» - записать как предел разрешения SRTM; реально улучшает п.5.

П.2 - направленная раскраска спусков

routeSlopeColor(gradePct) сделать знакочувствительной: подъём (>=0) - тёплый веер (как сейчас green->yellow->red->brown->black), спуск (<0) - холодный веер green->cyan->blue->navy по модулю. Два массива полос + обновить slopeLegendHtml (две шкалы: подъём/спуск). Направление = порядок точек (он же направление движения в треке/маршруте).

П.3 - brouter: избегать + показывать + опция

  • Профиль trekking.brf: добавить штрафы (через assign в ---context:way) на ford=yes, пересечение waterway без моста, tracktype=grade4|grade5, trail_visibility=bad|horrible, высокий sac_scale/mtb:scale; крутой спуск уже есть через downhillcost. Параметр %allow_adventure% (дефолт false) - при true штрафы снимаются («испытать удачу»).
  • Показ опасного: в routeBrouter рядом с GPX вернуть JSON тегов сегментов из OsmTrack.messageList (btools даёт теги путей); нужен короткий спайк - проверить, что messageList заполняется и какие поля. На превью (drawRoutePolyline/showRouteDetail) пометить участки ford/вода/крутой спуск отдельным стилем + счётчик «броды: N, крутые спуски: M». Тумблер «разрешить броды/сложные тропы» (localStorage + %allow_adventure% в вызов BRouter).
  • Если messageList тегов не отдаёт - запасной путь: крутые спуски берём из уклона (уже есть), броды - пометить честно как «не выяснено без тегов» и решить отдельно. Спайк решает.

П.4 - объезд ↪ только при реальном затыке впереди

В rejoinReroute: (а) спроецировать текущую позицию на маршрут -> индекс прогресса (не глобально ближайший - ломается на петлях), учитывать направление движения; (б) объезд строить ТОЛЬКО если есть nogo на маршруте ВПЕРЕДИ в пределах окна (напр. до +1.5км по маршруту); иначе toast «впереди затыка нет» и ничего не строить. Цель rejoin - первая точка маршрута за nogo + запас.

П.5 - поправка высот по проездам (база SRTM + медиана как сдвиг)

  • Rust (lib.rs, рядом с get_ride_graph): агрегат по сетке ~20-30м из gps_points.altitude: на ячейку median(alt) и count. Новый command, напр. get_elev_correction.
  • JS (fetchElevations/routeElevStats): после получения SRTM-высот, где у ячейки count>=порога, скорректировать: elev = SRTM + smooth(median_ridden - SRTM). SRTM остаётся базой и формой; треки только двигают вверх/вниз где катал. Порог проездов и сглаживание сдвига - в конфиг.
  • GPS-высота шумная -> median по многим точкам/проездам + отбрасывание выбросов; барометр DJI (отдельный TODO) улучшит позже.

Порядок и коммиты

Пункты независимы. Делать по одному, коммит на каждый (commit-before-test), сборка через ./build.sh -d -i android, проверка на телефоне; follow-up фиксы - отдельными коммитами. Рекомендуемый порядок: 2 (быстрый, визуальный) -> 1 -> 4 -> 5 -> 3 (самый большой, со спайком).

Проверка (на устройстве + где можно на ПК)

  • П.1/П.2: открыть предпросмотр реального маршрута с известным рельефом; переключить уклон/высота; убедиться, что крутой цвет только на реально крутом, спуски холодные. Вёрстку/цвета можно глянуть на ПК в dev (reference_verify_record_screen). Проверить ВСЕ места раскраски: превью маршрута, деталь записанного трека, редактор, экран записи.
  • П.3: построить маршрут через известный брод/грунтовку -> по умолчанию объезжает; включить «разрешить» -> ведёт через, но помечено на превью. Спайк messageList - логом из routeBrouter.
  • П.4: на записи вдали от затыка нажать ↪ -> «впереди затыка нет»; поставить nogo на маршруте впереди -> объезд строится за него, не к старту.
  • П.5: взять ячейки, где много проездов, сравнить профиль до/после коррекции; где не катал - SRTM.