-
Notifications
You must be signed in to change notification settings - Fork 2
/
CoordinateTransformations.h
573 lines (474 loc) · 19.3 KB
/
CoordinateTransformations.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
#if !defined(COORDINATETRANSFORMATIONS_H__INCLUDED_)
#define COORDINATETRANSFORMATIONS_H__INCLUDED_
#include <math.h>
// Written by Walter Piechulla in April 2000
// Sam Blackburn's prologue about Geodesy Foundation Classes:
// http://ourworld.compuserve.com/homepages/Sam_Blackburn/homepage.htm
// Definitions
// Ellipsoid
// A flattened sphere. Take a basketball (a sphere), let some air out of it then
// stand on it. You now have an ellipsoid. The basketball is flat on the bottom
// and the top but still round in the middle.
// Equator
// 0 degrees Latitude
// Flattening
// This is a ratio between polar radius and equatorial radius. Other names for
// flattening are elliptocyte or oblateness.
// Latitude
// The lines that run around the Earth like belts. They measure up/down angles.
// Longituide
// The lines that slice the Earth like an orange from top to bottom. They
// measure left/right angles.
// Meridian
// One of the imaginary circles on the earth's surface passing through the north
// and south poles
// Pi
// The most famous constant. It is a ratio.
// It is the Circumference of a circle divided by
// the diameter of the circle. It is 3.14 (or roughly 22 divivded by 7).
// Prime Meridian
// 0 degrees Longitude
// Radian
// The unit of measurement of a circle. It is a ratio.
// It's kind of hard to explain what
// a radian is so let me give you an example. Take the basketball and cut
// a length of string equal to the radius (i.e. half the width) of the
// ball. Now, lay this string on top of the ball. See how it forms an arc?
// Now, draw a line from each end of the string to the center of the ball.
// The angle of these two lines at the center of the ball is roughly
// 57.2958 degrees. This is true no matter what the size of the ball is.
// Why? Because as the size of the ball increases, so does the radius.
// So, a radian can be considered as a relationship between the radius of
// a sphere (ball) and the circumference of the sphere. Now, there's something
// interesting about the number of degrees in a radian (52.2958). It is
// equal to 180 divided by Pi. Yup! Go Figure! OK. Now we're getting somewhere.
// To find the length of the arc when all you have is the number of radians
// in the angle at the center of the sphere, you multiply the radius times
// the number of radians. It's that simple (on a perfect sphere anyway).
// Geodetic Datum - description of the shape of the earth
// Reference Ellipsoid - A flattened sphere. Let some air out of
// a basketball, then stand on it. The ball is now an ellipsoid.
// Your feet are standing on the North Pole and the floor is the
// South Pole.
// Ellipsoids are described by their polar and equatorial radii.
// Polar radius is also known as semi-minor axis.
// Equatorial radius is also know as semi-major axis.
// All other items of interest about the ellipsoid are derived
// from these two data.
// Flattening is ( Equatorial radius - Polar radius ) / Equatorial radius
// There's another thing called First Eccentricity Squared, this is computed
// by ( 2 * Flattening ) - ( Flattening squared ).
// Coordinates - a means of expressing a point in space
//
// Cartesian coordinates are X, Y, and Z coordinates. These are always
// expressed in distances from 0, 0, 0 (i,e, the center of the earth).
//
// Polar coordinates are theta (often seen as a letter O with a line
// through its middle running left-to-right), phi (often seen as a
// letter O with a line through its middle running top-to-bottom), and
// r (a distance).
// These are two angles and a distance. The angles are measured from
// a common origin (the center of the earth). Theta is the plane that
// runs through the equator, phi runs through the poles. R is the
// distance along the line running along the phi angle. For simplicity
// sake, we will refer to theta as Equatorial angle, phi as the
// polar angle, and r as Polar Distance.
//
// Converting coordinates
//
// You can convert from polar to cartesian cordinates using the following
// formula:
// X = Polar distance * cosine of Polar angle * cosine of Equatorial angle
// Y = Polar distance * cosine of Polar angle * sine of Equatorial angle
// Z = Polar distance * sine of Polar angle
// Applying this to the real world
//
// Cartesian coordinates ar commonly refered to an ECEF X,Y,Z coordinates.
// This means Earth Centered, Earth Fixed. The 0,0,0 coordinate is the
// center of the earth. The X axis runs towards the Prime Meridian.
// The Y axis runs towards the equator and the Z axis runs toward
// the poles. Positive Z numbers go towards the North pole while
// negative numbers go towards the South Pole. Positive
// Computing Distance
//
// If you have two cartesian coordinates, you can compute a line
// of sight (as the bullet flies, aiming a laser, pointing in a straight line)
// by this formula (some guy named Pythagoras figured this out):
// SQRT( ( X1 - X2 ) ^ 2 + ( Y1 - Y2 ) ^ 2 + ( Z1 - Z2 ) ^ 2 )
//
// or in pseudo code:
//
// cartesian_coordinate first_location;
// cartesian_coordinate second_location;
//
// double temp_x;
// double temp_y;
// double temp_z;
//
// temp_x = first_location.X - second_location.X;
// temp_y = first_location.Y - second_location.Y;
// temp_z = first_location.Z - second_location.Z;
//
// temp_x = temp_x * temp_x; // square them
// temp_y = temp_y * temp_y;
// temp_z = temp_z * temp_z;
//
// double temp_double;
//
// temp_double = temp_x + temp_y + temp_z;
//
// double distance;
//
// distance = sqrt( temp_double );
//
// While this will give you distance, it will not give you direction.
// End of Sam Blackburn's prologue
// Remember: North is positive, south is negative
inline double NMEAParserGetDegreesFromNMEALat(const char nOrS,const char lat[10])
{
char buf[3];
double result;
buf[0] = lat[0];
buf[1] = lat[1];
buf[2] = '\0';
result = atof(buf);
if (nOrS == 'S') // NMEA uses capital letters
{
result = -result;
}
return result;
}
inline double NMEAParserGetMinutesFromNMEALat(const char lat[10])
{
char buf[8];
buf[0] = lat[2];
buf[1] = lat[3];
buf[2] = lat[4];
buf[3] = lat[5];
buf[4] = lat[6];
buf[5] = lat[7];
buf[6] = lat[8];
buf[7] = '\0';
return(atof(buf));
}
// Remember: West is negative, East is positive
inline double NMEAParserGetDegreesFromNMEALon(const char eOrW,const char lon[11])
{
char buf[4];
double result;
buf[0] = lon[0];
buf[1] = lon[1];
buf[2] = lon[2];
buf[3] = '\0';
result = atof(buf);
if (eOrW == 'W') // NMEA uses capital letters
{
result = -result;
}
return result;
}
static inline double NMEAParserGetMinutesFromNMEALon(const char lon[11])
{
char buf[8];
buf[0] = lon[3];
buf[1] = lon[4];
buf[2] = lon[5];
buf[3] = lon[6];
buf[4] = lon[7];
buf[5] = lon[8];
buf[6] = lon[9];
buf[7] = '\0';
return(atof(buf));
}
// WGS84 to Gauss-Krueger converter (recoding of Pascal sources from Ottmar Labonde)
class WGS84toDHDN
{
protected:
void HelmertTransformation(double,double,double,double&,double&,double&);
void BesselBLnachGaussKrueger(double,double,double&,double&);
void BLRauenberg (double,double,double,double&,double&,double&);
double neuF(double,double,double,double);
double round(double);
private:
double awgs; // WGS84 Semi-Major Axis = Equatorial Radius in meters
double bwgs; // WGS84 Semi-Minor Axis = Polar Radius in meters
double abes; // Bessel Semi-Major Axis = Equatorial Radius in meters
double bbes; // Bessel Semi-Minor Axis = Polar Radius in meters
double cbes; // Bessel ???
double dx; // Translation Parameter 1
double dy; // Translation Parameter 2
double dz; // Translation Parameter 3
double rotx; // Rotation Parameter 1
double roty; // Rotation Parameter 2
double rotz; // Rotation Parameter 3
double sc; // Scaling Factor
double Pi; // The famous number
double l1; // Helper
double b1; // Helper
double l2; // Helper
double b2; // Helper
double h1; // Helper
double h2; // Helper
double R; // Helper
double H; // Helper
double a; // Helper
double b; // Helper
double eq; // Helper
double N; // Helper
double Xq; // Helper
double Yq; // Helper
double Zq; // Helper
double X; // Helper
double Y; // Helper
double Z; // Helper
public:
WGS84toDHDN();
~WGS84toDHDN();
void Convert(double,double,double,double&,double&);
};
// Geodesy Foundation Classes (Sam Blackburn)
class CMath
{
// This class encapsulates all of the math functions. It is here to allow you
// to replace the C Runtime functions with your home-grown (and maybe better
// implementation) routines
public:
static inline double AbsoluteValue( const double& value );
static inline double ArcCosine( const double& value );
static inline double ArcSine( const double& value );
static inline double ArcTangent( const double& value );
static inline double ArcTangentOfYOverX( const double& y, const double& x );
static inline double Ceiling( const double& value );
static inline double ConvertDegreesToRadians( const double& degrees );
static inline double ConvertRadiansToDegrees( const double& radians );
static inline void ConvertDecimalDegreesToDegreesMinutesSeconds( double decimal_degrees, double& degrees, double& minutes, double& seconds );
// West is negative, East is positive, North is positive, south is negative
static inline double ConvertDegreesMinutesSecondsCoordinateToDecimalDegrees( double degrees, double minutes, double seconds );
static inline double Cosine( const double& value );
static inline double HyperbolicCosine( const double& value );
static inline double Pi( void );
static inline double Sine( const double& value );
static inline double SquareRoot( const double& value );
};
// This is Blackburn's inline functions implementation (original "CMath.inl")
inline double CMath::AbsoluteValue(const double& value)
{
return(::fabs(value));
}
inline double CMath::ArcCosine(const double& value)
{
return(::acos(value));
}
inline double CMath::ArcSine(const double& value)
{
return(::asin(value));
}
inline double CMath::ArcTangent(const double& value)
{
return(::atan(value));
}
inline double CMath::ArcTangentOfYOverX( const double& y, const double& x)
{
return(::atan2(y,x));
}
inline double CMath::Ceiling(const double& value)
{
return(::ceil(value));
}
inline void CMath::ConvertDecimalDegreesToDegreesMinutesSeconds(double decimal_degrees,
double& degrees,
double& minutes,
double& seconds)
{
double fractional_part = 0.0;
double integer_part = 0;
fractional_part = ::modf(decimal_degrees,&integer_part);
degrees = integer_part;
if (decimal_degrees < 0.0)
{
fractional_part *= (-1.0);
}
minutes = fractional_part * 60.0;
fractional_part = ::modf(minutes,&integer_part);
minutes = integer_part;
seconds = fractional_part * 60.0;
}
inline double CMath::ConvertDegreesMinutesSecondsCoordinateToDecimalDegrees(double degrees,
double minutes,
double seconds)
{
double decimal_degrees = 0.0;
decimal_degrees = degrees;
if (decimal_degrees < 0.0)
{
decimal_degrees *= (-1.0);
}
decimal_degrees += (double) (minutes / 60.0);
decimal_degrees += (double) (seconds / 3600.0);
if ( degrees < 0.0 )
{
decimal_degrees *= (-1.0);
}
return(decimal_degrees);
}
inline double CMath::ConvertDegreesToRadians(const double& degrees)
{
double radians = 0.0;
double pi_divided_by_180 = CMath::Pi() / 180.0;
radians = degrees * pi_divided_by_180;
return( radians );
}
inline double CMath::ConvertRadiansToDegrees(const double& radians)
{
double degrees = 0.0;
double conversion_factor = 180.0 / CMath::Pi();
degrees = radians * conversion_factor;
return( degrees );
}
inline double CMath::Cosine(const double& value)
{
return(::cos(value));
}
inline double CMath::HyperbolicCosine(const double& value)
{
return(::cosh(value));
}
inline double CMath::Pi(void)
{
return( 3.1415926535897932384626433832795028841971693993751058209749445923078164 );
}
inline double CMath::Sine(const double& value)
{
return(::sin(value));
}
inline double CMath::SquareRoot(const double& value)
{
return(::sqrt(value));
}
// End of the inline functions
class CEarthCoordinate
{
// This is a Cartesian coordinate (Earth Centered, Earth Fixed)
protected:
double m_X_CoordinateInMeters; // Positive points to intersection of the Prime Meridian and the equator
double m_Y_CoordinateInMeters; // Positive points to the intersection of 90 degrees east of Prime Meridian and the equator
double m_Z_CoordinateInMeters; // Positive points to the North Pole, Negative towards the South Pole
public:
CEarthCoordinate();
CEarthCoordinate(const CEarthCoordinate& source);
~CEarthCoordinate();
void Copy(const CEarthCoordinate& source);
void Get(double& x_coordinate,double& y_coordinate,double& z_coordinate) const;
double GetXCoordinateInMeters(void) const;
double GetYCoordinateInMeters(void) const;
double GetZCoordinateInMeters(void) const;
void Set(double x_coordinate,double y_coordinate,double z_coordinate);
void SetXCoordinateInMeters(double x_coordinate);
void SetYCoordinateInMeters(double y_coordinate);
void SetZCoordinateInMeters(double z_coordinate);
CEarthCoordinate& operator = (const CEarthCoordinate& source);
};
class CPolarCoordinate
{
protected:
double m_UpDownAngleInDegrees; // Polar Angle, Phi
double m_LeftRightAngleInDegrees; // Equatorial Angle, Theta
double m_DistanceFromSurfaceInMeters;
public:
CPolarCoordinate();
CPolarCoordinate(const CPolarCoordinate& source);
~CPolarCoordinate();
void Copy(const CPolarCoordinate& source);
void Get(double& up_down_angle,double& left_right_angle,double& distance_from_surface) const;
double GetUpDownAngleInDegrees(void) const;
double GetLeftRightAngleInDegrees(void) const;
double GetDistanceFromSurfaceInMeters(void) const;
void Set(double up_down_angle,double left_right_angle,double distance_from_surface);
void SetUpDownAngleInDegrees(double up_down_angle);
void SetLeftRightAngleInDegrees(double left_right_angle);
void SetDistanceFromSurfaceInMeters(double distance_from_surface);
CPolarCoordinate& operator = (const CPolarCoordinate& source);
};
class CEarth
{
protected:
// These are the magic numbers. They are the "real" data. They are facts,
// measurements. Everything else about an ellipse is derived (or computed from)
// these two data items.
double m_PolarRadiusInMeters;
double m_EquatorialRadiusInMeters;
// Here be the things that can be derived from our hard data.
// We compute these values using the two pieces of data that we
// know about the ellipse.
double m_Flattening;
double m_EccentricitySquared;
// Here's stuff specific to the C++ class
int m_EllipsoidID;
void m_ComputeFlattening( void );
void m_ComputeEccentricitySquared( void );
void m_Initialize( void );
public:
enum _EllipsoidType
{
Unknown,
Perfect_Sphere,
Airy,
Austrailian_National,
Bessell_1841,
Bessel_1841_Nambia,
Clarke_1866,
Clarke_1880,
Everest,
Fischer_1960_Mercury,
Fischer_1968,
GRS_1967,
GRS_1980,
Helmert_1906,
Hough,
International,
Krassovsky,
Modified_Airy,
Modified_Everest,
Modified_Fischer_1960,
South_American_1969,
Topex_Poseidon_Pathfinder_ITRF,
WGS_60,
WGS_66,
WGS_72,
WGS_84,
Custom
}
ELLIPSOIDS;
enum _DatumTypes
{
NAD_27 = Clarke_1866,
Tokyo = Bessell_1841,
}
DATUMS;
CEarth(int ellipsoid = WGS_84); // Default constructor sets WGS 84 model
~CEarth();
virtual void AddLineOfSightDistanceAndDirectionToCoordinate(const CPolarCoordinate& point_1,double distance,double direction,CPolarCoordinate& point_2,double height_above_surface_of_point_2 = 0.0);
virtual void AddSurfaceDistanceAndDirectionToCoordinate(const CEarthCoordinate& point_1,double distance,double direction,CEarthCoordinate& point_2);
virtual void AddSurfaceDistanceAndDirectionToCoordinate(const CEarthCoordinate& point_1,double distance,double direction,CPolarCoordinate& point_2);
virtual void AddSurfaceDistanceAndDirectionToCoordinate(const CPolarCoordinate& point_1,double distance,double direction,CEarthCoordinate& point_2);
virtual void AddSurfaceDistanceAndDirectionToCoordinate(const CPolarCoordinate& point_1,double distance,double direction,CPolarCoordinate& point_2);
virtual void Convert(const CEarthCoordinate& cartesian_coordinate,CPolarCoordinate& polar_coordinate) const;
virtual void Convert(const CPolarCoordinate& polar_coordinate,CEarthCoordinate& cartesian_coordinate) const;
virtual double GetDistanceToHorizon(const CEarthCoordinate& point_1) const;
virtual double GetDistanceToHorizon(const CPolarCoordinate& point_1) const;
virtual double GetEquatorialRadiusInMeters(void) const;
virtual double GetPolarRadiusInMeters(void) const;
virtual double GetLineOfSightDistanceFromCourse(const CEarthCoordinate& current_location,const CEarthCoordinate& point_a,const CEarthCoordinate& point_b) const;
virtual double GetLineOfSightDistance(const CEarthCoordinate& point_1,const CEarthCoordinate& point_2) const;
virtual double GetLineOfSightDistance(const CPolarCoordinate& point_1,const CEarthCoordinate& point_2) const;
virtual double GetLineOfSightDistance(const CEarthCoordinate& point_1,const CPolarCoordinate& point_2) const;
virtual double GetLineOfSightDistance(const CPolarCoordinate& point_1,const CPolarCoordinate& point_2) const;
virtual double GetSurfaceDistance(const CEarthCoordinate& point_1,const CEarthCoordinate& point_2,double * heading_from_point_1_to_point_2 = 0,double * heading_from_point_2_to_point_1 = 0) const;
virtual double GetSurfaceDistance(const CEarthCoordinate& point_1,const CPolarCoordinate& point_2,double * heading_from_point_1_to_point_2 = 0,double * heading_from_point_2_to_point_1 = 0) const;
virtual double GetSurfaceDistance(const CPolarCoordinate& point_1,const CEarthCoordinate& point_2,double * heading_from_point_1_to_point_2 = 0,double * heading_from_point_2_to_point_1 = 0) const;
virtual double GetSurfaceDistance(const CPolarCoordinate& point_1,const CPolarCoordinate& point_2,double * heading_from_point_1_to_point_2 = 0,double * heading_from_point_2_to_point_1 = 0) const;
virtual void SetEllipsoid(int ellipsoid);
virtual void SetEllipsoidByRadii(double equatorial_radius,double polar_radius);
virtual void SetEllipsoidByEquatorialRadiusAndFlattening(double equatorial_radius,double flattening);
};
#endif // COORDINATETRANSFORMATIONS_H__INCLUDED_