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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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:

[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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
3.24.45.66.87.99.110.3

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]:
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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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 0x7ff571fcaa20>

Note that adding a color legend may be a good idea.

[15]:
colormap.caption = "Unemployment color scale"
colormap.add_to(m)

m
[15]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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” your style_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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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]:
Make this Notebook Trusted to load map: File -> Trust Notebook