/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package nl.wldelft.timeseriesserializers; import com.sun.org.apache.xml.internal.serialize.OutputFormat; import com.sun.org.apache.xml.internal.serialize.XMLSerializer; import nl.wldelft.util.TextUtils; import nl.wldelft.util.coverage.Geometry; 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 org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import java.text.SimpleDateFormat; public class UMAquo2009TimeSeriesSerializer implements TextSerializer { private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); private TimeSeriesContent timeSeriesContent = null; private LineWriter writer = null; private ContentHandler xmlContentHandler = null; private final AttributesImpl attributesBuffer = new AttributesImpl(); /** * Serialize time series * * @param timeSeriesContent * @param writer * @param virtualFileName * @throws Exception */ @Override public void serialize(TimeSeriesContent timeSeriesContent, LineWriter writer, String virtualFileName) throws Exception { this.timeSeriesContent = timeSeriesContent; this.writer = writer; if (Float.isNaN(timeSeriesContent.getDefaultMissingValue())) { timeSeriesContent.setMissingValue(-999f); } timeSeriesContent.setUnreliablesAsMissings(true); dateFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone()); timeFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone()); serialize(); } /** * Serialize * @throws Exception */ private void serialize() throws Exception { XMLSerializer serializer = new XMLSerializer(); OutputFormat of = new OutputFormat("XML", "UTF-8", true); serializer.setOutputFormat(of); serializer.setOutputCharStream(writer); xmlContentHandler = serializer.asContentHandler(); xmlContentHandler.startDocument(); attributesBuffer.clear(); addAttribute("xmlns:umam", "http://www.idsw.nl/umam2009"); addAttribute("xmlns:gml", "http://www.opengis.net/gml"); addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); addAttribute("xsi:schemaLocation", "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/base/gml.xsd http://www.idsw.nl/umam2009 http://www.idsw.nl/Aquo/schemas/uma2009.xsd"); xmlContentHandler.startElement("", "gml:FeatureCollection", "gml:FeatureCollection", attributesBuffer); // Determine envelope of points writeEnvelope(); // Export all time series for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) { timeSeriesContent.setTimeSeriesIndex(i); if (!timeSeriesContent.isTimeSeriesEmpty()) { TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader(); String meetobjectId = writeMeetobject(header); writeMonsterObject(header, meetobjectId); writeWaardeReeksTijd(header, meetobjectId); } } xmlContentHandler.endElement(null, null, "gml:FeatureCollection"); xmlContentHandler.endDocument(); } /** * Bepaal de maximum envelope van de punten */ private void writeEnvelope() throws Exception { int minX = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int maxY = Integer.MIN_VALUE; boolean ts = false; for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) { timeSeriesContent.setTimeSeriesIndex(i); if (!timeSeriesContent.isTimeSeriesEmpty()) { ts = true; TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader(); Geometry geometry = header.getGeometry(); int x = geometry == null ? 0 : (int) geometry.getX(0); int y = geometry == null ? 0 : (int) geometry.getY(0); if (x < minX) { minX = x; // new X minimum } if (x > maxX) { maxX = x; // new X maximum } if (y < minY) { minY = y; // new Y minimum } if (y > maxY) { maxY = y; // new Y maximum } } } // Vergroot envelope if (ts) { minX -= 100; minY -= 100; maxX += 100; maxY += 100; xmlContentHandler.startElement(null, null, "gml:boundedBy", null); attributesBuffer.clear(); addAttribute("srsName", "urn:ogc:def:crs:EPSG::28992"); addAttribute("srsDimension", "2"); xmlContentHandler.startElement(null, null, "gml:Envelope", attributesBuffer); writeElement("gml:lowerCorner", String.format("%d %d", minX, minY)); writeElement("gml:upperCorner", String.format("%d %d", maxX, maxY)); xmlContentHandler.endElement(null, null, "gml:Envelope"); xmlContentHandler.endElement(null, null, "gml:boundedBy"); } } /** * Aanduiding van een fysieke plaats of gebied waar een meting is/wordt verricht * * @param header * @return * @throws Exception */ private String writeMeetobject(TimeSeriesHeader header) throws Exception { xmlContentHandler.startElement(null, null, "gml:featureMember", null); attributesBuffer.clear(); if (header.getQualifierCount() == 0) { throw new Exception("externalQualifiers not set"); } String[] clientParts = TextUtils.split(header.getQualifierId(3), ';'); String[] locationIdParts = TextUtils.split(header.getLocationId(), ';'); String id = String.format("%s.umam.%s.%s", clientParts[0], clientParts[1], locationIdParts[0]); addAttribute("gml:id", id); xmlContentHandler.startElement(null, null, "umam:MeetObject", attributesBuffer); writeElement("umam:identificatie", id); xmlContentHandler.startElement(null, null, "umam:locatie", null); xmlContentHandler.startElement(null, null, "umam:LocatieNaam", null); writeElement("umam:locatieNaam", header.getLocationName()); xmlContentHandler.endElement(null, null, "umam:LocatieNaam"); xmlContentHandler.endElement(null, null, "umam:locatie"); xmlContentHandler.startElement(null, null, "umam:geometriePunt", null); xmlContentHandler.startElement(null, null, "gml:Point", null); Geometry geometry = header.getGeometry(); int x = geometry == null ? 0 : (int) geometry.getX(0); int y = geometry == null ? 0 : (int) geometry.getY(0); writeElement("gml:pos", String.format("%d %d", x, y)); xmlContentHandler.endElement(null, null, "gml:Point"); xmlContentHandler.endElement(null, null, "umam:geometriePunt"); // Altijd maar 1 mosterobject attributesBuffer.clear(); addAttribute("xlink:type", "simple"); addAttribute("xlink:href", String.format("#%s.%d", id, 1)); xmlContentHandler.startElement(null, null, "umam:heeftMonsterObject", attributesBuffer); xmlContentHandler.endElement(null, null, "umam:heeftMonsterObject"); xmlContentHandler.endElement(null, null, "umam:MeetObject"); xmlContentHandler.endElement(null, null, "gml:featureMember"); return id; } /** * Dat deel van de fysieke werkelijkheid dat wordt beschouwd of geanalyseerd * * @param id * @throws Exception */ private void writeMonsterObject(TimeSeriesHeader header, String meetobjectid) throws Exception { attributesBuffer.clear(); xmlContentHandler.startElement(null, null, "gml:featureMember", null); addAttribute("gml:id", String.format("%s.1", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:MonsterObject", attributesBuffer); writeElement("umam:identificatie", String.format("%s.1", meetobjectid)); writeElement("umam:compartiment", header.getQualifierId(2)); attributesBuffer.clear(); addAttribute("xlink:type", "simple"); addAttribute("xlink:href", String.format("#%s.1.1", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:heeftWaardeReeks", attributesBuffer); xmlContentHandler.endElement(null, null, "umam:heeftWaardeReeks"); attributesBuffer.clear(); addAttribute("xlink:type", "simple"); addAttribute("xlink:href", String.format("#%s", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:hoortBijMeetObject", attributesBuffer); xmlContentHandler.endElement(null, null, "umam:hoortBijMeetObject"); xmlContentHandler.endElement(null, null, "umam:MonsterObject"); xmlContentHandler.endElement(null, null, "gml:featureMember"); } /** * Reeks van uitkomsten van een meting of toetsing van een grootheid en/of parameter variërend in tijd * * @param header * @param meetobjectid * @throws Exception */ private void writeWaardeReeksTijd(TimeSeriesHeader header, String meetobjectid) throws Exception { attributesBuffer.clear(); xmlContentHandler.startElement(null, null, "gml:featureMember", null); addAttribute("gml:id", String.format("%s.1.1", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:WaardeReeksTijd", attributesBuffer); writeElement("umam:identificatie", String.format("%s.1.1", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:kwaliteitsElementOfParameter", null); xmlContentHandler.startElement(null, null, "umam:ParameterTyperingDataType", null); xmlContentHandler.startElement(null, null, "umam:parameterGrootheid", null); xmlContentHandler.startElement(null, null, "umam:ParameterGrootheidDataType", null); writeElement("umam:grootheid", header.getParameterId()); xmlContentHandler.endElement(null, null, "umam:ParameterGrootheidDataType"); xmlContentHandler.endElement(null, null, "umam:parameterGrootheid"); xmlContentHandler.endElement(null, null, "umam:ParameterTyperingDataType"); xmlContentHandler.endElement(null, null, "umam:kwaliteitsElementOfParameter"); writeElement("umam:eenheid", header.getQualifierId(0)); writeElement("umam:hoedanigheid", header.getQualifierId(1)); // waardeBewerkingsMethode //writeElement("umam:waardeBewerkingsMethode", "NVT;Niet van toepassing"); // waardeBepalingsMethode //writeElement("umam:waardeBepalingsMethode", "NVT;Niet van toepassing"); attributesBuffer.clear(); addAttribute("xlink:type", "simple"); addAttribute("xlink:href", String.format("#%s.1", meetobjectid)); xmlContentHandler.startElement(null, null, "umam:hoortBijMonsterObject", attributesBuffer); xmlContentHandler.endElement(null, null, "umam:hoortBijMonsterObject"); writeEvents(); xmlContentHandler.endElement(null, null, "umam:WaardeReeksTijd"); xmlContentHandler.endElement(null, null, "gml:featureMember"); } /** * Lus over alle tijdstippen * * @throws Exception */ private void writeEvents() throws Exception { for (int i = 0, n = timeSeriesContent.getContentTimeCount(); i < n; i++) { timeSeriesContent.setContentTimeIndex(i); if (!timeSeriesContent.isTimeAvailable()) { continue; } writeEvent(timeSeriesContent.getTime()); } } /** * Uitkomst van een meting of toetsing van een grootheid en/of parameter op tijdstip * * @param time * @throws Exception */ private void writeEvent(long time) throws Exception { xmlContentHandler.startElement(null, null, "umam:reekswaarde", null); xmlContentHandler.startElement(null, null, "umam:TijdWaarde", null); xmlContentHandler.startElement(null, null, "umam:beginTijd", null); xmlContentHandler.startElement(null, null, "umam:DatumTijdDataType", null); writeElement("umam:datum", dateFormat.format(time)); writeElement("umam:tijd", timeFormat.format(time)); xmlContentHandler.endElement(null, null, "umam:DatumTijdDataType"); xmlContentHandler.endElement(null, null, "umam:beginTijd"); xmlContentHandler.startElement(null, null, "umam:numeriekeWaarde", null); xmlContentHandler.startElement(null, null, "umam:WaardeDataType", null); writeElement("umam:getalswaarde", timeSeriesContent.getValue('.')); xmlContentHandler.endElement(null, null, "umam:WaardeDataType"); xmlContentHandler.endElement(null, null, "umam:numeriekeWaarde"); writeElement("umam:kwaliteitsOordeel", getKwaliteitsOordeel(timeSeriesContent.getFlag())); xmlContentHandler.endElement(null, null, "umam:TijdWaarde"); xmlContentHandler.endElement(null, null, "umam:reekswaarde"); } /** * Bepaal kwaliteitsoordeel * * see : http://www.idsw.nl/Aquo/schemas/Aquo-domein_kwaliteitsoordeel.xsd * * @param flag * @return */ private String getKwaliteitsOordeel(int flag) { String kwaliteitsOordeel = ""; switch (flag) { case 0: kwaliteitsOordeel = "00;Normale waarde"; break; case 3: kwaliteitsOordeel = "03;Waarde heeft een grotere spreiding dan beschreven"; break; case 4: kwaliteitsOordeel = "04;Bepaald met hele detectiegrens"; break; case 5: kwaliteitsOordeel = "05;Bepaald met halve detectiegrens"; break; case 6: kwaliteitsOordeel = "06;Bepaald met nul waarde voor detectiegrens"; break; case 10: kwaliteitsOordeel = "10;In de ruimte geïnterpoleerde waarde"; break; case 20: kwaliteitsOordeel = "20;In de tijd geïnterpoleerde waarde"; break; case 25: kwaliteitsOordeel = "25;In ruimte en tijd geïnterpoleerde waarde"; break; case 30: kwaliteitsOordeel = "30;Gebaggerd"; break; case 50: kwaliteitsOordeel = "50;Niet-plausibele waarde"; break; case 98: kwaliteitsOordeel = "98;Waarde bepaald op onvolledige basis"; break; case 99: kwaliteitsOordeel = "99;Hiaat waarde"; break; default: kwaliteitsOordeel = "99;Hiaat waarde"; break; } return kwaliteitsOordeel; } /** * write xml element * * @param name * @param value * @throws org.xml.sax.SAXException */ private void writeElement(String name, String value) throws SAXException { xmlContentHandler.startElement(null, null, name, null); xmlContentHandler.characters(value.toCharArray(), 0, value.length()); xmlContentHandler.endElement(null, null, name); } /** * write xml attributes * * @param name * @throws org.xml.sax.SAXException */ private void writeAttributes(String name) throws SAXException { xmlContentHandler.startElement("", name, name, attributesBuffer); xmlContentHandler.endElement(null, null, name); } /** * Add attribute to the attribute buffer * * @param name * @param value */ private void addAttribute(String name, String value) { attributesBuffer.addAttribute("", name, name, "CDATA", value); } }