elems topology.TopoElementArray = '{{0,0}}';
sql TEXT;
typ TEXT;
- tolerance FLOAT8;
BEGIN
-- Get topology information
atopology;
END;
- -- Get tolerance, if 0 was given
- tolerance := COALESCE( NULLIF(atolerance, 0), topology._st_mintolerance(atopology, ageom) );
-
-- Get layer information
BEGIN
SELECT *, CASE
'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
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
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);
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;