baldav/IMPLEMENTATION_PLAN.md
4aiman a52e998fb6 BRouter: analysis + embed the offline routing engine (Stage 1)
Analysis in docs/brouter-findings.md: how BRouter knows roads (.rd5
segments), routes and reaches low elevation gain (profile uphill/downhill
cost + kinematics), the three call surfaces, and an evaluation of two ideas
— fill GPS gaps along roads, and offline waypoint route planning. License is
MIT; the engine has no desktop-only deps, so it embeds on Android ART.

Stage 1 (embed + bridge, compiles into the apk; on-device route not yet run):
- vendored brouter-1.6.3-all.jar into the Android module (dexes cleanly) and
  bundled the trekking/shortest profiles + lookups.dat as assets, copied to
  files/brouter/profiles2 on launch
- MainActivity: a brouter_command.txt handler on the existing FileObserver
  runs btools.router.RoutingEngine off the UI thread (recipe taken via javap
  from ServerHandler) and returns the track as GPX
- Rust brouter_route command: same file bridge as GPS (request + command,
  poll for the result), registered in the invoke handler
- frontend/js/router.js: BRouter.route(points, profile) -> GPX (parseRouteGPX)
- LICENSE-brouter (MIT notice) kept alongside the vendored engine

Segments (.rd5, ~13-23 MB per 5x5 tile) are delivered later (Stage 2, wired
download inside BalDav). IMPLEMENTATION_PLAN.md tracks the 5 stages.
2026-09-09 00:56:13 +03:00

79 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# IMPLEMENTATION_PLAN: встраивание BRouter в BalDav
Основано на `docs/brouter-findings.md`. Цель — оффлайн-роутинг внутри BalDav (движок BRouter
встроен в Android-модуль, `btools.router.RoutingEngine` в процессе, MIT). Две прикладные фичи:
идея 1 (заполнение GPS-пропусков по дороге) и идея 2 (оффлайн-планирование маршрута по точкам).
Ключевые решения: встраивание (вариант B); сегменты `.rd5` качаются по проводу внутри BalDav в
app data dir; профиль trekking (идея 2) и shortest (идея 1); лицензия MIT (положить `LICENSE-brouter`).
Мост как у GPS-сервиса: JS → Rust `invoke` → Kotlin (движок) → результат обратно. Данные:
профили — assets в apk; сегменты — app data dir (скачиваются). Движок зовём напрямую (без RouteServer).
---
## Stage 1 — Встраивание движка + спайк «проложи 2 точки»
**Goal:** движок компилируется в составе BalDav и возвращает маршрут по паре точек на телефоне.
**Changes:**
- Извлечь `brouter-1.6.3-all.jar` в `src-tauri/gen/android/app/libs/`, подключить в gradle
(`implementation files('libs/brouter-1.6.3-all.jar')`), включить desugaring при необходимости.
- Профили в assets: `assets/brouter/profiles2/{lookups.dat,trekking.brf,shortest.brf}`.
- Kotlin `BRouterService`/функция: копирует профили из assets в files, зовёт
`btools.router.RoutingEngine(segmentsDir, profilesDir, profile, waypoints, outGpx)`, читает результат.
- Rust-команда `brouter_route(lonlats, profile) -> String(geojson/gpx)` в plugin bridge.
- JS-обёртка (новый `frontend/js/router.js` или в gps.js): `BRouter.route(points, profile)`.
- `LICENSE-brouter` (MIT-нотис) в корень.
**Success:** на телефоне с вручную положенным тайлом региона (adb push для спайка) вызов
`BRouter.route([[lon,lat],[lon,lat]], "shortest")` возвращает дорожный GPX/GeoJSON, а не ошибку.
**Tests:** ручной вызов из DevTools (CDP) + лог движка; сверить, что трек идёт по дорогам.
**Status:** not started
## Stage 2 — Экран загрузки сегментов по проводу
**Goal:** пользователь скачивает тайлы `.rd5` региона внутри BalDav в app data dir.
**Changes:**
- Экран/настройка «Сегменты дорог»: список нужных тайлов по региону (карта/выбор), кнопка скачать
с `brouter.de/brouter/segments4/<tile>.rd5`, прогресс, размер, дата. Хранить в app data dir
(путь, который читает Stage 1).
- Управление: список скачанного, удалить/обновить.
**Success:** дома по проводу тайлы региона (E35_N45/E40_N45 — уточнить) скачиваются и лежат так,
что Stage 1 их читает; в поле сеть не нужна.
**Tests:** скачать тайл, проложить маршрут офлайн (сеть выключить) — работает.
**Status:** not started
## Stage 3 — Идея 2: оффлайн-планирование по опорным точкам
**Goal:** тап опорных точек → дорожный маршрут trekking → существующий предпросмотр/профиль.
**Changes:**
- В `showRouteEditor` (frontend/js/main.js): между опорными точками звать `BRouter.route(...,
"trekking")` вместо прямых отрезков; результат (GPX/GeoJSON) парсить существующим `parseRouteGPX`,
дальше работает `showRouteDetail`/`routeElevStats`/`drawRoutePolyline`.
- Фолбэк на прямую линию, если движок/сегмент недоступны.
**Success:** тап точек даёт маршрут по дорогам с профилем высоты; сохраняется как маршрут.
**Tests:** спланировать знакомый маршрут офлайн, сверить с онлайн-BRouter (та же геометрия).
**Status:** not started
## Stage 4 — Идея 1: заполнение GPS-пропусков по дороге
**Goal:** GPS-разрыв заполняется дорожным путём (shortest), с фолбэком на хорду.
**Changes:**
- В ветке `isGap` `computeStats`/при обработке трека: проложить `BRouter.route(предгэп, послегэп,
"shortest")`; если длина по графу > ~3× хорды (константа, подобрать на практике) — оставить хорду.
- Точки-заполнители помечать `estimated`; решить зачёт дорожной дистанции (по умолчанию — считать
дорожную длину, но с лимитом гэпа).
**Success:** записанный трек с дропаутом GPS показывает дорожный фолловинг вместо хорды, кроме
случая «дорога в разы длиннее».
**Tests:** трек с известным дропаутом на дороге → заполнение по дороге; дропаут на поле → хорда.
**Status:** not started
## Stage 5 — Полировка (опционально)
**Goal:** удобство и корректность.
**Changes:** настройка «избегать подъёмов» (крутилка `uphillcost` в trekking); обновление сегментов;
доки (README/PROJECT_MAP), лог сессии; проверка размера apk.
**Status:** not started
---
## Потенциальные проблемы
- Desugaring/`java.lang.invoke` при dex — проверить сборку (AGP обычно тянет).
- Размер/имена тайлов региона на brouter.de — уточнить при Stage 2.
- Время роутинга на телефоне на больших `.rd5` — замерить (может понадобиться таймаут/поток).
- Формат вывода: GeoJSON проще парсить, чем GPX; выбрать при Stage 1.
- Заполнение гэпов не должно ломать существующие фильтры высоты/скорости (estimated-флаг).