/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * 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.constellation.sos.ws; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.sis.util.logging.Logging; import org.geotoolkit.gml.GmlInstant; import org.geotoolkit.gml.xml.AbstractFeature; import org.geotoolkit.gml.xml.AbstractTimePosition; import org.geotoolkit.gml.xml.Envelope; import org.geotoolkit.gml.xml.FeatureProperty; import org.geotoolkit.gml.xml.TimeIndeterminateValueType; import org.geotoolkit.observation.xml.AbstractObservation; import org.geotoolkit.observation.xml.Process; import org.geotoolkit.observation.xml.v100.MeasureType; import org.geotoolkit.observation.xml.v100.MeasurementType; import org.geotoolkit.sos.xml.Capabilities; import org.geotoolkit.sos.xml.SOSXmlFactory; import org.geotoolkit.sos.xml.v100.ObservationOfferingType; import org.geotoolkit.swe.xml.AbstractEncodingProperty; import org.geotoolkit.swe.xml.DataArray; import org.geotoolkit.swe.xml.DataArrayProperty; import org.geotoolkit.swe.xml.DataComponentProperty; import org.geotoolkit.swe.xml.PhenomenonProperty; import org.geotoolkit.swe.xml.v101.CompositePhenomenonType; import org.opengis.observation.Observation; import org.opengis.observation.ObservationCollection; import org.opengis.temporal.Instant; import org.opengis.temporal.Period; /** * Static methods use to create valid XML file, by setting object into referenceMode. * The goal is to avoid to declare the same block many times in a XML file. * * @author Guilhem Legal (Geomatys) */ public final class Normalizer { private static final Logger LOGGER = Logging.getLogger("org.constellation.sos"); private Normalizer() {} public static Capabilities normalizeDocument(final Capabilities capa){ if (capa instanceof org.geotoolkit.sos.xml.v100.Capabilities) { return normalizeDocumentv100((org.geotoolkit.sos.xml.v100.Capabilities)capa); } else { return capa; // no necessary in SOS 2 } } /** * Normalize the capabilities document by replacing the double by reference * * @param capa the unnormalized document. * * @return a normalized document */ private static Capabilities normalizeDocumentv100(final org.geotoolkit.sos.xml.v100.Capabilities capa){ final List<PhenomenonProperty> alreadySee = new ArrayList<>(); if (capa.getContents() != null) { for (ObservationOfferingType off: capa.getContents().getObservationOfferingList().getObservationOffering()) { for (PhenomenonProperty pheno: off.getRealObservedProperty()) { if (alreadySee.contains(pheno)) { pheno.setToHref(); } else { if (pheno.getPhenomenon() instanceof CompositePhenomenonType) { final CompositePhenomenonType compo = (CompositePhenomenonType) pheno.getPhenomenon(); for (PhenomenonProperty pheno2: compo.getRealComponent()) { if (alreadySee.contains(pheno2)) { pheno2.setToHref(); } else { alreadySee.add(pheno2); } } } alreadySee.add(pheno); } } } } return capa; } /** * Regroup the different Observation by sensor and by feature of interest. * * @param version * @param bounds * @param collection * * @return a collection */ public static ObservationCollection regroupObservation(final String version, final Envelope bounds, final ObservationCollection collection){ final List<Observation> members = collection.getMember(); final Map<String, Observation> merged = new HashMap<>(); for (Observation obs : members) { final Process process = (Process) obs.getProcedure(); final String featureID = getFeatureID(obs); final String key = process.getHref() + '-' + featureID; if (obs instanceof MeasurementType) { // measurment are not merged merged.put(UUID.randomUUID().toString(), obs); } else if (merged.containsKey(key)) { final AbstractObservation uniqueObs = (AbstractObservation) merged.get(key); if (uniqueObs.getResult() instanceof DataArrayProperty) { final DataArrayProperty mergedArrayP = (DataArrayProperty) uniqueObs.getResult(); final DataArray mergedArray = mergedArrayP.getDataArray(); if (obs.getResult() instanceof DataArrayProperty) { final DataArrayProperty arrayP = (DataArrayProperty) obs.getResult(); final DataArray array = arrayP.getDataArray(); //we merge this observation with the map one mergedArray.setElementCount(mergedArray.getElementCount().getCount().getValue() + array.getElementCount().getCount().getValue()); mergedArray.setValues(mergedArray.getValues() + array.getValues()); } } // merge the samplingTime if (uniqueObs.getSamplingTime() instanceof Period) { final Period totalPeriod = (Period)uniqueObs.getSamplingTime(); if (obs.getSamplingTime() instanceof Instant) { final Instant instant = (Instant)obs.getSamplingTime(); if (totalPeriod.getBeginning().getDate().getTime() > instant.getDate().getTime()) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, new Timestamp(instant.getDate().getTime()), new Timestamp(totalPeriod.getEnding().getDate().getTime())); uniqueObs.setSamplingTimePeriod(newPeriod); } if (totalPeriod.getEnding().getDate().getTime() < instant.getDate().getTime()) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, totalPeriod.getBeginning().getDate(), instant.getDate()); uniqueObs.setSamplingTimePeriod(newPeriod); } } else if (obs.getSamplingTime() instanceof Period) { final Period period = (Period)obs.getSamplingTime(); // BEGIN if (TimeIndeterminateValueType.BEFORE.equals((((GmlInstant)totalPeriod.getBeginning()).getTimePosition()).getIndeterminatePosition()) || TimeIndeterminateValueType.BEFORE.equals((((GmlInstant) period.getBeginning()).getTimePosition()).getIndeterminatePosition())) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, ((GmlInstant) totalPeriod.getBeginning()).getTimePosition(), ((GmlInstant) period.getEnding()).getTimePosition()); uniqueObs.setSamplingTimePeriod(newPeriod); } else if (totalPeriod.getBeginning().getDate().getTime() > period.getBeginning().getDate().getTime()) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, period.getBeginning().getDate(), totalPeriod.getEnding().getDate()); uniqueObs.setSamplingTimePeriod(newPeriod); } // END if (TimeIndeterminateValueType.NOW.equals((((GmlInstant)totalPeriod.getEnding()).getTimePosition()).getIndeterminatePosition()) || TimeIndeterminateValueType.NOW.equals((((GmlInstant) period.getEnding()).getTimePosition()).getIndeterminatePosition())) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, totalPeriod.getBeginning().getDate(), period.getEnding().getDate()); uniqueObs.setSamplingTimePeriod(newPeriod); } else if (totalPeriod.getEnding().getDate().getTime() < period.getEnding().getDate().getTime()) { final Period newPeriod = SOSXmlFactory.buildTimePeriod(version, totalPeriod.getBeginning().getDate(), period.getEnding().getDate()); uniqueObs.setSamplingTimePeriod(newPeriod); } } } } else { merged.put(key, SOSXmlFactory.cloneObservation(version, obs)); } } final List<Observation> obervations = new ArrayList<>(); for (Observation entry: merged.values()) { obervations.add(entry); } return SOSXmlFactory.buildGetObservationResponse(version, "collection-1", bounds, obervations); } private static String getFeatureID(final Observation obs) { if (obs instanceof AbstractObservation) { final AbstractObservation observation = (AbstractObservation)obs; final FeatureProperty featProp = observation.getPropertyFeatureOfInterest(); if (featProp != null) { if (featProp.getHref() != null) { return featProp.getHref(); } else if (featProp.getAbstractFeature() != null) { final AbstractFeature feature = featProp.getAbstractFeature(); if (feature.getName() != null) { return feature.getName().getCode(); } else if (feature.getId() != null) { return feature.getId(); } } } } return null; } /** * Normalize the Observation collection document by replacing the double by reference * * @param collection the unnormalized document. * * @return a normalized document */ public static ObservationCollection normalizeDocument(final String version, final ObservationCollection collection) { //first if the collection is empty if (collection.getMember().isEmpty()) { return SOSXmlFactory.buildObservationCollection(version, "urn:ogc:def:nil:OGC:inapplicable"); } final List<FeatureProperty> foiAlreadySee = new ArrayList<>(); final List<PhenomenonProperty> phenoAlreadySee = new ArrayList<>(); final List<AbstractEncodingProperty> encAlreadySee = new ArrayList<>(); final List<DataComponentProperty> dataAlreadySee = new ArrayList<>(); for (Observation observation: collection.getMember()) { //we do this for the feature of interest final FeatureProperty foi = getPropertyFeatureOfInterest(observation); if (foi != null) { if (foiAlreadySee.contains(foi)){ foi.setToHref(); } else { foiAlreadySee.add(foi); } } //for the phenomenon final PhenomenonProperty phenomenon = getPhenomenonProperty(observation); if (phenomenon != null) { if (phenoAlreadySee.contains(phenomenon)){ phenomenon.setToHref(); } else { if (phenomenon.getPhenomenon() instanceof CompositePhenomenonType) { final CompositePhenomenonType compo = (CompositePhenomenonType) phenomenon.getPhenomenon(); for (PhenomenonProperty pheno2: compo.getRealComponent()) { if (phenoAlreadySee.contains(pheno2)) { pheno2.setToHref(); } else { phenoAlreadySee.add(pheno2); } } } phenoAlreadySee.add(phenomenon); } } //for the result : textBlock encoding and element type if (observation.getResult() instanceof DataArrayProperty) { final DataArray array = ((DataArrayProperty)observation.getResult()).getDataArray(); //element type final DataComponentProperty elementType = array.getPropertyElementType(); if (dataAlreadySee.contains(elementType)){ elementType.setToHref(); } else { dataAlreadySee.add(elementType); } //encoding final AbstractEncodingProperty encoding = array.getPropertyEncoding(); if (encAlreadySee.contains(encoding)){ encoding.setToHref(); } else { encAlreadySee.add(encoding); } } else if (observation.getResult() instanceof MeasureType) { // do nothing } else { if (observation.getResult() != null) { LOGGER.log(Level.WARNING, "NormalizeDocument: Class not recognized for result:{0}", observation.getResult().getClass().getSimpleName()); } else { LOGGER.warning("NormalizeDocument: The result is null"); } } } return collection; } private static FeatureProperty getPropertyFeatureOfInterest(final Observation obs) { if (obs instanceof AbstractObservation) { final AbstractObservation observation = (AbstractObservation)obs; return observation.getPropertyFeatureOfInterest(); } return null; } private static PhenomenonProperty getPhenomenonProperty(final Observation obs) { if (obs instanceof AbstractObservation) { final AbstractObservation observation = (AbstractObservation)obs; return observation.getPropertyObservedProperty(); } return null; } }