package org.geotiff.image.jai;
import org.geotiff.image.KeyRegistry;
import org.libtiff.jai.codec.XTIFFDirectory;
import org.libtiff.jai.codec.XTIFFField;
import org.libtiff.jai.codec.XTIFF;
import java.util.TreeMap;
import java.util.Iterator;
// Warning: media libs subject to change
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 iter = geoKeyIndex.values().iterator();
double[]doubles = new double[numberOfKeys];
String strings = "";
int indx=4;
char numDoubles=0;
char tag=0;
char valueOrOffset=0;
while (iter.hasNext()) {
XTIFFField geoKey= (XTIFFField)iter.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 iter = geoKeyIndex.values().iterator();
int i = 0;
while (iter.hasNext()) {
keys[i++] = (XTIFFField)iter.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;
}
}