Author: aaime Date: 2012-03-03 02:21:13 -0800 (Sat, 03 Mar 2012) New Revision: 38593 Added: trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/SimilarityTransformProvider.java trunk/modules/library/referencing/src/test/java/org/geotools/referencing/operation/transform/SimilarityTransformProviderTest.java Modified: trunk/modules/library/referencing/src/main/resources/META-INF/services/org.geotools.referencing.operation.MathTransformProvider trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/CRSTest.java trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/factory/epsg/PropertyDumper.java Log: [GEOT-4050] Similarity Transformation, EPSG:9621, patch by Oscar Fonts Added: trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/SimilarityTransformProvider.java =================================================================== --- trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/SimilarityTransformProvider.java (rev 0) +++ trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/SimilarityTransformProvider.java 2012-03-03 10:21:13 UTC (rev 38593) @@ -0,0 +1,151 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2012, Open Source Geospatial Foundation (OSGeo) + * + * 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.geotools.referencing.operation.transform; + +import javax.measure.quantity.Dimensionless; +import javax.measure.unit.NonSI; +import javax.measure.unit.SI; + +import org.geotools.metadata.iso.citation.Citations; +import org.geotools.referencing.NamedIdentifier; +import org.geotools.referencing.operation.MathTransformProvider; +import org.geotools.referencing.operation.transform.AffineTransform2D; +import org.opengis.parameter.ParameterDescriptor; +import org.opengis.parameter.ParameterDescriptorGroup; +import org.opengis.parameter.ParameterNotFoundException; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.referencing.operation.MathTransform; + +/** + * The provider for the "<cite>Similarity transformation</cite>" (EPSG 9621). + * <p> + * Note that similarity transform is a special case of an Affine transform 2D. + * + * @source $URL$ + * @version $Id$ + * @author Oscar Fonts + */ +public class SimilarityTransformProvider extends MathTransformProvider { + + private static final long serialVersionUID = -7413519919588731455L; + + // TODO: TRANSLATION_1 and TRANSLATION_2 should be expressed in "target CRS units", not necessarily SI.METER. + + /** + * "Ordinate 1 of evaluation point in target CRS" EPSG::8621 + */ + public static final ParameterDescriptor<Double> TRANSLATION_1 = createDescriptor( + new NamedIdentifier[] { + new NamedIdentifier(Citations.EPSG, "Ordinate 1 of evaluation point in target CRS"), + new NamedIdentifier(Citations.EPSG, "8621") + }, + 0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METER); + + /** + * "Ordinate 2 of evaluation point in target CRS" EPSG::8622 + */ + public static final ParameterDescriptor<Double> TRANSLATION_2 = createDescriptor( + new NamedIdentifier[] { + new NamedIdentifier(Citations.EPSG, "Ordinate 2 of evaluation point in target CRS"), + new NamedIdentifier(Citations.EPSG, "8622") + }, + 0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METER); + + /** + * "Scale difference" EPSG::8611 + */ + public static final ParameterDescriptor<Double> SCALE = createDescriptor( + new NamedIdentifier[] { + new NamedIdentifier(Citations.EPSG, "Scale difference"), + new NamedIdentifier(Citations.EPSG, "8611") + }, + 1, Double.MIN_NORMAL, Double.POSITIVE_INFINITY, Dimensionless.UNIT); + + /** + * "Rotation angle of source coordinate reference system axes" EPSG::8614 + */ + public static final ParameterDescriptor<Double> ROTATION = createDescriptor( + new NamedIdentifier[] { + new NamedIdentifier(Citations.EPSG, "Rotation angle of source coordinate reference system axes"), + new NamedIdentifier(Citations.EPSG, "8614") + }, + 0, 0, 360 * 60 * 60, NonSI.SECOND_ANGLE); + + /** + * The parameter group for "Similarity transformation" EPSG::9621. + * + * Includes {@link #TRANSLATION_1}, {@link #TRANSLATION_2}, {@link #SCALE}, {@link #ROTATION}. + */ + static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup( + new NamedIdentifier[] { + new NamedIdentifier(Citations.EPSG, "Similarity transformation"), + new NamedIdentifier(Citations.EPSG, "9621") + }, + new ParameterDescriptor[] { + TRANSLATION_1, + TRANSLATION_2, + SCALE, + ROTATION + } + ); + + /** + * Creates a two-dimensional similarity transform. + * + * EPSG defines explicitly this transform as 2D. + */ + public SimilarityTransformProvider() { + super(2, 2, PARAMETERS); + } + + /** + * Constructs an {@link AffineTransform2D} math transform from the specified group of parameter values. + * + * The similarity transform is a particular case of Affine Transform 2D where: + * + * <blockquote><pre> + * m00 = SCALE * cos(ROTATION) + * m01 = SCALE * sin(ROTATION) + * m02 = TRANSLATION_1 + * m10 = -m01 + * m11 = m00 + * m12 = TRANSLATION_2 + * </pre></blockquote> + * + * @param values The group of parameter values {@link #PARAMETERS}. + * @return an {@link AffineTransform2D}. + * @throws ParameterNotFoundException if a required parameter was not found. + */ + protected MathTransform createMathTransform(ParameterValueGroup values) + throws ParameterNotFoundException { + + // The four parameters + double t1 = doubleValue(TRANSLATION_1, values); + double t2 = doubleValue(TRANSLATION_2, values); + double scale = doubleValue(SCALE, values); + double rotation = doubleValue(ROTATION, values); + + // Calculate affine transform coefficients + double theta = Math.PI * rotation / 648000; // arcsec to rad + double p1 = scale * Math.cos(theta); + double p2 = scale * Math.sin(theta); + + return new AffineTransform2D(p1, -p2, p2, p1, t1, t2); + } + +} + Modified: trunk/modules/library/referencing/src/main/resources/META-INF/services/org.geotools.referencing.operation.MathTransformProvider =================================================================== --- trunk/modules/library/referencing/src/main/resources/META-INF/services/org.geotools.referencing.operation.MathTransformProvider 2012-03-01 14:15:12 UTC (rev 38592) +++ trunk/modules/library/referencing/src/main/resources/META-INF/services/org.geotools.referencing.operation.MathTransformProvider 2012-03-03 10:21:13 UTC (rev 38593) @@ -10,6 +10,7 @@ org.geotools.referencing.operation.transform.MolodenskiTransform$Provider org.geotools.referencing.operation.transform.MolodenskiTransform$ProviderAbridged org.geotools.referencing.operation.transform.NADCONTransform$Provider +org.geotools.referencing.operation.transform.SimilarityTransformProvider org.geotools.referencing.operation.transform.WarpTransform2D$Provider org.geotools.referencing.operation.projection.EquidistantCylindrical$Provider org.geotools.referencing.operation.projection.EquidistantCylindrical$SphericalProvider Added: trunk/modules/library/referencing/src/test/java/org/geotools/referencing/operation/transform/SimilarityTransformProviderTest.java =================================================================== --- trunk/modules/library/referencing/src/test/java/org/geotools/referencing/operation/transform/SimilarityTransformProviderTest.java (rev 0) +++ trunk/modules/library/referencing/src/test/java/org/geotools/referencing/operation/transform/SimilarityTransformProviderTest.java 2012-03-03 10:21:13 UTC (rev 38593) @@ -0,0 +1,55 @@ +package org.geotools.referencing.operation.transform; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import org.geotools.parameter.ParameterGroup; +import org.geotools.referencing.operation.DefaultMathTransformFactory; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.TransformException; + +/** + * Tests {@link SimilarityTransformProvider}. + * + * @source $URL$ + * @version $Id$ + * @author Oscar Fonts + */ +public final class SimilarityTransformProviderTest { + + /** + * Tests {@link SimilarityTransformProvider}, both direct and inverse transfoms. + */ + @Test + public void testSimilarityTransform() throws FactoryException, TransformException { + + // Parameters from EPSG::5166 transform + ParameterGroup params = new ParameterGroup(SimilarityTransformProvider.PARAMETERS); + params.parameter("8621").setValue(-129.549); + params.parameter("8622").setValue(-208.185); + params.parameter("8611").setValue(1.0000015504); + params.parameter("8614").setValue(1.56504); + + // Transform instance + MathTransform mt = new DefaultMathTransformFactory().createParameterizedTransform(params); + + // Data from EPSG::9621 example + final double precision = 1E-3; // dstPoints have 3 decimal units + final double[] srcPoints = new double[] {300000, 4500000}; + final double[] dstPoints = new double[] {299905.060, 4499796.515}; + final double[] calculatedPoints = new double[srcPoints.length]; + + // Direct transform test + mt.transform(srcPoints, 0, calculatedPoints, 0, srcPoints.length/2); + for (int i=0; i<calculatedPoints.length; i++) { + assertEquals(dstPoints[i], calculatedPoints[i], precision); + } + + // Inverse transform test + mt.inverse().transform(dstPoints, 0, calculatedPoints, 0, dstPoints.length/2); + for (int i=0; i<calculatedPoints.length; i++) { + assertEquals(srcPoints[i], calculatedPoints[i], precision); + } + } + +} Modified: trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/CRSTest.java =================================================================== --- trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/CRSTest.java 2012-03-01 14:15:12 UTC (rev 38592) +++ trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/CRSTest.java 2012-03-03 10:21:13 UTC (rev 38593) @@ -413,6 +413,30 @@ } finally { Hints.removeSystemDefault(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER); } - } + + /** + * Tests similarity transform on the example provided in the EPSG projection guide, page 99 + * @throws Exception + */ + public void testSimilarityTransform() throws Exception { + // Tombak LNG Plant + CoordinateReferenceSystem tombak = CRS.decode("EPSG:5817", true); + // Nakhl-e Ghanem / UTM zone 39N + CoordinateReferenceSystem ng39 = CRS.decode("EPSG:3307", true); + + // forward + double[] src = new double[] {20000, 10000}; + double[] dst = new double[2]; + MathTransform mt = CRS.findMathTransform(tombak, ng39); + mt.transform(src, 0, dst, 0, 1); + + assertEquals(618336.748, dst[0], 0.001); + assertEquals(3067774.210, dst[1], 0.001); + + // and back + mt.inverse().transform(dst, 0, src, 0, 1); + assertEquals(20000, src[0], 0.001); + assertEquals(10000, src[1], 0.001); + } } Modified: trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/factory/epsg/PropertyDumper.java =================================================================== --- trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/factory/epsg/PropertyDumper.java 2012-03-01 14:15:12 UTC (rev 38592) +++ trunk/modules/plugin/epsg-hsql/src/test/java/org/geotools/referencing/factory/epsg/PropertyDumper.java 2012-03-03 10:21:13 UTC (rev 38593) @@ -51,7 +51,6 @@ List<String> codes = new ArrayList(CRS.getSupportedCodes("EPSG")); Collections.sort(codes, new Comparator<String>() { - @Override public int compare(String c1, String c2) { try { Long n1 = new Long(c1); |