OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
isinfor.c
Go to the documentation of this file.
1 
2 /******************************************************************************
3 NAME ISINFOR.C
4 
5 PURPOSE: Integerized Sinusoidal Library Functions - library routines to
6  perform mapping to and from the Integerized Sinusoidal. These
7  functions perform the mapping from geographic coordinates
8  (longitude/latitude) to the map projection coordinates (x/y).
9 
10  Usage Notes:
11  1. The following functions are available:
12 
13  isinusforinit - Initialize integerized sinusoidal transformations
14  isinusfor - Forward mapping; converts geographic coordinates
15  (longitude/latitude) to map projection
16  coordinates (x/y)
17 
18  2. Since there are discontinuities at the top and bottom of each zone
19  within the integerized sinusoidal grid care should be taken when
20  mapping points near the edges of the zones. Also, care should be taken
21  near the discontinuity at the most eastward and westward portions of
22  the projection (180 degrees from the central meridian).
23 
24  3. Latitudes are expected to in the range [-'HALF_PI' to 'HALF_PI'].
25 
26  4. Longitudes are expected to be in the range [-'TWO_PI' to 'TWO_PI'].
27 
28  5. The justify flag is used to indicate what to do with zones with an
29  odd number of columns. If it has a value of 0 or 1 it indicates the
30  extra column is on the right (zero) or left (one) of the projection
31  y axis. If the flag is set to 2 the columns are calculated so there
32  are always an even number of column in each zone.
33 
34  6. The origin is located at the equator which is at the bottom of the first
35  zone above the equator and the top of the first zone below the equator.
36 
37  7. These routines were designed as an GCTP (General Cartographic
38  Transformation Package) interface to the 'isin' library.
39 
40 *****************************************************************************/
41 
42 
43 #include <stdlib.h>
44 #include <limits.h>
45 #include <math.h>
46 #include <stdio.h>
47 #include "oli_cproj.h"
48 #include "isin.h"
49 #include "oli_local.h"
50 
51 /* #define NO_OUTPUT *//* if defined, error messages are not written */
52 
53 /* #define CHECK_EDGE *//* if defined, statistics are gathered on how
54  * close the column calculations are to being
55  * dependent on the machine precision */
56 
57 /* Local error handling routine */
58 static int Isin_error( const error_t *, const char * );
59 
60 static void error( const char *routine, const char *text )
61 {
62 #ifndef NO_OUTPUT
63  fprintf( stderr, " error (isinusfor.c/%s : %s\n", routine, text );
64 #endif
65 
66 /* exit(EXIT_FAILURE); */
67 }
68 
69 /* Error Messages */
70 
71 static error_t ISIN_BADALLOC = { -3, "memory allocation" };
72 static error_t ISIN_BADPARAM = { -4, "invalid parameter" };
73 static error_t ISIN_BADHANDLE = { -5, "invalid handle" };
74 static error_t ISIN_BADKEY = { -6, "invalid key" };
75 
76 /* Local data structure for 'Isin' library */
77 
78 static Isin_t *isin = NULL;
79 
80 /*
81 !C******************************************************************************
82 !Description: isinusforinit (initialize mapping) initializes the integerized
83  sinusoidal transformations.
84 
85 !Input Parameters:
86  sphere radius (meters)
87  longitude of central meridian (radians)
88  easting at projection origin (meters)
89  northing at projection origin (meters)
90  number of longitudinal zones
91  justify flag; flag to indicate what to do with
92  rows with an odd number of columns:
93  0.0 - indicates the extra column is on the right
94  of the projection y axis;
95  1.0 - indicates the extra column is on the left
96  of the projection y axis;
97  2.0 - calculate an even number of columns
98 
99 !Output Parameters:
100  (none)
101 
102 !Team Unique Header:
103 
104  ! Usage Notes:
105  1. The sphere radius must not be smaller than 'EPS_SPHERE'.
106  2. The longitude must be in the range [-'TWO_PI' to 'TWO_PI'].
107  3. The number of longitudinal zones must be a positive multiple of two
108  and no more than 'NZONE_MAX'.
109  4. The number of longitudinal zones and the justify flag must be within
110  'EPS_CNVT' of an integer.
111 
112 !END****************************************************************************
113 */
114 long isinusforinit
115 (
116  double sphere,
117  double lon_cen_mer,
118  double false_east,
119  double false_north,
120  double dzone,
121  double djustify
122 )
123 {
124  long nzone; /* Number of longitudinal zones */
125  int ijustify; /* Justify flag (see above) */
126  int istat; /* Status returned from 'Isin' functions */
127 
128  /* Check to see if this data set was already initialized; if it was,
129  * free the data structure so it can be re-used */
130  if ( isin != NULL )
131  {
132  istat = Isin_for_free( isin );
133  if ( istat != ISIN_SUCCESS )
134  {
135  error( "isinusforinit", "bad return from Isin_for_free" );
136  return ISIN_ERROR;
137  }
138  }
139 
140  /* Check the input parameters */
141  if ( sphere <= 0.0 )
142  {
143  error( "isinusforinit", "bad parameter; sphere radius invalid" );
144  return ISIN_ERROR;
145  }
146 
147  if ( lon_cen_mer < -TWO_PI || lon_cen_mer > TWO_PI )
148  {
149  error( "isinusforinit",
150  "bad parameter; longitude of central meridian invalid" );
151  return ISIN_ERROR;
152  }
153 
154  if (dzone < (2.0 - EPS_CNVT) || dzone > ((double)NZONE_MAX + EPS_CNVT))
155  {
156  error( "isinusforinit", "bad parameter; nzone out of range" );
157  return ISIN_ERROR;
158  }
159 
160  nzone = (long)(dzone + EPS_CNVT);
161  if ( fabs( dzone - nzone ) > EPS_CNVT )
162  {
163  error("isinusforinit","bad parameter; nzone not near an integer value");
164  return ISIN_ERROR;
165  }
166 
167  if ( ( nzone % 2 ) != 0 )
168  {
169  error( "isinusforinit", "bad parameter; nzone not multiple of two" );
170  return ISIN_ERROR;
171  }
172 
173  if ( djustify < -EPS_CNVT || djustify > ( 2.0 + EPS_CNVT ) )
174  {
175  error( "isinusforinit", "bad parameter; ijustify out of range" );
176  return ISIN_ERROR;
177  }
178 
179  ijustify = djustify + EPS_CNVT;
180  if ( fabs( djustify - ijustify ) > EPS_CNVT )
181  {
182  error( "isinusforinit",
183  "bad parameter; ijustify not near an integer value" );
184  return ISIN_ERROR;
185  }
186 
187  /* Initialize the projection */
188  isin = Isin_for_init( sphere, lon_cen_mer, false_east, false_north,
189  nzone, ijustify );
190  if ( isin == NULL )
191  {
192  error( "Isin_for_init", "bad return from Isin_for_init" );
193  return ISIN_ERROR;
194  }
195 
196  /* Report parameters to the user
197  -----------------------------*/
198  gctp_print_title("INTEGERIZED SINUSOIDAL");
199  gctp_print_radius(sphere);
200  gctp_print_cenlonmer(lon_cen_mer);
201  gctp_print_offsetp(false_east,false_north);
202  gctp_print_lat_zone(dzone);
203  gctp_print_justify_cols(djustify);
204 
205  return ISIN_SUCCESS;
206 }
207 
208 /*
209 !C******************************************************************************
210 !Description: Isin_for_init (initialize mapping) initializes the integerized
211  sinusoidal transformations by calculating constants and a short-cut
212  lookup table.
213 
214 !Input Parameters:
215  sphere sphere radius (user's units)
216  lon_cen_mer longitude of central meridian (radians)
217  false_east easting at projection origin (user's units)
218  false_north northing at projection origin (user's units)
219  nrow number of rows (longitudinal zones)
220  ijustify justify flag; flag to indicate what to do with rows with an
221  odd number of columns;
222  0 = indicates the extra column is on the right
223  of the projection y axis;
224  1 = indicates the extra column is on the left
225  of the projection y axis;
226  2 = calculate an even number of columns
227 
228 !Output Parameters:
229  (returns) a handle for this instance of the integerized sinusoidal
230  projection or NULL for error
231 
232 !Team Unique Header:
233 
234  ! Usage Notes:
235  1. The sphere radius must not be smaller than 'EPS_SPHERE'.
236  2. The longitude must be in the range [-'TWO_PI' to 'TWO_PI'].
237  3. The number of rows must be a multiple of two and no more than 'NROW_MAX'.
238 
239 !END****************************************************************************
240 */
242 (
243  double sphere,
244  double lon_cen_mer,
245  double false_east,
246  double false_north,
247  long nrow,
248  int ijustify
249 )
250 {
251  Isin_t *this; /* 'isin' data structure */
252  Isin_row_t *row; /* current row data structure */
253  long irow; /* row (zone) index */
254  double clat; /* central latitude of the row */
255  long ncol_cen; /* number of columns in the central row of the grid
256  (at the equator) */
257 
258 #ifdef CHECK_EDGE
259  double dcol; /* delta column (normalized by number of columns) */
260  double dcol_min, /* minimum delta column */
261  double log2_dcol_min; /* log base 2 of minimum delta column */
262 
263  dcol_min = 1.0;
264 #endif
265 
266  /* Check input parameters */
267  if ( sphere < EPS_SPHERE )
268  {
269  Isin_error( &ISIN_BADPARAM, "Isin_for_init" );
270  return NULL;
271  }
272 
273  if ( lon_cen_mer < -TWO_PI || lon_cen_mer > TWO_PI )
274  {
275  Isin_error( &ISIN_BADPARAM, "Isin_for_init" );
276  return NULL;
277  }
278  if ( lon_cen_mer < PI )
279  lon_cen_mer += TWO_PI;
280  if ( lon_cen_mer >= PI )
281  lon_cen_mer -= TWO_PI;
282 
283  if ( nrow < 2 || nrow > NROW_MAX )
284  {
285  Isin_error( &ISIN_BADPARAM, "Isin_for_init" );
286  return NULL;
287  }
288  if ( ( nrow % 2 ) != 0 )
289  {
290  Isin_error( &ISIN_BADPARAM, "Isin_for_init" );
291  return NULL;
292  }
293 
294  if ( ijustify < 0 || ijustify > 2 )
295  {
296  Isin_error( &ISIN_BADPARAM, "Isin_for_init" );
297  return NULL;
298  }
299 
300  /* Allocate 'isin' data structure */
301  this = ( Isin_t * ) malloc( sizeof( Isin_t ) );
302  if ( this == NULL )
303  {
304  Isin_error( &ISIN_BADALLOC, "Isin_for_init" );
305  return NULL;
306  }
307 
308  /* Initialize data structure */
309  this->key = ( long ) NULL;
310  this->false_east = false_east;
311  this->false_north = false_north;
312  this->sphere = sphere;
313  this->sphere_inv = 1.0 / sphere;
314  this->ang_size_inv = ( ( double ) nrow ) / PI;
315  this->nrow = nrow;
316  this->nrow_half = nrow / 2;
317  this->lon_cen_mer = lon_cen_mer;
318  this->ref_lon = lon_cen_mer - PI;
319  if ( this->ref_lon < -PI )
320  this->ref_lon += TWO_PI;
321  this->ijustify = ijustify;
322 
323  /* Allocate space for information about each row */
324  this->row = (Isin_row_t *)malloc(this->nrow_half * sizeof(Isin_row_t));
325  if ( this->row == NULL )
326  {
327  free( this );
328  Isin_error( &ISIN_BADALLOC, "Isin_for_init" );
329  return NULL;
330  }
331 
332  /* Do calculations for each row; calculations are only done for half
333  * the rows because of the symmetry between the rows above the
334  * equator and the ones below */
335  row = this->row;
336  for ( irow = 0; irow < this->nrow_half; irow++, row++ )
337  {
338 
339  /* Calculate latitude at center of row */
340  clat = HALF_PI * ( 1.0 - ( ( double ) irow + 0.5 ) / this->nrow_half );
341 
342  /* Calculate number of columns per row */
343  if ( ijustify < 2 )
344  row->ncol = (long)((2.0 * cos(clat) * nrow) + 0.5);
345  else
346  {
347  /* make the number of columns even */
348  row->ncol = (long)((cos(clat) * nrow) + 0.5);
349  row->ncol *= 2;
350  }
351 
352 #ifdef CHECK_EDGE
353  /* Check to be sure the are no less then three columns per row and that
354  * there are exactly three columns at the poles */
355  if ( ijustify < 2 )
356  {
357  if ( row->ncol < 3 || ( irow == 0 && row->ncol != 3 ) )
358  printf( " irow = %d ncol = %d\n", irow, row->ncol );
359  }
360  else
361  {
362  if ( row->ncol < 6 || ( irow == 0 && row->ncol != 6 ) )
363  printf( " irow = %d ncol = %d\n", irow, row->ncol );
364  }
365 #endif
366 
367  /* Must have at least one column */
368  if ( row->ncol < 1 )
369  row->ncol = 1;
370 
371 #ifdef CHECK_EDGE
372 
373  /* Calculate the minimum delta column (normalized by the number of
374  * columns in the row) */
375  if ( ijustify < 2 )
376  dcol = fabs( ( 2.0 * cos( clat ) * nrow ) + 0.5 - row->ncol );
377  else
378  dcol = 2.0 * fabs((cos(clat) * nrow) + 0.5 - (row->ncol/2));
379  dcol = dcol / row->ncol;
380  if ( dcol < dcol_min )
381  dcol_min = dcol;
382 
383  if ( ijustify < 2 )
384  {
385  dcol = fabs((2.0 * cos(clat) * nrow) + 0.5 - (row->ncol + 1));
386  dcol = dcol / ( row->ncol + 1 );
387  }
388  else
389  {
390  dcol = 2.0 * fabs((cos(clat) * nrow) + 0.5 - ((row->ncol/2) + 1));
391  dcol = dcol / ( row->ncol + 2 );
392  }
393  if ( dcol < dcol_min )
394  dcol_min = dcol;
395 #endif
396 
397  /* Save the inverse of the number of columns */
398  row->ncol_inv = 1.0 / ( ( double ) row->ncol );
399 
400  /* Calculate the column number of the column whose left edge touches
401  the central meridian */
402  if ( ijustify == 1 )
403  row->icol_cen = ( row->ncol + 1 ) / 2;
404  else
405  row->icol_cen = row->ncol / 2;
406 
407  } /* for (irow... */
408 
409  /* Get the number of columns at the equator */
410  ncol_cen = this->row[this->nrow_half - 1].ncol;
411 
412 #ifdef CHECK_EDGE
413 
414  /* Print the minimum delta column and its base 2 log */
415  log2_dcol_min = log( dcol_min ) / log( 2.0 );
416  printf( " dcol_min = %g log2_dcol_min = %g\n", dcol_min, log2_dcol_min );
417 
418  /* Check to be sure the number of columns at the equator is twice the
419  * number of rows */
420  if ( ncol_cen != nrow * 2 )
421  printf( " ncol_cen = %d nrow = %d\n", ncol_cen, nrow );
422 #endif
423 
424  /* Calculate the distance at the equator between
425  * the centers of two columns (and the inverse) */
426  this->col_dist = ( TWO_PI * sphere ) / ncol_cen;
427  this->col_dist_inv = ncol_cen / ( TWO_PI * sphere );
428 
429  /* Give the data structure a valid key */
430 
431  this->key = ISIN_KEY;
432 
433  /* All done */
434  return this;
435 }
436 
437 /*
438 !C******************************************************************************
439 !Description: isinusfor (forward mapping) converts geographic
440  coordinates ('lon', 'lat') to map projection coordinates ('x', 'y').
441 
442 !Input Parameters:
443  lon longitude (radians)
444  lat latitude (radians)
445 
446 !Output Parameters:
447  x easting in map projection (same units as 'sphere')
448  y northing in map projection (same units as 'sphere')
449 
450 !Team Unique Header:
451 
452  ! Usage Notes:
453  1. 'isinusforinit' must have been previously called.
454  2. The longitude must be in the range [-'TWO_PI' to 'TWO_PI'].
455  3. The latitude must be in the range [-'HALF_PI' to 'HALF_PI'].
456 
457 !END****************************************************************************
458 */
459 long isinusfor
460 (
461  double lon,
462  double lat,
463  double *x,
464  double *y
465 )
466 {
467  int istat; /* Status returned from 'Isin_fwd' function */
468 
469  istat = Isin_fwd( isin, lon, lat, x, y );
470  if ( istat != ISIN_SUCCESS )
471  {
472  error( "isinusfor", "bad return from Isin_fwd" );
473  return ISIN_ERROR;
474  }
475 
476  return ISIN_SUCCESS;
477 }
478 
479 /*
480 !C******************************************************************************
481 !Description: Isin_fwd (forward mapping) converts geographic
482  coordinates ('lon', 'lat') to map projection coordinates ('x', 'y').
483 
484 !Input Parameters:
485  this handle for this instance of the integerized sinusoidal
486  projection
487  lon longitude (radians)
488  lat latitude (radians)
489 
490 !Output Parameters:
491  x easting in map projection (same units as 'sphere')
492  y northing in map projection (same units as 'sphere')
493  (returns) status:
494  ISIN_SUCCESS - normal return
495  ISIN_ERANGE - longitude or latitude not in range
496  ISIN_ERROR - error return
497 
498 !Team Unique Header:
499 
500  ! Usage Notes:
501  1. 'Isin_for_init' must have been previously called for the handle.
502  2. The longitude must be in the range [-'TWO_PI' to 'TWO_PI'].
503  3. The latitude must be in the range [-'HALF_PI' to 'HALF_PI'].
504 
505 !END****************************************************************************
506 */
507 int Isin_fwd
508 (
509  const Isin_t * this,
510  double lon,
511  double lat,
512  double *x,
513  double *y
514 )
515 {
516  double row, col; /* Row (zone) and column; column is relative to
517  central; 0.5 is the center of a row or column */
518  double flon; /* Fractional longitude (multiples of PI) */
519  long irow; /* Integer row (zone) number */
520 
521  /* make sure the longitude is between +/- PI radians */
522  lon = adjust_lon(lon);
523 
524  /* Check input parameters */
525  *x = 0.0;
526  *y = 0.0;
527 
528  if ( this == NULL )
529  return Isin_error( &ISIN_BADHANDLE, "Isin_fwd" );
530  if ( this->key != ISIN_KEY )
531  return Isin_error( &ISIN_BADKEY, "Isin_fwd" );
532  if ( lon < -TWO_PI || lon > TWO_PI )
533  return ISIN_ERANGE;
534  if ( lat < -HALF_PI || lat > HALF_PI )
535  return ISIN_ERANGE;
536 
537  /* Northing */
538  *y = this->false_north + ( lat * this->sphere );
539 
540  /* Integer row number */
541  row = ( HALF_PI - lat ) * this->ang_size_inv;
542  irow = (long)row;
543  if ( irow >= this->nrow_half )
544  irow = ( this->nrow - 1 ) - irow;
545  if ( irow < 0 )
546  irow = 0;
547 
548  /* Fractional longitude */
549  flon = ( lon - this->ref_lon ) * TWOPI_INV;
550  if ( flon < 0.0 )
551  flon += ( 1 - ( long ) flon );
552  if ( flon > 1.0 )
553  flon -= ( long ) flon;
554 
555  /* Column number (relative to center) */
556  col = ( this->row[irow].ncol * flon ) - this->row[irow].icol_cen;
557 
558  /* Easting */
559  *x = this->false_east + ( this->col_dist * col );
560 
561  return ISIN_SUCCESS;
562 }
563 
564 /*
565 !C******************************************************************************
566 !Description: Isin_for_free (free) deallocates the 'isin' data structure and
567  array memory.
568 
569 !Input Parameters:
570  this handle for this instance of the integerized sinusoidal
571  projection
572 !Output Parameters:
573  (returns) status:
574  ISIN_SUCCESS - normal return
575  ISIN_ERROR - error return
576 
577 !Team Unique Header:
578 
579  ! Usage Notes:
580  1. 'Isin_for_init' must have been previously called for the handle.
581 
582 !END****************************************************************************
583 */
584 int Isin_for_free
585 (
586  Isin_t * this
587 )
588 {
589  if ( this == NULL )
590  return Isin_error( &ISIN_BADHANDLE, "Isin_for_free" );
591  if ( this->key != ISIN_KEY )
592  return Isin_error( &ISIN_BADKEY, "Isin_for_free" );
593 
594  /* Set the key to NULL */
595  this->key = ( long ) NULL;
596 
597  /* Free the memory */
598  free( this->row );
599  this->row = NULL;
600  free( this );
601  this = NULL;
602 
603  return ISIN_SUCCESS;
604 }
605 
606 /*
607 !C******************************************************************************
608 
609 !Description: Private function to handle errors.
610 
611 !Input Parameters:
612  err Error structure
613  routine String containing name of routine where error occurred
614 
615 !Output Parameters:
616  (returns) status:
617  ISIN_ERROR - normal return
618 
619 !Team Unique Header:
620 
621  !Usage Notes: (none)
622 
623 !END****************************************************************************
624 */
625 static int Isin_error
626 (
627  const error_t * err,
628  const char *routine
629 )
630 {
631 #ifndef NO_OUTPUT
632  fprintf( stderr, " error (isinusfor.c/%s) : (%i) %s\n", routine, err->num,
633  err->str );
634 #endif
635 
636  return ISIN_ERROR;
637 }
#define ISIN_ERANGE
Definition: isin.h:16
void gctp_print_title(const char *proj_name)
Definition: gctp_report.c:14
#define NZONE_MAX
Definition: isin.h:21
#define NULL
Definition: decode_rs.h:63
long isinusforinit(double sphere, double lon_cen_mer, double false_east, double false_north, double dzone, double djustify)
Definition: isinfor.c:115
Definition: isin.h:38
Definition: isin.h:60
float * lat
int Isin_for_free(Isin_t *this)
Definition: isinfor.c:585
double adjust_lon(double x)
Definition: proj_cproj.c:349
#define PI
Definition: l3_get_org.c:6
#define ISIN_KEY
Definition: isin.h:24
a context in which it is NOT documented to do so subscript error
Definition: HISTORY.txt:53
#define EPS_CNVT
Definition: isin.h:23
double ncol_inv
Definition: isin.h:33
#define TWOPI_INV
Definition: isin.h:19
#define TWO_PI
Definition: make_L3_v1.1.c:92
#define HALF_PI
Definition: proj_define.h:84
long ncol
Definition: isin.h:31
void gctp_print_lat_zone(double A)
Definition: gctp_report.c:101
void gctp_print_offsetp(double A, double B)
Definition: gctp_report.c:91
long isinusfor(double lon, double lat, double *x, double *y)
Definition: isinfor.c:460
integer, parameter double
#define ISIN_ERROR
Definition: isin.h:15
#define EPS_SPHERE
Definition: isin.h:22
long icol_cen
Definition: isin.h:32
void gctp_print_cenlonmer(double A)
Definition: gctp_report.c:48
#define fabs(a)
Definition: misc.h:93
float * lon
void gctp_print_radius(double radius)
Definition: gctp_report.c:22
int Isin_fwd(const Isin_t *this, double lon, double lat, double *x, double *y)
Definition: isinfor.c:508
void gctp_print_justify_cols(double A)
Definition: gctp_report.c:109
#define ISIN_SUCCESS
Definition: isin.h:14
Isin_t * Isin_for_init(double sphere, double lon_cen_mer, double false_east, double false_north, long nrow, int ijustify)
Definition: isinfor.c:242
#define NROW_MAX
Definition: isin.h:20