SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
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 /*
77  if (wcs->lin.dispre) {
78  wcsLogErr(wcs->lin.dispre->err);
79  }
80  if (wcs->lin.disseq) {
81  wcsLogErr(wcs->lin.disseq->err);
82  }
83 */
84  throw InvalidCoordinatesException() << wcs_errmsg[ret_code];
85  }
86 }
87 
91 static std::set<std::string> wcsExtractKeywords(const char *header, int number_of_records) {
92  std::set<std::string> result;
93  for (const char *p = header; *p != '\0' && number_of_records; --number_of_records, p += 80) {
94  size_t len = strcspn(p, "= ");
95  result.insert(std::string(p, len));
96  }
97  return result;
98 }
99 
103 static void wcsCheckHeaders(const wcsprm *wcs, const char *headers_str, int number_of_records) {
104 /*
105  auto headers = wcsExtractKeywords(headers_str, number_of_records);
106 
107  // SIP present, but not specified in CTYPE
108  // See https://github.com/astrorama/SourceXtractorPlusPlus/issues/254#issuecomment-765235869
109  if (wcs->lin.dispre) {
110  bool sip_used = false, sip_specified = false;
111  for (int i = 0; i < wcs->naxis; ++i) {
112  sip_used |= (strncmp(wcs->lin.dispre->dtype[i], "SIP", 3) == 0);
113  size_t ctype_len = strlen(wcs->ctype[i]);
114  sip_specified |= (strncmp(wcs->ctype[i] + ctype_len - 4, "-SIP", 4) == 0);
115  }
116  if (sip_used && !sip_specified) {
117  logger.warn() << "SIP coefficients present, but CTYPE has not the '-SIP' suffix";
118  logger.warn() << "SIP distortion will be applied, but this may not be desired";
119  logger.warn() << "To suppress this warning, explicitly add the '-SIP' suffix to the CTYPE,";
120  logger.warn() << "or remove the SIP distortion coefficients";
121  }
122  }
123 */
124 }
125 
129 static void wcsReportWarnings(const char *err_buffer) {
130  if (err_buffer[0]) {
131  logger.warn() << "WCS generated some errors in strict mode. This may be OK.";
132  logger.warn() << "Will run in relaxed mode.";
133  const char *eol;
134  do {
135  eol = strchr(err_buffer, '\n');
136  if (eol) {
137  logger.warn() << std::string(err_buffer, eol - err_buffer);
138  err_buffer = eol + 1;
139  }
140  else {
141  logger.warn() << std::string(err_buffer);
142  }
143  } while (eol);
144  }
145 }
146 
152 static int wrapped_wcssub(int alloc, const struct wcsprm* wcssrc, int* nsub, int axes[], struct wcsprm* wcsdst) {
153  static std::mutex cpy_mutex;
155 
156  return wcssub(alloc, wcssrc, nsub, axes, wcsdst);
157 }
158 
159 WCS::WCS(const FitsImageSource& fits_image_source) : m_wcs(nullptr, nullptr) {
160  int number_of_records = 0;
161  auto fits_headers = fits_image_source.getFitsHeaders(number_of_records);
162 
163  init(&(*fits_headers)[0], number_of_records);
164 }
165 
166 WCS::WCS(const WCS& original) : m_wcs(nullptr, nullptr) {
167 
168  //FIXME Horrible hack: I couldn't figure out how to properly do a deep copy wcsprm so instead
169  // of making a copy, I use the ascii headers output from the original to recreate a new one
170 
171  int number_of_records;
172  char *raw_header;
173 
174  if (wcshdo(WCSHDO_none, original.m_wcs.get(), &number_of_records, &raw_header) != 0) {
175  throw Elements::Exception() << "Failed to get the FITS headers for the WCS coordinate system when copying WCS";
176  }
177 
178  init(raw_header, number_of_records);
179 
180  free(raw_header);
181 }
182 
183 
184 void WCS::init(char* headers, int number_of_records) {
185  wcserr_enable(1);
186 
187  int nreject = 0, nwcs = 0, nreject_strict = 0;
188  wcsprm* wcs;
189 
190  // Write warnings to a buffer
191  wcsprintf_set(nullptr);
192 
193 /*
194  // Do a first pass, in strict mode, and ignore the result.
195  // Log the reported errors as warnings
196  int ret = wcspih(headers, number_of_records, WCSHDR_strict, 2, &nreject_strict, &nwcs, &wcs);
197  wcsRaiseOnParseError(ret);
198  wcsReportWarnings(wcsprintf_buf());
199 */
200 
201  // Do a second pass, in relaxed mode. We use the result.
202  int ret = wcspih(headers, number_of_records, WCSHDR_all, 0, &nreject, &nwcs, &wcs);
204  wcsset(wcs);
205 
206  // There are some things worth reporting about which WCS will not necessarily complain
207  wcsCheckHeaders(wcs, headers, number_of_records);
208 
209  m_wcs = decltype(m_wcs)(wcs, [nwcs](wcsprm* ptr) {
210  int nwcs_copy = nwcs;
211  wcsfree(ptr);
212  wcsvfree(&nwcs_copy, &ptr);
213  });
214 /*
215  int wcsver[3];
216  wcslib_version(wcsver);
217  if (wcsver[0] < 5 || (wcsver[0] == 5 && wcsver[1] < 18)) {
218  logger.info() << "wcslib " << wcsver[0] << "." << wcsver[1]
219  << " is not fully thread safe, using wrapped lincpy call!";
220  safe_wcssub = &wrapped_wcssub;
221  }
222 */
224 }
225 
226 
228 }
229 
231  // wcsprm is in/out
232  wcsprm wcs_copy;
233  wcs_copy.flag = -1;
234  safe_wcssub(true, m_wcs.get(), nullptr, nullptr, &wcs_copy);
235 
236  // +1 as fits standard coordinates start at 1
237  double pc_array[2] {image_coordinate.m_x + 1, image_coordinate.m_y + 1};
238 
239  double ic_array[2] {0, 0};
240  double wc_array[2] {0, 0};
241  double phi, theta;
242 
243  int status = 0;
244  int ret_val = wcsp2s(&wcs_copy, 1, 1, pc_array, ic_array, &phi, &theta, wc_array, &status);
245  wcsRaiseOnTransformError(&wcs_copy, ret_val);
246  wcsfree(&wcs_copy);
247 
248  return WorldCoordinate(wc_array[0], wc_array[1]);
249 }
250 
252  // wcsprm is in/out
253  wcsprm wcs_copy;
254  wcs_copy.flag = -1;
255  safe_wcssub(true, m_wcs.get(), nullptr, nullptr, &wcs_copy);
256 
257  double pc_array[2] {0, 0};
258  double ic_array[2] {0, 0};
259  double wc_array[2] {world_coordinate.m_alpha, world_coordinate.m_delta};
260  double phi, theta;
261 
262  int status = 0;
263  int ret_val = wcss2p(&wcs_copy, 1, 1, wc_array, &phi, &theta, ic_array, pc_array, &status);
264  wcsRaiseOnTransformError(&wcs_copy, ret_val);
265  wcsfree(&wcs_copy);
266 
267  return ImageCoordinate(pc_array[0] - 1, pc_array[1] - 1); // -1 as fits standard coordinates start at 1
268 }
269 
271  int nkeyrec;
272  char *raw_header;
273 
274  if (wcshdo(WCSHDO_none, m_wcs.get(), &nkeyrec, &raw_header) != 0) {
275  throw Elements::Exception() << "Failed to get the FITS headers for the WCS coordinate system";
276  }
277 
279  for (int i = 0; i < nkeyrec; ++i) {
280  char *hptr = &raw_header[80 * i];
281  std::string key(hptr, hptr + 8);
282  boost::trim(key);
283  std::string value(hptr + 9, hptr + 72);
284  boost::trim(value);
285  if (!key.empty()) {
286  headers.emplace(std::make_pair(key, value));
287  }
288  }
289 
290  free(raw_header);
291  return headers;
292 }
293 
295  m_wcs->crpix[0] -= pc.m_x;
296  m_wcs->crpix[1] -= pc.m_y;
297 }
298 
299 
300 }
static int wrapped_wcssub(int alloc, const struct wcsprm *wcssrc, int *nsub, int axes[], struct wcsprm *wcsdst)
Definition: WCS.cpp:152
WCS(const FitsImageSource &fits_image_source)
Definition: WCS.cpp:159
T empty(T...args)
static auto logger
Definition: WCS.cpp:41
std::unique_ptr< std::vector< char > > getFitsHeaders(int &number_of_records) const
static void wcsCheckHeaders(const wcsprm *wcs, const char *headers_str, int number_of_records)
Definition: WCS.cpp:103
static void wcsRaiseOnParseError(int ret_code)
Definition: WCS.cpp:48
T free(T...args)
void init(char *headers, int number_of_records)
Definition: WCS.cpp:184
STL class.
WorldCoordinate imageToWorld(ImageCoordinate image_coordinate) const override
Definition: WCS.cpp:230
static std::set< std::string > wcsExtractKeywords(const char *header, int number_of_records)
Definition: WCS.cpp:91
static void wcsRaiseOnTransformError(wcsprm *wcs, int ret_code)
Definition: WCS.cpp:72
T strcspn(T...args)
T lock(T...args)
T make_pair(T...args)
std::map< std::string, std::string > getFitsHeaders() const override
Definition: WCS.cpp:270
A pixel coordinate made of two integers m_x and m_y.
std::unique_ptr< wcsprm, std::function< void(wcsprm *)> > m_wcs
Definition: WCS.h:54
virtual ~WCS()
Definition: WCS.cpp:227
T get(T...args)
void addOffset(PixelCoordinate pc)
Definition: WCS.cpp:294
T insert(T...args)
decltype(&wcssub) safe_wcssub
Definition: WCS.cpp:43
STL class.
static void wcsReportWarnings(const char *err_buffer)
Definition: WCS.cpp:129
static void wcsLogErr(wcserr *err)
Definition: WCS.cpp:62
T emplace(T...args)
ImageCoordinate worldToImage(WorldCoordinate world_coordinate) const override
Definition: WCS.cpp:251
static Logging getLogger(const std::string &name="")
T strchr(T...args)