Files
ArchiTools/prisma/gisuat-postgis-setup.sql
T
AI Assistant 76c19449f3 perf(geoportal): zoom-dependent UAT simplification + Martin config + tile cache
PostGIS:
- gis_uats view: ST_SimplifyPreserveTopology(geom, 50) + only name/county/siruta
- gis_uats_simple view: ST_SimplifyPreserveTopology(geom, 500) for z0-z9

Martin config (martin.yaml):
- Explicit source definitions (auto_publish: false)
- gis_uats_simple (z0-9): only name+siruta, 500m simplified geometry
- gis_uats (z0-14): name+siruta+county, 50m simplified
- gis_terenuri (z10-18): object_id+siruta+cadastral_ref+area_value+layer_id
- gis_cladiri (z12-18): same properties
- 24h cache headers on all tiles

MapViewer:
- Dual UAT sources: simplified (z0-9) + detailed (z9+) with seamless handoff
- Zoom-interpolated paint: thin lines at z5, thicker at z12
- UAT labels only z9+, fill opacity z-interpolated (0.03→0.08)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:24:38 +02:00

187 lines
5.8 KiB
PL/PgSQL

-- =============================================================================
-- PostGIS native geometry setup for GisUat (UAT boundaries)
-- Run once manually: PGPASSWORD='...' psql -h 10.10.10.166 -p 5432 \
-- -U architools_user -d architools_db -f prisma/gisuat-postgis-setup.sql
--
-- Idempotent — safe to re-run.
--
-- What this does:
-- 1. Ensures PostGIS extension
-- 2. Adds native geometry column (geom) if missing
-- 3. Creates function to convert Esri ring JSON -> PostGIS geometry
-- 4. Creates trigger to auto-convert on INSERT/UPDATE
-- 5. Backfills existing rows
-- 6. Creates GiST spatial index
-- 7. Creates Martin/QGIS-friendly view 'gis_uats'
--
-- After running both SQL scripts (postgis-setup.sql + this file), Martin
-- will auto-discover these views (any table/view with a 'geom' geometry column):
-- - gis_features (master: all GisFeature rows with geometry)
-- - gis_terenuri (parcels from GisFeature)
-- - gis_cladiri (buildings from GisFeature)
-- - gis_documentatii (expertize/zone/receptii from GisFeature)
-- - gis_administrativ (limite UAT/intravilan/arii speciale from GisFeature)
-- - gis_uats (UAT boundaries from GisUat) <-- this script
--
-- All geometries are in EPSG:3844 (Stereo70).
-- =============================================================================
-- 1. Ensure PostGIS extension
CREATE EXTENSION IF NOT EXISTS postgis;
-- 2. Add native geometry column (idempotent)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'GisUat' AND column_name = 'geom'
) THEN
ALTER TABLE "GisUat" ADD COLUMN geom geometry(Geometry, 3844);
END IF;
END $$;
-- 3. Function: convert Esri ring JSON { rings: number[][][] } -> PostGIS geometry
-- Esri rings format: each ring is an array of [x, y] coordinate pairs.
-- First ring = exterior, subsequent rings = holes.
-- Multiple outer rings (non-holes) would need MultiPolygon, but UAT boundaries
-- from eTerra typically have a single polygon with possible holes.
--
-- Strategy: build WKT POLYGON/MULTIPOLYGON from the rings array, then
-- use ST_GeomFromText with SRID 3844.
CREATE OR REPLACE FUNCTION gis_uat_esri_to_geom(geom_json jsonb)
RETURNS geometry AS $$
DECLARE
rings jsonb;
ring jsonb;
coord jsonb;
ring_count int;
coord_count int;
i int;
j int;
wkt_ring text;
wkt text;
first_x double precision;
first_y double precision;
last_x double precision;
last_y double precision;
BEGIN
-- Extract the rings array from the JSON
rings := geom_json -> 'rings';
IF rings IS NULL OR jsonb_array_length(rings) = 0 THEN
RETURN NULL;
END IF;
ring_count := jsonb_array_length(rings);
-- Build WKT POLYGON with all rings (first = exterior, rest = holes)
wkt := 'POLYGON(';
FOR i IN 0 .. ring_count - 1 LOOP
ring := rings -> i;
coord_count := jsonb_array_length(ring);
IF coord_count < 3 THEN
CONTINUE; -- skip degenerate rings
END IF;
IF i > 0 THEN
wkt := wkt || ', ';
END IF;
wkt_ring := '(';
FOR j IN 0 .. coord_count - 1 LOOP
coord := ring -> j;
IF j > 0 THEN
wkt_ring := wkt_ring || ', ';
END IF;
wkt_ring := wkt_ring || (coord ->> 0) || ' ' || (coord ->> 1);
-- Track first and last coordinates to check ring closure
IF j = 0 THEN
first_x := (coord ->> 0)::double precision;
first_y := (coord ->> 1)::double precision;
END IF;
IF j = coord_count - 1 THEN
last_x := (coord ->> 0)::double precision;
last_y := (coord ->> 1)::double precision;
END IF;
END LOOP;
-- Close the ring if not already closed
IF first_x != last_x OR first_y != last_y THEN
wkt_ring := wkt_ring || ', ' || first_x::text || ' ' || first_y::text;
END IF;
wkt_ring := wkt_ring || ')';
wkt := wkt || wkt_ring;
END LOOP;
wkt := wkt || ')';
RETURN ST_GeomFromText(wkt, 3844);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- 4. Trigger function: auto-convert Esri JSON -> native PostGIS on INSERT/UPDATE
CREATE OR REPLACE FUNCTION gis_uat_sync_geom()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.geometry IS NOT NULL THEN
BEGIN
NEW.geom := gis_uat_esri_to_geom(NEW.geometry::jsonb);
EXCEPTION WHEN OTHERS THEN
-- Invalid geometry JSON -> leave geom NULL rather than fail the write
NEW.geom := NULL;
END;
ELSE
NEW.geom := NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 5. Attach trigger (drop + recreate for idempotency)
DROP TRIGGER IF EXISTS trg_gis_uat_sync_geom ON "GisUat";
CREATE TRIGGER trg_gis_uat_sync_geom
BEFORE INSERT OR UPDATE OF geometry ON "GisUat"
FOR EACH ROW
EXECUTE FUNCTION gis_uat_sync_geom();
-- 6. Backfill: convert existing Esri JSON geometries to native PostGIS
UPDATE "GisUat"
SET geom = gis_uat_esri_to_geom(geometry::jsonb)
WHERE geometry IS NOT NULL AND geom IS NULL;
-- 7. GiST spatial index for fast spatial queries
CREATE INDEX IF NOT EXISTS gis_uat_geom_idx
ON "GisUat" USING GIST (geom);
-- 8. Martin/QGIS-friendly view (moderate simplification — 50m tolerance)
CREATE OR REPLACE VIEW gis_uats AS
SELECT
siruta,
name,
county,
ST_SimplifyPreserveTopology(geom, 50) AS geom
FROM "GisUat"
WHERE geom IS NOT NULL;
-- 9. Simplified view for low zoom levels (z5-z9) — 500m tolerance
CREATE OR REPLACE VIEW gis_uats_simple AS
SELECT
siruta,
name,
ST_SimplifyPreserveTopology(geom, 500) AS geom
FROM "GisUat"
WHERE geom IS NOT NULL;
-- =============================================================================
-- Done! Martin serves these views as vector tiles:
-- - gis_uats (moderate detail, z9+)
-- - gis_uats_simple (coarse overview, z5-z9)
-- QGIS: PostgreSQL -> 10.10.10.166:5432 / architools_db -> gis_uats view
-- SRID: 3844 (Stereo70)
-- =============================================================================