title

Algoritmos de OSMnx para construir, proyectar, visualizar y analizar la red vial del Municipio Libertador - Caracas mediante el modelado de redes de trasporte, cálculo de estadísticas y la orientación media con datos de OpenStreetMap y el API de GoogleMaps.

Nuevos algoritmos como OSMnx nos presentan una serie de indicadores que ayudan a definir la l贸gica espacial y el orden urbano, revelando el orden y el desorden de las calles, as铆 como las mejores estrategias de la din谩mica del transporte de una ciudad. En esta oportunidad exploraremos el Municipio Libertador utilizando datos de OpenStreetMap y OSMnx en una primra oportunidad para conocer la red vial y ejecutar un modelado de rutas basado en las distancias y la impendancia y finalmente aplicar el indicador de orientaci贸n de las ciudades a manera de cuantificar c贸mo la red vial en Caracas sigue una l贸gica de ordenamiento geom茅trico de una cuadr铆cula 煤nica 贸 su patr贸n es heterog茅neo.

Recursos:

1.- Datos del API de OpenStreetMap para descargar los datos de la red.

2.- Datos del API de Google Maps para descargar los datos de altitud y asociarlos a la red vial, para desarrollar nuestro modelo de impendancias, definido este por la diferencia altitudinal de las rutas.

3.- Aplicar el algoristmo de OSMnx para desarrollar los modelos y definir las orientaciones cardinales de los ejes viales.

Importamos las librerias

In [45]:
from api import APIKey 
import networkx as nx
import numpy as np
import osmnx as ox
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
ox.config(log_console=True, use_cache=True)
ox.__version__
Out[45]:
'0.11.4'
In [33]:
# Extraemos los datos asociados al municipio Libertador: Caracas
place = 'Municipio Libertador'
G = ox.graph_from_place('Municipio Libertador, Venezuela', network_type='drive', which_result=5)
In [34]:
# Consultamos los datos para cada nodo vial extraido utilizando los datos de Google Elevation API
G = ox.add_node_elevations(G, api_key=APIKey)
G = ox.add_edge_grades(G)

Calculamos las variaciones de pendiente de la red vial

Usamos los nodos para definir la red y solo tomamos los valores 煤nicos para evitar duplicar los datos y finalmente calculamos los valores porcentuales de pendiente.

In [35]:
edge_grades = [data['grade_abs'] for u, v, k, data in ox.get_undirected(G).edges(keys=True, data=True)]
In [36]:
avg_grade = np.mean(edge_grades)
print('Pendiente Promedio de la red vial en Caracas  {} is {:.1f}%'.format(place, avg_grade*100))

med_grade = np.median(edge_grades)
print('Mediana de la pendiente de la red vial en Caracas {} is {:.1f}%'.format(place, med_grade*100))
Pendiente Promedio de la red vial en Caracas  Municipio Libertador is 6.5%
Mediana de la pendiente de la red vial en Caracas Municipio Libertador is 4.2%

Comparemos estos datos con el Guia de Diseno de Infraestructura y Equipamiento Ciclista elaborado por la Facultad de Arquitectura de la UNAM - M茅xico title

Vemos que en promedio el Municipio Libertador cuenta con una condici贸n Normal para el desarrollo de una infraestructura de Ciclovia si bien lo ideal ser铆a contar con valores cercanos a 1% en pendiente, la actual estructura vial ofrece condiciones igualmente favorables para su planificaci贸n, para ello a continuaci贸n visualizaremos la red elaborando un modelo de impendancia por la condici贸n altitudinal.

Visualizando los nodos viales y su altitud

Siendo los valores de menor altitud violeta y los de mayor altitud amarillo.

In [37]:
G_proj = ox.project_graph(G)
In [38]:
nc = ox.get_node_colors_by_attr(G_proj, 'elevation', cmap='plasma', num_bins=20)
fig, ax = ox.plot_graph(G_proj, fig_height=6, node_color=nc, node_size=12, node_zorder=2, edge_color='#dddddd')

Se incorporan los valores de Altitud a la red vial del Municipio Libertador para visualizar nuestro modelo de impendancias

In [39]:
# get a color for each edge, by grade, then plot the network
ec = ox.get_edge_colors_by_attr(G_proj, 'grade_abs', cmap='plasma', num_bins=100)
fig, ax = ox.plot_graph(G_proj, fig_height=6, edge_color=ec, edge_linewidth=0.8, node_size=0)

Ejecutamos el Modelo de Impendancias definiendo una ruta para calcular la ruta más corta y la ruta con menor pendiente

In [40]:
# Seleccionamos una ruta en la ciudad de Caracas
origin = ox.get_nearest_node(G, (10.501716,-66.946971))
destination = ox.get_nearest_node(G, (10.506104,-66.914742))
bbox = ox.bbox_from_point((10.510113,-66.931951), distance=3000, project_utm=True)
In [41]:
# Definimos nuestro modelo basado en la distancia y las variaciones de pendiente
def impedance(length, grade):
    penalty = grade ** 2
    return length * penalty


for u, v, k, data in G_proj.edges(keys=True, data=True):
    data['impedance'] = impedance(data['length'], data['grade_abs'])
    data['rise'] = data['length'] * data['grade']

Ruta más corta:

In [42]:
route_by_length = nx.shortest_path(G_proj, source=origin, target=destination, weight='length')
fig, ax = ox.plot_graph_route(G_proj, route_by_length, bbox=bbox, node_size=0)

Ruta con menor pendiente:

In [19]:
route_by_impedance = nx.shortest_path(G_proj, source=origin, target=destination, weight='impedance')
fig, ax = ox.plot_graph_route(G_proj, route_by_impedance, bbox=bbox, node_size=0)

Cálculemos los estadísticas para ambas rutas:

In [20]:
def print_route_stats(route):
    route_grades = ox.get_route_edge_attributes(G_proj, route, 'grade_abs')
    msg = 'The average grade is {:.1f}% and the max is {:.1f}%'
    print(msg.format(np.mean(route_grades)*100, np.max(route_grades)*100))

    route_rises = ox.get_route_edge_attributes(G_proj, route, 'rise')
    ascent = np.sum([rise for rise in route_rises if rise >= 0])
    descent = np.sum([rise for rise in route_rises if rise < 0])
    msg = 'Total elevation change is {:.0f} meters: a {:.0f} meter ascent and a {:.0f} meter descent'
    print(msg.format(np.sum(route_rises), ascent, abs(descent)))

    route_lengths = ox.get_route_edge_attributes(G_proj, route, 'length')
    print('Total trip distance: {:,.0f} meters'.format(np.sum(route_lengths)))
In [43]:
# Estad铆sticas de la ruta m谩s corta
print_route_stats(route_by_length)
The average grade is 6.6% and the max is 19.2%
Total elevation change is -85 meters: a 98 meter ascent and a 183 meter descent
Total trip distance: 4,540 meters
In [44]:
# Estad铆sticas para la ruta con la menor pendiente
print_route_stats(route_by_impedance)
The average grade is 2.6% and the max is 11.1%
Total elevation change is -85 meters: a 13 meter ascent and a 98 meter descent
Total trip distance: 5,365 meters

En nuestro modelo de menor pendiente disminuimos la pendiente promedio a lo largo de la ruta de una pendiente del 20% a una pendiente del 11% disminuyendo el ascenso total de 98 m茅tros a 13 m茅tros (pero la distancia de viaje aumenta de 4,5 Km a 5.3 km).

La orientación de la red vial como un nuevo indicador de la lógica urbana de nuestras ciudades

Calculamos las orientaciones y analizamos su frecuencia

In [47]:
G = ox.add_edge_bearings(G)
bearings = pd.Series([data['bearing'] for u, v, k, data in G.edges(keys=True, data=True)])
ax = bearings.hist(bins=30, zorder=2, alpha=0.8)
xlim = ax.set_xlim(0, 360)
ax.set_title('Municipio Libertador Orientaciones Cardinales de la Red Vial y su frecuencia')
plt.show()

Creamos un histograma polar d贸nde cada barra representar谩 la direcci贸n de las calles con relaci贸n a los puntos cardinales (Norte, Sur, Este y Oeste) y su longitud representar谩 la cantidad de calles en una direcci贸n concreta.

In [49]:
import numpy as np
n = 30
count, division = np.histogram(bearings, bins=[ang*360/n for ang in range(0,n+1)])
division = division[0:-1]
width =  2 * np.pi/n
ax = plt.subplot(111, projection='polar')
ax.set_theta_zero_location('N')
ax.set_theta_direction('clockwise')
bars = ax.bar(division * np.pi/180 - width * 0.5 , count, width=width, bottom=0.0)
ax.set_title('Histograma Polar de la red vial del Municipio Libertador de Caracas', y=1.1)
plt.show()

Este histograma nos permite dimensionar la heterogeneidad de