Package org.geoserver.wcs

Source Code of org.geoserver.wcs.DefaultWebCoverageService100

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wcs;

import static org.vfny.geoserver.wcs.WcsException.WcsExceptionCode.InvalidParameterValue;

import java.awt.Rectangle;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.Interpolation;

import net.opengis.gml.CodeType;
import net.opengis.gml.DirectPositionType;
import net.opengis.gml.RectifiedGridType;
import net.opengis.gml.TimePositionType;
import net.opengis.gml.VectorType;
import net.opengis.wcs10.AxisSubsetType;
import net.opengis.wcs10.DescribeCoverageType;
import net.opengis.wcs10.DomainSubsetType;
import net.opengis.wcs10.GetCapabilitiesType;
import net.opengis.wcs10.GetCoverageType;
import net.opengis.wcs10.InterpolationMethodType;
import net.opengis.wcs10.IntervalType;
import net.opengis.wcs10.OutputType;
import net.opengis.wcs10.RangeSubsetType;
import net.opengis.wcs10.SpatialSubsetType;
import net.opengis.wcs10.TimePeriodType;
import net.opengis.wcs10.TimeSequenceType;
import net.opengis.wcs10.TypedLiteralType;

import org.eclipse.emf.common.util.EList;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.DimensionInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.util.ReaderDimensionsAccessor;
import org.geoserver.config.GeoServer;
import org.geoserver.data.util.CoverageUtils;
import org.geoserver.ows.util.RequestUtils;
import org.geoserver.wcs.response.Wcs10CapsTransformer;
import org.geoserver.wcs.response.Wcs10DescribeCoverageTransformer;
import org.geoserver.wcs.responses.CoverageResponseDelegate;
import org.geoserver.wcs.responses.CoverageResponseDelegateFinder;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.parameter.DefaultParameterDescriptor;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.resources.CRSUtilities;
import org.geotools.util.DateRange;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.filter.Filter;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.vfny.geoserver.util.WCSUtils;
import org.vfny.geoserver.wcs.WcsException;

/**
* The Default WCS 1.0.0 Service implementation
*
* @author Alessio Fabiani, GeoSolutions
*/
public class DefaultWebCoverageService100 implements WebCoverageService100 {

    private Catalog catalog;

    private GeoServer geoServer;

    private CoverageResponseDelegateFinder responseFactory;

    private static final Logger LOGGER = org.geotools.util.logging.Logging
            .getLogger(DefaultWebCoverageService100.class);

    /**
     *
     * @param geoServer
     */
    public DefaultWebCoverageService100(GeoServer geoServer, CoverageResponseDelegateFinder responseFactory) {
        this.geoServer = geoServer;
        this.catalog = geoServer.getCatalog();
        this.responseFactory = responseFactory;
    }

    /**
     *
     */
    public WCSInfo getServiceInfo() {
        return geoServer.getService(WCSInfo.class);
    }

    /**
     *
     */
    public Wcs10CapsTransformer getCapabilities(GetCapabilitiesType request) {
        // do the version negotiation dance
        List<String> provided = new ArrayList<String>();
        provided.add("1.0.0");
        List<String> accepted = null;
        if (request.getVersion() != null) {
            accepted = new ArrayList<String>();
            accepted.add(request.getVersion());
        }
        String version = RequestUtils.getVersionPreOws(provided, accepted);

        if ("1.0.0".equals(version)) {
            Wcs10CapsTransformer capsTransformer = new Wcs10CapsTransformer(geoServer);
            capsTransformer.setEncoding(Charset.forName((getServiceInfo().getGeoServer().getSettings()
                    .getCharset())));
            return capsTransformer;
        }

        throw new WcsException("Could not understand version:" + version);
    }

    /**
     *
     */
    public Wcs10DescribeCoverageTransformer describeCoverage(DescribeCoverageType request) {
        final String version = request.getVersion();
        if ("1.0.0".equals(version)) {
            WCSInfo wcs = getServiceInfo();
            Wcs10DescribeCoverageTransformer describeTransformer = new Wcs10DescribeCoverageTransformer(
                    wcs, catalog);
            describeTransformer.setEncoding(Charset.forName((wcs.getGeoServer().getSettings()
                    .getCharset())));
            return describeTransformer;
        }

        throw new WcsException("Could not understand version:" + version);
    }

    /**
     *
     */
    public GridCoverage[] getCoverage(final GetCoverageType request) {
        WCSInfo wcs = getServiceInfo();
       
        CoverageInfo meta = null;
        GridCoverage2D coverage = null;
        final List<GridCoverage> coverageResults = new ArrayList<GridCoverage>();
        try {

            // acquire coverage info
            meta = catalog.getCoverageByName(request.getSourceCoverage());
            if (meta == null)
                throw new WcsException("Cannot find sourceCoverage " + request.getSourceCoverage() + " in the catalog!");

            // first let's run some sanity checks on the inputs
            checkRangeSubset(meta, request.getRangeSubset());
            checkInterpolationMethod(meta, request.getInterpolationMethod());
            checkOutput(meta, request.getOutput());

            //
            // PREPARE DOMAIN SUBSET ELEMENT
            //
            final DomainSubsetType domainSubset = request.getDomainSubset();
            // time
            final TimeSequenceType temporalSubset = domainSubset.getTemporalSubset();
            // spatial
            final SpatialSubsetType spatialSubset = domainSubset.getSpatialSubset();
            final EList grids = spatialSubset.getGrid();
            if (grids.size() == 0)
                throw new IllegalArgumentException(
                        "Invalid number of Grid for spatial subsetting was set:" + grids.size());
            final RectifiedGridType grid = (RectifiedGridType) grids.get(0);
            final List envelopes = spatialSubset.getEnvelope();
            if (envelopes.size() == 0)
                throw new IllegalArgumentException(
                        "Invalid number of Envelope for spatial subsetting was set:"
                                + envelopes.size());
            final GeneralEnvelope requestedEnvelope = (GeneralEnvelope) envelopes.get(0);
           
            final OutputType output = request.getOutput();
            if (output == null)
                throw new IllegalArgumentException("Output type was null");
            final CodeType outputCRS = output.getCrs();

            final int dimension = grid.getDimension().intValue();
            // WE SUPPORT 3D DIMENSION ONLY VIA A BAND
            if (dimension == 3)
                throw new WcsException(
                        "We support a third dimension only via a specifica Axis in Range",
                        InvalidParameterValue, null);

            //
            // GRAB A READER
            //
            // grab the reader using the default params
            final GridCoverage2DReader reader = (GridCoverage2DReader) meta
                    .getGridCoverageReader(null, WCSUtils.getReaderHints(wcs));
            if (reader == null) {
                // cannot instantiate a reader, we should return an empty array
                return coverageResults.toArray(new GridCoverage2D[] {});
            }

            // get native elements and then play with the the requested ones
            final GeneralEnvelope nativeEnvelope = reader.getOriginalEnvelope();
            final CoordinateReferenceSystem nativeCRS = nativeEnvelope
                    .getCoordinateReferenceSystem();

            // get requested crs
            String requestedCRS = null;
            if (outputCRS != null) {
                requestedCRS = outputCRS.getValue();
            }

            // Compute the target crs, the crs that the final coverage will be served into
            final CoordinateReferenceSystem targetCRS;
            if (requestedCRS == null) {
                targetCRS = reader.getOriginalEnvelope().getCoordinateReferenceSystem();
                requestedCRS = CRS.lookupIdentifier(targetCRS, true);
            } else {
                // FORCE LON,LAT!!!!
                targetCRS = CRS.decode(requestedCRS, true);
            }
           
            //
            // PREPARE DESTINATION DIMENSIONS
            //
            final Rectangle destinationSize;
            final AffineTransform2D destinationG2W;
            final GridEnvelope limits = grid.getLimits();
            if (limits != null) {
                //
                // we have imposed limits from the request, we just use them as they are
                //
                final int[] lowers = limits.getLow().getCoordinateValues();
                destinationG2W = null;
                destinationSize = new Rectangle(lowers[0], lowers[1], limits.getSpan(0), limits
                        .getSpan(1));
            } else if (grid.getOffsetVector() != null && grid.getOffsetVector().size() > 0) {
                //
                // we have NO imposed limits from the request, we need to create a proper G2W with
                // the RESOLUTION we where given.
                // Notice that this is specific to WCS 1.0.0 since the request just allow us to
                // specify ResX and ResY
                //
                final VectorType offsetVector = (VectorType) grid.getOffsetVector().get(0);
                final List offsetValues = offsetVector.getValue();
                final double resX = (Double) offsetValues.get(0);
                final double resY = (Double) offsetValues.get(1);

                final DirectPositionType origin_ = grid.getOrigin().getPos();
                destinationSize = null;
                destinationG2W = new AffineTransform2D(resX, 0d, 0d, resY, (Double) origin_
                        .getValue().get(0), (Double) origin_.getValue().get(1));

            } else {
                throw new WcsException("Invalid Grid value:" + grid.toString(),
                        InvalidParameterValue, null);
            }

            //
            // SETTING COVERAGE READING PARAMS
            //
            // get the group of parameters tha this reader supports
            final ParameterValueGroup readParametersDescriptor = reader.getFormat()
                    .getReadParameters();
            GeneralParameterValue[] readParameters = CoverageUtils.getParameters(
                    readParametersDescriptor, meta.getParameters());
            readParameters = (readParameters != null ? readParameters
                    : new GeneralParameterValue[0]);

            // read grid geometry
            final GridGeometry2D requestedGridGeometry;
            if (destinationSize != null)
                // we have been asked to support a specific raster size, we will set the grid2world
                // accordingly
                requestedGridGeometry = new GridGeometry2D(new GridEnvelope2D(destinationSize),
                        getHorizontalEnvelope(requestedEnvelope));
            else
                // we have been asked to support a specific g2w, we will set the raster size
                // accordingly
                requestedGridGeometry = new GridGeometry2D(PixelInCell.CELL_CENTER, destinationG2W,
                        getHorizontalEnvelope(requestedEnvelope), null);
            // NOTICE that we always have to respect the provided envelope
            final ParameterValue<GeneralGridGeometry> requestedGridGeometryParam = new DefaultParameterDescriptor<GeneralGridGeometry>(
                    AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString(),
                    GeneralGridGeometry.class, null, requestedGridGeometry).createValue();
            GeneralParameterValue[] tmpArray = new GeneralParameterValue[readParameters.length+1];
            System.arraycopy(readParameters, 0, tmpArray, 0, readParameters.length);
            tmpArray[tmpArray.length-1]=requestedGridGeometryParam;
            readParameters=tmpArray;
           
           
            /*
             * Test if the parameter "TIME" is present in the WMS request, and by the way in the
             * reading parameters. If it is the case, one can adds it to the request. If an
             * exception is thrown, we have nothing to do.
             */
            final List<GeneralParameterDescriptor> parameterDescriptors = new ArrayList<GeneralParameterDescriptor>(
                    readParametersDescriptor.getDescriptor().descriptors());
            Set<ParameterDescriptor<List>> dynamicParameters = reader.getDynamicParameters();
            parameterDescriptors.addAll(dynamicParameters);

            //
            // TIME
            //
            ReaderDimensionsAccessor dimensions = new ReaderDimensionsAccessor(reader);
            DimensionInfo timeDimension = meta.getMetadata().get(ResourceInfo.TIME, DimensionInfo.class);
            if(timeDimension != null && timeDimension.isEnabled() && dimensions.hasTime()) {
                final List<Object> timeValues = new ArrayList<Object>();
                if (temporalSubset != null && temporalSubset.getTimePosition() != null) {
                    // grab the time positions
                    final EList timePosition = temporalSubset.getTimePosition();
                    for (Iterator it = timePosition.iterator(); it.hasNext();) {
                        TimePositionType tp = (TimePositionType) it.next();
                        Date date = (Date) tp.getValue();
                        if(date == null) {
                            date = dimensions.getMaxTime();
                        }
                        timeValues.add(date);
                    }
                    // grab the time intervals
                    final EList timePeriods = temporalSubset.getTimePeriod();
                    for (Iterator it = timePeriods.iterator(); it.hasNext();) {
                        TimePeriodType tp = (TimePeriodType) it.next();
                        Date begin = (Date) tp.getBeginPosition().getValue();
                        Date end = (Date) tp.getEndPosition().getValue();
                        timeValues.add(new DateRange(begin, end));
                    }
                }
               
                if(timeValues.isEmpty()) {
                    Date date = dimensions.getMaxTime();
                    timeValues.add(date);
                }
               
                readParameters = CoverageUtils.mergeParameter(parameterDescriptors,
                        readParameters, timeValues, "TIME", "Time");
            }
           
            //
            // ELEVATION SUPPORT VIA A SPECIFIC AXIS ELEVATION
            //
            DimensionInfo elevationDimension = meta.getMetadata().get(ResourceInfo.ELEVATION, DimensionInfo.class);
            if(elevationDimension != null && elevationDimension.isEnabled() && dimensions.hasElevation()) {
                List<Double> elevations = new ArrayList<Double>();
                // extract elevation values
                List axisSubset = null;
                if (request.getRangeSubset() != null) {
                    axisSubset = request.getRangeSubset().getAxisSubset();
                    if (axisSubset.size() > 0) {
                        for (int a = 0; a < axisSubset.size(); a++) {
                            AxisSubsetType axis = (AxisSubsetType) axisSubset.get(a);

                            String axisName = axis.getName();
                            if (axisName.equalsIgnoreCase(WCSUtils.ELEVATION)) {
                                if (axis.getSingleValue().size() > 0) {
                                    for (int s = 0; s < axis.getSingleValue().size(); s++) {
                                        elevations.add(Double.parseDouble(((TypedLiteralType) axis
                                                .getSingleValue().get(s)).getValue()));
                                    }
                                } else if (axis.getInterval().size() > 0) {
                                    IntervalType interval = (IntervalType) axis.getInterval().get(0);
                                    int min = Integer.parseInt(interval.getMin().getValue());
                                    int max = Integer.parseInt(interval.getMax().getValue());
                                    int res = (interval.getRes() != null ? Integer.parseInt(interval
                                            .getRes().getValue()) : 1);

                                    int count = (int) (Math.floor(max - min) / res + 1);
                                    for (int b = 0; b < count; b++) {
                                        elevations.add(new Double(min + b * res));
                                    }
                                }
                            }
                        }
                    }
                }
               
                if(elevations.isEmpty()) {
                    elevations.add(dimensions.getMinElevation());
                }
               
                readParameters = CoverageUtils.mergeParameter(parameterDescriptors,
                        readParameters, elevations, "ELEVATION", "Elevation");
            }
           
            //
            // CUSTOM DIMENSION SUPPORT
            //
            if (request.getRangeSubset() != null) {
                EList<?> axisSubset = request.getRangeSubset().getAxisSubset();
                final int asCount = axisSubset == null ? 0 : axisSubset.size();
                for (int i = 0; i < asCount; i++) {
                    AxisSubsetType axis = (AxisSubsetType)axisSubset.get(i);
                    String axisName = axis.getName();
                    if (!axisName.equalsIgnoreCase(WCSUtils.ELEVATION)) {
                        Object dimInfo = meta.getMetadata().get(ResourceInfo.CUSTOM_DIMENSION_PREFIX + axisName);
                        axisName = axisName.toUpperCase(); // using uppercase with imagemosaic
                        if (dimInfo instanceof DimensionInfo && dimensions.hasDomain(axisName)) {
                            int valueCount = axis.getSingleValue().size();
                            if (valueCount > 0) {
                                List<String> dimValues = new ArrayList<String>(valueCount);
                                for (int s = 0; s < valueCount; s++) {
                                    dimValues.add(((TypedLiteralType) axis
                                            .getSingleValue().get(s)).getValue());
                                }
                                readParameters = CoverageUtils.mergeParameter(parameterDescriptors,
                                        readParameters, dimValues, axisName);
                            }
                        }
                    }
                }   
            }
           
            //
            // Check if we have a filter among the params
            //
            Filter filter = WCSUtils.getRequestFilter();
            if(filter != null) {
                readParameters = CoverageUtils.mergeParameter(parameterDescriptors,
                        readParameters, filter, "FILTER", "Filter");
            }
           
            // Check we're not going to read too much data
            WCSUtils.checkInputLimits(wcs, meta, reader, requestedGridGeometry);


            //
            // Checking for supported Interpolation Methods
            //
            Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);

            String interpolationType = null;
            if(request.getInterpolationMethod()!=null){
                interpolationType = request.getInterpolationMethod().getLiteral();           
                if (interpolationType != null) {
                  
                    if (interpolationType.equalsIgnoreCase("bilinear")) {
                        interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
                    } else if (interpolationType.equalsIgnoreCase("bicubic")) {
                        interpolation = Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
                    } else if (interpolationType.equalsIgnoreCase("nearest neighbor")) {
                        interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
                    }
                    readParameters = CoverageUtils.mergeParameter(parameterDescriptors,readParameters, interpolation, "interpolation");
                    if(meta.getStore().getFormat() instanceof ImageMosaicFormat){
                        GeneralParameterValue[] temp = new GeneralParameterValue[readParameters.length+1];
                        System.arraycopy(readParameters, 0, temp, 0, readParameters.length);
                        temp[temp.length-1]=ImageMosaicFormat.INTERPOLATION.createValue();
                        ((ParameterValue)temp[temp.length-1]).setValue(interpolation);
                        readParameters=temp;
                    }
                }
            }
            //
            // make sure we work in streaming mode
            //
            // work in streaming fashion when JAI is involved
            readParameters = WCSUtils.replaceParameter(readParameters, Boolean.FALSE,
                    AbstractGridFormat.USE_JAI_IMAGEREAD);
           
            //
            // perform read
            //
            coverage = (GridCoverage2D) reader.read(readParameters);
            if ((coverage == null) || !(coverage instanceof GridCoverage2D)) {
                throw new IOException("No raster data found in the request (it may be that " +
                    "the request bbox is outside of the coverage area, or that the filters used " +
                    "match no portions of it.");
            }

            // double check what we have loaded
            WCSUtils.checkInputLimits(wcs, coverage);

            //
            // Band Select (works on just one field)
            //
            GridCoverage2D bandSelectedCoverage = coverage;
            // ImageIOUtilities.visualize(coverage.getRenderedImage());
            if (request.getRangeSubset() != null) {
                // if (request.getRangeSubset().getAxisSubset().size() > 1) {
                // throw new WcsException("Multi field coverages are not supported yet");
                // }

                // extract the band indexes
                EList axisSubset = request.getRangeSubset().getAxisSubset();
                if (axisSubset.size() > 0) {
                    for (int a = 0; a < axisSubset.size(); a++) {
                        AxisSubsetType axis = (AxisSubsetType) axisSubset.get(a);

                        try {
                            String axisName = axis.getName();
                            if (axisName.equalsIgnoreCase("Band")) {
                                int[] bands = null;
                                if (axis.getSingleValue().size() > 0) {
                                    bands = new int[axis.getSingleValue().size()];
                                    for (int s = 0; s < axis.getSingleValue().size(); s++) {
                                        bands[s] = Integer.parseInt(((TypedLiteralType) axis
                                                .getSingleValue().get(s)).getValue()) - 1;
                                    }
                                } else if (axis.getInterval().size() > 0) {
                                    IntervalType interval = (IntervalType) axis.getInterval()
                                            .get(0);
                                    int min = Integer.parseInt(interval.getMin().getValue());
                                    int max = Integer.parseInt(interval.getMax().getValue());
                                    int res = (interval.getRes() != null ? Integer
                                            .parseInt(interval.getRes().getValue()) : 1);

                                    bands = new int[(int) (Math.floor(max - min) / res + 1)];
                                    for (int b = 0; b < bands.length; b++)
                                        bands[b] = (min + b * res) - 1;
                                }

                                // finally execute the band select
                                bandSelectedCoverage = (GridCoverage2D) WCSUtils.bandSelect(
                                        coverage, bands);
                            }
                        } catch (Exception e) {
                            // Warning: Axis not found!!!
                            throw new WcsException("Band Select Operation: "
                                    + e.getLocalizedMessage());
                        }
                    }
                }
            }

            //
            // final step for the requested coverage
            //
            // compute intersection envelope to be used
            GeneralEnvelope destinationEnvelope = (GeneralEnvelope) getHorizontalEnvelope(computeIntersectionEnvelope(
                    requestedEnvelope, nativeEnvelope));
            if(targetCRS != null) {
                destinationEnvelope = CRS.transform(destinationEnvelope, targetCRS);
                destinationEnvelope.setCoordinateReferenceSystem(targetCRS);
            }

            final GridGeometry2D destinationGridGeometry;
            if (destinationSize != null) {
                destinationGridGeometry = new GridGeometry2D(new GridEnvelope2D(destinationSize),
                        destinationEnvelope);
            } else {
                destinationGridGeometry = new GridGeometry2D(PixelInCell.CELL_CENTER,
                        destinationG2W, destinationEnvelope, null);
            }

            // verify we're not going to build a raster exceeding the output limits
            WCSUtils.checkOutputLimits(wcs, destinationGridGeometry.getGridRange2D(),
                    bandSelectedCoverage.getRenderedImage().getSampleModel());

            final GridCoverage2D finalCoverage = WCSUtils.resample(bandSelectedCoverage, nativeCRS,
                    targetCRS, destinationGridGeometry, interpolation);
            coverageResults.add(finalCoverage);

            return coverageResults.toArray(new GridCoverage2D[] {});
        } catch (Exception e) {
          CoverageCleanerCallback.addCoverages(coverage);
            if (e instanceof WcsException) {
                throw (WcsException) e;
            } else {
                throw new WcsException(e);
            }
        }

    }

    private static Envelope getHorizontalEnvelope(GeneralEnvelope originalEnvelope) throws FactoryException, TransformException  {
       
        final CoordinateReferenceSystem originalCRS = originalEnvelope.getCoordinateReferenceSystem();
        final CoordinateReferenceSystem horizontalCRS = CRS.getHorizontalCRS(originalEnvelope.getCoordinateReferenceSystem());
        if(CRS.equalsIgnoreMetadata(originalCRS, horizontalCRS)) {
            return originalEnvelope;
        }
       
        // create transformation and check that it is not the identity again
        final MathTransform transform= CRS.findMathTransform(originalCRS, horizontalCRS);
        if(transform.isIdentity()) {
            return originalEnvelope;
        }

        // do the actual transform
        return CRS.transform(transform,originalEnvelope);
    }

    private static GeneralEnvelope computeIntersectionEnvelope(
            final GeneralEnvelope requestedEnvelope, final GeneralEnvelope nativeEnvelope) {

        GeneralEnvelope retVal;
        // get the crs for the requested bbox
        final CoordinateReferenceSystem requestCRS = CRS.getHorizontalCRS(requestedEnvelope
                .getCoordinateReferenceSystem());
        final CoordinateReferenceSystem nativeCRS = CRS.getHorizontalCRS(nativeEnvelope
                .getCoordinateReferenceSystem());

        try {
            //
            // If this approach succeeds, either the request crs is the same of
            // the coverage crs or the request bbox can be reprojected to that
            // crs
            //         
            MathTransform destinationToSourceTransform = null;
            // STEP 1: reproject requested BBox to native CRS if needed
            if (!CRS.equalsIgnoreMetadata(requestCRS, nativeCRS)) {
                retVal = CRS.transform(getHorizontalEnvelope(requestedEnvelope), nativeCRS);
                retVal.setCoordinateReferenceSystem(nativeCRS);
            } else {
                // we do not need to do anything, but we do this in order to aboid problems with the
                // envelope checks
                retVal = new GeneralEnvelope(getHorizontalEnvelope(requestedEnvelope));

            }

            //
            // STEP 2: intersect requested BBox in native CRS with coverage native bbox to get the
            // crop bbox
            //
            // intersect the requested area with the bounds of this
            // layer in native crs
            if (!retVal.intersects(nativeEnvelope, true))
                return null;

            // intersect
            retVal.intersect(nativeEnvelope);
            retVal.setCoordinateReferenceSystem(nativeCRS);
            return retVal;

        } catch (TransformException te) {
            // something bad happened while trying to transform this
            // envelope. let's try with wgs84
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.log(Level.FINE, te.getLocalizedMessage(), te);
        } catch (FactoryException fe) {
            // something bad happened while trying to transform this
            // envelope. let's try with wgs84
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.log(Level.FINE, fe.getLocalizedMessage(), fe);
        }

        try {

            //
            // If we can not reproject the requested envelope to the native CRS,
            // we go back to reproject in the geographic crs of the native
            // coverage since this usually happens for conversions between CRS
            // whose area of definition is different
            //             
            final CoordinateReferenceSystem nativeGeoCRS = CRSUtilities
                    .getStandardGeographicCRS2D(nativeCRS);
            final GeneralEnvelope nativeGeoEnvelope = (GeneralEnvelope) CRS.transform(
                    nativeEnvelope, nativeGeoCRS);
            nativeGeoEnvelope.setCoordinateReferenceSystem(nativeGeoCRS);

            GeneralEnvelope requestedBBOXInNativeGeographicCRS = null;
            // STEP 1 reproject the requested envelope to the coverage geographic bbox
            if (!CRS.equalsIgnoreMetadata(nativeCRS, requestCRS)) {
                requestedBBOXInNativeGeographicCRS = CRS.transform(requestedEnvelope, nativeGeoCRS);
                requestedBBOXInNativeGeographicCRS.setCoordinateReferenceSystem(nativeGeoCRS);
            }
            if (requestedBBOXInNativeGeographicCRS == null)
                requestedBBOXInNativeGeographicCRS = new GeneralEnvelope(requestCRS);

            // STEP 2 intersection with the geographic bbox for this coverage
            if (!requestedBBOXInNativeGeographicCRS.intersects(nativeEnvelope, true))
                return null;

            // intersect with the coverage native geographic bbox
            // note that for the moment we got to use general envelope since there is no
            // intersection otherwise
            requestedBBOXInNativeGeographicCRS.intersect(nativeGeoEnvelope);
            requestedBBOXInNativeGeographicCRS.setCoordinateReferenceSystem(nativeGeoCRS);

            // now go back to the coverage native CRS in order to compute an approximate requested
            // resolution
            final GeneralEnvelope approximateRequestedBBox = CRS.transform(
                    requestedBBOXInNativeGeographicCRS, requestCRS);
            approximateRequestedBBox.setCoordinateReferenceSystem(requestCRS);
            return approximateRequestedBBox;

        } catch (TransformException te) {
            // something bad happened while trying to transform this
            // envelope. let's try with wgs84
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.log(Level.FINE, te.getLocalizedMessage(), te);
        }

        LOGGER
                .log(Level.INFO,
                        "We did not manage to crop the requested envelope, we fall back onto loading the whole coverage.");
        return null;
    }

    // private static void checkDomainSubset(CoverageInfo meta, DomainSubsetType domainSubset)
    // throws Exception {
    //     
    // final GeneralEnvelope requestedEnvelope = (GeneralEnvelope)
    // domainSubset.getSpatialSubset().getEnvelope().get(0);
    // final CoordinateReferenceSystem bboxCRs = requestedEnvelope.getCoordinateReferenceSystem();
    //
    // Envelope gridEnvelope = /* meta.getCoverage().getEnvelope() */null;
    // GeneralEnvelope requestedEnvelopeBboxCRS = null;
    // if (bboxCRs instanceof GeographicCRS) {
    // try {
    // final CoordinateOperationFactory cof = CRS.getCoordinateOperationFactory(true);
    // final CoordinateOperation operation =
    // cof.createOperation(gridEnvelope.getCoordinateReferenceSystem(), bboxCRs);
    // requestedEnvelopeBboxCRS = CRS.transform(operation, gridEnvelope);
    // } catch (Exception e) {
    // // this may happen, there is nothing we can do about it, we just
    // // use the back transformed envelope to be more lenient about
    // // which coordinate coorections to make on the longitude axis
    // // should the antimeridian style be used
    // }
    // }
    //
    // // check the coordinates, but make sure the case 175,-175 is handled
    // // as valid for the longitude axis in a geographic coordinate system
    // // see section 7.6.2 of the WCS 1.1.1 spec)
    // double[] lower = requestedEnvelope.getLowerCorner().getCoordinate();
    // double[] upper = requestedEnvelope.getUpperCorner().getCoordinate();
    // for (int i = 0; i < lower.length; i++) {
    // if (lower[i] > upper[i]) {
    // final CoordinateSystemAxis axis = bboxCRs.getCoordinateSystem().getAxis(i);
    // // see if the coordinates can be fixed
    // if (bboxCRs instanceof GeographicCRS && axis.getDirection() == AxisDirection.EAST) {
    //
    // if (requestedEnvelopeBboxCRS != null) {
    // // try to guess which one needs to be fixed
    // final double envMax = requestedEnvelopeBboxCRS.getMaximum(i);
    // if (envMax >= lower[i])
    // upper[i] = upper[i] + (axis.getMaximumValue() - axis.getMinimumValue());
    // else
    // lower[i] = lower[i] - (axis.getMaximumValue() - axis.getMinimumValue());
    //
    // } else {
    // // just fix the upper and hope...
    // upper[i] = upper[i] + (axis.getMaximumValue() - axis.getMinimumValue());
    // }
    // }
    //
    // // if even after the fix we're in the wrong situation, complain
    // if (lower[i] > upper[i]) {
    // throw new WcsException(
    // "illegal bbox, min of dimension " + (i + 1) + ": " + lower[i] + " is " +
    // "greater than max of same dimension: " + upper[i],
    // WcsExceptionCode.InvalidParameterValue, "BoundingBox");
    // }
    // }
    //
    // }
    // }

    private static void checkInterpolationMethod(CoverageInfo info,
            InterpolationMethodType interpolationMethod) {
        // check interpolation method
        String interpolation = interpolationMethod.getLiteral();
        if (interpolation != null) {
            boolean interpolationSupported = false;

            if (interpolation.startsWith("nearest")) {
                interpolation = "nearest neighbor";
            }
            if (interpolation.equals("nearest neighbor")
                    || (info.getDefaultInterpolationMethod() != null && info
                            .getDefaultInterpolationMethod().equalsIgnoreCase(interpolation))) {
                interpolationSupported = true;
            }
            for (String method : info.getInterpolationMethods()) {
                if (interpolation.equalsIgnoreCase(method)) {
                    interpolationSupported = true;
                }
            }

            if (!interpolationSupported)
                throw new WcsException(
                        "The requested Interpolation method is not supported by this Coverage.",
                        InvalidParameterValue, "RangeSubset");
        }
    }

    /**
     * Checks that the elements of the Output part of the request do make sense by comparing them to
     * the coverage metadata
     *
     * @param info
     * @param rangeSubset
     */
    private void checkOutput(CoverageInfo meta, OutputType output) {
        if (output == null)
            return;

        // check output format
        String format = output.getFormat().getValue();
        String declaredFormat = getDeclaredFormat(meta.getSupportedFormats(), format);
        if (declaredFormat == null)
            throw new WcsException("format " + format + " is not supported for this coverage",
                    InvalidParameterValue, "format");

        // check requested CRS
        // if (output.getCrs() != null) {
        // String requestedCRS = output.getCrs().getValue();
        // if (getRequestResponseCRS(meta.getRequestCRSs(), requestedCRS) == null &&
        // getRequestResponseCRS(meta.getResponseCRSs(), requestedCRS) == null)
        // throw new WcsException("CRS " + requestedCRS + " is not supported for this coverage",
        // InvalidParameterValue, "CRS");
        // } else {
        // // The requested CRS was not specified ... what to do ???
        // }
    }

    /**
     * Checks if the supported format string list contains the specified format, doing a case
     * insensitive search. If found the declared output format name is returned, otherwise null is
     * returned.
     *
     * @param supportedFormats
     * @param format
     * @return
     */
    private String getDeclaredFormat(List<String> supportedFormats, String format) {
        // supported formats may be setup using old style formats, first scan
        // the configured list
        for (String sf : supportedFormats) {
            if (sf.equalsIgnoreCase(format.trim())) {
                return sf;
            } else {
                CoverageResponseDelegate delegate = responseFactory.encoderFor(sf);
                if (delegate != null && delegate.canProduce(format))
                    return sf;
            }
        }
        return null;
    }

    /**
     * Checks that the elements of the RangeSubset part of the request do make sense by comparing
     * them to the coverage metadata
     *
     * @param info
     * @param rangeSubset
     */
    private static void checkRangeSubset(CoverageInfo info, RangeSubsetType rangeSubset) {
        // quick escape if no range subset has been specified (it's legal)
        if (rangeSubset == null)
            return;

        for (int a = 0; a < rangeSubset.getAxisSubset().size(); a++) {
            AxisSubsetType axisSubset = (AxisSubsetType) rangeSubset.getAxisSubset().get(a);

            if (axisSubset.getName().equalsIgnoreCase("Band")) {
                // prepare a support structure to quickly get the band index of a key
                // (and remember we replaced spaces with underscores in the keys to
                // avoid issues with the kvp parsing of indentifiers that include spaces)

                // check indexes
                int[] bands = null;
                if (axisSubset.getSingleValue().size() > 0) {
                    bands = new int[1];
                    bands[0] = Integer.parseInt(((TypedLiteralType) axisSubset.getSingleValue()
                            .get(0)).getValue());
                } else if (axisSubset.getInterval().size() > 0) {
                    IntervalType interval = (IntervalType) axisSubset.getInterval().get(0);
                    int min = Integer.parseInt(interval.getMin().getValue());
                    int max = Integer.parseInt(interval.getMax().getValue());
                    int res = (interval.getRes() != null ? Integer.parseInt(interval.getRes()
                            .getValue()) : 1);

                    bands = new int[(max - min) / res];
                    for (int b = 0; b < bands.length; b++)
                        bands[b] = min + (b * res);
                }

                if (bands == null)
                    throw new WcsException("Invalid values for axis " + axisSubset.getName(),
                            InvalidParameterValue, "AxisSubset");
            } else if (axisSubset.getName().equalsIgnoreCase(WCSUtils.ELEVATION)) {
                double[] elevations = null;
                if (axisSubset.getSingleValue().size() > 0) {
                    elevations = new double[axisSubset.getSingleValue().size()];
                    for (int s = 0; s < axisSubset.getSingleValue().size(); s++) {
                        elevations[s] = Double.parseDouble(((TypedLiteralType) axisSubset
                                .getSingleValue().get(s)).getValue());
                    }
                } else if (axisSubset.getInterval().size() > 0) {
                    IntervalType interval = (IntervalType) axisSubset.getInterval().get(0);
                    int min = Integer.parseInt(interval.getMin().getValue());
                    int max = Integer.parseInt(interval.getMax().getValue());
                    int res = (interval.getRes() != null ? Integer.parseInt(interval.getRes()
                            .getValue()) : 1);

                    elevations = new double[(int) (Math.floor(max - min) / res + 1)];
                    for (int b = 0; b < elevations.length; b++)
                        elevations[b] = (min + b * res);
                }
            }
        }
    }

}
TOP

Related Classes of org.geoserver.wcs.DefaultWebCoverageService100

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.