All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.formats.tiff.GeotiffReader 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.Disposable;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.data.*;
import gov.nasa.worldwind.formats.worldfile.WorldFile;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.util.*;

import java.awt.*;
import java.awt.color.*;
import java.awt.image.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;

/**
 * @author brownrigg
 * @version $Id: GeotiffReader.java 3289 2015-06-30 15:55:33Z tgaskins $
 */
public class GeotiffReader implements Disposable
{
    private TIFFReader tiffReader = null;

    private String sourceFilename;
    private RandomAccessFile sourceFile;
    private FileChannel theChannel;

    private GeoCodec gc = new GeoCodec();

    private ArrayList tiffIFDs = null;
    private ArrayList metadata = null;

    public GeotiffReader(String sourceFilename) throws IOException
    {
        this.sourceFilename = sourceFilename;
        this.sourceFile = new RandomAccessFile(sourceFilename, "r");
        this.theChannel = this.sourceFile.getChannel();

        this.tiffReader = new TIFFReader(this.theChannel);

        readTiffHeaders();
    }

    public GeotiffReader(File sourceFile) throws IOException
    {
        this(sourceFile.getAbsolutePath());
    }

    protected AVList getMetadata(int imageIndex) throws IOException
    {
        this.checkImageIndex(imageIndex);
        AVList values = this.metadata.get(imageIndex);
        return (null != values) ? values.copy() : null;
    }

    public AVList copyMetadataTo(int imageIndex, AVList values) throws IOException
    {
        AVList list = this.getMetadata(imageIndex);
        if (null != values)
        {
            values.setValues(list);
        }
        else
        {
            values = list;
        }
        return values;
    }

    public AVList copyMetadataTo(AVList list) throws IOException
    {
        return this.copyMetadataTo(0, list);
    }

//    public AVList getMetadata() throws IOException
//    {
//        return this.getMetadata(0);
//    }

    public void close()
    {
        try
        {
            this.sourceFile.close();
        }
        catch (Exception ex)
        { /* best effort */ }
    }

    public int getNumImages() throws IOException
    {
        return (this.tiffIFDs != null) ? this.tiffIFDs.size() : 0;
    }

    public int getWidth(int imageIndex) throws IOException
    {
        checkImageIndex(imageIndex);
        AVList values = this.metadata.get(imageIndex);
        return (values.hasKey(AVKey.WIDTH)) ? (Integer) values.getValue(AVKey.WIDTH) : 0;
    }

    public int getHeight(int imageIndex) throws IOException
    {
        checkImageIndex(imageIndex);
        AVList values = this.metadata.get(imageIndex);
        return (values.hasKey(AVKey.HEIGHT)) ? (Integer) values.getValue(AVKey.HEIGHT) : 0;
    }

    public DataRaster[] readDataRaster() throws IOException
    {
        int num = this.getNumImages();

        if (num <= 0)
        {
            return null;
        }

        DataRaster[] rasters = new DataRaster[num];
        for (int i = 0; i < num; i++)
        {
            rasters[i] = this.doRead(i);
        }
        return rasters;
    }

    public DataRaster readDataRaster(int imageIndex) throws IOException
    {
        checkImageIndex(imageIndex);
        return this.doRead(imageIndex);
    }

    public BufferedImage read() throws IOException
    {
        return this.read(0);
    }

    public BufferedImage read(int imageIndex) throws IOException
    {
        DataRaster raster = this.doRead(imageIndex);

        if (null == raster)
        {
            return null;
        }

        if (raster instanceof BufferedImageRaster)
        {
            return ((BufferedImageRaster) raster).getBufferedImage();
        }

        String message = Logging.getMessage("Geotiff.IsNotAnImage");
        Logging.logger().severe(message);
        throw new IOException(message);
    }

    public boolean isGeotiff(int imageIndex) throws IOException
    {
        AVList values = this.metadata.get(imageIndex);
        return (null != values && values.hasKey(AVKey.COORDINATE_SYSTEM));
    }

    public DataRaster doRead(int imageIndex) throws IOException
    {
        checkImageIndex(imageIndex);
        AVList values = this.metadata.get(imageIndex);

        // Extract the various IFD tags we need to read this image. We want to loop over the tag set once, instead
        // multiple times if we simply used our general getByTag() method.

        long[] stripOffsets = null;
        byte[][] cmap = null;
        long[] stripCounts = null;

        boolean tiffDifferencing = false;

        TiffIFDEntry[] ifd = this.tiffIFDs.get(imageIndex);

        BaselineTiff tiff = BaselineTiff.extract(ifd, this.tiffReader);

        if (null == tiff)
        {
            String message = Logging.getMessage("GeotiffReader.BadGeotiff");
            Logging.logger().severe(message);
            throw new IOException(message);
        }

        if (tiff.width <= 0)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.width,
                "width", Tiff.Tag.IMAGE_WIDTH);
            Logging.logger().severe(msg);
            throw new IOException(msg);
        }

        if (tiff.height <= 0)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.height,
                "height", Tiff.Tag.IMAGE_LENGTH);
            Logging.logger().severe(msg);
            throw new IOException(msg);
        }

        if (tiff.samplesPerPixel <= Tiff.Undefined)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.samplesPerPixel,
                "samplesPerPixel", Tiff.Tag.SAMPLES_PER_PIXEL);
            Logging.logger().severe(msg);
            throw new IOException(msg);
        }

        if (tiff.photometric <= Tiff.Photometric.Undefined || tiff.photometric > Tiff.Photometric.YCbCr)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.photometric,
                "PhotoInterpretation", Tiff.Tag.PHOTO_INTERPRETATION);
            Logging.logger().severe(msg);
            throw new IOException(msg);
        }

        if (tiff.rowsPerStrip <= Tiff.Undefined)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.rowsPerStrip,
                "RowsPerStrip", Tiff.Tag.ROWS_PER_STRIP);
            Logging.logger().fine(msg);
            tiff.rowsPerStrip = Integer.MAX_VALUE;
        }

        if (tiff.planarConfig != Tiff.PlanarConfiguration.PLANAR
            && tiff.planarConfig != Tiff.PlanarConfiguration.CHUNKY)
        {
            String msg = Logging.getMessage("GeotiffReader.InvalidIFDEntryValue", tiff.planarConfig,
                "PhotoInterpretation", Tiff.Tag.PHOTO_INTERPRETATION);
            Logging.logger().severe(msg);
            throw new IOException(msg);
        }

        for (TiffIFDEntry entry : ifd)
        {
            try
            {
                switch (entry.tag)
                {
                    case Tiff.Tag.STRIP_OFFSETS:
                        stripOffsets = entry.getAsLongs();
                        break;

                    case Tiff.Tag.STRIP_BYTE_COUNTS:
                        stripCounts = entry.getAsLongs();
                        break;

                    case Tiff.Tag.COLORMAP:
                        cmap = this.tiffReader.readColorMap(entry);
                        break;
                }
            }
            catch (IOException e)
            {
                Logging.logger().finest(e.toString());
            }
        }

        if (null == stripOffsets || 0 == stripOffsets.length)
        {
            String message = Logging.getMessage("GeotiffReader.MissingRequiredTag", "StripOffsets");
            Logging.logger().severe(message);
            throw new IOException(message);
        }

        if (null == stripCounts || 0 == stripCounts.length)
        {
            String message = Logging.getMessage("GeotiffReader.MissingRequiredTag", "StripCounts");
            Logging.logger().severe(message);
            throw new IOException(message);
        }

        TiffIFDEntry notToday = getByTag(ifd, Tiff.Tag.COMPRESSION);
        boolean lzwCompressed = false;
        if (notToday != null && notToday.asLong() == Tiff.Compression.LZW)
        {
            lzwCompressed = true;
            TiffIFDEntry predictorEntry = getByTag(ifd, Tiff.Tag.TIFF_PREDICTOR);
            if ((predictorEntry != null) && (predictorEntry.asLong() != 0))
            {
                tiffDifferencing = true;
            }
        }
        else if (notToday != null && notToday.asLong() != Tiff.Compression.NONE)
        {
            String message = Logging.getMessage("GeotiffReader.CompressionFormatNotSupported");
            Logging.logger().severe(message);
            throw new IOException(message);
        }

        notToday = getByTag(ifd, Tiff.Tag.TILE_WIDTH);
        if (notToday != null)
        {
            String message = Logging.getMessage("GeotiffReader.NoTiled");
            Logging.logger().severe(message);
            throw new IOException(message);
        }

        long offset = stripOffsets[0];
//        int sampleFormat = (null != tiff.sampleFormat) ? tiff.sampleFormat[0] : Tiff.Undefined;
//        int bitsPerSample = (null != tiff.bitsPerSample) ? tiff.bitsPerSample[0] : Tiff.Undefined;

        if (values.getValue(AVKey.PIXEL_FORMAT) == AVKey.ELEVATION)
        {
            ByteBufferRaster raster = new ByteBufferRaster(tiff.width, tiff.height,
                (Sector) values.getValue(AVKey.SECTOR), values);

            if (raster.getValue(AVKey.DATA_TYPE) == AVKey.INT8)
            {
                byte[][] data = this.tiffReader.readPlanar8(tiff.width, tiff.height, tiff.samplesPerPixel,
                    stripOffsets, stripCounts, tiff.rowsPerStrip);

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        raster.setDoubleAtPosition(y, x, (double) data[0][next++]);
                    }
                }
            }
            else if (raster.getValue(AVKey.DATA_TYPE) == AVKey.INT16)
            {
                short[][] data = this.tiffReader.readPlanar16(tiff.width, tiff.height, tiff.samplesPerPixel,
                    stripOffsets, stripCounts, tiff.rowsPerStrip);

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        raster.setDoubleAtPosition(y, x, (double) data[0][next++] );
                    }
                }
            }
            else if (raster.getValue(AVKey.DATA_TYPE) == AVKey.FLOAT32)
            {
                float[][] data = this.tiffReader.readPlanarFloat32(tiff.width, tiff.height, tiff.samplesPerPixel,
                    stripOffsets, stripCounts, tiff.rowsPerStrip);

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        raster.setDoubleAtPosition(y, x, (double) data[0][next++]);
                    }
                }
            }
            else
            {
                String message = Logging.getMessage("Geotiff.UnsupportedDataTypeRaster", tiff.toString());
                Logging.logger().severe(message);
                throw new IOException(message);
            }

            ElevationsUtil.rectify( raster );

            return raster;
        }
        else if (values.getValue(AVKey.PIXEL_FORMAT) == AVKey.IMAGE
            && values.getValue(AVKey.IMAGE_COLOR_FORMAT) == AVKey.GRAYSCALE)
        {
            BufferedImage grayImage = null;

            if (values.getValue(AVKey.DATA_TYPE) == AVKey.INT8)
            {
                byte[][] image = this.tiffReader.readPlanar8(tiff.width, tiff.height, tiff.samplesPerPixel,
                    stripOffsets, stripCounts, tiff.rowsPerStrip);

                grayImage = new BufferedImage(tiff.width, tiff.height, BufferedImage.TYPE_BYTE_GRAY);
                WritableRaster wrRaster = grayImage.getRaster();

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        wrRaster.setSample(x, y, 0, 0xFF & (int) (image[0][next++]));
                    }
                }
            }
            else if (values.getValue(AVKey.DATA_TYPE) == AVKey.INT16 && tiff.samplesPerPixel == 1)
            {
                short[][] image = this.tiffReader.readPlanar16(tiff.width, tiff.height, tiff.samplesPerPixel,
                    stripOffsets, stripCounts, tiff.rowsPerStrip);

                grayImage = new BufferedImage(tiff.width, tiff.height, BufferedImage.TYPE_USHORT_GRAY);
                WritableRaster wrRaster = grayImage.getRaster();

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        wrRaster.setSample(x, y, 0, 0xFFFF & (int) (image[0][next++]));
                    }
                }
            }
            else if (values.getValue(AVKey.DATA_TYPE) == AVKey.INT16 && tiff.samplesPerPixel > 1)
            {
                short[] image = this.tiffReader.read16bitPixelInterleavedImage(imageIndex, tiff.width, tiff.height,
                    tiff.samplesPerPixel, stripOffsets, stripCounts, tiff.rowsPerStrip);

                grayImage = new BufferedImage(tiff.width, tiff.height, BufferedImage.TYPE_USHORT_GRAY);
                WritableRaster wrRaster = grayImage.getRaster();

                int next = 0;
                for (int y = 0; y < tiff.height; y++)
                {
                    for (int x = 0; x < tiff.width; x++)
                    {
                        wrRaster.setSample(x, y, 0, 0xFFFF & (int) (image[next++]));
                    }
                }
            }

            if (null == grayImage)
            {
                String message = Logging.getMessage("Geotiff.UnsupportedDataTypeRaster", tiff.toString());
                Logging.logger().severe(message);
                throw new IOException(message);
            }

            grayImage = ImageUtil.toCompatibleImage(grayImage);
            return BufferedImageRaster.wrap(grayImage, values);
        }
        else if (values.getValue(AVKey.PIXEL_FORMAT) == AVKey.IMAGE
            && values.getValue(AVKey.IMAGE_COLOR_FORMAT) == AVKey.COLOR)
        {

            ColorModel colorModel = null;
            WritableRaster raster;
            BufferedImage colorImage;

            // make sure a DataBufferByte is going to do the trick
            for (int bits : tiff.bitsPerSample)
            {
                if (bits != 8)
                {
                    String message = Logging.getMessage("GeotiffReader.Not8bit", bits);
                    Logging.logger().warning(message);
                    throw new IOException(message);
                }
            }

            if (tiff.photometric == Tiff.Photometric.Color_RGB)
            {
                int transparency = Transparency.OPAQUE;
                boolean hasAlpha = false;

                if (tiff.samplesPerPixel == Tiff.SamplesPerPixel.RGB)
                {
                    transparency = Transparency.OPAQUE;
                    hasAlpha = false;
                }
                else if (tiff.samplesPerPixel == Tiff.SamplesPerPixel.RGBA)
                {
                    transparency = Transparency.TRANSLUCENT;
                    hasAlpha = true;
                }
                colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), tiff.bitsPerSample,
                    hasAlpha, false, transparency, DataBuffer.TYPE_BYTE);
            }
            else if (tiff.photometric == Tiff.Photometric.Color_Palette)
            {
                colorModel = new IndexColorModel(tiff.bitsPerSample[0], cmap[0].length, cmap[0], cmap[1], cmap[2]);
            }
            else if (tiff.photometric == Tiff.Photometric.CMYK)
            {
//                colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_), tiff.bitsPerSample,
//                    false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
            }

            int[] bankOffsets = new int[tiff.samplesPerPixel];
            for (int i = 0; i < tiff.samplesPerPixel; i++)
            {
                bankOffsets[i] = i;
            }
            int[] offsets = new int[(tiff.planarConfig == Tiff.PlanarConfiguration.CHUNKY) ? 1 : tiff.samplesPerPixel];
            for (int i = 0; i < offsets.length; i++)
            {
                offsets[i] = 0;
            }

            // construct the right SampleModel...
            ComponentSampleModel sampleModel;

            if (tiff.samplesPerPixel == Tiff.SamplesPerPixel.MONOCHROME)
            {
                sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, tiff.width, tiff.height, 1, tiff.width,
                    bankOffsets);
            }
            else
            {
                sampleModel = (tiff.planarConfig == Tiff.PlanarConfiguration.CHUNKY) ?
                    new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, tiff.width, tiff.height, tiff.samplesPerPixel,
                        tiff.width * tiff.samplesPerPixel, bankOffsets) :
                    new BandedSampleModel(DataBuffer.TYPE_BYTE, tiff.width, tiff.height, tiff.width, bankOffsets,
                        offsets);
            }

            // Get the image data and make our Raster...
            byte[][] imageData;
            if (tiff.planarConfig == Tiff.PlanarConfiguration.CHUNKY)
            {
                if (lzwCompressed && (tiff.samplesPerPixel > 2))
                {
                    imageData = new byte[1][tiff.width * tiff.height * tiff.samplesPerPixel];

                    imageData[0] = this.tiffReader.readLZWCompressed(tiff.width, tiff.height, offset,
                        tiff.samplesPerPixel, tiffDifferencing, stripOffsets, stripCounts);
                }
                else
                {
                    imageData = this.tiffReader.readPixelInterleaved8(tiff.width, tiff.height, tiff.samplesPerPixel,
                        stripOffsets, stripCounts);
                }
            }
            else
            {
                imageData = this.tiffReader.readPlanar8(tiff.width, tiff.height, tiff.samplesPerPixel, stripOffsets,
                    stripCounts, tiff.rowsPerStrip);
            }

            DataBufferByte dataBuff = new DataBufferByte(imageData, tiff.width * tiff.height, offsets);
            raster = Raster.createWritableRaster(sampleModel, dataBuff, new Point(0, 0));

            colorImage = new BufferedImage(colorModel, raster, false, null);

            if (null == colorImage)
            {
                String message = Logging.getMessage("Geotiff.UnsupportedDataTypeRaster", tiff.toString());
                Logging.logger().severe(message);
                throw new IOException(message);
            }

            colorImage = ImageUtil.toCompatibleImage(colorImage);
            return BufferedImageRaster.wrap(colorImage, values);
        }

        String message = Logging.getMessage("Geotiff.UnsupportedDataTypeRaster", tiff.toString());
        Logging.logger().severe(message);
        throw new IOException(message);
    }

    /**
     * Returns true if georeferencing information was found in this file.
     * 

* Note: see getGeoKeys() for determining if projection information is contained in the file. * * @throws java.io.IOException if data type is not supported or unknown */ private void repackageGeoReferencingTags() throws IOException { for (int i = 0; i < this.getNumImages(); i++) { TiffIFDEntry[] ifd = tiffIFDs.get(i); AVList values = this.metadata.get(i); values.setValue(AVKey.FILE_NAME, this.sourceFilename); // after we read all data, we have evrything as BIG_ENDIAN values.setValue(AVKey.BYTE_ORDER, AVKey.BIG_ENDIAN); BaselineTiff tiff = BaselineTiff.extract(ifd, this.tiffReader); if (null == tiff) { String message = Logging.getMessage("GeotiffReader.BadGeotiff"); Logging.logger().severe(message); throw new IOException(message); } if (tiff.width == Tiff.Undefined) { String message = Logging.getMessage("generic.InvalidWidth", tiff.width); Logging.logger().severe(message); throw new IOException(message); } values.setValue(AVKey.WIDTH, tiff.width); if (tiff.height == Tiff.Undefined) { String message = Logging.getMessage("generic.InvalidHeight", tiff.height); Logging.logger().severe(message); throw new IOException(message); } values.setValue(AVKey.HEIGHT, tiff.height); int sampleFormat = (null != tiff.sampleFormat) ? tiff.sampleFormat[0] : Tiff.Undefined; int bitsPerSample = (null != tiff.bitsPerSample) ? tiff.bitsPerSample[0] : Tiff.Undefined; if (null != tiff.displayName) { values.setValue(AVKey.DISPLAY_NAME, tiff.displayName); } if (null != tiff.imageDescription) { values.setValue(AVKey.DESCRIPTION, tiff.imageDescription); } if (null != tiff.softwareVersion) { values.setValue(AVKey.VERSION, tiff.softwareVersion); } if (null != tiff.dateTime) { values.setValue(AVKey.DATE_TIME, tiff.dateTime); } if (tiff.photometric == Tiff.Photometric.Color_RGB) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE); values.setValue(AVKey.IMAGE_COLOR_FORMAT, AVKey.COLOR); values.setValue(AVKey.DATA_TYPE, AVKey.INT8); } else if (tiff.photometric == Tiff.Photometric.CMYK) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE); values.setValue(AVKey.IMAGE_COLOR_FORMAT, AVKey.COLOR); values.setValue(AVKey.DATA_TYPE, AVKey.INT8); } else if (tiff.photometric == Tiff.Photometric.Color_Palette) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE); values.setValue(AVKey.IMAGE_COLOR_FORMAT, AVKey.COLOR); values.setValue(AVKey.DATA_TYPE, AVKey.INT8); } else if (tiff.samplesPerPixel == Tiff.SamplesPerPixel.MONOCHROME) { // Tiff.Photometric.Grayscale_BlackIsZero or Tiff.Photometric.Grayscale_WhiteIsZero if (sampleFormat == Tiff.SampleFormat.SIGNED) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION); if (bitsPerSample == Short.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT16); } else if (bitsPerSample == Byte.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT8); } else if (bitsPerSample == Integer.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT32); } } else if (sampleFormat == Tiff.SampleFormat.IEEEFLOAT) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION); if (bitsPerSample == Float.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.FLOAT32); } } else if (sampleFormat == Tiff.SampleFormat.UNSIGNED) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE); values.setValue(AVKey.IMAGE_COLOR_FORMAT, AVKey.GRAYSCALE); if (bitsPerSample == Short.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT16); } else if (bitsPerSample == Byte.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT8); } else if (bitsPerSample == Integer.SIZE) { values.setValue(AVKey.DATA_TYPE, AVKey.INT32); } } } if (!values.hasKey(AVKey.PIXEL_FORMAT) || !values.hasKey(AVKey.DATA_TYPE)) { String message = Logging.getMessage("Geotiff.UnsupportedDataTypeRaster", tiff.toString()); Logging.logger().severe(message); // throw new IOException(message); } // geo keys for (TiffIFDEntry entry : ifd) { try { switch (entry.tag) { case GeoTiff.Tag.GDAL_NODATA: Double d = Double.parseDouble(this.tiffReader.readString(entry)); values.setValue(AVKey.MISSING_DATA_SIGNAL, d); break; case Tiff.Tag.MIN_SAMPLE_VALUE: values.setValue(AVKey.ELEVATION_MIN, entry.getAsDouble()); break; case Tiff.Tag.MAX_SAMPLE_VALUE: values.setValue(AVKey.ELEVATION_MAX, entry.getAsDouble()); break; case GeoTiff.Tag.MODEL_PIXELSCALE: this.gc.setModelPixelScale(entry.getDoubles()); break; case GeoTiff.Tag.MODEL_TIEPOINT: this.gc.addModelTiePoints(entry.getDoubles()); break; case GeoTiff.Tag.MODEL_TRANSFORMATION: this.gc.setModelTransformation(entry.getDoubles()); break; case GeoTiff.Tag.GEO_KEY_DIRECTORY: this.gc.setGeokeys(entry.getShorts()); break; case GeoTiff.Tag.GEO_DOUBLE_PARAMS: this.gc.setDoubleParams(entry.getDoubles()); break; case GeoTiff.Tag.GEO_ASCII_PARAMS: this.gc.setAsciiParams(this.tiffReader.readBytes(entry)); break; } } catch (Exception e) { Logging.logger().finest(e.toString()); } } this.processGeoKeys(i); } } /* * Coordinates reading all the ImageFileDirectories in a Tiff file (there's typically only one). * */ private void readTiffHeaders() throws IOException { if (this.tiffIFDs != null) { return; } if (this.theChannel == null) { String message = Logging.getMessage("GeotiffReader.NullInputFile", this.sourceFilename); Logging.logger().severe(message); throw new IOException(message); } // Tiff image-file header (IFH) byte[] array = new byte[8]; ByteBuffer ifh = ByteBuffer.wrap(array).order(ByteOrder.LITTLE_ENDIAN); // determine byte ordering... this.theChannel.read(ifh); byte b0 = array[0]; byte b1 = array[1]; ByteOrder byteOrder = (b0 == 0x4D && b1 == 0x4D) ? ByteOrder.BIG_ENDIAN : ((b0 == 0x49 && b1 == 0x49) ? ByteOrder.LITTLE_ENDIAN : null); if (null == byteOrder) { String message = Logging.getMessage("GeotiffReader.BadTiffSig"); Logging.logger().severe(message); throw new IOException(message); } this.tiffReader.setByteOrder(byteOrder); // skip the magic number and get offset to first (and likely only) ImageFileDirectory... ifh = ByteBuffer.wrap(array).order(byteOrder); ifh.position(4); long ifdOffset = TIFFReader.getUnsignedInt(ifh); // position the channel to the ImageFileDirectory... this.theChannel.position(ifdOffset); ifh.clear().limit(2); this.theChannel.read(ifh); ifh.flip(); readIFD(ifh.getShort()); // decode any geotiff tags and structures that may be present into a manager object... this.repackageGeoReferencingTags(); } private void processGeoKeys(int imageIndex) throws IOException { this.checkImageIndex(imageIndex); AVList values = this.metadata.get(imageIndex); if (null == values || null == this.gc || !this.gc.hasGeoKey(GeoTiff.GeoKey.ModelType) || !values.hasKey(AVKey.WIDTH) || !values.hasKey(AVKey.HEIGHT) ) { return; } int width = (Integer) values.getValue(AVKey.WIDTH); int height = (Integer) values.getValue(AVKey.HEIGHT); // geo-tiff spec requires the VerticalCSType to be present for elevations (but ignores its value) if (this.gc.hasGeoKey(GeoTiff.GeoKey.VerticalCSType)) { values.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION); } if (this.gc.hasGeoKey(GeoTiff.GeoKey.VerticalUnits)) { int[] v = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.VerticalUnits); int units = (null != v && v.length > 0) ? v[0] : GeoTiff.Undefined; if (units == GeoTiff.Unit.Linear.Meter) { values.setValue(AVKey.ELEVATION_UNIT, AVKey.UNIT_METER); } else if (units == GeoTiff.Unit.Linear.Foot) { values.setValue(AVKey.ELEVATION_UNIT, AVKey.UNIT_FOOT); } } if (this.gc.hasGeoKey(GeoTiff.GeoKey.RasterType)) { int[] v = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.RasterType); int rasterType = (null != v && v.length > 0) ? v[0] : GeoTiff.Undefined; if (rasterType == GeoTiff.RasterType.RasterPixelIsArea) { values.setValue(AVKey.RASTER_PIXEL, AVKey.RASTER_PIXEL_IS_AREA); } else if (rasterType == GeoTiff.RasterType.RasterPixelIsPoint) { values.setValue(AVKey.RASTER_PIXEL, AVKey.RASTER_PIXEL_IS_POINT); } } if (this.gc.hasGeoKey(GeoTiff.GeoKey.GeogAngularUnits)) { // int[] v = this.gc.getGeoKeyAsInts( GeoTiff.GeoKey.GeogAngularUnits ); // int unit = ( null != v && v.length > 0 ) ? v[0] : GeoTiff.Undefined; // // if( unit == GeoTiff.Unit.Angular.Angular_Degree ) // else if( unit == GeoTiff.Unit.Angular.Angular_Radian ) } // AVKey.PROJECTION_DATUM Optional, // AVKey.PROJECTION_DESC Optional, // AVKey.PROJECTION_NAME Optional, // AVKey.PROJECTION_UNITS Optional, int gtModelTypeGeoKey = GeoTiff.ModelType.Undefined; if (this.gc.hasGeoKey(GeoTiff.GeoKey.ModelType)) { int[] gkValues = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.ModelType); if (null != gkValues && gkValues.length > 0) { gtModelTypeGeoKey = gkValues[0]; } } if (gtModelTypeGeoKey == GeoTiff.ModelType.Geographic) { values.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_GEOGRAPHIC); int epsg = GeoTiff.GCS.Undefined; if (this.gc.hasGeoKey(GeoTiff.GeoKey.GeographicType)) { int[] gkValues = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.GeographicType); if (null != gkValues && gkValues.length > 0) { epsg = gkValues[0]; } } if (epsg != GeoTiff.GCS.Undefined) { values.setValue(AVKey.PROJECTION_EPSG_CODE, epsg); } // TODO Assumes WGS84(4326)- should we check for this ? double[] bbox = this.gc.getBoundingBox(width, height); values.setValue(AVKey.SECTOR, Sector.fromDegrees(bbox[3], bbox[1], bbox[0], bbox[2])); values.setValue(AVKey.ORIGIN, LatLon.fromDegrees(bbox[1], bbox[0])); } else if (gtModelTypeGeoKey == GeoTiff.ModelType.Projected) { values.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_PROJECTED); int projection = GeoTiff.PCS.Undefined; String hemi; int zone; int[] vals = null; if (this.gc.hasGeoKey(GeoTiff.GeoKey.Projection)) { vals = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.Projection); } else if (this.gc.hasGeoKey(GeoTiff.GeoKey.ProjectedCSType)) { vals = this.gc.getGeoKeyAsInts(GeoTiff.GeoKey.ProjectedCSType); } if (null != vals && vals.length > 0) { projection = vals[0]; } if (projection != GeoTiff.PCS.Undefined) { values.setValue(AVKey.PROJECTION_EPSG_CODE, projection); } // TODO read more GeoKeys and GeoKeyDirectoryTag values /* from http://www.remotesensing.org/geotiff/spec/geotiff6.html#6.3.3.2 UTM (North) Format: 160zz UTM (South) Format: 161zz */ if ((projection >= 16100) && (projection <= 16199)) //UTM Zone South { hemi = AVKey.SOUTH; zone = projection - 16100; } else if ((projection >= 16000) && (projection <= 16099)) //UTM Zone North { hemi = AVKey.NORTH; zone = projection - 16000; } else if ((projection >= 26900) && (projection <= 26999)) //UTM : NAD83 { hemi = AVKey.NORTH; zone = projection - 26900; } else if ((projection >= 32201) && (projection <= 32260)) //UTM : WGS72 N { hemi = AVKey.NORTH; zone = projection - 32200; } else if ((projection >= 32301) && (projection <= 32360)) //UTM : WGS72 S { hemi = AVKey.SOUTH; zone = projection - 32300; } else if ((projection >= 32401) && (projection <= 32460)) //UTM : WGS72BE N { hemi = AVKey.NORTH; zone = projection - 32400; } else if ((projection >= 32501) && (projection <= 32560)) //UTM : WGS72BE S { hemi = AVKey.SOUTH; zone = projection - 32500; } else if ((projection >= 32601) && (projection <= 32660)) //UTM : WGS84 N { hemi = AVKey.NORTH; zone = projection - 32600; } else if ((projection >= 32701) && (projection <= 32760)) //UTM : WGS84 S { hemi = AVKey.SOUTH; zone = projection - 32700; } else { String message = Logging.getMessage("generic.UnknownProjection", projection); Logging.logger().severe(message); // throw new IOException(message); return; } double pixelScaleX = this.gc.getModelPixelScaleX(); double pixelScaleY = Math.abs(this.gc.getModelPixelScaleY()); //dump "world file" values into values values.setValue(AVKey.PROJECTION_HEMISPHERE, hemi); values.setValue(AVKey.PROJECTION_ZONE, zone); values.setValue(WorldFile.WORLD_FILE_X_PIXEL_SIZE, pixelScaleX); values.setValue(WorldFile.WORLD_FILE_Y_PIXEL_SIZE, -pixelScaleY); //shift to center GeoCodec.ModelTiePoint[] tps = this.gc.getTiePoints(); if (null != tps && tps.length > imageIndex) { GeoCodec.ModelTiePoint tp = tps[imageIndex]; double xD = tp.getX() + (pixelScaleX / 2d); double yD = tp.getY() - (pixelScaleY / 2d); values.setValue(WorldFile.WORLD_FILE_X_LOCATION, xD); values.setValue(WorldFile.WORLD_FILE_Y_LOCATION, yD); } values.setValue(AVKey.SECTOR, ImageUtil.calcBoundingBoxForUTM(values)); } else { String msg = Logging.getMessage("Geotiff.UnknownGeoKeyValue", gtModelTypeGeoKey, GeoTiff.GeoKey.ModelType); Logging.logger().severe(msg); // throw new IOException(msg); } } /* * Reads an ImageFileDirectory and places it in our list. It is assumed the caller has * prepositioned the file to the first entry (i.e., just past the short designating the * number of entries). * * Calls itself recursively if additional IFDs are indicated. * */ private void readIFD(int numEntries) throws IOException { try { if (null == this.tiffIFDs) { this.tiffIFDs = new ArrayList(); } java.util.List ifd = new ArrayList(); for (int i = 0; i < numEntries; i++) { ifd.add(TIFFIFDFactory.create(this.theChannel, this.tiffReader.getByteOrder())); } TiffIFDEntry[] array = ifd.toArray(new TiffIFDEntry[ifd.size()]); this.tiffIFDs.add(array); if (null == this.metadata) { this.metadata = new ArrayList(); } this.metadata.add(new AVListImpl()); ByteBuffer bb = ByteBuffer.allocate(4).order(this.tiffReader.getByteOrder()); this.theChannel.read(bb); bb.flip(); // If there's another IFD in this file, go get it (recursively)... long nextIFDOffset = TIFFReader.getUnsignedInt(bb); if (nextIFDOffset > 0) { this.theChannel.position(nextIFDOffset); bb.clear().limit(2); this.theChannel.read(bb); bb.flip(); readIFD(bb.getShort()); } } catch (Exception ex) { String message = Logging.getMessage("GeotiffReader.BadIFD", ex.getMessage()); Logging.logger().severe(message); throw new IOException(message); } } /* * Returns the (first!) IFD-Entry with the given tag, or null if not found. * */ private TiffIFDEntry getByTag(TiffIFDEntry[] ifd, int tag) { for (TiffIFDEntry anIfd : ifd) { if (anIfd.tag == tag) { return anIfd; } } return null; } /* * We need to check for a valid image index in several places. Consolidate that all here. * We throw an IllegalArgumentException if the index is not valid, otherwise, silently return. * */ private void checkImageIndex(int imageIndex) throws IOException { if (imageIndex < 0 || imageIndex >= getNumImages()) { String message = Logging.getMessage("GeotiffReader.BadImageIndex", imageIndex, 0, getNumImages()); Logging.logger().severe(message); throw new IllegalArgumentException(message); } } /* * Make sure we release this resource... * */ public void dispose() { try { WWIO.closeStream(this.theChannel, this.sourceFilename); WWIO.closeStream(this.sourceFile, this.sourceFilename); } catch (Throwable t) { String message = t.getMessage(); message = (WWUtil.isEmpty(message)) ? t.getCause().getMessage() : message; Logging.logger().log(java.util.logging.Level.FINEST, message, t); } } protected void finalize() throws Throwable { this.dispose(); super.finalize(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy