protected void validateParameters(AVList list, int srcWidth, int srcHeight) throws IllegalArgumentException { if (null == list || 0 == list.getValues().size()) { String reason = Logging.getMessage("nullValue.AVListIsNull"); String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", reason); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } if (!(srcWidth > 0 && srcHeight > 0)) { String msg = Logging.getMessage("generic.InvalidImageSize", srcWidth, srcHeight); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } if (list.hasKey(AVKey.WIDTH)) { int width = (Integer) list.getValue(AVKey.WIDTH); if (width != srcWidth) { String msg = Logging.getMessage("GeotiffWriter.ImageWidthMismatch", width, srcWidth); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else list.setValue(AVKey.WIDTH, srcWidth); if (list.hasKey(AVKey.HEIGHT)) { int height = (Integer) list.getValue(AVKey.HEIGHT); if (height != srcHeight) { String msg = Logging.getMessage("GeotiffWriter.ImageHeightMismatch", height, srcHeight); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else list.setValue(AVKey.HEIGHT, srcHeight); Sector sector = null; if (list.hasKey(AVKey.SECTOR)) sector = (Sector) list.getValue(AVKey.SECTOR); if (null == sector) { String msg = Logging.getMessage("GeotiffWriter.NoSectorSpecified"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (!list.hasKey(AVKey.COORDINATE_SYSTEM)) { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.COORDINATE_SYSTEM); Logging.logger().finest(msg); // throw new IllegalArgumentException(msg); // assume Geodetic Coordinate System list.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_GEOGRAPHIC); } if (!list.hasKey(AVKey.PROJECTION_EPSG_CODE)) { if (isGeographic(list)) { // assume WGS84 list.setValue(AVKey.PROJECTION_EPSG_CODE, GeoTiff.GCS.WGS_84); } else { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.PROJECTION_EPSG_CODE); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } } // if PIXEL_WIDTH is specified, we are not overriding it because UTM images // will have different pixel size if (!list.hasKey(AVKey.PIXEL_WIDTH)) { if (isGeographic(list)) { double pixelWidth = sector.getDeltaLonDegrees() / (double) srcWidth; list.setValue(AVKey.PIXEL_WIDTH, pixelWidth); } else { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.PIXEL_WIDTH); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } } // if PIXEL_HEIGHT is specified, we are not overriding it // because UTM images will have different pixel size if (!list.hasKey(AVKey.PIXEL_HEIGHT)) { if (isGeographic(list)) { double pixelHeight = sector.getDeltaLatDegrees() / (double) srcHeight; list.setValue(AVKey.PIXEL_HEIGHT, pixelHeight); } else { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.PIXEL_HEIGHT); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } } if (!list.hasKey(AVKey.PIXEL_FORMAT)) { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.PIXEL_FORMAT); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } else { String pixelFormat = list.getStringValue(AVKey.PIXEL_FORMAT); if (!AVKey.ELEVATION.equals(pixelFormat) && !AVKey.IMAGE.equals(pixelFormat)) { String msg = Logging.getMessage("Geotiff.UnknownGeoKeyValue", pixelFormat, AVKey.PIXEL_FORMAT); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } // validate elevation parameters if (AVKey.ELEVATION.equals(list.getValue(AVKey.PIXEL_FORMAT))) { if (!list.hasKey(AVKey.DATA_TYPE)) { String msg = Logging.getMessage("GeotiffWriter.GeoKeysMissing", AVKey.DATA_TYPE); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } String type = list.getStringValue(AVKey.DATA_TYPE); if (!AVKey.FLOAT32.equals(type) && !AVKey.INT16.equals(type)) { String msg = Logging.getMessage("Geotiff.UnknownGeoKeyValue", type, AVKey.DATA_TYPE); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } if (!list.hasKey(AVKey.ORIGIN)) { // set UpperLeft corner as the origin, if not specified LatLon origin = new LatLon(sector.getMaxLatitude(), sector.getMinLongitude()); list.setValue(AVKey.ORIGIN, origin); } if (list.hasKey(AVKey.BYTE_ORDER) && !AVKey.BIG_ENDIAN.equals(list.getStringValue(AVKey.BYTE_ORDER))) { String msg = Logging.getMessage( "generic.UnrecognizedByteOrder", list.getStringValue(AVKey.BYTE_ORDER)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (!list.hasKey(AVKey.DATE_TIME)) { // add NUL (\0) termination as required by TIFF v6 spec (20 bytes length) String timestamp = String.format("%1$tY:%1$tm:%1$td %tT\0", Calendar.getInstance()); list.setValue(AVKey.DATE_TIME, timestamp); } if (!list.hasKey(AVKey.VERSION)) { list.setValue(AVKey.VERSION, Version.getVersion()); } }
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); }