SourceXtractorPlusPlus  0.15
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  if (status != 0) {
121  if (m_is_writeable) {
122  // Create file if it does not exists
123  status = 0;
124  fits_create_file(&ptr, m_path.native().c_str(), &status);
125  }
126  if (status != 0) {
127  throw Elements::Exception() << "Can't open FITS file: " << m_path << " status: " << status;
128  }
129  }
130 
131  m_fits_ptr.reset(ptr);
132 }
133 
135 
136  // After modifying the FITS file, We need to close and reopen the file before we can query
137  // infos about it again without cfitsio crashing
138 
139  int status = 0;
140  fitsfile* ptr = nullptr;
141 
142  m_fits_ptr.reset(nullptr);
143 
144  fits_open_image(&ptr, m_path.native().c_str(), m_is_writeable ? READWRITE : READONLY, &status);
145  if (status != 0) {
146  throw Elements::Exception() << "Can't close and reopen FITS file: " << m_path << " status: " << status;
147  }
148  m_fits_ptr.reset(ptr);
149 
150  loadInfo();
151 }
152 
154  int status = 0;
155 
156  fitsfile* ptr = m_fits_ptr.get();
157 
158  // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already
159  int original_hdu = 0;
160  fits_get_hdu_num(ptr, &original_hdu);
161 
162  // Number of HDU
164  int number_of_hdus = 0;
165  if (fits_get_num_hdus(ptr, &number_of_hdus, &status) < 0) {
166  throw Elements::Exception() << "Can't get the number of HDUs in FITS file: " << m_path;
167  }
168  m_headers.clear();
169  m_headers.resize(number_of_hdus);
170 
171  // loop over HDUs to determine which ones are images
172  int hdu_type = 0;
173  for (int hdu_number = 1; hdu_number <= number_of_hdus; ++hdu_number) {
174  fits_movabs_hdu(ptr, hdu_number, &hdu_type, &status);
175  if (status != 0) {
176  throw Elements::Exception() << "Can't switch HDUs while opening: " << m_path;
177  }
178 
179  if (hdu_type == IMAGE_HDU) {
180  int bitpix, naxis;
181  long naxes[2] = {1, 1};
182 
183  fits_get_img_param(ptr, 2, &bitpix, &naxis, naxes, &status);
184  if (status == 0 && naxis == 2) {
185  m_image_hdus.emplace_back(hdu_number);
186  }
187  }
188  }
189 
190  // go back to saved HDU
191  fits_movabs_hdu(ptr, original_hdu, &hdu_type, &status);
192 
193  // load all FITS headers
194  loadFitsHeader();
195 
196  // load optional .head file to override headers
197  loadHeadFile();
198 }
199 
202  char record[81];
203  int keynum = 1, status = 0;
204 
205  fits_read_record(fptr, keynum, record, &status);
206  while (status == 0 && strncmp(record, "END", 3) != 0) {
207  static boost::regex regex("([^=]{8})=([^\\/]*)(\\/(.*))?");
208  std::string record_str(record);
209 
210  boost::smatch sub_matches;
211  if (boost::regex_match(record_str, sub_matches, regex)) {
212  auto keyword = boost::to_upper_copy(sub_matches[1].str());
213  auto value = sub_matches[2].str();
214  auto comment = sub_matches[4].str();
215  boost::trim(keyword);
216  boost::trim(value);
217  boost::trim(comment);
218  headers.emplace(keyword, MetadataEntry{valueAutoCast(value), {{"comment", comment}}});
219  }
220  fits_read_record(fptr, ++keynum, record, &status);
221  }
222 
223  return headers;
224 }
225 
227  int status = 0;
228 
229  // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already)
230  int original_hdu = 0;
231  fits_get_hdu_num(m_fits_ptr.get(), &original_hdu);
232 
233  int hdu_type = 0;
234  for (unsigned int i = 0; i < m_headers.size(); i++) {
235  fits_movabs_hdu(m_fits_ptr.get(), i + 1, &hdu_type, &status); // +1 hdus start at 1
236 
238  }
239 
240  // go back to saved HDU
241  fits_movabs_hdu(m_fits_ptr.get(), original_hdu, &hdu_type, &status);
242 }
243 
245  auto base_name = m_path.stem();
246  base_name.replace_extension(".head");
247  auto head_filename = m_path.parent_path() / base_name;
248 
249  if (!boost::filesystem::exists(head_filename)) {
250  return;
251  }
252 
253  auto hdu_iter = m_image_hdus.begin();
254  std::ifstream file;
255 
256  // open the file and check
257  file.open(head_filename.native());
258  if (!file.good() || !file.is_open()) {
259  throw Elements::Exception() << "Cannot load ascii header file: " << head_filename;
260  }
261 
262  while (file.good() && hdu_iter != m_image_hdus.end()) {
263  int current_hdu = *hdu_iter;
264 
265  std::string line;
266  std::getline(file, line);
267 
268  static boost::regex regex_blank_line("\\s*$");
269  line = boost::regex_replace(line, regex_blank_line, std::string(""));
270  if (line.size() == 0) {
271  continue;
272  }
273 
274  if (boost::to_upper_copy(line) == "END") {
275  current_hdu = *(++hdu_iter);
276  } else {
277  static boost::regex regex("([^=]{1,8})=([^\\/]*)(\\/ (.*))?");
278  boost::smatch sub_matches;
279  if (boost::regex_match(line, sub_matches, regex) && sub_matches.size() >= 3) {
280  auto keyword = boost::to_upper_copy(sub_matches[1].str());
281  auto value = sub_matches[2].str();
282  auto comment = sub_matches[4].str();
283  boost::trim(keyword);
284  boost::trim(value);
285  boost::trim(comment);
286  m_headers.at(current_hdu - 1)[keyword] = MetadataEntry{valueAutoCast(value), {{"comment", comment}}};
287  ;
288  }
289  }
290  }
291 }
292 
293 } // namespace SourceXtractor
SourceXtractor::loadHeadersFromFits
static std::map< std::string, MetadataEntry > loadHeadersFromFits(fitsfile *fptr)
Definition: FitsFile.cpp:200
SourceXtractor::close_fits
static void close_fits(fitsfile *ptr)
Definition: FitsFile.cpp:86
SourceXtractor::FitsFile::refresh
void refresh()
Definition: FitsFile.cpp:134
SourceXtractor::FitsFile::getImageHdus
const std::vector< int > & getImageHdus() const
Definition: FitsFile.cpp:106
std::string
STL class.
SourceXtractor::FitsFile::FitsFile
FitsFile(const boost::filesystem::path &path, bool writeable)
Definition: FitsFile.cpp:93
std::string::reserve
T reserve(T... args)
std::vector< int >
std::string::size
T size(T... args)
SourceXtractor::FitsFile::m_is_writeable
bool m_is_writeable
Definition: FitsFile.h:61
std::map::emplace
T emplace(T... args)
std::unique_ptr::get
T get(T... args)
std::unique_ptr::reset
T reset(T... args)
std::vector::clear
T clear(T... args)
SourceXtractor::valueAutoCast
static MetadataEntry::value_t valueAutoCast(const std::string &value)
Definition: FitsFile.cpp:50
std::string::push_back
T push_back(T... args)
SourceXtractor::FitsFile::m_headers
std::vector< std::map< std::string, MetadataEntry > > m_headers
Definition: FitsFile.h:64
std::stoll
T stoll(T... args)
SourceXtractor
Definition: Aperture.h:30
std::string::at
T at(T... args)
Exception.h
Elements::Exception
std::ifstream::open
T open(T... args)
SourceXtractor::MetadataEntry
Definition: ImageSource.h:39
std::int64_t
std::map
STL class.
std::regex
std::ifstream::good
T good(T... args)
SourceXtractor::FitsFile::getFitsFilePtr
fitsfile * getFitsFilePtr()
Definition: FitsFile.cpp:102
SourceXtractor::FitsFile::m_fits_ptr
std::unique_ptr< fitsfile, void(*)(fitsfile *)> m_fits_ptr
Definition: FitsFile.h:62
std::strncmp
T strncmp(T... args)
SourceXtractor::FitsFile::loadInfo
void loadInfo()
Definition: FitsFile.cpp:153
std::vector::emplace_back
T emplace_back(T... args)
std::stod
T stod(T... args)
SourceXtractor::FitsFile::~FitsFile
virtual ~FitsFile()
Definition: FitsFile.cpp:100
std::string::begin
T begin(T... args)
std::getline
T getline(T... args)
std::string::empty
T empty(T... args)
SourceXtractor::FitsFile::open
void open()
Definition: FitsFile.cpp:114
SourceXtractor::FitsFile::m_image_hdus
std::vector< int > m_image_hdus
Definition: FitsFile.h:63
std::string::end
T end(T... args)
SourceXtractor::FitsFile::getHDUHeaders
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition: FitsFile.cpp:110
SourceXtractor::FitsFile::loadHeadFile
void loadHeadFile()
Definition: FitsFile.cpp:244
path
Elements::Path::Item path
SourceXtractor::FitsFile::m_path
boost::filesystem::path m_path
Definition: FitsFile.h:60
SourceXtractor::FitsFile::loadFitsHeader
void loadFitsHeader()
Definition: FitsFile.cpp:226
std::ifstream::is_open
T is_open(T... args)
FitsFile.h
SourceXtractor::MetadataEntry::value_t
boost::variant< bool, char, int64_t, double, std::string > value_t
Definition: ImageSource.h:40
std::ifstream
STL class.