SourceXtractorPlusPlus
0.21
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 "ElementsKernel/Logging.h"
40
41
#include "
SEFramework/FITS/FitsFile.h
"
42
43
namespace
SourceXtractor
{
44
45
static
Elements::Logging
logger
=
Elements::Logging::getLogger
(
"FitsFile"
);
46
54
static
typename
MetadataEntry::value_t
valueAutoCast
(
const
std::string
& value) {
55
boost::regex
float_regex
(
"^[-+]?(\\d*\\.?\\d+|\\d+\\.?\\d*)([eE][-+]?\\d+)?$"
);
56
boost::regex
int_regex
(
"^[-+]?\\d+$"
);
57
58
try
{
59
if
(value.
empty
()) {
60
return
value;
61
}
else
if
(boost::regex_match(value,
int_regex
)) {
62
return
static_cast<
int64_t
>
(
std::stoll
(value));
63
}
else
if
(boost::regex_match(value,
float_regex
)) {
64
return
std::stod
(value);
65
}
else
if
(value.
size
() == 1) {
66
return
value.
at
(0);
67
}
68
}
catch
(...) {
69
}
70
71
// Single quotes are used as escape code of another single quote, and
72
// the string starts and ends with single quotes.
73
// We used to use boost::io::quoted here, but it seems that starting with 1.73 it
74
// does not work well when the escape code and the delimiter are the same
75
std::string
unquoted
;
76
bool
escape =
false
;
77
78
unquoted
.reserve(value.
size
());
79
for
(
auto
i
= value.
begin
();
i
!= value.
end
(); ++
i
) {
80
if
(*
i
==
'\''
&& !escape) {
81
escape =
true
;
82
// skip this char
83
}
else
{
84
escape =
false
;
85
unquoted
.push_back(*
i
);
86
}
87
}
88
return
unquoted
;
89
}
90
91
static
void
close_fits
(
fitsfile
* ptr) {
92
if
(ptr !=
nullptr
) {
93
int
status
= 0;
94
fits_close_file
(ptr, &
status
);
95
}
96
}
97
98
FitsFile::FitsFile
(
const
boost::filesystem::path& path,
bool
writeable
)
99
: m_path(path), m_is_writeable(
writeable
), m_fits_ptr(
nullptr
,
close_fits
) {
100
101
open
();
102
loadInfo
();
103
}
104
105
FitsFile::~FitsFile
() {}
106
107
fitsfile
*
FitsFile::getFitsFilePtr
() {
108
return
m_fits_ptr
.
get
();
109
}
110
111
const
std::vector<int>
&
FitsFile::getImageHdus
()
const
{
112
return
m_image_hdus
;
113
}
114
115
std::map<std::string, MetadataEntry>
&
FitsFile::getHDUHeaders
(
int
hdu
) {
116
return
m_headers
.at(
hdu
- 1);
117
}
118
119
void
FitsFile::open
() {
120
int
status
= 0;
121
fitsfile
* ptr =
nullptr
;
122
123
// Open
124
fits_open_image
(&ptr,
m_path
.native().c_str(),
m_is_writeable
?
READWRITE
:
READONLY
, &
status
);
125
m_fits_ptr
.
reset
(ptr);
126
if
(
status
!= 0) {
127
if
(
m_is_writeable
) {
128
// Create file if it does not exists
129
status
= 0;
130
fits_create_file
(&ptr,
m_path
.native().c_str(), &
status
);
131
m_fits_ptr
.
reset
(ptr);
132
}
133
if
(
status
!= 0) {
134
char
error_message
[32];
135
fits_get_errstatus
(
status
,
error_message
);
136
throw
Elements::Exception
()
137
<<
"Can't open FITS file: "
<<
m_path
<<
" status: "
<<
status
<<
" = "
<<
error_message
;
138
}
139
}
140
assert
(ptr->Fptr->open_count == 1);
141
}
142
143
void
FitsFile::refresh
() {
144
145
// After modifying the FITS file, We need to close and reopen the file before we can query
146
// infos about it again without cfitsio crashing
147
148
int
status
= 0;
149
fitsfile
* ptr =
nullptr
;
150
151
m_fits_ptr
.
reset
(
nullptr
);
152
153
fits_open_image
(&ptr,
m_path
.native().c_str(),
m_is_writeable
?
READWRITE
:
READONLY
, &
status
);
154
if
(
status
!= 0) {
155
char
error_message
[32];
156
fits_get_errstatus
(
status
,
error_message
);
157
throw
Elements::Exception
()
158
<<
"Can't close and reopen FITS file: "
<<
m_path
<<
" status: "
<<
status
<<
" = "
<<
error_message
;
159
}
160
assert
(ptr->Fptr->open_count == 1);
161
m_fits_ptr
.
reset
(ptr);
162
163
loadInfo
();
164
}
165
166
void
FitsFile::loadInfo
() {
167
int
status
= 0;
168
169
fitsfile
* ptr =
m_fits_ptr
.
get
();
170
171
// save current HDU (if the file is opened with advanced cfitsio syntax it might be set already
172
int
original_hdu
= 0;
173
fits_get_hdu_num
(ptr, &
original_hdu
);
174
175
// Number of HDU
176
m_image_hdus
.
clear
();
177
int
number_of_hdus
= 0;
178
if
(
fits_get_num_hdus
(ptr, &
number_of_hdus
, &
status
) < 0) {
179
char
error_message
[32];
180
fits_get_errstatus
(
status
,
error_message
);
181
throw
Elements::Exception
() <<
"Can't get the number of HDUs in FITS file: "
<<
m_path
182
<<
" status: "
<<
status
<<
" = "
<<
error_message
;
183
}
184
m_headers
.clear();
185
m_headers
.resize(
number_of_hdus
);
186
187
// loop over HDUs to determine which ones are images
188
int
hdu_type
= 0;
189
for
(
int
hdu_number
= 1;
hdu_number
<=
number_of_hdus
; ++
hdu_number
) {
190
fits_movabs_hdu
(ptr,
hdu_number
, &
hdu_type
, &
status
);
191
if
(
status
!= 0) {
192
char
error_message
[32];
193
fits_get_errstatus
(
status
,
error_message
);
194
throw
Elements::Exception
() <<
"Can't switch HDUs while opening: "
<<
m_path
195
<<
" status: "
<<
status
<<
" = "
<<
error_message
;
196
}
197
198
if
(
hdu_type
==
IMAGE_HDU
) {
199
int
bitpix
,
naxis
;
200
long
naxes
[3] = {1, 1, 1};
201
202
fits_get_img_param
(ptr, 3, &
bitpix
, &
naxis
,
naxes
, &
status
);
203
if
(
status
== 0 && (
naxis
== 2 ||
naxis
== 3)) {
204
m_image_hdus
.
emplace_back
(
hdu_number
);
205
}
206
}
207
}
208
209
// go back to saved HDU
210
fits_movabs_hdu
(ptr,
original_hdu
, &
hdu_type
, &
status
);
211
212
// load all FITS headers
213
loadFitsHeader
();
214
215
// load optional .head file to override headers
216
loadHeadFile
();
217
}
218
219
static
std::map<std::string, MetadataEntry>
loadHeadersFromFits
(
fitsfile
*
fptr
) {
220
std::map<std::string, MetadataEntry>
headers
;
221
char
record
[81];
222
int
keynum
= 1,
status
= 0;
223
224
fits_read_record
(
fptr
,
keynum
,
record
, &
status
);
225
while
(
status
== 0 &&
strncmp
(
record
,
"END"
, 3) != 0) {
226
static
boost::regex
regex
(
"([^=]{8})=([^\\/]*)(\\/(.*))?"
);
227
std::string
record_str
(
record
);
228
229
boost::smatch
sub_matches
;
230
if
(boost::regex_match(
record_str
,
sub_matches
,
regex
)) {
231
auto
keyword
= boost::to_upper_copy(
sub_matches
[1].str());
232
auto
value =
sub_matches
[2].str();
233
auto
comment
=
sub_matches
[4].str();
234
boost::trim(
keyword
);
235
boost::trim(value);
236
boost::trim(
comment
);
237
headers
.emplace(
keyword
,
MetadataEntry
{
valueAutoCast
(value), {{
"comment"
,
comment
}}});
238
}
239
fits_read_record
(
fptr
, ++
keynum
,
record
, &
status
);
240
}
241
242
return
headers
;
243
}
244
245
void
FitsFile::loadFitsHeader
() {
246
int
status
= 0;
247
248
// save current HDU (if the file is opened with advanced cfitsio syntax it might be set already)
249
int
original_hdu
= 0;
250
fits_get_hdu_num
(
m_fits_ptr
.
get
(), &
original_hdu
);
251
252
int
hdu_type
= 0;
253
for
(
unsigned
int
i
= 0;
i
<
m_headers
.size();
i
++) {
254
fits_movabs_hdu
(
m_fits_ptr
.
get
(),
i
+ 1, &
hdu_type
, &
status
);
// +1 hdus start at 1
255
256
m_headers
[
i
] =
loadHeadersFromFits
(
m_fits_ptr
.
get
());
257
}
258
259
// go back to saved HDU
260
fits_movabs_hdu
(
m_fits_ptr
.
get
(),
original_hdu
, &
hdu_type
, &
status
);
261
}
262
263
void
FitsFile::loadHeadFile
() {
264
auto
base_name
=
m_path
.stem();
265
base_name
.replace_extension(
".head"
);
266
auto
head_filename
=
m_path
.parent_path() /
base_name
;
267
268
if
(!boost::filesystem::exists(
head_filename
)) {
269
return
;
270
}
271
272
auto
hdu_iter
=
m_image_hdus
.
begin
();
273
std::ifstream
file;
274
275
// open the file and check
276
file.
open
(
head_filename
.native());
277
if
(!file.
good
() || !file.
is_open
()) {
278
throw
Elements::Exception
() <<
"Cannot load ascii header file: "
<<
head_filename
;
279
}
280
281
logger
.
info
() <<
"Loading .head file: "
<<
head_filename
<<
" for fits: "
<<
m_path
;
282
283
int
headers_nb
= 0;
284
int
hdu_nb
= 0;
285
bool
is_new_hdu
=
true
;
286
while
(file.
good
() &&
hdu_iter
!=
m_image_hdus
.
end
()) {
287
int
current_hdu
= *
hdu_iter
;
288
289
std::string
line
;
290
std::getline
(file,
line
);
291
292
static
boost::regex
regex_blank_line
(
"\\s*$"
);
293
line
= boost::regex_replace(
line
,
regex_blank_line
,
std::string
(
""
));
294
if
(
line
.size() == 0) {
295
continue
;
296
}
297
298
if
(boost::to_upper_copy(
line
) ==
"END"
) {
299
current_hdu
= *(++
hdu_iter
);
300
is_new_hdu
=
true
;
301
}
else
{
302
static
boost::regex
regex
(
"([^=]{1,8})=([^\\/]*)(\\/ (.*))?"
);
303
boost::smatch
sub_matches
;
304
if
(boost::regex_match(
line
,
sub_matches
,
regex
) &&
sub_matches
.size() >= 3) {
305
auto
keyword
= boost::to_upper_copy(
sub_matches
[1].str());
306
auto
value =
sub_matches
[2].str();
307
auto
comment
=
sub_matches
[4].str();
308
boost::trim(
keyword
);
309
boost::trim(value);
310
boost::trim(
comment
);
311
m_headers
.at(
current_hdu
- 1)[
keyword
] =
MetadataEntry
{
valueAutoCast
(value), {{
"comment"
,
comment
}}};
312
headers_nb
++;
313
if
(
is_new_hdu
) {
314
hdu_nb
++;
315
is_new_hdu
=
false
;
316
}
317
}
318
}
319
}
320
if
(
headers_nb
> 0) {
321
logger
.
info
() <<
"Headers overriden: "
<<
headers_nb
<<
" in "
<<
hdu_nb
<<
" hdu(s)"
;
322
}
323
}
324
325
std::vector<int>
FitsFile::getDimensions
(
int
hdu
)
const
{
326
int
status
= 0;
327
328
// save current HDU
329
int
original_hdu
= 0;
330
fits_get_hdu_num
(
m_fits_ptr
.
get
(), &
original_hdu
);
331
332
// got to requested HDU
333
int
hdu_type
= 0;
334
fits_movabs_hdu
(
m_fits_ptr
.
get
(),
hdu
, &
hdu_type
, &
status
);
335
336
// get dimensions
337
long
naxes
[3] = {1, 1, 1};
338
int
bitpix
,
naxis
;
339
340
fits_get_img_param
(
m_fits_ptr
.
get
(), 3, &
bitpix
, &
naxis
,
naxes
, &
status
);
341
if
(
status
!= 0 || (
naxis
!= 2 &&
naxis
!= 3)) {
342
char
error_message
[32];
343
fits_get_errstatus
(
status
,
error_message
);
344
throw
Elements::Exception
()
345
<<
"Can't find 2D image or data cube in FITS file: "
<<
m_path
<<
"["
<<
hdu
<<
"]"
346
<<
" status: "
<<
status
<<
" = "
<<
error_message
;
347
}
348
349
std::vector<int>
dims
;
350
dims
.push_back(
naxes
[0]);
351
dims
.push_back(
naxes
[1]);
352
if
(
naxis
== 3) {
353
dims
.push_back(
naxes
[2]);
354
}
355
356
// go back to saved HDU
357
fits_movabs_hdu
(
m_fits_ptr
.
get
(),
original_hdu
, &
hdu_type
, &
status
);
358
359
return
dims
;
360
}
361
362
363
}
// namespace SourceXtractor
FitsFile.h
std::string::at
T at(T... args)
std::ifstream
std::regex
std::string
std::string::begin
T begin(T... args)
Elements::Exception
Elements::Logging
Elements::Logging::getLogger
static Logging getLogger(const std::string &name="")
Elements::Logging::info
void info(const std::string &logMessage)
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:166
SourceXtractor::FitsFile::getHDUHeaders
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition
FitsFile.cpp:115
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:105
SourceXtractor::FitsFile::getFitsFilePtr
fitsfile * getFitsFilePtr()
Definition
FitsFile.cpp:107
SourceXtractor::FitsFile::open
void open()
Definition
FitsFile.cpp:119
SourceXtractor::FitsFile::getDimensions
std::vector< int > getDimensions(int hdu) const
Definition
FitsFile.cpp:325
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:98
SourceXtractor::FitsFile::getImageHdus
const std::vector< int > & getImageHdus() const
Definition
FitsFile.cpp:111
SourceXtractor::FitsFile::loadFitsHeader
void loadFitsHeader()
Definition
FitsFile.cpp:245
SourceXtractor::FitsFile::refresh
void refresh()
Definition
FitsFile.cpp:143
SourceXtractor::FitsFile::m_is_writeable
bool m_is_writeable
Definition
FitsFile.h:62
SourceXtractor::FitsFile::loadHeadFile
void loadHeadFile()
Definition
FitsFile.cpp:263
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)
Euclid::Configuration::logger
static Elements::Logging logger
SourceXtractor
Definition
Aperture.h:30
SourceXtractor::close_fits
static void close_fits(fitsfile *ptr)
Definition
FitsFile.cpp:91
SourceXtractor::loadHeadersFromFits
static std::map< std::string, MetadataEntry > loadHeadersFromFits(fitsfile *fptr)
Definition
FitsFile.cpp:219
SourceXtractor::valueAutoCast
static MetadataEntry::value_t valueAutoCast(const std::string &value)
Definition
FitsFile.cpp:54
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.10.0