Madison restaurants and their cuisine
For day 1 of #30DayMapChallenge, here’s a quick points map of restaurants in and around Madison and their cuisine.
The data come from OpenStreetMap, via the Overpass API, via the osmdata package. As evidenced by the many NA values, there’s clearly room for improving the completeness of the cuisine tag in OpenStreetMap…
plces <- tigris::places(state = "Wisconsin")
| | | 0% | | | 1% | |= | 1% | |= | 2% | |== | 3% | |== | 4% | |=== | 4% | |=== | 5% | |=== | 6% | |==== | 6% | |==== | 7% | |===== | 8% | |===== | 9% | |====== | 9% | |====== | 10% | |====== | 11% | |======= | 11% | |======= | 12% | |======== | 13% | |======== | 14% | |========= | 15% | |========= | 16% | |========== | 16% | |========== | 17% | |=========== | 18% | |=========== | 19% | |============ | 19% | |============ | 20% | |============ | 21% | |============= | 21% | |============= | 22% | |============== | 23% | |============== | 24% | |=============== | 24% | |=============== | 25% | |================ | 26% | |================ | 27% | |================= | 28% | |================= | 29% | |================== | 29% | |================== | 30% | |================== | 31% | |=================== | 31% | |=================== | 32% | |==================== | 33% | |==================== | 34% | |===================== | 34% | |===================== | 35% | |===================== | 36% | |====================== | 36% | |====================== | 37% | |======================= | 38% | |======================= | 39% | |======================== | 40% | |========================= | 41% | |========================= | 42% | |========================== | 43% | |========================== | 44% | |=========================== | 44% | |=========================== | 45% | |=========================== | 46% | |============================ | 46% | |============================ | 47% | |============================= | 48% | |============================= | 49% | |============================== | 49% | |============================== | 50% | |=============================== | 51% | |=============================== | 52% | |================================ | 53% | |================================ | 54% | |================================= | 55% | |================================= | 56% | |================================== | 56% | |================================== | 57% | |=================================== | 58% | |=================================== | 59% | |==================================== | 59% | |==================================== | 60% | |==================================== | 61% | |===================================== | 61% | |===================================== | 62% | |====================================== | 63% | |====================================== | 64% | |======================================= | 65% | |======================================== | 66% | |======================================== | 67% | |========================================= | 68% | |========================================= | 69% | |========================================== | 69% | |========================================== | 70% | |========================================== | 71% | |=========================================== | 71% | |=========================================== | 72% | |============================================ | 73% | |============================================ | 74% | |============================================= | 74% | |============================================= | 75% | |============================================= | 76% | |============================================== | 76% | |============================================== | 77% | |=============================================== | 78% | |=============================================== | 79% | |================================================ | 80% | |================================================ | 81% | |================================================= | 81% | |================================================= | 82% | |================================================== | 83% | |================================================== | 84% | |=================================================== | 84% | |=================================================== | 85% | |=================================================== | 86% | |==================================================== | 86% | |==================================================== | 87% | |===================================================== | 88% | |===================================================== | 89% | |====================================================== | 89% | |====================================================== | 90% | |======================================================= | 91% | |======================================================= | 92% | |======================================================== | 93% | |======================================================== | 94% | |========================================================= | 95% | |========================================================= | 96% | |========================================================== | 96% | |========================================================== | 97% | |=========================================================== | 98% | |=========================================================== | 99% | |============================================================| 99% | |============================================================| 100%
madison <- plces |>
filter(NAME == "Madison")
bbox <- st_bbox(madison)
restaurants <- opq(bbox = bbox) |>
add_osm_feature(key = "amenity", value = "restaurant") |>
osmdata_sf()
poly_rest <- restaurants$osm_polygons |>
st_centroid() |>
select(name, cuisine)
point_rest <- restaurants$osm_points |> select(name, cuisine)
all_rest <- rbind(poly_rest, point_rest) |>
filter(!is.na(name)) |>
mutate(cuisine = fct_lump_n(cuisine, 10),
cuisine = fct_infreq(cuisine))
counts <- all_rest |>
st_drop_geometry() |>
count(cuisine)
all_rest <- all_rest |> left_join(counts) |>
mutate(
cuisine_with_count = paste0(cuisine, " (", n, ")"),
cuisine_with_count = fct_infreq(cuisine_with_count)
)
tm_basemap() +
tm_shape(all_rest) +
tm_dots(fill = "cuisine_with_count", fill.legend = tm_legend(title = "Cuisine")) +
tm_credits("Data: OpenStreetMap (https://openstreetmap.org/copyright)") +
tm_title("Restaurants in Madison", position = tm_pos_in("center"))
For attribution, please cite this work as
Kliems (2025, Nov. 1). Harald Kliems: #30 Day Map Challenge: Points. Retrieved from https://haraldkliems.netlify.app/posts/2025-11-01-30-day-map-challenge-points/
BibTeX citation
@misc{kliems2025#30,
author = {Kliems, Harald},
title = {Harald Kliems: #30 Day Map Challenge: Points},
url = {https://haraldkliems.netlify.app/posts/2025-11-01-30-day-map-challenge-points/},
year = {2025}
}