0.引言
在极坐标系中,任意位置可由一个夹角和一段相对原点(极点)的距离表示。也就是说,我们可以用 (angle,r) 来表示极坐标系中的点。
1.数据
假设我们有如下数据集[ [10, 0.2], [6, 0.5], [23, 0.9],... ]。其中每个数据的一维表示时间(24小时制),二维表示信号强度。如[10, 0.2]表示十点的信号强度为0.2。现在,我们要将这些数据呈现在极坐标图(polar plot)上:
2.比例尺
我们发现,源数据的一维并不直接表示角度,二维也并不直接表示距离。那么我们需要用到比例尺。比例尺就是将某一区域的值映射到另一区域,其大小关系不变。
由于我们的区间都是连续的(连续的24小时映射到连续的360°,信号强度值映射到半径值),故这里我们用到的是线性比例尺。
1 var angle = d3.scale.linear() 2 .domain([0, 24]) 3 .range([0, 2 * Math.PI]); 4 5 var r = d3.scale.linear() 6 .domain([0, max_singnal]) 7 .range([0, radius]);
3.坐标系
为了清晰地呈现数据,绘制角度坐标轴(每45°画一条线)和半径坐标轴(半径每递增0.1画一个圆)。以及标上刻度。
// 角度坐标轴
1 var ga = svg.append("g") 2 .attr("class", "a axis") 3 .selectAll("g") 4 .data(d3.range(-90, 270, 45)) 5 .enter().append("g") 6 .attr("transform", function(d) { 7 return "rotate(" + d + ")"; 8 }); 9 10 ga.append("line") 11 .attr("x2", radius); 12 13 ga.append("text") 14 .attr("x", radius + 6) 15 .attr("dy", ".35em") 16 .style("text-anchor", function(d) { return d < 270 && d > 90 ? "end" : null; }) 17 .attr("transform", function(d) { 18 return d < 270 && d > 90 ? "rotate(180 " + (radius + 6) + ",0)" : null; 19 }) 20 .text(function(d,i) { return i*3 + ":00" });
// 半径坐标轴
1 var gr = svg.append("g") 2 .attr("class", "r axis") 3 .selectAll("g") 4 .data(r.ticks(max_singnal*10).slice(1)) 5 .enter().append("g"); 6 7 gr.append("circle") 8 .attr("r", r); 9 10 gr.append("text") 11 .attr("y", function(d) { return -r(d) - 4; }) 12 .attr("transform", "rotate(20)") 13 .style("text-anchor", "middle") 14 .text(function(d) { return d; });
4.绘制数据点
1 var color = d3.scale.category20(); 2 3 var line = d3.svg.line.radial() 4 .angle(function(d) { 5 return angle(d[0]); 6 }) 7 .radius(function(d) { 8 return r(d[1]); 9 }); 10 11 12 svg.selectAll("point") 13 .data(data) 14 .enter() 15 .append("circle") 16 .attr("class", "point") 17 .attr("transform", function(d) { 18 var coors = line([d]).slice(1).slice(0, -1); 19 return "translate(" + coors + ")" 20 }) 21 .attr("r", 8) 22 .attr("fill",function(d,i){ 23 return color(i); 24 });
5.完整代码
<!DOCTYPE html> <meta charset="utf-8"> <style> .frame { fill: none; stroke: #000; } .axis text { font: 10px sans-serif; } .axis line, .axis circle { fill: none; stroke: steelblue; stroke-dasharray: 4; } .axis:last-of-type circle { stroke: steelblue; stroke-dasharray: none; } .line { fill: none; stroke: orange; stroke- 3px; } </style> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> <script> var width = 960, height = 500, radius = Math.min(width, height) / 2 - 30; var data = [ [0, 0.4], [6, 0.2], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], [Math.random()*24, Math.random()], ] var y = _.map(data, _.last); var max = Math.max.apply(null, y); max = Math.ceil(max*10)/10; var angle = d3.scale.linear() .domain([0, 24]) .range([0, 2 * Math.PI]); var r = d3.scale.linear() .domain([0, max]) .range([0, radius]); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var gr = svg.append("g") .attr("class", "r axis") .selectAll("g") .data(r.ticks(max*10).slice(1)) .enter().append("g"); gr.append("circle") .attr("r", r); gr.append("text") .attr("y", function(d) { return -r(d) - 4; }) .attr("transform", "rotate(20)") .style("text-anchor", "middle") .text(function(d) { return d; }); var ga = svg.append("g") .attr("class", "a axis") .selectAll("g") .data(d3.range(-90, 270, 45)) .enter().append("g") .attr("transform", function(d) { return "rotate(" + d + ")"; }); ga.append("line") .attr("x2", radius); ga.append("text") .attr("x", radius + 6) .attr("dy", ".35em") .style("text-anchor", function(d) { return d < 270 && d > 90 ? "end" : null; }) .attr("transform", function(d) { return d < 270 && d > 90 ? "rotate(180 " + (radius + 6) + ",0)" : null; }) .text(function(d,i) { return i*3 + ":00" }); var color = d3.scale.category20(); var line = d3.svg.line.radial() .angle(function(d) { return angle(d[0]); }) .radius(function(d) { return r(d[1]); }); svg.selectAll("point") .data(data) .enter() .append("circle") .attr("class", "point") .attr("transform", function(d) { var coors = line([d]).slice(1).slice(0, -1); return "translate(" + coors + ")" }) .attr("r", 8) .attr("fill",function(d,i){ return color(i); }); </script>