protected void importImagery() {
      try {
        // Read the data and save it in a temp file.
        File sourceFile = ExampleUtil.saveResourceToTempFile(IMAGE_PATH, ".tif");

        // Create a raster reader to read this type of file. The reader is created from the
        // currently
        // configured factory. The factory class is specified in the Configuration, and a different
        // one can be
        // specified there.
        DataRasterReaderFactory readerFactory =
            (DataRasterReaderFactory)
                WorldWind.createConfigurationComponent(AVKey.DATA_RASTER_READER_FACTORY_CLASS_NAME);
        DataRasterReader reader = readerFactory.findReaderFor(sourceFile, null);

        // Before reading the raster, verify that the file contains imagery.
        AVList metadata = reader.readMetadata(sourceFile, null);
        if (metadata == null || !AVKey.IMAGE.equals(metadata.getStringValue(AVKey.PIXEL_FORMAT)))
          throw new Exception("Not an image file.");

        // Read the file into the raster. read() returns potentially several rasters if there are
        // multiple
        // files, but in this case there is only one so just use the first element of the returned
        // array.
        DataRaster[] rasters = reader.read(sourceFile, null);
        if (rasters == null || rasters.length == 0)
          throw new Exception("Can't read the image file.");

        DataRaster raster = rasters[0];

        // Determine the sector covered by the image. This information is in the GeoTIFF file or
        // auxiliary
        // files associated with the image file.
        final Sector sector = (Sector) raster.getValue(AVKey.SECTOR);
        if (sector == null) throw new Exception("No location specified with image.");

        // Request a sub-raster that contains the whole image. This step is necessary because only
        // sub-rasters
        // are reprojected (if necessary); primary rasters are not.
        int width = raster.getWidth();
        int height = raster.getHeight();

        // getSubRaster() returns a sub-raster of the size specified by width and height for the
        // area indicated
        // by a sector. The width, height and sector need not be the full width, height and sector
        // of the data,
        // but we use the full values of those here because we know the full size isn't huge. If it
        // were huge
        // it would be best to get only sub-regions as needed or install it as a tiled image layer
        // rather than
        // merely import it.
        DataRaster subRaster = raster.getSubRaster(width, height, sector, null);

        // Tne primary raster can be disposed now that we have a sub-raster. Disposal won't affect
        // the
        // sub-raster.
        raster.dispose();

        // Verify that the sub-raster can create a BufferedImage, then create one.
        if (!(subRaster instanceof BufferedImageRaster))
          throw new Exception("Cannot get BufferedImage.");
        BufferedImage image = ((BufferedImageRaster) subRaster).getBufferedImage();

        // The sub-raster can now be disposed. Disposal won't affect the BufferedImage.
        subRaster.dispose();

        // Create a SurfaceImage to display the image over the specified sector.
        final SurfaceImage si1 = new SurfaceImage(image, sector);

        // On the event-dispatch thread, add the imported data as an SurfaceImageLayer.
        SwingUtilities.invokeLater(
            new Runnable() {
              public void run() {
                // Add the SurfaceImage to a layer.
                SurfaceImageLayer layer = new SurfaceImageLayer();
                layer.setName("Imported Surface Image");
                layer.setPickEnabled(false);
                layer.addRenderable(si1);

                // Add the layer to the model and update the application's layer panel.
                insertBeforeCompass(AppFrame.this.getWwd(), layer);
                AppFrame.this.getLayerPanel().update(AppFrame.this.getWwd());

                // Set the view to look at the imported image.
                ExampleUtil.goTo(getWwd(), sector);
              }
            });
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
Beispiel #2
0
  public static DataRaster wrapAsGeoreferencedRaster(BufferedImage image, AVList params) {
    if (null == image) {
      String message = Logging.getMessage("nullValue.ImageIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (null == params) {
      String msg = Logging.getMessage("nullValue.AVListIsNull");
      Logging.logger().finest(msg);
      throw new IllegalArgumentException(msg);
    }

    if (params.hasKey(AVKey.WIDTH)) {
      int width = (Integer) params.getValue(AVKey.WIDTH);
      if (width != image.getWidth()) {
        String msg =
            Logging.getMessage("generic.InvalidWidth", "" + width + "!=" + image.getWidth());
        Logging.logger().finest(msg);
        throw new IllegalArgumentException(msg);
      }
    }

    if (params.hasKey(AVKey.HEIGHT)) {
      int height = (Integer) params.getValue(AVKey.HEIGHT);
      if (height != image.getHeight()) {
        String msg =
            Logging.getMessage("generic.InvalidHeight", "" + height + "!=" + image.getHeight());
        Logging.logger().finest(msg);
        throw new IllegalArgumentException(msg);
      }
    }

    if (!params.hasKey(AVKey.SECTOR)) {
      String msg = Logging.getMessage("generic.MissingRequiredParameter", AVKey.SECTOR);
      Logging.logger().finest(msg);
      throw new IllegalArgumentException(msg);
    }

    Sector sector = (Sector) params.getValue(AVKey.SECTOR);
    if (null == sector) {
      String msg = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (!params.hasKey(AVKey.COORDINATE_SYSTEM)) {
      // assume Geodetic Coordinate System
      params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_GEOGRAPHIC);
    }

    String cs = params.getStringValue(AVKey.COORDINATE_SYSTEM);
    if (!params.hasKey(AVKey.PROJECTION_EPSG_CODE)) {
      if (AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) {
        // assume WGS84
        params.setValue(AVKey.PROJECTION_EPSG_CODE, GeoTiff.GCS.WGS_84);
      } else {
        String msg =
            Logging.getMessage("generic.MissingRequiredParameter", 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 (!params.hasKey(AVKey.PIXEL_WIDTH)) {
      if (AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) {
        double pixelWidth = sector.getDeltaLonDegrees() / (double) image.getWidth();
        params.setValue(AVKey.PIXEL_WIDTH, pixelWidth);
      } else {
        String msg = Logging.getMessage("generic.MissingRequiredParameter", 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 (!params.hasKey(AVKey.PIXEL_HEIGHT)) {
      if (AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) {
        double pixelHeight = sector.getDeltaLatDegrees() / (double) image.getHeight();
        params.setValue(AVKey.PIXEL_HEIGHT, pixelHeight);
      } else {
        String msg = Logging.getMessage("generic.MissingRequiredParameter", AVKey.PIXEL_HEIGHT);
        Logging.logger().finest(msg);
        throw new IllegalArgumentException(msg);
      }
    }

    if (!params.hasKey(AVKey.PIXEL_FORMAT)) {
      params.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE);
    } else if (!AVKey.IMAGE.equals(params.getStringValue(AVKey.PIXEL_FORMAT))) {
      String msg =
          Logging.getMessage(
              "generic.UnknownValueForKey",
              params.getStringValue(AVKey.PIXEL_FORMAT),
              AVKey.PIXEL_FORMAT);
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (!params.hasKey(AVKey.ORIGIN) && AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) {
      // set UpperLeft corner as the origin, if not specified
      LatLon origin = new LatLon(sector.getMaxLatitude(), sector.getMinLongitude());
      params.setValue(AVKey.ORIGIN, origin);
    }

    if (!params.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());
      params.setValue(AVKey.DATE_TIME, timestamp);
    }

    if (!params.hasKey(AVKey.VERSION)) {
      params.setValue(AVKey.VERSION, Version.getVersion());
    }

    boolean hasAlpha = (null != image.getColorModel() && image.getColorModel().hasAlpha());
    params.setValue(AVKey.RASTER_HAS_ALPHA, hasAlpha);

    return new BufferedImageRaster(sector, image, params);
  }
  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());
    }
  }
 private static boolean isImage(AVList params) {
   return (null != params
       && params.hasKey(AVKey.PIXEL_FORMAT)
       && AVKey.IMAGE.equals(params.getValue(AVKey.PIXEL_FORMAT)));
 }
  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);
  }