/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 1999-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. * * This package contains formulas from the PROJ package of USGS. * USGS's work is fully acknowledged here. This derived work has * been relicensed under LGPL with Frank Warmerdam's permission. */ package org.geotools.referencing.operation.projection; import static java.lang.Math.*; import java.awt.geom.Point2D; import org.geotools.metadata.iso.citation.Citations; import org.geotools.referencing.NamedIdentifier; 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; /** * Mollweide projection * * @see <A HREF="http://mathworld.wolfram.com/MollweideProjection.html">Mollweide</A> * @see <A HREF="http://en.wikipedia.org/wiki/Mollweide_projection">"Mollweide" on Wikipedia</A> * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mollweide.html">"Mollweide" on * RemoteSensing.org</A> * * @since 2.7.0 * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/Mollweide.java $ * http://svn.osgeo.org/geotools/branches/2.6.x/modules/library/referencing/src/main/java * /org/geotools/referencing/operation/projection/Mercator.java $ * @author Andrea Aime */ public class Mollweide extends MapProjection { /** * For cross-version compatibility. */ private static final long serialVersionUID = -737778661392950540L; private static final int MAX_ITER = 10; private static final double LOOP_TOL = 1e-7; double C_x, C_y, C_p; static enum ProjectionMode {Mollweide, WagnerIV, WagnerV}; ParameterDescriptorGroup descriptors; /** * 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 Mollweide(ProjectionMode mode, final ParameterDescriptorGroup descriptors, final ParameterValueGroup parameters) throws ParameterNotFoundException { super(parameters, descriptors.descriptors()); this.descriptors = descriptors; if(mode == ProjectionMode.WagnerV) { C_x = 0.90977; C_y = 1.65014; C_p = 3.00896; } else { double p; if(mode == ProjectionMode.Mollweide) { p = PI / 2; } else { p = PI / 3; } double p2 = p + p; double sp = sin(p); double r = sqrt(PI * 2 * sp / (p2 + sin(p2))); C_x = 2 * r / PI; C_y = r / sp; C_p = p2 + sin(p2); } } /** * {@inheritDoc} */ public ParameterDescriptorGroup getParameterDescriptors() { return descriptors; } /** * 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(double lam, double phi, Point2D ptDst) throws ProjectionException { double k = C_p * sin(phi); int i = MAX_ITER; for (; i > 0 ; --i) { double V = (phi + sin(phi) - k) / (1. + cos(phi)); phi -= V; if (abs(V) < LOOP_TOL) break; } if (i == 0) { phi = (phi < 0) ? -PI / 2: PI / 2; } else { phi *= 0.5; } if (ptDst == null) { ptDst = new Point2D.Double(); } ptDst.setLocation(C_x * lam * cos(phi), C_y * sin(phi)); return ptDst; } /** * Transforms the specified (<var>x</var>,<var>y</var>) coordinates and stores the result in * {@code ptDst}. */ protected Point2D inverseTransformNormalized(double x, double y, Point2D ptDst) throws ProjectionException { double phi = aasin(y / C_y); double lam = x / (C_x * cos(phi)); phi += phi; phi = aasin((phi + sin(phi)) / C_p); // the above can occasionaly result in lon out of range, normalize it lam = rollLongitude(lam); if (ptDst == null) { ptDst = new Point2D.Double(); } ptDst.setLocation(lam, phi); return ptDst; } /** * Compares the specified object with this map projection for equality. */ @Override public boolean equals(final Object object) { if (object == this) { // Slight optimization return true; } return super.equals(object); } @Override protected double getToleranceForAssertions(double longitude, double latitude) { // the Robinson projection is meant for world-wide displays, don't be picky return 2; } // //////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////// // ////// //////// // ////// PROVIDERS //////// // ////// //////// // //////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////// /** * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform * provider} for the Mollweide projection (not part of the EPSG database). * * @since 2.7.0 * @author Andrea Aime * * @see org.geotools.referencing.operation.DefaultMathTransformFactory */ public static class MollweideProvider extends AbstractProvider { /** * For cross-version compatibility. */ private static final long serialVersionUID = -2616680275771881688L; /** * The parameters group. */ static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup( new NamedIdentifier[] { new NamedIdentifier(Citations.GEOTOOLS, "Mollweide"), new NamedIdentifier(Citations.ESRI, "Mollweide") }, new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, CENTRAL_MERIDIAN }); /** * Constructs a new provider. */ public MollweideProvider() { super(PARAMETERS); } /** * Creates a transform from the specified group of parameter values. * * @param parameters * The group of parameter values. * @return The created math transform. * @throws ParameterNotFoundException * if a required parameter was not found. */ protected MathTransform createMathTransform(final ParameterValueGroup parameters) throws ParameterNotFoundException { parameters.parameter("semi_minor").setValue(parameters.parameter("semi_major").getValue()); return new Mollweide(ProjectionMode.Mollweide, PARAMETERS, parameters); } } /** * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform * provider} for the Wagner IV projection (not part of the EPSG database). * * @since 2.7.0 * @author Andrea Aime * * @see org.geotools.referencing.operation.DefaultMathTransformFactory */ public static class WagnerIVProvider extends AbstractProvider { /** * For cross-version compatibility. */ private static final long serialVersionUID = 1079407274370647753L; /** * The parameters group. */ static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup( new NamedIdentifier[] { new NamedIdentifier(Citations.GEOTOOLS, "Wagner_IV") }, new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, CENTRAL_MERIDIAN }); /** * Constructs a new provider. */ public WagnerIVProvider() { super(PARAMETERS); } /** * Creates a transform from the specified group of parameter values. * * @param parameters * The group of parameter values. * @return The created math transform. * @throws ParameterNotFoundException * if a required parameter was not found. */ protected MathTransform createMathTransform(final ParameterValueGroup parameters) throws ParameterNotFoundException { // make sure we assume a spherical reference parameters.parameter("semi_minor").setValue(parameters.parameter("semi_major").getValue()); return new Mollweide(ProjectionMode.WagnerIV, PARAMETERS, parameters); } } /** * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform * provider} for the Wagner V projection (not part of the EPSG database). * * @since 2.7.0 * @author Andrea Aime * * @see org.geotools.referencing.operation.DefaultMathTransformFactory */ public static class WagnerVProvider extends AbstractProvider { /** * For cross-version compatibility. */ private static final long serialVersionUID = -3583284443974045930L; /** * The parameters group. */ static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup( new NamedIdentifier[] { new NamedIdentifier(Citations.GEOTOOLS, "Wagner_V") }, new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, CENTRAL_MERIDIAN }); /** * Constructs a new provider. */ public WagnerVProvider() { super(PARAMETERS); } /** * Creates a transform from the specified group of parameter values. * * @param parameters * The group of parameter values. * @return The created math transform. * @throws ParameterNotFoundException * if a required parameter was not found. */ protected MathTransform createMathTransform(final ParameterValueGroup parameters) throws ParameterNotFoundException { // make sure we assume a spherical reference parameters.parameter("semi_minor").setValue(parameters.parameter("semi_major").getValue()); return new Mollweide(ProjectionMode.WagnerV, PARAMETERS, parameters); } } }