baldav/IMPLEMENTATION_PLAN.md
4aiman 3aa3ffdac1 BRouter Stage 2 (primitive): download .rd5 segments inside the app
Kotlin owns the download so tiles land in the exact dir the engine reads
(files/brouter/segments4) — no Rust path guessing. processBrouterCommand now
dispatches: dltile:<tile> streams brouter.de/brouter/segments4/<tile>.rd5 to a
.tmp then renames, writing progress/final state to brouter_dl_<tile>.json;
listtiles writes brouter_tiles.json. Tile names are sanitized ([A-Za-z0-9_]).

Rust relays: brouter_download_tile / brouter_download_status / brouter_list_tiles.
JS: BRouter.downloadTile/downloadStatus/listTiles/downloadTileAndWait.

Compiles into the apk. Not yet exercised on device (phone offline) — the tile
selection UI and the end-to-end download->offline-route test are the remaining
Stage 2 work. IMPLEMENTATION_PLAN updated.
2026-09-09 01:11:20 +03:00

8.2 KiB
Raw Blame History

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: DONE (проверено на устройстве 2026-09-09). shortest Ростов 5.4км за 3.2с; trekking кросс-тайл Ростов→Новочеркасск 44.8км/набор 316м за 1.2с; GPX с track-length/ascend/cost. ВАЖНО: bundled lookups.dat из 1.6.3 — версии 10, а живые сегменты brouter.de/segments4 — v11 («lookup version mismatch»). Ассеты заменены на v11 из репозитория (abrensch/brouter master: misc/profiles2/{lookups.dat,trekking.brf,shortest.brf}); движок 1.6.3 их принимает. Профили/lookups ОБЯЗАНЫ совпадать по версии с сегментами.

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: ПРИМИТИВ ГОТОВ (компилируется; тест на железе завтра). Kotlin качает тайл в files/brouter/segments4 (путь детерминирован — качает сам Kotlin, без Rust-догадок): команды dltile:<tile>/listtiles в processBrouterCommand, прогресс/статус в brouter_dl_.json. Rust-реле: brouter_download_tile/brouter_download_status/brouter_list_tiles. JS: BRouter.downloadTile/downloadStatus/listTiles/downloadTileAndWait. ОСТАЛОСЬ: UI-экран выбора тайлов по региону + сквозной тест на устройстве (скачать → офлайн-маршрут).

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-флаг).