pgRouting in OpenLayers3 with reverse costs
The previous post looked at implementing the basic pgRouting workshop OpenLayers3 examples in EPSG:27700. This post adds reverse costs to the mix to take advantage of the road routing information (RRI) supplied with Ordnance Survey's ITN dataset.
The changes to the two functions I created are minor - I simply add in the reverse cost field to the pgr_dijkstra function and change the two boolean operators ("use directed graph?" and "use reverse costs?") from "false" to "true".
First function:
-- Function: routing.pgr_dijkstra_rcost(character varying, integer, integer)
-- DROP FUNCTION routing.pgr_dijkstra_rcost(character varying, integer, integer);
CREATE OR REPLACE FUNCTION routing.pgr_dijkstra_rcost(IN tbl character varying, IN source integer, IN target integer, OUT seq integer, OUT gid integer, OUT geom geometry)
RETURNS SETOF record AS
$BODY$
DECLARE
sql text;
rec record;
BEGIN
seq := 0;
sql := 'SELECT gid,geometry FROM ' ||
'pgr_dijkstra(''SELECT gid as id, source::int, target::int, '
|| 'cost_time::float AS cost, rcost_time::float AS reverse_cost FROM '
|| quote_ident(tbl) || ''', '
|| quote_literal(source) || ', '
|| quote_literal(target) || ' , true, true), '
|| quote_ident(tbl) || ' WHERE id2 = gid ORDER BY seq';
FOR rec IN EXECUTE sql
LOOP
seq := seq + 1;
gid := rec.gid;
geom := rec.geometry;
RETURN NEXT;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100
ROWS 1000;
ALTER FUNCTION routing.pgr_dijkstra_rcost(character varying, integer, integer)
OWNER TO postgres;
COMMENT ON FUNCTION routing.pgr_dijkstra_rcost(character varying, integer, integer) IS 'OL3 shortest path with reverse cost';
Second function:
-- Function: routing.pgr_fromatob_rcost(character varying, double precision, double precision, double precision, double precision)
-- DROP FUNCTION routing.pgr_fromatob_rcost(character varying, double precision, double precision, double precision, double precision);
CREATE OR REPLACE FUNCTION routing.pgr_fromatob_rcost(IN tbl character varying, IN x1 double precision, IN y1 double precision, IN x2 double precision, IN y2 double precision, OUT seq integer, OUT gid integer, OUT name text, OUT heading double precision, OUT cost double precision, OUT geom geometry)
RETURNS SETOF record AS
$BODY$
DECLARE
sql text;
rec record;
source integer;
target integer;
point integer;
BEGIN
-- Find nearest node
EXECUTE 'SELECT id::integer FROM itn_network_vertices_pgr
ORDER BY the_geom <-> ST_GeometryFromText(''POINT('
|| x1 || ' ' || y1 || ')'',27700) LIMIT 1' INTO rec;
source := rec.id;
EXECUTE 'SELECT id::integer FROM itn_network_vertices_pgr
ORDER BY the_geom <-> ST_GeometryFromText(''POINT('
|| x2 || ' ' || y2 || ')'',27700) LIMIT 1' INTO rec;
target := rec.id;
-- Shortest path query (TODO: limit extent by BBOX)
seq := 0;
sql := 'SELECT gid, geometry, cost, source, target,
ST_Reverse(geometry) AS flip_geom FROM ' ||
'pgr_dijkstra(''SELECT gid as id, source::int, target::int, '
|| 'cost_time::float AS cost, rcost_time::float AS reverse_cost FROM '
|| quote_ident(tbl) || ''', '
|| source || ', ' || target
|| ' , true, true), '
|| quote_ident(tbl) || ' WHERE id2 = gid ORDER BY seq';
-- Remember start point
point := source;
FOR rec IN EXECUTE sql
LOOP
-- Flip geometry (if required)
IF ( point != rec.source ) THEN
rec.geometry := rec.flip_geom;
point := rec.source;
ELSE
point := rec.target;
END IF;
-- Calculate heading (simplified)
EXECUTE 'SELECT degrees( ST_Azimuth(
ST_StartPoint(''' || rec.geometry::text || '''),
ST_EndPoint(''' || rec.geometry::text || ''') ) )'
INTO heading;
-- Return record
seq := seq + 1;
gid := rec.gid;
--name := rec.name;
cost := rec.cost;
geom := rec.geometry;
RETURN NEXT;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100
ROWS 1000;
ALTER FUNCTION routing.pgr_fromatob_rcost(character varying, double precision, double precision, double precision, double precision)
OWNER TO postgres;
If you now create a new layer in the same store we created in the last post (Ch. 9 of the pgRouting workshop) but change the SQL slightly you should get a different route from the same source and target nodes than from the function without reverse costs:
SELECT ST_MakeLine(route.geom) FROM (
SELECT geom FROM pgr_fromAtoB_rcost('itn_network', %x1%, %y1%, %x2%, %y2%
) ORDER BY seq) AS route