{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# GeoJSON and choropleth\n", "\n", "**A few examples of how to do that with `folium`.**" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "../folium/__init__.py\n", "0.2.0.dev\n" ] } ], "source": [ "import pandas as pd\n", "import json\n", "import sys\n", "sys.path.append('..')\n", "import folium\n", "print (folium.__file__)\n", "print (folium.__version__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using `GeoJson`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us load a GeoJSON file reprenseting the US states." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "geo_json_data = json.load(open('us-states.json'))" ] }, { "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": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "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 object." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "folium.GeoJson(open('us-states.json')).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you provide only the filename, Folium will consider you don't want to embed the data in the map. You'll need to copy the file on the server if you want it to be rendered." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "folium.GeoJson('us-states.json').add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "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": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "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' if 'e' in feature['properties']['name'].lower() 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": { "collapsed": false }, "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", "
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": [ "unemployment = pd.read_csv('./US_Unemployment_Oct2012.csv')\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": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "#c2e798\n" ] }, { "data": { "text/html": [ "3.210.3" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from folium.colormap import linear\n", "\n", "colormap = linear.YlGn.scale(\n", " unemployment.Unemployment.min(),\n", " unemployment.Unemployment.max())\n", "\n", "print(colormap(5.))\n", "\n", "colormap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need also to convert the table into a dictionnary, in order to map a feature to it's unemployment value." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "7.0999999999999996" ] }, "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": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 12, "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': colormap(unemployment_dict[feature['id']]),\n", " 'color' : 'black',\n", " 'weight' : 1,\n", " 'dashArray' : '5, 5',\n", " 'fillOpacity' : .9,\n", " }\n", " ).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, if you can create and/or use a dictionnary providing directly the good color. Thus, the finishing seems faster:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "color_dict = {key: colormap(unemployment_dict[key]) for key in unemployment_dict.keys()}" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 14, "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' : .9,\n", " }\n", " ).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that adding a color legend may be a good idea." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colormap.caption = 'Unemployment color scale'\n", "colormap.add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### GeoPandas\n", "\n", "Note that you can also create a GeoJson object in using a GeoPandas DataFrame. There's another notebook example for this. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using `choropleth` method\n", "\n", "Now if you want to get faster, you can use the `Map.choropleth` method. Have a look at it's docstring, it has several styling options.\n", "\n", "You can use it in providing a file name (`geo_path`) :" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "m.choropleth(\n", " geo_path='us-states.json',\n", " fill_color='red',\n", " fill_opacity=0.3,\n", " line_weight=2,\n", " )\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or in providing a GeoJSON string (`geo_str`) :" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "m.choropleth(geo_str=open('us-states.json').read())\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, in playing with keyword arguments, you can get a choropleth in (*almost*) one line :" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/journois/anaconda3/lib/python3.4/site-packages/ipykernel/__main__.py:8: FutureWarning: 'threshold_scale' default behavior has changed. Now you get a linear scale between the 'min' and the 'max' of your data. To get former behavior, use folium.utilities.split_six.\n" ] }, { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "m.choropleth(\n", " geo_str=open('us-states.json').read(),\n", " data=unemployment,\n", " columns=['State', 'Unemployment'],\n", " key_on='feature.id',\n", " fill_color='YlGn',\n", " )\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A cool thing: you can force the color scale to a given number of bins, in providing a `threshold_scale` argument." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([43,-100], zoom_start=4)\n", "\n", "m.choropleth(\n", " geo_str=open('us-states.json').read(),\n", " data=unemployment,\n", " columns=['State', 'Unemployment'],\n", " key_on='feature.id',\n", " fill_color='YlGn',\n", " threshold_scale = [3,4,9,10]\n", " )\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's all folks !\n", "\n", "Hope it'll be useful to you. Don't hesitate to provide a feedback on what can be improved, which method do you prefer, etc." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.4.4" } }, "nbformat": 4, "nbformat_minor": 0 }