OB.DAAC Logo
NASA Logo
Ocean Color Science Software

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