[postgis-users] Topology/Network problem

Stephen Woodbridge woodbri at swoodbridge.com
Sun Aug 2 13:37:48 PDT 2009


Hi,

Sorry for the cross post.

I think this is the kind of problem that one would typical ask of a 
topological model or maybe a graph model, but I'm pretty sure the 
topology code will not answer it yet :), And I'm pretty sure that 
pgRouting can not answer it yet either.

Basically I want to tag all the edges in my table with respect to 
whether they are connected or not. In graph theory this can be done with 
a simple depth first search and update the graph_id with a unique number 
for all edges connected to the graph with a unique number for that graph.

Table:
edge_id,    unique id for edge
source,     unique node number for start of edge
target,     unique node number for end of edge
             all interconnected edges will have a matching source or
             target node number
direction,  TF, FT, or ''|NULL to represent direction of travel
graph,      column to mark, all interconnected edges in the graph will
             will be marked with the same graph identifier
the_geom,   more columns
  ...

So I wrote the following code based on a recursive algorithm, but this 
generates a stack overflow very quickly. So I am thinking that this 
needs to be transformed into an iterative algorithm, but that needs a 
queue or stack to hold edges that have not been chased yet. That got me 
thinking that maybe there is a better way to do this with set theory 
along the lines:

loop while
   get a start edge from the table that is not marked
   initiate a queue and push this edge onto the queue
   mark the edge in the queue with graph_id number
   loop on queue
     replace the queue with the set of edges connected to those in the 
  queue and not already marked
     mark the edges in the queue with graph_id number
   end loop
   increment the graph_id number
end loop

I'm not sure what is an efficient way to replace the queue, this might 
be multiple steps in SQL.

The above assumes that all streets are two way and does not deal with 
the direction column which is used to identify oneway streets. 
"direction" can be FT - from -> to direction of travel, TF -  to - from 
direction of travel, otherwise it is assumed to be two way.

I realize that this is a little off-topic to the postgis list, but I 
hope it has value and interest to most of the list regardless.

Thanks,
   -Steve

--
-- tag-graphs - SQL functions to implement a depth first search on the
--              nodes of pgRouting prepped st table and tag each unique
--              interconnected set of edges as a unique graph.
--

CREATE OR REPLACE FUNCTION mark_edge(nid integer, g integer)
   RETURNS integer AS
$BODY$
DECLARE
     rec RECORD;
     cnt integer := 0;

BEGIN
     FOR rec IN SELECT gid, source, target FROM st
                 WHERE graph IS NULL AND (source=nid OR target=nid) LOOP
         cnt := cnt + 1;

         --RAISE NOTICE 'found gid=%, nid=%', rec.gid, nid;
         UPDATE st SET graph=g WHERE gid=rec.gid;
         IF rec.target=nid THEN
             cnt := cnt + mark_edge(rec.source, g);
         ELSE
             cnt := cnt + mark_edge(rec.target, g);
         END IF;
     END LOOP;
     RETURN cnt;
END;
$BODY$
   LANGUAGE 'plpgsql' VOLATILE STRICT
   COST 100;


CREATE OR REPLACE FUNCTION tag_graphs()
   RETURNS text AS
$BODY$
DECLARE
     g integer := 1;
     nid integer;
     cnt integer;

BEGIN
     BEGIN
         ALTER TABLE st ADD COLUMN graph integer;
     EXCEPTION
         WHEN duplicate_column THEN
             UPDATE st SET graph=NULL;
     END;

     LOOP
         SELECT INTO nid source FROM st WHERE graph IS NULL LIMIT 1;
         EXIT WHEN NOT FOUND;

         SELECT INTO cnt  mark_edge(nid, g);
         RAISE NOTICE 'graph % with % edges, starting with node % 
marked.', g, cnt, nid;
         g := g + 1;
     END LOOP;

     RETURN g::text || ' graphs found.';
END;
$BODY$
   LANGUAGE 'plpgsql' VOLATILE STRICT
   COST 100;



More information about the postgis-users mailing list