UNCLASSIFIED

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