[1]:
import transportation_tutorials as tt

Creating Dynamic Maps

In this gallery, we will demonstrate the creation of a variety of interactive maps. Interactive, dynamic maps are a good choice for analytical work that will be reviewed online, either in a Jupyter notebook by an analyst, or published on a website. In these examples, we will demonstrate creating static maps using Folium. There are many more examples at the Folium repository on GitHub.

[2]:
import numpy as np
import pandas as pd
import geopandas as gpd
import folium

We’ll begin by loading the TAZ and MAZ shapefiles, filtering them to a restricted study area, and defining the center point.

[3]:
xmin = 905712
ymin = 905343
taz = gpd.read_file(tt.data('SERPM8-TAZSHAPE')).cx[xmin:, ymin:].to_crs(epsg=4326)
maz = gpd.read_file(tt.data('SERPM8-MAZSHAPE')).cx[xmin:, ymin:].to_crs(epsg=4326)
center = (26.9198, -80.1121) # regular lat-lon

Simple Map

Simple maps showing the geographic data contained in a GeoDataFrame can be created by converting the GeoDataFrame to a GeoJson object, and adding that to a folium Map.

[4]:
m = folium.Map(center, zoom_start = 12)
folium.GeoJson(taz).add_to(m)
m
[4]:

Alternative Map Tiles

The default tiles are set to OpenStreetMap, but others tiles are built in, including tilesets from Stamen Design and Carto. The positron tiles are specifically designed to give geographic context without overwhelming maps with data that is not the analytic focus of the presentation.

[5]:
m = folium.Map(center, zoom_start = 12, tiles='CartoDB positron',)
m
[5]:

Map Markers

In addition to mapping data from GeoDataFrames, it is also possible to draw markers on folium maps by indicating them manually in code.

[6]:
m = folium.Map(
    location=[26.8645, -80.1040],
    tiles='Stamen Toner',
    zoom_start=13
)

folium.CircleMarker(
    radius=20, # in pixels, regardless of map zoom
    location=[26.8853, -80.1140],
    popup='Scripps Research Institute',
    color='blue', fill=True,
).add_to(m)

folium.Circle(
    radius=300, # in meters, scales with map zoom
    location=[26.8484, -80.0855],
    popup='The Gardens Mall',
    color='crimson', fill=True, fill_color='pink'
).add_to(m)

folium.Marker(
    [26.677037, -80.037117],
    popup='Mar-a-Lago Club',
    icon=folium.Icon(color='red', icon='info-sign'),
).add_to(m)

m
[6]:

Mapping Data

One of the input files for SERPM 8 is a MAZ-level demographics file. The file for the 2015 base year is included in the tutorial data, and we can load it with the read_csv function.

[7]:
mazd = pd.read_csv(tt.data('SERPM8-MAZDATA', '*.csv'))

Use info to see a summary of the DataFrame.

[8]:
mazd.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 12022 entries, 0 to 12021
Data columns (total 76 columns):
mgra                            12022 non-null int64
TAZ                             12022 non-null int64
HH                              12022 non-null int64
POP                             12022 non-null int64
emp_self                        12022 non-null int64
emp_ag                          12022 non-null int64
emp_const_non_bldg_prod         12022 non-null int64
emp_const_non_bldg_office       12022 non-null int64
emp_utilities_prod              12022 non-null int64
emp_utilities_office            12022 non-null int64
emp_const_bldg_prod             12022 non-null int64
emp_const_bldg_office           12022 non-null int64
emp_mfg_prod                    12022 non-null int64
emp_mfg_office                  12022 non-null int64
emp_whsle_whs                   12022 non-null int64
emp_trans                       12022 non-null int64
emp_retail                      12022 non-null int64
emp_prof_bus_svcs               12022 non-null int64
emp_prof_bus_svcs_bldg_maint    12022 non-null int64
emp_pvt_ed_k12                  12022 non-null int64
emp_pvt_ed_post_k12_oth         12022 non-null int64
emp_health                      12022 non-null int64
emp_personal_svcs_office        12022 non-null int64
emp_amusement                   12022 non-null int64
emp_hotel                       12022 non-null int64
emp_restaurant_bar              12022 non-null int64
emp_personal_svcs_retail        12022 non-null int64
emp_religious                   12022 non-null int64
emp_pvt_hh                      12022 non-null int64
emp_state_local_gov_ent         12022 non-null int64
emp_scrap_other                 12022 non-null int64
emp_fed_non_mil                 12022 non-null int64
emp_fed_mil                     12022 non-null int64
emp_state_local_gov_blue        12022 non-null int64
emp_state_local_gov_white       12022 non-null int64
emp_public_ed                   12022 non-null int64
emp_own_occ_dwell_mgmt          12022 non-null int64
emp_fed_gov_accts               12022 non-null int64
emp_st_lcl_gov_accts            12022 non-null int64
emp_cap_accts                   12022 non-null int64
emp_total                       12022 non-null int64
collegeEnroll                   12022 non-null int64
otherCollegeEnroll              12022 non-null int64
AdultSchEnrl                    12022 non-null int64
EnrollGradeKto8                 12022 non-null int64
EnrollGrade9to12                12022 non-null int64
PrivateEnrollGradeKto8          12022 non-null int64
ech_dist                        12022 non-null int64
hch_dist                        12022 non-null int64
parkarea                        12022 non-null int64
hstallsoth                      12022 non-null int64
hstallssam                      12022 non-null int64
hparkcost                       12022 non-null int64
numfreehrs                      12022 non-null int64
dstallsoth                      12022 non-null int64
dstallssam                      12022 non-null int64
dparkcost                       12022 non-null int64
mstallsoth                      12022 non-null int64
mstallssam                      12022 non-null int64
mparkcost                       12022 non-null float64
TotInt                          12022 non-null int64
DUDen                           12022 non-null float64
EmpDen                          12022 non-null float64
PopDen                          12022 non-null float64
RetEmpDen                       12022 non-null float64
IntDenBin                       12022 non-null int64
EmpDenBin                       12022 non-null int64
DuDenBin                        12022 non-null int64
POINT_X                         12022 non-null int64
POINT_Y                         12022 non-null int64
ACRES                           12022 non-null int64
HotelRoomTotal                  12022 non-null int64
mall_flag                       12022 non-null int64
beachAcres                      12022 non-null int64
geoSRate                        12022 non-null int64
geoSRateNm                      12022 non-null int64
dtypes: float64(5), int64(71)
memory usage: 7.0 MB

We can join the demographics table to the shape file we loaded previously, to enable some visualizations on this data. This can be done with the merge method of DataFrames.

[9]:
maz1 = maz.merge(mazd, how='left', left_on='MAZ', right_on='mgra')
[10]:
maz1.index=maz1.MAZ

Choropleth Maps

A choropleth map is a map with areas colored, shaded, or patterned in proportion to some measured value for the region displayed. This kind of map is commonly used to display things like population density.

A GeoDataFrame can be used to create a choropleth map with folium.

[11]:
m = folium.Map(center, zoom_start = 12)
folium.Choropleth(
    geo_data=maz1,
    data=maz1.PopDen,
    key_on='feature.properties.MAZ',
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='Population Density'
).add_to(m)
m
[11]:

The folium.Choropleth class is designed to be simple and easy, but not highly extensible. If you want to customize a dynamic choropleth with additional features, you can do so using the more flexible GeoJson class, which allows more customization. For example, below we draw a choropleth that colors actual zero values in pink, and adds a tooltip hover and blue outline that identifies some details about each zone when you mouseover it.

[12]:
from branca import colormap

colormapper = colormap.linear.YlGn_09.scale(
    maz1.PopDen.min(),
    maz1.PopDen.max(),
)
colormapper.caption = "Population Density"

def colormapper_with_zero(x):
    if x==0:
        return "#faded1"
    else:
        return colormapper(x)

m = folium.Map(center, zoom_start = 12)
gj = folium.GeoJson(
    maz1,
    style_function=lambda feature: {
        'fillColor': colormapper_with_zero(feature['properties']['PopDen']),
        'color': 'black',
        'weight': 0.5,
        'fillOpacity': 0.8,
    },
    highlight_function=lambda feature: {
        'color': 'blue',
        'weight': 4,
    },
    tooltip=folium.GeoJsonTooltip(
        fields=['TAZ', 'MAZ', 'PopDen'],
    ),
).add_to(m)
colormapper.add_to(m)
m
[12]: