Create a gif from a series of Leaflet maps in R

2019-01-18 14:39发布

问题:

I am looking for an automated method to convert leaflet R Studio plots into image files.

Seems like exporting a leaflet widget to HTML is straightforward (Saving leaflet output as html). However I cannot find any answers or docs about how to save the image produced by a leaflet widget as an image. It seems strange that I can do this manually in R Studio but that there isn't some function within R Studio that can be called to do the same thing.

I've tried the usual suspects, variations on the following:

png("test_png.png")
map
dev.off()

But these all just print white or print a file that can't even be opened. IF I understand this Git discussion correctly, seems like something in leaflet is not available but is desired by users.

In the meantime, R Studio clearly has a way to render this image into an image file, making me press a button to do it. Is there a way to automate this? How can I export the images plotted in R Studio to image files? I need image files and I need this to be programmatic because I want to make a gif out of a few hundred maps.

Alternately, I'd welcome suggestions for a workaround. I might try this: Python - render HTML content to GIF image but if someone has alternatibve suggestions, please share.

回答1:

I've been trying to do this with a combination of the webshot package and saveWidget from htmltools, although it's pretty slow. For a few hundred maps, it's probably not too bad if you're only doing it here and there. But, for real-time application it is too slow.

There are two external applications you need for this workflow. webshot takes screenshots of webpages and requires you to install PhantomJS first (it's tiny and easy). I also use ImageMagick (and needs to be accessible from the command line) to create the .gif files, but I'm sure there many other programs you could use to make gifs.

The idea is just to create the maps in a loop, save them to a temporary html file with saveWidget and use webshot to turn it into a png (slow). Then, once you have all the pngs, use ImageMagick to convert them to a gif (fast).

Here is an example, I also load ggmap, but only to get a location to zoom in on.

library(webshot)
library(leaflet)
library(htmlwidgets)
library(ggmap)

loc <- geocode('mt everest')  # zoom in everest
zooms <- seq(2,14,3)          # some zoom levels to animate

## Make the maps, this will make some pngs called 'Rplot%02d.png'
## in your current directory
for (i in seq_along(zooms)) {
    m <- leaflet(data=loc) %>%
      addProviderTiles('Esri.WorldImagery') %>%
      setView(lng=loc$lon, lat=loc$lat, zoom=zooms[i])
    if (i==1)
        m <- m %>% addPopups(popup="Going to see Mt Everest")
    if (i==length(zooms))
       m <- m %>%
          addCircleMarkers(radius=90, opacity = 0.5) %>%
          addPopups(popup = 'Mt Everest')

    ## This is the png creation part
    saveWidget(m, 'temp.html', selfcontained = FALSE)
    webshot('temp.html', file=sprintf('Rplot%02d.png', i),
            cliprect = 'viewport')
}

Then, it is just converting pngs to gif. I did this on a Windows, so command might be slightly different on a mac/linux (I think just single quotes instead of double quotes or something). These commands are from a command line/shell, but you could also use system/system2 to call from R or try the animation package that has some wrapper functions for ImageMagick. To make a simle gif with nothing fancy is simply, convert *.png animation.gif. I used a slightly longer code to make the pngs smaller/add some delays/and have the sequence go in and out.

convert Rplot%02d.png[1-5] -duplicate 1,-2-1, -resize "%50" gif:- | convert - -set delay "%[fx:(t==0||t==4)?240:40]" -quiet -layers OptimizePlus -loop 0 cycle.gif



回答2:

I have table with 3 columns: lat,lon,day (376 days).

The process is: create the map -> save the map as HTML -> save the map as PNG -> import the pic -> plot it (with plot + ggimage)

All this process, will be in a loop

library(leaflet)
library(animation)
library(png)
library(htmlwidgets)
library(webshot)
library(ggmap)

saveGIF({
for (i in 1:376) {
  map  = leaflet() %>%
    addTiles() %>%
    setView(lng = lon_lat[1,2], lat = lon_lat[1,1], zoom = 5)%>%
    addMarkers(lng = lon_lat[lon_lat$day == i,2],lat = lon_lat[lon_lat$day == i,1])

  saveWidget(map, 'temp.html', selfcontained = FALSE) ## save the html
  webshot('temp.html', file=sprintf('Rplot%02d.png', 1),cliprect = 'viewport') ## save as png
  img = readPNG("Rplot01.png") ### read the png
  plot(ggimage(img)) ###reading png file
}
})


回答3:

You can create a series of PNG files as answered by jenesaisquoi (first answer). Then create gif with the png files using the below code with magick package.

library(magick)

    png.files <- sprintf("Rplot%02d.png", 1:20) #Mention the number of files to read
GIF.convert <- function(x, output = "animation.gif")#Create a function to read, animate and convert the files to gif
        {
        image_read(x) %>%
        image_animate(fps = 1) %>%
        image_write(output)
        }

        GIF.convert(png.files)

You don't require to install ImageMagick software on PC.

Code Link: Animation.R