-- 134_planning_projects.sql -- #1085 (Site Finder v2 / future-supply «GG-форсайт») — store ППТ/ПМТ ЕКБ (геометрия + реквизиты). -- -- Future-supply: что/где утверждено к застройке. Источник — ЕКБ-геопортал GeoServer WFS -- (ekb_geoportal_client.py, #1083): слои portal_geo_dmd_prj_plan (ППТ — проекты планировки) и -- portal_geo_dmd_prj_mej (ПМТ — проекты межевания). Каждая фича — утверждённый документ -- планировки с геометрией охвата + doc_status/год (live-verified 2026-06-06, vault research/ -- EKB_Geoportal_WFS_Construction_Intel_Jun06). -- -- ОТДЕЛЬНАЯ таблица (не ird_overlays м.132): ird_overlays — НСПД-ограничения участка (per-feature -- ЗОУИТ/ПЗЗ/ОКН + reg_numb_border + category/subcategory), а тут документ-центричная модель -- геопортала (doc_status / dmd_actual_year / project_name / full_name) из другого источника (WFS, -- уже EPSG:4326). Смешивать схемы — натяжка; держим раздельно, analyze читает overlap отдельно. -- -- Что отдаёт WFS-фича (DescribeFeatureType + live): key (стабильный id портала), full_name -- (документ-описание), doc_full_name/doc_status/doc_status_name (статус утверждения), -- dmd_actual_year (год актуальности), project_name, geoloc (геометрия 4326). -- -- Зерно (grain): одна строка = одна WFS-фича (один документ планировки) в слое. Дедуп-ключ — -- (project_type, source_key): source_key = портальный `key`; project_type 'ppt'|'pmt'. -- re-harvest UPSERT-ит (latest wins): документы стабильны, обновление = свежий fetched_at. -- -- ТЭП/очередность из PDF пояснительных записок (pdfplumber) — отдельный проход (#1085 follow-up); -- колонки tep/staging добавятся ALTER-ом позже (тут только WFS-слой: геометрия + статус). -- -- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз по NN). Idempotent: -- CREATE TABLE/INDEX IF NOT EXISTS + guarded DO для UNIQUE (прецедент м.125/132/133). BEGIN; -- ── 1. Таблица planning_projects ─────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS planning_projects ( id BIGSERIAL PRIMARY KEY, project_type TEXT NOT NULL, -- 'ppt' (проект планировки) | 'pmt' (проект межевания) source_key BIGINT NOT NULL, -- стабильный `key` фичи геопортала (дедуп внутри типа) full_name TEXT, -- описание документа (ПАГЕ № / «Об утверждении ...») doc_full_name TEXT, -- doc_full_name WFS doc_status TEXT, -- doc_status (код/строка статуса утверждения) doc_status_name TEXT, -- читаемый статус ('действующий'/'утверждён'/...) project_name TEXT, -- project_name WFS dmd_actual_year INTEGER, -- год актуальности документа (dmd_actual_year) geom geometry(Geometry, 4326) NOT NULL, -- охват документа (WFS geoloc, уже 4326) raw_props JSONB, -- полный properties фичи (forward-compat / ТЭП-резолв) fetched_at timestamptz NOT NULL DEFAULT now(), -- когда harvest снял (freshness) CONSTRAINT planning_projects_type_chk CHECK (project_type IN ('ppt', 'pmt')), CONSTRAINT planning_projects_source_key_chk CHECK (source_key > 0) ); COMMENT ON TABLE planning_projects IS 'ППТ/ПМТ ЕКБ (#1085, future-supply GG-форсайт): утверждённые проекты планировки/межевания из ' 'ЕКБ-геопортала WFS (portal_geo_dmd_prj_plan/_mej). 1 строка = 1 WFS-фича (документ). UPSERT ' 'harvest-воркером по uq_planning_projects (project_type, source_key). analyze читает overlap ' 'участка через ST_Intersects(geom). ТЭП/очередность из PDF — отдельный проход (follow-up).'; COMMENT ON COLUMN planning_projects.source_key IS 'Стабильный `key` фичи ЕКБ-геопортала — дедуп-ключ внутри project_type'; COMMENT ON COLUMN planning_projects.doc_status_name IS 'Читаемый статус утверждения документа планировки (действующий/утверждён/...)'; -- ── 2. Дедуп/UPSERT-ключ (project_type, source_key) ──────────────────────────── DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'uq_planning_projects' ) THEN ALTER TABLE planning_projects ADD CONSTRAINT uq_planning_projects UNIQUE (project_type, source_key); END IF; END $$; -- ── 3. Индексы ───────────────────────────────────────────────────────────────── -- Главный read-path: spatial overlap участка (analyze: ST_Intersects(parcel.geom, pp.geom)). CREATE INDEX IF NOT EXISTS idx_planning_projects_geom ON planning_projects USING GIST (geom); -- Фильтр по типу проекта (выборка «все ППТ» / «все ПМТ»). CREATE INDEX IF NOT EXISTS idx_planning_projects_type ON planning_projects (project_type); COMMIT;