D3+mysql 画Force图

先上图再解释。


所谓力学图(Force),就是用点和线来表示各点之间的关系。比如上面这张错综复杂的关系,就是由一个json数组,然后利用D3画图来得到的。

之前学D3,一直停留在给他数据,然后用layout布局,再用不同的图表的格式来画图就好了。而数据,不是自己写死的,就是从一个json文件或者csv文件读出来的。这样显然不实用,我们实际用到的数据,多数是从数据库读出来的。于是上周就接到了任务,用动态数据来画D3的Force图。


做之前思考了一下,任务的重点就是构造数据。把数据库中读出来的数据构造为标准的json格式提供给D3的接口。说白了,就是字符串的拼接。

还有一个问题是根据要求,从数据库中查数据。这个就需要一定的mysql基础,这里不详细说了。

下面看一下我纠结了很久的代码吧。

  <?php 
  $con= mysql_connect("localhost","root","root");
  if(!$con)
  {
     die('Could not connect: ' . mysql_error());
  }
  mysql_select_db("test", $con);
  mysql_query('set names utf8');

  $des = isset($_REQUEST['Target'])?($_REQUEST['Target']):""; 

  $sql = "select distinct Target,IPaddress from tknifelog where Target= '{$des}' ";
  $query = mysql_query($sql);
  while($result= mysql_fetch_assoc($query)){
    $data[]=$result;
  }
  //一层关系
  $p=0;
  $source = 0;
  $source1 = 0;
  foreach ($data as $row ){
    $Target = $row['Target'];
    $IPaddress = $row['IPaddress'];
    //nodes0
    if($p++==0){
      $node[]= '{"name":"'.$Target.'","group":1}';
    }
    $node[]= '{"name":"'.$IPaddress.'","group":1}';
    //links0
    $source++;
    $link .= '{"source":'.$source.',"target":0,"value":'.rand(1,10).'},';
    $target = $source;
    $source1= $source;
  
  //二层关系
    $sql1 = "select distinct Target,IPaddress from tknifelog where IPaddress = '{$IPaddress}'";
    $query1 = mysql_query($sql1);
    while ($result1 = mysql_fetch_assoc($query1)) {
        $data1[] = $result1;
      }
    foreach ($data1 as $row1) {
      $Target1 = $row1['Target'];
      $IPaddress1 = $row1['IPaddress'];
      //nodes1
      if (!in_array('{"name":"'.$Target1.'","group":2}',$node)) {
        $node[]= '{"name":"'.$Target1.'","group":2}';
        //links1
      $source1++;
      $link .= '{"source":'.$source1.',"target":'.$target.',"value":'.rand(1,10).'},';
      $source++;
    }
      }
      
  }



 $json_n = json_encode($node); 
 $nodes = '{"nodes":'. $json_n ;
 $json_l= json_encode($link);
 $links = '"links":['.$json_l.']}';
 
 $json_f = $nodes.$links;

   $new1 = str_replace('\"','"',$json_f);
   $new2 = str_replace('"{','{',$new1);
   $new3 = str_replace('}"','}',$new2);
   $new4 = str_replace('}]"','}],"', $new3);
   $json = str_replace('},"]}','}]}', $new4);


file_put_contents("force.json",$json);

 // echo "json文件已生成";
  ?>
<html>
<head>
        <meta charset="utf-8">  
        <title>Force</title>  
        <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">

<style>

.link {
  stroke: #666;
}

.node text {
  pointer-events: none;
  font: 10px sans-serif;

.container {
  margin-top: 30px;
}
}

</style>
</head>
<body>  
<div class="container">
  <div class="row">
    <div class="col-md-3">
      <table class="table">
        <thead>
          <tr>
            <th>Target</th>
            <th>Source</th>
          </tr>
        </thead>
        <tbody>
        <?php
            foreach($data as $row){
        ?>
          <tr>
            <td><a href="test.php?Target=<?php echo $row['Target']?>"><?php echo $row['Target']; ?></a></td>
            <td><?php echo $row['IPaddress']; ?></td>
          </tr>
        <?php
        }
        ?>
        </tbody>
      </table>
    </div>
    <div class="col-md-9 graph"></div>
  </div>
</div>
<script src="js/d3.v3.js" charset="utf-8"></script>  
<script>    


    
    var width = 800;
    var height = 600;
    var img_w = 17;
    var img_h = 17;

    var color = d3.scale.category20();

    var zoom = d3.behavior.zoom()
          .scaleExtent([1, 10])
          .on("zoom", zoomed);

  var root = d3.select(".graph").append("svg")
      .attr("width", width)
      .attr("height", height)
      .call(zoom);

var svg = root.append('svg:g'); 

var force = d3.layout.force()
    .gravity(.05)
    .distance(100)
    .charge(-100)
    .size([width, height]);

d3.json("force.json", function(error, json) {
  if (error) throw error;

  force
      .nodes(json.nodes)
      .links(json.links)
      .start();

  var link = svg.selectAll(".link")
      .data(json.links)
    .enter().append("line")
      .attr("class", "link");

  var node = svg.selectAll(".node")
      .data(json.nodes)
      .enter().append("g")
      .attr("class", "node")
      .call(force.drag);

  node.append("circle")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); });

  node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) { return d.name });

  var drag =force.drag()
            .on("dragstart",function(d,i){
              d3.event.sourceEvent.cancelBubble = true;
              d.fixed = true;    //拖拽开始后设定被拖拽对象为固定
            });


  force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

        //限制结点的边界
        json.nodes.forEach(function(d,i){
          d.x = d.x - img_w/2 < 0     ? img_w/2 : d.x ;
          d.x = d.x + img_w/2 > width ? width - img_w/2 : d.x ;
          d.y = d.y - img_h/2 < 0      ? img_h/2 : d.y ;
          d.y = d.y + img_h/2 > height ? height - img_h/2 : d.y ;
        });

    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .on("dblclick",function(d,i){
                  d.fixed = false;
                })
        .call(drag);


  });
});

  function zoomed () {
        svg.attr("transform", 
          "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
      } ;
        </script>  
    
    </body>  
</html>  

首先,我用的数据源,是一些源和目的地址,第一层关系是查数据库中有多少不同的目标地址,列出来。(代码中没有显示这一部分)点击相应的目标地址,可以展示出数据库中和他有关联的源地址,然后以该目标地址为中心,源地址为他的下一层关系连接。第二层关系是查出源地址有关的目标地址(去重)。

画出的图是这样的:


前面的php代码是用来查数据,然后看到查出的数据构造出了nodes和links,这是force图要求的数据格式。构造的过程中可能会遇到很多问题,用字符串替换等函数来变换到需要的格式。

在第二层关系时,要注意links中target和source的关系,和他们与第一层的关系。(脑袋不好想了半天才理清楚啊真是要被自己蠢哭了)

最后把构造好的字符串生成一个json文件备用。


接下来就是重点,用D3画图了。

前面介绍过,引入D3的库就可以用D3的函数画图了!

先定义一些必要的参数,宽高什么的。(这里图片的宽高是这样的,第一版代码的时候节点是一个小爱心的图片,在限制节点边界的时候为了让图整个都在页面内,就要用到图片的宽高。但是为了更清楚的显示节点的层级,后来就用了不同group不同颜色的点点来表示。懒了就没有改图片宽高这里。)

接着定义颜色,用D3中定义好的20中随机的颜色组。

zoom是一个放大平移的函数。

接下来开始画图。

在html代码中有一个class是graph的div,我们的svg就要画在这上面。接着添加属性。重点:要再嵌套一层svg:g,因为在zoom起作用的时候,为了让他对整体放大或者平移而不是只对其中的点或者线作用,就要把点线这些元素放在一个容器中,然后对这个容器调用zoom。

d3.layout.force()是力学图的布局,下面的参数根据需要填写就是了。

接下来导入数据,把php生成的json数据导入。注意:接下来画图的时候就要把下面的内容放在数据的作用域中。

force的nodes和links是json中的nodes和links。接下来就是画边和点,注意到点还调用了drag。

节点用circle表示,填充定义好的颜色。

在节点上添加文字,就是节点中的name。

再然后就是拖拽函数。这里有一句是防止冒泡事件。也就是拖拽这件事节点也有,zoom事件也有,单击时就会有问题。所以用这句代码来防止冒泡事件。

下一句是拖拽结束后固定顶点位置。

下面是更新图的位置。就是在拖拽之后。还要限制节点的边界。最后是双击固定的节点之后可以解除固定。

在数据作用域的外面是zoom函数。

这样就画出了如上的D3图。

对了,展示一下json的格式。

{"nodes":[
	{"name":"180.149.153.216:443","group":1},
	{"name":"3062634345","group":1},
	{"name":"180.149.153.216:443","group":2},
	{"name":"3062635806","group":1},
	{"name":"3723292305","group":1},
	{"name":"1987589676","group":1},
	{"name":"1987589733","group":1},
	{"name":"1987591229","group":1},
	{"name":"3062644032","group":1},
	{"name":"1987584314","group":1},
	{"name":"1987585124","group":1},
	{"name":"3062640123","group":1},
	{"name":"1857966460","group":1},
	{"name":"1857967040","group":1},
	{"name":"1987771852","group":1},
	{"name":"3723291694","group":1},
	{"name":"2101543622","group":1},
	{"name":"180.149.139.248:443","group":2}],
"links":[
	{"source":1,"target":0,"value":9},
	{"source":2,"target":1,"value":7},
	{"source":3,"target":0,"value":8},
	{"source":4,"target":0,"value":10},
	{"source":5,"target":0,"value":1},
	{"source":6,"target":0,"value":2},
	{"source":7,"target":0,"value":4},
	{"source":8,"target":0,"value":6},
	{"source":9,"target":0,"value":1},
	{"source":10,"target":0,"value":9},
	{"source":11,"target":0,"value":6},
	{"source":12,"target":0,"value":3},
	{"source":13,"target":0,"value":4},
	{"source":14,"target":0,"value":8},
	{"source":15,"target":0,"value":7},
	{"source":16,"target":0,"value":8},
	{"source":17,"target":16,"value":1}]}

嗯,先写到这吧。以后有什么再补充。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值