Skip to main content
  1. Posts/

Enhancing Map Visualizations in Leaflet with Custom Markers and Colors in R

·939 words·5 mins
Leaflet Icons Maps

Leaflet is one of the best open-source JavaScript libraries for map visualizations, and its integration with R is excellent; however, only one minor detail bordering me, the limitation of 19 colours for the markers, besides the exhaustive way to map each value to each icon and colour.

Not everything can be expressed in light red, red and dark red; sometimes, you need something in between.

To solve this little problem, I created a new function called addCustomMakers, which provides a different way to create markers on maps and gives more flexibility for the icons and colours.

Using my addCustomMakers, you need to add to your data.frame if you wish 7 columns, besides the must-have lat and lon, which are

  • iconHeight and iconWidth,
  • icon from Bootstrap Icon or FontAwesome
  • group the grouping variable, in the following examples would be the property_type, house, duplex, condo etc…
  • fill a color which could be a hex
  • source fontawesome or bootstrap libraries
  • solid if source font awesome, solid TRUE or FALSE; if FALSE, then it uses the regular version; otherwise, the solid version.

The key points are:

  • you can switch between libraries, i.e., fontawesome or bootstrap icon, per row
  • you have your icons on the map instead of markers with the icons inside
  • you have the flexibility to choose any colour you want, which brings us to use palettes already made, like the Wes Anderson.

Let me show you. First let’s start the way available in the leaflet package using the dataset from my package (centris_ca).

library(aw)
library(leaflet)

The first 6 rows of the dataset.

# TODO solve the scroll-x and the not coloring code chunck using blogdown
head(centris_ca)
##                        title  price         lat          lon business_type
## 1             Condo for sale 332978 45.49459400 -73.57475600          sale
## 2 Condo / Apartment for rent   2055 45.49510600 -73.57183700          rent
## 3            Duplex for sale 649900 45.43608108 -73.59654725          sale
## 4             Condo for sale 730000 45.51036900 -73.70656400          sale
## 5 Condo / Apartment for rent   4500 45.49693753 -73.57167258          rent
## 6             Condo for sale 449000 45.54414500 -73.53635100          sale
##   property_type
## 1         Condo
## 2         Condo
## 3        Duplex
## 4         Condo
## 5         Condo
## 6         Condo

First I created a common layer for all examples, setting the map in Montreal, Quebec - Canada.

map <- leaflet::leaflet(width="100%") |>
    leaflet::addTiles() |>
    leaflet::setView(lat = 45.5519, lng = -73.61999, zoom = 12) |>
    leaflet::addProviderTiles(leaflet::providers$CartoDB.Positron)

addMarkers
#

The problem here is that to define different icons or colours, you need to have their PNG or SVG, which gives us a lot of work, so without specifying leaflet::iconList, you can’t have other colours or custom icons unless you use addCircles.

map |>
    leaflet::addMarkers(
        data = centris_ca,
        lng = ~lon,
        lat = ~lat,
        group = ~property_type
    )
map |>
    leaflet::addCircles(
        data = centris_ca |>
            dplyr::mutate(
                color = dplyr::case_when(
                    property_type == "Condo" ~ "blue",
                    property_type == "Duplex" ~ "green",
                    property_type == "House" ~ "red",
                    property_type == "Triplex" ~ "orange"
                )
            ),
        lng = ~lon,
        lat = ~lat,
        group = ~property_type,
        color = ~color,
        weight = 25
    )

The addCircles works pretty well when you aim to look for patterns in the data but not so well when you want to present the data.

addAwesomeMarkers
#

The function addAwesomeMarkers allows you to create markers with limited colours and icons inside it. As before, you need to map for all values, an icon and colour; the difference is that you don’t need to have the PNG or SVG; you just need to pass the icon name from the font awesome, and voila, it’ll show up on the map.

groups <- leaflet::awesomeIconList(
    "Condo" = leaflet::makeAwesomeIcon(
        icon = "building",
        markerColor = "blue",
        library = "fa"
    ),
    "Duplex" = leaflet::makeAwesomeIcon(
        icon = "building",
        markerColor = "green",
        library = "fa"
    ),
    "House" = leaflet::makeAwesomeIcon(
        icon = "home",
        markerColor = "red",
        library = "fa"
    ),
    "Triplex" = leaflet::makeAwesomeIcon(
        icon = "building",
        markerColor = "orange",
        library = "fa"
    )
)

map |>
    leaflet::addAwesomeMarkers(
        data = centris_ca,
        lng = ~lon,
        lat = ~lat,
        icon = ~ groups[property_type],
        group = ~property_type
    ) |> 
    leaflet::addLegend(
        labels = unique(centris_ca[["property_type"]]),
        colors = c("blue", "green", "red", "orange")
    ) |>
    leaflet::addLayersControl(
        overlayGroups = unique(centris_ca[["property_type"]]),
        position = "bottomleft"
    )


addCustomMakers
#

Now let me show a different way to generate the maps.

Same marker and colors
#

map |>
    addCustomMarkers(
        data = centris_ca |>
            dplyr::rename(group = property_type) |>
            dplyr::mutate(
                iconWidth = 40,
                iconHeight = 40,
                icon = "map-marker"
            ) |>
            dplyr::left_join(
                tibble::tibble(
                    group = unique(centris_ca$property_type),
                    fill = c(
                        "blue", 
                        "green", 
                        "red", 
                        "orange"
                    )
                ),
                by = "group"
            )
    )

Different markers and same color
#

map |>
    addCustomMarkers(centris_ca |>
        dplyr::rename(group = property_type) |>
        dplyr::mutate(
            iconWidth = 40,
            iconHeight = 40
        ) |>
        dplyr::left_join(
            tibble::tibble(
                group = unique(centris_ca$property_type),
                fill = c(
                    "blue", 
                    "green", 
                    "red", 
                    "orange"),
                icon = c(
                    "building", 
                    "building", 
                    "home", 
                    "building"
                )
            ),
            by = "group"
        ))

Looking at it from afar, it looks like a mess, but you get a better experience when you zoom in.

Different markers and colors
#

And finally and above all, the pallet colours flexibility, you can see in the code below, I set a pallet from the wesanderson package, which is some HEX colors, and it worked very well.

map |>
    addCustomMarkers(centris_ca |>
        dplyr::rename(group = property_type) |>
        dplyr::mutate(
            iconWidth = 40,
            iconHeight = 40
        ) |>
        dplyr::left_join(
            tibble::tibble(
                group = unique(centris_ca$property_type),
                fill = wesanderson::wes_palette("Zissou1")[-1],
                icon = c(
                    "building", 
                    "building",
                    "home",
                    "building"
                )
            ),
            by = "group"
        ))
## Registered S3 method overwritten by 'wesanderson':
##   method        from
##   print.palette aw

Conclusion
#

Now you have more flexibility and control to build your maps; your problem now is creating a nice relation between the icon and colors which is the hardest part, 🤣.

Happy coding!