JavaFX LineChart / AreaChart wrong sorted values on x-axis (CategoryAxis)
Asked Answered
C

6

5

When trying to build an application which contains an updateable LineChart or AreaChart I recognized strange behaviour - probably due to an error in application logic?

The goal is to fill data into or update a chart when a button "Generate" is clicked. User has to input a start time and an end time for the graph, in addition an interval Hours / Days / Weeks has to be chosen (by using a RadioGroup).

Creation of the initial chart works without any problems. Re-generating the chart works properly too, but only as long as data points didn't exist in previous chart. If data points with same x-values are contained in both charts (old and updated one), sorting isn't ascending anymore.

Example:

Execution with StartDate: 01.09.2013 EndDate: 25.09.2013 Interval: Weeks

Values on x-axis:

01.09.2013 / 08.09.2013 / 15.09.2013 / 22.09.2013

Clicking the "Days" RadioButton an re-generating the chart yields to following values on x-axis:

01.09.2013 / 08.09.2013 / 15.09.2013 / 22.09.2013 / 02.09.2013 / 03.09.2013 / 04.09.2013 / ...

(values should be 01.09.2013 / 02.09.2013 / 03.09.2013 / ...)

All values which already have been shown in first chart and are in second chart too, are sorted at the beginning of the new chart (and not in ascending order)

Here's the code that does the trick (code for method initializeTimeline is just for testing purposes (surely a bit optimizable ;))):

public class ChartDesignController implements Initializable {
@FXML
private AreaChart chartOne;
@FXML
private TextField startDate;
@FXML
private TextField endDate;
@FXML
private RadioButton radioHours;
@FXML
private RadioButton radioDays;
@FXML
private RadioButton radioWeeks;
@FXML
private ToggleGroup timeUnit;
@FXML
private Label msgBox;

@FXML
private void generateGraph(ActionEvent event) {
    String timeUnit = getTimeUnit();
    Series s = initializeTimeline(startDate.getText(), endDate.getText(), timeUnit);
    chartOne.getData().setAll(s);
}

private String getTimeUnit() {
    String timeUnitForQuery = "DD";
    RadioButton selectedRadio = (RadioButton) timeUnit.getSelectedToggle();
    if (selectedRadio == radioHours) {
        //System.out.println("RadioHours was selected");
        timeUnitForQuery = "HH24";
    }
    if (selectedRadio == radioDays) {
        //System.out.println("RadioDays was selected");
        timeUnitForQuery = "DD";
    }
    if (selectedRadio == radioWeeks) {
        //System.out.println("RadioWeeks was selected");
        timeUnitForQuery = "IW";
    }
    //System.out.println("Time unit changed");
    return timeUnitForQuery;
}

@Override
public void initialize(URL url, ResourceBundle rb) {
}

private Series initializeTimeline(String startTime, String endTime, String timeUnit) {
    msgBox.setText("");
    long delta;
    int nrOfTicks = 0;
    Data<String, Integer> dp;
    Series s = new Series();
    ArrayList<Data<String, Integer>> dataPoints = new ArrayList();
    SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
    Date startDate = new Date();
    Date endDate = new Date();
    GregorianCalendar startTimeGC = new GregorianCalendar();
    GregorianCalendar endTimeGC = new GregorianCalendar();
    if (timeUnit.equalsIgnoreCase("HH24")) {
        sdf = new SimpleDateFormat("dd.MM.yyyy HH");
    }
    try {
        startDate = sdf.parse(startTime);
        endDate = sdf.parse(endTime);
    } catch (ParseException ex) {
        msgBox.setText(ex.getMessage() + "\n" + "Format expected: " + sdf.toPattern());
    }
    startTimeGC.setTimeInMillis(startDate.getTime());
    endTimeGC.setTimeInMillis(endDate.getTime());
    delta = endTimeGC.getTimeInMillis() - startTimeGC.getTimeInMillis();
    if (timeUnit.equalsIgnoreCase("HH24")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60) + 1;
    } else if (timeUnit.equalsIgnoreCase("DD")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60 / 24) + 1;
    } else if (timeUnit.equalsIgnoreCase("IW")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60 / 24 / 7) + 1;
    }
    for (int i = 0; i < nrOfTicks; i++) {
        dp = new Data(sdf.format(startTimeGC.getTime()), 0);
        dataPoints.add(dp);
        if (timeUnit.equalsIgnoreCase("HH24")) {
            startTimeGC.add(GregorianCalendar.HOUR_OF_DAY, 1);
        } else if (timeUnit.equalsIgnoreCase("DD")) {
            startTimeGC.add(GregorianCalendar.DAY_OF_MONTH, 1);
        } else if (timeUnit.equalsIgnoreCase("IW")) {
            startTimeGC.add(GregorianCalendar.WEEK_OF_YEAR, 1);;
        }
    }
    dataPoints.sort(new DataComparator());
    s.setData(FXCollections.observableList(dataPoints));
    return s;
 }
}

Below, the DataComparator which is in charge of sorting the dataPoints alphabetically according to their x-values:

public class DataComparator implements Comparator<Data> {
@Override
public int compare(Data d1, Data d2) {
    String d1Xval = (String) d1.getXValue();
    String d2Xval = (String) d2.getXValue();
    return d1Xval.compareTo(d2Xval);
    }
}

When debugging the program values are sorted correctly in ArrayList dataPoints.

Any ideas why values are sorted incorrectly then in chart x-axis, in case they already appeared in old chart?

Would greatly appreciate help for this "issue".

Chaisson answered 9/10, 2013 at 6:43 Comment(0)
T
4

I had the same problem. No matter how I setup the plot the ordering would always get messed up by reploting with new datapoints. The only fix was using java 1.8.

This was with the latest fx at the time, 2.2.51-b13. had to go with fx8 early access release.

Travel answered 5/2, 2014 at 18:31 Comment(0)
M
3

I just wanted to say, I got the (nearly) same issue with a line Chart!

I load some data from a database to be displayed in a line chart. The data simply represents a sum for a month.

When I load the data for "user1" everything looks fine: Chart 1 with clear data.

But when I load the data with my 2nd user, it looks like this: Chart 2 with strange data.

I really can't figure out why this is happening, because i sort the data while selecting from database like this:

...NTING.CUSTOMER = '"+Customer+"' GROUP BY MONTH ORDER BY MONTH ASC;

So… that's all I can say… I order the data while fetching it from database, for one user it works, for the other one it doesn't! and it really freaks me out!

I just found a posting anywhere where someone recommends to download the 'Early Access Version' of JRE and JDK 8 - But in my opinion, I wouldn't recommend that for a productive system!

Hope anyone got the solution for that nasty problem!

Just before anyone rages about that this isn't the answer - I know that!

But I think collecting as much information as possible also appears to be a part of the solution.

And because of the (sometimes strange privilege-preferences of stackoverflow) I can't comment till I got a higher reputation value!

I didn't want to ask a new question, while this is already discussed here!

Hope this is ok…

Greets

Minstrelsy answered 9/11, 2013 at 18:42 Comment(2)
Could you find any solution for this strange bug?Bax
i found somethig strange that if you change categoryaxis to a custom value(for example "hello"+i) then your data is always sorted true.Bax
C
3

i have encountered a similar issue with linechart trying to add Date-Points and while debugging i figured out something:

i added a Series-Object to the ObservableList which containes 3 Date Points:
01.01.2014,
02.01.2014,
02.02.2014
and some double for the y-Axis.

(So far so good, the Line Chart is shown as it should).

after this i added a second Series-Object with the Date Points:
01.01.2014,
02.01.2014,
03.02.2014.

and again: the line chart works.

If i add the second Series-Object first, my linechart will show the X-Axis values as following:
01.01.2014
02.01.2014
03.02.2014
02.02.2014
which ruins my linechart by wrong order.

On my way trying to find a solution i figured out a 'workaround':
Adding a Series Object containing all Date-Values which are used later on other Series objects as first Series-Object in the Observable List helps.
it can be 'hidden' as "Average Value" or such but i am not really happy with it though but it works to display the chart in the correct order at least

My first post here by the way, just tried to help at least to get a solution, since i landed here searching for the same issue.

Cannady answered 28/8, 2014 at 7:51 Comment(3)
i only add one series object, if number of points are increased, it's more likely to ruin the linechart. i have a combobox when you select a date, it clears series and reload it with new values. when i try to load series more than 2 times, values are sorted wrongly. i really can't figure out what is the problem...Bax
@Bax Thank you for the temporary solution. Maybe the way i tried, might help the other guy(drown) with the line chart though.Cannady
bad thing is you can't hide that "i" variable on chart. can we just mask it, i don't know how to do it.Bax
I
1

This is a workaround, recreate the whole graph every time, it is not the cleanest way, but it works. Before you add the series to the graph, just call graph.getData().clear() and then graph.layout();, got the answer from Recreate bar chart without it remembering data

Irreclaimable answered 22/6, 2016 at 22:34 Comment(1)
barChart.layout() was indeed what I needed to do! Thank you!Wesla
S
1

I had the same issue with JavaFX 11, using JDK 14. It was a LineChart with CategoryAxis as xAxis that was filled with dates in a sortable format yyyy-MM-dd HH:mm:ss, but they didn't get auto-sorted, when adding multiple Series.

I've found out that for some reason the categories are not recognized (categories was empty):

    CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis();
    ObservableList<String> categories = xAxis.getCategories();

I'm still not sure what's the underlying reason for it not working properly out of the box, but that's the solution I came up with:

    lineChart.setData(chartSeriesList);
    CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis();
    ObservableList<String> categories = FXCollections.observableArrayList(allDates);
    Collections.sort(categories);
    xAxis.setAutoRanging(true);
    xAxis.setCategories(categories);

Basically explicitly setting up the categories (list of dates in string format) on the xAxis.

Syl answered 18/2, 2021 at 8:22 Comment(0)
B
0

I found a "temporary" solution about this.

series.getData().add(new BarChart.Data<>(rs.getString(1)+" "+i,rs.getDouble(2)));

Adding an i variable, which increases with data prevents data to be ruined.

Another solution is using JDK 8.

Bax answered 28/8, 2014 at 8:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.