~ubuntu-branches/ubuntu/wily/geotranz/wily

« back to all changes in this revision

Viewing changes to .pc/002-gxx-4-changes.patch/CCS/src/dtcc/CoordinateSystems/usng/USNG.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Roberto Lumbreras
  • Date: 2011-03-06 21:06:09 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20110306210609-tsvzx88vdmpgc7u4
Tags: 3.1-1
* New upstream version.
* Added debian/make-orig-tar-bz2 to repackage the upstream .tgz easily.
* Dropped 005-openjdk-forms.patch, it didn't work well in all systems.
* Renamed libgeotranz3 to libgeotranz3.1 because of ABI changes.
* Added symbols64 file for alpha, s390 and 64bit architectures.
  (Closes: #609504)

Show diffs side-by-side

added added

removed removed

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