/* * 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.coverage.ws; // J2SE dependencies import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.storage.DataStoreException; import org.apache.sis.xml.MarshallerPool; import org.constellation.Cstl; import org.constellation.ServiceDef; import org.constellation.api.QueryConstants; import org.constellation.configuration.Layer; import org.constellation.dto.Details; import org.constellation.portrayal.PortrayalUtil; import org.constellation.provider.CoverageData; import org.constellation.provider.Data; import org.constellation.util.DataReference; import org.constellation.util.WCSUtils; import org.constellation.ws.CstlServiceException; import org.constellation.ws.LayerWorker; import org.constellation.ws.MimeType; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.display2d.service.CanvasDef; import org.geotoolkit.display2d.service.SceneDef; import org.geotoolkit.display2d.service.ViewDef; import org.geotoolkit.geometry.jts.JTSEnvelope2D; import org.geotoolkit.gml.xml.v311.DirectPositionType; import org.geotoolkit.gml.xml.v311.EnvelopeType; import org.geotoolkit.gml.xml.v311.GridType; import org.geotoolkit.gml.xml.v311.RectifiedGridType; import org.geotoolkit.image.io.metadata.SpatialMetadata; import org.geotoolkit.map.MapContext; import org.geotoolkit.ows.xml.AbstractCapabilitiesCore; import org.geotoolkit.ows.xml.AbstractOperationsMetadata; import org.geotoolkit.ows.xml.AbstractServiceIdentification; import org.geotoolkit.ows.xml.AbstractServiceProvider; import org.geotoolkit.ows.xml.AcceptFormats; import org.geotoolkit.ows.xml.Sections; import org.geotoolkit.ows.xml.v110.BoundingBoxType; import org.geotoolkit.ows.xml.v110.SectionsType; import org.geotoolkit.ows.xml.v110.WGS84BoundingBoxType; import org.geotoolkit.referencing.CRS; import org.apache.sis.referencing.CommonCRS; import org.geotoolkit.resources.Errors; import org.geotoolkit.style.MutableStyle; import org.geotoolkit.temporal.util.TimeParser; import org.geotoolkit.wcs.xml.Content; import org.geotoolkit.wcs.xml.CoverageInfo; import org.geotoolkit.wcs.xml.DescribeCoverage; import org.geotoolkit.wcs.xml.DescribeCoverageResponse; import org.geotoolkit.wcs.xml.GetCapabilities; import org.geotoolkit.wcs.xml.GetCapabilitiesResponse; import org.geotoolkit.wcs.xml.GetCoverage; import org.geotoolkit.wcs.xml.WCSMarshallerPool; import org.geotoolkit.wcs.xml.WCSXmlFactory; import org.geotoolkit.wcs.xml.v100.CoverageOfferingType; import org.geotoolkit.wcs.xml.v100.DomainSetType; import org.geotoolkit.wcs.xml.v100.InterpolationMethod; import org.geotoolkit.wcs.xml.v100.LonLatEnvelopeType; import org.geotoolkit.wcs.xml.v100.RangeSetType; import org.geotoolkit.wcs.xml.v100.SupportedCRSsType; import org.geotoolkit.wcs.xml.v100.SupportedFormatsType; import org.geotoolkit.wcs.xml.v100.SupportedInterpolationsType; import org.geotoolkit.wcs.xml.v111.CoverageDescriptionType; import org.geotoolkit.wcs.xml.v111.CoverageDomainType; import org.geotoolkit.wcs.xml.v111.FieldType; import org.geotoolkit.wcs.xml.v111.GridCrsType; import org.geotoolkit.wcs.xml.v111.InterpolationMethods; import org.geotoolkit.wcs.xml.v111.RangeType; import org.geotoolkit.swe.xml.v200.Field; import org.opengis.coverage.grid.RectifiedGrid; import org.opengis.geometry.Envelope; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.ParseException; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.logging.Level; import javax.ws.rs.core.MediaType; import javax.xml.namespace.QName; import org.apache.sis.geometry.Envelopes; import org.apache.sis.util.CharSequences; import static org.constellation.coverage.ws.WCSConstant.ASCII_GRID; import static org.constellation.coverage.ws.WCSConstant.GEOTIFF; import static org.constellation.coverage.ws.WCSConstant.INTERPOLATION_V100; import static org.constellation.coverage.ws.WCSConstant.INTERPOLATION_V111; import static org.constellation.coverage.ws.WCSConstant.KEY_BBOX; import static org.constellation.coverage.ws.WCSConstant.KEY_COVERAGE; import static org.constellation.coverage.ws.WCSConstant.KEY_CRS; import static org.constellation.coverage.ws.WCSConstant.KEY_FORMAT; import static org.constellation.coverage.ws.WCSConstant.KEY_IDENTIFIER; import static org.constellation.coverage.ws.WCSConstant.KEY_INTERPOLATION; import static org.constellation.coverage.ws.WCSConstant.KEY_RESPONSE_CRS; import static org.constellation.coverage.ws.WCSConstant.KEY_SECTION; import static org.constellation.coverage.ws.WCSConstant.KEY_TIME; import static org.constellation.coverage.ws.WCSConstant.MATRIX; import static org.constellation.coverage.ws.WCSConstant.NETCDF; import static org.constellation.coverage.ws.WCSConstant.SUPPORTED_FORMATS_100; import static org.constellation.coverage.ws.WCSConstant.SUPPORTED_FORMATS_111; import static org.constellation.coverage.ws.WCSConstant.SUPPORTED_INTERPOLATIONS_V100; import static org.constellation.coverage.ws.WCSConstant.getOperationMetadata; import org.constellation.coverage.ws.rs.GeotiffResponse; import org.constellation.coverage.ws.rs.GridCoverageNCWriter; import org.constellation.coverage.ws.rs.GridCoverageWriter; import org.constellation.coverage.ws.rs.WCSResponseWrapper; import org.constellation.ws.ExceptionCode; import static org.constellation.ws.ExceptionCode.AXIS_LABEL_INVALID; import org.geotoolkit.coverage.Category; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.util.NamesExt; import org.geotoolkit.gml.xml.v321.AssociationRoleType; import org.geotoolkit.gml.xml.v321.FileType; import org.geotoolkit.gmlcov.geotiff.xml.v100.CompressionType; import org.geotoolkit.gmlcov.geotiff.xml.v100.ParametersType; import org.geotoolkit.gmlcov.xml.v100.AbstractDiscreteCoverageType; import org.geotoolkit.gmlcov.xml.v100.ObjectFactory; import org.geotoolkit.ows.xml.BoundingBox; import static org.geotoolkit.ows.xml.OWSExceptionCode.CURRENT_UPDATE_SEQUENCE; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_CRS; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_DIMENSION_VALUE; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_FORMAT; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_PARAMETER_VALUE; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_UPDATE_SEQUENCE; import static org.geotoolkit.ows.xml.OWSExceptionCode.LAYER_NOT_DEFINED; import static org.geotoolkit.ows.xml.OWSExceptionCode.LAYER_NOT_QUERYABLE; import static org.geotoolkit.ows.xml.OWSExceptionCode.MISSING_PARAMETER_VALUE; import static org.geotoolkit.ows.xml.OWSExceptionCode.NO_APPLICABLE_CODE; import static org.geotoolkit.ows.xml.OWSExceptionCode.VERSION_NEGOTIATION_FAILED; import org.geotoolkit.swe.xml.v200.AllowedValuesPropertyType; import org.geotoolkit.swe.xml.v200.AllowedValuesType; import org.geotoolkit.swe.xml.v200.DataRecordPropertyType; import org.geotoolkit.swe.xml.v200.DataRecordType; import org.geotoolkit.swe.xml.v200.QuantityType; import org.geotoolkit.swe.xml.v200.UnitReference; import org.geotoolkit.wcs.xml.DomainSubset; import org.geotoolkit.wcs.xml.ServiceMetadata; import org.geotoolkit.wcs.xml.v200.DimensionSliceType; import org.geotoolkit.wcs.xml.v200.DimensionTrimType; import org.geotoolkit.wcs.xml.v200.ExtensionType; import org.geotoolkit.wcs.xml.v200.ServiceParametersType; import org.glassfish.jersey.media.multipart.BodyPart; import org.glassfish.jersey.media.multipart.MultiPart; import org.opengis.coverage.grid.GridEnvelope; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; import org.opengis.util.GenericName; // GeoAPI dependencies /** * Worker for the WCS services in Constellation which services both the REST and * SOAP facades by issuing appropriate responses. * <p> * The classes implementing the REST or SOAP facades to this service will have * processed the requests sufficiently to ensure that all the information * conveyed by the HTTP request is in one of the fields of the object passed * to the worker methods as a parameter. * </p> * * @version 0.9 * * @author Cédric Briançon (Geomatys) * @author Guilhem Legal (Geomatys) * @since 0.3 */ public final class DefaultWCSWorker extends LayerWorker implements WCSWorker { public DefaultWCSWorker(final String id) { super(id, ServiceDef.Specification.WCS); if (isStarted) { LOGGER.log(Level.INFO, "WCS worker {0} running", id); } } /** * {@inheritDoc } */ @Override protected MarshallerPool getMarshallerPool() { return WCSMarshallerPool.getInstance(); } /** * The DescribeCoverage operation returns an XML file, containing the * complete description of the specific coverages requested. * <p> * This method extends the definition of each coverage given in the * Capabilities document with supplementary information. * </p> * * @param request A {@linkplain AbstractDescribeCoverage request} with the * parameters of the user message. * @return An XML document giving the full description of the requested coverages. * @throws CstlServiceException */ @Override public DescribeCoverageResponse describeCoverage(final DescribeCoverage request) throws CstlServiceException { isWorking(); final String version = request.getVersion().toString(); final String userLogin = getUserLogin(); if (version.isEmpty()) { throw new CstlServiceException("The parameter VERSION must be specified.", MISSING_PARAMETER_VALUE, QueryConstants.VERSION_PARAMETER.toLowerCase()); } if (request.getIdentifier().isEmpty()) { throw new CstlServiceException("The parameter IDENTIFIER must be specified", MISSING_PARAMETER_VALUE, KEY_IDENTIFIER.toLowerCase()); } final List<CoverageInfo> coverageOfferings = new ArrayList<>(); for (String coverage : request.getIdentifier()) { final GenericName tmpName = parseCoverageName(coverage); final Data layerRef = getLayerReference(userLogin, tmpName); if (layerRef.getType().equals(Data.TYPE.FEATURE)) { throw new CstlServiceException("The requested layer is vectorial. WCS is not able to handle it.", LAYER_NOT_DEFINED, KEY_COVERAGE.toLowerCase()); } if (!(layerRef instanceof CoverageData)) { // Should not occurs, since we have previously verified the type of layer. throw new CstlServiceException("The requested layer is not a coverage. WCS is not able to handle it.", LAYER_NOT_DEFINED, KEY_COVERAGE.toLowerCase()); } final CoverageData coverageRef = (CoverageData) layerRef; if (!coverageRef.isQueryable(ServiceDef.Query.WCS_ALL)) { throw new CstlServiceException("You are not allowed to request the layer \"" + coverage + "\".", LAYER_NOT_QUERYABLE, KEY_COVERAGE.toLowerCase()); } final Layer configLayer = getConfigurationLayer(layerRef.getName(), userLogin); final GenericName fullCoverageName = coverageRef.getName(); final String coverageName; if (configLayer.getAlias() != null && !configLayer.getAlias().isEmpty()) { coverageName = configLayer.getAlias().trim().replaceAll(" ", "_"); } else { if (NamesExt.getNamespace(fullCoverageName) != null && !NamesExt.getNamespace(fullCoverageName).isEmpty()) { coverageName = NamesExt.getNamespace(fullCoverageName) + ':' + fullCoverageName.tip().toString(); } else { coverageName = fullCoverageName.tip().toString(); } } if (version.equals("1.0.0")) { coverageOfferings.add(describeCoverage100(coverageName, coverageRef)); } else if (version.equals("1.1.1")) { coverageOfferings.add(describeCoverage111(coverageName, coverageRef)); } else if (version.equals("2.0.1")) { coverageOfferings.add(describeCoverage200(coverageName, coverageRef)); } else { throw new CstlServiceException("The version number specified for this GetCoverage request " + "is not handled.", NO_APPLICABLE_CODE, QueryConstants.VERSION_PARAMETER.toLowerCase()); } } return WCSXmlFactory.createDescribeCoverageResponse(version, coverageOfferings); } /** * Returns the description of the coverage requested in version 1.0.0 of WCS standard. * * @param request a {@linkplain org.geotoolkit.wcs.xml.v100.DescribeCoverage describe coverage} * request done by the user. * @return an XML document giving the full description of a coverage, in version 1.0.0. * * @throws CstlServiceException */ private CoverageInfo describeCoverage100(final String coverageName, final CoverageData coverageRef) throws CstlServiceException { try { final GeographicBoundingBox inputGeoBox = coverageRef.getGeographicBoundingBox(); final List<EnvelopeType> envelopes = new ArrayList<>(); final LonLatEnvelopeType llenvelope; final EnvelopeType envelope; if (inputGeoBox != null) { final SortedSet<Number> elevations = coverageRef.getAvailableElevations(); final List<DirectPositionType> pos = WCSUtils.buildPositions(inputGeoBox, elevations); llenvelope = new LonLatEnvelopeType(pos, "urn:ogc:def:crs:OGC:1.3:CRS84"); envelope = new EnvelopeType(pos, "urn:ogc:def:crs:EPSG::4326"); envelopes.add(envelope); } else { throw new CstlServiceException("The geographic bbox for the layer is null !", NO_APPLICABLE_CODE); } final List<String> keywords = Arrays.asList("WCS", coverageName); /* * Spatial metadata */ final EnvelopeType nativeEnvelope = new EnvelopeType(coverageRef.getEnvelope()); if (!envelopes.contains(nativeEnvelope)) { envelopes.add(nativeEnvelope); } GridType grid = null; try { SpatialMetadata meta = coverageRef.getSpatialMetadata(); if (meta != null) { RectifiedGrid brutGrid = meta.getInstanceForType(RectifiedGrid.class); if (brutGrid != null) { grid = new RectifiedGridType(brutGrid); /* * UGLY PATCH : remove it when geotk will fill this data */ if (grid.getDimension() == 0) { int dimension = brutGrid.getOffsetVectors().size(); grid.setDimension(dimension); } if (grid.getAxisName().isEmpty()) { if (grid.getDimension() == 2) { grid.setAxisName(Arrays.asList("x", "y")); } else if (grid.getDimension() == 3) { grid.setAxisName(Arrays.asList("x", "y", "z")); } } } } } catch (DataStoreException ex) { LOGGER.log(Level.WARNING, "Unable to get coverage spatial metadata", ex); } final org.geotoolkit.wcs.xml.v100.SpatialDomainType spatialDomain = new org.geotoolkit.wcs.xml.v100.SpatialDomainType(envelopes, Arrays.asList(grid)); // temporal metadata final List<Object> times = WCSUtils.formatDateList(coverageRef.getAvailableTimes()); final DomainSetType domainSet = new DomainSetType(spatialDomain, times); //TODO complete final RangeSetType rangeSet = new RangeSetType(null, coverageName, coverageName, null, null, null, null); //supported CRS final SupportedCRSsType supCRS = new SupportedCRSsType("urn:ogc:def:crs:EPSG::4326"); supCRS.addNativeCRSs(nativeEnvelope.getSrsName()); // supported formats String nativeFormat = coverageRef.getImageFormat(); if (nativeFormat == null || nativeFormat.isEmpty()) { nativeFormat = "unknown"; } final SupportedFormatsType supForm = new SupportedFormatsType(nativeFormat, SUPPORTED_FORMATS_100); //supported interpolations final SupportedInterpolationsType supInt = INTERPOLATION_V100; //we build the coverage offering for this layer/coverage CharSequence remarks = CharSequences.toASCII(coverageRef.getRemarks()); return new CoverageOfferingType(null, coverageName, coverageName, (remarks != null) ? remarks.toString() : null, llenvelope, keywords, domainSet, rangeSet, supCRS, supForm, supInt); } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } /** * Returns the description of the coverage requested in version 1.1.1 of WCS standard. * * @param request a {@linkplain org.geotoolkit.wcs.xml.v111.DescribeCoverage describe coverage} * request done by the user. * @return an XML document giving the full description of a coverage, in version 1.1.1. * * @throws CstlServiceException */ private CoverageInfo describeCoverage111(final String coverageName, final CoverageData coverageRef) throws CstlServiceException { try { final GeographicBoundingBox inputGeoBox = coverageRef.getGeographicBoundingBox(); WGS84BoundingBoxType outputBBox = null; if (inputGeoBox != null) { outputBBox = new WGS84BoundingBoxType(inputGeoBox); } /* * Spatial metadata */ final BoundingBoxType nativeEnvelope = new BoundingBoxType(coverageRef.getEnvelope()); GridCrsType grid = null; try { SpatialMetadata meta = coverageRef.getSpatialMetadata(); if (meta != null) { RectifiedGrid brutGrid = meta.getInstanceForType(RectifiedGrid.class); if (brutGrid != null) { grid = new GridCrsType(brutGrid); } } } catch (DataStoreException ex) { LOGGER.log(Level.WARNING, "Unable to get coverage spatial metadata", ex); } // spatial metadata final org.geotoolkit.wcs.xml.v111.SpatialDomainType spatial = new org.geotoolkit.wcs.xml.v111.SpatialDomainType(outputBBox,nativeEnvelope, grid, null, null, null); //general metadata final String title = coverageName; final CharSequence abstractt = CharSequences.toASCII(coverageRef.getRemarks()); final List<String> keywords = Arrays.asList("WCS", coverageName); // temporal metadata final List<Object> times = WCSUtils.formatDateList(coverageRef.getAvailableTimes()); final CoverageDomainType domain = new CoverageDomainType(spatial, times); //supported interpolations final InterpolationMethods interpolations = INTERPOLATION_V111; final CharSequence thematic = CharSequences.toASCII(coverageRef.getThematic()); final RangeType range = new RangeType(new FieldType((thematic != null) ? thematic.toString() : null, null, new org.geotoolkit.ows.xml.v110.CodeType("0.0"), interpolations)); //supported CRS final List<String> supportedCRS = Arrays.asList("urn:ogc:def:crs:EPSG::4326"); return new CoverageDescriptionType(title, (abstractt != null) ? abstractt.toString() : null, keywords, coverageName, domain, range, supportedCRS, SUPPORTED_FORMATS_111); } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } /** * Returns the description of the coverage requested in version 2.0.1 of WCS standard. * * @param request a {@linkplain org.geotoolkit.wcs.xml.v200.DescribeCoverage describe coverage} * request done by the user. * @return an XML document giving the full description of a coverage, in version 2.0.1. * * @throws CstlServiceException */ private org.geotoolkit.wcs.xml.v200.CoverageDescriptionType describeCoverage200(final String coverageName, final CoverageData coverageRef) throws CstlServiceException { try { /* * Spatial metadata */ final org.geotoolkit.gml.xml.v321.EnvelopeType nativeEnvelope = new org.geotoolkit.gml.xml.v321.EnvelopeType(coverageRef.getEnvelope()); org.geotoolkit.gml.xml.v321.GridType grid = null; try { SpatialMetadata meta = coverageRef.getSpatialMetadata(); if (meta != null) { RectifiedGrid brutGrid = meta.getInstanceForType(RectifiedGrid.class); if (brutGrid.getExtent() != null) { grid = new org.geotoolkit.gml.xml.v321.RectifiedGridType(brutGrid, meta.getInstanceForType(CoordinateReferenceSystem.class)); } } } catch (DataStoreException ex) { LOGGER.log(Level.WARNING, "Unable to get coverage spatial metadata", ex); } // spatial metadata final org.geotoolkit.gml.xml.v321.DomainSetType domain = new org.geotoolkit.gml.xml.v321.DomainSetType(grid); final List<GridSampleDimension> bands = coverageRef.getSampleDimensions(); final List<Field> fields = new ArrayList<>(); if (bands != null) { for (GridSampleDimension band : bands) { final QuantityType quantity = new QuantityType(); if (band.getUnits() != null) { quantity.setUom(new UnitReference(band.getUnits().toString())); } // TODO select only one category => which one? for (Category cat : band.getCategories()) { final AllowedValuesType av = new AllowedValuesType(); if (cat.getName() != null) { av.setId(cat.getName().toString()); } if (cat.getRange() != null) { av.setMin(cat.getRange().getMinDouble()); av.setMax(cat.getRange().getMaxDouble()); } quantity.setConstraint(new AllowedValuesPropertyType(av)); } final Field f = new Field(band.getDescription().toString(), quantity); fields.add(f); } } final DataRecordType dataRecord = new DataRecordType(null, null, false, fields); final DataRecordPropertyType rangeType = new DataRecordPropertyType(dataRecord); final ServiceParametersType serviceParametersType = new ServiceParametersType(new QName("GridCoverage"), coverageRef.getImageFormat()); return new org.geotoolkit.wcs.xml.v200.CoverageDescriptionType(coverageName, nativeEnvelope, domain, rangeType, serviceParametersType); } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } /** * Describe the capabilities and the layers available for the WCS service. * * @param request The request done by the user. * @return a WCSCapabilities XML document describing the capabilities of this service. * * @throws CstlServiceException */ @Override public GetCapabilitiesResponse getCapabilities(final GetCapabilities request) throws CstlServiceException { isWorking(); //we begin by extract the base attribute String version = request.getVersion().toString(); final String userLogin = getUserLogin(); if (version.isEmpty()) { // For the moment the only version that we really support is this one. version = "1.0.0"; } //set the current updateSequence parameter final boolean returnUS = returnUpdateSequenceDocument(request.getUpdateSequence(), version); if (returnUS) { return WCSXmlFactory.createCapabilitiesResponse(version, getCurrentUpdateSequence()); } /* * In WCS 1.0.0 the user can request only one section * ( or all by omitting the parameter section) */ final Sections sections = request.getSections(); if (sections != null && !sections.getSection().isEmpty()) { for (String sec : sections.getSection()) { if (!SectionsType.getExistingSections(version).contains(sec)) { throw new CstlServiceException("This sections " + sec + " is not allowed", INVALID_PARAMETER_VALUE, KEY_SECTION.toLowerCase()); } } } // if the user have specified one format accepted (only one for now != spec) final String format; if (version.equals("1.1.1")) { final AcceptFormats formats = request.getAcceptFormats(); if (formats == null || formats.getOutputFormat().isEmpty()) { format = MimeType.TEXT_XML; } else { format = formats.getOutputFormat().get(0); if (!format.equals(MimeType.TEXT_XML) && !format.equals(MimeType.APP_XML)) { throw new CstlServiceException("This format " + format + " is not allowed", INVALID_FORMAT, KEY_FORMAT.toLowerCase()); } } } // If the getCapabilities response is in cache, we just return it. final AbstractCapabilitiesCore cachedCapabilities = getCapabilitiesFromCache(version, null); if (cachedCapabilities != null) { return (GetCapabilitiesResponse) cachedCapabilities.applySections(sections); } // We unmarshall the static capabilities document. final Details skeleton = getStaticCapabilitiesObject("WCS", null); final GetCapabilitiesResponse staticCapabilities = WCSConstant.createCapabilities(version, skeleton); final AbstractServiceIdentification si = staticCapabilities.getServiceIdentification(); final AbstractServiceProvider sp = staticCapabilities.getServiceProvider(); final AbstractOperationsMetadata om = getOperationMetadata(version); om.updateURL(getServiceUrl()); WCSConstant.applyProfile(version, si); final List<CoverageInfo> offBrief = new ArrayList<>(); final List<Layer> layers = getConfigurationLayers(userLogin); try { for (Layer configLayer : layers) { final Data layer = getLayerReference(configLayer); if (layer == null) { throw new CstlServiceException("There is no existing layer named:" + configLayer.getName()); } if (layer.getType().equals(Data.TYPE.FEATURE)) { continue; } if (!layer.isQueryable(ServiceDef.Query.WCS_ALL)) { continue; } if (layer.getGeographicBoundingBox() == null) { // The coverage does not contain geometric information, we do not want this coverage // in the capabilities response. continue; } final CoverageInfo co; if (version.equals("1.0.0")) { co = getCoverageInfo100(layer, configLayer); } else { co = getCoverageInfo(version, layer, configLayer); } /* * coverage brief customisation */ if (configLayer.getTitle() != null) { co.setTitle(configLayer.getTitle()); } if (configLayer.getAbstrac() != null) { co.setAbstract(configLayer.getAbstrac()); } if (configLayer.getKeywords() != null && !configLayer.getKeywords().isEmpty()) { co.setKeywordValues(configLayer.getKeywords()); } if (configLayer.getMetadataURL() != null && configLayer.getMetadataURL().getOnlineResource() != null) { co.setMetadata(configLayer.getMetadataURL().getOnlineResource().getValue()); } offBrief.add(co); } } catch (DataStoreException exception) { throw new CstlServiceException(exception, NO_APPLICABLE_CODE); } final Content contents = WCSXmlFactory.createContent(version, offBrief); final ServiceMetadata sm = WCSConstant.getServiceMetadata(version); final GetCapabilitiesResponse response = WCSXmlFactory.createCapabilitiesResponse(version, si, sp, om, contents, getCurrentUpdateSequence(), sm); putCapabilitiesInCache(version, null, response); return (GetCapabilitiesResponse) response.applySections(sections); } /** * Returns the {@linkplain GetCapabilitiesResponse GetCapabilities} response of the request * given by parameter, in version 1.0.0 of WCS. * * @param request The request done by the user, in version 1.0.0. * @return a WCSCapabilities XML document describing the capabilities of this service. * * @throws CstlServiceException * @throws JAXBException when unmarshalling the default GetCapabilities file. */ private CoverageInfo getCoverageInfo100(final Data layer, final Layer configLayer) throws DataStoreException { final GenericName fullLayerName = layer.getName(); final String layerName; if (configLayer.getAlias() != null && !configLayer.getAlias().isEmpty()) { layerName = configLayer.getAlias().trim().replaceAll(" ", "_"); } else { if (NamesExt.getNamespace(fullLayerName) != null && !NamesExt.getNamespace(fullLayerName).isEmpty()) { layerName = NamesExt.getNamespace(fullLayerName) + ':' + fullLayerName.tip().toString(); } else { layerName = fullLayerName.tip().toString(); } } final GeographicBoundingBox inputGeoBox = layer.getGeographicBoundingBox(); final List<DirectPositionType> pos = WCSUtils.buildPositions(inputGeoBox, layer.getAvailableElevations()); final LonLatEnvelopeType outputBBox = new LonLatEnvelopeType(pos, "urn:ogc:def:crs:OGC:1.3:CRS84"); final SortedSet<Date> dates = layer.getAvailableTimes(); if (dates != null && dates.size() >= 2) { /* * Adds the first and last date available, since in the WCS GetCapabilities, * it is a brief description of the capabilities. * To get the whole available values, the describeCoverage request has to be * done on a specific coverage. */ final Date firstDate = dates.first(); final Date lastDate = dates.last(); synchronized(WCSUtils.FORMATTER) { outputBBox.addTimePosition(WCSUtils.FORMATTER.format(firstDate), WCSUtils.FORMATTER.format(lastDate)); } } return WCSXmlFactory.createCoverageInfo("1.0.0", layerName, layerName, null, outputBBox, null); } /** * Returns the {@linkplain GetCapabilitiesResponse GetCapabilities} response of the request given * by parameter, in version 1.1.1 of WCS. * * @param request The request done by the user, in version 1.1.1. * @return a WCSCapabilities XML document describing the capabilities of this service. * * @throws CstlServiceException */ private CoverageInfo getCoverageInfo(final String version, final Data layer, final Layer configLayer) throws DataStoreException { final CoverageData coverageLayer = (CoverageData)layer; final String identifier; if (configLayer.getAlias() != null && !configLayer.getAlias().isEmpty()) { identifier = configLayer.getAlias().trim().replaceAll(" ", "_"); } else { identifier = coverageLayer.getName().tip().toString(); } final String title = coverageLayer.getName().tip().toString(); final CharSequence remark = CharSequences.toASCII(coverageLayer.getRemarks()); final GeographicBoundingBox inputGeoBox = coverageLayer.getGeographicBoundingBox(); final BoundingBox outputBBox = WCSXmlFactory.buildWGS84BoundingBox(version, inputGeoBox); final String coverageSubType = "GridCoverage"; return WCSXmlFactory.createCoverageInfo(version, identifier, title, (remark != null) ? remark.toString() : null, outputBBox, coverageSubType); } /** * Get the coverage values for a specific coverage specified. * According to the output format chosen, the response could be an * {@linkplain RenderedImage image} or data representation. * * @param request The request done by the user. * @return An {@linkplain RenderedImage image}, or a data representation. * * @throws CstlServiceException */ @Override public Object getCoverage(final GetCoverage request) throws CstlServiceException { isWorking(); final String inputVersion = request.getVersion().toString(); final String userLogin = getUserLogin(); if (inputVersion == null) { throw new CstlServiceException("The parameter version must be specified", MISSING_PARAMETER_VALUE, QueryConstants.VERSION_PARAMETER.toLowerCase()); } else if (!"1.0.0".equals(inputVersion) && !"2.0.1".equals(inputVersion) && !"1.1.1".equals(inputVersion)) { throw new CstlServiceException("The version number specified for this request " + inputVersion + " is not handled.", VERSION_NEGOTIATION_FAILED, QueryConstants.VERSION_PARAMETER.toLowerCase()); } final String coverageName = request.getCoverage(); if (coverageName == null) { throw new CstlServiceException("You must specify the parameter: COVERAGE" , INVALID_PARAMETER_VALUE, KEY_COVERAGE.toLowerCase()); } final GenericName tmpName = parseCoverageName(request.getCoverage()); final Data tmplayerRef = getLayerReference(userLogin, tmpName); if (!tmplayerRef.isQueryable(ServiceDef.Query.WCS_ALL) || tmplayerRef.getType().equals(Data.TYPE.FEATURE)) { throw new CstlServiceException("You are not allowed to request the layer \"" + tmplayerRef.getName() + "\".", INVALID_PARAMETER_VALUE, KEY_COVERAGE.toLowerCase()); } if (!(tmplayerRef instanceof CoverageData)) { // Should not occurs, since we have previously verified the type of layer. throw new CstlServiceException("The requested layer is not a coverage. WCS is not able to handle it.", LAYER_NOT_DEFINED, KEY_COVERAGE.toLowerCase()); } final CoverageData layerRef = (CoverageData) tmplayerRef; final Layer configLayer = getConfigurationLayer(tmpName, userLogin); if ("2.0.1".equals(inputVersion)) { return getCoverage200(request, layerRef, configLayer); } Date date = null; try { date = TimeParser.toDate(request.getTime()); } catch (ParseException ex) { throw new CstlServiceException("Parsing of the date failed. Please verify that the specified" + " date is compliant with the ISO-8601 standard.", ex, INVALID_PARAMETER_VALUE, KEY_TIME.toLowerCase()); } // we verify the interpolation method even if we don't use it try { if (request.getInterpolationMethod() != null) { final InterpolationMethod interpolation = (InterpolationMethod)request.getInterpolationMethod(); if (!SUPPORTED_INTERPOLATIONS_V100.contains(interpolation)) { throw new CstlServiceException("Unsupported interpolation: " + request.getInterpolationMethod(), INVALID_PARAMETER_VALUE, KEY_INTERPOLATION.toLowerCase()); } } } catch (IllegalArgumentException ex) { throw new CstlServiceException(ex.getMessage(), INVALID_PARAMETER_VALUE, KEY_INTERPOLATION.toLowerCase()); } Envelope envelope; try { envelope = request.getEnvelope(); } catch (FactoryException ex) { throw new CstlServiceException(ex, INVALID_PARAMETER_VALUE, KEY_BBOX.toLowerCase()); } /* * Here the envelope can be null, if we have specified a TIME parameter. In this case we * do not have to test whether the bbox parameter are into the CRS axes definition. */ if (envelope != null) { // Ensures the bbox specified is inside the range of the CRS. final CoordinateReferenceSystem objectiveCrs; try { objectiveCrs = request.getCRS(); } catch (FactoryException ex) { throw new CstlServiceException(ex, INVALID_CRS, KEY_CRS.toLowerCase()); } for (int i = 0; i < objectiveCrs.getCoordinateSystem().getDimension(); i++) { final CoordinateSystemAxis axis = objectiveCrs.getCoordinateSystem().getAxis(i); if (envelope.getMaximum(i) < axis.getMinimumValue() || envelope.getMinimum(i) > axis.getMaximumValue()) { throw new CstlServiceException(Errors.format(Errors.Keys.IllegalRange_2, envelope.getMinimum(i), envelope.getMaximum(i)), INVALID_DIMENSION_VALUE, KEY_BBOX.toLowerCase()); } } // Ensures the requested envelope has, at least, a part that intersects the valid envelope // for the coverage. try { final GeographicBoundingBox geoBbox = layerRef.getGeographicBoundingBox(); if (geoBbox == null) { throw new CstlServiceException("The request coverage \""+ layerRef.getName() +"\" has" + " no geometric information.", NO_APPLICABLE_CODE); } final GeneralEnvelope validGeoEnv = new GeneralEnvelope(geoBbox); Envelope requestGeoEnv = envelope; // We have to transform the objective envelope into an envelope that uses a geographic CRS, // in order to be able to verify the intersection between those two envelopes. if (!CRS.equalsIgnoreMetadata(envelope.getCoordinateReferenceSystem(), CommonCRS.WGS84.normalizedGeographic())) { try { requestGeoEnv = CRS.transform(envelope, CommonCRS.WGS84.normalizedGeographic()); } catch (TransformException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE, KEY_BBOX.toLowerCase()); } } if (!(validGeoEnv.intersects(requestGeoEnv, false))) { throw new CstlServiceException("The requested bbox is outside the domain of validity " + "for this coverage", NO_APPLICABLE_CODE, KEY_BBOX.toLowerCase()); } } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE, KEY_BBOX.toLowerCase()); } } else if (date == null) { throw new CstlServiceException("One of Time or Envelope has to be specified", MISSING_PARAMETER_VALUE); } else { // We take the envelope from the data provider. That envelope can be a little bit imprecise. try { final GeographicBoundingBox geoBbox = layerRef.getGeographicBoundingBox(); if (geoBbox == null) { throw new CstlServiceException("The request coverage \""+ layerRef.getName() +"\" has" + " no geometric information.", NO_APPLICABLE_CODE); } envelope = new JTSEnvelope2D(geoBbox.getWestBoundLongitude(), geoBbox.getEastBoundLongitude(), geoBbox.getSouthBoundLatitude(), geoBbox.getNorthBoundLatitude(), CommonCRS.WGS84.normalizedGeographic()); } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE, KEY_BBOX.toLowerCase()); } } final JTSEnvelope2D refEnvel; try { final CoordinateReferenceSystem responseCRS = request.getResponseCRS(); if (responseCRS != null && !CRS.equalsIgnoreMetadata(responseCRS, envelope.getCoordinateReferenceSystem())) { final Envelope responseEnv = CRS.transform(envelope, responseCRS); refEnvel = new JTSEnvelope2D(responseEnv); } else { refEnvel = new JTSEnvelope2D(envelope); } } catch (FactoryException ex) { throw new CstlServiceException(ex, INVALID_CRS, KEY_CRS.toLowerCase()); } catch (TransformException ex) { throw new CstlServiceException(ex, INVALID_CRS, KEY_RESPONSE_CRS.toLowerCase()); } Dimension size = request.getSize(); if (size == null) { // Try with resx/resy, those parameters should be filled. final List<Double> resolutions = request.getResolutions(); if (resolutions == null || resolutions.isEmpty()) { // Should not occurs since it is already tested throw new CstlServiceException("If width/height are not specified, you have to give resx/resy"); } final double resx = resolutions.get(0); final double resy = resolutions.get(1); final double envWidth = refEnvel.getSpan(0); final double envHeight = refEnvel.getSpan(1); // Assume that the resolution is in unit per px -> unit / (unit/pixel) -> px // For example to obtain an image whose width is 1024 pixels, representing 360 degrees, // the resolution on the x axis is 360 / 1024 = 0,3515625 degrees/pixels. // In our case, we want to know the image width using the size of the envelope and the // given resolution on that axis, so: image_width = envelope_width / resx final int newWidth = (int) Math.round(envWidth / resx); final int newHeight = (int) Math.round(envHeight / resy); size = new Dimension(newWidth, newHeight); } final Double elevation = (envelope.getDimension() > 2) ? envelope.getMedian(2) : null; /* * Generating the response. * It can be a text one (format MATRIX) or an image one (png, gif ...). */ final String format = request.getFormat(); if ( format.equalsIgnoreCase(MATRIX) || format.equalsIgnoreCase(ASCII_GRID)) { //NOTE ADRIAN HACKED HERE final RenderedImage image; try { final GridCoverage2D gridCov = layerRef.getCoverage(refEnvel, size, elevation, date); image = gridCov.getRenderedImage(); } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } return image; } else if( format.equalsIgnoreCase(NETCDF) ){ throw new CstlServiceException(new IllegalArgumentException( "Constellation does not support netcdf writing."), INVALID_FORMAT, KEY_FORMAT.toLowerCase()); } else if( format.equalsIgnoreCase(GEOTIFF) ){ try { final SpatialMetadata metadata = layerRef.getSpatialMetadata(); final GridCoverage2D coverage = layerRef.getCoverage(refEnvel, size, elevation, date); return new SimpleEntry(coverage, metadata); } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } else { // We are in the case of an image format requested. //NOTE: ADRIAN HACKED HERE // SCENE final Map<String, Object> renderParameters = new HashMap<>(); renderParameters.put(KEY_TIME, date); renderParameters.put("ELEVATION", elevation); final SceneDef sdef = new SceneDef(); final List<DataReference> styles = configLayer.getStyles(); final MutableStyle style; if (!styles.isEmpty()) { final DataReference styleName = styles.get(0); final MutableStyle incomingStyle = getStyle(styleName); style = WCSUtils.filterStyle(incomingStyle, request.getRangeSubset()); } else { style = null; } try { final MapContext context = PortrayalUtil.createContext(layerRef, style, renderParameters); sdef.setContext(context); } catch (PortrayalException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } // VIEW final Double azimuth = 0.0; //HARD CODED SINCE PROTOCOL DOES NOT ALLOW final ViewDef vdef = new ViewDef(refEnvel, azimuth); // CANVAS Color background = null; if (MimeType.IMAGE_JPEG.equalsIgnoreCase(format)) { background = Color.WHITE; } final CanvasDef cdef = new CanvasDef(size, background); // IMAGE final BufferedImage img; try { img = Cstl.getPortrayalService().portray(sdef, vdef, cdef); } catch (PortrayalException ex) { /* * TODO: the binding xml for WCS and GML do not support the exceptions format, * consequently we can't extract the exception output mime-type information from * the request. Maybe a more recent version of the GML 3 spec has fixed this bug ... */ //if (exceptions != null && exceptions.equalsIgnoreCase(EXCEPTIONS_INIMAGE)) { // img = Cstl.Portrayal.writeInImage(ex, abstractRequest.getSize()); //} else { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); //} } return img; } } private Object getCoverage200(final GetCoverage request, final CoverageData layerRef, final Layer configLayer) throws CstlServiceException { boolean isMultiPart = false; if (request.getMediaType() != null) { if (request.getMediaType().equals("multipart/mixed")) { isMultiPart = true; } else { throw new CstlServiceException("Only multipart/mixed is supported for mediaType parameter", INVALID_PARAMETER_VALUE); } } final SpatialMetadata metadata; final GridCoverage2D gridCov; final CoordinateReferenceSystem crs; try { metadata = layerRef.getSpatialMetadata(); gridCov = layerRef.getCoverage(null, null, null, null); crs = metadata.getInstanceForType(CoordinateReferenceSystem.class); } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } final Envelope refEnvel; if (request.getDomainSubset().isEmpty()) { try { refEnvel = layerRef.getEnvelope(); } catch (DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } else { // build full envelope in pixel MathTransform mt = gridCov.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER); GridEnvelope grid = gridCov.getGridGeometry().getExtent(); GeneralEnvelope env = new GeneralEnvelope(grid.getDimension()); for (int i = 0; i < grid.getDimension(); i++) { env.setRange(i, grid.getLow(i), grid.getHigh(i)); } //trim / slice the envelope for (DomainSubset subset : request.getDomainSubset()) { if (subset instanceof DimensionTrimType) { final DimensionTrimType trim = (DimensionTrimType) subset; final int dimensionIndex = dimensionIndex(trim.getDimension(), crs); if (dimensionIndex == -1) { throw new CstlServiceException("There is no such dimension: " + trim.getDimension(), AXIS_LABEL_INVALID); } else { env.setRange(dimensionIndex, Integer.parseInt(trim.getTrimLow()), Integer.parseInt(trim.getTrimHigh())); } } else if (subset instanceof DimensionSliceType) { final DimensionSliceType slice = (DimensionSliceType) subset; final int dimensionIndex = dimensionIndex(slice.getDimension(), crs); if (dimensionIndex == -1) { throw new CstlServiceException("There is no such dimension: " + slice.getDimension(), AXIS_LABEL_INVALID); } else { int slicePoint = Integer.parseInt(slice.getSlicePoint()); env.setRange(dimensionIndex, slicePoint, slicePoint + 1); } } } try { env = Envelopes.transform(mt, env); env.setCoordinateReferenceSystem(crs); } catch (TransformException ex) { throw new CstlServiceException("Unable to project the grid envelope to target CRS", ex, NO_APPLICABLE_CODE); } refEnvel = env; } Dimension size = new Dimension(500, 500); Date date = null; /* * Generating the response. * It can be a text one (format MATRIX) or an image one (png, gif ...). */ final String format = request.getFormat(); if (format.equalsIgnoreCase(MATRIX) || format.equalsIgnoreCase(ASCII_GRID)) { //NOTE ADRIAN HACKED HERE final RenderedImage image; try { final GridCoverage2D coverage = layerRef.getCoverage(refEnvel, size, null, date); image = coverage.getRenderedImage(); } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } return image; } else if (format.equalsIgnoreCase(MimeType.NETCDF) ){ try { final GridCoverage2D coverage = layerRef.getCoverage(refEnvel, size, null, date); final SimpleEntry response = new SimpleEntry(coverage, metadata); if (isMultiPart) { final File img = File.createTempFile(coverage.getName().toString(), ".nc"); GridCoverageNCWriter.writeInStream(response, new FileOutputStream(img)); final WCSResponseWrapper xml = buildXmlPart(describeCoverage200(layerRef.getName().tip().toString(), layerRef), format); final MultiPart multiPart = new MultiPart(); multiPart.bodyPart(new BodyPart(xml, MediaType.APPLICATION_XML_TYPE)) .bodyPart(new BodyPart(img,MediaType.valueOf(format))); return multiPart; } else { return response; } } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } else if (format.equalsIgnoreCase(MimeType.IMAGE_TIFF) ){ try { final GeotiffResponse response = new GeotiffResponse(); response.coverage = layerRef.getCoverage(refEnvel, size, null, date); response.metadata = metadata; if (request.getExtension() instanceof ExtensionType) { final ExtensionType ext = (ExtensionType) request.getExtension(); final ParametersType geoExt = ext.getForClass(ParametersType.class); if (geoExt != null) { if (geoExt.getCompression() != null) { if (geoExt.getCompression() == CompressionType.LZW || geoExt.getCompression() == CompressionType.PACK_BITS || geoExt.getCompression() == CompressionType.NONE) { response.compression = geoExt.getCompression().value(); } else { throw new CstlServiceException("Server does not support the requested compression.", ExceptionCode.COMPRESSION_NOT_SUPPORTED, geoExt.getCompression().value()); } } if (geoExt.getInterleave() != null) { throw new CstlServiceException("Server does not support interleaving.", ExceptionCode.INTERLEAVING_NOT_SUPPORTED, geoExt.getInterleave().value()); } if (geoExt.getPredictor() != null) { throw new CstlServiceException("Server does not support predictor.", ExceptionCode.PREDICTOR_NOT_SUPPORTED, geoExt.getPredictor().value()); } if (geoExt.isTiling()) { if ("PackBits".equals(response.compression)) { throw new CstlServiceException("Server does not support Tiling for packbit compression.", ExceptionCode.TILING_NOT_SUPPORTED, "tiling"); } if (geoExt.getTileheight() != null && geoExt.getTilewidth() != null && geoExt.getTileheight() > 0 && geoExt.getTilewidth() > 0 && (geoExt.getTileheight() % 16 == 0) && (geoExt.getTilewidth() % 16 == 0)) { response.tiling = true; response.tileHeight = geoExt.getTileheight(); response.tileWidth = geoExt.getTilewidth(); } else { throw new CstlServiceException("Server does not support predictor.", ExceptionCode.TILING_INVALID, geoExt.getPredictor().value()); } } } } if (isMultiPart) { final File img = GridCoverageWriter.writeInFile(response); final WCSResponseWrapper xml = buildXmlPart(describeCoverage200(layerRef.getName().tip().toString(), layerRef), format); final MultiPart multiPart = new MultiPart(); multiPart.bodyPart(new BodyPart(xml, MediaType.APPLICATION_XML_TYPE)) .bodyPart(new BodyPart(img,MediaType.valueOf(format))); return multiPart; } else { return response; } } catch (IOException | DataStoreException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } } else { // We are in the case of an image format requested. //NOTE: ADRIAN HACKED HERE // SCENE final Map<String, Object> renderParameters = new HashMap<>(); renderParameters.put(KEY_TIME, date); renderParameters.put("ELEVATION", null); final SceneDef sdef = new SceneDef(); final List<DataReference> styles = configLayer.getStyles(); final MutableStyle style; if (!styles.isEmpty()) { final DataReference styleName = styles.get(0); final MutableStyle incomingStyle = getStyle(styleName); style = WCSUtils.filterStyle(incomingStyle, request.getRangeSubset()); } else { style = null; } try { final MapContext context = PortrayalUtil.createContext(layerRef, style, renderParameters); sdef.setContext(context); } catch (PortrayalException ex) { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); } // VIEW final Double azimuth = 0.0; //HARD CODED SINCE PROTOCOL DOES NOT ALLOW final ViewDef vdef = new ViewDef(refEnvel, azimuth); // CANVAS Color background = null; if (MimeType.IMAGE_JPEG.equalsIgnoreCase(format)) { background = Color.WHITE; } final CanvasDef cdef = new CanvasDef(size, background); // IMAGE final BufferedImage img; try { img = Cstl.getPortrayalService().portray(sdef, vdef, cdef); } catch (PortrayalException ex) { /* * TODO: the binding xml for WCS and GML do not support the exceptions format, * consequently we can't extract the exception output mime-type information from * the request. Maybe a more recent version of the GML 3 spec has fixed this bug ... */ //if (exceptions != null && exceptions.equalsIgnoreCase(EXCEPTIONS_INIMAGE)) { // img = Cstl.Portrayal.writeInImage(ex, abstractRequest.getSize()); //} else { throw new CstlServiceException(ex, NO_APPLICABLE_CODE); //} } return img; } } private int dimensionIndex(final String dimension, final CoordinateReferenceSystem crs) { for (int i = 0; i < crs.getCoordinateSystem().getDimension(); i++) { if (dimension.equals(crs.getCoordinateSystem().getAxis(i).getAbbreviation())) { return i; } } return -1; } /** * Overriden from AbstractWorker because in version 1.0.0 the behaviour is different when the request updateSequence * is equal to the current. * * @param updateSequence * @param version * @return * @throws CstlServiceException */ private boolean returnUpdateSequenceDocument(final String updateSequence, final String version) throws CstlServiceException { if (updateSequence == null) { return false; } if ("1.0.0".equals(version)) { try { final long sequenceNumber = Long.parseLong(updateSequence); final long currentUpdateSequence = Long.parseLong(getCurrentUpdateSequence()); if (sequenceNumber == currentUpdateSequence) { throw new CstlServiceException("The update sequence parameter is equal to the current", CURRENT_UPDATE_SEQUENCE, "updateSequence"); } else if (sequenceNumber > currentUpdateSequence) { throw new CstlServiceException("The update sequence parameter is invalid (higher value than the current)", INVALID_UPDATE_SEQUENCE, "updateSequence"); } return false; } catch(NumberFormatException ex) { throw new CstlServiceException("The update sequence must be an integer", ex, INVALID_PARAMETER_VALUE, "updateSequence"); } } else { return returnUpdateSequenceDocument(updateSequence); } } private WCSResponseWrapper buildXmlPart(org.geotoolkit.wcs.xml.v200.CoverageDescriptionType describeCoverage200, String mime) { final org.geotoolkit.gml.xml.v321.RangeSetType rangeSet = new org.geotoolkit.gml.xml.v321.RangeSetType(); final FileType ft = new FileType(); ft.setMimeType(mime); final String ext = WCSUtils.getExtension(mime); ft.setRangeParameters(new AssociationRoleType("cid:" + describeCoverage200.getCoverageId() + ext, "http://www.opengis.net/spec/GMLCOV_geotiff-coverages/1.0/conf/geotiff-coverage", "fileReference")); ft.setFileReference("cid:" + describeCoverage200.getCoverageId() + ext); rangeSet.setFile(ft); final AbstractDiscreteCoverageType cov = new AbstractDiscreteCoverageType(describeCoverage200, rangeSet); final ObjectFactory factory = new ObjectFactory(); return new WCSResponseWrapper(factory.createGridCoverage(cov)); } }