SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FitsImageSource.cpp
Go to the documentation of this file.
1 
18 /*
19  * FitsImageSource.cpp
20  *
21  * Created on: Mar 21, 2018
22  * Author: mschefer
23  */
24 
27 #include "SEUtils/VariantCast.h"
30 #include <boost/algorithm/string/case_conv.hpp>
31 #include <boost/algorithm/string/trim.hpp>
32 #include <boost/filesystem/operations.hpp>
33 #include <boost/filesystem/path.hpp>
34 #include <boost/regex.hpp>
35 #include <fstream>
36 #include <iomanip>
37 #include <numeric>
38 #include <string>
39 
40 namespace SourceXtractor {
41 
43 
44 namespace {
45 
46 ImageTile::ImageType convertImageType(int bitpix) {
47  ImageTile::ImageType image_type;
48 
49  switch (bitpix) {
50  case FLOAT_IMG:
51  image_type = ImageTile::FloatImage;
52  break;
53  case DOUBLE_IMG:
54  image_type = ImageTile::DoubleImage;
55  break;
56  case LONG_IMG:
57  image_type = ImageTile::IntImage;
58  break;
59  case ULONG_IMG:
60  image_type = ImageTile::UIntImage;
61  break;
62  case LONGLONG_IMG:
63  image_type = ImageTile::LongLongImage;
64  break;
65  default:
66  throw Elements::Exception() << "Unsupported FITS image type: " << bitpix;
67  }
68 
69  return image_type;
70 }
71 
72 }
73 
75  ImageTile::ImageType image_type,
77  : m_filename(filename)
78  , m_file_manager(std::move(manager))
79  , m_handler(m_file_manager->getFileHandler(filename))
80  , m_hdu_number(hdu_number) {
81  int status = 0;
82  int bitpix, naxis;
83  long naxes[3] = {1, 1, 1};
84 
85  auto acc = m_handler->getAccessor<FitsFile>();
86  auto fptr = acc->m_fd.getFitsFilePtr();
87 
88  if (m_hdu_number <= 0) {
89  if (fits_get_hdu_num(fptr, &m_hdu_number) < 0) {
90  throw Elements::Exception() << "Can't get the active HDU from the FITS file: " << filename;
91  }
92  }
93  else {
94  switchHdu(fptr, m_hdu_number);
95  }
96 
97  fits_get_img_param(fptr, 2, &bitpix, &naxis, naxes, &status);
98  if (status != 0 || (naxis != 2 && naxis != 3)) {
99  throw Elements::Exception()
100  << "Can't find 2D image or data cube in FITS file: " << filename << "[" << m_hdu_number << "]";
101  }
102 
103  m_width = naxes[0];
104  m_height = naxes[1];
105  m_depth = naxis >= 3 ? naxes[2] : 1;
106 
107  if (image_type < 0) {
108  m_image_type = convertImageType(bitpix);
109  }
110  else {
111  m_image_type = image_type;
112  }
113 }
114 
116  const std::shared_ptr<CoordinateSystem> coord_system, bool append, bool empty_primary,
118  : m_filename(filename)
119  , m_file_manager(std::move(manager))
120  , m_handler(m_file_manager->getFileHandler(filename))
121  , m_width(width)
122  , m_height(height)
123  , m_image_type(image_type) {
124 
125  int status = 0;
126  fitsfile* fptr = nullptr;
127 
128  if (!append) {
129  // delete file if it exists already
130  boost::filesystem::remove(m_filename);
131  }
132 
133  {
134  auto acc = m_handler->getAccessor<FitsFile>(FileHandler::kWrite);
135  fptr = acc->m_fd.getFitsFilePtr();
136 
137  assert(fptr != nullptr);
138 
139  if (empty_primary && acc->m_fd.getImageHdus().empty()) {
140  fits_create_img(fptr, FLOAT_IMG, 0, nullptr, &status);
141  if (status != 0) {
142  throw Elements::Exception() << "Can't create empty hdu: " << filename << " status: " << status;
143  }
144  }
145 
146  long naxes[2] = {width, height};
147  fits_create_img(fptr, getImageType(), 2, naxes, &status);
148 
149  if (fits_get_hdu_num(fptr, &m_hdu_number) < 0) {
150  throw Elements::Exception() << "Can't get the active HDU from the FITS file: " << filename << " status: " << status;
151  }
152 
153  int hdutype = 0;
154  fits_movabs_hdu(fptr, m_hdu_number, &hdutype, &status);
155 
156  if (coord_system) {
157  auto headers = coord_system->getFitsHeaders();
158  for (const auto& h : headers) {
159  std::ostringstream padded_key, serializer;
160  padded_key << std::setw(8) << std::left << h.first;
161 
162  serializer << padded_key.str() << "= " << std::left << std::setw(70) << h.second;
163  auto str = serializer.str();
164 
165  fits_update_card(fptr, padded_key.str().c_str(), str.c_str(), &status);
166  if (status != 0) {
167  char err_txt[31];
168  fits_get_errstatus(status, err_txt);
169  throw Elements::Exception() << "Couldn't write the WCS headers (" << err_txt << "): " << str << " status: " << status;
170  }
171  }
172  }
173 
174  std::vector<char> buffer(width * ImageTile::getTypeSize(image_type));
175  for (int i = 0; i < height; i++) {
176  long first_pixel[2] = {1, i + 1};
177  fits_write_pix(fptr, getDataType(), first_pixel, width, &buffer[0], &status);
178  }
179 
180  if (status != 0) {
181  throw Elements::Exception() << "Couldn't allocate space for new FITS file: " << filename << " status: " << status;
182  }
183 
184  acc->m_fd.refresh(); // make sure changes to the file structure are taken into account
185 
186  }
187 
188  // work around for canonical name bug:
189  // our file's canonical name might be wrong if it didn't exist, so we need to make sure we get the correct handler
190  // after we created the file
191 
192  m_handler = nullptr;
193  m_handler = m_file_manager->getFileHandler(filename);
194 }
195 
197  auto acc = m_handler->getAccessor<FitsFile>();
198  auto fptr = acc->m_fd.getFitsFilePtr();
199  switchHdu(fptr, m_hdu_number);
200 
201  auto tile = ImageTile::create(m_image_type, x, y, width, height,
202  std::const_pointer_cast<ImageSource>(shared_from_this()));
203 
204  long first_pixel[3] = {x + 1, y + 1, m_current_layer+1};
205  long last_pixel[3] = {x + width, y + height, m_current_layer+1};
206  long increment[3] = {1, 1, 1};
207  int status = 0;
208 
209  fits_read_subset(fptr, getDataType(), first_pixel, last_pixel, increment,
210  nullptr, tile->getDataPtr(), nullptr, &status);
211  if (status != 0) {
212  throw Elements::Exception() << "Error reading image tile from FITS file.";
213  }
214 
215  return tile;
216 }
217 
219  auto acc = m_handler->getAccessor<FitsFile>(FileHandler::kWrite);
220  auto fptr = acc->m_fd.getFitsFilePtr();
221  switchHdu(fptr, m_hdu_number);
222 
223  int x = tile.getPosX();
224  int y = tile.getPosY();
225  int width = tile.getWidth();
226  int height = tile.getHeight();
227 
228  long first_pixel[2] = {x + 1, y + 1};
229  long last_pixel[2] = {x + width, y + height};
230  int status = 0;
231 
232  fits_write_subset(fptr, getDataType(), first_pixel, last_pixel, tile.getDataPtr(), &status);
233  if (status != 0) {
234  throw Elements::Exception() << "Error saving image tile to FITS file.";
235  }
236  fits_flush_buffer(fptr, 0, &status);
237 }
238 
239 void FitsImageSource::switchHdu(fitsfile *fptr, int hdu_number) const {
240  int status = 0;
241  int hdu_type = 0;
242 
243  fits_movabs_hdu(fptr, hdu_number, &hdu_type, &status);
244 
245  if (status != 0) {
246  throw Elements::Exception() << "Could not switch to HDU # " << hdu_number << " in file "
247  << m_filename;
248  }
249  if (hdu_type != IMAGE_HDU) {
250  throw Elements::Exception() << "Trying to access non-image HDU in file " << m_filename;
251  }
252 }
253 
254 void FitsImageSource::setLayer(int layer) {
255  if (layer < 0 && layer >= m_depth) {
256  throw Elements::Exception() << "Trying to access an inexistent data cube layer (" << layer << ") in " << m_filename;
257  }
258  m_current_layer = layer;
259 }
260 
262  number_of_records = 0;
263  std::string records;
264 
265  auto& headers = getMetadata();
266  for (const auto& record : headers) {
267  const auto& key = record.first;
268 
269  std::string record_string(key);
270  if (record_string.size() > 8) {
271  throw Elements::Exception() << "FITS keyword longer than 8 characters";
272  }
273  else if (record_string.size() < 8) {
274  record_string += std::string(8 - record_string.size(), ' ');
275  }
276 
277  if (headers.at(key).m_value.type() == typeid(std::string)) {
278  record_string += "= '" + VariantCast<std::string>(headers.at(key).m_value) + "'";
279  }
280  else {
281  record_string += "= " + VariantCast<std::string>(headers.at(key).m_value);
282  }
283 
284  if (record_string.size() > 80) {
285  throw Elements::Exception() << "FITS record longer than 80 characters";
286  }
287 
288 
289  if (record_string.size() < 80) {
290  record_string += std::string(80 - record_string.size(), ' ');
291  }
292 
293  records += record_string;
294  number_of_records++;
295  }
296 
297  std::string record_string("END");
298  record_string += std::string(80 - record_string.size(), ' ');
299  records += record_string;
300 
301  auto buffer = make_unique<std::vector<char>>(records.begin(), records.end());
302  buffer->emplace_back(0);
303  return buffer;
304 }
305 
307  auto acc = m_handler->getAccessor<FitsFile>();
308  return acc->m_fd.getHDUHeaders(m_hdu_number);
309 }
310 
312  auto acc = m_handler->getAccessor<FitsFile>();
313  auto fptr = acc->m_fd.getFitsFilePtr();
314  switchHdu(fptr, m_hdu_number);
315 
316  std::ostringstream padded_key, serializer;
317  padded_key << std::setw(8) << std::left << key;
318 
319  serializer << padded_key.str() << "= " << std::left << std::setw(70)
320  << VariantCast<std::string>(value.m_value);
321  auto str = serializer.str();
322 
323  int status = 0;
324  fits_update_card(fptr, padded_key.str().c_str(), str.c_str(), &status);
325 
326  if (status != 0) {
327  char err_txt[31];
328  fits_get_errstatus(status, err_txt);
329  throw Elements::Exception() << "Couldn't write the metadata (" << err_txt << "): " << str;
330  }
331 
332  // update the metadata
333  acc->m_fd.getHDUHeaders(m_hdu_number)[key] = value;
334 }
335 
337  switch (m_image_type) {
338  default:
340  return TFLOAT;
342  return TDOUBLE;
343  case ImageTile::IntImage:
344  return TINT;
346  return TUINT;
348  return TLONGLONG;
349  }
350 }
351 
353  switch (m_image_type) {
354  default:
356  return FLOAT_IMG;
358  return DOUBLE_IMG;
359  case ImageTile::IntImage:
360  return LONG_IMG;
362  return ULONG_IMG;
364  return LONGLONG_IMG;
365  }
366 }
367 
368 //FIXME add missing types
369 
370 }
std::unique_ptr< std::vector< char > > getFitsHeaders(int &number_of_records) const
std::shared_ptr< FileManager > m_file_manager
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
T left(T...args)
ImageTile::ImageType m_image_type
T end(T...args)
std::shared_ptr< ImageTile > getImageTile(int x, int y, int width, int height) const override
STL class.
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
T setw(T...args)
int getPosX() const
Definition: ImageTile.h:54
STL class.
void setMetadata(const std::string &key, const MetadataEntry &value) override
FitsImageSource(const std::string &filename, int hdu_number=0, ImageTile::ImageType image_type=ImageTile::AutoType, std::shared_ptr< FileManager > manager=FileManager::getDefault())
string filename
Definition: conf.py:65
static size_t getTypeSize(ImageType image_type)
Definition: ImageTile.h:117
represents access to a whole FITS file and handles loading and caching FITS headers ...
Definition: FitsFile.h:43
const std::map< std::string, MetadataEntry > & getMetadata() const override
T move(T...args)
int getWidth() const
Definition: ImageTile.h:64
void switchHdu(fitsfile *fptr, int hdu_number) const
T size(T...args)
STL class.
STL class.
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition: FitsFile.cpp:110
static std::shared_ptr< ImageTile > create(ImageType image_type, int x, int y, int width, int height, std::shared_ptr< ImageSource > source=nullptr)
Definition: ImageTile.cpp:24
T begin(T...args)
int getHeight() const
Definition: ImageTile.h:68
std::unique_ptr< T > make_unique(Args &&...args)
void saveTile(ImageTile &tile) override
fitsfile * getFitsFilePtr()
Definition: FitsFile.cpp:102
int getPosY() const
Definition: ImageTile.h:58
std::shared_ptr< FileHandler > m_handler
virtual void * getDataPtr()=0