├── README.md
├── imgs
├── bbdd_taller.png
├── conexion_postgis.png
├── ejercicio_1_pgadmin.png
├── ejercicio_1_qgis.png
├── ejercicio_1_qgis_2.png
├── pgadmin_geometry_viewer.png
├── qgis_iso_1.png
├── qgis_iso_2.png
├── qgis_iso_3.png
├── qgis_iso_4.png
├── qgis_iso_5.png
├── qgis_iso_6.png
└── qgis_iso_7.png
├── importacion_de_archivos_usando_osm2pgrouting.txt
├── iscronas.qml
└── mapconfig_for_cars_mod.xml
/README.md:
--------------------------------------------------------------------------------
1 | # **Taller pgRouting - RNU QGIS México 2019**
2 |
3 | 
4 |
5 | *Este taller está basado en los [Workshops de pgRouting de los FOSS4G](https://workshop.pgrouting.org/) sin los cuales hubiera sido imposible aprender a usar pgRouting. Gracias!*
6 | ## **1. ¿Qué es pgRouting?**
7 |
8 | pgRouting es una extensión para PostgreSQL/PostGIS que añade funcionalidades para ánalisis de redes y planificación de rutas. Esto nos permite realizar cálculos de rutas óptimas, áreas de servicio e iscocronas (con la ayuda de QGIS), desde la propia base de datos con los beneficios que esto conlleva:
9 | * Los datos pueden ser modificados desde diversos tipos de clientes:
10 | * SIG de Escritorio (QGIS, gvSIG, uDig, etc)
11 | * Aplicaciones web
12 | * Dispositivos móviles
13 | * Las modificaciones de los datos pueden reflejarse de forma inmediata en el motor de enrutamiento.
14 | * Los parámetros de costo de desplazamiento por la red puede calcularse de forma dinámica usando SQL, permitiendo utilizar atributos de diferentes campos o tablas (por ejemplo, la velocidad máxima permitida en una carretera).
15 |
16 | La librería pgRouting contiene los siguientes algoritmos:
17 | * [Algoritmo de Dijkstra](https://es.wikipedia.org/wiki/Algoritmo_de_Dijkstra)
18 | * [Algoritmo de Johnson](https://es.wikipedia.org/wiki/Algoritmo_de_Johnson)
19 | * [Algoritmo de Floyd-Warshall](https://es.wikipedia.org/wiki/Algoritmo_de_Floyd-Warshall)
20 | * [Algoritmo A*](https://es.wikipedia.org/wiki/Algoritmo_de_b%C3%BAsqueda_A*)
21 | * [Algoritmos bidireccionales](https://en.wikipedia.org/wiki/Bidirectional_search): Dijkstra y A* bidireccionales
22 | * [Problema del viajante](https://es.wikipedia.org/wiki/Problema_del_viajante)
23 | * Distancia manejando
24 | * Camino más corto con restricción de giros
25 | * Etc.
26 |
27 | pgRouting es una librería de código abierto disponible con la licencia GPLv2 y soportada y mantenida por
28 | [Georepublic](http://georepublic.info/), [iMaptools](http://imaptools.com/) y una amplica comunidad de usarios.
29 |
30 | Una de las principales desarrolladoras es [Vicky Vergara](https://twitter.com/VickyVvergara)
31 |
32 | ## **2. Estructura de datos**
33 |
34 | Los archivos para el taller los podemos descargar desde [aquí](https://drive.google.com/drive/folders/1t9sBO8x97giIzbaBvq3jbdqyqbU7W_yC?usp=sharing)
35 |
36 | La estructura básica que necesitamos para empezar a trabajar con PgRouting es una capa de líneas (o tabla de base de datos) con una buena calidad topológica (que no tenga lineas desconectadas). Si queremos hacer cálculos en función del tiempo de desplazamiento necestaremos además un campo que contenga la velocidad máxima permitida en la vía y longitud de la linea (en metros). Si además queremos tener en cuenta el sentido de circulación necesitamos un atributo que nos indique el sentido de circulación o si la vía es de doble sentido.
37 |
38 | Además de la "capa" de líneas necesitamos una capa de nodos de la red. Estos nodos definen las conexiones entre calles y carreteras. La capa de lineas tiene que contener para cada segmento de la red cuál es el nodo de origen y el nodo de destino que conecta. Así que finalmente la capa de lineas tiene que contener dos atributos más, nodo de origen y nodo de estino (source y target).
39 |
40 | ### OpenStreetMap
41 |
42 | Una de las fuentes de datos con la que podemos trabajar es OpenStreetMap. Para ello necesitamos dos cosas:
43 | * Descargar los datos de OSM desde:
44 | * https://www.openstreetmap.org (nos ubicamos el en el área de interés y luego hacemos click en Overpass API)
45 | * Descargamos los conglomerados a nivel subregión, país o ciudad desde https://download.geofabrik.de
46 | * Instalar [osm2pgrouting](https://github.com/pgRouting/osm2pgrouting)
47 |
48 |
49 | osm2pgrouting nos va a permitir generar toda la estructura de base de datos que necesitamos para pgRouting de forma sencilla y rápida. El único incoveniente es que si queremos procesar conjuntos de datos muy grandes (por ejemplo, todo un estado de México o el país completo) vamos a necesitar hacerlo por partes (eliminando el parámetro --clean) o contar con un servidor con una cantidad enorme de memoria RAM.
50 |
51 | El ejemplo que aparece a continuación exportaría a nuestra base de datos PostgreSQL de nombre ruteo, el archivo que descargamos "tu_archivo_osm_xml.osm" usando la configuración en el archivo de configuración mapconfig.xml.
52 |
53 | ```bash
54 | osm2pgrouting --f tu_archivo_osm_xml.osm --conf mapconfig.xml --dbname ruteo --username postgres --clean
55 | ```
56 |
57 | A continuación se muestra la ayuda de osm2pgrouting:
58 |
59 | ```bash
60 | osm2pgrouting --help
61 | Allowed options:
62 |
63 | Help:
64 | --help Produce help message for this version.
65 | -v [ --version ] Print version string
66 |
67 | General:
68 | -f [ --file ] arg REQUIRED: Name of the osm file.
69 | -c [ --conf ] arg (=/usr/share/osm2pgrouting/mapconfig.xml)
70 | Name of the configuration xml file.
71 | --schema arg Database sch2pgrouting -f map.osm -c mapconfig_for_cars_mod.xml -d gislocal -p 5433 --schema ruteoamg -U gisadmin -W privacidad% --clean --chunk 20000ema to put tables.
72 | blank: defaults ´to default schema
73 | dictated by PostgreSQL
74 | search_path.
75 | --prefix arg Prefix added at the beginning of the
76 | table names.
77 | --suffix arg Suffix added at the end of the table
78 | names.
79 | --addnodes Import the osm_nodes, osm_ways &
80 | osm_relations tables.
81 | --attributes Include attributes information.
82 | --tags Include tag information.
83 | --chunk arg (=20000) Exporting chunk size.
84 | --clean Drop previously created tables.
85 | --no-index Do not create indexes (Use when indexes
86 | are already created)
87 |
88 | Database options:
89 | -d [ --dbname ] arg Name of your database (Required).
90 | -U [ --username ] arg Name of the user, which have write access to
91 | the database.
92 | -h [ --host ] arg (=localhost) Host of your postgresql database.
93 | -p [ --port ] arg (=5432) db_port of your database.
94 | -W [ --password ] arg Password for database access.
95 | ```
96 |
97 | El parámetro --conf nos permite utilizar un archivo de configuración para osm2pgrouting que va a definir que tipos de carreteras o vías queremos utilizar y cual es la velocidad de desplazamiento en cada tipo de vía. Por defecto vamos a encontrar 3 configuraciones en la carpeta /usr/share/osm2pgrouting/:
98 | * Para bicicletas
99 | * Para automóviles
100 | * Para peatones
101 |
102 | Si queremos podemos modificar los archivos de coniguración para que se adapte a nuestras necesidades, qué tipo de vías vamos a importar (definido por los tipos de vías de OSM) y la velocidad máxima a utilizar paraa cada tipo de vía.
103 |
104 | #### Beneficios e incovenientes de Utilizar OSM
105 | **Pros:**
106 | 1. Los datos están más actualizados que otras fuentes de información en México y se manejan como un único conjunto de datos (no son dos capas de información separadas como en el caso de INEGI, una para carreteras y otra para calles).
107 | 2. La información está en algunos casos más actualizada.
108 | 3. Las líneas cuentan con todos los atributos necesarios para pgRouting
109 | 4. Los nodos de conexión de la red siguen reglas precisas de manera que aunque dos lineas se crucen puede no existir un nodo de conexión porque sea un paso a desnivel, un tunel o un puente.
110 | 5. En general facilita enormemente la creación de una red "ruteable"
111 |
112 | **Contras:**
113 | 1. Puede que los datos en tu región no estén tan completos o actualizados como desearías
114 | 2. La herramienta osm2pgrouting puede consumir enormes cantidades de memoría por lo que necesitamos hacer importaciones "incrementales" si queremos trabajar en regiones muy grandes (también podemos utilizar un servidor en la nube con mucha RAM para hacer el proceso y una vez creada la red descargarla a un servidor con menos memoria RAM o un PC).
115 |
116 | ### INEGI
117 |
118 | La [Red Nacional de Caminos de INEGI](https://www.inegi.org.mx/app/biblioteca/ficha.html?upc=889463674641) soporta el estándar internacional ISO 14825:2011 Intelligent Transport Systems_Geographic Data Files_GDF5.0, la cual integra los elementos necesarios para ruteo, ya tiene el formato necesario para pgRouting:
119 | * Capa red_vial: Contiene las carreteras. Tiene como atributos VELOCIDAD, UNION_INI (nodo de inicio) y UNION_FIN (nodo de destino) y LONGITUD.
120 | * Capa union: Puntos que representan los nodos (uniones) de los segmentos de la red de caminos.Tiene el atributo ID_UNION que es el identificador que está almacenado en los campos UNION_INI y UNION_FIN de la capa. red_vial
121 | * Localidad: Localidades de México (puntual). Muchas de las localidades tiene las mismas coordenadas que los puntos de la capa de unión.
122 |
123 | **Pros:**
124 | * Ya viene preparada para ruteo con lo que simplifca mucho la creación de la red
125 |
126 | **Contras:**
127 | * La capa red_vial no incluye la mayoría de las calles de los núcleos urbanos, solo algunas de las vías principales que las cruzan.
128 |
129 | ## **Conexión a la base de datos**
130 |
131 | Para agilizar el taller no vamos a realizar la instalación de PostgreSQL, PostGIS y pgRouting. Utilizaremos una base de datos en un servidor remoto. Para conectar al servidor vamos a usar pgAdmin4.
132 |
133 | IP del Servidor: xx.xx.xx.xx
134 |
135 | Los datos están en los schemas `ruteoinegi` y `ruteoamg`.
136 |
137 | Pueden descargar la base de datos completa desde [aquí](https://drive.google.com/drive/folders/1t9sBO8x97giIzbaBvq3jbdqyqbU7W_yC?usp=sharing), es un dump con todos los datos del taller.
138 |
139 | ## **3. Algoritmos de pgRouting**
140 |
141 | *NOTA:*
142 | * Muchas de las funciones de pgRouting incluyen parámetros del tipo sql::text, es decir una consulta sql como una cadena de texto. Esto puede parecer algo confuso al principio pero permite gran flexibilidad ya que el usuario puede pasar cualquier sentencia `SELECT` como argumento para una función siempre que el resultado de dicho `SELECT` contenga el número de atributos requerido y los nombres de atributos correctos.
143 | * La mayoría de los algoritmos de pgRouting no necesitan la geometría de la red.
144 | * La mayoría de los algoritmos de pgRouting no retornan una geometría, si no una lista ordenada de nodos o segmentos.
145 |
146 | ### 3.1 pgr_dijkstra
147 |
148 | El algoritmo de Dijkstra fue el primer algorimto implementado en pgRouting. Solo requiere los atributos , `ìd` , `source` y `target` y `cost`. Podemos especificar si el grafo es dirigido o no dirigido (tendrá en cuenta el sentido de las vias o no).
149 |
150 | **Resumen de signaturas**
151 | ```sql
152 | pgr_dijkstra(edges_sql, start_vid, end_vid)
153 | pgr_dijkstra(edges_sql, start_vid, end_vid [, directed])
154 | pgr_dijkstra(edges_sql, start_vid, end_vids [, directed])
155 | pgr_dijkstra(edges_sql, start_vids, end_vid [, directed])
156 | pgr_dijkstra(edges_sql, start_vids, end_vids [, directed])
157 |
158 | RETURNS SET OF (seq, path_seq [, start_vid] [, end_vid], node, edge, cost, agg_cost)
159 | OR EMPTY SET
160 | ```
161 |
162 | La primera opción es un origen y un destino, la segunda es igual pero dirigida, la tercera un origen y muchos destinos (1:n), la cuarta muchos orígenes y un destino (n:1) y la última es muchos origens y muchos destinos (n:m).
163 |
164 | #### 3.1.1 Ejercicio 1 - Un origen y un destino
165 | Como comentamos en la nota anterior uno de los parámetros es un `SELECT` que se lo pasamos a la función como una cadena de texto. También hay que dar un alias al parámetro length_m para que la función entienda que es el costo. El costo puede ser cualquier atributo, en este caso usamos el atributo length_m pero podría ser tiempo en segundos (la ruta más rápida) o cualquier atributo que represente el costo de desplazamiento por la red. Después del `SELECT` tenemos los IDs de los nodos entre los que queremos calcular la ruta más corta. Finalmente tenemos el parámetro `directed` que nos indica si queremos que la ruta tenga en cuenta el sentido de circulación o no.
166 |
167 | ```sql
168 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
169 | source,
170 | target,
171 | length_m AS cost
172 | FROM ruteoamg.ways',
173 | 79012, 35280,
174 | directed := false)
175 | ```
176 | El resultado de la consulta es una tabla con las columnas:
177 | * `seq`: Identificador único
178 | * `path_seq`: Secuencia dentro de la ruta
179 | * `start_vid`: Nodo de inicio de la ruta.
180 | * `node`: Nodo por el que pasa la ruta
181 | * `edge`: Segmento de la red
182 | * `cost`: Costo de desplazamiento por el segmento
183 | * `agg_cost`: Costo agregado para cada paso de la ruta
184 |
185 | Como la función pgr_dijkstra no retorna las geometrías vamos a generar una consulta que nos permita obtener las geometrías de la red para poder visualizar la ruta. En este caso estamos trayendo las geometrías de los segmentos de la red que están en la tabla ruteoamg.ways mediante un join entre el id del segmento, edge, retornado por la función pgr_dijkstra y la llave primaria de la tabla ruteoamg.ways, gid.
186 |
187 | ```sql
188 | WITH ruta as (SELECT * FROM pgr_dijkstra(
189 | 'SELECT gid as id,
190 | source,
191 | target,
192 | length_m AS cost
193 | FROM ruteoamg.ways',
194 | 79012, 35280,
195 | directed := false))
196 | SELECT ruta.*, b.the_geom
197 | FROM ruta
198 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid ;
199 | ```
200 | Podemos visualizar el resultado de la consulta directamente en pgAdmin4 haciendo clic en el botón con un ojo. También podriamo visualizarlo en QGIS.
201 |
202 | 
203 |
204 | 
205 |
206 |
207 | #### 3.1.2 Ejercicio 2 - Varios orígenes y un destino
208 | En este caso vamos especificar varios orígenes y un destino:
209 | * Los orígenes son los nodos: 3443 y 29539
210 | * Para el costo vamos a volver a utilizar el atributo length_m
211 |
212 | Para pasarle los origenes a la función pgr_dijkstra tenemos que usar un `ARRAY` (arreglo: conjunto de elementos del mismo tipo).
213 |
214 | ```sql
215 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
216 | source,
217 | target,
218 | length_m AS cost
219 | FROM ruteoamg.ways',
220 | ARRAY[3443, 29539], 35280,
221 | directed := false)
222 | ```
223 | ```sql
224 | WITH ruta as (SELECT * FROM pgr_dijkstra(
225 | 'SELECT gid as id,
226 | source,
227 | target,
228 | length_m AS cost
229 | FROM ruteoamg.ways',
230 | ARRAY[3443, 29539], 35280,
231 | directed := false))
232 | SELECT ruta.*, b.the_geom
233 | FROM ruta
234 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
235 | ```
236 |
237 | #### 3.1.3 Ejercicio 3 - Un solo origen varios destinos
238 |
239 | En este caso vamos especificar un origen y varios destinos:
240 | * El origen es el nodos: 67272
241 | * Los nodos de destino son: 1017 y 55134
242 | * Para el costo vamos a utilizar el tiempo de desplazamiento suponiendo una velocidad constante de 20 km/h (5.56 m/s).`v = 5.56 m/s` y `t=d/v`
243 |
244 | ```sql
245 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
246 | source,
247 | target,
248 | length_m / 5.56 AS cost
249 | FROM ruteoamg.ways',
250 | 67272, ARRAY[1017, 55134],
251 | directed := false)
252 | ```
253 | ```sql
254 | WITH ruta as (SELECT * FROM pgr_dijkstra('SELECT gid as id,
255 | source,
256 | target,
257 | length_m / 5.56 AS cost
258 | FROM ruteoamg.ways',
259 | 67272, ARRAY[1017, 55134],
260 | directed := false))
261 | SELECT ruta.*, b.the_geom
262 | FROM ruta
263 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
264 | ```
265 |
266 | ¿Qué ocurre si cambiamos el parámetro de dirigido a no dirigido (`directed:=false`)?¿En qué unidades estamos midiendo el costo de desplazamiento en este último ejercicio?
267 |
268 | #### 3.1.4 Ejercicio 4 - Varios orígenes y varios destinos
269 |
270 | En este caso vamos especificar varios origenes y varios destinos:
271 | * El origen es el nodos: 67272 y 133535 (Hoteles recomendados)
272 | * Los nodos de destino son: 1017 y 55134 (Sedes del evento QGIS RNU 2019 - CUCSH y CUAAD)
273 | * Para el costo vamos a utilizar el tiempo de desplazamiento suponiendo una velocidad constante de 20 km/h (5.56 m/s) `v = 5.56 m/s` y `t=d/v`, pero en este caso vamos a obtener el tiempo en minutos.
274 |
275 | ```sql
276 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
277 | source,
278 | target,
279 | length_m / 5.56 / 60 AS cost
280 | FROM ruteoamg.ways',
281 | ARRAY[67272, 133535], ARRAY[1017, 55134],
282 | directed := false)
283 | ```
284 | ```sql
285 | WITH ruta as (SELECT * FROM pgr_dijkstra('SELECT gid as id,
286 | source,
287 | target,
288 | length_m / 5.56 / 60 AS cost
289 | FROM ruteoamg.ways',
290 | ARRAY[67272, 133535], ARRAY[1017, 55134],
291 | directed := false))
292 | SELECT ruta.*, b.the_geom
293 | FROM ruta
294 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
295 | ```
296 |
297 | *NOTA*: Si inspeccionamos el resultado de la consulta, veremos que hay algunas filas que tiene edge=-1, estas filas nos indican el costo total de cada ruta:
298 | * De 67272 a 1017 tardaremos 9.33 minutos.
299 | * De 67272 a 55134 tardaremos 27.91 minutos.
300 | * De 133535 a 1017 tardaremos 14.60 minutos.
301 | * De 133535 a 32.70 tardaremos 32.70 miuntos.
302 |
303 | ### 3.2 pgr_dijkstraCost
304 | Si lo que queremos es calcular el costo total de desplazamiento, sin necesidad de buscar en los resultados de pgr_dijkstra podemos utilizar `pgr_dijkstraCost` que nos arrojará resultados más compactos.
305 |
306 | **Resumen de signaturas**
307 | ```sql
308 | pgr_dijkstraCost(edges_sql, start_vid, end_vid)
309 | pgr_dijkstraCost(edges_sql, start_vid, end_vid [, directed])
310 | pgr_dijkstraCost(edges_sql, start_vid, end_vids [, directed])
311 | pgr_dijkstraCost(edges_sql, start_vids, end_vid [, directed])
312 | pgr_dijkstraCost(edges_sql, start_vids, end_vids [, directed])
313 |
314 | RETURNS SET OF (start_vid, end_vid, agg_cost)
315 | OR EMPTY SET
316 | ```
317 |
318 | #### 3.2.1 Ejercicio 5 - Calcular el costo total entre varios orígenes y varios destinos
319 | ```sql
320 | SELECT * FROM pgr_dijkstraCost('SELECT gid as id,
321 | source,
322 | target,
323 | length_m / 5.56 / 60 AS cost
324 | FROM ruteoamg.ways',
325 | ARRAY[67272, 133535], ARRAY[1017, 55134],
326 | directed := false)
327 | ```
328 | #### 3.2.2 Ejercicio 6 - Resumen de los costos totales por origen entre varios orígenes y varios destinos
329 | En este caso tenemos que utilizar la clausula `GROUP BY` para obtener los resultados agrupados para cada origen.
330 | ```sql
331 | SELECT start_vid, sum(agg_cost) as tiempo_total
332 | FROM pgr_dijkstraCost('SELECT gid as id,
333 | source,
334 | target,
335 | length_m / 5.56 / 60 AS cost
336 | FROM ruteoamg.ways',
337 | ARRAY[67272, 133535], ARRAY[1017, 55134],
338 | directed := false)
339 | GROUP BY start_vid
340 | ORDER BY start_vid;
341 | ```
342 |
343 | ¿En cuál de los dos hoteles conviene más hospedarse?
344 |
345 | ## **4. Funciones Avanzadas de Ruteo**
346 |
347 | ### **4.1 Ruteo para vehículos**
348 | Una consulta para ruteo de vehículos es diferente a una para peatones:
349 | * Los segmentos de la red de carreteras suelen considerarse "dirigidos" (pueden tener limitaciones en cuanto al sentido en el que pueden recorrerse)
350 | * El costo puede ser:
351 | * Distancia
352 | * Tiempo
353 | * Dinero
354 | * Emisiones de CO2
355 | * Desgaste del vehículo, etc.
356 | * El atributo reverse_cost debe tenerse en cuenta en vias de doble sentido
357 | * El costo tiene que tener las mismas unidades que el atributo cost
358 | * cost y reverse_cost pueden ser diferentes (Esto es debido a que existen vias de sentido único)
359 |
360 | Dependiendo de la geometría, la forma válida:
361 | * segmento (origen, destino) (`cost >= 0` y `reverse_cost` < 0)
362 | * segmento (destino, origen) (`cost < 0` y `reverse_cost` >= 0)
363 |
364 | De manera que un "sentido contrario" se indica mediante un valor negativo y no es insertado en el grafo para su procesamiento.
365 |
366 | Para vías de doble sentido `cost >= 0` y `reverse_cost >= 0` y sus valores pueden ser diferentes. Por ejemplo, es más rápido ir cuesta abajo en una carretera en pendiente. En general `cost` y `reverse_cost` no tienen porque ser distancias, en realidad pueden ser casi cualquier cosa, por ejemplo: tiempo, pendiente, superficie, tipo de carretera, o una combinación de varios parámetros.
367 |
368 | #### 4.1.1 Ejercicio 7 - Ruteo para vehículos - Ida
369 | Desde el Hotel Portobello al CUCSH:
370 | * El vehículo va desde el nodo 67272 al nodo 1017
371 | * Usaremos los atributos `cost` y `reverse_cost` que están en grados.
372 |
373 | ```sql
374 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
375 | source,
376 | target,
377 | cost,
378 | reverse_cost
379 | FROM ruteoamg.ways',
380 | 67272, 1017,
381 | directed := true);
382 | ```
383 |
384 | ```sql
385 | WITH ruta as (SELECT * FROM pgr_dijkstra(
386 | 'SELECT gid as id,
387 | source,
388 | target,
389 | cost,
390 | reverse_cost
391 | FROM ruteoamg.ways',
392 | 67272, 1017,
393 | directed := true))
394 | SELECT ruta.*, b.the_geom
395 | FROM ruta
396 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
397 | ```
398 |
399 | #### 4.1.2 Ejercicio 8 - Ruteo para vehículos - Regreso
400 | Desde el CUCSH al Hotel Portobello:
401 | * El vehículo va desde el nodo 1017 al nodo 67272
402 | * Usaremos los atributos `cost` y `reverse_cost` que están en grados.
403 |
404 | ```sql
405 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
406 | source,
407 | target,
408 | cost,
409 | reverse_cost
410 | FROM ruteoamg.ways',
411 | 1017, 67272,
412 | directed := true);
413 | ```
414 |
415 | ```sql
416 | WITH ruta as (SELECT * FROM pgr_dijkstra(
417 | 'SELECT gid as id,
418 | source,
419 | target,
420 | cost,
421 | reverse_cost
422 | FROM ruteoamg.ways',
423 | 1017, 67272,
424 | directed := true))
425 | SELECT ruta.*, b.the_geom
426 | FROM ruta
427 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
428 | ```
429 | En un grafo dirigido las rutas de ida y retorno son casi siempre diferentes.
430 |
431 | #### 4.1.3 Ejercicio 9 - Ruteo para vehículos - Cuando el tiempo es oro
432 | Desde el CUCSH al Hotel Portobello:
433 | * El vehículo va desde el nodo 1017 al nodo 67272
434 | * El costo es `$1000 por hora`
435 | * Usaremos los atributos `cost_s` y `reverse_cost_s` que están en segundos.
436 | * La duración del viaje en horas es `cost_s / 3600`
437 | * El costo del viaje en pesos es `cost_s * 1000 / 3600`
438 |
439 | Los atributos `cost_s` y `reverse_cost_s` se calculan usando la longitud de un segmento de la red y los atributos `maxspeed_forward` y `maxspeed_backward` (velocidad máxima y velocidad máxima hacia atrás). Estas velocidades vienen definidas en el archivo de configuración que utilizamos junto con osm2pgrouting para importar los datos a nuestra base de datos.
440 |
441 | ```sql
442 | SELECT * FROM pgr_dijkstra('SELECT gid as id,
443 | source,
444 | target,
445 | cost_s * 1000 / 3600 as cost,
446 | reverse_cost_s * 1000 / 3600 as reverse_cost
447 | FROM ruteoamg.ways',
448 | 1017, 67272,
449 | directed := true);
450 | ```
451 |
452 | ```sql
453 | WITH ruta as (SELECT * FROM pgr_dijkstra(
454 | 'SELECT gid as id,
455 | source,
456 | target,
457 | cost_s * 1000 / 3600 as cost,
458 | reverse_cost_s * 1000 / 3600 as reverse_cost
459 | FROM ruteoamg.ways',
460 | 1017, 67272,
461 | directed := true))
462 | SELECT ruta.*, b.the_geom
463 | FROM ruta
464 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
465 | ```
466 | Como podemos observar las rutas son idénticas y los costos son directamente proporcionales.
467 |
468 | ### **4.2 Manipulación de los costos**
469 |
470 | Cuando usamos datos de OSM importados a través de la herramienta osm2pgrouting, se crean algunas tablas adicionales, una de ellas se llama `configuration`. Esta tabla contiene los parámetros de importación que definimos en el archivo `mapconfig_for_cars_mod.xml`. Vamos a explorar los `tag_id` (identificador de etiqueta) de red.
471 | ```sql
472 | SELECT tag_id, tag_key, tag_value
473 | FROM ruteoamg.configuration
474 | ORDER BY tag_id;
475 | ```
476 | ```sql
477 | SELECT *
478 | FROM ruteoamg.configuration;
479 | ```
480 | #### 4.2.1 Ejercicio 10 - Ruteo para vehículos sin penalización
481 |
482 | Para modificar el comportamiento de los algoritmos vamos añadir una columna llamada `penalty` a la tabla `ruteoamg.configuration` y usarla para recalcular el costo de desplzamiento en función de unos criterios definidos por nosotros mismos. A continuación mostramos algunos ejemplos:
483 |
484 | ```sql
485 | ALTER TABLE ruteoamg.configuration ADD COLUMN penalty FLOAT;
486 | -- Sin penalización
487 | UPDATE ruteoamg.configuration SET penalty=1;
488 | ```
489 | Ahora al calcular la ruta vamos a traer el atributo `penalty` haciendo un `join` con la tabla `ruteoamg.configuration` usando el campo `tag_id` que también está en la tabla `ruteoamg.ways`
490 | ```sql
491 | WITH ruta as (SELECT * FROM pgr_dijkstra('
492 | SELECT gid AS id,
493 | source,
494 | target,
495 | cost_s * penalty AS cost,
496 | reverse_cost_s * penalty AS reverse_cost
497 | FROM ruteoamg.ways JOIN ruteoamg.configuration
498 | USING (tag_id)',
499 | 1017, 67272,
500 | directed := true))
501 | SELECT ruta.*, b.the_geom
502 | FROM ruta
503 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
504 | ```
505 | #### 4.2.2 Ejercicio 10 - Ruteo para vehículos con penalización
506 |
507 | Vamos a cambiar los valores de `penalty` para que algunos tipos de vía no sea utilizados:
508 |
509 | ```sql
510 | --- Vamos a modificar el penalty de las vías primarias
511 | UPDATE ruteoamg.configuration SET penalty=100 WHERE tag_value = 'primary';
512 | ```
513 | Ahora volvemos a calcular la ruta:
514 | ```sql
515 | WITH ruta as (SELECT * FROM pgr_dijkstra('
516 | SELECT gid AS id,
517 | source,
518 | target,
519 | cost_s * penalty AS cost,
520 | reverse_cost_s * penalty AS reverse_cost
521 | FROM ruteoamg.ways JOIN ruteoamg.configuration
522 | USING (tag_id)',
523 | 1017, 67272,
524 | directed := true))
525 | SELECT ruta.*, b.the_geom
526 | FROM ruta
527 | LEFT JOIN ruteoamg.ways b ON ruta.edge = b.gid;
528 | ```
529 | ¿Qué ha cambiado con respecto al ejercicio 9?
530 |
531 | ### **4.3 Isocronas**
532 | Las isocronas son iso-lineas que unen puntos con el mismo tiempo de deslazamiento respecto a un origen. Para este ejercicio vamos a usar la Red Nacional de Caminos de INEGI que ya está precargada en la base de datos, una capa de hospitales públicos generada a partir de datos de la Secretaría de Salud ([Catálogo de Clave Única de Establecimientos de Salud - CLUES](http://www.dgis.salud.gob.mx/contenidos/sinais/s_clues.html)), y la función de pgRouting `pgr_drivingDistance`.
533 |
534 | #### 4.3.1 pgr_drivingDistance
535 |
536 | La función pgr_drivingDistance calcula la distancia manejando desde uno o varios nodos iniciales. Usando el algoritmo de Dijkstra extrae todos los nodos que tengan un costo de desplazamiento igual o menor al valor `distance`, este parámetro puede ser en unidades de tiempo o distancia (las mismas que el parámetro de costo que vayamos a utilizar).
537 |
538 | **Sumario de Signaturas**
539 |
540 | ```sql
541 | pgr_drivingDistance(edges_sql, start_vid, distance) -- Un único nodo de origen
542 | pgr_drivingDistance(edges_sql, start_vid, distance, directed) -- Un único nodo de origen dirigido
543 | pgr_drivingDistance(edges_sql, start_vids, distance, directed, equicost) -- Varios nodos de origen
544 |
545 | RETURNS SET OF (seq, [start_vid,] node, edge, cost, agg_cost)
546 | ```
547 | El algoritmo retorna una sequencia, el nodo de inicio `start_vid,`, el nodo actual de la ruta `node`, el segmento recorrido `edge` para llegar al nodo actual, el costo y el costo acumulado desde `start_vid` a `node`.
548 |
549 | Para calcular el parámetro de costo `cost_s` en la RNC de INEGI podemos utilizar los atributos `longitud` y `velocidad`, donde `cost_s = longitud / (velocidad * 1000 / 3600)` para obtener el costo en segundos.
550 |
551 | ```sql
552 | ALTER TABLE ruteoinegi.red_vial ADD COLUMN cost_s FLOAT;
553 | ```
554 | ```sql
555 | UPDATE ruteoinegi.red_vial
556 | SET cost_s = longitud / (velocidad * 1000 / 3600);
557 | ```
558 |
559 | #### 4.3.2 Ejercicio 11 - Un solo origen
560 | Para este ejericio vamos a calcular la distancia de manejo desde el Hospital General Regional 46 del IMSS (`id_union = 635443`) y `distance = 3600` (en segundos). En parmetro de `id_union` lo hemos calculado previamente asignando el nodo más cercano de la RNC a todos los hospitales. Si observamos la tabla `ruteoinegi.red_vial` veremos que tiene dos atributos `id_union` y `distancia`, este segundo atributo nos indica la distancia lineal entre el punto donde está ubicado un hospital y el nodo más cercano de la red, que podemos encontrar en la tabla `ruteoinegi.union`.
561 |
562 | Para realizar este proceso hemos utilizado la consulta que aparece a continuación. En ella para cada hospital calculamos cual es el nodo de la red (tabla `ruteoinegi.union`) más cercano a cada hospital y la distancia a la que se encuentra. El atributo de la distancia nos da una idea de lo correcto que puede ser el cálculo de una ruta para cada hospital. Esto es necesario ya que la RNC, como ya habíamos comentado, no cuenta con toda la red de calles en las zonas urbanas y tampoco están algunos caminos/terracerías de zonas rurales. Este proceso de encontrar el nodo más cercano de la red lo vamos a tener que realizar siempre ya que para los parámetros de `start_vid` y `end_vid` de los diferentes algoritmos siempre tenemos que utilizar las IDs de los nodos de la red (en este caso es el atributo `id_union`)
563 |
564 | ```sql
565 | WITH foo as (
566 | SELECT
567 | hospitales.id,
568 | closest_node.id_union,
569 | closest_node.dist
570 | FROM ruteoinegi.hospitales
571 | CROSS JOIN LATERAL -- Este CROSS JOIN lateral funciona como un bucle "for each"
572 | (SELECT
573 | id_union,
574 | ST_Distance("union".geom, hospitales.geom) as dist
575 | FROM ruteoinegi."union"
576 | ORDER BY ST_Distance("union".geom, hospitales.geom)
577 | LIMIT 1 -- Este limit 1 hace que solo obtengamos el nodo más cercano de la red
578 | ) AS closest_node)
579 | UPDATE ruteoinegi.hospitales -- Finalmente actualizamos la capa de hospitales
580 | SET id_union = foo.id_union,
581 | distancia = foo.dist
582 | FROM foo
583 | WHERE hospitales.id = foo.id
584 | ```
585 | Ahora ya estamos listos para usar el algoritmo pgr_drivingDistance, a partir del que luego podemos generar las isocronas (utilizando QGIS).
586 |
587 | ```sql
588 | SELECT subquery.seq,
589 | subquery.node,
590 | subquery.edge,
591 | subquery.cost,
592 | subquery.aggcost,
593 | st_transform(pt.geom,4326) as geom -- Reproyectamos a epsg:4326 para poder ver el resultado en pgAdmin4
594 | FROM pgr_drivingDistance('SELECT
595 | id_red as id,
596 | union_ini AS source,
597 | union_fin AS target,
598 | cost_s AS cost
599 | FROM ruteoinegi.red_vial', 635443, 3600, false)
600 | subquery(seq, node, edge, cost, aggcost)
601 | JOIN ruteoinegi.union pt ON subquery.node = pt.id_union;
602 | ```
603 | #### 4.3.3 Ejercicio 12 - Varios orígenes
604 | Para generar la distancias de manejo desde varios orígenes tenemos que volver a utilizar un `ARRAY`. En este caso concreto vamos a extraer todos los `id_union` desde la tabla `ruteoinegi.hospitales` y pasárselos a `pgr_drivingDistance`. Para que podamos generar las isocronas de todos los hospitales en conjunto tenemos que hacer dos procesos, primero calculamos la distancia de manejo desde cada hospital y posteriormente agrupamos los resulados por nodo (y geometría) y nos quedamos con el costo agregado mínimo que nos va a representar el tiempo mínimo desde ese nodo a un hospital.
605 |
606 | ```sql
607 | WITH ruteo as (
608 | SELECT subquery.seq,
609 | subquery.start_v,
610 | subquery.node,
611 | subquery.edge,
612 | subquery.cost,
613 | subquery.aggcost,
614 | pt.geom
615 | FROM pgr_drivingDistance('SELECT
616 | id_red as id,
617 | union_ini AS source,
618 | union_fin AS target,
619 | cost_s AS cost
620 | FROM ruteoinegi.red_vial',
621 | array(SELECT id_union FROM ruteoinegi.hospitales), 1800, false)
622 | subquery(seq, start_v, node, edge, cost, aggcost)
623 | JOIN ruteoinegi.union pt ON subquery.node = pt.id_union)
624 | SELECT node, geom, min(aggcost) AS aggcost
625 | FROM ruteo
626 | GROUP By node, geom;
627 | ```
628 | #### 4.3.4 Ejercicio 13 - isocronas
629 |
630 | Para generar las isocronas necesitamos el resultado del ejercicio anterior, podemos crear la tabla usando:
631 |
632 | ```sql
633 | CREATE TABLE public."elnombrequetuquieras" as (
634 | WITH ruteo as (
635 | SELECT subquery.seq,
636 | subquery.start_v,
637 | subquery.node,
638 | subquery.edge,
639 | subquery.cost,
640 | subquery.aggcost,
641 | pt.geom
642 | FROM pgr_drivingDistance('SELECT
643 | id_red as id,
644 | union_ini AS source,
645 | union_fin AS target,
646 | cost_s AS cost
647 | FROM ruteoinegi.red_vial',
648 | array(SELECT id_union FROM ruteoinegi.hospitales), 1800, false)
649 | subquery(seq, start_v, node, edge, cost, aggcost)
650 | JOIN ruteoinegi.union pt ON subquery.node = pt.id_union)
651 | SELECT node, geom, min(aggcost) AS aggcost
652 | FROM ruteo
653 | GROUP By node, geom);
654 | ```
655 |
656 | Esto creará la tabla "elnombrequetuquieras" en el `schema public`. Para los siguientes pasos vamos a necesitar QGIS así que a continuación vamos a configurar la conexión a la base de datos dentro de QGIS.
657 | Para ello vamos al menú panel del navegador y en `PostGIS` creamos nueva conexión haciendo clic con el botón derecho.
658 |
659 | 
660 |
661 | Ahora podemos cargar la tabla que acabamos de crear que estará en el `schema public`.
662 |
663 | 
664 |
665 | Una vez cargada la capa vamos a exportar a un archivo local para poder trabajar con mayor comodidad sin depender de la conexión. Para ello hacemos clic con el botón derecho sobre la capa y la guardamos como un `geopackage`.
666 |
667 | 
668 |
669 | Para siguiente paso vamos a interpolar sobre la capa de puntos y generar una superficie de costo continua. Podemos utilizar la herramienta para generar TINs desde la caja de herramientas.
670 | 
671 | Configuramos la herramienta como se ve en la imagen.
672 | 
673 | El siguiente paso es generar las isolineas, en este caso, isocronas. Para ello vamos a usar la herramienta de `Curvas de nivel (Contours)` y la vamos a configurar como aparecen en la siguiente imagen.
674 | 
675 |
676 | El resultado final con un poco de diseño.
677 | 
678 |
679 | ### **4.4 Ruteo Masivo**
680 |
681 | Una de las capacidades más notables de pgRouting es la posibilidad de realizar operaciones con grandes cantidades de datos de forma extremadamente rápida. Por ejemplo, podemos contestar preguntas del tipo: ¿Cuáles centros de salud son los más cercanos a cada la localidad de Jalisco?¿Cuáles son los 3 hospitales más cercanos a cada centro de salud? ¿Cuáles localidades se encuentran a más de 30 minutos de una escuela?¿Cuál es la accesibilidad de las escuelas de todo un estado?¿Qué zonas de un estado tienen poca accesibilidad a un servicio (escuelas, centros de salud, taquerías, etc).
682 |
683 | #### 4.4.1 Ejercicio 14 - Hospitales más cercanos a localidades
684 | En este ejercicio vamos a utilizar la función `pgr_dijkstraCost` para analizar cuáles son los tres hospitales más cercanos a cada localidad de Jalisco. Para ello vamos a usar las tablas `ruteoinegi.hospitales` y `ruteoinegi.locpob`. De nuevo vamos a utilizar arreglos (`ARRAY`) para definir los parametros de los nodos de origen y destino del algoritmo `pgr_dijkstraCost`. Esto nos va a permitir analizar 10972 localidades contra 92 hospitales en unos pocos minutos (¡Más de 1,000,000 de combinacines posibles!). El tiempo de ejecución de la consulta es de 13 minutos 44 segundos en nuestro servidor. ¡¡¡Nada mal!!! Para no emplear demasiado tiempo vamos a limitar el número de localidades a analizar añadiendo un pequeño filtro al arreglo y seleccionando las localidaes con `id<1000`.
685 | ```sql
686 | CREATE TABLE ruteoinegi.localidadesVShospitales as (SELECT *
687 | FROM pgr_dijkstraCost(
688 | 'SELECT id,
689 | union_ini as source,
690 | union_fin as target,
691 | cost_s as cost
692 | FROM ruteoinegi.red_vial',
693 | array(SELECT id_union FROM ruteoinegi.locpob WHERE id<1000),
694 | array(SELECT id_union FROM ruteoinegi.hospitales),
695 | directed := false));
696 | ```
697 | Ahora vamos a seleccionar solo los tres hospitales más cercanos a cada localidad.
698 |
699 | ```sql
700 | CREATE TABLE ruteoinegi.locpobvshospitalesrank as ( SELECT foo.* FROM (
701 | SELECT localidadesVShospitales.*, rank() over (partition BY start_vid ORDER BY agg_cost asc)
702 | FROM ruteoinegi.localidadesVShospitales) foo WHERE RANK <= 3);
703 | ```
704 |
705 | El resultado de esta consulta es el costo de desplazamiento acumulado `agg_cost` desde cada localidad `start_vid` a todos los hospitales `end_vid`. Podemos mejorar la visualización de los resultados añadiendo los nombres de las localidades, los nombres de los hospitales y generando una linea usando `st_makeline` en caso de que queramos crear un mapa.
706 | ```sql
707 | CREATE TABLE ruteoinegi.matriz_locpobVShospitales as (
708 | SELECT row_number() over (order by a.start_vid) as id, a.start_vid as nodo_inicio, b.nom_loc as localidad,
709 | a.end_vid as nodo_fin, c."nombre de la unidad" as hospital,
710 | c."nombre de tipologia" as tipologia_hosp, a."agg_cost" / 60 as tiempo_minutos, a."rank",
711 | st_makeline(b.geom,c.geom)
712 | FROM ruteoinegi.locpobvshospitalesrank a
713 | LEFT JOIN ruteoinegi.locpob b ON a.start_vid = b.id_union
714 | LEFT JOIN ruteoinegi.hospitales c ON a.end_vid = c.id_union);
715 | ```
716 |
--------------------------------------------------------------------------------
/imgs/bbdd_taller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/bbdd_taller.png
--------------------------------------------------------------------------------
/imgs/conexion_postgis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/conexion_postgis.png
--------------------------------------------------------------------------------
/imgs/ejercicio_1_pgadmin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/ejercicio_1_pgadmin.png
--------------------------------------------------------------------------------
/imgs/ejercicio_1_qgis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/ejercicio_1_qgis.png
--------------------------------------------------------------------------------
/imgs/ejercicio_1_qgis_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/ejercicio_1_qgis_2.png
--------------------------------------------------------------------------------
/imgs/pgadmin_geometry_viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/pgadmin_geometry_viewer.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_1.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_2.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_3.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_4.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_5.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_6.png
--------------------------------------------------------------------------------
/imgs/qgis_iso_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnanclares/Taller-pgRouting/c1a1a65bc0ad270083e8aba5747c282a8db42d30/imgs/qgis_iso_7.png
--------------------------------------------------------------------------------
/importacion_de_archivos_usando_osm2pgrouting.txt:
--------------------------------------------------------------------------------
1 | osm2pgrouting -f map.osm -c mapconfig_for_cars_mod.xml -d gislocal --schema ruteoamg -U username -W userpassword --clean --chunk 20000
2 |
--------------------------------------------------------------------------------
/iscronas.qml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 1
6 | 1
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 | 0
730 | 0
731 | 1
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 | 0
815 |
816 |
833 | 0
834 | generatedlayout
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 | fid
847 |
848 | 1
849 |
850 |
--------------------------------------------------------------------------------
/mapconfig_for_cars_mod.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------