# Settlements UI

Демка · 8 сентября 2026

Frontend GIS для работы с границами населённых пунктов и территорий. MapLibre показывает реальную картографическую подложку, React и MobX связывают список, карточку, редактор геометрии и импорт. Публичный адаптер воспроизводит контракт backend в памяти вкладки: данные синтетические, оба режима импорта и проверка геометрии работают полностью.

Версия исходников: 5c5466fa43e675d53dd4d347cc711b2ba7e8e305

История репозитория: с февраля 2025. Публичное демо: 7 сентября 2026.

## Задача

Нужно было сохранить исходный рабочий интерфейс картографического приложения, убрать корпоративные адреса и данные и сделать безопасный публичный сценарий. В демо доступны просмотр, редактирование, экспорт, массовый импорт и импорт непосредственно в карточку выбранной территории.

Редактирование границ городов, районов и других территорий.

## Выполнено

- Карточки объектов, слои карты и редактирование геометрии.
- Связка React/MobX с MapLibre и Mapbox Draw. Импорт и экспорт геометрии.
- Для демки сделал адаптер вместо корпоративного API. Убрал рабочие данные и закрытые registry-зависимости.
- Деплой, правки вёрстки, проверка массового импорта и набор границ из OpenStreetMap.

## Что отличает реализацию

Массовый режим читает ОКТМО каждой feature с fallback на текущий контекст. Контекстный режим принудительно назначает ОКТМО открытой карточки и хранит собственный список файлов. Polygon и MultiPolygon проходят проверку CRS, размера, числа вершин и топологии; ошибку можно исправить или пропустить, а повторное применение файла не создаёт дублей.

## Состав прикладного кода

- React / TypeScript: 46.3%
- GeoJSON import / TypeScript: 31.7%
- MapLibre / TypeScript: 17.5%
- MobX / data adapters: 4.5%

Доли по размеру прикладных файлов Git в закреплённой версии. Каждый файл отнесён к одной подсистеме по правилам scripts/generate-stack-share.mjs; тесты, ассеты, зависимости и generated-файлы исключены. Округление — до 0,1%. Это состав кода, а не уровень владения технологиями.

## Стек

| Технология | Роль |
| --- | --- |
| React / MobX | Исходные экраны, состояние и импорт |
| MapLibre / Mapbox Draw | Подложка карты, слои и редактирование геометрии |
| GeoJSON / WKB | Валидация, загрузка и экспорт |

## Архитектура

- Исходная тема sppi-dark: тёмный фон, жёлтые элементы управления и исходные цвета статусов.
- MapLibre получает обычные тайлы OpenStreetMap. Указана атрибуция; массовая загрузка карт и офлайн-кеширование не реализованы.
- Штатные экраны FilesScreen → PolygonsScreen → PolygonEditScreen работают через локальный адаптер контрактов OData и geo-import.
- Импорт сначала проходит проверку и просмотр. Применение переносит только валидные полигоны в основной редактор; повторное применение не создаёт новых копий с тем же importId.
- Standalone-стили собраны в единую область .sppi-dark: исходные цвета, единые SVG-иконки, размер действий 40 px, адаптивные карточки файлов. ResizeObserver синхронизирует MapLibre с изменениями панелей и экрана.
- Контуры OpenStreetMap упрощены с сохранением топологии: 54 территории, 21 421 вершина, 7 MultiPolygon. Области, районы и города имеют разную прозрачность; исходные цвета управления и статусов сохранены.

### Компоненты и границы интеграций

![Компоненты и границы интеграций](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/components.svg)

[Mermaid](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/components.mmd)

### Массовый импорт: полный сценарий

![Массовый импорт: полный сценарий](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/import-flow.svg)

[Mermaid](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/import-flow.mmd)

### Развёртывание

![Развёртывание](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/deployment.svg)

[Mermaid](https://komaroff-dev.ru/evidence/geo/visual-f8cf0051/diagrams/deployment.mmd)

## Code-map

| Функция | Экран / API | Модуль | Данные / проверка |
| --- | --- | --- | --- |
| Карта и тема | Главный редактор | src_front/src/demo/config.ts; styles/themes/sppi-dark.scss | MapLibre, OSM, 54 территории и прозрачность вложенных границ |
| Массовая загрузка | Импорт файлов → /geo-import/import | modules/import/screens/FilesScreen.tsx; demo/import-api.ts | GeoJSON, CRS, WKB; файл из 54 реальных контуров |
| Проверка и применение | Список полигонов → /geo-import/files/:id/apply | PolygonsScreen.tsx; ImportRepository; import-api.ts | статусы, пропуск, валидация; 53 применённых, 107 в экспорте |
| Изменение | Изменение полигона → /polygons/:id/save | PolygonEditScreen.tsx; MapImportMapHelper | Mapbox Draw; original/default WKB |
| Изоляция и экспорт | Основной экран | demo/data.ts; demo/Controls.tsx | EventTarget, память вкладки; GeoJSON export и reset |
| Визуальная система | Все экраны и состояния | demo/foundation.css; demo/original-theme.css | SVG masks, поля, кнопки, таблицы; визуальный QA |
| Размер карты | Главный редактор и импорт | utils/observe-map-resize.ts; MapEditor.tsx; MapImport.tsx | ResizeObserver, requestAnimationFrame, cleanup; размеры canvas |
| Слои импорта | Меню подложки | MapImportLayersSwitcher.tsx; map-import.controller.ts | исходная видимость синхронизируется со store; штатный checkbox |
| Отступы редактора импорта | Изменение полигона | PolygonEditScreen.tsx; demo/original-theme.css | import-polygon-editor: общий padding 12 px; координаты полей и кнопок, сохранение |
| Набор и происхождение | Карта / скачать набор | demo/russia-boundaries.json; demo/data.ts | OSM relations, ODbL, учебные ОКТМО, Polygon/MultiPolygon |
| Общая граница | Карточка и карта импорта | geometryBbox; getUnitedPolygons; validatePolygons; MapImport.tsx | bbox всех частей; фокус выбранной территории после загрузки |

Перед изменением пройти связи экран → API → состояние → хранилище. Проверить входы, ошибки и повторные действия. Обновить карту и схемы вместе с кодом, выполнить связанные проверки и привязать материалы к версии.

## Сценарии

- Найти Владимир, Суздаль, Нижний Новгород, Суздальский район или Владимирскую область; открыть карточку и приблизить контур.
- Скачать набор из 54 территорий, загрузить его через массовый импорт и проверить все геометрии.
- Открыть файл: список полигонов, статусы, фильтры и карта с общими границами.
- Открыть полигон: редактирование, сброс исходной геометрии, явный пропуск и сохранение.
- Исправить или пропустить ошибки, нажать «Применить импорт», закрыть модуль и экспортировать результат из основного редактора.
- Сбросить пример; проверить, что другая вкладка не получила импортированные файлы.

## Проверки

- Опубликованная версия 4108a165: 54 именованные территории, пять карточек городов/области/района, поиск и пустой результат; экспорт исходных координат совпадает с набором.
- Массовый импорт 54 территорий: все геометрии валидны. Сохранение составного полигона, пропуск одной территории, применение 53; повторное применение не создаёт дубликатов; экспорт содержит 107 объектов и ссылки на источник.
- Самопересечение блокирует применение. Другая сессия не получает файлы; сброс восстанавливает 54 исходных объекта.
- На контуре Суздаля указателем перемещена вершина. После сохранения изменённая геометрия подтверждена экспортом; остальные 53 объекта не изменились.
- Desktop 1440×1000, мобильные 390×844 и 360×844: нет горизонтального переполнения или ошибок JavaScript. В мобильном редакторе ОКТМО выбранной территории и общие левый/правый края с расхождением не более 1 px.
- Сборка Vite и GitHub CI успешны. Полный legacy TypeScript/lint набор в эту проверку не входит.

[Сборка GIS с 54 территориями и фокусом редактора](https://github.com/komaroffsergei/settlements-ui/actions/runs/34117329997) — 4108a165419e815bc0d050dd281962115590fce9, 2026-09-07; версия демо.

[Сборка после выравнивания редактора импорта](https://github.com/komaroffsergei/settlements-ui/actions/runs/34092393232) — 9d0df859422ca2969a2d16f8c56aedf16895a9a2, 2026-09-07; исторический запуск.

[Production-сборка GIS после доработки интерфейса](https://github.com/komaroffsergei/settlements-ui/actions/runs/34090225900) — f8cf00517230292f86241813af71f2e23637bb81, 2026-09-07; исторический запуск.

## Результаты

- Статическая сборка обслуживается общим Nginx; отдельного backend-процесса GIS на VPS нет.
- 54 объекта; 21 421 вершина после упрощения, 76–926 на объект, до 480 на кольцо. Это характеристика набора, а не оценка точности границ.
- В одном контрольном запуске Chromium на опубликованной версии: первый список 1.80 с, импорт 54 территорий 0.66 с. Измерен интерфейс на тестовой машине; это не нагрузочный тест или SLA.

![Состав и сложность географического набора](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/metrics/dataset.svg)

[Данные](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/metrics/dataset-report.json)

## Ограничения

- Только backend заменён памятью вкладки. Серверный OData/PostGIS-сервис не подключён; закрытый исходный репозиторий остаётся приватным.
- Пределы адаптера: 20 файлов, 500 полигонов в файле, 1000 в сессии; текст файла до 5 MB. Поддержаны Polygon/MultiPolygon и EPSG:4326/3857.
- После перезагрузки импортированные данные исчезают. Экспорт GeoJSON позволяет сохранить результат у себя.
- Тайлы OpenStreetMap загружаются с внешнего сервера. Без него подложка не загрузится.
- Границы OSM упрощены для редактора, не являются кадастровыми данными. Города — контуры place=city/town, районы и области — boundary=administrative; коды ОКТМО учебные. Источник и ODbL указаны в демо и скачиваемом GeoJSON.

## Запуск и эксплуатация

Сборка: src_front, npm ci --ignore-scripts; npm run build:demo. Артефакт собирается в GitHub CI и публикуется как статический релиз Nginx.

Backend adapter расположен в demo/api.ts и demo/import-api.ts. При подключении настоящего API нужно реализовать контракт применения импорта и заменить локальный транспорт.

Проверка геометрии использует существующий validation.util.ts; WKB-конвертация — wkx. Пользовательские GeoJSON не отправляются на VPS.

Перед изменением проследить функцию через экран, ImportRepository, адаптер, геометрию и состояние. После изменения обновить code-map и пройти загрузку, ошибки, пропуск, применение, экспорт и сброс.

Доработка визуала 7 сентября 2026: CSS подключается после исходных компонентов; специфичность ограничена темой. Текстовые кнопки не получают размер icon-only. На узком экране импорт расположен вертикально; фон страницы блокируется при открытии модального окна. Тайлы OpenStreetMap остаются внешними и могут догружаться при масштабировании.

Выравнивание 7 сентября 2026: у редактора импорта один контейнер отступов 12 px. Удалены конфликтующие utility-классы и резерв скроллбара у одиночной карточки; footer и ОКТМО используют ту же ширину. Правка ограничена экраном PolygonEditScreen.

Географический набор: demo/russia-boundaries.json, срез OpenStreetMap от 7 сентября 2026, 11:09:36 UTC. У каждой территории сохранены relation URL, тип, дата и величина упрощения. Производный набор доступен по ODbL через скачивание и экспорт.

Вычисление bbox учитывает все кольца и части MultiPolygon. При сохранении и импорте bbox пересчитывается. После загрузки карты выбранная территория сохраняет фокус; в редакторе импорта показан её собственный ОКТМО.

## Скриншоты

[Владимир: подробный городской контур и карточка](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/04-vladimir-city.png) — 2026-09-07T11:36:12.348828+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[54 территории России: города, районы, округа и области](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/01-regions-overview.png) — 2026-09-07T11:35:50.610243+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Владимирская область и вложенные районы](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/02-vladimir-region.png) — 2026-09-07T11:35:54.200644+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Суздальский район на карте OpenStreetMap](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/03-suzdal-district.png) — 2026-09-07T11:35:58.091786+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Суздаль: городской контур вдоль реки и кварталов](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/05-suzdal-city.png) — 2026-09-07T11:36:16.846903+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Нижний Новгород: составная геометрия MultiPolygon](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/06-nizhny-multipart.png) — 2026-09-07T11:36:20.616868+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Поиск территории: пустое состояние](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/07-search-empty.png) — 2026-09-07T11:36:22.392052+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Массовый импорт: файл с 54 территориями](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/08-batch-file.png) — 2026-09-07T11:36:23.974932+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Все 54 геометрии прошли исходную проверку](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/09-batch-54-valid.png) — 2026-09-07T11:36:26.018698+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Редактор импорта: геометрия и ОКТМО выбранного города](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/10-multipart-import-editor.png) — 2026-09-07T11:36:29.106356+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Применение файла после пропуска одной территории](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/12-batch-applied.png) — 2026-09-07T11:36:33.705463+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Самопересечение блокирует применение импорта](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/13-invalid-blocked.png) — 2026-09-07T11:36:40.431532+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Мобильный вид карты и списка территорий](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/14-mobile-390.png) — 2026-09-07T11:36:44.101842+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Мобильный редактор: единые края полей и карта города](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/15-mobile-import-editor.png) — 2026-09-07T11:36:45.919672+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Суздаль в Mapbox Draw: редактируемые вершины](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/16-suzdal-vertices.png) — 2026-09-07T11:35:52.169980+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Контур после перемещения вершины и сохранения](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/full/18-vertex-saved.png) — 2026-09-07T11:35:56.279217+00:00, 4108a165419e815bc0d050dd281962115590fce9.

[Материалы](https://komaroff-dev.ru/evidence/geo/boundaries-4108a165/materials.zip)
