Purpose
The purpose of leaf.magic is to extend R
leaflet’s capability to use modern icon sets. While
leaflet::addAwesomeMarkers()
exists, it has a few core
disadvantages; markers are a fixed size, can only be one of 19 colours,
and pull icons from significantly out of date icon sets.
leaf.magic uses contemporary R packages - currently
fontawesome and bsicons - to access
up-to-date SVG icon sets, magick to knit them onto map
markers, and then leaflet::makeIcon()
to translate them
into a leaflet-usable map marker.
Some core advantages of magicIcons()
over
leaflet::awesomeIcons()
are:
Markers pull from up-to-date, larger icon sets.
Markers and icons can be any colour, and can be resized.
One disadvantage is that magicIcons()
may be
slower initially to create large numbers of different icons. However,
once a marker has been created in an R session, it will be significantly
quicker to redraw, closing the gap between awesomeIcons()
and magicIcons()
.
Example Data
In this document we’ll use the in-built port_talbot
dataset, which details the location of some air quality measurement
stations around the town of Port Talbot, Wales, UK, some of which are
open and some of which have closed.
We can give this data a look using vanilla leaflet:
port_talbot$open_year <- as.integer(format(port_talbot$start_date, "%Y"))
port_talbot$popup <-
paste0(
"<strong>",
toupper(port_talbot$site),
"</strong> (",
port_talbot$code,
")<hr>Site Type: ",
port_talbot$site_type,
"<br>Opened: ",
port_talbot$open_year
)
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(popup = ~popup)
Simple Example
We can use the magicIcons()
function to swap out the
default markers for some leaf.magic markers. The
important argument here is icon
, which is the (in this
case) Font Awesome icon of interest1.
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = magicIcons(
icon = "cloud"
)
)
Varying Icons
leaf.magic allows you to vary icons by some other
variable. You can do this manually, or use one of
leaf.magic constructor functions like
iconFactor()
.
site_types <- c("Urban Industrial", "Urban Background", "Urban Traffic")
iconPal <- iconFactor(
icons = c("industry", "house", "car"),
domain = site_types
)
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPal(site_type)
)
)
Note that there’s also iconBin()
and
iconQuantile()
which can help map icons to numeric data. In
this case, it makes most sense to choose icons that have some inherent
“order” to them.
These functions don’t have all of the features of
colorBin()
and colorQuantile()
, in part due to
the differences between mapping a continuous aesthetic like colour and a
discrete aesthetic like icons. A nice feature is that these are very
much icon-led - the breaks
and n
arguments
default to the length of the provided icons
, so you don’t
need to specify them if you’re happy letting the functions decide the
break-points/probs
for you.
iconPalYr <- iconQuantile(
icons = c("hourglass-start", "hourglass-half", "hourglass-end"),
domain = port_talbot$open_year
)
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPalYr(open_year)
)
)
Varying Colours
magicIcons()
has two arguments related to colour;
markerColor
which colours the tear-drop-shaped marker, and
iconColor
which colours the icon. Any hex-code can be
supplied to these arguments - either a constant value or colour values
mapped to a column using leaflet::colorFactor()
.
catPal <- colorFactor(c("#12436D", "#28A197", "#801650"), port_talbot$site_type)
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPal(site_type),
markerColor = catPal(site_type),
iconColor = "#F7F7F7FF"
)
) %>%
addLegend(
pal = catPal,
values = port_talbot$site_type,
title = "Site Type"
)
As the markers themselves can be set to any colour, we can
even use a continuous scale (e.g., from
leaflet::colorNumeric()
).
palNum <- colorNumeric("viridis", domain = port_talbot$open_year)
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPalYr(open_year),
markerColor = palNum(open_year),
iconColor = "white"
)
) %>%
addLegend(
pal = palNum,
values = port_talbot$open_year,
title = "Opening Year",
labFormat = labelFormat(big.mark = "")
)
Varying Size
Unlike leaflet::awesomeIcons()
,
magicIcons()
can be easily re-sized. Much like in
leaflet::addCircleMarkers()
, this can vary with another
variable; let’s make the closed sites a bit smaller than the open ones
with markerSize
. Note the default is 30L
,
which is roughly the same size as the default
leaflet::addMarkers()
marker.
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPal(site_type),
iconColor = "white",
markerSize = ifelse(open, 30L, 20L),
markerColor = catPal(site_type)
)
) %>%
addLegend(
pal = catPal,
values = port_talbot$site_type,
title = "Site Type"
)
Icon Legends
leaf.magic provides the addIconLegend()
function to help construct an icon legend, similar to
leaflet::addLegend()
constructs colour legends.
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPal(site_type),
markerColor = palNum(open_year),
iconColor = "white"
)
) %>%
addLegend(
pal = palNum,
values = port_talbot$open_year,
title = "Opening Year",
labFormat = labelFormat(big.mark = "")
) %>%
addIconLegend(
icons = c("industry", "car", "house"),
labels = site_types,
title = "Site Type"
)
Often, the colour of the marker (or icon) and the icon itself will
align. In that case, the colors
argument of
addIconLegend()
can be used to create an efficient,
combined legend.
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
popup = ~popup,
icon = ~ magicIcons(
icon = iconPal(site_type),
iconColor = "white",
markerColor = catPal(site_type)
)
) %>%
addIconLegend(
icons = iconPal(site_types),
labels = site_types,
colors = catPal(site_types),
title = "Site Type"
)
Alternative Marker Types
While the default marker type, the “tear-drop” marker, is useful in
most instances, you may find other markers types of interest. These
include circle, square, diamond, heart, and star-shaped markers.
Additionally, users can specify "none"
and remove the
marker entirely, placing the icon directly on the map itself.
addQuickMarker <- function(map, marker) {
iconColor <- "white"
markerColor <- catPal(port_talbot$site_type)
if (marker == "none") {
iconColor <- markerColor
}
map %>%
addMarkers(
popup = ~ popup,
icon = ~ magicIcons(
icon = iconPal(site_type),
iconColor = iconColor,
markerColor = markerColor,
marker = marker
),
group = marker
)
}
leaflet(port_talbot) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addQuickMarker("circle") %>%
addQuickMarker("square") %>%
addQuickMarker("diamond") %>%
addQuickMarker("heart") %>%
addQuickMarker("star") %>%
addQuickMarker("none") %>%
addLayersControl(
baseGroups = c("circle", "square", "diamond", "heart", "star", "none"),
options = layersControlOptions(F)
) %>%
addIconLegend(
position = "bottomleft",
icons = iconPal(site_types),
labels = site_types,
colors = catPal(site_types),
title = "Site Type"
)
Note that marker
can vary with some variable, allowing
the marker style itself to encode extra information.
markPal <- iconFactor(c("circle", "diamond", "square"), site_types)
leaflet(port_talbot) |>
addProviderTiles(providers$CartoDB.Positron) |>
addMarkers(icon = ~ magicIcons(
marker = markPal(site_type),
markerSize = ifelse(markPal(site_type) == "diamond", 35, 30)
)) |>
addIconLegend(
title = "Site Type",
icons = c("fas fa-circle", "fas fa-diamond", "fas fa-square"),
site_types
)
Other Utilities
leaf.magic exports the awesomePalette
list, which are hex codes for the colours used in
leaflet::awesomeIcons()
. You could use these if you want to
recreate the colour scheme of awesomeIcons()
with the
flexibility of magicIcons()
.
cols <- names(awesomePalette)[names(awesomePalette) != "white"]
breweries91$facolor <- sample(cols, nrow(breweries91), replace = TRUE)
breweries91$hexcolor <- unlist(use.names = FALSE, awesomePalette[breweries91$facolor])
leaflet(breweries91) %>%
addTiles() %>%
addAwesomeMarkers(
icon = ~ awesomeIcons(
icon = "circle",
markerColor = facolor,
iconColor = "#FFFFFF",
library = "fa"
),
group = "Awesome"
) %>%
addMarkers(
icon = ~ magicIcons(
icon = "fas fa-circle",
markerColor = hexcolor,
iconColor = "#FFFFFF"
),
group = "Magic"
) %>%
addLayersControl(
baseGroups = c("Awesome", "Magic")
)
If you use leaf.magic, you may want to add an
attribution to Font Awesome or one of the other icon providers.
addIconAttribution()
is a convenient way to do this, and
even comes with its own group
and layerId
options for precise “layer” control. Try toggling the breweries on and
off in the example below.
leaflet(breweries91) |>
addTiles() |>
addMarkers(icon = magicIcons("beer", "red", "white"), group = "Breweries") |>
addIconAttribution(group = "Breweries") |>
addLayersControl(overlayGroups = "Breweries",
options = leaflet::layersControlOptions(FALSE))