robwschlegel / heatwaveR

This GitHub repo contains all of the code for the heatwaveR package.

Home Page:https://robwschlegel.github.io/heatwaveR/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Interactive visuals via plotly in Shiny R

MarMariano opened this issue · comments

Hello Robert,

I have been trying to create an interactive plot via plotly in my Shiny R application.

I have implemented the code example that you made available in the vignette "Basic Detection and Visualisation of Events - Interactive visuals" into Shiny R:

output$Serie_HW <- renderPlotly({

ts_res_sub <- Climatologia()[19200:19300,]
p <-
ggplot(data = ts_res_sub, aes(x = t, y = tMax)) +
  geom_line(aes(y = tMax)) +
  geom_line(aes(y = seas), colour = "green") +
  geom_line(aes(y = thresh), colour = "red") +
  geom_flame(aes(
    x = t,
    y = tMax,
    y2 = thresh,
    colour = "salmon"
  )) +
  labs(x = "", y = "Temperature (°C)") 
ggplotly(p)  })

You mention that geom_flame() should work with plotly starting from version v0.3.6.9002. However, when I run the code in Shiny R, the resulting plot does not highlight heatwave events with filling. Since, I am using heatwaveR_0.4.5 I do not think that this is the issue so I cannot figure out why geom_flame is not working in Shiny R renderPlotly.

Thanks,
Mariano

Hello Mariano,
The plotly package stopped receiving active support near the end of 2020 so I had to remove it as a dependency in the heatwaveR package. Below please find the function necessary to be able to use geom_flame() with ggplotly():

geom2trace.GeomFlame <- function (data,
                                  params,
                                  p) {
  
  x <- y <- y2 <- NULL
  
  # Create data.frame for ease of use
  data1 <- data.frame(x = data[["x"]],
                      y = data[["y"]],
                      y2 = data[["y2"]])
  
  # Grab parameters
  n <- params[["n"]]
  n_gap <- params[["n_gap"]]
  
  # Find events that meet minimum length requirement
  data_event <- heatwaveR::detect_event(data1, x = x, y = y,
                                        seasClim = y,
                                        threshClim = y2,
                                        minDuration = n,
                                        maxGap = n_gap,
                                        protoEvents = T)
  
  # Detect spikes
  data_event$screen <- base::ifelse(data_event$threshCriterion == FALSE, FALSE,
                                    ifelse(data_event$event == FALSE, TRUE, FALSE))
  
  # Screen out spikes
  data1 <- data1[data_event$screen != TRUE,]
  
  # Prepare to find the ploygon corners
  x1 <- data1$y
  x2 <- data1$y2
  
  # # Find points where x1 is above x2.
  above <- x1 > x2
  above[above == TRUE] <- 1
  above[is.na(above)] <- 0
  
  # Points always intersect when above=TRUE, then FALSE or reverse
  intersect.points <- which(diff(above) != 0)
  
  # Find the slopes for each line segment.
  x1.slopes <- x1[intersect.points + 1] - x1[intersect.points]
  x2.slopes <- x2[intersect.points + 1] - x2[intersect.points]
  
  # # Find the intersection for each segment.
  x.points <- intersect.points + ((x2[intersect.points] - x1[intersect.points]) / (x1.slopes - x2.slopes))
  y.points <- x1[intersect.points] + (x1.slopes * (x.points - intersect.points))
  
  # Coerce x.points to the same scale as x
  x_gap <- data1$x[2] - data1$x[1]
  x.points <- data1$x[intersect.points] + (x_gap*(x.points - intersect.points))
  
  # Create new data frame and merge to introduce new rows of data
  data2 <- data.frame(y = c(data1$y, y.points), x = c(data1$x, x.points))
  data2 <- data2[order(data2$x),]
  data3 <- base::merge(data1, data2, by = c("x","y"), all.y = T)
  data3$y2[is.na(data3$y2)] <- data3$y[is.na(data3$y2)]
  
  # Remove missing values for better plotting
  data3$y[data3$y < data3$y2] <- NA
  missing_pos <- !stats::complete.cases(data3[c("x", "y", "y2")])
  ids <- cumsum(missing_pos) + 1
  ids[missing_pos] <- NA
  
  # Get the correct positions
  positions <- data.frame(x = c(data3$x, rev(data3$x)),
                          y = c(data3$y, rev(data3$y2)),
                          ids = c(ids, rev(ids)))
  
  # Convert to a format geom2trace is happy with
  positions <- plotly::group2NA(positions, groupNames = "ids")
  positions <- positions[stats::complete.cases(positions$ids),]
  positions <- dplyr::left_join(positions, data[,-c(2,3)], by = "x")
  positions$PANEL <- positions$PANEL[stats::complete.cases(positions$PANEL)][1]
  positions$group <- positions$group[stats::complete.cases(positions$group)][1]
  
  # Run the plotly polygon code
  getFromNamespace("geom2trace.GeomPolygon", asNamespace("plotly"))(positions)
}

You should be able to copy and paste this function into your R session. Once it has been loaded into your environment the example you provided here should run. Please let me know if this works for you.
All the best,
-Robert

I have copied and pasted it into my Shiny App but still geom_flame() does not seem to be working. Does it have anything to do specifically with Shiny R?

The order of operations matters. So you need to have this bit of code posted into your shiny app right at the beginning before the UI and server are called. Put it in just after you call the necessary libraries for your shiny app.

I have done what you suggested but still it does not work. Is there still something missing? I mean do I still need to call your function inside renderplotly when I call geom_flame()?

Hello Mariano,
Without seeing the code for your shiny app I can't comment any further on the applicability of this code chunk for how you are trying to achieve the use of geom_flame() in a shiny app. But I can offer an example of how it is used in a shiny app I authored. The code for the shiny app is here for you to reverse engineer what you need: https://github.com/robwschlegel/MHWapp/tree/master/shiny

Specifically, the geom2trace.GeomFlame() function is found at this script from line 93: https://github.com/robwschlegel/MHWapp/blob/master/shiny/functions.R

This function is then loaded into the app via the global script found here on line 45: https://github.com/robwschlegel/MHWapp/blob/master/shiny/global.R

This is unfortunately a rather complex app. But the pieces you need are there. The important thing to consider is that geom2trace.GeomFlame() needs to be loaded after you load the plotly library and before any code that would create plotly figures.
Bonne chance,
-Robert

Thank you Robert,

I am focussing on something else at the moment so I have sidelined this task of turning my plots into interactive plots via plotly for the time being. I will get back to it eventually when I have to.
I am quite new to Shiny R so I still need to get my head around some unique features of Shiny.

Hello,
I understand how it goes. I'll close this issue for now.
Please re-open it in the future as necessary.
All the best,
-Robert