Example: - * if a given {@code gridToCRS} was mapping the pixel corner to "real world" coordinates, then a call to - * translate(gridToCRS, {@link PixelInCell#CELL_CORNER}, {@link PixelInCell#CELL_CENTER}) - * will return a new transform performing the following steps: - *
- *
1. Translate the grid coordinates by +0.5, so that the (0,0) coordinate in "pixel center" convention - * map to (½,½) in "pixel corner" convention.
2. - *
3. Apply the transform described by the "pixel corner" convention.
4. - *
+ * if a given {@code gridToCRS} transform was mapping the cell corner to "real world" coordinates, then a call to + * translate(gridToCRS, {@link PixelInCell#CELL_CORNER CELL_CORNER}, {@link PixelInCell#CELL_CENTER CELL_CENTER}) + * will return a new transform performing the following steps: first convert grid coordinates from cell center + * convention ({@code desired}) to cell corner convention ({@code current}), then concatenate the given + * {@code gridToCRS} transform which was designed for the cell corner convention. + * The above-cited cell centercell corner conversion is done by translating the grid coordinates + * by +½, because the grid coordinates (0,0) relative to cell center is (½,½) relative to cell corner. * * If the given {@code gridToCRS} is null, then this method ignores all other arguments and returns {@code null}. - * Otherwise {@code current} and {@code expected} arguments must be non-null. + * Otherwise {@code current} and {@code desired} arguments must be non-null. * * @param gridToCRS a math transform from pixel coordinates to any CRS, or {@code null}. * @param current the pixel orientation of the given {@code gridToCRS} transform. - * @param expected the pixel orientation of the desired transform. - * @return the translation from {@code current} to {@code expected}, or {@code null} if {@code gridToCRS} was null. - * @throws IllegalArgumentException if {@code current} or {@code expected} is not a known code list value. + * @param desired the pixel orientation of the desired transform. + * @return the translation from {@code current} to {@code desired}, or {@code null} if {@code gridToCRS} was null. + * @throws IllegalArgumentException if {@code current} or {@code desired} is not a known code list value. */ - public static MathTransform translate(final MathTransform gridToCRS, final PixelInCell current, final PixelInCell expected) { - if (gridToCRS == null || expected.equals(current)) { + public static MathTransform translate(final MathTransform gridToCRS, final PixelInCell current, final PixelInCell desired) { + if (gridToCRS == null || desired.equals(current)) { return gridToCRS; } final int dimension = gridToCRS.getSourceDimensions(); - final double offset = getPixelTranslation(expected) - getPixelTranslation(current); + final double offset = getPixelTranslation(desired) - getPixelTranslation(current); final int ci; // Cache index. if (offset == -0.5) { ci = 2*dimension - 2; @@ -275,32 +274,27 @@ public final class PixelTranslation extends Static implements Serializable { * The given transform can have any number of input and output dimensions, but only two of them will be converted. * *
Example: - * if a given {@code gridToCRS} was mapping the upper-left corner to "real world" coordinates, then a call to - * translate(gridToCRS, {@link PixelOrientation#UPPER_LEFT}, {@link PixelOrientation#CENTER}, 0, 1) - * will return a new transform performing the following steps: - *
- *
1. Translate the grid coordinates by +0.5, so that the (0,0) coordinate in "pixel center" convention - * map to (½,½) in "upper-left corner" convention. This translation is applied only in the specified - * grid (source) dimensions; all other dimensions (if any) are left unchanged.
2. - *
3. Apply the transform described by the "upper-left corner" convention.
4. - *
*
• invoke {@code warning(LogRecord)} directly, or
• - *
• override {@code warning(LogRecord)} and invoke {@link LogRecord#setThrown(Throwable)} explicitely, or
• + *
• override {@code warning(LogRecord)} and invoke {@link LogRecord#setThrown(Throwable)} explicitly, or
• *
• register a listener which will log the record itself.
• *

Pre-requite:

- *
- *
• {@link #build(Vector, Vector, String)} must have been invoked before this method.
• - *
• {@link ImageFileDirectory} must have filled its part of metadata before to invoke this method.
• - *
• {@link MetadataBuilder#newGridRepresentation(MetadataBuilder.GridType)} should have been invoked - * with the appropriate {@code GEORECTIFIED} or {@code GEOREFERENCEABLE} type.
• - *
Pixel center versus pixel corner
+ * The policy about whether the conversion map pixel corner or pixel center if GeoTIFF files does not seem + * totally clear. But the practice at least with GDAL seems to consider the following as equivalent: + * + * {@preformat text + * ModelTiepointTag = (0.0, 0.0, 0.0, -180.0, 90.0, 0.0) + * ModelPixelScaleTag = (0.002777777778, 0.002777777778, 0.0) + * GeoKeyDirectoryTag: + * GTModelTypeGeoKey = 2 (ModelTypeGeographic) + * GTRasterTypeGeoKey = 1 (RasterPixelIsArea) + * GeographicTypeGeoKey = 4326 (GCS_WGS_84) + * } + * + * and + * + * {@preformat text + * ModelTiepointTag = (-0.5, -0.5, 0.0, -180.0, 90.0, 0.0) + * ModelPixelScaleTag = (0.002777777778, 0.002777777778, 0.0) + * GeoKeyDirectoryTag: + * GTModelTypeGeoKey = 2 (ModelTypeGeographic) + * GTRasterTypeGeoKey = 2 (RasterPixelIsPoint) + * GeographicTypeGeoKey = 4326 (GCS_WGS_84) + * } + * + * The former is {@link PixelInCell#CELL_CORNER} convention while the later is {@link PixelInCell#CELL_CENTER}. + * Note that the translation coefficients in the grid to CRS matrix is {@code crs - grid × scale}. + * So compared to the {@code CELL_CORNER} case, the {@code CELL_CENTER} case has a translation of +0.5 × scale. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * @since 1.0 + * @module + */ +final class GridGeometryBuilder { + /** + * The reader for which we will create coordinate reference systems. + * This is used for reporting warnings. + */ + private final Reader reader; + + //////////////////////////////////////////////////////////////////////////////////////// + //// //// + //// Information to be set by ImageFileDirectory during GeoTIFF file parsing. //// + //// //// + //////////////////////////////////////////////////////////////////////////////////////// + + /** + * References the {@link GeoKeys} needed for building the Coordinate Reference System. + */ + public Vector keyDirectory; + + /** + * The numeric values referenced by the {@link #keyDirectory}. + */ + public Vector numericParameters; + + /** + * The characters referenced by the {@link #keyDirectory}. + */ + public String asciiParameters; + + /** + * Raster model tie points. This vector contains ordinate values structured as (I,J,K, X,Y,Z) records. + * The (I,J,K) ordinate values specify the point at location (I,J) in raster space with pixel-value K, + * and (X,Y,Z) ordinate values specify the point in the Coordinate Reference System. In most cases the + * coordinate system is only two-dimensional, in which case both K and Z should be set to zero. + */ + public Vector modelTiePoints; + + /** + * The conversion from grid coordinates to CRS coordinates as an affine transform. + * The "grid to CRS" transform can be determined in different ways, from simpler to more complex: + * + *
+ *
• By a combination of a single {@link #modelTiePoints} with the 3 values given in + * {@link Tags#ModelPixelScaleTag} as documented in the Javadoc of that tag.
• + *
• By a {@link Tags#ModelTransformation} giving all coefficients of the 4×4 matrix}. + * Note that the third row and the third column have all their value set to 0 if the + * space model (or the coordinate reference system) should be two-dimensional.
• + *
• By building a non-linear transformation from all {@link #modelTiePoints}. + * Such transformation can not be stored in a matrix, so will leave this field {@code null}.
• + *
+ * + * By convention, the translation column is set to NaN values if it needs to be computed from the tie point. + */ + private MatrixSIS affine; + + /** + * {@code true} if {@link #affine} has been specified by a complete matrix ({@link Tags#ModelTransformation}), + * or {@code false} if it has been specified by the scale factors only ({@link Tags#ModelPixelScaleTag}). + */ + private boolean completeMatrixSpecified; + + /** + * Sets the {@link #affine} transform from a complete matrix. + * + * @param terms the matrix in a row-major fashion. + * @param size the matrix size, either 3 or 4. + */ + public void setGridToCRS(final Vector terms, final int size) { + final int length = terms.size(); + completeMatrixSpecified = true; + affine = Matrices.createZero(size, size); + affine.setElement(size-1, size-1, 1); + for (int i=0; iPre-requite:

+ *
+ *
• {@link #build(GridExtent)} must have been invoked successfully before this method.
• + *
• {@link ImageFileDirectory} must have filled its part of metadata before to invoke this method.
• + *
- *
• By a combination of a single {@link #modelTiePoints} with the 3 values given in - * {@link Tags#ModelPixelScaleTag} as documented in the Javadoc of that tag.
• - *
• By a {@link Tags#ModelTransformation} giving all coefficients of the 4×4 matrix}. - * Note that the third row and the third column have all their value set to 0 if the - * space model (or the coordinate reference system) should be two-dimensional.
• - *
• By building a non-linear transformation from all {@link #modelTiePoints}. - * Such transformation can not be stored in a matrix, so will leave this field {@code null}.
• + *
• + *
• + *
• + *