UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
egm2008_full_grid_package.cpp
Go to the documentation of this file.
1 
2 // CLASSIFICATION: UNCLASSIFIED
3 
5 // //
6 // File name: egm2008_full_grid_package.cpp //
7 // //
8 // Description of this module: //
9 // Utility software that interpolates EGM 2008 //
10 // geoid heights from one of NGA's geoid height grids. //
11 // //
12 // This interpolator loads the worldwide EGM 2008 grid upon //
13 // instantiation, and it interpolates from the worldwide grid. //
14 // //
15 // This interpolator gives exactly the same results as //
16 // the companion egm2008_aoi_grid_package's interpolator. //
17 // However, this interpolator is faster when //
18 // users are requesting tens of thousands of geoid //
19 // heights at widely dispersed horizontal locations. //
20 // //
21 // Revision History: //
22 // Date Name Description //
23 // ----------- ------------ ----------------------------------------------//
24 // 19 Nov 2010 RD Craig Release //
25 // 27 Jan 2011 S. Gillis BAEts28121, Terrain Service rearchitecture //
26 // 30 May 2013 RD Craig MSP 1.3: ER29758 //
27 // Added second constructor to //
28 // permit multiple geoid-height grids //
29 // when assessing relative interpolation errors. //
30 // //
32 
33 // This file contains definitions
34 // for functions in the Egm2008FullGrid class.
35 
36 #include <fstream>
37 
38 #ifdef IRIXN32
39 #include <math.h>
40 #else
41 #include <cmath>
42 #endif
43 
44 #include "CCSThreadLock.h"
46 
48 
49 using namespace MSP;
50 
51 namespace
52 {
53  const int BYTES_PER_DOUBLE = sizeof( double );
54  const int BYTES_PER_FLOAT = sizeof( float );
55  const int BYTES_PER_INT = sizeof( int );
56 
57  const double EPSILON = 1.0e-15; // ~4 times machine epsilon
58 
59  const double PI = 3.14159265358979323;
60 
61  const double PIDIV2 = PI / 2.0;
62  const double TWOPI = 2.0 * PI;
63 
64  const double DEG_PER_RAD = 180.0 / PI;
65  const double RAD_PER_DEG = PI / 180.0;
66 }
67 
68 // ***************************
69 // ** Public user functions **
70 // ***************************
71 
72 // ***************************************
73 // * Default Egm2008FullGrid constructor *
74 // ***************************************
75 
77 
78 // : Egm2008GeoidGrid() // base class initializer
79 {
80  // November 19, 2010: Version 1.00
81  // February 11, 2011: Version 1.01
82 
83  // This function implements the
84  // default Egm2008FullGrid constructor.
85 
86  int status;
87 
88  // The base class constructor
89  // initialized most data members.
90 
91  // Initialize grid pointer for proper
92  // operation of derived-class functions .....
93 
94  _heightGrid = NULL;
95 
96  // Read entire worldwide EGM 2008 grid here .....
97 
98  status = this->loadGrid();
99 
100  if ( status != 0 )
101  {
103  "Error: Egm2008GeoidGrid: constructor failed.");
104  }
105 
106 } // End of default Egm2008FullGrid constuctor
107 
108 
109 // *******************************************
110 // * Non-default Egm2008FullGrid constructor *
111 // *******************************************
112 
114  const std::string &gridFname ) // input
115 
116 : Egm2008GeoidGrid( gridFname ) // base class initializer
117 {
118  // November 19, 2010: Version 1.00
119  // February 11, 2011: Version 1.01
120  // May 30, 2013: Version 2.00
121 
122  // This function implements a
123  // non-default Egm2008FullGrid constructor.
124 
125  // Definition:
126 
127  // gridFname: The support geoid-height grid's file name; this
128  // file name should not contain the directory path;
129  // the base-class constructor will prepend the
130  // path specified by environment variable MSPCCS_DATA.
131 
132  int status;
133 
134  // The base class constructor
135  // initialized most data members.
136 
137  // Initialize grid pointer for proper
138  // operation of derived-class functions .....
139 
140  _heightGrid = NULL;
141 
142  // Read entire worldwide EGM 2008 grid here .....
143 
144  status = this->loadGrid();
145 
146  if ( status != 0 )
147  {
149  "Error: Egm2008GeoidGrid: constructor failed.");
150  }
151 
152 } // End of default Egm2008FullGrid constuctor
153 
154 
155 // ************************************
156 // * Egm2008FullGrid copy constructor *
157 // ************************************
158 
160  ( const Egm2008FullGrid& oldGrid ) // input
161 
162 : Egm2008GeoidGrid( oldGrid ) // base class initializer
163 
164 {
165  // November 19, 2010: Version 1.00
166 
167  int i;
168  int kount;
169 
170  // This function implements the
171  // Egm2008FullGrid copy constructor.
172 
173  // The base class copy
174  // constructor handles many data members .....
175 
176  // Definition:
177 
178  // oldGrid: The Egm2008FullGrid object being copied.
179 
180  // Copy the worldwide geoid separations .....
181 
182  try
183  {
184  _heightGrid = NULL;
185 
186  kount = _nGridRows * _nGridCols;
187 
188  _heightGrid = new float[ kount ];
189 
190  for ( i = 0; i < kount; i++ )
191  {
192  _heightGrid[ i ] = oldGrid._heightGrid[ i ];
193  }
194  }
195  catch (...)
196  {
197  oldGrid._mutex.unlock();
198 
200  "Error: Egm2008GeoidGrid: copy contructor failed");
201  }
202 
203  oldGrid._mutex.unlock(); // Use CCSThreadMutex function in copy constructors
204 
205 } // End of Egm2008FullGrid copy constuctor
206 
207 
208 // ******************************
209 // * Egm2008FullGrid destructor *
210 // ******************************
211 
213 {
214  // November 19, 2010: Version 1.00
215 
216  // This function implements
217  // the Egm2008FullGrid destructor.
218 
219  delete[] _heightGrid;
220 
221 } // End of Egm2008FullGrid destructor
222 
223 
224 // ***************************************
225 // * Egm2008FullGrid assignment operator *
226 // ***************************************
227 
230 {
231  // November 19, 2010: Version 1.00
232 
233  // This function implements the
234  // Egm2008FullGrid assignment operator.
235 
236  // Definition:
237 
238  // oldGrid: The Egm2008FullGrid object being assigned.
239 
240  int i;
241  int kount;
242 
243  // This function implements
244  // the Egm2008FullGrid assignment operator.
245 
246  if ( this == & oldGrid ) return ( *this );
247 
248  // Assign base class data members .....
249 
250  Egm2008GeoidGrid::operator= ( oldGrid );
251 
252  // Assign the worldwide geoid separations .....
253 
254  try
255  {
256  delete[] _heightGrid; _heightGrid = NULL;
257 
258  kount = _nGridRows * _nGridCols;
259 
260  _heightGrid = new float[ kount ];
261 
262  for ( i = 0; i < kount; i++ )
263  {
264  _heightGrid[ i ] = oldGrid._heightGrid[ i ];
265  }
266  }
267  catch (...)
268  {
269  _mutex.unlock();
270  oldGrid._mutex.unlock();
271 
273  "Error: Egm2008GeoidGrid: assignment operator failed");
274  }
275 
276  _mutex.unlock(); // Use CCSThreadMutex function in assignment operations
277  oldGrid._mutex.unlock();
278 
279  return( *this );
280 
281 } // End of Egm2008FullGrid assignment operator
282 
283 
284 // *************************************************
285 // * Find geoid height via 2D spline interpolation *
286 // *************************************************
287 
288 int
290  int wSize, // input
291  double latitude, // input
292  double longitude, // input
293  double& gHeight ) // output
294 {
295  // November 19, 2010: Version 1.00
296 
297  // This function computes
298  // geoid heights from a reformatted
299  // version of NGA's worldwide geoid-height grid.
300  // It primarily uses BICUBIC-SPLINE INTERPOLATION,
301  // but it uses bilinear interpolation for small windows.
302 
303  // Definitions:
304 
305  // gHeight: Interpolated geoid height (meters).
306  // latitude: Geodetic latitude (radians) at
307  // which geoid height is to be interpolated.
308  // longitude: Geodetic longitude (radians) at
309  // which geoid height is to be interpolated.
310  // wSize: The number of grid points
311  // along each edge of the interpolation
312  // window that surrounds the point of interest;
313  // the interpolation window has wSize**2 grid points;
314  // if wSize < 3, then the geoid height
315  // is computed using bilinear interpolation; the
316  // maximum allowable wSize is twenty grid points;
317  // typical window sizes are 4x4, 6x6, 8,8 etc;
318  // EGM 2008 interpolations normally use wSize = 6.
319 
320  // i: Grid's latitude index.
321  // j: Grid's longitude index.
322  // oddSize: A flag indicating whether the
323  // local interpolation grid's number
324  // of (rows = column) is even or odd;
325  // oddSize = true .....
326  // the number of rows = columns is odd,
327  // where 3x3, 5x5, 7x7, 9x9, etc are
328  // examples of grids with oddSize = true,
329  // oddSize = false .....
330  // the number of rows = columns is even,
331  // where 4x4, 6x6, 8x8, 10x10, etc are
332  // examples of grids with oddSize = false.
333  // The associated indexing logic has two goals:
334  // 2) if oddSize = true, select the
335  // the local grid so that the point of
336  // interest lies near the grid's center,
337  // 2) if oddSize = false, select the
338  // the local grid so that the point of
339  // interest lies in the grid's central cell.
340  // When oddSize = false, the interpolator will
341  // obtain a numerically-most-trustworthy solution.
342 
343  try {
344 
345 // MSP::CCSThreadLock aLock( &_mutex ); // copy constructor locks thread
346 
347  const int TWENTY = 20;
348 
349  bool oddSize;
350 
351  int i;
352  int i0;
353  int iIndex;
354  int iMin;
355  int j;
356  int j0;
357  int jIndex;
358  int jMin;
359  int kIndex;
360  int offset;
361  int status;
362 
363  double latIndex;
364  double lonIndex;
365  double temp;
366 
367  double latSupport[ TWENTY ];
368  double lonSupport[ TWENTY ];
369  double moments [ TWENTY ];
370 
371  // EDIT THE INPUT AND INITIALIZE .....
372 
373  gHeight = 0.0;
374 
375  if ( TWENTY != MAX_WSIZE ) return( 1 );
376 
377  if ( wSize > MAX_WSIZE ) wSize = MAX_WSIZE;
378 
379  if (( latitude < -PIDIV2 ) || ( latitude > PIDIV2 )) return( 1 );
380 
381  // Rationalize the longitude .....
382 
383  while ( longitude < 0.0 ) longitude += TWOPI;
384  while ( longitude > TWOPI ) longitude -= TWOPI;
385 
386  for (i = 0; i < TWENTY; i++)
387  {
388  latSupport[ i ] = lonSupport[ i ] = moments[ i ] = 0.0;
389  }
390 
391  // If window size is less than three, compute
392  // the geoid height using bilinear interpolation .....
393 
394  // (executes only when the interpolation window
395  // is too small for bicubic spline interpolation)
396 
397  if ( wSize < 3 )
398  {
399  status =
400  this->geoidHeight( latitude, longitude, gHeight );
401 
402  if ( status != 0 ) return( 1 );
403 
404  ; /* Normal bilinear interpolation return */ return( 0 );
405  }
406 
407  // Compute indices to the window's grid points .....
408 
409  // Roughly center the
410  // interpolation window at the station .....
411 
412  latIndex =
413  double( _nGridPad ) + ( latitude + PIDIV2 ) / _dLat;
414  lonIndex =
415  double( _nGridPad ) + ( longitude - 0.0 ) / _dLon;
416 
417  oddSize = ( wSize != (( wSize / 2 ) * 2 ));
418 
419  if ( oddSize ) {
420  i0 = int( latIndex + 0.5 );
421  j0 = int( lonIndex + 0.5 );
422 
423  iMin = i0 - ( wSize / 2 );
424  jMin = j0 - ( wSize / 2 );
425  }
426  else {
427  i0 = int( latIndex );
428  j0 = int( lonIndex );
429 
430  iMin = i0 - ( wSize / 2 ) + 1;
431  jMin = j0 - ( wSize / 2 ) + 1;
432  }
433 
434  // COMPUTE BI-CUBIC SPLINE INTERPOLATION .....
435 
436  // Compute interpolation window's
437  // relative column coordinate at which the
438  // first set of geoid heights will be interpolated ....
439 
440  temp =
441  lonIndex - double( jMin ); // 0 <= temp <= (wSize - 1)
442 
443  // Interpolate a synthetic geoid height
444  // for each row within the interpolation window .....
445 
446  for ( i = 0; i < wSize; i++ )
447  {
448  iIndex = iMin + i;
449 
450  offset =
451  ( _nGridRows - iIndex - 1 ) * _nGridCols;
452 
453  // Load interpolation window's
454  // i-th row of tabulated geoid heights .....
455 
456  for ( j = 0; j < wSize; j++ )
457  {
458  jIndex = jMin + j;
459 
460  kIndex = jIndex + offset;
461 
462  lonSupport[j] = _heightGrid[ kIndex ];
463  }
464 
465  // Compute moments,
466  // interpolate the i-th row's geoid
467  // height at the longitude of interest,
468  // then load the i-th latitude support point .....
469 
470  status =
471  this->initSpline( wSize, lonSupport, moments );
472 
473  if ( status != 0 ) return( 1 );
474 
475  latSupport[i] =
476  this->spline( wSize, temp, lonSupport, moments );
477  }
478 
479  // Compute interpolation window's
480  // relative row coordinate at which the
481  // final geoid height will be interpolated .....
482 
483  temp =
484  latIndex - double( iMin ); // 0 <= temp <= (wSize - 1)
485 
486  // Compute moments, and interpolate final
487  // geoid height at the latitude of interest .....
488 
489  status =
490  this->initSpline( wSize, latSupport, moments );
491 
492  if ( status != 0 ) return( 1 );
493 
494  gHeight =
495  this->spline( wSize, temp, latSupport, moments );
496 
497  // The thread is unlocked by aLock's destructor;
498  // this occurs even when exceptions are thrown.
499 
500  } // End of exceptions' try block
501 
502  catch ( ... ) { gHeight = 0.0; return( 1 ); }
503 
504  return( 0 ); // Normal-return flag
505 
506 } // End of function Egm2008FullGrid::geoidHeight
507 
508 
509 // **********************
510 // ** Hidden functions **
511 // **********************
512 
513 // **************************************
514 // * Interpolate EGM 2008 geoid heights *
515 // **************************************
516 
517 int
519  double latitude, // input
520  double longitude, // input
521  double& gHeight ) // output
522 {
523  // November 19, 2010: Version 1.00
524 
525  // This function, which is
526  // exercised only when wSize < 3, uses
527  // bilinear interpolation to find geoid heights.
528 
529  // Definitions:
530 
531  // gHeight: Interpolated geoid height (meters).
532  // latitude: GEODETIC latitude at which nominal
533  // geoid height is to be computed (radians).
534  // longitude: GEODETIC longitude at which nominal
535  // geoid height is to be computed (radians).
536 
537  // wSize: The local interpolation window's size:
538  // normally wSize = 6 for EGM 2008 interpolations.
539 
540  // Thread locks are not needed here, because this function
541  // can only be invoked from the bicubic spline geoidHeight function; the
542  // thread locks, if any, reside in the bicubic spline's geoidHeight function.
543 
544  try {
545 
546  int i1;
547  int i2;
548  int i3;
549  int i4;
550  int index;
551  int j1;
552  int j2;
553  int j3;
554  int j4;
555  int status;
556 
557  double a0;
558  double a1;
559  double a2;
560  double a3;
561  double lat1;
562  double lon1;
563  double n1;
564  double n2;
565  double n3;
566  double n4;
567  double x;
568  double y;
569 
570  // EDIT THE INPUT AND INITIALIZE .....
571 
572  gHeight = 0.0;
573 
574  if (( latitude < -PIDIV2 ) || ( latitude > PIDIV2 )) return( 1 );
575 
576  while ( longitude < 0.0 ) longitude += TWOPI;
577  while ( longitude > TWOPI ) longitude -= TWOPI;
578 
579  // COMPUTE THE SURROUNDING GRID POINTS' INDICES .....
580 
581  status =
582  this->swGridIndices
583  ( latitude, longitude, i1, j1 );
584 
585  if ( status != 0 ) return( 1 );
586 
587  i2 = i1;
588  j2 = j1 + 1;
589 
590  i3 = i1 + 1;
591  j3 = j2;
592 
593  i4 = i3;
594  j4 = j1;
595 
596  // GET THE SURROUNDING GRID POINTS' GEOID HEIGHTS .....
597 
598  index =
599  j1 + ( _nGridRows - i1 - 1 ) * _nGridCols;
600  n1 = _heightGrid[ index ];
601 
602  index =
603  j2 + ( _nGridRows - i2 - 1 ) * _nGridCols;
604  n2 = _heightGrid[ index ];
605 
606  index =
607  j3 + ( _nGridRows - i3 - 1 ) * _nGridCols;
608  n3 = _heightGrid[ index ];
609 
610  index =
611  j4 + ( _nGridRows - i4 - 1 ) * _nGridCols;
612  n4 = _heightGrid[ index ];
613 
614  // INTERPOLATE GEOID HEIGHT AT THE POINT OF INTEREST .....
615 
616  // Set bilinear coefficients .....
617 
618  a0 = n1;
619  a1 = n2 - n1;
620  a2 = n4 - n1;
621  a3 = n1 + n3 - n2 - n4;
622 
623  // Set first ( S/W ) corner's geodetic coordinates .....
624 
625  lat1 =
626  _baseLatitude + _dLat * double( i1 ); // radians
627  lon1 =
628  _baseLongitude + _dLon * double( j1 ); // radians
629 
630  // Set point coordinates relative
631  // to the grid square's S/W corner .....
632 
633  // 0 <= x <= 1; 0 <= y <= 1.
634 
635  x = ( longitude - lon1 ) / _dLon;
636  y = ( latitude - lat1 ) / _dLat;
637 
638  // Perform the interpolation .....
639 
640  gHeight = a0 + a1 * x + a2 * y + a3 * x * y; // meters
641 
642  } // End of exceptions' try block
643 
644  catch ( ... ) { gHeight = 0.0; return( 1 ); }
645 
646  return( 0 ); // Normal-return flag
647 
648 } // End of function Egm2008FullGrid::geoidHeight
649 
650 
651 // ******************************
652 // * Read reformatted version *
653 // * of NGA's geoid-height grid *
654 // ******************************
655 
656 int
658 {
659  // November 19, 2010: Version 1.00
660  // February 11, 2011: Version 2.00:
661  // the function name changed after code review
662 
663  // This function reads a reformatted version
664  // of NGA's EGM 2008 worldwide geoid-height grid.
665 
666  // The grid data is arranged in latitude rows,
667  // with the northernmost rows first, and with the
668  // geoid heights arranged west-to-east within each row.
669 
670  // Thread locks are not needed here, because this
671  // function can only be invoked from the constructor;
672  // the thread locks reside in the constructor.
673 
674  try {
675 
676  int endRow;
677  int i;
678  int index0;
679  int index1;
680  int index2;
681  int j;
682  int k;
683  int kount;
684  int startRow;
685 
686  std::ifstream fin;
687 
688  // INITIALIZE .....
689 
690  fin.open(
691  _gridFname.c_str(),
692  std::ifstream::binary | std::ifstream::in );
693 
694  if ( fin.fail() ) return( 1 );
695 
696  // READ AND STORE HEADER .....
697 
698  fin.read((char*) &_nGridPad, BYTES_PER_INT );
699 
700  if (fin.fail()) { fin.close(); return( 1 ); }
701 
702  fin.read((char*) &_nOrigRows, BYTES_PER_INT );
703 
704  if (fin.fail()) { fin.close(); return( 1 ); }
705 
706  fin.read((char*) &_nOrigCols, BYTES_PER_INT );
707 
708  if (fin.fail()) { fin.close(); return( 1 ); }
709 
710  fin.read((char*) &_dLat, BYTES_PER_DOUBLE );
711 
712  if (fin.fail()) { fin.close(); return( 1 ); }
713 
714  fin.read((char*) &_dLon, BYTES_PER_DOUBLE );
715 
716  if (fin.fail()) { fin.close(); return( 1 ); }
717 
718  // If needed, convert to LITTLE-ENDIAN representation .....
719 
720  #if LITTLE_ENDIAN
721 
722  this->swapBytes( &_nGridPad, BYTES_PER_INT, 1 );
723  this->swapBytes( &_nOrigRows, BYTES_PER_INT, 1 );
724  this->swapBytes( &_nOrigCols, BYTES_PER_INT, 1 );
725  this->swapBytes( &_dLat, BYTES_PER_DOUBLE, 1 );
726  this->swapBytes( &_dLon, BYTES_PER_DOUBLE, 1 );
727 
728  #endif
729 
730  _dLat *= RAD_PER_DEG; // grid file stores these in degrees
731  _dLon *= RAD_PER_DEG; // grid file stores these in degrees
732 
733  // Set derived parameters .....
734 
735  _nGridRows = _nOrigRows + ( 2 * _nGridPad );
736  _nGridCols = _nOrigCols + ( 2 * _nGridPad ) + 1;
737 
738  _baseLatitude =
739  -PIDIV2 - _dLat * double( _nGridPad ); // radians
741  0.0 - _dLon * double( _nGridPad ); // radians
742 
743  // READ AND STORE THE REFORMATTED GRID .....
744 
745  // (Recall that the reformatted grid
746  // a) doesn't have FORTRAN records' headers and trailers,
747  // b) does have padding around the original grid's periphery)
748 
749  _heightGrid = NULL;
750 
751  _heightGrid =
752  new float[ _nGridRows * _nGridCols ];
753 
754  if ( NULL == _heightGrid ) return( 1 );
755 
756  index0 = 0;
757 
758  startRow = _nGridRows - 1;
759  endRow = 0;
760 
761  for ( i = startRow; i >= endRow; i-- )
762  {
763  // Read row's geoid heights .....
764 
765  fin.read(
766  (char*) (&_heightGrid[ index0 ]),
767  ( _nGridCols * BYTES_PER_FLOAT ));
768 
769  if ( fin.fail() ) { fin.close(); return( 1 ); }
770 
771  // If needed, convert to LITTLE-ENDIAN representation .....
772 
773  #if LITTLE_ENDIAN
774 
775  this->swapBytes(
776  &_heightGrid[ index0 ], BYTES_PER_FLOAT, _nGridCols );
777 
778  #endif
779 
780  index0 += _nGridCols;
781  }
782 
783  fin.close();
784 
785  } // End of exceptions' try block
786 
787  catch ( ... ) { return( 1 ); }
788 
789  return ( 0 ); // Normal-return flag
790 
791 } // End of function Egm2008FullGrid::loadGrid
792 
793 // CLASSIFICATION: UNCLASSIFIED
794