Search code examples
phptypescripthighchartspie-chartdonut-chart

Offset Highcharts datalables in donut chart with inner chart


I'm struggling with an issue that is haunting me, and i can't seem to find any fix in the Highcharts documentation.

So i have this Donut Pie Chart, which is showing the amount of money spend on 3 sales channels (B2B, B2C, Normal sale) - That is what the inner circle contains. Then the outer circle contains which type it is (Stock sale or Pre sale) - that indicates how much of the sale is on either type.

The thing is. Whenever any sales channel has no sale on a type (Eg. Stock: 2500 EUR / Pre: 0). The datalables stack ontop of each other, so it's impossible to make out what's what.

See attached image.

Highcharts donut example

Then i have function that removes the point, since theres no need to show a 0 on pre or stock.

const filterEmptyData = (data: Highcharts.PointOptionsObject[]) => {
   return data.filter(point => point.y !== 0);
};

I would like to offset the datalabels from each other to make out what's what.

Here's the code for the Pie chart

private async initPieCharts(strCurrencyCode: string, oGraphData: GraphData) {
        const fnFormatter = function () {
            return `${this.point.name}: ${number_format(this.point.percentage, 2)} % <br> Amount: ${number_format(this.point.y, 0)} ${strCurrencyCode}`;
        }

        const filterEmptyData = (data: Highcharts.PointOptionsObject[]) => {
            return data.filter(point => point.y !== 0);
        };

        let arrPies: Highcharts.Chart[] = [];

        let oTurnOverPie = Highcharts.chart(document.getElementById('turnoverPieChart')!, <Highcharts.Options>{
            chart: {
                plotShadow: false,
                type: 'pie',
                backgroundColor: '#FCFCFC',
                height: 436,
                events: {
                    click: function () {
                        $('#turnoverPieChart').siblings().highcharts()
                    }
                },
            },
            navigation: {
                buttonOptions: {
                    enabled: false
                }
            },
            title: {
                text: oGraphData.oTurnoverGraphData.strTitle,
                style: {
                    color: '#3F3F3F'
                }
            },
            subtitle: {
                text: 'Percentage reflects the size of the pie cutout',
                style: {
                    color: '#3F3F3F'
                },
            },
            tooltip: {
                formatter: fnFormatter
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: 'pointer',
                    animation: false,
                    dataLabels: {
                        color: '#333333',
                        enabled: true,
                        connectorColor: 'black',
                        formatter: fnFormatter,
                    }
                }
            },
            series: [<Highcharts.SeriesPieOptions>{
                name: 'Percentage',
                size: '40%',
                data: filterEmptyData(oGraphData.oTurnoverGraphData.arrData)
            }, <Highcharts.SeriesPieOptions>{
                name: 'OrderType',
                size: '50%',
                innerSize: '80%',
                data: filterEmptyData(oGraphData.oTurnoverGraphData.arrOrderTypeData)
            }],
        });

        arrPies.push(oTurnOverPie);

        let oMarginPie = Highcharts.chart(document.getElementById('marginPieChart')!, <Highcharts.Options>{
            chart: {
                plotShadow: false,
                type: 'pie',
                backgroundColor: '#FCFCFC',
                height: 436,
            },
            navigation: {
                buttonOptions: {
                    enabled: false
                }
            },
            title: {
                text: oGraphData.oMarginGraphData.strTitle,
                style: {
                    color: '#3F3F3F'
                }
            },
            subtitle: {
                text: 'Percentage reflects the size of the pie cutout',
                style: {
                    color: '#3F3F3F'
                },
            },
            tooltip: {
                formatter: fnFormatter
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: 'pointer',
                    animation: false,
                    dataLabels: {
                        color: '#333333',
                        enabled: true,
                        connectorColor: 'black',
                        formatter: fnFormatter,
                    }
                }
            },
            series: [<Highcharts.SeriesPieOptions>{
                name: 'Percentage',
                size: '40%',
                data: filterEmptyData(oGraphData.oMarginGraphData.arrData)
            }, <Highcharts.SeriesPieOptions>{
                name: 'OrderType',
                size: '50%',
                innerSize: '80%',
                data: filterEmptyData(oGraphData.oMarginGraphData.arrOrderTypeData)
            }],
        });

        arrPies.push(oMarginPie);

        initPiePiece(arrPies);
    }

Thanks :)

Tried using the distance and padding to offset but it just moves both datalables.


Solution

  • You've probably tried to place these settings in plotOptions.series, which essentially applies the same properties to all series. If you prefer individual settings for each series, you should apply them directly to each of series as follows:

    Highcharts.chart('container', {
      chart: {
        type: 'pie',
      },
    
      series: [{
        dataLabels: {
          enabled: true,
          distance: 100,
          connectorShape: 'straight',
          connectorPadding: 0,
          padding: 0,
          y: -10
        },
        name: 'Percentage',
        size: '40%',
        data: [{
          name: 'Chrome',
          y: 70.67,
          sliced: true,
          selected: true
        }, {
          name: 'Edge',
          y: 14.77
        }]
      }, {
        dataLabels: {
          enabled: true,
          distance: 10,
          connectorPadding: 0,
        },
        name: 'OrderType',
        size: '50%',
        innerSize: '80%',
        data: [{
          name: 'Chrome',
          y: 70.67,
          sliced: true,
          selected: true
        }, {
          name: 'Edge',
          y: 14.77
        }, ]
      }],
    });
    

    Demo: https://jsfiddle.net/BlackLabel/3tfboxc8/