Skip to contents

Build the interactive map

This example displays results from arcgeocoder on an interactive leaflet map.

crosstalk links the map to a filterable reactable table.

# Coffee shops and bakeries around the Eiffel Tower.

library(arcgeocoder)
library(leaflet)
library(dplyr)
library(reactable)
library(crosstalk)

# Step 1: Locate the Eiffel Tower.
eiffel_tower <- arc_geo_multi("Eiffel Tower",
  city = "Paris", countrycode = "FR",
  category = "POI"
)

# Base URL for icons.
icon_url <- paste0(
  "https://raw.githubusercontent.com/dieghernan/arcgeocoder/",
  "main/vignettes/articles/"
)

eiffel_icon <- makeIcon(
  iconUrl = paste0(icon_url, "eiffel-tower.png"),
  iconWidth = 50, iconHeight = 50,
  iconAnchorX = 25, iconAnchorY = 25
)

# Step 2: Find nearby coffee shops and bakeries.
cf_bk <- arc_geo_categories(
  category = c("Coffee Shop", "Bakery"),
  x = eiffel_tower$lon, y = eiffel_tower$lat,
  limit = 50,
  full_results = TRUE
)

# Create labels and icons.
labs <- paste0("<strong>", cf_bk$PlaceName, "</strong><br>", cf_bk$StAddr)

# Assign icons.
leaf_icons <- icons(
  ifelse(cf_bk$Type == "Coffee Shop",
    paste0(icon_url, "coffee-cup.png"),
    paste0(icon_url, "croissant.png")
  ),
  iconWidth = 20, iconHeight = 20,
  iconAnchorX = 10, iconAnchorY = 10
)

# Step 3: Create a crosstalk object.
cf_bk_data <- cf_bk |>
  select(Place = ShortLabel, Type, Address = Place_addr, City, URL, Phone) |>
  SharedData$new(group = "Food")

# Step 4: Create a leaflet map with crosstalk.
# Initialize the leaflet map.
lmend <- leaflet(
  data = cf_bk_data,
  elementId = "EiffelTower", width = "100%", height = "60vh",
  options = leafletOptions(minZoom = 12)
) |>
  setView(eiffel_tower$lon, eiffel_tower$lat, zoom = 16) |>
  addProviderTiles(
    provider = "CartoDB.Positron",
    group = "CartoDB.Positron"
  ) |>
  addTiles(group = "OSM") |>
  addMarkers(data = eiffel_tower, ~lon, ~lat, icon = eiffel_icon) |>
  addMarkers(
    lat = cf_bk$lat, lng = cf_bk$lon, popup = labs, icon = leaf_icons
  ) |>
  addLayersControl(
    baseGroups = c("CartoDB.Positron", "OSM"),
    position = "topleft",
    options = layersControlOptions(collapsed = FALSE)
  )

# Step 5: Create a reactable for filtering.
tb <- reactable(cf_bk_data,
  selection = "multiple",
  onClick = "select",
  rowStyle = list(cursor = "pointer"),
  filterable = TRUE,
  searchable = TRUE,
  showPageSizeOptions = TRUE,
  striped = TRUE,
  defaultColDef = colDef(vAlign = "center", minWidth = 150),
  paginationType = "jump",
  elementId = "coffees",
  columns = list(
    Place = colDef(
      sticky = "left", rowHeader = TRUE, name = "",
      cell = function(value) {
        htmltools::strong(value)
      }
    ),
    URL = colDef(cell = function(value) {
      # Render as a link.
      if (any(is.null(value), is.na(value))) {
        return("")
      }
      htmltools::a(href = value, target = "_blank", as.character(value))
    }),
    Phone = colDef(cell = function(value) {
      # Render as a link.
      if (any(is.null(value), is.na(value))) {
        return("")
      }
      clearphone <- gsub("-", "", value, fixed = TRUE)
      clearphone <- gsub(" ", "", clearphone, fixed = TRUE)
      htmltools::a(
        href = paste0("tel:", clearphone), target = "_blank",
        as.character(value)
      )
    })
  )
)

Display the widget

# Display all components.
htmltools::browsable(
  htmltools::tagList(lmend, tb)
)

Attributions