D3: проблема с масштабированием легенды

1

Я пытаюсь изменить карту Бостока США по безработице, которая применяет хроматическую шкалу D3.

Теперь я могу изменить размеры ковша, как мне представляется, для построения моих данных, однако, когда я это делаю, легенда, кажется, растет в геометрической прогрессии, и я не могу сделать ее пригодной к желаемой ширине и соответствующим образом масштабировать интервалы.

Пожалуйста, ознакомьтесь с прилагаемым jsfiddle, где я продемонстрирую возникшую проблему. Я хотел бы изменить легенду двумя способами:

  1. пространство между тиками фиксировано, например, 50 пикселей
  2. пространство между тиками является функцией масштаба, но легенда по-прежнему соответствует желаемой ширине (например, 500 пикселей)

Моя проблема в том, что я, кажется, не в состоянии изменить параметры в следующей строке кода (я надеюсь, что это не значение по умолчанию в хроматическом сценарии масштаба).

g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
  .select(".domain")
    .remove();
  • 0
    Ответ? Кто-нибудь? :)
  • 1
    Я объясняю, как сделать «путь 2». Ваш «путь 1» включает в себя такой большой рефактор, что код будет практически другим.
Теги:
d3.js

1 ответ

4
Лучший ответ

В коде есть только две проблемы, которые легко исправить.

Первая проблема - это домен:

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

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

Как вы можете видеть, вы передаете массив с несколькими значениями. Однако для линейного масштаба с двумя значениями в диапазоне вам нужно передать массив с двумя значениями.

Следовательно, это должно быть:

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]);

Вторая проблема здесь:

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

Поскольку myDomain - это массив с несколькими значениями, вы передаете второе значение здесь. Но вы не хотите второго значения, вы хотите получить последнее значение.

Следовательно, это должно быть:

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

Вот код с этими изменениями (я удалил карту, мы не нуждаемся в ней для этого ответа, а также переместили легенду влево, чтобы она лучше соответствовала SO 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>
  • 0
    Спасибо Герардо, это сработало для меня. Похоже, что нет «родного» способа создания легенды, как я указал в (1). Я, вероятно, могу сделать это, начиная с нуля, просто позор, что здесь нет возможности сделать это.
  • 0
    Конечно, есть способ сделать это с фиксированной шириной, я только что сказал, что это потребует слишком больших изменений в коде, до такой степени, что было бы легче начать с нуля.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню