/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014-2015, Open Source Geospatial Foundation (OSGeo) * (C) 2014 TOPP - www.openplans.org. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.process.raster; import static org.junit.Assert.assertEquals; import it.geosolutions.jaiext.JAIExt; import java.awt.Rectangle; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridFormatFinder; import org.geotools.geometry.Envelope2D; import org.geotools.geometry.jts.JTS; import org.geotools.image.ImageWorker; import org.geotools.image.jai.Registry; import org.geotools.resources.coverage.CoverageUtilities; import org.geotools.test.TestData; import it.geosolutions.jaiext.vectorbin.ROIGeometry; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.opengis.coverage.grid.GridCoverageReader; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransform2D; import org.opengis.referencing.operation.TransformException; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; /** * This class is used for testing the processes operating on the bands: BandMerge and BandSelect. * * @author Nicola Lagomarsini, GeoSolutions S.A.S. * */ public class BandProcessTest { /** Tolerance value for the double comparison */ private static final double TOLERANCE = 0.1d; /** First coverage to use */ private static GridCoverage2D coverage1; /** Second coverage to use, equal to the first coverage */ private static GridCoverage2D coverage2; /** Third coverage to use, which equal to the first one but also translated on the X direction */ private static GridCoverage2D coverage3; @BeforeClass public static void setup() throws FileNotFoundException, IOException, NoninvertibleTransformException { JAIExt.initJAIEXT(true, true); // Disable medialib System.setProperty("com.sun.media.jai.disableMediaLib", "true"); // Disable bandmerge and mosaic native operation Registry.setNativeAccelerationAllowed("BandMerge", false); Registry.setNativeAccelerationAllowed("Mosaic", false); // First file selection File input = TestData.file(BandProcessTest.class, "sample.tif"); AbstractGridFormat format = GridFormatFinder.findFormat(input); // Get a reader for the selected format GridCoverageReader reader = format.getReader(input); // Read the input Coverage coverage1 = (GridCoverage2D) reader.read(null); // Reader disposal reader.dispose(); // Second File selection coverage2 = coverage1; // Third file selection (This file is similar to the first one but it is translated by 1 on the X direction) File input2 = TestData.file(BandProcessTest.class, "sample2.tif"); format = GridFormatFinder.findFormat(input2); // Get a reader for the selected format reader = format.getReader(input2); // Read the input Coverage coverage3 = (GridCoverage2D) reader.read(null); // Reader disposal reader.dispose(); } @AfterClass public static void afterClass() { // Enable medialib System.setProperty("com.sun.media.jai.disableMediaLib", "false"); // Enable bandmerge and mosaic native operation Registry.setNativeAccelerationAllowed("BandMerge", true); Registry.setNativeAccelerationAllowed("Mosaic", true); JAIExt.initJAIEXT(false, true); } // Ensure that the merging and selecting two equal images returns the same images @Test public void testEqualImages() { // Creation of a GridCoverage List List<GridCoverage2D> coverages = new ArrayList<GridCoverage2D>(); coverages.add(coverage1); coverages.add(coverage2); // /////////////// // // BANDMERGE STEP // // /////////////// // Call of the bandmerge process BandMergeProcess bandmerge = new BandMergeProcess(); // Execution of the process (used Last coverage g2w transformation) GridCoverage2D merged = bandmerge.execute(coverages, null, "last", null); // Checks on the new Coverage Assert.assertEquals(2, merged.getNumSampleDimensions()); // Input Coverage BBOX Envelope2D sourceEnv = coverage1.getEnvelope2D(); // Merged Coverage BBOX Envelope2D mergedEnv = merged.getEnvelope2D(); // Ensure same BBOX assertEqualBBOX(sourceEnv, mergedEnv); // Selection of the Images associated to each coverage RenderedImage mergedImg = merged.getRenderedImage(); RenderedImage srcImg1 = coverage1.getRenderedImage(); RenderedImage srcImg2 = coverage2.getRenderedImage(); // Ensure they have the same dimensions assertEqualImageDim(mergedImg, srcImg1); assertEqualImageDim(mergedImg, srcImg2); // /////////////// // // BANDSELECT STEP // // /////////////// // Call of the bandselect process BandSelectProcess bandselect = new BandSelectProcess(); // Selection of the first coverage GridCoverage2D selected1 = bandselect.execute(merged, new int[] { 0 }, null); // Selection of the second coverage GridCoverage2D selected2 = bandselect.execute(merged, new int[] { 1 }, null); // Check the sample dimensions number assertEquals(1, selected1.getNumSampleDimensions()); assertEquals(1, selected2.getNumSampleDimensions()); // Check the BBOX assertEqualBBOX(mergedEnv, selected1.getEnvelope2D()); assertEqualBBOX(mergedEnv, selected2.getEnvelope2D()); // Ensure they have the same dimensions assertEqualImageDim(mergedImg, selected1.getRenderedImage()); assertEqualImageDim(mergedImg, selected2.getRenderedImage()); // Ensure that the images are equals to the source ensureEqualImages(srcImg1, selected1.getRenderedImage()); ensureEqualImages(srcImg2, selected2.getRenderedImage()); } // Ensure that the merging and selecting two equal images with ROI returns the same images // with nodata outside the ROI @Test public void testROI() throws IOException, MismatchedDimensionException, TransformException { // Creation of a GridCoverage List List<GridCoverage2D> coverages = new ArrayList<GridCoverage2D>(); coverages.add(coverage1); coverages.add(coverage2); // Creation of a Geometry to use as ROI Geometry geo = createGeometry(coverage1); // /////////////// // // BANDMERGE STEP // // /////////////// // Call of the bandmerge process BandMergeProcess bandmerge = new BandMergeProcess(); // Execution of the process GridCoverage2D merged = bandmerge.execute(coverages, geo, null, null); // Checks on the new Coverage Assert.assertEquals(2, merged.getNumSampleDimensions()); // Input Coverage BBOX Envelope2D sourceEnv = coverage1.getEnvelope2D(); // Merged Coverage BBOX Envelope2D mergedEnv = merged.getEnvelope2D(); // Ensure same BBOX assertEqualBBOX(sourceEnv, mergedEnv); // Selection of the Images associated to each coverage RenderedImage mergedImg = merged.getRenderedImage(); RenderedImage srcImg1 = coverage1.getRenderedImage(); RenderedImage srcImg2 = coverage2.getRenderedImage(); // Ensure they have the same dimensions assertEqualImageDim(mergedImg, srcImg1); assertEqualImageDim(mergedImg, srcImg2); // /////////////// // // BANDSELECT STEP // // /////////////// // Call of the bandselect process BandSelectProcess bandselect = new BandSelectProcess(); // Selection of the first coverage GridCoverage2D selected1 = bandselect.execute(merged, new int[] { 0 }, null); // Selection of the second coverage GridCoverage2D selected2 = bandselect.execute(merged, new int[] { 1 }, null); // Check the sample dimensions number assertEquals(1, selected1.getNumSampleDimensions()); assertEquals(1, selected2.getNumSampleDimensions()); // Check the BBOX assertEqualBBOX(mergedEnv, selected1.getEnvelope2D()); assertEqualBBOX(mergedEnv, selected2.getEnvelope2D()); // Ensure they have the same dimensions assertEqualImageDim(mergedImg, selected1.getRenderedImage()); assertEqualImageDim(mergedImg, selected2.getRenderedImage()); // Ensure equal images inside ROI. This requires cropping the images // World to grid transform for mapping the ROI in the Raster apsce MathTransform2D tr = coverage1.getGridGeometry().getCRSToGrid2D(PixelOrientation.UPPER_LEFT); // ROI object inthe Raster Space ROI roi = new ROIGeometry(JTS.transform(geo, tr)); // Coverage Crop for the final coverages CropCoverage crop = new CropCoverage(); RenderedImage cropSel1 = crop.execute(selected1, geo, null).getRenderedImage(); RenderedImage cropSel2 = crop.execute(selected2, geo, null).getRenderedImage(); // Ensure that the images are equals on the cropped selection (The new images contains no data outside of the selection) ensureEqualImages(selected1.getRenderedImage(), cropSel1); ensureEqualImages(selected2.getRenderedImage(), cropSel2); // Ensure that the images contain No Data outside of the ROI bounds ensureNoDataOutside(selected1, geo); ensureNoDataOutside(selected2, geo); } // Ensure that the merging and selecting two images where one of them is translated, returns the two images, each of the placed // by taking into account their position inside the global image envelope @Test public void testDifferentImages() { // Creation of a GridCoverage List List<GridCoverage2D> coverages = new ArrayList<GridCoverage2D>(); coverages.add(coverage1); coverages.add(coverage3); // /////////////// // // BANDMERGE STEP // // /////////////// // Call of the bandmerge process BandMergeProcess bandmerge = new BandMergeProcess(); // Execution of the process GridCoverage2D merged = bandmerge.execute(coverages, null, null, null); // Checks on the new Coverage Assert.assertEquals(2, merged.getNumSampleDimensions()); // Ensure that the final Envelope is expanded Envelope2D cov1Env = coverage1.getEnvelope2D(); Envelope2D cov3Env = coverage3.getEnvelope2D(); // Global coverage creation Envelope2D globalEnv = new Envelope2D(cov1Env); globalEnv.include(cov3Env); assertEqualBBOX(globalEnv, merged.getEnvelope2D()); // /////////////// // // BANDSELECT STEP // // /////////////// // Call of the bandselect process BandSelectProcess bandselect = new BandSelectProcess(); // Selection of the first coverage GridCoverage2D selected1 = bandselect.execute(merged, new int[] { 0 }, null); // Selection of the second coverage GridCoverage2D selected2 = bandselect.execute(merged, new int[] { 1 }, null); // Check the sample dimensions number assertEquals(1, selected1.getNumSampleDimensions()); assertEquals(1, selected2.getNumSampleDimensions()); // Check the BBOX assertEqualBBOX(globalEnv, selected1.getEnvelope2D()); assertEqualBBOX(globalEnv, selected2.getEnvelope2D()); // Initial image RenderedImage srcImg = coverage1.getRenderedImage(); // Final images RenderedImage sel1 = selected1.getRenderedImage(); RenderedImage sel2 = selected2.getRenderedImage(); // Since we know that the first image is on the left side of the envelope and the // second on the right side, we crop and translate the images in order to // compare the initial image with them. // First image requires only cropping since it is on the right position ImageWorker w1 = new ImageWorker(sel1); RenderedOp crop1 = w1.crop(0f, 0f, (float) srcImg.getWidth(), (float) srcImg.getHeight()) .getRenderedOperation(); // Minimum float minX = sel1.getWidth() - srcImg.getWidth(); float minY = sel2.getHeight() - srcImg.getHeight(); // Cropping + Translation of the second image ImageWorker w2 = new ImageWorker(sel2); RenderedOp crop2 = w2 .crop(minX, minY, (float) srcImg.getWidth(), (float) srcImg.getHeight()) .translate(-minX, -minY, null).getRenderedOperation(); // Final check on the images ensureEqualImages(srcImg, crop1); ensureEqualImages(srcImg, crop2); } /** * Ensure that the input Images have the same dimensions * * @param mergedImg * @param srcImg1 */ private void assertEqualImageDim(RenderedImage mergedImg, RenderedImage srcImg1) { Assert.assertEquals(mergedImg.getMinX(), srcImg1.getMinX()); Assert.assertEquals(mergedImg.getMinY(), srcImg1.getMinY()); Assert.assertEquals(mergedImg.getWidth(), srcImg1.getWidth()); Assert.assertEquals(mergedImg.getHeight(), srcImg1.getHeight()); } /** * Method for checking that two bounding box are equals * * @param sourceEnv * @param dstEnv */ private void assertEqualBBOX(Envelope2D sourceEnv, Envelope2D dstEnv) { double srcX = sourceEnv.x; double srcY = sourceEnv.y; double srcW = sourceEnv.width; double srcH = sourceEnv.height; double dstX = dstEnv.x; double dstY = dstEnv.y; double dstW = dstEnv.width; double dstH = dstEnv.height; Assert.assertEquals(srcX, dstX, TOLERANCE); Assert.assertEquals(srcY, dstY, TOLERANCE); Assert.assertEquals(srcW, dstW, TOLERANCE); Assert.assertEquals(srcH, dstH, TOLERANCE); } /** * Method for checking that two images are equal * * @param source0 * @param source1 */ private void ensureEqualImages(RenderedImage source0, RenderedImage source1) { ImageWorker w = new ImageWorker(source0); // Subtraction between the two images w.subtract(source1); // Calculation of the mean value of the subtraction operation // Get the mean double mean = w.getMean()[0]; // Check that the mean value is 0 assertEquals(0, mean, TOLERANCE); } /** * Method for creating the ROI to test * * @param coverage * @return */ private Geometry createGeometry(GridCoverage2D coverage) { // Selection of the Envelope associated to the Coverage Envelope2D envelope = coverage.getEnvelope2D(); // Geometry creation GeometryFactory fact = new GeometryFactory(); Coordinate[] coordinates = new Coordinate[5]; // Populating the Coordinate array in order to create the Polygon for (int i = 0; i < coordinates.length; i++) { switch (i) { case 0: case 4: coordinates[i] = new Coordinate(envelope.getMinX(), envelope.getMinY()); break; case 1: coordinates[i] = new Coordinate(envelope.getMinX(), envelope.getMinY() + envelope.getHeight() / 2); break; case 2: coordinates[i] = new Coordinate(envelope.getMinX() + envelope.getWidth() / 2, envelope.getMinY() + envelope.getHeight() / 2); break; case 3: coordinates[i] = new Coordinate(envelope.getMinX() + envelope.getWidth() / 2, envelope.getMinY()); break; } } // polygon creation from the coordinate array Polygon poly = fact.createPolygon(coordinates); // Return feature return poly; } /** * Method for checking if the coverage values outside ROI are NoData * * @param coverage * @param feature * @throws MismatchedDimensionException * @throws TransformException */ private void ensureNoDataOutside(GridCoverage2D coverage, Geometry geom) throws MismatchedDimensionException, TransformException { // World to Grid transform used to project the Geometry to the RasterSpace MathTransform w2g = coverage.getGridGeometry().getCRSToGrid2D(PixelOrientation.UPPER_LEFT); // ROI in raster space ROI roi = new ROIGeometry(JTS.transform(geom, w2g)); // Approximate bounds by expanding them by one (coordinates are taken with a 0.5 offset) Rectangle rect = roi.getBounds(); rect.grow(1, 1); // No data value to use double nodata = CoverageUtilities.getBackgroundValues(coverage)[0]; // Cycle on the image Bounds in order to search if No Data are present RenderedImage img = coverage.getRenderedImage(); // Tile bounds int minTileX = img.getMinTileX(); int minTileY = img.getMinTileY(); int maxTileX = minTileX + img.getNumXTiles(); int maxTileY = minTileY + img.getNumYTiles(); // Cycle on the tiles for (int tx = minTileX; tx < maxTileX; tx++) { for (int ty = minTileY; ty < maxTileY; ty++) { Raster tile = img.getTile(tx, ty); // Cycle on the tile values int minx = tile.getMinX(); int miny = tile.getMinY(); int maxx = Math.min(minx + tile.getWidth(), img.getMinX() + img.getWidth() - tx * img.getTileWidth()); int maxy = Math.min(miny + tile.getHeight(), img.getMinY() + img.getHeight() - tx * img.getTileHeight()); // Check each pixel outside the ROI for (int x = minx; x < maxx; x++) { for (int y = miny; y < maxy; y++) { if (!rect.contains(x, y)) { assertEquals(nodata, tile.getSampleDouble(x, y, 0), TOLERANCE); } } } } } } }