/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2013 - 2016, Open Source Geospatial Foundation (OSGeo) * * 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.gce.imagemosaic; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.awt.RenderingHints; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.coverage.grid.io.footprint.FootprintBehavior; import org.geotools.coverage.grid.io.footprint.MultiLevelROIProviderFactory; import org.geotools.data.DataUtilities; import org.geotools.factory.Hints; import org.geotools.filter.text.ecql.ECQL; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.image.test.ImageAssert; import org.geotools.parameter.DefaultParameterDescriptor; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.test.TestData; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opengis.filter.Filter; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterValue; public class ImageMosaicEgrTest { private static ExecutorService coverageExecutor; private static Hints hints; private File testMosaic; private URL testMosaicUrl; @AfterClass public static void close() { System.clearProperty("org.geotools.referencing.forceXY"); CRS.reset("all"); coverageExecutor.shutdownNow(); } @BeforeClass public static void init() { // make sure CRS ordering is correct CRS.reset("all"); System.setProperty("org.geotools.referencing.forceXY", "true"); // create the executor coverageExecutor = Executors.newCachedThreadPool(); hints = new Hints(new RenderingHints(Hints.EXECUTOR_SERVICE, coverageExecutor)); } @Before public void cleanup() throws IOException { // clean up testMosaic = new File("target", "egrMosaic"); if (testMosaic.exists()) { FileUtils.deleteDirectory(testMosaic); } // create the base mosaic we are going to use File mosaicSource = TestData.file(this, "egr"); FileUtils.copyDirectory(mosaicSource, testMosaic); // System.out.println(testMosaic.getAbsolutePath()); testMosaicUrl = DataUtilities.fileToURL(testMosaic); } private GeneralParameterValue[] getFootprintReadParams(GridCoverage2DReader reader, SimpleEntry... entries) { // activate footprint management List<GeneralParameterValue> params = new ArrayList<>(); ParameterValue<String> footprintManagement = AbstractGridFormat.FOOTPRINT_BEHAVIOR .createValue(); footprintManagement.setValue(FootprintBehavior.Transparent.name()); params.add(footprintManagement); // this prevents us from having problems with link to files still open. ParameterValue<Boolean> jaiImageRead = ImageMosaicFormat.USE_JAI_IMAGEREAD.createValue(); jaiImageRead.setValue(false); params.add(jaiImageRead); // other entries if available boolean foundGridGeometry = false; if (entries != null) { for (SimpleEntry<DefaultParameterDescriptor, Object> entry : entries) { ParameterValue<?> pv = entry.getKey().createValue(); pv.setValue(entry.getValue()); params.add(pv); foundGridGeometry |= entry.getKey().equals(AbstractGridFormat.READ_GRIDGEOMETRY2D); } } // makes the output image small if(!foundGridGeometry) { ParameterValue<GridGeometry2D> geom = AbstractGridFormat.READ_GRIDGEOMETRY2D.createValue(); geom.setValue(new GridGeometry2D(new GridEnvelope2D(0, 0, 300, 300), reader.getOriginalEnvelope())); params.add(geom); } GeneralParameterValue[] result = (GeneralParameterValue[]) params .toArray(new GeneralParameterValue[params.size()]); return result; } private void createRasterFootprintsProperties(File testMosaicRaster) throws FileNotFoundException, IOException { Properties p = new Properties(); // Setting Raster property p.put(MultiLevelROIProviderFactory.SOURCE_PROPERTY, "raster"); FileOutputStream fos = null; try { fos = new FileOutputStream(new File(testMosaicRaster, "footprints.properties")); p.store(fos, null); } finally { IOUtils.closeQuietly(fos); } } private void createVectorFootprintsProperties(File testMosaicRaster) throws FileNotFoundException, IOException { Properties p = new Properties(); // Setting to use a sidecar wkt p.put(MultiLevelROIProviderFactory.SOURCE_PROPERTY, "sidecar"); // the vector is not an exact match, that would result in black artifacts p.put("footprint_inset", "0.01"); FileOutputStream fos = null; try { fos = new FileOutputStream(new File(testMosaicRaster, "footprints.properties")); p.store(fos, null); } finally { IOUtils.closeQuietly(fos); } } @Test public void testAllImagesRaster() throws Exception { createRasterFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-all-desc-raster.png"); testAllImages(sample); } @Test public void testAllImagesVector() throws Exception { createVectorFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-all-desc-vector.png"); testAllImages(sample); } public void testAllImages(File expectedOutput) throws Exception { ImageMosaicReader reader = new ImageMosaicReader(testMosaicUrl, hints); // test with no EGR GeneralParameterValue[] readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D")); testOutputCoverage(reader, readParams, expectedOutput, "3_mid.tiff", "2_right.tiff", "1_left.tiff", "0_large.tiff"); // test with EGR, should be the same, all features are essential readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D"), new SimpleEntry<>( ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI)); testOutputCoverage(reader, readParams, expectedOutput, "3_mid.tiff", "2_right.tiff", "1_left.tiff", "0_large.tiff"); // test with EGR and MT readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D"), new SimpleEntry<>(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI), new SimpleEntry<>(ImageMosaicFormat.ALLOW_MULTITHREADING, true)); testOutputCoverage(reader, readParams, expectedOutput, "3_mid.tiff", "2_right.tiff", "1_left.tiff", "0_large.tiff"); } @Test public void testRedCoversAllRaster() throws Exception { createRasterFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-red-covers-all-raster.png"); testRedCoversAll(sample); } @Test public void testRedCoversAllVector() throws Exception { createVectorFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-red-covers-all-vector.png"); testRedCoversAll(sample); } public void testRedCoversAll(File expectedOutput) throws Exception { ImageMosaicReader reader = new ImageMosaicReader(testMosaicUrl, hints); // test with no EGR GeneralParameterValue[] readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A")); testOutputCoverage(reader, readParams, expectedOutput, "0_large.tiff", "1_left.tiff", "2_right.tiff", "3_mid.tiff"); // test with EGR, should be the same, all features are essential readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A"), new SimpleEntry<>( ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI)); testOutputCoverage(reader, readParams, expectedOutput, "0_large.tiff"); // test with EGR and MT readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A"), new SimpleEntry<>(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI), new SimpleEntry<>(ImageMosaicFormat.ALLOW_MULTITHREADING, true)); testOutputCoverage(reader, readParams, expectedOutput, "0_large.tiff"); } @Test public void testLeftRightOnTopRaster() throws Exception { createRasterFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-left-right-top-raster.png"); testLeftRightOnTop(sample); } @Test public void testLeftRightOnTopVector() throws Exception { createVectorFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-left-right-top-vector.png"); testLeftRightOnTop(sample); } public void testLeftRightOnTop(File expectedOutput) throws Exception { ImageMosaicReader reader = new ImageMosaicReader(testMosaicUrl, hints); // filter out the large granule, the left and right ones will still cover the mid one Filter filter = ECQL.toFilter("z <> 0"); // test with no EGR GeneralParameterValue[] readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A"), new SimpleEntry<>(ImageMosaicFormat.FILTER, filter)); testOutputCoverage(reader, readParams, expectedOutput, "1_left.tiff", "2_right.tiff", "3_mid.tiff"); // test with EGR, should be the same, all features are essential readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A"), new SimpleEntry<>(ImageMosaicFormat.FILTER, filter), new SimpleEntry<>( ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI)); testOutputCoverage(reader, readParams, expectedOutput, "1_left.tiff", "2_right.tiff"); // test with EGR and MT readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z A"), new SimpleEntry<>(ImageMosaicFormat.FILTER, filter), new SimpleEntry<>(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI), new SimpleEntry<>(ImageMosaicFormat.ALLOW_MULTITHREADING, true)); testOutputCoverage(reader, readParams, expectedOutput, "1_left.tiff", "2_right.tiff"); } @Test public void testSingleRaster() throws Exception { createRasterFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-red-rect.png"); testSingle(sample); } @Test public void testSingleVector() throws Exception { createVectorFootprintsProperties(testMosaic); File sample = new File("src/test/resources/org/geotools/gce/imagemosaic/test-data/egr-red-rect.png"); testSingle(sample); } public void testSingle(File expectedOutput) throws Exception { ImageMosaicReader reader = new ImageMosaicReader(testMosaicUrl, hints); // read a small grid geometry that will only catch one feature GridGeometry2D readGeometry = new GridGeometry2D(new GridEnvelope2D(0, 0, 300, 300), new ReferencedEnvelope(-0.667, -0.640, 0.386, 0.412, DefaultGeographicCRS.WGS84)); // test with no EGR GeneralParameterValue[] readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.READ_GRIDGEOMETRY2D, readGeometry), new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D")); testOutputCoverage(reader, readParams, expectedOutput); // test with EGR, should be the same, all features are essential readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.READ_GRIDGEOMETRY2D, readGeometry), new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D"), new SimpleEntry<>( ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI)); testOutputCoverage(reader, readParams, expectedOutput, "0_large.tiff"); // test with EGR and MT readParams = getFootprintReadParams(reader, new SimpleEntry<>(ImageMosaicFormat.READ_GRIDGEOMETRY2D, readGeometry), new SimpleEntry<>(ImageMosaicFormat.SORT_BY, "z D"), new SimpleEntry<>(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL, ExcessGranulePolicy.ROI), new SimpleEntry<>(ImageMosaicFormat.ALLOW_MULTITHREADING, true)); testOutputCoverage(reader, readParams, expectedOutput, "0_large.tiff"); } private void testOutputCoverage(ImageMosaicReader reader, GeneralParameterValue[] readParams, File expectedOutput, String... expectedImages) throws IOException { GridCoverage2D coverage = reader.read(readParams); assertSourceFileNames(coverage, expectedImages); ImageAssert.assertEquals(expectedOutput, coverage.getRenderedImage(), 300); } private void assertSourceFileNames(GridCoverage2D coverage, String... expectedNamesArray) { String sources = (String) coverage.getProperty(GridCoverage2DReader.FILE_SOURCE_PROPERTY); if (expectedNamesArray != null && expectedNamesArray.length > 0) { String[] names = sources.split("\\s*,\\s*"); LinkedHashSet<String> actualNames = new LinkedHashSet<>(); for (String name : names) { actualNames.add(new File(name).getName()); } LinkedHashSet<String> expectedNames = new LinkedHashSet<>( Arrays.asList(expectedNamesArray)); assertEquals(expectedNames, actualNames); } } }