Best way of create/delete/restyle graph dynamically with Plotly.js? - javascript

I want to add and delete graphs with buttons on my page.
I have to pass layout, and data as Json to the plotly.plot() function.
How can i do this dynamically?
Example code from reference :
var trace1 = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter'
};
var trace2 = {
x: [1, 2, 3, 4],
y: [16, 5, 11, 9],
type: 'scatter'
};
var data = [trace1, trace2];
var layout = {
width: 500,
height: 500
};
Plotly.newPlot('myDiv', data,layout);
i receive my data via ajax from a database.
function getTrace_data(x,y) {
$.ajax({
type: "GET",
url: "get.php?action=data&x="+x+"&y="+y
dataType: "json",
success: function(data){
drawGraph(data);
},
error: function(error){
console.log(error);
}
});
}
function drawGraph(data)
{
var trace1 = {
x: data.x,
y: data.y,
type: 'scatter'
};
var layout = {
width: 500,
height: 500
};
Plotly.newPlot('myDiv', data,layout);
}
Now i can draw a graph, but how should i change the type of the graph dynamically? or layout options?

You can just overwrite the existing graph with a new one and dynamically change the layout of your graph with a few variables, see the snippet below. Just assume the buttons are different AJAX calls.
function changeGraph(graphType) {
var traces = [];
var graph_types = [];
var myDiv = document.getElementById("mydiv");
switch (graphType) {
case 1:
graph_types.push("scatter");
graph_types.push("bar");
break;
case 2:
graph_types.push("bar");
graph_types.push("bar");
break;
default:
graph_types.push("scatter");
graph_types.push("scatter");
}
traces.push({
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: graph_types[0]
});
traces.push({
x: [1, 2, 3, 4],
y: [16, 5, 11, 9],
type: graph_types[1]
});
var layout = {
width: 500,
height: 500
};
Plotly.newPlot(myDiv, traces, layout);
}
document.getElementById("button0").addEventListener("click", function () {
changeGraph(0);
});
document.getElementById("button1").addEventListener("click", function () {
changeGraph(1);
});
document.getElementById("button2").addEventListener("click", function () {
changeGraph(2);
});
document.getElementById("button0").click();
<script src=https://cdn.plot.ly/plotly-latest.min.js></script>
<div id="mydiv"></div>
<button id="button0">Scatter only</button>
<button id="button1">Bar&Scatter</button>
<button id="button2">Bars only</button>

Related

Dynamically Toggle Visibility of Shapes in Plotly.js

I am creating an array of plotly.js shapes which work great. I have added a custom data attribute of 'custType' : '1' or 'custType' : '2' based on the type of shape. The number of each shape type will not be consistent so I need to be able to update the visibility dynamically.
I have tried this... to no avail
var update = [];
for(x=0; x<data.layout.shapes.length; x++){
if(data.layout.shapes[x].custType == '1'){
update.push({'shapes[' + x + '].visible':false})
}
}
Plotly.relayout('main', update);
update has to be an object instead of an array, like
{
"shapes[0].visible": false,
"shapes[1].visible": false
}
See the snippet below.
const trace = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter'
};
const data = [trace];
const layout = {
shapes: [
{
type: 'rect',
x0: 1,
x1: 2,
y0: 10,
y1: 12,
fillcolor: '#d3d3d3',
line: {
width: 0
}
},
{
type: 'rect',
x0: 3,
x1: 4,
y0: 12,
y1: 14,
fillcolor: '#d3d3d3',
line: {
width: 0
}
}
]
}
Plotly.newPlot('myDiv', data, layout);
function update() {
const update = {};
for(let i = 0; i < layout.shapes.length; i++){
update['shapes[' + i + '].visible'] = false;
}
Plotly.relayout('myDiv', update);
}
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
<button type="button" onClick="update()">Update</button>
</body>

How to invert scale display on radar chart (chart.js)

hello does anyone has any idea on how to invert the scale display on my graph? i want it to start from 100, ending with 0
i tried with scale:reverse but it didnt work
var scaling = [10, 10, 10, 10, 10, 10];
var barData = {
labels: [["PH"],
["Heavy Metals"],
["Chemical Oxygen Demand"],
["Transparency"],
"Ammonia Nitrogen",
["Dissolved", "Oxygen"],],
datasets: [
{
fillColor: "#48A497",
strokeColor: "#48A4D1",
data: [2, 10, 7, 10, 7, 10].map(function (e, i) {
return e * scaling[i];
}),
},
],
};
var income = document.getElementById("income").getContext("2d");
new Chart(income).Radar(barData, {
tooltipTemplate: function (valueObject) {
return (
valueObject.value / scaling[data.labels.indexOf(valueObject.label)]
);
},
});
enter image description here
Not sure if this works,
var scaling = [10, 10, 10, 10, 10, 10];
[2, 10, 7, 10, 7, 10].map(function (e, i) {
return scaling[i] = e * scaling[i];
})
var barData = {
labels: [
["PH"],
["Heavy Metals"],
["Chemical Oxygen Demand"],
["Transparency"],
"Ammonia Nitrogen",
["Dissolved", "Oxygen"],
],
datasets: [
{
fillColor: "#48A497",
strokeColor: "#48A4D1",
data: scaling.sort(function(a, b) {
return b-a;
}),
},
],
};
var income = document.getElementById("income").getContext("2d");
new Chart(income).Radar(barData, {
tooltipTemplate: function (valueObject) {
return (
valueObject.value / scaling[data.labels.indexOf(valueObject.label)]
);
},
});

Echart: How to set mark area to fill sections in xAxis

I have a problem with marking area: i need to be able to select a bar area based on xAxis, for example from 0 to 1, from 1 to 2, etc. But when i try to provide options for bar like
[{xAxis: 0, itemStyle: {color: red}},{xAxis: 1}]
it marks an area from a middle of xAxis area with an index of 0 to a middle of xAxis area with an index of 1. Is there a way to make it mark from start of an area to an end. Currently i managed to do so only with x option in pixels:
https://codesandbox.io/s/react-echart-markarea-ksj31?file=/src/index.js:714-726
Is there a better way to do it?
I can't imagine a method that would cover your requirements. It seems there is no such but nothing prevents to do it ourselves, see below.
When call function with join = true markedArea will calc as range from first to last.
calcMarkAreaByBarIndex(myChart, join = true, [4, 9])
When call function with join = false markedArea will calc for each bar.
calcMarkAreaByBarIndex(myChart, join = true, [4, 5, 6, 9])
var myChart = echarts.init(document.getElementById('main'));
var option = {
tooltip: {},
xAxis: {
data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
},
yAxis: {},
series: [
{
id: 'myBar',
name: 'Series',
type: 'bar',
data: [11, 11, 11, 11, 12, 13, 110, 123, 113, 134, 93, 109],
markArea: {
data: [
[{x: 184},{x: 216}],
[{x: 224},{x: 256}],
]
},
},
]
};
myChart.setOption(option);
function calcMarkAreaByBarIndex(chartInstance, join = false, barIdx){
var series = chartInstance.getModel().getSeriesByType('bar');
var seriesData = series.map((s, idx) => s.getData())[0];
var barNum = seriesData.count();
var barCoors = [];
var layout = idx => seriesData.getItemLayout(idx);
for(var i = 0; i < barNum; i++){
if(!barIdx.includes(i)) continue;
barCoors.push([
{ x: layout(i).x },
{ x: layout(i).x + layout(i).width },
])
}
if(join){
return [
[
{ x: barCoors[0][0].x },
{ x: barCoors[barCoors.length - 1][1].x }
]
]
} else {
return barCoors
}
}
var markedAreas = {
series: {
id: 'myBar',
markArea: {
data: calcMarkAreaByBarIndex(myChart, join = true, [4,9])
}
}
};
myChart.setOption(markedAreas);
<script src="https://cdn.jsdelivr.net/npm/echarts#4.7.0/dist/echarts.min.js"></script>
<div id="main" style="width: 600px;height:400px;"></div>
I found a solution, that worked for me:
Basically, you need to manually set yAxis's max props, add another xAxis, make it invisible, create a custom series with type 'bar' and set xAxisIndex to 1:
data: [maxYaxisValue,maxYaxisValue...], //length === xAxis.data.length
type: 'bar',
barWidth: '100%',
color: transparent,
xAxisIndex: 1,
And style a bar by index with background color and borderWidth
You can check the working example here
https://codesandbox.io/s/react-echart-markarea-m0mgq?file=/src/index.js

Plotly.extendTraces only work with two traces but not with three

function getData() {
return Math.random();
};
function plotGraph(graph_div) {
let UPDATE_INTERVAL = 300;
Plotly.plot(graph_div, [{
y: [1, 2, 3].map(getData),
name: 'x',
mode: 'lines',
line: { color: '#80CAF6' }
}, {
y: [1, 2, 3].map(getData),
name: 'y',
mode: 'lines',
line: { color: '#DF56F1' }
}, {
y: [1, 2, 3].map(getData),
name: 'z',
mode: 'lines',
line: { color: '#4D92E9' }
}]);
var cnt = 0;
var interval = setInterval(function () {
var time = new Date();
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1])
cnt = cnt+1;
if (cnt === 100) clearInterval(interval);
}, UPDATE_INTERVAL);
}
error:
plotly-latest.min.js:7 Uncaught Error: attribute y must be an array of length equal to indices array length
at plotly-latest.min.js:7
at R (plotly-latest.min.js:7)
at Object.t [as extendTraces] (plotly-latest.min.js:7)
at realtime_vis.js:40
point to
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1])
Example from official documentation only shows how plot 2 lines, but that example not working with three lines.
Any help? I assume that I can explicitly specify the size of the array?!
The Plotly documentation isn't really clear here but the third parameter is an array of plot indexes you want to modify.
In your case you are telling Plotly to modify [0, 1] but you provide 3 new y-values. If you change it to [0, 1, 2] it should work, or you could provide only two new y-values.
function getData() {
return Math.random();
};
Plotly.plot(graph_div, [{
y: [1, 2, 3].map(getData),
name: 'x',
mode: 'lines',
line: { color: '#80CAF6' }
}, {
y: [1, 2, 3].map(getData),
name: 'y',
mode: 'lines',
line: { color: '#DF56F1' }
}, {
y: [1, 2, 3].map(getData),
name: 'z',
mode: 'lines',
line: { color: '#4D92E9' }
}]);
var cnt = 0;
var interval = setInterval(function () {
var time = new Date();
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1, 2])
cnt = cnt+1;
if (cnt === 100) clearInterval(interval);
}, 300);
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div id="graph_div"></div>
</body>

Test alignment 'Center' to the highchart legend content

I have been working on the highchart for a while, I have the chart like this
and i have placed the legend at the botton of the page and what i need to do is make the legend text align center like in the following image
I found few question of text alignment in highchart but does not suite my request. So I am unable to move further.
Fiddle http://jsfiddle.net/AbNpB/12/
Thanks in Advance!
It's possible to align each line using a custom code that will translate each legend item.
It's easier to resize output window in JSFiddle: http://jsfiddle.net/BlackLabel/a02czac2/
$(function() {
(function(H) {
H.wrap(H.Legend.prototype, 'render', function(proceed) {
// proceed
proceed.apply(this, [].slice.call(arguments, 1));
// custom
var legend = this,
centerRow = function(tab, w, x) {
var offset = (legend.legendWidth - (x + w)) / 2;
H.each(tab, function(elem) {
elem.legendGroup.attr({
translateX: elem.legendGroup.translateX + offset
});
});
}
if (legend.options.centerItemLines) {
var items = legend.allItems || [],
lastY = items[0] && items[0]._legendItemPos[1],
rowItems = [],
pos, prevX, prevW;
H.each(items, function(item) {
pos = item._legendItemPos;
if (pos[1] > lastY) {
lastY = pos[1];
centerRow(rowItems, prevW, prevX);
rowItems = [];
}
rowItems.push(item);
prevX = pos[0];
prevW = item.legendGroup.getBBox(true).width;
});
centerRow(rowItems, prevW, prevX); // last line
}
});
}(Highcharts))
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container'
},
legend: {
itemStyle: {
width: 350
},
centerItemLines: true
},
series: [{
data: [6, 4, 2],
name: 'First'
}, {
data: [7, 3, 2],
name: 'Second a longer legend text and longer and longer and longer'
}, {
data: [9, 4, 8],
name: 'Third'
}, {
data: [1, 2, 6],
name: 'Fourth'
}, {
data: [4, 6, 4],
name: 'Fifth'
}]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 400px;"></div>
Try to add this one:
legend: {
align: 'center',
verticalAlign: 'bottom',
x: 0,
y: 0
},
Hope this helps.

Categories

Resources