diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md index e4b2fce..07f2079 100644 --- a/IMPLEMENTATION_PLAN.md +++ b/IMPLEMENTATION_PLAN.md @@ -76,13 +76,16 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л - Фолбэк на прямую линию, если движок/сегмент недоступны. **Success:** тап точек даёт маршрут по дорогам с профилем высоты; сохраняется как маршрут. **Tests:** спланировать знакомый маршрут офлайн, сверить с онлайн-BRouter (та же геометрия). -**Status:** СДЕЛАНО (компилируется; backend роутинга проверен). В `showRouteEditor` кнопка-тумблер +**Status:** СДЕЛАНО и ПРОВЕРЕНО В UI (десктоп, Xvfb, 2026-09-09). В `showRouteEditor` кнопка-тумблер «🚲 По дорогам»: 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). Проверено: - RouteServer на 3 точки trekking отдал 83 trkpt (3594м); `parseRouteGPX` читает trkpt. ОСТАЛОСЬ: - сквозной прогон в UI (тап точек → дорожная линия) — на десктопе/телефоне. + HTTP (`http_request`, мимо CSP; хосты LAN 192.168.1.18:17777 / WAN 77.66.192.45:17777). + Сквозной прогон в редакторе: 3 точки по Новочеркасску → прямая 1.69км сменилась дорожной линией + 2.25км по улицам (Буденновская → кольцо → Ермака). БАГ, найденный и исправленный при этой проверке: + `doBrouter` в конце звал `changed()`, который обнулял `routeGeom` и заново ставил `doBrouter` в + очередь — бесконечный цикл, дорожная линия никогда не рисовалась. Заменено на прямой + `updatePolyline(); updateStats();`. ОСТАЛОСЬ: прогон на телефоне (встроенный движок). ## Stage 4 — Идея 1: заполнение GPS-пропусков по дороге **Goal:** GPS-разрыв заполняется дорожным путём (shortest), с фолбэком на хорду. @@ -94,11 +97,14 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л **Success:** записанный трек с дропаутом GPS показывает дорожный фолловинг вместо хорды, кроме случая «дорога в разы длиннее». **Tests:** трек с известным дропаутом на дороге → заполнение по дороге; дропаут на поле → хорда. -**Status:** СДЕЛАНО (v1, non-destructive). `GPS.fillGaps(points)` (gps.js): для каждого гэпа - (dt>gapThreshold и хорда>60м) прокладывает `shortest`, вставляет дорожные точки (флаг `estimated`); - фолбэк на хорду если дорога > 3× хорды. Кнопка «🧩» на детали трека — рисует дорожно-заполненный - путь голубым пунктиром оверлеем (данные не меняет). Пересчёт статистики по заполненным точкам — - следующий шаг (сейчас только визуализация). +**Status:** СДЕЛАНО (v1, non-destructive) и ПРОВЕРЕНО В UI (десктоп, Xvfb, 2026-09-09). + `GPS.fillGaps(points)` (gps.js): для каждого гэпа (dt>gapThreshold и хорда>60м) прокладывает + `shortest`, вставляет дорожные точки (флаг `estimated`); фолбэк на хорду если дорога > 3× хорды. + Кнопка «🧩» на детали трека — рисует дорожно-заполненный путь голубым пунктиром оверлеем (данные + не меняет). Прогон: трек 2026-06-22 (session 1782115144011, 16.24км) с двумя дропаутами GPS + (~2км хорды) — «🧩» проложил голубой пунктир по улицам между красно-зелёными концами гэпов. + Трек без гэпов (21.81км 2026-07-07) — тумблер включается, оверлей пуст (нечего заполнять), без + ошибок. Пересчёт статистики по заполненным точкам — следующий шаг (сейчас только визуализация). ## Release-хардненинг (item 4, сделано) - `network_security_config.xml` (res/xml): cleartext OFF глобально в release, разрешён только хостам diff --git a/PROJECT_MAP.md b/PROJECT_MAP.md index 44fa5a4..a9ae239 100644 --- a/PROJECT_MAP.md +++ b/PROJECT_MAP.md @@ -27,6 +27,8 @@ | `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/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 зоны, ландшафтный режим | | `index.html` | 28 | Точка входа, порядок загрузки скриптов | diff --git a/README.md b/README.md index d270ac0..469fd23 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ - **Ручные маршруты** — рисование трека на карте тапами, elevation из прошлых треков или open-meteo API. - **Сохранённые маршруты** — импорт GPX-маршрутов, нарисованных заранее (например, в конструкторе Яндекс.Карт: формат ``), выбор активного, наложение цветной линией поверх карты во время записи И на экране просмотра прошлого трека — для визуальной навигации. Управление на отдельной странице (импорт/переименование/цвет/удаление, один активный за раз), переключение линии кнопкой-тогглом. Цвета «проезженных дорог» настраиваются раздельно для вело/пеших/бега. - **Высоты маршрута (рельеф до поездки)** — экран предпросмотра маршрута (тап по названию): профиль высоты (крутые участки красным), длина/набор/сброс/макс. уклон, тап по точке — высота и уклон, разворот карты на весь экран. Топографическая обводка линии с тогглом **уклон/высота**: уклон дискретными полосами по 1% (зелёный→жёлтый→красный→коричневый→чёрный, ≥10% чёрный) или гипсометрия по высоте (зелёный→жёлтый→коричневый). Обводка и тоггл доступны и в оверлее на карте записанного трека и во время записи; маршрут рисуется ПОД треком. Высоты (`` из GPX или онлайн) тянутся по цепочке: **GPXZ** (Copernicus GLO-30, точнее для уклонов; ключ в GPS-настройках, хранится только на устройстве) → open-elevation → open-meteo → OpenTopoData (без ключа). Калории пересчитываются по сглаженным точкам; для уклона высоты сглаживаются (гасит вертикальный шум SRTM). -- **Редактор маршрутов** — создание («Новый маршрут») и правка сохранённого маршрута прямо в приложении: тап по карте — добавить точку в конец, тап по линии — вставить точку в это место, тащить точку — двигать, тап по точке — удалить. Высоты подтягиваются онлайн после отрисовки (из предпросмотра). +- **Редактор маршрутов** — создание («Новый маршрут») и правка сохранённого маршрута прямо в приложении: тап по карте — добавить точку в конец, тап по линии — вставить точку в это место, тащить точку — двигать, тап по точке — удалить. Высоты подтягиваются онлайн после отрисовки (из предпросмотра). Тумблер **«🚲 По дорогам»** прокладывает линию между опорными точками по реальным дорогам (BRouter, профиль trekking) и сохраняет дорожную геометрию; выключен — прямые отрезки. +- **Оффлайн-роутинг (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). На картах записи, деталей трека и дорог. - **Тёмная карта** — тумблер `☾` на экранах записи, деталей трека и дорог: инвертирует ТОЛЬКО слой тайлов (`invert + hue-rotate 180° + brightness`, трек/маршрут/дороги остаются цветными), выбор запоминается. Работает одинаково на офлайн-тайлах и онлайне, без ключей/квот/перепаковки. diff --git a/docs/session-logs/2026-09-07_brouter-findings.md b/docs/session-logs/2026-09-07_brouter-findings.md index 49b37fe..722a89c 100644 --- a/docs/session-logs/2026-09-07_brouter-findings.md +++ b/docs/session-logs/2026-09-07_brouter-findings.md @@ -87,3 +87,25 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4 нет OpenTopo/Yandex-vec). Транспортную инвертом — плохо в городе. Решение: OSM-стандарт + CSS `invert(1) hue-rotate(180deg) brightness(1.5)` на .leaflet-tile-pane, тумблер ☾ (запись/детали/дороги), без перепаковки. Проверено на устройстве. + +## Дополнение 2026-09-09 (5): визуальная проверка Stage 3/4 на десктопе (Xvfb) + фикс бага +Задача: доделать Stage 3 (планирование по точкам) и Stage 4 (заполнение гэпов), прогнать тесты +на десктопе со скринами на виртуальном экране (Xvfb :99), не только по API. + +Прогон в реальном UI (`npx tauri dev` на Xvfb :99, драйв через xdotool, скрины `import`): +- Stage 3 «🚲 По дорогам»: 3 опорные точки по Новочеркасску. Прямая линия 1.69км сменилась + дорожной 2.25км по улицам (Буденновская → кольцо → просп. Ермака). Роутинг — PC-RouteServer + (десктоп без встроенного движка), профиль trekking. +- Stage 4 «🧩»: трек 2026-06-22 (session 1782115144011, 16.24км, два дропаута ~2км хорды) — + оверлей проложил голубой пунктир по улицам между концами гэпов (профиль shortest). Трек без + фолловинг-гэпов (21.81км 2026-07-07) — тумблер включается, оверлей пуст, без ошибок. + +Баг, найденный этой проверкой (был в 8e3c9fd): `doBrouter` в конце звал `changed()`, а тот +обнулял `routeGeom` и снова ставил `doBrouter` в очередь — бесконечный ре-роутинг, дорожная +линия НИКОГДА не рисовалась (в UI линия оставалась прямой, дистанция не менялась). Исправлено: +`doBrouter` рисует напрямую (`updatePolyline(); updateStats();`), `changed()` не трогает. Отдельным +коммитом (цикл виден в истории). Для проверки временно уводил дефолт-центр редактора на +Новочеркасск — откатил. + +Проверка на телефоне встроенным движком (Stage 3 планирование, Stage 4 гэпы, докачка сегментов) — +когда тело подключат: `./build.sh install -d`. diff --git a/frontend/js/main.js b/frontend/js/main.js index 2e7788f..e7fbfec 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -3550,7 +3550,9 @@ async function showRouteEditor(container, routeId) { let rp = (GPS.parseRouteGPX(gpx) || {}).points || []; routeGeom = (rp.length >= 2) ? rp.map(p => ({ lat: p.lat, lon: p.lon })) : null; } catch (e) { routeGeom = null; showToast("BRouter: " + (e.message || e)); } - changed(); + // Draw the routed geometry directly. NOT changed() — that nulls routeGeom and + // re-schedules doBrouter, looping forever and never showing the road line. + updatePolyline(); updateStats(); } function scheduleRoute() { clearTimeout(routeTimer); routeTimer = setTimeout(doBrouter, 450); } // A waypoint changed: straight placeholder now, then re-route (if on) shortly after.