/** * 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.decode; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.opengis.ogc.BBOXType; import net.opengis.ogc.BinarySpatialOpType; import net.opengis.ogc.BinaryTemporalOpType; import net.opengis.ogc.PropertyNameDocument; import net.opengis.ogc.PropertyNameType; import net.opengis.ogc.SpatialOperatorType; import net.opengis.ogc.TemporalOperatorType; import net.opengis.ogc.impl.BBOXTypeImpl; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.n52.sos.exception.ows.InvalidParameterValueException; import org.n52.sos.exception.ows.NoApplicableCodeException; import org.n52.sos.exception.ows.concrete.UnsupportedDecoderInputException; import org.n52.sos.ogc.OGCConstants; import org.n52.sos.ogc.filter.FilterConstants; import org.n52.sos.ogc.filter.FilterConstants.TimeOperator; import org.n52.sos.ogc.filter.SpatialFilter; import org.n52.sos.ogc.filter.TemporalFilter; import org.n52.sos.ogc.gml.GmlConstants; import org.n52.sos.ogc.gml.time.Time; import org.n52.sos.ogc.gml.time.TimeInstant; import org.n52.sos.ogc.gml.time.TimePeriod; import org.n52.sos.ogc.ows.OwsExceptionReport; import org.n52.sos.ogc.sos.Sos1Constants; import org.n52.sos.ogc.sos.Sos2Constants; import org.n52.sos.service.ServiceConstants.SupportedTypeKey; import org.n52.sos.util.CodingHelper; import org.n52.sos.util.XmlHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.NodeList; import com.google.common.base.Joiner; import com.vividsolutions.jts.geom.Geometry; /** * @since 4.0.0 * */ public class OgcDecoderv100 implements Decoder<Object, XmlObject> { private static final Logger LOGGER = LoggerFactory.getLogger(OgcDecoderv100.class); private static final Set<DecoderKey> DECODER_KEYS = CodingHelper.decoderKeysForElements(OGCConstants.NS_OGC, SpatialOperatorType.class, TemporalOperatorType.class, BinarySpatialOpType.class, BinaryTemporalOpType.class, BBOXType.class, PropertyNameDocument.class); public OgcDecoderv100() { LOGGER.debug("Decoder for the following keys initialized successfully: {}!", Joiner.on(", ") .join(DECODER_KEYS)); } @Override public Set<DecoderKey> getDecoderKeyTypes() { return Collections.unmodifiableSet(DECODER_KEYS); } @Override public Set<String> getConformanceClasses() { return new HashSet<String>(0); } @Override public Object decode(XmlObject xmlObject) throws OwsExceptionReport { // validate document // FIXME Validation currently fails against abstract types // XmlHelper.validateDocument(xmlObject); if (xmlObject instanceof BinaryTemporalOpType) { return parseTemporalOperatorType((BinaryTemporalOpType) xmlObject); } if (xmlObject instanceof TemporalOperatorType) { throw new InvalidParameterValueException().at(Sos1Constants.GetObservationParams.eventTime).withMessage( "The requested temporal filter operand is not supported by this SOS!"); } // add propertyNameDoc here if (xmlObject instanceof PropertyNameDocument) { PropertyNameDocument xbPropertyNameDoc = ((PropertyNameDocument) xmlObject); return xbPropertyNameDoc.getPropertyName(); } // add BBOXType here if (xmlObject instanceof BinarySpatialOpType) { return parseSpatialOperatorType((BinarySpatialOpType) xmlObject); } if (xmlObject instanceof BBOXType) { return parseBBOXFilterType((BBOXTypeImpl) xmlObject); } if (xmlObject instanceof BBOXTypeImpl) { return parseBBOXFilterType((BBOXTypeImpl) xmlObject); } else { throw new UnsupportedDecoderInputException(this, xmlObject); } // TODO more spatial filters (contains, intersects, overlaps Point // Linestring Polygon, not supported by this SOS yet // return error message } @Override public Map<SupportedTypeKey, Set<String>> getSupportedTypes() { return Collections.emptyMap(); } /** * parses a single temporal filter of the requests and returns SOS temporal * filter * * @param xbBinaryTemporalOp * XmlObject representing the temporal filter * @return Returns SOS representation of temporal filter * * * @throws OwsExceptionReport * if parsing of the element failed */ private Object parseTemporalOperatorType(BinaryTemporalOpType xbBinaryTemporalOp) throws OwsExceptionReport { TemporalFilter temporalFilter = new TemporalFilter(); // FIXME local workaround against SOSHelper check value reference String valueRef = "phenomenonTime"; try { NodeList nodes = xbBinaryTemporalOp.getDomNode().getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { if (nodes.item(i).getNamespaceURI() != null && !nodes.item(i).getLocalName().equals(FilterConstants.EN_VALUE_REFERENCE)) { // GML decoder will return TimeInstant or TimePriod Object timeObject = CodingHelper.decodeXmlElement(XmlObject.Factory.parse(nodes.item(i))); if (timeObject instanceof PropertyNameType) { PropertyNameType propType = (PropertyNameType) timeObject; // TODO here apply logic for ogc property // om:samplingTime etc // valueRef = propType.getDomNode().getNodeValue(); } if (timeObject instanceof Time) { TimeOperator operator; Time time = (Time) timeObject; String localName = XmlHelper.getLocalName(xbBinaryTemporalOp); // change to SOS 1.0. TMDuring kind of if (localName.equals(TimeOperator.TM_During.name()) && time instanceof TimePeriod) { operator = TimeOperator.TM_During; } else if (localName.equals(TimeOperator.TM_Equals.name()) && time instanceof TimeInstant) { operator = TimeOperator.TM_Equals; } else if (localName.equals(TimeOperator.TM_After.name()) && time instanceof TimeInstant) { operator = TimeOperator.TM_After; } else if (localName.equals(TimeOperator.TM_Before.name()) && time instanceof TimeInstant) { operator = TimeOperator.TM_Before; } else { throw new InvalidParameterValueException() .at(Sos1Constants.GetObservationParams.eventTime).withMessage( "The requested temporal filter operand is not supported by this SOS!"); } temporalFilter.setOperator(operator); temporalFilter.setTime(time); // actually it should be eg om:samplingTime temporalFilter.setValueReference(valueRef); break; } } } } catch (XmlException xmle) { throw new NoApplicableCodeException().causedBy(xmle).withMessage("Error while parsing temporal filter!"); } return temporalFilter; } /** * Parses the spatial filter of a request. * * @param xbBBOX * XmlBean representing the feature of interest parameter of the * request * @return Returns SpatialFilter created from the passed foi request * parameter * * * @throws OwsExceptionReport * * if creation of the SpatialFilter failed */ private SpatialFilter parseBBOXFilterType(BBOXTypeImpl xbBBOX) throws OwsExceptionReport { SpatialFilter spatialFilter = new SpatialFilter(); // FIXME local workaround for SOSHelper check value reference String valueRef = "om:featureOfInterest/sams:SF_SpatialSamplingFeature/sams:shape"; try { spatialFilter.setOperator(FilterConstants.SpatialOperator.BBOX); XmlCursor geometryCursor = xbBBOX.newCursor(); if (geometryCursor.toChild(GmlConstants.QN_ENVELOPE)) { Object sosGeometry = CodingHelper.decodeXmlElement(XmlObject.Factory.parse(geometryCursor.getDomNode())); if (sosGeometry instanceof PropertyNameType) { PropertyNameType propType = (PropertyNameType) sosGeometry; // TODO here apply logic for ogc property // urn:ogc:data:location etc // valueRef = propType.getDomNode().getNodeValue(); } if (sosGeometry instanceof Geometry) { spatialFilter.setGeometry((Geometry) sosGeometry); spatialFilter.setValueReference(valueRef); } } else { throw new InvalidParameterValueException().at("FeatureOfInterest Filter").withMessage( "The requested spatial filter operand is not supported by this SOS!"); } geometryCursor.dispose(); } catch (XmlException xmle) { throw new NoApplicableCodeException().causedBy(xmle).withMessage("Error while parsing spatial filter!"); } return spatialFilter; } private Object parseSpatialOperatorType(BinarySpatialOpType xbSpatialOpsType) throws OwsExceptionReport { SpatialFilter spatialFilter = new SpatialFilter(); try { if (xbSpatialOpsType instanceof BBOXTypeImpl) { spatialFilter.setOperator(FilterConstants.SpatialOperator.BBOX); BBOXTypeImpl xbBBOX = (BBOXTypeImpl) xbSpatialOpsType; spatialFilter.setOperator(FilterConstants.SpatialOperator.BBOX); XmlCursor geometryCursor = xbBBOX.newCursor(); if (geometryCursor.toChild(GmlConstants.QN_ENVELOPE)) { Object sosGeometry = CodingHelper.decodeXmlElement(XmlObject.Factory.parse(geometryCursor.getDomNode())); if (sosGeometry instanceof Geometry) { spatialFilter.setGeometry((Geometry) sosGeometry); } } else { throw new InvalidParameterValueException().at(Sos2Constants.GetObservationParams.spatialFilter) .withMessage("The requested spatial filter operand is not supported by this SOS!"); } geometryCursor.dispose(); } else { throw new InvalidParameterValueException().at("GetFeatureOfInterest Filter").withMessage( "The requested spatial filter is not supported by this SOS!"); } } catch (XmlException xmle) { throw new NoApplicableCodeException().causedBy(xmle).withMessage("Error while parsing spatial filter!"); } return spatialFilter; } }