В D3 масштабируемые солнечные лучи, как я могу сделать маркировку зависимой от уровня масштабирования?

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

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

У меня вопрос, как мне вычислить "относительную" глубину при определенном уровне масштабирования, а затем отобразить метки на основе этого?

Насколько я понимаю, d.depth обозначает только абсолютные уровни.

2 ответа

Решение

Я предполагаю, что вы работаете на примере Джейсона Дэвиса.

Соответствующий код из его скрипта

  function click(d) {
    path.transition()
      .duration(duration)
      .attrTween("d", arcTween(d));

    // Somewhat of a hack as we rely on arcTween updating the scales.
    text.style("visibility", function(e) {
          return isParentOf(d, e) ? null : d3.select(this).style("visibility");
        })
      .transition()
        .duration(duration)
        .attrTween("text-anchor", function(d) {
          return function() {
            return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
          };
        })
        .attrTween("transform", function(d) {
          var multiline = (d.name || "").split(" ").length > 1;
          return function() {
            var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
                rotate = angle + (multiline ? -.5 : 0);
            return "rotate(" + rotate + ")translate(" 
                    + (y(d.y) + padding) + ")rotate(" 
                    + (angle > 90 ? -180 : 0) + ")";
          };
        })
        .style("fill-opacity", function(e) { 
                 return isParentOf(d, e) ? 1 : 1e-6; 
        })
        .each("end", function(e) {
          d3.select(this).style("visibility", 
                    isParentOf(d, e) ? null : "hidden");
        });
  }

Обратите внимание, что некоторые из этих функций ссылаются на два разных объекта данных, d против e, Это потому, что если он не маскируется внутренней функцией, d внутри функции щелчка находится объект данных выбранного элемента, который становится центром круга.

Если он дает внутренней функции другое имя для объекта данных (function(e){}), то это объект данных, связанный с отдельным элементом, атрибуты которого изменены. Таким образом, он может вызывать функции, которые сравнивают два объекта данных, чтобы определить, должен ли данный элемент быть скрытым или нет на этом уровне масштабирования.

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

          if (e.depth > d.depth + 3) return "hidden";

То, где вы добавляете этот код, зависит от выбора стиля - Джейсон Дэвис фактически меняет непрозрачность или видимость текста в трех точках: видимость устанавливается до и после перехода (во время события "конец"), а непрозрачность между ними исчезает. Вы хотите, чтобы ваши ярлыки появлялись и исчезали одним щелчком мыши, или вы хотите, чтобы они появлялись и исчезали?

Я пока не могу комментировать, так что вот что вам может пригодиться, если вы поищите код Дэвиса на его стороне и найдете минимизированную версию, которая вообще не поможет с этим ответом - полная версия перенесена сюда: https://code.google.com/p/testprogramming/source/browse/trunk/javascript/svg/d3/test/wheel.js?r=394&spec=svn394

Все важные функции isParentOf отсутствуют в ответе выше и должны быть найдены в полной версии.

- дополнительная информация: если вы хотите, чтобы метка отображалась на основе уровня "масштабирования", также важно отметить, что солнечные лучи не используют функцию.zoom(), и вам придется искать путь вручную. В моем случае я хотел скрыть все метки на верхнем уровне масштабирования, но показать их, если был выбран какой-либо другой уровень. Для этого я вначале скрываю текст, а затем при каждом нажатии использую следующий тест.

    var text = g.append("text")
    .attr("class","mylabel")
    .attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
    .attr("x", function(d) { return y(d.y); })
    .attr("dx", "4") // left - margin for text
    .attr("dy", ".25em") // vertical-align of text in cell
    .text(function(d) { return (d.name == 'root') ? ('') : d.name; })
    .attr("font-size", function(d) { return d.ci_type === 'type' ? 12 : 10}) //font-size of text
    //.attr("visibility",function(d) { return d.dx < 0.009? "hidden" : "visible"}) // hide text of labels of partitions less than 2%
    .attr("visibility", "hidden") // hide labels at root level - starting level


    function click(d) {
    // fade out all text elements
    text.transition().attr("opacity", 0);
    path.transition()
    .duration(750)
    .attrTween("d", arcTween(d))
    .each("end", function(e, i) {
      // check if the animated element's data e lies within the visible angle span given in d
      if (e.x >= d.x && e.x < (d.x + d.dx)) {
        // get a selection of the associated text element
        var arcText = d3.select(this.parentNode).select("text");
        // fade in the text element and recalculate positions
        arcText.transition().duration(750)
          .attr("opacity", 1)
          .attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" })
          .attr("x", function(d) { return y(d.y); })

      }
    });


    // if the vis is at the 'root' level hide text, otherwise show    <! here is the test!  
    var str = d.name;
    var patt = 'root';
    var atRoot = (str === patt) ? true : false ;

    //console.log(atRoot); 

    //console.log( d.name ) ;

    text.attr("visibility",function(d) { return atRoot ? "hidden" : "visible"})  


    // end of click      
     }
Другие вопросы по тегам