For this exercise I created an interactive map to compare local venues for the spookiest, beer-infused Halloween weekend.
I collected input from a local Facebook group using a Google Form, checked out spots in person, and compiled a list of ratings for each venue based on: overall spook-factor, opportunities for Instagram-worthy photos, food and drink options, safety, and walkability. I assigned weighted scores to each venue (based on a top-secret algorithm), made filters to explore different categories and created a proposed bar-hop route based on the top 5 rated venues.
Toggle through menus and click on pins for info at each venue!
For the proposed route, there were lots of rabbit holes I started to go down and decided not to pursue for the sake of time management and getting lost in Stack Overflow message boards or asking ChatGPT for help. But I think a fun Friday night based on this brief search would start at Mr. Billy’s Bar and Grill on Dover Rd for a pre-game, followed by Shelby’s Trio for a great view and a more cocktail-party style Halloween party, then Calle Taco for some mid-evening tacos & margs, hitting up Blackhorse for beers and a DJ, and ending the night at Electric Cowboy to do the Monster Mash and potentially win $500 for the best costume!
The second one I used myself to visit bars and field-verify latitudes
and longitudes I a prior collected from Google Maps. (I did not visit
all venues, I visited ~10 that were closest to my house).
I linked the Forms to Google Sheets and exported them as .csv files
to use in R. Additionally, I later decided to add event links for the
venues with Halloween-themed events so I created a .csv with the venue
lat/longs, ID, and the link.
First I weighted each of the “ratings” questions and then calculated the weighted sum for each venue. I then merged this data frame with my lat/long dataframe and grouped the data by unique venue IDs.
# calculating overall score
scores <- scores %>%
mutate(
# rating weights for overall score
w.spook = 0.25,
w.vibes = 0.25,
w.drink = 0.20,
w.food = 0.15,
w.walk = 0.10,
w.safety = 0.05,
# weighted sum of ratings
num =
w.spook*spook.factor +
w.vibes*vibes +
w.drink*coalesce(drink.rating,0) + # coalesce replaces NA's with 0's
w.food*coalesce(food.rating, 0) +
w.walk*walkability +
w.safety*safety,
# the sum of all weights that were actually used (excludes food rating for bars that don't serve food or fields that were left blank)
den =
w.spook*(!is.na(spook.factor)) +
w.vibes*(!is.na(vibes)) +
w.drink*(!is.na(drink.rating)) +
w.food*(!is.na(food.rating)) +
w.walk*(!is.na(walkability)) +
w.safety*(!is.na(safety)),
# overall score
overall.score = round(num/den, 2)) %>%
select(-w.spook, -w.vibes, -w.drink, -w.food, -w.walk, -w.safety, -num, -den)
Next I filtered out the top 10 venues and made a line for
proposed route between the top 5 venues.
# create top 10 list + row.id for label in leaflet
top.bars <- bars.df %>%
filter(overall.score > 3.1) %>%
ungroup() %>%
mutate(row.id = row_number())
# create top 5 list to make a route between them
top <- top.bars %>%
slice(1:5)
# set a route order to draw the lines
top$route.order <- c(2,5,4,1,3)
# convert to shapefile
top.sf <- st_as_sf(top, coords = c("longitude", "latitude"), crs = 4326)
# create a line
route.line <- top.sf %>%
arrange(route.order) %>%
summarise(do_union = FALSE) %>%
st_cast("LINESTRING")
For the final step before I wrote the leaflet map I designated
some custom icons for the different categories (toggle menus), which I
sourced from Flaticon.com.
halloween.icon <- makeIcon(iconUrl = "images/pin.png", iconWidth = 30, iconHeight = 30)
bar.icon <- makeIcon(iconUrl = "images/beer.png",iconWidth = 30, iconHeight = 30)
food.icon <- makeIcon(iconUrl = "images/food.png",iconWidth = 30, iconHeight = 30)
deals.icon <- makeIcon(iconUrl = "images/deals.png",iconWidth = 30, iconHeight = 30)
star.icon <- makeIcon(iconUrl = "images/star.png",iconWidth = 30, iconHeight = 30)
And finally I wrote the giant leaflet code:
map <-
leaflet(bars.df) %>%
addTiles() %>%
addProviderTiles(providers$CartoDB.DarkMatter, group = "Dark") %>%
addProviderTiles(providers$CartoDB.Positron, group = "Light") %>%
addMarkers(data = bars.df,
lng = bars.df$longitude,
lat = bars.df$latitude,
icon = bar.icon,
label = ~bars.df$venue.id,
popup = ~paste0(
"<b>", bars.df$venue.id, "</b><br>",
"<b>★ ", bars.df$overall.score, "</b><br>",
"Spook Factor: ★ ", round(bars.df$spook.factor,2), "<br>",
"Aesthetic: ★ ", round(bars.df$vibes,2), "<br>",
"Drinks: ★ ", round(bars.df$drink.rating,2), "<br>",
"Food: ★ ", round(bars.df$food.rating,2), "<br>",
"Walkability: ★ ", round(bars.df$walkability,2), "<br>",
"Safety: ★ ", round(bars.df$safety,2), "<br>"),
group = "All Venues") %>%
addMarkers(data = subset(bars.df, halloween.events == "Yes"),
lng = ~longitude,
lat = ~latitude,
icon = halloween.icon,
label = ~venue.id,
popup = ~paste0(
"<b>", venue.id, "</b><br>",
"Spook Factor: ★ ", round(spook.factor,2), "<br>",
"<a href='", event.links$event.link, "' target='_blank'>Visit Event Page</a>"),
group = "Halloween Events") %>%
addMarkers(data = subset(bars.df, food == "Yes"),
lng = ~longitude,
lat = ~latitude,
icon = food.icon,
label = ~venue.id,
popup = ~paste0(
"<b>", venue.id, "</b><br>",
"Food: ★ ", round(food.rating,2), "<br>"),
group = "Food") %>%
addMarkers(data = subset(bars.df, deals == "Yes"),
lng = ~longitude,
lat = ~latitude,
icon = deals.icon,
label = ~venue.id,
popup = ~paste0(
"<b>", venue.id, "</b><br>",
"Drinks: ★ ", round(drink.rating,2), "<br>",
"Happy Hour/Event Deals"),
group = "Deals") %>%
addMarkers(data = top.bars,
lng = top.bars$longitude,
lat = top.bars$latitude,
icon = star.icon,
label = ~top.bars$venue.id,
popup = ~paste0(
"<b>",top.bars$row.id, ". ", top.bars$venue.id, "</b><br>",
"★ ", round(top.bars$overall.score,2), "<br>"),
group = "Top 10 Venues") %>%
addPolylines(data = route.line,
color = "orange",
weight = 2.5,
opacity = 0.8,
dashArray = "10,6",
group = "Proposed Route") %>%
addMarkers(data = top,
lng = ~longitude,
lat = ~latitude,
icon = star.icon,
popup = ~paste0(
"<b>", top$route.order, ". ", venue.id, "</b><br>",
"★ ", round(overall.score,2), "<br>"),
group = "Proposed Route") %>%
addLayersControl(
baseGroups = c("Dark","Light"),
options = layersControlOptions(collapsed = FALSE),
overlayGroups = c("All Venues", "Halloween Events", "Food", "Deals", "Top 10 Venues", "Proposed Route")) %>%
hideGroup(c("Halloween Events", "Food", "Deals", "Top 10 Venues", "Proposed Route")) %>%
setView(lng = -87.356249, lat = 36.527874, zoom = 12) %>%
addFullscreenControl(position = "topleft") %>%
addScaleBar()