How to fix overlapping tick labels for svg legend using D3

Viewed 39

I have an color legend created using linear gradient and D3 concept. The issue currently I am facing is that the ticks labels of the legend gets overlapped.

I have to show the Min and Max values all the time and extra ticks as well.

I have shared code snippet and also the JS fiddle. I tried few things but nothing seems to work for me.

const itemColor = [{
    offset: 0.0,
    color: "#ff0000"
  },
  {
    offset: 0.1,
    color: "#ffff00"
  },
  {
    offset: 0.4,
    color: "#00ff00"
  },
  {
    offset: 0.5,
    color: "#00ffff"
  },
  {
    offset: 0.8,
    color: "#0000ff"
  },
  {
    offset: 1.0,
    color: "#ff00ff"
  }
];

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

let min = 1918
let max = 3780
 
const linearColorScale = d3.scaleLinear().domain([min, max]).range([0, 150]); 
const id = "linear-gradient-0";

const linearGradient = svg.append("defs")
  .append("linearGradient")
  .attr("id", "linear-gradient-1")
  .attr("x1", "0%")
  .attr("x2", "100%")
  .attr("y1", "0%")
  .attr("y2", "0%");

// append the color
linearGradient
  .selectAll("stop")
  .data(itemColor)
  .enter()
  .append("stop")
  .attr("offset", function(data) {
    return linearColorScale(min + (data.offset * (max - min))) / 1.5 + "%";
  })
  .attr("stop-color", function(data) {
    return data.color;
  });

// draw the rectangle and fill with gradient
svg.append("rect")
  .attr("x", 10)
  .attr("y", 108)
  .attr("width", 150)
  .attr("height", 20)
  .style("fill", "url(#linear-gradient-1)");
  
 svg.append("text").text("Linear Scale").attr("y", 93)

// create tick
svg.append("g").attr("transform", "translate(10,130)").call(d3.axisBottom(linearColorScale).tickValues( linearColorScale.ticks( 3 ).concat( linearColorScale.domain() ) ));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.0/d3.min.js"></script>
<svg width="500"></svg>

JS Fiddle: https://jsfiddle.net/1g7qmh3y/2/

1 Answers

If you are both a) constrained in the space you can use, and b) must show the extent of the domain plus the inner ticks; then you can consider another axis on top of the colour bar. This way you show the min/ max on the top axis and the intermediate ticks on the bottom axis. See below:

const itemColor = [ { offset: 0.0, color: "#ff0000" }, { offset: 0.1, color: "#ffff00" }, { offset: 0.4, color: "#00ff00" }, { offset: 0.5, color: "#00ffff" }, { offset: 0.8, color: "#0000ff" }, { offset: 1.0, color: "#ff00ff" } ];

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

let min = 1918
let max = 3780
 
const linearColorScale = d3.scaleLinear().domain([min, max]).range([0, 150]); 
const id = "linear-gradient-0";

const linearGradient = svg.append("defs")
  .append("linearGradient")
  .attr("id", "linear-gradient-1")
  .attr("x1", "0%")
  .attr("x2", "100%")
  .attr("y1", "0%")
  .attr("y2", "0%");

// append the color
linearGradient
  .selectAll("stop")
  .data(itemColor)
  .enter()
  .append("stop")
  .attr("offset", function(data) {
    return linearColorScale(min + (data.offset * (max - min))) / 1.5 + "%";
  })
  .attr("stop-color", function(data) {
    return data.color;
  });

// draw the rectangle and fill with gradient
svg.append("rect")
  .attr("x", 10)
  .attr("y", 108)
  .attr("width", 150)
  .attr("height", 20)
  .style("fill", "url(#linear-gradient-1)");
  
// move it up slightly to accommodate upper axis
svg.append("text").text("Linear Scale").attr("y", 73)

// create lower ticks - removed domain of scale and removed outer ticks
svg.append("g")
  .attr("transform", "translate(10,130)")
  .call(d3.axisBottom(linearColorScale)
    .tickValues( linearColorScale.ticks(3))
    .tickSizeOuter(0)
  );

// create another axis for upper ticks
svg.append("g")
  .attr("transform", "translate(10,103)")
  .call(d3.axisTop(linearColorScale)
    .tickValues(linearColorScale.domain()));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.0/d3.min.js"></script>
<svg width="500"></svg>

Related