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  /* get easting & northing */
440  n = num_digits/2;
441  *precision = n;
442  if (n > 0)
443  {
444  strncpy (east_string, tempMGRSString+j, n);
445  east_string[n] = 0;
446  sscanf (east_string, "%ld", &east);
447  strncpy (north_string, tempMGRSString+j+n, n);
448  north_string[n] = 0;
449  sscanf (north_string, "%ld", &north);
450  multiplier = computeScale( n );
451 
452  *easting = east * multiplier; // + (multiplier/2.0); // move to
453  *northing = north * multiplier; // + (multiplier/2.0); // grid center
454  }
455  else
456  {
457  *easting = 0.0;
458  *northing = 0.0;
459  }
460  }
461  else
462  throw CoordinateConversionException( ErrorMessages::mgrsString );
463 
464 }
465 
466 
467 /************************************************************************/
468 /* FUNCTIONS
469  *
470  */
471 
472 MGRS::MGRS(
473  double ellipsoidSemiMajorAxis,
474  double ellipsoidFlattening,
475  char* ellipsoidCode ) :
477  ups( 0 ),
478  utm( 0 )
479 {
480 /*
481  * The constructor receives the ellipsoid parameters and sets
482  * the corresponding state variables. If any errors occur, an
483  * exception is thrown with a description of the error.
484  *
485  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid in meters (input)
486  * ellipsoidFlattening : Flattening of ellipsoid (input)
487  * ellipsoid_Code : 2-letter code for ellipsoid (input)
488  */
489 
490  double inv_f = 1 / ellipsoidFlattening;
491 
492  if (ellipsoidSemiMajorAxis <= 0.0)
493  { /* Semi-major axis must be greater than zero */
495  }
496  if ((inv_f < 250) || (inv_f > 350))
497  { /* Inverse flattening must be between 250 and 350 */
499  }
500 
501  semiMajorAxis = ellipsoidSemiMajorAxis;
502  flattening = ellipsoidFlattening;
503 
504  strncpy (MGRSEllipsoidCode, ellipsoidCode, 2);
505  MGRSEllipsoidCode[2] = '\0';
506 
507  ups = new UPS( semiMajorAxis, flattening );
508 
509  utm = new UTM( semiMajorAxis, flattening, MGRSEllipsoidCode, 0 );
510 }
511 
512 
513 MGRS::MGRS( const MGRS &m )
514 {
515  ups = new UPS( *( m.ups ) );
516  utm = new UTM( *( m.utm ) );
517 
520  strcpy( MGRSEllipsoidCode, m.MGRSEllipsoidCode );
521 }
522 
523 
525 {
526  delete ups;
527  ups = 0;
528 
529  delete utm;
530  utm = 0;
531 }
532 
533 
535 {
536  if( this != &m )
537  {
538  ups->operator=( *m.ups );
539  utm->operator=( *m.utm );
540 
543  strcpy( MGRSEllipsoidCode, m.MGRSEllipsoidCode );
544  }
545 
546  return *this;
547 }
548 
549 
551 {
552 /*
553  * The function getParameters returns the current ellipsoid
554  * parameters.
555  *
556  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters (output)
557  * ellipsoidFlattening : Flattening of ellipsoid (output)
558  * ellipsoidCode : 2-letter code for ellipsoid (output)
559  */
560 
561  return new EllipsoidParameters(
562  semiMajorAxis, flattening, (char*)MGRSEllipsoidCode );
563 }
564 
565 
567  MSP::CCS::GeodeticCoordinates* geodeticCoordinates,
568  long precision )
569 {
570 /*
571  * The function convertFromGeodetic converts Geodetic (latitude and
572  * longitude) coordinates to an MGRS coordinate string, according to the
573  * current ellipsoid parameters. If any errors occur, an exception
574  * is thrown with a description of the error.
575  *
576  * latitude : Latitude in radians (input)
577  * longitude : Longitude in radians (input)
578  * precision : Precision level of MGRS string (input)
579  * MGRSString : MGRS coordinate string (output)
580  *
581  */
582 
583  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
584  UTMCoordinates* utmCoordinates = 0;
585  UPSCoordinates* upsCoordinates = 0;
586 
587  double latitude = geodeticCoordinates->latitude();
588  double longitude = geodeticCoordinates->longitude();
589 
590  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
591  { /* latitude out of range */
593  }
594  if ((longitude < (-PI - EPSILON)) || (longitude > (2*PI + EPSILON)))
595  { /* longitude out of range */
597  }
598  if ((precision < 0) || (precision > MAX_PRECISION))
600 
601  try
602  {
603  // If the latitude is within the valid mgrs non polar range [-80, 84),
604  // convert to mgrs using the utm path,
605  // otherwise convert to mgrs using the ups path
606  if((latitude >= MIN_MGRS_NON_POLAR_LAT - EPSILON) &&
607  (latitude < MAX_MGRS_NON_POLAR_LAT + EPSILON))
608  {
609  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
610  mgrsorUSNGCoordinates = fromUTM(
611  utmCoordinates, longitude, latitude, precision );
612  delete utmCoordinates;
613  utmCoordinates = 0;
614  }
615  else
616  {
617  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
618  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
619  delete upsCoordinates;
620  upsCoordinates = 0;
621  }
622  }
623  catch ( CoordinateConversionException e) {
624  delete utmCoordinates;
625  delete upsCoordinates;
626  throw e;
627  }
628 
629  return mgrsorUSNGCoordinates;
630 }
631 
632 
634  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
635 {
636 /*
637  * The function convertToGeodetic converts an MGRS coordinate string
638  * to Geodetic (latitude and longitude) coordinates
639  * according to the current ellipsoid parameters. If any errors occur,
640  * an exception is thrown with a description of the error.
641  *
642  * MGRS : MGRS coordinate string (input)
643  * latitude : Latitude in radians (output)
644  * longitude : Longitude in radians (output)
645  *
646  */
647 
648  long zone;
649  long letters[MGRS_LETTERS];
650  double mgrs_easting;
651  double mgrs_northing;
652  long precision;
653  GeodeticCoordinates* geodeticCoordinates = 0;
654  UTMCoordinates* utmCoordinates = 0;
655  UPSCoordinates* upsCoordinates = 0;
656 
658  mgrsorUSNGCoordinates->MGRSString(), &zone, letters,
659  &mgrs_easting, &mgrs_northing, &precision );
660 
661  try
662  {
663  if( zone )
664  {
665  utmCoordinates = toUTM(
666  zone, letters, mgrs_easting, mgrs_northing, precision );
667  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
668  if( strlen( utmCoordinates->warningMessage() ) > 0 )
669  {
670  geodeticCoordinates->setWarningMessage(
671  utmCoordinates->warningMessage() );
672  }
673  delete utmCoordinates;
674  utmCoordinates = 0;
675  }
676  else
677  {
678  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
679  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
680  delete upsCoordinates;
681  upsCoordinates = 0;
682  }
683  }
685  {
686  delete utmCoordinates;
687  delete upsCoordinates;
688  throw e;
689  }
690 
691  return geodeticCoordinates;
692 }
693 
694 
696  UTMCoordinates* utmCoordinates,
697  long precision )
698 {
699 /*
700  * The function convertFromUTM converts UTM (zone, easting, and
701  * northing) coordinates to an MGRS coordinate string, according to the
702  * current ellipsoid parameters. If any errors occur, an exception is
703  * thrown with a description of the error.
704  *
705  * zone : UTM zone (input)
706  * hemisphere : North or South hemisphere (input)
707  * easting : Easting (X) in meters (input)
708  * northing : Northing (Y) in meters (input)
709  * precision : Precision level of MGRS string (input)
710  * MGRSString : MGRS coordinate string (output)
711  */
712 
713  long zone = utmCoordinates->zone();
714  char hemisphere = utmCoordinates->hemisphere();
715  double easting = utmCoordinates->easting();
716  double northing = utmCoordinates->northing();
717 
718  GeodeticCoordinates* geodeticCoordinates = 0;
719  UPSCoordinates* upsCoordinates = 0;
720  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
721 
722  if ((zone < 1) || (zone > 60))
724  if ((hemisphere != 'S') && (hemisphere != 'N'))
726  if ((easting < MIN_EASTING) || (easting > MAX_EASTING))
728  if ((northing < MIN_NORTHING) || (northing > MAX_NORTHING))
730  if ((precision < 0) || (precision > MAX_PRECISION))
732 
733  try
734  {
735  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
736 
737  // If the latitude is within the valid mgrs non polar range [-80, 84),
738  // convert to mgrs using the utm path,
739  // otherwise convert to mgrs using the ups path
740  double latitude = geodeticCoordinates->latitude();
741 
742  if((latitude >= (MIN_MGRS_NON_POLAR_LAT - EPSILON)) &&
743  (latitude < (MAX_MGRS_NON_POLAR_LAT + EPSILON)))
744  mgrsorUSNGCoordinates = fromUTM(
745  utmCoordinates,
746  geodeticCoordinates->longitude(), latitude, precision);
747  else
748  {
749  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
750  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
751  }
752  }
754  {
755  delete upsCoordinates;
756  delete geodeticCoordinates;
757  throw e;
758  }
759 
760  delete upsCoordinates;
761  delete geodeticCoordinates;
762 
763  return mgrsorUSNGCoordinates;
764 }
765 
766 
768  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
769 {
770 /*
771  * The function convertToUTM converts an MGRS coordinate string
772  * to UTM projection (zone, hemisphere, easting and northing) coordinates
773  * according to the current ellipsoid parameters. If any errors occur,
774  * an exception is thrown with a description of the error.
775  *
776  * MGRSString : MGRS coordinate string (input)
777  * zone : UTM zone (output)
778  * hemisphere : North or South hemisphere (output)
779  * easting : Easting (X) in meters (output)
780  * northing : Northing (Y) in meters (output)
781  */
782 
783  long zone;
784  long letters[MGRS_LETTERS];
785  double mgrs_easting, mgrs_northing;
786  long precision;
787  UTMCoordinates* utmCoordinates = 0;
788  GeodeticCoordinates* geodeticCoordinates = 0;
789  UPSCoordinates* upsCoordinates = 0;
790 
791  try
792  {
794  mgrsorUSNGCoordinates->MGRSString(), &zone, letters,
795  &mgrs_easting, &mgrs_northing, &precision );
796  if (zone)
797  {
798  utmCoordinates = toUTM(
799  zone, letters, mgrs_easting, mgrs_northing, precision );
800  // Convert to geodetic to make sure that
801  // the coordinates are in the valid utm range
802  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
803  }
804  else
805  {
806  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
807  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
808  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
809  }
810  }
812  {
813  delete utmCoordinates;
814  delete upsCoordinates;
815  delete geodeticCoordinates;
816  throw e;
817  }
818 
819  delete upsCoordinates;
820  delete geodeticCoordinates;
821 
822  return utmCoordinates;
823 }
824 
825 
827  MSP::CCS::UPSCoordinates* upsCoordinates,
828  long precision )
829 {
830 /*
831  * The function convertFromUPS converts UPS (hemisphere, easting,
832  * and northing) coordinates to an MGRS coordinate string according to
833  * the current ellipsoid parameters. If any errors occur, an
834  * exception is thrown with a description of the error.
835  *
836  * hemisphere : Hemisphere either 'N' or 'S' (input)
837  * easting : Easting/X in meters (input)
838  * northing : Northing/Y in meters (input)
839  * precision : Precision level of MGRS string (input)
840  * MGRSString : MGRS coordinate string (output)
841  */
842 
843  int index = 0;
844 
845  char hemisphere = upsCoordinates->hemisphere();
846  double easting = upsCoordinates->easting();
847  double northing = upsCoordinates->northing();
848 
849  UTMCoordinates* utmCoordinates = 0;
850  GeodeticCoordinates* geodeticCoordinates = 0;
851  MGRSorUSNGCoordinates* mgrsorUSNGCoordinates = 0;
852 
853  if ((hemisphere != 'N') && (hemisphere != 'S'))
855  if ((easting < MIN_EAST_NORTH) || (easting > MAX_EAST_NORTH))
857  if ((northing < MIN_EAST_NORTH) || (northing > MAX_EAST_NORTH))
859  if ((precision < 0) || (precision > MAX_PRECISION))
861 
862  try
863  {
864  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
865 
866  // If the latitude is within the valid mgrs polar range [-90, -80) or
867  // [84, 90], convert to mgrs using the ups path,
868  // otherwise convert to mgrs using the utm path
869  double latitude = geodeticCoordinates->latitude();
870 
871  if((latitude < (MIN_MGRS_NON_POLAR_LAT - EPSILON)) ||
872  (latitude >= (MAX_MGRS_NON_POLAR_LAT + EPSILON)))
873  mgrsorUSNGCoordinates = fromUPS( upsCoordinates, precision );
874  else
875  {
876  utmCoordinates = utm->convertFromGeodetic( geodeticCoordinates );
877  double longitude = geodeticCoordinates->longitude();
878  mgrsorUSNGCoordinates = fromUTM(
879  utmCoordinates, longitude, latitude, precision );
880  }
881  }
883  {
884  delete utmCoordinates;
885  delete geodeticCoordinates;
886  throw e;
887  }
888 
889  delete utmCoordinates;
890  delete geodeticCoordinates;
891 
892  return mgrsorUSNGCoordinates;
893 }
894 
895 
897  MSP::CCS::MGRSorUSNGCoordinates* mgrsorUSNGCoordinates )
898 {
899 /*
900  * The function convertToUPS converts an MGRS coordinate string
901  * to UPS (hemisphere, easting, and northing) coordinates, according
902  * to the current ellipsoid parameters. If any errors occur, an
903  * exception is thrown with a description of the error.
904  *
905  * MGRSString : MGRS coordinate string (input)
906  * hemisphere : Hemisphere either 'N' or 'S' (output)
907  * easting : Easting/X in meters (output)
908  * northing : Northing/Y in meters (output)
909  */
910 
911  long zone;
912  long letters[MGRS_LETTERS];
913  long precision;
914  double mgrs_easting;
915  double mgrs_northing;
916  int index = 0;
917  UPSCoordinates* upsCoordinates = 0;
918  GeodeticCoordinates* geodeticCoordinates = 0;
919  UTMCoordinates* utmCoordinates = 0;
920 
921  try
922  {
924  mgrsorUSNGCoordinates->MGRSString(),
925  &zone, letters, &mgrs_easting, &mgrs_northing, &precision );
926 
927  if( !zone )
928  {
929  upsCoordinates = toUPS( letters, mgrs_easting, mgrs_northing );
930  // Convert to geodetic to make sure that
931  // the coordinates are in the valid ups range
932  geodeticCoordinates = ups->convertToGeodetic( upsCoordinates );
933  }
934  else
935  {
936  utmCoordinates = toUTM(
937  zone, letters, mgrs_easting, mgrs_northing, precision );
938  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
939  if( strlen( utmCoordinates->warningMessage() ) > 0 )
940  {
941  geodeticCoordinates->setWarningMessage(
942  utmCoordinates->warningMessage() );
943  }
944  upsCoordinates = ups->convertFromGeodetic( geodeticCoordinates );
945  }
946  }
948  {
949  delete utmCoordinates;
950  delete upsCoordinates;
951  delete geodeticCoordinates;
952  throw e;
953  }
954 
955  delete utmCoordinates;
956  delete geodeticCoordinates;
957 
958  return upsCoordinates;
959 }
960 
961 
962 MSP::CCS::MGRSorUSNGCoordinates* MGRS::fromUTM(
963  MSP::CCS::UTMCoordinates* utmCoordinates,
964  double longitude,
965  double latitude,
966  long precision )
967 {
968 /*
969  * The function fromUTM calculates an MGRS coordinate string
970  * based on the zone, latitude, easting and northing.
971  *
972  * zone : Zone number (input)
973  * hemisphere : Hemisphere (input)
974  * longitude : Longitude in radians (input)
975  * latitude : Latitude in radians (input)
976  * easting : Easting (input)
977  * northing : Northing (input)
978  * precision : Precision (input)
979  * MGRSString : MGRS coordinate string (output)
980  */
981 
982  double pattern_offset; /* Pattern offset for 3rd letter */
983  double grid_northing; /* Northing used to derive 3rd letter of MGRS */
984  long ltr2_low_value; /* 2nd letter range - low number */
985  long ltr2_high_value; /* 2nd letter range - high number */
986  int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
987  char MGRSString[21];
988  long override = 0;
989  long natural_zone;
990 
991  long zone = utmCoordinates->zone();
992  char hemisphere = utmCoordinates->hemisphere();
993  double easting = utmCoordinates->easting();
994  double northing = utmCoordinates->northing();
995 
996  getLatitudeLetter( latitude, &letters[0] );
997 
998  // Check if the point is within it's natural zone
999  // If it is not, put it there
1000  double pad = EPSILON2 / 6378137.0;
1001  if (longitude < PI)
1002  {
1003  natural_zone = (long)(31 + ((longitude+pad) / _6));
1004  }
1005  else
1006  {
1007  natural_zone = (long)(((longitude+pad) / _6) - 29);
1008  }
1009 
1010  if (natural_zone > 60)
1011  natural_zone = 1;
1012  if (zone != natural_zone)
1013  { // reconvert to override zone
1014 
1015  UTM utmOverride( semiMajorAxis, flattening, MGRSEllipsoidCode, natural_zone );
1016  GeodeticCoordinates geodeticCoordinates(
1017  CoordinateType::geodetic, longitude, latitude );
1018  UTMCoordinates* utmCoordinatesOverride =
1019  utmOverride.convertFromGeodetic( &geodeticCoordinates );
1020 
1021  zone = utmCoordinatesOverride->zone();
1022  hemisphere = utmCoordinatesOverride->hemisphere();
1023  easting = utmCoordinatesOverride->easting();
1024  northing = utmCoordinatesOverride->northing();
1025 
1026  delete utmCoordinatesOverride;
1027  utmCoordinatesOverride = 0;
1028  }
1029 
1030  /* UTM special cases */
1031  if (letters[0] == LETTER_V) // V latitude band
1032  {
1033  if ((zone == 31) && (easting >= _500000))
1034  override = 32; // extension of zone 32V
1035  }
1036  else if (letters[0] == LETTER_X)
1037  {
1038  if ((zone == 32) && (easting < _500000)) // extension of zone 31X
1039  override = 31;
1040  else if (((zone == 32) && (easting >= _500000)) || // western extension of zone 33X
1041  ((zone == 34) && (easting < _500000))) // eastern extension of zone 33X
1042  override = 33;
1043  else if (((zone == 34) && (easting >= _500000)) || // western extension of zone 35X
1044  ((zone == 36) && (easting < _500000))) // eastern extension of zone 35X
1045  override = 35;
1046  else if ((zone == 36) && (easting >= _500000)) // western extension of zone 37X
1047  override = 37;
1048  }
1049 
1050  if (override)
1051  { // reconvert to override zone
1052 
1053  UTM utmOverride( semiMajorAxis, flattening, MGRSEllipsoidCode, override );
1054  GeodeticCoordinates geodeticCoordinates(
1055  CoordinateType::geodetic, longitude, latitude );
1056  UTMCoordinates* utmCoordinatesOverride =
1057  utmOverride.convertFromGeodetic( &geodeticCoordinates );
1058 
1059  zone = utmCoordinatesOverride->zone();
1060  hemisphere = utmCoordinatesOverride->hemisphere();
1061  easting = utmCoordinatesOverride->easting();
1062  northing = utmCoordinatesOverride->northing();
1063 
1064  delete utmCoordinatesOverride;
1065  utmCoordinatesOverride = 0;
1066  }
1067 
1068  double divisor = computeScale( precision );
1069 
1070  easting = ( long )( (easting +EPSILON2) /divisor ) * divisor;
1071  northing = ( long )( (northing+EPSILON2) /divisor ) * divisor;
1072 
1073  if( latitude <= 0.0 && northing == 1.0e7 )
1074  {
1075  latitude = 0.0;
1076  northing = 0.0;
1077  }
1078 
1079  getGridValues( zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset );
1080 
1081  grid_northing = northing;
1082 
1083  while (grid_northing >= TWOMIL)
1084  {
1085  grid_northing = grid_northing - TWOMIL;
1086  }
1087  grid_northing = grid_northing + pattern_offset;
1088  if(grid_northing >= TWOMIL)
1089  grid_northing = grid_northing - TWOMIL;
1090 
1091  letters[2] = (long)(grid_northing / ONEHT);
1092  if (letters[2] > LETTER_H)
1093  letters[2] = letters[2] + 1;
1094 
1095  if (letters[2] > LETTER_N)
1096  letters[2] = letters[2] + 1;
1097 
1098  letters[1] = ltr2_low_value + ((long)(easting / ONEHT) - 1);
1099  if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
1100  letters[1] = letters[1] + 1;
1101 
1102  makeMGRSString( MGRSString, zone, letters, easting, northing, precision );
1103 
1104  return new MGRSorUSNGCoordinates(
1106  MSP::CCS::Precision::toPrecision(precision) );
1107 }
1108 
1109 
1110 MSP::CCS::UTMCoordinates* MGRS::toUTM(
1111  long zone,
1112  long letters[MGRS_LETTERS],
1113  double easting,
1114  double northing,
1115  long precision )
1116 {
1117 /*
1118  * The function toUTM converts an MGRS coordinate string
1119  * to UTM projection (zone, hemisphere, easting and northing) coordinates
1120  * according to the current ellipsoid parameters. If any errors occur,
1121  * an exception is thrown with a description of the error.
1122  *
1123  * MGRSString : MGRS coordinate string (input)
1124  * zone : UTM zone (output)
1125  * hemisphere : North or South hemisphere (output)
1126  * easting : Easting (X) in meters (output)
1127  * northing : Northing (Y) in meters (output)
1128  */
1129 
1130  char hemisphere;
1131  double min_northing;
1132  double northing_offset;
1133  long ltr2_low_value;
1134  long ltr2_high_value;
1135  double pattern_offset;
1136  double grid_easting; /* Easting for 100,000 meter grid square */
1137  double grid_northing; /* Northing for 100,000 meter grid square */
1138  double temp_grid_northing = 0.0;
1139  double fabs_grid_northing = 0.0;
1140  double latitude = 0.0;
1141  double longitude = 0.0;
1142  double divisor = 1.0;
1143  UTMCoordinates* utmCoordinates = 0;
1144 
1145  if((letters[0] == LETTER_X) && ((zone == 32) || (zone == 34) || (zone == 36)))
1147  else if ((letters[0] == LETTER_V) && (zone == 31) && (letters[1] > LETTER_D))
1149  else
1150  {
1151  if (letters[0] < LETTER_N)
1152  hemisphere = 'S';
1153  else
1154  hemisphere = 'N';
1155 
1156  getGridValues(zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
1157 
1158  /* Check that the second letter of the MGRS string is within
1159  * the range of valid second letter values
1160  * Also check that the third letter is valid */
1161  if((letters[1] < ltr2_low_value) ||
1162  (letters[1] > ltr2_high_value) ||
1163  (letters[2] > LETTER_V) )
1165 
1166  grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
1167  if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
1168  grid_easting = grid_easting - ONEHT;
1169 
1170  double row_letter_northing = (double)(letters[2]) * ONEHT;
1171  if (letters[2] > LETTER_O)
1172  row_letter_northing = row_letter_northing - ONEHT;
1173 
1174  if (letters[2] > LETTER_I)
1175  row_letter_northing = row_letter_northing - ONEHT;
1176 
1177  if (row_letter_northing >= TWOMIL)
1178  row_letter_northing = row_letter_northing - TWOMIL;
1179 
1180  getLatitudeBandMinNorthing(letters[0], &min_northing, &northing_offset);
1181 
1182  grid_northing = row_letter_northing - pattern_offset;
1183  if(grid_northing < 0)
1184  grid_northing += TWOMIL;
1185 
1186  grid_northing += northing_offset;
1187 
1188  if(grid_northing < min_northing)
1189  grid_northing += TWOMIL;
1190 
1191  easting = grid_easting + easting;
1192  northing = grid_northing + northing;
1193 
1194  utmCoordinates = new UTMCoordinates(
1196  zone, hemisphere, easting, northing );
1197 
1198  /* check that point is within Zone Letter bounds */
1199  GeodeticCoordinates* geodeticCoordinates;
1200  try
1201  {
1202  geodeticCoordinates = utm->convertToGeodetic( utmCoordinates );
1203  }
1205  {
1206  delete utmCoordinates;
1207  throw e;
1208  }
1209 
1210  double latitude = geodeticCoordinates->latitude();
1211 
1212  delete geodeticCoordinates;
1213  geodeticCoordinates = 0;
1214 
1215  divisor = ONEHT / computeScale( precision );
1216 
1217  if( ! inLatitudeRange(letters[0], latitude, PI_OVER_180/divisor) )
1218  {
1219  // check adjacent bands
1220  long prevBand = letters[0] - 1;
1221  long nextBand = letters[0] + 1;
1222 
1223  if( letters[0] == LETTER_C ) // if last band, do not go off list
1224  prevBand = letters[0];
1225 
1226  if( letters[0] == LETTER_X )
1227  nextBand = letters[0];
1228 
1229  if( prevBand == LETTER_I || prevBand == LETTER_O )
1230  prevBand--;
1231 
1232  if( nextBand == LETTER_I || nextBand == LETTER_O )
1233  nextBand++;
1234 
1235  if(inLatitudeRange( prevBand, latitude, PI_OVER_180/divisor ) ||
1236  inLatitudeRange( nextBand, latitude, PI_OVER_180/divisor ) )
1237  {
1238  utmCoordinates->setWarningMessage(
1240  }
1241  else
1242  {
1244  }
1245  }
1246  }
1247 
1248  return utmCoordinates;
1249 }
1250 
1251 
1252 MSP::CCS::MGRSorUSNGCoordinates* MGRS::fromUPS(
1253  MSP::CCS::UPSCoordinates* upsCoordinates,
1254  long precision )
1255 {
1256 /*
1257  * The function fromUPS converts UPS (hemisphere, easting,
1258  * and northing) coordinates to an MGRS coordinate string according to
1259  * the current ellipsoid parameters.
1260  *
1261  * hemisphere : Hemisphere either 'N' or 'S' (input)
1262  * easting : Easting/X in meters (input)
1263  * northing : Northing/Y in meters (input)
1264  * precision : Precision level of MGRS string (input)
1265  * MGRSString : MGRS coordinate string (output)
1266  */
1267 
1268  double false_easting; /* False easting for 2nd letter */
1269  double false_northing; /* False northing for 3rd letter */
1270  double grid_easting; /* Easting used to derive 2nd letter of MGRS */
1271  double grid_northing; /* Northing used to derive 3rd letter of MGRS */
1272  long ltr2_low_value; /* 2nd letter range - low number */
1273  int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
1274  double divisor;
1275  int index = 0;
1276  char MGRSString[21];
1277 
1278  char hemisphere = upsCoordinates->hemisphere();
1279  double easting = upsCoordinates->easting();
1280  double northing = upsCoordinates->northing();
1281 
1282  divisor = computeScale( precision );
1283 
1284  easting = (long)((easting +EPSILON2) /divisor) * divisor;
1285  northing = (long)((northing+EPSILON2) /divisor) * divisor;
1286 
1287  if (hemisphere == 'N')
1288  {
1289  if (easting >= TWOMIL)
1290  letters[0] = LETTER_Z;
1291  else
1292  letters[0] = LETTER_Y;
1293 
1294  index = letters[0] - 22;
1295  ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1296  false_easting = UPS_Constant_Table[index].false_easting;
1297  false_northing = UPS_Constant_Table[index].false_northing;
1298  }
1299  else
1300  {
1301  if (easting >= TWOMIL)
1302  letters[0] = LETTER_B;
1303  else
1304  letters[0] = LETTER_A;
1305 
1306  ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1307  false_easting = UPS_Constant_Table[letters[0]].false_easting;
1308  false_northing = UPS_Constant_Table[letters[0]].false_northing;
1309  }
1310 
1311  grid_northing = northing;
1312  grid_northing = grid_northing - false_northing;
1313  letters[2] = (long)(grid_northing / ONEHT);
1314 
1315  if (letters[2] > LETTER_H)
1316  letters[2] = letters[2] + 1;
1317 
1318  if (letters[2] > LETTER_N)
1319  letters[2] = letters[2] + 1;
1320 
1321  grid_easting = easting;
1322  grid_easting = grid_easting - false_easting;
1323  letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT));
1324 
1325  if (easting < TWOMIL)
1326  {
1327  if (letters[1] > LETTER_L)
1328  letters[1] = letters[1] + 3;
1329 
1330  if (letters[1] > LETTER_U)
1331  letters[1] = letters[1] + 2;
1332  }
1333  else
1334  {
1335  if (letters[1] > LETTER_C)
1336  letters[1] = letters[1] + 2;
1337 
1338  if (letters[1] > LETTER_H)
1339  letters[1] = letters[1] + 1;
1340 
1341  if (letters[1] > LETTER_L)
1342  letters[1] = letters[1] + 3;
1343  }
1344 
1345  makeMGRSString( MGRSString, 0, letters, easting, northing, precision );
1346 
1347  return new MGRSorUSNGCoordinates(
1349  MSP::CCS::Precision::toPrecision(precision) );
1350 }
1351 
1352 
1353 MSP::CCS::UPSCoordinates* MGRS::toUPS(
1354  long letters[MGRS_LETTERS],
1355  double easting,
1356  double northing )
1357 {
1358 /*
1359  * The function toUPS converts an MGRS coordinate string
1360  * to UPS (hemisphere, easting, and northing) coordinates, according
1361  * to the current ellipsoid parameters. If any errors occur, an
1362  * exception is thrown with a description of the error.
1363  *
1364  * MGRSString : MGRS coordinate string (input)
1365  * hemisphere : Hemisphere either 'N' or 'S' (output)
1366  * easting : Easting/X in meters (output)
1367  * northing : Northing/Y in meters (output)
1368  */
1369 
1370  long ltr2_high_value; /* 2nd letter range - high number */
1371  long ltr3_high_value; /* 3rd letter range - high number (UPS) */
1372  long ltr2_low_value; /* 2nd letter range - low number */
1373  double false_easting; /* False easting for 2nd letter */
1374  double false_northing; /* False northing for 3rd letter */
1375  double grid_easting; /* easting for 100,000 meter grid square */
1376  double grid_northing; /* northing for 100,000 meter grid square */
1377  char hemisphere;
1378  int index = 0;
1379 
1380  if ((letters[0] == LETTER_Y) || (letters[0] == LETTER_Z))
1381  {
1382  hemisphere = 'N';
1383 
1384  index = letters[0] - 22;
1385  ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1386  ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value;
1387  ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value;
1388  false_easting = UPS_Constant_Table[index].false_easting;
1389  false_northing = UPS_Constant_Table[index].false_northing;
1390  }
1391  else if ((letters[0] == LETTER_A) || (letters[0] == LETTER_B))
1392  {
1393  hemisphere = 'S';
1394 
1395  ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1396  ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value;
1397  ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value;
1398  false_easting = UPS_Constant_Table[letters[0]].false_easting;
1399  false_northing = UPS_Constant_Table[letters[0]].false_northing;
1400  }
1401  else
1403 
1404  /* Check that the second letter of the MGRS string is within
1405  * the range of valid second letter values
1406  * Also check that the third letter is valid */
1407  if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) ||
1408  ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) ||
1409  ( letters[1] == LETTER_M) || (letters[1] == LETTER_N) ||
1410  ( letters[1] == LETTER_V) || (letters[1] == LETTER_W)) ||
1411  ( letters[2] > ltr3_high_value))
1413 
1414  grid_northing = (double)letters[2] * ONEHT + false_northing;
1415  if (letters[2] > LETTER_I)
1416  grid_northing = grid_northing - ONEHT;
1417 
1418  if (letters[2] > LETTER_O)
1419  grid_northing = grid_northing - ONEHT;
1420 
1421  grid_easting = (double)((letters[1]) - ltr2_low_value) *ONEHT + false_easting;
1422  if (ltr2_low_value != LETTER_A)
1423  {
1424  if (letters[1] > LETTER_L)
1425  grid_easting = grid_easting - 300000.0;
1426 
1427  if (letters[1] > LETTER_U)
1428  grid_easting = grid_easting - 200000.0;
1429  }
1430  else
1431  {
1432  if (letters[1] > LETTER_C)
1433  grid_easting = grid_easting - 200000.0;
1434 
1435  if (letters[1] > LETTER_I)
1436  grid_easting = grid_easting - ONEHT;
1437 
1438  if (letters[1] > LETTER_L)
1439  grid_easting = grid_easting - 300000.0;
1440  }
1441 
1442  easting = grid_easting + easting;
1443  northing = grid_northing + northing;
1444 
1445  return new UPSCoordinates(
1446  CoordinateType::universalPolarStereographic, hemisphere, easting, northing);
1447 }
1448 
1449 
1450 void MGRS::getGridValues(
1451  long zone,
1452  long* ltr2_low_value,
1453  long* ltr2_high_value,
1454  double* pattern_offset )
1455 {
1456 /*
1457  * The function getGridValues sets the letter range used for
1458  * the 2nd letter in the MGRS coordinate string, based on the set
1459  * number of the utm zone. It also sets the pattern offset using a
1460  * value of A for the second letter of the grid square, based on
1461  * the grid pattern and set number of the utm zone.
1462  *
1463  * zone : Zone number (input)
1464  * ltr2_low_value : 2nd letter low number (output)
1465  * ltr2_high_value : 2nd letter high number (output)
1466  * pattern_offset : Pattern offset (output)
1467  */
1468 
1469  long set_number; /* Set number (1-6) based on UTM zone number */
1470  long aa_pattern; /* Pattern based on ellipsoid code */
1471 
1472  set_number = zone % 6;
1473 
1474  if (!set_number)
1475  set_number = 6;
1476 
1477  if(!strcmp(MGRSEllipsoidCode, CLARKE_1866) ||
1478  !strcmp(MGRSEllipsoidCode, CLARKE_1880) ||
1479  !strcmp(MGRSEllipsoidCode, BESSEL_1841) ||
1480  !strcmp(MGRSEllipsoidCode, BESSEL_1841_NAMIBIA))
1481  aa_pattern = FALSE;
1482  else
1483  aa_pattern = TRUE;
1484 
1485  if ((set_number == 1) || (set_number == 4))
1486  {
1487  *ltr2_low_value = LETTER_A;
1488  *ltr2_high_value = LETTER_H;
1489  }
1490  else if ((set_number == 2) || (set_number == 5))
1491  {
1492  *ltr2_low_value = LETTER_J;
1493  *ltr2_high_value = LETTER_R;
1494  }
1495  else if ((set_number == 3) || (set_number == 6))
1496  {
1497  *ltr2_low_value = LETTER_S;
1498  *ltr2_high_value = LETTER_Z;
1499  }
1500 
1501  /* False northing at A for second letter of grid square */
1502  if (aa_pattern)
1503  {
1504  if ((set_number % 2) == 0)
1505  *pattern_offset = 500000.0;
1506  else
1507  *pattern_offset = 0.0;
1508  }
1509  else
1510  {
1511  if ((set_number % 2) == 0)
1512  *pattern_offset = 1500000.0;
1513  else
1514  *pattern_offset = 1000000.00;
1515  }
1516 }
1517 
1518 
1519 void MGRS::getLatitudeBandMinNorthing(
1520  long letter,
1521  double* min_northing,
1522  double* northing_offset )
1523 {
1524 /*
1525  * The function getLatitudeBandMinNorthing receives a latitude band letter
1526  * and uses the Latitude_Band_Table to determine the minimum northing
1527  * and northing offset for that latitude band letter.
1528  *
1529  * letter : Latitude band letter (input)
1530  * min_northing : Minimum northing for that letter (output)
1531  * northing_offset : Latitude band northing offset (output)
1532  */
1533 
1534  if ((letter >= LETTER_C) && (letter <= LETTER_H))
1535  {
1536  *min_northing = Latitude_Band_Table[letter-2].min_northing;
1537  *northing_offset = Latitude_Band_Table[letter-2].northing_offset;
1538  }
1539  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
1540  {
1541  *min_northing = Latitude_Band_Table[letter-3].min_northing;
1542  *northing_offset = Latitude_Band_Table[letter-3].northing_offset;
1543  }
1544  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
1545  {
1546  *min_northing = Latitude_Band_Table[letter-4].min_northing;
1547  *northing_offset = Latitude_Band_Table[letter-4].northing_offset;
1548  }
1549  else
1551 }
1552 
1553 
1554 bool MGRS::inLatitudeRange( long letter, double latitude, double border )
1555 {
1556 /*
1557  * The function getLatitudeRange receives a latitude band letter
1558  * and uses the Latitude_Band_Table to determine the latitude band
1559  * boundaries for that latitude band letter.
1560  *
1561  * letter : Latitude band letter (input)
1562  * north : Northern latitude boundary for that letter (output)
1563  * north : Southern latitude boundary for that letter (output)
1564  */
1565  bool result = false;
1566  double north;
1567  double south;
1568 
1569  if ((letter >= LETTER_C) && (letter <= LETTER_H))
1570  {
1571  north = Latitude_Band_Table[letter-2].north * PI_OVER_180;
1572  south = Latitude_Band_Table[letter-2].south * PI_OVER_180;
1573  }
1574  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
1575  {
1576  north = Latitude_Band_Table[letter-3].north * PI_OVER_180;
1577  south = Latitude_Band_Table[letter-3].south * PI_OVER_180;
1578  }
1579  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
1580  {
1581  north = Latitude_Band_Table[letter-4].north * PI_OVER_180;
1582  south = Latitude_Band_Table[letter-4].south * PI_OVER_180;
1583  }
1584  else
1586 
1587  if( ((south - border) <= latitude) && (latitude <= (north + border)) )
1588  result = true;
1589 
1590  return result;
1591 }
1592 
1593 
1594 void MGRS::getLatitudeLetter( double latitude, int* letter )
1595 {
1596 /*
1597  * The function getLatitudeLetter receives a latitude value
1598  * and uses the Latitude_Band_Table to determine the latitude band
1599  * letter for that latitude.
1600  *
1601  * latitude : Latitude (input)
1602  * letter : Latitude band letter (output)
1603  */
1604 
1605  long band = 0;
1606 
1607  if (latitude >= _72 && latitude < _84_5)
1608  *letter = LETTER_X;
1609  else if (latitude > -_80_5 && latitude < _72)
1610  {
1611  band = (long)(((latitude + _80) / _8) + 1.0e-12);
1612  if(band < 0)
1613  band = 0;
1614  *letter = Latitude_Band_Table[band].letter;
1615  }
1616  else
1618 }
1619 
1620 // CLASSIFICATION: UNCLASSIFIED