-- 140_ekb_krt_geometry.sql -- #1130 (Site Finder v2 / GG-форсайт) — КРТ-геометрии геопортала ЕКБ в БД. -- -- Слой portal_geo_oks_krt («Здания в границах КРТ») → ~48 WFS-фич с Polygon-геометриями. -- Harvest-воркер (krt_geometry_sync.py) раз в неделю UPSERT-ит сюда все фичи одним bbox-запросом. -- analyze читает _krt_at через ST_Intersects(geom, point) — без живого WFS-вызова в hot-пути. -- -- UNIQUE-ключ: feature_id — стабильный строковый id WFS-фичи GeoServer -- (вида «portal_geo_oks_krt.NNN»; trailing-int выделяется для numeric lookup). -- Выбор обоснован: GeoServer feature_id стабилен между запросами одного слоя (это PK GeoServer), -- не зависит от изменяемых атрибутов (num_oks_krt/status/area) и присутствует у каждой фичи. -- Альтернатива (num_oks_krt+MD5(geom)) нестабильна при минимальных правках геометрии → излишний -- DELETE+INSERT вместо UPDATE; feature_id надёжнее. -- -- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз по NN). -- Idempotent (CREATE TABLE/INDEX IF NOT EXISTS + guarded DO-блок для UNIQUE). -- Зеркалит pattern м.132 (ird_overlays) и м.137 (ekb_krt_sites). -- Без INSERT — данные приходят из Celery-воркера krt_geometry_sync. BEGIN; -- ── 1. Таблица ekb_krt_geometry ────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS ekb_krt_geometry ( id BIGSERIAL PRIMARY KEY, feature_id TEXT NOT NULL, -- GeoServer WFS feature id («portal_geo_oks_krt.7») num_oks_krt INTEGER, -- порядковый номер КРТ-площадки full_name TEXT, -- полное наименование (из свойств слоя) status TEXT, -- статус КРТ («В работе», «Завершён» и т.п.) geom geometry(Geometry, 4326) NOT NULL, -- полигон КРТ-территории, EPSG:4326 area NUMERIC, -- площадь из свойств (га или кв. м — как в слое) raw_props JSONB, -- полный properties-объект WFS-фичи (forward-compat) fetched_at TIMESTAMPTZ NOT NULL DEFAULT now() ); COMMENT ON TABLE ekb_krt_geometry IS 'Геометрия КРТ-территорий ЕКБ (#1130, GG-форсайт). ' 'Источник: WFS-слой portal_geo_oks_krt геопортала ЕКБ (~48 фич). ' 'Harvest — Celery krt_geometry_sync раз в неделю (bbox ЕКБ-агломерации). ' 'analyze._krt_at читает ST_Intersects(geom, point) вместо живого WFS-вызова.'; COMMENT ON COLUMN ekb_krt_geometry.feature_id IS 'Стабильный GeoServer WFS feature id слоя («portal_geo_oks_krt.7»). UNIQUE ON CONFLICT target.'; COMMENT ON COLUMN ekb_krt_geometry.num_oks_krt IS 'Порядковый номер КРТ-площадки из свойств WFS-фичи (num_oks_krt). NULL если не задан.'; COMMENT ON COLUMN ekb_krt_geometry.geom IS 'Polygon/MultiPolygon КРТ-территории в EPSG:4326 (WFS GeoServer отдаёт в 4326 напрямую).'; -- ── 2. UNIQUE по feature_id (ON CONFLICT target для UPSERT) ───────────────── DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'uq_ekb_krt_geometry_feature_id' ) THEN ALTER TABLE ekb_krt_geometry ADD CONSTRAINT uq_ekb_krt_geometry_feature_id UNIQUE (feature_id); END IF; END $$; -- ── 3. Индексы ─────────────────────────────────────────────────────────────── -- ГЛАВНЫЙ read-path: ST_Intersects(geom, point) из _krt_at в analyze hot-пути. CREATE INDEX IF NOT EXISTS idx_ekb_krt_geometry_geom ON ekb_krt_geometry USING GIST (geom); -- Lookup по порядковому номеру (JOIN к ekb_krt_sites по num_oks_krt). CREATE INDEX IF NOT EXISTS idx_ekb_krt_geometry_num_oks_krt ON ekb_krt_geometry (num_oks_krt) WHERE num_oks_krt IS NOT NULL; -- Мониторинг свежести данных. CREATE INDEX IF NOT EXISTS idx_ekb_krt_geometry_fetched_at ON ekb_krt_geometry (fetched_at DESC); COMMIT;