Package org.geotools.coverage.processing

Source Code of org.geotools.coverage.processing.MosaicTest

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2014, 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.coverage.processing;

import it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReader;
import it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReaderSpi;

import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.imageio.ImageIO;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;

import org.geotools.TestData;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.processing.operation.Mosaic;
import org.geotools.coverage.processing.operation.Mosaic.GridGeometryPolicy;
import org.geotools.data.WorldFileReader;
import org.geotools.factory.GeoTools;
import org.geotools.factory.Hints;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.resources.coverage.CoverageUtilities;
import org.geotools.resources.image.ImageUtilities;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opengis.geometry.DirectPosition;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.parameter.InvalidParameterValueException;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;

/**
* This class tests the {@link Mosaic} operation. The tests ensures that the final {@link GridCoverage2D} created contains the union of the input
* bounding box or is equal to that provided by the external {@link GridGeometry2D}. Also the tests check if the output {@link GridCoverage2D}
* resolution is equal to that imposed in input with the {@link GridGeometryPolicy}.
*
*
* @author Nicola Lagomarsini GesoSolutions S.A.S.
*
*/
public class MosaicTest extends GridProcessingTestBase {

    /** Tolerance value for the double comparison */
    private static final double TOLERANCE = 0.01d;

    /**
     * WKT used for testing that the operation throws an exception when the input {@link GridCoverage2D}s does not have the same
     * {@link CoordinateReferenceSystem}.
     */
    private final static String GOOGLE_MERCATOR_WKT = "PROJCS[\"WGS 84 / Pseudo-Mercator\","
            + "GEOGCS[\"Popular Visualisation CRS\"," + "DATUM[\"Popular_Visualisation_Datum\","
            + "SPHEROID[\"Popular Visualisation Sphere\",6378137,0,"
            + "AUTHORITY[\"EPSG\",\"7059\"]]," + "TOWGS84[0,0,0,0,0,0,0],"
            + "AUTHORITY[\"EPSG\",\"6055\"]]," + "PRIMEM[\"Greenwich\",0,"
            + "AUTHORITY[\"EPSG\",\"8901\"]]," + "UNIT[\"degree\",0.01745329251994328,"
            + "AUTHORITY[\"EPSG\",\"9122\"]]," + "AUTHORITY[\"EPSG\",\"4055\"]],"
            + "UNIT[\"metre\",1," + "AUTHORITY[\"EPSG\",\"9001\"]],"
            + "PROJECTION[\"Mercator_1SP\"]," + "PARAMETER[\"central_meridian\",0],"
            + "PARAMETER[\"scale_factor\",1]," + "PARAMETER[\"false_easting\",0],"
            + "PARAMETER[\"false_northing\",0]," + "AUTHORITY[\"EPSG\",\"3785\"],"
            + "AXIS[\"X\",EAST]," + "AXIS[\"Y\",NORTH]]";

    /** First Coverage used */
    private static GridCoverage2D coverage1;

    /** Second Coverage used. It is equal to the first one but translated on the X axis. */
    private static GridCoverage2D coverage2;

    /** Third Coverage used. It is equal to the first one but contains an alpha band */
    private static GridCoverage2D coverage3;

    /** Fourth Coverage used. It is equal to the second one but contains an alpha band */
    private static GridCoverage2D coverage4;

    /**
     * The processor to be used for all tests.
     */
    private static final CoverageProcessor processor = CoverageProcessor.getInstance(GeoTools
            .getDefaultHints());

    private static final Mosaic MOSAIC = (Mosaic) processor.getOperation("Mosaic");

    // Static method used for preparing the input data.
    @BeforeClass
    public static void setup() throws FileNotFoundException, IOException {
        TestData.unzipFile(MosaicTest.class, "sampleData.zip");
        coverage1 = readInputFile("sampleData");
        coverage2 = readInputFile("sampleData2");
        coverage3 = readInputFile("sampleData3");
        coverage4 = readInputFile("sampleData4");
    }

    /**
     * Private method for reading the input file.
     *
     * @param filename
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static GridCoverage2D readInputFile(String filename) throws FileNotFoundException,
            IOException {
        final File tiff = TestData.file(MosaicTest.class, filename + ".tif");
        final File tfw = TestData.file(MosaicTest.class, filename + ".tfw");

        final TIFFImageReader reader = (it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReader) new TIFFImageReaderSpi()
                .createReaderInstance();
        reader.setInput(ImageIO.createImageInputStream(tiff));
        final BufferedImage image = reader.read(0);
        final MathTransform transform = new WorldFileReader(tfw).getTransform();
        final GridCoverage2D coverage2D = CoverageFactoryFinder.getGridCoverageFactory(null)
                .create("coverage" + filename,
                        image,
                        new GridGeometry2D(new GridEnvelope2D(PlanarImage.wrapRenderedImage(image)
                                .getBounds()), transform, DefaultGeographicCRS.WGS84), null, null,
                        null);
        return coverage2D;
    }

    // Simple test which mosaics two input coverages without any additional parameter
    @Test
    public void testMosaicSimple() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);
        sources.add(coverage2);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        // RenderingHints
        Hints hints = new Hints();
        // Ensure No Layout is set
        Assert.assertTrue(!hints.containsKey(JAI.KEY_IMAGE_LAYOUT));
        // Add a fake Layout for the operation
        ImageLayout il = new ImageLayout();
        hints.put(JAI.KEY_IMAGE_LAYOUT, il);
        // Mosaic operation
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param, hints);

        // Check that the final GridCoverage BoundingBox is equal to the union of the separate coverages bounding box
        Envelope2D expected = coverage1.getEnvelope2D();
        expected.include(coverage2.getEnvelope2D());
        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the first coverage
        double initialRes = calculateResolution(coverage1);
        double finalRes = calculateResolution(mosaic);
        double percentual = Math.abs(initialRes - finalRes) / initialRes;
        Assert.assertTrue(percentual < TOLERANCE);

        // Check that on the center of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getCenterX(), actual.getCenterY());
        double nodata = CoverageUtilities.getBackgroundValues(coverage1)[0];
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Ensure the Layout is already present after the mosaic
        Assert.assertTrue(hints.containsKey(JAI.KEY_IMAGE_LAYOUT));
        // Ensure no additional bound parameter is set
        ImageLayout layout = (ImageLayout) hints.get(JAI.KEY_IMAGE_LAYOUT);
        Assert.assertTrue(!layout.isValid(ImageLayout.MIN_X_MASK));
        Assert.assertTrue(!layout.isValid(ImageLayout.MIN_Y_MASK));
        Assert.assertTrue(!layout.isValid(ImageLayout.WIDTH_MASK));
        Assert.assertTrue(!layout.isValid(ImageLayout.HEIGHT_MASK));

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    // Simple test which mosaics two input coverages with a different value for the output nodata
    @Test
    public void testMosaicWithAnotherNoData() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);
        sources.add(coverage2);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        // Setting of the nodata
        double nodata = -9999;
        param.parameter(Mosaic.OUTNODATA_NAME).setValue(new double[] { nodata });
        // Mosaic
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param);

        // Check that the final GridCoverage BoundingBox is equal to the union of the separate coverages bounding box
        Envelope2D expected = coverage1.getEnvelope2D();
        expected.include(coverage2.getEnvelope2D());
        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the first coverage
        double initialRes = calculateResolution(coverage1);
        double finalRes = calculateResolution(mosaic);
        double percentual = Math.abs(initialRes - finalRes) / initialRes;
        Assert.assertTrue(percentual < TOLERANCE);

        // Check that on the center of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getCenterX(), actual.getCenterY());
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    // Test which mosaics two input coverages with alpha band
    @Test
    public void testMosaicWithAlpha() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage3);
        sources.add(coverage4);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        // Mosaic
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param);

        // Check that the final Coverage Bands are 2
        Assert.assertEquals(2, mosaic.getNumSampleDimensions());

        // Check that the final GridCoverage BoundingBox is equal to the union of the separate coverages bounding box
        Envelope2D expected = coverage3.getEnvelope2D();
        expected.include(coverage4.getEnvelope2D());
        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the first coverage
        double initialRes = calculateResolution(coverage3);
        double finalRes = calculateResolution(mosaic);
        double percentual = Math.abs(initialRes - finalRes) / initialRes;
        Assert.assertTrue(percentual < TOLERANCE);

        // Check that on the center of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getCenterX(), actual.getCenterY());
        double nodata = CoverageUtilities.getBackgroundValues(coverage1)[0];
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    // Test which mosaics two input coverages and resamples them by using the resolution of an external GridGeometry2D
    @Test
    public void testMosaicExternalGeometry() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);
        sources.add(coverage2);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);

        // Initial Bounding box
        Envelope2D startBBOX = coverage1.getEnvelope2D();
        startBBOX.include(coverage2.getEnvelope2D());
        Envelope2D expected = new Envelope2D(startBBOX);
        Point2D pt = new Point2D.Double(startBBOX.getMaxX() + 1, startBBOX.getMaxY() + 1);
        expected.add(pt);
        // External GridGeometry
        GridGeometry2D ggStart = new GridGeometry2D(PixelInCell.CELL_CORNER, coverage1
                .getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT), expected,
                GeoTools.getDefaultHints());

        param.parameter("geometry").setValue(ggStart);
        // Mosaic
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param);

        // Check that the final GridCoverage BoundingBox is equal to the bounding box provided in input

        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the first coverage
        double initialRes = calculateResolution(coverage1);
        double finalRes = calculateResolution(mosaic);
        Assert.assertEquals(initialRes, finalRes, TOLERANCE);

        // Check that on the Upper Right pixel of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getMinX() + finalRes, actual.getMaxY() - finalRes);
        double nodata = CoverageUtilities.getBackgroundValues(coverage1)[0];
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    // Test which mosaics two input coverages and tries to impose a null GridGeometry. An exception will be thrown
    @Test(expected = CoverageProcessingException.class)
    public void testMosaicNoExternalGeometry() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);
        sources.add(coverage2);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        param.parameter(Mosaic.POLICY).setValue("external");
        // Mosaic
        processor.doOperation(param);
    }

    // Test which mosaics two input coverages and creates a final GridCoverage with the worst resolution between those of the input GridCoverages
    @Test
    public void testMosaicCoarseResolution() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);

        // Resampling of the second Coverage to an higher resolution
        ParameterValueGroup paramResampling = processor.getOperation("resample").getParameters();
        paramResampling.parameter("Source").setValue(coverage2);
        GridEnvelope2D gridRange = coverage2.getGridGeometry().getGridRange2D();
        gridRange.add(gridRange.getMaxX() + 100, gridRange.getMaxY() + 100);
        GridGeometry2D ggNew = new GridGeometry2D(gridRange, coverage2.getEnvelope());
        paramResampling.parameter("GridGeometry").setValue(ggNew);
        GridCoverage2D resampled = (GridCoverage2D) processor.doOperation(paramResampling);

        sources.add(resampled);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        param.parameter(Mosaic.POLICY).setValue("coarse");
        // Mosaic
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param);

        // Check that the final GridCoverage BoundingBox is equal to the union of the separate coverages bounding box
        Envelope2D expected = coverage1.getEnvelope2D();
        expected.include(resampled.getEnvelope2D());
        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the first coverage
        double initialRes = calculateResolution(coverage1);
        double finalRes = calculateResolution(mosaic);
        double percentual = Math.abs(initialRes - finalRes) / initialRes;
        Assert.assertTrue(percentual < TOLERANCE);

        // Check that on the center of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getCenterX(), actual.getCenterY());
        double nodata = CoverageUtilities.getBackgroundValues(coverage1)[0];
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        resampled.dispose(true);
        disposeCoveragePlanarImage(mosaic);
        disposeCoveragePlanarImage(resampled);
    }

    // Test which mosaics two input coverages and creates a final GridCoverage with the best resolution between those of the input GridCoverages
    @Test
    public void testMosaicFineResolution() {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);

        // Resampling of the second Coverage to an higher resolution
        ParameterValueGroup paramResampling = processor.getOperation("resample").getParameters();
        paramResampling.parameter("Source").setValue(coverage2);
        GridEnvelope2D gridRange = coverage2.getGridGeometry().getGridRange2D();
        gridRange.add(gridRange.getMaxX() + 100, gridRange.getMaxY() + 100);
        GridGeometry2D ggNew = new GridGeometry2D(gridRange, coverage2.getEnvelope());
        paramResampling.parameter("GridGeometry").setValue(ggNew);
        GridCoverage2D resampled = (GridCoverage2D) processor.doOperation(paramResampling);

        sources.add(resampled);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        param.parameter(Mosaic.POLICY).setValue("fine");
        // Mosaic
        GridCoverage2D mosaic = (GridCoverage2D) processor.doOperation(param);

        // Check that the final GridCoverage BoundingBox is equal to the union of the separate coverages bounding box
        Envelope2D expected = coverage1.getEnvelope2D();
        expected.include(resampled.getEnvelope2D());
        // Mosaic Envelope
        Envelope2D actual = mosaic.getEnvelope2D();

        // Check the same Bounding Box
        assertEqualBBOX(expected, actual);

        // Check that the final Coverage resolution is equal to that of the second coverage
        double initialRes = calculateResolution(resampled);
        double finalRes = calculateResolution(mosaic);
        double percentual = Math.abs(initialRes - finalRes) / initialRes;
        Assert.assertTrue(percentual < TOLERANCE);

        // Check that on the center of the image there are nodata
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                actual.getCenterX(), actual.getCenterY());
        double nodata = CoverageUtilities.getBackgroundValues(coverage1)[0];
        double result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), actual.getMinX()
                + finalRes, actual.getMinY() + finalRes);
        result = ((int[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        resampled.dispose(true);
        disposeCoveragePlanarImage(mosaic);
        disposeCoveragePlanarImage(resampled);
    }

    // Test which mosaics two input coverages with different CRS. An exception will be thrown
    @Test(expected = CoverageProcessingException.class)
    public void testWrongCRS() throws InvalidParameterValueException, ParameterNotFoundException,
            FactoryException {
        /*
         * Do the crop without conserving the envelope.
         */
        ParameterValueGroup param = MOSAIC.getParameters();

        // Creation of a List of the input Sources
        List<GridCoverage2D> sources = new ArrayList<GridCoverage2D>(2);
        sources.add(coverage1);

        // Reprojection of the second Coverage
        ParameterValueGroup paramReprojection = processor.getOperation("resample").getParameters();
        paramReprojection.parameter("Source").setValue(coverage2);
        paramReprojection.parameter("CoordinateReferenceSystem").setValue(
                CRS.parseWKT(GOOGLE_MERCATOR_WKT));
        GridCoverage2D resampled = (GridCoverage2D) processor.doOperation(paramReprojection);

        sources.add(resampled);
        // Setting of the sources
        param.parameter("Sources").setValue(sources);
        // Mosaic
        processor.doOperation(param);
    }

    // Test which takes an input file, extracts two coverages from it, shifts the first on the right of the second one and then mosaics them.
    @Test
    public void testWorldFile() throws FileNotFoundException, IOException {
        // read the coverage
        GridCoverage2D test = readInputFile("sample0");
        // Envelope for the first half of the image
        ReferencedEnvelope re1 = new ReferencedEnvelope(10, 180, -90, 90,
                DefaultGeographicCRS.WGS84);
        // Coverage crop for extracting the first half of the image
        GridCoverage2D c1 = crop(test, new GeneralEnvelope(re1));
        // Envelope for the second half of the image
        ReferencedEnvelope re2 = new ReferencedEnvelope(-180, -10, -90, 90,
                DefaultGeographicCRS.WGS84);
        // Coverage crop for extracting the second half of the image
        GridCoverage2D c2 = crop(test, new GeneralEnvelope(re2));

        // Shift the first image on the right
        ReferencedEnvelope re3 = new ReferencedEnvelope(180, 350, -90, 90,
                DefaultGeographicCRS.WGS84);
        GridCoverage2D shifted = new GridCoverageFactory().create(c2.getName(),
                c2.getRenderedImage(), re3);
        // Envelope containing the bounding box for the two images
        ReferencedEnvelope reUnion = new ReferencedEnvelope(10, 350, -90, 90,
                DefaultGeographicCRS.WGS84);
        // Mosaic operation
        GridCoverage2D mosaic = mosaic(sortCoverages(Arrays.asList(c1, shifted)),
                new GeneralEnvelope(reUnion), new Hints());

        // Ensure the mosaic Bounding box is equal to that expected
        Envelope2D expected = new Envelope2D(reUnion);
        assertEqualBBOX(expected, mosaic.getEnvelope2D());

        // Check that the final Coverage resolution is equal to that of the first coverage
        double finalRes = calculateResolution(mosaic);

        // Check that on the center of the image there is valid data
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(),
                expected.getCenterX(), expected.getCenterY());
        double nodata = 0;
        double result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Left border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), expected.getMinX()
                + finalRes, expected.getMinY() + finalRes);
        result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Check that on the Upper Right border pixel there is valid data
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), expected.getMaxX()
                - finalRes, expected.getMinY() + finalRes);
        result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    // Test which takes an input file, extracts 4 coverages from it, shifts them in order to created a replication.
    @Test
    public void testWorldFile2() throws FileNotFoundException, IOException {
        // read the two coverages
        GridEnvelope2D gridRange = new GridEnvelope2D(0, 0, 400, 200);
        ReferencedEnvelope re = new ReferencedEnvelope(-180, 180, -85, 85,
                DefaultGeographicCRS.WGS84);
        GridGeometry2D gg = new GridGeometry2D(gridRange, re);

        GridCoverage2D cStart = readInputFile("sample0");
        GridCoverage2D cCrop = crop(cStart, new GeneralEnvelope(re));

        // Resampling of the Coverage to the defined resolution
        ParameterValueGroup paramResampling = processor.getOperation("resample").getParameters();
        paramResampling.parameter("Source").setValue(cCrop);
        paramResampling.parameter("GridGeometry").setValue(gg);
        GridCoverage2D c = (GridCoverage2D) processor.doOperation(paramResampling);

        // first shifted
        ReferencedEnvelope re2 = new ReferencedEnvelope(-540, -180, -85, 85,
                DefaultGeographicCRS.WGS84);
        GridCoverage2D c2 = new GridCoverageFactory()
                .create(c.getName(), c.getRenderedImage(), re2);

        // second shifted
        ReferencedEnvelope re3 = new ReferencedEnvelope(180, 540, -85, 85,
                DefaultGeographicCRS.WGS84);
        GridCoverage2D c3 = new GridCoverageFactory()
                .create(c.getName(), c.getRenderedImage(), re3);

        // third shifted
        ReferencedEnvelope re4 = new ReferencedEnvelope(-540, -900, -85, 85,
                DefaultGeographicCRS.WGS84);
        GridCoverage2D c4 = new GridCoverageFactory()
                .create(c.getName(), c.getRenderedImage(), re4);

        ReferencedEnvelope reUnion = new ReferencedEnvelope(-900, 540, -85, 85,
                DefaultGeographicCRS.WGS84);
        List<GridCoverage2D> sorted = sortCoverages(Arrays.asList(c4, c2, c, c3));
        GridCoverage2D mosaic = mosaic(sorted, new GeneralEnvelope(reUnion), new Hints());

        // Ensure the mosaic Bounding box is equal to that expected
        Envelope2D expected = new Envelope2D(reUnion);
        assertEqualBBOX(expected, mosaic.getEnvelope2D());

        // Calculate the mosaic resolution
        double res = calculateResolution(mosaic);

        // Ensure that no black lines are present on the border between the input images
        // Check that on the center of the image there is valid data
        DirectPosition point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), -540,
                -84);
        double nodata = 0;
        double result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), -540 - res, -84);
        result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);
        point = new DirectPosition2D(mosaic.getCoordinateReferenceSystem(), -540 + res, -84);
        result = ((byte[]) mosaic.evaluate(point))[0];
        Assert.assertNotEquals(nodata, result, TOLERANCE);

        // Coverage and RenderedImage disposal
        mosaic.dispose(true);
        disposeCoveragePlanarImage(mosaic);
    }

    @AfterClass
    public static void finalStep() {
        // Coverage and RenderedImage disposal
        coverage1.dispose(true);
        coverage2.dispose(true);
        disposeCoveragePlanarImage(coverage1);
        disposeCoveragePlanarImage(coverage2);
    }

    /**
     * Method for disposing the {@link RenderedImage} chain of the {@link GridCoverage2D}
     *
     * @param coverage
     */
    private static void disposeCoveragePlanarImage(GridCoverage2D coverage) {
        ImageUtilities.disposePlanarImageChain(PlanarImage.wrapRenderedImage(coverage
                .getRenderedImage()));
    }

    /**
     * Method for calculating the resolution of the input {@link GridCoverage2D}
     *
     * @param coverage
     * @return
     */
    private static double calculateResolution(GridCoverage2D coverage) {
        GridGeometry2D gg2D = coverage.getGridGeometry();
        double envW = gg2D.getEnvelope2D().width;
        double gridW = gg2D.getGridRange2D().width;
        double res = envW / gridW;
        return res;
    }

    /**
     * Method which ensures that the two {@link Envelope2D} objects are equals.
     *
     * @param expected
     * @param actual
     */
    private void assertEqualBBOX(Envelope2D expected, Envelope2D actual) {
        Assert.assertEquals(expected.getX(), actual.getX(), TOLERANCE);
        Assert.assertEquals(expected.getY(), actual.getY(), TOLERANCE);
        Assert.assertEquals(expected.getHeight(), actual.getHeight(), TOLERANCE);
        Assert.assertEquals(expected.getWidth(), actual.getWidth(), TOLERANCE);
    }

    /**
     * Method for cropping the input coverage with the defined envelope.
     *
     * @param gc
     * @param envelope
     * @return
     */
    private GridCoverage2D crop(GridCoverage2D gc, GeneralEnvelope envelope) {
        final GeneralEnvelope oldEnvelope = (GeneralEnvelope) gc.getEnvelope();
        // intersect the envelopes in order to prepare for cropping the coverage
        // down to the neded resolution
        final GeneralEnvelope intersectionEnvelope = new GeneralEnvelope(envelope);
        intersectionEnvelope.setCoordinateReferenceSystem(envelope.getCoordinateReferenceSystem());
        intersectionEnvelope.intersect((GeneralEnvelope) oldEnvelope);

        // Do we have something to show? After the crop I could get a null
        // coverage which would mean nothing to show.
        if (intersectionEnvelope.isEmpty()) {
            return null;
        }

        // crop
        final ParameterValueGroup param = (ParameterValueGroup) processor
                .getOperation("CoverageCrop").getParameters().clone();
        param.parameter("source").setValue(gc);
        param.parameter("Envelope").setValue(intersectionEnvelope);
        return (GridCoverage2D) processor.doOperation(param, GeoTools.getDefaultHints());
    }

    /**
     * Method for mosaicking two input images and setting the final BoundingBox
     * @param coverages
     * @param renderingEnvelope
     * @param hints
     * @return
     */
    private GridCoverage2D mosaic(List<GridCoverage2D> coverages,
            GeneralEnvelope renderingEnvelope, Hints hints) {
        // setup the grid geometry
        try {
            // find the intersection between the target envelope and the coverages one
            ReferencedEnvelope targetEnvelope = ReferencedEnvelope.reference(renderingEnvelope);
            ReferencedEnvelope coveragesEnvelope = null;
            for (GridCoverage2D coverage : coverages) {
                ReferencedEnvelope re = ReferencedEnvelope.reference(coverage.getEnvelope2D());
                if (coveragesEnvelope == null) {
                    coveragesEnvelope = re;
                } else {
                    coveragesEnvelope.expandToInclude(re);
                }
            }
            targetEnvelope = new ReferencedEnvelope(targetEnvelope.intersection(coveragesEnvelope),
                    renderingEnvelope.getCoordinateReferenceSystem());
            if (targetEnvelope.isEmpty() || targetEnvelope.isNull()) {
                return null;
            }

            MathTransform2D mt = coverages.get(0).getGridGeometry().getCRSToGrid2D();
            Rectangle rasterSpaceEnvelope;
            rasterSpaceEnvelope = CRS.transform(mt, targetEnvelope).toRectangle2D().getBounds();
            GridEnvelope2D gridRange = new GridEnvelope2D(rasterSpaceEnvelope);
            GridGeometry2D gridGeometry = new GridGeometry2D(gridRange, targetEnvelope);

            // mosaic
            final ParameterValueGroup param = MOSAIC.getParameters().clone();
            param.parameter("sources").setValue(coverages);
            param.parameter("geometry").setValue(gridGeometry);
            return (GridCoverage2D) MOSAIC.doOperation(param, hints);
        } catch (Exception e) {
            throw new RuntimeException("Failed to mosaic the input coverages", e);
        }
    }

    private List<GridCoverage2D> sortCoverages(List<GridCoverage2D> coverages) {
        Collections.sort(coverages, new Comparator<GridCoverage2D>() {

            @Override
            public int compare(GridCoverage2D o1, GridCoverage2D o2) {
                double minx1 = o1.getEnvelope().getMinimum(0);
                double minx2 = o2.getEnvelope().getMinimum(0);
                if (minx1 == minx2) {
                    double maxy1 = o1.getEnvelope().getMaximum(1);
                    double maxy2 = o2.getEnvelope().getMaximum(1);
                    return compareDoubles(maxy1, maxy2);
                } else {
                    return compareDoubles(minx1, minx2);
                }
            }

            private int compareDoubles(double maxy1, double maxy2) {
                if (maxy1 == maxy2) {
                    return 0;
                } else {
                    return (int) Math.signum(maxy1 - maxy2);
                }
            }
        });
        return coverages;
    }
}
TOP

Related Classes of org.geotools.coverage.processing.MosaicTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.