SourceXtractorPlusPlus 0.19.2
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
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
41namespace SourceXtractor {
42
50static 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
86static void close_fits(fitsfile* ptr) {
87 if (ptr != nullptr) {
88 int status = 0;
89 fits_close_file(ptr, &status);
90 }
91}
92
93FitsFile::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 char error_message[32];
130 fits_get_errstatus(status, error_message);
131 throw Elements::Exception()
132 << "Can't open FITS file: " << m_path << " status: " << status << " = " << error_message;
133 }
134 }
135 assert(ptr->Fptr->open_count == 1);
136}
137
139
140 // After modifying the FITS file, We need to close and reopen the file before we can query
141 // infos about it again without cfitsio crashing
142
143 int status = 0;
144 fitsfile* ptr = nullptr;
145
146 m_fits_ptr.reset(nullptr);
147
148 fits_open_image(&ptr, m_path.native().c_str(), m_is_writeable ? READWRITE : READONLY, &status);
149 if (status != 0) {
150 char error_message[32];
151 fits_get_errstatus(status, error_message);
152 throw Elements::Exception()
153 << "Can't close and reopen FITS file: " << m_path << " status: " << status << " = " << error_message;
154 }
155 assert(ptr->Fptr->open_count == 1);
156 m_fits_ptr.reset(ptr);
157
158 loadInfo();
159}
160
162 int status = 0;
163
164 fitsfile* ptr = m_fits_ptr.get();
165
166 // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already
167 int original_hdu = 0;
168 fits_get_hdu_num(ptr, &original_hdu);
169
170 // Number of HDU
172 int number_of_hdus = 0;
173 if (fits_get_num_hdus(ptr, &number_of_hdus, &status) < 0) {
174 char error_message[32];
175 fits_get_errstatus(status, error_message);
176 throw Elements::Exception() << "Can't get the number of HDUs in FITS file: " << m_path
177 << " status: " << status << " = " << error_message;
178 }
179 m_headers.clear();
180 m_headers.resize(number_of_hdus);
181
182 // loop over HDUs to determine which ones are images
183 int hdu_type = 0;
184 for (int hdu_number = 1; hdu_number <= number_of_hdus; ++hdu_number) {
185 fits_movabs_hdu(ptr, hdu_number, &hdu_type, &status);
186 if (status != 0) {
187 char error_message[32];
188 fits_get_errstatus(status, error_message);
189 throw Elements::Exception() << "Can't switch HDUs while opening: " << m_path
190 << " status: " << status << " = " << error_message;
191 }
192
193 if (hdu_type == IMAGE_HDU) {
194 int bitpix, naxis;
195 long naxes[3] = {1, 1, 1};
196
197 fits_get_img_param(ptr, 3, &bitpix, &naxis, naxes, &status);
198 if (status == 0 && (naxis == 2 || naxis == 3)) {
199 m_image_hdus.emplace_back(hdu_number);
200 }
201 }
202 }
203
204 // go back to saved HDU
205 fits_movabs_hdu(ptr, original_hdu, &hdu_type, &status);
206
207 // load all FITS headers
209
210 // load optional .head file to override headers
211 loadHeadFile();
212}
213
216 char record[81];
217 int keynum = 1, status = 0;
218
219 fits_read_record(fptr, keynum, record, &status);
220 while (status == 0 && strncmp(record, "END", 3) != 0) {
221 static boost::regex regex("([^=]{8})=([^\\/]*)(\\/(.*))?");
222 std::string record_str(record);
223
224 boost::smatch sub_matches;
225 if (boost::regex_match(record_str, sub_matches, regex)) {
226 auto keyword = boost::to_upper_copy(sub_matches[1].str());
227 auto value = sub_matches[2].str();
228 auto comment = sub_matches[4].str();
229 boost::trim(keyword);
230 boost::trim(value);
231 boost::trim(comment);
232 headers.emplace(keyword, MetadataEntry{valueAutoCast(value), {{"comment", comment}}});
233 }
234 fits_read_record(fptr, ++keynum, record, &status);
235 }
236
237 return headers;
238}
239
241 int status = 0;
242
243 // save current HDU (if the file is opened with advanced cfitsio syntax it might be set already)
244 int original_hdu = 0;
245 fits_get_hdu_num(m_fits_ptr.get(), &original_hdu);
246
247 int hdu_type = 0;
248 for (unsigned int i = 0; i < m_headers.size(); i++) {
249 fits_movabs_hdu(m_fits_ptr.get(), i + 1, &hdu_type, &status); // +1 hdus start at 1
250
252 }
253
254 // go back to saved HDU
255 fits_movabs_hdu(m_fits_ptr.get(), original_hdu, &hdu_type, &status);
256}
257
259 auto base_name = m_path.stem();
260 base_name.replace_extension(".head");
261 auto head_filename = m_path.parent_path() / base_name;
262
263 if (!boost::filesystem::exists(head_filename)) {
264 return;
265 }
266
267 auto hdu_iter = m_image_hdus.begin();
268 std::ifstream file;
269
270 // open the file and check
271 file.open(head_filename.native());
272 if (!file.good() || !file.is_open()) {
273 throw Elements::Exception() << "Cannot load ascii header file: " << head_filename;
274 }
275
276 while (file.good() && hdu_iter != m_image_hdus.end()) {
277 int current_hdu = *hdu_iter;
278
279 std::string line;
280 std::getline(file, line);
281
282 static boost::regex regex_blank_line("\\s*$");
283 line = boost::regex_replace(line, regex_blank_line, std::string(""));
284 if (line.size() == 0) {
285 continue;
286 }
287
288 if (boost::to_upper_copy(line) == "END") {
289 current_hdu = *(++hdu_iter);
290 } else {
291 static boost::regex regex("([^=]{1,8})=([^\\/]*)(\\/ (.*))?");
292 boost::smatch sub_matches;
293 if (boost::regex_match(line, sub_matches, regex) && sub_matches.size() >= 3) {
294 auto keyword = boost::to_upper_copy(sub_matches[1].str());
295 auto value = sub_matches[2].str();
296 auto comment = sub_matches[4].str();
297 boost::trim(keyword);
298 boost::trim(value);
299 boost::trim(comment);
300 m_headers.at(current_hdu - 1)[keyword] = MetadataEntry{valueAutoCast(value), {{"comment", comment}}};
301 ;
302 }
303 }
304 }
305}
306
308 int status = 0;
309
310 // save current HDU
311 int original_hdu = 0;
312 fits_get_hdu_num(m_fits_ptr.get(), &original_hdu);
313
314 // got to requested HDU
315 int hdu_type = 0;
316 fits_movabs_hdu(m_fits_ptr.get(), hdu, &hdu_type, &status);
317
318 // get dimensions
319 long naxes[3] = {1, 1, 1};
320 int bitpix, naxis;
321
322 fits_get_img_param(m_fits_ptr.get(), 3, &bitpix, &naxis, naxes, &status);
323 if (status != 0 || (naxis != 2 && naxis != 3)) {
324 char error_message[32];
325 fits_get_errstatus(status, error_message);
326 throw Elements::Exception()
327 << "Can't find 2D image or data cube in FITS file: " << m_path << "[" << hdu << "]"
328 << " status: " << status << " = " << error_message;
329 }
330
331 std::vector<int> dims;
332 dims.push_back(naxes[0]);
333 dims.push_back(naxes[1]);
334 if (naxis == 3) {
335 dims.push_back(naxes[2]);
336 }
337
338 // go back to saved HDU
339 fits_movabs_hdu(m_fits_ptr.get(), original_hdu, &hdu_type, &status);
340
341 return dims;
342}
343
344
345} // namespace SourceXtractor
T at(T... args)
T begin(T... args)
std::unique_ptr< fitsfile, void(*)(fitsfile *)> m_fits_ptr
Definition FitsFile.h:63
boost::filesystem::path m_path
Definition FitsFile.h:61
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition FitsFile.cpp:110
std::vector< std::map< std::string, MetadataEntry > > m_headers
Definition FitsFile.h:65
fitsfile * getFitsFilePtr()
Definition FitsFile.cpp:102
std::vector< int > getDimensions(int hdu) const
Definition FitsFile.cpp:307
std::vector< int > m_image_hdus
Definition FitsFile.h:64
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)
Path::Item path
static void close_fits(fitsfile *ptr)
Definition FitsFile.cpp:86
static std::map< std::string, MetadataEntry > loadHeadersFromFits(fitsfile *fptr)
Definition FitsFile.cpp:214
static MetadataEntry::value_t valueAutoCast(const std::string &value)
Definition FitsFile.cpp:50
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:43