user7558372
user7558372

Reputation:

Place the labels on the axes correctly in D3.js

I have an heatmap that shows some data. If user clicks on a label (x or y axis), data are correctly sort. My problem is the correct positioning of the labels before and after the sorting of the data.

This is the PLUNKER.

As you can see, initially the chart is like this:

enter image description here

The labels on the y axis are correct, those on the x axis are not, because they should all be at the same height. Instead they come down.

When I click on ddd it becomes:

enter image description here

Same problem.

when I click on 2001 it becomes:

enter image description here

Now it's all a mess.

The code that controls the positioning of the labels is this:

var rowLabels = svg.append('g')
    .attr('class', 'rowLabels')
    .selectAll('.rowLabels')
    .data(regionsName)
    .enter().append('text')
    .text(function(d) {
        return d;
    })
    .attr('x', 0)
    .attr('y', function(d, i) {
        return i * cellSize;
    })
    .attr('transform', function(d, i) {
        return 'translate(-25, 11)';
    })
    .attr('class', 'rowLabel mono')
    .attr('id', function(d) {
        return 'rowLabel_' + regionsName.indexOf(d);            
    })
    .attr('font-weight', 'normal')
    .on('mouseover', function(d) {
        d3.select(this).attr('font-weight', 'bold').style('fill', 'red');
    })
    .on('mouseout', function(d) {
        d3.select(this).attr('font-weight', 'normal').style('fill', 'black');
    })
    .on('click', function(d, i) {
        rowSortOrder = !rowSortOrder;
        sortByValues('r', i, rowSortOrder);
    });

var colLabels = svg.append('g')
    .attr('class', 'colLabels')
    .selectAll('.colLabels')
    .data(yearsName)
    .enter().append('text')
    .text(function(d) {
        return d;
    })
    .attr('x', 0)
    .attr('y', function(d, i) {
        return i * cellSize;
    })
    .attr('transform', function(d, i) {
        return 'translate(0, -3) rotate(-65)';
    })
    .attr('class', 'colLabel mono')
    .attr('id', function(d) {
        return 'colLabel_' + yearsName.indexOf(d);          
    })
    .attr('font-weight', 'normal')
    .style('text-anchor', 'left')
    .attr('dx', '.8em')
    .attr('dy', '.5em')
    .on('mouseover', function(d) {
        d3.select(this).attr('font-weight', 'bold').style('fill', 'red');
    })
    .on('mouseout', function(d) {
        d3.select(this).attr('font-weight', 'normal').style('fill', 'black');
    })
    .on('click', function(d, i) {
        colSortOrder = !colSortOrder;
        sortByValues('c', i, colSortOrder);
    });

and:

t.selectAll('.colLabel')
    .attr('y', function(d, i) {
        return sorted.indexOf(i) * cellSize;
    })
    .attr('transform', function(d, i) {
        return 'translate(-10, 2) rotate(-65) rotate(0, 0, ' + (sorted.indexOf(i) * cellSize) + ')';
    });

and:

t.selectAll('.rowLabel')
    .attr('y', function(d, i) {
        return sorted.indexOf(i) * cellSize;
    })
    .attr('transform', function(d, i) {
        return 'translate(-5, 0)';
    });

I changed it a thousand times, thinking about it, but nothing. Would anyone know how to help me?

Upvotes: 2

Views: 422

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38161

You are over complicating how you set the axis labels. When rotating labels, labels are rotated from the coordinate system origin - not the text anchor. We can see this better if we compare the labels as is and if we remove the transform and just use the x,y attributes you specify:

.attr('x', 0)
.attr('y', function(d, i) {
    return i * cellSize;
})
//.attr('transform', function(d, i) {
//    return 'translate(-25, 11)';
//})

enter image description here

We can see all labels being rotated together around a common point. A hint that something might be awry in the label placement also comes from the code that dynamically sets a y value and a fixed x value for to place values that differ only in x values.

We can simplify this, instead of setting x, y, and transform, let's just set transform. First we'll modify the coordinate system so that the origin of each labels coordinate system is where it is anchored. Then we'll rotate:

.attr('transform', function(d, i) {
    return 'translate('+(i*cellSize)+',2) rotate(-65)';
})

We'll also want to update the update function:

t.selectAll('.colLabel')
  .attr('transform', function(d, i) {
    return 'translate('+(sorted.indexOf(i)*cellSize)+',2) rotate(-65)';
  })

Giving us:

enter image description here

The other issue, the spacing of the y axis labels, you are updating the x position of the labels when you sort. This is un-necessary, the labels only need to move vertically, we can remove the transform change and just use:

t.selectAll('.rowLabel')
  .attr('y', function(d, i) {
     return sorted.indexOf(i) * cellSize;
   })

Here's an updated plunkr.

Upvotes: 1

Related Questions