SourceXtractorPlusPlus 0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
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
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
39namespace SourceXtractor {
40
42
44
49 switch (ret_code) {
51 break;
53 throw Elements::Exception() << "wcslib failed to allocate memory";
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
62static 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
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 }
83 }
84}
85
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
101static void wcsCheckHeaders(const wcsprm *wcs, const char *headers_str, int 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
125static 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) {
134 err_buffer = eol + 1;
135 }
136 else {
137 logger.warn() << std::string(err_buffer);
138 }
139 } while (eol);
140 }
141}
142
148static int wrapped_wcssub(int alloc, const struct wcsprm* wcssrc, int* nsub, int axes[], struct wcsprm* wcsdst) {
149 static std::mutex cpy_mutex;
151
152 return wcssub(alloc, wcssrc, nsub, axes, wcsdst);
153}
154
161
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
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
175
177}
178
179
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
194
195 // Do a second pass, in relaxed mode. We use the result.
198 wcsset(wcs);
199
200 // There are some things worth reporting about which WCS will not necessarily complain
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];
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!";
215 }
216}
217
218
220}
221
223 // wcsprm is in/out
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;
239
240 return WorldCoordinate(wc_array[0], wc_array[1]);
241}
242
244 // wcsprm is in/out
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;
256 if (ret_val != WCSERR_SUCCESS) {
257 logger.warn() << "Bad worldToImage from RA/Dec: " << wc_array[0] << "/" << wc_array[1];
260 }
262 return ImageCoordinate(pc_array[0] - 1, pc_array[1] - 1); // -1 as fits standard coordinates start at 1
263}
264
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
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
286 return headers;
287}
288
290 m_wcs->crpix[0] -= pc.m_x;
291 m_wcs->crpix[1] -= pc.m_y;
292}
293
294
295}
static Logging getLogger(const std::string &name="")
void warn(const std::string &logMessage)
void info(const std::string &logMessage)
WCS(const FitsImageSource &fits_image_source)
Definition WCS.cpp:155
void addOffset(PixelCoordinate pc)
Definition WCS.cpp:289
void init(char *headers, int number_of_records)
Definition WCS.cpp:180
std::map< std::string, std::string > getFitsHeaders() const override
Definition WCS.cpp:265
WorldCoordinate imageToWorld(ImageCoordinate image_coordinate) const override
Definition WCS.cpp:222
std::unique_ptr< wcsprm, std::function< void(wcsprm *)> m_wcs)
Definition WCS.h:54
ImageCoordinate worldToImage(WorldCoordinate world_coordinate) const override
Definition WCS.cpp:243
virtual ~WCS()
Definition WCS.cpp:219
T free(T... args)
T infinity(T... args)
T lock(T... args)
T make_pair(T... args)
static Elements::Logging logger
static void wcsRaiseOnTransformError(wcsprm *wcs, int ret_code)
Definition WCS.cpp:72
static int wrapped_wcssub(int alloc, const struct wcsprm *wcssrc, int *nsub, int axes[], struct wcsprm *wcsdst)
Definition WCS.cpp:148
static void wcsLogErr(wcserr *err)
Definition WCS.cpp:62
static void wcsCheckHeaders(const wcsprm *wcs, const char *headers_str, int number_of_records)
Definition WCS.cpp:101
decltype(&wcssub) safe_wcssub
Definition WCS.cpp:43
static void wcsRaiseOnParseError(int ret_code)
Definition WCS.cpp:48
static std::set< std::string > wcsExtractKeywords(const char *header, int number_of_records)
Definition WCS.cpp:89
static void wcsReportWarnings(const char *err_buffer)
Definition WCS.cpp:125
T strchr(T... args)
T strcspn(T... args)
T strlen(T... args)
T strncmp(T... args)
A pixel coordinate made of two integers m_x and m_y.