package nl.wldelft.timeseriesparsers;

import nl.wldelft.util.FastDateFormat;
import nl.wldelft.util.TextUtils;
import nl.wldelft.util.TimeSpan;
import nl.wldelft.util.io.LineReader;
import nl.wldelft.util.io.TextParser;
import nl.wldelft.util.timeseries.ComplexEquidistantTimeStep;
import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader;
import nl.wldelft.util.timeseries.SimpleEquidistantTimeStep;
import nl.wldelft.util.timeseries.TimeSeriesContentHandler;
import nl.wldelft.util.timeseries.TimeStep;
import java.io.IOException;
import java.text.ParseException;
import java.util.Locale;
/**
 * TimeSeries reader for TVA CorpsFlowsheet
 *
 * <p>
 * A detailed description can be found in JIRA issue FEWS-11705
 *<pre>
 *
 * The date is assumed to be on the 2nd line, and always formatted exactly the same in terms of spacing and layout.
 * Daily values at midnight central time (GMT-7) period ending. Format yyyy-mm-dd
 *
 * The number of days in the forecast will be on the 3rd line
 * flow in 1000cfs. Elevation in ft.
 *
 * Example:
 *
 * Cumberland Flowsheet
 * Start_Date: 2014-10-01
 * 11 day forecast
 * WolfCreek
 * In         0.1         0.1         0.1         0.1         0.1         0.1         0.1         0.1         0.1         0.1         0.0
 * Elev       702.5       702.4       702.2       702.1       701.9       701.8       701.6       701.5       701.4       701.2       701.1
 * Out         4.0         4.0         4.0         3.1         3.1         3.1         3.1         3.1         3.1         3.1         3.1
 * MWH      1080.0      1080.0      1080.0       855.0       855.0       855.0       855.0       855.0       855.0       855.0       855.0
 *
 *</pre>
 */
public class CorpsFlowsheetTimeSeriesParser implements TextParser<TimeSeriesContentHandler> {
    public static final int DAY_MILLISECONDS = 1000 * 60 * 60 * 24;
    private TimeSeriesContentHandler contentHandler = null;
    private final static char COLUMN_SEPARATOR_CHAR = ' ';
    private long numberOfDayForecast = 0L;
    private String[] buffer = null;
    private long startDate = 0L;
    private String currentLocation = null;
    private TimeStep equidistantTimeStep = null;
    private final static char decimalSeparator = '.';

    @Override
    public void parse(LineReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws IOException {
        this.contentHandler = contentHandler;
        parseHeader(reader);
        while (true) {
            buffer = reader.readLine(COLUMN_SEPARATOR_CHAR);
            if (buffer == null) {
                break;
            }
            if (buffer.length == 1) {
                // a new location
                currentLocation = buffer[0];
            } else {
                parseValues(reader, decimalSeparator, buffer);
            }
        }
    }
    private void parseHeader(LineReader reader) throws IOException {
        // skip the firste line.
        reader.readLine();
        // On the second line we expect 2 columns in yyyy-dd-mm format.
        buffer = reader.readLine(COLUMN_SEPARATOR_CHAR);
        if (buffer == null || buffer.length != 2 || !TextUtils.equals("Start_Date:", buffer[0])) {
            throw new IOException("Start_Date hasn't been specified correctly. Should be specified as follows: Start_Date: yyyy-mm-dd.\n" + reader.getFileAndLineNumber());
        }
        FastDateFormat dateFormat = FastDateFormat.getInstance("yyyy-MM-dd HHmm", this.contentHandler.getDefaultTimeZone(), Locale.US, null);
        try {
            this.startDate = dateFormat.parseToMillis(buffer[1]+" 0000");
        } catch (ParseException e) {
            throw new IOException("Start_Date hasn't been formatted correctly. Should be specified as follows: Start_Date: yyyy-mm-dd.\n" + reader.getFileAndLineNumber());
        }
       if (!this.contentHandler.getDefaultTimeZone().useDaylightTime()) {
           // use the simple equidistant timestep
           equidistantTimeStep = SimpleEquidistantTimeStep.getInstance(DAY_MILLISECONDS);
       } else {
           equidistantTimeStep = ComplexEquidistantTimeStep.getInstance(TimeSpan.DAY, this.contentHandler.getDefaultTimeZone());
       }
        buffer = reader.readLine(COLUMN_SEPARATOR_CHAR);
        this.numberOfDayForecast = Long.parseLong(buffer[0]);
    }
    private void parseValues(LineReader reader, char decimalSeparator, String[] buffer) throws IOException {
        //parse values for current header.
        // for values we expect exactly the number of forecast days + 1 for the paramater.
        if (buffer.length != numberOfDayForecast + 1) {
            throw new IOException("Value rows should contain " + (numberOfDayForecast + 1) + " columns.\n" + reader.getFileAndLineNumber() + ' ' + buffer.length);
        }
        String parameter = buffer[0];
        DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader();
        header.setForecastTime(startDate);
        header.setLocationId(this.currentLocation);
        header.setParameterId(parameter);
        this.contentHandler.setTimeSeriesHeader(header);
        long forecastTime = startDate;
        for (int i = 0; i < numberOfDayForecast; i++) {
            //noinspection StringConcatenationMissingWhitespace
            forecastTime = equidistantTimeStep.nextTime(forecastTime);
            this.contentHandler.setTime(forecastTime);
            this.contentHandler.setValue(decimalSeparator, buffer[i + 1]);
            this.contentHandler.applyCurrentFields();
        }
    }
}
 
  • No labels