[postgis-users] Help with Topology Triggers sought
Regina Obe
lr at pcorp.us
Sun Jul 10 12:23:41 PDT 2022
Sorry one more thought came to mind and now I realize the issue is more complicated than I had originally thought.
For the moving a primitive point, you’d want to use
https://postgis.net/docs/ ST_MoveIsoNode.html <https://postgis.net/docs/%20ST_MoveIsoNode.html>
So your trigger might look something like:
DECLARE v_result text;
-- you want to prevent triggering resulting from change of computed geom column as that could result in an infinite loop.
IF ( TG_OP = 'UPDATE' AND OLD.topo.layer_id = 1 AND OLD.geom = NEW.geom) THEN
-- Get old node id
v_node_id = (GetTopoGeomElementArray(OLD.topo))[1][1];
v_result = ST_MoveIsoNode('path', v_node_id, NEW.path_point);
v_geom := v_topogeom::geometry;
-- Then update the underlying topology
UPDATE <table> SET geom = v_geom WHERE id = OLD.id;
-- there has to be an update here to change the affected edges
-- something like:
UPDATE <table> SET geom = topo::geometry WHERE ST_Intersects(topo, NEW.topo);
RETURN NEW;
END IF;
-- the edge case is a bit trickier as an edge might be composed of multiple primitives, if it is just one then it would be:
https://postgis.net/docs/ ST_ChangeEdgeGeom.html <https://postgis.net/docs/%20ST_ChangeEdgeGeom.html>
IF ( TG_OP = 'UPDATE' AND OLD.topo.layer_id = 2 AND OLD.geom = NEW.geom) THEN
-- Get old node id
v_edge_id = (GetTopoGeomElementArray(OLD.topo))[1][1];
v_result = ST_ChangeEdgeGeom('path', v_edge_id, NEW.path_line);
-- Then update the underlying topology
UPDATE <table> SET geom = topo::geometry WHERE id = OLD.id;
UPDATE <table> SET geom = topo::geometry WHERE ST_Intersects(topo, NEW.topo);
RETURN NEW;
END IF;
For the case where an edge was split, I have to think that thru some more.
I suspect then you’d want to do the clearTopoGeom route, but delete the old edges from the system if they have no related topo geoms and do not have nodes related to others.
I also found a lot of documentation errors. Sorry about that will fix them in a bit and thanks for highlighting that.
Thanks,
Regina
From: Regina Obe [mailto:lr at pcorp.us]
Sent: Sunday, July 10, 2022 2:41 PM
To: 'PostGIS Users Discussion' <postgis-users at lists.osgeo.org>
Cc: 'strk at kbt.io' <strk at kbt.io>
Subject: RE: [postgis-users] Help with Topology Triggers sought
I don’t think pgRouting would serve your needs. I think postgis topology is a better fit. Pgr_CreateTopology would only create nodes at each line segment and in doing so would create a points table and ascribe the node ids to that point. It will not inject the points in your line work if they are not already there, which it sounds like you might want.
That said the newer function -- https://docs.pgrouting.org/latest/en/pgr_extractVertices.html might be a better fit to allow you to pick the pieces you want.
PgRouting also does it as a one time thing so can’t really be put in a trigger.
Regarding your other questions, see my comments below:
From: postgis-users [mailto:postgis-users-bounces at lists.osgeo.org] On Behalf Of Simon SPDBA Greener
Sent: Friday, July 8, 2022 1:07 AM
To: postgis-users at lists.osgeo.org <mailto:postgis-users at lists.osgeo.org>
Subject: [postgis-users] Help with Topology Triggers sought
Folks,
I have a topology built from two business tables containing linestrings (pipes) and a table containing points (pits).
The business requirement means we need the topology to clean and form common topological primitives for these lines and their nodes.
The topology will never contain polygons (faces) - should I use pgRouting instead?
While fairly static, both tables are subject to occasional DML.
Insert Trigger:
A before insert trigger (trigger function - tf) handles any additions (uses toTopoGeom).
DECLARE -- pseudo code
v_layer_id integer := 1;
v_geom geometry;
v_topogeom topology.TopoGeometry;
BEGIN
IF ( TG_OP = 'INSERT') THEN
-- Create topology primitives for new linestring
v_topogeom := topology.toTopoGeom(NEW.path_line,'path', v_layer_id, 0.1);
-- The action of adding the linestring to the topology may see it become slightly different from the original NEW.path_line
v_geom := v_topogeom::geometry;
-- Then conduct the insert to the business table
INSERT <table> (geom,topogeom) VALUES (v_geom,v_topoGeom);
RETURN NEW;
END IF;
END;
QUESTION: If the new linestring that is added to the existing topology causes an existing underlying edge to change (eg split),
what happens to any other topogeom objects in the table that reference the edge before it is changed?
Should the affected topogeoms be found and updated to reflect any splits to edges?
I am not sure what are the best topology methods to handle updates and/or deletes.
They would be changed as well. The topogeom is defined as a relation of other topo elements or primitives. So whenever you add a new edge, related edges are readjusted. E.g. if you break an edge into two, the topo geom relations would be adjusted too and have those two new edge ids in the relation and the old one removed.
Take a look at the table path.relation. That is where the topogeom relationships are stored.
[Regina Obe]
Update Trigger.
Here I want to construct an UPDATE trigger (tf) to handle situations where points are moved or linestrings are reshaped.
In both situations the existing topogeoms point to existing topological primitives. To handle a change should I first delete the existing topogeom using clearTopoGeom() which modifies the underlying topology, and then use toTopoGeom to construct the new topological primitives and the topogeom value?
BEGIN -- pseudo code
IF ( TG_OP = 'UPDATE' ) THEN
-- clear any topology primitives the old topogeom uniquely referenced
v_topogeom := topology.clearTopoGeom(OLD.topogeom);
-- Construct new topology primitives for new linestring.
v_topogeom := topology.toTopoGeom(NEW.path_line,'path', v_layer_id, 0.1);
v_geom := v_topogeom::geometry;
-- Then update the underlying topology
UPDATE <table> SET topogeom = v_topogeom, geom = v_geom WHERE id = OLD.id;
RETURN NEW;
END IF;
END;
This looks almost right, except I think you are using the wrong version of toTopoGeom. What you want is to maintain the same id but fill with new data, so I would change to this:
BEGIN -- pseudo code
IF ( TG_OP = 'UPDATE' ) THEN
-- clear any topology primitives the old topogeom uniquely referenced
v_topogeom := topology.clearTopoGeom(OLD.topogeom);
-- Construct new topology primitives for new linestring.
v_topogeom := topology.toTopoGeom(NEW.path_line,OLD.topogeom, 0.1);
v_geom := v_topogeom::geometry;
-- Then update the underlying topology
UPDATE <table> SET geom = v_geom WHERE id = OLD.id;
RETURN NEW;
END IF;
END;
[Regina Obe]
Delete Trigger
Here I want to construct an DELETE trigger (tf). Is this all I need to do?
BEGIN -- pseudo code
IF ( TG_OP = 'DELETE' ) THEN
-- Synchronize delete with underlying topology
v_topogeom := topology.clearTopoGeom(NEW.topogeom);
-- Then delete the record
DELETE FROM <table> WHERE id = OLD.id;
RETURN OLD;
END IF;
END;
Any help/pointers greatly appreciated.
Simon
Yah I think that should do it.
One thing I am unclear about with clearTopogeom is if it would anneal edges and remove edges no longer used. I suspect not. But I’m thinking you are hoping this will happen.
Sandro you have any comments on this?
[Regina Obe]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/postgis-users/attachments/20220710/16191231/attachment.htm>
More information about the postgis-users
mailing list