]> granicus.if.org Git - postgis/commitdiff
Provide a version of toTopoGeom taking a TopoGeometry object
authorSandro Santilli <strk@keybit.net>
Thu, 6 Dec 2012 23:23:57 +0000 (23:23 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 6 Dec 2012 23:23:57 +0000 (23:23 +0000)
Such version would _add_ the space taken by the input geometry
to an existing TopoGeometry.

git-svn-id: http://svn.osgeo.org/postgis/trunk@10809 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
topology/sql/topogeometry/totopogeom.sql.in.c
topology/test/regress/totopogeom.sql
topology/test/regress/totopogeom_expected

diff --git a/NEWS b/NEWS
index bd203fab4c08b81678dc18bfdf305454c2c50203..6652b19c647ab8a0ce3daf527aa2dd4525694f62 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -29,7 +29,8 @@ PostGIS 2.1.0
 
 * New Features *
 
-  - topology.clearTopoGeom (Sandro Santilli / Vizzuality)
+  - toTopoGeom with TopoGeometry sink (Sandro Santilli / Vizzuality)
+  - clearTopoGeom (Sandro Santilli / Vizzuality)
   - ST_Segmentize(geography) (Paul Ramsey / OpenGeo)
   - ST_DelaunayTriangles (Sandro Santilli / Vizzuality)
   - ST_NearestValue, ST_Neighborhood (Bborie Park / UC Davis)
index 20bdf939db25f951075b2085e5b3ecb1ba147d7a..74525a8d26c337840930a273230bba51610032fa 100644 (file)
@@ -28,7 +28,6 @@ DECLARE
   elems topology.TopoElementArray = '{{0,0}}';
   sql TEXT;
   typ TEXT;
-  tolerance FLOAT8;
 BEGIN
 
   -- Get topology information
@@ -42,9 +41,6 @@ BEGIN
         atopology;
   END;
 
-  -- Get tolerance, if 0 was given
-  tolerance := COALESCE( NULLIF(atolerance, 0), topology._st_mintolerance(atopology, ageom) );
-
   -- Get layer information
   BEGIN
     SELECT *, CASE
@@ -114,7 +110,124 @@ BEGIN
         'Unsupported feature type %', typ;
   END IF;
 
-  -- Now that we have a topogeometry, we loop over distinct components 
+  tg := topology.toTopoGeom(ageom, tg, atolerance);
+
+  RETURN tg;
+
+END
+$$
+LANGUAGE 'plpgsql' VOLATILE STRICT;
+-- }
+
+-- {
+--  Convert a simple geometry to a topologically-defined one
+--  adding its components to a pre-existing TopoGeometry
+--
+-- }{
+CREATE OR REPLACE FUNCTION topology.toTopoGeom(ageom Geometry, tg topology.TopoGeometry, atolerance float8 DEFAULT 0)
+  RETURNS topology.TopoGeometry
+AS
+$$
+DECLARE
+  layer_info RECORD;
+  topology_info RECORD;
+  rec RECORD;
+  elems topology.TopoElementArray = '{{0,0}}';
+  sql TEXT;
+  typ TEXT;
+  tolerance FLOAT8;
+  alayer INT;
+  atopology TEXT;
+BEGIN
+
+  RAISE NOTICE 'TopoGeometry is "%", its topology_id is "%"', tg, topology_id(tg);
+
+  -- Get topology information
+  SELECT id, name FROM topology.topology
+    INTO topology_info
+    WHERE id = topology_id(tg);
+  IF NOT FOUND THEN
+    RAISE EXCEPTION 'No topology with id "%" in topology.topology',
+                    topology_id(tg);
+  END IF;
+
+  alayer := layer_id(tg);
+  atopology := topology_info.name;
+
+
+  -- Get tolerance, if 0 was given
+  tolerance := COALESCE( NULLIF(atolerance, 0), topology._st_mintolerance(topology_info.name, ageom) );
+
+  -- Get layer information
+  BEGIN
+    SELECT *, CASE
+      WHEN feature_type = 1 THEN 'puntal'
+      WHEN feature_type = 2 THEN 'lineal'
+      WHEN feature_type = 3 THEN 'areal'
+      WHEN feature_type = 4 THEN 'mixed'
+      ELSE 'unexpected_'||feature_type
+      END as typename
+    FROM topology.layer l
+      INTO STRICT layer_info
+      WHERE l.layer_id = layer_id(tg)
+      AND l.topology_id = topology_info.id;
+  EXCEPTION
+    WHEN NO_DATA_FOUND THEN
+      RAISE EXCEPTION 'No layer with id "%" in topology "%"',
+        alayer, atopology;
+  END;
+
+  -- Can't convert to a hierarchical topogeometry
+  IF layer_info.level > 0 THEN
+      RAISE EXCEPTION 'Layer "%" of topology "%" is hierarchical, cannot convert a simple geometry to it.',
+        alayer, atopology;
+  END IF;
+
+
+  -- 
+  -- Check type compatibility and set TopoGeometry type
+  -- 1:puntal, 2:lineal, 3:areal, 4:collection
+  --
+  typ = geometrytype(ageom);
+  IF typ = 'GEOMETRYCOLLECTION' THEN
+    --  A collection can only go to collection layer
+    IF layer_info.feature_type != 4 THEN
+      RAISE EXCEPTION
+        'Layer "%" of topology "%" is %, cannot hold a collection feature.',
+        layer_info.layer_id, topology_info.name, layer_info.typename;
+    END IF;
+    tg.type := 4;
+  ELSIF typ = 'POINT' OR typ = 'MULTIPOINT' THEN -- puntal
+    --  A point can go in puntal or collection layer
+    IF layer_info.feature_type != 4 and layer_info.feature_type != 1 THEN
+      RAISE EXCEPTION
+        'Layer "%" of topology "%" is %, cannot hold a puntal feature.',
+        layer_info.layer_id, topology_info.name, layer_info.typename;
+    END IF;
+    tg.type := 1;
+  ELSIF typ = 'LINESTRING' or typ = 'MULTILINESTRING' THEN -- lineal
+    --  A line can go in lineal or collection layer
+    IF layer_info.feature_type != 4 and layer_info.feature_type != 2 THEN
+      RAISE EXCEPTION
+        'Layer "%" of topology "%" is %, cannot hold a lineal feature.',
+        layer_info.layer_id, topology_info.name, layer_info.typename;
+    END IF;
+    tg.type := 2;
+  ELSIF typ = 'POLYGON' OR typ = 'MULTIPOLYGON' THEN -- areal
+    --  An area can go in areal or collection layer
+    IF layer_info.feature_type != 4 and layer_info.feature_type != 3 THEN
+      RAISE EXCEPTION
+        'Layer "%" of topology "%" is %, cannot hold an areal feature.',
+        layer_info.layer_id, topology_info.name, layer_info.typename;
+    END IF;
+    tg.type := 3;
+  ELSE
+      -- Should never happen
+      RAISE EXCEPTION
+        'Unexpected feature dimension %', ST_Dimension(ageom);
+  END IF;
+
+  -- Now that we have an empty topogeometry, we loop over distinct components 
   -- and add them to the definition of it. We add them as soon
   -- as possible so that each element can further edit the
   -- definition by splitting
@@ -124,20 +237,26 @@ BEGIN
          WHEN ST_Dimension(geom) = 2 THEN 3
     END as type,
     CASE WHEN ST_Dimension(geom) = 0 THEN
-           topology.topogeo_addPoint(atopology, geom, tolerance)
+           topology.topogeo_addPoint(topology_info.name, geom, tolerance)
          WHEN ST_Dimension(geom) = 1 THEN
-           topology.topogeo_addLineString(atopology, geom, tolerance)
+           topology.topogeo_addLineString(topology_info.name, geom, tolerance)
          WHEN ST_Dimension(geom) = 2 THEN
-           topology.topogeo_addPolygon(atopology, geom, tolerance)
+           topology.topogeo_addPolygon(topology_info.name, geom, tolerance)
     END as primitive
     FROM (SELECT (ST_Dump(ageom)).geom) as f
     WHERE NOT ST_IsEmpty(geom)
   LOOP
     -- TODO: consider use a single INSERT statement for the whole thing
-    sql := 'INSERT INTO ' || quote_ident(atopology)
-        || '.relation(topogeo_id, layer_id, element_type, element_id) VALUES ('
+    sql := 'INSERT INTO ' || quote_ident(topology_info.name)
+        || '.relation(topogeo_id, layer_id, element_type, element_id) SELECT '
         || rec.id || ',' || rec.lyr || ',' || rec.type
-        || ',' || rec.primitive || ')';
+        || ',' || rec.primitive
+        -- NOTE: we're avoiding duplicated rows here
+        || ' EXCEPT SELECT ' || rec.id || ', ' || rec.lyr
+        || ', element_type, element_id FROM '
+        || quote_ident(topology_info.name)
+        || '.relation WHERE layer_id = ' || rec.lyr
+        || ' AND topogeo_id = ' || rec.id; 
 #ifdef POSTGIS_TOPOLOGY_DEBUG
     RAISE DEBUG '%', sql;
 #endif
index 54c92e825fe47f34281962a17d8587da3c056ce2..afe230204de8f1c1ad5048aa52f4bd55ea956ef5 100644 (file)
@@ -9,7 +9,7 @@ select totopogeom('POINT(0 0)'::geometry, 'tt', 1);
 select totopogeom(null, 'tt', 1);
 select totopogeom('POINT(0 0)'::geometry, '', 1);
 select totopogeom('POINT(0 0)'::geometry, null, 1);
-select totopogeom('POINT(0 0)'::geometry, 'tt', null);
+select totopogeom('POINT(0 0)'::geometry, 'tt', null::integer);
 
 -- Create simple puntual layer (will be layer 1)
 CREATE TABLE tt.f_puntal(id serial);
@@ -169,6 +169,24 @@ with inp as ( select
 tg as ( select totopogeom(g, 'tt', 5) as g from inp )
 select '#1790.3', ST_HausdorffDistance(inp.g, tg.g::geometry), ST_HausdorffDistance(tg.g::geometry, inp.g) FROM inp, tg;
 
+-- Test adding portions to an existing TopoGeometry
+INSERT INTO tt.f_areal (id, g)
+ SELECT -1, toTopoGeom('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 'tt', 4);
+SELECT 'tgup1.1', id(t.g), st_area(t.g), count(r.*)
+  FROM tt.f_areal t, tt.relation r
+  WHERE t.id = -1 AND r.layer_id = 4 AND r.topogeo_id = id(t.g)
+  GROUP BY id(t.g), st_area(t.g);
+UPDATE tt.f_areal SET g = toTopoGeom(st_translate(g, st_xmax(g::geometry)+1, 0), g);
+SELECT 'tgup1.2', id(t.g), st_area(t.g), count(r.*)
+  FROM tt.f_areal t, tt.relation r
+  WHERE t.id = -1 AND r.layer_id = 4 AND r.topogeo_id = id(t.g)
+  GROUP BY id(t.g), st_area(t.g);
+-- now add a smaller area
+UPDATE tt.f_areal SET g = toTopoGeom(st_buffer(g, -1), g);
+SELECT 'tgup1.3', id(t.g), st_area(t.g), count(r.*)
+  FROM tt.f_areal t, tt.relation r
+  WHERE t.id = -1 AND r.layer_id = 4 AND r.topogeo_id = id(t.g)
+  GROUP BY id(t.g), st_area(t.g);
 
 DROP TABLE tt.f_coll;
 DROP TABLE tt.f_areal;
index 1ce95d7fb5c6c25afdbca02d12bf0c942e38825c..ae7ddac6d37922d1efe177f66f6d5b5e938917c2 100644 (file)
@@ -39,4 +39,7 @@ custom_search_path|0
 #1790.1|0|0
 #1790.2|0|0
 #1790.3|0|0
+tgup1.1|5|100|1
+tgup1.2|5|200|2
+tgup1.3|5|200|4
 Topology 'tt' dropped