2016-06-17 13 views
8

私は正と負の値の異なるリングが含まれていますD3でドーナツグラフの特別な種類を作成しようとしています。値は100%より大きくても-100%よりも小さくすることができるので、残りの値を表す弧があります。以下は、チャートのサンプル画像です。enter image description here 最初の肯定的なカテゴリ(Category_1 - 灰色)の値は80なので、次の肯定的なカテゴリのために20%を残して、灰色で80%の円を塗りつぶします。次のポジティブカテゴリ値(Category_2 - オレンジ)は160です。したがって、最初にCategory_1(残りの140の値)に20%残っています。次に、次の円(上向き)100%(今は40の値)を満たしており、残りの値(40)については、上向きの部分円を作成しています。特別ドーナツグラフ

ここで、Category_3(暗赤色)は負(-120%)なので、内接円を作成して100%(今は20値)を埋めると、残りの値(20)。私たちは別の否定的なカテゴリ(Category_4 - 赤)を持っているので、前の否定的なカテゴリ(Category_3)が終了した場所から始まり、そこから20%のエリアを埋めるでしょう。

編集3:私は非常に基本的なアークベースのドーナツグラフを作成したとの合計値が100を超えているとき、私は残りの値のための外輪を作成することができています。以下はJSFiddleリンクです:

http://jsfiddle.net/rishabh1990/zmuqze80/

data = [20, 240]; 

var startAngle = 0; 
var previousData = 0; 
var exceedingData; 
var cumulativeData = 0; 
var remainder = 100; 
var innerRadius = 60; 
var outerRadius = 40; 
var filledFlag; 

var arc = d3.svg.arc() 
    .innerRadius(innerRadius) 
    .outerRadius(outerRadius) 

for (var i = 0; i < data.length; i++) { 

    filledFlag = 0; 
    exceedingData = 0; 

    console.log("---------- Iteration: " + (i + 1) + "---------"); 

    if (data[i] > remainder) { 
    filledFlag = 1; 
    exceedingData = data[i] - remainder; 
    console.log("Exceeding: " + exceedingData); 
    data[i] = data[i] - exceedingData; 
    data.splice(i + 1, 0, exceedingData); 
    } 

    if(filledFlag === 1) { 
    cumulativeData = 0; 
    } else { 
    cumulativeData += data[i]; 
    } 

    console.log("Previous: " + previousData); 
    console.log("Data: " + data, "Current Data: " + data[i]); 
    var endAngle = (previousData + (data[i]/50)) * Math.PI; 
    console.log("Start " + startAngle, "End " + endAngle); 
    previousData = previousData + data[i]/50; 

    //if(i===1) endAngle = 1.4 * Math.PI; 
    //if(i===2) endAngle = 2 * Math.PI; 

    var vis = d3.select("#svg_donut"); 

    arc.startAngle(startAngle).endAngle(endAngle); 


    vis.append("path") 
    .attr("d", arc) 
    .attr("transform", "translate(200,200)") 
    .style("fill", function(d) { 
     if (i === 0) return "red"; 
     //if (i === 1) return "green"; 
     //if (i === 2) return "blue" 
     //if (i === 3) return "orange" 
     //if (i === 4) return "yellow"; 
    }); 

    if (exceedingData > 0) { 
    console.log("Increasing Radius From " + outerRadius + " To " + (outerRadius + 40)); 
    outerRadius = outerRadius + 22; 
    innerRadius = innerRadius + 22; 
    arc.innerRadius(innerRadius).outerRadius(outerRadius); 
    console.log("Outer: ", outerRadius); 
    } 

    if (remainder === 100) { 
    remainder = 100 - data[i]; 
    } else { 
    remainder = 100 - cumulativeData; 
    }; 

    if (filledFlag === 1) { 
    remainder = 100; 
    } 

    console.log("Remainder: " + remainder); 

    startAngle = endAngle; 

} 

実装のためのいくつかのアイデアを共有してください。

+0

2つの正の値が> 100%の場合はどうすればよいですか?同様に、カテゴリー1が180%だったら?カテゴリ2と一緒にどのように表示されますか? – meetamit

+0

は、それは次のようになります@meetamit: http://jsfiddle.net/dw0sx79v/3/ – Rishabh

+0

はそれを手に入れました。これは興味深い問題ですが、完全に動作する例を得るにはしばらく時間がかかります。私は助けることができると思うが、いつ私がそれに着くかわからない。たぶん明日または翌日。その間、この質問に対して賞金を支払うことを検討してください。あなたはより多くの目玉を得るでしょう – meetamit

答えて

3

[OK]を、これには少し時間がかかりましたが、それが動作しているようです。まず、ドーナツチャートとして記述したものが、まったく同じデータを使用して一連のバーとしてレンダリングされることを確認しましょう。だから私はそこから始めて、最終的にはドーナツチャートに取り入れましたが、バーの実装もそこに残しました。もう一つは、包括的なソリューションは、100だけでなく、どのような値でもセグメントを折り返すことができるはずだから、ラッピング値を変えることができるスライダが含まれています。最後に、ドーナツの実装ではなく、バーで説明する方が簡単です。バーをテキストのように左から右に折り返すのではなく、ジグザグにすることが望ましいかもしれません。つまり、左から右に交互に折り返し、左から右へと移動します。これがもたらす効果は、金額が2つの別々の線で2つのセグメントに分割されたとき、ジグザグのアプローチは、それらの2つのセグメントを互いに隣り合わせに保つことです。このジグザグの動作をオン/オフするチェックボックスを追加しました。

は、ここでのworking jsFiddleanother iterationです。ここで

は重要なビットです:

これらの値をラップするdata値とのwrapLengthの配列をとる関数wrap(data, wrapLength)があります。その関数は、値がサブセグメントに分割されなければならないデータのうちの図面及び各セグメントの目的は、x1x2y値を有する、それらの新しい配列を返します。 x1x2は、各バーの開始と終了であり、yバーの行です。ドーナツグラフでこれらの値は、等価的に角度(x1)、各円弧の終了角度(x2)と半径(y)を開始しています。

関数wrap()は、負の値と正の値を考慮する方法を知らないため、wrap()を2回呼び出す必要があります。1回はすべてのネガティブで行い、次にすべてのポジティブで行います。そこからいくつかの処理がネガのみに選択的に適用され、さらに2つのセットの組み合わせにさらに処理が適用されます。最後の2つの段落で説明されている変換のセット全体は、次のスニペットでキャプチャされます。私はwrap()の実装をここに含めません。それを呼び出すコードだけです。またレンダリングコードは含まれていません。segmentsが生成されるとかなり簡単です。

// Turn N data points into N + x segments, as dictated by wrapLength. Do this separately 
// for positive and negative values. They'll be merged further down, after we apply 
// a specific transformation to just the negatives 
var positiveSegments = wrap(data.filter(function(d) { return d.value > 0; }), wrapLength); 
var negativeSegments = wrap(data.filter(function(d) { return d.value < 0; }), wrapLength); 

// Flip and offset-by-one the y-value of every negative segment. I.e. 0 becomes -1, 1 becomes -2 
negativeSegments.forEach(function(segment) { segment.y = -(segment.y + 1); }); 

// Flip the order of the negative segments, so that their sorted from negative-most y-value and up 
negativeSegments.reverse() 

// Combine negative and positive segments 
segments = negativeSegments.concat(positiveSegments); 

if(zigzag) { 
    segments.forEach(function(segment) { 
    if(Math.abs(segment.y) % 2 == (segment.y < 0 ? 0 : 1)) { flipSegment(segment, wrapLength); } 
    }); 
} 

// Offset the y of every segment (negative or positive) so that the minimum y is 0 
// and goes up from there 
var maxNegativeY = negativeSegments[0].y * -1; 
segments.forEach(function(segment) { segment.y += maxNegativeY; });