/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.coverage; // J2SE and JAI dependencies import javax.media.jai.PropertySource; import org.geotools.factory.Hints; import org.geotools.geometry.GeneralDirectPosition; import org.geotools.geometry.GeneralEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; import org.opengis.coverage.CannotEvaluateException; import org.opengis.coverage.Coverage; import org.opengis.coverage.PointOutsideCoverageException; import org.opengis.coverage.SampleDimension; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; /** * A coverage wrapping an other one with a different coordinate reference system. The coordinate * transformation is applied on the fly every time an {@code evaluate} method is invoked. It may * be efficient if few points are queried, but become ineficient if a large amount of points is * queried. In the later case, consider reprojecting the whole grid coverage instead. * <br><br> * <strong>Note:</strong> This class is not thread safe for performance reasons. If desired, * users should create one instance of {@code TransformedCoverage} for each thread. * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @since 2.1 */ public class TransformedCoverage extends AbstractCoverage { /** * The hints for the creation of coordinate operation. * The default coordinate operation factory should be suffisient. */ private static final Hints HINTS = null; /** * The wrapped coverage. */ protected final Coverage coverage; /** * The transform from this coverage CRS to the wrapped coverage CRS. */ private final MathTransform toWrapped; /** * The projected point. */ private final GeneralDirectPosition position; /** * Creates a new coverage wrapping the specified one. * * @param name The name for this new coverage. * @param crs The crs for this coverage. * @param coverage The coverage to wraps. * @throws FactoryException if no transformation can be found from the coverage CRS to the * specified CRS. */ protected TransformedCoverage(final CharSequence name, final CoordinateReferenceSystem crs, final Coverage coverage) throws FactoryException { super(name, crs, (coverage instanceof PropertySource) ? ((PropertySource) coverage) : null, null); this.coverage = coverage; position = new GeneralDirectPosition(crs.getCoordinateSystem().getDimension()); toWrapped = ReferencingFactoryFinder.getCoordinateOperationFactory(HINTS) .createOperation(crs, coverage.getCoordinateReferenceSystem()) .getMathTransform(); } /** * Creates a new coverage wrapping the specified one with a different CRS. * If the specified coverage already uses the specified CRS (or an equivalent one), * it is returned unchanged. * * @param name The name for this new coverage. * @param crs The crs for this coverage. * @param coverage The coverage to wraps. * @return A coverage using the specified CRS. * @throws FactoryException if no transformation can be found from the coverage CRS to the * specified CRS. */ public static Coverage reproject(final CharSequence name, final CoordinateReferenceSystem crs, Coverage coverage) throws FactoryException { while (true) { if (CRS.equalsIgnoreMetadata(coverage.getCoordinateReferenceSystem(), crs)) { return coverage; } if (TransformedCoverage.class.equals(coverage.getClass())) { coverage = ((TransformedCoverage) coverage).coverage; continue; } break; } return new TransformedCoverage(name, crs, coverage); } /** * The number of sample dimensions in the coverage. * For grid coverages, a sample dimension is a band. * * @return The number of sample dimensions in the coverage. */ public int getNumSampleDimensions() { return coverage.getNumSampleDimensions(); } /** * Retrieve sample dimension information for the coverage. * * @param index Index for sample dimension to retrieve. Indices are numbered 0 to * (<var>{@linkplain #getNumSampleDimensions n}</var>-1). * @return Sample dimension information for the coverage. * @throws IndexOutOfBoundsException if {@code index} is out of bounds. */ public SampleDimension getSampleDimension(final int index) throws IndexOutOfBoundsException { return coverage.getSampleDimension(index); } /** * Wraps the checked exception into an unchecked one. * * @todo Provides a localized message. */ private final CannotEvaluateException transformationFailed(final TransformException cause) { return new CannotEvaluateException("Transformation failed", cause); } /** * Returns the envelope. */ public Envelope getEnvelope() { final GeneralEnvelope envelope; try { envelope = CRS.transform(toWrapped.inverse(), coverage.getEnvelope()); } catch (TransformException exception) { throw transformationFailed(exception); } envelope.setCoordinateReferenceSystem(crs); return envelope; } /** * Returns the value vector for a given point in the coverage. * * @param coord The coordinate point where to evaluate. * @throws PointOutsideCoverageException if {@code coord} is outside coverage. * @throws CannotEvaluateException if the computation failed for some other reason. */ public final Object evaluate(final DirectPosition coord) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position)); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of boolean values for a given point in the coverage. */ public final boolean[] evaluate(final DirectPosition coord, boolean[] dest) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of byte values for a given point in the coverage. */ public final byte[] evaluate(final DirectPosition coord, byte[] dest) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of integer values for a given point in the coverage. */ public final int[] evaluate(final DirectPosition coord, int[] dest) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of float values for a given point in the coverage. */ public final float[] evaluate(final DirectPosition coord, float[] dest) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of double values for a given point in the coverage. */ public final double[] evaluate(final DirectPosition coord, final double[] dest) throws CannotEvaluateException { try { return coverage.evaluate(toWrapped.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } }