Wenyan Deng

Ph.D. Candidate

Massachusetts Institute of Technology

Folium: Interactive Choropleth and Scatter Maps

August 18, 2017

I have explained in an earlier post how to overlay choropleth maps with scatter maps using Folium. In the example in this post, I show a more complicated map and explain a different pop-up option. See the final product and the codes at my GitHub repository.

The case study

This study uses a set of data on Sri Lanka Freedom Party's (SLFP) vote share in the 1970 parliamentary election, as well as the police station attacks data we used in the last post.

Getting started

After opening a jupyter notebook, import the following:

%matplotlib inline

import geopandas as gpd

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

Load and process data files

Load the shape file as geo_df:

geo_df = gpd.read_file("data1/map.shp")

geo_df['coords'] = geo_df['geometry'].apply(lambda x: x.representative_point().coords[:])

geo_df['coords'] = [coords[0] for coords in geo_df['coords']]


geo_df.rename(columns = {"polling_di" : "Divisions"}, inplace = True)

In this example, we also want to see if police station attacks happened along major roads, highways, and arteries. Here, load the Sri Lankan roads shapefile:

roads_df = gpd.read_file("dataroads/roads.shp")

roads_df['coords'] = roads_df['geometry'].apply(lambda x: x.representative_point().coords[:])

roads_df['coords'] = [coords[0] for coords in roads_df['coords']]

Load the file for SLFP vote share:

df1 = pd.read_excel("1970_SLFP.xlsx")

df1 = df1.dropna(subset = ['share'])


Merge the geo-dataframe with the SLFP vote share file, on electoral divisions ("Divisions"):

geo_merge = geo_df.merge(df1, on='Divisions', how='left')

col_name = geo_merge.columns[0]

geo_merge = geo_merge.dropna(subset = ['share'])


Load police data files, one for those under insurgent control and one for those not:

police = pd.read_excel('controlled.xlsx')

police2 = pd.read_excel('notcontrolled.xlsx')


The data is ready for us to get mapping. First, import the following:

import folium

from folium.element import IFrame

from geopy.geocoders import Nominatim

Get the base map:

m = folium.Map([8, 79.8], zoom_start=8)


Specify choropleth (SLFP vote share) layer options:

ft = "share"

cmap = folium.colormap.linear.YlOrBr.scale(geo_merge[ft].min(), geo_merge[ft].max())


style_function=lambda feature: {

'fillColor': cmap(feature['properties'][ft]),

'fillOpacity' : 0.9, 'weight' : 1,

'color' : 'black'


Make gradient legend:

cmap.caption = 'SLFP Coalition Vote Share (Parliamentary Election, 1970)'


Specify roads layer options:


style_function=lambda feature: {

'fillOpacity' : 0.5, 'weight' : 1.5,

'color' : 'gray'


Add police stations as scatter points. If you would like the marker to cluster, the first line below serves that purpose. If not, just delete it.

my_marker_cluster = folium.MarkerCluster().add_to(m)

for ix, row in police.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=500, popup="Station: " + row['stations'], color='#0000FF', fill_color='#0000FF').add_to(m)

for ix, row in police2.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=500, popup="Station: " + row['stations'], color='#00FF33', fill_color="#00FF33").add_to(m)

Save map:


Your final product would look something like the screenshot below. Clicking on each station would give the name of the station.