1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME       : image_conversion.c
3 @DESCRIPTION: File of functions to manipulate image conversion variables
4               (icv). These variables allow conversion of netcdf variables
5               (the MINC image variable, in particular) to a form more
6               convenient for a program.
7 @METHOD     : Routines included in this file :
8               public :
9                  miicv_create
10                  miicv_free
11                  miicv_setdbl
12                  miicv_setint
13                  miicv_setlong
14                  miicv_setstr
15                  miicv_inqdbl
16                  miicv_inqint
17                  miicv_inqlong
18                  miicv_inqstr
19                  miicv_ndattach
20                  miicv_detach
21                  miicv_get
22                  miicv_put
23               semiprivate :
24                  MI_icv_chkid
25               private :
26                  MI_icv_get_type
27                  MI_icv_get_vrange
28                  MI_get_default_range
29                  MI_icv_get_norm
30                  MI_icv_access
31                  MI_icv_zero_buffer
32                  MI_icv_coords_tovar
33                  MI_icv_calc_scale
34 @CREATED    : July 27, 1992. (Peter Neelin, Montreal Neurological Institute)
35 @MODIFIED   :
36  * $Log: image_conversion.c,v $
37  * Revision 6.17  2010-03-02 12:23:14  rotor
38  *  * ported HDF calls to 1.8.x
39  *  * Makefile.am: updated for minccmp
40  *
41  * Revision 6.16  2008/01/17 02:33:02  rotor
42  *  * removed all rcsids
43  *  * removed a bunch of ^L's that somehow crept in
44  *  * removed old (and outdated) BUGS file
45  *
46  * Revision 6.15  2008/01/12 19:08:14  stever
47  * Add __attribute__ ((unused)) to all rcsid variables.
48  *
49  * Revision 6.14  2007/12/12 20:55:26  rotor
50  *  * added a bunch of bug fixes from Claude.
51  *
52  * Revision 6.13  2004/12/14 23:53:46  bert
53  * Get rid of compilation warnings
54  *
55  * Revision 6.12  2004/10/15 13:45:28  bert
56  * Minor changes for Windows compatibility
57  *
58  * Revision 6.11  2004/04/27 15:40:22  bert
59  * Revised logging/error handling
60  *
61  * Revision 6.10  2003/09/18 16:17:00  bert
62  * Correctly cast double to nc_type
63  *
64  * Revision 6.9  2001/11/28 15:38:07  neelin
65  * Removed limit on number of icvs that can exist at one time.
66  *
67  * Revision 6.8  2001/11/13 21:00:24  neelin
68  * Modified icv scaling calculations for no normalization. When the icv
69  * type is double, normalization is always done, regardless of the
70  * normalization setting. When the external type is floating point,
71  * normalization to the slice real range is done (essentially a valid
72  * range scaling, but where the valid range for a float is the slice real
73  * range).
74  *
75  * Revision 6.7  2001/11/13 14:15:17  neelin
76  * Added functions miget_image_range and mivar_exists
77  *
78  * Revision 6.6  2001/08/20 13:16:53  neelin
79  * Removed extraneous variables from MI_icv_get_vrange.
80  *
81  * Revision 6.5  2001/08/16 19:24:11  neelin
82  * Fixes to the code handling valid_range values.
83  *
84  * Revision 6.4  2001/08/16 16:41:31  neelin
85  * Added library functions to handle reading of datatype, sign and valid range,
86  * plus writing of valid range and setting of default ranges. These functions
87  * properly handle differences between valid_range type and image type. Such
88  * difference can cause valid data to appear as invalid when double to float
89  * conversion causes rounding in the wrong direction (out of range).
90  * Modified voxel_loop, volume_io and programs to use these functions.
91  *
92  * Revision 6.3  2001/08/16 13:32:18  neelin
93  * Partial fix for valid_range of different type from image (problems
94  * arising from double to float conversion/rounding). NOT COMPLETE.
95  *
96  * Revision 6.2  2001/04/17 18:40:12  neelin
97  * Modifications to work with NetCDF 3.x
98  * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints).
99  * Changed NC_UNSPECIFIED to NC_NAT.
100  * A few fixes to the configure script.
101  *
102  * Revision 6.1  1999/10/19 14:45:07  neelin
103  * Fixed Log subsitutions for CVS
104  *
105  * Revision 6.0  1997/09/12 13:24:54  neelin
106  * Release of minc version 0.6
107  *
108  * Revision 5.0  1997/08/21  13:25:53  neelin
109  * Release of minc version 0.5
110  *
111  * Revision 4.0  1997/05/07  20:07:52  neelin
112  * Release of minc version 0.4
113  *
114  * Revision 3.3  1997/04/21  17:32:04  neelin
115  * Fixed calculation of scale for icv so that values are not re-scaled
116  * from real values to file floating-point values.
117  *
118  * Revision 3.2  1997/04/10  19:22:18  neelin
119  * Removed redefinition of NULL and added pointer casts in appropriate places.
120  *
121  * Revision 3.1  1997/04/10  18:14:50  neelin
122  * Fixed handling of invalid data when icv scale is zero.
123  *
124  * Revision 3.0  1995/05/15  19:33:12  neelin
125  * Release of minc version 0.3
126  *
127  * Revision 2.3  1995/02/08  19:14:44  neelin
128  * More changes for irix 5 lint.
129  *
130  * Revision 2.2  1995/02/08  19:01:06  neelin
131  * Moved private function declarations from minc_routines.h to appropriate file.
132  *
133  * Revision 2.1  1994/12/09  09:12:30  neelin
134  * Added test in miicv_detach to make sure that icv is attached before
135  * detaching it.
136  *
137  * Revision 2.0  94/09/28  10:37:55  neelin
138  * Release of minc version 0.2
139  *
140  * Revision 1.18  94/09/28  10:37:06  neelin
141  * Pre-release
142  *
143  * Revision 1.17  93/08/11  12:59:31  neelin
144  * We need only increment the chunk pointer (see previous fix) if we are
145  * not doing dimension conversion (dimension conversion handles the
146  * offsets itself).
147  *
148  * Revision 1.16  93/08/11  11:49:36  neelin
149  * Added RCS logging in source.
150  * Fixed bug in MI_icv_access so that pointer to values buffer is incremented
151  * as we loop through the chunks. This affected calls to miicv_get/put that
152  * had MIimagemax/min varying over the values read in one call (ie. reading
153  * or writing a volume with MIimagemax/min varying over slices will give
154  * incorrect results if the volume is read with one call).
155  *
156               January 22, 1993 (P.N.)
157                  - Modified handling of icv properties with miicv_set<type>.
158                    Removed routine miicv_set. Use routines miicv_setdbl,
159                    miicv_setint, miicv_setlong, miicv_setstr instead (this
160                    gives type checking at compile time).
161 @COPYRIGHT  :
162               Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre,
163               Montreal Neurological Institute, McGill University.
164               Permission to use, copy, modify, and distribute this
165               software and its documentation for any purpose and without
166               fee is hereby granted, provided that the above copyright
167               notice appear in all copies.  The author and McGill University
168               make no representations about the suitability of this
169               software for any purpose.  It is provided "as is" without
170               express or implied warranty.
171 ---------------------------------------------------------------------------- */
172 
173 #include "minc_private.h"
174 #include "type_limits.h"
175 
176 /* Private functions */
177 PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid);
178 PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid);
179 PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign);
180 PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid);
181 PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[],
182                           long count[], void *values);
183 PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values);
184 PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp,
185                                 long icv_start[], long icv_count[],
186                                 long var_start[], long var_count[]);
187 PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[]);
188 
189 /* Array of pointers to image conversion structures */
190 static int minc_icv_list_nalloc = 0;
191 static mi_icv_type **minc_icv_list = NULL;
192 
193 /* ----------------------------- MNI Header -----------------------------------
194 @NAME       : miicv_create
195 @INPUT      : (none)
196 @OUTPUT     : (none)
197 @RETURNS    : icv id or MI_ERROR when an error occurs
198 @DESCRIPTION: Creates an image conversion variable (icv) and returns
199               a handle to it.
200 @METHOD     :
201 @GLOBALS    :
202 @CALLS      :
203 @CREATED    : August 7, 1992 (Peter Neelin)
204 @MODIFIED   :
205 ---------------------------------------------------------------------------- */
miicv_create()206 MNCAPI int miicv_create()
207 {
208    int new_icv;       /* Id of newly created icv */
209    mi_icv_type *icvp;  /* Pointer to new icv structure */
210    int idim;
211    int new_nalloc;
212 
213    MI_SAVE_ROUTINE_NAME("miicv_create");
214 
215    /* Look for free slot */
216    for (new_icv=0; new_icv<minc_icv_list_nalloc; new_icv++)
217       if (minc_icv_list[new_icv]==NULL) break;
218 
219    /* If none, then extend the list */
220    if (new_icv>=minc_icv_list_nalloc) {
221 
222       /* How much space will be needed? */
223       new_nalloc = minc_icv_list_nalloc + MI_MAX_NUM_ICV;
224 
225       /* Check for first allocation */
226       if (minc_icv_list_nalloc == 0) {
227          minc_icv_list = MALLOC(new_nalloc, mi_icv_type *);
228       }
229       else {
230          minc_icv_list = REALLOC(minc_icv_list, new_nalloc, mi_icv_type *);
231       }
232 
233       /* Check that the allocation was successful */
234       if (minc_icv_list == NULL) {
235          MI_LOG_SYS_ERROR1("miicv_create");
236          MI_RETURN(MI_ERROR);
237       }
238       /* Put in NULL pointers */
239       for (new_icv=minc_icv_list_nalloc; new_icv<new_nalloc; new_icv++)
240          minc_icv_list[new_icv] = NULL;
241 
242       /* Use the first free slot and update the list length */
243       new_icv = minc_icv_list_nalloc;
244       minc_icv_list_nalloc = new_nalloc;
245 
246    }
247 
248    /* Allocate a new structure */
249    if ((minc_icv_list[new_icv]=MALLOC(1, mi_icv_type))==NULL) {
250       MI_LOG_SYS_ERROR1("miicv_create");
251       MI_RETURN(MI_ERROR);
252    }
253    icvp=minc_icv_list[new_icv];
254 
255    /* Fill in defaults */
256 
257    /* Stuff for calling MI_varaccess */
258    icvp->do_scale = FALSE;
259    icvp->do_dimconvert = FALSE;
260    icvp->do_fillvalue = FALSE;
261    icvp->fill_valid_min = -DBL_MAX;
262    icvp->fill_valid_max = DBL_MAX;
263 
264    /* User defaults */
265    icvp->user_type = NC_SHORT;
266    icvp->user_typelen = nctypelen(icvp->user_type);
267    icvp->user_sign = MI_PRIV_SIGNED;
268    icvp->user_do_range = TRUE;
269    icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type,
270                                             icvp->user_sign);
271    icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type,
272                                             icvp->user_sign);
273    icvp->user_do_norm = FALSE;
274    icvp->user_user_norm = FALSE;
275    icvp->user_maxvar = strdup(MIimagemax);
276    icvp->user_minvar = strdup(MIimagemin);
277    icvp->user_imgmax = MI_DEFAULT_MAX;
278    icvp->user_imgmin = MI_DEFAULT_MIN;
279    icvp->user_do_dimconv = FALSE;
280    icvp->user_do_scalar = TRUE;
281    icvp->user_xdim_dir = MI_ICV_POSITIVE;
282    icvp->user_ydim_dir = MI_ICV_POSITIVE;
283    icvp->user_zdim_dir = MI_ICV_POSITIVE;
284    icvp->user_num_imgdims = 2;
285    icvp->user_keep_aspect = TRUE;
286    icvp->user_do_fillvalue = FALSE;
287    icvp->user_fillvalue = -DBL_MAX;
288    for (idim=0; idim<MI_MAX_IMGDIMS; idim++) {
289       icvp->user_dim_size[idim]=MI_ICV_ANYSIZE;
290    }
291 
292    /* Variable values */
293    icvp->cdfid = MI_ERROR;            /* Set so that we can recognise an */
294    icvp->varid = MI_ERROR;            /* unattached icv */
295 
296    /* Values that can be read by user */
297    icvp->derv_imgmax = MI_DEFAULT_MAX;
298    icvp->derv_imgmin = MI_DEFAULT_MIN;
299    for (idim=0; idim<MI_MAX_IMGDIMS; idim++) {
300       icvp->derv_dim_step[idim] = 0.0;
301       icvp->derv_dim_start[idim] = 0.0;
302    }
303 
304    MI_RETURN(new_icv);
305 }
306 
307 /* ----------------------------- MNI Header -----------------------------------
308 @NAME       : miicv_free
309 @INPUT      : icvid
310 @OUTPUT     : (none)
311 @RETURNS    : MI_ERROR if an error occurs
312 @DESCRIPTION: Frees the image conversion variable (icv)
313 @METHOD     :
314 @GLOBALS    :
315 @CALLS      :
316 @CREATED    : August 7, 1992 (Peter Neelin)
317 @MODIFIED   :
318 ---------------------------------------------------------------------------- */
miicv_free(int icvid)319 MNCAPI int miicv_free(int icvid)
320 {
321    mi_icv_type *icvp;
322 
323    MI_SAVE_ROUTINE_NAME("miicv_free");
324 
325    /* Check icv id */
326    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
327 
328    /* Detach the icv if it is attached */
329    if (icvp->cdfid != MI_ERROR) {
330        if (miicv_detach(icvid) < 0) {
331            MI_RETURN(MI_ERROR);
332        }
333    }
334 
335    /* Free anything allocated at creation time */
336    FREE(icvp->user_maxvar);
337    FREE(icvp->user_minvar);
338 
339    /* Free the structure */
340    FREE(icvp);
341    minc_icv_list[icvid]=NULL;
342 
343    /* Delete entire structure if no longer in use. */
344    int new_icv;
345    for (new_icv=0; new_icv<minc_icv_list_nalloc; new_icv++)
346       if (minc_icv_list[new_icv]!=NULL) break;
347 
348    if (new_icv>=minc_icv_list_nalloc) {
349       FREE(minc_icv_list);
350       minc_icv_list_nalloc=0;
351    }
352 
353    MI_RETURN(MI_NOERROR);
354 }
355 
356 /* ----------------------------- MNI Header -----------------------------------
357 @NAME       : miicv_setdbl
358 @INPUT      : icvid        - icv id
359               icv_property - property of icv to set
360               value        - value to set it to
361 @OUTPUT     : (none)
362 @RETURNS    : MI_ERROR if an error occurs
363 @DESCRIPTION: Sets a property of an icv to a given double value
364               Properties cannot be modified while the icv is attached to a
365               cdf file and variable (see miicv_attach and miicv_detach).
366 @METHOD     :
367 @GLOBALS    :
368 @CALLS      :
369 @CREATED    : August 7, 1992 (Peter Neelin)
370 @MODIFIED   :
371 ---------------------------------------------------------------------------- */
miicv_setdbl(int icvid,int icv_property,double value)372 MNCAPI int miicv_setdbl(int icvid, int icv_property, double value)
373 {
374    int ival, idim;
375    mi_icv_type *icvp;
376 
377    MI_SAVE_ROUTINE_NAME("miicv_setdbl");
378 
379    /* Check icv id */
380    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
381 
382    /* Check that the icv is not attached to a file */
383    if (icvp->cdfid != MI_ERROR) {
384        MI_LOG_ERROR(MI_MSG_ICVATTACHED);
385        MI_RETURN(MI_ERROR);
386    }
387 
388    /* Set the property */
389    switch (icv_property) {
390    case MI_ICV_TYPE:
391       icvp->user_type   = (nc_type) (int) value;
392       icvp->user_typelen= nctypelen(icvp->user_type);
393       icvp->user_vmax   = MI_get_default_range(MIvalid_max, icvp->user_type,
394                                                icvp->user_sign);
395       icvp->user_vmin   = MI_get_default_range(MIvalid_min, icvp->user_type,
396                                                icvp->user_sign);
397       break;
398    case MI_ICV_DO_RANGE:
399       icvp->user_do_range = value;
400       break;
401    case MI_ICV_VALID_MAX:
402       icvp->user_vmax   = value;
403       break;
404    case MI_ICV_VALID_MIN:
405       icvp->user_vmin   = value;
406       break;
407    case MI_ICV_DO_NORM:
408       icvp->user_do_norm = value;
409       break;
410    case MI_ICV_USER_NORM:
411       icvp->user_user_norm = value;
412       break;
413    case MI_ICV_IMAGE_MAX:
414       icvp->user_imgmax = value;
415       break;
416    case MI_ICV_IMAGE_MIN:
417       icvp->user_imgmin = value;
418       break;
419    case MI_ICV_DO_FILLVALUE:
420       icvp->user_do_fillvalue = value;
421       break;
422    case MI_ICV_FILLVALUE:
423       icvp->user_fillvalue = value;
424       break;
425    case MI_ICV_DO_DIM_CONV:
426       icvp->user_do_dimconv = value;
427       break;
428    case MI_ICV_DO_SCALAR:
429       icvp->user_do_scalar = value;
430       break;
431    case MI_ICV_XDIM_DIR:
432       ival = value;
433       icvp->user_xdim_dir = ((ival==MI_ICV_POSITIVE) ||
434                              (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR;
435       break;
436    case MI_ICV_YDIM_DIR:
437       ival = value;
438       icvp->user_ydim_dir = ((ival==MI_ICV_POSITIVE) ||
439                              (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR;
440       break;
441    case MI_ICV_ZDIM_DIR:
442       ival = value;
443       icvp->user_zdim_dir = ((ival==MI_ICV_POSITIVE) ||
444                              (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR;
445       break;
446    case MI_ICV_NUM_IMGDIMS:
447       ival = value;
448       if ((ival<0) || (ival>MI_MAX_IMGDIMS)) {
449           MI_LOG_ERROR(MI_MSG_BADPROP, _("MI_ICV_NUM_IMGDIMS out of range"));
450          MI_RETURN(MI_ERROR);
451       }
452       icvp->user_num_imgdims = ival;
453       break;
454    case MI_ICV_ADIM_SIZE:
455       icvp->user_dim_size[0] = value;
456       break;
457    case MI_ICV_BDIM_SIZE:
458       icvp->user_dim_size[1] = value;
459       break;
460    case MI_ICV_KEEP_ASPECT:
461       icvp->user_keep_aspect = value;
462       break;
463    case MI_ICV_SIGN:
464    case MI_ICV_MAXVAR:
465    case MI_ICV_MINVAR:
466       MI_LOG_ERROR(MI_MSG_BADPROP,
467                     _("Can't store a number in a string value"));
468       MI_RETURN(MI_ERROR);
469       break;
470    default:
471       /* Check for image dimension properties */
472       if ((icv_property>=MI_ICV_DIM_SIZE) &&
473           (icv_property<MI_ICV_DIM_SIZE+MI_MAX_IMGDIMS)) {
474          idim = icv_property - MI_ICV_DIM_SIZE;
475          icvp->user_dim_size[idim] = value;
476       }
477       else {
478           MI_LOG_ERROR(MI_MSG_BADPROP, "Unknown code");
479           MI_RETURN(MI_ERROR);
480       }
481       break;
482    }
483 
484    MI_RETURN(MI_NOERROR);
485 }
486 
487 /* ----------------------------- MNI Header -----------------------------------
488 @NAME       : miicv_setint
489 @INPUT      : icvid        - icv id
490               icv_property - property of icv to set
491               value        - value to set it to
492 @OUTPUT     : (none)
493 @RETURNS    : MI_ERROR if an error occurs
494 @DESCRIPTION: Sets a property of an icv to a given integer value.
495               Properties cannot be modified while the icv is attached to a
496               cdf file and variable (see miicv_attach and miicv_detach).
497 @METHOD     :
498 @GLOBALS    :
499 @CALLS      :
500 @CREATED    : August 7, 1992 (Peter Neelin)
501 @MODIFIED   : January 22, 1993 (P.N.)
502                  - modified handling of icv properties
503 ---------------------------------------------------------------------------- */
miicv_setint(int icvid,int icv_property,int value)504 MNCAPI int miicv_setint(int icvid, int icv_property, int value)
505 {
506 
507    MI_SAVE_ROUTINE_NAME("miicv_setint");
508 
509    if (miicv_setdbl(icvid, icv_property, (double) value) < 0) {
510        MI_RETURN(MI_ERROR);
511    }
512 
513    MI_RETURN(MI_NOERROR);
514 }
515 
516 /* ----------------------------- MNI Header -----------------------------------
517 @NAME       : miicv_setlong
518 @INPUT      : icvid        - icv id
519               icv_property - property of icv to set
520               value        - value to set it to
521 @OUTPUT     : (none)
522 @RETURNS    : MI_ERROR if an error occurs
523 @DESCRIPTION: Sets a property of an icv to a given long integer value.
524               Properties cannot be modified while the icv is attached to a
525               cdf file and variable (see miicv_attach and miicv_detach).
526 @METHOD     :
527 @GLOBALS    :
528 @CALLS      :
529 @CREATED    : January 22, 1993 (Peter Neelin)
530 @MODIFIED   :
531 ---------------------------------------------------------------------------- */
miicv_setlong(int icvid,int icv_property,long value)532 MNCAPI int miicv_setlong(int icvid, int icv_property, long value)
533 {
534 
535    MI_SAVE_ROUTINE_NAME("miicv_setlong");
536 
537    if (miicv_setdbl(icvid, icv_property, (double) value) < 0) {
538        MI_RETURN(MI_ERROR);
539    }
540 
541    MI_RETURN(MI_NOERROR);
542 }
543 
544 /* ----------------------------- MNI Header -----------------------------------
545 @NAME       : miicv_setstr
546 @INPUT      : icvid        - icv id
547               icv_property - property of icv to set
548               value        - value to set it to
549 @OUTPUT     : (none)
550 @RETURNS    : MI_ERROR if an error occurs
551 @DESCRIPTION: Sets a property of an icv to a given string value.
552               Properties cannot be modified while the icv is attached to a
553               cdf file and variable (see miicv_attach and miicv_detach).
554 @METHOD     :
555 @GLOBALS    :
556 @CALLS      :
557 @CREATED    : January 22, 1993 (Peter Neelin)
558 @MODIFIED   :
559 ---------------------------------------------------------------------------- */
miicv_setstr(int icvid,int icv_property,const char * value)560 MNCAPI int miicv_setstr(int icvid, int icv_property, const char *value)
561 {
562    mi_icv_type *icvp;
563 
564    MI_SAVE_ROUTINE_NAME("miicv_setstr");
565 
566    /* Check icv id */
567    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
568 
569    /* Check that the icv is not attached to a file */
570    if (icvp->cdfid != MI_ERROR) {
571        MI_LOG_ERROR(MI_MSG_ICVATTACHED);
572        MI_RETURN(MI_ERROR);
573    }
574 
575    /* Set the property */
576    switch (icv_property) {
577    case MI_ICV_SIGN:
578       icvp->user_sign   = MI_get_sign_from_string(icvp->user_type, value);
579       icvp->user_vmax   = MI_get_default_range(MIvalid_max, icvp->user_type,
580                                                icvp->user_sign);
581       icvp->user_vmin   = MI_get_default_range(MIvalid_min, icvp->user_type,
582                                                icvp->user_sign);
583       break;
584    case MI_ICV_MAXVAR:
585       if (value!=NULL) {
586          FREE(icvp->user_maxvar);
587          icvp->user_maxvar = strdup(value);
588       }
589       break;
590    case MI_ICV_MINVAR:
591       if (value!=NULL) {
592          FREE(icvp->user_minvar);
593          icvp->user_minvar = strdup(value);
594       }
595       break;
596    case MI_ICV_TYPE:
597    case MI_ICV_DO_RANGE:
598    case MI_ICV_VALID_MAX:
599    case MI_ICV_VALID_MIN:
600    case MI_ICV_DO_NORM:
601    case MI_ICV_USER_NORM:
602    case MI_ICV_IMAGE_MAX:
603    case MI_ICV_IMAGE_MIN:
604    case MI_ICV_DO_DIM_CONV:
605    case MI_ICV_DO_SCALAR:
606    case MI_ICV_XDIM_DIR:
607    case MI_ICV_YDIM_DIR:
608    case MI_ICV_ZDIM_DIR:
609    case MI_ICV_NUM_IMGDIMS:
610    case MI_ICV_ADIM_SIZE:
611    case MI_ICV_BDIM_SIZE:
612    case MI_ICV_KEEP_ASPECT:
613        MI_LOG_ERROR(MI_MSG_BADPROP, "Can't store a string in a numeric property");
614        MI_RETURN(MI_ERROR);
615       break;
616    default:
617       /* Check for image dimension properties */
618       if ((icv_property>=MI_ICV_DIM_SIZE) &&
619           (icv_property<MI_ICV_DIM_SIZE+MI_MAX_IMGDIMS)) {
620           MI_LOG_ERROR(MI_MSG_BADPROP, "Can't store a string in a numeric property");
621           MI_RETURN(MI_ERROR);
622       }
623       else {
624           MI_LOG_ERROR(MI_MSG_BADPROP, "Unknown code");
625           MI_RETURN(MI_ERROR);
626       }
627       break;
628    }
629 
630    MI_RETURN(MI_NOERROR);
631 }
632 
633 /* ----------------------------- MNI Header -----------------------------------
634 @NAME       : miicv_inqdbl
635 @INPUT      : icvid        - icv id
636               icv_property - icv property to get
637 @OUTPUT     : value        - value returned
638 @RETURNS    : MI_ERROR if an error occurs
639 @DESCRIPTION: Gets the value of an icv property
640 @METHOD     :
641 @GLOBALS    :
642 @CALLS      :
643 @CREATED    : January 22, 1993 (Peter Neelin)
644 @MODIFIED   :
645 ---------------------------------------------------------------------------- */
miicv_inqdbl(int icvid,int icv_property,double * value)646 MNCAPI int miicv_inqdbl(int icvid, int icv_property, double *value)
647 {
648    int idim;
649    mi_icv_type *icvp;
650 
651    MI_SAVE_ROUTINE_NAME("miicv_inqdbl");
652 
653    /* Check icv id */
654    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
655 
656    /* Set the property */
657    switch (icv_property) {
658    case MI_ICV_TYPE:
659       *value = icvp->user_type; break;
660    case MI_ICV_DO_RANGE:
661       *value = icvp->user_do_range; break;
662    case MI_ICV_VALID_MAX:
663       *value = icvp->user_vmax; break;
664    case MI_ICV_VALID_MIN:
665       *value = icvp->user_vmin; break;
666    case MI_ICV_DO_NORM:
667       *value = icvp->user_do_norm; break;
668    case MI_ICV_USER_NORM:
669       *value = icvp->user_user_norm; break;
670    case MI_ICV_IMAGE_MAX:
671       *value = icvp->user_imgmax; break;
672    case MI_ICV_IMAGE_MIN:
673       *value = icvp->user_imgmin; break;
674    case MI_ICV_NORM_MAX:
675       *value = icvp->derv_imgmax; break;
676    case MI_ICV_NORM_MIN:
677       *value = icvp->derv_imgmin; break;
678    case MI_ICV_DO_FILLVALUE:
679       *value = icvp->user_do_fillvalue; break;
680    case MI_ICV_FILLVALUE:
681       *value = icvp->user_fillvalue; break;
682    case MI_ICV_DO_DIM_CONV:
683       *value = icvp->user_do_dimconv; break;
684    case MI_ICV_DO_SCALAR:
685       *value = icvp->user_do_scalar; break;
686    case MI_ICV_XDIM_DIR:
687       *value = icvp->user_xdim_dir; break;
688    case MI_ICV_YDIM_DIR:
689       *value = icvp->user_ydim_dir; break;
690    case MI_ICV_ZDIM_DIR:
691       *value = icvp->user_zdim_dir; break;
692    case MI_ICV_NUM_IMGDIMS:
693       *value = icvp->user_num_imgdims; break;
694    case MI_ICV_NUM_DIMS:
695       *value = icvp->var_ndims;
696       if (icvp->var_is_vector && icvp->user_do_scalar) (*value)--;
697       break;
698    case MI_ICV_CDFID:
699       *value = icvp->cdfid; break;
700    case MI_ICV_VARID:
701       *value = icvp->varid; break;
702    case MI_ICV_ADIM_SIZE:
703       *value = icvp->user_dim_size[0]; break;
704    case MI_ICV_BDIM_SIZE:
705       *value = icvp->user_dim_size[1]; break;
706    case MI_ICV_ADIM_STEP:
707       *value = icvp->derv_dim_step[0]; break;
708    case MI_ICV_BDIM_STEP:
709       *value = icvp->derv_dim_step[1]; break;
710    case MI_ICV_ADIM_START:
711       *value = icvp->derv_dim_start[0]; break;
712    case MI_ICV_BDIM_START:
713       *value = icvp->derv_dim_start[1]; break;
714    case MI_ICV_KEEP_ASPECT:
715       *value = icvp->user_keep_aspect; break;
716    case MI_ICV_SIGN:
717    case MI_ICV_MAXVAR:
718    case MI_ICV_MINVAR:
719        MI_LOG_ERROR(MI_MSG_BADPROP,
720                      _("Tried to get icv string property as a number"));
721       MI_RETURN(MI_ERROR);
722       break;
723    default:
724       /* Check for image dimension properties */
725       if ((icv_property>=MI_ICV_DIM_SIZE) &&
726           (icv_property<MI_ICV_DIM_SIZE+MI_MAX_IMGDIMS)) {
727          idim = icv_property - MI_ICV_DIM_SIZE;
728          *value = icvp->user_dim_size[idim];
729       }
730       else if ((icv_property>=MI_ICV_DIM_STEP) &&
731                (icv_property<MI_ICV_DIM_STEP+MI_MAX_IMGDIMS)) {
732          idim = icv_property - MI_ICV_DIM_STEP;
733          *value = icvp->derv_dim_step[idim];
734       }
735       else if ((icv_property>=MI_ICV_DIM_START) &&
736                (icv_property<MI_ICV_DIM_START+MI_MAX_IMGDIMS)) {
737          idim = icv_property - MI_ICV_DIM_START;
738          *value = icvp->derv_dim_start[idim];
739       }
740       else {
741          MI_LOG_ERROR(MI_MSG_BADPROP, _("Tried to get unknown icv property"));
742          MI_RETURN(MI_ERROR);
743       }
744       break;
745    }
746 
747    MI_RETURN(MI_NOERROR);
748 }
749 
750 
751 /* ----------------------------- MNI Header -----------------------------------
752 @NAME       : miicv_inqint
753 @INPUT      : icvid        - icv id
754               icv_property - icv property to get
755 @OUTPUT     : value        - value returned
756 @RETURNS    : MI_ERROR if an error occurs
757 @DESCRIPTION: Gets the value of an icv property
758 @METHOD     :
759 @GLOBALS    :
760 @CALLS      :
761 @CREATED    : January 22, 1993 (Peter Neelin)
762 @MODIFIED   :
763 ---------------------------------------------------------------------------- */
miicv_inqint(int icvid,int icv_property,int * value)764 MNCAPI int miicv_inqint(int icvid, int icv_property, int *value)
765 {
766    double dvalue;
767 
768    MI_SAVE_ROUTINE_NAME("miicv_inqint");
769 
770    if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) {
771        MI_RETURN(MI_ERROR);
772    }
773 
774    *value = dvalue;
775 
776    MI_RETURN(MI_NOERROR);
777 }
778 
779 
780 /* ----------------------------- MNI Header -----------------------------------
781 @NAME       : miicv_inqlong
782 @INPUT      : icvid        - icv id
783               icv_property - icv property to get
784 @OUTPUT     : value        - value returned
785 @RETURNS    : MI_ERROR if an error occurs
786 @DESCRIPTION: Gets the value of an icv property
787 @METHOD     :
788 @GLOBALS    :
789 @CALLS      :
790 @CREATED    : January 22, 1993 (Peter Neelin)
791 @MODIFIED   :
792 ---------------------------------------------------------------------------- */
miicv_inqlong(int icvid,int icv_property,long * value)793 MNCAPI int miicv_inqlong(int icvid, int icv_property, long *value)
794 {
795    double dvalue;
796 
797    MI_SAVE_ROUTINE_NAME("miicv_inqlong");
798 
799    if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) {
800        MI_RETURN(MI_ERROR);
801    }
802 
803    *value = dvalue;
804 
805    MI_RETURN(MI_NOERROR);
806 }
807 
808 
809 /* ----------------------------- MNI Header -----------------------------------
810 @NAME       : miicv_inqstr
811 @INPUT      : icvid        - icv id
812               icv_property - icv property to get
813 @OUTPUT     : value        - value returned. Caller must allocate enough
814                  space for return string.
815 @RETURNS    : MI_ERROR if an error occurs
816 @DESCRIPTION: Gets the value of an icv property
817 @METHOD     :
818 @GLOBALS    :
819 @CALLS      :
820 @CREATED    :
821 @MODIFIED   :
822 ---------------------------------------------------------------------------- */
miicv_inqstr(int icvid,int icv_property,char * value)823 MNCAPI int miicv_inqstr(int icvid, int icv_property, char *value)
824 {
825    mi_icv_type *icvp;
826 
827    MI_SAVE_ROUTINE_NAME("miicv_inqstr");
828 
829    /* Check icv id */
830    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
831 
832    /* Set the property */
833    switch (icv_property) {
834    case MI_ICV_SIGN:
835       if (icvp->user_sign==MI_PRIV_SIGNED)
836          (void) strcpy(value, MI_SIGNED);
837       else if (icvp->user_sign==MI_PRIV_UNSIGNED)
838          (void) strcpy(value, MI_UNSIGNED);
839       else
840          (void) strcpy(value, MI_EMPTY_STRING);
841       break;
842    case MI_ICV_MAXVAR:
843       (void) strcpy(value, icvp->user_maxvar);
844       break;
845    case MI_ICV_MINVAR:
846       (void) strcpy(value, icvp->user_minvar);
847       break;
848    case MI_ICV_TYPE:
849    case MI_ICV_DO_RANGE:
850    case MI_ICV_VALID_MAX:
851    case MI_ICV_VALID_MIN:
852    case MI_ICV_DO_NORM:
853    case MI_ICV_USER_NORM:
854    case MI_ICV_IMAGE_MAX:
855    case MI_ICV_IMAGE_MIN:
856    case MI_ICV_NORM_MAX:
857    case MI_ICV_NORM_MIN:
858    case MI_ICV_DO_DIM_CONV:
859    case MI_ICV_DO_SCALAR:
860    case MI_ICV_XDIM_DIR:
861    case MI_ICV_YDIM_DIR:
862    case MI_ICV_ZDIM_DIR:
863    case MI_ICV_NUM_IMGDIMS:
864    case MI_ICV_ADIM_SIZE:
865    case MI_ICV_BDIM_SIZE:
866    case MI_ICV_ADIM_STEP:
867    case MI_ICV_BDIM_STEP:
868    case MI_ICV_ADIM_START:
869    case MI_ICV_BDIM_START:
870    case MI_ICV_KEEP_ASPECT:
871    case MI_ICV_NUM_DIMS:
872    case MI_ICV_CDFID:
873    case MI_ICV_VARID:
874       MI_LOG_ERROR(MI_MSG_BADPROP,
875                     _("Tried to get icv numeric property as a string"));
876       MI_RETURN(MI_ERROR);
877       break;
878    default:
879       /* Check for image dimension properties */
880       if (((icv_property>=MI_ICV_DIM_SIZE) &&
881            (icv_property<MI_ICV_DIM_SIZE+MI_MAX_IMGDIMS)) ||
882           ((icv_property>=MI_ICV_DIM_STEP) &&
883            (icv_property<MI_ICV_DIM_STEP+MI_MAX_IMGDIMS)) ||
884           ((icv_property>=MI_ICV_DIM_START) &&
885            (icv_property<MI_ICV_DIM_START+MI_MAX_IMGDIMS))) {
886          MI_LOG_ERROR(MI_MSG_BADPROP,
887                        _("Tried to get icv numeric property as a string"));
888          MI_RETURN(MI_ERROR);
889       }
890       else {
891          MI_LOG_ERROR(MI_MSG_BADPROP,
892                        _("Tried to get unknown icv property"));
893          MI_RETURN(MI_ERROR);
894       }
895       break;
896    }
897 
898    MI_RETURN(MI_NOERROR);
899 }
900 
901 
902 /* ----------------------------- MNI Header -----------------------------------
903 @NAME       : miicv_ndattach
904 @INPUT      : icvid - icv id
905               cdfid - cdf file id
906               varid - cdf variable id
907 @OUTPUT     : (none)
908 @RETURNS    : MI_ERROR if an error occurs
909 @DESCRIPTION: Attaches an open cdf file and variable to an image conversion
910               variable for subsequent access through miicvget and miicvput.
911               File must be in data mode. This routine differs from
912               miicv_attach in that no dimension conversions will be made
913               on the variable (avoids linking in a significant amount
914               of code).
915 @METHOD     :
916 @GLOBALS    :
917 @CALLS      : NetCDF routines
918 @CREATED    : September 9, 1992 (Peter Neelin)
919 @MODIFIED   :
920 ---------------------------------------------------------------------------- */
miicv_ndattach(int icvid,int cdfid,int varid)921 MNCAPI int miicv_ndattach(int icvid, int cdfid, int varid)
922 {
923    mi_icv_type *icvp;         /* Pointer to icv structure */
924    int idim;
925 
926    MI_SAVE_ROUTINE_NAME("miicv_ndattach");
927 
928    /* Check icv id */
929    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
930 
931    /* If the icv is attached, then detach it */
932    if (icvp->cdfid != MI_ERROR) {
933        if (miicv_detach(icvid) < 0) {
934            MI_RETURN(MI_ERROR);
935        }
936    }
937 
938    /* Inquire about the variable's type, sign and number of dimensions */
939    if (MI_icv_get_type(icvp, cdfid, varid) < 0) {
940        MI_RETURN(MI_ERROR);
941    }
942 
943    /* If not doing range calculations, just set derv_firstdim for
944       MI_icv_access, otherwise, call routines to calculate range and
945       normalization */
946    if (!icvp->user_do_range) {
947       icvp->derv_firstdim = -1;
948    }
949    else {
950       /* Get valid range */
951        if (MI_icv_get_vrange(icvp, cdfid, varid) < 0) {
952            MI_RETURN(MI_ERROR);
953        }
954 
955       /* Get normalization info */
956        if (MI_icv_get_norm(icvp, cdfid, varid) < 0) {
957            MI_RETURN(MI_ERROR);
958        }
959    }
960 
961    /* Set other fields to defaults */
962    icvp->var_is_vector = FALSE;
963    icvp->var_vector_size = 1;
964    icvp->derv_do_zero = FALSE;
965    icvp->derv_do_bufsize_step = FALSE;
966    icvp->derv_var_pix_off = NULL;
967    icvp->derv_usr_pix_off = NULL;
968    for (idim=0; idim<icvp->user_num_imgdims; idim++) {
969       icvp->derv_dim_flip[idim] = FALSE;
970       icvp->derv_dim_grow[idim] = TRUE;
971       icvp->derv_dim_scale[idim] = 1;
972       icvp->derv_dim_off[idim] = 0;
973       icvp->derv_dim_step[idim] = 0.0;
974       icvp->derv_dim_start[idim] = 0.0;
975    }
976 
977    /* Set the do_scale and do_dimconvert fields of icv structure
978       We have to scale only if do_range is TRUE. If ranges don't
979       match, or we have to do user normalization, or if we are normalizing
980       and MIimagemax or MIimagemin vary over the variable. We don't have
981       to scale if input and output are both floating point. */
982 
983    icvp->do_scale =
984       (icvp->user_do_range &&
985        ((icvp->user_vmax!=icvp->var_vmax) ||
986         (icvp->user_vmin!=icvp->var_vmin) ||
987         (icvp->user_do_norm && icvp->user_user_norm) ||
988         (icvp->user_do_norm && (icvp->derv_firstdim>=0))) );
989 
990    if ((icvp->derv_usr_float && icvp->derv_var_float))
991       icvp->do_scale = FALSE;
992 
993    icvp->do_dimconvert = FALSE;
994 
995    /* Set the cdfid and varid fields */
996    icvp->cdfid = cdfid;
997    icvp->varid = varid;
998 
999    MI_RETURN(MI_NOERROR);
1000 }
1001 
1002 
1003 /* ----------------------------- MNI Header -----------------------------------
1004 @NAME       : MI_icv_get_type
1005 @INPUT      : icvp  - pointer to icv structure
1006               cdfid - cdf file id
1007               varid - variable id
1008 @OUTPUT     : (none)
1009 @RETURNS    : MI_ERROR if an error occurs
1010 @DESCRIPTION: Gets the type and sign of a variable for miicv_attach.
1011 @METHOD     :
1012 @GLOBALS    :
1013 @CALLS      : NetCDF routines
1014 @CREATED    : August 10, 1992 (Peter Neelin)
1015 @MODIFIED   :
1016 ---------------------------------------------------------------------------- */
MI_icv_get_type(mi_icv_type * icvp,int cdfid,int varid)1017 PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid)
1018 {
1019    int oldncopts;            /* For saving value of ncopts */
1020    char stringa[MI_MAX_ATTSTR_LEN];
1021    char *string=stringa;     /* String for sign info */
1022 
1023    MI_SAVE_ROUTINE_NAME("MI_icv_get_type");
1024 
1025    /* Inquire about the variable */
1026    if (ncvarinq(cdfid, varid, NULL, &(icvp->var_type),
1027                 &(icvp->var_ndims), icvp->var_dim, NULL) < 0) {
1028        MI_RETURN(MI_ERROR);
1029    }
1030 
1031    /* Check that the variable type is numeric */
1032    if (icvp->var_type==NC_CHAR) {
1033        MI_LOG_ERROR(MI_MSG_VARNOTNUM);
1034        MI_RETURN(MI_ERROR);
1035    }
1036 
1037    /* Try to find out the sign of the variable using MIsigntype. */
1038    oldncopts =get_ncopts(); set_ncopts(0);
1039    string=miattgetstr(cdfid, varid, MIsigntype, MI_MAX_ATTSTR_LEN, string);
1040    set_ncopts(oldncopts);
1041    icvp->var_sign  = MI_get_sign_from_string(icvp->var_type, string);
1042 
1043    /* Get type lengths */
1044    icvp->var_typelen = nctypelen(icvp->var_type);
1045    icvp->user_typelen = nctypelen(icvp->user_type);
1046 
1047    MI_RETURN(MI_NOERROR);
1048 }
1049 
1050 
1051 /* ----------------------------- MNI Header -----------------------------------
1052 @NAME       : MI_icv_get_vrange
1053 @INPUT      : icvp  - pointer to icv structure
1054               cdfid - cdf file id
1055               varid - variable id
1056 @OUTPUT     : (none)
1057 @RETURNS    : MI_ERROR if an error occurs
1058 @DESCRIPTION: Gets the valid range of a variable
1059 @METHOD     :
1060 @GLOBALS    :
1061 @CALLS      : NetCDF routines
1062 @CREATED    : August 10, 1992 (Peter Neelin)
1063 @MODIFIED   :
1064 ---------------------------------------------------------------------------- */
MI_icv_get_vrange(mi_icv_type * icvp,int cdfid,int varid)1065 PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid)
1066 {
1067    double vrange[2];         /* Valid range buffer */
1068 
1069    MI_SAVE_ROUTINE_NAME("MI_icv_get_vrange");
1070 
1071    if (miget_valid_range(cdfid, varid, vrange) == MI_ERROR) {
1072       MI_RETURN(MI_ERROR);
1073    }
1074    icvp->var_vmin = vrange[0];
1075    icvp->var_vmax = vrange[1];
1076 
1077    MI_RETURN(MI_NOERROR);
1078 }
1079 
1080 
1081 /* ----------------------------- MNI Header -----------------------------------
1082 @NAME       : MI_get_default_range
1083 @INPUT      : what     - MIvalid_min means get default min, MIvalid_min means
1084                  get default min
1085               datatype - type of variable
1086               sign     - sign of variable
1087 @OUTPUT     : (none)
1088 @RETURNS    : default maximum or minimum for the datatype
1089 @DESCRIPTION: Return the defaults maximum or minimum for a given datatype
1090               and sign.
1091 @METHOD     :
1092 @GLOBALS    :
1093 @CALLS      : NetCDF routines
1094 @CREATED    : August 10, 1992 (Peter Neelin)
1095 @MODIFIED   :
1096 ---------------------------------------------------------------------------- */
MI_get_default_range(char * what,nc_type datatype,int sign)1097 PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign)
1098 {
1099    double range[2];
1100 
1101    MI_SAVE_ROUTINE_NAME("MI_get_default_range");
1102 
1103    (void) miget_default_range(datatype, (sign == MI_PRIV_SIGNED), range);
1104 
1105    if (STRINGS_EQUAL(what, MIvalid_max)) {
1106       MI_RETURN(range[1]);
1107    }
1108    else if (STRINGS_EQUAL(what, MIvalid_min)) {
1109       MI_RETURN(range[0]);
1110    }
1111    else {
1112       set_ncopts(NC_VERBOSE | NC_FATAL);
1113       MI_LOG_PKG_ERROR2(-1,"MINC bug - this line should never be printed");
1114    }
1115 
1116    MI_RETURN(MI_DEFAULT_MIN);
1117 }
1118 
1119 
1120 /* ----------------------------- MNI Header -----------------------------------
1121 @NAME       : MI_icv_get_norm
1122 @INPUT      : icvp  - pointer to icv structure
1123               cdfid - cdf file id
1124               varid - variable id
1125 @OUTPUT     : (none)
1126 @RETURNS    : MI_ERROR if an error occurs
1127 @DESCRIPTION: Gets the normalization info for a variable
1128 @METHOD     :
1129 @GLOBALS    :
1130 @CALLS      : NetCDF routines
1131 @CREATED    : August 10, 1992 (Peter Neelin)
1132 @MODIFIED   :
1133 ---------------------------------------------------------------------------- */
MI_icv_get_norm(mi_icv_type * icvp,int cdfid,int varid)1134 PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid)
1135      /* ARGSUSED */
1136 {
1137    int oldncopts;             /* For saving value of ncopts */
1138    int vid[2];                /* Variable ids for max and min */
1139    int ndims;                 /* Number of dimensions for image max and min */
1140    int dim[MAX_VAR_DIMS];     /* Dimensions */
1141    int imm;                   /* Counter for looping through max and min */
1142    double image_range[2];
1143    int idim, i;
1144 
1145    MI_SAVE_ROUTINE_NAME("MI_icv_get_norm");
1146 
1147    /* Check for floating point or double precision values for user or
1148       in variable - set flag to not do normalization if needed */
1149    icvp->derv_var_float = ((icvp->var_type == NC_DOUBLE) ||
1150                            (icvp->var_type == NC_FLOAT));
1151    icvp->derv_usr_float = ((icvp->user_type == NC_DOUBLE) ||
1152                            (icvp->user_type == NC_FLOAT));
1153 
1154    /* Initialize first dimension over which MIimagemax or MIimagemin
1155       vary - assume that they don't vary at all */
1156    icvp->derv_firstdim=(-1);
1157 
1158    /* Look for image max, image min variables */
1159    oldncopts=get_ncopts(); set_ncopts(0);
1160    icvp->imgmaxid=ncvarid(cdfid, icvp->user_maxvar);
1161    icvp->imgminid=ncvarid(cdfid, icvp->user_minvar);
1162    set_ncopts(oldncopts);
1163 
1164    /* Check to see if normalization to variable max, min should be done */
1165    if (!icvp->user_do_norm) {
1166       icvp->derv_imgmax = MI_DEFAULT_MAX;
1167       icvp->derv_imgmin = MI_DEFAULT_MIN;
1168    }
1169    else {
1170 
1171       /* Get the image min and max, either from the user definition or
1172          from the file. */
1173       if (icvp->user_user_norm) {
1174          icvp->derv_imgmax = icvp->user_imgmax;
1175          icvp->derv_imgmin = icvp->user_imgmin;
1176       }
1177       else {
1178           if (miget_image_range(cdfid, image_range) < 0) {
1179               MI_RETURN(MI_ERROR);
1180           }
1181          icvp->derv_imgmin = image_range[0];
1182          icvp->derv_imgmax = image_range[1];
1183       }
1184 
1185       /* Check each of the dimensions of image-min/max variables to see
1186          which is the fastest varying dimension of the image variable. */
1187       vid[0]=icvp->imgminid;
1188       vid[1]=icvp->imgmaxid;
1189       if ((vid[0] != MI_ERROR) && (vid[1] != MI_ERROR)) {
1190          for (imm=0; imm < 2; imm++) {
1191              if (ncvarinq(cdfid, vid[imm], NULL, NULL, &ndims, dim, NULL) < 0) {
1192                  MI_RETURN(MI_ERROR);
1193              }
1194             for (idim=0; idim<ndims; idim++) {
1195                for (i=0; i<icvp->var_ndims; i++) {
1196                   if (icvp->var_dim[i]==dim[idim])
1197                      icvp->derv_firstdim = MAX(icvp->derv_firstdim, i);
1198                }
1199             }
1200          }
1201       }
1202 
1203    }
1204 
1205    MI_RETURN(MI_NOERROR);
1206 }
1207 
1208 
1209 /* ----------------------------- MNI Header -----------------------------------
1210 @NAME       : miicv_detach
1211 @INPUT      : icvid - icv id
1212 @OUTPUT     : (none)
1213 @RETURNS    : MI_ERROR if an error occurs
1214 @DESCRIPTION: Detaches the cdf file and variable from the image conversion
1215               variable, allowing modifications to the icv.
1216 @METHOD     :
1217 @GLOBALS    :
1218 @CALLS      : NetCDF routines
1219 @CREATED    : August 10, 1992 (Peter Neelin)
1220 @MODIFIED   :
1221 ---------------------------------------------------------------------------- */
miicv_detach(int icvid)1222 MNCAPI int miicv_detach(int icvid)
1223 {
1224    mi_icv_type *icvp;
1225    int idim;
1226 
1227    MI_SAVE_ROUTINE_NAME("miicv_detach");
1228 
1229    /* Check icv id */
1230    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
1231 
1232    /* Check that the icv is in fact attached */
1233    if (icvp->cdfid == MI_ERROR)
1234       MI_RETURN(MI_NOERROR);
1235 
1236    /* Free the pixel offset arrays */
1237    if (icvp->derv_var_pix_off != NULL) FREE(icvp->derv_var_pix_off);
1238    if (icvp->derv_usr_pix_off != NULL) FREE(icvp->derv_usr_pix_off);
1239 
1240    /* Reset values that are read-only (and set when attached) */
1241    icvp->derv_imgmax = MI_DEFAULT_MAX;
1242    icvp->derv_imgmin = MI_DEFAULT_MIN;
1243    for (idim=0; idim<MI_MAX_IMGDIMS; idim++) {
1244       icvp->derv_dim_step[idim] = 0.0;
1245       icvp->derv_dim_start[idim] = 0.0;
1246    }
1247 
1248    /* Set cdfid field to MI_ERROR to indicate that icv is detached */
1249    icvp->cdfid = MI_ERROR;
1250    icvp->varid = MI_ERROR;
1251 
1252    MI_RETURN(MI_NOERROR);
1253 }
1254 
1255 
1256 /* ----------------------------- MNI Header -----------------------------------
1257 @NAME       : miicv_get
1258 @INPUT      : icvid  - icv id
1259               start  - coordinates of start of hyperslab (see ncvarget)
1260               count  - size of hyperslab (see ncvarget)
1261 @OUTPUT     : values - array of values returned
1262 @RETURNS    : MI_ERROR if an error occurs
1263 @DESCRIPTION: Gets a hyperslab of values from a netcdf variable through
1264               the image conversion variable (icvid)
1265 @METHOD     :
1266 @GLOBALS    :
1267 @CALLS      : NetCDF routines
1268 @CREATED    : August 10, 1992 (Peter Neelin)
1269 @MODIFIED   :
1270 ---------------------------------------------------------------------------- */
miicv_get(int icvid,long start[],long count[],void * values)1271 MNCAPI int miicv_get(int icvid, long start[], long count[], void *values)
1272 {
1273    mi_icv_type *icvp;
1274 
1275    MI_SAVE_ROUTINE_NAME("miicv_get");
1276 
1277    /* Check icv id */
1278    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
1279 
1280    /* Get the data */
1281    if (MI_icv_access(MI_PRIV_GET, icvp, start, count, values) < 0) {
1282        MI_RETURN(MI_ERROR);
1283    }
1284 
1285    MI_RETURN(MI_NOERROR);
1286 }
1287 
1288 
1289 /* ----------------------------- MNI Header -----------------------------------
1290 @NAME       : miicv_put
1291 @INPUT      : icvid  - icv id
1292               start  - coordinates of start of hyperslab (see ncvarput)
1293               count  - size of hyperslab (see ncvarput)
1294               values - array of values to store
1295 @OUTPUT     : (none)
1296 @RETURNS    : MI_ERROR if an error occurs
1297 @DESCRIPTION: Stores a hyperslab of values in a netcdf variable through
1298               the image conversion variable (icvid)
1299 @METHOD     :
1300 @GLOBALS    :
1301 @CALLS      : NetCDF routines
1302 @CREATED    :
1303 @MODIFIED   :
1304 ---------------------------------------------------------------------------- */
miicv_put(int icvid,long start[],long count[],void * values)1305 MNCAPI int miicv_put(int icvid, long start[], long count[], void *values)
1306 {
1307    mi_icv_type *icvp;
1308 
1309    MI_SAVE_ROUTINE_NAME("miicv_put");
1310 
1311    /* Check icv id */
1312    if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR);
1313 
1314 
1315    if (MI_icv_access(MI_PRIV_PUT, icvp, start, count, values) < 0) {
1316        MI_RETURN(MI_ERROR);
1317    }
1318 
1319    MI_RETURN(MI_NOERROR);
1320 }
1321 
1322 
1323 /* ----------------------------- MNI Header -----------------------------------
1324 @NAME       : MI_icv_access
1325 @INPUT      : operation - MI_PRIV_GET or MI_PRIV_PUT
1326               icvid     - icv id
1327               start     - coordinates of start of hyperslab (see ncvarput)
1328               count     - size of hyperslab (see ncvarput)
1329               values    - array of values to put
1330 @OUTPUT     : values    - array of values to get
1331 @RETURNS    : MI_ERROR if an error occurs
1332 @DESCRIPTION: Does the work of getting or putting values from an icv.
1333 @METHOD     :
1334 @GLOBALS    :
1335 @CALLS      : NetCDF routines
1336 @CREATED    : August 11, 1992 (Peter Neelin)
1337 @MODIFIED   :
1338 ---------------------------------------------------------------------------- */
MI_icv_access(int operation,mi_icv_type * icvp,long start[],long count[],void * values)1339 PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[],
1340                           long count[], void *values)
1341 {
1342    int *bufsize_step;                /* Pointer to array giving increments
1343                                         for allocating variable buffer
1344                                         (NULL if we don't care) */
1345    long chunk_count[MAX_VAR_DIMS];   /* Number of elements to get for chunk */
1346    long chunk_start[MAX_VAR_DIMS];   /* Starting index for getting a chunk */
1347    long chunk_size;                  /* Size of chunk in bytes */
1348    void *chunk_values;               /* Pointer to next chunk to get */
1349    long var_start[MAX_VAR_DIMS];     /* Coordinates of first var element */
1350    long var_count[MAX_VAR_DIMS];     /* Edge lengths in variable */
1351    long var_end[MAX_VAR_DIMS];       /* Coordinates of last var element */
1352    int firstdim;
1353    int idim, ndims;
1354 
1355    MI_SAVE_ROUTINE_NAME("MI_icv_access");
1356 
1357    /* Check that icv is attached to a variable */
1358    if (icvp->cdfid == MI_ERROR) {
1359        MI_LOG_ERROR(MI_MSG_ICVNOTATTACHED);
1360 
1361        MI_RETURN(MI_ERROR);
1362    }
1363 
1364    /* Zero the user's buffer if needed */
1365    if ((operation == MI_PRIV_GET) && (icvp->derv_do_zero))
1366        if (MI_icv_zero_buffer(icvp, count, values) < 0) {
1367            MI_RETURN(MI_ERROR);
1368        }
1369 
1370    /* Translate icv coordinates to variable coordinates */
1371    if (MI_icv_coords_tovar(icvp, start, count, var_start, var_count) < 0) {
1372        MI_RETURN(MI_ERROR);
1373    }
1374 
1375    /* Save icv coordinates for future reference (for dimension conversion
1376       routines) */
1377    ndims = icvp->var_ndims;
1378    if (icvp->var_is_vector && icvp->user_do_scalar)
1379       ndims--;
1380    for (idim=0; idim < ndims; idim++) {
1381       icvp->derv_icv_start[idim] = start[idim];
1382       icvp->derv_icv_count[idim] = count[idim];
1383    }
1384 
1385    /* Do we care about getting variable in convenient increments ?
1386       Only if we are getting data and the icv structure wants it */
1387    if ((operation==MI_PRIV_GET) && (icvp->derv_do_bufsize_step))
1388       bufsize_step = icvp->derv_bufsize_step;
1389    else
1390       bufsize_step = NULL;
1391 
1392    /* Set up variables for looping through variable. The biggest chunk that
1393       we can get in one call is determined by the subscripts of MIimagemax
1394       and MIimagemin. These must be constant over the chunk that we get if
1395       we are doing normalization. */
1396    for (idim=0; idim<icvp->var_ndims; idim++) {
1397       chunk_start[idim] = var_start[idim];
1398       var_end[idim]=var_start[idim]+var_count[idim];
1399    }
1400    (void) miset_coords(icvp->var_ndims, 1L, chunk_count);
1401    /* Get size of chunk in user's buffer. Dimension conversion routines
1402       don't need the buffer pointer incremented - they do it themselves */
1403    if (!icvp->do_dimconvert)
1404       chunk_size = nctypelen(icvp->user_type);
1405    else
1406       chunk_size = 0;
1407    for (idim=MAX(icvp->derv_firstdim+1,0); idim < icvp->var_ndims; idim++) {
1408       chunk_count[idim]=var_count[idim];
1409       chunk_size *= chunk_count[idim];
1410    }
1411    firstdim = MAX(icvp->derv_firstdim, 0);
1412 
1413    /* Loop through variable */
1414    chunk_values = values;
1415    while (chunk_start[0] < var_end[0]) {
1416 
1417       /* Set the do_fillvalue flag if the user wants it and we are doing
1418          a get. We must do it inside the loop since the scale factor
1419          calculation can change it if the scale is zero. (Fillvalue checking
1420          is always done if the the scale is zero.) */
1421       icvp->do_fillvalue =
1422          icvp->user_do_fillvalue && (operation == MI_PRIV_GET);
1423       icvp->fill_valid_min = icvp->var_vmin;
1424       icvp->fill_valid_max = icvp->var_vmax;
1425 
1426       /* Calculate scale factor */
1427       if (icvp->do_scale) {
1428           if (MI_icv_calc_scale(operation, icvp, chunk_start) < 0) {
1429               MI_RETURN(MI_ERROR);
1430           }
1431       }
1432 
1433 // fprintf(stderr, "Getting values at %p\n", chunk_start);
1434 
1435       /* Get the values */
1436       if (MI_varaccess(operation, icvp->cdfid, icvp->varid,
1437                        chunk_start, chunk_count,
1438                        icvp->user_type, icvp->user_sign,
1439                        chunk_values, bufsize_step, icvp) < 0) {
1440           MI_RETURN(MI_ERROR);
1441       }
1442 
1443       /* Increment the start counter */
1444       chunk_start[firstdim] += chunk_count[firstdim];
1445       for (idim=firstdim;
1446            (idim>0) && (chunk_start[idim]>=var_end[idim]); idim--) {
1447          chunk_start[idim]=var_start[idim];
1448          chunk_start[idim-1]++;
1449       }
1450 
1451       /* Increment the pointer to values */
1452       chunk_values = (void *) ((char *) chunk_values + (size_t) chunk_size);
1453 
1454    }
1455 
1456    MI_RETURN(MI_NOERROR);
1457 }
1458 
1459 
1460 /* ----------------------------- MNI Header -----------------------------------
1461 @NAME       : MI_icv_zero_buffer
1462 @INPUT      : icvp      - icv structure pointer
1463               count     - count vector
1464               values    - pointer to user's buffer
1465 @OUTPUT     :
1466 @RETURNS    : MI_ERROR if an error occurs
1467 @DESCRIPTION: Zeros the user's buffer, with a size given by the vector count.
1468 @METHOD     :
1469 @GLOBALS    :
1470 @CALLS      : NetCDF routines
1471 @CREATED    : September 9, 1992 (Peter Neelin)
1472 @MODIFIED   :
1473 ---------------------------------------------------------------------------- */
MI_icv_zero_buffer(mi_icv_type * icvp,long count[],void * values)1474 PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values)
1475 {
1476    double zeroval, zerobuf;
1477    void *zerostart;
1478    int zerolen, idim, ndims;
1479    char *bufptr, *bufend, *zeroptr, *zeroend;
1480    long buflen;
1481 
1482    MI_SAVE_ROUTINE_NAME("MI_icv_zero_buffer");
1483 
1484    /* Create a zero pixel and get its size */
1485    zerostart = (void *) (&zerobuf);
1486    if (icvp->do_scale)
1487       zeroval = icvp->offset;
1488    else
1489       zeroval = 0.0;
1490    {MI_FROM_DOUBLE(zeroval, icvp->user_type, icvp->user_sign, zerostart)}
1491    zerolen = icvp->user_typelen;
1492 
1493    /* Get the buffer size */
1494    ndims = icvp->var_ndims;
1495    if (icvp->var_is_vector && icvp->user_do_scalar)
1496       ndims--;
1497    buflen = zerolen;
1498    for (idim=0; idim<ndims; idim++)
1499       buflen *= count[idim];
1500 
1501    /* Loop through the buffer, copying the zero pixel */
1502    bufend = (char *) values + buflen;
1503    zeroend = (char *) zerostart + zerolen;
1504    for (bufptr = (char *) values, zeroptr = (char *) zerostart;
1505         bufptr < bufend; bufptr++, zeroptr++) {
1506       if (zeroptr >= zeroend)
1507          zeroptr = (char *) zerostart;
1508       *bufptr = *zeroptr;
1509    }
1510 
1511    MI_RETURN(MI_NOERROR);
1512 }
1513 
1514 
1515 /* ----------------------------- MNI Header -----------------------------------
1516 @NAME       : MI_icv_coords_tovar
1517 @INPUT      : icvp      - icv structure pointer
1518               icv_start - start vector for icv
1519               icv_count - count vector for icv
1520 @OUTPUT     : var_start - start vector for variable
1521               var_count - count vector for variable
1522 @RETURNS    : MI_ERROR if an error occurs
1523 @DESCRIPTION: Converts a start and count vector for referencing an icv
1524               to the corresponding vectors for referencing a NetCDF variable.
1525 @METHOD     :
1526 @GLOBALS    :
1527 @CALLS      : NetCDF routines
1528 @CREATED    : September 1, 1992 (Peter Neelin)
1529 @MODIFIED   :
1530 ---------------------------------------------------------------------------- */
MI_icv_coords_tovar(mi_icv_type * icvp,long icv_start[],long icv_count[],long var_start[],long var_count[])1531 PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp,
1532                                 long icv_start[], long icv_count[],
1533                                 long var_start[], long var_count[])
1534 {
1535    int i, j;
1536    int num_non_img_dims;
1537    long coord, last_coord, icv_dim_size;
1538 
1539    MI_SAVE_ROUTINE_NAME("MI_icv_coords_tovar");
1540 
1541    /* Do we have to worry about dimension conversions? If not, then
1542       just copy the vectors and return. */
1543    if (!icvp->do_dimconvert) {
1544       for (i=0; i < icvp->var_ndims; i++) {
1545          var_count[i] = icv_count[i];
1546          var_start[i] = icv_start[i];
1547       }
1548       MI_RETURN(MI_NOERROR);
1549    }
1550 
1551    /* Get the number of non image dimensions */
1552    num_non_img_dims=icvp->var_ndims-icvp->user_num_imgdims;
1553    if (icvp->var_is_vector)
1554       num_non_img_dims--;
1555 
1556    /* Go through first, non-image dimensions */
1557    for (i=0; i < num_non_img_dims; i++) {
1558       var_count[i] = icv_count[i];
1559       var_start[i] = icv_start[i];
1560    }
1561 
1562    /* Go through image dimensions */
1563    for (i=num_non_img_dims, j=icvp->user_num_imgdims-1;
1564         i < num_non_img_dims+icvp->user_num_imgdims; i++, j--) {
1565       /* Check coordinates. */
1566       icv_dim_size = (icvp->user_dim_size[j] > 0) ?
1567             icvp->user_dim_size[j] : icvp->var_dim_size[j];
1568       last_coord = icv_start[i] + icv_count[i] - 1;
1569       if ((icv_start[i]<0) || (icv_start[i]>=icv_dim_size) ||
1570           (last_coord<0) || (last_coord>=icv_dim_size) ||
1571           (icv_count[i]<0)) {
1572           MI_LOG_ERROR(MI_MSG_ICVCOORDS);
1573           MI_RETURN(MI_ERROR);
1574       }
1575       /* Remove offset */
1576       coord = icv_start[i]-icvp->derv_dim_off[j];
1577       /* Check for growing or shrinking */
1578       if (icvp->derv_dim_grow[j]) {
1579          var_count[i] = (icv_count[i]+icvp->derv_dim_scale[j]-1)
1580             /icvp->derv_dim_scale[j];
1581          coord /= icvp->derv_dim_scale[j];
1582       }
1583       else {
1584          var_count[i] = icv_count[i]*icvp->derv_dim_scale[j];
1585          coord *= icvp->derv_dim_scale[j];
1586       }
1587       /* Check for flipping */
1588       if (icvp->derv_dim_flip[j])
1589          coord = icvp->var_dim_size[j] - coord -
1590             ((icv_count!=NULL) ? var_count[i] : 0L);
1591       var_start[i] = coord;
1592       /* Check for indices out of variable bounds (but in icv bounds) */
1593       last_coord = var_start[i] + var_count[i];
1594       if ((var_start[i]<0) || (last_coord>=icvp->var_dim_size[j])) {
1595          if (var_start[i]<0) var_start[i] = 0;
1596          if (last_coord>=icvp->var_dim_size[j])
1597             last_coord = icvp->var_dim_size[j] - 1;
1598          /* Enforce similar bounds on var_start (bert) */
1599          if (var_start[i] >= icvp->var_dim_size[j])
1600             var_start[i] = icvp->var_dim_size[j] - 1;
1601          var_count[i] = last_coord - var_start[i] + 1;
1602       }
1603    }
1604 
1605    /* Check for vector dimension */
1606    if (icvp->var_is_vector) {
1607       if (icvp->user_do_scalar) {
1608          var_count[icvp->var_ndims-1] = icvp->var_vector_size;
1609          var_start[icvp->var_ndims-1] = 0;
1610       }
1611       else {
1612          var_count[icvp->var_ndims-1] = icv_count[icvp->var_ndims-1];
1613          var_start[icvp->var_ndims-1] = icv_start[icvp->var_ndims-1];
1614       }
1615    }
1616 
1617    MI_RETURN(MI_NOERROR);
1618 }
1619 
1620 
1621 /* ----------------------------- MNI Header -----------------------------------
1622 @NAME       : MI_icv_calc_scale
1623 @INPUT      : operation - MI_PRIV_GET or MI_PRIV_PUT
1624               icvp      - icv structure pointer
1625               coords    - coordinates of first value to get or put
1626 @OUTPUT     : icvp      - fields scale and offset set
1627 @RETURNS    : MI_ERROR if an error occurs
1628 @DESCRIPTION: Calculates the scale and offset needed for getting or putting
1629               values, starting at index coords (assumes that scale is constant
1630               over that range).
1631 @METHOD     :
1632 @GLOBALS    :
1633 @CALLS      : NetCDF routines
1634 @CREATED    : August 10, 1992 (Peter Neelin)
1635 @MODIFIED   :
1636 ---------------------------------------------------------------------------- */
MI_icv_calc_scale(int operation,mi_icv_type * icvp,long coords[])1637 PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[])
1638 {
1639    long mmcoords[MAX_VAR_DIMS];   /* Coordinates for max/min variable */
1640    double usr_imgmax, usr_imgmin;
1641    double var_imgmax, var_imgmin;
1642    double var_imgmax_true, var_imgmin_true;
1643    double usr_vmax, usr_vmin;
1644    double var_vmax, var_vmin;
1645    double slice_imgmax, slice_imgmin;
1646    double usr_scale;
1647    double denom;
1648 
1649    MI_SAVE_ROUTINE_NAME("MI_icv_calc_scale");
1650 
1651    /* Set variable valid range */
1652    var_vmax = icvp->var_vmax;
1653    var_vmin = icvp->var_vmin;
1654 
1655    /* Set image max/min for user and variable values depending on whether
1656       normalization should be done or not. Whenever floating-point values
1657       are involved, some type of normalization is done. When the icv type
1658       is floating point, normalization is always done. When the file type
1659       is floating point and the icv type is integer, slices are normalized
1660       to the real range of the slice (or chunk being read). */
1661    if (!icvp->derv_var_float && !icvp->derv_usr_float && !icvp->user_do_norm) {
1662       usr_imgmax = var_imgmax = MI_DEFAULT_MAX;
1663       usr_imgmin = var_imgmin = MI_DEFAULT_MIN;
1664    }
1665    else {
1666 
1667       /* Get the real range for the slice or chunk that is being examined */
1668       slice_imgmax = MI_DEFAULT_MAX;
1669       slice_imgmin = MI_DEFAULT_MIN;
1670       if ((!icvp->derv_var_float || !icvp->user_do_norm) &&
1671           (icvp->imgmaxid!=MI_ERROR) && (icvp->imgminid!=MI_ERROR)) {
1672          if (mitranslate_coords(icvp->cdfid, icvp->varid, coords,
1673                                 icvp->imgmaxid, mmcoords) == NULL)
1674             MI_RETURN(MI_ERROR);
1675          if (mivarget1(icvp->cdfid, icvp->imgmaxid, mmcoords,
1676                        NC_DOUBLE, NULL, &slice_imgmax) < 0) {
1677              MI_RETURN(MI_ERROR);
1678          }
1679          if (mitranslate_coords(icvp->cdfid, icvp->varid, coords,
1680                                 icvp->imgminid, mmcoords) == NULL) {
1681             MI_RETURN(MI_ERROR);
1682          }
1683          if (mivarget1(icvp->cdfid, icvp->imgminid, mmcoords,
1684                        NC_DOUBLE, NULL, &slice_imgmin) < 0) {
1685              MI_RETURN(MI_ERROR);
1686          }
1687       }
1688 
1689       /* Get the user real range */
1690       if (icvp->user_do_norm) {
1691          usr_imgmax = icvp->derv_imgmax;
1692          usr_imgmin = icvp->derv_imgmin;
1693       }
1694       else {
1695          usr_imgmax = slice_imgmax;
1696          usr_imgmin = slice_imgmin;
1697       }
1698 
1699       /* Get the file real range */
1700       if (icvp->derv_var_float) {
1701          var_imgmax = var_vmax;
1702          var_imgmin = var_vmin;
1703       }
1704       else {
1705          var_imgmax = slice_imgmax;
1706          var_imgmin = slice_imgmin;
1707       }
1708    }
1709 
1710    /* Prevent scaling between file floats and real value */
1711    if (icvp->derv_var_float) {
1712       var_imgmax = var_vmax;
1713       var_imgmin = var_vmin;
1714    }
1715 
1716    /* Get user valid range */
1717    if (icvp->derv_usr_float) {
1718       usr_vmax = usr_imgmax;
1719       usr_vmin = usr_imgmin;
1720    }
1721    else {
1722       usr_vmax = icvp->user_vmax;
1723       usr_vmin = icvp->user_vmin;
1724    }
1725 
1726    /* Save real var_imgmin/max for fillvalue checking later */
1727    var_imgmax_true = var_imgmax;
1728    var_imgmin_true = var_imgmin;
1729 
1730    /* Even though we have already carefully set the vmax/min and imgmax/min
1731       values to handle the floating point case, we can still have problems
1732       with the scale calculations (rounding errors) if full range max/min
1733       are used (-FLT_MAX to FLT_MAX). To avoid this, we just force the
1734       values to 0 and 1 which will give the correct scale. That is why
1735       we save the true values above. */
1736 
1737    if (icvp->derv_usr_float) {
1738       usr_imgmax = usr_vmax = MI_DEFAULT_MAX;
1739       usr_imgmin = usr_vmin = MI_DEFAULT_MIN;
1740    }
1741    if (icvp->derv_var_float) {
1742       var_imgmax = var_vmax = MI_DEFAULT_MAX;
1743       var_imgmin = var_vmin = MI_DEFAULT_MIN;
1744    }
1745 
1746    /* Calculate scale and offset for MI_PRIV_GET */
1747 
1748    /* Scale */
1749    denom = usr_imgmax - usr_imgmin;
1750    if (denom!=0.0)
1751       usr_scale=(usr_vmax - usr_vmin) / denom;
1752    else
1753       usr_scale=0.0;
1754    denom = var_vmax - var_vmin;
1755    if (denom!=0.0)
1756       icvp->scale = usr_scale * (var_imgmax - var_imgmin) / denom;
1757    else
1758       icvp->scale = 0.0;
1759 
1760    /* Offset */
1761    icvp->offset = usr_vmin - icvp->scale * var_vmin
1762                 + usr_scale * (var_imgmin - usr_imgmin);
1763 
1764    /* If we want a MI_PRIV_PUT, invert */
1765    if (operation==MI_PRIV_PUT) {
1766       if (icvp->scale!=0.0) {
1767          icvp->offset = (-icvp->offset) / icvp->scale;
1768          icvp->scale  = 1.0/icvp->scale;
1769       }
1770       else {
1771          icvp->offset = var_vmin;
1772          icvp->scale  = 0.0;
1773       }
1774    }
1775 
1776    /* Do fill value checking if scale is zero */
1777    if (icvp->scale == 0.0) {
1778 
1779       /* Check for floating point on both sides of conversion. We should
1780          not be doing scaling in this case, but we will check to be safe. */
1781       if (icvp->derv_var_float && icvp->derv_usr_float) {
1782          icvp->do_scale = FALSE;
1783          icvp->do_fillvalue = FALSE;
1784       }
1785 
1786       else {      /* Not pure floating point */
1787 
1788          icvp->do_fillvalue = TRUE;
1789 
1790          /* For output, set the range properly depending on whether the user
1791             type is floating point or not */
1792          if (operation == MI_PRIV_PUT) {
1793             if (icvp->derv_usr_float) {
1794                icvp->fill_valid_min = var_imgmin_true;
1795                icvp->fill_valid_max = var_imgmax_true;
1796             }
1797             else if (usr_scale != 0.0) {
1798                icvp->fill_valid_min =
1799                   usr_vmin + (var_imgmin_true - usr_imgmin) / usr_scale;
1800                icvp->fill_valid_max =
1801                   usr_vmin + (var_imgmax_true - usr_imgmin) / usr_scale;
1802             }
1803             else {
1804                icvp->fill_valid_min = usr_vmin;
1805                icvp->fill_valid_max = usr_vmax;
1806             }
1807          }        /* If output operation */
1808 
1809       }        /* If not pure floating point */
1810 
1811    }       /* If scale == 0.0 */
1812 
1813    MI_RETURN(MI_NOERROR);
1814 }
1815 
1816 
1817 /* ----------------------------- MNI Header -----------------------------------
1818 @NAME       : MI_icv_chkid
1819 @INPUT      : icvid  - icv id
1820 @OUTPUT     : (none)
1821 @RETURNS    : Pointer to icv structure if it exists, otherwise NULL.
1822 @DESCRIPTION: Checks that an icv id is valid and returns a pointer to the
1823               structure.
1824 @METHOD     :
1825 @GLOBALS    :
1826 @CALLS      : NetCDF routines
1827 @CREATED    : August 7, 1992 (Peter Neelin)
1828 @MODIFIED   :
1829 ---------------------------------------------------------------------------- */
MI_icv_chkid(int icvid)1830 SEMIPRIVATE mi_icv_type *MI_icv_chkid(int icvid)
1831 {
1832    MI_SAVE_ROUTINE_NAME("MI_icv_chkid");
1833 
1834    /* Check icv id */
1835    if ((icvid<0) || (icvid>=minc_icv_list_nalloc) ||
1836        (minc_icv_list[icvid]==NULL)) {
1837        MI_LOG_ERROR(MI_MSG_BADICV);
1838        MI_RETURN((void *) NULL);
1839    }
1840 
1841    MI_RETURN(minc_icv_list[icvid]);
1842 }
1843