Jupyter Notebook: ohsome API

Python is a widely used programming language, especially in the GIS world, to perform spatial analysis and create visualizations like diagrams. Combining python code, explanations and visualizations in one go, a Jupyter Notebook is a useful tool to achieve just that. So we thought it was time to make Jupyter Notebooks ohsome. More examples will follow!

ohsome API: Data Aggregation Endpoint

Via the ohsome API you have access to the OSM history on a global scale :). Our Swagger documentation provides you with the needed information to fire some requests against the API. More infomation about ohsome can be found here.

In the following we show you some examples how to grab aggregated statistics on the evolution of OSM using Python.

Import Python packages

In [6]:
# pandas for working with table structured data
import pandas as pd

# json for handling with json responses
import json

# requests for http get/post requests
import requests

# plotly for generating interactive graphs
import plotly.plotly as py
import plotly.graph_objs as go

from IPython.display import *

Define global constants

Let's get started and have a more detailed look at three areas in Germany: Heidelberg (hd), Mannheim (ma) and Ludwigshafen (lu).

In [7]:
OHSOME_API = "https://api.ohsome.org/v0.9"

# Define areas of interest
BBOX = { 
    "hd" : "8.6581,49.3836,8.7225,49.4363",
    "ma" : "8.4514,49.4589,8.5158,49.5114",
    "lu" : "8.3936,49.4448,8.4579,49.4974"}

# Define time intervals: https://en.wikipedia.org/wiki/ISO_8601
TIME_MONTHLY = "2007-11-01/2018-11-01/P1M"
TIME_YEARLY = "2007-11-01/2018-11-01/P1Y"

First we request some metadata.

In [8]:
metadata = requests.get(OHSOME_API+"/metadata").json()

display(metadata)
{'apiVersion': '0.9',
 'attribution': {'text': '© OpenStreetMap contributors',
  'url': 'https://ohsome.org/copyrights'},
 'extractRegion': {'spatialExtent': {'coordinates': [[[-180.0, -90.0],
     [180.0, -90.0],
     [180.0, 90.0],
     [-180.0, 90.0],
     [-180.0, -90.0]]],
   'type': 'Polygon'},
  'temporalExtent': {'fromTimestamp': '2007-10-08',
   'toTimestamp': '2019-02-19T23:00:00'}}}

Lossless information on the historical evolution of OSM is available from 8th of October 2007 to 19th of February 2019. This means that all properties of the original OSM data are maintained.

Aggregation endpoint

The aggregation endpoint allows you to retrieve aggregated statistics for the OSM history data. You can filter by any OSM tag (keys, values), define your area of interest (bboxes) and choose any time interval (time).

Count buildings in Heidelberg over time

In [5]:
params = {'bboxes': BBOX['hd'],
          'keys': 'building',
          'time': TIME_MONTHLY
         }

# sending get request to ohsome API 
res = requests.get(OHSOME_API+"/elements/count", params)

display(HTML('<h4>ohsome API request</h4>'))
display(res.url)

# extracting data in json format and storing it in a dataframe
display(HTML('<h4>API response</h4>'))
df = pd.DataFrame(res.json()['result'])
display(df.head())

# plotting the result
data = [go.Scatter(x = df.timestamp, y = df.value)]
fig = dict(data = data, layout = go.Layout(title = "Count of OSM buildings within Heidelberg"))
py.iplot(fig, filename = 'count')

ohsome API request

'https://api.ohsome.org/v0.9/elements/count?bboxes=8.6581%2C49.3836%2C8.7225%2C49.4363&keys=building&time=2007-11-01%2F2018-11-01%2FP1M'

API response

timestamp value
0 2007-11-01T00:00:00Z 0.0
1 2007-12-01T00:00:00Z 2.0
2 2008-01-01T00:00:00Z 2.0
3 2008-02-01T00:00:00Z 2.0
4 2008-03-01T00:00:00Z 2.0
Out[5]:

Ratio between two OSM tag groups over time

The ohsome API also allows you two generate ratios between two different OSM tag groups. In this example we answer the question: What is the percentage of OSM buildings that include house numbers (are tagged with the OSM key addr:housenumber)?

In [10]:
params = {'bboxes': BBOX['hd'],
          'keys': 'building',
          'keys2': 'building,addr:housenumber',
          'time': TIME_MONTHLY
         }

# sending get request to ohsome API 
res = requests.get(OHSOME_API+"/elements/count/ratio", params)

display(HTML('<h4>ohsome API request</h4>'))
display(res.url)

# extracting data in json format and storing it in a dataframe
display(HTML('<h4>API response</h4>'))
df = pd.DataFrame(res.json()['ratioResult'])
display(df.head())

# plotting the result
building = go.Scatter(
    x = df.timestamp,
    y = df.value,
    name = "building"
)

building_housenumber = go.Scatter(
    x = df.timestamp,
    y = df.value2,
    name = "building,addr:housenumber"
)

ratio = go.Scatter(
    x = df.timestamp,
    y = df.ratio,
    yaxis='y2',
    name = "ratio",
    line = dict(
        color = 'green',
        width = 2,
        dash = 'dash')
)

data = [building, building_housenumber, ratio]

layout = go.Layout(
    title='Propotion of OSM buildings with house numbers in Heidelberg',
    yaxis2 = dict(
        side = 'right',
        overlaying = 'y',
        showgrid = False
    ),
    legend = dict(
        orientation = "h")      
)

fig = go.Figure(data, layout)
py.iplot(fig, filename = 'ratio')

ohsome API request

'https://api.ohsome.org/v0.9/elements/count/ratio?bboxes=8.6581%2C49.3836%2C8.7225%2C49.4363&keys=building&keys2=building%2Caddr%3Ahousenumber&time=2007-11-01%2F2018-11-01%2FP1M'

API response

ratio timestamp value value2
0 NaN 2007-11-01T00:00:00Z 0.0 0.0
1 0 2007-12-01T00:00:00Z 2.0 0.0
2 0 2008-01-01T00:00:00Z 2.0 0.0
3 0 2008-02-01T00:00:00Z 2.0 0.0
4 0 2008-03-01T00:00:00Z 2.0 0.0
Out[10]:

Count over time grouped by bounding box

We are not only interested in the Heidelberg area but also in Mannheim and Ludwigshafen. Using the groupBy/boundary resource allows you to get the OSM evolution for all areas with one single request.

In [11]:
bboxes = '|'.join("{}:{}".format(k,v) for (k,v) in BBOX.items())

params = {'bboxes': bboxes,
          'keys': 'building',
          'time': TIME_YEARLY
         }

# sending get request to ohsome API 
res = requests.get(OHSOME_API+"/elements/count/groupBy/boundary", params)

display(HTML('<h4>ohsome API request</h4>'))
display(res.url)

# extracting data in json format and storing it in a dataframe
df = pd.DataFrame(res.json()['groupByResult'])

display(HTML('<h4>API response</h4>'))
display(df.head(10))

display(HTML('<h4>API response: result for hd</h4>'))
display(pd.DataFrame(df.result[0]).head(10))

# plotting the result
result1 = pd.DataFrame(df.result[0])
trace1 = go.Bar(
    x = result1.timestamp,
    y = result1.value,
    name = df.groupByObject[0]
)

result2 = pd.DataFrame(df.result[1])
trace2 = go.Bar(
    x = result2.timestamp,
    y = result2.value,
    name = df.groupByObject[1]
)

result3 = pd.DataFrame(df.result[2])
trace3 = go.Bar(
    x = result3.timestamp,
    y = result3.value,
    name = df.groupByObject[2]
)

data = [trace1, trace2, trace3]
layout = go.Layout(
    title = 'Number of OSM buildings in Heidelberg, Mannheim and Ludwigshafen',
    barmode = 'group',
    legend = dict(
        orientation = "h")
)

fig = go.Figure(data = data, layout = layout)
py.iplot(fig, filename = 'groupBy')

ohsome API request

'https://api.ohsome.org/v0.9/elements/count/groupBy/boundary?bboxes=hd%3A8.6581%2C49.3836%2C8.7225%2C49.4363%7Cma%3A8.4514%2C49.4589%2C8.5158%2C49.5114%7Clu%3A8.3936%2C49.4448%2C8.4579%2C49.4974&keys=building&time=2007-11-01%2F2018-11-01%2FP1Y'

API response

groupByObject result
0 hd [{'timestamp': '2007-11-01T00:00:00Z', 'value'...
1 ma [{'timestamp': '2007-11-01T00:00:00Z', 'value'...
2 lu [{'timestamp': '2007-11-01T00:00:00Z', 'value'...

API response: result for hd

timestamp value
0 2007-11-01T00:00:00Z 0.0
1 2008-11-01T00:00:00Z 248.0
2 2009-11-01T00:00:00Z 414.0
3 2010-11-01T00:00:00Z 493.0
4 2011-11-01T00:00:00Z 4977.0
5 2012-11-01T00:00:00Z 5801.0
6 2013-11-01T00:00:00Z 7537.0
7 2014-11-01T00:00:00Z 10192.0
8 2015-11-01T00:00:00Z 10499.0
9 2016-11-01T00:00:00Z 11333.0
Out[11]:

More examples will follow. Stay tuned!

Comments are closed.