UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
GEOREF.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: GEOREF
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions from Geodetic coordinates (latitude
9  * and longitude in radians) to a GEOREF coordinate string.
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  * GEOREF_NO_ERROR : No errors occurred in function
19  * GEOREF_LAT_ERROR : Latitude outside of valid range
20  * (-90 to 90 degrees)
21  * GEOREF_LON_ERROR : Longitude outside of valid range
22  * (-180 to 360 degrees)
23  * GEOREF_STR_ERROR : A GEOREF string error: string too long,
24  * string too short, or string length
25  * not even.
26  * GEOREF_STR_LAT_ERROR : The latitude part of the GEOREF string
27  * (second or fourth character) is invalid.
28  * GEOREF_STR_LON_ERROR : The longitude part of the GEOREF string
29  * (first or third character) is invalid.
30  * GEOREF_STR_LAT_MIN_ERROR : The latitude minute part of the GEOREF
31  * string is greater than 60.
32  * GEOREF_STR_LON_MIN_ERROR : The longitude minute part of the GEOREF
33  * string is greater than 60.
34  * GEOREF_PRECISION_ERROR : The precision must be between 0 and 5
35  * inclusive.
36  *
37  *
38  * REUSE NOTES
39  *
40  * GEOREF is intended for reuse by any application that performs a
41  * conversion between Geodetic and GEOREF coordinates.
42  *
43  * REFERENCES
44  *
45  * Further information on GEOREF can be found in the Reuse Manual.
46  *
47  * GEOREF originated from : U.S. Army Topographic Engineering Center
48  * Geospatial Information Division
49  * 7701 Telegraph Road
50  * Alexandria, VA 22310-3864
51  *
52  * LICENSES
53  *
54  * None apply to this component.
55  *
56  * RESTRICTIONS
57  *
58  * GEOREF has no restrictions.
59  *
60  * ENVIRONMENT
61  *
62  * GEOREF was tested and certified in the following environments:
63  *
64  * 1. Solaris 2.5 with GCC version 2.8.1
65  * 2. Windows 95 with MS Visual C++ version 6
66  *
67  * MODIFICATIONS
68  *
69  * Date Description
70  * ---- -----------
71  * 02-20-97 Original Code
72  * 03-02-07 Original C++ Code
73  */
74 
75 
76 /***************************************************************************/
77 /*
78  * INCLUDES
79  */
80 
81 #include <ctype.h>
82 #include <math.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include "GEOREF.h"
87 #include "GEOREFCoordinates.h"
89 #include "GeodeticCoordinates.h"
91 #include "ErrorMessages.h"
92 
93 /*
94  * ctype.h - Standard C character handling library
95  * math.h - Standard C math library
96  * stdio.h - Standard C input/output library
97  * stdlib.h - Standard C general utility library
98  * string.h - Standard C string handling library
99  * GEOREF.h - for prototype error checking
100  * GEOREFCoordinates.h - defines GEOREF coordinates
101  * MapProjectionCoordinates.h - defines map projection coordinates
102  * GeodeticCoordinates.h - defines geodetic coordinates
103  * CoordinateConversionException.h - Exception handler
104  * ErrorMessages.h - Contains exception messages
105  */
106 
107 
108 using namespace MSP::CCS;
109 
110 
111 /***************************************************************************/
112 /* DEFINES
113  *
114  */
115 
116 const int TRUE = 1;
117 const int FALSE = 0;
118 const double LATITUDE_LOW = -90.0; /* Minimum latitude */
119 const double LATITUDE_HIGH = 90.0; /* Maximum latitude */
120 const double LONGITUDE_LOW = -180.0; /* Minimum longitude */
121 const double LONGITUDE_HIGH = 360.0; /* Maximum longitude */
122 const double MIN_PER_DEG = 60.0; /* Number of minutes per degree */
123 const int GEOREF_MINIMUM = 4; /* Minimum number of chars for GEOREF */
124 const int GEOREF_MAXIMUM = 14; /* Maximum number of chars for GEOREF */
125 const int GEOREF_LETTERS = 4; /* Number of letters in GEOREF string */
126 const int MAX_PRECISION = 5; /* Maximum precision of minutes part */
127 const int LETTER_I = 8; /* Index for letter I */
128 const int LETTER_M = 12; /* Index for letter M */
129 const int LETTER_O = 14; /* Index for letter O */
130 const int LETTER_Q = 16; /* Index for letter Q */
131 const int LETTER_Z = 25; /* Index for letter Z */
132 const int LETTER_A_OFFSET = 65; /* Letter A offset in character set */
133 const int ZERO_OFFSET = 48; /* Number zero offset in character set */
134 const double PI = 3.14159265358979323e0; /* PI */
135 const double DEGREE_TO_RADIAN = (PI / 180.0);
136 const double RADIAN_TO_DEGREE = (180.0 / PI);
137 const double QUAD = 15.0; /* Degrees per grid square */
138 const double ROUND_ERROR = 0.0000005; /* Rounding factor */
139 
140 
141 /************************************************************************/
142 /* LOCAL FUNCTIONS
143  *
144  */
145 
146 void extractDegrees( char *GEOREFString, double *longitude, double *latitude )
147 {
148 /*
149  * This function extracts the latitude and longitude degree parts of the
150  * GEOREF string. The latitude and longitude degree parts are the first four
151  * characters.
152  *
153  * GEOREFString : GEOREF string (input)
154  * longitude : Longitude in degrees (output)
155  * latitude : Latitude in degrees (output)
156  */
157  long i; /* counter in for loops */
158  long temp_char; /* temporary character */
159  long letter_number[GEOREF_LETTERS]; /* number corresponding to letter */
160 
161  for (i=0;i<GEOREF_LETTERS;i++)
162  {
163  temp_char = toupper(GEOREFString[i]);
164  temp_char = temp_char - LETTER_A_OFFSET;
165  if ((!isalpha(GEOREFString[i]))
166  || (temp_char == LETTER_I)
167  || (temp_char == LETTER_O))
168  {
169  if ((i == 0) || (i == 2))
171  else
173  }
174  letter_number[i] = temp_char;
175  }
176  for (i=0;i<4;i++)
177  {
178  if (letter_number[i] > LETTER_O)
179  letter_number[i] -= 2;
180  else if (letter_number[i] > LETTER_I)
181  letter_number[i] -= 1;
182  }
183  if ((letter_number[0] > 23) || (letter_number[2] > 14))
185  if ((letter_number[1] > 11) || (letter_number[3] > 14))
187 
188  *latitude = (double)(letter_number[1]) * QUAD + (double)(letter_number[3]);
189  *longitude = (double)(letter_number[0]) * QUAD + (double)(letter_number[2]);
190 }
191 
192 
193 void extractMinutes( char *GEOREFString, long start, long length, long errorType, double *minutes )
194 {
195 /*
196  * This function extracts the minutes from the GEOREF string. The minutes
197  * part begins at position start and has length length. The ERROR_TYPE is
198  * to allow this function to work with both latitude and longitude minutes.
199  *
200  * GEOREFString : GEOREF string (input)
201  * start : Start position in the GEOREF string (input)
202  * length : length of minutes in the GEOREF string (input)
203  * errorType : has a value of either GEOREF_STR_LAT_MIN_ERROR (input)
204  * or GEOREF_STR_LON_MIN_ERROR
205  * minutes : minute part (output)
206  */
207  long i; /* counter in for loop */
208  char temp_str[(GEOREF_MAXIMUM-GEOREF_LETTERS)/2 + 1];
209 
210  for (i=0;i<length;i++)
211  {
212  if (isdigit(GEOREFString[start+i]))
213  temp_str[i] = GEOREFString[start+i];
214  else
215  {
216  if( errorType == GEOREF_STR_LAT_MIN_ERROR )
218  else
220  }
221  }
222  temp_str[length] = 0;
223  *minutes = (double)atof(temp_str); /* need atof, atoi can't handle 59999 */
224  while (length > 2)
225  {
226  *minutes = *minutes / 10;
227  length = length - 1;
228  }
229  if (*minutes > (double)MIN_PER_DEG)
231 }
232 
233 
234 long roundGEOREF( double value )
235 {
236 /* Round value to nearest integer, using standard engineering rule */
237 
238  double ivalue;
239  long ival;
240  double fraction = modf (value, &ivalue);
241 
242  ival = (long)(ivalue);
243  if ((fraction > 0.5) || ((fraction == 0.5) && (ival%2 == 1)))
244  ival++;
245 
246  return ival;
247 }
248 
249 
250 void convertMinutesToString( double minutes, long precision, char *str )
251 {
252 /*
253  * This function converts minutes to a string of length precision.
254  *
255  * minutes : Minutes to be converted (input)
256  * precision : Length of resulting string (input)
257  * str : String to hold converted minutes (output)
258  */
259 
260  double divisor;
261  long min;
262  divisor = pow (10.0, (5.0 - precision));
263  if (minutes == 60.0)
264  minutes = 59.999;
265  minutes = minutes * 1000;
266  min = roundGEOREF (minutes/divisor);
267  sprintf (str, "%*.*ld", precision, precision, min);
268  if (precision == 1)
269  strcat (str, "0");
270 }
271 
272 
273 /************************************************************************/
274 /* FUNCTIONS
275  *
276  */
277 
279  CoordinateSystem( 0, 0 )
280 {
281 }
282 
283 
285 {
288 }
289 
290 
292 {
293 }
294 
295 
297 {
298  if( this != &g )
299  {
302  }
303 
304  return *this;
305 }
306 
307 
309 {
310 /*
311  * The function convertFromGeodetic converts Geodetic (latitude and longitude in radians)
312  * coordinates to a GEOREF coordinate string. Precision specifies the
313  * number of digits in the GEOREF string for latitude and longitude:
314  * 0 for nearest degree
315  * 1 for nearest ten minutes
316  * 2 for nearest minute
317  * 3 for nearest tenth of a minute
318  * 4 for nearest hundredth of a minute
319  * 5 for nearest thousandth of a minute
320  *
321  * longitude : Longitude in radians. (input)
322  * latitude : Latitude in radians. (input)
323  * precision : Precision specified by the user. (input)
324  * GEOREFString : GEOREF coordinate string. (output)
325  *
326  */
327 
328  double long_min; /* GEOREF longitude minute part */
329  double lat_min; /* GEOREF latitude minute part */
330  double origin_long; /* Origin longitude (-180 degrees)*/
331  double origin_lat; /* Origin latitude (-90 degrees) */
332  long letter_number[GEOREF_LETTERS + 1]; /* GEOREF letters */
333  char long_min_str[MAX_PRECISION + 1]; /* Longitude minute string */
334  char lat_min_str[MAX_PRECISION + 1]; /* Latitude minute string */
335  long i; /* counter in for loop */
336  char GEOREFString[21];
337 
338  double latitude = geodeticCoordinates->latitude() * RADIAN_TO_DEGREE;
339  double longitude = geodeticCoordinates->longitude() * RADIAN_TO_DEGREE;
340 
341  if ((latitude < (double)LATITUDE_LOW)
342  || (latitude > (double)LATITUDE_HIGH))
344  if ((longitude < (double)LONGITUDE_LOW)
345  || (longitude > (double)LONGITUDE_HIGH))
347  if ((precision < 0) || (precision > MAX_PRECISION))
349 
350  if (longitude > 180)
351  longitude -= 360;
352 
353  origin_long = (double)LONGITUDE_LOW;
354  origin_lat = (double)LATITUDE_LOW;
355  letter_number[0] = (long)((longitude-origin_long) / QUAD + ROUND_ERROR);
356  longitude = longitude - ((double)letter_number[0] * QUAD + origin_long);
357  letter_number[2] = (long)(longitude + ROUND_ERROR);
358  long_min = (longitude - (double)letter_number[2]) * (double)MIN_PER_DEG;
359  letter_number[1] = (long)((latitude - origin_lat) / QUAD + ROUND_ERROR);
360  latitude = latitude - ((double)letter_number[1] * QUAD + origin_lat);
361  letter_number[3] = (long)(latitude + ROUND_ERROR);
362  lat_min = (latitude - (double)letter_number[3]) * (double)MIN_PER_DEG;
363  for (i = 0;i < 4; i++)
364  {
365  if (letter_number[i] >= LETTER_I)
366  letter_number[i] += 1;
367  if (letter_number[i] >= LETTER_O)
368  letter_number[i] += 1;
369  }
370 
371  if (letter_number[0] == 26)
372  { /* longitude of 180 degrees */
373  letter_number[0] = LETTER_Z;
374  letter_number[2] = LETTER_Q;
375  long_min = 59.999;
376  }
377  if (letter_number[1] == 13)
378  { /* latitude of 90 degrees */
379  letter_number[1] = LETTER_M;
380  letter_number[3] = LETTER_Q;
381  lat_min = 59.999;
382  }
383 
384  for (i=0;i<4;i++)
385  GEOREFString[i] = (char)(letter_number[i] + LETTER_A_OFFSET);
386  GEOREFString[4] = 0;
387  convertMinutesToString(long_min,precision,long_min_str);
388  convertMinutesToString(lat_min,precision,lat_min_str);
389  strcat(GEOREFString,long_min_str);
390  strcat(GEOREFString,lat_min_str);
391 
392  return new GEOREFCoordinates( CoordinateType::georef, GEOREFString );
393 }
394 
395 
397 {
398 /*
399  * The function convertToGeodetic converts a GEOREF coordinate string to Geodetic (latitude
400  * and longitude in radians) coordinates.
401  *
402  * GEOREFString : GEOREF coordinate string. (input)
403  * longitude : Longitude in radians. (output)
404  * latitude : Latitude in radians. (output)
405  *
406  */
407 
408  long start; /* Position in the GEOREF string */
409  long minutes_length; /* length of minutes in the GEOREF string */
410  long georef_length; /* length of GEOREF string */
411  double origin_long; /* Origin longitude */
412  double origin_lat; /* Origin latitude */
413  double long_minutes; /* Longitude minute part of GEOREF */
414  double lat_minutes; /* Latitude minute part of GEOREF */
415  double longitude, latitude;
416 
417  origin_long = (double)LONGITUDE_LOW;
418  origin_lat = (double)LATITUDE_LOW;
419 
420  char* GEOREFString = georefCoordinates->GEOREFString();
421 
422  georef_length = strlen(GEOREFString);
423  if ((georef_length < GEOREF_MINIMUM) || (georef_length > GEOREF_MAXIMUM)
424  || ((georef_length % 2) != 0))
426 
427  extractDegrees( GEOREFString, &longitude, &latitude );
428  start = GEOREF_LETTERS;
429  minutes_length = (georef_length - start) / 2;
430 
431  extractMinutes(GEOREFString, start, minutes_length,
432  GEOREF_STR_LON_MIN_ERROR, &long_minutes);
433 
434  extractMinutes(GEOREFString, (start+minutes_length),
435  minutes_length, GEOREF_STR_LAT_MIN_ERROR, &lat_minutes);
436 
437  latitude = latitude + origin_lat + lat_minutes / (double)MIN_PER_DEG;
438  longitude = longitude + origin_long + long_minutes / (double)MIN_PER_DEG;
439  latitude = latitude * DEGREE_TO_RADIAN;
440  longitude = longitude * DEGREE_TO_RADIAN;
441 
442  return new GeodeticCoordinates( CoordinateType::geodetic, longitude, latitude );
443 }
444 
445 
446 
447 // CLASSIFICATION: UNCLASSIFIED