/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2003-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* 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.geotoolkit.coverage.processing;
import java.util.Collections;
import java.util.Map;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import javax.media.jai.JAI;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.util.FactoryException;
import org.opengis.util.NoSuchIdentifierException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.OperationNotFoundException;
import org.geotoolkit.factory.Hints;
import org.apache.sis.referencing.CRS;
import org.geotoolkit.referencing.cs.PredefinedCS;
import org.apache.sis.referencing.crs.DefaultProjectedCRS;
import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
import org.geotoolkit.referencing.operation.MathTransforms;
import org.geotoolkit.coverage.CoverageFactoryFinder;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridGeometry2D;
import org.geotoolkit.coverage.grid.GeneralGridEnvelope;
import org.geotoolkit.coverage.grid.SampleCoverage;
import org.geotoolkit.coverage.grid.ViewType;
import org.apache.sis.referencing.operation.DefaultConversion;
import org.junit.*;
import static org.geotoolkit.test.Assert.*;
import static org.geotoolkit.test.Commons.*;
/**
* Visual test of the "Resample" operation. A remote sensing image is projected from a fitted
* coordinate system to a geographic one.
*
* @author Rémi Eve (IRD)
* @author Martin Desruisseaux (IRD)
* @version 3.02
*
* @since 2.1
*/
public final strictfp class ResampleTest extends GridProcessingTestBase {
/**
* Creates a new test suite.
*/
public ResampleTest() {
super(Operations.class);
}
/**
* Returns a projected CRS for test purpose.
*/
private static CoordinateReferenceSystem getProjectedCRS(final GridCoverage2D coverage) {
try {
final GeographicCRS base = (GeographicCRS) coverage.getCoordinateReferenceSystem();
final Ellipsoid ellipsoid = base.getDatum().getEllipsoid();
final DefaultMathTransformFactory factory = new DefaultMathTransformFactory();
final ParameterValueGroup parameters = factory.getDefaultParameters("Oblique_Stereographic");
parameters.parameter("semi_major").setValue(ellipsoid.getSemiMajorAxis());
parameters.parameter("semi_minor").setValue(ellipsoid.getSemiMinorAxis());
parameters.parameter("central_meridian").setValue(5);
parameters.parameter("latitude_of_origin").setValue(-5);
final MathTransform mt;
try {
mt = factory.createParameterizedTransform(parameters);
} catch (FactoryException exception) {
fail(exception.getLocalizedMessage());
return null;
}
final Map<String, String> name = Collections.singletonMap(DefaultConversion.NAME_KEY, "Stereographic");
return new DefaultProjectedCRS(name, base, new DefaultConversion(name, factory.getLastMethodUsed(), mt, parameters), PredefinedCS.PROJECTED);
} catch (NoSuchIdentifierException exception) {
fail(exception.getLocalizedMessage());
return null;
}
}
/**
* Projects the specified coverage to the same CRS without hints.
* The result will be displayed in a window if {@link #show} is set to {@code true}.
*
* @param coverage The coverage to project.
* @return The operation name which was applied on the image, or {@code null} if none.
*/
private String showProjected() {
return showResampled(coverage.getCoordinateReferenceSystem(), null, null, true);
}
/**
* Tests the "Resample" operation with an identity transform.
*
* @todo Investigate why we get a Lookup operation on the first coverage.
*/
@Test
public void testIdentity() {
loadSampleCoverage(SampleCoverage.SST);
assertEquals("Lookup", showProjected());
loadSampleCoverage(SampleCoverage.FLOAT);
assertNull(showProjected());
}
/**
* Tests the "Resample" operation with a "Crop" transform.
*/
@Test
public void testCrop() {
final GridGeometry2D g1,g2;
final MathTransform gridToCRS = null;
g1 = new GridGeometry2D(new GeneralGridEnvelope(new Rectangle(50,50,100,100), 2), gridToCRS, null);
g2 = new GridGeometry2D(new GeneralGridEnvelope(new Rectangle(50,50,200,200), 2), gridToCRS, null);
loadSampleCoverage(SampleCoverage.SST);
assertEquals("Crop", showResampled(null, g2, null, false));
assertEquals("Lookup", showResampled(null, g2, null, true ));
loadSampleCoverage(SampleCoverage.FLOAT);
assertEquals("Crop", showResampled(null, g1,
new Hints(Hints.COVERAGE_PROCESSING_VIEW, ViewType.PHOTOGRAPHIC), true));
}
/**
* Tests the "Resample" operation with a stereographic coordinate system.
*/
@Test
public void testStereographic() {
loadSampleCoverage(SampleCoverage.SST);
assertEquals("Warp", showResampled(getProjectedCRS(coverage), null, null, true));
}
/**
* Tests the "Resample" operation with a stereographic coordinate system.
*
* @throws FactoryException If the CRS can't not be created.
*/
@Test
public void testsNad83() throws FactoryException {
final Hints photo = new Hints(Hints.COVERAGE_PROCESSING_VIEW, ViewType.PHOTOGRAPHIC);
final CoordinateReferenceSystem crs = CRS.fromWKT(
"GEOGCS[\"NAD83\"," +
"DATUM[\"North_American_Datum_1983\"," +
"SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]]," +
"TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]]," +
"PRIMEM[\"Greenwich\",0, AUTHORITY[\"EPSG\",\"8901\"]]," +
"UNIT[\"degree\",0.0174532925199433]," +
"AXIS[\"Lat\",NORTH]," +
"AXIS[\"Long\",EAST]," +
"AUTHORITY[\"EPSG\",\"4269\"]]");
loadSampleCoverage(SampleCoverage.FLOAT);
org.junit.Assume.assumeTrue(viewEnabled);
// Following code work correctly when displayed in a component, but
// cause an IndexOutOfBoundsException otherwide (TODO: investigate).
assertEquals("Warp", showResampled(crs, null, photo, true));
}
/**
* Tests the "Resample" operation with an "Affine" transform.
*/
@Test
public void testAffine() {
final Hints photo = new Hints(Hints.COVERAGE_PROCESSING_VIEW, ViewType.PHOTOGRAPHIC);
loadSampleCoverage(SampleCoverage.SST);
showTranslated(null, true, "Lookup", "Affine");
loadSampleCoverage(SampleCoverage.FLOAT);
// TODO : Re-activate when we've identified why GridCoverage2D.getViewTypes() modifications impact it.
//showTranslated(photo, false, "org.geotoolkit.SampleTranscode", "org.geotoolkit.SampleTranscode");
}
/**
* Tests <var>X</var>,<var>Y</var> translation in the {@link GridGeometry} after
* a "Resample" operation.
*
* @throws NoninvertibleTransformException If a "grid to CRS" transform is not invertible.
*/
@Test
@Ignore("There is an issue with GridGeometry2D constructor with PixelInCell value.")
public void testTranslation() throws NoninvertibleTransformException {
loadSampleCoverage(SampleCoverage.SST);
final int transX = -253;
final int transY = -456;
final double scaleX = 0.04;
final double scaleY = -0.04;
final ParameterBlock block = new ParameterBlock().
addSource(coverage.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(coverage);
assertNotNull(expected);
expected = new AffineTransform(expected); // Get a mutable instance.
coverage = CoverageFactoryFinder.getGridCoverageFactory(null).create("Translated",
image, coverage.getEnvelope(), coverage.getSampleDimensions(),
new GridCoverage2D[]{coverage}, coverage.getProperties());
expected.translate(-transX, -transY);
assertTransformEquals(expected, getAffineTransform(coverage));
/*
* 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 = MathTransforms.linear(at);
final GridGeometry2D geometry = new GridGeometry2D(null, tr, null);
coverage = (GridCoverage2D) Operations.DEFAULT.resample(coverage,
coverage.getCoordinateReferenceSystem(), geometry, null);
assertEquals(at, getAffineTransform(coverage));
image = coverage.getRenderedImage();
expected.preConcatenate(at.createInverse());
final Point point = new Point(transX, transY);
assertSame(point, expected.transform(point, point)); // Round toward neareast integer
assertEquals("Incorrect X translation", point.x, image.getMinX());
assertEquals("Incorrect Y translation", point.y, image.getMinY());
}
/**
* Ensures that the resampling takes in account the system-wide "lenient datum shift" hint.
* The actual data doesn't matter for this test. We are interested only in the exception.
*
* @throws FactoryException Should never happen.
*
* @since 3.02
*/
@Test
@Ignore("Usage of hints will be removed in Apache SIS.")
public void testLenientDatumShift() throws FactoryException {
final CoordinateReferenceSystem sourceCRS = CRS.fromWKT(
"PROJCS[\"Bessel_1841_Hotine_Oblique_Mercator_Azimuth_Natural_Origin\"," +
"GEOGCS[\"GCS_Bessel_1841\",DATUM[\"D_Bessel_1841\"," +
"SPHEROID[\"Bessel_1841\",6377397.155,299.1528128]]," +
"PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]," +
"PROJECTION[\"Hotine_Oblique_Mercator_Azimuth_Natural_Origin\"]," +
"PARAMETER[\"False_Easting\",-9419820.5907]," +
"PARAMETER[\"False_Northing\",200000.0]," +
"PARAMETER[\"Scale_Factor\",1.0]," +
"PARAMETER[\"Azimuth\",90.0]," +
"PARAMETER[\"Longitude_Of_Center\",7.439583333333333]," +
"PARAMETER[\"Latitude_Of_Center\",46.95240555555556]," +
"UNIT[\"Meter\",1.0]]");
final CoordinateReferenceSystem targetCRS = CRS.fromWKT(decodeQuotes(
"PROJCS[“ETRS89 / ETRS-LAEA”,\n" +
" GEOGCS[“ETRS89”,\n" +
" DATUM[“European Terrestrial Reference System 1989”,\n" +
" SPHEROID[“GRS 1980”, 6378137.0, 298.257222101, AUTHORITY[“EPSG”,“7019”]],\n" +
" TOWGS84[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],\n" +
" AUTHORITY[“EPSG”,“6258”]],\n" +
" PRIMEM[“Greenwich”, 0.0, AUTHORITY[“EPSG”,“8901”]],\n" +
" UNIT[“degree”, 0.017453292519943295],\n" +
" AXIS[“Geodetic longitude”, EAST],\n" +
" AXIS[“Geodetic latitude”, NORTH],\n" +
" AUTHORITY[“EPSG”,“4258”]],\n" +
" PROJECTION[“Lambert Azimuthal Equal Area”, AUTHORITY[“EPSG”,“9820”]],\n" +
" PARAMETER[“latitude_of_center”, 52.0],\n" +
" PARAMETER[“longitude_of_center”, 10.0],\n" +
" PARAMETER[“false_easting”, 4321000.0],\n" +
" PARAMETER[“false_northing”, 3210000.0],\n" +
" UNIT[“metre”, 1.0],\n" +
" AXIS[“Easting”, EAST],\n" +
" AXIS[“Northing”, NORTH],\n" +
" AUTHORITY[“EPSG”,“3035”]]"));
createRandomCoverage(sourceCRS);
final GridCoverage2D sample = coverage;
try {
resample(targetCRS, null, null, true);
fail("Projection without Bursa-Wolf parameters should fail.");
} catch (CannotReprojectException e) {
// This is the exepcted exception.
// Cause should be: "Missing Bursa-Wolf parameters".
assertTrue(e.getCause() instanceof OperationNotFoundException);
}
coverage = sample;
Hints.putSystemDefault(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
try {
resample(targetCRS, null, null, true);
// Should not throw exception anymore.
} finally {
Hints.removeSystemDefault(Hints.LENIENT_DATUM_SHIFT);
}
}
}