001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.wcs.responses;
006:
007: import org.geotools.coverage.grid.GeneralGridRange;
008: import org.geotools.coverage.grid.GridCoverage2D;
009: import org.geotools.coverage.grid.GridGeometry2D;
010: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
011: import org.geotools.coverage.grid.io.AbstractGridFormat;
012: import org.geotools.factory.Hints;
013: import org.geotools.geometry.GeneralEnvelope;
014: import org.geotools.referencing.CRS;
015: import org.geotools.resources.CRSUtilities;
016: import org.opengis.coverage.Coverage;
017: import org.opengis.coverage.grid.Format;
018: import org.opengis.coverage.grid.GridCoverage;
019: import org.opengis.parameter.ParameterValueGroup;
020: import org.opengis.referencing.FactoryException;
021: import org.opengis.referencing.crs.CoordinateReferenceSystem;
022: import org.opengis.referencing.cs.AxisDirection;
023: import org.opengis.referencing.operation.MathTransform;
024: import org.opengis.referencing.operation.TransformException;
025: import org.vfny.geoserver.Request;
026: import org.vfny.geoserver.Response;
027: import org.vfny.geoserver.ServiceException;
028: import org.vfny.geoserver.global.CoverageInfo;
029: import org.vfny.geoserver.global.Data;
030: import org.vfny.geoserver.global.GeoServer;
031: import org.vfny.geoserver.global.Service;
032: import org.vfny.geoserver.util.CoverageUtils;
033: import org.vfny.geoserver.util.WCSUtils;
034: import org.vfny.geoserver.wcs.WcsException;
035: import org.vfny.geoserver.wcs.requests.CoverageRequest;
036: import java.awt.Rectangle;
037: import java.io.IOException;
038: import java.io.OutputStream;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.Map;
042: import java.util.NoSuchElementException;
043: import java.util.logging.Level;
044: import java.util.logging.Logger;
045: import javax.media.jai.Interpolation;
046:
047: /**
048: * DOCUMENT ME!
049: *
050: * @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last
051: * modification)
052: * @author $Author: Simone Giannecchini (simboss1@gmail.com) $ (last
053: * modification)
054: */
055: public class CoverageResponse implements Response {
056: /** Standard logging instance for class */
057: private static final Logger LOGGER = org.geotools.util.logging.Logging
058: .getLogger("org.vfny.geoserver.responses");
059: private final static Hints LENIENT_HINT = new Hints(
060: Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
061: private final static Hints IGNORE_OVERVIEWS = new Hints(
062: Hints.IGNORE_COVERAGE_OVERVIEW, Boolean.TRUE);
063: private final static Hints hints = new Hints(new HashMap(5));
064:
065: static {
066: // ///////////////////////////////////////////////////////////////////
067: //
068: // HINTS
069: //
070: // ///////////////////////////////////////////////////////////////////
071: hints.add(LENIENT_HINT);
072: hints.add(IGNORE_OVERVIEWS);
073: }
074:
075: /**
076: * Tolerance for NOT drawing a coverage.
077: *
078: * If after a scaling a coverage has all dimensions smaller than
079: * {@link GridCoverageRenderer#MIN_DIM_TOLERANCE} we just do not draw it.
080: */
081: private static final int MIN_DIM_TOLERANCE = 1;
082:
083: /**
084: *
085: */
086: CoverageResponseDelegate delegate;
087:
088: /**
089: * This is the request provided to the execute( Request ) method.
090: *
091: * <p>
092: * We save it so we can access the handle provided by the user for error
093: * reporting during the writeTo( OutputStream ) opperation.
094: * </p>
095: *
096: * <p>
097: * This value will be <code>null</code> until execute is called.
098: * </p>
099: *
100: * @uml.property name="request"
101: * @uml.associationEnd multiplicity="(0 1)"
102: */
103: private CoverageRequest request;
104:
105: /**
106: * Empty constructor
107: */
108: public CoverageResponse() {
109: request = null;
110: }
111:
112: /**
113: * Returns any extra headers that this service might want to set in the HTTP response object.
114: * @see org.vfny.geoserver.Response#getResponseHeaders()
115: */
116: public HashMap getResponseHeaders() {
117: return null;
118: }
119:
120: /**
121: * DOCUMENT ME!
122: *
123: * @param gs
124: * DOCUMENT ME!
125: *
126: * @return DOCUMENT ME!
127: */
128: public String getContentType(GeoServer gs) {
129: return delegate.getContentType(gs);
130: }
131:
132: public String getContentEncoding() {
133: return delegate.getContentEncoding();
134: }
135:
136: public String getContentDisposition() {
137: return delegate.getContentDisposition();
138: }
139:
140: /**
141: * Jody here with one pass replacement for writeTo.
142: *
143: * <p>
144: * This code is a discussion point, when everyone has had there input we
145: * will try and set things up properly.
146: * </p>
147: *
148: * <p>
149: * I am providing a mirror of the existing desing: - execute gathers the
150: * resultList - sets up the header
151: * </p>
152: *
153: * @param out
154: * DOCUMENT ME!
155: *
156: * @throws WcsException
157: * DOCUMENT ME!
158: * @throws IOException
159: * DOCUMENT ME!
160: * @throws IllegalStateException
161: * DOCUMENT ME!
162: */
163: public void writeTo(OutputStream out) throws ServiceException,
164: IOException {
165: if ((request == null) || (delegate == null)) {
166: throw new IllegalStateException(
167: "execute has not been called prior to writeTo");
168: }
169:
170: delegate.encode(out);
171: }
172:
173: /**
174: * Executes CoverageRequest.
175: *
176: * <p>
177: * Willing to execute a CoverageRequest.
178: * </p>
179: *
180: * @param req
181: * DOCUMENT ME!
182: *
183: * @throws WcsException
184: * DOCUMENT ME!
185: */
186: public void execute(Request req) throws WcsException {
187: execute((CoverageRequest) req);
188: }
189:
190: public void execute(CoverageRequest request) throws WcsException {
191: if (LOGGER.isLoggable(Level.FINEST)) {
192: LOGGER
193: .finest(new StringBuffer(
194: "execute CoverageRequest response. Called request is: ")
195: .append(request).toString());
196: }
197:
198: this .request = request;
199:
200: final String outputFormat = request.getOutputFormat();
201:
202: try {
203: delegate = CoverageResponseDelegateFactory
204: .encoderFor(outputFormat);
205: } catch (NoSuchElementException ex) {
206: WcsException newEx = new WcsException(new StringBuffer(
207: "output format: ").append(outputFormat).append(
208: " not ").append(
209: "supported by geoserver for this Coverage")
210: .toString(), ex);
211: throw newEx;
212: }
213:
214: final Data catalog = request.getWCS().getData();
215: CoverageInfo meta = null;
216: GridCoverage coverage = null;
217:
218: try {
219: meta = catalog.getCoverageInfo(request.getCoverage());
220:
221: if (!meta.getSupportedFormats().contains(
222: outputFormat.toUpperCase())) {
223: WcsException newEx = new WcsException(new StringBuffer(
224: "output format: ").append(outputFormat).append(
225: " not ").append(
226: "supported by geoserver for this Coverage")
227: .toString());
228: throw newEx;
229: }
230:
231: final Format format = meta.getFormatInfo().getFormat();
232: final AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) meta
233: .createReader(hints);
234:
235: // /////////////////////////////////////////////////////////
236: //
237: // Setting coverage reading params.
238: //
239: // /////////////////////////////////////////////////////////
240: final ParameterValueGroup params = reader.getFormat()
241: .getReadParameters();
242:
243: final GridCoverage2D finalCoverage = getFinalCoverage(
244: request, meta, reader, CoverageUtils
245: .getParametersKVP(params));
246: delegate.prepare(outputFormat, finalCoverage);
247: } catch (IOException e) {
248: final WcsException newEx = new WcsException(e,
249: "problem with CoverageResults", request.getHandle());
250:
251: throw newEx;
252: } catch (NoSuchElementException e) {
253: final WcsException newEx = new WcsException(e,
254: "problem with CoverageResults", request.getHandle());
255: throw newEx;
256: } catch (IllegalArgumentException e) {
257: final WcsException newEx = new WcsException(e,
258: "problem with CoverageResults", request.getHandle());
259: throw newEx;
260: } catch (SecurityException e) {
261: final WcsException newEx = new WcsException(e,
262: "problem with CoverageResults", request.getHandle());
263: throw newEx;
264: } catch (WcsException e) {
265: final WcsException newEx = new WcsException(e,
266: "problem with CoverageResults", request.getHandle());
267: throw newEx;
268: } catch (FactoryException e) {
269: final WcsException newEx = new WcsException(e,
270: "problem with CoverageResults", request.getHandle());
271: throw newEx;
272: } catch (IndexOutOfBoundsException e) {
273: final WcsException newEx = new WcsException(e,
274: "problem with CoverageResults", request.getHandle());
275: throw newEx;
276: } catch (TransformException e) {
277: final WcsException newEx = new WcsException(e,
278: "problem with CoverageResults", request.getHandle());
279: throw newEx;
280: }
281: }
282:
283: /**
284: * Release locks if we are into that sort of thing.
285: *
286: * @see org.vfny.geoserver.responses.Response#abort()
287: */
288: public void abort(Service gs) {
289: if (request == null) {
290: return; // request was not attempted
291: }
292:
293: Data catalog = gs.getData();
294: }
295:
296: /**
297: * GetCroppedCoverage
298: *
299: * @param request CoverageRequest
300: * @param meta CoverageInfo
301: * @param parameters
302: * @param coverage GridCoverage
303: * @return GridCoverage2D
304: * @throws WcsException
305: * @throws IOException
306: * @throws IndexOutOfBoundsException
307: * @throws FactoryException
308: * @throws TransformException
309: */
310: private static GridCoverage2D getFinalCoverage(
311: CoverageRequest request,
312: CoverageInfo meta,
313: AbstractGridCoverage2DReader coverageReader /*GridCoverage coverage*/,
314: Map parameters) throws WcsException, IOException,
315: IndexOutOfBoundsException, FactoryException,
316: TransformException {
317: // This is the final Response CRS
318: final String responseCRS = request.getResponseCRS();
319:
320: // - first check if the responseCRS is present on the Coverage
321: // ResponseCRSs list
322: if (!meta.getResponseCRSs().contains(responseCRS)) {
323: throw new WcsException(
324: "This Coverage does not support the requested Response-CRS.");
325: }
326:
327: // - then create the Coordinate Reference System
328: final CoordinateReferenceSystem targetCRS = CRS
329: .decode(responseCRS);
330:
331: // This is the CRS of the requested Envelope
332: final String requestCRS = request.getCRS();
333:
334: // - first check if the requestCRS is present on the Coverage
335: // RequestCRSs list
336: if (!meta.getResponseCRSs().contains(requestCRS)) {
337: throw new WcsException(
338: "This Coverage does not support the requested CRS.");
339: }
340:
341: // - then create the Coordinate Reference System
342: final CoordinateReferenceSystem sourceCRS = CRS
343: .decode(requestCRS);
344:
345: // This is the CRS of the Coverage Envelope
346: final CoordinateReferenceSystem cvCRS = ((GeneralEnvelope) coverageReader
347: .getOriginalEnvelope()).getCoordinateReferenceSystem();
348: final MathTransform GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform = CRS
349: .findMathTransform(cvCRS, sourceCRS, true);
350: final MathTransform GCCRSTodeviceCRSTransform = CRS
351: .findMathTransform(cvCRS, targetCRS, true);
352: final MathTransform deviceCRSToGCCRSTransform = GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform
353: .inverse();
354:
355: com.vividsolutions.jts.geom.Envelope envelope = request
356: .getEnvelope();
357: GeneralEnvelope destinationEnvelope;
358: final boolean lonFirst = sourceCRS.getCoordinateSystem()
359: .getAxis(0).getDirection().absolute().equals(
360: AxisDirection.EAST);
361:
362: // the envelope we are provided with is lon,lat always
363: if (!lonFirst) {
364: destinationEnvelope = new GeneralEnvelope(new double[] {
365: envelope.getMinY(), envelope.getMinX() },
366: new double[] { envelope.getMaxY(),
367: envelope.getMaxX() });
368: } else {
369: destinationEnvelope = new GeneralEnvelope(new double[] {
370: envelope.getMinX(), envelope.getMinY() },
371: new double[] { envelope.getMaxX(),
372: envelope.getMaxY() });
373: }
374:
375: destinationEnvelope.setCoordinateReferenceSystem(sourceCRS);
376:
377: // this is the destination envelope in the coverage crs
378: final GeneralEnvelope destinationEnvelopeInSourceCRS = (!deviceCRSToGCCRSTransform
379: .isIdentity()) ? CRSUtilities.transform(
380: deviceCRSToGCCRSTransform, destinationEnvelope)
381: : new GeneralEnvelope(destinationEnvelope);
382: destinationEnvelopeInSourceCRS
383: .setCoordinateReferenceSystem(cvCRS);
384:
385: /**
386: * Reading Coverage on Requested Envelope
387: */
388: Rectangle destinationSize = null;
389:
390: if ((request.getGridLow() != null)
391: && (request.getGridHigh() != null)) {
392: final int[] lowers = new int[] {
393: request.getGridLow()[0].intValue(),
394: request.getGridLow()[1].intValue() };
395: final int[] highers = new int[] {
396: request.getGridHigh()[0].intValue(),
397: request.getGridHigh()[1].intValue() };
398:
399: destinationSize = new Rectangle(lowers[0], lowers[1],
400: highers[0], highers[1]);
401: } else {
402: /*destinationSize = coverageReader.getOriginalGridRange().toRectangle();*/
403: throw new WcsException(
404: "Neither Grid Size nor Grid Resolution have been specified.");
405: }
406:
407: /**
408: * Checking for supported Interpolation Methods
409: */
410: Interpolation interpolation = Interpolation
411: .getInstance(Interpolation.INTERP_NEAREST);
412: final String interpolationType = request.getInterpolation();
413:
414: if (interpolationType != null) {
415: boolean interpolationSupported = false;
416: Iterator internal = meta.getInterpolationMethods()
417: .iterator();
418:
419: while (internal.hasNext()) {
420: if (interpolationType
421: .equalsIgnoreCase((String) internal.next())) {
422: interpolationSupported = true;
423: }
424: }
425:
426: if (!interpolationSupported) {
427: throw new WcsException(
428: "The requested Interpolation method is not supported by this Coverage.");
429: } else {
430: if (interpolationType.equalsIgnoreCase("bilinear")) {
431: interpolation = Interpolation
432: .getInstance(Interpolation.INTERP_BILINEAR);
433: } else if (interpolationType
434: .equalsIgnoreCase("bicubic")) {
435: interpolation = Interpolation
436: .getInstance(Interpolation.INTERP_BICUBIC);
437: }
438: }
439: }
440:
441: // /////////////////////////////////////////////////////////
442: //
443: // Reading the coverage
444: //
445: // /////////////////////////////////////////////////////////
446: parameters.put(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName()
447: .toString(), new GridGeometry2D(new GeneralGridRange(
448: destinationSize), destinationEnvelopeInSourceCRS));
449:
450: final GridCoverage coverage = coverageReader.read(CoverageUtils
451: .getParameters(coverageReader.getFormat()
452: .getReadParameters(), parameters, true));
453:
454: if ((coverage == null) || !(coverage instanceof GridCoverage2D)) {
455: throw new IOException(
456: "The requested coverage could not be found.");
457: }
458:
459: /**
460: * Band Select
461: */
462: Coverage bandSelectedCoverage = null;
463:
464: try {
465: bandSelectedCoverage = WCSUtils.bandSelect(request
466: .getParameters(), coverage);
467: } catch (WcsException e) {
468: throw new WcsException(e.getLocalizedMessage());
469: }
470:
471: /**
472: * Crop
473: */
474: final GridCoverage2D croppedGridCoverage = WCSUtils.crop(
475: bandSelectedCoverage, (GeneralEnvelope) coverage
476: .getEnvelope(), cvCRS,
477: destinationEnvelopeInSourceCRS, Boolean.TRUE);
478:
479: /**
480: * Scale/Resampling (if necessary)
481: */
482: GridCoverage2D subCoverage = croppedGridCoverage;
483: final GeneralGridRange newGridrange = new GeneralGridRange(
484: destinationSize);
485:
486: /*if (!newGridrange.equals(croppedGridCoverage.getGridGeometry()
487: .getGridRange())) {*/
488: subCoverage = WCSUtils.scale(croppedGridCoverage, newGridrange,
489: croppedGridCoverage, cvCRS,
490: destinationEnvelopeInSourceCRS);
491: //}
492:
493: /**
494: * Reproject
495: */
496: subCoverage = WCSUtils.reproject(subCoverage, sourceCRS,
497: targetCRS, interpolation);
498:
499: return subCoverage;
500: }
501: }
|