/* * 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 java.awt.geom.AffineTransform; import org.opengis.util.FactoryException; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.NoninvertibleTransformException; import org.junit.*; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.internal.referencing.j2d.AffineTransform2D; import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D; import static org.geotoolkit.test.Assert.*; import static org.geotoolkit.test.Commons.*; /** * Tests WKT formatting. This test suite do not execute any coordinate transformation. * However it relies extensively on {@link AbstractMathTransform2D} implementation, * which should be intelligent enough for recognizing that the concatenated transform * was created from a single set of parameters. * * @author Martin Desruisseaux (Geomatys) * * @since 3.00 */ public final strictfp class FormattingTest extends ProjectionTestBase { /** * Creates a default test suite. */ public FormattingTest() { super(AbstractMathTransform2D.class, null); } /** * Tests using Mercator projections. We use the same transforms than the one defined * in {@link MercatorTest#testMercator1SP} and {@link MercatorTest#testMercator2SP} * except that the semi-minor axis length is defined explicitly for safety. * * @throws FactoryException Should never happen. * @throws NoninvertibleTransformException Should never happen. */ @Test @Ignore public void testMercator() throws FactoryException, NoninvertibleTransformException { ParameterValueGroup parameters = mtFactory.getDefaultParameters("Mercator_1SP"); parameters.parameter("semi-major axis").setValue(6377397.155); parameters.parameter("semi-minor axis").setValue(6356078.9626186555); parameters.parameter("Longitude of natural origin").setValue(110.0); parameters.parameter("Scale factor at natural origin").setValue(0.997); parameters.parameter("False easting").setValue(3900000.0); parameters.parameter("False northing").setValue(900000.0); transform = mtFactory.createParameterizedTransform(parameters); assertTrue(isInverseTransformSupported); String wkt; wkt = "PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "INVERSE_MT[PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); transform = transform.inverse(); } /* * Add axis swap from (latitude, longitude) order to (longitude, latitude) order. */ final AffineTransform2D swap = new AffineTransform2D(0, 1, 1, 0, 0, 0); transform = MathTransforms.concatenate(swap, transform); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]],\n" + " PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "CONCAT_MT[INVERSE_MT[PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); transform = transform.inverse(); } /* * Add unit conversions from metres to kilometres. */ final AffineTransform2D convert = new AffineTransform2D(0.001, 0, 0, 0.001, 0, 0); transform = MathTransforms.concatenate(transform, convert); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]],\n" + " PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.001],\n" + " PARAMETER[“elt_1_1”, 0.001]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 1000.0],\n" + " PARAMETER[“elt_1_1”, 1000.0]],\n" + " INVERSE_MT[PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]]]"; assertWktEquals(wkt); // Can not test the full object because of slight rounding error // (last digit in the translateY term of an affine transform). assertMultilinesEquals(wkt, mtFactory.createFromWKT(wkt).toWKT()); transform = transform.inverse(); } /* * At this point the first transform has been fully created. Save it for future reference * and now process to the same tests for Mercator2SP (except that we will concatenate the * "swap" and "convert" affines in the opposite order). */ final MathTransform first = transform; parameters = mtFactory.getDefaultParameters("Mercator_2SP"); parameters.parameter("semi-major axis").setValue(6378245.0); parameters.parameter("semi-minor axis").setValue(6356863.018773047); parameters.parameter("Latitude of 1st standard parallel").setValue(42.0); parameters.parameter("Longitude of natural origin").setValue(51.0); transform = mtFactory.createParameterizedTransform(parameters); wkt = "PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "INVERSE_MT[PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); transform = transform.inverse(); } /* * Add unit conversions from metres to kilometres. */ transform = MathTransforms.concatenate(transform, convert); wkt = "CONCAT_MT[PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.001],\n" + " PARAMETER[“elt_1_1”, 0.001]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 1000.0],\n" + " PARAMETER[“elt_1_1”, 1000.0]],\n" + " INVERSE_MT[PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); transform = transform.inverse(); } /* * Add axis swap from (latitude, longitude) order to (longitude, latitude) order. */ transform = MathTransforms.concatenate(swap, transform); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]],\n" + " PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.001],\n" + " PARAMETER[“elt_1_1”, 0.001]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); if (isInverseTransformSupported) { assertSame(transform, transform.inverse().inverse()); transform = transform.inverse(); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 1000.0],\n" + " PARAMETER[“elt_1_1”, 1000.0]],\n" + " INVERSE_MT[PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.0],\n" + " PARAMETER[“elt_0_1”, 1.0],\n" + " PARAMETER[“elt_1_0”, 1.0],\n" + " PARAMETER[“elt_1_1”, 0.0]]]"; assertWktEquals(wkt); assertEquals(transform, mtFactory.createFromWKT(wkt)); transform = transform.inverse(); } /* * Now the interesting part: concatenate the two transforms. We ignore the change of * ellipsoid, which is completely wrong but our intend here is to test concatenation, * not datum change. The referencing framework just concatenates what we said without * question. Note that while the WKT we get here seems rather straightforward, the * internal storage is actually quite different (invoke "printInternalWKT() to see). */ final MathTransform second = transform; transform = MathTransforms.concatenate(first.inverse(), second); wkt = "CONCAT_MT[PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 1000.0],\n" + " PARAMETER[“elt_1_1”, 1000.0]],\n" + " INVERSE_MT[PARAM_MT[“Mercator_1SP”,\n" + " PARAMETER[“semi_major”, 6377397.155],\n" + " PARAMETER[“semi_minor”, 6356078.9626186555],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 110.0],\n" + " PARAMETER[“scale_factor”, 0.997],\n" + " PARAMETER[“false_easting”, 3900000.0],\n" + " PARAMETER[“false_northing”, 900000.0]]],\n" + " PARAM_MT[“Mercator_2SP”,\n" + " PARAMETER[“semi_major”, 6378245.0],\n" + " PARAMETER[“semi_minor”, 6356863.018773047],\n" + " PARAMETER[“standard_parallel_1”, 42.0],\n" + " PARAMETER[“latitude_of_origin”, 0.0],\n" + " PARAMETER[“central_meridian”, 51.0],\n" + " PARAMETER[“false_easting”, 0.0],\n" + " PARAMETER[“false_northing”, 0.0]],\n" + " PARAM_MT[“Affine”,\n" + " PARAMETER[“num_row”, 3],\n" + " PARAMETER[“num_col”, 3],\n" + " PARAMETER[“elt_0_0”, 0.001],\n" + " PARAMETER[“elt_1_1”, 0.001]]]"; assertWktEquals(wkt); // Can not test the full object because of slight rounding error assertMultilinesEquals(wkt, mtFactory.createFromWKT(wkt).toWKT()); /* * Sets the axis lengths to the same value than the first projection, so the concatenation * can recognize the two unitary projections as equivalent. The referencing module should * be able to simplify all this big stuff to a single affine transform. Note that the * "Longitude of natural origin" still differents. In the particular case of the Mercator * projection, a concatenation still possible. */ parameters.parameter("semi-major axis").setValue(6377397.155); parameters.parameter("semi-minor axis").setValue(6356078.9626186555); transform = mtFactory.createParameterizedTransform(parameters); transform = MathTransforms.concatenate(swap, transform, convert); transform = MathTransforms.concatenate(first.inverse(), transform); assertInstanceOf("Concatenation should have been optimized.", AffineTransform.class, transform); final AffineTransform at = (AffineTransform) transform; tolerance = 1E-10; assertEquals(0.0, at.getShearX(), tolerance); assertEquals(0.0, at.getShearY(), tolerance); assertEquals(0.7464972023333, at.getScaleX(), tolerance); assertEquals(0.7464972023333, at.getScaleY(), tolerance); assertEquals(1976.2668705583, at.getTranslateX(), tolerance); assertEquals(-671.8474821000, at.getTranslateY(), tolerance); } /** * Tests a WKT involving axis swapping. This is a way to verify that the * {@link org.geotoolkit.internal.referencing.Semaphores#PROJCS} special case * is correctly handled. * * @throws FactoryException Should never happen. */ @Test @Ignore public void testAxisSwapping() throws FactoryException { String wkt = decodeQuotes( "PROJCS[“OSGB 1936 / British National Grid”,\n" + " GEOGCS[“OSGB 1936”,DATUM[“OSGB_1936”,\n" + " SPHEROID[“Airy 1830”,6377563.396,299.3249646,AUTHORITY[“EPSG”, “7001”]],\n" + " TOWGS84[375,-111,431,0,0,0,0],AUTHORITY[“EPSG”, “6277”]],\n" + " PRIMEM[“Greenwich”,0,AUTHORITY[“EPSG”, “8901”]],\n" + " UNIT[“DMSH”,0.0174532925199433],\n" + " AXIS[“Lat”,NORTH],AXIS[“Long”,EAST],AUTHORITY[“EPSG”, “4277”]],\n" + " PROJECTION[“Transverse_Mercator”],\n" + " PARAMETER[“latitude_of_origin”,49],\n" + " PARAMETER[“central_meridian”,-2],\n" + " PARAMETER[“scale_factor”,0.999601272],\n" + " PARAMETER[“false_easting”,400000],\n" + " PARAMETER[“false_northing”,-100000],\n" + " UNIT[“metre”,1,AUTHORITY[“EPSG”, “9001”]],\n" + " AXIS[“E”,EAST],AXIS[“N”,NORTH],AUTHORITY[“EPSG”, “27700”]]"); final ProjectedCRS parsedCRS = (ProjectedCRS) crsFactory.createFromWKT(wkt); wkt = parsedCRS.toWKT(); final ProjectedCRS parsedAgain = (ProjectedCRS) crsFactory.createFromWKT(wkt); /* * In an older version, the WKT was not properly formatted because the axis swapping * inside the inner GEOCS element was causing confusion in ConcatenatedTransform and * DefaultSingleOperation. This bug could be reproduced by parsing a WKT, formatting * it and reparsing it again. * * Set the following condition to "true" if debugging is needed. */ if (false) { transform = parsedCRS .getConversionFromBase().getMathTransform(); printInternalWKT(); transform = parsedAgain.getConversionFromBase().getMathTransform(); printInternalWKT(); } assertEquals(parsedCRS, parsedAgain); assertSame(parsedCRS, parsedAgain); } }