vis.js large nodes overlapping in hierarchical mode - javascript

Is it possible to have nodes in vis.js in hierarchical layout without overlaping on X axis?
I tried to add options:
var options = {
layout: {
hierarchical: {
direction: "UD",
sortMethod: "directed"
},
physics: {
solver: "barnesHut"
,barnesHut: {
avoidOverlap: 1
}
}
}
};

You can increase layout.hierarchical.nodeSpacing for the initial layout.
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'Node 1 with lage text'},
{id: 2, label: 'Node 2 with lage text'},
{id: 3, label: 'Node 3 with lage text'},
{id: 4, label: 'Node 4 with lage text'},
{id: 5, label: 'Node 5 with lage text'}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
layout: {
hierarchical: {
direction: "UD",
sortMethod: "directed",
nodeSpacing: 200 // <-- !!!!!!!
}
},
physics: false
};
var network = new vis.Network(container, data, options);
#mynetwork {
width: 100%;
height: 100%;
border: 1px solid lightgray;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.17.0/vis.min.js" type="text/javascript"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.17.0/vis.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="mynetwork"></div>
</body>
</html>

Another solution might be
myVisOptions = {
layout: {
hierarchical: {
direction: "UD",
sortMethod: "directed",
shakeTowards: 'roots'
}
},
physics: {
// https://visjs.github.io/vis-network/docs/network/physics.html
enabled: true,
hierarchicalRepulsion: {
avoidOverlap: 0.99
},
stabilization: {
iterations: 2000,
updateInterval: 100
},
solver: 'hierarchicalRepulsion'
},
nodes: {
margin: 8,
shape: 'box',
font: {size: 20},
color: {
background: 'white',
border: '#ffc468',
hover: '#ffc468',
highlight: '#ffc468'//, '#bd6500'
},
}
};

Related

Disable the overlap of nodes

I have created visualisation using vis-network, the problem is that the nodes are overlapping one on top of the other.
I have tried changing different values of nodeSpacing till 1000, that didn't help
Also i have tried setting barnesHut.avoidOverlap to 1 and that didn't help either,
any suggestions to fix this will be highly appreciated.
Updated options object
var options = {
interaction: {
hover: true
},
manipulation: {
enabled: true,
},
layout: {
hierarchical: {
direction: "LR",
shakeTowards: "leaves",
nodeSpacing: 1000,
treeSpacing: 300,
},
},
physics: {
stabilization: {
enabled: true, // <------ Disables animation.
iterations: 4000
},
barnesHut: {
avoidOverlap: 1
}
}
};
As mentioned in the documentation here for the layout.hierarchical.nodeSpacing option:
This is only for the initial layout. If you enable physics, the node distance there will be the effective node distance.
Therefore nodeSpacing isn't the final distance when physics is enabled. Two possible options are described below with examples.
nodeSpacing with Physics Disabled
The nodeSpacing option could be used with physics disabled. With physics disabled edges will likely overlap nodes when there are a large number of nodes. An example of this can be seen at https://jsfiddle.net/pbx8m9sk/.
nodeDistance with Hierarchical Repulsion Solver
As mentioned in the solver section of the documentation here the hierarchical repulsion solver is used by default when using a hierarchical layout. When the hierarchicalRepulsion solver is used the nodeDistance option can be used to control the distance between the nodes.
For example:
var options = {
physics: {
hierarchicalRepulsion: {
nodeDistance: 300,
}
}
};
Snippet using hierarchical repulsion solver with node distance set is below. You would likely want to adjust the nodeDistance and could adjust other hierarchicalRepulsion options, depending on the data.
// Setup initial nodes / edges
const nodes = [
{id: 1, label: '1'},
{id: 2, label: '2'},
{id: 3, label: '3'},
{id: 4, label: '4'},
{id: 5, label: '5'},
{id: 6, label: '6'},
{id: 7, label: '7'},
{id: 8, label: '8'},
{id: 9, label: '9'},
{id: 10, label: '10'},
{id: 11, label: '11'},
];
const edges = [
{from: 1, to: 2},
{from: 2, to: 3},
{from: 2, to: 4},
{from: 2, to: 5},
{from: 2, to: 6},
{from: 2, to: 7},
{from: 2, to: 8},
{from: 2, to: 9},
{from: 2, to: 10},
{from: 2, to: 11},
];
// Add a number nodes / edges
const nodesToAdd = 200;
for (let i = 12; i < 12 + nodesToAdd; i++){
nodes.push({id: i, label: `${i}`});
edges.push({from: 11, to: i});
}
var data = {
nodes: nodes,
edges: edges,
};
// create a network
var container = document.getElementById("mynetwork");
var options = {
interaction: {
hover: true
},
manipulation: {
enabled: true
},
nodes: {
shape: 'dot',
},
layout: {
hierarchical: { },
},
physics: {
hierarchicalRepulsion: {
nodeDistance: 300,
}
},
};
var network = new vis.Network(container, data, options);
#mynetwork {
width: 100%;
height: 170px;
border: 1px solid lightgray;
}
<script src="https://visjs.github.io/vis-network/standalone/umd/vis-network.min.js"></script>
<div id="mynetwork"></div>

Hide edge labels in vis.js-network

I would like to simply show/hide the labels of the edges of my vis.js-network - is this possible?
I have tried to update the edges in the vis.js-data structure:
Delete the label property - doesn't work
Set the label to undefined - doesn't work
Set the label to '' - doesn't work
Set the label to ' ' - works
I would prefer a network-wise toggle of some kind, but I haven't found one.
Is there a better way of doing this?
An alternative to updating the label property on each edge is to change the font color to be transparent for all edges. The setOptions() method can be used to update the options and will apply all edges in the network. The options edges.font.color and edges.font.strokeColor should both be updated, then returned to their original values to display the edges.
Example below and also at https://jsfiddle.net/rk9s87ud/.
var nodes = new vis.DataSet([
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" },
]);
var edges = new vis.DataSet([
{ from: 1, to: 2, label: 'Edge 1' },
{ from: 2, to: 3, label: 'Edge 2' },
{ from: 3, to: 4, label: 'Edge 3' },
{ from: 4, to: 5, label: 'Edge 4' },
]);
var container = document.getElementById("mynetwork");
var data = {
nodes: nodes,
edges: edges,
};
var options = {
nodes: {
// Set any other options, for example node color to gold
color: 'gold'
},
edges: {
font: {
// Set to the default colors as per the documentation
color: '#343434',
strokeColor: '#ffffff'
}
}
}
var hiddenEdgeTextOptions = {
edges: {
font: {
// Set the colors to transparent
color: 'transparent',
strokeColor: 'transparent'
}
}
};
var network = new vis.Network(container, data, options);
var displayLabels = true;
document.getElementById('toggleLabels').onclick = function() {
if(displayLabels){
// Apply options for hidden edge text
// This will override the existing options for text color
// This does not clear other options (e.g. node.color)
network.setOptions(hiddenEdgeTextOptions);
displayLabels = false;
} else {
// Apply standard options
network.setOptions(options);
displayLabels = true;
}
}
#mynetwork {
width: 600px;
height: 160px;
border: 1px solid lightgray;
}
<script src="https://visjs.github.io/vis-network/standalone/umd/vis-network.min.js"></script>
<button id="toggleLabels">Toggle labels</button>
<div id="mynetwork"></div>

Add button beneath legend (or key) in Plotly.js chart

I have a Plotly.js chart. How can I position a button beneath the legend (or key)?
Here is an example of what I’m working with: https://codepen.io/synaptik/pen/YLmRMM
<div id="myDiv"></div>
<div id="inset" style="margin-left:80px;">
<button style="background:grey;">Target<br/>Info</button
</div>
A complicated but robust solution is, use javascript and get the position of the legend in the plot ( using the function getBoundingClientRect) and then set that positioning to the button, JQuery library is being used by me to do this, but can be also possible through javascript with some adjustments.
Run function after plot render:
The javascript funtion that positions the button should run only after the plotly graph has completed rendering, so based on this SO Answer, I call the function that positions the buttons after the plot has completely rendered.
References:
getBoundingClientRect
var data_x = ['2018-05-01', '2018-05-02', '2018-05-03', '2018-05-04', '2018-05-05', '2018-05-06', '2018-05-07', '2018-05-08', '2018-05-09'];
// data
var Data = {
type: 'scatter',
x: data_x,
y: [4, 2, -1, 4, -5, -7, 0, 3, 8],
mode: 'lines+markers',
name: 'Data',
showlegend: true,
hoverinfo: 'all',
line: {
color: 'blue',
width: 2
},
marker: {
color: 'blue',
size: 8,
symbol: 'circle'
}
}
// violations
var Viol = {
type: 'scatter',
x: ['2018-05-06', '2018-05-09'],
y: [-7, 8],
mode: 'markers',
name: 'Violation',
showlegend: true,
marker: {
color: 'rgb(255,65,54)',
line: {
width: 6
},
opacity: 0.5,
size: 14,
symbol: 'circle-open'
}
}
// control limits
var CL = {
type: 'scatter',
x: data_x.concat([null]).concat(data_x),
y: [5, 5, 5, 5, 6, 6, 7, 7, 7, null, -5, -5, -5, -5, -6, -6, -7, -7, -7],
mode: 'lines',
name: 'LCL/UCL',
showlegend: true,
line: {
color: 'green',
width: 2,
dash: 'dash'
}
}
// centre
var Centre = {
type: 'scatter',
x: data_x,
y: [3, 1, 2, 2, 2, -2, 2, 2, 2],
mode: 'lines',
name: 'EWMA',
showlegend: true,
line: {
color: 'grey',
width: 2
}
}
// all traces
var data = [Data, Viol, CL, Centre]
// layout
var layout = {
title: 'Basic SPC Chart',
xaxis: {
zeroline: false
},
yaxis: {
range: [data_x[0], data_x[data_x.length - 1]],
zeroline: false
}
}
function positionButton() {
var offsets = $("g.legend")[0].getBoundingClientRect();
$("div#inset").css("left", offsets.left);
var offsetTop = offsets.height + offsets.top;
$("div#inset").css("top", offsetTop);
}
Plotly.plot('myDiv', data, layout).then(function() {
window.requestAnimationFrame(function() {
window.requestAnimationFrame(function() {
positionButton();
});
});
});
$(window).on("resize", function() {
positionButton();
});
.position-button {
position: absolute;
}
.style-this {
background: grey;
}
.wrapper {
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<head>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<!-- Plotly chart will be drawn inside this DIV -->
<div class="wrapper">
<div id="myDiv"></div>
<div id="inset" class="position-button">
<button class="style-this">Target<br/>Info</button
</div>
</div>
<script>
/* JAVASCRIPT CODE GOES HERE */
</script>
</body>
</html>
A simple way to do it will be, wrap the plot in a div set to relative positioning and make the div wrapping the button absolute positioned and align it to the legend. Please refer the below sample.
var data_x = ['2018-05-01' , '2018-05-02' , '2018-05-03' , '2018-05-04' , '2018-05-05' , '2018-05-06' , '2018-05-07' , '2018-05-08' , '2018-05-09' ];
// data
var Data = {
type: 'scatter',
x: data_x,
y: [4,2,-1,4,-5,-7,0,3,8],
mode: 'lines+markers',
name: 'Data',
showlegend: true,
hoverinfo: 'all',
line: {
color: 'blue',
width: 2
},
marker: {
color: 'blue',
size: 8,
symbol: 'circle'
}
}
// violations
var Viol = {
type: 'scatter',
x: ['2018-05-06', '2018-05-09'],
y: [-7,8],
mode: 'markers',
name: 'Violation',
showlegend: true,
marker: {
color: 'rgb(255,65,54)',
line: {width: 6},
opacity: 0.5,
size: 14,
symbol: 'circle-open'
}
}
// control limits
var CL = {
type: 'scatter',
x: data_x.concat([null]).concat(data_x),
y: [5,5,5,5,6,6,7,7,7, null, -5,-5,-5,-5,-6,-6,-7,-7,-7],
mode: 'lines',
name: 'LCL/UCL',
showlegend: true,
line: {
color: 'green',
width: 2,
dash: 'dash'
}
}
// centre
var Centre = {
type: 'scatter',
x: data_x,
y: [3,1,2,2,2,-2,2,2,2],
mode: 'lines',
name: 'EWMA',
showlegend: true,
line: {
color: 'grey',
width: 2
}
}
// all traces
var data = [Data,Viol,CL,Centre]
// layout
var layout = {
title: 'Basic SPC Chart',
xaxis: {
zeroline: false
},
yaxis: {
range: [data_x[0], data_x[data_x.length-1]],
zeroline: false
}
}
Plotly.plot('myDiv', data,layout);
.position-button {
position: absolute;
top: 190px;
left: 89%;
}
.style-this{
background:grey;
}
.wrapper{
position:relative;
}
<head>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<!-- Plotly chart will be drawn inside this DIV -->
<div class="wrapper">
<div id="myDiv"></div>
<div id="inset" class="position-button">
<button class="style-this">Target<br/>Info</button
</div>
</div>
<script>
/* JAVASCRIPT CODE GOES HERE */
</script>
</body>
</html>

Visjs network place node fixed at bottom right

I would like to create a node in the network plugin of the visjs framework, so that it stays fixed at the bottom.
Getting it staying fixed is no problem, however the rest of the nodes seem to have somekind of algorithm for the x and y coordinates.
Doing a { x: 0, y: 0 } will not work it seems because the x and y are coordinates in relation to other nodes.
I think (if I got you right), you should:
set physics to false.
give fixed position to true for all nodes,
set the node positions as you wish.
after drawing the network (in the next tick) remove the fixed position from all other nodes.
See the snippet below. (the fixed position stays on node no 5).
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'Node 1', fixed: true, group: 1, x:0, y:0},
{id: 2, label: 'Node 2', fixed: true, group: 2, x:10, y:110},
{id: 3, label: 'Node 3', fixed: true, group: 1, x:120, y:20},
{id: 4, label: 'Node 4', fixed: true, group: 2, x:-110, y:-110},
{id: 5, label: 'Node 5', fixed: true, group: 2, x:220, y:220}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 3},
{from: 2, to: 4},
{from: 3, to: 4},
{from: 2, to: 5}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics: false
};
var network = new vis.Network(container, data, options);
setTimeout(function(){
nodes.forEach(node => {
if(node.id !== 5){
nodes.update({id: node.id, fixed: false});
}
})
})
#mynetwork {
width: 600px;
height: 400px;
border: 1px solid lightgray;
}
p {
max-width: 600px;
}
<!doctype html>
<html>
<head>
<title>Network | Basic usage</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.0/vis.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.0/vis.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="mynetwork"></div>
</body>
</html>

Modify the style for a specific node selected in the network for vis.js

Is there a way to change the node size for the selected node without changing the size for all nodes in the options ?
These are my node options:
nodes: {
borderWidth: 1,
borderWidthSelected: 2,
physics: true,
color: {
border: '#000000',
background: '#ffffff',
highlight: {
border: '#000000',
background: '#B9B9BF'
}
},
shadow: {
enabled: false,
color: '#C11818',
size: 10,
x: 5,
y: 5
},
shape: 'circularImage',
mass: 2,
size: 25
}
I want to enlarge the selected node so it is more visible than the others.
network.on("selectNode", function (params) {
var nodeId = params.nodes[0];
var node = nodes.get(nodeId);
nodeClick(nodeId, nodes, edges, network);
// var options= {
// nodes: {
// size: 40
// }
// };
// network.setOptions(options);
});
The commented part sets the size for all nodes rather than the one selected and the node object doesn't have any handle on the options either.
You can change the font-size of the selected node to increase its size:
var nodes = new vis.DataSet([
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'}
]);
var edges = new vis.DataSet([
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
interaction: { hover:true },
nodes: { font: { size: 14 }}
};
var network = new vis.Network(container, data, options);
network.on("selectNode", function (params) {
var selectedNodeId = params.nodes[0];
var node = network.body.nodes[selectedNodeId];
node.setOptions({
font: {
size: 20
}
});
});
network.on("deselectNode", function (params) {
var deselectedNodeId = params.previousSelection.nodes[0];
var node = network.body.nodes[deselectedNodeId];
node.setOptions({
font: {
size: options.nodes.font.size
}
});
});
#mynetwork {
width: 100%;
height: 100%;
border: 1px solid lightgray;
}
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="mynetwork"></div>
</body>
</html>
if you have multiselect enabled, you can loop over params.nodes
for (id in params.nodes){
var node = network.body.nodes[params.nodes[id]];
...
}
(deselect respectivly)

Categories