R/SNAhelper.R

Defines functions SNAhelperAddin SNAhelperGadget SNAhelper

Documented in SNAhelper SNAhelperAddin SNAhelperGadget

#' SNAhelper
#'
#' \code{SNAhelper} is a RStudio-Addin that provides a graphical interface for network analysis and visualization.
#'
#' @details To run the addin, highlight an igraph-object in your current script and select \code{SNAhelper} from the Addins-menu within RStudio. After terminating the addin, a character string containing the code for visualization is inserted in your current script. Alternatively you can run it with SNAhelperGadget(graph) from the console.
#' @param graph An igraph object to visualize
#' @return \code{SNAhelper} returns a character vector with code.
#' @import miniUI
#' @import shiny
#' @import ggplot2
#' @import ggraph
#' @import rstudioapi
#' @import igraph
#' @importFrom colourpicker colourInput
#' @importFrom grDevices col2rgb
#' @importFrom grDevices colors
#' @name SNAhelper
NULL

SNAhelper <- function(text) {
    if (!requireNamespace("graphlayouts", quietly = TRUE)) {
        stop("graphlayouts package not found. Install it with install.packages('graphlayouts')", call. = FALSE)
    }
    if (any(ls(envir = .GlobalEnv) == text)) {
        g <- get(text, envir = .GlobalEnv)
        if (!igraph::is.igraph(g)) {
            stop(paste0(text, " is not an igraph object"))
        }
        if (any(igraph::vertex_attr_names(g) == "x") & any(igraph::vertex_attr_names(g) == "y")) {
            xy <- cbind(igraph::V(g)$x, igraph::V(g)$y)
        } else {
            xy <- graphlayouts::layout_with_stress(g)
        }
        rv <- reactiveValues(g = g, xy = xy)
    } else {
        stop(paste0("Couldn't find  the graph ", text, "."))
    }
    if (any(igraph::is.multiple(g))) {
        edge_geom <- "geom_edge_parallel0("
    } else {
        edge_geom <- "geom_edge_link0("
    }

    # ui ----
    ui <- miniPage(
        tags$script(jscodeWidth),
        tags$script(jscodeHeight),
        tags$style(type = "text/css", ".selectize-dropdown{ width: 200px !important; }"),
        tags$style(type = "text/css", ".form-group.shiny-input-container{width:50%;}"),
        gadgetTitleBar("SNA helper"),
        miniTabstripPanel(
            selected = "layout",
            miniTabPanel("layout",
                icon = icon("sliders-h"),
                miniContentPanel(
                    scrollable = FALSE,
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Choose Layout")
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        selectizeInput("graphLayout",
                            label = "Layout Algorithm",
                            choices = layouts.available,
                            selected = "graphlayouts::layout_with_stress", width = input.width
                        ),
                        shiny::conditionalPanel(
                            "input.graphLayout=='graphlayouts::layout_with_focus'",
                            selectizeInput("focalNode",
                                label = "Choose Focal Node ID",
                                choices = 1:igraph::vcount(g),
                                width = input.width
                            )
                        ),
                        shiny::conditionalPanel(
                            "input.graphLayout=='graphlayouts::layout_with_centrality'",
                            selectizeInput("centralLay",
                                label = "Choose Centrality",
                                choices = NULL,
                                width = input.width
                            )
                        )
                    ),
                    fillRow(
                        height = line.height, width = "50%",
                        actionButton("do.layout", "Calculate Layout"),
                        actionButton("del.isolate", "Delete Isolates")
                    ),
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Tweak Layout"),
                        checkboxInput("showLabs", label = "Show NodeIDs", value = FALSE)
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        selectizeInput("nodeId",
                            label = "NodeID", choices = 1:vcount(g),
                            width = input.width
                        ),
                        p("tweak node position by clicking on the desired location.")
                    )
                ),
                plotOutput("Graph1", width = "80%", height = "55%", click = "tweakxy")
            ),
            miniTabPanel("node attributes",
                icon = icon("list-ol"),
                miniContentPanel(
                    scrollable = FALSE,
                    fillRow(
                        height = line.height, width = "100%",
                        selectizeInput("centindex",
                            label = "Index",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("clusteralg",
                            label = "Clustering",
                            choices = c("Louvain" = "cluster_louvain(rv$g)"),
                            width = input.width
                        )
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        actionButton("calcIndex", "Calculate Index"),
                        actionButton("calcClust", "Calculate Clustering")
                    ),
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Node Attributes")
                    ),
                    DT::dataTableOutput("attrManageN")
                )
            ),
            miniTabPanel("nodes",
                icon = icon("circle"),
                miniContentPanel(
                    scrollable = FALSE,
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Manual")
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        colourInput("nodeColMan", label = "Colour", value = "gray32"),
                        numericInput("nodeSizeMan",
                            label = "Size",
                            min = 0, max = 20, step = 0.5, value = 5, width = input.width
                        ),
                        colourInput("nodeBorderColMan", label = "Border Colour", value = "black"),
                        numericInput("nodeBorderSizeMan",
                            label = "Border Size",
                            min = 0, max = 2, step = 0.1, value = 0.3, width = input.width
                        )
                    ),
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Attribute")
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        selectizeInput("nodeColAttr",
                            label = "Colour (Cont.)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("nodeColAttrD",
                            label = "Colour (Discrete)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("nodeSizeAttr",
                            label = "Size (Cont.)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("nodeLabelAttr",
                            label = "Node Label",
                            choices = NULL,
                            width = input.width
                        )
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        shiny::conditionalPanel(
                            "input.nodeColAttr!='None'",
                            colourInput("nodeColAttrL", label = "Min Colour", value = "skyblue1"),
                            colourInput("nodeColAttrH", label = "Max Colour", value = "royalblue4")
                        ),
                        shiny::conditionalPanel(
                            "input.nodeColAttrD!='None'",
                            selectizeInput("nodeColAttrP",
                                label = "Palette",
                                choices = c(
                                    "Set1", "Set2", "Set3", "Pastel2", "Pastel1",
                                    "Paired", "Dark2", "Accent"
                                ),
                                width = input.width
                            )
                        ),
                        shiny::conditionalPanel(
                            "input.nodeSizeAttr!='None'",
                            numericInput("nodeSizeAttrL",
                                label = "Min Size",
                                min = 0, max = 20, step = 0.5, value = 3, width = input.width
                            ),
                            numericInput("nodeSizeAttrH",
                                label = "Max Size",
                                min = 0, max = 20, step = 0.5, value = 8, width = input.width
                            )
                        ),
                        shiny::conditionalPanel(
                            "input.nodeLabelAttr!='None'",
                            colourInput("nodeLabelCol", label = "Colour", value = "black"),
                            # selectizeInput('nodeLabelCol',label = 'Colour',
                            #                choices = NULL, width = input.width),
                            numericInput("nodeLabelSize",
                                label = "Size",
                                min = 0, max = 20, step = 0.5, value = 6, width = input.width
                            ),
                            selectizeInput("nodeLabelFont",
                                label = "Font",
                                choices = fonts.available, width = input.width
                            ),
                            shiny::checkboxInput("nodeLabelRepel", label = "Repel Labels?", value = FALSE)
                        )
                    )
                ),
                plotOutput("Graph2", width = "80%", height = "55%")
            ),
            miniTabPanel("edge attributes",
                icon = icon("list-ol"),
                miniContentPanel(
                    scrollable = FALSE,
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Edge Attributes")
                    ),
                    DT::dataTableOutput("attrManageE")
                )
            ),
            miniTabPanel("edges",
                icon = icon("minus"),
                miniContentPanel(
                    scrollable = FALSE,
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Manual")
                    ),
                    fillRow(
                        height = line.height, width = "75%",
                        colourInput("edgeColMan", label = "Colour", value = "gray66"),
                        numericInput("edgeSizeMan",
                            label = "Width",
                            min = 0, max = 10, step = 0.1, value = 0.8, width = input.width
                        ),
                        numericInput("edgeAlphaMan",
                            label = "Alpha",
                            min = 0, max = 1, step = 0.01, value = 1.0, width = input.width
                        )
                    ),
                    fillRow(
                        height = heading.height, width = "100%",
                        headingOutput("Attribute")
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        selectizeInput("edgeColAttr",
                            label = "Colour (Cont.)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("edgeColAttrD",
                            label = "Colour (Discrete)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("edgeSizeAttr",
                            label = "Width (Cont.)",
                            choices = NULL,
                            width = input.width
                        ),
                        selectizeInput("edgeAlphaAttr",
                            label = "Alpha (Cont.)",
                            choices = NULL,
                            width = input.width
                        )
                    ),
                    fillRow(
                        height = line.height, width = "100%",
                        shiny::conditionalPanel(
                            "input.edgeColAttr!='None'",
                            colourInput("edgeColAttrL", label = "Min Colour", value = "skyblue1"),
                            colourInput("edgeColAttrH", label = "Max Colour", value = "royalblue4")
                        ),
                        shiny::conditionalPanel(
                            "input.edgeColAttrD!='None'",
                            selectizeInput("edgeColAttrP",
                                label = "Palette",
                                choices = c(
                                    "Set1", "Set2", "Set3", "Pastel2", "Pastel1",
                                    "Paired", "Dark2", "Accent"
                                ),
                                width = input.width
                            )
                        ),
                        shiny::conditionalPanel(
                            "input.edgeSizeAttr!='None'",
                            numericInput("edgeSizeAttrL",
                                label = "Min Width",
                                min = 0, max = 10, step = 0.1, value = 0.3, width = input.width
                            ),
                            numericInput("edgeSizeAttrH",
                                label = "Max Width",
                                min = 0, max = 10, step = 0.1, value = 1.2, width = input.width
                            )
                        ),
                        shiny::conditionalPanel(
                            "input.edgeAlphaAttr!='None'",
                            numericInput("edgeAlphaAttrL",
                                label = "Min Alpha",
                                min = 0, max = 1, step = 0.01, value = 0.1, width = input.width
                            ),
                            numericInput("edgeAlphaAttrH",
                                label = "Max Alpha",
                                min = 0, max = 1, step = 0.01, value = 1, width = input.width
                            )
                        )
                    )
                ),
                plotOutput("Graph3", width = "80%", height = "55%")
            ),
            miniTabPanel("result",
                icon = icon("bezier-curve"),
                plotOutput("Graph4", width = "90%", height = "80%"),
                miniContentPanel(
                    scrollable = TRUE,
                    fillRow(
                        height = line.height, width = "50%",
                        selectInput("legendPos",
                            label = "Show Legend:",
                            choices = c("none", "top", "bottom", "left", "right"),
                            width = input.width
                        )
                    ),
                    downloadButton("downloadData", "Save PNG")
                )
            )
        )
    )



    server <- function(input, output, session) {
        #--------------------#
        # constants ----
        #--------------------#
        vattr.to.aes <- igraph::vertex_attr_names(g)[!grepl("name", igraph::vertex_attr_names(g))]
        if (length(vattr.to.aes) > 0) {
            idC <- which(sapply(vattr.to.aes, function(x) is.numeric(igraph::get.vertex.attribute(g, x))))
            vattrC.to.aes <- c("None", vattr.to.aes[idC])
            idC <- which(sapply(vattr.to.aes, function(x) !is.numeric(igraph::get.vertex.attribute(g, x))))
            vattrD.to.aes <- c("None", vattr.to.aes[idC])
        } else {
            vattrC.to.aes <- c("None")
            vattrD.to.aes <- c("None")
        }

        eattr.to.aes <- igraph::edge_attr_names(g)
        if (length(eattr.to.aes) > 0) {
            idC <- which(sapply(eattr.to.aes, function(x) is.numeric(igraph::get.edge.attribute(g, x))))
            eattrC.to.aes <- c("None", eattr.to.aes[idC])
            idC <- which(sapply(eattr.to.aes, function(x) !is.numeric(igraph::get.edge.attribute(g, x))))
            eattrD.to.aes <- c("None", eattr.to.aes[idC])
        } else {
            eattrC.to.aes <- c("None")
            eattrD.to.aes <- c("None")
        }
        #--------------------#
        # check graph properties
        #--------------------#
        if (is.directed(g) & !is.weighted(g)) {
            cent_choice <- c(
                "In-Degree" = "degree(rv$g,mode='in')",
                "Out-Degree" = "degree(rv$g,mode='out')",
                "Degree" = "degree(rv$g,mode='all')",
                "Betwenness" = "betweenness(rv$g)",
                "Closeness" = "closeness(rv$g)",
                "PageRank" = "page_rank(rv$g)$vector"
            )
        } else if (!is.directed(g) & is.weighted(g)) {
            cent_choice <- c(
                "Degree" = "degree(rv$g)",
                "Weighted Degree" = "graph.strength(rv$g)",
                "Betwenness" = "betweenness(rv$g)",
                "Closeness" = "closeness(rv$g)",
                "Eigenvector" = "eigen_centrality(rv$g)$vector"
            )
        } else if (!is.directed(g) & !is.weighted(g)) {
            cent_choice <- c(
                "Degree" = "degree(rv$g)",
                "Betwenness" = "betweenness(rv$g)",
                "Closeness" = "closeness(rv$g)",
                "Eigenvector" = "eigen_centrality(rv$g)$vector"
            )
        } else {
            cent_choice <- c(
                "In-Degree" = "degree(rv$g,mode='in')",
                "Out-Degree" = "degree(rv$g,mode='out')",
                "Degree" = "degree(rv$g,mode='all')",
                "Weighted In-Degree" = "graph.strength(rv$g,mode='in')",
                "Weighted Out-Degree" = "graph.strength(rv$g,mode='out')",
                "Weighted Degree" = "graph.strength(rv$g,mode='all')",
                "Betwenness" = "betweenness(rv$g)",
                "Closeness" = "closeness(rv$g)",
                "PageRank" = "page_rank(rv$g)$vector"
            )
        }

        #--------------------#
        # initialize selectors ----
        #--------------------#
        updateSelectizeInput(
            session = session, inputId = "centralLay",
            choices = vattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "nodeColAttr",
            choices = vattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "nodeLabelAttr",
            choices = c("None", igraph::vertex_attr_names(g)),
            selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "nodeColAttrD",
            choices = vattrD.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "nodeSizeAttr",
            choices = vattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )


        updateSelectizeInput(
            session = session, inputId = "edgeColAttr",
            choices = eattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "edgeColAttrD",
            choices = eattrD.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "edgeSizeAttr",
            choices = eattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "edgeAlphaAttr",
            choices = eattrC.to.aes, selected = "None", server = TRUE,
            options = list(create = TRUE)
        )

        updateSelectizeInput(
            session = session, inputId = "centindex",
            choices = cent_choice, selected = cent_choice[1], server = TRUE,
            options = list(create = TRUE)
        )
        #--------------------#
        # be sure either discrete or continuos is selected ----
        #--------------------#
        shiny::observe({
            if (input$nodeColAttr != "None") {
                shiny::updateSelectInput(session, "nodeColAttrD", selected = "None")
            }
        })

        shiny::observe({
            if (input$nodeColAttrD != "None") {
                shiny::updateSelectInput(session, "nodeColAttr", selected = "None")
            }
        })

        shiny::observe({
            if (input$edgeColAttr != "None") {
                shiny::updateSelectInput(session, "edgeColAttrD", selected = "None")
            }
        })

        shiny::observe({
            if (input$edgeColAttrD != "None") {
                shiny::updateSelectInput(session, "edgeColAttr", selected = "None")
            }
        })

        #--------------------#
        # calculate initial layout ----
        #--------------------#
        shiny::observeEvent(input$del.isolate, {
            idx <- which(degree(rv$g) == 0)
            if (length(idx) >= 1) {
                g <- igraph::delete.vertices(rv$g, idx)
                xy <- rv$xy[-idx, ]
                rv$g <- g
                rv$xy <- xy
                gg_reactive()
            }
        })

        shiny::observeEvent(input$do.layout, {
            if (input$graphLayout == "graphlayouts::layout_as_backbone") {
                xy <- eval(parse(text = paste0(input$graphLayout, "(rv$g)")))
                rv$xy <- xy$xy

                bb <- rep(0, ecount(rv$g))
                bb[xy$backbone] <- 1
                g <- igraph::set.edge.attribute(graph = rv$g, name = "backbone", value = bb)
                rv$g <- g

                eattr.to.aes <- igraph::edge_attr_names(g)
                if (length(eattr.to.aes) > 0) {
                    idC <- which(sapply(eattr.to.aes, function(x) is.numeric(igraph::get.edge.attribute(g, x))))
                    eattrC.to.aes <- c("None", eattr.to.aes[idC])
                } else {
                    eattrC.to.aes <- c("None")
                }
                updateSelectizeInput(
                    session = session, inputId = "edgeColAttr",
                    choices = eattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )

                updateSelectizeInput(
                    session = session, inputId = "edgeSizeAttr",
                    choices = eattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )

                updateSelectizeInput(
                    session = session, inputId = "edgeAlphaAttr",
                    choices = eattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )
            } else if (input$graphLayout == "graphlayouts::layout_with_focus") {
                xy <- eval(parse(text = paste0(input$graphLayout, "(rv$g, v = ", input$focalNode, ")$xy")))
                rv$xy <- xy
            } else if (input$graphLayout == "graphlayouts::layout_with_centrality") {
                xy <- eval(parse(text = paste0(input$graphLayout, "(rv$g, cent = get.vertex.attribute(rv$g,\"", input$centralLay, "\"))")))
                rv$xy <- xy
            } else {
                xy <- eval(parse(text = paste0(input$graphLayout, "(rv$g)")))
                rv$xy <- xy
            }
            gg_reactive()
        })
        #--------------------#
        # tweak layout ----
        #--------------------#
        shiny::observeEvent(input$tweakxy, {
            indX <- as.numeric(input$nodeId)
            rv$xy[indX, 1] <- input$tweakxy$x
            rv$xy[indX, 2] <- input$tweakxy$y
            gg_reactive()
        })

        #--------------------#
        # calculate centrality/clustering ----
        #--------------------#
        shiny::observeEvent(input$calcIndex, {
            attr_name <- gsub("\\(rv.*", "", input$centindex)
            if (igraph::is_directed(g) & attr_name == "degree") {
                opt <- gsub("')", "", gsub(".*mode='", "", input$centindex))
                attr_name <- paste0(opt, "_", attr_name)
            }
            if (igraph::is_directed(g) & attr_name == "graph.strength") {
                opt <- gsub("')", "", gsub(".*mode='", "", input$centindex))
                attr_name <- paste0(opt, "_", attr_name)
            }
            if (!attr_name %in% igraph::vertex_attr_names(rv$g)) {
                ind <- eval(parse(text = input$centindex))

                g <- igraph::set.vertex.attribute(graph = rv$g, name = attr_name, value = ind)
                rv$g <- g

                vattr.to.aes <- igraph::vertex_attr_names(rv$g)[!grepl("name", igraph::vertex_attr_names(rv$g))]
                idC <- which(sapply(vattr.to.aes, function(x) is.numeric(igraph::get.vertex.attribute(rv$g, x))))
                vattrC.to.aes <- c("None", vattr.to.aes[idC])
                updateSelectizeInput(
                    session = session, inputId = "centralLay",
                    choices = vattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )

                updateSelectizeInput(
                    session = session, inputId = "nodeColAttr",
                    choices = vattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )

                updateSelectizeInput(
                    session = session, inputId = "nodeSizeAttr",
                    choices = vattrC.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )

                updateSelectizeInput(
                    session = session, inputId = "nodeLabelAttr",
                    choices = c("None", igraph::vertex_attr_names(g)),
                    selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )
            }
        })

        shiny::observeEvent(input$calcClust, {
            attr_name <- gsub("\\(rv.*", "", input$clusteralg)
            if (!attr_name %in% igraph::vertex_attr_names(rv$g)) {
                ind <- eval(parse(text = input$clusteralg))
                ind <- as.character((igraph::membership(ind)))
                g <- igraph::set.vertex.attribute(graph = rv$g, name = attr_name, value = ind)
                rv$g <- g
                vattr.to.aes <- igraph::vertex_attr_names(rv$g)[!grepl("name", igraph::vertex_attr_names(rv$g))]
                idC <- which(sapply(vattr.to.aes, function(x) !is.numeric(igraph::get.vertex.attribute(rv$g, x))))
                vattrD.to.aes <- c("None", vattr.to.aes[idC])

                updateSelectizeInput(
                    session = session, inputId = "nodeColAttrD",
                    choices = vattrD.to.aes, selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )
                updateSelectizeInput(
                    session = session, inputId = "nodeLabelAttr",
                    choices = c("None", igraph::vertex_attr_names(g)),
                    selected = "None", server = TRUE,
                    options = list(create = TRUE)
                )
            }
        })

        #------------------------------------------------------------#
        #--------------------#
        # main plotting function ----
        #--------------------#
        gg_reactive <- reactive({
            validate(
                need(is.validColour(input$nodeColMan), ""),
                need(is.validColour(input$edgeColMan), ""),
                need(is.validColour(input$nodeBorderColMan), ""),
                # need(is.validColour(input$nodeColAttr), ''),
                # need(is.validColour(input$nodeColAttrL), ''),
                # need(is.validColour(input$nodeColAttrH), ''),
                # need(is.validColour(input$edgeColAttr), ''),
                # need(is.validColour(input$edgeColAttrL), ''),
                # need(is.validColour(input$edgeColAttrH), ''),
                need(is.validColour(input$edgeColMan), "")
            )

            #--------------------#
            # layout ----
            #--------------------#
            # xy <- get_layout()
            code_layout <- "ggraph(rv$g,layout = \"manual\", x = rv$xy[,1], y = rv$xy[,2])"

            #--------------------#
            # nodes ----
            #--------------------#
            if (input$nodeColAttr == "None" & input$nodeColAttrD == "None" & input$nodeSizeAttr == "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "fill = \"", input$nodeColMan, "\"",
                    ",colour = \"", input$nodeBorderColMan, "\"",
                    ",size = ", input$nodeSizeMan,
                    ",stroke = ", input$nodeBorderSizeMan,
                    ",shape = 21",
                    ")"
                )
            } else if (input$nodeColAttr != "None" & input$nodeSizeAttr == "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "aes(fill = ", input$nodeColAttr, ")",
                    ",\ncolour = \"", input$nodeBorderColMan, "\"",
                    ",\nsize = ", input$nodeSizeMan,
                    ",\nshape = 21",
                    ", stroke = ", input$nodeBorderSizeMan,
                    ")"
                )
                nodes_scale_col <- paste0(
                    "scale_fill_gradient(low = \"", input$nodeColAttrL, "\",",
                    "high = \"", input$nodeColAttrH, "\")"
                )
                code_nodes <- paste(code_nodes, nodes_scale_col, sep = " + ")
            } else if (input$nodeColAttrD != "None" & input$nodeSizeAttr == "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "aes(fill = ", input$nodeColAttrD, ")",
                    ",\ncolour = \"", input$nodeBorderColMan, "\"",
                    ",\nsize = ", input$nodeSizeMan,
                    ",\nshape = 21",
                    ", stroke = ", input$nodeBorderSizeMan,
                    ")"
                )
                nodes_scale_col <- paste0("scale_fill_brewer(palette = \"", input$nodeColAttrP, "\", na.value = \"gray53\")")
                code_nodes <- paste(code_nodes, nodes_scale_col, sep = " + ")
            } else if (input$nodeColAttr == "None" & input$nodeColAttrD == "None" & input$nodeSizeAttr != "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "aes(size = ", input$nodeSizeAttr, ")",
                    ",\nfill = \"", input$nodeColMan, "\"",
                    ",\ncolour = \"", input$nodeBorderColMan, "\"",
                    ",\nshape = 21",
                    ", stroke = ", input$nodeBorderSizeMan,
                    ")"
                )
                nodes_scale_size <- paste0("scale_size(range = c(", input$nodeSizeAttrL, ",", input$nodeSizeAttrH, "))")
                code_nodes <- paste(code_nodes, nodes_scale_size, sep = " + ")
            } else if (input$nodeColAttr != "None" & input$nodeSizeAttr != "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "aes(fill = ", input$nodeColAttr,
                    ",size = ", input$nodeSizeAttr, ")",
                    ",\ncolour = \"", input$nodeBorderColMan, "\"",
                    ",\nshape = 21",
                    ", stroke = ", input$nodeBorderSizeMan,
                    ")"
                )

                nodes_scale_col <- paste0(
                    "scale_fill_gradient(low = \"", input$nodeColAttrL, "\",",
                    "high = \"", input$nodeColAttrH, "\")"
                )
                nodes_scale_size <- paste0("scale_size(range = c(", input$nodeSizeAttrL, ",", input$nodeSizeAttrH, "))")
                code_nodes <- paste(code_nodes, nodes_scale_col, nodes_scale_size, sep = " + ")
            } else if (input$nodeColAttrD != "None" & input$nodeSizeAttr != "None") {
                code_nodes <- paste0(
                    "geom_node_point(",
                    "aes(fill = ", input$nodeColAttrD,
                    ", size = ", input$nodeSizeAttr, ")",
                    ",\ncolour = \"", input$nodeBorderColMan, "\"",
                    ",\nshape = 21",
                    ", stroke = ", input$nodeBorderSizeMan,
                    ")"
                )

                nodes_scale_col <- paste0("scale_fill_brewer(palette = \"", input$nodeColAttrP, "\", na.value = \"gray53\")")
                nodes_scale_size <- paste0("scale_size(range = c(", input$nodeSizeAttrL, ",", input$nodeSizeAttrH, "))")
                code_nodes <- paste(code_nodes, nodes_scale_col, nodes_scale_size, sep = " + ")
            }
            #--------------------#
            # nodes labels ----
            #--------------------#
            if (input$nodeLabelAttr != "None" & input$nodeLabelAttr != "") {
                code_labels <- paste0(
                    "geom_node_text(",
                    "aes(label = ", input$nodeLabelAttr, ")",
                    ", colour = \"", input$nodeLabelCol, "\"",
                    ", size = ", input$nodeLabelSize,
                    ", family = \"", input$nodeLabelFont, "\"",
                    ")"
                )
                if (input$nodeLabelRepel) {
                    insert <- paste0(", repel = ", input$nodeLabelRepel, ",segment.alpha=0)")
                    code_labels <- gsub("\\)$", insert, code_labels)
                }
                code_nodes <- paste(code_nodes, code_labels, sep = " + ")
            }
            #--------------------#
            # edges ----
            #--------------------#
            if (input$edgeColAttr == "None" & input$edgeColAttrD == "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "edge_colour = \"", input$edgeColMan, "\"",
                    ",edge_width = ", input$edgeSizeMan,
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
            } else if (input$edgeColAttr != "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(colour = ", input$edgeColAttr, ")",
                    ",edge_width = ", input$edgeSizeMan,
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_col <- paste0(
                    "scale_edge_colour_gradient(low = \"", input$edgeColAttrL, "\",",
                    "high = \"", input$edgeColAttrH, "\")"
                )
                code_edges <- paste(code_edges, edge_scale_col, sep = " + ")
            } else if (input$edgeColAttrD != "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(colour = ", input$edgeColAttrD, ")",
                    ",edge_width = ", input$edgeSizeMan,
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_col <- paste0(
                    "scale_edge_colour_brewer(palette = \"",
                    input$edgeColAttrP, "\", na.value = \"gray53\")"
                )
                code_edges <- paste(code_edges, edge_scale_col, sep = " + ")
            } else if (input$edgeColAttr == "None" & input$edgeColAttrD == "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(width = ", input$edgeSizeAttr, ")",
                    ",\nedge_colour = \"", input$edgeColMan, "\"",
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )
                code_edges <- paste(code_edges, edge_scale_size, sep = " + ")
            } else if (input$edgeColAttr == "None" & input$edgeColAttrD == "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr, ")",
                    ",\nedge_colour = \"", input$edgeColMan, "\"",
                    ",\nedge_width = ", input$edgeSizeMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                code_edges <- paste(code_edges, edge_scale_alpha, sep = " + ")
            } else if (input$edgeColAttr != "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(width = ", input$edgeSizeAttr,
                    ",\ncolour = ", input$edgeColAttr, ")",
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )
                edge_scale_col <- paste0(
                    "scale_edge_colour_gradient(low = \"", input$edgeColAttrL, "\",",
                    "high = \"", input$edgeColAttrH, "\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_size, sep = " + ")
            } else if (input$edgeColAttrD != "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr == "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(width = ", input$edgeSizeAttr,
                    ",\ncolour = ", input$edgeColAttrD, ")",
                    ",edge_alpha = ", input$edgeAlphaMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )
                edge_scale_col <- paste0(
                    "scale_edge_colour_brewer(palette = \"",
                    input$edgeColAttrP, "\", na.value = \"gray53\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_size, sep = " + ")
            } else if (input$edgeColAttr != "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr,
                    ",colour = ", input$edgeColAttr, ")",
                    ",\nedge_width = ", input$edgeSizeMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                edge_scale_col <- paste0(
                    "scale_edge_colour_gradient(low = \"", input$edgeColAttrL, "\",",
                    "high = \"", input$edgeColAttrH, "\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_alpha, sep = " + ")
            } else if (input$edgeColAttrD != "None" & input$edgeSizeAttr == "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr,
                    ",colour = ", input$edgeColAttrD, ")",
                    ",\nedge_width = ", input$edgeSizeMan, ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                edge_scale_col <- paste0(
                    "scale_edge_colour_brewer(palette = \"",
                    input$edgeColAttrP, "\", na.value = \"gray53\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_alpha, sep = " + ")
            } else if (input$edgeColAttr == "None" & input$edgeColAttrD == "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr,
                    ",width = ", input$edgeSizeAttr, ")",
                    ",\nedge_colour = \"", input$edgeColMan, "\"", ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )

                code_edges <- paste(code_edges, edge_scale_size, edge_scale_alpha, sep = " + ")
            } else if (input$edgeColAttr != "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr,
                    ",width = ", input$edgeSizeAttr,
                    ",\ncolour = ", input$edgeColAttr, ")", ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )

                edge_scale_col <- paste0(
                    "scale_edge_colour_gradient(low = \"", input$edgeColAttrL, "\",",
                    "high = \"", input$edgeColAttrH, "\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_size, edge_scale_alpha, sep = " + ")
            } else if (input$edgeColAttrD != "None" & input$edgeSizeAttr != "None" & input$edgeAlphaAttr != "None") {
                code_edges <- paste0(
                    edge_geom,
                    "aes(alpha = ", input$edgeAlphaAttr,
                    ",width = ", input$edgeSizeAttr,
                    ",\ncolour = ", input$edgeColAttrD, ")", ")"
                )
                if (is.directed(g)) {
                    arrow_code <- paste0(
                        ",\narrow = arrow(angle = 30, length = unit(0.15, \"inches\")",
                        ",\nends = \"last\", type = \"closed\"))"
                    )
                    code_edges <- gsub(")$", arrow_code, code_edges)
                }
                edge_scale_alpha <- paste0(
                    "scale_edge_alpha(",
                    "range = c(", input$edgeAlphaAttrL, ",", input$edgeAlphaAttrH, "))"
                )
                edge_scale_size <- paste0(
                    "scale_edge_width(",
                    "range = c(", input$edgeSizeAttrL, ",", input$edgeSizeAttrH, "))"
                )

                edge_scale_col <- paste0(
                    "scale_edge_colour_brewer(palette = \"",
                    input$edgeColAttrP, "\", na.value = \"gray53\")"
                )

                code_edges <- paste(code_edges, edge_scale_col, edge_scale_size, edge_scale_alpha, sep = " + ")
            }

            #----------------#
            # theme ----
            #----------------#
            code_theme <- paste0("theme_graph() + theme(legend.position = \"", input$legendPos, "\")")

            #----------------#
            # glue ----
            #----------------#
            code <- paste(code_layout, code_edges, code_nodes, code_theme, sep = " + ")
            if (input$showLabs) {
                code <- paste0(code, "+ geom_node_text(label = 1:vcount(rv$g),colour=\"white\")")
            }
            # p <- eval(parse(text = code))
            p <- code

            return(p)
        })
        #----------------#
        DT_reactiveN <- reactive({
            create_attribute_df(rv$g, which = "nodes")
        })
        DT_reactiveE <- reactive({
            create_attribute_df(rv$g, which = "edges")
        })
        #----------------#
        # render plot
        ggnet <- renderPlot({
            eval(parse(text = gg_reactive()))
        })

        # render for save
        plotInput <- function() {
            eval(parse(text = gg_reactive()))
        }

        # save plot as png
        output$downloadData <- downloadHandler(
            filename = "graph.png",
            content = function(file) {
                ggsave(file, plot = plotInput())
            }
        )
        # render Attribute Manager
        dfattrN <- DT::renderDataTable(
            {
                DT_reactiveN()
            },
            options = list(
                lengthMenu = list(c(10, 20, -1), c("10", "20", "All")),
                pageLength = 10,
                searching = FALSE
            )
        )

        dfattrE <- DT::renderDataTable(
            {
                DT_reactiveE()
            },
            options = list(
                lengthMenu = list(c(10, 20, -1), c("10", "20", "All")),
                pageLength = 10,
                searching = FALSE
            )
        )

        output$Graph1 <- ggnet
        output$Graph2 <- ggnet
        output$Graph3 <- ggnet
        output$Graph4 <- ggnet
        output$attrManageN <- dfattrN
        output$attrManageE <- dfattrE

        #  DONE -----
        observeEvent(input$done, {
            result <- gg_reactive()
            result <- gsub("ggraph\\(rv\\$g,", paste0("ggraph\\(", text, ","), result)
            V(rv$g)$x <- rv$xy[, 1]
            V(rv$g)$y <- rv$xy[, 2]
            result <- gsub("rv\\$xy\\[,1\\]", "x", result)
            result <- gsub("rv\\$xy\\[,2\\]", "y", result)
            # result <- gsub("rv\\$xy\\[,1\\]",paste0("V(",text,")$x"),result)
            # result <- gsub("rv\\$xy\\[,2\\]",paste0("V(",text,")$y"),result)
            #
            result <- paste0("y <- ", "c(", paste0(round(rv$xy[, 2], 4), collapse = ", "), ")", "\n\n", result)
            result <- paste0("x <- ", "c(", paste0(round(rv$xy[, 1], 4), collapse = ", "), ")", "\n", result)


            result <- formatR::tidy_source(text = result, output = FALSE)$text.tidy
            result <- gsub("\\+", "\\+ \n\t", result)
            result <- gsub("\n\\s*\n", "\n", result)
            eval(parse(text = paste0("assign(\"", text, "\",rv$g", ",envir = .GlobalEnv)")))
            rstudioapi::insertText(result)
            invisible(stopApp())
        })

        observeEvent(input$cancel, {
            invisible(stopApp())
        })
    }

    viewer <- dialogViewer(dialogName = "SNAhelper", width = 990, height = 900)
    # viewer <- browserViewer(browser = getOption("browser"))
    runGadget(ui, server, stopOnCancel = FALSE, viewer = viewer)
}

#' @export
#' @examples
#' if (interactive()) {
#'     graph <- igraph::sample_gnp(100, 0.2)
#'     SNAhelperGadget(graph)
#' }
#' @rdname SNAhelper
SNAhelperGadget <- function(graph) {
    if (missing(graph)) {
        stop("You must provide an igraph object.", call. = FALSE)
    }
    graph <- deparse(substitute(graph))
    if (grepl("^\\s*[[:alpha:]]+[[:alnum:]\\.]*\\s*$", paste0(graph, collapse = ""))) {
        SNAhelper(graph)
    } else {
        stop("You must provide an igraph object.", call. = FALSE)
    }
}

#' @export
#' @rdname SNAhelper

SNAhelperAddin <- function() {
    context <- rstudioapi::getActiveDocumentContext()

    text <- context$selection[[1]]$text
    # text <- "gr"
    if (nchar(text) == 0) {
        stop("Please highlight an igraph object before using this addin.")
    }

    SNAhelper(text)
}
schochastics/snahelper documentation built on Nov. 19, 2023, 7:12 p.m.