/** * 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.json.impl; import java.math.BigDecimal; import java.util.List; import org.n52.sos.coding.json.JSONConstants; import org.n52.sos.encode.json.JSONEncoder; import org.n52.sos.exception.ows.concrete.UnsupportedEncoderInputException; import org.n52.sos.ogc.om.NamedValue; import org.n52.sos.ogc.om.OmConstants; import org.n52.sos.ogc.om.OmObservation; import org.n52.sos.ogc.om.OmObservationConstellation; import org.n52.sos.ogc.om.TimeValuePair; 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.NilTemplateValue; import org.n52.sos.ogc.om.values.QuantityValue; import org.n52.sos.ogc.om.values.SweDataArrayValue; import org.n52.sos.ogc.om.values.TVPValue; import org.n52.sos.ogc.om.values.TextValue; import org.n52.sos.ogc.om.values.Value; import org.n52.sos.ogc.ows.OwsExceptionReport; import org.n52.sos.ogc.swe.SweAbstractDataComponent; import org.n52.sos.ogc.swe.SweAbstractDataRecord; import org.n52.sos.ogc.swe.SweDataArray; import org.n52.sos.ogc.swe.SweDataRecord; import org.n52.sos.ogc.swe.SweField; import org.n52.sos.ogc.swe.simpleType.SweBoolean; import org.n52.sos.ogc.swe.simpleType.SweCategory; import org.n52.sos.ogc.swe.simpleType.SweCount; import org.n52.sos.ogc.swe.simpleType.SweQuantity; import org.n52.sos.ogc.swe.simpleType.SweText; import org.n52.sos.ogc.swe.simpleType.SweTime; import org.n52.sos.util.JSONUtils; import org.n52.sos.util.OMHelper; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; /** * TODO JavaDoc * * @author Christian Autermann <c.autermann@52north.org> * * @since 4.0.0 */ public class ObservationEncoder extends JSONEncoder<OmObservation> { public ObservationEncoder() { super(OmObservation.class); } @Override public JsonNode encodeJSON(OmObservation o) throws OwsExceptionReport { ObjectNode json = nodeFactory().objectNode(); encodeObservationType(o, json); encodeIdentifier(o, json); encodeProcedure(o, json); encodeOfferings(o, json); encodeParameter(o, json); encodeObservableProperty(o, json); encodeFeatureOfInterest(o, json); encodePhenomenonTime(o, json); encodeResultTime(o, json); encodeValidTime(o, json); encodeResult(o, json); return json; } private void encodeIdentifier(OmObservation o, ObjectNode json) { if (o.isSetIdentifier()) { json.put(JSONConstants.IDENTIFIER, encodeCodeWithAuthority(o.getIdentifierCodeWithAuthority())); } } private void encodeProcedure(OmObservation o, ObjectNode json) { json.put(JSONConstants.PROCEDURE, o.getObservationConstellation().getProcedure().getIdentifier()); } private void encodeParameter(OmObservation o, ObjectNode json) throws OwsExceptionReport { if (o.isSetParameter()) { if (o.getParameter().size() == 1) { json.put(JSONConstants.PARAMETER, encodeNamedValue(o.getParameter().iterator().next())); } else { ArrayNode parameters = json.putArray(JSONConstants.PARAMETER); for (NamedValue<?> namedValue : o.getParameter()) { parameters.add(encodeNamedValue(namedValue)); } } } } private JsonNode encodeNamedValue(NamedValue<?> namedValue) throws OwsExceptionReport { ObjectNode namedValueObject = nodeFactory().objectNode(); namedValueObject.put(JSONConstants.NAME, namedValue.getName().getHref()); namedValueObject.put(JSONConstants.VALUE, encodeObjectToJson(namedValue.getValue().getValue())); ObjectNode parameterObject = nodeFactory().objectNode(); parameterObject.put(JSONConstants.NAMED_VALUE, namedValueObject); return parameterObject; } private void encodeObservableProperty(OmObservation o, ObjectNode json) { json.put(JSONConstants.OBSERVABLE_PROPERTY, o.getObservationConstellation().getObservableProperty() .getIdentifier()); } private void encodeObservationType(OmObservation o, ObjectNode json) { json.put(JSONConstants.TYPE, getObservationType(o)); } private void encodeOfferings(OmObservation o, ObjectNode json) { OmObservationConstellation oc = o.getObservationConstellation(); if (oc.isSetOfferings()) { if (oc.getOfferings().size() == 1) { json.put(JSONConstants.OFFERING, oc.getOfferings().iterator().next()); } else { ArrayNode offerings = json.putArray(JSONConstants.OFFERING); for (String offering : oc.getOfferings()) { offerings.add(offering); } } } } private void encodeFeatureOfInterest(OmObservation o, ObjectNode json) throws OwsExceptionReport { OmObservationConstellation oc = o.getObservationConstellation(); json.put(JSONConstants.FEATURE_OF_INTEREST, encodeObjectToJson(oc.getFeatureOfInterest())); } private void encodeResultTime(OmObservation o, ObjectNode json) throws OwsExceptionReport { if (o.isSetResultTime()) { json.put(JSONConstants.RESULT_TIME, encodeObjectToJson(o.getResultTime())); } } private void encodeValidTime(OmObservation o, ObjectNode json) throws OwsExceptionReport { if (o.isSetValidTime()) { json.put(JSONConstants.VALID_TIME, encodeObjectToJson(o.getValidTime())); } } private void encodePhenomenonTime(OmObservation o, ObjectNode json) throws OwsExceptionReport { json.put(JSONConstants.PHENOMENON_TIME, encodeObjectToJson(o.getPhenomenonTime())); } private void encodeResult(OmObservation o, ObjectNode json) throws OwsExceptionReport { json.put(JSONConstants.RESULT, encodeResult(o)); } private JsonNode encodeResult(OmObservation o) throws OwsExceptionReport { Value<?> value = o.getValue().getValue(); String type = getObservationType(o); if (value instanceof QuantityValue) { return encodeQualityValue(value); } else if (value instanceof CountValue) { return encodeCountValue(value); } else if (value instanceof TextValue) { return encodeTextValue(value); } else if (value instanceof BooleanValue) { return encodeBooleanValue(value); } else if (value instanceof CategoryValue) { return encodeCategoryValue(value); } else if (value instanceof GeometryValue) { return encodeGeometryValue(value); } else if (value instanceof SweDataArrayValue) { if (type.equals(OmConstants.OBS_TYPE_SWE_ARRAY_OBSERVATION)) { return encodeSweDataArrayValue(value); } else if (type.equals(OmConstants.OBS_TYPE_COMPLEX_OBSERVATION)) { return encodeComplexValue(value); } } else if (value instanceof TVPValue) { if (type.equals(OmConstants.OBS_TYPE_SWE_ARRAY_OBSERVATION)) { return encodeTVPValue(o); } } throw new UnsupportedEncoderInputException(this, value); } private JsonNode encodeQualityValue(Value<?> value) { QuantityValue quantityValue = (QuantityValue) value; return nodeFactory().objectNode().put(JSONConstants.UOM, quantityValue.getUnit()) .put(JSONConstants.VALUE, quantityValue.getValue()); } private JsonNode encodeCountValue(Value<?> value) { CountValue countValue = (CountValue) value; return nodeFactory().numberNode(countValue.getValue()); } private JsonNode encodeTextValue(Value<?> value) { TextValue textValue = (TextValue) value; return nodeFactory().textNode(textValue.getValue()); } private JsonNode encodeBooleanValue(Value<?> value) { BooleanValue booleanValue = (BooleanValue) value; return nodeFactory().booleanNode(booleanValue.getValue()); } private JsonNode encodeCategoryValue(Value<?> value) { CategoryValue categoryValue = (CategoryValue) value; return nodeFactory().objectNode().put(JSONConstants.CODESPACE, categoryValue.getUnit()) .put(JSONConstants.VALUE, categoryValue.getValue()); } private JsonNode encodeGeometryValue(Value<?> value) throws OwsExceptionReport { GeometryValue geometryValue = (GeometryValue) value; return encodeObjectToJson(geometryValue.getValue()); } private JsonNode encodeComplexValue(Value<?> value) throws OwsExceptionReport { ArrayNode result = nodeFactory().arrayNode(); SweDataArrayValue sweDataArrayValue = (SweDataArrayValue) value; SweDataArray sweDataArray = sweDataArrayValue.getValue(); SweAbstractDataRecord sweAbstractDataRecord = (SweAbstractDataRecord) sweDataArray.getElementType(); for (SweField field : sweAbstractDataRecord.getFields()) { result.add(encodeObjectToJson(field)); } return result; } private JsonNode encodeSweDataArrayValue(Value<?> value) throws OwsExceptionReport { SweDataArrayValue sweDataArrayValue = (SweDataArrayValue) value; ObjectNode result = nodeFactory().objectNode(); ArrayNode jfields = result.putArray(JSONConstants.FIELDS); ArrayNode jvalues = result.putArray(JSONConstants.VALUES); List<SweField> fields = ((SweDataRecord) sweDataArrayValue.getValue().getElementType()).getFields(); List<List<String>> values = sweDataArrayValue.getValue().getValues(); TokenConverter[] conv = new TokenConverter[fields.size()]; int i = 0; for (SweField field : fields) { try { conv[i++] = TokenConverter.forField(field); } catch (IllegalArgumentException e) { throw new UnsupportedEncoderInputException(this, field); } jfields.add(encodeObjectToJson(field)); } for (List<String> block : values) { ArrayNode jblock = jvalues.addArray(); i = 0; for (String token : block) { jblock.add(conv[i++].convert(token)); } } return result; } private String getObservationType(OmObservation o) { if (o.getObservationConstellation().isSetObservationType()) { return o.getObservationConstellation().getObservationType(); } else { return OMHelper.getObservationTypeFor(o.getValue().getValue()); } } private JsonNode encodeTVPValue(OmObservation o) throws OwsExceptionReport { TVPValue tvpValue = (TVPValue) o.getValue().getValue(); ObjectNode result = nodeFactory().objectNode(); List<TimeValuePair> values = tvpValue.getValue(); if (values != null && !values.isEmpty()) { String obsProp = o.getObservationConstellation().getObservableProperty().getIdentifier(); SweTime timeDef = new SweTime(); timeDef.setDefinition(OmConstants.PHENOMENON_TIME); timeDef.setUom(OmConstants.PHEN_UOM_ISO8601); SweField timeField = new SweField(OmConstants.PHENOMENON_TIME_NAME, timeDef); SweField valueField = getFieldForValue(obsProp, values.get(0).getValue()); result.putArray(JSONConstants.FIELDS).add(encodeObjectToJson(timeField)) .add(encodeObjectToJson(valueField)); ArrayNode jvalues = result.putArray(JSONConstants.VALUES); for (TimeValuePair tvp : values) { jvalues.addArray().add(encodeObjectToJson(tvp.getTime())).add(getTokenForValue(tvp.getValue())); } } return result; } private SweField getFieldForValue(String phenomenon, Value<?> value) throws UnsupportedEncoderInputException { final SweAbstractDataComponent def; if (value instanceof BooleanValue) { def = new SweBoolean(); } else if (value instanceof CategoryValue) { SweCategory sweCategory = new SweCategory(); CategoryValue categoryValue = (CategoryValue) value; sweCategory.setCodeSpace(categoryValue.getUnit()); def = sweCategory; } else if (value instanceof CountValue) { def = new SweCount(); } else if (value instanceof QuantityValue) { SweQuantity sweQuantity = new SweQuantity(); QuantityValue quantityValue = (QuantityValue) value; sweQuantity.setUom(quantityValue.getUnit()); def = sweQuantity; } else if (value instanceof TextValue) { def = new SweText(); } else if (value instanceof NilTemplateValue) { def = new SweText(); } else if (value instanceof BooleanValue) { def = new SweBoolean(); } else if (value instanceof GeometryValue) { def = new SweText(); } else { throw new UnsupportedEncoderInputException(this, value); } def.setDefinition(phenomenon); return new SweField(phenomenon, def); } private JsonNode getTokenForValue(Value<?> value) throws OwsExceptionReport { if (value instanceof QuantityValue) { QuantityValue quantityValue = (QuantityValue) value; return nodeFactory().numberNode(quantityValue.getValue()); } else if (value instanceof CountValue) { CountValue countValue = (CountValue) value; return nodeFactory().numberNode(countValue.getValue()); } else if (value instanceof TextValue) { TextValue textValue = (TextValue) value; return nodeFactory().textNode(textValue.getValue()); } else if (value instanceof BooleanValue) { BooleanValue booleanValue = (BooleanValue) value; return nodeFactory().booleanNode(booleanValue.getValue()); } else if (value instanceof CategoryValue) { CategoryValue categoryValue = (CategoryValue) value; return nodeFactory().textNode(categoryValue.getValue()); } else if (value instanceof GeometryValue) { GeometryValue geometryValue = (GeometryValue) value; // TODO WKT? return encodeObjectToJson(geometryValue); } else if (value instanceof NilTemplateValue) { return nodeFactory().nullNode(); } else { throw new UnsupportedEncoderInputException(this, value); } } /** * Class used to convert string values of a SweDataArray back to a more * native representation. */ private static abstract class TokenConverter { private static final TokenConverter TEXT_CONVERTER = new TextConverter(); private static final TokenConverter COUNT_CONVERTER = new CountConverter(); private static final TokenConverter QUANTITY_CONVERTER = new QuantityConverter(); private static final TokenConverter BOOLEAN_CONVERTER = new BooleanConverter(); private static final TokenConverter OBSERVABLE_PROPERTY_CONVERTER = TEXT_CONVERTER; private static final TokenConverter CATEGORY_CONVERTER = TEXT_CONVERTER; private static final TokenConverter TIME_CONVERTER = TEXT_CONVERTER; private static final TokenConverter TIME_RANGE_CONVERTER = new RangeTokenConverter(TIME_CONVERTER); private static final TokenConverter QUANTITY_RANGE_CONVERTER = new RangeTokenConverter(QUANTITY_CONVERTER); private static final TokenConverter COUNT_RANGE_CONVERTER = new RangeTokenConverter(COUNT_CONVERTER); JsonNodeFactory nodeFactory() { return JSONUtils.nodeFactory(); } abstract JsonNode convert(String s); static TokenConverter forField(SweField field) { switch (field.getElement().getDataComponentType()) { case Count: return COUNT_CONVERTER; case Boolean: return BOOLEAN_CONVERTER; case CountRange: return COUNT_RANGE_CONVERTER; case ObservableProperty: return OBSERVABLE_PROPERTY_CONVERTER; case Text: return TEXT_CONVERTER; case Quantity: return QUANTITY_CONVERTER; case QuantityRange: return QUANTITY_RANGE_CONVERTER; case Time: return TIME_CONVERTER; case TimeRange: return TIME_RANGE_CONVERTER; case Category: return CATEGORY_CONVERTER; default: throw new IllegalArgumentException("Unknown field type"); } } private static class RangeTokenConverter extends TokenConverter { private final TokenConverter conv; RangeTokenConverter(TokenConverter conv) { this.conv = conv; } @Override JsonNode convert(String s) { String[] split = s.split("/"); return nodeFactory().arrayNode().add(conv.convert(split[0])).add(conv.convert(split[1])); } } private static class TextConverter extends TokenConverter { @Override JsonNode convert(String s) { return nodeFactory().textNode(s); } } private static class QuantityConverter extends TokenConverter { @Override JsonNode convert(String s) { return nodeFactory().numberNode(new BigDecimal(s)); } } private static class BooleanConverter extends TokenConverter { @Override JsonNode convert(String s) { return nodeFactory().booleanNode(Boolean.parseBoolean(s)); } } private static class CountConverter extends TokenConverter { @Override JsonNode convert(String s) { return nodeFactory().numberNode(Integer.parseInt(s)); } } } }