Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


Code Block

package nl.wldelft.fews.system.plugin.dataExport;

import nl.wldelft.util.PropertiesConsumer;
import nl.wldelft.util.FastDateFormat;
import nl.wldelft.util.Period;
import nl.wldelft.util.Properties;
import nl.wldelft.util.TextUtils;
import nl.wldelft.util.io.LineWriter;
import nl.wldelft.util.io.TextSerializer;
import nl.wldelft.util.timeseries.TimeSeriesContent;
import nl.wldelft.util.timeseries.TimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeStep;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.util.Date;
import java.util.Locale;

/**
 * <p>The class <code>ShefTimeSeriesWriter</code> exports the time series from Fews system
 * in SHEF formatted export file(s). See the description of the SHEF format for more information.
 * </p>
 */

public class ShefTimeSeriesSerializer implements TextSerializer<TimeSeriesContent>, PropertiesConsumer {
    private static final Logger log = LogManager.getLogger();

    //          location,  missingValue, values
    //ToDo Use configured precision of the parameters to print the values


    private static final String MISSING_VALUE = "       M";

    private static final String shefFormatProperty = "format";
    private static final String  shefSourceProperty = "source";

    private String shefFormat = "E";
    private String shefSource = "FEWS";

    private final long timeZero;

    private FastDateFormat dateFormat = null;
    private FastDateFormat dateFormatCreation = null;
    private FastDateFormat obsTimeFormat = null;
    private FastDateFormat obsDateTimeFormat = null;

    private TimeSeriesContent content = null;
    private LineWriter writer = null;

    private TimeSeriesHeader header = null;

    private long currentTime = System.currentTimeMillis();

    public ShefTimeSeriesSerializer(long timeZero) {
        this.timeZero = timeZero;
    }

    @Override
    public void setProperties(Properties properties) {
        for (int i = 0; i < properties.size(); i++) {
            String key = properties.getKey(i);
            if (key.equals(shefFormatProperty)) {
                this.shefFormat = properties.getString(i);
            }
            if (key.equals(shefSourceProperty)) {
                this.shefSource = properties.getString(i);
            }
        }
    }

    @Override
    public void serialize(TimeSeriesContent content, LineWriter writer, String virtualFileName) throws Exception {
        dateFormat = FastDateFormat.getInstance("yyyyMMdd", content.getDefaultTimeZone(), Locale.US, dateFormat);
        dateFormatCreation = FastDateFormat.getInstance("yyyyMMddHHmm", content.getDefaultTimeZone(), Locale.US, dateFormatCreation);
        obsTimeFormat = FastDateFormat.getInstance("HHmm", content.getDefaultTimeZone(), Locale.US, obsTimeFormat);
        obsDateTimeFormat = FastDateFormat.getInstance("yyMMddHHmm", content.getDefaultTimeZone(), Locale.US, obsDateTimeFormat);

        this.currentTime = System.currentTimeMillis();
        this.content = content;
        this.writer = writer;

        writeFileHeader();

        switch (this.shefFormat) {
            case "A":
                serializeFormatA();
                break;
            case "B":
                serializeFormatB();
                break;
            case "E":
                serializeFormatE();
                break;
        }
    }

    private void serializeFormatA() throws Exception {

        for (int is = 0, ns = content.getTimeSeriesCount(); is < ns; is++) {
            content.setTimeSeriesIndex(is);
            header = content.getTimeSeriesHeader();
            for (int it = 0, nt = content.getContentTimeCount(); it < nt; it++) {
                content.setContentTimeIndex(it);
                writeDataLineFormatA();
            }
        }
    }

    private void serializeFormatB() throws Exception {
        String parameter = null;
        for (int is = 0, ns = content.getTimeSeriesCount(); is < ns; is++) {
            content.setTimeSeriesIndex(is);
            header = content.getTimeSeriesHeader();
            if (parameter == null || !parameter.equals(header.getParameterId())) {
                if (parameter != null) writeEnd();
                parameter = header.getParameterId();
                writeHeaderLineFormatB();
            }
            for (int it = 0, nt = content.getContentTimeCount(); it < nt; it++) {
                content.setContentTimeIndex(it);
                if (content.isTimeAvailable()) {
                    writeDataLineFormatB();
                }
            }
        }
    }

    private void serializeFormatE() throws Exception {
        for (int i = 0, nt = content.getTimeSeriesCount(); i < nt; i++) {
            content.setTimeSeriesIndex(i);
            header = content.getTimeSeriesHeader();
            if (header.getTimeStep().isRegular()) {
                writeHeaderLineFormatE();
                writeLineDataFormatE();
            } else {
                log.error("Cannot write nonEquidistant data to SHEF (.E) format for " + header);
                break;
            }
        }
    }

    private void writeFileHeader() throws IOException {
        writer.writeLine(": Delft-FEWS - exported SHEF file");
        String timeZeroText = this.dateFormat.format(new Date(this.timeZero));
        writer.writeLine(": Date/time forecast: " + timeZeroText);
    }

    private void writeDataLineFormatA() throws IOException {
        //Date startDate = content.getTimeSeriesPeriod().getStartDate();
        long obsTime = content.getTime();
        writer.write(".AR ");
        writer.write(header.getLocationId() + " ");
        writer.write(this.dateFormat.format(obsTime));
        writer.write(getTimeZoneShefCode(content.getDefaultTimeZone().getID()));
        writer.write("DH" + obsTimeFormat.format(obsTime));
        writer.write("/DC");
        writer.write(this.dateFormatCreation.format(currentTime));
        writer.write("/");
        writer.write(header.getParameterId() + " ") ;
        writer.writeLine(content.isValueMissing() ? MISSING_VALUE : content.getValue('.') );
    }

    private void writeHeaderLineFormatB() throws IOException {
        writer.write(".BR ");
        writer.write(shefSource + " ");
        Date startDate = content.getTimeSeriesPeriod().getStartDate();
        writer.write(this.dateFormat.format(startDate));
        writer.write(getTimeZoneShefCode(content.getDefaultTimeZone().getID()));
        writer.write("DH" + obsTimeFormat.format(startDate));
        writer.write("/DC");
        writer.write(this.dateFormatCreation.format(currentTime));
        writer.writeLine("/" + header.getParameterId());
    }

    private void writeDataLineFormatB() throws IOException {
        long obsTime = content.getTime();
        writer.write(header.getLocationId() + " ");
        writer.write("DY" + this.obsDateTimeFormat.format(obsTime));
        writer.write("/");
        writer.writeLine(content.isValueMissing() ? MISSING_VALUE : content.getValue('.') );
    }

    private void writeEnd() throws IOException {
        writer.writeLine(".END");
    }

    private void writeHeaderLineFormatE() throws Exception {

        if(content.getTimeSeriesPeriod() == Period.NEVER){
            if (log.isInfoEnabled()) log.info("No data found for " + header);
            return;
        }
        writer.newLine();
        writer.write(".ER ");
        writer.write(header.getLocationId());
        writer.write(' ') ;
        Date startDate = content.getTimeSeriesPeriod().getStartDate();
        writer.write(this.dateFormat.format(startDate));
        writer.write(getTimeZoneShefCode(content.getDefaultTimeZone().getID()));
        writer.write("DH" + obsTimeFormat.format(startDate));
        writer.write("/DC") ;
        writer.write(this.dateFormatCreation.format(currentTime));
        writer.write('/') ;
        writer.write(TextUtils.padRight(header.getParameterId(), 7)) ;
        writer.write('/') ;
        writeTimeStep();

        writer.newLine();
    }

    private void writeLineDataFormatE() throws Exception {

        //Determine width of column with line number (e.g.  .E37)
        int lineCountColWidth = 5;  // default  .E + 2 places for line nr + one space
        int ndig = TextUtils.getNumberOfDigits(content.getContentTimeCount() / 8);  //8 = number of values per line
        if (ndig > 2) lineCountColWidth = ndig + 3; //  .E + ndig places for line nr + one space

        int linecount = 1;

        int count = 0;
        for (int i = 0, n = content.getContentTimeCount(); i < n; i++) {
            content.setContentTimeIndex(i);
            if (!content.isTimeAvailable()) continue;
            if (count == 0) {
                writer.write(TextUtils.padRight(".E" + linecount, lineCountColWidth));
            }
            if (content.isValueMissing() && Float.isNaN(content.getDefaultMissingValue())) {
                writer.write(MISSING_VALUE);
            } else {
                writer.write(content.getValue('.'));  //get formatted value
            }
            writer.write('/');
            count++;
            if (count == 8 || i == n - 1) {
                linecount++;
                writer.newLine();
                count = 0;
            }


        }
    }

    private void writeTimeStep() throws Exception {

        TimeStep timeStep = header.getTimeStep();
        long dtime = timeStep.getMaximumStepMillis()/ 1000;

        writer.write("DI");

        if (dtime < 60) {
            writer.write("I" + dtime);
        } else if (dtime < 3600) {
            writer.write("N" + dtime / 60);
        } else if (dtime < 36000) {
            writer.write("H0" + dtime / 3600);
        } else if (dtime < 86400) {
            writer.write("H" + dtime / 3600);
        } else {
            writer.write("D" + dtime / 86400);
        }
    }

    private static String getTimeZoneShefCode(String timeZoneId) throws IOException {
        //Documented in http://www.nws.noaa.gov/om/water/resources/SHEF_CodeManual_5July2012.pdf   ,  Table 8
        if (timeZoneId.equals("GMT")) return " Z ";
        if (timeZoneId.equals("CNT")) return " N ";
        if (timeZoneId.equals("CNT")) return " N ";
        if (timeZoneId.equals("PRT")) return " A ";
        if (timeZoneId.equals("EST")) return " E ";
        if (timeZoneId.equals("IET")) return " E ";
        if (timeZoneId.equals("CST")) return " C ";
        if (timeZoneId.equals("MST")) return " M ";
        if (timeZoneId.equals("PST")) return " P ";
        if (timeZoneId.equals("HST")) return " H ";
        if (timeZoneId.equals("AST")) return " L ";

        throw new IOException("SHEF export does not supported time zone Id  " + timeZoneId);
    }

}