87% des trajets s’effectuent sur les routes. Élément essentiel du maillage territorial, pour cette raison, nous étudierons le nouveaux algorithmes comme OSMnx nous présentent une série d'indicateurs qui aident à définir la logique spatiale et l'ordre urbain, révélant l'ordre et le désordre des rues, ainsi que les meilleures stratégies pour la dynamique du transport d'une ville. Dans cette opportunité, nous explorerons la ville de Paris en utilisant les données d'OpenStreetMap et OSMnx dans une première occasion de connaître le réseau routier et d'exécuter une modélisation des itinéraires basée sur les distances et l'impédance et enfin d'appliquer l'indicateur d'orientation des villes afin de quantifier la façon dont les réseaux routiers dans les villes françaises suivent une logique d’ordonnancement géométrique d’une grille unique ou son schéma est hétérogène.
1.- Données de l'API OpenStreetMap pour télécharger des données depuis le réseau routier.
2.- Données Google Maps API pour télécharger les données d'altitude et les associer au réseau routier, pour développer notre modèle d'impédances, défini par la différence d'altitude des itinéraires.
3.- Appliquer l'algorithme OSMnx pour développer les modèles et définir les directions cardinales des axes routiers.
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
import datetime
import pandas as pd
weight_by_length = False
ox.__version__
ox.config(log_console=True, use_cache=True)
ox.__version__
# Nous extrayons les données associées à Paris
place = 'Paris'
place_query = {'city':'Paris', 'state':'Paris', 'country':'France'}
G = ox.graph_from_place(place_query, network_type='drive')
# Nous consultons les données de chaque nœud routier extrait à l'aide des données de l'API Google Elevation
G = ox.add_node_elevations(G, api_key=APIKey)
G = ox.add_edge_grades(G)
Nous utilisons les nœuds pour définir le réseau et nous prenons uniquement les valeurs uniques pour éviter de dupliquer les données et enfin calculer les valeurs de pourcentage de pente.
edge_grades = [data['grade_abs'] for u, v, k, data in ox.get_undirected(G).edges(keys=True, data=True)]
avg_grade = np.mean(edge_grades)
print('Pente moyenne du réseau routier à Paris {} is {:.1f}%'.format(place, avg_grade*100))
med_grade = np.median(edge_grades)
print('Pente médiane du réseau routier à Paris {} is {:.1f}%'.format(place, med_grade*100))
Nous voyons qu'en moyenne la ville de Paris a une condition idéale pour le développement d'une infrastructure pour Ciclovia, maintenant nous allons visualiser le réseau développant un modèle d'impédance en raison de l'état des distances et de la différence d'altitude.
Ce sont les valeurs du violet de basse altitude et celles du jaune de plus haute altitude.
G_proj = ox.project_graph(G)
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')
# Nous cartographions le réseau routier avec les valeurs d'altitude téléchargées
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)
# sélectionnez un nœud d'origine et de destination et un cadre de sélection autour d'eux
origin = ox.get_nearest_node(G, (48.826955,2.366481))
destination = ox.get_nearest_node(G, (48.890483,2.337470))
bbox = ox.bbox_from_point((48.859661,2.333608), distance=5000, project_utm=True)
# Nous définissons notre modèle en fonction des variations de distance et de pente
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']
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)
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)
def print_route_stats(route):
route_grades = ox.get_route_edge_attributes(G_proj, route, 'grade_abs')
msg = 'la pente moyenne est {:.1f}% et le max est {:.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 = 'Le changement altitudinal total est {:.0f} mètres: {:.0f} mètres dans ascension et {:.0f} mètres descente'
print(msg.format(np.sum(route_rises), ascent, abs(descent)))
route_lengths = ox.get_route_edge_attributes(G_proj, route, 'length')
print('Distance totale du trajet: {:,.0f} mètres'.format(np.sum(route_lengths)))
print_route_stats(route_by_length)
print_route_stats(route_by_impedance)
Dans notre modèle de pente la plus raide, nous avons réduit la pente moyenne le long du parcours d'une pente de 21% à une pente de 6%, diminuant la montée totale de 83 mètres à 63 mètres (mais la distance de déplacement augmente de 8,9 km à 14,6 km).
Cette étude examine l'orientation du réseau routier dans les villes françaises à l'aide des données OpenStreetMap et OSMnx. Il mesure l'entropie des relèvements des rues dans les modèles de réseau pondérés et non pondérés, ainsi que la longueur typique des segments de rue de chaque ville, la circuité moyenne, le degré moyen des nœuds et les proportions du réseau d'intersections à quatre voies et d'impasses. Il développe également un nouvel indicateur d’ordre d’orientation qui quantifie la façon dont le réseau routier d’une ville suit la logique d’ordre géométrique d’une grille unique.
places = {'Paris' : 'Paris, France',
'Rennes' : 'Rennes, France',
'Lyon' : 'Lyon, France',
'Reims' : 'Reims, France',
'Le Havre' : 'Le Havre, France',
'Marseille' : 'Marseille, France',
'Saint-Étienne' : 'Saint-Étienne, France',
'Toulon' : 'Toulon, France',
'Toulouse' : 'Toulouse, France',
'Grenoble' : 'Grenoble, France',
'Nice' : 'Nice, France',
'Dijon' : 'Dijon, France',
'Nantes' : 'Nantes, France',
'Strasbourg' : 'Strasbourg, France',
'Nîmes' : 'Nîmes, France',
'Angers' : 'Angers, France',
'Villeurbanne' : 'Villeurbanne, France',
'Le Mans' : 'Le Mans, France',
'Saint-Denis' : 'Saint-Denis, France',
'Montpellier' : {'city':'Montpellier', 'state':'Occitanie', 'country':'France'},
'Aix-en-Provence' : 'Aix-en-Provence, France',
'Clermont-Ferrand' : 'Clermont-Ferrand, France',
'Brest' : 'Brest, France',
'Lille' : 'Lille, France'}
gdf = ox.gdf_from_places(places.values())
gdf
def reverse_bearing(x):
return x + 180 if x < 180 else x - 180
bearings = {}
for place in sorted(places.keys()):
print(datetime.datetime.now(), place)
query = places[place]
G = ox.graph_from_place(query, network_type='drive')
Gu = ox.add_edge_bearings(ox.get_undirected(G))
if weight_by_length:
city_bearings = []
for u, v, k, d in Gu.edges(keys=True, data=True):
city_bearings.extend([d['bearing']] * int(d['length']))
b = pd.Series(city_bearings)
bearings[place] = pd.concat([b, b.map(reverse_bearing)]).reset_index(drop='True')
else:
b = pd.Series([d['bearing'] for u, v, k, d in Gu.edges(keys=True, data=True)])
bearings[place] = pd.concat([b, b.map(reverse_bearing)]).reset_index(drop='True')
Nous créons un histogramme polaire pour chaque ville où chaque barre représentera la direction des rues par rapport aux points cardinaux (Nord, Sud, Est et Ouest) et sa longueur représentera le nombre de rues dans une direction spécifique.
def count_and_merge(n, bearings):
n = n * 2
bins = np.arange(n + 1) * 360 / n
count, _ = np.histogram(bearings, bins=bins)
count = np.roll(count, 1)
return count[::2] + count[1::2]
def polar_plot(ax, bearings, n=36, title=''):
bins = np.arange(n + 1) * 360 / n
count = count_and_merge(n, bearings)
_, division = np.histogram(bearings, bins=bins)
frequency = count / count.sum()
division = division[0:-1]
width = 2 * np.pi / n
ax.set_theta_zero_location('N')
ax.set_theta_direction('clockwise')
x = division * np.pi / 180
bars = ax.bar(x, height=frequency, width=width, align='center', bottom=0, zorder=2,
color='#003366', edgecolor='k', linewidth=0.5, alpha=0.7)
ax.set_ylim(top=frequency.max())
title_font = {'family':'Century Gothic', 'size':24, 'weight':'bold'}
xtick_font = {'family':'Century Gothic', 'size':10, 'weight':'bold', 'alpha':1.0, 'zorder':3}
ytick_font = {'family':'Century Gothic', 'size': 9, 'weight':'bold', 'alpha':0.2, 'zorder':3}
ax.set_title(title.upper(), y=1.05, fontdict=title_font)
ax.set_yticks(np.linspace(0, max(ax.get_ylim()), 5))
yticklabels = ['{:.2f}'.format(y) for y in ax.get_yticks()]
yticklabels[0] = ''
ax.set_yticklabels(labels=yticklabels, fontdict=ytick_font)
xticklabels = ['N', '', 'E', '', 'S', '', 'W', '']
ax.set_xticklabels(labels=xticklabels, fontdict=xtick_font)
ax.tick_params(axis='x', which='major', pad=-2)
n = len(places)
ncols = int(np.ceil(np.sqrt(n)))
nrows = int(np.ceil(n / ncols))
figsize = (ncols * 5, nrows * 5)
fig, axes = plt.subplots(nrows, ncols, figsize=figsize, subplot_kw={'projection':'polar'})
for ax, place in zip(axes.flat, sorted(places.keys())):
polar_plot(ax, bearings[place].dropna(), title=place)
suptitle_font = {'family':'Century Gothic', 'fontsize':40, 'fontweight':'normal', 'y':1.07}
fig.suptitle('Orientation du réseau routier des principales Villes Françaises.', **suptitle_font)
fig.tight_layout()
fig.subplots_adjust(hspace=0.35)
plt.show()
Cette analyse nous permet d'explorer les similitudes et les différences entre les villes françaises. Notez qu'il existe des relations statistiques importantes entre l'ordre d'orientation de la ville, par exemple, en moyenne, les villes ont un schéma hétérogène de leurs orientations, bien que des villes comme Villeurbanne, Rennes, Saint-Denis et Clermont-Ferrand soient beaucoup plus quadrillées que les autres.
Le réseau routier organise et limite la dynamique des transports dans la ville selon une certaine logique spatiale, planifiée ou non, ordonnée ou désordonnée.
Cette étude a présenté un modèle basé sur les algorithmes OSMnx pour l'optimisation des itinéraires cyclables potentiels en fonction de la longueur et de l'altitude du réseau routier en utilisant les données d'OpenStreetMap et de l'API GoogleMaps.
Enfin, le histogramme polaire des orientations a été visualisé pour découvrir pour la première fois la logique spatiale de la structure routière des principales Villes Françaises, montrant que les modèles sont une technique viable pour mesurer empiriquement et visualiser la complexité de l'ordre spatial, illustrant les modèles d'urbanisation et de transport.
Pour un affichage dynamique de l'algorithme OSMnx, il est possible de le trouver ici.