I am trying to build a series chart using JavaFX, where data is inserted dynamically.
Each time that a new value is inserted I would like to check if this is the highest value so far, and if so, I want to draw an horizontal line to show that this is the maximum value.
In JFree chart I would have used a ValueMarker, but I am trying to do the same with JavaFX.
I tried using the Line object, but it is definitely not the same, because I cannot provide the Chart values, it takes the relative pixel positions in the windows.
Here is the screenshot of chart I want to achieve:
http://postimg.org/image/s5fkupsuz/
Any suggestions? Thank you.
To convert chart values to pixels you can use method NumberAxis#getDisplayPosition()
which return actual coordinates of the chart nodes.
Although these coordinates are relative to chart area, which you can find out by next code:
Node chartArea = chart.lookup(".chart-plot-background");
Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());
Note localToScene()
method which allows you to convert any coordinates to Scene ones. Thus you can use them to update your value marker coordinates. Make sure you make localToScene
call after your Scene have been shown.
See sample program below which produces next chart:
public class LineChartValueMarker extends Application {
private Line valueMarker = new Line();
private XYChart.Series<Number, Number> series = new XYChart.Series<>();
private NumberAxis yAxis;
private double yShift;
private void updateMarker() {
// find maximal y value
double max = 0;
for (Data<Number, Number> value : series.getData()) {
double y = value.getYValue().doubleValue();
if (y > max) {
max = y;
}
}
// find pixel position of that value
double displayPosition = yAxis.getDisplayPosition(max);
// update marker
valueMarker.setStartY(yShift + displayPosition);
valueMarker.setEndY(yShift + displayPosition);
}
@Override
public void start(Stage stage) {
LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(0, 100, 10), yAxis = new NumberAxis(0, 100, 10));
series.getData().add(new XYChart.Data(0, 0));
series.getData().add(new XYChart.Data(10, 20));
chart.getData().addAll(series);
Pane pane = new Pane();
pane.getChildren().addAll(chart, valueMarker);
Scene scene = new Scene(pane);
// add new value on mouseclick for testing
chart.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
series.getData().add(new XYChart.Data(series.getData().size() * 10, 30 + 50 * new Random().nextDouble()));
updateMarker();
}
});
stage.setScene(scene);
stage.show();
// find chart area Node
Node chartArea = chart.lookup(".chart-plot-background");
Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());
// remember scene position of chart area
yShift = chartAreaBounds.getMinY();
// set x parameters of the valueMarker to chart area bounds
valueMarker.setStartX(chartAreaBounds.getMinX());
valueMarker.setEndX(chartAreaBounds.getMaxX());
updateMarker();
}
public static void main(String[] args) {
launch();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With