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.
This commit is contained in:
parent
34927e3f52
commit
a52e998fb6
15 changed files with 2338 additions and 1 deletions
79
IMPLEMENTATION_PLAN.md
Normal file
79
IMPLEMENTATION_PLAN.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# 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-флаг).
|
||||
30
LICENSE-brouter
Normal file
30
LICENSE-brouter
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
BalDav embeds the BRouter routing engine (brouter-1.6.3-all.jar,
|
||||
src-tauri/gen/android/app/libs/) and its routing profiles
|
||||
(src-tauri/gen/android/app/src/main/assets/brouter/profiles2/).
|
||||
|
||||
BRouter is a third-party component distributed under the MIT License.
|
||||
Source: https://github.com/abrensch/brouter
|
||||
|
||||
---
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 BRouter contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -84,6 +84,13 @@
|
|||
| `ion_products.json` | 1111 продуктов ION (сырые данные скрейпера) |
|
||||
| `5ka_products_fresh.json` | 3103 продукта 5ka (сырые данные) |
|
||||
|
||||
### Разборы (docs/)
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `docs/gps-model-findings.md` | Находки по расчётной модели GPS (Crr/ккал/высота/ветер) — часть внедрена, см. лог 2026-09-07 |
|
||||
| `docs/brouter-findings.md` | Разбор BRouter 1.6.3 (оффлайн-роутинг): данные `.rd5`, профили, малый набор высоты, 3 способа вызова + встраивание; под идеи «заполнение GPS-пропусков» и «оффлайн-планирование по точкам». Рекомендация — встраивание движка (jar 356 КБ) в Android-модуль |
|
||||
|
||||
## База данных
|
||||
|
||||
### Таблицы (Rust v1 миграция + JS миграции)
|
||||
|
|
|
|||
191
docs/brouter-findings.md
Normal file
191
docs/brouter-findings.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# Разбор BRouter 1.6.3 под две идеи BalDav (2026-09-07)
|
||||
|
||||
Получено разбором `brouter-1.6.3.zip` (read-only: листинг архива, чтение текстовых
|
||||
доков и профилей через stdout, инспекция `brouter-1.6.3-all.jar` в памяти — распаковки
|
||||
на диск и запуска Java не было). Цель — понять, как BRouter «знает, где дорога» и строит
|
||||
маршруты с малым набором высоты, и оценить под это две идеи пользователя:
|
||||
|
||||
1. **Заполнение GPS-пропусков по дороге** — когда GPS пропадает и появляется, пробел
|
||||
заполнять кратчайшим путём по реальным дорогам, а не хордой/скорость×время.
|
||||
2. **Оффлайн-планирование маршрута по опорным точкам** в BalDav — замена онлайн-BRouter.
|
||||
|
||||
Обе идеи требуют оффлайн-роутинга; сеть в поле недоступна (РЭБ/РКН — как в обсуждениях
|
||||
ветра и DEM), поэтому годятся только оффлайн-схемы.
|
||||
|
||||
---
|
||||
|
||||
## 1. Что внутри дистрибутива
|
||||
|
||||
| Часть | Что это |
|
||||
|---|---|
|
||||
| `BRouterApp.1.6.3-api19-release.apk`, `...api30-release.apk` | Android-приложение `btools.routingapp` (одно и то же, под разные min-SDK). Считает маршрут, карту НЕ рисует. |
|
||||
| `brouter-1.6.3-all.jar` (356 КБ) | Standalone-движок на чистой Java. Main-Class `btools.server.BRouter` (HTTP-сервер). Тот же движок, что в apk. |
|
||||
| `profiles2/*.brf` | Профили маршрутизации (текстовые скрипты стоимости). `lookups.dat` — таблица тегов/значений. |
|
||||
| `readmes/` | Документация: установка, `profile_developers_guide.txt`, `mapcreation.md`, интеграция с OsmAnd, особенности Android 11. |
|
||||
|
||||
BRouter — **маршрутизатор без карты**: на вход опорные точки + профиль, на выход трек
|
||||
(GPX/GeoJSON). Отрисовка и навигация — на стороне карт-тула (OsmAnd/Locus/…), либо, в нашем
|
||||
случае, на стороне BalDav.
|
||||
|
||||
## 2. Где дорога: данные `.rd5`
|
||||
|
||||
- Граф дорог — **сегментные файлы `.rd5`**, тайлы **5°×5°** по широте/долготе. Внутри —
|
||||
OSM-ways и nodes, закодированные в бинарный битстрим по таблице `lookups.dat` (для каждого
|
||||
тега — набор значений с частотами и алиасами).
|
||||
- Готовые тайлы качаются с **brouter.de/brouter/segments4** (пересобираются еженедельно) или
|
||||
строятся самому из OSM-dump + SRTM (`mapcreation.md`, скрипт `process_pbf_planet.sh`,
|
||||
требуется Osmosis/pbfparser).
|
||||
- **Высоты** в сегментах берутся из **SRTM-90м** (CGIAR). Именно они дают BRouter'у профиль
|
||||
рельефа, по которому он считает набор высоты (см. §4). Без SRTM сегменты соберутся, но без
|
||||
высот — маршруты «с малым набором» не получатся.
|
||||
- Для нашего региона (Ростовская обл., ~47N/40E) нужны тайлы, чьи SW-углы кратны 5:
|
||||
вероятно **`E35_N45.rd5`** (35–40E, 45–50N) и **`E40_N45.rd5`** (40–45E) — город ~40.1E
|
||||
попадает во второй. Точные имена и размер (обычно десятки МБ на тайл) — **проверить при
|
||||
загрузке**. Загрузка одноразовая по проводу — тот же паттерн «скачать дома», что для DEM/прогноза.
|
||||
|
||||
## 3. Как строится маршрут (движок)
|
||||
|
||||
- Класс `btools.router.RoutingEngine`. Ключевые шаги (по строкам байткода):
|
||||
`matchWaypointsToNodes` — **привязка каждой опорной точки к ближайшему дорожному узлу графа**
|
||||
(это и есть ответ «где дорога, а где нет»: точка вне дорог подтягивается к дороге), затем поиск
|
||||
пути по графу между привязанными узлами.
|
||||
- Стоимость ребра считает **интерпретатор профиля** (`btools.expressions`) по тегам ребра из
|
||||
`.rd5`. Поиск минимизирует суммарную стоимость (по сути A*/Dijkstra по сегментному графу с
|
||||
кэшами профиля).
|
||||
- На выход движок отдаёт трек и метрики: `track-length`, `filtered ascend` и `plain-ascend`
|
||||
(набор высоты — сглаженный и сырой), `cost`, `energy`, `time` (по кинематике профиля).
|
||||
Формат — GPX (`formatAsGpx`/`writeGpx`) или GeoJSON/KML.
|
||||
|
||||
## 4. Малый набор высоты — как именно (основной режим)
|
||||
|
||||
Это чисто вопрос **профиля**, а не движка. В `trekking.brf` (context:global):
|
||||
|
||||
```
|
||||
assign consider_elevation = true
|
||||
assign downhillcost = 60 # штраф за спуск
|
||||
assign downhillcutoff = 1.5 # уклоны меньше 1.5% не штрафуются
|
||||
assign uphillcost = 0 # (в trekking по умолчанию 0; крутить вверх — здесь)
|
||||
assign uphillcutoff = 1.5
|
||||
```
|
||||
|
||||
Плюс **кинематическая модель времени/энергии** — прямой аналог модели мощности BalDav:
|
||||
|
||||
```
|
||||
assign totalMass = 90 # масса байк+седок, кг
|
||||
assign maxSpeed = 45 # км/ч
|
||||
assign S_C_x = 0.225 # CdA (площадь×коэф. лобового сопротивления)
|
||||
assign C_r = 0.01 # коэффициент качения
|
||||
assign bikerPower = 100 # средняя мощность, Вт
|
||||
```
|
||||
|
||||
Чтобы получить маршрут **с малым набором высоты**, поднимают `uphillcost` (и/или `downhillcost`):
|
||||
движок начинает предпочитать обходные, но более пологие дороги. `uphillcutoff`/`downhillcutoff`
|
||||
задают порог уклона, ниже которого рельеф игнорируется. То есть «основной режим» пользователя в
|
||||
онлайн-BRouter — это trekking с ненулевым `uphillcost`.
|
||||
|
||||
Отдельно есть **`shortest.brf`**: `downhillcost=0`, `uphillcost=0`, `turncost=0` — чистейший
|
||||
кратчайший путь по дорогам, без учёта рельефа. Это ровно то, что нужно для **идеи 1** (заполнить
|
||||
GPS-пропуск — важно «просто чтобы была дорога», кратчайшая).
|
||||
|
||||
## 5. Как вызвать оффлайн — три поверхности
|
||||
|
||||
**(а) Standalone-jar на компе (HTTP-сервер).** Подтверждённая строка usage:
|
||||
|
||||
```
|
||||
java -cp brouter-1.6.3-all.jar btools.server.RouteServer <segmentdir> <profiledir> <customprofiledir> <port> <maxthreads> [bindaddress]
|
||||
```
|
||||
|
||||
Запрос (тот же API, что у brouter.de/brouter-web):
|
||||
|
||||
```
|
||||
GET http://127.0.0.1:<port>/brouter?lonlats=39.7,47.2|40.1,47.4&profile=shortest&format=geojson&timode=0&alternativeidx=0
|
||||
```
|
||||
|
||||
Параметры (из байткода `RouteServer`): `lonlats` (пары lon,lat через `|`), `profile`, `format`
|
||||
(gpx/geojson/kml), `timode` (turnInstructionMode), `alternativeidx`, `nogos`. Нужен JRE + каталог
|
||||
сегментов `.rd5` + каталог профилей. Годится для инструментов `tools/track/` (проложить маршрут по
|
||||
`lonlats`, что уже выдаёт `tools/steep_segments.js`).
|
||||
|
||||
**(б) Приложение BRouter на телефоне — Server-Mode.** Приложение поднимает локальный HTTP-сервер
|
||||
(порт 17777 по умолчанию); любой клиент на устройстве, включая WebView BalDav, шлёт тот же
|
||||
`GET /brouter?lonlats=...`. Требует установленного приложения BRouter и скачанных сегментов.
|
||||
|
||||
**(в) Приложение BRouter — Android-intent.** С v1.6.2 у приложения есть «интерфейс без файлового
|
||||
доступа»: BalDav (Kotlin) шлёт intent к `btools.routingapp` с опорными точками и профилем, получает
|
||||
GPX/GeoJSON обратно. Данные приложения с Android 11 лежат в
|
||||
`Android/media/btools.routingapp/brouter/segments4`.
|
||||
|
||||
**(г) Встраивание движка в BalDav.** `brouter-1.6.3-all.jar` — чистая Java (356 КБ), пакеты
|
||||
`router`/`mapaccess`/`expressions`/`codec`/`util`. Классы движка можно добавить в Android-модуль
|
||||
BalDav (`src-tauri/gen/android`) и звать из Kotlin, а из JS — через существующий Rust↔Kotlin-мост
|
||||
(как GPS-сервис). Полностью самодостаточно и оффлайн, второе приложение не нужно.
|
||||
|
||||
## 6. Идея 1 — заполнение GPS-пропусков по дороге
|
||||
|
||||
- **Как:** при разрыве взять последнюю точку до пропуска и первую после, проложить между ними
|
||||
маршрут профилем `shortest.brf`, вставить дорожную полилинию как заполнение гэпа.
|
||||
- **Стык с `computeStats` (frontend/js/gps.js, ветка `isGap`):** сейчас гэп заполняется хордой или
|
||||
`скорость×время`. Дорожный путь точнее (по реальным дорогам). Точки-заполнители помечать
|
||||
`estimated` (как dead-reckoning), чтобы они не влияли на глюк-фильтры высоты и были видимо
|
||||
«достроенными». **Решить:** идёт ли дорожная дистанция в зачёт трека или только рисуется (сейчас
|
||||
гэп считается по скорости; дорожная длина может быть и точнее, и длиннее реального пути).
|
||||
- **Риски/границы:** без сегментов на регион заполнить нечем; дорога-кратчайшая может отличаться от
|
||||
фактически проеханной; на бездорожье (поля, где пользователь реально ездит) кратчайший путь по
|
||||
графу дорог может увести. Нужен разумный лимит длины гэпа, при котором достраиваем.
|
||||
|
||||
## 7. Идея 2 — оффлайн-планирование маршрута по точкам
|
||||
|
||||
- **Что уже есть в BalDav** (frontend/js/main.js): `showRouteEditor` (тап по карте = точка,
|
||||
тап по линии = вставка, тащить/удалять), `showRouteDetail` (предпросмотр + профиль высоты
|
||||
`routeElevStats` + фуллскрин), `drawRoutePolyline`, «Сохранённые маршруты», топо/уклон-обводка.
|
||||
Парсер `parseRouteGPX` уже читает GPX/`<ele>`.
|
||||
- **Чего не хватает:** оффлайн-роутера между опорными точками. Сейчас линия между тапами — прямая
|
||||
(ручное рисование). BRouter вернёт дорожный GPX/GeoJSON, который **уже парсится** `parseRouteGPX`,
|
||||
и дальше работает существующий предпросмотр/профиль высоты.
|
||||
- То есть UI-обвязка в основном готова; предметная работа — вызов BRouter и подмена прямых отрезков
|
||||
дорожным маршрутом (+ выбор профиля: trekking с `uphillcost` для «поменьше в горку»).
|
||||
|
||||
## 8. Варианты интеграции и рекомендация
|
||||
|
||||
| Вариант | Плюсы | Минусы |
|
||||
|---|---|---|
|
||||
| **A. Зависимость от приложения BRouter** (intent или localhost HTTP) | Быстро; роутинга в BalDav нет; BRouter сам качает/обновляет сегменты | Второе приложение ставить и настраивать; управление сегментами вне BalDav; связанность |
|
||||
| **B. Встраивание движка** (jar в Android-модуль + Rust/JS-команда) | Самодостаточно, полностью оффлайн, один apk; сегменты кладём куда хотим | Бандлинг Java-движка; доставка `.rd5` в приложение; **проверить лицензию** (GitHub abrensch/brouter); управление хранилищем сегментов |
|
||||
| **C. Порт на Rust/JS** | Нет внешних зависимостей | Огромный объём (движок + формат `.rd5` + интерпретатор профилей) — **отклонить** |
|
||||
|
||||
**Решение — B (встраивание)** (подтверждено пользователем). Встраиваемость проверена по байткоду:
|
||||
движок (150 классов) использует только `java.io/net/util/lang/text/security/zip` — ни AWT/Swing/
|
||||
RMI/ImageIO/servlet/sun.*, всё есть на Android ART. `RouteServer` — на голых `ServerSocket`/`Socket`
|
||||
(контейнер не нужен). Значит в процессе можно звать напрямую `btools.router.RoutingEngine`
|
||||
(без HTTP-сервера): дать каталог сегментов + профиль + опорные точки, получить GPX/GeoJSON.
|
||||
Для инструментов `tools/track/` (анализ, не телефон) сразу годится **вариант (а)** — jar на компе.
|
||||
|
||||
## 9. Решения и открытые вопросы
|
||||
|
||||
Решено (2026-09-07):
|
||||
1. **Лицензия — MIT** («Copyright (c) 2019 BRouter contributors», github.com/abrensch/brouter).
|
||||
Встраивание разрешено без ограничений; при распространении сохранить копирайт-нотис MIT
|
||||
(положить `LICENSE-brouter` в репозиторий рядом с движком).
|
||||
2. **Интеграция — вариант B (встраивание движка)**. Код BalDav открыт, MIT это позволяет.
|
||||
3. **Профиль — trekking** (для обеих идей). Идея 1 всё же считается `shortest` для чистого
|
||||
кратчайшего пути (см. §6); trekking — для планирования (идея 2).
|
||||
4. **Идея 1, порог хорды:** прокладывать по графу, НО если дорожная длина в разы больше хорды —
|
||||
рисовать хорду (граф увёл не туда, напр. вокруг поля). Коэффициент подобрать на практике
|
||||
(старт ~3×, вынести в конфиг/константу).
|
||||
5. **«Крутилка» `uphillcost`** (см. §4) — пользователь про неё не знал. План: начать со
|
||||
стандартного trekking; если маршруты слишком в горку — добавить настройку «избегать подъёмов»
|
||||
(поднимать `uphillcost`). Пока не крутим.
|
||||
|
||||
6. **Доставка сегментов — загрузка по проводу внутри BalDav** (решено). Экран в BalDav: дома на
|
||||
Wi-Fi/проводе скачать готовые тайлы региона с brouter.de/segments4 в папку приложения
|
||||
(app data dir, аналог seed-БД); в поле сеть не нужна. Тот же паттерн, что задумывался для DEM/
|
||||
прогноза. Свою сборку сегментов из OSM+SRTM не делаем.
|
||||
7. **Встраиваемость подтверждена** (см. §8): движок без десктоп-зависимостей, зовём
|
||||
`btools.router.RoutingEngine` в процессе из Kotlin.
|
||||
|
||||
## 10. Проверка (PoC, отдельным шагом, по согласию)
|
||||
|
||||
Требует JRE и загрузки сегментов региона (сеть) — вне текущей задачи. Когда разрешишь:
|
||||
запустить `RouteServer` на компе с каталогом `.rd5` и `profiles2/`, дёрнуть
|
||||
`GET /brouter?lonlats=...&profile=shortest&format=geojson` по паре точек из
|
||||
`tools/steep_segments.js`, убедиться, что возвращается дорожный маршрут с `track-length`/`ascend`.
|
||||
353
docs/gps-model-findings.md
Normal file
353
docs/gps-model-findings.md
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
# Находки по расчётной модели GPS (2026-09-07)
|
||||
|
||||
Получено при независимом разборе базы `baldav.db` (156 поездок, 366 тыс. точек)
|
||||
сторонним инструментом `tools/track/`. Константы там взяты из `GPS.computeStats`,
|
||||
поэтому расхождения означают разницу в модели, а не в настройках.
|
||||
|
||||
Проверка сходимости: дистанция расходится с базой на **0.2%**, калории по формуле
|
||||
приложения -- на **4%** (трек 4 сентября). То есть база и формула воспроизводятся,
|
||||
и нижеперечисленное -- не ошибки чтения.
|
||||
|
||||
Пункты про **нулевую отметку высоты** и **паузу на подъёме** сюда не включены:
|
||||
они уже обсуждены отдельно.
|
||||
|
||||
Файл состоит из двух частей. **Первая -- находки:** измеренные расхождения,
|
||||
каждое с замером и по возможности с контрольной проверкой. **Вторая --
|
||||
предположения:** варианты отслеживания ветра, составленные по просьбе
|
||||
пользователя, с вопросами к вам в конце. Смешивать их не стоит: первая часть
|
||||
проверена на данных, вторая -- это перечень возможностей, а не рекомендация.
|
||||
|
||||
---
|
||||
|
||||
## 1. Потолок качения 0.008 занижен втрое
|
||||
|
||||
`crr_from_accel`: при `accel_rms > 7` возвращается 0.008 и выше не поднимается.
|
||||
0.008 -- это разбитый асфальт. Реальные грунты дают втрое больше.
|
||||
|
||||
**Замер.** Трек 6 сентября (`id 1788683393199`), километры 36.5-38.5: 1996 м,
|
||||
ровно (уклон −0.1%), `accel_rms` медиана **15.1**, скорость 13.8 км/ч.
|
||||
Пользователь описал место как траву и тракторную колею с ямами.
|
||||
Обратное решение уравнения мощности относительно Crr:
|
||||
|
||||
| если мощность была | требуемый Crr |
|
||||
|---|---|
|
||||
| 90 Вт | 0.0198 |
|
||||
| 110 Вт | 0.0252 |
|
||||
| 130 Вт | 0.0307 |
|
||||
|
||||
Справочные значения: плотный грунт 0.012, щебень 0.020, трава и песок 0.035.
|
||||
Замер попадает ровно в диапазон, соответствующий описанию.
|
||||
|
||||
**Контрольная проверка обратным знаком.** Те же вычисления на километрах 9-11
|
||||
(25 км/ч, тряска 5.2) дают Crr отрицательным при 90-110 Вт -- значит там
|
||||
мощность была около 150 Вт. Сплиты на тех километрах показывают 148-172 Вт.
|
||||
Метод не завышает и не занижает систематически.
|
||||
|
||||
**Предлагаемая шкала** (взята нижняя граница замера -- лучше недосчитать):
|
||||
|
||||
< 3 -> 0.004 гладкий асфальт (как сейчас)
|
||||
3-7 -> 0.006 смешанное (как сейчас)
|
||||
7-11 -> 0.008 разбитый асфальт (как сейчас)
|
||||
11-15 -> 0.012 плотный грунт (новое)
|
||||
> 15 -> 0.020 трава, колея, щебень (новое)
|
||||
|
||||
**Влияние на трек 6 сентября:** средняя мощность 89 -> 108 Вт,
|
||||
механическая работа 1233 -> 1585 кДж. То есть на грунтах модель
|
||||
недосчитывала примерно четверть работы.
|
||||
|
||||
## 2. `max(physics, MET)` никогда не складывает, и подъёмы от этого страдают
|
||||
|
||||
На одних и тех же отрезках трека 6 сентября:
|
||||
|
||||
| участки | физика | MET | MET/физика |
|
||||
|---|---|---|---|
|
||||
| вся поездка | 1318 | 1764 | 1.34 |
|
||||
| равнина, \|уклон\| < 1% | 727 | 1344 | **1.85** |
|
||||
| в горку круче 2% | 400 | 165 | **0.41** |
|
||||
|
||||
Комментарий в коде говорит про «~2x занижение физики» -- на равнине это верно
|
||||
(1.85), но по поездке в целом всего 1.34, потому что на подъёмах физика
|
||||
выигрывает с большим запасом.
|
||||
|
||||
Изъян в том, что оценки не складываются. Биологические накладные расходы
|
||||
(размахивание ногами, равновесие, гашение тряски корпусом) никуда не деваются
|
||||
на подъёме, но там берётся физика и табличная оценка отбрасывается целиком.
|
||||
**Направление ошибки известно: подъёмы недосчитываются.**
|
||||
|
||||
Осмысленная альтернатива, если решите менять: не максимум, а
|
||||
`физика/КПД + накладные`, где накладные оцениваются табличным минимумом
|
||||
для медленной езды. Требует калибровки, поэтому просто как направление.
|
||||
|
||||
## 3. Ходьба с велосипедом засчитывается как езда
|
||||
|
||||
На односкоростном передача даёт точный признак: 40/18 на колесе 24" --
|
||||
это 4.26 м за оборот шатунов. Скорость ниже **7.7 км/ч** означает каденс
|
||||
ниже 30 об/мин, а на таком каденсе велосипед не едет, он падает. Значит
|
||||
человек идёт и ведёт или несёт его.
|
||||
|
||||
На треке 6 сентября таких кусков **0.66 км за 8:44 в движении** -- пользователь
|
||||
подтвердил, что нёс велосипед руками в горку. Участок: км 56.31, длина 289 м,
|
||||
набор 47.8 м, **уклон 16.6%**, 4.3 км/ч.
|
||||
|
||||
Побочный эффект для статистики: если этот кусок не отделять, он склеивается
|
||||
с последующим ездовым подъёмом (км 56.60, 624 м, 3.8%, 12.3 км/ч) в один
|
||||
«подъём 915 м, 7.8%, 7.7 км/ч», которого в природе не было.
|
||||
|
||||
Признак применим только когда `bikes.gear_count` пусто или 1. На многоскоростном
|
||||
низкая скорость ничего не доказывает.
|
||||
|
||||
**По акселерометру это не различается, проверено:** нести велосипед в гору 3.24,
|
||||
ехать по гладкому асфальту 2.88, идти пешком (сессия walk) 3.98. `accel_rms`
|
||||
показывает дорогу, а не походку.
|
||||
|
||||
## 4. Набор высоты расходится с независимым пересчётом в обе стороны
|
||||
|
||||
| трек | в базе | независимый пересчёт |
|
||||
|---|---|---|
|
||||
| 6 сентября | 328 м | 528 м |
|
||||
| 4 сентября | 314 м | 264 м |
|
||||
|
||||
Знак разный, значит это не простое смещение. Без модели рельефа проверить
|
||||
нечем: попытка построить карту высот по своим же 156 поездкам провалилась --
|
||||
перекрытие маршрутов всего 26% ячеек, а разброс высоты между поездками
|
||||
в одной и той же точке имеет медиану **10 метров**.
|
||||
|
||||
**Поправка после уточнения у пользователя.** Изначально я счёл пустую
|
||||
`altitude_srtm_m` (заполнена в одной сессии из 156) недоработкой. Это не так:
|
||||
поле заполняется вручную кнопкой «уточнить высоты», которая ходит в GPXZ
|
||||
и подобные сервисы, и пользователь жмёт её редко. Полагаться на сервисы
|
||||
в поле нельзя -- РЭБ и РКН глушат, устойчиво работает преимущественно
|
||||
проводной интернет.
|
||||
|
||||
Отсюда следует не «починить SRTM», а вопрос по существу: **расхождение
|
||||
в наборе высоты остаётся неразрешённым, и разрешить его без внешнего рельефа
|
||||
нечем.** Если это важно -- имеет смысл думать в сторону предварительной
|
||||
загрузки рельефа по району дома, по проводу, один раз. Если не важно --
|
||||
достаточно знать, что цифра набора имеет точность порядка десятков процентов,
|
||||
и не строить на ней выводов. На расчёт мощности это влияет слабо: там работает
|
||||
уклон, а он считается по окну и ограничен сверху.
|
||||
|
||||
## 5. Пульса в точках нет, и это ожидаемо
|
||||
|
||||
`gps_points.heart_rate` равен нулю во всех 156 сессиях. **Это не дефект
|
||||
записи:** пользователь потерял браслет и пульсометр не носит, писать в поле
|
||||
попросту нечего. `ble_hr_data.json` и запись устройства в `profile`
|
||||
остались от прежнего использования.
|
||||
|
||||
Упомянуто только затем, чтобы никто не искал ошибку там, где её нет,
|
||||
и чтобы разбор треков не обещал анализ по пульсу.
|
||||
|
||||
## 6. Ветер вычисляется из `bearing_deg`
|
||||
|
||||
Побочная находка, в приложении такого нет. Ветер виден по тому, что на ровных
|
||||
однотипных дорогах скорость зависит от курса. Берутся только точки с ровным
|
||||
уклоном, невысокой тряской и в седле, считается медианная скорость по восьми
|
||||
секторам курса, и подбирается ветер, при котором подразумеваемая мощность
|
||||
во всех секторах одинакова.
|
||||
|
||||
Трек 6 сентября: **6.3 км/ч из направления 345°**. Разброс мощности
|
||||
по направлениям падает с 33% до 11%. Пользователь независимо подтвердил,
|
||||
что ветер часто дул в лицо.
|
||||
|
||||
Медианная скорость по курсу на этом треке -- 23.6 км/ч на юго-восток
|
||||
против 15.7 км/ч на северо-запад, при одинаковых дорогах.
|
||||
|
||||
Второй, независимый метод -- по встречным проездам одного места (758 пар)
|
||||
-- даёт 3.6 км/ч из 325°. Направление сходится в пределах 20°, сила --
|
||||
в пределах множителя 1.8. Это и есть достижимая точность оценки ветра
|
||||
без датчика.
|
||||
|
||||
Разбор вариантов отслеживания ветра, включая идею пользователя про голосовые
|
||||
отметки, -- во второй части этого файла. Там же перечислены вопросы,
|
||||
по которым пользователь просит ваше мнение.
|
||||
|
||||
## 7. Ловушка при отделении ходьбы: порог по длительности не нужен
|
||||
|
||||
Найдено после замечания пользователя, что встречные проезды одного места
|
||||
случались и пешком.
|
||||
|
||||
Первая реализация признака ходьбы требовала, чтобы медленный кусок длился
|
||||
дольше 30 секунд подряд. Это оказалось неверно. На треке 6 сентября всего
|
||||
**14.7 минут** движения медленнее 7.7 км/ч, из них сплошными кусками --
|
||||
только 8.7 минуты. Оставшиеся **6 минут** -- короткие эпизоды, и они попадали
|
||||
в статистику как педалирование.
|
||||
|
||||
Видно это по корзине каденса: «0-40 об/мин» занимала 21 минуту, и **6.4 минуты
|
||||
из неё приходилось на каденс ниже 30**, которого при езде не бывает вовсе.
|
||||
После исправления корзина стала 9 минут, а средний каденс поднялся с 64 до 67.
|
||||
|
||||
**Правильное разделение двух вопросов:**
|
||||
|
||||
- *Исключать из статистики* нужно **любую** точку ниже порога, без оглядки
|
||||
на длительность: на этой передаче ниже 30 об/мин не едут, и точка. Сюда же
|
||||
попадут обычные торможения до нуля, но они короткие и маломощные, вреда нет.
|
||||
- *Называть кусок ходьбой в отчёте* стоит только при длительности дольше
|
||||
30 секунд подряд -- иначе отчёт будет объявлять ходьбой каждое торможение
|
||||
перед ямой.
|
||||
|
||||
На оценку ветра эта примесь, к счастью, не повлияла: там отдельный порог
|
||||
9 км/ч, и пешие точки не проходили. Проверено прямым сравнением -- с ними
|
||||
3.5 км/ч из 326°, без них 3.6 км/ч из 325°.
|
||||
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
# ЧАСТЬ ВТОРАЯ: предложения по отслеживанию ветра
|
||||
|
||||
Ниже -- **не находки, а предположения**, составленные по просьбе пользователя.
|
||||
Это перечень способов с замерами там, где замеры удалось сделать.
|
||||
Решение принимает сессия разработки.
|
||||
|
||||
## Зачем
|
||||
|
||||
Ветер -- крупнейший неучтённый источник ошибки в расчёте мощности на равнине.
|
||||
На треке 6 сентября медианная скорость на одинаковых дорогах составила
|
||||
**23.6 км/ч на юго-восток против 15.7 км/ч на северо-запад**. Разброс
|
||||
подразумеваемой мощности по направлениям -- **33%**. Пока ветра в модели нет,
|
||||
эти 33% списываются на «просел» и «разогнался».
|
||||
|
||||
## Ограничения, названные пользователем
|
||||
|
||||
- **Сетевые сервисы в поле недоступны.** РЭБ и РКН глушат, устойчиво работает
|
||||
преимущественно проводной интернет. Любая схема, которой нужна сеть во время
|
||||
поездки, нерабочая.
|
||||
- **Метеостанция меряет в другой точке.** Стандартное измерение -- на высоте
|
||||
10 м в открытом месте, усреднение за 10 минут. Велосипедист едет на высоте
|
||||
1.5 м среди лесополос, домов и насыпей, где ветер и слабее, и порывистее.
|
||||
Среднее по области пользователю не поможет.
|
||||
|
||||
Оба возражения справедливы и отсекают наивное «дёргать погодный API».
|
||||
|
||||
## Вариант 1. Считать ветер из самого трека (без железа и без сети)
|
||||
|
||||
Реализовано и проверено в `tools/track/track.py`. Два независимых метода.
|
||||
|
||||
**1а. Подбор по секторам курса.** Берутся только точки на ровном участке,
|
||||
с невысокой тряской, в седле. Медианная скорость считается по восьми секторам
|
||||
направления. Подбирается такой ветер, при котором подразумеваемая мощность
|
||||
во всех секторах одинакова.
|
||||
|
||||
**1б. Встречные проезды одного места.** Ищутся пары точек в одной ячейке 30 м,
|
||||
разнесённые по времени, с противоположными курсами, одинаковым покрытием
|
||||
и зеркальным уклоном. На одной и той же дороге качение и уклон сокращаются,
|
||||
и разница скоростей при равной мощности задаёт составляющую ветра вдоль дороги.
|
||||
Составляющие по разным дорогам сводятся в вектор методом наименьших квадратов.
|
||||
|
||||
**Результат на треке 6 сентября:**
|
||||
|
||||
| метод | скорость | направление |
|
||||
|---|---|---|
|
||||
| по секторам курса | 6.3 км/ч | 345° |
|
||||
| по встречным проездам (758 пар) | 3.6 км/ч | 325° |
|
||||
|
||||
Направление сходится в пределах 20°, сила -- в пределах множителя 1.8.
|
||||
|
||||
**Это и есть достижимая точность способа без железа: направление уверенно,
|
||||
сила -- с точностью до двух раз.** Любой вариант с датчиком должен
|
||||
эту планку превосходить, иначе он не нужен.
|
||||
|
||||
Ограничения: работает только задним числом, требует разнообразия курсов
|
||||
(кольцевой маршрут или «туда-обратно» -- идеально, прямая в одну сторону --
|
||||
бесполезно), и предполагает, что ветер за поездку не менялся. Последнее
|
||||
лечится подгонкой в скользящем окне 20-30 минут, но точность падает.
|
||||
|
||||
## Вариант 2. Прогноз, скачанный ЗАРАНЕЕ по проводу
|
||||
|
||||
Обходит запрет на сеть в поле: перед выездом, на домашнем проводном интернете,
|
||||
скачивается почасовой прогноз ветра по району и кладётся в базу. В поездке сеть
|
||||
не нужна вообще.
|
||||
|
||||
Прогноз при этом используется **не как истина, а как начальное приближение**
|
||||
для варианта 1: он задаёт правдоподобный диапазон, а трек его уточняет.
|
||||
Возражение про «меряют в другой точке» остаётся в силе, но начальное
|
||||
приближение с известным смещением лучше, чем поиск вслепую.
|
||||
|
||||
Спорный момент: прогноз может оказаться хуже, чем ничего, если приземный ветер
|
||||
в лесополосах отличается от станционного в разы. Стоит проверить на нескольких
|
||||
треках, прежде чем закладывать.
|
||||
|
||||
## Вариант 3. Трубка Пито на руле (единственный способ мерить по-настоящему)
|
||||
|
||||
Так устроены настоящие аэродинамические измерители для велосипеда (Notio,
|
||||
Aeropod). Трубка, направленная вперёд, плюс датчик разности давлений дают
|
||||
**скорость набегающего воздуха напрямую**, с частотой в десятки герц.
|
||||
|
||||
Разность между воздушной скоростью и путевой скоростью с GPS, вместе с курсом,
|
||||
даёт вектор ветра непрерывно, включая порывы. Сети не требуется вообще.
|
||||
|
||||
- датчик разности давлений класса MPXV7002DP плюс микроконтроллер с BLE
|
||||
- порядок цены комплектующих -- полторы-три тысячи рублей
|
||||
- BLE в приложении уже есть, канал знакомый
|
||||
- нужна калибровка (сравнением с вариантом 1 на кольцевом маршруте в штиль)
|
||||
и защита от дождя и грязи
|
||||
- трубка на руле уязвима при падении и при переноске велосипеда
|
||||
|
||||
**Это единственный из перечисленных способов, который измеряет, а не оценивает.**
|
||||
И единственный, который видит порывы, а не среднее.
|
||||
|
||||
## Вариант 4. Флюгер на руле
|
||||
|
||||
Дешевле Пито: измеряется только **угол** набегающего потока относительно
|
||||
велосипеда, обычным поворотным датчиком. Скорости ветра не даёт, но сильно
|
||||
сужает решение в варианте 1 -- направление перестаёт быть неизвестным,
|
||||
остаётся подобрать одну величину вместо двух.
|
||||
|
||||
На велосипеде хрупок и чувствителен к завихрениям от самого седока.
|
||||
|
||||
## Вариант 5. Голосовой ввод по словам-триггерам (идея пользователя)
|
||||
|
||||
Седок произносит «в лицо», «в спину», «боковой», «порыв», приложение ставит
|
||||
отметку времени. Распознавание на Android умеет работать без сети.
|
||||
|
||||
**Замечание, которое считаю важным.** Как измерение это слабо: оценка
|
||||
субъективна, отметки редкие, а говорить на тяжёлом дыхании и в шуме ветра
|
||||
неудобно -- причём шум ветра в микрофоне мешает именно тогда, когда ветер
|
||||
и надо отметить.
|
||||
|
||||
Но у этого есть куда более сильное применение: **не как измерение,
|
||||
а как разметка для проверки автоматической оценки.** Десяток отметок
|
||||
«в лицо» и «в спину» за поездку -- это готовый ответ, по которому можно
|
||||
проверить и откалибровать вариант 1, не покупая железа. Если оценка по треку
|
||||
совпадает с отметками -- ей можно верить и без отметок.
|
||||
|
||||
При таком применении редкость и субъективность отметок почти не мешают:
|
||||
проверяется знак и порядок величины, а не точное значение.
|
||||
|
||||
## Вариант 6. Кнопка вместо голоса
|
||||
|
||||
То же самое, но нажатием: выносная кнопка на руле по BLE либо кнопка
|
||||
на гарнитуре. Надёжнее голоса на тяжёлом дыхании и в шуме, но выражает меньше
|
||||
-- реально различимы два-три состояния, не больше.
|
||||
|
||||
## Вариант 7. Шум ветра в микрофоне
|
||||
|
||||
Обдув микрофона даёт характерный низкочастотный шум, растущий со скоростью
|
||||
потока. Теоретически из него извлекается воздушная скорость.
|
||||
|
||||
Практически: в кармане бесполезно, нужен вынесенный микрофон; шум дороги
|
||||
и шин мешает; калибровка своя для каждого телефона и чехла. Дёшево проверить,
|
||||
но рассчитывать не стоит. Упомянуто для полноты.
|
||||
|
||||
---
|
||||
|
||||
## О чём хотелось бы услышать мнение сессии разработки
|
||||
|
||||
1. **Стоит ли вообще ветер в модели**, или проще честно писать «мощность
|
||||
на равнине оценена с точностью ±30%» и не усложнять. Разброс 33% измерен,
|
||||
вопрос в том, окупается ли его устранение.
|
||||
2. **Вариант 5 в роли разметки** (а не измерения) -- насколько это ложится
|
||||
на текущий UI и на запись событий с отметкой времени. По цене внедрения
|
||||
это самый дешёвый из осмысленных шагов, и он не требует решать заранее,
|
||||
покупать ли датчик.
|
||||
3. **Оправдан ли вариант 3.** Планка задана: без железа выходит направление
|
||||
с точностью около 20° и сила с точностью до двух раз. Датчик за
|
||||
несколько тысяч рублей должен дать заметно больше, иначе он не нужен.
|
||||
4. **Не ломает ли вариант 2 принцип автономности.** Скачивание заранее
|
||||
по проводу формально сеть в поле не требует, но привязывает точность
|
||||
расчёта к тому, вспомнил ли пользователь нажать кнопку перед выездом.
|
||||
Это тот же изъян, что и у кнопки «уточнить высоты».
|
||||
|
||||
Готовый код обоих методов варианта 1 -- в `tools/track/track.py`
|
||||
(функция `wind_by_passes` и секция «ветер» в `report`), выше описан
|
||||
алгоритмически.
|
||||
39
docs/session-logs/2026-09-07_brouter-findings.md
Normal file
39
docs/session-logs/2026-09-07_brouter-findings.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Сессия 2026-09-07: разбор BRouter под две идеи
|
||||
|
||||
## Контекст
|
||||
Пользователь спросил, разбирал ли я brouter apk. Не разбирал. В дереве оказался
|
||||
`brouter-1.6.3.zip` (внутри два apk `btools.routingapp`, движок `brouter-1.6.3-all.jar`,
|
||||
профили, доки) и `FitPro...apk` (не brouter). Цель разбора (уточнена пользователем): понять,
|
||||
как BRouter строит маршруты с малым набором высоты, ради двух идей —
|
||||
1) заполнять пропажу GPS кратчайшим путём по дороге; 2) оффлайн-планирование маршрута по точкам
|
||||
в BalDav (замена онлайн-BRouter).
|
||||
|
||||
## Действия
|
||||
Работали по утверждённому плану `~/.claude/plans/merry-discovering-dream.md` (сохранён смысл ниже;
|
||||
файл планов ротируется). Разбор read-only: листинг архива, чтение доков/профилей через stdout,
|
||||
инспекция jar в памяти (python zipfile из BytesIO), извлечение строк байткода — распаковки на диск
|
||||
и запуска Java не было.
|
||||
|
||||
Выяснено: где дорога — сегменты `.rd5` (тайлы 5×5°, OSM в битстрим по lookups.dat, высоты из
|
||||
SRTM-90м); роутинг — `btools.router.RoutingEngine` (matchWaypointsToNodes → поиск по графу,
|
||||
стоимость из профиля `btools.expressions`); малый набор высоты — параметры профиля
|
||||
uphillcost/downhillcost/cutoff + кинематика (totalMass/S_C_x=CdA/C_r/bikerPower, аналог модели
|
||||
мощности BalDav); `shortest.brf` — чистый кратчайший путь (под идею 1). Вызов оффлайн: jar
|
||||
`RouteServer <segmentdir> <profiledir> <customprofiledir> <port> <maxthreads>` + HTTP
|
||||
`/brouter?lonlats=..&profile=..&format=geojson`; приложение BRouter (Server-Mode HTTP или intent);
|
||||
встраивание jar (356 КБ) в Android-модуль BalDav.
|
||||
|
||||
## Результат
|
||||
Создан `docs/brouter-findings.md` — полный разбор: состав, данные, алгоритм, малый набор высоты,
|
||||
3 способа вызова + встраивание, обе идеи со стыком к существующему коду (computeStats ветка isGap;
|
||||
showRouteEditor/showRouteDetail/parseRouteGPX/routeElevStats в main.js), таблица вариантов
|
||||
интеграции с рекомендацией (целевой — встраивание движка; сначала проверить ценность на
|
||||
установленном приложении BRouter), открытые вопросы (лицензия abrensch/brouter, сегменты
|
||||
качать/строить, хранилище на телефоне, профили). Реализацию идей НЕ делали — отдельные фичи.
|
||||
|
||||
README не трогал: пользовательской фичи пока нет, это анализ. PROJECT_MAP — добавлен раздел
|
||||
«Разборы (docs/)» с указателями на gps-model-findings и brouter-findings.
|
||||
|
||||
## Следующие шаги
|
||||
- Ответы пользователя на открытые вопросы §9 разбора → планировать идею 1 и/или идею 2.
|
||||
- Опционально PoC: RouteServer на компе + сегменты региона (нужен JRE + загрузка, по согласию).
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
<script src="js/bmr.js"></script>
|
||||
<script src="js/tilecache.js"></script>
|
||||
<script src="js/gps.js"></script>
|
||||
<script src="js/router.js"></script>
|
||||
<script src="js/sync.js"></script>
|
||||
<script src="js/chzn.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
|
|
|
|||
20
frontend/js/router.js
Normal file
20
frontend/js/router.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Offline routing via the embedded BRouter engine.
|
||||
// JS → Rust command `brouter_route` → Kotlin (btools.router) → GPX back.
|
||||
// BRouter.route(points, profile):
|
||||
// points — array of [lon, lat] pairs (BRouter order; NOT [lat, lon])
|
||||
// profile — "trekking" (planning) | "shortest" (gap fill); default "trekking"
|
||||
// Returns a GPX string (parse with parseRouteGPX) or throws with the engine's error.
|
||||
// Needs the region .rd5 segments present on device (files/brouter/segments4).
|
||||
window.BRouter = {
|
||||
async route(points, profile) {
|
||||
let lonlats = JSON.stringify((points || []).map(p => [p[0], p[1]]));
|
||||
let raw = await window.__TAURI__.core.invoke("brouter_route", {
|
||||
lonlats,
|
||||
profile: profile || "trekking",
|
||||
});
|
||||
let res;
|
||||
try { res = JSON.parse(raw); } catch (e) { throw new Error("brouter: bad result: " + raw); }
|
||||
if (!res.ok) throw new Error("brouter: " + (res.error || "unknown error"));
|
||||
return res.gpx;
|
||||
},
|
||||
};
|
||||
77
src-tauri/gen/android/app/build.gradle.kts
Normal file
77
src-tauri/gen/android/app/build.gradle.kts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("rust")
|
||||
}
|
||||
|
||||
val tauriProperties = Properties().apply {
|
||||
val propFile = file("tauri.properties")
|
||||
if (propFile.exists()) {
|
||||
propFile.inputStream().use { load(it) }
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 36
|
||||
namespace = "dev.baldav.app"
|
||||
defaultConfig {
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
applicationId = "dev.baldav.app"
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
|
||||
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
|
||||
}
|
||||
buildTypes {
|
||||
getByName("debug") {
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "true"
|
||||
isDebuggable = true
|
||||
isJniDebuggable = true
|
||||
isMinifyEnabled = false
|
||||
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
|
||||
}
|
||||
}
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
*fileTree(".") { include("**/*.pro") }
|
||||
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
.toList().toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
rust {
|
||||
rootDirRel = "../../../"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// BRouter offline routing engine (MIT, github.com/abrensch/brouter) — embedded so
|
||||
// routes are computed on-device without network. Called in-process from Kotlin
|
||||
// (btools.router.RoutingEngine). Segments (.rd5) live in app data dir; profiles in assets.
|
||||
implementation(files("libs/brouter-1.6.3-all.jar"))
|
||||
implementation("androidx.webkit:webkit:1.14.0")
|
||||
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||
implementation("androidx.activity:activity-ktx:1.10.1")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
// Optional fused location provider (opt-in via settings). Guarded at runtime so
|
||||
// the app still works if Google Play services are absent — see GpsRecorderService.
|
||||
implementation("com.google.android.gms:play-services-location:21.3.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.4")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
|
||||
}
|
||||
|
||||
apply(from = "tauri.build.gradle.kts")
|
||||
BIN
src-tauri/gen/android/app/libs/brouter-1.6.3-all.jar
Normal file
BIN
src-tauri/gen/android/app/libs/brouter-1.6.3-all.jar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,938 @@
|
|||
---lookupversion:10
|
||||
---minorversion:13
|
||||
|
||||
---context:way
|
||||
|
||||
highway;0029035962 residential
|
||||
highway;0010319731 service
|
||||
highway;0007688809 track
|
||||
highway;0007656124 unclassified
|
||||
highway;0004141444 footway
|
||||
highway;0003493551 tertiary
|
||||
highway;0002852601 path
|
||||
highway;0002185240 secondary
|
||||
highway;0001447719 primary
|
||||
highway;0000699577 cycleway
|
||||
highway;0000608469 trunk
|
||||
highway;0000568118 living_street
|
||||
highway;0000515044 motorway
|
||||
highway;0000451760 motorway_link
|
||||
highway;0000442502 steps
|
||||
highway;0000360177 road
|
||||
highway;0000318426 pedestrian
|
||||
highway;0000210535 trunk_link
|
||||
highway;0000192461 primary_link
|
||||
highway;0000120758 secondary_link
|
||||
highway;0000079637 tertiary_link
|
||||
highway;0000070238 construction
|
||||
highway;0000058257 bridleway
|
||||
highway;0000039003 platform
|
||||
highway;0000037192 proposed planned virtual
|
||||
highway;0000010307 raceway
|
||||
highway;0000003152 rest_area
|
||||
highway;0000002942 abandoned disused razed demolished dismantled
|
||||
highway;0000002631 services
|
||||
highway;0000002133 corridor
|
||||
highway;0000002093 crossing
|
||||
highway;0000001440 bus_stop
|
||||
highway;0000001274 yes
|
||||
highway;0000000679 unsurfaced
|
||||
highway;0000000108 byway
|
||||
highway;0000000037 driveway
|
||||
highway;0000000021 mini_roundabout
|
||||
highway;0000000020 turning_loop
|
||||
|
||||
tracktype;0000887965 grade2
|
||||
tracktype;0000868414 grade3
|
||||
tracktype;0000595882 grade1
|
||||
tracktype;0000568372 grade4
|
||||
tracktype;0000405959 grade5
|
||||
|
||||
surface;0002497676 asphalt
|
||||
surface;0001568957 paved
|
||||
surface;0001562253 unpaved
|
||||
surface;0000727427 gravel
|
||||
surface;0000560191 ground
|
||||
surface;0000350378 dirt
|
||||
surface;0000237226 grass
|
||||
surface;0000212587 concrete concrete:plates concrete:lanes
|
||||
surface;0000188743 paving_stones paving_stones:30 paving_stones:20
|
||||
surface;0000113800 cobblestone cobblestone:flattened
|
||||
surface;0000093164 compacted
|
||||
surface;0000091171 sand dirt/sand
|
||||
surface;0000023293 wood
|
||||
surface;0000019915 pebblestone
|
||||
surface;0000012866 fine_gravel
|
||||
surface;0000010681 earth
|
||||
surface;0000007331 sett
|
||||
surface;0000005778 mud
|
||||
surface;0000004549 grass_paver
|
||||
surface;0000004398 clay
|
||||
surface;0000003760 metal
|
||||
|
||||
maxspeed;0001058313 50 30_mph 30mph
|
||||
maxspeed;0000860780 30 20_mph 20mph
|
||||
maxspeed;0000025232 10 5 7 15
|
||||
maxspeed;0000083989 20 10_mph 10mph 15_mph 15mph
|
||||
maxspeed;0000195097 40 45 25_mph 25mph
|
||||
maxspeed;0000204646 60 35_mph 35mph 40_mph 40mph
|
||||
maxspeed;0000130108 70 45_mph 45mph
|
||||
maxspeed;0000225071 80 50_mph 50mph
|
||||
maxspeed;0000106719 90 55_mph 55mph
|
||||
maxspeed;0000134522 100 60_mph 60mph 65_mph 65mph
|
||||
maxspeed;0000025242 110 70_mph 70mph
|
||||
maxspeed;0000038763 120 75_mph 75mph
|
||||
maxspeed;0000026953 130
|
||||
maxspeed;0000138654 urban RO:urban RU:urban FR:urban IT:urban AT:urban DE:urban UA:urban
|
||||
maxspeed;0000138654 rural RO:rural RU:rural FR:rural IT:rural AT:rural DE:rural UA:rural
|
||||
|
||||
service;0001433919 parking_aisle
|
||||
service;0001305879 driveway
|
||||
service;0000382788 alley
|
||||
service;0000018777 drive-through drive_through
|
||||
service;0000008290 emergency_access
|
||||
service;0000003138 bus
|
||||
service;0000001250 parking
|
||||
service;0000001159 logging
|
||||
|
||||
lit;0000809223 yes
|
||||
|
||||
lanes;0002838405 2
|
||||
lanes;0000718138 1
|
||||
lanes;0000259502 3
|
||||
lanes;0000141651 4
|
||||
lanes;0000018473 -1
|
||||
lanes;0000017934 5
|
||||
lanes;0000008241 6
|
||||
lanes;0000003643 1.5
|
||||
lanes;0000001087 7
|
||||
|
||||
access;0002688349 private
|
||||
access;0000319927 yes
|
||||
access;0000144799 no
|
||||
access;0000140215 permissive
|
||||
access;0000108802 destination
|
||||
access;0000099899 agricultural forestry
|
||||
access;0000039934 designated official
|
||||
access;0000011813 customers
|
||||
access;0000004007 delivery
|
||||
access;0000000100 psv
|
||||
access;0000000100 hov
|
||||
|
||||
foot;0001659694 yes allowed Yes
|
||||
foot;0000424847 designated official
|
||||
foot;0000202364 no
|
||||
foot;0000053031 permissive
|
||||
foot;0000011661 destination
|
||||
foot;0000007289 private
|
||||
foot;0000000167 use_sidepath sidewalk
|
||||
|
||||
bicycle;0001245560 yes allowed
|
||||
bicycle;0000452059 no
|
||||
bicycle;0000324902 designated official
|
||||
bicycle;0000025707 dismount
|
||||
bicycle;0000020440 permissive
|
||||
bicycle;0000008286 private
|
||||
bicycle;0000001553 destination
|
||||
bicycle;0000000719 use_sidepath use_cycleway
|
||||
bicycle;0000000385 mtb
|
||||
bicycle;0000000117 opposite
|
||||
|
||||
motorcar;0000135124 no
|
||||
motorcar;0000045407 yes
|
||||
motorcar;0000021494 agricultural forestry
|
||||
motorcar;0000012090 destination
|
||||
motorcar;0000008733 private
|
||||
motorcar;0000005757 designated official
|
||||
motorcar;0000004116 permissive
|
||||
motorcar;0000000979 restricted
|
||||
motorcar;0000000100 psv
|
||||
motorcar;0000000100 hov
|
||||
|
||||
motor_vehicle;0000212692 no
|
||||
motor_vehicle;0000184982 yes
|
||||
motor_vehicle;0000045128 private
|
||||
motor_vehicle;0000032622 agricultural forestry agricultural;forestry agricultural,forestry
|
||||
motor_vehicle;0000025396 designated official
|
||||
motor_vehicle;0000025092 destination
|
||||
motor_vehicle;0000010895 permissive
|
||||
motor_vehicle;0000000175 emergency Emergency
|
||||
motor_vehicle;0000000100 psv
|
||||
motor_vehicle;0000000100 hov
|
||||
|
||||
motorcycle;0000092079 no
|
||||
motorcycle;0000027978 yes
|
||||
motorcycle;0000014652 agricultural forestry
|
||||
motorcycle;0000008862 destination
|
||||
motorcycle;0000004877 designated official
|
||||
motorcycle;0000003936 private
|
||||
motorcycle;0000002209 permissive
|
||||
motorcycle;0000000100 psv
|
||||
motorcycle;0000000100 hov
|
||||
|
||||
vehicle;0000030218 no
|
||||
vehicle;0000013333 destination
|
||||
vehicle;0000011692 yes
|
||||
vehicle;0000007147 agricultural forestry agricultural;forestry
|
||||
vehicle;0000006305 private
|
||||
vehicle;0000001294 permissive
|
||||
vehicle;0000000105 designated
|
||||
vehicle;0000000100 psv
|
||||
vehicle;0000000100 hov
|
||||
|
||||
horse;0000227398 no
|
||||
horse;0000144432 yes
|
||||
horse;0000014566 designated
|
||||
horse;0000007223 permissive
|
||||
horse;0000004755 private
|
||||
horse;0000000983 official
|
||||
horse;0000000968 unknown
|
||||
horse;0000000205 destination
|
||||
|
||||
wheelchair;0000036603 no
|
||||
wheelchair;0000028451 yes
|
||||
wheelchair;0000002713 limited
|
||||
wheelchair;0000001043 unknown
|
||||
wheelchair;0000000439 designated official
|
||||
wheelchair;0000000184 destination
|
||||
|
||||
hgv;0000206836 designated
|
||||
hgv;0000071222 yes
|
||||
hgv;0000043783 no
|
||||
hgv;0000019115 destination
|
||||
hgv;0000005273 delivery
|
||||
hgv;0000003055 local
|
||||
hgv;0000001088 agricultural forestry
|
||||
hgv;0000000461 private
|
||||
hgv;0000000320 unsuitable
|
||||
hgv;0000000306 permissive
|
||||
|
||||
cycleway;0000137526 no
|
||||
cycleway;0000124777 lane
|
||||
cycleway;0000106948 track
|
||||
cycleway;0000044652 opposite
|
||||
cycleway;0000011237 shared
|
||||
cycleway;0000007312 opposite_lane
|
||||
cycleway;0000005737 shared_lane
|
||||
cycleway;0000002533 yes
|
||||
cycleway;0000002356 opposite_track
|
||||
cycleway;0000001945 share_busway
|
||||
cycleway;0000001883 none
|
||||
cycleway;0000001705 crossing
|
||||
cycleway;0000001560 unmarked_lane
|
||||
cycleway;0000001542 right
|
||||
cycleway;0000001291 segregated
|
||||
cycleway;0000001065 both
|
||||
cycleway;0000000892 left
|
||||
cycleway;0000000399 street
|
||||
cycleway;0000000344 shoulder
|
||||
cycleway;0000000326 designated
|
||||
cycleway;0000000247 proposed planned virtual
|
||||
cycleway;0000000224 cyclestreet
|
||||
cycleway;0000000172 path
|
||||
cycleway;0000000154 sidewalk
|
||||
|
||||
footway;0000104998 sidewalk
|
||||
footway;0000065943 crossing
|
||||
footway;0000012342 both
|
||||
footway;0000008363 none
|
||||
footway;0000005903 right
|
||||
footway;0000004159 left
|
||||
footway;0000003966 no
|
||||
footway;0000001093 yes
|
||||
footway;0000000558 separate
|
||||
|
||||
segregated;0000224960 no
|
||||
segregated;0000051124 yes
|
||||
|
||||
sidewalk;0000194579 none
|
||||
sidewalk;0000111468 both
|
||||
sidewalk;0000052950 right
|
||||
sidewalk;0000024489 left
|
||||
sidewalk;0000012916 no
|
||||
sidewalk;0000005725 separate
|
||||
sidewalk;0000001950 yes
|
||||
|
||||
mtb:scale;0000114272 0
|
||||
mtb:scale;0000068284 1
|
||||
mtb:scale;0000027311 2
|
||||
mtb:scale;0000011529 3
|
||||
mtb:scale;0000003666 4
|
||||
mtb:scale;0000001957 0+
|
||||
mtb:scale;0000001472 5
|
||||
mtb:scale;0000000498 1+
|
||||
mtb:scale;0000000478 1-
|
||||
mtb:scale;0000000268 0-
|
||||
mtb:scale;0000000177 2-
|
||||
mtb:scale;0000000131 2+
|
||||
mtb:scale;0000000115 6
|
||||
|
||||
sac_scale;0000150704 hiking
|
||||
sac_scale;0000070463 mountain_hiking
|
||||
sac_scale;0000010993 demanding_mountain_hiking
|
||||
sac_scale;0000004549 alpine_hiking
|
||||
sac_scale;0000001620 demanding_alpine_hiking
|
||||
sac_scale;0000000831 yes
|
||||
sac_scale;0000000712 difficult_alpine_hiking
|
||||
sac_scale;0000000265 T1-hiking
|
||||
|
||||
noexit;0000118665 yes
|
||||
|
||||
motorroad;0000056844 yes
|
||||
|
||||
oneway;0005387257 yes
|
||||
oneway;0001455407 no
|
||||
oneway;0000139188 -1
|
||||
oneway;0000000892 reversible
|
||||
oneway;0000000756 1
|
||||
oneway;0000000481 true
|
||||
|
||||
junction;0000321066 roundabout
|
||||
junction;0000002828 spui
|
||||
junction;0000002134 jughandle
|
||||
junction;0000001493 approach
|
||||
junction;0000000100 circular
|
||||
|
||||
bridge;0001842517 yes viaduct true suspension
|
||||
|
||||
tunnel;0000247305 yes
|
||||
tunnel;0000016890 building_passage
|
||||
tunnel;0000004237 no
|
||||
tunnel;0000000265 passage
|
||||
tunnel;0000000241 culvert
|
||||
tunnel;0000000122 avalanche_protector
|
||||
tunnel;0000000114 covered
|
||||
|
||||
lcn;0000073956 yes
|
||||
lcn;0000002631 proposed
|
||||
|
||||
oneway:bicycle;0000012034 no
|
||||
oneway:bicycle;0000005217 yes
|
||||
oneway:bicycle;0000000161 opposite
|
||||
|
||||
cycleway:right;0000012522 lane Lane
|
||||
cycleway:right;0000006644 track
|
||||
cycleway:right;0000000971 share_busway
|
||||
cycleway:right;0000000686 sidepath
|
||||
cycleway:right;0000000410 shared_lane
|
||||
cycleway:right;0000000104 opposite_lane
|
||||
cycleway:right;0000000058 opposite_track
|
||||
cycleway:right;0000000045 no none
|
||||
cycleway:right;0000000037 yes
|
||||
cycleway:right;0000000004 opposite
|
||||
|
||||
cycleway:left;0000005134 lane Lane
|
||||
cycleway:left;0000003169 track
|
||||
cycleway:left;0000000656 share_busway
|
||||
cycleway:left;0000000608 opposite_lane
|
||||
cycleway:left;0000000475 sidepath
|
||||
cycleway:left;0000000257 shared_lane
|
||||
cycleway:left;0000000246 no none
|
||||
cycleway:left;0000000130 opposite_track
|
||||
cycleway:left;0000000053 opposite
|
||||
cycleway:left;0000000014 yes
|
||||
|
||||
incline;0000052784 up
|
||||
incline;0000035413 down
|
||||
incline;0000001628 yes
|
||||
incline;0000000779 steep
|
||||
incline;0000000861 3% 0 1 2 3 0% 1% 2% 3%
|
||||
incline;0000000724 5% 4 5 4%
|
||||
incline;0000000530 8% 6 7 8 6% 7%
|
||||
incline;0000003109 10% 9 10 9% 10°
|
||||
incline;0000001297 15% 11 12 13 14 15 11% 12% 13% 14%
|
||||
incline;0000000997 20% 16 17 18 19 20 16% 17% 18% 19%
|
||||
incline;0000000409 25% 21 22 23 24 25 21% 22% 23% 24%
|
||||
incline;0000000263 30% 30 40 50 40% 50%
|
||||
incline;0000000861 -3% -1 -2 -3 -1% -2% -3%
|
||||
incline;0000000724 -5% -4 -5 -4%
|
||||
incline;0000000530 -8% -6 -7 -8 -6% -7%
|
||||
incline;0000001515 -10% -9 -10 -9% -10°
|
||||
incline;0000001297 -15% -11 -12 -13 -14 -15 -11% -12% -13% -14%
|
||||
incline;0000000997 -20% -16 -17 -18 -19 -20 -16% -17% -18% -19%
|
||||
incline;0000000409 -25% -21 -22 -23 -24 -25 -21% -22% -23% -24%
|
||||
incline;0000000172 -30% -30 -40 -50 -40% -50%
|
||||
|
||||
toll;0000090536 yes true
|
||||
|
||||
railway;0000157547 rail
|
||||
railway;0000019316 abandoned
|
||||
railway;0000016982 tram
|
||||
railway;0000014387 platform
|
||||
railway;0000011143 disused
|
||||
railway;0000004623 light_rail
|
||||
railway;0000002982 subway
|
||||
railway;0000002422 narrow_gauge
|
||||
railway;0000001960 razed
|
||||
railway;0000001859 preserved
|
||||
|
||||
seamark:type;0001564 recommended_track
|
||||
seamark:type;0000522 fairway
|
||||
|
||||
waterway;0000016046 river
|
||||
waterway;0000009496 canal
|
||||
waterway;0000007876 riverbank
|
||||
waterway;0000002202 weir
|
||||
waterway;0000001364 dam
|
||||
waterway;0000000386 lock
|
||||
waterway;0000000321 tidal_flat_slough
|
||||
waterway;0000000179 wadi
|
||||
waterway;0000000126 dock
|
||||
waterway;0000000113 fish_pass
|
||||
waterway;0000000086 boatyard
|
||||
waterway;0000000071 fairway
|
||||
waterway;0000000059 lock_gate
|
||||
|
||||
boat;0000019888 no
|
||||
boat;0000002718 yes
|
||||
boat;0000000232 private
|
||||
boat;0000000064 permissive
|
||||
boat;0000000045 designated
|
||||
|
||||
motorboat;0000001077 yes
|
||||
motorboat;0000000808 no
|
||||
motorboat;0000000025 private privat
|
||||
|
||||
route;0000000850 ferry
|
||||
route;0000000539 hiking
|
||||
route;0000000505 bicycle
|
||||
route;0000000454 ski
|
||||
route;0000000413 mtb
|
||||
route;0000000194 canoe
|
||||
route;0000000151 road
|
||||
route;0000000104 bus
|
||||
|
||||
smoothness;0000068136 good
|
||||
smoothness;0000042124 bad Bad
|
||||
smoothness;0000040763 intermediate
|
||||
smoothness;0000033941 excellent
|
||||
smoothness;0000012683 very_bad
|
||||
smoothness;0000009837 horrible terrible
|
||||
smoothness;0000003515 very_horrible
|
||||
smoothness;0000000919 impassable
|
||||
smoothness;0000000251 robust_wheels
|
||||
smoothness;0000000221 high_clearance
|
||||
smoothness;0000000132 very_good
|
||||
smoothness;0000000083 off_road_wheels
|
||||
smoothness;0000000057 medium Medium average
|
||||
smoothness;0000000048 poor
|
||||
smoothness;0000000039 rough
|
||||
|
||||
rcn;0000014518 yes
|
||||
rcn;0000002862 proposed
|
||||
|
||||
ncn;0000002941 yes
|
||||
ncn;0000001036 proposed
|
||||
|
||||
ford;0000020552 yes
|
||||
ford;0000000289 stepping_stones
|
||||
|
||||
trail_visibility;0000067438 good
|
||||
trail_visibility;0000041280 intermediate
|
||||
trail_visibility;0000039801 excellent
|
||||
trail_visibility;0000023482 bad
|
||||
trail_visibility;0000005853 horrible
|
||||
trail_visibility;0000002222 no
|
||||
|
||||
class:bicycle:mtb;0000002079 1 +1
|
||||
class:bicycle:mtb;0000001191 0
|
||||
class:bicycle:mtb;0000001089 2 +2
|
||||
class:bicycle:mtb;0000000703 -1
|
||||
class:bicycle:mtb;0000000234 -2
|
||||
class:bicycle:mtb;0000000140 3 +3
|
||||
class:bicycle:mtb;0000000068 -3
|
||||
|
||||
class:bicycle;0000002842 1 +1
|
||||
class:bicycle;0000000595 -1
|
||||
class:bicycle;0000000533 2 +2
|
||||
class:bicycle;0000000516 -2
|
||||
class:bicycle;0000000245 -3
|
||||
class:bicycle;0000000170 0
|
||||
class:bicycle;0000000108 3 +3
|
||||
|
||||
route_bicycle_icn;0000088753 yes
|
||||
route_bicycle_icn;0000000001 proposed
|
||||
route_bicycle_ncn;0000268180 yes
|
||||
route_bicycle_ncn;00000000001 proposed
|
||||
route_bicycle_rcn;0000718163 yes
|
||||
route_bicycle_rcn;00000000001 proposed
|
||||
route_bicycle_lcn;0000469215 yes
|
||||
route_bicycle_lcn;00000000001 proposed
|
||||
|
||||
route_bicycle_;0000024662 yes
|
||||
route_bicycle_radweit;0000004604 yes
|
||||
|
||||
route_hiking_iwn;0000056005 yes
|
||||
route_hiking_nwn;0000315813 yes
|
||||
route_hiking_rwn;0000343219 yes
|
||||
route_hiking_lwn;0000359332 yes
|
||||
route_hiking_;0000103733 yes
|
||||
|
||||
route_foot_nwn;0000047923 yes
|
||||
route_foot_lwn;0000135371 yes
|
||||
route_foot_rwn;0000115325 yes
|
||||
route_foot_;0000070583 yes
|
||||
|
||||
route_mtb_;0000066263 yes
|
||||
route_mtb_lcn;0000023718 yes
|
||||
route_mtb_ncn;0000004853 yes
|
||||
route_mtb_rcn;0000013321 yes
|
||||
route_mtb_mtb;0000006853 yes
|
||||
route_bicycle_mtb;0000002240 yes
|
||||
|
||||
brouter_route_placeholder_dummy_01;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_02;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_03;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_04;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_05;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_06;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_07;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_08;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_09;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_10;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_11;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_12;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_13;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_14;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_15;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_16;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_17;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_18;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_19;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_20;0000000001 dummy
|
||||
brouter_route_placeholder_dummy_21;0000000001 dummy
|
||||
|
||||
ramp:bicycle;0000001305 yes both permissive right left
|
||||
ramp:bicycle;0000000385 no
|
||||
|
||||
ramp:stroller;0000001099 yes
|
||||
ramp:stroller;0000000326 no
|
||||
|
||||
ramp:wheelchair;0000000610 yes
|
||||
ramp:wheelchair;0000000439 no
|
||||
|
||||
ramp:luggage;0000000162 no
|
||||
ramp:luggage;0000000054 yes automatic manual
|
||||
|
||||
estimated_traffic_class;0000000001 1
|
||||
estimated_traffic_class;0000000001 2
|
||||
estimated_traffic_class;0000000001 3
|
||||
estimated_traffic_class;0000000001 4
|
||||
estimated_traffic_class;0000000001 5
|
||||
estimated_traffic_class;0000000001 6
|
||||
estimated_traffic_class;0000000001 7
|
||||
|
||||
mtb:scale:uphill;0000018869 0 0+ 0-
|
||||
mtb:scale:uphill;0000015578 1 1+ 1-
|
||||
mtb:scale:uphill;0000012338 2 2+ 2-
|
||||
mtb:scale:uphill;0000009099 3 3+ 3-
|
||||
mtb:scale:uphill;0000005825 4 4+ 4-
|
||||
mtb:scale:uphill;0000004628 5 5+ 5-
|
||||
|
||||
crossing;0000101049 zebra
|
||||
crossing;0000017509 unmarked
|
||||
crossing;0000013817 traffic_signals
|
||||
crossing;0000011062 uncontrolled
|
||||
crossing;0000001722 yes
|
||||
crossing;0000001678 island
|
||||
crossing;0000000457 marked
|
||||
crossing;0000000131 pedestrian_signals
|
||||
crossing;0000000122 no
|
||||
|
||||
informal;0000002424 yes
|
||||
|
||||
indoor;0000058418 yes
|
||||
indoor;0000025038 room
|
||||
indoor;0000005295 wall
|
||||
indoor;0000004322 corridor
|
||||
indoor;0000002410 area
|
||||
indoor;0000000816 column
|
||||
indoor;0000000568 no
|
||||
indoor;0000000129 shop
|
||||
indoor;0000000099 steps
|
||||
|
||||
4wd_only;0000008129 yes Yes
|
||||
4wd_only;0000000487 recommended
|
||||
4wd_only;0000000041 no
|
||||
|
||||
concrete;0000000043 plates
|
||||
concrete;0000000013 lanes
|
||||
|
||||
bus;0001178365 yes
|
||||
bus;0000006419 designated
|
||||
bus;0000005602 no
|
||||
bus;0000001424 urban
|
||||
|
||||
psv;0000072077 yes
|
||||
psv;0000007456 no
|
||||
psv;0000007428 designated official
|
||||
|
||||
hov;0000006684 lane
|
||||
hov;0000003258 designated
|
||||
hov;0000002162 no
|
||||
hov;0000001512 yes
|
||||
|
||||
busway;0000000000 opposite opposite_lane opposite_track
|
||||
busway:left;0000000000 opposite opposite_lane opposite_track
|
||||
busway:right;0000000000 opposite opposite_lane opposite_track
|
||||
|
||||
cycleway:left:oneway;0000000769 yes
|
||||
cycleway:left:oneway;0000001595 no
|
||||
cycleway:left:oneway;0000000927 -1
|
||||
|
||||
cycleway:right:oneway;0000003084 yes
|
||||
cycleway:right:oneway;0000002499 no
|
||||
cycleway:right:oneway;0000000017 -1
|
||||
|
||||
zone:maxspeed;0000001616 20 DE:20 FR:20
|
||||
zone:maxspeed;0000063721 30 DE:30 FR:30 BE:30 HU:30 NO:30 AT:30 ES:30 NL:30
|
||||
|
||||
cycleway:surface;0000002609 asphalt
|
||||
cycleway:surface;0000000150 paved
|
||||
cycleway:surface;0000000012 unpaved
|
||||
cycleway:surface;0000000010 gravel
|
||||
cycleway:surface;0000000157 concrete concrete:plates concrete:lanes
|
||||
cycleway:surface;0000002239 paving_stones paving_stones:30 paving_stones:20
|
||||
cycleway:surface;0000000011 cobblestone cobblestone:flattened
|
||||
cycleway:surface;0000000013 compacted
|
||||
cycleway:surface;0000000006 fine_gravel
|
||||
cycleway:surface;0000000011 sett
|
||||
|
||||
maxspeed:backward;0001058313 50 30_mph 30mph
|
||||
maxspeed:backward;0000860780 30 20_mph 20mph
|
||||
maxspeed:backward;0000025232 10 5 7 15
|
||||
maxspeed:backward;0000083989 20 10_mph 10mph 15_mph 15mph
|
||||
maxspeed:backward;0000195097 40 45 25_mph 25mph
|
||||
maxspeed:backward;0000204646 60 35_mph 35mph 40_mph 40mph
|
||||
maxspeed:backward;0000130108 70 45_mph 45mph
|
||||
maxspeed:backward;0000225071 80 50_mph 50mph
|
||||
maxspeed:backward;0000106719 90 55_mph 55mph
|
||||
maxspeed:backward;0000134522 100 60_mph 60mph 65_mph 65mph
|
||||
maxspeed:backward;0000025242 110 70_mph 70mph
|
||||
maxspeed:backward;0000038763 120 75_mph 75mph
|
||||
maxspeed:backward;0000026953 130
|
||||
maxspeed:backward;0000138654 urban RO:urban RU:urban FR:urban IT:urban AT:urban DE:urban UA:urban
|
||||
maxspeed:backward;0000138654 rural RO:rural RU:rural FR:rural IT:rural AT:rural DE:rural UA:rural
|
||||
|
||||
maxspeed:forward;0001058313 50 30_mph 30mph
|
||||
maxspeed:forward;0000860780 30 20_mph 20mph
|
||||
maxspeed:forward;0000025232 10 5 7 15
|
||||
maxspeed:forward;0000083989 20 10_mph 10mph 15_mph 15mph
|
||||
maxspeed:forward;0000195097 40 45 25_mph 25mph
|
||||
maxspeed:forward;0000204646 60 35_mph 35mph 40_mph 40mph
|
||||
maxspeed:forward;0000130108 70 45_mph 45mph
|
||||
maxspeed:forward;0000225071 80 50_mph 50mph
|
||||
maxspeed:forward;0000106719 90 55_mph 55mph
|
||||
maxspeed:forward;0000134522 100 60_mph 60mph 65_mph 65mph
|
||||
maxspeed:forward;0000025242 110 70_mph 70mph
|
||||
maxspeed:forward;0000038763 120 75_mph 75mph
|
||||
maxspeed:forward;0000026953 130
|
||||
maxspeed:forward;0000138654 urban RO:urban RU:urban FR:urban IT:urban AT:urban DE:urban UA:urban
|
||||
maxspeed:forward;0000138654 rural RO:rural RU:rural FR:rural IT:rural AT:rural DE:rural UA:rural
|
||||
|
||||
embedded_rails;0000000928 tram
|
||||
embedded_rails;0000000007 yes
|
||||
embedded_rails;0000000003 rail
|
||||
|
||||
living_street;0000000404 yes
|
||||
|
||||
sidewalk:bicycle;0000000439 yes designated
|
||||
sidewalk:left:bicycle;0000001722 yes designated
|
||||
sidewalk:right:bicycle;0000002667 yes designated
|
||||
|
||||
bicycle_road;0000006521 yes designated
|
||||
|
||||
construction;0000144871 yes
|
||||
construction;0000008214 minor
|
||||
construction;0029035962 residential
|
||||
construction;0010319731 service
|
||||
construction;0007688809 track
|
||||
construction;0007656124 unclassified
|
||||
construction;0004141444 footway
|
||||
construction;0003493551 tertiary
|
||||
construction;0002852601 path
|
||||
construction;0002185240 secondary
|
||||
construction;0001447719 primary
|
||||
construction;0000699577 cycleway
|
||||
construction;0000608469 trunk
|
||||
construction;0000568118 living_street
|
||||
construction;0000515044 motorway
|
||||
construction;0000451760 motorway_link
|
||||
construction;0000442502 steps
|
||||
construction;0000360177 road
|
||||
construction;0000318426 pedestrian
|
||||
construction;0000210535 trunk_link
|
||||
construction;0000192461 primary_link
|
||||
construction;0000120758 secondary_link
|
||||
construction;0000079637 tertiary_link
|
||||
construction;0000070238 construction
|
||||
construction;0000058257 bridleway
|
||||
construction;0000039003 platform
|
||||
construction;0000037192 proposed
|
||||
construction;0000010307 raceway
|
||||
construction;0000003152 rest_area
|
||||
construction;0000002942 abandoned
|
||||
construction;0000002631 services
|
||||
construction;0000002133 corridor
|
||||
construction;0000002093 crossing
|
||||
construction;0000001440 bus_stop
|
||||
construction;0000001274 yes
|
||||
construction;0000000679 unsurfaced
|
||||
construction;0000000108 byway
|
||||
construction;0000000037 driveway
|
||||
construction;0000000021 mini_roundabout
|
||||
construction;0000000020 turning_loop
|
||||
|
||||
---context:node
|
||||
|
||||
highway;0001314954 bus_stop
|
||||
highway;0001130090 crossing
|
||||
highway;0001031274 turning_circle
|
||||
highway;0000609262 traffic_signals
|
||||
highway;0000306900 street_lamp
|
||||
highway;0000136339 stop
|
||||
highway;0000105097 motorway_junction
|
||||
highway;0000058076 give_way
|
||||
highway;0000049111 mini_roundabout
|
||||
highway;0000030072 milestone
|
||||
highway;0000017567 speed_camera
|
||||
highway;0000013806 emergency_access_point
|
||||
highway;0000009721 platform
|
||||
highway;0000007369 passing_place
|
||||
highway;0000005939 ford
|
||||
highway;0000004831 rest_area
|
||||
highway;0000003535 elevator
|
||||
highway;0000002572 turning_loop
|
||||
highway;0000002540 steps
|
||||
highway;0000002493 services
|
||||
highway;0000002133 emergency_bay
|
||||
highway;0000001372 residential
|
||||
highway;0000001324 street_light
|
||||
highway;0000001147 incline_steep
|
||||
highway;0000001101 stile
|
||||
highway;0000000904 incline
|
||||
highway;0000000819 service
|
||||
highway;0000000817 traffic_calming
|
||||
highway;0000000662 path
|
||||
highway;0000000603 footway
|
||||
highway;0000000438 track
|
||||
highway;0000000436 no
|
||||
highway;0000000353 door
|
||||
highway;0000000283 level_crossing
|
||||
highway;0000000267 yes
|
||||
highway;0000000262 road
|
||||
highway;0000000244 construction
|
||||
highway;0000000214 unclassified
|
||||
highway;0000000213 proposed
|
||||
highway;0000000197 junction
|
||||
highway;0000000176 distance_marker
|
||||
highway;0000000158 noexit
|
||||
highway;0000000155 unknown
|
||||
highway;0000000134 traffic_sign
|
||||
highway;0000000123 tertiary
|
||||
highway;0000000115 trailhead
|
||||
highway;0000000113 priority_to_right
|
||||
highway;0000000113 culvert
|
||||
highway;0000000100 <residential>
|
||||
highway;0000000046 toll_bridge
|
||||
highway;0000000037 city_entry
|
||||
highway;0000002967 traffic_mirror
|
||||
highway;0000001724 priority
|
||||
|
||||
barrier;0000606512 gate
|
||||
barrier;0000164120 bollard
|
||||
barrier;0000112184 lift_gate
|
||||
barrier;0000046779 stile
|
||||
barrier;0000046255 cycle_barrier
|
||||
barrier;0000038597 entrance
|
||||
barrier;0000027579 block
|
||||
barrier;0000023074 toll_booth
|
||||
barrier;0000016782 cattle_grid
|
||||
barrier;0000016154 kissing_gate
|
||||
barrier;0000003182 turnstile
|
||||
barrier;0000003160 fence
|
||||
barrier;0000002701 border_control
|
||||
barrier;0000002536 sally_port
|
||||
barrier;0000002504 chain
|
||||
barrier;0000002470 door
|
||||
barrier;0000002089 swing_gate
|
||||
barrier;0000001912 bump_gate
|
||||
barrier;0000001856 yes
|
||||
barrier;0000001683 hampshire_gate
|
||||
barrier;0000000445 wall
|
||||
barrier;0000000440 bus_trap
|
||||
barrier;0000000435 ditch
|
||||
barrier;0000000420 debris
|
||||
barrier;0000000381 log
|
||||
barrier;0000000336 chicane
|
||||
barrier;0000000316 kerb
|
||||
barrier;0000000270 sump_buster
|
||||
barrier;0000000268 obstacle
|
||||
barrier;0000000223 no
|
||||
barrier;0000000213 horse_stile
|
||||
barrier;0000000210 full-height_turnstile
|
||||
barrier;0000000176 windfall
|
||||
barrier;0000000168 spikes
|
||||
barrier;0000000168 checkpoint
|
||||
barrier;0000000166 hedge
|
||||
barrier;0000000164 footgate
|
||||
barrier;0000000141 tree
|
||||
barrier;0000000133 guard_rail
|
||||
barrier;0000000129 bar
|
||||
barrier;0000000124 fallen_tree
|
||||
barrier;0000000118 jersey_barrier
|
||||
barrier;0000000114 motorcycle_barrier
|
||||
barrier;0000000114 barrier
|
||||
barrier;0000000108 rope
|
||||
barrier;0000000095 stone
|
||||
barrier;0000000069 traffic_crossing_pole
|
||||
|
||||
|
||||
access;0000078544 private
|
||||
access;0000014933 yes public
|
||||
access;0000014456 no
|
||||
access;0000008670 permissive
|
||||
access;0000006316 destination customers
|
||||
access;0000000973 agricultural forestry
|
||||
access;0000000942 designated official
|
||||
|
||||
foot;0000272169 yes
|
||||
foot;0000036271 no
|
||||
foot;0000001118 designated official
|
||||
foot;0000000960 private
|
||||
foot;0000000833 permissive
|
||||
foot;0000000127 destination
|
||||
|
||||
bicycle;0000267569 yes
|
||||
bicycle;0000067796 no
|
||||
bicycle;0000004075 designated official
|
||||
bicycle;0000002596 dismount
|
||||
bicycle;0000000498 permissive
|
||||
bicycle;0000000359 private
|
||||
bicycle;0000000075 destination
|
||||
|
||||
motorcar;0000038901 yes
|
||||
motorcar;0000009323 no
|
||||
motorcar;0000000895 private
|
||||
motorcar;0000000216 destination
|
||||
motorcar;0000000214 permissive
|
||||
motorcar;0000000093 agricultural forestry
|
||||
motorcar;0000000084 designated official
|
||||
|
||||
motor_vehicle;0000000066 yes
|
||||
motor_vehicle;0000000001 permissive
|
||||
motor_vehicle;0000000000 designated official
|
||||
motor_vehicle;0000000030 destination
|
||||
motor_vehicle;0000000073 agricultural forestry
|
||||
motor_vehicle;0000000136 private
|
||||
motor_vehicle;0000000469 no
|
||||
|
||||
motorcycle;0000028697 yes
|
||||
motorcycle;0000007061 no
|
||||
motorcycle;0000000243 private
|
||||
motorcycle;0000000237 designated
|
||||
motorcycle;0000000117 destination
|
||||
motorcycle;0000000080 permissive
|
||||
motorcycle;0000000029 agricultural forestry
|
||||
|
||||
vehicle;0000002176 no
|
||||
vehicle;0000000576 yes
|
||||
vehicle;0000000556 private
|
||||
vehicle;0000000262 destination
|
||||
vehicle;0000000138 agricultural forestry
|
||||
vehicle;0000000105 permissive
|
||||
vehicle;0000000003 designated official
|
||||
|
||||
horse;0000021837 yes
|
||||
horse;0000010811 no
|
||||
horse;0000000105 private
|
||||
horse;0000000065 permissive
|
||||
horse;0000000053 designated official
|
||||
horse;0000000009 critical
|
||||
horse;0000000007 destination
|
||||
|
||||
wheelchair;0000203478 yes true
|
||||
wheelchair;0000100082 no false
|
||||
wheelchair;0000080430 limited
|
||||
wheelchair;0000002769 designated official
|
||||
wheelchair;0000000005 private
|
||||
wheelchair;0000000005 permissive
|
||||
wheelchair;0000000139 bad
|
||||
wheelchair;0000000031 half
|
||||
wheelchair;0000000005 partial
|
||||
|
||||
hgv;0000001141 no
|
||||
hgv;0000000359 yes true
|
||||
hgv;0000000111 destination
|
||||
hgv;0000000108 designated official
|
||||
hgv;0000000065 private
|
||||
hgv;0000000053 delivery
|
||||
hgv;0000000016 agricultural forestry
|
||||
hgv;0000000010 permissive
|
||||
|
||||
crossing;0000251032 uncontrolled
|
||||
crossing;0000200387 traffic_signals
|
||||
crossing;0000029717 unmarked
|
||||
crossing;0000022119 island
|
||||
crossing;0000006981 zebra
|
||||
crossing;0000003897 no
|
||||
crossing;0000002166 yes
|
||||
|
||||
railway;0000312710 level_crossing
|
||||
railway;0000078746 station
|
||||
railway;0000067876 buffer_stop
|
||||
railway;0000056184 switch
|
||||
railway;0000049600 crossing
|
||||
railway;0000037871 tram_stop
|
||||
railway;0000024038 halt
|
||||
railway;0000014285 subway_entrance
|
||||
railway;0000010890 signal
|
||||
|
||||
waterway;0000004698 weir
|
||||
waterway;0000001647 lock_gate
|
||||
waterway;0000000425 waterfall
|
||||
waterway;0000000337 take_right_side
|
||||
waterway;0000000332 take_left_side
|
||||
waterway;0000000219 milestone
|
||||
waterway;0000000187 depth
|
||||
waterway;0000000170 lock
|
||||
|
||||
noexit;0000195286 yes
|
||||
|
||||
entrance;0000301732 yes
|
||||
entrance;0000159853 main
|
||||
entrance;0000025621 staircase
|
||||
entrance;0000006666 home
|
||||
entrance;0000005428 service
|
||||
entrance;0000001853 emergency
|
||||
entrance;0000001144 exit
|
||||
entrance;0000000953 residence
|
||||
entrance;0000000620 garage
|
||||
entrance;0000000558 entrance
|
||||
entrance;0000000504 main_entrance
|
||||
entrance;0000000439 secondary_entrance
|
||||
entrance;0000000285 shop
|
||||
entrance;0000000258 private
|
||||
|
||||
traffic_calming;0000045987 bump bump;choker
|
||||
traffic_calming;0000040022 hump hump;choker
|
||||
traffic_calming;0000012499 table table;choker
|
||||
traffic_calming;0000006808 yes *
|
||||
traffic_calming;0000005754 cushion cushion;choker
|
||||
traffic_calming;0000005466 choker choker;cushion choker;hump choker;table choker;bump
|
||||
traffic_calming;0000005305 island
|
||||
traffic_calming;0000004686 chicane
|
||||
traffic_calming;0000004032 rumble_strip
|
||||
traffic_calming;0000000847 speed_bump
|
||||
traffic_calming;0000000186 dip
|
||||
|
||||
ford;0000037927 yes
|
||||
ford;0000000310 stepping_stones
|
||||
|
||||
direction;0000274642 forward
|
||||
direction;0000249637 backward
|
||||
direction;0000021634 both
|
||||
|
||||
traffic_signals:direction;0000062645 forward
|
||||
traffic_signals:direction;0000033961 backward
|
||||
traffic_signals:direction;0000007309 both
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
---context:global # following code refers to global config
|
||||
|
||||
# the elevation parameters
|
||||
|
||||
assign downhillcost 0
|
||||
assign downhillcutoff 1.5
|
||||
assign uphillcost 0
|
||||
assign uphillcutoff 1.5
|
||||
|
||||
assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style
|
||||
assign validForFoot 1
|
||||
|
||||
---context:way # following code refers to way-tags
|
||||
|
||||
assign any_cycleroute or route_bicycle_icn=yes or route_bicycle_ncn=yes or route_bicycle_rcn=yes route_bicycle_lcn=yes
|
||||
assign nodeaccessgranted or any_cycleroute lcn=yes
|
||||
|
||||
assign turncost 0
|
||||
|
||||
assign initialclassifier =
|
||||
if route=ferry then 1
|
||||
else 0
|
||||
|
||||
assign initialcost switch route=ferry 10000 0
|
||||
|
||||
#
|
||||
# implicit access here just from the motorroad tag
|
||||
# (implicit access rules from highway tag handled elsewhere)
|
||||
#
|
||||
assign defaultaccess
|
||||
switch access=
|
||||
not motorroad=yes
|
||||
switch or access=private access=no
|
||||
0
|
||||
1
|
||||
|
||||
#
|
||||
# calculate logical bike access
|
||||
#
|
||||
assign bikeaccess
|
||||
or any_cycleroute
|
||||
switch bicycle=
|
||||
switch bicycle_road=yes
|
||||
1
|
||||
switch vehicle=
|
||||
defaultaccess
|
||||
switch or vehicle=private vehicle=no
|
||||
0
|
||||
1
|
||||
not or bicycle=private or bicycle=no bicycle=dismount
|
||||
|
||||
#
|
||||
# calculate logical foot access
|
||||
#
|
||||
assign footaccess
|
||||
or bikeaccess
|
||||
or bicycle=dismount
|
||||
switch foot=
|
||||
defaultaccess
|
||||
not or foot=private foot=no
|
||||
|
||||
assign accesspenalty switch or bikeaccess footaccess 0 100000
|
||||
|
||||
assign costfactor
|
||||
add accesspenalty
|
||||
|
||||
switch and highway= not route=ferry 100000
|
||||
|
||||
switch route=ferry 5.67
|
||||
switch or highway=motorway highway=motorway_link 100000
|
||||
switch or highway=proposed highway=abandoned 100000
|
||||
1
|
||||
|
||||
# way priorities used for voice hint generation
|
||||
|
||||
assign isresidentialorliving = or highway=residential|living_street living_street=yes
|
||||
|
||||
assign priorityclassifier =
|
||||
|
||||
if ( highway=motorway ) then 30
|
||||
else if ( highway=motorway_link ) then 29
|
||||
else if ( highway=trunk ) then 28
|
||||
else if ( highway=trunk_link ) then 27
|
||||
else if ( highway=primary ) then 26
|
||||
else if ( highway=primary_link ) then 25
|
||||
else if ( highway=secondary ) then 24
|
||||
else if ( highway=secondary_link ) then 23
|
||||
else if ( highway=tertiary ) then 22
|
||||
else if ( highway=tertiary_link ) then 21
|
||||
else if ( highway=unclassified ) then 20
|
||||
else if ( isresidentialorliving ) then 6
|
||||
else if ( highway=service ) then 6
|
||||
else if ( highway=cycleway ) then 6
|
||||
else if ( or bicycle=designated bicycle_road=yes ) then 6
|
||||
else if ( highway=track ) then if tracktype=grade1 then 6 else 4
|
||||
else if ( highway=bridleway|road|path|footway ) then 4
|
||||
else if ( highway=steps ) then 2
|
||||
else if ( highway=pedestrian ) then 2
|
||||
else 0
|
||||
|
||||
# some more classifying bits used for voice hint generation...
|
||||
|
||||
assign isroundabout = junction=roundabout
|
||||
assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
|
||||
assign isgoodforcars = if greater priorityclassifier 6 then true
|
||||
else if ( or isresidentialorliving highway=service ) then true
|
||||
else if ( and highway=track tracktype=grade1 ) then true
|
||||
else false
|
||||
|
||||
|
||||
# ... encoded into a bitmask
|
||||
|
||||
assign classifiermask add multiply isroundabout 4
|
||||
add multiply islinktype 8
|
||||
multiply isgoodforcars 16
|
||||
|
||||
# include `smoothness=` tags in the response's WayTags for track analysis
|
||||
assign dummyUsage = smoothness=
|
||||
|
||||
---context:node # following code refers to node tags
|
||||
|
||||
assign defaultaccess
|
||||
switch access=
|
||||
1 # add default barrier restrictions here!
|
||||
switch or access=private access=no
|
||||
0
|
||||
1
|
||||
|
||||
assign bikeaccess
|
||||
or nodeaccessgranted=yes
|
||||
switch bicycle=
|
||||
switch vehicle=
|
||||
defaultaccess
|
||||
switch or vehicle=private vehicle=no
|
||||
0
|
||||
1
|
||||
switch or bicycle=private or bicycle=no bicycle=dismount
|
||||
0
|
||||
1
|
||||
|
||||
assign footaccess
|
||||
or bicycle=dismount
|
||||
switch foot=
|
||||
defaultaccess
|
||||
switch or foot=private foot=no
|
||||
0
|
||||
1
|
||||
|
||||
assign initialcost switch or bikeaccess footaccess 0 1000000
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
# *** The trekking profile is for slow travel
|
||||
# *** and avoiding car traffic, but still with
|
||||
# *** a focus on approaching your destination
|
||||
# *** efficiently.
|
||||
|
||||
---context:global # following code refers to global config
|
||||
|
||||
# Bike profile
|
||||
assign validForBikes = true
|
||||
|
||||
# Use the following switches to change behaviour
|
||||
assign allow_steps = true # %allow_steps% | Set to false to disallow steps | boolean
|
||||
assign allow_ferries = true # %allow_ferries% | set to false to disallow ferries | boolean
|
||||
assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set to true for better elevation results | boolean
|
||||
assign stick_to_cycleroutes = false # %stick_to_cycleroutes% | Set to true to just follow cycleroutes | boolean
|
||||
assign avoid_unsafe = false # %avoid_unsafe% | Set to true to avoid standard highways | boolean
|
||||
assign considerTurnRestrictions = true # %considerTurnRestrictions% | Set to true to take turn restrictions into account | boolean
|
||||
assign processUnusedTags = false # %processUnusedTags% | Set to true to output unused tags in data tab | boolean
|
||||
|
||||
# Change elevation parameters
|
||||
assign consider_elevation = true # %consider_elevation% | Set to false to ignore elevation in routing | boolean
|
||||
assign downhillcost = 60 # %downhillcost% | Cost for going downhill | number
|
||||
assign downhillcutoff = 1.5 # %downhillcutoff% | Gradients below this value in percents are not counted. | number
|
||||
assign uphillcost = 0 # %uphillcost% | Cost for going uphill | number
|
||||
assign uphillcutoff = 1.5 # %uphillcutoff% | Gradients below this value in percents are not counted. | number
|
||||
|
||||
assign downhillcost = if consider_elevation then downhillcost else 0
|
||||
assign uphillcost = if consider_elevation then uphillcost else 0
|
||||
|
||||
# Kinematic model parameters (travel time computation)
|
||||
assign totalMass = 90 # %totalMass% | Mass (in kg) of the bike + biker, for travel time computation | number
|
||||
assign maxSpeed = 45 # %maxSpeed% | Absolute maximum speed (in km/h), for travel time computation | number
|
||||
assign S_C_x = 0.225 # %S_C_x% | Drag coefficient times the reference area (in m^2), for travel time computation | number
|
||||
assign C_r = 0.01 # %C_r% | Rolling resistance coefficient (dimensionless), for travel time computation | number
|
||||
assign bikerPower = 100 # %bikerPower% | Average power (in W) provided by the biker, for travel time computation | number
|
||||
|
||||
# Turn instructions settings
|
||||
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style]
|
||||
assign turnInstructionCatchingRange = 40 # %turnInstructionCatchingRange% | Within this distance (in m) several turning instructions are combined into one and the turning angles are better approximated to the general direction | number
|
||||
assign turnInstructionRoundabouts = true # %turnInstructionRoundabouts% | Set to "false" to avoid generating special turning instructions for roundabouts | boolean
|
||||
|
||||
|
||||
---context:way # following code refers to way-tags
|
||||
|
||||
# classifier constants
|
||||
assign classifier_none = 1
|
||||
assign classifier_ferry = 2
|
||||
|
||||
#
|
||||
# pre-calculate some logical expressions
|
||||
#
|
||||
|
||||
assign any_cycleroute =
|
||||
if route_bicycle_icn=yes then true
|
||||
else if route_bicycle_ncn=yes then true
|
||||
else if route_bicycle_rcn=yes then true
|
||||
else if route_bicycle_lcn=yes then true
|
||||
else false
|
||||
|
||||
assign nodeaccessgranted =
|
||||
if any_cycleroute then true
|
||||
else lcn=yes
|
||||
|
||||
assign is_ldcr =
|
||||
if ignore_cycleroutes then false
|
||||
else any_cycleroute
|
||||
|
||||
assign isbike = or bicycle_road=yes or bicycle=yes or or bicycle=permissive bicycle=designated lcn=yes
|
||||
assign ispaved = surface=paved|asphalt|concrete|paving_stones|sett
|
||||
assign isunpaved = not or surface= or ispaved surface=fine_gravel|cobblestone
|
||||
assign probablyGood = or ispaved and ( or isbike highway=footway ) not isunpaved
|
||||
|
||||
|
||||
#
|
||||
# this is the cost (in Meter) for a 90-degree turn
|
||||
# The actual cost is calculated as turncost*cos(angle)
|
||||
# (Suppressing turncost while following longdistance-cycleways
|
||||
# makes them a little bit more magnetic)
|
||||
#
|
||||
assign turncost = if is_ldcr then 0
|
||||
else if junction=roundabout then 0
|
||||
else 90
|
||||
|
||||
|
||||
#
|
||||
# for any change in initialclassifier, initialcost is added once
|
||||
#
|
||||
assign initialclassifier =
|
||||
if route=ferry then classifier_ferry
|
||||
else classifier_none
|
||||
|
||||
|
||||
#
|
||||
# calculate the initial cost
|
||||
# this is added to the total cost each time the costfactor
|
||||
# changed
|
||||
#
|
||||
assign initialcost =
|
||||
if ( equal initialclassifier classifier_ferry ) then 10000
|
||||
else 0
|
||||
|
||||
#
|
||||
# implicit access here just from the motorroad tag
|
||||
# (implicit access rules from highway tag handled elsewhere)
|
||||
#
|
||||
assign defaultaccess =
|
||||
if access= then not motorroad=yes
|
||||
else if access=private|no then false
|
||||
else true
|
||||
|
||||
#
|
||||
# calculate logical bike access
|
||||
#
|
||||
assign bikeaccess =
|
||||
if any_cycleroute then true
|
||||
else if bicycle= then
|
||||
(
|
||||
if bicycle_road=yes then true
|
||||
else if vehicle= then ( if highway=footway then false else defaultaccess )
|
||||
else not vehicle=private|no
|
||||
)
|
||||
else not bicycle=private|no|dismount|use_sidepath
|
||||
|
||||
#
|
||||
# calculate logical foot access
|
||||
#
|
||||
assign footaccess =
|
||||
if bikeaccess then true
|
||||
else if bicycle=dismount then true
|
||||
else if foot= then defaultaccess
|
||||
else not foot=private|no
|
||||
|
||||
#
|
||||
# if not bike-, but foot-acess, just a moderate penalty,
|
||||
# otherwise access is forbidden
|
||||
#
|
||||
assign accesspenalty =
|
||||
if bikeaccess then 0
|
||||
else if footaccess then 4
|
||||
else 10000
|
||||
|
||||
#
|
||||
# handle one-ways. On primary roads, wrong-oneways should
|
||||
# be close to forbidden, while on other ways we just add
|
||||
# 4 to the costfactor (making it at least 5 - you are allowed
|
||||
# to push your bike)
|
||||
#
|
||||
assign badoneway =
|
||||
if reversedirection=yes then
|
||||
if oneway:bicycle=yes then true
|
||||
else if oneway= then junction=roundabout
|
||||
else oneway=yes|true|1
|
||||
else oneway=-1
|
||||
|
||||
assign onewaypenalty =
|
||||
if ( badoneway ) then
|
||||
(
|
||||
if ( cycleway=opposite|opposite_lane|opposite_track ) then 0
|
||||
else if ( oneway:bicycle=no ) then 0
|
||||
else if ( highway=primary|primary_link ) then 50
|
||||
else if ( highway=secondary|secondary_link ) then 30
|
||||
else if ( highway=tertiary|tertiary_link ) then 20
|
||||
else 4.0
|
||||
)
|
||||
else 0.0
|
||||
|
||||
#
|
||||
# calculate the cost-factor, which is the factor
|
||||
# by which the distance of a way-segment is multiplied
|
||||
# to calculate the cost of that segment. The costfactor
|
||||
# must be >=1 and it's supposed to be close to 1 for
|
||||
# the type of way the routing profile is searching for
|
||||
#
|
||||
assign isresidentialorliving = or highway=residential|living_street living_street=yes
|
||||
assign costfactor
|
||||
|
||||
#
|
||||
# exclude rivers, rails etc.
|
||||
#
|
||||
if ( and highway= not route=ferry ) then 10000
|
||||
|
||||
#
|
||||
# exclude motorways and proposed roads
|
||||
#
|
||||
else if ( highway=motorway|motorway_link ) then 10000
|
||||
else if ( highway=proposed|abandoned ) then 10000
|
||||
|
||||
#
|
||||
# all other exclusions below (access, steps, ferries,..)
|
||||
# should not be deleted by the decoder, to be available
|
||||
# in voice-hint-processing
|
||||
#
|
||||
else min 9999
|
||||
|
||||
#
|
||||
# apply oneway-and access-penalties
|
||||
#
|
||||
add max onewaypenalty accesspenalty
|
||||
|
||||
#
|
||||
# steps and ferries are special. Note this is handled
|
||||
# before the cycleroute-switch, to be able
|
||||
# to really exlude them be setting cost to infinity
|
||||
#
|
||||
if ( highway=steps ) then ( if allow_steps then 40 else 10000 )
|
||||
else if ( route=ferry ) then ( if allow_ferries then 5.67 else 10000 )
|
||||
|
||||
#
|
||||
# handle long-distance cycle-routes.
|
||||
#
|
||||
else if ( is_ldcr ) then 1 # always treated as perfect (=1)
|
||||
else
|
||||
add ( if stick_to_cycleroutes then 0.5 else 0.05 ) # everything else somewhat up
|
||||
|
||||
#
|
||||
# some other highway types
|
||||
#
|
||||
if ( highway=pedestrian ) then 3
|
||||
else if ( highway=bridleway ) then 5
|
||||
else if ( highway=cycleway ) then 1
|
||||
else if ( isresidentialorliving ) then ( if isunpaved then 1.5 else 1.1 )
|
||||
else if ( highway=service ) then ( if isunpaved then 1.6 else 1.3 )
|
||||
|
||||
#
|
||||
# tracks and track-like ways are rated mainly be tracktype/grade
|
||||
# But note that if no tracktype is given (mainly for road/path/footway)
|
||||
# it can be o.k. if there's any other hint for quality
|
||||
#
|
||||
else if ( highway=track|road|path|footway ) then
|
||||
(
|
||||
if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 )
|
||||
else if ( tracktype=grade2 ) then ( if probablyGood then 1.1 else 2.0 )
|
||||
else if ( tracktype=grade3 ) then ( if probablyGood then 1.5 else 3.0 )
|
||||
else if ( tracktype=grade4 ) then ( if probablyGood then 2.0 else 5.0 )
|
||||
else if ( tracktype=grade5 ) then ( if probablyGood then 3.0 else 5.0 )
|
||||
else ( if probablyGood then 1.0 else 5.0 )
|
||||
)
|
||||
|
||||
#
|
||||
# When avoiding unsafe ways, avoid highways without a bike hint
|
||||
#
|
||||
else add ( if ( and avoid_unsafe not isbike ) then 2 else 0 )
|
||||
|
||||
#
|
||||
# actuals roads are o.k. if we have a bike hint
|
||||
#
|
||||
if ( highway=trunk|trunk_link ) then ( if isbike then 1.5 else 10 )
|
||||
else if ( highway=primary|primary_link ) then ( if isbike then 1.2 else 3 )
|
||||
else if ( highway=secondary|secondary_link ) then ( if isbike then 1.1 else 1.6 )
|
||||
else if ( highway=tertiary|tertiary_link ) then ( if isbike then 1.0 else 1.4 )
|
||||
else if ( highway=unclassified ) then ( if isbike then 1.0 else 1.3 )
|
||||
|
||||
#
|
||||
# default for any other highway type not handled above
|
||||
#
|
||||
else 2.0
|
||||
|
||||
|
||||
# way priorities used for voice hint generation
|
||||
|
||||
assign priorityclassifier =
|
||||
|
||||
if ( highway=motorway ) then 30
|
||||
else if ( highway=motorway_link ) then 29
|
||||
else if ( highway=trunk ) then 28
|
||||
else if ( highway=trunk_link ) then 27
|
||||
else if ( highway=primary ) then 26
|
||||
else if ( highway=primary_link ) then 25
|
||||
else if ( highway=secondary ) then 24
|
||||
else if ( highway=secondary_link ) then 23
|
||||
else if ( highway=tertiary ) then 22
|
||||
else if ( highway=tertiary_link ) then 21
|
||||
else if ( highway=unclassified ) then 20
|
||||
else if ( isresidentialorliving ) then 6
|
||||
else if ( highway=service ) then 6
|
||||
else if ( highway=cycleway ) then 6
|
||||
else if ( or bicycle=designated bicycle_road=yes ) then 6
|
||||
else if ( highway=track ) then if tracktype=grade1 then 6 else 4
|
||||
else if ( highway=bridleway|road|path|footway ) then 4
|
||||
else if ( highway=steps ) then 2
|
||||
else if ( highway=pedestrian ) then 2
|
||||
else 0
|
||||
|
||||
# some more classifying bits used for voice hint generation...
|
||||
|
||||
assign isbadoneway = not equal onewaypenalty 0
|
||||
assign isgoodoneway = if reversedirection=yes then oneway=-1
|
||||
else if oneway= then junction=roundabout else oneway=yes|true|1
|
||||
assign isroundabout = junction=roundabout
|
||||
assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
|
||||
assign isgoodforcars = if greater priorityclassifier 6 then true
|
||||
else if ( or isresidentialorliving highway=service ) then true
|
||||
else if ( and highway=track tracktype=grade1 ) then true
|
||||
else false
|
||||
|
||||
# ... encoded into a bitmask
|
||||
|
||||
assign classifiermask add isbadoneway
|
||||
add multiply isgoodoneway 2
|
||||
add multiply isroundabout 4
|
||||
add multiply islinktype 8
|
||||
multiply isgoodforcars 16
|
||||
|
||||
# include `smoothness=` tags in the response's WayTags for track analysis
|
||||
assign dummyUsage = smoothness=
|
||||
|
||||
---context:node # following code refers to node tags
|
||||
|
||||
assign defaultaccess =
|
||||
if ( access= ) then true # add default barrier restrictions here!
|
||||
else if ( access=private|no ) then false
|
||||
else true
|
||||
|
||||
assign bikeaccess =
|
||||
if nodeaccessgranted=yes then true
|
||||
else if bicycle= then
|
||||
(
|
||||
if vehicle= then defaultaccess
|
||||
else not vehicle=private|no
|
||||
)
|
||||
else not bicycle=private|no|dismount
|
||||
|
||||
assign footaccess =
|
||||
if bicycle=dismount then true
|
||||
else if foot= then defaultaccess
|
||||
else not foot=private|no
|
||||
|
||||
assign initialcost =
|
||||
if bikeaccess then 0
|
||||
else ( if footaccess then 100 else 1000000 )
|
||||
|
|
@ -104,10 +104,26 @@ class MainActivity : TauriActivity() {
|
|||
if (!cmdFile.exists()) cmdFile.createNewFile()
|
||||
val bleCmdFile = File(appDataDir, "ble_command.txt")
|
||||
if (!bleCmdFile.exists()) bleCmdFile.createNewFile()
|
||||
val brouterCmdFile = File(appDataDir, "brouter_command.txt")
|
||||
if (!brouterCmdFile.exists()) brouterCmdFile.createNewFile()
|
||||
|
||||
// Copy BRouter routing profiles (+ lookups.dat) from assets to files/brouter/profiles2.
|
||||
// The embedded engine (btools.router) reads them by path alongside the segments.
|
||||
try {
|
||||
val profDir = File(filesDir, "brouter/profiles2")
|
||||
profDir.mkdirs()
|
||||
for (f in (assets.list("brouter/profiles2") ?: arrayOf())) {
|
||||
assets.open("brouter/profiles2/$f").use { i -> File(profDir, f).outputStream().use { o -> i.copyTo(o) } }
|
||||
}
|
||||
Log.d("BalDav", "BRouter profiles copied to ${profDir.absolutePath}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("BalDav", "Failed to copy BRouter profiles", e)
|
||||
}
|
||||
|
||||
setupCommandWatcher(appDataDir)
|
||||
processGpsCommand(appDataDir)
|
||||
processBleCommand(appDataDir)
|
||||
processBrouterCommand(appDataDir)
|
||||
Log.d("BalDav", "Command watcher started on ${appDataDir.absolutePath}")
|
||||
|
||||
// Diagnostic HTTP endpoint on localhost — GET /gps makes the app request a live
|
||||
|
|
@ -161,6 +177,8 @@ class MainActivity : TauriActivity() {
|
|||
processGpsCommand(appDataDir)
|
||||
processBleCommand(appDataDir)
|
||||
}
|
||||
} else if (path == "brouter_command.txt") {
|
||||
runOnUiThread { processBrouterCommand(appDataDir) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,6 +186,69 @@ class MainActivity : TauriActivity() {
|
|||
Log.d("BalDav", "FileObserver started on ${appDataDir.absolutePath}")
|
||||
}
|
||||
|
||||
// Offline routing via the embedded BRouter engine (btools.router). Request/response
|
||||
// over files like the GPS bridge: Rust writes brouter_request.json + brouter_command.txt
|
||||
// ("route:<id>"), we route on a background thread (routing is heavy — never the UI
|
||||
// thread) and write brouter_result_<id>.json ({ok, gpx} | {ok:false, error}).
|
||||
private fun processBrouterCommand(appDataDir: File) {
|
||||
val cmdFile = File(appDataDir, "brouter_command.txt")
|
||||
if (!cmdFile.exists()) return
|
||||
val cmd = cmdFile.readText().trim()
|
||||
if (cmd.isEmpty()) return
|
||||
cmdFile.writeText("")
|
||||
if (!cmd.startsWith("route:")) return
|
||||
val id = cmd.substringAfter("route:")
|
||||
Thread {
|
||||
val resultFile = File(appDataDir, "brouter_result_$id.json")
|
||||
val out = org.json.JSONObject()
|
||||
try {
|
||||
val req = org.json.JSONObject(File(appDataDir, "brouter_request.json").readText())
|
||||
val profile = req.getString("profile")
|
||||
val arr = req.getJSONArray("lonlats")
|
||||
val pts = ArrayList<DoubleArray>()
|
||||
for (i in 0 until arr.length()) {
|
||||
val pair = arr.getJSONArray(i)
|
||||
pts.add(doubleArrayOf(pair.getDouble(0), pair.getDouble(1)))
|
||||
}
|
||||
val profilePath = File(filesDir, "brouter/profiles2/$profile.brf").absolutePath
|
||||
val segmentsDir = File(filesDir, "brouter/segments4")
|
||||
val gpx = routeBrouter(segmentsDir, profilePath, pts)
|
||||
out.put("ok", true)
|
||||
out.put("gpx", gpx)
|
||||
} catch (e: Exception) {
|
||||
Log.e("BalDav", "BRouter route failed", e)
|
||||
out.put("ok", false)
|
||||
out.put("error", e.message ?: e.toString())
|
||||
}
|
||||
try { resultFile.writeText(out.toString()) } catch (e: Exception) { Log.e("BalDav", "brouter result write failed", e) }
|
||||
}.start()
|
||||
}
|
||||
|
||||
// Minimal embedding recipe (mirrors btools.server.request.ServerHandler): build a
|
||||
// RoutingContext pointing at the profile (.brf; the engine parses it via ProfileCache
|
||||
// and loads lookups.dat from the same dir), waypoints as OsmNodeNamed (ilon/ilat are
|
||||
// micro-degrees), run the engine, return the track as GPX.
|
||||
private fun routeBrouter(segmentsDir: File, profilePath: String, pts: List<DoubleArray>): String {
|
||||
val rc = btools.router.RoutingContext()
|
||||
rc.memoryclass = 128
|
||||
rc.localFunction = profilePath
|
||||
rc.setAlternativeIdx(0)
|
||||
val wps = ArrayList<btools.router.OsmNodeNamed>()
|
||||
for (p in pts) {
|
||||
val n = btools.router.OsmNodeNamed()
|
||||
n.ilon = ((p[0] + 180.0) * 1000000.0 + 0.5).toInt()
|
||||
n.ilat = ((p[1] + 90.0) * 1000000.0 + 0.5).toInt()
|
||||
n.name = "wp"
|
||||
wps.add(n)
|
||||
}
|
||||
val re = btools.router.RoutingEngine(null, null, segmentsDir, wps, rc)
|
||||
re.doRun(0)
|
||||
val err = re.getErrorMessage()
|
||||
if (err != null && err.isNotEmpty()) throw RuntimeException(err)
|
||||
val track = re.getFoundTrack() ?: throw RuntimeException("no track found")
|
||||
return track.formatAsGpx()
|
||||
}
|
||||
|
||||
private fun processGpsCommand(appDataDir: File) {
|
||||
val cmdFile = File(appDataDir, "gps_command.txt")
|
||||
if (!cmdFile.exists()) return
|
||||
|
|
|
|||
|
|
@ -393,6 +393,48 @@ fn gps_service_command(app: tauri::AppHandle, cmd: String) -> Result<String, Str
|
|||
Ok(format!("sent:{}", cmd))
|
||||
}
|
||||
|
||||
// Offline routing via the embedded BRouter engine (Kotlin runs btools.router). Same
|
||||
// file bridge as GPS: write the request + a "route:<id>" command where MainActivity's
|
||||
// FileObserver watches, then poll for brouter_result_<id>.json. `lonlats` is a JSON
|
||||
// array of [lon,lat] pairs; returns the Kotlin result JSON ({ok,gpx}|{ok:false,error}).
|
||||
#[tauri::command]
|
||||
fn brouter_route(app: tauri::AppHandle, lonlats: String, profile: String) -> Result<String, String> {
|
||||
let data_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
|
||||
std::fs::create_dir_all(&data_dir).ok();
|
||||
let id = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH).map_err(|e| e.to_string())?
|
||||
.as_millis().to_string();
|
||||
let req = format!(
|
||||
"{{\"profile\":{},\"lonlats\":{}}}",
|
||||
serde_json::to_string(&profile).map_err(|e| e.to_string())?,
|
||||
lonlats
|
||||
);
|
||||
let result_name = format!("brouter_result_{}.json", id);
|
||||
// Write request + command to app_data_dir AND app root (where Kotlin watches).
|
||||
let root = data_dir.parent().and_then(|p| p.parent()).map(|p| p.to_path_buf());
|
||||
let write_both = |name: &str, content: &str| {
|
||||
let _ = std::fs::write(data_dir.join(name), content);
|
||||
if let Some(r) = &root { let _ = std::fs::write(r.join(name), content); }
|
||||
};
|
||||
write_both("brouter_request.json", &req);
|
||||
write_both("brouter_command.txt", &format!("route:{}", id));
|
||||
// Poll for the result (routing can take a few seconds; cap ~60s).
|
||||
let mut candidates = vec![data_dir.join(&result_name)];
|
||||
if let Some(r) = &root { candidates.push(r.join(&result_name)); }
|
||||
for _ in 0..600 {
|
||||
for p in &candidates {
|
||||
if p.exists() {
|
||||
if let Ok(s) = std::fs::read_to_string(p) {
|
||||
let _ = std::fs::remove_file(p);
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
Err("brouter: timeout waiting for result".into())
|
||||
}
|
||||
|
||||
// Read GPS points from service's JSONL file
|
||||
#[tauri::command]
|
||||
fn gps_service_read_points(app: tauri::AppHandle) -> Result<String, String> {
|
||||
|
|
@ -904,7 +946,7 @@ pub fn run() {
|
|||
.add_migrations("sqlite:baldav.db", migrations)
|
||||
.build(),
|
||||
)
|
||||
.invoke_handler(tauri::generate_handler![get_safe_insets, import_seed_products, import_tiles, download_file, get_points_filename, delete_points_file, gps_set_session_id, list_points_files, read_points_file, pick_file_open, get_picked_file, debug_native_paths, rebuild_roads, get_road_points, get_app_info, gps_service_command, gps_service_read_points, save_file, get_update_info, http_request, chzn_http, ble::ble_scan, ble::ble_scan_results, ble::ble_connect, ble::ble_disconnect, ble::ble_get_hr, ble::ble_is_connected, ble::ble_check_bluetooth, ble::ble_save_device, ble::ble_clear_hr])
|
||||
.invoke_handler(tauri::generate_handler![get_safe_insets, import_seed_products, import_tiles, download_file, get_points_filename, delete_points_file, gps_set_session_id, list_points_files, read_points_file, pick_file_open, get_picked_file, debug_native_paths, rebuild_roads, get_road_points, get_app_info, gps_service_command, gps_service_read_points, brouter_route, save_file, get_update_info, http_request, chzn_http, ble::ble_scan, ble::ble_scan_results, ble::ble_connect, ble::ble_disconnect, ble::ble_get_hr, ble::ble_is_connected, ble::ble_check_bluetooth, ble::ble_save_device, ble::ble_clear_hr])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue