/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2008-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.referencing.operation.projection;
import org.opengis.util.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.transform.CoordinateDomain;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.test.DependsOn;
import org.junit.*;
import static java.lang.Double.*;
import static java.lang.StrictMath.*;
import static org.junit.Assert.*;
import static org.geotoolkit.referencing.operation.provider.LambertAzimuthalEqualArea.PARAMETERS;
/**
* Tests the {@link LambertAzimuthalEqualArea} class. We test using various values
* of the latitude of origin, which is the only parameter impacting the internal
* coefficients of that class (except for the eccentricity).
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
*
* @since 3.00
*/
@DependsOn(UnitaryProjectionTest.class)
public final strictfp class LambertAzimuthalEqualAreaTest extends ProjectionTestBase {
/**
* Tolerance level for comparing floating point numbers.
*/
private static final double TOLERANCE = 1E-8;
/**
* Creates a default test suite.
*/
public LambertAzimuthalEqualAreaTest() {
super(LambertAzimuthalEqualArea.class, null);
}
/**
* Returns a new instance of {@link LambertAzimuthalEqualArea}.
*
* @param ellipse {@code false} for a sphere, or {@code true} for WGS84 ellipsoid.
* @param latitudeOfOrigin The latitude of origin.
* @return Newly created projection.
*/
private static LambertAzimuthalEqualArea create(final boolean ellipse, final double latitudeOfOrigin) {
final Parameters parameters = parameters(wrap(PARAMETERS), ellipse, 0);
parameters.parameter("latitude_of_origin").setValue(latitudeOfOrigin);
if (ellipse) {
return new LambertAzimuthalEqualArea(new org.geotoolkit.referencing.operation.provider.LambertAzimuthalEqualArea(), parameters);
} else {
return new LambertAzimuthalEqualArea.Spherical(new org.geotoolkit.referencing.operation.provider.LambertAzimuthalEqualArea(), parameters);
}
}
/**
* Tests the projection at a few extreme points in the polar case. We test on the sphere
* only, which imply the test using ellipsoidal formulas as well through Java assertions.
*
* @throws TransformException Should never happen.
*/
@Test
public void testPolar() throws TransformException {
transform = concatenated(create(false, 90));
tolerance = TOLERANCE;
validate();
/*
* Project the origin. Result should be (0,0). Do not test the inverse projection
* since the longitude could be anything and still be the North pole. We test that
* by projecting again with an other longitude, set to 45°, and expect the same result.
*/
final double[] point = new double[] {0, 90};
transform.transform(point, 0, point, 0, 1);
assertEquals(0, point[0], tolerance);
assertEquals(0, point[1], tolerance);
transform.inverse().transform(point, 0, point, 0, 1);
assertEquals( 0, point[0], 180);
assertEquals(90, point[1], tolerance);
point[0] = 45;
point[1] = 90;
transform.transform(point, 0, point, 0, 1);
assertEquals(0, point[0], tolerance);
assertEquals(0, point[1], tolerance);
transform.inverse().transform(point, 0, point, 0, 1);
assertEquals(45, point[0], 180);
assertEquals(90, point[1], tolerance);
/*
* Project a point on the equator, at 0° and 180° longitude. Result should
* be (0, sqrt(2)) positive or negative depending on the longitude.
*/
point[0] = 0;
point[1] = 0;
transform.transform(point, 0, point, 0, 1);
assertEquals(0, point[0], tolerance);
assertEquals(-sqrt(2), point[1] / SPHERE_RADIUS, tolerance);
transform.inverse().transform(point, 0, point, 0, 1);
assertEquals(0, point[0], tolerance);
assertEquals(0, point[1], tolerance);
point[0] = 180;
point[1] = 0;
transform.transform(point, 0, point, 0, 1);
assertEquals(0, point[0], tolerance);
assertEquals(sqrt(2), point[1] / SPHERE_RADIUS, tolerance);
transform.inverse().transform(point, 0, point, 0, 1);
assertEquals(180, point[0], tolerance);
assertEquals( 0, point[1], tolerance);
/*
* Project the antipode. Result would be (0, -2) if this operation was allowed.
* Actually the formulas would work, but every points on a circle or radius 2
* would be the pole so returning a single value may not be appropriate. Proj4
* was returning an error code in this case.
*/
point[0] = 0;
point[1] = -90;
transform.transform(point, 0, point, 0, 1);
assertEquals(NaN, point[0] / SPHERE_RADIUS, tolerance);
assertEquals(NaN, point[1] / SPHERE_RADIUS, tolerance);
}
/**
* Tests the projection at a few extreme points in the oblique case. We test on the sphere
* only, which imply the test using ellipsoidal formulas as well through Java assertions.
*
* @throws TransformException Should never happen.
*/
@Test
public void testOblique() throws TransformException {
transform = concatenated(create(false, 45));
tolerance = TOLERANCE;
validate();
// Projects the origin.
final double[] point = new double[] {0, 45};
final double[] expected = new double[] {0, 0};
verifyTransform(point, expected);
// Project the antipode.
point[0] = 180;
point[1] = -45;
transform.transform(point, 0, point, 0, 1);
assertEquals(NaN, point[0] / SPHERE_RADIUS, tolerance);
assertEquals(NaN, point[1] / SPHERE_RADIUS, tolerance);
// Project the almost-antipode.
point[0] = 180;
point[1] = -44.9;
transform.transform(point, 0, point, 0, 1);
assertEquals(0, point[0] / SPHERE_RADIUS, tolerance);
assertEquals(2, point[1] / SPHERE_RADIUS, 1E-6);
}
/**
* Tests the unitary projection on an ellipse.
*
* @throws TransformException Should never happen.
*/
@Test
public void testUnitaryOnEllipse() throws TransformException {
for (int phi=-90; phi<=90; phi+=15) {
transform = create(true, phi);
tolerance = 1E-2; // TODO: investigate why the difference is so large.
validate();
assertFalse(isSpherical());
final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres.
derivativeDeltas = new double[] {delta, delta};
verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS, 484117986);
}
}
/**
* Tests the unitary projection on a sphere. This is actually a more expensive check
* than the one on ellipse because the spherical case contains assertion statements
* comparing the results with the ones obtained from ellipsoidal formulas.
*
* @throws TransformException Should never happen.
*/
@Test
public void testUnitaryOnSphere() throws TransformException {
for (int phi=-90; phi<=90; phi+=15) {
transform = create(false, phi);
tolerance = TOLERANCE;
validate();
assertTrue(isSpherical());
final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres.
derivativeDeltas = new double[] {delta, delta};
verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS, 862247543);
}
}
/**
* Tests a point which is was known problematic in GeoTools 2.x.
*
* @throws TransformException Should never happen.
*/
@Test
public void testSpecific() throws TransformException {
transform = create(false, -75);
tolerance = TOLERANCE;
assertTrue(isSpherical());
final double[] point = new double[] {toRadians(-90), toRadians(-8)};
transform.transform(point, 0, point, 0, 1);
transform.inverse().transform(point, 0, point, 0, 1);
assertEquals(-90, toDegrees(point[0]), tolerance);
assertEquals( -8, toDegrees(point[1]), tolerance);
}
/**
* Creates a projection and tests the derivatives at a few points.
*
* @throws TransformException Should never happen.
*
* @since 3.18
*/
@Test
public void testDerivative() throws TransformException {
tolerance = 1E-9;
final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres.
derivativeDeltas = new double[] {delta, delta};
// Polar projection.
transform = create(true, 90);
validate();
verifyDerivative(toRadians(-6), toRadians(80));
// Intentionally above the pole.
verifyDerivative(toRadians(-6), toRadians(100));
// Polar projection, spherical formulas.
transform = create(false, 90);
validate();
verifyDerivative(toRadians(-6), toRadians(85));
// Equatorial projection, spherical formulas.
transform = create(false, 0);
validate();
verifyDerivative(toRadians(3), toRadians(4));
// Oblique projection, ellipsoidal formulas.
transform = create(true, 8);
validate();
verifyDerivative(toRadians(-6), toRadians(2));
// Oblique projection, spherical formulas.
transform = create(false, 8);
validate();
verifyDerivative(toRadians(-6), toRadians(2));
}
/**
* Runs the test defined in the GeoAPI-conformance module.
*
* @throws FactoryException Should never happen.
* @throws TransformException Should never happen.
*/
@Test
@Ignore
public void runGeoapiTest() throws FactoryException, TransformException {
new GeoapiTest(mtFactory).testLambertAzimuthalEqualArea();
}
}