/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2004-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.geometry; import java.awt.geom.Rectangle2D; import org.opengis.coverage.grid.GridEnvelope; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.geometry.Envelope; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.metadata.spatial.PixelOrientation; import org.geotoolkit.resources.Errors; import org.geotoolkit.util.Cloneable; import org.geotoolkit.internal.InternalUtilities; import org.geotoolkit.metadata.iso.spatial.PixelTranslation; import org.geotoolkit.display.shape.XRectangle2D; import org.apache.sis.geometry.Envelopes; import static org.apache.sis.util.ArgumentChecks.*; /** * A minimum bounding box or rectangle. * * <p>This class is kept as a workaround for the "not yet working" {@code GeneralEnvelope(GeographicBoundingBox)} * constructor in the Apache SIS class, and for some methods not yet ported to SIS (because of uncertain value).</p> * * <p>Note that the {@code reorderCorners()} method in Geotk has been renamed {@code simplify()} in SIS, * and {@code reduceToDomain(false)} has been renamed as {@code normalize()}.</p> * * @author Martin Desruisseaux (IRD, Geomatys) * @author Simone Giannecchini (Geosolutions) * @author Johann Sorel (Geomatys) * @version 3.21 * * @since 1.2 * @module * * @deprecated Moved to Apache SIS as {@link org.apache.sis.geometry.GeneralEnvelope}. */ @Deprecated public class GeneralEnvelope extends org.apache.sis.geometry.GeneralEnvelope implements Cloneable { /** * Serial number for inter-operability with different versions. */ private static final long serialVersionUID = 1752330560227688940L; /** * Constructs a new envelope with the same data than the specified envelope. * * @param envelope The envelope to copy. * * @see Envelope2D#Envelope2D(Envelope) */ public GeneralEnvelope(final Envelope envelope) { super(envelope); } /** * Constructs two-dimensional envelope defined by a {@link Rectangle2D}. * The coordinate reference system is initially undefined. * * @param rect The rectangle to copy. * * @see Envelope2D#Envelope2D(CoordinateReferenceSystem, Rectangle2D) */ public GeneralEnvelope(final Rectangle2D rect) { super(new double[] {rect.getMinX(), rect.getMinY()}, new double[] {rect.getMaxX(), rect.getMaxY()}); } /** * Constructs a georeferenced envelope from a grid envelope transformed using the specified * math transform. The <cite>grid to CRS</cite> transform should map either the * {@linkplain PixelInCell#CELL_CENTER cell center} (as in OGC convention) or * {@linkplain PixelInCell#CELL_CORNER cell corner} (as in Java2D/JAI convention) * depending on the {@code anchor} value. This constructor creates an envelope * containing entirely all pixels on a <cite>best effort</cite> basis - usually * accurate for affine transforms. * <p> * <b>Note:</b> The convention is specified as a {@link PixelInCell} code instead than * the more detailed {@link PixelOrientation}, because the later is restricted to the * two-dimensional case while the former can be used for any number of dimensions. * * @param gridEnvelope The grid envelope in integer coordinates. * @param anchor Whatever grid coordinates map to pixel center or pixel corner. * @param gridToCRS The transform (usually affine) from grid envelope to the CRS. * @param crs The CRS for the envelope to be created, or {@code null} if unknown. * * @throws MismatchedDimensionException If one of the supplied object doesn't have * a dimension compatible with the other objects. * @throws IllegalArgumentException if an argument is illegal for some other raisons, * including failure to use the provided math transform. * * @since 2.3 * * @see org.geotoolkit.referencing.operation.builder.GridToEnvelopeMapper * @see org.geotoolkit.coverage.grid.GeneralGridEnvelope#GeneralGridEnvelope(Envelope,PixelInCell,boolean) */ public GeneralEnvelope(final GridEnvelope gridEnvelope, final PixelInCell anchor, final MathTransform gridToCRS, final CoordinateReferenceSystem crs) throws IllegalArgumentException { // Uncomment next line if Sun fixes RFE #4093999 // ensureNonNull("gridEnvelope", gridEnvelope); super(gridEnvelope.getDimension()); ensureNonNull("gridToCRS", gridToCRS); final int dimension = getDimension(); ensureSameDimension(dimension, gridToCRS.getSourceDimensions()); ensureSameDimension(dimension, gridToCRS.getTargetDimensions()); final double offset = PixelTranslation.getPixelTranslation(anchor) + 0.5; for (int i=0; i<dimension; i++) { /* * According OpenGIS specification, GridGeometry maps pixel's center. We want a bounding * box for all pixels, not pixel's centers. Offset by 0.5 (use -0.5 for maximum too, not * +0.5, since maximum is exclusive). * * Note: the offset of 1 after getHigh(i) is because high values are inclusive according * ISO specification, while our algorithm and Java usage expect exclusive values. */ setRange(i, gridEnvelope.getLow(i) - offset, gridEnvelope.getHigh(i) - (offset - 1)); } final Envelope transformed; try { transformed = Envelopes.transform(gridToCRS, this); } catch (TransformException exception) { throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalTransformForType_1, gridToCRS.getClass()), exception); } super.setEnvelope(transformed); setCoordinateReferenceSystem(crs); } /** * Makes sure the specified dimensions are identical. */ private static void ensureSameDimension(final int dim1, final int dim2) throws MismatchedDimensionException { if (dim1 != dim2) { throw new MismatchedDimensionException(Errors.format( Errors.Keys.MismatchedDimension_2, dim1, dim2)); } } private double[] ordinates() { final int dimension = super.getDimension(); final double[] ordinates = new double[dimension * 2]; for (int i=0; i<dimension; i++) { ordinates[i] = super.getLower(i); ordinates[i + dimension] = super.getUpper(i); } return ordinates; } private void ordinates(final double[] ordinates) { final int dimension = super.getDimension(); for (int i=0; i<dimension; i++) { super.setRange(i, ordinates[i], ordinates[i + dimension]); } } /** * Fixes rounding errors up to a given tolerance level. For each value {@code ordinates[i]} * at dimension <var>i</var>, this method multiplies the ordinate value by the given factor, * then round the result only if the product is close to an integer value. The threshold is * defined by the {@code maxULP} argument in ULP units (<cite>Unit in the Last Place</cite>). * If and only if the product has been rounded, it is divided by the factor and stored in this * envelope in place of the original ordinate. * <p> * This method is useful after envelope calculations subject to rounding errors, like the * {@link #GeneralEnvelope(GridEnvelope, PixelInCell, MathTransform, CoordinateReferenceSystem)} * constructor. * * @param factor The factor by which to multiply ordinates before rounding * and divide after rounding. A recommended value is 360. * @param maxULP The maximal change allowed in ULPs (Unit in the Last Place). * * @since 3.11 */ public void roundIfAlmostInteger(final double factor, final int maxULP) { ensureStrictlyPositive("factor", factor); final double[] ordinates = ordinates(); for (int i=0; i<ordinates.length; i++) { ordinates[i] = InternalUtilities.adjustForRoundingError(ordinates[i], factor, maxULP); } ordinates(ordinates); } /** * Returns a {@link Rectangle2D} with the {@linkplain #getMinimum(int) minimum} * and {@linkplain #getMaximum(int) maximum} values of this {@code Envelope}. * This envelope must be two-dimensional before this method is invoked. * * {@section Spanning the anti-meridian of a Geographic CRS} * If this envelope spans the anti-meridian, then the longitude dimension will be * extended to full range of its coordinate system axis (typically [-180 … 180]°). * * @return This envelope as a two-dimensional rectangle. * @throws IllegalStateException if this envelope is not two-dimensional. * * @since 3.20 (derived from 3.00) */ public Rectangle2D toRectangle2D() throws IllegalStateException { final int dimension = getDimension(); if (dimension != 2) { throw new IllegalStateException(Errors.format( Errors.Keys.NotTwoDimensional_1, dimension)); } return XRectangle2D.createFromExtremums( getMinimum(0), getMinimum(1), getMaximum(0), getMaximum(1)); } }