d3。分区旭日形色:旋转文本和其他故障

时间:2019-01-02 21:44:12

标签: d3.js hierarchy partition

通常是d3.hierarchy,尤其是d3.partition 那个很棒的库中最喜欢的工具。但我正在将它们应用于 第一次出现放射状“朝阳”现象,似乎缺少了一些 重要的位。

下面是MCVE 产生森伯斯特的示例,以说明我的主要问题: sample sunburst

旋转文字

旋转文本标签超过180度是一个常见问题;    cf. this recent SO post

以下 @mbostock's own example 具有以下转换代码:

    .attr("transform", function(d) { 
                    const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
                    const y = (d.y0 + d.y1) / 2;
                    return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
                    })           

,但是在转换中使用此translate()会使文本偏离很远 图表?

所以下面的代码根据相同的平均值进行旋转 内/外弧半径,并将标签放置在右侧(角度<180) 除了使用text-anchor对齐方式以外,其他方法相同 将两个深度的标签相对于同一公共圆对齐。

  • 1我必须修改1.22的半径才能微调半径 (右侧)标签靠近线条; 为什么

  • 2这对除根标签外的所有标签都有效,我想要 居中我该怎么做?

  • 3但是新的左侧标签(> 180度)放置不正确, 并且我需要添加特定于深度的hackRatio才能 适度地翻译,以至于使他们如此接近; 为什么?

  • 4一个更深层次的问题是弄清楚如何使用相同的 text-anchor用于其他标签的对齐技巧吗?我想“原地”进行旋转 对齐之前被应用; 我该怎么做?

d3.hierarchy.sum()应该如何工作?

标签的括号中还包含freq属性。 yearHier数据 仅为数据提供此属性。我的印象 d3.hierarchy.sum()d3.partition文档 是因为对根上的sum()的调用会计算出 非叶子(“ ...用于此节点和后遍历中的每个后代”); 为什么这些频率为零?

因此,作为替代方案,我尝试使用yearHierFreq数据,该数据可得出根源和每年的总频率。但是使用它,d3.partition仅分配年份弧的三分之二,并且每年仅分配月份弧的一半;请参见下面的渲染。就像内部节点的freq被重复计算了一样; 为什么

using yearHierFreq data with all freq provided     

<script src="https://d3js.org/d3.v5.min.js"></script>
<script>

var ColorNames = ["Blue", "Gray", "Purple", "Fuchsia", "Aqua", "Maroon", "Olive", "Yellow", "Teal", "Navy", "Green", "Silver", "Red", "Lime"];

// following after http://bl.ocks.org/kerryrodden/7090426

var width = 900;
var height = 900;
var radius = Math.min(width, height) / 2 * 0.7;

var vis = d3.select("#chart").append("svg:svg")
    .attr("width", width)
    .attr("height", height)
    .append("svg:g")
    .attr("id", "container")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var partition = d3.partition()
    .size([2 * Math.PI, radius * radius]);

var arc = d3.arc()
    .startAngle(function(d)  { return d.x0; })
    .endAngle(function(d)    { return d.x1; })
    .innerRadius(function(d) { return Math.sqrt(d.y0); })
    .outerRadius(function(d) { return Math.sqrt(d.y1); });

function createSunburst(json) {

 vis.append("svg:circle")
      .attr("r", radius)
      .style("opacity", 0);

  // Turn the data into a d3 hierarchy and calculate the sums.
  var root = d3.hierarchy(json)
      .sum(function(d) { return d.freq; })
      .sort(function(a, b) { return b.name - a.name; });

  var partition = d3.partition()
    .size([2 * Math.PI, radius * radius]);

  var nodes = partition(root).descendants();

   var path = vis.data([json]).selectAll("path")
      .data(nodes)
      .enter().append("svg:path")
      .attr("display", function(d) { return d.depth ? null : "none"; })
      .attr("d", arc)
      .attr("fill-rule", "evenodd")
      .style("fill", function(d,i) { return ColorNames[i % 14]; })
      .style("opacity", 1);

    var texts = vis.selectAll("text")
        .data(nodes)
.enter().append("text")

/*      .attr("transform", function(d) { 
            // https://beta.observablehq.com/@mbostock/d3-sunburst
                        const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
                        const y = (d.y0 + d.y1) / 2;
                        return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
                        })           
*/
        .attr("transform", function(d) { 
            var deg;
            if (d.depth==0) {
                deg = 90;
            } else {
                deg = 180 / Math.PI * (d.x0 +d.x1) / 2;
            }
            var trans = `rotate(${deg-90})`;
            if (deg > 180) { 
                var hackRatio = (d.depth == 0) ? 160 : 130;
                var yavg = (d.y0 + d.y1) / 2 / hackRatio;
                trans += ` translate(${yavg},0) rotate(180)`; 
            }
            return trans})

        .attr("x", radius / 1.22 )
        .text(function(d) {return  `${d.data.name} (${d.data.freq})`;})

        .attr("text-anchor", function(d) { 
            var alignVec = ["center","end","start"];
            return alignVec[d.depth];})
 };

var yearHier = {"freq": 0, "name": "AllYears", "children": [{"freq": 0, "name": "2017", "children": [{"freq": 5, "name": "January", "children": []}, {"freq": 17, "name": "February", "children": []}, {"freq": 16, "name": "March", "children": []}, {"freq": 2, "name": "April", "children": []}, {"freq": 18, "name": "May", "children": []}, {"freq": 14, "name": "June", "children": []}, {"freq": 17, "name": "July", "children": []}, {"freq": 2, "name": "August", "children": []}, {"freq": 10, "name": "September", "children": []}, {"freq": 6, "name": "October", "children": []}, {"freq": 10, "name": "November", "children": []}, {"freq": 17, "name": "December", "children": []}]}, {"freq": 0, "name": "2018", "children": [{"freq": 14, "name": "January", "children": []}, {"freq": 6, "name": "February", "children": []}, {"freq": 13, "name": "March", "children": []}, {"freq": 15, "name": "April", "children": []}, {"freq": 15, "name": "May", "children": []}, {"freq": 4, "name": "June", "children": []}, {"freq": 7, "name": "July", "children": []}, {"freq": 12, "name": "August", "children": []}, {"freq": 17, "name": "September", "children": []}, {"freq": 8, "name": "October", "children": []}, {"freq": 10, "name": "November", "children": []}, {"freq": 12, "name": "December", "children": []}]}, {"freq": 0, "name": "2019", "children": [{"freq": 10, "name": "January", "children": []}, {"freq": 12, "name": "February", "children": []}, {"freq": 15, "name": "March", "children": []}, {"freq": 6, "name": "April", "children": []}, {"freq": 14, "name": "May", "children": []}, {"freq": 3, "name": "June", "children": []}, {"freq": 6, "name": "July", "children": []}, {"freq": 9, "name": "August", "children": []}, {"freq": 18, "name": "September", "children": []}, {"freq": 4, "name": "October", "children": []}, {"freq": 8, "name": "November", "children": []}, {"freq": 16, "name": "December", "children": []}]}]}

var yearHierFreq = {"freq": 355, "name": "AllMonths", "children": [{"freq": 83, "name": "2017", "children": [{"freq": 4, "name": "January", "children": []}, {"freq": 7, "name": "February", "children": []}, {"freq": 4, "name": "March", "children": []}, {"freq": 11, "name": "April", "children": []}, {"freq": 16, "name": "May", "children": []}, {"freq": 8, "name": "June", "children": []}, {"freq": 5, "name": "July", "children": []}, {"freq": 3, "name": "August", "children": []}, {"freq": 10, "name": "September", "children": []}, {"freq": 3, "name": "October", "children": []}, {"freq": 2, "name": "November", "children": []}, {"freq": 10, "name": "December", "children": []}]}, {"freq": 156, "name": "2018", "children": [{"freq": 14, "name": "January", "children": []}, {"freq": 8, "name": "February", "children": []}, {"freq": 12, "name": "March", "children": []}, {"freq": 10, "name": "April", "children": []}, {"freq": 16, "name": "May", "children": []}, {"freq": 17, "name": "June", "children": []}, {"freq": 19, "name": "July", "children": []}, {"freq": 14, "name": "August", "children": []}, {"freq": 4, "name": "September", "children": []}, {"freq": 17, "name": "October", "children": []}, {"freq": 19, "name": "November", "children": []}, {"freq": 6, "name": "December", "children": []}]}, {"freq": 116, "name": "2019", "children": [{"freq": 4, "name": "January", "children": []}, {"freq": 15, "name": "February", "children": []}, {"freq": 12, "name": "March", "children": []}, {"freq": 8, "name": "April", "children": []}, {"freq": 3, "name": "May", "children": []}, {"freq": 5, "name": "June", "children": []}, {"freq": 13, "name": "July", "children": []}, {"freq": 19, "name": "August", "children": []}, {"freq": 12, "name": "September", "children": []}, {"freq": 11, "name": "October", "children": []}, {"freq": 5, "name": "November", "children": []}, {"freq": 9, "name": "December", "children": []}]}]}

createSunburst(yearHier);
d3.select(self.frameElement).style("height", "700px");
</script>

1 个答案:

答案 0 :(得分:1)

您可以获得以下结果

enter image description here

使用此代码

var radiusSeparation = 5;

var texts = vis.selectAll("text")
  .data(nodes)
  .enter().append("text")
    .attr("transform", function(d) {
        if (d.depth == 0) return null;
        d.deg = 180 / Math.PI * (d.x0 + d.x1) * 0.5;
        var translate = d.depth == 1 ? Math.sqrt(d.y1)-radiusSeparation : Math.sqrt(d.y0)+radiusSeparation;
        var trans = `rotate(${(d.deg-90).toFixed(2)}) translate(${translate.toFixed(2)},0)`;
        if (d.deg > 180) {
            trans += ` rotate(180)`;
        }
        return trans;
    })
    .text( d => `${d.data.name} (${d.value})` )
    .attr("text-anchor", function(d) {
        if (d.depth == 0) return "middle";
        if (d.depth == 1) return d.deg < 180 ? "end" : "start";
        return d.deg < 180 ? "start" : "end";
    })
    .attr("dominant-baseline", "middle")
  • 使用圆弧的半径定位文本。使用较小的分隔距离,以使文本不会碰到弧形

  • deg值存储在基准中,以便将其用作文本锚点

  • 根据度值切换文本锚点

  • 治疗深度= 0在所有情况下都是特殊的

  • 使用dominant-baseline

  • 将文本垂直对齐到中间
  • d3.hierarchy.sum()将结果存储在d.value中,因此请在文本中使用它