3

I'm creating a Leaflet widget in R using the following code

m <- leaflet(map_data_wgs84) %>% addTiles() %>% addCircles(popup = (paste(sep="<br/>", as.character(map_data_wgs84$MEMBER_REF), map_data_wgs84$Name)))

saveWidget(m, file="c://software//members.html")

I want a popup that palaces an ID number and name separated by a line break. However when I run the saveWidget command I get the following error

Error in gsub("</", "\\u003c/", payload, fixed = TRUE) : 
  input string 1 is invalid UTF-8

which is because of the <br/> separator.

What am I doing wrong here?

thanks

UPDATE: it would appear it is not the <br/> separator but rather character(s) in the map_data_wgs84$Name column. These 12000 records are pulled from a contact database before mapping.

I suspect I need some way to make the characters clean for use in Leaflet with something like htmlEscaoe however I cant figure out how to use this within paste. This doesnt work because htmlEscape is parsed as a string:

addCircles(popup = paste(as.character(map_data_wgs84$MEMBER_REF), ~htmlEscape(map_data_wgs84$Name), sep=","))

For for a MEMBER_REF of 56202 the popup becomes:

56202,htmlEscape(map_data_wgs84$Name)

Miksmith
  • 149
  • 2
  • 13

2 Answers2

5

Overview

To resolve the UTF-8 error, I followed three steps:

  1. After reading How to identify/delete non-UTF-8 characters in R, I used base::Encoding() to manually encode the values with the Name column to UTF-8. I then used base::iconv() to replace all non UTF-8 characters with an empty space;

  2. I manually placed the line break element - <br> rather than <br/> - inside of the sep argument within the paste() function when creating the popup within the markers of the leaflet object; and

  3. To be safe, I used htmltools::htmlEscape() inside of the text vectors used inside of paste().

All together, I was able to export that object as an HMTL file. All packages versions are copied down below in the Session Info section.

SS of HTML Widget

Reproducible Example

# load necessary packages
library( htmltools )
library( htmlwidgets )
library( leaflet )

# create data
map_data_wgs84 <-
  data.frame( MEMBER_REF = "Popup"
              , Name = "Th\x86e birthplace of R."
              , Long = 174.768
              , Lat = -36.852
              , stringsAsFactors = FALSE )

# pre-processing
# ensure that all characters in the `Name` column
# are valid UTF-8 encoded
# Thank you to SO for this gem 
# https://stackoverflow.com/questions/17291287/how-to-identify-delete-non-utf-8-characters-in-r
Encoding( x = map_data_wgs84$Name ) <- "UTF-8"

# replace all non UTF-8 character strings with an empty space
map_data_wgs84$Name <-
  iconv( x = map_data_wgs84$Name
         , from = "UTF-8"
         , to = "UTF-8"
         , sub = "" )

# check work
map_data_wgs84$Name # [1] "The birthplace of R."

# create leaflet object
my.map <-
  leaflet( data = map_data_wgs84 ) %>%
  addTiles() %>%  
  addCircles( lng = ~Long
              , lat = ~Lat
              , popup = paste( as.character( map_data_wgs84$MEMBER_REF )
                               , htmlEscape( map_data_wgs84$Name )
                               , sep = "<br>" )
              , radius = 50 )

# export leaflet object as HMTL file
saveWidget( widget = my.map
            , file = "mywidget.html" )

# end of script #

Session Info

R version 3.5.1 (2018-07-02)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] leaflet_2.0.1   htmlwidgets_1.2 htmltools_0.3.6

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.18    digest_0.6.15   later_0.7.3     mime_0.5       
 [5] R6_2.2.2        xtable_1.8-2    jsonlite_1.5    magrittr_1.5   
 [9] promises_1.0.1  tools_3.5.1     crosstalk_1.0.0 shiny_1.1.0    
[13] httpuv_1.4.5    yaml_2.1.19     compiler_3.5.1 
Cristian E. Nuno
  • 2,822
  • 2
  • 19
  • 33
  • Thanks for the pointer @cristian-e-nuno (and reproducible example) - having experimented further the characters causing the error are actually in the map_data_wgs84$Name column which comes from a database. However I cant work out how to use the ~htmlEscape within paste. Any ideas as this fails: addCircles(popup = paste(as.character(map_data_wgs84$MEMBER_REF), ~htmlEscape(map_data_wgs84$Name), sep=",")) – Miksmith Jul 20 '18 at 11:08
  • @Miksmith, would you be able to give an example of the types of characters stored in that column? That would help me and others help you further. I'm also unsure if `map_data_wgs84` is a spatial polygon data frame, sf, or data frame which may or may not be affecting us here. – Cristian E. Nuno Jul 20 '18 at 13:59
  • @Miksmith, would you mind copying and pasting your new error code? Feel free to revise your question to reflect your new situation. – Cristian E. Nuno Jul 20 '18 at 14:05
  • 1
    @cristian-e-nuno Thanks very much - updated the original Q – Miksmith Jul 20 '18 at 14:24
  • @cristian-e-nuno PS There are spatial columns (lat,long) as they have been converted using spTransform from British National Grid. However the Name column is just a text string (of the contact name) – Miksmith Jul 20 '18 at 14:30
  • @Miksmith, ah I understand much clearer now. Thank you for updating your question. I have updated my answer. I think `leaflet`'s use of the `~` to access variables doesn't play nice with `paste()`. I would suggest you simply remove the use of `~` prior to using `htmlEscape()`. See here for more information: https://stackoverflow.com/questions/47789632/customizing-leaflet-popup-in-r/49212330#49212330. – Cristian E. Nuno Jul 20 '18 at 16:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176432/discussion-between-cristian-e-nuno-and-miksmith). – Cristian E. Nuno Jul 20 '18 at 17:08
1

I saw this solution in another post (sorry, lost the link!) which use sprintf to combine text and variables and then using htmltools within lapply to make the content HTML-happy! In this particular I created the following list:

labels <- sprintf(
  "<strong>%s</strong><br/>%o<br/>%s",
  map_data$Name, map_data$Events, map_data$member_ref
) %>% lapply(htmltools::HTML)

and then called this from leaflet:

my_map <- leaflet(map_data_wgs84) %>% addTiles() %>% addCircles(popup = labels)

Hope this helps someone else!

Miksmith
  • 149
  • 2
  • 13