-- 139_ekb_genplan_functional_zone.sql -- #1058 / #1067 (Site Finder v2, «GG-форсайт») — функциональные зоны генплана ЕКБ-2045. -- -- Источник: ГИСОГД-СО WFS (gisogd66.midural.ru), слой -- agate_ekbgo:agate_ekbgo_geo_urban_10_functional_zone_. -- Harvest-воркер: tasks/genplan_zones_sync.py (Celery beat «genplan-zones-sync-weekly»). -- Read-side: app/services/site_finder/functional_zone_lookup.py → parcel_functional_zones(). -- Analyze-врезка: follow-up после merge #1131 (чтобы не конфликтить с ird-analyze PR). -- -- Grain: одна строка = одна зона (Polygon/MultiPolygon). Дедуп-ключ — globalid (UUID из ГИСОГД). -- -- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз по NN). Idempotent: -- CREATE TABLE/INDEX IF NOT EXISTS + guarded DO-блок для UNIQUE (зеркаль м.132, м.125). -- НИКАКИХ строк здесь не вставляется — данные приходят из harvest-воркера. BEGIN; -- ── 1. Таблица ekb_genplan_functional_zone ──────────────────────────────────── CREATE TABLE IF NOT EXISTS ekb_genplan_functional_zone ( id BIGSERIAL PRIMARY KEY, globalid TEXT NOT NULL, -- UUID из ГИСОГД (дедуп-ключ, UNIQUE ниже) zone_type TEXT, -- читаемый тип зоны (full_name / urban10_classid_v) zone_class_code TEXT, -- числовой код класса (urban10_classid) status TEXT, -- «Существующий» / «Планируемый» (urban10_status_v) area_ha NUMERIC, -- площадь, га (urban10_area) doc_ref TEXT, -- ссылка на документ генплана (info_set_key_ref) geom geometry(Geometry, 4326) NOT NULL, -- Polygon/MultiPolygon, EPSG:4326 raw_props JSONB, -- полный properties фичи (forward-compat / debug) fetched_at timestamptz NOT NULL DEFAULT now() ); COMMENT ON TABLE ekb_genplan_functional_zone IS 'Функциональные зоны генплана ЕКБ-2045 (#1058, GG-форсайт). ' 'Источник: ГИСОГД-СО WFS (gisogd66.midural.ru). ' 'UPSERT harvest-воркером tasks/genplan_zones_sync.py по globalid. ' 'analyze читает overlap участка через ST_Intersects(geom). Пустая = 0 overlap.'; COMMENT ON COLUMN ekb_genplan_functional_zone.globalid IS 'UUID объекта из ГИСОГД-СО (urban10_globalid); дедуп-ключ UPSERT'; COMMENT ON COLUMN ekb_genplan_functional_zone.zone_type IS 'Читаемый тип зоны: full_name или urban10_classid_v («Жилые зоны» и т.д.)'; COMMENT ON COLUMN ekb_genplan_functional_zone.zone_class_code IS 'Числовой код класса зоны (urban10_classid)'; -- ── 2. UNIQUE по globalid (дедуп для UPSERT) ───────────────────────────────── -- Guard через pg_constraint — idempotent re-apply (зеркаль м.132 / м.125 / м.110). DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'uq_ekb_genplan_functional_zone_globalid' ) THEN ALTER TABLE ekb_genplan_functional_zone ADD CONSTRAINT uq_ekb_genplan_functional_zone_globalid UNIQUE (globalid); END IF; END $$; -- ── 3. Индексы ──────────────────────────────────────────────────────────────── -- Read-path: ST_Intersects(parcel.geom, zone.geom) через GIST. CREATE INDEX IF NOT EXISTS idx_ekb_genplan_fz_geom ON ekb_genplan_functional_zone USING GIST (geom); -- Фильтрация по типу зоны (выборка «все жилые» / «все производственные»). CREATE INDEX IF NOT EXISTS idx_ekb_genplan_fz_zone_type ON ekb_genplan_functional_zone (zone_type); -- Фильтрация по статусу (существующие vs планируемые). CREATE INDEX IF NOT EXISTS idx_ekb_genplan_fz_status ON ekb_genplan_functional_zone (status) WHERE status IS NOT NULL; COMMIT;