/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2016, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.metadata.geotiff;
import com.sun.media.imageio.plugins.tiff.GeoTIFFTagSet;
import java.util.logging.Level;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Logger;
import javax.imageio.metadata.IIOMetadata;
import org.opengis.metadata.spatial.CellGeometry;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.util.FactoryException;
import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
import org.apache.sis.referencing.operation.transform.LinearTransform;
import org.apache.sis.referencing.operation.matrix.Matrix3;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
import org.apache.sis.util.Classes;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.gui.swing.tree.Trees;
import org.geotoolkit.image.io.metadata.SpatialMetadata;
import org.geotoolkit.image.io.metadata.SpatialMetadataFormat;
import org.geotoolkit.internal.image.io.GridDomainAccessor;
import org.geotoolkit.internal.jdk8.JDK8;
import org.geotoolkit.metadata.iso.spatial.PixelTranslation;
import org.geotoolkit.referencing.operation.MathTransforms;
import org.w3c.dom.Node;
import static com.sun.media.imageio.plugins.tiff.GeoTIFFTagSet.*;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.crs.DefaultImageCRS;
import org.apache.sis.referencing.operation.matrix.Matrix2;
import org.geotoolkit.image.io.metadata.ReferencingBuilder;
import static org.geotoolkit.metadata.geotiff.GeoTiffConstants.*;
import static org.geotoolkit.metadata.geotiff.GeoTiffMetaDataUtils.*;
import org.geotoolkit.referencing.crs.PredefinedCRS;
import static org.geotoolkit.util.DomUtilities.*;
/**
* Utility class to read geotiff metadata tags.
* http://www.remotesensing.org/geotiff/faq.html
*
* @author Johann Sorel (Geomatys)
* @author Remi Marechal (Geomatys)
*/
public final class GeoTiffMetaDataReader {
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.metadata.geotiff");
public static class ValueMap extends HashMap<Integer, Object> {
/**
* @param key
* @return A string representing the value, or null if the key was not
* found or failed to parse.
*/
public String getAsString(final int key) {
final Object value = get(key);
if (value instanceof String) return (String) value;
if (value instanceof Number) return ((Number)value).toString();
return null;
}
public double getAsDouble(final int key) {
final Object value = get(key);
if (value == null) return Double.NaN;
if (value instanceof Number) return ((Number)value).doubleValue();
try {
final String geoKey = value.toString();
return Double.parseDouble(geoKey);
} catch (NumberFormatException ne) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, ne.getLocalizedMessage(), ne);
}
return Double.NaN;
} catch (Exception e) {
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
return Double.NaN;
}
}
}
private final IIOMetadata imageMetadata;
private final Node root;
private final Node imgFileDir;
private final Node geoKeyDir;
public GeoTiffMetaDataReader(final IIOMetadata imageMetadata) throws IOException{
this.imageMetadata = imageMetadata;
root = imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName());
if (root == null) throw new IOException("No image metadatas");
imgFileDir = getNodeByLocalName(root,TAG_GEOTIFF_IFD);
if (imgFileDir == null) throw new IOException("No GeoTiff metadatatas");
geoKeyDir = getNodeByNumber(imgFileDir,TAG_GEO_KEY_DIRECTORY);
}
/**
* Read the Spatial Metadatas.
*
* @return SpatialMetadata
* @throws NoSuchAuthorityCodeException
* @throws FactoryException
* @throws java.io.IOException
*/
public SpatialMetadata readSpatialMetaData() throws NoSuchAuthorityCodeException, FactoryException, IOException {
final SpatialMetadata spatialMetadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(SpatialMetadataFormat.GEOTK_FORMAT_NAME));
if (geoKeyDir != null) {
final int[] structure = readTiffShorts(getNodeByLocalName(geoKeyDir, TAG_GEOTIFF_SHORTS));
//first line (4 int) contain the version and number of keys
//Header={KeyDirectoryVersion, KeyRevision, MinorRevision, NumberOfKeys}
final int directoryVersion = structure[0];
final int keyVersion = structure[1];
final int minorVersion = structure[2];
final int nbKeys = structure[3];
//read all entries
final ValueMap entries = new ValueMap();
for(int i=0,l=4; i<nbKeys; i++,l+=4){
final Object value;
final int valueKey = structure[l+0];
final int valuelocation = structure[l+1];
final int valueNb = structure[l+2];
final int valueOffset = structure[l+3];
if (valuelocation == 0) {
//value is located in the offset field
value = valueOffset;
} else {
//value is in another tag
value = readValue(valuelocation, valueOffset, valueNb);
}
entries.put(valueKey, value);
}
//create the spatial metadatas.
fillGridMetaDatas(spatialMetadata, entries);
final GeoTiffCRSReader crsReader = new GeoTiffCRSReader();
crsReader.fillCRSMetaDatas(spatialMetadata, entries);
} else {
new ReferencingBuilder(spatialMetadata).setCoordinateReferenceSystem(PredefinedCRS.GRID_2D);
GridDomainAccessor gridDomainAccessor = new GridDomainAccessor(spatialMetadata);
gridDomainAccessor.setAll(AffineTransforms2D.castOrCopy(new Matrix3()), readBounds(),
CellGeometry.AREA, PixelOrientation.UPPER_LEFT);
spatialMetadata.clearInstancesCache();
}
//-- looks for additional informations
final ThirdPartyMetaDataReader thirdReader = new ThirdPartyMetaDataReader(imageMetadata);
thirdReader.fillSpatialMetaData(spatialMetadata);
return spatialMetadata;
}
/**
* Fill the spatial metadatas with the values available in the geotiff tags.
*/
private void fillGridMetaDatas(final SpatialMetadata metadatas, final ValueMap entries) throws IOException{
final GridDomainAccessor accesor = new GridDomainAccessor(metadatas);
/*
* FAQ GEOTIFF :
* Setting the GTRasterTypeGeoKey value to RasterPixelIsPoint or RasterPixelIsArea
* alters how the raster coordinate space is to be interpreted.
* This is defined in section 2.5.2.2 of the GeoTIFF specification.
* => In the case of PixelIsArea (default) a pixel is treated as an area
* and the raster coordinate (0,0) is the top left corner of the top left pixel.
* => PixelIsPoint treats pixels as point samples with empty space between the "pixel" samples.
* In this case raster (0,0) is the location of the top left raster pixel.
*
* Note : GeoTiff mix the concepts of CellGeometry and PixelOrientation.
*/
//get the raster type
final Object value = entries.get(GTRasterTypeGeoKey);
PixelOrientation orientation;
if (value != null) {
int type = (Integer)value;
//-- faire un log
if (type < 1 || type > 2) {
final String strLog = "Undefine raster Type from geotiff metadatas : \n"
+ "From GeoKeyDirectoryTag (34735) the internaly key GTRasterTypeGeoKey (1025) should be : \n"
+ "- 1 for RasterPixelIsArea, or \n"
+ "- 2 for RasterPixelIsPoint.\n"
+ "Bad founded raster Type value is : "+type;
LOGGER.log(Level.SEVERE, strLog);
}
orientation = (type == RasterPixelIsPoint) ? PixelOrientation.CENTER : PixelOrientation.UPPER_LEFT;
} else {
orientation = PixelOrientation.UPPER_LEFT;
}
final CellGeometry cellGeometry = (orientation == PixelOrientation.UPPER_LEFT)
? CellGeometry.AREA : CellGeometry.POINT;
//-- read the image bounds
final Rectangle bounds = readBounds();
//-- check if a transformation is present /////////////////////////////////
AffineTransform gridToCRS = readTransformation();
if (gridToCRS == null) {
//-- check for pixel scale and tie points /////////////////////////////////
final double[] pixelScale = readPixelScale();
final double[] tiePoint = readTiePoint();
if (pixelScale == null && tiePoint != null) {
final int l = tiePoint.length;
assert l % 6 == 0 : "In tiff specification tiePoint array length should be congrue 6.";
//-- pixelOrientation Offset
final int pIOffset = (orientation.equals(PixelOrientation.CENTER)) ? 1 : 0;
final double[] lowerLeftCorner = new double[]{Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 0, 0, 0}; //-- xmin ymin
final double[] lowerRightCorner = new double[]{Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 0, 0, 0}; //-- xmax ymin
final double[] upperLeftCorner = new double[]{Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0, 0, 0, 0}; //-- xmax ymax
final double[] upperRightCorner = new double[]{Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0, 0, 0, 0}; //-- xmax ymax
//-- first loop to find the two minimum grid poins in X axis to define XStep
//-- and also find the two minimum grid poins in Y axis to define YStep
//-- in other to know if grid is regulary
for (int i = 0; i < l; i += 6) {
final double currentX = tiePoint[i];
final double currentY = tiePoint[i+1];
if (currentX <= lowerLeftCorner[0]
&& currentY <= lowerLeftCorner[1]) {
System.arraycopy(tiePoint, i, lowerLeftCorner, 0, 6);
}
if (currentX >= lowerRightCorner[0]
&& currentY <= lowerRightCorner[1]) {
System.arraycopy(tiePoint, i, lowerRightCorner, 0, 6);
}
if (currentX >= upperRightCorner[0]
&& currentY >= upperRightCorner[1]) {
System.arraycopy(tiePoint, i, upperRightCorner, 0, 6);
}
if (currentX <= upperLeftCorner[0]
&& currentY >= upperLeftCorner[1]) {
System.arraycopy(tiePoint, i, upperLeftCorner, 0, 6);
}
}
//-- the fourth corner points found
assert JDK8.isFinite(lowerLeftCorner[0]) : "lowerLeftCorner grid point not found";
assert JDK8.isFinite(lowerRightCorner[0]) : "lowerRightCorner grid point not found";
assert JDK8.isFinite(upperLeftCorner[0]) : "upperLeftCorner grid point not found";
assert JDK8.isFinite(upperRightCorner[0]) : "upperRightCorner grid point not found";
////////////////////////////////////////////////////////////////////////////////
//// Commentary code to support NON LINEAR GRIDTOCRS into coverage metadata ////
//// TODO : add non linear MathTransform into setUserObject from ////
//// metadatas for Coverage gridtocrs and use it during pyramid build ////
////////////////////////////////////////////////////////////////////////////////
// double[] preLowerCorner = null;
// double[] preUpperCorner = null;
//
// //-- round value because expected value at 0 and 1 array index are integer raster coordinates.
// int stepX = (int) StrictMath.round(upperRightCorner[0] - lowerLeftCorner[0]);
// int stepY = (int) StrictMath.round(upperRightCorner[1] - lowerLeftCorner[1]);
//
// //-- if more than 5 points
// if (l > 30) {
// preLowerCorner = new double[]{Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 0, 0, 0};
// preUpperCorner = new double[]{Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0, 0, 0, 0};
//
// //-- I didn't find algorithm to avoid this second loop
// for (int i = 0; i < l; i += 6) {
// final double currentX = tiePoint[i];
// final double currentY = tiePoint[i+1];
// if (currentX > lowerLeftCorner[0]
// && currentX <= preLowerCorner[0]
// && currentY > lowerLeftCorner[1]
// && currentY <= preLowerCorner[1]) {
// System.arraycopy(tiePoint, i, preLowerCorner, 0, 6);
// }
// if (currentX < upperRightCorner[0]
// && currentX >= preUpperCorner[0]
// && currentY < upperRightCorner[1]
// && currentY >= preUpperCorner[1]) {
// System.arraycopy(tiePoint, i, preUpperCorner, 0, 6);
// }
// }
// assert JDK8.isFinite(preLowerCorner[0]) : "preLowerCorner grid point not found";
// assert JDK8.isFinite(preUpperCorner[0]) : "preUpperCorner grid point not found";
//
// stepX = (int) StrictMath.round(preLowerCorner[0] - lowerLeftCorner[0]);
// stepY = (int) StrictMath.round(preLowerCorner[1] - lowerLeftCorner[1]);
//
// }
//
// assert stepX >= 1;// >= 1
// assert stepY >= 1;// >= 1
//
// double[] gridLowerCorner = lowerLeftCorner;
// double[] gridUpperCorner = null;
//
// //-- often last higher point on X or Y direction is not congrue to regular points step.
// //-- try to generate regular grid without last higher border points.
// if ((int) StrictMath.round(upperRightCorner[0] - lowerLeftCorner[0]) % stepX == 0
// && (int) StrictMath.round(upperRightCorner[1] - lowerLeftCorner[1]) % stepY == 0) {
// gridUpperCorner = upperRightCorner;
// } else {
// gridUpperCorner = preUpperCorner;
// }
//
// assert gridUpperCorner != null : "gridUpperCorner should not be null.";
//
// final int gridWidth = (int) StrictMath.round(gridUpperCorner[0] - gridLowerCorner[0]);
// final int gridHeight = (int) StrictMath.round(gridUpperCorner[1] - gridLowerCorner[1]);
//
// BitSet bit = null;
// LocalizationGrid grid = null;
// int expectedBitCardinality = -1;
//
// if (gridWidth % stepX == 0
// && gridHeight % stepY == 0) {
// final int nbPointX = gridWidth / stepX + 1;//-- [p0 -- step --> p1 -- step --> p2]
// final int nbPointY = gridHeight / stepY + 1;
// expectedBitCardinality = nbPointX * nbPointY;
//
// grid = new LocalizationGrid(nbPointX, nbPointY);
// bit = new BitSet(expectedBitCardinality);
//
// for (int i = 0; i < l; i += 6) {
// final double currentX = tiePoint[i];
// final double currentY = tiePoint[i+1];
//
// //-- case where we ignore higher border points
// //-- avoid higher border points
// if (currentX > gridUpperCorner[0]
// || currentY > gridUpperCorner[1]) {
// continue;
// }
//
// final int px = (int) StrictMath.round(currentX - gridLowerCorner[0]);
// final int py = (int) StrictMath.round(currentY - gridLowerCorner[1]);
//
// assert StrictMath.abs(px - tiePoint[i]) < 1E-9;
// assert StrictMath.abs(py - tiePoint[i+1]) < 1E-9;
//
// //-- current point coordinates are congrue step
// if (px % stepX != 0
// || py % stepY != 0) {
// break;
// }
// bit.set(py * nbPointX + px, true);
// grid.setLocalizationPoint(px / stepX, //-- src X
// py / stepY, //-- src Y
// tiePoint[i + 3], //-- dest X
// tiePoint[i + 4]); //-- dest Y
// }
// }
//
// //-- if all points represent regulary grid
// if (bit != null && bit.cardinality() == expectedBitCardinality) {
//
// //-- gridToCrs create from grid
// assert grid != null;
// gridToCRS = grid.getAffineTransform();
// gridToCRS.transform(new Point2D.Double(10, 4), null);
// gridToCRS.scale(1.0 / stepX, 1.0 / stepY);
// gridToCRS.translate(lowerLeftCorner[0] - pIOffset, lowerLeftCorner[1] - pIOffset);
//
// } else {
//-- create matrix transformation from first fourth corner points.
//-- note : src points always define as PixelInCell.CELL_CORNER
//-- for PixelInCell.CELL_CENTER a translation is effectuate later.
final Matrix3 srcPoints = new Matrix3(lowerLeftCorner[0] - pIOffset, lowerRightCorner[0] - pIOffset, upperRightCorner[0] - pIOffset,
lowerLeftCorner[1] - pIOffset, lowerRightCorner[1] - pIOffset, upperRightCorner[1] - pIOffset,
1, 1, 1);
final Matrix3 destPoints = new Matrix3(lowerLeftCorner[3], lowerRightCorner[3], upperRightCorner[3],
lowerLeftCorner[4], lowerRightCorner[4], upperRightCorner[4],
1, 1, 1);
MatrixSIS gtcrs;
try {
gtcrs = destPoints.multiply(srcPoints.inverse());
} catch (NoninvertibleMatrixException ex) {
throw new IOException("Grid To Crs creation impossible to inverse source points.", ex);
}
//-- last matrix row verification
//-- avoid double approximation error
double epsilon = 0;
for (int j = 0; j < 3; j++) {
final double elt = gtcrs.getElement(2, j);
epsilon += elt * elt;
}
epsilon = StrictMath.sqrt(epsilon); //-- compute row vector magnitude
epsilon *= 1E-15; //-- define tolerance from vector magnitude.
for (int j = 0; j < 3; j++) {
final double elt = gtcrs.getElement(2, j);
if (elt < epsilon) gtcrs.setElement(2, j, 0);
}
gridToCRS = AffineTransforms2D.castOrCopy(org.apache.sis.referencing.operation.transform.MathTransforms.linear(gtcrs).getMatrix());
// }
} else if (pixelScale != null && tiePoint != null) {
//TODO the is a third value in the tie point
final double scaleX = pixelScale[0];
final double scaleY = -pixelScale[1];
final double tiePointColumn = tiePoint[0];
final double tiePointRow = tiePoint[1];
final double translateX = tiePoint[3] - (scaleX * tiePointColumn);
final double translateY = tiePoint[4] - (scaleY * tiePointRow);
gridToCRS = new AffineTransform(scaleX, 0, 0, scaleY, translateX, translateY);
}
}
if (gridToCRS != null) {
// force orientation to PixelOrientation.UPPER_LEFT to keep consistent transformation
// when we add additional dimensions.
if (PixelOrientation.CENTER.equals(orientation)) {
LinearTransform linear = MathTransforms.linear(gridToCRS);
LinearTransform translate = (LinearTransform)PixelTranslation.translate(linear, PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER);
gridToCRS = AffineTransforms2D.castOrCopy(translate.getMatrix());
orientation = PixelOrientation.UPPER_LEFT;
}
accesor.setAll(gridToCRS, bounds, cellGeometry, orientation);
return;
}
//unknowned definition /////////////////////////////////////////////////
LOGGER.log(Level.FINE, "Unknowned Grid to CRS transformation definition from image internaly metadatas");
}
/**
* Returns the image size from the tags : {@link GeoTiffConstants#ImageWidth} and {@link GeoTiffConstants#ImageLength}.<br>
* May return {@code null}.
*
* @return a {@link Rectangle} if expected geotiff tags are filled, else return {@code null}.
*/
private Rectangle readBounds() {
//-- get metadata node about image boundary
final Node width = getNodeByNumber(imgFileDir, ImageWidth);
final Node height = getNodeByNumber(imgFileDir, ImageLength);
if (width == null || height == null) return null;
//-- study width
int rectWidth = -1;
//-- value can be stored in a short field
Node widthNode = getNodeByLocalName(width, TAG_GEOTIFF_SHORTS);
if(widthNode != null) rectWidth = readTiffShorts(widthNode)[0];
//-- can be in a long field
if (rectWidth == -1) {
if(widthNode == null) widthNode = getNodeByLocalName(width, TAG_GEOTIFF_LONGS);
if(widthNode != null) rectWidth = (int) readTiffLongs(widthNode)[0];
}
if (rectWidth == -1) {
LOGGER.info("Unable to find geographic image boundary. Image width.");
return null;
}
//-- study height
int rectHeight = -1;
//-- value can be stored in a short field
Node heightNode = getNodeByLocalName(height, TAG_GEOTIFF_SHORTS);
if(heightNode != null) rectHeight = readTiffShorts(heightNode)[0];
//-- can be in a long field
if (rectHeight == -1) {
if(heightNode == null) heightNode = getNodeByLocalName(height, TAG_GEOTIFF_LONGS);
if(heightNode != null) rectHeight = (int) readTiffLongs(heightNode)[0];
}
if (rectHeight == -1) {
LOGGER.info("Unable to find geographic image boundary. Image height.");
return null;
}
assert rectWidth >= 0 : "SpatialMetadata : Geographic image boudary.width should be positive.";
assert rectHeight >= 0 : "SpatialMetadata : Geographic image boudary.height should be positive.";
return new Rectangle(rectWidth, rectHeight);
}
/**
* Returns the transformation from the {@link GeoTIFFTagSet#TAG_MODEL_TRANSFORMATION} if it exist.<br>
* May return {@code null}.
*
* @return an {@link AffineTransform} if expected geotiff tags are filled, else return {@code null}.
* @throws IOException if defined transformation is unknow.
*/
private AffineTransform readTransformation() throws IOException {
final Node node = getNodeByNumber(imgFileDir, TAG_MODEL_TRANSFORMATION);
if (node == null) return null;
final Node valueNode = getNodeByLocalName(node, TAG_GEOTIFF_DOUBLES);
if(valueNode == null) return null;
//-- TODO find a multidimensional way
final double[] matrix = readTiffDoubles(valueNode);
if (matrix.length == 9) {
//2D matrix
return new AffineTransform(matrix[0],matrix[4],matrix[1],
matrix[5],matrix[6],matrix[7]);
} else if (matrix.length == 16) {
//3D matrix, only keep the 2d part
return new AffineTransform(matrix[0],matrix[4],matrix[1],
matrix[5],matrix[3],matrix[7]);
} else {
throw new IOException("Unvalid transformation definition. expected 9 or 16 parameters but was "+matrix.length);
}
}
/**
* Read the tie points from the TAG_MODEL_TIE_POINT if it exist.
*/
private double[] readTiePoint() {
final Node node = getNodeByNumber(imgFileDir, TAG_MODEL_TIE_POINT);
if (node == null) return null;
final Node valueNode = getNodeByLocalName(node, TAG_GEOTIFF_DOUBLES);
if (valueNode == null) return null;
return readTiffDoubles(valueNode);
}
/**
* Read the pixel scale from the TAG_MODEL_PIXEL_SCALE if it exist.
*/
private double[] readPixelScale() {
final Node node = getNodeByNumber(imgFileDir, TAG_MODEL_PIXEL_SCALE);
if (node == null) return null;
final Node valueNode = getNodeByLocalName(node, TAG_GEOTIFF_DOUBLES);
if(valueNode == null) return null;
return readTiffDoubles(valueNode);
}
/**
* Read values for the given tag number.
*
* @param tagNumber
* @param offset
* @param lenght
* @return
* @throws IOException
*/
private Object readValue(final int tagNumber, final int offset, final int lenght) throws IOException{
final Node node = getNodeByNumber(imgFileDir, tagNumber);
if (node == null)
throw new IOException("Incorrect metadata description, no tag with number "+tagNumber);
//node should have a single subNode containing the value
final Node valueNode = node.getChildNodes().item(0);
if (valueNode == null)
throw new IOException("Incorrect metadata description, no value in tag number "+tagNumber);
final String typeName = valueNode.getLocalName();
final Object value;
if (TAG_GEOTIFF_ASCII.equalsIgnoreCase(typeName)) {
if (lenght != 1)
throw new IOException("Incorrect metadata description, single value type "
+typeName+" used to retrieve more than one value");
value = readTiffAscii(valueNode);
} else if (TAG_GEOTIFF_ASCIIS.equalsIgnoreCase(typeName)) {
value = readTiffAsciis(valueNode).substring(offset, offset+lenght);
} else if (TAG_GEOTIFF_SHORT.equalsIgnoreCase(typeName)) {
if (lenght != 1)
throw new IOException("Incorrect metadata description, single value type "
+typeName+" used to retrieve more than one value");
value = readTiffShort(valueNode);
} else if (TAG_GEOTIFF_SHORTS.equalsIgnoreCase(typeName)) {
final int[] shorts = readTiffShorts(valueNode);
if (lenght == 1) {
value = shorts[offset];
} else {
value = new int[lenght];
System.arraycopy(shorts, offset, value, 0, lenght);
}
} else if (TAG_GEOTIFF_LONG.equalsIgnoreCase(typeName)) {
if (lenght != 1)
throw new IOException("Incorrect metadata description, single value type "
+typeName+" used to retrieve more than one value");
value = readTiffLong(valueNode);
} else if (TAG_GEOTIFF_LONGS.equalsIgnoreCase(typeName)) {
final long[] longs = readTiffLongs(valueNode);
if (lenght == 1) {
value = longs[offset];
} else {
value = new long[lenght];
System.arraycopy(longs, offset, value, 0, lenght);
}
} else if (TAG_GEOTIFF_DOUBLE.equalsIgnoreCase(typeName)) {
if(lenght != 1)
throw new IOException("Incorrect metadata description, single value type "
+typeName+" used to retrieve more than one value");
value = readTiffDouble(valueNode);
} else if (TAG_GEOTIFF_DOUBLES.equalsIgnoreCase(typeName)) {
final double[] doubles = readTiffDoubles(valueNode);
if (lenght == 1) {
value = doubles[offset];
} else {
value = new double[lenght];
System.arraycopy(doubles, offset, value, 0, lenght);
}
} else {
throw new IOException("Incorrect metadata description, unknowned value type "
+typeName+" for tag number "+ tagNumber);
}
return value;
}
/**
* @return String of tagsets on the image file directory node.
*/
public String readTagSets(){
return getAttributeValue(imgFileDir, ATT_TAGSETS);
}
@Override
public String toString() {
return Classes.getShortName(this.getClass()) +"\n"+Trees.toString(Trees.xmlToSwing(root));
}
}