UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
Cassini.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: CASSINI
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions between Geodetic coordinates
9  * (latitude and longitude in radians) and Cassini 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  * CASS_NO_ERROR : No errors occurred in function
20  * CASS_LAT_ERROR : Latitude outside of valid range
21  * (-90 to 90 degrees)
22  * CASS_LON_ERROR : Longitude outside of valid range
23  * (-180 to 360 degrees)
24  * CASS_EASTING_ERROR : Easting outside of valid range
25  * (False_Easting +/- ~20,000,000 m,
26  * depending on ellipsoid parameters
27  * and Origin_Latitude)
28  * CASS_NORTHING_ERROR : Northing outside of valid range
29  * (False_Northing +/- ~57,000,000 m,
30  * depending on ellipsoid parameters
31  * and Origin_Latitude)
32  * CASS_ORIGIN_LAT_ERROR : Origin latitude outside of valid range
33  * (-90 to 90 degrees)
34  * CASS_CENT_MER_ERROR : Central meridian outside of valid range
35  * (-180 to 360 degrees)
36  * CASS_A_ERROR : Semi-major axis less than or equal to zero
37  * CASS_INV_F_ERROR : Inverse flattening outside of valid range
38  * (250 to 350)
39  * CASS_LON_WARNING : Distortion will result if longitude is more
40  * than 4 degrees from the Central Meridian
41  *
42  * REUSE NOTES
43  *
44  * CASSINI is intended for reuse by any application that performs a
45  * Cassini projection or its inverse.
46  *
47  * REFERENCES
48  *
49  * Further information on CASSINI can be found in the Reuse Manual.
50  *
51  * CASSINI originated from : U.S. Army Topographic Engineering Center
52  * Geospatial Information Division
53  * 7701 Telegraph Road
54  * Alexandria, VA 22310-3864
55  *
56  * LICENSES
57  *
58  * None apply to this component.
59  *
60  * RESTRICTIONS
61  *
62  * CASSINI has no restrictions.
63  *
64  * ENVIRONMENT
65  *
66  * CASSINI was tested and certified in the following environments:
67  *
68  * 1. Solaris 2.5 with GCC 2.8.1
69  * 2. MS Windows 95 with MS Visual C++ 6
70  *
71  * MODIFICATIONS
72  *
73  * Date Description
74  * ---- -----------
75  * 04-16-99 Original Code
76  * 03-07-07 Original C++ Code
77  *
78  */
79 
80 
81 /***************************************************************************/
82 /*
83  * INCLUDES
84  */
85 
86 #include <math.h>
87 #include "Cassini.h"
90 #include "GeodeticCoordinates.h"
92 #include "ErrorMessages.h"
93 #include "WarningMessages.h"
94 
95 /*
96  * math.h - Standard C math library
97  * Cassini.h - Is for prototype error checking.
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  * WarningMessages.h - Contains warning messages
103  */
104 
105 
106 using namespace MSP::CCS;
107 
108 
109 /***************************************************************************/
110 /* DEFINES
111  *
112  */
113 
114 const double PI = 3.14159265358979323e0; /* PI */
115 const double PI_OVER_2 = ( PI / 2.0);
116 const double TWO_PI = (2.0 * PI);
117 const double THIRTY_ONE = (31.0 * PI / 180); /* 31 degrees in radians */
118 
119 double cassCoeffTimesSine( double coeff, double x, double latit )
120 {
121  return coeff * (sin (x * latit));
122 }
123 
124 
125 /************************************************************************/
126 /* FUNCTIONS
127  *
128  */
129 
130 Cassini::Cassini( double ellipsoidSemiMajorAxis, double ellipsoidFlattening, double centralMeridian, double originLatitude, double falseEasting, double falseNorthing ) :
132  es2( 0.0066943799901413800 ),
133  es4( 4.4814723452405e-005 ),
134  es6( 3.0000678794350e-007 ),
135  M0( 0.0 ),
136  c0( .99832429845280 ),
137  c1( .0025146070605187 ),
138  c2( 2.6390465943377e-006 ),
139  c3( 3.4180460865959e-009 ),
140  One_Minus_es2( .99330562000986 ),
141  a0( .0025188265843907 ),
142  a1( 3.7009490356205e-006 ),
143  a2( 7.4478137675038e-009 ),
144  a3( 1.7035993238596e-011 ),
145  Cass_Origin_Long( 0.0 ),
146  Cass_Origin_Lat( 0.0 ),
147  Cass_False_Easting( 0.0 ),
148  Cass_False_Northing( 0.0 ),
149  Cass_Min_Easting( -20037508.4 ),
150  Cass_Max_Easting( 20037508.4 ),
151  Cass_Min_Northing( -56575846.0 ),
152  Cass_Max_Northing( 56575846.0 )
153 {
154 /*
155  * The constructor receives the ellipsoid parameters and
156  * Cassini projection parameters as inputs, and sets the corresponding state
157  * variables. If any errors occur, an exception is thrown with a description
158  * of the error.
159  *
160  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (input)
161  * ellipsoidFlattening : Flattening of ellipsoid (input)
162  * centralMeridian : Longitude in radians at the center of (input)
163  * the projection
164  * originLatitude : Latitude in radians at which the (input)
165  * point scale factor is 1.0
166  * falseEasting : A coordinate value in meters assigned to the
167  * central meridian of the projection. (input)
168  * falseNorthing : A coordinate value in meters assigned to the
169  * origin latitude of the projection (input)
170  */
171 
172  double j,three_es4;
173  double x, e1, e2, e3, e4;
174  double lat, sin2lat, sin4lat, sin6lat;
175  double inv_f = 1 / ellipsoidFlattening;
176 
177  if (ellipsoidSemiMajorAxis <= 0.0)
178  { /* Semi-major axis must be greater than zero */
180  }
181  if ((inv_f < 250) || (inv_f > 350))
182  { /* Inverse flattening must be between 250 and 350 */
184  }
185  if ((originLatitude < -PI_OVER_2) || (originLatitude > PI_OVER_2))
186  { /* origin latitude out of range */
188  }
189  if ((centralMeridian < -PI) || (centralMeridian > TWO_PI))
190  { /* origin longitude out of range */
192  }
193 
194  semiMajorAxis = ellipsoidSemiMajorAxis;
195  flattening = ellipsoidFlattening;
196 
197  Cass_Origin_Lat = originLatitude;
198  if (centralMeridian > PI)
199  centralMeridian -= TWO_PI;
200  Cass_Origin_Long = centralMeridian;
201  Cass_False_Northing = falseNorthing;
202  Cass_False_Easting = falseEasting;
203  es2 = 2 * flattening - flattening * flattening;
204  es4 = es2 * es2;
205  es6 = es4 * es2;
206  j = 45.0 * es6 / 1024.0;
207  three_es4 = 3.0 * es4;
208  c0 = 1 - es2 / 4.0 - three_es4 / 64.0 - 5.0 * es6 / 256.0;
209  c1 = 3.0 * es2 /8.0 + three_es4 / 32.0 + j;
210  c2 = 15.0 * es4 / 256.0 + j;
211  c3 = 35.0 * es6 / 3072.0;
212  lat = c0 * Cass_Origin_Lat;
213  sin2lat = cassCoeffTimesSine(c1, 2.0, Cass_Origin_Lat);
214  sin4lat = cassCoeffTimesSine(c2, 4.0, Cass_Origin_Lat);
215  sin6lat = cassCoeffTimesSine(c3, 6.0, Cass_Origin_Lat);
216  M0 = cassM( lat, sin2lat, sin4lat, sin6lat );
217 
218  One_Minus_es2 = 1.0 - es2;
219  x = sqrt (One_Minus_es2);
220  e1 = (1 - x) / (1 + x);
221  e2 = e1 * e1;
222  e3 = e2 * e1;
223  e4 = e3 * e1;
224  a0 = 3.0 * e1 / 2.0 - 27.0 * e3 / 32.0;
225  a1 = 21.0 * e2 / 16.0 - 55.0 * e4 / 32.0;
226  a2 = 151.0 * e3 / 96.0;
227  a3 = 1097.0 * e4 /512.0;
228 
229  if (Cass_Origin_Long > 0)
230  {
231  GeodeticCoordinates gcMax( CoordinateType::geodetic, Cass_Origin_Long - PI, THIRTY_ONE );
232  MapProjectionCoordinates* tempCoordinates = convertFromGeodetic( &gcMax );
233  Cass_Max_Northing = tempCoordinates->northing();
234  delete tempCoordinates;
235 
236  GeodeticCoordinates gcMin( CoordinateType::geodetic, Cass_Origin_Long - PI, -THIRTY_ONE );
237  tempCoordinates = convertFromGeodetic( &gcMin );
238  Cass_Min_Northing = tempCoordinates->northing();
239  delete tempCoordinates;
240 
241  Cass_Max_Easting = 19926188.9;
242  Cass_Min_Easting = -20037508.4;
243  }
244  else if (Cass_Origin_Long < 0)
245  {
246  GeodeticCoordinates gcMax( CoordinateType::geodetic, PI + Cass_Origin_Long, THIRTY_ONE );
247  MapProjectionCoordinates* tempCoordinates = convertFromGeodetic( &gcMax );
248  Cass_Max_Northing = tempCoordinates->northing();
249  delete tempCoordinates;
250 
251  GeodeticCoordinates gcMin( CoordinateType::geodetic, PI + Cass_Origin_Long, -THIRTY_ONE );
252  tempCoordinates = convertFromGeodetic( &gcMin );
253  Cass_Min_Northing = tempCoordinates->northing();
254  delete tempCoordinates;
255 
256  Cass_Max_Easting = 20037508.4;
257  Cass_Min_Easting = -19926188.9;
258  }
259  else
260  {
262  MapProjectionCoordinates* tempCoordinates = convertFromGeodetic( &gcMax );
263  Cass_Max_Northing = tempCoordinates->northing();
264  delete tempCoordinates;
265 
267  tempCoordinates = convertFromGeodetic( &gcMin );
268  Cass_Min_Northing = tempCoordinates->northing();
269  delete tempCoordinates;
270 
271  Cass_Max_Easting = 20037508.4;
272  Cass_Min_Easting = -20037508.4;
273  }
274 
275  if(Cass_False_Northing)
276  {
277  Cass_Min_Northing -= Cass_False_Northing;
278  Cass_Max_Northing -= Cass_False_Northing;
279  }
280 }
281 
282 
284 {
287  es2 = c.es2;
288  es4 = c.es4;
289  es6 = c.es6;
290  M0 = c.M0;
291  c0 = c.c0;
292  c1 = c.c1;
293  c2 = c.c2;
294  c3 = c.c3;
295  One_Minus_es2 = c.One_Minus_es2;
296  a0 = c.a0;
297  a1 = c.a1;
298  a2 = c.a2;
299  a3 = c.a3;
300  Cass_Origin_Long = c.Cass_Origin_Long;
301  Cass_Origin_Lat = c.Cass_Origin_Lat;
302  Cass_False_Easting = c.Cass_False_Easting;
303  Cass_False_Northing = c.Cass_False_Northing;
304  Cass_Min_Easting = c.Cass_Min_Easting;
305  Cass_Max_Easting = c.Cass_Max_Easting;
306  Cass_Min_Northing = c.Cass_Min_Northing;
307  Cass_Max_Northing = c.Cass_Max_Northing;
308 }
309 
310 
312 {
313 }
314 
315 
317 {
318  if( this != &c )
319  {
322  es2 = c.es2;
323  es4 = c.es4;
324  es6 = c.es6;
325  M0 = c.M0;
326  c0 = c.c0;
327  c1 = c.c1;
328  c2 = c.c2;
329  c3 = c.c3;
330  One_Minus_es2 = c.One_Minus_es2;
331  a0 = c.a0;
332  a1 = c.a1;
333  a2 = c.a2;
334  a3 = c.a3;
335  Cass_Origin_Long = c.Cass_Origin_Long;
336  Cass_Origin_Lat = c.Cass_Origin_Lat;
337  Cass_False_Easting = c.Cass_False_Easting;
338  Cass_False_Northing = c.Cass_False_Northing;
339  Cass_Min_Easting = c.Cass_Min_Easting;
340  Cass_Max_Easting = c.Cass_Max_Easting;
341  Cass_Min_Northing = c.Cass_Min_Northing;
342  Cass_Max_Northing = c.Cass_Max_Northing;
343  }
344 
345  return *this;
346 }
347 
348 
350 {
351 /*
352  * The function getParameters returns the current ellipsoid
353  * parameters, Cassini projection parameters.
354  *
355  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (output)
356  * ellipsoidFlattening : Flattening of ellipsoid (output)
357  * centralMeridian : Longitude in radians at the center of (output)
358  * the projection
359  * originLatitude : Latitude in radians at which the (output)
360  * point scale factor is 1.0
361  * falseEasting : A coordinate value in meters assigned to the
362  * central meridian of the projection. (output)
363  * falseNorthing : A coordinate value in meters assigned to the
364  * origin latitude of the projection (output)
365  */
366 
367  return new MapProjection4Parameters( CoordinateType::cassini, Cass_Origin_Long, Cass_Origin_Lat, Cass_False_Easting, Cass_False_Northing );
368 }
369 
370 
372 {
373 /*
374  * The function convertFromGeodetic converts geodetic (latitude and
375  * longitude) coordinates to Cassini projection (easting and northing)
376  * coordinates, according to the current ellipsoid and Cassini projection
377  * parameters. If any errors occur, an exception is thrown with a description
378  * of the error.
379  *
380  * longitude : Longitude (lambda) in radians (input)
381  * latitude : Latitude (phi) in radians (input)
382  * easting : Easting (X) in meters (output)
383  * northing : Northing (Y) in meters (output)
384  */
385 
386  double lat, sin2lat, sin4lat, sin6lat;
387  double RD;
388  double dlam; /* Longitude - Central Meridan */
389  double NN;
390  double TT;
391  double AA, A2, A3, A4, A5;
392  double CC;
393  double MM;
394 
395  double longitude = geodeticCoordinates->longitude();
396  double latitude = geodeticCoordinates->latitude();
397  double tlat = tan(latitude);
398  double clat = cos(latitude);
399  double slat = sin(latitude);
400 
401  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
402  { /* Latitude out of range */
404  }
405  if ((longitude < -PI) || (longitude > TWO_PI))
406  { /* Longitude out of range */
408  }
409 
410  dlam = longitude - Cass_Origin_Long;
411 
412  char warning[100];
413  warning[0] = '\0';
414  if (fabs(dlam) > (4.0 * PI / 180))
415  { /* Distortion results if Longitude is > 4 deg from the Central Meridian */
416  strcat( warning, MSP::CCS::WarningMessages::longitude );
417  }
418 
419  if (dlam > PI)
420  {
421  dlam -= TWO_PI;
422  }
423  if (dlam < -PI)
424  {
425  dlam += TWO_PI;
426  }
427  RD = cassRd( slat );
428  NN = semiMajorAxis / RD;
429  TT = tlat * tlat;
430  AA = dlam * clat;
431  A2 = AA * AA;
432  A3 = AA * A2;
433  A4 = AA * A3;
434  A5 = AA * A4;
435  CC = es2 * clat * clat / One_Minus_es2;
436  lat = c0 * latitude;
437  sin2lat = cassCoeffTimesSine(c1, 2.0, latitude);
438  sin4lat = cassCoeffTimesSine(c2, 4.0, latitude);
439  sin6lat = cassCoeffTimesSine(c3, 6.0, latitude);
440  MM = cassM( lat, sin2lat, sin4lat, sin6lat );
441 
442  double easting = NN * (AA - (TT * A3 / 6.0) - (8.0 - TT + 8.0 * CC) *
443  (TT * A5 / 120.0)) + Cass_False_Easting;
444  double northing = MM - M0 + NN * tlat * ((A2 / 2.0) + (5.0 - TT +
445  6.0 * CC) * A4 / 24.0) + Cass_False_Northing;
446 
447  return new MapProjectionCoordinates(
448  CoordinateType::cassini, warning, easting, northing );
449 }
450 
451 
453  MSP::CCS::MapProjectionCoordinates* mapProjectionCoordinates )
454 {
455 /*
456  * The function convertToGeodetic converts Cassini projection
457  * (easting and northing) coordinates to geodetic (latitude and longitude)
458  * coordinates, according to the current ellipsoid and Cassini projection
459  * coordinates. If any errors occur, an exception is thrown with a description
460  * of the error.
461  *
462  * easting : Easting (X) in meters (input)
463  * northing : Northing (Y) in meters (input)
464  * longitude : Longitude (lambda) in radians (output)
465  * latitude : Latitude (phi) in radians (output)
466  */
467 
468  double dx; /* Delta easting - Difference in easting (easting-FE) */
469  double dy; /* Delta northing - Difference in northing (northing-FN) */
470  double mu1;
471  double sin2mu, sin4mu, sin6mu, sin8mu;
472  double M1;
473  double phi1;
474  double tanphi1, sinphi1, cosphi1;
475  double T1, T;
476  double N1;
477  double RD, R1;
478  double DD, D2, D3, D4, D5;
479  const double epsilon = 1.0e-1;
480  double longitude, latitude;
481 
482  double easting = mapProjectionCoordinates->easting();
483  double northing = mapProjectionCoordinates->northing();
484 
485  if ((easting < (Cass_False_Easting + Cass_Min_Easting))
486  || (easting > (Cass_False_Easting + Cass_Max_Easting)))
487  { /* Easting out of range */
489  }
490  if ((northing < (Cass_False_Northing + Cass_Min_Northing - epsilon))
491  || (northing > (Cass_False_Northing + Cass_Max_Northing + epsilon)))
492  { /* Northing out of range */
494  }
495 
496  dy = northing - Cass_False_Northing;
497  dx = easting - Cass_False_Easting;
498  M1 = M0 + dy;
499  mu1 = M1 / (semiMajorAxis * c0);
500 
501  sin2mu = cassCoeffTimesSine(a0, 2.0, mu1);
502  sin4mu = cassCoeffTimesSine(a1, 4.0, mu1);
503  sin6mu = cassCoeffTimesSine(a2, 6.0, mu1);
504  sin8mu = cassCoeffTimesSine(a3, 8.0, mu1);
505  phi1 = mu1 + sin2mu + sin4mu + sin6mu + sin8mu;
506 
507  if (floatEq(phi1,PI_OVER_2,.00001))
508  {
509  latitude = PI_OVER_2;
510  longitude = Cass_Origin_Long;
511  }
512  else if (floatEq(phi1,-PI_OVER_2,.00001))
513  {
514  latitude = -PI_OVER_2;
515  longitude = Cass_Origin_Long;
516  }
517  else
518  {
519  tanphi1 = tan(phi1);
520  sinphi1 = sin(phi1);
521  cosphi1 = cos(phi1);
522  T1 = tanphi1 * tanphi1;
523  RD = cassRd( sinphi1 );
524  N1 = semiMajorAxis / RD;
525  R1 = N1 * One_Minus_es2 / (RD * RD);
526  DD = dx / N1;
527  D2 = DD * DD;
528  D3 = D2 * DD;
529  D4 = D3 * DD;
530  D5 = D4 * DD;
531  T = (1.0 + 3.0 * T1);
532  latitude = phi1 - (N1 * tanphi1 / R1) * (D2 / 2.0 - T * D4 / 24.0);
533 
534  longitude = Cass_Origin_Long + (DD - T1 * D3 / 3.0 + T * T1 * D5 / 15.0) / cosphi1;
535 
536  if (latitude > PI_OVER_2) /* force distorted values to 90, -90 degrees */
537  latitude = PI_OVER_2;
538  else if (latitude < -PI_OVER_2)
539  latitude = -PI_OVER_2;
540 
541  if (longitude > PI)
542  longitude -= TWO_PI;
543  if (longitude < -PI)
544  longitude += TWO_PI;
545 
546  if (longitude > PI) /* force distorted values to 180, -180 degrees */
547  longitude = PI;
548  else if (longitude < -PI)
549  longitude = -PI;
550  }
551 
552  char warning[100];
553  warning[0] = '\0';
554  if (fabs(longitude - Cass_Origin_Long) > (4.0 * PI / 180))
555  { /* Distortion results if Longitude is > 4 degr from the Central Meridian */
556  strcat( warning, MSP::CCS::WarningMessages::longitude );
557  }
558 
559  return new GeodeticCoordinates(
560  CoordinateType::geodetic, warning, longitude, latitude );
561 }
562 
563 
564 double Cassini::cassM(
565  double c0lat, double c1s2lat, double c2s4lat, double c3s6lat )
566 {
567  return semiMajorAxis*(c0lat-c1s2lat+c2s4lat-c3s6lat);
568 }
569 
570 
571 double Cassini::cassRd( double sinlat )
572 {
573  return sqrt(1.0 - es2 * (sinlat * sinlat));
574 }
575 
576 
577 double Cassini::floatEq( double x, double v, double epsilon )
578 {
579  return ((v - epsilon) < x) && (x < (v + epsilon));
580 }
581 
582 
583 
584 // CLASSIFICATION: UNCLASSIFIED