package org.geotiff.image.jai;
import java.util.Iterator;
import java.util.TreeMap;
import org.libtiff.jai.codec.XTIFF;
import org.libtiff.jai.codec.XTIFFDirectory;
import org.libtiff.jai.codec.XTIFFField;
import com.sun.media.jai.codec.SeekableStream;
/**
* An extension of the XTIFFDirectory that understands the structure of the
* GeoTIFF key set
*
* @author Niles D. Ritter
*/
public class GeoTIFFDirectory extends XTIFFDirectory implements
java.io.Serializable {
private TreeMap geoKeyIndex = new TreeMap();
private int keyDirectoryVersion;
private int majorRevision;
private int minorRevision;
private int numberOfKeys;
private double[] tiepoints = null;
private double[] scales = null;
private double[] matrix = null;
private boolean hasGeoKeys = false;
/**
* public constructor (for serializability)
*/
public GeoTIFFDirectory() {}
/**
* Constructs a GeoTIFFDirectory by reading a SeekableStream. The ifd_offset
* parameter specifies the stream offset from which to begin reading; this
* mechanism is sometimes used to store private IFDs within a TIFF file that
* are not part of the normal sequence of IFDs.
*/
public GeoTIFFDirectory(SeekableStream stream, long ifd_offset)
throws java.io.IOException {
super(stream, ifd_offset);
readGeoKeys();
log("GeoTIFFDirectory constructor success.");
}
/**
* Constructs a GeoTIFFDirectory from a SeekableStream. The directory
* parameter specifies which directory to read from the linked list present
* in the stream; directory 0 is normally read but it is possible to store
* multiple images in a single TIFF file by maintaing multiple directories.
*/
public GeoTIFFDirectory(SeekableStream stream, int directory)
throws java.io.IOException {
super(stream, directory);
readGeoKeys();
log("GeoTIFFDirectory constructor success.");
}
private void log(String msg) {}
/**
* Generates the TIFF fields from the GeoKey list
*/
private void createGeoTags() {
if (!hasGeoKeys)
return;
char numberOfKeys = (char) geoKeyIndex.size();
char[] keys = new char[(numberOfKeys + 1) * 4];
// Write the 4-entry header
keys[0] = 1; // key version
keys[1] = 1; // majorRevision
keys[2] = 0; // minorRevision
keys[3] = numberOfKeys;
// Write the key directory out
Iterator it = geoKeyIndex.values().iterator();
double[] doubles = new double[numberOfKeys];
String strings = "";
int indx = 4;
char numDoubles = 0;
char tag = 0;
char valueOrOffset = 0;
while (it.hasNext()) {
XTIFFField geoKey = (XTIFFField) it.next();
switch (geoKey.getType()) {
case XTIFFField.TIFF_SHORT:
// short values are stored in the valueOrOffset
tag = 0;
valueOrOffset = (char) geoKey.getAsInt(0);
break;
case XTIFFField.TIFF_DOUBLE:
tag = (char) XTIFF.TIFFTAG_GEO_DOUBLE_PARAMS;
doubles[numDoubles] = geoKey.getAsDouble(0);
valueOrOffset = numDoubles++;
break;
case XTIFFField.TIFF_ASCII:
// strings are '|' pipe delimited
tag = (char) XTIFF.TIFFTAG_GEO_ASCII_PARAMS;
valueOrOffset = (char) strings.length();
strings = strings + geoKey.getAsString(0) + "|";
break;
} // switch
keys[indx++] = (char) geoKey.getTag();
keys[indx++] = tag;
keys[indx++] = (char) geoKey.getCount();
keys[indx++] = valueOrOffset;
} // while
// Add the Directory tag
addField(XTIFF.TIFFTAG_GEO_KEY_DIRECTORY,
XTIFFField.TIFF_SHORT,
keys.length,
keys);
// Add the Ascii tag if needed
if (strings.length() > 0) {
char zero = 0;
strings = strings + zero;
addField(XTIFF.TIFFTAG_GEO_ASCII_PARAMS,
XTIFFField.TIFF_ASCII,
strings.length(),
new String[] { strings });
}
// Add the double tag if needed
if (numDoubles > 0) {
double[] doubleVals = new double[numDoubles];
for (int i = 0; i < numDoubles; i++)
doubleVals = doubles;
addField(XTIFF.TIFFTAG_GEO_DOUBLE_PARAMS,
XTIFFField.TIFF_DOUBLE,
numDoubles,
doubleVals);
}
// set up the other values stored in tags
if (matrix != null)
addField(XTIFF.TIFFTAG_GEO_TRANS_MATRIX,
XTIFFField.TIFF_DOUBLE,
matrix.length,
matrix);
if (tiepoints != null)
addField(XTIFF.TIFFTAG_GEO_TIEPOINTS,
XTIFFField.TIFF_DOUBLE,
tiepoints.length,
tiepoints);
if (scales != null)
addField(XTIFF.TIFFTAG_GEO_PIXEL_SCALE,
XTIFFField.TIFF_DOUBLE,
scales.length,
scales);
}
/**
* stores a single geoKey in the index table, from the existing field
* information
*/
private void storeGeoKey(int keyID, int tiffTag, int valueCount,
int valueOrOffset) throws java.io.IOException {
int type = XTIFFField.TIFF_SHORT;
Object value = null;
if (tiffTag > 0) {
// Values are in another tag:
XTIFFField values = getField(tiffTag);
if (values != null) {
type = values.getType();
if (type == XTIFFField.TIFF_ASCII) {
String svalue = values.getAsString(0)
.substring(valueOrOffset,
valueOrOffset + valueCount - 1);
value = new String[] { svalue };
} else if (type == XTIFFField.TIFF_DOUBLE) {
// we shouldn't have valueCount != 1 here
double dvalue = values.getAsDouble(valueOrOffset);
value = new double[] { dvalue };
}
} else {
throw new java.io.IOException("GeoTIFF tag not found");
} // values tag found
} else {
// value is SHORT, stored in valueOrOffset
type = XTIFFField.TIFF_SHORT;
value = new char[] { (char) valueOrOffset };
} // tiffTag
addGeoKey(keyID, type, valueCount, value);
}
/**
* Returns an array of XTIFFFields containing all the fields in this
* directory. Prior to returning array, determine if there are any GeoKeys,
* and if so, set up the corresponding GeoTIFF fields.
*/
public XTIFFField[] getFields() {
if (hasGeoKeys)
createGeoTags();
return super.getFields();
}
/**
* populates the geoKeyIndex table from the values stored in the current
* TIFF fields.
*/
private void readGeoKeys() throws java.io.IOException {
// read in the keys
XTIFFField geoKeyTag = getField(XTIFF.TIFFTAG_GEO_KEY_DIRECTORY);
if (geoKeyTag != null) {
char[] keys = geoKeyTag.getAsChars();
// Set up header info
keyDirectoryVersion = keys[0]; // should be 1 forever.
majorRevision = keys[1]; // currently 1
minorRevision = keys[2]; // 0,1, or 2...
numberOfKeys = keys[3];
// Parse out keys and values
for (int i = 4; i < keys.length; i += 4) {
int keyID = keys[i];
int tiffTag = keys[i + 1];
int valueCount = keys[i + 2];
int valueOrOffset = keys[i + 3];
storeGeoKey(keyID, tiffTag, valueCount, valueOrOffset);
}
}
// set up the values stored in tags
// read in the data stored as real tags
XTIFFField matrixTag = getField(XTIFF.TIFFTAG_GEO_TRANS_MATRIX);
XTIFFField tiepointTag = getField(XTIFF.TIFFTAG_GEO_TIEPOINTS);
XTIFFField scaleTag = getField(XTIFF.TIFFTAG_GEO_PIXEL_SCALE);
if (tiepointTag != null) {
tiepoints = tiepointTag.getAsDoubles();
}
if (scaleTag != null) {
scales = scaleTag.getAsDoubles();
}
if (matrixTag != null) {
matrix = matrixTag.getAsDoubles();
}
} // readGeoKeys
/**
* Add a geoKey to the directory
*/
public void addGeoKey(int key, int type, int count, Object data) {
XTIFFField geoKey = createField(key, type, count, data);
addGeoKey(geoKey);
}
/**
* Add an existing geoKey to the directory.
*/
public void addGeoKey(XTIFFField geoKey) {
geoKeyIndex.put(new Integer(geoKey.getTag()), geoKey);
hasGeoKeys = true;
}
/**
* Returns an array of XTIFFFields containing all the fields corresponding
* to the GeoKeys.
*/
public XTIFFField[] getGeoKeys() {
XTIFFField[] keys = new XTIFFField[geoKeyIndex.size()];
Iterator it = geoKeyIndex.values().iterator();
int i = 0;
while (it.hasNext()) {
keys[i++] = (XTIFFField) it.next();
}
return keys;
}
/**
* Indexed Accessor to the Geokeys,indexed by the key values.
*/
public XTIFFField getGeoKey(int key) {
return (XTIFFField) geoKeyIndex.get(new Integer(key));
}
/**
* return the tiepoint tag values
*/
public double[] getTiepoints() {
return tiepoints;
}
/**
* return the pixel scale tag values
*/
public double[] getPixelScale() {
return scales;
}
/**
* return the transformation matrix tag values
*/
public double[] getTransformationMatrix() {
return matrix;
}
/**
* set the tiepoint tag values
*/
public void setTiepoints(double[] tiepoints) {
this.tiepoints = tiepoints;
}
/**
* return the pixel scale tag values
*/
public void setPixelScale(double[] scales) {
this.scales = scales;
}
/**
* return the pixel scale tag values
*/
public void setTransformationMatrix(double[] matrix) {
this.matrix = matrix;
}
public int getKeyDirectoryVersion() {
return keyDirectoryVersion;
}
public void setKeyDirectoryVersion(int keyDirectoryVersion) {
this.keyDirectoryVersion = keyDirectoryVersion;
}
public int getMajorRevision() {
return majorRevision;
}
public void setMajorRevision(int majorRevision) {
this.majorRevision = majorRevision;
}
public int getMinorRevision() {
return minorRevision;
}
public void setMinorRevision(int minorRevision) {
this.minorRevision = minorRevision;
}
public int getNumberOfKeys() {
return numberOfKeys;
}
public void setNumberOfKeys(int numberOfKeys) {
this.numberOfKeys = numberOfKeys;
}
}