javajavafxregressionjfreechartjfreechart-fx

Create a checkbox that adds a line of best fit to the graph when checked and removes the best fit line when unchecked in real time


I would like to create a checkbox that adds a line of best fit when checked and removes the line of best fit when unchecked in real-time, below I've created a button which adds a best fit line when the series is longer than 2 items I would like to do something similar to this but with a checkbox and in real-time.

public class ScatterAdd extends Application {


    private static final XYSeries series = new XYSeries("Voltage");
    private final XYSeries trend = new XYSeries("Trend");
    private final XYSeriesCollection dataset = new XYSeriesCollection(series);
    TableView table;

    ChoiceBox<String> domainLabels = new ChoiceBox<>();
    ChoiceBox<String> rangeLabels = new ChoiceBox<>();

    private JFreeChart createChart() {
        return ChartFactory.createScatterPlot("VI Characteristics", "Current", "Voltage", dataset);
    }

    @Override
    public void start(Stage stage) {

        Image image = new Image("Grava.logo.png");
        stage.getIcons().add(image);
        var chart = createChart();


        XYPlot plot = chart.getXYPlot();
        plot.setDomainCrosshairVisible(true);
        plot.setRangeCrosshairVisible(true);
        XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
        r.setSeriesLinesVisible(1, Boolean.TRUE);
        r.setSeriesShapesVisible(1, Boolean.FALSE);


        var equation = new TextField();
        var rValuebox = new TextField();
        var rBox = new TextField();
        series.addChangeListener((event) -> {
            if (series.getItemCount() > 1) {
                double[] coefficients = Regression.getOLSRegression(dataset,0);
                double b = coefficients[0]; // intercept
                double m = coefficients[1]; // slope
                equation.setText("y = " + m + " x + " + b);
                double x = series.getDataItem(0).getXValue();
            equation.setText(String.valueOf(series.getDataItem(0).getXValue()));

            }
        });


        domainLabels.getSelectionModel().selectedItemProperty().addListener((ov, s0, s1) -> {
            chart.getXYPlot().getDomainAxis().setLabel(s1);
        });
        rangeLabels.getSelectionModel().selectedItemProperty().addListener((ov, s0, s1) -> {
            chart.getXYPlot().getRangeAxis().setLabel(s1);
        });

        domainLabels.getItems().addAll("Current", "Seconds");
        domainLabels.setValue("Current");

        rangeLabels.getItems().addAll("Voltage", "Metres");
        rangeLabels.setValue("Voltage");


        var xSpin = new Spinner<Double>(-10000000.000, 10000000.000, 0, 0.1);
        xSpin.setEditable(true);
        xSpin.setPromptText("Xvalue");

        var ySpin = new Spinner<Double>(-10000000.000, 10000000.000, 0, 0.1);
        ySpin.setEditable(true);
        ySpin.setPromptText("Yvalue");

        var addButton = new Button("Add");
        addButton.setOnAction(ae -> series.add(xSpin.getValue(), ySpin.getValue()));

        var testButton = new Button("bestfit Line");
        testButton.setOnAction(ae -> {
            if (series.getItemCount() > 1) {
                double[] coefficients = Regression.getOLSRegression(dataset,0);
                double b = coefficients[0]; // intercept
                double m = coefficients[1]; // slope
                double x = series.getDataItem(0).getXValue();
                trend.clear();
                trend.add(x, m * x + b);
                x = series.getDataItem(series.getItemCount() - 1).getXValue();
                trend.add(x, m * x + b);
                dataset.addSeries(trend);
            }
        });


        HBox xBox = new HBox();
        xBox.getChildren().addAll(domainLabels);

        HBox yBox = new HBox();
        yBox.getChildren().addAll(rangeLabels);

        var enter = new ToolBar(xBox, xSpin, yBox, ySpin, addButton, equation, rValButton, rValuebox, testButton, rBox);
        BorderPane.setAlignment(enter, Pos.CENTER);

        BorderPane root = new BorderPane();
        root.setCenter(new ChartViewer(chart));
        root.setBottom(enter);

        stage.setTitle("ScatterAdd");
        stage.setScene(new Scene(root, 720, 480));
        stage.show();

   }
}

*I have attempted to create the checkbox and create the best fit line within the addListner however entering more than 2 values and selecting the checkbox I get an error.

var g = chart.getXYPlot().getRenderer();
        var cb = new CheckBox("Trend");

        cb.selectedProperty().addListener((o) -> {
            if (series.getItemCount() > 1) {
                g.setSeriesVisible(1, cb.isSelected());
                double[] coefficients = Regression.getOLSRegression(dataset, 0);
                double b = coefficients[0]; // intercept
                double m = coefficients[1]; // slope
                double x = series.getDataItem(0).getXValue();
                trend.clear();
                trend.add(x, m * x + b);
                x = series.getDataItem(series.getItemCount() - 1).getXValue();
                trend.add(x, m * x + b);
                dataset.addSeries(trend);
            }
        });

Solution

  • As shown here, the plot's renderer controls series visibility. In this case, add a listener to the check box's selected property and update the trend series visibility accordingly.

    var r = chart.getXYPlot().getRenderer();
    var cb = new CheckBox("Trend");
    cb.setSelected(true);
    cb.selectedProperty().addListener((o) -> {
        r.setSeriesVisible(1, cb.isSelected());
    });
    var enter = new ToolBar(…, cb, …);
    

    Note that you must not repeatedly add the trend series to the dataset in your button handler; adding it once will give it index 1, shown above.