I'm making a website with one graph that use ChartJS library. This chart display, on the website, all of the data available as default.
My goal is to let the visitors to choose a different number of data of the graph. So, I did one button at the top of the chart which, when a visitor click on it, must change the number of data displayed.
The problem is that ChartJS use a JSON object and I not succeed configure different behaviors in it.
...
datasets: [
{
label: "Exemple",
data: [
{ x: "Day1", y: 0 },
{ x: "Day2", y: 1 },
{ x: "Day3", y: 2 },
{ x: "Day4", y: 3 },
{ x: "Day5", y: 4 },
....
],
...
I tried to put an event on it, like :
data: mybutton.addEventListener("click", () => {
[{x: "Day1", y: 0}]
},
A function, like :
data: myFunction(),
Even a variable with function into, like :
const myData = () => {
// function
}
data: myData,
Or an If else.
Nothing worked... Do you any idea ?
I found the solution by myself.
I put the JSON object in a function, put an AddEventListener("click", function()), targeted the part of the JSON to modify and added an update() at the end.
function chartFunction() {
....
// data before
datasets: [
{
label: "Exemple",
data: [
{ x: "Day1", y: 0 },
.....
button.addEventListener("click", () => {
(myChart.data.datasets[0].data = [...]).myChart.update();
});
};
chartFunction();
Does anyone here use the library react-d3-graph?
https://danielcaldas.github.io/react-d3-graph/docs/index.html
I'm trying to find a way to give my nodes starting positions, I'm unsure how their positions are set upon render right now. Any idea how to do so ?
Thanks
I couldn't find it in the documentation, but you can just add an x and y property to a node to give it an initial position. (Works whether you set the prop staticGraph or not.)
The following will place node 'a' above node 'b' every time, instead of placing them randomly.
<Graph
id="test-graph"
data={{
nodes: [
{ id: 'a', x: 0, y: 0 },
{ id: 'b', x: 0, y: 1 },
],
links: [{ source: 'a', target: 'b' }],
}}
/>
It's also possible to have coordinates on only some nodes. Then the ones without coordinates will be placed randomly.
highcharts: 8.0.4
I have created a jsfiddle to demonstrate what is happening, showing a pie and bar chart and how they behave differently. In my scenario the chart is instantiated without data. Then data is requested from an api (simulate this in the jsfiddle by clicking the buttons). The charts display, then data is refreshed (click the buttons again). You should notice that the pie chart "disappears" while the bar chart does not. I would guess this is because the "color" property is set to null.
My question is why does the pie chart behave differently to the bar chart?
In my scenario I may or may not have a "color" value returned from the api. I did come up with a solution to conditionally add the property only if it has a value:
data.map((d) => {
if (d.color) {
return (
{
name: d.group,
y: d.value,
color: d.color,
id: d.id,
});
}
return (
{
name: d.group,
y: d.value,
id: d.id,
});
});
This does seem to work, however (here is my second question), is there a better way to do this?
For this particular use case:
You can use the update instead of the setData feature.
Demo: https://jsfiddle.net/BlackLabel/1bjfh63c/
Code:
button1.addEventListener('click', function() {
let x = Math.floor(Math.random() * (99)) + 1;
let y = Math.floor(Math.random() * (99)) + 1;
chart1.series[0].setData(
[{
y: x,
name: 'Apple',
color: null,
id: 1,
},
{
y: y,
name: 'Peach',
color: null,
id: 2,
}
], false, false, false);
chart1.redraw();
})
In setData set the updatePoints flag to false - that's why the error occurred.
demo: https://jsfiddle.net/BlackLabel/umo14be0/
API: https://api.highcharts.com/class-reference/Highcharts.Series#setData
Currently I have a request to have a Bullet Chart with two targets (min and max).
To do it I am simply using a normal Bullet Chart with a Scatter series to draw the other target. I would like to wrap this behavior inside the bullet chart, so it would have something like the following options:
series: [{
data: [{
y: 275,
target: 250,
minTarget: 100
}]
},
And then, on the wrap, I would get this minTarget and make a scatter plot automatically. How can I do it?
Here's the fiddle I have so far: http://jsfiddle.net/gwkxd02p/
I do not think that render is a good method to add another series - anyway, you can try to do it like this:
Highcharts.wrap(Highcharts.seriesTypes.bullet.prototype, 'render', function(p) {
if (!this.hasRendered) {
const scatterData = this.points
.map(({ x, y, options }) => ({
x,
y: options.minTarget !== undefined ? options.minTarget : null
}))
if (scatterData.length) {
const scatter = this.chart.addSeries({
type: 'scatter',
data: scatterData,
marker: {
symbol: 'line',
lineWidth: 3,
radius: 8,
lineColor: '#000'
}
}, false)
scatter.translate()
scatter.render()
}
}
p.call(this)
})
And data for bullet:
series: [{
data: [{
y: 275,
target: 250,
minTarget: 100
}, {
y: 100,
target: 50
}, {
y: 500,
target: 600,
minTarget: 20
}]
live example: http://jsfiddle.net/n4p0ezzw/
I think that the better place is bullet's init method but in that method the points do not exist yet - so you would have to match the x values (if it is needed) on your own.
My suggestion is - do not wrap Highcharts if you don't have to. A better (simpler, safer, cleaner, easier to debug, it does not change Highcharts internal code) practice would be to wrap the Highcharts constructor in a function and parse the options inside it and then call the chart constructor with new options, like this:
function customBullet(container, options) {
const newOptions = {} // parse options, check for minTarget, etc. and create new options
return Highcharts.chart(container, newOptions)
}
How do I set a node's position in vis.js?
I want to initially position at least one node manually.
I know that a node has the options x and y. I set both, and also tried variations of layout options (randomSeed, improvedLayout, hierarchical), the node was never placed where I set it.
Here's the simple network I defined:
nodes = new vis.DataSet([
{id: 1, shape: 'circularImage', image: DIR + '1_circle', label:"1", x: 200, y: 100},
{id: 2, shape: 'circularImage', image: DIR + '2_circle', label:"2"},
{id: 3, shape: 'circularImage', image: DIR + '3_circle', label:"3"},
]);
edges = [
{id: "01-03", from: 1, to: 3, length: 300, label: '1 - 3'},
{id: "02-03", from: 2, to: 3},
];
var container = document.getElementById('graphcontainer');
var data = {
nodes: nodes,
edges: edges
};
var options = {
nodes: {
borderWidth: 4,
size: 30,
color: {
border: '#222222',
background: '#666666'
},
font:{
color:'#000000'
}
},
edges: {
color: 'lightgray'
},
//layout: {randomSeed:0}
//layout: {hierarchical: true}
layout: {
randomSeed: undefined,
improvedLayout:true,
hierarchical: {
enabled:false,
levelSeparation: 150,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed
}
}
};
network = new vis.Network(container, data, options);
The node is placed, but not at the point I set (200,100), but at another position.
I haven't found an example for explicitly setting a node's position on the vis.js page. Could someone please provide one? Thanks!
You can indeed set a fixed position for a node by setting its x and y properties, and yes, this feature works and is not broken.
The x and y position of a node does not mean a position in pixels on the screen, but is a fixed position in the Networks coordinate system. When you move and zoom in the Network, the fixed items will move and zoom too, but they will always keep the same position relative to each other. It's like your home town has a fixed location (long, lat) on earth, but you can still zoom and move your town in Google Maps.
EDIT: To achieve what you want, you can fix zooming and moving, and adjust the viewport such that it matches the pixels of the HTML canvas, here is a demo:
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'x=200, y=200', x: 200, y: 200},
{id: 2, label: 'node 2', x: 0, y: 0},
{id: 3, label: 'node 3', x: 0, y: 400},
{id: 4, label: 'node 4', x: 400, y: 400},
{id: 5, label: 'node 5', x: 400, y: 0}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 2, label: 'to x=0, y=0'},
{from: 1, to: 3, label: 'to x=0, y=400'},
{from: 1, to: 4, label: 'to x=400, y=400'},
{from: 1, to: 5, label: 'to x=400, y=0'}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var width = 400;
var height = 400;
var options = {
width: width + 'px',
height: height + 'px',
nodes: {
shape: 'dot'
},
edges: {
smooth: false
},
physics: false,
interaction: {
dragNodes: false,// do not allow dragging nodes
zoomView: false, // do not allow zooming
dragView: false // do not allow dragging
}
};
var network = new vis.Network(container, data, options);
// Set the coordinate system of Network such that it exactly
// matches the actual pixels of the HTML canvas on screen
// this must correspond with the width and height set for
// the networks container element.
network.moveTo({
position: {x: 0, y: 0},
offset: {x: -width/2, y: -height/2},
scale: 1,
})
#mynetwork {
border: 1px solid black;
background: white;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.css" rel="stylesheet" type="text/css" />
<p>The following network has a fixed scale and position, such that the networks viewport exactly matches the pixels of the HTML canvas.</p>
<div id="mynetwork"></div>
Documentation says that nodes positioned by the layout algorithm
When using the hierarchical layout, either the x or y position is set
by the layout engine depending on the type of view
You can put they in to explicit points but I would not recommend this - it's not the correct way for the work with the graphs - better review your task - maybe you do not need graphs (or your do not need to put points in to exactly position).
Anyway - if you really want to put in to some position then you need to use random layout with the fixed option set to true or physic option set to false
var DIR = 'http://cupofting.com/wp-content/uploads/2013/10/';
nodes = new vis.DataSet([
{id: 1, shape: 'circularImage', image: DIR + '1-circle.jpg', label:"1", x:0, y:0},
{id: 2, shape: 'circularImage', image: DIR + '2-circle.jpg', label:"2", x:100, y:0},
{id: 3, shape: 'circularImage', image: DIR + '3-circle.jpg', label:"3", x:0, y:100},
]);
edges = [
{id: "01-03", from: 1, to: 3, length: 300, label: '1 - 3'},
{id: "02-03", from: 2, to: 3},
];
var container = document.getElementById('graphcontainer');
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics:{
stabilization: true,
},
nodes: {
borderWidth: 4,
size: 10,
//fixed:true,
physics:false,
color: {
border: '#222222',
background: '#666666'
},
font:{
color:'#000000'
}
},
edges: {
color: 'lightgray'
},
layout: {randomSeed:0}
};
network = new vis.Network(container, data, options);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.js"></script>
<div id="graphcontainer" style="border: 1px solid red; width:300px; height:300px; "> </div>
:) I just did this for the first time this morning. X = 0 and Y = 0 for a graph start centered, not at the top left of the canvas. There is a fixed attribute of the node that you can set to true for both x and y on a node with its x and y values set and have other nodes use physics in relation to it.
Look at the full options tab on page
http://visjs.org/docs/network/nodes.html#
and you'll see this piece:
fixed: {
x:false,
y:false
},