| /* |
| * 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 java.util.Arrays; |
| import java.util.Random; |
| import java.io.IOException; |
| import java.io.LineNumberReader; |
| import org.opengis.util.FactoryException; |
| import org.opengis.referencing.operation.TransformException; |
| import org.apache.sis.internal.referencing.Formulas; |
| import org.apache.sis.internal.referencing.provider.TransverseMercatorSouth; |
| import org.apache.sis.referencing.operation.transform.CoordinateDomain; |
| import org.apache.sis.parameter.Parameters; |
| import org.apache.sis.util.CharSequences; |
| import org.apache.sis.test.OptionalTestData; |
| import org.apache.sis.test.DependsOnMethod; |
| import org.apache.sis.test.DependsOn; |
| import org.junit.Test; |
| |
| import static java.lang.Double.NaN; |
| import static java.lang.StrictMath.min; |
| import static java.lang.StrictMath.toRadians; |
| import static org.apache.sis.test.Assert.*; |
| import static org.apache.sis.internal.referencing.provider.TransverseMercator.LATITUDE_OF_ORIGIN; |
| import org.opengis.test.CalculationType; |
| |
| |
| /** |
| * Tests the {@link TransverseMercator} class. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 1.0 |
| * @since 0.6 |
| * @module |
| */ |
| @DependsOn(NormalizedProjectionTest.class) |
| public final strictfp class TransverseMercatorTest extends MapProjectionTestCase { |
| /** |
| * Creates a new instance of {@link TransverseMercator}. |
| * |
| * @param ellipsoidal {@code false} for a sphere, or {@code true} for WGS84 ellipsoid. |
| */ |
| private void createNormalizedProjection(final boolean ellipsoidal, final double latitudeOfOrigin) { |
| final org.apache.sis.internal.referencing.provider.TransverseMercator method = |
| new org.apache.sis.internal.referencing.provider.TransverseMercator(); |
| final Parameters parameters = parameters(method, ellipsoidal); |
| parameters.getOrCreate(LATITUDE_OF_ORIGIN).setValue(latitudeOfOrigin); |
| transform = new TransverseMercator(method, parameters); |
| tolerance = NORMALIZED_TOLERANCE; |
| validate(); |
| } |
| |
| /** |
| * Tests the <cite>Transverse Mercator</cite> case (EPSG:9807). |
| * 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. |
| * |
| * @see org.opengis.test.referencing.ParameterizedTransformTest#testTransverseMercator() |
| */ |
| @Test |
| public void testTransverseMercator() throws FactoryException, TransformException { |
| createGeoApiTest(new org.apache.sis.internal.referencing.provider.TransverseMercator()).testTransverseMercator(); |
| } |
| |
| /** |
| * Tests the <cite>Transverse Mercator (South Orientated)</cite> case (EPSG:9808). |
| * 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. |
| * |
| * @see org.opengis.test.referencing.ParameterizedTransformTest#testTransverseMercatorSouthOrientated() |
| */ |
| @Test |
| @DependsOnMethod("testTransverseMercator") |
| public void testTransverseMercatorSouthOrientated() throws FactoryException, TransformException { |
| createGeoApiTest(new TransverseMercatorSouth()).testTransverseMercatorSouthOrientated(); |
| } |
| |
| /** |
| * 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("testTransverseMercator") |
| public void compareEllipticalWithSpherical() throws FactoryException, TransformException { |
| createCompleteProjection(new org.apache.sis.internal.referencing.provider.TransverseMercator(), |
| 6371007, // Semi-major axis length |
| 6371007, // Semi-minor axis length |
| 0.5, // Central meridian |
| 2.5, // Latitude of origin |
| 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.RANGE_10, 0); |
| } |
| |
| /** |
| * Creates a projection and derivates a few points. |
| * |
| * @throws TransformException if an error occurred while projecting a point. |
| */ |
| @Test |
| public void testSphericalDerivative() throws TransformException { |
| createNormalizedProjection(false, 0); |
| tolerance = 1E-9; |
| |
| final double delta = toRadians(100.0 / 60) / 1852; // Approximately 100 metres. |
| derivativeDeltas = new double[] {delta, delta}; |
| verifyDerivative(toRadians( 0), toRadians( 0)); |
| verifyDerivative(toRadians(-3), toRadians(30)); |
| verifyDerivative(toRadians(+6), toRadians(60)); |
| } |
| |
| /** |
| * Creates a projection and derivates a few points. |
| * |
| * @throws TransformException if an error occurred while projecting a point. |
| */ |
| @Test |
| public void testEllipsoidalDerivative() throws TransformException { |
| createNormalizedProjection(true, 0); |
| tolerance = 1E-9; |
| |
| final double delta = toRadians(100.0 / 60) / 1852; // Approximately 100 metres. |
| derivativeDeltas = new double[] {delta, delta}; |
| verifyDerivative(toRadians( 0), toRadians( 0)); |
| verifyDerivative(toRadians(-3), toRadians(30)); |
| verifyDerivative(toRadians(+6), toRadians(60)); |
| } |
| |
| /** |
| * Verifies that deserialized projections work as expected. This implies that deserialization |
| * recomputed the internal transient fields, especially the series expansion coefficients. |
| * |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while projecting a coordinate. |
| */ |
| @Test |
| @DependsOnMethod("testTransverseMercator") |
| public void testSerialization() throws FactoryException, TransformException { |
| createNormalizedProjection(true, 40); |
| /* |
| * Use a fixed seed for the random number generator in this test, because in case of failure this class will not |
| * report which seed it used. This limitation exists because this test class does not extend the SIS TestCase. |
| */ |
| final double[] source = CoordinateDomain.GEOGRAPHIC_RADIANS_HALF_λ.generateRandomInput(new Random(5346144739450824145L), 2, 10); |
| final double[] target = new double[source.length]; |
| for (int i=0; i<source.length; i+=2) { |
| // A longitude range of [-90 … +90]° is still too wide for Transverse Mercator. Reduce to [-45 … +45]°. |
| source[i] /= 2; |
| } |
| transform.transform(source, 0, target, 0, 10); |
| transform = assertSerializedEquals(transform); |
| tolerance = Formulas.LINEAR_TOLERANCE; |
| verifyTransform(source, target); |
| } |
| |
| /** |
| * Compares with <cite>Karney (2009) Test data for the transverse Mercator projection</cite>. |
| * This is an optional test executed only if the {@code $SIS_DATA/Tests/TMcoords.dat} file is found. |
| * |
| * @throws IOException if an error occurred while reading the test file. |
| * @throws FactoryException if an error occurred while creating the map projection. |
| * @throws TransformException if an error occurred while transforming coordinates. |
| */ |
| @Test |
| public void compareAgainstDataset() throws IOException, FactoryException, TransformException { |
| try (LineNumberReader reader = OptionalTestData.TRANSVERSE_MERCATOR.reader()) { |
| createCompleteProjection(new org.apache.sis.internal.referencing.provider.TransverseMercator(), |
| WGS84_A, // Semi-major axis length |
| WGS84_B, // Semi-minor axis length |
| 0, // Central meridian |
| 0, // Latitude of origin |
| NaN, // Standard parallel 1 (none) |
| NaN, // Standard parallel 2 (none) |
| 0.9996, // Scale factor |
| 0, // False easting |
| 0); // False northing |
| final double[] source = new double[2]; |
| final double[] target = new double[2]; |
| String line; |
| while ((line = reader.readLine()) != null) { |
| Arrays.fill(source, Double.NaN); |
| final CharSequence[] split = CharSequences.split(line, ' '); |
| for (int i=min(split.length, 4); --i >= 0;) { |
| final double value = Double.parseDouble(split[i].toString()); |
| if (i <= 1) source[i ^ 1] = value; // Swap axis order. |
| else target[i - 2] = value; |
| } |
| // Relax tolerance for longitudes very far from central meridian. |
| final double longitude = StrictMath.abs(source[0]); |
| if (longitude < TransverseMercator.DOMAIN_OF_VALIDITY) { |
| if (StrictMath.abs(source[1]) >= 89.9) tolerance = 0.1; |
| else if (longitude <= 60) tolerance = Formulas.LINEAR_TOLERANCE; |
| else if (longitude <= 66) tolerance = 0.1; |
| else if (longitude <= 70) tolerance = 1; |
| else if (longitude <= 72) tolerance = 2; |
| else if (longitude <= 74) tolerance = 10; |
| else if (longitude <= 76) tolerance = 30; |
| else tolerance = 1000; |
| transform.transform(source, 0, source, 0, 1); |
| assertCoordinateEquals(line, target, source, reader.getLineNumber(), CalculationType.DIRECT_TRANSFORM); |
| } |
| } |
| } |
| } |
| } |