Heat Map Showing Percentage Of Adult Population At High Risk Of Diabetes Using A Q640 1809864

Using geographical heat maps to visualise cultural influence

Streets, squares and places are usually named after influential people. As a consequence, place names give an idea of ​​who shaped local cultures. In this post, I use open street map data to visualize the impact of various historical figures in Germany.

Requesting data from an open street map

To find out where geographic features are named after certain historical figures, we need a database of geographic names and coordinates. In this post, we will use the open street map of the volunteer project for this.

It is possible to query the open street map database using the Python overpy module. Before we write a function to do this, let’s first import the modules we need for this project.

import overpy  import time  import matplotlib.pyplot as plt  import pandas as pd  import geopandas as gpd  import numpy as np  from scipy import ndimage  

The API of open street map is thankfully explained here. We follow this guide and write a query_str which defines which text we want to look for in the place names of which country. All the place names which match the text are saved in the local variable df together with their latitude and longitude. The function run_overpy_query() does all that.

API_WAIT_TIME = 60  # seconds  def run_overpy_query(search_term, country_code='DE'):        api=overpy.Overpass()      query_str = '''      area["ISO3166-1"="{0}"][admin_level=2];      (node["name"~"{1}", i](area);      way["name"~"{1}", i](area);      rel["name"~"{1}", i](area);      );      out center;      '''.format(country_code, search_term)      try:          r = api.query(query_str)      except:          print('Query unsuccessful. Wait {} seconds and try again.'.format(API_WAIT_TIME))          time.sleep(API_WAIT_TIME)          r = api.query(query_str)        # extract coordinates from query result      coords  = []      coords += [(float(node.lon), float(node.lat)) for node in r.nodes]      coords += [(float(way.center_lon), float(way.center_lat)) for way in r.ways]      coords += [(float(rel.center_lon), float(rel.center_lat)) for rel in r.relations]      df = pd.DataFrame(coords)      df.columns = ['lon', 'lat']        return df  

Please note that, as with many APIs, we can run into a rate limitation if we query too often. Waiting 60 seconds (API_WAIT_TIME) should fix that.

Let’s try out the function to see where Germany’s first modern chancellor, Konrad Adenauer, gets honored.

df_ka = run_overpy_query(search_term='adenauer')  df_ka.head()  

The result looks like this:

lat long
9.988323 48.388831
8.692704 49.407714
6.273176 50.887146
13.307399 52.500242
8.929486 48.466880

No, I also don’t know where these places are. But we can turn to a map of Germany to find out.

Visualising open street map data

Before we turn to the heat map, it is worth plotting the results first with a simple scatter plot.

def visualise_osm_scatter(search_term, title_name, country_code='DE', author_line=True):      df = run_overpy_query(search_term=search_term, country_code=country_code)      world = gpd.read_file('PATH\TO\SHAPE\FILE\FOLDER\ne_10m_admin_1_states_provinces\ne_10m_admin_1_states_provinces.shp')      fig, ax = plt.subplots(figsize=[8, 9.5])      ax.axis('off')      world[world['iso_a2']=='DE'].plot(color='w', edgecolor='black', linewidth=0.5, ax=ax)      ax.scatter(df['lon'], df['lat'], c='red')      ax.set_title('Location of the {} placesnnamed after {}n'.format(len(df), title_name),                   color=(0.41, 0.41, 0.41),                   size=20)      #author line      if author_line:          fig.text(0.99, 0.01, '@ri', color='lightgrey', style='italic',               horizontalalignment='right')        return fig, ax  

Note how we first query the API using our new function run_overpy_query() and then visualise the results with ax.scatter(). For reference, I use a map of Germany with federal state borders (world) which I got from here. Otherwise, the function sets a title on top and an author line in the bottom right hand corner.

The result looks like this:

fig, ax = visualise_osm_scatter('adenauer', 'Konrad Adenauer')  

Apparently, the first political leader of West Germany gets honored in place names all over, well, West Germany. Thirty years after the reunification of Germany he hardly has any place named after him in the former East.

We can get a better overall picture by combining nearby dots on the map into areas. These appear “hot” on a heat map. We adjust the plotting function accordingly.

def visualise_osm(search_term, title_name, country_code='DE', plot='heatmap', author_line=True):        df = run_overpy_query(search_term=search_term, country_code=country_code)        # plot query results on map of Germany      world = gpd.read_file('PATH\TO\SHAPE\FILE\FOLDER\ne_10m_admin_1_states_provinces\ne_10m_admin_1_states_provinces.shp')      fig, ax = plt.subplots(figsize=[8, 9.5])      ax.axis('off')      if plot=='scatter':          world[world['iso_a2']=='DE'].plot(color='w', edgecolor='black', linewidth=0.5, ax=ax)          ax.scatter(df['lon'], df['lat'], c='red')      elif plot=='heatmap':          # see https://nbviewer.jupyter.org/gist/perrygeo/c426355e40037c452434          smoothing = 30  # smoothing factor          bins=1000  # "pixel count"          cmap='hot'  # color scale          xlim = [5.7, 15.2]  # East-West extent of Germany          ylim = [47.1, 55.1]  # North-South extent of Germany          heatmap, xedges, yedges = np.histogram2d(df['lat'], df['lon'],                                                   bins=bins, range=[ylim, xlim])          extent = [yedges[0], yedges[-1],                    xedges[-1], xedges[0]]          logheatmap = np.log(heatmap)          logheatmap[np.isneginf(logheatmap)] = 0          logheatmap = ndimage.filters.gaussian_filter(logheatmap, smoothing, mode='nearest')  # smooth out peaks          logheatmap[logheatmap < 0.0001] = 0  # clearly show areas without occurence          logheatmap = np.max(logheatmap) - logheatmap  # reverse color scale            world[world['iso_a2']=='DE']['geometry'].boundary.plot(color='k', edgecolor='black', linewidth=0.2, ax=ax)          ax.imshow(logheatmap, cmap=cmap, extent=extent)          ax.invert_yaxis()          ax.set(xlim=xlim, ylim=ylim)        ax.set_title('Location of the {} placesnnamed after {}n'.format(len(df), title_name),                      color=(0.41, 0.41, 0.41),                      size=20)      #author line      if author_line:          fig.text(0.99, 0.01, '@ri', color='lightgrey', style='italic',               horizontalalignment='right')        return fig, ax  

Under the hood the heat map is a 2D histogram (np.histogram2d()). It’s as if we layed a grid onto the map of Germany and counted how many red dots appear in each cell. This 2D histogram then gets smoothed (ndimage.filters.gaussian_filter()), i.e. the values of nearby cells are averaged. I plot the result using ax.imshow().

Let’s try it out.

fig, ax = visualise_osm_scatter('adenauer', 'Konrad Adenauer')  

Three hot spots of Adenauer commemoration are obvious: Berlin, Frankfurt, and the Ruhr region. This was not as obvious when using a scatter plot.

East-west differences

While the first West German chancellor gets commemorated nearly exclusively in West Germany, East Germany also features historical figures which are only honored there. Two examples are famous socialist leaders of the 1918 revolution.

Karl Liebknecht:

fig, ax = name_locator('karl-liebknecht', 'Karl Liebknecht')  

Rosa Luxemburg:

fig, ax = visualise_osm('rosa-luxemburg', 'Rosa Luxemburg', plot='heatmap')  

Note how these two figures are nearly exclusively honored in East German place names. The socialist past of this part of Germany is still very much visible.

Another interesting figure is Paul von Hindenburg. Second president of the Weimar Republic who appointed Hitler as chancellor. Where can we find this controversial historical person in place names?

fig, ax = visualise_osm('hindenburg', 'Paul von Hindenburg', plot='heatmap')  

Turns out Hindenburg is nearly only found in West German place names. Germany’s cultural east-west divide is still clearly visible in how geographical entities are named.

Local influence

We can even go further back in time and reveal an enduring influence of historical personalities. Take Martin Luther, a 16th century religious figure who founded the protestant church.

fig, ax = visualise_osm('luther', 'Martin Luther', plot='heatmap')  

That Eastern cluster of hot spots are exactly the places where Martin Luther lived. That influence is still visible today. Also interestingly, the protestant north of Bavaria honors Luther in place names while the catholic south does not.

Conclusion

Heatmaps of place names today can be used to track cultural differences between different regions of Germany. However, the approach used in this post has some caveats:

  • Densely populated regions, like the Ruhr region, can appear hot by chance because the denser the population of a region, the more place names there are there in general in this region.
  • The open street map data is based on volunteer submissions and, thus, does not offer a complete picture of place names in Germany.

Nonetheless, as the examples in this post show, a Germany wide scan of place names is possible using open street map. It reveals the local history of Germany.

The complete code to recreate the analyses and plots of this blog post can, as always, be found on github here.

Like this post? Share it with your followers or follow me on Twitter!

Leave a Comment

Your email address will not be published. Required fields are marked *