رسم نمودار دایره‌ای (Pie Chart) با استفاده از کتابخانه D3

در این مقاله قصد داریم یک نمودار دایره‌ای (Pie Chart) با استفاده از کتابخانه D3 رسم کنیم که درصد جمعیت استان‌های مختلف ایران را نمایش می‌دهد.

 

کد نمودار دایره‌ای (Pie Chart):

 

var width = 600;
var height = 400;
var svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
 
var g = svg.append('g') // افزودن تگ g
.attr('transform', 'translate(300, 190)');
 
var iranPopulation = [
{province: 'تهران', population: 13267637},
{province: 'خراسان رضوی', population: 6434501},
{province: 'اصفهان', population: 5120850},
{province: 'فارس', population: 4851274},
{province: 'خوزستان', population: 4710509},
{province: 'آذربایجان شرقی', population: 3909652},
{province: 'سایر استان‌ها', population: 41651681}
];
 
var totalCount = iranPopulation.reduce(function(sum, d) {
return sum + d.population;
}, 0);
 
iranPopulation.forEach(function(d) {
d.percentage = (d.population / totalCount) * 100;
});
 
var pieGenerator = d3.pie()
.value(function(d) {return d.percentage;});
 
var arcData = pieGenerator(iranPopulation);
 
var arcGenerator = d3.arc()
.innerRadius(10)
.outerRadius(180)
.padAngle(.02)
.padRadius(50)
.cornerRadius(5);
 
var sequentialScale = d3.scaleSequential()
.domain([0, 11])
.interpolator(d3.interpolateRainbow);
 
svg.select('g')
.selectAll('path')
.data(arcData)
.join('path')
.attr('d', arcGenerator)
.attr('fill', function(d, i) {
return sequentialScale(i);
})
.attr('stroke', 'white');
 
svg.select('g')
.selectAll('text')
.data(arcData)
.join('text')
.each(function(d) {
var centroid = arcGenerator.centroid(d);
d3.select(this)
.attr('x', centroid[0])
.attr('y', centroid[1])
.attr('dy', '0.33em')
.text(d.data.percentage.toFixed(2) + '%')
.attr('text-anchor', 'middle')
.attr('font-size', 12)
});
 
svg.append('text')
.attr('x', width / 2)
.attr('y', height - 5)
.attr('text-anchor', 'middle')
.style('font-size', '16px')
.style('font-weight', 'bold')
.text('درصد جمعیت در استان‌ها');
 
var legend = svg.append('g')
.attr('class', 'legend')
.attr('transform', `translate(${width - 90}, ${height / 2 - 150})`);
 
legend.selectAll('rect')
.data(iranPopulation)
.join('rect')
.attr('x', 0)
.attr('y', (d, i) => i * 20)
.attr('width', 10)
.attr('height', 10)
.style('fill', (d, i) => sequentialScale(i));
 
legend.selectAll('text')
.data(iranPopulation)
.join('text')
.attr('x', 15)
.attr('y', (d, i) => i * 20 + 9)
.style('font-size', '12px')
.text(d => d.province);
 
svg.node()

 

خروجی کد:

 

 

تنظیمات اولیه

 

ابتدا عرض و ارتفاع SVG را تعریف کرده و یک عنصر SVG ایجاد می‌کنیم.

  • var width = 600 مشخص کردن عرض svg

  • var height = 400 مشخص کردن ارتفاع svg

  • var svg = d3.create('svg') ایجاد svg

  • attr('width', width) مقداردهی عرض به svg

  • attr('height', height); مقداردهی ارتفاع به svg

 

افزودن گروه و انتقال به مرکز

 

یک گروه (<g>) به SVG اضافه کرده و آن را به مرکز نمودار منتقل می‌کنیم.

  • var g = svg.append('g') ایجاد تگ g
  • attr('transform', 'translate(300, 190)') انتقال تگ g به مرکز svg

 

هدف از اضافه کردن این گروه و تنظیم موقعیت آن این است که محور مبنای گروه (g) از مرکز نمودار دایره‌ای شروع شود. با این کار، رسم کمان‌ها و برچسب‌های نمودار دایره‌ای بر اساس مرکز نمودار آسان‌تر می‌شود. به عبارت دیگر، مرکز نمودار دایره‌ای به نقطه (300, 190) در داخل svg منتقل می‌شود.

 

داده‌های جمعیت استان‌ها

 

در متغییر iranPopulation داده‌های مربوط به جمعیت برخی  از استان‌های کشور ثبت شده است.

 

محاسبه درصدها

 

ابتدا جمع کل جمعیت را محاسبه کرده و سپس درصد جمعیت هر استان را نسبت به کل محاسبه می‌کنیم.

  •  var totalCount = iranPopulation.reduce(function(sum, d) { return sum + d.population; }, 0) محاسبه کل جمعیت با استفاده از داده‌های موجود
  • iranPopulation.forEach(function(d) {  d.percentage = (d.population / totalCount) * 100; }) مشخص کردن درصد جمعیت هر یک از استان‌ها

 

ایجاد Pie Generator

 

یک Pie Generator ایجاد می‌کنیم که داده‌های درصد را به صورت زاویه‌ای برای نمودار دایره‌ای تبدیل می‌کند.

  • var pieGenerator = d3.pie() استفاده از تابع d3.pie() که یک تابع تولیدکننده (generator) برای داده‌های نمودار دایره‌ای (Pie Chart) می‌باشد.
  • value(function(d) {return d.percentage;}) با استفاده از متد value مقدار هر بخش از نمودار دایره‌ای را مشخص می‌کنیم. در اینجا d.percentage است.
  • var arcData = pieGenerator(iranPopulation) با استفاده از pieGenerator داده‌های iranPopulation را به داده‌های مورد نیاز برای رسم قوس‌های نمودار دایره‌ای تبدیل می‌کنیم

 

ایجاد Arc Generator

 

یک Arc Generator ایجاد می‌کنیم که مشخصات هر کمان از نمودار دایره‌ای را تعریف می‌کند.

  • var arcGenerator = d3.arc() با استفاده از تابع d3.arc() که یک تولیدکننده (generator) برای رسم قوس‌ها (arcs) در نمودار دایره‌ای قوس‌ها را ایجاد می‌کنیم.
  • innerRadius(10) تنظیم شعاع داخلی قوس‌ها
  • outerRadius(180) تنظیم شعاع خارجی قوس‌ها
  • padAngle(.02) ایجاد فضا بین قوس‌ها
  • padRadius(50) شعاع فاصله بین قوس‌ها
  • cornerRadius(5) گرد کردن گوشه‌های قوس‌ها

 

ایجاد مقیاس رنگ

 

یک مقیاس رنگ ترتیبی تعریف می‌کنیم که برای هر بخش از نمودار دایره‌ای یک رنگ متفاوت تخصیص دهد.

  • var sequentialScale = d3.scaleSequential() استفاده از مقیاس ترتیبی d3.scaleSequential() که برای نگاشت یک دامنه پیوسته از اعداد به یک بازه پیوسته از رنگ‌ها استفاده می‌شود.
  • domain([0, 7]) تعیین دامنه ورودی به مقیاس
  • interpolator(d3.interpolateRainbow) با استفاده از d3.interpolateRainbow که یک مفسر رنگی است رنگین‌کمانی از رنگ‌ها را تولید می‌کنیم.

 

رسم کمان‌های نمودار

 

با استفاده از داده‌های کمان، کمان‌های نمودار دایره‌ای را رسم کرده و رنگ هر کمان را تنظیم می‌کنیم.

  • svg.select('g') انتخاب تگ g که در ابتدا ایجاد کرده‌ایم
  • selectAll('path') انتخاب همه عناصر path
  • data(arcData) پیوند داده‌ها به عناصر path
  • join('path') ایجاد یک عنصر path به ازای هر داده
  • attr('d', arcGenerator) ویژگی d هر عنصر path با استفاده از تابع arcGenerator تنظیم می‌کند
  • attr('fill', function(d, i) {return sequentialScale(i);}) رنگ هر بخش از نمودار را مشخص می‌کند
  • attr('stroke', 'white') خط حاشیه سفید رنگی به اطراف هر بخش از نمودار دایره‌ای اضافه می‌کند

 

افزودن برچسب درصدها

 

در این بخش، برچسب‌های درصد را در مرکز هر کمان قرار می‌دهیم.

  • svg.select('g') انتخاب تگ g  که در ابتدا ایجاد کرده‌ایم
  • selectAll('text') انتخاب تمام عناصر text (در ابتدا هیچ عنصری وجود ندارد ولی D3 آن‌ها را ایجاد می‌کند.)
  • data(arcData) پیوند داده‌های arcData به text
  • join('text') ایجاد عنصر text به ازای هر داده
  • each(function(d) { اجرای این تابع به ازای هر text
  • var centroid = arcGenerator.centroid(d) محاسبه مرکز هر بخش نمودار
  • d3.select(this) انتخاب عنصر text
  • attr('x', centroid[0]) مشخص کردن محل قرارگیری متن در محور افقی
  • attr('y', centroid[1]) مشخص کردن محل قرارگیری متن در محور عمودی
  • attr('dy', '0.33em') قرارگیری متن در مرکز
  • text(d.data.percentage.toFixed(2) + '%') مشخص کردن متن و اضافه کردن درصد به آن
  • attr('text-anchor', 'middle') قرارگیری متن در مرکز
  • attr('font-size', 12) مشخص کردن اندازه متن

 

افزودن عنوان نمودار

 

یک عنوان به پایین نمودار اضافه می‌کنیم.

  • svg.append('text') ایجاد عنصر text

  • attr('x', width / 2) مشخص کردن محل قرارگیری متن در محور افقی

  • attr('y', height - 5) مشخص کردن محل قرارگیری متن در محور افقی

  • attr('text-anchor', 'middle') قرارگیری متن در مرکز صفحه

  • style('font-size', '16px') مشخص کردن اندازه متن

  • style('font-weight', 'bold') پررنگ کردن متن

  • text('درصد جمعیت در استان‌ها') متن

 

ایجاد توضیحات (Legend)

 

در این بخش، توضیحات برای نمودار ایجاد می‌کنیم که هر استان را با رنگ مربوطه نشان می‌دهد.

  • var legend = svg.append('g') اضافه کردن تگ g

  • attr('class', 'legend') اضافه کردن کلاس legend

  • attr('transform', `translate(${width - 90}, ${height / 2 - 150})`) انتقال تگ g

  • legend.selectAll('rect') انتخاب تمام عناصر rect (مستطیل) درون گروه legend، در ابتدا هیچ مستطیلی وجود ندارد.

  • data(iranPopulation) پیوند دادن داده‌ها به مستطیل‌ها

  • join('rect') ایجاد یک مستطیل به ازای هر داده

  • attr('x', 0) مشخص کردن موقعیت مستطیل در محور افقی

  • attr('y', (d, i) => i * 20) مشخص کردن موقعیت مستطیل در محور عمودی

  • attr('width', 10) مشخص کردن عرض هر مستطیل

  • attr('height', 10) مشخص کردن ارتفاع هر مستطیل

  • style('fill', (d, i) => sequentialScale(i)) مشخص کردن رنگ هر مستطیل

  • legend.selectAll('text') انتخاب تمام textهای درون گروه legend

  • data(iranPopulation) پیوند داده‌ها به متن‌ها

  • join('text') ایجاد متن به ازای هر یک از داده‌ها

  • attr('x', 15) مشخص کردن موقعیت متن در محور افقی

  • attr('y', (d, i) => i * 20 + 9) مشخص کردن موقعیت متن در محور عمودی

  • style('font-size', '12px') مشخص کردن اندازه متن

  • text(d => d.province) مشخص کردن هر متن