'CategoryAxis (xAxis) get Position of each Category

I want to add a Line between each Category on a CategoryAxis, the Problem is that the Categories change based on User Input, so i would need to add them at runtime, which I am doing by extending CustomBarChart and adding them on seriesAdded().

How can I get get the Position of each Category (xAxis) aswell as the Length of each Category (xAxis) so i can calculate where to put the Line between the Graphs.

Haven't found a way to get the Width of each Category yet.

Using the getDisplayPosition() should get me the Position but using it for this Graph e.g. gave me these results and I can't seem to figure out how they're related to the actual Position.

enter image description here

4.545454545454546
3.6363636363636367
2.7272727272727275
1.8181818181818188
0.9090909090909096
8.881784197001252E-16
-0.9090909090909083
-1.8181818181818175
-2.7272727272727266
-3.636363636363636
-4.545454545454544


Solution 1:[1]

This is an adaptation of the idea here. It should provide a good starting point.

package org.jamesd.examples.barchartwithlines;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.io.IOException;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        CategoryAxis xAxis = new CategoryAxis();
        NumberAxis yAxis = new NumberAxis();
        BarChart<String, Number> chart = new BarChart<>(xAxis, yAxis) {
            private List<Line> lines = new ArrayList<>();
            @Override
            protected void layoutPlotChildren() {
                super.layoutPlotChildren();
                List<Series<String, Number>> data = getData();
                if (data.size() == 0) {
                    getPlotChildren().removeAll(lines);
                    lines.clear();
                    return ;
                }
                // assuming only one series
                Series<String, Number> series = data.get(0);
                int numLines = series.getData().size() - 1;
                // remove unused lines:
                for (int i = lines.size() - 1 ; i >= numLines ; i--) {
                    getPlotChildren().remove(lines.get(i));
                    lines.remove(i);
                }
                // create new lines:
                for (int i = lines.size() ; i < numLines ; i++) {
                    Line line = new Line();
                    lines.add(line);
                    getPlotChildren().add(line);
                }

                double[] catPositions = new double[numLines + 1];
                for (int i = 0 ; i < catPositions.length ; i++) {
                    Data<String, Number> d = series.getData().get(i);
                    catPositions[i] = xAxis.getDisplayPosition(d.getXValue());
                }
                Arrays.sort(catPositions);
                double startY = yAxis.getDisplayPosition(yAxis.getLowerBound());
                double endY = yAxis.getDisplayPosition(yAxis.getUpperBound());
                for (int i = 0 ; i < catPositions.length - 1 ; i++) {
                    Line line = lines.get(i);
                    double lineX = (catPositions[i] +  catPositions[i+1]) / 2 ;
                    line.setStartX(lineX);
                    line.setEndX(lineX);
                    line.setStartY(startY);
                    line.setEndY(endY);
                }
            }
        };

        Random rng = new Random();
        XYChart.Series<String, Number> series = new XYChart.Series<>();
        DateTimeFormatter format = DateTimeFormatter.ofPattern("LLL");
        for (Month month : Month.values()) {
            series.getData().add(new XYChart.Data<>(format.format(month), rng.nextDouble() * 100));
        }
        series.setName("2020");
        chart.getData().add(series);

        BorderPane root = new BorderPane(chart);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

This results in

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1