147 lines
6.1 KiB
Python
147 lines
6.1 KiB
Python
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
import osmnx as ox
|
|
import pandas as pd
|
|
|
|
ox.config(log_console=True, use_cache=True)
|
|
weight_by_length = False
|
|
|
|
# define the study sites as label : query
|
|
places = {'Bucuresti' : {'q':'Bucharest', 'exclude_place_ids':'298450'},
|
|
'Cluj-Napoca' : {'q':'Cluj Napoca', 'exclude_place_ids':'195112'},
|
|
'Oradea' : {'q':'Oradea', 'exclude_place_ids':'29122273' },
|
|
'Constanta' : 'Constanta',
|
|
'Suceava' : 'Suceava',
|
|
'Iasi' : {'q':'Iasi', 'exclude_place_ids':'324553' },
|
|
'Timisoara' : {'q':'Timisoara', 'exclude_place_ids':'246522' },
|
|
'Sibiu' : {'q':'Sibiu', 'exclude_place_ids':'223295999' },
|
|
'Craiova' : {'q': 'Craiova', 'exclude_place_ids':'3810059'},
|
|
'Brasov' : {'q': 'Brasov', 'exclude_place_ids':'785207'},
|
|
'Galati' : {'q':'Galati', 'exclude_place_ids':'11640887,305993'},
|
|
'Ploiesti' : {'q':'Ploiesti', 'exclude_place_ids':'251193'},
|
|
'Braila' : {'q':'Braila', 'exclude_place_ids':'1306402,885269'},
|
|
'Arad' : 'Arad' ,
|
|
'Pitesti' : {'q':'Pitesti', 'exclude_place_ids':'326999'},
|
|
'Bacau' : {'q':'Bacau', 'exclude_place_ids':'14936163'},
|
|
'Targu Mures' : {'q':'Targu Mures', 'exclude_place_ids':'130794'},
|
|
'Baia Mare' : {'q':'Baia Mare','exclude_place_ids':'321848'},
|
|
'Buzau' : 'Buzau' ,
|
|
'Botosani' : 'Botosani' ,
|
|
'Satu Mare' : 'Satu Mare' ,
|
|
'Ramnicu Valcea' : {'q':'Ramnicu Valcea','exclude_place_ids':'335988'},
|
|
'Drobeta-Turnu Severin' : 'Drobeta-Turnu Severin' ,
|
|
'Piatra Neamt' : {'q':'Piatra Neamt','exclude_place_ids':'326492'},
|
|
'Targu Jiu' : 'Targu Jiu' ,
|
|
'Targoviste' : {'q':'Targoviste', 'exclude_place_ids':'315265' },
|
|
'Focsani' : {'q':'Focsani', 'exclude_place_ids':'146569'},
|
|
'Bistrita' : 'Bistrita' ,
|
|
}
|
|
|
|
# verify OSMnx geocodes each query to what you expect
|
|
gdf = ox.gdf_from_places(places.values())
|
|
print(gdf)
|
|
|
|
for place in places.keys():
|
|
G = ox.graph_from_place(places[place])
|
|
ox.plot_graph(ox.project_graph(G), save=True, show=False, file_format='svg', filename=place)
|
|
|
|
exit(0)
|
|
|
|
|
|
bearings = {}
|
|
for place in sorted(places.keys()):
|
|
|
|
print("Processing", place)
|
|
|
|
# get the graph
|
|
query = places[place]
|
|
G = ox.graph_from_place(query, network_type='drive')
|
|
|
|
# calculate edge bearings
|
|
G = ox.add_edge_bearings(G)
|
|
|
|
if weight_by_length:
|
|
# weight bearings by length (meters)
|
|
streets = [(d['bearing'], int(d['length'])) for u, v, k, d in G.edges(keys=True, data=True)]
|
|
city_bearings = []
|
|
for street in streets:
|
|
city_bearings.extend([street[0]] * street[1])
|
|
bearings[place] = pd.Series(city_bearings)
|
|
else:
|
|
# don't weight bearings, just take one value per street segment
|
|
bearings[place] = pd.Series([data['bearing'] for u, v, k, data in G.edges(keys=True, data=True)])
|
|
|
|
|
|
# function to draw a polar histogram for a set of edge bearings
|
|
def polar_plot(ax, bearings, n=36, title=''):
|
|
|
|
bins = [ang * 360 / n for ang in range(0, n + 1)]
|
|
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':'Roboto', 'size':24, 'weight':'bold'}
|
|
xtick_font = {'family':'Roboto', 'size':10, 'weight':'bold', 'alpha':1.0, 'zorder':3}
|
|
ytick_font = {'family':'Roboto', '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)
|
|
|
|
def count_and_merge(n, bearings):
|
|
# make twice as many bins as desired, then merge them in pairs
|
|
# this prevents bin-edge effects around common values like 0 deg and 90 deg
|
|
n = n * 2
|
|
bins = [ang * 360 / n for ang in range(0, n + 1)]
|
|
count, _ = np.histogram(bearings, bins=bins)
|
|
|
|
# move the last bin to the front, so eg 0.01 deg and 359.99 deg will be binned together
|
|
count = count.tolist()
|
|
count = [count[-1]] + count[:-1]
|
|
|
|
count_merged = []
|
|
count_iter = iter(count)
|
|
for count in count_iter:
|
|
merged_count = count + next(count_iter)
|
|
count_merged.append(merged_count)
|
|
|
|
return np.array(count_merged)
|
|
|
|
# create figure and axes
|
|
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'})
|
|
axes = [item for sublist in axes for item in sublist]
|
|
|
|
# plot each city's polar histogram
|
|
for ax, place in zip(axes, sorted(places.keys())):
|
|
polar_plot(ax, bearings[place], title=place)
|
|
|
|
# add super title and save full image
|
|
suptitle_font = {'family':'Roboto', 'fontsize':60, 'fontweight':'normal', 'y':1.07}
|
|
fig.suptitle('Romanian City Street Network Orientation', **suptitle_font)
|
|
fig.tight_layout()
|
|
fig.subplots_adjust(hspace=0.35)
|
|
plt.gcf().savefig('images/street-orientations.png', dpi=120, bbox_inches='tight')
|
|
plt.close()
|