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; } }