user2858153
user2858153

Reputation: 51

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

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".

Upvotes: 5

Views: 3580

Answers (6)

Galya
Galya

Reputation: 6364

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.

Upvotes: 1

sbjorng
sbjorng

Reputation: 131

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

Upvotes: 1

AloneInTheDark
AloneInTheDark

Reputation: 938

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.

Upvotes: 0

KnusperPudding
KnusperPudding

Reputation: 412

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.

Upvotes: 3

SamHuman
SamHuman

Reputation: 186

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.

Upvotes: 4

downdrown
downdrown

Reputation: 361

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

Upvotes: 3

Related Questions