/** * Copyright (C) 2012-2017 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * License version 2 and the aforementioned licenses. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. */ package org.n52.sos.encode; import java.io.OutputStream; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.stream.XMLStreamException; import org.apache.xmlbeans.XmlObject; import org.n52.sos.encode.streaming.WmlTVPEncoderv20XmlStreamWriter; import org.n52.sos.exception.ows.NoApplicableCodeException; import org.n52.sos.exception.ows.concrete.UnsupportedEncoderInputException; import org.n52.sos.ogc.om.AbstractObservationValue; import org.n52.sos.ogc.om.MultiObservationValues; import org.n52.sos.ogc.om.ObservationValue; import org.n52.sos.ogc.om.OmConstants; import org.n52.sos.ogc.om.OmObservableProperty; import org.n52.sos.ogc.om.OmObservation; import org.n52.sos.ogc.om.SingleObservationValue; import org.n52.sos.ogc.om.TimeValuePair; import org.n52.sos.ogc.om.values.CountValue; import org.n52.sos.ogc.om.values.QuantityValue; import org.n52.sos.ogc.om.values.TVPValue; import org.n52.sos.ogc.ows.OwsExceptionReport; import org.n52.sos.ogc.sos.Sos2Constants; import org.n52.sos.ogc.sos.SosConstants; import org.n52.sos.ogc.sos.SosConstants.HelperValues; import org.n52.sos.ogc.wml.ConformanceClassesWML2; import org.n52.sos.ogc.wml.WaterMLConstants; import org.n52.sos.response.GetObservationResponse; import org.n52.sos.service.ServiceConstants.SupportedTypeKey; import org.n52.sos.util.CodingHelper; import org.n52.sos.util.CollectionHelper; import org.n52.sos.util.StringHelper; import org.n52.sos.w3c.SchemaLocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.collect.Sets; import net.opengis.om.x20.OMObservationType; import net.opengis.waterml.x20.DefaultTVPMeasurementMetadataDocument; import net.opengis.waterml.x20.MeasureTVPType; import net.opengis.waterml.x20.MeasurementTimeseriesDocument; import net.opengis.waterml.x20.MeasurementTimeseriesMetadataType; import net.opengis.waterml.x20.MeasurementTimeseriesType; import net.opengis.waterml.x20.TVPDefaultMetadataPropertyType; import net.opengis.waterml.x20.TVPMeasurementMetadataType; /** * Encoder class for WaterML 2.0 TimeseriesValuePair (TVP) * * @author Carsten Hollmann <c.hollmann@52north.org> * @since 4.0.0 * */ public class WmlTVPEncoderv20 extends AbstractWmlEncoderv20 { private static final Logger LOGGER = LoggerFactory.getLogger(WmlTVPEncoderv20.class); // TODO: change to correct conformance class private static final Set<String> CONFORMANCE_CLASSES = Sets.newHashSet( ConformanceClassesWML2.UML_MEASUREMENT_TIMESERIES_TVP_OBSERVATION, ConformanceClassesWML2.UML_TIMESERIES_TVP_OBSERVATION, ConformanceClassesWML2.UML_MEASUREMENT_TIMESERIES_TVP_OBSERVATION, ConformanceClassesWML2.XSD_XML_RULES, ConformanceClassesWML2.XSD_TIMESERIES_OBSERVATION, ConformanceClassesWML2.XSD_TIMESERIES_TVP_OBSERVATION, ConformanceClassesWML2.XSD_MEASUREMENT_TIMESERIES_TVP); private static final Set<EncoderKey> ENCODER_KEYS = createEncoderKeys(); private static final Map<SupportedTypeKey, Set<String>> SUPPORTED_TYPES = Collections.singletonMap(SupportedTypeKey.ObservationType, Collections.singleton(WaterMLConstants.OBSERVATION_TYPE_MEASURMENT_TVP));; private static final Map<String, Map<String, Set<String>>> SUPPORTED_RESPONSE_FORMATS = Collections.singletonMap( SosConstants.SOS, Collections.singletonMap(Sos2Constants.SERVICEVERSION, Collections.singleton(WaterMLConstants.NS_WML_20))); public WmlTVPEncoderv20() { LOGGER.debug("Encoder for the following keys initialized successfully: {}!", Joiner.on(", ").join(ENCODER_KEYS)); } @SuppressWarnings("unchecked") private static Set<EncoderKey> createEncoderKeys() { return CollectionHelper.union(getDefaultEncoderKeys(), CodingHelper.encoderKeysForElements(WaterMLConstants.NS_WML_20, GetObservationResponse.class, OmObservation.class, SingleObservationValue.class, MultiObservationValues.class)); } @Override public Set<EncoderKey> getEncoderKeyType() { return Collections.unmodifiableSet(ENCODER_KEYS); } @Override public Map<SupportedTypeKey, Set<String>> getSupportedTypes() { return Collections.unmodifiableMap(SUPPORTED_TYPES); } @Override public Set<String> getConformanceClasses() { return Collections.unmodifiableSet(CONFORMANCE_CLASSES); } @Override public Set<String> getSupportedResponseFormats(String service, String version) { if (SUPPORTED_RESPONSE_FORMATS.get(service) != null && SUPPORTED_RESPONSE_FORMATS.get(service).get(version) != null) { return SUPPORTED_RESPONSE_FORMATS.get(service).get(version); } return Collections.emptySet(); } @Override public Set<SchemaLocation> getSchemaLocations() { return Sets.newHashSet(WaterMLConstants.WML_20_SCHEMA_LOCATION, WaterMLConstants.WML_20_TS_SCHEMA_LOCATION); } @Override public boolean supportsResultStreamingForMergedValues() { return true; } @Override public XmlObject encode(Object element, Map<HelperValues, String> additionalValues) throws OwsExceptionReport, UnsupportedEncoderInputException { XmlObject encodedObject = null; if (element instanceof ObservationValue) { encodedObject = encodeResult((ObservationValue<?>) element); } else { encodedObject = super.encode(element, additionalValues); } return encodedObject; } @Override public void encode(Object objectToEncode, OutputStream outputStream, EncodingValues encodingValues) throws OwsExceptionReport { encodingValues.setEncoder(this); if (objectToEncode instanceof OmObservation) { try { new WmlTVPEncoderv20XmlStreamWriter().write((OmObservation) objectToEncode, outputStream, encodingValues); } catch (XMLStreamException xmlse) { throw new NoApplicableCodeException().causedBy(xmlse) .withMessage("Error while writing element to stream!"); } } else { super.encode(objectToEncode, outputStream, encodingValues); } } @Override protected XmlObject createResult(OmObservation sosObservation) throws OwsExceptionReport { return createMeasurementTimeseries(sosObservation); } @Override protected XmlObject encodeResult(ObservationValue<?> observationValue) throws OwsExceptionReport { return createMeasurementTimeseries((AbstractObservationValue<?>) observationValue); } @Override protected void addObservationType(OMObservationType xbObservation, String observationType) { if (StringHelper.isNotEmpty(observationType)) { if (observationType.equals(OmConstants.OBS_TYPE_MEASUREMENT) || observationType.equals(WaterMLConstants.OBSERVATION_TYPE_MEASURMENT_TVP)) { xbObservation.addNewType().setHref(WaterMLConstants.OBSERVATION_TYPE_MEASURMENT_TVP); } else if (observationType.equals(OmConstants.OBS_TYPE_CATEGORY_OBSERVATION) || observationType.equals(WaterMLConstants.OBSERVATION_TYPE_CATEGORICAL_TVP)) { xbObservation.addNewType().setHref(WaterMLConstants.OBSERVATION_TYPE_CATEGORICAL_TVP); } } } /** * Create a XML MeasurementTimeseries object from SOS observation for * om:result * * @param sosObservation * SOS observation * @return XML MeasurementTimeseries object * @throws OwsExceptionReport * If an error occurs */ private XmlObject createMeasurementTimeseries(OmObservation sosObservation) throws OwsExceptionReport { MeasurementTimeseriesDocument measurementTimeseriesDoc = MeasurementTimeseriesDocument.Factory.newInstance(); MeasurementTimeseriesType measurementTimeseries = measurementTimeseriesDoc.addNewMeasurementTimeseries(); measurementTimeseries.setId("timeseries." + sosObservation.getObservationID()); addTimeseriesMetadata(measurementTimeseries, sosObservation.getPhenomenonTime().getGmlId()); TVPDefaultMetadataPropertyType xbMetaComponent = measurementTimeseries.addNewDefaultPointMetadata(); DefaultTVPMeasurementMetadataDocument xbDefMeasureMetaComponent = DefaultTVPMeasurementMetadataDocument.Factory.newInstance(); TVPMeasurementMetadataType defaultTVPMeasurementMetadata = xbDefMeasureMetaComponent.addNewDefaultTVPMeasurementMetadata(); defaultTVPMeasurementMetadata.addNewInterpolationType() .setHref("http://www.opengis.net/def/timeseriesType/WaterML/2.0/continuous"); xbDefMeasureMetaComponent.getDefaultTVPMeasurementMetadata().getInterpolationType().setTitle("Instantaneous"); String unit = null; if (sosObservation.getValue() instanceof SingleObservationValue) { SingleObservationValue<?> singleObservationValue = (SingleObservationValue<?>) sosObservation.getValue(); String time = getTimeString(singleObservationValue.getPhenomenonTime()); unit = singleObservationValue.getValue().getUnit(); if (sosObservation.getValue().getValue() instanceof QuantityValue) { QuantityValue quantityValue = (QuantityValue) singleObservationValue.getValue(); if (!quantityValue.getValue().equals(Double.NaN)) { String value = Double.toString(quantityValue.getValue().doubleValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } else if (sosObservation.getValue().getValue() instanceof CountValue) { CountValue countValue = (CountValue) singleObservationValue.getValue(); if (countValue.getValue() != null) { String value = Integer.toString(countValue.getValue().intValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } } else if (sosObservation.getValue() instanceof MultiObservationValues) { MultiObservationValues<?> observationValue = (MultiObservationValues<?>) sosObservation.getValue(); TVPValue tvpValue = (TVPValue) observationValue.getValue(); List<TimeValuePair> timeValuePairs = tvpValue.getValue(); unit = tvpValue.getUnit(); for (TimeValuePair timeValuePair : timeValuePairs) { if (timeValuePair.getValue() instanceof QuantityValue) { QuantityValue quantityValue = (QuantityValue) timeValuePair.getValue(); if (!quantityValue.getValue().equals(Double.NaN)) { String time = getTimeString(timeValuePair.getTime()); String value = Double.toString(quantityValue.getValue().doubleValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } else if (timeValuePair.getValue() instanceof CountValue) { CountValue countValue = (CountValue) timeValuePair.getValue(); if (countValue.getValue() != null) { String time = getTimeString(timeValuePair.getTime()); String value = Integer.toString(countValue.getValue().intValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } } } // set uom if (unit != null && !unit.isEmpty()) { defaultTVPMeasurementMetadata.addNewUom().setCode(unit); } else { OmObservableProperty observableProperty = (OmObservableProperty) sosObservation.getObservationConstellation().getObservableProperty(); if (observableProperty.isSetUnit()) { defaultTVPMeasurementMetadata.addNewUom().setCode(observableProperty.getUnit()); } } xbMetaComponent.set(xbDefMeasureMetaComponent); return measurementTimeseriesDoc; } /** * Add a time an value to MeasureTVPType * * @param measurementTVP * MeasureTVPType XML object * @param time * Time a string * @param value * value as string */ private void addValuesToMeasurementTVP(MeasureTVPType measurementTVP, String time, String value) { measurementTVP.addNewTime().setStringValue(time); if (value != null && !value.isEmpty()) { measurementTVP.addNewValue().setStringValue(value); } else { measurementTVP.addNewValue().setNil(); measurementTVP.addNewMetadata().addNewTVPMeasurementMetadata().addNewNilReason().setNilReason("missing"); } } private XmlObject createMeasurementTimeseries(AbstractObservationValue<?> observationValue) throws OwsExceptionReport { MeasurementTimeseriesDocument measurementTimeseriesDoc = MeasurementTimeseriesDocument.Factory.newInstance(); MeasurementTimeseriesType measurementTimeseries = measurementTimeseriesDoc.addNewMeasurementTimeseries(); measurementTimeseries.setId("timeseries." + observationValue.getObservationID()); addTimeseriesMetadata(measurementTimeseries, observationValue.getPhenomenonTime().getGmlId()); TVPDefaultMetadataPropertyType xbMetaComponent = measurementTimeseries.addNewDefaultPointMetadata(); DefaultTVPMeasurementMetadataDocument xbDefMeasureMetaComponent = DefaultTVPMeasurementMetadataDocument.Factory.newInstance(); TVPMeasurementMetadataType defaultTVPMeasurementMetadata = xbDefMeasureMetaComponent.addNewDefaultTVPMeasurementMetadata(); defaultTVPMeasurementMetadata.addNewInterpolationType() .setHref("http://www.opengis.net/def/timeseriesType/WaterML/2.0/continuous"); xbDefMeasureMetaComponent.getDefaultTVPMeasurementMetadata().getInterpolationType().setTitle("Instantaneous"); String unit = null; if (observationValue instanceof SingleObservationValue) { SingleObservationValue<?> singleObservationValue = (SingleObservationValue<?>) observationValue; String time = getTimeString(singleObservationValue.getPhenomenonTime()); unit = singleObservationValue.getValue().getUnit(); if (observationValue.getValue() instanceof QuantityValue) { QuantityValue quantityValue = (QuantityValue) singleObservationValue.getValue(); if (!quantityValue.getValue().equals(Double.NaN)) { String value = Double.toString(quantityValue.getValue().doubleValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } else if (observationValue.getValue() instanceof CountValue) { CountValue countValue = (CountValue) singleObservationValue.getValue(); if (countValue.getValue() != null) { String value = Integer.toString(countValue.getValue().intValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } } else if (observationValue instanceof MultiObservationValues) { MultiObservationValues<?> multiObservationValue = (MultiObservationValues<?>) observationValue; TVPValue tvpValue = (TVPValue) multiObservationValue.getValue(); List<TimeValuePair> timeValuePairs = tvpValue.getValue(); unit = tvpValue.getUnit(); for (TimeValuePair timeValuePair : timeValuePairs) { if (timeValuePair.getValue() instanceof QuantityValue) { QuantityValue quantityValue = (QuantityValue) timeValuePair.getValue(); if (!quantityValue.getValue().equals(Double.NaN)) { timeValuePair.getTime(); String time = getTimeString(timeValuePair.getTime()); String value = Double.toString(quantityValue.getValue().doubleValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } else if (timeValuePair.getValue() instanceof CountValue) { CountValue countValue = (CountValue) timeValuePair.getValue(); if (countValue.getValue() != null) { String time = getTimeString(timeValuePair.getTime()); String value = Integer.toString(countValue.getValue().intValue()); addValuesToMeasurementTVP(measurementTimeseries.addNewPoint().addNewMeasurementTVP(), time, value); } } } } // set uom if (unit != null && !unit.isEmpty()) { defaultTVPMeasurementMetadata.addNewUom().setCode(unit); // } else { // OmObservableProperty observableProperty = // (OmObservableProperty) // sosObservation.getObservationConstellation().getObservableProperty(); // if (observableProperty.isSetUnit()) { // defaultTVPMeasurementMetadata.addNewUom().setCode(observableProperty.getUnit()); // } } xbMetaComponent.set(xbDefMeasureMetaComponent); return measurementTimeseriesDoc; } private void addTimeseriesMetadata(MeasurementTimeseriesType mtt, String gmlId) { MeasurementTimeseriesMetadataType mtmt = (MeasurementTimeseriesMetadataType) mtt.addNewMetadata().addNewTimeseriesMetadata().substitute( WaterMLConstants.QN_MEASUREMENT_TIMESERIES_METADATA, MeasurementTimeseriesMetadataType.type); createMeasurementTimeseriesMetadataType(mtmt, gmlId); } private MeasurementTimeseriesMetadataType createMeasurementTimeseriesMetadataType( MeasurementTimeseriesMetadataType mtmt, String gmlId) { mtmt.addNewTemporalExtent().setHref("#" + gmlId); return mtmt; } }