Rendering images in DT table in R Shiny - r

I'm making an R Shiny webpage that uses a DT table with many columns, the first of which is an image. Currently, I've found two different ways to get these images to show up and want to know if there are any others as neither is fitting my needs.
METHOD 1 - Point to an existing web hosted iamge
The first, is to have a column in the dataframe used to build to the table that follows this format:
<img src='URL_TO_IMG.png' height='200'></img>
where URL_TO_IMG.png links to a pre-existing, already hosted, web image.
METHOD 2 - encode image file to a data URI
The other is to use download the images locally and instead use the following:
<img src='RESULTS_OF_KNITR_IMAGE_URI' height='200'></img>
Where RESULTS_OF_KNITR_IMAGE_URI are the results of the knitr::image_uri(x) function call where x is the path to my local image. From the knitr documentation this function:
...can encode an image file as a base64 string, which can be used in the img tag in HTML.
Findings:
METHOD 1 works great, but has the problem were I want to post process the web hosted images to be a smaller size. Sometimes they'll be very large and take a long time to load when I really only need a 200 pixel width image, at most. Also, I'd like to have more control over the location of the image files.
METHOD 2's data URI approach allows me to locally pre-process the images to fit nicely into the columns and be smaller. However, since I have 200+ images, this causes the application to have a 10+ second load time as I believe the app loads ALL the images at once this way.
I'm thinking I may want to host my post processed images on an image hosting site and point to the URL like METHOD 1 but am unsure. Any recommendations?
If anyone wants to play with my setup, I made a minimum working example:
library(shiny)
library(DT)
# read in CSV with table information
my_image_df = read.csv('image_test_case_table.csv')
# Define UI
ui <- fluidPage(
fluidRow(
DTOutput("my_table", width = "100%")
)
)
# Define server
server <- function(input, output) {
output$my_table = DT::renderDataTable(
DT::datatable(
{data <- my_image_df},
escape = FALSE,
rownames = FALSE,
options = list(columnDefs = list(list(className = 'dt-center', targets = 0))),
selection = 'single'
)#datatable
)#renderDataTable
}
# Run the application
shinyApp(ui = ui, server = server)
Any ideas?

A little hidden in the Shiny documentation, you will find METHOD 3 that involves supplying the image files inside a www directory (example reference here or in the cheatsheet).
If your folder structure looks as follows
├── image_test_case.R
├── image_test_case_table.csv
├── www/
│ ├── amerikaan.jpg
│ └── tilia.jpg
then you may set the src tag to the image path relative to the www path.
<img src='amerikaan.jpg' height='200'></img>
<img src='tilia.jpg' height='200'></img>
Note that www is used for all sorts of static resources (most common images, javascript and css files).

Related

Image displayed when app used locally but not working after deployment to Shinyapp.io

I am trying to get an image to display in my shiny app based on a user selection by clicking points on a map that display the photos’ location. The app works fine and displays the photo when I use the app locally via Rstudio but the photo does not display when I deploy the app to shinyapp.io. I believe the problem is that the fpath is not annotated correctly to reach the image but I am not sure what the path should read to reach the image when deployed to shinyapp.io.
I have tried a number of solutions including:
As per think link, I saved copies of the images to both the base folder that holds the app file and the www folder thinking that shiny might automatically look in the www folder since that is where images are normally sourced for displaying in the app but that did not work as paste0(dat$PhotoID,".jpg") or paste0(“www”,dat$PhotoID,".jpg") or paste0(“/www”,dat$PhotoID,".jpg") or paste0(“/www/”,dat$PhotoID,".jpg"). When I run the app locally I can access the image using paste0("./",dat$PhotoID,".jpg") or paste0(dat$PhotoID,".jpg")
I tried to link to a “resource folder” but could not find a function for this call nor do I have an Rmd file as per this link.
This link looked useful but altering if a backslash occurred above did not affect the outcome. I also am unfamiliar with relative vs absolute file paths although for apps deployed on shinyapp.io I would expect relative file paths would be the most appropriate.
I have previously used a static image in my app accessed using img(src = "FigureCombo.png") but since I am using an imageOutput() I don’t think that is appropriate.
Does anyone have any advice on how to access stored images when app is deployed to shinyapp.io?
Thanks
Here is the relevant code:
`
ui <- fluidPage(
mainPanel(
imageOutput("myImage", height = "200px"))
)
server <- function(input, output, session) {
output$myImage <- renderImage({
photo <- selected_photo()
if(is.null(photo)) {
return(NULL)
}
else {
list(src = photo,
contentType = 'image/jpg',
width = 300,
height = 400,
style = 'border-style: solid; border-color: red;',
alt = "No point selected yet or that point does not have an image...")
}
}, deleteFile = FALSE)
selected_photo <- reactive({
dat <- reactive_data()
photoid <- input$wsmap_marker_click$id
dat <- dat[dat$PhotoID %in% photoid,]
fpath <- paste0(dat$PhotoID,".jpg") # works when local but not shinyapp.io
return(fpath)
})
}
shinyApp(ui = ui, server = server)
`
To get an idea of the directory structure on an unfamiliar host, you might list the subdirectories of the work directory (as seen by server.R) and log it to your user interface like so:
ui <- fluidPage(
## ...
verbatimTextOutput('log')
## ...
)
server <- function(input, output) {
## ...
output$log <- renderPrint(list.dirs(getwd()))
## ...
}

How are you handling theme updates across multiple Shiny apps?

I have a general shiny question, rather than a specific call to help with code.
I've just finished creating a branded shiny template using a mix of fresh and some custom CSS. It's branded and looks sharp for the dashboards I'm building for my organisation. However, I've got one eye on how to maintain the look and feel of our dashboards as our analytics team continues to grow and mature. When we have 50 apps deployed and we want to change the theme, we will have to go through and update the code in 50 different shinyapps if we continue with the current method of fresh and custom CSS in www/.
Does anyone have experience in centralising themes? Perhaps serving CSS from somewhere external to Shiny and calling that at the start of app.R? This would likely require me to unify fresh and my additional custom CSS into a single .css file, but that's something I'm willing to do.
Alternatively, perhaps someone clever has already developed a package to solve this problem?
Any general pointers warmly received.
For those who might be interested, I built the theme into a package I was developing. Within my package's root dir, I added an inst folder and, inside that, a www dir. I added the organisation logo and some extra CSS that couldn't be easily configured with {fresh}.
To set the theme, I created a function like this:
set_theme<-function(){
theme<-fresh::create_theme(
fresh::adminlte_color(
light_blue = "#383F48",
aqua = "#094357",
green = "#094357",
blue = "#383F48"
),
fresh::adminlte_global(
box_bg = "#FFFFFF",
info_box_bg = "#D1E0E5"
),
fresh::adminlte_vars(
"sidebar-width" = "275px",
"sidebar-dark-bg" = "#3A3F46",
"sidebar-dark-hover-color" = "#FFB151",
"btn-border-radius" = "1px"
)
)
shiny::addResourcePath('www', system.file("www", package = "myPackage"))
return(theme)
}
I used shiny::addResourcePath to create a resource path to link the package's inst/www folder to the project within which myPackage::set_theme() is called. Then, within my Shiny dashboards:
library(myPackage)
theme <- myPackage::set_theme()
ui<-(
fresh::use_theme(theme)
...
tags$head(tags$link(rel = "stylesheet", type = "text/css", href =
"www/style.css"))
...
)
Make note of the www/ in the href argument. If you were to keep these files 'locally' within your Shiny app, you'd create a www dir in root and forego the www/ prefix. When calling files from your package, simply include the www/

Display locally-stored image in R Shiny

I spent a fair amount of time trying to solve that issue.
Of course I did my homework before sharing my issue here.
In particular I have unsuccessfully consulted :
local image in shiny app without img(src())?
Shiny can not display Image locally
adding local image with html to a Shiny app
R Shiny img() on UI side does not render the image
Display images from web in shiny R
Image failing to display in R shiny
Embedding Image in Shiny App
How to place an image in an R Shiny title
So I did create a 'www' folder at the root of the RStudio project file where I put some pictures.
These pictures are used in the titlePanel but also by the main htmlwidget the application calls.
It is crucial for me to have these pictures stored locally because the application may be running in a secured environment without any access to the Internet.
I tried a relative path to these pictures and an absolute path: no picture was displayed.
Then I noticed some kind of inconsistency: I experience this issue only when I run the application through the regular command in RStudio, "Run Selected Line(s)".
On the other hand, when I run the application through the dedicated command "Run App" (in the top right corner in RStudio, green arrow), I don't have this issue anymore, the pictures display nicely (but the input data are somehow inspected and it takes a lot of time before the application is launched).
Initially I thought that displaying local images would be much easier than with remote images stored on the Internet but it seems it is rather the other way around.
Hence my questions:
Do you know why we can observe this difference (which is an inconsistency to me)?
And do you know how I could still continue to use the regular execution command ("Run Selected Line(s)")?
Best regards,
Olivier
For me the following also works when running the app via Run Selected Line(s) in RStudio:
library(shiny)
# create some local images
if(!dir.exists("myimages")){
dir.create("myimages")
}
myPlotPaths <- paste0("myimages/myplot", seq_len(3), ".png")
for (myPlot in myPlotPaths) {
png(file = myPlot, bg = "transparent")
plot(runif(10))
dev.off()
}
myImgResources <- paste0("imgResources/myplot", seq_len(3), ".png")
# Add directory of static resources to Shiny's web server
addResourcePath(prefix = "imgResources", directoryPath = "myimages")
ui <- fluidPage(
tags$img(src = myImgResources[1], width = "400px", height = "400px"),
tags$img(src = myImgResources[2], width = "400px", height = "400px"),
tags$img(src = myImgResources[3], width = "400px", height = "400px")
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
Managing directories can be tricky.
You could use the here package to make things much easier to handle directories in R projects, see Ode to the here package.
After opening the project, images in www can then easily be accessed by:
here::here('www/myimage.jpg')
This will also work for sourcing an app or a script.
I don't have a specific answer, but Hadley has showed an example of how to display images from your stored locally under the 'Graphics' Chapter in 'Mastering shiny' book. The book is under development and it should be released soon, I will paste the link for that chapter:
Graphics chapter
The example is under images section.
HTH

Can you access css file from git/shared drive for shared use by company?

Is there a better way for programmers within a group, looking to share a common style for shiny apps or rmarkdown docs, to access a css file from a single location rather than manually copying the desired file into the subcontents of each app or document?
The ideal outcome would be to place the file(s) on a github repo, then attach it to any shiny app or rmarkdown file with its web link, is this possible?
Thanks.
It might be easier to include your stylesheets in an R package. This would eliminate the need for external request each time your app loads or doc is opened. Place your css files in a folder in inst and write a single R function that sets the resource paths to your css and loads files accordingly.
Let's say your package has the following structure. (For this example, I'm naming the package mypkg)
mypkg/
R/
use_stylesheets.R
inst/
stylesheets/
styles.min.css
...
...
In use_stylesheets.R, create a function that loads the stylesheets into the head of the document (tags$head) using tags$link.
use_stylesheets <- function() {
shiny::addResourcePath(
"styles",
system.file("stylesheets", package = "mypkg")
)
shiny::tags$head(
shiny::tags$link(rel = "stylesheet", href = "styles.min.css")
)
}
Then in your app or Rmarkdown document, you can load the files into your app using: mypkg::use_stylesheets()
After some tinkering I was able to have success (waiting on coworkers to test on their machines) using these structures for adding css or html from a package into both Shiny Apps and Rmarkdown documents.
# For attaching a css file to an Rmarkdown file
use_package_style_rmd <- function(css_file = "my_css.css"){
# css file
file_name <- paste0("/", css_file)
file_path <- paste0(system.file("stylesheets", package = "my_pkg"), file_name)
shiny::includeCSS(path = file_path)
}
# For placing an HTML headers/footers in place
use_package_header <- function(html_file = "my_header.html"){
# HTML Header
file_name <- paste0("/", html_file)
file_path <- paste0(system.file("stylesheets", package = "my_pkg"), file_name)
shiny::includeHTML(path = file_path)
}
# For attaching css to a shiny app via resourcePath
use_package_style_shiny <- function(stylesheet = "my_css.css"){
# Add Resource Path
shiny::addResourcePath(
prefix = "styles",
directoryPath = system.file("stylesheets", package = "my_pkg"))
# Link to App
shiny::tags$head(
shiny::tags$link(rel = "stylesheet",
href = paste0("styles/", stylesheet)))
}
The use_package_style_rmd function can be placed in any code chunk, whereas the header function will add the html in-place where the function is run.
For the Shiny app use-case the function should be run to establish the resource path to the folder, then in the UI under fluidpage the theme option can be set to the css file using the prefix for the Resource Path styles/my_css.css.
One issue I am still having which may be a separate question, is where to place a supporting image file, and how to add the relative path such that the image can be placed in the header or footer.

Save base64 JPG to disk in R - Shiny

I have the following data frame which can be downloaded from here. The column image_path has jpg files in base64 format. I want to extract the image and store it in a local folder. I tried using the code given here and here.
While the second one perfectly opens the image in the browser, I couldn't figure out how to save the file locally. I tried the following code:
library(shiny)
for (i in 1:length(df)){
file <- paste(df$id[i])
png(paste0(~images/file, '.png'))
tags$img(src = df$image_path[i])
dev.off()
}
The following just runs but doesn't create any image files and no errors are shown. When I tried running tags$img(src = df$image_path[1]) to see if it generates the image, it doesn't. I understand tags$img is a function within shiny and works when I pass it inside ui (as suggested by #daatali), but not sure how do I save the files locally.
What I want is to run a for loop from inside a server environment of shiny and save the images locally as jpg using id numbers as filename, which can be rendered with various other details captured in the survey.
I have never worked with images and please bear with me if this is completely novice.
This creates your images from the base64 strings and saves the files to your current working directory, subfolder "/images/". This article describes pretty well how to save files locally in Shiny.
library(shiny)
library(base64enc)
filepath <- "images/"
dir.create(file.path(filepath), showWarnings = FALSE)
df <- read.csv("imagefiletest.csv", header=T, stringsAsFactors = F)
for (i in 1:nrow(df)){
if(df[i,"image_path"] == "NULL"){
next
}
testObj <- strsplit(df[i,"image_path"],",")[[1]][2]
inconn <- testObj
outconn <- file(paste0(filepath,"image_id",df[i,"id"],".png"),"wb")
base64decode(what=inconn, output=outconn)
close(outconn)
}

Resources