| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.referencing.operation.projection; |
| |
| import org.opengis.util.FactoryException; |
| import org.opengis.referencing.operation.TransformException; |
| import org.opengis.referencing.operation.NoninvertibleTransformException; |
| import org.apache.sis.internal.referencing.Formulas; |
| import org.apache.sis.internal.referencing.provider.Mercator1SP; |
| import org.apache.sis.internal.referencing.provider.Mercator2SP; |
| import org.apache.sis.internal.referencing.provider.PseudoMercator; |
| import org.apache.sis.internal.referencing.provider.MillerCylindrical; |
| import org.apache.sis.referencing.operation.transform.CoordinateDomain; |
| import org.apache.sis.test.DependsOnMethod; |
| import org.apache.sis.test.DependsOn; |
| import org.junit.Test; |
| |
| import static java.lang.Double.*; |
| import static java.lang.StrictMath.*; |
| import static org.opengis.test.Assert.*; |
| import static org.apache.sis.referencing.operation.projection.ConformalProjectionTest.LN_INFINITY; |
| |
| // Branch-specific imports |
| import static org.junit.Assume.assumeTrue; |
| import static org.apache.sis.test.Assert.PENDING_NEXT_GEOAPI_RELEASE; |
| |
| |
| /** |
| * Tests the {@link Mercator} projection. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @author Simon Reynard (Geomatys) |
| * @author Rémi Maréchal (Geomatys) |
| * @version 0.8 |
| * @since 0.6 |
| * @module |
| */ |
| @DependsOn(ConformalProjectionTest.class) |
| public final strictfp class MercatorTest extends MapProjectionTestCase { |
| /** |
| * Creates a new instance of {@link Mercator} for a sphere or an ellipsoid. |
| * The new instance is stored in the inherited {@link #transform} field. |
| * |
| * @param ellipsoidal {@code false} for a sphere, or {@code true} for WGS84 ellipsoid. |
| */ |
| private void createNormalizedProjection(final boolean ellipsoidal) { |
| final Mercator2SP method = new Mercator2SP(); |
| transform = new Mercator(method, parameters(method, ellipsoidal)); |
| if (!ellipsoidal) { |
| transform = new Mercator.Spherical((Mercator) transform); |
| } |
| tolerance = NORMALIZED_TOLERANCE; |
| validate(); |
| } |
| |
| /** |
| * Tests the WKT formatting of {@link NormalizedProjection}. For the Mercator projection, we expect only |
| * the ellipsoid eccentricity. We expect nothing else because all other parameters are used |
| * by the (de)normalization affine transforms instead than the {@link Mercator} class itself. |
| * |
| * @throws NoninvertibleTransformException if the transform can not be inverted. |
| * |
| * @see LambertConicConformalTest#testNormalizedWKT() |
| */ |
| @Test |
| public void testNormalizedWKT() throws NoninvertibleTransformException { |
| createNormalizedProjection(true); |
| assertWktEquals("PARAM_MT[“Mercator (radians domain)”,\n" + |
| " PARAMETER[“eccentricity”, 0.0818191908426215]]"); |
| |
| transform = transform.inverse(); |
| assertWktEquals("INVERSE_MT[\n" + |
| " PARAM_MT[“Mercator (radians domain)”,\n" + |
| " PARAMETER[“eccentricity”, 0.0818191908426215]]]"); |
| } |
| |
| /** |
| * Tests WKT of a complete map projection. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws NoninvertibleTransformException if the transform can not be inverted. |
| */ |
| @Test |
| @DependsOnMethod("testNormalizedWKT") |
| public void testCompleteWKT() throws FactoryException, NoninvertibleTransformException { |
| createCompleteProjection(new Mercator1SP(), |
| WGS84_A, // Semi-major axis length |
| WGS84_B, // Semi-minor axis length |
| 0.5, // Central meridian |
| NaN, // Latitude of origin (none) |
| NaN, // Standard parallel 1 (none) |
| NaN, // Standard parallel 2 (none) |
| 0.997, // Scale factor |
| 200, // False easting |
| 100); // False northing |
| |
| assertWktEquals("PARAM_MT[“Mercator_1SP”,\n" + |
| " PARAMETER[“semi_major”, 6378137.0],\n" + |
| " PARAMETER[“semi_minor”, 6356752.314245179],\n" + |
| " PARAMETER[“central_meridian”, 0.5],\n" + |
| " PARAMETER[“scale_factor”, 0.997],\n" + |
| " PARAMETER[“false_easting”, 200.0],\n" + |
| " PARAMETER[“false_northing”, 100.0]]"); |
| |
| transform = transform.inverse(); |
| assertWktEquals("INVERSE_MT[\n" + |
| " PARAM_MT[“Mercator_1SP”,\n" + |
| " PARAMETER[“semi_major”, 6378137.0],\n" + |
| " PARAMETER[“semi_minor”, 6356752.314245179],\n" + |
| " PARAMETER[“central_meridian”, 0.5],\n" + |
| " PARAMETER[“scale_factor”, 0.997],\n" + |
| " PARAMETER[“false_easting”, 200.0],\n" + |
| " PARAMETER[“false_northing”, 100.0]]]"); |
| } |
| |
| /** |
| * Tests the projection at some special latitudes (0, ±π/2, NaN). |
| * |
| * @throws ProjectionException if an error occurred while projecting a point. |
| */ |
| @Test |
| public void testSpecialLatitudes() throws ProjectionException { |
| if (transform == null) { // May have been initialized by 'testSphericalCase'. |
| createNormalizedProjection(true); // Elliptical case |
| } |
| assertEquals ("Not a number", NaN, transform(NaN), tolerance); |
| assertEquals ("Out of range", NaN, transform(+2), tolerance); |
| assertEquals ("Out of range", NaN, transform(-2), tolerance); |
| assertEquals ("Forward 0°N", 0, transform(0), tolerance); |
| assertEquals ("Forward 90°N", POSITIVE_INFINITY, transform(+PI/2), tolerance); |
| assertEquals ("Forward 90°S", NEGATIVE_INFINITY, transform(-PI/2), tolerance); |
| assertEquals ("Forward (90+ε)°N", POSITIVE_INFINITY, transform(nextUp ( PI/2)), tolerance); |
| assertEquals ("Forward (90+ε)°S", NEGATIVE_INFINITY, transform(nextDown(-PI/2)), tolerance); |
| assertBetween("Forward (90-ε)°N", +MIN_VALUE, +MAX_VALUE, transform(nextDown( PI/2))); |
| assertBetween("Forward (90-ε)°S", -MAX_VALUE, -MIN_VALUE, transform(nextUp (-PI/2))); |
| |
| assertEquals ("Not a number", NaN, inverseTransform(NaN), tolerance); |
| assertEquals ("Inverse 0 m", 0, inverseTransform(0), tolerance); |
| assertEquals ("Inverse +∞", +PI/2, inverseTransform(POSITIVE_INFINITY), tolerance); |
| assertEquals ("Inverse +∞ appr.", +PI/2, inverseTransform(LN_INFINITY + 1), tolerance); |
| assertEquals ("Inverse −∞", -PI/2, inverseTransform(NEGATIVE_INFINITY), tolerance); |
| assertEquals ("Inverse −∞ appr.", -PI/2, inverseTransform(-(LN_INFINITY + 1)), tolerance); |
| } |
| |
| /** |
| * Tests the derivatives at a few points. This method compares the derivatives computed by |
| * the projection with an estimation of derivatives computed by the finite differences method. |
| * |
| * @throws TransformException if an error occurred while projecting a point. |
| */ |
| @Test |
| @DependsOnMethod("testSpecialLatitudes") |
| public void testDerivative() throws TransformException { |
| if (transform == null) { // May have been initialized by 'testSphericalCase'. |
| createNormalizedProjection(true); // Elliptical case |
| } |
| final double delta = toRadians(100.0 / 60) / 1852; // Approximately 100 metres. |
| derivativeDeltas = new double[] {delta, delta}; |
| tolerance = 1E-9; // More severe than Formulas.LINEAR_TOLERANCE. |
| verifyDerivative(toRadians(15), toRadians( 30)); |
| verifyDerivative(toRadians(10), toRadians(-60)); |
| } |
| |
| /** |
| * Tests the <cite>"Mercator (variant A)"</cite> case (EPSG:9804). |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod({"testSpecialLatitudes", "testDerivative"}) |
| public void testMercator1SP() throws FactoryException, TransformException { |
| createGeoApiTest(new Mercator1SP()).testMercator1SP(); |
| } |
| |
| /** |
| * Tests the <cite>"Mercator (variant B)"</cite> case (EPSG:9805). |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testMercator1SP") |
| public void testMercator2SP() throws FactoryException, TransformException { |
| createGeoApiTest(new Mercator2SP()).testMercator2SP(); |
| } |
| |
| /** |
| * Tests the <cite>"Mercator (variant C)"</cite> case (EPSG:1044). |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testMercator2SP") |
| public void testRegionalMercator() throws FactoryException, TransformException { |
| assumeTrue(PENDING_NEXT_GEOAPI_RELEASE); // Test not available in GeoAPI 3.0 |
| } |
| |
| /** |
| * Tests the <cite>"Mercator (Spherical)"</cite> case (EPSG:1026). |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testMercator1SP") |
| public void testMercatorSpherical() throws FactoryException, TransformException { |
| assumeTrue(PENDING_NEXT_GEOAPI_RELEASE); // Test not available in GeoAPI 3.0 |
| } |
| |
| /** |
| * Tests the <cite>"Popular Visualisation Pseudo Mercator"</cite> case (EPSG:1024). |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testMercatorSpherical") |
| public void testPseudoMercator() throws FactoryException, TransformException { |
| createGeoApiTest(new PseudoMercator()).testPseudoMercator(); |
| } |
| |
| /** |
| * Tests the <cite>"Miller Cylindrical"</cite> case. |
| * This test is defined in GeoAPI conformance test suite. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testMercator1SP") |
| public void testMiller() throws FactoryException, TransformException { |
| createGeoApiTest(new MillerCylindrical()).testMiller(); |
| } |
| |
| /** |
| * Performs the same tests than {@link #testSpecialLatitudes()} and {@link #testDerivative()}, |
| * but using spherical formulas. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod({"testSpecialLatitudes", "testDerivative"}) |
| public void testSphericalCase() throws FactoryException, TransformException { |
| createNormalizedProjection(false); // Spherical case |
| testSpecialLatitudes(); |
| testDerivative(); |
| |
| // Make sure that the above methods did not overwrote the 'transform' field. |
| assertEquals("transform.class", Mercator.Spherical.class, transform.getClass()); |
| } |
| |
| /** |
| * Verifies the consistency of elliptical formulas with the spherical formulas. |
| * This test compares the results of elliptical formulas with the spherical ones |
| * for some random points. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testSphericalCase") |
| public void compareEllipticalWithSpherical() throws FactoryException, TransformException { |
| createCompleteProjection(new Mercator1SP(), |
| 6371007, // Semi-major axis length |
| 6371007, // Semi-minor axis length |
| 0.5, // Central meridian |
| NaN, // Latitude of origin (none) |
| NaN, // Standard parallel 1 (none) |
| NaN, // Standard parallel 2 (none) |
| 0.997, // Scale factor |
| 200, // False easting |
| 100); // False northing |
| tolerance = Formulas.LINEAR_TOLERANCE; |
| compareEllipticalWithSpherical(CoordinateDomain.GEOGRAPHIC_SAFE, 0); |
| } |
| } |