/**
   * Create a collection of layer lists and their included layers described by an array of XML
   * layer-list description elements.
   *
   * <p>Any exceptions occurring during creation of the layer lists or their included layers are
   * logged and not re-thrown. The layers associated with the exceptions are not included in the
   * returned layer list.
   *
   * @param elements the XML elements describing the layer lists to create.
   * @param params any parameters to apply when creating the included layers.
   * @return an array containing the specified layer lists.
   */
  protected LayerList[] createLayerLists(Element[] elements, AVList params) {
    ArrayList<LayerList> layerLists = new ArrayList<LayerList>();

    for (Element element : elements) {
      try {
        String href = WWXML.getText(element, "@href");
        if (href != null && href.length() > 0) {
          Object o = this.createFromConfigSource(href, params);
          if (o == null) continue;

          if (o instanceof Layer) {
            LayerList ll = new LayerList();
            ll.add((Layer) o);
            o = ll;
          }

          if (o instanceof LayerList) {
            LayerList list = (LayerList) o;
            if (list != null && list.size() > 0) layerLists.add(list);
          } else if (o instanceof LayerList[]) {
            LayerList[] lists = (LayerList[]) o;
            if (lists != null && lists.length > 0) layerLists.addAll(Arrays.asList(lists));
          } else {
            String msg =
                Logging.getMessage("LayerFactory.UnexpectedTypeForLayer", o.getClass().getName());
            Logging.logger().log(java.util.logging.Level.WARNING, msg);
          }

          continue;
        }

        String title = WWXML.getText(element, "@title");
        Element[] children = WWXML.getElements(element, "./Layer", null);
        if (children != null && children.length > 0) {
          LayerList list = this.createLayerList(children, params);
          if (list != null && list.size() > 0) {
            layerLists.add(list);
            if (title != null && title.length() > 0) list.setValue(AVKey.DISPLAY_NAME, title);
          }
        }
      } catch (Exception e) {
        Logging.logger().log(java.util.logging.Level.WARNING, e.getMessage(), e);
        // keep going to create other layers
      }
    }

    return layerLists.toArray(new LayerList[layerLists.size()]);
  }
  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 Iterable<GeographicText> makeIterable(DrawContext dc) {
   // get dispay dist for this service for use in label annealing
   double maxDisplayDistance = this.getPlaceNameService().getMaxDisplayDistance();
   ArrayList<GeographicText> list = new ArrayList<GeographicText>();
   for (int i = 0; i < this.numEntries; i++) {
     CharSequence str = getText(i);
     Position pos = getPosition(i);
     GeographicText text = new UserFacingText(str, pos);
     text.setFont(this.placeNameService.getFont());
     text.setColor(this.placeNameService.getColor());
     text.setBackgroundColor(this.placeNameService.getBackgroundColor());
     text.setVisible(isNameVisible(dc, this.placeNameService, pos));
     text.setPriority(maxDisplayDistance);
     list.add(text);
   }
   return list;
 }
    public List<NavigationTile> navTilesVisible(
        DrawContext dc, double minDistSquared, double maxDistSquared) {
      ArrayList<NavigationTile> navList = new ArrayList<NavigationTile>();
      if (this.isNavSectorVisible(dc, minDistSquared, maxDistSquared)) {
        if (this.level > 0 && !this.hasSubTiles()) this.buildSubNavTiles();

        if (this.hasSubTiles()) {
          for (NavigationTile nav : subNavTiles) {
            navList.addAll(nav.navTilesVisible(dc, minDistSquared, maxDistSquared));
          }
        } else // at bottom level navigation tile
        {
          navList.add(this);
        }
      }

      return navList;
    }
  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);
  }