import os
import folium
print(folium.__version__)
0.6.0+159.g84a27d0.dirty
import json
us_states = os.path.join('data', 'us-states.json')
geo_json_data = json.load(open(us_states))
It is a classical GeoJSON FeatureCollection
(see https://en.wikipedia.org/wiki/GeoJSON) of the form :
{
"type": "FeatureCollection",
"features": [
{
"properties": {"name": "Alabama"},
"id": "AL",
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[-87.359296, 35.00118], ...]]
}
},
{
"properties": {"name": "Alaska"},
"id": "AK",
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-131.602021, 55.117982], ... ]]]
}
},
...
]
}
A first way of drawing it on a map, is simply to use folium.GeoJson
:
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(geo_json_data).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_0.html'))
m
Note that you can avoid loading the file on yourself ; in simply providing a file path.
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(us_states).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_1.html'))
m
You can pass a geopandas object.
import geopandas
gdf = geopandas.read_file(us_states)
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
gdf,
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_3.html'))
m
Now this is cool and simple, but we may be willing to choose the style of the data.
You can provide a function of the form lambda feature: {}
that sets the style of each feature.
For possible options, see:
Point
and MultiPoint
, see http://leafletjs.com/reference.html#markerm = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
geo_json_data,
style_function=lambda feature: {
'fillColor': '#ffff00',
'color': 'black',
'weight': 2,
'dashArray': '5, 5'
}
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_3.html'))
m
What's cool in providing a function, is that you can specify a style depending on the feature. For example, if you want to visualize in green all states whose name contains the letter 'E', just do:
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
geo_json_data,
style_function=lambda feature: {
'fillColor': 'green' if 'e' in feature['properties']['name'].lower() else '#ffff00',
'color': 'black',
'weight': 2,
'dashArray': '5, 5'
}
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_4.html'))
m
Wow, this looks almost like a choropleth. To do one, we just need to compute a color for each state.
Let's imagine we want to draw a choropleth of unemployment in the US.
First, we may load the data:
import pandas as pd
US_Unemployment_Oct2012 = os.path.join('data', 'US_Unemployment_Oct2012.csv')
unemployment = pd.read_csv(US_Unemployment_Oct2012)
unemployment.head(5)
State | Unemployment | |
---|---|---|
0 | AL | 7.1 |
1 | AK | 6.8 |
2 | AZ | 8.1 |
3 | AR | 7.2 |
4 | CA | 10.1 |
Now we need to create a function that maps one value to a RGB color (of the form #RRGGBB
).
For this, we'll use colormap tools from folium.colormap
.
from branca.colormap import linear
colormap = linear.YlGn_09.scale(
unemployment.Unemployment.min(),
unemployment.Unemployment.max())
print(colormap(5.0))
colormap
#d8f0a3
We need also to convert the table into a dictionnary, in order to map a feature to it's unemployment value.
unemployment_dict = unemployment.set_index('State')['Unemployment']
unemployment_dict['AL']
7.0999999999999996
Now we can do the choropleth.
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
geo_json_data,
name='unemployment',
style_function=lambda feature: {
'fillColor': colormap(unemployment_dict[feature['id']]),
'color': 'black',
'weight': 1,
'dashArray': '5, 5',
'fillOpacity': 0.9,
}
).add_to(m)
folium.LayerControl().add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_5.html'))
m
Of course, if you can create and/or use a dictionnary providing directly the good color. Thus, the finishing seems faster:
color_dict = {key: colormap(unemployment_dict[key]) for key in unemployment_dict.keys()}
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
geo_json_data,
style_function=lambda feature: {
'fillColor': color_dict[feature['id']],
'color': 'black',
'weight': 1,
'dashArray': '5, 5',
'fillOpacity': 0.9,
}
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_6.html'))
m
Note that adding a color legend may be a good idea.
colormap.caption = 'Unemployment color scale'
colormap.add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_7.html'))
m
Choropleth
class¶Now if you want to get faster, you can use the Choropleth
class. Have a look at it's docstring, it has several styling options.
You can use it in providing a file name (geo_path
) :
m = folium.Map([43, -100], zoom_start=4)
folium.Choropleth(
geo_data=geopandas.read_file(us_states),
fill_color='red',
fill_opacity=0.3,
line_weight=2,
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_7.html'))
m
Or in providing a GeoJSON string (geo_str
) :
m = folium.Map([43, -100], zoom_start=4)
folium.Choropleth(geo_data=us_states).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_8.html'))
m
Then, in playing with keyword arguments, you can get a choropleth in a few lines:
m = folium.Map([43, -100], zoom_start=4)
folium.Choropleth(
geo_data=us_states,
data=unemployment,
columns=['State', 'Unemployment'],
key_on='feature.id',
fill_color='YlGn',
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_9.html'))
m
You can force the color scale to a given number of bins (or directly list the bins you would like), by providing the bins
argument.
m = folium.Map([43, -100], zoom_start=4)
folium.Choropleth(
geo_data=us_states,
data=unemployment,
columns=['State', 'Unemployment'],
key_on='feature.id',
fill_color='YlGn',
bins=[3, 4, 9, 11]
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_10.html'))
m
You can also enable the highlight function, to enable highlight functionality when you hover over each area.
state_data = pd.read_csv(US_Unemployment_Oct2012)
m = folium.Map(location=[48, -102], zoom_start=3)
folium.Choropleth(
geo_data=us_states,
data=state_data,
columns=['State', 'Unemployment'],
key_on='feature.id',
fill_color='YlGn',
fill_opacity=0.7,
line_opacity=0.2,
legend_name='Unemployment Rate (%)',
highlight=True
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_11.html'))
m
You can customize the way missing and nan
values are displayed on your map using the two parameters nan_fill_color
and nan_fill_opacity
.
m = folium.Map([43, -100], zoom_start=4)
messed_up_data = unemployment.drop(0)
messed_up_data.loc[4, 'Unemployment'] = float('nan')
folium.Choropleth(
geo_data=us_states,
data=messed_up_data,
columns=['State', 'Unemployment'],
nan_fill_color='purple',
nan_fill_opacity=0.4,
key_on='feature.id',
fill_color='YlGn'
).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_10.html'))
m
Internally Choropleth uses the GeoJson
or TopoJson
class, depending on your settings, and the StepColormap
class. Both objects are attributes of your Choropleth
object called geojson
and color_scale
. You can make changes to them, but for regular things you won't have to. For example setting a name for in the layer controls or disabling showing the layer on opening the map is possible in Choropleth
itself.
m = folium.Map([43, -100], zoom_start=4)
choropleth = folium.Choropleth(
geo_data=us_states,
data=state_data,
columns=['State', 'Unemployment'],
key_on='feature.id',
fill_color='YlGn',
name='Unenployment',
show=False,
).add_to(m)
# The underlying GeoJson and StepColormap objects are reachable
print(type(choropleth.geojson))
print(type(choropleth.color_scale))
folium.LayerControl(collapsed=False).add_to(m)
m.save(os.path.join('results', 'GeoJSON_and_choropleth_11.html'))
m
<class 'folium.features.GeoJson'> <class 'branca.colormap.StepColormap'>