web-dev-qa-db-ja.com

円弧の外側のラベル(円グラフ)d3.js

私はd3.jsが初めてで、それを使って円グラフを作成しようとしています。たった1つの問題があります。ラベルをアークの外側に取得できません...ラベルはarc.centroidで配置されます。

arcs.append("svg:text")
    .attr("transform", function(d) {
        return "translate(" + arc.centroid(d) + ")";
    })
    .attr("text-anchor", "middle")

誰が私を助けてくれますか?

44
Ibe Vanmeenen

私はその問題を解決できます-三角法で:)。

フィドルを参照してください: http://jsfiddle.net/nrabinowitz/GQDUS/

基本的に、arc.centroid(d)を呼び出すと、[x,y]配列が返されます。ピタゴラスの定理を使用して斜辺を計算できます。斜辺は、円の中心から円弧の重心までの線の長さです。次に、計算x/h * desiredLabelRadiusおよびy/h * desiredLabelRadiusを使用して、ラベルアンカーに必要なx,yを計算できます。

.attr("transform", function(d) {
    var c = arc.centroid(d),
        x = c[0],
        y = c[1],
        // pythagorean theorem for hypotenuse
        h = Math.sqrt(x*x + y*y);
    return "translate(" + (x/h * labelr) +  ',' +
       (y/h * labelr) +  ")"; 
})

ここでの唯一の欠点は、text-anchor: middleはもはや素晴らしい選択ではないということです-パイのどちら側にあるかに基づいてtext-anchorを設定する方が良いでしょう:

.attr("text-anchor", function(d) {
    // are we past the center?
    return (d.endAngle + d.startAngle)/2 > Math.PI ?
        "end" : "start";
})
62
nrabinowitz

特に円グラフの場合、d3.layout.pie()関数はstartAngleおよびendAngle属性を使用してデータをフォーマットします。半径は、希望するものであれば何でもかまいません(ラベルを配置したい中心からの距離)。

これらの情報をいくつかの 三角関数 と組み合わせると、ラベルのx座標とy座標を決定できます。

これを考慮してください Gist / block

テキストのx/y配置に関して、魔法はこの行にあります(読みやすいようにフォーマットされています):

_.attr("transform", function(d) {
  return "translate(" + 
    ( (radius - 12) * Math.sin( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
    ", " +
    ( -1 * (radius - 12) * Math.cos( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
  ")";
 })
_
  • _((d.endAngle - d.startAngle) / 2) + d.startAngle_は、ラジアン単位の角度(シータ)を提供します。
  • _(radius - 12)_は、テキストの位置に選択した任意の半径です。
  • _-1 *_ y軸が反転します(以下を参照)。

使用されるトリガー関数は、_cos = adjacent / hypotenuse_および_sin = opposite / hypotenuse_です。しかし、ラベルでこれらを機能させるために考慮しなければならないことがいくつかあります。

  1. 0の角度は12時です。
  2. 角度はまだ時計回りに増加します。
  3. Y軸は、標準のデカルト座標系から反転しています。正のyは6時方向-下方向です。
  4. 正のxは、まだ3時の方向にあります-右。

それはかなり物事を台無しにし、基本的にsincosを交換する効果があります。その後、trig関数は_sin = adjacent / hypotenuse_および_cos = opposite / hypotenuse_になります。

変数名をsin(radians) = x / rcos(radians) = y / rに置き換えます。いくつかの代数的操作の後、xとyの両方の関数をそれぞれr * sin(radians) = xr * cos(radians) = yで取得できます。そこから、それらを変換/変換属性にプラグインするだけです。

ラベルを適切な場所に配置して、見栄えを良くするには、次のようなスタイリングロジックが必要です。

_.style("text-anchor", function(d) {
    var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
    if ( (rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4) ) {
      return "middle";
    } else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
      return "start";
    } else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
      return "end";
    } else {
      return "middle";
    }
  })
_

これにより、ラベルは10時30分から1時30分まで、4時30分から7時30分までは中央(上と下)に固定され、1左側の:30時から4:30のアンカー(右側にあります)、および右側の7:30から10:30のアンカー(右側にあります)左)。

D3放射状グラフには同じ式を使用できますが、唯一の違いは角度の決定方法です。

これがつまずく人を助けることを願っています!

16
clayzermk1

ありがとう!

この問題を解決する別の方法を見つけましたが、あなたの方が良いようです:-)

大きな半径の2番目の円弧を作成し、それを使用してラベルを配置しました。

///// Arc Labels ///// 
// Calculate position 
var pos = d3.svg.arc().innerRadius(r + 20).outerRadius(r + 20); 

// Place Labels 
arcs.append("svg:text") 
       .attr("transform", function(d) { return "translate(" + 
    pos.centroid(d) + ")"; }) 
       .attr("dy", 5) 
       .attr("text-anchor", "middle") 
       .attr("fill", function(d, i) { return colorL(i); }) //Colorarray Labels
       .attr("display", function(d) { return d.value >= 2 ? null : "none"; })  
       .text(function(d, i) { return d.value.toFixed(0) + "%"});
15
Ibe Vanmeenen

これが役立つかどうかはわかりませんが、アークを作成して、テキストをアーク上とそのすぐ外側の両方に配置することができました。あるケースでは、アーク内にアークの大きさを配置し、アークの角度に合わせてテキストをアーク上で回転させます。もう1つは、テキストを円弧の外側に配置する場合、単純に水平です。コードは次の場所にあります。 http://bl.ocks.org/229526

私のベスト、

フランク

はい、それは [〜#〜] sohcahtoa [〜#〜]

function coordinates_on_circle(hyp, angle){
  var radian= angle * Math.PI / 180 //trig uses radians
  return {
    x: Math.cos(radian) * hyp, //adj = cos(r) * hyp
    y: Math.sin(radian) * hyp //opp = sin(r) * hyp
  }
}
var radius=100
var angle=45
coordinates_on_circle(radius, angle)
3
spencercooly

次のCoffeeScriptは、ラベルをパイスライスの内側にあるが外側のエッジに向かって取得するために機能しました。

attr 'transform', (d) ->
  radius = width / 2 # radius of whole pie chart
  d.innerRadius = radius * 0.5
  d.outerRadius = radius * 1.5
  'translate(' + arc.centroid(d) + ')'
1
Sarah Vessels

円グラフグラフの外側のラベルとしてパーセンテージを描画することで同じことを達成しました。ここにコードがあります http://bl.ocks.org/farazshuja/e2cb52828c080ba85da5458e2304a61f

g.append("text")
        .attr("transform", function(d) {
        var _d = arc.centroid(d);
        _d[0] *= 2.2;   //multiply by a constant factor
        _d[1] *= 2.2;   //multiply by a constant factor
        return "translate(" + _d + ")";
      })
      .attr("dy", ".50em")
      .style("text-anchor", "middle")
      .text(function(d) {
        if(d.data.percentage < 8) {
          return '';
        }
        return d.data.percentage + '%';
      });
1
FarazShuja

これは私が満足していた低コストの回答でした。すべてのラベルを水平方向に押し出します(そこに余分なスペースがあります)。

g.append("text")
  .attr("transform", function(d) { 
      var pos = arc.centroid(d); 
      return "translate(" + (pos[0] + (.5 - (pos[0] < 0)) * radius) + "," + (pos[1]*2) + ")"; 
  })
  .attr("dy", ".35em")
  .style("text-anchor", function(d) { 
      return arc.centroid(d)[0] > 0 ? "start" : "end";
   })
  .text(function(d) { return d.label; });
1
cheepychappy