/*--------------------------------------------------------------------------*/ int ffiimgll(fitsfile *fptr, /* I - FITS file pointer */ int bitpix, /* I - bits per pixel */ int naxis, /* I - number of axes in the array */ LONGLONG *naxes, /* I - size of each axis */ int *status) /* IO - error status */ /* insert an IMAGE extension following the current HDU */ { int bytlen, nexthdu, maxhdu, ii, onaxis; long nblocks; LONGLONG npixels, newstart, datasize; char errmsg[FLEN_ERRMSG], card[FLEN_CARD], naxiskey[FLEN_KEYWORD]; if (*status > 0) return(*status); if (fptr->HDUposition != (fptr->Fptr)->curhdu) ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status); maxhdu = (fptr->Fptr)->maxhdu; if (*status != PREPEND_PRIMARY) { /* if the current header is completely empty ... */ if (( (fptr->Fptr)->headend == (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu]) /* or, if we are at the end of the file, ... */ || ( (((fptr->Fptr)->curhdu) == maxhdu ) && ((fptr->Fptr)->headstart[maxhdu + 1] >= (fptr->Fptr)->logfilesize ) ) ) { /* then simply append new image extension */ ffcrimll(fptr, bitpix, naxis, naxes, status); return(*status); } } if (bitpix == 8) bytlen = 1; else if (bitpix == 16) bytlen = 2; else if (bitpix == 32 || bitpix == -32) bytlen = 4; else if (bitpix == 64 || bitpix == -64) bytlen = 8; else { sprintf(errmsg, "Illegal value for BITPIX keyword: %d", bitpix); ffpmsg(errmsg); return(*status = BAD_BITPIX); /* illegal bitpix value */ } if (naxis < 0 || naxis > 999) { sprintf(errmsg, "Illegal value for NAXIS keyword: %d", naxis); ffpmsg(errmsg); return(*status = BAD_NAXIS); } for (ii = 0; ii < naxis; ii++) { if (naxes[ii] < 0) { sprintf(errmsg, "Illegal value for NAXIS%d keyword: %ld", ii + 1, (long) naxes[ii]); ffpmsg(errmsg); return(*status = BAD_NAXES); } } /* calculate number of pixels in the image */ if (naxis == 0) npixels = 0; else npixels = naxes[0]; for (ii = 1; ii < naxis; ii++) npixels = npixels * naxes[ii]; datasize = npixels * bytlen; /* size of image in bytes */ nblocks = (long) (((datasize + 2879) / 2880) + 1); /* +1 for the header */ if ((fptr->Fptr)->writemode == READWRITE) /* must have write access */ { /* close the CHDU */ ffrdef(fptr, status); /* scan header to redefine structure */ ffpdfl(fptr, status); /* insure correct data file values */ } else return(*status = READONLY_FILE); if (*status == PREPEND_PRIMARY) { /* inserting a new primary array; the current primary */ /* array must be transformed into an image extension. */ *status = 0; ffmahd(fptr, 1, NULL, status); /* move to the primary array */ ffgidm(fptr, &onaxis, status); if (onaxis > 0) ffkeyn("NAXIS",onaxis, naxiskey, status); else strcpy(naxiskey, "NAXIS"); ffgcrd(fptr, naxiskey, card, status); /* read last NAXIS keyword */ ffikyj(fptr, "PCOUNT", 0, "required keyword", status); /* add PCOUNT and */ ffikyj(fptr, "GCOUNT", 1, "required keyword", status); /* GCOUNT keywords */ if (*status > 0) return(*status); if (ffdkey(fptr, "EXTEND", status) ) /* delete the EXTEND keyword */ *status = 0; /* redefine internal structure for this HDU */ ffrdef(fptr, status); /* insert space for the primary array */ if (ffiblk(fptr, nblocks, -1, status) > 0) /* insert the blocks */ return(*status); nexthdu = 0; /* number of the new hdu */ newstart = 0; /* starting addr of HDU */ } else { nexthdu = ((fptr->Fptr)->curhdu) + 1; /* number of the next (new) hdu */ newstart = (fptr->Fptr)->headstart[nexthdu]; /* save starting addr of HDU */ (fptr->Fptr)->hdutype = IMAGE_HDU; /* so that correct fill value is used */ /* ffiblk also increments headstart for all following HDUs */ if (ffiblk(fptr, nblocks, 1, status) > 0) /* insert the blocks */ return(*status); } ((fptr->Fptr)->maxhdu)++; /* increment known number of HDUs in the file */ for (ii = (fptr->Fptr)->maxhdu; ii > (fptr->Fptr)->curhdu; ii--) (fptr->Fptr)->headstart[ii + 1] = (fptr->Fptr)->headstart[ii]; /* incre start addr */ if (nexthdu == 0) (fptr->Fptr)->headstart[1] = nblocks * 2880; /* start of the old Primary array */ (fptr->Fptr)->headstart[nexthdu] = newstart; /* set starting addr of HDU */ /* set default parameters for this new empty HDU */ (fptr->Fptr)->curhdu = nexthdu; /* we are now located at the next HDU */ fptr->HDUposition = nexthdu; /* we are now located at the next HDU */ (fptr->Fptr)->nextkey = (fptr->Fptr)->headstart[nexthdu]; (fptr->Fptr)->headend = (fptr->Fptr)->headstart[nexthdu]; (fptr->Fptr)->datastart = ((fptr->Fptr)->headstart[nexthdu]) + 2880; (fptr->Fptr)->hdutype = IMAGE_HDU; /* might need to be reset... */ /* write the required header keywords */ ffphprll(fptr, TRUE, bitpix, naxis, naxes, 0, 1, TRUE, status); /* redefine internal structure for this HDU */ ffrdef(fptr, status); return(*status); }
/*--------------------------------------------------------------------------*/ int ffiter(int n_cols, iteratorCol *cols, long offset, long n_per_loop, int (*work_fn)(long total_n, long offset, long first_n, long n_values, int n_cols, iteratorCol *cols, void *userPointer), void *userPointer, int *status) /* The iterator function. This function will pass the specified columns from a FITS table or pixels from a FITS image to the user-supplied function. Depending on the size of the table or image, only a subset of the rows or pixels may be passed to the function on each call, in which case the function will be called multiple times until all the rows or pixels have been processed. */ { typedef struct /* structure to store the column null value */ { int nullsize; /* length of the null value, in bytes */ union { /* default null value for the column */ char *stringnull; unsigned char charnull; int intnull; short shortnull; long longnull; unsigned int uintnull; unsigned short ushortnull; unsigned long ulongnull; float floatnull; double doublenull; } null; } colNulls; void *dataptr, *defaultnull; colNulls *col; int ii, jj, tstatus; int typecode, hdutype, jtype, type, anynul, nfiles, nbytes; long totaln, nleft, frow, felement, n_optimum, i_optimum, ntodo; long rept, width, tnull; double zeros = 0.; char message[FLEN_ERRMSG], keyname[FLEN_KEYWORD], nullstr[FLEN_VALUE]; char **stringptr, *nullptr, *cptr; if (*status > 0) return(*status); if (n_cols < 0 || n_cols > 999 ) { ffpmsg("Illegal number of columms (ffiter)"); return(*status = BAD_COL_NUM); /* negative number of columns */ } col = calloc(n_cols, sizeof(colNulls) ); /* memory for the null values */ /*------------------------------------------------------------*/ /* Make sure column numbers and datatypes are in legal range */ /* and column numbers and datatypes are legal. */ /* Also fill in other parameters in the column structure. */ /*------------------------------------------------------------*/ ffghdt(cols[0].fptr, &hdutype, status); /* type of first HDU */ for (jj = 0; jj < n_cols; jj++) { /* check that output datatype code value is legal */ type = cols[jj].datatype; if (type != 0 && type != TBYTE && type != TLOGICAL && type != TSTRING && type != TSHORT && type != TINT && type != TLONG && type != TFLOAT && type != TDOUBLE && type != TCOMPLEX && type != TULONG && type != TUSHORT && type != TDBLCOMPLEX) { sprintf(message, "Illegal datatype for column number %d: %d (ffiter)", jj + 1, cols[jj].datatype); ffpmsg(message); return(*status = BAD_DATATYPE); } /* initialize TLMINn, TLMAXn, column name, and display format */ cols[jj].tlmin = 0; cols[jj].tlmax = 0; cols[jj].tunit[0] = '\0'; cols[jj].tdisp[0] = '\0'; ffghdt(cols[jj].fptr, &jtype, status); /* get HDU type */ if (hdutype == IMAGE_HDU) { if (jtype != IMAGE_HDU) { sprintf(message, "File %d not positioned to an image extension (ffiter)", jj + 1); return(*status = NOT_IMAGE); } /* images are stored in column 2; ignore the input value */ cols[jj].colnum = 2; strcpy(cols[jj].colname, "IMAGE"); /* dummy name for images */ tstatus = 0; ffgkys(cols[jj].fptr, "BUNIT", cols[jj].tunit, 0, &tstatus); } else { if (jtype == IMAGE_HDU) { sprintf(message, "File %d not positioned to a table extension (ffiter)", jj + 1); return(*status = NOT_TABLE); } if (cols[jj].colnum < 1) { /* find the column number for the named column */ if (ffgcno(cols[jj].fptr, CASEINSEN, cols[jj].colname, &cols[jj].colnum, status) ) { sprintf(message, "Column '%s' not found for column number %d (ffiter)", cols[jj].colname, jj + 1); ffpmsg(message); return(*status); } } if (cols[jj].colnum < 1 || cols[jj].colnum > ((cols[jj].fptr)->Fptr)->tfield) { sprintf(message, "Column %d has illegal table position number: %d (ffiter)", jj + 1, cols[jj].colnum); ffpmsg(message); return(*status = BAD_COL_NUM); } /* look for column description keywords and update structure */ tstatus = 0; ffkeyn("TLMIN", cols[jj].colnum, keyname, &tstatus); ffgkyj(cols[jj].fptr, keyname, &cols[jj].tlmin, 0, &tstatus); tstatus = 0; ffkeyn("TLMAX", cols[jj].colnum, keyname, &tstatus); ffgkyj(cols[jj].fptr, keyname, &cols[jj].tlmax, 0, &tstatus); tstatus = 0; ffkeyn("TTYPE", cols[jj].colnum, keyname, &tstatus); ffgkys(cols[jj].fptr, keyname, cols[jj].colname, 0, &tstatus); if (tstatus) cols[jj].colname[0] = '\0'; tstatus = 0; ffkeyn("TUNIT", cols[jj].colnum, keyname, &tstatus); ffgkys(cols[jj].fptr, keyname, cols[jj].tunit, 0, &tstatus); tstatus = 0; ffkeyn("TDISP", cols[jj].colnum, keyname, &tstatus); ffgkys(cols[jj].fptr, keyname, cols[jj].tdisp, 0, &tstatus); } } /*-----------------------------------------------------------------*/ /* use the first file to set the total number of values to process */ /*-----------------------------------------------------------------*/ offset = maxvalue(offset, 0L); /* make sure offset is legal */ if (hdutype == IMAGE_HDU) /* get total number of pixels in the image */ { ffgtcl(cols[0].fptr, cols[0].colnum, NULL, &totaln, &width, status); frow = 1; felement = 1 + offset; } else /* get total number or rows in the table */ { ffgkyj(cols[0].fptr, "NAXIS2", &totaln, 0, status); frow = 1 + offset; felement = 1; } /* adjust total by the input starting offset value */ totaln -= offset; totaln = maxvalue(totaln, 0L); /* don't allow negative number */ /*------------------------------------------------------------------*/ /* Determine number of values to pass to work function on each loop */ /*------------------------------------------------------------------*/ if (n_per_loop == 0) { /* Determine optimum number of values for each iteration. */ /* Look at all the fitsfile pointers to determine the number */ /* of unique files. */ nfiles = 1; ffgrsz(cols[0].fptr, &n_optimum, status); for (jj = 1; jj < n_cols; jj++) { for (ii = 0; ii < jj; ii++) { if (cols[ii].fptr == cols[jj].fptr) break; } if (ii == jj) /* this is a new file */ { nfiles++; ffgrsz(cols[jj].fptr, &i_optimum, status); n_optimum = minvalue(n_optimum, i_optimum); } } n_optimum = n_optimum / nfiles; } else if (n_per_loop < 0) /* must pass all the values at one time */ { n_optimum = totaln; } else /* calling routine specified how many values to pass at a time */ { n_optimum = minvalue(n_per_loop, totaln); } /*--------------------------------------*/ /* allocate work arrays for each column */ /* and determine the null pixel value */ /*--------------------------------------*/ for (jj = 0; jj < n_cols; jj++) { /* get column datatype and vector length */ if (ffgtcl(cols[jj].fptr, cols[jj].colnum, &typecode, &rept, &width, status) > 0) goto cleanup; /* special case where sizeof(long) = 8: use TINT instead of TLONG */ if (typecode == TLONG && sizeof(long) == 8 && sizeof(int) == 4) typecode = TINT; /* Special case: interprete 'X' column as 'B' */ if (abs(typecode) == TBIT) { typecode = typecode / TBIT * TBYTE; rept = (rept + 7) / 8; } if (cols[jj].datatype == 0) /* output datatype not specified? */ { /* special case if sizeof(long) = 8: use TINT instead of TLONG */ if (typecode == TLONG && sizeof(long) == 8 && sizeof(int) == 4) cols[jj].datatype = TINT; else cols[jj].datatype = typecode; } /* calc total number of elements to do on each iteration */ if (hdutype == IMAGE_HDU || cols[jj].datatype == TSTRING) { ntodo = n_optimum; cols[jj].repeat = 1; /* get the BLANK keyword value, if it exists */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tstatus = 0; ffgkyj(cols[jj].fptr, "BLANK", &tnull, 0, &tstatus); if (tstatus) { tnull = 0L; /* no null values */ } } } else { ntodo = n_optimum * rept; /* vector columns */ cols[jj].repeat = rept; /* get the TNULL keyword value, if it exists */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tstatus = 0; if (hdutype == ASCII_TBL) /* TNULLn value is a string */ { ffkeyn("TNULL", cols[jj].colnum, keyname, &tstatus); ffgkys(cols[jj].fptr, keyname, nullstr, 0, &tstatus); if (tstatus) { tnull = 0L; /* keyword doesn't exist; no null values */ } else { cptr = nullstr; while (*cptr == ' ') /* skip over leading blanks */ cptr++; if (*cptr == '\0') /* TNULLn is all blanks? */ tnull = LONG_MIN; else { /* attempt to read TNULLn string as an integer */ ffc2ii(nullstr, &tnull, &tstatus); if (tstatus) tnull = LONG_MIN; /* choose smallest value */ } /* to represent nulls */ } } else /* Binary table; TNULLn value is an integer */ { ffkeyn("TNULL", cols[jj].colnum, keyname, &tstatus); ffgkyj(cols[jj].fptr, keyname, &tnull, 0, &tstatus); if (tstatus) { tnull = 0L; /* keyword doesn't exist; no null values */ } else if (tnull == 0) { /* worst possible case: a value of 0 is used to */ /* represent nulls in the FITS file. We have to */ /* use a non-zero null value here (zero is used to */ /* mean there are no null values in the array) so we */ /* will use the smallest possible integer instead. */ tnull = LONG_MIN; /* choose smallest possible value */ } } } } /* Note that the data array starts with 2nd element; */ /* 1st element of the array gives the null data value */ switch (cols[jj].datatype) { case TBYTE: cols[jj].array = calloc(ntodo + 1, sizeof(char)); col[jj].nullsize = sizeof(char); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tnull = minvalue(tnull, 255); tnull = maxvalue(tnull, 0); col[jj].null.charnull = (unsigned char) tnull; } else { col[jj].null.charnull = (unsigned char) 255; /* use 255 as null */ } break; case TSHORT: cols[jj].array = calloc(ntodo + 1, sizeof(short)); col[jj].nullsize = sizeof(short); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tnull = minvalue(tnull, SHRT_MAX); tnull = maxvalue(tnull, SHRT_MIN); col[jj].null.shortnull = (short) tnull; } else { col[jj].null.shortnull = SHRT_MIN; /* use minimum as null */ } break; case TUSHORT: cols[jj].array = calloc(ntodo + 1, sizeof(unsigned short)); col[jj].nullsize = sizeof(unsigned short); /* bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tnull = minvalue(tnull, USHRT_MAX); tnull = maxvalue(tnull, 0); /* don't allow negative value */ col[jj].null.ushortnull = (unsigned short) tnull; } else { col[jj].null.ushortnull = USHRT_MAX; /* use maximum null */ } break; case TINT: cols[jj].array = calloc(ntodo + 1, sizeof(int)); col[jj].nullsize = sizeof(int); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tnull = minvalue(tnull, INT_MAX); tnull = maxvalue(tnull, INT_MIN); col[jj].null.intnull = (int) tnull; } else { col[jj].null.intnull = INT_MIN; /* use minimum as null */ } break; case TUINT: cols[jj].array = calloc(ntodo + 1, sizeof(unsigned int)); col[jj].nullsize = sizeof(unsigned int); /* bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { tnull = minvalue(tnull, INT32_MAX); tnull = maxvalue(tnull, 0); col[jj].null.uintnull = (unsigned int) tnull; } else { col[jj].null.intnull = UINT_MAX; /* use maximum as null */ } break; case TLONG: cols[jj].array = calloc(ntodo + 1, sizeof(long)); col[jj].nullsize = sizeof(long); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { col[jj].null.longnull = tnull; } else { col[jj].null.longnull = LONG_MIN; /* use minimum as null */ } break; case TULONG: cols[jj].array = calloc(ntodo + 1, sizeof(unsigned long)); col[jj].nullsize = sizeof(unsigned long); /* bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { if (tnull < 0) /* can't use a negative null value */ col[jj].null.ulongnull = LONG_MAX; else col[jj].null.ulongnull = (unsigned long) tnull; } else { col[jj].null.ulongnull = LONG_MAX; /* use maximum as null */ } break; case TFLOAT: cols[jj].array = calloc(ntodo + 1, sizeof(float)); col[jj].nullsize = sizeof(float); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { col[jj].null.floatnull = (float) tnull; } else { col[jj].null.floatnull = FLOATNULLVALUE; /* special value */ } break; case TCOMPLEX: cols[jj].array = calloc((ntodo * 2) + 1, sizeof(float)); col[jj].nullsize = sizeof(float); /* number of bytes per value */ col[jj].null.floatnull = FLOATNULLVALUE; /* special value */ break; case TDOUBLE: cols[jj].array = calloc(ntodo + 1, sizeof(double)); col[jj].nullsize = sizeof(double); /* number of bytes per value */ if (typecode == TBYTE || typecode == TSHORT || typecode == TLONG) { col[jj].null.doublenull = (double) tnull; } else { col[jj].null.doublenull = DOUBLENULLVALUE; /* special value */ } break; case TDBLCOMPLEX: cols[jj].array = calloc((ntodo * 2) + 1, sizeof(double)); col[jj].nullsize = sizeof(double); /* number of bytes per value */ col[jj].null.doublenull = DOUBLENULLVALUE; /* special value */ break; case TSTRING: /* allocate array of pointers to all the strings */ if( hdutype==ASCII_TBL ) rept = width; stringptr = calloc((ntodo + 1) , sizeof(stringptr)); cols[jj].array = stringptr; col[jj].nullsize = rept + 1; /* number of bytes per value */ if (stringptr) { /* allocate string to store the null string value */ col[jj].null.stringnull = calloc(rept + 1, sizeof(char) ); col[jj].null.stringnull[1] = 1; /* to make sure string != 0 */ /* allocate big block for the array of table column strings */ stringptr[0] = calloc((ntodo + 1) * (rept + 1), sizeof(char) ); if (stringptr[0]) { for (ii = 1; ii <= ntodo; ii++) { /* pointer to each string */ stringptr[ii] = stringptr[ii - 1] + (rept + 1); } /* get the TNULL keyword value, if it exists */ tstatus = 0; ffkeyn("TNULL", cols[jj].colnum, keyname, &tstatus); ffgkys(cols[jj].fptr, keyname, nullstr, 0, &tstatus); if (!tstatus) strncat(col[jj].null.stringnull, nullstr, rept); } else { ffpmsg("ffiter failed to allocate memory arrays"); *status = MEMORY_ALLOCATION; /* memory allocation failed */ goto cleanup; } } break; case TLOGICAL: cols[jj].array = calloc(ntodo + 1, sizeof(char)); col[jj].nullsize = sizeof(char); /* number of bytes per value */ /* use value = 2 to flag null values in logical columns */ col[jj].null.charnull = 2; break; default: sprintf(message, "Column %d datatype currently not supported: %d: (ffiter)", jj + 1, cols[jj].datatype); ffpmsg(message); *status = BAD_DATATYPE; goto cleanup; } /* end of switch block */ /* check that all the arrays were allocated successfully */ if (!cols[jj].array) { ffpmsg("ffiter failed to allocate memory arrays"); *status = MEMORY_ALLOCATION; /* memory allocation failed */ goto cleanup; } } /*--------------------------------------------------*/ /* main loop while there are values left to process */ /*--------------------------------------------------*/ nleft = totaln; while (nleft) { ntodo = minvalue(nleft, n_optimum); /* no. of values for this loop */ /* read input columns from FITS file(s) */ for (jj = 0; jj < n_cols; jj++) { if (cols[jj].iotype != OutputCol) { if (cols[jj].datatype == TSTRING) { stringptr = cols[jj].array; dataptr = stringptr + 1; defaultnull = col[jj].null.stringnull; /* ptr to the null value */ } else { dataptr = (char *) cols[jj].array + col[jj].nullsize; defaultnull = &col[jj].null.charnull; /* ptr to the null value */ } if (ffgcv(cols[jj].fptr, cols[jj].datatype, cols[jj].colnum, frow, felement, cols[jj].repeat * ntodo, defaultnull, dataptr, &anynul, status) > 0) { break; } /* copy the appropriate null value into first array element */ if (anynul) /* are there any nulls in the data? */ { if (cols[jj].datatype == TSTRING) { stringptr = cols[jj].array; memcpy(*stringptr, col[jj].null.stringnull, col[jj].nullsize); } else { memcpy(cols[jj].array, defaultnull, col[jj].nullsize); } } else /* no null values so copy zero into first element */ { if (cols[jj].datatype == TSTRING) { stringptr = cols[jj].array; memset(*stringptr, 0, col[jj].nullsize); } else { memset(cols[jj].array, 0, col[jj].nullsize); } } } } if (*status > 0) break; /* looks like an error occurred; quit immediately */ /* call work function */ if (hdutype == IMAGE_HDU) *status = work_fn(totaln, offset, felement, ntodo, n_cols, cols, userPointer); else *status = work_fn(totaln, offset, frow, ntodo, n_cols, cols, userPointer); if (*status > 0 || *status < -1 ) break; /* looks like an error occurred; quit immediately */ /* write output columns before quiting if status = -1 */ tstatus = 0; for (jj = 0; jj < n_cols; jj++) { if (cols[jj].iotype != InputCol) { if (cols[jj].datatype == TSTRING) { stringptr = cols[jj].array; dataptr = stringptr + 1; nullptr = *stringptr; nbytes = 2; } else { dataptr = (char *) cols[jj].array + col[jj].nullsize; nullptr = (char *) cols[jj].array; nbytes = col[jj].nullsize; } if (memcmp(nullptr, &zeros, nbytes) ) { /* null value flag not zero; must check for and write nulls */ if (ffpcn(cols[jj].fptr, cols[jj].datatype, cols[jj].colnum, frow, felement, cols[jj].repeat * ntodo, dataptr, nullptr, &tstatus) > 0) break; } else { /* no null values; just write the array */ if (ffpcl(cols[jj].fptr, cols[jj].datatype, cols[jj].colnum, frow, felement, cols[jj].repeat * ntodo, dataptr, &tstatus) > 0) break; } } } if (*status == 0) *status = tstatus; /* propagate any error status from the writes */ if (*status) break; /* exit on any error */ nleft -= ntodo; if (hdutype == IMAGE_HDU) felement += ntodo; else frow += ntodo; } cleanup: /*----------------------------------*/ /* free work arrays for the columns */ /*----------------------------------*/ for (jj = 0; jj < n_cols; jj++) { if (cols[jj].datatype == TSTRING) { if (cols[jj].array) { stringptr = cols[jj].array; free(*stringptr); /* free the block of strings */ free(col[jj].null.stringnull); /* free the null string */ } } free(cols[jj].array); /* memory for the array of values from the col */ } free(col); /* the structure containing the null values */ return(*status); }
/*--------------------------------------------------------------------------*/ int ffhist(fitsfile **fptr, /* IO - pointer to table with X and Y cols; */ /* on output, points to histogram image */ char *outfile, /* I - name for the output histogram file */ int imagetype, /* I - datatype for image: TINT, TSHORT, etc */ int naxis, /* I - number of axes in the histogram image */ char colname[4][FLEN_VALUE], /* I - column names */ double *minin, /* I - minimum histogram value, for each axis */ double *maxin, /* I - maximum histogram value, for each axis */ double *binsizein, /* I - bin size along each axis */ char minname[4][FLEN_VALUE], /* I - optional keywords for min */ char maxname[4][FLEN_VALUE], /* I - optional keywords for max */ char binname[4][FLEN_VALUE], /* I - optional keywords for binsize */ double weightin, /* I - binning weighting factor */ char wtcol[FLEN_VALUE], /* I - optional keyword or col for weight*/ int recip, /* I - use reciprocal of the weight? */ char *selectrow, /* I - optional array (length = no. of */ /* rows in the table). If the element is true */ /* then the corresponding row of the table will*/ /* be included in the histogram, otherwise the */ /* row will be skipped. Ingnored if *selectrow*/ /* is equal to NULL. */ int *status) { int ii, datatype, repeat, imin, imax, ibin, bitpix, tstatus, use_datamax = 0; long haxes[4]; fitsfile *histptr; char errmsg[FLEN_ERRMSG], keyname[FLEN_KEYWORD], card[FLEN_CARD]; tcolumn *colptr; iteratorCol imagepars[1]; int n_cols = 1, nkeys; long offset = 0; long n_per_loop = -1; /* force whole array to be passed at one time */ histType histData; /* Structure holding histogram info for iterator */ float amin[4], amax[4], binsize[4], maxbin[4]; float datamin = FLOATNULLVALUE, datamax = FLOATNULLVALUE; char svalue[FLEN_VALUE]; double dvalue; char cpref[4][FLEN_VALUE]; char *cptr; if (*status > 0) return(*status); if (naxis > 4) { ffpmsg("histogram has more than 4 dimensions"); return(*status = BAD_DIMEN); } /* reset position to the correct HDU if necessary */ if ((*fptr)->HDUposition != ((*fptr)->Fptr)->curhdu) ffmahd(*fptr, ((*fptr)->HDUposition) + 1, NULL, status); histData.tblptr = *fptr; histData.himagetype = imagetype; histData.haxis = naxis; histData.rowselector = selectrow; if (imagetype == TBYTE) bitpix = BYTE_IMG; else if (imagetype == TSHORT) bitpix = SHORT_IMG; else if (imagetype == TINT) bitpix = LONG_IMG; else if (imagetype == TFLOAT) bitpix = FLOAT_IMG; else if (imagetype == TDOUBLE) bitpix = DOUBLE_IMG; else return(*status = BAD_DATATYPE); /* The CPREF keyword, if it exists, gives the preferred columns. */ /* Otherwise, assume "X", "Y", "Z", and "T" */ tstatus = 0; ffgky(*fptr, TSTRING, "CPREF", cpref[0], NULL, &tstatus); if (!tstatus) { /* Preferred column names are given; separate them */ cptr = cpref[0]; /* the first preferred axis... */ while (*cptr != ',' && *cptr != '\0') cptr++; if (*cptr != '\0') { *cptr = '\0'; cptr++; while (*cptr == ' ') cptr++; strcpy(cpref[1], cptr); cptr = cpref[1]; /* the second preferred axis... */ while (*cptr != ',' && *cptr != '\0') cptr++; if (*cptr != '\0') { *cptr = '\0'; cptr++; while (*cptr == ' ') cptr++; strcpy(cpref[2], cptr); cptr = cpref[2]; /* the third preferred axis... */ while (*cptr != ',' && *cptr != '\0') cptr++; if (*cptr != '\0') { *cptr = '\0'; cptr++; while (*cptr == ' ') cptr++; strcpy(cpref[3], cptr); } } } } for (ii = 0; ii < naxis; ii++) { /* get the min, max, and binsize values from keywords, if specified */ if (*minname[ii]) { if (ffgky(*fptr, TDOUBLE, minname[ii], &minin[ii], NULL, status) ) { ffpmsg("error reading histogramming minimum keyword"); ffpmsg(minname[ii]); return(*status); } } if (*maxname[ii]) { if (ffgky(*fptr, TDOUBLE, maxname[ii], &maxin[ii], NULL, status) ) { ffpmsg("error reading histogramming maximum keyword"); ffpmsg(maxname[ii]); return(*status); } } if (*binname[ii]) { if (ffgky(*fptr, TDOUBLE, binname[ii], &binsizein[ii], NULL, status) ) { ffpmsg("error reading histogramming binsize keyword"); ffpmsg(binname[ii]); return(*status); } } if (binsizein[ii] == 0.) { ffpmsg("error: histogram binsize = 0"); return(*status = ZERO_SCALE); } if (*colname[ii] == '\0') { strcpy(colname[ii], cpref[ii]); /* try using the preferred column */ if (*colname[ii] == '\0') { if (ii == 0) strcpy(colname[ii], "X"); else if (ii == 1) strcpy(colname[ii], "Y"); else if (ii == 2) strcpy(colname[ii], "Z"); else if (ii == 3) strcpy(colname[ii], "T"); } } /* get the column number in the table */ if (ffgcno(*fptr, CASEINSEN, colname[ii], histData.hcolnum+ii, status) > 0) { strcpy(errmsg, "column for histogram axis doesn't exist: "); strcat(errmsg, colname[ii]); ffpmsg(errmsg); return(*status); } colptr = ((*fptr)->Fptr)->tableptr; colptr += (histData.hcolnum[ii] - 1); repeat = (int) colptr->trepeat; /* vector repeat factor of the column */ if (repeat > 1) { strcpy(errmsg, "Can't bin a vector column: "); strcat(errmsg, colname[ii]); ffpmsg(errmsg); return(*status = BAD_DATATYPE); } /* get the datatype of the column */ fits_get_coltype(*fptr, histData.hcolnum[ii], &datatype, NULL, NULL, status); if (datatype < 0 || datatype == TSTRING) { strcpy(errmsg, "Inappropriate datatype; can't bin this column: "); strcat(errmsg, colname[ii]); ffpmsg(errmsg); return(*status = BAD_DATATYPE); } /* use TLMINn and TLMAXn keyword values if min and max were not given */ /* else use actual data min and max if TLMINn and TLMAXn don't exist */ if (minin[ii] == DOUBLENULLVALUE) { ffkeyn("TLMIN", histData.hcolnum[ii], keyname, status); if (ffgky(*fptr, TFLOAT, keyname, amin+ii, NULL, status) > 0) { /* use actual data minimum value for the histogram minimum */ *status = 0; if (fits_get_col_minmax(*fptr, histData.hcolnum[ii], amin+ii, &datamax, status) > 0) { strcpy(errmsg, "Error calculating datamin and datamax for column: "); strcat(errmsg, colname[ii]); ffpmsg(errmsg); return(*status); } } } else { amin[ii] = (float) minin[ii]; } if (maxin[ii] == DOUBLENULLVALUE) { ffkeyn("TLMAX", histData.hcolnum[ii], keyname, status); if (ffgky(*fptr, TFLOAT, keyname, &amax[ii], NULL, status) > 0) { *status = 0; if(datamax != FLOATNULLVALUE) /* already computed max value */ { amax[ii] = datamax; } else { /* use actual data maximum value for the histogram maximum */ if (fits_get_col_minmax(*fptr, histData.hcolnum[ii], &datamin, &amax[ii], status) > 0) { strcpy(errmsg, "Error calculating datamin and datamax for column: "); strcat(errmsg, colname[ii]); ffpmsg(errmsg); return(*status); } } } use_datamax = 1; /* flag that the max was determined by the data values */ /* and not specifically set by the calling program */ } else { amax[ii] = (float) maxin[ii]; } /* use TDBINn keyword or else 1 if bin size is not given */ if (binsizein[ii] == DOUBLENULLVALUE) { tstatus = 0; ffkeyn("TDBIN", histData.hcolnum[ii], keyname, &tstatus); if (ffgky(*fptr, TDOUBLE, keyname, binsizein + ii, NULL, &tstatus) > 0) { /* make at least 10 bins */ binsizein[ii] = (amax[ii] - amin[ii]) / 10. ; if (binsizein[ii] > 1.) binsizein[ii] = 1.; /* use default bin size */ } } if ( (amin[ii] > amax[ii] && binsizein[ii] > 0. ) || (amin[ii] < amax[ii] && binsizein[ii] < 0. ) ) binsize[ii] = (float) -binsizein[ii]; /* reverse the sign of binsize */ else binsize[ii] = (float) binsizein[ii]; /* binsize has the correct sign */ ibin = (int) binsize[ii]; imin = (int) amin[ii]; imax = (int) amax[ii]; /* Determine the range and number of bins in the histogram. This */ /* depends on whether the input columns are integer or floats, so */ /* treat each case separately. */ if (datatype <= TLONG && (float) imin == amin[ii] && (float) imax == amax[ii] && (float) ibin == binsize[ii] ) { /* This is an integer column and integer limits were entered. */ /* Shift the lower and upper histogramming limits by 0.5, so that */ /* the values fall in the center of the bin, not on the edge. */ haxes[ii] = (imax - imin) / ibin + 1; /* last bin may only */ /* be partially full */ maxbin[ii] = (float) (haxes[ii] + 1.); /* add 1. instead of .5 to avoid roundoff */ if (amin[ii] < amax[ii]) { amin[ii] = (float) (amin[ii] - 0.5); amax[ii] = (float) (amax[ii] + 0.5); } else { amin[ii] = (float) (amin[ii] + 0.5); amax[ii] = (float) (amax[ii] - 0.5); } } else if (use_datamax) { /* Either the column datatype and/or the limits are floating point, */ /* and the histogram limits are being defined by the min and max */ /* values of the array. Add 1 to the number of histogram bins to */ /* make sure that pixels that are equal to the maximum or are */ /* in the last partial bin are included. */ maxbin[ii] = (amax[ii] - amin[ii]) / binsize[ii]; haxes[ii] = (long) (maxbin[ii] + 1); } else { /* float datatype column and/or limits, and the maximum value to */ /* include in the histogram is specified by the calling program. */ /* The lower limit is inclusive, but upper limit is exclusive */ maxbin[ii] = (amax[ii] - amin[ii]) / binsize[ii]; haxes[ii] = (long) maxbin[ii]; if (amin[ii] < amax[ii]) { if (amin[ii] + (haxes[ii] * binsize[ii]) < amax[ii]) haxes[ii]++; /* need to include another partial bin */ } else { if (amin[ii] + (haxes[ii] * binsize[ii]) > amax[ii]) haxes[ii]++; /* need to include another partial bin */ } } } /* get the histogramming weighting factor */ if (*wtcol) { /* first, look for a keyword with the weight value */ if (ffgky(*fptr, TFLOAT, wtcol, &histData.weight, NULL, status) ) { /* not a keyword, so look for column with this name */ *status = 0; /* get the column number in the table */ if (ffgcno(*fptr, CASEINSEN, wtcol, &histData.wtcolnum, status) > 0) { ffpmsg( "keyword or column for histogram weights doesn't exist: "); ffpmsg(wtcol); return(*status); } histData.weight = FLOATNULLVALUE; } } else histData.weight = (float) weightin; if (histData.weight <= 0. && histData.weight != FLOATNULLVALUE) { ffpmsg("Illegal histogramming weighting factor <= 0."); return(*status = URL_PARSE_ERROR); } if (recip && histData.weight != FLOATNULLVALUE) /* take reciprocal of weight */ histData.weight = (float) (1.0 / histData.weight); histData.wtrecip = recip; /* size of histogram is now known, so create temp output file */ if (ffinit(&histptr, outfile, status) > 0) { ffpmsg("failed to create temp output file for histogram"); return(*status); } if (ffcrim(histptr, bitpix, histData.haxis, haxes, status) > 0) { ffpmsg("failed to create primary array histogram in temp file"); ffclos(histptr, status); return(*status); } /* copy all non-structural keywords from the table to the image */ fits_get_hdrspace(*fptr, &nkeys, NULL, status); for (ii = 1; ii <= nkeys; ii++) { fits_read_record(*fptr, ii, card, status); if (fits_get_keyclass(card) >= 120) fits_write_record(histptr, card, status); } /* Set global variables with histogram parameter values. */ /* Use separate scalar variables rather than arrays because */ /* it is more efficient when computing the histogram. */ histData.amin1 = amin[0]; histData.maxbin1 = maxbin[0]; histData.binsize1 = binsize[0]; histData.haxis1 = haxes[0]; if (histData.haxis > 1) { histData.amin2 = amin[1]; histData.maxbin2 = maxbin[1]; histData.binsize2 = binsize[1]; histData.haxis2 = haxes[1]; if (histData.haxis > 2) { histData.amin3 = amin[2]; histData.maxbin3 = maxbin[2]; histData.binsize3 = binsize[2]; histData.haxis3 = haxes[2]; if (histData.haxis > 3) { histData.amin4 = amin[3]; histData.maxbin4 = maxbin[3]; histData.binsize4 = binsize[3]; histData.haxis4 = haxes[3]; } } } /* define parameters of image for the iterator function */ fits_iter_set_file(imagepars, histptr); /* pointer to image */ fits_iter_set_datatype(imagepars, imagetype); /* image datatype */ fits_iter_set_iotype(imagepars, OutputCol); /* image is output */ /* call the iterator function to write out the histogram image */ if (fits_iterate_data(n_cols, imagepars, offset, n_per_loop, ffwritehisto, (void*)&histData, status) ) return(*status); /* write the World Coordinate System (WCS) keywords */ /* create default values if WCS keywords are not present in the table */ for (ii = 0; ii < histData.haxis; ii++) { /* CTYPEn */ tstatus = 0; ffkeyn("TCTYP", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TSTRING, keyname, svalue, NULL, &tstatus); if (tstatus) { /* just use column name as the type */ tstatus = 0; ffkeyn("TTYPE", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TSTRING, keyname, svalue, NULL, &tstatus); } if (!tstatus) { ffkeyn("CTYPE", ii + 1, keyname, &tstatus); ffpky(histptr, TSTRING, keyname, svalue, "Coordinate Type", &tstatus); } else tstatus = 0; /* CUNITn */ ffkeyn("TCUNI", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TSTRING, keyname, svalue, NULL, &tstatus); if (tstatus) { /* use the column units */ tstatus = 0; ffkeyn("TUNIT", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TSTRING, keyname, svalue, NULL, &tstatus); } if (!tstatus) { ffkeyn("CUNIT", ii + 1, keyname, &tstatus); ffpky(histptr, TSTRING, keyname, svalue, "Coordinate Units", &tstatus); } else tstatus = 0; /* CRPIXn - Reference Pixel */ ffkeyn("TCRPX", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, NULL, &tstatus); if (tstatus) { dvalue = 1.0; /* choose first pixel in new image as ref. pix. */ tstatus = 0; } else { /* calculate locate of the ref. pix. in the new image */ dvalue = (dvalue - amin[ii]) / binsize[ii] + .5; } ffkeyn("CRPIX", ii + 1, keyname, &tstatus); ffpky(histptr, TDOUBLE, keyname, &dvalue, "Reference Pixel", &tstatus); /* CRVALn - Value at the location of the reference pixel */ ffkeyn("TCRVL", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, NULL, &tstatus); if (tstatus) { /* calculate value at ref. pix. location (at center of 1st pixel) */ dvalue = amin[ii] + binsize[ii]/2.; tstatus = 0; } ffkeyn("CRVAL", ii + 1, keyname, &tstatus); ffpky(histptr, TDOUBLE, keyname, &dvalue, "Reference Value", &tstatus); /* CDELTn - unit size of pixels */ ffkeyn("TCDLT", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, NULL, &tstatus); if (tstatus) { dvalue = 1.0; /* use default pixel size */ tstatus = 0; } dvalue = dvalue * binsize[ii]; ffkeyn("CDELT", ii + 1, keyname, &tstatus); ffpky(histptr, TDOUBLE, keyname, &dvalue, "Pixel size", &tstatus); /* CROTAn - Rotation angle (degrees CCW) */ /* There should only be a CROTA2 keyword, and only for 2+ D images */ if (ii == 1) { ffkeyn("TCROT", histData.hcolnum[ii], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, NULL, &tstatus); if (!tstatus && dvalue != 0.) /* only write keyword if angle != 0 */ { ffkeyn("CROTA", ii + 1, keyname, &tstatus); ffpky(histptr, TDOUBLE, keyname, &dvalue, "Rotation angle", &tstatus); } else { /* didn't find CROTA for the 2nd axis, so look for one */ /* on the first axis */ tstatus = 0; ffkeyn("TCROT", histData.hcolnum[0], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, NULL, &tstatus); if (!tstatus && dvalue != 0.) /* only write keyword if angle != 0 */ { dvalue *= -1.; /* negate the value, because mirror image */ ffkeyn("CROTA", ii + 1, keyname, &tstatus); ffpky(histptr, TDOUBLE, keyname, &dvalue, "Rotation angle", &tstatus); } } } } /* convert any TPn_k keywords to PCi_j; the value remains unchanged */ /* also convert any TCn_k to CDi_j; the value is modified by n binning size */ /* This is a bit of a kludge, and only works for 2D WCS */ if (histData.haxis == 2) { /* PC1_1 */ tstatus = 0; ffkeyn("TP", histData.hcolnum[0], card, &tstatus); strcat(card,"_"); ffkeyn(card, histData.hcolnum[0], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) ffpky(histptr, TDOUBLE, "PC1_1", &dvalue, card, &tstatus); tstatus = 0; keyname[1] = 'C'; ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) { dvalue *= binsize[0]; ffpky(histptr, TDOUBLE, "CD1_1", &dvalue, card, &tstatus); } /* PC1_2 */ tstatus = 0; ffkeyn("TP", histData.hcolnum[0], card, &tstatus); strcat(card,"_"); ffkeyn(card, histData.hcolnum[1], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) ffpky(histptr, TDOUBLE, "PC1_2", &dvalue, card, &tstatus); tstatus = 0; keyname[1] = 'C'; ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) { dvalue *= binsize[0]; ffpky(histptr, TDOUBLE, "CD1_2", &dvalue, card, &tstatus); } /* PC2_1 */ tstatus = 0; ffkeyn("TP", histData.hcolnum[1], card, &tstatus); strcat(card,"_"); ffkeyn(card, histData.hcolnum[0], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) ffpky(histptr, TDOUBLE, "PC2_1", &dvalue, card, &tstatus); tstatus = 0; keyname[1] = 'C'; ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) { dvalue *= binsize[1]; ffpky(histptr, TDOUBLE, "CD2_1", &dvalue, card, &tstatus); } /* PC2_2 */ tstatus = 0; ffkeyn("TP", histData.hcolnum[1], card, &tstatus); strcat(card,"_"); ffkeyn(card, histData.hcolnum[1], keyname, &tstatus); ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) ffpky(histptr, TDOUBLE, "PC2_2", &dvalue, card, &tstatus); tstatus = 0; keyname[1] = 'C'; ffgky(*fptr, TDOUBLE, keyname, &dvalue, card, &tstatus); if (!tstatus) { dvalue *= binsize[1]; ffpky(histptr, TDOUBLE, "CD2_2", &dvalue, card, &tstatus); } } /* finally, close the original file and return ptr to the new image */ ffclos(*fptr, status); *fptr = histptr; return(*status); }