I am creating a shiny application where I allow the user to write the data out to either csv or kml. However, my code below does not write the features out to the kml file, such that when I open the KML in google earth it shows black dots and when clicked it displays the row index of the original data rather than All column values for that specific point. I was using writeOGR function but it was not writing the file, so I switched to using plotKML package. I want the user to choose where the file is saved (using the filename I specify with date and timestamp) and display in Google Earth all features of any given datapoint.
output$downloadData <- downloadHandler(
filename = function() {
paste0("data_",Sys.Date(), input$download_type)
},
content = function(file) {
if (input$download_type == ".csv"){
write.csv(data, file, row.names = FALSE)
} else if (input$download_type == ".KML") {
features <- c("COLUMN_1", "COLUMN_2", "COLUMN_3") #These are the features I want displayed in Google Earth
data[features] <- as.character(data[features])
coordinates(data) <- ~X + Y
proj4string(data) <- CRS("+proj=longlat +datum=WGS84")
kml_description(data, caption = "Data",
delim.sign = "_", asText = F)
kml(data, file = file) #Not sure why this produces points but doesn't display features in Google Earth
#writeOGR(data, dsn = file, layer="Data", driver = "KML")
}
})
enter image description here
Getting a KML in a way that can be adeuqately read by Google Maps is not yet as easy as it should be.
You may want to give a try to export via the sf package and libkml.
sf::st_write(obj = an_sf_object,
dsn = kml_file_path,
driver = "libkml")
(you may need to install libkml on your server)
See also this function from the latlon2map package for a working (albeit less than ideal) implementation.
Related
I tried sample codes for visExport() and is able to add a "Export as PNG" button to a Shiny webpage. But this would need user interaction to press the button to export the network to a PNG image file.
I have over hundred of networks and would like to export them each to a PNG file. And some of these would need regular updating. So it would be logistically nice to be able to export all them to PNG files without a user pressing the "Export as PNG" button for each of the network.
So is it possible to programmatically export a list of networks one by one to PNG? This would be just like for visSave() to export the HTML for each network one by one.
In reference to other post, here is working code making use of the webshot package.
library(visNetwork)
library(tidyverse)
library(webshot)
# create network data
nodes = data.frame(id = numeric(),label=character(),set = numeric(),stringsAsFactors = F)
edges = data.frame(from = numeric(),to = numeric(),set = numeric(),stringsAsFactors = F)
for (i in 1:10){
tempNodes <- data.frame(id = 1:15, label = paste("Label", 1:15), set = i)
tempEdges <- data.frame(from = trunc(runif(15)*(15-1))+1,
to = trunc(runif(15)*(15-1))+1, set = i)
nodes = rbind(nodes,tempNodes)
edges = rbind(edges,tempEdges)
}
# loop through each set to export to PNG
for (i in 1:max(nodes$set)){
subNodes = nodes[nodes$set==i,]
subEdges = edges[edges$set==i,]
network = visNetwork(subNodes, subEdges, width="100vw",height = "100vh") %>%
visLayout(randomSeed=1,improvedLayout=TRUE) %>%
visGroups(groupname = "actorImported",shape="circle") %>%
visEdges(smooth=FALSE) %>%
visPhysics(solver = "barnesHut")
fname = paste0("network",sprintf("%03d",i),".html")
visSave(network,fname)
webshot(fname,delay=0.5,zoom=2,file=paste0("network",sprintf("%03d",i),".png"),
vwidth=900,vheight=900)
}
The short answer must be "Yes", but since you've not given us any sample code, it's impossible to give you a tested solution. But you say you have a hundred networks. Let's assume there in a vector networks. (A list would work as well with minor changes to the sample code below.) You can export one graph using a Shiny button. So you must have code to create a single network image. Suppose that's in a function createImageForNetwork(x), where x is a network. You'll also need a function to create an output file name for each image. Suppose that's called getFilename(x). Then something like the untested code below will do what you want:
exportNetworkImage <- function(x) {
png(getFilename(x))
createImageForNetwork(x)
dev.off()
}
lapply(1:length(networks), function(x) exportNetworkImage(networks[x]))
If you want to do that with a single button click in your Shiny app, then just put that code inside the button's observeEvent handler.
I am working with a function that outputs an svg object. As I see the SVG object is essentially a string of characters.
I was wondering how to
1) plot the svg output from the function
2) save this svg object to disk under an svg extension? I tried ggsave but just resulted in an error.
I am fairly new to svg handling, so would appreciate any inputs.
Thanks!
1) I tried that for a package I was developing and it was not straightforward. In the end, I needed two libraries: rsvg and grImport2. Here is the code I used:
tfile <- tempfile(fileext = ".svg")
tfile2 <- tempfile(fileext = ".png")
cat(svg_code, file=tfile)
if (requireNamespace("rsvg", quietly = TRUE) && requireNamespace("grImport2", quietly = TRUE)) {
rsvg::rsvg_svg(svg = tfile, tfile2)
p <- grImport2::readPicture(tfile2)
grImport2::grid.picture(p)
} else {
if (systemShow == FALSE && outFile == ''){
warning("The figure cannot be rendered in the plot window. Please, use the arguments outFile and/or systemShow.")
}
}
if (systemShow){
utils::browseURL(tfile)
}
The first conditional is in case the system does not allow the installation of either package. As you can see, you first need to write the svg code (svg_code) to a file, in this case temporary (tfile). Then, rsvg_svg writes a temporary png file (tfile2). Finally, grImport2::readPicture and grImport2::grid.picture show the converted file in the plot window. I also left the part where the user can set a boolean variable (systemShow) and the package will attempt to open the file on the default system svg viewer.
2) That one is much easier. You just need to write the code to a file as text, like cat(svg_code, file='path_to_file.svg').
I had trouble to generate a PDF file from a scatterplot created trough ggplot in a ShinyApp. I was succesful with a similar approach as the one from the answer from user juba to this stackoverflow question, but then the nearPoints() function I was using didn't work and gave an error message saying something about not being able to find the coordinfo. Then I used the ggsave option that Yihui Xie recommended, but with this strategy I'm getting files saved in the folder where my ShinyApp resides. I'm worried that if I try to use this in my ShinyApp hosted in the shinyapps.io site, there would be problems when trying to save these transient and temporal files. I also tried removing the file after the download is done but, anytime the plot is shown the file is created, so the file is created again after the copied file is downloaded. Here is only a sample (some important lines) of the code I used to allow for the download of the plot as a PDF file:
#### User Interface ----
# Show scatterplot with clicking capability
plotOutput(outputId = "scatterplot", click = "plot_click")
# Show data table where data points clicked will be shown
dataTableOutput(outputId = "datatable")
# Download button
downloadButton('dlScatPlot', 'Download plot as PDF')
# Server ----
# Wrap the creation of the scatterplot in a function so the plot can be
# downloaded as PDF
makeScatPlot <- function() {
## some code to generate a ggplot plot
}
# Create the scatterplot object the plotOutput function is expecting
output$scatterplot <- renderPlot({
# The file saved as ggsave originally will be first saved in the server, and
# then in the client side if the Download Button is used
filename <- paste('scatterPlot_', Sys.Date(), '.pdf', sep='')
ggsave(filename, makeScatPlot(), width = 11, height = 4, dpi = 300, units = "in")
makeScatPlot()
})
# Create data table showing points that have been clicked
output$datatable <- DT::renderDataTable({
rows <- nearPoints(df1, input$plot_click) %>%
select(sample_ID, compound, DOI)
DT::datatable(rows, rownames = FALSE)
})
output$dlScatPlot <- downloadHandler(
filename = function() {
paste('scatPlot_', Sys.Date(), '.pdf', sep='')
},
content = function(file) {
file.copy(paste('scatPlot_', Sys.Date(), '.pdf', sep=''), file, overwrite = TRUE)
# To avoid the accumulation of PDFs in the server
file.remove(paste('scatPlot_', Sys.Date(), '.pdf', sep=''))
}
)
I guess it can cause trouble if I upload a ShinyApp script to shinyapps.io that creates one PDF file each time the plot is rendered, right?
Instead of saving files to a specific path, you can save them as temporary file using tempfile(fileext = ".pdf"). Those files will be automatically removed once the session is over. So no need to remove them manually.
I finally came out with an obvious answer. I wasn't doing the straightforward thing that was call the ggsave in the downloadHandler call because I was using the Yihui answer directly. So, finally I just don't create the file inside the renderPlot() function, but in the downloadHandler where it rightfully should be.
# Create the scatterplot object the plotOutput function is expecting
output$scatterplot <- renderPlot({
makeScatPlot()
})
# Create the button to download the scatterplot as PDF
output$dlScatPlot <- downloadHandler(
filename = function() {
paste('scatterPlot_', Sys.Date(), '.pdf', sep='')
},
content = function(file) {
ggsave(file, makeScatPlot(), width = 11, height = 4, dpi = 300, units = "in")
}
)
Using the above code, everything (including the nearPoints call) works now :)
I'm using this amazing package to be able to read and upload data with my shiny app. It's working ok, but when I add a row to the sheet, it does not keep the same encoding from server, neither behaves like the data in the previous rows. Spanish names I manually entered are OK, but when I use the app to load data, special latin characters (UTF-8) are replaced in the sheet.
That data, is not recognized by the app in the following sessions.
library(googlesheets)
table <- "Reportes"
saveData <- function(data) {
# Grab the Google Sheet
sheet <- gs_title(table)
# Add the data as a new row
gs_add_row(sheet, input = data)
}
loadData <- function() {
# Grab the Google Sheet
sheet <- gs_title(table)
# Read the data
gs_read_csv(sheet)
}
Then, I use a button in the UI, and an observer in the SERVER to load the data...
observeEvent(input$enviar, {
exit <- input$enviar
if (exit==1){
addData <- c( as.character(input$fecha),
as.character(input$local),
as.character(input$dpto),
as.character(input$estado),
as.character(input$fsiembra),
as.character(input$ref),
as.character(loc$lat[loc$Departamento==input$dpto & loc$Localidad==input$local]),
as.character(loc$long[loc$Departamento==input$dpto & loc$Localidad==input$local]),
as.character(getZafra(input$fecha)))
saveData(addData)
d <- loadData()
reset('fecha')
reset('dpto')
reset('local')
reset('estado')
reset('fsiembra')
reset('ref')
reset('pass')
disable('enviar')
}
})
Please... if anyone can help I'd be very happy.
I discovered that I needed to encode the character vector before uploding...
I used:
Encoding(addData) = "latin1"
saveData(addData)
and worked just fine!.
I'm new to R (and programming in general), so apologies if this has been answered elsewhere. I was not able to find an answer via searching, but any help or direction would be great!
I'm trying to make a clickable interface in R, where I can have users click to find a file of choice that proceeds to get automatically analyzed in R.
Here's what I'm having trouble with:
require(tcltk)
getfile <- function() {name <- tclvalue(tkgetOpenFile(
filetypes = "{{CSV Files} {.csv}}"))
if (name == "") return;
datafile <- read.csv(name,header=T)
}
tt <- tktoplevel()
button.widget <- tkbutton(tt, text = "Select CSV File to be analyzed", command = getfile)
tkpack(button.widget)
# The content of the CSV file is placed in the variable 'datafile'
Yet when I try to execute it, and click on a CSV file of interest after the button pops up, nothing happens. By that I mean R gives me the error below when I type in datafile.
Error: object 'datafile' not found
Once again, any help is much appreciated. Thanks!
The top level object has an environment in which you can store things, keeping them local to the GUI. Give your callbacks that object as an argument:
# callback that reads a file and stores the DF in the env of the toplevel:
getfile <- function(tt) {name <- tclvalue(tkgetOpenFile(
filetypes = "{{CSV Files} {.csv}}"))
if (name == "") return;
tt$env$datafile <- read.csv(name,header=T)
}
# callback that does something to the data frame (should check for existence first!)
dosomething <- function(tt){
print(summary(tt$env$datafile))
}
# make a dialog with a load button and a do something button:
tt <- tktoplevel()
button.choose <- tkbutton(tt, text = "Select CSV File to be analyzed", command = function(){getfile(tt)})
tkpack(button.choose)
button.dosomething <- tkbutton(tt, text = "Do something", command = function(){dosomething(tt)})
tkpack(button.dosomething)
That should be fairly robust, although I'm not sure if there's anything already in the environment that you should beware stomping on, in which case create an environment in that environment and use that for your local storage.
If you want to quit the dialog and save things for the user, prompt for a name and use assign to store them in .GlobalEnv on exit.