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 oblique case of the {@link Orthographic} projection. Only the spherical
037: * form is given here.
038: *
039: * @since 2.4
040: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/ObliqueOrthographic.java $
041: * @version $Id: ObliqueOrthographic.java 24578 2007-02-24 00:57:19Z desruisseaux $
042: * @author Rueben Schulz
043: */
044: public class ObliqueOrthographic extends Orthographic {
045: /**
046: * Maximum difference allowed when comparing real numbers.
047: */
048: private static final double EPSILON = 1E-6;
049:
050: /**
051: * The sine of the {@link #latitudeOfOrigin}.
052: */
053: private final double sinphi0;
054:
055: /**
056: * The cosine of the {@link #latitudeOfOrigin}.
057: */
058: private final double cosphi0;
059:
060: /**
061: * Constructs an oblique orthographic projection.
062: *
063: * @param parameters The parameter values in standard units.
064: * @throws ParameterNotFoundException if a mandatory parameter is missing.
065: */
066: protected ObliqueOrthographic(final ParameterValueGroup parameters)
067: throws ParameterNotFoundException {
068: super (parameters);
069: sinphi0 = Math.sin(latitudeOfOrigin);
070: cosphi0 = Math.cos(latitudeOfOrigin);
071: ensureSpherical();
072: }
073:
074: /**
075: * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates
076: * (units in radians) and stores the result in {@code ptDst} (linear distance
077: * on a unit sphere).
078: */
079: protected Point2D transformNormalized(double x, double y,
080: final Point2D ptDst) throws ProjectionException {
081: final double cosphi = Math.cos(y);
082: final double coslam = Math.cos(x);
083: final double sinphi = Math.sin(y);
084:
085: if (sinphi0 * sinphi + cosphi0 * cosphi * coslam < -EPSILON) {
086: throw new ProjectionException(Errors
087: .format(ErrorKeys.POINT_OUTSIDE_HEMISPHERE));
088: }
089:
090: y = cosphi0 * sinphi - sinphi0 * cosphi * coslam;
091: x = cosphi * Math.sin(x);
092:
093: if (ptDst != null) {
094: ptDst.setLocation(x, y);
095: return ptDst;
096: }
097: return new Point2D.Double(x, y);
098: }
099:
100: /**
101: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
102: * and stores the result in {@code ptDst}.
103: */
104: protected Point2D inverseTransformNormalized(double x, double y,
105: final Point2D ptDst) throws ProjectionException {
106: final double rho = Math.sqrt(x * x + y * y);
107: double sinc = rho;
108: if (sinc > 1.0) {
109: if ((sinc - 1.0) > EPSILON) {
110: throw new ProjectionException(Errors
111: .format(ErrorKeys.POINT_OUTSIDE_HEMISPHERE));
112: }
113: sinc = 1.0;
114: }
115:
116: final double cosc = Math.sqrt(1.0 - sinc * sinc); /* in this range OK */
117: if (rho <= EPSILON) {
118: y = latitudeOfOrigin;
119: x = 0.0;
120: } else {
121: double phi = (cosc * sinphi0) + (y * sinc * cosphi0 / rho);
122: y = (cosc - sinphi0 * phi) * rho; //rather clever; equivalent to part of (20-15)
123: x *= sinc * cosphi0;
124:
125: // begin sinchk
126: if (Math.abs(phi) >= 1.0) {
127: phi = (phi < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0;
128: } else {
129: phi = Math.asin(phi);
130: }
131: // end sinchk
132:
133: if (y == 0.0) {
134: if (x == 0.0) {
135: x = 0.0;
136: } else {
137: x = (x < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0;
138: }
139: } else {
140: x = Math.atan2(x, y);
141: }
142: y = phi;
143: }
144:
145: if (ptDst != null) {
146: ptDst.setLocation(x, y);
147: return ptDst;
148: }
149: return new Point2D.Double(x, y);
150: }
151: }
|