bright-star
bright-star

Reputation: 6437

Containing reactive objects in Shiny for export

I have tried to apply the answers given in both this question on saving plots, and this question on downloading reactive outputs without success. I am not sure if my reactive function is outputting the wrong type of data or if my downloadHandler() is not written properly.

Also, the linked questions pass function()s to reactive() which I am warned is deprecated, so I have avoided it here. (The behavior did not change, though.)

ui.R:

library(shiny)

# Define UI for application
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("App"),

  sidebarPanel(
    downloadButton("savemap", label="Download Map (Hires)")
  ),

  mainPanel(
    tabsetPanel(
      tabPanel("Map",
               plotOutput("myworld", height="650px",width="750px",
                          clickId="plotclick")
      )
    )
  )
))

server.R

library(shiny)
library(maps)
library(mapdata)
library(rworldmap)
library(gridExtra)

shinyServer(function(input, output) {

  theworld <- reactive({
    myplot <- map("world2", wrap=TRUE, plot=TRUE,
        resolution=2)
  })

  output$myworld <- renderPlot({
    print(theworld())
  })

  output$savemap <- downloadHandler(
    filename = function() {
      paste('fuzzymap-', Sys.Date(), '.png', sep="")
    },
    content = function(file) { 
      # function to actually write the image file
      # https://stackoverflow.com/questions/14810409/save-plots-made-in-a-shiny-app?rq=1
      png(file)
      print(theworld())
      dev.off()
    })

})

The map in the reactive function is plotted sucessfully at startup. The download prompt is generated and a png file downloads, but it contains no data. Additionally, the following non-fatal error is returned, but a search turned up no leads:

Error opening file: 2
Error reading: 9

Upvotes: 1

Views: 1756

Answers (1)

hadley
hadley

Reputation: 103898

I think you're confused about what map() returns. You call it for its side-effect of drawing a plot, not it's return value. Rather than making it a reactive value, just keep it as an anonymous function.

Here's a simplified version that works for me:

library(shiny)
library(maps)

ui <- bootstrapPage(
  plotOutput("myworld", height = 450, width = 750),
  downloadButton("savemap", "Download map")
)

theworld <- function() {
  map("world2", wrap = TRUE, resolution = 2)
}

server <- function(input, output) {
  output$myworld <- renderPlot({
    theworld()
  })

  output$savemap <- downloadHandler(
    filename = function() {
      paste0("fuzzymap-", Sys.Date(), ".png")
    },
    content = function(file) { 
      png(file)
      theworld()
      dev.off()
    }
  )
}

runApp(list(server = server, ui = ui))

Upvotes: 5

Related Questions