I am trying to plot a 3d network using plotly.js
<html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div id="myDiv"></div>
<script>
var nodes = [
{ x: 0, y: 0, z: 0 },
{ x: 1, y: 1, z: 1 },
{ x: 2, y: 0, z: 2 },
{ x: 3, y: 1, z: 3 },
{ x: 4, y: 0, z: 4 }
];
var edges = [
{ source: 0, target: 1 },
{ source: 1, target: 2 },
{ source: 2, target: 3 },
{ source: 3, target: 4 }
];
var x = [];
var y = [];
var z = [];
for (var i = 0; i < nodes.length; i++) {
x.push(nodes[i].x);
y.push(nodes[i].y);
z.push(nodes[i].z);
}
var xS = [];
var yS = [];
var zS = [];
var xT = [];
var yT = [];
var zT = [];
for (var i = 0; i < edges.length; i++) {
xS.push(nodes[edges[i].source].x);
yS.push(nodes[edges[i].source].y);
zS.push(nodes[edges[i].source].z);
xT.push(nodes[edges[i].target].x);
yT.push(nodes[edges[i].target].y);
zT.push(nodes[edges[i].target].z);
}
var traceNodes = {
x: x, y: y, z: z,
mode: 'markers',
marker: { size: 12, color: 'red' },
type: 'scatter3d'
};
var traceEdges = {
x: [xS, xT],
y: [yS, yT],
z: [zS, zT],
mode: 'lines',
line: { color: 'red', width: 2},
opacity: 0.8,
type: 'scatter3d'
};
var layout = {
margin: { l: 0, r: 0, b: 0, t: 0 }
};
Plotly.newPlot('myDiv', [traceNodes, traceEdges], layout);
</script>
</body>
</html>
The nodes of the network are visible but the edges are not visible. Suggestions on
how to fix this issue will be really helpful.
The problem is how traceEdges is built. On each axis, there should be a list of (source, target) coordinates separated by a null value, ie. :
x: [source_0.x, target_0.x, null, source_1.x, target_1.x, null, ...]
We use null values to properly isolate each edge from the others and prevent chaining them all together (we want to draw a line between source_0 and target_0, but not between target_0 and source_1).
So instead of having [xS, xT], [yS, yT], and [zS, zT] we will just have edge_x, edge_y, and edge_z, defined as follows :
const edge_x = [];
const edge_y = [];
const edge_z = [];
for (var i = 0; i < edges.length; i++) {
const a = nodes[edges[i].source];
const b = nodes[edges[i].target];
edge_x.push(a.x, b.x, null);
edge_y.push(a.y, b.y, null);
edge_z.push(a.z, b.z, null);
}
const traceEdges = {
x: edge_x,
y: edge_y,
z: edge_z,
type: 'scatter3d',
mode: 'lines',
line: { color: 'red', width: 2},
opacity: 0.8
};
// (rest of the code doesn't change)
Here is the output :
Related
I am trying to add gradient colors in edges of a force directed graph made in Plotly.js.
I have taken the input through Json file and have also used 'color_continuous_scale' to add the colors. But the desired result is not coming.
Full code:
<html>
<head>
<script src="https://cdn.plot.ly/plotly-1.58.5.min.js"></script>
<style>
.graph-container {
display: flex;
justify-content: center;
align-items: center;
}
.main-panel {
width: 70%;
float: left;
}
.side-panel {
width: 30%;
background-color: lightgray;
min-height: 300px;
overflow: auto;
float: right;
}
</style>
</head>
<body>
<div class="graph-container">
<div id="myDiv" class="main-panel"></div>
<div id="lineGraph" class="side-panel"></div>
</div>
<script>
fetch('data.json')
.then(response => response.json())
.then(data => {
var nodes = data.nodes;
var edges = data.edges;
var x = [];
var y = [];
var z = [];
for (var i = 0; i < nodes.length; i++) {
x.push(nodes[i].x);
y.push(nodes[i].y);
z.push(nodes[i].z);
}
const edge_x = [];
const edge_y = [];
const edge_z = [];
for (var i = 0; i < edges.length; i++) {
const a = nodes[data.edges[i].source];
const b = nodes[data.edges[i].target];
edge_x.push(a.x, b.x, null);
edge_y.push(a.y, b.y, null);
edge_z.push(a.z, b.z, null);
}
const traceEdges = {
x: edge_x,
y: edge_y,
z: edge_z,
type: 'scatter3d',
mode: 'lines',
line: { color: 'traceEdges', color_continuous_scale: 'Inferno', width: 10},
opacity: 0.8
};
var traceNodes = {
x: x, y: y, z: z,
mode: 'markers',
marker: { size: 12, color: 'red' },
type: 'scatter3d',
text: [0, 1, 2, 3, 4],
hoverinfo: 'text',
hoverlabel: {
bgcolor: 'white'
},
customdata: nodes.map(function(node) {
if (node.value !== undefined)
return node.value;
}),
type: 'scatter3d'
};
var layout = {
margin: { l: 0, r: 0, b: 0, t: 0 }
};
Plotly.newPlot('myDiv',[traceEdges, traceNodes], layout);
// max y value for the line plot
const ymax = Math.max(...nodes.map(n => n.value).flat());
document.getElementById('myDiv').on('plotly_click', function(data){
var nodeIndex = data.points[0].pointNumber;
var values = nodes[nodeIndex].value;
Plotly.newPlot('lineGraph', [{
type: 'scatter',
mode: 'lines',
x: [0, 1, 2],
y: values
}], {
margin: { t: 0 },
yaxis: {autorange: false, range: [0, ymax + 1]}
}
);
});
})
</script>
</body>
</html>
As you can see in the above image that the plot is showing only one color in the edges. I want to change this attribute to gradient coloring. Any changes in the code will be welcomed.
You need to specify a color array so that values from that array can be mapped to a colorscale :
color - Sets the line color. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to line.cmin and line.cmax if set.
colorscale - Sets the colorscale. Has an effect only if in line.color is set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string [...]. Alternatively, colorscale may be a palette name string [...].
For example by setting color: edge_y, edges are colored according to their positioning on the y axis (y coordinates of the source and target nodes) :
const traceEdges = {
x: edge_x,
y: edge_y,
z: edge_z,
type: 'scatter3d',
mode: 'lines',
line: {
autocolorscale: false,
colorscale: 'Cividis',
color: edge_y,
width: 10
},
opacity: 0.8,
};
Nb. color_continuous_scale is specific to plotly.express (python), and the 'Inferno' color scale as well. You can't use it with plotly.js.
I am trying to plot a 3d network using plotly.js
<html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div id="myDiv"></div>
<script>
var nodes = [
{ x: 0, y: 0, z: 0 },
{ x: 1, y: 1, z: 1 },
{ x: 2, y: 0, z: 2 },
{ x: 3, y: 1, z: 3 },
{ x: 4, y: 0, z: 4 }
];
var edges = [
{ source: 0, target: 1 },
{ source: 1, target: 2 },
{ source: 2, target: 3 },
{ source: 3, target: 4 }
];
var x = [];
var y = [];
var z = [];
for (var i = 0; i < nodes.length; i++) {
x.push(nodes[i].x);
y.push(nodes[i].y);
z.push(nodes[i].z);
}
var xS = [];
var yS = [];
var zS = [];
var xT = [];
var yT = [];
var zT = [];
for (var i = 0; i < edges.length; i++) {
xS.push(nodes[edges[i].source].x);
yS.push(nodes[edges[i].source].y);
zS.push(nodes[edges[i].source].z);
xT.push(nodes[edges[i].target].x);
yT.push(nodes[edges[i].target].y);
zT.push(nodes[edges[i].target].z);
}
var traceNodes = {
x: x, y: y, z: z,
mode: 'markers',
marker: { size: 12, color: 'red' },
type: 'scatter3d'
};
var traceEdges = {
x: [xS, xT],
y: [yS, yT],
z: [zS, zT],
mode: 'lines',
line: { color: 'red', width: 2},
opacity: 0.8,
type: 'scatter3d'
};
var layout = {
margin: { l: 0, r: 0, b: 0, t: 0 }
};
Plotly.newPlot('myDiv', [traceNodes, traceEdges], layout);
</script>
</body>
</html>
The nodes of the network are visible but the edges are not visible. Suggestions on
how to fix this issue will be really helpful.
The problem is how traceEdges is built. On each axis, there should be a list of (source, target) coordinates separated by a null value, ie. :
x: [source_0.x, target_0.x, null, source_1.x, target_1.x, null, ...]
We use null values to properly isolate each edge from the others and prevent chaining them all together (we want to draw a line between source_0 and target_0, but not between target_0 and source_1).
So instead of having [xS, xT], [yS, yT], and [zS, zT] we will just have edge_x, edge_y, and edge_z, defined as follows :
const edge_x = [];
const edge_y = [];
const edge_z = [];
for (var i = 0; i < edges.length; i++) {
const a = nodes[edges[i].source];
const b = nodes[edges[i].target];
edge_x.push(a.x, b.x, null);
edge_y.push(a.y, b.y, null);
edge_z.push(a.z, b.z, null);
}
const traceEdges = {
x: edge_x,
y: edge_y,
z: edge_z,
type: 'scatter3d',
mode: 'lines',
line: { color: 'red', width: 2},
opacity: 0.8
};
// (rest of the code doesn't change)
Here is the output :
I'd like to know if it's possible to draw this function in chart js :
And have something that look like :
For now, using the solution given in an other post I have this result :
But as you can see, the parabola part of the graph extends too far on the x-axis and therefore does not look like the expected result..
I'm using this code :
const data = {
labels: labels,
datasets: [
{
function: function (x) {
if (x <= Ec2) {
return fck * (1 - Math.pow(1 - x / Ec2, n));
} else {
return fck;
}
},
borderColor: 'red',
data: [],
fill: false,
pointRadius: 0,
},
],
};
Chart.pluginService.register({
beforeInit: function (chart) {
if (Ec2 > 0) {
for (let i = 0; i <= Ec2; i += Ec2 / 5) {
labels.push(i.toFixed(1));
}
}
if (Ecu2 > 0) {
labels.push(Ecu2);
}
var data = chart.config.data;
for (var i = 0; i < data.datasets.length; i++) {
for (var j = 0; j < data.labels.length; j++) {
var fct = data.datasets[i].function,
x = data.labels[j],
y = fct(x);
data.datasets[i].data.push(y);
}
}
},
});
I think your code is just right, but there are not enough data points in the X axis and therefore the shape of the function looks like a totally different function.
Here is the same code with more X axis data points:
var Ec2 = 5
var fck = 2
var ctx = document.getElementById("myChart");
var data = {
labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
datasets: [{
label: "f(x)",
function: function(x) {
if (x <= Ec2) {
return fck * (1 - Math.pow(1 - x / Ec2, 2));
} else {
return fck;
}
},
borderColor: "rgba(75, 192, 192, 1)",
data: [],
fill: false
}]
};
Chart.pluginService.register({
beforeInit: function(chart) {
var data = chart.config.data;
for (var i = 0; i < data.datasets.length; i++) {
for (var j = 0; j < data.labels.length; j++) {
var fct = data.datasets[i].function,
x = data.labels[j],
y = fct(x);
data.datasets[i].data.push(y);
}
}
}
});
var myBarChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
title: {
display: true
},
legend: {
position: 'bottom'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
<canvas id="myChart"></canvas>
I simply added more label entries with so that data.labels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
the issue I have now is that I'm trying to use objects in the "data" field of my Chartjs script. This is my code below:
<canvas id="QGL_Chart"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script>
var interval = window.onload = () => {
var selectedDate;
const buyPriceData = [];
const buyVolData = [];
const sellPriceData = [];
const sellVolData = [];
var ctx = document.getElementById("QGL_Chart").getContext('2d');
ctx.canvas.width = 934;
ctx.canvas.height = 400;
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Line A',
data: [{
x: 90,
y: 90
}, {
x: 20,
y: 96
}, {
x: 15,
y: 97
}]
},
{
label: 'Line B',
data: [{
x: 10,
y: 96
}, {
x: 20,
y: 95.8
}, {
x: 100,
y: 99
}]
}
]
},
options: {
title: {
display: true,
text: 'Equilibrum Graph',
fontSize: 18
},
legend: {
display: true,
position: "bottom"
},
scales: {
xAxes: [{
ticks: {
reverse: false,
// stepSize: 6,
min: 0
}
}]
}
}
});
function refreshCurveData () {
selectedDate = document.querySelectorAll(".buyclass .column-date.sorting_1")[1].textContent;
buyPriceTable = document.querySelectorAll(".buyclass td.column-price");
buyVolTable = document.querySelectorAll(".buyclass td.column-volume");
sellPriceTable = document.querySelectorAll(".sellclass td.column-price");
sellVolTable = document.querySelectorAll(".sellclass td.column-volume");
let i = 0;
do {
var buyPriceData1 = buyPriceTable[i].textContent;
var buyVolData1 = buyVolTable[i].textContent;
var sellPriceData1 = sellPriceTable[i].textContent;
var sellVolData1 = sellVolTable[i].textContent;
buyPriceData[i] = `{x: ${buyPriceData1}, y: ${buyVolData1}}`
sellPriceData[i] = `{x: ${sellPriceData1}, y: ${sellVolData1}}`
//buyPriceData[i] = buyPriceData[i].replace(/"/g, "");
//sellPriceData[i] = sellPriceData[i].replace(/"/g, "");
// buyPriceData.push ({ x: buyPriceData1, y: buyVolData1 });
// sellPriceData.push ({ x: sellPriceData1, y: sellVolData1 });
i++;
}
while ( document.querySelectorAll(".column-date.sorting_1")[i].textContent == selectedDate && i < 9);
}
setInterval(() => {
refreshCurveData();
myChart.update();
},2500);
};
</script>
When I replace the codes in the "data" fields with buyPriceData and sellPriceData respectively, the graph does not visualize, I also tried doing:
data: {
datasets: [{
label: 'Line A',
data: Object.values(buyPriceData)
},
{
label: 'Line B',
data: Object.values(sellPriceData)
}
]
},
But I still have the same problem, please can someone help me with this?
I've created a pen here: https://codepen.io/scottfranc/pen/BaLGwZK
Thank you.
it looks like you are saving a String in buyPriceData. It looks like it should be an object
Maybe try this:
buyPriceData[i] = { x: buyPriceData1, y: buyVolData1 };
and then do the same for sellPriceData
Is it possible to add an image to a specific data point in an X-Series Highchart graph?
For example, I have the following chart:
/**
* Highcharts X-range series plugin
*/
(function (H) {
var defaultPlotOptions = H.getOptions().plotOptions,
columnType = H.seriesTypes.column,
each = H.each,
extendClass = H.extendClass,
pick = H.pick,
Point = H.Point,
Series = H.Series;
defaultPlotOptions.xrange = H.merge(defaultPlotOptions.column, {
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
}
});
H.seriesTypes.xrange = H.extendClass(columnType, {
pointClass: extendClass(Point, {
// Add x2 and yCategory to the available properties for tooltip formats
getLabelConfig: function () {
var cfg = Point.prototype.getLabelConfig.call(this);
cfg.x2 = this.x2;
cfg.yCategory = this.yCategory = this.series.yAxis.categories && this.series.yAxis.categories[this.y];
return cfg;
}
}),
type: 'xrange',
forceDL: true,
parallelArrays: ['x', 'x2', 'y'],
requireSorting: false,
animate: H.seriesTypes.line.prototype.animate,
/**
* Borrow the column series metrics, but with swapped axes. This gives free access
* to features like groupPadding, grouping, pointWidth etc.
*/
getColumnMetrics: function () {
var metrics,
chart = this.chart;
function swapAxes() {
each(chart.series, function (s) {
var xAxis = s.xAxis;
s.xAxis = s.yAxis;
s.yAxis = xAxis;
});
}
swapAxes();
this.yAxis.closestPointRange = 1;
metrics = columnType.prototype.getColumnMetrics.call(this);
swapAxes();
return metrics;
},
/**
* Override cropData to show a point where x is outside visible range
* but x2 is outside.
*/
cropData: function (xData, yData, min, max) {
// Replace xData with x2Data to find the appropriate cropStart
var crop = Series.prototype.cropData.call(this, this.x2Data, yData, min, max);
// Re-insert the cropped xData
crop.xData = xData.slice(crop.start, crop.end);
return crop;
},
translate: function () {
columnType.prototype.translate.apply(this, arguments);
var series = this,
xAxis = series.xAxis,
metrics = series.columnMetrics,
minPointLength = series.options.minPointLength || 0;
H.each(series.points, function (point) {
var plotX = point.plotX,
plotX2 = xAxis.toPixels(H.pick(point.x2, point.x + (point.len || 0)), true),
width = plotX2 - plotX,
widthDifference;
if (minPointLength) {
widthDifference = width < minPointLength ? minPointLength - width : 0;
plotX -= widthDifference / 2;
plotX2 += widthDifference / 2;
}
plotX = Math.max(plotX, -10);
plotX2 = Math.min(Math.max(plotX2, -10), xAxis.len + 10);
point.shapeArgs = {
x: plotX,
y: point.plotY + metrics.offset,
width: plotX2 - plotX,
height: metrics.width
};
point.tooltipPos[0] += width / 2;
point.tooltipPos[1] -= metrics.width / 2;
});
}
});
/**
* Max x2 should be considered in xAxis extremes
*/
H.wrap(H.Axis.prototype, 'getSeriesExtremes', function (proceed) {
var axis = this,
dataMax,
modMax;
proceed.call(this);
if (this.isXAxis) {
dataMax = pick(axis.dataMax, Number.MIN_VALUE);
each(this.series, function (series) {
each(series.x2Data || [], function (val) {
if (val > dataMax) {
dataMax = val;
modMax = true;
}
});
});
if (modMax) {
axis.dataMax = dataMax;
}
}
});
}(Highcharts));
// THE CHART
Highcharts.chart('container', {
chart: {
type: 'xrange'
},
title: {
text: 'Item List'
},
xAxis: {
type: 'datetime',
min: Date.UTC(2014, 11, 3)
},
yAxis: {
title: '',
categories: ['Item 1', 'Item 2'],
reversed: true
},
series: [{
name: 'Project 1',
// pointPadding: 0,
// groupPadding: 0,
borderRadius: 5,
pointWidth: 10,
data: [{
x: Date.UTC(2014, 11, 3),
x2: Date.UTC(2014, 11, 3),
y: 0
}, {
x: Date.UTC(2014, 11, 6),
x2: Date.UTC(2014, 11, 7),
y: 0
},
{
x: Date.UTC(2014, 11, 9),
x2: Date.UTC(2014, 11, 11),
y: 0
}], color: '#BF0B23'
}]
});
JSFiddle Example
In the first data point:
{
x: Date.UTC(2014, 11, 3),
x2: Date.UTC(2014, 11, 3),
y: 0
},
I want to add a marker image as the date range renders it too small to actually be displayed on the graph (like the "sun" image from this example) but I can't work out where the marker would need to be placed in my JSFiddle example.
The marker is placed on your point:
{
x: Date.UTC(2014, 11, 3),
x2: Date.UTC(2014, 11, 3),
y: 0,
// Like this:
marker: {
symbol: 'url(...)'
}
},