11 KiB
type, work_item_id, title, version, status, created_at, updated_at, authors, depends_on
| type | work_item_id | title | version | status | created_at | updated_at | authors | depends_on | ||
|---|---|---|---|---|---|---|---|---|---|---|
| trz | ET-010 | ТЗ: Координаты центра карты в scale-zoom-bar | 1 | draft | 2026-05-31 | 2026-05-31 |
|
|
Техническое задание — ET-010: Координаты центра карты
1. Общая архитектура изменений
Изменение чисто фронтовое. Никакого нового кода в src/api/,
миграций или Docker-настроек не вносится. Затрагиваются ровно два
файла:
src/web/app.js— расширение существующей функцииupdateScaleZoom()(около строки 1411) и инициализация суб-элемента.szb-coordsрядом с.szb-scale/.szb-zoom.src/web/app.css— стили.szb-coords(по образцу.szb-zoom).
src/web/index.html не трогаем: #scale-zoom-bar создаётся
динамически в initMap(), в HTML его нет.
2. Функциональные требования
REQ-F-01. Суб-элемент .szb-coords
В initMap() (src/web/app.js, рядом со строкой 1408), при сборке
#scale-zoom-bar, добавить третий блок:
<div class="szb-scale">…</div>
<div class="szb-zoom">z7</div>
<div class="szb-coords" aria-hidden="true">55.500, 40.500</div>
Порядок строго scale → zoom → coords (слева направо в flex-строке).
Стартовое значение — текущий центр карты после new maplibregl.Map(...).
REQ-F-02. Формат координат
"<lat>, <lon>"
Где:
lat=map.getCenter().lat.toFixed(3)(три знака после точки)lon=map.getCenter().lng.toFixed(3)(три знака после точки)- Разделитель — запятая + один пробел (
", ") - Знак минус сохраняется (
"-0.100") - Без подписей N/E/S/W
Никаких других форматов не поддерживается.
REQ-F-03. Обновление в updateScaleZoom()
Внутри существующей updateScaleZoom() после блока обновления
zoomEl (строка 1457) добавить:
const coordsEl = scaleZoomBar.querySelector('.szb-coords');
if (coordsEl) {
const c = map.getCenter();
const next = `${c.lat.toFixed(3)}, ${c.lng.toFixed(3)}`;
if (coordsEl.textContent !== next) {
coordsEl.textContent = next;
}
}
Условие if (coordsEl.textContent !== next) обязательно — оно
исключает лишнюю работу с DOM при move-событиях, которые не сдвинули
центр карты заметно (например, конец инерции).
REQ-F-04. Подключение к событиям
Никаких новых обработчиков навешивать не нужно. Существующие
map.on('zoom', updateScaleZoom) и map.on('move', updateScaleZoom)
(строки 1463-1464) автоматически вызывают updateScaleZoom() при
любом изменении вьюпорта, включая panTo, flyTo, easeTo, zoomIn,
zoomOut, перетаскивание мышью, pinch-zoom на тач-устройстве и
программный map.jumpTo(...).
REQ-F-05. Поведение при setStyle() (тема)
#scale-zoom-bar создаётся через document.getElementById('map').appendChild(...)
и не принадлежит DOM MapLibre — setStyle() его не затрагивает. После
переключения темы updateScaleZoom() вызывается следующим же
map.on('move') или map.on('zoom') — и текст координат корректно
обновляется. Дополнительных вызовов в rebuildMapOverlays() не нужно.
REQ-F-06. Стили .szb-coords
В src/web/app.css после блока .szb-zoom { … } (после строки 926)
добавить:
.szb-coords {
font-size: 10px;
font-weight: 500;
color: rgba(255,255,255,0.85);
text-shadow: 0 0 3px rgba(0,0,0,0.8), 0 1px 2px rgba(0,0,0,0.6);
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
Семейство шрифта наследуется от body. tabular-nums гарантирует,
что строка не «прыгает» по ширине при изменении цифр.
REQ-F-07. Доступность
.szb-coordsполучает атрибутaria-hidden="true"сразу при создании (см. REQ-F-01).- Никакого
tabindexэлемент не получает. - Не вмешивается в фокусную последовательность и не озвучивается скринридером.
REQ-F-08. Мобильный viewport
В существующем #scale-zoom-bar { display: flex; gap: 6px; } нет
переноса строк — все три суб-элемента остаются в одну линию. Если на
очень узких экранах (< 360 px) общая ширина превысит свободное место,
браузер обрежет правый край .szb-coords (с nowrap) — это
ожидаемое поведение dry-run, не требующее отдельной обработки.
В рамках ET-010 не вводим медиа-правил для скрытия .szb-coords
на мобильном. Если регрессия по верстке всё же обнаружится — fix
поедет отдельной задачей.
3. Нефункциональные требования
REQ-NF-01. Производительность
updateScaleZoom()вызывается синхронно изmap.on('move')— это уже факт текущего кода. Добавляемая работа: одинgetCenter(), дваtoFixed(3), одна конкатенация, одно сравнение, опционально одно присваиваниеtextContent. Все операции — O(1).- Никаких новых
requestAnimationFrame/setTimeout/debounceне вводим. - Бюджет: суммарная работа на
moveне должна расти больше чем на +0.5 ms в среднем кадре (ориентир —performance.now()в DevTools).
REQ-NF-02. Совместимость браузеров
- Поддержка: Chrome ≥ 110, Firefox ≥ 110, Safari ≥ 16, мобильные Chrome/Safari iOS ≥ 16 (как у текущего приложения).
Number.prototype.toFixed,Array.prototype.querySelector, template-strings — поддержаны во всех целевых браузерах с 2017 года.
REQ-NF-03. Логирование
- В нормальной работе никакого вывода в
console.*. - Никаких новых уровней логирования не вводим.
REQ-NF-04. Совместимость с другими work item
- Не пересекается с ET-007 (satellite map): тот трогает
style*.json,index.html,app.css, добавляет источникsat-raster. ET-010 не трогает ни источники, ни слои MapLibre. - Не пересекается с ET-009 (ScaleControl bottom-left): тот добавляет
нативный MapLibre control в
bottom-left. ET-010 расширяет custom-overlay#scale-zoom-barвtop-right. Разные DOM-узлы.
4. Структура данных
Никаких новых ключей localStorage. Никаких новых полей в
layerState. Состояние существует только в DOM (textContent
элемента .szb-coords) и пересчитывается из map.getCenter() при
каждом вызове updateScaleZoom().
5. UI / UX контракт
| Элемент | Местоположение | Поведение |
|---|---|---|
#scale-zoom-bar |
top-right карты (absolute, top ≈ 8 px, right 12 px) |
Контейнер — не меняется визуально |
.szb-scale |
левый элемент flex-строки | Шкала «30 km» — не меняется |
.szb-zoom |
средний элемент flex-строки | «z7» — не меняется |
.szb-coords |
правый элемент flex-строки | «55.500, 40.500» — обновляется на move/zoom |
Шрифт .szb-coords |
10px / 500 / белый с тенью |
Идентичен .szb-label, чуть мельче .szb-zoom |
aria-hidden |
"true" на .szb-coords |
Скринридер игнорирует элемент |
6. Тестируемость
- В
tests/web/: unit-тест функции форматирования (extractformatCenterCoords(lat, lng)если оркестратор хочет — допустимо и инлайн-форматирование без extract). - Integration-тест (jsdom): проверка, что после имитации
updateScaleZoom().szb-coords.textContentсоответствует ожидаемой строке. - E2E (Playwright) — см.
04-test-plan.yamlи04b-ui-test-cases.md.
7. Совместимость и миграции
- БД: миграций нет.
- API: новых эндпоинтов нет.
- Конфиги: новых ENV не добавляем.
- Существующие work item: ET-005 (units), ET-006 (gpx), ET-007 (satellite), ET-009 (scale control) не затрагиваются.
- Бизнес-конфиги, скрипты сборки, CI — не меняются.
8. Откат
В случае проблем:
- В
src/web/app.jsудалить изinitMap()строку создания.szb-coords(частьscaleZoomBar.innerHTML = '<div…><div class="szb-coords"…></div>'). - Удалить из
updateScaleZoom()блок обновленияcoordsEl(5 строк). - В
src/web/app.cssудалить блок.szb-coords { … }.
Полный откат — один коммит, без побочных эффектов. Существующие шкала и зум-индикатор продолжат работать как раньше.