protected double[] getMinMax(BufferWrapper elevations, double missingDataSignal) {
   double min = Double.MAX_VALUE;
   double max = -Double.MAX_VALUE;
   for (int i = 0; i < elevations.length(); i++) {
     double value = elevations.getDouble(i);
     if (value != missingDataSignal) {
       min = Math.min(min, elevations.getDouble(i));
       max = Math.max(max, elevations.getDouble(i));
     }
   }
   if (min < minElevationClamp) min = minElevationClamp;
   if (max > maxElevationClamp) max = maxElevationClamp;
   /*if (min == Double.MAX_VALUE || max == -Double.MAX_VALUE)
   {
   	Logging.logger().warning("No elevations found in tile");
   }*/
   return new double[] {min, max};
 }
  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);
  }