Skip to contents

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))