chartshighchartsbar-chart

How to show overlapped values properly under Highchart?


I have implement a large amount of code to display highchart

Below is the full code

my JS Code

 const getBenchmarkChartSettings = (model, isEpd) => {

    let isStrScore = model.isStrScore;
    let metricSeries = reorderMetricSeries(model.series.metricSeries);
    let generatedSeries = model.series.generatedSeries;
    let benchmarkSeries = model.series.benchmarkSeries;
    let pitSeries = getPitSeries(model);

    _.forEach(metricSeries, s => { s.color = s.prop ? propColor : metricColor; });
    _.forEach(generatedSeries, s => { s.color = genColor; });
    _.forEach(benchmarkSeries, (s, i) => {
        s.color = colors[i];
        s.type = 'spline';
        if (s.benchmarkLegend && s.benchmarkType) {
            s.name = s.benchmarkLegend;
        }
        _.forEach(s.data, d => {
            d.x = new Date(d.dataDate).getTime();
            d.y = isStrScore ? defaultscores.indexOf(d.value) : parseFloat(d.value);
        });
    });

    _.forEach(metricSeries, e => { e.minPointLength = 6; e.type = 'column'; });
    _.forEach(generatedSeries, e => { e.minPointLength = 6; e.type = 'column'; });

    let seriesRaw = _.union(metricSeries, generatedSeries, benchmarkSeries, pitSeries);
    let series = _.filter(seriesRaw, { 'errorMessage': null });

    let shiftedSeries = _.map(series, ser => _.assign({}, ser, {
        data: _.map(ser.data, d => _.assign({}, d, { x: d.x }))
    }));

    let allDefaultScore = isStrScore && _(shiftedSeries).flatMap('data').flatMap('value').value()
            .every(val => val === 'd');

    _.forEach(shiftedSeries, e => {
        // allow for series longer than 1000 elements
        e.turboThreshold = 0;
        _.forEach(e.data, d => {
            if (!d.isPit) {
                d.x = new Date(d.dataDate).getTime();
                d.y = isStrScore ? 
                    (allDefaultScore ? defaultCompanyScores.indexOf(d.value) : defaultscores.indexOf(d.value)) 
                    : parseFloat(d.value);
            }
        });
    });

    const getShiftedSeries = (shiftedSeries, date) => {
        let maxYear = new Date(date).getFullYear();
        let updatedShiftedSeries = _.map(shiftedSeries, o => {
            if (!o.isPit) {
                let sdata = o.data.filter(itm => new Date(itm.x).getFullYear() >= maxYear);
                o.data = sdata;
                if (sdata.length > 0) { return o; }
                return null;
            }
            return o;
        });
        updatedShiftedSeries = _.without(updatedShiftedSeries, undefined || null);
        return updatedShiftedSeries;
    };

    let maxDate = _.chain(shiftedSeries)
        .map(o => _.maxBy(o.data, 'x'))
        .filter(e => e !== undefined)
        .map('x')
        .max()
        .value();

    if (pitSeries) {
        let pitAdjSeries = _.filter(shiftedSeries, { 'isPit': true });
        pitAdjSeries[0].data[0].x = maxDate;
    }

    let minDate = _.chain(shiftedSeries)
        .map(o => _.minBy(o.data, 'x'))
        .filter(e => e !== undefined)
        .map('x')
        .min()
        .value();

    let dateBuffer = ((maxDate - minDate) * 0.1);
    shiftedSeries = getShiftedSeries(shiftedSeries, minDate);

    let hasOnlyFewStatements = _.filter(shiftedSeries, e => 
                                    (e.name.indexOf('CIQ Data') > -1 || e.name.indexOf('Prop Data') > -1))
                                .reduce((dataPoints, { data }) => dataPoints.concat(data), [])
                                .length < 3;

    let chartSettings = {
        chart: {
            width: 220,
            height: 135,
            spacingLeft: 0,
            marginLeft: !isStrScore ? 25 : 12,
            marginRight: 1,
            backgroundColor: isEpd ? '#f2f2f2' : 'transparent',
            plotBorderColor: '#9A9A9A',
            plotBorderWidth: 1
        },
        title: {
            text: null
        },
        credits: {
            enabled: false
        },
        legend: {
            align: 'left',
            layout: 'horizontal',
            verticalAlign: 'top',
            alignColumns: false,
            itemDistance: 0,
            symbolHeight: 10,
            symbolWidth: 6,
            padding: 0,
            width: 200,
            symbolRadius: 0,
            margin: 7,
            itemMarginTop: 2,
            itemStyle: {
                fontSize: '4px'
            },
            y: -5,
            x: !isStrScore ? 25 : 10
        },
        xAxis: [
            {
                type: 'datetime',
                dateTimeLabelFormats: {
                    millisecond: '%b-%d-%Y',
                    second: '%b-%d-%Y',
                    minute: '%b-%d-%Y',
                    hour: '%b-%d-%Y',
                    day: '%b-%Y',
                    week: '%b-%Y',
                    month: '%b-%d-%Y',
                    year: '%Y'
                },
                labels: {
                    rotation: 0,
                    style: {
                        fontSize: '4px',
                        color: '#000'
                    },
                    y: 7,
                    x: isEpd ? -5 : !isStrScore ? 0 : (hasOnlyFewStatements ? 0 : -10)
                },
                min: minDate - dateBuffer,
                max: maxDate + dateBuffer,
                marginRight: 10,
                gridLineColor: 'white',
                gridLineWidth: 1,
                lineWidth: 0.5,
                minorGridLineWidth: 0,
                lineColor: '#808080',
                minorTickLength: 0,
                tickLength: 0,                
                tickInterval: isEpd ? (364 * 24 * 60 * 60000) : (maxDate - minDate) > 32000000000 ? undefined : 
                        (hasOnlyFewStatements ? undefined : 8000000000),
                align: 'left'
            }
        ],
        yAxis: [
            {
                gridLineWidth: 0,
                lineWidth: 0.5,
                lineColor: '#808080',
                minRange: 0,
                min: 0,
                ceiling: 100,
                tickAmount: isStrScore ? undefined : 5,
                labels: {
                    style: {
                        fontSize: '4px',
                        color: 'black'
                    },
                    x: -2,
                    y: isStrScore ? 7 : 3
                },
                title: {
                    text: !isStrScore ? '(PD%)' : '',
                    align: 'middle',
                    margin: 0,
                    x: -2,
                    rotation: -90,
                    ceiling: 100,
                    style: {
                        fontSize: '5px',
                        color: 'black'
                    },
                    labels: {
                        style: {
                            fontWeight: 'bold',
                            color: 'black',
                            fontSize: '2px'
                        },
                        x: -3
                    }
                }
            }
        ],
        plotOptions: {
            column: {
                maxPointWidth: 10
            },
            series: {
                states: {
                    hover: {
                        enabled: false
                    },
                    inactive: {
                        opacity: 1
                    }
                },
                enableMouseTracking: false,
                borderWidth: 1,
                borderColor: '#FFFFFF',
                threshold: 0,
                lineWidth: 1.5,
                marker: {
                    enabled: false
                },
                minPointLength: 10,
                dataLabels: {
                    enabled: true,
                    style: {
                        fontSize: '4px',
                        color: 'black',
                        textOutline: false
                    },
                    y: isLabelAdjustmentRequired(metricSeries, benchmarkSeries) ? -2 : undefined
                    
                }
            }
        },
        series: shiftedSeries?.map(item => ({
            ...item,
            data: item?.data.sort((a, b) => new Date(a.dataDate || null) - new Date(b.dataDate || null))
        })) || []
    };

    if (isStrScore) {
        chartSettings.yAxis[0].tickInterval = 4;
    }

    _.forEach(chartSettings.series, e => {
        if (e.type === 'column' || e.isPit) {
            e.dataLabels = { 
                padding: 1, 
                allowOverlap:true, 
                format: '<span style=\'font-size:4px;\'>{point.formattedValue}</span>' };
        }
        else if (e.type === 'spline') {
            e.dataLabels = { enabled: false };
        }
    });

    return chartSettings;
    }

enter image description here

Below is the code, with this code higchart is displaying the overlapped values on chart

     _.forEach(chartSettings.series, e => {
        if (e.type === 'column' || e.isPit) {
            e.dataLabels = { 
                padding: 1, 
                allowOverlap:true, 
                format: '<span style=\'font-size:4px;\'>{point.formattedValue}</span>'    };
          }
        else if (e.type === 'spline') {
            e.dataLabels = { enabled: false };
        }
      });

How to manage value displaying under chart shows proper, I have tried doing allowOverlap:false but it hide one of the value, even tried many thing but not able to get proper result like displaying values differently on chart.

Even tried finding answer on copilot and ChatGPT but still not get proper answer, the changes they ask me to do doesn't helped me.

MY data is coming something like below

0 = {
  data: (2)[{… }, {… }]
  0: {
    formattedValue: '9.36%'
  value: '9.36062867480189400'
  x: 1640889000000
  y: 9.360628674801895
  }
  
  1: {
    formattedValue: '9.23%'
  value: '9.23670432063350600'
  x: 1672425000000
  y: 9.236704320633507
    [[Prototype]]: Object
  }
}

1 = {
  data: (1)[{… }]
  0:
  {
    formattedValue: '9.23%'
  value: '9.23670432063350600'
  x: 1672425000000
  y: 9.236704320633507
  }
}

Here x and y axis for value 9.23 is coming same and because of that my data is displaying on same place, I have tried adding rotation like rotation: -45, and I am getting chart display as

enter image description here

But I dont want my value to rotate

then after I tried updating logic like below, but top:10px is not applying to my chart value

let previousX = null;
let previousY = null;

_.forEach(chartSettings.series, e => {
    if (e.type === 'column' || e.isPit) {
        _.forEach(e.data, point => {
            let formatString = '<span style="font-size:4px;">{point.formattedValue}</span>';
            if (previousX === point.x && previousY === point.y) {
                formatString = '<span style="font-size:4px; position: relative; top: 10px;">{point.formattedValue}</span>'; // Adjust top position as needed
            }
            point.dataLabels = {
                allowOverlap: true,
                format: formatString
            };
            previousX = point.x;
            previousY = point.y;
        });
    } 
    else if (e.type === 'spline') {
        e.dataLabels = { enabled: false };
    }
});

Solution

  • I have tried putting y which made to value top and then after start displaying label properly, matched the x and y axis with previous 1 if they matched it will set y as -6 which shift the label at top.

    let previousX = null;
    let previousY = null;
    _.forEach(chartSettings.series, e => {
        if (e.type === 'column' || e.isPit) {
            _.forEach(e.data, point => {                
                let yOffset = undefined;
                if (previousX === point.x && previousY === point.y) {
                    yOffset = -6;
                }
                point.dataLabels = {
                    allowOverlap: true,
                    format: '<span style=\'font-size:4px;\'>{point.formattedValue}</span>',
                    y: yOffset
                };
                previousX = point.x;
                previousY = point.y;
            });
        }
        else if (e.type === 'spline') {
            e.dataLabels = { enabled: false };
        }
    });