/* ----------------------------- MNI Header ----------------------------------- @NAME : image_conversion.c @DESCRIPTION: File of functions to manipulate image conversion variables (icv). These variables allow conversion of netcdf variables (the MINC image variable, in particular) to a form more convenient for a program. @METHOD : Routines included in this file : public : miicv_create miicv_free miicv_setdbl miicv_setint miicv_setlong miicv_setstr miicv_inqdbl miicv_inqint miicv_inqlong miicv_inqstr miicv_ndattach miicv_detach miicv_get miicv_put semiprivate : MI_icv_chkid private : MI_icv_get_type MI_icv_get_vrange MI_get_default_range MI_icv_get_norm MI_icv_access MI_icv_zero_buffer MI_icv_coords_tovar MI_icv_calc_scale @CREATED : July 27, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: image_conversion.c,v $ * Revision 6.13 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.12 2004/10/15 13:45:28 bert * Minor changes for Windows compatibility * * Revision 6.11 2004/04/27 15:40:22 bert * Revised logging/error handling * * Revision 6.10 2003/09/18 16:17:00 bert * Correctly cast double to nc_type * * Revision 6.9 2001/11/28 15:38:07 neelin * Removed limit on number of icvs that can exist at one time. * * Revision 6.8 2001/11/13 21:00:24 neelin * Modified icv scaling calculations for no normalization. When the icv * type is double, normalization is always done, regardless of the * normalization setting. When the external type is floating point, * normalization to the slice real range is done (essentially a valid * range scaling, but where the valid range for a float is the slice real * range). * * Revision 6.7 2001/11/13 14:15:17 neelin * Added functions miget_image_range and mivar_exists * * Revision 6.6 2001/08/20 13:16:53 neelin * Removed extraneous variables from MI_icv_get_vrange. * * Revision 6.5 2001/08/16 19:24:11 neelin * Fixes to the code handling valid_range values. * * Revision 6.4 2001/08/16 16:41:31 neelin * Added library functions to handle reading of datatype, sign and valid range, * plus writing of valid range and setting of default ranges. These functions * properly handle differences between valid_range type and image type. Such * difference can cause valid data to appear as invalid when double to float * conversion causes rounding in the wrong direction (out of range). * Modified voxel_loop, volume_io and programs to use these functions. * * Revision 6.3 2001/08/16 13:32:18 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.2 2001/04/17 18:40:12 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:07 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.3 1997/04/21 17:32:04 neelin * Fixed calculation of scale for icv so that values are not re-scaled * from real values to file floating-point values. * * Revision 3.2 1997/04/10 19:22:18 neelin * Removed redefinition of NULL and added pointer casts in appropriate places. * * Revision 3.1 1997/04/10 18:14:50 neelin * Fixed handling of invalid data when icv scale is zero. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.3 1995/02/08 19:14:44 neelin * More changes for irix 5 lint. * * Revision 2.2 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.1 1994/12/09 09:12:30 neelin * Added test in miicv_detach to make sure that icv is attached before * detaching it. * * Revision 2.0 94/09/28 10:37:55 neelin * Release of minc version 0.2 * * Revision 1.18 94/09/28 10:37:06 neelin * Pre-release * * Revision 1.17 93/08/11 12:59:31 neelin * We need only increment the chunk pointer (see previous fix) if we are * not doing dimension conversion (dimension conversion handles the * offsets itself). * * Revision 1.16 93/08/11 11:49:36 neelin * Added RCS logging in source. * Fixed bug in MI_icv_access so that pointer to values buffer is incremented * as we loop through the chunks. This affected calls to miicv_get/put that * had MIimagemax/min varying over the values read in one call (ie. reading * or writing a volume with MIimagemax/min varying over slices will give * incorrect results if the volume is read with one call). * January 22, 1993 (P.N.) - Modified handling of icv properties with miicv_set. Removed routine miicv_set. Use routines miicv_setdbl, miicv_setint, miicv_setlong, miicv_setstr instead (this gives type checking at compile time). @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef lint static char rcsid[] = "$Header: /software/source/minc/libsrc/image_conversion.c,v 6.13 2004/12/14 23:53:46 bert Exp $ MINC (MNI)"; #endif #include "minc_private.h" #include "type_limits.h" /* Private functions */ PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid); PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid); PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign); PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid); PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[], long count[], void *values); PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values); PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp, long icv_start[], long icv_count[], long var_start[], long var_count[]); PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[]); /* Array of pointers to image conversion structures */ static int minc_icv_list_nalloc = 0; static mi_icv_type **minc_icv_list = NULL; /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_create @INPUT : (none) @OUTPUT : (none) @RETURNS : icv id or MI_ERROR when an error occurs @DESCRIPTION: Creates an image conversion variable (icv) and returns a handle to it. @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_create() { int new_icv; /* Id of newly created icv */ mi_icv_type *icvp; /* Pointer to new icv structure */ int idim; int new_nalloc; MI_SAVE_ROUTINE_NAME("miicv_create"); /* Look for free slot */ for (new_icv=0; new_icv=minc_icv_list_nalloc) { /* How much space will be needed? */ new_nalloc = minc_icv_list_nalloc + MI_MAX_NUM_ICV; /* Check for first allocation */ if (minc_icv_list_nalloc == 0) { minc_icv_list = MALLOC(new_nalloc, mi_icv_type *); } else { minc_icv_list = REALLOC(minc_icv_list, new_nalloc, mi_icv_type *); } /* Check that the allocation was successful */ if (minc_icv_list == NULL) { MI_LOG_SYS_ERROR1("miicv_create"); MI_RETURN(MI_ERROR); } /* Put in NULL pointers */ for (new_icv=minc_icv_list_nalloc; new_icvdo_scale = FALSE; icvp->do_dimconvert = FALSE; icvp->do_fillvalue = FALSE; icvp->fill_valid_min = -DBL_MAX; icvp->fill_valid_max = DBL_MAX; /* User defaults */ icvp->user_type = NC_SHORT; icvp->user_typelen = nctypelen(icvp->user_type); icvp->user_sign = MI_PRIV_SIGNED; icvp->user_do_range = TRUE; icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); icvp->user_do_norm = FALSE; icvp->user_user_norm = FALSE; icvp->user_maxvar = strdup(MIimagemax); icvp->user_minvar = strdup(MIimagemin); icvp->user_imgmax = MI_DEFAULT_MAX; icvp->user_imgmin = MI_DEFAULT_MIN; icvp->user_do_dimconv = FALSE; icvp->user_do_scalar = TRUE; icvp->user_xdim_dir = MI_ICV_POSITIVE; icvp->user_ydim_dir = MI_ICV_POSITIVE; icvp->user_zdim_dir = MI_ICV_POSITIVE; icvp->user_num_imgdims = 2; icvp->user_keep_aspect = TRUE; icvp->user_do_fillvalue = FALSE; icvp->user_fillvalue = -DBL_MAX; for (idim=0; idimuser_dim_size[idim]=MI_ICV_ANYSIZE; } /* Variable values */ icvp->cdfid = MI_ERROR; /* Set so that we can recognise an */ icvp->varid = MI_ERROR; /* unattached icv */ /* Values that can be read by user */ icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; for (idim=0; idimderv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } MI_RETURN(new_icv); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_free @INPUT : icvid @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Frees the image conversion variable (icv) @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_free(int icvid) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_free"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Detach the icv if it is attached */ if (icvp->cdfid != MI_ERROR) { if (miicv_detach(icvid) < 0) { MI_RETURN(MI_ERROR); } } /* Free anything allocated at creation time */ FREE(icvp->user_maxvar); FREE(icvp->user_minvar); /* Free the structure */ FREE(icvp); minc_icv_list[icvid]=NULL; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setdbl @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given double value Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setdbl(int icvid, int icv_property, double value) { int ival, idim; mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_setdbl"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is not attached to a file */ if (icvp->cdfid != MI_ERROR) { milog_message(MI_MSG_ICVATTACHED); MI_RETURN(MI_ERROR); } /* Set the property */ switch (icv_property) { case MI_ICV_TYPE: icvp->user_type = (nc_type) (int) value; icvp->user_typelen= nctypelen(icvp->user_type); icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); break; case MI_ICV_DO_RANGE: icvp->user_do_range = value; break; case MI_ICV_VALID_MAX: icvp->user_vmax = value; break; case MI_ICV_VALID_MIN: icvp->user_vmin = value; break; case MI_ICV_DO_NORM: icvp->user_do_norm = value; break; case MI_ICV_USER_NORM: icvp->user_user_norm = value; break; case MI_ICV_IMAGE_MAX: icvp->user_imgmax = value; break; case MI_ICV_IMAGE_MIN: icvp->user_imgmin = value; break; case MI_ICV_DO_FILLVALUE: icvp->user_do_fillvalue = value; break; case MI_ICV_FILLVALUE: icvp->user_fillvalue = value; break; case MI_ICV_DO_DIM_CONV: icvp->user_do_dimconv = value; break; case MI_ICV_DO_SCALAR: icvp->user_do_scalar = value; break; case MI_ICV_XDIM_DIR: ival = value; icvp->user_xdim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_YDIM_DIR: ival = value; icvp->user_ydim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_ZDIM_DIR: ival = value; icvp->user_zdim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_NUM_IMGDIMS: ival = value; if ((ival<0) || (ival>MI_MAX_IMGDIMS)) { milog_message(MI_MSG_BADPROP, _("MI_ICV_NUM_IMGDIMS out of range")); MI_RETURN(MI_ERROR); } icvp->user_num_imgdims = ival; break; case MI_ICV_ADIM_SIZE: icvp->user_dim_size[0] = value; break; case MI_ICV_BDIM_SIZE: icvp->user_dim_size[1] = value; break; case MI_ICV_KEEP_ASPECT: icvp->user_keep_aspect = value; break; case MI_ICV_SIGN: case MI_ICV_MAXVAR: case MI_ICV_MINVAR: milog_message(MI_MSG_BADPROP, _("Can't store a number in a string value")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_dim_size[idim] = value; } else { milog_message(MI_MSG_BADPROP, "Unknown code"); MI_RETURN(MI_ERROR); } break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setint @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given integer value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : January 22, 1993 (P.N.) - modified handling of icv properties ---------------------------------------------------------------------------- */ MNCAPI int miicv_setint(int icvid, int icv_property, int value) { MI_SAVE_ROUTINE_NAME("miicv_setint"); if (miicv_setdbl(icvid, icv_property, (double) value) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setlong @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given long integer value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setlong(int icvid, int icv_property, long value) { MI_SAVE_ROUTINE_NAME("miicv_setlong"); if (miicv_setdbl(icvid, icv_property, (double) value) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setstr @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given string value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setstr(int icvid, int icv_property, char *value) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_setstr"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is not attached to a file */ if (icvp->cdfid != MI_ERROR) { milog_message(MI_MSG_ICVATTACHED); MI_RETURN(MI_ERROR); } /* Set the property */ switch (icv_property) { case MI_ICV_SIGN: icvp->user_sign = MI_get_sign_from_string(icvp->user_type, value); icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); break; case MI_ICV_MAXVAR: if (value!=NULL) { FREE(icvp->user_maxvar); icvp->user_maxvar = strdup(value); } break; case MI_ICV_MINVAR: if (value!=NULL) { FREE(icvp->user_minvar); icvp->user_minvar = strdup(value); } break; case MI_ICV_TYPE: case MI_ICV_DO_RANGE: case MI_ICV_VALID_MAX: case MI_ICV_VALID_MIN: case MI_ICV_DO_NORM: case MI_ICV_USER_NORM: case MI_ICV_IMAGE_MAX: case MI_ICV_IMAGE_MIN: case MI_ICV_DO_DIM_CONV: case MI_ICV_DO_SCALAR: case MI_ICV_XDIM_DIR: case MI_ICV_YDIM_DIR: case MI_ICV_ZDIM_DIR: case MI_ICV_NUM_IMGDIMS: case MI_ICV_ADIM_SIZE: case MI_ICV_BDIM_SIZE: case MI_ICV_KEEP_ASPECT: milog_message(MI_MSG_BADPROP, "Can't store a string in a numeric property"); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_type; break; case MI_ICV_DO_RANGE: *value = icvp->user_do_range; break; case MI_ICV_VALID_MAX: *value = icvp->user_vmax; break; case MI_ICV_VALID_MIN: *value = icvp->user_vmin; break; case MI_ICV_DO_NORM: *value = icvp->user_do_norm; break; case MI_ICV_USER_NORM: *value = icvp->user_user_norm; break; case MI_ICV_IMAGE_MAX: *value = icvp->user_imgmax; break; case MI_ICV_IMAGE_MIN: *value = icvp->user_imgmin; break; case MI_ICV_NORM_MAX: *value = icvp->derv_imgmax; break; case MI_ICV_NORM_MIN: *value = icvp->derv_imgmin; break; case MI_ICV_DO_FILLVALUE: *value = icvp->user_do_fillvalue; break; case MI_ICV_FILLVALUE: *value = icvp->user_fillvalue; break; case MI_ICV_DO_DIM_CONV: *value = icvp->user_do_dimconv; break; case MI_ICV_DO_SCALAR: *value = icvp->user_do_scalar; break; case MI_ICV_XDIM_DIR: *value = icvp->user_xdim_dir; break; case MI_ICV_YDIM_DIR: *value = icvp->user_ydim_dir; break; case MI_ICV_ZDIM_DIR: *value = icvp->user_zdim_dir; break; case MI_ICV_NUM_IMGDIMS: *value = icvp->user_num_imgdims; break; case MI_ICV_NUM_DIMS: *value = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) (*value)--; break; case MI_ICV_CDFID: *value = icvp->cdfid; break; case MI_ICV_VARID: *value = icvp->varid; break; case MI_ICV_ADIM_SIZE: *value = icvp->user_dim_size[0]; break; case MI_ICV_BDIM_SIZE: *value = icvp->user_dim_size[1]; break; case MI_ICV_ADIM_STEP: *value = icvp->derv_dim_step[0]; break; case MI_ICV_BDIM_STEP: *value = icvp->derv_dim_step[1]; break; case MI_ICV_ADIM_START: *value = icvp->derv_dim_start[0]; break; case MI_ICV_BDIM_START: *value = icvp->derv_dim_start[1]; break; case MI_ICV_KEEP_ASPECT: *value = icvp->user_keep_aspect; break; case MI_ICV_SIGN: case MI_ICV_MAXVAR: case MI_ICV_MINVAR: milog_message(MI_MSG_BADPROP, _("Tried to get icv string property as a number")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_dim_size[idim]; } else if ((icv_property>=MI_ICV_DIM_STEP) && (icv_propertyderv_dim_step[idim]; } else if ((icv_property>=MI_ICV_DIM_START) && (icv_propertyderv_dim_start[idim]; } else { milog_message(MI_MSG_BADPROP, _("Tried to get unknown icv property")); MI_RETURN(MI_ERROR); } break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqint @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqint(int icvid, int icv_property, int *value) { double dvalue; MI_SAVE_ROUTINE_NAME("miicv_inqint"); if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) { MI_RETURN(MI_ERROR); } *value = dvalue; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqlong @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqlong(int icvid, int icv_property, long *value) { double dvalue; MI_SAVE_ROUTINE_NAME("miicv_inqlong"); if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) { MI_RETURN(MI_ERROR); } *value = dvalue; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqstr @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned. Caller must allocate enough space for return string. @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqstr(int icvid, int icv_property, char *value) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_inqstr"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Set the property */ switch (icv_property) { case MI_ICV_SIGN: if (icvp->user_sign==MI_PRIV_SIGNED) (void) strcpy(value, MI_SIGNED); else if (icvp->user_sign==MI_PRIV_UNSIGNED) (void) strcpy(value, MI_UNSIGNED); else (void) strcpy(value, MI_EMPTY_STRING); break; case MI_ICV_MAXVAR: (void) strcpy(value, icvp->user_maxvar); break; case MI_ICV_MINVAR: (void) strcpy(value, icvp->user_minvar); break; case MI_ICV_TYPE: case MI_ICV_DO_RANGE: case MI_ICV_VALID_MAX: case MI_ICV_VALID_MIN: case MI_ICV_DO_NORM: case MI_ICV_USER_NORM: case MI_ICV_IMAGE_MAX: case MI_ICV_IMAGE_MIN: case MI_ICV_NORM_MAX: case MI_ICV_NORM_MIN: case MI_ICV_DO_DIM_CONV: case MI_ICV_DO_SCALAR: case MI_ICV_XDIM_DIR: case MI_ICV_YDIM_DIR: case MI_ICV_ZDIM_DIR: case MI_ICV_NUM_IMGDIMS: case MI_ICV_ADIM_SIZE: case MI_ICV_BDIM_SIZE: case MI_ICV_ADIM_STEP: case MI_ICV_BDIM_STEP: case MI_ICV_ADIM_START: case MI_ICV_BDIM_START: case MI_ICV_KEEP_ASPECT: case MI_ICV_NUM_DIMS: case MI_ICV_CDFID: case MI_ICV_VARID: milog_message(MI_MSG_BADPROP, _("Tried to get icv numeric property as a string")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if (((icv_property>=MI_ICV_DIM_SIZE) && (icv_property=MI_ICV_DIM_STEP) && (icv_property=MI_ICV_DIM_START) && (icv_propertycdfid != MI_ERROR) { if (miicv_detach(icvid) < 0) { MI_RETURN(MI_ERROR); } } /* Inquire about the variable's type, sign and number of dimensions */ if (MI_icv_get_type(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } /* If not doing range calculations, just set derv_firstdim for MI_icv_access, otherwise, call routines to calculate range and normalization */ if (!icvp->user_do_range) { icvp->derv_firstdim = -1; } else { /* Get valid range */ if (MI_icv_get_vrange(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } /* Get normalization info */ if (MI_icv_get_norm(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } } /* Set other fields to defaults */ icvp->var_is_vector = FALSE; icvp->var_vector_size = 1; icvp->derv_do_zero = FALSE; icvp->derv_do_bufsize_step = FALSE; icvp->derv_var_pix_off = NULL; icvp->derv_usr_pix_off = NULL; for (idim=0; idimuser_num_imgdims; idim++) { icvp->derv_dim_flip[idim] = FALSE; icvp->derv_dim_grow[idim] = TRUE; icvp->derv_dim_scale[idim] = 1; icvp->derv_dim_off[idim] = 0; icvp->derv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } /* Set the do_scale and do_dimconvert fields of icv structure We have to scale only if do_range is TRUE. If ranges don't match, or we have to do user normalization, or if we are normalizing and MIimagemax or MIimagemin vary over the variable. We don't have to scale if input and output are both floating point. */ icvp->do_scale = (icvp->user_do_range && ((icvp->user_vmax!=icvp->var_vmax) || (icvp->user_vmin!=icvp->var_vmin) || (icvp->user_do_norm && icvp->user_user_norm) || (icvp->user_do_norm && (icvp->derv_firstdim>=0))) ); if ((icvp->derv_usr_float && icvp->derv_var_float)) icvp->do_scale = FALSE; icvp->do_dimconvert = FALSE; /* Set the cdfid and varid fields */ icvp->cdfid = cdfid; icvp->varid = varid; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_type @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the type and sign of a variable for miicv_attach. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid) { int oldncopts; /* For saving value of ncopts */ char stringa[MI_MAX_ATTSTR_LEN]; char *string=stringa; /* String for sign info */ MI_SAVE_ROUTINE_NAME("MI_icv_get_type"); /* Inquire about the variable */ if (ncvarinq(cdfid, varid, NULL, &(icvp->var_type), &(icvp->var_ndims), icvp->var_dim, NULL) < 0) { MI_RETURN(MI_ERROR); } /* Check that the variable type is numeric */ if (icvp->var_type==NC_CHAR) { milog_message(MI_MSG_VARNOTNUM); MI_RETURN(MI_ERROR); } /* Try to find out the sign of the variable using MIsigntype. */ oldncopts = ncopts; ncopts = 0; string=miattgetstr(cdfid, varid, MIsigntype, MI_MAX_ATTSTR_LEN, string); ncopts = oldncopts; icvp->var_sign = MI_get_sign_from_string(icvp->var_type, string); /* Get type lengths */ icvp->var_typelen = nctypelen(icvp->var_type); icvp->user_typelen = nctypelen(icvp->user_type); MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_vrange @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the valid range of a variable @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid) { double vrange[2]; /* Valid range buffer */ MI_SAVE_ROUTINE_NAME("MI_icv_get_vrange"); if (miget_valid_range(cdfid, varid, vrange) == MI_ERROR) { MI_RETURN(MI_ERROR); } icvp->var_vmin = vrange[0]; icvp->var_vmax = vrange[1]; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_get_default_range @INPUT : what - MIvalid_min means get default min, MIvalid_min means get default min datatype - type of variable sign - sign of variable @OUTPUT : (none) @RETURNS : default maximum or minimum for the datatype @DESCRIPTION: Return the defaults maximum or minimum for a given datatype and sign. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign) { double range[2]; MI_SAVE_ROUTINE_NAME("MI_get_default_range"); (void) miget_default_range(datatype, (sign == MI_PRIV_SIGNED), range); if (STRINGS_EQUAL(what, MIvalid_max)) { MI_RETURN(range[1]); } else if (STRINGS_EQUAL(what, MIvalid_min)) { MI_RETURN(range[0]); } else { ncopts = NC_VERBOSE | NC_FATAL; MI_LOG_PKG_ERROR2(-1,"MINC bug - this line should never be printed"); } MI_RETURN(MI_DEFAULT_MIN); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_norm @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the normalization info for a variable @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid) /* ARGSUSED */ { int oldncopts; /* For saving value of ncopts */ int vid[2]; /* Variable ids for max and min */ int ndims; /* Number of dimensions for image max and min */ int dim[MAX_VAR_DIMS]; /* Dimensions */ int imm; /* Counter for looping through max and min */ double image_range[2]; int idim, i; MI_SAVE_ROUTINE_NAME("MI_icv_get_norm"); /* Check for floating point or double precision values for user or in variable - set flag to not do normalization if needed */ icvp->derv_var_float = ((icvp->var_type == NC_DOUBLE) || (icvp->var_type == NC_FLOAT)); icvp->derv_usr_float = ((icvp->user_type == NC_DOUBLE) || (icvp->user_type == NC_FLOAT)); /* Initialize first dimension over which MIimagemax or MIimagemin vary - assume that they don't vary at all */ icvp->derv_firstdim=(-1); /* Look for image max, image min variables */ oldncopts=ncopts; ncopts=0; icvp->imgmaxid=ncvarid(cdfid, icvp->user_maxvar); icvp->imgminid=ncvarid(cdfid, icvp->user_minvar); ncopts = oldncopts; /* Check to see if normalization to variable max, min should be done */ if (!icvp->user_do_norm) { icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; } else { /* Get the image min and max, either from the user definition or from the file. */ if (icvp->user_user_norm) { icvp->derv_imgmax = icvp->user_imgmax; icvp->derv_imgmin = icvp->user_imgmin; } else { if (miget_image_range(cdfid, image_range) < 0) { MI_RETURN(MI_ERROR); } icvp->derv_imgmin = image_range[0]; icvp->derv_imgmax = image_range[1]; } /* Check each of the dimensions of image-min/max variables to see which is the fastest varying dimension of the image variable. */ vid[0]=icvp->imgminid; vid[1]=icvp->imgmaxid; if ((vid[0] != MI_ERROR) && (vid[1] != MI_ERROR)) { for (imm=0; imm < 2; imm++) { if (ncvarinq(cdfid, vid[imm], NULL, NULL, &ndims, dim, NULL) < 0) { MI_RETURN(MI_ERROR); } for (idim=0; idimvar_ndims; i++) { if (icvp->var_dim[i]==dim[idim]) icvp->derv_firstdim = MAX(icvp->derv_firstdim, i); } } } } } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_detach @INPUT : icvid - icv id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Detaches the cdf file and variable from the image conversion variable, allowing modifications to the icv. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_detach(int icvid) { mi_icv_type *icvp; int idim; MI_SAVE_ROUTINE_NAME("miicv_detach"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is in fact attached */ if (icvp->cdfid == MI_ERROR) MI_RETURN(MI_NOERROR); /* Free the pixel offset arrays */ if (icvp->derv_var_pix_off != NULL) FREE(icvp->derv_var_pix_off); if (icvp->derv_usr_pix_off != NULL) FREE(icvp->derv_usr_pix_off); /* Reset values that are read-only (and set when attached) */ icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; for (idim=0; idimderv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } /* Set cdfid field to MI_ERROR to indicate that icv is detached */ icvp->cdfid = MI_ERROR; icvp->varid = MI_ERROR; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_get @INPUT : icvid - icv id start - coordinates of start of hyperslab (see ncvarget) count - size of hyperslab (see ncvarget) @OUTPUT : values - array of values returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets a hyperslab of values from a netcdf variable through the image conversion variable (icvid) @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_get(int icvid, long start[], long count[], void *values) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_get"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Get the data */ if (MI_icv_access(MI_PRIV_GET, icvp, start, count, values) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_put @INPUT : icvid - icv id start - coordinates of start of hyperslab (see ncvarput) count - size of hyperslab (see ncvarput) values - array of values to store @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Stores a hyperslab of values in a netcdf variable through the image conversion variable (icvid) @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_put(int icvid, long start[], long count[], void *values) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_put"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); if (MI_icv_access(MI_PRIV_PUT, icvp, start, count, values) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_access @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvid - icv id start - coordinates of start of hyperslab (see ncvarput) count - size of hyperslab (see ncvarput) values - array of values to put @OUTPUT : values - array of values to get @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Does the work of getting or putting values from an icv. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 11, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[], long count[], void *values) { int *bufsize_step; /* Pointer to array giving increments for allocating variable buffer (NULL if we don't care) */ long chunk_count[MAX_VAR_DIMS]; /* Number of elements to get for chunk */ long chunk_start[MAX_VAR_DIMS]; /* Starting index for getting a chunk */ long chunk_size; /* Size of chunk in bytes */ void *chunk_values; /* Pointer to next chunk to get */ long var_start[MAX_VAR_DIMS]; /* Coordinates of first var element */ long var_count[MAX_VAR_DIMS]; /* Edge lengths in variable */ long var_end[MAX_VAR_DIMS]; /* Coordinates of last var element */ int firstdim; int idim, ndims; MI_SAVE_ROUTINE_NAME("MI_icv_access"); /* Check that icv is attached to a variable */ if (icvp->cdfid == MI_ERROR) { milog_message(MI_MSG_ICVNOTATTACHED); MI_RETURN(MI_ERROR); } /* Zero the user's buffer if needed */ if ((operation == MI_PRIV_GET) && (icvp->derv_do_zero)) if (MI_icv_zero_buffer(icvp, count, values) < 0) { MI_RETURN(MI_ERROR); } /* Translate icv coordinates to variable coordinates */ if (MI_icv_coords_tovar(icvp, start, count, var_start, var_count) < 0) { MI_RETURN(MI_ERROR); } /* Save icv coordinates for future reference (for dimension conversion routines) */ ndims = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) ndims--; for (idim=0; idim < ndims; idim++) { icvp->derv_icv_start[idim] = start[idim]; icvp->derv_icv_count[idim] = count[idim]; } /* Do we care about getting variable in convenient increments ? Only if we are getting data and the icv structure wants it */ if ((operation==MI_PRIV_GET) && (icvp->derv_do_bufsize_step)) bufsize_step = icvp->derv_bufsize_step; else bufsize_step = NULL; /* Set up variables for looping through variable. The biggest chunk that we can get in one call is determined by the subscripts of MIimagemax and MIimagemin. These must be constant over the chunk that we get if we are doing normalization. */ for (idim=0; idimvar_ndims; idim++) { chunk_start[idim] = var_start[idim]; var_end[idim]=var_start[idim]+var_count[idim]; } (void) miset_coords(icvp->var_ndims, 1L, chunk_count); /* Get size of chunk in user's buffer. Dimension conversion routines don't need the buffer pointer incremented - they do it themselves */ if (!icvp->do_dimconvert) chunk_size = nctypelen(icvp->user_type); else chunk_size = 0; for (idim=MAX(icvp->derv_firstdim+1,0); idim < icvp->var_ndims; idim++) { chunk_count[idim]=var_count[idim]; chunk_size *= chunk_count[idim]; } firstdim = MAX(icvp->derv_firstdim, 0); /* Loop through variable */ chunk_values = values; while (chunk_start[0] < var_end[0]) { /* Set the do_fillvalue flag if the user wants it and we are doing a get. We must do it inside the loop since the scale factor calculation can change it if the scale is zero. (Fillvalue checking is always done if the the scale is zero.) */ icvp->do_fillvalue = icvp->user_do_fillvalue && (operation == MI_PRIV_GET); icvp->fill_valid_min = icvp->var_vmin; icvp->fill_valid_max = icvp->var_vmax; /* Calculate scale factor */ if (icvp->do_scale) { if (MI_icv_calc_scale(operation, icvp, chunk_start) < 0) { MI_RETURN(MI_ERROR); } } /* Get the values */ if (MI_varaccess(operation, icvp->cdfid, icvp->varid, chunk_start, chunk_count, icvp->user_type, icvp->user_sign, chunk_values, bufsize_step, icvp) < 0) { MI_RETURN(MI_ERROR); } /* Increment the start counter */ chunk_start[firstdim] += chunk_count[firstdim]; for (idim=firstdim; (idim>0) && (chunk_start[idim]>=var_end[idim]); idim--) { chunk_start[idim]=var_start[idim]; chunk_start[idim-1]++; } /* Increment the pointer to values */ chunk_values = (void *) ((char *) chunk_values + (size_t) chunk_size); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_zero_buffer @INPUT : icvp - icv structure pointer count - count vector values - pointer to user's buffer @OUTPUT : @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Zeros the user's buffer, with a size given by the vector count. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 9, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values) { double zeroval, zerobuf; void *zerostart; int zerolen, idim, ndims; char *bufptr, *bufend, *zeroptr, *zeroend; long buflen; MI_SAVE_ROUTINE_NAME("MI_icv_zero_buffer"); /* Create a zero pixel and get its size */ zerostart = (void *) (&zerobuf); if (icvp->do_scale) zeroval = icvp->offset; else zeroval = 0.0; {MI_FROM_DOUBLE(zeroval, icvp->user_type, icvp->user_sign, zerostart)} zerolen = icvp->user_typelen; /* Get the buffer size */ ndims = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) ndims--; buflen = zerolen; for (idim=0; idim= zeroend) zeroptr = (char *) zerostart; *bufptr = *zeroptr; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_coords_tovar @INPUT : icvp - icv structure pointer icv_start - start vector for icv icv_count - count vector for icv @OUTPUT : var_start - start vector for variable var_count - count vector for variable @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Converts a start and count vector for referencing an icv to the corresponding vectors for referencing a NetCDF variable. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 1, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp, long icv_start[], long icv_count[], long var_start[], long var_count[]) { int i, j; int num_non_img_dims; long coord, last_coord, icv_dim_size; MI_SAVE_ROUTINE_NAME("MI_icv_coords_tovar"); /* Do we have to worry about dimension conversions? If not, then just copy the vectors and return. */ if (!icvp->do_dimconvert) { for (i=0; i < icvp->var_ndims; i++) { var_count[i] = icv_count[i]; var_start[i] = icv_start[i]; } MI_RETURN(MI_NOERROR); } /* Get the number of non image dimensions */ num_non_img_dims=icvp->var_ndims-icvp->user_num_imgdims; if (icvp->var_is_vector) num_non_img_dims--; /* Go through first, non-image dimensions */ for (i=0; i < num_non_img_dims; i++) { var_count[i] = icv_count[i]; var_start[i] = icv_start[i]; } /* Go through image dimensions */ for (i=num_non_img_dims, j=icvp->user_num_imgdims-1; i < num_non_img_dims+icvp->user_num_imgdims; i++, j--) { /* Check coordinates. */ icv_dim_size = (icvp->user_dim_size[j] > 0) ? icvp->user_dim_size[j] : icvp->var_dim_size[j]; last_coord = icv_start[i] + icv_count[i] - 1; if ((icv_start[i]<0) || (icv_start[i]>=icv_dim_size) || (last_coord<0) || (last_coord>=icv_dim_size) || (icv_count[i]<0)) { milog_message(MI_MSG_ICVCOORDS); MI_RETURN(MI_ERROR); } /* Remove offset */ coord = icv_start[i]-icvp->derv_dim_off[j]; /* Check for growing or shrinking */ if (icvp->derv_dim_grow[j]) { var_count[i] = (icv_count[i]+icvp->derv_dim_scale[j]-1) /icvp->derv_dim_scale[j]; coord /= icvp->derv_dim_scale[j]; } else { var_count[i] = icv_count[i]*icvp->derv_dim_scale[j]; coord *= icvp->derv_dim_scale[j]; } /* Check for flipping */ if (icvp->derv_dim_flip[j]) coord = icvp->var_dim_size[j] - coord - ((icv_count!=NULL) ? var_count[i] : 0L); var_start[i] = coord; /* Check for indices out of variable bounds (but in icv bounds) */ last_coord = var_start[i] + var_count[i]; if ((var_start[i]<0) || (last_coord>=icvp->var_dim_size[j])) { if (var_start[i]<0) var_start[i] = 0; if (last_coord>=icvp->var_dim_size[j]) last_coord = icvp->var_dim_size[j] - 1; var_count[i] = last_coord - var_start[i] + 1; } } /* Check for vector dimension */ if (icvp->var_is_vector) { if (icvp->user_do_scalar) { var_count[icvp->var_ndims-1] = icvp->var_vector_size; var_start[icvp->var_ndims-1] = 0; } else { var_count[icvp->var_ndims-1] = icv_count[icvp->var_ndims-1]; var_start[icvp->var_ndims-1] = icv_start[icvp->var_ndims-1]; } } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_calc_scale @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvp - icv structure pointer coords - coordinates of first value to get or put @OUTPUT : icvp - fields scale and offset set @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Calculates the scale and offset needed for getting or putting values, starting at index coords (assumes that scale is constant over that range). @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[]) { long mmcoords[MAX_VAR_DIMS]; /* Coordinates for max/min variable */ double usr_imgmax, usr_imgmin; double var_imgmax, var_imgmin; double var_imgmax_true, var_imgmin_true; double usr_vmax, usr_vmin; double var_vmax, var_vmin; double slice_imgmax, slice_imgmin; double usr_scale; double denom; MI_SAVE_ROUTINE_NAME("MI_icv_calc_scale"); /* Set variable valid range */ var_vmax = icvp->var_vmax; var_vmin = icvp->var_vmin; /* Set image max/min for user and variable values depending on whether normalization should be done or not. Whenever floating-point values are involved, some type of normalization is done. When the icv type is floating point, normalization is always done. When the file type is floating point and the icv type is integer, slices are normalized to the real range of the slice (or chunk being read). */ if (!icvp->derv_var_float && !icvp->derv_usr_float && !icvp->user_do_norm) { usr_imgmax = var_imgmax = MI_DEFAULT_MAX; usr_imgmin = var_imgmin = MI_DEFAULT_MIN; } else { /* Get the real range for the slice or chunk that is being examined */ slice_imgmax = MI_DEFAULT_MAX; slice_imgmin = MI_DEFAULT_MIN; if ((!icvp->derv_var_float || !icvp->user_do_norm) && (icvp->imgmaxid!=MI_ERROR) && (icvp->imgminid!=MI_ERROR)) { if (mitranslate_coords(icvp->cdfid, icvp->varid, coords, icvp->imgmaxid, mmcoords) == NULL) MI_RETURN(MI_ERROR); if (mivarget1(icvp->cdfid, icvp->imgmaxid, mmcoords, NC_DOUBLE, NULL, &slice_imgmax) < 0) { MI_RETURN(MI_ERROR); } if (mitranslate_coords(icvp->cdfid, icvp->varid, coords, icvp->imgminid, mmcoords) == NULL) { MI_RETURN(MI_ERROR); } if (mivarget1(icvp->cdfid, icvp->imgminid, mmcoords, NC_DOUBLE, NULL, &slice_imgmin) < 0) { MI_RETURN(MI_ERROR); } } /* Get the user real range */ if (icvp->user_do_norm) { usr_imgmax = icvp->derv_imgmax; usr_imgmin = icvp->derv_imgmin; } else { usr_imgmax = slice_imgmax; usr_imgmin = slice_imgmin; } /* Get the file real range */ if (icvp->derv_var_float) { var_imgmax = var_vmax; var_imgmin = var_vmin; } else { var_imgmax = slice_imgmax; var_imgmin = slice_imgmin; } } /* Prevent scaling between file floats and real value */ if (icvp->derv_var_float) { var_imgmax = var_vmax; var_imgmin = var_vmin; } /* Get user valid range */ if (icvp->derv_usr_float) { usr_vmax = usr_imgmax; usr_vmin = usr_imgmin; } else { usr_vmax = icvp->user_vmax; usr_vmin = icvp->user_vmin; } /* Save real var_imgmin/max for fillvalue checking later */ var_imgmax_true = var_imgmax; var_imgmin_true = var_imgmin; /* Even though we have already carefully set the vmax/min and imgmax/min values to handle the floating point case, we can still have problems with the scale calculations (rounding errors) if full range max/min are used (-FLT_MAX to FLT_MAX). To avoid this, we just force the values to 0 and 1 which will give the correct scale. That is why we save the true values above. */ if (icvp->derv_usr_float) { usr_imgmax = usr_vmax = MI_DEFAULT_MAX; usr_imgmin = usr_vmin = MI_DEFAULT_MIN; } if (icvp->derv_var_float) { var_imgmax = var_vmax = MI_DEFAULT_MAX; var_imgmin = var_vmin = MI_DEFAULT_MIN; } /* Calculate scale and offset for MI_PRIV_GET */ /* Scale */ denom = usr_imgmax - usr_imgmin; if (denom!=0.0) usr_scale=(usr_vmax - usr_vmin) / denom; else usr_scale=0.0; denom = var_vmax - var_vmin; if (denom!=0.0) icvp->scale = usr_scale * (var_imgmax - var_imgmin) / denom; else icvp->scale = 0.0; /* Offset */ icvp->offset = usr_vmin - icvp->scale * var_vmin + usr_scale * (var_imgmin - usr_imgmin); /* If we want a MI_PRIV_PUT, invert */ if (operation==MI_PRIV_PUT) { if (icvp->scale!=0.0) { icvp->offset = (-icvp->offset) / icvp->scale; icvp->scale = 1.0/icvp->scale; } else { icvp->offset = var_vmin; icvp->scale = 0.0; } } /* Do fill value checking if scale is zero */ if (icvp->scale == 0.0) { /* Check for floating point on both sides of conversion. We should not be doing scaling in this case, but we will check to be safe. */ if (icvp->derv_var_float && icvp->derv_usr_float) { icvp->do_scale = FALSE; icvp->do_fillvalue = FALSE; } else { /* Not pure floating point */ icvp->do_fillvalue = TRUE; /* For output, set the range properly depending on whether the user type is floating point or not */ if (operation == MI_PRIV_PUT) { if (icvp->derv_usr_float) { icvp->fill_valid_min = var_imgmin_true; icvp->fill_valid_max = var_imgmax_true; } else if (usr_scale != 0.0) { icvp->fill_valid_min = usr_vmin + (var_imgmin_true - usr_imgmin) / usr_scale; icvp->fill_valid_max = usr_vmin + (var_imgmax_true - usr_imgmin) / usr_scale; } else { icvp->fill_valid_min = usr_vmin; icvp->fill_valid_max = usr_vmax; } } /* If output operation */ } /* If not pure floating point */ } /* If scale == 0.0 */ MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_chkid @INPUT : icvid - icv id @OUTPUT : (none) @RETURNS : Pointer to icv structure if it exists, otherwise NULL. @DESCRIPTION: Checks that an icv id is valid and returns a pointer to the structure. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ SEMIPRIVATE mi_icv_type *MI_icv_chkid(int icvid) { MI_SAVE_ROUTINE_NAME("MI_icv_chkid"); /* Check icv id */ if ((icvid<0) || (icvid>=minc_icv_list_nalloc) || (minc_icv_list[icvid]==NULL)) { milog_message(MI_MSG_BADICV); MI_RETURN((void *) NULL); } MI_RETURN(minc_icv_list[icvid]); }