genplan_zones_sync: ST_MakeValid для геометрий + backfill 881 битых полигонов (ekb_genplan_functional_zone) #1156

Closed
opened 2026-06-07 17:41:02 +00:00 by lekss361 · 0 comments
Owner

Проблема (найдено бэктестом ИРД-слоя 2026-06-07)

ekb_genplan_functional_zone: 881 из 6436 (13.7%) полигонов имеют ST_IsValid(geom)=false.

SELECT count(*) FILTER (WHERE NOT ST_IsValid(geom)) FROM ekb_genplan_functional_zone;  -- 881

Битые геометрии при ST_Intersects в parcel_functional_zones могут ронять резолв функц.зоны генплана (#1058) или молча его искажать — функц.зона участка в analyze (ird.functional_zones) может теряться/быть неверной для участков над битыми зонами.

Корень

genplan_zones_sync.py (_UPSERT_SQL, строка ~49) пишет geom без валидации:

ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326)

Исходный геопортальный GeoJSON содержит self-intersecting / ring-order дефекты → попадают в БД как есть.

Фикс

1. Harvest (предотвращение новых)

В genplan_zones_sync.py обернуть geom-выражение в ST_MakeValid (и в VALUES, и в ON CONFLICT ... DO UPDATE SET geom = EXCLUDED.geom — EXCLUDED уже возьмёт исправленное, если поправить VALUES):

ST_MakeValid(ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326))

2. Backfill существующих (одноразово)

SQL-миграция data/sql/NN_genplan_makevalid_backfill.sql (идемпотентна, авто-применяется через deploy.yml):

BEGIN;
UPDATE ekb_genplan_functional_zone
   SET geom = ST_MakeValid(geom)
 WHERE NOT ST_IsValid(geom);
COMMIT;

Acceptance

  • После фикса: SELECT count(*) FILTER (WHERE NOT ST_IsValid(geom)) FROM ekb_genplan_functional_zone = 0.
  • ST_MakeValid не меняет тип на GeometryCollection для валидных входов (проверить — если есть вырожденные, отфильтровать ST_CollectionExtract(...,3) для полигонов).
  • Тест harvest: замоканная фича с self-intersecting ring → upsert → ST_IsValid true.
  • Re-harvest не реинтродуцирует битые.

Файлы: backend/app/workers/tasks/genplan_zones_sync.py, data/sql/NN_*.sql. Refs #1058 #1067.

scope/backend · site-finder · ИРД · priority/p2

## Проблема (найдено бэктестом ИРД-слоя 2026-06-07) `ekb_genplan_functional_zone`: **881 из 6436 (13.7%)** полигонов имеют `ST_IsValid(geom)=false`. ```sql SELECT count(*) FILTER (WHERE NOT ST_IsValid(geom)) FROM ekb_genplan_functional_zone; -- 881 ``` Битые геометрии при `ST_Intersects` в `parcel_functional_zones` могут ронять резолв функц.зоны генплана (#1058) или молча его искажать — функц.зона участка в analyze (`ird.functional_zones`) может теряться/быть неверной для участков над битыми зонами. ## Корень `genplan_zones_sync.py` (`_UPSERT_SQL`, строка ~49) пишет geom без валидации: ```sql ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326) ``` Исходный геопортальный GeoJSON содержит self-intersecting / ring-order дефекты → попадают в БД как есть. ## Фикс ### 1. Harvest (предотвращение новых) В `genplan_zones_sync.py` обернуть geom-выражение в `ST_MakeValid` (и в VALUES, и в `ON CONFLICT ... DO UPDATE SET geom = EXCLUDED.geom` — EXCLUDED уже возьмёт исправленное, если поправить VALUES): ```sql ST_MakeValid(ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326)) ``` ### 2. Backfill существующих (одноразово) SQL-миграция `data/sql/NN_genplan_makevalid_backfill.sql` (идемпотентна, авто-применяется через deploy.yml): ```sql BEGIN; UPDATE ekb_genplan_functional_zone SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom); COMMIT; ``` ## Acceptance - После фикса: `SELECT count(*) FILTER (WHERE NOT ST_IsValid(geom)) FROM ekb_genplan_functional_zone` = **0**. - `ST_MakeValid` не меняет тип на GeometryCollection для валидных входов (проверить — если есть вырожденные, отфильтровать `ST_CollectionExtract(...,3)` для полигонов). - Тест harvest: замоканная фича с self-intersecting ring → upsert → `ST_IsValid` true. - Re-harvest не реинтродуцирует битые. Файлы: `backend/app/workers/tasks/genplan_zones_sync.py`, `data/sql/NN_*.sql`. Refs #1058 #1067. scope/backend · site-finder · ИРД · priority/p2
lekss361 added the
priority/p2
scope/backend
site-finder
ИРД
labels 2026-06-07 17:41:12 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#1156
No description provided.