OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
GEO_DEM.c
Go to the documentation of this file.
1 /*
2  * Contains: GEO_close_DEM(), GEO_DEMalloc(), GEO_initialize_DEM(),
3  * GEO_latlon2height(), and GEO_read_DEM().
4  *
5  * Revision History:
6  * $Log: GEO_DEM.c,v $
7  * Revision 6.4 2011/12/08 19:58:03 kuyper
8  * Corrected calculation of minimum latitude considered valid by the SDP
9  * Toolkit's DEM routines.
10  *
11  * Revision 6.3 2011/02/15 18:44:17 kuyper
12  * Changed to call GEO_get_geoid() whenever needed, rather than calling it only
13  * once and storing the value. This needs to be done, now that the geiod height
14  * is being bilinearly interpolated, and will therefore not be exactly
15  * constant.
16  *
17  * Revision 6.2 2010/12/14 22:01:54 kuyper
18  * Changed to return heights above ellipsoid, rather than geoid.
19  *
20  * Revision 6.1 2010/08/12 22:09:49 kuyper
21  * Changed to use 15 arc second DEM files, falling back to 30 arc second if
22  * necessary.
23  * Improved error messaging.
24  * Changed to use GEO_DEM.h
25  * Changed GEO_initialize_DEM() to return status codes.
26  *
27  * Revision 4.1 2003/08/11 15:40:07 vlin
28  * GEO_read_DEM(): Removed conversion of fill values, specified initialization.
29  *
30  * Revision 3.3 2002/06/17 20:07:20 kuyper
31  * Added messages to accompany all FAIL returns from GEO_read_DEM().
32  *
33  * Revision 3.2 2002/06/13 22:46:42 kuyper
34  * Removed unneeded NCSA acknowledgement
35  *
36  * Revision 3.1 2002/06/11 13:16:28 kuyper
37  * Increased the border size.
38  *
39  * Revision 2.0 1997/07/28 16:29:41 kuyper
40  * Merged together GEO_DEM.h, GEO_initialize_DEM.c, GEO_read_DEM.c, and
41  * GEO_latlon2height.c
42  * Added GEO_close_DEM().
43  * Total rewrite of GEO_intialize_DEM() and GEO_read_DEM() to make use of
44  * new Toolkit DEM utilities.
45  */
46 
47 #include <float.h>
48 #include <ctype.h>
49 #include <stddef.h>
50 #include "PGS_MODIS_35251.h"
51 #include "GEO_DEM.h"
52 #include "GEO_earth.h"
53 #include "smfio.h"
54 
55 /* Values from Toolkit DEM metadata used to identify spatial units. */
56 #define DEGREE_STRING "Decimal Degree (DD)"
57 #define METER_STRING "METERS"
58 #define DEMCACHE 50 /* number of entries for tile_cache_struct */
59 
60 /* Toolkit DEM interface parameters. */
61 /* PGSt_DEM_Tag DEM_resolutions[RESOLUTIONS] = {PGSd_DEM_15ARC, PGSd_DEM_30ARC}; */
62 PGSt_DEM_Tag DEM_resolutions[RESOLUTIONS] = {PGSd_DEM_30ARC};
65 
66 /* We may increase the number of layers, with time. */
67 #define LAYERS 1
68 static PGSt_integer DEM_layers[LAYERS]={PGSd_DEM_ELEV};
69 static PGSt_double fillValue[LAYERS]={-9999.0};
70 static PGSt_double offset[LAYERS]={0.0};
71 static PGSt_double scaling[LAYERS]={1.0};
72 
73 /* DEM tile parameters. */
74 static int num_rows; /* Number of tile rows. */
75 static int hor_size; /* Pixels per pixel row. */
76 static int ver_size; /* Number of pixel rows per tile. */
77 static int hor_start; /* Pixel column corresponding to corner_lon. */
78 static int ver_start; /* Pixel row corresponding to corner_lat. */
79 static int num_tiles; /* Total number of tiles in all rows. */
80 static double tile_ver_size=8; /* Latitude spacing of tiles (radians) */
81 /* Initialization of tile_ver_size and row_num_tiles avoids division by zero in
82  * GEO_read_DEM(), in case it is accidentally called without calling
83  * GEO_initialize_DEM().
84  */
85 
86 /* DEM file row indicies */
87 /* Number of first tile in each tile row. */
88 static short int row_start_tile[MAX_DEM_ROWS];
89 /* Number of tiles in each tile row. */
90 static short int row_num_tiles[MAX_DEM_ROWS]={1};
91 
92 /* DEM tile record parameters */
93 static short int *DEM_record; /* Indicates which index to use on
94  height_min and height_max for each tile. Obsolescent,
95  but used by GEO_latlon2height() as an error indicator. */
96 static short int *height_min; /* Minimum height within a tile. */
97 static short int *height_max; /* Maximum height within a tile. */
98 
99 /* Current tile parameters */
100 static short int *DEM_tile_data=NULL; /* Elevation (meters). */
101 static int current_tile=-1; /* Number of current tile. */
102 static double corner_lat; /* Latitude (radians) corresponding to ver_start */
103 static double corner_lon; /* Longitude (radians) corresponding to hor_start */
104 static double lat_inc=DEG2RAD/120.0; /* Radians of latitude between pixels */
105 static double lon_inc=DEG2RAD/120.0; /* Radians of Longitude between pixels */
106 
107 
108 typedef struct {
109  int tile;
110  int hor_size;
111  int ver_size;
114  double corner_lat;
115  double corner_lon;
116  short int *DEM_tile_data;
118 
120 unsigned short int tiles_cached=0;
121 
122 
124  static int firstRun = 1;
125  if(firstRun) {
126  firstRun = 0;
127  DEM_record = (short int*) calloc(MAX_DEM_TILES, sizeof(short int));
128  height_min = (short int*) calloc(MAX_DEM_TILES, sizeof(short int));
129  height_max = (short int*) calloc(MAX_DEM_TILES, sizeof(short int));
131  }
132 }
133 
134 PGSt_SMF_status GEO_initialize_DEM(
135  void
136 )
137 /*
138 !C*****************************************************************************
139 !Description:
140  Routine in earth location group of the Level-1A geolocation software to
141  open the DEM file and read the file parameters into global variables.
142 
143 !Input Parameters:
144  None
145 
146 !Output Parameters:
147  None
148 
149 Return parameter:
150  MODIS_E_GEO If any subroutine returns an error code
151  MODIS_E_DEM_METADATA If a DEM metadata field has an unexpected value
152  PGS_S_SUCCESS otherwise
153 
154 Externally defined:
155  File scope objects defined in GEO_DEM.c:
156  Inputs:
157  DEM_layers
158  DEM_resolutions "GEO_DEM.h"
159 
160  Outputs:
161  fillValue
162  height_max
163  height_min
164  lat_inc
165  lon_inc
166  num_rows
167  num_tiles
168  max_lon "GEO_DEM.h"
169  min_lat "GEO_DEM.h"
170  offset
171  row_num_tiles
172  row_start_tile
173  scaling
174  tile_ver_size
175 
176  Macros:
177  DEG2RAD "GEO_geo.h"
178  EQUATOR_TILES "GEO_DEM.c"
179  LAYERS "GEO_DEM.c"
180  MAX_DEM_ROWS "GEO_geo.h"
181  MAX_DEM_TILES "GEO_geo.h"
182  NO_DEM_DATA "GEO_geo.h
183  PGS_S_SUCCESS "PGS_SMF.h"
184  PGSd_DEM_30ARC "PGS_DEM.h"
185  PGSd_DEM_ELEV "PGS_DEM.h"
186  RESOLUTIONS "GEO_DEM.h"
187 
188 Functions called:
189  PGS_DEM_Open() - Opens Toolkit DEM files.
190  PGS_DEM_GetMetadata() - Gets metadata from DEM files.
191  modsmf() - writes status messages to log
192 
193 !Revision History:
194  See top of file for revision history.
195 
196 
197 !Requirements:
198  PR03-F-3.3.3-1
199 
200 
201 !Team-unique Header:
202  This software is developed by the MODIS Science Data Support
203  Team for the National Aeronautics and Space Administration,
204  Goddard Space Flight Center, under contract NAS5-32373.
205 
206 
207 !END*************************************************************************
208 */
209 /* Adjustable parameters chosen to minimize average I/O load. */
210 #define TILES_BASE 90.0
211 #define COSLAT_OFFSET 0.1059
212 
213 {
215 
216  /* Local variable declarations */
217  /* The first element of each pix*Info pair is the size of the pixels in
218  degrees. The second element is the offset of the pixel center (the point at
219  which the retrieved data is most relevant) from the NW corner of the pixel.
220  */
221  PGSt_double pixLatInfo[2]={1.0/120.0, 1.0/240.0};
222  PGSt_double pixLonInfo[2]={1.0/120.0, 1.0/240.0};
223  double top_lat; /* Latitude of tile edge farthest from equator */
224  double coslat; /* cos(top_lat) */
225  int row, tile; /* Loop indices. */
226  char positionUnits[30]="", dataUnits[30]="";
227  char msgbuf[sizeof(positionUnits)+sizeof(dataUnits)+64];
228  char *p1, *p2; /* First non-blank characters of Units strings*/
229  char filefunc[] = __FILE__ ", GEO_initialize_DEM";
230 
231  /* Begin program logic */
232  if(PGS_DEM_Open(DEM_resolutions, RESOLUTIONS, DEM_layers, LAYERS)
233  != PGS_S_SUCCESS)
234  {
235  sprintf(msgbuf, "PGS_DEM_Open(%d,%d)", RESOLUTIONS, LAYERS);
236  modsmf(MODIS_E_GEO, msgbuf, filefunc);
237 
238  return MODIS_E_GEO;
239  }
240 
241 
242  if(PGS_DEM_GetMetadata(DEM_resolutions[0], DEM_layers[0], pixLatInfo,
243  pixLonInfo, positionUnits, &scaling[0], &offset[0], &fillValue[0],
244  dataUnits, NULL, NULL) !=PGS_S_SUCCESS)
245  {
246  sprintf(msgbuf, "PGS_DEM_GetMetadata(%ld,%ld)",
247  (long)DEM_resolutions[0], (long)DEM_layers[0]);
248  modsmf(MODIS_E_GEO, msgbuf, filefunc);
249 
250  return FAIL;
251  }
252 
253  for(p1=positionUnits; p1 < positionUnits+sizeof(positionUnits) &&
254  isspace((int)*p1); p1++);
255  for(p2=dataUnits; p2 < dataUnits+sizeof(dataUnits) &&
256  isspace((int)*p2); p2++);
257 
258  if(strncmp(p1, DEGREE_STRING,
259  sizeof(positionUnits)-(size_t)(p1-positionUnits)) ||
260  strncmp(p2, METER_STRING, sizeof(dataUnits)-(size_t)(p2-dataUnits)) ||
261  fabs(scaling[0]-1.0) > DBL_EPSILON || fabs(offset[0]) > DBL_MIN )
262  {
263  sprintf(msgbuf, "\"%.*s\", \"%.*s\" scale:%g offset:%g",
264  (int)sizeof(positionUnits), positionUnits,
265  (int)sizeof(dataUnits), dataUnits, scaling[0], offset[0]);
266  modsmf(MODIS_E_DEM_METADATA, msgbuf, "GEO_DEM.c, GEO_initialize_DEM");
267 
268  return FAIL;
269  }
270 
271  /* Setting these variables here allows GEO_latlon2height() to remain
272  unchanged from version 1. */
273  lon_inc = pixLonInfo[0]*DEG2RAD;
274  lat_inc = -pixLatInfo[0]*DEG2RAD;
275  min_lat = pixLatInfo[0]*0.5 - 90.0;
276  max_lon = 180.0 - 0.5 * pixLonInfo[0];
277  num_rows = MAX_DEM_ROWS;
278  tile_ver_size = PGS_PI / (double)num_rows;
279 
280  row_start_tile[0] = 0;
281  for(row=0; row<num_rows; row++)
282  { /* Calculate number of tiles in row. */
283 
284  /* Calculate latitude of the tile edge farthest from equator. */
285  top_lat = -0.5*PGS_PI+(double)row*tile_ver_size;
286  if(top_lat>0.0)
287  top_lat += tile_ver_size;
288 
289  coslat = cos(top_lat);
290  /* Calculate the optimum number of tiles in this row. This formula is an
291  empirical fit to exact values calculated using numerical methods and
292  timing data from an SGI Power Challenger, running version 5.2 of the
293  Toolkit. For details, check "Derivation of Optimum Tile Layout" in the
294  Software Development Folder.*/
295  row_num_tiles[row] =
296  (short)floor(TILES_BASE*coslat/sqrt(coslat+COSLAT_OFFSET)+0.5);
297  /* GEO_latlon2height() will fail if there is not at least one tile in each
298  tile row, and may fail if there is more than one tile in a polar tile
299  row. */
300  if(row_num_tiles[row] < 1 || row==0 || row==num_rows-1)
301  row_num_tiles[row] = 1;
302 
303  if(row==num_rows-1)
304  num_tiles = row_start_tile[row]+row_num_tiles[row];
305  else
306  row_start_tile[row+1] = (short)(row_start_tile[row]+row_num_tiles[row]);
307  }
308 
309  for(tile=0; tile<num_tiles; tile++)
310  { /* Set values indicating no data. */
311  height_min[tile] = SHRT_MAX;
312  height_max[tile] = SHRT_MIN;
313  DEM_record[tile] = NO_DEM_DATA;
314  }
315 
316  return SUCCESS;
317 }
318 
319 /*============================================================================*/
320 
322  size_t size
323  )
324 
325 /*
326 !C*****************************************************************************
327 !Description:
328  Rourine in the DEM group of the Level-1A geolocation
329  software to allocate memory for new tiles, freeing older tiles
330  if necessary.
331 
332 !Input Parameters:
333  size Number of bytes to allocate
334 
335 !Output Parameters:
336  None
337 
338 Return parameter:
339  NULL If the requested memory could not be
340  allocated.
341  A pointer to the Otherwise.
342  allocated memory.
343 
344 Global variables:
345  unsigned tiles_cached
346  tile_cache_struct tile_cache
347 
348 Call functions:
349  malloc()
350  free()
351 Called by:
352  GEO_read_DEM
353 
354 !Revision History:
355  See top of file for revision history.
356 
357 !Requirements:
358  PR03-F-3.3.3-1
359 
360 !Team-unique Header:
361  This software is developed by the MODIS Science Data Support
362  Team for the National Aeronautics and Space Administration,
363  Goddard Space Flight Center, under contract NAS5-32373.
364 
365 !END*************************************************************************
366 */
367 {
369 
370  void *p;
371 
372  if( tiles_cached > DEMCACHE )
373  return NULL; /* This should never happen. */
374 
375  p = malloc(size);
376 
377  while( p == NULL && tiles_cached > 0 )
378  {
379 
380  tiles_cached--;
381 
382  free(tile_cache[tiles_cached].DEM_tile_data);
384 
385  p = malloc(size);
386 
387  }
388 
389  return p;
390 
391 }
392 
393 /*============================================================================*/
394 
396  double lat,
397  double lon,
398  int * const hgtmin,
399  int * const hgtmax
400 )
401 /******************************************************************************
402 !C
403 !Description:
404  Routine in Earth location group of the Level-1A geolocation software to
405  determine the DEM tile number for the input lat/lon, load the min, max
406  and flag and read the DEM record into a global variable.
407 
408 !Input Parameters:
409  lat the input latitude (radians)
410  lon the input longitude (radians)
411 
412 !Output Parameters:
413  hgtmin the minimum DEM height above the ellipsoid for the tile
414  (meters)
415  hgtmax the maximum DEM height above the ellipsoid for the tile
416  (meters)
417 
418 Return Values:
419  FAIL if NULL pointers are passed, or Toolkit DEM functions
420  or GEO_DEMalloc() fails, or if the Toolkit DEM
421  dataSize has changed.
422  SUCCESS otherwise
423 
424 Externally Defined:
425  File globals defined in GEO_initialize_DEM:
426  Input:
427  DEM_resolutions "GEO_DEM.h"
428  fillValue
429  lat_inc
430  lon_inc
431  num_rows
432  offset
433  row_start_tile
434  row_num_tiles
435  scaling
436  tile_ver_size
437  Input/Output:
438  DEM_flags
439  DEM_record
440  height_max
441  height_min
442  File globals defined here:
443  Input/Output:
444  current_tile index of current tile in memory,
445  initialize to -1
446  DEM_tile_data DEM data for current tile, initialize to NULL
447  Output:
448  corner_lat latitude of first element of tile (radians)
449  corner_lon longitude of first element of tile (radians)
450  hor_size Horizontal dimension of tile array, including
451  any overlap with adjacent tiles (pixels)
452  ver_size Vertical dimension of tile array, including any
453  overlap with adjacent tiles (pixels)
454  hor_start Horizontal array index aligned with tile corner
455  ver_start Vertical array index aligned with tile corner
456  tile_cache details about cached tiles.
457  tiles_cached Count of valid entries in tile_cache
458 
459  Macros:
460  BAD_GEOID "GEO_earth.h"
461  DEG2RAD "GEO_geo.h"
462  DEMCACHE "GEO_DEM.c"
463  FAIL "GEO_basic.h"
464  LAT_FVALUE "GEO_geo.h"
465  LAYERS "GEO_DEM.c"
466  LONG_FVALUE "GEO_DEM.c"
467  MAX_DEM_ROWS "GEO_geo.h"
468  MAX_DEM_TILES "GEO_geo.h"
469  max_lon "GEO_DEM.h"
470  min_lat "GEO_DEM.h"
471  MODIS_E_BAD_DEM "PGS_MODIS_35251.h"
472  MODIS_E_DEM_DATA_SIZE "PGS_MODIS_35251.h
473  MODIS_E_GEO "PGS_MODIS_35251.h"
474  PGS_FALSE "PGS_SMF.h"
475  PGS_S_SUCCESS "PGS_SMF.h"
476  PGSd_DEM_DEGREE "PGS_DEM.h"
477  PGSd_DEM_ELEV "PGS_DEM.h"
478  PGSd_DEM_NEAREST_NEIGHBOR "PGS_DEM.h"
479  RAD2DEG "GEO_geo.h"
480  RESOLUTIONS "GEO_DEM.h"
481  SUCCESS "GEO_basic.h"
482 
483 Called by:
484  GEO_terrain_correct
485 
486 Routines called:
487  GEO_get_geoid() "GEO_earth.h"
488  GEO_DEMalloc() "GEO_earth.h"
489  PGS_DEM_GetSize() "PGS_DEM.h"
490  PGS_DEM_GetRegion() "PGS_DEM.h"
491  PGS_SMF_TestErrorLevel() "PGS_SMF.h"
492  modsmf() "smfio.h"
493 
494 Requirements:
495  PR03-F-3.3.3-1
496  PR03-F-3.3.3-2 (Not implemented yet - waived)
497 
498 !Revision History:
499  See top of file for revision history.
500 
501  6/26/95
502  Frederick S. Patt (patt@modis-xl.gsfc.nasa.gov)
503  Finished coding.
504 
505 !Team-unique Header:
506  This software is developed by the MODIS Science Data Support
507  Team for the National Aeronautics and Space Administration,
508  Goddard Space Flight Center, under contract NAS5-32373.
509 
510 Limitations: The scale and offset coefficients provided for scaling the
511  DEM data are not applied to them.
512 
513 !END
514 *****************************************************************************/
515 
516 /* The number of radians of latitude corresponding to 26km along the Earth's
517  surface. Oblateness is too small to matter, but if it did, this should be
518  the value for locations near the poles
519 */
520 #define BORDER 0.004084
521 
522 {
524  /* Parameters describing forbidden zone for DEM files with version 5.2 of
525  Toolkit. See the README file in the DEM file directory. Hopefully later
526  versions will correct this. */
527 
528  /* Labels for the parts of tiles which cross longitude +/-180 */
529  enum {WEST, EAST, PARTS};
530 
531  double tile_hor_size=1.0; /* Hor. dimension of tile */
532  /* tile_ver_size is constant, calculated by GEO_initialize_DEM(). */
533  PGSt_double latitude[2], longitude[PARTS][2];
534  PGSt_double firstElement[2]={0.0};
535  double border=0.0;
536  /* Radians of longitude corresponding to 25km */
537  long pole = 0L;
538  PGSt_integer sizeDataType=0;
539  PGSt_integer numPixVertical=0, numPixHorizontal[PARTS];
540  int16 *tempdata[PARTS]={NULL,NULL};
541  int row, col; /* Row and column tile indices.*/
542  int i, j; /* Pixel indices within tiles.*/
543  int tile; /* tile index. */
544  int16 *inrow, *outrow;
545  char msgbuf[64];
546  unsigned short int ci;
547  tile_cache_struct temp_cache;
548  char filefunc[] = __FILE__ ", GEO_read_DEM";
549 
550 
551  if( hgtmin == NULL || hgtmax == NULL ||
552  (fabs(lat) > PGS_PI*0.5*(1.0+DBL_EPSILON)) )
553  {
554  sprintf(msgbuf, "lat:%g hgtmin:%p hgtmax:%p", lat, (void *)hgtmin,
555  (void *)hgtmax);
556  modsmf(MODIS_E_BAD_DEM, msgbuf, filefunc);
557 
558  return FAIL;
559  }
560 
561  /* Compute tile index for input lat/lon */
562  /* Compute row number */
563  /* Force lat into range -PI/2 <= lat <= PI/2 */
564  if( lat >= PGS_PI*0.5 )
565  lat = PGS_PI*0.5-DBL_EPSILON*10.0;
566  else if( lat < -PGS_PI*0.5 )
567  lat = -PGS_PI*0.5;
568 
569  row = (int)floor((lat + PGS_PI*0.5)/tile_ver_size);
570 
571  /* Compute column number */
572  tile_hor_size = PGS_PI*2.0/(double)row_num_tiles[row];
573 
574  /* Force lon to be in the range -PI <= lon < PI */
575  lon -= 2.0*PGS_PI*floor(lon/(PGS_PI*2.0)+0.5);
576  col = (int)floor((lon + PGS_PI)/tile_hor_size);
577 
578  /* Compute tile number */
579  tile = row_start_tile[row] + col;
580 
581  /* search for tile_cache[ci].tile == tile */
582  for( ci = 0; ci < tiles_cached && tile_cache[ci].tile!=tile; ci++ );
583 
584  if(ci<tiles_cached)
585  { /* Matching entry was found. */
586  temp_cache = tile_cache[ci];
587 
588  if( ci > 0 )
589  {
590  hor_size = temp_cache.hor_size;
591  ver_size = temp_cache.ver_size;
592  hor_start = temp_cache.hor_start;
593  ver_start = temp_cache.ver_start;
594  corner_lat = temp_cache.corner_lat;
595  corner_lon = temp_cache.corner_lon;
596  DEM_tile_data = temp_cache.DEM_tile_data;
597  }
598  }
599  else
600  { /* Need to load in a new tile of data */
601  if( ci == DEMCACHE )
602  { /* Drop least recently used cache entry, to make room for new one.*/
603  free(tile_cache[DEMCACHE-1].DEM_tile_data);
604 
605  /* Initialize pointer if calls to Free or malloc are made later */
606  tiles_cached--;
607  ci = tiles_cached;
608  }
609 
610  current_tile = tile;
611 
612  /* set output to error values, in case of early returns or errors*/
613  *hgtmin = SHRT_MAX;
614  *hgtmax = SHRT_MIN;
615  hor_size = ver_size = 0;
616  /* Guarantees early exit from GEO_latlon2height(). */
617 
618  DEM_tile_data = (short int *)NULL;
619 
620 
621  /*From here on in returning with DEM_tile_data==NULL indicates an error.*/
622  /* Calculate latitude boundaries. Nominal tiles are defined by the
623  * calculations above. Full tile boundaries must include every point
624  * within a great-circle distance of BORDER radians from any point in the
625  * nominal tile area.
626  */
627 
628  if( row == 0 )
629  { /* south polar tile. */
630  latitude[0] = -90.0 + (tile_ver_size+BORDER)*RAD2DEG;
631  latitude[1] = min_lat;
632  }
633  else if( row == num_rows-1 )
634  { /* North polar tile. */
635  latitude[0] = 90.0;
636  latitude[1] = 90.0 - (tile_ver_size+BORDER)*RAD2DEG;
637  }
638  else
639  { /* Non-polar tile. */
640  corner_lat = (double)row*tile_ver_size - PGS_PI/2.0;
641  latitude[0] = (corner_lat+tile_ver_size+BORDER)*RAD2DEG;
642  latitude[1] = (corner_lat-BORDER)*RAD2DEG;
643 
644  /* Use the edge farthest from the equator to calculate the border size. */
645  if( corner_lat < 0.0 )
646  border = BORDER/cos(corner_lat);
647  else
648  border = BORDER/cos(corner_lat+tile_ver_size);
649  }
650 
651  /* Calculate the longitude boundaries. For tiles whose border cross +/-180
652  * degrees longitude, two sets of logitude boundaries are needed, one for
653  * each side of that barrier. If only one set is needed, it is stored in
654  * the WEST part.
655  * Note that the WEST and EAST parts of the tile are named from the point
656  * of view of the tile, not from the prime meridian; the WEST part is in
657  * the eastern hemisphere, and just east of it is the EAST part, which is
658  * in the western hemisphere.
659  */
660 
661  if( row_num_tiles[row] == 1 )
662  { /*Tile covers entire range of longitude, doesn't need a border.*/
663  longitude[WEST][0] = -180.0;
664  longitude[WEST][1] = max_lon;
665  }
666  else
667  { /* Calculate East/West border sizes. */
668  longitude[WEST][0] = ((double)col*tile_hor_size-border)*RAD2DEG-180.0;
669  longitude[WEST][1] = longitude[WEST][0] +
670  (tile_hor_size+2.0*border)*RAD2DEG;
671  if( longitude[WEST][0] < -180.0 )
672  { /* Western border overlaps -180.0, must be split */
673  longitude[EAST][0] = -180.0;
674  longitude[EAST][1] = longitude[WEST][1];
675  longitude[WEST][0] += 360.0;
676  longitude[WEST][1] = max_lon;
677  }
678  else if(longitude[WEST][1] > 180.0 )
679  { /* Eastern border overlaps 180.0, must be split */
680  longitude[EAST][0] = -180.0;
681  longitude[EAST][1] = longitude[WEST][1]-360.0;
682  longitude[WEST][1] = max_lon;
683  }
684  }
685 
686  /* Retrieve data for first part of tile */
687  if( PGS_DEM_GetSize(DEM_resolutions[0], PGSd_DEM_ELEV, PGSd_DEM_DEGREE,
688  latitude, longitude[WEST], &numPixVertical, &numPixHorizontal[WEST],
689  &sizeDataType)
690  != PGS_S_SUCCESS || numPixVertical<=0 || numPixHorizontal[WEST]<=0)
691  {
692  sprintf(msgbuf, "PGS_DEM_GetSize({%g,%g}, {%g,%g})", latitude[0],
693  latitude[1], longitude[WEST][0], longitude[WEST][1]);
694  modsmf(MODIS_E_GEO, msgbuf, "GEO_DEM.c, GEO_read_DEM");
695 
696  return FAIL;
697  }
698 
699  if( (size_t)sizeDataType != sizeof(tempdata[0][0]) )
700  { /* Unexpected data size. */
701  sprintf(msgbuf, "%ld", (long)sizeDataType);
702  modsmf(MODIS_E_DEM_DATA_SIZE, msgbuf, "GEO_DEM.c, GEO_read_DEM");
703  return FAIL;
704  }
705 
706  ver_size = numPixVertical;
707  hor_size = numPixHorizontal[WEST];
708 
709  tempdata[WEST] = (int16 *)GEO_DEMalloc(
710  (size_t)(numPixVertical*numPixHorizontal[WEST]*sizeDataType) );
711 
712  if( tempdata[WEST] != NULL )
713  { /* Get data for WEST part. */
714  if( PGS_SMF_TestErrorLevel(PGS_DEM_GetRegion(DEM_resolutions, RESOLUTIONS,
715  PGSd_DEM_ELEV, PGSd_DEM_DEGREE, PGSd_DEM_NEAREST_NEIGHBOR,
716  latitude, longitude[WEST], tempdata[WEST], NULL, firstElement,
717  NULL))==PGS_FALSE )
718  { /* Got elevation data for WEST part */
719  corner_lat = firstElement[0]*DEG2RAD;
720  corner_lon = firstElement[1]*DEG2RAD;
721  hor_start = ver_start = 0;
722 
723  if( row_num_tiles[row]==1 )
724  {
725  /* Need extra columns on East and West edges, to allow
726  * interpolation
727  */
728  hor_size += 2;
729 
730  /* Change column which corresponds to firstElement[1] */
731  hor_start++;
732 
733  /* Polar tile rows must never contain more than 1 tile, or
734  * GEO_latlon2height() could fail. Therefore, we only need
735  * to test the following cases if row_num_tiles[row]==1
736  */
737  if( row == 0 )
738  {
739  /* Need an extra row on South edge, to allow interpolation
740  * all the way to South pole
741  */
742  ver_size++;
743 
744  pole = 0L;
745  inrow = tempdata[WEST] + (numPixVertical-2)*numPixHorizontal[WEST];
746  for( i = 0; i < numPixHorizontal[WEST]; i++ )
747  pole += (long)inrow[i];
748  pole /= (long)numPixHorizontal[WEST];
749  }
750  else if( row == num_rows-1 )
751  {
752  /* Need an extra row at the North edge, to allow
753  * interpolation all the way to north pole
754  */
755  ver_size++;
756  /* Change row which corresponds to firstElement[0] */
757  ver_start++;
758 
759  pole = 0L;
760  inrow = tempdata[WEST];
761  for( i = 0; i < numPixHorizontal[WEST]; i++ )
762  pole += (long)inrow[i];
763  pole /= (long)numPixHorizontal[WEST];
764  }
765 
766  DEM_tile_data = (int16 *)GEO_DEMalloc((size_t)
767  (ver_size*hor_size*sizeDataType));
768 
769  if( DEM_tile_data != NULL )
770  { /* Copy tempdata[WEST] to DEM_tile_data. */
771  for( i = 0; i < numPixVertical; i++ )
772  {
773  inrow = tempdata[WEST]+i*numPixHorizontal[WEST];
774 
775  /* Note that copy is offset by ver_start, for North polar row. */
776  outrow = DEM_tile_data + (i+ver_start)*hor_size;
777 
778  /* Fill in extra buffer columns needed for interpolation. */
779  outrow[0] = inrow[numPixHorizontal[WEST]-1];
780  outrow[hor_size-1] = inrow[0];
781 
782  for( j = 0; j < numPixHorizontal[WEST]; j++ )
783  outrow[j+1] = inrow[j];
784  }
785 
786  if( row == 0 )
787  outrow = DEM_tile_data + (ver_size-1)*hor_size;
788  else if(row == num_rows-1)
789  outrow = DEM_tile_data;
790  else
791  outrow = NULL;
792 
793  if( outrow != NULL ) /* Fill in average near-polar value. */
794  for( j = 0; j < hor_size; j++ )
795  outrow[j] = (int16)pole;
796  }
797  }
798  else if( col == 0 || col == row_num_tiles[row]-1 )
799  {
800  /* This tile straddles longitude +/-180 degrees. */
801  if( col == 0 )
802  corner_lon -= 2.0*PGS_PI;
803 
804  if( PGS_DEM_GetSize(DEM_resolutions[0], PGSd_DEM_ELEV,
805  PGSd_DEM_DEGREE, latitude, longitude[EAST], &numPixVertical,
806  &numPixHorizontal[EAST], NULL)
807  == PGS_S_SUCCESS && numPixVertical>0 && numPixHorizontal[EAST]>0)
808  {
809  tempdata[EAST] = (int16 *)GEO_DEMalloc((size_t)
810  (numPixVertical*numPixHorizontal[EAST]*sizeDataType));
811  }
812 
813  if( tempdata[EAST] != NULL )
814  { /* Get elevation for EAST part. */
815  if( PGS_SMF_TestErrorLevel(PGS_DEM_GetRegion( DEM_resolutions,
816  RESOLUTIONS, PGSd_DEM_ELEV, PGSd_DEM_DEGREE,
817  PGSd_DEM_NEAREST_NEIGHBOR, latitude, longitude[EAST],
818  tempdata[EAST], NULL, NULL, NULL) ) == PGS_FALSE )
819  {
820  /* Got elevation for EAST part. */
821  hor_size += numPixHorizontal[EAST];
822  DEM_tile_data = (int16 *)GEO_DEMalloc((size_t)
823  (ver_size*hor_size*sizeDataType));
824  }
825  }
826 
827  if( DEM_tile_data != NULL )
828  { /* Merge data from the two parts. */
829  for( i = 0; i < ver_size; i++ )
830  {
831  /* copy tempdata[WEST] into first numPixHorizontal[WEST]
832  * columns of DEM_tile_data.
833  */
834  outrow = DEM_tile_data + i*hor_size;
835  inrow = tempdata[WEST] + i*numPixHorizontal[WEST];
836  for( j = 0; j < numPixHorizontal[WEST]; j++ )
837  outrow[j] = inrow[j];
838 
839  /* copy tempdata[EAST] into last numPixHorizontal[EAST]
840  * columns of DEM_tile_data.
841  */
842  outrow += numPixHorizontal[WEST];
843  inrow = tempdata[EAST] + i*numPixHorizontal[EAST];
844  for( j = 0; j < numPixHorizontal[EAST]; j++ )
845  outrow[j] = inrow[j];
846  }
847  }
848  }
849  else
850  { /* No special case for this tile. */
851  DEM_tile_data = tempdata[WEST];
852  tempdata[WEST] = NULL;
853  }
854 
855  if( DEM_tile_data != NULL )
856  {
857  int ver, hor;
858 
859  for(i = ver = 0; ver < ver_size; ver++)
860  for(hor = 0; hor < hor_size; hor++,i++)
861  {
862  /* In principle, the following lines should be inserted here:
863  *
864  * SET DEM_tile_data[i] = DEM_tile_data[i]*scaling[0]+offset[0]
865  * (rounded to nearest integer)
866  *
867  * However, this code assumes scaling[0]==1 and offset[0]==0, which
868  * is currently the case. Those assumptions are checked in
869  * GEO_initialize_DEM(), in case they change in the future.
870  */
871  if( DEM_record[tile] == NO_DEM_DATA )
872  { /* First time this tile has been loaded. */
873  /* Cumulate height_min, height_max. */
874  double lat = (ver - ver_start)*lat_inc + corner_lat;
875  double lon = (hor - hor_start)*lon_inc + corner_lon;
876  int geoid_height = GEO_get_geoid(lat, lon);
877 
878  if(geoid_height == BAD_GEOID)
879  {
880  sprintf(msgbuf, "GEO_get_geoid(%g,%g) = %d",
881  lat, lon, geoid_height);
882  modsmf(MODIS_E_GEO, msgbuf, filefunc);
883  return FAIL;
884  }
885 
886  DEM_tile_data[i] += geoid_height;
887  if( DEM_tile_data[i] < height_min[tile] )
888  height_min[tile] = DEM_tile_data[i];
889 
890  if( DEM_tile_data[i] > height_max[tile] )
891  height_max[tile] = DEM_tile_data[i];
892  }
893  }
894  /* DEM_record is set solely to avoid changing GEO_latlon2height();
895  * the actual value doesn't matter, as long as it's not NO_DEM_DATA.
896  */
897  DEM_record[tile] = (short)tile;
898 
899  temp_cache.tile = tile;
900  temp_cache.hor_size = hor_size;
901  temp_cache.ver_size = ver_size;
902  temp_cache.hor_start = hor_start;
903  temp_cache.ver_start = ver_start;
904  temp_cache.corner_lat = corner_lat;
905  temp_cache.corner_lon = corner_lon;
906  temp_cache.DEM_tile_data = DEM_tile_data;
907 
908  tiles_cached++;
909 
910  if( ci == 0 )
911  tile_cache[0] = temp_cache;
912 
913  } /* END if DEM_tile_data has been successfully allocated */
914  } /* END if first call to PGS_DEM_Get_Region() succeeded */
915 
916  free(tempdata[WEST]);
917  free(tempdata[EAST]);
918 
919  } /* end if tempdata[WEST] has been successfully allocated */
920  } /* end if DEM tile not in memory */
921 
922  if( DEM_tile_data == NULL )
923  {
924  modsmf(MODIS_E_GEO, "GEO_DEMalloc()", "GEO_DEM.c, GEO_read_DEM");
925 
926  return FAIL;
927  }
928 
929  /* Can return valid values. */
930  if( ci > 0 )
931  { /* overlapping copy; must use memmove(), not memcpy(). */
932  memmove(&tile_cache[1], tile_cache, (size_t)(ci)*sizeof(tile_cache[0]));
933  tile_cache[0] = temp_cache;
934  }
935 
936  *hgtmin = (int)height_min[tile];
937  *hgtmax = (int)height_max[tile];
938 
939  return SUCCESS;
940 }
941 
942 /*===========================================================================*/
943 
945  double const lat,
946  double const lon,
947  double * const h
948  )
949 
950 /*
951 !C******************************************************************************
952 !Description:
953  Routine in earth location group of the Level-1A geolocation
954  software to determine terrain height given lat/lon. It
955  uses an array of terrain heights for a tile previously read
956  into memory by GEO_DEM_read. The offset into the tile is
957  computed for the lat/lon and the height is computed by
958  bilinear interpolation from the four adjacent points
959 
960 !Input Parameters:
961  double lat - latitude
962  double lon - longitude
963 
964 !Output Parameters:
965  double *h - height
966 
967 Return parameter:
968  int err_stat - error status
969 
970 Global variables:
971  int hor_size - Horizontal dimension of tile array (includes any
972  overlap with adjacent tiles)
973  int ver_size - Vertical dimension of tile array (includes any
974  overlap with adjacent tiles)
975  int hor_start - Horizontal array index aligned with tile corner
976  int ver_start - Vertical array index aligned with tile corner
977  short int DEM_record[MAX_DEM_TILES] - Pointers to DEM records
978  (-1 = no DEM record for tile)
979  short int height_min[MAX_DEM_TILES] - Minimum terrain height
980  for tile (meters)
981  short int height_max[MAX_DEM_TILES] - Maximum terrain height
982  for tile (meters)
983  short int DEM_TILE_DATA[MAX_DEM_HORIZONTAL*MAX_DEM_VERTICAL] -
984  DEM data for current tile
985  int current_tile - index of current tile in memory
986  double corner_lat - latitude of tile lower-left corner
987  double corner_lon - longitude of tile lower-left corner
988  double lat_inc - latitude increment of data in tile
989  double lon_inc - longitude increment of data in tile
990 
991 
992 Call functions:
993  modsmf(MODIS_X_MNEMONIC_STRING, "user message string",
994  "function", GEO_latlon2height.c") - writes error
995  status messages to log
996 
997 !Revision History:
998  see top of file for revision history.
999 
1000  6/27/95
1001  Frederick S. Patt (patt@modis-xl.gsfc.nasa.gov)
1002  Finished coding.
1003 
1004 !Team-unique Header:
1005  This software is developed by the MODIS Science Data Support
1006  Team for the National Aeronautics and Space Administration,
1007  Goddard Space Flight Center, under contract NAS5-32373.
1008 
1009 !END*************************************************************************
1010 */
1011 {
1012  /* Local variable definitions */
1013 
1014  double hor = 0.0; /* relative horizontal location in tile */
1015  double ver = 0.0; /* relative vertical location in tile */
1016  double dihor = 0.0; /* integral part of horizontal position */
1017  double diver = 0.0; /* integral part of vertical position */
1018  double dhor = 0.0; /* fractional part of horizonal position */
1019  double dver = 0.0; /* fractional part of vertical position */
1020  double dlon = 0.0; /* difference in longitude */
1021  double dlat = 0.0; /* difference in latitude */
1022  int ihor = 0; /* horizontal index into tile */
1023  int iver = 0; /* vertical index into tile */
1024  int near_DEM[2][2] = {0}; /* DEM points surrounding lat/lon */
1025 
1026  /* Begin program logic */
1027 
1028 
1029  /* Compute offset relative to current tile */
1030 
1031  dlon = lon - corner_lon;
1032  dlat = lat - corner_lat;
1033 
1034  /* Check for longitude rollover */
1035 
1036  if(dlon > 2.0*PGS_PI)
1037  dlon = dlon - 2.0*PGS_PI;
1038 
1039  if(dlon < 0.0)
1040  dlon = dlon + 2.0*PGS_PI;
1041 
1042  /* Compute indices into tile data */
1043 
1044  hor = dlon / lon_inc + (double)hor_start;
1045  ver = dlat / lat_inc + (double)ver_start;
1046 
1047  /* Check if we have required data */
1048 
1049  if( (hor < 0.0) || (hor >= (double)hor_size) ||
1050  (ver < 0.0) || (ver >= (double)ver_size)) {
1051  /* call SDP function to report error */
1052  modsmf(MODIS_E_DEM_IN_MEM, "",
1053  "GEO_DEM.c, GEO_latlon2height");
1054 
1055  return FAIL;
1056  }
1057 
1058  /* Check flag to see if there is data for this tile */
1059 
1060  if(DEM_record[current_tile] == NO_DEM_DATA) {
1061  *h = 0.0;
1062 
1063  }
1064  else {
1065 
1066  /* Compute integral and fractional parts of index */
1067 
1068  dhor = modf(hor, &dihor);
1069  dver = modf(ver, &diver);
1070  ihor = (int)dihor;
1071  iver = (int)diver;
1072 
1073  /* Get four nearest DEM points */
1074 
1075  near_DEM[0][0] = (int)DEM_tile_data[iver * hor_size + ihor];
1076  near_DEM[0][1] = (int)DEM_tile_data[iver * hor_size + ihor + 1];
1077  near_DEM[1][0] = (int)DEM_tile_data[(iver + 1) * hor_size + ihor];
1078  near_DEM[1][1] = (int)DEM_tile_data[(iver + 1) * hor_size + ihor + 1];
1079 
1080  /* Compute height as weighted sum */
1081 
1082  *h = (double)near_DEM[0][0] * (1.0 - dhor) * (1.0 - dver)
1083  + (double)near_DEM[0][1] * dhor * (1.0 - dver)
1084  + (double)near_DEM[1][0] * (1.0 - dhor) * dver
1085  + (double)near_DEM[1][1] * dhor * dver;
1086 
1087  } /* End if there is data for this tile */
1088  return SUCCESS;
1089 }
1090 
1091 /*===========================================================================*/
1092 
1093 int GEO_close_DEM(void)
1094 
1095 /*
1096 !C*****************************************************************************
1097 !Description:
1098  Routine in the DEM group of the Level-1A geolocation
1099  software to close access to the DEM files and free space
1100  allocated to store it
1101 
1102 !Input Parameters:
1103  None
1104 
1105 !Output Parameters:
1106  None
1107 
1108 Return value:
1109  FAIL if PGS_DEM_Close() call fails
1110  SUCCESS otherwise
1111 
1112 
1113 Externally defined:
1114  DEM_resolution
1115  DEM_layers
1116  tile_cache details about cached tiles.
1117  tiles_cached Count of valid entries in tile_cache
1118 
1119 Called by:
1120  main()
1121 
1122 
1123 Call functions:
1124  PGS_DEM_Close() "PGS_DEM.h"
1125 
1126 !Revision History:
1127  * Revision /main/GEO_V2_DEV/1 1997/08/26 kuyper
1128  * Corrected SUCCEED to SUCCESS.
1129 
1130 !Requirements:
1131  PR03-F-3.3.3-1
1132 
1133 !Team-unique Header:
1134  This software is developed by the MODIS Science Data Support
1135  Team for the National Aeronautics and Space Administration,
1136  Goddard Space Flight Center, under contract NAS5-32373.
1137 
1138 !END*************************************************************************
1139 */
1140 {
1141  char msgbuf[64];
1142  char filefunc[] = __FILE__ ", GEO_close_DEM";
1143 
1144  if( tiles_cached > DEMCACHE )
1145  {
1146  /* Something is seriously wrong. Safest approach is to not clean up. */
1147  sprintf(msgbuf, "tiles_cached = %u", tiles_cached);
1148  modsmf(MODIS_E_BAD_INPUT_ARG, msgbuf, filefunc);
1149  return MODIS_E_BAD_INPUT_ARG;
1150  }
1151 
1152  while( tiles_cached > 0 )
1153  free(tile_cache[--tiles_cached].DEM_tile_data);
1154 
1155  if(PGS_DEM_Close(DEM_resolutions, RESOLUTIONS, DEM_layers, 1)
1156  != PGS_S_SUCCESS)
1157  {
1158  sprintf(msgbuf, "PGS_DEM_Close(%d, 1)", RESOLUTIONS);
1159  modsmf(MODIS_E_GEO, msgbuf, filefunc);
1160 
1161  return MODIS_E_GEO;
1162  }
1163 
1164  return PGS_S_SUCCESS;
1165 }
1166 
integer, parameter int16
Definition: cubeio.f90:3
#define LAT_FVALUE
Definition: GEO_geo.h:147
void * GEO_DEMalloc(size_t size)
Definition: GEO_DEM.c:321
#define TILES_BASE
tile_cache_struct * tile_cache
Definition: GEO_DEM.c:119
#define SUCCESS
Definition: ObpgReadGrid.h:15
#define DEGREE_STRING
Definition: GEO_DEM.c:56
int j
Definition: decode_rs.h:73
#define L(lambda, T)
Definition: PreprocessP.h:185
real(single), dimension(:,:), allocatable longitude
#define FAIL
Definition: ObpgReadGrid.h:18
#define LAYERS
Definition: GEO_DEM.c:67
#define NULL
Definition: decode_rs.h:63
void geoDemInitGlobals()
Definition: GEO_DEM.c:123
#define MODIS_E_BAD_INPUT_ARG
short int * DEM_tile_data
Definition: GEO_DEM.c:116
#define PGS_PI
Definition: GEO_geo.h:172
real(single), dimension(:,:), allocatable latitude
#define MAX_DEM_TILES
Definition: GEO_geo.h:161
float h[MODELMAX]
Definition: atrem_corl1.h:131
float * lat
#define LONG_FVALUE
Definition: GEO_geo.h:146
#define MODIS_E_GEO
#define MODIS_E_DEM_METADATA
#define EAST
Definition: ancil.h:61
double corner_lon
Definition: GEO_DEM.c:115
#define COSLAT_OFFSET
int GEO_read_DEM(double lat, double lon, int *const hgtmin, int *const hgtmax)
Definition: GEO_DEM.c:395
#define RAD2DEG
Definition: GEO_geo.h:173
int GEO_close_DEM(void)
Definition: GEO_DEM.c:1093
#define MODIS_E_BAD_DEM
PGSt_DEM_Tag DEM_resolutions[RESOLUTIONS]
Definition: GEO_DEM.c:62
integer, parameter double
#define NO_DEM_DATA
Definition: GEO_geo.h:164
PGSt_SMF_status GEO_initialize_DEM(void)
Definition: GEO_DEM.c:134
#define MAX_DEM_ROWS
Definition: GEO_geo.h:160
#define DEMCACHE
Definition: GEO_DEM.c:58
#define isspace(c)
#define MODIS_E_DEM_IN_MEM
unsigned short int tiles_cached
Definition: GEO_DEM.c:120
int GEO_get_geoid(double latitude, double longitude)
Definition: GEO_get_geoid.c:10
#define fabs(a)
Definition: misc.h:93
#define METER_STRING
Definition: GEO_DEM.c:57
int GEO_latlon2height(double const lat, double const lon, double *const h)
Definition: GEO_DEM.c:944
#define WEST
Definition: ancil.h:59
#define MODIS_E_DEM_DATA_SIZE
double max_lon
Definition: GEO_DEM.c:64
float * lon
double min_lat
Definition: GEO_DEM.c:63
l2prod offset
double corner_lat
Definition: GEO_DEM.c:114
#define BAD_GEOID
Definition: GEO_earth.h:119
int i
Definition: decode_rs.h:71
#define DEG2RAD
Definition: GEO_geo.h:174
float p[MODELMAX]
Definition: atrem_corl1.h:131
#define RESOLUTIONS
Definition: GEO_DEM.h:52
#define BORDER