• 【 D3.js 高级系列 — 2.0 】 捆图


    捆图(Bundle)是 D3 中比較奇特的一个布局,仅仅有两个函数,并且须要与其它布局配合使用。

    本文讲述捆图的制作方法。

    201

    有关捆图的样例极少,非常easy找到的是:http://bl.ocks.org/mbostock/1044242


    可是此例有些复杂,不太easy弄懂。并且也没有相应的说明文字(D3官网上的例大多没有相应文字讲解),根本无法作为參考。本文提供一个简单的样例。讲解捆图究竟是什么,应该怎么做。


    捆图仅仅有两个函数:


    d3.layout.bundle():创建一个捆图布局。


    bundle(links):依据数组links的source和target,计算路径。


    捆图的布局之所以函数少,是因为它常与其它层级布局一起使用。所谓层级布局,是指採用嵌套结构(父子节点关系)来描写叙述节点信息,依据层级布局扩展出来的布局即:集群图、打包图、分区图、树状图、矩阵树图。最常见的是与集群图一起使用。使用集群图布局计算节点的位置,再用捆图布局计算连线路径。

    也就是说。捆图布局仅仅干一件事:


    计算连线的路径。


    以下举一个样例。

    中国的高铁已经在非常多城市开通。如北京到上海,北京到桂林等。

    现要制作一图来表示经过哪一座城市的高铁最密集。


    1. 数据

    有9座城市:

    var cities = {
         name: "",
         children:[
         	{name: "北京"},{name: "上海"},{name: "杭州"},
         	{name: "广州"},{name: "桂林"},{name: "昆明"},
         	{name: "成都"},{name: "西安"},{name: "太原"}
         ]
     };


    这9座城市所属的节点有一个公共的父节点,父节点名称为空,稍后并不绘制此父节点。另外还有连接各城市高铁的数组,例如以下:

    var railway = [
    			{source: "北京", target: "上海"},
    			{source: "北京", target: "广州"},
    			{source: "北京", target: "杭州"},
    			{source: "北京", target: "西安"},
    			{source: "北京", target: "成都"},
    			{source: "北京", target: "太原"},
    			{source: "北京", target: "桂林"},
    			{source: "北京", target: "昆明"},
    			{source: "北京", target: "成都"},
    			{source: "上海", target: "杭州"},
    			{source: "昆明", target: "成都"},
    			{source: "西安", target: "太原"}
    ];	//(该数据为如果。并没有经过调查)


    source和target分别表示高铁的两端。

    2. 布局(数据转换)

    前面提到,捆图布局要和其它布局联合使用,在这里与集群图布局联合。

    分别创建一个集群图布局和一个捆图布局。

    var cluster = d3.layout.cluster()
    				.size([360, width/2 - 50])
    				.separation(function(a, b) { 
    return (a.parent == b.parent ?

    1 : 2) / a.depth; }); var bundle = d3.layout.bundle();


    从集群图布局的參数能够看出。接下来节点将要分布呈圆形。捆图布局没有參数能够设置,仅仅创建就可以,保存在变量bundle中。


    先使用集群图布局计算节点:

    var nodes = cluster.nodes(cities);
    console.log(nodes);


    将计算后的节点数组保存在nodes中,并输出该数组,结果如图1所看到的,第一个节点有9个子节点,其它的节点都有且仅仅有一个父节点,没有子节点。

    这是接下来捆图要使用的节点数组,可是却是用集群图布局计算而来的。

    10-7-2

    图1


    下一步是重点,要使用数组railway。

    因为railway中存储的source和target都仅仅有城市名称,因此先要将其相应成nodes中的节点对象。

    写一个函数,按城市名将railway中的source和target替换成节点对象。

    function map( nodes, links ){
    var hash = [];
    	for(var i = 0; i < nodes.length; i++){
    		hash[nodes[i].name] = nodes[i];
    	}
    	var resultLinks = [];
    	for(var i = 0; i < links.length; i++){
    		resultLinks.push({  	source: hash[ links[i].source ], 
    								target: hash[ links[i].target ]
    							});
    	}
    	return resultLinks;
    }


    使用该函数返回的数组,就可以作为捆图布局bundle的參数使用:

    var oLinks = map(nodes, railway);
    	console.log(oLinks);
    		
    var links = bundle(oLinks);
    console.log(links);


    map()返回的结果保存在oLinks。bundle()返回的结果保存在links中。输出结果分别如图2和图3所看到的,即捆图布局的连线数组转换前后的变换。

    10-7-1

    图2


    如图2。转换前,连线数组的每一项都仅仅有两个变量:source和target。内容是节点对象。对于第一个连线,是从“北京”到“上海”。如图3。转换之后。source和target不见了。取而代之的是0、1、2。变成了一个数组。

    非常明显。该数组的第一项和source的内容一样,最后一项和target的内容一样,但中间多出了一项(图3的红框内)。

    多出的这一项是依据source和target公共的父节点计算出来的。于是。该数组表示了一条路径。


    事实上,捆图布局依据各连线的source和target为我们计算了一条条连线路径,我们能够把捆图布局的作用简单地解释为:使用这些路径绘制的线条能更美观地表示“经过哪座城市的高铁最多”

    10-7-3

    图3


    3. 绘制

    经过捆图布局转换后的数据非常适合用d3.svg.line()和d3.svg.line.radial()来绘制,前者是线段生成器,后者是放射式线段生成器。

    在line.interpolate()所提前定义的插值模式中,有一种就叫做bundle,正是为捆图准备的。


    因为本例中用集群图布局计算节点数组使用的使用圆形的,因此要用放射式的线段生成器。先创建一个:

    var line = d3.svg.line.radial()
    					.interpolate("bundle")
    					.tension(.85)
    					.radius(function(d) { return d.y; })
    					.angle(function(d) { return d.x / 180 * Math.PI; });


    此线段生成器是用来获取连线路径的。接下来,加入一个分组元素<g>,用来放全部与捆图相关的元素。

    gBundle = svg.append("g")
    				.attr("transform", 
    "translate(" + (width/2) + "," + (height/2) + ")");
    
    var color = d3.scale.category20c();	//颜色比例尺


    然后,先在gBundle中加入连线路径:

    var link = gBundle.selectAll(".link")
    			  .data(links)
    			  .enter()
    			  .append("path")
    			  .attr("class", "link")
    			  .attr("d", line);	//使用线段生成器


    在该连线的样式中,加入透明度能够在连线汇聚处更能显示出“捆”的效果。比如样式设定为:

    .link {
    		fill: none;
    		stroke: black;
    		stroke-opacity: .5;
    		stroke- 8px;
    	}


    连线的绘制结果如图4所看到的。

    10-7-4

    图4


    最后。向图中加入节点。

    节点用一个圈。里面写上城市的名称来表示。首先,绑定节点数组。并加入与之相应的<g>元素。

    var node = gBundle.selectAll(".node")
    			  .data( nodes.filter(function(d) { return !d.children; }) )
    			  .enter()
    			  .append("g")
    			  .attr("class", "node")
    			  .attr("transform", function(d) {
    					return "rotate(" + (d.x- 90) + ")translate(" 
    + d.y + ")" + "rotate("+ (90 - d.x) +")"; 
    			  });


    要注意,被绑定的数组是经过过滤后的nodes数组。

    此处的filter是JavaScript数组对象自身的函数,粗体字的部分表示仅仅绑定没有子节点的节点。也就是说9座城市的公共父节点不绘制。

    然后仅仅要在该分组元素<g>中分别加入<circle>和<text>就可以。

    node.append("circle")
    			  .attr("r", 20)
    			  .style("fill",function(d,i){ return color(i); });
    			
    	node.append("text")
    			.attr("dy",".2em")
    			.style("text-anchor", "middle")
    			.text(function(d) { return d.name; });


    结果如图5所看到的。因为经过北京的高铁线路最多,连线在北京的圆圈处最密集,就好像将非常多条绳子“捆”在这里一样。

    10-7-5 

    图5

     


    当节点和连线变得非常多非常复杂时。就变成了本文开头处的样例。就像本文开头说的一样,那个样例完毕度太高,根本不能提供參考。希望本文的样例能给大家作为捆图的參考范例。


    完整代码请点击以下链接查看:


    http://www.ourd3js.com/demo/G-2.0/bundle.html


    谢谢阅读。

    文档信息

  • 相关阅读:
    Codeforces 777B Game of Credit Cards
    Codeforces 777A Shell Game
    零基础学贪心算法
    1283 最小周长
    容斥原理
    Humble Numbers(丑数) 超详解!
    1284 2 3 5 7的倍数
    1305 Pairwise Sum and Divide
    1347 旋转字符串
    HDU 2549 壮志难酬
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5170952.html
Copyright © 2020-2023  润新知