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); } }