From: Sandro Santilli Date: Wed, 19 Oct 2005 10:04:15 +0000 (+0000) Subject: Added ST_ModEdgesSplit function, cleaned up test files, added tests for X-Git-Tag: pgis_1_1_0~209 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e51b2fe6b663ba440a0d6d46ef760ef622149208;p=postgis Added ST_ModEdgesSplit function, cleaned up test files, added tests for the new topology editing functions. git-svn-id: http://svn.osgeo.org/postgis/trunk@1971 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/test/Makefile b/topology/test/Makefile index e72d6eba5..2996eb7ad 100644 --- a/topology/test/Makefile +++ b/topology/test/Makefile @@ -1,6 +1,8 @@ DATABASE=postgis_topo_regress all: + @echo + @echo "make test - create the database, run all tests except sqlmm" @echo @echo "make initdb - create the regress database" @echo "make inittopo - create topology routines" @@ -10,6 +12,8 @@ all: @echo "make validate - validate 'city_data' loaded topology" @echo @echo "make hier - define some hierarchical TopoGeoms" + @echo + @echo "make edit - test sqlmm editing functions" @echo @echo "make cache - create geom caches for features tables" @echo "make topopred - run predicates on topogeoms" @@ -19,6 +23,14 @@ all: @echo "make sqlmm - test SQL/MM functions (exceptions are expected)" @echo +pred: + @make geompred | grep -v make > /tmp/geompred.out + @make topopred | grep -v make > /tmp/topopred.out + @diff /tmp/geompred.out /tmp/topopred.out + @diff /tmp/geompred.out predicate.expected + +test: inittopo load loadmore hier cache pred invalid edit + initdb: createdb $(DATABASE) createlang plpgsql $(DATABASE) @@ -49,10 +61,10 @@ sqlmm: psql -f sqlmm_topology.sql $(DATABASE) topopred: topo_predicates.sql - psql -f topo_predicates.sql $(DATABASE) + @psql -tf topo_predicates.sql $(DATABASE) geompred: geom_predicates.sql - psql -f geom_predicates.sql $(DATABASE) + @psql -tf geom_predicates.sql $(DATABASE) topo_predicates.sql: predicates.sql.in cpp -P -traditional-cpp predicates.sql.in | sed -e 's:@COLUMN@:feature:g;s:@SCHEMA@:topology.:g' > topo_predicates.sql @@ -60,6 +72,8 @@ topo_predicates.sql: predicates.sql.in geom_predicates.sql: predicates.sql.in cpp -P -traditional-cpp predicates.sql.in | sed -e 's:@COLUMN@:the_geom:g;s:@SCHEMA@::g' > geom_predicates.sql +edit: + psql -f edit_topology.sql clean distclean: rm -f geom_predicates.sql topo_predicates.sql diff --git a/topology/test/cache_geometries.sql b/topology/test/cache_geometries.sql index c42ffec6b..20bdf1726 100644 --- a/topology/test/cache_geometries.sql +++ b/topology/test/cache_geometries.sql @@ -2,17 +2,17 @@ -- created by load_topology.sql and stores there the SFS Geometry -- derived by the TopoGeometry column -ALTER TABLE features.city_streets ADD the_geom geometry; -UPDATE features.city_streets set the_geom = topology.Geometry(feature); +--ALTER TABLE features.city_streets ADD the_geom geometry; +UPDATE features.city_streets set the_geom = multi(topology.Geometry(feature)); -ALTER TABLE features.traffic_signs ADD the_geom geometry; -UPDATE features.traffic_signs set the_geom = topology.Geometry(feature); +--ALTER TABLE features.traffic_signs ADD the_geom geometry; +UPDATE features.traffic_signs set the_geom = multi(topology.Geometry(feature)); -ALTER TABLE features.land_parcels ADD the_geom geometry; -UPDATE features.land_parcels set the_geom = topology.Geometry(feature); +--ALTER TABLE features.land_parcels ADD the_geom geometry; +UPDATE features.land_parcels set the_geom = multi(topology.Geometry(feature)); -ALTER TABLE features.big_parcels ADD the_geom geometry; -UPDATE features.big_parcels set the_geom = topology.Geometry(feature); +--ALTER TABLE features.big_parcels ADD the_geom geometry; +UPDATE features.big_parcels set the_geom = multi(topology.Geometry(feature)); -ALTER TABLE features.big_signs ADD the_geom geometry; -UPDATE features.big_signs set the_geom = topology.Geometry(feature); +--ALTER TABLE features.big_signs ADD the_geom geometry; +UPDATE features.big_signs set the_geom = multi(topology.Geometry(feature)); diff --git a/topology/test/edit_topology.sql b/topology/test/edit_topology.sql new file mode 100644 index 000000000..aef7c1cc6 --- /dev/null +++ b/topology/test/edit_topology.sql @@ -0,0 +1,3 @@ +select topology.ST_NewEdgesSplit('city_data',25,'POINT(10 35)'); +select topology.ST_ModEdgesSplit('city_data',27,'POINT(9.5 35)'); +select topology.ST_ModEdgesSplit('city_data',28,'POINT(11 35)'); diff --git a/topology/test/hierarchy.sql b/topology/test/hierarchy.sql index 0133bc8b9..382a15eb4 100644 --- a/topology/test/hierarchy.sql +++ b/topology/test/hierarchy.sql @@ -15,6 +15,8 @@ SELECT topology.AddTopoGeometryColumn('city_data', 'features', 1 -- the land_parcles ); +SELECT AddGeometryColumn('features','big_parcels','the_geom',-1,'MULTIPOLYGON',2); + INSERT INTO features.big_parcels VALUES ('P1P2', -- Feature name topology.CreateTopoGeom( 'city_data', -- Topology name @@ -36,13 +38,6 @@ INSERT INTO features.big_parcels VALUES ('F3F6', -- Feature name (SELECT layer_id FROM topology.layer WHERE table_name = 'big_parcels'), '{{6,1},{7,1}}')); -- F3 and F6 -INSERT INTO features.big_parcels VALUES ('F3F6', -- Feature name - topology.CreateTopoGeom( - 'city_data', -- Topology name - 3, -- Topology geometry type (polygon/multipolygon) - (SELECT layer_id FROM topology.layer WHERE table_name = 'big_parcels'), - '{{7,1},{6,1}}')); -- F3 and F6 - SELECT feature_name, astext(topology.geometry(feature)) from features.big_parcels; SELECT a.feature_name, b.feature_name @@ -62,6 +57,8 @@ SELECT topology.AddTopoGeometryColumn('city_data', 'features', 2 -- the traffic_signs ); +SELECT AddGeometryColumn('features','big_signs','the_geom',-1,'MULTIPOINT',2); + INSERT INTO features.big_signs VALUES ('S1S2', -- Feature name topology.CreateTopoGeom( 'city_data', -- Topology name diff --git a/topology/test/load_topology.sql b/topology/test/load_topology.sql index 379146603..049ec43f9 100644 --- a/topology/test/load_topology.sql +++ b/topology/test/load_topology.sql @@ -221,6 +221,12 @@ SELECT topology.AddTopoGeometryColumn('city_data', 'features', 'city_streets','f --NOTYET---- 5. Initialize topology metadata. --NOTYET--EXECUTE topology.INITIALIZE_METADATA('CITY_DATA'); +-- 4bis. Add geometry columns, for caching Geometries from TopoGeometries + +SELECT AddGeometryColumn('features','land_parcels','the_geom',-1,'MULTIPOLYGON',2); +SELECT AddGeometryColumn('features','city_streets','the_geom',-1,'MULTILINESTRING',2); +SELECT AddGeometryColumn('features','traffic_signs','the_geom',-1,'MULTIPOINT',2); + -- 6. Load feature tables using the CreateTopoGeom constructor. diff --git a/topology/test/predicate.expected b/topology/test/predicate.expected new file mode 100644 index 000000000..dc8c2e184 --- /dev/null +++ b/topology/test/predicate.expected @@ -0,0 +1,118 @@ +BEGIN + POINT/POINT INTERSECTS + + S1 | N1N6N14 + S3 | N1N6N14 + S4 | N3N4 + S4 | N4 + N1N2N3 | N1N6N14 + N1N2N3 | N3N4 + N3N4 | N4 + + POINT/LINE INTERSECTS + + S1 | R1 + S1 | E20E19 + S1 | R1a + S2 | R1 + S2 | R1a + S3 | R2 + N1N2N3 | R4 + N1N6N14 | R1 + N1N6N14 | R2 + N1N6N14 | E20E19 + N1N6N14 | R1a + N3N4 | R4 + + LINE/LINE INTERSECTS + + R1 | E20E19 + R1 | R1a + R3 | E25 + E7E8 | E20E19 + E20E19 | R1a + + POINT/POLY INTERSECTS + + S1 | P1 + S1 | P2 + S1 | F3 + S1 | F6 + S1 | F3F4 + S2 | P2 + S2 | P3 + S2 | F3F4 + S4 | P4 + N1N2N3 | P4 + N1N2N3 | P5 + N1N2N3 | F1 + N1N6N14 | P1 + N1N6N14 | P2 + N1N6N14 | P5 + N1N6N14 | F3 + N1N6N14 | F6 + N1N6N14 | F3F4 + N1N6N14 | F1 + N3N4 | P4 + N4 | P4 + + LINE/POLY INTERSECTS + + R1 | P1 + R1 | P2 + R1 | P3 + R1 | F3 + R1 | F6 + R1 | F3F4 + R3 | P5 + R3 | F1 + R4 | P4 + E7E8 | P1 + E7E8 | P2 + E7E8 | P3 + E7E8 | F3 + E7E8 | F3F4 + E20E19 | P1 + E20E19 | P2 + E20E19 | F3 + E20E19 | F6 + E20E19 | F3F4 + E25 | P5 + E25 | F1 + R1a | P1 + R1a | P2 + R1a | P3 + R1a | F3 + R1a | F6 + R1a | F3F4 + + POLY/POLY INTERSECTS + + P1 | P2 + P1 | F3 + P1 | F6 + P1 | F3F4 + P2 | P3 + P2 | F3 + P2 | F6 + P2 | F3F4 + P3 | F3F4 + P5 | F1 + F3 | F6 + F3 | F3F4 + F6 | F3F4 + + POINT/POINT EQUALS + + S4 | N4 + + LINE/LINE EQUALS + + R1 | R1a + R3 | E25 + + POLYGON/POLYGON EQUALS + + P5 | F1 + +COMMIT diff --git a/topology/topology.sql.in b/topology/topology.sql.in index 8a1538e3a..1380358bb 100644 --- a/topology/topology.sql.in +++ b/topology/topology.sql.in @@ -140,7 +140,12 @@ -- Complete -- -- ST_NewEdgesSplit --- Complete, exceptions untested +-- 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 ? -- @@ -2880,7 +2885,7 @@ BEGIN || '' 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; + --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) @@ -2932,8 +2937,8 @@ BEGIN END LOOP; - RAISE NOTICE ''Edge % split in edges % and % by node %'', - anedge, edgeid1, edgeid2, nodeid; + --RAISE NOTICE ''Edge % split in edges % and % by node %'', + -- anedge, edgeid1, edgeid2, nodeid; RETURN nodeid; END @@ -2941,6 +2946,212 @@ 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