{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# GeoJSON and choropleth\n", "\n", "**A few examples of how to do that with `folium`.**\n", "\n", "\n", "## Using `GeoJson`\n", "\n", "### Loading data\n", "\n", "Let us load a GeoJSON file representing the US states." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "import requests\n", "\n", "url = (\n", " \"https://raw.githubusercontent.com/python-visualization/folium/main/examples/data\"\n", ")\n", "us_states = f\"{url}/us-states.json\"\n", "\n", "geo_json_data = json.loads(requests.get(us_states).text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is a classical GeoJSON `FeatureCollection` (see https://en.wikipedia.org/wiki/GeoJSON) of the form :\n", "\n", " {\n", " \"type\": \"FeatureCollection\",\n", " \"features\": [\n", " {\n", " \"properties\": {\"name\": \"Alabama\"},\n", " \"id\": \"AL\",\n", " \"type\": \"Feature\",\n", " \"geometry\": {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [[[-87.359296, 35.00118], ...]]\n", " }\n", " },\n", " {\n", " \"properties\": {\"name\": \"Alaska\"},\n", " \"id\": \"AK\",\n", " \"type\": \"Feature\",\n", " \"geometry\": {\n", " \"type\": \"MultiPolygon\",\n", " \"coordinates\": [[[[-131.602021, 55.117982], ... ]]]\n", " }\n", " },\n", " ...\n", " ]\n", " }\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A first way of drawing it on a map, is simply to use `folium.GeoJson` :" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import folium\n", "\n", "\n", "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(geo_json_data).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that you can avoid loading the file on yourself ; in simply providing a file path." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(us_states).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can pass a geopandas object." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import geopandas\n", "\n", "gdf = geopandas.read_file(us_states)\n", "\n", "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(\n", " gdf,\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Click on zoom\n", "\n", "You can enable an option that if you click on a part of the geometry the map will zoom in to that.\n", "\n", "Try it on the map below:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(geo_json_data, zoom_on_click=True).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Styling\n", "\n", "Now this is cool and simple, but we may be willing to choose the style of the data.\n", "\n", "You can provide a function of the form `lambda feature: {}` that sets the style of each feature.\n", "\n", "For possible options, see:\n", "\n", "* For `Point` and `MultiPoint`, see http://leafletjs.com/reference.html#marker\n", "* For other features, see http://leafletjs.com/reference.html#path-options and http://leafletjs.com/reference.html#polyline-options\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(\n", " geo_json_data,\n", " style_function=lambda feature: {\n", " \"fillColor\": \"#ffff00\",\n", " \"color\": \"black\",\n", " \"weight\": 2,\n", " \"dashArray\": \"5, 5\",\n", " },\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(\n", " geo_json_data,\n", " style_function=lambda feature: {\n", " \"fillColor\": \"green\"\n", " if \"e\" in feature[\"properties\"][\"name\"].lower()\n", " else \"#ffff00\",\n", " \"color\": \"black\",\n", " \"weight\": 2,\n", " \"dashArray\": \"5, 5\",\n", " },\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wow, this looks almost like a choropleth. To do one, we just need to compute a color for each state.\n", "\n", "Let's imagine we want to draw a choropleth of unemployment in the US.\n", "\n", "First, we may load the data:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StateUnemployment
0AL7.1
1AK6.8
2AZ8.1
3AR7.2
4CA10.1
\n", "
" ], "text/plain": [ " State Unemployment\n", "0 AL 7.1\n", "1 AK 6.8\n", "2 AZ 8.1\n", "3 AR 7.2\n", "4 CA 10.1" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "US_Unemployment_Oct2012 = f\"{url}/US_Unemployment_Oct2012.csv\"\n", "unemployment = pd.read_csv(US_Unemployment_Oct2012)\n", "\n", "unemployment.head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we need to create a function that maps one value to a RGB color (of the form `#RRGGBB`).\n", "For this, we'll use colormap tools from `folium.colormap`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "#d8f0a3ff\n" ] }, { "data": { "text/html": [ "3.210.3" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from branca.colormap import linear\n", "\n", "colormap = linear.YlGn_09.scale(\n", " unemployment.Unemployment.min(), unemployment.Unemployment.max()\n", ")\n", "\n", "print(colormap(5.0))\n", "\n", "colormap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need also to convert the table into a dictionary, in order to map a feature to it's unemployment value." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7.1" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unemployment_dict = unemployment.set_index(\"State\")[\"Unemployment\"]\n", "\n", "unemployment_dict[\"AL\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can do the choropleth." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(\n", " geo_json_data,\n", " name=\"unemployment\",\n", " style_function=lambda feature: {\n", " \"fillColor\": colormap(unemployment_dict[feature[\"id\"]]),\n", " \"color\": \"black\",\n", " \"weight\": 1,\n", " \"dashArray\": \"5, 5\",\n", " \"fillOpacity\": 0.9,\n", " },\n", ").add_to(m)\n", "\n", "folium.LayerControl().add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, if you can create and/or use a dictionary providing directly the good color. Thus, the finishing seems faster:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "color_dict = {key: colormap(unemployment_dict[key]) for key in unemployment_dict.keys()}" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.GeoJson(\n", " geo_json_data,\n", " style_function=lambda feature: {\n", " \"fillColor\": color_dict[feature[\"id\"]],\n", " \"color\": \"black\",\n", " \"weight\": 1,\n", " \"dashArray\": \"5, 5\",\n", " \"fillOpacity\": 0.9,\n", " },\n", ").add_to(m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that adding a color legend may be a good idea." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colormap.caption = \"Unemployment color scale\"\n", "colormap.add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using `Choropleth` class\n", "\n", "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.\n", "\n", "Just like the `GeoJson` class you can provide it a filename, a dict, or a geopandas object." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.Choropleth(\n", " geo_data=us_states,\n", " fill_opacity=0.3,\n", " line_weight=2,\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, in playing with keyword arguments, you can get a choropleth in a few lines:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.Choropleth(\n", " geo_data=us_states,\n", " data=unemployment,\n", " columns=[\"State\", \"Unemployment\"],\n", " key_on=\"feature.id\",\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "folium.Choropleth(\n", " geo_data=us_states,\n", " data=unemployment,\n", " columns=[\"State\", \"Unemployment\"],\n", " key_on=\"feature.id\",\n", " fill_color=\"YlGn\",\n", " bins=[3, 4, 9, 11],\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also enable the highlight function, to enable highlight functionality when you hover over each area." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state_data = pd.read_csv(US_Unemployment_Oct2012)\n", "\n", "m = folium.Map(location=[48, -102], zoom_start=3)\n", "folium.Choropleth(\n", " geo_data=us_states,\n", " data=state_data,\n", " columns=[\"State\", \"Unemployment\"],\n", " key_on=\"feature.id\",\n", " fill_color=\"YlGn\",\n", " fill_opacity=0.7,\n", " line_opacity=0.2,\n", " legend_name=\"Unemployment Rate (%)\",\n", " highlight=True,\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "messed_up_data = unemployment.drop(0)\n", "messed_up_data.loc[4, \"Unemployment\"] = float(\"nan\")\n", "\n", "folium.Choropleth(\n", " geo_data=us_states,\n", " data=messed_up_data,\n", " columns=[\"State\", \"Unemployment\"],\n", " nan_fill_color=\"purple\",\n", " nan_fill_opacity=0.4,\n", " key_on=\"feature.id\",\n", " fill_color=\"YlGn\",\n", ").add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n" ] }, { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43, -100], zoom_start=4)\n", "\n", "choropleth = folium.Choropleth(\n", " geo_data=us_states,\n", " data=state_data,\n", " columns=[\"State\", \"Unemployment\"],\n", " key_on=\"feature.id\",\n", " fill_color=\"YlGn\",\n", " name=\"Unenployment\",\n", " show=False,\n", ").add_to(m)\n", "\n", "# The underlying GeoJson and StepColormap objects are reachable\n", "print(type(choropleth.geojson))\n", "print(type(choropleth.color_scale))\n", "\n", "folium.LayerControl(collapsed=False).add_to(m)\n", "\n", "m" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }