SourceXtractorPlusPlus
0.19.2
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
SEFramework
src
lib
FITS
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
38
#include "
ElementsKernel/Exception.h
"
39
#include "
SEFramework/FITS/FitsFile.h
"
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
100
FitsFile::~FitsFile
() {}
101
102
fitsfile
*
FitsFile::getFitsFilePtr
() {
103
return
m_fits_ptr
.
get
();
104
}
105
106
const
std::vector<int>
&
FitsFile::getImageHdus
()
const
{
107
return
m_image_hdus
;
108
}
109
110
std::map<std::string, MetadataEntry>
&
FitsFile::getHDUHeaders
(
int
hdu
) {
111
return
m_headers
.at(
hdu
- 1);
112
}
113
114
void
FitsFile::open
() {
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
138
void
FitsFile::refresh
() {
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
161
void
FitsFile::loadInfo
() {
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
171
m_image_hdus
.
clear
();
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
208
loadFitsHeader
();
209
210
// load optional .head file to override headers
211
loadHeadFile
();
212
}
213
214
static
std::map<std::string, MetadataEntry>
loadHeadersFromFits
(
fitsfile
*
fptr
) {
215
std::map<std::string, MetadataEntry>
headers
;
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
240
void
FitsFile::loadFitsHeader
() {
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
251
m_headers
[
i
] =
loadHeadersFromFits
(
m_fits_ptr
.
get
());
252
}
253
254
// go back to saved HDU
255
fits_movabs_hdu
(
m_fits_ptr
.
get
(),
original_hdu
, &
hdu_type
, &
status
);
256
}
257
258
void
FitsFile::loadHeadFile
() {
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
307
std::vector<int>
FitsFile::getDimensions
(
int
hdu
)
const
{
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
Exception.h
FitsFile.h
std::string::at
T at(T... args)
std::ifstream
std::regex
std::string
std::string::begin
T begin(T... args)
Elements::Exception
SourceXtractor::FitsFile::m_fits_ptr
std::unique_ptr< fitsfile, void(*)(fitsfile *)> m_fits_ptr
Definition
FitsFile.h:63
SourceXtractor::FitsFile::m_path
boost::filesystem::path m_path
Definition
FitsFile.h:61
SourceXtractor::FitsFile::loadInfo
void loadInfo()
Definition
FitsFile.cpp:161
SourceXtractor::FitsFile::getHDUHeaders
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition
FitsFile.cpp:110
SourceXtractor::FitsFile::m_headers
std::vector< std::map< std::string, MetadataEntry > > m_headers
Definition
FitsFile.h:65
SourceXtractor::FitsFile::~FitsFile
virtual ~FitsFile()
Definition
FitsFile.cpp:100
SourceXtractor::FitsFile::getFitsFilePtr
fitsfile * getFitsFilePtr()
Definition
FitsFile.cpp:102
SourceXtractor::FitsFile::open
void open()
Definition
FitsFile.cpp:114
SourceXtractor::FitsFile::getDimensions
std::vector< int > getDimensions(int hdu) const
Definition
FitsFile.cpp:307
SourceXtractor::FitsFile::m_image_hdus
std::vector< int > m_image_hdus
Definition
FitsFile.h:64
SourceXtractor::FitsFile::FitsFile
FitsFile(const boost::filesystem::path &path, bool writeable)
Definition
FitsFile.cpp:93
SourceXtractor::FitsFile::getImageHdus
const std::vector< int > & getImageHdus() const
Definition
FitsFile.cpp:106
SourceXtractor::FitsFile::loadFitsHeader
void loadFitsHeader()
Definition
FitsFile.cpp:240
SourceXtractor::FitsFile::refresh
void refresh()
Definition
FitsFile.cpp:138
SourceXtractor::FitsFile::m_is_writeable
bool m_is_writeable
Definition
FitsFile.h:62
SourceXtractor::FitsFile::loadHeadFile
void loadHeadFile()
Definition
FitsFile.cpp:258
std::vector::clear
T clear(T... args)
std::vector::emplace_back
T emplace_back(T... args)
std::string::empty
T empty(T... args)
std::string::end
T end(T... args)
std::function
std::unique_ptr::get
T get(T... args)
std::getline
T getline(T... args)
std::ifstream::good
T good(T... args)
std::int64_t
std::ifstream::is_open
T is_open(T... args)
SourceXtractor
Definition
Aperture.h:30
SourceXtractor::close_fits
static void close_fits(fitsfile *ptr)
Definition
FitsFile.cpp:86
SourceXtractor::loadHeadersFromFits
static std::map< std::string, MetadataEntry > loadHeadersFromFits(fitsfile *fptr)
Definition
FitsFile.cpp:214
SourceXtractor::valueAutoCast
static MetadataEntry::value_t valueAutoCast(const std::string &value)
Definition
FitsFile.cpp:50
std::ifstream::open
T open(T... args)
std::unique_ptr::reset
T reset(T... args)
std::string::size
T size(T... args)
std::stod
T stod(T... args)
std::stoll
T stoll(T... args)
std::strncmp
T strncmp(T... args)
SourceXtractor::MetadataEntry
Definition
ImageSource.h:42
SourceXtractor::MetadataEntry::value_t
boost::variant< bool, char, int64_t, double, std::string > value_t
Definition
ImageSource.h:43
Generated by
1.9.8