mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #3817 from bgruening/cytoscape
Cytoscape as the Galaxy charts plugin
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
define( [], function() { return {nvd3_bar:require( "visualizations/nvd3/bar/config" ), nvd3_bar_horizontal:require( "visualizations/nvd3/bar_horizontal/config" ), nvd3_bar_horizontal_stacked:require( "visualizations/nvd3/bar_horizontal_stacked/config" ), nvd3_bar_stacked:require( "visualizations/nvd3/bar_stacked/config" ), nvd3_line:require( "visualizations/nvd3/line/config" ), nvd3_line_focus:require( "visualizations/nvd3/line_focus/config" ), nvd3_scatter:require( "visualizations/nvd3/scatter/config" ), nvd3_stackedarea:require( "visualizations/nvd3/stackedarea/config" ), nvd3_stackedarea_full:require( "visualizations/nvd3/stackedarea_full/config" ), nvd3_stackedarea_stream:require( "visualizations/nvd3/stackedarea_stream/config" ), nvd3_pie:require( "visualizations/nvd3/pie/config" ), nvd3_histogram:require( "visualizations/nvd3/histogram/config" ), nvd3_histogram_discrete:require( "visualizations/nvd3/histogram_discrete/config" ), jqplot_bar:require( "visualizations/jqplot/bar/config" ), jqplot_boxplot:require( "visualizations/jqplot/boxplot/config" ), jqplot_histogram_discrete:require( "visualizations/jqplot/histogram_discrete/config" ), jqplot_line:require( "visualizations/jqplot/line/config" ), jqplot_scatter:require( "visualizations/jqplot/scatter/config" ), biojs_msa:require( "visualizations/biojs/msa/config" ), biojs_drawrnajs:require( "visualizations/biojs/drawrnajs/config" ), others_example:require( "visualizations/others/example/config" ), others_heatmap:require( "visualizations/others/heatmap/config" ), others_heatmap_cluster:require( "visualizations/others/heatmap_cluster/config" ), cytoscape_basic:require( "visualizations/cytoscape/basic/config" ), pv_viewer:require( "visualizations/pv/viewer/config" ), benfred_venn:require( "visualizations/benfred/venn/config" ), ngl_viewer:require( "visualizations/ngl/viewer/config" ),} } );
|
||||
define( [], function() { return {nvd3_bar:require( "visualizations/nvd3/bar/config" ), nvd3_bar_horizontal:require( "visualizations/nvd3/bar_horizontal/config" ), nvd3_bar_horizontal_stacked:require( "visualizations/nvd3/bar_horizontal_stacked/config" ), nvd3_bar_stacked:require( "visualizations/nvd3/bar_stacked/config" ), nvd3_line:require( "visualizations/nvd3/line/config" ), nvd3_line_focus:require( "visualizations/nvd3/line_focus/config" ), nvd3_scatter:require( "visualizations/nvd3/scatter/config" ), nvd3_stackedarea:require( "visualizations/nvd3/stackedarea/config" ), nvd3_stackedarea_full:require( "visualizations/nvd3/stackedarea_full/config" ), nvd3_stackedarea_stream:require( "visualizations/nvd3/stackedarea_stream/config" ), nvd3_pie:require( "visualizations/nvd3/pie/config" ), nvd3_histogram:require( "visualizations/nvd3/histogram/config" ), nvd3_histogram_discrete:require( "visualizations/nvd3/histogram_discrete/config" ), jqplot_bar:require( "visualizations/jqplot/bar/config" ), jqplot_boxplot:require( "visualizations/jqplot/boxplot/config" ), jqplot_histogram_discrete:require( "visualizations/jqplot/histogram_discrete/config" ), jqplot_line:require( "visualizations/jqplot/line/config" ), jqplot_scatter:require( "visualizations/jqplot/scatter/config" ), biojs_msa:require( "visualizations/biojs/msa/config" ), biojs_drawrnajs:require( "visualizations/biojs/drawrnajs/config" ), others_example:require( "visualizations/others/example/config" ), others_heatmap:require( "visualizations/others/heatmap/config" ), others_heatmap_cluster:require( "visualizations/others/heatmap_cluster/config" ), cytoscape_viewer:require( "visualizations/cytoscape/viewer/config" ), pv_viewer:require( "visualizations/pv/viewer/config" ), benfred_venn:require( "visualizations/benfred/venn/config" ), ngl_viewer:require( "visualizations/ngl/viewer/config" ),} } );
|
||||
@@ -3,7 +3,7 @@
|
||||
"jqplot" : [ "bar", "boxplot", "histogram_discrete", "line", "scatter" ],
|
||||
"biojs" : [ "msa", "drawrnajs" ],
|
||||
"others" : [ "example", "heatmap", "heatmap_cluster" ],
|
||||
"cytoscape" : [ "basic" ],
|
||||
"cytoscape" : [ "viewer" ],
|
||||
"pv" : [ "viewer" ],
|
||||
"benfred" : [ "venn" ],
|
||||
"ngl" : [ "viewer" ]
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/** Convert SIF files to JSON in a format required by Cytoscape
|
||||
for rendering the graph */
|
||||
/** Inspired from https://www.npmjs.com/package/sif.js */
|
||||
define( [], function() {
|
||||
|
||||
function SIFJS() {};
|
||||
|
||||
// Private variables and methods
|
||||
var nodes = {}, links = {}, content = [];
|
||||
|
||||
// Add a node
|
||||
var _getNode = function( id ) {
|
||||
if(!nodes[id]) {
|
||||
nodes[id] = {id: id};
|
||||
}
|
||||
return nodes[id];
|
||||
};
|
||||
|
||||
// Parse each line of the SIF file
|
||||
var _parse = function( line, i ) {
|
||||
var source = _getNode( line[0] ), interaction = ( line[1] ? line[1] : "" ), j, length;
|
||||
line = ( line.split('\t').length > 1 ) ? line.split('\t') : line.split(' ');
|
||||
if( line.length && line.length > 0 && line[0] !== "" ) {
|
||||
if( interaction !== "" ) {
|
||||
// Get all the target nodes for a source
|
||||
for ( j = 2, length = line.length; j < length; j++ ) {
|
||||
if( line[j] !== "" ) {
|
||||
// Create an object for each target for the source
|
||||
var target = _getNode( line[j] ),
|
||||
relation_object = {target: target.id,
|
||||
source: source.id,
|
||||
id: source.id + target.id,
|
||||
relation: interaction.replace(/[''""]+/g, '') }; // Replace quotes in relation
|
||||
if( source < target ) {
|
||||
links[ source.id + target.id + interaction ] = relation_object;
|
||||
} else {
|
||||
links[ target.id + source.id + interaction ] = relation_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle the case of single node i.e. no relation with any other node
|
||||
// and only the source exists
|
||||
else {
|
||||
links[ source.id ] = { target: "", source: source.id, id: source.id, relation: "" };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Convert to array from objects
|
||||
var _toArr = function( obj ) {
|
||||
var arr = [];
|
||||
for (var key in obj) {
|
||||
arr.push( obj[key] );
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Make content from list of nodes and links
|
||||
var _toDataArr = function( nodes, links ) {
|
||||
var content = [], node_length, links_length;
|
||||
// Make a list of all nodes
|
||||
for(var i = 0, node_length = nodes.length; i < node_length; i++) {
|
||||
content.push( { 'data': nodes[i] } );
|
||||
}
|
||||
// Make a list of all relationships among nodes
|
||||
for(var i = 0, links_length = links.length; i < links_length; i++) {
|
||||
content.push( { 'data': links[i] } );
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
// Public method. Return graph data as JSON
|
||||
SIFJS.parse = function( text ) {
|
||||
var lines = text.split('\n'), i, length, nodesarr, linksarr;
|
||||
for ( i = 0, length = lines.length; i < length; i++ ) {
|
||||
_parse( lines[i], i );
|
||||
}
|
||||
nodesarr = _toArr( nodes );
|
||||
linksarr = _toArr( links );
|
||||
return { content: _toDataArr( nodesarr, linksarr ) };
|
||||
};
|
||||
|
||||
return {
|
||||
SIFJS : SIFJS
|
||||
};
|
||||
});
|
||||
@@ -120,4 +120,4 @@ define( [], function() {
|
||||
clone : clone,
|
||||
isJSON : isJSON
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
define( [], function() {
|
||||
return {
|
||||
title : 'Cytoscape (Default)',
|
||||
library : 'Cytoscape',
|
||||
datatypes : [ 'json' ],
|
||||
keywords : 'cytoscape graph nodes edges',
|
||||
description : 'Basic Cytoscape viewer for connected graphs hosted at http://js.cytoscape.org.'
|
||||
}
|
||||
});
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
-56
@@ -1,56 +0,0 @@
|
||||
define( [ 'utilities/utils', 'plugins/cytoscape/cytoscape' ], function( Utils, Cytoscape ) {
|
||||
return Backbone.Model.extend({
|
||||
initialize: function( options ) {
|
||||
var chart = options.chart;
|
||||
var dataset = options.dataset;
|
||||
Utils.get( {
|
||||
url : dataset.download_url,
|
||||
success : function( content ) {
|
||||
try {
|
||||
Cytoscape( Utils.merge( content, {
|
||||
container: $( '#' + options.targets[ 0 ] ),
|
||||
layout: {
|
||||
name: 'cose',
|
||||
idealEdgeLength: 100,
|
||||
nodeOverlap: 20
|
||||
},
|
||||
style: Cytoscape.stylesheet()
|
||||
.selector('node')
|
||||
.css({
|
||||
'content': 'data(name)',
|
||||
'text-valign': 'center',
|
||||
'color': 'white',
|
||||
'text-outline-width': 2,
|
||||
'text-outline-color': '#888'
|
||||
})
|
||||
.selector('edge')
|
||||
.css({
|
||||
'target-arrow-shape': 'triangle'
|
||||
})
|
||||
.selector(':selected')
|
||||
.css({
|
||||
'background-color': 'black',
|
||||
'line-color': 'black',
|
||||
'target-arrow-color': 'black',
|
||||
'source-arrow-color': 'black'
|
||||
})
|
||||
.selector('.faded')
|
||||
.css({
|
||||
'opacity': 0.25,
|
||||
'text-opacity': 0
|
||||
})
|
||||
} ) );
|
||||
chart.state( 'ok', 'Chart drawn.' );
|
||||
} catch( err ) {
|
||||
chart.state( 'failed', err );
|
||||
}
|
||||
options.process.resolve();
|
||||
},
|
||||
error: function() {
|
||||
chart.state( 'failed', 'Failed to access dataset.' );
|
||||
options.process.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
define( [], function() {
|
||||
return {
|
||||
title : 'Cytoscape',
|
||||
library : 'Cytoscape',
|
||||
datatypes : [ 'sif', 'json' ],
|
||||
keywords : 'cytoscape graph nodes edges',
|
||||
description : 'A viewer based on graph theory/ network library for analysis and visualisation hosted at http://js.cytoscape.org.',
|
||||
settings : {
|
||||
curve_style : {
|
||||
label : 'Curve style',
|
||||
help : 'Select a curving method used to separate two or more edges between two nodes.',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
value : 'haystack',
|
||||
data : [ { label : 'Haystack', value : 'haystack' },
|
||||
{ label : 'Bezier', value : 'bezier' },
|
||||
{ label : 'Unbundled bezier', value : 'unbundled-bezier' },
|
||||
{ label : 'Segments', value: 'segments' } ]
|
||||
},
|
||||
layout_name : {
|
||||
label : 'Layout name',
|
||||
help : 'Select a kind of position of nodes in graph.',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
value : 'haystack',
|
||||
data : [ { label : 'Breadth First', value : 'breadthfirst' },
|
||||
{ label : 'Circle', value : 'circle' },
|
||||
{ label : 'Concentric', value : 'concentric' },
|
||||
{ label : 'Cose', value : 'cose' },
|
||||
{ label : 'Grid', value : 'grid' },
|
||||
{ label : 'Null', value: 'null' },
|
||||
{ label : 'Preset', value : 'preset' },
|
||||
{ label : 'Random', value : 'random' } ]
|
||||
},
|
||||
directed : {
|
||||
label : 'Directed/Undirected',
|
||||
help : 'Select a kind of edge.',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
value : '',
|
||||
data : [ { label : 'Directed', value : 'triangle' },
|
||||
{ label : 'Undirected', value : '' } ]
|
||||
},
|
||||
search_algorithm : {
|
||||
label : 'Graph algorithms',
|
||||
help : 'Select a search algorithm. For Breadth First Search and Depth First Search, please click on any node of the graph. For A*, please click on two nodes, one for the root and another for the destination.',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
value : '',
|
||||
data : [ { label : 'Breadth First Search', value : 'bfs' },
|
||||
{ label : 'Depth First Search', value : 'dfs' },
|
||||
{ label : 'Minimum Spanning Tree (Kruskal)', value : 'kruskal' },
|
||||
{ label : 'A*', value : 'astar' },
|
||||
{ label : 'None', value : '' } ]
|
||||
},
|
||||
graph_traversal: {
|
||||
label : 'Graph traversal',
|
||||
help : 'To select a graph traversal type, please click on any node of the graph',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
value : '',
|
||||
data : [ { label : 'Successors', value : 'successors' },
|
||||
{ label : 'Predecessors', value : 'predecessors' },
|
||||
{ label : 'Outgoers', value : 'outgoers' },
|
||||
{ label : 'Incomers', value : 'incomers' },
|
||||
{ label : 'Roots', value : 'roots' },
|
||||
{ label : 'Leaves', value : 'leaves' },
|
||||
{ label : 'None', value : '' } ]
|
||||
},
|
||||
color_picker_nodes : {
|
||||
label : 'Select a color for nodes',
|
||||
type : 'color',
|
||||
value : '#548DB8'
|
||||
},
|
||||
color_picker_edges : {
|
||||
label : 'Select a color for edges',
|
||||
type : 'color',
|
||||
value : '#A5A5A5'
|
||||
},
|
||||
color_picker_highlighted : {
|
||||
label : 'Select a color for highlighted nodes and edges',
|
||||
type : 'color',
|
||||
value : '#C00000'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
+196
@@ -0,0 +1,196 @@
|
||||
define( [ 'utilities/utils', 'utilities/sifjson', 'plugins/cytoscape/cytoscape' ], function( Utils, SIF, Cytoscape ) {
|
||||
return Backbone.Model.extend({
|
||||
initialize: function( options ) {
|
||||
var self = this,
|
||||
chart = options.chart,
|
||||
dataset = options.dataset,
|
||||
settings = options.chart.settings,
|
||||
data_content = null,
|
||||
cytoscape = null,
|
||||
astar_root = "",
|
||||
astar_destination = "",
|
||||
sif_file_ext = "sif",
|
||||
highlighted_color = settings.get( 'color_picker_highlighted' );
|
||||
|
||||
Utils.get( {
|
||||
url : dataset.download_url,
|
||||
success : function( content ) {
|
||||
// Select data for the graph
|
||||
if( dataset.file_ext === sif_file_ext ) {
|
||||
data_content = SIF.SIFJS.parse( content ).content;
|
||||
}
|
||||
else {
|
||||
data_content = content.elements ? content.elements : content;
|
||||
}
|
||||
try {
|
||||
cytoscape = Cytoscape({
|
||||
container: document.getElementById( options.targets[ 0 ] ),
|
||||
layout: {
|
||||
name: settings.get( 'layout_name' ),
|
||||
idealEdgeLength: 100,
|
||||
nodeOverlap: 20
|
||||
},
|
||||
minZoom: 0.1,
|
||||
maxZoom: 20,
|
||||
style: [{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'background-color': settings.get( 'color_picker_nodes' ),
|
||||
'opacity': 1,
|
||||
'content': 'data(id)',
|
||||
'text-valign': 'center',
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'core',
|
||||
style: {
|
||||
'selection-box-color':'#AAD8FF',
|
||||
'selection-box-border-color':'#8BB0D0',
|
||||
'selection-box-opacity':'0.5'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'curve-style': settings.get( 'curve_style' ),
|
||||
'haystack-radius': 0,
|
||||
'width': 3,
|
||||
'opacity': 1,
|
||||
'line-color': settings.get( 'color_picker_edges' ),
|
||||
'target-arrow-shape': settings.get( 'directed' ),
|
||||
"overlay-padding":"3px"
|
||||
}
|
||||
},
|
||||
{
|
||||
selector:'node:selected',
|
||||
style: {
|
||||
"border-width": "6px",
|
||||
"border-color": "#AAD8FF",
|
||||
"border-opacity": "0.5",
|
||||
"background-color": "#77828C",
|
||||
"text-outline-color": "#77828C"
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: '.searchpath',
|
||||
style: {
|
||||
'background-color': highlighted_color,
|
||||
'line-color': highlighted_color,
|
||||
'target-arrow-color': highlighted_color,
|
||||
'transition-property': 'background-color, line-color, target-arrow-color',
|
||||
'transition-duration': '0.5s'
|
||||
}
|
||||
}],
|
||||
elements: data_content // Set the JSON data for viewing
|
||||
});
|
||||
|
||||
// Highlight the minimum spanning tree found using Kruskal algorithm
|
||||
if( settings.get( 'search_algorithm' ) === "kruskal" ) {
|
||||
var kruskal = cytoscape.elements().kruskal();
|
||||
kruskal.edges().addClass( 'searchpath' );
|
||||
}
|
||||
// Register tap (clicking a node) event on graph nodes
|
||||
// On tapping any node, BFS or DFS start from that node
|
||||
cytoscape.$( 'node' ).on('tap', function( e ) {
|
||||
var ele = e.cyTarget,
|
||||
search_algorithm = settings.get( 'search_algorithm' ),
|
||||
traversal_type = settings.get( 'graph_traversal' );
|
||||
// If search algorithm and traversal both are chosen,
|
||||
// search algorithm will take preference
|
||||
if( settings.get( 'search_algorithm' ) !== "" ) {
|
||||
self.run_search_algorithm( cytoscape, ele.id(), search_algorithm, self );
|
||||
}
|
||||
else if( settings.get( 'graph_traversal' ) !== "" ) {
|
||||
self.run_traversal_type( cytoscape, ele.id(), traversal_type );
|
||||
}
|
||||
});
|
||||
chart.state( 'ok', 'Chart drawn.' );
|
||||
// Re-renders the graph view when window is resized
|
||||
$( window ).resize( function() { cytoscape.layout(); } );
|
||||
} catch( err ) {
|
||||
chart.state( 'failed', err );
|
||||
}
|
||||
options.process.resolve();
|
||||
},
|
||||
error: function() {
|
||||
chart.state( 'failed', 'Failed to access dataset.' );
|
||||
options.process.resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
run_search_algorithm: function( cytoscape, root_id, type, self ) {
|
||||
var algorithm = "", i = 0;
|
||||
var selectNextElement = function() {
|
||||
if( i < algorithm.path.length ) {
|
||||
// Add css class for the selected edge(s)
|
||||
algorithm.path[i].addClass( 'searchpath' );
|
||||
i++;
|
||||
// Animate the edges and nodes coloring
|
||||
// of the path with a delay of 500ms
|
||||
setTimeout( selectNextElement, 500 );
|
||||
}
|
||||
};
|
||||
switch( type ) {
|
||||
// Breadth First Search
|
||||
case "bfs":
|
||||
algorithm = cytoscape.elements().bfs( '#' + root_id, function() { }, true );
|
||||
selectNextElement();
|
||||
break;
|
||||
// Depth First Search
|
||||
case "dfs":
|
||||
algorithm = cytoscape.elements().dfs( '#' + root_id, function() { }, true );
|
||||
selectNextElement();
|
||||
break;
|
||||
// A* search
|
||||
case "astar":
|
||||
// Choose root and destination for performing A*
|
||||
if( !self.astar_root ) {
|
||||
self.astar_root = root_id;
|
||||
}
|
||||
else {
|
||||
self.astar_destination = root_id;
|
||||
}
|
||||
if( self.astar_root && self.astar_destination ) {
|
||||
algorithm = cytoscape.elements().aStar({ root: "#" + self.astar_root, goal: "#" + self.astar_destination },function() {}, true);
|
||||
selectNextElement();
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
run_traversal_type: function( cytoscape, root_id, type ) {
|
||||
var node_collection;
|
||||
switch( type ) {
|
||||
// Recursively get edges (and their sources) coming into the nodes in a collection
|
||||
case "predecessors":
|
||||
node_collection = cytoscape.$( '#' + root_id ).predecessors();
|
||||
break;
|
||||
// Recursively get edges (and their targets) coming out of the nodes in a collection
|
||||
case "successors":
|
||||
node_collection = cytoscape.$( '#' + root_id ).successors();
|
||||
break;
|
||||
// Get edges (and their targets) coming out of the nodes in a collection.
|
||||
case "outgoers":
|
||||
node_collection = cytoscape.$( '#' + root_id ).outgoers();
|
||||
break;
|
||||
// Get edges (and their sources) coming into the nodes in a collection.
|
||||
case "incomers":
|
||||
node_collection = cytoscape.$( '#' + root_id ).incomers();
|
||||
break;
|
||||
// From the set of calling nodes, get the nodes which are roots
|
||||
case "roots":
|
||||
node_collection = cytoscape.$( '#' + root_id ).roots();
|
||||
break;
|
||||
// From the set of calling nodes, get the nodes which are leaves
|
||||
case "leaves":
|
||||
node_collection = cytoscape.$( '#' + root_id ).leaves();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// Add CSS class for selected nodes and edges
|
||||
node_collection.edges().addClass( 'searchpath' );
|
||||
node_collection.nodes().addClass( 'searchpath' );
|
||||
}
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user