UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
MGRS.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: MGRS
5  *
6  * ABSTRACT
7  *
8  * This component converts between geodetic coordinates (latitude and
9  * longitude) and Military Grid Reference System (MGRS) coordinates.
10  *
11  * ERROR HANDLING
12  *
13  * This component checks parameters for valid values. If an invalid value
14  * is found, the error code is combined with the current error code using
15  * the bitwise or. This combining allows multiple error codes to be
16  * returned. The possible error codes are:
17  *
18  * MGRS_NO_ERROR : No errors occurred in function
19  * MGRS_LAT_ERROR : Latitude outside of valid range
20  * (-90 to 90 degrees)
21  * MGRS_LON_ERROR : Longitude outside of valid range
22  * (-180 to 360 degrees)
23  * MGRS_STR_ERROR : An MGRS string error: string too long,
24  * too short, or badly formed
25  * MGRS_PRECISION_ERROR : The precision must be between 0 and 5
26  * inclusive.
27  * MGRS_A_ERROR : Semi-major axis less than or equal to zero
28  * MGRS_INV_F_ERROR : Inverse flattening outside of valid range
29  * (250 to 350)
30  * MGRS_EASTING_ERROR : Easting outside of valid range
31  * (100,000 to 900,000 meters for UTM)
32  * (0 to 4,000,000 meters for UPS)
33  * MGRS_NORTHING_ERROR : Northing outside of valid range
34  * (0 to 10,000,000 meters for UTM)
35  * (0 to 4,000,000 meters for UPS)
36  * MGRS_ZONE_ERROR : Zone outside of valid range (1 to 60)
37  * MGRS_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S')
38  *
39  * REUSE NOTES
40  *
41  * MGRS is intended for reuse by any application that does conversions
42  * between geodetic coordinates and MGRS coordinates.
43  *
44  * REFERENCES
45  *
46  * Further information on MGRS can be found in the Reuse Manual.
47  *
48  * MGRS originated from : U.S. Army Topographic Engineering Center
49  * Geospatial Information Division
50  * 7701 Telegraph Road
51  * Alexandria, VA 22310-3864
52  *
53  * LICENSES
54  *
55  * None apply to this component.
56  *
57  * RESTRICTIONS
58  *
59  *
60  * ENVIRONMENT
61  *
62  * MGRS was tested and certified in the following environments:
63  *
64  * 1. Solaris 2.5 with GCC version 2.8.1
65  * 2. Windows 95 with MS Visual C++ version 6
66  *
67  * MODIFICATIONS
68  *
69  * Date Description
70  * ---- -----------
71  * 2-27-07 Original Code
72  * 3/23/2011 NGL BAEts28583 Updated for memory leak in method toUTM,
73  * if MGRS input is not within zone letter bounds.
74  * 9/16/2013 KNL MSP_29918 Validate MGRS string, display errors if
75  * special character is found in input string.
76  * 1/18/2016 KC BAE_MSP00030349/BAE_MSP00030211 Fix the setting of
77  * the latitude band in MGRS.
78  * 1/19/2016 A. Layne MSP_DR30125 Updated to pass ellipsoid code
79  * into call to UTM and UTM override.
80  */
81 
82 /***************************************************************************/
83 /*
84  * INCLUDES
85  */
86 
87 #include <ctype.h>
88 #include <math.h>
89 #include <string.h>
90 #include <stdio.h>
91 #include <string>
92 #include <sstream>
93 #include "UPS.h"
94 #include "UTM.h"
95 #include "MGRS.h"
96 #include "EllipsoidParameters.h"
97 #include "MGRSorUSNGCoordinates.h"
98 #include "GeodeticCoordinates.h"
99 #include "UPSCoordinates.h"
100 #include "UTMCoordinates.h"
102 #include "ErrorMessages.h"
103 #include "WarningMessages.h"
104 
105 using namespace std;
106 /*
107  * ctype.h - Standard C character handling library
108  * math.h - Standard C math library
109  * stdio.h - Standard C input/output library
110  * string.h - Standard C string handling library
111  * UPS.h - Universal Polar Stereographic (UPS) projection
112  * UTM.h - Universal Transverse Mercator (UTM) projection
113  * MGRS.h - function prototype error checking
114  * MGRSorUSNGCoordinates.h - defines mgrs coordinates
115  * GeodeticCoordinates.h - defines geodetic coordinates
116  * UPSCoordinates.h - defines ups coordinates
117  * UTMCoordinates.h - defines utm coordinates
118  * CoordinateConversionException.h - Exception handler
119  * ErrorMessages.h - Contains exception messages
120  * WarningMessages.h - Contains warning messages
121  */
122 
123 using namespace MSP::CCS;
124 
125 
126 /************************************************************************/
127 /* DEFINES
128  *
129  */
130 
131 #define EPSILON 1.75e-7 /* approx 1.0e-5 degrees (~1 meter) in radians */
132 
133 #define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */
134 #define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */
135 #define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */
136 #define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */
137 #define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */
138 #define LETTER_F 5 /* ARRAY INDEX FOR LETTER F */
139 #define LETTER_G 6 /* ARRAY INDEX FOR LETTER G */
140 #define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */
141 #define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */
142 #define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */
143 #define LETTER_K 10 /* ARRAY INDEX FOR LETTER K */
144 #define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */
145 #define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */
146 #define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */
147 #define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */
148 #define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */
149 #define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */
150 #define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */
151 #define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */
152 #define LETTER_T 19 /* ARRAY INDEX FOR LETTER T */
153 #define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */
154 #define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */
155 #define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */
156 #define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */
157 #define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */
158 #define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */
159 
160 #define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */
161 #define TWOMIL 2000000.e0 /* TWO MILLION */
162 #define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */
163 #define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */
164 #define PI 3.14159265358979323e0
165 #define PI_OVER_2 (PI / 2.0e0)
166 #define PI_OVER_180 (PI / 180.0e0)
167 
168 #define MIN_EASTING 100000.0
169 #define MAX_EASTING 900000.0
170 #define MIN_NORTHING 0.0
171 #define MAX_NORTHING 10000000.0
172 #define MAX_PRECISION 5 /* Maximum precision of easting & northing */
173 #define MIN_MGRS_NON_POLAR_LAT (-80.0 * ( PI / 180.0 )) /* -80 deg in rad */
174 #define MAX_MGRS_NON_POLAR_LAT ( 84.0 * ( PI / 180.0 )) /* 84 deg in rad */
175 
176 #define MIN_EAST_NORTH 0.0
177 #define MAX_EAST_NORTH 3999999.0
178 
179 #define _6 (6.0 * (PI / 180.0))
180 #define _8 (8.0 * (PI / 180.0))
181 #define _72 (72.0 * (PI / 180.0))
182 #define _80 (80.0 * (PI / 180.0))
183 #define _80_5 (80.5 * (PI / 180.0))
184 #define _84_5 (84.5 * (PI / 180.0))
185 
186 #define _500000 500000.0
187 
188 /*
189  * CLARKE_1866 : Ellipsoid code for CLARKE_1866
190  * CLARKE_1880 : Ellipsoid code for CLARKE_1880
191  * BESSEL_1841 : Ellipsoid code for BESSEL_1841
192  * BESSEL_1841_NAMIBIA : Ellipsoid code for BESSEL 1841 (NAMIBIA)
193  */
194 #define CLARKE_1866 "CC"
195 #define CLARKE_1880 "CD"
196 #define BESSEL_1841 "BR"
197 #define BESSEL_1841_NAMIBIA "BN"
198 
199 #define EPSILON2 4.99e-4
200 
202 {
203  long letter; /* letter representing latitude band */
204  double min_northing; /* minimum northing for latitude band */
205  double north; /* upper latitude for latitude band */
206  double south; /* lower latitude for latitude band */
207  double northing_offset; /* latitude band northing offset */
208 };
209 
211  {LETTER_C, 1100000.0, -72.0, -80.5, 0.0},
212  {LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0},
213  {LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0},
214  {LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0},
215  {LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0},
216  {LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0},
217  {LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0},
218  {LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0},
219  {LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0},
220  {LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0},
221  {LETTER_N, 0.0, 8.0, 0.0, 0.0},
222  {LETTER_P, 800000.0, 16.0, 8.0, 0.0},
223  {LETTER_Q, 1700000.0, 24.0, 16.0, 0.0},
224  {LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0},
225  {LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0},
226  {LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0},
227  {LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0},
228  {LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0},
229  {LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0},
230  {LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}};
231 
232 
234 {
235  long letter; /* letter representing latitude band */
236  long ltr2_low_value; /* 2nd letter range - low number */
237  long ltr2_high_value; /* 2nd letter range - high number */
238  long ltr3_high_value; /* 3rd letter range - high number (UPS) */
239  double false_easting; /* False easting based on 2nd letter */
240  double false_northing; /* False northing based on 3rd letter */
241 };
242 
244  {{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0},
245  {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0},
246  {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0},
247  {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}};
248 
249 
250 /************************************************************************/
251 /* LOCAL FUNCTIONS
252  *
253  */
254 
255 
256 double computeScale( int prec )
257 {
258  double scale = 1.0e5;
259  switch( prec )
260  {
261  case 0:
262  scale = 1.0e5;
263  break;
264 
265  case 1:
266  scale = 1.0e4;
267  break;
268 
269  case 2:
270  scale = 1.0e3;
271  break;
272 
273  case 3:
274  scale = 1.0e2;
275  break;
276 
277  case 4:
278  scale = 1.0e1;
279  break;
280 
281  case 5:
282  scale = 1.0e0;
283  break;
284 
285  default:
286  break;
287  }
288  return scale;
289 }
290 
292  char* MGRSString,
293  long zone,
294  int letters[MGRS_LETTERS],
295  double easting,
296  double northing,
297  long precision )
298 {
299 /*
300  * The function makeMGRSString constructs an MGRS string
301  * from its component parts.
302  *
303  * MGRSString : MGRS coordinate string (output)
304  * zone : UTM Zone (input)
305  * letters : MGRS coordinate string letters (input)
306  * easting : Easting value (input)
307  * northing : Northing value (input)
308  * precision : Precision level of MGRS string (input)
309  */
310 
311  long i;
312  long j;
313  double divisor;
314  long east;
315  long north;
316  char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
317 
318  i = 0;
319  if (zone)
320  i = sprintf (MGRSString+i,"%2.2ld",zone);
321  else
322  strncpy(MGRSString, " ", 2); // 2 spaces
323 
324  for (j=0;j<3;j++)
325  MGRSString[i++] = alphabet[letters[j]];
326 
327  divisor = computeScale( precision );
328 
329  easting = fmod (easting, 100000.0);
330  if (easting >= 99999.5)
331  easting = 99999.0;
332  east = (long)((easting+EPSILON2) /divisor);
333  i += sprintf (MGRSString+i, "%*.*ld", precision, precision, east);
334  northing = fmod (northing, 100000.0);
335  if (northing >= 99999.5)
336  northing = 99999.0;
337  north = (long)((northing+EPSILON2) /divisor);
338  i += sprintf (MGRSString+i, "%*.*ld", precision, precision, north);
339 }
340 
341 
343  char* MGRSString,
344  long* zone,
345  long letters[MGRS_LETTERS],
346  double* easting,
347  double* northing,
348  long* precision )
349 {
350 /*
351  * The function breakMGRSString breaks down an MGRS
352  * coordinate string into its component parts.
353  *
354  * MGRS : MGRS coordinate string (input)
355  * zone : UTM Zone (output)
356  * letters : MGRS coordinate string letters (output)
357  * easting : Easting value (output)
358  * northing : Northing value (output)
359  * precision : Precision level of MGRS string (output)
360  */
361 
362  long num_digits;
363  long num_letters;
364  long i = 0;
365  long j = 0;
366 
367  // remove any spaces from MGRS string
368  char tempMGRSString[100+1];
369  while( MGRSString[i] != '\0' || j == 100 )
370  {
371  if( MGRSString[i] != ' ' )
372  {
373  // check for invalid character
374  if (!isdigit(MGRSString[i]) && !isalpha(MGRSString[i]) )
375  throw CoordinateConversionException( ErrorMessages::mgrsString );
376  tempMGRSString[j] = MGRSString[i];
377  j++;
378  }
379  i++;
380  }
381  tempMGRSString[j] = '\0';
382 
383  i = 0;
384  j = 0;
385 
386  while (tempMGRSString[i] == ' ')
387  i++; /* skip any leading blanks */
388  j = i;
389  while (isdigit(tempMGRSString[i]))
390  i++;
391  num_digits = i - j;
392  if (num_digits <= 2)
393  if (num_digits > 0)
394  {
395  char zone_string[3];
396  /* get zone */
397  strncpy (zone_string, tempMGRSString+j, 2);
398  zone_string[2] = 0;
399  sscanf (zone_string, "%ld", zone);
400  if ((*zone < 1) || (*zone > 60))
401  throw CoordinateConversionException( ErrorMessages::mgrsString );
402  }
403  else
404  *zone = 0;
405  else
406  throw CoordinateConversionException( ErrorMessages::mgrsString );
407  j = i;
408 
409  while (isalpha(tempMGRSString[i]))
410  i++;
411  num_letters = i - j;
412  if (num_letters == 3)
413  {
414  /* get letters */
415  letters[0] = (toupper(tempMGRSString[j]) - (long)'A');
416  if ((letters[0] == LETTER_I) || (letters[0] == LETTER_O))
417  throw CoordinateConversionException( ErrorMessages::mgrsString );
418  letters[1] = (toupper(tempMGRSString[j+1]) - (long)'A');
419  if ((letters[1] == LETTER_I) || (letters[1] == LETTER_O))
420  throw CoordinateConversionException( ErrorMessages::mgrsString );
421  letters[2] = (toupper(tempMGRSString[j+2]) - (long)'A');
422  if ((letters[2] == LETTER_I) || (letters[2] == LETTER_O))
423  throw CoordinateConversionException( ErrorMessages::mgrsString );
424  }
425  else
426  throw CoordinateConversionException( ErrorMessages::mgrsString );
427  j = i;
428  while (isdigit(tempMGRSString[i]))
429  i++;
430  num_digits = i - j;
431  if ((num_digits <= 10) && (num_digits%2 == 0))
432  {
433  long n;
434  char east_string[6];
435  char north_string[6];
436  long east;
437  long north;
438  double multiplier;
439  double halfMulti;
440 
441  /* get easting & northing */
442  n = num_digits/2;
443  *precision = n;
444  if (n > 0)
445  {
446  strncpy (east_string, tempMGRSString+j, n);
447  east_string[n] = 0;
448  sscanf (east_string, "%ld", &east);
449  strncpy (north_string, tempMGRSString+j+n, n);
450  north_string[n] = 0;
451  sscanf (north_string, "%ld", &north);
452  multiplier = computeScale( n );
453  halfMulti = multiplier * 0.5;
454 
455  *easting = east * multiplier + halfMulti;
456  *northing = north * multiplier + halfMulti;
457  }
458  else
459  {
460  *easting = 0.0;
461  *northing = 0.0;
462  }
463  }
464  else
465  throw CoordinateConversionException( ErrorMessages::mgrsString );
466 
467 }
468 
469 
470 /************************************************************************/
471 /* FUNCTIONS
472  *
473  */
474 
475 MGRS::MGRS(
476  double ellipsoidSemiMajorAxis,
477  double ellipsoidFlattening,
478  char* ellipsoidCode ) :
480  ups( 0 ),
481  utm( 0 )
482 {
483 /*
484  * The constructor receives the ellipsoid parameters and sets
485  * the corresponding state variables. If any errors occur, an
486  * exception is thrown with a description of the error.
487  *
488  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid in meters (input)
489  * ellipsoidFlattening : Flattening of ellipsoid (input)
490  * ellipsoid_Code : 2-letter code for ellipsoid (input)
491  */
492 
493  double inv_f = 1 / ellipsoidFlattening;
494 
495  if (ellipsoidSemiMajorAxis <= 0.0)
496  { /* Semi-major axis must be greater than zero */
498  }
499  if ((inv_f < 250) || (inv_f > 350))
500  { /* Inverse flattening must be between 250 and 350 */
502  }
503 
504  semiMajorAxis = ellipsoidSemiMajorAxis;
505  flattening = ellipsoidFlattening;
506 
507  strncpy (MGRSEllipsoidCode, ellipsoidCode, 2);
508  MGRSEllipsoidCode[2] = '\0';
509 
510  ups = new UPS( semiMajorAxis, flattening );
511 
512  utm = new UTM( semiMajorAxis, flattening, MGRSEllipsoidCode, 0 );
513 }
514 
515 
516 MGRS::MGRS( const MGRS &m )
517 {
518  ups = new UPS( *( m.ups ) );
519  utm = new UTM( *( m.utm ) );
520 
523  strcpy( MGRSEllipsoidCode, m.MGRSEllipsoidCode );
524 }
525 
526 
528 {
529  delete ups;
530  ups = 0;
531 
532  delete utm;
533  utm = 0;
534 }
535 
536 
538 {
539  if( this != &m )
540  {
541  ups->operator=( *m.ups );
542  utm->operator=( *m.utm );
543 
546  strcpy( MGRSEllipsoidCode, m.MGRSEllipsoidCode );
547  }
548 
549  return *this;
550 }
551 
552 
554 {
555 /*
556  * The function getParameters returns the current ellipsoid
557  * parameters.
558  *
559  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (output)
560  * ellipsoidFlattening : Flattening of ellipsoid (output)
561  * ellipsoidCode : 2-letter code for ellipsoid (output)
562  */
563 
564  return new EllipsoidParameters(
565  semiMajorAxis, flattening, (char*)MGRSEllipsoidCode );
566 }
567 
568 
570  MSP::CCS::GeodeticCoordinates* geodeticCoordinates,
571  long precision )
572 {
573 /*
574  * The function convertFromGeodetic converts Geodetic (latitude and
575  * longitude) coordinates to an MGRS coordinate string, according to the
576  * current ellipsoid parameters. If any errors occur, an exception
577  * is thrown with a description of the error.
578  *
579  * latitude : Latitude in radians (input)
580  * longitude : Longitude in radians (input)
581  * precision : Precision level of MGRS string (input)
582  * MGRSString : MGRS coordinate string (output)
583  *
584  */
585 
586  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
587  UTMCoordinates* utmCoordinates = 0;
588  UPSCoordinates* upsCoordinates = 0;
589 
590  double latitude = geodeticCoordinates->latitude();
591  double longitude = geodeticCoordinates->longitude();
592 
593  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
594  { /* latitude out of range */
596  }
597  if ((longitude < (-PI - EPSILON)) || (longitude > (2*PI + EPSILON)))
598  { /* longitude out of range */
600  }
601  if ((precision < 0) || (precision > MAX_PRECISION))
603 
604  try
605  {
606  // If the latitude is within the valid mgrs non polar range [-80, 84),
607  // convert to mgrs using the utm path,
608  // otherwise convert to mgrs using the ups path
609  if((latitude >= MIN_MGRS_NON_POLAR_LAT - EPSILON) &&
610  (latitude < MAX_MGRS_NON_POLAR_LAT + EPSILON))
611  {
612  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
613  mgrsorUSNGCoordinates = fromUTM(
614  utmCoordinates, longitude, latitude, precision );
615  delete utmCoordinates;
616  utmCoordinates = 0;
617  }
618  else
619  {
620  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
621  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
622  delete upsCoordinates;
623  upsCoordinates = 0;
624  }
625  }
626  catch ( CoordinateConversionException e) {
627  delete utmCoordinates;
628  delete upsCoordinates;
629  throw e;
630  }
631 
632  return mgrsorUSNGCoordinates;
633 }
634 
635 
637  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
638 {
639 /*
640  * The function convertToGeodetic converts an MGRS coordinate string
641  * to Geodetic (latitude and longitude) coordinates
642  * according to the current ellipsoid parameters. If any errors occur,
643  * an exception is thrown with a description of the error.
644  *
645  * MGRS : MGRS coordinate string (input)
646  * latitude : Latitude in radians (output)
647  * longitude : Longitude in radians (output)
648  *
649  */
650 
651  long zone;
652  long letters[MGRS_LETTERS];
653  double mgrs_easting;
654  double mgrs_northing;
655  long precision;
656  GeodeticCoordinates* geodeticCoordinates = 0;
657  UTMCoordinates* utmCoordinates = 0;
658  UPSCoordinates* upsCoordinates = 0;
659 
661  mgrsorUSNGCoordinates->MGRSString(), &zone, letters,
662  &mgrs_easting, &mgrs_northing, &precision );
663 
664  try
665  {
666  if( zone )
667  {
668  utmCoordinates = toUTM(
669  zone, letters, mgrs_easting, mgrs_northing, precision );
670  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
671  if( strlen( utmCoordinates->warningMessage() ) > 0 )
672  {
673  geodeticCoordinates->setWarningMessage(
674  utmCoordinates->warningMessage() );
675  }
676  delete utmCoordinates;
677  utmCoordinates = 0;
678  }
679  else
680  {
681  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
682  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
683  delete upsCoordinates;
684  upsCoordinates = 0;
685  }
686  }
688  {
689  delete utmCoordinates;
690  delete upsCoordinates;
691  throw e;
692  }
693 
694  return geodeticCoordinates;
695 }
696 
697 
699  UTMCoordinates* utmCoordinates,
700  long precision )
701 {
702 /*
703  * The function convertFromUTM converts UTM (zone, easting, and
704  * northing) coordinates to an MGRS coordinate string, according to the
705  * current ellipsoid parameters. If any errors occur, an exception is
706  * thrown with a description of the error.
707  *
708  * zone : UTM zone (input)
709  * hemisphere : North or South hemisphere (input)
710  * easting : Easting (X) in meters (input)
711  * northing : Northing (Y) in meters (input)
712  * precision : Precision level of MGRS string (input)
713  * MGRSString : MGRS coordinate string (output)
714  */
715 
716  long zone = utmCoordinates->zone();
717  char hemisphere = utmCoordinates->hemisphere();
718  double easting = utmCoordinates->easting();
719  double northing = utmCoordinates->northing();
720 
721  GeodeticCoordinates* geodeticCoordinates = 0;
722  UPSCoordinates* upsCoordinates = 0;
723  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
724 
725  if ((zone < 1) || (zone > 60))
727  if ((hemisphere != 'S') && (hemisphere != 'N'))
729  if ((easting < MIN_EASTING) || (easting > MAX_EASTING))
731  if ((northing < MIN_NORTHING) || (northing > MAX_NORTHING))
733  if ((precision < 0) || (precision > MAX_PRECISION))
735 
736  try
737  {
738  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
739 
740  // If the latitude is within the valid mgrs non polar range [-80, 84),
741  // convert to mgrs using the utm path,
742  // otherwise convert to mgrs using the ups path
743  double latitude = geodeticCoordinates->latitude();
744 
745  if((latitude >= (MIN_MGRS_NON_POLAR_LAT - EPSILON)) &&
746  (latitude < (MAX_MGRS_NON_POLAR_LAT + EPSILON)))
747  mgrsorUSNGCoordinates = fromUTM(
748  utmCoordinates,
749  geodeticCoordinates->longitude(), latitude, precision);
750  else
751  {
752  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
753  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
754  }
755  }
757  {
758  delete upsCoordinates;
759  delete geodeticCoordinates;
760  throw e;
761  }
762 
763  delete upsCoordinates;
764  delete geodeticCoordinates;
765 
766  return mgrsorUSNGCoordinates;
767 }
768 
769 
771  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
772 {
773 /*
774  * The function convertToUTM converts an MGRS coordinate string
775  * to UTM projection (zone, hemisphere, easting and northing) coordinates
776  * according to the current ellipsoid parameters. If any errors occur,
777  * an exception is thrown with a description of the error.
778  *
779  * MGRSString : MGRS coordinate string (input)
780  * zone : UTM zone (output)
781  * hemisphere : North or South hemisphere (output)
782  * easting : Easting (X) in meters (output)
783  * northing : Northing (Y) in meters (output)
784  */
785 
786  long zone;
787  long letters[MGRS_LETTERS];
788  double mgrs_easting, mgrs_northing;
789  long precision;
790  UTMCoordinates* utmCoordinates = 0;
791  GeodeticCoordinates* geodeticCoordinates = 0;
792  UPSCoordinates* upsCoordinates = 0;
793 
794  try
795  {
797  mgrsorUSNGCoordinates->MGRSString(), &zone, letters,
798  &mgrs_easting, &mgrs_northing, &precision );
799  if (zone)
800  {
801  utmCoordinates = toUTM(
802  zone, letters, mgrs_easting, mgrs_northing, precision );
803  // Convert to geodetic to make sure that
804  // the coordinates are in the valid utm range
805  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
806  }
807  else
808  {
809  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
810  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
811  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
812  }
813  }
815  {
816  delete utmCoordinates;
817  delete upsCoordinates;
818  delete geodeticCoordinates;
819  throw e;
820  }
821 
822  delete upsCoordinates;
823  delete geodeticCoordinates;
824 
825  return utmCoordinates;
826 }
827 
828 
830  MSP::CCS::UPSCoordinates* upsCoordinates,
831  long precision )
832 {
833 /*
834  * The function convertFromUPS converts UPS (hemisphere, easting,
835  * and northing) coordinates to an MGRS coordinate string according to
836  * the current ellipsoid parameters. If any errors occur, an
837  * exception is thrown with a description of the error.
838  *
839  * hemisphere : Hemisphere either 'N' or 'S' (input)
840  * easting : Easting/X in meters (input)
841  * northing : Northing/Y in meters (input)
842  * precision : Precision level of MGRS string (input)
843  * MGRSString : MGRS coordinate string (output)
844  */
845 
846  int index = 0;
847 
848  char hemisphere = upsCoordinates->hemisphere();
849  double easting = upsCoordinates->easting();
850  double northing = upsCoordinates->northing();
851 
852  UTMCoordinates* utmCoordinates = 0;
853  GeodeticCoordinates* geodeticCoordinates = 0;
854  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
855 
856  if ((hemisphere != 'N') && (hemisphere != 'S'))
858  if ((easting < MIN_EAST_NORTH) || (easting > MAX_EAST_NORTH))
860  if ((northing < MIN_EAST_NORTH) || (northing > MAX_EAST_NORTH))
862  if ((precision < 0) || (precision > MAX_PRECISION))
864 
865  try
866  {
867  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
868 
869  // If the latitude is within the valid mgrs polar range [-90, -80) or
870  // [84, 90], convert to mgrs using the ups path,
871  // otherwise convert to mgrs using the utm path
872  double latitude = geodeticCoordinates->latitude();
873 
874  if((latitude < (MIN_MGRS_NON_POLAR_LAT - EPSILON)) ||
875  (latitude >= (MAX_MGRS_NON_POLAR_LAT + EPSILON)))
876  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
877  else
878  {
879  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
880  double longitude = geodeticCoordinates->longitude();
881  mgrsorUSNGCoordinates = fromUTM(
882  utmCoordinates, longitude, latitude, precision );
883  }
884  }
886  {
887  delete utmCoordinates;
888  delete geodeticCoordinates;
889  throw e;
890  }
891 
892  delete utmCoordinates;
893  delete geodeticCoordinates;
894 
895  return mgrsorUSNGCoordinates;
896 }
897 
898 
900  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
901 {
902 /*
903  * The function convertToUPS converts an MGRS coordinate string
904  * to UPS (hemisphere, easting, and northing) coordinates, according
905  * to the current ellipsoid parameters. If any errors occur, an
906  * exception is thrown with a description of the error.
907  *
908  * MGRSString : MGRS coordinate string (input)
909  * hemisphere : Hemisphere either 'N' or 'S' (output)
910  * easting : Easting/X in meters (output)
911  * northing : Northing/Y in meters (output)
912  */
913 
914  long zone;
915  long letters[MGRS_LETTERS];
916  long precision;
917  double mgrs_easting;
918  double mgrs_northing;
919  int index = 0;
920  UPSCoordinates* upsCoordinates = 0;
921  GeodeticCoordinates* geodeticCoordinates = 0;
922  UTMCoordinates* utmCoordinates = 0;
923 
924  try
925  {
927  mgrsorUSNGCoordinates->MGRSString(),
928  &zone, letters, &mgrs_easting, &mgrs_northing, &precision );
929 
930  if( !zone )
931  {
932  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
933  // Convert to geodetic to make sure that
934  // the coordinates are in the valid ups range
935  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
936  }
937  else
938  {
939  utmCoordinates = toUTM(
940  zone, letters, mgrs_easting, mgrs_northing, precision );
941  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
942  if( strlen( utmCoordinates->warningMessage() ) > 0 )
943  {
944  geodeticCoordinates->setWarningMessage(
945  utmCoordinates->warningMessage() );
946  }
947  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
948  }
949  }
951  {
952  delete utmCoordinates;
953  delete upsCoordinates;
954  delete geodeticCoordinates;
955  throw e;
956  }
957 
958  delete utmCoordinates;
959  delete geodeticCoordinates;
960 
961  return upsCoordinates;
962 }
963 
964 
965 MSP::CCS::MGRSorUSNGCoordinates* MGRS::fromUTM(
966  MSP::CCS::UTMCoordinates* utmCoordinates,
967  double longitude,
968  double latitude,
969  long precision )
970 {
971 /*
972  * The function fromUTM calculates an MGRS coordinate string
973  * based on the zone, latitude, easting and northing.
974  *
975  * zone : Zone number (input)
976  * hemisphere : Hemisphere (input)
977  * longitude : Longitude in radians (input)
978  * latitude : Latitude in radians (input)
979  * easting : Easting (input)
980  * northing : Northing (input)
981  * precision : Precision (input)
982  * MGRSString : MGRS coordinate string (output)
983  */
984 
985  double pattern_offset; /* Pattern offset for 3rd letter */
986  double grid_northing; /* Northing used to derive 3rd letter of MGRS */
987  long ltr2_low_value; /* 2nd letter range - low number */
988  long ltr2_high_value; /* 2nd letter range - high number */
989  int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
990  char MGRSString[21];
991  long override = 0;
992  long natural_zone;
993 
994  long zone = utmCoordinates->zone();
995  char hemisphere = utmCoordinates->hemisphere();
996  double easting = utmCoordinates->easting();
997  double northing = utmCoordinates->northing();
998 
999  getLatitudeLetter( latitude, &letters[0] );
1000 
1001  // Check if the point is within it's natural zone
1002  // If it is not, put it there
1003  double pad = EPSILON2 / 6378137.0;
1004  if (longitude < PI)
1005  {
1006  natural_zone = (long)(31 + ((longitude+pad) / _6));
1007  }
1008  else
1009  {
1010  natural_zone = (long)(((longitude+pad) / _6) - 29);
1011  }
1012 
1013  if (natural_zone > 60)
1014  natural_zone = 1;
1015  if (zone != natural_zone)
1016  { // reconvert to override zone
1017  UTM utmOverride( semiMajorAxis, flattening, MGRSEllipsoidCode, natural_zone );
1018  GeodeticCoordinates geodeticCoordinates(
1019  CoordinateType::geodetic, longitude, latitude );
1020  UTMCoordinates* utmCoordinatesOverride =
1021  utmOverride.convertFromGeodetic( &geodeticCoordinates );
1022 
1023  zone = utmCoordinatesOverride->zone();
1024  hemisphere = utmCoordinatesOverride->hemisphere();
1025  easting = utmCoordinatesOverride->easting();
1026  northing = utmCoordinatesOverride->northing();
1027 
1028  delete utmCoordinatesOverride;
1029  utmCoordinatesOverride = 0;
1030  }
1031 
1032  /* UTM special cases */
1033  if (letters[0] == LETTER_V) // V latitude band
1034  {
1035  if ((zone == 31) && (easting >= _500000))
1036  override = 32; // extension of zone 32V
1037  }
1038  else if (letters[0] == LETTER_X)
1039  {
1040  if ((zone == 32) && (easting < _500000)) // extension of zone 31X
1041  override = 31;
1042  else if (((zone == 32) && (easting >= _500000)) || // western extension of zone 33X
1043  ((zone == 34) && (easting < _500000))) // eastern extension of zone 33X
1044  override = 33;
1045  else if (((zone == 34) && (easting >= _500000)) || // western extension of zone 35X
1046  ((zone == 36) && (easting < _500000))) // eastern extension of zone 35X
1047  override = 35;
1048  else if ((zone == 36) && (easting >= _500000)) // western extension of zone 37X
1049  override = 37;
1050  }
1051 
1052  if (override)
1053  { // reconvert to override zone
1054  UTM utmOverride( semiMajorAxis, flattening, MGRSEllipsoidCode, override );
1055  GeodeticCoordinates geodeticCoordinates(
1056  CoordinateType::geodetic, longitude, latitude );
1057  UTMCoordinates* utmCoordinatesOverride =
1058  utmOverride.convertFromGeodetic( &geodeticCoordinates );
1059 
1060  zone = utmCoordinatesOverride->zone();
1061  hemisphere = utmCoordinatesOverride->hemisphere();
1062  easting = utmCoordinatesOverride->easting();
1063  northing = utmCoordinatesOverride->northing();
1064 
1065  delete utmCoordinatesOverride;
1066  utmCoordinatesOverride = 0;
1067  }
1068 
1069  double divisor = computeScale( precision );
1070 
1071  easting = ( long )( (easting +EPSILON2) /divisor ) * divisor;
1072  northing = ( long )( (northing+EPSILON2) /divisor ) * divisor;
1073 
1074  if( latitude <= 0.0 && northing == 1.0e7 )
1075  {
1076  latitude = 0.0;
1077  northing = 0.0;
1078  }
1079 
1080  getGridValues( zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset );
1081 
1082  grid_northing = northing;
1083 
1084  while (grid_northing >= TWOMIL)
1085  {
1086  grid_northing = grid_northing - TWOMIL;
1087  }
1088  grid_northing = grid_northing + pattern_offset;
1089  if(grid_northing >= TWOMIL)
1090  grid_northing = grid_northing - TWOMIL;
1091 
1092  letters[2] = (long)(grid_northing / ONEHT);
1093  if (letters[2] > LETTER_H)
1094  letters[2] = letters[2] + 1;
1095 
1096  if (letters[2] > LETTER_N)
1097  letters[2] = letters[2] + 1;
1098 
1099  letters[1] = ltr2_low_value + ((long)(easting / ONEHT) - 1);
1100  if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
1101  letters[1] = letters[1] + 1;
1102 
1103  makeMGRSString( MGRSString, zone, letters, easting, northing, precision );
1104 
1105  return new MGRSorUSNGCoordinates(
1107  MSP::CCS::Precision::toPrecision(precision) );
1108 }
1109 
1110 
1111 MSP::CCS::UTMCoordinates* MGRS::toUTM(
1112  long zone,
1113  long letters[MGRS_LETTERS],
1114  double easting,
1115  double northing,
1116  long precision )
1117 {
1118 /*
1119  * The function toUTM converts an MGRS coordinate string
1120  * to UTM projection (zone, hemisphere, easting and northing) coordinates
1121  * according to the current ellipsoid parameters. If any errors occur,
1122  * an exception is thrown with a description of the error.
1123  *
1124  * MGRSString : MGRS coordinate string (input)
1125  * zone : UTM zone (output)
1126  * hemisphere : North or South hemisphere (output)
1127  * easting : Easting (X) in meters (output)
1128  * northing : Northing (Y) in meters (output)
1129  */
1130 
1131  char hemisphere;
1132  double min_northing;
1133  double northing_offset;
1134  long ltr2_low_value;
1135  long ltr2_high_value;
1136  double pattern_offset;
1137  double grid_easting; /* Easting for 100,000 meter grid square */
1138  double grid_northing; /* Northing for 100,000 meter grid square */
1139  double temp_grid_northing = 0.0;
1140  double fabs_grid_northing = 0.0;
1141  double latitude = 0.0;
1142  double longitude = 0.0;
1143  double divisor = 1.0;
1144  UTMCoordinates* utmCoordinates = 0;
1145 
1146  if((letters[0] == LETTER_X) && ((zone == 32) || (zone == 34) || (zone == 36)))
1148  else if ((letters[0] == LETTER_V) && (zone == 31) && (letters[1] > LETTER_D))
1150  else
1151  {
1152  if (letters[0] < LETTER_N)
1153  hemisphere = 'S';
1154  else
1155  hemisphere = 'N';
1156 
1157  getGridValues(zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
1158 
1159  /* Check that the second letter of the MGRS string is within
1160  * the range of valid second letter values
1161  * Also check that the third letter is valid */
1162  if((letters[1] < ltr2_low_value) ||
1163  (letters[1] > ltr2_high_value) ||
1164  (letters[2] > LETTER_V) )
1166 
1167  grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
1168  if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
1169  grid_easting = grid_easting - ONEHT;
1170 
1171  double row_letter_northing = (double)(letters[2]) * ONEHT;
1172  if (letters[2] > LETTER_O)
1173  row_letter_northing = row_letter_northing - ONEHT;
1174 
1175  if (letters[2] > LETTER_I)
1176  row_letter_northing = row_letter_northing - ONEHT;
1177 
1178  if (row_letter_northing >= TWOMIL)
1179  row_letter_northing = row_letter_northing - TWOMIL;
1180 
1181  getLatitudeBandMinNorthing(letters[0], &min_northing, &northing_offset);
1182 
1183  grid_northing = row_letter_northing - pattern_offset;
1184  if(grid_northing < 0)
1185  grid_northing += TWOMIL;
1186 
1187  grid_northing += northing_offset;
1188 
1189  if(grid_northing < min_northing)
1190  grid_northing += TWOMIL;
1191 
1192  easting = grid_easting + easting;
1193  northing = grid_northing + northing;
1194 
1195  utmCoordinates = new UTMCoordinates(
1197  zone, hemisphere, easting, northing );
1198 
1199  /* check that point is within Zone Letter bounds */
1200  GeodeticCoordinates* geodeticCoordinates;
1201  try
1202  {
1203  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
1204  }
1206  {
1207  delete utmCoordinates;
1208  throw e;
1209  }
1210 
1211  double latitude = geodeticCoordinates->latitude();
1212 
1213  delete geodeticCoordinates;
1214  geodeticCoordinates = 0;
1215 
1216  divisor = ONEHT / computeScale( precision );
1217 
1218  if( ! inLatitudeRange(letters[0], latitude, PI_OVER_180/divisor) )
1219  {
1220  // check adjacent bands
1221  long prevBand = letters[0] - 1;
1222  long nextBand = letters[0] + 1;
1223 
1224  if( letters[0] == LETTER_C ) // if last band, do not go off list
1225  prevBand = letters[0];
1226 
1227  if( letters[0] == LETTER_X )
1228  nextBand = letters[0];
1229 
1230  if( prevBand == LETTER_I || prevBand == LETTER_O )
1231  prevBand--;
1232 
1233  if( nextBand == LETTER_I || nextBand == LETTER_O )
1234  nextBand++;
1235 
1236  if(inLatitudeRange( prevBand, latitude, PI_OVER_180/divisor ) ||
1237  inLatitudeRange( nextBand, latitude, PI_OVER_180/divisor ) )
1238  {
1239  utmCoordinates->setWarningMessage(
1241  }
1242  else
1243  {
1245  }
1246  }
1247  }
1248 
1249  return utmCoordinates;
1250 }
1251 
1252 
1253 MSP::CCS::MGRSorUSNGCoordinates* MGRS::fromUPS(
1254  MSP::CCS::UPSCoordinates* upsCoordinates,
1255  long precision )
1256 {
1257 /*
1258  * The function fromUPS converts UPS (hemisphere, easting,
1259  * and northing) coordinates to an MGRS coordinate string according to
1260  * the current ellipsoid parameters.
1261  *
1262  * hemisphere : Hemisphere either 'N' or 'S' (input)
1263  * easting : Easting/X in meters (input)
1264  * northing : Northing/Y in meters (input)
1265  * precision : Precision level of MGRS string (input)
1266  * MGRSString : MGRS coordinate string (output)
1267  */
1268 
1269  double false_easting; /* False easting for 2nd letter */
1270  double false_northing; /* False northing for 3rd letter */
1271  double grid_easting; /* Easting used to derive 2nd letter of MGRS */
1272  double grid_northing; /* Northing used to derive 3rd letter of MGRS */
1273  long ltr2_low_value; /* 2nd letter range - low number */
1274  int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
1275  double divisor;
1276  int index = 0;
1277  char MGRSString[21];
1278 
1279  char hemisphere = upsCoordinates->hemisphere();
1280  double easting = upsCoordinates->easting();
1281  double northing = upsCoordinates->northing();
1282 
1283  divisor = computeScale( precision );
1284 
1285  easting = (long)((easting +EPSILON2) /divisor) * divisor;
1286  northing = (long)((northing+EPSILON2) /divisor) * divisor;
1287 
1288  if (hemisphere == 'N')
1289  {
1290  if (easting >= TWOMIL)
1291  letters[0] = LETTER_Z;
1292  else
1293  letters[0] = LETTER_Y;
1294 
1295  index = letters[0] - 22;
1296  ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1297  false_easting = UPS_Constant_Table[index].false_easting;
1298  false_northing = UPS_Constant_Table[index].false_northing;
1299  }
1300  else
1301  {
1302  if (easting >= TWOMIL)
1303  letters[0] = LETTER_B;
1304  else
1305  letters[0] = LETTER_A;
1306 
1307  ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1308  false_easting = UPS_Constant_Table[letters[0]].false_easting;
1309  false_northing = UPS_Constant_Table[letters[0]].false_northing;
1310  }
1311 
1312  grid_northing = northing;
1313  grid_northing = grid_northing - false_northing;
1314  letters[2] = (long)(grid_northing / ONEHT);
1315 
1316  if (letters[2] > LETTER_H)
1317  letters[2] = letters[2] + 1;
1318 
1319  if (letters[2] > LETTER_N)
1320  letters[2] = letters[2] + 1;
1321 
1322  grid_easting = easting;
1323  grid_easting = grid_easting - false_easting;
1324  letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT));
1325 
1326  if (easting < TWOMIL)
1327  {
1328  if (letters[1] > LETTER_L)
1329  letters[1] = letters[1] + 3;
1330 
1331  if (letters[1] > LETTER_U)
1332  letters[1] = letters[1] + 2;
1333  }
1334  else
1335  {
1336  if (letters[1] > LETTER_C)
1337  letters[1] = letters[1] + 2;
1338 
1339  if (letters[1] > LETTER_H)
1340  letters[1] = letters[1] + 1;
1341 
1342  if (letters[1] > LETTER_L)
1343  letters[1] = letters[1] + 3;
1344  }
1345 
1346  makeMGRSString( MGRSString, 0, letters, easting, northing, precision );
1347 
1348  return new MGRSorUSNGCoordinates(
1350  MSP::CCS::Precision::toPrecision(precision) );
1351 }
1352 
1353 
1354 MSP::CCS::UPSCoordinates* MGRS::toUPS(
1355  long letters[MGRS_LETTERS],
1356  double easting,
1357  double northing )
1358 {
1359 /*
1360  * The function toUPS converts an MGRS coordinate string
1361  * to UPS (hemisphere, easting, and northing) coordinates, according
1362  * to the current ellipsoid parameters. If any errors occur, an
1363  * exception is thrown with a description of the error.
1364  *
1365  * MGRSString : MGRS coordinate string (input)
1366  * hemisphere : Hemisphere either 'N' or 'S' (output)
1367  * easting : Easting/X in meters (output)
1368  * northing : Northing/Y in meters (output)
1369  */
1370 
1371  long ltr2_high_value; /* 2nd letter range - high number */
1372  long ltr3_high_value; /* 3rd letter range - high number (UPS) */
1373  long ltr2_low_value; /* 2nd letter range - low number */
1374  double false_easting; /* False easting for 2nd letter */
1375  double false_northing; /* False northing for 3rd letter */
1376  double grid_easting; /* easting for 100,000 meter grid square */
1377  double grid_northing; /* northing for 100,000 meter grid square */
1378  char hemisphere;
1379  int index = 0;
1380 
1381  if ((letters[0] == LETTER_Y) || (letters[0] == LETTER_Z))
1382  {
1383  hemisphere = 'N';
1384 
1385  index = letters[0] - 22;
1386  ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1387  ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value;
1388  ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value;
1389  false_easting = UPS_Constant_Table[index].false_easting;
1390  false_northing = UPS_Constant_Table[index].false_northing;
1391  }
1392  else if ((letters[0] == LETTER_A) || (letters[0] == LETTER_B))
1393  {
1394  hemisphere = 'S';
1395 
1396  ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1397  ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value;
1398  ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value;
1399  false_easting = UPS_Constant_Table[letters[0]].false_easting;
1400  false_northing = UPS_Constant_Table[letters[0]].false_northing;
1401  }
1402  else
1404 
1405  /* Check that the second letter of the MGRS string is within
1406  * the range of valid second letter values
1407  * Also check that the third letter is valid */
1408  if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) ||
1409  ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) ||
1410  ( letters[1] == LETTER_M) || (letters[1] == LETTER_N) ||
1411  ( letters[1] == LETTER_V) || (letters[1] == LETTER_W)) ||
1412  ( letters[2] > ltr3_high_value))
1414 
1415  grid_northing = (double)letters[2] * ONEHT + false_northing;
1416  if (letters[2] > LETTER_I)
1417  grid_northing = grid_northing - ONEHT;
1418 
1419  if (letters[2] > LETTER_O)
1420  grid_northing = grid_northing - ONEHT;
1421 
1422  grid_easting = (double)((letters[1]) - ltr2_low_value) *ONEHT + false_easting;
1423  if (ltr2_low_value != LETTER_A)
1424  {
1425  if (letters[1] > LETTER_L)
1426  grid_easting = grid_easting - 300000.0;
1427 
1428  if (letters[1] > LETTER_U)
1429  grid_easting = grid_easting - 200000.0;
1430  }
1431  else
1432  {
1433  if (letters[1] > LETTER_C)
1434  grid_easting = grid_easting - 200000.0;
1435 
1436  if (letters[1] > LETTER_I)
1437  grid_easting = grid_easting - ONEHT;
1438 
1439  if (letters[1] > LETTER_L)
1440  grid_easting = grid_easting - 300000.0;
1441  }
1442 
1443  easting = grid_easting + easting;
1444  northing = grid_northing + northing;
1445 
1446  return new UPSCoordinates(
1447  CoordinateType::universalPolarStereographic, hemisphere, easting, northing);
1448 }
1449 
1450 
1451 void MGRS::getGridValues(
1452  long zone,
1453  long* ltr2_low_value,
1454  long* ltr2_high_value,
1455  double* pattern_offset )
1456 {
1457 /*
1458  * The function getGridValues sets the letter range used for
1459  * the 2nd letter in the MGRS coordinate string, based on the set
1460  * number of the utm zone. It also sets the pattern offset using a
1461  * value of A for the second letter of the grid square, based on
1462  * the grid pattern and set number of the utm zone.
1463  *
1464  * zone : Zone number (input)
1465  * ltr2_low_value : 2nd letter low number (output)
1466  * ltr2_high_value : 2nd letter high number (output)
1467  * pattern_offset : Pattern offset (output)
1468  */
1469 
1470  long set_number; /* Set number (1-6) based on UTM zone number */
1471  long aa_pattern; /* Pattern based on ellipsoid code */
1472 
1473  set_number = zone % 6;
1474 
1475  if (!set_number)
1476  set_number = 6;
1477 
1478  if(!strcmp(MGRSEllipsoidCode, CLARKE_1866) ||
1479  !strcmp(MGRSEllipsoidCode, CLARKE_1880) ||
1480  !strcmp(MGRSEllipsoidCode, BESSEL_1841) ||
1481  !strcmp(MGRSEllipsoidCode, BESSEL_1841_NAMIBIA))
1482  aa_pattern = FALSE;
1483  else
1484  aa_pattern = TRUE;
1485 
1486  if ((set_number == 1) || (set_number == 4))
1487  {
1488  *ltr2_low_value = LETTER_A;
1489  *ltr2_high_value = LETTER_H;
1490  }
1491  else if ((set_number == 2) || (set_number == 5))
1492  {
1493  *ltr2_low_value = LETTER_J;
1494  *ltr2_high_value = LETTER_R;
1495  }
1496  else if ((set_number == 3) || (set_number == 6))
1497  {
1498  *ltr2_low_value = LETTER_S;
1499  *ltr2_high_value = LETTER_Z;
1500  }
1501 
1502  /* False northing at A for second letter of grid square */
1503  if (aa_pattern)
1504  {
1505  if ((set_number % 2) == 0)
1506  *pattern_offset = 500000.0;
1507  else
1508  *pattern_offset = 0.0;
1509  }
1510  else
1511  {
1512  if ((set_number % 2) == 0)
1513  *pattern_offset = 1500000.0;
1514  else
1515  *pattern_offset = 1000000.00;
1516  }
1517 }
1518 
1519 
1520 void MGRS::getLatitudeBandMinNorthing(
1521  long letter,
1522  double* min_northing,
1523  double* northing_offset )
1524 {
1525 /*
1526  * The function getLatitudeBandMinNorthing receives a latitude band letter
1527  * and uses the Latitude_Band_Table to determine the minimum northing
1528  * and northing offset for that latitude band letter.
1529  *
1530  * letter : Latitude band letter (input)
1531  * min_northing : Minimum northing for that letter (output)
1532  * northing_offset : Latitude band northing offset (output)
1533  */
1534 
1535  if ((letter >= LETTER_C) && (letter <= LETTER_H))
1536  {
1537  *min_northing = Latitude_Band_Table[letter-2].min_northing;
1538  *northing_offset = Latitude_Band_Table[letter-2].northing_offset;
1539  }
1540  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
1541  {
1542  *min_northing = Latitude_Band_Table[letter-3].min_northing;
1543  *northing_offset = Latitude_Band_Table[letter-3].northing_offset;
1544  }
1545  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
1546  {
1547  *min_northing = Latitude_Band_Table[letter-4].min_northing;
1548  *northing_offset = Latitude_Band_Table[letter-4].northing_offset;
1549  }
1550  else
1552 }
1553 
1554 
1555 bool MGRS::inLatitudeRange( long letter, double latitude, double border )
1556 {
1557 /*
1558  * The function getLatitudeRange receives a latitude band letter
1559  * and uses the Latitude_Band_Table to determine the latitude band
1560  * boundaries for that latitude band letter.
1561  *
1562  * letter : Latitude band letter (input)
1563  * north : Northern latitude boundary for that letter (output)
1564  * north : Southern latitude boundary for that letter (output)
1565  */
1566  bool result = false;
1567  double north;
1568  double south;
1569 
1570  if ((letter >= LETTER_C) && (letter <= LETTER_H))
1571  {
1572  north = Latitude_Band_Table[letter-2].north * PI_OVER_180;
1573  south = Latitude_Band_Table[letter-2].south * PI_OVER_180;
1574  }
1575  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
1576  {
1577  north = Latitude_Band_Table[letter-3].north * PI_OVER_180;
1578  south = Latitude_Band_Table[letter-3].south * PI_OVER_180;
1579  }
1580  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
1581  {
1582  north = Latitude_Band_Table[letter-4].north * PI_OVER_180;
1583  south = Latitude_Band_Table[letter-4].south * PI_OVER_180;
1584  }
1585  else
1587 
1588  if( ((south - border) <= latitude) && (latitude <= (north + border)) )
1589  result = true;
1590 
1591  return result;
1592 }
1593 
1594 
1595 void MGRS::getLatitudeLetter( double latitude, int* letter )
1596 {
1597 /*
1598  * The function getLatitudeLetter receives a latitude value
1599  * and uses the Latitude_Band_Table to determine the latitude band
1600  * letter for that latitude.
1601  *
1602  * latitude : Latitude (input)
1603  * letter : Latitude band letter (output)
1604  */
1605 
1606  long band = 0;
1607 
1608  if (latitude >= _72 && latitude < _84_5)
1609  *letter = LETTER_X;
1610  else if (latitude > -_80_5 && latitude < _72)
1611  {
1612  band = (long)(((latitude + _80) / _8) + 1.0e-12);
1613  if(band < 0)
1614  band = 0;
1615  *letter = Latitude_Band_Table[band].letter;
1616  }
1617  else
1619 }
1620 
1621 // CLASSIFICATION: UNCLASSIFIED
double min_northing
Definition: MGRS.cpp:204
#define MAX_EAST_NORTH
Definition: MGRS.cpp:177
#define ONEHT
Definition: MGRS.cpp:160
#define LETTER_A
Definition: MGRS.cpp:133
#define LETTER_H
Definition: MGRS.cpp:140
~MGRS(void)
Definition: MGRS.cpp:527
#define LETTER_U
Definition: MGRS.cpp:153
long letter
Definition: MGRS.cpp:203
#define PI_OVER_2
Definition: MGRS.cpp:165
MSP::CCS::MGRSorUSNGCoordinates * convertFromUTM(UTMCoordinates *utmCoordinates, long precision)
Definition: MGRS.cpp:698
static const char * ellipsoidFlattening
Definition: ErrorMessages.h:47
#define TRUE
Definition: MGRS.cpp:162
MSP::CCS::GeodeticCoordinates * convertToGeodetic(MSP::CCS::UPSCoordinates *upsCoordinates)
Definition: UPS.cpp:264
#define MIN_EAST_NORTH
Definition: MGRS.cpp:176
MSP::CCS::UPSCoordinates * convertToUPS(MSP::CCS::MGRSorUSNGCoordinates *mgrsorUSNGCoordinates)
Definition: MGRS.cpp:899
#define LETTER_I
Definition: MGRS.cpp:141
#define CLARKE_1880
Definition: MGRS.cpp:195
double computeScale(int prec)
Definition: MGRS.cpp:256
const UPS_Constant UPS_Constant_Table[4]
Definition: MGRS.cpp:243
double false_northing
Definition: MGRS.cpp:240
#define LETTER_Q
Definition: MGRS.cpp:149
#define LETTER_N
Definition: MGRS.cpp:146
#define BESSEL_1841_NAMIBIA
Definition: MGRS.cpp:197
#define _84_5
Definition: MGRS.cpp:184
EllipsoidParameters * getParameters() const
Definition: MGRS.cpp:553
static const char * longitude
Definition: ErrorMessages.h:76
long ltr2_high_value
Definition: MGRS.cpp:237
#define LETTER_J
Definition: MGRS.cpp:142
#define _6
Definition: MGRS.cpp:179
long letter
Definition: MGRS.cpp:235
#define LETTER_L
Definition: MGRS.cpp:144
double north
Definition: MGRS.cpp:205
static const char * zone
Definition: ErrorMessages.h:53
double false_easting
Definition: MGRS.cpp:239
#define LETTER_S
Definition: MGRS.cpp:151
#define MAX_PRECISION
Definition: MGRS.cpp:172
#define LETTER_Y
Definition: MGRS.cpp:157
MSP::CCS::GeodeticCoordinates * convertToGeodetic(MSP::CCS::MGRSorUSNGCoordinates *mgrsCoordinates)
Definition: MGRS.cpp:636
static const char * semiMajorAxis
Definition: ErrorMessages.h:46
static const char * latitude
Definition: ErrorMessages.h:75
MGRS(double ellipsoidSemiMajorAxis, double ellipsoidFlattening, char *ellipsoidCode)
Definition: MGRS.cpp:475
double south
Definition: MGRS.cpp:206
#define MIN_NORTHING
Definition: MGRS.cpp:170
#define LETTER_B
Definition: MGRS.cpp:134
#define _80
Definition: MGRS.cpp:182
#define MIN_MGRS_NON_POLAR_LAT
Definition: MGRS.cpp:173
MGRS & operator=(const MGRS &m)
Definition: MGRS.cpp:537
#define MIN_EASTING
Definition: MGRS.cpp:168
#define LETTER_M
Definition: MGRS.cpp:145
MSP::CCS::UPSCoordinates * convertFromGeodetic(MSP::CCS::GeodeticCoordinates *geodeticCoordinates)
Definition: UPS.cpp:208
#define LETTER_E
Definition: MGRS.cpp:137
#define EPSILON
Definition: MGRS.cpp:131
#define PI
Definition: MGRS.cpp:164
static const char * mgrsString
Definition: ErrorMessages.h:84
#define LETTER_O
Definition: MGRS.cpp:147
#define FALSE
Definition: MGRS.cpp:163
static const char * northing
Definition: ErrorMessages.h:78
static const char * precision
Definition: ErrorMessages.h:59
#define LETTER_F
Definition: MGRS.cpp:138
#define LETTER_Z
Definition: MGRS.cpp:158
#define LETTER_T
Definition: MGRS.cpp:152
MSP::CCS::MGRSorUSNGCoordinates * convertFromGeodetic(MSP::CCS::GeodeticCoordinates *geodeticCoordinates, long precision)
Definition: MGRS.cpp:569
#define _500000
Definition: MGRS.cpp:186
#define LETTER_K
Definition: MGRS.cpp:143
double northing_offset
Definition: MGRS.cpp:207
#define EPSILON2
Definition: MGRS.cpp:199
MSP::CCS::MGRSorUSNGCoordinates * convertFromUPS(MSP::CCS::UPSCoordinates *upsCoordinates, long precision)
Definition: MGRS.cpp:829
void makeMGRSString(char *MGRSString, long zone, int letters[MGRS_LETTERS], double easting, double northing, long precision)
Definition: MGRS.cpp:291
MSP::CCS::UTMCoordinates * convertToUTM(MSP::CCS::MGRSorUSNGCoordinates *mgrsorUSNGCoordinates)
Definition: MGRS.cpp:770
static const char * easting
Definition: ErrorMessages.h:77
#define PI_OVER_180
Definition: MGRS.cpp:166
#define CLARKE_1866
Definition: MGRS.cpp:194
static Enum toPrecision(int prec)
Definition: Precision.h:28
#define _80_5
Definition: MGRS.cpp:183
#define MAX_NORTHING
Definition: MGRS.cpp:171
long ltr3_high_value
Definition: MGRS.cpp:238
void breakMGRSString(char *MGRSString, long *zone, long letters[MGRS_LETTERS], double *easting, double *northing, long *precision)
Definition: MGRS.cpp:342
MSP::CCS::GeodeticCoordinates * convertToGeodetic(MSP::CCS::UTMCoordinates *utmCoordinates)
Definition: UTM.cpp:429
void setWarningMessage(const char *__warningMessage)
#define LETTER_X
Definition: MGRS.cpp:156
const Latitude_Band Latitude_Band_Table[20]
Definition: MGRS.cpp:210
#define _8
Definition: MGRS.cpp:180
#define MAX_MGRS_NON_POLAR_LAT
Definition: MGRS.cpp:174
#define LETTER_G
Definition: MGRS.cpp:139
#define LETTER_W
Definition: MGRS.cpp:155
static const char * latitude
MSP::CCS::UTMCoordinates * convertFromGeodetic(MSP::CCS::GeodeticCoordinates *geodeticCoordinates, int utmZoneOverride=0)
Definition: UTM.cpp:294
#define TWOMIL
Definition: MGRS.cpp:161
#define LETTER_R
Definition: MGRS.cpp:150
long ltr2_low_value
Definition: MGRS.cpp:236
#define LETTER_P
Definition: MGRS.cpp:148
#define BESSEL_1841
Definition: MGRS.cpp:196
#define LETTER_V
Definition: MGRS.cpp:154
#define MGRS_LETTERS
Definition: MGRS.h:93
#define LETTER_D
Definition: MGRS.cpp:136
#define MAX_EASTING
Definition: MGRS.cpp:169
#define _72
Definition: MGRS.cpp:181
const char * warningMessage() const
static const char * hemisphere
Definition: ErrorMessages.h:71
#define LETTER_C
Definition: MGRS.cpp:135