# 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/.rd5`, прогресс, размер, дата. Хранить в app data dir (путь, который читает Stage 1). - Управление: список скачанного, удалить/обновить. **Success:** дома по проводу тайлы региона (E35_N45/E40_N45 — подтверждены) скачиваются и лежат так, что Stage 1 их читает; в поле сеть не нужна. **Tests:** скачать тайл, проложить маршрут офлайн (сеть выключить) — работает. **Status:** ПРИМИТИВ ГОТОВ (компилируется; тест на железе завтра). Kotlin качает тайл в files/brouter/segments4 (путь детерминирован — качает сам Kotlin, без Rust-догадок): команды `dltile:`/`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-флаг).