Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
381 views
in Technique[技术] by (71.8m points)

d3.js - Place pie charts on nodes of force directed layout graph in D3

I would like to place pie charts on the node of a D3 force directed layout graph using D3.js. This is a common visualization in population genetics, see for example http://mathildasanthropologyblog.files.wordpress.com/2008/06/as3.jpg

enter image description here

I've started with a very basic graph plot:

<!DOCTYPE html>
<html lang="en">
    <head>
        <script type="text/javascript" src="d3/d3.v3.js"></script>
    </head>
    <body>
        <script type="text/javascript">

graph = { "nodes":[{"proportions": [{"group": 1, "value": 1}, 
                                    {"group": 2, "value": 2}, 
                                    {"group": 3, "value": 3}]},
                   {"proportions": [{"group": 1, "value": 2}, 
                                    {"group": 2, "value": 2}, 
                                    {"group": 3, "value": 2}]}],
          "links":[{"source": 0, "target": 1, "length": 500, "width": 1}]
        }

var width = 960,
    height = 500,
    radius = 10,
    color = d3.scale.category20c();

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

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

force
    .nodes(graph.nodes)
    .links(graph.links)
    .start();

var link = svg.selectAll(".link")
    .data(graph.links)
  .enter().append("line")
    .attr("class", "link");

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

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; });
});
        </script>
    </body>
</html> 

But when I try to replace the circle nodes with pie charts, the pie charts all end up stacked in the corner of the plot.

<!DOCTYPE html>
<html lang="en">
    <head>
        <script type="text/javascript" src="d3/d3.v3.js"></script>
    </head>
    <body>
        <script type="text/javascript">

graph = { "nodes":[{"proportions": [{"group": 1, "value": 1}, 
                                    {"group": 2, "value": 2}, 
                                    {"group": 3, "value": 3}]},
                   {"proportions": [{"group": 1, "value": 2}, 
                                    {"group": 2, "value": 2}, 
                                    {"group": 3, "value": 2}]}],
          "links":[{"source": 0, "target": 1, "length": 500, "width": 1}]
        }

var width = 960,
    height = 500,
    radius = 10,
    color = d3.scale.category20c();

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) { return d.value; });

var arc = d3.svg.arc()
    .outerRadius(radius)
    .innerRadius(0);

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

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

force
    .nodes(graph.nodes)
    .links(graph.links)
    .start();

var link = svg.selectAll(".link")
    .data(graph.links)
  .enter().append("line")
    .attr("class", "link");

var node = svg.selectAll(".node")
    .data(graph.nodes)
  .enter().append("g")
   .attr("class", "node");

node.selectAll("path")
  .data(function(d) {return pie(d.proportions); })
 .enter().append("svg:path")
  .attr("d", arc)
  .style("fill", function(d) { return color(d.group); });;

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("x", function(d) { return d.x; })
      .attr("y", function(d) { return d.y; });
});
        </script>
    </body>
</html> 

Any assistance would be very appreciated!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem appears to be the last statement in your force-on-tick callback:

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

SVG paths have no such x/y attributes. Try translating the path instead:

node.attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...