private void writeGeographicImageGeoKeys(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException { long offset = this.theChannel.position(); if (isImage(params) && isGeographic(params)) { int epsg = GeoTiff.GCS.WGS_84; if (params.hasKey(AVKey.PROJECTION_EPSG_CODE)) epsg = (Integer) params.getValue(AVKey.PROJECTION_EPSG_CODE); short[] values = new short[] { // GeoKeyDirectory header GeoTiff.GeoKeyHeader.KeyDirectoryVersion, GeoTiff.GeoKeyHeader.KeyRevision, GeoTiff.GeoKeyHeader.MinorRevision, 0, // IMPORTANT!! we will update count below, after the array initialization // end of header - // geo keys array /* key 1 */ GeoTiff.GeoKey.ModelType, 0, 1, GeoTiff.ModelType.Geographic, /* key 2 */ GeoTiff.GeoKey.RasterType, 0, 1, (short) (0xFFFF & GeoTiff.RasterType.RasterPixelIsArea), /* key 3 */ GeoTiff.GeoKey.GeographicType, 0, 1, (short) (0xFFFF & epsg), /* key 4 */ GeoTiff.GeoKey.GeogAngularUnits, 0, 1, GeoTiff.Unit.Angular.Angular_Degree }; // IMPORTANT!! update count - number of geokeys values[3] = (short) (values.length / 4); byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(GeoTiff.Tag.GEO_KEY_DIRECTORY, Tiff.Type.SHORT, values.length, offset)); } }
private void writeGeographicElevationGeoKeys(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException { long offset = this.theChannel.position(); if (isElevation(params) && isGeographic(params)) { int epsg = GeoTiff.GCS.WGS_84; if (params.hasKey(AVKey.PROJECTION_EPSG_CODE)) epsg = (Integer) params.getValue(AVKey.PROJECTION_EPSG_CODE); int elevUnits = GeoTiff.Unit.Linear.Meter; if (params.hasKey(AVKey.ELEVATION_UNIT)) { if (AVKey.UNIT_FOOT.equals(params.getValue(AVKey.ELEVATION_UNIT))) elevUnits = GeoTiff.Unit.Linear.Foot; } int rasterType = GeoTiff.RasterType.RasterPixelIsArea; if (params.hasKey(AVKey.RASTER_PIXEL) && AVKey.RASTER_PIXEL_IS_POINT.equals(params.getValue(AVKey.RASTER_PIXEL))) rasterType = GeoTiff.RasterType.RasterPixelIsPoint; short[] values = new short[] { // GeoKeyDirectory header GeoTiff.GeoKeyHeader.KeyDirectoryVersion, GeoTiff.GeoKeyHeader.KeyRevision, GeoTiff.GeoKeyHeader.MinorRevision, 0, // IMPORTANT!! we will update count below, after the array initialization // end of header - // geo keys array /* key 1 */ GeoTiff.GeoKey.ModelType, 0, 1, GeoTiff.ModelType.Geographic, /* key 2 */ // TODO: Replace GeoTiff.RasterType.RasterPixelIsPoint GeoTiff.GeoKey.RasterType, 0, 1, (short) (0xFFFF & rasterType), /* key 3 */ GeoTiff.GeoKey.GeographicType, 0, 1, (short) (0xFFFF & epsg), /* key 4 */ GeoTiff.GeoKey.GeogAngularUnits, 0, 1, GeoTiff.Unit.Angular.Angular_Degree, /* key 5 */ GeoTiff.GeoKey.VerticalCSType, 0, 1, GeoTiff.VCS.WGS_84_ellipsoid, /* key 6 */ GeoTiff.GeoKey.VerticalUnits, 0, 1, (short) (0xFFFF & elevUnits), }; // IMPORTANT!! update count - number of geokeys values[3] = (short) (values.length / 4); byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(GeoTiff.Tag.GEO_KEY_DIRECTORY, Tiff.Type.SHORT, values.length, offset)); } }
private void appendGeoTiff(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException, IllegalArgumentException { if (null == params || 0 == params.getEntries().size()) { String reason = Logging.getMessage("nullValue.AVListIsNull"); Logging.logger().finest(Logging.getMessage("GeotiffWriter.GeoKeysMissing", reason)); return; } long offset = this.theChannel.position(); if (params.hasKey(AVKey.DISPLAY_NAME)) { String value = params.getStringValue(AVKey.DISPLAY_NAME); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(Tiff.Tag.DOCUMENT_NAME, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.DESCRIPTION)) { String value = params.getStringValue(AVKey.DESCRIPTION); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(Tiff.Tag.IMAGE_DESCRIPTION, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.VERSION)) { String value = params.getStringValue(AVKey.VERSION); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(Tiff.Tag.SOFTWARE_VERSION, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.DATE_TIME)) { String value = params.getStringValue(AVKey.DATE_TIME); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(Tiff.Tag.DATE_TIME, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.SECTOR)) { if (params.hasKey(AVKey.PIXEL_WIDTH) && params.hasKey(AVKey.PIXEL_HEIGHT)) { offset = this.theChannel.position(); double[] values = new double[] { (Double) params.getValue(AVKey.PIXEL_WIDTH), (Double) params.getValue(AVKey.PIXEL_HEIGHT), isElevation(params) ? 1d : 0d }; byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry( GeoTiff.Tag.MODEL_PIXELSCALE, Tiff.Type.DOUBLE, values.length, offset)); } if (params.hasKey(AVKey.WIDTH) && params.hasKey(AVKey.HEIGHT)) { offset = this.theChannel.position(); double w = (Integer) params.getValue(AVKey.WIDTH); double h = (Integer) params.getValue(AVKey.HEIGHT); Sector sec = (Sector) params.getValue(AVKey.SECTOR); double[] values = new double[] { // i , j, k=0, x, y, z=0 0d, 0d, 0d, sec.getMinLongitude().degrees, sec.getMaxLatitude().degrees, 0d, w - 1, 0d, 0d, sec.getMaxLongitude().degrees, sec.getMaxLatitude().degrees, 0d, w - 1, h - 1, 0d, sec.getMaxLongitude().degrees, sec.getMinLatitude().degrees, 0d, 0d, h - 1, 0d, sec.getMinLongitude().degrees, sec.getMinLatitude().degrees, 0d, }; byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(GeoTiff.Tag.MODEL_TIEPOINT, Tiff.Type.DOUBLE, values.length, offset)); } // Tiff.Tag.MODEL_TRANSFORMATION excludes Tiff.Tag.MODEL_TIEPOINT & Tiff.Tag.MODEL_PIXELSCALE if (params.hasKey(AVKey.MISSING_DATA_SIGNAL) || params.hasKey(AVKey.MISSING_DATA_REPLACEMENT)) { offset = this.theChannel.position(); Object nodata = params.hasKey(AVKey.MISSING_DATA_SIGNAL) ? params.getValue(AVKey.MISSING_DATA_SIGNAL) : params.getValue(AVKey.MISSING_DATA_REPLACEMENT); String value = "" + nodata + "\0"; byte[] bytes = value.getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(GeoTiff.Tag.GDAL_NODATA, Tiff.Type.ASCII, bytes.length, offset)); } if (params.hasKey(AVKey.COORDINATE_SYSTEM)) { String cs = params.getStringValue(AVKey.COORDINATE_SYSTEM); if (AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) { if (isElevation(params)) this.writeGeographicElevationGeoKeys(ifds, params); else this.writeGeographicImageGeoKeys(ifds, params); } else if (AVKey.COORDINATE_SYSTEM_PROJECTED.equals(cs)) { String msg = Logging.getMessage("GeotiffWriter.FeatureNotImplementedd", cs); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); // TODO extract PCS (Projection Coordinate System) } else { String msg = Logging.getMessage("GeotiffWriter.UnknownCoordinateSystem", cs); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } } }
private void writeGrayscaleImage(BufferedImage image, AVList params) throws IOException { int type = image.getType(); int bitsPerSample = (BufferedImage.TYPE_USHORT_GRAY == type) ? Tiff.BitsPerSample.MONOCHROME_UINT16 : Tiff.BitsPerSample.MONOCHROME_UINT8; int numBands = image.getSampleModel().getNumBands(); // well, numBands for GrayScale images must be 1 int bytesPerSample = numBands * bitsPerSample / Byte.SIZE; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * bytesPerSample); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * bytesPerSample; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); if (BufferedImage.TYPE_USHORT_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedShort(dataBuff, rowData[j]); } } else if (BufferedImage.TYPE_BYTE_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedByte(dataBuff, rowData[j]); } } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, 1, bitsPerSample)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Grayscale_BlackIsZero)); ifds.add( new TiffIFDEntry(Tiff.Tag.SAMPLE_FORMAT, Tiff.Type.SHORT, 1, Tiff.SampleFormat.UNSIGNED)); long offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the // ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
private void writeColorImage(BufferedImage image, AVList params) throws IOException { int numBands = image.getRaster().getNumBands(); long offset; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * numBands); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * numBands; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); for (int j = 0; j < numCols * numBands; j++) { putUnsignedByte(dataBuff, rowData[j]); } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add( new TiffIFDEntry( Tiff.Tag.PLANAR_CONFIGURATION, Tiff.Type.SHORT, 1, Tiff.PlanarConfiguration.CHUNKY)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Color_RGB)); ifds.add(new TiffIFDEntry(Tiff.Tag.ORIENTATION, Tiff.Type.SHORT, 1, Tiff.Orientation.DEFAULT)); offset = this.theChannel.position(); short[] bps = new short[numBands]; for (int i = 0; i < numBands; i++) { bps[i] = Tiff.BitsPerSample.MONOCHROME_BYTE; } this.theChannel.write(ByteBuffer.wrap(this.getBytes(bps))); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, numBands, offset)); offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
public void writeRaster(BufferWrapperRaster raster) throws IOException, IllegalArgumentException { if (raster == null) { String msg = Logging.getMessage("nullValue.RasterIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (0 == raster.getWidth() || 0 == raster.getHeight()) { String msg = Logging.getMessage("generic.InvalidImageSize", raster.getWidth(), raster.getHeight()); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.validateParameters(raster, raster.getWidth(), raster.getHeight()); int bitsPerSample, samplesPerPixel, sampleFormat, photometric, numBands; if (AVKey.ELEVATION.equals(raster.getValue(AVKey.PIXEL_FORMAT))) { if (AVKey.FLOAT32.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.IEEEFLOAT; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.ELEVATIONS_FLOAT32; } else if (AVKey.INT16.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.SIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.ELEVATIONS_INT16; } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.DATA_TYPE)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else if (AVKey.IMAGE.equals(raster.getValue(AVKey.PIXEL_FORMAT))) { if (AVKey.INT8.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.MONOCHROME_UINT8; } else if (AVKey.INT16.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.MONOCHROME_UINT16; } else if (AVKey.INT32.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 3; // TODO check ALPHA / Transparency samplesPerPixel = Tiff.SamplesPerPixel.RGB; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Color_RGB; bitsPerSample = Tiff.BitsPerSample.RGB; } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.DATA_TYPE)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.PIXEL_FORMAT)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } int bytesPerSample = numBands * bitsPerSample / Byte.SIZE; this.writeTiffHeader(); // write the image data... int numRows = raster.getHeight(); int numCols = raster.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; BufferWrapper srcBuffer = raster.getBuffer(); ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * bytesPerSample); switch (bitsPerSample) { // case Tiff.BitsPerSample.MONOCHROME_BYTE: case Tiff.BitsPerSample.MONOCHROME_UINT8: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.put(srcBuffer.getByte(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; // case Tiff.BitsPerSample.MONOCHROME_UINT16: case Tiff.BitsPerSample.ELEVATIONS_INT16: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.putShort(srcBuffer.getShort(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; case Tiff.BitsPerSample.ELEVATIONS_FLOAT32: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.putFloat(srcBuffer.getFloat(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; case Tiff.BitsPerSample.RGB: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols; x++) { int color = srcBuffer.getInt(x + y * numCols); byte red = (byte) (0xFF & (color >> 16)); byte green = (byte) (0xFF & (color >> 8)); byte blue = (byte) (0xFF & color); // dataBuff.put(0xFF & (color >> 24)); // alpha dataBuff.put(red).put(green).put(blue); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); long offset = this.theChannel.position(); if (Tiff.BitsPerSample.RGB == bitsPerSample) { short[] bps = new short[numBands]; for (int i = 0; i < numBands; i++) { bps[i] = Tiff.BitsPerSample.MONOCHROME_BYTE; } this.theChannel.write(ByteBuffer.wrap(this.getBytes(bps))); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, numBands, offset)); } else ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, 1, bitsPerSample)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add(new TiffIFDEntry(Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, photometric)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, samplesPerPixel)); ifds.add(new TiffIFDEntry(Tiff.Tag.ORIENTATION, Tiff.Type.SHORT, 1, Tiff.Orientation.DEFAULT)); ifds.add( new TiffIFDEntry( Tiff.Tag.PLANAR_CONFIGURATION, Tiff.Type.SHORT, 1, Tiff.PlanarConfiguration.CHUNKY)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLE_FORMAT, Tiff.Type.SHORT, 1, sampleFormat)); offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the // ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, raster); this.writeIFDs(ifds); }