The new Smart Flanders API: a demo

Gilles Vandewiele
3 min readOct 6, 2017

--

Recently, a new version of the Smart Flanders API got released. In this short blog-post, I will show how to use this API by creating a simple plot that shows the occupancy of the different parking spots in Ghent.

We will use my favorite language, Python and the following libraries:
* rdflib (to parse the rdf data provided to us by the API)
* requests (to send a HTTP request to the API)
* pandas and numpy (for our data processing)
* matplotlib (to create our plot)

Now, let’s get started. First thing we need to do is get our data. With requests, this is relatively simple. One thing that we need to pay attention to is the fact that we need to adjust our HTTP header. More specifically, the Accept-field of our header.

URL = 'https://linked.open.gent/parking'#Important! Make sure our accept-header is set to 'application/trig'
headers = {'Accept': 'application/trig'}
r = requests.get(URL, headers=headers)

We can replace our variable URL https://kortrijk.datapiloten.be/parking if we want to generate a plot for the parking spots in Kortrijk. Now that we have our data, let’s load it into a graph using rdflib. This turns out to be relatively easy as well…

g = rdflib.ConjunctiveGraph()
g.parse(data=r.text, format='trig')

Now the variable g will contain data from one of the following hours [2 AM, 5 AM, 11 AM, 14 PM, 17 PM, 20 PM, 23 PM] up until your current time (the time the HTTP request was sent). If we want more data, we can make use of a triple with predicate hydra:previous. So, let’s query our graph to get this triple…

qres = g.query(
"""SELECT * WHERE {
?a hydra:previous ?b .
}""")
for row in qres:
previous_page_url = row['b'].toPython()
r = requests.get(previous_page_url, headers=headers)
g.parse(data=r.text, format='trig')

Calling g.parse with an already loaded graph will just append the new triples (or quads) to the graph. Exactly what we need! Now we have all the data of the hour from the list above, closest to our current time AND all the data of the three hours prior to that. Let’s plot the occupied spots in each parking spot. First, we execute a query in order to get the relevant triples (with a datex:parkingNumberOfVacantSpaces predicate). Then, we iterate over these selected quads (which is, in layman terms , a triple with an extra context field) and do some minimal processing of the data. Finally, we store our processed vectors (consisting of a location, number of vacant spaces and a time) into a pandas DataFrame and parse our time-column.

qres = g.query(
"""SELECT * WHERE {
GRAPH ?context { ?a datex:parkingNumberOfVacantSpaces ?b . }
}""")
vectors = []
for row in qres:
vectors.append([row['a'].toPython().split('/')[-1],
int(row['b'].toPython()),
row['context'].toPython().split('=')[1]])
df = pd.DataFrame(vectors,
columns=['Location', 'Vacant Spaces', 'Time'])
df['Time'] = pd.to_datetime(df['Time'])

Now that we have everything in a pandas DataFrame, it is time to plot our data.

plt.figure(figsize=(20, 10))
for location in np.unique(df['Location']):
df_filtered = df[df['Location'] == location]
df_filtered = df_filtered.sort_values(by='Time')
values = df_filtered['Vacant Spaces'].values
plt.plot(range(len(df_filtered)), df_filtered['Vacant Spaces'].values,
label=df_filtered.iloc[0, :]['Location'])
plt.legend()
plt.xticks(range(0, len(df_filtered), 25),
df_filtered['Time'].apply(lambda x: str(x.hour)+':'+'0'*(x.minute<10)+str(x.minute))[::25],
rotation='vertical')
plt.show()

And that’s it! Here’s the result for Ghent, queried at around 17:15. So our first http request only contains data from 17:00 til 17:15. By using the previousPage-property, we get data from 14:00 til 17:15.

If you want information about the parkings: https://stad.gent/id/parking/<Parking-id> is the location. Of course, you can find all the required information in the HTTP response as well. As an example, to get the name, we can look for a triple with as subject the URI and as predicate rdfs:label. Seems like a lot of people park their car at the Sint-Pieters-Plein! For completeness, here’s the full code below:

--

--