[1]:
import transportation_tutorials as tt
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 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]:
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]:
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]:
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
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]: