[postgis-commits] svn - r2779 - trunk/topology
postgis-commits at postgis.refractions.net
postgis-commits at postgis.refractions.net
Thu May 22 07:31:03 PDT 2008
Author: mcayland
Date: 2008-05-22 07:31:02 -0700 (Thu, 22 May 2008)
New Revision: 2779
Added:
trunk/topology/topology.sql.in.c
Removed:
trunk/topology/topology.sql.in
Modified:
trunk/topology/Makefile
Log:
Update topology/ directory to use the new PGXS build system.
Modified: trunk/topology/Makefile
===================================================================
--- trunk/topology/Makefile 2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/Makefile 2008-05-22 14:31:02 UTC (rev 2779)
@@ -1,27 +1,28 @@
-# Configuration Directives
-include ../Makefile.config
-include ../Version.config
+#
+# PostGIS PGXS build system
+#
-#---------------------------------------------------------------
-# Postgis version and build date
-#---------------------------------------------------------------
+# Files to be copied to the contrib/ directory
+DATA_built=topology.sql
-all: topology.sql
+# SQL objects (files requiring C pre-processing)
+SQL_OBJS=topology.sql.in
-topology.sql: topology.sql.in
- cpp -P -traditional-cpp -DUSE_VERSION=$(USE_VERSION) $< | grep -v '^#' > $@
+# Extra files to remove during 'make clean'
+EXTRA_CLEAN=$(SQL_OBJS)
-install: all install-topology-scripts
+# PGXS information
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
-uninstall: uninstall-topology-scripts
-install-topology-scripts:
- @mkdir -p $(DESTDIR)$(datadir)
- $(INSTALL_DATA) topology.sql $(DESTDIR)$(datadir)/topology.sql
+# Unfortunately we have to copy this from the PGXS Makefile as it only gets picked up
+# if MODULE_big is defined
+%.sql: %.sql.in
+ sed 's,MODULE_PATHNAME,$$libdir/$*,g' $< >$@
-uninstall-topology-scripts:
- rm -f $(DESTDIR)$(datadir)/postgis/topology.sql
+# Generate any .sql.in files from .sql.in.c files by running them through the C pre-processor
+$(SQL_OBJS): %.in: %.in.c
+ $(CPP) -traditional-cpp $< | grep -v '^#' > $@
-clean distclean:
- rm -f topology.sql
-
Deleted: trunk/topology/topology.sql.in
===================================================================
--- trunk/topology/topology.sql.in 2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/topology.sql.in 2008-05-22 14:31:02 UTC (rev 2779)
@@ -1,3827 +0,0 @@
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
--- $Id$
---
--- PostGIS - Spatial Types for PostgreSQL
--- http://postgis.refractions.net
--- Copyright 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 at refractions.net>
---
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
--- 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 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
--- Being working on. TODO: continue implementation
---
--- ST_AddIsoNode
--- Complete
---
--- ST_RemoveIsoNode
--- Complete
---
--- ST_MoveIsoNode
--- Complete
---
--- ST_AddIsoEdge
--- Complete
---
--- ST_RemoveIsoEdge
--- Complete, exceptions untested
---
--- ST_ChangeEdgeGeom
--- Complete
---
--- ST_NewEdgesSplit
--- Complete
--- this also updates the Relation table
--- TODO: add entries to the History table ?
---
--- ST_ModEdgesSplit
--- Complete
--- this also updates the Relation table
--- TODO: add entries to the History table ?
---
--- ST_AddEdgeNewFaces
--- Being working on. TODO: continue
---
--- ST_GetFaceEdges
--- Unimplemented (C seems appropriate)
---
--- ST_GetFaceGeometry
--- Implemented using polygonize()
---
---
-
-
-#define CREATEFUNCTION CREATE OR REPLACE FUNCTION
-
-#if USE_VERSION > 72
-# define _IMMUTABLE_STRICT IMMUTABLE STRICT
-# define _IMMUTABLE IMMUTABLE
-# define _STABLE_STRICT STABLE STRICT
-# define _STABLE STABLE
-# define _VOLATILE_STRICT VOLATILE STRICT
-# define _VOLATILE VOLATILE
-# define _STRICT STRICT
-#else
-# define _IMMUTABLE_STRICT with(iscachable,isstrict)
-# define _IMMUTABLE with(iscachable)
-# define _STABLE_STRICT with(isstrict)
-# define _STABLE
-# define _VOLATILE_STRICT with(isstrict)
-# define _VOLATILE
-# define _STRICT with(isstrict)
-#endif
-
-DROP SCHEMA topology CASCADE;
-
-BEGIN;
-
-CREATE SCHEMA topology;
-
---={ ----------------------------------------------------------------
--- 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
-);
-
---{ LayerTrigger()
---
--- Layer integrity trigger
---
-CREATEFUNCTION 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 * FROM pg_namespace
- WHERE text(nspname) = toponame
- LOOP
- query = ''SELECT * ''
- || '' FROM '' || quote_ident(toponame)
- || ''.relation ''
- || '' WHERE topogeo_id = '' || OLD.topology_id
- || '' AND 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 VARCHAR 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.
-
---
--- Type returned by ST_GetFaceEdges
---
-CREATE TYPE topology.GetFaceEdges_ReturnType AS (
- sequence integer,
- edge integer
-);
-
---
--- Type returned by ValidateTopology
---
-CREATE TYPE topology.ValidateTopology_ReturnType AS (
- error varchar,
- id1 integer,
- id2 integer
-);
-
---
--- TopoGeometry type
---
-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
---
-CREATE DOMAIN topology.TopoElement AS integer[]
- CONSTRAINT DIMENSIONS CHECK (
- array_upper(VALUE, 2) IS NULL
- AND array_upper(VALUE, 1) = 2
- );
---
--- TopoElementArray domain
---
-CREATE DOMAIN topology.TopoElementArray AS integer[][]
- CONSTRAINT DIMENSIONS CHECK (
- array_upper(VALUE, 2) IS NOT NULL
- AND array_upper(VALUE, 2) = 2
- AND array_upper(VALUE, 3) IS NULL
- );
-
---
--- TopoGeomElementArray domain
---
-CREATE DOMAIN topology.TopoGeomElementArray AS integer[][]
- CONSTRAINT DIMENSIONS CHECK (
- array_upper(VALUE, 2) IS NOT NULL
- AND array_upper(VALUE, 2) = 2
- AND array_upper(VALUE, 3) IS NULL
- );
-
---{ RelationTrigger()
---
--- Relation integrity trigger
---
-CREATEFUNCTION 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()
-
---{
--- AddTopoGeometryColumn(toponame, schema, table, colum, type, [child])
---
--- Add a TopoGeometry column to a table, making it a topology layer.
--- Returns created layer id.
---
---
-CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar, integer)
- RETURNS integer
-AS '
-DECLARE
- toponame alias for $1;
- schema alias for $2;
- table alias for $3;
- col alias for $4;
- ltype alias for $5;
- child alias for $6;
- intltype integer;
- level integer;
- topoid integer;
- rec RECORD;
- layer_id integer;
- query text;
-BEGIN
-
- -- Get topology id
- SELECT id FROM topology.topology into topoid
- WHERE name = toponame;
-
- IF topoid IS NULL THEN
- RAISE EXCEPTION ''Topology % does not exist'', toponame;
- END IF;
-
- --
- -- Get new layer id from sequence
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- quote_ident(toponame) || ''.layer_id_seq'''')''
- LOOP
- layer_id = rec.nextval;
- END LOOP;
-
- IF ltype = ''POINT'' THEN
- intltype = 1;
- ELSIF ltype = ''LINE'' THEN
- intltype = 2;
- ELSIF ltype = ''POLYGON'' THEN
- intltype = 3;
- ELSIF ltype = ''COLLECTION'' THEN
- intltype = 4;
- ELSE
- RAISE EXCEPTION ''Layer type must be one of POINT,LINE,POLYGON,COLLECTION'';
- END IF;
-
- --
- -- See if child id exists and extract its level
- --
- IF child IS NULL THEN
- EXECUTE ''INSERT INTO ''
- || ''topology.layer(topology_id, ''
- || ''layer_id, schema_name, ''
- || ''table_name, feature_column, feature_type) ''
- || ''VALUES (''
- || topoid || '',''
- || layer_id || '',''
- || quote_literal(schema) || '',''
- || quote_literal(table) || '',''
- || quote_literal(col) || '',''
- || intltype || '');'';
- ELSE
- FOR rec IN EXECUTE ''SELECT level FROM topology.layer''
- || '' WHERE layer_id = '' || child
- LOOP
- level = rec.level + 1;
- END LOOP;
-
- EXECUTE ''INSERT INTO ''
- || ''topology.layer(topology_id, ''
- || ''layer_id, level, child_id, schema_name, ''
- || ''table_name, feature_column, feature_type) ''
- || ''VALUES (''
- || topoid || '',''
- || layer_id || '','' || level || '',''
- || child || '',''
- || quote_literal(schema) || '',''
- || quote_literal(table) || '',''
- || quote_literal(col) || '',''
- || intltype || '');'';
- END IF;
-
-
- --
- -- Create a sequence for TopoGeometries in this new layer
- --
- EXECUTE ''CREATE SEQUENCE '' || quote_ident(toponame)
- || ''.topogeo_s_'' || layer_id;
-
- --
- -- Add new TopoGeometry column in schema.table
- --
- EXECUTE ''ALTER TABLE '' || quote_ident(schema)
- || ''.'' || quote_ident(table)
- || '' ADD COLUMN '' || quote_ident(col)
- || '' topology.TopoGeometry;'';
-
- --
- -- Add constraints on TopoGeom column
- --
- EXECUTE ''ALTER TABLE '' || quote_ident(schema)
- || ''.'' || quote_ident(table)
- || '' ADD CONSTRAINT check_topogeom CHECK (''
- || ''topology_id('' || quote_ident(col) || '') = '' || topoid
- || '' AND ''
- || ''layer_id('' || quote_ident(col) || '') = '' || layer_id
- || '' AND ''
- || ''type('' || quote_ident(col) || '') = '' || intltype
- || '');'';
-
- --
- -- Add dependency of the feature column on the topology schema
- --
- query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
- || ''fcat.oid, fobj.oid, fsub.attnum, tcat.oid, ''
- || ''tobj.oid, 0, ''''n'''' ''
- || ''FROM pg_class fcat, pg_namespace fnsp, ''
- || '' pg_class fobj, pg_attribute fsub, ''
- || '' pg_class tcat, pg_namespace tobj ''
- || '' WHERE fcat.relname = ''''pg_class'''' ''
- || '' AND fnsp.nspname = '' || quote_literal(schema)
- || '' AND fobj.relnamespace = fnsp.oid ''
- || '' AND fobj.relname = '' || quote_literal(table)
- || '' AND fsub.attrelid = fobj.oid ''
- || '' AND fsub.attname = '' || quote_literal(col)
- || '' AND tcat.relname = ''''pg_namespace'''' ''
- || '' AND tobj.nspname = '' || quote_literal(toponame);
-
---
--- The only reason to add this dependency is to avoid
--- simple drop of a feature column. Still, drop cascade
--- will remove both the feature column and the sequence
--- corrupting the topology anyway ...
---
-#if 0
- --
- -- Add dependency of the topogeom sequence on the feature column
- -- This is a dirty hack ...
- --
- query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
- || ''scat.oid, sobj.oid, 0, fcat.oid, ''
- || ''fobj.oid, fsub.attnum, ''''n'''' ''
- || ''FROM pg_class fcat, pg_namespace fnsp, ''
- || '' pg_class fobj, pg_attribute fsub, ''
- || '' pg_class scat, pg_class sobj, ''
- || '' pg_namespace snsp ''
- || '' WHERE fcat.relname = ''''pg_class'''' ''
- || '' AND fnsp.nspname = '' || quote_literal(schema)
- || '' AND fobj.relnamespace = fnsp.oid ''
- || '' AND fobj.relname = '' || quote_literal(table)
- || '' AND fsub.attrelid = fobj.oid ''
- || '' AND fsub.attname = '' || quote_literal(col)
- || '' AND scat.relname = ''''pg_class'''' ''
- || '' AND snsp.nspname = '' || quote_literal(toponame)
- || '' AND sobj.relnamespace = snsp.oid ''
- || '' AND sobj.relname = ''
- || '' ''''topogeo_s_'' || layer_id || '''''' '';
-
- RAISE NOTICE ''%'', query;
- EXECUTE query;
-#endif
-
- RETURN layer_id;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE;
-
-CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar)
- RETURNS integer
-AS '
- SELECT topology.AddTopoGeometryColumn($1, $2, $3, $4, $5, NULL);
-'
-LANGUAGE 'sql' _VOLATILE;
-
---
---} AddTopoGeometryColumn
-
---{
--- DropTopoGeometryColumn(schema, table, colum)
---
--- Drop a TopoGeometry column, unregister the associated layer,
--- cleanup the relation table.
---
---
-CREATEFUNCTION topology.DropTopoGeometryColumn(varchar, varchar, varchar)
- RETURNS text
-AS '
-DECLARE
- schema alias for $1;
- table alias for $2;
- col alias for $3;
- rec RECORD;
- lyrinfo RECORD;
- ok BOOL;
- result text;
-BEGIN
-
- -- Get layer and topology info
- ok = false;
- FOR rec IN EXECUTE ''SELECT t.name as toponame, l.* FROM ''
- || ''topology.topology t, topology.layer l ''
- || '' WHERE l.schema_name = '' || quote_literal(schema)
- || '' AND l.table_name = '' || quote_literal(table)
- || '' AND l.feature_column = '' || quote_literal(col)
- LOOP
- ok = true;
- lyrinfo = rec;
- END LOOP;
-
- -- Layer not found
- IF NOT ok THEN
- RAISE EXCEPTION ''No layer registered on %.%.%'',
- schema,table,col;
- END IF;
-
- -- Clean up the topology schema
- FOR rec IN SELECT * FROM pg_namespace
- WHERE text(nspname) = lyrinfo.toponame
- LOOP
- -- Cleanup the relation table
- EXECUTE ''DELETE FROM '' || quote_ident(lyrinfo.toponame)
- || ''.relation ''
- || '' WHERE ''
- || ''layer_id = '' || lyrinfo.layer_id;
-
- -- Drop the sequence for topogeoms in this layer
- EXECUTE ''DROP SEQUENCE '' || quote_ident(lyrinfo.toponame)
- || ''.topogeo_s_'' || lyrinfo.layer_id;
-
- END LOOP;
-
- 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) = table
- AND a.attrelid = c.oid
- AND text(a.attname) = col
- LOOP
- ok = true;
- EXIT;
- END LOOP;
-
-
- IF ok THEN
- -- Set feature column to NULL to bypass referential integrity
- -- checks
- EXECUTE ''UPDATE '' || quote_ident(schema) || ''.''
- || quote_ident(table)
- || '' SET '' || quote_ident(col)
- || '' = NULL'';
- END IF;
-
- -- Delete the layer record
- EXECUTE ''DELETE FROM topology.layer ''
- || '' WHERE topology_id = '' || lyrinfo.topology_id
- || '' AND layer_id = '' || lyrinfo.layer_id;
-
- IF ok THEN
- -- Drop the layer column
- EXECUTE ''ALTER TABLE '' || quote_ident(schema) || ''.''
- || quote_ident(table)
- || '' DROP '' || quote_ident(col)
- || '' cascade'';
- END IF;
-
- result = ''Layer '' || lyrinfo.layer_id || '' (''
- || schema || ''.'' || table || ''.'' || col
- || '') dropped'';
-
- RETURN result;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---
---} DropTopoGeometryColumn
-
-
---{
--- 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).
---
--- Return a topology.TopoGeometry object.
---
-CREATEFUNCTION topology.CreateTopoGeom(varchar, integer, integer, topology.TopoElementArray)
- RETURNS topology.TopoGeometry
-AS '
-DECLARE
- toponame alias for $1;
- tg_type alias for $2; -- 1:[multi]point
- -- 2:[multi]line
- -- 3:[multi]poly
- -- 4:collection
- layer_id alias for $3;
- tg_objs alias for $4;
- 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'';
- END IF;
-
- -- Get topology id into return TopoGeometry
- SELECT id FROM topology.topology into ret.topology_id
- WHERE name = toponame;
-
- --
- -- 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('''''' ||
- 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];
- 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 || '');'';
-
- 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, TopoObject[])
-
---{
--- GetTopologyName(topology_id)
---
-CREATEFUNCTION topology.GetTopologyName(integer)
- RETURNS varchar
-AS
-'
-DECLARE
- topoid alias for $1;
- ret varchar;
-BEGIN
- SELECT name FROM topology.topology into ret
- WHERE id = topoid;
- RETURN ret;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} GetTopologyName(topoid)
-
---{
--- GetTopologyId(toponame)
---
-CREATEFUNCTION topology.GetTopologyId(varchar)
- RETURNS integer
-AS
-'
-DECLARE
- toponame alias for $1;
- ret integer;
-BEGIN
- SELECT id FROM topology.topology into ret
- WHERE name = toponame;
- RETURN ret;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} GetTopologyId(toponame)
-
-
-
---{
--- GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
--- GetTopoGeomElementArray(TopoGeometry)
---
--- Returns a set of element_id,element_type
---
-CREATEFUNCTION topology.GetTopoGeomElementArray(varchar, integer, integer)
- RETURNS topology.TopoElementArray
-AS
-'
-DECLARE
- toponame alias for $1;
- layerid alias for $2;
- tgid alias for $3;
- rec RECORD;
- tg_objs varchar := ''{'';
- i integer;
- query text;
-BEGIN
-
- query = ''SELECT * FROM topology.GetTopoGeomElements(''
- || quote_literal(toponame) || '',''
- || quote_literal(layerid) || '',''
- || quote_literal(tgid)
- || '') as obj ORDER BY obj'';
-
- RAISE NOTICE ''Query: %'', query;
-
- 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' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.GetTopoGeomElementArray(topology.TopoGeometry)
- RETURNS topology.TopoElementArray
-AS
-'
-DECLARE
- tg alias for $1;
- toponame varchar;
- ret topology.TopoElementArray;
-BEGIN
- toponame = topology.GetTopologyName(tg.topology_id);
- ret = topology.GetTopoGeomElementArray(toponame,tg.layer_id,tg.id);
- RETURN ret;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
---} GetTopoGeomElementArray()
-
---{
--- GetTopoGeomElements(toponame, layer_id, topogeom_id)
--- GetTopoGeomElements(TopoGeometry)
---
--- Returns a set of element_id,element_type
---
-CREATEFUNCTION topology.GetTopoGeomElements(varchar, integer, integer)
- RETURNS SETOF topology.TopoElement
-AS
-'
-DECLARE
- toponame alias for $1;
- layerid alias for $2;
- tgid alias for $3;
- ret topology.TopoElement;
- rec RECORD;
- rec2 RECORD;
- query text;
- query2 text;
- lyr RECORD;
- ok bool;
-BEGIN
-
- -- Get layer info
- ok = false;
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || '' topology.layer ''
- || '' WHERE layer_id = '' || layerid
- 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' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.GetTopoGeomElements(topology.TopoGeometry)
- RETURNS SETOF topology.TopoElement
-AS
-'
-DECLARE
- tg alias for $1;
- 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' _VOLATILE_STRICT;
-
---} GetTopoGeomElements()
-
---{
--- Geometry(TopoGeometry)
---
--- Construct a Geometry from a TopoGeometry.
---
---
-CREATE OR REPLACE FUNCTION topology.Geometry(topology.TopoGeometry)
- RETURNS Geometry
-AS '
-DECLARE
- topogeom alias for $1;
- toponame varchar;
- geom geometry;
- rec RECORD;
- plyr RECORD;
- clyr RECORD;
- query text;
- ok BOOL;
-BEGIN
- -- Get topology name
- SELECT name FROM topology.topology into toponame
- WHERE id = topogeom.topology_id;
-
- -- Get layer info
- ok = false;
- FOR rec IN EXECUTE ''SELECT * FROM topology.layer ''
- || '' WHERE topology_id = '' || topogeom.topology_id
- || '' AND layer_id = '' || topogeom.layer_id
- LOOP
- ok = true;
- plyr = rec;
- END LOOP;
-
- IF NOT ok 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
- FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
- || '' WHERE layer_id = '' || plyr.child_id
- LOOP
- clyr = rec;
- END LOOP;
-
- query = ''SELECT geomunion(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 NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN rec.geom;
- END LOOP;
-
- END IF;
-
-
- IF topogeom.type = 3 THEN -- [multi]polygon
- FOR rec IN EXECUTE ''SELECT geomunion(''
- || ''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 ''
- LOOP
- geom := rec.g;
- END LOOP;
-
- ELSIF topogeom.type = 2 THEN -- [multi]line
- FOR rec IN EXECUTE ''SELECT linemerge(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''
- LOOP
- geom := rec.g;
- END LOOP;
-
- ELSIF topogeom.type = 1 THEN -- [multi]point
- FOR rec IN EXECUTE ''SELECT geomunion(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''
- LOOP
- geom := rec.g;
- END LOOP;
-
- ELSE
- RAISE NOTICE ''Geometry from TopoGeometry does not support TopoGeometries of type % so far'', topogeom.type;
- geom := ''GEOMETRYCOLLECTION EMPTY'';
- END IF;
-
- RETURN geom;
-END
-'
-LANGUAGE 'plpgsql' VOLATILE STRICT;
---} Geometry(TopoGeometry)
-
---{
--- ValidateTopology(toponame)
---
--- Return a Set of ValidateTopology_ReturnType containing
--- informations on all topology inconsistencies
---
-CREATEFUNCTION topology.ValidateTopology(varchar)
- RETURNS setof topology.ValidateTopology_ReturnType
-AS
-'
-DECLARE
- toponame alias for $1;
- retrec topology.ValidateTopology_ReturnType;
- rec RECORD;
- i integer;
-BEGIN
-
- -- Check for coincident nodes
- FOR rec IN EXECUTE ''SELECT a.node_id as id1, b.node_id as id2 FROM ''
- || quote_ident(toponame) || ''.node a, ''
- || quote_ident(toponame) || ''.node b ''
- || ''WHERE a.node_id < b.node_id AND a.geom && b.geom''
- LOOP
- retrec.error = ''coincident nodes'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for edge crossed nodes
- FOR rec IN EXECUTE ''SELECT n.node_id as id1, e.edge_id as id2 FROM ''
- || quote_ident(toponame) || ''.node n, ''
- || quote_ident(toponame) || ''.edge e ''
- || ''WHERE e.start_node != n.node_id ''
- || ''AND e.end_node != n.node_id ''
- || ''AND n.geom && e.geom ''
- || ''AND intersects(n.geom, e.geom)''
- LOOP
- retrec.error = ''edge crosses node'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for non-simple edges
- FOR rec IN EXECUTE ''SELECT e.edge_id as id1 FROM ''
- || quote_ident(toponame) || ''.edge e ''
- || ''WHERE not issimple(e.geom)''
- LOOP
- retrec.error = ''edge not simple'';
- retrec.id1 = rec.id1;
- retrec.id2 = NULL;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for edge crossing
- FOR rec IN EXECUTE ''SELECT e1.edge_id as id1, e2.edge_id as id2 FROM ''
- || quote_ident(toponame) || ''.edge e1, ''
- || quote_ident(toponame) || ''.edge e2 ''
- || ''WHERE e1.edge_id < e2.edge_id ''
- || ''AND e1.geom && e2.geom ''
- || ''AND crosses(e1.geom, e2.geom)''
- LOOP
- retrec.error = ''edge crosses edge'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for edge start_node geometry mis-match
- FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
- || quote_ident(toponame) || ''.edge e, ''
- || quote_ident(toponame) || ''.node n ''
- || ''WHERE e.start_node = n.node_id ''
- || ''AND NOT Equals(StartPoint(e.geom), n.geom)''
- LOOP
- retrec.error = ''edge start node geometry mis-match'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for edge end_node geometry mis-match
- FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
- || quote_ident(toponame) || ''.edge e, ''
- || quote_ident(toponame) || ''.node n ''
- || ''WHERE e.end_node = n.node_id ''
- || ''AND NOT Equals(EndPoint(e.geom), n.geom)''
- LOOP
- retrec.error = ''edge end node geometry mis-match'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for faces w/out edges
- FOR rec IN EXECUTE ''SELECT face_id as id1 FROM ''
- || quote_ident(toponame) || ''.face ''
- || ''EXCEPT ( SELECT left_face FROM ''
- || quote_ident(toponame) || ''.edge ''
- || '' UNION SELECT right_face FROM ''
- || quote_ident(toponame) || ''.edge ''
- || '')''
- LOOP
- retrec.error = ''face without edges'';
- retrec.id1 = rec.id1;
- retrec.id2 = NULL;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for overlapping faces
- -- TODO: this check requires some thinking, do we really
- -- have to construct face geometry to detect overlap with
- -- another face ??
- FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
- || quote_ident(toponame) || ''.face f1, ''
- || quote_ident(toponame) || ''.face f2 ''
- || ''WHERE f1.face_id > 0 AND f1.face_id < f2.face_id AND ''
- || '' Overlaps(topology.ST_GetFaceGeometry(''
- || quote_literal(toponame) || '', f1.face_id), ''
- || '' topology.ST_GetFaceGeometry(''
- || quote_literal(toponame) || '', f2.face_id))''
- LOOP
- retrec.error = ''face overlaps face'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
- -- Check for face within face
- -- TODO: this check requires some thinking, do we really
- -- have to construct face geometry to detect within condition
- -- another face ??
- FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
- || quote_ident(toponame) || ''.face f1, ''
- || quote_ident(toponame) || ''.face f2 ''
- || ''WHERE f1.face_id != 0 AND f2.face_id != 0 ''
- || ''AND f1.face_id != f2.face_id ''
- || ''AND Within(topology.ST_GetFaceGeometry(''
- || quote_literal(toponame) || '', f1.face_id), ''
- || '' topology.ST_GetFaceGeometry(''
- || quote_literal(toponame) || '', f2.face_id))''
- LOOP
- retrec.error = ''face within face'';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
- END LOOP;
-
-#if 0
- -- Check SRID consistency
- FOR rec in EXECUTE
- ''SELECT count(*) FROM ( getSRID(geom) FROM ''
- || quote_ident(toponame) || ''.edge ''
- || '' UNION ''
- ''SELECT getSRID(geom) FROM ''
- || quote_ident(toponame) || ''.node )''
- LOOP
- IF rec.count > 1 THEN
- retrec.error = ''mixed SRIDs'';
- retrec.id1 = NULL;
- retrec.id2 = NULL;
- RETURN NEXT retrec;
- END IF;
- END LOOP;
-#endif
-
- RETURN;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
--- } ValidateTopology(toponame)
-
---{
--- TopoGeo_AddPoint(toponame, pointgeom, layer_id, topogeom_id)
---
--- Add a Point (node) into a topology
---
-CREATEFUNCTION topology.TopoGeo_AddPoint(varchar, geometry, integer, integer)
- RETURNS int AS
-'
-DECLARE
- atopology alias for $1;
- apoint alias for $2;
- ret int;
-BEGIN
-
- -- Add nodes (contained in NO face)
- SELECT topology.ST_AddIsoNode(atopology, NULL, apoint) INTO ret;
-
- RAISE NOTICE ''TopoGeo_AddPoint: node: %'', ret;
-
- RETURN ret;
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} TopoGeo_AddPoint
-
---{
--- TopoGeo_addLinestring
---
--- Add a Point (node) into a topology
---
-CREATEFUNCTION topology.TopoGeo_addLinestring(varchar, geometry)
- RETURNS int AS
-'
-DECLARE
- atopology alias for $1;
- aline alias for $2;
- rec RECORD;
- query text;
- firstpoint geometry;
- lastpoint geometry;
- firstnode int;
- lastnode int;
- edgeid int;
-BEGIN
- firstpoint = StartPoint(aline);
- lastpoint = EndPoint(aline);
-
- -- Add first and last point nodes (contained in NO face)
- SELECT topology.ST_AddIsoNode(atopology, NULL, firstpoint) INTO firstnode;
- SELECT topology.ST_AddIsoNode(atopology, NULL, lastpoint) INTO lastnode;
-
- RAISE NOTICE ''First node: %, Last node: %'', firstnode, lastnode;
-
- -- Add edge
- SELECT topology.ST_AddIsoEdge(atopology, firstnode, lastnode, aline)
- INTO edgeid;
-
- RAISE NOTICE ''Edge: %'', edgeid;
-
- RETURN edgeid;
-END
-'
-LANGUAGE 'plpgsql';
---} TopoGeo_addLinestring
-
---{
--- TopoGeo_AddPolygon
---
--- Add a Polygon into a topology
---
-CREATEFUNCTION topology.TopoGeo_AddPolygon(varchar, geometry)
- RETURNS int AS
-'
-DECLARE
- rec RECORD;
- query text;
-BEGIN
-
- RAISE EXCEPTION ''TopoGeo_AddPolygon not implemented yet'';
-END
-'
-LANGUAGE 'plpgsql';
---} TopoGeo_AddPolygon
-
---{
--- CreateTopology(name, SRID, precision)
---
--- Create a topology schema, add a topology info record
--- in the topology.topology relation, return it's numeric
--- id.
---
-CREATEFUNCTION topology.CreateTopology(varchar, integer, float8)
-RETURNS integer
-AS '
-DECLARE
- atopology alias for $1;
- srid alias for $2;
- precision alias for $3;
- rec RECORD;
- topology_id integer;
-BEGIN
-
--- FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
--- LOOP
--- RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
--- END LOOP;
-
- ------ Fetch next id for the new topology
- FOR rec IN SELECT nextval(''topology.topology_id_seq'')
- LOOP
- topology_id = rec.nextval;
- END LOOP;
-
-
- EXECUTE ''
-CREATE SCHEMA '' || quote_ident(atopology) || '';
- '';
-
- -------------{ face CREATION
- EXECUTE
- ''CREATE TABLE '' || quote_ident(atopology) || ''.face (''
- || ''face_id SERIAL,''
- || ''mbr BOX2D,''
- || '' CONSTRAINT face_primary_key PRIMARY KEY(face_id)''
- || '');'';
- -------------} END OF face CREATION
-
-
- --------------{ node CREATION
-
- EXECUTE
- ''CREATE TABLE '' || quote_ident(atopology) || ''.node (''
- || ''node_id SERIAL,''
- --|| ''geom GEOMETRY,''
- || ''containing_face INTEGER,''
-
- || ''CONSTRAINT node_primary_key PRIMARY KEY(node_id),''
-
- --|| ''CONSTRAINT node_geometry_type CHECK ''
- --|| ''( GeometryType(geom) = ''''POINT'''' ),''
-
- || ''CONSTRAINT face_exists FOREIGN KEY(containing_face) ''
- || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id)''
-
- || '');'';
-
- -- Add geometry column to the node table
- EXECUTE
- ''SELECT AddGeometryColumn(''||quote_literal(atopology)
- ||'',''''node'''',''''geom'''',''||quote_literal(srid)
- ||'',''''POINT'''',''''2'''')'';
-
- --------------} END OF node CREATION
-
- --------------{ edge CREATION
-
- -- edge_data table
- EXECUTE
- ''CREATE TABLE '' || quote_ident(atopology) || ''.edge_data (''
- || ''edge_id SERIAL NOT NULL PRIMARY KEY,''
- || ''start_node INTEGER NOT NULL,''
- || ''end_node INTEGER NOT NULL,''
- || ''next_left_edge INTEGER NOT NULL,''
- || ''abs_next_left_edge INTEGER NOT NULL,''
- || ''next_right_edge INTEGER NOT NULL,''
- || ''abs_next_right_edge INTEGER NOT NULL,''
- || ''left_face INTEGER NOT NULL,''
- || ''right_face INTEGER NOT NULL,''
- --|| ''geom GEOMETRY NOT NULL,''
-
- --|| ''CONSTRAINT edge_geometry_type CHECK ''
- --|| ''( GeometryType(geom) = ''''LINESTRING'''' ),''
-
- || ''CONSTRAINT start_node_exists FOREIGN KEY(start_node)''
- || '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
-
- || ''CONSTRAINT end_node_exists FOREIGN KEY(end_node) ''
- || '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
-
- || ''CONSTRAINT left_face_exists FOREIGN KEY(left_face) ''
- || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
-
- || ''CONSTRAINT right_face_exists FOREIGN KEY(right_face) ''
- || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
-
- || ''CONSTRAINT next_left_edge_exists FOREIGN KEY(abs_next_left_edge)''
- || '' REFERENCES '' || quote_ident(atopology)
- || ''.edge_data(edge_id)''
- || '' DEFERRABLE INITIALLY DEFERRED,''
-
- || ''CONSTRAINT next_right_edge_exists ''
- || ''FOREIGN KEY(abs_next_right_edge)''
- || '' REFERENCES '' || quote_ident(atopology)
- || ''.edge_data(edge_id) ''
- || '' DEFERRABLE INITIALLY DEFERRED''
- || '');'';
-
- -- Add geometry column to the edge_data table
- EXECUTE
- ''SELECT AddGeometryColumn(''||quote_literal(atopology)
- ||'',''''edge_data'''',''''geom'''',''||quote_literal(srid)
- ||'',''''LINESTRING'''',''''2'''')'';
-
-
- -- edge standard view (select rule)
- EXECUTE ''CREATE VIEW '' || quote_ident(atopology)
- || ''.edge AS SELECT ''
- || '' edge_id,start_node, end_node, next_left_edge, ''
- || '' next_right_edge, ''
- || '' left_face, right_face, geom FROM ''
- || quote_ident(atopology) || ''.edge_data'';
-
- -- edge standard view (insert rule)
- EXECUTE ''CREATE RULE edge_insert_rule AS ON INSERT ''
- || ''TO '' || quote_ident(atopology)
- || ''.edge DO INSTEAD ''
- || '' INSERT into '' || quote_ident(atopology)
- || ''.edge_data ''
- || '' VALUES (NEW.edge_id, NEW.start_node, NEW.end_node, ''
- || '' NEW.next_left_edge, abs(NEW.next_left_edge), ''
- || '' NEW.next_right_edge, abs(NEW.next_right_edge), ''
- || '' NEW.left_face, NEW.right_face, NEW.geom);'';
-
- --------------} END OF edge CREATION
-
- --------------{ layer sequence
- EXECUTE ''CREATE SEQUENCE ''
- || quote_ident(atopology) || ''.layer_id_seq;'';
- --------------} layer sequence
-
- --------------{ relation CREATION
- --
- EXECUTE
- ''CREATE TABLE '' || quote_ident(atopology) || ''.relation (''
- || '' topogeo_id integer NOT NULL, ''
- || '' layer_id integer NOT NULL, ''
- || '' element_id integer NOT NULL, ''
- || '' element_type integer NOT NULL, ''
- || '' UNIQUE(layer_id,topogeo_id,element_id,element_type));'';
-
- EXECUTE
- ''CREATE TRIGGER relation_integrity_checks ''
- ||''BEFORE UPDATE OR INSERT ON ''
- || quote_ident(atopology) || ''.relation FOR EACH ROW ''
- || '' EXECUTE PROCEDURE topology.RelationTrigger(''
- ||topology_id||'',''||quote_literal(atopology)||'')'';
- --------------} END OF relation CREATION
-
-
- ------- Default (world) face
- EXECUTE ''INSERT INTO '' || quote_ident(atopology) || ''.face(face_id) VALUES(0);'';
-
- ------- GiST index on node
- EXECUTE ''CREATE INDEX node_gist ON ''
- || quote_ident(atopology)
- || ''.node using gist (geom gist_geometry_ops);'';
-
- ------- GiST index on edge
- EXECUTE ''CREATE INDEX edge_gist ON ''
- || quote_ident(atopology)
- || ''.edge_data using gist (geom gist_geometry_ops);'';
-
- ------- Add record to the "topology" metadata table
- EXECUTE ''INSERT INTO topology.topology (id, name, srid, precision) ''
- || '' VALUES ('' || quote_literal(topology_id) || '',''
- || quote_literal(atopology) || '',''
- || quote_literal(srid) || '','' || quote_literal(precision)
- || '')'';
-
- RETURN topology_id;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
--- wrappers for unspecified srid or precision
-
-CREATEFUNCTION topology.CreateTopology(varchar, integer)
-RETURNS integer AS
-' SELECT topology.CreateTopology($1, $2, -1); '
-LANGUAGE 'SQL' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.CreateTopology(varchar)
-RETURNS integer AS
-' SELECT topology.CreateTopology($1, -1, -1); '
-LANGUAGE 'SQL' _VOLATILE_STRICT;
-
---} CreateTopology
-
---{
--- DropTopology(name)
---
--- Drops a topology schema getting rid of every dependent object.
---
-CREATEFUNCTION topology.DropTopology(varchar)
-RETURNS text
-AS '
-DECLARE
- atopology alias for $1;
- topoid integer;
- rec RECORD;
-BEGIN
-
- -- Get topology id
- SELECT id FROM topology.topology into topoid
- WHERE name = atopology;
-
-
- IF topoid IS NOT NULL THEN
-
- RAISE NOTICE ''Dropping all layers from topology % (%)'',
- atopology, topoid;
-
- -- Drop all layers in the topology
- FOR rec IN EXECUTE ''SELECT * FROM topology.layer WHERE ''
- || '' topology_id = '' || topoid
- LOOP
-
- EXECUTE ''SELECT topology.DropTopoGeometryColumn(''
- || quote_literal(rec.schema_name)
- || '',''
- || quote_literal(rec.table_name)
- || '',''
- || quote_literal(rec.feature_column)
- || '')'';
- END LOOP;
-
- -- Delete record from topology.topology
- EXECUTE ''DELETE FROM topology.topology WHERE id = ''
- || topoid;
-
- END IF;
-
-
- -- Drop the schema (if it exists)
- FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
- LOOP
- EXECUTE ''DROP SCHEMA ''||quote_ident(atopology)||'' CASCADE'';
- END LOOP;
-
-
- RETURN ''Topology '' || quote_literal(atopology) || '' dropped'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} DropTopology
-
---={ ----------------------------------------------------------------
--- POSTGIS-SPECIFIC topology predicates
---
--- This part contains function NOT in the SQL/MM specification
---
----------------------------------------------------------------------
-
---{
--- Intersects(TopoGeometry, TopoGeometry)
---
-CREATEFUNCTION topology.intersects(topology.TopoGeometry, topology.TopoGeometry)
- RETURNS bool
-AS
-'
-DECLARE
- tg1 alias for $1;
- tg2 alias for $2;
- tgbuf topology.TopoGeometry;
- rec RECORD;
- toponame varchar;
- query text;
-BEGIN
- IF tg1.topology_id != tg2.topology_id THEN
- RAISE EXCEPTION ''Cannot compute intersection between TopoGeometries from different topologies'';
- END IF;
-
- -- Order TopoGeometries so that tg1 has less-or-same
- -- dimensionality of tg1 (point,line,polygon,collection)
- IF tg1.type > tg2.type THEN
- tgbuf := tg2;
- tg2 := tg1;
- tg1 := tgbuf;
- END IF;
-
- --RAISE NOTICE ''tg1.id:% tg2.id:%'', tg1.id, tg2.id;
- -- Geometry collection are not currently supported
- IF tg2.type = 4 THEN
- RAISE EXCEPTION ''GeometryCollection are not supported by intersects()'';
- END IF;
-
- -- Get topology name
- SELECT name FROM topology.topology into toponame
- WHERE id = tg1.topology_id;
-
- -- Hierarchical TopoGeometries are not currently supported
- query = ''SELECT level FROM topology.layer''
- || '' WHERE ''
- || '' topology_id = '' || tg1.topology_id
- || '' AND ''
- || ''( layer_id = '' || tg1.layer_id
- || '' OR layer_id = '' || tg2.layer_id
- || '' ) ''
- || '' AND level > 0 '';
-
- --RAISE NOTICE ''%'', query;
-
- FOR rec IN EXECUTE query
- LOOP
- RAISE EXCEPTION ''Hierarchical TopoGeometries are not currently supported by intersects()'';
- END LOOP;
-
- IF tg1.type = 1 THEN -- [multi]point
-
-
- IF tg2.type = 1 THEN -- point/point
- ---------------------------------------------------------
- --
- -- Two [multi]point features intersect if they share
- -- any Node
- --
- --
- --
- query =
- ''SELECT a.topogeo_id FROM ''
- || quote_ident(toponame) ||
- ''.relation a, ''
- || quote_ident(toponame) ||
- ''.relation b ''
- || ''WHERE a.layer_id = '' || tg1.layer_id
- || '' AND b.layer_id = '' || tg2.layer_id
- || '' AND a.topogeo_id = '' || tg1.id
- || '' AND b.topogeo_id = '' || tg2.id
- || '' AND a.element_id = b.element_id ''
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN TRUE; -- they share an element
- END LOOP;
- RETURN FALSE; -- no elements shared
- --
- ---------------------------------------------------------
-
-
- ELSIF tg2.type = 2 THEN -- point/line
- ---------------------------------------------------------
- --
- -- A [multi]point intersects a [multi]line if they share
- -- any Node.
- --
- --
- --
- query =
- ''SELECT a.topogeo_id FROM ''
- || quote_ident(toponame) ||
- ''.relation a, ''
- || quote_ident(toponame) ||
- ''.relation b, ''
- || quote_ident(toponame) ||
- ''.edge_data e ''
- || ''WHERE a.layer_id = '' || tg1.layer_id
- || '' AND b.layer_id = '' || tg2.layer_id
- || '' AND a.topogeo_id = '' || tg1.id
- || '' AND b.topogeo_id = '' || tg2.id
- || '' AND abs(b.element_id) = e.edge_id ''
- || '' AND ( ''
- || '' e.start_node = a.element_id ''
- || '' OR ''
- || '' e.end_node = a.element_id ''
- || '' )''
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN TRUE; -- they share an element
- END LOOP;
- RETURN FALSE; -- no elements shared
- --
- ---------------------------------------------------------
-
- ELSIF tg2.type = 3 THEN -- point/polygon
- ---------------------------------------------------------
- --
- -- A [multi]point intersects a [multi]polygon if any
- -- Node of the point is contained in any face of the
- -- polygon OR ( is end_node or start_node of any edge
- -- of any polygon face ).
- --
- -- We assume the Node-in-Face check is faster becasue
- -- there will be less Faces then Edges in any polygon.
- --
- --
- --
- --
- -- Check if any node is contained in a face
- query =
- ''SELECT n.node_id as id FROM ''
- || quote_ident(toponame) ||
- ''.relation r1, ''
- || quote_ident(toponame) ||
- ''.relation r2, ''
- || quote_ident(toponame) ||
- ''.node n ''
- || ''WHERE r1.layer_id = '' || tg1.layer_id
- || '' AND r2.layer_id = '' || tg2.layer_id
- || '' AND r1.topogeo_id = '' || tg1.id
- || '' AND r2.topogeo_id = '' || tg2.id
- || '' AND n.node_id = r1.element_id ''
- || '' AND r2.element_id = n.containing_face ''
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- --RAISE NOTICE ''Node % in polygon face'', rec.id;
- RETURN TRUE; -- one (or more) nodes are
- -- contained in a polygon face
- END LOOP;
-
- -- Check if any node is start or end of any polygon
- -- face edge
- query =
- ''SELECT n.node_id as nid, e.edge_id as eid ''
- || '' FROM ''
- || quote_ident(toponame) ||
- ''.relation r1, ''
- || quote_ident(toponame) ||
- ''.relation r2, ''
- || quote_ident(toponame) ||
- ''.edge_data e, ''
- || quote_ident(toponame) ||
- ''.node n ''
- || ''WHERE r1.layer_id = '' || tg1.layer_id
- || '' AND r2.layer_id = '' || tg2.layer_id
- || '' AND r1.topogeo_id = '' || tg1.id
- || '' AND r2.topogeo_id = '' || tg2.id
- || '' AND n.node_id = r1.element_id ''
- || '' AND ( ''
- || '' e.left_face = r2.element_id ''
- || '' OR ''
- || '' e.right_face = r2.element_id ''
- || '' ) ''
- || '' AND ( ''
- || '' e.start_node = r1.element_id ''
- || '' OR ''
- || '' e.end_node = r1.element_id ''
- || '' ) ''
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- --RAISE NOTICE ''Node % on edge % bound'', rec.nid, rec.eid;
- RETURN TRUE; -- one node is start or end
- -- of a face edge
- END LOOP;
-
- RETURN FALSE; -- no intersection
- --
- ---------------------------------------------------------
-
- ELSIF tg2.type = 4 THEN -- point/collection
- RAISE EXCEPTION ''Intersection point/collection not implemented yet'';
-
- ELSE
- RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
- END IF;
-
- ELSIF tg1.type = 2 THEN -- [multi]line
- IF tg2.type = 2 THEN -- line/line
- ---------------------------------------------------------
- --
- -- A [multi]line intersects a [multi]line if they share
- -- any Node.
- --
- --
- --
- query =
- ''SELECT e1.start_node FROM ''
- || quote_ident(toponame) ||
- ''.relation r1, ''
- || quote_ident(toponame) ||
- ''.relation r2, ''
- || quote_ident(toponame) ||
- ''.edge_data e1, ''
- || quote_ident(toponame) ||
- ''.edge_data e2 ''
- || ''WHERE r1.layer_id = '' || tg1.layer_id
- || '' AND r2.layer_id = '' || tg2.layer_id
- || '' AND r1.topogeo_id = '' || tg1.id
- || '' AND r2.topogeo_id = '' || tg2.id
- || '' AND abs(r1.element_id) = e1.edge_id ''
- || '' AND abs(r2.element_id) = e2.edge_id ''
- || '' AND ( ''
- || '' e1.start_node = e2.start_node ''
- || '' OR ''
- || '' e1.start_node = e2.end_node ''
- || '' OR ''
- || '' e1.end_node = e2.start_node ''
- || '' OR ''
- || '' e1.end_node = e2.end_node ''
- || '' )''
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN TRUE; -- they share an element
- END LOOP;
- RETURN FALSE; -- no elements shared
- --
- ---------------------------------------------------------
-
- ELSIF tg2.type = 3 THEN -- line/polygon
- ---------------------------------------------------------
- --
- -- A [multi]line intersects a [multi]polygon if they share
- -- any Node (touch-only case), or if any line edge has any
- -- polygon face on the left or right (full-containment case
- -- + edge crossing case).
- --
- --
- -- E1 are line edges, E2 are polygon edges
- -- R1 are line relations.
- -- R2 are polygon relations.
- -- R2.element_id are FACE ids
- query =
- ''SELECT e1.edge_id''
- || '' FROM ''
- || quote_ident(toponame) ||
- ''.relation r1, ''
- || quote_ident(toponame) ||
- ''.relation r2, ''
- || quote_ident(toponame) ||
- ''.edge_data e1, ''
- || quote_ident(toponame) ||
- ''.edge_data e2 ''
- || ''WHERE r1.layer_id = '' || tg1.layer_id
- || '' AND r2.layer_id = '' || tg2.layer_id
- || '' AND r1.topogeo_id = '' || tg1.id
- || '' AND r2.topogeo_id = '' || tg2.id
-
- -- E1 are line edges
- || '' AND e1.edge_id = abs(r1.element_id) ''
-
- -- E2 are face edges
- || '' AND ( e2.left_face = r2.element_id ''
- || '' OR e2.right_face = r2.element_id ) ''
-
- || '' AND ( ''
-
- -- Check if E1 have left-or-right face
- -- being part of R2.element_id
- || '' e1.left_face = r2.element_id ''
- || '' OR ''
- || '' e1.right_face = r2.element_id ''
-
- -- Check if E1 share start-or-end node
- -- with any E2.
- || '' OR ''
- || '' e1.start_node = e2.start_node ''
- || '' OR ''
- || '' e1.start_node = e2.end_node ''
- || '' OR ''
- || '' e1.end_node = e2.start_node ''
- || '' OR ''
- || '' e1.end_node = e2.end_node ''
-
- || '' ) ''
-
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN TRUE; -- either common node
- -- or edge-in-face
- END LOOP;
-
- RETURN FALSE; -- no intersection
- --
- ---------------------------------------------------------
-
- ELSIF tg2.type = 4 THEN -- line/collection
- RAISE EXCEPTION ''Intersection line/collection not implemented yet'', tg1.type, tg2.type;
-
- ELSE
- RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
- END IF;
-
-
- ELSIF tg1.type = 3 THEN -- [multi]polygon
-
- IF tg2.type = 3 THEN -- polygon/polygon
- ---------------------------------------------------------
- --
- -- A [multi]polygon intersects a [multi]polygon if they share
- -- any Node (touch-only case), or if any face edge has any of the
- -- other polygon face on the left or right (full-containment case
- -- + edge crossing case).
- --
- --
- -- E1 are poly1 edges.
- -- E2 are poly2 edges
- -- R1 are poly1 relations.
- -- R2 are poly2 relations.
- -- R1.element_id are poly1 FACE ids
- -- R2.element_id are poly2 FACE ids
- query =
- ''SELECT e1.edge_id''
- || '' FROM ''
- || quote_ident(toponame) ||
- ''.relation r1, ''
- || quote_ident(toponame) ||
- ''.relation r2, ''
- || quote_ident(toponame) ||
- ''.edge_data e1, ''
- || quote_ident(toponame) ||
- ''.edge_data e2 ''
- || ''WHERE r1.layer_id = '' || tg1.layer_id
- || '' AND r2.layer_id = '' || tg2.layer_id
- || '' AND r1.topogeo_id = '' || tg1.id
- || '' AND r2.topogeo_id = '' || tg2.id
-
- -- E1 are poly1 edges
- || '' AND ( e1.left_face = r1.element_id ''
- || '' OR e1.right_face = r1.element_id ) ''
-
- -- E2 are poly2 edges
- || '' AND ( e2.left_face = r2.element_id ''
- || '' OR e2.right_face = r2.element_id ) ''
-
- || '' AND ( ''
-
- -- Check if any edge from a polygon face
- -- has any of the other polygon face
- -- on the left or right
- || '' e1.left_face = r2.element_id ''
- || '' OR ''
- || '' e1.right_face = r2.element_id ''
- || '' OR ''
- || '' e2.left_face = r1.element_id ''
- || '' OR ''
- || '' e2.right_face = r1.element_id ''
-
- -- Check if E1 share start-or-end node
- -- with any E2.
- || '' OR ''
- || '' e1.start_node = e2.start_node ''
- || '' OR ''
- || '' e1.start_node = e2.end_node ''
- || '' OR ''
- || '' e1.end_node = e2.start_node ''
- || '' OR ''
- || '' e1.end_node = e2.end_node ''
-
- || '' ) ''
-
- || '' LIMIT 1'';
- --RAISE NOTICE ''%'', query;
- FOR rec IN EXECUTE query
- LOOP
- RETURN TRUE; -- either common node
- -- or edge-in-face
- END LOOP;
-
- RETURN FALSE; -- no intersection
- --
- ---------------------------------------------------------
-
- ELSIF tg2.type = 4 THEN -- polygon/collection
- RAISE EXCEPTION ''Intersection poly/collection not implemented yet'', tg1.type, tg2.type;
-
- ELSE
- RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
- END IF;
-
- ELSIF tg1.type = 4 THEN -- collection
- IF tg2.type = 4 THEN -- collection/collection
- RAISE EXCEPTION ''Intersection collection/collection not implemented yet'', tg1.type, tg2.type;
- ELSE
- RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
- END IF;
-
- ELSE
- RAISE EXCEPTION ''Invalid TopoGeometry type %'', tg1.type;
- END IF;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} intersects(TopoGeometry, TopoGeometry)
-
---{
--- equals(TopoGeometry, TopoGeometry)
---
-CREATEFUNCTION topology.equals(topology.TopoGeometry, topology.TopoGeometry)
- RETURNS bool
-AS
-'
-DECLARE
- tg1 alias for $1;
- tg2 alias for $2;
- rec RECORD;
- toponame varchar;
- query text;
-BEGIN
-
- IF tg1.topology_id != tg2.topology_id THEN
- RAISE EXCEPTION ''Cannot compare TopoGeometries from different topologies'';
- END IF;
-
- -- Not the same type, not equal
- IF tg1.type != tg2.type THEN
- RETURN FALSE;
- END IF;
-
- -- Geometry collection are not currently supported
- IF tg2.type = 4 THEN
- RAISE EXCEPTION ''GeometryCollection are not supported by equals()'';
- END IF;
-
- -- Get topology name
- SELECT name FROM topology.topology into toponame
- WHERE id = tg1.topology_id;
-
- -- Two geometries are equal if they are composed by
- -- the same TopoElements
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || '' topology.GetTopoGeomElements(''
- || quote_literal(toponame) || '', ''
- || tg1.layer_id || '','' || tg1.id || '') ''
- || '' EXCEPT SELECT * FROM ''
- || '' topology.GetTopogeomElements(''
- || quote_literal(toponame) || '', ''
- || tg2.layer_id || '','' || tg2.id || '');''
- LOOP
- RETURN FALSE;
- END LOOP;
-
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || '' topology.GetTopoGeomElements(''
- || quote_literal(toponame) || '', ''
- || tg2.layer_id || '','' || tg2.id || '')''
- || '' EXCEPT SELECT * FROM ''
- || '' topology.GetTopogeomElements(''
- || quote_literal(toponame) || '', ''
- || tg1.layer_id || '','' || tg1.id || ''); ''
- LOOP
- RETURN FALSE;
- END LOOP;
- RETURN TRUE;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} equals(TopoGeometry, TopoGeometry)
-
---=} POSTGIS-SPECIFIC topology predicates
-
-
---=} POSTGIS-SPECIFIC block
-
---={ ----------------------------------------------------------------
--- SQL/MM block
---
--- This part contains function in the SQL/MM specification
---
----------------------------------------------------------------------
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.5
---
--- ST_GetFaceEdges(atopology, aface)
---
---
---
-CREATEFUNCTION topology.ST_GetFaceEdges(varchar, integer)
- RETURNS setof topology.GetFaceEdges_ReturnType
-AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- aface ALIAS FOR $2;
- rec RECORD;
-BEGIN
- --
- -- Atopology and aface are required
- --
- IF atopology IS NULL OR aface IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- RAISE EXCEPTION
- ''ST_GetFaceEdges: not implemented yet'';
-
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_GetFaceEdges
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.16
---
--- ST_GetFaceGeometry(atopology, aface)
---
-CREATEFUNCTION topology.ST_GetFaceGeometry(varchar, integer)
- RETURNS GEOMETRY AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- aface ALIAS FOR $2;
- rec RECORD;
-BEGIN
- --
- -- Atopology and aface are required
- --
- IF atopology IS NULL OR aface IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Construct face
- --
- FOR rec IN EXECUTE ''SELECT polygonize(geom) FROM ( SELECT geom FROM ''
- || quote_ident(atopology)
- || ''.edge WHERE left_face = '' || aface ||
- '' OR right_face = '' || aface || '') as foo''
- LOOP
- RETURN geometryN(rec.polygonize, 1);
- --RETURN rec.polygonize;
- END LOOP;
-
-
- --
- -- No face found
- --
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent face.'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_GetFaceGeometry
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.1
---
--- ST_AddIsoNode(atopology, aface, apoint)
---
-CREATEFUNCTION topology.ST_AddIsoNode(varchar, integer, geometry)
- RETURNS INTEGER AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- aface ALIAS FOR $2;
- apoint ALIAS FOR $3;
- rec RECORD;
- nodeid integer;
-BEGIN
-
- --
- -- Atopology and apoint are required
- --
- IF atopology IS NULL OR apoint IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Apoint must be a point
- --
- IF substring(geometrytype(apoint), 1, 5) != ''POINT''
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - invalid point'';
- END IF;
-
- --
- -- Check if a coincident node already exists
- --
- -- We use index AND x/y equality
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node '' ||
- ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
- ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
- ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - coincident node'';
- END LOOP;
-
- --
- -- Check if any edge crosses (intersects) this node
- -- I used _intersects_ here to include boundaries (endpoints)
- --
- FOR rec IN EXECUTE ''SELECT edge_id FROM ''
- || quote_ident(atopology) || ''.edge ''
- || ''WHERE geom && '' || quote_literal(apoint)
- || '' AND intersects(geom, '' || quote_literal(apoint)
- || '')''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception edge crosses node.'';
- END LOOP;
-
-
- --
- -- Verify that aface contains apoint
- -- if aface is null no check is done
- --
- IF aface IS NOT NULL THEN
-
- FOR rec IN EXECUTE ''SELECT within(''
- || quote_literal(apoint) || ''::geometry,
- topology.ST_GetFaceGeometry(''
- || quote_literal(atopology) || '', '' || aface ||
- ''))''
- LOOP
- IF rec.within = ''f'' THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not within face'';
- ELSIF rec.within IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent face'';
- END IF;
- END LOOP;
-
- END IF;
-
- --
- -- Get new node id from sequence
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.node_node_id_seq'''')''
- LOOP
- nodeid = rec.nextval;
- END LOOP;
-
- --
- -- Insert the new row
- --
- IF aface IS NOT NULL THEN
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.node(node_id, geom, containing_face)
- VALUES(''||nodeid||'',''||quote_literal(apoint)||
- '',''||aface||'')'';
- ELSE
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.node(node_id, geom)
- VALUES(''||nodeid||'',''||quote_literal(apoint)||
- '')'';
- END IF;
-
- RETURN nodeid;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.2
---
--- ST_MoveIsoNode(atopology, anode, apoint)
---
-CREATEFUNCTION topology.ST_MoveIsoNode(varchar, integer, geometry)
- RETURNS TEXT AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anode ALIAS FOR $2;
- apoint ALIAS FOR $3;
- rec RECORD;
-BEGIN
-
- --
- -- All arguments are required
- --
- IF atopology IS NULL OR anode IS NULL OR apoint IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Apoint must be a point
- --
- IF substring(geometrytype(apoint), 1, 5) != ''POINT''
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - invalid point'';
- END IF;
-
- --
- -- Check node isolation.
- --
- FOR rec IN EXECUTE ''SELECT edge_id FROM ''
- || quote_ident(atopology) || ''.edge '' ||
- '' WHERE start_node = '' || anode ||
- '' OR end_node = '' || anode
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not isolated node'';
- END LOOP;
-
- --
- -- Check if a coincident node already exists
- --
- -- We use index AND x/y equality
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node '' ||
- ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
- ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
- ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - coincident node'';
- END LOOP;
-
- --
- -- Check if any edge crosses (intersects) this node
- -- I used _intersects_ here to include boundaries (endpoints)
- --
- FOR rec IN EXECUTE ''SELECT edge_id FROM ''
- || quote_ident(atopology) || ''.edge ''
- || ''WHERE geom && '' || quote_literal(apoint)
- || '' AND intersects(geom, '' || quote_literal(apoint)
- || '')''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception edge crosses node.'';
- END LOOP;
-
- --
- -- Update node point
- --
- EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.node ''
- || '' SET geom = '' || quote_literal(apoint)
- || '' WHERE node_id = '' || anode;
-
- RETURN ''Isolated Node '' || anode || '' moved to location ''
- || x(apoint) || '','' || y(apoint);
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_MoveIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.3
---
--- ST_RemoveIsoNode(atopology, anode)
---
-CREATEFUNCTION topology.ST_RemoveIsoNode(varchar, integer)
- RETURNS TEXT AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anode ALIAS FOR $2;
- rec RECORD;
-BEGIN
-
- --
- -- Atopology and apoint are required
- --
- IF atopology IS NULL OR anode IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Check node isolation.
- --
- FOR rec IN EXECUTE ''SELECT edge_id FROM ''
- || quote_ident(atopology) || ''.edge_data '' ||
- '' WHERE start_node = '' || anode ||
- '' OR end_node = '' || anode
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not isolated node'';
- END LOOP;
-
- EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.node ''
- || '' WHERE node_id = '' || anode;
-
- RETURN ''Isolated node '' || anode || '' removed'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_RemoveIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.7
---
--- ST_RemoveIsoEdge(atopology, anedge)
---
-CREATEFUNCTION topology.ST_RemoveIsoEdge(varchar, integer)
- RETURNS TEXT AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anedge ALIAS FOR $2;
- edge RECORD;
- rec RECORD;
- ok BOOL;
-BEGIN
-
- --
- -- Atopology and anedge are required
- --
- IF atopology IS NULL OR anedge IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Check node existance
- --
- ok = false;
- FOR edge IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data '' ||
- '' WHERE edge_id = '' || anedge
- LOOP
- ok = true;
- END LOOP;
- IF NOT ok THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent edge'';
- END IF;
-
- --
- -- Check node isolation
- --
- IF edge.left_face != edge.right_face THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not isolated edge'';
- END IF;
-
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data ''
- || '' WHERE edge_id != '' || anedge
- || '' AND ( start_node = '' || edge.start_node
- || '' OR start_node = '' || edge.end_node
- || '' OR end_node = '' || edge.start_node
- || '' OR end_node = '' || edge.end_node
- || '' ) ''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not isolated edge'';
- END LOOP;
-
- --
- -- Delete the edge
- --
- EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
- || '' WHERE edge_id = '' || anedge;
-
- RETURN ''Isolated edge '' || anedge || '' removed'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_RemoveIsoEdge
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.8
---
--- ST_NewEdgesSplit(atopology, anedge, apoint)
---
-CREATEFUNCTION topology.ST_NewEdgesSplit(varchar, integer, geometry)
- RETURNS INTEGER AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anedge ALIAS FOR $2;
- apoint ALIAS FOR $3;
- oldedge RECORD;
- rec RECORD;
- tmp integer;
- topoid integer;
- nodeid integer;
- nodepos float8;
- edgeid1 integer;
- edgeid2 integer;
- edge1 geometry;
- edge2 geometry;
- ok BOOL;
-BEGIN
-
- --
- -- All args required
- --
- IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Check node existance
- --
- ok = false;
- FOR oldedge IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data '' ||
- '' WHERE edge_id = '' || anedge
- LOOP
- ok = true;
- END LOOP;
- IF NOT ok THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent edge'';
- END IF;
-
- --
- -- Check that given point is Within(anedge.geom)
- --
- IF NOT within(apoint, oldedge.geom) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - point not on edge'';
- END IF;
-
- --
- -- Check if a coincident node already exists
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node '' ||
- ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
- ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
- ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - coincident node'';
- END LOOP;
-
- --
- -- Get new node id
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.node_node_id_seq'''')''
- LOOP
- nodeid = rec.nextval;
- END LOOP;
-
- --RAISE NOTICE ''Next node id = % '', nodeid;
-
- --
- -- Add the new node
- --
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.node(node_id, geom)
- VALUES(''||nodeid||'',''||quote_literal(apoint)||
- '')'';
-
- --
- -- Delete the old edge
- --
- EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
- || '' WHERE edge_id = '' || anedge;
-
- --
- -- Compute new edges
- --
- nodepos = line_locate_point(oldedge.geom, apoint);
- edge1 = line_substring(oldedge.geom, 0, nodepos);
- edge2 = line_substring(oldedge.geom, nodepos, 1);
-
- --
- -- Get ids for the new edges
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.edge_data_edge_id_seq'''')''
- LOOP
- edgeid1 = rec.nextval;
- END LOOP;
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.edge_data_edge_id_seq'''')''
- LOOP
- edgeid2 = rec.nextval;
- END LOOP;
-
- --RAISE NOTICE ''EdgeId1 % EdgeId2 %'', edgeid1, edgeid2;
-
- --
- -- Insert the two new edges
- --
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.edge VALUES(''
- ||edgeid1||'',''||oldedge.start_node
- ||'',''||nodeid
- ||'',''||edgeid2
- ||'',''||oldedge.next_right_edge
- ||'',''||oldedge.left_face
- ||'',''||oldedge.right_face
- ||'',''||quote_literal(edge1)
- ||'')'';
-
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.edge VALUES(''
- ||edgeid2||'',''||nodeid
- ||'',''||oldedge.end_node
- ||'',''||oldedge.next_left_edge
- ||'',-''||edgeid1
- ||'',''||oldedge.left_face
- ||'',''||oldedge.right_face
- ||'',''||quote_literal(edge2)
- ||'')'';
-
- --
- -- Update all next edge references to match new layout
- --
-
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET next_right_edge = ''
- || edgeid2
- || '',''
- || '' abs_next_right_edge = '' || edgeid2
- || '' WHERE next_right_edge = '' || anedge;
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET next_right_edge = ''
- || -edgeid1
- || '',''
- || '' abs_next_right_edge = '' || edgeid1
- || '' WHERE next_right_edge = '' || -anedge;
-
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET next_left_edge = ''
- || edgeid1
- || '',''
- || '' abs_next_left_edge = '' || edgeid1
- || '' WHERE next_left_edge = '' || anedge;
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET ''
- || '' next_left_edge = '' || -edgeid2
- || '',''
- || '' abs_next_left_edge = '' || edgeid2
- || '' WHERE next_left_edge = '' || -anedge;
-
- -- Get topology id
- SELECT id FROM topology.topology into topoid
- WHERE name = atopology;
-
- --
- -- Update references in the Relation table.
- -- We only take into considerations non-hierarchical
- -- TopoGeometry here, for obvious reasons.
- --
- FOR rec IN EXECUTE ''SELECT r.* FROM ''
- || quote_ident(atopology)
- || ''.relation r, topology.layer l ''
- || '' WHERE ''
- || '' l.topology_id = '' || topoid
- || '' AND l.level = 0 ''
- || '' AND l.layer_id = r.layer_id ''
- || '' AND abs(r.element_id) = '' || anedge
- || '' AND r.element_type = 2''
- LOOP
- --RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split'', rec.topogeo_id, rec.layer_id;
-
- -- Delete old reference
- EXECUTE ''DELETE FROM '' || quote_ident(atopology)
- || ''.relation ''
- || '' WHERE ''
- || ''layer_id = '' || rec.layer_id
- || '' AND ''
- || ''topogeo_id = '' || rec.topogeo_id
- || '' AND ''
- || ''element_type = '' || rec.element_type
- || '' AND ''
- || ''abs(element_id) = '' || anedge;
-
- -- Add new reference to edge1
- IF rec.element_id < 0 THEN
- tmp = -edgeid1;
- ELSE
- tmp = edgeid1;
- END IF;
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.relation ''
- || '' VALUES( ''
- || rec.topogeo_id
- || '',''
- || rec.layer_id
- || '',''
- || tmp
- || '',''
- || rec.element_type
- || '')'';
-
- -- Add new reference to edge2
- IF rec.element_id < 0 THEN
- tmp = -edgeid2;
- ELSE
- tmp = edgeid2;
- END IF;
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.relation ''
- || '' VALUES( ''
- || rec.topogeo_id
- || '',''
- || rec.layer_id
- || '',''
- || tmp
- || '',''
- || rec.element_type
- || '')'';
-
- END LOOP;
-
- --RAISE NOTICE ''Edge % split in edges % and % by node %'',
- -- anedge, edgeid1, edgeid2, nodeid;
-
- RETURN nodeid;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_NewEdgesSplit
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.9
---
--- ST_ModEdgesSplit(atopology, anedge, apoint)
---
-CREATEFUNCTION topology.ST_ModEdgesSplit(varchar, integer, geometry)
- RETURNS INTEGER AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anedge ALIAS FOR $2;
- apoint ALIAS FOR $3;
- oldedge RECORD;
- rec RECORD;
- tmp integer;
- topoid integer;
- nodeid integer;
- nodepos float8;
- newedgeid integer;
- newedge1 geometry;
- newedge2 geometry;
- query text;
- ok BOOL;
-BEGIN
-
- --
- -- All args required
- --
- IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Check node existance
- --
- ok = false;
- FOR oldedge IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data '' ||
- '' WHERE edge_id = '' || anedge
- LOOP
- ok = true;
- END LOOP;
- IF NOT ok THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent edge'';
- END IF;
-
- --
- -- Check that given point is Within(anedge.geom)
- --
- IF NOT within(apoint, oldedge.geom) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - point not on edge'';
- END IF;
-
- --
- -- Check if a coincident node already exists
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node '' ||
- ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
- ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
- ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - coincident node'';
- END LOOP;
-
- --
- -- Get new node id
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.node_node_id_seq'''')''
- LOOP
- nodeid = rec.nextval;
- END LOOP;
-
- --RAISE NOTICE ''Next node id = % '', nodeid;
-
- --
- -- Add the new node
- --
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.node(node_id, geom)
- VALUES(''||nodeid||'',''||quote_literal(apoint)||
- '')'';
-
- --
- -- Compute new edge
- --
- nodepos = line_locate_point(oldedge.geom, apoint);
- newedge1 = line_substring(oldedge.geom, 0, nodepos);
- newedge2 = line_substring(oldedge.geom, nodepos, 1);
-
-
- --
- -- Get ids for the new edge
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.edge_data_edge_id_seq'''')''
- LOOP
- newedgeid = rec.nextval;
- END LOOP;
-
- --
- -- Insert the new edge
- --
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.edge ''
- || ''(edge_id, start_node, end_node,''
- || ''next_left_edge, next_right_edge,''
- || ''left_face, right_face, geom) ''
- || ''VALUES(''
- ||newedgeid||'',''||nodeid
- ||'',''||oldedge.end_node
- ||'',''||oldedge.next_left_edge
- ||'',-''||anedge
- ||'',''||oldedge.left_face
- ||'',''||oldedge.right_face
- ||'',''||quote_literal(newedge2)
- ||'')'';
-
- --
- -- Update the old edge
- --
- EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
- || '' SET geom = '' || quote_literal(newedge1)
- || '',''
- || '' next_left_edge = '' || newedgeid
- || '',''
- || '' end_node = '' || nodeid
- || '' WHERE edge_id = '' || anedge;
-
-
- --
- -- Update all next edge references to match new layout
- --
-
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET next_right_edge = ''
- || -newedgeid
- || '',''
- || '' abs_next_right_edge = '' || newedgeid
- || '' WHERE next_right_edge = '' || -anedge;
-
- EXECUTE ''UPDATE '' || quote_ident(atopology)
- || ''.edge_data SET ''
- || '' next_left_edge = '' || -newedgeid
- || '',''
- || '' abs_next_left_edge = '' || newedgeid
- || '' WHERE next_left_edge = '' || -anedge;
-
- -- Get topology id
- SELECT id FROM topology.topology into topoid
- WHERE name = atopology;
-
- --
- -- Update references in the Relation table.
- -- We only take into considerations non-hierarchical
- -- TopoGeometry here, for obvious reasons.
- --
- FOR rec IN EXECUTE ''SELECT r.* FROM ''
- || quote_ident(atopology)
- || ''.relation r, topology.layer l ''
- || '' WHERE ''
- || '' l.topology_id = '' || topoid
- || '' AND l.level = 0 ''
- || '' AND l.layer_id = r.layer_id ''
- || '' AND abs(r.element_id) = '' || anedge
- || '' AND r.element_type = 2''
- LOOP
- --RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split (%) - updating to add new edge %'', rec.topogeo_id, rec.layer_id, anedge, newedgeid;
-
- -- Add new reference to edge1
- IF rec.element_id < 0 THEN
- tmp = -newedgeid;
- ELSE
- tmp = newedgeid;
- END IF;
- query = ''INSERT INTO '' || quote_ident(atopology)
- || ''.relation ''
- || '' VALUES( ''
- || rec.topogeo_id
- || '',''
- || rec.layer_id
- || '',''
- || tmp
- || '',''
- || rec.element_type
- || '')'';
-
- --RAISE NOTICE ''%'', query;
- EXECUTE query;
- END LOOP;
-
- --RAISE NOTICE ''Edge % split in edges % and % by node %'',
- -- anedge, anedge, newedgeid, nodeid;
-
- RETURN nodeid;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_ModEdgesSplit
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.4
---
--- ST_AddIsoEdge(atopology, anode, anothernode, acurve)
---
-CREATEFUNCTION topology.ST_AddIsoEdge(varchar, integer, integer, geometry)
- RETURNS INTEGER AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anode ALIAS FOR $2;
- anothernode ALIAS FOR $3;
- acurve ALIAS FOR $4;
- aface INTEGER;
- face GEOMETRY;
- snodegeom GEOMETRY;
- enodegeom GEOMETRY;
- count INTEGER;
- rec RECORD;
- edgeid INTEGER;
-BEGIN
-
- --
- -- All arguments required
- --
- IF atopology IS NULL
- OR anode IS NULL
- OR anothernode IS NULL
- OR acurve IS NULL
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Acurve must be a LINESTRING
- --
- IF substring(geometrytype(acurve), 1, 4) != ''LINE''
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - invalid curve'';
- END IF;
-
- --
- -- Acurve must be a simple
- --
- IF NOT issimple(acurve)
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - curve not simple'';
- END IF;
-
- --
- -- Check for:
- -- existence of nodes
- -- nodes faces match
- -- Extract:
- -- nodes face id
- -- nodes geoms
- --
- aface := NULL;
- count := 0;
- FOR rec IN EXECUTE ''SELECT geom, containing_face, node_id FROM ''
- || quote_ident(atopology) || ''.node
- WHERE node_id = '' || anode ||
- '' OR node_id = '' || anothernode
- LOOP
- IF count > 0 AND aface != rec.containing_face THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - nodes in different faces'';
- ELSE
- aface := rec.containing_face;
- END IF;
-
- -- Get nodes geom
- IF rec.node_id = anode THEN
- snodegeom = rec.geom;
- ELSE
- enodegeom = rec.geom;
- END IF;
-
- count = count+1;
-
- END LOOP;
- IF count < 2 THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent node'';
- END IF;
-
-
- --
- -- Check nodes isolation.
- --
- FOR rec IN EXECUTE ''SELECT edge_id FROM ''
- || quote_ident(atopology) || ''.edge_data '' ||
- '' WHERE start_node = '' || anode ||
- '' OR end_node = '' || anode ||
- '' OR start_node = '' || anothernode ||
- '' OR end_node = '' || anothernode
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - not isolated node'';
- END LOOP;
-
- --
- -- Check acurve to be within endpoints containing face
- -- (unless it is the world face, I suppose)
- --
- IF aface IS NOT NULL THEN
-
- --
- -- Extract endpoints face geometry
- --
- FOR rec IN EXECUTE ''SELECT topology.ST_GetFaceGeometry(''
- || quote_literal(atopology) ||
- '','' || aface || '') as face''
- LOOP
- face := rec.face;
- END LOOP;
-
- --
- -- Check acurve to be within face
- --
- IF ! within(acurve, face) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception geometry not within face.'';
- END IF;
-
- END IF;
-
- --
- -- l) Check that start point of acurve match start node
- -- geoms.
- --
- IF x(snodegeom) != x(StartPoint(acurve)) OR
- y(snodegeom) != y(StartPoint(acurve)) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception start node not geometry start point.'';
- END IF;
-
- --
- -- m) Check that end point of acurve match end node
- -- geoms.
- --
- IF x(enodegeom) != x(EndPoint(acurve)) OR
- y(enodegeom) != y(EndPoint(acurve)) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception end node not geometry end point.'';
- END IF;
-
- --
- -- n) Check if curve crosses (contains) any node
- -- I used _contains_ here to leave endpoints out
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node ''
- || '' WHERE geom && '' || quote_literal(acurve)
- || '' AND contains('' || quote_literal(acurve)
- || '',geom)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry crosses a node'';
- END LOOP;
-
- --
- -- o) Check if curve intersects any other edge
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data
- WHERE geom && '' || quote_literal(acurve) || ''::geometry
- AND intersects(geom, '' || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry intersects an edge'';
- END LOOP;
-
- --
- -- Get new edge id from sequence
- --
- FOR rec IN EXECUTE ''SELECT nextval('''''' ||
- atopology || ''.edge_data_edge_id_seq'''')''
- LOOP
- edgeid = rec.nextval;
- END LOOP;
-
- --
- -- Insert the new row
- --
- IF aface IS NULL THEN aface := 0; END IF;
-
- EXECUTE ''INSERT INTO '' || quote_ident(atopology)
- || ''.edge VALUES(''||edgeid||'',''||anode||
- '',''||anothernode||'',''
- ||(-edgeid)||'',''||edgeid||'',''
- ||aface||'',''||aface||'',''
- ||quote_literal(acurve)||'')'';
-
- RETURN edgeid;
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddIsoEdge
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.6
---
--- ST_ChangeEdgeGeom(atopology, anedge, acurve)
---
-CREATEFUNCTION topology.ST_ChangeEdgeGeom(varchar, integer, geometry)
- RETURNS TEXT AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anedge ALIAS FOR $2;
- acurve ALIAS FOR $3;
- aface INTEGER;
- face GEOMETRY;
- snodegeom GEOMETRY;
- enodegeom GEOMETRY;
- count INTEGER;
- rec RECORD;
- edgeid INTEGER;
- oldedge RECORD;
-BEGIN
-
- --
- -- All arguments required
- --
- IF atopology IS NULL
- OR anedge IS NULL
- OR acurve IS NULL
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Acurve must be a LINESTRING
- --
- IF substring(geometrytype(acurve), 1, 4) != ''LINE''
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - invalid curve'';
- END IF;
-
- --
- -- Acurve must be a simple
- --
- IF NOT issimple(acurve)
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - curve not simple'';
- END IF;
-
- --
- -- e) Check StartPoint consistency
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data e, ''
- || quote_ident(atopology) || ''.node n ''
- || '' WHERE e.edge_id = '' || anedge
- || '' AND n.node_id = e.start_node ''
- || '' AND ( x(n.geom) != ''
- || x(StartPoint(acurve))
- || '' OR y(n.geom) != ''
- || y(StartPoint(acurve))
- || '')''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - start node not geometry start point.'';
- END LOOP;
-
- --
- -- f) Check EndPoint consistency
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data e, ''
- || quote_ident(atopology) || ''.node n ''
- || '' WHERE e.edge_id = '' || anedge
- || '' AND n.node_id = e.end_node ''
- || '' AND ( x(n.geom) != ''
- || x(EndPoint(acurve))
- || '' OR y(n.geom) != ''
- || y(EndPoint(acurve))
- || '')''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - end node not geometry end point.'';
- END LOOP;
-
- --
- -- g) Check if curve crosses any node
- -- _within_ used to let endpoints out
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node
- WHERE geom && '' || quote_literal(acurve) || ''::geometry
- AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry crosses a node'';
- END LOOP;
-
- --
- -- h) Check if curve intersects any other edge
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data ''
- || '' WHERE edge_id != '' || anedge
- || '' AND geom && ''
- || quote_literal(acurve) || ''::geometry ''
- || '' AND intersects(geom, ''
- || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry intersects an edge'';
- END LOOP;
-
- --
- -- Update edge geometry
- --
- EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
- || '' SET geom = '' || quote_literal(acurve)
- || '' WHERE edge_id = '' || anedge;
-
- RETURN ''Edge '' || anedge || '' changed'';
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_ChangeEdgeGeom
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.12
---
--- ST_AddEdgeNewFaces(atopology, anode, anothernode, acurve)
---
-CREATEFUNCTION topology.ST_AddEdgeNewFaces(varchar, integer, integer, geometry)
- RETURNS INTEGER AS
-'
-DECLARE
- atopology ALIAS FOR $1;
- anode ALIAS FOR $2;
- anothernode ALIAS FOR $3;
- acurve ALIAS FOR $4;
- rec RECORD;
- i INTEGER;
- az FLOAT8;
- azdif FLOAT8;
- myaz FLOAT8;
- minazimuth FLOAT8;
- maxazimuth FLOAT8;
- p2 GEOMETRY;
-BEGIN
-
- --
- -- All args required
- --
- IF atopology IS NULL
- OR anode IS NULL
- OR anothernode IS NULL
- OR acurve IS NULL
- THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- --
- -- Acurve must be a LINESTRING
- --
- IF substring(geometrytype(acurve), 1, 4) != ''LINE''
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - invalid curve'';
- END IF;
-
- --
- -- Curve must be simple
- --
- IF NOT issimple(acurve) THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - curve not simple'';
- END IF;
-
- --
- -- Check endpoints existance and match with Curve geometry
- --
- i=0;
- FOR rec IN EXECUTE ''SELECT ''
- || '' CASE WHEN node_id = '' || anode
- || '' THEN 1 WHEN node_id = '' || anothernode
- || '' THEN 0 END AS start, geom FROM ''
- || quote_ident(atopology)
- || ''.node ''
- || '' WHERE node_id IN ( ''
- || anode || '','' || anothernode
- || '')''
- LOOP
- IF rec.start THEN
- IF NOT Equals(rec.geom, StartPoint(acurve))
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - start node not geometry start point.'';
- END IF;
- ELSE
- IF NOT Equals(rec.geom, EndPoint(acurve))
- THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - end node not geometry end point.'';
- END IF;
- END IF;
-
- i=i+1;
- END LOOP;
-
- IF i < 2 THEN
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - non-existent node'';
- END IF;
-
- --
- -- Check if this geometry crosses any node
- --
- FOR rec IN EXECUTE ''SELECT node_id FROM ''
- || quote_ident(atopology) || ''.node
- WHERE geom && '' || quote_literal(acurve) || ''::geometry
- AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry crosses a node'';
- END LOOP;
-
- --
- -- Check if this geometry crosses any existing edge
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data
- WHERE geom && '' || quote_literal(acurve) || ''::geometry
- AND crosses(geom, '' || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - geometry crosses an edge'';
- END LOOP;
-
- --
- -- Check if another edge share this edge endpoints
- --
- FOR rec IN EXECUTE ''SELECT * FROM ''
- || quote_ident(atopology) || ''.edge_data ''
- || '' WHERE ''
- || '' geom && '' || quote_literal(acurve) || ''::geometry ''
- || '' AND ''
- || '' ( (''
- || '' start_node = '' || anode
- || '' AND ''
- || '' end_node = '' || anothernode
- || '' ) OR ( ''
- || '' end_node = '' || anode
- || '' AND ''
- || '' start_node = '' || anothernode
- || '' ) )''
- || '' AND ''
- || ''equals(geom,'' || quote_literal(acurve) || ''::geometry)''
- LOOP
- RAISE EXCEPTION
- ''SQL/MM Spatial exception - coincident edge'';
- END LOOP;
-
- ---------------------------------------------------------------
- --
- -- All checks passed, time to extract informations about
- -- endpoints:
- --
- -- next_left_edge
- -- next_right_edge
- -- left_face
- -- right_face
- --
- ---------------------------------------------------------------
-
- --
- --
- -- Compute next_left_edge
- --
- -- We fetch all edges with an endnode equal to
- -- this edge end_node (anothernode).
- -- For each edge we compute azimuth of the segment(s).
- -- Of interest are the edges with closest (smaller
- -- and bigger) azimuths then the azimuth of
- -- this edge last segment.
- --
-
- myaz = azimuth(EndPoint(acurve), PointN(acurve, NumPoints(acurve)-1));
- RAISE NOTICE ''My end-segment azimuth: %'', myaz;
- FOR rec IN EXECUTE ''SELECT ''
- || ''edge_id, end_node, start_node, geom''
- || '' FROM ''
- || quote_ident(atopology)
- || ''.edge_data ''
- || '' WHERE ''
- || '' end_node = '' || anothernode
- || '' OR ''
- || '' start_node = '' || anothernode
- LOOP
-
- IF rec.start_node = anothernode THEN
- --
- -- Edge starts at our node, we compute
- -- azimuth from node to its second point
- --
- az = azimuth(EndPoint(acurve),
- PointN(rec.geom, 2));
-
- RAISE NOTICE ''Edge % starts at node % - azimuth %'',
- rec.edge_id, rec.start_node, az;
- END IF;
-
- IF rec.end_node = anothernode THEN
- --
- -- Edge ends at our node, we compute
- -- azimuth from node to its second-last point
- --
- az = azimuth(EndPoint(acurve),
- PointN(rec.geom, NumPoints(rec.geom)-1));
-
- RAISE NOTICE ''Edge % ends at node % - azimuth %'',
- rec.edge_id, rec.end_node, az;
- END IF;
- END LOOP;
-
-
- RAISE EXCEPTION ''Not implemented yet'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddEdgeNewFaces
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.17
---
--- ST_InitTopoGeo(atopology)
---
-CREATEFUNCTION topology.ST_InitTopoGeo(varchar)
-RETURNS text
-AS '
-DECLARE
- atopology alias for $1;
- rec RECORD;
- topology_id numeric;
-BEGIN
- IF atopology IS NULL THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
- LOOP
- RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
- END LOOP;
-
- FOR rec IN EXECUTE ''SELECT topology.CreateTopology(''
- ||quote_literal(atopology)|| '') as id''
- LOOP
- topology_id := rec.id;
- END LOOP;
-
- RETURN ''Topology-Geometry '' || quote_literal(atopology)
- || '' (id:'' || topology_id || '') created. '';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_InitTopoGeo
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.18
---
--- ST_CreateTopoGeo(atopology, acollection)
---
-CREATEFUNCTION topology.ST_CreateTopoGeo(varchar, geometry)
-RETURNS text
-AS '
-DECLARE
- atopology alias for $1;
- acollection alias for $2;
- typ char(4);
- rec RECORD;
- ret int;
- schemaoid oid;
-BEGIN
- IF atopology IS NULL OR acollection IS NULL THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
- END IF;
-
- -- Verify existance of the topology schema
- FOR rec in EXECUTE ''SELECT oid,count(*) FROM pg_namespace WHERE ''
- || '' nspname = '' || quote_literal(atopology)
- || '' GROUP BY oid''
-
- LOOP
- IF rec.count < 1 THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent schema'';
- END IF;
- schemaoid := rec.oid;
- END LOOP;
-
- -- Verify existance of the topology views in the topology schema
- FOR rec in EXECUTE ''SELECT count(*) FROM pg_class WHERE ''
- || '' relnamespace = '' || schemaoid
- || '' and relname = ''''node''''''
- || '' OR relname = ''''edge''''''
- || '' OR relname = ''''face''''''
- LOOP
- IF rec.count < 3 THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent view'';
- END IF;
- END LOOP;
-
- -- Verify the topology views in the topology schema to be empty
- FOR rec in EXECUTE
- ''SELECT count(*) FROM ''
- || quote_ident(atopology) || ''.edge_data ''
- || '' UNION '' ||
- ''SELECT count(*) FROM ''
- || quote_ident(atopology) || ''.node ''
- LOOP
- IF rec.count > 0 THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty view'';
- END IF;
- END LOOP;
-
- -- face check is separated as it will contain a single (world)
- -- face record
- FOR rec in EXECUTE
- ''SELECT count(*) FROM ''
- || quote_ident(atopology) || ''.face ''
- LOOP
- IF rec.count != 1 THEN
- RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty face view'';
- END IF;
- END LOOP;
-
- --
- -- LOOP through the elements invoking the specific function
- --
- FOR rec IN SELECT geom(dump(acollection))
- LOOP
- typ := substring(geometrytype(rec.geom), 1, 3);
-
- IF typ = ''LIN'' THEN
- SELECT topology.TopoGeo_addLinestring(atopology, rec.geom) INTO ret;
- ELSIF typ = ''POI'' THEN
- SELECT topology.TopoGeo_AddPoint(atopology, rec.geom) INTO ret;
- ELSIF typ = ''POL'' THEN
- SELECT topology.TopoGeo_AddPolygon(atopology, rec.geom) INTO ret;
- ELSE
- RAISE EXCEPTION ''ST_CreateTopoGeo got unknown geometry type: %'', typ;
- END IF;
-
- END LOOP;
-
- RETURN ''Topology '' || atopology || '' populated'';
-
- RAISE EXCEPTION ''ST_CreateTopoGeo not implemente yet'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_CreateTopoGeo
-
---=} SQL/MM block
-
-COMMIT;
-
Added: trunk/topology/topology.sql.in.c
===================================================================
--- trunk/topology/topology.sql.in.c 2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/topology.sql.in.c 2008-05-22 14:31:02 UTC (rev 2779)
@@ -0,0 +1,3808 @@
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+-- $Id: topology.sql.in 2469 2006-09-06 11:16:59Z strk $
+--
+-- PostGIS - Spatial Types for PostgreSQL
+-- http://postgis.refractions.net
+-- Copyright 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 at refractions.net>
+--
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+-- 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 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
+-- Being working on. TODO: continue implementation
+--
+-- ST_AddIsoNode
+-- Complete
+--
+-- ST_RemoveIsoNode
+-- Complete
+--
+-- ST_MoveIsoNode
+-- Complete
+--
+-- ST_AddIsoEdge
+-- Complete
+--
+-- ST_RemoveIsoEdge
+-- Complete, exceptions untested
+--
+-- ST_ChangeEdgeGeom
+-- Complete
+--
+-- ST_NewEdgesSplit
+-- Complete
+-- this also updates the Relation table
+-- TODO: add entries to the History table ?
+--
+-- ST_ModEdgesSplit
+-- Complete
+-- this also updates the Relation table
+-- TODO: add entries to the History table ?
+--
+-- ST_AddEdgeNewFaces
+-- Being working on. TODO: continue
+--
+-- ST_GetFaceEdges
+-- Unimplemented (C seems appropriate)
+--
+-- ST_GetFaceGeometry
+-- Implemented using polygonize()
+--
+--
+
+#include "../lwgeom/sqldefines.h"
+
+DROP SCHEMA topology CASCADE;
+
+BEGIN;
+
+CREATE SCHEMA topology;
+
+--={ ----------------------------------------------------------------
+-- 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
+);
+
+--{ LayerTrigger()
+--
+-- Layer integrity trigger
+--
+CREATEFUNCTION 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 * FROM pg_namespace
+ WHERE text(nspname) = toponame
+ LOOP
+ query = ''SELECT * ''
+ || '' FROM '' || quote_ident(toponame)
+ || ''.relation ''
+ || '' WHERE topogeo_id = '' || OLD.topology_id
+ || '' AND 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 VARCHAR 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.
+
+--
+-- Type returned by ST_GetFaceEdges
+--
+CREATE TYPE topology.GetFaceEdges_ReturnType AS (
+ sequence integer,
+ edge integer
+);
+
+--
+-- Type returned by ValidateTopology
+--
+CREATE TYPE topology.ValidateTopology_ReturnType AS (
+ error varchar,
+ id1 integer,
+ id2 integer
+);
+
+--
+-- TopoGeometry type
+--
+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
+--
+CREATE DOMAIN topology.TopoElement AS integer[]
+ CONSTRAINT DIMENSIONS CHECK (
+ array_upper(VALUE, 2) IS NULL
+ AND array_upper(VALUE, 1) = 2
+ );
+--
+-- TopoElementArray domain
+--
+CREATE DOMAIN topology.TopoElementArray AS integer[][]
+ CONSTRAINT DIMENSIONS CHECK (
+ array_upper(VALUE, 2) IS NOT NULL
+ AND array_upper(VALUE, 2) = 2
+ AND array_upper(VALUE, 3) IS NULL
+ );
+
+--
+-- TopoGeomElementArray domain
+--
+CREATE DOMAIN topology.TopoGeomElementArray AS integer[][]
+ CONSTRAINT DIMENSIONS CHECK (
+ array_upper(VALUE, 2) IS NOT NULL
+ AND array_upper(VALUE, 2) = 2
+ AND array_upper(VALUE, 3) IS NULL
+ );
+
+--{ RelationTrigger()
+--
+-- Relation integrity trigger
+--
+CREATEFUNCTION 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()
+
+--{
+-- AddTopoGeometryColumn(toponame, schema, table, colum, type, [child])
+--
+-- Add a TopoGeometry column to a table, making it a topology layer.
+-- Returns created layer id.
+--
+--
+CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar, integer)
+ RETURNS integer
+AS '
+DECLARE
+ toponame alias for $1;
+ schema alias for $2;
+ table alias for $3;
+ col alias for $4;
+ ltype alias for $5;
+ child alias for $6;
+ intltype integer;
+ level integer;
+ topoid integer;
+ rec RECORD;
+ layer_id integer;
+ query text;
+BEGIN
+
+ -- Get topology id
+ SELECT id FROM topology.topology into topoid
+ WHERE name = toponame;
+
+ IF topoid IS NULL THEN
+ RAISE EXCEPTION ''Topology % does not exist'', toponame;
+ END IF;
+
+ --
+ -- Get new layer id from sequence
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ quote_ident(toponame) || ''.layer_id_seq'''')''
+ LOOP
+ layer_id = rec.nextval;
+ END LOOP;
+
+ IF ltype = ''POINT'' THEN
+ intltype = 1;
+ ELSIF ltype = ''LINE'' THEN
+ intltype = 2;
+ ELSIF ltype = ''POLYGON'' THEN
+ intltype = 3;
+ ELSIF ltype = ''COLLECTION'' THEN
+ intltype = 4;
+ ELSE
+ RAISE EXCEPTION ''Layer type must be one of POINT,LINE,POLYGON,COLLECTION'';
+ END IF;
+
+ --
+ -- See if child id exists and extract its level
+ --
+ IF child IS NULL THEN
+ EXECUTE ''INSERT INTO ''
+ || ''topology.layer(topology_id, ''
+ || ''layer_id, schema_name, ''
+ || ''table_name, feature_column, feature_type) ''
+ || ''VALUES (''
+ || topoid || '',''
+ || layer_id || '',''
+ || quote_literal(schema) || '',''
+ || quote_literal(table) || '',''
+ || quote_literal(col) || '',''
+ || intltype || '');'';
+ ELSE
+ FOR rec IN EXECUTE ''SELECT level FROM topology.layer''
+ || '' WHERE layer_id = '' || child
+ LOOP
+ level = rec.level + 1;
+ END LOOP;
+
+ EXECUTE ''INSERT INTO ''
+ || ''topology.layer(topology_id, ''
+ || ''layer_id, level, child_id, schema_name, ''
+ || ''table_name, feature_column, feature_type) ''
+ || ''VALUES (''
+ || topoid || '',''
+ || layer_id || '','' || level || '',''
+ || child || '',''
+ || quote_literal(schema) || '',''
+ || quote_literal(table) || '',''
+ || quote_literal(col) || '',''
+ || intltype || '');'';
+ END IF;
+
+
+ --
+ -- Create a sequence for TopoGeometries in this new layer
+ --
+ EXECUTE ''CREATE SEQUENCE '' || quote_ident(toponame)
+ || ''.topogeo_s_'' || layer_id;
+
+ --
+ -- Add new TopoGeometry column in schema.table
+ --
+ EXECUTE ''ALTER TABLE '' || quote_ident(schema)
+ || ''.'' || quote_ident(table)
+ || '' ADD COLUMN '' || quote_ident(col)
+ || '' topology.TopoGeometry;'';
+
+ --
+ -- Add constraints on TopoGeom column
+ --
+ EXECUTE ''ALTER TABLE '' || quote_ident(schema)
+ || ''.'' || quote_ident(table)
+ || '' ADD CONSTRAINT check_topogeom CHECK (''
+ || ''topology_id('' || quote_ident(col) || '') = '' || topoid
+ || '' AND ''
+ || ''layer_id('' || quote_ident(col) || '') = '' || layer_id
+ || '' AND ''
+ || ''type('' || quote_ident(col) || '') = '' || intltype
+ || '');'';
+
+ --
+ -- Add dependency of the feature column on the topology schema
+ --
+ query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
+ || ''fcat.oid, fobj.oid, fsub.attnum, tcat.oid, ''
+ || ''tobj.oid, 0, ''''n'''' ''
+ || ''FROM pg_class fcat, pg_namespace fnsp, ''
+ || '' pg_class fobj, pg_attribute fsub, ''
+ || '' pg_class tcat, pg_namespace tobj ''
+ || '' WHERE fcat.relname = ''''pg_class'''' ''
+ || '' AND fnsp.nspname = '' || quote_literal(schema)
+ || '' AND fobj.relnamespace = fnsp.oid ''
+ || '' AND fobj.relname = '' || quote_literal(table)
+ || '' AND fsub.attrelid = fobj.oid ''
+ || '' AND fsub.attname = '' || quote_literal(col)
+ || '' AND tcat.relname = ''''pg_namespace'''' ''
+ || '' AND tobj.nspname = '' || quote_literal(toponame);
+
+--
+-- The only reason to add this dependency is to avoid
+-- simple drop of a feature column. Still, drop cascade
+-- will remove both the feature column and the sequence
+-- corrupting the topology anyway ...
+--
+#if 0
+ --
+ -- Add dependency of the topogeom sequence on the feature column
+ -- This is a dirty hack ...
+ --
+ query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
+ || ''scat.oid, sobj.oid, 0, fcat.oid, ''
+ || ''fobj.oid, fsub.attnum, ''''n'''' ''
+ || ''FROM pg_class fcat, pg_namespace fnsp, ''
+ || '' pg_class fobj, pg_attribute fsub, ''
+ || '' pg_class scat, pg_class sobj, ''
+ || '' pg_namespace snsp ''
+ || '' WHERE fcat.relname = ''''pg_class'''' ''
+ || '' AND fnsp.nspname = '' || quote_literal(schema)
+ || '' AND fobj.relnamespace = fnsp.oid ''
+ || '' AND fobj.relname = '' || quote_literal(table)
+ || '' AND fsub.attrelid = fobj.oid ''
+ || '' AND fsub.attname = '' || quote_literal(col)
+ || '' AND scat.relname = ''''pg_class'''' ''
+ || '' AND snsp.nspname = '' || quote_literal(toponame)
+ || '' AND sobj.relnamespace = snsp.oid ''
+ || '' AND sobj.relname = ''
+ || '' ''''topogeo_s_'' || layer_id || '''''' '';
+
+ RAISE NOTICE ''%'', query;
+ EXECUTE query;
+#endif
+
+ RETURN layer_id;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+
+CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar)
+ RETURNS integer
+AS '
+ SELECT topology.AddTopoGeometryColumn($1, $2, $3, $4, $5, NULL);
+'
+LANGUAGE 'sql' _VOLATILE;
+
+--
+--} AddTopoGeometryColumn
+
+--{
+-- DropTopoGeometryColumn(schema, table, colum)
+--
+-- Drop a TopoGeometry column, unregister the associated layer,
+-- cleanup the relation table.
+--
+--
+CREATEFUNCTION topology.DropTopoGeometryColumn(varchar, varchar, varchar)
+ RETURNS text
+AS '
+DECLARE
+ schema alias for $1;
+ table alias for $2;
+ col alias for $3;
+ rec RECORD;
+ lyrinfo RECORD;
+ ok BOOL;
+ result text;
+BEGIN
+
+ -- Get layer and topology info
+ ok = false;
+ FOR rec IN EXECUTE ''SELECT t.name as toponame, l.* FROM ''
+ || ''topology.topology t, topology.layer l ''
+ || '' WHERE l.schema_name = '' || quote_literal(schema)
+ || '' AND l.table_name = '' || quote_literal(table)
+ || '' AND l.feature_column = '' || quote_literal(col)
+ LOOP
+ ok = true;
+ lyrinfo = rec;
+ END LOOP;
+
+ -- Layer not found
+ IF NOT ok THEN
+ RAISE EXCEPTION ''No layer registered on %.%.%'',
+ schema,table,col;
+ END IF;
+
+ -- Clean up the topology schema
+ FOR rec IN SELECT * FROM pg_namespace
+ WHERE text(nspname) = lyrinfo.toponame
+ LOOP
+ -- Cleanup the relation table
+ EXECUTE ''DELETE FROM '' || quote_ident(lyrinfo.toponame)
+ || ''.relation ''
+ || '' WHERE ''
+ || ''layer_id = '' || lyrinfo.layer_id;
+
+ -- Drop the sequence for topogeoms in this layer
+ EXECUTE ''DROP SEQUENCE '' || quote_ident(lyrinfo.toponame)
+ || ''.topogeo_s_'' || lyrinfo.layer_id;
+
+ END LOOP;
+
+ 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) = table
+ AND a.attrelid = c.oid
+ AND text(a.attname) = col
+ LOOP
+ ok = true;
+ EXIT;
+ END LOOP;
+
+
+ IF ok THEN
+ -- Set feature column to NULL to bypass referential integrity
+ -- checks
+ EXECUTE ''UPDATE '' || quote_ident(schema) || ''.''
+ || quote_ident(table)
+ || '' SET '' || quote_ident(col)
+ || '' = NULL'';
+ END IF;
+
+ -- Delete the layer record
+ EXECUTE ''DELETE FROM topology.layer ''
+ || '' WHERE topology_id = '' || lyrinfo.topology_id
+ || '' AND layer_id = '' || lyrinfo.layer_id;
+
+ IF ok THEN
+ -- Drop the layer column
+ EXECUTE ''ALTER TABLE '' || quote_ident(schema) || ''.''
+ || quote_ident(table)
+ || '' DROP '' || quote_ident(col)
+ || '' cascade'';
+ END IF;
+
+ result = ''Layer '' || lyrinfo.layer_id || '' (''
+ || schema || ''.'' || table || ''.'' || col
+ || '') dropped'';
+
+ RETURN result;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--
+--} DropTopoGeometryColumn
+
+
+--{
+-- 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).
+--
+-- Return a topology.TopoGeometry object.
+--
+CREATEFUNCTION topology.CreateTopoGeom(varchar, integer, integer, topology.TopoElementArray)
+ RETURNS topology.TopoGeometry
+AS '
+DECLARE
+ toponame alias for $1;
+ tg_type alias for $2; -- 1:[multi]point
+ -- 2:[multi]line
+ -- 3:[multi]poly
+ -- 4:collection
+ layer_id alias for $3;
+ tg_objs alias for $4;
+ 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'';
+ END IF;
+
+ -- Get topology id into return TopoGeometry
+ SELECT id FROM topology.topology into ret.topology_id
+ WHERE name = toponame;
+
+ --
+ -- 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('''''' ||
+ 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];
+ 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 || '');'';
+
+ 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, TopoObject[])
+
+--{
+-- GetTopologyName(topology_id)
+--
+CREATEFUNCTION topology.GetTopologyName(integer)
+ RETURNS varchar
+AS
+'
+DECLARE
+ topoid alias for $1;
+ ret varchar;
+BEGIN
+ SELECT name FROM topology.topology into ret
+ WHERE id = topoid;
+ RETURN ret;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} GetTopologyName(topoid)
+
+--{
+-- GetTopologyId(toponame)
+--
+CREATEFUNCTION topology.GetTopologyId(varchar)
+ RETURNS integer
+AS
+'
+DECLARE
+ toponame alias for $1;
+ ret integer;
+BEGIN
+ SELECT id FROM topology.topology into ret
+ WHERE name = toponame;
+ RETURN ret;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} GetTopologyId(toponame)
+
+
+
+--{
+-- GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
+-- GetTopoGeomElementArray(TopoGeometry)
+--
+-- Returns a set of element_id,element_type
+--
+CREATEFUNCTION topology.GetTopoGeomElementArray(varchar, integer, integer)
+ RETURNS topology.TopoElementArray
+AS
+'
+DECLARE
+ toponame alias for $1;
+ layerid alias for $2;
+ tgid alias for $3;
+ rec RECORD;
+ tg_objs varchar := ''{'';
+ i integer;
+ query text;
+BEGIN
+
+ query = ''SELECT * FROM topology.GetTopoGeomElements(''
+ || quote_literal(toponame) || '',''
+ || quote_literal(layerid) || '',''
+ || quote_literal(tgid)
+ || '') as obj ORDER BY obj'';
+
+ RAISE NOTICE ''Query: %'', query;
+
+ 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' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.GetTopoGeomElementArray(topology.TopoGeometry)
+ RETURNS topology.TopoElementArray
+AS
+'
+DECLARE
+ tg alias for $1;
+ toponame varchar;
+ ret topology.TopoElementArray;
+BEGIN
+ toponame = topology.GetTopologyName(tg.topology_id);
+ ret = topology.GetTopoGeomElementArray(toponame,tg.layer_id,tg.id);
+ RETURN ret;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+--} GetTopoGeomElementArray()
+
+--{
+-- GetTopoGeomElements(toponame, layer_id, topogeom_id)
+-- GetTopoGeomElements(TopoGeometry)
+--
+-- Returns a set of element_id,element_type
+--
+CREATEFUNCTION topology.GetTopoGeomElements(varchar, integer, integer)
+ RETURNS SETOF topology.TopoElement
+AS
+'
+DECLARE
+ toponame alias for $1;
+ layerid alias for $2;
+ tgid alias for $3;
+ ret topology.TopoElement;
+ rec RECORD;
+ rec2 RECORD;
+ query text;
+ query2 text;
+ lyr RECORD;
+ ok bool;
+BEGIN
+
+ -- Get layer info
+ ok = false;
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || '' topology.layer ''
+ || '' WHERE layer_id = '' || layerid
+ 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' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.GetTopoGeomElements(topology.TopoGeometry)
+ RETURNS SETOF topology.TopoElement
+AS
+'
+DECLARE
+ tg alias for $1;
+ 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' _VOLATILE_STRICT;
+
+--} GetTopoGeomElements()
+
+--{
+-- Geometry(TopoGeometry)
+--
+-- Construct a Geometry from a TopoGeometry.
+--
+--
+CREATE OR REPLACE FUNCTION topology.Geometry(topology.TopoGeometry)
+ RETURNS Geometry
+AS '
+DECLARE
+ topogeom alias for $1;
+ toponame varchar;
+ geom geometry;
+ rec RECORD;
+ plyr RECORD;
+ clyr RECORD;
+ query text;
+ ok BOOL;
+BEGIN
+ -- Get topology name
+ SELECT name FROM topology.topology into toponame
+ WHERE id = topogeom.topology_id;
+
+ -- Get layer info
+ ok = false;
+ FOR rec IN EXECUTE ''SELECT * FROM topology.layer ''
+ || '' WHERE topology_id = '' || topogeom.topology_id
+ || '' AND layer_id = '' || topogeom.layer_id
+ LOOP
+ ok = true;
+ plyr = rec;
+ END LOOP;
+
+ IF NOT ok 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
+ FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
+ || '' WHERE layer_id = '' || plyr.child_id
+ LOOP
+ clyr = rec;
+ END LOOP;
+
+ query = ''SELECT geomunion(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 NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN rec.geom;
+ END LOOP;
+
+ END IF;
+
+
+ IF topogeom.type = 3 THEN -- [multi]polygon
+ FOR rec IN EXECUTE ''SELECT geomunion(''
+ || ''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 ''
+ LOOP
+ geom := rec.g;
+ END LOOP;
+
+ ELSIF topogeom.type = 2 THEN -- [multi]line
+ FOR rec IN EXECUTE ''SELECT linemerge(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''
+ LOOP
+ geom := rec.g;
+ END LOOP;
+
+ ELSIF topogeom.type = 1 THEN -- [multi]point
+ FOR rec IN EXECUTE ''SELECT geomunion(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''
+ LOOP
+ geom := rec.g;
+ END LOOP;
+
+ ELSE
+ RAISE NOTICE ''Geometry from TopoGeometry does not support TopoGeometries of type % so far'', topogeom.type;
+ geom := ''GEOMETRYCOLLECTION EMPTY'';
+ END IF;
+
+ RETURN geom;
+END
+'
+LANGUAGE 'plpgsql' VOLATILE STRICT;
+--} Geometry(TopoGeometry)
+
+--{
+-- ValidateTopology(toponame)
+--
+-- Return a Set of ValidateTopology_ReturnType containing
+-- informations on all topology inconsistencies
+--
+CREATEFUNCTION topology.ValidateTopology(varchar)
+ RETURNS setof topology.ValidateTopology_ReturnType
+AS
+'
+DECLARE
+ toponame alias for $1;
+ retrec topology.ValidateTopology_ReturnType;
+ rec RECORD;
+ i integer;
+BEGIN
+
+ -- Check for coincident nodes
+ FOR rec IN EXECUTE ''SELECT a.node_id as id1, b.node_id as id2 FROM ''
+ || quote_ident(toponame) || ''.node a, ''
+ || quote_ident(toponame) || ''.node b ''
+ || ''WHERE a.node_id < b.node_id AND a.geom && b.geom''
+ LOOP
+ retrec.error = ''coincident nodes'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for edge crossed nodes
+ FOR rec IN EXECUTE ''SELECT n.node_id as id1, e.edge_id as id2 FROM ''
+ || quote_ident(toponame) || ''.node n, ''
+ || quote_ident(toponame) || ''.edge e ''
+ || ''WHERE e.start_node != n.node_id ''
+ || ''AND e.end_node != n.node_id ''
+ || ''AND n.geom && e.geom ''
+ || ''AND intersects(n.geom, e.geom)''
+ LOOP
+ retrec.error = ''edge crosses node'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for non-simple edges
+ FOR rec IN EXECUTE ''SELECT e.edge_id as id1 FROM ''
+ || quote_ident(toponame) || ''.edge e ''
+ || ''WHERE not issimple(e.geom)''
+ LOOP
+ retrec.error = ''edge not simple'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = NULL;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for edge crossing
+ FOR rec IN EXECUTE ''SELECT e1.edge_id as id1, e2.edge_id as id2 FROM ''
+ || quote_ident(toponame) || ''.edge e1, ''
+ || quote_ident(toponame) || ''.edge e2 ''
+ || ''WHERE e1.edge_id < e2.edge_id ''
+ || ''AND e1.geom && e2.geom ''
+ || ''AND crosses(e1.geom, e2.geom)''
+ LOOP
+ retrec.error = ''edge crosses edge'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for edge start_node geometry mis-match
+ FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
+ || quote_ident(toponame) || ''.edge e, ''
+ || quote_ident(toponame) || ''.node n ''
+ || ''WHERE e.start_node = n.node_id ''
+ || ''AND NOT Equals(StartPoint(e.geom), n.geom)''
+ LOOP
+ retrec.error = ''edge start node geometry mis-match'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for edge end_node geometry mis-match
+ FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
+ || quote_ident(toponame) || ''.edge e, ''
+ || quote_ident(toponame) || ''.node n ''
+ || ''WHERE e.end_node = n.node_id ''
+ || ''AND NOT Equals(EndPoint(e.geom), n.geom)''
+ LOOP
+ retrec.error = ''edge end node geometry mis-match'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for faces w/out edges
+ FOR rec IN EXECUTE ''SELECT face_id as id1 FROM ''
+ || quote_ident(toponame) || ''.face ''
+ || ''EXCEPT ( SELECT left_face FROM ''
+ || quote_ident(toponame) || ''.edge ''
+ || '' UNION SELECT right_face FROM ''
+ || quote_ident(toponame) || ''.edge ''
+ || '')''
+ LOOP
+ retrec.error = ''face without edges'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = NULL;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for overlapping faces
+ -- TODO: this check requires some thinking, do we really
+ -- have to construct face geometry to detect overlap with
+ -- another face ??
+ FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
+ || quote_ident(toponame) || ''.face f1, ''
+ || quote_ident(toponame) || ''.face f2 ''
+ || ''WHERE f1.face_id > 0 AND f1.face_id < f2.face_id AND ''
+ || '' Overlaps(topology.ST_GetFaceGeometry(''
+ || quote_literal(toponame) || '', f1.face_id), ''
+ || '' topology.ST_GetFaceGeometry(''
+ || quote_literal(toponame) || '', f2.face_id))''
+ LOOP
+ retrec.error = ''face overlaps face'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+ -- Check for face within face
+ -- TODO: this check requires some thinking, do we really
+ -- have to construct face geometry to detect within condition
+ -- another face ??
+ FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
+ || quote_ident(toponame) || ''.face f1, ''
+ || quote_ident(toponame) || ''.face f2 ''
+ || ''WHERE f1.face_id != 0 AND f2.face_id != 0 ''
+ || ''AND f1.face_id != f2.face_id ''
+ || ''AND Within(topology.ST_GetFaceGeometry(''
+ || quote_literal(toponame) || '', f1.face_id), ''
+ || '' topology.ST_GetFaceGeometry(''
+ || quote_literal(toponame) || '', f2.face_id))''
+ LOOP
+ retrec.error = ''face within face'';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
+ END LOOP;
+
+#if 0
+ -- Check SRID consistency
+ FOR rec in EXECUTE
+ ''SELECT count(*) FROM ( getSRID(geom) FROM ''
+ || quote_ident(toponame) || ''.edge ''
+ || '' UNION ''
+ ''SELECT getSRID(geom) FROM ''
+ || quote_ident(toponame) || ''.node )''
+ LOOP
+ IF rec.count > 1 THEN
+ retrec.error = ''mixed SRIDs'';
+ retrec.id1 = NULL;
+ retrec.id2 = NULL;
+ RETURN NEXT retrec;
+ END IF;
+ END LOOP;
+#endif
+
+ RETURN;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+-- } ValidateTopology(toponame)
+
+--{
+-- TopoGeo_AddPoint(toponame, pointgeom, layer_id, topogeom_id)
+--
+-- Add a Point (node) into a topology
+--
+CREATEFUNCTION topology.TopoGeo_AddPoint(varchar, geometry, integer, integer)
+ RETURNS int AS
+'
+DECLARE
+ atopology alias for $1;
+ apoint alias for $2;
+ ret int;
+BEGIN
+
+ -- Add nodes (contained in NO face)
+ SELECT topology.ST_AddIsoNode(atopology, NULL, apoint) INTO ret;
+
+ RAISE NOTICE ''TopoGeo_AddPoint: node: %'', ret;
+
+ RETURN ret;
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} TopoGeo_AddPoint
+
+--{
+-- TopoGeo_addLinestring
+--
+-- Add a Point (node) into a topology
+--
+CREATEFUNCTION topology.TopoGeo_addLinestring(varchar, geometry)
+ RETURNS int AS
+'
+DECLARE
+ atopology alias for $1;
+ aline alias for $2;
+ rec RECORD;
+ query text;
+ firstpoint geometry;
+ lastpoint geometry;
+ firstnode int;
+ lastnode int;
+ edgeid int;
+BEGIN
+ firstpoint = StartPoint(aline);
+ lastpoint = EndPoint(aline);
+
+ -- Add first and last point nodes (contained in NO face)
+ SELECT topology.ST_AddIsoNode(atopology, NULL, firstpoint) INTO firstnode;
+ SELECT topology.ST_AddIsoNode(atopology, NULL, lastpoint) INTO lastnode;
+
+ RAISE NOTICE ''First node: %, Last node: %'', firstnode, lastnode;
+
+ -- Add edge
+ SELECT topology.ST_AddIsoEdge(atopology, firstnode, lastnode, aline)
+ INTO edgeid;
+
+ RAISE NOTICE ''Edge: %'', edgeid;
+
+ RETURN edgeid;
+END
+'
+LANGUAGE 'plpgsql';
+--} TopoGeo_addLinestring
+
+--{
+-- TopoGeo_AddPolygon
+--
+-- Add a Polygon into a topology
+--
+CREATEFUNCTION topology.TopoGeo_AddPolygon(varchar, geometry)
+ RETURNS int AS
+'
+DECLARE
+ rec RECORD;
+ query text;
+BEGIN
+
+ RAISE EXCEPTION ''TopoGeo_AddPolygon not implemented yet'';
+END
+'
+LANGUAGE 'plpgsql';
+--} TopoGeo_AddPolygon
+
+--{
+-- CreateTopology(name, SRID, precision)
+--
+-- Create a topology schema, add a topology info record
+-- in the topology.topology relation, return it's numeric
+-- id.
+--
+CREATEFUNCTION topology.CreateTopology(varchar, integer, float8)
+RETURNS integer
+AS '
+DECLARE
+ atopology alias for $1;
+ srid alias for $2;
+ precision alias for $3;
+ rec RECORD;
+ topology_id integer;
+BEGIN
+
+-- FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+-- LOOP
+-- RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
+-- END LOOP;
+
+ ------ Fetch next id for the new topology
+ FOR rec IN SELECT nextval(''topology.topology_id_seq'')
+ LOOP
+ topology_id = rec.nextval;
+ END LOOP;
+
+
+ EXECUTE ''
+CREATE SCHEMA '' || quote_ident(atopology) || '';
+ '';
+
+ -------------{ face CREATION
+ EXECUTE
+ ''CREATE TABLE '' || quote_ident(atopology) || ''.face (''
+ || ''face_id SERIAL,''
+ || ''mbr BOX2D,''
+ || '' CONSTRAINT face_primary_key PRIMARY KEY(face_id)''
+ || '');'';
+ -------------} END OF face CREATION
+
+
+ --------------{ node CREATION
+
+ EXECUTE
+ ''CREATE TABLE '' || quote_ident(atopology) || ''.node (''
+ || ''node_id SERIAL,''
+ --|| ''geom GEOMETRY,''
+ || ''containing_face INTEGER,''
+
+ || ''CONSTRAINT node_primary_key PRIMARY KEY(node_id),''
+
+ --|| ''CONSTRAINT node_geometry_type CHECK ''
+ --|| ''( GeometryType(geom) = ''''POINT'''' ),''
+
+ || ''CONSTRAINT face_exists FOREIGN KEY(containing_face) ''
+ || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id)''
+
+ || '');'';
+
+ -- Add geometry column to the node table
+ EXECUTE
+ ''SELECT AddGeometryColumn(''||quote_literal(atopology)
+ ||'',''''node'''',''''geom'''',''||quote_literal(srid)
+ ||'',''''POINT'''',''''2'''')'';
+
+ --------------} END OF node CREATION
+
+ --------------{ edge CREATION
+
+ -- edge_data table
+ EXECUTE
+ ''CREATE TABLE '' || quote_ident(atopology) || ''.edge_data (''
+ || ''edge_id SERIAL NOT NULL PRIMARY KEY,''
+ || ''start_node INTEGER NOT NULL,''
+ || ''end_node INTEGER NOT NULL,''
+ || ''next_left_edge INTEGER NOT NULL,''
+ || ''abs_next_left_edge INTEGER NOT NULL,''
+ || ''next_right_edge INTEGER NOT NULL,''
+ || ''abs_next_right_edge INTEGER NOT NULL,''
+ || ''left_face INTEGER NOT NULL,''
+ || ''right_face INTEGER NOT NULL,''
+ --|| ''geom GEOMETRY NOT NULL,''
+
+ --|| ''CONSTRAINT edge_geometry_type CHECK ''
+ --|| ''( GeometryType(geom) = ''''LINESTRING'''' ),''
+
+ || ''CONSTRAINT start_node_exists FOREIGN KEY(start_node)''
+ || '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
+
+ || ''CONSTRAINT end_node_exists FOREIGN KEY(end_node) ''
+ || '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
+
+ || ''CONSTRAINT left_face_exists FOREIGN KEY(left_face) ''
+ || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
+
+ || ''CONSTRAINT right_face_exists FOREIGN KEY(right_face) ''
+ || ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
+
+ || ''CONSTRAINT next_left_edge_exists FOREIGN KEY(abs_next_left_edge)''
+ || '' REFERENCES '' || quote_ident(atopology)
+ || ''.edge_data(edge_id)''
+ || '' DEFERRABLE INITIALLY DEFERRED,''
+
+ || ''CONSTRAINT next_right_edge_exists ''
+ || ''FOREIGN KEY(abs_next_right_edge)''
+ || '' REFERENCES '' || quote_ident(atopology)
+ || ''.edge_data(edge_id) ''
+ || '' DEFERRABLE INITIALLY DEFERRED''
+ || '');'';
+
+ -- Add geometry column to the edge_data table
+ EXECUTE
+ ''SELECT AddGeometryColumn(''||quote_literal(atopology)
+ ||'',''''edge_data'''',''''geom'''',''||quote_literal(srid)
+ ||'',''''LINESTRING'''',''''2'''')'';
+
+
+ -- edge standard view (select rule)
+ EXECUTE ''CREATE VIEW '' || quote_ident(atopology)
+ || ''.edge AS SELECT ''
+ || '' edge_id,start_node, end_node, next_left_edge, ''
+ || '' next_right_edge, ''
+ || '' left_face, right_face, geom FROM ''
+ || quote_ident(atopology) || ''.edge_data'';
+
+ -- edge standard view (insert rule)
+ EXECUTE ''CREATE RULE edge_insert_rule AS ON INSERT ''
+ || ''TO '' || quote_ident(atopology)
+ || ''.edge DO INSTEAD ''
+ || '' INSERT into '' || quote_ident(atopology)
+ || ''.edge_data ''
+ || '' VALUES (NEW.edge_id, NEW.start_node, NEW.end_node, ''
+ || '' NEW.next_left_edge, abs(NEW.next_left_edge), ''
+ || '' NEW.next_right_edge, abs(NEW.next_right_edge), ''
+ || '' NEW.left_face, NEW.right_face, NEW.geom);'';
+
+ --------------} END OF edge CREATION
+
+ --------------{ layer sequence
+ EXECUTE ''CREATE SEQUENCE ''
+ || quote_ident(atopology) || ''.layer_id_seq;'';
+ --------------} layer sequence
+
+ --------------{ relation CREATION
+ --
+ EXECUTE
+ ''CREATE TABLE '' || quote_ident(atopology) || ''.relation (''
+ || '' topogeo_id integer NOT NULL, ''
+ || '' layer_id integer NOT NULL, ''
+ || '' element_id integer NOT NULL, ''
+ || '' element_type integer NOT NULL, ''
+ || '' UNIQUE(layer_id,topogeo_id,element_id,element_type));'';
+
+ EXECUTE
+ ''CREATE TRIGGER relation_integrity_checks ''
+ ||''BEFORE UPDATE OR INSERT ON ''
+ || quote_ident(atopology) || ''.relation FOR EACH ROW ''
+ || '' EXECUTE PROCEDURE topology.RelationTrigger(''
+ ||topology_id||'',''||quote_literal(atopology)||'')'';
+ --------------} END OF relation CREATION
+
+
+ ------- Default (world) face
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology) || ''.face(face_id) VALUES(0);'';
+
+ ------- GiST index on node
+ EXECUTE ''CREATE INDEX node_gist ON ''
+ || quote_ident(atopology)
+ || ''.node using gist (geom gist_geometry_ops);'';
+
+ ------- GiST index on edge
+ EXECUTE ''CREATE INDEX edge_gist ON ''
+ || quote_ident(atopology)
+ || ''.edge_data using gist (geom gist_geometry_ops);'';
+
+ ------- Add record to the "topology" metadata table
+ EXECUTE ''INSERT INTO topology.topology (id, name, srid, precision) ''
+ || '' VALUES ('' || quote_literal(topology_id) || '',''
+ || quote_literal(atopology) || '',''
+ || quote_literal(srid) || '','' || quote_literal(precision)
+ || '')'';
+
+ RETURN topology_id;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+-- wrappers for unspecified srid or precision
+
+CREATEFUNCTION topology.CreateTopology(varchar, integer)
+RETURNS integer AS
+' SELECT topology.CreateTopology($1, $2, -1); '
+LANGUAGE 'SQL' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.CreateTopology(varchar)
+RETURNS integer AS
+' SELECT topology.CreateTopology($1, -1, -1); '
+LANGUAGE 'SQL' _VOLATILE_STRICT;
+
+--} CreateTopology
+
+--{
+-- DropTopology(name)
+--
+-- Drops a topology schema getting rid of every dependent object.
+--
+CREATEFUNCTION topology.DropTopology(varchar)
+RETURNS text
+AS '
+DECLARE
+ atopology alias for $1;
+ topoid integer;
+ rec RECORD;
+BEGIN
+
+ -- Get topology id
+ SELECT id FROM topology.topology into topoid
+ WHERE name = atopology;
+
+
+ IF topoid IS NOT NULL THEN
+
+ RAISE NOTICE ''Dropping all layers from topology % (%)'',
+ atopology, topoid;
+
+ -- Drop all layers in the topology
+ FOR rec IN EXECUTE ''SELECT * FROM topology.layer WHERE ''
+ || '' topology_id = '' || topoid
+ LOOP
+
+ EXECUTE ''SELECT topology.DropTopoGeometryColumn(''
+ || quote_literal(rec.schema_name)
+ || '',''
+ || quote_literal(rec.table_name)
+ || '',''
+ || quote_literal(rec.feature_column)
+ || '')'';
+ END LOOP;
+
+ -- Delete record from topology.topology
+ EXECUTE ''DELETE FROM topology.topology WHERE id = ''
+ || topoid;
+
+ END IF;
+
+
+ -- Drop the schema (if it exists)
+ FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+ LOOP
+ EXECUTE ''DROP SCHEMA ''||quote_ident(atopology)||'' CASCADE'';
+ END LOOP;
+
+
+ RETURN ''Topology '' || quote_literal(atopology) || '' dropped'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} DropTopology
+
+--={ ----------------------------------------------------------------
+-- POSTGIS-SPECIFIC topology predicates
+--
+-- This part contains function NOT in the SQL/MM specification
+--
+---------------------------------------------------------------------
+
+--{
+-- Intersects(TopoGeometry, TopoGeometry)
+--
+CREATEFUNCTION topology.intersects(topology.TopoGeometry, topology.TopoGeometry)
+ RETURNS bool
+AS
+'
+DECLARE
+ tg1 alias for $1;
+ tg2 alias for $2;
+ tgbuf topology.TopoGeometry;
+ rec RECORD;
+ toponame varchar;
+ query text;
+BEGIN
+ IF tg1.topology_id != tg2.topology_id THEN
+ RAISE EXCEPTION ''Cannot compute intersection between TopoGeometries from different topologies'';
+ END IF;
+
+ -- Order TopoGeometries so that tg1 has less-or-same
+ -- dimensionality of tg1 (point,line,polygon,collection)
+ IF tg1.type > tg2.type THEN
+ tgbuf := tg2;
+ tg2 := tg1;
+ tg1 := tgbuf;
+ END IF;
+
+ --RAISE NOTICE ''tg1.id:% tg2.id:%'', tg1.id, tg2.id;
+ -- Geometry collection are not currently supported
+ IF tg2.type = 4 THEN
+ RAISE EXCEPTION ''GeometryCollection are not supported by intersects()'';
+ END IF;
+
+ -- Get topology name
+ SELECT name FROM topology.topology into toponame
+ WHERE id = tg1.topology_id;
+
+ -- Hierarchical TopoGeometries are not currently supported
+ query = ''SELECT level FROM topology.layer''
+ || '' WHERE ''
+ || '' topology_id = '' || tg1.topology_id
+ || '' AND ''
+ || ''( layer_id = '' || tg1.layer_id
+ || '' OR layer_id = '' || tg2.layer_id
+ || '' ) ''
+ || '' AND level > 0 '';
+
+ --RAISE NOTICE ''%'', query;
+
+ FOR rec IN EXECUTE query
+ LOOP
+ RAISE EXCEPTION ''Hierarchical TopoGeometries are not currently supported by intersects()'';
+ END LOOP;
+
+ IF tg1.type = 1 THEN -- [multi]point
+
+
+ IF tg2.type = 1 THEN -- point/point
+ ---------------------------------------------------------
+ --
+ -- Two [multi]point features intersect if they share
+ -- any Node
+ --
+ --
+ --
+ query =
+ ''SELECT a.topogeo_id FROM ''
+ || quote_ident(toponame) ||
+ ''.relation a, ''
+ || quote_ident(toponame) ||
+ ''.relation b ''
+ || ''WHERE a.layer_id = '' || tg1.layer_id
+ || '' AND b.layer_id = '' || tg2.layer_id
+ || '' AND a.topogeo_id = '' || tg1.id
+ || '' AND b.topogeo_id = '' || tg2.id
+ || '' AND a.element_id = b.element_id ''
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN TRUE; -- they share an element
+ END LOOP;
+ RETURN FALSE; -- no elements shared
+ --
+ ---------------------------------------------------------
+
+
+ ELSIF tg2.type = 2 THEN -- point/line
+ ---------------------------------------------------------
+ --
+ -- A [multi]point intersects a [multi]line if they share
+ -- any Node.
+ --
+ --
+ --
+ query =
+ ''SELECT a.topogeo_id FROM ''
+ || quote_ident(toponame) ||
+ ''.relation a, ''
+ || quote_ident(toponame) ||
+ ''.relation b, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e ''
+ || ''WHERE a.layer_id = '' || tg1.layer_id
+ || '' AND b.layer_id = '' || tg2.layer_id
+ || '' AND a.topogeo_id = '' || tg1.id
+ || '' AND b.topogeo_id = '' || tg2.id
+ || '' AND abs(b.element_id) = e.edge_id ''
+ || '' AND ( ''
+ || '' e.start_node = a.element_id ''
+ || '' OR ''
+ || '' e.end_node = a.element_id ''
+ || '' )''
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN TRUE; -- they share an element
+ END LOOP;
+ RETURN FALSE; -- no elements shared
+ --
+ ---------------------------------------------------------
+
+ ELSIF tg2.type = 3 THEN -- point/polygon
+ ---------------------------------------------------------
+ --
+ -- A [multi]point intersects a [multi]polygon if any
+ -- Node of the point is contained in any face of the
+ -- polygon OR ( is end_node or start_node of any edge
+ -- of any polygon face ).
+ --
+ -- We assume the Node-in-Face check is faster becasue
+ -- there will be less Faces then Edges in any polygon.
+ --
+ --
+ --
+ --
+ -- Check if any node is contained in a face
+ query =
+ ''SELECT n.node_id as id FROM ''
+ || quote_ident(toponame) ||
+ ''.relation r1, ''
+ || quote_ident(toponame) ||
+ ''.relation r2, ''
+ || quote_ident(toponame) ||
+ ''.node n ''
+ || ''WHERE r1.layer_id = '' || tg1.layer_id
+ || '' AND r2.layer_id = '' || tg2.layer_id
+ || '' AND r1.topogeo_id = '' || tg1.id
+ || '' AND r2.topogeo_id = '' || tg2.id
+ || '' AND n.node_id = r1.element_id ''
+ || '' AND r2.element_id = n.containing_face ''
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ --RAISE NOTICE ''Node % in polygon face'', rec.id;
+ RETURN TRUE; -- one (or more) nodes are
+ -- contained in a polygon face
+ END LOOP;
+
+ -- Check if any node is start or end of any polygon
+ -- face edge
+ query =
+ ''SELECT n.node_id as nid, e.edge_id as eid ''
+ || '' FROM ''
+ || quote_ident(toponame) ||
+ ''.relation r1, ''
+ || quote_ident(toponame) ||
+ ''.relation r2, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e, ''
+ || quote_ident(toponame) ||
+ ''.node n ''
+ || ''WHERE r1.layer_id = '' || tg1.layer_id
+ || '' AND r2.layer_id = '' || tg2.layer_id
+ || '' AND r1.topogeo_id = '' || tg1.id
+ || '' AND r2.topogeo_id = '' || tg2.id
+ || '' AND n.node_id = r1.element_id ''
+ || '' AND ( ''
+ || '' e.left_face = r2.element_id ''
+ || '' OR ''
+ || '' e.right_face = r2.element_id ''
+ || '' ) ''
+ || '' AND ( ''
+ || '' e.start_node = r1.element_id ''
+ || '' OR ''
+ || '' e.end_node = r1.element_id ''
+ || '' ) ''
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ --RAISE NOTICE ''Node % on edge % bound'', rec.nid, rec.eid;
+ RETURN TRUE; -- one node is start or end
+ -- of a face edge
+ END LOOP;
+
+ RETURN FALSE; -- no intersection
+ --
+ ---------------------------------------------------------
+
+ ELSIF tg2.type = 4 THEN -- point/collection
+ RAISE EXCEPTION ''Intersection point/collection not implemented yet'';
+
+ ELSE
+ RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+ END IF;
+
+ ELSIF tg1.type = 2 THEN -- [multi]line
+ IF tg2.type = 2 THEN -- line/line
+ ---------------------------------------------------------
+ --
+ -- A [multi]line intersects a [multi]line if they share
+ -- any Node.
+ --
+ --
+ --
+ query =
+ ''SELECT e1.start_node FROM ''
+ || quote_ident(toponame) ||
+ ''.relation r1, ''
+ || quote_ident(toponame) ||
+ ''.relation r2, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e1, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e2 ''
+ || ''WHERE r1.layer_id = '' || tg1.layer_id
+ || '' AND r2.layer_id = '' || tg2.layer_id
+ || '' AND r1.topogeo_id = '' || tg1.id
+ || '' AND r2.topogeo_id = '' || tg2.id
+ || '' AND abs(r1.element_id) = e1.edge_id ''
+ || '' AND abs(r2.element_id) = e2.edge_id ''
+ || '' AND ( ''
+ || '' e1.start_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.start_node = e2.end_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.end_node ''
+ || '' )''
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN TRUE; -- they share an element
+ END LOOP;
+ RETURN FALSE; -- no elements shared
+ --
+ ---------------------------------------------------------
+
+ ELSIF tg2.type = 3 THEN -- line/polygon
+ ---------------------------------------------------------
+ --
+ -- A [multi]line intersects a [multi]polygon if they share
+ -- any Node (touch-only case), or if any line edge has any
+ -- polygon face on the left or right (full-containment case
+ -- + edge crossing case).
+ --
+ --
+ -- E1 are line edges, E2 are polygon edges
+ -- R1 are line relations.
+ -- R2 are polygon relations.
+ -- R2.element_id are FACE ids
+ query =
+ ''SELECT e1.edge_id''
+ || '' FROM ''
+ || quote_ident(toponame) ||
+ ''.relation r1, ''
+ || quote_ident(toponame) ||
+ ''.relation r2, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e1, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e2 ''
+ || ''WHERE r1.layer_id = '' || tg1.layer_id
+ || '' AND r2.layer_id = '' || tg2.layer_id
+ || '' AND r1.topogeo_id = '' || tg1.id
+ || '' AND r2.topogeo_id = '' || tg2.id
+
+ -- E1 are line edges
+ || '' AND e1.edge_id = abs(r1.element_id) ''
+
+ -- E2 are face edges
+ || '' AND ( e2.left_face = r2.element_id ''
+ || '' OR e2.right_face = r2.element_id ) ''
+
+ || '' AND ( ''
+
+ -- Check if E1 have left-or-right face
+ -- being part of R2.element_id
+ || '' e1.left_face = r2.element_id ''
+ || '' OR ''
+ || '' e1.right_face = r2.element_id ''
+
+ -- Check if E1 share start-or-end node
+ -- with any E2.
+ || '' OR ''
+ || '' e1.start_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.start_node = e2.end_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.end_node ''
+
+ || '' ) ''
+
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN TRUE; -- either common node
+ -- or edge-in-face
+ END LOOP;
+
+ RETURN FALSE; -- no intersection
+ --
+ ---------------------------------------------------------
+
+ ELSIF tg2.type = 4 THEN -- line/collection
+ RAISE EXCEPTION ''Intersection line/collection not implemented yet'', tg1.type, tg2.type;
+
+ ELSE
+ RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+ END IF;
+
+
+ ELSIF tg1.type = 3 THEN -- [multi]polygon
+
+ IF tg2.type = 3 THEN -- polygon/polygon
+ ---------------------------------------------------------
+ --
+ -- A [multi]polygon intersects a [multi]polygon if they share
+ -- any Node (touch-only case), or if any face edge has any of the
+ -- other polygon face on the left or right (full-containment case
+ -- + edge crossing case).
+ --
+ --
+ -- E1 are poly1 edges.
+ -- E2 are poly2 edges
+ -- R1 are poly1 relations.
+ -- R2 are poly2 relations.
+ -- R1.element_id are poly1 FACE ids
+ -- R2.element_id are poly2 FACE ids
+ query =
+ ''SELECT e1.edge_id''
+ || '' FROM ''
+ || quote_ident(toponame) ||
+ ''.relation r1, ''
+ || quote_ident(toponame) ||
+ ''.relation r2, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e1, ''
+ || quote_ident(toponame) ||
+ ''.edge_data e2 ''
+ || ''WHERE r1.layer_id = '' || tg1.layer_id
+ || '' AND r2.layer_id = '' || tg2.layer_id
+ || '' AND r1.topogeo_id = '' || tg1.id
+ || '' AND r2.topogeo_id = '' || tg2.id
+
+ -- E1 are poly1 edges
+ || '' AND ( e1.left_face = r1.element_id ''
+ || '' OR e1.right_face = r1.element_id ) ''
+
+ -- E2 are poly2 edges
+ || '' AND ( e2.left_face = r2.element_id ''
+ || '' OR e2.right_face = r2.element_id ) ''
+
+ || '' AND ( ''
+
+ -- Check if any edge from a polygon face
+ -- has any of the other polygon face
+ -- on the left or right
+ || '' e1.left_face = r2.element_id ''
+ || '' OR ''
+ || '' e1.right_face = r2.element_id ''
+ || '' OR ''
+ || '' e2.left_face = r1.element_id ''
+ || '' OR ''
+ || '' e2.right_face = r1.element_id ''
+
+ -- Check if E1 share start-or-end node
+ -- with any E2.
+ || '' OR ''
+ || '' e1.start_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.start_node = e2.end_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.start_node ''
+ || '' OR ''
+ || '' e1.end_node = e2.end_node ''
+
+ || '' ) ''
+
+ || '' LIMIT 1'';
+ --RAISE NOTICE ''%'', query;
+ FOR rec IN EXECUTE query
+ LOOP
+ RETURN TRUE; -- either common node
+ -- or edge-in-face
+ END LOOP;
+
+ RETURN FALSE; -- no intersection
+ --
+ ---------------------------------------------------------
+
+ ELSIF tg2.type = 4 THEN -- polygon/collection
+ RAISE EXCEPTION ''Intersection poly/collection not implemented yet'', tg1.type, tg2.type;
+
+ ELSE
+ RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+ END IF;
+
+ ELSIF tg1.type = 4 THEN -- collection
+ IF tg2.type = 4 THEN -- collection/collection
+ RAISE EXCEPTION ''Intersection collection/collection not implemented yet'', tg1.type, tg2.type;
+ ELSE
+ RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+ END IF;
+
+ ELSE
+ RAISE EXCEPTION ''Invalid TopoGeometry type %'', tg1.type;
+ END IF;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} intersects(TopoGeometry, TopoGeometry)
+
+--{
+-- equals(TopoGeometry, TopoGeometry)
+--
+CREATEFUNCTION topology.equals(topology.TopoGeometry, topology.TopoGeometry)
+ RETURNS bool
+AS
+'
+DECLARE
+ tg1 alias for $1;
+ tg2 alias for $2;
+ rec RECORD;
+ toponame varchar;
+ query text;
+BEGIN
+
+ IF tg1.topology_id != tg2.topology_id THEN
+ RAISE EXCEPTION ''Cannot compare TopoGeometries from different topologies'';
+ END IF;
+
+ -- Not the same type, not equal
+ IF tg1.type != tg2.type THEN
+ RETURN FALSE;
+ END IF;
+
+ -- Geometry collection are not currently supported
+ IF tg2.type = 4 THEN
+ RAISE EXCEPTION ''GeometryCollection are not supported by equals()'';
+ END IF;
+
+ -- Get topology name
+ SELECT name FROM topology.topology into toponame
+ WHERE id = tg1.topology_id;
+
+ -- Two geometries are equal if they are composed by
+ -- the same TopoElements
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || '' topology.GetTopoGeomElements(''
+ || quote_literal(toponame) || '', ''
+ || tg1.layer_id || '','' || tg1.id || '') ''
+ || '' EXCEPT SELECT * FROM ''
+ || '' topology.GetTopogeomElements(''
+ || quote_literal(toponame) || '', ''
+ || tg2.layer_id || '','' || tg2.id || '');''
+ LOOP
+ RETURN FALSE;
+ END LOOP;
+
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || '' topology.GetTopoGeomElements(''
+ || quote_literal(toponame) || '', ''
+ || tg2.layer_id || '','' || tg2.id || '')''
+ || '' EXCEPT SELECT * FROM ''
+ || '' topology.GetTopogeomElements(''
+ || quote_literal(toponame) || '', ''
+ || tg1.layer_id || '','' || tg1.id || ''); ''
+ LOOP
+ RETURN FALSE;
+ END LOOP;
+ RETURN TRUE;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} equals(TopoGeometry, TopoGeometry)
+
+--=} POSTGIS-SPECIFIC topology predicates
+
+
+--=} POSTGIS-SPECIFIC block
+
+--={ ----------------------------------------------------------------
+-- SQL/MM block
+--
+-- This part contains function in the SQL/MM specification
+--
+---------------------------------------------------------------------
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.5
+--
+-- ST_GetFaceEdges(atopology, aface)
+--
+--
+--
+CREATEFUNCTION topology.ST_GetFaceEdges(varchar, integer)
+ RETURNS setof topology.GetFaceEdges_ReturnType
+AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ aface ALIAS FOR $2;
+ rec RECORD;
+BEGIN
+ --
+ -- Atopology and aface are required
+ --
+ IF atopology IS NULL OR aface IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ RAISE EXCEPTION
+ ''ST_GetFaceEdges: not implemented yet'';
+
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_GetFaceEdges
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.16
+--
+-- ST_GetFaceGeometry(atopology, aface)
+--
+CREATEFUNCTION topology.ST_GetFaceGeometry(varchar, integer)
+ RETURNS GEOMETRY AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ aface ALIAS FOR $2;
+ rec RECORD;
+BEGIN
+ --
+ -- Atopology and aface are required
+ --
+ IF atopology IS NULL OR aface IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Construct face
+ --
+ FOR rec IN EXECUTE ''SELECT polygonize(geom) FROM ( SELECT geom FROM ''
+ || quote_ident(atopology)
+ || ''.edge WHERE left_face = '' || aface ||
+ '' OR right_face = '' || aface || '') as foo''
+ LOOP
+ RETURN geometryN(rec.polygonize, 1);
+ --RETURN rec.polygonize;
+ END LOOP;
+
+
+ --
+ -- No face found
+ --
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent face.'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_GetFaceGeometry
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.1
+--
+-- ST_AddIsoNode(atopology, aface, apoint)
+--
+CREATEFUNCTION topology.ST_AddIsoNode(varchar, integer, geometry)
+ RETURNS INTEGER AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ aface ALIAS FOR $2;
+ apoint ALIAS FOR $3;
+ rec RECORD;
+ nodeid integer;
+BEGIN
+
+ --
+ -- Atopology and apoint are required
+ --
+ IF atopology IS NULL OR apoint IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Apoint must be a point
+ --
+ IF substring(geometrytype(apoint), 1, 5) != ''POINT''
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - invalid point'';
+ END IF;
+
+ --
+ -- Check if a coincident node already exists
+ --
+ -- We use index AND x/y equality
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node '' ||
+ ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+ ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+ ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - coincident node'';
+ END LOOP;
+
+ --
+ -- Check if any edge crosses (intersects) this node
+ -- I used _intersects_ here to include boundaries (endpoints)
+ --
+ FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+ || quote_ident(atopology) || ''.edge ''
+ || ''WHERE geom && '' || quote_literal(apoint)
+ || '' AND intersects(geom, '' || quote_literal(apoint)
+ || '')''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - edge crosses node.'';
+ END LOOP;
+
+
+ --
+ -- Verify that aface contains apoint
+ -- if aface is null no check is done
+ --
+ IF aface IS NOT NULL THEN
+
+ FOR rec IN EXECUTE ''SELECT within(''
+ || quote_literal(apoint) || ''::geometry,
+ topology.ST_GetFaceGeometry(''
+ || quote_literal(atopology) || '', '' || aface ||
+ ''))''
+ LOOP
+ IF rec.within = ''f'' THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not within face'';
+ ELSIF rec.within IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent face'';
+ END IF;
+ END LOOP;
+
+ END IF;
+
+ --
+ -- Get new node id from sequence
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.node_node_id_seq'''')''
+ LOOP
+ nodeid = rec.nextval;
+ END LOOP;
+
+ --
+ -- Insert the new row
+ --
+ IF aface IS NOT NULL THEN
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.node(node_id, geom, containing_face)
+ VALUES(''||nodeid||'',''||quote_literal(apoint)||
+ '',''||aface||'')'';
+ ELSE
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.node(node_id, geom)
+ VALUES(''||nodeid||'',''||quote_literal(apoint)||
+ '')'';
+ END IF;
+
+ RETURN nodeid;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.2
+--
+-- ST_MoveIsoNode(atopology, anode, apoint)
+--
+CREATEFUNCTION topology.ST_MoveIsoNode(varchar, integer, geometry)
+ RETURNS TEXT AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anode ALIAS FOR $2;
+ apoint ALIAS FOR $3;
+ rec RECORD;
+BEGIN
+
+ --
+ -- All arguments are required
+ --
+ IF atopology IS NULL OR anode IS NULL OR apoint IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Apoint must be a point
+ --
+ IF substring(geometrytype(apoint), 1, 5) != ''POINT''
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - invalid point'';
+ END IF;
+
+ --
+ -- Check node isolation.
+ --
+ FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+ || quote_ident(atopology) || ''.edge '' ||
+ '' WHERE start_node = '' || anode ||
+ '' OR end_node = '' || anode
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not isolated node'';
+ END LOOP;
+
+ --
+ -- Check if a coincident node already exists
+ --
+ -- We use index AND x/y equality
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node '' ||
+ ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+ ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+ ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - coincident node'';
+ END LOOP;
+
+ --
+ -- Check if any edge crosses (intersects) this node
+ -- I used _intersects_ here to include boundaries (endpoints)
+ --
+ FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+ || quote_ident(atopology) || ''.edge ''
+ || ''WHERE geom && '' || quote_literal(apoint)
+ || '' AND intersects(geom, '' || quote_literal(apoint)
+ || '')''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - edge crosses node.'';
+ END LOOP;
+
+ --
+ -- Update node point
+ --
+ EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.node ''
+ || '' SET geom = '' || quote_literal(apoint)
+ || '' WHERE node_id = '' || anode;
+
+ RETURN ''Isolated Node '' || anode || '' moved to location ''
+ || x(apoint) || '','' || y(apoint);
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_MoveIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.3
+--
+-- ST_RemoveIsoNode(atopology, anode)
+--
+CREATEFUNCTION topology.ST_RemoveIsoNode(varchar, integer)
+ RETURNS TEXT AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anode ALIAS FOR $2;
+ rec RECORD;
+BEGIN
+
+ --
+ -- Atopology and apoint are required
+ --
+ IF atopology IS NULL OR anode IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Check node isolation.
+ --
+ FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+ || quote_ident(atopology) || ''.edge_data '' ||
+ '' WHERE start_node = '' || anode ||
+ '' OR end_node = '' || anode
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not isolated node'';
+ END LOOP;
+
+ EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.node ''
+ || '' WHERE node_id = '' || anode;
+
+ RETURN ''Isolated node '' || anode || '' removed'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_RemoveIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.7
+--
+-- ST_RemoveIsoEdge(atopology, anedge)
+--
+CREATEFUNCTION topology.ST_RemoveIsoEdge(varchar, integer)
+ RETURNS TEXT AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anedge ALIAS FOR $2;
+ edge RECORD;
+ rec RECORD;
+ ok BOOL;
+BEGIN
+
+ --
+ -- Atopology and anedge are required
+ --
+ IF atopology IS NULL OR anedge IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Check node existance
+ --
+ ok = false;
+ FOR edge IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data '' ||
+ '' WHERE edge_id = '' || anedge
+ LOOP
+ ok = true;
+ END LOOP;
+ IF NOT ok THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent edge'';
+ END IF;
+
+ --
+ -- Check node isolation
+ --
+ IF edge.left_face != edge.right_face THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not isolated edge'';
+ END IF;
+
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data ''
+ || '' WHERE edge_id != '' || anedge
+ || '' AND ( start_node = '' || edge.start_node
+ || '' OR start_node = '' || edge.end_node
+ || '' OR end_node = '' || edge.start_node
+ || '' OR end_node = '' || edge.end_node
+ || '' ) ''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not isolated edge'';
+ END LOOP;
+
+ --
+ -- Delete the edge
+ --
+ EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
+ || '' WHERE edge_id = '' || anedge;
+
+ RETURN ''Isolated edge '' || anedge || '' removed'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_RemoveIsoEdge
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.8
+--
+-- ST_NewEdgesSplit(atopology, anedge, apoint)
+--
+CREATEFUNCTION topology.ST_NewEdgesSplit(varchar, integer, geometry)
+ RETURNS INTEGER AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anedge ALIAS FOR $2;
+ apoint ALIAS FOR $3;
+ oldedge RECORD;
+ rec RECORD;
+ tmp integer;
+ topoid integer;
+ nodeid integer;
+ nodepos float8;
+ edgeid1 integer;
+ edgeid2 integer;
+ edge1 geometry;
+ edge2 geometry;
+ ok BOOL;
+BEGIN
+
+ --
+ -- All args required
+ --
+ IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Check node existance
+ --
+ ok = false;
+ FOR oldedge IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data '' ||
+ '' WHERE edge_id = '' || anedge
+ LOOP
+ ok = true;
+ END LOOP;
+ IF NOT ok THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent edge'';
+ END IF;
+
+ --
+ -- Check that given point is Within(anedge.geom)
+ --
+ IF NOT within(apoint, oldedge.geom) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - point not on edge'';
+ END IF;
+
+ --
+ -- Check if a coincident node already exists
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node '' ||
+ ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+ ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+ ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - coincident node'';
+ END LOOP;
+
+ --
+ -- Get new node id
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.node_node_id_seq'''')''
+ LOOP
+ nodeid = rec.nextval;
+ END LOOP;
+
+ --RAISE NOTICE ''Next node id = % '', nodeid;
+
+ --
+ -- Add the new node
+ --
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.node(node_id, geom)
+ VALUES(''||nodeid||'',''||quote_literal(apoint)||
+ '')'';
+
+ --
+ -- Delete the old edge
+ --
+ EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
+ || '' WHERE edge_id = '' || anedge;
+
+ --
+ -- Compute new edges
+ --
+ nodepos = line_locate_point(oldedge.geom, apoint);
+ edge1 = line_substring(oldedge.geom, 0, nodepos);
+ edge2 = line_substring(oldedge.geom, nodepos, 1);
+
+ --
+ -- Get ids for the new edges
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.edge_data_edge_id_seq'''')''
+ LOOP
+ edgeid1 = rec.nextval;
+ END LOOP;
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.edge_data_edge_id_seq'''')''
+ LOOP
+ edgeid2 = rec.nextval;
+ END LOOP;
+
+ --RAISE NOTICE ''EdgeId1 % EdgeId2 %'', edgeid1, edgeid2;
+
+ --
+ -- Insert the two new edges
+ --
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.edge VALUES(''
+ ||edgeid1||'',''||oldedge.start_node
+ ||'',''||nodeid
+ ||'',''||edgeid2
+ ||'',''||oldedge.next_right_edge
+ ||'',''||oldedge.left_face
+ ||'',''||oldedge.right_face
+ ||'',''||quote_literal(edge1)
+ ||'')'';
+
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.edge VALUES(''
+ ||edgeid2||'',''||nodeid
+ ||'',''||oldedge.end_node
+ ||'',''||oldedge.next_left_edge
+ ||'',-''||edgeid1
+ ||'',''||oldedge.left_face
+ ||'',''||oldedge.right_face
+ ||'',''||quote_literal(edge2)
+ ||'')'';
+
+ --
+ -- Update all next edge references to match new layout
+ --
+
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET next_right_edge = ''
+ || edgeid2
+ || '',''
+ || '' abs_next_right_edge = '' || edgeid2
+ || '' WHERE next_right_edge = '' || anedge;
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET next_right_edge = ''
+ || -edgeid1
+ || '',''
+ || '' abs_next_right_edge = '' || edgeid1
+ || '' WHERE next_right_edge = '' || -anedge;
+
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET next_left_edge = ''
+ || edgeid1
+ || '',''
+ || '' abs_next_left_edge = '' || edgeid1
+ || '' WHERE next_left_edge = '' || anedge;
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET ''
+ || '' next_left_edge = '' || -edgeid2
+ || '',''
+ || '' abs_next_left_edge = '' || edgeid2
+ || '' WHERE next_left_edge = '' || -anedge;
+
+ -- Get topology id
+ SELECT id FROM topology.topology into topoid
+ WHERE name = atopology;
+
+ --
+ -- Update references in the Relation table.
+ -- We only take into considerations non-hierarchical
+ -- TopoGeometry here, for obvious reasons.
+ --
+ FOR rec IN EXECUTE ''SELECT r.* FROM ''
+ || quote_ident(atopology)
+ || ''.relation r, topology.layer l ''
+ || '' WHERE ''
+ || '' l.topology_id = '' || topoid
+ || '' AND l.level = 0 ''
+ || '' AND l.layer_id = r.layer_id ''
+ || '' AND abs(r.element_id) = '' || anedge
+ || '' AND r.element_type = 2''
+ LOOP
+ --RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split'', rec.topogeo_id, rec.layer_id;
+
+ -- Delete old reference
+ EXECUTE ''DELETE FROM '' || quote_ident(atopology)
+ || ''.relation ''
+ || '' WHERE ''
+ || ''layer_id = '' || rec.layer_id
+ || '' AND ''
+ || ''topogeo_id = '' || rec.topogeo_id
+ || '' AND ''
+ || ''element_type = '' || rec.element_type
+ || '' AND ''
+ || ''abs(element_id) = '' || anedge;
+
+ -- Add new reference to edge1
+ IF rec.element_id < 0 THEN
+ tmp = -edgeid1;
+ ELSE
+ tmp = edgeid1;
+ END IF;
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.relation ''
+ || '' VALUES( ''
+ || rec.topogeo_id
+ || '',''
+ || rec.layer_id
+ || '',''
+ || tmp
+ || '',''
+ || rec.element_type
+ || '')'';
+
+ -- Add new reference to edge2
+ IF rec.element_id < 0 THEN
+ tmp = -edgeid2;
+ ELSE
+ tmp = edgeid2;
+ END IF;
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.relation ''
+ || '' VALUES( ''
+ || rec.topogeo_id
+ || '',''
+ || rec.layer_id
+ || '',''
+ || tmp
+ || '',''
+ || rec.element_type
+ || '')'';
+
+ END LOOP;
+
+ --RAISE NOTICE ''Edge % split in edges % and % by node %'',
+ -- anedge, edgeid1, edgeid2, nodeid;
+
+ RETURN nodeid;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_NewEdgesSplit
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.9
+--
+-- ST_ModEdgesSplit(atopology, anedge, apoint)
+--
+CREATEFUNCTION topology.ST_ModEdgesSplit(varchar, integer, geometry)
+ RETURNS INTEGER AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anedge ALIAS FOR $2;
+ apoint ALIAS FOR $3;
+ oldedge RECORD;
+ rec RECORD;
+ tmp integer;
+ topoid integer;
+ nodeid integer;
+ nodepos float8;
+ newedgeid integer;
+ newedge1 geometry;
+ newedge2 geometry;
+ query text;
+ ok BOOL;
+BEGIN
+
+ --
+ -- All args required
+ --
+ IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Check node existance
+ --
+ ok = false;
+ FOR oldedge IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data '' ||
+ '' WHERE edge_id = '' || anedge
+ LOOP
+ ok = true;
+ END LOOP;
+ IF NOT ok THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent edge'';
+ END IF;
+
+ --
+ -- Check that given point is Within(anedge.geom)
+ --
+ IF NOT within(apoint, oldedge.geom) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - point not on edge'';
+ END IF;
+
+ --
+ -- Check if a coincident node already exists
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node '' ||
+ ''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+ ||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+ ||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - coincident node'';
+ END LOOP;
+
+ --
+ -- Get new node id
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.node_node_id_seq'''')''
+ LOOP
+ nodeid = rec.nextval;
+ END LOOP;
+
+ --RAISE NOTICE ''Next node id = % '', nodeid;
+
+ --
+ -- Add the new node
+ --
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.node(node_id, geom)
+ VALUES(''||nodeid||'',''||quote_literal(apoint)||
+ '')'';
+
+ --
+ -- Compute new edge
+ --
+ nodepos = line_locate_point(oldedge.geom, apoint);
+ newedge1 = line_substring(oldedge.geom, 0, nodepos);
+ newedge2 = line_substring(oldedge.geom, nodepos, 1);
+
+
+ --
+ -- Get ids for the new edge
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.edge_data_edge_id_seq'''')''
+ LOOP
+ newedgeid = rec.nextval;
+ END LOOP;
+
+ --
+ -- Insert the new edge
+ --
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.edge ''
+ || ''(edge_id, start_node, end_node,''
+ || ''next_left_edge, next_right_edge,''
+ || ''left_face, right_face, geom) ''
+ || ''VALUES(''
+ ||newedgeid||'',''||nodeid
+ ||'',''||oldedge.end_node
+ ||'',''||oldedge.next_left_edge
+ ||'',-''||anedge
+ ||'',''||oldedge.left_face
+ ||'',''||oldedge.right_face
+ ||'',''||quote_literal(newedge2)
+ ||'')'';
+
+ --
+ -- Update the old edge
+ --
+ EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
+ || '' SET geom = '' || quote_literal(newedge1)
+ || '',''
+ || '' next_left_edge = '' || newedgeid
+ || '',''
+ || '' end_node = '' || nodeid
+ || '' WHERE edge_id = '' || anedge;
+
+
+ --
+ -- Update all next edge references to match new layout
+ --
+
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET next_right_edge = ''
+ || -newedgeid
+ || '',''
+ || '' abs_next_right_edge = '' || newedgeid
+ || '' WHERE next_right_edge = '' || -anedge;
+
+ EXECUTE ''UPDATE '' || quote_ident(atopology)
+ || ''.edge_data SET ''
+ || '' next_left_edge = '' || -newedgeid
+ || '',''
+ || '' abs_next_left_edge = '' || newedgeid
+ || '' WHERE next_left_edge = '' || -anedge;
+
+ -- Get topology id
+ SELECT id FROM topology.topology into topoid
+ WHERE name = atopology;
+
+ --
+ -- Update references in the Relation table.
+ -- We only take into considerations non-hierarchical
+ -- TopoGeometry here, for obvious reasons.
+ --
+ FOR rec IN EXECUTE ''SELECT r.* FROM ''
+ || quote_ident(atopology)
+ || ''.relation r, topology.layer l ''
+ || '' WHERE ''
+ || '' l.topology_id = '' || topoid
+ || '' AND l.level = 0 ''
+ || '' AND l.layer_id = r.layer_id ''
+ || '' AND abs(r.element_id) = '' || anedge
+ || '' AND r.element_type = 2''
+ LOOP
+ --RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split (%) - updating to add new edge %'', rec.topogeo_id, rec.layer_id, anedge, newedgeid;
+
+ -- Add new reference to edge1
+ IF rec.element_id < 0 THEN
+ tmp = -newedgeid;
+ ELSE
+ tmp = newedgeid;
+ END IF;
+ query = ''INSERT INTO '' || quote_ident(atopology)
+ || ''.relation ''
+ || '' VALUES( ''
+ || rec.topogeo_id
+ || '',''
+ || rec.layer_id
+ || '',''
+ || tmp
+ || '',''
+ || rec.element_type
+ || '')'';
+
+ --RAISE NOTICE ''%'', query;
+ EXECUTE query;
+ END LOOP;
+
+ --RAISE NOTICE ''Edge % split in edges % and % by node %'',
+ -- anedge, anedge, newedgeid, nodeid;
+
+ RETURN nodeid;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_ModEdgesSplit
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.4
+--
+-- ST_AddIsoEdge(atopology, anode, anothernode, acurve)
+--
+CREATEFUNCTION topology.ST_AddIsoEdge(varchar, integer, integer, geometry)
+ RETURNS INTEGER AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anode ALIAS FOR $2;
+ anothernode ALIAS FOR $3;
+ acurve ALIAS FOR $4;
+ aface INTEGER;
+ face GEOMETRY;
+ snodegeom GEOMETRY;
+ enodegeom GEOMETRY;
+ count INTEGER;
+ rec RECORD;
+ edgeid INTEGER;
+BEGIN
+
+ --
+ -- All arguments required
+ --
+ IF atopology IS NULL
+ OR anode IS NULL
+ OR anothernode IS NULL
+ OR acurve IS NULL
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Acurve must be a LINESTRING
+ --
+ IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - invalid curve'';
+ END IF;
+
+ --
+ -- Acurve must be a simple
+ --
+ IF NOT issimple(acurve)
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - curve not simple'';
+ END IF;
+
+ --
+ -- Check for:
+ -- existence of nodes
+ -- nodes faces match
+ -- Extract:
+ -- nodes face id
+ -- nodes geoms
+ --
+ aface := NULL;
+ count := 0;
+ FOR rec IN EXECUTE ''SELECT geom, containing_face, node_id FROM ''
+ || quote_ident(atopology) || ''.node
+ WHERE node_id = '' || anode ||
+ '' OR node_id = '' || anothernode
+ LOOP
+ IF count > 0 AND aface != rec.containing_face THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - nodes in different faces'';
+ ELSE
+ aface := rec.containing_face;
+ END IF;
+
+ -- Get nodes geom
+ IF rec.node_id = anode THEN
+ snodegeom = rec.geom;
+ ELSE
+ enodegeom = rec.geom;
+ END IF;
+
+ count = count+1;
+
+ END LOOP;
+ IF count < 2 THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent node'';
+ END IF;
+
+
+ --
+ -- Check nodes isolation.
+ --
+ FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+ || quote_ident(atopology) || ''.edge_data '' ||
+ '' WHERE start_node = '' || anode ||
+ '' OR end_node = '' || anode ||
+ '' OR start_node = '' || anothernode ||
+ '' OR end_node = '' || anothernode
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - not isolated node'';
+ END LOOP;
+
+ --
+ -- Check acurve to be within endpoints containing face
+ -- (unless it is the world face, I suppose)
+ --
+ IF aface IS NOT NULL THEN
+
+ --
+ -- Extract endpoints face geometry
+ --
+ FOR rec IN EXECUTE ''SELECT topology.ST_GetFaceGeometry(''
+ || quote_literal(atopology) ||
+ '','' || aface || '') as face''
+ LOOP
+ face := rec.face;
+ END LOOP;
+
+ --
+ -- Check acurve to be within face
+ --
+ IF ! within(acurve, face) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry not within face.'';
+ END IF;
+
+ END IF;
+
+ --
+ -- l) Check that start point of acurve match start node
+ -- geoms.
+ --
+ IF x(snodegeom) != x(StartPoint(acurve)) OR
+ y(snodegeom) != y(StartPoint(acurve)) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - start node not geometry start point.'';
+ END IF;
+
+ --
+ -- m) Check that end point of acurve match end node
+ -- geoms.
+ --
+ IF x(enodegeom) != x(EndPoint(acurve)) OR
+ y(enodegeom) != y(EndPoint(acurve)) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - end node not geometry end point.'';
+ END IF;
+
+ --
+ -- n) Check if curve crosses (contains) any node
+ -- I used _contains_ here to leave endpoints out
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node ''
+ || '' WHERE geom && '' || quote_literal(acurve)
+ || '' AND contains('' || quote_literal(acurve)
+ || '',geom)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry crosses a node'';
+ END LOOP;
+
+ --
+ -- o) Check if curve intersects any other edge
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data
+ WHERE geom && '' || quote_literal(acurve) || ''::geometry
+ AND intersects(geom, '' || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry intersects an edge'';
+ END LOOP;
+
+ --
+ -- Get new edge id from sequence
+ --
+ FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+ atopology || ''.edge_data_edge_id_seq'''')''
+ LOOP
+ edgeid = rec.nextval;
+ END LOOP;
+
+ --
+ -- Insert the new row
+ --
+ IF aface IS NULL THEN aface := 0; END IF;
+
+ EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+ || ''.edge VALUES(''||edgeid||'',''||anode||
+ '',''||anothernode||'',''
+ ||(-edgeid)||'',''||edgeid||'',''
+ ||aface||'',''||aface||'',''
+ ||quote_literal(acurve)||'')'';
+
+ RETURN edgeid;
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddIsoEdge
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.6
+--
+-- ST_ChangeEdgeGeom(atopology, anedge, acurve)
+--
+CREATEFUNCTION topology.ST_ChangeEdgeGeom(varchar, integer, geometry)
+ RETURNS TEXT AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anedge ALIAS FOR $2;
+ acurve ALIAS FOR $3;
+ aface INTEGER;
+ face GEOMETRY;
+ snodegeom GEOMETRY;
+ enodegeom GEOMETRY;
+ count INTEGER;
+ rec RECORD;
+ edgeid INTEGER;
+ oldedge RECORD;
+BEGIN
+
+ --
+ -- All arguments required
+ --
+ IF atopology IS NULL
+ OR anedge IS NULL
+ OR acurve IS NULL
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Acurve must be a LINESTRING
+ --
+ IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - invalid curve'';
+ END IF;
+
+ --
+ -- Acurve must be a simple
+ --
+ IF NOT issimple(acurve)
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - curve not simple'';
+ END IF;
+
+ --
+ -- e) Check StartPoint consistency
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data e, ''
+ || quote_ident(atopology) || ''.node n ''
+ || '' WHERE e.edge_id = '' || anedge
+ || '' AND n.node_id = e.start_node ''
+ || '' AND ( x(n.geom) != ''
+ || x(StartPoint(acurve))
+ || '' OR y(n.geom) != ''
+ || y(StartPoint(acurve))
+ || '')''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - start node not geometry start point.'';
+ END LOOP;
+
+ --
+ -- f) Check EndPoint consistency
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data e, ''
+ || quote_ident(atopology) || ''.node n ''
+ || '' WHERE e.edge_id = '' || anedge
+ || '' AND n.node_id = e.end_node ''
+ || '' AND ( x(n.geom) != ''
+ || x(EndPoint(acurve))
+ || '' OR y(n.geom) != ''
+ || y(EndPoint(acurve))
+ || '')''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - end node not geometry end point.'';
+ END LOOP;
+
+ --
+ -- g) Check if curve crosses any node
+ -- _within_ used to let endpoints out
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node
+ WHERE geom && '' || quote_literal(acurve) || ''::geometry
+ AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry crosses a node'';
+ END LOOP;
+
+ --
+ -- h) Check if curve intersects any other edge
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data ''
+ || '' WHERE edge_id != '' || anedge
+ || '' AND geom && ''
+ || quote_literal(acurve) || ''::geometry ''
+ || '' AND intersects(geom, ''
+ || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry intersects an edge'';
+ END LOOP;
+
+ --
+ -- Update edge geometry
+ --
+ EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
+ || '' SET geom = '' || quote_literal(acurve)
+ || '' WHERE edge_id = '' || anedge;
+
+ RETURN ''Edge '' || anedge || '' changed'';
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_ChangeEdgeGeom
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.12
+--
+-- ST_AddEdgeNewFaces(atopology, anode, anothernode, acurve)
+--
+CREATEFUNCTION topology.ST_AddEdgeNewFaces(varchar, integer, integer, geometry)
+ RETURNS INTEGER AS
+'
+DECLARE
+ atopology ALIAS FOR $1;
+ anode ALIAS FOR $2;
+ anothernode ALIAS FOR $3;
+ acurve ALIAS FOR $4;
+ rec RECORD;
+ i INTEGER;
+ az FLOAT8;
+ azdif FLOAT8;
+ myaz FLOAT8;
+ minazimuth FLOAT8;
+ maxazimuth FLOAT8;
+ p2 GEOMETRY;
+BEGIN
+
+ --
+ -- All args required
+ --
+ IF atopology IS NULL
+ OR anode IS NULL
+ OR anothernode IS NULL
+ OR acurve IS NULL
+ THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ --
+ -- Acurve must be a LINESTRING
+ --
+ IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - invalid curve'';
+ END IF;
+
+ --
+ -- Curve must be simple
+ --
+ IF NOT issimple(acurve) THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - curve not simple'';
+ END IF;
+
+ --
+ -- Check endpoints existance and match with Curve geometry
+ --
+ i=0;
+ FOR rec IN EXECUTE ''SELECT ''
+ || '' CASE WHEN node_id = '' || anode
+ || '' THEN 1 WHEN node_id = '' || anothernode
+ || '' THEN 0 END AS start, geom FROM ''
+ || quote_ident(atopology)
+ || ''.node ''
+ || '' WHERE node_id IN ( ''
+ || anode || '','' || anothernode
+ || '')''
+ LOOP
+ IF rec.start THEN
+ IF NOT Equals(rec.geom, StartPoint(acurve))
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - start node not geometry start point.'';
+ END IF;
+ ELSE
+ IF NOT Equals(rec.geom, EndPoint(acurve))
+ THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - end node not geometry end point.'';
+ END IF;
+ END IF;
+
+ i=i+1;
+ END LOOP;
+
+ IF i < 2 THEN
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - non-existent node'';
+ END IF;
+
+ --
+ -- Check if this geometry crosses any node
+ --
+ FOR rec IN EXECUTE ''SELECT node_id FROM ''
+ || quote_ident(atopology) || ''.node
+ WHERE geom && '' || quote_literal(acurve) || ''::geometry
+ AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry crosses a node'';
+ END LOOP;
+
+ --
+ -- Check if this geometry crosses any existing edge
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data
+ WHERE geom && '' || quote_literal(acurve) || ''::geometry
+ AND crosses(geom, '' || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - geometry crosses an edge'';
+ END LOOP;
+
+ --
+ -- Check if another edge share this edge endpoints
+ --
+ FOR rec IN EXECUTE ''SELECT * FROM ''
+ || quote_ident(atopology) || ''.edge_data ''
+ || '' WHERE ''
+ || '' geom && '' || quote_literal(acurve) || ''::geometry ''
+ || '' AND ''
+ || '' ( (''
+ || '' start_node = '' || anode
+ || '' AND ''
+ || '' end_node = '' || anothernode
+ || '' ) OR ( ''
+ || '' end_node = '' || anode
+ || '' AND ''
+ || '' start_node = '' || anothernode
+ || '' ) )''
+ || '' AND ''
+ || ''equals(geom,'' || quote_literal(acurve) || ''::geometry)''
+ LOOP
+ RAISE EXCEPTION
+ ''SQL/MM Spatial exception - coincident edge'';
+ END LOOP;
+
+ ---------------------------------------------------------------
+ --
+ -- All checks passed, time to extract informations about
+ -- endpoints:
+ --
+ -- next_left_edge
+ -- next_right_edge
+ -- left_face
+ -- right_face
+ --
+ ---------------------------------------------------------------
+
+ --
+ --
+ -- Compute next_left_edge
+ --
+ -- We fetch all edges with an endnode equal to
+ -- this edge end_node (anothernode).
+ -- For each edge we compute azimuth of the segment(s).
+ -- Of interest are the edges with closest (smaller
+ -- and bigger) azimuths then the azimuth of
+ -- this edge last segment.
+ --
+
+ myaz = azimuth(EndPoint(acurve), PointN(acurve, NumPoints(acurve)-1));
+ RAISE NOTICE ''My end-segment azimuth: %'', myaz;
+ FOR rec IN EXECUTE ''SELECT ''
+ || ''edge_id, end_node, start_node, geom''
+ || '' FROM ''
+ || quote_ident(atopology)
+ || ''.edge_data ''
+ || '' WHERE ''
+ || '' end_node = '' || anothernode
+ || '' OR ''
+ || '' start_node = '' || anothernode
+ LOOP
+
+ IF rec.start_node = anothernode THEN
+ --
+ -- Edge starts at our node, we compute
+ -- azimuth from node to its second point
+ --
+ az = azimuth(EndPoint(acurve),
+ PointN(rec.geom, 2));
+
+ RAISE NOTICE ''Edge % starts at node % - azimuth %'',
+ rec.edge_id, rec.start_node, az;
+ END IF;
+
+ IF rec.end_node = anothernode THEN
+ --
+ -- Edge ends at our node, we compute
+ -- azimuth from node to its second-last point
+ --
+ az = azimuth(EndPoint(acurve),
+ PointN(rec.geom, NumPoints(rec.geom)-1));
+
+ RAISE NOTICE ''Edge % ends at node % - azimuth %'',
+ rec.edge_id, rec.end_node, az;
+ END IF;
+ END LOOP;
+
+
+ RAISE EXCEPTION ''Not implemented yet'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddEdgeNewFaces
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.17
+--
+-- ST_InitTopoGeo(atopology)
+--
+CREATEFUNCTION topology.ST_InitTopoGeo(varchar)
+RETURNS text
+AS '
+DECLARE
+ atopology alias for $1;
+ rec RECORD;
+ topology_id numeric;
+BEGIN
+ IF atopology IS NULL THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+ LOOP
+ RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
+ END LOOP;
+
+ FOR rec IN EXECUTE ''SELECT topology.CreateTopology(''
+ ||quote_literal(atopology)|| '') as id''
+ LOOP
+ topology_id := rec.id;
+ END LOOP;
+
+ RETURN ''Topology-Geometry '' || quote_literal(atopology)
+ || '' (id:'' || topology_id || '') created. '';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_InitTopoGeo
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.18
+--
+-- ST_CreateTopoGeo(atopology, acollection)
+--
+CREATEFUNCTION topology.ST_CreateTopoGeo(varchar, geometry)
+RETURNS text
+AS '
+DECLARE
+ atopology alias for $1;
+ acollection alias for $2;
+ typ char(4);
+ rec RECORD;
+ ret int;
+ schemaoid oid;
+BEGIN
+ IF atopology IS NULL OR acollection IS NULL THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+ END IF;
+
+ -- Verify existance of the topology schema
+ FOR rec in EXECUTE ''SELECT oid,count(*) FROM pg_namespace WHERE ''
+ || '' nspname = '' || quote_literal(atopology)
+ || '' GROUP BY oid''
+
+ LOOP
+ IF rec.count < 1 THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent schema'';
+ END IF;
+ schemaoid := rec.oid;
+ END LOOP;
+
+ -- Verify existance of the topology views in the topology schema
+ FOR rec in EXECUTE ''SELECT count(*) FROM pg_class WHERE ''
+ || '' relnamespace = '' || schemaoid
+ || '' and relname = ''''node''''''
+ || '' OR relname = ''''edge''''''
+ || '' OR relname = ''''face''''''
+ LOOP
+ IF rec.count < 3 THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent view'';
+ END IF;
+ END LOOP;
+
+ -- Verify the topology views in the topology schema to be empty
+ FOR rec in EXECUTE
+ ''SELECT count(*) FROM ''
+ || quote_ident(atopology) || ''.edge_data ''
+ || '' UNION '' ||
+ ''SELECT count(*) FROM ''
+ || quote_ident(atopology) || ''.node ''
+ LOOP
+ IF rec.count > 0 THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty view'';
+ END IF;
+ END LOOP;
+
+ -- face check is separated as it will contain a single (world)
+ -- face record
+ FOR rec in EXECUTE
+ ''SELECT count(*) FROM ''
+ || quote_ident(atopology) || ''.face ''
+ LOOP
+ IF rec.count != 1 THEN
+ RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty face view'';
+ END IF;
+ END LOOP;
+
+ --
+ -- LOOP through the elements invoking the specific function
+ --
+ FOR rec IN SELECT geom(dump(acollection))
+ LOOP
+ typ := substring(geometrytype(rec.geom), 1, 3);
+
+ IF typ = ''LIN'' THEN
+ SELECT topology.TopoGeo_addLinestring(atopology, rec.geom) INTO ret;
+ ELSIF typ = ''POI'' THEN
+ SELECT topology.TopoGeo_AddPoint(atopology, rec.geom) INTO ret;
+ ELSIF typ = ''POL'' THEN
+ SELECT topology.TopoGeo_AddPolygon(atopology, rec.geom) INTO ret;
+ ELSE
+ RAISE EXCEPTION ''ST_CreateTopoGeo got unknown geometry type: %'', typ;
+ END IF;
+
+ END LOOP;
+
+ RETURN ''Topology '' || atopology || '' populated'';
+
+ RAISE EXCEPTION ''ST_CreateTopoGeo not implemente yet'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_CreateTopoGeo
+
+--=} SQL/MM block
+
+COMMIT;
+
More information about the postgis-commits
mailing list