/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011-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.process.raster; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import org.geotools.coverage.CoverageFactoryFinder; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridFormatFinder; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.test.TestData; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; /** * Unit tests for ContourProcess. * * @author Michael Bedward * @since 8.0 * * @source $URL$ * @version $Id$ */ public class ContourProcessTest { private static final double TOL = 1.0e-6; private static final GridCoverageFactory covFactory = CoverageFactoryFinder.getGridCoverageFactory(null); private ContourProcess process; @Before public void setup() throws FileNotFoundException, IOException { process = new ContourProcess(); TestData.unzipFile(this, "contours.zip"); } /** * Creates a coverage with just two rows where values are constant within rows * and differ between rows, then checks for correctly generated single contour * between rows. */ @Test public void singleContourInVerticalGradient() { final int COVERAGE_COLS = 10; final int COVERAGE_ROWS = 2; final double CELL_SIZE = 100; final ReferencedEnvelope WORLD = new ReferencedEnvelope( 1000, 1000 + COVERAGE_COLS * CELL_SIZE, 5000, 5000 + COVERAGE_ROWS * CELL_SIZE, null); final float DATA_MIN = 100; final float DATA_MAX = 200; GridCoverage2D cov = createVerticalGradient( COVERAGE_ROWS, COVERAGE_COLS, WORLD, DATA_MIN, DATA_MAX); final double levelValue = (DATA_MIN + DATA_MAX) / 2; SimpleFeatureCollection fc = process.execute( cov, 0, new double[] {levelValue}, null, null, null, null, null); // Should be a single contour assertEquals(1, fc.size()); SimpleFeatureIterator iter = fc.features(); SimpleFeature feature = null; try { feature = iter.next(); } finally { iter.close(); } // Check contour value Double value = (Double) feature.getAttribute("value"); assertEquals(levelValue, value, TOL); LineString contour = (LineString) feature.getDefaultGeometry(); Coordinate[] coords = contour.getCoordinates(); // Contour should have had co-linear vertices removed by default assertEquals(2, coords.length); // Contour end-point X ordinates should be within half cell-width of // coverage X extrema double minX = Math.min(coords[0].x, coords[1].x); assertEquals(WORLD.getMinX(), minX, CELL_SIZE / 2 + TOL); double maxX = Math.max(coords[0].x, coords[1].x); assertEquals(WORLD.getMaxX(), maxX, CELL_SIZE / 2 + TOL); // Contour Y ordinate should be at mid-Y of coverage and // contour should be horizontal double expectedY = (WORLD.getMinY() + WORLD.getMaxY()) / 2; assertEquals(expectedY, coords[0].y, TOL); assertEquals(expectedY, coords[1].y, TOL); } /** * Tests that the process doesn't blow up when there are no * contours to return */ @Test public void noContours() { // Coverage with values in range [0, 10] GridCoverage2D cov = createVerticalGradient(10, 10, null, 0, 10); // Run process asking for contours at level = 20 SimpleFeatureCollection fc = process.execute(cov, 0, new double[]{20}, null, null, null, null, null); assertNotNull(fc); assertTrue(fc.isEmpty()); } /** * Tests that the process doesn't blow up when the contour process provides invalid lineStrings * * @throws IOException * @throws FileNotFoundException * @throws FactoryException * @throws NoSuchAuthorityCodeException */ @Test public void invalidLinestrings() throws FileNotFoundException, IOException, NoSuchAuthorityCodeException, FactoryException { File input = TestData.file(ContourProcessTest.class, "contoursample.tif"); AbstractGridFormat format = GridFormatFinder.findFormat(input); AbstractGridCoverage2DReader reader = null; boolean success = true; try { reader = format.getReader(input); GridCoverage2D coverage = (GridCoverage2D) reader.read(null); CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326", true); // Run process asking for contours at different levels double[] levels = new double[40]; double start = -2.0; for (int i = 0; i < levels.length; i++) { levels[i] = start + (0.2 * i); } SimpleFeatureCollection fc = process.execute(coverage, 0, levels, null, null, null, null, null); SimpleFeatureIterator features = fc.features(); while (features.hasNext()) { // Apply a set of transformations to the feature geometries to make sure // no exceptions are thrown. (This would happen when dealing with invalid // lineStrings, resulting into IllegalArgumentException) SimpleFeature feature = features.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); ReferencedEnvelope ge = new ReferencedEnvelope(geometry.getEnvelopeInternal(), wgs84); JTS.toGeometry((Envelope) ge); } } catch (IllegalArgumentException iae) { success = false; } finally { if (reader != null) { try { reader.dispose(); } catch (Throwable t) { // Ignore exception on dispose } } } assertTrue(success); } private GridCoverage2D createVerticalGradient( final int dataRows, final int dataCols, ReferencedEnvelope worldEnv, final float startValue, final float endValue) { if (dataRows < 2) { throw new IllegalArgumentException("dataRows must be >= 2"); } if (dataCols < 1) { throw new IllegalArgumentException("dataCols must be positive"); } if (worldEnv == null) { worldEnv = new ReferencedEnvelope(0, dataCols, 0, dataRows, null); } float[][] DATA = new float[dataRows][dataCols]; float delta = (endValue - startValue) / (dataRows - 1); for (int iy = 0; iy < dataRows; iy++) { float value = startValue + iy * delta; for (int ix = 0; ix < dataCols; ix++) { DATA[iy][ix] = value; } value += delta; } return covFactory.create("coverage", DATA, worldEnv); } }