Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a horizontal legend?

In D3.js how can I create a horizontal legend with 2 rows?

This is current JavaScript code that creates a legend:

var legendGroup = svg.append("g")
    .attr("transform", "translate("+(width-50)+",30)");

  var legend = legendGroup.selectAll(".legend")
    .data(nations.map(d=>d.name))
        .enter()
        .append("g")
        .attr("transform", (d,i)=>"translate(0," + 20*i + ")")

  var legendRects = legend.append("rect")
    .attr("width", 10)
        .attr("height", 10)
        .attr("fill", d=> colorScale(d));

  var legendText = legend.append("text")
    .attr("x", 14)
        .attr("y", 8)
    .text(d=>d);
like image 666
Dinosaurius Avatar asked Nov 29 '25 11:11

Dinosaurius


2 Answers

The code you posted is a column, as you stated, because it translates each piece of the legend 20 pixels below the last: "translate(0," + 20*i + ")". By manipulating the translate statement you can create a 2 row legend if you know how many elements are in your array, though even if you don't, you can specify how many elements can be in each row.

In the following code block, n refers to how many items should be in a row, itemWidth a pixel value for the width of each legend entry, and itemHeight the height.

I don't know how many legend items you have, but you could set it up as such:

.attr("transform", function(d,i) { return 
               "translate(" + i%n * itemWidth + 
               "," +
               Math.floor(i/n) * itemHeight + ")"; })

This should work like so:

var data = ["Cat A","Cat B","Cat C", "Cat D", "Dog A", "Dog B", "Dog C", "Dog D"];
var n = data.length/2;
var itemWidth =80;
var itemHeight = 18;

var svg = d3.select("svg");

var color = d3.scale.category10();


var legend = svg.selectAll(".legend")
	.data(data)
	.enter()
	.append("g")
	.attr("transform", function(d,i) { return "translate(" + i%n * itemWidth + "," + Math.floor(i/n) * itemHeight + ")"; })
	.attr("class","legend");
	
var rects = legend.append('rect')
	.attr("width",15)
	.attr("height",15)
	.attr("fill", function(d,i) { return color(i); });
	
var text = legend.append('text')
	.attr("x", 15)
	.attr("y",12)
	.text(function(d) { return d; });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>


<svg width="500" height="100"></svg>

Edit: Here's a snippet moving the entire horizontal legend (using four rows to show the movement better to the right side better in this narrow view) based on the comment below:

var data = ["Cat A","Cat B","Cat C", "Cat D", "Dog A", "Dog B", "Dog C", "Dog D"];
var n = data.length/4;
var itemWidth =80;
var itemHeight = 18;
var width = 500;

var svg = d3.select("svg");

var color = d3.scale.category10();

var legendGroup = svg.append("g")
    .attr("transform", "translate("+(width-150)+",10)");

var legend = legendGroup.selectAll(".legend")
	.data(data)
	.enter()
	.append("g")
	.attr("transform", function(d,i) { return "translate(" + i%n * itemWidth + "," + Math.floor(i/n) * itemHeight + ")"; })
	.attr("class","legend");
	
var rects = legend.append('rect')
	.attr("width",15)
	.attr("height",15)
	.attr("fill", function(d,i) { return color(i); });
	
var text = legend.append('text')
	.attr("x", 15)
	.attr("y",12)
	.text(function(d) { return d; });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<svg width="500" height="200"> </svg>
like image 193
Andrew Reid Avatar answered Dec 02 '25 00:12

Andrew Reid


const legendData = ['The United States', 'Brazil', 'India', 'China', 'United Arab Emirates']  // The names which you want to appear as legends should be inside this array.

const legend = d3.select('svg')
  .append('g')
  .attr('transform', 'translate(' + (margin.left + margin.right + 60) + ',' + (margin.top - 20) + ')')
  .selectAll('g')
  .data(legendData)
  .enter()
  .append('g');

legend.append('rect')
  .attr('fill', (d, i) => color(d))     //   const color = d3.scaleOrdinal(d3.schemeCategory10);
  .attr('height', 15)
  .attr('width', 15);

legend.append('text')
  .attr('x', 18)
  .attr('y', 10)
  .attr('dy', '.15em')
  .text((d, i) => d)
  .style('text-anchor', 'start')
  .style('font-size', 12);

// Now space the groups out after they have been appended:
const padding = 10;
legend.attr('transform', function (d, i) {
  return 'translate(' + (d3.sum(legendData, function (e, j) {
    if (j < i) { return legend.nodes()[j].getBBox().width; } else { return 0; }
  }) + padding * i) + ',0)';
});
like image 20
Yaseen Avatar answered Dec 02 '25 01:12

Yaseen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!