2017-08-03 101 views
0

我是新來的d3.js,我目前被困在一個問題上。我正在使用強制有向圖來顯示我的數據關係。這應該允許用戶將新節點添加到現有圖形並繪製2個或更多節點之間的關係鏈接。我的警告是我的數據正從ajax調用中填充,我將其分配給一個變量並將其傳遞給生成該圖的函數。數據的初始負載很好,一切都正常顯示。我的問題是當用戶點擊一個按鈕來添加新節點。在這個動作中,我正在調用ajax來檢索新的未鏈接關係以添加到圖中。我將新檢索的數據添加到節點數組,並嘗試重新繪製整個圖形。但是,我在x & y屬性被設置爲NaN時收到錯誤。我相信這與forceSimulation如何分配這些值有關。我曾嘗試使用simulation.reset(),但它不成功。d3 v4添加新節點強制有向圖

這是我的一些代碼;

初始調用以檢索所有現有關係。

function getGraphData(){ 
$.ajax({ 
    url: [link to rest uri], 
    type: 'GET', 
    contentType: 'application/json' 
}).done(function(response){ 
    drawGraph(response); 
}) 
}; 

這是我第二次調用來檢索一個新的聯合國聯關係

function getNewRelationshipData(){ 
    $.ajax({ 
    url: [link to second rest uri], 
    type: 'GET' 
    contentType: 'application/json' 
    }).done(function(response){ 
    var newNode = response.nodes; 
    updateGraph(); 
    //---same as getGraphData() 
    $.ajax({ 
     url: [link to rest uri], 
     type: 'GET', 
     contentType: 'application/json' 
    }).done(function(response){ 
     var graphData = response; 
     graphData.nodes[graphData.nodes.length] = newNode[0] 
     //assigned relationship data to graphData and appended the newNode value 
     drawGraph(graphData); 
    }) 
    }); 
}; 

function updateGraph(){ 
// clears out old graph 
d3.selectAll("svg > *").remove(); 
}; 

這是我怎麼設置我的圖表。

function drawGraph(relationships){ 
var svg = d3.select("svg"), 
    w = +svg.attr("width"), 
    h = +svg.attr("height); 
var g = svg.append("g"); 
var color = d3.scaleOrdinal(d3.schemeCategory20); 

var simulation = d3.forceSimulation() 
    .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(60)) 
    .force("charge", d3.forceManyBody()) 
    .force("center", d3.forceCenter(w/2, h/2)) 
    .force("attraceForce",d3.forceManyBody().strength(-900)); 

var opacity = 0.05; 
var transitionPeriod = 500; 
var graph = relationships; 
var link = g.selectAll("line") 
.data(graph.links) 
    .enter().append("line") 
    .style("stroke-width", function(d) { return d.value; }) 
    .style("stroke", "#999") 
    .style("opacity", "1") 
    .attr("group",function(d) {return d.group; }) 
    .on("click", function(d) { 
     // This is to toggle visibility - need to do it on the nodes, links & text 
     d3.selectAll("line:not([group='"+d.group+"'])") 
     .transition().duration(transitionPeriod).style("opacity", function() { 
      var currentDisplay = d3.select(this).style("opacity"); 
      currentDisplay = currentDisplay == "1" ? opacity : "1"; 
      return currentDisplay; 
     }); 
     d3.selectAll("circle:not([group='"+d.group+"'])") 
     .transition().duration(transitionPeriod).style("opacity",function() { 
      var currentDisplay = d3.select(this).style("opacity"); 
      currentDisplay = currentDisplay == "1" ? opacity : "1"; 
      return currentDisplay; 
     }); 
     d3.selectAll("text:not([group='"+d.group+"'])") 
     .transition().duration(transitionPeriod).style("opacity",function() { 
      var currentDisplay = d3.select(this).style("opacity"); 
      currentDisplay = currentDisplay == "1" ? opacity : "1"; 
      return currentDisplay; 
     }); 

    }) 

    var node = g 
    .attr("class", "nodes") 
    .selectAll("circle") 
    .data(graph.nodes) 
    .enter().append("circle") 
    .attr("r", 14) 
    .attr("fill", function(d) { return color(d.group); }) 
    .call(d3.drag() 
      .on("start", dragstarted) 
      .on("drag", dragged) 
      .on("end", dragended)) 

    var images = g.selectAll("image") 
    .data(graph.nodes) 
    .enter().append("image") 
    .attr("xlink:href",function(d){ 
     var type = d.type, 
      typeIcon = "", 
     switch(type){ 
     //assigns an image based on the subject type person, address, phone, ect. 
     } 
     return typeIcon; 
    }) 
    // This is the label for each node 
    var text = g.selectAll("text") 
     .data(graph.nodes) 
     .enter().append("text") 
     .attr("dx",12) 
     .attr("dy",".35m") 
     .text(function(d) { return d.id;}) 
     .attr("text-anchor", "middle") 
     .attr("group",function(d) {return d.group;}) ; 

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

    simulation 
     .nodes(graph.nodes) 
     .on("tick", ticked); 

    simulation.force("link") 
     .links(graph.links); 

    function ticked() { 
    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; }); 
    text 
     .attr("x", function(d) { return d.x; }) 
     .attr("y", function(d) { return d.y; }); 
    } 
}); 



//Used to drag the graph round the screen 
function dragstarted(d) { 
    if (!d3.event.active) simulation.alphaTarget(0.3).restart(); 
    d.fx = d.x; 
    d.fy = d.y; 
} 

function dragged(d) { 
    d.fx = d3.event.x; 
    d.fy = d3.event.y; 
} 

function dragended(d) { 
    if (!d3.event.active) simulation.alphaTarget(0); 
    d.fx = null; 
    d.fy = null; 
} 

// This is the zoom handler 
var zoom_handler = d3.zoom() 
    .scaleExtent([1/4, 4]) 
    .on("zoom", zoom_actions); 

//specify what to do when zoom event listener is triggered 
function zoom_actions(){ 
    g.attr("transform", d3.event.transform); 
} 

// initial scaling on the svg container - this means everything in it is scaled as well 
svg.call(zoom_handler) 
.call(zoom_handler.transform, d3.zoomIdentity.scale(0.9,0.9)) 
; 

zoom_handler(svg); 
}; 

我的AJAX數據看起來像這樣

{ 
"nodes":[ 
    {"id": "1", "group": "1", "type": "person", "name":"Jon Doe"}, 
    {"id": "2", "group": "1", "type": "person", "name":"Jane Doe"} 
    //ect list of ~50 
], 
"links":[ 
    {"source": "1", "target":"2"}, 
    //ect list of ~50 
] 
} 

我希望有人與更多的d3.js經驗可以點我在正確的方向。

回答

0

我發佈這裏,以防其他人有同樣的問題。我通過將drawGraph函數分解爲更小的小部件來解決了我的問題。

我將以下內容移至父範圍。

var svg = d3.select("svg"), 
     w = +svg.attr("width"), 
     h = +svg.attr("height), 
     node, 
     link; 

var simulation = d3.forceSimulation(nodes) 
    .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(60)) 
    .force("charge", d3.forceManyBody()) 
    .force("center", d3.forceCenter(w/2, h/2)) 
    .force("attraceForce",d3.forceManyBody().strength(-900)); 

var color = d3.scaleOrdinal(d3.schemeCategory20); 

然後在drawGraph函數中,我做了以下更改。

function drawGraph(nodes,links){ 
    var g = svg.append("g"); 
    link = g.selectAll(".link").data(links,function(d){ return d.target.id; }) 
    link = link.enter() 
     .append("line") 
     .attr("class","link") 
     .style("stroke-width", function(d) { return d.value; }) 
     .style("stroke", "#999") 

    node = g.selectAll(".node").data(nodes,function(d){ return d.id; }) 
    node = node.enter() 
     .append("g") 
     .attr("class","node") 
     .call(d3.drag() 
      .on("start", dragstarted) 
      .on("drag", dragged) 
      .on("end", dragended)) 
    node.append("circle").attr("r", 14).attr("fill",function(d){return color(d.group);}) 
    node.append("text").attr("dy", -15) 
     .text(function(d){ return d.id; }) 
     .attr("text-anchor","middle") 
     .style("fill", "#555"); 

    node.append("image") 
     .attr("xlink:href",function(d){ 
      var type = d.type, 
      typeIcon = "", 
      switch(type){ 
      //assigns an image based on the subject type person, address, phone, ect. 
      } 
     return typeIcon; 
     }) 
     .attr("x", -8) 
     .attr("y", -8) 
     .attr("height", 16) 
     .attr("width", 16); 

simulation.nodes(nodes).on("tick", ticked); 
simulation.force("link").links(links); 

function zoom_actions(){ 
    g.attr("transform", d3.event.transform); 
}; 

var zoom_handler = d3.zoom() 
    .scaleExtent([1/4, 4]) 
    .on("zoom", zoom_actions); 

svg.call(zoom_handler).call(zoom_handler.transform, d3.zoomIdentity.scale(0.9,0.9)); 

zoom_handler(svg); 

}; 

function ticked() { 
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 dragstarted(d) { 
    if (!d3.event.active) simulation.alphaTarget(0.3).restart(); 
    d.fx = d.x; 
    d.fy = d.y; 
} 

function dragged(d) { 
    d.fx = d3.event.x; 
    d.fy = d3.event.y; 
} 

function dragended(d) { 
    if (!d3.event.active) simulation.alphaTarget(0); 
    d.fx = null; 
    d.fy = null; 
} 

然後我添加了下面的函數來設置數據並繪製圖形。

function formatGraphData(relationships){ 
nodes = relationships.nodes; 
links = relationships.links; 
simulation.alpha(0.5).restart(); //<- needed to restart simulation and position nodes 
drawGraph(nodes,links); 
} 

然後,ajax調用被更新爲使用formatGraphData而不是drawGraph。

添加以下到我的CSS文件

.links line{ 
stroke: #999; 
} 
.nodes circle{ 
stroke: #fff; 
stroke-width: 1.5px; 
}