Martin Burch
Martin Burch

Reputation: 2931

Preventing d3 chords from overlapping

This demo (not my code) compares two different categories of items to each other, so -- unlike a chord diagram where any item can be connected to any other item -- coffee shops can't be connected to coffee shops and states can't be connected to states.

Some wedges look like this, if Dunkin' Donuts come first:

enter image description here

But other wedges look like this, if Starbucks has a higher value in the state:

enter image description here

Unnecessary overlap, in my opinion. The order of the elements shouldn't be determined by their value, but instead by the order of the elements on the left side -- that is, always Dunkin' first.

I see a sort happening in

 var chord = d3.layout.chord()
             .padding(.02)
             .sortSubgroups(d3.descending)

but I'm not sure how I can specify a custom sort for the state elements. It makes sense to have the coffee shop subgroups sorted desc (or asc) but the states shouldn't get the same treatment.

How do we know if a chord is a state or not? It seems that information can be pulled by passing a chord to the rdr instance of the chordRdr function, which ties the matrix, which is what's being sorted, to meta-information from the mmap object.

How can I create a conditional subgroup sort?

Upvotes: 3

Views: 757

Answers (2)

Adam Pearce
Adam Pearce

Reputation: 9293

I would not recommend actually using this solution:

//all the values in matrix are integers
//we can track the numbers that are starbucks stores
//by adding a small decimal to them
matrix = matrix.map(function(row){
  return row.map(function(d, i){
    return d + (i == row.length - 1 ? 1e-7 : 0) })
})

//now we can do a lexicographic sort 
//first checking for the presence of a decimal and then size
var chord = d3.layout.chord()
    .sortSubgroups(function(a, b){
      if (a != Math.round(a)) return false
      if (b != Math.round(b)) return true
      return b < a ? -1 : b > a ? 1 : 0;
    })

Modifying d3.layout.chord or starting fresh with something simpler that doesn't require a redundant matrix of values would probably work better in most situations.

Upvotes: 1

Mike Precup
Mike Precup

Reputation: 4218

Short answer: you can't do it in d3.

This is how d3 performs the sort:

// Sort subgroups…
if (sortSubgroups) {
  subgroupIndex.forEach(function(d, i) {
    d.sort(function(a, b) {
      return sortSubgroups(matrix[i][a], matrix[i][b]);
    });
  });
}

Source

It will perform the sort for every subgroup.

The only way I can think of to make this work legitimately would be to have a custom copy of d3.chord.layout(), which can be found at the above link, if you want to toy with it.

Upvotes: 2

Related Questions