/*
* 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.rest.api;
import static org.constellation.utils.RESTfulUtilities.ok;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.constellation.ServiceDef;
import org.constellation.business.IProviderBusiness;
import org.constellation.business.ISensorBusiness;
import org.constellation.configuration.AcknowlegementType;
import org.constellation.configuration.ConfigurationException;
import org.constellation.configuration.NotRunningServiceException;
import org.constellation.configuration.StringList;
import org.constellation.dto.ObservationFilter;
import org.constellation.dto.ParameterValues;
import org.constellation.dto.SimpleValue;
import org.constellation.database.api.jooq.tables.pojos.Data;
import org.constellation.database.api.jooq.tables.pojos.Sensor;
import org.constellation.provider.DataProviders;
import org.constellation.provider.Provider;
import org.constellation.provider.coveragestore.CoverageStoreProvider;
import org.constellation.provider.observationstore.ObservationStoreProvider;
import org.constellation.sos.configuration.SOSConfigurer;
import org.constellation.sos.configuration.SensorMLGenerator;
import org.constellation.sos.ws.SOSUtils;
import org.constellation.ws.ServiceConfigurer;
import org.geotoolkit.gml.xml.v321.AbstractGeometryType;
import org.geotoolkit.sml.xml.AbstractSensorML;
import org.geotoolkit.sos.netcdf.ExtractionResult;
import org.geotoolkit.sos.netcdf.ExtractionResult.ProcedureTree;
import org.springframework.web.bind.annotation.RequestParam;
/**
*
* @author Guilhem Legal (Geomatys)
*/
@Path("/1/SOS")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class SOSRest {
@Inject
private ISensorBusiness sensorBusiness;
@Inject
private IProviderBusiness providerBusiness;
@GET
@Path("{id}/build")
public Response buildDatasourceOM(final @PathParam("id") String id) throws Exception {
final AcknowlegementType ack;
if (getConfigurer().buildDatasource(id)) {
ack = AcknowlegementType.success("O&M datasource created");
} else {
ack = AcknowlegementType.failure("error while creating O&M datasource");
}
return ok(ack);
}
@PUT
@Path("{id}/sensors")
public Response importSensorMetadata(final @PathParam("id") String id, final File sensor) throws Exception {
return ok(getConfigurer().importSensor(id, sensor, "xml"));
}
@DELETE
@Path("{id}/sensor/{sensorID}")
public Response removeSensor(final @PathParam("id") String id, final @PathParam("sensorID") String sensorID) throws Exception {
return ok(getConfigurer().removeSensor(id, sensorID));
}
@DELETE
@Path("{id}/sensors")
public Response removeAllSensor(final @PathParam("id") String id) throws Exception {
return ok(getConfigurer().removeAllSensors(id));
}
@GET
@Path("{id}/sensor/{sensorID}")
public Response getSensorMetadata(final @PathParam("id") String id, final @PathParam("sensorID") String sensorID) throws Exception {
return ok(getConfigurer().getSensor(id, sensorID));
}
@GET
@Path("{id}/sensors")
public Response getSensorTree(final @PathParam("id") String id) throws Exception {
return ok(getConfigurer().getSensorTree(id));
}
@GET
@Path("{id}/sensors/identifiers")
public Response getSensorIds(final @PathParam("id") String id) throws Exception {
return ok(new StringList(getConfigurer().getSensorIds(id)));
}
@POST
@Path("{id}/sensors/identifiers/id")
public Response getSensorIdsForObservedProperty(final @PathParam("id") String id, final @RequestParam("observedProperty") String observedProperty) throws Exception {
return ok(new StringList(getConfigurer().getSensorIdsForObservedProperty(id, observedProperty)));
}
@GET
@Path("{id}/sensors/count")
public Response getSensortCount(final @PathParam("id") String id) throws Exception {
return ok(new SimpleValue(getConfigurer().getSensorCount(id)));
}
@PUT
@Path("{id}/sensor/location/{sensorID}")
public Response updateSensorLocation(final @PathParam("id") String id, final @PathParam("sensorID") String sensorID, final AbstractGeometryType location) throws Exception {
return ok(getConfigurer().updateSensorLocation(id, sensorID, location));
}
@POST
@Path("{id}/sensor/location/id")
public Response getWKTSensorLocation(final @PathParam("id") String id, final SimpleValue value) throws Exception {
return ok(new SimpleValue(getConfigurer().getWKTSensorLocation(id, value.getValue())));
}
@POST
@Path("{id}/observedProperty/identifiers/id")
public Response getObservedPropertiesForSensor(final @PathParam("id") String id, final SimpleValue value) throws Exception {
return ok(new StringList(getConfigurer().getObservedPropertiesForSensorId(id, value.getValue())));
}
@GET
@Path("{id}/time/{sensorID}")
public Response getTimeForSensor(final @PathParam("id") String id, final @PathParam("sensorID") String sensorID) throws Exception {
return ok(getConfigurer().getTimeForSensorId(id, sensorID));
}
@POST
@Path("{id}/observations")
public Response getObservations(final @PathParam("id") String id, final ObservationFilter filter) throws Exception {
return ok(getConfigurer().getDecimatedObservationsCsv(id, filter.getSensorID(), filter.getObservedProperty(), filter.getStart(), filter.getEnd(), filter.getWidth()));
}
@PUT
@Path("{id}/observations")
public Response importObservation(final @PathParam("id") String id, final File obs) throws Exception {
return ok(getConfigurer().importObservations(id, obs));
}
@DELETE
@Path("{id}/observation/{observationID}")
public Response removeObservation(final @PathParam("id") String id, final @PathParam("observationID") String observationID) throws Exception {
return ok(getConfigurer().removeSingleObservation(id, observationID));
}
@DELETE
@Path("{id}/observation/procedure/{procedureID}")
public Response removeObservationForProcedure(final @PathParam("id") String id, final @PathParam("procedureID") String procedureID) throws Exception {
return ok(getConfigurer().removeObservationForProcedure(id, procedureID));
}
@GET
@Path("{id}/observedProperties/identifiers")
public Response getObservedPropertiesIds(final @PathParam("id") String id) throws Exception {
return ok(new StringList(getConfigurer().getObservedPropertiesIds(id)));
}
@PUT
@Path("{id}/data/import")
public Response importSensorFromData(final @PathParam("id") String id, final ParameterValues params) throws Exception {
final String providerId = params.get("providerId");
final String dataId = params.get("dataId");
final Provider provider = DataProviders.getInstance().getProvider(providerId);
final ExtractionResult result;
if (provider instanceof ObservationStoreProvider) {
final ObservationStoreProvider omProvider = (ObservationStoreProvider) provider;
result = omProvider.getObservationStore().getResults();
} else if (provider instanceof CoverageStoreProvider) {
final CoverageStoreProvider covProvider = (CoverageStoreProvider) provider;
if (covProvider.isSensorAffectable()) {
result = covProvider.getObservationStore().getResults();
} else {
return ok(new AcknowlegementType("Failure", "Only available on netCDF file for coverage for now"));
}
} else {
return ok(new AcknowlegementType("Failure", "Available only on Observation provider (and netCDF coverage) for now"));
}
final SOSConfigurer configurer = getConfigurer();
// import in O&M database
configurer.importObservations(id, result.observations, result.phenomenons);
// SensorML generation
for (ProcedureTree process : result.procedures) {
generateSensorML(id, process, result, configurer);
}
return ok(new AcknowlegementType("Success", "The specified observations have been imported in the SOS"));
}
private void generateSensorML(final String id, final ProcedureTree process, final ExtractionResult result, final SOSConfigurer configurer) throws ConfigurationException {
final Properties prop = new Properties();
prop.put("id", process.id);
if (process.spatialBound.dateStart != null) {
prop.put("beginTime", process.spatialBound.dateStart);
}
if (process.spatialBound.dateEnd != null) {
prop.put("endTime", process.spatialBound.dateEnd);
}
if (process.spatialBound.minx != null) {
prop.put("longitude", process.spatialBound.minx);
}
if (process.spatialBound.miny != null) {
prop.put("latitude", process.spatialBound.miny);
}
prop.put("phenomenon", result.fields);
final List<String> component = new ArrayList<>();
for (ProcedureTree child : process.children) {
component.add(child.id);
generateSensorML(id, child, result, configurer);
}
prop.put("component", component);
final AbstractSensorML sml = SensorMLGenerator.getTemplateSensorML(prop, process.type);
configurer.importSensor(id, sml, process.id);
//record location
final AbstractGeometryType geom = (AbstractGeometryType) process.spatialBound.getGeometry("2.0.0");
if (geom != null) {
configurer.updateSensorLocation(id, process.id, geom);
}
}
private void writeProcedures(final String id, final ProcedureTree process, final String parent, final SOSConfigurer configurer) throws ConfigurationException {
final AbstractGeometryType geom = (AbstractGeometryType) process.spatialBound.getGeometry("2.0.0");
configurer.writeProcedure(id, process.id, geom, parent, process.type);
for (ProcedureTree child : process.children) {
writeProcedures(id, child, process.id, configurer);
}
}
@PUT
@Path("{id}/sensor/import")
public Response importSensor(final @PathParam("id") String id, final ParameterValues params) throws Exception {
final String sensorID = params.get("sensorId");
final Sensor sensor = sensorBusiness.getSensor(sensorID);
final List<Data> datas = sensorBusiness.getLinkedData(sensor);
final SOSConfigurer configurer = getConfigurer();
final List<String> sensorIds = new ArrayList<>();
//import SML
final AbstractSensorML sml = SOSUtils.unmarshallSensor(sensor.getMetadata());
configurer.importSensor(id, sml, sensorID);
sensorIds.add(sensorID);
//import sensor children
final List<Sensor> sensors = sensorBusiness.getChildren(sensor);
for (Sensor child : sensors) {
final AbstractSensorML smlChild = SOSUtils.unmarshallSensor(child.getMetadata());
configurer.importSensor(id, smlChild, child.getIdentifier());
datas.addAll(sensorBusiness.getLinkedData(child));
sensorIds.add(child.getIdentifier());
}
// look for provider ids
final Set<String> providerIDs = new HashSet<>();
for (Data data : datas) {
final org.constellation.database.api.jooq.tables.pojos.Provider provider = providerBusiness.getProvider(data.getProvider());
providerIDs.add(provider.getIdentifier());
}
// import observations
for (String providerId : providerIDs) {
final Provider provider = DataProviders.getInstance().getProvider(providerId);
final ExtractionResult result;
if (provider instanceof ObservationStoreProvider) {
final ObservationStoreProvider omProvider = (ObservationStoreProvider) provider;
result = omProvider.getObservationStore().getResults(sensorID, sensorIds);
} else if (provider instanceof CoverageStoreProvider) {
final CoverageStoreProvider covProvider = (CoverageStoreProvider) provider;
if (covProvider.isSensorAffectable()) {
result = covProvider.getObservationStore().getResults(sensorID, sensorIds);
} else {
return ok(new AcknowlegementType("Failure", "Only available on netCDF file for coverage for now"));
}
} else {
return ok(new AcknowlegementType("Failure", "Available only on Observation provider (and netCDF coverage) for now"));
}
// update sensor location
for (ProcedureTree process : result.procedures) {
writeProcedures(id, process, null, configurer);
}
// import in O&M database
configurer.importObservations(id, result.observations, result.phenomenons);
}
return ok(new AcknowlegementType("Success", "The specified sensor has been imported in the SOS"));
}
private static SOSConfigurer getConfigurer() throws NotRunningServiceException {
return (SOSConfigurer) ServiceConfigurer.newInstance(ServiceDef.Specification.SOS);
}
}