```{code-cell} ipython3 --- nbsphinx: hidden --- import folium ``` ## Using `GeoJson` ### Loading data Let us load a GeoJSON file representing the US states. ```{code-cell} ipython3 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` : ```{code-cell} ipython3 m = folium.Map([43, -100], zoom_start=4) folium.GeoJson(geo_json_data).add_to(m) m ``` Note that you can avoid loading the file on yourself, by providing a (local) file path or a url. ```{code-cell} ipython3 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 ``` You can pass a geopandas object. ```{code-cell} ipython3 import geopandas gdf = geopandas.read_file(url) m = folium.Map([43, -100], zoom_start=4) folium.GeoJson( gdf, ).add_to(m) m ``` ### 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: ```{code-cell} ipython3 m = folium.Map([43, -100], zoom_start=4) folium.GeoJson(geo_json_data, zoom_on_click=True).add_to(m) m ``` ### 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` and `MultiPoint`, see https://leafletjs.com/reference.html#marker * For other features, see https://leafletjs.com/reference.html#path and https://leafletjs.com/reference.html#polyline ```{code-cell} ipython3 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 ``` 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: ```{code-cell} ipython3 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 ``` 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: ```{code-cell} ipython3 import pandas unemployment = pandas.read_csv( "https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_unemployment_oct_2012.csv" ) unemployment.head(5) ``` 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`. ```{code-cell} ipython3 from branca.colormap import linear colormap = linear.YlGn_09.scale( unemployment.Unemployment.min(), unemployment.Unemployment.max() ) print(colormap(5.0)) colormap ``` We need also to convert the table into a dictionary, in order to map a feature to it's unemployment value. ```{code-cell} ipython3 unemployment_dict = unemployment.set_index("State")["Unemployment"] unemployment_dict["AL"] ``` Now we can do the choropleth. ```{code-cell} ipython3 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 ``` Of course, if you can create and/or use a dictionary providing directly the good color. Thus, the finishing seems faster: ```{code-cell} ipython3 color_dict = {key: colormap(unemployment_dict[key]) for key in unemployment_dict.keys()} ``` ```{code-cell} ipython3 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) ``` Note that adding a color legend may be a good idea. ```{code-cell} ipython3 colormap.caption = "Unemployment color scale" colormap.add_to(m) m ``` > > **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: > ```python > 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. ```{code-cell} ipython3 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 ``` #### 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. ```{code-cell} ipython3 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 ```