~ubuntu-branches/ubuntu/raring/geotranz/raring

« back to all changes in this revision

Viewing changes to dt_cc/usng/usng.c

  • Committer: Bazaar Package Importer
  • Author(s): Roberto Lumbreras
  • Date: 2008-10-17 14:43:09 UTC
  • Revision ID: james.westby@ubuntu.com-20081017144309-jb7uzfi1y1lvez8j
Tags: upstream-2.4.2
ImportĀ upstreamĀ versionĀ 2.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************/
 
2
/* RSC IDENTIFIER:  USNG
 
3
 *
 
4
 * ABSTRACT
 
5
 *
 
6
 *    This component converts between geodetic coordinates (latitude and
 
7
 *    longitude) and United States National Grid (USNG) coordinates.
 
8
 *
 
9
 * ERROR HANDLING
 
10
 *
 
11
 *    This component checks parameters for valid values.  If an invalid value
 
12
 *    is found, the error code is combined with the current error code using
 
13
 *    the bitwise or.  This combining allows multiple error codes to be
 
14
 *    returned. The possible error codes are:
 
15
 *
 
16
 *          USNG_NO_ERROR          : No errors occurred in function
 
17
 *          USNG_LAT_ERROR         : Latitude outside of valid range
 
18
 *                                    (-90 to 90 degrees)
 
19
 *          USNG_LON_ERROR         : Longitude outside of valid range
 
20
 *                                    (-180 to 360 degrees)
 
21
 *          USNG_STR_ERROR         : An USNG string error: string too long,
 
22
 *                                    too short, or badly formed
 
23
 *          USNG_PRECISION_ERROR   : The precision must be between 0 and 5
 
24
 *                                    inclusive.
 
25
 *          USNG_A_ERROR           : Semi-major axis less than or equal to zero
 
26
 *          USNG_INV_F_ERROR       : Inverse flattening outside of valid range
 
27
 *                                    (250 to 350)
 
28
 *          USNG_EASTING_ERROR     : Easting outside of valid range
 
29
 *                                    (100,000 to 900,000 meters for UTM)
 
30
 *                                    (0 to 4,000,000 meters for UPS)
 
31
 *          USNG_NORTHING_ERROR    : Northing outside of valid range
 
32
 *                                    (0 to 10,000,000 meters for UTM)
 
33
 *                                    (0 to 4,000,000 meters for UPS)
 
34
 *          USNG_ZONE_ERROR        : Zone outside of valid range (1 to 60)
 
35
 *          USNG_HEMISPHERE_ERROR  : Invalid hemisphere ('N' or 'S')
 
36
 *
 
37
 * REUSE NOTES
 
38
 *
 
39
 *    USNG is intended for reuse by any application that does conversions
 
40
 *    between geodetic coordinates and USNG coordinates.
 
41
 *
 
42
 * REFERENCES
 
43
 *
 
44
 *    Further information on USNG can be found in the Reuse Manual.
 
45
 *
 
46
 *    USNG originated from : Federal Geographic Data Committee
 
47
 *                           590 National Center
 
48
 *                           12201 Sunrise Valley Drive
 
49
 *                           Reston, VA  22092
 
50
 *
 
51
 * LICENSES
 
52
 *
 
53
 *    None apply to this component.
 
54
 *
 
55
 * RESTRICTIONS
 
56
 *
 
57
 *
 
58
 * ENVIRONMENT
 
59
 *
 
60
 *    USNG was tested and certified in the following environments:
 
61
 *
 
62
 *    1. Solaris 2.5 with GCC version 2.8.1
 
63
 *    2. Windows XP with MS Visual C++ version 6
 
64
 *
 
65
 * MODIFICATIONS
 
66
 *
 
67
 *    Date              Description
 
68
 *    ----              -----------
 
69
 *    06-05-06          Original Code (cloned from MGRS)
 
70
 */
 
71
 
 
72
 
 
73
/***************************************************************************/
 
74
/*
 
75
 *                               INCLUDES
 
76
 */
 
77
#include <ctype.h>
 
78
#include <math.h>
 
79
#include <stdio.h>
 
80
#include <string.h>
 
81
#include "ups.h"
 
82
#include "utm.h"
 
83
#include "usng.h"
 
84
 
 
85
/*
 
86
 *      ctype.h     - Standard C character handling library
 
87
 *      math.h      - Standard C math library
 
88
 *      stdio.h     - Standard C input/output library
 
89
 *      string.h    - Standard C string handling library
 
90
 *      ups.h       - Universal Polar Stereographic (UPS) projection
 
91
 *      utm.h       - Universal Transverse Mercator (UTM) projection
 
92
 *      usng.h      - function prototype error checking
 
93
 */
 
94
 
 
95
 
 
96
/***************************************************************************/
 
97
/*
 
98
 *                              GLOBAL DECLARATIONS
 
99
 */
 
100
#define DEG_TO_RAD       0.017453292519943295 /* PI/180                      */
 
101
#define RAD_TO_DEG       57.29577951308232087 /* 180/PI                      */
 
102
#define LETTER_A               0   /* ARRAY INDEX FOR LETTER A               */
 
103
#define LETTER_B               1   /* ARRAY INDEX FOR LETTER B               */
 
104
#define LETTER_C               2   /* ARRAY INDEX FOR LETTER C               */
 
105
#define LETTER_D               3   /* ARRAY INDEX FOR LETTER D               */
 
106
#define LETTER_E               4   /* ARRAY INDEX FOR LETTER E               */
 
107
#define LETTER_F               5   /* ARRAY INDEX FOR LETTER F               */
 
108
#define LETTER_G               6   /* ARRAY INDEX FOR LETTER G               */
 
109
#define LETTER_H               7   /* ARRAY INDEX FOR LETTER H               */
 
110
#define LETTER_I               8   /* ARRAY INDEX FOR LETTER I               */
 
111
#define LETTER_J               9   /* ARRAY INDEX FOR LETTER J               */
 
112
#define LETTER_K              10   /* ARRAY INDEX FOR LETTER K               */
 
113
#define LETTER_L              11   /* ARRAY INDEX FOR LETTER L               */
 
114
#define LETTER_M              12   /* ARRAY INDEX FOR LETTER M               */
 
115
#define LETTER_N              13   /* ARRAY INDEX FOR LETTER N               */
 
116
#define LETTER_O              14   /* ARRAY INDEX FOR LETTER O               */
 
117
#define LETTER_P              15   /* ARRAY INDEX FOR LETTER P               */
 
118
#define LETTER_Q              16   /* ARRAY INDEX FOR LETTER Q               */
 
119
#define LETTER_R              17   /* ARRAY INDEX FOR LETTER R               */
 
120
#define LETTER_S              18   /* ARRAY INDEX FOR LETTER S               */
 
121
#define LETTER_T              19   /* ARRAY INDEX FOR LETTER T               */
 
122
#define LETTER_U              20   /* ARRAY INDEX FOR LETTER U               */
 
123
#define LETTER_V              21   /* ARRAY INDEX FOR LETTER V               */
 
124
#define LETTER_W              22   /* ARRAY INDEX FOR LETTER W               */
 
125
#define LETTER_X              23   /* ARRAY INDEX FOR LETTER X               */
 
126
#define LETTER_Y              24   /* ARRAY INDEX FOR LETTER Y               */
 
127
#define LETTER_Z              25   /* ARRAY INDEX FOR LETTER Z               */
 
128
#define USNG_LETTERS            3  /* NUMBER OF LETTERS IN USNG              */
 
129
#define ONEHT          100000.e0    /* ONE HUNDRED THOUSAND                  */
 
130
#define TWOMIL        2000000.e0    /* TWO MILLION                           */
 
131
#define TRUE                      1  /* CONSTANT VALUE FOR TRUE VALUE  */
 
132
#define FALSE                     0  /* CONSTANT VALUE FOR FALSE VALUE */
 
133
#define PI    3.14159265358979323e0  /* PI                             */
 
134
#define PI_OVER_2  (PI / 2.0e0)
 
135
 
 
136
#define MIN_EASTING  100000
 
137
#define MAX_EASTING  900000
 
138
#define MIN_NORTHING 0
 
139
#define MAX_NORTHING 10000000
 
140
#define MAX_PRECISION           5   /* Maximum precision of easting & northing */
 
141
#define MIN_UTM_LAT      ( (-80 * PI) / 180.0 ) /* -80 degrees in radians    */
 
142
#define MAX_UTM_LAT      ( (84 * PI) / 180.0 )  /* 84 degrees in radians     */
 
143
 
 
144
#define MIN_EAST_NORTH 0
 
145
#define MAX_EAST_NORTH 4000000
 
146
 
 
147
 
 
148
/* Ellipsoid parameters, default to WGS 84 */
 
149
double USNG_a = 6378137.0;    /* Semi-major axis of ellipsoid in meters */
 
150
double USNG_f = 1 / 298.257223563; /* Flattening of ellipsoid           */
 
151
double USNG_recpf = 298.257223563;
 
152
char   USNG_Ellipsoid_Code[3] = {'W','E',0};
 
153
 
 
154
 
 
155
typedef struct Latitude_Band_Value
 
156
{
 
157
  long letter;            /* letter representing latitude band  */
 
158
  double min_northing;    /* minimum northing for latitude band */
 
159
  double north;           /* upper latitude for latitude band   */
 
160
  double south;           /* lower latitude for latitude band   */
 
161
  double northing_offset; /* latitude band northing offset      */
 
162
} Latitude_Band;
 
163
 
 
164
static const Latitude_Band Latitude_Band_Table[20] =
 
165
  {{LETTER_C, 1100000.0, -72.0, -80.5, 0.0}, 
 
166
  {LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0},
 
167
  {LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0},
 
168
  {LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0},
 
169
  {LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0},
 
170
  {LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0},
 
171
  {LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0},
 
172
  {LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0},
 
173
  {LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0},
 
174
  {LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0},
 
175
  {LETTER_N, 0.0, 8.0, 0.0, 0.0},
 
176
  {LETTER_P, 800000.0, 16.0, 8.0, 0.0},
 
177
  {LETTER_Q, 1700000.0, 24.0, 16.0, 0.0},
 
178
  {LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0},
 
179
  {LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0},
 
180
  {LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0},
 
181
  {LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0},
 
182
  {LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0},
 
183
  {LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0},
 
184
  {LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}};
 
185
 
 
186
 
 
187
typedef struct UPS_Constant_Value
 
188
{
 
189
  long letter;            /* letter representing latitude band      */
 
190
  long ltr2_low_value;    /* 2nd letter range - low number         */
 
191
  long ltr2_high_value;   /* 2nd letter range - high number          */
 
192
  long ltr3_high_value;   /* 3rd letter range - high number (UPS)   */
 
193
  double false_easting;   /* False easting based on 2nd letter      */
 
194
  double false_northing;  /* False northing based on 3rd letter     */
 
195
} UPS_Constant;
 
196
 
 
197
static const UPS_Constant UPS_Constant_Table[4] =
 
198
  {{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0},
 
199
  {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0},
 
200
  {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0},
 
201
  {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}};
 
202
 
 
203
/***************************************************************************/
 
204
/*
 
205
 *                              FUNCTIONS
 
206
 */
 
207
 
 
208
long USNG_Get_Latitude_Band_Min_Northing(long letter, double* min_northing, double* northing_offset)
 
209
/*
 
210
 * The function USNG_Get_Latitude_Band_Min_Northing receives a latitude band letter
 
211
 * and uses the Latitude_Band_Table to determine the minimum northing and northing offset
 
212
 * for that latitude band letter.
 
213
 *
 
214
 *   letter        : Latitude band letter             (input)
 
215
 *   min_northing  : Minimum northing for that letter (output)
 
216
 */
 
217
{ /* USNG_Get_Latitude_Band_Min_Northing */
 
218
  long error_code = USNG_NO_ERROR;
 
219
 
 
220
  if ((letter >= LETTER_C) && (letter <= LETTER_H))
 
221
  {
 
222
    *min_northing = Latitude_Band_Table[letter-2].min_northing;
 
223
    *northing_offset = Latitude_Band_Table[letter-2].northing_offset;
 
224
  }
 
225
  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
 
226
  {
 
227
    *min_northing = Latitude_Band_Table[letter-3].min_northing;
 
228
    *northing_offset = Latitude_Band_Table[letter-3].northing_offset;
 
229
  }
 
230
  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
 
231
  {
 
232
    *min_northing = Latitude_Band_Table[letter-4].min_northing;
 
233
    *northing_offset = Latitude_Band_Table[letter-4].northing_offset;
 
234
  }
 
235
  else
 
236
    error_code |= USNG_STRING_ERROR;
 
237
 
 
238
  return error_code;
 
239
} /* USNG_Get_Latitude_Band_Min_Northing */
 
240
 
 
241
 
 
242
long USNG_Get_Latitude_Range(long letter, double* north, double* south)
 
243
/*
 
244
 * The function USNG_Get_Latitude_Range receives a latitude band letter
 
245
 * and uses the Latitude_Band_Table to determine the latitude band
 
246
 * boundaries for that latitude band letter.
 
247
 *
 
248
 *   letter   : Latitude band letter                        (input)
 
249
 *   north    : Northern latitude boundary for that letter  (output)
 
250
 *   north    : Southern latitude boundary for that letter  (output)
 
251
 */
 
252
{ /* USNG_Get_Latitude_Range */
 
253
  long error_code = USNG_NO_ERROR;
 
254
 
 
255
  if ((letter >= LETTER_C) && (letter <= LETTER_H))
 
256
  {
 
257
    *north = Latitude_Band_Table[letter-2].north * DEG_TO_RAD;
 
258
    *south = Latitude_Band_Table[letter-2].south * DEG_TO_RAD;
 
259
  }
 
260
  else if ((letter >= LETTER_J) && (letter <= LETTER_N))
 
261
  {
 
262
    *north = Latitude_Band_Table[letter-3].north * DEG_TO_RAD;
 
263
    *south = Latitude_Band_Table[letter-3].south * DEG_TO_RAD;
 
264
  }
 
265
  else if ((letter >= LETTER_P) && (letter <= LETTER_X))
 
266
  {
 
267
    *north = Latitude_Band_Table[letter-4].north * DEG_TO_RAD;
 
268
    *south = Latitude_Band_Table[letter-4].south * DEG_TO_RAD;
 
269
  }
 
270
  else
 
271
    error_code |= USNG_STRING_ERROR;
 
272
 
 
273
  return error_code;
 
274
} /* USNG_Get_Latitude_Range */
 
275
 
 
276
 
 
277
long USNG_Get_Latitude_Letter(double latitude, int* letter)
 
278
/*
 
279
 * The function USNG_Get_Latitude_Letter receives a latitude value
 
280
 * and uses the Latitude_Band_Table to determine the latitude band
 
281
 * letter for that latitude.
 
282
 *
 
283
 *   latitude   : Latitude              (input)
 
284
 *   letter     : Latitude band letter  (output)
 
285
 */
 
286
{ /* USNG_Get_Latitude_Letter */
 
287
  double temp = 0.0;
 
288
  long error_code = USNG_NO_ERROR;
 
289
  double lat_deg = latitude * RAD_TO_DEG;
 
290
 
 
291
  if (lat_deg >= 72 && lat_deg < 84.5)
 
292
    *letter = LETTER_X;
 
293
  else if (lat_deg > -80.5 && lat_deg < 72)
 
294
  {
 
295
    temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12;
 
296
    *letter = Latitude_Band_Table[(int)temp].letter;
 
297
  }
 
298
  else
 
299
    error_code |= USNG_LAT_ERROR;
 
300
 
 
301
  return error_code;
 
302
} /* USNG_Get_Latitude_Letter */
 
303
 
 
304
 
 
305
long USNG_Check_Zone(char* USNG, long* zone_exists)
 
306
/*
 
307
 * The function USNG_Check_Zone receives a USNG coordinate string.
 
308
 * If a zone is given, TRUE is returned. Otherwise, FALSE
 
309
 * is returned.
 
310
 *
 
311
 *   USNG           : USNG coordinate string        (input)
 
312
 *   zone_exists    : TRUE if a zone is given,
 
313
 *                    FALSE if a zone is not given  (output)
 
314
 */
 
315
{ /* USNG_Check_Zone */
 
316
  int i = 0;
 
317
  int j = 0;
 
318
  int num_digits = 0;
 
319
  long error_code = USNG_NO_ERROR;
 
320
 
 
321
  /* skip any leading blanks */
 
322
  while (USNG[i] == ' ')
 
323
    i++;
 
324
  j = i;
 
325
  while (isdigit(USNG[i]))
 
326
    i++;
 
327
  num_digits = i - j;
 
328
  if (num_digits <= 2)
 
329
    if (num_digits > 0)
 
330
      *zone_exists = TRUE;
 
331
    else
 
332
      *zone_exists = FALSE;
 
333
  else
 
334
    error_code |= USNG_STRING_ERROR;
 
335
 
 
336
  return error_code;
 
337
} /* USNG_Check_Zone */
 
338
 
 
339
 
 
340
long Make_USNG_String (char* USNG,
 
341
                       long Zone,
 
342
                       int Letters[USNG_LETTERS],
 
343
                       double Easting,
 
344
                       double Northing,
 
345
                       long Precision)
 
346
/*
 
347
 * The function Make_USNG_String constructs a USNG string
 
348
 * from its component parts.
 
349
 *
 
350
 *   USNG           : USNG coordinate string          (output)
 
351
 *   Zone           : UTM Zone                        (input)
 
352
 *   Letters        : USNG coordinate string letters  (input)
 
353
 *   Easting        : Easting value                   (input)
 
354
 *   Northing       : Northing value                  (input)
 
355
 *   Precision      : Precision level of USNG string  (input)
 
356
 */
 
357
{ /* Make_USNG_String */
 
358
  long i;
 
359
  long j;
 
360
  double divisor;
 
361
  long east;
 
362
  long north;
 
363
  char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
364
  long error_code = USNG_NO_ERROR;
 
365
 
 
366
  i = 0;
 
367
  if (Zone)
 
368
    i = sprintf (USNG+i,"%2.2ld",Zone);
 
369
  else
 
370
    strncpy(USNG, "  ", 2);  // 2 spaces
 
371
 
 
372
  for (j=0;j<3;j++)
 
373
    USNG[i++] = alphabet[Letters[j]];
 
374
  divisor = pow (10.0, (5 - Precision));
 
375
  Easting = fmod (Easting, 100000.0);
 
376
  if (Easting >= 99999.5)
 
377
    Easting = 99999.0;
 
378
  east = (long)(Easting/divisor);
 
379
  i += sprintf (USNG+i, "%*.*ld", Precision, Precision, east);
 
380
  Northing = fmod (Northing, 100000.0);
 
381
  if (Northing >= 99999.5)
 
382
    Northing = 99999.0;
 
383
  north = (long)(Northing/divisor);
 
384
  i += sprintf (USNG+i, "%*.*ld", Precision, Precision, north);
 
385
  return (error_code);
 
386
} /* Make_USNG_String */
 
387
 
 
388
 
 
389
long Break_USNG_String (char* USNG,
 
390
                        long* Zone,
 
391
                        long Letters[USNG_LETTERS],
 
392
                        double* Easting,
 
393
                        double* Northing,
 
394
                        long* Precision)
 
395
/*
 
396
 * The function Break_USNG_String breaks down a USNG
 
397
 * coordinate string into its component parts.
 
398
 *
 
399
 *   USNG           : USNG coordinate string          (input)
 
400
 *   Zone           : UTM Zone                        (output)
 
401
 *   Letters        : USNG coordinate string letters  (output)
 
402
 *   Easting        : Easting value                   (output)
 
403
 *   Northing       : Northing value                  (output)
 
404
 *   Precision      : Precision level of USNG string  (output)
 
405
 */
 
406
{ /* Break_USNG_String */
 
407
  long num_digits;
 
408
  long num_letters;
 
409
  long i = 0;
 
410
  long j = 0;
 
411
  long error_code = USNG_NO_ERROR;
 
412
 
 
413
  while (USNG[i] == ' ')
 
414
    i++;  /* skip any leading blanks */
 
415
  j = i;
 
416
  while (isdigit(USNG[i]))
 
417
    i++;
 
418
  num_digits = i - j;
 
419
  if (num_digits <= 2)
 
420
    if (num_digits > 0)
 
421
    {
 
422
      char zone_string[3];
 
423
      /* get zone */
 
424
      strncpy (zone_string, USNG+j, 2);
 
425
      zone_string[2] = 0;
 
426
      sscanf (zone_string, "%ld", Zone);
 
427
      if ((*Zone < 1) || (*Zone > 60))
 
428
        error_code |= USNG_STRING_ERROR;
 
429
    }
 
430
    else
 
431
      *Zone = 0;
 
432
  else
 
433
    error_code |= USNG_STRING_ERROR;
 
434
  j = i;
 
435
 
 
436
  while (isalpha(USNG[i]))
 
437
    i++;
 
438
  num_letters = i - j;
 
439
  if (num_letters == 3)
 
440
  {
 
441
    /* get letters */
 
442
    Letters[0] = (toupper(USNG[j]) - (long)'A');
 
443
    if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O))
 
444
      error_code |= USNG_STRING_ERROR;
 
445
    Letters[1] = (toupper(USNG[j+1]) - (long)'A');
 
446
    if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O))
 
447
      error_code |= USNG_STRING_ERROR;
 
448
    Letters[2] = (toupper(USNG[j+2]) - (long)'A');
 
449
    if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O))
 
450
      error_code |= USNG_STRING_ERROR;
 
451
  }
 
452
  else
 
453
    error_code |= USNG_STRING_ERROR;
 
454
  j = i;
 
455
  while (isdigit(USNG[i]))
 
456
    i++;
 
457
  num_digits = i - j;
 
458
  if ((num_digits <= 10) && (num_digits%2 == 0))
 
459
  {
 
460
    long n;
 
461
    char east_string[6];
 
462
    char north_string[6];
 
463
    long east;
 
464
    long north;
 
465
    double multiplier;
 
466
    /* get easting & northing */
 
467
    n = num_digits/2;
 
468
    *Precision = n;
 
469
    if (n > 0)
 
470
    {
 
471
      strncpy (east_string, USNG+j, n);
 
472
      east_string[n] = 0;
 
473
      sscanf (east_string, "%ld", &east);
 
474
      strncpy (north_string, USNG+j+n, n);
 
475
      north_string[n] = 0;
 
476
      sscanf (north_string, "%ld", &north);
 
477
      multiplier = pow (10.0, 5 - n);
 
478
      *Easting = east * multiplier;
 
479
      *Northing = north * multiplier;
 
480
    }
 
481
    else
 
482
    {
 
483
      *Easting = 0.0;
 
484
      *Northing = 0.0;
 
485
    }
 
486
  }
 
487
  else
 
488
    error_code |= USNG_STRING_ERROR;
 
489
 
 
490
  return (error_code);
 
491
} /* Break_USNG_String */
 
492
 
 
493
 
 
494
void USNG_Get_Grid_Values (long zone,
 
495
                           long* ltr2_low_value,
 
496
                           long* ltr2_high_value,
 
497
                           double *pattern_offset)
 
498
/*
 
499
 * The function USNG_Get_Grid_Values sets the letter range used for
 
500
 * the 2nd letter in the USNG coordinate string, based on the set
 
501
 * number of the utm zone. It also sets the pattern offset using a
 
502
 * value of A for the second letter of the grid square, based on
 
503
 * the grid pattern and set number of the utm zone.
 
504
 *
 
505
 *    zone            : Zone number             (input)
 
506
 *    ltr2_low_value  : 2nd letter low number   (output)
 
507
 *    ltr2_high_value : 2nd letter high number  (output)
 
508
 *    pattern_offset  : Pattern offset          (output)
 
509
 */
 
510
{ /* BEGIN USNG_Get_Grid_Values */
 
511
  long set_number;    /* Set number (1-6) based on UTM zone number */
 
512
 
 
513
  set_number = zone % 6;
 
514
 
 
515
  if (!set_number)
 
516
    set_number = 6;
 
517
 
 
518
  if ((set_number == 1) || (set_number == 4))
 
519
  {
 
520
    *ltr2_low_value = LETTER_A;
 
521
    *ltr2_high_value = LETTER_H;
 
522
  }
 
523
  else if ((set_number == 2) || (set_number == 5))
 
524
  {
 
525
    *ltr2_low_value = LETTER_J;
 
526
    *ltr2_high_value = LETTER_R;
 
527
  }
 
528
  else if ((set_number == 3) || (set_number == 6))
 
529
  {
 
530
    *ltr2_low_value = LETTER_S;
 
531
    *ltr2_high_value = LETTER_Z;
 
532
  }
 
533
 
 
534
  /* False northing at A for second letter of grid square */
 
535
  if ((set_number % 2) ==  0)
 
536
    *pattern_offset = 500000.0;
 
537
  else
 
538
    *pattern_offset = 0.0;
 
539
 
 
540
} /* END OF USNG_Get_Grid_Values */
 
541
 
 
542
 
 
543
long UTM_To_USNG (long Zone,
 
544
                  double Latitude,
 
545
                  double Easting,
 
546
                  double Northing,
 
547
                  long Precision,
 
548
                  char *USNG)
 
549
/*
 
550
 * The function UTM_To_USNG calculates a USNG coordinate string
 
551
 * based on the zone, latitude, easting and northing.
 
552
 *
 
553
 *    Zone      : Zone number             (input)
 
554
 *    Latitude  : Latitude in radians     (input)
 
555
 *    Easting   : Easting                 (input)
 
556
 *    Northing  : Northing                (input)
 
557
 *    Precision : Precision               (input)
 
558
 *    USNG      : USNG coordinate string  (output)
 
559
 */
 
560
{ /* BEGIN UTM_To_USNG */
 
561
  double pattern_offset;      /* Pattern offset for 3rd letter               */
 
562
  double grid_northing;       /* Northing used to derive 3rd letter of USNG  */
 
563
  long ltr2_low_value;        /* 2nd letter range - low number               */
 
564
  long ltr2_high_value;       /* 2nd letter range - high number              */
 
565
  int letters[USNG_LETTERS];  /* Number location of 3 letters in alphabet    */
 
566
  double divisor;
 
567
  long error_code = USNG_NO_ERROR;
 
568
 
 
569
  /* Round easting and northing values */
 
570
  divisor = pow (10.0, (5 - Precision));
 
571
  Easting = (long)(Easting/divisor) * divisor;
 
572
  Northing = (long)(Northing/divisor) * divisor;
 
573
 
 
574
  if( Latitude <= 0.0 && Northing == 1.0e7)
 
575
  {
 
576
    Latitude = 0.0;
 
577
    Northing = 0.0;
 
578
  }
 
579
 
 
580
  USNG_Get_Grid_Values(Zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
 
581
 
 
582
  error_code = USNG_Get_Latitude_Letter(Latitude, &letters[0]);
 
583
 
 
584
  if (!error_code)
 
585
  {
 
586
    grid_northing = Northing;
 
587
 
 
588
    while (grid_northing >= TWOMIL)
 
589
    {
 
590
      grid_northing = grid_northing - TWOMIL;
 
591
    }
 
592
    grid_northing = grid_northing + pattern_offset;
 
593
    if(grid_northing >= TWOMIL)
 
594
      grid_northing = grid_northing - TWOMIL;
 
595
 
 
596
    letters[2] = (long)(grid_northing / ONEHT);
 
597
    if (letters[2] > LETTER_H)
 
598
      letters[2] = letters[2] + 1;
 
599
 
 
600
    if (letters[2] > LETTER_N)
 
601
      letters[2] = letters[2] + 1;
 
602
 
 
603
    letters[1] = ltr2_low_value + ((long)(Easting / ONEHT) -1);
 
604
    if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
 
605
      letters[1] = letters[1] + 1;
 
606
 
 
607
    Make_USNG_String (USNG, Zone, letters, Easting, Northing, Precision);
 
608
  }
 
609
  return error_code;
 
610
} /* END UTM_To_USNG */
 
611
 
 
612
 
 
613
long Set_USNG_Parameters (double a,
 
614
                          double f,
 
615
                          char   *Ellipsoid_Code)
 
616
/*
 
617
 * The function SET_USNG_PARAMETERS receives the ellipsoid parameters and sets
 
618
 * the corresponding state variables. If any errors occur, the error code(s)
 
619
 * are returned by the function, otherwise USNG_NO_ERROR is returned.
 
620
 *
 
621
 *   a                : Semi-major axis of ellipsoid in meters  (input)
 
622
 *   f                : Flattening of ellipsoid                 (input)
 
623
 *   Ellipsoid_Code   : 2-letter code for ellipsoid             (input)
 
624
 */
 
625
{ /* Set_USNG_Parameters  */
 
626
 
 
627
  double inv_f = 1 / f;
 
628
  long Error_Code = USNG_NO_ERROR;
 
629
 
 
630
  if (a <= 0.0)
 
631
  { /* Semi-major axis must be greater than zero */
 
632
    Error_Code |= USNG_A_ERROR;
 
633
  }
 
634
  if ((inv_f < 250) || (inv_f > 350))
 
635
  { /* Inverse flattening must be between 250 and 350 */
 
636
    Error_Code |= USNG_INV_F_ERROR;
 
637
  }
 
638
  if (!Error_Code)
 
639
  { /* no errors */
 
640
    USNG_a = a;
 
641
    USNG_f = f;
 
642
    USNG_recpf = inv_f;
 
643
    strcpy (USNG_Ellipsoid_Code, Ellipsoid_Code);
 
644
  }
 
645
  return (Error_Code);
 
646
}  /* Set_USNG_Parameters  */
 
647
 
 
648
 
 
649
void Get_USNG_Parameters (double *a,
 
650
                          double *f,
 
651
                          char* Ellipsoid_Code)
 
652
/*
 
653
 * The function Get_USNG_Parameters returns the current ellipsoid
 
654
 * parameters.
 
655
 *
 
656
 *  a                : Semi-major axis of ellipsoid, in meters (output)
 
657
 *  f                : Flattening of ellipsoid                 (output)
 
658
 *  Ellipsoid_Code   : 2-letter code for ellipsoid             (output)
 
659
 */
 
660
{ /* Get_USNG_Parameters */
 
661
  *a = USNG_a;
 
662
  *f = USNG_f;
 
663
  strcpy (Ellipsoid_Code, USNG_Ellipsoid_Code);
 
664
  return;
 
665
} /* Get_USNG_Parameters */
 
666
 
 
667
 
 
668
long Convert_Geodetic_To_USNG (double Latitude,
 
669
                               double Longitude,
 
670
                               long Precision,
 
671
                               char* USNG)
 
672
/*
 
673
 * The function Convert_Geodetic_To_USNG converts Geodetic (latitude and
 
674
 * longitude) coordinates to a USNG coordinate string, according to the
 
675
 * current ellipsoid parameters.  If any errors occur, the error code(s)
 
676
 * are returned by the function, otherwise USNG_NO_ERROR is returned.
 
677
 *
 
678
 *    Latitude   : Latitude in radians              (input)
 
679
 *    Longitude  : Longitude in radians             (input)
 
680
 *    Precision  : Precision level of USNG string   (input)
 
681
 *    USNG       : USNG coordinate string           (output)
 
682
 *
 
683
 */
 
684
{ /* Convert_Geodetic_To_USNG */
 
685
  long zone;
 
686
  char hemisphere;
 
687
  double easting;
 
688
  double northing;
 
689
  long temp_error_code = USNG_NO_ERROR;
 
690
  long error_code = USNG_NO_ERROR;
 
691
 
 
692
  if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
 
693
  { /* Latitude out of range */
 
694
    error_code |= USNG_LAT_ERROR;
 
695
  }
 
696
  if ((Longitude < -PI) || (Longitude > (2*PI)))
 
697
  { /* Longitude out of range */
 
698
    error_code |= USNG_LON_ERROR;
 
699
  }
 
700
  if ((Precision < 0) || (Precision > MAX_PRECISION))
 
701
    error_code |= USNG_PRECISION_ERROR;
 
702
  if (!error_code)
 
703
  {
 
704
    if ((Latitude < MIN_UTM_LAT) || (Latitude > MAX_UTM_LAT))
 
705
    {
 
706
      temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f);
 
707
      if(!temp_error_code)
 
708
      {
 
709
        temp_error_code |= Convert_Geodetic_To_UPS (Latitude, Longitude, &hemisphere, &easting, &northing);
 
710
        if(!temp_error_code)
 
711
          error_code |= Convert_UPS_To_USNG (hemisphere, easting, northing, Precision, USNG);
 
712
        else
 
713
        {
 
714
          if(temp_error_code & UPS_LAT_ERROR)
 
715
            error_code |= USNG_LAT_ERROR;
 
716
          if(temp_error_code & UPS_LON_ERROR)
 
717
            error_code |= USNG_LON_ERROR;
 
718
        }
 
719
      }
 
720
      else
 
721
      {
 
722
        if(temp_error_code & UPS_A_ERROR)
 
723
          error_code |= USNG_A_ERROR;
 
724
        if(temp_error_code & UPS_INV_F_ERROR)
 
725
          error_code |= USNG_INV_F_ERROR;
 
726
      }
 
727
    }
 
728
    else
 
729
    {
 
730
      temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0);
 
731
      if(!temp_error_code)
 
732
      {
 
733
        temp_error_code |= Convert_Geodetic_To_UTM (Latitude, Longitude, &zone, &hemisphere, &easting, &northing);
 
734
        if(!temp_error_code)
 
735
          error_code |= UTM_To_USNG (zone, Latitude, easting, northing, Precision, USNG);
 
736
        else
 
737
        {
 
738
          if(temp_error_code & UTM_LAT_ERROR)
 
739
            error_code |= USNG_LAT_ERROR;
 
740
          if(temp_error_code & UTM_LON_ERROR)
 
741
            error_code |= USNG_LON_ERROR;
 
742
          if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
 
743
            error_code |= USNG_ZONE_ERROR;
 
744
          if(temp_error_code & UTM_EASTING_ERROR)
 
745
            error_code |= USNG_EASTING_ERROR;
 
746
          if(temp_error_code & UTM_NORTHING_ERROR)
 
747
            error_code |= USNG_NORTHING_ERROR;
 
748
        }
 
749
      }
 
750
      else
 
751
      {
 
752
        if(temp_error_code & UTM_A_ERROR)
 
753
          error_code |= USNG_A_ERROR;
 
754
        if(temp_error_code & UTM_INV_F_ERROR)
 
755
          error_code |= USNG_INV_F_ERROR;
 
756
        if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
 
757
          error_code |= USNG_ZONE_ERROR;
 
758
      }
 
759
    }
 
760
  }
 
761
  return (error_code);
 
762
} /* Convert_Geodetic_To_USNG */
 
763
 
 
764
 
 
765
long Convert_USNG_To_Geodetic (char* USNG,
 
766
                               double *Latitude,
 
767
                               double *Longitude)
 
768
/*
 
769
 * The function Convert_USNG_To_Geodetic converts a USNG coordinate string
 
770
 * to Geodetic (latitude and longitude) coordinates
 
771
 * according to the current ellipsoid parameters.  If any errors occur,
 
772
 * the error code(s) are returned by the function, otherwise UTM_NO_ERROR
 
773
 * is returned.
 
774
 *
 
775
 *    USNG       : USNG coordinate string           (input)
 
776
 *    Latitude   : Latitude in radians              (output)
 
777
 *    Longitude  : Longitude in radians             (output)
 
778
 *
 
779
 */
 
780
{ /* Convert_USNG_To_Geodetic */
 
781
  long zone;
 
782
  char hemisphere;
 
783
  double easting;
 
784
  double northing;
 
785
  long zone_exists;
 
786
  long temp_error_code = USNG_NO_ERROR;
 
787
  long error_code = USNG_NO_ERROR;
 
788
 
 
789
  error_code = USNG_Check_Zone(USNG, &zone_exists);
 
790
  if (!error_code)
 
791
  {
 
792
    if (zone_exists)
 
793
    {
 
794
      error_code |= Convert_USNG_To_UTM (USNG, &zone, &hemisphere, &easting, &northing);
 
795
      if(!error_code || (error_code & USNG_LAT_WARNING))
 
796
      {
 
797
        temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0);
 
798
        if(!temp_error_code)
 
799
        {
 
800
          temp_error_code |= Convert_UTM_To_Geodetic (zone, hemisphere, easting, northing, Latitude, Longitude);
 
801
          if(temp_error_code)
 
802
          {
 
803
            if((temp_error_code & UTM_ZONE_ERROR) || (temp_error_code & UTM_HEMISPHERE_ERROR))
 
804
              error_code |= USNG_STRING_ERROR;
 
805
            if(temp_error_code & UTM_EASTING_ERROR)
 
806
              error_code |= USNG_EASTING_ERROR;
 
807
            if(temp_error_code & UTM_NORTHING_ERROR)
 
808
              error_code |= USNG_NORTHING_ERROR;
 
809
          }
 
810
        }
 
811
        else
 
812
        {
 
813
          if(temp_error_code & UTM_A_ERROR)
 
814
            error_code |= USNG_A_ERROR;
 
815
          if(temp_error_code & UTM_INV_F_ERROR)
 
816
            error_code |= USNG_INV_F_ERROR;
 
817
          if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
 
818
            error_code |= USNG_ZONE_ERROR;
 
819
        }
 
820
      }
 
821
    }
 
822
    else
 
823
    {
 
824
      error_code |= Convert_USNG_To_UPS (USNG, &hemisphere, &easting, &northing);
 
825
      if(!error_code)
 
826
      {
 
827
        temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f);
 
828
        if(!temp_error_code)
 
829
        {
 
830
          temp_error_code |= Convert_UPS_To_Geodetic (hemisphere, easting, northing, Latitude, Longitude);
 
831
          if(temp_error_code)
 
832
          {
 
833
            if(temp_error_code & UPS_HEMISPHERE_ERROR)
 
834
              error_code |= USNG_STRING_ERROR;
 
835
            if(temp_error_code & UPS_EASTING_ERROR)
 
836
              error_code |= USNG_EASTING_ERROR;
 
837
            if(temp_error_code & UPS_LAT_ERROR)
 
838
              error_code |= USNG_NORTHING_ERROR;
 
839
          }
 
840
        }
 
841
        else
 
842
        {
 
843
          if(temp_error_code & UPS_A_ERROR)
 
844
            error_code |= USNG_A_ERROR;
 
845
          if(temp_error_code & UPS_INV_F_ERROR)
 
846
            error_code |= USNG_INV_F_ERROR;
 
847
        }
 
848
      }
 
849
    }
 
850
  }
 
851
  return (error_code);
 
852
} /* END OF Convert_USNG_To_Geodetic */
 
853
 
 
854
 
 
855
long Convert_UTM_To_USNG (long Zone,
 
856
                          char Hemisphere,
 
857
                          double Easting,
 
858
                          double Northing,
 
859
                          long Precision,
 
860
                          char* USNG)
 
861
/*
 
862
 * The function Convert_UTM_To_USNG converts UTM (zone, easting, and
 
863
 * northing) coordinates to a USNG coordinate string, according to the
 
864
 * current ellipsoid parameters.  If any errors occur, the error code(s)
 
865
 * are returned by the function, otherwise USNG_NO_ERROR is returned.
 
866
 *
 
867
 *    Zone       : UTM zone                         (input)
 
868
 *    Hemisphere : North or South hemisphere        (input)
 
869
 *    Easting    : Easting (X) in meters            (input)
 
870
 *    Northing   : Northing (Y) in meters           (input)
 
871
 *    Precision  : Precision level of USNG string   (input)
 
872
 *    USNG       : USNG coordinate string           (output)
 
873
 */
 
874
{ /* Convert_UTM_To_USNG */
 
875
  double latitude;           /* Latitude of UTM point */
 
876
  double longitude;          /* Longitude of UTM point */
 
877
  long utm_error_code = USNG_NO_ERROR;
 
878
  long error_code = USNG_NO_ERROR;
 
879
 
 
880
  if ((Zone < 1) || (Zone > 60))
 
881
    error_code |= USNG_ZONE_ERROR;
 
882
  if ((Hemisphere != 'S') && (Hemisphere != 'N'))
 
883
    error_code |= USNG_HEMISPHERE_ERROR;
 
884
  if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING))
 
885
    error_code |= USNG_EASTING_ERROR;
 
886
  if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING))
 
887
    error_code |= USNG_NORTHING_ERROR;
 
888
  if ((Precision < 0) || (Precision > MAX_PRECISION))
 
889
    error_code |= USNG_PRECISION_ERROR;
 
890
  if (!error_code)
 
891
  {
 
892
    Set_UTM_Parameters (USNG_a, USNG_f, 0);
 
893
    utm_error_code = Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing, &latitude, &longitude);
 
894
 
 
895
    if(utm_error_code)
 
896
    {
 
897
      if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
 
898
        error_code |= USNG_STRING_ERROR;
 
899
      if(utm_error_code & UTM_EASTING_ERROR)
 
900
        error_code |= USNG_EASTING_ERROR;
 
901
      if(utm_error_code & UTM_NORTHING_ERROR)
 
902
        error_code |= USNG_NORTHING_ERROR;
 
903
    }
 
904
 
 
905
    error_code |= UTM_To_USNG (Zone, latitude, Easting, Northing, Precision, USNG);
 
906
  }
 
907
  return (error_code);
 
908
} /* Convert_UTM_To_USNG */
 
909
 
 
910
 
 
911
long Convert_USNG_To_UTM (char   *USNG,
 
912
                          long   *Zone,
 
913
                          char   *Hemisphere,
 
914
                          double *Easting,
 
915
                          double *Northing)
 
916
/*
 
917
 * The function Convert_USNG_To_UTM converts a USNG coordinate string
 
918
 * to UTM projection (zone, hemisphere, easting and northing) coordinates
 
919
 * according to the current ellipsoid parameters.  If any errors occur,
 
920
 * the error code(s) are returned by the function, otherwise UTM_NO_ERROR
 
921
 * is returned.
 
922
 *
 
923
 *    USNG       : USNG coordinate string           (input)
 
924
 *    Zone       : UTM zone                         (output)
 
925
 *    Hemisphere : North or South hemisphere        (output)
 
926
 *    Easting    : Easting (X) in meters            (output)
 
927
 *    Northing   : Northing (Y) in meters           (output)
 
928
 */
 
929
{ /* Convert_USNG_To_UTM */
 
930
  double min_northing;
 
931
  double northing_offset;
 
932
  long ltr2_low_value;
 
933
  long ltr2_high_value;
 
934
  double pattern_offset;
 
935
  double upper_lat_limit;     /* North latitude limits based on 1st letter  */
 
936
  double lower_lat_limit;     /* South latitude limits based on 1st letter  */
 
937
  double grid_easting;        /* Easting for 100,000 meter grid square      */
 
938
  double grid_northing;       /* Northing for 100,000 meter grid square     */
 
939
  long letters[USNG_LETTERS];
 
940
  long in_precision;
 
941
  double latitude = 0.0;
 
942
  double longitude = 0.0;
 
943
  double divisor = 1.0;
 
944
  long utm_error_code = USNG_NO_ERROR;
 
945
  long error_code = USNG_NO_ERROR;
 
946
 
 
947
  error_code = Break_USNG_String (USNG, Zone, letters, Easting, Northing, &in_precision);
 
948
  if (!*Zone)
 
949
    error_code |= USNG_STRING_ERROR;
 
950
  else
 
951
  {
 
952
    if (!error_code)
 
953
    {
 
954
      if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36)))
 
955
        error_code |= USNG_STRING_ERROR;
 
956
      else
 
957
      {
 
958
        if (letters[0] < LETTER_N)
 
959
          *Hemisphere = 'S';
 
960
        else
 
961
          *Hemisphere = 'N';
 
962
 
 
963
        USNG_Get_Grid_Values(*Zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
 
964
 
 
965
        /* Check that the second letter of the USNG string is within
 
966
         * the range of valid second letter values
 
967
         * Also check that the third letter is valid */
 
968
        if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V))
 
969
          error_code |= USNG_STRING_ERROR;
 
970
 
 
971
        if (!error_code)
 
972
        {
 
973
          double row_letter_northing = (double)(letters[2]) * ONEHT;
 
974
          grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
 
975
          if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
 
976
            grid_easting = grid_easting - ONEHT;
 
977
 
 
978
          if (letters[2] > LETTER_O)
 
979
            row_letter_northing = row_letter_northing - ONEHT;
 
980
 
 
981
          if (letters[2] > LETTER_I)
 
982
            row_letter_northing = row_letter_northing - ONEHT; 
 
983
 
 
984
          if (row_letter_northing >= TWOMIL)
 
985
            row_letter_northing = row_letter_northing - TWOMIL;
 
986
 
 
987
          error_code = USNG_Get_Latitude_Band_Min_Northing(letters[0], &min_northing, &northing_offset);
 
988
          if (!error_code)
 
989
          {
 
990
            grid_northing = row_letter_northing - pattern_offset;
 
991
            if(grid_northing < 0)
 
992
              grid_northing += TWOMIL;
 
993
            
 
994
            grid_northing += northing_offset;
 
995
 
 
996
            if(grid_northing < min_northing)
 
997
              grid_northing += TWOMIL;
 
998
 
 
999
            *Easting = grid_easting + *Easting;
 
1000
            *Northing = grid_northing + *Northing;
 
1001
 
 
1002
            /* check that point is within Zone Letter bounds */
 
1003
            utm_error_code = Set_UTM_Parameters(USNG_a, USNG_f, 0);
 
1004
            if (!utm_error_code)
 
1005
            {
 
1006
              utm_error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude);
 
1007
              if (!utm_error_code)
 
1008
              {
 
1009
                divisor = pow (10.0, in_precision);
 
1010
                error_code = USNG_Get_Latitude_Range(letters[0], &upper_lat_limit, &lower_lat_limit);
 
1011
                if (!error_code)
 
1012
                {
 
1013
                  if (!(((lower_lat_limit - DEG_TO_RAD/divisor) <= latitude) && (latitude <= (upper_lat_limit + DEG_TO_RAD/divisor))))
 
1014
                    error_code |= USNG_LAT_ERROR;
 
1015
                }
 
1016
              }
 
1017
              else
 
1018
              {
 
1019
                if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
 
1020
                  error_code |= USNG_STRING_ERROR;
 
1021
                if(utm_error_code & UTM_EASTING_ERROR)
 
1022
                  error_code |= USNG_EASTING_ERROR;
 
1023
                if(utm_error_code & UTM_NORTHING_ERROR)
 
1024
                  error_code |= USNG_NORTHING_ERROR;
 
1025
              }
 
1026
            }
 
1027
            else
 
1028
            {
 
1029
              if(utm_error_code & UTM_A_ERROR)
 
1030
                error_code |= USNG_A_ERROR;
 
1031
              if(utm_error_code & UTM_INV_F_ERROR)
 
1032
                error_code |= USNG_INV_F_ERROR;
 
1033
              if(utm_error_code & UTM_ZONE_OVERRIDE_ERROR)
 
1034
                error_code |= USNG_ZONE_ERROR;
 
1035
            }
 
1036
          }
 
1037
        }
 
1038
      }
 
1039
    }
 
1040
  }
 
1041
  return (error_code);
 
1042
} /* Convert_USNG_To_UTM */
 
1043
 
 
1044
 
 
1045
long Convert_UPS_To_USNG (char   Hemisphere,
 
1046
                          double Easting,
 
1047
                          double Northing,
 
1048
                          long   Precision,
 
1049
                          char*  USNG)
 
1050
/*
 
1051
 *  The function Convert_UPS_To_USNG converts UPS (hemisphere, easting,
 
1052
 *  and northing) coordinates to a USNG coordinate string according to
 
1053
 *  the current ellipsoid parameters.  If any errors occur, the error
 
1054
 *  code(s) are returned by the function, otherwise UPS_NO_ERROR is
 
1055
 *  returned.
 
1056
 *
 
1057
 *    Hemisphere    : Hemisphere either 'N' or 'S'     (input)
 
1058
 *    Easting       : Easting/X in meters              (input)
 
1059
 *    Northing      : Northing/Y in meters             (input)
 
1060
 *    Precision     : Precision level of USNG string   (input)
 
1061
 *    USNG          : USNG coordinate string           (output)
 
1062
 */
 
1063
{ /* Convert_UPS_To_USNG */
 
1064
  double false_easting;       /* False easting for 2nd letter                 */
 
1065
  double false_northing;      /* False northing for 3rd letter                */
 
1066
  double grid_easting;        /* Easting used to derive 2nd letter of USNG    */
 
1067
  double grid_northing;       /* Northing used to derive 3rd letter of USNG   */
 
1068
  long ltr2_low_value;        /* 2nd letter range - low number                */
 
1069
  int letters[USNG_LETTERS];  /* Number location of 3 letters in alphabet     */
 
1070
  double divisor;
 
1071
  int index = 0;
 
1072
  long error_code = USNG_NO_ERROR;
 
1073
 
 
1074
  if ((Hemisphere != 'N') && (Hemisphere != 'S'))
 
1075
    error_code |= USNG_HEMISPHERE_ERROR;
 
1076
  if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH))
 
1077
    error_code |= USNG_EASTING_ERROR;
 
1078
  if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH))
 
1079
    error_code |= USNG_NORTHING_ERROR;
 
1080
  if ((Precision < 0) || (Precision > MAX_PRECISION))
 
1081
    error_code |= USNG_PRECISION_ERROR;
 
1082
  if (!error_code)
 
1083
  {
 
1084
    divisor = pow (10.0, (5 - Precision));
 
1085
    Easting = (long)(Easting/divisor + 1.0e-9) * divisor;
 
1086
    Northing = (long)(Northing/divisor) * divisor;
 
1087
 
 
1088
    if (Hemisphere == 'N')
 
1089
    {
 
1090
      if (Easting >= TWOMIL)
 
1091
        letters[0] = LETTER_Z;
 
1092
      else
 
1093
        letters[0] = LETTER_Y;
 
1094
 
 
1095
      index = letters[0] - 22;
 
1096
      ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
 
1097
      false_easting = UPS_Constant_Table[index].false_easting;
 
1098
      false_northing = UPS_Constant_Table[index].false_northing;
 
1099
    }
 
1100
    else
 
1101
    {
 
1102
      if (Easting >= TWOMIL)
 
1103
        letters[0] = LETTER_B;
 
1104
      else
 
1105
        letters[0] = LETTER_A;
 
1106
 
 
1107
      ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
 
1108
      false_easting = UPS_Constant_Table[letters[0]].false_easting;
 
1109
      false_northing = UPS_Constant_Table[letters[0]].false_northing;
 
1110
    }
 
1111
 
 
1112
    grid_northing = Northing;
 
1113
    grid_northing = grid_northing - false_northing;
 
1114
    letters[2] = (long)(grid_northing / ONEHT);
 
1115
 
 
1116
    if (letters[2] > LETTER_H)
 
1117
      letters[2] = letters[2] + 1;
 
1118
 
 
1119
    if (letters[2] > LETTER_N)
 
1120
      letters[2] = letters[2] + 1;
 
1121
 
 
1122
    grid_easting = Easting;
 
1123
    grid_easting = grid_easting - false_easting;
 
1124
    letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT));
 
1125
 
 
1126
    if (Easting < TWOMIL)
 
1127
    {
 
1128
      if (letters[1] > LETTER_L)
 
1129
        letters[1] = letters[1] + 3;
 
1130
 
 
1131
      if (letters[1] > LETTER_U)
 
1132
        letters[1] = letters[1] + 2;
 
1133
    }
 
1134
    else
 
1135
    {
 
1136
      if (letters[1] > LETTER_C)
 
1137
        letters[1] = letters[1] + 2;
 
1138
 
 
1139
      if (letters[1] > LETTER_H)
 
1140
        letters[1] = letters[1] + 1;
 
1141
 
 
1142
      if (letters[1] > LETTER_L)
 
1143
        letters[1] = letters[1] + 3;
 
1144
    }
 
1145
 
 
1146
    Make_USNG_String (USNG, 0, letters, Easting, Northing, Precision);
 
1147
  }
 
1148
  return (error_code);
 
1149
} /* Convert_UPS_To_USNG */
 
1150
 
 
1151
 
 
1152
long Convert_USNG_To_UPS ( char   *USNG,
 
1153
                           char   *Hemisphere,
 
1154
                           double *Easting,
 
1155
                           double *Northing)
 
1156
/*
 
1157
 *  The function Convert_USNG_To_UPS converts a USNG coordinate string
 
1158
 *  to UPS (hemisphere, easting, and northing) coordinates, according
 
1159
 *  to the current ellipsoid parameters. If any errors occur, the error
 
1160
 *  code(s) are returned by the function, otherwide UPS_NO_ERROR is returned.
 
1161
 *
 
1162
 *    USNG          : USNG coordinate string           (input)
 
1163
 *    Hemisphere    : Hemisphere either 'N' or 'S'     (output)
 
1164
 *    Easting       : Easting/X in meters              (output)
 
1165
 *    Northing      : Northing/Y in meters             (output)
 
1166
 */
 
1167
{ /* Convert_USNG_To_UPS */
 
1168
  long ltr2_high_value;       /* 2nd letter range - high number             */
 
1169
  long ltr3_high_value;       /* 3rd letter range - high number (UPS)       */
 
1170
  long ltr2_low_value;        /* 2nd letter range - low number              */
 
1171
  double false_easting;       /* False easting for 2nd letter               */
 
1172
  double false_northing;      /* False northing for 3rd letter              */
 
1173
  double grid_easting;        /* easting for 100,000 meter grid square      */
 
1174
  double grid_northing;       /* northing for 100,000 meter grid square     */
 
1175
  long zone;
 
1176
  long letters[USNG_LETTERS];
 
1177
  long in_precision;
 
1178
  int index = 0;
 
1179
  long error_code = USNG_NO_ERROR;
 
1180
 
 
1181
  error_code = Break_USNG_String (USNG, &zone, letters, Easting, Northing, &in_precision);
 
1182
  if (zone)
 
1183
    error_code |= USNG_STRING_ERROR;
 
1184
  else
 
1185
  {
 
1186
    if (!error_code)
 
1187
    {
 
1188
      if (letters[0] >= LETTER_Y)
 
1189
      {
 
1190
        *Hemisphere = 'N';
 
1191
 
 
1192
        index = letters[0] - 22;
 
1193
        ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
 
1194
        ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value;
 
1195
        ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value;
 
1196
        false_easting = UPS_Constant_Table[index].false_easting;
 
1197
        false_northing = UPS_Constant_Table[index].false_northing;
 
1198
      }
 
1199
      else
 
1200
      {
 
1201
        *Hemisphere = 'S';
 
1202
 
 
1203
        ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
 
1204
        ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value;
 
1205
        ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value;
 
1206
        false_easting = UPS_Constant_Table[letters[0]].false_easting;
 
1207
        false_northing = UPS_Constant_Table[letters[0]].false_northing;
 
1208
      }
 
1209
 
 
1210
      /* Check that the second letter of the USNG string is within
 
1211
       * the range of valid second letter values
 
1212
       * Also check that the third letter is valid */
 
1213
      if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) ||
 
1214
          ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) ||
 
1215
          (letters[1] == LETTER_M) || (letters[1] == LETTER_N) ||
 
1216
          (letters[1] == LETTER_V) || (letters[1] == LETTER_W)) ||
 
1217
          (letters[2] > ltr3_high_value))
 
1218
          error_code = USNG_STRING_ERROR;
 
1219
 
 
1220
      if (!error_code)
 
1221
      {
 
1222
        grid_northing = (double)letters[2] * ONEHT + false_northing;
 
1223
        if (letters[2] > LETTER_I)
 
1224
          grid_northing = grid_northing - ONEHT;
 
1225
 
 
1226
        if (letters[2] > LETTER_O)
 
1227
          grid_northing = grid_northing - ONEHT;
 
1228
 
 
1229
        grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + false_easting;
 
1230
        if (ltr2_low_value != LETTER_A)
 
1231
        {
 
1232
          if (letters[1] > LETTER_L)
 
1233
            grid_easting = grid_easting - 300000.0;
 
1234
 
 
1235
          if (letters[1] > LETTER_U)
 
1236
            grid_easting = grid_easting - 200000.0;
 
1237
        }
 
1238
        else
 
1239
        {
 
1240
          if (letters[1] > LETTER_C)
 
1241
            grid_easting = grid_easting - 200000.0;
 
1242
 
 
1243
          if (letters[1] > LETTER_I)
 
1244
            grid_easting = grid_easting - ONEHT;
 
1245
 
 
1246
          if (letters[1] > LETTER_L)
 
1247
            grid_easting = grid_easting - 300000.0;
 
1248
        }
 
1249
 
 
1250
        *Easting = grid_easting + *Easting;
 
1251
        *Northing = grid_northing + *Northing;
 
1252
      }
 
1253
    }
 
1254
  }
 
1255
  return (error_code);
 
1256
} /* Convert_USNG_To_UPS */