D3js - 尝试更新强制布局中的链接

时间:2014-01-30 14:55:11

标签: javascript d3.js force-layout

我想在运行时更新强制定向布局的链接和节点。但这种行为很奇怪,因为有时它不会添加新的链接,有时也不会删除旧的链接。你有什么建议吗?

任何帮助都将不胜感激。

这是我的代码:

network.js

// Network View size
var width = 1280,
height = 500

var radius = 200;
var robj   = 8;
var scaled_radius = d3.scale.linear()
  .domain([0, 7000000000000])
  .range([10, 40]);

var svg_network = d3.select(document.createElementNS(d3.ns.prefix.svg, "svg"))
                  .attr("width", width)
                  .attr("height", height);

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

var timestamp_info = svg_network.append("text")
      .attr("dx", 10)
      .attr("dy", 10);

function graph_network_start(flowz) 
{
  /* ------------------------ */
  /* DATA PREPROCESSING       */
  /* ------------------------ */

  flowz = network_preprocess(flowz) 

  /* ------------------------ */
  /* NODE POSITIONING         */
  /* ------------------------ */

  nodePositioning(flowz.activeNodes);
  var x = d3.scale.linear().domain([0, flowz.activeNodes.length]).range([0, 180]);

  // Bind link and node data to DOM elements
  var link = svg_network.selectAll(".link").data(flowz.flow, function(d) { return d.source + "-" + d.target;  });
  var node = svg_network.selectAll("g.node").data(flowz.activeNodes)//, function(d,i) {return i;});  

  /* ------------------------ */
  /*  UPDATE LINKS            */
  /* ------------------------ */
  link.exit().transition().duration(10).remove();

  link.enter().append("line")
    .attr("id",function(d){return d.source.ID + "-" + d.target.ID;})
    .attr("class", "link")
    .style("opacity",0)
    .transition()
    .duration(1000)
    .style("opacity",1);

  /* ------------------------ */
  /*  UPDATE NODES            */
  /* ------------------------ */

  node.exit().transition().duration(100).remove();

  var newNode = node.enter().append("svg:g")
    .attr("id", function(d) {return d.ID})
    .attr("class", "node")
    .call(force.drag).on("click", function(d){
    if (nodes[d.ID].name == "Workstations Site 1") {
        selectedSite=1; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
    } else if (nodes[d.ID].name == "Workstations Site 2") {
        selectedSite=2; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
    } else if (nodes[d.ID].name == "Workstations Site 3") {
        selectedSite=3; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
    }})
    .on("mouseover", fade(.1)).on("mouseout", fade(1))

  newNode.append("circle")
      .attr("r", 8) //function(d){return scaled_radius(d.output)})
      .style("fill", color)
      .style("stroke", "black" )
      .style("opacity",0)
      .transition()
      .duration(1000)
      .style("opacity",1);

  newNode.append("text")
      .attr("dx", robj + 2)
      .attr("dy", ".1em")
      .text(function(d) { return nodes[d.ID].name })
      .style("opacity",0)
      .transition()
      .duration(1000)
      .style("opacity",1);
//if (nodes[d.ID].name == "Workstations Site" || nodes[d.ID].name == "Workstations Site 2" || nodes[d.ID].name == "Workstations Site 3") return nodes[d.ID].name });

  /* ------------------------ */
  /*  UPDATE INFOS            */
  /* ------------------------ */
  timestamp_info.text(function(d) { return new Date(flowz.timestamp).toString()});

  force
    .nodes(flowz.activeNodes)
    .links(flowz.flow)
    .on("tick", tick);
  force.start();

  var linkedByIndex = {};
  flowz.flow.forEach(function(d) {
      linkedByIndex[d.source + "," + d.target] = 1;
  });

  function color(d){
    return "steelblue"
  }

  function isConnected(a, b) {
    return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
  }

  function tick()
  {
    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; });

    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
  }

  function fade(opacity) {
    return function(d) {
        node.style("stroke-opacity", function(o) {
            thisOpacity = isConnected(d, o) ? 1 : opacity;
            this.setAttribute('fill-opacity', thisOpacity);
            return thisOpacity;
        });

        link.style("stroke-opacity", function(o) {
            return o.source === d || o.target === d ? 1 : opacity;
        });
    };
  }

  function nodePositioning(nodesparam){
    for (var i=0; i<nodesparam.length; i++)
    {
      if(nodes[nodesparam[i].ID].name == "Internet")
      {
        nodesparam[i].x=width/2;
        nodesparam[i].y=30;
      }
      else if (nodes[nodesparam[i].ID].name == "Firewall")
      {
        nodesparam[i].x=width/2;
        nodesparam[i].y=50;
      }
      else if (nodes[nodesparam[i].ID].name == "172.0.0.1")
      {
        nodesparam[i].x=width/2;
        nodesparam[i].y=120;
      }

      else
      {
        nodesparam[i].x = width/2 + (radius - robj) * Math.cos(Math.PI + i * (Math.PI / nodesparam.length +1)) ;
        nodesparam[i].y = height/2 + (radius - robj) * Math.sin(Math.PI + i * (Math.PI / nodesparam.length +1)) ;
      }
      nodesparam[i].fixed = true;
    }
  }

  function positionX(node, index)
  {
    if(node.name == "Internet" || node.name == "Firewall" || node.name == "127.0.0.1")
    {
        return width/2;
    }
    else
    {
        return (width/2 + (radius - robj) * Math.cos(Math.PI + index * (Math.PI / nodes.length +1))) ;
    }
  }

  function positionY(node, index)
  {
      if(node.name == "Internet")
      {
        return 30;
      }
      else if (node.name == "Firewall")
      {
        return 50;
      }
      else if (node.name == "172.0.0.1")
      {
        return 120;
      }
      else
      {
        return (height/2 + (radius - robj) * Math.sin(Math.PI + index * (Math.PI / nodes.length + 1))) ;;
      }
  }

  function network_preprocess(flowz){
    for(var i=+0; i<flowz.flow.length;i++)
    {
      for(var j=0; j<flowz.activeNodes.length;j++)
      {
        if(flowz.activeNodes[j].ID == flowz.flow[i].source)
          flowz.flow[i].source = j;
        if(flowz.activeNodes[j].ID == flowz.flow[i].target)
          flowz.flow[i].target = j;
      }     
    }
    return flowz
  }
}

links.json摘录

[
  {
  "timestamp": 1364795760000,
  "flow": [
    {
      "source": 0,
      "target": 1,
      "value": 15540
    }
  ],
  "activeNodes": [
    {
      "output": 15540,
      "ID": 0
    },
    {
      "output": 0,
      "ID": 1
    }
  ]
},
{
  "timestamp": 1364795880000,
  "flow": [
    {
      "source": 2,
      "target": 1,
      "value": 2960
    },
    {
      "source": 0,
      "target": 1,
      "value": 14800
    }
  ],
  "activeNodes": [
    {
      "output": 14800,
      "ID": 0
    },
    {
      "output": 0,
      "ID": 1
    },
    {
      "output": 2960,
      "ID": 2
    }
  ]
}
]

编辑:1 我可以缩小范围。我的代码正在销毁links.source和links.target元素。由于某种原因,它们从int更改为对象:

时间[0]的输出:

Object {source: 3, target: 4, value: 213143231}
Object {source: 5, target: 4, value: 448560}  

当我转到另一个数据集并回到时间[0]时:

Object {source: Object, target: Object, value: "213143231"}
Object {source: Object, target: Object, value: "448560"}

甚至将值更改为字符串。也许它发生在这段代码摘录中:

function graph_network_start(flowz) 
{
flowz = network_preprocess(flowz);
...
var link = svg_network.selectAll(".link").data(flowz.flow, function(d) { console.log(d);   return d.source + "-" + d.target;  });
var node = svg_network.selectAll("g.node").data(flowz.activeNodes, function(d) {return d.ID})  
...
force
.nodes(flowz.activeNodes)
.links(flowz.flow)
.on("tick", tick);
force.start();


function network_preprocess(flowz){
  /* 
    We have to remove not existing nodes and add new nodes to our network nodes
  */ 

  // Remove every node which does not exist anymore
  var activeSet = new HashSet();
  activeSet.addAll(flowz.activeNodes);
  var toRemove = network_nodes.complement(activeSet).values();
  for(var i=0; i<toRemove.length; i++)
  {
    network_nodes.remove(toRemove[i]);
  }

  // Add new nodes
  var activenodes = flowz.activeNodes;
  for(var i=0; i<activenodes.length; i++)
  {
    if (! network_nodes.contains(activenodes[i]) )
    {
      network_nodes.add(activenodes[i])
    }
  }

  // Order nodes
  var ufzuffu = network_nodes.values();
  ufzuffu.sort(function(a,b){
    var keya = a.ID;
    var keyb = b.ID;
    if(keya < keyb) return -1;
    if(keya > keyb) return 1;
    return 0;
  });
  flowz.activeNodes = ufzuffu;
  // Edit links
  for(var i=0; i<flowz.flow.length;i++)
  {
    for(var j=0; j<flowz.activeNodes.length;j++)
    {
      if(flowz.activeNodes[j].ID == flowz.flow[i].source)
      {
          flowz.flow[i].source = +j;
          continue;
      }
      if(flowz.activeNodes[j].ID == flowz.flow[i].target)
      {
        flowz.flow[i].target = +j;
      }
    }     
  }
    return flowz
  }
}

编辑2

嗯..看起来我发现了一个解决方法:

var link = svg_network.selectAll(".link").data(flowz.flow, function(d) { 
    if(d.source.hasOwnProperty("ID"))
      return d.source.index + "-" + d.target.index;
    else
      return d.source + "-" + d.target;
      });

它有效,但我仍然不知道它为什么随机(?)生成整数对象..

0 个答案:

没有答案