UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
NZMG.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: NEW ZEALAND MAP GRID
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions between Geodetic coordinates
9  * (latitude and longitude) and New Zealand Map Grid coordinates
10  * (easting and northing).
11  *
12  * ERROR HANDLING
13  *
14  * This component checks parameters for valid values. If an invalid value
15  * is found the error code is combined with the current error code using
16  * the bitwise or. This combining allows multiple error codes to be
17  * returned. The possible error codes are:
18  *
19  * NZMG_NO_ERROR : No errors occurred in function
20  * NZMG_LAT_ERROR : Latitude outside of valid range
21  * (-33.5 to -48.5 degrees)
22  * NZMG_LON_ERROR : Longitude outside of valid range
23  * (165.5 to 180.0 degrees)
24  * NZMG_EASTING_ERROR : Easting outside of valid range
25  * (depending on ellipsoid and
26  * projection parameters)
27  * NZMG_NORTHING_ERROR : Northing outside of valid range
28  * (depending on ellipsoid and
29  * projection parameters)
30  * NZMG_ELLIPSOID_ERROR : Invalid ellipsoid - must be International
31  *
32  * REUSE NOTES
33  *
34  * NEW ZEALAND MAP GRID is intended for reuse by any application that
35  * performs a New Zealand Map Grid projection or its inverse.
36  *
37  * REFERENCES
38  *
39  * Further information on NEW ZEALAND MAP GRID can be found in the
40  * Reuse Manual.
41  *
42  * NEW ZEALAND MAP GRID originated from :
43  * U.S. Army Topographic Engineering Center
44  * Geospatial Information Division
45  * 7701 Telegraph Road
46  * Alexandria, VA 22310-3864
47  *
48  * LICENSES
49  *
50  * None apply to this component.
51  *
52  * RESTRICTIONS
53  *
54  * NEW ZEALAND MAP GRID has no restrictions.
55  *
56  * ENVIRONMENT
57  *
58  * NEW ZEALAND MAP GRID was tested and certified in the following
59  * environments:
60  *
61  * 1. Solaris 2.5 with GCC, version 2.8.1
62  * 2. Windows 95 with MS Visual C++, version 6
63  *
64  * MODIFICATIONS
65  *
66  * Date Description
67  * ---- -----------
68  * 09-14-00 Original Code
69  * 03-2-07 Original C++ Code
70  *
71  *
72  */
73 
74 
75 /***************************************************************************/
76 /*
77  * INCLUDES
78  */
79 
80 #include <string.h>
81 #include <math.h>
82 #include "NZMG.h"
83 #include "EllipsoidParameters.h"
85 #include "GeodeticCoordinates.h"
87 #include "ErrorMessages.h"
88 
89 /*
90  * string.h - Standard C string handling library
91  * math.h - Standard C math library
92  * NZMG.h - Is for prototype error checking
93  * MapProjectionCoordinates.h - defines map projection coordinates
94  * GeodeticCoordinates.h - defines geodetic coordinates
95  * CoordinateConversionException.h - Exception handler
96  * ErrorMessages.h - Contains exception messages
97  */
98 
99 
100 using namespace MSP::CCS;
101 
102 
103 /***************************************************************************/
104 /* DEFINES
105  *
106  */
107 
108 const double PI = 3.14159265358979323e0; /* PI */
109 const double PI_OVER_2 = (PI / 2.0);
110 const double TWO_PI = (2.0 * PI);
111 const double MAX_LAT = (-33.5 * PI / 180.0); /* -33.5 degrees */
112 const double MIN_LAT = (-48.5 * PI / 180.0); /* -48.5 degrees */
113 const double MAX_LON = (180.0 * PI / 180.0); /* 180 degrees */
114 const double MIN_LON = (165.5 * PI / 180.0); /* 165.5 degrees */
115 
116 const char* International = "IN";
117 
118 /* NZMG projection Parameters */
119 const double NZMG_Origin_Lat = (-41.0); /* Latitude of origin, in radians */
120 const double NZMG_Origin_Long = (173.0 * PI / 180.0); /* Longitude of origin, in radians */
121 const double NZMG_False_Northing = 6023150.0; /* False northing, in meters */
122 const double NZMG_False_Easting = 2510000.0; /* False easting, in meters */
123 
124 /* Maximum variance for easting and northing values for International. */
125 const double NZMG_Max_Easting = 3170000.0;
126 const double NZMG_Max_Northing = 6900000.0;
127 const double NZMG_Min_Easting = 1810000.0;
128 const double NZMG_Min_Northing = 5160000.0;
129 
130 struct Complex
131 {
132  double real;
133  double imag;
134 };
135 
136 double A[] = { 0.6399175073, -0.1358797613, 0.063294409,
137  -0.02526853, 0.0117879, -0.0055161,
138  0.0026906, -0.001333, 0.00067, -0.00034 };
139 
140 Complex B[] = { { 0.7557853228, 0.0 },
141  { 0.249204646, 0.003371507 },
142  { -0.001541739, 0.041058560 },
143  { -0.10162907, 0.01727609 },
144  { -0.26623489, -0.36249218 },
145  { -0.6870983, -1.1651967 } };
146 
147 Complex C[] = { { 1.3231270439, 0.0 },
148  { -0.577245789, -0.007809598 },
149  { 0.508307513, -0.112208952 },
150  { -0.15094762, 0.18200602 },
151  { 1.01418179, 1.64497696 },
152  { 1.9660549, 2.5127645 } };
153 
154 double D[] = { 1.5627014243, 0.5185406398, -0.03333098,
155  -0.1052906, -0.0368594, 0.007317,
156  0.01220, 0.00394, -0.0013 };
157 
158 /************************************************************************/
159 /* FUNCTIONS
160  *
161  */
162 
163 /* Add two complex numbers */
165 {
166  Complex z;
167 
168  z.real = z1.real + z2.real;
169  z.imag = z1.imag + z2.imag;
170 
171  return z;
172 }
173 
174 
175 /* Multiply two complex numbers */
177 {
178  Complex z;
179 
180  z.real = z1.real * z2.real - z1.imag * z2.imag;
181  z.imag = z1.imag * z2.real + z1.real * z2.imag;
182 
183  return z;
184 }
185 
186 
187 /* Divide two complex numbers */
189 {
190  Complex z;
191  double denom;
192 
193  denom = z2.real * z2.real + z2.imag * z2.imag;
194  z.real = (z1.real * z2.real + z1.imag * z2.imag) / denom;
195  z.imag = (z1.imag * z2.real - z1.real * z2.imag) / denom;
196 
197  return z;
198 }
199 
200 
201 NZMG::NZMG( char* ellipsoidCode ) :
202  CoordinateSystem( 6378388.0, 1 / 297.0 )
203 {
204 /*
205  * The constructor receives the ellipsoid code and sets
206  * the corresponding state variables. If any errors occur,
207  * an exception is thrown with a description of the error.
208  *
209  * ellipsoidCode : 2-letter code for ellipsoid (input)
210  */
211 
212  strcpy( NZMGEllipsoidCode, "IN" );
213 
214  if (strcmp(ellipsoidCode, International) != 0)
215  { /* Ellipsoid must be International */
217  }
218 
219  strcpy( NZMGEllipsoidCode, ellipsoidCode );
220 }
221 
222 
223 NZMG::NZMG( const NZMG &n )
224 {
227 }
228 
229 
231 {
232 }
233 
234 
236 {
237  if( this != &n )
238  {
241  }
242 
243  return *this;
244 }
245 
246 
248 {
249 /*
250  * The function getParameters returns the current ellipsoid
251  * code.
252  *
253  * ellipsoidCode : 2-letter code for ellipsoid (output)
254  */
255 
256  return new EllipsoidParameters( semiMajorAxis, flattening, (char*)NZMGEllipsoidCode );
257 }
258 
259 
261 {
262 /*
263  * The function convertFromGeodetic converts geodetic (latitude and
264  * longitude) coordinates to New Zealand Map Grid projection (easting and northing)
265  * coordinates, according to the current ellipsoid and New Zealand Map Grid
266  * projection parameters. If any errors occur, an exception is thrown
267  * with a description of the error.
268  *
269  * longitude : Longitude (lambda), in radians (input)
270  * latitude : Latitude (phi), in radians (input)
271  * easting : Easting (X), in meters (output)
272  * northing : Northing (Y), in meters (output)
273  */
274 
275  Complex Zeta, z;
276  int n;
277  double dphi;
278  double du, dlam;
279 
280  double longitude = geodeticCoordinates->longitude();
281  double latitude = geodeticCoordinates->latitude();
282 
283  if ((latitude < MIN_LAT) || (latitude > MAX_LAT))
284  { /* Latitude out of range */
286  }
287  if ((longitude < MIN_LON) || (longitude > MAX_LON))
288  { /* Longitude out of range */
290  }
291 
292  dphi = (latitude * (180.0 / PI) - NZMG_Origin_Lat) * 3600.0 * 1.0e-5;
293  du = A[9];
294  for (n = 8; n >= 0; n--)
295  du = du * dphi + A[n];
296  du *= dphi;
297 
298  dlam = longitude - NZMG_Origin_Long;
299 
300  Zeta.real = du;
301  Zeta.imag = dlam;
302 
303  z.real = B[5].real;
304  z.imag = B[5].imag;
305  for (n = 4; n >= 0; n--)
306  {
307  z = multiply(z, Zeta);
308  z = add(B[n], z);
309  }
310  z = multiply(z, Zeta);
311 
312  double easting = (z.imag * semiMajorAxis) + NZMG_False_Easting;
313  double northing = (z.real * semiMajorAxis) + NZMG_False_Northing;
314 
315  if ((easting < NZMG_Min_Easting) || (easting > NZMG_Max_Easting))
317  if ((northing < NZMG_Min_Northing) || (northing > NZMG_Max_Northing))
319 
320  return new MapProjectionCoordinates( CoordinateType::newZealandMapGrid, easting, northing );
321 }
322 
323 
325 {
326 /*
327  * The function convertToGeodetic converts New Zealand Map Grid projection
328  * (easting and northing) coordinates to geodetic (latitude and longitude)
329  * coordinates, according to the current ellipsoid and New Zealand Map Grid projection
330  * coordinates. If any errors occur, an exception is thrown with a description
331  * of the error.
332  *
333  * easting : Easting (X), in meters (input)
334  * northing : Northing (Y), in meters (input)
335  * longitude : Longitude (lambda), in radians (output)
336  * latitude : Latitude (phi), in radians (output)
337  */
338 
339  int i, n;
340  Complex coeff;
341  Complex z, Zeta, Zeta_Numer, Zeta_Denom, Zeta_sqr;
342  double dphi;
343 
344  double easting = mapProjectionCoordinates->easting();
345  double northing = mapProjectionCoordinates->northing();
346 
347  if ((easting < NZMG_Min_Easting) || (easting > NZMG_Max_Easting))
348  { /* Easting out of range */
350  }
351  if ((northing < NZMG_Min_Northing) || (northing > NZMG_Max_Northing))
352  { /* Northing out of range */
354  }
355 
356  z.real = (northing - NZMG_False_Northing) / semiMajorAxis;
357  z.imag = (easting - NZMG_False_Easting) / semiMajorAxis;
358 
359  Zeta.real = C[5].real;
360  Zeta.imag = C[5].imag;
361  for (n = 4; n >= 0; n--)
362  {
363  Zeta = multiply(Zeta, z);
364  Zeta = add(C[n], Zeta);
365  }
366  Zeta = multiply(Zeta, z);
367 
368  for (i = 0; i < 2; i++)
369  {
370  Zeta_Numer.real = 5.0 * B[5].real;
371  Zeta_Numer.imag = 5.0 * B[5].imag;
372  Zeta_Denom.real = 6.0 * B[5].real;
373  Zeta_Denom.imag = 6.0 * B[5].imag;
374  for (n = 4; n >= 1; n--)
375  {
376  Zeta_Numer = multiply(Zeta_Numer, Zeta);
377  coeff.real = n * B[n].real;
378  coeff.imag = n * B[n].imag;
379  Zeta_Numer = add(coeff, Zeta_Numer);
380 
381  Zeta_Denom = multiply(Zeta_Denom, Zeta);
382  coeff.real = (n+1) * B[n].real;
383  coeff.imag = (n+1) * B[n].imag;
384  Zeta_Denom = add(coeff, Zeta_Denom);
385  }
386  Zeta_sqr = multiply(Zeta, Zeta);
387 
388  Zeta_Numer = multiply(Zeta_Numer, Zeta_sqr);
389  Zeta_Numer = add(z, Zeta_Numer);
390 
391  Zeta_Denom = multiply(Zeta_Denom, Zeta);
392  Zeta_Denom = add(B[0], Zeta_Denom);
393 
394  Zeta = divide(Zeta_Numer, Zeta_Denom);
395  }
396  dphi = D[8];
397  for (n = 7; n >= 0; n--)
398  dphi = dphi * Zeta.real + D[n];
399  dphi *= Zeta.real;
400 
401  double latitude = NZMG_Origin_Lat + (dphi * 1.0e5 / 3600.0);
402  latitude *= PI / 180.0;
403  double longitude = NZMG_Origin_Long + Zeta.imag;
404 
405  if ((longitude > PI) && (longitude - PI < 1.0e-6))
406  longitude = PI;
407 
408  if ((latitude < MIN_LAT) || (latitude > MAX_LAT))
410  if ((longitude < MIN_LON) || (longitude > MAX_LON))
412 
413  return new GeodeticCoordinates( CoordinateType::geodetic, longitude, latitude );
414 }
415 
416 
417 
418 // CLASSIFICATION: UNCLASSIFIED