from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import geopandas as gpd
import altair as alt
pd.options.display.max_columns = 999
Nov 2, 2022
https://github.com/MUSA-550-Fall-2022/final-project
Three parts:
import osmnx as ox
Haven't we already done this?
We've used hvplot, holoviews, datashader, etc. to create interactive map-based visualizations
Pros
Cons
OSMnx leverages Folium under the hood to make interactive graphs of street networks!
Key function: ox.plot_graph_folium
will make an interactive map of the graph object
Load the street network around City Hall
G = ox.graph_from_address('City Hall, Philadelphia, USA',
dist=1500,
network_type='drive')
# plot the street network with folium
graph_map = ox.plot_graph_folium(G,
popup_attribute='name',
edge_width=2)
graph_map
And now save the map object and load it into the Jupyter notebook
from IPython.display import IFrame # loads HTML files
filepath = 'graph.html'
graph_map.save(filepath)
IFrame(filepath, width=600, height=500)
Folium map objects are supposed to render automatically in the Jupyter notebook, so if you output a Folium map from a notebook cell, it will render.
However, there a lot of times when the map won't show up properly, especially if the map has a large amount of data. Saving the file locally and loading it to the notebook via an IFrame will always work.
type(graph_map)
folium.folium.Map
graph_map
Let's calculate the shortest route between the Art Museum and the Liberty Bell.
See last lecture (Lecture 9A) for a guide!
Use OSMnx to download all amenities in Philadelphia of type "tourism".
ox.geometries_from_place()
can download OSM features with a specific tagphilly_tourism = ox.geometries_from_place("Philadelphia, PA", tags={"tourism": True})
len(philly_tourism)
505
philly_tourism.head()
ref | geometry | ele | gnis:county_id | gnis:created | gnis:feature_id | gnis:state_id | name | tourism | brand | brand:wikidata | brand:wikipedia | operator | artwork_type | wikidata | information | addr:state | gnis:county_name | gnis:import_uuid | gnis:reviewed | source | fee | opening_hours | artist_name | wheelchair | wikipedia | addr:city | historic | name:de | designation | amenity | description | museum | operator:type | alt_name | website | source_1 | addr:housenumber | addr:postcode | addr:street | opening_hours:covid19 | layer | phone | contact:email | check_date:opening_hours | name:en | barrier | board_type | toilets:wheelchair | inscription | material | start_date | subject:wikipedia | memorial | level | natural | artwork_subject | attraction | note | comment | historic:amenity | garden:type | leisure | postal_code | internet_access | official_name | short_name | parking | name:ru | guest_house | covered | drinking_water | openfire | addr:unit | artist:wikidata | image | mimics | source:url | direction | nodes | building | building:material | ref:nrhp | name:hi | building:levels | height | roof:shape | highway | incline | step_count | old_name | smoking | building:colour | place | air_conditioning | fax | rooms | stars | roof:colour | heritage | heritage:operator | addr:country | contact:fax | contact:phone | ship:type | addr:housename | roof:material | abandoned:amenity | internet_access:fee | internet_access:ssid | not:brand:wikidata | shop | roof:levels | area | name:zh | artist:website | bicycle | bridge | foot | horse | lit | sac_scale | surface | trail_visibility | width | bridge:structure | man_made | boundary | heritage:website | nrhp:criteria | nrhp:inscription_date | ownership | protected | protection_title | building:part | subject | subject:wikidata | ways | type | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
element_type | osmid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
node | 357371322 | NaN | POINT (-75.19580 39.96970) | 17 | 101 | 08/23/2007 | 2347097 | 42 | Bird Lake Picnic Area | picnic_site | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
360500844 | NaN | POINT (-75.19582 39.95352) | NaN | NaN | NaN | NaN | NaN | Hilton Inn at Penn | hotel | Hilton | Q598884 | en:Hilton Hotels & Resorts | Hilton | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
360542779 | NaN | POINT (-75.18932 39.95540) | NaN | NaN | NaN | NaN | NaN | Mario the Magnificent | artwork | NaN | NaN | NaN | NaN | statue | Q98563440 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
360777728 | NaN | POINT (-75.19021 39.95230) | NaN | NaN | NaN | NaN | NaN | Pennsylvania Historical Marker: ENIAC, first a... | information | NaN | NaN | NaN | NaN | NaN | NaN | board | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
360777735 | NaN | POINT (-75.19166 39.95123) | NaN | NaN | NaN | NaN | NaN | John Harrison, Chemist | artwork | NaN | NaN | NaN | NaN | statue | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
You should notice we have the building footprint for the Art Museum (a polygon geometry) and the point location for the Liberty Bell.
.squeeze()
function can be useful for converting to a Series object from a DataFrame of length 1# How to find the name of the POI: search for keywords
philly_tourism.loc[philly_tourism['name'].str.contains("Art", na=False)]
ref | geometry | ele | gnis:county_id | gnis:created | gnis:feature_id | gnis:state_id | name | tourism | brand | brand:wikidata | brand:wikipedia | operator | artwork_type | wikidata | information | addr:state | gnis:county_name | gnis:import_uuid | gnis:reviewed | source | fee | opening_hours | artist_name | wheelchair | wikipedia | addr:city | historic | name:de | designation | amenity | description | museum | operator:type | alt_name | website | source_1 | addr:housenumber | addr:postcode | addr:street | opening_hours:covid19 | layer | phone | contact:email | check_date:opening_hours | name:en | barrier | board_type | toilets:wheelchair | inscription | material | start_date | subject:wikipedia | memorial | level | natural | artwork_subject | attraction | note | comment | historic:amenity | garden:type | leisure | postal_code | internet_access | official_name | short_name | parking | name:ru | guest_house | covered | drinking_water | openfire | addr:unit | artist:wikidata | image | mimics | source:url | direction | nodes | building | building:material | ref:nrhp | name:hi | building:levels | height | roof:shape | highway | incline | step_count | old_name | smoking | building:colour | place | air_conditioning | fax | rooms | stars | roof:colour | heritage | heritage:operator | addr:country | contact:fax | contact:phone | ship:type | addr:housename | roof:material | abandoned:amenity | internet_access:fee | internet_access:ssid | not:brand:wikidata | shop | roof:levels | area | name:zh | artist:website | bicycle | bridge | foot | horse | lit | sac_scale | surface | trail_visibility | width | bridge:structure | man_made | boundary | heritage:website | nrhp:criteria | nrhp:inscription_date | ownership | protected | protection_title | building:part | subject | subject:wikidata | ways | type | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
element_type | osmid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
node | 367974278 | NaN | POINT (-75.15240 40.03748) | 61 | NaN | NaN | 2349240 | NaN | La Salle University Art Museum | museum | NaN | NaN | NaN | NaN | NaN | Q16893816 | NaN | PA | Philadelphia | 57871b70-0100-4405-bb30-88b2e001a944 | no | USGS Geonames | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4064343335 | NaN | POINT (-75.22112 40.02427) | NaN | NaN | NaN | NaN | NaN | Soft Illusions Art Gallery | gallery | NaN | NaN | NaN | NaN | NaN | NaN | NaN | PA | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Philadelphia | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | http://www.softillusions.net/ | NaN | 4226 | 19127 | Main Street | NaN | NaN | (215) 840-0832 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
5368454121 | NaN | POINT (-75.19477 39.95406) | NaN | NaN | NaN | NaN | NaN | Institute of Contemporary Art | museum | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | yes | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | yes | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
5718906477 | NaN | POINT (-75.13058 39.99573) | NaN | NaN | NaN | NaN | NaN | Art Making Machine Studios | gallery | NaN | NaN | NaN | NaN | NaN | NaN | NaN | PA | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Philadelphia | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3000 | 19133 | North Hope Street | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
6639391809 | NaN | POINT (-75.16088 39.96383) | NaN | NaN | NaN | NaN | NaN | Philadelphia Museum of Jewish Art | museum | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | https://rodephshalom.org/community/philadelphi... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
6680708848 | NaN | POINT (-75.16833 39.94733) | NaN | NaN | NaN | NaN | NaN | Romanian Folk Art Museum | museum | NaN | NaN | NaN | NaN | NaN | Q113484178 | NaN | PA | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Philadelphia | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | http://www.romanianculture.us/ | NaN | 1606 | 19103 | Spruce Street | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
way | 28533972 | NaN | POLYGON ((-75.18116 39.96467, -75.18138 39.964... | 32 | NaN | NaN | NaN | NaN | Philadelphia Museum of Art | museum | NaN | NaN | NaN | NaN | NaN | Q510324 | NaN | PA | NaN | NaN | NaN | NaN | NaN | Su,Mo,Th,Sa 10:00-17:00; Fr 10:00-20:45 | NaN | yes | en:Philadelphia Museum of Art | Philadelphia | NaN | NaN | NaN | NaN | NaN | art | NaN | NaN | https://www.philamuseum.org/ | NaN | 2600 | 19130 | Benjamin Franklin Parkway | NaN | NaN | +1-215-763-8100 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | [313440467, 313440468, 313440469, 313440470, 3... | yes | NaN | NaN | फिलाडेल्फिया कला संग्रहालय | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
333931830 | NaN | POLYGON ((-75.16410 39.95543, -75.16416 39.955... | 13 | NaN | NaN | 1196718 | NaN | Pennsylvania Academy of the Fine Arts | museum | NaN | NaN | NaN | NaN | NaN | Q1952033 | NaN | PA | NaN | NaN | NaN | NaN | NaN | Tu-Fr 10:00-17:00; Sa-Su 11:00-17:00 | NaN | NaN | en:Pennsylvania Academy of the Fine Arts | Philadelphia | building | NaN | NaN | NaN | NaN | art | NaN | NaN | https://www.pafa.org/ | NaN | 118 | 19102 | North Broad Street | NaN | NaN | +1-215-972-7600 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | [3409804705, 3409804706, 3453181052, 340980470... | yes | NaN | 71000731 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | US | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
988345364 | NaN | POLYGON ((-75.21924 40.08337, -75.21934 40.083... | 84 | NaN | NaN | 2350852 | NaN | Woodmere Art Museum | museum | NaN | NaN | NaN | NaN | NaN | Q2424929 | NaN | PA | Philadelphia | 57871b70-0100-4405-bb30-88b2e001a944 | no | USGS Geonames | NaN | NaN | NaN | NaN | NaN | Philadelphia | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 9201 | 19118 | Germantown Avenue | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | [367974360, 9135569422, 9135569423, 9135569424... | yes | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
art_museum = philly_tourism.query("name == 'Philadelphia Museum of Art'").squeeze()
art_museum.geometry
liberty_bell = philly_tourism.query("name == 'Liberty Bell'").squeeze()
liberty_bell.geometry
For the Art Museum geometry, we can use the .geometry.centroid
attribute to calculate the centroid of the building footprint.
Remember: we need the coordinates in the order of (latitude, longitude)
liberty_bell_x = liberty_bell.geometry.x
liberty_bell_y = liberty_bell.geometry.y
art_museum_x = art_museum.geometry.centroid.x
art_museum_y = art_museum.geometry.centroid.y
Use the street network graph around City Hall and the ox.get_nearest_node()
function to find the starting and ending nodes for the trip.
# Get the origin node
orig_node = ox.distance.nearest_nodes(G, liberty_bell_x, liberty_bell_y)
# Get the destination node
dest_node = ox.distance.nearest_nodes(G, art_museum_x, art_museum_y)
networkx
to find the shortest path between nodes¶The nx.shortest_path()
will do the calculation for you!
import networkx as nx
# Calculate the shortest path between these nodes
route = nx.shortest_path(G, orig_node, dest_node)
route
[3408446156, 110217312, 109814427, 109825548, 109825559, 109809258, 109825575, 109825606, 109789819, 775433860, 110232279, 646149049, 110232451, 109755755, 109744095, 109792682, 775896555, 775896556, 110329757, 110329738, 110329851, 110125895, 534969088, 109745905, 109740423, 110330569, 550175455, 550175399]
Now, we can overlay the shortest route between two nodes on the folium map.
Key function: use ox.plot_route_folium
to plot the route.
# plot the route with folium
route_map = ox.plot_route_folium(G, route)
filepath = 'route.html'
route_map.save(filepath)
IFrame(filepath, width=600, height=500)
We can also add the underlying street network graph
# plot the route with folium on top of the previously created graph_map
route_graph_map = ox.plot_route_folium(G, route, route_map=graph_map, color="red", weight=5,)
# save as html file then display map as an iframe
filepath = 'route_graph.html'
route_graph_map.save(filepath)
IFrame(filepath, width=600, height=500)
Note the Leaflet
annotation in the bottom right corner of the maps...
Things we'll cover:
import folium
Key function: folium.Map
Some key ones:
Let's take a look at the help message:
folium.Map?
Init signature: folium.Map( location=None, width='100%', height='100%', left='0%', top='0%', position='relative', tiles='OpenStreetMap', attr=None, min_zoom=0, max_zoom=18, zoom_start=10, min_lat=-90, max_lat=90, min_lon=-180, max_lon=180, max_bounds=False, crs='EPSG3857', control_scale=False, prefer_canvas=False, no_touch=False, disable_3d=False, png_enabled=False, zoom_control=True, **kwargs, ) Docstring: Create a Map with Folium and Leaflet.js Generate a base map of given width and height with either default tilesets or a custom tileset URL. The following tilesets are built-in to Folium. Pass any of the following to the "tiles" keyword: - "OpenStreetMap" - "Mapbox Bright" (Limited levels of zoom for free tiles) - "Mapbox Control Room" (Limited levels of zoom for free tiles) - "Stamen" (Terrain, Toner, and Watercolor) - "Cloudmade" (Must pass API key) - "Mapbox" (Must pass API key) - "CartoDB" (positron and dark_matter) You can pass a custom tileset to Folium by passing a Leaflet-style URL to the tiles parameter: ``http://{s}.yourtiles.com/{z}/{x}/{y}.png``. You can find a list of free tile providers here: ``http://leaflet-extras.github.io/leaflet-providers/preview/``. Be sure to check their terms and conditions and to provide attribution with the `attr` keyword. Parameters ---------- location: tuple or list, default None Latitude and Longitude of Map (Northing, Easting). width: pixel int or percentage string (default: '100%') Width of the map. height: pixel int or percentage string (default: '100%') Height of the map. tiles: str, default 'OpenStreetMap' Map tileset to use. Can choose from a list of built-in tiles, pass a custom URL or pass `None` to create a map without tiles. For more advanced tile layer options, use the `TileLayer` class. min_zoom: int, default 0 Minimum allowed zoom level for the tile layer that is created. max_zoom: int, default 18 Maximum allowed zoom level for the tile layer that is created. zoom_start: int, default 10 Initial zoom level for the map. attr: string, default None Map tile attribution; only required if passing custom tile URL. crs : str, default 'EPSG3857' Defines coordinate reference systems for projecting geographical points into pixel (screen) coordinates and back. You can use Leaflet's values : * EPSG3857 : The most common CRS for online maps, used by almost all free and commercial tile providers. Uses Spherical Mercator projection. Set in by default in Map's crs option. * EPSG4326 : A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. * EPSG3395 : Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. * Simple : A simple CRS that maps longitude and latitude into x and y directly. May be used for maps of flat surfaces (e.g. game maps). Note that the y axis should still be inverted (going from bottom to top). control_scale : bool, default False Whether to add a control scale on the map. prefer_canvas : bool, default False Forces Leaflet to use the Canvas back-end (if available) for vector layers instead of SVG. This can increase performance considerably in some cases (e.g. many thousands of circle markers on the map). no_touch : bool, default False Forces Leaflet to not use touch events even if it detects them. disable_3d : bool, default False Forces Leaflet to not use hardware-accelerated CSS 3D transforms for positioning (which may cause glitches in some rare environments) even if they're supported. zoom_control : bool, default True Display zoom controls on the map. **kwargs Additional keyword arguments are passed to Leaflets Map class: https://leafletjs.com/reference-1.6.0.html#map Returns ------- Folium Map Object Examples -------- >>> m = folium.Map(location=[45.523, -122.675], width=750, height=500) >>> m = folium.Map(location=[45.523, -122.675], tiles='cartodb positron') >>> m = folium.Map( ... location=[45.523, -122.675], ... zoom_start=2, ... tiles='https://api.mapbox.com/v4/mapbox.streets/{z}/{x}/{y}.png?access_token=mytoken', ... attr='Mapbox attribution' ...) File: ~/mambaforge/envs/musa-550-fall-2022/lib/python3.9/site-packages/folium/folium.py Type: type Subclasses:
# let's center the map on Philadelphia
m = folium.Map(
location=[39.99, -75.13],
zoom_start=11
)
m
m = folium.Map(
location=[39.99, -75.13],
zoom_start=11,
tiles='stamenwatercolor'
)
m
Let's try out the ESRI World Map:
Important: for custom tile providers, you need to specify the attribution too!
tile_url = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
attr = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
m = folium.Map(
location=[39.99, -75.13],
zoom_start=11,
tiles=tile_url,
attr=attr
)
m
Key function: folium.GeoJson
Key parameters:
style_function
: set the default style of the featureshighlight_function
: set the style when the mouse hovers over the featurestooltip
: add a tooltip when hovering over a featurefolium.GeoJson?
Init signature: folium.GeoJson( data, style_function=None, highlight_function=None, name=None, overlay=True, control=True, show=True, smooth_factor=None, tooltip=None, embed=True, popup=None, zoom_on_click=False, marker=None, ) Docstring: Creates a GeoJson object for plotting into a Map. Parameters ---------- data: file, dict or str. The GeoJSON data you want to plot. * If file, then data will be read in the file and fully embedded in Leaflet's JavaScript. * If dict, then data will be converted to JSON and embedded in the JavaScript. * If str, then data will be passed to the JavaScript as-is. * If `__geo_interface__` is available, the `__geo_interface__` dictionary will be serialized to JSON and reprojected if `to_crs` is available. style_function: function, default None Function mapping a GeoJson Feature to a style dict. highlight_function: function, default None Function mapping a GeoJson Feature to a style dict for mouse events. name : string, default None The name of the Layer, as it will appear in LayerControls overlay : bool, default True Adds the layer as an optional overlay (True) or the base layer (False). control : bool, default True Whether the Layer will be included in LayerControls show: bool, default True Whether the layer will be shown on opening (only for overlays). smooth_factor: float, default None How much to simplify the polyline on each zoom level. More means better performance and smoother look, and less means more accurate representation. Leaflet defaults to 1.0. tooltip: GeoJsonTooltip, Tooltip or str, default None Display a text when hovering over the object. Can utilize the data, see folium.GeoJsonTooltip for info on how to do that. popup: GeoJsonPopup, optional Show a different popup for each feature by passing a GeoJsonPopup object. marker: Circle, CircleMarker or Marker, optional If your data contains Point geometry, you can format the markers by passing a Circle, CircleMarker or Marker object with your wanted options. The `style_function` and `highlight_function` will also target the marker object you passed. embed: bool, default True Whether to embed the data in the html file or not. Note that disabling embedding is only supported if you provide a file link or URL. zoom_on_click: bool, default False Set to True to enable zooming in on a geometry when clicking on it. Examples -------- >>> # Providing filename that shall be embedded. >>> GeoJson('foo.json') >>> # Providing filename that shall not be embedded. >>> GeoJson('foo.json', embed=False) >>> # Providing dict. >>> GeoJson(json.load(open('foo.json'))) >>> # Providing string. >>> GeoJson(open('foo.json').read()) >>> # Provide a style_function that color all states green but Alabama. >>> style_function = lambda x: {'fillColor': '#0000ff' if ... x['properties']['name']=='Alabama' else ... '#00ff00'} >>> GeoJson(geojson, style_function=style_function) File: ~/mambaforge/envs/musa-550-fall-2022/lib/python3.9/site-packages/folium/features.py Type: type Subclasses:
folium.GeoJsonTooltip?
Init signature: folium.GeoJsonTooltip( fields, aliases=None, labels=True, localize=False, style=None, class_name='foliumtooltip', sticky=True, **kwargs, ) Docstring: Create a tooltip that uses data from either geojson or topojson. Parameters ---------- fields: list or tuple. Labels of GeoJson/TopoJson 'properties' or GeoPandas GeoDataFrame columns you'd like to display. aliases: list/tuple of strings, same length/order as fields, default None. Optional aliases you'd like to display in the tooltip as field name instead of the keys of `fields`. labels: bool, default True. Set to False to disable displaying the field names or aliases. localize: bool, default False. This will use JavaScript's .toLocaleString() to format 'clean' values as strings for the user's location; i.e. 1,000,000.00 comma separators, float truncation, etc. Available for most of JavaScript's primitive types (any data you'll serve into the template). style: str, default None. HTML inline style properties like font and colors. Will be applied to a div with the text in it. sticky: bool, default True Whether the tooltip should follow the mouse. **kwargs: Assorted. These values will map directly to the Leaflet Options. More info available here: https://leafletjs.com/reference-1.6.0#tooltip Examples -------- # Provide fields and aliases, with Style. >>> GeoJsonTooltip( >>> fields=['CNTY_NM', 'census-pop-2015', 'census-md-income-2015'], >>> aliases=['County', '2015 Census Population', '2015 Median Income'], >>> localize=True, >>> style=('background-color: grey; color: white; font-family:' >>> 'courier new; font-size: 24px; padding: 10px;') >>> ) # Provide fields, with labels off and fixed tooltip positions. >>> GeoJsonTooltip(fields=('CNTY_NM',), labels=False, sticky=False) File: ~/mambaforge/envs/musa-550-fall-2022/lib/python3.9/site-packages/folium/features.py Type: type Subclasses:
# Load neighborhoods from GitHub
url = "https://github.com/azavea/geo-data/raw/master/Neighborhoods_Philadelphia/Neighborhoods_Philadelphia.geojson"
hoods = gpd.read_file(url).rename(columns={"mapname": "neighborhood"})
# Load ZIP codes from Open Data Philly
zip_url = "http://data.phl.opendata.arcgis.com/datasets/b54ec5210cee41c3a884c9086f7af1be_0.geojson"
zip_codes = gpd.read_file(zip_url).rename(columns={"CODE":"ZIP Code"})
ax = ox.project_gdf(hoods).plot(fc="lightblue", ec="gray")
ax.set_axis_off()
ax = ox.project_gdf(zip_codes).plot(fc="lightblue", ec="gray")
ax.set_axis_off()
Define functions to set the styles:
def get_zip_code_style(feature):
"""Return a style dict."""
return {"weight": 2, "color": "white"}
def get_neighborhood_style(feature):
"""Return a style dict."""
return {"weight": 2, "color": "lightblue", "fillOpacity": 0.1}
def get_highlighted_style(feature):
"""Return a style dict when highlighting a feature."""
return {"weight": 2, "color": "red"}
# Create the map
m = folium.Map(
location=[39.99, -75.13],
tiles='Cartodb dark_matter',
zoom_start=11
)
# Add the ZIP Codes GeoJson to the map
folium.GeoJson(
zip_codes.to_crs(epsg=4326), # IMPORTANT: make sure CRS is lat/lng (EPSG=4326)
name='Philadelphia ZIP_codes',
style_function=get_zip_code_style,
highlight_function=get_highlighted_style,
tooltip=folium.GeoJsonTooltip(['ZIP Code'])
).add_to(m)
# Add a SECOND layer for neighborhoods
folium.GeoJson(
hoods.to_crs(epsg=4326), # IMPORTANT: make sure CRS is lat/lng (EPSG=4326)
name='Neighborhoods',
style_function=get_neighborhood_style,
highlight_function=get_highlighted_style,
tooltip=folium.GeoJsonTooltip(['neighborhood'])
).add_to(m)
# Also add option to toggle layers
folium.LayerControl().add_to(m)
m
.to_json()
LayerControl
to toggle different layers on the mapfolium.GeoJsonTooltip
Overlay GeoJSON features on an interactive map, colored by a specific data variable
In the interest of time, I've used cenpy
to download data for internet availability from the 2019 5-year ACS, but a good at-home exercise is to try to replicate my work.
census_data = pd.read_csv("./data/internet_avail_census.csv", dtype={"geoid": str})
census_data.head()
NAME | universe | no_internet | state | county | geoid | |
---|---|---|---|---|---|---|
0 | Washington County, Mississippi | 18299.0 | 6166.0 | 28 | 151 | 28151 |
1 | Perry County, Mississippi | 4563.0 | 1415.0 | 28 | 111 | 28111 |
2 | Choctaw County, Mississippi | 3164.0 | 1167.0 | 28 | 19 | 28019 |
3 | Itawamba County, Mississippi | 8706.0 | 1970.0 | 28 | 57 | 28057 |
4 | Carroll County, Mississippi | 3658.0 | 1218.0 | 28 | 15 | 28015 |
# Remove counties with no households
valid = census_data['universe'] > 0
census_data = census_data.loc[valid]
# Calculate the percent without internet
census_data['percent_no_internet'] = census_data['no_internet'] / census_data['universe']
census_data.head()
NAME | universe | no_internet | state | county | geoid | percent_no_internet | |
---|---|---|---|---|---|---|---|
0 | Washington County, Mississippi | 18299.0 | 6166.0 | 28 | 151 | 28151 | 0.336958 |
1 | Perry County, Mississippi | 4563.0 | 1415.0 | 28 | 111 | 28111 | 0.310103 |
2 | Choctaw County, Mississippi | 3164.0 | 1167.0 | 28 | 19 | 28019 | 0.368837 |
3 | Itawamba County, Mississippi | 8706.0 | 1970.0 | 28 | 57 | 28057 | 0.226281 |
4 | Carroll County, Mississippi | 3658.0 | 1218.0 | 28 | 15 | 28015 | 0.332969 |
Load counties from the data folder as well:
counties = gpd.read_file("./data/us-counties-10m.geojson")
counties.head()
id | geometry | |
---|---|---|
0 | 53073 | MULTIPOLYGON (((-120.85361 49.00011, -120.7674... |
1 | 30105 | POLYGON ((-106.11238 48.99904, -106.15187 48.8... |
2 | 30029 | POLYGON ((-114.06985 48.99904, -114.05908 48.8... |
3 | 16021 | POLYGON ((-116.04755 49.00065, -116.04755 48.5... |
4 | 30071 | POLYGON ((-107.17840 49.00011, -107.20712 48.9... |
folium.Choropleth?
Init signature: folium.Choropleth( geo_data, data=None, columns=None, key_on=None, bins=6, fill_color=None, nan_fill_color='black', fill_opacity=0.6, nan_fill_opacity=None, line_color='black', line_weight=1, line_opacity=1, name=None, legend_name='', overlay=True, control=True, show=True, topojson=None, smooth_factor=None, highlight=None, **kwargs, ) Docstring: Apply a GeoJSON overlay to the map. Plot a GeoJSON overlay on the base map. There is no requirement to bind data (passing just a GeoJSON plots a single-color overlay), but there is a data binding option to map your columnar data to different feature objects with a color scale. If data is passed as a Pandas DataFrame, the "columns" and "key-on" keywords must be included, the first to indicate which DataFrame columns to use, the second to indicate the layer in the GeoJSON on which to key the data. The 'columns' keyword does not need to be passed for a Pandas series. Colors are generated from color brewer (http://colorbrewer2.org/) sequential palettes. By default, linear binning is used between the min and the max of the values. Custom binning can be achieved with the `bins` parameter. TopoJSONs can be passed as "geo_data", but the "topojson" keyword must also be passed with the reference to the topojson objects to convert. See the topojson.feature method in the TopoJSON API reference: https://github.com/topojson/topojson/wiki/API-Reference Parameters ---------- geo_data: string/object URL, file path, or data (json, dict, geopandas, etc) to your GeoJSON geometries data: Pandas DataFrame or Series, default None Data to bind to the GeoJSON. columns: dict or tuple, default None If the data is a Pandas DataFrame, the columns of data to be bound. Must pass column 1 as the key, and column 2 the values. key_on: string, default None Variable in the `geo_data` GeoJSON file to bind the data to. Must start with 'feature' and be in JavaScript objection notation. Ex: 'feature.id' or 'feature.properties.statename'. bins: int or sequence of scalars or str, default 6 If `bins` is an int, it defines the number of equal-width bins between the min and the max of the values. If `bins` is a sequence, it directly defines the bin edges. For more information on this parameter, have a look at numpy.histogram function. fill_color: string, optional Area fill color, defaults to blue. Can pass a hex code, color name, or if you are binding data, one of the following color brewer palettes: 'BuGn', 'BuPu', 'GnBu', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'RdPu', 'YlGn', 'YlGnBu', 'YlOrBr', and 'YlOrRd'. nan_fill_color: string, default 'black' Area fill color for nan or missing values. Can pass a hex code, color name. fill_opacity: float, default 0.6 Area fill opacity, range 0-1. nan_fill_opacity: float, default fill_opacity Area fill opacity for nan or missing values, range 0-1. line_color: string, default 'black' GeoJSON geopath line color. line_weight: int, default 1 GeoJSON geopath line weight. line_opacity: float, default 1 GeoJSON geopath line opacity, range 0-1. legend_name: string, default empty string Title for data legend. topojson: string, default None If using a TopoJSON, passing "objects.yourfeature" to the topojson keyword argument will enable conversion to GeoJSON. smooth_factor: float, default None How much to simplify the polyline on each zoom level. More means better performance and smoother look, and less means more accurate representation. Leaflet defaults to 1.0. highlight: boolean, default False Enable highlight functionality when hovering over a GeoJSON area. name : string, optional The name of the layer, as it will appear in LayerControls overlay : bool, default True Adds the layer as an optional overlay (True) or the base layer (False). control : bool, default True Whether the Layer will be included in LayerControls. show: bool, default True Whether the layer will be shown on opening (only for overlays). Returns ------- GeoJSON data layer in obj.template_vars Examples -------- >>> Choropleth(geo_data='us-states.json', line_color='blue', ... line_weight=3) >>> Choropleth(geo_data='geo.json', data=df, ... columns=['Data 1', 'Data 2'], ... key_on='feature.properties.myvalue', ... fill_color='PuBu', ... bins=[0, 20, 30, 40, 50, 60]) >>> Choropleth(geo_data='countries.json', ... topojson='objects.countries') >>> Choropleth(geo_data='geo.json', data=df, ... columns=['Data 1', 'Data 2'], ... key_on='feature.properties.myvalue', ... fill_color='PuBu', ... bins=[0, 20, 30, 40, 50, 60], ... highlight=True) File: ~/mambaforge/envs/musa-550-fall-2022/lib/python3.9/site-packages/folium/features.py Type: type Subclasses:
Steps:
key_on=
), using the GeoJSON "features.properties." syntaxcolumns=
keywordm = folium.Map(location=[40, -98], zoom_start=4)
# Convert the counties geometries into GeoJSON
counties_geojson = counties.to_crs(epsg=4326)
folium.Choropleth(
geo_data=counties_geojson, # Pass in GeoJSON data for counties
data=census_data, # the census data
columns=["geoid", 'percent_no_internet'], # First column must be the key, second the values
key_on="feature.properties.id", # Key to match on in the geometries --> Remember to prepend "feature.properties"
fill_color='RdPu', # any ColorBrewer name will work here
fill_opacity=0.7,
line_opacity=1,
line_weight=0.5,
legend_name='Households without Internet (%)',
name='choropleth',
).add_to(m)
m
Note: this is different than using folium.Choropleth
, where data and features are stored in two separate data frames.
# Merge the county geometries with census data
# Left column: "id"
# Right column: "geoid"
census_joined = counties.merge(census_data, left_on="id", right_on="geoid")
census_joined.head()
id | geometry | NAME | universe | no_internet | state | county | geoid | percent_no_internet | |
---|---|---|---|---|---|---|---|---|---|
0 | 53073 | MULTIPOLYGON (((-120.85361 49.00011, -120.7674... | Whatcom County, Washington | 85008.0 | 9189.0 | 53 | 73 | 53073 | 0.108096 |
1 | 30105 | POLYGON ((-106.11238 48.99904, -106.15187 48.8... | Valley County, Montana | 3436.0 | 672.0 | 30 | 105 | 30105 | 0.195576 |
2 | 30029 | POLYGON ((-114.06985 48.99904, -114.05908 48.8... | Flathead County, Montana | 38252.0 | 5662.0 | 30 | 29 | 30029 | 0.148018 |
3 | 16021 | POLYGON ((-116.04755 49.00065, -116.04755 48.5... | Boundary County, Idaho | 4605.0 | 1004.0 | 16 | 21 | 16021 | 0.218024 |
4 | 30071 | POLYGON ((-107.17840 49.00011, -107.20712 48.9... | Phillips County, Montana | 1770.0 | 484.0 | 30 | 71 | 30071 | 0.273446 |
# Minimum
min_val = census_joined['percent_no_internet'].min()
# Maximum
max_val = census_joined['percent_no_internet'].max()
# Calculate a normalized column
normalized = (census_joined['percent_no_internet'] - min_val) / (max_val - min_val)
# Add to the dataframe
census_joined['percent_no_internet_normalized'] = normalized
plt.get_cmap()
import matplotlib.colors as mcolors
# Use a red-purple colorbrewer color scheme
cmap = plt.get_cmap('RdPu')
# The minimum value of the color map as an RGB tuple
cmap(0)
(1.0, 0.9686274509803922, 0.9529411764705882, 1.0)
# The minimum value of the color map as a hex string
mcolors.rgb2hex(cmap(0.0))
'#fff7f3'
# The maximum value of the color map as a hex string
mcolors.rgb2hex(cmap(1.0))
'#49006a'
def get_style(feature):
"""
Given an input GeoJSON feature, return a style dict.
Notes
-----
The color in the style dict is determined by the
"percent_no_internet_normalized" column in the
input "feature".
"""
# Get the data value from the feature
value = feature['properties']['percent_no_internet_normalized']
# Evaluate the color map
# NOTE: value must between 0 and 1
rgb_color = cmap(value) # this is an RGB tuple
# Convert to hex string
color = mcolors.rgb2hex(rgb_color)
# Return the style dictionary
return {'weight': 0.5, 'color': color, 'fillColor': color, "fillOpacity": 0.75}
def get_highlighted_style(feature):
"""
Return a style dict to use when the user highlights a
feature with the mouse.
"""
return {"weight": 3, "color": "black"}
.to_json()
function to convert to a GeoJSON stringneeded_cols = ['NAME', 'percent_no_internet', 'percent_no_internet_normalized', 'geometry']
census_json = census_joined[needed_cols]
# STEP 1: Initialize the map
m = folium.Map(location=[40, -98], zoom_start=4)
# STEP 2: Add the GeoJson to the map
folium.GeoJson(
census_json, # The geometry + data columns in GeoJSON format
style_function=get_style, # The style function to color counties differently
highlight_function=get_highlighted_style,
tooltip=folium.GeoJsonTooltip(['NAME', 'percent_no_internet'])
).add_to(m)
# avoid a rendering bug by saving as HTML and re-loading
m.save('percent_no_internet.html')
The hard way is harder, but we have a tooltip and highlight interactivity!
from IPython.display import IFrame
IFrame('percent_no_internet.html', width=800, height=500)
See you on Monday for clustering!