Mapping College Campuses with OpenStreetMap and Python¶

In this lesson, we will explore how to use OpenStreetMap (OSM) and Python to create maps of college campuses. OpenStreetMap is a free, community-created map of the world that contains information about buildings, roads, trails, parks, and many other geographic features. Because OSM data are open and publicly available, they provide an excellent resource for learning spatial data science and geographic visualization.

We will use the Python package OSMnx to download and work with OSM data. OSMnx allows us to search for geographic features such as buildings and roads using simple Python commands. Throughout this lesson, we will experiment with several different ways to query OSM data, including searching by address, place name, geographic point, and polygon boundary.

Our goal is to build interactive campus maps that help us explore how colleges and universities are represented in OpenStreetMap. Along the way, we will also learn some important ideas in geographic information science (GIS), including spatial features, geometry types, and map visualization.

Throughout the notebook we'll be demonstrating with the University of Colorado Boulder, and then provide space for you try it on your own for a campus you are interested in exploring. By the end of this notebook, you will be able to:

  • Query OpenStreetMap data using Python
  • Extract buildings and paths from OSM
  • Create interactive maps using hvplot
  • Explore and visualize college campuses spatially
  • Understand how geographic features are stored in OSM

No prior GIS experience is required for this lesson.

In [2]:
# Import python libraries

# Work with vector data
import geopandas as gpd
import pandas as pd

# Save maps and plots to files
import holoviews as hv
# Create interactive maps and plots
import hvplot.pandas

# Search for locations by name - this might take a moment
import osmnx as osm

Part 1 - Searching by address¶

Here we will show how to query OSM using a known address of a college campus using the osm.features_from_address() function. This approach can be tricky given that campus names are not always valid addresses, rural colleges sometimes geocode poorly, and slight wording differences can break the query leading students to get frustrated with geocoder errors.

In [3]:
# Search OSM for CU Boulder using features_by_address
cu_gdf = osm.features_from_address(
    'University of Colorado Boulder, Boulder, CO, United States',
    {'amenity': ['university']})
cu_gdf
Out[3]:
geometry nodes amenity boundary internet_access name operator short_name website wikidata wikipedia ele gnis:feature_id
element_type osmid
way 46226108 POLYGON ((-105.27589 40.01047, -105.27597 40.0... [589514700, 2188984094, 2188984095, 326889012,... university administrative wlan University of Colorado Boulder (Main Campus) University of Colorado Boulder CU Boulder https://www.colorado.edu/ Q736674 en:University of Colorado Boulder NaN NaN
391232001 POLYGON ((-105.26632 40.01452, -105.26631 40.0... [3944423259, 3944423260, 3944423261, 394442326... university NaN NaN Naropa University Naropa University NaN https://www.naropa.edu Q2975783 en:Naropa University 1620 178660

Notice here that in addition to returning CU Boulder like we had asked for it also includes information about Naropa University, also located in Boulder. We can remove that entry if we only want to focus on the data for CU as follows. Here we select by the 'name' column, and provide the unique ID for CU (University of Colorado Boulder (Main Campus)).

In [4]:
# Select only CU
cu_gdf_filtered = cu_gdf[cu_gdf["name"] == "University of Colorado Boulder (Main Campus)"]
cu_gdf_filtered
Out[4]:
geometry nodes amenity boundary internet_access name operator short_name website wikidata wikipedia ele gnis:feature_id
element_type osmid
way 46226108 POLYGON ((-105.27589 40.01047, -105.27597 40.0... [589514700, 2188984094, 2188984095, 326889012,... university administrative wlan University of Colorado Boulder (Main Campus) University of Colorado Boulder CU Boulder https://www.colorado.edu/ Q736674 en:University of Colorado Boulder NaN NaN

Now let's plot an interactive map of the boundary of our data using hvplot.

In [5]:
cu_gdf_filtered.hvplot(
    geo=True,
    tiles="EsriImagery",
    line_color="black",
    fill_color="lightgray",
    alpha=0.7,
    width=800,
    height=600,
    title="University of Colorado Boulder (Main Campus)"
)
/opt/conda/lib/python3.11/site-packages/dask/dataframe/__init__.py:31: FutureWarning: 
Dask dataframe query planning is disabled because dask-expr is not installed.

You can install it with `pip install dask[dataframe]` or `conda install dask`.
This will raise in a future version.

  warnings.warn(msg, FutureWarning)
Out[5]:

Part 2 - Searching by point¶

Here we'll demonstrate how to set up a query using the features_from_point() function. We'll estimate the lat/lon of the center of CU Main Campus using Google Maps or another mapping tool of your choice. From there, we'll look for everything that is tagged as a 'building' within a specified distance and then plot the results. You could also look for things tagged as parks, waterways, highways (anything that is road-like), etc. depending on how much information you want your map to contain. Here is your opportunity to be creative. Read more about how tags are used in OSM here: https://wiki.openstreetmap.org/wiki/Tags.

In [6]:
# Approximate center of the University of Colorado Boulder campus
cu_point = (40.0076, -105.2659)  # latitude, longitude
In [7]:
# Search for buildings near the campus point
cu_buildings = osm.features_from_point(
    cu_point,
    tags={"building": True},
    dist=750
)

cu_buildings
Out[7]:
geometry access addr:city addr:housenumber addr:postcode addr:state addr:street description phone website ... location cooling:method check_date opening_date ways type construction roof:colour internet_access internet_access:fee
element_type osmid
way 28775431 POLYGON ((-105.26585 40.00596, -105.26585 40.0... NaN Boulder 2200 NaN CO Baker Drive Residence hall with a new food court; home of ... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775448 POLYGON ((-105.26486 40.00597, -105.26469 40.0... NaN Boulder 1001 NaN CO Cockerell Drive Residence hall with buffet apartments, for sen... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775533 POLYGON ((-105.26486 40.00654, -105.26481 40.0... NaN Boulder 1015 NaN CO Cockerell Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775536 POLYGON ((-105.26461 40.00653, -105.26441 40.0... NaN Boulder 2370 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775541 POLYGON ((-105.26530 40.00707, -105.26515 40.0... NaN Boulder 2350 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
relation 12097289 POLYGON ((-105.26285 40.00283, -105.26285 40.0... NaN Boulder 2480 NaN NaN Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN [46938900, 888721100] multipolygon residential NaN NaN NaN
12098405 POLYGON ((-105.26289 40.00134, -105.26289 40.0... NaN Boulder 2450 NaN CO Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN [168647687, 888779941, 888779946] multipolygon NaN red NaN NaN
12903697 MULTIPOLYGON (((-105.26872 40.01444, -105.2686... NaN Boulder 2020 NaN CO Arapahoe Avenue NaN NaN https://www.choicehotels.com/colorado/boulder/... ... NaN NaN NaN NaN [119524745, 452957200] multipolygon NaN NaN wlan customers
18555106 MULTIPOLYGON (((-105.26781 40.00893, -105.2678... NaN Boulder 2085 NaN NaN Colorado Avenue Athletics and events stadium, expanded with sk... NaN NaN ... NaN NaN NaN NaN [46371962, 318135984] multipolygon NaN NaN NaN NaN
18723484 POLYGON ((-105.27360 40.01321, -105.27366 40.0... NaN Boulder 1604 80302 CO Arapahoe Avenue NaN NaN NaN ... NaN NaN NaN NaN [52429583, 1360976244] multipolygon NaN NaN NaN NaN

752 rows × 97 columns

In [8]:
# Plot the buildings near CU Main campus
cu_buildings.hvplot(
    geo=True,
    tiles="EsriImagery",
    line_color="black",
    fill_color="yellow",
    alpha=1,
    width=800,
    height=600,
    title="University of Colorado Boulder Buildings (by point)"
)
Out[8]:

Here we were fortunate that all of the buildings that were returned in this search were geocoded as polygons. You may encounter situations where your query returns a mix of points (nodes) and polygons (ways) which will require filtering before the data can be plotted interacatively with hvplot. We'll try to demonstrate how to resolve this issue when it comes up.

Part 3 - Searching by place¶

In this example, we will use the features_from_place() function from OSMnx to search for geographic features inside the boundary of the University of Colorado Boulder campus.

Unlike features_from_point(), which searches around a coordinate location, features_from_place() first searches OpenStreetMap for the boundary of a named place and then retrieves features that fall inside that boundary.

In [9]:
# Define a variable name for our place
place = "University of Colorado Boulder (Main Campus), Boulder, Colorado, USA"
In [10]:
# Search for all buildings within the boundary of this place
cu_buildings = osm.features_from_place(
    place,
    tags={"building": True}
)

cu_buildings
Out[10]:
geometry access addr:city addr:housenumber addr:postcode addr:state addr:street description website amenity ... covered bench location cooling:method check_date opening_date ways type construction roof:colour
element_type osmid
way 28775431 POLYGON ((-105.26585 40.00596, -105.26585 40.0... NaN Boulder 2200 NaN CO Baker Drive Residence hall with a new food court; home of ... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775448 POLYGON ((-105.26486 40.00597, -105.26469 40.0... NaN Boulder 1001 NaN CO Cockerell Drive Residence hall with buffet apartments, for sen... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775533 POLYGON ((-105.26486 40.00654, -105.26481 40.0... NaN Boulder 1015 NaN CO Cockerell Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775536 POLYGON ((-105.26461 40.00653, -105.26441 40.0... NaN Boulder 2370 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775541 POLYGON ((-105.26530 40.00707, -105.26515 40.0... NaN Boulder 2350 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
relation 9826677 POLYGON ((-105.26199 40.00749, -105.26199 40.0... NaN Boulder 1095 NaN NaN Regent Drive State-of-the-art research and learning laborat... NaN NaN ... NaN NaN NaN NaN NaN NaN [706728927, 706728926] multipolygon NaN NaN
11964909 POLYGON ((-105.26163 40.00346, -105.26129 40.0... NaN Boulder 25890 80310 CO Kitterege Loop Road NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [879469306, 879469307, 879469308, 879469305, 8... multipolygon NaN NaN
12097289 POLYGON ((-105.26285 40.00283, -105.26285 40.0... NaN Boulder 2480 NaN NaN Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [46938900, 888721100] multipolygon residential NaN
12098405 POLYGON ((-105.26289 40.00134, -105.26289 40.0... NaN Boulder 2450 NaN CO Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [168647687, 888779941, 888779946] multipolygon NaN red
18555106 MULTIPOLYGON (((-105.26781 40.00893, -105.2678... NaN Boulder 2085 NaN NaN Colorado Avenue Athletics and events stadium, expanded with sk... NaN NaN ... NaN NaN NaN NaN NaN NaN [46371962, 318135984] multipolygon NaN NaN

164 rows × 76 columns

In [11]:
# Plot the data interactively using hvplot
cu_buildings.hvplot(
    geo=True,
    tiles="EsriImagery",
    line_color="black",
    fill_color="yellow",
    alpha=1,
    width=800,
    height=600,
    title="University of Colorado Boulder Buildings (from place)"
)
Out[11]:

Observe that there are now fewer buildings than resulted from the previous example that used features_by_point, and are now strictly contained within the boundary of the CU Boulder Main Campus. This may be preferred depending on the information you are trying to convey with your map.

NOTE: features_from_place() works best when OpenStreetMap contains a clearly defined boundary for the place being searched. Some campuses and institutions may not have complete or well-defined boundaries in OSM. When this happens, features_from_point() is often a more reliable alternative.

Part 4 - Searching by polygon¶

Here we will extract polygon geometry and then query OSM for all buildings inside the specified boundary. Polygons can come from many different sources including features_from_place like we just saw, OSM way ID (obtained from the interactive web map), geoJSON or shapefile, or from a boundary that you define with coordinates. For this demonstration we will use the OSM way ID (https://www.openstreetmap.org/way/46226108) and the geocode_to_gdf() function, but you can try playing around with these other approaches if you are curious and have time. Once we have our polygon geometry, we can then use features_from_polygon to search for buildings or other OSM objects.

In [12]:
# OSM way ID
osm_way_id = 46226108

# Retrieve polygon boundary
campus_boundary = osm.geocode_to_gdf(
    "W46226108",
    by_osmid=True
)

campus_boundary
Out[12]:
geometry bbox_north bbox_south bbox_east bbox_west place_id osm_type osm_id lat lon class type place_rank importance addresstype name display_name
0 POLYGON ((-105.27660 40.01029, -105.27660 40.0... 40.013632 40.000279 -105.258821 -105.276605 329820921 way 46226108 40.007006 -105.266442 amenity university 30 0.583709 amenity University of Colorado Boulder (Main Campus) University of Colorado Boulder (Main Campus), ...
In [13]:
# Extract polygon geometry
campus_polygon = campus_boundary.geometry.iloc[0]
campus_polygon
Out[13]:
No description has been provided for this image
In [14]:
# Search for everthing tagged as 'building' within the specified boundary
campus_buildings = osm.features_from_polygon(
    campus_polygon,
    tags={"building": True}
)

campus_buildings
Out[14]:
geometry access addr:city addr:housenumber addr:postcode addr:state addr:street description website amenity ... covered bench location cooling:method check_date opening_date ways type construction roof:colour
element_type osmid
way 28775431 POLYGON ((-105.26585 40.00596, -105.26585 40.0... NaN Boulder 2200 NaN CO Baker Drive Residence hall with a new food court; home of ... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775448 POLYGON ((-105.26486 40.00597, -105.26469 40.0... NaN Boulder 1001 NaN CO Cockerell Drive Residence hall with buffet apartments, for sen... NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775533 POLYGON ((-105.26486 40.00654, -105.26481 40.0... NaN Boulder 1015 NaN CO Cockerell Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775536 POLYGON ((-105.26461 40.00653, -105.26441 40.0... NaN Boulder 2370 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28775541 POLYGON ((-105.26530 40.00707, -105.26515 40.0... NaN Boulder 2350 NaN CO Libby Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
relation 9826677 POLYGON ((-105.26199 40.00749, -105.26199 40.0... NaN Boulder 1095 NaN NaN Regent Drive State-of-the-art research and learning laborat... NaN NaN ... NaN NaN NaN NaN NaN NaN [706728927, 706728926] multipolygon NaN NaN
11964909 POLYGON ((-105.26163 40.00346, -105.26129 40.0... NaN Boulder 25890 80310 CO Kitterege Loop Road NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [879469306, 879469307, 879469308, 879469305, 8... multipolygon NaN NaN
12097289 POLYGON ((-105.26285 40.00283, -105.26285 40.0... NaN Boulder 2480 NaN NaN Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [46938900, 888721100] multipolygon residential NaN
12098405 POLYGON ((-105.26289 40.00134, -105.26289 40.0... NaN Boulder 2450 NaN CO Kittredge Loop Drive NaN NaN NaN ... NaN NaN NaN NaN NaN NaN [168647687, 888779941, 888779946] multipolygon NaN red
18555106 MULTIPOLYGON (((-105.26781 40.00893, -105.2678... NaN Boulder 2085 NaN NaN Colorado Avenue Athletics and events stadium, expanded with sk... NaN NaN ... NaN NaN NaN NaN NaN NaN [46371962, 318135984] multipolygon NaN NaN

164 rows × 76 columns

Let's try adding the water bodies that fall within the campus boundary to this map.

In [29]:
# Search for everthing tagged as 'water' within the specified boundary
campus_water = osm.features_from_polygon(
    campus_polygon,
    tags={"natural":"water",
        "waterway": True,
        "water":True}
)

campus_water
Out[29]:
nodes ele gnis:feature_id name natural short_name water geometry description intermittent waterway source usage
element_type osmid
way 46197729 [589040165, 2496839092, 589040178, 589040176, ... 1649 178656 Varsity Lake water W-VL reservoir POLYGON ((-105.27394 40.00972, -105.27393 40.0... NaN NaN NaN NaN NaN
46938874 [599116490, 599116491, 599116492, 599116493, 5... NaN NaN NaN water NaN NaN POLYGON ((-105.25921 40.00682, -105.25919 40.0... NaN NaN NaN NaN NaN
46938875 [599116499, 4753724662, 599116500, 599116501, ... NaN NaN NaN water NaN NaN POLYGON ((-105.25954 40.00786, -105.25957 40.0... NaN NaN NaN NaN NaN
46938882 [599116565, 599116566, 599116567, 599116568, 5... NaN NaN Kitt Pond water NaN pond POLYGON ((-105.26259 40.00270, -105.26258 40.0... No fishing allowed, frequented by canadian gee... no NaN NaN NaN
61758946 [434098986, 434098987, 434098988, 434098989, 4... NaN NaN Boulder Creek NaN NaN NaN LINESTRING (-105.29443 40.01364, -105.29417 40... NaN no stream NaN NaN
194217260 [2047352879, 2047352903, 2047352859, 204735286... NaN NaN NaN NaN NaN NaN LINESTRING (-105.26105 40.00291, -105.26101 40... NaN NaN drain bing NaN
482560081 [4753724705, 4753724706] NaN NaN NaN NaN NaN NaN LINESTRING (-105.25917 40.00785, -105.25913 40... NaN yes canal NaN spillway
888538283 [8260987529, 8260987530] NaN NaN NaN NaN NaN NaN LINESTRING (-105.26306 40.00090, -105.26300 40... NaN NaN drain NaN NaN
In [19]:
campus_water.plot()
Out[19]:
<Axes: >
No description has been provided for this image

Here you'll notice that the query returns polygons (lakes/ponds) and linestrings (streams/creeks). This will complicate things when we try to map them interactively later on with hvplot. We can separate out the lines from the polygons as outlined below for easier plotting.

In [22]:
# Splitting water into lines and polygons for easier plotting 
water_polygons = campus_water[
    campus_water.geometry.geom_type.isin(
        ["Polygon", "MultiPolygon"])]

water_polygons


water_lines = campus_water[
    campus_water.geometry.geom_type.isin(
        ["LineString", "MultiLineString"])]

water_lines
Out[22]:
nodes ele gnis:feature_id name natural short_name water geometry description intermittent waterway source usage
element_type osmid
way 61758946 [434098986, 434098987, 434098988, 434098989, 4... NaN NaN Boulder Creek NaN NaN NaN LINESTRING (-105.29443 40.01364, -105.29417 40... NaN no stream NaN NaN
194217260 [2047352879, 2047352903, 2047352859, 204735286... NaN NaN NaN NaN NaN NaN LINESTRING (-105.26105 40.00291, -105.26101 40... NaN NaN drain bing NaN
482560081 [4753724705, 4753724706] NaN NaN NaN NaN NaN NaN LINESTRING (-105.25917 40.00785, -105.25913 40... NaN yes canal NaN spillway
888538283 [8260987529, 8260987530] NaN NaN NaN NaN NaN NaN LINESTRING (-105.26306 40.00090, -105.26300 40... NaN NaN drain NaN NaN
In [26]:
# Clip water lines to campus boundary
water_lines_clipped = gpd.clip(
    water_lines,
    campus_boundary
)

water_lines_clipped
Out[26]:
nodes ele gnis:feature_id name natural short_name water geometry description intermittent waterway source usage
element_type osmid
way 888538283 [8260987529, 8260987530] NaN NaN NaN NaN NaN NaN LINESTRING (-105.26306 40.00090, -105.26300 40... NaN NaN drain NaN NaN
194217260 [2047352879, 2047352903, 2047352859, 204735286... NaN NaN NaN NaN NaN NaN LINESTRING (-105.26105 40.00291, -105.26101 40... NaN NaN drain bing NaN
482560081 [4753724705, 4753724706] NaN NaN NaN NaN NaN NaN LINESTRING (-105.25917 40.00785, -105.25913 40... NaN yes canal NaN spillway
61758946 [434098986, 434098987, 434098988, 434098989, 4... NaN NaN Boulder Creek NaN NaN NaN LINESTRING (-105.27247 40.01180, -105.27216 40... NaN no stream NaN NaN

Let's try adding the roads that fall within the campus boundary to this map.

In [30]:
# Search for everthing tagged as 'highway' within the specified boundary
campus_roads = osm.features_from_polygon(
    campus_polygon,
    tags={"highway": True}
)

campus_roads
Out[30]:
crossing crossing:markings highway geometry traffic_signals button_operated crossing:signals traffic_signals:direction direction tactile_paving ... area informal smoothness cycleway:right:buffer covered loc_name junction cutting source:width traffic_calming
element_type osmid
node 176407697 uncontrolled yes crossing POINT (-105.26847 40.00650) NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
176526906 uncontrolled zebra crossing POINT (-105.27277 40.00943) NaN NaN no NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
176565498 zebra NaN crossing POINT (-105.26954 40.00788) NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
176567142 NaN NaN stop POINT (-105.26485 40.00331) NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
176567160 NaN NaN give_way POINT (-105.26401 40.00159) NaN NaN NaN NaN backward NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
way 1476032523 NaN NaN tertiary LINESTRING (-105.27277 40.00943, -105.27307 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1476932185 NaN NaN cycleway LINESTRING (-105.26038 40.00059, -105.26039 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN table
1476932544 uncontrolled surface cycleway LINESTRING (-105.26949 40.00455, -105.26953 40... NaN NaN no NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1476932545 NaN NaN cycleway LINESTRING (-105.26956 40.00454, -105.26960 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1506480041 NaN NaN path LINESTRING (-105.26732 40.01184, -105.26729 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1438 rows × 113 columns

In [31]:
# Create simple plot with .plot()
campus_roads.plot()
Out[31]:
<Axes: >
No description has been provided for this image

Again, like with the water data, you'll notice there are lines and points in this roads data. They can be plotted together using a .plot() function as above, but will need to be separated by geometry if we want to plot them together interactively using hvplot. We'll separate out the linestrings below and use those for interactive plotting.

In [40]:
# Select the road objects represented with LineString/MultiLineString
road_lines = campus_roads[
    campus_roads.geometry.geom_type.isin(
        ["LineString", "MultiLineString"])]

road_lines
Out[40]:
crossing crossing:markings highway geometry traffic_signals button_operated crossing:signals traffic_signals:direction direction tactile_paving ... area informal smoothness cycleway:right:buffer covered loc_name junction cutting source:width traffic_calming
element_type osmid
way 4325623 NaN NaN service LINESTRING (-105.26497 40.00734, -105.26519 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8051748 NaN NaN service LINESTRING (-105.26296 40.00331, -105.26287 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
17018618 NaN NaN service LINESTRING (-105.26862 40.00495, -105.26855 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
17019971 NaN NaN service LINESTRING (-105.26879 40.00987, -105.26868 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
17020517 NaN NaN tertiary LINESTRING (-105.26136 40.00735, -105.26136 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1476032523 NaN NaN tertiary LINESTRING (-105.27277 40.00943, -105.27307 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1476932185 NaN NaN cycleway LINESTRING (-105.26038 40.00059, -105.26039 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN table
1476932544 uncontrolled surface cycleway LINESTRING (-105.26949 40.00455, -105.26953 40... NaN NaN no NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1476932545 NaN NaN cycleway LINESTRING (-105.26956 40.00454, -105.26960 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1506480041 NaN NaN path LINESTRING (-105.26732 40.01184, -105.26729 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1241 rows × 113 columns

In [41]:
# Clip road lines to campus boundary
road_lines_clipped = gpd.clip(
    road_lines,
    campus_boundary
)

road_lines_clipped
Out[41]:
crossing crossing:markings highway geometry traffic_signals button_operated crossing:signals traffic_signals:direction direction tactile_paving ... area informal smoothness cycleway:right:buffer covered loc_name junction cutting source:width traffic_calming
element_type osmid
way 194216335 NaN NaN cycleway LINESTRING (-105.26260 40.00053, -105.26251 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
33969475 NaN NaN path LINESTRING (-105.26324 40.00060, -105.26335 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
194216330 NaN NaN cycleway LINESTRING (-105.26260 40.00053, -105.26273 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
216703143 NaN NaN footway LINESTRING (-105.26294 40.00078, -105.26282 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
878796413 NaN NaN service LINESTRING (-105.26315 40.00093, -105.26313 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
767654726 NaN NaN footway LINESTRING (-105.26769 40.01340, -105.26761 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006625 NaN NaN footway LINESTRING (-105.26850 40.01316, -105.26856 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
767654728 NaN NaN footway LINESTRING (-105.26894 40.01334, -105.26896 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006627 NaN NaN footway LINESTRING (-105.26851 40.01361, -105.26851 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006617 NaN NaN footway LINESTRING (-105.26836 40.01334, -105.26837 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1241 rows × 113 columns

In [45]:
# Select the clipped road objects represented with LineString/MultiLineString
clipped_road_lines = road_lines_clipped[
    road_lines_clipped.geometry.geom_type.isin(
        ["LineString", "MultiLineString"])]

clipped_road_lines
Out[45]:
crossing crossing:markings highway geometry traffic_signals button_operated crossing:signals traffic_signals:direction direction tactile_paving ... area informal smoothness cycleway:right:buffer covered loc_name junction cutting source:width traffic_calming
element_type osmid
way 194216335 NaN NaN cycleway LINESTRING (-105.26260 40.00053, -105.26251 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
33969475 NaN NaN path LINESTRING (-105.26324 40.00060, -105.26335 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
194216330 NaN NaN cycleway LINESTRING (-105.26260 40.00053, -105.26273 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
216703143 NaN NaN footway LINESTRING (-105.26294 40.00078, -105.26282 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
878796413 NaN NaN service LINESTRING (-105.26315 40.00093, -105.26313 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
767654726 NaN NaN footway LINESTRING (-105.26769 40.01340, -105.26761 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006625 NaN NaN footway LINESTRING (-105.26850 40.01316, -105.26856 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
767654728 NaN NaN footway LINESTRING (-105.26894 40.01334, -105.26896 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006627 NaN NaN footway LINESTRING (-105.26851 40.01361, -105.26851 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
214006617 NaN NaN footway LINESTRING (-105.26836 40.01334, -105.26837 40... NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1239 rows × 113 columns

In [51]:
# Create an interactive map of the boundary and buildings
building_layer = campus_buildings.hvplot(
    geo=True,
    tiles="EsriImagery",
    line_color="black",
    fill_color="yellow",
    alpha=0.9,
    width=850,
    height=650,
    title="CU Boulder (Main Campus)"
)

road_line_layer = clipped_road_lines.hvplot(
    geo=True,
    line_color="grey",
    line_width=1
)

boundary_layer = campus_boundary.hvplot(
    geo=True,
    line_color="yellow",
    fill_color="yellow",
    fill_alpha=0.2,
    line_width=4
)

water_boundary_layer = water_polygons.hvplot(
    geo=True,
    line_color="blue",
    line_width=1.5,
    fill_color="lightblue",
    fill_alpha=0.8
)

water_line_layer = water_lines_clipped.hvplot(
    geo=True,
    line_color="blue",
    line_width=2
)

building_layer * road_line_layer * boundary_layer * water_boundary_layer * water_line_layer
Out[51]:

Why Use an OSM Object ID?

Advantages:

  • avoids geocoding problems
  • uses an exact OSM boundary
  • highly reproducible
  • common in GIS and research workflows

This workflow is especially useful when:

  • place names fail
  • campuses have multiple boundaries
  • you want consistent study areas across analyses

Now it is your turn¶

You have seen 4 different approaches to querying OSM for information about CU Boulder through seaching by address, point, place, and polygon. Now it is your turn to try this workflow out for a campus you are intersted in. Try adding buildings, highways, waterways, parks, etc. if you are feeling creative. Try adding more code and markdown cells below to get started creating your campus maps, and have some fun!

In [ ]:
%%capture
%%bash
jupyter nbconvert new-map-workflow.ipynb --to html