Fixing Cluttered Titles on Graphs
Asked Answered
H

2

6

I made the following 25 network graphs (all of these graphs are copies for simplicity - in reality, they will all be different):

library(tidyverse)
library(igraph)


set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))

relations = data.frame(tibble(
  from = sample(data$d),
  to = lead(from, default=from[1]),
))

data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )

graph = graph_from_data_frame(relations, directed=T, vertices = data) 

V(graph)$color <- ifelse(data$d == relations$from[1], "red", "orange")

plot(graph, layout=layout.circle, edge.arrow.size = 0.2, main = "my_graph")

library(visNetwork)

    a = visIgraph(graph)  

m_1 = 1
m_2 = 23.6

 a = toVisNetworkData(graph) %>%
    c(., list(main = paste0("Trip ", m_1, " : "), submain = paste0 (m_2, "KM") )) %>%
    do.call(visNetwork, .) %>%
    visIgraphLayout(layout = "layout_in_circle") %>% 
    visEdges(arrows = 'to') 



y = x = w = v = u = t = s = r = q  = p = o = n = m = l = k = j = i = h = g = f = e = d = c = b = a

I would like to "tile" them as 5 x 5 : Since these are interactive html plots - I used the following command:

library(manipulateWidget)
library(htmltools)

ff = combineWidgets(y , x , w , v , u , t , s , r , q  , p , o , n , m , l , k , j , i , h , g , f , e , d , c , b , a)

htmltools::save_html(html = ff, file = "widgets.html")

I found out how to add a zoom option for each individual graph:

 a = toVisNetworkData(graph) %>%
    c(., list(main = paste0("Trip ", m_1, " : "), submain = paste0 (m_2, "KM") )) %>%
    do.call(visNetwork, .) %>%
    visIgraphLayout(layout = "layout_in_circle") %>%  
    visInteraction(navigationButtons = TRUE) %>% 
    visEdges(arrows = 'to') 

y = x = w = v = u = t = s = r = q  = p = o = n = m = l = k = j = i = h = g = f = e = d = c = b = a

ff = combineWidgets(y , x , w , v , u , t , s , r , q  , p , o , n , m , l , k , j , i , h , g , f , e , d , c , b , a)

htmltools::save_html(html = ff, file = "widgets.html")

[![enter image description here][1]][1]

But now the "zoom" options and "titles" have "cluttered" all the graphs!

I was thinking it might be better to "stack" all these graphs on top of each other and save each graph as a "group type" - and then hide/unhide as we please:

visNetwork(data, relations) %>% 
 visOptions(selectedBy = "group")
  • Can we put all 25 graphs on one page and then "zoom" into each individual graph to view it better (e.g. have only one set of zoom/navigation buttons in the corner of the screen that works for all graphs)?

  • Is there a way to stop the titles from overlapping with the graphs?

  • Can we put all 25 graphs on one page and then "hide" individual graphs by "checking" an option menu button? (like the last example on this page: https://datastorm-open.github.io/visNetwork/options.html)

Here are the possible solutions I have thought of for this problem:

  • Option 1: (a single zoom/navigation option for all graphs and no cluttered labels)

  • Option 2: (In the future, each "trip" will be different - "trips" will contain the same nodes, but have different edge connections and different titles/subtitles.)

I know that this style of selection ("Option 2") can be made using the following code:

nodes <- data.frame(id = 1:15, label = paste("Label", 1:15),
 group = sample(LETTERS[1:3], 15, replace = TRUE))

edges <- data.frame(from = trunc(runif(15)*(15-1))+1,
 to = trunc(runif(15)*(15-1))+1)



visNetwork(nodes, edges) %>% 
    visOptions(selectedBy = "group")

But I am not sure how to adapt the above code for a pre-existing set of "visNetwork" graphs. For example, suppose I already have "visNetwork" graphs "a, b, c, d, e" - how can I "stack them on top of each other" and "shuffle through them" with a "select menu" like in the above code?

[![enter image description here][4]][4]

Can someone please show me a way of addressing this clutter problem using Option 1 and Option 2?

Thank you!

Homothermal answered 23/2, 2022 at 22:42 Comment(7)
I'm curious if you'd be open to using something like a dashboard? I think that will give you a lot more flexibility. Shiny is another option. How much space will the final rendering inhabit? I can make my viewer as big as I would like, but that doesn't tell me how you'll use it.Integrant
Hi Kat! Can the dashboard be saved as an "html" file? Ideally, I would like the final rendering to be less than 10 MB (so I can attach in an email). Can dashboards be used for Option 1 and Option 2? (btw I thought what I was already doing were dashboards - just checked this online, im wrong LOL)Homothermal
Dashboards are definitely best rendered in HTML. Yes and yes (for the options). I'll work down a solution with a dashboard.Integrant
Thank you so much Kat! I have started reading more about dashboards in R ... what packages are you using for these? Thank you so much for your help, support and kindness - it's very generous of you!Homothermal
I'm thinking RMarkdown and Flexdashboard. There are a lot of great options out there, though. If you haven't worked with RMarkdown very much, it's a whole new animal. The fact that you can program in multiple languages in the same script file...that's pretty amazing if you ask me!Integrant
As always, thank you so much Kat for all your suggestions and advice! I am working on this as well...right now, I am thinking of just manually copying/pasting different graphs from R into microsoft paint LOLHomothermal
I'm sorry I saw that you had a good answer and didn't look at it further. I can add my answer. I will finish it up and add it to the question.Integrant
I
1

The sizing works, but at first glance, it looks like it doesn't. It's not ready, though.

When you select options, it doesn't trigger the auto-resize functionality within the canvases.

The auto-resize of the graph objects works just fine. (You'll see in the gif.)

The Viewer pane in RStudio is not the best way to check the knitted file. Look at it in a browser after knitting...especially if you want to make changes. It appears as if sometimes it thinks that all of RStudio is the container size, and you get graphs running off the screen. I'm sure it's how I have it coded, but that doesn't appear to be an issue in Safari or Chrome (I didn't check the other browsers).

I have tried to trigger the resizing of the canvas many different ways. This code may have some redundancies from attempts to trigger a resize/zoom extent of the canvases. (I think I deleted all of the things that didn't work.) Perhaps with this, someone else can figure that part out.

I used some Shiny code, but this is not using a Shiny runtime. Essentially the static work is R, but dynamic elements cannot be in R (i.e., resizing events, reading selections, etc.).

In the libraries I used, I called shinyRPG. I added and commented out package installation code because that package isn't a Cran package. (It's on Github.)

Assumptions I've made in coding (and this answer):

  • You have working knowledge of Rmarkdown.
  • There are 25 of these network diagrams.
  • There are no other HTML widgets in the script.

If these are not true, let me know.

The YAML

The Output Options

---
title: "Just for antonoyaro8"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
---

The Styles

This code goes between the YAML and the first R code chunk. In the regular text area of the RMD–not in an R chunk.

<style>
select {
  // A reset of styles, including removing the default dropdown arrow
  appearance: none;
  background-color: transparent;
  border: none;
  padding: 0 1em 0 0;
  margin: 0;
  width: 100%;
  font-family: inherit;
  font-size: inherit;
  cursor: inherit;
  line-height: inherit;
}
.select {
  display: grid;
  grid-template-areas: "select";
  align-items: center;
  position: relative;
  min-width: 15ch;
  max-width: 100ch;
  border: 1px solid var(--select-border);
  border-radius: 0.25em;
  padding: 0.25em 0.5em;
  font-size: 1.25rem;
  cursor: pointer;
  line-height: 1.1;
  background-color: #fff;
  background-image: linear-gradient(to top, #f9f9f9, #fff 33%);
}
select[multiple] {
  padding-right: 0; 
  /* Safari will not show options unless labels fit   */
  height: 50rem;   // how many options show at one time
  font-size: 1rem;
}
#column-1 > div.containIt > div.visNetwork canvas {
  width: 100%;
  height: 80%;
}
.containIt {
  display: flex;
  flex-flow: row wrap;
  flex-grow: 1;
  justify-content: space-around;
  align-items: flex-start;
  align-content: space-around;
  overflow: hidden;
  height: 100%;
  width: 100%;
  margin-top: 2vw;
  height: 80vh;
  widhth: 80vw;
  overflow: hidden;
}

</style>

Libraries

The first R chunk is next. You don't have to set echo = F in flexdashboard.

```{r setup, include=FALSE}

library(flexdashboard)
library(visNetwork)
library(htmltools)
library(igraph)
library(tidyverse)
library(shinyRPG) # remotes::install_github("RinteRface/shinyRPG")

```

R Code to Create the Diagrams

This next part is essentially your code. I changed a few things in the final version of the call to create the vizNetwork.

```{r dataStuff}

set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))

relations = data.frame(tibble(
  from = sample(data$d),
  to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )

graph = graph_from_data_frame(relations, directed=T, vertices = data) 

#red circle: starting point and final point
V(graph)$color <- ifelse(data$d == relations$from[1], "red", "orange")

a = visIgraph(graph)  

m_1 = 1
m_2 = 23.6

a = toVisNetworkData(graph) %>%
  c(., list(main = paste0("Trip ", m_1, " : "), 
            submain = paste0 (m_2, "KM") )) %>%
  do.call(visNetwork, .) %>%
  visIgraphLayout(layout = "layout_in_circle") %>% 
  visEdges(arrows = 'to')

# collect the correct order
df2 <- data %>% 
  mutate(d = as.numeric(d),
         nuname = factor(a$x$edges$from, 
                         levels = unlist(data$name))) %>%
  arrange(nuname) %>% 
  select(d) %>% unlist(use.names = F)
#  [1] 11  5  2  8  7  6 10 14 15  4 12  9 13  3  1 
V(graph)$name = data$label = paste0(df2, "\n", data$name)
a = visIgraph(graph)  

m_1 = 1
m_2 = 23.6
a = toVisNetworkData(graph) %>%
  c(., list(main = list(text = paste0("Trip ", m_1, " : "), 
                        style = "font-family: Georgia; font-size: 100%; font-weight: bold; text-align:center;"),
            submain = list(text = paste0(m_2, "KM"),
                           style = "font-family: Georgia; font-size: 100%; text-align:center;"))) %>%
  do.call(visNetwork, .) %>%
  visInteraction(navigationButtons = TRUE) %>%
  visIgraphLayout(layout = "layout_in_circle") %>% 
  visEdges(arrows = 'to') %>% 
  visOptions(width = "100%", height = "80%", autoResize = T)

a[["sizingPolicy"]][["knitr"]][["figure"]] <- FALSE

y = x = w = v = u = t = s = r = q  = p = o = n = m = l = k = j = i = h = g = f = e = d = c = b = a

```

The Multi-Select Box

Between the last chunk and before the next chunk of code is where this next part goes. This creates the left column, where the multi-select box is. (This is not in a code chunk.)

Column {data-width=200}
-----------------------------------------------------------------------

### Select Options

You can select one or more options from the list. 

No to build the select box and append the function that will trigger changes. This part will require modification. Name the options that the user sees on the screen here. (letters[1:25] in this code.)

Your object names do not have to match the names you have here. They do need to be in the same order, though.

```{r selectiver}
tagSel <- rpgSelect(
  "selectBox",                      # don't change this (connected)
  "Selections:",                    # visible on HTML; change away or set to ""
  c(setNames(1:25, letters[1:25])), # left is values, right is labels
  multiple = T                      # all multiple selections
)        # other attributes controlled by css at the top

tagSel$attribs$class <- 'select select--multiple'       # connect styles
tagSel$children[[2]]$attribs$class <- "mutli-select"    # connect styles
tagSel$children[[2]]$attribs$onchange <- "getOps(this)" # connect the JS function

tagSel

```

The Network Diagrams

Then between the previous chunk and the next chunk (not in a chunk):

Column
-----------------------------------------------------------------------

<div class="containIt">

Now call your graphs.

```{r notNow, include=T}

a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y

```

Close the div tag after that chunk:

</div>

Final Chunk: Javascript

This started out nice and neat...but after a lot of trial and error–WYSIWYG. Effective commenting fizzled out somewhere along the way, too. If there are questions as to what does what, let me know.

This chunk won't do anything if you run the chunk in R Markdown (while in the Source pane). To execute JS, you have to knit.

```{r pickMe,results='asis',engine='js'}

//remove inherent knitr element-- after using mutlti-select starts harboring space
byeknit = document.querySelector('#column-1 > div.containIt > div.knitr-options');
byeknit.remove(1);

// Reset Sizing of Widgets
h = document.querySelector('#column-1 > div.containIt').clientHeight;
w = document.querySelector('#column-1 > div.containIt').clientWidth;
hw = h * w;

cont = document.querySelectorAll('#column-1 > div.containIt > div');

newHeight = Math.floor(Math.sqrt(hw/cont.length)) * .85;

for(i = 0; i < cont.length; ++i){
  cont[i].style.height = newHeight + 'px';
  cont[i].style.width = newHeight + 'px';
  cn = cont[i].childNodes;
  if(cn.length > 0){
      th = cn[0].clientHeight + cn[1].clientHeight;
      console.log("canvas found");
      mb = newheight - th;
      cn[5].style.height = mb + 'px'; //canvas control attempt
  }
}

function resizePlease(count) { //resize plots based on selections
  // screen may have resized**
  h = document.querySelector('#column-1 > div.containIt').clientHeight;
  w = document.querySelector('#column-1 > div.containIt').clientWidth;
  hw = h * w;  // get the area
  
  // based on selected count** these should fit--- 
  // RStudio!
  newHeight = Math.floor(Math.sqrt(hw/count)) * .85; 
  for(i = 0; i < graphy.length; ++i){
    graphy[i].style.height = newHeight + 'px';
    graphy[i].style.width = newHeight + 'px';
    gcn = graphy[i].childNodes;
    if(cn.length > 0){
        th = gcn[0].clientHeight + gcn[1].clientHeight;
        mb = newHeight - th;
        gcn[5].style.height = mb + 'px'; //canvas control attempt
        canYouPLEASElisten = graphy[i].querySelector('canvas');
        canYouPLEASElisten.style.height = mb + 'px'; //trigger zoom extent!!
        canYouPLEASElisten.style.height = '100%';
    }
  }
}


// Something selected triggers this function
function getOps(sel) {   
  //get ref to select list and display text box
  graphy = document.querySelectorAll('#column-1 div.visNetwork');
  count = 0; // reset count of selected vis
  // loop through selections
  for(i = 0; i < sel.length; i++) {
    opt = sel.options[i];
    if ( opt.selected ) {
      count++
      graphy[i].style.display = 'block';
      console.log(opt + "selected");
      console.log(count + " options selected");
    } else {
      graphy[i].style.display = 'none';
    }
  }
  resizePlease(count); 
}

```

Developer Tools Console

If you go to the developer tools console, you will be able to see how many and which options are selected as the selections are made. That way, if there is something odd like reverse order (which I suspect but couldn't validate), you'll see what is or isn't happening as you might have expected. Where ever you see console.log, that is sending a message to the console, so you can watch what's happening.

Dashboard Colors

If there are any colors, custom or otherwise you would like in the background, let me know. I can help with that part, as well. Right now, the colors of the dashboard are the default colors.

Integrant answered 7/3, 2022 at 19:8 Comment(2)
@ Kat: Thank you for your answer and sorry for the delay! I obsessively worked on this question myself... and in the end, I just ended up manually copy/pasting individual graphs into Microsoft Paint and manually adjusting them and making a collage. Thank you so much for all your help! It would take me about a year to understand all the background needed to understand all the code you posted. Thank you so much!Homothermal
I was just thinking that it would have been easier if I could have just shared the entire RMD file....so ya, here it is.Integrant
P
2

While my solution isn't exactly what you describe under Option 2, it is close. We use combineWidgets() to create a grid with a single column and a row height where one graph covers most of the screen height. We squeeze in a link between each widget instance that scrolls the browser window down to show the following graph when clicked.

Let me know if this is working for you. It should be possible to automatically adjust the row size according to the browser window size. Currently, this depends on the browser window height being around 1000px.

I modified your code for the graph creation slightly and wrapped it in a function. This allows us to create 25 different-looking graphs easily. This way testing the resulting HTML file is more fun! What follows the function definition is the code to create a list of HTML objects that we then feed into combineWidgets().

library(visNetwork)
library(tidyverse)
library(igraph)
library(manipulateWidget)
library(htmltools)

create_trip_graph <-
  function(x, distance = NULL) {
    n <- 15
    data <- tibble(d = 1:n,
                   name =
                     c(
                       "new york",
                       "chicago",
                       "los angeles",
                       "orlando",
                       "houston",
                       "seattle",
                       "washington",
                       "baltimore",
                       "atlanta",
                       "las vegas",
                       "oakland",
                       "phoenix",
                       "kansas",
                       "miami",
                       "newark"
                     ))
    
    relations <-  tibble(from = sample(data$d),
                         to = lead(from, default = from[1]))    
    graph <-
      graph_from_data_frame(relations, directed = TRUE, vertices = data)
    
    V(graph)$color <-
      ifelse(data$d == relations$from[1], "red", "orange")
    
    if (is.null(distance))
      # This generates a random distance value if none is 
      # specified in the function call. Values are just for 
      # demonstration, no actual distances are calculated.
      distance <- sample(seq(19, 25, .1), 1)
    
    toVisNetworkData(graph) %>%
      c(., list(
        main = paste0("Trip ", x, " : "),
        submain = paste0(distance, "KM")
      )) %>%
      do.call(visNetwork, .) %>%
      visIgraphLayout(layout = "layout_in_circle") %>%
      visInteraction(navigationButtons = TRUE) %>%
      visEdges(arrows = 'to')
  }

comb_vgraphs <- lapply(1:25, function (x) list(
  create_trip_graph(x),
  htmltools::a("NEXT TRIP", 
               onclick = 'window.scrollBy(0,950)', 
               style = 'color:blue; text-decoration:underline;')))  %>%
  unlist(recursive = FALSE)


ff <-
  combineWidgets(
    list = comb_vgraphs,
    ncol = 1,
    height = 25 * 950,
    rowsize = c(24, 1)
  )

htmltools::save_html(html = ff, file = "widgets.html")

If you want to have 5 network maps per row the code gets a bit more complex and it also might lead to a situation where the user might have to do horizontal scrolling in order to see everything, which is something you usually want to avoid when creating HTML pages. Here is the code for a 5 maps per row solution:

comb_vgraphs2 <- lapply(1:25, function(x) {
  a <- list(create_trip_graph(x))
  # We detect whenever we are creating the 5th, 10th, 15th etc. network map
  # and add the link after that one.
  if (x %% 5 == 0 & x < 25) a[[2]] <- htmltools::a("NEXT 5 TRIPS", 
                                          onclick = 'window.scrollBy(0,500)', 
                                          style = 'color:blue; text-decoration:underline;')
  a
}) %>%
  unlist(recursive = FALSE)

ff2 <-
  combineWidgets(
    list = comb_vgraphs2,
    ncol = 6, # We need six columns, 5 for the network maps 
              # and 1 for the link to scroll the page.
    height = 6 * 500,
    width = 1700
    #rowsize = c(24, 1)
  )

# We need to add some white space in for the scrolling by clicking the link to 
# still work for the last row.
ff2$widgets[[length(ff2$widgets) + 1]] <- htmltools::div(style = "height: 1000px;")

htmltools::save_html(html = ff2, file = "widgets2.html")

In general I'd recommend you play around with the height and width, ncol and nrow arguments of combineWidgets() to achieve a satisfying solution. My strategy when building this was to first create a grid without the scroll link and add that in, after getting the grid right.

Plethoric answered 3/3, 2022 at 6:10 Comment(11)
Thank you for your answer! I tried running your code but I couldn't get pas the "comb_vgraphs()" part :Homothermal
comb_vgraphs <- lapply(1:25, (x) list( Error: unexpected input in "comb_vgraphs <- lapply(1:25, \"Homothermal
Here is a part of my session info: sessionInfo() R version 4.0.3 (2020-10-10) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows 10 x64 (build 22000)Homothermal
Sorry, my solution was using the anonymous function shorthand and the base R pipe |>, both introduced in R 4.1.0. I edited my answer to be compatible with previous R versions.Plethoric
Thank you so much! This is so cool!! Is it possible to take these 25 graphs and put them 5 in each row?Homothermal
Can you please explain this line of code? if (is.null(distance)) distance <- sample(seq(19, 25, .1), 1) - how exactly is this calculating the distance? Thanks again!Homothermal
It's NOT calculating the distance. It's just creating a random number between 19 and 25 so that the plots differ from each other - I added a comment in the code. I also added an alternative solution for 5 maps per row.Plethoric
I can not thank you enough for all your work! This is all so amazing! I feel really bad in asking one more thing - suppose I already have all my graphs pre-made (e.g. visnetwork_1, visnetwork_2, ... visnetwork_25). Is it possible to adapt your code so that it can work from here? Thank you so much!Homothermal
Sure. Make sure to have all your graphs stored in a list() and instead of lapply(1:25, ...) you run lapply(your_list_of_graphs, ...).Plethoric
Just to clarify : your_list_of_graphs = list(graph_1, graph_2 ...)Homothermal
That would work. A better solution would be to create the graphs in a list, to begin with. I encourage you to try to get it to work and ask a new question when you encounter an issue you can't solve by yourself.Plethoric
I
1

The sizing works, but at first glance, it looks like it doesn't. It's not ready, though.

When you select options, it doesn't trigger the auto-resize functionality within the canvases.

The auto-resize of the graph objects works just fine. (You'll see in the gif.)

The Viewer pane in RStudio is not the best way to check the knitted file. Look at it in a browser after knitting...especially if you want to make changes. It appears as if sometimes it thinks that all of RStudio is the container size, and you get graphs running off the screen. I'm sure it's how I have it coded, but that doesn't appear to be an issue in Safari or Chrome (I didn't check the other browsers).

I have tried to trigger the resizing of the canvas many different ways. This code may have some redundancies from attempts to trigger a resize/zoom extent of the canvases. (I think I deleted all of the things that didn't work.) Perhaps with this, someone else can figure that part out.

I used some Shiny code, but this is not using a Shiny runtime. Essentially the static work is R, but dynamic elements cannot be in R (i.e., resizing events, reading selections, etc.).

In the libraries I used, I called shinyRPG. I added and commented out package installation code because that package isn't a Cran package. (It's on Github.)

Assumptions I've made in coding (and this answer):

  • You have working knowledge of Rmarkdown.
  • There are 25 of these network diagrams.
  • There are no other HTML widgets in the script.

If these are not true, let me know.

The YAML

The Output Options

---
title: "Just for antonoyaro8"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
---

The Styles

This code goes between the YAML and the first R code chunk. In the regular text area of the RMD–not in an R chunk.

<style>
select {
  // A reset of styles, including removing the default dropdown arrow
  appearance: none;
  background-color: transparent;
  border: none;
  padding: 0 1em 0 0;
  margin: 0;
  width: 100%;
  font-family: inherit;
  font-size: inherit;
  cursor: inherit;
  line-height: inherit;
}
.select {
  display: grid;
  grid-template-areas: "select";
  align-items: center;
  position: relative;
  min-width: 15ch;
  max-width: 100ch;
  border: 1px solid var(--select-border);
  border-radius: 0.25em;
  padding: 0.25em 0.5em;
  font-size: 1.25rem;
  cursor: pointer;
  line-height: 1.1;
  background-color: #fff;
  background-image: linear-gradient(to top, #f9f9f9, #fff 33%);
}
select[multiple] {
  padding-right: 0; 
  /* Safari will not show options unless labels fit   */
  height: 50rem;   // how many options show at one time
  font-size: 1rem;
}
#column-1 > div.containIt > div.visNetwork canvas {
  width: 100%;
  height: 80%;
}
.containIt {
  display: flex;
  flex-flow: row wrap;
  flex-grow: 1;
  justify-content: space-around;
  align-items: flex-start;
  align-content: space-around;
  overflow: hidden;
  height: 100%;
  width: 100%;
  margin-top: 2vw;
  height: 80vh;
  widhth: 80vw;
  overflow: hidden;
}

</style>

Libraries

The first R chunk is next. You don't have to set echo = F in flexdashboard.

```{r setup, include=FALSE}

library(flexdashboard)
library(visNetwork)
library(htmltools)
library(igraph)
library(tidyverse)
library(shinyRPG) # remotes::install_github("RinteRface/shinyRPG")

```

R Code to Create the Diagrams

This next part is essentially your code. I changed a few things in the final version of the call to create the vizNetwork.

```{r dataStuff}

set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))

relations = data.frame(tibble(
  from = sample(data$d),
  to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )

graph = graph_from_data_frame(relations, directed=T, vertices = data) 

#red circle: starting point and final point
V(graph)$color <- ifelse(data$d == relations$from[1], "red", "orange")

a = visIgraph(graph)  

m_1 = 1
m_2 = 23.6

a = toVisNetworkData(graph) %>%
  c(., list(main = paste0("Trip ", m_1, " : "), 
            submain = paste0 (m_2, "KM") )) %>%
  do.call(visNetwork, .) %>%
  visIgraphLayout(layout = "layout_in_circle") %>% 
  visEdges(arrows = 'to')

# collect the correct order
df2 <- data %>% 
  mutate(d = as.numeric(d),
         nuname = factor(a$x$edges$from, 
                         levels = unlist(data$name))) %>%
  arrange(nuname) %>% 
  select(d) %>% unlist(use.names = F)
#  [1] 11  5  2  8  7  6 10 14 15  4 12  9 13  3  1 
V(graph)$name = data$label = paste0(df2, "\n", data$name)
a = visIgraph(graph)  

m_1 = 1
m_2 = 23.6
a = toVisNetworkData(graph) %>%
  c(., list(main = list(text = paste0("Trip ", m_1, " : "), 
                        style = "font-family: Georgia; font-size: 100%; font-weight: bold; text-align:center;"),
            submain = list(text = paste0(m_2, "KM"),
                           style = "font-family: Georgia; font-size: 100%; text-align:center;"))) %>%
  do.call(visNetwork, .) %>%
  visInteraction(navigationButtons = TRUE) %>%
  visIgraphLayout(layout = "layout_in_circle") %>% 
  visEdges(arrows = 'to') %>% 
  visOptions(width = "100%", height = "80%", autoResize = T)

a[["sizingPolicy"]][["knitr"]][["figure"]] <- FALSE

y = x = w = v = u = t = s = r = q  = p = o = n = m = l = k = j = i = h = g = f = e = d = c = b = a

```

The Multi-Select Box

Between the last chunk and before the next chunk of code is where this next part goes. This creates the left column, where the multi-select box is. (This is not in a code chunk.)

Column {data-width=200}
-----------------------------------------------------------------------

### Select Options

You can select one or more options from the list. 

No to build the select box and append the function that will trigger changes. This part will require modification. Name the options that the user sees on the screen here. (letters[1:25] in this code.)

Your object names do not have to match the names you have here. They do need to be in the same order, though.

```{r selectiver}
tagSel <- rpgSelect(
  "selectBox",                      # don't change this (connected)
  "Selections:",                    # visible on HTML; change away or set to ""
  c(setNames(1:25, letters[1:25])), # left is values, right is labels
  multiple = T                      # all multiple selections
)        # other attributes controlled by css at the top

tagSel$attribs$class <- 'select select--multiple'       # connect styles
tagSel$children[[2]]$attribs$class <- "mutli-select"    # connect styles
tagSel$children[[2]]$attribs$onchange <- "getOps(this)" # connect the JS function

tagSel

```

The Network Diagrams

Then between the previous chunk and the next chunk (not in a chunk):

Column
-----------------------------------------------------------------------

<div class="containIt">

Now call your graphs.

```{r notNow, include=T}

a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y

```

Close the div tag after that chunk:

</div>

Final Chunk: Javascript

This started out nice and neat...but after a lot of trial and error–WYSIWYG. Effective commenting fizzled out somewhere along the way, too. If there are questions as to what does what, let me know.

This chunk won't do anything if you run the chunk in R Markdown (while in the Source pane). To execute JS, you have to knit.

```{r pickMe,results='asis',engine='js'}

//remove inherent knitr element-- after using mutlti-select starts harboring space
byeknit = document.querySelector('#column-1 > div.containIt > div.knitr-options');
byeknit.remove(1);

// Reset Sizing of Widgets
h = document.querySelector('#column-1 > div.containIt').clientHeight;
w = document.querySelector('#column-1 > div.containIt').clientWidth;
hw = h * w;

cont = document.querySelectorAll('#column-1 > div.containIt > div');

newHeight = Math.floor(Math.sqrt(hw/cont.length)) * .85;

for(i = 0; i < cont.length; ++i){
  cont[i].style.height = newHeight + 'px';
  cont[i].style.width = newHeight + 'px';
  cn = cont[i].childNodes;
  if(cn.length > 0){
      th = cn[0].clientHeight + cn[1].clientHeight;
      console.log("canvas found");
      mb = newheight - th;
      cn[5].style.height = mb + 'px'; //canvas control attempt
  }
}

function resizePlease(count) { //resize plots based on selections
  // screen may have resized**
  h = document.querySelector('#column-1 > div.containIt').clientHeight;
  w = document.querySelector('#column-1 > div.containIt').clientWidth;
  hw = h * w;  // get the area
  
  // based on selected count** these should fit--- 
  // RStudio!
  newHeight = Math.floor(Math.sqrt(hw/count)) * .85; 
  for(i = 0; i < graphy.length; ++i){
    graphy[i].style.height = newHeight + 'px';
    graphy[i].style.width = newHeight + 'px';
    gcn = graphy[i].childNodes;
    if(cn.length > 0){
        th = gcn[0].clientHeight + gcn[1].clientHeight;
        mb = newHeight - th;
        gcn[5].style.height = mb + 'px'; //canvas control attempt
        canYouPLEASElisten = graphy[i].querySelector('canvas');
        canYouPLEASElisten.style.height = mb + 'px'; //trigger zoom extent!!
        canYouPLEASElisten.style.height = '100%';
    }
  }
}


// Something selected triggers this function
function getOps(sel) {   
  //get ref to select list and display text box
  graphy = document.querySelectorAll('#column-1 div.visNetwork');
  count = 0; // reset count of selected vis
  // loop through selections
  for(i = 0; i < sel.length; i++) {
    opt = sel.options[i];
    if ( opt.selected ) {
      count++
      graphy[i].style.display = 'block';
      console.log(opt + "selected");
      console.log(count + " options selected");
    } else {
      graphy[i].style.display = 'none';
    }
  }
  resizePlease(count); 
}

```

Developer Tools Console

If you go to the developer tools console, you will be able to see how many and which options are selected as the selections are made. That way, if there is something odd like reverse order (which I suspect but couldn't validate), you'll see what is or isn't happening as you might have expected. Where ever you see console.log, that is sending a message to the console, so you can watch what's happening.

Dashboard Colors

If there are any colors, custom or otherwise you would like in the background, let me know. I can help with that part, as well. Right now, the colors of the dashboard are the default colors.

Integrant answered 7/3, 2022 at 19:8 Comment(2)
@ Kat: Thank you for your answer and sorry for the delay! I obsessively worked on this question myself... and in the end, I just ended up manually copy/pasting individual graphs into Microsoft Paint and manually adjusting them and making a collage. Thank you so much for all your help! It would take me about a year to understand all the background needed to understand all the code you posted. Thank you so much!Homothermal
I was just thinking that it would have been easier if I could have just shared the entire RMD file....so ya, here it is.Integrant

© 2022 - 2024 — McMap. All rights reserved.