Using GeoJson
#
Loading data#
Let us load a GeoJSON file representing the US states.
[2]:
import requests
geo_json_data = requests.get(
"https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_states.json"
).json()
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
:
[3]:
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(geo_json_data).add_to(m)
m
[3]:
Note that you can avoid loading the file on yourself, by providing a (local) file path or a url.
[4]:
m = folium.Map([43, -100], zoom_start=4)
url = "https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_states.json"
folium.GeoJson(url).add_to(m)
m
[4]:
You can pass a geopandas object.
[5]:
import geopandas
gdf = geopandas.read_file(url)
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
gdf,
).add_to(m)
m
[5]:
Click on zoom#
You can enable an option that if you click on a part of the geometry the map will zoom in to that.
Try it on the map below:
[6]:
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(geo_json_data, zoom_on_click=True).add_to(m)
m
[6]:
Styling#
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:
For
Point
andMultiPoint
, see https://leafletjs.com/reference.html#markerFor other features, see https://leafletjs.com/reference.html#path and https://leafletjs.com/reference.html#polyline
[7]:
m = 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
[7]:
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:
[8]:
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
[8]:
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:
[9]:
import pandas
unemployment = pandas.read_csv(
"https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_unemployment_oct_2012.csv"
)
unemployment.head(5)
[9]:
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
.
[10]:
from branca.colormap import linear
colormap = linear.YlGn_09.scale(
unemployment.Unemployment.min(), unemployment.Unemployment.max()
)
print(colormap(5.0))
colormap
#d8f0a3ff
[10]:
We need also to convert the table into a dictionary, in order to map a feature to it’s unemployment value.
[11]:
unemployment_dict = unemployment.set_index("State")["Unemployment"]
unemployment_dict["AL"]
[11]:
np.float64(7.1)
Now we can do the choropleth.
[12]:
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
[12]:
Of course, if you can create and/or use a dictionary providing directly the good color. Thus, the finishing seems faster:
[13]:
color_dict = {key: colormap(unemployment_dict[key]) for key in unemployment_dict.keys()}
[14]:
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)
[14]:
<folium.features.GeoJson at 0x108c57200>
Note that adding a color legend may be a good idea.
[15]:
colormap.caption = "Unemployment color scale"
colormap.add_to(m)
m
[15]:
Caveat
When using
style_function
in a loop you may encounter Python’s ‘Late Binding Closure’ gotcha! See https://docs.python-guide.org/writing/gotchas/#late-binding-closures for more info. There are a few ways around it from using a GeoPandas object instead, to “hacking” yourstyle_function
to force early closure, like:for geom, my_style in zip(geoms, my_styles): style = my_style style_function = lambda x, style=style: style folium.GeoJson( data=geom, style_function=style_function, ).add_to(m)
Highlight function#
The GeoJson
class provides a highlight_function
argument, which works similarly to style_function
, but applies on mouse events. In the following example the fill color will change when you hover your mouse over a feature.
[16]:
m = folium.Map([43, -100], zoom_start=4)
folium.GeoJson(
geo_json_data,
highlight_function=lambda feature: {
"fillColor": (
"green" if "e" in feature["properties"]["name"].lower() else "#ffff00"
),
},
).add_to(m)
m
[16]:
Keep highlighted while popup is open#
The GeoJson
class provides a popup_keep_highlighted
boolean argument. Whenever a GeoJson layer is associated with a popup and a highlight function is defined, this argument allows you to decide if the highlighting should remain active while the popup is open.
[17]:
m = folium.Map([43, -100], zoom_start=4)
popup = folium.GeoJsonPopup(fields=["name"])
folium.GeoJson(
geo_json_data,
highlight_function=lambda feature: {
"fillColor": (
"green" if "e" in feature["properties"]["name"].lower() else "#ffff00"
),
},
popup=popup,
popup_keep_highlighted=True,
).add_to(m)
m
[17]: