Create Animated Maps with Folium
A tutorial on how to create time series maps by animating marker placement with Python and Folium.
Introduction and Motivation
In this article I’ll show you how to create a map with folium and animate the placement of markers according to the time an event occurred at the marked location. We’ll be using the Smart Trash Containers dataset which contains data on smart trash cans deployed by the Pittsburgh, Pennsylvania Department of Public Works. Before discovering this dataset I had never heard of a smart trash can before and it fascinated me which is largely why I decided to explore what the data contained.
If you’re like me and have never heard of a smart trash can before, they are trash cans containing sensors which track how full it is. The goal for Pittsburgh in adopting the public smart trash cans was to aid in efficiency and lower costs[1]. Imagine if you had to send workers out to check trash cans on a scheduled basis. Some of those trash cans would likely be only partially full or empty. Sending workers to empty trash cans is inefficient and costly. Knowing which trash cans are full helps the city send employees to trash cans only when they need to be emptied and can assign employees to other tasks when there aren’t trash cans to empty.
Exploratory Data Analysis
The Smart Trash Containers dataset contains the following columns:
[‘container_id’, ‘receptacle_model_id’, ‘assignment_date’, ‘last_updated_date’, ‘group_name’, ‘address’, ‘city’, ‘state’, ‘zip’, ‘neighborhood’, ‘dpw_division’, ‘council_district’, ‘ward’, ‘fire_zone’, ‘x’, ‘y’]
Most of these variables are fairly intuitive. One caveat however, here I am assuming that the assignment_date
column is the date in which the trash can was installed or made active. I did do some research in attempt to verify this assumption but was unsuccessful. In addition, the ['x', 'y']
columns are latitude and longitude coordinates of the trash can locations. Another discovery I made was that x = longitude
and y = latitude
which I found counterintuitive at first.
Upon completing some exploratory data analysis (EDA), I realized there were few questions about the trash cans that I could actually answer. Some questions that were immediately answerable from the data were;
- What years and months had the highest number of instillations?
- Which neighborhoods of Pittsburgh had the most smart trash containers?
- Where are the trash cans located?
After exploring the answers to these questions, I discovered what I thought was the most interesting question residing in the data. How were these trash cans installed by location over time?
This is ultimately what led me to learn how to animate the placement of markers on a map. I wanted to be able to see where these smart trash cans were installed within the city of Pittsburgh over time.
Creating a Preliminary Map
If you’d like to follow along, the data we’ll be using can be found on my GitHub page and you can copy code from this Jupyter Notebook.
The first thing I needed to do was understand the placement of the trash cans in the city of Pittsburgh. The first step then was to plot a map of Pittsburgh with no trash can markers. To do this, I did a quick search on my favorite search engine for the coordinates of Pittsburgh which returned the coordinates of 40.4406° N, 79.9959° W. These coordinates help us center the map on Pittsburgh. The code to create the map centered on Pittsburgh we will place the trash can location markers on is as follows.
import folium
containers_map = folium.Map(location=[40.4406, -79.9959], zoom_start=10)
The resulting map:
Next I wanted to make sure all of the smart trash container locations would fit within the frame so I plotted these on the map. You can get more info on how this code works from a previous article I wrote.
records = df[['x', 'y']].to_records()
containers_map = containers_map = folium.Map(location=[40.4406, -79.9959], zoom_start=11)
# Add the markers to the map...
for record in records:
folium.Marker(location=[record[2], record[1]],
icon=folium.Icon(color='green',
icon_color='white',
icon='trash-o',
prefix='fa',
)
).add_to(containers_map)
I’ve left the zoom_start
parameter set to 11 here because at 12 the marker showing the southern most trash can won’t appear in the map. I’m not sure if this is an accurate datapoint or an error. I believe it’s an error as it’s so far removed from the main cluster of trash can locations but I cannot prove this so I’ve left it as is.
Now that we have a baseline map, we can look at animating the plotting of the markers to show where the trash cans were installed over time.
Animating the Map
Data Cleaning
As with any data science project you embark on, there will always be some data cleaning needed to accomplish your goal. With this project there was some data cleaning needed to get the data ready for animation. The Folium library contains a TimestampedGeoJson
plugin that allows you to animate the placement of markers on a Folium map. According to the documentation the data must have the following elements.
- The data must be GeoJson.
- Each feature has a ‘times’ property with the same length as the coordinates array.
- Each element of each ‘times’ property is a timestamp in ms since epoch, or in ISO string.
- The ‘times’ property must be contained within a list.
Fortunately the Data.gov site provided the GeoJson file in addition to a CSV file. The next two requirements were a little more tricky. The data provided in the GeoJson file contained properties for assignment_date
which we are using as the installation date and therefore the date we wish to animate the placement of the markers for. The second issue in the third bullet point above is that the assignment_date
values came in the format of '2017-09-14T13:24:40.35'
. To remedy this, I cleaned the data in the following manner.
import jsonwith open('data/smart_trash_containers.geojson') as j:
data = json.load(j)for i in range(len(data['features'])):
data['features'][i]['properties']['times'] = [data['features'][i]['properties']['assignment_date'][:19]]
Yes, this looks sloppy on Medium. Just head over to my GitHub and look at the full code. Essentially what this code is doing is grabbing only the first 19 characters of the assignment_date
feature so it appears in a YYYY-MM-DD HH:mm:ss
format, renaming the feature to times
so the TimestampedGeoJson
will recognize it as the time to do the animations with, and putting the times
values into a list. In other words the data we started with '2017-09-14T13:24:40.35'
becomes [‘2017–09–14T13:24:40’]
.
Creating the Animation
Now that the data is in a format that can be interpreted by TimestampedGeoJson
, all we need do now is create the animation.
containers_map = folium.Map(location=[40.4406, -79.9959], zoom_start=12)TimestampedGeoJson(data, transition_time=20).add_to(containers_map)
And there you have it. You can now see where the Smart Trash Containers were placed over time in Pittsburgh, PA. The above output is only a short clip of the full dataset as animating the full dataset took about 9 minutes in playback time. Also, since most of the trash can installations took place in Spring and Summer, there is a lot of time that passes without many markers being placed.
If you’d like to see the full code all together, you can visit the NB Viewer version which has interactive outputs, or my GitHub page. Also, here are a couple of other articles I’ve written on Folium if you’re interested. As always, thanks for reading. Drop a comment if you have any questions.
References
- The City of Pittsburgh. (2019, June 4). Smart Garbage Cans Deployed Citywide. Retrieved from https://pittsburghpa.gov/press-releases/press-releases.html?id=2986