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.plot()
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'])
df1.head()
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'])
geo_merge.head()
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')
Mapping
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)
folium.TileLayer('cartodbpositron').add_to(m)
Specify choropleth (SLFP vote share) layer options:
ft = "share"
cmap = folium.colormap.linear.YlOrBr.scale(geo_merge[ft].min(), geo_merge[ft].max())
folium.GeoJson(geo_merge,
style_function=lambda feature: {
'fillColor': cmap(feature['properties'][ft]),
'fillOpacity' : 0.9, 'weight' : 1,
'color' : 'black'
}).add_to(m)
Make gradient legend:
cmap.caption = 'SLFP Coalition Vote Share (Parliamentary Election, 1970)'
cmap.add_to(m)
Specify roads layer options:
folium.GeoJson(roads_df,
style_function=lambda feature: {
'fillOpacity' : 0.5, 'weight' : 1.5,
'color' : 'gray'
}).add_to(m)
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:
m.save("HeatMap_SLFP.html")
Your final product would look something like the screenshot below. Clicking on each station would give the name of the station.