Text along circles in a D3 circle pack layout

喜夏-厌秋 提交于 2019-12-10 17:03:30

问题


I am trying to label some circles in a circle pack layout with text that flows along the circle itself.

Here is one experimental jsfiddle:

As you can see, it is possible to render text along the circle, centered at its top. Though browser's rendering of curved SVG text is terrible. But let's say we don't care about it.

Here is another jsfiddle

I would like to place curved labels on this graph, under these conditions:

  • The circle represents a province (only depth==1) (BRITISH COLUMBIA, ALBERTA, and so forth)
  • The sum of sizes of all children (in other words, number of parliament seats allotted) of the province is greater than 5.
  • The name of the province should be all UPPERCASE.

You can see some of my attempts in the code itself. I have been trying for hours. My main problem is that circles in the circle are now somewhere in X Y space, whereas, in the first jsfiddle, all circles have centers in coordinate system origin.

Maybe you can help me by taking a fresh look at this.

Underlying data is based on this table:

(NOTE: This is somewhat related to the question 'Circle packs as nodes of a D3 force layout' I asked the other day, however this is an independent experiment.)

I decided to use regular SVG arcs instead of d3.svg.arc(). I still think it is a right decision. However, here is what I have now: :) jsfiddle


回答1:


Just updating some of VividD's code.

The describeArc function was not working correctly for arcs with less than 180 degrees, and the start and end variables was flipped on the function and when calling it.

Here is an updated describeArc function that handles all alternatives of arcs, including small arcs and upside-down arcs:

function describeArc(x, y, radius, startAngle, endAngle) {
  var start = polarToCartesian(x, y, radius, startAngle);
  var end = polarToCartesian(x, y, radius, endAngle);
  var arcLength = endAngle - startAngle;
  if (arcLength < 0) arcLength += 360;
  var longArc = arcLength >= 180 ? 1 : 0;
  var d = [
    "M", start.x, start.y,
    "A", radius, radius, 0, longArc, 1, end.x, end.y
  ].join(" ");
  return d;
}

With this change the function should be called with reversed start and end values that make more sense:

describeArc(d.x, d.y, d.r, -160, 160);



回答2:


NOTE (since I am answering my question): If some of you already spent time on this problem and found another solution, please post it, and I will accept your answer. Thanks to @FernOfTheAndes for participating in process of finding this solution, as it was filled with pain and misery of working with svg arcs.

Here is jsfiddle of the solution:

As mentioned in comments, the key part was generating arcs as plain vanilla svg arcs, not via d3.svg.arc().

SVG rules for defining arcs are clear, but a little hard to manage. Here is an interactive explorer of svg syntax for arcs.

Also, these two functions helped me during this process of defining the right arcs:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
}

function describeArc(x, y, radius, startAngle, endAngle){
    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);
    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, 1, 1, end.x, end.y
    ].join(" ");
    return d;       
}

This is the code that actually directly generates curved labels:

var arcPaths = vis.append("g")
    .style("fill","navy");
var labels = arcPaths.append("text")
    .style("opacity", function(d) {
        if (d.depth == 0) {
            return 0.0;
        }
        if (!d.children) {
            return 0.0;
        }
        var sumOfChildrenSizes = 0;
        d.children.forEach(function(child){sumOfChildrenSizes += child.size;});
        //alert(sumOfChildrenSizes);
        if (sumOfChildrenSizes <= 5) {
            return 0.0;
        }
        return 0.8;
    })
    .attr("font-size",10)
    .style("text-anchor","middle")
    .append("textPath")
    .attr("xlink:href",function(d,i){return "#s"+i;})
    .attr("startOffset",function(d,i){return "50%";})
    .text(function(d){return d.name.toUpperCase();})

Fortunately, centering text on an arc was just a matter of setting the right property.



来源:https://stackoverflow.com/questions/21255967/text-along-circles-in-a-d3-circle-pack-layout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!