Drawing Customized Square Grids over highchart map in shiny - r

I have created a simple shiny app where I have country, name, and month as inputs according to which a certain map is plotted.
What I would like to do, is to draw cubic 50*50 km grids over each base map.
I have as data, the coordinates of the centroid of each grid, a certain value to plot per grid (filtered per year and month)
What I have till now, is the filter inputs and the base maps, still have no clue how to dram the grids over the map.
Any help is appreciated
Here is part of my code
dashboardHeader(title=""),
dashboardSidebar(sidebarMenu(
menuItem("",tabName="",icon =icon("dashboard"))
)),
dashboardBody(
tabItem(tabName="",
fluidRow(
box(title="Please choose a country,month and year", width=12,status="warning",solidHeader=TRUE,
selectInput("Country","Country",choices=countries_list, multiple=FALSE),
selectInput("Month","Month",choices=month_list,multiple=FALSE),
selectInput("Year","Year",choices=year_list,multiple=FALSE),
downloadButton("DownloadData","Download Data as csv"))
),
fluidRow(
box(title="Grids",width=12,highchartOutput("Map",height=450),status="primary",solidHeader=TRUE)
)
)
)
)
# Server input/output Function ---------------------------------------------------------
server<-function(input,output){
#Interactive filters & base Maps & Grids
data1<-reactive(
{
req(input$Country)
req(input$Month)
req(input$Year)
data_filtered<- df_Main%>%filter(country %in% input$Country)%>%filter(month_name %in% input$Month)%>%filter(year %in% input$Year)
}
)
output$Map <- renderHighchart(
{
if(input$Country=="Algeria"){
hcmap("https://code.highcharts.com/mapdata/countries/dz/dz-all.js")%>%hc_title(text = "Algeria")
}
else if(input$Country=="Bahrain"){
hcmap("https://code.highcharts.com/mapdata/countries/bh/bh-all.js")%>%hc_title(text = "Bahrain")
}
else if(input$Country=="Comoros"){
hcmap("https://code.highcharts.com/mapdata/countries/km/km-all.js")%>%hc_title(text = "Comoros")
}
else if(input$Country=="Djibouti"){
hcmap("https://code.highcharts.com/mapdata/countries/dj/dj-all.js")%>%hc_title(text = "Djibouti")
}
else if(input$Country=="Egypt"){
hcmap("https://code.highcharts.com/mapdata/countries/eg/eg-all.js")%>%hc_title(text = "Egypt")
}
else if(input$Country=="Iraq"){
hcmap("https://code.highcharts.com/mapdata/countries/iq/iq-all.js")%>%hc_title(text = "Iraq")
}
else if(input$Country=="Jordan"){
hcmap("https://code.highcharts.com/mapdata/countries/jo/jo-all.js")%>%hc_title(text = "Jordan")
}
else if(input$Country=="Kuwait"){
hcmap("https://code.highcharts.com/mapdata/countries/kw/kw-all.js")%>%hc_title(text = "Kuwait")
}
else if(input$Country=="Lebanon"){
hcmap("https://code.highcharts.com/mapdata/countries/lb/lb-all.js")%>%hc_title(text = "Lebanon")
}
else if(input$Country=="Libya"){
hcmap("https://code.highcharts.com/mapdata/countries/ly/ly-all.js")%>%hc_title(text = "Libya")
}
else if(input$Country=="Mauritania"){
hcmap("https://code.highcharts.com/mapdata/countries/mr/mr-all.js")%>%hc_title(text = "Mauritania")
}
else if(input$Country=="Morocco"){
hcmap("https://code.highcharts.com/mapdata/countries/ma/ma-all.js")%>%hc_title(text = "Morocco")
}
else if(input$Country=="Oman"){
hcmap("https://code.highcharts.com/mapdata/countries/om/om-all.js")%>%hc_title(text = "Oman")
}
else if(input$Country=="Qatar"){
hcmap("https://code.highcharts.com/mapdata/countries/qa/qa-all.js")%>%hc_title(text = "Qatar")
}
else if(input$Country=="Saudi Arabia"){
hcmap("https://code.highcharts.com/mapdata/countries/sa/sa-all.js")%>%hc_title(text = "Saudi Arabia")
}
else if(input$Country=="Somalia"){
hcmap("https://code.highcharts.com/mapdata/countries/so/so-all.js")%>%hc_title(text = "Somalia")
}
........
}
)
#Downloadable data .csv format
output$DownloadData <- downloadHandler(
filename = function() {
paste0(Sys.time(), ".csv", sep = "")
},
content = function(file) {
data_filtered<-df_Main %>%filter(month_name %in% input$Month)%>%filter(year %in% input$Year)%>%filter(country %in% input$Country)
write.csv(data_filtered, file)
}
)
}
# Call Server -------------------------------------------------------------
shinyApp(ui,server)```

Related

Problem getting current list of input values in Shiny

I'm having quite a few problems to get the updated values from several coonditionalPanels. I created a reactive variable parList which should contain the parN_sig input variables. These variables should come from conditional panels and they are all named parN_sig. I do not know why but I always end up getting values from the double_sig panel when another panel is shown at the UI.
Here's my code:
ui.r
jscode <- "
shinyjs.disableTab = function(name) {
var tab = $('.nav li a[data-value=' + name + ']');
tab.bind('click.tab', function(e) {
e.preventDefault();
return false;
});
tab.addClass('disabled');
}
shinyjs.enableTab = function(name) {
var tab = $('.nav li a[data-value=' + name + ']');
tab.unbind('click.tab');
tab.removeClass('disabled');
}
"
css <- "
.nav li a.disabled {
background-color: #aaa !important;
color: #333 !important;
cursor: not-allowed !important;
border-color: #aaa !important;
}"
# Create Shiny object
library(shiny)
library(shinythemes)
library(shinyjs)
library(ggplot2)
library(grid)
library(egg)
# source("input.r")
source("functions.r")
formula_tabs<-tabsetPanel(
tabPanel("double_sig",
withMathJax("$$y=d+\\frac{a}{1+exp^{-b*(t-c)}}+\\frac{e}{1+exp^{-f*(t-g)}}$$")
),
tabPanel("gompertz",
withMathJax("$$y=b*exp^{\\ln(\\frac{c}{b})*exp^{-a*t}}$$")
),
tabPanel("verhulst",
withMathJax("$$y=\\frac{b*c}{b+(b-c)*exp^{-a*t}}$$")
),
id = "formulas",
type = "tabs"
)
fluidPage(useShinyjs(),theme = shinytheme("lumen"),useShinyjs(),tags$style("#params { display:none; } #formulas { display:none; }"),
extendShinyjs(text = jscode),inlineCSS(css),
navbarPage("Protein turnover model",id="tabs",
tabsetPanel(tabPanel("Input data",
checkboxInput("multiple","Single file",value = FALSE),
uiOutput("mult_files"),
uiOutput("sing_file"),
actionButton("disp_distr","Show distributions"),
plotOutput("distr_plot"),
plotOutput("distr_stade")
),
tabPanel("Weight fitting",
fileInput("weight_data","Choose weight data to be fitted",accept = c("text/csv")),
div(style="display:inline-block",selectInput("method_we","Select fitting formula",choices = c("Logistic"="verhulst","Gompertz"="gompertz","Empiric"="empirique","Log polynomial"="log_poly","Double sigmoid"="double_sig"))),
div(style="display:inline-block",formula_tabs), ## "Contois"="contois",,"Noyau"="seed",
conditionalPanel("input.method_we=='verhulst'",textInput("par1_sig","Enter value of a",value =0.1),
textInput("par2_sig","Enter value of b",value = 100),
textInput("par3_sig","Enter value of c",value = 1)),
conditionalPanel("input.method_we=='gompertz'",textInput("par1_sig","Enter value of a",value =0.065),
textInput("par2_sig","Enter value of b",value = 114.39),
textInput("par3_sig","Enter value of c",value = 0.52)),
conditionalPanel("input.method_we=='empirique'",textInput("par1_sig","Enter value of a",value =5.38),
textInput("par2_sig","Enter value of b",value = 8),
textInput("par3_sig","Enter value of c",value = 7)),
conditionalPanel("input.method_we=='double_sig'",textInput("par1_sig","Enter value of a",value = 48),
textInput("par2_sig","Enter value of b",value = 0.144),
textInput("par3_sig","Enter value of c",value = 35),
textInput("par4_sig","Enter value of d",value = 0.4),
textInput("par5_sig","Enter value of e",value = 48),
textInput("par6_sig","Enter value of f",value = 0.042),
textInput("par7_sig","Enter value of g",value = 90)),
actionButton("fit_op","Fit"),
plotOutput("fitplot")
),
tabPanel("mRNA fitting and calculation",
textInput("ksmin","Value of ksmin",value =3*4*3*3.6*24),
selectInput("fit_mrna","Select fitting formula",choices=c("3rd degree polynomial"="3_deg","6th degree polynomial"="6_deg","3rd degree logarithmic polynomial"="3_deg_log")),
actionButton("run_loop","Run calculation"),
disabled(downloadButton("downFile","Save results"))
),
tabPanel("Results",id="tabRes",
uiOutput("select_res"),
plotOutput("fit_prot_plot")
)
))
)
server.r
library(shiny)
library(shinythemes)
library(shinyjs)
library(ggplot2)
library(grid)
library(egg)
# source("input.r")
source("functions.r")
function(input, output, session) {
js$disableTab("tabRes")
fit_op<-reactiveValues(data=NULL)
run_calc<-reactiveValues(data=NULL)
en_but<-reactiveValues(enable=FALSE)
theme<<-theme(panel.background = element_blank(),panel.border=element_rect(fill=NA),panel.grid.major = element_blank(),panel.grid.minor = element_blank(),strip.background=element_blank(),axis.text.x=element_text(colour="black"),axis.text.y=element_text(colour="black"),axis.ticks=element_line(colour="black"),plot.margin=unit(c(1,1,1,1),"line"))
output$mult_files<-renderUI({
if (!input$multiple){
tagList(fileInput("prot_file","Choose protein file"),
fileInput("mrna_file","Choose transcript file"))
}
})
output$sing_file<-renderUI({
if (input$multiple){
tagList(textInput("protein_tab","Name of protein tab",value = "Proteines"),
textInput("rna_tab","Name of mRNA tab",value = "Transcrits"),
fileInput("data_file","Choose xls/xlsx file",accept=c(".xls",".xlsx")))
}
})
observeEvent(input$method_we, {
# updateTabsetPanel(session, "params", selected = input$method_we)
updateTabsetPanel(session,"formulas",selected = input$method_we)
})
observe({
if(!is.null(input$data_file)){
inFile<-input$data_file
list_data<-loadData(inFile$datapath,input$rna_tab,input$protein_tab,poids=F)
mrna_data<-list_data$mrna
prot_data<-list_data$prot
test_list<-list_data$parse
test_list<<-sample(test_list,3)
clean_mrna_data<<-mrna_data[,-which(is.na(as.numeric(as.character(colnames(mrna_data)))))]
clean_prot_data<<-prot_data[,-which(is.na(as.numeric(as.character(colnames(prot_data)))))]
}
})
observe({
if((!is.null(input$prot_file)) & (!is.null(input$mrna_file))){
protFile<-input$prot_file
mrnaFile<-input$mrna_file
prot_data<-loadData(protFile$datapath,"","",poids=F)
mrna_data<-loadData(mrnaFile$datapath,"","",poids=F)
clean_mrna_data<<-mrna_data[,-which(is.na(as.numeric(as.character(colnames(mrna_data)))))]
clean_prot_data<<-prot_data[,-which(is.na(as.numeric(as.character(colnames(prot_data)))))]
total_data<-merge(mrna_data,prot_data)
lista<-vector("list",nrow(mrna_data))
for (i in seq(1,nrow(total_data))){
lista[[i]]<-list("Protein_ID"=total_data[i,"Protein"],"Transcrit_ID"=total_data[i,"Transcrit"],"Transcrit_val"=as.matrix(total_data[i,3:29]),"Protein_val"=as.matrix(total_data[i,30:ncol(total_data)]),"DPA"=t)
}
# test_list<<-lista
test_list<<-sample(lista,3)
}
})
observeEvent(input$disp_distr,{
print("Plotting...")
output$distr_plot<-renderPlot({print(combineGraphs(clean_mrna_data,clean_prot_data,"",moyenne = T))})
print("Finished")
})
# parList<-reactiveValues()
# observe({
# for (i in reactiveValuesToList(input)){
# print(i)
# if (grepl("par[1-9]+_sig",i,perl = T)){
# newlist[[input[[i]]]]<-input[[i]]
# }
# }
# # })
parList<-reactive({
x<-reactiveValuesToList(input)
x_ind<-grep("par[1-9]+",names(x),perl = T)
newlist<-vector("list",length(x_ind))
names(newlist)<-names(x[x_ind])
for (el in names(newlist)){
newlist[[el]]<-as.numeric(as.character(input[[el]]))
}
names(newlist)<-gsub("_sig","",names(newlist))
newlist<-newlist[order(names(newlist))]
newlist
})
observeEvent(input$fit_op,{
browser()
print(parList())
inFile<-input$weight_data
days_kiwi<-rep(c(0,13,26,39,55,76,118,179,222), each = 3)
poids_data<-loadData(inFile$datapath,"","",poids=T)
print("Fitting...")
tryCatch({
coefs_poids<<-fitPoids_v2(poids_data[,1],poids_data[,2],input$method_we,parList())
},
warning = function(warn){
showNotification(paste0(warn), type = 'warning')
},
error = function(err){
showNotification(paste0(err), type = 'err')
})
print(coefs_poids$coefs)
val_mu<-mu(c(poids_data$DPA),input$method_we,coefs_poids$coefs,coefs_poids$formula,dpa_analyse = NULL)
data_mu<-data.frame("DPA"=c(poids_data$DPA),"Mu"=val_mu)
g_mu<<-ggplot(data_mu,aes(x=DPA,y=Mu))+geom_line()+theme+xlab("DPA")+ylab("Growth rate (days^-1)")
fit_op$state<-TRUE
print("Finished!!")
output$fitplot<-renderPlot({
req((fit_op$state)==TRUE,exists("coefs_poids"))
ggarrange(coefs_poids$graph,g_mu,ncol=2)
})
})
observeEvent(input$run_loop,{
if (input$fit_mrna!=""){
ksmin=as.numeric(as.character(input$ksmin))
score=0
cont<-0
poids_coef<<-coefs_poids$coefs
formula_poids<<-coefs_poids$formula
mess<-showNotification(paste("Running..."),duration = NULL,type = "message")
for (el in test_list){
tryCatch({
run_calc$run<-TRUE
cont<-cont+1
print(cont)
norm_data<-normaMean(el$Protein_val,el$Transcrit_val,ksmin)
fittedmrna<<-fit_testRNA(el$DPA,norm_data$mrna,"3_deg")
par_k<-solgss_Borne(el$DPA,as.vector(norm_data$prot),as.numeric(norm_data$ks),score)
par_k[["plot_fit_prot"]]<-plotFitProt(el$DPA,as.vector(norm_data$prot),par_k$prot_fit)
X<-matrice_sens(el$DPA,par_k[["solK"]][,1])
diff<-(par_k[["error"]][["errg"]][1]*norm(as.vector(norm_data$prot),"2"))^2
par_k[["corr_matrix"]]<-matrice_corr(X,length(norm_data$prot),diff)
if (!is.null(par_k)){
test_list[[cont]]$SOL<-par_k
# write.csv(test_list[[cont]][["SOL"]][["solK"]],paste("solK/",paste(test_list[[cont]][["Transcrit_ID"]],"_Sol_ks_kd.csv"),sep = ""))
}
},error=function(e){showNotification(paste0("Protein fitting not achieved for ",el$Transcrit_ID,sep=" "),type = "error",duration = NULL)})
}
valid_res<<-Filter(function(x) {length(x) > 5}, test_list)
print(valid_res[[1]])
mess<-showNotification(paste("Finished!!"),duration = NULL,type = "message")
en_but$enable<-TRUE
}
})
output$downFile<-downloadHandler(
filename = function(){
paste("results_KsKd-",Sys.Date(),".zip",sep="")
},
content = function(file){
owd <- setwd(tempdir())
on.exit(setwd(owd))
files <- NULL;
for (res in valid_res){
fileName<-paste(res[["Transcrit_ID"]],"_Sol_ks_kd.csv",sep = "")
write.csv(res[["SOL"]][["solK"]],fileName)
files<-c(files,fileName)
}
zip(zipfile = file,files = files)
if(file.exists(paste0(file, ".zip"))) {file.rename(paste0(file, ".zip"), file)}
},contentType = "application/zip"
)
observe({
if (en_but$enable){
enable("downFile")
}
})
}
I recently started using Shiny, so any help would be extremely appreciated. Thanks in advance!
Welcome to SO.
You've identified the problem yourself: "...are all named parN_sig". That means you don't have several inputs, you only have one. So you always get the value of (say) input$par2_sig from the first conditional panel regardless of which panel you're trying to access.
You have two options:
Provide unique names for every textInput. That will be a pain. Or...
Use modules. They take a bit of getting used to, but are worth the effrt in the end.
If you set the module up correctly, you'll be able to use the same module for each conditional panel, even though they have different numbers of textinputs.
See this page for help on creating your first module.

Exporting an Excel file from R with columns color coded?

When exporting the excel file the columns are not color coded. I am using the condformat package. i know that condformat creates a html file, i am using the condformat2excel command but its not color coding the columns i need. any help or tip are welcome thank you.
library(readxl)
library(condformat)
teast1 <- read_excel("Kenilworth_DataIssues-testR.xls",sheet = "Data Cleanup")
color_pick <- function(column) {
sapply(column,
FUN = function(value) {
if (value >= 25) {
return("red")
} else {
return("dodgerblue")
}
})
}
color_pick2 <- function(column2) {
sapply(column2,
FUN = function(value2) {
if (value2 >9000) {
return("red")
} else if(value2 < 8000) {
return("dodgerblue")
}else{
return("green")
}
})
}
condformat2excel(teast1,"flip.xlsx",sheet_name = "lebeon",overwrite_wb = FALSE,overwrite_sheet = TRUE)%>%
rule_fill_discrete("PlannedPremiseTime",expression = color_pick(PlannedPremiseTime),colours = identity)%>%
rule_fill_discrete("PostalCode",expression = color_pick2(PostalCode),colours = identity)

Change cell background of rHandsontable with afterChange event on client side

I'd like to change the background color of a handsontable cell after it's been edited by the user on the client side. The handsontable is defined through a Shiny application; so this is really a question about how to define event hooks in rHandsontable in the Shiny application. The general use case that I'm trying to accomplish is: users edit cell data; the background color change to indicate that it's been changed and is pending saving to a database; the change is passed back to Shiny's observeEvent(); the change is sent to an external database and saved; the the rHandsontable is redrawn on the output with default background coloring that removes the color set be the change. The result is the flicker indicates that data has been saved. And if there's a database connection error or other issue, the color will persist, indicating the data is unsaved. I've been able to accomplish a working example, pasted below.
Specific question: the hook is currently implemented using hot_col(1,renderer=change_hook) however, this isn't about rendering the cell, just a way that allows adding the hook. I assume hot_table() is the correct function, but can it be used to register events? More generally, is there a more built-in way of accomplishing this?
change_hook <- "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.hooks.add('afterChange', function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
cell = this.getCell(row,col);
cell.style.background = 'pink';
}
}
},instance);
Handsontable.renderers.TextRenderer.apply(this, arguments);
}"
ui <- div(width=300,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
hTable <- reactiveVal(df)
observeEvent(input$hTable, {
withProgress(message = "Saving changes to database...", value=0.5, {
Sys.sleep(1)
incProgress(1, detail = "done")
input_hTable <- hot_to_r(input$hTable)
hTable(input_hTable)
})
})
output$hTable <- renderRHandsontable({
rhandsontable(hTable(),stretchH="all",height=300) %>%
hot_col(1,renderer=change_hook)
})
}
shinyApp(ui, server)
After searching for a while, I have used the info from these pages (handsontable, htmlwidgets) to change the background color of an edited cell.
In the afterChange function, list of all changed cells will be kept. In the afterRender function the background color of those cells will be changed to yellow. In the afterLoadData function, the background color of changed cells will be reset to white and it will be emptied.
library(shiny)
library(rhandsontable)
change_hook <- "function(el,x) {
var hot = this.hot;
var cellChanges = [];
var changefn = function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
var cell = hot.getCell(row, col);
cell.style.background = 'pink';
cellChanges.push({'rowid':row, 'colid':col});
}
}
}
var renderfn = function(isForced) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'yellow';
}
}
var loadfn = function(initialLoad) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'white';
}
cellChanges = []
}
hot.addHook('afterChange', changefn);
hot.addHook('afterRender', renderfn);
hot.addHook('afterLoadData', loadfn);
} "
ui <- div(actionButton(inputId = "reset_button",label = "Reset")
,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
reset <- reactiveVal(0)
hTable <- reactiveVal(df)
output$hTable <- renderRHandsontable({
r = reset()
rht = rhandsontable(hTable(),reset=r,stretchH="all",height=300)%>%
hot_col('col1',readOnly=T)
reset(0)
htmlwidgets::onRender(rht,change_hook)
})
observeEvent(input$reset_button,
{
reset(1)
})
}
shinyApp(ui, server)
The code below changes the background color of a rhandsontable cell after it's been edited by a user.
library(shiny)
library(rhandsontable)
ui <- fluidPage(
titlePanel ('Give it a try and make changes!'),
mainPanel(rHandsontableOutput('table'))
)
server <- function(input, output, session) {
df <- data.frame(SYMBOLS = c('AAPL', 'ALRM', 'AMZN', 'BABA', 'CRM', 'CSCO',
'FB'),
NAMES = c('APPLE', 'ALARM.com', 'AMAZON', 'ALIBABA',
'SALESFORCE', 'CISCO', 'FACEBOOK'),
NOTE = c('sale', '', 'buy', '', '', '', 'watch'))
output$table <- renderRHandsontable(
rhandsontable(df))
rv <- reactiveValues(row = c(), col = c())
observeEvent(input$table$changes$changes, {
rv$row <- c(rv$row, input$table$changes$changes[[1]][[1]])
rv$col <- c(rv$col, input$table$changes$changes[[1]][[2]])
output$table <- renderRHandsontable({
rhandsontable(hot_to_r(input$table), row_highlight = rv$row,
col_highlight = rv$col) %>%
hot_cols(
renderer = "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
if (instance.params) {
hrows = instance.params.row_highlight;
hrows = hrows instanceof Array ? hrows : [hrows];
hcols = instance.params.col_highlight;
hcols = hcols instanceof Array ? hcols : [hcols];
}
for (let i = 0; i < hrows.length; i++) {
if (instance.params && hrows[i] == row && hcols[i] == col) {
td.style.background = 'pink';
}}
}"
)
})
})
}
shinyApp(ui, server)
Each time a cell is changed,
The row index of this cell (input$table$changes$changes[[1]][[1]]) and its column index (input$table$changes$changes[[1]][[2]]) are appended to the reactive variable rv$row and rv$col, respectively,
The rHandsontable is rendered again using the reactive variable (rv) that includes the indices of all the cells that have been changed so far, and finally,
A for loop in the javascript renderer goes through all the indices in rv one pair at a time against all cols and rows of the table. If there is a match in both row and col indices the background color of this cell is changed to pink.

Shiny: How to change the shape and/or size of the clicked point?

I would like to change the shape and size of the clicked point in the below plot. How to achieve it? For this toy plot, I have reduced the number of points from original 100k to 2k. So, the expected solution should be highly scalable and do not deviate from the original plot i.e., all the colors before and after the update of the click point should be the same.
library(shiny)
library(plotly)
df <- data.frame(X=runif(2000,0,2), Y=runif(2000,0,20),
Type=c(rep(c('Type1','Type2'),600),
rep(c('Type3','Type4'),400)),
Val=sample(LETTERS,2000,replace=TRUE))
# table(df$Type, df$Val)
ui <- fluidPage(
title = 'Select experiment',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("SelType", "Select Types to plot:",
choices = unique(df$Type),
selected = NA)
),
mainPanel(
plotlyOutput("plot", width = "400px"),
verbatimTextOutput("click")
)
)
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
if(length(input$SelType) != 0){
df <- subset(df, Type %in% input$SelType)
p <- ggplot(df, aes(X, Y, col = as.factor(Val))) +
geom_point()
}else{
p <- ggplot(df, aes(X, Y, col = as.factor(Val))) +
geom_point()
}
ggplotly(p) %>% layout(height = 800, width = 800)
})
output$click <- renderPrint({
d <- event_data("plotly_click")
if (is.null(d)) "Click events appear here (double-click to clear)"
else cat("Selected point associated with value: ", d$Val)
})
}
shinyApp(ui, server)
A related question has been asked here, but that approach of highlighting the point with a color does not work(when the number of levels of a variable is high, it is difficult to hard code a color which might be already present in the plot).
Plotly's restyle function won't help us here but we can still use the onclick event together with a little bit of JavaScript. The code has acceptable performance for 10,000 points.
We can get the point which was clicked on in JavaScript using:
var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
(scatterlayer is the layer where all the scatterplot elements are located,
scatter[n] is the n-th scatter plot and point[p] is the p-th point in it)
Now we just make this point a lot bigger (or whatever other shape/transformation you want):
point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
In order to get the possibility to revert everything, we store the unaltered info about the point together with the rest of the Plotly information:
var plotly_div = document.getElementsByClassName('plotly')[0];
plotly_div.backup = {curveNumber: data.points[0].curveNumber,
pointNumber: data.points[0].pointNumber,
d: point.attributes['d'].value
}
and later we can restore the point:
var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
old_point.setAttribute('d', plotly_div.backup.d);
Now we can add all the code to the plotly widget.
javascript <- "
function(el, x){
el.on('plotly_click', function(data) {
var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
var plotly_div = document.getElementsByClassName('plotly')[0];
if (plotly_div.backup !== undefined) {
var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
if (old_point !== undefined) {
old_point.setAttribute('d', plotly_div.backup.d);
}
}
plotly_div.backup = {curveNumber: data.points[0].curveNumber,
pointNumber: data.points[0].pointNumber,
d: point.attributes['d'].value,
style: point.attributes['style'].value
}
point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
});
}"
[...]
ggplotly(p) %>% onRender(javascript)
Alternatively you could make a new SVG element based on the location of the clicked point but in the color and shape you would like.
You can try it here without R/Shiny.
//create some random data
var data = [];
for (var i = 0; i < 10; i += 1) {
data.push({x: [],
y: [],
mode: 'markers',
type: 'scatter'});
for (var p = 0; p < 200; p += 1) {
data[i].x.push(Math.random());
data[i].y.push(Math.random());
}
}
//create the plot
var myDiv = document.getElementById('myDiv');
Plotly.newPlot(myDiv, data, layout = { hovermode:'closest'});
//add the same click event as the snippet above
myDiv.on('plotly_click', function(data) {
//let's check if some traces are hidden
var traces = document.getElementsByClassName('legend')[0].getElementsByClassName('traces');
var realCurveNumber = data.points[0].curveNumber;
for (var i = 0; i < data.points[0].curveNumber; i += 1) {
if (traces[i].style['opacity'] < 1) {
realCurveNumber -= 1
}
}
data.points[0].curveNumber = realCurveNumber;
var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
var plotly_div = document.getElementsByClassName('plotly')[0];
if (plotly_div.backup !== undefined) {
var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
if (old_point !== undefined) {
old_point.setAttribute('d', plotly_div.backup.d);
}
}
plotly_div.backup = {curveNumber: data.points[0].curveNumber,
pointNumber: data.points[0].pointNumber,
d: point.attributes['d'].value,
style: point.attributes['style'].value
}
point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv">

Problems with "observe" in Shiny

I am working in a ShinyApp which objective here is to create a selection based on attributes that are generated from an Excel database (the number of attributes may vary). Below is the most important part of my server.R code:
shinyServer(function(input, output, session){
seleciona_planilha <- observe({
dados = input$arquivo
if (is.null(dados))
{
return(NULL)
}
else
{
capturar = names(getSheets(loadWorkbook(dados$datapath)))
updateSelectInput(session,"planilha",choices = capturar)
}
})
carrega_dados <- reactive({
dados = input$arquivo
if (is.null(dados))
{
return(NULL)
}
else{return(read.xlsx(dados$datapath, sheetName = input$planilha))}
})
The first "observe" function above works well in order to select the correct sheet and proceed to analysis with the code below.
rotina <- reactive({
dados = carrega_dados()
tam_dados = length(dados)
pos_ini = 22
vet_comp = vector()
resultados = as.data.frame(matrix(nrow = length(seq(pos_ini,tam_dados,2)), ncol = 10))
nomes = names(dados)
cd = 4
k = 1
amostra = vector()
for(i in 1:length(dados[,1]))
{
if(dados[i,6] == 1)
{
amostra[1] = as.character(dados[i,7])
break
}
}
for(i in 1:length(dados[,1]))
{
if(dados[i,6] == 2)
{
amostra[2] = as.character(dados[i,7])
break
}
}
names(resultados) = c("Name",amostra[1], amostra[2],"NSD",
"Tau","Var(Tau)","D-Prime",
"Var(D-Prime)","IC(D-Prime)","p-value(D-Prime)")
for(i in seq(pos_ini,tam_dados,2))
{
cNSD = 0
c1 = 0
c2 = 0
for(j in 1:length(dados[,i]))
{
if(as.character(dados[j,i]) == " NSD" || as.character(dados[j,i]) == "NSD")
{
cNSD = cNSD + 1
}
if(as.character(dados[j,i]) == "1")
{
c1 = c1 + 1
}
if(as.character(dados[j,i]) == "2")
{
c2 = c2 + 1
}
}
vet_comp = c(c1,cNSD,c2)
resultados[k,1] = nomes[i]
resultados[k,2] = c1
resultados[k,3] = c2
resultados[k,4] = cNSD
resultados[k,5] = round(twoAC(vet_comp)$coefficients[1,1],cd)
resultados[k,6] = round((twoAC(vet_comp)$coefficients[1,2])^2,cd)
resultados[k,7] = round(twoAC(vet_comp)$coefficients[2,1],cd)
resultados[k,8] = round((twoAC(vet_comp)$coefficients[1,2])^2,cd)
if(vet_comp[1] != 0 && vet_comp[2] != 0 && vet_comp[3] != 0)
{
resultados[k,9] = paste("[",round(twoAC(vet_comp)$confint[,1],cd),";",
round(twoAC(vet_comp)$confint[,2],cd),"]")
}
else
{
resultados[k,9] = paste("No IC")
}
resultados[k,10] = round((twoAC(vet_comp)$p.value)/2,cd)
k = k + 1
}
return(resultados)
})
The reactive function "rotina" returns a data.frame. The first column in the data.frame are the attribute names that I would like to use in a selector.
But for some reason I don't know, when I call another "observe" function to get the attribute names and pass to the selector, it not works.
seleciona_atributo <- observe({
resultados = rotina()
atributos = resultados[,1]
updateSelectInput(session,"atributo",choices = atributos)
})
I tried to assign "resultados" as a global variable too, but with no success.
Finally, my ui.R code:
shinyUI(fluidPage(
titlePanel("2-AC Sensory Tool"),
sidebarLayout(
sidebarPanel(
fileInput('arquivo', 'Choose XLS/XLSX File',
accept=c('.xls','.xlsx')),
tags$hr(),
selectInput("planilha",label = h4("Select data sheet"),""),
tags$hr(),
selectInput("atributo",label = h4("Select attribute to generate d-prime graphic",""),
downloadButton('download', 'Download results')
),
mainPanel(
plotOutput("grafico_dp"),
plotOutput("grafico_dist"),
h4("Results Table"),
dataTableOutput("saida")
)
)
))
Thanks in advance!

Resources