diff --git a/README.md b/README.md index 62abccd..b2463df 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,14 @@ This SQL function finds the network reach from a given node for the input distan This SQL function returns all the edges associated with accessible nodes on the network within the input distance, and returns partial edges when the last node is not accessible but some part of the edge is within the accessible -distance. +distance. The function returns for each edge the factor (how much of the original edge's geometry is returned) for said edge (based on edge length). -The function currently uses a table named 'streets', a field for the distance value named 'cost', and a field -for edge geometry named 'the_geom'. Also the 'streets' table needs to have a unique id field named 'id'. +The function takes as arguments the edge table name, the geometry field, the cost, the startnode, and the maximum cost in the network (calculated on said cost field) +Also the edge table needs to have a unique id field named 'id'. -There is a custom type that needs to be created for this to work. The SQL for creating the custom type is commented -out at the beginning of the networkReach.sql file. +The function no longer uses a custom type, but declares the table type returned. ============ Things to do ========================= -Add table name and cost field name parameters to the function so it can work on any database. - -Make it so that any lon/lat input will find the closest point on the closest line and then do the network reach. diff --git a/networkReach.sql b/networkReach.sql index 712a2e4..a651a44 100644 --- a/networkReach.sql +++ b/networkReach.sql @@ -1,86 +1,93 @@ -/* ---create the following TYPE in your database: -CREATE TYPE networkReachType AS +/* +--Returns the following : +Table ( - id integer, - the_geom geometry + id integer, -- The edge's ID + geom geometry, -- The geometry (is partial if factor <> 1) + factor double precision -- How much of the original edge (based on length) did we take ); */ /* networkReach parameters: -1: id of the starting point in the vertices_tmp table -2: network reach distance (in units of the edge table cost column) +1: The edge table +2: The cost field in the edgeTable to base calculations on +3: The geometry field in the edgeTable +4: Id of the starting point in the vertices_tmp table +5: Network reach distance (in units of the edge table cost column) */ - -CREATE OR REPLACE FUNCTION networkReach(integer, integer) -RETURNS SETOF networkReachType AS +CREATE OR REPLACE FUNCTION parkering_routing.networkReachPartial(edgeTable regclass, costfield character varying, geomfield character varying, startnode integer, networkReachDistance double precision) +RETURNS TABLE(id integer, geom geometry, factor double precision) AS $BODY$ DECLARE - startNode ALIAS FOR $1; - networkReachDistance ALIAS FOR $2; - - networkReach networkReachType; + -- nothing to declare here, we now return a table instead of declaring types BEGIN - + + -- Create a temporary table for the network whilst we process it + -- DROP on commit so we do not get problems reusing it again in this session CREATE TEMP TABLE tempNetwork ( id integer NOT NULL, - the_geom geometry NOT NULL - ); - - INSERT INTO tempNetwork(id, the_geom) - SELECT s.id edgeID, s.the_geom + geom geometry NOT NULL, + factor double precision NOT NULL + ) + ON COMMIT DROP; + + + EXECUTE ' + INSERT INTO tempNetwork(id, geom, factor) + SELECT et.id, et.' || format('%s',geomfield) || ' AS geom, 1 as factor FROM (SELECT id1,cost from pgr_drivingDistance( - 'SELECT id, source, target, cost FROM streets', - startNode,networkReachDistance,false,false) - ) firstPath + ''SELECT id, source, target, ' || quote_ident(costField) || ' AS cost FROM ' || format('%s',edgeTable) || ''', + ' || format('%s',startnode) || ',' || format('%s', networkReachDistance) || ',false,false) + ) firstPath CROSS JOIN (SELECT id1,cost from pgr_drivingDistance( - 'SELECT id, source, target, cost FROM streets', - startNode,networkReachDistance,false,false) - ) secondPath - INNER JOIN streets s - ON firstPath.id1 = s.source - AND secondPath.id1 = s.target; + ''SELECT id, source, target, ' || quote_ident(costField) || ' AS cost FROM ' || format('%s',edgeTable) || ''', + ' || format('%s',startnode) || ',' || format('%s', networkReachDistance) || ',false,false) + ) secondPath + INNER JOIN ' || format('%s',edgeTable) || ' et + ON firstPath.id1 = et.source + AND secondPath.id1 = et.target;'; - INSERT INTO tempNetwork(id, the_geom) - SELECT allNodes.id, st_linesubstring(allNodes.the_geom, 0.0, (networkReachDistance-allNodes.distance)/allNodes.Cost) + EXECUTE ' + INSERT INTO tempNetwork(id, geom, factor) + SELECT allNodes.id, st_line_substring(allNodes.geom, 0.0, (' || format('%s', networkReachDistance) || '-allNodes.distance)/allNodes.Cost), (' || format('%s', networkReachDistance) || '-allNodes.distance)/allNodes.Cost AS factor FROM - (SELECT reachNodes.id1, s1.id, s1.cost, reachNodes.cost distance, s1.the_geom + (SELECT reachNodes.id1, et.id, et.cost, reachNodes.cost distance, et.' || format('%s',geomfield) || ' FROM (SELECT id1, cost FROM pgr_drivingDistance( - 'SELECT id, source, target, cost FROM streets', - startNode,networkReachDistance,false,false) - ) reachNodes - JOIN (SELECT id, target, source, cost, the_geom FROM streets) s1 ON reachNodes.id1 = s1.source + ''SELECT id, source, target, ' || quote_ident(costField) || ' AS cost FROM ' || format('%s',edgeTable) || ''', + ' || format('%s',startnode) || ',' || format('%s', networkReachDistance) || ',false,false) + ) reachNodes + JOIN (SELECT p.id, p.target, p.source, p.' || quote_ident(costField) || ' AS cost, p.geom FROM ' || format('%s',edgeTable) || ' p) et ON reachNodes.id1 = et.source ORDER BY reachNodes.id1 ) allNodes FULL OUTER JOIN tempNetwork ON tempNetwork.id = allNodes.id - WHERE tempNetwork.id IS NULL; + WHERE tempNetwork.id IS NULL;'; - INSERT INTO tempNetwork(id, the_geom) - SELECT allNodes.id, st_linesubstring(allNodes.the_geom,1-((networkReachDistance-allNodes.distance)/allNodes.Cost),1) + EXECUTE ' + INSERT INTO tempNetwork(id, geom, factor) + SELECT allNodes.id, st_line_substring(allNodes.geom,1-((' || format('%s', networkReachDistance) || '-allNodes.distance)/allNodes.Cost),1), (' || format('%s', networkReachDistance) || '-allNodes.distance)/allNodes.Cost AS factor FROM - (SELECT reachNodes.id1, s1.id, s1.cost, reachNodes.cost distance, s1.the_geom + (SELECT reachNodes.id1, et.id, et.cost, reachNodes.cost distance, et.' || format('%s',geomfield) || ' FROM (SELECT id1, cost FROM pgr_drivingDistance( - 'SELECT id, source, target, cost FROM streets', - startNode,networkReachDistance,false,false) - ) reachNodes - JOIN (SELECT id, target, source, cost, the_geom FROM streets) s1 ON reachNodes.id1 = s1.target + ''SELECT id, source, target, ' || quote_ident(costField) || ' AS cost FROM ' || format('%s',edgeTable) || ''', + ' || format('%s',startnode) || ',' || format('%s', networkReachDistance) || ',false,false) + ) reachNodes + JOIN (SELECT p.id, p.target, p.source, p.' || quote_ident(costField) || ' AS cost, p.' || format('%s',geomfield) || ' FROM ' || format('%s',edgeTable) || ' p) et ON reachNodes.id1 = et.target ORDER BY reachNodes.id1 ) allNodes FULL OUTER JOIN tempNetwork ON tempNetwork.id = allNodes.id - WHERE tempNetwork.id IS NULL; + WHERE tempNetwork.id IS NULL;'; - FOR networkReach IN SELECT * FROM tempNetwork - LOOP - RETURN NEXT networkReach; - END LOOP; + + RETURN QUERY SELECT t.id, t.geom, t.factor FROM tempNetwork t; + END; $BODY$ - LANGUAGE plpgsql; + LANGUAGE plpgsql; \ No newline at end of file