End-to-end offline routing confirmed on the phone: shortest ~5.4 km in Rostov
(3.2s cold), trekking cross-tile Rostov->region 44.8 km / 316 m ascend
(1.2s), 617 ms on the apk-shipped data. JS -> Rust brouter_route -> Kotlin
engine -> GPX works through the whole chain.
The 1.6.3 distribution ships lookups.dat v10, but the live brouter.de/segments4
tiles are v11 ("lookup version mismatch"). Replaced the vendored lookups.dat +
trekking/shortest profiles with the v11 set from abrensch/brouter master
(misc/profiles2); the 1.6.3 engine accepts them. Profiles and lookups MUST match
the segments' version.
7.5 KiB
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. ВАЖНО: bundledlookups.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: 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:
- В ветке
isGapcomputeStats/при обработке трека: проложить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-флаг).