根据数据表格绘制热力图是d3.js的拿手好戏。
本文用存放在csv文件中的表格数据,绘制各个房间内人数的热力图
图形效果如下:
数据文件内容:
personNumber.csv
name,room1,room2,room3,room4,room5,room6,room7,room8,room9,room10,room11,room12,room13,room14,room15,room16,room17,room18,room19,room20
楼层1,9,39,30,11,22,0,8,10,1,1,4,0,1,4,5,8,2,1,3,2
楼层2,81,38,28,10,20,0,7,9,1,2,5,0,1,6,8,7,2,1,3,2
楼层3,2,36,27,10,21,0,6,7,1,1,4,0,1,4,5,5,2,1,3,2
楼层4,1,38,26,10,20,0,6,7,1,1,2,0,1,7,8,2,1,1,2,2
楼层5,6,36,26,9,19,0,6,7,1,3,7,0,1,4,5,3,1,1,3,3
楼层6,74,39,25,9,19,0,6,7,1,1,3,0,1,6,7,3,1,1,3,2
html文件内容
index.html
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<title>房间人数热力图</title>
<style>
rect.bordered {
stroke: #A1A1A1;
stroke-width:1px;
}
text.mono {
font-size: 6pt;
font-family: Consolas, courier;
fill: #000;
}
text.axis-workweek {
fill: #00FF;
}
text.axis-worktime {
fill: #ff00;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<div id="roomHeatMap"></div>
<script type="text/javascript">
// 定义Heatmap的位置、宽高、房间格子的边长等等与布局有关的变量
var margin = { top: 50, right: 0, bottom: 100, left: 150 },
width = 1300 - margin.left - margin.right, // Heatmap的宽度
height = 600 - margin.top - margin.bottom,
gridSize = Math.floor(width / 20), // 房间格子大小,即去掉小数部分,width分成20份
gridInnerSize=gridSize-10,
legendElementWidth = gridSize * 2, // 下部图例格子的长度,是房间格子边长的两倍
buckets = 9, // 一共9种颜色级别
colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"],
rooms = ["房间1","房间2","房间3","房间4","房间5","房间6","房间7","房间8","房间9","房间10","房间11","房间12","房间13","房间14","房间15","房间16","房间17","房间18","房间19","房间20"];
// 读取 CSV 文件,房间内的人数存放于此文件中
d3.csv("personNumber.csv",
function(error, data) {
// console.log(data);
// 设置roomHeatMap,svg
var svg = d3.select("#roomHeatMap").append("svg") // 选择“roomHeatMap”(就是div),加入一个svg,设置属性跟div一样大
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g") // 在svg内加入一个g(group组),并设置元素g的显示位置
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 行标题
var dayLabels = svg.selectAll(".nameLabel")
.data(data)
.enter() // 为data中每一项创建一个".dayLabel"
.append("text") // 为days中每一项创建一的".dayLabel"添加文本,下面全是设置文本的属性
.text(function (d, i) { return data[i].name; })
.attr("x", 0)
.attr("y", function (d, i) { return i * (gridSize+20)-10; })
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize / 1.5 + ")")
.attr("class", function (d, i)
{ return ((i >= 0 && i <= 4) ? "nameLabel mono axis axis-workweek" : "nameLabel mono axis"); }
);
// 列标题
var timeLabels = svg.selectAll(".testLabel")
.data(rooms)
.enter().append("text")
.text(function(d) { return d; })
.attr("x", function(d, i) { return i * gridSize+10; })
.attr("y", 0)
.style("text-anchor", "end")
.attr("transform", "translate(" + gridSize / 2 + ", -6)")
.attr("class", function(d, i) {
return ( "testLabel mono axis"); }
);
// 网格数组,这里将二维数据转换为一维数组,一维数组方便于d3.js绑定
var personCountArray=[];
for (var i = 0; i < 6; i++){
personCountArray[i*20] = parseInt(data[i].room1);
personCountArray[i*20+1] = parseInt(data[i].room2);
personCountArray[i*20+2] = parseInt(data[i].room3);
personCountArray[i*20+3] = parseInt(data[i].room4);
personCountArray[i*20+4] = parseInt(data[i].room5);
personCountArray[i*20+5] = parseInt(data[i].room6);
personCountArray[i*20+6] = parseInt(data[i].room7);
personCountArray[i*20+7] = parseInt(data[i].room8);
personCountArray[i*20+8] = parseInt(data[i].room9);
personCountArray[i*20+9] = parseInt(data[i].room10);
personCountArray[i*20+10] = parseInt(data[i].room11);
personCountArray[i*20+11] = parseInt(data[i].room12);
personCountArray[i*20+12] = parseInt(data[i].room13);
personCountArray[i*20+13] = parseInt(data[i].room14);
personCountArray[i*20+14] = parseInt(data[i].room15);
personCountArray[i*20+15] = parseInt(data[i].room16);
personCountArray[i*20+16] = parseInt(data[i].room17);
personCountArray[i*20+17] = parseInt(data[i].room18);
personCountArray[i*20+18] = parseInt(data[i].room19);
personCountArray[i*20+19] = parseInt(data[i].room20);
}
//绘制房间格子,一开始全部是白色
var heatMap = svg.selectAll(".personNumber")
.data(personCountArray)
.enter()
.append("rect")
.attr("x", function(d, i){ return (i % 20)*gridSize;})
.attr("y", function(d, i){ return parseInt(i / 20)*(gridSize+20);})
.attr("rx", 6)
.attr("ry", 6)
.attr("class", "bordered")
.attr("width", gridInnerSize)
.attr("height", gridInnerSize)
.style("fill", "#FFFFFF");
// 颜色比例尺
var colorScale = d3.scale.quantile() // 按分位数取值,可使每个区域内元素个数相等
.domain([0, buckets - 1, d3.max(personCountArray)]) // 定义域
// domain([0, n, 数据的最大值]);
.range(colors); // 值域:是颜色数组,函数的返回值是代表某种颜色的字符串
// duration(n) 在n毫秒内根据房间内人数为房间格子上色
heatMap.transition().duration(2000)
.style("fill", function(d) { return colorScale(d); });
// 鼠标停留在房间格子上时显示人数
heatMap.append("title").text(function(d) { return "房间内有 "+d+" 人"; });
// 图例
var legend = svg.selectAll(".legend")
//[0].concat(colorScale.quantiles())将数组[0]与含有8个元素的olorScale.quantiles()连接起来得到一个有9个元素的数组
.data([0].concat(colorScale.quantiles()), function(d) {
return d;
})
.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height+gridSize)
.attr("width", legendElementWidth)
.attr("height", gridSize/2)
.style("fill", function(d, i) { return colors[i]; });
legend.append("text")
.attr("class", "mono")
.text(function(d) {
return ""+Math.round(d)+"人以上";
})
.attr("x", function(d, i) { return legendElementWidth * i+20; })
.attr("y", height +gridSize+40);
});
</script>
</body>
</html>