source: josm/trunk/src/org/openstreetmap/josm/data/projection/proj/AbstractProj.java

Last change on this file was 18353, checked in by Don-vip, 2 years ago

fix #21696 - add robustness in case NaN coordinates are being projected (just log a warning)

  • Property svn:eol-style set to native
File size: 7.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.projection.proj;
3
4import org.openstreetmap.josm.data.projection.Ellipsoid;
5import org.openstreetmap.josm.data.projection.ProjectionConfigurationException;
6import org.openstreetmap.josm.tools.CheckParameterUtil;
7import org.openstreetmap.josm.tools.Logging;
8
9/**
10 * Abstract base class providing utilities for implementations of the Proj
11 * interface.
12 *
13 * This class has been derived from the implementation of the Geotools project;
14 * git 8cbf52d, org.geotools.referencing.operation.projection.MapProjection
15 * at the time of migration.
16 * <p>
17 *
18 * @author André Gosselin
19 * @author Martin Desruisseaux (PMO, IRD)
20 * @author Rueben Schulz
21*/
22public abstract class AbstractProj implements Proj {
23
24 /**
25 * Maximum number of iterations for iterative computations.
26 */
27 private static final int MAXIMUM_ITERATIONS = 15;
28
29 /**
30 * Difference allowed in iterative computations.
31 */
32 private static final double ITERATION_TOLERANCE = 1E-10;
33
34 /**
35 * Relative iteration precision used in the <code>mlfn</code> method
36 */
37 private static final double MLFN_TOL = 1E-11;
38
39 /**
40 * Constants used to calculate {@link #en0}, {@link #en1},
41 * {@link #en2}, {@link #en3}, {@link #en4}.
42 */
43 private static final double C00 = 1.0;
44 private static final double C02 = 0.25;
45 private static final double C04 = 4.6875E-02;
46 private static final double C06 = 1.953125E-02;
47 private static final double C08 = 1.068115234375E-02;
48 private static final double C22 = 0.75;
49 private static final double C44 = 4.6875E-01;
50 private static final double C46 = 1.30208333333333E-02;
51 private static final double C48 = 7.12076822916667E-03;
52 private static final double C66 = 3.64583333333333E-01;
53 private static final double C68 = 5.69661458333333E-03;
54 private static final double C88 = 3.076171875E-01;
55
56 /**
57 * Constant needed for the <code>mlfn</code> method.
58 * Setup at construction time.
59 */
60 protected double en0, en1, en2, en3, en4;
61
62 /**
63 * Ellipsoid excentricity, equals to <code>sqrt({@link #e2 excentricity squared})</code>.
64 * Value 0 means that the ellipsoid is spherical.
65 *
66 * @see #e2
67 */
68 protected double e;
69
70 /**
71 * The square of excentricity: e² = (a²-b²)/a² where
72 * <var>e</var> is the excentricity,
73 * <var>a</var> is the semi major axis length and
74 * <var>b</var> is the semi minor axis length.
75 *
76 * @see #e
77 */
78 protected double e2;
79
80 /**
81 * is ellipsoid spherical?
82 * @see Ellipsoid#spherical
83 */
84 protected boolean spherical;
85
86 @Override
87 public void initialize(ProjParameters params) throws ProjectionConfigurationException {
88 CheckParameterUtil.ensureParameterNotNull(params, "params");
89 CheckParameterUtil.ensureParameterNotNull(params.ellps, "params.ellps");
90 e2 = params.ellps.e2;
91 e = params.ellps.e;
92 spherical = params.ellps.spherical;
93 // Compute constants for the mlfn
94 double t;
95 // CHECKSTYLE.OFF: SingleSpaceSeparator
96 en0 = C00 - e2 * (C02 + e2 *
97 (C04 + e2 * (C06 + e2 * C08)));
98 en1 = e2 * (C22 - e2 *
99 (C04 + e2 * (C06 + e2 * C08)));
100 en2 = (t = e2 * e2) *
101 (C44 - e2 * (C46 + e2 * C48));
102 en3 = (t *= e2) * (C66 - e2 * C68);
103 en4 = t * e2 * C88;
104 // CHECKSTYLE.ON: SingleSpaceSeparator
105 }
106
107 @Override
108 public boolean isGeographic() {
109 return false;
110 }
111
112 /**
113 * Calculates the meridian distance. This is the distance along the central
114 * meridian from the equator to {@code phi}. Accurate to &lt; 1e-5 meters
115 * when used in conjunction with typical major axis values.
116 *
117 * @param phi latitude to calculate meridian distance for.
118 * @param sphi sin(phi).
119 * @param cphi cos(phi).
120 * @return meridian distance for the given latitude.
121 */
122 protected final double mlfn(final double phi, double sphi, double cphi) {
123 cphi *= sphi;
124 sphi *= sphi;
125 return en0 * phi - cphi *
126 (en1 + sphi *
127 (en2 + sphi *
128 (en3 + sphi *
129 en4)));
130 }
131
132 /**
133 * Calculates the latitude ({@code phi}) from a meridian distance.
134 * Determines phi to TOL (1e-11) radians, about 1e-6 seconds.
135 *
136 * @param arg meridian distance to calculate latitude for.
137 * @return the latitude of the meridian distance.
138 * @throws RuntimeException if the itteration does not converge.
139 */
140 protected final double invMlfn(double arg) {
141 double s, t, phi, k = 1.0/(1.0 - e2);
142 int i;
143 phi = arg;
144 for (i = MAXIMUM_ITERATIONS; true;) { // rarely goes over 5 iterations
145 if (--i < 0) {
146 throw new IllegalStateException("Too many iterations");
147 }
148 s = Math.sin(phi);
149 t = 1.0 - e2 * s * s;
150 t = (mlfn(phi, s, Math.cos(phi)) - arg) * (t * Math.sqrt(t)) * k;
151 phi -= t;
152 if (Math.abs(t) < MLFN_TOL) {
153 return phi;
154 }
155 }
156 }
157
158 /**
159 * Tolerant asin that will just return the limits of its output range if the input is out of range
160 * @param v the value whose arc sine is to be returned.
161 * @return the arc sine of the argument.
162 */
163 protected final double aasin(double v) {
164 double av = Math.abs(v);
165 if (av >= 1.) {
166 return (v < 0. ? -Math.PI / 2 : Math.PI / 2);
167 }
168 return Math.asin(v);
169 }
170
171 // Iteratively solve equation (7-9) from Snyder.
172 final double cphi2(final double ts) {
173 if (Double.isNaN(ts)) {
174 Logging.warn("Trying to project invalid NaN coordinates");
175 return Double.NaN;
176 }
177 final double eccnth = 0.5 * e;
178 double phi = (Math.PI/2) - 2.0 * Math.atan(ts);
179 for (int i = 0; i < MAXIMUM_ITERATIONS; i++) {
180 final double con = e * Math.sin(phi);
181 final double dphi = (Math.PI/2) - 2.0*Math.atan(ts * Math.pow((1-con)/(1+con), eccnth)) - phi;
182 phi += dphi;
183 if (Math.abs(dphi) <= ITERATION_TOLERANCE) {
184 return phi;
185 }
186 }
187 throw new IllegalStateException("no convergence for ts="+ts);
188 }
189
190 /**
191 * Computes function <code>f(s,c,e²) = c/sqrt(1 - s²&times;e²)</code> needed for the true scale
192 * latitude (Snyder 14-15), where <var>s</var> and <var>c</var> are the sine and cosine of
193 * the true scale latitude, and <var>e²</var> is the {@linkplain #e2 eccentricity squared}.
194 * @param s sine of the true scale latitude
195 * @param c cosine of the true scale latitude
196 * @return <code>c/sqrt(1 - s²&times;e²)</code>
197 */
198 final double msfn(final double s, final double c) {
199 return c / Math.sqrt(1.0 - (s*s) * e2);
200 }
201
202 /**
203 * Computes function (15-9) and (9-13) from Snyder.
204 * Equivalent to negative of function (7-7).
205 * @param lat the latitude
206 * @param sinlat sine of the latitude
207 * @return auxiliary value computed from <code>lat</code> and <code>sinlat</code>
208 */
209 final double tsfn(final double lat, double sinlat) {
210 sinlat *= e;
211 // NOTE: change sign to get the equivalent of Snyder (7-7).
212 return Math.tan(0.5 * (Math.PI/2 - lat)) / Math.pow((1 - sinlat) / (1 + sinlat), 0.5*e);
213 }
214}
Note: See TracBrowser for help on using the repository browser.