D3: Substituting d3.svg.diagonal() with d3.svg.line()
Asked Answered
A

4

20

I have implemented the following graph with the edges rendered with d3.svg.diagonal(). However, when I try substituting the diagonal with d3.svg.line(), it doesn't appear to pull the target and source data. What am I missing? Is there something I don't understand about d3.svg.line?

enter image description here

The following is the code I am referring to, followed by the full code:

var line = d3.svg.line()
    .x(function(d) { return d.lx; })
    .y(function(d) { return d.ly; });

...

var link= svg.selectAll("path")
    .data(links)
  .enter().append("path")
    .attr("d",d3.svg.diagonal())
    .attr("class", ".link")
    .attr("stroke", "black")
    .attr("stroke-width", "2px")
    .attr("shape-rendering", "auto")
    .attr("fill", "none"); 

The entire code:

var margin = {top: 20, right: 20, bottom: 20, left: 20},
    width =1500, 
    height = 1500, 
    diameter = Math.min(width, height),
    radius = diameter / 2;


var balloon = d3.layout.balloon()
  .size([width, height])
  .value(function(d) { return d.size; })
  .gap(50)                  

var line = d3.svg.line()
    .x(function(d) { return d.lx; })
    .y(function(d) { return d.ly; });


var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + (margin.left + radius) + "," + (margin.top + radius) + ")")

    root = "flare.json";
    root.y0 = height / 2;
    root.x0 = width / 2;

d3.json("flare.json", function(root) {
  var nodes = balloon.nodes(root),
      links = balloon.links(nodes);


var link= svg.selectAll("path")
    .data(links)
  .enter().append("path")
    .attr("d",d3.svg.diagonal())
    .attr("class", ".link")
    .attr("stroke", "black")
    .attr("stroke-width", "2px")
    .attr("shape-rendering", "auto")
    .attr("fill", "none");   

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

  node.append("circle")
      .attr("r", function(d) { return d.r; })
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

  node.append("text")
      .attr("dx", function(d) { return d.x })
      .attr("dy", function(d) { return d.y })
      .attr("font-size", "5px")
      .attr("fill", "white")
      .style("text-anchor", function(d) { return d.children ? "middle" : "middle"; })
      .text(function(d) { return d.name; })
});

A comparison of how the d attribute of the svg disappears when using "line." enter image description here enter image description here

Arteriole answered 17/4, 2013 at 20:59 Comment(6)
You don't seem to be using line anywhere in the code. The d attribute of the path is still set using d3.svg.diagonal.Talented
Yes, but when I substitute "line" for "d3.svg.diagonal()"0 it breaks the vis. Can't figure out why.Arteriole
The d attribute for the svg disappears. I will upload a comparison above.Arteriole
Have you tried just d3.svg.line() without the custom accessors for x and y?Talented
Have you got a JSFiddle or similar to look at?Edgar
@JakubSvec Can you please help me on this #67002741 ?Vaticinal
L
23

Question is quite dated, but since I don't see an answer and someone might face the same problem, here it is.

The reason why simple replacement of diagonal with line is not working is because d3.svg.line and d3.svg.diagonal return different results:

  • d3.svg.diagonal returns function that accepts datum and its index and transforms it to path using projection. In other words diagonal.projection determines how the function will get points' coordinates from supplied datum.
  • d3.svg.line returns function that accepts an array of points of the line and transforms it to path. Methods line.x and line.y determine how coordinates of the point retreived from the single element of supplied array

D3 SVG-Shapes reference

SVG Paths and D3.js

So you can not use result of the d3.svg.line directly in d3 selections (at least when you want to draw multiple lines).

You need to wrap it in another function like this:

var line = d3.svg.line()
                 .x( function(point) { return point.lx; })
                 .y( function(point) { return point.ly; });

function lineData(d){
    // i'm assuming here that supplied datum 
    // is a link between 'source' and 'target'
    var points = [
        {lx: d.source.x, ly: d.source.y},
        {lx: d.target.x, ly: d.target.y}
    ];
    return line(points);
}

// usage:
var link= svg.selectAll("path")
    .data(links)
    .enter().append("path")
    .attr("d",lineData)
    .attr("class", ".link")
    .attr("stroke", "black")
    .attr("stroke-width", "2px")
    .attr("shape-rendering", "auto")
    .attr("fill", "none");   

Here's working version of jsFiddle mobeets posted: jsFiddle

Lh answered 21/11, 2013 at 9:9 Comment(3)
I think that .attr("d", function (d) { return line([ {lx: d.target.x, ly: d.target.y},{lx: d.source.x, ly: d.source.y} ]); }); is a better way to do it instead of wrap line() in a global function.Undulatory
Extra points for providing a drop-in replacement for diagonal for this context, elusive-code, rather than providing an alternative solution. It's more than a year and a half later, and as you said, "someone might face the same problem" (me). I hadn't found this solution in such a simple form anywhere else, and there are sure to be a number of people who want it some day.Hoahoactzin
@Lh Can you help me on this #67002741 ?Vaticinal
L
3

I had the same problem...There's a jsFiddle here.

Note that changing line to diagonal will make it work.

Loree answered 15/7, 2013 at 18:5 Comment(2)
My suggestion would be to post the code from jsFiddle here as it is not always reliable and up. Then the specific answer will be available in this answer without having to go to an outside website.Passe
@Loree You've to pass the correct input to line(). You can do this wrap line() on another function as recomend @Lh or return to line() the correct input values like you can see on my mod of your fiddle.Undulatory
R
2

Perhaps encapsulating the diagonal function and editing its parameters could work for you:

var diagonal = d3.svg.diagonal();
var new_diagonal = function (obj, a, b) {
    //Here you may change the reference a bit.
    var nobj = {
        source : {
            x: obj.source.x,
            y: obj.source.y
        },
        target : {
            x: obj.target.x,
            y: obj.target.y
        }
    }
    return diagonal.apply(this, [nobj, a, b]);
}
var link= svg.selectAll("path")
    .data(links)
    .enter().append("path")
    .attr("d",new_diagonal)
    .attr("class", ".link")
    .attr("stroke", "black")
    .attr("stroke-width", "2px")
    .attr("shape-rendering", "auto")
    .attr("fill", "none"); 
Runofthemill answered 28/10, 2013 at 13:51 Comment(0)
T
1

Just set the d attribute of link to line:

.attr("d", line)
Toboggan answered 18/4, 2013 at 5:32 Comment(3)
Yes, but when I substitute "line" for "d3.svg.diagonal()"0 it breaks the vis.Arteriole
Can you create a small example on jsFiddle?Toboggan
I have tried, but the source code with the balloon layout is on github and it doesn't seem to be accepting it.Arteriole

© 2022 - 2024 — McMap. All rights reserved.