/**
   * Checking if NoData and ROI are defined correctly
   *
   * @param indexed
   * @param image
   * @param roi
   * @param nodata
   * @param destNoData
   */
  private void checkNoDataROI(
      RenderedImage indexed, RenderedImage image, ROI roi, Range nodata, int destNoData) {
    // Ensure the dimensions are the same
    assertEquals(indexed.getMinX(), image.getMinX());
    assertEquals(indexed.getMinY(), image.getMinY());
    assertEquals(indexed.getWidth(), image.getWidth());
    assertEquals(indexed.getHeight(), image.getHeight());

    boolean roiExists = roi != null;
    boolean nodataExists = nodata != null;
    // Simply ensure no exception is thrown
    if (!nodataExists && !roiExists) {
      PlanarImage.wrapRenderedImage(indexed).getTiles();
      return;
    }

    if (nodataExists) {
      nodata = RangeFactory.convertToDoubleRange(nodata);
    }
    RandomIter roiIter = null;
    Rectangle roiBounds = null;
    if (roiExists) {
      PlanarImage roiIMG = roi.getAsImage();
      roiIter = RandomIterFactory.create(roiIMG, null, true, true);
      roiBounds = roi.getBounds();
    }

    // Else check ROI and NoData
    RandomIter sourceIter = RandomIterFactory.create(image, null, true, true);
    RandomIter destIter = RandomIterFactory.create(indexed, null, true, true);
    // Start the iteration (we iterate only the first band)
    int w = image.getWidth();
    int h = image.getHeight();
    int minX = image.getMinX();
    int minY = image.getMinY();
    int maxX = minX + w;
    int maxY = minY + h;
    int limx = minX - image.getTileGridXOffset();
    int limy = minY - image.getTileGridYOffset();
    Rectangle translated = new Rectangle(limx, limy, w, h);

    for (int y = minY; y < maxY; y++) {
      for (int x = minX; x < maxX; x++) {

        double src = sourceIter.getSampleDouble(x, y, 0);
        double dest = destIter.getSampleDouble(x, y, 0);

        boolean valid = true;

        // ROI Check
        if (roiExists
            && !(roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0)
            && translated.contains(x, y)) {
          valid = false;
        }

        // NoData Check
        if (nodataExists && nodata.contains(src)) {
          valid = false;
        }
        if (!valid) {
          assertEquals(destNoData, dest, TOLERANCE);
        }
      }
    }
  }
  /**
   * Returns a tile for reading.
   *
   * @param tileX The X index of the tile.
   * @param tileY The Y index of the tile.
   * @return The tile as a <code>Raster</code>.
   */
  public Raster computeTile(int tileX, int tileY) {
    // STATISTICAL ELABORATIONS
    // selection of the format tags
    RasterFormatTag[] formatTags = getFormatTags();
    // Selection of the RasterAccessor parameters
    Raster source = getSourceImage(0).getTile(tileX, tileY);
    // Control if the Period is bigger than the tile dimension, in that case, the
    // statistics are not updated
    if (xPeriod > getTileWidth() || yPeriod > getTileHeight()) {
      return source;
    }

    Rectangle srcRect = getSourceImage(0).getBounds().intersection(source.getBounds());
    // creation of the RasterAccessor
    RasterAccessor src =
        new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel());

    // ROI calculations if roiAccessor is used
    RasterAccessor roi = null;
    RandomIter roiIter = null;
    if (useROIAccessor) {
      // Note that the getExtendedData() method is not called because the input images are padded.
      // For each image there is a check if the rectangle is contained inside the source image;
      // if this not happen, the data is taken from the padded image.
      Raster roiRaster = null;
      if (srcROIImage.getBounds().contains(srcRect)) {
        roiRaster = srcROIImage.getData(srcRect);
      } else {
        roiRaster = srcROIImgExt.getData(srcRect);
      }

      // creation of the rasterAccessor
      roi =
          new RasterAccessor(
              roiRaster,
              srcRect,
              RasterAccessor.findCompatibleTags(new RenderedImage[] {srcROIImage}, srcROIImage)[0],
              srcROIImage.getColorModel());
    } else if (hasROI) {
      roiIter = RandomIterFactory.create(srcROIImage, srcROIImage.getBounds(), true, true);
    }

    // Computation of the statistics
    switch (src.getDataType()) {
      case DataBuffer.TYPE_BYTE:
        byteLoop(src, srcRect, roi, stats, roiIter);
        break;
      case DataBuffer.TYPE_USHORT:
        ushortLoop(src, srcRect, roi, stats, roiIter);
        break;
      case DataBuffer.TYPE_SHORT:
        shortLoop(src, srcRect, roi, stats, roiIter);
        break;
      case DataBuffer.TYPE_INT:
        intLoop(src, srcRect, roi, stats, roiIter);
        break;
      case DataBuffer.TYPE_FLOAT:
        floatLoop(src, srcRect, roi, stats, roiIter);
        break;
      case DataBuffer.TYPE_DOUBLE:
        doubleLoop(src, srcRect, roi, stats, roiIter);
        break;
    }

    return source;
  }