Как сделать так, чтобы текст в ячейке древовидной карты не переполнялся? - PullRequest
0 голосов
/ 05 сентября 2018

http://bl.ocks.org/mundhradevang/1387786

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

Я использую https://github.com/iros/underscore.nest underscore.nest для вложения моих данных JSON, чтобы они выглядели так

{"children":[{"name":"Afghanistan",
 "children":[{"name":"Qal eh-ye Now","value":2997},
 {"name":"Mahmud-E Eraqi","value":7407}

Но при рисовании моей древовидной карты текст повсюду, enter image description here

Это мой код D3 для добавления текста:

 cells
.append("text")
.attr("x", function (d) {
  return d.x + d.dx / 2;
})
.attr("y", function (d) {
  return d.y + d.dy / 2;
})
.attr("text-anchor", "middle")
.text(function (d) { return d.children ? null : d.name })

Полный набор данных: https://api.myjson.com/bins/g9p4s

Мой полный код :

 var coordinates = { x: 0, y: 0 };
 var margin = { top: 40, right: 10, bottom: 90, left: 20 }
  var cfg = {
    margin: { top: 40, right: 10, bottom: 90, left: 140 },
    width: 960 - margin.left - margin.right,
    height: 500 - margin.top - margin.bottom,
    color: d3.scale.category20()
  };

  //Put all of the options into a variable called cfg
  if ('undefined' !== typeof options) {
    for (var i in options) {
      if ('undefined' !== typeof options[i]) { cfg[i] = options[i]; }
    }//for i
  }

  var treemap,
    legendCategories,
    uniqueCategories;
  var half = cfg.height / 2;
  var tool = d3.select("body").append("div").attr("class", "toolTip");

  /////////////////////////////////////////////////////////
  //////////// Create the container SVG and g /////////////
  /////////////////////////////////////////////////////////


  //Remove whatever chart with the same id/class was present before
  d3.select(id).select("svg").remove();

  //Initiate the radar chart SVG
  var canvas = d3
    .select(id)
    .append("svg")
    .attr("class", "chart")
    .attr("width", cfg.width + cfg.margin.left + cfg.margin.right)
    .attr("height", cfg.height + cfg.margin.top + cfg.margin.bottom)
    .attr("id", "canvas");
  var innercanvas = canvas
    .append("g")
    .attr("class", "innercanvas")
    .attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")");

  legendCategories = data.children.map(a => a.name);
  uniqueCategories = legendCategories.filter(onlyUnique);

  var categoryTitle = String(categoryKey);
  categoryTitle = categoryTitle.substring(categoryTitle.indexOf("." + 1));
  categoryScale = cfg.color;
  categoryScale.domain(uniqueCategories);
  verticalLegend = d3.svg
    .legend()
    .labelFormat("none")
    .cellPadding(5)
    .orientation("vertical")
    .units(categoryTitle)
    .cellWidth(20)
    .cellHeight(10)
    .place(coordinates)
    .inputScale(categoryScale)
    .cellStepping(10);


  treemap = d3.layout
    .treemap()
    .round(false)
    .size([cfg.width, cfg.height])
    .padding(.25)
    .sticky(true)
    .nodes(data);

  var cells = innercanvas
    .selectAll(".newcell")
    .data(treemap)
    .enter()
    .append("g")
    .attr("class", "newcell");

  cells
    .append("rect")
    .attr("x", function (d) {
      return d.x;
    })
    .attr("y", function (d) {
      return d.y;
    })
    .attr("id", "rectangle")
    .attr("width", function (d) {
      return d.dx;
    })
    .attr("height", function (d) {
      return d.dy;
    })
    .style("fill", function (d) {
      return d.children ? cfg.color(d.name) : null;
    })
    .attr("stroke", "#000000")
    .attr('pointer-events', 'all')
    .on("mousemove", function (d) {
      tool.style("left", d3.event.pageX + 10 + "px")
      tool.style("top", d3.event.pageY - 20 + "px")
      tool.style("display", "inline-block");
      tool.html(d.children ? null : d.name + "<br>" + d.value);
    }).on("mouseout", function (d) {
      tool.style("display", "none");
    });

  cells
    .append("text")
    .attr("x", function (d) {
      return d.x + d.dx / 2;
    })
    .attr("y", function (d) {
      return d.y + d.dy / 2;
    })
    .attr("text-anchor", "middle")
    .text(function (d) { return d.children ? null : d.name })



  canvas
    .append("g")
    .attr("transform", "translate(40,50)")
    .attr("class", "legend")
    .attr("id", "legend")
    .call(verticalLegend);




  function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
  }

1 Ответ

0 голосов
/ 06 сентября 2018

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

Одним из способов было бы добавить обтравочный контур (clip-path) к каждому элементу прямоугольника, но я думаю, что это будет слишком для такой визуализации, поэтому мы будем использовать другие средства.

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

cells
  .append('title')
  .text(function(d){ return d.name });

Теперь давайте посмотрим на текстовые элементы. Установите font-family и font-size для текстовых узлов в вашей таблице стилей или заголовке документа, чтобы мы имели дело с предсказуемыми размерами текста.

.newcell text {
  font-family: Arial, sans-serif;
  font-size: 10px;
}

Я бы посоветовал немного сместить текст вниз, так как текущий код устанавливает базовую линию текста в вертикальном центре ячейки, которая является слишком высокой. Здесь я добавил смещение 0.35em:

selection
  .append("text")
  .attr("x", function (d) {
    return d.x + d.dx / 2;
  })
  .attr("y", function (d) {
    return d.y + d.dy / 2;
  })
  .attr('dy', '.35em')
  .attr("text-anchor", "middle")
  .text(function (d) {
    return d.children ? '' : d.name;
  })

Мы можем фильтровать видимость текстовых узлов, изменяя непрозрачность (что означает, что мы можем легко переключать непрозрачность, чтобы показать / скрыть текст), используя

    cells
    .style('opacity', function(d){
      // some function returning 0 or 1
    });

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

  var minHeight = 12,
  minWidth = 20;
  cells
    .style('opacity', function(d){
      if ( d.dx <= minWidth || d.dy <= minHeight ) {
        return 0
      };
      return 1;
    });

Другим способом может быть вычисление приблизительной ширины слова и проверка его по ширине поля. Я нашел таблицу средней ширины символов для обычных шрифтов в https://www.math.utah.edu/~beebe/fonts/afm-widths.html,, поэтому мы можем угадать ширину слова, умножив длину слова на среднюю ширину символа (в точках), и умножив ее на размер шрифта ( в точках) и коэффициент пересчета для преобразования точек в пиксели

  var pt_px = 0.75, // convert font size in pt into pixels
  font_size = 10,   // or whatever it is set to in the stylesheet
  averageLetterWidth = 0.58344; // average character width for Arial

  cells
    .style('opacity', function(d){
      if ( d.name.length * averageLetterWidth * pt_px * font_size >= d.dx ) {
        return 0
      }
      return 1;
    });

И еще один способ - сравнить размер ограничительной рамки текстового элемента с высотой и шириной ячейки:

  cells
    .style('opacity', function(d){
      var bbox = this.getBBox();
      if ( d.dx <= bbox.width || d.dy <= bbox.height ) {
        return 0;
      }
      return 1;
    });

Вы также можете убедиться, что между краем текста и рамкой есть отступы:

  var h_pad = 2, // 2 pixels vertical padding
  v_pad = 4; // 4 pixels of horizontal padding (2 px at each side)
  cells
    .style('opacity', function(d){
      var bbox = this.getBBox();
      if ( d.dx <= bbox.width + h_pad || d.dy <= bbox.height + v_pad ) {
        return 0;
      }
      return 1;
    });

Вы можете выбрать, какой из этих методов наиболее подходит для вашего варианта использования (или объединить их все!). Я собрал блок, который демонстрирует различные вычисления и их эффекты .

...