UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
Bonne.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: BONNE
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions between Geodetic coordinates
9  * (latitude and longitude in radians) and Bonne projection coordinates
10  * (easting and northing in meters).
11  *
12  * ERROR HANDLING
13  *
14  * This component checks parameters for valid values. If an invalid value
15  * is found, the error code is combined with the current error code using
16  * the bitwise or. This combining allows multiple error codes to be
17  * returned. The possible error codes are:
18  *
19  * BONN_NO_ERROR : No errors occurred in function
20  * BONN_LAT_ERROR : Latitude outside of valid range
21  * (-90 to 90 degrees)
22  * BONN_LON_ERROR : Longitude outside of valid range
23  * (-180 to 360 degrees)
24  * BONN_EASTING_ERROR : Easting outside of valid range
25  * (False_Easting +/- ~20,500,000 m,
26  * depending on ellipsoid parameters
27  * and Origin_Latitude)
28  * BONN_NORTHING_ERROR : Northing outside of valid range
29  * (False_Northing +/- ~23,500,000 m,
30  * depending on ellipsoid parameters
31  * and Origin_Latitude)
32  * BONN_ORIGIN_LAT_ERROR : Origin latitude outside of valid range
33  * (-90 to 90 degrees)
34  * BONN_CENT_MER_ERROR : Central meridian outside of valid range
35  * (-180 to 360 degrees)
36  * BONN_A_ERROR : Semi-major axis less than or equal to zero
37  * BONN_INV_F_ERROR : Inverse flattening outside of valid range
38  * (250 to 350)
39  *
40  * REUSE NOTES
41  *
42  * BONNE is intended for reuse by any application that performs a
43  * Bonne projection or its inverse.
44  *
45  * REFERENCES
46  *
47  * Further information on BONNE can be found in the Reuse Manual.
48  *
49  * BONNE originated from : U.S. Army Topographic Engineering Center
50  * Geospatial Information Division
51  * 7701 Telegraph Road
52  * Alexandria, VA 22310-3864
53  *
54  * LICENSES
55  *
56  * None apply to this component.
57  *
58  * RESTRICTIONS
59  *
60  * BONNE has no restrictions.
61  *
62  * ENVIRONMENT
63  *
64  * BONNE was tested and certified in the following environments:
65  *
66  * 1. Solaris 2.5 with GCC 2.8.1
67  * 2. MS Windows 95 with MS Visual C++ 6
68  *
69  * MODIFICATIONS
70  *
71  * Date Description
72  * ---- -----------
73  * 04-16-99 Original Code
74  * 03-05-07 Original C++ Code
75  *
76  */
77 
78 
79 /***************************************************************************/
80 /*
81  * INCLUDES
82  */
83 
84 #include <math.h>
85 #include "Bonne.h"
86 #include "Sinusoidal.h"
89 #include "GeodeticCoordinates.h"
91 #include "ErrorMessages.h"
92 
93 /*
94  * math.h - Is needed to call the math functions (sqrt, pow, exp, log,
95  * sin, cos, tan, and atan).
96  * Bonne.h - Is for prototype error checking.
97  * Sinusoidal.h - Is called when the origin latitude is zero.
98  * MapProjectionCoordinates.h - defines map projection coordinates
99  * GeodeticCoordinates.h - defines geodetic coordinates
100  * CoordinateConversionException.h - Exception handler
101  * ErrorMessages.h - Contains exception messages
102  */
103 
104 
105 using namespace MSP::CCS;
106 
107 
108 /***************************************************************************/
109 /* DEFINES
110  *
111  */
112 
113 const double PI = 3.14159265358979323e0; /* PI */
114 const double PI_OVER_2 = (PI / 2.0);
115 const double TWO_PI = (2.0 * PI);
116 
117 
118 double bonnCoeffTimesSine( double coeff, double x, double latit )
119 {
120  return coeff * (sin(x * latit));
121 }
122 
123 
124 /************************************************************************/
125 /* FUNCTIONS
126  *
127  */
128 
129 Bonne::Bonne( double ellipsoidSemiMajorAxis, double ellipsoidFlattening, double centralMeridian, double originLatitude, double falseEasting, double falseNorthing ) :
131  sinusoidal( 0 ),
132  es2( 0.0066943799901413800 ),
133  es4( 4.4814723452405e-005 ),
134  es6( 3.0000678794350e-007 ),
135  M1( 4984944.3782319 ),
136  m1( .70829317069372 ),
137  c0( .99832429845280 ),
138  c1( .0025146070605187 ),
139  c2( 2.6390465943377e-006 ),
140  c3( 3.4180460865959e-009 ),
141  a0( .0025188265843907 ),
142  a1( 3.7009490356205e-006 ),
143  a2( 7.4478137675038e-009 ),
144  a3( 1.7035993238596e-011 ),
145  Bonn_Origin_Long( 0.0 ),
146  Bonn_Origin_Lat( ((45 * PI) / 180.0) ),
147  Bonn_False_Easting( 0.0 ),
148  Bonn_False_Northing( 0.0 ),
149  Sin_Bonn_Origin_Lat( .70710678118655 ),
150  Bonn_am1sin( 6388838.2901211 ),
151  Bonn_Max_Easting( 20027474.0 ),
152  Bonn_Min_Easting( -20027474.0 ),
153  Bonn_Delta_Northing( 20003932.0 )
154 {
155 /*
156  * The constructor receives the ellipsoid parameters and
157  * Bonne projection parameters as inputs, and sets the corresponding state
158  * variables. If any errors occur, an exception is thrown with a description
159  * of the error.
160  *
161  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (input)
162  * ellipsoidFlattening : Flattening of ellipsoid (input)
163  * centralMeridian : Longitude in radians at the center of (input)
164  * the projection
165  * originLatitude : Latitude in radians at which the (input)
166  * point scale factor is 1.0
167  * falseEasting : A coordinate value in meters assigned to the
168  * central meridian of the projection. (input)
169  * falseNorthing : A coordinate value in meters assigned to the
170  * origin latitude of the projection (input)
171  */
172 
173  double j, three_es4;
174  double x,e1,e2,e3,e4;
175  double clat;
176  double sin2lat, sin4lat, sin6lat, lat;
177  double inv_f = 1 / ellipsoidFlattening;
178 
179  if (ellipsoidSemiMajorAxis <= 0.0)
180  { /* Semi-major axis must be greater than zero */
182  }
183  if ((inv_f < 250) || (inv_f > 350))
184  { /* Inverse flattening must be between 250 and 350 */
186  }
187  if ((originLatitude < -PI_OVER_2) || (originLatitude > PI_OVER_2))
188  { /* origin latitude out of range */
190  }
191  if ((centralMeridian < -PI) || (centralMeridian > TWO_PI))
192  { /* origin longitude out of range */
194  }
195 
196  semiMajorAxis = ellipsoidSemiMajorAxis;
197  flattening = ellipsoidFlattening;
198 
199  Bonn_Origin_Lat = originLatitude;
200  if (centralMeridian > PI)
201  centralMeridian -= TWO_PI;
202  Bonn_Origin_Long = centralMeridian;
203  Bonn_False_Northing = falseNorthing;
204  Bonn_False_Easting = falseEasting;
205  if (Bonn_Origin_Lat == 0.0)
206  {
207  if (Bonn_Origin_Long > 0)
208  {
209  Bonn_Max_Easting = 19926189.0;
210  Bonn_Min_Easting = -20037509.0;
211  }
212  else if (Bonn_Origin_Long < 0)
213  {
214  Bonn_Max_Easting = 20037509.0;
215  Bonn_Min_Easting = -19926189.0;
216  }
217  else
218  {
219  Bonn_Max_Easting = 20037509.0;
220  Bonn_Min_Easting = -20037509.0;
221  }
222  Bonn_Delta_Northing = 10001966.0;
223 
224  sinusoidal = new Sinusoidal( semiMajorAxis, flattening, Bonn_Origin_Long, Bonn_False_Easting, Bonn_False_Northing );
225  }
226  else
227  {
228  Sin_Bonn_Origin_Lat = sin(Bonn_Origin_Lat);
229 
230  es2 = 2 * flattening - flattening * flattening;
231  es4 = es2 * es2;
232  es6 = es4 * es2;
233  j = 45.0 * es6 / 1024.0;
234  three_es4 = 3.0 * es4;
235  c0 = 1 - es2 / 4.0 - three_es4 / 64.0 - 5.0 * es6 / 256.0;
236  c1 = 3.0 * es2 / 8.0 + three_es4 / 32.0 + j;
237  c2 = 15.0 * es4 / 256.0 + j;
238  c3 = 35.0 * es6 / 3072.0;
239 
240  clat = cos(Bonn_Origin_Lat);
241  m1 = bonnm(clat, Sin_Bonn_Origin_Lat);
242 
243  lat = c0 * Bonn_Origin_Lat;
244  sin2lat = bonnCoeffTimesSine(c1, 2.0, Bonn_Origin_Lat);
245  sin4lat = bonnCoeffTimesSine(c2, 4.0, Bonn_Origin_Lat);
246  sin6lat = bonnCoeffTimesSine(c3, 6.0, Bonn_Origin_Lat);
247  M1 = bonnM(lat, sin2lat, sin4lat, sin6lat);
248 
249  x = sqrt (1.0 - es2);
250  e1 = (1.0 - x) / (1.0 + x);
251  e2 = e1 * e1;
252  e3 = e2 * e1;
253  e4 = e3 * e1;
254  a0 = 3.0 * e1 / 2.0 - 27.0 * e3 / 32.0;
255  a1 = 21.0 * e2 / 16.0 - 55.0 * e4 / 32.0;
256  a2 = 151.0 * e3 / 96.0;
257  a3 = 1097.0 * e4 / 512.0;
258  if (Sin_Bonn_Origin_Lat == 0.0)
259  Bonn_am1sin = 0.0;
260  else
261  Bonn_am1sin = semiMajorAxis * m1 / Sin_Bonn_Origin_Lat;
262 
263  Bonn_Max_Easting = 20027474.0;
264  Bonn_Min_Easting = -20027474.0;
265  Bonn_Delta_Northing = 20003932.0;
266  }
267 }
268 
269 
270 Bonne::Bonne( const Bonne &b )
271 {
272  sinusoidal = new Sinusoidal( *( b.sinusoidal ) );
275  es2 = b.es2;
276  es4 = b.es4;
277  es6 = b.es6;
278  M1 = b.M1;
279  m1 = b.m1;
280  c0 = b.c0;
281  c1 = b.c1;
282  c2 = b.c2;
283  c3 = b.c3;
284  a0 = b.a0;
285  a1 = b.a1;
286  a2 = b.a2;
287  a3 = b.a3;
288  Bonn_Origin_Long = b.Bonn_Origin_Long;
289  Bonn_Origin_Lat = b.Bonn_Origin_Lat;
290  Bonn_False_Easting = b.Bonn_False_Easting;
291  Bonn_False_Northing = b.Bonn_False_Northing;
292  Sin_Bonn_Origin_Lat = b.Sin_Bonn_Origin_Lat;
293  Bonn_am1sin = b.Bonn_am1sin;
294  Bonn_Max_Easting = b.Bonn_Max_Easting;
295  Bonn_Min_Easting = b.Bonn_Min_Easting;
296  Bonn_Delta_Northing = b.Bonn_Delta_Northing;
297 }
298 
299 
301 {
302  if( sinusoidal )
303  {
304  delete sinusoidal;
305  sinusoidal = 0;
306  }
307 }
308 
309 
311 {
312  if( this != &b )
313  {
314  sinusoidal->operator=( *b.sinusoidal );
317  es2 = b.es2;
318  es4 = b.es4;
319  es6 = b.es6;
320  M1 = b.M1;
321  m1 = b.m1;
322  c0 = b.c0;
323  c1 = b.c1;
324  c2 = b.c2;
325  c3 = b.c3;
326  a0 = b.a0;
327  a1 = b.a1;
328  a2 = b.a2;
329  a3 = b.a3;
330  Bonn_Origin_Long = b.Bonn_Origin_Long;
331  Bonn_Origin_Lat = b.Bonn_Origin_Lat;
332  Bonn_False_Easting = b.Bonn_False_Easting;
333  Bonn_False_Northing = b.Bonn_False_Northing;
334  Sin_Bonn_Origin_Lat = b.Sin_Bonn_Origin_Lat;
335  Bonn_am1sin = b.Bonn_am1sin;
336  Bonn_Max_Easting = b.Bonn_Max_Easting;
337  Bonn_Min_Easting = b.Bonn_Min_Easting;
338  Bonn_Delta_Northing = b.Bonn_Delta_Northing;
339  }
340 
341  return *this;
342 }
343 
344 
346 {
347 /*
348  * The function getParameters returns the current ellipsoid
349  * parameters and Bonne projection parameters.
350  *
351  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (output)
352  * ellipsoidFlattening : Flattening of ellipsoid (output)
353  * centralMeridian : Longitude in radians at the center of (output)
354  * the projection
355  * originLatitude : Latitude in radians at which the (output)
356  * point scale factor is 1.0
357  * falseEasting : A coordinate value in meters assigned to the
358  * central meridian of the projection. (output)
359  * falseNorthing : A coordinate value in meters assigned to the
360  * origin latitude of the projection (output)
361  */
362 
363  return new MapProjection4Parameters( CoordinateType::bonne, Bonn_Origin_Long, Bonn_Origin_Lat, Bonn_False_Easting, Bonn_False_Northing );
364 }
365 
366 
368 {
369 /*
370  * The function convertFromGeodetic converts geodetic (latitude and
371  * longitude) coordinates to Bonne projection (easting and northing)
372  * coordinates, according to the current ellipsoid and Bonne projection
373  * parameters. If any errors occur, an exception is thrown with a description
374  * of the error.
375  *
376  * longitude : Longitude (lambda) in radians (input)
377  * latitude : Latitude (phi) in radians (input)
378  * easting : Easting (X) in meters (output)
379  * northing : Northing (Y) in meters (output)
380  */
381 
382  double dlam; /* Longitude - Central Meridan */
383  double mm;
384  double MM;
385  double rho;
386  double EE;
387  double lat, sin2lat, sin4lat, sin6lat;
388  double easting, northing;
389 
390  double longitude = geodeticCoordinates->longitude();
391  double latitude = geodeticCoordinates->latitude();
392  double clat = cos(latitude);
393  double slat = sin(latitude);
394 
395  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
396  { /* Latitude out of range */
398  }
399  if ((longitude < -PI) || (longitude > TWO_PI))
400  { /* Longitude out of range */
402  }
403 
404  if (Bonn_Origin_Lat == 0.0)
405  return sinusoidal->convertFromGeodetic( geodeticCoordinates );
406  else
407  {
408  dlam = longitude - Bonn_Origin_Long;
409  if (dlam > PI)
410  {
411  dlam -= TWO_PI;
412  }
413  if (dlam < -PI)
414  {
415  dlam += TWO_PI;
416  }
417  if ((latitude - Bonn_Origin_Lat) == 0.0 && floatEq(fabs(latitude),PI_OVER_2,.00001))
418  {
419  easting = 0.0;
420  northing = 0.0;
421  }
422  else
423  {
424  mm = bonnm(clat, slat);
425  lat = c0 * latitude;
426  sin2lat = bonnCoeffTimesSine(c1, 2.0, latitude);
427  sin4lat = bonnCoeffTimesSine(c2, 4.0, latitude);
428  sin6lat = bonnCoeffTimesSine(c3, 6.0, latitude);
429  MM = bonnM(lat, sin2lat, sin4lat, sin6lat);
430 
431  rho = Bonn_am1sin + M1 - MM;
432  if (rho == 0)
433  EE = 0;
434  else
435  EE = semiMajorAxis * mm * dlam / rho;
436  easting = rho * sin(EE) + Bonn_False_Easting;
437  northing = Bonn_am1sin - rho * cos(EE) + Bonn_False_Northing;
438  }
439 
440  return new MapProjectionCoordinates(
441  CoordinateType::bonne, easting, northing );
442  }
443 }
444 
445 
447  MSP::CCS::MapProjectionCoordinates* mapProjectionCoordinates )
448 {
449 /*
450  * The function convertToGeodetic converts Bonne projection
451  * (easting and northing) coordinates to geodetic (latitude and longitude)
452  * coordinates, according to the current ellipsoid and Bonne projection
453  * coordinates. If any errors occur, an exception is thrown with a description
454  * of the error.
455  *
456  * easting : Easting (X) in meters (input)
457  * northing : Northing (Y) in meters (input)
458  * longitude : Longitude (lambda) in radians (output)
459  * latitude : Latitude (phi) in radians (output)
460  */
461 
462  double dx; /* Delta easting - Difference in easting (easting-FE) */
463  double dy; /* Delta northing - Difference in northing (northing-FN) */
464  double mu;
465  double MM;
466  double mm;
467  double am1sin_dy;
468  double rho;
469  double sin2mu, sin4mu, sin6mu, sin8mu;
470  double clat, slat;
471  double longitude, latitude;
472 
473  double easting = mapProjectionCoordinates->easting();
474  double northing = mapProjectionCoordinates->northing();
475 
476  if ((easting < (Bonn_False_Easting + Bonn_Min_Easting))
477  || (easting > (Bonn_False_Easting + Bonn_Max_Easting)))
478  { /* Easting out of range */
480  }
481  if ((northing < (Bonn_False_Northing - Bonn_Delta_Northing))
482  || (northing > (Bonn_False_Northing + Bonn_Delta_Northing)))
483  { /* Northing out of range */
485  }
486 
487  if (Bonn_Origin_Lat == 0.0)
488  return sinusoidal->convertToGeodetic( mapProjectionCoordinates );
489  else
490  {
491  dy = northing - Bonn_False_Northing;
492  dx = easting - Bonn_False_Easting;
493  am1sin_dy = Bonn_am1sin - dy;
494  rho = sqrt(dx * dx + am1sin_dy * am1sin_dy);
495  if (Bonn_Origin_Lat < 0.0)
496  rho = -rho;
497  MM = Bonn_am1sin + M1 - rho;
498 
499  mu = MM / (semiMajorAxis * c0);
500  sin2mu = bonnCoeffTimesSine(a0, 2.0, mu);
501  sin4mu = bonnCoeffTimesSine(a1, 4.0, mu);
502  sin6mu = bonnCoeffTimesSine(a2, 6.0, mu);
503  sin8mu = bonnCoeffTimesSine(a3, 8.0, mu);
504  latitude = mu + sin2mu + sin4mu + sin6mu + sin8mu;
505 
506  if (floatEq(fabs(latitude),PI_OVER_2,.00001))
507  {
508  longitude = Bonn_Origin_Long;
509  }
510  else
511  {
512  clat = cos(latitude);
513  slat = sin(latitude);
514  mm = bonnm(clat, slat);
515 
516  if (Bonn_Origin_Lat < 0.0)
517  {
518  dx = -dx;
519  am1sin_dy = -am1sin_dy;
520  }
521  longitude = Bonn_Origin_Long + rho * (atan2(dx, am1sin_dy)) /
522  (semiMajorAxis * mm);
523  }
524 
525  if (latitude > PI_OVER_2) /* force distorted values to 90, -90 degrees */
526  latitude = PI_OVER_2;
527  else if (latitude < -PI_OVER_2)
528  latitude = -PI_OVER_2;
529 
530  if (longitude > PI)
531  longitude -= TWO_PI;
532  if (longitude < -PI)
533  longitude += TWO_PI;
534 
535  if (longitude > PI) /* force distorted values to 180, -180 degrees */
536  longitude = PI;
537  else if (longitude < -PI)
538  longitude = -PI;
539 
540  return new GeodeticCoordinates(
541  CoordinateType::geodetic, longitude, latitude );
542  }
543 }
544 
545 
546 double Bonne::bonnm( double coslat, double sinlat )
547 {
548  return coslat/sqrt(1.0 - es2*sinlat*sinlat);
549 }
550 
551 
552 double Bonne::bonnM( double c0lat, double c1s2lat, double c2s4lat, double c3s6lat )
553 {
554  return semiMajorAxis*(c0lat-c1s2lat+c2s4lat-c3s6lat);
555 }
556 
557 
558 double Bonne::floatEq( double x, double v, double epsilon )
559 {
560  return ((v - epsilon) < x) && (x < (v + epsilon));
561 }
562 
563 
564 
565 // CLASSIFICATION: UNCLASSIFIED