求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
数据可视化-使用 SVG 和 D3 可视化浏览指标
 
作者 guolin,火龙果软件    发布于 2013-12-05
 

摘要: 学习如何结合使用可缩放矢量图形 (SVG) 和开源 D3 JavaScript 库创建数据可视化。形状、颜色和布局可能对从业务角度理解海量数据有很大的帮助。一个示例场景演示了如何使用 SVG 和 D3 来根据社交媒体的浏览指标创建 ...

社交媒体大数据挑战

社交媒体站点(比如 Twitter、Facebook 和 YouTube)提供了全面的 Web 服务接口来公开它们的功能。例如,YouTube Data API 支持应用程序将视频上传到 YouTube,或播放一个网站上现有的 YouTube 视频。现在这些站点也在开发分析 API。举例而言,YouTube Analytics API 向编程客户端提供了查看次数和喜欢次数(number of likes)等统计数据。结果,更多的业务应用程序可通过可视和编程接口与社交媒体互动。对所有规模的公司而言,下一个挑战是通过大数据分析将大量社交数据最有效地应用到业务中。数据可视化(整个分析场景中的一个组成部分)是这个文章系列的关注重点。您可了解 IBM 大数据平台的 IBM InfoSphere Streams和 IBM InfoSphere BigInsights产品全面的分析功能。

分析社交媒体指标

公司理解客户行为的一种创造性方式是,通过社交媒体提出想法,并让潜在客户参与到交互式讨论中。社交媒体上的互动反映了双向的人际互动:要理解人们的好恶,您必须聆听他们在说什么,就像您希望与积极主动的人互动一样。

以一家家庭装饰公司为假设场景,该公司以博客、视频、Facebook 页面和论坛形式发布公开内容。这些内容通过社交媒体资源展示了公司的想法,并尝试发起讨论和其他形式的用户互动。这些内容迎合各个客户的口味和偏好,帮助他们从一个社交资源导航到另一个。为了判断不断变化的客户趋势并提出新方法和新设计,公司希望从三个方面分析浏览数据:

1、流行度,由每个社交资源的 查看次数表示

2、参与该资源上的 互动的用户数量

3、用户从一个资源 导航到另一个资源的方向

表 1、2和 3 分别显示了三周内用户查看、用户互动和导航次数。请注意,这些表使用了彩色名称来表示公司使用的社交媒体资源类型(比如博客和 Facebook 页面)。

表 1 显示了每个资源的用户查看次数:

社交资源 蓝色 金色 绿色 红色 紫红色
第 1 周 7057 7483 3749 3846 4598
第 2 周 2371 7397 4589 2861 8249
第 3 周 5972 5672 9152 9725 8983

在 表 1中可以看到,蓝色资源在第 1 周被查看了 7,057 次,金色资源是这一周被查看次数最多的资源。

表 2 显示了用户互动数据:

社交资源 蓝色 金色 绿色 红色 紫红色
第 1 周 2052 2089 1586 1426 2632
第 2 周 2071 2190 7214 3782 2721
第 3 周 3076 3190 4532 3825 4831

从 表 2中可以看到,第 1 周在蓝色资源上有 2,052 名用户参与了互动,在这一周紫红色资源上的用户互动次数最高。

表 3 显示了从蓝色资源导航到其他资源的用户数量:

社交资源 蓝色到金色 蓝色到绿色 蓝色到红色 蓝色到紫红色
第 1 周 3057 3483 8749 8456
第 2 周 2371 7397 4589 2861
第 3 周 5972 5672 9152 9725

从 表 3可以看到,在第 1 周有 3,057 名用户在访问蓝色资源后导航到了金色资源,而且红色资源收到了来自蓝色资源的最多观众。

浏览数据的可视化

可视内容提供了一种比数字表格更容易、更快捷的方式来解释大数据量。可通过多种方式以图形方式表示表 1、2和 3中的数据。例如,图 1 就是一种显示 表 1中的第 1 周数据的简单方式:

图 1. 用圆圈表示的每种社交资源在第 1 周的查看次数

图 1 将每种资源的查看次数表示为一个圆圈。圆圈的相对大小与它们表示的次数成正比,所以查看最多的资源(金色)由最大的圆圈表示。每个圆圈还显示了每种资源第 1 周获得的实际查看次数。

图 2 是 图 1的一种细微变形,使用了一种不同的圆圈布局:

图 2. 以稍微不同的圆圈布局显示的第 1 周查看次数

每种资源的流行度只是公司希望分析的一个数据维度。图 3 同时显示了第 1 周每种资源的查看次数和希望在该资源上互动的用户数量 —一种可视化表示中包含两种维度:

图 3. 嵌套的圆圈表示了第 1 周的用户查看次数和互动的用户数量

在 图 3 中,外圈表示资源的总查看次数,内圈表示在该资源上互动的用户数量。我还在每个圆圈中放入了数字来表示实际的查看和互动数据。

图 1、图 2和 图 3 显示了组合颜色、形状和实际数字来表示数据的简单方式。通过查看 图 3,您可看到金色资源在第 1 周吸引了最多用户查看,而紫红色资源上的互动最多。

动态生成的可视化 JavaScript

D3、DVG 和 JavaScript 相结合,形成了一个基于浏览器的完整的数据可视化套件。为了在此文章系列中演示它们的结合使用,我在示例代码中应编码了示例数据。通过服务器端组件动态组合了真实项目中的大部分 JavaScript 代码。在大数据分析应用程序中,这些组件可能包括基于 Apache Hadoop 的集群,托管各种各样服务器端模块的 Web 和应用服务器,以及数据库服务器。使用 D3 以及来自这类来源的 JavaScript 生成 SVG 代码,这种做法非常适合支持 JavaScript 的现代业务应用程序,比如 IBM Business Process Manager(参见 参考资料)。

使用开放技术实现数据可视化

目前的开放标准和开源工具已足够强大,能够支持数据的图形表示。SVG 是一个开放的万维网联盟 (W3C) 标准,它定义了一种基于 XML 的格式来绘制二维图形对象。(参见 参考资料,获取一篇关于 SVG 的介绍性文章的链接。)SVG 受多种浏览器支持。我在 Google Chrome 上测试了本文的所有 SVG 和 JavaScript 代码。

如果您在处理数据和 SVG,D3 库提供了一些不错功能。它获取您的数据及绘图说明,将您的数据与所需的 SVG 标记关联起来,然后迅速生成您可在浏览器中查看的 SVG 代码。D3 在绘制复杂的图形对象时将数据放在容易获取的地方,所以您可不断深入地设计您的图形,一次一个步骤,而且仅需处理绘制图形的每一小部分所需的数据。

对于本文和 第 2 部分 中的数据,我从简单到复杂图形由浅入深地探索 D3 的功能,提供每个图形的 SVG 和 JavaScript 代码。我从最简单的情形开始:生成 图 1中所示圆圈的 SVG 代码。

SVG 数据表示

清单 1 是绘制 图 1中所示圆圈的 SVG 代码:

<?xml
version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="1000"> <g
transform="translate(70,143)"> <circle r="70.57" style="fill:
#000fff;"></circle> <text x="-10" fill="grey">7057</text> </g>
<g transform="translate(216,143)"> <circle r="74.83" style="fill:
#fff555;"></circle> <text x="-10" fill="grey">7483</text> </g>
<g transform="translate(329,143)"> <circle r="37.49" style="fill: #aaf000;">
</circle> <text x="-10" fill="grey">3749</text> </g> <g
transform="translate(404,143)"> <circle r="38.46" style="fill:
#cc0000;"></circle> <text x="-10" fill="grey">3846</text> </g>
<g transform="translate(489,143)"> <circle r="45.98" style="fill:
#993344;"></circle> <text x="-10" fill="grey">4598</text> </g>
</svg>

可以看到,清单 1中的根标记是 <svg>。<svg>标记创建了画布,在该画布上,您可以使用其他 SVG 标记来绘图,该标记也用作这些标记的包装器。<svg>标记的 width和 height属性指定了 SVG 画布的尺寸。我在 清单 1中设置的画布为 1,000 x 1,000 个单位。

根 <svg>标记有 5 个 <g> 子标记,每个子标记用于 图 1中的一个圆圈。<g> 标记被用作为创建每个圆圈和配套文本而实现的绘图的包装器。每个 <g>标记只有一个属性,名为 transform,它的值为 translate (X, Y)。translate (X, Y)值确定了围绕其绘制圆圈的点。例如,清单 1中第一个 <g>标记的 transform属性放在圆圈的中心位置 70, 143。

5 个 <g>标记中的每一个的 transform 属性都有一个不同的 X值,但它们拥有相同的 Y值。结果,5 个圆圈排列在一条水平线上。<g>标记的不同 X 值导致每个圆圈仅挨着前一个圆圈绘制,它们之间没有明显的空白。稍后我将给出生成这些值的简单 JavaScript。

进一步分析 清单 1,您可能会注意到,每个 <g> 标记有两个子标记(一个 <circle>标记和一个 <text>标记),它们分别绘制圆圈和配套的文本。每个 <circle>标记有一个 r属性,它定义了圆圈的半径。请注意,每个 <circle> 标记的 r属性的值为查看次数除以 100。每个 <circle>标记还有一个 style属性,它指定填充圆圈的颜色。我使用了 6 位数的十六进制格式来表示颜色的 RGB(红绿蓝)代码。<text>标记包装了要在每个圆圈内显示的文本。包含在每个 <text>标记中的 x="-10"属性会轻微地移位文本,以便将它放置在更好的位置。<text>标记的 fill属性指定了文本颜色。

您可以下载本文的所有代码示例,并在浏览器窗口中打开它们。尝试在 清单 1中的 SVG 代码中添加更多圆圈或文本,并查看浏览器如何显示您的 SVG。

使用 JavaScript 和 D3 生成 SVG

清单 2 中的 JavaScript 代码使用 D3 生成了 清单 1中的 SVG 代码:

<!DOCTYPE 
html> <meta charset="utf-8"> <body> <script
src="http://d3js.org/d3.v3.min.js"></script> <script> //Step 1: var views = [
[7057, 7483, 3749, 3846, 4598], [ 2371, 7397, 4589, 2861, 8249], [ 5972, 5672, 9152, 9725,
8983], ]; var width = 1000, height = 1000; var colors = [ "blue", "yellow", "green", "red",
"maroon" ]; var week = 0; //Step 2: var svg = d3.select("body").append("svg") .attr("width",
width) .attr("height", height) //Step 3: .selectAll("g") .data(views[week]) .enter(); //Step
4: var g = svg.append("g") //Step 5: .attr("transform", function(d,i){ var x = 0; for (var
count=0; count<i; count++ ) x+=views[week][count]; for (var count=0; count<=i; count++
) x+=views[week][count]; return "translate(" + x/100 + "," + height / 7 + ")" } ); //Step 6:
g.append("circle") .attr("r", function(d){return d/100}) .style("fill", function(d,
i){return colors[i];}); g.append("text") .attr("x", -10) .attr("fill", "grey")
.text(function(d){return d}); </script> </body>

我提供了注释来说明 清单 2中的步骤:

第 1 步:第 1 步是将用户查看数据存储在一个数组中。我还设置了一些变量来存储画布的尺寸和我希望在 SVG 图形中使用的演示。

第 2 步:我开始通过 d3.select("body").append("svg")使用 D3,它将一个 <svg>标记添加到 HTML 页面的正文中。这里我还通过调用 .attr()函数设置了 SVG 画布的宽度和高度。

第 3 步:三个函数调用 .selectAll().data().enter()一起构成了 D3 带来的强大功能和易用性:

.selectAll("g")调用选择根 SVG 标记的所有 <g>子标记。尽管它现在还无法选择任何子标记(因为没有人和子标记),但它知道我在寻找什么。

.data(views[week])调用向 D3 提供了所有 5 种社交资源第 1 周的用户查看数据。

.enter()调用将各个 <g>标记与各个用户查看数据记录相关联。

第 4 步:.append("g")调用高速 D3 添加足够的 <g>标记,以便为数据中的每条记录提供一个 <g> 标记。因为最初没有任何 <g>标记,而且数据数组中的每一周都有 5 个记录(每个记录对应一种社交资源),所以 D3 向根 <svg>标记添加了 5 个 <g>标记。第一个 <g> 标记在内部与第一条数据记录关联(也就是查看数据数组中的 7057),第二个 <g> 标记与第二条记录关联,依此类推。D3 在内部将数据与 SVG 标记相关联,所以我在创作时只需要传递数据数组一次,无需担忧是否将正确的数据与每个 SVG 标记相关联。

第 5 步:我还必须为 5 个 <g>标记中的每个标记都添加一个 transform属性,所以我调用了 .attr() 函数。我只需调用该函数一次,D3 在内部已执行了循环,以确保 transform属性添加到了 5 个 <g>标记中的每一个标记上。

.attr()函数获取您创作的属性名称(在本例中为 transform),使用该名称作为它的第一个参数。回想一下对 清单 1的讨论,transform属性确定了圆圈的位置,所以它的值必须在一个函数中计算。出于此原因,.attr() 函数的第二个参数由另一个函数调用,后者本身接受两个参数:d和 i。

D3 为您提供了这个编写函数调用的机会,因为您在创作该属性值时可能需要使用与 <g>标记有关联的数据。d参数包含您目前为其创作了 transform属性值的特定 <g>标记的数据部分(例如,第一个与表示蓝色社交资源的用户查看数据记录有关联的 <g>标记的数据为 7057)。D3 的魔力在于,它在内部处理了数据与各个标记的正确关联,所以您无需担忧是否挑选了正确的数据部分。您在创作 SVG 时可将精力集中在数据处理上。

i参数是数据的从 0 开始的索引(例如 i= 0 表示与第一条记录有关联的第一个 <g>标记)。您可在这一步中看到,我使用了 i参数来计算 <g> 标记的合适位置,因此在最左侧绘制了第一种(蓝色)社交资源的圆圈,然后紧挨第一个圆圈绘制了第二个圆圈,依此类推。

如果尝试运行第 1 布到第 5 步的代码,您会看到 5 个 <g>子标记和它们的 transform属性,如清单 3 中的 SVG 代码所示。(这些 <g>标记还不会在浏览器窗口中显示图形。)

清单 3. 仅包含 5 个 <g> 标记的 SVG 代码

<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC 
"-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="1000"> <g
transform="translate(70,143)"> </g> <g transform="translate(216,143)">
</g> <g transform="translate(329,143)"> </g> <g
transform="translate(404,143)"> </g> <g transform="translate(489,143)">
</g> </svg>

第 6 步:剩余的任务是向 5 个 <g>标记中的每一个标记添加一个 <circle>标记和一个 <text> 标记。为此,您可以调用 .append()函数两次(一次针对 <circle>标记,另一次针对 <text>标记)。

请注意,我使用了 .attr("r", function(d){return d/100})来创作圆圈的 r属性(半径)。在本例中,我必须知道特定资源的查看次数,然后才能绘制一个具有相应大小的圆圈。因此,我只需要使用 d参数,而不需要使用 i参数,所以我省略了 i。d参数自动包含正确的数据(查看次数),所以我将 d 的值除以 100 来确定圆圈的大小。

基于 D3 的 JavaScript 代码已经可以绘制 图 1中的圆圈。如果您在浏览器窗口中打开来自示例代码 下载部分的 Listing2.html,就会看到它生成了 清单 1的 SVG,显示了 图 1中的圆圈。

一种稍微不同的圆圈布局

您可稍微修改一下 清单 2中的代码,绘制如 图 2中所示的圆圈布局。清单 4 给出了已修改的 JavaScript:

清单 4. 绘制图 2 中的布局的已修改代码

<!DOCTYPE 
html> <meta charset="utf-8"> <body> <script
src="http://d3js.org/d3.v3.min.js"></script> <script> var views = [ [7057,
7483, 3749, 3846, 4598], [ 2371, 7397, 4589, 2861, 8249], [ 5972, 5672, 9152, 9725, 8983], [
9763, 8462, 9782, 1953, 5182], [ 9567, 1571, 2895, 2783, 1874], [ 2371, 7397, 4589, 2861,
8249] ]; var width = 1000, height = 1000; var colors = [ "#0000ff", //blue "#ffd700", //gold
"#008000", //green "#ff0000", //red "#800000" //maroon ]; var week = 0, scale = 100; var svg
= d3.select("body").append("svg") .attr("width", width) .attr("height", height)
.selectAll("g").data(views[week]).enter(); var g = svg.append("g") .attr("transform",
function(d,i){ var x = 10000, y = 10000; if (!(i%2)){ for (var count=0; count<i;
count++) x+=views[week][count]; for (var count=0; count<=i; count++)
y+=views[week][count]; } else { for (var count=0; count<i; count++)
y+=views[week][count]; for (var count=0; count<=i; count++) x+=views[week][count]; }
return "translate(" + x/scale + "," + y/scale + ")"} ); g.append("circle")
.attr("r", function(d){return d/scale}) .style("fill", function(d, i){return colors[i];});
g.append("text") .attr("x", -10) .attr("fill", "grey") .text(function(d){return d});
</script> </body>

通过对比 清单 2与 清单 4就可以看到,修改之处在 transform属性值的计算上,在 清单 4中已经用粗体突出显示。

嵌套圆圈

现在看看清单 5,它是绘制 图 3中的圆圈的 SVG 代码(同时表示查看数据和互动数据的嵌套圆圈):

清单 5. 图 3 的 SVG 代码

<?xml version="1.0"
standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="1000"> <g
transform="translate(100,170.57)"> <circle r="70.57" style="fill: #0000ff;">
</circle> <circle r="20.52" style="fill: #add8e6;"> </circle> <text
x="-15" y="35.52" fill="white">7057</text> <text x="-15"
y="5">2052</text> </g> <g transform="translate(245.4,170.57)">
<circle r="74.83" style="fill: #ffd700;"> </circle> <circle r="20.89"
style="fill: #ffff00;"> </circle> <text x="-15" y="35.89"
fill="white">7483</text> <text x="-15" y="5">2089</text> </g>
<g transform="translate(245.4,282.89)"> <circle r="37.49" style="fill:
#008000;"></circle> <circle r="15.86" style="fill: #90ee90;"></circle>
<text x="-15" y="30.86" fill="white">3749</text> <text x="-15"
y="5">1586</text> </g> <g transform="translate(321.35,282.89)">
<circle r="38.46" style="fill: #ff0000;"></circle> <circle r="14.26"
style="fill: #f08080;"></circle> <text x="-15" y="29.26"
fill="white">3846</text> <text x="-15" y="5">1426</text> </g>
<g transform="translate(321.35,367.33)"> <circle r="45.98" style="fill:
#800000;"></circle> <circle r="26.32" style="fill: #cd5c5c;"></circle>
<text x="-15" y="41.32" fill="white">4598</text> <text x="-15"
y="5">2632</text> </g> </svg>

可以看到,清单 5在每个 <g>标记内有两个 <circle>和两个 <text>标记,而 清单 1只有一对 <circle>和 <text>标记。这很容易理解,因为图 3 必须绘制两个具有相同中心的圆圈,以提供一种嵌套圆圈的视图,而且每个圆圈包含一个关联的数字。

清单 6 显示了生成 图 3的基于 D3 的 JavaScript 代码:

清单 6. 绘制图 3 的 JavaScript

<!DOCTYPE 
html> <meta charset="utf-8"> <body> <script
src="http://d3js.org/d3.v3.min.js"></script> <script> var viewsAndInteraction
= [ [ [7057, 2052], [7483, 2089], [3749, 1586], [3846, 1426], [4598, 2632] ], [ [5972,
2071], [5672, 2190], [9152, 7214], [9725, 3782], [8983, 2721] ], [ [8749, 3076], [4768,
3190], [6738, 4532], [9546, 3825], [6983, 4831] ] ]; var width = 1000, height = 1000; var
viewColors = [ "blue", "gold", "green", "red", "maroon" ]; var interactionColors = [
"lightblue", "yellow", "lightgreen", "lightcoral", "indianred" ]; var week = 0, scale = 100;
var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height)
.selectAll("g") .data(viewsAndInteraction[week]) .enter(); var g = svg.append("g")
.attr("transform", function(d,i){ var x = 10000, y = 10000; if (!(i%2)){ for (var count=0;
count<i; count++) x+=viewsAndInteraction[week][count][0]; for (var count=0; count<=i;
count++) y+=viewsAndInteraction[week][count][0]; } else { for (var count=0; count<i;
count++) y+=viewsAndInteraction[week][count][0]; for (var count=0; count<=i; count++)
x+=viewsAndInteraction[week][count][0]; } return "translate(" + x/scale + "," + y/scale +
")"} ); g.append("circle") .attr("r", function(d){return d[0]/scale}) .style("fill",
function(d, i){return viewColors[i];}); g.append("circle") .attr("r", function(d){return
d[1]/scale}) .style("fill", function(d, i){return interactionColors[i];}); g.append("text")
.attr("x", -15) .attr("y", function(d){return (15 + d[1]/scale);}) .attr("fill", "white")
.text(function(d){return d[0]}); g.append("text") .attr("x", -15) .attr("y", 5)
.text(function(d){return d[1]}); </script> </body>

如果对比 清单 2中的 JavaScript 与 清单 6 中的代码,您可以看到,绝大多数代码都是相同的。区别在于,在 清单 6中:
您必须处理查看数据和互动数据,所以一个 viewsAndInteraction数组同时包含查看数据和互动数据。

有两对 g.append("circle")和 g.append("text")函数调用用于绘制两个圆圈,每个圆圈具有相同的中心且包含文本。

您现在已经看到了所有三幅图的 SVG 和 JavaScript。下一个示例可将会视化导航数据。

导航数据的可视化表示

图 4 使用不同颜色的圆弧和 弦来表示来自第 1 周的一些导航数据:从蓝色资源导航到其他资源的用户数量:

图 4. 显示导航数据的彩色圆弧和弦

图 4中的彩色圆弧表示不同的社交资源。蓝色资源通过弦连接到其他所有资源。每条弦表示一种导航方向。

蓝色弦在蓝色资源上开始和结束,表示开始在蓝色资源上浏览并且没有进一步导航的用户数量。金色弦从蓝色资源开始到金色资源结束,表示从蓝色资源导航到金色资源的用户数量。类似地,图 4描绘了从蓝色资源到剩余资源的导航。

请注意,(从蓝色资源开始的)弦是按升序排列的,首先使用较细的弦表示较低的用户数量。所以您不需要通过数字了解哪些资源获得了更多的用户。

使用 SVG 绘制圆弧和弦

清单 7 给出了绘制 图 4的部分 SVG 代码:

清单 7. 绘制图 4 的部分 SVG 代码

<?xml 
version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500"> <g
transform="translate(250,250)"> <path style="fill: #0000ff;"
d="M1.1633760361312584e-14,-190A190,190 0 0,1
177.80498922385382,-66.97302298019123L140.372
3599135688,-52.873439194887816A150,150 0 0,0
9.18454765366783e-15,-150Z"></path> <path style="fill:
#ffd700;"
d="M177.80498922385382,-66.97302298019123A190,190 0 0,1
141.9432774826573,126.30164677264251L112.06048222315049,99
.71182639945462A150,150 0 0,0 140.3723599135688,-
52.873439194887816Z"></path> <path style="fill: #008000;"
d="M141.9432774826573,126.30164677264251A190,190 0 0,1 -
141.87314929322426,126.38041584684909L-
112.00511786307179,99.77401251067033A150,150 0 0,0
112.06048222315049,99.71182639945462Z"></path> <path
style="fill: #ff0000;"
d="M-141.87314929322426,126.38041584684909A190,190 0 0,1
-136.03537960886078,-132.64379176830383L-
107.39635232278484,-104.7187829749767A150,150 0 0,0 -
112.00511786307179,99.77401251067033Z"></path> <path
style="fill: #800000;"
d="M-136.03537960886078,-132.64379176830383A190,190 0
0,1 - 3.7240908056998534e-13,-190L-2.9400716887104106e-13,-
150A150,150 0 0,0 -107.39635232278484,-
104.7187829749767Z"></path> <g> <!--Drawing SVG code
for chords goes here. --> </g> </g> </svg>

可以看到,清单 7中的根 <svg>标记有一个 <g>子标记,它只有 5 个 <path> 标记和另一个内部 <g>子标记。这 5 个 <path>标记绘制了 5 种不同颜色的圆弧。内部的 <g>标记包装绘制弦的 SVG 代码,这稍后将会解释。首先,我将解释一下 <path>标记如何绘制 5 条圆弧。

绘制一组彩色圆弧

使用适当的颜色绘制一组圆弧,可以使用 SVG <path> 标记轻松完成此操作,这些标记的用途是定义绘制路径,就像使用钢笔在纸上描绘一样。路径可以是封闭的(例如一个三角形)或开放的(例如一组互联的线条)。您需要一个封闭路径来绘制圆弧,如图 5 所示:

图 5. 使用 <path>标记绘制圆弧

在 图 5 中,圆弧的封闭路径的周长使用黑色绘制,封闭路径中填入的是黄色。如果使用钢笔绘制此圆弧,可使用黑色绘制两条弧线和两条直线构成周长线,然后在内部填入黄色。

图 4中的每条圆弧对其周长和封闭路径中的填充色使用了相同的颜色。所以您需要 5 个 <path>标记,每个对应圆圈的一段圆弧。

在 清单 7中,可以看到 5 个 <path> 标记都有两个属性:style和 d。style属性定义了绘制颜色,d属性定义绘制路径。
d属性的值(例如 M8.572244476756641e-15,-140A140,140 0 0,1 93.95877067680189,103.78703875197591L67.11340762628707,74.13359910855422A100,100 0 0,0 6.123031769111886e-15,-100Z)看起来很长、很复杂。官方 SVG 规范要求对此值使用这种格式来定义准确的绘制路径。

该格式中依次为一个 M后跟两个逗号分隔的数字,一个 A 后跟多个数字,一个 L后跟两个逗号分隔的数字,另一个 A 后跟多个数字,最后是一个 Z。这个顺序表示:移动到 M后的数字定义的点,然后绘制 A后的数字定义的一个 弧线,然后绘制 L 后的数字定义的一条 线,最后绘制另一条 弧线(第二条弧线)。末尾的 Z表示 返回到初始位置,从而形成一条封闭路径。

好消息是,您不需要知道如何按这个顺序准确绘制所需圆弧的任何更多细节 —因为所有这些值都使用 D3 库计算,这个库包含绘制复杂图形的易用功能。稍后,我将给出执行所有绘制计算和动态生成 图 4的完整 SVG 代码的 D3 JavaScript 代码。

每个圆圈的 5 个 <path>标记都是绘制圆弧所需要的。在浏览器中打开 Listing7.svg 文件(参见 下载),查看没有任何弦的彩色圆弧,如图 6 所示:

图 6. 彩色圆弧

现在我将介绍如何绘制弦来表示导航。

绘制弦

像圆弧一样,弦使用一个 <g>标记绘制,该标记包装了 5 个 <path>标记(分别用于每根弦),如清单 8 所示:

清单 8. 绘制 5 根弦的 SVG 代码

<?xml
version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500"> <g
transform="translate(250,250)"> <!--Drawing SVG code for arcs goes here. -->
<g> <path d="M9.18454765366783e-15,-150A150,150 0 0,1
19.52349842170555,-148.72401624948697Q 0,0 9.18454765366783e-15,-150Z" style="fill:
#0000ff;"> </path> <path d="M19.52349842170555,- 148.72401624948697A150,150 0
0,1 41.344235247425466,-144.18964668729006Q 0,0
140.3723599135688,-52.873439194887816A150,150 0 0,1 144.99722485611238,-38.41620470616511Q
0,0 19.52349842170555,-148.72401624948697Z" style="fill: #ffd700;"> </path>
<path d="M111.39598065576133,-100.4536484839712A150,150 0 0,1
140.3723599135688,-52.873439194887816Q 0,0 84.8772338498757,123.67641316756206A150,150 0 0,1
50.93706344958661,141.08655345968583Q 0,0 111.39598065576133,-100.4536484839712Z"
style="fill: #008000;"> </path> <path
d="M-149.71409635840777,9.256854302914952A150,150 0 0,1
-140.64142529945457,-52.15351847898602Q 0,0 68.6764412000974,-133.35496400243062A150,150 0
0,1 111.39598065576133,-100.4536484839712Q 0,0 - 149.71409635840777,9.256854302914952Z"
style="fill: #ff0000;"> </path> <path
d="M-59.58349836433014,-137.65829696268898A150,150 0 0,1 -1.6078040591602227e-13,-150Q 0,0
41.344235247425466,-144.18964668729006A150,150 0 0,1 68.6764412000974,-133.35496400243062Q
0,0 - 59.58349836433014,-137.65829696268898Z" style="fill: #800000;"> </path>
</g> </g> </svg>

可以看到,清单 8中弦的 <path>标记都(像 清单 7中的弦一样)有一个较长的、复杂的 d属性值需要使用 D3 来计算。
清单 8为圆弧省略了 <path>标记。如果在浏览器中查看 清单 8中的 SVG 代码,您将看到没有圆弧(因此没有圆圈周长)的弦,如图 7 所示:

图 7. 5组没有圆弧的弦

现在,我将介绍生成 图 4的 SVG 代码的基于 D3 的 JavaScript 代码。

使用 D3 绘制带弦的圆圈

可在清单 9 中的 JavaScript 代码中看到 5 个步骤,它使用了 D3 来创作 图 4的 SVG:

清单 9. 生成图 4 中的 SVG 代码的 5 个步骤

<!DOCTYPE 
html> <meta charset="utf-8"> <body> <script
src="http://d3js.org/d3.v3.min.js"></script> <script> //Step 1: var
navigation = [ [3057, 3483, 8749, 8465, 4598], [ 2371, 7397, 4589, 2861, 8249], [ 5972,
5672, 9152, 9725, 8983], [ 9763, 8462, 9782, 1953, 5182], [ 9567, 1571, 2895, 2783, 1874] ];
var navigationChord = d3.layout.chord() .matrix(navigation); var chordCalculations =
navigationChord.chords; var colors = [ "blue", "gold", "green", "red", "maroon" ]; var width
= 500, height = 500, radius = 150, arcStroke = 40; //Step 2: var svg = d3.select("body")
.append("svg") .attr("width", width) .attr("height", height) //Step 3: .append("g")
.attr("transform", function(d,i){ return "translate(" + width/2 + "," + height / 2 + ")" });
//Step 4: svg.selectAll("path") .data(navigationChord.groups()) .enter() .append("path")
.style("fill", function(d, i) { return colors[i]; }) .attr("d",
d3.svg.arc().innerRadius(radius) .outerRadius(radius+arcStroke)); //Step 5:
navigationChord.sortSubgroups(d3.ascending); svg.append("g") .selectAll("path")
.data(chordCalculations) .enter() .append("path") .attr("d", d3.svg.chord().radius(radius))
.style("fill", function(d, i) { if (i < colors.length) return colors[i]})
.style("opacity", function(d, i){ if (!(i < colors.length)) return 0 }); </script>
</body>

第 1 步是存储数据和设置变量。我将导航数据存储在一个名为 navigation 的数组中,然后将它传递到 d3.layout,后者会在内部处理布局。我将 d3.layout所完成的计算存储在一个名为 chordCalculations的变量中,第 5 步会使用该变量绘制弦。

第 2 步是创作 <svg>包装器并设置它的尺寸。

第 3 步是添加用作所有绘制标记的包装器的 <g>标记。

第 4 步为您在 清单 7中看到其 SVG 代码的圆弧创作 5 个 <path>标记。使用 d3.svg.arc(),它在内部执行所有计算来创建彩色圆弧的所有 5 个 <path>标记的 d属性值。

第 5 步为弦创建了 <path> 标记。我首先将弦按升序排列,然后将弦计算结果传递给 D3 的 4 个神奇的步骤:.selectAll().data().enter().append()。最后,d3.svg.chord() 创建了绘制 5 跳线的 <path>标记的 d 属性值。

结束语

在本文中,我设定了一个场景来发现 SVG 与 D3 携手提供的全部图形功能。第 2 部分将演示 D3 布局的优势,这些布局可执行图形计算,从而在单个 SVG 画布上绘制一个形状的多个副本。我还将展示如何绘制流行度、用户互动和导航数据的图表表示。最后,我将介绍如何将圆圈、圆弧、弦、图形和布局结合到单个 SVG 图形中。

相关文章

基于EA的数据库建模
数据流建模(EA指南)
“数据湖”:概念、特征、架构与案例
在线商城数据库系统设计 思路+效果
 
相关文档

Greenplum数据库基础培训
MySQL5.1性能优化方案
某电商数据中台架构实践
MySQL高扩展架构设计
相关课程

数据治理、数据架构及数据标准
MongoDB实战课程
并发、大容量、高性能数据库设计与优化
PostgreSQL数据库实战培训
 
分享到
 
 


MySQL索引背后的数据结构
MySQL性能调优与架构设计
SQL Server数据库备份与恢复
让数据库飞起来 10大DB2优化
oracle的临时表空间写满磁盘
数据库的跨平台设计
更多...   


并发、大容量、高性能数据库
高级数据库架构设计师
Hadoop原理与实践
Oracle 数据仓库
数据仓库和数据挖掘
Oracle数据库开发与管理


GE 区块链技术与实现培训
航天科工某子公司 Nodejs高级应用开发
中盛益华 卓越管理者必须具备的五项能力
某信息技术公司 Python培训
某博彩IT系统厂商 易用性测试与评估
中国邮储银行 测试成熟度模型集成(TMMI)
中物院 产品经理与产品管理
更多...