/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, 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.projection; import java.util.Collection; import java.awt.geom.Point2D; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.ReferenceIdentifier; import org.geotools.math.Complex; import org.geotools.referencing.NamedIdentifier; import org.geotools.metadata.iso.citation.Citations; import static java.lang.Math.*; /** * The NZMG (New Zealand Map Grid) projection. * <p> * This is an implementation of algorithm published by * <a href="http://www.govt.nz/record?recordid=28">Land Information New Zealand</a>. * The algorithm is documented <a href="http://www.linz.govt.nz/rcs/linz/6137/">here</a>. * <p> * <b>Implementation note</b><br> * This class make extensive use of {@link Complex} type which may be costly unless the compiler * can inline on the stack. We assume that Jave 6 and above can do this optimization. * * @since 2.2 * * @source $URL$ * @version $Id$ * @author Justin Deoliveira * @author Martin Desruisseaux */ public class NewZealandMapGrid extends MapProjection { /** * For compatibility with different versions during deserialization. */ private static final long serialVersionUID = 8394817836243729133L; /** * Coefficients for forward and inverse projection. */ private static final Complex[] A = { new Complex( 0.7557853228, 0.0 ), new Complex( 0.249204646, 0.003371507 ), new Complex( -0.001541739, 0.041058560 ), new Complex( -0.10162907, 0.01727609 ), new Complex( -0.26623489, -0.36249218 ), new Complex( -0.6870983, -1.1651967 ) }; /** * Coefficients for inverse projection. */ private static final Complex[] B = { new Complex( 1.3231270439, 0.0 ), new Complex( -0.577245789, -0.007809598 ), new Complex( 0.508307513, -0.112208952 ), new Complex( -0.15094762, 0.18200602 ), new Complex( 1.01418179, 1.64497696 ), new Complex( 1.9660549, 2.5127645 ) }; /** * Coefficients for inverse projection. */ private static final double[] TPHI = new double[] { 1.5627014243, 0.5185406398, -0.03333098, -0.1052906, -0.0368594, 0.007317, 0.01220, 0.00394, -0.0013 }; /** * Coefficients for forward projection. */ private static final double[] TPSI = new double[] { 0.6399175073, -0.1358797613, 0.063294409, -0.02526853, 0.0117879, -0.0055161, 0.0026906, -0.001333, 0.00067, -0.00034 }; /** * Constructs a new map projection with default parameter values. */ protected NewZealandMapGrid() { this(Provider.PARAMETERS.createValue()); } /** * Constructs a new map projection from the supplied parameters. * * @param parameters The parameter values in standard units. * @throws ParameterNotFoundException if a mandatory parameter is missing. */ protected NewZealandMapGrid(final ParameterValueGroup parameters) throws ParameterNotFoundException { super(parameters); } /** * {@inheritDoc} */ public ParameterDescriptorGroup getParameterDescriptors() { return Provider.PARAMETERS; } /** * Must be overridden because {@link Provider} uses instances of * {@link ModifiedParameterDescriptor}. This hack was needed because the New Zeland map * projection uses particular default values for parameters like "False Easting", etc. */ @Override final boolean isExpectedParameter(final Collection<GeneralParameterDescriptor> expected, final ParameterDescriptor param) { return ModifiedParameterDescriptor.contains(expected, param); } /** * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates * (units in radians) and stores the result in {@code ptDst} (linear distance * on a unit sphere). */ protected Point2D transformNormalized(final double x, final double y, final Point2D ptDst) throws ProjectionException { final double dphi = (y - latitudeOfOrigin) * (180/PI * 3600E-5); double dphi_pow_i = dphi; double dpsi = 0; for (int i=0; i<TPSI.length; i++) { dpsi += (TPSI[i] * dphi_pow_i); dphi_pow_i *= dphi; } // See implementation note in class javadoc. final Complex theta = new Complex(dpsi, x); final Complex power = new Complex(theta); final Complex z = new Complex(); z.multiply(A[0], power); for (int i=1; i<A.length; i++) { power.multiply(power, theta); z.addMultiply(z, A[i], power); } if (ptDst != null) { ptDst.setLocation(z.imag, z.real); return ptDst; } return new Point2D.Double(z.imag, z.real); } /** * Transforms the specified (<var>x</var>,<var>y</var>) coordinates * and stores the result in {@code ptDst}. */ protected Point2D inverseTransformNormalized(final double x, final double y, final Point2D ptDst) throws ProjectionException { // See implementation note in class javadoc. final Complex z = new Complex(y, x); final Complex power = new Complex(z); final Complex theta = new Complex(); theta.multiply(B[0], z); for (int j=1; j<B.length; j++) { power.multiply(power, z); theta.addMultiply(theta, B[j], power); } // Increasing the number of iterations through this loop decreases // the error in the calculation, but 3 iterations gives 10-3 accuracy. final Complex num = new Complex(); final Complex denom = new Complex(); final Complex t = new Complex(); for (int j=0; j<3; j++) { power.power(theta, 2); num.addMultiply(z, A[1], power); for (int k=2; k<A.length; k++) { power.multiply(power, theta); t.multiply(A[k], power); t.multiply(t, k); num.add(num, t); } power.real = 1; power.imag = 0; denom.copy(A[0]); for (int k=1; k<A.length; k++) { power.multiply(power, theta); t.multiply(A[k], power); t.multiply(t, k+1); denom.add(denom, t); } theta.divide(num, denom); } final double dpsi = theta.real; double dpsi_pow_i = dpsi; double dphi = TPHI[0] * dpsi; for (int i=1; i<TPHI.length; i++) { dpsi_pow_i *= dpsi; dphi += (TPHI[i] * dpsi_pow_i); } dphi = dphi / (180/PI * 3600E-5) + latitudeOfOrigin; if (ptDst != null) { ptDst.setLocation(theta.imag, dphi); return ptDst; } return new Point2D.Double(theta.imag, dphi); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// //////// //////// //////// PROVIDERS //////// //////// //////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// /** * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform * provider} for {@linkplain NewZealandMapGrid New Zealand Map Grid} (EPSG code 27200). * * @since 2.2 * @version $Id$ * @author Justin Deoliveira * * @see org.geotools.referencing.operation.DefaultMathTransformFactory */ public static class Provider extends AbstractProvider { /** * For compatibility with different versions during deserialization. */ private static final long serialVersionUID = -7716733400419275656L; /** * The parameters group. */ static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup( new ReferenceIdentifier[] { new NamedIdentifier(Citations.OGC, "New_Zealand_Map_Grid"), new NamedIdentifier(Citations.EPSG, "New Zealand Map Grid"), new NamedIdentifier(Citations.EPSG, "27200") }, new ParameterDescriptor[] { new ModifiedParameterDescriptor(SEMI_MAJOR, 6378388.0), new ModifiedParameterDescriptor(SEMI_MINOR, 6378388.0*(1-1/297.0)), new ModifiedParameterDescriptor(LATITUDE_OF_ORIGIN, -41.0), new ModifiedParameterDescriptor(CENTRAL_MERIDIAN, 173.0), new ModifiedParameterDescriptor(FALSE_EASTING, 2510000.0), new ModifiedParameterDescriptor(FALSE_NORTHING, 6023150.0) }); /** * Constructs a new provider. */ public Provider() { super(PARAMETERS); } /** * Creates a transform from the specified group of parameter values. This method doesn't * check for the spherical case, since the New Zealand Map Grid projection is used with * the International 1924 ellipsoid. * * @param parameters The group of parameter values. * @return The created math transform. * @throws ParameterNotFoundException if a required parameter was not found. */ public MathTransform createMathTransform(final ParameterValueGroup parameters) throws ParameterNotFoundException { return new NewZealandMapGrid(parameters); } } }