Statistics
| Revision:

svn-gvsig-desktop / branches / CqCMSDvp / libraries / libCq CMS for java.old / src / org / cresques / cts / GeoCalc.java @ 2312

History | View | Annotate | Download (12.7 KB)

1
/*
2
 * Cresques Mapping Suite. Graphic Library for constructing mapping applications.
3
 * 
4
 * Copyright (C) 2004-5. 
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
19
 *
20
 * For more information, contact:
21
 * 
22
 * cresques@gmail.com
23
 */
24
package org.cresques.cts;
25

    
26
import java.awt.geom.Point2D;
27

    
28
/**
29
 * Operaciones relacionadas con las proyecciones y sistemas
30
 * de coordenadas.
31
 * @author Luis W. Sevilla (sevilla_lui@gva.es)
32
 */
33
public class GeoCalc {
34
        IProjection proj;
35
        /**
36
         * 
37
         * @param proj
38
         */
39
        public GeoCalc(IProjection proj) {
40
                this.proj = proj;
41
        }
42
                
43
        /**
44
         * Distancia entre dos puntos en la esfera.
45
         * Los puntos deben estar en coordenadas geogr?ficas
46
         * @param pt1
47
         * @param pt2
48
         * @return distancia en km.
49
         */
50
        public double distanceGeo(Point2D pt1, Point2D pt2) {
51
                double R2 = Math.pow(proj.getDatum().getESemiMajorAxis(), 2);
52
                double dLat = Math.toRadians(pt2.getY()-pt1.getY());
53
                double dLong = Math.toRadians(pt2.getX()-pt1.getX());
54
                
55
                double alfa = Math.toRadians(pt1.getY()),
56
                        alfa2 = Math.toRadians(pt2.getY());
57
                if (Math.abs(alfa2)<Math.abs(alfa)) alfa = alfa2;
58
                double ds2 = R2*dLat*dLat + R2*Math.cos(alfa)*Math.cos(alfa)*dLong*dLong;
59
                        return Math.sqrt(ds2);
60
        }
61
        
62
        /**
63
         * Distancia entre dos puntos en el elipsoide.
64
         * Los puntos deben estar en coordenadas geogr?ficas
65
         * ver http://www.codeguru.com/Cpp/Cpp/algorithms/general/article.php/c5115/
66
         * @param lat1
67
         * @param lon1
68
         * @param lat2
69
         * @param lon2
70
         * @return
71
         */
72
        public double distanceEli(Point2D pt1, Point2D pt2) {
73
        
74
                double lat1 = Math.toRadians(pt1.getY());
75
                double lon1 = -Math.toRadians(pt1.getX());
76
                double lat2 = Math.toRadians(pt2.getY());
77
                double lon2 = -Math.toRadians(pt2.getX());
78
                
79
                double F = (lat1 + lat2) / 2D;
80
                double G = (lat1 - lat2) / 2D;
81
                double L = (lon1 - lon2) / 2D;
82
                
83
                double sing = Math.sin(G);
84
                double cosl = Math.cos(L);
85
                double cosf = Math.cos(F);
86
                double sinl = Math.sin(L);
87
                double sinf = Math.sin(F);
88
                double cosg = Math.cos(G);
89
                
90
                double flat = 1D / proj.getDatum().getEIFlattening();
91
                
92
                double S = sing*sing*cosl*cosl + cosf*cosf*sinl*sinl;
93
                double C = cosg*cosg*cosl*cosl + sinf*sinf*sinl*sinl;
94
                double W = Math.atan2(Math.sqrt(S),Math.sqrt(C));
95
                double R = Math.sqrt((S*C))/W;
96
                double H1 = (3D * R - 1D) / (2D * C);
97
                double H2 = (3D * R + 1D) / (2D * S);
98
                double D = 2D * W * proj.getDatum().getESemiMajorAxis();
99
                return (D * (1D + flat * H1 * sinf*sinf*cosg*cosg -
100
                                flat*H2*cosf*cosf*sing*sing));
101
        }
102
        
103
        /*
104
         * F?rmulas de Vincenty's.
105
         * (pasadas de http://wegener.mechanik.tu-darmstadt.de/GMT-Help/Archiv/att-8710/Geodetic_py
106
         * http://www.icsm.gov.au/icsm/gda/gdatm/index.html
107
         */
108

    
109
        class GeoData {
110
                Point2D pt;
111
                double azimut, revAzimut;
112
                double dist;
113
                
114
                public GeoData(double x, double y) {
115
                        pt = new Point2D.Double(x, y);
116
                        azimut = revAzimut = dist = 0;
117
                }
118
                
119
                public GeoData(double x, double y, double dist, double azi, double rAzi) {
120
                        pt = new Point2D.Double(x, y);
121
                        azimut = azi;
122
                        revAzimut = rAzi;
123
                        this.dist = dist;
124
                }
125
        }
126
        /**
127
         * Algrothims from Geocentric Datum of Australia Technical Manual
128
         * 
129
         * http://www.anzlic.org.au/icsm/gdatum/chapter4.html
130
         * 
131
         * This page last updated 11 May 1999
132
         * 
133
         * Computations on the Ellipsoid
134
         * 
135
         * There are a number of formulae that are available
136
         * to calculate accurate geodetic positions,
137
         * azimuths and distances on the ellipsoid.
138
         * 
139
         * Vincenty's formulae (Vincenty, 1975) may be used
140
         * for lines ranging from a few cm to nearly 20,000 km,
141
         * with millimetre accuracy.
142
         * The formulae have been extensively tested
143
         * for the Australian region, by comparison with results
144
         * from other formulae (Rainsford, 1955 & Sodano, 1965).
145
         * 
146
         * * Inverse problem: azimuth and distance from known
147
         *                 latitudes and longitudes
148
         * * Direct problem: Latitude and longitude from known
149
         *                 position, azimuth and distance.
150
         * * Sample data
151
         * * Excel spreadsheet 
152
         * 
153
         * Vincenty's Inverse formulae
154
         * Given: latitude and longitude of two points
155
         *                 (phi1, lembda1 and phi2, lembda2),
156
         * Calculate: the ellipsoidal distance (s) and 
157
         * forward and reverse azimuths between the points (alpha12, alpha21).
158
         */
159

    
160
        /**
161
         * Devuelve la distancia entre dos puntos usando las formulas
162
         * de vincenty.
163
         * @param pt1
164
         * @param pt2
165
         * @return
166
         */
167
        public double distanceVincenty( Point2D pt1, Point2D pt2 ) {
168
                return distanceAzimutVincenty(pt1, pt2).dist;
169
        }
170
        
171
        /**
172
         * Returns the distance between two geographic points on the ellipsoid
173
         *        and the forward and reverse azimuths between these points.
174
         *       lats, longs and azimuths are in decimal degrees, distance in metres 
175
         *        Returns ( s, alpha12,  alpha21 ) as a tuple
176
         * @param pt1
177
         * @param pt2
178
         * @return
179
         */
180
        public GeoData distanceAzimutVincenty( Point2D pt1, Point2D pt2 ) {
181
                GeoData gd = new GeoData(0,0);
182
                double f = 1D / proj.getDatum().getEIFlattening(),
183
                        a = proj.getDatum().getESemiMajorAxis(),
184
                        phi1 = pt1.getY(), lembda1 = pt1.getX(),
185
                        phi2 = pt2.getY(),  lembda2 = pt2.getX();
186

    
187
            if ((Math.abs( phi2 - phi1 ) < 1e-8) && ( Math.abs( lembda2 - lembda1) < 1e-8 ))
188
                  return gd;
189
          
190
                double piD4   = Math.atan( 1.0 );
191
                double two_pi = piD4 * 8.0;
192

    
193
                phi1    = phi1 * piD4 / 45.0;
194
                lembda1 = lembda1 * piD4 / 45.0;                // unfortunately lambda is a key word!
195
                phi2    = phi2 * piD4 / 45.0;
196
                lembda2 = lembda2 * piD4 / 45.0;
197

    
198
                double b = a * (1.0 - f);
199

    
200
                double TanU1 = (1-f) * Math.tan( phi1 );
201
                double TanU2 = (1-f) * Math.tan( phi2 );
202
                
203
                double U1 = Math.atan(TanU1);
204
                double U2 = Math.atan(TanU2);
205

    
206
                double lembda = lembda2 - lembda1;
207
                double last_lembda = -4000000.0;                // an impossibe value
208
                double omega = lembda;
209

    
210
                // Iterate the following equations, 
211
                //  until there is no significant change in lembda 
212
                double Sin_sigma = 0, Cos_sigma = 0;
213
                double Cos2sigma_m = 0, alpha = 0;
214
                double sigma = 0, sqr_sin_sigma = 0;
215
                while ( last_lembda < -3000000.0 || lembda != 0 && Math.abs( (last_lembda - lembda)/lembda) > 1.0e-9 ) {
216
                
217
                        sqr_sin_sigma = Math.pow( Math.cos(U2) * Math.sin(lembda), 2) +
218
                        Math.pow( (Math.cos(U1) * Math.sin(U2) -
219
                                                  Math.sin(U1) *  Math.cos(U2) * Math.cos(lembda) ), 2 );
220

    
221
                        Sin_sigma = Math.sqrt( sqr_sin_sigma );
222
                  
223
                        Cos_sigma = Math.sin(U1) * Math.sin(U2) + Math.cos(U1) * Math.cos(U2) * Math.cos(lembda);
224
                  
225
                        sigma = Math.atan2( Sin_sigma, Cos_sigma );
226

    
227
                        double Sin_alpha = Math.cos(U1) * Math.cos(U2) * Math.sin(lembda) / Math.sin(sigma);
228
                        alpha = Math.asin( Sin_alpha );
229
                  
230
                        Cos2sigma_m = Math.cos(sigma) - (2 * Math.sin(U1) * Math.sin(U2) / Math.pow(Math.cos(alpha), 2) );
231
                  
232
                        double C = (f/16) * Math.pow(Math.cos(alpha), 2) * (4 + f * (4 - 3 * Math.pow(Math.cos(alpha), 2)));
233
                  
234
                        last_lembda = lembda;
235
                  
236
                        lembda = omega + (1-C) * f * Math.sin(alpha) * (sigma + C * Math.sin(sigma) *
237
                                (Cos2sigma_m + C * Math.cos(sigma) * (-1 + 2 * Math.pow(Cos2sigma_m, 2) )));
238
                }
239
                
240

    
241
                double u2 = Math.pow(Math.cos(alpha),2) * (a*a-b*b) / (b*b);
242
                
243
                double A = 1 + (u2/16384) * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)));
244
                
245
                double B = (u2/1024) * (256 + u2 * (-128+ u2 * (74 - 47 * u2)));
246
                
247
                double delta_sigma = B * Sin_sigma * (Cos2sigma_m + (B/4) *
248
                        (Cos_sigma * (-1 + 2 * Math.pow(Cos2sigma_m, 2) ) -
249
                        (B/6) * Cos2sigma_m * (-3 + 4 * sqr_sin_sigma) *
250
                        (-3 + 4 * Math.pow(Cos2sigma_m,2 ) )));
251
                
252
                double s = b * A * (sigma - delta_sigma);
253
                
254
                double alpha12 = Math.atan2( (Math.cos(U2) * Math.sin(lembda)),
255
                        (Math.cos(U1) * Math.sin(U2) - Math.sin(U1) * Math.cos(U2) * Math.cos(lembda)));
256
                
257
                double alpha21 = Math.atan2( (Math.cos(U1) * Math.sin(lembda)),
258
                        (-Math.sin(U1) * Math.cos(U2) + Math.cos(U1) * Math.sin(U2) * Math.cos(lembda)));
259

    
260
                if ( alpha12 < 0.0 )
261
                        alpha12 =  alpha12 + two_pi;
262
                if ( alpha12 > two_pi )
263
                        alpha12 = alpha12 - two_pi;
264

    
265
                alpha21 = alpha21 + two_pi / 2.0;
266
                if ( alpha21 < 0.0 )
267
                        alpha21 = alpha21 + two_pi;
268
                if ( alpha21 > two_pi )
269
                        alpha21 = alpha21 - two_pi;
270

    
271
                alpha12    = alpha12    * 45.0 / piD4;
272
                alpha21    = alpha21    * 45.0 / piD4;
273
                return new GeoData(0,0, s, alpha12,  alpha21); 
274
        }
275

    
276
        /**
277
         * Vincenty's Direct formulae
278
         * Given: latitude and longitude of a point (phi1, lembda1) and
279
         * the geodetic azimuth (alpha12)
280
         * and ellipsoidal distance in metres (s) to a second point,
281
         *
282
         * Calculate: the latitude and longitude of the second point (phi2, lembda2)
283
         * and the reverse azimuth (alpha21).
284
         */
285
        
286
        /**
287
         * Returns the lat and long of projected point and reverse azimuth
288
         *        given a reference point and a distance and azimuth to project.
289
         *  lats, longs and azimuths are passed in decimal degrees.
290
         * Returns ( phi2,  lambda2,  alpha21 ) as a tuple 
291
         * @param pt
292
         * @param azimut
293
         * @param dist
294
         * @return
295
         */
296
        public GeoData getPointVincenty( Point2D pt, double azimut, double dist) {
297
                GeoData ret = new GeoData(0,0);
298
                double f = 1D / proj.getDatum().getEIFlattening(),
299
                        a = proj.getDatum().getESemiMajorAxis(),
300
                        phi1 = pt.getY(), lembda1 = pt.getX(),
301
                        alpha12 = azimut, s = dist;
302

    
303
                double piD4 = Math.atan( 1.0 );
304
                double two_pi = piD4 * 8.0;
305

    
306
                phi1    = phi1    * piD4 / 45.0;
307
                lembda1 = lembda1 * piD4 / 45.0;
308
                alpha12 = alpha12 * piD4 / 45.0;
309
                if ( alpha12 < 0.0 )
310
                        alpha12 = alpha12 + two_pi;
311
                if ( alpha12 > two_pi )
312
                        alpha12 = alpha12 - two_pi;
313

    
314
                
315
                double b = a * (1.0 - f);
316

    
317
                double TanU1 = (1-f) * Math.tan(phi1);
318
                double U1 = Math.atan( TanU1 );
319
                double sigma1 = Math.atan2( TanU1, Math.cos(alpha12) );
320
                double Sinalpha = Math.cos(U1) * Math.sin(alpha12);
321
                double cosalpha_sq = 1.0 - Sinalpha * Sinalpha;
322
                
323
                double u2 = cosalpha_sq * (a * a - b * b ) / (b * b);
324
                double A = 1.0 + (u2 / 16384) * (4096 + u2 * (-768 + u2 * 
325
                        (320 - 175 * u2) ) );
326
                double B = (u2 / 1024) * (256 + u2 * (-128 + u2 * (74 - 47 * u2) ) );
327
                
328
                // Starting with the approximation
329
                double sigma = (s / (b * A));
330

    
331
                double last_sigma = 2.0 * sigma + 2.0;        // something impossible
332
                
333
                // Iterate the following three equations 
334
                //  until there is no significant change in sigma 
335

    
336
                // two_sigma_m , delta_sigma
337

    
338
                double two_sigma_m = 0;
339
                while ( Math.abs( (last_sigma - sigma) / sigma) > 1.0e-9 ) {
340

    
341
                   two_sigma_m = 2 * sigma1 + sigma;
342
                   
343
                   double delta_sigma = B * Math.sin(sigma) * ( Math.cos(two_sigma_m) 
344
                                + (B/4) * (Math.cos(sigma) * 
345
                                   (-1 + 2 * Math.pow( Math.cos(two_sigma_m), 2 ) -  
346
                                   (B/6) * Math.cos(two_sigma_m) * 
347
                                     (-3 + 4 * Math.pow(Math.sin(sigma), 2 )) *  
348
                                   (-3 + 4 * Math.pow( Math.cos (two_sigma_m), 2 )))));
349
                   
350
                   last_sigma = sigma;
351
                   sigma = (s / (b * A)) + delta_sigma;
352
        }
353
                
354
                
355
                double phi2 = Math.atan2 ( (Math.sin(U1) * Math.cos(sigma) + Math.cos(U1) * Math.sin(sigma) * Math.cos(alpha12) ),
356
                        ((1-f) * Math.sqrt( Math.pow(Sinalpha, 2) + 
357
                        Math.pow(Math.sin(U1) * Math.sin(sigma) - Math.cos(U1) * Math.cos(sigma) * Math.cos(alpha12), 2))));
358
                
359

    
360
                double lembda = Math.atan2( (Math.sin(sigma) * Math.sin(alpha12 )), (Math.cos(U1) * Math.cos(sigma) - 
361
                        Math.sin(U1) *  Math.sin(sigma) * Math.cos(alpha12)));
362
                
363
                double C = (f/16) * cosalpha_sq * (4 + f * (4 - 3 * cosalpha_sq ));
364
                
365
                double omega = lembda - (1-C) * f * Sinalpha *  
366
                        (sigma + C * Math.sin(sigma) * (Math.cos(two_sigma_m) + 
367
                        C * Math.cos(sigma) * (-1 + 2 * Math.pow(Math.cos(two_sigma_m),2) )));
368
                
369
                double lembda2 = lembda1 + omega;
370
                
371
                double alpha21 = Math.atan2 ( Sinalpha, (-Math.sin(U1) * Math.sin(sigma) +  
372
                        Math.cos(U1) * Math.cos(sigma) * Math.cos(alpha12)));
373

    
374
                alpha21 = alpha21 + two_pi / 2.0;
375
                if ( alpha21 < 0.0 )
376
                        alpha21 = alpha21 + two_pi;
377
                if ( alpha21 > two_pi )
378
                        alpha21 = alpha21 - two_pi;
379

    
380

    
381
                phi2       = phi2       * 45.0 / piD4;
382
                lembda2    = lembda2    * 45.0 / piD4;
383
                alpha21    = alpha21    * 45.0 / piD4;
384

    
385
                ret.pt = new Point2D.Double(lembda2, phi2);
386
                ret.azimut = alpha21;
387
                return ret;
388
        }
389

    
390
        
391
        /**
392
         * Superficie de un triangulo (esf?rico). Los puntos deben de estar
393
         * en coordenadas geogr?ficas.
394
         * @param pt1
395
         * @param pt2
396
         * @param pt3
397
         * @return
398
         */
399
        public double surfaceSphere(Point2D pt1, Point2D pt2, Point2D pt3) {
400
                double sup = -1;
401
                double A = distanceGeo(pt1, pt2);
402
                double B = distanceGeo(pt2, pt3);
403
                double C = distanceGeo(pt3, pt1);
404
                sup = (A+B+C-Math.toRadians(180D))*
405
                        Math.PI*proj.getDatum().getESemiMajorAxis()/Math.toRadians(180D);
406
                return sup;
407
        }
408
}