【发布时间】:2015-08-08 08:12:25
【问题描述】:
-
我正在尝试使用 LineChart 来显示来自不同渠道(即时间序列)的医疗数据,但这些序列应根据所附图像显示在不同的 yAxis 上
- 我是否需要开发自己的图表组件或 LineChart 可以用于这种情况?
http://simetronsac.com/images/dx/eeg-24/eeg6.gif
====================更新... 扩展 LineChart 并添加 ExtraData 以适应 yValue,同时将 yAxis 更改为 CategoryAxis:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javafx.beans.NamedArg;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
public class StackedLineChart<X,Y> extends LineChart<X,Y> {
private double swimlaneHeight = 50;
private double currentYoffset = 0;
public static class ExtraData {
public float channelPower;
public ExtraData(float channelPower) {
super();
this.channelPower = channelPower;
}
public float getChannelPower() {
return channelPower;
}
public void setChannelPower(float channelPower) {
this.channelPower = channelPower;
}
}
public StackedLineChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) {
this(xAxis, yAxis, FXCollections.<Series<X, Y>>observableArrayList());
}
public StackedLineChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X,Y>> data) {
super(xAxis, yAxis);
setData(data);
}
public double getSwimlaneHeight() {
return swimlaneHeight;
}
public void setSwimlaneHeight(double swimlaneHeight) {
this.swimlaneHeight = swimlaneHeight;
}
private static float getChannelPower( Object obj) {
return ((ExtraData) obj).getChannelPower();
}
final int getDataSize() {
final ObservableList<Series<X,Y>> data = getData();
return (data!=null) ? data.size() : 0;
}
/** @inheritDoc */
@Override protected void layoutPlotChildren() {
List<LineTo> constructedPath = new ArrayList<>(getDataSize());
currentYoffset = 0;
for (int seriesIndex=0; seriesIndex < getDataSize(); seriesIndex++) {
Series<X,Y> series = getData().get(seriesIndex);
if(series.getNode() instanceof Path) {
final ObservableList<PathElement> seriesLine = ((Path)series.getNode()).getElements();
seriesLine.clear();
constructedPath.clear();
for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext(); ) {
Data<X, Y> item = it.next();
double yCat = getYAxis().getDisplayPosition(item.getYValue());
double x = getXAxis().getDisplayPosition(item.getXValue());
double y = getChannelPower(item.getExtraValue()) + yCat;
if (Double.isNaN(x) || Double.isNaN(y)) {
continue;
}
constructedPath.add(new LineTo(x, y));
Node symbol = item.getNode();
if (symbol != null) {
final double w = symbol.prefWidth(-1);
final double h = symbol.prefHeight(-1);
symbol.resizeRelocate(x-(w/2), y-(h/2),w,h);
}
}
if (!constructedPath.isEmpty()) {
LineTo first = constructedPath.get(0);
seriesLine.add(new MoveTo(first.getX(), first.getY()));
seriesLine.addAll(constructedPath);
}
}
currentYoffset+= this.getSwimlaneHeight();
}
}
@Override protected void updateAxisRange() {
final Axis<X> xa = getXAxis();
final Axis<Y> ya = getYAxis();
List<X> xData = null;
List<Y> yData = null;
if(xa.isAutoRanging()) xData = new ArrayList<X>();
if(ya.isAutoRanging()) yData = new ArrayList<Y>();
if(xData != null || yData != null) {
for(Series<X,Y> series : getData()) {
for(Data<X,Y> data: series.getData()) {
if(xData != null) xData.add(data.getXValue());
if(yData != null) yData.add(data.getYValue());
}
}
// RT-32838 No need to invalidate range if there is one data item - whose value is zero.
if(xData != null) xa.invalidateRange(xData);
if(yData != null) ya.invalidateRange(yData);
}
}
}
【问题讨论】:
-
即:某个时间序列的 yAxis 可以向上/向下移动吗?所以系列可以绘制在不同的相对 yAxis...不确定...
-
y 轴上没有值。只需根据您想要在图表上的位置为每个系列中的每个点添加一个数量。从你的时间尺度来看,javaFX 图表不够快。
-
yes 会尝试(抵消 y 值),至于渲染性能我不确定 javafx 如何在内部渲染图表(将查看源代码)但是是的,应该是基于“画布”的图表快点 !对吗?
-
画布更快,但 javaFX 画布仍会呈现像线条一样的节点,这比直接绘制要慢。相比之下,AWT 画布非常快。也没有时间轴,但只显示几秒钟,您可以为 NumberAxis 制作格式化程序。我会先在 FX 中尝试,因为它应该很容易。
-
谢谢 Brian,问题是医疗数据非常密集,你可能每秒有 256 个点(数据采集采样率),屏幕必须显示至少 10 秒,这样医生才能有想法...如果 javaFX 将它们都视为对象,那么它就行不通了!