Compare commits

...

No commits in common. "6c0a32bf17cf867aafe8ce52bd9fbbe50d8687b4" and "2551394cd33576251f97242453c6528d8d9be277" have entirely different histories.

13 changed files with 32 additions and 32 deletions

View file

@ -26,8 +26,8 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л
**Success:** на телефоне с вручную положенным тайлом региона (adb push для спайка) вызов
`BRouter.route([[lon,lat],[lon,lat]], "shortest")` возвращает дорожный GPX/GeoJSON, а не ошибку.
**Tests:** ручной вызов из DevTools (CDP) + лог движка; сверить, что трек идёт по дорогам.
**Status:** DONE (проверено на устройстве 2026-09-09). `shortest` Ростов 5.4км за 3.2с;
`trekking` кросс-тайл Ростов→Новочеркасск 44.8км/набор 316м за 1.2с; GPX с track-length/ascend/cost.
**Status:** DONE (проверено на устройстве 2026-09-09). `shortest` по региону 5.4км за 3.2с;
`trekking` кросс-тайл в регионе 44.8км/набор 316м за 1.2с; GPX с track-length/ascend/cost.
ВАЖНО: bundled `lookups.dat` из 1.6.3 — версии 10, а живые сегменты brouter.de/segments4 — v11
(«lookup version mismatch»). Ассеты заменены на v11 из репозитория (abrensch/brouter master:
misc/profiles2/{lookups.dat,trekking.brf,shortest.brf}); движок 1.6.3 их принимает. Профили/lookups
@ -59,7 +59,7 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л
ЗЕРКАЛО (по решению пользователя, 2026-09-09): данные качаются НЕ с brouter.de напрямую, а со
своего зеркала. Источники в загрузчике по порядку: LAN `http://192.168.1.1:88/brouter/segments4`
→ WAN `http://77.66.192.45:8088/brouter/segments4` → brouter.de (запас). Зеркало на роутере
→ WAN `http://mirror.invalid:8088/brouter/segments4` → brouter.de (запас). Зеркало на роутере
(Keenetic uhttpd :88, docroot /opt/share/www → файлы в /opt/share/www/brouter/{segments4,profiles2},
USB, всегда включён). ПРОВЕРЕНО: телефон качает тайл с LAN-зеркала за <2с, маршрут на нём ок.
Плюс BRouter RouteServer на ПК (jar, /mnt/02F6E7F6F6E7E7BD/brouter-mirror, systemd brouter.service —
@ -80,8 +80,8 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л
«🚲 По дорогам»: waypoints остаются редактируемыми маркерами, линия между ними прокладывается
BRouter'ом (`brouterMode`/`routeGeom`, debounce 450мс), сохраняется дорожная геометрия. Роутинг —
`BRouter.route` (router.js): Android → встроенный движок, иначе (десктоп/дом) → PC-RouteServer по
HTTP (`http_request`, мимо CSP; хосты LAN 192.168.1.18:17777 / WAN 77.66.192.45:17777).
Сквозной прогон в редакторе: 3 точки по Новочеркасску → прямая 1.69км сменилась дорожной линией
HTTP (`http_request`, мимо CSP; хосты LAN 192.168.1.18:17777 / WAN mirror.invalid:17777).
Сквозной прогон в редакторе: 3 точки в городе → прямая 1.69км сменилась дорожной линией
2.25км по улицам (Буденновская → кольцо → Ермака). БАГ, найденный и исправленный при этой проверке:
`doBrouter` в конце звал `changed()`, который обнулял `routeGeom` и заново ставил `doBrouter` в
очередь — бесконечный цикл, дорожная линия никогда не рисовалась. Заменено на прямой
@ -108,7 +108,7 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л
## Release-хардненинг (item 4, сделано)
- `network_security_config.xml` (res/xml): cleartext OFF глобально в release, разрешён только хостам
зеркала/RouteServer (192.168.1.1 / 192.168.1.18 / 77.66.192.45); `debug-overrides` — всё в debug.
зеркала/RouteServer (192.168.1.1 / 192.168.1.18 / mirror.invalid); `debug-overrides` — всё в debug.
Подключён в AndroidManifest (`android:networkSecurityConfig`).
- `proguard-rules.pro`: `-keep class btools.** { *; }` + `-dontwarn btools.**` — минификация release
не вырежет встроенный движок.
@ -127,7 +127,7 @@ ATM, банк, скамейка, туалеты, вода, кафе/бар/фа
автобусная остановка, пекарня/супермаркет/кофе, смотровая, укрытие, пикник и т.д. (набор — как в
brouter-web overlays, см. скрины). С фильтром категорий (тумблеры), иконки скачать и рисовать.
**Что проанализировать перед реализацией:**
- Источник данных POI ОФФЛАЙН: Overpass-выгрузка по региону (bbox ~100км вокруг Новочеркасска) в
- Источник данных POI ОФФЛАЙН: Overpass-выгрузка по региону (bbox ~100км вокруг опорной точки) в
локальный стор (SQLite/JSON, как seed/tiles), т.к. сеть в поле недоступна. rd5 не годится как
POI-хранилище. Обновление — по проводу с зеркала (как тайлы/сегменты).
- Иконки: взять набор (напр. используемый brouter-web / maki / OSM-carto sprites), лицензия,
@ -136,12 +136,12 @@ brouter-web overlays, см. скрины). С фильтром категори
z-order (POI над тайлами/дорогами, под треком/паком).
- Перф: сотни-тысячи точек — кластеризация/культинг по зуму и bbox видимой области.
**Статус:** v1 СДЕЛАНО (проверено функционально на устройстве). Данные: Overpass по ~100км вокруг
Новочеркасска (адаптивная выгрузка — дробить не понадобилось, весь bbox отдал 5662 POI одним
опорной точки (адаптивная выгрузка — дробить не понадобилось, весь bbox отдал 5662 POI одним
запросом), компактный `frontend/data/pois.json` [{la,lo,c,n}] (358КБ, бандл, оффлайн). Отрисовка:
`frontend/js/poi.js` — круглые контрастные значки (эмодзи в кружке, `.poi-badge`, светлый/тёмный
под `body.dark-map`), куллинг по видимым границам + от zoom 13, cap 500/кадр; Leaflet-контрол 📍 с
панелью категорий (чекбоксы), выбор в localStorage. Привязан к картам записи/деталей/дорог
(`POI.attach`). Проверено: 124 значка в кадре z16 над Новочеркасском, границы/фильтр корректны.
(`POI.attach`). Проверено: 124 значка в кадре z16 над регионом, границы/фильтр корректны.
ОСТАЛОСЬ: визуальная проверка на живом экране (WebView в CDP не рисует — скрин чёрный); при желании —
расширить область за 100км и добавить недостающие категории (сейчас все 21 категория есть).

View file

@ -27,7 +27,7 @@
| `js/sync.js` | ~300 | Синхронизация между устройствами (LAN, PHP сервер на роутере) |
| `js/chzn.js` | ~330 | Честный знак (CRPT). Скан DataMatrix → карточка Нацкаталога → префилл КБЖУ. `ChZ`: сессия в localStorage (`chzn_session`), `importFromText`, `refresh`/`ensureSid` (sid 48ч, refreshToken ~год), `authedPost/Get` (userId+sid в query, авто-refresh на 401), `check`, `nutritionInfo` (витамины, часто 204), `normalizeCode` (реконструкция GS 0x1D перед AI 93), `parseCard` (группа «Энергетическая ценность…», имена атрибутов разнятся у производителей), `resolve`. `/authorize/anonymous` закрыт Play Integrity — свою сессию не завести, только импорт из приложения (`tools/chzn_export.sh`) |
| `js/tilecache.js` | 178 | Оффлайн кэш тайлов карты (IndexedDB) |
| `js/router.js` | ~80 | Оффлайн-роутинг BRouter. `BRouter.route(points,[profile])`: Android → встроенный движок (`invoke("brouter_route")`), иначе/при ошибке → PC-RouteServer по HTTP (`http_request`, мимо CSP; LAN 192.168.1.18:17777 / WAN 77.66.192.45:17777). Профили `trekking` (планирование) / `shortest` (гэпы). Докачка сегментов `.rd5`: `downloadTile/downloadStatus/listTiles/downloadTileAndWait` |
| `js/router.js` | ~80 | Оффлайн-роутинг BRouter. `BRouter.route(points,[profile])`: Android → встроенный движок (`invoke("brouter_route")`), иначе/при ошибке → PC-RouteServer по HTTP (`http_request`, мимо CSP; LAN 192.168.1.18:17777 / WAN mirror.invalid:17777). Профили `trekking` (планирование) / `shortest` (гэпы). Докачка сегментов `.rd5`: `downloadTile/downloadStatus/listTiles/downloadTileAndWait` |
| `js/poi.js` | ~200 | POI-оверлей на картах: круглые контрастные бейджи (эмодзи в круге, не пины), фильтр категорий (`POI.attach(map)`, данные `data/pois.json`, localStorage `baldav_poi_cats/shown`) |
| `js/bmr.js` | ~70 | BMR (Mifflin-St Jeor), РСП из БД (33 нутриента), полураспад |
| `css/styles.css` | ~2100 | Темная тема, мобильный дизайн, HR зоны, ландшафтный режим |

View file

@ -21,7 +21,7 @@
- **Оффлайн-роутинг (BRouter)** — движок BRouter встроен в приложение (Android): маршруты строятся по дорогам без сети, сегменты `.rd5` региона качаются с домашнего зеркала прямо в приложении (экран дорог → «Сегменты»). Используется в редакторе маршрутов («По дорогам») и для заполнения GPS-пропусков. На десктопе/дома движка нет — те же вызовы уходят на домашний BRouter-RouteServer по HTTP.
- **Заполнение GPS-пропусков по дороге** — на детали трека тумблер **«🧩»** прокладывает дорожный путь (BRouter, профиль shortest) через каждый GPS-дропаут (разрыв по времени + реальная хорда) и рисует его голубым пунктиром поверх трека; если дорога в разы длиннее хорды — оставляет хорду. Оверлей, данные трека не меняет.
- **Де-джиттер трека** — сглаживание дрожащего GPS-трека методом Савицкого-Голея (настраиваемое окно/число итераций, живое превью на карте детали трека). Хранится обратимо: оригинал и сглаженный вариант лежат раздельно, калории пересчитываются по сглаженным точкам, откат к оригиналу в один тап. Точность каждого фикса (`accuracy_m`) пишется в БД: при пересчёте трека точки хуже порога «Макс. погрешность GPS» исключаются из расчёта и линии (обратимо — точки в БД остаются; поднять порог = вернуть). Работает во всех пересчётах (кнопка «Пересчитать», «Пересчитать всё», после де-джиттера); импорт/старьё без точности и dead-reckoning заполнения не трогает.
- **POI на карте** — точки интереса (кафе, магазины, вода, туалеты, банки/ATM, остановки, укрытия, смотровые и т.д.) круглыми контрастными значками (эмодзи в кружке, читаемо на светлой и тёмной карте). Кнопка `📍` с панелью-фильтром категорий; рендер по видимой области от зума 13. Данные оффлайн (`data/pois.json`, ~100 км вокруг Новочеркасска, 5662 объекта из OSM/Overpass). На картах записи, деталей трека и дорог.
- **POI на карте** — точки интереса (кафе, магазины, вода, туалеты, банки/ATM, остановки, укрытия, смотровые и т.д.) круглыми контрастными значками (эмодзи в кружке, читаемо на светлой и тёмной карте). Кнопка `📍` с панелью-фильтром категорий; рендер по видимой области от зума 13. Данные оффлайн (`data/pois.json`, ~100 км вокруг опорной точки, 5662 объекта из OSM/Overpass). На картах записи, деталей трека и дорог.
- **Тёмная карта** — тумблер `☾` на экранах записи, деталей трека и дорог: инвертирует ТОЛЬКО слой тайлов (`invert + hue-rotate 180° + brightness`, трек/маршрут/дороги остаются цветными), выбор запоминается. Работает одинаково на офлайн-тайлах и онлайне, без ключей/квот/перепаковки.
- **Метки ветра** — кнопка «Ветер» на экране записи: тап открывает интервал встречного ветра, тап закрывает. Метки хранятся с треком (`wind_marks`) как эталон для проверки оценки ветра по треку; на расчёт калорий/мощности не влияют. Голосовые метки — на будущее.
- **Экспорт/импорт** — свои продукты, рецепты и добавки одним файлом (JSON).

View file

@ -38,9 +38,9 @@ BRouter — **маршрутизатор без карты**: на вход оп
- **Высоты** в сегментах берутся из **SRTM-90м** (CGIAR). Именно они дают BRouter'у профиль
рельефа, по которому он считает набор высоты (см. §4). Без SRTM сегменты соберутся, но без
высот — маршруты «с малым набором» не получатся.
- Для нашего региона (Ростовская обл., ~47N/40E) нужны тайлы, чьи SW-углы кратны 5:
- Для нашего региона (~47N/40E) нужны тайлы, чьи SW-углы кратны 5:
**`E35_N45.rd5`** (35–40E, 45–50N, ~23.8 МБ) и **`E40_N45.rd5`** (40–45E, ~14.1 МБ) —
Новочеркасск ~40.1E попадает во второй (проверено загрузкой). Одноразовая загрузка по проводу —
опорная точка ~40.1E попадает во второй (проверено загрузкой). Одноразовая загрузка по проводу —
тот же паттерн «скачать дома», что для DEM/прогноза.
- **ВЕРСИЯ ДАННЫХ (важно, проверено на устройстве).** `lookups.dat` и профили ОБЯЗАНЫ совпадать
по версии с сегментами, иначе «lookup version mismatch». Дистрибутив 1.6.3 несёт `lookups.dat`

View file

@ -43,8 +43,8 @@ README не трогал: пользовательской фичи пока н
run-as cp), запустил через CDP. Первый вызов упал: «lookup version mismatch lookups.dat=10 rd5=11»
— дистрибутив 1.6.3 несёт lookups.dat v10, а сегменты brouter.de/segments4 — v11. Взял v11
lookups.dat + trekking.brf + shortest.brf из abrensch/brouter master (misc/profiles2), заменил
вложенные ассеты; движок 1.6.3 их принял. Результаты: shortest Ростов ~5.4км за 3.2с (холодный),
trekking Ростов→Новочеркасск 44.8км/набор 316м за 1.2с (кросс-тайл), на зашитых в apk v11-данных
вложенные ассеты; движок 1.6.3 их принял. Результаты: shortest по региону ~5.4км за 3.2с (холодный),
trekking кросс-тайл в регионе 44.8км/набор 316м за 1.2с, на зашитых в apk v11-данных
617мс. Мост JS→Rust→Kotlin(движок)→GPX работает целиком. Этап 1 = DONE.
Урок: профили и lookups.dat ОБЯЗАНЫ совпадать по версии с сегментами (сейчас v11).
@ -63,7 +63,7 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4
## Дополнение 2026-09-09 (3): своё зеркало вместо прямого brouter.de
По решению пользователя данные берём со своего зеркала (не с brouter.de напрямую). Инфраструктура:
- Роутер Keenetic (белый IP 77.66.192.45): на :88 уже uhttpd (docroot /opt/share/www, USB 23ГБ free).
- Роутер Keenetic (внешний адрес mirror.invalid): на :88 уже uhttpd (docroot /opt/share/www, USB 23ГБ free).
Положил тайлы+профили в /opt/share/www/brouter/{segments4,profiles2} — раздаётся как
http://192.168.1.1:88/brouter/... (uhttpd игнорирует Range → отдаёт файл целиком; на LAN неважно).
- ПК: BRouter RouteServer (jar) в /mnt/02F6E7F6F6E7E7BD/brouter-mirror (segments4/profiles2/customs +
@ -77,11 +77,11 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4
## Дополнение 2026-09-09 (4): своё зеркало на роутере + BRouter-Web + тёмная карта
- Зеркало на Keenetic: тайлы+профили в /opt/share/www/brouter/ (uhttpd :88), доступно LAN 192.168.1.1:88
и WAN 77.66.192.45:8088 (static-NAT PPPoE0 :8088→:88). BalDav качает тайлы с зеркала (LAN→WAN→brouter.de).
и WAN mirror.invalid:8088 (static-NAT PPPoE0 :8088→:88). BalDav качает тайлы с зеркала (LAN→WAN→brouter.de).
- BRouter RouteServer на ПК: /mnt/02F6E7F6F6E7E7BD/brouter-mirror, СИСТЕМНЫЙ systemd brouter.service
(multi-user.target, автостарт без логина, поставил пользователь sudo). WAN :17777→ПК:17777.
- BRouter-Web (nrenner 0.18.1) на зеркале: http://192.168.1.1:88/brouter-web/ (+WAN), config.js → RouteServer,
дефолт-слой ÖPNV (CARTO/др. — «needs key»), центр Новочеркасск.
дефолт-слой ÖPNV (CARTO/др. — «needs key»), центр — опорная точка.
- Тёмная карта в BalDav: перебрали провайдеров (CARTO/Thunderforest/2GIS/Yandex — ключи/квоты/ToS/проекция/
недоступность в РФ; проба доступности из BalDav по мобильному Tele2: доступны OSM/CARTO/ÖPNV/Esri/2GIS,
нет OpenTopo/Yandex-vec). Транспортную инвертом — плохо в городе. Решение: OSM-стандарт + CSS
@ -93,7 +93,7 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4
на десктопе со скринами на виртуальном экране (Xvfb :99), не только по API.
Прогон в реальном UI (`npx tauri dev` на Xvfb :99, драйв через xdotool, скрины `import`):
- Stage 3 «🚲 По дорогам»: 3 опорные точки по Новочеркасску. Прямая линия 1.69км сменилась
- Stage 3 «🚲 По дорогам»: 3 опорные точки в городе. Прямая линия 1.69км сменилась
дорожной 2.25км по улицам (Буденновская → кольцо → просп. Ермака). Роутинг — PC-RouteServer
(десктоп без встроенного движка), профиль trekking.
- Stage 4 «🧩»: трек 2026-06-22 (session 1782115144011, 16.24км, два дропаута ~2км хорды) —
@ -105,7 +105,7 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4
линия НИКОГДА не рисовалась (в UI линия оставалась прямой, дистанция не менялась). Исправлено:
`doBrouter` рисует напрямую (`updatePolyline(); updateStats();`), `changed()` не трогает. Отдельным
коммитом (цикл виден в истории). Для проверки временно уводил дефолт-центр редактора на
Новочеркасск — откатил.
опорную точку — откатил.
Проверка на телефоне встроенным движком (Stage 3 планирование, Stage 4 гэпы, докачка сегментов) —
когда тело подключат: `./build.sh install -d`.

View file

@ -4473,7 +4473,7 @@ let MAP_REGIONS = [
{ name: "Москва", bounds: [[55.55, 37.35], [55.95, 37.85]] },
{ name: "Санкт-Петербург", bounds: [[59.80, 30.05], [60.10, 30.55]] },
{ name: "Ростов-на-Дону", bounds: [[47.15, 39.55], [47.35, 39.85]] },
{ name: "Новочеркасск + окрестности", bounds: [[47.35, 39.95], [47.52, 40.22]] },
{ name: "Мой регион", bounds: [[47.35, 39.95], [47.52, 40.22]] },
{ name: "Краснодар", bounds: [[44.95, 38.85], [45.15, 39.15]] },
{ name: "Казань", bounds: [[55.70, 49.00], [55.90, 49.30]] },
{ name: "Новосибирск", bounds: [[54.90, 82.75], [55.10, 83.15]] },
@ -5906,7 +5906,7 @@ let TILES_BASE_URL = "http://192.168.1.1:88/baldav/tiles/";
// Regions the packing tools know about. Shown even when the router has no .db for
// them yet (greyed out) so nothing silently disappears from the list.
let KNOWN_REGIONS = {
novocherkassk: "Новочеркасск",
novocherkassk: "Мой регион",
rostov_city: "Ростов-на-Дону",
rostov_oblast: "Ростовская обл.",
moscow_city: "Москва",
@ -7397,8 +7397,8 @@ function formatDate(iso) {
// from the mirror (LAN -> WAN -> brouter.de) by BRouter.downloadTileAndWait.
async function showBrouterSegments() {
let region = [
{ name: "E35_N45", label: "E35_N45 — Ростов, запад (35–40°E, 45–50°N)" },
{ name: "E40_N45", label: "E40_N45 — Новочеркасск, восток (40–45°E)" },
{ name: "E35_N45", label: "E35_N45 — запад (35–40°E, 45–50°N)" },
{ name: "E40_N45", label: "E40_N45 — восток (40–45°E)" },
];
let haveSet = new Set();
try { (await BRouter.listTiles()).forEach(t => haveSet.add(t.name)); } catch (e) {}

View file

@ -9,7 +9,7 @@
// embedded engine isn't available (desktop) or errors — lets planning work on the desktop
// too. http_request is Rust-native (ureq), so it's not subject to the WebView CSP.
window.BRouter = {
_servers: ["http://192.168.1.18:17777", "http://77.66.192.45:17777"],
_servers: ["http://192.168.1.18:17777", "http://mirror.invalid:17777"],
async _routeServer(pts, profile) {
let lonlats = pts.map(p => p[0] + "," + p[1]).join("|");

2
src-tauri/Cargo.lock generated
View file

@ -139,7 +139,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "baldav"
version = "0.1.448"
version = "0.1.449"
dependencies = [
"base64 0.22.1",
"btleplug",

View file

@ -1,6 +1,6 @@
[package]
name = "baldav"
version = "0.1.448"
version = "0.1.449"
edition = "2021"
[lib]

View file

@ -219,7 +219,7 @@ class MainActivity : TauriActivity() {
// fine on the fast LAN.
val bases = listOf(
"http://192.168.1.1:88/brouter/segments4", // LAN mirror
"http://77.66.192.45:8088/brouter/segments4", // WAN mirror (router port-forward)
"http://mirror.invalid:8088/brouter/segments4", // WAN mirror (router port-forward; set real host locally)
"https://brouter.de/brouter/segments4" // upstream fallback
)
var total = -1L

View file

@ -6,7 +6,7 @@
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">192.168.1.1</domain>
<domain includeSubdomains="false">192.168.1.18</domain>
<domain includeSubdomains="false">77.66.192.45</domain>
<domain includeSubdomains="false">mirror.invalid</domain>
</domain-config>
<debug-overrides cleartextTrafficPermitted="true" />
</network-security-config>

View file

@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/nicegui/tauri-v2/refs/heads/main/tooling/cli/schema.json",
"productName": "BalDav",
"version": "0.1.448",
"version": "0.1.449",
"identifier": "dev.baldav.app",
"build": {
"frontendDist": "../frontend"

View file

@ -4,8 +4,8 @@
`/opt/share/www/brouter-web/`. Смотрит на свой RouteServer (ПК, systemd brouter.service, :17777).
## Файлы кастомизации (кладутся рядом с распакованным brouter-web)
- `config.js` — host RouteServer (LAN 192.168.1.18:17777 / WAN 77.66.192.45:17777), профили с зеркала,
базовые слои (ÖPNV дефолт, CARTO Dark «needs key» и т.д.), центр Новочеркасск, профили trekking/shortest.
- `config.js` — host RouteServer (LAN 192.168.1.18:17777 / WAN mirror.invalid:17777), профили с зеркала,
базовые слои (ÖPNV дефолт, CARTO Dark «needs key» и т.д.), центр — опорная точка, профили trekking/shortest.
- `baldav-custom.css` + `baldav-custom.js` — подключены в index.html после `dist/brouter-web.js`:
тумблер день/ночь (☾/☀) в тулбаре → тёмная карта (invert только слой тайлов) + тёмная правая панель;
авто-простановка нужных POI-оверлеев один раз (флаг localStorage `baldav_bw_overlays_v1`).