D3 помещает дуговые метки в круговую диаграмму, если места достаточно

Я добавлю текстовый элемент в каждую дугу моей круговой диаграммы (в центре) - как показано в этом примере: http://bl.ocks.org/mbostock/3887235

Но я помещу текстовый элемент, только если места достаточно для всего текста, поэтому я должен сравнить размер моего текстового элемента с "доступным" пространством в каждой дуге.

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

Спасибо...!

2 ответа

Решение

Этот вопрос задавался несколько раз раньше.

Решения, которые я предложил, есть rotate лейбл, но он никогда не удовлетворял меня. Частично это было ужасным рендерингом шрифтов, сделанным некоторыми браузерами и потерей разборчивости, который приносит и странный flip когда одна метка пересекает 180° линия. В некоторых случаях результаты были приемлемыми и неизбежными, например, когда этикетки были слишком длинными.

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

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

Скрыть переполненные метки

Сравните оригинал, который имеет >= 65 метка переполняется в Solution, где метка переполнения исчезла.

Уменьшение проблемы

Ключевое понимание заключается в том, чтобы увидеть, что эта проблема заключается в поиске того, содержится ли один выпуклый многоугольник (прямоугольник, ограничивающий прямоугольник) внутри другого выпуклого многоугольника (-ish) (клин).

Задача может быть сведена к тому, чтобы определить, все ли точки прямоугольника лежат внутри клина или нет. Если они это сделают, то прямоугольник находится внутри дуги.

Точка лежит внутри клина

Теперь эта часть проста. Все, что нужно сделать, это проверить:

  1. Расстояние точки от центра меньше, чем radius
  2. Угол, образованный точкой в ​​центре, находится между startAngle а также endAngle дуги.
function pointIsInArc(pt, ptData, d3Arc) {
  // Center of the arc is assumed to be 0,0
  // (pt.x, pt.y) are assumed to be relative to the center
  var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius
      r2 = d3Arc.outerRadius()(ptData),
      theta1 = d3Arc.startAngle()(ptData),
      theta2 = d3Arc.endAngle()(ptData);

  var dist = pt.x * pt.x + pt.y * pt.y,
      angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system.

  angle = (angle < 0) ? (angle + Math.PI * 2) : angle;

  return (r1 * r1 <= dist) && (dist <= r2 * r2) && 
         (theta1 <= angle) && (angle <= theta2);
}

Найдите ограничивающий прямоугольник с ярлыками

Теперь, когда мы имеем это в стороне, вторая часть выясняет, каковы четыре угла прямоугольника. Это также легко:

g.append("text")
    .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .text(function(d) { return d.data.age; })
    .each(function (d) {
       var bb = this.getBBox(),
           center = arc.centroid(d);

       var topLeft = {
         x : center[0] + bb.x,
         y : center[1] + bb.y
       };

       var topRight = {
         x : topLeft.x + bb.width,
         y : topLeft.y
       };

       var bottomLeft = {
         x : topLeft.x,
         y : topLeft.y + bb.height
       };

       var bottomRight = {
         x : topLeft.x + bb.width,
         y : topLeft.y + bb.height
       };

       d.visible = pointIsInArc(topLeft, d, arc) &&
                   pointIsInArc(topRight, d, arc) &&
                   pointIsInArc(bottomLeft, d, arc) &&
                   pointIsInArc(bottomRight, d, arc);

    })
    .style('display', function (d) { return d.visible ? null : "none"; });

Суть решения находится в each функция. Сначала мы размещаем текст в нужном месте, чтобы DOM отображал его. Затем мы используем getBBox() метод, чтобы получить ограничивающую рамку text в пространстве пользователя. Новое пользовательское пространство создается любым элементом, который имеет transform Атрибут установлен на него. Этот элемент, в нашем случае, является text сама коробка. Таким образом, возвращаемая ограничительная рамка относится к центру текста, так как мы установили text-anchor быть middle,

Положение text относительно arc можно рассчитать, так как мы применили преобразование 'translate(' + arc.centroid(d) + ')' к этому. Как только у нас есть центр, мы просто вычисляем topLeft, topRight, bottomLeft а также bottomRight указывает на это и посмотреть, все ли они лежат внутри wedge,

Наконец, мы определяем, все ли точки лежат внутри клина, и если они не подходят, установите display CSS свойство для none,

Рабочая демонстрация

оригинал

Решение

Заметка

  1. Я использую innerRadius который, если не ноль, делает wedge невыпуклый, который сделает вычисления намного более сложными! Тем не менее, я думаю, что здесь опасность невелика, так как единственный случай, когда она может потерпеть неудачу, - это, и, честно говоря, я не думаю, что это случится часто (у меня были проблемы с поиском этого контрпримера):

    Случай неудачи

  2. x а также y перевернуты и y имеет отрицательный знак при расчете Math.atan2, Это из-за разницы между тем, как Math.atan2 а также d3.svg.arc просмотреть систему координат и направление положительного y с svg,

    Система координат для Math.atan2

    Система координат для <code>θ = Math.atan2(y, x) = Math.atan2(-svg.y, x)</code></p><p><strong>Система координат для</strong> <code>d3.svg.arc</code></p><p><img src=

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

К сожалению, нет простого способа сделать то, что вы пытаетесь сделать (тестирование на уровне пикселей). Смотрите, например, этот вопрос для получения дополнительной информации. Я бы предложил просто поместить текстовые метки вне круговой диаграммы, чтобы вы не столкнулись с этой проблемой.

Другие вопросы по тегам