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 shared data 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 filterable table.
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

The interactive map centers on the Eiffel Tower in Paris. A tower icon marks the landmark, cup icons mark coffee shops and croissant icons mark bakeries. The table below lists place names, types, addresses, cities, websites and phone numbers. Search, filter or select rows to narrow the places shown on the map.

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

Attributions