1. 程式人生 > >D3.js中Population Pyramid詳解

D3.js中Population Pyramid詳解

// 定義相關尺寸 // margin定義svg畫圖的上 、右、下、左的外邊距 var margin = {top: 20, right: 40, bottom: 30, left: 20}, // 計算寬度 width = 960 - margin.left - margin.right, // 計算高度 height = 500 - margin.top - margin.bottom, // 計算柱狀條的寬度,其中19由於分了19個年齡段 barWidth = Math.floor(width / 19) - 1; // 為x軸定義線性比例尺,值域range的定義可以看出,x軸的刻度尺都會位於柱狀圖的底部中間位置
var x = d3.scale.linear() .range([barWidth / 2, width - barWidth / 2]); // 為y軸定義線性比例尺,值域為height到0 var y = d3.scale.linear() .range([height, 0]); // 定義y座標軸 var yAxis = d3.svg.axis() // 設定y軸的比例尺 .scale(y) // y軸座標刻度文字在右側 .orient("right") // 這裡設定為“-width”,個人理解為,y軸刻度線本應該在軸的右邊,設定為負數,刻度線繪製在y軸的左邊
// 而且刻度線的長度為圖形的寬度,表現在圖上就是那些橫穿柱狀條的白色線,看不見白色線的部分是因為 // 圖背景和刻度線都是白色 .tickSize(-width) // 設定y軸刻度的格式 .tickFormat(function(d) { return Math.round(d / 1e6) + "M"; }); // An SVG element with a bottom-right origin. // 定義svg畫布 var svg = d3.select("body").append("svg") // 設定svg畫布的寬度 .attr("width"
, width + margin.left + margin.right) // 設定svg畫布的高度 .attr("height", height + margin.top + margin.bottom) .append("g") // 定位svg畫布 .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // A sliding container to hold the bars by birthyear. // 定義表示 出生年 的元素 var birthyears = svg.append("g") .attr("class", "birthyears"); // A label for the current year. // 繪製當年的年份文字,即圖中左上角的 2000字樣 var title = svg.append("text") .attr("class", "title") .attr("dy", ".71em") .text(2000); // 處理資料 d3.csv("population.csv", function(error, data) { // Convert strings to numbers. // 將csv資料檔案中的pepole,yaer,age欄位的值轉換成數字型別 data.forEach(function(d) { d.people = +d.people; d.year = +d.year; d.age = +d.age; }); // Compute the extent of the data set in age and years. // 計算年齡和年份資料集的範圍 // 獲取最大年齡 var age1 = d3.max(data, function(d) { return d.age; }), // 獲取最小年份 year0 = d3.min(data, function(d) { return d.year; }), // 獲取最大年份 year1 = d3.max(data, function(d) { return d.year; }), // 設定year為最大年份 year = year1; // Update the scale domains. // 上面在定義x,y的比例尺時沒有設定“定義域”,此處開始設定 // 設定x比例尺的定義域,可以看出,x軸表示年齡的變化 x.domain([year1 - age1, year1]); // 設定y比例尺的定義域,可以看出,y軸表示人口數量的變化 y.domain([0, d3.max(data, function(d) { return d.people; })]); // Produce a map from year and birthyear to [male, female]. // d3.nest()函式用來將資料分組為任意層次結構 // d3.nest().key(fun)用來對每資料以fun函式返回的鍵值來進行分組,此處以year來進行分組 // 後,返回的是以year作為鍵的不同的陣列;再以year-age作為鍵值進行第二次分組; // rollup()函式將用返回的值d.people來替換key所對應的值 // d3.nest().map()返回最終的分組後的層次結構的資料 // 可以通過在瀏覽器中除錯狀態下看到最終返回的data陣列是以年份進行第一層分組,每個年份下又以 // d.year -d.age進行了第二層的分組,第二層分組對應的資料為rollup中指定的d.people。 data = d3.nest() .key(function(d) { return d.year; }) .key(function(d) { return d.year - d.age; }) .rollup(function(v) { return v.map(function(d) { return d.people; }); }) .map(data); // Add an axis to show the population values. // 繪製y軸 svg.append("g") .attr("class", "y axis") // 將y軸定位到畫布右側 .attr("transform", "translate(" + width + ",0)") // 對該g元素執行yAxis定義的操作 .call(yAxis) .selectAll("g") // 篩選出 value為空的 .filter(function(value) { return !value; }) // 將篩選出的value為空的元素,為期新增zero樣式類 .classed("zero", true); // Add labeled rects for each birthyear (so that no enter or exit is required). // 為表示出生年份的元素繫結資料,定義年份步長為5年 var birthyear = birthyears.selectAll(".birthyear") .data(d3.range(year0 - age1, year1 + 1, 5)) .enter().append("g") .attr("class", "birthyear") // 定位年份的位置,通過上面定義的x()比例尺函式來計算 .attr("transform", function(birthyear) { return "translate(" + x(birthyear) + ",0)"; }); // 繪製柱狀條 birthyear.selectAll("rect") // 獲取2000這一年裡,出生年份為birthyear的分組 .data(function(birthyear) { return data[year][birthyear] || [0, 0]; }) .enter().append("rect") .attr("x", -barWidth / 2) .attr("width", barWidth) // 設定y位置通過y比例尺來計算 .attr("y", y) // 設定柱狀條的高度 .attr("height", function(value) { return height - y(value); }); // Add labels to show birthyear. // 添加出生年份文字 birthyear.append("text") .attr("y", height - 4) .text(function(birthyear) { return birthyear; }); // Add labels to show age (separate; not animated). // 新增年齡文字 svg.selectAll(".age") // 為年齡文字繫結資料,年齡步長為5 .data(d3.range(0, age1 + 1, 5)) .enter().append("text") .attr("class", "age") .attr("x", function(age) { return x(year - age); }) .attr("y", height + 4) .attr("dy", ".71em") .text(function(age) { return age; }); // Allow the arrow keys to change the displayed year. // 通過方向鍵“←”和“→”來查滑動年份視窗,檢視更多年份的人口分佈情況 // 用focus()方法可把鍵盤焦點給予當前視窗 window.focus(); //為方向鍵“←”和“→”操作繫結動作 d3.select(window).on("keydown", function() { switch (d3.event.keyCode) { // 若為向左←,則將當前年份倒退10年 case 37: year = Math.max(year0, year - 10); break; // 若為向右→,則將當前年份向前推進10年 case 39: year = Math.min(year1, year + 10); break; } // 對圖進行更新 update(); }); // 定義更改年份視窗後,對圖進行更新的操作 function update() { // 若更改年份視窗後,data中無當前年份的資料,則不進行任何操作,直接返回 if (!(year in data)) return; // 託更改年份視窗後,data中有當前年份資料,則首先更新左上角顯示的年份 title.text(year); // 更新出生年份,此處定義更新過渡動畫 birthyears.transition() // 動作持續750毫秒 .duration(750) // 定義更新動作 .attr("transform", "translate(" + (x(year1) - x(year)) + ",0)"); // 更新柱狀條 birthyear.selectAll("rect") // 繫結新的年份視窗資料 .data(function(birthyear) { return data[year][birthyear] || [0, 0]; }) // 定義過渡動畫 .transition() .duration(750) .attr("y", y) .attr("height", function(value) { return height - y(value); }); } });