SourceXtractorPlusPlus  0.16
Please provide a description of the project.
FitsFile.cpp
Go to the documentation of this file.
1 
18 /*
19  * FitsFile.cpp
20  *
21  * Created on: Jun 9, 2020
22  * Author: mschefer
23  */
24 
25 #include <assert.h>
26 
27 #include <fstream>
28 #include <iomanip>
29 #include <iostream>
30 #include <string>
31 
32 #include <boost/algorithm/string/case_conv.hpp>
33 #include <boost/algorithm/string/trim.hpp>
34 #include <boost/filesystem/operations.hpp>
35 #include <boost/filesystem/path.hpp>
36 #include <boost/regex.hpp>
37 
40 
41 namespace SourceXtractor {
42 
50 static typename MetadataEntry::value_t valueAutoCast(const std::string& value) {
51  boost::regex float_regex("^[-+]?(\\d*\\.?\\d+|\\d+\\.?\\d*)([eE][-+]?\\d+)?$");
52  boost::regex int_regex("^[-+]?\\d+$");
53 
54  try {
55  if (value.empty()) {
56  return value;
57  } else if (boost::regex_match(value, int_regex)) {
58  return static_cast<int64_t>(std::stoll(value));
59  } else if (boost::regex_match(value, float_regex)) {
60  return std::stod(value);
61  } else if (value.size() == 1) {
62  return value.at(0);
63  }
64  } catch (...) {
65  }
66 
67  // Single quotes are used as escape code of another single quote, and
68  // the string starts and ends with single quotes.
69  // We used to use boost::io::quoted here, but it seems that starting with 1.73 it
70  // does not work well when the escape code and the delimiter are the same
71  std::string unquoted;
72  bool escape = false;
73  unquoted.reserve(value.size());
74  for (auto i = value.begin(); i != value.end(); ++i) {
75  if (*i == '\'' && !escape) {
76  escape = true;
77  // skip this char
78  } else {
79  escape = false;
80  unquoted.push_back(*i);
81  }
82  }
83  return unquoted;
84 }
85 
86 static void close_fits(fitsfile* ptr) {
87  if (ptr != nullptr) {
88  int status = 0;
89  fits_close_file(ptr, &status);
90  }
91 }
92 
93 FitsFile::FitsFile(const boost::filesystem::path& path, bool writeable)
94  : m_path(path), m_is_writeable(writeable), m_fits_ptr(nullptr, close_fits) {
95 
96  open();
97  loadInfo();
98 }
99 
101 
103  return m_fits_ptr.get();
104 }
105 
107  return m_image_hdus;
108 }
109 
111  return m_headers.at(hdu - 1);
112 }
113 
115  int status = 0;
116  fitsfile* ptr = nullptr;
117 
118  // Open
119  fits_open_image(&ptr, m_path.native().c_str(), m_is_writeable ? READWRITE : READONLY, &status);
120  m_fits_ptr.reset(ptr);
121  if (status != 0) {
122  if (m_is_writeable) {
123  // Create file if it does not exists
124  status = 0;
125  fits_create_file(&ptr, m_path.native().c_str(), &status);
126  m_fits_ptr.reset(ptr);
127  }
128  if (status != 0) {
129  throw Elements::Exception() << "Can't open FITS file: " << m_path << " status: " << status;
130  }
131  }
132  assert(ptr->Fptr->open_count == 1);
133 }
134 
136 
137  // After modifying the FITS file, We need to close and reopen the file before we can query
138  // infos about it again without cfitsio crashing
139 
140  int status = 0;
141  fitsfile* ptr = nullptr;
142 
143  m_fits_ptr.reset(nullptr);
144 
145  fits_open_image(&ptr, m_path.native().c_str(), m_is_writeable ? READWRITE : READONLY, &status);
146  if (status != 0) {
147  throw Elements::Exception() << "Can't close and reopen FITS file: " << m_path << " status: " << status;
148  }
149  assert(ptr->Fptr->open_count == 1);
150  m_fits_ptr.reset(ptr);
151 
152  loadInfo();
153 }
154 
156  int status = 0;
157 
158  fitsfile* ptr = m_fits_ptr.get();
159 
160  // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already
161  int original_hdu = 0;
162  fits_get_hdu_num(ptr, &original_hdu);
163 
164  // Number of HDU
166  int number_of_hdus = 0;
167  if (fits_get_num_hdus(ptr, &number_of_hdus, &status) < 0) {
168  throw Elements::Exception() << "Can't get the number of HDUs in FITS file: " << m_path;
169  }
170  m_headers.clear();
171  m_headers.resize(number_of_hdus);
172 
173  // loop over HDUs to determine which ones are images
174  int hdu_type = 0;
175  for (int hdu_number = 1; hdu_number <= number_of_hdus; ++hdu_number) {
176  fits_movabs_hdu(ptr, hdu_number, &hdu_type, &status);
177  if (status != 0) {
178  throw Elements::Exception() << "Can't switch HDUs while opening: " << m_path;
179  }
180 
181  if (hdu_type == IMAGE_HDU) {
182  int bitpix, naxis;
183  long naxes[2] = {1, 1};
184 
185  fits_get_img_param(ptr, 2, &bitpix, &naxis, naxes, &status);
186  if (status == 0 && naxis == 2) {
187  m_image_hdus.emplace_back(hdu_number);
188  }
189  }
190  }
191 
192  // go back to saved HDU
193  fits_movabs_hdu(ptr, original_hdu, &hdu_type, &status);
194 
195  // load all FITS headers
196  loadFitsHeader();
197 
198  // load optional .head file to override headers
199  loadHeadFile();
200 }
201 
204  char record[81];
205  int keynum = 1, status = 0;
206 
207  fits_read_record(fptr, keynum, record, &status);
208  while (status == 0 && strncmp(record, "END", 3) != 0) {
209  static boost::regex regex("([^=]{8})=([^\\/]*)(\\/(.*))?");
210  std::string record_str(record);
211 
212  boost::smatch sub_matches;
213  if (boost::regex_match(record_str, sub_matches, regex)) {
214  auto keyword = boost::to_upper_copy(sub_matches[1].str());
215  auto value = sub_matches[2].str();
216  auto comment = sub_matches[4].str();
217  boost::trim(keyword);
218  boost::trim(value);
219  boost::trim(comment);
220  headers.emplace(keyword, MetadataEntry{valueAutoCast(value), {{"comment", comment}}});
221  }
222  fits_read_record(fptr, ++keynum, record, &status);
223  }
224 
225  return headers;
226 }
227 
229  int status = 0;
230 
231  // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already)
232  int original_hdu = 0;
233  fits_get_hdu_num(m_fits_ptr.get(), &original_hdu);
234 
235  int hdu_type = 0;
236  for (unsigned int i = 0; i < m_headers.size(); i++) {
237  fits_movabs_hdu(m_fits_ptr.get(), i + 1, &hdu_type, &status); // +1 hdus start at 1
238 
240  }
241 
242  // go back to saved HDU
243  fits_movabs_hdu(m_fits_ptr.get(), original_hdu, &hdu_type, &status);
244 }
245 
247  auto base_name = m_path.stem();
248  base_name.replace_extension(".head");
249  auto head_filename = m_path.parent_path() / base_name;
250 
251  if (!boost::filesystem::exists(head_filename)) {
252  return;
253  }
254 
255  auto hdu_iter = m_image_hdus.begin();
256  std::ifstream file;
257 
258  // open the file and check
259  file.open(head_filename.native());
260  if (!file.good() || !file.is_open()) {
261  throw Elements::Exception() << "Cannot load ascii header file: " << head_filename;
262  }
263 
264  while (file.good() && hdu_iter != m_image_hdus.end()) {
265  int current_hdu = *hdu_iter;
266 
267  std::string line;
268  std::getline(file, line);
269 
270  static boost::regex regex_blank_line("\\s*$");
271  line = boost::regex_replace(line, regex_blank_line, std::string(""));
272  if (line.size() == 0) {
273  continue;
274  }
275 
276  if (boost::to_upper_copy(line) == "END") {
277  current_hdu = *(++hdu_iter);
278  } else {
279  static boost::regex regex("([^=]{1,8})=([^\\/]*)(\\/ (.*))?");
280  boost::smatch sub_matches;
281  if (boost::regex_match(line, sub_matches, regex) && sub_matches.size() >= 3) {
282  auto keyword = boost::to_upper_copy(sub_matches[1].str());
283  auto value = sub_matches[2].str();
284  auto comment = sub_matches[4].str();
285  boost::trim(keyword);
286  boost::trim(value);
287  boost::trim(comment);
288  m_headers.at(current_hdu - 1)[keyword] = MetadataEntry{valueAutoCast(value), {{"comment", comment}}};
289  ;
290  }
291  }
292  }
293 }
294 
295 } // namespace SourceXtractor
T at(T... args)
T begin(T... args)
std::unique_ptr< fitsfile, void(*)(fitsfile *)> m_fits_ptr
Definition: FitsFile.h:62
boost::filesystem::path m_path
Definition: FitsFile.h:60
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition: FitsFile.cpp:110
std::vector< std::map< std::string, MetadataEntry > > m_headers
Definition: FitsFile.h:64
fitsfile * getFitsFilePtr()
Definition: FitsFile.cpp:102
std::vector< int > m_image_hdus
Definition: FitsFile.h:63
FitsFile(const boost::filesystem::path &path, bool writeable)
Definition: FitsFile.cpp:93
const std::vector< int > & getImageHdus() const
Definition: FitsFile.cpp:106
T clear(T... args)
T emplace_back(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T get(T... args)
T getline(T... args)
T good(T... args)
T is_open(T... args)
Elements::Path::Item path
static void close_fits(fitsfile *ptr)
Definition: FitsFile.cpp:86
static MetadataEntry::value_t valueAutoCast(const std::string &value)
Definition: FitsFile.cpp:50
static std::map< std::string, MetadataEntry > loadHeadersFromFits(fitsfile *fptr)
Definition: FitsFile.cpp:202
T open(T... args)
T push_back(T... args)
T reserve(T... args)
T reset(T... args)
T size(T... args)
T stod(T... args)
T stoll(T... args)
T strncmp(T... args)
boost::variant< bool, char, int64_t, double, std::string > value_t
Definition: ImageSource.h:40