Wenyan Deng

Ph.D. Candidate

Massachusetts Institute of Technology

Folium: Setting up Color Scales

January 14, 2018

In an earlier post, I explained how to make choropleth and scatter maps with Folium. In this post, I explain another way of setting up color bars. The example this time is with armed groups in South and Southeast Asia. I would like to thank Professor Paul Staniland again for giving me the opportunity to be part of this project, and to all the RAs for collecting the data.

See my GitHub repo for the final product and my codes.

The case study

This study uses a set of data on the relationship between armed groups and polity scores of four countries in South and Southeast Asia — Bangladesh, India, Myanmar, and Pakistan.

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

Provinces and states shapefiles

Load and process Shapefiles for provinces and states. No shapefile was available for all Southeast and South Asian countries at the province level, so let's download them separately and concat later. You can also use a loop to do what I do below.

Bangladesh

Bangladesh_geodf = gpd.read_file("BGD_adm_shp/BGD_adm1.shp")

Bangladesh_geodf['coords'] = Bangladesh_geodf['geometry'].apply(lambda x: x.representative_point().coords[:]) Bangladesh_geodf['coords'] = [coords[0] for coords in Bangladesh_geodf['coords']]

India

India_geodf = gpd.read_file("IND_adm_shp/IND_adm1.shp")

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

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

Myanmar

Myanmar_geodf = gpd.read_file("MMR_adm_shp/MMR_adm1.shp")

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

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

Pakistan

Pakistan_geodf = gpd.read_file("PAK_adm_shp/PAK_adm1.shp")

Pakistan_geodf['coords'] = Pakistan_geodf['geometry'].apply(lambda x: x.representative_point().coords[:]) Pakistan_geodf['coords'] = [coords[0] for coords in Pakistan_geodf['coords']]

Concatenate the geodataframes and see what the result looks like:

frames = [Bangladesh_geodf, India_geodf, Myanmar_geodf, Pakistan_geodf]

result = pd.concat(frames)

result.plot()

Countries shapefiles

I want to make a map for which the country borders are a different color and thickness from the provinces and states borders. Download separate country shapefiles for the four countries and concatenate.

Bangladesh

Bangladesh_country = gpd.read_file("BGD_adm_shp/BGD_adm0.shp")

Bangladesh_country['coords'] = Bangladesh_country['geometry'].apply(lambda x: x.representative_point().coords[:]) Bangladesh_country['coords'] = [coords[0] for coords in Bangladesh_country['coords']]adesh_geodf['coords']]

India

India_country = gpd.read_file("IND_adm_shp/IND_adm0.shp")

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

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

Myanmar

Myanmar_country = gpd.read_file("MMR_adm_shp/MMR_adm0.shp")

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

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

Pakistan

Pakistan_country = gpd.read_file("PAK_adm_shp/PAK_adm0.shp")

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

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

Concatenate the geodataframes and see what the result looks like:

frames_country = [Bangladesh_country, India_country, Myanmar_country, Pakistan_country]

result_country = pd.concat(frames_country)

Read dataframes

Polity score data

Read the dataframe with polity scores for coloring in the countries.

df1 = pd.read_excel("polity.xlsx")

Merge with the state/province shapefile that we just concatenated.

polity_merge = result.merge(df1, on='NAME_0', how='left')

Armed groups data

Read the dataframes for terminated and unterminated groups.

terminated = pd.read_excel("Terminated.xlsx", usecols = [3, 4, 5, 7, 9, 10, 12, 13, 22, 25])

not_terminated = pd.read_excel("Not_terminated.xlsx", usecols = [3, 4, 5, 7, 9, 11, 13, 16, 18, 22, 25])

Mapping

I had mentioned earlier that there are two ways of setting the color ramp in folium. In this first map, I use the old way introduced in earlier posts.

Simple map using predefined color ramp

Import Folium:

import folium

from folium.element import IFrame

from geopy.geocoders import Nominatim

Make a basemap that starts at 24, 84 for latlong and start at zoom level 5. Ask Folium to create a choropleth map on the field "Polity_score", using the Yellow Green color scheme. Here, I made the color ramp steps instead of linear.

m = folium.Map([24, 84], zoom_start=5)

folium.TileLayer('Mapbox Bright').add_to(m)


ft = "Polity_score"

cmap = folium.colormap.linear.YlGn.to_step(9).scale(polity_merge[ft].min(), polity_merge[ft].max())

Define opacity, weight of lines, etc. Caption the legend. I kept the province/state lines black to see how it looks.

folium.GeoJson(polity_merge, style_function=lambda feature: {

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

'fillOpacity' : 0.95,

'weight' : 1,

'color' : 'black' }).add_to(m)


cmap.caption = 'Polity Score Scale'

cmap.add_to(m)

Add the country shapefile, define weight of lines, color, etc. Here, I made the weight slightly heavier to see how it looks.

folium.GeoJson(result_country, style_function=lambda feature: {'weight': 2, 'color':'black'}).add_to(m)

Read again the dataframes for groups.

terminated_groups = terminated

terminated_groups["fullname"] = terminated_groups["fullname"].astype(str)

not_terminated_groups = not_terminated

not_terminated_groups["fullname"] = not_terminated_groups["fullname"].astype(str)

Add the dataframes to the map, with markers in different colors for the two types of groups, and conveying the information necessary in the pop ups.

for ix, row in terminated_groups.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=5000, popup=row['fullname']

+ ", formed in " + str(row['startdate']) + ", terminated in " + str(row['obsyear']) + " with "

+ str(row['terminateform']) + ". Armed order was " + str(row['armedorder'])

+ ". Group goal was " + str(row['aggoals']) + ". Political participation status was "

+ str(row['polpar']) + ".", color='#FF0000', fill_color='#FF0000').add_to(m)


for ix, row in not_terminated_groups.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=5000, popup=row['fullname']

+ ", formation year " + str(row['startdate']) + ". " + str(row['terminate']) + ". There is "

+ str(row['ceasefireongoing']) + " ongoing ceasefire, " + "and " + str(row['peacedealongoing'])

+ " ongoing peacedeal" + ". Armed order is " + str(row['armedorder']) + ". Group goal is "

+ str(row['aggoals']) + ". Political participation status is " + str(row['polpar'])

+ ".", color='#00FF00', fill_color='#00FF00').add_to(m)

Display and save the map

m.save("HeatMap.html")

m

The next section introduces how to define your own color ramp.

Defining your own color ramp

Define a stepped color scheme. Here I used hex color codes. You can also directly use color names. Since the polity score variable ranges from 0 to 9, and there are no further levels between 0 and 7, I ask the stepped color scheme to make everything between 0 to 6 one color. 7, 8, 9 are their individual colors.

step = cm.StepColormap(['#AED6F1', '#3498DB', '#2874A6', '#1B4F72'], vmin=0., vmax=9., index=[0,6,7,8,9], caption='Polity Score')

Make a folium base map that starts at 24, 84 for latlong and zoom level 5. Use the stepped scheme we defined earlier on polity score. Different from above, this time, I made the province/state line color white, which seems to look better. Add the stepped color scheme as a legend to the map.

m = folium.Map([24, 84], tiles='cartodbpositron', zoom_start=5)

ft='Polity_score'


folium.GeoJson(polity_merge, style_function=lambda feature: {

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

'fillOpacity' : 0.95,

'weight' : 1,

'color' : 'white'

}).add_to(m) step.add_to(m)

Add the country shapefile to the map. As before, increase the weight of the lines and make the color black.

folium.GeoJson(result_country, style_function=lambda feature: {'weight': 3, 'color':'black'}).add_to(m)

Read the dataframes for groups:

terminated_groups = terminated

terminated_groups["fullname"] = terminated_groups["fullname"].astype(str)

not_terminated_groups = not_terminated

not_terminated_groups["fullname"] = not_terminated_groups["fullname"].astype(str)

Add the dataframes to the map, with markers in different colors for the two types of groups, and conveying the information necessary in the pop ups.

for ix, row in terminated_groups.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=5000, popup=row['fullname']

+ ", formed in " + str(row['startdate']) + ", terminated in " + str(row['obsyear']) + " with "

+ str(row['terminateform']) + ". Armed order was " + str(row['armedorder']) + ". Group goal was "

+ str(row['aggoals']) + ". Political participation status was " + str(row['polpar'])

+ ".", color='#FF0000', fill_color='#FF0000').add_to(m)


for ix, row in not_terminated_groups.iterrows():

folium.CircleMarker(location = [row['Latitude'],row['Longitude']], radius=5000, popup=row['fullname']

+ ", formation year " + str(row['startdate']) + ". " + str(row['terminate']) + ". There is "

+ str(row['ceasefireongoing']) + " ongoing ceasefire, " + "and " + str(row['peacedealongoing'])

+ " ongoing peacedeal" + ". Armed order is " + str(row['armedorder']) + ". Group goal is "

+ str(row['aggoals']) + ". Political participation status is " + str(row['polpar'])

+ ".", color='#00FF00', fill_color='#00FF00').add_to(m)

Display and save map:

m.save("HeatMap2.html")

m

Note how the color scale for Polity Score is now different. This color scale also matches another map I made using ArcGIS: