Esempio n. 1
0
 private BufferedImage getImage(File file) {
   ImageInputStream iis = null;
   BufferedImage image = null;
   try {
     iis = ImageIO.createImageInputStream(file);
     Iterator<ImageReader> it = ImageIO.getImageReaders(iis);
     if (!it.hasNext()) throw new UnsupportedOperationException("No image reader fround.");
     ImageReader reader = it.next();
     reader.setInput(iis);
     int scaleFactor;
     if (reader.getWidth(0) >= reader.getHeight(0))
       scaleFactor = Math.round(((float) reader.getWidth(0)) / MAX_SIZE);
     else scaleFactor = Math.round(((float) reader.getHeight(0)) / MAX_SIZE);
     ImageReadParam param = reader.getDefaultReadParam();
     param.setSourceSubsampling(scaleFactor, scaleFactor, 0, 0);
     image = reader.read(0, param);
   } catch (IOException e) {
     e.printStackTrace();
   } finally {
     if (iis != null)
       try {
         iis.close();
       } catch (IOException e) {
         e.printStackTrace();
       }
   }
   return image;
 }
Esempio n. 2
0
  @Test
  public void testDataTypes() throws IOException, FileNotFoundException {
    if (!isGDALAvailable) {
      return;
    }
    final List<String> fileList = new ArrayList<String>(4);
    fileList.add("paletted.tif");
    fileList.add("utmByte.tif");
    fileList.add("utmInt16.tif");
    fileList.add("utmInt32.tif");
    fileList.add("utmFloat32.tif");
    fileList.add("utmFloat64.tif");

    for (String fileName : fileList) {
      final ImageReadParam irp = new ImageReadParam();
      final File inputFile = TestData.file(this, fileName);
      irp.setSourceSubsampling(1, 1, 0, 0);
      ImageReader reader = new GeoTiffImageReaderSpi().createReaderInstance();
      reader.setInput(inputFile);
      final RenderedImage image = reader.readAsRenderedImage(0, irp);
      if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, fileName);
      if (!fileName.contains("paletted")) {
        Assert.assertEquals(256, image.getHeight());
        Assert.assertEquals(256, image.getWidth());
      } else {
        Assert.assertEquals(128, image.getHeight());
        Assert.assertEquals(128, image.getWidth());
      }

      reader.dispose();
    }
  }
Esempio n. 3
0
  /**
   * Test Writing capabilities.
   *
   * @throws FileNotFoundException
   * @throws IOException
   */
  @Test
  public void write() throws IOException, FileNotFoundException {
    if (!isGDALAvailable) {
      return;
    }
    final File outputFile = TestData.temp(this, "writetest.tif", false);
    outputFile.deleteOnExit();
    final File inputFile = TestData.file(this, "utmByte.tif");

    ImageReadParam rparam = new ImageReadParam();
    rparam.setSourceRegion(new Rectangle(1, 1, 300, 500));
    rparam.setSourceSubsampling(1, 2, 0, 0);
    ImageReader reader = new GeoTiffImageReaderSpi().createReaderInstance();
    reader.setInput(inputFile);
    final IIOMetadata metadata = reader.getImageMetadata(0);

    final ParameterBlockJAI pbjImageRead = new ParameterBlockJAI("ImageRead");
    pbjImageRead.setParameter("Input", inputFile);
    pbjImageRead.setParameter("reader", reader);
    pbjImageRead.setParameter("readParam", rparam);

    final ImageLayout l = new ImageLayout();
    l.setTileGridXOffset(0).setTileGridYOffset(0).setTileHeight(256).setTileWidth(256);

    RenderedOp image =
        JAI.create("ImageRead", pbjImageRead, new RenderingHints(JAI.KEY_IMAGE_LAYOUT, l));

    if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, "geotiff");

    // ////////////////////////////////////////////////////////////////
    // preparing to write
    // ////////////////////////////////////////////////////////////////
    final ParameterBlockJAI pbjImageWrite = new ParameterBlockJAI("ImageWrite");
    ImageWriter writer = new GeoTiffImageWriterSpi().createWriterInstance();
    pbjImageWrite.setParameter("Output", outputFile);
    pbjImageWrite.setParameter("writer", writer);
    pbjImageWrite.setParameter("ImageMetadata", metadata);
    pbjImageWrite.setParameter("Transcode", false);
    ImageWriteParam param = new ImageWriteParam(Locale.getDefault());
    param.setSourceRegion(new Rectangle(10, 10, 100, 100));
    param.setSourceSubsampling(2, 1, 0, 0);
    pbjImageWrite.setParameter("writeParam", param);

    pbjImageWrite.addSource(image);
    final RenderedOp op = JAI.create("ImageWrite", pbjImageWrite);
    final ImageWriter writer2 =
        (ImageWriter) op.getProperty(ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER);
    writer2.dispose();

    // ////////////////////////////////////////////////////////////////
    // preparing to read again
    // ////////////////////////////////////////////////////////////////
    final ParameterBlockJAI pbjImageReRead = new ParameterBlockJAI("ImageRead");
    pbjImageReRead.setParameter("Input", outputFile);
    pbjImageReRead.setParameter("Reader", new GeoTiffImageReaderSpi().createReaderInstance());
    final RenderedOp image2 = JAI.create("ImageRead", pbjImageReRead);
    if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image2, "geotif2");
    else Assert.assertNotNull(image2.getTiles());
  }
  public void readDescendingRasterBand(
      final int sourceOffsetX,
      final int sourceOffsetY,
      final int sourceStepX,
      final int sourceStepY,
      final ProductData destBuffer,
      final int destOffsetX,
      final int destOffsetY,
      final int destWidth,
      final int destHeight,
      final int imageID,
      final ImageIOFile img,
      final int bandSampleOffset,
      final boolean isAntennaPointingRight)
      throws IOException {

    final Raster data;

    synchronized (dataDir) {
      final ImageReader reader = img.getReader();
      final ImageReadParam param = reader.getDefaultReadParam();
      param.setSourceSubsampling(
          sourceStepX, sourceStepY, sourceOffsetX % sourceStepX, sourceOffsetY % sourceStepY);

      final RenderedImage image = reader.readAsRenderedImage(0, param);
      if (flipToSARGeometry && isAntennaPointingRight) { // flip the image left to right
        data =
            image.getData(
                new Rectangle(
                    img.getSceneWidth() - destOffsetX - destWidth,
                    destOffsetY,
                    destWidth,
                    destHeight));
      } else {
        data = image.getData(new Rectangle(destOffsetX, destOffsetY, destWidth, destHeight));
      }
    }

    final DataBuffer dataBuffer = data.getDataBuffer();
    final SampleModel sampleModel = data.getSampleModel();
    final int sampleOffset = imageID + bandSampleOffset;

    if (flipToSARGeometry && isAntennaPointingRight) { // flip the image left to right
      final int[] dArray = new int[destWidth * destHeight];
      sampleModel.getSamples(0, 0, destWidth, destHeight, sampleOffset, dArray, dataBuffer);

      int srcStride, destStride;
      for (int r = 0; r < destHeight; r++) {
        srcStride = r * destWidth;
        destStride = r * destWidth + destWidth;
        for (int c = 0; c < destWidth; c++) {
          destBuffer.setElemIntAt(destStride - c - 1, dArray[srcStride + c]);
        }
      }
    } else { // no flipping is needed
      sampleModel.getSamples(
          0, 0, destWidth, destHeight, sampleOffset, (int[]) destBuffer.getElems(), dataBuffer);
    }
  }
  /**
   * This method is responsible for evaluating possible subsampling factors once the best resolution
   * level has been found, in case we have support for overviews, or starting from the original
   * coverage in case there are no overviews available.
   *
   * <p>Anyhow this method should not be called directly but subclasses should make use of the
   * setReadParams method instead in order to transparently look for overviews.
   *
   * @param imageChoice
   * @param readP
   * @param requestedRes
   */
  protected final void decimationOnReadingControl(
      String coverageName, Integer imageChoice, ImageReadParam readP, double[] requestedRes) {
    {
      int w, h;
      double selectedRes[] = new double[2];
      final int choice = imageChoice.intValue();
      if (choice == 0) {
        // highest resolution
        w = getOriginalGridRange(coverageName).getSpan(0);
        h = getOriginalGridRange(coverageName).getSpan(1);
        selectedRes[0] = getHighestRes()[0];
        selectedRes[1] = getHighestRes()[1];
      } else {
        // some overview
        selectedRes[0] = overViewResolutions[choice - 1][0];
        selectedRes[1] = overViewResolutions[choice - 1][1];
        w = (int) Math.round(getOriginalEnvelope(coverageName).getSpan(0) / selectedRes[0]);
        h = (int) Math.round(getOriginalEnvelope(coverageName).getSpan(1) / selectedRes[1]);
      }
      // /////////////////////////////////////////////////////////////////////
      // DECIMATION ON READING
      // Setting subsampling factors with some checkings
      // 1) the subsampling factors cannot be zero
      // 2) the subsampling factors cannot be such that the w or h are
      // zero
      // /////////////////////////////////////////////////////////////////////
      if (requestedRes == null) {
        readP.setSourceSubsampling(1, 1, 0, 0);

      } else {
        int subSamplingFactorX = (int) Math.floor(requestedRes[0] / selectedRes[0]);
        subSamplingFactorX = subSamplingFactorX == 0 ? 1 : subSamplingFactorX;

        while (w / subSamplingFactorX <= 0 && subSamplingFactorX >= 0) subSamplingFactorX--;
        subSamplingFactorX = subSamplingFactorX == 0 ? 1 : subSamplingFactorX;

        int subSamplingFactorY = (int) Math.floor(requestedRes[1] / selectedRes[1]);
        subSamplingFactorY = subSamplingFactorY == 0 ? 1 : subSamplingFactorY;

        while (h / subSamplingFactorY <= 0 && subSamplingFactorY >= 0) subSamplingFactorY--;
        subSamplingFactorY = subSamplingFactorY == 0 ? 1 : subSamplingFactorY;

        readP.setSourceSubsampling(subSamplingFactorX, subSamplingFactorY, 0, 0);
      }
    }
  }
Esempio n. 6
0
  @Test
  public void imageReadParam2() throws IOException {
    Iterator<ImageReader> itr = ImageIO.getImageReadersByFormatName("jpg");
    ImageReader imageReader = itr.next();

    ImageInputStream imageInputStream = ImageIO.createImageInputStream(new File(SRCIMG));
    imageReader.setInput(imageInputStream, true);

    ImageReadParam imageReadParam = imageReader.getDefaultReadParam();
    imageReadParam.setSourceSubsampling(3, 3, 0, 0);

    BufferedImage bufferedImage = imageReader.read(0, imageReadParam);

    boolean b = ImageIO.write(bufferedImage, "jpg", new File(CONTEXT + "1.jpg"));
    System.out.println(b);
  }
Esempio n. 7
0
  /**
   * Test Read without exploiting JAI-ImageIO Tools
   *
   * @throws FileNotFoundException
   * @throws IOException
   */
  @Test
  public void manualRead() throws IOException, FileNotFoundException {
    if (!isGDALAvailable) {
      return;
    }
    final ImageReadParam irp = new ImageReadParam();

    // Reading a simple GrayScale image
    String fileName = "utmByte.tif";
    final File inputFile = TestData.file(this, fileName);
    irp.setSourceSubsampling(2, 2, 0, 0);
    ImageReader reader = new GeoTiffImageReaderSpi().createReaderInstance();
    reader.setInput(inputFile);
    final RenderedImage image = reader.readAsRenderedImage(0, irp);
    if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, fileName);
    Assert.assertEquals(128, image.getWidth());
    Assert.assertEquals(128, image.getHeight());
    reader.dispose();
  }
  private synchronized Raster readRect(
      int sourceOffsetX,
      int sourceOffsetY,
      int sourceStepX,
      int sourceStepY,
      int destOffsetX,
      int destOffsetY,
      int destWidth,
      int destHeight)
      throws IOException {
    ImageReadParam readParam = imageReader.getDefaultReadParam();
    int subsamplingXOffset = sourceOffsetX % sourceStepX;
    int subsamplingYOffset = sourceOffsetY % sourceStepY;
    readParam.setSourceSubsampling(
        sourceStepX, sourceStepY, subsamplingXOffset, subsamplingYOffset);
    RenderedImage subsampledImage = imageReader.readAsRenderedImage(FIRST_IMAGE, readParam);

    return subsampledImage.getData(new Rectangle(destOffsetX, destOffsetY, destWidth, destHeight));
  }
  private BufferedImage scaleDown(
      final ImageInputStream inputStream, final int maxWidth, final int maxHeight)
      throws IOException {
    final Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
    if (!readers.hasNext()) {
      throw new IOException("No ImageReader available for the given ImageInputStream");
    }
    // Use the first reader, next will instantiate ImageReader which needs to be disposed
    final ImageReader reader = readers.next();
    try {
      final ImageReadParam param = reader.getDefaultReadParam();
      reader.setInput(inputStream);

      final Dimension original = new Dimension(reader.getWidth(0), reader.getHeight(0));
      final Dimension target = new Dimension(maxWidth, maxHeight);
      final int ratio = maintainAspectRatio(original, target);
      param.setSourceSubsampling(ratio, ratio, 0, 0);

      return reader.read(0, param);
    } finally {
      reader.dispose();
    }
  }
  /**
   * This method is responsible for preparing the read param for doing an {@link
   * ImageReader#read(int, ImageReadParam)}.
   *
   * <p>This method is responsible for preparing the read param for doing an {@link
   * ImageReader#read(int, ImageReadParam)}. It sets the passed {@link ImageReadParam} in terms of
   * decimation on reading using the provided requestedEnvelope and requestedDim to evaluate the
   * needed resolution. It also returns and {@link Integer} representing the index of the raster to
   * be read when dealing with multipage raster.
   *
   * @param overviewPolicy it can be one of {@link Hints#VALUE_OVERVIEW_POLICY_IGNORE}, {@link
   *     Hints#VALUE_OVERVIEW_POLICY_NEAREST}, {@link Hints#VALUE_OVERVIEW_POLICY_QUALITY} or {@link
   *     Hints#VALUE_OVERVIEW_POLICY_SPEED}. It specifies the policy to compute the overviews level
   *     upon request.
   * @param readP an instance of {@link ImageReadParam} for setting the subsampling factors.
   * @param requestedEnvelope the {@link GeneralEnvelope} we are requesting.
   * @param requestedDim the requested dimensions.
   * @return the index of the raster to read in the underlying data source.
   * @throws IOException
   * @throws TransformException
   */
  protected Integer setReadParams(
      String coverageName,
      OverviewPolicy overviewPolicy,
      ImageReadParam readP,
      GeneralEnvelope requestedEnvelope,
      Rectangle requestedDim)
      throws IOException, TransformException {

    // //
    //
    // Default image index 0
    //
    // //
    Integer imageChoice = new Integer(0);

    // //
    //
    // Init overview policy
    //
    // //
    // when policy is explictly provided it overrides the policy provided
    // using hints.
    if (overviewPolicy == null) overviewPolicy = extractOverviewPolicy();

    // //
    //
    // default values for subsampling
    //
    // //
    readP.setSourceSubsampling(1, 1, 0, 0);

    // //
    //
    // requested to ignore overviews
    //
    // //
    if (overviewPolicy.equals(OverviewPolicy.IGNORE)) return imageChoice;

    // //
    //
    // Am I going to decimate or to use overviews? If this file has only
    // one page we use decimation, otherwise we use the best page available.
    // Future versions should use both.
    //
    // //
    final boolean useOverviews = (numOverviews > 0) ? true : false;

    // //
    //
    // Resolution requested. I am here computing the resolution required by
    // the user.
    //
    // //
    double[] requestedRes =
        getResolution(requestedEnvelope, requestedDim, getCoordinateReferenceSystem(coverageName));
    if (requestedRes == null) return imageChoice;

    // //
    //
    // overviews or decimation
    //
    // //
    if (useOverviews) imageChoice = pickOverviewLevel(coverageName, overviewPolicy, requestedRes);

    // /////////////////////////////////////////////////////////////////////
    // DECIMATION ON READING
    // /////////////////////////////////////////////////////////////////////
    decimationOnReadingControl(coverageName, imageChoice, readP, requestedRes);
    return imageChoice;
  }
Esempio n. 11
0
  /**
   * Load a specified a raster as a portion of the granule describe by this {@link
   * GranuleDescriptor}.
   *
   * @param imageReadParameters the {@link ImageReadParam} to use for reading.
   * @param index the index to use for the {@link ImageReader}.
   * @param cropBBox the bbox to use for cropping.
   * @param mosaicWorldToGrid the cropping grid to world transform.
   * @param request the incoming request to satisfy.
   * @param hints {@link Hints} to be used for creating this raster.
   * @return a specified a raster as a portion of the granule describe by this {@link
   *     GranuleDescriptor}.
   * @throws IOException in case an error occurs.
   */
  public GranuleLoadingResult loadRaster(
      final ImageReadParam imageReadParameters,
      final int index,
      final ReferencedEnvelope cropBBox,
      final MathTransform2D mosaicWorldToGrid,
      final RasterLayerRequest request,
      final Hints hints)
      throws IOException {

    if (LOGGER.isLoggable(java.util.logging.Level.FINER)) {
      final String name = Thread.currentThread().getName();
      LOGGER.finer(
          "Thread:" + name + " Loading raster data for granuleDescriptor " + this.toString());
    }
    ImageReadParam readParameters = null;
    int imageIndex;
    final ReferencedEnvelope bbox =
        inclusionGeometry != null
            ? new ReferencedEnvelope(
                granuleBBOX.intersection(inclusionGeometry.getEnvelopeInternal()),
                granuleBBOX.getCoordinateReferenceSystem())
            : granuleBBOX;
    boolean doFiltering = false;
    if (filterMe) {
      doFiltering = Utils.areaIsDifferent(inclusionGeometry, baseGridToWorld, granuleBBOX);
    }

    // intersection of this tile bound with the current crop bbox
    final ReferencedEnvelope intersection =
        new ReferencedEnvelope(
            bbox.intersection(cropBBox), cropBBox.getCoordinateReferenceSystem());
    if (intersection.isEmpty()) {
      if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
        LOGGER.fine(
            new StringBuilder("Got empty intersection for granule ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString());
      }
      return null;
    }

    ImageInputStream inStream = null;
    ImageReader reader = null;
    try {
      //
      // get info about the raster we have to read
      //

      // get a stream
      assert cachedStreamSPI != null : "no cachedStreamSPI available!";
      inStream =
          cachedStreamSPI.createInputStreamInstance(
              granuleUrl, ImageIO.getUseCache(), ImageIO.getCacheDirectory());
      if (inStream == null) return null;

      // get a reader and try to cache the relevant SPI
      if (cachedReaderSPI == null) {
        reader = ImageIOExt.getImageioReader(inStream);
        if (reader != null) cachedReaderSPI = reader.getOriginatingProvider();
      } else reader = cachedReaderSPI.createReaderInstance();
      if (reader == null) {
        if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
          LOGGER.warning(
              new StringBuilder("Unable to get s reader for granuleDescriptor ")
                  .append(this.toString())
                  .append(" with request ")
                  .append(request.toString())
                  .append(" Resulting in no granule loaded: Empty result")
                  .toString());
        }
        return null;
      }
      // set input
      reader.setInput(inStream);

      // Checking for heterogeneous granules
      if (request.isHeterogeneousGranules()) {
        // create read parameters
        readParameters = new ImageReadParam();

        // override the overviews controller for the base layer
        imageIndex =
            ReadParamsController.setReadParams(
                request.getRequestedResolution(),
                request.getOverviewPolicy(),
                request.getDecimationPolicy(),
                readParameters,
                request.rasterManager,
                overviewsController);
      } else {
        imageIndex = index;
        readParameters = imageReadParameters;
      }

      // get selected level and base level dimensions
      final GranuleOverviewLevelDescriptor selectedlevel = getLevel(imageIndex, reader);

      // now create the crop grid to world which can be used to decide
      // which source area we need to crop in the selected level taking
      // into account the scale factors imposed by the selection of this
      // level together with the base level grid to world transformation
      AffineTransform2D cropWorldToGrid =
          new AffineTransform2D(selectedlevel.gridToWorldTransformCorner);
      cropWorldToGrid = (AffineTransform2D) cropWorldToGrid.inverse();
      // computing the crop source area which lives into the
      // selected level raster space, NOTICE that at the end we need to
      // take into account the fact that we might also decimate therefore
      // we cannot just use the crop grid to world but we need to correct
      // it.
      final Rectangle sourceArea =
          CRS.transform(cropWorldToGrid, intersection).toRectangle2D().getBounds();
      // gutter
      if (selectedlevel.baseToLevelTransform.isIdentity()) sourceArea.grow(2, 2);
      XRectangle2D.intersect(
          sourceArea,
          selectedlevel.rasterDimensions,
          sourceArea); // make sure roundings don't bother us
      // is it empty??
      if (sourceArea.isEmpty()) {
        if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
          LOGGER.fine(
              "Got empty area for granuleDescriptor "
                  + this.toString()
                  + " with request "
                  + request.toString()
                  + " Resulting in no granule loaded: Empty result");
        }
        return null;

      } else if (LOGGER.isLoggable(java.util.logging.Level.FINER)) {
        LOGGER.finer(
            "Loading level "
                + imageIndex
                + " with source region: "
                + sourceArea
                + " subsampling: "
                + readParameters.getSourceXSubsampling()
                + ","
                + readParameters.getSourceYSubsampling()
                + " for granule:"
                + granuleUrl);
      }

      // Setting subsampling
      int newSubSamplingFactor = 0;
      final String pluginName = cachedReaderSPI.getPluginClassName();
      if (pluginName != null && pluginName.equals(ImageUtilities.DIRECT_KAKADU_PLUGIN)) {
        final int ssx = readParameters.getSourceXSubsampling();
        final int ssy = readParameters.getSourceYSubsampling();
        newSubSamplingFactor = ImageIOUtilities.getSubSamplingFactor2(ssx, ssy);
        if (newSubSamplingFactor != 0) {
          if (newSubSamplingFactor > maxDecimationFactor && maxDecimationFactor != -1) {
            newSubSamplingFactor = maxDecimationFactor;
          }
          readParameters.setSourceSubsampling(newSubSamplingFactor, newSubSamplingFactor, 0, 0);
        }
      }

      // set the source region
      readParameters.setSourceRegion(sourceArea);
      final RenderedImage raster;
      try {
        // read
        raster =
            request
                .getReadType()
                .read(
                    readParameters,
                    imageIndex,
                    granuleUrl,
                    selectedlevel.rasterDimensions,
                    reader,
                    hints,
                    false);

      } catch (Throwable e) {
        if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
          LOGGER.log(
              java.util.logging.Level.FINE,
              "Unable to load raster for granuleDescriptor "
                  + this.toString()
                  + " with request "
                  + request.toString()
                  + " Resulting in no granule loaded: Empty result",
              e);
        }
        return null;
      }

      // use fixed source area
      sourceArea.setRect(readParameters.getSourceRegion());

      //
      // setting new coefficients to define a new affineTransformation
      // to be applied to the grid to world transformation
      // -----------------------------------------------------------------------------------
      //
      // With respect to the original envelope, the obtained planarImage
      // needs to be rescaled. The scaling factors are computed as the
      // ratio between the cropped source region sizes and the read
      // image sizes.
      //
      // place it in the mosaic using the coords created above;
      double decimationScaleX = ((1.0 * sourceArea.width) / raster.getWidth());
      double decimationScaleY = ((1.0 * sourceArea.height) / raster.getHeight());
      final AffineTransform decimationScaleTranform =
          XAffineTransform.getScaleInstance(decimationScaleX, decimationScaleY);

      // keep into account translation  to work into the selected level raster space
      final AffineTransform afterDecimationTranslateTranform =
          XAffineTransform.getTranslateInstance(sourceArea.x, sourceArea.y);

      // now we need to go back to the base level raster space
      final AffineTransform backToBaseLevelScaleTransform = selectedlevel.baseToLevelTransform;

      // now create the overall transform
      final AffineTransform finalRaster2Model = new AffineTransform(baseGridToWorld);
      finalRaster2Model.concatenate(CoverageUtilities.CENTER_TO_CORNER);
      final double x = finalRaster2Model.getTranslateX();
      final double y = finalRaster2Model.getTranslateY();

      if (!XAffineTransform.isIdentity(backToBaseLevelScaleTransform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(backToBaseLevelScaleTransform);
      if (!XAffineTransform.isIdentity(afterDecimationTranslateTranform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(afterDecimationTranslateTranform);
      if (!XAffineTransform.isIdentity(decimationScaleTranform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(decimationScaleTranform);

      // keep into account translation factors to place this tile
      finalRaster2Model.preConcatenate((AffineTransform) mosaicWorldToGrid);
      final Interpolation interpolation = request.getInterpolation();
      // paranoiac check to avoid that JAI freaks out when computing its internal layouT on images
      // that are too small
      Rectangle2D finalLayout =
          ImageUtilities.layoutHelper(
              raster,
              (float) finalRaster2Model.getScaleX(),
              (float) finalRaster2Model.getScaleY(),
              (float) finalRaster2Model.getTranslateX(),
              (float) finalRaster2Model.getTranslateY(),
              interpolation);
      if (finalLayout.isEmpty()) {
        if (LOGGER.isLoggable(java.util.logging.Level.INFO))
          LOGGER.info(
              "Unable to create a granuleDescriptor "
                  + this.toString()
                  + " due to jai scale bug creating a null source area");
        return null;
      }
      ROI granuleLoadingShape = null;
      if (granuleROIShape != null) {

        final Point2D translate =
            mosaicWorldToGrid.transform(new DirectPosition2D(x, y), (Point2D) null);
        AffineTransform tx2 = new AffineTransform();
        tx2.preConcatenate(
            AffineTransform.getScaleInstance(
                ((AffineTransform) mosaicWorldToGrid).getScaleX(),
                -((AffineTransform) mosaicWorldToGrid).getScaleY()));
        tx2.preConcatenate(
            AffineTransform.getScaleInstance(
                ((AffineTransform) baseGridToWorld).getScaleX(),
                -((AffineTransform) baseGridToWorld).getScaleY()));
        tx2.preConcatenate(
            AffineTransform.getTranslateInstance(translate.getX(), translate.getY()));
        granuleLoadingShape = (ROI) granuleROIShape.transform(tx2);
      }
      // apply the affine transform  conserving indexed color model
      final RenderingHints localHints =
          new RenderingHints(
              JAI.KEY_REPLACE_INDEX_COLOR_MODEL,
              interpolation instanceof InterpolationNearest ? Boolean.FALSE : Boolean.TRUE);
      if (XAffineTransform.isIdentity(finalRaster2Model, Utils.AFFINE_IDENTITY_EPS)) {
        return new GranuleLoadingResult(raster, granuleLoadingShape, granuleUrl, doFiltering);
      } else {
        //
        // In case we are asked to use certain tile dimensions we tile
        // also at this stage in case the read type is Direct since
        // buffered images comes up untiled and this can affect the
        // performances of the subsequent affine operation.
        //
        final Dimension tileDimensions = request.getTileDimensions();
        if (tileDimensions != null && request.getReadType().equals(ReadType.DIRECT_READ)) {
          final ImageLayout layout = new ImageLayout();
          layout.setTileHeight(tileDimensions.width).setTileWidth(tileDimensions.height);
          localHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
        } else {
          if (hints != null && hints.containsKey(JAI.KEY_IMAGE_LAYOUT)) {
            final Object layout = hints.get(JAI.KEY_IMAGE_LAYOUT);
            if (layout != null && layout instanceof ImageLayout) {
              localHints.add(
                  new RenderingHints(JAI.KEY_IMAGE_LAYOUT, ((ImageLayout) layout).clone()));
            }
          }
        }
        if (hints != null && hints.containsKey(JAI.KEY_TILE_CACHE)) {
          final Object cache = hints.get(JAI.KEY_TILE_CACHE);
          if (cache != null && cache instanceof TileCache)
            localHints.add(new RenderingHints(JAI.KEY_TILE_CACHE, (TileCache) cache));
        }
        if (hints != null && hints.containsKey(JAI.KEY_TILE_SCHEDULER)) {
          final Object scheduler = hints.get(JAI.KEY_TILE_SCHEDULER);
          if (scheduler != null && scheduler instanceof TileScheduler)
            localHints.add(new RenderingHints(JAI.KEY_TILE_SCHEDULER, (TileScheduler) scheduler));
        }
        boolean addBorderExtender = true;
        if (hints != null && hints.containsKey(JAI.KEY_BORDER_EXTENDER)) {
          final Object extender = hints.get(JAI.KEY_BORDER_EXTENDER);
          if (extender != null && extender instanceof BorderExtender) {
            localHints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER, (BorderExtender) extender));
            addBorderExtender = false;
          }
        }
        // border extender
        if (addBorderExtender) {
          localHints.add(ImageUtilities.BORDER_EXTENDER_HINTS);
        }
        //                boolean hasScaleX=!(Math.abs(finalRaster2Model.getScaleX()-1) <
        // 1E-2/(raster.getWidth()+1-raster.getMinX()));
        //                boolean hasScaleY=!(Math.abs(finalRaster2Model.getScaleY()-1) <
        // 1E-2/(raster.getHeight()+1-raster.getMinY()));
        //                boolean hasShearX=!(finalRaster2Model.getShearX() == 0.0);
        //                boolean hasShearY=!(finalRaster2Model.getShearY() == 0.0);
        //                boolean hasTranslateX=!(Math.abs(finalRaster2Model.getTranslateX()) <
        // 0.01F);
        //                boolean hasTranslateY=!(Math.abs(finalRaster2Model.getTranslateY()) <
        // 0.01F);
        //                boolean isTranslateXInt=!(Math.abs(finalRaster2Model.getTranslateX() -
        // (int) finalRaster2Model.getTranslateX()) <  0.01F);
        //                boolean isTranslateYInt=!(Math.abs(finalRaster2Model.getTranslateY() -
        // (int) finalRaster2Model.getTranslateY()) <  0.01F);
        //
        //                boolean isIdentity = finalRaster2Model.isIdentity() &&
        // !hasScaleX&&!hasScaleY &&!hasTranslateX&&!hasTranslateY;

        //                // TODO how can we check that the a skew is harmelss????
        //                if(isIdentity){
        //                    // TODO check if we are missing anything like tiling or such that
        // comes from hints
        //                    return new GranuleLoadingResult(raster, granuleLoadingShape,
        // granuleUrl, doFiltering);
        //                }
        //
        //                // TOLERANCE ON PIXELS SIZE
        //
        //                // Check and see if the affine transform is in fact doing
        //                // a Translate operation. That is a scale by 1 and no rotation.
        //                // In which case call translate. Note that only integer translate
        //                // is applicable. For non-integer translate we'll have to do the
        //                // affine.
        //                // If the hints contain an ImageLayout hint, we can't use
        //                // TranslateIntOpImage since it isn't capable of dealing with that.
        //                // Get ImageLayout from renderHints if any.
        //                ImageLayout layout = RIFUtil.getImageLayoutHint(localHints);
        //                if ( !hasScaleX &&
        //                     !hasScaleY  &&
        //                      !hasShearX&&
        //                      !hasShearY&&
        //                      isTranslateXInt&&
        //                      isTranslateYInt&&
        //                    layout == null) {
        //                    // It's a integer translate
        //                    return new GranuleLoadingResult(new TranslateIntOpImage(raster,
        //                                                    localHints,
        //                                                   (int) finalRaster2Model.getShearX(),
        //                                                   (int)
        // finalRaster2Model.getShearY()),granuleLoadingShape, granuleUrl, doFiltering);
        //                }

        ImageWorker iw = new ImageWorker(raster);
        iw.setRenderingHints(localHints);
        iw.affine(finalRaster2Model, interpolation, request.getBackgroundValues());
        return new GranuleLoadingResult(
            iw.getRenderedImage(), granuleLoadingShape, granuleUrl, doFiltering);
      }

    } catch (IllegalStateException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;
    } catch (org.opengis.referencing.operation.NoninvertibleTransformException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;
    } catch (TransformException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;

    } finally {
      try {
        if (request.getReadType() != ReadType.JAI_IMAGEREAD && inStream != null) {
          inStream.close();
        }
      } finally {
        if (request.getReadType() != ReadType.JAI_IMAGEREAD && reader != null) {
          reader.dispose();
        }
      }
    }
  }