feat(sf): EKB geoportal WFS client (#1067 C7) #1083

Merged
bot-reviewer merged 1 commit from feat/ekb-geoportal-wfs-client into main 2026-06-06 18:37:10 +00:00
Owner

Что

Новый backend/app/services/scrapers/ekb_geoportal_client.py — клиент муниципального геопортала Екатеринбурга (ИСОГД/Geometa GeoServer, ~1149 слоёв, public, без auth, нормальный TLS).

Зачем / дизайн

По разведке 2026-06-06 (vault research/EKB_Geoportal_WFS_Construction_Intel_Jun06) геопортал — единый структурный источник «землепользование + что построят» для ЕКБ, которого нет в НСПД (только геометрия+reg-номер) и в городских документах по-отдельности.

  • Primary = OGC WFS GetFeature /api/gis/ows/3/portal/wfs — стандарт стабильнее reverse-eng urbanCard, один клиент ко всем слоям: ПЗЗ терзоны/подзоны, функц.зоны генплана (#1058), КРТ (#1060), ППТ/ПМТ, инвест-зоны, ЗОУИТ.
  • features_at_point (CQL INTERSECTS(geoloc, POINT(lon lat))), features_in_bbox (CQL BBOX), zone_index_at (crosswalk координата→индекс ПЗЗ Ц-1 для резолва #1059).
  • EPSG:4326, X=lon Y=lat. ⚠️ НЕ WMS GetFeatureInfo (reproject-баг GeoServer «too close to a pole»).
  • httpx (как ekburg_permits.py), типизация (EKBFeature) как nspd_client.py. _http_get_json module-level → мокается в тестах.
  • WFS_LAYERS — читаемый ключ → typeName (подтверждены live GetFeature).

Тесты

7 тестов (CQL INTERSECTS/BBOX, typeName-резолв + raw passthrough, zone_index_at, from_raw, каталог). pytest tests/test_ekb_geoportal_client.py7 passed. ruff + ruff-format clean.

Scope

Это WFS-ядро. Рич ПЗЗ-карточка (urbanCard: 3 списка ВРИ + предельные параметры) — follow-up C8. parcel-резолв → analyze — D9.

Refs #1067 (C7). Связано: #1058 функц.зоны, #1060 КРТ, #1059 ПЗЗ (теперь оба через этот клиент).

## Что Новый `backend/app/services/scrapers/ekb_geoportal_client.py` — клиент муниципального геопортала Екатеринбурга (ИСОГД/Geometa GeoServer, ~1149 слоёв, public, без auth, нормальный TLS). ## Зачем / дизайн По разведке 2026-06-06 (vault `research/EKB_Geoportal_WFS_Construction_Intel_Jun06`) геопортал — единый структурный источник «землепользование + что построят» для ЕКБ, которого нет в НСПД (только геометрия+reg-номер) и в городских документах по-отдельности. - **Primary = OGC WFS GetFeature** `/api/gis/ows/3/portal/wfs` — стандарт стабильнее reverse-eng `urbanCard`, один клиент ко всем слоям: ПЗЗ терзоны/подзоны, функц.зоны генплана (#1058), КРТ (#1060), ППТ/ПМТ, инвест-зоны, ЗОУИТ. - `features_at_point` (CQL `INTERSECTS(geoloc, POINT(lon lat))`), `features_in_bbox` (CQL `BBOX`), `zone_index_at` (crosswalk координата→индекс ПЗЗ `Ц-1` для резолва #1059). - EPSG:4326, X=lon Y=lat. ⚠️ **НЕ** WMS GetFeatureInfo (reproject-баг GeoServer «too close to a pole»). - httpx (как `ekburg_permits.py`), типизация (`EKBFeature`) как `nspd_client.py`. `_http_get_json` module-level → мокается в тестах. - `WFS_LAYERS` — читаемый ключ → typeName (подтверждены live GetFeature). ## Тесты 7 тестов (CQL INTERSECTS/BBOX, typeName-резолв + raw passthrough, zone_index_at, from_raw, каталог). `pytest tests/test_ekb_geoportal_client.py` → **7 passed**. ruff + ruff-format clean. ## Scope Это WFS-ядро. Рич ПЗЗ-карточка (`urbanCard`: 3 списка ВРИ + предельные параметры) — follow-up C8. parcel-резолв → analyze — D9. Refs #1067 (C7). Связано: #1058 функц.зоны, #1060 КРТ, #1059 ПЗЗ (теперь оба через этот клиент).
lekss361 added 1 commit 2026-06-06 18:32:40 +00:00
feat(sf): EKB geoportal WFS client (#1067 C7)
All checks were successful
CI / changes (push) Successful in 6s
CI / changes (pull_request) Successful in 6s
CI / backend-tests (push) Successful in 5m50s
CI / backend-tests (pull_request) Successful in 5m46s
f6b3b77e11
Клиент муниципального геопортала ЕКБ (ИСОГД/Geometa GeoServer, 1149 слоёв, public,
без auth). Primary = OGC WFS GetFeature /api/gis/ows/3/portal/wfs (стабильнее
reverse-eng urbanCard, один клиент ко всем слоям): ПЗЗ терзоны/подзоны, функц.зоны
генплана (#1058), КРТ (#1060), ППТ/ПМТ, ЗОУИТ. features_at_point (CQL INTERSECTS),
features_in_bbox (CQL BBOX), zone_index_at (crosswalk координата→индекс ПЗЗ для #1059).
EPSG:4326 (X=lon Y=lat). НЕ GetFeatureInfo (reproject-баг GeoServer).
Источник: vault research/EKB_Geoportal_WFS_Construction_Intel_Jun06.

Refs #1067 (C7). Next: рич ПЗЗ-карточка urbanCard (C8), parcel-резолв (D9).
lekss361 added the
status/review
label 2026-06-06 18:32:58 +00:00
bot-reviewer reviewed 2026-06-06 18:36:59 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Чистый net-new WFS-клиент, нулевой regression risk (импортируется только тестами, прод-вызовов нет — wiring в C8/D9):

  • CQL injection surface чист: lon/lat/bbox типизированы float → f-string интерполяция не несёт CQL-синтаксиса; layer идёт в typeNames param (httpx urlencode), не в CQL.
  • _http_get_json module-level — корректный паттерн для monkeypatch; 7 тестов без сети.
  • EKBFeature frozen+slots; (data or {}).get("features") or [] устойчив к None/missing; zone_index_at short-circuit'ит на пустом списке.
  • WFS_LAYERS typeNames сверены с vault recon; WMS GetFeatureInfo сознательно исключён (reproject-баг, задокументирован). ruff/format clean.

Минор (advisory, → follow-up issue):

  1. CQL float-формат: на патологических координатах Python repr даст scientific notation (1e-05) → GeoServer CQL может отвергнуть. Для ЕКБ (~60/56) неактуально, но :.8f робастнее перед C8-wiring.
  2. Нет User-Agentekburg_permits.py шлёт GenDesign/1.0; консистентность для муниципальных источников.
  3. Bare httpx.get без httpx.Client pooling — под bbox-grid burst медленнее; не correctness.

Заведу один follow-up на эти три (применить при C8). Merge → deploy-verify.

<!-- gendesign-review-bot: sha=f6b3b77 verdict=approve --> ✅ **APPROVE** Чистый net-new WFS-клиент, нулевой regression risk (импортируется только тестами, прод-вызовов нет — wiring в C8/D9): - **CQL injection surface чист**: `lon`/`lat`/bbox типизированы `float` → f-string интерполяция не несёт CQL-синтаксиса; `layer` идёт в `typeNames` param (httpx urlencode), не в CQL. - `_http_get_json` module-level — корректный паттерн для monkeypatch; 7 тестов без сети. - `EKBFeature` frozen+slots; `(data or {}).get("features") or []` устойчив к None/missing; `zone_index_at` short-circuit'ит на пустом списке. - `WFS_LAYERS` typeNames сверены с vault recon; WMS GetFeatureInfo сознательно исключён (reproject-баг, задокументирован). ruff/format clean. **Минор (advisory, → follow-up issue):** 1. CQL float-формат: на патологических координатах Python repr даст scientific notation (`1e-05`) → GeoServer CQL может отвергнуть. Для ЕКБ (~60/56) неактуально, но `:.8f` робастнее перед C8-wiring. 2. Нет `User-Agent` — `ekburg_permits.py` шлёт `GenDesign/1.0`; консистентность для муниципальных источников. 3. Bare `httpx.get` без `httpx.Client` pooling — под bbox-grid burst медленнее; не correctness. Заведу один follow-up на эти три (применить при C8). Merge → deploy-verify.
bot-reviewer merged commit 4e41f9be73 into main 2026-06-06 18:37:10 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1083
No description provided.