Wijmo(FlexChart)の標準機能として「ラベル同士の重なりを自動的に検知して非表示にする」ための直接的なプロパティはありません。
しかし、dataLabel.position
を Auto
に設定することで、ある程度自動的にラベル位置を調整することが可能です。さらに、rendered
イベントなどを用いてラベルの座標を取得・判定することで、重なりを検知して個別に非表示にするカスタマイズが実現できます。
以下、要件を満たすためのステップを順に解説します。
1. ラベル位置をデータポイントの位置に応じて変える方法
1.1 dataLabel.position
プロパティでの自動配置
Wijmoの FlexChart には、ラベルの表示位置を指定するための列挙型 Position
が用意されています。主な値は以下のとおりです。
None
Left
Top
Right
Bottom
Center
-
Auto
← これを指定すると、チャート自身がラベルの位置をある程度調整してくれる
まずは、散布図の Series
に対して dataLabel
を有効にし、position
を Auto
に設定する例です:
// 例)FlexChartを生成して series を設定する場合
var chart = new wijmo.chart.FlexChart('#theChart', {
itemsSource: myData,
chartType: wijmo.chart.ChartType.Scatter,
bindingX: 'x',
series: [
{
name: 'SampleSeries',
binding: 'y',
dataLabel: {
content: '{y}', // ラベルに表示するテキスト
position: wijmo.chart.Position.Auto, // 自動調整
}
}
]
});
Auto
を指定すると、ある程度ラベルの重なりが軽減される場合があります。しかし、非常に近いデータポイントが多いと、完全には重なりを避けられないケースも出てきます。
1.2 ラベルの表示内容を動的に切り替える
散布図の場合、たとえば X 座標が一定以上ならラベルを右側、それ以下なら左側に寄せたいというような使い分けをしたいときがあります。その場合は、dataLabel.content
にコールバック関数を指定して、各データポイントの値によってラベルの表示位置を切り替えます。
ただし「ラベル位置(Top/Left 等)」自体は dataLabel.position
単位でしか設定できず、データポイントごとに切り替える標準APIはありません。代わりに
-
dataLabel.position
をAuto
やTop
等に設定しておく - もしくは後述の「
rendering
イベントで自前カスタマイズ」を行う
のいずれかになります。
もし「特定の条件下ではラベルそのものを表示しない」だけなら、content
関数内で null
を返せば対応できます。
dataLabel: {
content: function (ht) {
// ht はヒットテスト情報
if (ht.item.x > 50) {
return ht.item.y; // 例えばデータが大きい時だけ y 値を表示
} else {
return null; // それ以外は表示しない
}
},
position: wijmo.chart.Position.Auto,
}
2. 近くにあるデータポイントのラベル同士が重なる場合に非表示にする方法
2.1 結論
標準API (プロパティ) だけで「自動的に重なりを検知して非表示にする」仕組みは提供されていません。
ラベルが非常に多い、あるいはデータポイントが密集しているケースでは、rendered
イベント もしくは rendering
イベント で SVG 要素を直接操作し、重なりを検知して個別にラベルを隠す(または配置を修正する)という実装が必要です。
2.2 rendered
イベントでのカスタム実装例
rendered
イベントは、チャート描画完了後に呼ばれるイベントです。ここで、描画済みのラベル (SVG の <text>
要素) を取得し、各ラベルのバウンディングボックスが重なるかを判定して重なるものを非表示にできます。
chart.rendered.addHandler(function(sender, args) {
// 全てのデータラベル(text要素)を取得
var labels = sender.hostElement.querySelectorAll('text.wj-data-label');
// ラベルのバウンディングボックスを格納する配列
var labelRects = [];
labels.forEach(function(lbl) {
// 現在の <text> 要素の位置とサイズを取得 (getBBoxはSVG専用)
var rect = lbl.getBBox();
// 2つのラベルが重なっているかを判定する関数
var isOverlap = labelRects.some(function(r) {
return !(
r.x + r.width < rect.x ||
rect.x + rect.width < r.x ||
r.y + r.height < rect.y ||
rect.y + rect.height < r.y
);
});
// 重なっていれば非表示にする
if (isOverlap) {
lbl.setAttribute('visibility', 'hidden');
} else {
// 重なっていなければ、後続の判定用に追加
labelRects.push(rect);
}
});
});
上記はシンプルなサンプルですが、次のような注意が必要です。
-
重なり判定を厳密にする
たとえばラベルの余白を考慮したり、多少の近接は許容したりする場合は工夫が必要になります。 -
配置自体を動かす場合
今回は「重なったらラベルを隠す」だけですが、位置をずらす場合は<text>
要素のx
,y
属性をいじる必要があり、さらに相互に影響し合うため実装が複雑になります。 -
rendering
イベントとの使い分け
rendering
イベントだと、まだ描画処理の途中段階になるため、engine
オブジェクトを使ってラベルを出力するタイミングで制御できます。ただし自力で描画する処理を書くことになりがちなので、より高度な実装になります。
シンプルにやるなら、すでに生成された SVG 要素に対して最終調整を行うrendered
イベントのほうがわかりやすいことが多いです。
まとめ
-
ラベルの位置調整
-
dataLabel.position = Position.Auto
を利用すれば、ある程度チャート側で自動的にラベル位置を振り分けてくれます。 - それ以上にデータごとにきめ細かく位置を切り替えたい場合は、
rendered
イベントでラベルを直接操作するなどのカスタマイズが必要になります。
-
-
重なり防止
- 標準プロパティ(例:
chart.dataLabel.overlapping
)のようなものは用意されていないため、自力で重なりを判定して非表示にする仕組みを組むのが現状の方法です。 - サンプルのように
rendered
イベントで SVG<text>
要素の位置関係を調べ、重なったラベルを非表示にする実装例が典型です。
- 標準プロパティ(例:
-
複雑さを抑えるポイント
- まずは
position: 'Auto'
と必要に応じてcontent
コールバックを使い、ある程度の位置調整や不要ラベルの非表示を行う。 - ラベルの重なりが顕著になった場合のみ、
rendered
イベントでさらに制御するアプローチを検討する。
- まずは
こうした手順で、できるだけ Wijmo の標準機能を活かしつつ要件に近い挙動を実現できます。