Related
I have been trying to create overlays with Plotly.js, e.g. Week over Week, Month over Month etc., and I am required to render a similar looking chart as below. I believe the candleStick chart can get me somewhat near to it, but just wanted to explore if anyone has got a better idea.
Year Over Year, chart sample
The candleStick plots are a good option indeed, but they are somewhat limited in Plotly (see below). Another one is to use fill between two curves, something that'd look like this:
Here is the code to produce this:
<html>
<head>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div class="item-graph" id="plotly_graph">
</div>
</body>
<script>
// Some random data
const date = ["2022/01/01", "2022/01/07", "2022/01/13", "2022/01/18", "2022/01/25", "2022/02/01", "2022/02/07", "2022/02/13", "2022/02/18", "2022/02/25"]
const new_date = date.map(function (d) { return new Date(d) })
const bluecurve = [450, 550, 460, 450, 530, 440, 340, 345, 290, 270]
const graycurve = [390, 410, 320, 490, 470, 380, 480, 410, 190, 310]
const min = [350, 430, 420, 410, 480, 350, 320, 310, 230, 190]
const max = [500, 600, 520, 490, 540, 500, 450, 390, 350, 360]
// Setting range of Forecast for hoverinfo
var hovertemplate = []
for (let i = 0; i < min.length; i++) {
hovertemplate.push(min[i] + ' - ' + max[i])
}
// Setting up the traces:
traces = [
{
x: new_date,
y: max,
// name: 'Forecast',
yaxis: 'y',
showlegend: false,
line: { width: 1, shape: 'hvh', color: "lightblue" },
hoverinfo: 'skip',
mode: 'lines',
},
{
x: new_date,
y: min,
name: 'Forecast',
yaxis: 'y',
showlegend: false,
line: { width: 1, shape: 'hvh', color: "lightblue" },
// hoverinfo: 'skip',
hovertemplate: hovertemplate,
mode: 'lines',
fillcolor: "lightblue",
fill: 'tonexty',
},
{
x: new_date,
y: bluecurve,
name: "This Year",
yaxis: 'y',
type: 'scatter',
showlegend: false,
mode: 'markers',
marker: {
size: 20,
color: "blue",
line: {
width: 2,
color: 'DarkSlateGrey'
},
},
},
{
x: new_date,
y: graycurve,
name: "Last Year",
yaxis: 'y',
type: 'scatter',
showlegend: false,
mode: 'markers',
marker: {
size: 20,
color: "lightgray",
line: {
width: 2,
color: 'DarkSlateGrey'
},
},
},
]
// Setting up layout
const layout = {
yaxis: {
rangemode: 'nonnegative',
range: [0, 700]
},
hovermode: "x unified",
};
// Creating the plots
Plotly.react("plotly_graph", traces, layout);
</script>
</html>
If you want to use candlesticks, you can change the layout and the first two elements on the traces to, respectively:
const layout= {
xaxis: {
type: 'date',
rangeslider: {
visible: false,
},
},
yaxis: {
rangemode: 'nonnegative',
range: [0,700]
},
hovermode: "x unified",
};
and
{
x: new_date,
low: min,
open: min,
high: max,
close: max,
decreasing: {line: {color: "lightblue" }},
increasing: {line: {color: "lightblue" }},
yaxis: 'y',
type: 'candlestick',
showlegend: false,
// text: hovertemplate,
hoverinfo: 'skip',
},
The hoverinfo of the candlesticks seem to always include high, low, open and close (I couldn't find a way to edit it, as text just adds to those, so I deactivated it). Here is how it looks like:
There's also no control over the width, apart from the number of sticks in the figure.
Is it possible to Draw this type of gauges in the left and right of the circle using Highcharts?
Basically left side gauge is representing the shorts' %age at left, the right side gauge is representing the shorts' %age at right, and the center is one simple round circle with the %age of center shots.
Series variable pie it's looks fit better for this case.
Tooltip we built setting useHTML:true, circle at the center we rendered by SVG renderer.
Highcharts.chart('container', {
chart: {
events: {
render() {
const chart = this;
if (!chart.customCenterCircle) {
chart.customCenterCircle = chart.renderer.circle(
chart.plotLeft + chart.plotSizeX / 2,
chart.plotTop + chart.plotSizeY / 2,
0
).add().toFront();
}
if (!chart.customCenterText) {
chart.customCenterText = chart.renderer.text('50%', 0, 0).css({
fontSize: '24px',
color: '#d4d4d4'
}).add().toFront();
}
chart.customCenterCircle.animate({
x: chart.plotLeft + chart.plotSizeX / 2,
y: chart.plotTop + chart.plotSizeY / 2,
r: chart.series[0].center[3] / 2,
fill: 'rgba(31, 112, 31, 0.5)',
stroke: 'rgba(154, 214, 154, 1)',
'stroke-width': 4
});
chart.customCenterText.attr({
x: chart.plotLeft + chart.plotSizeX / 2 -
chart.customCenterText.getBBox().width / 2,
y: chart.plotTop + chart.plotSizeY / 2 -
chart.customCenterText.getBBox().y -
chart.customCenterText.getBBox().height / 2
});
}
}
},
tooltip: {
backgroundColor: '#ffffff',
borderWidth: 0,
useHTML: true,
formatter: function() {
return `<p><h1 style="color:${this.color};">${this.y}</h1></br>${this.key}</p>`
}
},
series: [{
type: 'variablepie',
minPointSize: 0,
innerSize: '30%',
startAngle: 30,
dataLabels: {
enabled: false
},
borderWidth: 3,
zMin: 0,
data: [{
y: 80,
z: 100,
name: 'other',
color: 'black'
}, {
y: 30,
z: 100,
name: 'missed right',
color: 'red'
}, {
y: 50,
z: 0
}, {
y: 25,
z: 80,
name: 'missed right',
color: 'red'
}, {
y: 75,
z: 80,
name: 'other',
color: 'black'
}, {
y: 60,
z: 0,
name: 'other',
}]
}]
});
Live demo:
https://jsfiddle.net/BlackLabel/sc9vwzjt/2/
API References:
https://www.highcharts.com/docs/chart-and-series-types/variable-radius-pie-chart
https://api.highcharts.com/highcharts/series.variablepie
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer
I'm creating a line chart using Chart.js. The chart has two datasets. The x-axis is date, and y-axis is the value. When I hover the first data in dataset 1, the first data in dataset 2 is also active(zoom). That isn't I expected. Is there any way to active only the hovered data. And is there any way to active all data by their y value, not by index.
I've tried edit the tooltips mode but the result is not I expected. It shows the tooltips in difference dataset with same index.
https://www.chartjs.org/docs/latest/general/interactions/modes.html
var ctx = document.getElementById("myChart");
var barChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Dataset 1',
data: [{
x: '2019-01-15',
y: 100
}, {
x: '2019-02-03',
y: 300
}],
backgroundColor: 'rgba(0, 0, 0, 0)',
borderColor: 'rgba(255, 0, 0, 1)',
borderWidth: 1
}, {
label: 'Dataset 2',
data: [{
x: '2019-01-03',
y: 150
}, {
x: '2019-01-15',
y: 200
}, {
x: '2019-02-06',
y: 250
}],
backgroundColor: 'rgba(0, 0, 0, 0)',
borderColor: 'rgba(0, 255, 255, 1)',
borderWidth: 1
}],
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}],
xAxes: [{
type: 'time',
position: 'bottom',
time: {
min: "2019-1-1",
max: "2019-2-28",
unit: "month",
displayFormats: {
"month": "YYYY-MM",
}
}
}]
}
}
});
https://jsfiddle.net/t1gmrujo/2/
I've tried edit the tooltips mode but the result is not I expected. It shows the tooltips in difference dataset with same index.
The tooltip configuration controls display of the tooltip popup, not the dataset points. Points are controlled by the hover configuration. This is mentioned on the page you linked (emphasis mine):
When configuring interaction with the graph via hover or tooltips, a number of different modes are available.
If you only want the single point you are hovering over to be highlighted, use:
options: {
hover: {
mode: 'point'
}
}
If you want the whole dataset highlighted when hovering any single dataset point, use:
options: {
hover: {
mode: 'dataset'
}
}
If you want to highlight the dataset and see all dataset values in the tooltip, use:
options: {
hover: {
mode: 'dataset'
},
tooltips: {
mode: 'dataset'
}
}
I am trying to get the max value of the x axis and set an annotation just at the end:
y: chart.xAxis[0].max-1
What is the right syntax to get it working in react? See a live demo here.
You can not use values from the chart in options. You should add your annotations dynamically in addAnnotation method:
chart: {
events: {
load: function(){
const chart = this;
chart.addAnnotation({
labels: [
{
point: {
x: 3,
y: chart.xAxis[0].max - 1,
xAxis: 0,
yAxis: 0
},
text: "x: {x}<br/>y: {y}"
},
{
point: {
x: 0,
y: 0
},
text: "x: {point.plotX} px<br/>y: {point.plotY} px"
},
{
point: {
x: 5,
y: 100,
xAxis: 0
},
text: "x: {x}<br/>y: {point.plotY} px"
}
],
labelOptions: {
x: 40,
y: -10
}
})
}
}
}
Live demo: https://codesandbox.io/s/537kz8xwyx
I have extended a component and I can't get it to render to a panel. Seems like I am missing something pretty basic since the debugger in Chrome actually shows that the panel has the data, it just isn't showing up.
Here is the fiddle and here are parts of the code:
Custom Component:
Ext.define('StoplightControl', {
extend: 'Ext.draw.Component',
alias: 'widget.Stoplight',
constructor: function (config) {
this.initConfig(config);
this.callParent(arguments);
},
initComponent: function () {
var kpiData; // = Ext.data.StoreManager.get('KPIStore').getById(this.model.get('KPI_ID'));
if (typeof (kpiData) === 'undefined' || kpiData == null) {
kpiData = Ext.create('KPIModel', {
ControlBackground: '#000000',
Caution_label: 'On',
Good_label: 'Ahead',
Poor_label: 'Behind',
Good_color: '#00FF00',
Poor_color: '#ff0000',
Caution_color: '#550000',
Header: 'Test'
});
}
this.setGradients(kpiData.get('ControlBackground'));
this.drawItems(this.model, kpiData);
},
setGradients: function (controlColor) {
this.gradients = [{
id: 'middleGradient',
angle: 180,
stops: {
0: {
color: controlColor,
opacity: 1
},
50: {
color: controlColor,
opacity: .6
},
100: {
color: controlColor,
opacity: 1
}
}
}, {
id: 'lightGradient1',
angle: -90,
stops: {
0: {
color: '#ffffff',
opacity: 0.01
},
100: {
color: '#ffffff',
opacity: .8
}
}
}]
},
drawItems: function (model, kpiData) {
var cautionValueX = -22.5 * (model.get('cautionValue').toString().length) + 227.5,
goodValueX = -22.5 * (model.get('goodValue').toString().length) + 227.5,
poorValueX = -22.5 * (model.get('poorValue').toString().length) + 227.5,
maxLineLength = 15,
changeOfY = -50,
cautionLabel = linebreaks(kpiData.get('Caution_label'), maxLineLength),
goodLabel = linebreaks(kpiData.get('Good_label'), maxLineLength),
poorLabel = linebreaks(kpiData.get('Poor_label'), maxLineLength),
cautionChangeY = (cautionLabel.split("\n").length - 1) * changeOfY,
goodChangeY = (goodLabel.split("\n").length - 1) * changeOfY,
poorChangeY = (poorLabel.split("\n").length - 1) * changeOfY,
headerFontSize = '100px arial,sans-serif',
textFontSize = '80px arial,sans-serif';
var drawItems = [{
type: 'rect',
x: 1.6620979,
y: 52.362183,
radius: 90,
width: 448.10959,
height: 1000,
fill: 'url(#middleGradient)',
stroke: 'none'
}, {
type: "circle",
radius: 140,
x: 224,
y: 896,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Good_color')
}, {
type: "circle",
x: 224,
y: 214,
radius: 140,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Poor_color')
}, {
type: "circle",
x: 224,
y: 555,
radius: 140,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Caution_color')
}, {
type: "text",
id: "svg-stoplight-poorValue",
text: model.get('poorValue'),
x: poorValueX,
y: 220,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-cautionValue",
text: model.get('cautionValue'),
x: cautionValueX,
y: 560,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-goodValue",
text: model.get('goodValue'),
x: goodValueX,
y: 900,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-poorLabel",
text: poorLabel,
x: 500,
y: 220 + poorChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-cautionLabel",
text: cautionLabel,
x: 500,
y: 560 + cautionChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-goodLabel",
text: goodLabel,
x: 500,
y: 900 + goodChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-headerLabel",
text: kpiData.get('Header'),
x: 145,
y: -40,
fill: "Black",
font: headerFontSize
}, {
type: "text",
id: "svg-stoplight-totalLabel",
text: "Total = " + model.get('total'),
x: 100,
y: 1250,
fill: "Black",
font: textFontSize
}];
//don't add gradients if IE is > 10 or documentMode is less than 9
if (!(ie > 10 || document.documentMode < 9)) {
drawItems.push({
type: "ellipse",
id: 'test1',
radiusX: 112,
radiusY: 80,
x: 224,
y: 156,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 498,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 838,
fill: 'url(#lightGradient1)'
});
}
},
width: 210,
height: 250
});
Creation of the Panel and adding the component:
var displayPanel = Ext.create('widget.panel', {
width: 600,
height: 800,
title: 'Cost & Schedule Variance',
renderTo: 'WorkstreamStoplights',
pack: 'center',
shrinkWrap: 3,
layout: {
type: 'table',
column: 2
},
});
stoplightStore.each(function (model, idx) {
var stoplight = Ext.create('StoplightControl', {
model: model
});
displayPanel.add(stoplight);
});
displayPanel.doLayout();
As you'll be able to see from the fiddle, the Title displays properly and I have even added an item to the displayPanel on creation, but doing a .add() doesn't seem to have any effect even with the .doLayout()
Haven't dabbled in ExtJS for a long time, but the 4.2 doc states, for this method:
This method needs to be called whenever you change something on this
component that requires the Component's layout to be recalculated.
Ah, silly me. I needed to add items to my component. I needed to make the assignment in the initComponents of
this.items = drawItems;
Working fiddle