I have this app:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(includeScript("www/script.js"),
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(session, input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
observe({
if(input$bins > 25) {
Message1 = input$bins
session$sendCustomMessage("bla", Message1)
}
})
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
My Oberserver checks if the value is larger than 25. I send the value to Javascript.
$( document ).ready(function() {
Shiny.addCustomMessageHandler("bla", dosomething);
function dosomething(Message1) {
alert(Message1)
}
});
The code works perfectly, BUT every time i change the slider, the code seems to get executed one more time than before. After changing it 2 times, I get 3 alerts for example. Why is that happening and what can I do against it?
The reason this is so broken is that your observe() is inside the renderPlot() function. Generally speaking, observers should not be inside render functions, it's almost always a recipe for very strange undefined behaviours to happen!
Simply moving the observer outside of the render function fixes your problem. This also fixes another problem you didn't mention, that the alert box was actually showing the previous number rather than the current one.
For completeness, this is the correct server code:
server <- function(session, input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
observe({
if(input$bins > 25) {
Message1 = input$bins
session$sendCustomMessage("bla", Message1)
}
})
}
Related
Question
#This shinny app creates a search Bar feature which shows suggestions in real time as soon as user starts #typing in the search bar I don't have clear idea how the last part of the code works:
"
Any explanation of I() functions is used with dropdown and ontype in the last two lines of the "UI" part? is this R code or CSS code? any tutorial reference or readable source for such code will be appreciated.
library(shiny)
ui <- fluidPage(
title = "Search Bar",
fluidRow(
selectizeInput(
inputId = "searchme",
label = "Search Bar",
multiple = FALSE,
choices = c("Search Bar" = "", paste0(LETTERS,sample(LETTERS, 26))),
options = list(
create = FALSE,
placeholder = "Search Me",
maxItems = '1',
onDropdownOpen = I("function($dropdown) {if (!this.lastQuery.length) {this.close(); this.settings.openOnFocus = false;}}"),
onType = I("function (str) {if (str === \"\") {this.close();}}")
)
))
)
server <- function(input, output, session) {
# Show Selected Value in Console
observe({
print(input$searchme)
})
}
shinyApp(ui, server)
I was trying to create search bar and found this code but could not understand the last two lines of the "ui' Part very well. is this R code or CSS code? any tutorial reference or readable source for such code will be appreciated.
I am using conditionalPanel in the ui.R file. I want to compare if the given date in a selectinput before or after a certain date (30.09.2019) is.
my selectInput looks like:
selectInput(inputId = 'date',
label = 'Stichtag:',
choices = sub("([0-9]{4}).([0-9]{2}).([0-9]{2})", "\\3.\\2.\\1",sort(as.Date(
sub("([0-9]{2})([0-9]{2})([0-9]{4})KRB.csv", "\\1.\\2.\\3",
list.files('./data', full.names = FALSE,
recursive = FALSE)),format="%d.%m.%Y"),decreasing = T)
)),
and my conditionalPanel
conditionalPanel(
#condition = " input.date == '30.09.2019'", #(this works)
condition="Date.parse(input.date)>Date.parse(30.08.2019)", #(it dose not work)
## select the variables and order
pickerInput(
inputId = "assetclass",
label = "Assetklassen:",
choices = c(sort(unique(bestand.name))),
sort(unique(bestand.name)),
multiple = T
) ),
in the code above you see 2 conditions. The first one
condition = " input.date == '30.09.2019'"
works but is not the general smart solution because I will every 3 months I will have an additional date.
Therefore I am looking for a general solution like
condition="Date.parse(input.date)>Date.parse(30.08.2019)"
I know that I have to use Js. But it dose not work!
Addendum: I tried to see the format of the input date in server.R
Browse[2]> input$date
[1] "30.09.2019"
so I have probably to transforme the strings in dates befor I compare them in JS!?
I tried just for fun the following statement:
condition= "new Date('2013-05-23') > new Date('2013-05-24')",
however, it dose not work!
I know that you are asking about how to compare dates in conditionalPanel, but whatever you are trying to do, it will be much easier using renderUI on the server side.
From your question I assume that you have some quarterly reports running and that with the changes of the quarter you want to display different filters / selectInputs.
Below I show a toy example which checks if the chosen input$date is equal to end date of last quarter (round_date(Sys.Date(), "quarter") - days(1))).
Note, that I added library calls to stringr and lubridate.
I further made up a character vector of csv files names, since I cannot reproduce the code you provide.
library("shiny")
library("shinyWidgets")
library("lubridate")
library("stringr")
# made up character vector of csv file names
date_vec <- c("30092019KRB.csv",
"31082019KRB.csv",
"31072019KRB.csv",
"30062019KRB.csv",
"31052019KRB.csv",
"30042019KRB.csv")
shinyApp(
ui = fluidPage( # user interface
sidebarLayout( # layout with Sidebar
sidebarPanel( # input sidebarPanel
selectInput(inputId = 'date',
label = 'Stichtag:',
choices = sub("([0-9]{4}).([0-9]{2}).([0-9]{2})",
"\\3.\\2.\\1",
sort(as.Date(sub("([0-9]{2})([0-9]{2})([0-9]{4})KRB.csv",
"\\1.\\2.\\3",
# below date_vec replaces your list.files() call
date_vec),
format="%d.%m.%Y"),
decreasing = T)
)
) ,
uiOutput("classes")
), # closes sidebarPanel
mainPanel( # Output in mainPabel
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
output$classes <- renderUI({
# example condition: if input$date is equal to the date of the actual quarter minus 1 day then...
if(dmy(str_remove(input$date, "KRB.csv")) == (round_date(Sys.Date(), "quarter") - days(1))) {
# use show this pickerInput ....
pickerInput(
inputId = "assetclass",
label = "Assetklassen:",
choices = c("class a", "class b", "class c"),
multiple = T
)
# otherwise show this pickerInput ...
} else {
pickerInput(
inputId = "equity",
label = "Equity classes:",
choices = c("class d", "class e", "class f"),
multiple = T
)
}
})
}
) # closes shinyApp
If you prefer conditionalPanel you can build on the approach from Udit (below), but instead writing a JS function you could bring your input vector into the right format and use it as is.
However, if you use the input vector later on the server side you would need to bring it into the old format with some string manipulation.
library("shiny")
library("shinyWidgets")
# made up character vector of csv file names
date_vec <- c("30092019KRB.csv",
"31082019KRB.csv",
"31072019KRB.csv",
"30062019KRB.csv",
"31052019KRB.csv",
"30042019KRB.csv")
choice_vec <- gsub("[-]",
"/",
sort(as.Date(sub("([0-9]{2})([0-9]{2})([0-9]{4})KRB.csv",
"\\3.\\2.\\1",
# below date_vec replaces your list.files() call
date_vec),
format="%Y.%m.%d"),
decreasing = T)
)
names(choice_vec) <- sub("([0-9]{4}).([0-9]{2}).([0-9]{2})",
"\\3.\\2.\\1",
sort(as.Date(sub("([0-9]{2})([0-9]{2})([0-9]{4})KRB.csv",
"\\1.\\2.\\3",
# below date_vec replaces your list.files() call
date_vec),
format="%d.%m.%Y"),
decreasing = T)
)
shinyApp(
ui = fluidPage( # user interface
sidebarLayout( # layout with Sidebar
sidebarPanel( # input sidebarPanel
selectInput(inputId = 'date',
label = 'Stichtag:',
choices = choice_vec
) ,
conditionalPanel(
condition = "new Date('2019/09/30') > new Date(input.date)",
pickerInput(
inputId = "assetclass",
label = "Asset casses:",
choices = c("class a", "class b", "class c"),
multiple = T
)
)
), # closes sidebarPanel
mainPanel( # Output in mainPabel
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
}
) # closes shinyApp
this is how you can convert your date to a valid date, you might consider making a function out of it:
var date = "30.09.2019"
var arr = date.split(".")
var array = Array.from(arr);
array.reverse();
array.join("/"); // this will return a valid date
Try Comparing like following
new Date(input.date) > new Date("2019/09/30")
following is the function which would give you converted Date
function convertDate(data){
var arr = data.split(".")
var array = Array.from(arr);
var converted = array.reverse().join("/")
return converted;
}
Now where you are comparing just use you normal comparison method
new Date() > new Date(convertDate("30.09.2019"))
and it will return if it is true or not.
I hope this would help you.
I'm dealing with a callback method to create a line chart on the go, given a specific dataframe.
def Total_value(DF):
return pd.DataFrame(pd.DataFrame(DF)['FinalSalePrice'].
groupby(level=0, group_keys=False).
apply(lambda x: x.sort_values(ascending=False).head(15))).reset_index()
def TOP_Item(data):
return np.array(data.ItemCode.value_counts()[data.ItemCode.value_counts() > 20].index)
def figure_creator(arr,l):
# colors = ["#%06x" % random.randint(0,0xFFFFFF) for c in range(len(arr))]
fig = figure(plot_width=1000, plot_height=300,x_axis_type='datetime')
for item in arr:
fig.line(l[l.ItemCode == item].ServicedOn.unique(),l[l.ItemCode == item][np.int(0)], line_width=2)
# fig.add_tools(HoverTool(show_arrow=False,
# line_policy='nearest',
# tooltips=None))
return fig
at the very end I call:
show(figure_creator(TOP_Item(Total_value(SER_2016)),Total_value(SER_2016)))
I want to add a Hovertool which could Highlight the given chart and also display the label for the line.
The DataFrame for these is quite big, hence I can't upload it Here.
But the premise of each of the function is explained below:
Total_value: is used to calculate the total value of money, each unique item in the dataframe has made,sort them, and take only the top 15 items.
Top_Item: is used to calculate which of the 15 items has appeared more than 20 times for a 14 day period in a year(there are 25ish, 14 day periods in a year). Further return the list of the items.
fig_creator: creates a line for each of returned item.
**
Is there a way to create a callback method on the hovertool(commented out) per new line that is being generated ?
I figured it out using select tool. Posting for others who might run into a similar problem.
def figure_creator(arr,l):
# colors = ["#%06x" % random.randint(0,0xFFFFFF) for c in range(len(arr))]
fig = figure(plot_width=1000, plot_height=300,x_axis_type='datetime',tools="reset,hover")
for item in arr:
# dicta
fig.line(l[l.ItemCode == item].ServicedOn.unique(),l[l.ItemCode == item][np.int(0)], line_width=2,alpha=0.4,
hover_line_color='red',hover_line_alpha=0.8)
fig.select(dict(type=HoverTool)).tooltips = {"item":item}
# fig.add_tools(HoverTool(show_arrow=False,
# line_policy='nearest',
# tooltips=None))
return fig
This renders:
I am in the circuit board manufacturing industry, and we measure the temperature at a variety of locations on our circuit boards in an effort to identify if certain components are exceeding their required temperatures.
I have some exposure to .js visualization libraries, RStudio and Shiny. I would like to implement this application into an existing R or ShinyDashboard that I am developing.
What I would like to accomplish is to have a 2d or 3d image of my circuit board, and a heatmap that takes the maximum temperature and shows it on that image. My data sets have columns of temperatures over time for up to 20 different locations.
I know this is a crude description, but I was wondering if anyone has any initial suggestions to accomplish this?
Update1
This is the result of running the first code:
Update2
Here is the sample dataset that I would like to base this heatmap off of.
https://docs.google.com/spreadsheets/d/11I19uQyND7YehKrYd-NPiib4bQSHmHmWENFvausigvU/edit?usp=sharing
You could use ggplot for something like this, for example:
library(grid)
library(ggplot2)
# Download image
library(jpeg)
download.file("http://www.expresspcb.com/wp-content/uploads/2015/06/PhotoProductionPCB_TL_800.jpg","pcb.jpg")
img <- readJPEG("/home/oskar/pcb.jpg")
## Load image, use this if you can't download image
#library(png)
#img <- readPNG(system.file("img", "Rlogo.png", package="png"))
g <- rasterGrob(img, interpolate=TRUE,width=1,height=1)
coords <- data.frame("x"=c(0,1),"y"=c(0,1))
# Simulate data
df <- data.frame("x.pos" = c(runif(200),runif(20,min=0.5,max=0.8)),
"y.pos" = c(runif(200),runif(20,min=0.5,max=0.8)),
"heat" = c(runif(200),runif(20,min=0.7,max=1)))
# Show overlay of image and heatmap
ggplot(data=df,aes(x=x.pos,y=y.pos,fill=heat)) +
annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
stat_density2d( alpha=0.2,aes(fill = ..level..), geom="polygon" ) +
scale_fill_gradientn(colours = rev( rainbow(3) )) +
scale_x_continuous(expand=c(0,0)) +
scale_y_continuous(expand=c(0,0))
# Show where max temperature is
dat.max = df[which.max(df$heat),]
ggplot(data=coords,aes(x=x,y=y)) +
annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
geom_point(data=dat.max,aes(x=x.pos,y=y.pos), shape=21,size=5,color="black",fill="red") +
geom_text(data=dat.max,aes(x=x.pos,y=y.pos,label=round(heat,3)),vjust=-1,color="red",size=10)
The ggplot image part is from here
You can also bin the data manually and overlay it on the image like this (run this part after the script above):
# bin data manually
# Manually set number of rows and columns in the matrix containing sums of heat for each square in grid
nrows <- 30
ncols <- 30
# Define image coordinate ranges
x.range <- c(0,1) # x-coord range
y.range <- c(0,1) # x-coord range
# Create matrix and set all entries to 0
heat.density.dat <- matrix(nrow=nrows,ncol=ncols)
heat.density.dat[is.na(heat.density.dat)] <- 0
# Subdivide the coordinate ranges to n+1 values so that i-1,i gives a segments start and stop coordinates
x.seg <- seq(from=min(x.range),to=max(x.range),length.out=ncols+1)
y.seg <- seq(from=min(y.range),to=max(y.range),length.out=nrows+1)
# List to hold found values
a <- list()
cnt <- 1
for( ri in 2:(nrows+1)){
for ( ci in 2:(ncols+1)){
# Get current segments, for example x.vals = [0.2, 0.3]
x.vals <- x.seg [c(ri-1,ri)]
y.vals <- y.seg [c(ci-1,ci)]
# Find which of the entries in the data.frame that has x or y coordinates in the current grid
x.inds <- which( ((df$x.pos >= min(x.vals)) & (df$x.pos <= max(x.vals)))==T )
y.inds <- which( ((df$y.pos >= min(y.vals)) & (df$y.pos <= max(y.vals)))==T )
# Find which entries has both x and y in current grid
inds <- which( x.inds %in% y.inds )
# If there's any such coordinates
if (length(inds) > 0){
# Append to list
a[[cnt]] <- data.frame("x.start"=min(x.vals), "x.stop"=max(x.vals),
"y.start"=min(y.vals), "y.stop"=max(y.vals),
"acc.heat"=sum(df$heat[inds],na.rm = T) )
# Increment counter variable
cnt <- cnt + 1
}
}
}
# Construct data.frame from list
heat.dens.df <- do.call(rbind,a)
# Plot again
ggplot(data=heat.dens.df,aes(x=x.start,y=y.start)) +
annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
geom_rect(data=heat.dens.df, aes(xmin=x.start, xmax=x.stop, ymin=y.start, ymax=y.stop, fill=acc.heat), alpha=0.5) +
scale_fill_gradientn(colours = rev( rainbow(3) )) +
scale_x_continuous(expand=c(0,0)) +
scale_y_continuous(expand=c(0,0))
Coordinate conversion from your data to my format can be done like:
sensor.data <- read.csv("~/Sample_Dataset.csv - Sample_Dataset.csv.csv")
# Create position -> coord conversion
pos.names <- names(sensor.data)[ grep("*Pos",names(sensor.data)) ] # Get column names with "Pos" in them
mock.coords <<- list()
lapply(pos.names, function(name){
# Create mocup coords between 0-1
mock.coords[[name]] <<- data.frame("x"=runif(1),"y"=runif(1))
})
# Change format of your data matrix
df.l <- list()
cnt <- 1
for (i in 1:nrow(sensor.data)){
for (j in 1:length(pos.names)){
name <- pos.names[j]
curr.coords <- mock.coords[[name]]
df.l[[cnt]] <- data.frame("x.pos"=curr.coords$x,
"y.pos"=curr.coords$x,
"heat" =sensor.data[i,j])
cnt <- cnt + 1
}
}
# Create matrix
df <- do.call(rbind, df.l)
Trying to use one of the javascript addons for leaflet - specifically the heatmap functionality - https://github.com/Leaflet/Leaflet.heat
Thing is - I want to incorporate this into Shiny, but leaflet for R doesn't seem to have this addon included by default, so I would have to somehow include this JS manually. The closest I got to figuring out how to do this is through a post on rCharts that showed this:
server.R.
HeatMap$addAssets(jshead = c("http://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js"))
HeatMap$setTemplate(afterScript = sprintf("<script>
var addressPoints = %s
var heat = L.heatLayer(addressPoints).addTo(map)
</script>",
rjson::toJSON(dt)))
(taken from: https://github.com/ramnathv/rCharts/issues/498 )
But being not too familiar with JS, and new to leaflet it's still not quite clear how it can be incorporated from beginning to end - i.e. taking this JS from github and ending up with a heatmap created using leaflet on the dataset 'quakes'.
My server side code is something like the following:
library(leaflet)
output$mymap <- renderLeaflet({
leaflet() %>%
addProviderTiles("OpenMapSurfer.Roads",
options = providerTileOptions(noWrap = TRUE))
%>% addMarkers(clusterOptions = markerClusterOptions(), data = quakes))
Where instead of clusterOptions I would like to add a heatmap of the magnitude of the earthquakes (the dataset 'quakes' is included in R so you can see it for yourself).
Any help on figuring this out would be very much appreciated! :)
The setTemplate(afterscript...) bit doesn't work in shiny. Instead you need to use tags$() and render the heatmap output separately to the map.
Here is a basic app that uses heatmaps (inspired by this SO answer )
server.R
library(shiny)
library(rCharts)
dat <- data.frame(Offence = c("Assault","Assault","Assault","Weapon","Assault","Burglary"),
Date = c("2015-10-02","2015-10-03","2015-10-04","2015-04-12","2015-06-30","2015-09-04"),
Longitude = c(-122.3809, -122.3269, -122.3342, -122.2984, -122.3044, -122.2754),
Latitude = c(47.66796,47.63436,47.57665,47.71930,47.60616,47.55392),
intensity = c(10,20,30,40,50,30000))
shinyServer(function(input, output, session) {
output$baseMap <- renderMap({
baseMap <- Leaflet$new()
baseMap$setView(c(47.5982623,-122.3415519) ,12)
baseMap$tileLayer(provider="Esri.WorldStreetMap")
baseMap
})
output$heatMap <- renderUI({
## here I'm creating the JSON through 'paste0()'.
## you can also use jsonlite::toJSON or RJSONIO::toJSON
j <- paste0("[",dat[,"Latitude"], ",", dat[,"Longitude"], ",", dat[,"intensity"], "]", collapse=",")
j <- paste0("[",j,"]")
j
tags$body(tags$script(HTML(sprintf("
var addressPoints = %s
var heat = L.heatLayer(addressPoints).addTo(map)"
, j
))))
})
})
ui.R
library(shiny)
library(rCharts)
shinyUI(fluidPage(
mainPanel(
headerPanel("title"),
chartOutput("baseMap", "leaflet"),
tags$style('.leaflet {height: 500px;}'),
tags$head(tags$script(src="http://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js")),
uiOutput('heatMap')
)
))
Edit - Using Google Maps
There is also a way to do this in the development version of googleway. For this you'll need a valid Google API key, and currently it only works in a browser
## devtools::install_github("googleway")
library(googleway)
library(magrittr)
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
actionButton(inputId = "traffic", label = "traffic"),
box(width = 10,
height = 600,
google_mapOutput("myMap")
)
)
)
server <- function(input, output){
map_key <- "your_valid_api_key"
## https://developers.google.com/maps/documentation/javascript/get-api-key
## set up some data-------
pl <- "~s|dF}{~rZnNoExBq#|#SfAIjA#~Et#fBBp#Iv#QxCoArNqGfA_#dB]`KgAfVkC|Gu#rAYf#Q|#i#p#m#n#{#^u#`#kAR_ALiADuACiAIeAOy#_#qA{#uB{#sB]gAUmAOaB?oCTkKr#kZZiN?s#Cq#EQDOLILFn#A\\CpI_A|AQjB[BGPOX#LHz#CpAKT?v#KpHu#vD]LGt#Ix#I\\QBGLOVCPJd#Dj#GnFq#`PaBp#KfBQzA[zAq#nAaAx#aA~ByDp#yAXe#VSVO#EVWPCRDJLBF#Hd#TrDj#rK`ADEJGJ#JFBFrSxBJOPCNHHPdBLnCb#bBb#lAf#zA~#lAbApAzAt#nAxA|C~BhHrAxD~AtEb#|#xAtBpBlBzCbB`AZhIhBrFpA|AZl#HRDLENGXORe#DKJSf#wD`#cDt#}INq#ZuEt#mHfBsN~BkS`CmR\\eDnAiKzAcM`CePNmAhAsGXmArAgFtDsM|DaOh#sC^kCf#kDb#uDl#kI\\sHn#yM?gDEoAOsA[}BUiBUsC#qCNuBViBrCcPp#oGHW|#oPBuDI_DKqAy#wD{Ja^}#oFY_CWoDIqBGqEBsENqE`C{^JuA\\aDj#oDn#cDxAcFz#yBtC{Fp#eAn#_An#s#t#}#j#g#bCaBtCsA`GiAzBm#`C}#jBmA~CiC~DcDjCwAfAa#bBe#nBa#pCYlCArDBlCHhCGnC_#~A]vBk#hAa#lF_CnMaGbDeArD}#vB[zEe#jFS`GFfBFxBJzO\\zZfAfCJdEPbDNvDRnEHvD?tEE~BQhC[zAYnCu#bA]dBm#bIkDtBy#bAYhB[rDYxJ[nB#vAHfBLbCf#|C~#vAp#nCdB|A`A`CzApAr#|Al#rBl#bBZbUbCZBzBDvBEtAMnF_AvB[vBOlCAlBFnBXbDr#~Bv#z#`#bBfAdD~BtB`Bv#f#nAn#x#ZZJ~A\\dBTdADtBEbAGnEg#dFi#`DYdDQdF?|DNfCV`BTlCl#dNvD`HnBdLvClAZn#DzB^hCRd#?fA?|#Ih#O`#Ud#a#h#w#\\u#Pm#Lw#HoBq#qK]eLUcIE{DC{AD}Fn#eSLeCJs#RwFRkDf#sCj#aE`AsFhAuGh#gDt#wEp#}En#_FPeBRkDByBCgBEgAS}B{#oEsA}Dy#eCi#yBGq#?s#Ds#V}#Rg#r#u#ZOj#Ml#Az#PrA^fBb#j#HV#f#e#`B}AbB_B]Ie#KeASiO}CmH_B{L}Bk#QTqBTgCAm#g#kCSaAs#V{CdAmDrAuAh#{#Ra#H{#D{Af#wBt#gAb#]ReBl#"
df_line <- decode_pl(pl)
set.seed(123)
df_line$weight <- runif(nrow(df_line), min = 1, max = 100)
## ------------
## plot the map
output$myMap <- renderGoogle_map({
google_map(key = map_key, data = df_line, search_box = F) %>%
add_heatmap(weight = "weight") %>%
add_traffic()
})
}
shinyApp(ui, server)