amg
amg

Reputation: 204

Download a shape file from Shiny

I have a Shiny app that reads in a gpx track file and buffers it. I then want the user to be able to download that shapefile to a destination of their choice. I have been trying to use the downloadHandler function, but so far I have no joy.

The name of the shapefile that I have created is called trk_buff. Within R I can just use:

my_dsn<-"C:\\Documents\\TrackFiles"
writeOGR(obj=trk_buff, dsn=my_dsn, layer="BufferedTracks", driver="ESRI Shapefile")

I have tried to use the downloadHandler thus:

  output$downloadData<-downloadHandler(
    filename=function(file){trk_buff},
    content=function(file){
      writeOGR(obj=trk_buff, dsn=file, layer="BufferedTracks", driver="ESRI Shapefile")
    })

But I am obviously doing something wrong as nothing happens... :|

EDIT TO ADD I can get the behaviour I want if I use a combination of an action Button and a textFile box. But that is a little clumsy and involves the user explicitly writing the filepath rather than searching for it, which will probably lead to errors: e.g. in the ui.R I have:

textInput("filepath","Filepath to download data"),
actionButton("act1","Download shapefile")

In the server.R I have:

  action_btn_code <- eventReactive(input$act1, {
    file_path<-input$filepath    
    writeOGR(obj=trk_buff, dsn=paste(file_path,"/Tracks",sep=""), layer="BufferedTracks", 
      driver="ESRI Shapefile", overwrite_layer=TRUE)
  })

Upvotes: 4

Views: 1690

Answers (2)

Milse Nzingou
Milse Nzingou

Reputation: 21

library(sf)
library(zip)

output$download_shp <- downloadHandler(
    filename <- function() {
        "Data_shpExport.zip"
        
    },
    content = function(file) {
        withProgress(message = "Exporting Data", {
            
            incProgress(0.5)
            tmp.path <- dirname(file)
            
            name.base <- file.path(tmp.path, "BufferedTracks")
            name.glob <- paste0(name.base, ".*")
             name.shp  <- paste0(name.base, ".shp")
            name.zip  <- paste0(name.base, ".zip")
            
            if (length(Sys.glob(name.glob)) > 0) file.remove(Sys.glob(name.glob))
            sf::st_write(trk_buff, dsn = name.shp, ## layer = "shpExport",
                      driver = "ESRI Shapefile", quiet = TRUE)
            
            zip::zipr(zipfile = name.zip, files = Sys.glob(name.glob))
            req(file.copy(name.zip, file))
            
            if (length(Sys.glob(name.glob)) > 0) file.remove(Sys.glob(name.glob))
            
            incProgress(0.5)
        })
    }  
)

Upvotes: 2

moho wu
moho wu

Reputation: 491

The following works for me. The idea is that you have to zip up of the shapefiles because downloadHandler can only handle downloading one file.

output$download_shp <- downloadHandler(
    filename = "shapefile.zip",
    content = function(file) {
        data = trk_buff() # I assume this is a reactive object
        # create a temp folder for shp files
        temp_shp <- tempdir()
        # write shp files
        writeOGR(data, temp_shp, "trk_buff", "ESRI Shapefile", 
                 overwrite_layer = TRUE)
        # zip all the shp files
        zip_file <- file.path(temp_shp, "trk_buff_shp.zip")
        shp_files <- list.files(temp_shp,
                                "trk_buff",
                                full.names = TRUE)
        # the following zip method works for me in linux but substitute with whatever method working in your OS 
        zip_command <- paste("zip -j", 
                             zip_file, 
                             paste(shp_files, collapse = " "))
        system(zip_command)
        # copy the zip file to the file argument
        file.copy(zip_file, file)
        # remove all the files created
        file.remove(zip_file, shp_files)
    }
)

Upvotes: 6

Related Questions