UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
egm2008_geoid_grid.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
4 // //
5 // File name: egm2008_geoid_grid.cpp //
6 // //
7 // Description of this module: //
8 // //
9 // Utility software that interpolates EGM 2008 //
10 // geoid heights from one of NGA's geoid height grids. //
11 // //
12 // This base class supports two geoid height interpolators: //
13 // //
14 // 1) the first one reads the entire worldwide EGM2008 grid //
15 // before interpolating any geoid heights from the worldwide grid; //
16 // 2) the second one does not read geoid height data from //
17 // file until a user requests that a geoid height be computed; //
18 // the 2nd interpolator then reads an Area of Interest //
19 // (AOI) grid from file before interpolating from the AOI grid; //
20 // the Area of Interest grid is refreshed, if needed, //
21 // when users submit subsequent geoid height requests. //
22 // //
23 // Revision History: //
24 // Date Name Description //
25 // ----------- ------------ ----------------------------------------------//
26 // 19 Nov 2010 RD Craig Release //
27 // 27 Jan 2011 S. Gillis BAEts28121, Terrain Service rearchitecture //
28 // 17 May 2011 T. Thompson BAEts27393, let user know when problem //
29 // is due to undefined MSPCCS_DATA //
30 // 30 May 2013 RD Craig MSP 1.3: ER29758 //
31 // Added second constructor to //
32 // permit multiple geoid-height grids //
33 // when assessing relative interpolation errors. //
34 // //
36 
37 // THIS PACKAGE REQUIRES NGA's WORLDWIDE GEOID HEIGHT GRID. THIS GRID'S
38 // DIRECTORY PATH MUST BE SPECIFIED BY ENVIRONMENT VARIABLE "MSPCCS_DATA".
39 
40 // Note: Following common usage, this class uses the term
41 // "geoid height" to mean heights of the geoid above or
42 // below the earth ellipsoid. The GeoidLibrary class confuses
43 // the term "geoid height" with height of a point above or below the
44 // geoid: these heights are properly called elevations or heights-above-MSL.
45 
46 #include <fstream>
47 #include <iomanip> // DEBUG
48 #include <iostream> // DEBUG
49 #include <stdlib.h>
50 #include <string>
51 
52 #ifdef IRIXN32
53 #include <math.h>
54 #else
55 #include <cmath>
56 #endif
57 
58 /* DEBUG
59 
60 using std::ios;
61 
62 using std::cin;
63 using std::cout;
64 using std::endl;
65 using std::setprecision;
66 using std::setw;
67 
68 END DEBUG */
69 
70 
72 
73 #include "egm2008_geoid_grid.h"
74 
75 namespace {
76 
77  const double EPSILON = 1.0e-15; // ~4 times machine epsilon
78 
79  const double PI = 3.14159265358979323;
80 
81  const double PIDIV2 = PI / 2.0;
82  const double TWOPI = 2.0 * PI;
83 
84  const double DEG_PER_RAD = 180.0 / PI;
85  const double RAD_PER_DEG = PI / 180.0;
86 
87  const double SEMI_MAJOR_AXIS = 6378137.0; // WGS-84
88  const double FLATTENING = 1.0 / 298.257223563; // WGS-84
89 }
90 
91 using namespace MSP;
92 
93 // ***************************
94 // ** Public user functions **
95 // ***************************
96 
97 // ****************************************
98 // * Default Egm2008GeoidGrid constructor *
99 // ****************************************
100 
102 {
103  // November 19, 2010: Version 1.00
104 
105  // This function implements the
106  // default Egm2008GeoidGrid constructor.
107 
108  //_mutex.lock(); // Use CCSThreadMutex function in constructors
109 
110  int i = 0;
111  int length = 0;
112 
113  char* pathName = NULL;
114 
115  // Get reformatted geoid height grid's file name .....
116 #ifdef NDK_BUILD
117  pathName = "/data/data/com.baesystems.msp.geotrans/lib/";
118  _gridFname += pathName;
119  _gridFname += "libegm2008grd.so";
120 #else
121  pathName = getenv( "MSPCCS_DATA" );
122 
123  if ( NULL == pathName )
124  {
125  strcpy( pathName, "../../data" );
126  }
127 
128  length = strlen( pathName );
129 
130  for (i = 0; i < length; i++) _gridFname += pathName[i];
131 
132  _gridFname +=
133  "/Und_min2.5x2.5_egm2008_WGS84_TideFree_reformatted";
134 #endif
135 
136  // BAEts27393 Reverse the change for this DR for plug-in backward
137  // compatible with MSP 1.1, i.e. do not throw error when plug-in is
138  // dropped into MSP 1.1 where EGM2008 data file is not available.
139 
140  //int undefEnvVar = 0;
141  //FILE* fp = NULL; /* File pointer to file */
142  //
144  //char* fileName = new char[50];
145  //strcpy( fileName, "Und_min2.5x2.5_egm2008_WGS84_TideFree_reformatted" );
146  //
148  //char* dataPath = getenv( "MSPCCS_DATA" );
149  //if ( NULL == dataPath )
150  //{
151  // undefEnvVar = 1;
152  // strcpy( dataPath, "../../data" );
153  //}
154  //
156  //std::string pathName = dataPath;
157  //pathName.append("/");
158  //pathName.append(fileName);
159  //
161  //if ( ( fp = fopen( pathName.c_str(), "r" ) ) == NULL )
162  //{
163  // ErrorControl errCtrl;
164  // if ( 1 == undefEnvVar )
165  // {
166  // errCtrl.issueError( "Environment variable undefined: MSPCCS_DATA.",
167  // "Egm2008GeoidGrid::Egm2008GeoidGrid");
168  // }
169  // else
170  // {
171  // std::string errMsg = "Unable to open ";
172  // errMsg.append(pathName);
173  // errCtrl.issueError( errMsg, "Egm2008GeoidGrid::Egm2008GeoidGrid" );
174  // }
175  //}
176  //fclose( fp );
177 
179  //_gridFname = pathName.c_str();
180 
181 
182  // Initialize base grid parameters .....
183 
184  MAX_WSIZE = 20;
185 
186  _nGridPad = 0;
187  _nGridCols = 0;
188  _nGridRows = 0;
189  _nOrigCols = 0;
190  _nOrigRows = 0;
191 
192  _baseLatitude = 0.0;
193  _baseLongitude = 0.0;
194 
195  _dLat = 0.0;
196  _dLon = 0.0;
197 
198  // The thread will be unlocked
199  // by a derived class constructor.
200 
201 } // End of default Egm2008GeoidGrid constuctor
202 
203 
204 // ********************************************
205 // * Non-default Egm2008GeoidGrid constructor *
206 // ********************************************
207 
208 Egm2008GeoidGrid::Egm2008GeoidGrid( const std::string &gridFname )
209 {
210  // 30 May 2013: Version 1.00
211 
212  // This function implements a
213  // non-default Egm2008GeoidGrid constructor.
214 
215  // Definition:
216 
217  // gridFname: The geoid height grid's file name; this
218  // file name should not contain the directory path;
219  // this function will pre-pend the path
220  // specified by environment variable MSPCCS_DATA.
221 
222  int i = 0;
223  int length = 0;
224 
225  char* pathName = NULL;
226 
227  // Get reformatted geoid height grid's file name .....
228 #ifdef NDK_BUILD
229  pathName = "/data/data/com.baesystems.msp.geotrans/lib/";
230  _gridFname += pathName;
231  _gridFname += "libegm2008grd.so";
232 #else
233  pathName = getenv( "MSPCCS_DATA" );
234 
235  if ( NULL == pathName )
236  {
237  strcpy( pathName, "../../data" );
238  }
239 
240  length = strlen( pathName );
241 
242  for( i = 0; i < length; i++ ) _gridFname += pathName[ i ];
243 
244  _gridFname += '/';
245 
246  _gridFname += gridFname;
247 #endif
248 
249  // Initialize base grid parameters .....
250 
251  MAX_WSIZE = 20;
252 
253  _nGridPad = 0;
254  _nGridCols = 0;
255  _nGridRows = 0;
256  _nOrigCols = 0;
257  _nOrigRows = 0;
258 
259  _baseLatitude = 0.0;
260  _baseLongitude = 0.0;
261 
262  _dLat = 0.0;
263  _dLon = 0.0;
264 
265 } // End of non-default Egm2008GeoidGrid constructor
266 
267 
268 // *************************************
269 // * Egm2008GeoidGrid copy constructor *
270 // *************************************
271 
273  ( const Egm2008GeoidGrid& oldGrid ) // input
274 {
275  // November 19, 2010: Version 1.00
276 
277  // This function implements the
278  // Egm2008GeoidGrid copy constructor.
279 
280  // Definition:
281 
282  // oldGrid: The Egm2008GeoidGrid object being copied.
283 
284  oldGrid._mutex.lock(); // Use CCSThreadMutex function in copy constructors
285 
286  // Copy worldwide grid's file name .....
287 
288  _gridFname = oldGrid._gridFname;
289 
290  // Copy grid's parameters .....
291 
292  MAX_WSIZE = oldGrid.MAX_WSIZE;
293 
294  _nGridPad = oldGrid._nGridPad;
295 
296  _nGridRows = oldGrid._nGridRows;
297  _nGridCols = oldGrid._nGridCols;
298 
299  _nOrigRows = oldGrid._nOrigRows;
300  _nOrigCols = oldGrid._nOrigCols;
301 
302  _baseLatitude = oldGrid._baseLatitude;
303  _baseLongitude = oldGrid._baseLongitude;
304 
305  _dLat = oldGrid._dLat;
306  _dLon = oldGrid._dLon;
307 
308  // The oldGrid object will be unlocked
309  // by a derived class copy constructor.
310 
311 } // End of Egm2008GeoidGrid copy constuctor
312 
313 
314 // *******************************
315 // * Egm2008GeoidGrid destructor *
316 // *******************************
317 
319 {
320  // November 19, 2010: Version 1.00
321 
322  // This function implements
323  // the Egm2008GeoidGrid destructor.
324 
325  ;
326 
327 } // End of Egm2008GeoidGrid destructor
328 
329 
330 // ****************************************
331 // * Egm2008GeoidGrid assignment operator *
332 // ****************************************
333 
336 {
337  // November 19, 2010: Version 1.00
338 
339  // Definition:
340 
341  // oldGrid: The Egm2008GeoidGrid object being assigned.
342 
343  // This function implements the
344  // Egm2008GeoidGrid assignment operator.
345 
346  _mutex.lock(); // Use CCSThreadMutex function in assignments
347  oldGrid._mutex.lock();
348 
349  if ( this == & oldGrid ) return ( *this );
350 
351  // Assign worldwide grid's file name .....
352 
353  _gridFname = oldGrid._gridFname;
354 
355  // Assign grids' parameters .....
356 
357  MAX_WSIZE = oldGrid.MAX_WSIZE;
358 
359  _nGridPad = oldGrid._nGridPad;
360 
361  _nGridRows = oldGrid._nGridRows;
362  _nGridCols = oldGrid._nGridCols;
363 
364  _nOrigRows = oldGrid._nOrigRows;
365  _nOrigCols = oldGrid._nOrigCols;
366 
367  _baseLatitude = oldGrid._baseLatitude;
368  _baseLongitude = oldGrid._baseLongitude;
369 
370  _dLat = oldGrid._dLat;
371  _dLon = oldGrid._dLon;
372 
373  // The object will be unlocked by a
374  // derived class assignment operator.
375 
376  return( *this );
377 
378 } // End of Egm2008GeoidGrid assignment operator
379 
380 
381 // **********************
382 // ** Hidden functions **
383 // **********************
384 
385 // **************************************
386 // * Retrieve user-specified grid point *
387 // **************************************
388 
389 int
391  int i, // input
392  int j, // input
393  double& latitude, // output
394  double& longitude ) // output
395 {
396  // November 19, 2010: Version 1.00
397 
398  // This function retrieves geodetic
399  // coordinates corresponding to geoid height posts.
400 
401  // This function allows users to supply
402  // indices refering to the worldwide grid's pad region.
403 
404  // Definitions:
405 
406  // i: Worldwide grid post's row index.
407  // j: Worldwide grid post's column index.
408  // latitude: Grid point's geodetic latitude (radians).
409  // longitude: Grid point's geodetic longitude (radians).
410 
411  // Thread locks are not needed here, because this function
412  // can only be invoked from a bicubic spline geoidHeight function; the
413  // thread locks, if any, reside in the bicubic spline's geoidHeight function.
414 
415  try {
416 
417  const int LIMIT1 = _nGridPad;
418  const int LIMIT2 = _nGridRows - _nGridPad - 1;
419 
420  int k;
421 
422  // EDIT THE INPUT AND INITIALIZE .....
423 
424  if (( i < 0 ) || ( i >= _nGridRows )) return( 1 );
425 
426  while ( j < 0 ) j += _nGridCols;
427  while ( j >= _nGridCols ) j -= _nGridCols;
428 
429  // COMPUTE GEODETIC COORDINATES .....
430 
431  // Indices refer to Southern pad region .....
432 
433  if ( i < LIMIT1 )
434  {
435  latitude =
436  ( -PIDIV2 - _dLat * double( i - LIMIT1 ));
437 
438  k = j + ( _nOrigCols / 2 );
439 
440  if ( k >= _nGridCols ) k -= _nOrigCols;
441 
442  longitude =
443  ( _baseLongitude + _dLon * double( k ));
444  }
445 
446  // Indices refer to NGA grid region .....
447 
448  if (( i >= LIMIT1 ) && ( i <= LIMIT2 ))
449  {
450  latitude =
451  ( _baseLatitude + _dLat * double( i ));
452 
453  longitude =
454  ( _baseLongitude + _dLon * double( j ));
455  }
456 
457  // Indices refer to Northern pad region .....
458 
459  if ( i > LIMIT2 )
460  {
461  latitude =
462  ( PIDIV2 - _dLat * double( i - LIMIT2 ));
463 
464  k = j + ( _nOrigCols / 2 );
465 
466  if ( k >= _nGridCols ) k -= _nOrigCols;
467 
468  longitude =
469  ( _baseLongitude + _dLon * double( k ));
470  }
471 
472  } // End of exceptions' try block
473 
474  catch ( ... ) { return( 1 ); }
475 
476  return( 0 ); // Normal-return flag
477 
478 } // End of function Egm2008GeoidGrid::loadGridCoords
479 
480 
481 // **********************************************
482 // * Compute a one-dimensional spline's moments *
483 // **********************************************
484 
485 int
487  int n, // input
488  const double posts[], // input
489  double moments[] ) // output
490 {
491  // November 4, 2010: Version 1.00
492  // February 12, 2011: Version 2.00:
493  // new variable names following code review
494 
495  // Using equally-spaced abscissae, this
496  // function computes a one-dimensional spline's moments.
497 
498  // This function assumes that the endmost
499  // support points' second derivatives are zero.
500 
501  // Definitions:
502 
503  // moments: Spline moments (2nd derivatives)
504  // that are evaluated at the spline's support
505  // points; this array contains at least n elements.
506  // n: The spline's number of support points; the
507  // calling function ensures that 3 <= n <= 20.
508  // posts: An array containing the support points'
509  // ordinates; this array contains at least n elements.
510 
511  // w: An array that, on completion of the Gauss
512  // elimination step, contains the triangularized
513  // coefficient matrix' superdiagonal. Element w[0]
514  // DOES NOT contain a superdiagonal matrix element.
515 
516  // Thread locks are not needed here, because this function
517  // can only be invoked from a bicubic spline geoidHeight function;
518  // the thread locks reside in the bicubic spline's geoidHeight function.
519 
520  // This C++ function
521  // refines FORTRAN subroutine INITSP,
522  // which was originally written (July 1983)
523  // by Rene Forsberg, Institut Fur Erdmessung,
524  // Universitat Hannover, Federal Republic of Germany.
525 
526  // NGA used this FORTRAN function in
527  // its internal EGM 2008 interpolation software.
528 
529  // References:
530 
531  // Einfuhrung in die Numerische Mathematik, Band I,
532  // Josef Stoer,
533  // Springer-Verlag, Heidelberg, 1972, pp 82 und 86
534 
535  // Introduction to Numerical Analysis,
536  // Josef Stoer and Roland Bulirsch,
537  // Springer-Verlag, New York 1980,
538  // Sections 2.4.1 - 2.4.2 (pp 93 - 102)
539 
540  // Algorithm:
541 
542  // For the special case of equally-
543  // spaced abscissae, where the first and
544  // last moments are zero, the moments are found by
545  // solving the linear tridiagonal system of equations
546 
547  // .. .. .. .. .. ..
548  // : : : : : :
549  // : 2.0 0.0 : : m : : 0 :
550  // : : : 0 : : :
551  // : 0.5 2.0 0.5 : : m : : rhs :
552  // : : : 1 : : 1 :
553  // : 0.5 2.0 0.5 : : m : : rhs :
554  // : : : 2 : = : 2 :,
555  // : . : : : : :
556  // : . : : : : . :
557  // : . : : : : . :
558  // : 0.5 2.0 0.5 : : : : . :
559  // : : : : : :
560  // : 0.0 2.0 : : m : : 0 :
561  // :. .: :. n-1.: :. .:
562 
563  // where
564 
565  // 1) n is the number of support data points,
566 
567  // 2) m is the j-th support point's unknown second derivative,
568  // j
569 
570  // ..
571  // : 0.0, j = 0,
572  // :
573  // :
574  // 3) rhs = : 3 * (y - 2 y + y ), 1 <= j <= (n-2),
575  // j : j+1 j j-1
576  // :
577  // : 0.0, j = n - 1.
578  // :.
579 
580  // This system of equations is never singular,
581  // so tests for singularity are not necessary.
582 
583  // The coefficient matrix is tridiagonal,
584  // so system solution time is proportional
585  // to the number of moments being computed.
586 
587  try {
588 
589  const int TWENTY = 20;
590 
591  int k;
592 
593  double p;
594 
595  double w[ TWENTY ];
596 
597  // EDIT THE INPUT AND INITIALIZE .....
598 
599  if ( TWENTY != MAX_WSIZE ) return( 1 );
600 
601  if ( n < 3 ) return( 1 );
602 
603  w[ 0 ] = 0.0;
604  moments[ 0 ] = 0.0;
605 
606  // APPLY GAUSS ELIMINATION TO THE
607  // MOMENTS' TRI-DIAGONAL SYSTEM OF EQUATIONS .....
608 
609  for (k = 2; k < n; k++)
610  {
611  p = ( w[ k-2 ] / 2.0 ) + 2.0;
612  w[ k-1 ] = -0.5 / p;
613  moments[ k-1 ] =
614  (3.0 *
615  ( posts[ k ] - ( 2.0 * posts[ k-1 ]) +
616  posts[ k-2 ]) - ( moments[ k-2 ] / 2.0 )) / p;
617  }
618 
619  // SOLVE THE RESULTING BI-DIAGONAL SYSTEM .....
620 
621  moments[ n-1 ] = 0.0;
622 
623  for (k = n - 1; k >= 2; k--)
624  {
625  moments[ k-1 ] += ( w[ k-1 ] * moments[ k ]);
626  }
627 
628  } // End of exceptions' try block
629 
630  catch ( ... ) { return( 1 ); }
631 
632  return( 0 ); // Normal-return flag
633 
634 } // End of function Egm2008GeoidGrid::initSpline
635 
636 
637 // ***************************************************
638 // * Specialized one-dimensional spline interpolator *
639 // ***************************************************
640 
641 double
643  int n, // input
644  double x, // input
645  const double posts[], // input
646  const double moments[] ) // input
647 {
648  // November 4, 2010: Version 1.00
649  // February 12, 2011: Version 2.00:
650  // new variable names following code review
651 
652  // Using support points having
653  // equally-spaced abscissae, this function quickly
654  // evaluates a cubic spline at an abscissa of interest.
655 
656  // Definitions:
657 
658  // moments: Spline moments (2nd derivatives)
659  // that were evaluated at the spline's support
660  // points; this array contains at least n elements.
661  // n: The spline's number of support points; the
662  // calling function ensures that 3 <= n <= 20.
663  // posts: An array containing the support points'
664  // ordinates; this array has at least n elements.
665  // x: The interpolation argument;
666  // (x == 0.0) ==> x equals the first support abscissa,
667  // (x == double(n-1) ==> x equals the final support abscissa.
668 
669  // Thread locks are not needed here, because this function
670  // can only be invoked from a bicubic spline geoidHeight function;
671  // the thread locks reside in the bicubic spline's geoidHeight function.
672 
673  // This C++ function
674  // refines FORTRAN function SPLINE,
675  // which was originally written (June 1983)
676  // by Rene Forsberg, Institut Fur Erdmessung,
677  // Universitat Hannover, Federal Republic of Germany.
678 
679  // NGA used this FORTRAN function in
680  // its internal EGM 2008 interpolation software.
681 
682  // References:
683 
684  // Einfuhrung in die Numerische Mathematik, Band I,
685  // Josef Stoer,
686  // Springer-Verlag, Heidelberg, 1972, p 81
687 
688  // Introduction to Numerical Analysis,
689  // Josef Stoer and Roland Bulirsch,
690  // Springer-Verlag, New York 1980,
691  // Sections 2.4.1 and 2.4.2 (pp 93 - 102)
692 
693  // When (x < 0) or (x > n-1), this function linearly
694  // extrapolates the dependent variable from the nearest
695  // support points. Recall that the endpoints' moments are zero.
696 
697  double height;
698 
699  const double MIN_ABSCISSA = 0.0;
700 
701  int j;
702 
703  double dx;
704  double maxAbscissa;
705  double mJ;
706  double mJp1;
707 
708  // Edit the input and initialize .....
709 
710  if ( n < 3 )
711  {
713  "Error: Egm2008GeoidGrid::spline: not enough points");
714  }
715 
716  try {
717 
718  maxAbscissa = double( n - 1 );
719 
720  // Use one-dimensional extrapolation or
721  // interpolation to determine geoid height .....
722 
723  if ( x <= MIN_ABSCISSA ) // linear extrapolation
724  {
725  height =
726  posts[ 0 ] +
727  ( x - MIN_ABSCISSA ) *
728  ( posts[ 1 ] - posts[ 0 ] -
729  ( moments[ 1 ] / 6.0 ));
730  }
731  else if ( x >= maxAbscissa ) // linear extrapolation
732  {
733  height =
734  posts[ n-1 ] +
735  ( x - maxAbscissa ) *
736  ( posts[ n-1 ] - posts[ n-2 ] +
737  ( moments[ n-2 ] / 6.0 ));
738  }
739  else // cubic spline interpolation
740  {
741  j = int( floor( x ));
742 
743  dx = x - double( j );
744  mJ = moments[ j ];
745  mJp1 = moments[ j+1 ];
746 
747  height = // compute geoid height using Horner's rule
748  posts[ j ] +
749  dx * (( posts[ j+1 ] - posts[ j ] -
750  ( mJ / 3.0 ) - ( mJp1 / 6.0 )) +
751  dx * (( mJ / 2.0 ) +
752  dx * ( mJp1 - mJ ) / 6.0 ));
753  }
754 
755  } // End of exceptions' try block
756 
757  catch ( ... )
758  {
760  "Error: Egm2008GeoidGrid::spline");
761  }
762 
763  return ( height );
764 
765 } // End of function Egm2008GeoidGrid::spline
766 
767 
768 // **************
769 // * Swap bytes *
770 // **************
771 
772 void
774  void* buffer, // input & output
775  size_t size, // input
776  size_t count ) // input
777 {
778  // November 8, 2010: Version 1.00
779 
780  // This function swaps
781  // bytes when transforming between
782  // BIG-ENDIAN and LITTLE-ENDIAN binary formats.
783 
784  // Definitions:
785 
786  // buffer: Buffer containing binary numbers.
787  // count: Number of data items in the buffer.
788  // size: Size (bytes) of each buffer data item.
789 
790  // Thread locks are not needed here, because this function
791  // can only be invoked from a bicubic spline geoidHeight function;
792  // the thread locks reside in the bicubic spline's geoidHeight function.
793 
794  char temp;
795 
796  char* b = (char *) buffer;
797 
798  for ( size_t c = 0; c < count; c ++ )
799  {
800  for ( size_t s = 0; s < ( size / 2 ); s++ )
801  {
802  temp = b[ c * size + s ];
803  b[ c * size + s] = b[ c * size + size - s - 1 ];
804  b[ c * size + size - s - 1] = temp;
805  }
806  }
807 
808 } // End of function Egm2008GeoidGrid::swapBytes
809 
810 
811 // *************************************
812 // * Find Southwest grid-point indices *
813 // *************************************
814 
815 int
817  double latitude, // input
818  double longitude, // input
819  int& i, // output
820  int& j ) // output
821 {
822  // November 19, 2010: Version 1.00
823 
824  // Given geodetic latitude and longitude,
825  // this function finds indices to the nearest
826  // grid point TO THE SOUTHWEST of the input point.
827  // The indices refer to the padded version
828  // of NGA's worldwide worldwide geoid-height grid.
829 
830  // Definitions:
831 
832  // i: Worldwide grid's latitude index.
833  // j: Worldwide grid's longitude index.
834  // latitude: Input geodetic latitude (radians).
835  // longitude: Input geodetic longitude (radians).
836 
837  // Thread locks are not needed here, because this function
838  // can only be invoked from a bicubic spline geoidHeight function;
839  // the thread locks reside in the bicubic spline's geoidHeight function.
840 
841  // Edit the input and initialize .....
842 
843  try {
844 
845  double maxLatitude;
846 
847  maxLatitude =
848  ( _baseLatitude + double( _nGridRows - 1 ) * _dLat );
849 
850  if (( latitude < _baseLatitude ) ||
851  ( latitude > maxLatitude )) return( 1 );
852 
853  while ( longitude < 0.0 ) longitude += TWOPI;
854  while ( longitude > TWOPI ) longitude -= TWOPI;
855 
856  // Compute the indices .....
857 
858  i = _nGridPad + int(( latitude + PIDIV2 ) / _dLat );
859 
860  j = _nGridPad + int( longitude / _dLon );
861 
862  } // End of exceptions' try block
863 
864  catch ( ... ) { return( 1 ); }
865 
866  return( 0 ); // Normal-return flag
867 
868 } // End of function Egm2008GeoidGrid::swGridIndices
869 
870 // CLASSIFICATION: UNCLASSIFIED