Display popup showing node statistics in cytoscape.js - cytoscape.js

Started exploring cytoscape.js since last one week. Its simply amazing. I am using cytoscape.js for one of my usecases wherein i need to show a graph of flight network. Here, I would like to show a small pop-up on clicking an airport (node) that contains few statistics such as degree, pagerank etc. Any pointer will help. For your reference, here is the code I wrote. I could see the graph exactly how I wanted. But what I am missing is the piece of code that can display a pop up on clicking the nodes. Thanks in advance for your help
<script>
var cy1 = cytoscape({
container: document.getElementById('cy1'),
layout: {
name: 'concentric',
concentric: function( node ){
return node.degree();
},
levelWidth: function( nodes ){
return 15;
}
},
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 1,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
elements: {
nodes: airportsJSON,
edges: edgeJSON
}
});
var pr = cy1.elements().pageRank();
console.log("Statistics*****");
for(i = 0; i<cy1.nodes().length; i++)
{
console.log(cy1.nodes()[i].data('id') + "->"+cy1.nodes()[i].indegree(true) + " "+cy1.nodes()[i].outdegree(true) + " "+cy1.nodes()[i].totalDegree(true));
}
console.log("done");
</script>

Try the qtip extension: https://github.com/cytoscape/cytoscape.js-qtip
It lets you show tooltips/popups on elements.

Related

Get current state of graph after elements are added dynamically

I am using Cytoscape.js and the context menus extension within a React app through react-cytoscape.js. Right now, I can dynamically add nodes through an onClickFunction as a menu item. I would like to get the current state of the graph - that is, all the nodes that have been added as well as the ones it started with.
How can I do this? So far, I've tried cy.json(), but that only returns the initial state of the graph.
Below is all of my cytoscape code.
class MyApp extends React.Component {
constructor(props){
super(props);
}
render(){
const elements = [ // Nodes and edges to be added initially
{ data: { id: 'one', label: 'Node 1' }, position: { x: 250, y: 250 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 300, y: 250 } },
{ data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' } }
];
return <CytoscapeComponent
cy = {cy => { // Extensions and their options
cy.contextMenus({
menuItems: [
{ // Adds the ability to add nodes from the right-click menu
id: 'add-node',
content: 'add node',
tooltipText: 'add node',
image: {src: "./node_modules/cytoscape-context-menus/assets/add.svg", width: 12, height: 12, x: 6, y: 4},
coreAsWell: true,
// When we move adding nodes to a sidebar, probably use this as reference
onClickFunction: function (event) {
var data = {
group: 'nodes'
};
var pos = event.position || event.cyPosition;
cy.add({
data: data,
position: {
x: pos.x,
y: pos.y
}
});
}
}
]
})
}}
elements={elements}
style={ { width: '100%', height: '40vmax'} } />;
}
}

ArcGIS JS API: make circle not zoom

I have a ArcGIS JavaScript API map. On map click I want to display a circle and highlight all the points within that circle.
However, when I create the circle and zoom in the map, the circle zooms in as well. How can I get the circle to stay the same pixel radius no matter what the zoom level of the map?
<script>
require([
"esri/Map",
"esri/layers/GeoJSONLayer",
"esri/views/MapView",
"esri/widgets/Zoom",
"esri/core/watchUtils",
"esri/geometry/geometryEngine"
], function (Map, GeoJSONLayer, MapView, Zoom, watchUtils, {buffer}) {
const url =
"https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson";
const template = {
title: "Earthquake Info",
content: "Magnitude {mag} {type} hit {place} on {time}",
fieldInfos: [
{
fieldName: "time",
format: {
dateFormat: "short-date-short-time"
}
}
]
};
const renderer = {
type: "simple",
field: "mag",
symbol: {
type: "simple-marker",
color: "orange",
outline: {
color: "white"
}
},
visualVariables: [
{
type: "size",
field: "mag",
stops: [
{
value: 2.5,
size: "4px"
},
{
value: 8,
size: "40px"
}
]
}
]
};
const geojsonLayer = new GeoJSONLayer({
url: url,
copyright: "USGS Earthquakes",
popupTemplate: template,
renderer: renderer //optional
});
const map = new Map({
basemap: "gray",
layers: [geojsonLayer]
});
const view = new MapView({
container: "viewDiv",
center: [-168, 46],
zoom: 3,
map: map
});
let csvLayerView;
let highlight;
let resultFeatures;
view.whenLayerView(geojsonLayer).then(layerView => {
csvLayerView = layerView;
});
view.on("click", ({mapPoint}) => {
const geom = buffer(mapPoint, 500, "miles");
view.graphics.removeAll();
view.graphics.add({
geometry: geom,
symbol: {
type: "simple-fill",
color: [51, 51, 204, 0.2],
style: "solid",
outline: {
color: "white",
width: 1
}
}
});
const query = geojsonLayer.createQuery();
query.geometry = geom;
csvLayerView.queryObjectIds(query).then(oids => {
if (highlight) {
highlight.remove();
}
highlight = csvLayerView.highlight(oids);
return csvLayerView.queryFeatures(query);
}).then(({features}) => {
resultFeatures = features;
});
});
});
</script>
The circle that you create, using a 500 miles buffer of the click point, is a polygon. So naturally will be the same one no matter what zoom level you have. In other words, represent a geographic area so it can not change based on the zoom level.
On contrary, in the same example you have the points that represent the earthquakes, that are represented with a orange circle. Because it is a symbol that represents a geographic place, it keeps the size in pixels for all zoom levels.
Resuming, you can not make the 500 miles buffer area, represented by the polygon, to change with the zoom levels, because it is an area.

how to automove any node in cytoscape js

I use cytoscape js automove extention, I can move two node together:
cy.automove({
nodesMatching: cy.$('#a'),
reposition: 'drag',
dragWith: cy.$('#b')
});
But I must specify the node #a and #b, now I need whenever I move any node, its neighborhood move together, not a special node.
So I try this:
<style>
#cy{
width:600px;
height:800px
}
</style>
<script src="cytoscape.js"></script>
<script src="cytoscape-automove.js"></script>
<div id="cy"></div>
<script>
var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
elements: [ // list of graph elements to start with
{ // node a
data: { id: 'a' }
},
{ // node b
data: { id: 'b' }
},
{ // node c
data: { id: 'c' }
},
{ // node d
data: { id: 'd' }
},
{ // edge ab
data: { id: 'ab', source: 'a', target: 'b' }
},
{ // edge cd
data: { id: 'cd', source: 'c', target: 'd' }
}
],
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#566',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#bcc',
'target-arrow-shape': 'triangle'
}
}
],
layout: {
name: 'grid',
rows: 1
},
boxSelectionEnabled:true,
panningEnabled: true,
selectionType:"additive"
});
cy.on("drag",function(evt){
var node=evt.target;
cy.automove({
nodesMatching: node.neighbourhood().nodes(),
reposition: 'drag',
dragWith: node
});
});
</script>
The result is once I move node #a, node #b will not move together.But I move the node #a again, #b is moved but the position is wrong, it is too far.
So does the dragWith must be a special one? What if I want to move any one?
How about this:
var i = 0;
cy.unbind("tapdrag");
cy.unbind("tapend");
cy.bind("tapdrag", "node", function(evt){
if (i = 0) {
i = 1;
var node = evt.target;
var target = cy.edges("[source = '" + node.id() + "']").target();
cy.automove({
nodesMatching: target,
reposition: 'drag',
dragWith: node
});
}
});
cy.bind("tapend", "node", function(evt) {
i = 0;
});

How to set edges using JSON in cytoscape.js

I am using cytoscape.js for one of my usecases. Here, I prefer to display the graph with only nodes in the beginning as shown below.
var cy1 = cytoscape({
container: document.getElementById('cy'),
elements: {
nodes: countriesJSON
},
style: [
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 1,
'line-color': '#aaa',
}
}
]
});
cy1.layout({
name: 'circle'
});
Now, inside a function, I will form the edges as like this
function myfunc(){
...
...
allRoutes.map(function(d){
var inner={};
var obj = {}
inner["id"]="edge"+index
inner["source"]=data[0]
inner["target"]=data[1]
obj["data"]=inner
allLegsJSON[i]=obj;
i++;
})
...
...
cy1.elements({ edges: allLegsJSON });
}
Once edges are created, I want to assign edges to the graph as above. However, this code is not working. Can someone tell me how to do this? I wish to assign the edges and then update the cy1 graph.
This did this
cy1.add({ edges: allLegsJSON });

Cytoscape.js setting node colour and other details

I am using Cytoscape.js to create a basic diagram and am curious as to how to set the colour and particular shape of each node. Here is my existing code:
layout: {
name: 'preset'
},
ready: function(){
window.cy = this;
cy.add([
{ group: "nodes", data: { id: "n0" }, position: { x: 100, y: 100 } },
{ group: "nodes", data: { id: "n1", shape: 'rectangle' }, position: { x: 200, y: 200 } },
{ group: "edges", data: { id: "e0", source: "n0", target: "n1" } }
]);
}
});
});
I was also curious as to how to animate the diagram and if there were any similar examples out there on that.
cheers,
...
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'shape': 'rectangle',
}
},
],
...
background-color is probably the attribute you are looking for, this is set in the style option. More info can be in http://js.cytoscape.org/#getting-started/specifying-basic-options.
As for the shape, cytoscape has a few predefined shapes such as rectangle, circle, etc. You can even make your own, or have an svg as the node's image. Options for nodes are listed in http://js.cytoscape.org/#style/node-body.
For animation I would look at some of the demos uploaded and go from there. This one is particularly interesting js.cytoscape.org/demos/aedff159b0df05ccfaa5