/* * Copyright 2015-2020 52°North Initiative for Geospatial Open Source * Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.n52.svalbard.decode; import java.util.Collections; import java.util.Map; import java.util.Set; import net.opengis.om.x20.NamedValuePropertyType; import net.opengis.om.x20.OMObservationDocument; import net.opengis.om.x20.OMObservationType; import net.opengis.om.x20.TimeObjectPropertyType; import org.apache.xmlbeans.XmlBoolean; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlInteger; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlString; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.n52.shetland.ogc.SupportedType; import org.n52.shetland.ogc.gml.AbstractFeature; import org.n52.shetland.ogc.gml.AbstractGeometry; import org.n52.shetland.ogc.gml.GmlMeasureType; import org.n52.shetland.ogc.gml.ReferenceType; import org.n52.shetland.ogc.gml.time.IndeterminateValue; import org.n52.shetland.ogc.gml.time.Time; import org.n52.shetland.ogc.gml.time.Time.NilReason; import org.n52.shetland.ogc.gml.time.TimeInstant; import org.n52.shetland.ogc.gml.time.TimePeriod; import org.n52.shetland.ogc.om.AbstractPhenomenon; import org.n52.shetland.ogc.om.ObservationValue; import org.n52.shetland.ogc.om.OmConstants; import org.n52.shetland.ogc.om.OmObservableProperty; import org.n52.shetland.ogc.om.OmObservation; import org.n52.shetland.ogc.om.OmObservationConstellation; import org.n52.shetland.ogc.om.SingleObservationValue; import org.n52.shetland.ogc.om.values.BooleanValue; import org.n52.shetland.ogc.om.values.CategoryValue; import org.n52.shetland.ogc.om.values.ComplexValue; import org.n52.shetland.ogc.om.values.CountValue; import org.n52.shetland.ogc.om.values.GeometryValue; import org.n52.shetland.ogc.om.values.NilTemplateValue; import org.n52.shetland.ogc.om.values.QuantityValue; import org.n52.shetland.ogc.om.values.ReferenceValue; import org.n52.shetland.ogc.om.values.SweDataArrayValue; import org.n52.shetland.ogc.om.values.TextValue; import org.n52.shetland.ogc.sensorML.SensorML; import org.n52.shetland.ogc.sos.Sos2Constants; import org.n52.shetland.ogc.sos.SosConstants; import org.n52.shetland.ogc.swe.SweDataArray; import org.n52.shetland.ogc.swe.SweDataRecord; import org.n52.shetland.w3c.Nillable; import org.n52.svalbard.ConformanceClasses; import org.n52.svalbard.decode.exception.DecodingException; import org.n52.svalbard.util.CodingHelper; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import org.locationtech.jts.geom.Geometry; /** * @since 1.0.0 * */ public class OmDecoderv20 extends AbstractOmDecoderv20 { private static final Logger LOGGER = LoggerFactory.getLogger(OmDecoderv20.class); private static final Set<DecoderKey> DECODER_KEYS = CodingHelper.decoderKeysForElements(OmConstants.NS_OM_2, OMObservationType.class, NamedValuePropertyType.class, OMObservationDocument.class, NamedValuePropertyType[].class); private static final Set<SupportedType> SUPPORTED_TYPES = ImmutableSet.of( OmConstants.OBS_TYPE_SWE_ARRAY_OBSERVATION_TYPE, OmConstants.OBS_TYPE_COMPLEX_OBSERVATION_TYPE, OmConstants.OBS_TYPE_GEOMETRY_OBSERVATION_TYPE, OmConstants.OBS_TYPE_CATEGORY_OBSERVATION_TYPE, OmConstants.OBS_TYPE_COUNT_OBSERVATION_TYPE, OmConstants.OBS_TYPE_MEASUREMENT_TYPE, OmConstants.OBS_TYPE_TEXT_OBSERVATION_TYPE, OmConstants.OBS_TYPE_TRUTH_OBSERVATION_TYPE); private static final Set<String> CONFORMANCE_CLASSES = ImmutableSet.of(ConformanceClasses.OM_V2_MEASUREMENT, ConformanceClasses.OM_V2_CATEGORY_OBSERVATION, ConformanceClasses.OM_V2_COUNT_OBSERVATION, ConformanceClasses.OM_V2_TRUTH_OBSERVATION, ConformanceClasses.OM_V2_GEOMETRY_OBSERVATION, ConformanceClasses.OM_V2_COMPLEX_OBSERVATION, ConformanceClasses.OM_V2_TEXT_OBSERVATION); public OmDecoderv20() { LOGGER.debug("Decoder for the following keys initialized successfully: {}!", Joiner.on(", ").join(DECODER_KEYS)); } @Override public Set<DecoderKey> getKeys() { return Collections.unmodifiableSet(DECODER_KEYS); } @Override public Set<SupportedType> getSupportedTypes() { return Collections.unmodifiableSet(SUPPORTED_TYPES); } @Override public Set<String> getConformanceClasses(String service, String version) { if (SosConstants.SOS.equals(service) && Sos2Constants.SERVICEVERSION.equals(version)) { return Collections.unmodifiableSet(CONFORMANCE_CLASSES); } return Collections.emptySet(); } @Override public Object decode(Object object) throws DecodingException { // validate document // XmlHelper.validateDocument((XmlObject) object); if (object instanceof OMObservationDocument) { OMObservationDocument omObservationDocument = (OMObservationDocument) object; return parseOmObservation(omObservationDocument.getOMObservation()); } else if (object instanceof OMObservationType) { return parseOmObservation((OMObservationType) object); } return super.decode(object); } private OmObservation parseOmObservation(OMObservationType omObservation) throws DecodingException { Map<String, AbstractFeature> featureMap = Maps.newHashMap(); OmObservation sosObservation = new OmObservation(); // parse identifier, description parseAbstractFeatureType(omObservation, sosObservation); OmObservationConstellation observationConstallation = getObservationConstellation(omObservation, featureMap); sosObservation.setObservationConstellation(observationConstallation); sosObservation.setResultTime(getResultTime(omObservation)); sosObservation.setValidTime(getValidTime(omObservation)); if (omObservation.getParameterArray() != null) { sosObservation.setParameter(parseNamedValueTypeArray(omObservation.getParameterArray())); } sosObservation.setValue(getObservationValue(omObservation)); // TODO: later for spatial filtering profile // omObservation.getParameterArray(); return sosObservation; } private OmObservationConstellation getObservationConstellation(OMObservationType omot, Map<String, AbstractFeature> featureMap) throws DecodingException { OmObservationConstellation observationConstellation = new OmObservationConstellation(); observationConstellation.setObservationType(getObservationType(omot)); observationConstellation.setProcedure(createProcedure(omot)); observationConstellation.setObservableProperty(getObservableProperty(omot)); observationConstellation.setFeatureOfInterest(createFeatureOfInterest(omot, featureMap)); return observationConstellation; } private String getObservationType(OMObservationType omObservation) { if (omObservation.getType() != null) { return omObservation.getType().getHref(); } return null; } private AbstractPhenomenon getObservableProperty(OMObservationType omObservation) { if (omObservation.getObservedProperty() != null) { return new OmObservableProperty(omObservation.getObservedProperty().getHref()); } return null; } private Time getPhenomenonTime(OMObservationType omObservation) throws DecodingException { TimeObjectPropertyType phenomenonTime = omObservation.getPhenomenonTime(); if (phenomenonTime.isSetHref() && phenomenonTime.getHref().startsWith("#")) { TimeInstant timeInstant = new TimeInstant(); timeInstant.setGmlId(phenomenonTime.getHref()); return timeInstant; } else if (phenomenonTime.isSetNilReason() && phenomenonTime.getNilReason() instanceof String && ((String) phenomenonTime.getNilReason()).equals(IndeterminateValue.TEMPLATE.getValue())) { return new TimeInstant(IndeterminateValue.TEMPLATE); } else if (phenomenonTime.isSetAbstractTimeObject()) { Object decodedObject = decodeXmlObject(phenomenonTime.getAbstractTimeObject()); if (decodedObject instanceof Time) { return (Time) decodedObject; } // FIXME else } throw new DecodingException(Sos2Constants.InsertObservationParams.observation, "The requested phenomenonTime type is not supported by this service!"); } private TimeInstant getResultTime(OMObservationType omObservation) throws DecodingException { if (omObservation.getResultTime().isSetHref()) { TimeInstant timeInstant = new TimeInstant(); timeInstant.setGmlId(omObservation.getResultTime().getHref()); if (omObservation.getResultTime().getHref().charAt(0) == '#') { // document internal link // TODO parse linked element timeInstant.setReference(Sos2Constants.EN_PHENOMENON_TIME); } else { timeInstant.setReference(omObservation.getResultTime().getHref()); } return timeInstant; } else if (omObservation.getResultTime().isSetNilReason() && omObservation.getResultTime().getNilReason() instanceof String && NilReason.template .equals(NilReason.getEnumForString((String) omObservation.getResultTime().getNilReason()))) { TimeInstant timeInstant = new TimeInstant(); timeInstant .setNilReason(NilReason.getEnumForString((String) omObservation.getResultTime().getNilReason())); return timeInstant; } else if (omObservation.getResultTime().isSetTimeInstant()) { Object decodedObject = decodeXmlObject(omObservation.getResultTime().getTimeInstant()); if (decodedObject instanceof TimeInstant) { return (TimeInstant) decodedObject; } throw unsupportedResultTimeType(); } else { throw unsupportedResultTimeType(); } } private TimePeriod getValidTime(OMObservationType omObservation) throws DecodingException { if (omObservation.isSetValidTime()) { Object decodedObject = decodeXmlObject(omObservation.getValidTime().getTimePeriod()); if (decodedObject instanceof TimePeriod) { return (TimePeriod) decodedObject; } throw new DecodingException(Sos2Constants.InsertObservationParams.observation, "The requested validTime type is not supported by this service!"); } return null; } private ObservationValue<?> getObservationValue(OMObservationType omObservation) throws DecodingException { Time phenomenonTime = getPhenomenonTime(omObservation); ObservationValue<?> observationValue; if (!omObservation.getResult().getDomNode().hasChildNodes() && phenomenonTime.isSetNilReason() && phenomenonTime.getNilReason().equals(NilReason.template)) { observationValue = new SingleObservationValue<>(new NilTemplateValue()); } else { observationValue = getResult(omObservation); } observationValue.setPhenomenonTime(phenomenonTime); return observationValue; } private ObservationValue<?> getResult(OMObservationType omObservation) throws DecodingException { XmlObject xbResult = omObservation.getResult(); if (xbResult.schemaType() == XmlAnyTypeImpl.type) { // Template observation for InsertResultTemplate operation if (!xbResult.getDomNode().hasChildNodes()) { return new SingleObservationValue<>(new NilTemplateValue()); } else { try { xbResult = XmlObject.Factory.parse(xbResult.xmlText(getXmlOptions()).trim()); } catch (XmlException e) { LOGGER.error("Error while parsing NamedValueValue", e); } } } // // Template observation for InsertResultTemplate operation // if (omObservation.getResult().schemaType() == XmlAnyTypeImpl.type && // !omObservation.getResult().getDomNode().hasChildNodes()) { // return new SingleObservationValue<String>(new NilTemplateValue()); // } if (xbResult.schemaType() == XmlBoolean.type) { // TruthObservation XmlBoolean xbBoolean = (XmlBoolean) xbResult; BooleanValue booleanValue = new BooleanValue(xbBoolean.getBooleanValue()); return new SingleObservationValue<>(booleanValue); } else if (xbResult.schemaType() == XmlInteger.type) { // CountObservation XmlInteger xbInteger = (XmlInteger) xbResult; CountValue countValue = new CountValue(Integer.parseInt(xbInteger.getBigIntegerValue().toString())); return new SingleObservationValue<>(countValue); } else if (xbResult.schemaType() == XmlString.type) { // TextObservation XmlString xbString = (XmlString) xbResult; TextValue stringValue = new TextValue(xbString.getStringValue()); return new SingleObservationValue<>(stringValue); } else { // result elements with other encoding like SWE_ARRAY_OBSERVATION Object decodedObject = decodeXmlObject(xbResult); if (decodedObject instanceof ObservationValue) { return (ObservationValue<?>) decodedObject; } else if (decodedObject instanceof GmlMeasureType) { GmlMeasureType measureType = (GmlMeasureType) decodedObject; QuantityValue quantitiyValue = new QuantityValue(measureType.getValue(), measureType.getUnit()); return new SingleObservationValue<>(quantitiyValue); } else if (decodedObject instanceof ReferenceType) { if (omObservation.isSetType() && omObservation.getType().isSetHref() && omObservation.getType().getHref().equals(OmConstants.OBS_TYPE_REFERENCE_OBSERVATION)) { return new SingleObservationValue<>(new ReferenceValue((ReferenceType) decodedObject)); } return new SingleObservationValue<>(new CategoryValue(((ReferenceType) decodedObject).getHref())); } else if (decodedObject instanceof Geometry) { return new SingleObservationValue<>(new GeometryValue((Geometry) decodedObject)); } else if (decodedObject instanceof AbstractGeometry) { SingleObservationValue<Geometry> result = new SingleObservationValue<>(); result.setValue(new GeometryValue(((AbstractGeometry) decodedObject).getGeometry())); return result; } else if (decodedObject instanceof SweDataArray) { return new SingleObservationValue<>(new SweDataArrayValue((SweDataArray) decodedObject)); } else if (decodedObject instanceof SweDataRecord) { return new SingleObservationValue<>(new ComplexValue((SweDataRecord) decodedObject)); } throw new DecodingException(Sos2Constants.InsertObservationParams.observation, "The requested result type '%s' is not supported by this service!", decodedObject.getClass().getSimpleName()); } } private AbstractFeature checkFeatureWithMap(AbstractFeature featureOfInterest, Map<String, AbstractFeature> featureMap) { if (featureOfInterest.getGmlId() != null && !featureOfInterest.getGmlId().isEmpty()) { if (featureMap.containsKey(featureOfInterest.getGmlId())) { return featureMap.get(featureOfInterest.getGmlId()); } else { featureMap.put(featureOfInterest.getGmlId(), featureOfInterest); } } return featureOfInterest; } private Nillable<AbstractFeature> createProcedure(OMObservationType omObservation) { if (omObservation.getProcedure().isNil() || omObservation.getProcedure().isSetNilReason()) { if (omObservation.getProcedure().isSetNilReason()) { return Nillable.<AbstractFeature> nil(omObservation.getProcedure().getNilReason().toString()); } } else if (omObservation.getProcedure().isSetHref()) { SensorML procedure = new SensorML(); procedure.setIdentifier(omObservation.getProcedure().getHref()); return Nillable.<AbstractFeature> of(procedure); } return Nillable.<AbstractFeature> nil(); } private Nillable<AbstractFeature> createFeatureOfInterest(OMObservationType omot, Map<String, AbstractFeature> featureMap) throws DecodingException { if (omot.getFeatureOfInterest().isNil() || omot.getFeatureOfInterest().isSetNilReason()) { if (omot.getFeatureOfInterest().isSetNilReason()) { return Nillable.<AbstractFeature> nil(omot.getFeatureOfInterest().getNilReason().toString()); } } else { Object decodeXmlElement = decodeXmlElement(omot.getFeatureOfInterest()); if (decodeXmlElement instanceof AbstractFeature) { AbstractFeature featureOfInterest = (AbstractFeature) decodeXmlElement; return Nillable.of(checkFeatureWithMap(featureOfInterest, featureMap)); } } return Nillable.<AbstractFeature> nil(); } private static DecodingException unsupportedResultTimeType() { return new DecodingException(Sos2Constants.InsertObservationParams.observation, "The requested resultTime type is not supported by this service!"); } }