2015-06-30 57 views
0

我有一個按預期工作的強制佈局圖。它從磁盤上的JSON讀取圖形並顯示它。我現在有服務器使用套接字io將數據推送到客戶端頁面。該數據是包含節點和鏈接數據的JSON。如何動態更新d3.js強制佈局圖?

我怎麼有力量佈局刷新,當它從服務器接收新的JSON更新的佈局?

var width = 1900, 
height = 1100; 

var color = d3.scale.category20(); 

var force = d3.layout.force() 
    .charge(-120)   
    .linkDistance(30) 
    .size([width, height]); 

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 


var networkData = {}; 
var socket = io.connect('http://localhost:3000'); 

socket.on("networkData", function (data) { 
    networkData = data; //This is the data I want to use 
}); 


d3.json("data.json", function(error, graph) { //"data.json" is the data 
    if (error) throw error;     // currently being used 
               // want to use "networkData" 
force 
    .nodes(graph.nodes) 
    .links(graph.links) 
    .start(); 

var link = svg.selectAll(".link") 
    .data(graph.links)   
    .enter().append("line") 
    .attr("class", "link") 
    .style("stroke-width", function(d) { return Math.sqrt(d.value); }); 

var node = svg.selectAll(".node") 
    .data(graph.nodes) 
    .enter().append("circle") 
    .attr("class", "node") 
    .attr("r", 5) 
    .style("fill", "orange") 
    .call(force.drag); 

node.append("title") 
    .text(function(d) { return d.name; }); 

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; }); 

    node.attr("cx", function(d) { return d.x; }) 
    .attr("cy", function(d) { return d.y; }); 
}); 
}); 

編輯

我已經更新了代碼奎勒莫·加西亞提示如下:

socket.on("networkData", function (data) { 
    force.stop() 
    render(data); 
    force 
     .nodes(data.nodes) 
     .links(data.links) 
     .start();    
}); 

function render(data) { 

    var link = svg.selectAll(".link") 
     .data(data.links)  
     .enter().append("line") 
     .attr("class", "link") 
     .style("stroke-width", function(d) { return Math.sqrt(d.value); }); 

    var node = svg.selectAll(".node") 
     .data(data.nodes) 
     .enter().append("circle") 
     .attr("class", "node") 
     .attr("r", 5) 
     .style("fill", "orange") 
     .call(force.drag); 

    node.append("title") 
     .text(function(d) { return d.name; }); 

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; }); 

    node.attr("cx", function(d) { return d.x; }) 
    .attr("cy", function(d) { return d.y; }); 
}); 
} 

然而,圖只適用於從服務器發送的第一networkData JSON。我還必須手動刷新頁面才能顯示此數據。

爲什麼數據不適用於>第二個數據文件?我怎樣才能讓頁面動態更新? (即不必手動刷新)

回答

1

您是否嘗試過創建渲染功能,並調用它在socket.io事件?

渲染功能所應有的一切從var link = svg.selectAll(".link")代碼force.on("tick", function() {

var link = svg.selectAll(".link") 
.data(graph.links)  

... 
... 

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; }); 

    node.attr("cx", function(d) { return d.x; }) 
    .attr("cy", function(d) { return d.y; }); 
}); 

祝你好運!

+0

我已經用您的建議更新了OP,但遇到了更多的問題。 – Shengus

+0

創建一個帶有錯誤的jsfiddle是否太麻煩了?此外,看看這個答案,我認爲這是一個類似的情況http://stackoverflow.com/a/18378803/1923565 –

+0

我不認爲有可能模仿推送數據從服務器使用jsfiddle。我會研究其他線索的想法。 – Shengus

0

也許這可能是很難實現的你的情況有點,但我敢肯定,我的代碼可以給你至少有一些想法。因此,這些都是我基本上使用功能清理介紹和從數據庫中動態地添加新的節點。你可以看到結果here。這些行還檢查是否有重複項並將其過濾掉。如果您需要更多定義或變量,請告訴我。

 cleanPresentation: function() { 
      svg.remove(); 
      nodeCircles = {}; 
      alreadyThere = false; 
     }, 
     getAlreadyThere: function() { 
      return alreadyThere; 
     }, 
     createGraph: function (newJSON) { 
      if (alreadyThere) { 
       svg.remove(); 
       nodeCircles = {}; 
      } 
      this.updateForceUsingNewNodes(this.generateObjects(newJSON)); 
      currentJSON = newJSON; 
      if (alreadyThere == false) { 
       this.setbasiczoom(); 
      } 
      alreadyThere = true; 
     }, 
     updateGraph: function (newJSON) { 
      svg.remove(); 
      this.findDuplicatesAndSetEmpty(newJSON); 
      this.deleteEmptyObjectsInJSON(newJSON); 
      currentJSON = currentJSON.concat(newJSON); 
      this.updateForceUsingNewNodes(this.generateObjects(currentJSON)); 
     }, 
     findDuplicatesAndSetEmpty: function (newJSON) { 
      for (var i = 0; i < currentJSON.length; i++) { 
       for (var o = 0; o < newJSON.length; o++) { 
        if ((currentJSON[i].source.ID == newJSON[o].source) && (currentJSON[i].target.ID == newJSON[o].target) 
         || (currentJSON[i].source.ID == newJSON[o].target) && (currentJSON[i].target.ID == newJSON[o].source)) { 
         newJSON[o] = {}; 
        } 
       } 
      } 
     }, 
     deleteEmptyObjectsInJSON: function (json) { 
      for (var i = 0; i < json.length; i++) { 
       var y = json[i].source; 
       if (y === "null" || y === null || y === "" || typeof y === "undefined") { 
        json.splice(i, 1); 
        i--; 
       } 
      } 
     }, 
     updateGraphByRemoveElement: function (clickedNode, index) { 
      svg.remove(); 
      var json4Splicing = currentJSON; 
      for (var i = 0; i < json4Splicing.length; i++) { 
       if (json4Splicing[i].source.ID == clickedNode.ID) { 
        json4Splicing[i] = {}; 
       } else if (json4Splicing[i].target.ID == clickedNode.ID) { 
        json4Splicing[i] = {}; 
       } 
      } 
      familytree.deleteEmptyObjectsInJSON(json4Splicing); 
      familytree.deleteNode(force.nodes(), clickedNode); 
      currentJSON = json4Splicing; 
      familytree.updateForceRemoveElement(familytree.generateObjects(currentJSON)); 
     }, 
     deleteNode: function (allNodes, clickedNode) { 
      allNodes.forEach(function (node) { 
       if (node == clickedNode) { 
        force.links().forEach(function (link) { 
         if (node.ID == link.source.ID) { 
          link.target.linkCount--; 
         } 
         if (node.ID == link.target.ID) { 
          link.source.linkCount--; 
         } 
        }); 
        node.linkCount = 0; 
       } 
      }); 
     }, 
     generateObjects: function (json) { 
      json.forEach(function (link) { 
       if (typeof(link.source) == "string") { 
        link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, significance: link.sourceSign, uniquename: link.sourceUName, ID: link.source, class: link.sourceClass, relation: link.relation, race: link.sourceRace, linkCount: 0}); 
        link.source.linkCount++; 
       } 
       if (typeof(link.target) == "string") { 
        link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, significance: link.targetSign, uniquename: link.targetUName, ID: link.target, class: link.targetClass, relation: link.relation, race: link.targetRace, linkCount: 0}); 
        link.target.linkCount++; 
       } 
      }); 
      return json; 
     }, 
     updateForceRemoveElement: function (links) { 
      force.nodes(d3.values(nodeCircles).filter(function (d) { 
       return d.linkCount; 
      })); 
      force.links(d3.values(links)); 
      familytree.initializeGraph(); 
     }, 
     updateForceUsingNewNodes: function (links) { 
      force.nodes(d3.values(nodeCircles).filter(function (d) { 
       return d.linkCount; 
      })); 
      force.links(d3.values(links)); 
      this.initializeGraph(); 
     }