/** * Performs a translation using the "Resample" operation. * * @param grid the {@link GridCoverage2D} to apply the translation on. * @throws NoninvertibleTransformException If a "grid to CRS" transform is not invertible. */ private void doTranslation(GridCoverage2D grid) throws NoninvertibleTransformException { final int transX = -253; final int transY = -456; final double scaleX = 0.04; final double scaleY = -0.04; final ParameterBlock block = new ParameterBlock() .addSource(grid.getRenderedImage()) .add((float) transX) .add((float) transY); RenderedImage image = JAI.create("Translate", block); assertEquals("Incorrect X translation", transX, image.getMinX()); assertEquals("Incorrect Y translation", transY, image.getMinY()); /* * Create a grid coverage from the translated image but with the same envelope. * Consequently, the 'gridToCoordinateSystem' should be translated by the same * amount, with the opposite sign. */ AffineTransform expected = getAffineTransform(grid); assertNotNull(expected); expected = new AffineTransform(expected); // Get a mutable instance. final GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null); grid = factory.create( "Translated", image, grid.getEnvelope(), grid.getSampleDimensions(), new GridCoverage2D[] {grid}, grid.getProperties()); expected.translate(-transX, -transY); assertTransformEquals(expected, getAffineTransform(grid)); /* * Apply the "Resample" operation with a specific 'gridToCoordinateSystem' transform. * The envelope is left unchanged. The "Resample" operation should compute automatically * new image bounds. */ final AffineTransform at = AffineTransform.getScaleInstance(scaleX, scaleY); final MathTransform tr = ProjectiveTransform.create(at); // account for the half pixel correction between the two spaces since we are talking raster here // but the resample will talk model! final MathTransform correctedTransform = PixelTranslation.translate(tr, PixelInCell.CELL_CORNER, PixelInCell.CELL_CENTER); final GridGeometry2D geometry = new GridGeometry2D(null, correctedTransform, null); final GridCoverage2D newGrid = (GridCoverage2D) Operations.DEFAULT.resample(grid, grid.getCoordinateReferenceSystem(), geometry, null); assertEquals(correctedTransform, getAffineTransform(newGrid)); image = newGrid.getRenderedImage(); expected.preConcatenate(at.createInverse()); final Point point = new Point(transX, transY); assertSame(point, expected.transform(point, point)); // Round toward neareast integer }
@Test public void testDomainSubsetRxRy() throws Exception { // get base coverage final GridCoverage baseCoverage = catalog.getCoverageByName(TASMANIA_BM.getLocalPart()).getGridCoverage(null, null); final AffineTransform2D expectedTx = (AffineTransform2D) baseCoverage.getGridGeometry().getGridToCRS(); final GeneralEnvelope originalEnvelope = (GeneralEnvelope) baseCoverage.getEnvelope(); final GeneralEnvelope newEnvelope = new GeneralEnvelope(originalEnvelope); newEnvelope.setEnvelope( originalEnvelope.getMinimum(0), originalEnvelope.getMaximum(1) - originalEnvelope.getSpan(1) / 2, originalEnvelope.getMinimum(0) + originalEnvelope.getSpan(0) / 2, originalEnvelope.getMaximum(1)); final MathTransform cornerWorldToGrid = PixelTranslation.translate(expectedTx, PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER); final GeneralGridEnvelope expectedGridEnvelope = new GeneralGridEnvelope( CRS.transform(cornerWorldToGrid.inverse(), newEnvelope), PixelInCell.CELL_CORNER, false); final StringBuilder envelopeBuilder = new StringBuilder(); envelopeBuilder.append(newEnvelope.getMinimum(0)).append(","); envelopeBuilder.append(newEnvelope.getMinimum(1)).append(","); envelopeBuilder.append(newEnvelope.getMaximum(0)).append(","); envelopeBuilder.append(newEnvelope.getMaximum(1)); Map<String, Object> raw = baseMap(); final String layerID = getLayerId(TASMANIA_BM); raw.put("sourcecoverage", layerID); raw.put("version", "1.0.0"); raw.put("format", "image/geotiff"); raw.put("BBox", envelopeBuilder.toString()); raw.put("crs", "EPSG:4326"); raw.put("resx", Double.toString(expectedTx.getScaleX())); raw.put("resy", Double.toString(Math.abs(expectedTx.getScaleY()))); final GridCoverage[] coverages = executeGetCoverageKvp(raw); final GridCoverage2D result = (GridCoverage2D) coverages[0]; assertTrue(coverages.length == 1); final AffineTransform2D tx = (AffineTransform2D) result.getGridGeometry().getGridToCRS(); assertEquals("resx", expectedTx.getScaleX(), tx.getScaleX(), 1E-6); assertEquals("resx", Math.abs(expectedTx.getScaleY()), Math.abs(tx.getScaleY()), 1E-6); final GridEnvelope gridEnvelope = result.getGridGeometry().getGridRange(); assertEquals("w", 180, gridEnvelope.getSpan(0)); assertEquals("h", 180, gridEnvelope.getSpan(1)); assertEquals("grid envelope", expectedGridEnvelope, gridEnvelope); // dispose CoverageCleanerCallback.disposeCoverage(baseCoverage); CoverageCleanerCallback.disposeCoverage(coverages[0]); }
private void inspectCoordinateReferenceSystems() throws DataSourceException { // get the crs for the requested bbox requestCRS = CRS.getHorizontalCRS(requestedBBox.getCoordinateReferenceSystem()); // // Check if the request CRS is different from the coverage native CRS // if (!CRS.equalsIgnoreMetadata(requestCRS, coverageProperties.crs2D)) try { destinationToSourceTransform = CRS.findMathTransform(requestCRS, coverageProperties.crs2D, true); } catch (FactoryException e) { throw new DataSourceException("Unable to inspect request CRS", e); } // now transform the requested envelope to source crs if (destinationToSourceTransform != null && destinationToSourceTransform.isIdentity()) { destinationToSourceTransform = null; // the CRS is basically the same } else if (destinationToSourceTransform instanceof AffineTransform) { needsReprojection = true; // // k, the transformation between the various CRS is not null or the // Identity, let's see if it is an affine transform, which case we // can incorporate it into the requested grid to world // // we should not have any problems with regards to BBOX reprojection // update the requested grid to world transformation by pre concatenating the destination to // source transform AffineTransform mutableTransform = (AffineTransform) requestedGridToWorld.clone(); mutableTransform.preConcatenate((AffineTransform) destinationToSourceTransform); // update the requested envelope try { final MathTransform tempTransform = PixelTranslation.translate( ProjectiveTransform.create(mutableTransform), PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER); requestedBBox = new ReferencedEnvelope( CRS.transform(tempTransform, new GeneralEnvelope(requestedRasterArea))); } catch (MismatchedDimensionException e) { throw new DataSourceException("Unable to inspect request CRS", e); } catch (TransformException e) { throw new DataSourceException("Unable to inspect request CRS", e); } // now clean up all the traces of the transformations destinationToSourceTransform = null; } }
/** * Checks whether a world file is associated with the data source. If found, set a proper * envelope. * * @throws IllegalStateException * @throws IOException */ protected void parseWorldFile() { final String worldFilePath = new StringBuffer(this.parentPath) .append(GridCoverageUtilities.SEPARATOR) .append(coverageName) .toString(); File file2Parse = null; boolean worldFileExists = false; // // // // Check for a world file with the format specific extension // // // if (worldFileExt != null && worldFileExt.length() > 0) { file2Parse = new File(worldFilePath + worldFileExt); worldFileExists = file2Parse.exists(); } // // // // Check for a world file with the default extension // // // if (!worldFileExists) { file2Parse = new File(worldFilePath + GridCoverageUtilities.DEFAULT_WORLDFILE_EXT); worldFileExists = file2Parse.exists(); } if (worldFileExists) { try { final WorldFileReader reader = new WorldFileReader(file2Parse); raster2Model = reader.getTransform(); // // // // In case we read from a real world file we have together the // envelope. World file transformation assumes to work in the // CELL_CENTER condition // // // MathTransform tempTransform = PixelTranslation.translate( raster2Model, PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER); final Envelope gridRange = new GeneralEnvelope((GridEnvelope2D) originalGridRange); final GeneralEnvelope coverageEnvelope = CRS.transform(tempTransform, gridRange); originalEnvelope = coverageEnvelope; return; } catch (TransformException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } catch (IllegalStateException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } catch (IOException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); } } } // if we got here we did not find a suitable transform raster2Model = null; }
/** * Return a crop region from a specified envelope, leveraging on the grid to world transformation. * * @param refinedRequestedBBox the crop envelope * @return a {@code Rectangle} representing the crop region. * @throws TransformException in case a problem occurs when going back to raster space. * @throws DataSourceException */ private void computeCropRasterArea() throws DataSourceException { // we have nothing to crop if (cropBBox == null) { destinationRasterArea = null; return; } // // We need to invert the requested gridToWorld and then adjust the requested raster area are // accordingly // // invert the requested grid to world keeping into account the fact that it is related to cell // center // while the raster is related to cell corner MathTransform2D requestedWorldToGrid; try { requestedWorldToGrid = (MathTransform2D) PixelTranslation.translate( ProjectiveTransform.create(requestedGridToWorld), PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER) .inverse(); } catch (NoninvertibleTransformException e) { throw new DataSourceException(e); } if (destinationToSourceTransform == null || destinationToSourceTransform.isIdentity()) { // now get the requested bbox which have been already adjusted and project it back to raster // space try { destinationRasterArea = new GeneralGridEnvelope( CRS.transform(requestedWorldToGrid, new GeneralEnvelope(cropBBox)), PixelInCell.CELL_CORNER, false) .toRectangle(); } catch (IllegalStateException e) { throw new DataSourceException(e); } catch (TransformException e) { throw new DataSourceException(e); } } else { // // reproject the crop bbox back and then crop, notice that we are imposing // try { final GeneralEnvelope cropBBOXInRequestCRS = CRS.transform(cropBBox, requestedBBox.getCoordinateReferenceSystem()); cropBBOXInRequestCRS.setCoordinateReferenceSystem( requestedBBox.getCoordinateReferenceSystem()); // make sure it falls within the requested envelope cropBBOXInRequestCRS.intersect(requestedBBox); // now go back to raster space destinationRasterArea = new GeneralGridEnvelope( CRS.transform(requestedWorldToGrid, cropBBOXInRequestCRS), PixelInCell.CELL_CORNER, false) .toRectangle(); // intersect with the original requested raster space to be sure that we stay within the // requested raster area XRectangle2D.intersect(destinationRasterArea, requestedRasterArea, destinationRasterArea); } catch (NoninvertibleTransformException e) { throw new DataSourceException(e); } catch (TransformException e) { throw new DataSourceException(e); } } // is it empty?? if (destinationRasterArea.isEmpty()) { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log( Level.FINE, "Requested envelope too small resulting in empty cropped raster region. cropBbox:" + cropBBox); // TODO: Future versions may define a 1x1 rectangle starting // from the lower coordinate empty = true; return; } }