SourceXtractorPlusPlus
0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
SEFramework
src
lib
CoordinateSystem
WCS.cpp
Go to the documentation of this file.
1
18
/*
19
* WCS.cpp
20
*
21
* Created on: Nov 17, 2016
22
* Author: mschefer
23
*/
24
25
#include "
SEFramework/CoordinateSystem/WCS.h
"
26
27
#include <boost/algorithm/string/trim.hpp>
28
#include <fitsio.h>
29
#include <mutex>
30
#include <wcslib/dis.h>
31
#include <wcslib/wcs.h>
32
#include <wcslib/wcsfix.h>
33
#include <wcslib/wcshdr.h>
34
#include <wcslib/wcsprintf.h>
35
36
#include "ElementsKernel/Exception.h"
37
#include "ElementsKernel/Logging.h"
38
39
namespace
SourceXtractor
{
40
41
static
auto
logger
=
Elements::Logging::getLogger
(
"WCS"
);
42
43
decltype
(&
wcssub
)
safe_wcssub
= &
wcssub
;
44
48
static
void
wcsRaiseOnParseError
(
int
ret_code
) {
49
switch
(
ret_code
) {
50
case
WCSHDRERR_SUCCESS
:
51
break
;
52
case
WCSHDRERR_MEMORY
:
53
throw
Elements::Exception
() <<
"wcslib failed to allocate memory"
;
54
case
WCSHDRERR_PARSER
:
55
throw
Elements::Exception
() <<
"wcslib failed to parse the FITS headers"
;
56
default
:
57
throw
Elements::Exception
() <<
"Unexpected error when parsing the FITS WCS header: "
58
<<
ret_code
;
59
}
60
}
61
62
static
void
wcsLogErr
(
wcserr
*
err
) {
63
if
(
err
) {
64
logger
.error() <<
err
->file <<
":"
<<
err
->line_no <<
" "
<<
err
->
function
;
65
logger
.error() <<
err
->msg;
66
}
67
}
68
72
static
void
wcsRaiseOnTransformError
(
wcsprm
*
wcs
,
int
ret_code
) {
73
if
(
ret_code
!=
WCSERR_SUCCESS
) {
74
wcsLogErr
(
wcs
->err);
75
wcsLogErr
(
wcs
->lin.err);
76
if
(
wcs
->lin.dispre) {
77
wcsLogErr
(
wcs
->lin.dispre->err);
78
}
79
if
(
wcs
->lin.disseq) {
80
wcsLogErr
(
wcs
->lin.disseq->err);
81
}
82
throw
InvalidCoordinatesException
() <<
wcs_errmsg
[
ret_code
];
83
}
84
}
85
89
static
std::set<std::string>
wcsExtractKeywords
(
const
char
*
header
,
int
number_of_records
) {
90
std::set<std::string>
result
;
91
for
(
const
char
*p =
header
; *p !=
'\0'
&&
number_of_records
; --
number_of_records
, p += 80) {
92
size_t
len
=
strcspn
(p,
"= "
);
93
result
.insert(
std::string
(p,
len
));
94
}
95
return
result
;
96
}
97
101
static
void
wcsCheckHeaders
(
const
wcsprm
*
wcs
,
const
char
*
headers_str
,
int
number_of_records
) {
102
auto
headers
=
wcsExtractKeywords
(
headers_str
,
number_of_records
);
103
104
// SIP present, but not specified in CTYPE
105
// See https://github.com/astrorama/SourceXtractorPlusPlus/issues/254#issuecomment-765235869
106
if
(
wcs
->lin.dispre) {
107
bool
sip_used
=
false
,
sip_specified
=
false
;
108
for
(
int
i
= 0;
i
<
wcs
->naxis; ++
i
) {
109
sip_used
|= (
strncmp
(
wcs
->lin.dispre->dtype[
i
],
"SIP"
, 3) == 0);
110
size_t
ctype_len
=
strlen
(
wcs
->ctype[
i
]);
111
sip_specified
|= (
strncmp
(
wcs
->ctype[
i
] +
ctype_len
- 4,
"-SIP"
, 4) == 0);
112
}
113
if
(
sip_used
&& !
sip_specified
) {
114
logger
.warn() <<
"SIP coefficients present, but CTYPE has not the '-SIP' suffix"
;
115
logger
.warn() <<
"SIP distortion will be applied, but this may not be desired"
;
116
logger
.warn() <<
"To suppress this warning, explicitly add the '-SIP' suffix to the CTYPE,"
;
117
logger
.warn() <<
"or remove the SIP distortion coefficients"
;
118
}
119
}
120
}
121
125
static
void
wcsReportWarnings
(
const
char
*
err_buffer
) {
126
if
(
err_buffer
[0]) {
127
logger
.warn() <<
"WCS generated some errors in strict mode. This may be OK."
;
128
logger
.warn() <<
"Will run in relaxed mode."
;
129
const
char
*
eol
;
130
do
{
131
eol
=
strchr
(
err_buffer
,
'\n'
);
132
if
(
eol
) {
133
logger
.warn() <<
std::string
(
err_buffer
,
eol
-
err_buffer
);
134
err_buffer
=
eol
+ 1;
135
}
136
else
{
137
logger
.warn() <<
std::string
(
err_buffer
);
138
}
139
}
while
(
eol
);
140
}
141
}
142
148
static
int
wrapped_wcssub
(
int
alloc
,
const
struct
wcsprm
*
wcssrc
,
int
*
nsub
,
int
axes
[],
struct
wcsprm
*
wcsdst
) {
149
static
std::mutex
cpy_mutex
;
150
std::lock_guard<std::mutex>
lock
(
cpy_mutex
);
151
152
return
wcssub
(
alloc
,
wcssrc
,
nsub
,
axes
,
wcsdst
);
153
}
154
155
WCS::WCS
(
const
FitsImageSource
&
fits_image_source
) : m_wcs(
nullptr
,
nullptr
) {
156
int
number_of_records
= 0;
157
auto
fits_headers
=
fits_image_source
.getFitsHeaders(
number_of_records
);
158
159
init
(&(*
fits_headers
)[0],
number_of_records
);
160
}
161
162
WCS::WCS
(
const
WCS
&
original
) : m_wcs(
nullptr
,
nullptr
) {
163
164
//FIXME Horrible hack: I couldn't figure out how to properly do a deep copy wcsprm so instead
165
// of making a copy, I use the ascii headers output from the original to recreate a new one
166
167
int
number_of_records
;
168
char
*
raw_header
;
169
170
if
(
wcshdo
(
WCSHDO_none
,
original
.m_wcs.get(), &
number_of_records
, &
raw_header
) != 0) {
171
throw
Elements::Exception
() <<
"Failed to get the FITS headers for the WCS coordinate system when copying WCS"
;
172
}
173
174
init
(
raw_header
,
number_of_records
);
175
176
free
(
raw_header
);
177
}
178
179
180
void
WCS::init
(
char
*
headers
,
int
number_of_records
) {
181
wcserr_enable
(1);
182
183
int
nreject
= 0,
nwcs
= 0,
nreject_strict
= 0;
184
wcsprm
*
wcs
;
185
186
// Write warnings to a buffer
187
wcsprintf_set
(
nullptr
);
188
189
// Do a first pass, in strict mode, and ignore the result.
190
// Log the reported errors as warnings
191
int
ret
=
wcspih
(
headers
,
number_of_records
,
WCSHDR_strict
, 2, &
nreject_strict
, &
nwcs
, &
wcs
);
192
wcsRaiseOnParseError
(
ret
);
193
wcsReportWarnings
(
wcsprintf_buf
());
194
195
// Do a second pass, in relaxed mode. We use the result.
196
ret
=
wcspih
(
headers
,
number_of_records
,
WCSHDR_all
, 0, &
nreject
, &
nwcs
, &
wcs
);
197
wcsRaiseOnParseError
(
ret
);
198
wcsset
(
wcs
);
199
200
// There are some things worth reporting about which WCS will not necessarily complain
201
wcsCheckHeaders
(
wcs
,
headers
,
number_of_records
);
202
203
m_wcs
=
decltype
(
m_wcs
)(
wcs
, [
nwcs
](
wcsprm
* ptr) {
204
int
nwcs_copy
=
nwcs
;
205
wcsfree
(ptr);
206
wcsvfree
(&
nwcs_copy
, &ptr);
207
});
208
209
int
wcsver
[3];
210
wcslib_version
(
wcsver
);
211
if
(
wcsver
[0] < 5 || (
wcsver
[0] == 5 &&
wcsver
[1] < 18)) {
212
logger
.
info
() <<
"wcslib "
<<
wcsver
[0] <<
"."
<<
wcsver
[1]
213
<<
" is not fully thread safe, using wrapped lincpy call!"
;
214
safe_wcssub
= &
wrapped_wcssub
;
215
}
216
}
217
218
219
WCS::~WCS
() {
220
}
221
222
WorldCoordinate
WCS::imageToWorld
(
ImageCoordinate
image_coordinate
)
const
{
223
// wcsprm is in/out
224
wcsprm
wcs_copy
;
225
wcs_copy
.flag = -1;
226
safe_wcssub
(
true
,
m_wcs
.get(),
nullptr
,
nullptr
, &
wcs_copy
);
227
228
// +1 as fits standard coordinates start at 1
229
double
pc_array
[2] {
image_coordinate
.m_x + 1,
image_coordinate
.m_y + 1};
230
231
double
ic_array
[2] {0, 0};
232
double
wc_array
[2] {0, 0};
233
double
phi
,
theta
;
234
235
int
status
= 0;
236
int
ret_val
=
wcsp2s
(&
wcs_copy
, 1, 1,
pc_array
,
ic_array
, &
phi
, &
theta
,
wc_array
, &
status
);
237
wcsRaiseOnTransformError
(&
wcs_copy
,
ret_val
);
238
wcsfree
(&
wcs_copy
);
239
240
return
WorldCoordinate
(
wc_array
[0],
wc_array
[1]);
241
}
242
243
ImageCoordinate
WCS::worldToImage
(
WorldCoordinate
world_coordinate
)
const
{
244
// wcsprm is in/out
245
wcsprm
wcs_copy
;
246
wcs_copy
.flag = -1;
247
safe_wcssub
(
true
,
m_wcs
.get(),
nullptr
,
nullptr
, &
wcs_copy
);
248
249
double
pc_array
[2] {0, 0};
250
double
ic_array
[2] {0, 0};
251
double
wc_array
[2] {
world_coordinate
.m_alpha,
world_coordinate
.m_delta};
252
double
phi
,
theta
;
253
254
int
status
= 0;
255
int
ret_val
=
wcss2p
(&
wcs_copy
, 1, 1,
wc_array
, &
phi
, &
theta
,
ic_array
,
pc_array
, &
status
);
256
if
(
ret_val
!=
WCSERR_SUCCESS
) {
257
logger
.
warn
() <<
"Bad worldToImage from RA/Dec: "
<<
wc_array
[0] <<
"/"
<<
wc_array
[1];
258
pc_array
[0] = -
std::numeric_limits<double>::infinity
();
259
pc_array
[1] = -
std::numeric_limits<double>::infinity
();
260
}
261
wcsfree
(&
wcs_copy
);
262
return
ImageCoordinate
(
pc_array
[0] - 1,
pc_array
[1] - 1);
// -1 as fits standard coordinates start at 1
263
}
264
265
std::map<std::string, std::string>
WCS::getFitsHeaders
()
const
{
266
int
nkeyrec
;
267
char
*
raw_header
;
268
269
if
(
wcshdo
(
WCSHDO_none
,
m_wcs
.get(), &
nkeyrec
, &
raw_header
) != 0) {
270
throw
Elements::Exception
() <<
"Failed to get the FITS headers for the WCS coordinate system"
;
271
}
272
273
std::map<std::string, std::string>
headers
;
274
for
(
int
i
= 0;
i
<
nkeyrec
; ++
i
) {
275
char
*
hptr
= &
raw_header
[80 *
i
];
276
std::string
key
(
hptr
,
hptr
+ 8);
277
boost::trim(
key
);
278
std::string
value(
hptr
+ 9,
hptr
+ 72);
279
boost::trim(value);
280
if
(!
key
.empty()) {
281
headers
.emplace(
std::make_pair
(
key
, value));
282
}
283
}
284
285
free
(
raw_header
);
286
return
headers
;
287
}
288
289
void
WCS::addOffset
(
PixelCoordinate
pc) {
290
m_wcs
->crpix[0] -= pc.m_x;
291
m_wcs
->crpix[1] -= pc.m_y;
292
}
293
294
295
}
WCS.h
std::string
Elements::Exception
Elements::Logging::getLogger
static Logging getLogger(const std::string &name="")
Elements::Logging::warn
void warn(const std::string &logMessage)
Elements::Logging::info
void info(const std::string &logMessage)
SourceXtractor::FitsImageSource
Definition
FitsImageSource.h:46
SourceXtractor::InvalidCoordinatesException
Definition
CoordinateSystem.h:62
SourceXtractor::WCS
Definition
WCS.h:37
SourceXtractor::WCS::WCS
WCS(const FitsImageSource &fits_image_source)
Definition
WCS.cpp:155
SourceXtractor::WCS::addOffset
void addOffset(PixelCoordinate pc)
Definition
WCS.cpp:289
SourceXtractor::WCS::init
void init(char *headers, int number_of_records)
Definition
WCS.cpp:180
SourceXtractor::WCS::getFitsHeaders
std::map< std::string, std::string > getFitsHeaders() const override
Definition
WCS.cpp:265
SourceXtractor::WCS::imageToWorld
WorldCoordinate imageToWorld(ImageCoordinate image_coordinate) const override
Definition
WCS.cpp:222
SourceXtractor::WCS::m_wcs
std::unique_ptr< wcsprm, std::function< void(wcsprm *)> m_wcs)
Definition
WCS.h:54
SourceXtractor::WCS::worldToImage
ImageCoordinate worldToImage(WorldCoordinate world_coordinate) const override
Definition
WCS.cpp:243
SourceXtractor::WCS::~WCS
virtual ~WCS()
Definition
WCS.cpp:219
std::free
T free(T... args)
std::function
std::numeric_limits::infinity
T infinity(T... args)
std::lock
T lock(T... args)
std::make_pair
T make_pair(T... args)
std::mutex
Euclid::Configuration::logger
static Elements::Logging logger
SourceXtractor
Definition
Aperture.h:30
SourceXtractor::wcsRaiseOnTransformError
static void wcsRaiseOnTransformError(wcsprm *wcs, int ret_code)
Definition
WCS.cpp:72
SourceXtractor::wrapped_wcssub
static int wrapped_wcssub(int alloc, const struct wcsprm *wcssrc, int *nsub, int axes[], struct wcsprm *wcsdst)
Definition
WCS.cpp:148
SourceXtractor::wcsLogErr
static void wcsLogErr(wcserr *err)
Definition
WCS.cpp:62
SourceXtractor::wcsCheckHeaders
static void wcsCheckHeaders(const wcsprm *wcs, const char *headers_str, int number_of_records)
Definition
WCS.cpp:101
SourceXtractor::safe_wcssub
decltype(&wcssub) safe_wcssub
Definition
WCS.cpp:43
SourceXtractor::wcsRaiseOnParseError
static void wcsRaiseOnParseError(int ret_code)
Definition
WCS.cpp:48
SourceXtractor::wcsExtractKeywords
static std::set< std::string > wcsExtractKeywords(const char *header, int number_of_records)
Definition
WCS.cpp:89
SourceXtractor::wcsReportWarnings
static void wcsReportWarnings(const char *err_buffer)
Definition
WCS.cpp:125
std::strchr
T strchr(T... args)
std::strcspn
T strcspn(T... args)
std::strlen
T strlen(T... args)
std::strncmp
T strncmp(T... args)
SourceXtractor::ImageCoordinate
Definition
CoordinateSystem.h:43
SourceXtractor::PixelCoordinate
A pixel coordinate made of two integers m_x and m_y.
Definition
PixelCoordinate.h:37
SourceXtractor::WorldCoordinate
Definition
CoordinateSystem.h:34
Generated by
1.10.0