1471 lines
38 KiB
MySQL
1471 lines
38 KiB
MySQL
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
--
|
|
--
|
|
-- PostGIS - Spatial Types for PostgreSQL
|
|
-- http://postgis.net
|
|
--
|
|
-- Copyright (C) 2010, 2011 Sandro Santilli <strk@kbt.io>
|
|
-- Copyright (C) 2005 Refractions Research Inc.
|
|
--
|
|
-- This is free software; you can redistribute and/or modify it under
|
|
-- the terms of the GNU General Public Licence. See the COPYING file.
|
|
--
|
|
-- Author: Sandro Santilli <strk@kbt.io>
|
|
--
|
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
--
|
|
-- STATUS:
|
|
--
|
|
-- All objects are created in the 'topology' schema.
|
|
--
|
|
-- We have PostGIS-specific objects and SQL/MM objects.
|
|
-- PostGIS-specific objects have no prefix, SQL/MM ones
|
|
-- have the ``ST_' prefix.
|
|
--
|
|
-- [PostGIS-specific]
|
|
--
|
|
-- TABLE topology
|
|
-- Table storing topology info (name, srid, precision)
|
|
--
|
|
-- TYPE TopoGeometry
|
|
-- Complex type storing topology_id, layer_id, geometry type
|
|
-- and topogeometry id.
|
|
--
|
|
-- DOMAIN TopoElement
|
|
-- An array of two elements: element_id and element_type.
|
|
-- In fact, an array of integers.
|
|
--
|
|
-- DOMAIN TopoElementArray
|
|
-- An array of element_id,element_type values.
|
|
-- In fact, a bidimensional array of integers:
|
|
-- '{{id,type}, {id,type}, ...}'
|
|
--
|
|
-- FUNCTION CreateTopology(name, [srid], [precision])
|
|
-- Initialize a new topology (creating schema with
|
|
-- edge,face,node,relation) and add a record into
|
|
-- the topology.topology table.
|
|
-- TODO: add triggers (or rules, or whatever) enforcing
|
|
-- precision to the edge and node tables.
|
|
--
|
|
-- FUNCTION DropTopology(name)
|
|
-- Delete a topology removing reference from the
|
|
-- topology.topology table
|
|
--
|
|
-- FUNCTION GetTopologyId(name)
|
|
-- FUNCTION GetTopologySRID(name)
|
|
-- FUNCTION GetTopologyName(id)
|
|
-- Return info about a Topology
|
|
--
|
|
-- FUNCTION AddTopoGeometryColumn(toponame, schema, table, column, geomtype)
|
|
-- Add a TopoGeometry column to a table, making it a topology layer.
|
|
-- Returns created layer id.
|
|
--
|
|
-- FUNCTION DropTopoGeometryColumn(schema, table, column)
|
|
-- Drop a TopoGeometry column, unregister the associated layer,
|
|
-- cleanup the relation table.
|
|
--
|
|
-- FUNCTION CreateTopoGeom(toponame, geomtype, layer_id, topo_objects)
|
|
-- Create a TopoGeometry object from existing Topology elements.
|
|
-- The "topo_objects" parameter is of TopoElementArray type.
|
|
--
|
|
-- FUNCTION GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
|
|
-- FUNCTION GetTopoGeomElementArray(TopoGeometry)
|
|
-- Returns a TopoElementArray object containing the topological
|
|
-- elements of the given TopoGeometry.
|
|
--
|
|
-- FUNCTION GetTopoGeomElements(toponame, layer_id, topogeom_id)
|
|
-- FUNCTION GetTopoGeomElements(TopoGeometry)
|
|
-- Returns a set of TopoElement objects containing the
|
|
-- topological elements of the given TopoGeometry (primitive
|
|
-- elements)
|
|
--
|
|
-- FUNCTION ValidateTopology(toponame)
|
|
-- Run validity checks on the topology, returning, for each
|
|
-- detected error, a 3-columns row containing error string
|
|
-- and references to involved topo elements: error, id1, id2
|
|
--
|
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
--
|
|
-- Overloaded functions for TopoGeometry inputs
|
|
--
|
|
-- FUNCTION intersects(TopoGeometry, TopoGeometry)
|
|
-- FUNCTION equals(TopoGeometry, TopoGeometry)
|
|
--
|
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
--
|
|
-- FUNCTION TopoGeo_AddPoint(toponame, point)
|
|
-- Add a Point geometry to the topology
|
|
-- TODO: accept a topology/layer id
|
|
-- rework to use existing node if existent
|
|
--
|
|
-- FUNCTION TopoGeo_AddLinestring(toponame, line)
|
|
-- Add a LineString geometry to the topology
|
|
-- TODO: accept a topology/layer id
|
|
-- rework to use existing nodes/edges
|
|
-- splitting them if required
|
|
--
|
|
-- FUNCTION TopoGeo_AddPolygon(toponame, polygon)
|
|
-- Add a Polygon geometry to the topology
|
|
-- TODO: implement
|
|
--
|
|
-- TYPE GetFaceEdges_ReturnType
|
|
-- Complex type used to return tuples from ST_GetFaceEdges
|
|
--
|
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
--
|
|
-- [SQL/MM]
|
|
--
|
|
-- ST_InitTopoGeo
|
|
-- Done, can be modified to include explicit sequences or
|
|
-- more constraints. Very noisy due to implicit index creations
|
|
-- for primary keys and sequences for serial fields...
|
|
--
|
|
-- ST_CreateTopoGeo
|
|
-- Complete
|
|
--
|
|
-- ST_AddIsoNode
|
|
-- Complete
|
|
--
|
|
-- ST_RemoveIsoNode
|
|
-- Complete
|
|
--
|
|
-- ST_MoveIsoNode
|
|
-- Complete
|
|
--
|
|
-- ST_AddIsoEdge
|
|
-- Complete
|
|
--
|
|
-- ST_RemoveIsoEdge
|
|
-- Complete, exceptions untested
|
|
--
|
|
-- ST_ChangeEdgeGeom
|
|
-- Complete
|
|
--
|
|
-- ST_NewEdgesSplit
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_ModEdgeSplit
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_AddEdgeNewFaces
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_AddEdgeModFace
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_GetFaceEdges
|
|
-- Complete
|
|
--
|
|
-- ST_ModEdgeHeal
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_NewEdgeHeal
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_GetFaceGeometry
|
|
-- Implemented using ST_BuildArea()
|
|
--
|
|
-- ST_RemEdgeNewFace
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_RemEdgeModFace
|
|
-- Complete
|
|
-- Also updates the Relation table
|
|
--
|
|
-- ST_ValidateTopoGeo
|
|
-- Unimplemented (probably a wrapper around ValidateTopology)
|
|
--
|
|
--
|
|
|
|
-- Uninstalling previous installation isn't really a good habit ...
|
|
-- Let people decide about that
|
|
-- DROP SCHEMA topology CASCADE;
|
|
|
|
#include "sqldefines.h"
|
|
|
|
-- INSTALL VERSION: POSTGIS_LIB_VERSION
|
|
|
|
CREATE SCHEMA topology;
|
|
COMMENT ON SCHEMA topology IS 'PostGIS Topology schema';
|
|
|
|
-- Doing everything outside of a transaction helps
|
|
-- upgrading in the best case.
|
|
BEGIN;
|
|
|
|
--={ ----------------------------------------------------------------
|
|
-- POSTGIS-SPECIFIC block
|
|
--
|
|
-- This part contains function NOT in the SQL/MM specification
|
|
--
|
|
---------------------------------------------------------------------
|
|
|
|
--
|
|
-- Topology table.
|
|
-- Stores id,name,precision and SRID of topologies.
|
|
--
|
|
CREATE TABLE topology.topology (
|
|
id SERIAL NOT NULL PRIMARY KEY,
|
|
name VARCHAR NOT NULL UNIQUE,
|
|
SRID INTEGER NOT NULL,
|
|
precision FLOAT8 NOT NULL,
|
|
hasz BOOLEAN NOT NULL DEFAULT false
|
|
);
|
|
|
|
--{ LayerTrigger()
|
|
--
|
|
-- Layer integrity trigger
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.LayerTrigger()
|
|
RETURNS trigger
|
|
AS
|
|
$$
|
|
DECLARE
|
|
rec RECORD;
|
|
ok BOOL;
|
|
toponame varchar;
|
|
query TEXT;
|
|
BEGIN
|
|
|
|
--RAISE NOTICE 'LayerTrigger called % % at % level', TG_WHEN, TG_OP, TG_LEVEL;
|
|
|
|
IF TG_OP = 'INSERT' THEN
|
|
RAISE EXCEPTION 'LayerTrigger not meant to be called on INSERT';
|
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
RAISE EXCEPTION 'The topology.layer table cannot be updated';
|
|
END IF;
|
|
|
|
-- Check for existance of any feature column referencing
|
|
-- this layer
|
|
FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
|
|
WHERE text(n.nspname) = OLD.schema_name
|
|
AND c.relnamespace = n.oid
|
|
AND text(c.relname) = OLD.table_name
|
|
AND a.attrelid = c.oid
|
|
AND text(a.attname) = OLD.feature_column
|
|
LOOP
|
|
query = 'SELECT * '
|
|
' FROM ' || quote_ident(OLD.schema_name)
|
|
|| '.' || quote_ident(OLD.table_name)
|
|
|| ' WHERE layer_id('
|
|
|| quote_ident(OLD.feature_column)||') '
|
|
'=' || OLD.layer_id
|
|
|| ' LIMIT 1';
|
|
--RAISE NOTICE '%', query;
|
|
FOR rec IN EXECUTE query
|
|
LOOP
|
|
RAISE NOTICE 'A feature referencing layer % of topology % still exists in %.%.%', OLD.layer_id, OLD.topology_id, OLD.schema_name, OLD.table_name, OLD.feature_column;
|
|
RETURN NULL;
|
|
END LOOP;
|
|
END LOOP;
|
|
|
|
-- Get topology name
|
|
SELECT name FROM topology.topology INTO toponame
|
|
WHERE id = OLD.topology_id;
|
|
|
|
IF toponame IS NULL THEN
|
|
RAISE NOTICE 'Could not find name of topology with id %',
|
|
OLD.layer_id;
|
|
END IF;
|
|
|
|
-- Check if any record in the relation table references this layer
|
|
FOR rec IN SELECT c.oid FROM pg_namespace n, pg_class c
|
|
WHERE text(n.nspname) = toponame AND c.relnamespace = n.oid
|
|
AND c.relname = 'relation'
|
|
LOOP
|
|
query = 'SELECT * '
|
|
' FROM ' || quote_ident(toponame)
|
|
|| '.relation '
|
|
' WHERE layer_id = '|| OLD.layer_id
|
|
|| ' LIMIT 1';
|
|
--RAISE NOTICE '%', query;
|
|
FOR rec IN EXECUTE query
|
|
LOOP
|
|
RAISE NOTICE 'A record in %.relation still references layer %', toponame, OLD.layer_id;
|
|
RETURN NULL;
|
|
END LOOP;
|
|
END LOOP;
|
|
|
|
RETURN OLD;
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' VOLATILE STRICT;
|
|
--} LayerTrigger()
|
|
|
|
--{
|
|
-- Layer table.
|
|
-- Stores topology layer informations
|
|
--
|
|
CREATE TABLE topology.layer (
|
|
topology_id INTEGER NOT NULL
|
|
REFERENCES topology.topology(id),
|
|
layer_id integer NOT NULL,
|
|
schema_name VARCHAR NOT NULL,
|
|
table_name VARCHAR NOT NULL,
|
|
feature_column VARCHAR NOT NULL,
|
|
feature_type integer NOT NULL,
|
|
level INTEGER NOT NULL DEFAULT 0,
|
|
child_id INTEGER DEFAULT NULL,
|
|
UNIQUE(schema_name, table_name, feature_column),
|
|
PRIMARY KEY(topology_id, layer_id)
|
|
);
|
|
|
|
CREATE TRIGGER layer_integrity_checks BEFORE UPDATE OR DELETE
|
|
ON topology.layer FOR EACH ROW EXECUTE PROCEDURE topology.LayerTrigger();
|
|
|
|
--} Layer table.
|
|
|
|
--
|
|
-- TopoGeometry type
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE TYPE topology.TopoGeometry AS (
|
|
topology_id integer,
|
|
layer_id integer,
|
|
id integer,
|
|
type integer -- 1: [multi]point, 2: [multi]line,
|
|
-- 3: [multi]polygon, 4: collection
|
|
);
|
|
|
|
--
|
|
-- TopoElement domain
|
|
--
|
|
-- This is an array of two elements: element_id and element_type.
|
|
--
|
|
-- When used to define _simple_ TopoGeometries,
|
|
-- element_type can be:
|
|
-- 0: a node
|
|
-- 1: an edge
|
|
-- 2: a face
|
|
-- and element_id will be the node, edge or face identifier
|
|
--
|
|
-- When used to define _hierarchical_ TopoGeometries,
|
|
-- element_type will be the child layer identifier and
|
|
-- element_id will be composing TopoGeometry identifier
|
|
--
|
|
CREATE DOMAIN topology.TopoElement AS integer[]
|
|
CONSTRAINT DIMENSIONS CHECK (
|
|
array_upper(VALUE, 2) IS NULL
|
|
AND array_upper(VALUE, 1) = 2
|
|
);
|
|
ALTER DOMAIN topology.TopoElement ADD
|
|
CONSTRAINT lower_dimension CHECK (
|
|
array_lower(VALUE, 1) = 1
|
|
);
|
|
ALTER DOMAIN topology.TopoElement DROP CONSTRAINT
|
|
IF EXISTS
|
|
type_range;
|
|
ALTER DOMAIN topology.TopoElement ADD
|
|
CONSTRAINT type_range CHECK (
|
|
VALUE[2] > 0
|
|
);
|
|
|
|
--
|
|
-- TopoElementArray domain
|
|
--
|
|
-- Changed: 3.1.0 - get rid of IS NOT NULL check
|
|
CREATE DOMAIN topology.TopoElementArray AS integer[][]
|
|
CONSTRAINT DIMENSIONS CHECK (
|
|
array_upper(VALUE, 2) = 2
|
|
AND array_upper(VALUE, 3) IS NULL
|
|
);
|
|
|
|
-- Changed: 3.1.0
|
|
ALTER DOMAIN topology.TopoElementArray DROP CONSTRAINT
|
|
IF EXISTS
|
|
DIMENSIONS;
|
|
|
|
-- Changed: 3.1.0 - get rid of IS NOT NULL check
|
|
ALTER DOMAIN topology.TopoElementArray ADD
|
|
CONSTRAINT type_range CHECK (
|
|
array_upper(VALUE, 2) = 2
|
|
AND array_upper(VALUE, 3) IS NULL
|
|
);
|
|
|
|
--{ RelationTrigger()
|
|
--
|
|
-- Relation integrity trigger
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.RelationTrigger()
|
|
RETURNS trigger
|
|
AS
|
|
$$
|
|
DECLARE
|
|
toponame varchar;
|
|
topoid integer;
|
|
plyr RECORD; -- parent layer
|
|
rec RECORD;
|
|
ok BOOL;
|
|
|
|
BEGIN
|
|
IF TG_NARGS != 2 THEN
|
|
RAISE EXCEPTION 'RelationTrigger called with wrong number of arguments';
|
|
END IF;
|
|
|
|
topoid = TG_ARGV[0];
|
|
toponame = TG_ARGV[1];
|
|
|
|
--RAISE NOTICE 'RelationTrigger called % % on %.relation for a %', TG_WHEN, TG_OP, toponame, TG_LEVEL;
|
|
|
|
IF TG_OP = 'DELETE' THEN
|
|
RAISE EXCEPTION 'RelationTrigger not meant to be called on DELETE';
|
|
END IF;
|
|
|
|
-- Get layer info (and verify it exists)
|
|
ok = false;
|
|
FOR plyr IN EXECUTE 'SELECT * FROM topology.layer '
|
|
'WHERE '
|
|
' topology_id = ' || topoid
|
|
|| ' AND'
|
|
' layer_id = ' || NEW.layer_id
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'Layer % does not exist in topology %',
|
|
NEW.layer_id, topoid;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF plyr.level > 0 THEN -- this is hierarchical layer
|
|
|
|
-- ElementType must be the layer child id
|
|
IF NEW.element_type != plyr.child_id THEN
|
|
RAISE EXCEPTION 'Type of elements in layer % must be set to its child layer id %', plyr.layer_id, plyr.child_id;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
-- ElementId must be an existent TopoGeometry in child layer
|
|
ok = false;
|
|
FOR rec IN EXECUTE 'SELECT topogeo_id FROM '
|
|
|| quote_ident(toponame) || '.relation '
|
|
' WHERE layer_id = ' || plyr.child_id
|
|
|| ' AND topogeo_id = ' || NEW.element_id
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'TopoGeometry % does not exist in the child layer %', NEW.element_id, plyr.child_id;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
ELSE -- this is a basic layer
|
|
|
|
-- ElementType must be compatible with layer type
|
|
IF plyr.feature_type != 4
|
|
AND plyr.feature_type != NEW.element_type
|
|
THEN
|
|
RAISE EXCEPTION 'Element of type % is not compatible with layer of type %', NEW.element_type, plyr.feature_type;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
--
|
|
-- Now lets see if the element is consistent, which
|
|
-- is it exists in the topology tables.
|
|
--
|
|
|
|
--
|
|
-- Element is a Node
|
|
--
|
|
IF NEW.element_type = 1
|
|
THEN
|
|
ok = false;
|
|
FOR rec IN EXECUTE 'SELECT node_id FROM '
|
|
|| quote_ident(toponame) || '.node '
|
|
' WHERE node_id = ' || NEW.element_id
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'Node % does not exist in topology %', NEW.element_id, toponame;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
--
|
|
-- Element is an Edge
|
|
--
|
|
ELSIF NEW.element_type = 2
|
|
THEN
|
|
ok = false;
|
|
FOR rec IN EXECUTE 'SELECT edge_id FROM '
|
|
|| quote_ident(toponame) || '.edge_data '
|
|
' WHERE edge_id = ' || abs(NEW.element_id)
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'Edge % does not exist in topology %', NEW.element_id, toponame;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
--
|
|
-- Element is a Face
|
|
--
|
|
ELSIF NEW.element_type = 3
|
|
THEN
|
|
IF NEW.element_id = 0 THEN
|
|
RAISE EXCEPTION 'Face % cannot be associated with any feature', NEW.element_id;
|
|
RETURN NULL;
|
|
END IF;
|
|
ok = false;
|
|
FOR rec IN EXECUTE 'SELECT face_id FROM '
|
|
|| quote_ident(toponame) || '.face '
|
|
' WHERE face_id = ' || NEW.element_id
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'Face % does not exist in topology %', NEW.element_id, toponame;
|
|
RETURN NULL;
|
|
END IF;
|
|
END IF;
|
|
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' VOLATILE STRICT;
|
|
--} RelationTrigger()
|
|
|
|
--{
|
|
-- DropTopoGeometryColumn(schema, table, colum)
|
|
--
|
|
-- Drop a TopoGeometry column, unregister the associated layer,
|
|
-- cleanup the relation table.
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.DropTopoGeometryColumn(schema varchar, tbl varchar, col varchar)
|
|
RETURNS text
|
|
AS
|
|
$BODY$
|
|
DECLARE
|
|
rec RECORD;
|
|
lyrinfo RECORD;
|
|
ok BOOL;
|
|
result text;
|
|
sql TEXT;
|
|
BEGIN
|
|
|
|
-- Get layer and topology info
|
|
|
|
sql := $$
|
|
SELECT t.name as toponame, l.*
|
|
FROM topology.topology t, topology.layer l
|
|
WHERE l.topology_id = t.id
|
|
AND l.schema_name = $1
|
|
AND l.table_name = $2
|
|
AND l.feature_column = $3
|
|
$$;
|
|
|
|
ok := false;
|
|
FOR rec IN EXECUTE sql USING schema, tbl, col
|
|
LOOP
|
|
ok := true;
|
|
lyrinfo := rec;
|
|
END LOOP;
|
|
|
|
-- Layer not found
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'No layer registered on %.%.%',
|
|
schema,tbl,col;
|
|
END IF;
|
|
|
|
-- Cleanup the relation table (if it exists)
|
|
BEGIN
|
|
sql := format(
|
|
'DELETE FROM %I.relation WHERE layer_id = $1',
|
|
lyrinfo.toponame
|
|
);
|
|
EXECUTE sql USING lyrinfo.layer_id;
|
|
EXCEPTION
|
|
WHEN UNDEFINED_TABLE THEN
|
|
RAISE NOTICE '%', SQLERRM;
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'Got % (%)', SQLERRM, SQLSTATE;
|
|
END;
|
|
|
|
-- Drop the sequence for topogeoms in this layer
|
|
sql := format(
|
|
'DROP SEQUENCE IF EXISTS %I.topogeo_s_%s',
|
|
lyrinfo.toponame,
|
|
lyrinfo.layer_id
|
|
);
|
|
EXECUTE sql;
|
|
|
|
ok = false;
|
|
FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
|
|
WHERE text(n.nspname) = schema
|
|
AND c.relnamespace = n.oid
|
|
AND text(c.relname) = tbl
|
|
AND a.attrelid = c.oid
|
|
AND text(a.attname) = col
|
|
LOOP
|
|
ok = true;
|
|
EXIT;
|
|
END LOOP;
|
|
|
|
IF ok THEN
|
|
-- Drop the layer column
|
|
sql := format(
|
|
'ALTER TABLE %I.%I DROP %I CASCADE',
|
|
schema, tbl, col
|
|
);
|
|
EXECUTE sql;
|
|
END IF;
|
|
|
|
-- Delete the layer record
|
|
sql := $$
|
|
DELETE FROM topology.layer
|
|
WHERE topology_id = $1
|
|
AND layer_id = $2
|
|
$$;
|
|
EXECUTE sql USING lyrinfo.topology_id, lyrinfo.layer_id;
|
|
|
|
|
|
result := format(
|
|
'Layer %s (%I.%I.%I) dropped',
|
|
lyrinfo.layer_id, schema, tbl, col
|
|
);
|
|
|
|
RETURN result;
|
|
END;
|
|
$BODY$
|
|
LANGUAGE 'plpgsql' VOLATILE;
|
|
--
|
|
--} DropTopoGeometryColumn
|
|
|
|
-- {
|
|
--
|
|
-- populate_topology_layer
|
|
--
|
|
-- Register missing layers into topology.topology, looking at
|
|
-- their constraints.
|
|
--
|
|
-- The function doesn't attempt to determine if a layer is
|
|
-- hierarchical or primitive, but always assumes primitive.
|
|
--
|
|
-- }{
|
|
DROP FUNCTION IF EXISTS topology.populate_topology_layer();
|
|
CREATE OR REPLACE FUNCTION topology.populate_topology_layer()
|
|
RETURNS TABLE(schema_name text, table_name text, feature_column text)
|
|
AS
|
|
$$
|
|
INSERT INTO topology.layer
|
|
WITH checks AS (
|
|
SELECT
|
|
n.nspname sch, r.relname tab,
|
|
replace(c.conname, 'check_topogeom_', '') col,
|
|
--c.consrc src,
|
|
regexp_matches(c.consrc,
|
|
'\.topology_id = (\d+).*\.layer_id = (\d+).*\.type = (\d+)') inf
|
|
FROM (SELECT conname, connamespace, conrelid, conkey, pg_get_constraintdef(oid) As consrc
|
|
FROM pg_constraint) AS c, pg_class r, pg_namespace n
|
|
WHERE c.conname LIKE 'check_topogeom_%'
|
|
AND r.oid = c.conrelid
|
|
AND n.oid = r.relnamespace
|
|
), newrows AS (
|
|
SELECT inf[1]::int as topology_id,
|
|
inf[2]::int as layer_id,
|
|
sch, tab, col, inf[3]::int as feature_type --, src
|
|
FROM checks c
|
|
WHERE NOT EXISTS (
|
|
SELECT * FROM topology.layer l
|
|
WHERE l.schema_name = c.sch
|
|
AND l.table_name = c.tab
|
|
AND l.feature_column = c.col
|
|
)
|
|
)
|
|
SELECT topology_id, layer_id, sch,
|
|
tab, col, feature_type,
|
|
0, NULL
|
|
FROM newrows RETURNING schema_name,table_name,feature_column;
|
|
$$
|
|
LANGUAGE 'sql' VOLATILE;
|
|
|
|
--{
|
|
-- CreateTopoGeom(topology_name, topogeom_type, layer_id, elements)
|
|
--
|
|
-- Create a TopoGeometry object from Topology elements.
|
|
-- The elements parameter is a two-dimensional array.
|
|
-- Every element of the array is either a Topology element represented by
|
|
-- (id, type) or a TopoGeometry element represented by (id, layer).
|
|
-- The actual semantic depends on the TopoGeometry layer, either at
|
|
-- level 0 (elements are topological primitives) or higer (elements
|
|
-- are TopoGeoms from child layer).
|
|
--
|
|
-- @param toponame Topology name
|
|
--
|
|
-- @param tg_type Spatial type of geometry
|
|
-- 1:[multi]point (puntal)
|
|
-- 2:[multi]line (lineal)
|
|
-- 3:[multi]poly (areal)
|
|
-- 4:collection (mixed)
|
|
--
|
|
-- @param layer_id Layer identifier
|
|
--
|
|
-- @param tg_objs Array of components
|
|
--
|
|
-- Return a topology.TopoGeometry object.
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.CreateTopoGeom(toponame varchar, tg_type integer, layer_id integer, tg_objs topology.TopoElementArray)
|
|
RETURNS topology.TopoGeometry
|
|
AS
|
|
$$
|
|
DECLARE
|
|
i integer;
|
|
dims varchar;
|
|
outerdims varchar;
|
|
innerdims varchar;
|
|
obj_type integer;
|
|
obj_id integer;
|
|
ret topology.TopoGeometry;
|
|
rec RECORD;
|
|
layertype integer;
|
|
layerlevel integer;
|
|
layerchild integer;
|
|
BEGIN
|
|
|
|
IF tg_type < 1 OR tg_type > 4 THEN
|
|
RAISE EXCEPTION 'Invalid TopoGeometry type % (must be in the range 1..4)', tg_type;
|
|
END IF;
|
|
|
|
-- Get topology id into return TopoGeometry
|
|
SELECT id INTO ret.topology_id
|
|
FROM topology.topology WHERE name = toponame;
|
|
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'Topology % does not exist', quote_literal(toponame);
|
|
END IF;
|
|
|
|
--
|
|
-- Get layer info
|
|
--
|
|
layertype := NULL;
|
|
FOR rec IN EXECUTE 'SELECT * FROM topology.layer'
|
|
' WHERE topology_id = ' || ret.topology_id
|
|
|| ' AND layer_id = ' || layer_id
|
|
LOOP
|
|
layertype = rec.feature_type;
|
|
layerlevel = rec.level;
|
|
layerchild = rec.child_id;
|
|
END LOOP;
|
|
|
|
-- Check for existence of given layer id
|
|
IF layertype IS NULL THEN
|
|
RAISE EXCEPTION 'No layer with id % is registered with topology %', layer_id, toponame;
|
|
END IF;
|
|
|
|
-- Verify compatibility between layer geometry type and
|
|
-- TopoGeom requested geometry type
|
|
IF layertype != 4 and layertype != tg_type THEN
|
|
RAISE EXCEPTION 'A Layer of type % cannot contain a TopoGeometry of type %', layertype, tg_type;
|
|
END IF;
|
|
|
|
-- Set layer id and type in return object
|
|
ret.layer_id = layer_id;
|
|
ret.type = tg_type;
|
|
|
|
--
|
|
-- Get new TopoGeo id from sequence
|
|
--
|
|
FOR rec IN EXECUTE 'SELECT nextval(' ||
|
|
quote_literal(
|
|
quote_ident(toponame) || '.topogeo_s_' || layer_id
|
|
) || ')'
|
|
LOOP
|
|
ret.id = rec.nextval;
|
|
END LOOP;
|
|
|
|
-- Loop over outer dimension
|
|
i = array_lower(tg_objs, 1);
|
|
LOOP
|
|
obj_id = tg_objs[i][1];
|
|
obj_type = tg_objs[i][2];
|
|
|
|
-- Elements of type 0 represent emptiness, just skip them
|
|
IF obj_type = 0 THEN
|
|
IF obj_id != 0 THEN
|
|
RAISE EXCEPTION 'Malformed empty topo element {0,%} -- id must be 0 as well', obj_id;
|
|
END IF;
|
|
ELSE
|
|
IF layerlevel = 0 THEN -- array specifies lower-level objects
|
|
IF tg_type != 4 and tg_type != obj_type THEN
|
|
RAISE EXCEPTION 'A TopoGeometry of type % cannot contain topology elements of type %', tg_type, obj_type;
|
|
END IF;
|
|
ELSE -- array specifies lower-level topogeometries
|
|
IF obj_type != layerchild THEN
|
|
RAISE EXCEPTION 'TopoGeom element layer do not match TopoGeom child layer';
|
|
END IF;
|
|
-- TODO: verify that the referred TopoGeometry really
|
|
-- exists in the relation table ?
|
|
END IF;
|
|
|
|
--RAISE NOTICE 'obj:% type:% id:%', i, obj_type, obj_id;
|
|
|
|
--
|
|
-- Insert record into the Relation table
|
|
--
|
|
EXECUTE 'INSERT INTO '||quote_ident(toponame)
|
|
|| '.relation(topogeo_id, layer_id, '
|
|
'element_id,element_type) '
|
|
' VALUES ('||ret.id
|
|
||','||ret.layer_id
|
|
|| ',' || obj_id || ',' || obj_type || ');';
|
|
END IF;
|
|
|
|
i = i+1;
|
|
IF i > array_upper(tg_objs, 1) THEN
|
|
EXIT;
|
|
END IF;
|
|
END LOOP;
|
|
|
|
RETURN ret;
|
|
|
|
END
|
|
$$
|
|
LANGUAGE 'plpgsql' VOLATILE STRICT;
|
|
--} CreateTopoGeom(toponame,topogeom_type, layer_id, TopoElementArray)
|
|
|
|
--{
|
|
-- CreateTopoGeom(topology_name, topogeom_type, layer_id) - creates the empty topogeom
|
|
-- Availability: 1.1.0
|
|
CREATE OR REPLACE FUNCTION topology.CreateTopoGeom(toponame varchar, tg_type integer, layer_id integer)
|
|
RETURNS topology.TopoGeometry
|
|
AS
|
|
$$
|
|
SELECT topology.CreateTopoGeom($1,$2,$3,'{{0,0}}');
|
|
$$ LANGUAGE 'sql' VOLATILE STRICT;
|
|
--} CreateTopoGeom(toponame, topogeom_type, layer_id)
|
|
|
|
--{
|
|
-- GetTopologyName(topology_id)
|
|
--
|
|
-- TODO: rewrite in SQL, as a wrapper around findTopology
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.GetTopologyName(topoid integer)
|
|
RETURNS varchar
|
|
AS
|
|
$$
|
|
DECLARE
|
|
ret varchar;
|
|
BEGIN
|
|
SELECT name FROM topology.topology into ret
|
|
WHERE id = topoid;
|
|
RETURN ret;
|
|
END
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
--} GetTopologyName(topoid)
|
|
|
|
--{
|
|
-- GetTopologyId(toponame)
|
|
--
|
|
-- TODO: rewrite in SQL, as a wrapper around findTopology
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.GetTopologyId(toponame varchar)
|
|
RETURNS integer
|
|
AS
|
|
$$
|
|
DECLARE
|
|
ret integer;
|
|
BEGIN
|
|
SELECT id INTO ret
|
|
FROM topology.topology WHERE name = toponame;
|
|
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'Topology % does not exist', quote_literal(toponame);
|
|
END IF;
|
|
|
|
RETURN ret;
|
|
END
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
--} GetTopologyId(toponame)
|
|
|
|
--{
|
|
-- GetTopologySRID(toponame)
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.GetTopologySRID(toponame varchar)
|
|
RETURNS integer
|
|
AS $$
|
|
SELECT SRID FROM topology.topology WHERE name = $1;
|
|
$$ LANGUAGE 'sql' STABLE STRICT;
|
|
--} GetTopologySRID(toponame)
|
|
|
|
--{
|
|
-- GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
|
|
-- GetTopoGeomElementArray(TopoGeometry)
|
|
--
|
|
-- Returns a set of element_id,element_type
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.GetTopoGeomElementArray(toponame varchar, layer_id integer, tgid integer)
|
|
RETURNS topology.TopoElementArray
|
|
AS
|
|
$$
|
|
DECLARE
|
|
rec RECORD;
|
|
tg_objs varchar := '{';
|
|
i integer;
|
|
query text;
|
|
BEGIN
|
|
|
|
query = 'SELECT * FROM topology.GetTopoGeomElements('
|
|
|| quote_literal(toponame) || ','
|
|
|| quote_literal(layer_id) || ','
|
|
|| quote_literal(tgid)
|
|
|| ') as obj ORDER BY obj';
|
|
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Query: %', query;
|
|
#endif
|
|
|
|
-- TODO: why not using array_agg here ?
|
|
|
|
i = 1;
|
|
FOR rec IN EXECUTE query
|
|
LOOP
|
|
IF i > 1 THEN
|
|
tg_objs = tg_objs || ',';
|
|
END IF;
|
|
tg_objs = tg_objs || '{'
|
|
|| rec.obj[1] || ',' || rec.obj[2]
|
|
|| '}';
|
|
i = i+1;
|
|
END LOOP;
|
|
|
|
tg_objs = tg_objs || '}';
|
|
|
|
RETURN tg_objs;
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
|
|
CREATE OR REPLACE FUNCTION topology.GetTopoGeomElementArray(tg topology.TopoGeometry)
|
|
RETURNS topology.TopoElementArray
|
|
AS
|
|
$$
|
|
DECLARE
|
|
toponame varchar;
|
|
BEGIN
|
|
toponame = topology.GetTopologyName(tg.topology_id);
|
|
RETURN topology.GetTopoGeomElementArray(toponame, tg.layer_id, tg.id);
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
|
|
--} GetTopoGeomElementArray()
|
|
|
|
--{
|
|
-- GetTopoGeomElements(toponame, layer_id, topogeom_id)
|
|
-- GetTopoGeomElements(TopoGeometry)
|
|
--
|
|
-- Returns a set of element_id,element_type
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.GetTopoGeomElements(toponame varchar, layerid integer, tgid integer)
|
|
RETURNS SETOF topology.TopoElement
|
|
AS
|
|
$$
|
|
DECLARE
|
|
ret topology.TopoElement;
|
|
rec RECORD;
|
|
rec2 RECORD;
|
|
query text;
|
|
query2 text;
|
|
lyr RECORD;
|
|
ok bool;
|
|
topoid INTEGER;
|
|
BEGIN
|
|
|
|
-- Get topology id
|
|
SELECT id INTO topoid
|
|
FROM topology.topology WHERE name = toponame;
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'Topology % does not exist', quote_literal(toponame);
|
|
END IF;
|
|
|
|
-- Get layer info
|
|
ok = false;
|
|
FOR rec IN EXECUTE 'SELECT * FROM topology.layer '
|
|
' WHERE layer_id = $1 AND topology_id = $2'
|
|
USING layerid, topoid
|
|
LOOP
|
|
lyr = rec;
|
|
ok = true;
|
|
END LOOP;
|
|
|
|
IF NOT ok THEN
|
|
RAISE EXCEPTION 'Layer % does not exist', layerid;
|
|
END IF;
|
|
|
|
query = 'SELECT abs(element_id) as element_id, element_type FROM '
|
|
|| quote_ident(toponame) || '.relation WHERE '
|
|
' layer_id = ' || layerid
|
|
|| ' AND topogeo_id = ' || quote_literal(tgid)
|
|
|| ' ORDER BY element_type, element_id';
|
|
|
|
--RAISE NOTICE 'Query: %', query;
|
|
|
|
FOR rec IN EXECUTE query
|
|
LOOP
|
|
IF lyr.level > 0 THEN
|
|
query2 = 'SELECT * from topology.GetTopoGeomElements('
|
|
|| quote_literal(toponame) || ','
|
|
|| rec.element_type
|
|
|| ','
|
|
|| rec.element_id
|
|
|| ') as ret;';
|
|
--RAISE NOTICE 'Query2: %', query2;
|
|
FOR rec2 IN EXECUTE query2
|
|
LOOP
|
|
RETURN NEXT rec2.ret;
|
|
END LOOP;
|
|
ELSE
|
|
ret = '{' || rec.element_id || ',' || rec.element_type || '}';
|
|
RETURN NEXT ret;
|
|
END IF;
|
|
|
|
END LOOP;
|
|
|
|
RETURN;
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
|
|
CREATE OR REPLACE FUNCTION topology.GetTopoGeomElements(tg topology.TopoGeometry)
|
|
RETURNS SETOF topology.TopoElement
|
|
AS
|
|
$$
|
|
DECLARE
|
|
toponame varchar;
|
|
rec RECORD;
|
|
BEGIN
|
|
toponame = topology.GetTopologyName(tg.topology_id);
|
|
FOR rec IN SELECT * FROM topology.GetTopoGeomElements(toponame,
|
|
tg.layer_id,tg.id) as ret
|
|
LOOP
|
|
RETURN NEXT rec.ret;
|
|
END LOOP;
|
|
RETURN;
|
|
END;
|
|
$$
|
|
LANGUAGE 'plpgsql' STABLE STRICT;
|
|
|
|
--} GetTopoGeomElements()
|
|
|
|
--{
|
|
-- Geometry(TopoGeometry)
|
|
--
|
|
-- Construct a Geometry from a TopoGeometry.
|
|
--
|
|
-- }{
|
|
CREATE OR REPLACE FUNCTION topology.Geometry(topogeom topology.TopoGeometry)
|
|
RETURNS Geometry
|
|
AS $BODY$
|
|
DECLARE
|
|
toponame varchar;
|
|
toposrid INT;
|
|
geom geometry;
|
|
elements_count INT;
|
|
rec RECORD;
|
|
plyr RECORD;
|
|
clyr RECORD;
|
|
sql TEXT;
|
|
BEGIN
|
|
|
|
-- Get topology name
|
|
SELECT name, srid FROM topology.topology
|
|
WHERE id = topogeom.topology_id
|
|
INTO toponame, toposrid;
|
|
IF toponame IS NULL THEN
|
|
RAISE EXCEPTION 'Invalid TopoGeometry (unexistent topology id %)', topogeom.topology_id;
|
|
END IF;
|
|
|
|
-- Get layer info
|
|
SELECT * FROM topology.layer
|
|
WHERE topology_id = topogeom.topology_id
|
|
AND layer_id = topogeom.layer_id
|
|
INTO plyr;
|
|
IF plyr IS NULL THEN
|
|
RAISE EXCEPTION 'Could not find TopoGeometry layer % in topology %', topogeom.layer_id, topogeom.topology_id;
|
|
END IF;
|
|
|
|
--
|
|
-- If this feature layer is on any level > 0 we will
|
|
-- compute the topological union of all child features
|
|
-- in fact recursing.
|
|
--
|
|
IF plyr.level > 0 THEN -- {
|
|
|
|
-- Get child layer info
|
|
SELECT * FROM topology.layer WHERE layer_id = plyr.child_id
|
|
AND topology_id = topogeom.topology_id
|
|
INTO clyr;
|
|
IF clyr IS NULL THEN
|
|
RAISE EXCEPTION 'Invalid layer % in topology % (unexistent child layer %)', topogeom.layer_id, topogeom.topology_id, plyr.child_id;
|
|
END IF;
|
|
|
|
sql := 'SELECT st_multi(st_union(topology.Geometry('
|
|
|| quote_ident(clyr.feature_column)
|
|
|| '))) as geom FROM '
|
|
|| quote_ident(clyr.schema_name) || '.'
|
|
|| quote_ident(clyr.table_name)
|
|
|| ', ' || quote_ident(toponame) || '.relation pr'
|
|
' WHERE '
|
|
' pr.topogeo_id = ' || topogeom.id
|
|
|| ' AND '
|
|
' pr.layer_id = ' || topogeom.layer_id
|
|
|| ' AND '
|
|
' id('||quote_ident(clyr.feature_column)
|
|
|| ') = pr.element_id '
|
|
' AND '
|
|
'layer_id('||quote_ident(clyr.feature_column)
|
|
|| ') = pr.element_type ';
|
|
--RAISE DEBUG '%', query;
|
|
EXECUTE sql INTO geom;
|
|
|
|
ELSIF topogeom.type = 3 THEN -- [multi]polygon -- }{
|
|
|
|
sql := format(
|
|
$$
|
|
SELECT
|
|
count(element_id),
|
|
ST_Multi(
|
|
ST_Union(
|
|
topology.ST_GetFaceGeometry(
|
|
%1$L,
|
|
element_id
|
|
)
|
|
)
|
|
) as g
|
|
FROM %1$I.relation
|
|
WHERE topogeo_id = %2$L
|
|
AND layer_id = %3$L
|
|
AND element_type = 3
|
|
$$,
|
|
toponame,
|
|
topogeom.id,
|
|
topogeom.layer_id
|
|
);
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Query: %', sql;
|
|
#endif
|
|
EXECUTE sql INTO elements_count, geom;
|
|
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'TopoGeometry of type 3 (areal)'
|
|
' in topo with srid % defined by % face elements'
|
|
' casted to geometry with srid %',
|
|
toposrid,
|
|
elements_count,
|
|
ST_Srid(geom)
|
|
;
|
|
#endif
|
|
|
|
ELSIF topogeom.type = 2 THEN -- [multi]line -- }{
|
|
|
|
sql := format(
|
|
$$
|
|
SELECT
|
|
st_multi(
|
|
ST_LineMerge(
|
|
ST_Collect(
|
|
CASE
|
|
WHEN r.element_id > 0 THEN
|
|
e.geom
|
|
ELSE
|
|
ST_Reverse(e.geom)
|
|
END
|
|
)
|
|
)
|
|
) as g
|
|
FROM %1$I.edge e, %1$I.relation r
|
|
WHERE r.topogeo_id = id($1)
|
|
AND r.layer_id = layer_id($1)
|
|
AND r.element_type = 2
|
|
AND abs(r.element_id) = e.edge_id
|
|
$$,
|
|
toponame
|
|
);
|
|
EXECUTE sql USING topogeom INTO geom;
|
|
|
|
ELSIF topogeom.type = 1 THEN -- [multi]point -- }{
|
|
|
|
sql :=
|
|
'SELECT st_multi(st_union(n.geom)) as g FROM '
|
|
|| quote_ident(toponame) || '.node n, '
|
|
|| quote_ident(toponame) || '.relation r '
|
|
' WHERE r.topogeo_id = ' || topogeom.id
|
|
|| ' AND r.layer_id = ' || topogeom.layer_id
|
|
|| ' AND r.element_type = 1 '
|
|
' AND r.element_id = n.node_id';
|
|
EXECUTE sql INTO geom;
|
|
|
|
ELSIF topogeom.type = 4 THEN -- mixed collection -- }{
|
|
|
|
sql := 'WITH areas AS ( SELECT ST_Union('
|
|
'topology.ST_GetFaceGeometry('
|
|
|| quote_literal(toponame) || ','
|
|
|| 'element_id)) as g FROM '
|
|
|| quote_ident(toponame)
|
|
|| '.relation WHERE topogeo_id = '
|
|
|| topogeom.id || ' AND layer_id = '
|
|
|| topogeom.layer_id || ' AND element_type = 3), '
|
|
'lines AS ( SELECT ST_LineMerge(ST_Collect(e.geom)) as g FROM '
|
|
|| quote_ident(toponame) || '.edge e, '
|
|
|| quote_ident(toponame) || '.relation r '
|
|
' WHERE r.topogeo_id = ' || topogeom.id
|
|
|| ' AND r.layer_id = ' || topogeom.layer_id
|
|
|| ' AND r.element_type = 2 '
|
|
' AND abs(r.element_id) = e.edge_id ), '
|
|
' points as ( SELECT st_union(n.geom) as g FROM '
|
|
|| quote_ident(toponame) || '.node n, '
|
|
|| quote_ident(toponame) || '.relation r '
|
|
' WHERE r.topogeo_id = ' || topogeom.id
|
|
|| ' AND r.layer_id = ' || topogeom.layer_id
|
|
|| ' AND r.element_type = 1 '
|
|
' AND r.element_id = n.node_id ), '
|
|
' un as ( SELECT g FROM areas UNION ALL SELECT g FROM lines '
|
|
' UNION ALL SELECT g FROM points ) '
|
|
'SELECT ST_Multi(ST_Collect(g)) FROM un';
|
|
EXECUTE sql INTO geom;
|
|
|
|
ELSE -- }{
|
|
|
|
RAISE EXCEPTION 'Invalid TopoGeometries (unknown type %)', topogeom.type;
|
|
|
|
END IF; -- }
|
|
|
|
IF geom IS NULL THEN
|
|
IF topogeom.type = 3 THEN -- [multi]polygon
|
|
geom := 'MULTIPOLYGON EMPTY';
|
|
ELSIF topogeom.type = 2 THEN -- [multi]line
|
|
geom := 'MULTILINESTRING EMPTY';
|
|
ELSIF topogeom.type = 1 THEN -- [multi]point
|
|
geom := 'MULTIPOINT EMPTY';
|
|
ELSE
|
|
geom := 'GEOMETRYCOLLECTION EMPTY';
|
|
END IF;
|
|
geom := ST_SetSRID(geom, toposrid);
|
|
END IF;
|
|
|
|
RETURN geom;
|
|
END
|
|
$BODY$
|
|
LANGUAGE 'plpgsql' VOLATILE STRICT;
|
|
--} Geometry(TopoGeometry)
|
|
|
|
-- 7.3+ explicit cast
|
|
CREATE CAST (topology.TopoGeometry AS Geometry) WITH FUNCTION topology.Geometry(topology.TopoGeometry) AS IMPLICIT;
|
|
|
|
--{
|
|
-- TopoElement(TopoGeometry)
|
|
--
|
|
-- Construct a TopoElement from a TopoGeometry.
|
|
--
|
|
-- Availability: 3.4.0
|
|
-- }{
|
|
CREATE OR REPLACE FUNCTION topology.TopoElement(topo topology.TopoGeometry)
|
|
RETURNS topology.TopoElement
|
|
LANGUAGE sql
|
|
COST 1
|
|
IMMUTABLE PARALLEL SAFE
|
|
AS $$SELECT ARRAY[topo.id,topo.layer_id]::topology.topoelement;$$;
|
|
--} TopoElement(TopoGeometry)
|
|
|
|
--{
|
|
-- TopoGeometry(int[])
|
|
--
|
|
-- Cast a TopoGeometry to a TopoElement.
|
|
--
|
|
-- Availability: 3.4.0
|
|
-- }{
|
|
CREATE CAST (topogeometry AS int[]) WITH FUNCTION TopoElement(topogeometry) AS ASSIGNMENT;
|
|
--} TopoGeometry(int[])
|
|
|
|
--{
|
|
-- DropTopology(name)
|
|
--
|
|
-- Drops a topology schema getting rid of every dependent object.
|
|
--
|
|
-- Availability: 1.1.0
|
|
--
|
|
CREATE OR REPLACE FUNCTION topology.DropTopology(atopology varchar)
|
|
RETURNS text
|
|
AS
|
|
$$
|
|
DECLARE
|
|
topoid integer;
|
|
rec RECORD;
|
|
sql TEXT;
|
|
toposchema REGNAMESPACE;
|
|
deferred_constraints TEXT[];
|
|
BEGIN
|
|
-- Get topology id
|
|
SELECT id INTO topoid
|
|
FROM topology.topology WHERE name = atopology;
|
|
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'Topology % does not exist', quote_literal(atopology);
|
|
END IF;
|
|
|
|
RAISE NOTICE 'Dropping all layers from topology % (%)',
|
|
quote_literal(atopology), topoid;
|
|
|
|
-- Drop all layers in the topology
|
|
sql := 'SELECT * FROM topology.layer WHERE topology_id = $1';
|
|
FOR rec IN EXECUTE sql USING topoid
|
|
LOOP
|
|
|
|
sql := format(
|
|
'SELECT topology.DropTopoGeometryColumn(%L, %L, %L)',
|
|
rec.schema_name, rec.table_name, rec.feature_column
|
|
);
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Executing: %', sql;
|
|
#endif
|
|
EXECUTE sql;
|
|
END LOOP;
|
|
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Deleting record from topology.topology';
|
|
#endif
|
|
|
|
-- Delete record from topology.topology
|
|
sql := 'DELETE FROM topology.topology WHERE id = $1';
|
|
EXECUTE sql USING topoid;
|
|
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Query pg_namespace';
|
|
#endif
|
|
|
|
-- Drop the schema (if it exists)
|
|
SELECT oid FROM pg_namespace WHERE text(nspname) = atopology
|
|
INTO toposchema;
|
|
|
|
IF toposchema IS NOT NULL THEN --{
|
|
|
|
-- Give immediate execution to pending constraints
|
|
-- in the topology schema.
|
|
--
|
|
-- See https://trac.osgeo.org/postgis/ticket/5115
|
|
SELECT array_agg(format('%I.%I', atopology, conname))
|
|
FROM pg_constraint c
|
|
WHERE connamespace = toposchema AND condeferred
|
|
INTO deferred_constraints;
|
|
|
|
IF deferred_constraints IS NOT NULL THEN --{
|
|
sql := format(
|
|
'SET constraints %s IMMEDIATE',
|
|
array_to_string(deferred_constraints, ',')
|
|
);
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Executing: %', sql;
|
|
#endif
|
|
EXECUTE sql;
|
|
END IF; --}
|
|
|
|
sql := format('DROP SCHEMA %I CASCADE', atopology);
|
|
#ifdef POSTGIS_TOPOLOGY_DEBUG
|
|
RAISE DEBUG 'Executing: %', sql;
|
|
#endif
|
|
EXECUTE sql;
|
|
END IF; --}
|
|
|
|
RETURN format('Topology %L dropped', atopology);
|
|
END
|
|
$$
|
|
LANGUAGE 'plpgsql' VOLATILE STRICT;
|
|
--} DropTopology
|
|
|
|
-- Spatial predicates
|
|
#include "sql/predicates.sql.in"
|
|
|
|
-- Querying
|
|
#include "sql/query/getnodebypoint.sql.in"
|
|
#include "sql/query/getedgebypoint.sql.in"
|
|
#include "sql/query/getfacebypoint.sql.in"
|
|
#include "sql/query/GetFaceContainingPoint.sql.in"
|
|
|
|
-- Populating
|
|
#include "sql/populate.sql.in"
|
|
#include "sql/polygonize.sql.in"
|
|
|
|
-- TopoElement
|
|
#include "sql/topoelement/topoelement_agg.sql.in"
|
|
|
|
-- TopoGeometry
|
|
#include "sql/topogeometry/type.sql.in"
|
|
#include "sql/topogeometry/srid.sql.in"
|
|
#include "sql/topogeometry/cleartopogeom.sql.in"
|
|
#include "sql/topogeometry/simplify.sql.in"
|
|
#include "sql/topogeometry/totopogeom.sql.in"
|
|
#include "sql/topogeometry/topogeom_edit.sql.in"
|
|
|
|
-- Exports
|
|
#include "sql/export/gml.sql.in"
|
|
#include "sql/export/TopoJSON.sql.in"
|
|
|
|
--=} POSTGIS-SPECIFIC block
|
|
|
|
-- SQL/MM block
|
|
#include "sql/sqlmm.sql.in"
|
|
|
|
-- The following files needs getfaceedges_returntype, defined in sqlmm.sql
|
|
#include "sql/query/GetRingEdges.sql.in"
|
|
#include "sql/query/GetNodeEdges.sql.in"
|
|
|
|
--general management --
|
|
#include "sql/manage/ManageHelper.sql.in"
|
|
#include "sql/manage/AddTopoGeometryColumn.sql.in"
|
|
#include "sql/manage/RenameTopoGeometryColumn.sql.in"
|
|
#include "sql/manage/CreateTopology.sql.in"
|
|
#include "sql/manage/TopologySummary.sql.in"
|
|
#include "sql/manage/CopyTopology.sql.in"
|
|
#include "sql/manage/FindTopology.sql.in"
|
|
#include "sql/manage/FindLayer.sql.in"
|
|
#include "sql/manage/RenameTopology.sql.in"
|
|
#include "sql/manage/ValidateTopology.sql.in"
|
|
#include "sql/manage/ValidateTopologyRelation.sql.in"
|
|
|
|
-- Cleanup functions
|
|
#include "sql/cleanup/RemoveUnusedPrimitives.sql.in"
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION topology.postgis_topology_scripts_installed() RETURNS text
|
|
AS _POSTGIS_SQL_SELECT_POSTGIS_SCRIPTS_VERSION
|
|
LANGUAGE 'sql' IMMUTABLE;
|
|
|
|
-- Make sure topology is in database search path --
|
|
SELECT topology.AddToSearchPath('topology');
|
|
|
|
-- Make metadata tables publically visible
|
|
-- See https://trac.osgeo.org/postgis/ticket/4575
|
|
GRANT SELECT ON topology.topology TO PUBLIC;
|
|
GRANT SELECT ON topology.layer TO PUBLIC;
|
|
-- See https://trac.osgeo.org/postgis/ticket/5124
|
|
GRANT USAGE ON SCHEMA topology TO PUBLIC;
|
|
|
|
COMMIT;
|