/**
   * This method reads in the TIFF image, constructs an appropriate CRS, determines the math
   * transform from raster to the CRS model, and constructs a GridCoverage.
   *
   * @param params currently ignored, potentially may be used for hints.
   * @return grid coverage represented by the image
   * @throws IOException on any IO related troubles
   */
  public GridCoverage2D read(GeneralParameterValue[] params) throws IOException {
    GeneralEnvelope requestedEnvelope = null;
    Rectangle dim = null;
    Color inputTransparentColor = null;
    OverviewPolicy overviewPolicy = null;
    int[] suggestedTileSize = null;
    if (params != null) {

      //
      // Checking params
      //
      if (params != null) {
        for (int i = 0; i < params.length; i++) {
          final ParameterValue param = (ParameterValue) params[i];
          final ReferenceIdentifier name = param.getDescriptor().getName();
          if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
            final GridGeometry2D gg = (GridGeometry2D) param.getValue();
            requestedEnvelope = new GeneralEnvelope((Envelope) gg.getEnvelope2D());
            dim = gg.getGridRange2D().getBounds();
            continue;
          }
          if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) {
            overviewPolicy = (OverviewPolicy) param.getValue();
            continue;
          }
          if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) {
            inputTransparentColor = (Color) param.getValue();
            continue;
          }
          if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) {
            String suggestedTileSize_ = (String) param.getValue();
            if (suggestedTileSize_ != null && suggestedTileSize_.length() > 0) {
              suggestedTileSize_ = suggestedTileSize_.trim();
              int commaPosition = suggestedTileSize_.indexOf(",");
              if (commaPosition < 0) {
                int tileDim = Integer.parseInt(suggestedTileSize_);
                suggestedTileSize = new int[] {tileDim, tileDim};
              } else {
                int tileW = Integer.parseInt(suggestedTileSize_.substring(0, commaPosition));
                int tileH = Integer.parseInt(suggestedTileSize_.substring(commaPosition + 1));
                suggestedTileSize = new int[] {tileW, tileH};
              }
            }
            continue;
          }
        }
      }
    }

    //
    // set params
    //
    Integer imageChoice = new Integer(0);
    final ImageReadParam readP = new ImageReadParam();
    try {
      imageChoice = setReadParams(overviewPolicy, readP, requestedEnvelope, dim);
    } catch (TransformException e) {
      new DataSourceException(e);
    }

    //
    // IMAGE READ OPERATION
    //
    Hints newHints = null;
    if (suggestedTileSize != null) {
      newHints = hints.clone();
      final ImageLayout layout = new ImageLayout();
      layout.setTileGridXOffset(0);
      layout.setTileGridYOffset(0);
      layout.setTileHeight(suggestedTileSize[1]);
      layout.setTileWidth(suggestedTileSize[0]);
      newHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
    }
    final ParameterBlock pbjRead = new ParameterBlock();
    if (extOvrImgChoice >= 0 && imageChoice >= extOvrImgChoice) {
      pbjRead.add(
          ovrInStreamSPI.createInputStreamInstance(
              ovrSource, ImageIO.getUseCache(), ImageIO.getCacheDirectory()));
      pbjRead.add(imageChoice - extOvrImgChoice);
    } else {
      pbjRead.add(
          inStreamSPI != null
              ? inStreamSPI.createInputStreamInstance(
                  source, ImageIO.getUseCache(), ImageIO.getCacheDirectory())
              : ImageIO.createImageInputStream(source));
      pbjRead.add(imageChoice);
    }
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(null);
    pbjRead.add(null);
    pbjRead.add(readP);
    pbjRead.add(READER_SPI.createReaderInstance());
    RenderedOp coverageRaster =
        JAI.create("ImageRead", pbjRead, newHints != null ? (RenderingHints) newHints : null);

    //
    // MASKING INPUT COLOR as indicated
    //
    if (inputTransparentColor != null) {
      coverageRaster =
          new ImageWorker(coverageRaster)
              .setRenderingHints(newHints)
              .makeColorTransparent(inputTransparentColor)
              .getRenderedOperation();
    }

    AffineTransform rasterToModel = getRescaledRasterToModel(coverageRaster);
    try {
      return createCoverage(coverageRaster, ProjectiveTransform.create(rasterToModel));
    } catch (Exception e) {
      // dispose and close file
      ImageUtilities.disposePlanarImageChain(coverageRaster);

      // rethrow
      if (e instanceof DataSourceException) {
        throw (DataSourceException) e;
      }
      throw new DataSourceException(e);
    }
  }
  public boolean extractTiffMetadata(File tifFile, TiffMeta surface)
      throws UnknownCRSException, FactoryException, TransformException, IOException {

    Preconditions.checkArgument(tifFile != null, "File is null.");

    GeoTiffReader gtr = new GeoTiffReader(tifFile);

    try {

      CoordinateReferenceSystem crs = gtr.getCoordinateReferenceSystem();
      surface.setCRS(crs);
      Integer epsgCode = CRS.lookupEpsgCode(crs, true);

      if (epsgCode == null) {
        ReferenceIdentifier name = crs.getName();
        String crsName = "Unknown";
        if (name != null) {
          crsName = name.toString();
        }
        throw new UnknownCRSException(crsName);
      }

      String srid = "EPSG:" + epsgCode;
      surface.setSrid(srid);

      //
      // extremaOp(surface, gtr.read(null));

      /*
       * Build the envelope and set to WGS84
       */
      GeneralEnvelope origEnv = gtr.getOriginalEnvelope();
      DirectPosition ll = origEnv.getLowerCorner();
      DirectPosition ur = origEnv.getUpperCorner();

      Envelope e = new Envelope();
      e.expandToInclude(ll.getOrdinate(0), ll.getOrdinate(1));
      e.expandToInclude(ur.getOrdinate(0), ur.getOrdinate(1));

      Geometry poly = envelopeToWgs84(epsgCode, e);

      if (poly instanceof Polygon) {
        surface.setEnvelope((Polygon) poly);
      }

      /*
       * Figure out the pixel size
       */
      ImageLayout imageLayout = gtr.getImageLayout();
      int imageWidth = imageLayout.getWidth(null);
      int imageHeight = imageLayout.getHeight(null);

      double pixelSizeX = e.getWidth() / imageWidth;
      double pixelSizeY = e.getHeight() / imageHeight;

      surface.setPixelSizeX(pixelSizeX);
      surface.setPixelSizeY(pixelSizeY);

      surface.setMinVal(0d);
      surface.setMaxVal(100d);

      GridCoverage2D gridCoverage2D = gtr.read(null);

      try {
        int nDims = gridCoverage2D.getNumSampleDimensions();
        surface.setNumSampleDimensions(nDims);

        extremaOp(surface, gridCoverage2D);

      } finally {
        gridCoverage2D.dispose(false);
      }

    } finally {

      gtr.dispose();
    }

    return true;
  }