Create Animated Maps with Folium

Michael McManus
6 min readDec 24, 2021

--

A tutorial on how to create time series maps by animating marker placement with Python and Folium.

Photo by L B on Unsplash

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;

  1. What years and months had the highest number of instillations?
  2. Which neighborhoods of Pittsburgh had the most smart trash containers?
  3. 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:

Image by Author

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)
Image by Author

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)
Image by Author

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

  1. The City of Pittsburgh. (2019, June 4). Smart Garbage Cans Deployed Citywide. Retrieved from https://pittsburghpa.gov/press-releases/press-releases.html?id=2986

--

--

Michael McManus

Master of Applied Data Science — University of Michigan School of Information.