[Chartjs]-2 layer doughnut chart using Chart.js

1👍

Please take a look at below runnable code and see how it could be done.

When it comes to the legend, you’ll probably have to implement a solution as explained in this answer.

let data = [
  {city: "Budapest", country: "Hungary"},
  {city: "Shenzen", country: "China"},
  {city: "Beijing", country: "China"},
  {city: "Shenzen", country: "China"},
  {city: "Istanbul", country: "Turkey"},
  {city: "Ho Chi Minh", country: "Vietnam"},
  {city: "Shenzen", country: "China"},
  {city: "Debrecen", country: "Hungary"},
  {city: "Budapest", country: "Hungary"},
  {city: "Shenzen", country: "China"},
  {city: "Shenzen", country: "China"},
  {city: "Shenzen", country: "China"},
  {city: "Istanbul", country: "Turkey"},
  {city: "Budapest", country: "Hungary"},
  {city: "Beijing", country: "China"},
  {city: "Shenzen", country: "China"},
  {city: "Shenzen", country: "China"},
  {city: "Istanbul", country: "Turkey"}
];
const colors = ['204, 0, 0', '0, 0, 255', '0, 153, 0', '153, 51, 255'];

data = data
  .sort((o1, o2) => o1.country.localeCompare(o2.country));
const countries = data
  .reduce((acc, o) => {
    let country = acc.find(v => v.country == o.country);
    if (!country) {
      country = { country: o.country, cities: 0 };
      acc.push(country);       
    }
    ++country.cities;   
    return acc;
  }, []);
countries.forEach((c, i) => c.color = colors[i]);
const cities = data
  .reduce((acc, o) => {
    let city = acc.find(v => v.city == o.city);
    if (!city) {
      city = { country: o.country, city: o.city, count: 0 };
      acc.push(city);       
    }
    ++city.count;   
    return acc;
  }, []);
cities.forEach(c => c.color = countries.find(o => o.country == c.country).color); 


Chart.register(ChartDataLabels);
new Chart('myChart', {
  type: 'doughnut',
  data: {
    datasets: [{
        data: cities.map(o => o.count),
        labels: cities.map(o => o.city),
        backgroundColor: cities.map(c => 'rgba(' + c.color + ', 0.2)'),
        borderWidth: 3
      },
      {
        data: countries.map(c => c.cities),
        labels: countries.map(c => c.country),
        backgroundColor: countries.map(c => 'rgb(' + c.color + ', 0.4)'),
        borderWidth: 3
      }
    ]
  },
  options: {
    cutout: '40%',
    plugins: {
      datalabels: {
        textAlign: 'center',
        formatter: (v, ctx) => {
          const dataset = ctx.chart.data.datasets[ctx.datasetIndex];
          return dataset.labels[ctx.dataIndex] + ': ' + dataset.data[ctx.dataIndex];
        }
      }
    }
  }
});
canvas {
  max-height: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>
<canvas id="myChart"></canvas>

Leave a comment