2013-08-30 3 views
1

d3 및 강제 레이아웃을 사용하여 그래프를 만들려고합니다. 나는 또한 이미지를 필요d3js 펼치기 후 노드 클릭시 숨기기/숨김 해제 레이아웃을 강제로 적용합니다.

enter image description here

그래서 나는 내가 그들을 추가 할 수 있는지 아이디어를 얻을이 예 http://bl.ocks.org/mbostock/950642 보았다 라벨 : 나는 시작하는 다음의 예 http://bl.ocks.org/mbostock/1062288을 사용했다.

노드와의 사용자 상호 작용에 따라 그래프도 커지므로 사용자가 자식이없는 노드를 클릭하면 아약스 요청이 백엔드 서비스로 이동하여 더 많은 노드를 요청하게됩니다. 그래프는 관계 발견 응용 프로그램으로 사용하기위한 것입니다. 나는 달성하기 위해 노력하고있는 아이디어를 얻기 위해 다음의 jsfiddle http://jsfiddle.net/R2JGa/7/을 만들었습니다.

비교적 잘 작동하지만 그래프에 새 노드를 추가 할 때 이전 노드가 어떻게 든 잘못 배치됩니다. 예를 들어, 3 노드부터 시작하고, 루트 노드는 "플레어"입니다. 나머지 두 노드는 "애니메이션"및 "분석"입니다. 내 예제에서는 현재 자식이없는 노드를 클릭 할 때마다 자식은 항상 "x", "y", "z", "t"가됩니다. 몇 개의 노드를 확장하면 "애니메이션"또는 "분석"이 루트 노드 "플레어"에 연결되지 않고 다른 노드 (x, y, z, t)에 연결된다는 것을 알 수 있습니다. 또는 x 또는 y 또는 z 또는 t를 확장하면 자식 노드에 중복 x 또는 y 또는 z 또는 t가있는 경우가 있습니다. "플레어 (flare)"를 클릭하여 전체 그래프를 숨긴 다음 "플레어 (flare)"를 다시 열면 노드가 올바르게 링크되고 이름이 표시됩니다.

왜 이런 일이 발생하는지 알 수 없습니다. 여기 누군가가 빛을 비춰 줄 수 있습니까?

var w = 960, 
      h = 800, 
      node, 
      link, 
      root; 

    var force = d3.layout.force() 
      .charge(-1000) 
      .size([w, h]); 

    var vis = d3.select("#chart").append("svg:svg") 
      .attr("width", w) 
      .attr("height", h); 

    d3.json("data.json", function (json) { 
     root = json; 
     update(); 
    }); 

    function update() { 
     var nodes = flatten(root); 
     nodes.reverse(); 
     nodes = nodes.sort(function (a, b) { 
      return a.index - b.index; 
     }); 

     var links = d3.layout.tree().links(nodes); 

     console.log(nodes); 

     // Restart the force layout. 
     force 
       .nodes(nodes) 
       .links(links) 
       .linkDistance(55) 
       .start(); 


     var link = vis.selectAll(".link") 
       .data(links); 

     link.enter().append("line") 
       .attr("class", "link"); 

     link.exit().remove(); 

     var node = vis.selectAll("g.node") 
       .data(nodes) 


     var groups = node.enter().append("g") 
       .attr("class", "node") 
       .attr("id", function (d) { 
        return d.id 
       }) 
       .on('click', click) 
       .call(force.drag); 


     groups.append("image") 
       .attr("xlink:href", "https://github.com/favicon.ico") 
       .attr("x", -8) 
       .attr("y", -8) 
       .attr("width", 16) 
       .attr("height", 16); 


     groups.append("text") 
       .attr("dx", 12) 
       .attr("dy", "0.35em") 
       .style("font-size", "10px") 
       .text(function (d) { 
        console.log(d); 
        return d.name 
       }); 


     node.exit().remove(); 


     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("transform", function (d) { 
       return "translate(" + d.x + "," + d.y + ")"; 
      }); 
     }); 
    } 


    // Color leaf nodes orange, and packages white or blue. 
    function color(d) { 
     return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; 
    } 

    // Toggle children on click. 
    function click(d) { 
     console.log(d); 
     if (d.children) { 
      d._children = d.children; 
      d.children = null; 
      update(); 
     } else if (d._children) { 
      d.children = d._children; 
      d._children = null; 
      update(); 
     } 
     else { 
      d3.json("expand.json", function (json) { 
       d.children = json.children; 
       update(); 
      }) 
     } 
    } 

    // Returns a list of all nodes under the root. 
    function flatten(root) { 
     var nodes = [], i = 0; 

     function recurse(node) { 
      if (node.children) node.children.forEach(recurse); 
      if (!node.id) node.id = ++i; 
      nodes.push(node); 
     } 

     recurse(root); 
     return nodes; 
    } 

그리고 여기에 내가 요구하고있어이 개 JSON 파일입니다 : 여기

코드입니다 ... 나는 아직도 D3와 정말 흥미 새로운 오전하지만이 문제는 너무 짜증나

data.json

{ 
    "name": "flare", 
    "id" : "flare", 
    "children": [ 
     { 
      "name": "analytics", 
      "id": "analytics" 

     }, 
     { 
      "name": "animate", 
      "id": "animate" 
     } 
    ] 
} 

그리고 expand.json

{"children": [ 
    { 
     "name": "x", 
     "id": "x", 
     "size": 1983 
    }, 
    { 
     "name": "y", 
     "id": "y", 
     "size": 2047 
    }, 
    { 
     "name": "z", 
     "id": "z", 
     "size": 1375 
    }, 
    { 
     "name": "t", 
     "id": "t", 
     "size": 1375 
    } 
]} 

추신 : 그렇지 않으면 노드 배열을 정렬해야했습니다. 그렇지 않은 경우 그래프에 문제가 발생했습니다. 이유를 이해할 수 없습니다.

답변

2

여기서 작업 용액에 fiddle. 나는 당신의 ID를 선언하고 배열 인덱스를 기반으로 정렬하는 방식에 문제가 있다고 생각한다. ID가 평평한 코드에 의해 선언되도록 한 다음 ID에 따라 정렬해야합니다. 또한 재귀 함수에서 부모를 먼저 선언 한 다음 자식을 선언 할 수 있습니다.

function recurse(node) { 
     if(!node.id) node.id = ++i; 
     nodes.push(node); 
     if (node.children) node.children.forEach(recurse); 
    } 
+0

실제로 문제는 재귀 기능과 관련이 있지만 이유를 파악할 수 없습니다. 대부분의 경우 force 그래프는 노드를 어떤 방식으로 정렬해야하지만 ... 어느 하나를 파악할 수없는 것 같습니다 – user253530

+0

해결 방법은 무엇이 었는지 말해 주시겠습니까? 나는 그것이 AOK에서 일하고 있다고 생각했다. – Yogesh

+0

jsfiddle는 제대로 작동하는 것처럼 보이지만 소스를 가져 와서 브라우저에서 실행하면 볼 수 없습니다. 기묘한. 어쨌든, 때로는 잘못 놓이는 x, y, z, t 노드를 봐야합니다. 나는 당신의 해결책을 시도했지만 나는 그것을 얻을 수 없었다. 당신이 말한 것을 시작으로 문제 스펙트럼을 좁히고 실제로 해결책을 찾았 기 때문에 나는 도움을 정말 고맙게 생각합니다. 문제는 매번 같은 위치에 노드를 삽입하지 않으므로 그래프가 혼란스러워지는 재귀 함수에 있습니다. – user253530

0

저는 Yogesh의 대답부터 시작하여이 문제에 대한 해결책을 찾을 수있었습니다. 다음은 update() 함수에 추가해야하는 코드입니다.

  var currentNodes = force.nodes(); 
      var nodes = flatten(root); 
      var actualNodes = []; 

      var values = currentNodes.map(function(obj) { return obj.name}); 
      var newNodesValues = nodes.map(function(obj) { return obj.name }); 


      for(var i = 0; i < currentNodes.length; i++) { 
       if(newNodesValues.indexOf(currentNodes[i].name) !== -1) { 
        actualNodes.push(currentNodes[i]); 
       } 
      } 

      for(var i = 0; i < nodes.length; i++) { 
       if(values.indexOf(nodes[i].name) == -1) { 
        actualNodes.push(nodes[i]); 
       } 
      } 

      nodes = actualNodes; 

      var links = d3.layout.tree().links(nodes); 

      // Restart the force layout. 
      force 
        .nodes(nodes) 
        .links(links) 
        .linkDistance(55) 
        .start(); 
0

다음은 트릭을해야 다음

var i = 0; 

...

var link = vis.selectAll(".link") 
    .data(links, function (d) { 
       return d.id || (d.id = ++i); 
      }); 

을 ...

var node = vis.selectAll("g.node") 
.data(nodes, function (d) { 
       return d.id || (d.id = ++i); 
      }); 

데이터 번째 인자()는, 데이텀으로 호출되면, 대응 데이텀에있어 각 DOM 노드를 결합하는 키를 반환 콜백 함수가된다. 이러한 함수를 제공하지 않으면 d3에 옵션이없고 인덱스를 사용하여 데이터를 DOM 노드에 바인딩합니다.