/** * 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; } }
/** * 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()); } }
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(); } } } }
/** * 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; }