1

I would like to display a temperature curve over time. I have now read a file, which is CSV-similar, that let me know the time and the temperature indicated. Now I want to use JFreeChart to display an XY graph from the values. The original data of the file looks as follows:

utc,local,celsius
2017-07-12T07:02:53+00:00,2017-07-12T09:02:53+02:00,26.25
2017-07-12T08:02:54+00:00,2017-07-12T10:02:54+02:00,26.08
2017-07-12T09:02:55+00:00,2017-07-12T11:02:55+02:00,25.78
2017-07-12T10:02:56+00:00,2017-07-12T12:02:56+02:00,25.96
2017-07-12T10:51:02+00:00,2017-07-12T12:51:02+02:00,26.14
2017-07-12T10:51:02+00:00,2017-07-12T12:51:02+02:00,26.14

The output of time & temperature values (I have separated from the original file) looks like:

09:02:53,26.25
10:02:54,26.08
11:02:55,25.78
12:02:56,25.96
12:51:02,26.14
12:51:02,26.14

EDIT: Now I have inserted a DateTimeFormatter in the example from Trashgod: It looks like:

public class Test {
public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
        ApplicationFrame frame = new ApplicationFrame("CSVTest");
        Test test = new Test();
        frame.add(test.createChart("Temperature profile"));
        frame.pack();
        frame.setLocationRelativeTo(null);;
        frame.setVisible(true);
    });
}

private ChartPanel createChart(String chartTitle) {
    JFreeChart chart = ChartFactory.createTimeSeriesChart(chartTitle,
        "Time", "Temperature", createDataset(), true, true, false);
    ChartPanel chartPanel = new ChartPanel(chart);
    XYPlot plot = chart.getXYPlot();
    DateAxis domain = (DateAxis) plot.getDomainAxis();
    domain.setDateFormatOverride(DateFormat.getDateInstance());
    plot.setBackgroundPaint(Color.WHITE);
    return chartPanel;
}


    private XYDataset createDataset() {
    TimeSeries series = new TimeSeries("Temperature");
    TimeSeriesCollection dataset = new TimeSeriesCollection(series);
    try (FileReader fr = new FileReader("TestCSV.csv");
        BufferedReader br = new BufferedReader(fr)) {
        String line;
        br.readLine();
        while ((line = br.readLine()) != null) {
            String[] split = line.split(",");

           System.out.println(ZonedDateTime.parse(split[1]).format(DateTimeFormatter.ISO_LOCAL_TIME)  + "," +split[2]);
           ZonedDateTime zdt = ZonedDateTime.of(LocalDate.now(),LocalTime.parse(split[0]), ZoneId.systemDefault());
           String s = ZonedDateTime.parse(split[0]).format(DateTimeFormatter.ISO_LOCAL_TIME);

           Second second =  new Second(Date.from(zdt.toInstant()));
           series.add(second, Double.valueOf(split[1]));
        }
    } catch (IOException | SeriesException e) {
        System.err.println("Error: " + e);
    }
    return dataset;
}

The first line of the "CSV" -like file is still displayed 09:02:53,26.25 Then I get a DateTimeParseException: Text '2017-07-12T09:02:53+02:00' could not be parsed at index 2

Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '2017-07-12T07:02:53+00:00' could not be parsed at index 2
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalTime.parse(Unknown Source)
at java.time.LocalTime.parse(Unknown Source)
at org.jfree.chart.demo.Test.createDataset(Test.java:63)
at org.jfree.chart.demo.Test.createChart(Test.java:43)
at org.jfree.chart.demo.Test.lambda$0(Test.java:34)

Why can not the rest of the file be read nor displayed? ("System.out.println()" should only serve as a control at the end). The DateTimeFomatter is correct, isn´t it?

With your approach, the time to make locally I come no further & the program can not translate. What did I do wrong? How could it work if the direct output

09:02:53,26.25
10:02:54,26.08
11:02:55,25.78
12:02:56,25.96
12:51:02,26.14
12:51:02,26.14

is displayed in a chart? I think to split and to transform like I did is okay, isn´t it? Now I have setDateFormatOverride () in the code, but the error message, as well as the output remain the same.

Jonalcaide
  • 560
  • 8
  • 21
LostInTranslate
  • 93
  • 1
  • 2
  • 11

1 Answers1

1

Several problems are evident:

  • You never add anything to lines; at a minimum, you'll need something like this:

    lines.add(line);
    
  • Instead of ChartFactory.createXYLineChart(), consider creating a time series:

    ChartFactory.createTimeSeriesChart(…)
    
  • The XYDataset returned by createDataset() should be a TimeSeriesCollection to which you add a TimeSeries.

  • In createDataset(), iterate though lines, parse the data fields, and add the values to the TimeSeries.

  • The time values given are most closely modeled by LocalTime, but TimeSeries expects to add() coordinates defined by a RegularTimePeriod and a double; see Legacy Date-Time Code concerning the conversion shown below.

  • Note that TimeSeries throws SeriesException for duplicate domain values; as a result, only three of the four lines int eh sample input air charted.

  • Instead of replacing the factory supplied XYLineAndShapeRenderer, get a reference to it for later modification.

  • Alter the chart's size using one of the approaches shown here.

  • Avoid extending top-level containers line ApplicationFrame.

  • Construct and manipulate Swing GUI objects only on the event dispatch thread.

  • Use a try-with-resources statement to ensure that each resource is closed at the end of the statement.

  • As your actual data contains ISO 8601 dates, ZonedDateTime.parse() can be used directly; use setDateFormatOverride() to format the date axis labels; the example below specifies a UTC time zone in ISO 8601 format for easy comparison; comment out the call to setDateFormatOverride() to see the times in your local time zone.

image

import java.awt.Color;
import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.TimeZone;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.general.SeriesException;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;

/** @see https://stackoverflow.com/a/45173688/230513 */
public class CSVTest {

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            ApplicationFrame frame = new ApplicationFrame("CSVTest");
            CSVTest test = new CSVTest();
            frame.add(test.createChart("Temperature profile"));
            frame.pack();
            frame.setLocationRelativeTo(null);;
            frame.setVisible(true);
        });
    }

    private ChartPanel createChart(String chartTitle) {
        JFreeChart chart = ChartFactory.createTimeSeriesChart(chartTitle,
            "Time", "Temperature", createDataset(), true, true, false);
        ChartPanel chartPanel = new ChartPanel(chart);
        XYPlot plot = chart.getXYPlot();
        plot.setBackgroundPaint(Color.WHITE);
        XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
        r.setBaseShapesVisible(true);
        DateAxis axis = (DateAxis) plot.getDomainAxis();
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ssX");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));
        axis.setDateFormatOverride(df);
        return chartPanel;
    }

    private XYDataset createDataset() {
        TimeSeries series = new TimeSeries("Temperature");
        TimeSeriesCollection dataset = new TimeSeriesCollection(series);
        try (FileReader fr = new FileReader("temp.csv");
            BufferedReader br = new BufferedReader(fr)) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] s = line.split(",");
                ZonedDateTime zdt = ZonedDateTime.parse(s[0]);
                Second second = new Second(Date.from(zdt.toInstant()));
                series.add(second, Double.valueOf(s[2]));
            }
        } catch (IOException | SeriesException e) {
            System.err.println("Error: " + e);
        }
        return dataset;
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the advice. But now the problem is the split. The "CSV" similar file was split, and in the end it looks like "Time and Temperature" originally it looks like this: 2017-07-12T07:02:53+00:00,2017-07-12T09:02:53+02:00,26.25 With your approach, the time to make locally I come no further & the program can not translate. What make I wrong? How could it work if the direct output: 11:02:55,25.78 12:02:56,25.96 Is displayed in a chart? I think to split and to transform like I did is okay, isn´t it? – LostInTranslate Jul 19 '17 at 09:40
  • It looks like you'll need a `DateTimeFormatter`; the tutorial cited has a section on parsing. Now that you have a working example, please [edit](https://stackoverflow.com/posts/45166365/edit) your question to include a [mcve] that shows the _actual_ data and your revised approach. – trashgod Jul 19 '17 at 10:03
  • I have the question revised and now an EDIT added with the appropriate error messages – LostInTranslate Jul 19 '17 at 12:40
  • I'm still seeing the old data; please include the actual data. – trashgod Jul 19 '17 at 16:19
  • Are you still seeing the actual data? – LostInTranslate Jul 20 '17 at 07:49
  • Your string array, `split`, has two components with indexes 0 and 1; try parsing `split[0]`. – trashgod Jul 20 '17 at 10:00
  • I´ve tried parsing split[0], but it doesn´t work. Is series.add() okay? – LostInTranslate Jul 20 '17 at 11:44
  • My example produces the chart shown with the data you originally posted. Please edit your question to include a few representative lines of the _actual_ data you want to parse; a single line is not enough. The single line you posted looks like two offset date-times and a value. Use `OffsetDateTime` to parse either substring; they both appear to represent the same `Instant`. – trashgod Jul 20 '17 at 17:53
  • Need I really still OffsetDateTime if I have already separated the data required & only want to display this? – LostInTranslate Jul 21 '17 at 07:17
  • Use `ZonedDateTime`; otherwise you lose crucial information; if needed, use `setDateFormatOverride()` to format the date as shown above. – trashgod Jul 21 '17 at 08:31
  • Now I have setDateFormatOverride () in the code, but the error message, as well as the output remain the same. Any idea why? I don´t understand that there are no problems with the first line (output is okay), but then the java.time.format.DateTimeParseException. – LostInTranslate Jul 21 '17 at 09:07
  • Your code does not match mine; see [revision 5](https://stackoverflow.com/posts/45173688/revisions). – trashgod Jul 21 '17 at 10:09
  • oh, okay. Now I´ve tried and it works. Thanks! When you use setDateFormatOverride(df.getTimeInstance()), the output time is shown – LostInTranslate Jul 21 '17 at 11:43