gov.nasa.worldwind.formats.tiff.GeoCodec Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.formats.tiff;
import gov.nasa.worldwind.util.*;
import gov.nasa.worldwind.geom.*;
import java.util.*;
/**
* A class to bundle the GeoTiff structures found in a GeoTiff file, and to assist in the coding/decoding of those
* structures.
*
* @author brownrigg
* @version $Id: GeoCodec.java 1171 2013-02-11 21:45:02Z dcollins $
*/
class GeoCodec
{
private HashMap geoKeys = null;
// Collection of ModelTiePoints.
private Vector tiePoints = new Vector(1);
// ModelPixelScale values...
private double xScale;
private double yScale;
private double zScale;
private Matrix modelTransform; // the ModelTransformation matrix
private short[] shortParams; // raw short parameters array
private double[] doubleParams; // raw double parameters array
private byte[] asciiParams; // raw ascii parameters array
public GeoCodec()
{
}
/**
* Adds ModelTiePoints from an array. Recall that by definition, a tie point is a 6-tuple of values.
*
* @param values A 6-tuple representing a Geotiff ModelTiePoint.
* @throws IllegalArgumentException if values not a multiple of 6.
*/
public void addModelTiePoints(double[] values) throws IllegalArgumentException
{
if (values == null || values.length == 0 || (values.length % 6) != 0)
{
String message = Logging.getMessage("GeoCodec.BadTiePoints");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
for (int i = 0; i < values.length; i += 6)
{
addModelTiePoint(values[i], values[i + 1], values[i + 2], values[i + 3], values[i + 4], values[i + 5]);
}
}
public void addModelTiePoint(double i, double j, double k, double x, double y, double z)
{
ModelTiePoint t = new ModelTiePoint(i, j, k, x, y, z);
this.tiePoints.add(t);
}
public ModelTiePoint[] getTiePoints()
{
ModelTiePoint[] tiePoints = new ModelTiePoint[this.tiePoints.size()];
return this.tiePoints.toArray(tiePoints);
}
/**
* Sets the 3 ModelPixelScale values.
*
* @param values The ModelPixelScale values.
* @throws IllegalArgumentException if values is not of length 3.
*/
public void setModelPixelScale(double[] values)
{
if (values == null || values.length != 3)
{
String message = Logging.getMessage("GeoCodec.BadPixelValues");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
this.setModelPixelScale( values[0], values[1], values[2] );
}
public void setModelPixelScale(double xScale, double yScale, double zScale)
{
this.xScale = xScale;
this.yScale = yScale;
this.zScale = zScale;
}
public double getModelPixelScaleX()
{
return this.xScale;
}
public double getModelPixelScaleY()
{
return this.yScale;
}
/**
* Sets the ModelTransformation matrix. This is logically a 4x4 matrix of doubles by definition.
*
* @param matrix A logical 4x4 matrix, in row-major order.
* @throws IllegalArgumentException if matrix is not of length 16.
*/
public void setModelTransformation(double[] matrix) throws IllegalArgumentException
{
if (matrix == null || matrix.length != 16)
{
String message = Logging.getMessage("GeoCodec.BadMatrix");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
this.setModelTransformation( Matrix.fromArray(matrix, 0, true) );
}
public void setModelTransformation(Matrix matrix)
{
this.modelTransform = matrix;
// There could be only ModelTransformation (34264) or ModelTiePoints (33922) & ModelPixelScale(3355)
// If we are here, we have ModelTransformation,
// so let's calculate ModelTiePoints of the image origin (0,0)
Matrix tp = this.modelTransform.multiply( Matrix.fromTranslation( 0d, 0d, 0d ));
this.addModelTiePoint( 0d, 0d, 0d, tp.m14, tp.m24, tp.m34 );
// Now, since ModelTiePoints (33922) & ModelPixelScale(3355) always go together
// let's set ModelPixelScale values
this.setModelPixelScale( matrix.m11 , matrix.m22, 0d );
}
/**
* Returns the bounding box of an image that is width X height pixels, as determined by this GeoCodec. Returns
* UnsupportedOperationException if the transformation can not be determined (see getXYZAtPixel()). The bounding Box
* is returned as an array of double of length 4: [0] is x coordinate of upper-left corner [1] is y coordinate of
* upper-left corner [2] is x coordinate of lower-right corner [3] is y coordinate of lower-right corner Note that
* coordinate units are those of the underlying modeling transformation, and are not guaranteed to be in lon/lat.
*
* @param width Width of a hypothetical image.
* @param height Height of a hypothetical image.
* @return Returns xUL, yUL, xLR, yLR of bounding box.
* @throws UnsupportedOperationException if georeferencing can not be computed.
*/
public double[] getBoundingBox(int width, int height) throws UnsupportedOperationException
{
double[] bbox = new double[4];
double[] pnt = getXYAtPixel(0, 0);
bbox[0] = pnt[0];
bbox[1] = pnt[1];
pnt = getXYAtPixel( height, width);
bbox[2] = pnt[0];
bbox[3] = pnt[1];
return bbox;
}
/**
* Returns the geocoordinates for a given pixel, as determined by the modeling coordinate tranformation embodied in
* the GeoCodec.
*
* TODO: Also throws UnsupportedOperationException if this is anything other than a "simple" georeferenced mapping,
* meaning that there's a single tie-point known about the point 0,0, we know the inter-pixel spacing, and there's
* no rotation of the image required. Geo referencing may also be specified via a general 4x4 matrix, or by a list
* if tie-points, implying a rubbersheeting transformation. These two cases remain to be implemented.
*
*
* @param row pixel-row index
* @param col pixel-column index
* @return double[2] containing x,y coordinate of pixel in modelling coordinate units.
* @throws IllegalArgumentException if row or column outside image bounds.
* @throws UnsupportedOperationException if georeferencing can not be determined.
*/
public double[] getXYAtPixel(int row, int col) throws UnsupportedOperationException
{
if (this.tiePoints.size() == 0 )
{
String message = Logging.getMessage("GeotiffReader.NotSimpleGeotiff");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
double[] xy = new double[2];
ModelTiePoint t = this.tiePoints.get(0);
xy[0] = t.x + col * this.xScale;
xy[1] = t.y - row * this.yScale;
return xy;
}
/**
* Gets the values of the given GeoKey as an array of ints.
*
* While this method handles the general case of multiple ints associated with a key, typically there will be only a
* single value.
*
* @param key GeoKey value
* @return Array of int values associated with the key, or null if the key was not found.
* @throws IllegalArgumentException Thrown if the key does not embody integer values.
*/
public int[] getGeoKeyAsInts(int key) throws IllegalArgumentException
{
int[] vals = null;
GeoKeyEntry entry;
if (this.geoKeys != null && (entry = this.geoKeys.get(key)) != null)
{
if (entry.array != this.shortParams)
{
String message = Logging.getMessage("GeoCodec.NotIntegerKey", key);
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
vals = new int[entry.count];
for (int i = 0; i < vals.length; i++)
{
vals[i] = 0xffff & (int) this.shortParams[entry.offset + i];
}
}
return vals;
}
/*
* Returns true if the given key is a GeoKey in this file; false otherwise.
*/
public boolean hasGeoKey(int key)
{
return (this.geoKeys != null && this.geoKeys.get(key) != null);
}
//
// Package visibility. Not generally intended for use by end users.
//
void setGeokeys(short[] keys)
{
// Decode the geokey entries into our internal management structure. Recall that the keys are organized as
// entries of 4 shorts, where the first 4-tuple contains versioning and the number of geokeys to follow.
// The remaining entries look very much like regular Tiff tags.
if (keys != null && keys.length > 4)
{
this.shortParams = new short[keys.length];
System.arraycopy(keys, 0, this.shortParams, 0, keys.length);
int numKeys = keys[3];
this.geoKeys = new HashMap();
int i = 0;
for (int k = 0; k < numKeys; k++ )
{
i += 4;
int tag = 0x0000ffff & keys[i];
int tagLoc = 0x0000ffff & keys[i + 1];
if (tagLoc == 0)
{
// value is in the 4th field of this entry...
this.geoKeys.put(tag, new GeoKeyEntry(tag, 1, i + 3, this.shortParams));
}
else
{
// in this case, one or more values are given relative to one of the params arrays...
Object sourceArray = null;
if (tagLoc == GeoTiff.Tag.GEO_KEY_DIRECTORY)
sourceArray = this.shortParams;
else if (tagLoc == GeoTiff.Tag.GEO_DOUBLE_PARAMS)
sourceArray = this.doubleParams;
else if (tagLoc == GeoTiff.Tag.GEO_ASCII_PARAMS)
sourceArray = this.asciiParams;
if (sourceArray != null)
this.geoKeys.put(tag, new GeoKeyEntry(tag, 0x0000ffff & keys[i + 2],
0x0000ffff & keys[i + 3], sourceArray));
}
}
}
}
//
// Package visibility. Not generally intended for use by end users.
//
void setDoubleParams(double[] params)
{
this.doubleParams = new double[params.length];
System.arraycopy(params, 0, this.doubleParams, 0, params.length);
}
//
// Package visibility. Not generally intended for use by end users.
//
void setAsciiParams(byte[] params)
{
this.asciiParams = new byte[params.length];
System.arraycopy(params, 0, this.asciiParams, 0, params.length);
}
/*
* A class to bundle up ModelTiePoints. From the Geotiff spec, a ModelTiePoint is a 6-tuple that is an
* association of the pixel to the model coordinate .
*
*/
public class ModelTiePoint
{
public double i, j, k, x, y, z;
public ModelTiePoint(double i, double j, double k, double x, double y, double z)
{
this.i = i;
this.j = j;
this.k = k;
this.x = x;
this.y = y;
this.z = z;
}
public double getRow()
{
return this.j;
}
public double getColumn()
{
return this.i;
}
public double getX()
{
return this.x;
}
public double getY()
{
return this.y;
}
}
/*
* A little class that we use to manage GeoKeys.
*/
private class GeoKeyEntry
{
int tag;
int count;
int offset;
Object array; // a reference to one of the short/double/asciiParams arrays
GeoKeyEntry(int tag, int count, int offset, Object array)
{
this.tag = tag;
this.count = count;
this.offset = offset;
this.array = array;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy