private static boolean isGeographic(AVList params) {
   return (null != params
       && params.hasKey(AVKey.COORDINATE_SYSTEM)
       && AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(params.getValue(AVKey.COORDINATE_SYSTEM)));
 }
  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);
  }
  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);
        }
      }
    }
  }