vis.js - How to resize nodes at runtime - javascript

I would like to resize nodes of a vis.js network at runtime. My goal is to create a slidercontrol to expand all nodes (an labels) or collapse them.
First, I've tried to manipulate the Scaling values.
var options = {nodes: {scaling: {label: {max: 180 , maxVisible: 180}}}};
network.setOptions(options);
But there are no results.
My second idea was to manipulate the value of every single node.
function IncNodeSize(Increment) {
var CurrentNodes = nodesDS.get();
for (var i = 0; i < CurrentNodes.length; i++) {
CurrentNodes[i].value = CurrentNodes[i].value + 100;
}
}
nodesDS.update(CurrentNodes);
}
But this didn't work either.
Has anyone an idea how to resize nodes at runtime?

With the help of the Developer Community I have solved the issue.
At first I had to change the options and than I had to update the nodes (with no datachange)
This two things together changed the sizes.

In my case I simply wanted to resize a single node. I too tried the DataSet.update() method, but to no avail. What did work, however, was to simply set the new property directly on the node object and call Network.setData(). Here's an example:
const nodes = new vis.DataSet([
{ id: 1, label: 'Node 1', shape: 'circle', margin: 20 },
{ id: 2, label: 'Node 2', shape: 'circle', margin: 20 },
]);
const edges = new vis.DataSet([
{ from: 1, to: 2 }
]);
const dta = { nodes: nodes, edges: edges };
const network = new vis.Network(
document.getElementById('graph'),
dta,
{ interaction: { zoomSpeed: 0.2 } }
);
setTimeout(function() {
const node1 = nodes.get(1);
node1.margin = 50;
network.setData(dta);
}, 2000);
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
<div id="graph" style="width: 100%; height: 100%"></div>
Notice that I make sure to have a reference to the object (the dta constant) that includes both the nodes DataSet and the edges DataSet, before I start manipulating the node. That way I can call Network.setData() using that dta object.

Related

How to change color text in graph when use chart js [duplicate]

Is there a way to set a different color to a datapoint in a Line Chart if its above a certain value?
I found this example for dxChart - https://stackoverflow.com/a/24928967/949195 - and now looking for something similar for ChartJS
In updating to version 2.2.2 of ChartJS, I found that the accepted answer no longer works. The datasets will take an array holding styling information for the properties.
In this case:
var pointBackgroundColors = [];
var myChart = new Chart($('#myChart').get(0).getContext('2d'), {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: pointBackgroundColors
}
]
}
});
for (i = 0; i < myChart.data.datasets[0].data.length; i++) {
if (myChart.data.datasets[0].data[i] > 100) {
pointBackgroundColors.push("#90cd8a");
} else {
pointBackgroundColors.push("#f58368");
}
}
myChart.update();
I found this looking through the samples for ChartJS, specifically this one: "Different Point Sizes Example"
With recent versions of chart.js I would recommend doing this with scriptable options.
Scriptable options give you an easy way to vary the style of a dataset property (e.g. line point colour) dynamically according to some function you provide. Your function is passed a 'context' object that tells it the index and value of the point etc. (see below).
Most chart properties can be scripted; the dataset properties for each chart type tell you the exact list (e.g. see here for line chart).
Here is how you might use scriptable options on a line chart (based on the example in the docs). On this chart negative data points are shown in red, and positive ones in alternating blue/green:
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data,
label: "Test Data",
borderColor: "#3e95cd",
fill: false,
pointBackgroundColor: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value < 0 ? 'red' : // draw negative values in red
index % 2 ? 'blue' : // else, alternate values in blue and green
'green';
}
}
],
}
});
The context object passed to your function can have the following properties. Some of these won't be present for certain types of entity, so test before use.
chart: the associated chart
dataIndex: index of the current data
dataset: dataset at index datasetIndex
datasetIndex: index of the
current dataset
hover: true if hovered
Here's what worked for me (v 2.7.0), first I had to set pointBackgroundColor and pointBorderColor in the dataset to an array (you can fill this array with colours in the first place if you want):
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: [],
pointBorderColor: [],
}
]
}
});
Then you can monkey with the colours of the points directly:
myChart.data.datasets[0].pointBackgroundColor[4] = "#cc00cc";
myChart.data.datasets[0].pointBorderColor[4] = "#cc0000";
myChart.update();
Some other properties to play with to distinguish a point: pointStrokeColor (it apparently exists but I can't seem to get it to work), pointRadius & pointHoverRadius (integers), pointStyle ('triangle', 'rect', 'rectRot', 'cross', 'crossRot', 'star', 'line', and 'dash'), though I can't seem to figure out the defaults for pointRadius and pointStyle.
For chartjs 2.0 see this following answer.
Original answer below.
Good question regarding ChartJS. I've been wanting to do a similar thing. i.e dynamically change the point colour to a different colour. Have you tried this below. I just tried it and it worked for me.
Try this:
myLineChart.datasets[0].points[4].fillColor = "rgba(000,111,111,55)" ;
Or Try this:
myLineChart.datasets[0].points[4].fillColor = "#FF0000";
Or even this:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
Then do this:
myLineChart.update();
I guess you could have something like;
if (myLineChart.datasets[0].points[4].value > 100) {
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
myLineChart.update();
}
Give it a try anyway.
Just adding what worked for me in the new 2.0 version.
Instead of:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
I had to use:
myChart.config.data.datasets[0].backgroundColor[4] = "lightgreen";
Not sure if that's because of a change in 2.0 or because I'm using a bar chart and not a line chart.
If you initialize the myChart in this manner,
var myChart = new Chart(ctx, {
type: 'line',
data: {
you have to change line color by this code
myChart.data.datasets[0].backgroundColor[0] ="#87CEFA";
If you initialize the myChart in this manner,
myBar = new Chart(ctx).Line(barChartData, {
you have to change line color by this code
myLineChart.datasets[0].points[4].fillColor = "#FF0000";

Updating Bokeh Plot in JS

I am developing in Javascript to render a table using a DataTables (Table plug-in for jQuery). I am also using Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect) to filter the table. I would like to re-render my BokehJS plot everytime the table is re-drawn. I have hooked up the correct calls but I am also calling Bokeh.Plotting.show to re-render the graph. This forces me to remove the last created div to avoid multiple graphs plotted. I am new to the JS side of Bokeh and wanted to understand if there is a cleaner method to update the plot in JS?
var eventFired = function ( type ) {
//var n = $('#demo_info')[0];
//n.innerHTML += '<div>'+type+' event - '+new Date().getTime()+'</div>';
//n.scrollTop = n.scrollHeight;
var plt = Bokeh.Plotting;
// set up some data
var M = 25;
var xx = [];
var yy = [];
var colors = [];
var radii = [];
for (var y = 0; y <= M; y += 4) {
for (var x = 0; x <= M; x += 4) {
xx.push(x * (Math.random() +0.5));
yy.push(y * (Math.random() +0.5));
colors.push(plt.color(50+8*x, 30+8*y, 150));
radii.push(Math.random() * 0.4 + 1.7)
}
}
// create a data source
var source = new Bokeh.ColumnDataSource({
data: { x: xx, y: yy, radius: radii, colors: colors }
});
// make the plot and add some tools
var tools = "pan,crosshair,wheel_zoom,box_zoom,reset,save";
var p = plt.figure({ title: type+' event - '+new Date().getTime(),
height: 300,
width: 300,
tools: tools });
// call the circle glyph method to add some circle glyphs
var circles = p.circle({ field: "x" }, { field: "y" }, {
source: source,
radius: radii,
fill_color: colors,
fill_alpha: 0.6,
line_color: null
});
//remove old plot on update conditions
$('.bk-root').remove();
// Show the plot, appending it to the end of the current
// section of the document we are in.
Bokeh.Plotting.show(p, document.getElementById('myplot'));
//To Do: add in transition to improve the UI appearance
}
And here is the the datatable setting up the call back to the BokehJS script.
</script>
$('#table').DataTable({
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal( {
header: function ( row ) {
var data = row.data();
return 'Details for '+data[0]+' '+data[1];
}
} ),
renderer: $.fn.dataTable.Responsive.renderer.tableAll( {
tableClass: 'table'
} )
}
},
paginate: false,
info: false,
paging: true,
autoWidth: true,
dom: '<"dt-buttons">Bf<"clear">lirtp',
"buttons": [ 'copy', 'csv', 'excel' ],
}).on( 'draw.dt', function () { eventFired( 'Draw' ); } );
Lastly, is there a good method to update the plot via a transition effect to improve the appearance of the re-plotting?
I eventually figured out all of the components in the process in a modified use case compared to the one shown above. In my present approach I needed to utilize my source data and emit a 'change' trigger.
source.data.factors = [my new factor list]
source.trigger('change');
The new factor list based on the jQuery datatable can be obtained as followed.
$('mytable').DataTable().columns(0, {search: 'applied'}).data()[0]
Also in my use case I am working with a categorical axis. In my specific use case, my factors will dynamically change upon redraw of the graph. This can be achieved using the plot's attributes.
p.attributes.x_range.factors = [ my new factor list from my updated source data]
There is also no need to remove the old plot, and the added plus is the redraw is very fast for my simple plots.

Chart.js: stack the bars to the left

I need to stack the bars in the bar chart to the left as per the image attached
is there a way to do that in chart.js?
EDIT:
Just to clarify what I am looking for.
The number of the bars in my chart is dynamic, if there are 10 of them then chart looks fine but if there are only 2 they each take 50% of the width of the chart (see picture #2)
I want both of those bars to be exactly the same width as if there were 10 of them and be stacked to the left.
One option that I'm currently considering is just to add (10 - no of bars) bars with 0 value so that they won't be visible. But I'm hoping that there is a better solution.
Thanks.
Instead of creating a graph with 10 empty bar charts, then populate it with your values, I think it would be better to add empty values to reach the number of 10 (same idea though).
If you take a look in the Chart.js documentation, you can see that you can create plugins for your charts and graphs. Plugins are extremely useful when editing your chart (instead of just hardcoding what you want) since they allow you to handle what is happening while creating your charts.
For instance : beforeUpdate, afterDraw are some of the events you can handle with plugins.
Now, you must know that the chart object contains a lot of information :
If you want to edit a global option, you'd check chart.config.options
If you want to edit a specific chart, you'd check chart.config.data
In our case, we'd need the data attribute.
If you take a deep look in it, you'd see that the number of values come from the lengh of both data.labels and data.datasets[n].data (n being the nth dataset).
Now that you know what to do, and how to do it, you can do it.
I still made a quick example of what you are looking for :
var ctx = document.getElementById("myChart").getContext("2d");
// stores the number of bars you have at the beginning.
var length = -1;
// creates a new plugin
Chart.pluginService.register({
// before the update ..
beforeUpdate: function(chart) {
var data = chart.config.data;
for (var i = data.labels.length; i < data.maxBarNumber; i++) {
length = (length == -1) ? i : length;
// populates both arrays with default values, you can put anything though
data.labels[i] = i;
data.datasets[0].data[i] = 0;
}
},
// after the update ..
afterUpdate: function(chart) {
console.log(chart);
var data = chart.config.data;
if (length == -1) return;
// prevents new charts to be drawn
for (var i = length; i < data.maxBarNumber; i++) {
data.datasets[0]._meta[0].data[i].draw = function() {
return
};
}
}
});
var data = {
// change here depending on how many bar charts you can have
maxBarNumber: 10,
labels: [1, 2, 3, 4, 5, 6, 7],
datasets: [{
label: "dataset",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
data: [65, 59, 80, 81, 56, 55, 40],
}]
};
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [{
display: false
}],
yAxes: [{
stacked: true
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.1/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
You can use Array.prototype.reverse() to reverse the data if it is currently stacked to the right. Otherwise you will need to use some type of sorting to go from largest data to smallest data.

[possible bug]Values in array as attribute of object set to undefined

I have encountered one of the most bizzare and frustrating behaviours yet. I have sample data:
var nodes = [ //Sample data
{
ID: 1,
Chart: 1,
x: 50,
y: 50,
width: 100,
height: 80,
color: "#167ee5",
text: "Start",
label: "Start",
targets: [2]
},
{
ID: 2,
Chart: 1,
x: 500,
y: 170,
width: 100,
height: 80,
color: "#167ee5",
text: "End",
label: "End",
targets: [3]
},
{
ID: 3,
Chart: 1,
x: 270,
y: 350,
width: 100,
height: 80,
color: "#167ee5",
text: "Mid",
label: "Mid",
targets: []
}
];
for my web application. The issue is with the targets attribute. As you can see it is array. However when I do
console.log(nodes[0]);
and inspect the result in the browser it shows that the value for targets at index 0 is undefined. Same for every other targets that has some values in them (whether 1 or more).
However if I do
console.log(nodes.[0].targets);
it prints out [2]. If I do Array.isArray(nodes[0].targets) it returns false, yet if I do console.log(nodes[0]) and inspect the result in the browser console, it shows that the object prototype is in fact Array and simply the value at index 0 is undefined.
It worked the day before and now it doesn't. The only thing I did was I restructured the object that uses this variable later. But the console log is being called before the object is even instantiated for the first time (and it doesn't change the nodes var anyway, only reads it).
Does anyone have any clues as to what might be causing this behaviour. If it helps I am using Paperscript and this code runs in the paperscript scope (as it did before when everything worked fine).
UPDATE
Ok after more blind debugging I have determined the block of code that causes the issue, how or why is completely beyond me.
Basically I define an object constructor beflow. The constructor loops through the nodes, makes Paperscript shapes and adds the targets to the arbitrary data attribute of the paperJS path object:
function Flowchart(nodes, chartdata) {
//Member vars. They are only used internally
var connections = [];
var shapes = [];
var connectors = [];
//Constructor operations
createShapes(nodes); //If I uncomment this, the problem goes away
//...
function createShapes(nodes) {
nodes.forEach(function (node) { //for each node data entry
console.log(node); //At this point, the targets are screwed up already
var point = new Point(node.x, node.y); //make a PaperJS point for placement
var size = new Size(node.width, node.height); //make a PaperJS size object
var shape = makeRectangle(point, size, 8, node.color); //Pass to the object instantiating function
shape.data = { //Store arbitrary data for programming reference.
ID: node.ID,
label: node.label,
text: node.text,
'connectors': {
to: [],
from: []
},
targets: node.targets //this is undefined
};
console.log(node.targets) //this logs [2] or [3] but not Array[1]...
shapes.push(shape); //Store reference for later
});
shapes.forEach(function (shape) { //loop though all drawn objects
if (shape.data.targets.length > 0) { //if shape has targets
var targets = _.filter(this.shapes, function (target) {
return _.contains(shape.data.targets, target.data.ID);
});
for (var i = 0; i < shape.data.targets.length; i++) {
shape.data.targets[i] = targets[i]; //Replace the ID-type reference with drawn object reference
}
}
});
}
//... The rest of the object
}
console.log(nodes);
//It doesnt seem to matter whether i put this before or after instantiating.
//It doesnt even matter IF I instantiate in the first place.
var chart = new Flowchart(nodes, chartdata);
This behaviour has been caused by the changes to how Chrome treats enumerable properties of objects. Because Chrome updates silently, it's impossible to notice.
It must have been causing me a lot of headache if I remembered the cause after all this time... (Also it's embarrassing how bad I was at writing questions, but I guess that I realise it means I have progressed since then somewhat).

Dygraphs - Highlight two series at the same time

I know the question was already asked before but I am very new to Dygraphs and struggling to find the answer.
I have the following datastructure in javascript:
x , Label1, Label2, label3.... label1_2, label1_3, etc...
new Date(...), 1.23,1.45,.... , .... , ....,
new Date(...), null, null, ......., 1.23,1.434
new Date(....), 1.4656, 1.6765.......,null, null,null
The whole idea is to have a plot on which a certain part of the line is dashed and the remaining part is not. I initially have 7 time series, I splitted each time serie in two (the dashed part and the non-dashed part), now I would like to highlight the whole time series ( so 2 distinct series in terms of Dygraphs the dashed serie, and the non-dashed that I splitted in two) when I pass the mouse over either the dashed region either the non dashed region.
I ve seen that people were stipulating using HihlightCallback but I am struggling to put it in practice.
What I have for the moment:
data =[new Date(), ..,..,.,,.,,.]
labels= {'A','B', ..... }
series= {'A': {strokePattern: [10, 20] }, 'B': .......}
g = new Dygraph( demo, data, {width: 1000,height: 700,labelsDivStyles: { 'textAlign': 'right' }, labels: labels,series:series, visibility: visibility, gridLineColor: 'red', gridLinePattern: [5,5], highlightCircleSize: 2,strokeWidth: 1, strokeBorderWidth: 1,highlightSeriesOpts: { strokeWidth: 3,strokeBorderWidth: 1,highlightCircleSize: 5}});
I believe my structure should be as follows:
g.updateOptions({ highlightCallback: function(event, x, points, row, seriesName) {
//1)here I need to somehow reference the other series whose label is situated N columns from the highlighted serie ( I can also reference it by its name).
// 2) Hilight the other serie
}});
I tried many different syntaxe but nothing seems to be working properly.
Could anyone please help me on this I am lost.
Here is what I would like to achieve :
http://www.google.co.uk/publicdata/explore?ds=k3s92bru78li6_#!ctype=l&strail=false&bcs=d&nselm=h&met_y=ggxwdn_ngdp&scale_y=lin&ind_y=false&rdim=world&idim=world:Earth&idim=country:AR:DZ:AU:AZ&ifdim=world&tstart=343382400000&tend=1574064000000&hl=en_US&dl=en_US&ind=false
Thanks a lot!
If I understand correctly, you've set up something like this: jsbin
Typically you style the highlighted series using highlightSeriesOpts, but that comes with the assumption that there's only a single highlighted series.
If you want to model the data this way (as separate series for actual & projected), you'll need to style the series yourself using highlightCallback. There are a few gross things about this which I'll mention below, but this is doable.
Demo: jsbin
g = new Dygraph(document.getElementById("graph"),
"X,Y,Y projected,Z,Z projected\n" +
"2006,0,,3,\n" +
"2008,2,,6,\n" +
"2010,4,,8,\n" +
"2012,6,,9,\n" +
"2014,8,8,9,9\n" +
"2016,,10,,8\n" +
"2018,,12,,6\n" +
"2020,,14,,3\n",
{
colors: ['blue', 'blue', 'red', 'red'],
series: {
'Y': { },
'Y projected': { strokePattern: [5, 5] },
'Z': { },
'Z projected': { strokePattern: [5, 5] }
},
highlightCallback: function(_, _, _, row, seriesName) {
update(seriesName, row);
},
unhighlightCallback: function() {
update();
},
highlightSeriesOpts: {},
highlightSeriesBackgroundAlpha: 1.0
});
function update(selectedSeries, row) {
var newOptions = {};
var seriesNames = g.getLabels().slice(1);
seriesNames.forEach(function(label) {
newOptions[label] = {strokeWidth: 1};
});
if (selectedSeries == 'Y' || selectedSeries == 'Y projected') {
newOptions['Y'] = newOptions['Y projected'] = {strokeWidth: 3};
} else if (selectedSeries == 'Z' || selectedSeries == 'Z projected') {
newOptions['Z'] = newOptions['Z projected'] = {strokeWidth: 3};
}
g.updateOptions({series: newOptions});
if (typeof(row) !== 'undefined') {
g.setSelection(row);
}
}
The idea is that you call updateOptions in your highlightCallback, setting the strokeWidth property for each series according to whether it (or its paired series) is selected.
There are a few gross things about this:
You have to set highlightSeriesOpts for the seriesName parameter to be passed to highlightCallback.
You need to counteract the default fading behavior of highlightSeriesOpts by setting highlightSeriesBackgroundAlpha.
Calling updateOptions clears the selection, so you have to call setSelection explicitly to re-select.
If you're willing to model the measured & projected values as a single series, then you can accomplish this more cleanly by writing a custom plotter which switches from solid to dashed lines at some point.
Here's a demo: jsbin
g = new Dygraph(document.getElementById("graph"),
"X,Y,Z\n" +
"2004,0,3\n" +
"2006,2,6\n" +
"2008,4,8\n" +
"2010,6,9\n" +
"2012,8,9\n" +
"2014,10,8\n" +
"2016,12,6\n" +
"2018,14,3\n",
{
plotter: function(e) {
var ctx = e.drawingContext;
ctx.beginPath();
ctx.moveTo(e.points[0].canvasx, e.points[0].canvasy);
for (var i = 1; i < e.points.length; i++) {
var p = e.points[i];
ctx.lineTo(p.canvasx, p.canvasy);
if (p.xval == 2014) {
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p.canvasx, p.canvasy);
ctx.setLineDash([5]);
}
}
ctx.stroke();
ctx.setLineDash([]);
},
highlightSeriesOpts: {
strokeWidth: 3
}
});
Because your data is a single series, you no longer need to highlight multiple series simultaneously and hence you can use highlightSeriesOpts.

Categories

Resources