D3: issue with legend scaling

纵然是瞬间 提交于 2020-01-03 03:28:12

问题


I am trying to amend M Bostock's US unemployment choropleth map that applies D3 scale chromatic.

I am now able to amend the bucket-sizes as I please to plot my data, however when I do so the legend seems to grow in size exponentially and I am unable to make it fit to a desired width and scale the intervals appropriately.

Please see the attached jsfiddle where I demonstrate the issue encountered. I would like to amend the legend in two ways:

  1. space between ticks is fixed, e.g. 50px
  2. space between ticks is a function of scale, but legend still fits within desired width (e.g. 500px)

My problem is that I do not seem to be able to amend parameters in the following line of code (I am hoping this is not a default in the scale chromatic script..)

g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
  .select(".domain")
    .remove();

回答1:


There are only two problems in your code, both easy to fix.

The first problem is the domain here:

var myDomain = [1, 5, 8, 9, 12, 18, 20, 25]

var x = d3.scaleLinear()
    .domain(myDomain)
    .rangeRound([600, 860]);

As you can see, you're passing an array with several values. However, for a linear scale with just two values in the range, you have to pass an array with just two values.

Therefore, it should be:

var myDomain = [1, 5, 8, 9, 12, 18, 20, 25]

var x = d3.scaleLinear()
    .domain(d3.extent(myDomain))//just two values here
    .rangeRound([600, 860]);

The second problem is here:

if (d[1] == null) d[1] = x.domain()[1];
//this is the second element -------^

Since myDomain is an array with several values, you're passing the second value here. But you don't want the second value, you want the last value.

Therefore, it should be:

if (d[1] == null) d[1] = x.domain()[x.domain().length - 1];
//now this is the last element --------------^

Here is the code with those changes (I removed the map, we don't need it for this answer, and also moved the legend to the left, so it better fits S.O. snippet):

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var myDomain = [1, 5, 8, 9, 12, 18, 20, 25]

var x = d3.scaleLinear()
    .domain(d3.extent(myDomain))
    .rangeRound([200, 460]);

var color = d3.scaleThreshold()
    .domain(myDomain)
    .range(d3.schemeBlues[9]);

var g = svg.append("g")
    .attr("class", "key");

g.selectAll("rect")
  .data(color.range().map(function(d) {
      d = color.invertExtent(d);
      if (d[0] == null) d[0] = x.domain()[0];
      if (d[1] == null) d[1] = x.domain()[x.domain().length - 1];
      return d;
    }))
  .enter().append("rect")
    .attr("height", 8)
    .attr("x", function(d) { return x(d[0]); })
    .attr("width", function(d) { return x(d[1]) - x(d[0]); })
    .attr("fill", function(d) { return color(d[0]); });

g.append("text")
    .attr("class", "caption")
    .attr("x", x.range()[0])
    .attr("y", -6)
    .attr("fill", "#000")
    .attr("text-anchor", "start")
    .attr("font-weight", "bold")
    .text("Unemployment rate");

g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
  .select(".domain")
    .remove();
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<svg width="600" height="200"></svg>


来源:https://stackoverflow.com/questions/48138332/d3-issue-with-legend-scaling

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!