Python Webmapping with Folium¶

For this exercise we will use the following libraries:¶

>>> import pandas as pd
>>> import geopandas as gpd
>>> import folium
>>> import branca
>>> import os

Pandas/GeoPandas for reading geospatial data, Folium to generate the web map, Branca to create a colormap, and OS for system navigation as needed.

In [ ]:
import pandas as pd
import geopandas as gpd
import folium
import branca
import os

Now, create a map as m with folium.Map():¶

>>> m = folium.Map()
>>> m
In [2]:
m = folium.Map()
m
Out[2]:
Make this Notebook Trusted to load map: File -> Trust Notebook

There's your basic map. Now let's set the starting location to the Boulder area and change the basemap tiles:¶

>>> m = folium.Map(location = [40.1, -105.4], tiles = 'Stamen Terrain', zoom_start = 10)
>>> m
In [3]:
m = folium.Map(location = [40.1, -105.5], tiles = 'CartoDB Positron', zoom_start= 10)
m
Out[3]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Now let's add a point to the map:¶

>>> m = folium.Map(location = [40.1, -105.4], tiles = 'Stamen Terrain', zoom_start = 10)

>>> folium.Marker([40.014984, -105.270546]).add_to(m)

>>> m
In [5]:
m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start=10)

folium.Marker([40.014984, -105.270547]).add_to(m)

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

There are multiple methods for adding and styling simple points on maps:¶

Try:

>>> folium.Marker([40.014984, -105.270546], icon=folium.Icon(color='red')).add_to(m)

Or:

>>> folium.vector_layers.CircleMarker(  
                            location = [40.014984, -105.270546],   
                            radius=10 ,  
                            weight=3,  
                            color='purple',  
                            fill_color='red',  
                            fill_opacity=1).add_to(m)
In [6]:
m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start=10)

folium.vector_layers.CircleMarker(location = [40.014984, -105.270546], radius=10, weight=3, color='purple', fill_color='red', fill_opacity=1).add_to(m)

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

Now, let's make a choropleth map.¶

First, we will take a look at the Boulder County Census Tracts geojson layer.¶
>>> m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start = 10)
>>> folium.GeoJson('Boulder_Co_Tracts.geojson').add_to(m)
>>> m
In [7]:
m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start=10)
folium.GeoJson('Boulder_Co_Tracts.geojson').add_to(m)
m
Out[7]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Next, use Pandas to import the Boulder County Median Household Income csv file:¶
>>> MHHI = pd.read_csv('Boulder_Co_MHHI.csv', dtype={'GEOID10':str})
>>> MHHI
In [8]:
MHHI = pd.read_csv('Boulder_Co_MHHI.csv', dtype={'GEOID':str})
MHHI
Out[8]:
GEOID10 Tract State State_FIPS Count_FIPS Tract_FIPS FIPS Area_Land Area_Water MHHI2014
0 8013012101 Census Tract 121.01, Boulder County, Colorado co 8 13 12101 14000US08013012101 2859895 0 101328
1 8013012102 Census Tract 121.02, Boulder County, Colorado co 8 13 12102 14000US08013012102 2882035 0 63333
2 8013012103 Census Tract 121.03, Boulder County, Colorado co 8 13 12103 14000US08013012103 2305549 2740 87708
3 8013012104 Census Tract 121.04, Boulder County, Colorado co 8 13 12104 14000US08013012104 2700890 96592 87875
4 8013012105 Census Tract 121.05, Boulder County, Colorado co 8 13 12105 14000US08013012105 2475536 470 66885
... ... ... ... ... ... ... ... ... ... ...
63 8013060700 Census Tract 607, Boulder County, Colorado co 8 13 60700 14000US08013060700 10020054 82697 107273
64 8013060800 Census Tract 608, Boulder County, Colorado co 8 13 60800 14000US08013060800 21250339 150276 44246
65 8013060900 Census Tract 609, Boulder County, Colorado co 8 13 60900 14000US08013060900 7699944 28819 74954
66 8013061300 Census Tract 613, Boulder County, Colorado co 8 13 61300 14000US08013061300 2106503 6895 151743
67 8013061400 Census Tract 614, Boulder County, Colorado co 8 13 61400 14000US08013061400 2553846 8890 135644

68 rows × 10 columns

Now, we will use folium.Choropleth to join the Median Household Income tracts dataframe to the Boulder County Census Tracts geojson layer on the fly:¶
>>> m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start = 10)

>>> folium.Choropleth(geo_data = 'Boulder_Co_Tracts.geojson',
                    data = MHHI,
                    columns = ['GEOID10', 'MHHI2014'],
                    key_on = 'feature.properties.GEOID10').add_to(m)

>>> m
In [9]:
m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start = 10)

folium.Choropleth(geo_data = 'Boulder_Co_Tracts.geojson', data=MHHI, columns = ['GEOID10', 'MHHI2014'], key_on = 'feature.properties.GEOID10').add_to(m)

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

Next, we'll modify the color palette¶

>>> m = folium.Map(location = [40.1, -105.4], tiles = 'Stamen Terrain', zoom_start = 10)

>>> folium.Choropleth(geo_data = 'Boulder_Co_Tracts.geojson',
                    data = MHHI,
                    columns = ['GEOID10', 'MHHI2014'],
                    key_on = 'feature.properties.GEOID10',
                    fill_color = 'RdYlGn').add_to(m)

>>> m

Note: fill_color accepts Color Brewer palettes

In [10]:
m = folium.Map(location = [40.1, -105.4], tiles = 'CartoDB Positron', zoom_start = 10)

folium.Choropleth(geo_data = 'Boulder_Co_Tracts.geojson',
                data = MHHI,
                columns = ['GEOID10', 'MHHI2014'],
                key_on = 'feature.properties.GEOID10',
                fill_color = 'RdYlGn').add_to(m).add_to(m)

m
Out[10]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Export your map:¶
>>> m.save('my_map.html')
In [ ]:
m.save('my_map.html')
  • folium.choropleth is a quick & easy method of making a choropleth by doing an on the fly table join.¶
  • If you want more funcitonality or have a geojson layer with the data already baked in, you will need to do a bit more work...¶

Advanced Choropleth¶

Now we will make a choropleth map of 2020 presidential election results in Colorado.¶

Start by importing the Colorado_Vote.geojson layer using GeoPandas:

>>> CO_Vote = gpd.read_file('Colorado_Vote.geojson', driver='GeoJSON')

Note: This data is from the New York Times.

In [11]:
CO_Vote = gpd.read_file('Colorado_Vote.geojson', driver='GeoJSON')
View some details about the CO_Vote geodataframe:¶
>>> CO_Vote

Note: In this data, pct_dem_lead represents an index the victory percentage of either party. Negative values represent the percentage by which Republicans won and positive values represent the percent by which Democrats won.

In [12]:
CO_Vote
Out[12]:
GEOID votes_dem votes_rep votes_total votes_per_sqkm pct_dem_lead geometry
0 08001-187 430.0 464.0 920.0 1146.5 -3.7 POLYGON ((-104.79932 39.98680, -104.79683 39.9...
1 08041-744 667.0 446.0 1140.0 772.9 19.4 POLYGON ((-104.88658 38.85703, -104.88666 38.8...
2 08041-745 116.0 137.0 261.0 23.8 -8.0 POLYGON ((-104.89584 38.83894, -104.88676 38.8...
3 08059-060 252.0 294.0 560.0 1.1 -7.5 POLYGON ((-105.17096 39.40783, -105.17074 39.4...
4 08059-061 703.0 504.0 1243.0 39.8 16.0 POLYGON ((-105.39899 39.57012, -105.39904 39.5...
... ... ... ... ... ... ... ...
3196 08001-188 305.0 422.0 740.0 1164.1 -15.8 POLYGON ((-104.79912 39.97759, -104.79966 39.9...
3197 08041-740 863.0 938.0 1848.0 9.1 -4.1 POLYGON ((-104.86897 38.76925, -104.87056 38.7...
3198 08001-189 290.0 421.0 731.0 1540.8 -17.9 POLYGON ((-104.80752 39.97593, -104.80703 39.9...
3199 08041-741 193.0 292.0 493.0 10.2 -20.1 POLYGON ((-104.93542 38.92851, -104.93494 38.9...
3200 08001-186 512.0 612.0 1153.0 1130.8 -8.7 POLYGON ((-104.78230 39.98638, -104.78250 39.9...

3201 rows × 7 columns

Initialize a map including this GeoJson layer:¶
>>> m = folium.Map(location = [39, -105.7], tiles = 'CartoDB Positron', zoom_start=7)

>>> folium.GeoJson(CO_Vote).add_to(m)

>>> m
In [13]:
m = folium.Map(location = [39, -105.7], tiles = 'CartoDB Positron', zoom_start=7)
    
folium.GeoJson(CO_Vote).add_to(m)
    
m
Out[13]:
Make this Notebook Trusted to load map: File -> Trust Notebook
We will create a colormap based on the 'pct_dem_lead' field in the geojson:¶
>>> colormap = branca.colormap.LinearColormap(
        colors=["#c93135", "#eaa9a9", "#92bde0", "#1375b7"],
        index=CO_Vote['pct_dem_lead'].quantile([0, .25, .5, .75]),
        vmin=-100,
        vmax=100)

>>> colormap
In [14]:
colormap = branca.colormap.LinearColormap(
    colors=["#c93135", "#eaa9a9", "#92bde0", "#1375b7"],
    index=CO_Vote['pct_dem_lead'].quantile([0, .25, .5, .75]),
    vmin=-100,
    vmax=100)
        
colormap
Out[14]:
-100-66.7-33.30.033.366.7100
Next we will create a style function:¶
>>> def style(x):
    return {
        "fillColor": colormap(x["properties"]["pct_dem_lead"]),
        "color": "black",
        "weight": 0.25,
        "fillOpacity": .6}
In [15]:
def style(x):
    return {
        "fillColor": colormap(x["properties"]["pct_dem_lead"]),
        "color": "black",
        "weight": 0.25,
        "fillOpacity": .6}
Now we will style our voter disctrict polygons by the 'pct_dem_lead' field using our custom colormap:¶
>>> m = folium.Map(location=[40.1, -105.7], tiles='CartoDB Positron', zoom_start=7)

>>> folium.GeoJson(CO_Vote,style_function=style).add_to(m)

>>> m
In [16]:
m = folium.Map(location = [39, -105.7], tiles = 'CartoDB Positron', zoom_start=7)

folium.GeoJson(CO_Vote, style_function=style).add_to(m)

m
Out[16]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Now, we will finish our map by adding a tool tip so users can see the results data when they scroll over a polygon:¶
>>> m = folium.Map(location=[40.1, -105.4], tiles='CartoDB Positron', zoom_start=7)

>>> votes = folium.GeoJson(CO_Vote,
                          style_function=style,
                          tooltip=folium.GeoJsonTooltip(
                                  fields=['GEOID','votes_dem', 'votes_rep'], 
                                  aliases=['District ID', 'Democratic', 'Republican'])
                          ).add_to(m)
>>> m
In [17]:
m = folium.Map(location=[39, -105.7], tiles='CartoDB Positron', zoom_start=7)

votes = folium.GeoJson(CO_Vote,
                       style_function=style,  
                       tooltip=folium.GeoJsonTooltip(
                           fields=['GEOID','votes_dem', 'votes_rep'],
                           aliases=['District ID', 'Democratic', 'Republican'])).add_to(m)

m
Out[17]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Export your map:¶
>>> m.save('co_vote_map.html')
In [ ]:
m.save('co_vote_map.html')

Nice Job!¶