Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 811
user2858153 Avatar asked Dec 20 '22 23:12

user2858153


1 Answers

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.

like image 183
SamHuman Avatar answered Dec 27 '22 05:12

SamHuman