Introduction
In this post I’ll show how you can use R and OpenStreetMap data to map the locations of all the ice-cream shops in a city! I’ll use the {osmdata} package ( et al. (2017)) to get the locations of ice-cream shops in Denver, and then plot them on a map.
Getting ice-cream locations from OpenStreetMap
Defining the Bounding box
First we need to define the area to search for our locations of interest. The {osmdata} package provides a function getbb() to return the bounding box for a city or location. The output can be returned in sf format to easily plot (figure Figure 1).
Code
# Make bounding box for Denver
denver_bb <-
getbb(place_name = "Denver,CO" ,
format_out = "sf_polygon")
leaflet() |>
addTiles() |>
addPolygons(data = denver_bb,
color = "black")Finding the ice cream
How do we find just ice-cream shops in the OpenStreetMap data? The list of available features and tags are available from the functions available_features() and available_tags(). In this case, the feature of interest is “amenity”, which has 137 tag options, including “ice_cream”.
Putting it all together
To get the ice-cream locations, we construct an overpass query object (opq):
First we specify the bounding box using the getbb() function.
Next we create an overpass query object with opq()
Then we specify which features we want with the add_osm_feature() function.
Finally we call the osmdata_sf() function to retrieve the data.
This returns an object of class osmdata which contains information about the API call as well as the results. The locations we are interested in will be in $osm_points .
Code
denver_ice_cream <-
getbb(place_name = "Denver, CO") |>
opq() |>
add_osm_feature(key = "amenity", value = "ice_cream") |>
osmdata_sf()
denver_ice_creamObject of class 'osmdata' with:
$bbox : 39.6143008,-105.1098845,39.9142087,-104.5996997
$overpass_call : The call submitted to the overpass API
$meta : metadata including timestamp and version numbers
$osm_points : 'sf' Simple Features Collection with 170 points
$osm_lines : NULL
$osm_polygons : 'sf' Simple Features Collection with 7 polygons
$osm_multilines : NULL
$osm_multipolygons : NULL
$osm_points is a simple feature collection; basically a data frame with geometry added. We can see there are a lot of features/variables, which are missing for most locations.
Rows: 92
Columns: 60
$ osm_id <chr> "631845194", "967331912", "1030196174", "15…
$ name <chr> "Ben & Jerry's", "Cold Stone Creamery", "Sw…
$ `addr:city` <chr> "Denver", NA, "Denver", "Lakewood", NA, "De…
$ `addr:housename` <chr> NA, NA, NA, NA, NA, NA, NA, "799", NA, NA, …
$ `addr:housenumber` <chr> "2339", NA, "52", "9910", NA, NA, NA, "799"…
$ `addr:postcode` <chr> "80210", NA, "80203", "80123", NA, "80238",…
$ `addr:state` <chr> "CO", NA, "CO", "CO", NA, "CO", "CO", "CO",…
$ `addr:street` <chr> "East Evans Avenue", NA, "Broadway", "West …
$ `addr:unit` <chr> NA, NA, NA, NA, NA, NA, NA, NA, "101", NA, …
$ air_conditioning <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ amenity <chr> "ice_cream", "ice_cream", "ice_cream", "ice…
$ brand <chr> "Ben & Jerry's", "Cold Stone Creamery", NA,…
$ `brand:website` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `brand:wikidata` <chr> "Q816412", "Q1094923", NA, "Q8054428", "Q10…
$ building <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ check_date <chr> NA, NA, NA, NA, NA, NA, NA, "2024-12-30", N…
$ `check_date:opening_hours` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ craft <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ cuisine <chr> "ice_cream", "ice_cream", NA, "frozen_yogur…
$ delivery <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ description <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ designation <chr> NA, NA, NA, "frozen yogurt", NA, NA, NA, NA…
$ `diet:halal` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `diet:kosher` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `diet:vegan` <chr> NA, NA, "yes", NA, NA, NA, NA, NA, NA, NA, …
$ `diet:vegetarian` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ door <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ drive_through <chr> NA, NA, NA, NA, NA, NA, NA, "no", NA, NA, N…
$ emergency <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ entrance <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ facebook <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `fire_hydrant:type` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ indoor <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ indoor_seating <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ level <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ mobile <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `name:en` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ note <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ old_name <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ opening_hours <chr> NA, NA, "Mo-Su 13:00-24:00", NA, NA, NA, NA…
$ outdoor_seating <chr> NA, NA, NA, "yes", NA, NA, NA, "yes", NA, N…
$ `outdoor_seating:comfort` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `payment:cash` <chr> NA, NA, NA, NA, NA, NA, NA, "yes", NA, NA, …
$ `payment:credit_cards` <chr> NA, NA, NA, NA, NA, NA, NA, "yes", NA, NA, …
$ `payment:debit_cards` <chr> NA, NA, NA, NA, NA, NA, NA, "yes", NA, NA, …
$ phone <chr> "+1-303-733-8878", NA, "+1-720-994-5203", "…
$ ref <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ shop <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ short_name <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ smoking <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ source <chr> NA, NA, NA, "ground_survey", "local_knowled…
$ start_date <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ takeaway <chr> NA, NA, NA, "yes", NA, NA, NA, NA, NA, NA, …
$ toilets <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ `toilets:access` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ weather_protection <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ website <chr> "https://www.benjerry.com/uofdenver", NA, "…
$ `website:menu` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ wheelchair <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ geometry <POINT [°]> POINT (-104.9597 39.67862), POINT (-1…
I’ll filter out the locations without names, and then plot the remaining locations on a leaflet map ( Figure 2).
You might notice that some of the locations returned are outside of the sf polygons returned by getbb() we looked at earlier (Figure 1). The {osmdata} query actually uses the rectangular bounding box that contains these polygons. I’ve plotted that box here also; the Denver airport (to the northeast) extends that box pretty far north.
Code
# filter out locations with NA names
denver_ice_cream_points <- denver_ice_cream$osm_points |>
filter(!is.na(name))
# get rectangular bounding box
bb = st_as_sfc(st_bbox(denver_bb))
leaflet() |>
addTiles() |>
addMarkers(data = denver_ice_cream_points,
popup = ~name) |>
addPolygons(data = denver_bb,
color = "black") |>
addPolygons(data = bb,
color = "gray",
fillColor = "transparent")What are the most common ice-cream shops? Looks like Coors Field has a lot of ice cream stands, and they are all treated as separate shops in the data. The second most-frequent shop is Cold Stone Creamery.
Code
denver_ice_cream_points |>
sf::st_drop_geometry() |>
count(name, sort = TRUE) |>
filter(n > 1) |>
DT::datatable()Summary
In this post I used the {osmdata} package to get the locations of ice-cream shops in Denver from OpenStreetMap, and plot them on a map. The same techniques could be used to locate, map, and analyze all kinds of different features from OpenStreetMap. I hope you found this informational and interesting!
SessionInfo
R version 4.4.1 (2024-06-14)
Platform: x86_64-apple-darwin20
Running under: macOS 15.6.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/Denver
tzcode source: internal
attached base packages:
[1] stats graphics grDevices datasets utils methods base
other attached packages:
[1] leaflet_2.2.3 osmdata_0.2.5 sf_1.0-21 dplyr_1.1.4
loaded via a namespace (and not attached):
[1] jsonlite_2.0.0 compiler_4.4.1 renv_1.0.9 tidyselect_1.2.1
[5] Rcpp_1.1.0 xml2_1.3.8 jquerylib_0.1.4 yaml_2.3.10
[9] fastmap_1.2.0 R6_2.6.1 generics_0.1.4 curl_6.4.0
[13] classInt_0.4-11 httr2_1.2.1 knitr_1.50 htmlwidgets_1.6.4
[17] tibble_3.3.0 units_0.8-7 lubridate_1.9.4 DBI_1.2.3
[21] bslib_0.9.0 pillar_1.11.0 rlang_1.1.6 DT_0.34.0
[25] cachem_1.1.0 xfun_0.52 sass_0.4.10 timechange_0.3.0
[29] cli_3.6.5 magrittr_2.0.4 class_7.3-23 crosstalk_1.2.2
[33] digest_0.6.37 grid_4.4.1 rstudioapi_0.17.1 rappdirs_0.3.3
[37] lifecycle_1.0.4 vctrs_0.6.5 KernSmooth_2.23-26 proxy_0.4-27
[41] evaluate_1.0.5 glue_1.8.0 e1071_1.7-16 rmarkdown_2.29
[45] tools_4.4.1 pkgconfig_2.0.3 htmltools_0.5.8.1