/**
   * Retrieves the original grid to world transformation for this {@link
   * AbstractGridCoverage2DReader}.
   *
   * @param pixInCell specifies the datum of the transformation we want.
   * @return the original grid to world transformation for this {@link
   *     AbstractGridCoverage2DReader}.
   */
  @Override
  public MathTransform getOriginalGridToWorld(String coverageName, PixelInCell pixInCell) {
    if (!checkName(coverageName)) {
      throw new IllegalArgumentException(
          "The specified coverageName " + coverageName + "is not supported");
    }
    synchronized (this) {
      if (raster2Model == null) {
        final GridToEnvelopeMapper geMapper =
            new GridToEnvelopeMapper(
                getOriginalGridRange(coverageName), getOriginalEnvelope(coverageName));
        geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
        raster2Model = geMapper.createTransform();
      }
    }

    // we do not have to change the pixel datum
    if (pixInCell == PixelInCell.CELL_CENTER) return raster2Model;

    // we do have to change the pixel datum
    if (raster2Model instanceof AffineTransform) {
      final AffineTransform tr = new AffineTransform((AffineTransform) raster2Model);
      tr.concatenate(AffineTransform.getTranslateInstance(-0.5, -0.5));
      return ProjectiveTransform.create(tr);
    }
    if (raster2Model instanceof IdentityTransform) {
      final AffineTransform tr = new AffineTransform(1, 0, 0, 1, 0, 0);
      tr.concatenate(AffineTransform.getTranslateInstance(-0.5, -0.5));
      return ProjectiveTransform.create(tr);
    }
    throw new IllegalStateException("This reader's grid to world transform is invalud!");
  }
  /**
   * Sets up the affine transform
   *
   * <p>NOTE It is worth to note that here we do not take into account the half a pixel translation
   * stated by ogc for coverages bounds. One reason is that WMS 1.1.1 does not follow it!!!
   *
   * @param mapExtent the map extent
   * @param paintArea the size of the rendering output area
   * @return a transform that maps from real world coordinates to the screen
   */
  public static AffineTransform worldToScreenTransform(
      ReferencedEnvelope mapExtent, Rectangle paintArea) {

    // //
    //
    // Convert the JTS envelope and get the transform
    //
    // //
    final Envelope2D genvelope = new Envelope2D(mapExtent);

    // //
    //
    // Get the transform
    //
    // //
    final GridToEnvelopeMapper m = (GridToEnvelopeMapper) gridToEnvelopeMappers.get();
    try {
      m.setGridRange(new GridEnvelope2D(paintArea));
      m.setEnvelope(genvelope);
      return m.createAffineTransform().createInverse();
    } catch (MismatchedDimensionException e) {
      LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
      return null;
    } catch (NoninvertibleTransformException e) {
      LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
      return null;
    }
  }
 @Override
 protected GridToEnvelopeMapper initialValue() {
   final GridToEnvelopeMapper mapper = new GridToEnvelopeMapper();
   mapper.setPixelAnchor(PixelInCell.CELL_CORNER);
   return mapper;
 }
Beispiel #4
0
  private void init(
      final BoundingBox granuleBBOX,
      final URL granuleUrl,
      final ImageReaderSpi suggestedSPI,
      final Geometry inclusionGeometry,
      final boolean heterogeneousGranules,
      final boolean handleArtifactsFiltering) {
    this.granuleBBOX = ReferencedEnvelope.reference(granuleBBOX);
    this.granuleUrl = granuleUrl;
    this.inclusionGeometry = inclusionGeometry;
    this.handleArtifactsFiltering = handleArtifactsFiltering;
    filterMe = handleArtifactsFiltering && inclusionGeometry != null;

    // create the base grid to world transformation
    ImageInputStream inStream = null;
    ImageReader reader = null;
    try {
      //
      // get info about the raster we have to read
      //

      // get a stream
      if (cachedStreamSPI == null) {
        cachedStreamSPI = ImageIOExt.getImageInputStreamSPI(granuleUrl, true);
        if (cachedStreamSPI == null) {
          final File file = DataUtilities.urlToFile(granuleUrl);
          if (file != null) {
            if (LOGGER.isLoggable(Level.WARNING)) {
              LOGGER.log(Level.WARNING, Utils.getFileInfo(file));
            }
          }
          throw new IllegalArgumentException(
              "Unable to get an input stream for the provided granule " + granuleUrl.toString());
        }
      }
      assert cachedStreamSPI != null : "no cachedStreamSPI available!";
      inStream =
          cachedStreamSPI.createInputStreamInstance(
              granuleUrl, ImageIO.getUseCache(), ImageIO.getCacheDirectory());
      if (inStream == null) {
        final File file = DataUtilities.urlToFile(granuleUrl);
        if (file != null) {
          if (LOGGER.isLoggable(Level.WARNING)) {
            LOGGER.log(Level.WARNING, Utils.getFileInfo(file));
          }
        }
        throw new IllegalArgumentException(
            "Unable to get an input stream for the provided file " + granuleUrl.toString());
      }

      // get a reader and try to cache the suggested SPI first
      if (cachedReaderSPI == null) {
        inStream.mark();
        if (suggestedSPI != null && suggestedSPI.canDecodeInput(inStream)) {
          cachedReaderSPI = suggestedSPI;
          inStream.reset();
        } else {
          inStream.mark();
          reader = ImageIOExt.getImageioReader(inStream);
          if (reader != null) cachedReaderSPI = reader.getOriginatingProvider();
          inStream.reset();
        }
      }
      reader = cachedReaderSPI.createReaderInstance();
      if (reader == null)
        throw new IllegalArgumentException(
            "Unable to get an ImageReader for the provided file " + granuleUrl.toString());
      reader.setInput(inStream);
      // get selected level and base level dimensions
      final Rectangle originalDimension = Utils.getDimension(0, reader);

      // build the g2W for this tile, in principle we should get it
      // somehow from the tile itself or from the index, but at the moment
      // we do not have such info, hence we assume that it is a simple
      // scale and translate
      final GridToEnvelopeMapper geMapper =
          new GridToEnvelopeMapper(new GridEnvelope2D(originalDimension), granuleBBOX);
      geMapper.setPixelAnchor(
          PixelInCell
              .CELL_CENTER); // this is the default behavior but it is nice to write it down anyway
      this.baseGridToWorld = geMapper.createAffineTransform();

      try {
        if (inclusionGeometry != null) {
          geMapper.setPixelAnchor(PixelInCell.CELL_CORNER);
          Geometry mapped = JTS.transform(inclusionGeometry, geMapper.createTransform().inverse());
          this.granuleROIShape = new ROIGeometry(mapped);
        }

      } catch (TransformException e1) {
        throw new IllegalArgumentException(e1);
      }

      // add the base level
      this.granuleLevels.put(
          Integer.valueOf(0),
          new GranuleOverviewLevelDescriptor(
              1, 1, originalDimension.width, originalDimension.height));

      ////////////////////// Setting overviewController ///////////////////////
      if (heterogeneousGranules) {
        // //
        //
        // Right now we are setting up overviewsController by assuming that
        // overviews are internal images as happens in TIFF images
        // We can improve this by leveraging on coverageReaders
        //
        // //

        // Getting the first level descriptor
        final GranuleOverviewLevelDescriptor baseOverviewLevelDescriptor = granuleLevels.get(0);

        // Variables initialization
        final int numberOfOvervies = reader.getNumImages(true) - 1;
        final AffineTransform2D baseG2W = baseOverviewLevelDescriptor.getGridToWorldTransform();
        final int width = baseOverviewLevelDescriptor.getWidth();
        final int height = baseOverviewLevelDescriptor.getHeight();
        final double resX = AffineTransform2D.getScaleX0(baseG2W);
        final double resY = AffineTransform2D.getScaleY0(baseG2W);
        final double[] highestRes = new double[] {resX, resY};
        final double[][] overviewsResolution = new double[numberOfOvervies][2];

        // Populating overviews and initializing overviewsController
        for (int i = 0; i < numberOfOvervies; i++) {
          overviewsResolution[i][0] = (highestRes[0] * width) / reader.getWidth(i + 1);
          overviewsResolution[i][1] = (highestRes[1] * height) / reader.getWidth(i + 1);
        }
        overviewsController =
            new OverviewsController(highestRes, numberOfOvervies, overviewsResolution);
      }
      //////////////////////////////////////////////////////////////////////////

    } catch (IllegalStateException e) {
      throw new IllegalArgumentException(e);

    } catch (IOException e) {
      throw new IllegalArgumentException(e);
    } finally {
      // close/dispose stream and readers
      try {
        if (inStream != null) {
          inStream.close();
        }
      } catch (Throwable e) {
        throw new IllegalArgumentException(e);
      } finally {
        if (reader != null) {
          reader.dispose();
        }
      }
    }
  }
  /**
   * Tests the {@link OverviewsController} with support for different resolutions/different number
   * of overviews.
   *
   * <p>world_a.tif => Pixel Size = (0.833333333333333,-0.833333333333333); 4 overviews world_b.tif
   * => Pixel Size = (1.406250000000000,-1.406250000000000); 2 overviews
   *
   * @throws IOException
   * @throws MismatchedDimensionException
   * @throws FactoryException
   * @throws TransformException
   */
  @Test
  public void testHeterogeneousGranules()
      throws IOException, MismatchedDimensionException, FactoryException, TransformException {

    final CoordinateReferenceSystem WGS84 = CRS.decode("EPSG:4326", true);
    final ReferencedEnvelope TEST_BBOX_A = new ReferencedEnvelope(-180, 0, -90, 90, WGS84);
    final ReferencedEnvelope TEST_BBOX_B = new ReferencedEnvelope(0, 180, 0, 90, WGS84);

    URL heterogeneousGranulesURL = TestData.url(this, "heterogeneous");
    // //
    //
    // Initialize mosaic variables
    //
    // //
    final Hints hints = new Hints(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, WGS84);
    final AbstractGridFormat format =
        (AbstractGridFormat) GridFormatFinder.findFormat(heterogeneousGranulesURL, hints);
    Assert.assertNotNull(format);
    Assert.assertFalse("UknownFormat", format instanceof UnknownFormat);

    final ImageMosaicReader reader =
        (ImageMosaicReader) format.getReader(heterogeneousGranulesURL, hints);
    Assert.assertNotNull(reader);

    final int nOv = reader.getNumberOfOvervies();
    final double[] hRes = reader.getHighestRes();
    final RasterManager rasterManager = new RasterManager(reader);

    // //
    //
    // Initialize granules related variables
    //
    // //
    final File g1File = new File(DataUtilities.urlToFile(heterogeneousGranulesURL), "world_a.tif");
    final File g2File = new File(DataUtilities.urlToFile(heterogeneousGranulesURL), "world_b.tif");
    final ImageReadParam readParamsG1 = new ImageReadParam();
    final ImageReadParam readParamsG2 = new ImageReadParam();
    int imageIndexG1 = 0;
    int imageIndexG2 = 0;

    final GranuleDescriptor granuleDescriptor1 =
        new GranuleDescriptor(g1File.getAbsolutePath(), TEST_BBOX_A, spi, (Geometry) null, true);
    final GranuleDescriptor granuleDescriptor2 =
        new GranuleDescriptor(g2File.getAbsolutePath(), TEST_BBOX_B, spi, (Geometry) null, true);
    assertNotNull(granuleDescriptor1.toString());
    assertNotNull(granuleDescriptor2.toString());

    final OverviewsController ovControllerG1 = granuleDescriptor1.overviewsController;
    final OverviewsController ovControllerG2 = granuleDescriptor2.overviewsController;

    // //
    //
    // Initializing read request
    //
    // //
    final GeneralEnvelope envelope = reader.getOriginalEnvelope();
    final GridEnvelope originalRange = reader.getOriginalGridRange();
    final Rectangle rasterArea =
        new Rectangle(
            0,
            0,
            (int) Math.ceil(originalRange.getSpan(0) / 9.0),
            (int) Math.ceil(originalRange.getSpan(1) / 9.0));
    final GridEnvelope2D range = new GridEnvelope2D(rasterArea);
    final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(range, envelope);
    geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
    final AffineTransform gridToWorld = geMapper.createAffineTransform();
    final double requestedResolution[] =
        new double[] {
          XAffineTransform.getScaleX0(gridToWorld), XAffineTransform.getScaleY0(gridToWorld)
        };

    TestSet at = null;
    if (nOv == 4 && Math.abs(hRes[0] - 0.833333333333) <= THRESHOLD) {
      at = at1;
    } else if (nOv == 2 && Math.abs(hRes[0] - 1.40625) <= THRESHOLD) {
      at = at2;
    } else {
      return;
    }

    // //
    //
    // Starting OverviewsController tests
    //
    // //
    final OverviewPolicy[] ovPolicies =
        new OverviewPolicy[] {
          OverviewPolicy.QUALITY,
          OverviewPolicy.SPEED,
          OverviewPolicy.NEAREST,
          OverviewPolicy.IGNORE
        };
    for (int i = 0; i < ovPolicies.length; i++) {
      OverviewPolicy ovPolicy = ovPolicies[i];
      LOGGER.info("Testing with OverviewPolicy = " + ovPolicy.toString());
      imageIndexG1 =
          ReadParamsController.setReadParams(
              requestedResolution,
              ovPolicy,
              DecimationPolicy.ALLOW,
              readParamsG1,
              rasterManager,
              ovControllerG1);
      imageIndexG2 =
          ReadParamsController.setReadParams(
              requestedResolution,
              ovPolicy,
              DecimationPolicy.ALLOW,
              readParamsG2,
              rasterManager,
              ovControllerG2);
      assertSame(at.ot[i].g1.imageIndex, imageIndexG1);
      assertSame(at.ot[i].g2.imageIndex, imageIndexG2);
      assertSame(at.ot[i].g1.ssx, readParamsG1.getSourceXSubsampling());
      assertSame(at.ot[i].g1.ssy, readParamsG1.getSourceYSubsampling());
      assertSame(at.ot[i].g2.ssx, readParamsG2.getSourceXSubsampling());
      assertSame(at.ot[i].g2.ssy, readParamsG2.getSourceYSubsampling());
    }
  }
  /**
   * Computes the requested resolution which is going to be used for selecting overviews and or
   * deciding decimation factors on the target coverage.
   *
   * <p>In case the requested envelope is in the same {@link CoordinateReferenceSystem} of the
   * coverage we compute the resolution using the requested {@link MathTransform}. Notice that it
   * must be a {@link LinearTransform} or else we fail.
   *
   * <p>In case the requested envelope is not in the same {@link CoordinateReferenceSystem} of the
   * coverage we
   *
   * @throws DataSourceException in case something bad happens during reprojections and/or
   *     intersections.
   */
  private void computeRequestedResolution() throws DataSourceException {

    try {

      // let's try to get the resolution from the requested gridToWorld
      if (requestedGridToWorld instanceof LinearTransform) {

        //
        // the crs of the request and the one of the coverage are NOT the
        // same and the conversion is not , we can get the resolution from envelope + raster
        // directly
        //
        if (destinationToSourceTransform != null && !destinationToSourceTransform.isIdentity()) {

          //
          // compute the approximated resolution in the request crs, notice that we are
          // assuming a reprojection that keeps the raster area unchanged hence
          // the effect is a degradation of quality, but we might take that into account emprically
          //
          requestedResolution = null;
          //
          // // compute the raster that correspond to the crop bbox at the highest resolution
          // final Rectangle sourceRasterArea = new GeneralGridEnvelope(
          // CRS.transform(
          // PixelTranslation.translate(rasterManager.getRaster2Model(),PixelInCell.CELL_CENTER,PixelInCell.CELL_CORNER).inverse(),
          // cropBBox),PixelInCell.CELL_CORNER,false).toRectangle();
          // XRectangle2D.intersect(sourceRasterArea,
          // rasterManager.spatialDomainManager.coverageRasterArea, sourceRasterArea);
          // if(sourceRasterArea.isEmpty())
          // throw new DataSourceException("The request source raster area is empty");

          final GridToEnvelopeMapper geMapper =
              new GridToEnvelopeMapper(new GridEnvelope2D(destinationRasterArea), cropBBox);
          final AffineTransform tempTransform = geMapper.createAffineTransform();
          // final double scaleX=XAffineTransform.getScaleX0((AffineTransform)
          // requestedGridToWorld)/XAffineTransform.getScaleX0(tempTransform);
          // final double scaleY=XAffineTransform.getScaleY0((AffineTransform)
          // requestedGridToWorld)/XAffineTransform.getScaleY0(tempTransform);
          // //
          // // empiric adjustment to get a finer resolution to have better quality when
          // reprojecting
          // // TODO make it parametric
          // //
          // requestedRasterScaleFactors= new double[2];
          // requestedRasterScaleFactors[0]=scaleX*1.0;
          // requestedRasterScaleFactors[1]=scaleY*1.0;

          requestedResolution =
              new double[] {
                XAffineTransform.getScaleX0(tempTransform),
                XAffineTransform.getScaleY0(tempTransform)
              };

        } else {

          // the crs of the request and the one of the coverage are the
          // same, we can get the resolution from the grid to world
          requestedResolution =
              new double[] {
                XAffineTransform.getScaleX0(requestedGridToWorld),
                XAffineTransform.getScaleY0(requestedGridToWorld)
              };
        }
      } else
        // should not happen
        throw new UnsupportedOperationException(
            Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, requestedGridToWorld.toString()));

      // leave
      return;
    } catch (Throwable e) {
      if (LOGGER.isLoggable(Level.INFO))
        LOGGER.log(Level.INFO, "Unable to compute requested resolution", e);
    }

    //
    // use the coverage resolution since we cannot compute the requested one
    //
    LOGGER.log(Level.WARNING, "Unable to compute requested resolution, using highest available");
    requestedResolution = coverageProperties.fullResolution;
  }