package nl.wldelft.fews.pi;
import nl.wldelft.util.*Arguments;
import nl.wldelft.util.coverage.GeometryBinaryUtils;
import nl.wldelft.util.coverage.GeometryUtilsClasz;
import nl.wldelft.util.geodatum.GeoPointFastDateFormat;
import nl.wldelft.util.io.VirtualOutputDirFileUtils;
import nl.wldelft.util.io.VirtualOutputDirConsumerMathUtils;
import nl.wldelft.util.io.XmlSerializerNumberType;
import nl.wldelft.util.timeseries.TimeSeriesContentObjectUtils;
import nl.wldelft.util.timeseries.TimeSeriesHeaderPeriod;
import nl.wldelft.util.timeseries.TimeStepProperties;
import javaxnl.xmlwldelft.streamutil.XMLStreamExceptionTextUtils;
import javaxnl.xmlwldelft.streamutil.XMLStreamWriterTimeZoneUtils;
import java.io.IOExceptionnl.wldelft.util.function.Supplier;
import javanl.wldelft.util.io.OutputStreamVirtualOutputDir;
import java.nio.ByteOrdernl.wldelft.util.io.VirtualOutputDirConsumer;
import javanl.wldelft.util.io.ArraysXmlChunkedSerializer;
import javanl.wldelft.util.timeseries.LocaleProduct;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
public class PiTimeSeriesSerializer implements XmlSerializer<TimeSeriesContent>, VirtualOutputDirConsumer {
private static final int BUFFER_SIZE = 2048;
private TimeZone timeZoneNoDst = null;
public enum EventDestination {
XML_EMBEDDED, SEPARATE_BINARY_FILE, ONLY_HEADERS
}
public enum EnsembleMemberFormat {nl.wldelft.util.timeseries.ProductInfo;
import nl.wldelft.util.timeseries.Statistics;
import nl.wldelft.util.timeseries.TimeSeriesContent;
import nl.wldelft.util.timeseries.TimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeStep;
import nl.wldelft.util.timeseries.TimesOfDayDaylightSavingTimeStep;
import nl.wldelft.util.timeseries.TimesOfDayTimeStep;
import nl.wldelft.util.timeseries.TimesOfHourTimeStep;
import nl.wldelft.util.timeseries.ValueSource;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
public class PiTimeSeriesSerializer implements XmlChunkedSerializer<TimeSeriesContent>, VirtualOutputDirConsumer {
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_2 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_2);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_3 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_3);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_4 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_4);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_5 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_5);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_6 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_6);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_7 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_7);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_8 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_8);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_9 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_9);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_10 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_10);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_11 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_11);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_12 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_12);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_13 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_13);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_14 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_14);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_15 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_15);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_16 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_16);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_17 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_17);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_18 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_18);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_19 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_19);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_20 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_20);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_21 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_21);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_22 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_22);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_23 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_23);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_24 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_24);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_25 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_25);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_26 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_26);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_27 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_27);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_28 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_28);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_29 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_29);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_30 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_30);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_31 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_31);
private static final int BUFFER_SIZE = 2048;
private TimeZone timeZone = null;
private String missingValueText = "NaN";
private float missingValue = Float.NaN;
private String virtualFileName = null;
private boolean skipEmptyTimeSeries = false;
public void setSkipEmptyTimeSeries(boolean skipEmptyTimeSeries) {
this.skipEmptyTimeSeries = skipEmptyTimeSeries;
}
public void setUseMilliseconds (boolean useMilliseconds) {this.useMilliseconds = useMilliseconds; }
public enum EventDestination {
XML_EMBEDDED, SEPARATE_BINARY_FILE, ONLY_HEADERS
}
public enum EnsembleMemberFormat {
INDEX, ID, HIDE
}
private EventDestination eventDestination = EventDestination.XML_EMBEDDED;
private PiVersion version = PiVersion.VERSION_1_2;
private EnsembleMemberFormat ensembleMemberFormat = EnsembleMemberFormat.INDEX;
private boolean anyTimeSeriesWritten = false;
private FastDateFormat dateFormat = null;
private FastDateFormat timeFormat = null;
private boolean useMilliseconds = false;
private TimeSeriesContent timeSeriesContent = null;
private XMLStreamWriter writer = null;
private VirtualOutputDir virtualOutputDir = null;
private OutputStream binaryOutputSteam = null;
private byte[] byteBuffer = null;
private char[] charBuffer = null;
private float[] floatBuffer = null;
private int bufferPos = 0;
private long[] cachedTimes = new long[6];
private String[] cachedDateStrings = new String[6];
private String[] cachedTimeStrings = new String[6];
private int cachedSeconds = -1;
private String cachedSecondsString = null;
private float[][] domainAxesValues = null;
private float[][] newDomainValues = null;
public PiTimeSeriesSerializer() {
}
public PiTimeSeriesSerializer(PiVersion version) {
this.version = version;
}
public void setEventDestination(EventDestination eventDestination) {
this.eventDestination = eventDestination;
}
public PiVersion getVersion() {
return version;
}
public void setVersion(PiVersion version) {
Arguments.require.notNull(version);
this.version = version;
}
public void setEnsembleMemberFormat(EnsembleMemberFormat ensembleMemberFormat) {
Arguments.require.notNull(ensembleMemberFormat);
this.ensembleMemberFormat = ensembleMemberFormat;
}
@Override
public void setVirtualOutputDir(VirtualOutputDir virtualOutputDir) {
this.virtualOutputDir = virtualOutputDir;
}
@Override
public void serializeStartDocument(XMLStreamWriter streamWriter, String virtualFileName) throws Exception {
anyTimeSeriesWritten = false;
this.virtualFileName = virtualFileName;
this.writer = streamWriter;
if (ensembleMemberFormat == EnsembleMemberFormat.ID && version.getIntId() < PiVersion.VERSION_1_10.getIntId()) {
throw new IOException("ensembleMemberId not supported for pi version " + version);
}
writeStartDocument();
String binFileName = FileUtils.getPathWithOtherExtension(virtualFileName, "bin");
if (virtualOutputDir != null) virtualOutputDir.delete(binFileName);
if (eventDestination != EventDestination.SEPARATE_BINARY_FILE) return;
if (virtualOutputDir == null)
throw new IllegalStateException("virtualOutputDir == null");
binaryOutputSteam = virtualOutputDir.getOutputStream(binFileName);
bufferPos = 0;
if (byteBuffer != null) return;
byteBuffer = new byte[BUFFER_SIZE * NumberType.FLOAT_SIZE];
floatBuffer = new float[BUFFER_SIZE];
}
public void writeStartDocument() throws XMLStreamException {
writer.writeStartDocument();
writer.writeStartElement("TimeSeries");
writer.setDefaultNamespace("http://www.wldelft.nl/fews/PI");
writer.writeNamespace("", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
// writer.writeAttribute("xmlns", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xsi:schemaLocation", PiSchemaLocations.get("pi_timeseries.xsd"));
writer.writeAttribute("version", version.toString());
if (version.getIntId() >= PiVersion.VERSION_1_19.getIntId()) {
writer.writeAttribute("xmlns:fs", "http://www.wldelft.nl/fews/fs");
}
}
@Override
public void serializeContent(TimeSeriesContent timeSeriesContent) throws Exception {
if (timeSeriesContent.getTimeSeriesCount() == 0) return;
this.timeSeriesContent = timeSeriesContent;
timeSeriesContent.setRequireEnsembleMemberIndices(ensembleMemberFormat == EnsembleMemberFormat.INDEX);
if (!anyTimeSeriesWritten) {
anyTimeSeriesWritten = true;
initMissingValueText();
initTimeZone();
writeTimeZone();
}
for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) {
timeSeriesContent.setTimeSeriesIndex(i);
if (skipEmptyTimeSeries && timeSeriesContent.isTimeSeriesEmpty()) continue;
if (!timeSeriesContent.isTimeSeriesEmpty()) timeSeriesContent.setContentTimeIndex(0);
writer.writeStartElement("series");
writeHeader();
writeEvents();
writer.writeEndElement();
}
}
@Override
public void serializeEndDocument() throws Exception {
if (!anyTimeSeriesWritten) throw new PiTimeSeriesNotFoundException("No TimeSeries to be Written to " + virtualFileName);
writer.writeEndElement();
writer.writeEndDocument();
if (eventDestination != EventDestination.SEPARATE_BINARY_FILE) return;
flushBinEvents();
binaryOutputSteam.close();
binaryOutputSteam = null;
}
private void initMissingValueText() {
missingValueText = timeSeriesContent.getDefaultMissingValue('.');
try {
missingValue = Float.parseFloat(missingValueText);
} catch (NumberFormatException e) {
timeSeriesContent.setMissingValue(Float.NaN);
missingValue = Float.NaN;
missingValueText = "NaN";
}
}
private void initTimeZone() {
if (!ObjectUtils.equals(timeZone, this.timeSeriesContent.getDefaultTimeZone())) {
Arrays.fill(cachedTimes, Long.MIN_VALUE);
timeZone = timeSeriesContent.getDefaultTimeZone();
}
dateFormat = FastDateFormat.getInstance("yyyy-MM-dd", timeZone, Locale.US, dateFormat);
timeFormat = useMilliseconds?FastDateFormat.getInstance("HH:mm:ss.SSS", timeZone, Locale.US, timeFormat) : FastDateFormat.getInstance("HH:mm:ss", timeZone, Locale.US, timeFormat);
}
private void writeTimeZone() throws XMLStreamException {
if (timeZone.useDaylightTime()) {
PiSerializerUtils.writeElement(writer, "daylightSavingObservingTimeZone", timeZone.getID());
} else {
PiSerializerUtils.writeElement(writer, "timeZone", String.valueOf(TimeZoneUtils.getTimeZoneOffsetHours(timeZone)));
}
}
private void writeHeader() throws Exception {
TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader();
PiTimeSeriesHeader piHeader = header instanceof PiTimeSeriesHeader ? (PiTimeSeriesHeader) header : new PiTimeSeriesHeader();
writer.writeStartElement("header");
PiSerializerUtils.writeElement(writer, "type", header.getParameterType() == null ? "instantaneous" : header.getParameterType().getName());
if (version.getIntId() >= PiVersion.VERSION_1_17.getIntId() && header.getModuleInstanceId() != null) PiSerializerUtils.writeElement(writer, "moduleInstanceId", header.getModuleInstanceId());
PiSerializerUtils.writeElement(writer, "locationId", header.getLocationId() == null ? "unknown" : header.getLocationId());
PiSerializerUtils.writeElement(writer, "parameterId", header.getParameterId() == null ? "unknown" : header.getParameterId());
if (version.getIntId() >= PiVersion.VERSION_1_4.getIntId()) {
for (int i = 0, n = header.getQualifierCount(); i < n; i++) {
PiSerializerUtils.writeElement(writer, "qualifierId", header.getQualifierId(i));
}
}
if (version.getIntId() >= PiVersion.VERSION_1_4.getIntId() && header.getEnsembleId() != null && !header.getEnsembleId().equals("main")) {
if (ensembleMemberFormat != EnsembleMemberFormat.HIDE) {
writeOptionalElement("ensembleId", header.getEnsembleId());
writeOptionalElement(ensembleMemberFormat == EnsembleMemberFormat.INDEX
? "ensembleMemberIndex" : "ensembleMemberId", header.getEnsembleMemberId());
}
}
writeTimeStep(header);
writePeriod();
if (version.getIntId() >= PiVersion.VERSION_1_5.getIntId()) writeTime("forecastDate", header.getForecastTime(), 2);
if (version.getIntId() >= PiVersion.VERSION_1_20.getIntId()) writeTime("approvedDate", header.getApprovedTime(), 4);
INDEXPiSerializerUtils.writeElement(writer, ID"missVal", HIDE
timeSeriesContent.getDefaultMissingValue('.'));
}
writeOptionalElement("longName", piHeader.getLongName());
private EventDestination eventDestination = EventDestination.XML_EMBEDDED;
writeOptionalElement("stationName", header.getLocationName());
private PiVersionif (version.getIntId() >= PiVersion.VERSION_1_27.getIntId()) PiSerializerUtils.writeCoordinates(writer, header, version);
private EnsembleMemberFormat ensembleMemberFormat = EnsembleMemberFormat.INDEX;
writeOptionalElement("units", header.getUnit());
private FastDateFormat dateFormatfor (int i = 0, n = null;
private FastDateFormat timeFormat = null;
header.getDomainParameterCount(); i < n; i++) {
private TimeSeriesContent timeSeriesContent = null writeDomainAxis(header.getDomainParameterId(i), header.getDomainUnit(i));
private XMLStreamWriter writer = null;}
private VirtualOutputDir virtualOutputDir = null;
writeOptionalElement("sourceOrganisation", piHeader.getSourceOrganisation());
private OutputStream binaryOutputSteam = null;
writeOptionalElement("sourceSystem", piHeader.getSourceSystem());
private byte[] byteBuffer = null; writeOptionalElement("fileDescription", piHeader.getFileDescription());
private float[] floatBuffer = null;
private int bufferPos = 0;
if (header.getCreationTime() != Long.MIN_VALUE) {
private long[] cachedTimes = new long[4] updateDateTimeStringCache(header.getCreationTime(), 3);
private String[] cachedDateStrings = new String[4];
private String[] cachedTimeStrings = new String[4] PiSerializerUtils.writeElement(writer, "creationDate", cachedDateStrings[3]);
private int cachedSeconds = -1;
private String cachedSecondsString = null;
PiSerializerUtils.writeElement(writer, "creationTime", cachedTimeStrings[3]);
public PiTimeSeriesSerializer() {
}
public PiTimeSeriesSerializer(PiVersion version) {
writeOptionalElement("region", piHeader.getRegion());
if this.version = version;(header.getHighLevelThresholdCount() > 0) writeHighThresholds(header);
}
public EventDestination getEventDestinationif (version.getIntId() {
>= PiVersion.VERSION_1_16.getIntId() && timeSeriesContent.hasStatistics()) writeTimeSeriesStatistics();
return eventDestination;
}
if (version.getIntId() public void setEventDestination(EventDestination eventDestination) {>= PiVersion.VERSION_1_26.getIntId() && timeSeriesContent.getProduct() != null) writeProduct();
this.eventDestination = eventDestinationwriter.writeEndElement();
}
publicprivate PiVersionvoid getVersionwriteProduct() throws XMLStreamException {
Product product return version= timeSeriesContent.getProduct();
}
public void setVersion(PiVersion version) {writer.writeStartElement("product");
if (version == null)writer.writeAttribute("id", product.getId());
throw new IllegalArgumentException("version == null"writer.writeAttribute("name", product.getName());
this.version = version;writeTime("productDate", product.getProductTime(), 5);
}
public EnsembleMemberFormat getEnsembleMemberFormat() {writer.writeEmptyElement("category");
return ensembleMemberFormatwriter.writeAttribute("id", product.getCategory().getId());
}
public void setEnsembleMemberFormat(EnsembleMemberFormat ensembleMemberFormat) {writer.writeAttribute("name", product.getCategory().getName());
if (ensembleMemberFormat =product.getProductInfos() != null && product.getProductInfos().length > 0) {
throwfor new IllegalArgumentException("ensembleMemberFormat == null");
this.ensembleMemberFormat = ensembleMemberFormat;
}
@Override
public void setVirtualOutputDir(VirtualOutputDir virtualOutputDir) {
(int i = 0; i < product.getProductInfos().length; i++) {
ProductInfo productInfo = product.getProductInfos()[i];
this.virtualOutputDir = virtualOutputDir;
}writer.writeStartElement("productInfo");
@Override
public void serialize(TimeSeriesContent timeSeriesContent, XMLStreamWriter streamWriter, String virtualFileName) throws Exception {
writer.writeStartElement("user");
this.timeSeriesContent = timeSeriesContent writer.writeCharacters(productInfo.getUser());
if (timeSeriesContent.getTimeSeriesCount() <=0) throw new IOException("No TimeSeries to be Written to " + virtualFileName);
writer.writeEndElement();
this.writer = streamWriter writer.writeStartElement("confidence");
if (ensembleMemberFormat == EnsembleMemberFormat.ID && version.getIntId() < PiVersion.VERSION_1_10.getIntIdwriter.writeCharacters(productInfo.getConfidence()) {
;
throw new IOException("ensembleMemberId not supported for pi version " + versionwriter.writeEndElement();
}
timeSeriesContentwriter.setRequireEnsembleMemberIndices(ensembleMemberFormat == EnsembleMemberFormat.INDEXwriteStartElement("classification");
if (!ObjectUtils.equals(timeZoneNoDst, this.timeSeriesContent.getDefaultTimeZone writer.writeCharacters(productInfo.getClassification())) {
;
Arrayswriter.fill(cachedTimes, Long.MIN_VALUEwriteEndElement();
timeZoneNoDst = timeSeriesContentwriter.getDefaultTimeZonewriteStartElement("comment");
}
if (timeZoneNoDst.useDaylightTimewriter.writeCharacters(productInfo.getComment()){;
//Make timeZone not use day light saving time (DST) because the timeZone field does not use DST therefore
writer.writeEndElement();
writer.writeEndElement(); // productInfo
// all values must also not use DST}
}
timeZoneNoDst = new SimpleTimeZone(timeZoneNoDst.getRawOffset(), timeZoneNoDst.getID(), 0, 0, 0, 0, 0, 0, 0, 0);
}
writer.writeEndElement(); // product
}
private void writeTimeSeriesStatistics() throws XMLStreamException {
Statistics dateFormattimeSeriesInfoStatistics = FastDateFormattimeSeriesContent.getInstance("yyyy-MM-dd", timeZoneNoDst, Locale.US, dateFormatgetStatistics();
timeFormatif = FastDateFormat.getInstance("HH:mm:ss", timeZoneNoDst, Locale.US, timeFormat)(timeSeriesInfoStatistics == null) return;
String binFileName = FileUtils.getPathWithOtherExtension(virtualFileName, "bin");
if (timeSeriesInfoStatistics.hasFirstValueTime()) writeTime("firstValueTime", timeSeriesInfoStatistics.getFirstValueTime(), 0);
if (virtualOutputDir != null) virtualOutputDir.delete(binFileName(timeSeriesInfoStatistics.hasLastValueTime()) writeTime("lastValueTime", timeSeriesInfoStatistics.getLastValueTime(), 1);
if (timeSeriesInfoStatistics.hasMaxValue()) PiSerializerUtils.writeElement(eventDestination == EventDestination.SEPARATE_BINARY_FILE) {
writer, "maxValue", timeSeriesInfoStatistics.getMaxValue('.'));
if (virtualOutputDir == null)(timeSeriesInfoStatistics.hasMinValue()) PiSerializerUtils.writeElement(writer, "minValue", timeSeriesInfoStatistics.getMinValue('.'));
if throw new IllegalStateException("virtualOutputDir == null"(timeSeriesInfoStatistics.hasValueCount()) PiSerializerUtils.writeElement(writer,"valueCount", Integer.toString(timeSeriesInfoStatistics.getValueCount()));
if (timeSeriesInfoStatistics.hasMaxWarningLevelName()) PiSerializerUtils.writeElement(writer, binaryOutputSteam = virtualOutputDir.getOutputStream(binFileName);
"maxWarningLevelName", timeSeriesInfoStatistics.getMaxWarningLevelName());
}
private void if (byteBuffer == null)writeHighThresholds(TimeSeriesHeader header) throws XMLStreamException {
byteBufferboolean thresholdsAvailable = new byte[BUFFER_SIZE * NumberType.FLOAT_SIZE]false;
floatBuffer = new float[BUFFER_SIZE];boolean firstThreshold = true;
for (int i = }
0; i < try header.getHighLevelThresholdCount(); i++) {
TimeSeriesHeader.Threshold threshold = serializeheader.getHighLevelThreshold(i);
}String id finally {= threshold.getId();
String name bufferPos = 0threshold.getName();
//Note: here value can be binaryOutputSteam.close();
}
NaN for LevelThresholdValues that have different values for different aggregationTimeSpans configured.
float value = returnthreshold.getValue();
}
if (Float.isNaN(value)) {
binaryOutputSteam = null;
serialize();
}continue;
private void serialize() throws Exception {
}
writer.writeStartDocument();
thresholdsAvailable writer.writeStartElement("TimeSeries");
= true;
if writer.setDefaultNamespace("http://www.wldelft.nl/fews/PI");(firstThreshold) {
writer.writeNamespace("", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instancewriteStartElement("thresholds");
// writer.writeAttribute("xmlns", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xsi:schemaLocation", PiSchemaLocations.get("pi_timeseries.xsd"));
firstThreshold = false;
writer.writeAttribute("version", version.toString());
}
writeElement("timeZone", String.valueOf((double) timeSeriesContent.getDefaultTimeZone().getRawOffset() / (double) TimeUnit.HOUR_MILLIS));
if (version.getIntId() >= PiVersion.VERSION_1_14.getIntId()) {
for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) {
String label = null;
timeSeriesContent.setTimeSeriesIndex(i);
String comment = null;
writer.writeStartElement("series");
String description = writeHeader()null;
writeEvents();
if (version.getIntId() writer.writeEndElement();>= PiVersion.VERSION_1_22.getIntId()){
}
writer.writeEndElement();
label = writerthreshold.writeEndDocumentgetLabel();
if (eventDestination == EventDestination.SEPARATE_BINARY_FILE) flushBinEvents();
}
private void writeHeader() throws Exception {
TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader();
PiTimeSeriesHeader piHeader = header instanceof PiTimeSeriesHeader ? (PiTimeSeriesHeader) header : new PiTimeSeriesHeader();
if (version.getIntId() >= PiVersion.VERSION_1_27.getIntId()){
writer.writeStartElement("header");
writeElement("type", header.getParameterType() == null ? "instantaneous" : header.getParameterType().getName());
comment = threshold.getComment();
writeElement("locationId", header.getLocationId() == null ? "unknown"description := headerthreshold.getLocationIdgetDescription());
writeElement("parameterId", header.getParameterId() == null ? "unknown" : header.getParameterId());
}
if (versionthreshold.getIntIdgetGroupCount() >= PiVersion.VERSION_1_4.getIntId() 0) {
for (int ij = 0, nm = headerthreshold.getQualifierCountgetGroupCount(); ij < nm; ij++) {
writeElement("qualifierId", header.getQualifierId(i)String groupId = threshold.getGroupId(j);
String groupName = threshold.getGroupName(j);
}
}
if (versionthreshold.getIntIdgetGroupCount() >== PiVersion.VERSION_1_4.getIntId() && header.getEnsembleId()groupId != null && !header.getEnsembleId().equalsgroupId.equalsIgnoreCase("mainDeprecatedThresholdsConfigured")) {
if (ensembleMemberFormat != EnsembleMemberFormat.HIDE) {
groupId = null;
writeOptionalElement("ensembleId", header.getEnsembleId());
groupName = null;
writeOptionalElement(ensembleMemberFormat == EnsembleMemberFormat.INDEX
}
? "ensembleMemberIndex" : "ensembleMemberId", header.getEnsembleMemberId());
writeThreshold(id, name, value, groupId, groupName, label, description, }comment);
}
writeTimeStep(header);
}
writePeriod();
if (version.getIntId() >= PiVersion.VERSION_1_5.getIntId()) writeTime("forecastDate", header.getForecastTime(), 2);
} else {
writeElement("missVal", Float.toString(timeSeriesContent.getDefaultMissingValue()));
writeOptionalElementwriteThreshold("longName"id, piHeader.getLongName());
writeOptionalElement("stationName", header.getLocationName());
if (version.getIntId() >= PiVersion.VERSION_1_7.getIntId()) writeCoordinates(header);
name, value, null, null, label, description, comment);
writeOptionalElement("units", header.getUnit());
}
writeOptionalElement("sourceOrganisation", piHeader.getSourceOrganisation());
} else {
writeOptionalElement("sourceSystem", piHeader.getSourceSystem());
writeOptionalElement("fileDescription", piHeader.getFileDescription());
if (header.getCreationTime() != Long.MIN_VALUE) {
writeThreshold(id, name, value, null, null, null, null, null);
}
updateDateTimeStringCache(header.getCreationTime(), 3);
}
if writeElement("creationDate", cachedDateStrings[3]);(thresholdsAvailable) {
writeElement("creationTime", cachedTimeStrings[3]writer.writeEndElement();
}
}
writeOptionalElement("region", piHeader.getRegion());
if (header.getHighLevelThresholdCount() > 0) {
private void writeThreshold(String id, String name, float value, String groupId, String groupName, String label, String description, String comment) throws XMLStreamException {
writeHighThresholds(header);
}
writer.writeStartElement("highLevelThreshold");
writer.writeEndElement(writeAttribute("id", id);
}
private void writeProperties(Properties properties) throws XMLStreamException {writer.writeAttribute("name", name);
if (version.getIntId(label != null) < PiVersion.VERSION_1_13.getIntId()) returnwriter.writeAttribute("label", label);
if (properties.isEmpty()) {description != null) writer.writeAttribute("description", description);
if (comment != null) writer.writeEmptyElementwriteAttribute("propertiescomment", comment);
writer.writeAttribute("value", Float.toString(value));
// next events don't have properties
if (groupId != null) returnwriter.writeAttribute("groupId", groupId);
}
if (groupName != null) writer.writeStartElementwriteAttribute("propertiesgroupName", groupName);
for (int i = 0, n = properties.size(); i < n; i++) {
writer.writeEndElement();
}
private void writePeriod() throws XMLStreamException {
StringTimeStep keytimeStep = properties.getKey(itimeSeriesContent.getTimeSeriesHeader().getTimeStep();
PropertyType typePeriod period = propertiestimeSeriesContent.getTypegetTimeSeriesPeriod(i);
Period headerPeriod;
switch (type) {
if (period == Period.NEVER) {
case STRING:
// create a dummy period
long now = writer.writeEmptyElement("string"timeStep.nearestTime(System.currentTimeMillis());
headerPeriod = writer.writeAttribute("key", keynew Period(now, now);
} else {
writer.writeAttribute("value", properties.getString(i))headerPeriod = period;
}
writeTime("startDate", continueheaderPeriod.getStartTime(), 0);
writeTime("endDate", headerPeriod.getEndTime(), 1);
case INT:}
private void updateDateTimeStringCache(long time, int cacheIndex) {
if writer.writeEmptyElement("int")(cachedTimes[cacheIndex] == time) return;
cachedTimes[cacheIndex] = time;
cachedDateStrings[cacheIndex] = writerdateFormat.writeAttribute("key", keyformat(time);
cachedTimeStrings[cacheIndex] writer.writeAttribute("value", TextUtils.toString(properties.getInt(i))= timeFormat.format(time);
}
private void writeTime(String name, long time, int cacheIndex) throws XMLStreamException {
continue;
if (time == Long.MIN_VALUE) return;
case FLOAT:writer.writeEmptyElement(name);
updateDateTimeStringCache(time, cacheIndex);
writer.writeEmptyElementwriteAttribute("float"date", cachedDateStrings[cacheIndex]);
writer.writeAttribute("keytime", keycachedTimeStrings[cacheIndex]);
}
private void writeTimeStep(TimeSeriesHeader header) throws XMLStreamException {
writer.writeAttributewriteEmptyElement("value", Float.toString(properties.getInt(i)))timeStep");
TimeStep timeStep continue;= header.getTimeStep();
// todo add support for month time step
case DOUBLE:
if (timeStep.isEquidistantMillis()) {
writer.writeEmptyElementwriteAttribute("float"unit", "second");
int seconds = writer.writeAttribute("key", key(int) (timeStep.getStepMillis() / 1000);
if (cachedSeconds != seconds) {
writer.writeAttribute("value", Double.toString(properties.getDouble(i)));
cachedSecondsString = TextUtils.toString(seconds);
continue;
cachedSeconds = seconds;
case BOOLEAN:
}
writer.writeEmptyElementwriteAttribute("boolmultiplier", cachedSecondsString);
} else if (timeStep instanceof TimesOfDayDaylightSavingTimeStep && version.getIntId() >= PiVersion.VERSION_1_18.getIntId()) {
writer.writeAttribute("keytimes", key PiCastorUtils.getTimesOfDayString((TimesOfDayDaylightSavingTimeStep) timeStep));
} else if (timeStep instanceof TimesOfDayTimeStep writer.writeAttribute("value", Boolean.toString(properties.getBool(i)));&& version.getIntId() >= PiVersion.VERSION_1_18.getIntId()) {
writer.writeAttribute("times", continuePiCastorUtils.getTimesOfDayString((TimesOfDayTimeStep) timeStep));
} else if (timeStep instanceof TimesOfHourTimeStep && case DATE_TIME:version.getIntId() >= PiVersion.VERSION_1_24.getIntId()) {
writer.writeEmptyElementwriteAttribute("dateTime"minutes", PiCastorUtils.getTimesOfHourString((TimesOfHourTimeStep) timeStep));
} else {
writer.writeAttribute("keyunit", key"nonequidistant");
}
}
private long dateTime = properties.getDateTime(i);
void writeEvents() throws Exception {
switch (eventDestination) {
writer.writeAttribute("date", dateFormat.format(dateTime));
case ONLY_HEADERS:
writer.writeAttribute("time", timeFormat.format(dateTime)) return;
}case SEPARATE_BINARY_FILE:
}
writer.writeEndElementwriteBinEvents();
}
private void writeHighThresholds(TimeSeriesHeader header) throws XMLStreamException {
boolean thresholdsAvailable = falsereturn;
boolean firstThreshold = true;
case XML_EMBEDDED:
for (int i = 0; i < header.getHighLevelThresholdCountwriteXmlEvents();
i++) {
}
}
String idprivate =void header.getHighLevelThresholdIdwriteBinEvents(i);) throws Exception {
for (int i = String0, namen = headertimeSeriesContent.getHighLevelThresholdNamegetContentTimeCount(i);
i < //Note: here value can be NaN for LevelThresholdValues that have different values for different aggregationTimeSpans configured.n; i++) {
timeSeriesContent.setContentTimeIndex(i);
float value = header.getHighLevelThresholdValue(i)if (!timeSeriesContent.isTimeAvailable()) continue;
if (Float.isNaN(value)) {bufferPos == BUFFER_SIZE) flushBinEvents();
floatBuffer[bufferPos++] continue= timeSeriesContent.getValue();
}
}
private void flushBinEvents() throws IOException {
if thresholdsAvailable(bufferPos == true0) return;
BinaryUtils.copy(floatBuffer, 0, if (firstThreshold) {
bufferPos, byteBuffer, 0, bufferPos * NumberType.FLOAT_SIZE, ByteOrder.LITTLE_ENDIAN);
binaryOutputSteam.write(byteBuffer, 0, bufferPos * writer.writeStartElement("thresholds"NumberType.FLOAT_SIZE);
bufferPos = 0;
}
private void writeXmlEvents() firstThresholdthrows =XMLStreamException false;{
Properties properties }= Properties.NONE;
TimeSeriesHeader header = writertimeSeriesContent.writeStartElementgetTimeSeriesHeader("highLevelThreshold");
writer.writeAttribute("id", id)domainAxesValues = null;
int domainParameterCount = writerheader.writeAttribute("name", namegetDomainParameterCount();
float[] floatBuffer = writer.writeAttribute("value", Float.toString(value))this.floatBuffer;
for (int i = if (version.getIntId() >= PiVersion.VERSION_1_14.getIntId()) {
0, n = timeSeriesContent.getContentTimeCount(); i < n; i++) {
String groupId = header.getHighLevelThresholdGroupIdtimeSeriesContent.setContentTimeIndex(i);
if (groupId != null){!timeSeriesContent.isTimeAvailable()) continue;
Properties newProperties writer.writeAttribute("groupId", groupId= timeSeriesContent.getProperties();
if (!newProperties.equals(properties)) {
String groupName = header.getHighLevelThresholdGroupName(i);
properties = newProperties;
if (groupName != null) writerPiSerializerUtils.writeAttribute("groupName", groupNamewriteProperties(properties, version, writer, dateFormat, timeFormat);
}
}
writer.writeEndElementboolean valueMissing = timeSeriesContent.isValueMissing();
}
if (domainParameterCount > 0 if (thresholdsAvailable&& !valueMissing) {
int valueCount = writer.writeEndElementwriteAxisValues();
}
}
if (floatBuffer == privatenull void writePeriod() throws XMLStreamException|| floatBuffer.length != valueCount) {
TimeStep timeStep = timeSeriesContent.getTimeSeriesHeader().getTimeStep();
Period periodfloatBuffer = timeSeriesContent.getTimeSeriesPeriod()new float[valueCount];
Period headerPeriod;
if (period == Period.NEVER) {this.floatBuffer = floatBuffer;
// create a dummy period}
long now = timeSteptimeSeriesContent.nearestTime(System.currentTimeMillis()readValues(floatBuffer);
headerPeriod = new Period(now, now);
}
if (domainParameterCount == 0 }|| elsevalueMissing) {
headerPeriod = period writer.writeEmptyElement("event");
}
else {
writeTime("startDate", headerPeriod.getStartTime(), 0);
writeTime("endDate", headerPeriod.getEndTime(), 1);
}
writer.writeStartElement("event");
private void writeCoordinates(TimeSeriesHeader header) throws XMLStreamException {}
Geometry geometry = header.getGeometrywriteTime();
if (geometrydomainParameterCount == null0) returnwriteValue();
GeoPoint geoPoint = GeometryUtilsPiSerializerUtils.getPoint(geometrywriteFlagsUserComment(writer, timeSeriesContent, 0version);
writeElement("lat", Double.toString(geoPoint.getLatitude()));
if (domainParameterCount == writeElement("lon", Double.toString(geoPoint.getLongitude()))0 || valueMissing) continue;
writeElement("x", Double.toString(geoPoint.getX()));
writeElement("y", Double.toString(geoPoint.getY())) int valuesPerRow = domainParameterCount == 1 ? 1 : domainAxesValues[1].length;
writeElement("z", Double.toString(geoPoint.getZ()));
}
writeValues(floatBuffer, valuesPerRow);
private void updateDateTimeStringCache(long time, int cacheIndex) {writer.writeEndElement();
if (cachedTimes[cacheIndex] == time) return;}
}
private void cachedTimes[cacheIndex] = time;writeValue() throws XMLStreamException {
cachedDateStrings[cacheIndex]String stringValue = dateFormattimeSeriesContent.formatgetValue(time'.');
cachedTimeStrings[cacheIndex] = timeFormat.format(timewriter.writeAttribute("value", stringValue);
}
private void writeTime(String name, long time, int cacheIndex) throws XMLStreamException {if (version.ordinal() >= PiVersion.VERSION_1_31.ordinal() && timeSeriesContent.getValueSource() == ValueSource.MANUAL) writer.writeAttribute("valueSource", "MAN");
if (version.ordinal(time) ==< LongPiVersion.MIN_VALUEVERSION_1_23.ordinal()) return;
writer.writeEmptyElement(name);
if (timeSeriesContent.isValueMissing()) return;
float value = updateDateTimeStringCache(time, cacheIndextimeSeriesContent.getValue();
if (timeSeriesContent.getMinValue() != value) writer.writeAttribute("dateminValue", cachedDateStrings[cacheIndex]timeSeriesContent.getMinValue('.'));
if (timeSeriesContent.getMaxValue() != value) writer.writeAttribute("timemaxValue", cachedTimeStrings[cacheIndex]timeSeriesContent.getMaxValue('.'));
}
private void writeTimeStep(TimeSeriesHeader header) throws XMLStreamException {
writer.writeEmptyElement("timeStep");
TimeStep timeStep = header.getTimeStep();
if (version.ordinal() >= PiVersion.VERSION_1_29.ordinal() && !"".equals(timeSeriesContent.getDetectionSymbol())) writer.writeAttribute("detection", timeSeriesContent.getDetectionSymbol());
}
//private todovoid addwriteTime() supportthrows forXMLStreamException month{
time step
long time if= (timeSteptimeSeriesContent.isEquidistantMillisgetTime()) {
;
String dateText = dateFormat.format(time);
writer.writeAttribute("unitdate", "second"dateText);
int secondsString timeText = (int) (timeStep.getStepMillis() / 1000)timeFormat.format(time);
writer.writeAttribute("time", timeText);
if (cachedSeconds != seconds) {
if (version.ordinal() < PiVersion.VERSION_1_23.ordinal()) return;
long cachedSecondsStringstartTime = TextUtilstimeSeriesContent.toStringgetRangeStartTime(seconds);
cachedSecondslong endTime = secondstimeSeriesContent.getRangeEndTime();
writeAttribute("startDate", dateFormat, startTime, time, }
dateText);
writer.writeAttribute("multiplier"startTime", timeFormat, startTime, time, cachedSecondsStringtimeText);
writeAttribute("endDate", dateFormat, }endTime, else {
time, dateText);
writer.writeAttribute("unitendTime" , "nonequidistant");
}timeFormat, endTime, time, timeText);
}
private void writeEventswriteAttribute()String throwsattributeName, ExceptionFastDateFormat {
dateFormat, long time, long defaultTime, String defaultText) throws switchXMLStreamException (eventDestination) {
if (time == case ONLY_HEADERS:defaultTime) return;
String text = dateFormat.format(time);
return;
if (TextUtils.equals(text, defaultText)) return;
case SEPARATE_BINARY_FILE:
writer.writeAttribute(attributeName, text);
}
private int writeBinEventswriteAxisValues();
throws XMLStreamException {
TimeSeriesHeader header return= timeSeriesContent.getTimeSeriesHeader();
if (domainAxesValues == null) domainAxesValues = case XML_EMBEDDED:new float[header.getDomainParameterCount()][];
if (newDomainValues == null) newDomainValues = writeXmlEventsnew float[header.getDomainParameterCount()][];
}
}
assert !timeSeriesContent.isValueMissing();
private void writeBinEvents()int throwsres Exception= {1;
for (int i = 0, n = timeSeriesContentheader.getContentTimeCountgetDomainParameterCount(); i < n; i++) {
float[] values timeSeriesContent.setContentTimeIndex(i)= newDomainValues[i];
int valueCount if= (!timeSeriesContent.isTimeAvailablegetDomainAxisValueCount(i)) continue;
ifres (bufferPos *== BUFFER_SIZE) flushBinEvents() valueCount;
floatBuffer[bufferPos++] = timeSeriesContent.getValue();
}
}
private void flushBinEvents() throws IOException {
if (values == null || values.length != valueCount) {
if (bufferPosvalues == 0) returnnew float[valueCount];
BinaryUtils.copy(floatBuffer, 0, bufferPos, byteBuffer, 0, bufferPos * NumberType.FLOAT_SIZE, ByteOrder.LITTLE_ENDIAN)newDomainValues[i] = values;
binaryOutputSteam.write(byteBuffer, 0, bufferPos * NumberType.FLOAT_SIZE); }
bufferPos = 0;
}
private void writeXmlEvents() throws XMLStreamException {
timeSeriesContent.readDomainAxisValues(i, values);
Properties properties = Properties.NONEif (Arrays.equals(values, domainAxesValues[i])) continue;
for (int i = 0, n = timeSeriesContent.getContentTimeCount(); i < n; i++) {
writer.writeStartElement("domainAxisValues");
timeSeriesContent.setContentTimeIndex(iwriter.writeAttribute("parameterId", header.getDomainParameterId(i));
int valuePerRow = i == 0 ? 1 if: (!timeSeriesContent.isTimeAvailable()) continuevalues.length;
Properties newProperties = timeSeriesContent.getProperties(writeValues(values, valuePerRow);
if (!newProperties.equals(properties)) {domainAxesValues[i] = Clasz.floats.copyOfArray(values);
properties = newPropertieswriter.writeEndElement();
}
return writeProperties(properties)res;
}
private void writeValues(float[] values, }
int valuesPerRow) throws XMLStreamException {
long time = assert !timeSeriesContent.getTimeisValueMissing();
char[] buffer = writer.writeEmptyElement("event")this.charBuffer;
if (buffer == writer.writeAttribute("date", dateFormat.format(time));
null) {
buffer writer.writeAttribute("time", timeFormat.format(time))= new char[15];
writer.writeAttribute("value", timeSeriesContent.getValue('.'))this.charBuffer = buffer;
writer.writeAttribute("flag", timeSeriesContent.getStringFlag());}
for (int i if (version.getIntId() >= PiVersion.VERSION_1_11.getIntId()= 0; i < values.length; i++) {
buffer[0] = i % StringvaluesPerRow flagSource == timeSeriesContent.getFlagSource();
0 ? '\n' : '\t';
iffloat (flagSourcevalue != null) writer.writeAttribute("flagSource", flagSource)values[i];
int }endPos;
if (version.getIntId() >= PiVersion.VERSION_1_3.getIntId(MathUtils.equals(value, missingValue)) {
String comment = timeSeriesContent.getComment(missingValueText.getChars(0, missingValueText.length(), buffer, 1);
ifendPos (comment != nullmissingValueText.length() writer.writeAttribute("comment", comment)+ 1;
}
else {
if (version.getIntId() >= PiVersion.VERSION_1_10.getIntId()) {
endPos = TextUtils.format(buffer, 1, String user = timeSeriesContent.getUser();
value, '.', 0, 10);
}
if (user != null) writer.writeAttribute("user"writeCharacters(buffer, 0, userendPos);
// start with space
}
writer.writeCharacters("\n }");
}
private void writeOptionalElement(String elementName, String s) throws XMLStreamException {
if (s == null) return;
if (s.trim().isEmpty()) return;
PiSerializerUtils.writeElement(writer, elementName, s);
}
private void writeElementwriteDomainAxis(String nameparameterId, String valueunit) throws XMLStreamException {
writer.writeStartElementwriteEmptyElement(name"domainAxis");
writer.writeCharacters(valuewriteAttribute("parameterId", parameterId);
if (unit != null) writer.writeEndElement(writeAttribute("units", unit);
}
}
|