Sebastian Bochanが彼の回答で述べているように、データから直接オーバー/アンダー シェーディング用の追加領域を作成するだけの方がはるかに簡単です。シェーディング領域を構築するために行っている作業を実際に行う必要はないと思います. ただし、それではデータのグループ化の問題は解決しません。
近づくことはできますが、完全ではありません。最初は、問題はすべてのデータセットで同じ集計関数を使用しているだけだと思っていましたが、交差点で集計するたびにうまくいかないため、うまくいきません。
私がしたことは、A から B に移動する範囲データの 2 つのセットを作成することでした。
for (i = 0; i < data1.length; i ++) {
var valA = data1[i][1];
var valB = data2[i][2];
under[i] = [data1[i][0], valA, valB ];
over[i] = [data1[i][0], valB, valA ];
}
次に、範囲データの各セットに、グラフの形状を変更して、必要に応じて上/下のみを表示する集計関数のみを与えました。
function shadeApproxUnder(low,high) {
low = average_(low);
high = average_(high);
if (low > high) return [ high, high ];
return [ low, high ];
}
function shadeApproxOver(low,high) {
low = average_(low);
high = average_(high);
if (low > high) return [ low, low ];
return [ low, high ];
}
また、集計値が同じであることを確認できるように、行用に 1 つ:
function lineApprox(values) {
var avg = average_(values);
return avg;
}
このfiddleでわかるように、これに近づきますが、完全には機能しないことがわかります。これは、データポイントがない場所で 2 つの線が交差するポイントがあるためです。システムは 2 つのデータ ポイント間で物事をまとめて描画していると想定しているため、それを機能させる方法はありません。
[ null, null ]
集計関数の代わりに、[ high, high ]
または集計関数内で返してみる[ low, low ]
と、ギャップがより明確にわかります。
どうにかしてクロスオーバー ジャンクションにデータポイントを追加する必要があるようですが、その方法がわかりません。データのグループ化によってデータが変更されるため、データを編集することはできませんが、データポイントを追加できないため、集計関数では編集できません。
線を描画するときに生成されたすべての集計データ ポイント値を保存し、それを使用して、推定された余分なデータ ポイントを含む面グラフを挿入することもできますが、グラフ (そうならないと思います)。
編集
私はより良い解決策を試してみましたが、それでもうまくいきません。おそらく、あなたか他の誰かがその欠陥を見つけることができるでしょう。
基本的に、上で述べたように、シェーディング カーブの線の交点にデータポイントを追加したいのですが、これは私の試みです。グループ化された後のデータである「dataGrouping」に格納されているチャートのデータを使用して行われます。
function updateChart(chart, refresh) {
var dir = 0;
var over = Array(), under = Array();
var dataA = chart.series[2].groupedData;
var dataB = chart.series[3].groupedData;
j = 0; // Index into over/under
for (i=0;i < dataA.length; i++) {
if (dataA[i].y < dataB[i].y) {
if (dir == -1) {
over[j] = under[j] = getIntersection(dataA[i-1], dataA[i],
dataB[i-i], dataB[i]);
j++;
}
dir = 1;
over[j] = [ dataA[i].x, dataA[i].y, dataB[i].y ];
under[j] = [ dataA[i].x, dataB[i].y, dataB[i].y ];
} else if (dataA[i].y == dataB[i].y) {
dir = 0;
over[j] = [ dataA[i].x, dataA[i].y, dataA[i].y ];
under[j] = [ dataA[i].x, dataA[i].y, dataA[i].y ];
} else {
if (dir == 1) {
over[j] = under[j] = getIntersection(dataA[i-1], dataA[i],
dataB[i-i], dataB[i]);
j++;
}
dir = -1;
over[j] = [ dataA[i].x, dataB[i].y, dataB[i].y ];
under[j] = [ dataA[i].x, dataB[i].y, dataA[i].y ];
}
j++;
}
chart.series[0].setData(over, refresh);
chart.series[1].setData(under, refresh);
}
フィドル。
注: 時々、これがループに陥っているように見えますが、その理由はわかりません。
フィドルで線の交差関数を見ることができます。これはウィキペディアから持ち上げられたものです。これには 2 つの問題があります。まず、この関数をアタッチする適切なイベントがありませload
んredraw
。ただし、イベント内から再描画を開始することはできませんredraw
(これは理にかなっています)。そのため、表示されるシェーディングは常にプロット ラインの背後にある 1 つのデータ セットになります。これは、たとえばズーム設定を切り替えると明らかです。ただし、Highchart のソース コードを編集する準備ができていて、グループ化の後、最終的なレンダリングの前に update 関数をアタッチできるようにするフックを配置すれば、かなり簡単に修正できるはずです。
シェーディングがまだ正しくないのはなぜですか? 線の交差ロジックが間違っているか、非常に小さい (正確な) 数値と非常に大きい数値を扱う場合の精度に関係しています。または、チャートの制限が高いだけです。
もう 1 つのオプションは、おそらく正しくないように見えますが、redraw
とRenderer
クラスを使用して、影付きの領域を SVG パスとしてグラフに直接描画することです。
他に何か思いついたら、もう一度編集しますが、これを行う方法についてのアイデアが不足していると思います。
編集2
OK、これを行う「正しい」方法は、おそらくハイチャート自体を変更し、独自のレンダラーを使用して新しいチャート タイプを作成することだと思います (したがって、クロスオーバーの色を変更して描画することを知っている拡張エリア チャートになります)異なる色の線)。それがどれほど簡単かわかりません。
ただし、SVG パスをグラフ上に直接描画するハックは実際には問題なく機能し、このページの他の例のすべてのロジックが既にあるため、チャートの更新関数は次のようになります。
var over_path = null;
var under_path = null;
function updateChart(chart) {
// Create the paths on the first call ...
if (!over_path) {
over_path = chart.renderer.path();
over_path.attr({fill: '#bbeebb'}).add();
}
if (!under_path) {
under_path = chart.renderer.path();
under_path.attr({fill: '#eebbbb'}).add();
}
// Get the data from the chart
aPts = chart.series[0].points;
bPts = chart.series[1].points;
var i;
var overPts = Array(); // SVG path instructions
var underPts = Array();
var mX = chart.plotLeft; // Offset for each point
var mY = chart.plotTop;
var iPt; // For the intersection points
// Draw the path along line B, than back along A/B
// to complete a closed space. First go along B
overPts[0] = underPts[0] = 'M'; // Move to start
overPts[1] = underPts[1] = bPts[0].plotX + mX;
overPts[2] = underPts[2] = bPts[0].plotY + mY;
overPts[3] = underPts[3] = 'L'; // Draw from here
for (i=1; i<aPts.length; i++) {
overPts[overPts.length] = underPts[underPts.length] = bPts[i].plotX + mX;
overPts[overPts.length] = underPts[underPts.length] = bPts[i].plotY + mY;
}
// Now go backwards along A looking for the cross-overs ...
var dir = 0;
var newDir = 0;
for (i=(aPts.length - 1); i>=0; i--) {
var A = aPts[i].plotY + mY;
var B = bPts[i].plotY + mY;
newDir = (A>B)?1:((A<B)?-1:0);
if (dir && (dir != newDir)) {
// Change direction, add intersect point
iPt = getIntersection(aPts[i], aPts[i+1], bPts[i], bPts[i+1]);
overPts[overPts.length] = underPts[underPts.length] =iPt.plotX + mX;
overPts[overPts.length] = underPts[underPts.length] = iPt.plotY + mY
}
dir = newDir;
// Add matching data point
overPts[overPts.length] = aPts[i].plotX + mX;
underPts[underPts.length] = aPts[i].plotX + mX;
if (A > B) {
overPts[overPts.length] = B;
underPts[underPts.length] = A;
} else {
overPts[overPts.length] = A;
underPts[underPts.length] = B;
}
}
// Update new path
over_path.attr({'d': overPts});
under_path.attr({'d': underPts});
}
フィドル
注:ここでは、チャート オブジェクトの文書化されていないコンテンツを読んでポイントがどこにあるかを調べています。これは公式の API には含まれていないため、Highcharts の将来のバージョンでは常に変更される可能性があります (ただし、おそらく変更されることはありませんが、可能性があることを知っておくとよい)。
主な問題は、最初のグラフ描画アニメーションの前に領域が表示されることですが、おそらくそれを回避する方法を見つけることができます (最初にフェードアウトすると、クールに見えるかもしれません)。それ以上の再描画はアニメーション化されないので問題ありません。一部のビューには、アンチエイリアシングが原因であると思われる「あいまいさ」もあります。
線の交点を考慮することができるデータのグループ化を行う別のチャート パッケージがない限り、チャート パッケージのソースを掘り下げない限り、上記の方法がおそらく最良のソリューションだと思います。
とにかく、今週は興味深い問題に頭を悩ませてくれてありがとう。:)