Arash Howaida
Arash Howaida

Reputation: 2617

d3.js simple link using source/targets

I'm trying to use bezier lines to connect big circles stacked in a column to smaller circles which are appended onto a linear scale. Here is my snippet:

var margins = {top:100, bottom:300, left:100, right:100};

var height = 1300;
var width = 900;

var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;

var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);

var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");

var yScale = d3.scaleLinear()
.range([450,0])
.domain([-4,4]);

graphGroup.append('g')
.call(d3.axisRight(yScale).ticks(5))
.attr("transform", "translate(350,0)");

var circData = [
  {'type':'bonds','growth':3.3},
  {'type':'bond funds','growth':0},
  {'type':'pstock','growth':-.4},
  {'type':'pbonds','growth':.9},
  {'type':'ploans','growth':0},
  {'type':'debt','growth':.2},
  {'type':'other','growth':-2.5},
];

var yScale2 = d3.scaleLinear()
.domain([0,6])
.range([420,30]);

graphGroup.selectAll(null)
.data(circData)
.enter()
.append('circle')
.attr('cx',250)
.attr('cy', function(d,i) {return yScale2(i)})
.attr('r', 20)
.on('click', function(d) {return console.log(this)})
.style('fill',"#003366");

var multiLinkData = [
    {source: [250,30], target: [350,39.4]},
    {source: [250,95], target: [350,225]},
    {source: [250,160], target: [350,248]},
    {source: [250,225], target: [350,174]},
    {source: [250,290], target: [350,225]},
    {source: [250,355], target: [350,213]},
    {source: [250,420], target: [350,366]},
];

var link = d3.linkHorizontal();

graphGroup.selectAll(null)
.data(circData)
.enter()
.append('circle')
.attr('cx',350)
.attr('cy', function(d,i) {return yScale(d.growth)})
.attr('r', 7)
.on('click', function(d) {return console.log(this)})
.style('fill',"#003366");

d3.select(null)
.selectAll("path")
.data(multiLinkData)
.join("path")
.attr("d", link)
.attr("fill", "none")
.attr("stroke", "#d9d9d9");
<script src="https://d3js.org/d3.v5.min.js"></script>

As we can see, the big circles and smaller circles are appended as desired, however the bezier lines I'm attempting to connect are not drawn. Error reads:

Uncaught TypeError: .selectAll().data().join() is not a function

However this is pretty much exactly how this tutorial lays out the process.

Question

How can I draw the bezier lines as desired? And as a bonus, how can I append them dynamically? (note I hard-coded multiLinkData by painstakingly clicking and recording the x/y positions in the console log).

Upvotes: 1

Views: 197

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102198

You don't have a container for your paths: with d3.select(null).selectAll("path") you're telling D3 to append the paths to the d3.select(null) selection's elements, which makes no sense.

It should be:

graphGroup.selectAll("path")
    //etc...

Here is your code with that change:

var margins = {top:100, bottom:300, left:100, right:100};

var height = 1300;
var width = 900;

var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;

var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);

var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");

var yScale = d3.scaleLinear()
.range([450,0])
.domain([-4,4]);

graphGroup.append('g')
.call(d3.axisRight(yScale).ticks(5))
.attr("transform", "translate(350,0)");

var circData = [
  {'type':'bonds','growth':3.3},
  {'type':'bond funds','growth':0},
  {'type':'pstock','growth':-.4},
  {'type':'pbonds','growth':.9},
  {'type':'ploans','growth':0},
  {'type':'debt','growth':.2},
  {'type':'other','growth':-2.5},
];

var yScale2 = d3.scaleLinear()
.domain([0,6])
.range([420,30]);

graphGroup.selectAll(null)
.data(circData)
.enter()
.append('circle')
.attr('cx',250)
.attr('cy', function(d,i) {return yScale2(i)})
.attr('r', 20)
.on('click', function(d) {return console.log(this)})
.style('fill',"#003366");

var multiLinkData = [
    {source: [250,30], target: [350,39.4]},
    {source: [250,95], target: [350,225]},
    {source: [250,160], target: [350,248]},
    {source: [250,225], target: [350,174]},
    {source: [250,290], target: [350,225]},
    {source: [250,355], target: [350,213]},
    {source: [250,420], target: [350,366]},
];

var link = d3.linkHorizontal();

graphGroup.selectAll(null)
.data(circData)
.enter()
.append('circle')
.attr('cx',350)
.attr('cy', function(d,i) {return yScale(d.growth)})
.attr('r', 7)
.on('click', function(d) {return console.log(this)})
.style('fill',"#003366");

graphGroup.selectAll("path")
.data(multiLinkData)
.join("path")
.attr("d", link)
.attr("fill", "none")
.attr("stroke", "#d9d9d9");
<script src="https://d3js.org/d3.v5.min.js"></script>

Upvotes: 1

Related Questions