/*
* 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;
/**
* Eckert IV projection
*
* @see <A HREF="http://mathworld.wolfram.com/EckertIVProjection.html">Robinson projection on
* MathWorld</A>
* @see <A HREF="http://www.equal-area-maps.com/info_eckert.php">"Eckert IV" on the Equal Area Maps
* web site</A>
* @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/eckert_iv.html">"Robinson" 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/EckertIV.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 EckertIV extends MapProjection {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 1635471013603509976L;
private static final double C_x = 0.42223820031577120149;
private static final double C_y = 1.32650042817700232218;
private static final double C_p = 3.57079632679489661922;
private static final double RC_p = 0.28004957675577868795;
private static final double EPS = 1e-7;
private static final int NITER = 6;
/**
* 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 EckertIV(final ParameterValueGroup parameters) throws ParameterNotFoundException {
super(parameters);
}
/**
* {@inheritDoc}
*/
public ParameterDescriptorGroup getParameterDescriptors() {
return Provider.PARAMETERS;
}
/**
* 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 p = C_p * sin(phi);
double V = phi * phi;
phi *= 0.895168 + V * (0.0218849 + V * 0.00826809);
int i = NITER;
for (; i > 0; --i) {
double c = cos(phi);
double s = sin(phi);
phi -= V = (phi + s * (c + 2d) - p) / (1d + c * (c + 2d) - s * s);
if (abs(V) < EPS) {
break;
}
}
if (ptDst == null) {
ptDst = new Point2D.Double();
}
if (i == 0) {
ptDst.setLocation(C_x * lam, phi < 0. ? -C_y : C_y);
} else {
ptDst.setLocation(C_x * lam * (1. + 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 c = cos(phi);
double lam = x / (C_x * (1. + c));
phi = aasin((phi + sin(phi) * (c + 2.)) / C_p);
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 Eckert IV projection (not part of the EPSG database).
*
* @since 2.7.0
* @author Andrea Aime
*
* @see org.geotools.referencing.operation.DefaultMathTransformFactory
*/
public static class Provider extends AbstractProvider {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 1136453952351519284L;
/**
* The parameters group.
*/
static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
new NamedIdentifier[] { new NamedIdentifier(Citations.GEOTOOLS, "Eckert_IV"),
new NamedIdentifier(Citations.ESRI, "Eckert_IV") },
new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, CENTRAL_MERIDIAN });
/**
* Constructs a new provider.
*/
public Provider() {
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 EckertIV(parameters);
}
}
}