Contenido
Esta es una guía simple para llevarte a través de los pasos básicos para trabajar con pgRouting. Esta guía cubre:
Lo primero que hay que hacer es crear una base de datos y cargar pgRouting a la base de datos. Típicamente se creará una base de datos para cada proyecto. Una vez que se tiene una base de datos donde trabajar, se pueden cargar los datos y construir la aplicación en la base de datos. Esto hace que sea sencillo de mover el proyecto en el futuro, como por ejemplo, a un servidor de producción.
Para Postgresql 9.2 y versiones posteriores
createdb mydatabase
psql mydatabase -c "create extension postgis"
psql mydatabase -c "create extension pgrouting"
El cómo cargar los datos dependerá de el formato que tienen. Existen varias herramientas de Código Abierto que le pueden ayudar, como:
osm2pgrouting: |
|
---|---|
shp2pgsql: |
|
ogr2ogr: |
|
osm2pgsql: |
|
Así, estas herramientas y probablemente otras le permitirán leer datos vectoriales para que luego pueda cargar esos datos en su base de datos como una tabla de algún tipo. En este punto, necesita saber un poco sobre su estructura de datos y contenido. Una forma fácil de navegar por su nueva tabla de datos es con pgAdmin3 o phpPgAdmin.
A continuación, tenemos que construir una topología para nuestros datos de calle. Esto significa que para cualquier arista dada dentro de los datos de calle, los extremos de la arista se conectarán a un nodo único y a otras aristas que también están conectados a ese mismo nodo único. Una vez que todas las aristas están conectados a los nodos tenemos un grafo que se puede utilizar para el ruteo con pgRouting. Proporcionamos una herramienta que le ayudará con esto:
Nota
este paso no es necesario si los datos se cargan con osm2pgrouting
select pgr_createTopology('myroads', 0.000001);
Hay muchas fuentes posibles de errores en un grafo. Es posible que los datos con los que comenzó no se hayan diseñado teniendo en cuenta el ruteo. Un grafo tiene algunos requisitos muy específicos. Una es que es NODED, esto significa que, a excepción de algunos casos de usos muy específicos, cada segmento de carretera comienza y termina en un nodo y, en general, no cruza otro segmento de carretera al que debería estar conectado.
Puede haber otros errores como en una calle de un solo sentido tener el sentido equivocado. No contamos con herramientas para buscar todos los errores posibles, pero tenemos algunas herramientas básicas que pueden ayudar.
select pgr_analyzegraph('myroads', 0.000001);
select pgr_analyzeoneway('myroads', s_in_rules, s_out_rules,
t_in_rules, t_out_rules
direction)
select pgr_nodeNetwork('myroads', 0.001);
Una vez que haya realizado todo el trabajo de preparación realizado anteriormente, calcular una ruta es bastante fácil. Tenemos una gran cantidad de algoritmos diferentes que pueden funcionar con su red de carreteras preparada. La forma general de una consulta de ruta es:
select pgr_dijkstra(`SELECT * FROM myroads', 1, 2)
Como puede ver esto es bastante sencillo y puede buscar los algoritmos específicos para los detalles de las firmas y cómo usarlos. Estos resultados tienen información como edge id y/o el identificador de nodo junto con el costo o la geometría del paso en la ruta de acceso de start a end. Con los identificadores puede unir estos resultados a la tabla de aristas para obtener más información sobre cada paso de la ruta.
Una función puede tener diferentes sobrecargas. A través de esta documentación, para indicar qué sobrecarga usamos los siguientes términos:
Dependiendo de la sobrecarga son los parámetros utilizados, manteniendo la coherencia en todas las funciones.
Hay varios tipos de consultas internas válidas y también las columnas devueltas dependen de la función. El tipo de consulta interna dependerá de los requisitos de las funcion(es). Para simplificar la variedad de tipos, se utiliza ANY-INTEGER
y ANY-NUMERICAL
.
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|---|
ANY-NUMERICAL: | SMALLINT, INTEGER, BIGINT, REAL, FLOAT |
Columna | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
id | ANY-INTEGER |
Identificador de la arista. | |
origen | ANY-INTEGER |
Identificador del primer punto final en el vértice de la arista. | |
objetivo | ANY-INTEGER |
Identificador del segundo punto final en el vértice de la arista. | |
cost | ANY-NUMERICAL |
Peso de la arista (source, target)
|
|
reverse_cost | ANY-NUMERICAL |
-1 | Peso de la arista (target, source),
|
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|---|
ANY-NUMERICAL: | SMALLINT, INTEGER, BIGINT, REAL, FLOAT |
Descripción de la consulta edges_sql (no es necesarioun id)
edges_sql: | Una consulta SQL, que debe regresar un conjunto de filas con las siguientes columnas: |
---|
Columna | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
origen | ANY-INTEGER |
Identificador del primer punto final en el vértice de la arista. | |
objetivo | ANY-INTEGER |
Identificador del segundo punto final en el vértice de la arista. | |
cost | ANY-NUMERICAL |
Peso de la arista (source, target)
|
|
reverse_cost | ANY-NUMERICAL |
-1 | Peso de la arista (target, source),
|
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|---|
ANY-NUMERICAL: | SMALLINT, INTEGER, BIGINT, REAL, FLOAT |
Parámetro | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
edges_sql | TEXT |
Consulta SQL como se describió anteriormente. | |
via_vertices | ARRAY[ANY-INTEGER] |
Arreglo de identificadores de vértices ordenados que serán visitados. | |
dirigido | BOOLEAN |
true |
|
strict | BOOLEAN |
false |
|
U_turn_on_edge | BOOLEAN |
true |
|
edges_sql: | Una consulta SQL, que debe regresar un conjunto de filas con las siguientes columnas: |
---|
Columna | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
id | ANY-INTEGER |
Identificador de la arista. | |
origen | ANY-INTEGER |
Identificador del primer punto final en el vértice de la arista. | |
objetivo | ANY-INTEGER |
Identificador del segundo punto final en el vértice de la arista. | |
cost | ANY-NUMERICAL |
Peso de la arista (source, target)
|
|
reverse_cost | ANY-NUMERICAL |
-1 | Peso de la arista (target, source),
|
x1 | ANY-NUMERICAL |
Coordenada X del vértice source. | |
y1 | ANY-NUMERICAL |
Coordenada Y del vértice source. | |
x2 | ANY-NUMERICAL |
Coordenada X del vértice objetivo. | |
y2 | ANY-NUMERICAL |
Coordenada Y del vértice target. |
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|---|
ANY-NUMERICAL: | SMALLINT, INTEGER, BIGINT, REAL, FLOAT |
Para pgr_pushRelabel, pgr_edmondsKarp, pgr_boykovKolmogorov :
Edges SQL: | Consulta SQL de un grafo dirigido de capacidades, que debe devolver un conjunto de filas con las siguientes columnas: |
---|
Columna | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
id | ANY-INTEGER |
Identificador de la arista. | |
origen | ANY-INTEGER |
Identificador del primer punto final en el vértice de la arista. | |
objetivo | ANY-INTEGER |
Identificador del segundo punto final en el vértice de la arista. | |
capacidad | ANY-INTEGER |
Peso de la arista (source, target)
|
|
reverse_capacity (capacidad inversa) | ANY-INTEGER |
-1 | Peso de la arista (target, source),
|
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|
Para pgr_maxFlowMinCost - Experimental and pgr_maxFlowMinCost_Cost - Experimental:
Edges SQL: | Consulta SQL de un grafo dirigido de capacidades, que debe devolver un conjunto de filas con las siguientes columnas: |
---|
Columna | Tipo | Valores predeterminados | Descripción |
---|---|---|---|
id | ANY-INTEGER |
Identificador de la arista. | |
origen | ANY-INTEGER |
Identificador del primer punto final en el vértice de la arista. | |
objetivo | ANY-INTEGER |
Identificador del segundo punto final en el vértice de la arista. | |
capacidad | ANY-INTEGER |
Capacidad de la arista (origen, destino)
|
|
reverse_capacity (capacidad inversa) | ANY-INTEGER |
-1 | Capacidad de la arista (destino, origen),
|
cost | ANY-NUMERICAL |
Peso de la arista (origen, destino) si existe. | |
reverse_cost | ANY-NUMERICAL |
0 | Peso de la arista (destino, origen) si existe. |
Donde:
ANY-INTEGER: | SMALLINT, INTEGER, BIGINT |
---|---|
ANY-NUMERICAL: | smallint, int, bigint, real, float |
Descripción de la consulta SSQL de Puntos
points_sql: | Una consulta SQL, que debe regresar un conjunto de filas con las siguientes columnas: |
---|
Columna | Tipo | Descripción |
---|---|---|
pid | ANY-INTEGER |
(opcional) Identificador del punto.
|
edge_id | ANY-INTEGER |
Identificador de la arista «más cercano» al punto. |
fraction | ANY-NUMERICAL |
El valor en <0,1> que indica la posición relativa desde el primer punto final de la arista. |
side | CHAR |
(opcional) Valor en [“b”, “r”, “l”, NULL] que indica si el punto es:
|
Donde:
ANY-INTEGER: | smallint, int, bigint |
---|---|
ANY-NUMERICAL: | smallint, int, bigint, real, float |
Hay varios tipos de columnas devueltas que dependen de la función.
Devuelve el conjunto de (seq, path_seq [, start_vid] [, end_vid], node, edge, cost, agg_cost)
Columna | Tipo | Descripción |
---|---|---|
seq | INT |
Valor secuencial a partir de 1. |
path_seq | INT |
Posición relativa en la ruta. Tiene el valor 1 para el principio de una ruta. |
start_vid | BIGINT |
Identificador del vértice inicial. Se devuelve cuando hay varias vetrices iniciales en la consulta. |
end_vid | BIGINT |
Identificador del vértice final. Se devuelve cuando hay varios vértices finales en la consulta. |
node | BIGINT |
Identificador del nodo en la ruta de start_vid a end_vid . |
edge | BIGINT |
Identificador del borde utilizado para ir del nodo al siguiente nodo de la secuencia de ruta. -1 para el último nodo de la ruta. |
cost | FLOAT |
Costo de desplazamiento desde node usando `` edge`` hasta el siguiente nodo en la secuencia de ruta. |
agg_cost | FLOAT |
Coste agregado de start_v to node . |
Devuelve el conjunto de (seq, path_id, path_seq [, start_vid] [, end_vid], node, edge, cost, agg_cost)
Columna | Tipo | Descripción |
---|---|---|
seq | INT |
Valor secuencial a partir de 1. |
path_id | INT |
Identificador de ruta. Tiene el valor 1 para el primero de una ruta. Se utiliza cuando hay varias rutas para la misma combinación de start_vid a end_vid . |
path_seq | INT |
Posición relativa en la ruta. Tiene el valor 1 para el principio de una ruta. |
start_vid | BIGINT |
Identificador del vértice inicial. Se devuelve cuando hay varias vetrices iniciales en la consulta. |
end_vid | BIGINT |
Identificador del vértice final. Se devuelve cuando hay varios vértices finales en la consulta. |
node | BIGINT |
Identificador del nodo en la ruta de start_vid a end_vid . |
edge | BIGINT |
Identificador del borde utilizado para ir del nodo al siguiente nodo de la secuencia de ruta. -1 para el último nodo de la ruta. |
cost | FLOAT |
Costo de desplazamiento desde node usando `` edge`` hasta el siguiente nodo en la secuencia de ruta. |
agg_cost | FLOAT |
Coste agregado de start_v to node . |
Devuelve SET OF (start_vid, end_vid, agg_cost)
Columna | Tipo | Descripción |
---|---|---|
start_vid | BIGINT |
Identificador del vértice inicial. Se utiliza cuando hay varias vetrices iniciales en la consulta. |
end_vid | BIGINT |
Identificador del vértice final. Se utiliza cuando hay varios vértices finales en la consulta. |
agg_cost | FLOAT |
Coste agregado de start_vid a end_vid . |
Para pgr_pushRelabel, pgr_edmondsKarp, pgr_boykovKolmogorov :
Columna | Tipo | Descripción |
---|---|---|
seq | INT |
Valor secuencial a partir de 1. |
edge | BIGINT |
Identificador de la arista en la consulta original(edges_sql). |
start_vid | BIGINT |
Identificador del primer punto final en el vértice de la arista. |
end_vid | BIGINT |
Identificador del segundo punto final en el vértice de la arista. |
flujo | BIGINT |
Flujo a través del arista en la dirección (start_vid , end_vid ). |
residual_capacity (capacidad residual) | BIGINT |
Capacidad residual del arista en la dirección (start_vid , end_vid ). |
Para pgr_maxFlowMinCost - Experimental
Columna | Tipo | Descripción |
---|---|---|
seq | INT |
Valor secuencial a partir de 1. |
edge | BIGINT |
Identificador de la arista en la consulta original(edges_sql). |
origen | BIGINT |
Identificador del primer punto final en el vértice de la arista. |
objetivo | BIGINT |
Identificador del segundo punto final en el vértice de la arista. |
flujo | BIGINT |
Flujo a través de la arista en la dirección (origen, destino). |
residual_capacity (capacidad residual) | BIGINT |
Capacidad residual de la arista en la dirección (origen, destino). |
cost | FLOAT |
El costo de enviar este flujo a través de la arista en la dirección (origen, destino). |
agg_cost | FLOAT |
El costo agregado. |
Resumen
Normalmente cuando se cargan archivos GIS en la base de datos para su uso con pgRouting no tienen información de la topología asociada a ellos. Para crear una topología útil, los datos debe ser «noded». Esto significa que donde dos o más caminos forman una intersección allí debe existir y todos los segmentos de carretera necesitan estar partidos en la intersección, eso suponiendo que puede navegar a cualquier otro segmento mediante la intersección de cualquiera de estos segmentos.
Puedes utilizar graph analysis functions para ayudarte a visualizar dónde podría haber problemas de topología en tus datos. Si necesitas crear nodos a partir de tus datos, también tenemos una función pgr_nodeNetwork () que deberían funcionarte. Esta función divide TODOS los segmentos cruzados y los une. Hay algunos casos en los que esto podría NO ser lo correcto.
Por ejemplo, cuando se tiene un cruce de puente o de un túnel, no quieres estos contengan un nodo, pero pgr_nodeNetwork no sabe que ese es el caso y va a crear un nodo, entonces el enrutador interpretará el puente o el paso a desnivel como si fuera una intersección plana en 2D. Para lidiar con este problema, se debe utilizar niveles z ya se para estos tipos de intersecciones o para otros casos en donde crear el nodo de la intersección no sea lo correcto.
Para los casos donde la topología debe construirse, las siguientes funciones pueden ser de utilidad. Una forma de preparar los datos para pgRouting es agregar los siguientes campos a la tabla y luego poblarlas según corresponda. Este ejemplo tiene como supuestos: que la tabla original ya tiene columnas como one_way
, fcc
y posiblemente otras y que contienen valores de datos específicos. Esto es sólo para darle una idea de lo que se puede hacer con los datos.
ALTER TABLE edge_table
ADD COLUMN source integer,
ADD COLUMN target integer,
ADD COLUMN cost_len double precision,
ADD COLUMN cost_time double precision,
ADD COLUMN rcost_len double precision,
ADD COLUMN rcost_time double precision,
ADD COLUMN x1 double precision,
ADD COLUMN y1 double precision,
ADD COLUMN x2 double precision,
ADD COLUMN y2 double precision,
ADD COLUMN to_cost double precision,
ADD COLUMN rule text,
ADD COLUMN isolated integer;
SELECT pgr_createTopology('edge_table', 0.000001, 'the_geom', 'id');
La función pgr_createTopology crea la tabla vertices_tmp
y llena las columnas de source
y target
. El siguiente ejemplo llenó las columnas restantes. Aquí, la columna fcc
contiene código de clase de entidad y las declaraciones CASE
lo convierten a una velocidad promedio.
UPDATE edge_table SET x1 = st_x(st_startpoint(the_geom)),
y1 = st_y(st_startpoint(the_geom)),
x2 = st_x(st_endpoint(the_geom)),
y2 = st_y(st_endpoint(the_geom)),
cost_len = st_length_spheroid(the_geom, 'SPHEROID["WGS84",6378137,298.25728]'),
rcost_len = st_length_spheroid(the_geom, 'SPHEROID["WGS84",6378137,298.25728]'),
len_km = st_length_spheroid(the_geom, 'SPHEROID["WGS84",6378137,298.25728]')/1000.0,
len_miles = st_length_spheroid(the_geom, 'SPHEROID["WGS84",6378137,298.25728]')
/ 1000.0 * 0.6213712,
speed_mph = CASE WHEN fcc='A10' THEN 65
WHEN fcc='A15' THEN 65
WHEN fcc='A20' THEN 55
WHEN fcc='A25' THEN 55
WHEN fcc='A30' THEN 45
WHEN fcc='A35' THEN 45
WHEN fcc='A40' THEN 35
WHEN fcc='A45' THEN 35
WHEN fcc='A50' THEN 25
WHEN fcc='A60' THEN 25
WHEN fcc='A61' THEN 25
WHEN fcc='A62' THEN 25
WHEN fcc='A64' THEN 25
WHEN fcc='A70' THEN 15
WHEN fcc='A69' THEN 10
ELSE null END,
speed_kmh = CASE WHEN fcc='A10' THEN 104
WHEN fcc='A15' THEN 104
WHEN fcc='A20' THEN 88
WHEN fcc='A25' THEN 88
WHEN fcc='A30' THEN 72
WHEN fcc='A35' THEN 72
WHEN fcc='A40' THEN 56
WHEN fcc='A45' THEN 56
WHEN fcc='A50' THEN 40
WHEN fcc='A60' THEN 50
WHEN fcc='A61' THEN 40
WHEN fcc='A62' THEN 40
WHEN fcc='A64' THEN 40
WHEN fcc='A70' THEN 25
WHEN fcc='A69' THEN 15
ELSE null END;
-- UPDATE the cost information based on oneway streets
UPDATE edge_table SET
cost_time = CASE
WHEN one_way='TF' THEN 10000.0
ELSE cost_len/1000.0/speed_kmh::numeric*3600.0
END,
rcost_time = CASE
WHEN one_way='FT' THEN 10000.0
ELSE cost_len/1000.0/speed_kmh::numeric*3600.0
END;
-- clean up the database because we have updated a lot of records
VACUUM ANALYZE VERBOSE edge_table;
Ahora su base de datos debe estar lista para usarse en cualquiera (mayoría?) de los algoritmos de pgRouting.
Resumen
Es común encontrar problemas con los gráficos que no se han construido completamente ligados con nodos o con gráficos con niveles z en intersecciónes que se han introducido incorrectamente. Otro problema es que las calles han sido introducidas en la dirección equivocada. No podemos detectar errores con respecto a la verdad respecto a la «tierra», pero podemos buscar inconsistencias y algunas anomalías en un gráfico y les reportarlas para inspecciones adicionales.
Nosotros no tenemos herramientas de visualización para estos problemas, pero se ha utilizado MapServer para reproducir la gráfica y poner en relieve las áreas con problemas potenciales. Alguien familiarizado con graphviz podría contribuir con herramientas para generar imágenes para este propósito.
Con pgr_analyzeGraph el grafo puede revisarse en busca de errores. Por ejemplo, para la tabla «mytab» que incluye a «mytab_vertices_pgr» como la tabla de vértices:
SELECT pgr_analyzeGraph('mytab', 0.000002);
NOTICE: Performing checks, pelase wait...
NOTICE: Analyzing for dead ends. Please wait...
NOTICE: Analyzing for gaps. Please wait...
NOTICE: Analyzing for isolated edges. Please wait...
NOTICE: Analyzing for ring geometries. Please wait...
NOTICE: Analyzing for intersections. Please wait...
NOTICE: ANALYSIS RESULTS FOR SELECTED EDGES:
NOTICE: Isolated segments: 158
NOTICE: Dead ends: 20028
NOTICE: Potential gaps found near dead ends: 527
NOTICE: Intersections detected: 2560
NOTICE: Ring geometries: 0
pgr_analyzeGraph
----------
OK
(1 row)
En la tabla de vértices «mytab_vertices_pgr»:
cnt=1
SELECT count(*) as deadends FROM mytab_vertices_pgr WHERE cnt = 1;
deadends
----------
20028
(1 row)
SELECT count(*) as gaps FROM mytab_vertices_pgr WHERE chk = 1;
gaps
-----
527
(1 row)
Si se tienen segmentos aislados de caminos, por ejemplo, un segmento en el cual los dos extremos son callejones sin salida, se pueden encontrar con la siguiente consulta:
SELECT *
FROM mytab a, mytab_vertices_pgr b, mytab_vertices_pgr c
WHERE a.source=b.id AND b.cnt=1 AND a.target=c.id AND c.cnt=1;
Si quieren visualizar en una imagen gráfica, entonces usted puede utilizar algo como MapServer para representar los bordes y los vértices y el estilizar en base a cnt
o si están aislados, etc. También se puede hacer esto con una herramienta como graphviz o geoserver u otras herramientas similares.
pgr_analyzeOneWay analiza las calles unidireccionales de un grafo e identifica los segmentos volteados. Básicamente, si cuenta las aristas que entran en un nodo y las aristas bordes que salen de un nodo, el número tiene que ser mayor que uno.
Esta consulta agrega dos columnas a la tabla vertices_tmp ein int
y eout int
(ein = borde de entrada, eout=borde de salida) y las rellena con los conteos correspondientes. Después de su ejecución, en un grafo se pueden identificar los nodos con problemas potenciales utilizando la siguiente consulta.
Las reglas se definen como un conjunto de cadenas de texto que si coinciden con el valor de col
se considera como válido para el origen o destino dentro o fuera de la condición.
Supongamos que tenemos una tabla «st» de bordes y una columna «one_way» que podría tener valores como:
Entonces se puede formar la siguiente consulta para analizar las calles unidireccionales para errores.
SELECT pgr_analyzeOneway('mytab',
ARRAY['', 'B', 'TF'],
ARRAY['', 'B', 'FT'],
ARRAY['', 'B', 'FT'],
ARRAY['', 'B', 'TF'],
);
-- now we can see the problem nodes
SELECT * FROM mytab_vertices_pgr WHERE ein=0 OR eout=0;
-- and the problem edges connected to those nodes
SELECT gid FROM mytab a, mytab_vertices_pgr b WHERE a.source=b.id AND ein=0 OR eout=0
UNION
SELECT gid FROM mytab a, mytab_vertices_pgr b WHERE a.target=b.id AND ein=0 OR eout=0;
Normalmente estos problemas son generados por una rotura en la red, la dirección un camino equivocado, quizás un error relacionado con niveles z o una red que no esté debidamente indicada con nodos.
Las herramientas anteriores no detectan todos los problemas de la red, pero identifican algunos problemas comunes. Hay otros problemas que son difíciles de detectar porque son más globales en su naturaleza como múltiples redes desconectadas. Piense usted en una isla con una red de carreteras que no está conectada a la red del continente porque faltan las rutas del puente o del ferry.
Para obtener resultados más rápidos, las consultas enlazan el área de interés para tener, por ejemplo, no más de un millón de filas.
Utilice una consulta SQL interna que no incluya algunas aristas en la función de ruteo
SELECT id, source, target from edge_table WHERE
id < 17 and
the_geom && (select st_buffer(the_geom,1) as myarea FROM edge_table where id = 5)
Integración de la consulta interna a la función pgRouting:
SELECT * FROM pgr_dijkstra(
'SELECT id, source, target from edge_table WHERE
id < 17 and
the_geom && (select st_buffer(the_geom,1) as myarea FROM edge_table where id = 5)',
1, 2)
Cuando «sabe» que va a eliminar un conjunto de bordes de la tabla de bordes, y sin esos bordes va a utilizar una función de enrrutamiento puede hacer el siguiente:
Analizar la topología nueva basada en la topología actual:
pgr_analyzegraph('edge_table',rows_where:='id < 17');
O crear una nueva topología si el cambio es permanente:
pgr_createTopology('edge_table',rows_where:='id < 17');
pgr_analyzegraph('edge_table',rows_where:='id < 17');
Wiki
Agregar funcionalidad a pgRouting
Consultar developer’s documentation
Índices y tablas