SourceXtractorPlusPlus
0.19.2
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
wcsRaiseOnTransformError
(&
wcs_copy
,
ret_val
);
257
wcsfree
(&
wcs_copy
);
258
259
return
ImageCoordinate
(
pc_array
[0] - 1,
pc_array
[1] - 1);
// -1 as fits standard coordinates start at 1
260
}
261
262
std::map<std::string, std::string>
WCS::getFitsHeaders
()
const
{
263
int
nkeyrec
;
264
char
*
raw_header
;
265
266
if
(
wcshdo
(
WCSHDO_none
,
m_wcs
.
get
(), &
nkeyrec
, &
raw_header
) != 0) {
267
throw
Elements::Exception
() <<
"Failed to get the FITS headers for the WCS coordinate system"
;
268
}
269
270
std::map<std::string, std::string>
headers
;
271
for
(
int
i
= 0;
i
<
nkeyrec
; ++
i
) {
272
char
*
hptr
= &
raw_header
[80 *
i
];
273
std::string
key
(
hptr
,
hptr
+ 8);
274
boost::trim(
key
);
275
std::string
value(
hptr
+ 9,
hptr
+ 72);
276
boost::trim(value);
277
if
(!
key
.empty()) {
278
headers
.emplace(
std::make_pair
(
key
, value));
279
}
280
}
281
282
free
(
raw_header
);
283
return
headers
;
284
}
285
286
void
WCS::addOffset
(
PixelCoordinate
pc) {
287
m_wcs
->crpix[0] -= pc.m_x;
288
m_wcs
->crpix[1] -= pc.m_y;
289
}
290
291
292
}
Exception.h
Logging.h
WCS.h
std::string
Elements::Exception
Elements::Logging::getLogger
static Logging getLogger(const std::string &name="")
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:286
SourceXtractor::WCS::m_wcs
std::unique_ptr< wcsprm, std::function< void(wcsprm *)> > m_wcs
Definition
WCS.h:54
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:262
SourceXtractor::WCS::imageToWorld
WorldCoordinate imageToWorld(ImageCoordinate image_coordinate) const override
Definition
WCS.cpp:222
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::unique_ptr::get
T get(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.9.8