001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/projections/ProjectionUtils.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    
037    package org.deegree.crs.projections;
038    
039    import java.awt.geom.Rectangle2D;
040    
041    /**
042     * The <code>Utils</code> class combines some helpful constants and forms.
043     *
044     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
045     *
046     * @author last edited by: $Author: mschneider $
047     *
048     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
049     *
050     */
051    
052    public class ProjectionUtils {
053    
054        // Very handy for setting maximum number of iterations in a for loop.
055        private final static int MAX_ITER = 10;
056    
057        /**
058         * A small epsilon value
059         */
060        public final static double EPS10 = 1e-10;
061    
062        /**
063         * An even smaller epsilon value
064         */
065        public final static double EPS11 = 1e-11;
066    
067        /**
068         * Cotaining the value 0.5*pi
069         */
070        public final static double HALFPI = Math.PI * 0.5;
071    
072        /**
073         * Containing the value 0.25*pi
074         */
075        public final static double QUARTERPI = Math.PI * 0.25;
076    
077        /**
078         * Containing the value 2*pi
079         */
080        public final static double TWOPI = Math.PI * 2.0;
081    
082        /**
083         * Radians to Degrees (180.0/Math.PI)
084         */
085        public final static double RTD = 180.0 / Math.PI;
086    
087        /**
088         * Degrees to Radians (Math.PI/180.0)
089         */
090        public final static double DTR = Math.PI / 180.0;
091    
092        /**
093         * The max and min of the projected word map in radians (-Math.PI, -HALFPI, TWOPI, Math.PI)
094         */
095        public final static Rectangle2D WORLD_BOUNDS_RAD = new Rectangle2D.Double( -Math.PI, -HALFPI, TWOPI, Math.PI );
096    
097        /**
098         * The max and min of the projected word map in degrees (-180, -90, 360, 180)
099         */
100        public final static Rectangle2D WORLD_BOUNDS = new Rectangle2D.Double( -180, -90, 360, 180 );
101    
102        /**
103         * From the proj4 library, to determine small q which is needed to calculate the authalic (equal-areaed) latitude
104         * beta, on a sphere having the same surface area as the ellipsoid, relative to the ellipsoid. Snyder (3 -12).
105         *
106         * @param sinphi
107         *            the sine of the angle between the positive z-axis and the line formed between the origin and P.
108         * @param e
109         *            the eccentricity
110         * @return the q value from Snyder (3-12)
111         * @deprecated use {@link ProjectionUtils#calcQForAuthalicLatitude(double, double)} instead.
112         */
113        @Deprecated
114        public static double qsfn( double sinphi, double e ) {
115            return calcQForAuthalicLatitude( sinphi, e );
116        }
117    
118        /**
119         * From the proj4 library, to determine small q which is needed to calculate the authalic (equal-areaed) latitude
120         * beta, on a sphere having the same surface area as the ellipsoid, relative to the ellipsoid. Snyder (3 -12).
121         *
122         * @param sinphi
123         *            the sine of the angle between the positive z-axis and the line formed between the origin and P.
124         * @param eccentricity
125         *            the eccentricity of the ellipsoid to map the sphere to.
126         * @return the q value from Snyder (3-12)
127         */
128        public static double calcQForAuthalicLatitude( double sinphi, double eccentricity ) {
129            if ( eccentricity >= EPS10 ) {
130                double eAndSinphi = eccentricity * sinphi;
131                double es = eccentricity * eccentricity;
132                return ( 1 - es )
133                       * ( sinphi / ( 1. - eAndSinphi * eAndSinphi ) - ( .5 / eccentricity )
134                                                                       * Math.log( ( 1. - eAndSinphi ) / ( 1. + eAndSinphi ) ) );
135            }
136            // we have a sphere.
137            return ( sinphi + sinphi );
138        }
139    
140        /**
141         * Pre-calculated values for Snyder's formula (3-5)
142         */
143        private final static double T00 = 0.5;
144    
145        private final static double T01 = .20833333333333333333;/* 5/24. */
146    
147        private final static double T02 = .08333333333333333333;/* 1/12. */
148    
149        private final static double T03 = .03611111111111111111;/* 13/360 */
150    
151        private final static double T10 = .14583333333333333333;/* 7/48 */
152    
153        private final static double T11 = .12083333333333333333;/* 29/240 */
154    
155        private final static double T12 = .07039930555555555555;/* 811/11520 */
156    
157        private final static double T20 = .05833333333333333333;/* 7/120 */
158    
159        private final static double T21 = .07232142857142857142;/* 81/1120 */
160    
161        private final static double T30 = .02653149801587301587;/* 4279/161280 */
162    
163        /**
164         * Pre-Calculates the values (used for the adams? series) which will be used to calculate the phi value of an
165         * inverse projection. Snyder (3-5).
166         *
167         * @param eccentricitySquared
168         *            the squared eccentricity from the ellipsoid to calculate the theta for.
169         * @return the precalculated values.
170         */
171        public static double[] preCalcedThetaSeries( double eccentricitySquared ) {
172            double[] precalculatedSerie = new double[4];
173            precalculatedSerie[0] = eccentricitySquared * T00;
174    
175            // eccentricity^4
176            double tmp = eccentricitySquared * eccentricitySquared;
177            precalculatedSerie[0] += tmp * T01;
178            precalculatedSerie[1] = tmp * T10;
179    
180            // eccentricity^6
181            tmp *= eccentricitySquared;
182            precalculatedSerie[0] += tmp * T02;
183            precalculatedSerie[1] += tmp * T11;
184            precalculatedSerie[2] = tmp * T20;
185    
186            // eccentricity^8
187            tmp *= eccentricitySquared;
188            precalculatedSerie[0] += tmp * T03;
189            precalculatedSerie[1] += tmp * T12;
190            precalculatedSerie[2] += tmp * T21;
191            precalculatedSerie[3] = tmp * T30;
192    
193            return precalculatedSerie;
194        }
195    
196        /**
197         * Gets Phi from the given conformal latitude chi and the precalculated values (gotten from
198         * {@link ProjectionUtils#preCalcedThetaSeries(double)} ) of the adams? serie. From Snyder (3-5).
199         *
200         * @param chi
201         *            the conformal latitude
202         * @param APA
203         *            the precalculated values from the serie gotten from
204         *            {@link ProjectionUtils#preCalcedThetaSeries(double)}.
205         * @return the Phi as a polarcoordinate on the ellipsoid or chi if the length of APA != 4.
206         */
207        public static double calcPhiFromConformalLatitude( double chi, double[] APA ) {
208            if ( APA.length != 4 ) {
209                return chi;
210            }
211            double tmp = chi + chi;
212            return ( chi + APA[0] * Math.sin( tmp ) + APA[1] * Math.sin( tmp + tmp ) + APA[2] * Math.sin( tmp + tmp + tmp ) + APA[3]
213                                                                                                                              * Math.sin( tmp
214                                                                                                                                          + tmp
215                                                                                                                                          + tmp
216                                                                                                                                          + tmp ) );
217        }
218    
219        /**
220         * P[0][0-2] = 1/3, 31/180, 517/5040, P[1][0-2] = 23/360, 251/3780 P[2][0] = 761/45360
221         */
222        private final static double P00 = .33333333333333333333; /* 1/3 */
223    
224        private final static double P01 = .17222222222222222222; /* 31 / 180 */
225    
226        private final static double P02 = .10257936507936507936; /* 517 / 5040 */
227    
228        private final static double P10 = .06388888888888888888; /* 23/360 */
229    
230        private final static double P11 = .06640211640211640211; /* 251/3780 */
231    
232        private final static double P20 = .01641501294219154443; /* 761/45360 */
233    
234        /**
235         * Pre-Calculates the values (used for the adams? series) which will be used to calculate the authalic latitude.
236         * Snyder (3-18).
237         *
238         * @param eccentricitySquared
239         *            the squared eccentricity from the ellipsoid to calculate the authalic latitude for.
240         * @return the precalculated values.
241         * @deprecated use {@link ProjectionUtils#getAuthalicLatitudeSeriesValues(double)} instead.;
242         */
243        @Deprecated
244        public static double[] authset( double eccentricitySquared ) {
245            return getAuthalicLatitudeSeriesValues( eccentricitySquared );
246        }
247    
248        /**
249         * Pre-Calculates the values (used for the adams? series) which will be used to calculate the authalic latitude.
250         * Snyder (3-18).
251         *
252         * @param eccentricitySquared
253         *            the squared eccentricity from the ellipsoid to calculate the authalic latitude for.
254         * @return the precalculated values [0] = e^2/3 + e^4*(31/180) + e^6*(517/5040), [1]= e^4*(23/360) + e^6*(251/3780)
255         *         and [2] = e^6*(761/45360).
256         */
257        public static double[] getAuthalicLatitudeSeriesValues( double eccentricitySquared ) {
258            double[] precalculatedSerie = new double[3];
259            precalculatedSerie[0] = eccentricitySquared * P00;
260            double t = eccentricitySquared * eccentricitySquared;
261            precalculatedSerie[0] += t * P01;
262            precalculatedSerie[1] = t * P10;
263            t *= eccentricitySquared;
264            precalculatedSerie[0] += t * P02;
265            precalculatedSerie[1] += t * P11;
266            precalculatedSerie[2] = t * P20;
267            return precalculatedSerie;
268        }
269    
270        /**
271         * Gets phi from the authalic latitude beta and the precalculated values of the adams? serie. From Snyder (3-18).
272         *
273         * @param beta
274         *            authalic latitude.
275         * @param APA
276         *            the precalculated values from the series gotten from
277         *            {@link ProjectionUtils#getAuthalicLatitudeSeriesValues(double)}.
278         * @return the phi on the ellipsoid.
279         * @deprecated use {@link ProjectionUtils#calcPhiFromAuthalicLatitude(double, double[])} instead.;
280         */
281        @Deprecated
282        public static double authlat( double beta, double[] APA ) {
283            return calcPhiFromAuthalicLatitude( beta, APA );
284        }
285    
286        /**
287         * Gets phi from the authalic latitude beta and the precalculated values of the adams? serie. From Snyder (3-18).
288         *
289         * @param beta
290         *            authalic latitude.
291         * @param APA
292         *            the precalculated values from the serie gotten from
293         *            {@link ProjectionUtils#getAuthalicLatitudeSeriesValues(double)}.
294         * @return the phi on the ellipsoid.
295         */
296        public static double calcPhiFromAuthalicLatitude( double beta, double[] APA ) {
297            double t = beta + beta;
298            return ( beta + APA[0] * Math.sin( t ) + APA[1] * Math.sin( t + t ) + APA[2] * Math.sin( t + t + t ) );
299        }
300    
301        /**
302         * Calcs the length of a vector given by two points x and y
303         *
304         * @param dx
305         *            of the vector
306         * @param dy
307         *            of the vector
308         * @return the length
309         */
310        public static double length( double dx, double dy ) {
311            return Math.hypot( dx, dy );
312        }
313    
314        /**
315         * This method calculates the innerpart of the conformal latitude's definition (Snyder p.15 3-1). This formula is
316         * almost equal to the calculation of the half colatitude from the conformal latitude (Snyder p.108 15-9). They only
317         * differ a sign in the first term.
318         *
319         * @param phi
320         *            to calculate the conformal latitude from
321         * @param sinphi
322         *            the sinus of the phi.
323         * @param eccentricity
324         *            of the ellipsoid to which the phi should be made conformal to.
325         * @return the value of the innerpart of the conformal latitude formula. i.e. tan( pi/4 <b>+</b> phi/2)<b>*</b>[(1-e*sin(phi))/1+e*sin(phi))]^e/2.
326         */
327        public static double conformalLatitudeInnerPart( double phi, double sinphi, double eccentricity ) {
328            sinphi *= eccentricity;
329            return ( Math.tan( .5 * ( HALFPI + phi ) ) ) * Math.pow( ( 1. - sinphi ) / ( 1. + sinphi ), .5 * eccentricity );
330        }
331    
332        /**
333         * This method calculates the innerpart of the conformal latitude's definition (Snyder p.15 3-1). This formula is
334         * almost equal to the calculation of the half colatitude from the conformal latitude (Snyder p.108 15-9). They only
335         * differ a sign in the first term.
336         *
337         * @param phi
338         *            to calculate the conformal latitude from
339         * @param sinphi
340         *            the sinus of the phi.
341         * @param eccentricity
342         *            of the ellipsoid to which the phi should be made conformal to.
343         * @return the value of the innerpart of the conformal latitude formula. i.e. tan( pi/4 <b>+</b>
344         *         phi/2)*[(1-e*sin(phi))/1+e*sin(phi))]^e/2.
345         * @deprecated Use {@link #conformalLatitudeInnerPart(double,double,double)} instead
346         */
347        @Deprecated
348        public static double ssfn( double phi, double sinphi, double eccentricity ) {
349            return conformalLatitudeInnerPart( phi, sinphi, eccentricity );
350        }
351    
352        /**
353         * This method calculates the tangens of the half colatitude from the conformal latitude (Snyder p.108 15-9).
354         *
355         * @param phi
356         *            to calculate the half of the co latitude of the conformal latitude from
357         * @param sinphi
358         *            the sinus of the phi.
359         * @param eccentricity
360         *            of the ellipsoid to which the phi should be made conformal to.
361         * @return the value of the tangens of half of the conformal latitude formula. i.e. tan( pi/4 <b>-</b> phi/2)<b>/</b>[(1-e*sin(phi))/1+e*sin(phi))]^e/2.
362         */
363        public static double tanHalfCoLatitude( double phi, double sinphi, double eccentricity ) {
364            sinphi *= eccentricity;
365            return ( Math.tan( .5 * ( HALFPI - phi ) ) ) / Math.pow( ( 1. - sinphi ) / ( 1. + sinphi ), .5 * eccentricity );
366        }
367    
368        /**
369         * This method calculates the tangens of the half colatitude from the conformal latitude (Snyder p.108 15-9). This
370         * formula is almost equal to the calculation of the innerpart of the conformal latitude's definition (Snyder p.15
371         * 3-1). They only differ a sign in the first term.
372         *
373         * @param phi
374         *            to calculate the half of the co latitude of the conformal latitude from
375         * @param sinphi
376         *            the sinus of the phi.
377         * @param eccentricity
378         *            of the ellipsoid to which the phi should be made conformal to.
379         * @return the value of the innerpart of the conformal latitude formula (given sign + or -). i.e. tan( pi/4 (+-)
380         *         phi/2)*[(1-e*sin(phi))/1+e*sin(phi))]^e/2.
381         * @deprecated Use {@link #tanHalfCoLatitude(double,double,double)} instead
382         */
383        @Deprecated
384        public static double tsfn( double phi, double sinphi, double eccentricity ) {
385            return tanHalfCoLatitude( phi, sinphi, eccentricity );
386        }
387    
388        /**
389         * This method can be used to calculate the value of a variable called 'm' by Snyder (Snyder p.101 14-15).
390         *
391         * @param sinphi
392         *            the sinus of the phi
393         * @param cosphi
394         *            the cosinus of the phi
395         * @param eccentricitySquared
396         *            the value eccentricity * eccentricity.
397         * @return cos( phi) / Math.sqrt( 1 - eccentricity*eccentricity*sin(phi)*sin(phi) ).
398         * @deprecated Use {@link #calcMFromSnyder(double,double,double)} instead
399         */
400        @Deprecated
401        public static double msfn( double sinphi, double cosphi, double eccentricitySquared ) {
402            return calcMFromSnyder( sinphi, cosphi, eccentricitySquared );
403        }
404    
405        /**
406         * This method can be used to calculate the value of a variable called 'm' by Snyder (Snyder p.101 14-15).
407         *
408         * @param sinphi
409         *            the sinus of the phi
410         * @param cosphi
411         *            the cosinus of the phi
412         * @param eccentricitySquared
413         *            the value eccentricity * eccentricity.
414         * @return cos( phi) / Math.sqrt( 1 - eccentricity*eccentricity*sin(phi)*sin(phi) ).
415         */
416        public static double calcMFromSnyder( double sinphi, double cosphi, double eccentricitySquared ) {
417            return cosphi / Math.sqrt( 1.0 - eccentricitySquared * sinphi * sinphi );
418        }
419    
420        /**
421         * Copied these value from proj4, I think they are reformed to fit some rule... but I don't know which rule that is
422         * :-(
423         */
424        private final static double C00 = 1; /* 1 :-) */
425    
426        private final static double C02 = .25; /* 1/4 */
427    
428        private final static double C04 = .046875;/* 3/64 */
429    
430        private final static double C06 = .01953125;/* 5/256 */
431    
432        private final static double C08 = .01068115234375;/* 175 / 16384 */
433    
434        private final static double C22 = .75;
435    
436        private final static double C44 = .46875;
437    
438        private final static double C46 = .01302083333333333333;
439    
440        private final static double C48 = .00712076822916666666;
441    
442        private final static double C66 = .36458333333333333333;
443    
444        private final static double C68 = .00569661458333333333;
445    
446        private final static double C88 = .3076171875;
447    
448        /**
449         * Pre Calculates the values for the series to calculate for a given ellipsoid with given eccentricity the distance
450         * along the meridian from the equator to a given latitude
451         * {@link #getDistanceAlongMeridian(double, double, double, double[])}.
452         *
453         * @param es
454         *            the squared eccentricity of the underlying ellipsoid.
455         * @return the precalculated values for given ellipsoid.
456         * @deprecated Use {@link #getRectifiyingLatitudeValues(double)} instead
457         */
458        @Deprecated
459        public static double[] enfn( double es ) {
460            return getRectifiyingLatitudeValues( es );
461        }
462    
463        /**
464         * Pre Calculates the values for the series to calculate for a given ellipsoid with given eccentricity the distance
465         * along the meridian from the equator to a given latitude
466         * {@link #getDistanceAlongMeridian(double, double, double, double[])}.
467         *
468         * @param es
469         *            the squared eccentricity of the underlying ellipsoid.
470         * @return the precalculated values for given ellipsoid.
471         */
472        public static double[] getRectifiyingLatitudeValues( double es ) {
473            double[] en = new double[5];
474            en[0] = C00 - es * ( C02 + es * ( C04 + es * ( C06 + es * C08 ) ) );
475            en[1] = es * ( C22 - es * ( C04 + es * ( C06 + es * C08 ) ) );
476    
477            double t = es * es;
478            en[2] = t * ( C44 - es * ( C46 + es * C48 ) );
479    
480            t *= es;
481            en[3] = t * ( C66 - es * C68 );
482    
483            en[4] = t * es * C88;
484    
485            return en;
486        }
487    
488        /**
489         * This method calcs for a a given ellispoid the distance along the meridian from the equator to latitude phi Snyder
490         * (p.17 3-21). It is used to calculate the rectifying latitude <i>mu</i>.
491         *
492         * @param phi
493         *            the lattitude of the point in radians
494         * @param sphi
495         *            the sinus of the latitude
496         * @param cphi
497         *            the cosinus of the latitude
498         * @param en
499         *            an array (of length 5) containing the precalculate values for this ellipsoid gotten from
500         *            {@link #getRectifiyingLatitudeValues(double)}.
501         * @return the distance along the meridian from the equator to latitude phi.
502         * @deprecated Use {@link #getDistanceAlongMeridian(double,double,double,double[])} instead
503         */
504        @Deprecated
505        public static double mlfn( double phi, double sphi, double cphi, double[] en ) {
506            return getDistanceAlongMeridian( phi, sphi, cphi, en );
507        }
508    
509        /**
510         * This method calcs the distance along the meridian from the equator to latitude phi for a a given ellispoid Snyder
511         * (p.17 3-21). It is used to calculate the rectifying latitude <i>mu</i>.
512         *
513         * @param phi
514         *            the lattitude of the point in radians
515         * @param sphi
516         *            the sinus of the latitude
517         * @param cphi
518         *            the cosinus of the latitude
519         * @param en
520         *            an array (of length 5) containing the precalculate values for this ellipsoid gotten from
521         *            {@link #getRectifiyingLatitudeValues(double)}.
522         * @return the distance along the meridian from the equator to latitude phi.
523         */
524        public static double getDistanceAlongMeridian( double phi, double sphi, double cphi, double[] en ) {
525            cphi *= sphi;
526            sphi *= sphi;
527            return ( en[0] * phi ) - ( cphi * ( en[1] + sphi * ( en[2] + sphi * ( en[3] + sphi * en[4] ) ) ) );
528        }
529    
530        /**
531         * This method calcs lattitude phi from a given distance along the meridian to the equator for a a given ellispoid
532         * and is therefore the inverse of the {@link #getDistanceAlongMeridian(double, double, double, double[])}. Phi is
533         * determined to EPS (1e-11) radians, which is about 1e-6 seconds.
534         *
535         * @param initialValue
536         *            to calculate phi from, a good starting value is using the (distance along the meridian / y*scale) e.g.
537         *            the scaled y value on the meridian.
538         * @param squaredEccentricity
539         *            the squared eccentricity of the ellipsoid.
540         * @param en
541         *            an array (of length 5) containing the precalculate values for this ellipsoid gotten from
542         *            {@link #getRectifiyingLatitudeValues(double)}.
543         * @return the lattitude phi.
544         * @deprecated Use {@link #calcPhiFromMeridianDistance(double,double,double[])} instead
545         */
546        @Deprecated
547        public static double inv_mlfn( double initialValue, double squaredEccentricity, double[] en ) {
548            return calcPhiFromMeridianDistance( initialValue, squaredEccentricity, en );
549        }
550    
551        /**
552         * This method calcs lattitude phi from a given distance along the meridian to the equator for a a given ellispoid
553         * and is therefore the inverse of the {@link #getDistanceAlongMeridian(double, double, double, double[])}. Phi is
554         * determined to EPS (1e-11) radians, which is about 1e-6 seconds.
555         *
556         * @param initialValue
557         *            to calculate phi from, a good starting value is using the (distance along the meridian / y*scale) e.g.
558         *            the scaled y value on the meridian.
559         * @param squaredEccentricity
560         *            the squared eccentricity of the ellipsoid.
561         * @param en
562         *            an array (of length 5) containing the precalculate values for this ellipsoid gotten from
563         *            {@link #getRectifiyingLatitudeValues(double)}.
564         * @return the lattitude phi or the best approximated value if no suitable convergence was found.
565         */
566        public static double calcPhiFromMeridianDistance( double initialValue, double squaredEccentricity, double[] en ) {
567            double k = 1. / ( 1. - squaredEccentricity );
568            double phi = initialValue;
569            /* (from proj4: ->) rarely goes over 2 iterations */
570            for ( int i = MAX_ITER; i != 0; i-- ) {
571                double s = Math.sin( phi );
572                double t = 1. - squaredEccentricity * s * s;
573                t = ( getDistanceAlongMeridian( phi, s, Math.cos( phi ), en ) - initialValue ) * ( t * Math.sqrt( t ) ) * k;
574                phi -= t;
575                if ( Math.abs( t ) < EPS11 ) {
576                    return phi;
577                }
578            }
579            return phi;
580        }
581    
582        /**
583         * A helper method, which returns the acos from value or if value < -1 pi or value>1 0.
584         *
585         * @param value
586         *            (in radians) from which the acos must be calculated
587         * @return the acos from value or if value < -1 pi or if value > 1 0.
588         */
589        public static double acosScaled( double value ) {
590            return ( value < -1 ) ? Math.PI : ( value > 1 ) ? 0 : Math.acos( value );
591        }
592    
593        /**
594         * A helper method, which returns the asin from value or if value < -1 (-pi/2) or value>1 (pi/2).
595         *
596         * @param value
597         *            (in radians) from which the asin must be calculated
598         * @return the asin from value or if value < -1 (-pi/2) or value>1 (pi/2).
599         */
600        public static double asinScaled( double value ) {
601            return ( value < -1 ) ? -HALFPI : ( value > 1 ) ? HALFPI : Math.asin( value );
602        }
603    
604        /**
605         * A helper method modulos (pi)the given angle (in radians) until the result fits betwee -HALFPI and HALF_PI.
606         *
607         * @param angle
608         *            in radians
609         * @return the angle adjusted to -pi/2 + pi/2 or 0 if the angle is NaN or Infinite.
610         */
611        public static double normalizeLatitude( double angle ) {
612            if ( Double.isInfinite( angle ) || Double.isNaN( angle ) ) {
613                return 0;
614            }
615            while ( angle > HALFPI ) {
616                angle -= Math.PI;
617            }
618            while ( angle < -HALFPI ) {
619                angle += Math.PI;
620            }
621            return angle;
622        }
623    
624        /**
625         * A helper method modulos (2*pi)the given angle (in radians) until the result fits betwee -PI and PI.
626         *
627         * @param angle
628         *            to be normalized
629         * @return the angle adjusted to -2*pi + pi*2 or 0 if the angle is NaN or Infinite.
630         */
631        public static double normalizeLongitude( double angle ) {
632            if ( Double.isInfinite( angle ) || Double.isNaN( angle ) ) {
633                return 0;
634            }
635            while ( angle > Math.PI ) {
636                angle -= TWOPI;
637            }
638            while ( angle < -Math.PI ) {
639                angle += TWOPI;
640            }
641            return angle;
642        }
643    
644        /**
645         * Converts a Deegree.MinSec value into it's radian equivalent. <code>
646         * for example 13.120637 dms -> 13.201769444444446° -> 0.23041434389473822 rd
647         * </code>
648         *
649         * @param inCoord
650         *            to be converted to radians.
651         * @return the radian equivalent of the inCoord.
652         */
653        public static double DecMinSecToRadians( double inCoord ) {
654            // get decimal minutes
655            double remainder = getRemainder( inCoord ) * 100;
656            // add the decimal minutes to the decimal seconds
657            remainder = ( Math.floor( remainder ) * 60 ) + ( getRemainder( remainder ) * 100 );
658            return ( Math.floor( inCoord ) + ( remainder / 3600 ) ) * DTR;
659        }
660    
661        /**
662         * Converts a radian to its Deegree.MinSec equivalent.<code>
663         * For example 0.23041434389473822 rd -> 13.201769444444446° -> 13.120637 dms
664         * </code>
665         *
666         * @param inCoord
667         *            to be converted to degrees.minsec
668         * @return the radian equivalent of the inCoord.
669         */
670        public static double RadiansToDecMinSec( double inCoord ) {
671    
672            double degrees = RTD * inCoord;
673            double remainder = getRemainder( degrees );
674            return Math.floor( degrees ) + ( Math.floor( remainder * 60 ) / 100 )
675                   + ( ( remainder * .36 ) - ( Math.floor( remainder * 60 ) * .006 ) );
676        }
677    
678        /**
679         * Retrieve the remainder of a given double value, e.g. value - Math.floor( value ).
680         *
681         * @param value
682         *            to get the remainder from.
683         *
684         * @return the remainder of the given value.
685         */
686        private static double getRemainder( double value ) {
687            return value - Math.floor( value );
688        }
689    }