d3.js节点和边缘格式

时间:2015-07-12 19:30:46

标签: d3.js force-layout

我正在尝试使用 d3.js 绘制力布局。我的json对象传递给d3以使强制布局具有正确的格式,但我得到以下错误:

TypeError: e[u.source.index] is undefined

...++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.tar...

注意 e [u.source.index] .push(u.target)

我的json对象包含两个对象:

  1. 节点
  2. 边缘
  3. 节点是承载节点数据的对象列表(可能是我所知道的任何事情),而edge是一个对象列表,它引用 source target <中的节点对象/ em> properties

    这是我的代码段:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
    
    body {
      font: 10px sans-serif;
      shape-rendering: crispEdges;
    }
    
    .link.flow {
        opacity: 1!important;
        /*stroke-width: 1.5px;*/
    }
    
    #licensing {
        fill: green;
    }
    
    .link.flow.licensing {
        stroke: green;
    }
    
    .link.flow.resolved {
        stroke-dasharray: 0,2 1;
    }
    
    circle.flow {
        fill: #ff2575;
        stroke: #ff2575;
        /*stroke-width: 1.5px;*/
    }
    
    text.flow {
        font: 10px sans-serif;
        pointer-events: none;
        text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
    }
    
    path.link.flow {
        fill: none;
    }
    
    
    
    
    </style>
    <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script>
    var maxWeight = 0;
    var maxSize = 0;
    
    //  new graph: start
    var margin = 10;
    var width = 455,
    	height = 350;
    
    var svgMaster = d3.select("body").append("svg") //  initiate svg
    	.attr("id","flow")
    	.attr("width", width)
    	.attr("height", height)
    	.style("margin-right",margin+"px")
    	.style("margin-left",margin+"px");
    
    var svg = svgMaster.append('svg:g')
    	.attr('id','groupFlow');
    
    var link = svg.selectAll(".link"),
    	node = svg.selectAll(".node");  //  nodes and links
    // :end new graph
    
    var jsonData = {
    	"nodes": [
    		{"username": "S_Christophorus", "social_net_id": "55641120cdfa6618acdd1952", "last_name":
    		 "Christophorus", "first_name": "Stanly", "avatar": "/media/avatars/C02.png", "person_id": "556431f3cdfa661108325774"
    		, "id": "55641120cdfa6618acdd1a8e"},
    		{"username": "A_Field", "social_net_id": "55641120cdfa6618acdd1952"
    		, "last_name": "Field", "first_name": "Abdul", "avatar": "/media/avatars/B01.png", "person_id": "556431f3cdfa6611083257f6"
    		, "id": "55641120cdfa6618acdd1b94"}, 
    		{"username": "B_Hugh", "social_net_id": "55641120cdfa6618acdd1952"
    		, "last_name": "Hugh", "first_name": "Beale", "avatar": "/media/avatars/B02.png", "person_id": "556431f3cdfa6611083257f7"
    		, "id": "55641120cdfa6618acdd1b96"}, 
    		{"username": "M_Kennedy", "social_net_id": "55641120cdfa6618acdd1952"
    		, "last_name": "Kennedy", "first_name": "Mordy", "avatar": "/media/avatars/B05.png", "person_id": "556431facdfa661108327e21"
    		, "id": "55641128cdfa6618acdd9fed"}
    	], 
    	"edges": [
    		{"source": "0", "target": "1", "weight": 1.5}, 
    		{"source": "2", "target": "0", "weight": 46.5}, 
    		{"source": "0", "target": "2", "weight": 6.0}, 
    		{"source": "2", "target": "1", "weight": 1.5}, 
    		{"source": "2", "target": "3", "weight": 3.0}
    	]
    }
    
    
    /*
     * refresh graph based on given data
     */
    var refreshForceFlow = function(json){
    	clearGraphFlow();
    	var maxWeight = 0;
    	
        for(var edgeIndex = 0; edgeIndex < json.edges.length; edgeIndex++){
          if(json.edges[edgeIndex].weight > maxWeight) maxWeight = json.edges[edgeIndex];
          }
    	  console.log(json);
    	var force = d3.layout.force()
    		.nodes(d3.values(json.nodes))
    		.links(json.edges)
    		.size([width, height])
    		.linkDistance(60)
    		.charge(-300)
    		.on("tick", tickFlow)
    		.start();
    
    	// Per-type markers, as they don't inherit styles.
    	svg.append("defs").selectAll("marker")
    		.data(json.edges)
    		.enter().append("marker")
    		.attr("id", function(d) { return ("weight_"+d.weight).replace(".","_"); })
    		.attr("viewBox", "0 -5 10 10")
    		.attr("refX", 13)    //  15
    		.attr("refY", 0)    //  -1.5
    		.attr("markerWidth", 6)
    		.attr("markerHeight", 6)
    		.attr("orient", "auto")
    		.append("path")
    		.style("fill", function(d){
    			var color = 'FF';
    			var c = Math.floor((d.weight*99)/maxWeight);
    			c = 100 - c;
    			if( c < 10) c = '0'+c;
    			color = c + color;
    			color = c + color;
    			//console.log('#'+color);
    			return '#'+color;
    		})
    		.style("stroke", function(d){
    			var color = 'FF';
    			var c = Math.floor((d.weight*99)/maxWeight);
    			c = 100 - c;
    			if( c < 10) c = '0'+c;
    			color = c + color;
    			color = c + color;
    			//console.log('#'+color);
    			return '#'+color;
    		})
    		.style("stroke-width", "1px")
    		.attr("d", "M0,-5L10,0L0,5");
    
    	var path = svg.append("g").selectAll("path")
    		.data(force.links())
    		.enter().append("path")
    		.attr("class", function(d) { return "link flow "; })
    		.style("stroke-width", function(d) {
    			var res = 1 + ((d.weight * 2.5) / maxWeight);
    			return res + 'px';
    		})
    		.style("stroke", function(d){
    			var color = 'FF';
    			var c = Math.floor((d.weight*99)/maxWeight);
    			c = 100 - c;
    			if( c < 10) c = '0'+c;
    			color = c + color;
    			color = c + color;
    			//console.log('#'+color);
    			return '#'+color;
    		})
    		.attr("marker-end", function(d) { return "url(#" + ("weight_"+d.weight).replace(".","_") + ")"; });
    
    	var circle = svg.append("g").selectAll("circle")
    		.data(force.nodes())
    		.enter().append("circle")
    		.attr("r", 6)
    		.attr("class", "flow")
    		.on("contextmenu", d3.contextMenu(menu))
    		.call(force.drag);
    
    	var text = svg.append("g").selectAll("text")
    		.data(force.nodes())
    		.enter().append("text")
    		.attr("class", "flow")
    		.attr("x", 8)
    		.attr("y", ".31em")
    		.text(function(d) { return d.username; });
    
    	// Use elliptical arc path segments to doubly-encode directionality.
    	function tickFlow() {
    		path.attr("d", linkArc);
    		circle.attr("transform", transform);
    		text.attr("transform", transform);
    	}
    
    	function linkArc(d) {
    		var tx = d.target.x - 0;
    		var ty = d.target.y - 0;
    		var sx = d.source.x - 0;
    		var sy = d.source.y - 0;
    
    		var dx = tx - sx,
    			dy = ty - sy,
    			dr = Math.sqrt(dx * dx + dy * dy);
    		return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
    	}
    
    	function transform(d) {
    		return "translate(" + d.x + "," + d.y + ")";
    	}
    };  //  end of function
    
    /*
     * clear gragh
     */
    var clearGraphFlow = function(){
    	svg.selectAll("g").remove();
    	svg.selectAll("defs").remove();
    };
    
    refreshForceFlow(jsonData);
    
    </script>

1 个答案:

答案 0 :(得分:2)

由于它们表示索引,因此edge数组的源和目标属性需要是数字而不是字符串。

"edges": [
    {"source": 0, "target": 1, "weight": 1.5}, 
    {"source": 2, "target": 0, "weight": 46.5}, 
    {"source": 0, "target": 2, "weight": 6.0}, 
    {"source": 2, "target": 1, "weight": 1.5}, 
    {"source": 2, "target": 3, "weight": 3.0}
]

这里有效:

&#13;
&#13;
<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font: 10px sans-serif;
  shape-rendering: crispEdges;
}

.link.flow {
    opacity: 1!important;
    /*stroke-width: 1.5px;*/
}

#licensing {
    fill: green;
}

.link.flow.licensing {
    stroke: green;
}

.link.flow.resolved {
    stroke-dasharray: 0,2 1;
}

circle.flow {
    fill: #ff2575;
    stroke: #ff2575;
    /*stroke-width: 1.5px;*/
}

text.flow {
    font: 10px sans-serif;
    pointer-events: none;
    text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}

path.link.flow {
    fill: none;
}




</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var maxWeight = 0;
var maxSize = 0;

//  new graph: start
var margin = 10;
var width = 455,
	height = 350;

var svgMaster = d3.select("body").append("svg") //  initiate svg
	.attr("id","flow")
	.attr("width", width)
	.attr("height", height)
	.style("margin-right",margin+"px")
	.style("margin-left",margin+"px");

var svg = svgMaster.append('svg:g')
	.attr('id','groupFlow');

var link = svg.selectAll(".link"),
	node = svg.selectAll(".node");  //  nodes and links
// :end new graph

var jsonData = {
	"nodes": [
		{"username": "S_Christophorus", "social_net_id": "55641120cdfa6618acdd1952", "last_name":
		 "Christophorus", "first_name": "Stanly", "avatar": "/media/avatars/C02.png", "person_id": "556431f3cdfa661108325774"
		, "id": "55641120cdfa6618acdd1a8e"},
		{"username": "A_Field", "social_net_id": "55641120cdfa6618acdd1952"
		, "last_name": "Field", "first_name": "Abdul", "avatar": "/media/avatars/B01.png", "person_id": "556431f3cdfa6611083257f6"
		, "id": "55641120cdfa6618acdd1b94"}, 
		{"username": "B_Hugh", "social_net_id": "55641120cdfa6618acdd1952"
		, "last_name": "Hugh", "first_name": "Beale", "avatar": "/media/avatars/B02.png", "person_id": "556431f3cdfa6611083257f7"
		, "id": "55641120cdfa6618acdd1b96"}, 
		{"username": "M_Kennedy", "social_net_id": "55641120cdfa6618acdd1952"
		, "last_name": "Kennedy", "first_name": "Mordy", "avatar": "/media/avatars/B05.png", "person_id": "556431facdfa661108327e21"
		, "id": "55641128cdfa6618acdd9fed"}
	], 
	"edges": [
		{"source": 0, "target": 1, "weight": 1.5}, 
		{"source": 2, "target": 0, "weight": 46.5}, 
		{"source": 0, "target": 2, "weight": 6.0}, 
		{"source": 2, "target": 1, "weight": 1.5}, 
		{"source": 2, "target": 3, "weight": 3.0}
	]
}


/*
 * refresh graph based on given data
 */
var refreshForceFlow = function(json){
	clearGraphFlow();
	var maxWeight = 0;
	
    for(var edgeIndex = 0; edgeIndex < json.edges.length; edgeIndex++){
      if(json.edges[edgeIndex].weight > maxWeight) maxWeight = json.edges[edgeIndex];
      }
	  console.log(json);
	var force = d3.layout.force()
		.nodes(d3.values(json.nodes))
		.links(json.edges)
		.size([width, height])
		.linkDistance(60)
		.charge(-300)
		.on("tick", tickFlow)
		.start();

	// Per-type markers, as they don't inherit styles.
	svg.append("defs").selectAll("marker")
		.data(json.edges)
		.enter().append("marker")
		.attr("id", function(d) { return ("weight_"+d.weight).replace(".","_"); })
		.attr("viewBox", "0 -5 10 10")
		.attr("refX", 13)    //  15
		.attr("refY", 0)    //  -1.5
		.attr("markerWidth", 6)
		.attr("markerHeight", 6)
		.attr("orient", "auto")
		.append("path")
		.style("fill", function(d){
			var color = 'FF';
			var c = Math.floor((d.weight*99)/maxWeight);
			c = 100 - c;
			if( c < 10) c = '0'+c;
			color = c + color;
			color = c + color;
			//console.log('#'+color);
			return '#'+color;
		})
		.style("stroke", function(d){
			var color = 'FF';
			var c = Math.floor((d.weight*99)/maxWeight);
			c = 100 - c;
			if( c < 10) c = '0'+c;
			color = c + color;
			color = c + color;
			//console.log('#'+color);
			return '#'+color;
		})
		.style("stroke-width", "1px")
		.attr("d", "M0,-5L10,0L0,5");

	var path = svg.append("g").selectAll("path")
		.data(force.links())
		.enter().append("path")
		.attr("class", function(d) { return "link flow "; })
		.style("stroke-width", function(d) {
			var res = 1 + ((d.weight * 2.5) / maxWeight);
			return res + 'px';
		})
		.style("stroke", function(d){
			var color = 'FF';
			var c = Math.floor((d.weight*99)/maxWeight);
			c = 100 - c;
			if( c < 10) c = '0'+c;
			color = c + color;
			color = c + color;
			//console.log('#'+color);
			return '#'+color;
		})
		.attr("marker-end", function(d) { return "url(#" + ("weight_"+d.weight).replace(".","_") + ")"; });

	var circle = svg.append("g").selectAll("circle")
		.data(force.nodes())
		.enter().append("circle")
		.attr("r", 6)
		.attr("class", "flow")
		//.on("contextmenu", d3.contextMenu(menu))
		.call(force.drag);

	var text = svg.append("g").selectAll("text")
		.data(force.nodes())
		.enter().append("text")
		.attr("class", "flow")
		.attr("x", 8)
		.attr("y", ".31em")
		.text(function(d) { return d.username; });

	// Use elliptical arc path segments to doubly-encode directionality.
	function tickFlow() {
		path.attr("d", linkArc);
		circle.attr("transform", transform);
		text.attr("transform", transform);
	}

	function linkArc(d) {
		var tx = d.target.x - 0;
		var ty = d.target.y - 0;
		var sx = d.source.x - 0;
		var sy = d.source.y - 0;

		var dx = tx - sx,
			dy = ty - sy,
			dr = Math.sqrt(dx * dx + dy * dy);
		return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
	}

	function transform(d) {
		return "translate(" + d.x + "," + d.y + ")";
	}
};  //  end of function

/*
 * clear gragh
 */
var clearGraphFlow = function(){
	svg.selectAll("g").remove();
	svg.selectAll("defs").remove();
};

refreshForceFlow(jsonData);

</script>
&#13;
&#13;
&#13;