【问题标题】:Merge new json data to existing graph in d3.js and cola.js将新的 json 数据合并到 d3.js 和 cola.js 中的现有图表
【发布时间】:2018-08-07 14:14:48
【问题描述】:

我有一个函数 buildTree,它将 json 数据作为输入,并使用 d3.js 和 cola.js 将它们可视化。这个想法是我单击一个按钮并将 json 添加到 d3。然后通过单击另一个按钮,我添加了另一个 js 并保留了旧的,所以我最终得到了两棵树。在我的示例中,我有一个节点存在于两个 json 文件中,因此我希望最终将这两棵树连接起来,并且该节点出现一次。

我设法获取现有树,添加新树,两次删除存在的节点并更新链接,但一个链接从未连接到共存节点。

JSON 文件具有以下格式:

{
  "nodes": [
    {"id": "a", "name": "a", "type": "tool", "width": 60, "height": 40},
    {"id": "b", "name": "b", "type": "tool", "width": 60, "height": 40},
    {"id": "c", "name": "c", "type": "tool", "width": 60, "height": 40}

  ],
  "links": [
    {"source": 0, "target": 1},
    {"source": 0, "target": 2}
  ],
  "groups": []
}

第二个:

{
  "nodes": [
    {"id": "h", "name": "h", "type": "tool", "width": 60, "height": 40},
    {"id": "i", "name": "i", "type": "tool", "width": 60, "height": 40},
    {"id": "c", "name": "c", "type": "tool", "width": 60, "height": 40}

  ],
  "links": [
    {"source": 0, "target": 1},
    {"source": 0, "target": 2}

  ],
  "groups": []
}

所以 c 是两个 JSON 文件中都存在的节点,并且应该只出现在树中一次,但同时包含两个链接。

buildTree 类似于:

function buildTree(jsonSource) {
  d3.json(jsonSource, function (error, graph) {

    //get existing data if any and merge them with new data
    myNodes = svg.selectAll(".node");
    myLinks = svg.selectAll(".link");

    //update the existing nodes with the new ones, remove duplications and store them in removedNodes
    allNodes = graph.nodes.concat(myNodes.data());
    var uniqueIds = [];
    var allNodesUnique = [];
    var removedNodes = [];
    for (var i = 0; i < allNodes.length; i++) {
      var id = allNodes[i].id;
      if (uniqueIds.indexOf(id) == -1) {
        uniqueIds.push(id);
        allNodesUnique.push(allNodes[i]);
      } else {
        removedNodes.push(allNodes[i]);
      }
    }
    allNodes = allNodesUnique;

    //update links                  
    allLinks = graph.links.concat(myLinks.data());

    d3.selectAll("svg > *").remove();


    cola
      .nodes(allNodes)
      .links(allLinks)
      .groups(graph.groups)
      .start();

  ...

【问题讨论】:

    标签: javascript json d3.js webcola


    【解决方案1】:

    我认为您的问题是您传入的链接指的是它们所在数组中节点的索引。当您将这些节点合并到 1 个数组中时,索引现在不再匹配。 您必须将旧索引从新数据映射到这些节点现在在节点数组中的位置。

    我还建议您使用 Map 数据结构来删除重复项。 即你通过你的节点将它们全部按ids放在一个地图中。然后浏览地图并提取您现在重复的免费列表

    例如(请原谅我可能犯的任何愚蠢的错误)

    // map indexes of nodes to their IDs 
    const indexIDMap = graph.links.map(d=>d.id);
    
    // remove duplicates
    const nodeMap = new Map();
    // add old nodes to map
    myNodes.data().forEach(d=> nodeMap.set(d.id, d));
    //note this would over write any data contained in the new node that isn't on the old node
    myNodes.links.forEach(d=> nodeMap.set(d.id, d)); 
    // Maps maintain insertion order which is important
    const allNodes = [...myNodes.values()]
    
    // links
    const newIndices = indexIdMap.map(id => allNodes.findIndex(d => d.id === id))
    const newLinks = graph.links.map(l => ({
        source: newIndices[l.source],
        target: newIndices[l.target]
    }))
    
    const allLinks = myLinks.data().concat(newLinks) 
    

    【讨论】:

    • 索引已更新。我没有发布所有代码,但我最终得到了具有正确索引但 x 和 y 不同的链接。所以,我最终得到一个应该显示节点的链接,即节点“c”,它只是固定在节点 c 的旧 x 和 y 位置。
    • 您是在启动可乐模拟之后还是之前合并这些节点?你试过重画边缘吗?
    • 我正在重绘节点和链接,至少我在尝试。我在这里添加了我所有的代码:jsfiddle.net/galateia/pgdfe0vm/4 所以它更清楚了,但是由于 cola.js 不可用而无法正常工作
    【解决方案2】:

    最后我通过正确更新链接解决了这个问题,javascript代码可以在这里找到:

    <script src="d3v4.js"></script>
    <script src="cola.min.js"></script>
    <script>
    	
    //function on button click		
    function myFunction() {
    	buildTree("Data1.json");
    	document.getElementById('graph').style.visibility="visible";
       
    }
    
    //function on button click	
    function myFunction2() { 
      buildTree("Data2.json");
      document.getElementById('graph').style.visibility="visible";
    
       
    }
    
    <!-- initialize cola -->
      var width = 960,
          height = 500; //these are the dimensions of the graph
    
    	// map colors for nodes to their type	
    	var color = d3.scaleOrdinal()
    				.domain(["workflow", "tool", "task", "exposed variable"])
    				.range(["#009933", "#E3a322", "#E05B2B", "#81D42F"]);
    
        var cola = cola.d3adaptor(d3)
            .linkDistance(100)
            .avoidOverlaps(true)
            .handleDisconnected(false)
            .size([width, height]);
    
        var svg = d3.select("#graph").append("svg")
            .attr("width", width)
            .attr("height", height);
    
    <!-- end of initialize cola -->	
    
    
    
    
    /**
    This function takes as an iput a json with nodes and links and creates a tree.
    If another tree already exists it merges the json data and redraws old trees and new ones
    
    **/
    function buildTree(jsonSource){
    		var myNodes =[];
    		var myLinks=[];
    		var allNodes=[];
    		var allLinks=[];
    		
    		
    
      d3.json(jsonSource, function (error, graph) {
    	//console.log(error);
    
    	<!-- Data Merging -->
    	//get existing data if any and merge them with new data
    	myNodes = svg.selectAll(".node").data();
    	myLinks = svg.selectAll(".link").data();	
    		
    		
    	//update the existing NODES with the new ones, remove duplications and store them in removedNodes
    		allNodes = graph.nodes.concat(myNodes);
    		var uniqueIds=[];
    		var allNodesUnique=[];
    		var removedNodes=[];
    		var oldIds=[];
    		
    		for (var i=0; i < allNodes.length; i++){
    				var currentId = allNodes[i].id;
    				if(uniqueIds.indexOf(currentId) == -1){
    					uniqueIds.push(currentId);
    					allNodesUnique.push(allNodes[i]);
    				}else{
    					oldIds.push(currentId);
    					removedNodes.push(allNodes[i]);
    				}
        	}
    		allNodes=allNodesUnique;
    
    		var remainedNodes=[];
    		for (var j=0; j < oldIds.length; j++){
    			for (var i=0; i < allNodes.length; i++){
    				if(oldIds[j]!='undefined' && oldIds[j]==allNodes[i].id){
    				remainedNode = allNodes[i];
    				remainedNodes.push(allNodes[i]);
    				}
    			}
    		}
    		
    					
    		//update LINKS (remove dublications)		
    		var myCount = (myNodes.length);
    		   if(myCount>-1){
    			for (var j=0; j < remainedNodes.length; j++){
    				for (var i=0; i < myLinks.length; i++){
    					  if(myLinks[i].source.id == remainedNodes[j].id){	  
    						myLinks[i].source = remainedNodes[j];		  
    					  }
    					
    					if(myLinks[i].target.id == remainedNodes[j].id){		  
    						myLinks[i].target = remainedNodes[j];
    					  
    					  }
    					
    							myLinks[i].source.index=myLinks[i].source.index+myCount;
    							myLinks[i].target.index=myLinks[i].target.index+myCount;
    					}
    				}			
    			}
    			
    	    allLinks = graph.links.concat(myLinks); 
    		//update removed info
    		
    
    		//search for the removed node 
    		tempLinks=[];
    		for(var j=0; j<removedNodes.length; j++){
    			for (var i=0; i < allLinks.length; i++){
    				if(allLinks[i].source.id==removedNodes[j].id){		
    					allLinks[i].source.index = allLinks[i].source.index - myCount 
    				}
    				if(allLinks[i].target.id==removedNodes[j].id){
    					allLinks[i].target.index = allLinks[i].target.index - myCount 				
    				}
    			}
    		
    		}
    
    	<!-- End of Data Merging -->
    
    
    d3.selectAll("svg > *").remove();
    
    
            cola
                .nodes(allNodes)
                .links(allLinks)
                .groups(graph.groups)
                .start();
    
            var group = svg.selectAll(".group")
                .data(graph.groups)
                .enter().append("rect")
                .attr("rx", 8).attr("ry", 8)
                .attr("class", "group")
                .style("fill", function (d, i) { return color(i);})
                .call(cola.drag);
    
            var link = svg.selectAll(".link")
                .data(allLinks)
                .enter().append("line")
                .attr("class", function(d){ return ["link", d.source.name, d.target.name].join(" "); });
    			
    	
    
    
            var pad = 3;
            var node = svg.selectAll(".node")
                .data(allNodes)
                .enter().append("rect")
                .attr("class", "node")
                .attr("width", function (d) { return d.width - 2 * pad; })
                .attr("height", function (d) { return d.height - 2 * pad; })
                .attr("rx", 5).attr("ry", 5)
    			.style("fill", function(d) {  return color(d.type);   }) //color based on type
                .call(cola.drag);
    
    	
            node.append("title")
                .text(function (d) { return d.name; });
    
    			
            cola.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("x", function (d) { return d.x - d.width / 2 + pad; })
                    .attr("y", function (d) { return d.y - d.height / 2 + pad; });
                
                group.attr("x", function (d) { return d.bounds.x; })
                     .attr("y", function (d) { return d.bounds.y; })
                    .attr("width", function (d) { return d.bounds.width(); })
                    .attr("height", function (d) { return d.bounds.height(); });
    
    			
            });
    		
    		
    		
        });
    	
    }
    	
    </script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-19
      • 1970-01-01
      相关资源
      最近更新 更多