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.