0

Hello I do an sunburst or bilevel chart it's middle of a pie & donut chart ^^ When I append all path it works fine:

this.path = this.svg.selectAll("path")
.data(this.partition.nodes(rootData).slice(1))
.enter().append("path")
.attr("d", this.arc)
.style("fill", function(d) { return d.fill; })
.each(function(d){ this._current = thiss.updateArc(d);});

But the probleme is when I'm trying to add a circle in middle-extern of all my path so it didn't work, this code add circle in the middle-middle of all my path fine

var indicator =  this.svg.selectAll('circle')
.data(this.partition.nodes(rootData))
.enter().append("circle")
.attr("cx", function(d){return thiss.arc.centroid(d)[0]})
.attr("cx", function(d){return thiss.arc.centroid(d)[1]})
.attr("r", 5).style('fill','#ff0000');

But I need to add this little circle in the midle but on extern border of the path. I don't know how I can get the right cx and cy attributs, help please ?

This is screenshot of my goal (black points are what I had) and (red points are what I want to do)

https://i.stack.imgur.com/GXPYM.jpg

enter image description here

VividD
  • 10,456
  • 6
  • 64
  • 111
istiti
  • 75
  • 10
  • You need to compute the center of the arcs, given their start and end angles and inner and outer radii. – Lars Kotthoff Feb 14 '14 at 10:14
  • ok and how Can I get all these things you said ? – istiti Feb 14 '14 at 13:34
  • The short answer is trigonometry, the longer answer is that the x coordinate would be `Math.cos(d.startAngle + (d.endAngle-d.startAngle)/2) * (innerRadius + (outerRadius - innerRadius)/2)` and similarly with `Math.sin` for the y coordinate. – Lars Kotthoff Feb 14 '14 at 13:38
  • Wow to complicated for me I have : `this.arc = d3.svg.arc() .startAngle(function(d) { return d.x; }) .endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); }) .innerRadius(function(d) { return thiss.radius / 3 * d.depth; }) .outerRadius(function(d) { return thiss.radius / 3 * (d.depth + 1) - 1; });` What must I add instead of: `.attr("cx", function(d){return thiss.arc.centroid(d)[0]}) .attr("cy", function(d){return thiss.arc.centroid(d)[1]})` I'm not able to understand this part of d3 ^^ I just begin since 1 week – istiti Feb 14 '14 at 13:41
  • That's not D3, that's just general trig and Javascript. – Lars Kotthoff Feb 14 '14 at 13:42
  • Ok damage i'm not able to understand you because when I put: `function(d){console.log(d.startAngle;})` I have anything – istiti Feb 14 '14 at 13:47
  • Well this needs to be run in the context of adding new elements, e.g. `svg.selectAll("circle").data(data).enter().append("circle").attr("cx", ...)`. – Lars Kotthoff Feb 14 '14 at 13:50
  • Yes I do that, if you see my code : you will se I do exactly what you write and I have my circle in each path in the middle-middle :) but the problem is that I don't know I can put it in the middle on extern border of each path :( and I have nothing when I debgu with d.startAngle – istiti Feb 14 '14 at 13:56
  • Well then it's `Math.cos(d.startAngle + (d.endAngle-d.startAngle)/2) * outerRadius` for x. – Lars Kotthoff Feb 14 '14 at 13:59
  • like this for cx? : `.attr("cx", function(d){ return Math.cos(d.startAngle + (d.endAngle-d.startAngle)/2) * outerRadius})` ? it doesn't work – istiti Feb 14 '14 at 14:11
  • At this point, it would help if you could provide a complete example. – Lars Kotthoff Feb 14 '14 at 14:17
  • Ok thank you so I upload my test on server you can check it here : http://www.rigel.ch/sunburst/composition.html and you must hover each path and you will se a circle will be added... but your Math.cos and sinus code put my circle always in the middle of svg... I write my code in this file http://www.rigel.ch/sunburst/portfeuille/js/createChart.js at line:291 (sorry for missing file) – istiti Feb 14 '14 at 15:55
  • Your example is rather complex, so I did not take the time to figure out where in your code you are having the problem. However, from your console it is clear that your `d.x` and `d.y` values are `NaN`, and so of course your `cx` and `cy` values for your circles are invalid. – AmeliaBR Feb 15 '14 at 00:52
  • Correction: it is your calculated `x` and `y` values that are NaN, because you are referring to `d.startAngle` in the calculation, which doesn't exist in the partition layout (@LarsKotthoff' suggestions assumed you were using a pie chart layout). You have to replace *all* references to `d.startAngle` and `d.endAngle` with the values calculated from `d.x` and `d.dx`; I am also not sure why you are using the depth value when calculating the end angle. – AmeliaBR Feb 15 '14 at 01:06

2 Answers2

0

As an alternative to trigonometry, you could use transformations to position the circles. If the first step in a transformation is a rotation, and then you apply a translation afterwards, the translation will be applied in the rotated coordinate system.

A little extra complication, though, is that the d3 pie chart gives angles in radians (since that's what the trigonometry functions use), but the rotation needs angles in degrees.

  var degreesPerRadian = 180/Math.PI;
  g.append("circle") //circles inherit pie chart data from the <g>
      .attr("r", 5)
      .attr("transform", function(d) { 
          return "rotate(" +  degreesPerRadian*((d.startAngle + d.endAngle)/2) 
          + ")" +
              //rotate by the average of the start and end angle
              //Note that d3 specifies angles in radians, but the rotate
              //function needs them in degrees
              "translate(0," + -radius + ")";
              //then translate "up" the distance of the radius;
              //"up" is interpretted according to the rotated coordinates,
              //but for zero rotation it will position the dot at the top
              //of the circle, which is the zero angle for d3
      });

Live example: http://fiddle.jshell.net/4x9ap/

(based on this simple pie chart code )

AmeliaBR
  • 27,344
  • 6
  • 86
  • 119
  • Of course, for your more complex sunburst layout, you would replace `((d.startAngle + d.endAngle)/2)` with `(d.x + d.dx/2)`, and you would replace `radius` with the outer radius calculated from the depth. But be careful that you still make the radius negative, so that the translation is in the correct direction! – AmeliaBR Feb 15 '14 at 01:08
  • I should clarify that, depending on how you draw your sunburst layout, the appropriate translation may be positive or negative, and may be horizontal or vertical. However, for the example code from the original poster, zero angle (`d.x`) values are drawn the same as in the pie chart -- as a vertical line at the top of the circle. – AmeliaBR Feb 15 '14 at 01:33
  • with both method : cx and cy or transform my circles aren't in the middle http://i.imgur.com/Qd6PPWQ.jpg – istiti Feb 19 '14 at 14:36
  • Oops, looks like your arc drawing method doesn't exactly match the default pie layout after all. Your dots are getting drawn on the opposite side from the arcs, so the easy fix is to reverse the sign of the x and y coordinates: make `cx` negative sin of the angle and `cy` positive cosine. – AmeliaBR Feb 19 '14 at 15:37
  • Ok when I do your fix, I have same problem just in other side: http://i.imgur.com/ClZgJxS.png – istiti Feb 19 '14 at 15:48
  • Sorry, wasn't looking closely at your code: `dx` is NOT equivalent to `endAngle`; it is the *difference* between the start and end angles. so either use `endAngle = d.x + d.dx`, or use the formula I give above for figuring out the midpoint of the angle as `(d.x + d.dx/2)`; in other words, the start angle (`d.x`) plus half of the total arc width (`d.dx`). – AmeliaBR Feb 19 '14 at 17:51
0

This is in part a repeat of Lars' equations from the comments, but I thought it was worth recapping all at once because the trig identities for converting from angles to x/y coordinates won't match your trig text book.

Most text books assume that angles start at the right horizontal axis and increase counter-clockwise, and that the vertical axis has larger values higher up on the page.

In SVG, larger y values are lower on the page, and the angles created by the pie chart layout (and the example code the OP is using for the sunburst layout) draw an angle of zero as vertical line to the top of the circle, with angles increasing clockwise.

With that information, you can convert to x and y values with the following trig equations:

  g.append("circle") //circles inherit pie chart data from the <g>
      .attr("r", 5) 
      .attr("cx", function(d) { 
          return Math.sin((d.startAngle + d.endAngle)/2) *radius;
      })
      .attr("cy", function(d) { 
          return -Math.cos((d.startAngle + d.endAngle)/2) *radius;
      });

Live example: http://fiddle.jshell.net/4x9ap/1/

Again, this simple example uses a pie chart layout, so the data has startAngle and endAngle values, and the radius is constant. For a sunburst diagram made with the partition layout, you would replace (d.startAngle + d.endAngle)/2 with d.x + d.dx/2, and you would replace radius with a function based on d.depth.

AmeliaBR
  • 27,344
  • 6
  • 86
  • 119
  • Hello, sorry for the delay I was afk and thank you for these comments, so I understood 50% part of these comments and I did this code with this result on mousehover: http://i.imgur.com/Qd6PPWQ.jpg The distance are OK but my circle aren't in the middle of each path, where is the problem ? and when I use transform solution with degreesPerRadian I had the samethings :) – istiti Feb 19 '14 at 10:23