/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.vfny.geoserver.wcs.responses;
import java.awt.Rectangle;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.jai.Interpolation;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.config.ServiceInfo;
import org.geoserver.data.util.CoverageUtils;
import org.geoserver.platform.ServiceException;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.opengis.coverage.Coverage;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.vfny.geoserver.Request;
import org.vfny.geoserver.Response;
import org.vfny.geoserver.util.WCSUtils;
import org.vfny.geoserver.wcs.WcsException;
import org.vfny.geoserver.wcs.WcsException.WcsExceptionCode;
import org.vfny.geoserver.wcs.requests.CoverageRequest;
/**
* DOCUMENT ME!
*
* @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last
* modification)
* @author $Author: Simone Giannecchini (simboss1@gmail.com) $ (last
* modification)
*/
public class CoverageResponse implements Response {
/** Standard logging instance for class */
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses");
private final static Hints LENIENT_HINT = new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
private final static Hints hints = new Hints();
static {
// ///////////////////////////////////////////////////////////////////
//
// HINTS
//
// ///////////////////////////////////////////////////////////////////
hints.add(LENIENT_HINT);
}
/**
*
*/
CoverageResponseDelegate delegate;
/**
* This is the request provided to the execute( Request ) method.
*
* <p>
* We save it so we can access the handle provided by the user for error
* reporting during the writeTo( OutputStream ) opperation.
* </p>
*
* <p>
* This value will be <code>null</code> until execute is called.
* </p>
*
* @uml.property name="request"
* @uml.associationEnd multiplicity="(0 1)"
*/
private CoverageRequest request;
/**
* Empty constructor
*/
public CoverageResponse() {
request = null;
}
/**
* Returns any extra headers that this service might want to set in the HTTP response object.
* @see org.vfny.geoserver.Response#getResponseHeaders()
*/
public HashMap getResponseHeaders() {
return null;
}
/**
* DOCUMENT ME!
*
* @param gs
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public String getContentType(GeoServer geoServer) {
return delegate.getContentType();
}
public String getContentEncoding() {
return delegate.getContentEncoding();
}
public String getContentDisposition() {
return delegate.getContentDisposition();
}
/**
* Jody here with one pass replacement for writeTo.
*
* <p>
* This code is a discussion point, when everyone has had there input we
* will try and set things up properly.
* </p>
*
* <p>
* I am providing a mirror of the existing desing: - execute gathers the
* resultList - sets up the header
* </p>
*
* @param out
* DOCUMENT ME!
*
* @throws WcsException
* DOCUMENT ME!
* @throws IOException
* DOCUMENT ME!
* @throws IllegalStateException
* DOCUMENT ME!
*/
public void writeTo(OutputStream out) throws ServiceException, IOException {
if ((request == null) || (delegate == null)) {
throw new IllegalStateException("execute has not been called prior to writeTo");
}
delegate.encode(out);
}
/**
* Executes CoverageRequest.
*
* <p>
* Willing to execute a CoverageRequest.
* </p>
*
* @param req
* DOCUMENT ME!
*
* @throws WcsException
* DOCUMENT ME!
*/
public void execute(Request req) throws WcsException {
execute((CoverageRequest) req);
}
public void execute(CoverageRequest request) throws WcsException {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest(new StringBuffer("execute CoverageRequest response. Called request is: ").append(
request).toString());
}
this.request = request;
final String outputFormat = request.getOutputFormat();
delegate = CoverageResponseDelegateFactory.encoderFor(outputFormat);
if(delegate == null)
throw new WcsException("Output format: " + outputFormat + " not supported by geoserver " +
"for this Coverage", WcsExceptionCode.InvalidParameterValue, "format");
final Catalog catalog = request.getWCS().getGeoServer().getCatalog();
CoverageInfo meta = null;
GridCoverage coverage = null;
try {
meta = catalog.getCoverageByName(request.getCoverage());
if (!meta.getSupportedFormats().contains(outputFormat.toUpperCase())) {
WcsException newEx = new WcsException(new StringBuffer("output format: ").append(
outputFormat).append(" not ")
.append("supported by geoserver for this Coverage")
.toString());
throw newEx;
}
final Format format = meta.getStore().getFormat();
final AbstractGridCoverage2DReader reader =
(AbstractGridCoverage2DReader) catalog.getResourcePool().getGridCoverageReader(meta.getStore(),hints);
// /////////////////////////////////////////////////////////
//
// Setting coverage reading params.
//
// /////////////////////////////////////////////////////////
final ParameterValueGroup params = reader.getFormat().getReadParameters();
final GridCoverage2D finalCoverage = getFinalCoverage(request, meta, reader,
CoverageUtils.getParametersKVP(params));
delegate.prepare(outputFormat, finalCoverage);
} catch (IOException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (NoSuchElementException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (IllegalArgumentException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (SecurityException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (WcsException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (FactoryException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (IndexOutOfBoundsException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
} catch (TransformException e) {
final WcsException newEx = new WcsException(e, "problem with CoverageResults",
request.getHandle());
throw newEx;
}
}
/**
* Release locks if we are into that sort of thing.
*
* @see org.vfny.geoserver.responses.Response#abort()
*/
public void abort(ServiceInfo gs) {
if (request == null) {
return; // request was not attempted
}
}
/**
* GetCroppedCoverage
*
* @param request CoverageRequest
* @param meta CoverageInfo
* @param parameters
* @param coverage GridCoverage
* @return GridCoverage2D
* @throws WcsException
* @throws IOException
* @throws IndexOutOfBoundsException
* @throws FactoryException
* @throws TransformException
*/
private static GridCoverage2D getFinalCoverage(CoverageRequest request, CoverageInfo meta,
AbstractGridCoverage2DReader coverageReader /*GridCoverage coverage*/, Map parameters)
throws WcsException, IOException, IndexOutOfBoundsException, FactoryException,
TransformException {
// This is the final Response CRS
final String responseCRS = request.getResponseCRS();
// - first check if the responseCRS is present on the Coverage
// ResponseCRSs list
if (!meta.getResponseSRS().contains(responseCRS)) {
throw new WcsException("This Coverage does not support the requested Response-CRS.");
}
// - then create the Coordinate Reference System
final CoordinateReferenceSystem targetCRS = CRS.decode(responseCRS);
// This is the CRS of the requested Envelope
final String requestCRS = request.getCRS();
// - first check if the requestCRS is present on the Coverage
// RequestCRSs list
if (!meta.getResponseSRS().contains(requestCRS)) {
throw new WcsException("This Coverage does not support the requested CRS.");
}
// - then create the Coordinate Reference System
final CoordinateReferenceSystem sourceCRS = CRS.decode(requestCRS);
// This is the CRS of the Coverage Envelope
final CoordinateReferenceSystem cvCRS = ((GeneralEnvelope) coverageReader
.getOriginalEnvelope()).getCoordinateReferenceSystem();
final MathTransform GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform = CRS
.findMathTransform(cvCRS, sourceCRS, true);
final MathTransform GCCRSTodeviceCRSTransform = CRS.findMathTransform(cvCRS, targetCRS, true);
final MathTransform deviceCRSToGCCRSTransform = GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform
.inverse();
com.vividsolutions.jts.geom.Envelope envelope = request.getEnvelope();
GeneralEnvelope destinationEnvelope;
final boolean lonFirst = sourceCRS.getCoordinateSystem().getAxis(0).getDirection().absolute()
.equals(AxisDirection.EAST);
// the envelope we are provided with is lon,lat always
if (!lonFirst) {
destinationEnvelope = new GeneralEnvelope(new double[] {
envelope.getMinY(), envelope.getMinX()
}, new double[] { envelope.getMaxY(), envelope.getMaxX() });
} else {
destinationEnvelope = new GeneralEnvelope(new double[] {
envelope.getMinX(), envelope.getMinY()
}, new double[] { envelope.getMaxX(), envelope.getMaxY() });
}
destinationEnvelope.setCoordinateReferenceSystem(sourceCRS);
// this is the destination envelope in the coverage crs
final GeneralEnvelope destinationEnvelopeInSourceCRS = (!deviceCRSToGCCRSTransform
.isIdentity()) ? CRS.transform(deviceCRSToGCCRSTransform, destinationEnvelope)
: new GeneralEnvelope(destinationEnvelope);
destinationEnvelopeInSourceCRS.setCoordinateReferenceSystem(cvCRS);
/**
* Reading Coverage on Requested Envelope
*/
Rectangle destinationSize = null;
if ((request.getGridLow() != null) && (request.getGridHigh() != null)) {
final int[] lowers = new int[] {
request.getGridLow()[0].intValue(), request.getGridLow()[1].intValue()
};
final int[] highers = new int[] {
request.getGridHigh()[0].intValue(), request.getGridHigh()[1].intValue()
};
destinationSize = new Rectangle(lowers[0], lowers[1], highers[0], highers[1]);
} else {
/*destinationSize = coverageReader.getOriginalGridRange().toRectangle();*/
throw new WcsException("Neither Grid Size nor Grid Resolution have been specified.");
}
/**
* Checking for supported Interpolation Methods
*/
Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
final String interpolationType = request.getInterpolation();
if (interpolationType != null) {
boolean interpolationSupported = false;
Iterator internal = meta.getInterpolationMethods().iterator();
while (internal.hasNext()) {
if (interpolationType.equalsIgnoreCase((String) internal.next())) {
interpolationSupported = true;
}
}
if (!interpolationSupported) {
throw new WcsException(
"The requested Interpolation method is not supported by this Coverage.");
} else {
if (interpolationType.equalsIgnoreCase("bilinear")) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
} else if (interpolationType.equalsIgnoreCase("bicubic")) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
}
}
}
// /////////////////////////////////////////////////////////
//
// Reading the coverage
//
// /////////////////////////////////////////////////////////
parameters.put(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString(),
new GridGeometry2D(new GeneralGridEnvelope(destinationSize), destinationEnvelopeInSourceCRS));
final GridCoverage coverage = coverageReader.read(CoverageUtils.getParameters(
coverageReader.getFormat().getReadParameters(), parameters, true));
if ((coverage == null) || !(coverage instanceof GridCoverage2D)) {
throw new IOException("The requested coverage could not be found.");
}
/**
* Band Select
*/
Coverage bandSelectedCoverage = null;
bandSelectedCoverage = WCSUtils.bandSelect(request.getParameters(), coverage);
/**
* Crop
*/
final GridCoverage2D croppedGridCoverage = WCSUtils.crop(bandSelectedCoverage,
(GeneralEnvelope) coverage.getEnvelope(), cvCRS, destinationEnvelopeInSourceCRS,
Boolean.TRUE);
/**
* Scale/Resampling (if necessary)
*/
GridCoverage2D subCoverage = croppedGridCoverage;
final GeneralGridEnvelope newGridrange = new GeneralGridEnvelope(destinationSize);
/*if (!newGridrange.equals(croppedGridCoverage.getGridGeometry()
.getGridRange())) {*/
subCoverage = WCSUtils.scale(croppedGridCoverage, newGridrange, croppedGridCoverage, cvCRS,
destinationEnvelopeInSourceCRS);
//}
/**
* Reproject
*/
subCoverage = WCSUtils.reproject(subCoverage, sourceCRS, targetCRS, interpolation);
return subCoverage;
}
}