From bba82600fe9f01315d1bd3f6148232097241c797 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Wed, 6 Aug 2025 21:19:06 -0400 Subject: [PATCH 1/5] successfully created and previewed html files for cytoscape viz --- NAMESPACE | 4 + R/visualizeNetworksWithHTML.R | 1047 ++++++++++++++++++++++++++++++++ man/exportCytoscapeToHTML.Rd | 57 ++ man/exportNetworkToHTML.Rd | 45 ++ man/generateCytoscapeConfig.Rd | 38 ++ man/generateJavaScriptCode.Rd | 17 + man/previewNetworkInBrowser.Rd | 20 + 7 files changed, 1228 insertions(+) create mode 100644 R/visualizeNetworksWithHTML.R create mode 100644 man/exportCytoscapeToHTML.Rd create mode 100644 man/exportNetworkToHTML.Rd create mode 100644 man/generateCytoscapeConfig.Rd create mode 100644 man/generateJavaScriptCode.Rd create mode 100644 man/previewNetworkInBrowser.Rd diff --git a/NAMESPACE b/NAMESPACE index 13b316c..cca1340 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,10 @@ # Generated by roxygen2: do not edit by hand export(annotateProteinInfoFromIndra) +export(exportNetworkToHTML) export(getPathwaysFromIndra) export(getSubnetworkFromIndra) +export(previewNetworkInBrowser) export(visualizeNetworks) importFrom(MASS,fitdistr) importFrom(RCy3,addAnnotationShape) @@ -15,6 +17,8 @@ importFrom(RCy3,groupAnnotation) importFrom(RCy3,layoutNetwork) importFrom(RCy3,mapVisualProperty) importFrom(RCy3,setVisualStyle) +importFrom(grDevices,colorRamp) +importFrom(grDevices,rgb) importFrom(httr,GET) importFrom(httr,POST) importFrom(httr,add_headers) diff --git a/R/visualizeNetworksWithHTML.R b/R/visualizeNetworksWithHTML.R new file mode 100644 index 0000000..a064063 --- /dev/null +++ b/R/visualizeNetworksWithHTML.R @@ -0,0 +1,1047 @@ +# ============================================================================= +# CYTOSCAPE VISUALIZATION PACKAGE FUNCTIONS +# These functions should go in your separate package +# ============================================================================= + +#' Helper function to map logFC values to colors +#' @param logFC_values Numeric vector of log fold change values +#' @importFrom grDevices colorRamp rgb +#' @noRd +mapLogFCToColor <- function(logFC_values) { + # Define the color palette + colors <- c("#ADD8E6", "#ADD8E6", "#D3D3D3", "#FFA590", "#FFA590") + + # Handle case where all values are the same or missing + if (all(is.na(logFC_values)) || length(unique(logFC_values[!is.na(logFC_values)])) <= 1) { + return(rep("#D3D3D3", length(logFC_values))) + } + + # Get range of logFC values + default_max <- 2 + max_logFC <- max(c(abs(logFC_values), default_max), na.rm = TRUE) + min_logFC <- -1 * max_logFC + + # Create color mapping function + color_map <- colorRamp(colors) + + # Normalize logFC values to [0, 1] range + normalized_values <- (logFC_values - min_logFC) / (max_logFC - min_logFC) + + # Handle NA values + normalized_values[is.na(normalized_values)] <- 0.5 # Default to middle color + + # Get RGB colors and convert to hex + rgb_colors <- color_map(normalized_values) + hex_colors <- rgb(rgb_colors[,1], rgb_colors[,2], rgb_colors[,3], maxColorValue = 255) + + return(hex_colors) +} + +# Define relationship categories and their properties +getRelationshipProperties <- function() { + list( + complex = list( + types = c("Complex"), + color = "#8B4513", # Brown + style = "solid", + arrow = "none", # Undirected + width = 4, + consolidate = "undirected" + ), + regulatory = list( + types = c("Inhibition", "Activation", "IncreaseAmount", "DecreaseAmount"), + colors = list( + "Inhibition" = "#FF4444", # Red + "Activation" = "#44AA44", # Green + "IncreaseAmount" = "#4488FF", # Blue + "DecreaseAmount" = "#FF8844" # Orange + ), + style = "solid", + arrow = "triangle", + width = 3, + consolidate = "bidirectional" + ), + phosphorylation = list( + types = c("Phosphorylation"), + color = "#9932CC", # Purple + style = "dashed", + arrow = "triangle", + width = 2, + consolidate = "directed" + ), + other = list( + color = "#666666", # Gray + style = "dotted", + arrow = "triangle", + width = 2, + consolidate = "directed" + ) + ) +} + +# Consolidate bidirectional edges based on relationship type +consolidateEdges <- function(edges) { + if (nrow(edges) == 0) return(edges) + + required_cols <- c("source", "target", "interaction") + missing_cols <- setdiff(required_cols, names(edges)) + if (length(missing_cols) > 0) { + stop("Missing required columns: ", paste(missing_cols, collapse = ", ")) + } + + relationship_props <- getRelationshipProperties() + consolidated_edges <- list() + processed_pairs <- c() + + for (i in 1:nrow(edges)) { + edge <- edges[i, ] + pair_key <- paste(sort(c(edge$source, edge$target)), edge$interaction, collapse = "-") + reverse_key <- paste(sort(c(edge$source, edge$target), decreasing = TRUE), edge$interaction, sep = "-") + + # Skip if we've already processed this pair + if (pair_key %in% processed_pairs) next + + # Determine relationship category + interaction_type <- edge$interaction + category <- "other" + for (cat_name in names(relationship_props)) { + if (interaction_type %in% relationship_props[[cat_name]]$types) { + category <- cat_name + break + } + } + + # Find reverse edge if it exists + reverse_edges <- edges[edges$source == edge$target & + edges$target == edge$source & + edges$interaction == edge$interaction, ] + + consolidation_type <- relationship_props[[category]]$consolidate + + if (nrow(reverse_edges) > 0 && consolidation_type %in% c("undirected", "bidirectional")) { + # Create consolidated edge + if (consolidation_type == "undirected") { + # For complex relationships - create undirected edge + consolidated_edge <- data.frame( + source = edge$source, + target = edge$target, + interaction = edge$interaction, + edge_type = "undirected", + category = category, + stringsAsFactors = FALSE + ) + } else { + # For regulatory relationships - create bidirectional edge + consolidated_edge <- data.frame( + source = edge$source, + target = edge$target, + interaction = paste(edge$interaction, "(bidirectional)"), + edge_type = "bidirectional", + category = category, + stringsAsFactors = FALSE + ) + } + + # Copy any additional columns from original edge + other_cols <- setdiff(names(edge), c("source", "target", "interaction")) + for (col in other_cols) { + consolidated_edge[[col]] <- edge[[col]] + } + + edge_key <- paste(edge$source, edge$target, consolidated_edge$interaction, sep = "-") + consolidated_edges[[edge_key]] <- consolidated_edge + + # Mark both directions as processed + processed_pairs <- c(processed_pairs, pair_key) + + } else { + # Keep as directed edge + directed_edge <- edge + directed_edge$edge_type <- "directed" + directed_edge$category <- category + + edge_key <- paste(edge$source, edge$target, edge$interaction, sep = "-") + consolidated_edges[[edge_key]] <- directed_edge + } + } + + # Convert list back to data frame + if (length(consolidated_edges) > 0) { + result <- do.call(rbind, consolidated_edges) + rownames(result) <- NULL + return(result) + } else { + return(edges[0, ]) # Return empty data frame with same structure + } +} + +# Get edge styling properties based on category and interaction type +getEdgeStyle <- function(interaction, category, edge_type) { + relationship_props <- getRelationshipProperties() + + if (category %in% names(relationship_props)) { + props <- relationship_props[[category]] + + # Handle regulatory relationships with specific colors + if (category == "regulatory" && "colors" %in% names(props)) { + base_interaction <- gsub(" \\(bidirectional\\)", "", interaction) + color <- if (base_interaction %in% names(props$colors)) { + props$colors[[base_interaction]] + } else { + "#666666" # Default gray + } + } else { + color <- props$color + } + + # Adjust arrow type based on edge type + arrow <- if (edge_type == "undirected") { + "none" + } else if (edge_type == "bidirectional") { + "triangle" # Will be handled specially in CSS + } else { + props$arrow + } + + return(list( + color = color, + style = props$style, + arrow = arrow, + width = props$width + )) + } else { + # Default styling for unknown relationships + return(relationship_props$other) + } +} + +createNodeElements <- function(nodes, displayLabelType = "id") { + # Map logFC to colors if logFC column exists + if ("logFC" %in% names(nodes)) { + node_colors <- mapLogFCToColor(nodes$logFC) + } else { + node_colors <- rep("#D3D3D3", nrow(nodes)) # Default color + } + + # Determine which column to use for labels + label_column <- if(displayLabelType == "hgncName" && "hgncName" %in% names(nodes)) { + "hgncName" + } else { + "id" + } + + apply(cbind(nodes, color = node_colors), 1, function(row) { + # Use the appropriate label, fallback to id if hgncName is missing/empty + display_label <- if(label_column == "hgncName" && !is.na(row['hgncName']) && row['hgncName'] != "") { + row['hgncName'] + } else { + row['id'] + } + + paste0("{ data: { id: '", row['id'], "', label: '", display_label, "', color: '", row['color'], "' } }") + }) +} + +createEdgeElements <- function(edges) { + if (nrow(edges) == 0) return(list()) + + # First consolidate edges + consolidated_edges <- consolidateEdges(edges) + + edge_elements <- list() + + for (i in 1:nrow(consolidated_edges)) { + row <- consolidated_edges[i,] + edge_key <- paste(row$source, row$target, row$interaction, sep = "-") + + # Get styling for this edge + style <- getEdgeStyle(row$interaction, row$category, row$edge_type) + + # Create edge data with styling information + edge_data <- paste0("{ data: { source: '", row$source, + "', target: '", row$target, + "', id: '", edge_key, + "', interaction: '", row$interaction, + "', edge_type: '", row$edge_type, + "', category: '", row$category, + "', color: '", style$color, + "', line_style: '", style$style, + "', arrow_shape: '", style$arrow, + "', width: ", style$width, " } }") + + edge_elements[[edge_key]] <- edge_data + } + + return(edge_elements) +} + +#' Generate Cytoscape visualization configuration +#' +#' This function creates a complete Cytoscape configuration object that can be +#' used to render a network visualization. It's decoupled from any specific +#' UI framework. +#' +#' @param node_elements List of node elements created by createNodeElements() +#' @param edge_elements List of edge elements created by createEdgeElements() +#' @param container_id ID of the HTML container element (default: 'network-cy') +#' @param event_handlers Optional list of event handler configurations +#' @param layout_options Optional list of layout configuration options +#' +#' @return List containing: +#' - elements: Combined node and edge elements +#' - style: Cytoscape style configuration +#' - layout: Layout configuration +#' - container_id: Container element ID +#' - js_code: Complete JavaScript code (for backward compatibility) +generateCytoscapeConfig <- function(node_elements, edge_elements, + container_id = "network-cy", + event_handlers = NULL, + layout_options = NULL) { + + # Default layout options + default_layout <- list( + name = "dagre", + rankDir = "TB", + animate = TRUE, + fit = TRUE, + padding = 30, + spacingFactor = 1.5, + nodeSep = 50, + edgeSep = 20, + rankSep = 80 + ) + + # Merge with custom layout options if provided + layout_config <- default_layout + if (!is.null(layout_options)) { + for (layout_name in names(layout_options)) { + layout_config[[layout_name]] <- layout_options[[layout_name]] + } + } + + # Define the style configuration (same as before) + style_config <- list( + list( + selector = "node", + style = list( + `background-color` = "data(color)", + label = "data(label)", + width = "function(ele) { var label = ele.data('label') || ''; var labelLength = label.length; return Math.max(60, Math.min(labelLength * 8 + 20, 150)); }", + height = "function(ele) { var label = ele.data('label') || ''; var labelLength = label.length; return Math.max(40, Math.min(labelLength * 2 + 30, 60)); }", + shape = "round-rectangle", + `font-size` = "11px", + `font-weight` = "bold", + color = "#000", + `text-valign` = "center", + `text-halign` = "center", + `text-wrap` = "wrap", + `text-max-width` = "function(ele) { var label = ele.data('label') || ''; var labelLength = label.length; return Math.max(50, Math.min(labelLength * 8 + 10, 140)); }", + `border-width` = 2, + `border-color` = "#333", + padding = "5px" + ) + ), + list( + selector = "edge", + style = list( + width = "data(width)", + `line-color` = "data(color)", + `line-style` = "data(line_style)", + label = "data(interaction)", + `curve-style` = "bezier", + `target-arrow-shape` = "data(arrow_shape)", + `target-arrow-color` = "data(color)", + `source-arrow-shape` = "function(ele) { return ele.data('edge_type') === 'bidirectional' ? 'triangle' : 'none'; }", + `source-arrow-color` = "data(color)", + `edge-text-rotation` = "autorotate", + `text-margin-y` = -12, + `text-halign` = "center", + `font-size` = "9px", + `font-weight` = "bold", + color = "data(color)", + `text-background-color` = "#ffffff", + `text-background-opacity` = 0.8, + `text-background-padding` = "2px" + ) + ), + list( + selector = "edge[category = 'complex']", + style = list( + `line-style` = "solid", + `target-arrow-shape` = "none", + `source-arrow-shape` = "none" + ) + ), + list( + selector = "edge[category = 'phosphorylation']", + style = list( + `line-style` = "dashed", + width = 2 + ) + ), + list( + selector = "edge[edge_type = 'bidirectional']", + style = list( + `source-arrow-shape` = "triangle", + `target-arrow-shape` = "triangle" + ) + ) + ) + + # Combine elements + elements <- c(node_elements, edge_elements) + + # Create the main configuration object + config <- list( + elements = elements, + style = style_config, + layout = layout_config, + container_id = container_id, + event_handlers = event_handlers + ) + + # Generate JavaScript code for backward compatibility + config$js_code <- generateJavaScriptCode(config) + + return(config) +} + +#' Generate JavaScript code from Cytoscape configuration +#' +#' Internal function to convert configuration object to JavaScript code +#' +#' @param config Configuration object from generateCytoscapeConfig() +#' @return Character string containing JavaScript code +generateJavaScriptCode <- function(config) { + + # Convert R list to JSON-like string for JavaScript + elements_js <- paste(config$elements, collapse = ", ") + + # Convert style configuration to JavaScript + style_js <- convertStyleToJS(config$style) + + # Convert layout configuration to JavaScript + layout_js <- convertLayoutToJS(config$layout) + + # Build event handlers JavaScript + event_handlers_js <- "" + if (!is.null(config$event_handlers)) { + handlers <- sapply(names(config$event_handlers), function(event) { + handler_code <- config$event_handlers[[event]] + switch(event, + "edge_click" = paste0("cy.on('tap', 'edge', ", handler_code, ");"), + "node_click" = paste0("cy.on('tap', 'node', ", handler_code, ");"), + handler_code # Custom event handler + ) + }) + event_handlers_js <- paste(handlers, collapse = "\n ") + } + + # Generate the complete JavaScript code + js_code <- paste0(" + cytoscape.use(cytoscapeDagre); + var cy = cytoscape({ + container: document.getElementById('", config$container_id, "'), + elements: [", elements_js, "], + style: ", style_js, ", + layout: ", layout_js, " + }); + + ", event_handlers_js) + + return(js_code) +} + +# Helper function to convert style list to JavaScript +convertStyleToJS <- function(style_list) { + style_items <- sapply(style_list, function(item) { + # Properly escape selector strings, especially those with special characters + selector_js <- paste0("\"", gsub("\"", "\\\"", item$selector), "\"") + + # Convert style properties + style_props <- sapply(names(item$style), function(prop) { + value <- item$style[[prop]] + if (is.character(value) && !grepl("^function\\(", value)) { + # Use double quotes and escape any existing double quotes + escaped_prop <- gsub("\"", "\\\"", prop) + escaped_value <- gsub("\"", "\\\"", value) + paste0("\"", escaped_prop, "\": \"", escaped_value, "\"") + } else { + escaped_prop <- gsub("\"", "\\\"", prop) + paste0("\"", escaped_prop, "\": ", value) + } + }) + + paste0("{ selector: ", selector_js, ", style: { ", paste(style_props, collapse = ", "), " } }") + }) + + paste0("[", paste(style_items, collapse = ", "), "]") +} + +# Helper function to convert layout list to JavaScript +convertLayoutToJS <- function(layout_list) { + layout_props <- sapply(names(layout_list), function(prop) { + value <- layout_list[[prop]] + if (is.character(value)) { + escaped_prop <- gsub("\"", "\\\"", prop) + escaped_value <- gsub("\"", "\\\"", value) + paste0("\"", escaped_prop, "\": \"", escaped_value, "\"") + } else if (is.logical(value)) { + escaped_prop <- gsub("\"", "\\\"", prop) + paste0("\"", escaped_prop, "\": ", tolower(value)) + } else { + escaped_prop <- gsub("\"", "\\\"", prop) + paste0("\"", escaped_prop, "\": ", value) + } + }) + + paste0("{ ", paste(layout_props, collapse = ", "), " }") +} + +#' Export Cytoscape network visualization to standalone HTML file +#' +#' This function takes a Cytoscape configuration object and creates a complete +#' standalone HTML file that can be opened in any web browser. +#' +#' @param config Configuration object from generateCytoscapeConfig() +#' @param filename Output HTML filename (default: "network_visualization.html") +#' @param title HTML page title (default: "Network Visualization") +#' @param width Container width (default: "100%") +#' @param height Container height (default: "600px") +#' @param include_controls Whether to include basic zoom/fit controls (default: TRUE) +#' @param custom_css Additional CSS styling (optional) +#' @param custom_js Additional JavaScript code (optional) +#' +#' @return Invisibly returns the file path of the created HTML file +#' +#' @examples +#' \dontrun{ +#' # Assuming you have nodes and edges data +#' node_elements <- createNodeElements(nodes) +#' edge_elements <- createEdgeElements(edges) +#' config <- generateCytoscapeConfig(node_elements, edge_elements) +#' +#' # Export to HTML +#' exportCytoscapeToHTML(config, "my_network.html") +#' } +exportCytoscapeToHTML <- function(config, + filename = "network_visualization.html", + title = "Network Visualization", + width = "100%", + height = "600px", + include_controls = TRUE, + custom_css = "", + custom_js = "") { + + # Validate config object + if (!is.list(config) || !all(c("elements", "style", "layout", "container_id") %in% names(config))) { + stop("Invalid config object. Must be generated by generateCytoscapeConfig()") + } + + # Generate the JavaScript code if not already present + if (!"js_code" %in% names(config)) { + config$js_code <- generateJavaScriptCode(config) + } + + # Create controls HTML and JavaScript if requested + controls_html <- "" + controls_js <- "" + controls_css <- "" + + if (include_controls) { + controls_html <- ' +
+ + + + + +
' + + controls_css <- ' + .control-btn { + margin: 2px; + padding: 6px 12px; + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + } + .control-btn:hover { + background-color: #e9ecef; + } + .control-btn:active { + background-color: #dee2e6; + }' + + controls_js <- ' + // Add control event listeners + document.getElementById("fit-btn").addEventListener("click", function() { + cy.fit(); + }); + + document.getElementById("center-btn").addEventListener("click", function() { + cy.center(); + }); + + document.getElementById("zoom-in-btn").addEventListener("click", function() { + cy.zoom({ + level: cy.zoom() * 1.2, + renderedPosition: { x: cy.width()/2, y: cy.height()/2 } + }); + }); + + document.getElementById("zoom-out-btn").addEventListener("click", function() { + cy.zoom({ + level: cy.zoom() * 0.8, + renderedPosition: { x: cy.width()/2, y: cy.height()/2 } + }); + }); + + document.getElementById("reset-btn").addEventListener("click", function() { + cy.reset(); + cy.fit(); + });' + } + + # Create the complete HTML content + html_content <- paste0(' + + + + + ', title, ' + + + + + + + + + + +
+

', title, '

+ + ', controls_html, ' + +
+ +
+ Instructions: + • Click and drag to pan the network + • Use mouse wheel to zoom in/out + • Click on nodes or edges to select them + ', if(include_controls) '• Use the buttons above for common navigation actions' else '', ' +
+
+ + + +') + + # Write the HTML file + writeLines(html_content, filename) + + # Print success message + cat("Network visualization exported to:", normalizePath(filename), "\n") + + # Return the file path invisibly + invisible(normalizePath(filename)) +} + +#' Export network data with Cytoscape visualization +#' +#' Convenience function that takes nodes and edges data directly and creates +#' both the configuration and HTML export in one step. +#' +#' @param nodes Data frame with node information +#' @param edges Data frame with edge information +#' @param filename Output HTML filename +#' @param displayLabelType Type of label to display ("id" or "hgncName") +#' @param ... Additional arguments passed to exportCytoscapeToHTML() +#' @export +#' @return Invisibly returns the file path of the created HTML file +exportNetworkToHTML <- function(nodes, edges, + filename = "network_visualization.html", + displayLabelType = "id", + ...) { + + # Create elements + node_elements <- createNodeElements(nodes, displayLabelType) + edge_elements <- createEdgeElements(edges) + + # Generate configuration + config <- generateCytoscapeConfig(node_elements, edge_elements) + + # Export to HTML + exportCytoscapeToHTML(config, filename, ...) +} + +#' Export Cytoscape network visualization to standalone HTML file +#' +#' This function takes a Cytoscape configuration object and creates a complete +#' standalone HTML file that can be opened in any web browser. +#' +#' @param config Configuration object from generateCytoscapeConfig() +#' @param filename Output HTML filename (default: "network_visualization.html") +#' @param title HTML page title (default: "Network Visualization") +#' @param width Container width (default: "100%") +#' @param height Container height (default: "600px") +#' @param include_controls Whether to include basic zoom/fit controls (default: TRUE) +#' @param custom_css Additional CSS styling (optional) +#' @param custom_js Additional JavaScript code (optional) +#' +#' @return Invisibly returns the file path of the created HTML file +#' +#' @examples +#' \dontrun{ +#' # Assuming you have nodes and edges data +#' node_elements <- createNodeElements(nodes) +#' edge_elements <- createEdgeElements(edges) +#' config <- generateCytoscapeConfig(node_elements, edge_elements) +#' +#' # Export to HTML +#' exportCytoscapeToHTML(config, "my_network.html") +#' } +exportCytoscapeToHTML <- function(config, + filename = "network_visualization.html", + title = "Network Visualization", + width = "100%", + height = "600px", + include_controls = TRUE, + custom_css = "", + custom_js = "") { + + # Validate config object + if (!is.list(config) || !all(c("elements", "style", "layout", "container_id") %in% names(config))) { + stop("Invalid config object. Must be generated by generateCytoscapeConfig()") + } + + # Generate the JavaScript code if not already present + if (!"js_code" %in% names(config)) { + config$js_code <- generateJavaScriptCode(config) + } + + # Create controls HTML and JavaScript if requested + controls_html <- "" + controls_js <- "" + controls_css <- "" + + if (include_controls) { + controls_html <- ' +
+ + + + + +
' + + controls_css <- ' + .control-btn { + margin: 2px; + padding: 6px 12px; + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + } + .control-btn:hover { + background-color: #e9ecef; + } + .control-btn:active { + background-color: #dee2e6; + }' + + controls_js <- ' + // Add control event listeners + document.getElementById("fit-btn").addEventListener("click", function() { + cy.fit(); + }); + + document.getElementById("center-btn").addEventListener("click", function() { + cy.center(); + }); + + document.getElementById("zoom-in-btn").addEventListener("click", function() { + cy.zoom({ + level: cy.zoom() * 1.2, + renderedPosition: { x: cy.width()/2, y: cy.height()/2 } + }); + }); + + document.getElementById("zoom-out-btn").addEventListener("click", function() { + cy.zoom({ + level: cy.zoom() * 0.8, + renderedPosition: { x: cy.width()/2, y: cy.height()/2 } + }); + }); + + document.getElementById("reset-btn").addEventListener("click", function() { + cy.reset(); + cy.fit(); + });' + } + + # Create the complete HTML content + html_content <- paste0(' + + + + + ', title, ' + + + + + + + + + + +
+

', title, '

+ + ', controls_html, ' + +
+ +
+ Instructions: + • Click and drag to pan the network + • Use mouse wheel to zoom in/out + • Click on nodes or edges to select them + ', if(include_controls) '• Use the buttons above for common navigation actions' else '', ' +
+
+ + + +') + + # Write the HTML file + writeLines(html_content, filename) + + # Print success message + cat("Network visualization exported to:", normalizePath(filename), "\n") + + # Return the file path invisibly + invisible(normalizePath(filename)) +} + +#' Export network data with Cytoscape visualization +#' +#' Convenience function that takes nodes and edges data directly and creates +#' both the configuration and HTML export in one step. +#' +#' @param nodes Data frame with node information +#' @param edges Data frame with edge information +#' @param filename Output HTML filename +#' @param displayLabelType Type of label to display ("id" or "hgncName") +#' @param ... Additional arguments passed to exportCytoscapeToHTML() +#' +#' @return Invisibly returns the file path of the created HTML file +exportNetworkToHTML <- function(nodes, edges, + filename = "network_visualization.html", + displayLabelType = "id", + ...) { + + # Create elements + node_elements <- createNodeElements(nodes, displayLabelType) + edge_elements <- createEdgeElements(edges) + + # Generate configuration + config <- generateCytoscapeConfig(node_elements, edge_elements) + + # Export to HTML + exportCytoscapeToHTML(config, filename, ...) +} + +#' Preview network in browser +#' +#' Creates a temporary HTML file and opens it in the default web browser +#' @export +#' @param nodes Data frame with node information +#' @param edges Data frame with edge information +#' @param displayLabelType Type of label to display ("id" or "hgncName") +#' @param ... Additional arguments passed to exportCytoscapeToHTML() +previewNetworkInBrowser <- function(nodes, edges, + displayLabelType = "id", + ...) { + + # Create elements + node_elements <- createNodeElements(nodes, displayLabelType) + edge_elements <- createEdgeElements(edges) + + # Generate configuration + config <- generateCytoscapeConfig(node_elements, edge_elements) + + # Create temporary filename + temp_file <- tempfile(fileext = ".html") + + # Export to temp file + exportCytoscapeToHTML(config, temp_file, ...) + + # Open in browser + if (interactive()) { + browseURL(temp_file) + cat("Network opened in browser. Temporary file:", temp_file, "\n") + } + + invisible(temp_file) +} \ No newline at end of file diff --git a/man/exportCytoscapeToHTML.Rd b/man/exportCytoscapeToHTML.Rd new file mode 100644 index 0000000..d886141 --- /dev/null +++ b/man/exportCytoscapeToHTML.Rd @@ -0,0 +1,57 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/visualizeNetworksWithHTML.R +\name{exportCytoscapeToHTML} +\alias{exportCytoscapeToHTML} +\title{Export Cytoscape network visualization to standalone HTML file} +\usage{ +exportCytoscapeToHTML( + config, + filename = "network_visualization.html", + title = "Network Visualization", + width = "100\%", + height = "600px", + include_controls = TRUE, + custom_css = "", + custom_js = "" +) + +exportCytoscapeToHTML( + config, + filename = "network_visualization.html", + title = "Network Visualization", + width = "100\%", + height = "600px", + include_controls = TRUE, + custom_css = "", + custom_js = "" +) +} +\arguments{ +\item{config}{Configuration object from generateCytoscapeConfig()} + +\item{filename}{Output HTML filename (default: "network_visualization.html")} + +\item{title}{HTML page title (default: "Network Visualization")} + +\item{width}{Container width (default: "100%")} + +\item{height}{Container height (default: "600px")} + +\item{include_controls}{Whether to include basic zoom/fit controls (default: TRUE)} + +\item{custom_css}{Additional CSS styling (optional)} + +\item{custom_js}{Additional JavaScript code (optional)} +} +\value{ +Invisibly returns the file path of the created HTML file + +Invisibly returns the file path of the created HTML file +} +\description{ +This function takes a Cytoscape configuration object and creates a complete +standalone HTML file that can be opened in any web browser. + +This function takes a Cytoscape configuration object and creates a complete +standalone HTML file that can be opened in any web browser. +} diff --git a/man/exportNetworkToHTML.Rd b/man/exportNetworkToHTML.Rd new file mode 100644 index 0000000..52ec877 --- /dev/null +++ b/man/exportNetworkToHTML.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/visualizeNetworksWithHTML.R +\name{exportNetworkToHTML} +\alias{exportNetworkToHTML} +\title{Export network data with Cytoscape visualization} +\usage{ +exportNetworkToHTML( + nodes, + edges, + filename = "network_visualization.html", + displayLabelType = "id", + ... +) + +exportNetworkToHTML( + nodes, + edges, + filename = "network_visualization.html", + displayLabelType = "id", + ... +) +} +\arguments{ +\item{nodes}{Data frame with node information} + +\item{edges}{Data frame with edge information} + +\item{filename}{Output HTML filename} + +\item{displayLabelType}{Type of label to display ("id" or "hgncName")} + +\item{...}{Additional arguments passed to exportCytoscapeToHTML()} +} +\value{ +Invisibly returns the file path of the created HTML file + +Invisibly returns the file path of the created HTML file +} +\description{ +Convenience function that takes nodes and edges data directly and creates +both the configuration and HTML export in one step. + +Convenience function that takes nodes and edges data directly and creates +both the configuration and HTML export in one step. +} diff --git a/man/generateCytoscapeConfig.Rd b/man/generateCytoscapeConfig.Rd new file mode 100644 index 0000000..cb62cb3 --- /dev/null +++ b/man/generateCytoscapeConfig.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/visualizeNetworksWithHTML.R +\name{generateCytoscapeConfig} +\alias{generateCytoscapeConfig} +\title{Generate Cytoscape visualization configuration} +\usage{ +generateCytoscapeConfig( + node_elements, + edge_elements, + container_id = "network-cy", + event_handlers = NULL, + layout_options = NULL +) +} +\arguments{ +\item{node_elements}{List of node elements created by createNodeElements()} + +\item{edge_elements}{List of edge elements created by createEdgeElements()} + +\item{container_id}{ID of the HTML container element (default: 'network-cy')} + +\item{event_handlers}{Optional list of event handler configurations} + +\item{layout_options}{Optional list of layout configuration options} +} +\value{ +List containing: + - elements: Combined node and edge elements + - style: Cytoscape style configuration + - layout: Layout configuration + - container_id: Container element ID + - js_code: Complete JavaScript code (for backward compatibility) +} +\description{ +This function creates a complete Cytoscape configuration object that can be +used to render a network visualization. It's decoupled from any specific +UI framework. +} diff --git a/man/generateJavaScriptCode.Rd b/man/generateJavaScriptCode.Rd new file mode 100644 index 0000000..3e4beea --- /dev/null +++ b/man/generateJavaScriptCode.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/visualizeNetworksWithHTML.R +\name{generateJavaScriptCode} +\alias{generateJavaScriptCode} +\title{Generate JavaScript code from Cytoscape configuration} +\usage{ +generateJavaScriptCode(config) +} +\arguments{ +\item{config}{Configuration object from generateCytoscapeConfig()} +} +\value{ +Character string containing JavaScript code +} +\description{ +Internal function to convert configuration object to JavaScript code +} diff --git a/man/previewNetworkInBrowser.Rd b/man/previewNetworkInBrowser.Rd new file mode 100644 index 0000000..6dde024 --- /dev/null +++ b/man/previewNetworkInBrowser.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/visualizeNetworksWithHTML.R +\name{previewNetworkInBrowser} +\alias{previewNetworkInBrowser} +\title{Preview network in browser} +\usage{ +previewNetworkInBrowser(nodes, edges, displayLabelType = "id", ...) +} +\arguments{ +\item{nodes}{Data frame with node information} + +\item{edges}{Data frame with edge information} + +\item{displayLabelType}{Type of label to display ("id" or "hgncName")} + +\item{...}{Additional arguments passed to exportCytoscapeToHTML()} +} +\description{ +Creates a temporary HTML file and opens it in the default web browser +} From 2f76437894d3f41674700ec20bf3f2f8a85511b8 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Wed, 6 Aug 2025 21:28:02 -0400 Subject: [PATCH 2/5] added legends into html --- R/visualizeNetworksWithHTML.R | 150 +++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/R/visualizeNetworksWithHTML.R b/R/visualizeNetworksWithHTML.R index a064063..ac6771a 100644 --- a/R/visualizeNetworksWithHTML.R +++ b/R/visualizeNetworksWithHTML.R @@ -907,6 +907,69 @@ exportCytoscapeToHTML <- function(config, background-color: #fff; } + #legend { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 15px; + } + + .legend-title { + font-weight: bold; + margin-bottom: 10px; + font-size: 14px; + color: #333; + } + + .legend-item { + display: flex; + align-items: center; + margin-bottom: 8px; + font-size: 12px; + } + + .legend-color { + width: 20px; + height: 20px; + border: 2px solid #333; + border-radius: 3px; + margin-right: 8px; + } + + .legend-gradient { + height: 120px; + width: 20px; + border: 2px solid #333; + border-radius: 3px; + margin-right: 8px; + background: linear-gradient(to top, #ADD8E6, #D3D3D3, #FFA590); + } + + .legend-gradient-labels { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 120px; + font-size: 11px; + } + + .edge-legend { + margin-top: 20px; + } + + .edge-legend-item { + display: flex; + align-items: center; + margin-bottom: 6px; + font-size: 11px; + } + + .edge-legend-line { + width: 30px; + height: 2px; + margin-right: 8px; + } + ', controls_css, ' ', custom_css, ' @@ -928,7 +991,12 @@ exportCytoscapeToHTML <- function(config, ', controls_html, ' -
+
+
+
+ +
+
Instructions: @@ -940,6 +1008,83 @@ exportCytoscapeToHTML <- function(config,
- - - -
-

', title, '

- - ', controls_html, ' - -
- -
- Instructions: - • Click and drag to pan the network - • Use mouse wheel to zoom in/out - • Click on nodes or edges to select them - ', if(include_controls) '• Use the buttons above for common navigation actions' else '', ' -
-
- - - -') - - # Write the HTML file - writeLines(html_content, filename) - - # Print success message - cat("Network visualization exported to:", normalizePath(filename), "\n") - - # Return the file path invisibly - invisible(normalizePath(filename)) -} - -#' Export network data with Cytoscape visualization -#' -#' Convenience function that takes nodes and edges data directly and creates -#' both the configuration and HTML export in one step. -#' -#' @param nodes Data frame with node information -#' @param edges Data frame with edge information -#' @param filename Output HTML filename -#' @param displayLabelType Type of label to display ("id" or "hgncName") -#' @param ... Additional arguments passed to exportCytoscapeToHTML() -#' @export -#' @return Invisibly returns the file path of the created HTML file -exportNetworkToHTML <- function(nodes, edges, - filename = "network_visualization.html", - displayLabelType = "id", - ...) { - - # Create elements - node_elements <- createNodeElements(nodes, displayLabelType) - edge_elements <- createEdgeElements(edges) - - # Generate configuration - config <- generateCytoscapeConfig(node_elements, edge_elements) - - # Export to HTML - exportCytoscapeToHTML(config, filename, ...) -} - -#' Export Cytoscape network visualization to standalone HTML file -#' -#' This function takes a Cytoscape configuration object and creates a complete -#' standalone HTML file that can be opened in any web browser. -#' -#' @param config Configuration object from generateCytoscapeConfig() -#' @param filename Output HTML filename (default: "network_visualization.html") -#' @param title HTML page title (default: "Network Visualization") -#' @param width Container width (default: "100%") -#' @param height Container height (default: "600px") -#' @param include_controls Whether to include basic zoom/fit controls (default: TRUE) -#' @param custom_css Additional CSS styling (optional) -#' @param custom_js Additional JavaScript code (optional) -#' -#' @return Invisibly returns the file path of the created HTML file -#' -#' @examples -#' \dontrun{ -#' # Assuming you have nodes and edges data -#' node_elements <- createNodeElements(nodes) -#' edge_elements <- createEdgeElements(edges) -#' config <- generateCytoscapeConfig(node_elements, edge_elements) -#' -#' # Export to HTML -#' exportCytoscapeToHTML(config, "my_network.html") -#' } -exportCytoscapeToHTML <- function(config, - filename = "network_visualization.html", - title = "Network Visualization", - width = "100%", - height = "600px", - include_controls = TRUE, - custom_css = "", - custom_js = "") { - - # Validate config object - if (!is.list(config) || !all(c("elements", "style", "layout", "container_id") %in% names(config))) { - stop("Invalid config object. Must be generated by generateCytoscapeConfig()") - } - - # Generate the JavaScript code if not already present - if (!"js_code" %in% names(config)) { - config$js_code <- generateJavaScriptCode(config) - } - - # Create controls HTML and JavaScript if requested - controls_html <- "" - controls_js <- "" - controls_css <- "" - - if (include_controls) { - controls_html <- ' -
- - - - - -
' - - controls_css <- ' - .control-btn { - margin: 2px; - padding: 6px 12px; - background-color: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 4px; - cursor: pointer; - font-size: 12px; - } - .control-btn:hover { - background-color: #e9ecef; - } - .control-btn:active { - background-color: #dee2e6; - }' - - controls_js <- ' - // Add control event listeners - document.getElementById("fit-btn").addEventListener("click", function() { - cy.fit(); - }); - - document.getElementById("center-btn").addEventListener("click", function() { - cy.center(); - }); - - document.getElementById("zoom-in-btn").addEventListener("click", function() { - cy.zoom({ - level: cy.zoom() * 1.2, - renderedPosition: { x: cy.width()/2, y: cy.height()/2 } - }); - }); - - document.getElementById("zoom-out-btn").addEventListener("click", function() { - cy.zoom({ - level: cy.zoom() * 0.8, - renderedPosition: { x: cy.width()/2, y: cy.height()/2 } - }); - }); - - document.getElementById("reset-btn").addEventListener("click", function() { - cy.reset(); - cy.fit(); - });' - } - - # Create the complete HTML content - html_content <- paste0(' - - - - - ', title, ' - - - - - - -