0

I am using JavaFX Linechart in an application. I would like to read X and Y axis of the graph from the display. I mean from the graph. How can I do that? I would like to save the values in an ArrayList/ Array.

Thanks in advance.

  • Just to clarify, you're looking for the _visual coordinates_ of each `XYChart.Data` in your `LineChart`, right? – Slaw Jun 21 '18 at 12:12
  • The values of the `Chart` are already stored in a `List`. https://docs.oracle.com/javafx/2/charts/pie-chart.htm – SedJ601 Jun 21 '18 at 13:15

1 Answers1

0

Since I believe your question could be asking for either the visual coordinates of your data or the real data used to plot the chart I'm going to provide answers for both.


Real Data

A LineChart extends from XYChart which has a data property. This property contains an ObservableList of XYChart.Series. Each XYChart.Series also has its own data property. This property contains an ObservableList of XYChart.Data. Each XYChart.Data has a property for its X value and a property for its Y value. Access these properties to get the X and Y values used to plot the graph.

Since the chart already contains the data you're looking for in a collection there may be no need to add it to a separate ArrayList or array.


Visual Coordinates

A XYChart (which again LineChart extends from) has a X axis and a Y axis. These are of the type Axis which provides the following method: getDisplayPosition(Object). This method takes as an argument the real data that was plotted on the axis. For example, if you plotted the point (4, 16) you'd get the display positions by:

  • For the X coordinate: chart.getXAxis().getDisplayPosition(4);
  • For the Y coordinate: chart.getYAxis().getDisplayPosition(16);

When using a LineChart (and most likely other charts but I've only tested it with a LineChart) these coordinates are not relative to the LineChart. Instead they are relative to a child Region that has a style-class of chart-plot-background (at least in my testing). Here is the code I used to test getting the visual coordinates. It creates a LineChart with an exponential function from [0-10] and adds Circles to a Group stacked on top of the LineChart in the same positions as the plotted data.

Note: I used the var keyword a lot which was added to Java 10. You'll either need Java 10+ to run this or you'll need to replace the var keywords with the explicit type.

import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.ParallelTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        var chart = createChart();

        var group = new Group();
        group.setManaged(false);
        primaryStage.setOnShown(we -> addFadingCircles(group, chart));

        var root = new StackPane(chart, group);
        var scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Workshop");
        primaryStage.setResizable(false);
        primaryStage.show();

    }

    private LineChart<Number, Number> createChart() {
        var xAxis = new NumberAxis();
        xAxis.setLabel("X");

        var yAxis = new NumberAxis();
        yAxis.setLabel("Y");

        var chart = new LineChart<>(xAxis, yAxis);
        var series = new XYChart.Series<Number, Number>();

        for (int x = 0; x <= 10; x++) {
            series.getData().add(new XYChart.Data<>(x, Math.pow(x, 2)));
        }

        chart.getData().add(series);
        return chart;
    }

    /*
     * This method assumes that "group" is stacked on top of "chart" and takes up the
     * same area in the scene-graph.
     */
    private void addFadingCircles(Group group, LineChart<Number, Number> chart) {
        var animation = new ParallelTransition();
        animation.setAutoReverse(true);
        animation.setCycleCount(Animation.INDEFINITE);

        var plotRegion = chart.lookup(".chart-plot-background");

        for (var series : chart.getData()) {
            for (var data : series.getData()) {
                var xPos = chart.getXAxis().getDisplayPosition(data.getXValue());
                var yPos = chart.getYAxis().getDisplayPosition(data.getYValue());

                var scenePoint = plotRegion.localToScene(xPos, yPos);
                var groupPoint = group.sceneToLocal(scenePoint);

                var circle = new Circle(groupPoint.getX(), groupPoint.getY(), 10);
                circle.setFill(Color.DEEPSKYBLUE);

                var fadeTrans = new FadeTransition(Duration.millis(500), circle);
                fadeTrans.setFromValue(0.0);
                fadeTrans.setToValue(0.8);
                animation.getChildren().add(fadeTrans);

                group.getChildren().add(circle);
            }
        }

        animation.play();
    }

}

Running this code results in:

GIF animation of circles stacked on top of LineChart's plotted data

If your goal is to add things to the Node used to indicate plotted data it may be better to manipulate the node property of the XYChart.Data, however.

Slaw
  • 37,820
  • 8
  • 53
  • 80