datastorm-open / visNetwork

R package, using vis.js library for network visualization
Other
544 stars 126 forks source link

Does anyone know how to save a drag and drop graph in Rshiny after drawing a flowchart with visNetwork #467

Open WSinana opened 5 months ago

WSinana commented 5 months ago

I want to implement it on the shiny platform to remember the shape of the flow chart after I modify it. When I rename a node each time, its shape will not reset, and I can drag it to my favorite shape and click download to save it to the local png format.

Has anyone dealt with this issue before? If so, could you share how you managed to save and reload the network with the nodes in their new positions? Thank you in advance for your help!

create_flow_chart_with_weights <- function(weight_data, initial_alpha_values) {

nodes <- data.frame(id = 1:nrow(weight_data),
                    label = sapply(1:nrow(weight_data), function(i) {
                      sprintf("H%d\nα=%s", i, formatC(initial_alpha_values[i], format = "g"))
                    }),
                    color = "lightblue",
                    shape = "ellipse",
                    shadow = TRUE)

edges <- data.frame()

added_edges <- matrix(FALSE, nrow = nrow(weight_data), ncol = ncol(weight_data))
for (i in 1:nrow(weight_data)) {
  for (j in 1:ncol(weight_data)) {
    if (!is.na(weight_data[i, j]) && weight_data[i, j] > 0) {
      if (i != j && !is.na(weight_data[j, i]) && weight_data[j, i] > 0 && !added_edges[j, i]) {

        added_edges[i, j] <- TRUE
        added_edges[j, i] <- TRUE

        edges <- rbind(edges, data.frame(from = i, to = j, label = formatC(weight_data[i, j], format = "g"), arrows = "to"))
        edges <- rbind(edges, data.frame(from = j, to = i, label = formatC(weight_data[j, i], format = "g"), arrows = "to"))
      } else if (!added_edges[i, j]) {

        added_edges[i, j] <- TRUE
        edges <- rbind(edges, data.frame(from = i, to = j, label = formatC(weight_data[i, j], format = "g"), arrows = "to"))
      }
    }
  }
}
edges$smooth <- mapply(function(from, to) {
  if (added_edges[from, to] && added_edges[to, from]) {
    list(enabled = TRUE, type = "curved", roundness = 0.5)
  } else {
    FALSE
  }
}, edges$from, edges$to, SIMPLIFY = FALSE)

visNetwork(nodes, edges) %>% visEdges(arrows = 'to', font = list(background = 'white')) %>% visInteraction(dragNodes = TRUE) %>% visPhysics(enabled = FALSE,solver = "forceAtlas2Based", forceAtlas2Based = list(springLength = 250, gravitationalConstant = -300, springConstant = 1.0)) %>% visOptions(highlightNearest = TRUE, nodesIdSelection = FALSE) %>% visInteraction(zoomView = FALSE) %>% visLayout(randomSeed = 123) } observe({ visNetworkProxy("weightBasedFlowChart") %>% visStorePositions() })

proxy <- dataTableProxy('weightTable') observeEvent(input$weightTable_cell_edit, { info <- input$weightTable_cell_edit rv$data[info$row, info$col] <- as.numeric(info$value)

initial_alpha_values <- alpha_table_data()[, "分配的Alpha"]

for (i in 1:nrow(rv$data)) {
  rowSum <- sum(rv$data[i, ], na.rm = TRUE)
  if (rowSum > 1) {
    showModal(modalDialog(
      title = "错误",
      paste0("第 ", i, " 行的数值总和不能超过 1。您当前的和为: ", rowSum),
      easyClose = TRUE,
      footer = NULL
    ))
    rv$data[i, info$col] <- NA_real_ # Reset the value
    break
  }
}

output$weightBasedFlowChart <- renderVisNetwork({
  create_flow_chart_with_weights(rv$data, initial_alpha_values)
})

})

observeEvent(input$renameNodes1, { if (!is.null(alpha_table_data()) && "分配的Alpha" %in% names(alpha_table_data())) { initial_alpha_values <- alpha_table_data()[, "分配的Alpha"] nodes_data <- create_nodes_data_for_weight_based_chart(rv$data, initial_alpha_values)

  nodes_info$names <- nodes_data$name

  output$renameTable1 <- renderDT({
    datatable(nodes_data[, c("name", "alpha")], editable = 'cell', options = list(dom = 't'))
  })
}

})

observeEvent(input$renameTable1_cell_edit, { info <- input$renameTable1_cell_edit if (!is.null(alpha_table_data()) && "分配的Alpha" %in% names(alpha_table_data())) { initial_alpha_values <- alpha_table_data()[, "分配的Alpha"]

  if (info$col == 1) {  # “name”列
    nodes_info$names[info$row] <- info$value
  } else if (info$col == 2) {  # “alpha”列
    initial_alpha_values[info$row] <- as.numeric(info$value)
  }

  output$weightBasedFlowChart <- renderVisNetwork({
    nodes_data <- create_nodes_data_for_weight_based_chart(rv$data, initial_alpha_values)
    for (i in seq_along(nodes_info$names)) {
      nodes_data$name[i] <- nodes_info$names[i]
      nodes_data$alpha[i] <- initial_alpha_values[i]  
    }
    create_flow_chart_with_weights_custom(nodes_data, rv$data)

  })
}

})

create_nodes_data_for_weight_based_chart <- function(weight_data, initial_alpha_values) {

nodes_data <- data.frame(
  id = 1:nrow(weight_data),
  name = sapply(1:nrow(weight_data), function(i) sprintf("H%d", i)),
  alpha = initial_alpha_values
)

return(nodes_data)

} create_flow_chart_with_weights_custom <- function(nodes_data, weight_data) {

nodes <- data.frame(
  id = nodes_data$id,
  label = sapply(1:nrow(nodes_data), function(i) {
    sprintf("%s\nα=%s", nodes_data$name[i], formatC(nodes_data$alpha[i], format = "g"))
  }),
  color = "lightblue",
  shape = "ellipse",
  shadow = TRUE
)

edges <- data.frame()
added_edges <- matrix(FALSE, nrow = nrow(weight_data), ncol = ncol(weight_data))

for (i in 1:nrow(weight_data)) {
  for (j in 1:ncol(weight_data)) {
    if (!is.na(weight_data[i, j]) && weight_data[i, j] > 0) {
      if (i != j && !is.na(weight_data[j, i]) && weight_data[j, i] > 0 && !added_edges[j, i]) {

        added_edges[i, j] <- TRUE
        added_edges[j, i] <- TRUE

        edges <- rbind(edges, data.frame(from = i, to = j, label = formatC(weight_data[i, j], format = "g"), arrows = "to"))
        edges <- rbind(edges, data.frame(from = j, to = i, label = formatC(weight_data[j, i], format = "g"), arrows = "to"))
      } else if (!added_edges[i, j]) {

        added_edges[i, j] <- TRUE
        edges <- rbind(edges, data.frame(from = i, to = j, label = formatC(weight_data[i, j], format = "g"), arrows = "to"))
      }
    }
  }
}
edges$smooth <- mapply(function(from, to) {
  if (added_edges[from, to] && added_edges[to, from]) {
    list(enabled = TRUE, type = "curved", roundness = 0.5)
  } else {
    FALSE
  }
}, edges$from, edges$to, SIMPLIFY = FALSE)

visNetwork(nodes, edges) %>% visEdges(arrows = 'to', font = list(background = 'white')) %>% visInteraction(dragNodes = TRUE) %>% visPhysics(enabled = FALSE,solver = "forceAtlas2Based", forceAtlas2Based = list(springLength = 250, gravitationalConstant = -300, springConstant = 1.0)) %>% visOptions(highlightNearest = TRUE, nodesIdSelection = FALSE) %>% visInteraction(zoomView = FALSE) %>% visLayout(randomSeed = 123) }

observeEvent(input$savePositions, { visNetworkProxy("weightBasedFlowChart") %>% visGetPositions() })

nodePositions <- reactive({ positions <- input$weightBasedFlowChart_positions if(!is.null(positions)){ nodePositions <- do.call("rbind", lapply(positions, function(x){ data.frame(x = x$x, y = x$y)})) nodePositions$id <- names(positions) nodePositions } else { NULL } })

output$downloadFlowChart <- downloadHandler( filename = function() { paste("weight_flowchart", Sys.Date(), ".png", sep = "") }, content = function(file) {

  updated_alpha_values <- rv$initial_alpha_values
  if (!is.null(alpha_table_data()) && "分配的Alpha" %in% names(alpha_table_data())) {
    updated_alpha_values <- alpha_table_data()[, "分配的Alpha"]
  }
  updated_node_data <- create_nodes_data_for_weight_based_chart(rv$data, updated_alpha_values)

  if (!is.null(nodes_info$names)) {
    updated_node_data$name <- nodes_info$names
  }

  if (!is.null(input$nodePositions)) {
    for (id in names(input$nodePositions$x)) {
      if (id %in% updated_node_data$id) {
        updated_node_data$x[updated_node_data$id == id] <- input$nodePositions$x[id]
        updated_node_data$y[updated_node_data$id == id] <- input$nodePositions$y[id]
      }
    }
  }

  weight_flow_chart <- create_flow_chart_with_weights_custom(updated_node_data, rv$data)

  temp_html_file <- tempfile(fileext = ".html")
  visNetwork::visSave(weight_flow_chart, temp_html_file)

  webshot(temp_html_file, file = file, vwidth = 800, vheight = 600)

  unlink(temp_html_file)
}

)`