/* * 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.referencing.operation; import org.opengis.util.FactoryException; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.geometry.DirectPosition; import org.apache.sis.geometry.DirectPosition2D; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.operation.transform.AbstractMathTransform; import org.geotoolkit.test.TestBase; import org.junit.*; import static org.junit.Assert.*; import static java.lang.StrictMath.*; /** * Tests projection equations as well as the integration with {@link MathTransformFactory}. * Projections are tested through creation and testing of {@link MathTransform} objects; * no {@link org.opengis.referencing.operation.Projection} are created here. * <p> * The spherical tests here tests real spheres (tests in {@code "Simple"} test scripts are * not exactly spherical). * * @author Rueben Schulz (UBC) * @version 3.00 * * @since 2.1 */ public final strictfp class MathTransformFactoryTest extends TestBase { /** * Tolerance for test when units are degrees. */ private final static double[] TOL_DEG = {1E-6, 1E-6}; /** * Tolerance for test when units are metres. */ private final static double[] TOL_M = {1E-2, 1E-2}; /** * Factory to use to create projection transforms. */ private MathTransformFactory mtFactory; /** * Set up common objects used by all tests. */ @Before public void setUp() { mtFactory = DefaultFactories.forBuildin(MathTransformFactory.class); } /** * Checks if two coordinate points are equals, in the limits of the specified * tolerance vector. * * @param expected The expected coordinate point. * @param actual The actual coordinate point. * @param tolerance The tolerance vector. If this vector length is smaller than the number * of dimension of <code>actual</code>, then the last tolerance value will * be reused for all extra dimensions. */ private static void assertPositionEquals(final DirectPosition expected, final DirectPosition actual, final double[] tolerance) { final int dimension = actual.getDimension(); final int lastToleranceIndex = tolerance.length - 1; assertEquals("The coordinate point doesn't have the expected dimension", expected.getDimension(), dimension); for (int i=0; i<dimension; i++) { assertEquals("Mismatch for ordinate "+i+" (zero-based):", expected.getOrdinate(i), actual.getOrdinate(i), tolerance[min(i, lastToleranceIndex)]); } } /** * Helper method to test transform from a source to a target point. * Coordinate points are (x,y) or (long, lat) */ private static void doTransform(final DirectPosition source, final DirectPosition target, final MathTransform transform) throws TransformException { doTransform(source, target, transform, TOL_M); } /** * Helper method to test transform from a source to a target point. * Coordinate points are (x,y) or (long, lat) */ private static void doTransform(DirectPosition source, DirectPosition target, MathTransform transform, final double[] tolerance) throws TransformException { DirectPosition calculated; calculated = transform.transform(source, null); assertPositionEquals(target, calculated, tolerance); // The inverse target = source; source = calculated; calculated = transform.inverse().transform(source, null); assertPositionEquals(target, calculated, TOL_DEG); } /** * Prints the given transform, if the output is verbose. */ private void printTransform(final MathTransform transform) { if (out != null) { out.println(transform); out.println(); } } /** * Some tests for the Equidistant Cylindrical projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testEquidistantCylindrical() throws FactoryException, TransformException { /////////////////////////////////////// // Equidistant_Cylindrical tests // /////////////////////////////////////// MathTransform transform; ParameterValueGroup params; // Approx bristol UK params = mtFactory.getDefaultParameters("Equidistant Cylindrical (Spherical)"); params.parameter("semi_major") .setValue(6378137); params.parameter("semi_minor") .setValue(6378137); params.parameter("central_meridian") .setValue( 0.000); params.parameter("standard_parallel_1").setValue( 0.000); params.parameter("false_easting") .setValue( 0.0 ); params.parameter("false_northing") .setValue( 0.0 ); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(-2.5, 51.37), new DirectPosition2D(-278298.73, 5718482.24), transform); } /** * Some tests for the Mercator Projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testMercator() throws FactoryException, TransformException { /////////////////////////////////////// // Mercator_1SP tests // /////////////////////////////////////// MathTransform transform; ParameterValueGroup params; // EPSG example (p. 26) params = mtFactory.getDefaultParameters("Mercator_1SP"); params.parameter("semi_major") .setValue(6377397.155); params.parameter("semi_minor") .setValue(6356078.963); params.parameter("central_meridian").setValue( 110.000); params.parameter("scale_factor") .setValue( 0.997); params.parameter("false_easting") .setValue(3900000.0 ); params.parameter("false_northing") .setValue( 900000.0 ); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(120.0, -3.0), new DirectPosition2D(5009726.58, 569150.82), transform); // Spherical test (Snyder p. 266) params.parameter("semi_major") .setValue( 1.0); params.parameter("semi_minor") .setValue( 1.0); params.parameter("central_meridian").setValue(-180.0); params.parameter("scale_factor") .setValue( 1.0); params.parameter("false_easting") .setValue( 0.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(-75.0, 35.0), new DirectPosition2D(1.8325957, 0.6528366), transform); // Spherical test 2 (original units for target were feet) params.parameter("semi_major") .setValue(6370997.0); params.parameter("semi_minor") .setValue(6370997.0); params.parameter("central_meridian").setValue( 0.0); params.parameter("scale_factor") .setValue( 1.0); params.parameter("false_easting") .setValue( 0.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(-123.1, 49.2166666666), new DirectPosition2D(-13688089.02443480, 6304639.84599441), transform); // Ellipsoidal with latitude of origin not zero, (simone) // TODO: Not valid for "Mercator_1SP", but could be valid for "Mercator (variant C)". params.parameter("semi_major") .setValue(6378137.0); params.parameter("semi_minor") .setValue(6356752.314245); // params.parameter("latitude_of_origin").setValue( 38.0); params.parameter("central_meridian") .setValue( 3.03); params.parameter("scale_factor") .setValue( 1.0); params.parameter("false_easting") .setValue( 0.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); // printTransform(transform); // doTransform(new DirectPosition2D(5.0, 26.996561536844165), // new DirectPosition2D(173029.94823812644, 2448819.342941506), transform); /////////////////////////////////////// // Mercator_2SP tests // /////////////////////////////////////// // EPSG p25. Note FE and FN are wrong in guide 7. Correct in epsg database v6.3. params = mtFactory.getDefaultParameters("Mercator_2SP"); params.parameter("semi_major") .setValue(6378245.000); params.parameter("semi_minor") .setValue(6356863.019); params.parameter("central_meridian") .setValue( 51.0); params.parameter("standard_parallel_1").setValue( 42.0); params.parameter("false_easting") .setValue( 0.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(53.0, 53.0), new DirectPosition2D(165704.29, 5171848.07), transform); // A spherical case (me) params = mtFactory.getDefaultParameters("Mercator_2SP"); params.parameter("semi_major") .setValue( 6370997.0); params.parameter("semi_minor") .setValue( 6370997.0); params.parameter("central_meridian") .setValue( 180.0); params.parameter("standard_parallel_1").setValue( 60.0); params.parameter("false_easting") .setValue( -500000.0); params.parameter("false_northing") .setValue(-1000000.0); transform = mtFactory.createParameterizedTransform(params); // printTransform(transform); // TODO: point too far from central meridian. // doTransform(new DirectPosition2D(-123.1, 49.2166666666), // new DirectPosition2D(2663494.1734, 2152319.9230), transform); } /** * Some tests for the Oblique Mercator Projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testObliqueMercator() throws FactoryException, TransformException { MathTransform transform; ParameterValueGroup params; params = mtFactory.getDefaultParameters("Oblique Mercator"); setObliqueMercatorParameter(params); transform = mtFactory.createParameterizedTransform(params); ParameterDescriptorGroup descriptor = ((AbstractMathTransform) transform).getParameterDescriptors(); assertTrue (IdentifiedObjects.isHeuristicMatchForName(descriptor, "Oblique Mercator")); assertFalse(IdentifiedObjects.isHeuristicMatchForName(descriptor, "Hotine Oblique Mercator")); final MathTransform standard = transform; params = mtFactory.getDefaultParameters("Hotine Oblique Mercator"); setObliqueMercatorParameter(params); transform = mtFactory.createParameterizedTransform(params); descriptor = ((AbstractMathTransform) transform).getParameterDescriptors(); assertFalse(IdentifiedObjects.isHeuristicMatchForName(descriptor, "Oblique Mercator")); assertTrue (IdentifiedObjects.isHeuristicMatchForName(descriptor, "Hotine Oblique Mercator")); assertFalse(transform.equals(standard)); } /** * For {@link #testObliqueMercator} internal use only. */ private static void setObliqueMercatorParameter(ParameterValueGroup params) { params.parameter("semi_major") .setValue(6377397.155); params.parameter("semi_minor") .setValue(6356078.963); params.parameter("longitude_of_center") .setValue( 7.439583333333333); params.parameter("latitude_of_center") .setValue(46.952405555555555); params.parameter("azimuth") .setValue(90.0); params.parameter("scale_factor") .setValue(1.0); params.parameter("false_easting") .setValue(600000.0); params.parameter("false_northing") .setValue(200000.0); params.parameter("rectified_grid_angle").setValue(90.0); } /** * Some tests for the Lambert Conic Conformal Projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testLambert() throws FactoryException, TransformException { /////////////////////////////////////// // Lambert_Conformal_Conic_1SP tests // /////////////////////////////////////// MathTransform transform; ParameterValueGroup params; // EPGS p. 18 params = mtFactory.getDefaultParameters("Lambert_Conformal_Conic_1SP"); params.parameter("semi_major") .setValue(6378206.4); params.parameter("semi_minor") .setValue(6356583.8); params.parameter("central_meridian") .setValue( -77.0); params.parameter("latitude_of_origin").setValue( 18.0); params.parameter("scale_factor") .setValue( 1.0); params.parameter("false_easting") .setValue( 250000.0); params.parameter("false_northing") .setValue( 150000.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(-76.943683333, 17.932166666), new DirectPosition2D(255966.58, 142493.51), transform); // Spherical (me) params.parameter("semi_major") .setValue(6370997.0); params.parameter("semi_minor") .setValue(6370997.0); params.parameter("central_meridian") .setValue( 111.0); params.parameter("latitude_of_origin").setValue( -55.0); params.parameter("scale_factor") .setValue( 1.0); params.parameter("false_easting") .setValue( 500000.0); params.parameter("false_northing") .setValue(1000000.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(151.283333333, -33.916666666), new DirectPosition2D(4232963.1816, 2287639.9866), transform); /////////////////////////////////////// // Lambert_Conformal_Conic_2SP tests // /////////////////////////////////////// // EPSG p. 17 params = mtFactory.getDefaultParameters("Lambert_Conformal_Conic_2SP"); params.parameter("semi_major") .setValue(6378206.4); params.parameter("semi_minor") .setValue(6356583.8); params.parameter("central_meridian") .setValue( -99.0); params.parameter("latitude_of_origin") .setValue( 27.833333333); params.parameter("standard_parallel_1").setValue( 28.383333333); params.parameter("standard_parallel_2").setValue( 30.283333333); params.parameter("false_easting") .setValue( 609601.218); //metres params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(-96.0, 28.5), new DirectPosition2D(903277.7965, 77650.94219), transform); // Spherical (me) params.parameter("semi_major") .setValue(6370997.0); params.parameter("semi_minor") .setValue(6370997.0); params.parameter("central_meridian") .setValue( -120.0); params.parameter("latitude_of_origin") .setValue( 0.0); params.parameter("standard_parallel_1").setValue( 2.0); params.parameter("standard_parallel_2").setValue( 60.0); params.parameter("false_easting") .setValue( 0.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); // printTransform(transform); // TODO: test point below is too far from central meridian. // doTransform(new DirectPosition2D(139.733333333, 35.6833333333), // new DirectPosition2D(-6789805.6471, 7107623.6859), transform); // 1SP where SP != lat of origin (me) params.parameter("semi_major") .setValue(6378137.0); params.parameter("semi_minor") .setValue(6356752.31424518); params.parameter("central_meridian") .setValue( 0.0); params.parameter("latitude_of_origin") .setValue( -50.0); params.parameter("standard_parallel_1").setValue( -40.0); params.parameter("standard_parallel_2").setValue( -40.0); params.parameter("false_easting") .setValue( 100000.0); params.parameter("false_northing") .setValue( 0.0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(18.45, -33.9166666666), new DirectPosition2D(1803288.3324, 1616657.7846), transform); /////////////////////////////////////////////// // Lambert_Conformal_Conic_2SP_Belgium test // /////////////////////////////////////////////// // EPSG p. 19 params = mtFactory.getDefaultParameters("Lambert_Conformal_Conic_2SP_Belgium"); params.parameter("semi_major") .setValue(6378388.0); params.parameter("semi_minor") .setValue(6356911.946); params.parameter("central_meridian") .setValue( 4.356939722); params.parameter("latitude_of_origin") .setValue( 90.0); params.parameter("standard_parallel_1").setValue( 49.833333333); params.parameter("standard_parallel_2").setValue( 51.166666666); params.parameter("false_easting") .setValue( 150000.01); params.parameter("false_northing") .setValue(5400088.44); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(5.807370277, 50.6795725), new DirectPosition2D(251763.20, 153034.13), transform); } /** * Some tests for Krovak Projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testKrovak() throws FactoryException, TransformException { /////////////////////////////////////// // Krovak tests // /////////////////////////////////////// MathTransform transform; ParameterValueGroup params; params = mtFactory.getDefaultParameters("Krovak"); params.parameter("semi_major") .setValue(6377397.155); params.parameter("semi_minor") .setValue(6356078.963); params.parameter("latitude_of_center") .setValue(49.5); params.parameter("longitude_of_center") .setValue(42.5 - 17.66666666666667); params.parameter("azimuth") .setValue(30.28813972222222); params.parameter("pseudo_standard_parallel_1").setValue(78.5); params.parameter("scale_factor") .setValue(0.9999); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); doTransform(new DirectPosition2D(14.370530947, 50.071153856), new DirectPosition2D(-746742.6075, -1044389.4516), transform); } /** * Some tests for Stereographic projection. * * @throws FactoryException Should never happen. * @throws TransformException Should never happen. */ @Test public void testStereographic() throws FactoryException, TransformException { /////////////////////////////////////// // Polar_Stereographic tests // /////////////////////////////////////// MathTransform transform; ParameterValueGroup params; // // http://jira.codehaus.org/browse/GEOS-1037 // params = mtFactory.getDefaultParameters("Polar_Stereographic"); params.parameter("semi_major") .setValue(6378137.0); params.parameter("semi_minor") .setValue(6356752.31424518); params.parameter("latitude_of_origin") .setValue(-90); params.parameter("central_meridian") .setValue(0); params.parameter("scale_factor") .setValue(0.97276901289); params.parameter("false_easting") .setValue(0); params.parameter("false_northing") .setValue(0); transform = mtFactory.createParameterizedTransform(params); printTransform(transform); final double[] tolerance = new double[] {0.1, 0.1}; doTransform(new DirectPosition2D(10, -85), new DirectPosition2D(94393.99, 535334.89), transform); doTransform(new DirectPosition2D(-75, -80), new DirectPosition2D(-1052066.625, 281900.375), transform, tolerance); doTransform(new DirectPosition2D(-75, -70), new DirectPosition2D(-2119718.750, 567976.875), transform, tolerance); doTransform(new DirectPosition2D(-75, -60), new DirectPosition2D(-3219560.250, 862678.563), transform, tolerance); } }