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

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>

Related

How to get radar chart coordinates using getValueForDistanceFromCenter with Chart.js?

I am experimenting with Chart.js to build radar charts. I mastered the basics (see basic chart below), but I would like to use the x y coordinates of the graph to place texts directly on the canvas.
After some digging, I found out that it is not possible to use getValueForPixel or getPixelForTick in a radar chart. See this github issue. In the connecting thread, a new method getValueForDistanceFromCenter is introduced.
As I understand it, it would be possible to calculate the distance from the center with this method, and use it to get coordinates. I searched the Chart.js documentation and other sites, but cannot find any code examples or information on how to implement this.
Can somebody point me in the right direction how to implement the method in the code?
var data = {
labels: ["Ball Skills", "Shooting", "Physical"],
datasets: [{
label: [`ikke`, `jij`],
backgroundColor: "rgba(38,120,255,0.2)",
borderColor: "rgba(38,120,255, 1)",
data: [90, 90, 90]
}]
};
var options = {
responsive: true,
tooltips: false,
title: {
text: 'Basic example',
display: true,
position: `bottom`,
},
scale: {
angleLines: {
display: true
},
ticks: {
suggestedMin: 0,
suggestedMax: 100,
stepSize: 25,
maxTicksLimit: 11,
display: false,
}
},
legend: {
labels: {
padding: 10,
fontSize: 14,
lineHeight: 30,
},
},
};
var myChart = new Chart(document.getElementById("chart"), {
type: 'radar',
data: data,
options: options
});
The radialLinear scale (in version 2.9.4 that I have seen your are using version 2) there is the method getValueForDistanceFromCenter(value) to get the distance from center but there is another method getPointPositionForValue(index, value) which can provide you the point at a specif index of your data.
To use them and to draw what you want on chart using those points, you need to implement a plugin.
In the below snippet, I'm drawing a rect between the points at a specific value.
const ctx = document.getElementById("myChart");
const data = {
labels: ["Ball Skills", "Shooting", "Physical"],
datasets: [{
label: [`ikke`, `jij`],
backgroundColor: "rgba(38,120,255,0.2)",
borderColor: "rgba(38,120,255, 1)",
data: [50, 50, 50]
}]
};
const options = {
responsive: true,
tooltips: false,
title: {
text: 'Basic example',
display: true,
position: `bottom`,
},
scale: {
angleLines: {
display: true
},
ticks: {
suggestedMin: 0,
suggestedMax: 100,
stepSize: 25,
maxTicksLimit: 11,
display: false,
}
},
legend: {
labels: {
padding: 10,
fontSize: 14,
lineHeight: 30,
},
},
};
const plugin = {
id: 'getDistance',
afterDraw(chart) {
const c = chart.ctx;
const rScale = chart.scale;
c.save();
chart.data.datasets[0].data.forEach(function(item, index) {
const point = rScale.getPointPositionForValue(0.5 + index, 50);
c.beginPath();
c.fillStyle = 'red';
c.fillRect(point.x - 5, point.y - 5, 10, 10);
c.fill();
});
c.restore();
}
};
const myChart = new Chart(ctx, {
type: 'radar',
plugins: [plugin],
data: data,
options: options
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.4/dist/Chart.min.js"></script>
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"/>
</div>
</body>
</html>

How can I change the position of legends in chart.js?

I use chart.js for creating this chart:
I want to change the position of two legends( legend1 and legend2), but it did not work!
NOTE: all features work correctly, but problem is that I can't change position of legends:
the version of CDN chart.js :
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
I can't understand, what is the problem? Is it because of it's CDN package version?
my pure js code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
</head>
<body>
<canvas id="myChart" width="778" height="433"></canvas>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
data: {
labels: ['۱۴۰۰/۰۱/۰۱', '۱۴۰۰/۰۱/۰۲', '۱۴۰۰/۰۱/۰۳', '۱۴۰۰/۰۱/۰۴', '۱۴۰۰/۰۱/۰۵', '۱۴۰۰/۰۱/۰۶'],
datasets: [{
type: 'line',
label: 'legend1',
data: [6, 5.5, 4, 7, 5.4, 5.8],
fill:false,
borderColor: 'rgb(14, 156, 255)',
tension:0 },
{
type: 'line',
label: 'legend2',
data: [6.2, 5.7, 3.8, 7.2, 5.2, 5.9],
fill:false,
borderColor: 'rgb(22, 185, 156)',
tension:0
} ]
},
options: {
legend: {
position: 'bottom',
}, // end: legend
scales: {
y: {
beginAtZero: true,
display: false }// end: y
,x: {
grid: {
borderDash: [6, 5],
lineWidth: 1,
color:'#CCCCCC' }// end grid
}//end:x
} // end: scales
} //end options
});
</script>
</body>
</html>
I believe it needs to be set under the plugins property in options, like
options: {
plugins: { /// PUT LEGEND UNDER THIS PROP
legend: {
position: 'bottom',
}
},
scales: {
y: {
beginAtZero: true,
display: false }// end: y
,x: {
grid: {
borderDash: [6, 5],
lineWidth: 1,
color:'#CCCCCC' }// end grid
}//end:x
} // end: scales
} //end options

Want to reduce padding/space between Plotly.JS heatmap and text from another div - need CSS

I have a Plotly.JS heatmap that's working great and I have some descriptive text I want to put above it in a separate div, however the gap looks a bit awkward. Somehow I am struggling to reduce the padding so that my text (e.g., My description) and the heatmap and closer together. I tried using CSS, but I am clearly off. I'm sure this is an easy answer and that I'm overlooking something simple. Any help is appreciated.
Codepen: https://codepen.io/tenebris_silentio/pen/eYzgZMw
<!DOCTYPE html>
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<br>
My description.
<div id='myDiv'><br> </div>
</div>
</div><script>
var colorscaleValue = [
[0, '#ADD8E6'],
[1, '#000080']
];
var data = [
{
z: [[41, 60, 25, 24, 28], [56, 27, 14, 45, 17], [47, 17, 12, 47, 17]],
x: ['Geographical', 'Temporal', 'Cultural', 'Digital', 'Financial'],
y: ['Category 1', 'Category 2', 'Category 3'],
colorscale: colorscaleValue,
type: 'heatmap',
hovertemplate: '<b># of %{y} Projects with a %{x} classification</b>: %{z}' + '<extra></extra>',
hoverongaps: true
}
];
var layout = {
title: '',
width: 900,
height: 560,
autosize: false,
yaxis: {automargin: true}
};
Plotly.newPlot('myDiv', data, layout);
</script><!-- Plotly chart will be drawn inside this DIV --></div></div></div>
<style>
.myDiv{
padding: -1px;
}
</style>
You can use the margin configuration in your layout:
var layout = {
// ...
margin: {
t: 10
},
// ...
<!DOCTYPE html>
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<br>
My description.
<div id='myDiv'><br> </div>
</div>
</div><script>
var colorscaleValue = [
[0, '#ADD8E6'],
[1, '#000080']
];
var data = [
{
z: [[41, 60, 25, 24, 28], [56, 27, 14, 45, 17], [47, 17, 12, 47, 17]],
x: ['Geographical', 'Temporal', 'Cultural', 'Digital', 'Financial'],
y: ['Category 1', 'Category 2', 'Category 3'],
colorscale: colorscaleValue,
type: 'heatmap',
hovertemplate: '<b># of %{y} Projects with a %{x} classification</b>: %{z}' + '<extra></extra>',
hoverongaps: true
}
];
var layout = {
title: '',
width: 900,
height: 560,
autosize: false,
margin: {
t: 10
},
yaxis: {automargin: true}
};
Plotly.newPlot('myDiv', data, layout);
</script><!-- Plotly chart will be drawn inside this DIV --></div></div></div>
<style>
.myDiv{
padding: -1px;
}
</style>
Why not add the text in the layout itself?
var layout = {
"title": {"text": "My description."},
...
};
Because Plotly.JS is generating a SVG its not easy to style it. A solution is to style a element with position absolute so you can make it "overlap" with the SVG.
For example:
var colorscaleValue = [
[0, '#ADD8E6'],
[1, '#000080']
];
var data = [
{
z: [[41, 60, 25, 24, 28], [56, 27, 14, 45, 17], [47, 17, 12, 47, 17]],
x: ['Geographical', 'Temporal', 'Cultural', 'Digital', 'Financial'],
y: ['Category 1', 'Category 2', 'Category 3'],
colorscale: colorscaleValue,
type: 'heatmap',
hovertemplate: '<b># of %{y} Projects with a %{x} classification</b>: %{z}' + '<extra></extra>',
hoverongaps: true
}
];
var layout = {
title: '',
width: 900,
height: 560,
autosize: false,
yaxis: {automargin: true}
};
Plotly.newPlot('myDiv', data, layout);
</script><!-- Plotly chart will be drawn inside this DIV --></div></div></div>
.myDiv{
padding: -1px;
}
p {
position: absolute;
z-index: 10;
margin-top: 45px;
margin-left: 80px;
}
<p>My description.</p>
<div id='myDiv'></div>
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>

vis.js large nodes overlapping in hierarchical mode

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'
},
}
};

Double donut-like chart with negative values

I've been asked to do this kind of graph (40,9% and 16,4% are examples, they should indicate something like -6% and 9%):
Any idea on how I can get that kind of result, using a javascript library, if possible (but it is not a must) Highcharts?
Thanks
It's possible with HighCharts, Documentation
e.g.
$(function () {
data = [{
valSecond: 25,
valFirst: 62.5
}];
// Build the data arrays
var secondData = [];
var firstData = [];
for (var i = 0; i < data.length; i++) {
// add second data
secondData.push({
name: "Second",
y: data[i].valSecond,
color: "#00FF00"
});
// add first data
firstData.push({
name: "First",
y: data[i].valFirst,
color:'#FF0000'
});
}
// Create the chart
$('#container').highcharts({
chart: {
type: 'pie'
},
title: {
text: ''
},
plotOptions: {
pie: {
animation: false,
shadow: false,
center: ['50%', '50%']
}
},
tooltip: {
valueSuffix: '%'
},
series: [{
name: 'second',
data: secondData,
size: '30%',
startAngle: 270,
endAngle: 360,
innerSize: '20%'
}, {
name: 'first',
color:'#FFFFFF',
data: firstData,
size: '80%',
startAngle: 0,
endAngle: 225,
innerSize: '60%',
}]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="width: 600px; height: 400px; margin: 0 auto"></div>
Jsfiddle
In the highcharts you can adapt donut chart http://www.highcharts.com/demo/pie-donut, remove connectors, set useHTML for dataLabels and rotate by css / rotation SVG element. Missing elements can by added by renderer.

Categories