D3+mysql 画Force图

来源:互联网 发布:origin点显示数据 编辑:程序博客网 时间:2024/06/11 17:41

先上图再解释。


所谓力学图(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}]}

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


0 0