UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
egm2008_aoi_grid_package.cpp
Go to the documentation of this file.
1 
2 // CLASSIFICATION: UNCLASSIFIED
3 
5 // //
6 // File name: egm2008_aoi_grid_package.cpp //
7 // //
8 // Description of this module: //
9 // Utility software that interpolates EGM 2008 geoid //
10 // heights from one of NGA's reformatted geoid height grids. //
11 // //
12 // This interpolator uses Area of Interest (AOI) //
13 // geoid height grids, not the worldwide geoid height grid. //
14 // //
15 // This interpolator does not load an Area of Interest (AOI) //
16 // geoid height grid until a user first requests a geoid height. //
17 // The interpolator then loads an AOI grid centered near the point of //
18 // interest, and it interpolates local geoid height from the AOI grid. //
19 // This interpolator re-uses the AOI grid until a subsequent point of //
20 // interest lies outside the AOI. The interpolator then loads a //
21 // new AOI grid centered at the new horizontal location of interest. //
22 // //
23 // This interpolator gives exactly the same results as //
24 // the companion egm2008_full_grid_package's interpolator. //
25 // However, the AOI interpolator requires far less computer memory. //
26 // //
27 // Revision History: //
28 // Date Name Description //
29 // ----------- ------------ ----------------------------------------------//
30 // 19 Nov 2010 RD Craig Release //
31 // 27 Jan 2011 S. Gillis BAEts28121, Terrain Service rearchitecture //
32 // 11 Feb 2011 RD Craig Updates following code review //
33 // 30 May 2013 RD Craig MSP 1.3: ER29758 //
34 // Added second constructor to //
35 // permit multiple geoid-height grids //
36 // when assessing relative interpolation errors. //
37 // //
39 
40 // This file contains definitions
41 // for functions in the Egm2008AoiGrid class.
42 
43 #include <fstream>
44 
45 #ifdef IRIXN32
46 #include <math.h>
47 #else
48 #include <cmath>
49 #endif
50 
51 #include "CCSThreadLock.h"
53 
55 
56 namespace
57 {
58  const int BYTES_PER_DOUBLE = sizeof( double );
59  const int BYTES_PER_FLOAT = sizeof( float );
60  const int BYTES_PER_INT = sizeof( int );
61 
62  const int BYTES_IN_HEADER =
63  ( 3 * BYTES_PER_INT + 2 * BYTES_PER_DOUBLE );
64 
65  const int NOM_AOI_COLS1 = 100; // nominal # columns in 1' x 1' AOI grid
66  const int NOM_AOI_ROWS1 = 100; // nominal # rows in 1' x 1' AOI grid
67  const int NOM_AOI_COLS2_5 = 50; // nominal # columns in 2.5' x 2.5' AOI grid
68  const int NOM_AOI_ROWS2_5 = 50; // nominal # rows in 2.5' x 2.5' AOI grid
69 
70  const double PI = 3.14159265358979323;
71 
72  const double PIDIV2 = PI / 2.0;
73  const double TWOPI = 2.0 * PI;
74 
75  const double DEG_PER_RAD = 180.0 / PI;
76  const double RAD_PER_DEG = PI / 180.0;
77 
78  const double MTR_PER_NM = 1852.0; // meters per International nautical mile
79 
80  const double EPSILON = 1.0e-15; // ~4 times machine epsilon
81 
82  const double SEMI_MAJOR_AXIS = 6378137.0; // WGS-84
83  const double FLATTENING = 1.0 / 298.257223563; // WGS-84
84 }
85 
86 using namespace MSP;
87 
88 // ***************************
89 // ** Public user functions **
90 // ***************************
91 
92 // **************************************
93 // * Default Egm2008AoiGrid constructor *
94 // **************************************
95 
97 
98 // : Egm2008GeoidGrid() // base class initializer
99 {
100  // November 19, 2010: Version 1.00
101  // February 11, 2011: Version 1.01
102 
103  int status;
104 
105  // This function implements the
106  // default Egm2008AoiGrid constructor.
107 
108  // The base class constructor
109  // initialized most data members.
110 
111  // Initialize AOI grid parameters .....
112 
113  _minAoiRowIndex = 10000000; // these invalid values
114  _maxAoiRowIndex = -10000000; // will force the first
115  _minAoiColIndex = 10000000; // geoid height request
116  _maxAoiColIndex = -10000000; // to load a new AOI grid
117 
118  _nAoiCols = 0;
119  _nAoiRows = 0;
120 
121  _nomAoiCols = 0;
122  _nomAoiRows = 0;
123 
124  _heightGrid = NULL;
125 
126  // Get worldwide grid's metadata .....
127 
128  status = this->loadGridMetadata();
129 
130  if ( status != 0 )
131  {
133  "Error: Egm2008AoiGrid: constructor failed." );
134  }
135 
136 } // End of Egm2008AoiGrid constuctor
137 
138 
139 // ******************************************
140 // * Non-default Egm2008AoiGrid constructor *
141 // ******************************************
142 
144  const std::string &gridFname ) // input
145 
146 : Egm2008GeoidGrid( gridFname ) // base class initializer
147 {
148  // November 19, 2010: Version 1.00
149  // February 11, 2011: Version 1.01
150  // May 30, 2013: Version 2.00
151 
152  // Definition:
153 
154  // gridFname: The support geoid-height grid's file name; this
155  // file name should not contain the directory path;
156  // the base-class constructor will prepend the
157  // path specified by environment variable MSPCCS_DATA.
158 
159  int status;
160 
161  // This function implements the
162  // non-default Egm2008AoiGrid constructor.
163 
164  // The base class constructor
165  // initialized most data members.
166 
167  // Initialize AOI grid parameters .....
168 
169  _minAoiRowIndex = 10000000; // these invalid values
170  _maxAoiRowIndex = -10000000; // will force the first
171  _minAoiColIndex = 10000000; // geoid height request
172  _maxAoiColIndex = -10000000; // to load a new AOI grid
173 
174  _nAoiCols = 0;
175  _nAoiRows = 0;
176 
177  _nomAoiCols = 0;
178  _nomAoiRows = 0;
179 
180  _heightGrid = NULL;
181 
182  // Get worldwide grid's metadata .....
183 
184  status = this->loadGridMetadata();
185 
186  if ( status != 0 )
187  {
189  "Error: Egm2008AoiGrid: constructor failed." );
190  }
191 
192 } // End of non-default Egm2008AoiGrid constuctor
193 
194 
195 // ***********************************
196 // * Egm2008AoiGrid copy constructor *
197 // ***********************************
198 
200  ( const Egm2008AoiGrid& oldGrid ) // input
201 
202 : Egm2008GeoidGrid( oldGrid ) // base class initializer
203 
204 {
205  // November 19, 2010: Version 1.00
206 
207  // This function implements
208  // the Egm2008AoiGrid copy constructor.
209 
210  // Definition:
211 
212  // oldGrid: The Egm2008AoiGrid object being copied.
213 
214  int i;
215  int kount;
216 
217  // Copy AOI grid's parameters .....
218 
219  _maxAoiColIndex = oldGrid._maxAoiColIndex;
220  _minAoiColIndex = oldGrid._minAoiColIndex;
221 
222  _maxAoiRowIndex = oldGrid._maxAoiRowIndex;
223  _minAoiRowIndex = oldGrid._minAoiRowIndex;
224 
225  _nAoiCols = oldGrid._nAoiCols;
226  _nAoiRows = oldGrid._nAoiRows;
227 
228  _nomAoiCols = oldGrid._nomAoiCols;
229  _nomAoiRows = oldGrid._nomAoiRows;
230 
231  _heightGrid = NULL;
232 
233  // Copy AOI grid's geoid separations .....
234 
235  // (the AOI geoid height grid
236  // will not have been loaded if copying
237  // precedes first geoid height interpolation)
238 
239  try
240  {
241  if ( NULL != oldGrid._heightGrid )
242  {
243  kount = _nAoiRows * _nAoiCols;
244 
245  _heightGrid = new float[ kount ];
246 
247  for ( i = 0; i < kount; i++ )
248  {
249  _heightGrid[ i ] = oldGrid._heightGrid[ i ];
250  }
251  }
252  }
253  catch (...)
254  {
255  oldGrid._mutex.unlock();
256 
258  "Error: Egm2008AoiGrid: copy contructor failed");
259  }
260 
261  oldGrid._mutex.unlock(); // Use CCSThreadMutex in copy constructors
262 
263 } // End of Egm2008AoiGrid copy constuctor
264 
265 
266 // *****************************
267 // * Egm2008AoiGrid destructor *
268 // *****************************
269 
271 {
272  // November 19, 2010: Version 1.00
273 
274  // This function implements
275  // the Egm2008AoiGrid destructor.
276 
277  delete[] _heightGrid;
278 
279 } // End of Egm2008AoiGrid destructor
280 
281 
282 // **************************************
283 // * Egm2008AoiGrid assignment operator *
284 // **************************************
285 
288 {
289  // November 19, 2010: Version 1.00
290 
291  // Definition:
292 
293  // oldGrid: The Egm2008AoiGrid object being assigned.
294 
295  int i;
296  int kount;
297 
298  // This function implements
299  // the Egm2008AoiGrid assignment operator.
300 
301  if ( this == & oldGrid ) return ( *this );
302 
303  // Assign most base class data members .....
304 
305  Egm2008GeoidGrid::operator= ( oldGrid );
306 
307  // Assign AOI grid parameters .....
308 
311 
314 
315  _nAoiCols = oldGrid._nAoiCols;
316  _nAoiRows = oldGrid._nAoiRows;
317 
318  _nomAoiCols = oldGrid._nomAoiCols;
319  _nomAoiRows = oldGrid._nomAoiRows;
320 
321  // Assign AOI grid's geoid separations .....
322 
323  // (the AOI geoid height grid will
324  // not have been loaded if assignment
325  // precedes first geoid height interpolation)
326 
327  try
328  {
329  delete[] _heightGrid; _heightGrid = NULL;
330 
331  if ( NULL != oldGrid._heightGrid )
332  {
333  kount = _nAoiRows * _nAoiCols;
334 
335  _heightGrid = new float[ kount ];
336 
337  for ( i = 0; i < kount; i++ )
338  {
339  _heightGrid[ i ] = oldGrid._heightGrid[ i ];
340  }
341  }
342  }
343  catch (...)
344  {
345  _mutex.unlock();
346  oldGrid._mutex.unlock();
347 
349  "Error: Egm2008AoiGrid: assignment operator failed");
350  }
351 
352  _mutex.unlock(); // Use CCSThreadMutex function in assignment operations
353  oldGrid._mutex.unlock();
354 
355  return( *this );
356 
357 } // End of Egm2008AoiGrid assignment operator
358 
359 
360 // *************************************************
361 // * Find geoid height via 2D spline interpolation *
362 // *************************************************
363 
364 int
366  int wSize, // input
367  double latitude, // input
368  double longitude, // input
369  double& gHeight ) // output
370 {
371  // November 19, 2010: Version 1.00
372  // February 11, 2011: Version 1.01
373 
374  // This function interpolates
375  // geoid heights from an AOI grid,
376  // which, in turn, was extracted from a
377  // reformatted version of NGA's worldwide grid.
378  // It primarily uses BICUBIC-SPLINE INTERPOLATION,
379  // but it uses bilinear interpolation for small windows.
380 
381  // Definitions:
382 
383  // gHeight: Interpolated geoid height (meters).
384  // latitude: Geodetic latitude (radians) at
385  // which geoid height is to be interpolated.
386  // longitude: Geodetic longitude (radians) at
387  // which geoid height is to be interpolated.
388  // wSize: The number of grid points
389  // along each edge of the interpolation
390  // window that surrounds the point of interest;
391  // the interpolation window has wSize**2 grid points;
392  // if wSize < 3, then the geoid height
393  // is computed using bilinear interpolation; the
394  // maximum allowable wSize is twenty grid points;
395  // typical window sizes are 4x4, 6x6, 8,8 etc.
396 
397  // i: Worldwide grid's latitude index.
398  // j: Worldwide grid's longitude index.
399  // oddSize: A flag indicating whether the
400  // local interpolation grid's number
401  // of (rows = column) is even or odd;
402  // oddSize = true .....
403  // the number of rows = columns is odd,
404  // where 3x3, 5x5, 7x7, 9x9, etc are
405  // examples of grids with oddSize = true,
406  // oddSize = false .....
407  // the number of rows = columns is even,
408  // where 4x4, 6x6, 8x8, 10x10, etc are
409  // examples of grids with oddSize = false.
410  // The associated indexing logic has two goals:
411  // 2) if oddSize = true, select the
412  // the local grid so that the point of
413  // interest lies near the grid's center,
414  // 2) if oddSize = false, select the
415  // the local grid so that the point of
416  // interest lies in the grid's central cell.
417  // When oddSize = false, the interpolator will
418  // obtain a numerically-most-trustworthy solution.
419 
420  try {
421 
422  MSP::CCSThreadLock aLock( &_mutex ); // copy constructor locks thread
423 
424  const int TWENTY = 20;
425 
426  bool oddSize;
427 
428  int i;
429  int i0;
430  int iIndex;
431  int iMax;
432  int iMin;
433  int j;
434  int j0;
435  int jIndex;
436  int jMax;
437  int jMin;
438  int kIndex;
439  int offset;
440  int status;
441 
442  double latIndex;
443  double lonIndex;
444  double temp;
445 
446  double latSupport[ TWENTY ];
447  double lonSupport[ TWENTY ];
448  double moments [ TWENTY ];
449 
450  // EDIT THE INPUT AND INITIALIZE .....
451 
452  gHeight = 0.0;
453 
454  if ( wSize > MAX_WSIZE ) wSize = MAX_WSIZE;
455 
456  if (( latitude < -PIDIV2 ) || ( latitude > PIDIV2 )) return( 1 );
457 
458  while ( longitude < 0.0 ) longitude += TWOPI;
459  while ( longitude > TWOPI ) longitude -= TWOPI;
460 
461  if ( TWENTY != MAX_WSIZE ) return( 1 );
462 
463  for (i = 0; i < TWENTY; i++)
464  {
465  latSupport[ i ] = lonSupport[ i ] = moments[ i ] = 0.0;
466  }
467 
468  // Determine point indices
469  // (relative to worldwide grid) .....
470 
471  latIndex =
472  double( _nGridPad ) +
473  ( latitude + PIDIV2 ) / _dLat;
474  lonIndex =
475  double( _nGridPad ) +
476  ( longitude - 0.0 ) / _dLon;
477 
478  // Determine interpolation window
479  // indices (relative to worldwide grid) .....
480 
481  oddSize = ( wSize != (( wSize / 2 ) * 2 ));
482 
483  if ( oddSize ) {
484  i0 = int( latIndex + 0.5 );
485  j0 = int( lonIndex + 0.5 );
486 
487  iMin = i0 - ( wSize / 2 );
488  jMin = j0 - ( wSize / 2 );
489  }
490  else {
491  i0 = int( latIndex );
492  j0 = int( lonIndex );
493 
494  iMin = i0 - ( wSize / 2 ) + 1;
495  jMin = j0 - ( wSize / 2 ) + 1;
496  }
497 
498  iMax = iMin + wSize - 1;
499  jMax = jMin + wSize - 1;
500 
501  // Compare interpolation
502  // window's limits to AOI grid's
503  // limits, and reload AOI grid if required .....
504 
505  if (( iMin < _minAoiRowIndex ) ||
506  ( iMax > _maxAoiRowIndex ) ||
507  ( jMin < _minAoiColIndex ) ||
508  ( jMax > _maxAoiColIndex ))
509  {
510  status = this->loadAoiParms( i0, j0 );
511 
512  if ( status != 0 ) return( 1 );
513 
514  status = this->loadGrid();
515 
516  if ( status != 0 ) return( 1 );
517  }
518 
519  // COMPUTE BILINEAR INTERPOLATION .....
520 
521  // (executes only when the interpolation window
522  // is too small for bicubic spline interpolation)
523 
524  if ( wSize < 3 )
525  {
526  status =
527  this->geoidHeight( latitude, longitude, gHeight );
528 
529  if ( status != 0 ) return( 1 );
530 
531  ; /* Normal bilinear interpolation return */ return( 0 );
532  }
533 
534  // COMPUTE BI-CUBIC SPLINE INTERPOLATION .....
535 
536  // Compute interpolation window's
537  // relative column coordinate at which the
538  // first set of geoid heights will be interpolated ....
539 
540  temp =
541  lonIndex - double( jMin ); // 0 <= temp <= (wSize - 1)
542 
543  // Interpolate a synthetic geoid height
544  // for each row within the interpolation window .....
545 
546  for ( i = 0; i < wSize; i++ )
547  {
548  iIndex = iMin + i - _minAoiRowIndex; // AOI referenced
549 
550  offset = ( _nAoiRows - iIndex - 1 ) * _nAoiCols;
551 
552  // Load interpolation window's
553  // i-th row of tabulated geoid heights .....
554 
555  for ( j = 0; j < wSize; j++ )
556  {
557  jIndex = jMin + j - _minAoiColIndex; // AOI referenced
558 
559  kIndex = jIndex + offset;
560 
561  lonSupport[ j ] = _heightGrid[ kIndex ];
562  }
563 
564  // Compute moments,
565  // interpolate the i-th row's geoid
566  // height at the longitude of interest,
567  // then load the i-th latitude support point .....
568 
569  status =
570  this->initSpline( wSize, lonSupport, moments );
571 
572  if ( status != 0 ) return( 1 );
573 
574  latSupport[ i ] =
575  this->spline( wSize, temp, lonSupport, moments );
576  }
577 
578  // Compute interpolation window's
579  // relative row coordinate at which the
580  // final geoid height will be interpolated .....
581 
582  temp =
583  latIndex - double( iMin ); // 0 <= temp <= (wSize - 1)
584 
585  // Compute moments, and interpolate final
586  // geoid height at the latitude of interest .....
587 
588  status =
589  this->initSpline( wSize, latSupport, moments );
590 
591  if ( status != 0 ) return( 1 );
592 
593  gHeight =
594  this->spline( wSize, temp, latSupport, moments );
595 
596  // Thread is unlocked by aLock's destructor;
597  // this occurs even when exceptions are thrown.
598 
599  } // End of exceptions' try block
600 
601  catch ( ... ) { return( 1 ); }
602 
603  return ( 0 ); // Normal-return flag
604 
605 } // End of function Egm2008AoiGrid::geoidHeight
606 
607 
608 // **********************
609 // ** Hidden functions **
610 // **********************
611 
612 // **************************************
613 // * Interpolate EGM 2008 geoid heights *
614 // **************************************
615 
616 int
618  double latitude, // input
619  double longitude, // input
620  double& gHeight ) // output
621 {
622  // November 19, 2010: Version 1.00
623 
624  // This function, which is
625  // exercised only when wSize < 3, uses
626  // bilinear interpolation to find geoid heights.
627 
628  // Definitions:
629 
630  // gHeight: Interpolated geoid height (meters).
631  // latitude: GEODETIC latitude at which
632  // nominal geoid height is to be computed.
633  // longitude: GEODETIC longitude at which
634  // nominal geoid height is to be computed.
635 
636  // wSize: The local interpolation window's size:
637  // normally wSize = 6 for EGM 2008 interpolations.
638 
639  // Thread locks are not needed here, because this function
640  // can only be invoked by the bicubic spline's geoidHeight function;
641  // the thread locks reside in the bicubic spline's geoidHeight function.
642 
643  try {
644 
645  int i1;
646  int i1ww;
647  int i2;
648  int i3;
649  int i4;
650  int index;
651  int j1;
652  int j1ww;
653  int j2;
654  int j3;
655  int j4;
656  int status;
657 
658  double a0;
659  double a1;
660  double a2;
661  double a3;
662  double lat1;
663  double lon1;
664  double n1;
665  double n2;
666  double n3;
667  double n4;
668  double x;
669  double y;
670 
671  // EDIT THE INPUT AND INITIALIZE .....
672 
673  gHeight = 0.0;
674 
675  if (( latitude < -PIDIV2 ) || ( latitude > PIDIV2 )) return( 1 );
676 
677  while ( longitude < 0.0 ) longitude += TWOPI;
678  while ( longitude > TWOPI ) longitude -= TWOPI;
679 
680  // Compute the surrounding
681  // points' worldwide grid indices .....
682 
683  status =
684  this->swGridIndices
685  ( latitude, longitude, i1, j1 );
686 
687  if ( status != 0 ) return( 1 );
688 
689  i1ww = i1; // save worldwide index
690  j1ww = j1; // save worldwide index
691 
692  i2 = i1;
693  j2 = j1 + 1;
694 
695  i3 = i1 + 1;
696  j3 = j2;
697 
698  i4 = i3;
699  j4 = j1;
700 
701  // Compute the
702  // corresponding AOI grid indices .....
703 
704  i1 -= _minAoiRowIndex;
705  j1 -= _minAoiColIndex;
706 
707  i2 -= _minAoiRowIndex;
708  j2 -= _minAoiColIndex;
709 
710  i3 -= _minAoiRowIndex;
711  j3 -= _minAoiColIndex;
712 
713  i4 -= _minAoiRowIndex;
714  j4 -= _minAoiColIndex;
715 
716  // GET THE SURROUNDING GRID POINTS' GEOID HEIGHTS .....
717 
718  index =
719  j1 + ( _nAoiRows - i1 - 1 ) * _nAoiCols;
720  n1 = _heightGrid[ index ];
721 
722  index =
723  j2 + ( _nAoiRows - i2 - 1 ) * _nAoiCols;
724  n2 = _heightGrid[ index ];
725 
726  index =
727  j3 + ( _nAoiRows - i3 - 1 ) * _nAoiCols;
728  n3 = _heightGrid[ index ];
729 
730  index =
731  j4 + ( _nAoiRows - i4 - 1 ) * _nAoiCols;
732  n4 = _heightGrid[ index ];
733 
734  // INTERPOLATE GEOID HEIGHT AT THE POINT OF INTEREST .....
735 
736  // Set bilinear coefficients .....
737 
738  a0 = n1;
739  a1 = n2 - n1;
740  a2 = n4 - n1;
741  a3 = n1 + n3 - n2 - n4;
742 
743  // Set first ( S/W ) corner's geodetic coordinates .....
744 
745  lat1 =
746  _baseLatitude + _dLat * double( i1ww ); // radians
747  lon1 =
748  _baseLongitude + _dLon * double( j1ww ); // radians
749 
750  // Set point coordinates relative
751  // to the grid square's S/W corner .....
752 
753  // 0 <= x <= 1; 0 <= y <= 1.
754 
755  x = ( longitude - lon1 ) / _dLon;
756  y = ( latitude - lat1 ) / _dLat;
757 
758  // Perform the interpolation .....
759 
760  gHeight = a0 + a1 * x + a2 * y + a3 * x * y; // meters
761 
762  } // End of exceptions' try block
763 
764  catch ( ... ) { return( 1 ); }
765 
766  return( 0 ); // Normal-return flag
767 
768 } // End of function Egm2008AoiGrid::geoidHeight
769 
770 
771 // ************************************
772 // * Compute new AOI's new parameters *
773 // ************************************
774 
775 int
777  int i0, // input
778  int j0 ) // input
779 {
780  // November 19, 2010: Version 1.00
781  // February 11, 2011: Version 2.00:
782  // the function's name was changed following code review
783 
784  // This function determines
785  // current AOI grid parameters.
786 
787  // Definitions;
788 
789  // i0: Worldwide row index
790  // for geoid height post that's
791  // near the ground point of interest.
792  // j0: Worldwide column index
793  // for geoid height post that's
794  // near the ground point of interest.
795 
796  // THRESHOLD: Compute the E/W horizontal
797  // distance between grid columns
798  // a) at the equator (ewDelta0) and
799  // b) at the latitude of interest (ewDelta);
800  // then form the ratio ( ewDelta / ewDelta0 );
801  // THRESHOLD is the minimum allowable E/W ratio,
802  // where ratio controls the # of AOI grid columns.
803 
804  // Thread locks are not needed here, because this function
805  // can only be invoked by the bicubic spline geoidHeight function;
806  // the thread locks reside in the bicubic spline's geoidHeight function.
807 
808  try {
809 
810  const double THRESHOLD = 0.05; // unitless
811 
812  int status;
813 
814  double cLat;
815  double eSquared;
816  double ewDelta;
817  double ewDelta0;
818  double latitude;
819  double longitude;
820  double nRadius;
821  double ratio;
822  double sLat;
823  double temp;
824 
825  // INITIALIZE .....
826 
827  // Compute latitude & longitude
828  // corresponding to indices i0 & j0 .....
829 
830  status =
831  this->loadGridCoords(
832  i0, j0, latitude, longitude );
833 
834  if ( status != 0 ) return( 1 );
835 
836  cLat = cos( latitude );
837  sLat = sin( latitude );
838 
839  // Compute earth ellipsoid's
840  // radius of curvature in the prime vertical .....
841 
842  eSquared = FLATTENING * ( 2.0 - FLATTENING );
843 
844  temp = 1.0 - eSquared * sLat * sLat;
845 
846  nRadius = SEMI_MAJOR_AXIS / sqrt( temp );
847 
848  // Set AOI grid's number of rows and columns .....
849 
850  ewDelta0 = SEMI_MAJOR_AXIS * _dLon;
851 
852  ewDelta = nRadius * _dLon * cLat;
853 
854  ratio = ewDelta / ewDelta0;
855 
856  if ( ratio < THRESHOLD ) ratio = THRESHOLD;
857 
858  _nAoiCols = int( double( _nomAoiCols ) / ratio );
859 
860  _nAoiCols = 2 * ( _nAoiCols / 2 ); // These both
861  _nAoiRows = 2 * ( _nomAoiRows / 2 ); // become even numbers
862 
863  // SET AOI GRID'S ABSOLUTE LOCATION .....
864 
865  // (indices are referenced to the worldwide grid)
866 
867  _minAoiRowIndex = i0 - (( _nAoiRows - 2 ) / 2 ) + 1;
869 
870  _minAoiColIndex = j0 - (( _nAoiCols - 2 ) / 2 ) + 1;
872 
873  // MOVE AOI GRID IF IT EXTENDS
874  // BEYOND A WORLDWIDE GRID BOUNDARY .....
875 
876  if ( _minAoiRowIndex < 0 )
877  {
878  _minAoiRowIndex = 0;
879  _maxAoiRowIndex = _minAoiRowIndex + _nAoiRows - 1;
880  }
881 
882  if ( _maxAoiRowIndex >= _nGridRows )
883  {
885  _minAoiRowIndex = _maxAoiRowIndex - _nAoiRows + 1;
886  }
887 
888  if ( _minAoiColIndex < 0 )
889  {
890  _minAoiColIndex = 0;
891  _maxAoiColIndex = _minAoiColIndex + _nAoiCols - 1;
892  }
893 
894  if ( _maxAoiColIndex >= _nGridCols )
895  {
897  _minAoiColIndex = _maxAoiColIndex - _nAoiCols + 1;
898  }
899 
900  } // End of exception's try block
901 
902  catch ( ... ) { return( 1 ); }
903 
904  return( 0 ); // Normal return flag
905 
906 } // End of function Egm2008AoiGrid::loadAoiParms
907 
908 
909 // ******************************
910 // * Read reformatted version *
911 // * of NGA's geoid-height grid *
912 // ******************************
913 
914 int
916 {
917  // November 19, 2010: Version 1.00
918  // February 11, 2011: Version 2.00:
919  // the function's name was changed following code review
920 
921  // This function reads
922  // AOI geoid heights from NGA's
923  // reformatted EGM 2008 geoid height file.
924 
925  // The grid data is arranged in latitude rows,
926  // with the northernmost rows first, and with the
927  // geoid heights arranged west-to-east within each row.
928 
929  // Thread locks are not needed here, because this function
930  // can only be invoked by the bicubic spline geoidHeight function;
931  // the thread locks reside in the bicubic spline's geoidHeight function.
932 
933  try {
934 
935  int endRow;
936  int i;
937  int index0;
938  int index1;
939  int index2;
940  int kount;
941  int startCol;
942  int startRow;
943 
944  std::ifstream fin;
945 
946  // INITIALIZE .....
947 
948  fin.open(
949  _gridFname.c_str(),
950  std::ifstream::binary | std::ifstream::in );
951 
952  if ( fin.fail() ) return( 1 );
953 
954  delete[] _heightGrid; _heightGrid = NULL;
955 
956  _heightGrid = new float[ _nAoiRows * _nAoiCols ];
957 
958  if ( NULL == _heightGrid ) return ( 1 );
959 
960  // READ THE AOI GRID's GEOID HEIGHTS .....
961 
962  index0 = 0; // Starting AOI grid locaion
963 
964  startRow = _maxAoiRowIndex;
965  endRow = _minAoiRowIndex;
966 
967  index1 = // Starting worldwide file location (bytes)
968  BYTES_IN_HEADER +
969  BYTES_PER_FLOAT *
970  ( _minAoiColIndex +
971  ( _nGridRows - startRow - 1 ) * _nGridCols );
972 
973  for ( i = startRow; i >= endRow; i-- )
974  {
975  // Read row's geoid heights .....
976 
977  fin.seekg( index1 );
978 
979  fin.read(
980  (char*) ( &_heightGrid[ index0 ] ),
981  ( _nAoiCols * BYTES_PER_FLOAT ));
982 
983  if ( fin.fail() ) { fin.close(); return( 1 ); }
984 
985  // If needed, convert to LITTLE-ENDIAN format .....
986 
987  #if LITTLE_ENDIAN
988 
989  this->swapBytes(
990  &_heightGrid[ index0 ],
991  BYTES_PER_FLOAT, _nAoiCols );
992 
993  #endif
994 
995  index0 += _nAoiCols;
996  index1 += ( BYTES_PER_FLOAT * _nGridCols );
997  }
998 
999  fin.close();
1000 
1001  } // End of exceptions' try block
1002 
1003  catch ( ... ) { return( 1 ); }
1004 
1005  return ( 0 ); // Normal-return flag
1006 
1007 } // End of function Egm2008AoiGrid::loadGrid
1008 
1009 
1010 // *******************************
1011 // * Read header's metadata from *
1012 // * reformatted version *
1013 // * of NGA's geoid-height grid *
1014 // *******************************
1015 
1016 int
1018 {
1019  // November 19, 2010: Version 1.00
1020  // February 11, 2011: Version 2.00:
1021  // the function's name was changed following code review
1022  // June 4, 2013: Version 2.01
1023 
1024  // This function reads
1025  // the grid metadata from NGA's
1026  // reformatted EGM 2008 geoid height file.
1027 
1028  // THE GRID METADATA OCCUPIES 28 BYTES AT THE
1029  // BEGINNING OF THE REFORMATTED WORLDWIDE FILE.
1030 
1031  // Thread locks are not needed here, because this
1032  // function can only be invoked by the constructor;
1033  // thread locks / unlocks reside in the constructors.
1034 
1035  try {
1036 
1037  double ewDelta;
1038 
1039  std::ifstream fin;
1040 
1041  // INITIALIZE .....
1042 
1043  fin.open(
1044  _gridFname.c_str(),
1045  std::ifstream::binary | std::ifstream::in );
1046 
1047  if ( fin.fail() ) { return( 1 ); }
1048 
1049  // READ AND STORE HEADER .....
1050 
1051  fin.read((char*) &_nGridPad, BYTES_PER_INT );
1052 
1053  if ( fin.fail() ) { fin.close(); return( 1 ); }
1054 
1055  fin.read((char*) &_nOrigRows, BYTES_PER_INT );
1056 
1057  if ( fin.fail() ) { fin.close(); return( 1 ); }
1058 
1059  fin.read((char*) &_nOrigCols, BYTES_PER_INT );
1060 
1061  if ( fin.fail() ) { fin.close(); return( 1 ); }
1062 
1063  fin.read((char*) &_dLat, BYTES_PER_DOUBLE );
1064 
1065  if ( fin.fail() ) { fin.close(); return( 1 ); }
1066 
1067  fin.read((char*) &_dLon, BYTES_PER_DOUBLE );
1068 
1069  if ( fin.fail() ) { fin.close(); return( 1 ); }
1070 
1071  fin.close();
1072 
1073  #if LITTLE_ENDIAN
1074 
1075  this->swapBytes( &_nGridPad, BYTES_PER_INT, 1 );
1076  this->swapBytes( &_nOrigRows, BYTES_PER_INT, 1 );
1077  this->swapBytes( &_nOrigCols, BYTES_PER_INT, 1 );
1078  this->swapBytes( &_dLat, BYTES_PER_DOUBLE, 1 );
1079  this->swapBytes( &_dLon, BYTES_PER_DOUBLE, 1 );
1080 
1081  #endif
1082 
1083  _dLat *= RAD_PER_DEG; // grid file stores these in degrees
1084  _dLon *= RAD_PER_DEG; // grid file stores these in degrees
1085 
1086  // SET DERIVED METADATA .....
1087 
1088  _nGridRows = _nOrigRows + ( 2 * _nGridPad );
1089  _nGridCols = _nOrigCols + ( 2 * _nGridPad ) + 1;
1090 
1091  ewDelta = _dLon * SEMI_MAJOR_AXIS;
1092 
1093  _nomAoiCols = 1 + int( 150.0 * MTR_PER_NM / ewDelta );
1094 
1095  if( _nomAoiCols < 25 ) _nomAoiCols = 25;
1096  if( _nomAoiCols > _nOrigCols/40 ) _nomAoiCols = _nOrigCols / 40;
1097 
1099 
1100  _baseLatitude =
1101  -PIDIV2 - _dLat * double( _nGridPad ); // radians
1102  _baseLongitude =
1103  0.0 - _dLon * double( _nGridPad ); // radians
1104 
1105  } // End of exceptions' try block
1106 
1107  catch ( ... ) { return( 1 ); }
1108 
1109  return( 0 ); // Normal-return flag
1110 
1111 } // End of function Egm2008AoiGrid::loadGridMetadata
1112 
1113 // CLASSIFICATION: UNCLASSIFIED
1114