SourceXtractorPlusPlus 0.19
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 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[3] = {1, 1, 1};
184
185 fits_get_img_param(ptr, 3, &bitpix, &naxis, naxes, &status);
186 if (status == 0 && (naxis == 2 || naxis == 3)) {
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
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
296 int status = 0;
297
298 // save current HDU
299 int original_hdu = 0;
300 fits_get_hdu_num(m_fits_ptr.get(), &original_hdu);
301
302 // got to requested HDU
303 int hdu_type = 0;
304 fits_movabs_hdu(m_fits_ptr.get(), hdu, &hdu_type, &status);
305
306 // get dimensions
307 long naxes[3] = {1, 1, 1};
308 int bitpix, naxis;
309
310 fits_get_img_param(m_fits_ptr.get(), 3, &bitpix, &naxis, naxes, &status);
311 if (status != 0 || (naxis != 2 && naxis != 3)) {
312 throw Elements::Exception()
313 << "Can't find 2D image or data cube in FITS file: " << m_path << "[" << hdu << "]";
314 }
315
316 std::vector<int> dims;
317 dims.push_back(naxes[0]);
318 dims.push_back(naxes[1]);
319 if (naxis == 3) {
320 dims.push_back(naxes[2]);
321 }
322
323 // go back to saved HDU
324 fits_movabs_hdu(m_fits_ptr.get(), original_hdu, &hdu_type, &status);
325
326 return dims;
327}
328
329
330} // 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:295
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:202
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:41