The leaflet.js JavaScript library, as extended by {leaflet} package, is a powerful tool for visualizing spatial data in R. On the other hand the combination of R and JavaScript worlds creates complexities, some of them with not immediately obvious solutions.

In this post I present several approaches to plotting spatial point data in a visually pleasing and convincing way.

I am demonstrating the approach on a dataset of headquarters of four technological companies, colloquially abbreviated as FANGs (Facebook, Apple, Netflix and Google) – but the techniques are universal and can be easily adapted to any point data.

library(leaflet)
library(dplyr)
library(sf)

# dramatis personae: Company HQs as a {sf} data frame
fangs <- data.frame(
  ticker = c("FB", "AAPL", "NFLX", "GOOG"),
  address = c(
    "1 Hacker Way Menlo Park, CA 94025",
    "One Apple Park Way Cupertino, CA 95014",
    "100 Winchester Cir. Los Gatos, CA 95032",
    "1600 Amphitheatre Pkwy. Mountain View, CA 94043"
  )
) %>%
  tidygeocoder::geocode(address = address,
                        method = "osm") %>%
  sf::st_as_sf(coords = c("long", "lat"), crs = 4326) 

The initial step is geocoding the addresses; for this purpose I am using the highly recommended {tidygeocoder} package by Jesse Cambon. I have found the OSM Nominatim backend very reliable.

The geocoded data frame is then converted to {sf} package format, allowing for more concise leaflet calls (there will be no need to specify lon/lat columns).

The first map is drawn with plain vanilla markers – at only three lines of code this is a not great, not terrible approach. To personalize it somewhat I am including the HQ address as a point label (note the tilde notation, required by {leaflet}).

leaflet(fangs) %>%
  addMarkers(label = ~ address) %>%
  addProviderTiles("CartoDB.Positron")

There are cases when the inverted teardrop of Leaflet marker is not desirable; in such situation a call of Circle Markers may be a better approach.

This is a common use case when dealing with very numerous points – circles can be scaled down much more easily than the teardrops.

With regards to Circle Markers I have found it preferable to override the default behavior of stroke (border color) and opacity (fill intensity).

leaflet(fangs) %>%
  addCircleMarkers(
    label = ~ address,
    fillColor = "goldenrod",
    fillOpacity = 1,
    stroke = F
  ) %>%
  addProviderTiles("CartoDB.Positron")

The classical leaflet markers with a little white dot may seem a bit overused on today’s web.

They can be made to look a bit more interesting by using a Font Awesome icon in place of the white dot. A number of icons is supported, in this case I am choosing the fire icon.

Note that I am on purpose using a single marker, with the same icon, for all HQ locations.

# a single icon is declared
awesome <- makeAwesomeIcon(
  icon = "fire",
  iconColor = "black",
  markerColor = "blue",
  library = "fa"
)

leaflet(fangs) %>%
  addAwesomeMarkers(icon = awesome,
                    label = ~ address) %>%
  addProviderTiles("CartoDB.Positron")

A logical next step is to personalize the markers and icons of each location; such personalization is possible with regards to both icon and marker color.

To achieve this a named list (called logos in my case) of Awesome Icons is created first. In the main leaflet call the actual icon shown is looked up based on a key value (ticker code) stored in fangs$ticker column. Again, note the tilde notation.

Font Awesome supports logos of Facebook, Apple and Google. For Netflix, the smallest of the lot, I had to make do with a TV.

# named list of awesome icons
logos <- awesomeIconList(
  "FB" = makeAwesomeIcon(
    icon = "facebook",
    markerColor = "blue",
    library = "fa"
  ),
  "AAPL" = makeAwesomeIcon(
    icon = "apple",
    markerColor = "green",
    library = "fa"
  ),
  "NFLX" = makeAwesomeIcon(
    icon = "tv",
    markerColor = "red",
    library = "fa"
  ),
  "GOOG" = makeAwesomeIcon(
    icon = "google",
    markerColor = "orange",
    library = "fa"
  )
)

leaflet(fangs) %>%
  addAwesomeMarkers(icon = ~ logos[ticker], # lookup from list based on ticker
                    label = ~ address) %>%
  addProviderTiles("CartoDB.Positron")

The last step is representing each spatial point / company headquarters by a free format image. I am choosing a website favicon of the respective company, rescaled to 25×25 pixels.

This again requires setting up a named list of favicons first, with URLs to individual homepage icons. This lookup list is then used in the main leaflet call to refer to image to be displayed.

# a named list of rescaled icons with links to images
favicons <- iconList(
  "FB" = makeIcon(
    iconUrl = "https://www.facebook.com/favicon.ico",
    iconWidth = 25,
    iconHeight = 25
  ),
  "AAPL" = makeIcon(
    iconUrl = "https://www.apple.com/favicon.ico",
    iconWidth = 25,
    iconHeight = 25
  ),
  "NFLX" = makeIcon(
    iconUrl = "https://www.netflix.com/favicon.ico",
    iconWidth = 25,
    iconHeight = 25
  ),
  "GOOG" = makeIcon(
    iconUrl = "https://www.google.com/favicon.ico",
    iconWidth = 25,
    iconHeight = 25
  )
)

leaflet(fangs) %>%
  addMarkers(icon = ~ favicons[ticker], # lookup based on ticker
             label = ~ address) %>%
  addProviderTiles("CartoDB.Positron")

I believe I have in my short example shown both the ease, and the potential, of visualizing point data in Leaflet.

For more information consider my more detailed Leaflet in R walkthtough.