/**
* 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.json.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.n52.sos.coding.json.JSONConstants;
import org.n52.sos.coding.json.JSONValidator;
import org.n52.sos.coding.json.SchemaConstants;
import org.n52.sos.decode.json.JSONDecoder;
import org.n52.sos.decode.json.JSONDecodingException;
import org.n52.sos.ogc.gml.AbstractFeature;
import org.n52.sos.ogc.gml.CodeWithAuthority;
import org.n52.sos.ogc.gml.ReferenceType;
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.om.AbstractPhenomenon;
import org.n52.sos.ogc.om.NamedValue;
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.OmObservationConstellation;
import org.n52.sos.ogc.om.SingleObservationValue;
import org.n52.sos.ogc.om.values.BooleanValue;
import org.n52.sos.ogc.om.values.CategoryValue;
import org.n52.sos.ogc.om.values.CountValue;
import org.n52.sos.ogc.om.values.GeometryValue;
import org.n52.sos.ogc.om.values.HrefAttributeValue;
import org.n52.sos.ogc.om.values.QuantityValue;
import org.n52.sos.ogc.om.values.TextValue;
import org.n52.sos.ogc.ows.OwsExceptionReport;
import org.n52.sos.ogc.sensorML.SensorML;
import org.n52.sos.ogc.sos.SosProcedureDescription;
import org.n52.sos.service.ServiceConstants.SupportedTypeKey;
import org.n52.sos.w3c.xlink.W3CHrefAttribute;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.vividsolutions.jts.geom.Geometry;
/**
* TODO JavaDoc
*
* @author Christian Autermann <c.autermann@52north.org>
*
* @since 4.0.0
*/
public class ObservationDecoder extends JSONDecoder<OmObservation> {
private static final Map<SupportedTypeKey, Set<String>> SUPPORTED_TYPES = ImmutableMap.of(
SupportedTypeKey.ObservationType, (Set<String>) ImmutableSet.of(
// OmConstants.OBS_TYPE_SWE_ARRAY_OBSERVATION,
// OmConstants.OBS_TYPE_COMPLEX_OBSERVATION,
OmConstants.OBS_TYPE_GEOMETRY_OBSERVATION, OmConstants.OBS_TYPE_CATEGORY_OBSERVATION,
OmConstants.OBS_TYPE_COUNT_OBSERVATION, OmConstants.OBS_TYPE_MEASUREMENT,
OmConstants.OBS_TYPE_TEXT_OBSERVATION, OmConstants.OBS_TYPE_TRUTH_OBSERVATION));
private final JSONDecoder<AbstractFeature> featureDecoder = new FeatureDecoder();
private final JSONDecoder<Geometry> geometryDecoder = new GeoJSONDecoder();
public ObservationDecoder() {
super(OmObservation.class);
}
@Override
public Map<SupportedTypeKey, Set<String>> getSupportedTypes() {
return Collections.unmodifiableMap(SUPPORTED_TYPES);
}
@Override
public OmObservation decodeJSON(JsonNode node, boolean validate) throws OwsExceptionReport {
if (node == null) {
return null;
}
if (validate) {
JSONValidator.getInstance().validateAndThrow(node, SchemaConstants.Observation.OBSERVATION);
}
return decodeJSON(node);
}
protected OmObservation decodeJSON(JsonNode node) throws OwsExceptionReport {
if (node.isObject()) {
OmObservation o = new OmObservation();
o.setIdentifier(parseIdentifier(node));
o.setValidTime(parseValidTime(node));
o.setResultTime(parseResultTime(node));
o.setValue(parseValue(node));
o.setParameter(parseParameter(node));
o.setObservationConstellation(parseObservationConstellation(node));
return o;
} else {
return null;
}
}
public OmObservationConstellation parseObservationConstellation(JsonNode node) throws OwsExceptionReport {
OmObservationConstellation oc = new OmObservationConstellation();
oc.setProcedure(parseProcedure(node));
oc.setObservableProperty(parseObservableProperty(node));
oc.setObservationType(parseObservationType(node));
oc.setFeatureOfInterest(parseFeatureOfInterest(node));
return oc;
}
protected SosProcedureDescription parseProcedure(JsonNode node) {
return new SensorML().setIdentifier(node.path(JSONConstants.PROCEDURE).textValue());
}
private AbstractPhenomenon parseObservableProperty(JsonNode node) {
return new OmObservableProperty(node.path(JSONConstants.OBSERVED_PROPERTY).textValue());
}
private CodeWithAuthority parseIdentifier(JsonNode node) {
return parseCodeWithAuthority(node.path(JSONConstants.IDENTIFIER));
}
protected String parseObservationType(JsonNode node) {
return node.path(JSONConstants.TYPE).textValue();
}
protected TimePeriod parseValidTime(JsonNode node) throws OwsExceptionReport {
return parseTimePeriod(node.path(JSONConstants.VALID_TIME));
}
protected TimeInstant parseResultTime(JsonNode node) throws OwsExceptionReport {
return parseTimeInstant(node.path(JSONConstants.RESULT_TIME));
}
private Time parsePhenomenonTime(JsonNode node) throws OwsExceptionReport {
return parseTime(node.path(JSONConstants.PHENOMENON_TIME));
}
protected Collection<NamedValue<?>> parseParameter(JsonNode node) throws OwsExceptionReport {
Set<NamedValue<?>> parameters = Sets.newHashSet();
JsonNode parameter = node.path(JSONConstants.PARAMETER);
if (parameter.isArray()) {
for (JsonNode jsonNode : parameter) {
parameters.add(parseNamedValue(jsonNode));
}
} else if (parameter.isObject()) {
parameters.add(parseNamedValue(parameter));
}
return parameters;
}
private NamedValue<?> parseNamedValue(JsonNode parameter) throws OwsExceptionReport {
JsonNode namedValue = parameter.path(JSONConstants.NAMED_VALUE);
NamedValue<?> nv = parseNamedValueValue(namedValue);
ReferenceType referenceType = new ReferenceType(namedValue.path(JSONConstants.NAME).asText());
nv.setName(referenceType);
return nv;
}
private NamedValue<?> parseNamedValueValue(JsonNode namedValue) throws OwsExceptionReport {
JsonNode value = namedValue.path(JSONConstants.VALUE);
if (value.isTextual()) {
NamedValue<W3CHrefAttribute> nv = new NamedValue<W3CHrefAttribute>();
nv.setValue(new HrefAttributeValue(new W3CHrefAttribute(value.asText())));
return nv;
} else {
NamedValue<Geometry> nv = new NamedValue<Geometry>();
nv.setValue(new GeometryValue(geometryDecoder.decodeJSON(value, false)));
return nv;
}
}
protected AbstractFeature parseFeatureOfInterest(JsonNode node) throws OwsExceptionReport {
return featureDecoder.decodeJSON(node.path(JSONConstants.FEATURE_OF_INTEREST), false);
}
private ObservationValue<?> parseValue(JsonNode node) throws OwsExceptionReport {
String type = parseObservationType(node);
if (type.equals(OmConstants.OBS_TYPE_MEASUREMENT)) {
return parseMeasurementValue(node);
} else if (type.equals(OmConstants.OBS_TYPE_TEXT_OBSERVATION)) {
return parseTextObservationValue(node);
} else if (type.equals(OmConstants.OBS_TYPE_COUNT_OBSERVATION)) {
return parseCountObservationValue(node);
} else if (type.equals(OmConstants.OBS_TYPE_TRUTH_OBSERVATION)) {
return parseTruthObservationValue(node);
} else if (type.equals(OmConstants.OBS_TYPE_CATEGORY_OBSERVATION)) {
return parseCategoryObservationValue(node);
} else if (type.equals(OmConstants.OBS_TYPE_GEOMETRY_OBSERVATION)) {
return parseGeometryObservation(node);
} else {
throw new JSONDecodingException("Unsupported observationType: " + type);
}
}
protected ObservationValue<?> parseMeasurementValue(JsonNode node) throws OwsExceptionReport {
final QuantityValue qv =
new QuantityValue(node.path(JSONConstants.RESULT).path(JSONConstants.VALUE).doubleValue(), node
.path(JSONConstants.RESULT).path(JSONConstants.UOM).textValue());
return new SingleObservationValue<Double>(parsePhenomenonTime(node), qv);
}
private ObservationValue<?> parseTextObservationValue(JsonNode node) throws OwsExceptionReport {
final TextValue v = new TextValue(node.path(JSONConstants.RESULT).textValue());
return new SingleObservationValue<String>(parsePhenomenonTime(node), v);
}
private ObservationValue<?> parseCountObservationValue(JsonNode node) throws OwsExceptionReport {
final CountValue v = new CountValue(node.path(JSONConstants.RESULT).intValue());
return new SingleObservationValue<Integer>(parsePhenomenonTime(node), v);
}
private ObservationValue<?> parseTruthObservationValue(JsonNode node) throws OwsExceptionReport {
final BooleanValue v = new BooleanValue(node.path(JSONConstants.RESULT).booleanValue());
return new SingleObservationValue<Boolean>(parsePhenomenonTime(node), v);
}
private ObservationValue<?> parseCategoryObservationValue(JsonNode node) throws OwsExceptionReport {
final CategoryValue v =
new CategoryValue(node.path(JSONConstants.RESULT).path(JSONConstants.VALUE).textValue(), node
.path(JSONConstants.RESULT).path(JSONConstants.CODESPACE).textValue());
return new SingleObservationValue<String>(parsePhenomenonTime(node), v);
}
private ObservationValue<?> parseGeometryObservation(JsonNode node) throws OwsExceptionReport {
GeometryValue v = new GeometryValue(geometryDecoder.decodeJSON(node.path(JSONConstants.RESULT), false));
return new SingleObservationValue<Geometry>(parsePhenomenonTime(node), v);
}
}