/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-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.grid; import java.awt.Rectangle; import java.awt.image.RenderedImage; import org.geotools.geometry.Envelope2D; import org.geotools.metadata.iso.spatial.PixelTranslation; import org.opengis.referencing.datum.PixelInCell; import org.opengis.util.Cloneable; import org.opengis.coverage.grid.GridEnvelope; import org.opengis.geometry.Envelope; /** * Defines a range of two-dimensional grid coverage coordinates. This implementation extends * {@link Rectangle} for interoperability with Java2D. Note that at the opposite of * {@link GeneralGridEnvelope}, this class is mutable. *
* CAUTION: * ISO 19123 defines {@linkplain #getHigh high} coordinates as inclusive. * We follow this specification for all getters methods, but keep in mind that this is the * opposite of Java2D usage where {@link Rectangle} maximal values are exclusive. * * @since 2.5 * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux * * @see GeneralGridEnvelope */ public class GridEnvelope2D extends Rectangle implements GridEnvelope, Cloneable { /** * For cross-version interoperability. */ private static final long serialVersionUID = -3370515914148690059L; /** * Creates an initially empty grid envelope. */ public GridEnvelope2D() { } /** * Creates a grid envelope initialized to the specified rectangle. * * @param rectangle The rectangle to use for initializing this grid envelope. */ public GridEnvelope2D(final Rectangle rectangle) { super(rectangle); } /** * Creates a grid envelope initialized to the specified rectangle. * * @param x The minimal x ordinate. * @param y The minimal y ordinate. * @param width The number of valid ordinates along the x axis. * @param height The number of valid ordinates along the y axis. */ public GridEnvelope2D(final int x, final int y, final int width, final int height) { super(x, y, width, height); } /** * Casts the specified envelope into a grid envelope. This is sometime useful after an * envelope has been transformed from "real world" coordinates to grid coordinates using the * {@linkplain org.opengis.coverage.grid.GridGeometry#getGridToCRS grid to CRS} transform. * The floating point values are rounded toward the nearest integers. *
* Notice that highest values are interpreted as non-inclusive * *
* Anchor
* According OpenGIS specification, {@linkplain org.opengis.coverage.grid.GridGeometry grid
* geometry} maps pixel's center. But envelopes typically encompass all pixels. This means
* that grid coordinates (0,0) has an envelope starting at (-0.5, -0.5). In order to revert
* back such envelope to a grid envelope, it is necessary to add 0.5 to every coordinates
* (including the maximum value since it is exclusive in a grid envelope). This offset is
* applied only if {@code anchor} is {@link PixelInCell#CELL_CENTER}. Users who don't want
* such offset should specify {@link PixelInCell#CELL_CORNER}.
*
* The convention is specified as a {@link PixelInCell} code instead than the more detailed * {@link org.opengis.metadata.spatial.PixelOrientation} because the latter is restricted to * the two-dimensional case while the former can be used for any number of dimensions. * * @param envelope * The envelope to use for initializing this grid envelope. * @param anchor * Whatever envelope coordinates map to pixel center or pixel corner. Should be * {@link PixelInCell#CELL_CENTER} for OGC convention (an offset of 0.5 will be * added to every envelope coordinate values), or {@link PixelInCell#CELL_CORNER} * for Java2D/JAI convention (no offset will be added). * @throws IllegalArgumentException * If {@code anchor} is not valid. * */ public GridEnvelope2D(final Envelope2D envelope, final PixelInCell anchor) throws IllegalArgumentException { this(envelope,anchor,false); } /** * Casts the specified envelope into a grid envelope. This is sometime useful after an * envelope has been transformed from "real world" coordinates to grid coordinates using the * {@linkplain org.opengis.coverage.grid.GridGeometry#getGridToCRS grid to CRS} transform. * The floating point values are rounded toward the nearest integers. *
* Note about rounding mode
* It would have been possible to round the {@linkplain Envelope#getMinimum minimal value}
* toward {@linkplain Math#floor floor} and the {@linkplain Envelope#getMaximum maximal value}
* toward {@linkplain Math#ceil ceil} in order to make sure that the grid envelope encompass
* fully the envelope - like what Java2D does when converting {@link java.awt.geom.Rectangle2D}
* to {@link Rectangle}). But this approach may increase by 1 or 2 units the image
* {@linkplain RenderedImage#getWidth width} or {@linkplain RenderedImage#getHeight height}. For
* example the range {@code [-0.25 ... 99.75]} (which is exactly 101 units wide) would be casted
* to {@code [-1 ... 100]}, which is 102 units wide. This leads to unexpected results when using
* grid envelope with image operations like "{@link javax.media.jai.operator.AffineDescriptor
* Affine}". For avoiding such changes in size, it is necessary to use the same rounding mode
* for both minimal and maximal values. The selected rounding mode is {@linkplain Math#round
* nearest integer} in this implementation.
*
* Anchor
* According OpenGIS specification, {@linkplain org.opengis.coverage.grid.GridGeometry grid
* geometry} maps pixel's center. But envelopes typically encompass all pixels. This means
* that grid coordinates (0,0) has an envelope starting at (-0.5, -0.5). In order to revert
* back such envelope to a grid envelope, it is necessary to add 0.5 to every coordinates
* (including the maximum value since it is exclusive in a grid envelope). This offset is
* applied only if {@code anchor} is {@link PixelInCell#CELL_CENTER}. Users who don't want
* such offset should specify {@link PixelInCell#CELL_CORNER}.
*
* The convention is specified as a {@link PixelInCell} code instead than the more detailed
* {@link org.opengis.metadata.spatial.PixelOrientation} because the latter is restricted to
* the two-dimensional case while the former can be used for any number of dimensions.
*
* @param envelope
* The envelope to use for initializing this grid envelope.
* @param anchor
* Whatever envelope coordinates map to pixel center or pixel corner. Should be
* {@link PixelInCell#CELL_CENTER} for OGC convention (an offset of 0.5 will be
* added to every envelope coordinate values), or {@link PixelInCell#CELL_CORNER}
* for Java2D/JAI convention (no offset will be added).
* @param isHighIncluded
* {@code true} if the envelope maximal values are inclusive, or {@code false} if
* they are exclusive. This argument does not apply to minimal values, which are
* always inclusive.
* @throws IllegalArgumentException
* If {@code anchor} is not valid.
*
*/
public GridEnvelope2D(final Envelope2D envelope, final PixelInCell anchor,
final boolean isHighIncluded)
throws IllegalArgumentException
{
final double offset = PixelTranslation.getPixelTranslation(anchor) + 0.5;
final int dimension = envelope.getDimension();
assert dimension==2;
final int[] index = new int[dimension * 2];
for (int i=0; i