Search code examples
rr-leaflet

How can I programmatically create a list of objects of the leaflet icon class?


How can I programmatically create a list of objects of the leaflet icon class?

I have created a map using the leaflet library that will eventually show dozens of locations. For this I want to add some custom icons using the addMarkers function, which takes an iconSet created using iconList as documented here.

In the example below (which uses icons from https://icon-library.net/), the creation of myicons by means of iconList, which contains two direct calls to makeIcon, is unproblematic because only two icons are used. However, in the real world the number of icons, their URLs and other attributes will not be known in advance.

If create a list using iconList and use cbind to attach it to the data frame as a new column, I get the expected "cannot coerce class" error message.

My only option appears to be to programmatically create the myicons list, but using something like mynewicons <- iconList(sapply(1:nrow(df.data), function(i) {makeIcon(df.data$url[i],iconWidth = df.data$width[i],iconHeight = df.data$height[i])})) results in an Arguments passed to iconList() must be icon objects returned from makeIcon() error.

How can I create this list of leaflet icons dynamically rather than specifying it in advance?

require(leaflet)
require(magrittr)

entrynames <- c("Entry 1","Entry 2")
lat <- c(51.509950,51.510736)
lng <- c(-0.1345093,-0.135190)
iconurl <- c("https://icon-library.net/images/right-arrow-icon-png/right-arrow-icon-png-9.jpg",
                "https://icon-library.net/images/back_previous_arrow_play_next_stop_pause_101040.png")
iconwidth <- c(60,50)
iconheight <- c(60,50)
df.data <- data.frame(entrynames=entrynames,lat=lat,lng=lng,
                      url=iconurl,width=iconwidth,height=iconheight,stringsAsFactors = FALSE)

df.data$entrynames <- as.character(df.data$entrynames)

myicons <- iconList(
    marker1 = makeIcon(iconUrl = df.data$url[1],iconWidth = df.data$width[1],iconHeight = df.data$height[1]),
    marker2 = makeIcon(iconUrl = df.data$url[2],iconWidth = df.data$width[2],iconHeight = df.data$height[2])
)

m <- leaflet() %>% setView(lng = -0.1345093, lat = 51.510090, zoom = 18) %>% addTiles() %>%
    addMarkers(data = df.data,
               lat = ~lat,
               lng = ~lng,
               icon = myicons)
m

MRE output:

Custom markers in leaflet for R


Solution

  • It's a bit hacky and I am not too familiar with leaflet but using purrr::map and purrr::flatten plus fixing names and attributes seems to work:

    require(leaflet)
    #> Indlæser krævet pakke: leaflet
    require(magrittr)
    #> Indlæser krævet pakke: magrittr
    
    entrynames <- c("Entry 1","Entry 2")
    lat <- c(51.509950,51.510736)
    lng <- c(-0.1345093,-0.135190)
    iconurl <- c("https://icon-library.net/images/right-arrow-icon-png/right-arrow-icon-png-9.jpg",
                 "https://icon-library.net/images/back_previous_arrow_play_next_stop_pause_101040.png")
    iconwidth <- c(60,50)
    iconheight <- c(60,50)
    df.data <- data.frame(entrynames=entrynames,lat=lat,lng=lng,
                          url=iconurl,width=iconwidth,height=iconheight,stringsAsFactors = FALSE)
    
    df.data$entrynames <- as.character(df.data$entrynames)
    
    myicons <- iconList(
      marker1 = makeIcon(iconUrl = df.data$url[1],iconWidth = df.data$width[1],iconHeight = df.data$height[1]),
      marker2 = makeIcon(iconUrl = df.data$url[2],iconWidth = df.data$width[2],iconHeight = df.data$height[2])
    )
    
    mynewicons <- purrr::map(1:nrow(df.data), 
                             function(i) {
                               iconList(makeIcon(df.data$url[i],
                                                 iconWidth = df.data$width[i],
                                                 iconHeight = df.data$height[i])
                                        )
                               }
                             ) %>% 
      purrr::flatten()
    names(mynewicons) <- glue::glue("marker{1:nrow(df.data)}")
    attr(mynewicons, "class") <-  "leaflet_icon_set"
    identical(myicons, mynewicons)
    #> [1] TRUE
    

    Created on 2019-10-24 by the reprex package (v0.3.0)