I am converting Google Chart confidence band (line) to Apache Echarts and I have a major issue when using areaStyle on confidence-band stacking in the line charts with null values.
The issue arises only when having null values in the min/max lines. The connectNulls seems to not be affecting the areaStyle, where the actual line is drawn. Also, the option for skipping xAxis values where the yAxis values are nulls is not an option since in some cases, multiple confidence bands sets will be show (as seen in the second screenshot).
Here is a screenshot of the issue
And here is a screenshot of how it should look like
Here is a simplified data and options:
var dataPoints = [
{
"date": "1988",
"value": null,
"max": null,
"min": null
},
{
"date": "1993",
"value": null,
"max": null,
"min": null
},
{
"date": "1994",
"value": -25.06951746031746,
"max": -4.489982345190423,
"min": -45.6490525754445
},
{
"date": "1995",
"value": null,
"max": null,
"min": null
},
{
"date": "1996",
"value": null,
"max": null,
"min": null
},
{
"date": "1997",
"value": null,
"max": null,
"min": null
},
{
"date": "1998",
"value": -22.481579457241587,
"max": -13.261933619688394,
"min": -31.70122529479478
},
{
"date": "1999",
"value": null,
"max": null,
"min": null
},
{
"date": "2001",
"value": null,
"max": null,
"min": null
},
{
"date": "2002",
"value": -15.762091749175111,
"max": -7.987435161930629,
"min": -23.536748336419592
},
{
"date": "2004",
"value": null,
"max": null,
"min": null
},
{
"date": "2005",
"value": -15.991292690156723,
"max": -10.553503467629257,
"min": -21.42908191268419
},
{
"date": "2006",
"value": -15.571244626678256,
"max": -10.249573325928019,
"min": -20.892915927428493
},
{
"date": "2008",
"value": null,
"max": null,
"min": null
},
{
"date": "2009",
"value": -15.220561608383338,
"max": -10.272665580901526,
"min": -20.168457635865153
},
{
"date": "2010",
"value": -14.663968598988458,
"max": -9.842327372229358,
"min": -19.48560982574756
},
{
"date": "2011",
"value": -12.819286135182983,
"max": -7.97250885239146,
"min": -17.666063417974506
},
{
"date": "2012",
"value": null,
"max": null,
"min": null
},
{
"date": "2014",
"value": null,
"max": null,
"min": null
},
{
"date": "2015",
"value": null,
"max": null,
"min": null
},
{
"date": "2016",
"value": null,
"max": null,
"min": null
},
{
"date": "2017",
"value": -13.945108519331527,
"max": -9.216373427532616,
"min": -18.673843611130437
},
{
"date": "2018",
"value": null,
"max": null,
"min": null
},
{
"date": "2019",
"value": -13.384577319672625,
"max": -9.017162823138271,
"min": -17.751991816206978
}
];
option =
{
grid: {
containLabel: true,
},
tooltip: {
trigger: 'axis',
backgroundColor: '#38393c',
textStyle: {
color: "#FFF",
},
},
legend: {
show: true,
top: 'top',
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dataPoints.map(function (item) {
return item.date;
}),
},
yAxis: {
},
series: [
{ //Max Line
name: 'Max',
type: 'line',
data: dataPoints.map(function (item) {
return item.max;
}),
lineStyle: {
opacity: 1,
},
areaStyle: {
color: 'transparent',
opacity: 1,
},
stack: 'confidence-band',
stackStrategy: 'all',
symbol: 'none',
connectNulls: true,
},
{ //Min Line
name: 'Min',
type: 'line',
data: dataPoints.map(function (item) {
return item.min - item.max;
}),
lineStyle: {
opacity: 1,
},
areaStyle: {
color: 'red',
opacity: 0.5,
},
stack: 'confidence-band',
stackStrategy: 'all',
symbol: 'none',
connectNulls: true,
},
{ //Value Line
name: 'Mean Values',
type: 'line',
data: dataPoints.map(function (item) {
return item.value;
}),
itemStyle: {
color: 'teal',
},
showSymbol: false,
connectNulls: true,
},
],
};
Here is a link to the echarts with reproducible example
Thanks!
I think this possibly qualifies as a bug in echarts - it fails to work with null
s in stacked data. It boils down to this if statement that results in a replacement of null
to zero in polygons, shapes that get filled if areaStyle
is enabled. Still, given the complexity of the code, I don't know whether that replacement was not necessary for some other type of plot.
In the code below I made a patch that filters out NaN values from polygons produced by LineView.prototype._newPolygon
; I prepended it to your code. It appears to do the job, but this is a very hacky solution. I would recommend considering taking out the null
values, or even interpolating for the missing data.
const LineView = echarts.ChartView.getClass('line');
const _newPolygon = LineView.prototype._newPolygon;
LineView.prototype._newPolygon = function(points, stackedOnPoints){
const polygon = _newPolygon.call(this, points, stackedOnPoints);
if(points && stackedOnPoints){
const setShape = polygon.setShape;
polygon.setShape = function(keyOrObj, value){
const _poly = setShape.call(polygon, keyOrObj, value);
const points = polygon.shape.points,
stackedOnPoints = polygon.shape.stackedOnPoints,
points2 = [], stackedOnPoints2 = [];
let foundNaN = false;
for(let i = 0; i < points.length/2; i++){
if(!isNaN(points[2*i+1]) && !isNaN(stackedOnPoints[2*i+1])){
points2.push(points[2*i], points[2*i+1]);
stackedOnPoints2.push(stackedOnPoints[2*i], stackedOnPoints[2*i+1]);
}
else{
foundNaN = true;
}
}
if(foundNaN){
polygon.shape.points = new Float32Array(points2);
polygon.shape.stackedOnPoints = new Float32Array(stackedOnPoints2);
}
return _poly;
}
}
return polygon;
};
var dom = document.getElementById('chart-container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
var dataPoints = [
{
"date": "1988",
"value": null,
"max": null,
"min": null
},
{
"date": "1993",
"value": null,
"max": null,
"min": null
},
{
"date": "1994",
"value": -25.06951746031746,
"max": -4.489982345190423,
"min": -45.6490525754445
},
{
"date": "1995",
"value": null,
"max": null,
"min": null
},
{
"date": "1996",
"value": null,
"max": null,
"min": null
},
{
"date": "1997",
"value": null,
"max": null,
"min": null
},
{
"date": "1998",
"value": -22.481579457241587,
"max": -13.261933619688394,
"min": -31.70122529479478
},
{
"date": "1999",
"value": null,
"max": null,
"min": null
},
{
"date": "2001",
"value": null,
"max": null,
"min": null
},
{
"date": "2002",
"value": -15.762091749175111,
"max": -7.987435161930629,
"min": -23.536748336419592
},
{
"date": "2004",
"value": null,
"max": null,
"min": null
},
{
"date": "2005",
"value": -15.991292690156723,
"max": -10.553503467629257,
"min": -21.42908191268419
},
{
"date": "2006",
"value": -15.571244626678256,
"max": -10.249573325928019,
"min": -20.892915927428493
},
{
"date": "2008",
"value": null,
"max": null,
"min": null
},
{
"date": "2009",
"value": -15.220561608383338,
"max": -10.272665580901526,
"min": -20.168457635865153
},
{
"date": "2010",
"value": -14.663968598988458,
"max": -9.842327372229358,
"min": -19.48560982574756
},
{
"date": "2011",
"value": -12.819286135182983,
"max": -7.97250885239146,
"min": -17.666063417974506
},
{
"date": "2012",
"value": null,
"max": null,
"min": null
},
{
"date": "2014",
"value": null,
"max": null,
"min": null
},
{
"date": "2015",
"value": null,
"max": null,
"min": null
},
{
"date": "2016",
"value": null,
"max": null,
"min": null
},
{
"date": "2017",
"value": -13.945108519331527,
"max": -9.216373427532616,
"min": -18.673843611130437
},
{
"date": "2018",
"value": null,
"max": null,
"min": null
},
{
"date": "2019",
"value": -13.384577319672625,
"max": -9.017162823138271,
"min": -17.751991816206978
}
];
option =
{
grid: {
containLabel: true,
},
tooltip: {
trigger: 'axis',
backgroundColor: '#38393c',
textStyle: {
color: "#FFF",
},
},
legend: {
show: true,
top: 'top',
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dataPoints.map(function (item) {
return item.date;
}),
},
yAxis: {
},
series: [
{ //Max Line
name: 'Max',
type: 'line',
data: dataPoints.map(function (item) {
return item.max;
}),
lineStyle: {
opacity: 1,
},
areaStyle: {
color: 'transparent',
opacity: 1,
},
stack: 'confidence-band',
stackStrategy: 'all',
symbol: 'none',
connectNulls: true,
},
{ //Min Line
name: 'Min',
type: 'line',
data: dataPoints.map(function (item) {
return item.min - item.max;
}),
lineStyle: {
opacity: 1,
},
areaStyle: {
color: 'red',
opacity: 0.5,
},
stack: 'confidence-band',
stackStrategy: 'all',
symbol: 'none',
connectNulls: true,
},
{ //Value Line
name: 'Mean Values',
type: 'line',
data: dataPoints.map(function (item) {
return item.value;
}),
itemStyle: {
color: 'teal',
},
showSymbol: false,
connectNulls: true,
},
],
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
window.addEventListener('resize', myChart.resize);
#chart-container {
position: relative;
height: 100vh;
overflow: hidden;
}
<script src="https://fastly.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<div id="chart-container"></div>