/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2005-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, Geomatys * * 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.geotoolkit.coverage; import javax.media.jai.PropertySource; import org.opengis.coverage.Coverage; import org.opengis.coverage.SampleDimension; import org.opengis.coverage.CannotEvaluateException; import org.opengis.coverage.PointOutsideCoverageException; import org.opengis.geometry.Envelope; import org.opengis.geometry.DirectPosition; import org.opengis.util.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.geotoolkit.lang.Decorator; import org.apache.sis.geometry.Envelopes; import org.apache.sis.referencing.CRS; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.util.Utilities; /** * 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 inefficient if a large amount of points is * queried. In the later case, consider reprojecting the whole grid coverage instead. * * {@section Synchronization} * This class is not thread safe for performance reasons. If desired, users should create one * instance of {@code TransformedCoverage} for each thread. * * @author Martin Desruisseaux (IRD) * @version 3.00 * * @since 2.1 * @module */ @Decorator(Coverage.class) public class TransformedCoverage extends AbstractCoverage { /** * For cross-version compatibility. */ private static final long serialVersionUID = 638094266593359879L; /** * The wrapped coverage. This is the coverage where {@code evaluate} methods in * this class will delegate the work, after having transformed the coordinates. */ protected final Coverage coverage; /** * The transform from this coverage CRS to the CRS of the wrapped coverage. */ protected final MathTransform toOriginalCRS; /** * The projected point. */ private transient DirectPosition 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; toOriginalCRS = CRS.findOperation(crs, coverage.getCoordinateReferenceSystem(), null).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 (Utilities.equalsIgnoreMetadata(coverage.getCoordinateReferenceSystem(), crs)) { return coverage; } if (coverage.getClass() == TransformedCoverage.class) { 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. */ @Override 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. */ @Override 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 CannotEvaluateException transformationFailed(final TransformException cause) { return new CannotEvaluateException("Transformation failed", cause); } /** * Returns the envelope. */ @Override public Envelope getEnvelope() { final GeneralEnvelope envelope; try { envelope = Envelopes.transform(toOriginalCRS.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. */ @Override public final Object evaluate(final DirectPosition coord) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position)); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of boolean values for a given point in the coverage. */ @Override public final boolean[] evaluate(final DirectPosition coord, boolean[] dest) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of byte values for a given point in the coverage. */ @Override public final byte[] evaluate(final DirectPosition coord, byte[] dest) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of integer values for a given point in the coverage. */ @Override public final int[] evaluate(final DirectPosition coord, int[] dest) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of float values for a given point in the coverage. */ @Override public final float[] evaluate(final DirectPosition coord, float[] dest) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } /** * Returns a sequence of double values for a given point in the coverage. */ @Override public final double[] evaluate(final DirectPosition coord, final double[] dest) throws PointOutsideCoverageException, CannotEvaluateException { try { return coverage.evaluate(position = toOriginalCRS.transform(coord, position), dest); } catch (TransformException exception) { throw transformationFailed(exception); } } }