How to develop an application with pgrouting in Windows (5): writing a Wrapper

A wrapper is a PostgreSQL function that contains pgRouting and PostGIS features easier to use than pgRouting and PostGIS functions straightaway.
The pgRouting native functions have been designed to be generic so they can be used in a wide range of applications . Although this offers flexibility, the disadvantage is that, most likely, you will have to reshape, temporarily, your own data structure before you applying pgRouting functions
Writing a wrapper function adapted to your data structure decreases the need to go through cumbersome SQL queries .
Another reason to use the wrapper function is to bypass the SQL injection attacks. Building a functional barrier between the  user’s input and the function of native pgRouting native functions will allow you clear the user’s input .

PostgreSQL supports many programming languages for writing functions, but
SQL and PL / pgSQL are the most common . We will discuss an example with PL / pgSQL
.

Until now, in our series of articles , we have restricted ourselves to
the network data level, but the final goal is to set up a route search web
application. In this case , the idea is to introduce to the user a map , and let
him define a starting and an ending point. Among the many possibilities , let’s
remember the simplest: a click on the map to define the starting point and
another click to set the finish point .

Our web page submits two pairs of xy coordinates and it is expected to retrieve
the advised itinerary connecting these two geographical points .

If you have followed the series of articles , you can measure the
distance between what the web page results and the itinerary searches we have discussed,
where we have to re-enter as parameters the starting and end nodes identifiers,
as well as other settings according to the algorithm used .

Let’s not forget that between our web page and our Postgresql / Postgis
database we will, also, have Geoserver . A possibility is to write a complex query
in Geoserver , but we have a much simpler and more effective solution by using
a wrapper in PostgreSQL.

This wrapper, which constitutes a new function that we will add in our Postgres database, will translate the xy transmitted by the web page in usable parameters by our route search algorithm, optimize its execution , and format the result for displaying in the web page.

The code being used is a variation of an example found in different
sites dealing with pgrouting under the name pgr_fromAtoB .

Wrapper text

–DROP FUNCTION pgr_route_entreAetB(varchar, double precision, double precision,
— double precision, double precision);

CREATE OR REPLACE FUNCTION pgr_route_entreAetB(
IN tbl varchar,
IN x1 double precision,
IN y1 double precision,
IN x2 double precision,
IN y2 double precision,
OUT seq integer,
OUT gid integer,
OUT direction 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;
buff double precision;
dist double precision;
legid integer;

BEGIN

— Trouver le nœud le plus proche des lat/lon en entrée

EXECUTE ‘SELECT id::integer, the_geom::geometry FROM ‘ || tbl || ‘_vertices_pgr
ORDER BY the_geom <->ST_GeometryFromText(”POINT(‘
|| x1 || ‘ ‘ || y1 || ‘)”,4326) LIMIT 1’ INTO rec;
source := rec.id;

EXECUTE ‘SELECT id::integer, the_geom::geometry FROM ‘ || tbl || ‘_vertices_pgr
ORDER BY the_geom <->ST_GeometryFromText(”POINT(‘
|| x2 || ‘ ‘ || y2 || ‘)”,4326) LIMIT 1’ INTO rec;
target := rec.id;

–Définir une sous-zone de travail
EXECUTE ‘SELECT ST_Distance(ST_GeomFromText(”POINT(‘ || x1 ||’ ‘ || y1 ||’)”,4326),’
‘ST_GeomFromText(”POINT(‘ || x2 ||’ ‘ || y2 ||’)”,4326)) as dist’ INTO rec;
buff := GREATEST(rec.dist,0.5);

–Trouver l’identifiant du tronçon de départ
EXECUTE ‘SELECT gid as legid FROM ‘ || tbl || ‘ WHERE source = ‘ || source || ‘ LIMIT 1’ INTO rec;
legid := rec.legid;

— Calculer l’itinéraire avec l’algorithme A*

seq := 0;
sql := ‘SELECT gid, the_geom,pgr_astar.cost, source, target, ST_Reverse(the_geom) AS flip_geom
FROM ‘
‘pgr_astar(”SELECT gid as id, source::int, target::int, ‘
‘length_m * ‘ || tbl || ‘.cost as cost, x1, y1, x2, y2 FROM ‘
|| quote_ident(tbl) ||
‘ WHERE the_geom @ (SELECT ST_buffer(the_geom,’
|| buff || ‘) FROM ‘
|| quote_ident(tbl) || ‘ WHERE gid=’ || legid || ‘)’
”’, ‘
|| source || ‘, ‘ || target
|| ‘ , false, false), ‘
|| quote_ident(tbl) || ‘ WHERE id2 = gid ORDER BY seq’;

— mémoriser la point de départ

point := source;

FOR rec IN EXECUTE sql
LOOP

— Inverser les tronçons si nécessaire

IF ( point != rec.source ) THEN
rec.the_geom := rec.flip_geom;
point := rec.source;
ELSE
point := rec.target;
END IF;

— Calculer le cap

EXECUTE ‘SELECT degrees( ST_Azimuth(
ST_StartPoint(”’ || rec.the_geom::text ||’ ”),
ST_EndPoint(”’ || rec.the_geom::text || ‘ ”) ) )’
INTO direction;

— Return

seq := seq + 1;
gid := rec.gid;
cost := rec.cost;
geom := rec.the_geom;
RETURN NEXT;

END LOOP;
RETURN;
END;
$BODY$
LANGUAGE ‘plpgsql’ VOLATILE STRICT;

Let’s see
how this function works .

Setting up the function

First of all we have to set up of the function :
CREATE OR REPLACE FUNCTION pgr_route_entreAetB (
IN tbl varchar,
IN x1 double precision,
IN y1 double precision,
IN x2 double precision,
IN y2 double precision,
OUT seq integer,
OUT gid integer,
OUT direction double precision,
OUT cost double precision,
OUT geom geometry)

We define 5 input parameters: the table containing the network and the
coordinates of the starting and arrival point.
We, also,  define the output parameters:
a series of sections having a sequence number (seq ), the section identifier (
gid ), the geographical direction or section cap (direction), the cost
associated with the section and its geometry in order to be able to draw it in
the resulting map.

Pre-processing of parameters

Now we have to prepare the parameters of our algorithm . Here we will
use the a-star pgrouting algorithm .

EXECUTE ‘SELECT id :: integer, the_geom :: geometry
FROM’ ||
tbl
|| ‘_ vertices_pgr
ORDER BY the_geom ST_GeometryFromText ( “POINT(‘

|| x1 || ” || y1 || ‘) “,
4326) LIMIT 1 ‘INTO rec;

source: = rec.id;

EXECUTE ‘SELECT id :: integer, the_geom :: geometry
FROM’ ||
tbl
|| ‘_ vertices_pgr
ORDER BY the_geom ST_GeometryFromText ( “POINT(‘

|| x2 || ” || y2 || ‘) “,
4326) LIMIT 1 ‘INTO rec;

target: = rec.id;

These two paragraphs search the identifiers of the two nearest  points  to the input coordinates of the node table of
our network.
Values between || are replaced when running with the input parameters of the
function .

Define a working sub- area
EXECUTE ‘SELECT ST_Distance ( ST_GeomFromText (
POINT (‘|| x1 ||’ ‘|| y1 ||’) “4326),

ST_GeomFromText ( POINT (‘|| x2 ||’ ‘||
y2 ||’)
“, 4326)) as dist ‘INTO rec;

buff: = GREATEST (rec.dist, 0.5);

To optimize the routes search, it is strongly recommended to limit the
search to a sub-area ( bbox ) of our network . This paragraph calculates the
distance between the departure and arrival points and defines a search area one
and a half times greater than that distance. The sections located outside this
area will not be taken in account by the possible route search.   In this
case, we have used the GREATEST function which allows to define a minimum
subfield : if, at a glance, the distance is less than 0.5 °, by default a 0.5 °
subarea is considered. We could have defined a factor such as :

buff: = rec.dist * 1.5

It is obvious that this factor (1.5) must be decided knowingly , if not it
can preclude the function to find any solution. For example , if you have two
valleys separated by a mountain without crossing roads, the calculated distance
at a glance , cannot include the nearest path .

Find the identifier of the starting
section
EXECUTE ‘SELECT gid as legid FROM’ || tbl
|| ‘WHERE source =’ || source || ‘LIMIT 1’ INTO rec;

legid : = rec.legid ;

To exclude the very distant sections, we will use the ST_Buffer
function applied from the starting section.  Then, we must find the identifier of this
section , since for the time being,  we
have just the identifiers of the nodes table.

Now, we can execute the algorithm :

sql : = ‘SELECT gid , the_geom,
pgr_astar.cost
, source, target, ST_Reverse (the_geom) AS
flip_geom
FROM ‘
pgr_astar ( SELECT gid as
id, source ::
int , target :: int , ‘
length_m *’ || tbl || ‘.cost as cost,
x1, y1, x2, y2 FROM’

|| quote_ident ( tbl ) ||
‘WHERE the_geom @ (SELECT ST_buffer (the_geom,’
|| buff || ‘) FROM’
|| quote_ident ( tbl ) || ‘WHERE gid
=’ || legid || ‘)’
»,
|| source || ‘,’ || TARGET
|| ‘, false, false),’
|| quote_ident ( tbl ) || ‘WHERE id2 =
gid ORDER BY seq ‘;

The cost in use right here is the length in meters of the section
multiplied by the column ”   cost   ” of the table. The
function ST_Buffer limits the number of sections of the search to those located
in the defined area upon generation of the buff variable.

Next, reverse the section and calculate the direction all together. They
are here to show an example of unplanned calculation in the algorithm and how
to integrate it in the wrapper. If you do not find it useful , erase them from
the wrapper, it will not have an effect on the resulting  route. The inversion of the section is
necessary to calculate the true direction in the case where the section is taken
in opposite direction (from the target point to the source point). This explains
the section ”   S T_Reverse (the_geom) AS flip_geom
  ” in the SELECT clause.

How to add the wrapper to the postgres database

To be able to include this new function in your
PostgreSQL database, simply copy the wrapper text into a SQL window and run the
query :   

The new function is, now, in the list of functions of your database.

Now we can test the function with QGis by using
the database management window.

We enter the name of the table containing our network , and the
coordinates of the departure and arrival points.

Once executed , the query returns the list of
sections of the route . By clicking the button “Load”  we can see
the route displayed (against the backdrop of OpensStreetMap):

We
can also test a request a little more complex , to obtain just one entity as result
:

SELECT ST_MakeLine ( route.geom )
FROM (SELECT
geom FROM pgr_route_entreAetB (‘ways’,
– 4.4888433,48.3967909, -4.5267335, 48.403296) ORDER BY
seq ) AS
route

This request uses the sections list resulting from the function
pgr_route_entreAetB and builds an unique entity MultiLine . Notice the name of
the generated geometry column.

If we load this entity in QGis we will notice
that, visually, the result is identical . Nevertheless,  the quantity of information returned is
optimized .  

We will keep this last request to use it when formatting  Geoserver , what we will do in the following article.

Si cet article vous a intéressé et que vous pensez qu'il pourrait bénéficier à d'autres personnes, n'hésitez pas à le partager sur vos réseaux sociaux en utilisant les boutons ci-dessous. Votre partage est apprécié !

3 thoughts on “How to develop an application with pgrouting in Windows (5): writing a Wrapper

      1. Thanks, Let me try again:

        I am not that experienced in writing functions. Do you perhaps have a copy of the full SQL for the function?

Leave a Reply to Atilio Francois Cancel reply

Your email address will not be published. Required fields are marked *