001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2000, Frank Warmerdam
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * This package contains formulas from the PROJ package of USGS.
019: * USGS's work is fully acknowledged here. This derived work has
020: * been relicensed under LGPL with Frank Warmerdam's permission.
021: */
022: package org.geotools.referencing.operation.projection;
023:
024: // J2SE dependencies and extensions
025: import java.awt.geom.Point2D;
026:
027: // OpenGIS dependencies
028: import org.opengis.parameter.ParameterNotFoundException;
029: import org.opengis.parameter.ParameterValueGroup;
030:
031: // Geotools dependencies
032: import org.geotools.resources.i18n.Errors;
033: import org.geotools.resources.i18n.ErrorKeys;
034:
035: /**
036: * The equatorial case of the {@link Orthographic} projection. This is a
037: * simplification of the oblique case for {@link #latitudeOfOrigin} == 0.0.
038: * Only the spherical form is given here.
039: *
040: * @todo this code is identical to the oblique except for 6 lines.
041: * It could be moved to the oblique with an isEquatorial flag.
042: *
043: * @since 2.4
044: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/EquatorialOrthographic.java $
045: * @version $Id: EquatorialOrthographic.java 24578 2007-02-24 00:57:19Z desruisseaux $
046: * @author Rueben Schulz
047: */
048: public class EquatorialOrthographic extends ObliqueOrthographic {
049: /**
050: * Maximum difference allowed when comparing real numbers.
051: */
052: private static final double EPSILON = 1E-6;
053:
054: /**
055: * Constructs an equatorial orthographic projection.
056: *
057: * @param parameters The parameter values in standard units.
058: * @throws ParameterNotFoundException if a mandatory parameter is missing.
059: */
060: protected EquatorialOrthographic(
061: final ParameterValueGroup parameters)
062: throws ParameterNotFoundException {
063: super (parameters);
064: ensureLatitudeEquals(Provider.LATITUDE_OF_ORIGIN,
065: latitudeOfOrigin, 0);
066: latitudeOfOrigin = 0.0;
067: ensureSpherical();
068: }
069:
070: /**
071: * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates
072: * (units in radians) and stores the result in {@code ptDst} (linear distance
073: * on a unit sphere).
074: */
075: protected Point2D transformNormalized(double x, double y,
076: Point2D ptDst) throws ProjectionException {
077: // Compute using oblique formulas, for comparaison later.
078: assert (ptDst = super .transformNormalized(x, y, ptDst)) != null;
079:
080: final double cosphi = Math.cos(y);
081: final double coslam = Math.cos(x);
082:
083: if (cosphi * coslam < -EPSILON) {
084: throw new ProjectionException(Errors
085: .format(ErrorKeys.POINT_OUTSIDE_HEMISPHERE));
086: }
087:
088: y = Math.sin(y);
089: x = cosphi * Math.sin(x);
090:
091: assert checkTransform(x, y, ptDst);
092: if (ptDst != null) {
093: ptDst.setLocation(x, y);
094: return ptDst;
095: }
096: return new Point2D.Double(x, y);
097: }
098:
099: /**
100: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
101: * and stores the result in {@code ptDst}.
102: */
103: protected Point2D inverseTransformNormalized(double x, double y,
104: Point2D ptDst) throws ProjectionException {
105: // Compute using oblique formulas, for comparaison later.
106: assert (ptDst = super .inverseTransformNormalized(x, y, ptDst)) != null;
107:
108: final double rho = Math.sqrt(x * x + y * y);
109: double sinc = rho;
110: if (sinc > 1.0) {
111: if ((sinc - 1.0) > EPSILON) {
112: throw new ProjectionException(Errors
113: .format(ErrorKeys.POINT_OUTSIDE_HEMISPHERE));
114: }
115: sinc = 1.0;
116: }
117:
118: final double cosc = Math.sqrt(1.0 - sinc * sinc); /* in this range OK */
119: if (rho <= EPSILON) {
120: y = latitudeOfOrigin;
121: x = 0.0;
122: } else {
123: double phi = y * sinc / rho;
124: x *= sinc;
125: y = cosc * rho;
126:
127: // begin sinchk
128: if (Math.abs(phi) >= 1.0) {
129: phi = (phi < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0;
130: } else {
131: phi = Math.asin(phi);
132: }
133: // end sinchk
134:
135: if (y == 0.0) {
136: if (x == 0.0) {
137: x = 0.0;
138: } else {
139: x = (x < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0;
140: }
141: } else {
142: x = Math.atan2(x, y);
143: }
144: y = phi;
145: }
146:
147: assert checkInverseTransform(x, y, ptDst);
148: if (ptDst != null) {
149: ptDst.setLocation(x, y);
150: return ptDst;
151: }
152: return new Point2D.Double(x, y);
153: }
154: }
|