56 Card(
const std::string &name,
bool val,
const char *commnt =
"")
57 : keyword(name), value(val), comment(commnt) {}
58 Card(
const std::string &name,
int val,
const char *commnt =
"")
59 : keyword(name), value(val), comment(commnt) {}
60 Card(
const std::string &name,
double val,
const char *commnt =
"")
61 : keyword(name), value(val), comment(commnt) {}
62 Card(
const std::string &name,
float val,
const char *commnt =
"")
63 : keyword(name), value(val), comment(commnt) {}
65 : keyword(name), value(val), comment(commnt) {}
66 Card(
const std::string &name,
const char *val,
const char *commnt =
"")
67 : keyword(name), value(
std::string(val)), comment(commnt) {}
71 int write(
int fd,
int ncard,
char *record)
const;
81int Card::write(
int fd,
int ncard,
char *record)
const {
82 char *card = &record[80 * ncard];
85 std::string const &str = std::any_cast<std::string>(value);
86 if (keyword ==
"" || keyword ==
"COMMENT" || keyword ==
"END" || keyword ==
"HISTORY") {
87 sprintf(card,
"%-8.8s%-72s", keyword.c_str(), str.c_str());
89 sprintf(card,
"%-8.8s= '%s' %c%-*s", keyword.c_str(), str.c_str(), (comment ==
"" ?
' ' :
'/'),
90 (
int)(80 - 14 - str.size()), comment.c_str());
93 sprintf(card,
"%-8.8s= ", keyword.c_str());
95 if (
value.type() ==
typeid(
bool)) {
96 sprintf(card,
"%20s", std::any_cast<bool>(value) ?
"T" :
"F");
97 }
else if (
value.type() ==
typeid(
int)) {
98 sprintf(card,
"%20d", std::any_cast<int>(value));
99 }
else if (
value.type() ==
typeid(
double)) {
100 sprintf(card,
"%20.10f", std::any_cast<double>(value));
101 }
else if (
value.type() ==
typeid(
float)) {
102 sprintf(card,
"%20.7f", std::any_cast<float>(value));
105 sprintf(card,
" %c%-48s", (comment ==
"" ?
' ' :
'/'), comment.c_str());
127void flip_high_bit(
char *arr,
131 (boost::format(
"Attempt to bit flip odd number of bytes: %d") % n).str());
134 unsigned short *uarr =
reinterpret_cast<unsigned short *
>(arr);
135 for (
unsigned short *end = uarr + n / 2; uarr <
end; ++uarr) {
145void swap_2(
char *arr,
149 (boost::format(
"Attempt to byte swap odd number of bytes: %d") % n).str());
152 for (
char *end = arr + n; arr <
end; arr += 2) {
161void swap_4(
char *arr,
165 (boost::format(
"Attempt to byte swap non-multiple of 4 bytes: %d") % n).str());
168 for (
char *end = arr + n; arr <
end; arr += 4) {
181void swap_8(
char *arr,
185 (boost::format(
"Attempt to byte swap non-multiple of 8 bytes: %d") % n).str());
188 for (
char *end = arr + n; arr <
end; arr += 8) {
204int write_fits_hdr(
int fd,
int bitpix,
int naxis,
int *naxes,
std::list<Card> &cards,
212 Card card(
"SIMPLE",
true);
213 ncard = card.write(fd, ncard, record);
215 Card card(
"XTENSION",
"IMAGE");
216 ncard = card.write(fd, ncard, record);
220 Card card(
"BITPIX", bitpix);
221 ncard = card.write(fd, ncard, record);
224 Card card(
"NAXIS", naxis);
225 ncard = card.write(fd, ncard, record);
227 for (i = 0; i < naxis; i++) {
228 char key[] =
"NAXIS.";
229 sprintf(key,
"NAXIS%d", i + 1);
230 Card card(key, naxes[i]);
231 ncard = card.write(fd, ncard, record);
234 Card card(
"EXTEND",
true,
"There may be extensions");
235 ncard = card.write(fd, ncard, record);
240 for (std::list<Card>::const_iterator card = cards.
begin(); card != cards.
end(); card++) {
241 ncard = card->write(fd, ncard, record);
245 Card card(
"END",
"");
246 ncard = card.write(fd, ncard, record);
250 ncard = card.write(fd, ncard, record);
259void pad_to_fits_record(
int fd,
263 const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix) / 8;
264 int nbyte = npixel * bytes_per_pixel;
270 memset(record,
' ', nbyte);
271 if (write(fd, record, nbyte) != nbyte) {
273 "error padding file to multiple of fits block size");
278int write_fits_data(
int fd,
int bitpix,
char *begin,
char *end) {
279 const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix) / 8;
281#if defined(LSST_LITTLE_ENDIAN)
282 if (bytes_per_pixel > 1) {
287 char *buff =
nullptr;
288 bool allocated =
false;
289 if (swap_bytes || bitpix == 16) {
290 buff =
new char[
FITS_SIZE * bytes_per_pixel];
296 for (
char *ptr = begin; ptr !=
end; nbyte -= nwrite, ptr += nwrite) {
297 if (end - ptr < nwrite) {
302 memcpy(buff, ptr, nwrite);
304 flip_high_bit(buff, nwrite);
307 if (bytes_per_pixel == 2) {
308 swap_2(buff, nwrite);
309 }
else if (bytes_per_pixel == 4) {
310 swap_4(buff, nwrite);
311 }
else if (bytes_per_pixel == 8) {
312 swap_8(buff, nwrite);
314 fprintf(stderr,
"You cannot get here\n");
319 memcpy(buff, ptr, nwrite);
320 flip_high_bit(buff, nwrite);
326 if (write(fd, buff, nwrite) != nwrite) {
327 perror(
"Error writing image: ");
336 return (nbyte == 0 ? 0 : -1);
340 cards.
emplace_back(str(boost::format(
"CRVAL1%s") % wcsName), x0,
"(output) Column pixel of Reference Pixel");
341 cards.
emplace_back(str(boost::format(
"CRVAL2%s") % wcsName), y0,
"(output) Row pixel of Reference Pixel");
342 cards.
emplace_back(str(boost::format(
"CRPIX1%s") % wcsName), 1.0,
"Column Pixel Coordinate of Reference");
343 cards.
emplace_back(str(boost::format(
"CRPIX2%s") % wcsName), 1.0,
"Row Pixel Coordinate of Reference");
344 cards.
emplace_back(str(boost::format(
"CTYPE1%s") % wcsName),
"LINEAR",
"Type of projection");
345 cards.
emplace_back(str(boost::format(
"CTYPE1%s") % wcsName),
"LINEAR",
"Type of projection");
346 cards.
emplace_back(str(boost::format(
"CUNIT1%s") % wcsName),
"PIXEL",
"Column unit");
347 cards.
emplace_back(str(boost::format(
"CUNIT2%s") % wcsName),
"PIXEL",
"Row unit");
355template <
typename ImageT>
358 geom::SkyWcs
const *Wcs,
373 }
else if (bitpix == 0) {
379 addWcs(
"A", cards, data.getX0(), data.getY0());
386 cards.
emplace_back(
"OBJECT", title,
"Image being displayed");
391 if (Wcs ==
nullptr) {
397 auto newWcs = Wcs->copyAtShiftedPixelOrigin(shift);
401 NameList paramNames = metadata->paramNames();
403 for (
auto const ¶mName : paramNames) {
404 if (paramName ==
"SIMPLE" || paramName ==
"BITPIX" || paramName ==
"NAXIS" || paramName ==
"NAXIS1" || paramName ==
"NAXIS2" ||
405 paramName ==
"XTENSION" || paramName ==
"PCOUNT" || paramName ==
"GCOUNT") {
409 if (type ==
typeid(
bool)) {
411 }
else if (type ==
typeid(
int)) {
413 }
else if (type ==
typeid(
float)) {
415 }
else if (type ==
typeid(
double)) {
427 naxes[0] = data.getWidth();
428 naxes[1] = data.getHeight();
430 write_fits_hdr(fd, bitpix, naxis, naxes, cards, 1);
431 for (
int y = 0; y != data.getHeight(); ++y) {
432 if (write_fits_data(fd, bitpix, (
char *)(data.row_begin(y)), (
char *)(data.row_end(y))) < 0) {
434 (boost::format(
"Error writing data for row %d") % y).str());
438 pad_to_fits_record(fd, data.getWidth() * data.getHeight(), bitpix);
441template <
typename ImageT>
444 geom::SkyWcs
const *Wcs,
448 if ((filename.
c_str())[0] ==
'|') {
449 const char *cmd = filename.
c_str() + 1;
450 while (isspace(*cmd)) {
454 fd = fileno(popen(cmd,
"w"));
456 fd = creat(filename.
c_str(), 777);
461 (boost::format(
"Cannot open \"%s\"") % filename).str());
475#define INSTANTIATE(IMAGET) \
476 template void writeBasicFits(int, IMAGET const &, geom::SkyWcs const *, char const *); \
477 template void writeBasicFits(std::string const &, IMAGET const &, geom::SkyWcs const *, char const *)
479#define INSTANTIATE_IMAGE(T) INSTANTIATE(lsst::afw::image::Image<T>)
480#define INSTANTIATE_MASK(T) INSTANTIATE(lsst::afw::image::Mask<T>)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Class for storing generic metadata.
Provides consistent interface for LSST exceptions.
Reports errors that are due to events beyond the control of the program.
T emplace_back(T... args)
#define INSTANTIATE_IMAGE(IMAGE)
void writeBasicFits(int fd, ImageT const &data, geom::SkyWcs const *Wcs, char const *title)
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
std::int32_t MaskPixel
default type for Masks and MaskedImage Masks
Extent< double, 2 > Extent2D