lsst.jointcal  14.0-17-ge3cc87b+9
Gtransfo.cc
Go to the documentation of this file.
1 #include <iostream>
2 #include <iomanip>
3 #include <iterator> /* for ostream_iterator */
4 #include <math.h> // for sin and cos and may be others
5 #include <fstream>
6 #include "assert.h"
7 #include <sstream>
8 
9 #include "Eigen/Core"
10 
11 #include "lsst/log/Log.h"
12 #include "lsst/jointcal/Gtransfo.h"
13 #include "lsst/jointcal/Frame.h"
15 #include "lsst/pex/exceptions.h"
16 #include "Eigen/Cholesky"
17 
19 
20 using namespace std;
21 
22 namespace {
23 LOG_LOGGER _log = LOG_GET("jointcal.Gtransfo");
24 }
25 
26 namespace lsst {
27 namespace jointcal {
28 
30  return (dynamic_cast<const GtransfoIdentity *>(gtransfo) != nullptr);
31 }
32 
34  const GtransfoPoly *shift = dynamic_cast<const GtransfoPoly *>(gtransfo);
35  if (shift == nullptr) return false;
36 
37  static const double eps = 1e-5;
38 
39  double dx = shift->coeff(0, 0, 0);
40  double dy = shift->coeff(0, 0, 1);
41 
42  static Point dumb(4000, 4000);
43  if (fabs(dx - int(floor(dx + 0.5))) < eps && fabs(dy - int(floor(dy + 0.5))) < eps &&
44  fabs(dumb.x + dx - shift->apply(dumb).x) < eps && fabs(dumb.y + dy - shift->apply(dumb).y) < eps)
45  return true;
46 
47  return false;
48 }
49 
50 /********* Gtransfo ***********************/
51 
52 std::unique_ptr<Gtransfo> Gtransfo::reduceCompo(const Gtransfo *) const { // by default no way to compose
53  return std::unique_ptr<Gtransfo>(nullptr);
54 }
55 
56 double Gtransfo::getJacobian(const double x, const double y) const {
57  double x2, y2;
58  double eps = x * 0.01;
59  if (eps == 0) eps = 0.01;
60  apply(x, y, x2, y2);
61  double dxdx, dydx;
62  apply(x + eps, y, dxdx, dydx);
63  dxdx -= x2;
64  dydx -= y2;
65  double dxdy, dydy;
66  apply(x, y + eps, dxdy, dydy);
67  dxdy -= x2;
68  dydy -= y2;
69  return ((dxdx * dydy - dxdy * dydx) / (eps * eps));
70 }
71 
75 void Gtransfo::computeDerivative(const Point &where, GtransfoLin &derivative, const double step) const {
76  double x = where.x;
77  double y = where.y;
78  double xp0, yp0;
79  apply(x, y, xp0, yp0);
80 
81  double xp, yp;
82  apply(x + step, y, xp, yp);
83  derivative.a11() = (xp - xp0) / step;
84  derivative.a21() = (yp - yp0) / step;
85  apply(x, y + step, xp, yp);
86  derivative.a12() = (xp - xp0) / step;
87  derivative.a22() = (yp - yp0) / step;
88  derivative.dx() = 0;
89  derivative.dy() = 0;
90 }
91 
92 GtransfoLin Gtransfo::linearApproximation(const Point &where, const double step) const {
93  Point outwhere = apply(where);
94  GtransfoLin der;
95  computeDerivative(where, der, step);
96  return GtransfoLinShift(outwhere.x, outwhere.y) * der * GtransfoLinShift(-where.x, -where.y);
97 }
98 
99 void Gtransfo::transformPosAndErrors(const FatPoint &in, FatPoint &out) const {
100  FatPoint res; // in case in and out are the same address...
101  res = apply(in);
102  GtransfoLin der;
103  // could save a call here, since Derivative needs the transform of where that we already have
104  // 0.01 may not be a very good idea in all cases. May be we should provide a way of altering that.
105  computeDerivative(in, der, 0.01);
106  double a11 = der.A11();
107  double a22 = der.A22();
108  double a21 = der.A21();
109  double a12 = der.A12();
110  res.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
111  res.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
112  res.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
113  out = res;
114 }
115 
116 void Gtransfo::transformErrors(const Point &where, const double *vIn, double *vOut) const {
117  GtransfoLin der;
118  computeDerivative(where, der, 0.01);
119  double a11 = der.A11();
120  double a22 = der.A22();
121  double a21 = der.A21();
122  double a12 = der.A12();
123 
124  /* (a11 a12) (vxx vxy)
125  M = ( ) and V = ( )
126  (a21 a22) (xvy vyy)
127 
128  Vxx = Vin[0], vyy = Vin[1], Vxy = Vin[2];
129  we want to compute M*V*tp(M)
130  A lin alg light package would be perfect...
131  */
132  int xx = 0;
133  int yy = 1;
134  int xy = 2;
135  // M*V :
136 
137  double b11 = a11 * vIn[xx] + a12 * vIn[xy];
138  double b22 = a21 * vIn[xy] + a22 * vIn[yy];
139  double b12 = a11 * vIn[xy] + a12 * vIn[yy];
140  double b21 = a21 * vIn[xx] + a22 * vIn[xy];
141 
142  // (M*V) * tp(M)
143 
144  vOut[xx] = b11 * a11 + b12 * a12;
145  vOut[xy] = b11 * a21 + b12 * a22;
146  vOut[yy] = b21 * a21 + b22 * a22;
147 }
148 
149 std::unique_ptr<Gtransfo> Gtransfo::roughInverse(const Frame &region) const {
150  // "in" and "out" refer to the inverse direction.
151  Point centerOut = region.getCenter();
152  Point centerIn = apply(centerOut);
153  GtransfoLin der;
154  computeDerivative(centerOut, der, sqrt(region.getArea()) / 5.);
155  der = der.invert();
156  der = GtransfoLinShift(centerOut.x, centerOut.y) * der * GtransfoLinShift(-centerIn.x, -centerIn.y);
157  return std::unique_ptr<Gtransfo>(new GtransfoLin(der));
158 }
159 
160 /* implement one in Gtransfo, so that all derived
161  classes do not need to provide one... */
162 
163 /* the routines that follow are used for ea generic parameter
164  transformation serialization, used e.g. for fits. Enables
165  to manipulate transformation parameters as vectors.
166 */
167 
168 // not dummy : what it does is virtual because paramRef is virtual.
169 void Gtransfo::getParams(double *params) const {
170  int npar = getNpar();
171  for (int i = 0; i < npar; ++i) params[i] = paramRef(i);
172 }
173 
174 void Gtransfo::offsetParams(Eigen::VectorXd const &delta) {
175  int npar = getNpar();
176  for (int i = 0; i < npar; ++i) paramRef(i) += delta[i];
177 }
178 
179 double Gtransfo::paramRef(const int) const {
181  std::string("Gtransfo::paramRef should never be called "));
182 }
183 
184 double &Gtransfo::paramRef(const int) {
185  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Gtransfo::paramRef should never be called ");
186 }
187 
188 void Gtransfo::paramDerivatives(const Point &, double *, double *) const {
190  "Gtransfo::paramDerivatives() should never be called ");
191 }
192 
194  gtransfo.dump(stream);
195  return stream;
196 }
197 
198 void Gtransfo::write(const std::string &fileName) const {
199  ofstream s(fileName.c_str());
200  write(s);
201  bool ok = !s.fail();
202  s.close();
203  if (!ok)
205  "Gtransfo::write, something went wrong for file " + fileName);
206 }
207 
208 void Gtransfo::write(ostream &stream) const {
210  "Gtransfo::write(ostream), should never be called. MEans that it is missing in some "
211  "derived class ");
212 }
213 
214 /******************* GTransfoInverse ****************/
215 /* inverse transformation, solved by iterations. Before using
216  it (probably via Gtransfo::inverseTransfo), consider
217  seriously StarMatchList::inverseTransfo */
218 class GtransfoInverse : public Gtransfo {
219 private:
221  std::unique_ptr<Gtransfo> _roughInverse;
222  double precision2;
223 
224 public:
225  GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region);
226 
229  void apply(const double xIn, const double yIn, double &xOut, double &yOut) const;
230 
231  void dump(ostream &stream) const;
232 
233  double fit(const StarMatchList &starMatchList);
234 
235  virtual std::unique_ptr<Gtransfo> clone() const;
236 
238 
240  std::unique_ptr<Gtransfo> roughInverse(const Frame &) const { return _direct->clone(); }
241 
243  std::unique_ptr<Gtransfo> inverseTransfo(double, const Frame &) const { return _direct->clone(); }
244 
245  ~GtransfoInverse();
246 
247 private:
248  void operator=(const GtransfoInverse &);
249 };
250 
251 std::unique_ptr<Gtransfo> Gtransfo::inverseTransfo(const double precision, const Frame &region) const {
252  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
253 }
254 
255 GtransfoInverse::GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region) {
256  _direct = direct->clone();
257  _roughInverse = _direct->roughInverse(region);
258  precision2 = precision * precision;
259 }
260 
261 GtransfoInverse::GtransfoInverse(const GtransfoInverse &model) : Gtransfo() {
262  _direct = model._direct->clone();
263  _roughInverse = model._roughInverse->clone();
264  precision2 = model.precision2;
265 }
266 
268 
269 void GtransfoInverse::operator=(const GtransfoInverse &model) {
270  _direct = model._direct->clone();
271  _roughInverse = model._roughInverse->clone();
272  precision2 = model.precision2;
273 }
274 
275 void GtransfoInverse::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
276  Point in(xIn, yIn);
277  Point outGuess = _roughInverse->apply(in);
278  GtransfoLin directDer, reverseDer;
279  int loop = 0;
280  int maxloop = 20;
281  double move2;
282  do {
283  loop++;
284  Point inGuess = _direct->apply(outGuess);
285  _direct->computeDerivative(outGuess, directDer);
286  reverseDer = directDer.invert();
287  double xShift, yShift;
288  reverseDer.apply(xIn - inGuess.x, yIn - inGuess.y, xShift, yShift);
289  outGuess.x += xShift;
290  outGuess.y += yShift;
291  move2 = xShift * xShift + yShift * yShift;
292  } while ((move2 > precision2) && (loop < maxloop));
293  if (loop == maxloop) LOGLS_WARN(_log, "Problems applying GtransfoInverse at " << in);
294  xOut = outGuess.x;
295  yOut = outGuess.y;
296 }
297 
298 void GtransfoInverse::dump(ostream &stream) const {
299  stream << " GtransfoInverse of :" << endl << *_direct << endl;
300 }
301 
303  throw pexExcept::RuntimeError("Cannot fit a GtransfoInverse. Use StarMatchList::inverseTransfo instead.");
304 }
305 
307  return std::unique_ptr<Gtransfo>(new GtransfoInverse(*this));
308 }
309 
310 /************* GtransfoComposition **************/
311 
312 // This class was done to allow composition of Gtransfo's, without specifications of their types.
313 // does not need to be public. Invoked by gtransfoCompose(left,right)
314 
318 private:
319  std::unique_ptr<Gtransfo> _first, _second;
320 
321 public:
324 
326  void apply(const double xIn, const double yIn, double &xOut, double &yOut) const;
327  void dump(ostream &stream = cout) const;
328 
330  double fit(const StarMatchList &starMatchList);
331 
334 };
335 
337  _first = first->clone();
338  _second = second->clone();
339 }
340 
341 void GtransfoComposition::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
342  double xout, yout;
343  _first->apply(xIn, yIn, xout, yout);
344  _second->apply(xout, yout, xOut, yOut);
345 }
346 
347 void GtransfoComposition::dump(ostream &stream) const {
348  _first->dump(stream);
349  _second->dump(stream);
350 }
351 
352 double GtransfoComposition::fit(const StarMatchList &starMatchList) {
353  /* fits only one of them. could check that first can actually be fitted... */
354  return _first->fit(starMatchList);
355 }
356 
358  return std::unique_ptr<Gtransfo>(new GtransfoComposition(_second.get(), _first.get()));
359 }
360 
362 
368  /* is right Identity ? if left is Identity , GtransfoIdentity::reduceCompo does the right job */
369  if (isIdentity(right)) {
370  return left->clone();
371  }
372  /* Try to use the reduceCompo method from left. If absent,
373  Gtransfo::reduceCompo return NULL. reduceCompo is non trivial for
374  polynomials */
375  std::unique_ptr<Gtransfo> composition(left->reduceCompo(right));
376  /* composition == NULL means no reduction : just build a Composition
377  that pipelines "left" and "right" */
378  if (composition == nullptr)
379  return std::unique_ptr<Gtransfo>(new GtransfoComposition(left, right));
380  else
381  return composition;
382 }
383 
384 // just a speed up, to avoid useless numerical derivation.
385 void GtransfoIdentity::computeDerivative(const Point &, GtransfoLin &derivative, const double) const {
386  derivative = GtransfoLin();
387 }
388 
390  GtransfoLin result;
391  return result; // rely on default Gtransfolin constructor;
392 }
393 
394 void GtransfoIdentity::write(ostream &stream) const { stream << "GtransfoIdentity 1" << endl; }
395 
397  int format;
398  stream >> format;
399  if (format != 1)
401  " GtransfoIdentity::read : format is not 1 ");
402 }
403 
404 /*************** GtransfoPoly **************************************/
405 
407 
408 GtransfoPoly::GtransfoPoly(const unsigned degree) : _degree(degree) {
409  _nterms = (degree + 1) * (degree + 2) / 2;
410 
411  // allocate and fill coefficients
412  _coeffs.resize(2 * _nterms, 0.);
413  // the default is supposed to be the identity, (for degree>=1).
414  if (_degree >= 1) {
415  coeff(1, 0, 0) = 1;
416  coeff(0, 1, 1) = 1;
417  }
418 }
419 
420 //#ifdef TO_BE_FIXED
421 GtransfoPoly::GtransfoPoly(const Gtransfo *gtransfo, const Frame &frame, unsigned degree, unsigned nPoint) {
422  StarMatchList sm;
423 
424  double step = sqrt(fabs(frame.getArea()) / double(nPoint));
425  for (double x = frame.xMin + step / 2; x <= frame.xMax; x += step)
426  for (double y = frame.yMin + step / 2; y <= frame.yMax; y += step) {
427  auto pix = std::make_shared<BaseStar>(x, y, 0, 0);
428  double xtr, ytr;
429  gtransfo->apply(x, y, xtr, ytr);
430  auto tp = std::make_shared<BaseStar>(xtr, ytr, 0, 0);
431  /* These are fake stars so no need to transform fake errors.
432  all errors (and weights) will be equal : */
433  sm.push_back(StarMatch(*pix, *tp, pix, tp));
434  }
435  GtransfoPoly ret(degree);
436  ret.fit(sm);
437  *this = ret;
438 }
439 //#endif
440 
441 void GtransfoPoly::computeMonomials(double xIn, double yIn, double *monomial) const {
442  /* The ordering of monomials is implemented here.
443  You may not change it without updating the "mapping" routines
444  coeff(unsigned, unsigned, unsigned).
445  I (P.A.) did not find a clever way to loop over monomials.
446  Improvements welcome.
447  This routine is used also by the fit to fill monomials.
448  We could certainly be more elegant.
449  */
450 
451  double xx = 1;
452  for (unsigned ix = 0; ix <= _degree; ++ix) {
453  double yy = 1;
454  unsigned k = ix * (ix + 1) / 2;
455  for (unsigned iy = 0; iy <= _degree - ix; ++iy) {
456  monomial[k] = xx * yy;
457  yy *= yIn;
458  k += ix + iy + 2;
459  }
460  xx *= xIn;
461  }
462 }
463 
464 void GtransfoPoly::setDegree(const unsigned degree) {
465  _degree = degree;
466  unsigned old_nterms = _nterms;
467  _nterms = (_degree + 1) * (_degree + 2) / 2;
468 
469  // temporarily save coefficients
470  vector<double> old_coeffs = _coeffs;
471  // reallocate enough size
472  _coeffs.resize(2 * _nterms);
473  // reassign to zero (this is necessary because ycoeffs
474  // are after xcoeffs and so their meaning changes
475  for (unsigned k = 0; k < _nterms; ++k) _coeffs[k] = 0;
476  // put back what we had before
477  unsigned kmax = min(old_nterms, _nterms);
478  for (unsigned k = 0; k < kmax; ++k) {
479  _coeffs[k] = old_coeffs[k]; // x terms
480  _coeffs[k + _nterms] = old_coeffs[k + old_nterms]; // y terms
481  }
482 }
483 
484 /* this is reasonably fast, when optimized */
485 void GtransfoPoly::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
486  /*
487  This routine computes the monomials only once for both
488  polynomials. This is why GtransfoPoly does not use an auxilary
489  class (such as PolyXY) to handle each polynomial.
490 
491  The code works even if &xIn == &xOut (or &yIn == &yOut)
492  It uses Variable Length Allocation (VLA) rather than a vector<double>
493  because allocating the later costs about 50 ns. All VLA uses are tagged.
494  */
495  double monomials[_nterms]; // this is VLA, which is (perhaps) not casher C++
496  computeMonomials(xIn, yIn, monomials);
497 
498  xOut = 0;
499  yOut = 0;
500  const double *c = &_coeffs[0];
501  const double *pm = &monomials[0];
502  // the ordering of the coefficients and the monomials are identical.
503  for (int k = _nterms; k--;) xOut += (*(pm++)) * (*(c++));
504  pm = &monomials[0];
505  for (int k = _nterms; k--;) yOut += (*(pm++)) * (*(c++));
506 }
507 
508 void GtransfoPoly::computeDerivative(const Point &where, GtransfoLin &derivative, const double step)
509  const { /* routine checked against numerical derivatives from Gtransfo::Derivative */
510  if (_degree == 1) {
511  derivative = GtransfoLin(*this);
512  derivative.dx() = derivative.dy() = 0;
513  return;
514  }
515 
516  double dermx[2 * _nterms]; // VLA
517  double *dermy = dermx + _nterms;
518  double xin = where.x;
519  double yin = where.y;
520 
521  double xx = 1;
522  double xxm1 = 1; // xx^(ix-1)
523  for (unsigned ix = 0; ix <= _degree; ++ix) {
524  unsigned k = (ix) * (ix + 1) / 2;
525  // iy = 0
526  dermx[k] = ix * xxm1;
527  dermy[k] = 0;
528  k += ix + 2;
529  double yym1 = 1; // yy^(iy-1)
530  for (unsigned iy = 1; iy <= _degree - ix; ++iy) {
531  dermx[k] = ix * xxm1 * yym1 * yin;
532  dermy[k] = iy * xx * yym1;
533  yym1 *= yin;
534  k += ix + iy + 2;
535  }
536  xx *= xin;
537  if (ix >= 1) xxm1 *= xin;
538  }
539 
540  derivative.dx() = 0;
541  derivative.dy() = 0;
542 
543  const double *mx = &dermx[0];
544  const double *my = &dermy[0];
545  const double *c = &_coeffs[0];
546  // dx'
547  double a11 = 0, a12 = 0;
548  for (int k = _nterms; k--;) {
549  a11 += (*(mx++)) * (*c);
550  a12 += (*(my++)) * (*(c++));
551  }
552  derivative.a11() = a11;
553  derivative.a12() = a12;
554  // dy'
555  double a21 = 0, a22 = 0;
556  mx = &dermx[0];
557  my = &dermy[0];
558  for (int k = _nterms; k--;) {
559  a21 += (*(mx++)) * (*c);
560  a22 += (*(my++)) * (*(c++));
561  }
562  derivative.a21() = a21;
563  derivative.a22() = a22;
564 }
565 
567  /*
568  The results from this routine were compared to what comes out
569  from apply and transformErrors. The Derivative routine was
570  checked against numerical derivatives from
571  Gtransfo::Derivative. (P.A dec 2009).
572 
573  This routine could be made much simpler by calling apply and
574  Derivative (i.e. you just suppress it, and the fallback is the
575  generic version in Gtransfo). BTW, I checked that both routines
576  provide the same result. This version is however faster
577  (monomials get recycled).
578  */
579  double monomials[_nterms]; // VLA
580 
581  FatPoint res; // to store the result, because nothing forbids &in == &out.
582 
583  double dermx[2 * _nterms]; // monomials for derivative w.r.t. x (VLA)
584  double *dermy = dermx + _nterms; // same for y
585  double xin = in.x;
586  double yin = in.y;
587 
588  double xx = 1;
589  double xxm1 = 1; // xx^(ix-1)
590  for (unsigned ix = 0; ix <= _degree; ++ix) {
591  unsigned k = (ix) * (ix + 1) / 2;
592  // iy = 0
593  dermx[k] = ix * xxm1;
594  dermy[k] = 0;
595  monomials[k] = xx;
596  k += ix + 2;
597  double yy = yin;
598  double yym1 = 1; // yy^(iy-1)
599  for (unsigned iy = 1; iy <= _degree - ix; ++iy) {
600  monomials[k] = xx * yy;
601  dermx[k] = ix * xxm1 * yy;
602  dermy[k] = iy * xx * yym1;
603  yym1 *= yin;
604  yy *= yin;
605  k += ix + iy + 2;
606  }
607  xx *= xin;
608  if (ix >= 1) xxm1 *= xin;
609  }
610 
611  // output position
612  double xout = 0, yout = 0;
613  const double *c = &_coeffs[0];
614  const double *pm = &monomials[0];
615  for (int k = _nterms; k--;) xout += (*(pm++)) * (*(c++));
616  pm = &monomials[0];
617  for (int k = _nterms; k--;) yout += (*(pm++)) * (*(c++));
618  res.x = xout;
619  res.y = yout;
620 
621  // derivatives
622  c = &_coeffs[0];
623  const double *mx = &dermx[0];
624  const double *my = &dermy[0];
625  double a11 = 0, a12 = 0;
626  for (int k = _nterms; k--;) {
627  a11 += (*(mx++)) * (*c);
628  a12 += (*(my++)) * (*(c++));
629  }
630 
631  double a21 = 0, a22 = 0;
632  mx = &dermx[0];
633  my = &dermy[0];
634  for (int k = _nterms; k--;) {
635  a21 += (*(mx++)) * (*c);
636  a22 += (*(my++)) * (*(c++));
637  }
638 
639  // output co-variance
640  res.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
641  res.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
642  res.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
643  out = res;
644 }
645 
646 /* The coefficient ordering is defined both here *AND* in the
647  GtransfoPoly::apply, GtransfoPoly::Derivative, ... routines
648  Change all or none ! */
649 
650 double GtransfoPoly::coeff(const unsigned degX, const unsigned degY, const unsigned whichCoord) const {
651  assert((degX + degY <= _degree) && whichCoord < 2);
652  /* this assertion above is enough to ensure that the index used just
653  below is within bounds since the reserved length is
654  2*_nterms=(degree+1)*(degree+2) */
655  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
656 }
657 
658 double &GtransfoPoly::coeff(const unsigned degX, const unsigned degY, const unsigned whichCoord) {
659  assert((degX + degY <= _degree) && whichCoord < 2);
660  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
661 }
662 
663 double GtransfoPoly::coeffOrZero(const unsigned degX, const unsigned degY, const unsigned whichCoord) const {
664  // assert((degX+degY<=degree) && whichCoord<2);
665  assert(whichCoord < 2);
666  if (degX + degY <= _degree)
667  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
668  return 0;
669 }
670 
671 /* parameter serialization for "virtual" fits */
672 double GtransfoPoly::paramRef(const int i) const {
673  assert(unsigned(i) < 2 * _nterms);
674  return _coeffs[i];
675 }
676 
677 double &GtransfoPoly::paramRef(const int i) {
678  assert(unsigned(i) < 2 * _nterms);
679  return _coeffs[i];
680 }
681 
682 void GtransfoPoly::paramDerivatives(const Point &where, double *dx, double *dy)
683  const { /* first half : dxout/dpar, second half : dyout/dpar */
684  computeMonomials(where.x, where.y, dx);
685  for (unsigned k = 0; k < _nterms; ++k) {
686  dy[_nterms + k] = dx[k];
687  dx[_nterms + k] = dy[k] = 0;
688  }
689 }
690 
691 /* utility for the dump(ostream&) routine */
692 static string monomialString(const unsigned powX, const unsigned powY) {
693  stringstream ss;
694  if (powX + powY) ss << "*";
695  if (powX > 0) ss << "x";
696  if (powX > 1) ss << "^" << powX;
697  if (powY > 0) ss << "y";
698  if (powY > 1) ss << "^" << powY;
699  return ss.str();
700 }
701 
702 void GtransfoPoly::dump(ostream &stream) const {
703  for (unsigned ic = 0; ic < 2; ++ic) {
704  if (ic == 0)
705  stream << "newx = ";
706  else
707  stream << "newy = ";
708  for (unsigned p = 0; p <= _degree; ++p)
709  for (unsigned py = 0; py <= p; ++py) {
710  if (p + py != 0) stream << " + ";
711  stream << coeff(p - py, py, ic) << monomialString(p - py, py);
712  }
713  stream << endl;
714  }
715  if (_degree > 0) stream << " Linear determinant = " << determinant() << endl;
716 }
717 
719  if (_degree >= 1) return coeff(1, 0, 0) * coeff(0, 1, 1) - coeff(0, 1, 0) * coeff(1, 0, 1);
720  return 0;
721 }
722 
725  Point center = frame.getCenter();
726  return GtransfoLinScale(2. / frame.getWidth(), 2. / frame.getHeight()) *
727  GtransfoLinShift(-center.x, -center.y);
728 }
729 
730 /*utility for the GtransfoPoly::fit() routine */
731 static GtransfoLin shiftAndNormalize(const StarMatchList &starMatchList) {
732  double xav = 0;
733  double x2 = 0;
734  double yav = 0;
735  double y2 = 0;
736  double count = 0;
737  for (auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
738  const StarMatch &a_match = *it;
739  const Point &point1 = a_match.point1;
740  xav += point1.x;
741  yav += point1.y;
742  x2 += std::pow(point1.x, 2);
743  y2 += std::pow(point1.y, 2);
744  count++;
745  }
746  if (count == 0) return GtransfoLin();
747  xav /= count;
748  yav /= count;
749  // 3.5 stands for sqrt(12).
750  double xspan = 3.5 * sqrt(x2 / count - std::pow(xav, 2));
751  double yspan = 3.5 * sqrt(y2 / count - std::pow(yav, 2));
752  return GtransfoLinScale(2. / xspan, 2. / yspan) * GtransfoLinShift(-xav, -yav);
753 }
754 
755 static double sq(double x) { return x * x; }
756 
757 double GtransfoPoly::computeFit(const StarMatchList &starMatchList, const Gtransfo &shiftToCenter,
758  const bool useErrors) {
759  Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
760  A.setZero();
761  Eigen::VectorXd B(2 * _nterms);
762  B.setZero();
763  double sumr2 = 0;
764  double monomials[_nterms];
765  for (auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
766  const StarMatch &a_match = *it;
767  Point tmp = shiftToCenter.apply(a_match.point1);
768  FatPoint point1(tmp, a_match.point1.vx, a_match.point1.vy, a_match.point1.vxy);
769  const FatPoint &point2 = a_match.point2;
770  double wxx, wyy, wxy;
771  FatPoint tr1;
772  computeMonomials(point1.x, point1.y, monomials);
773  if (useErrors) {
774  transformPosAndErrors(point1, tr1); // we might consider recycling the monomials
775  double vxx = (tr1.vx + point2.vx);
776  double vyy = (tr1.vy + point2.vy);
777  double vxy = (tr1.vxy + point2.vxy);
778  double det = vxx * vyy - vxy * vxy;
779  wxx = vyy / det;
780  wyy = vxx / det;
781  wxy = -vxy / det;
782  } else {
783  wxx = wyy = 1;
784  wxy = 0;
785  apply(point1.x, point1.y, tr1.x, tr1.y);
786  }
787  double resx = point2.x - tr1.x;
788  double resy = point2.y - tr1.y;
789  sumr2 += wxx * sq(resx) + wyy * sq(resy) + 2 * wxy * resx * resy;
790 
791  double bxcoeff = wxx * resx + wxy * resy;
792  double bycoeff = wyy * resy + wxy * resx;
793  for (unsigned j = 0; j < _nterms; ++j) {
794  for (unsigned i = j; i < _nterms; ++i) {
795  A(i, j) += wxx * monomials[i] * monomials[j];
796  A(i + _nterms, j + _nterms) += wyy * monomials[i] * monomials[j];
797  A(j, i + _nterms) = A(i, j + _nterms) += wxy * monomials[i] * monomials[j];
798  }
799  B(j) += bxcoeff * monomials[j];
800  B(j + _nterms) += bycoeff * monomials[j];
801  }
802  } // end loop on points
803  Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
804  // should probably throw
805  if (factor.info() != Eigen::Success) {
806  LOGL_ERROR(_log, "GtransfoPoly::fit could not factorize");
807  return -1;
808  }
809 
810  Eigen::VectorXd sol = factor.solve(B);
811  for (unsigned k = 0; k < 2 * _nterms; ++k) _coeffs[k] += sol(k);
812  if (starMatchList.size() == _nterms) return 0;
813  return (sumr2 - B.dot(sol));
814 }
815 
816 double GtransfoPoly::fit(const StarMatchList &starMatchList) {
817  if (starMatchList.size() < _nterms) {
818  LOGLS_FATAL(_log, "GtransfoPoly::fit trying to fit a polynomial transfo of degree "
819  << _degree << " with only " << starMatchList.size() << " matches.");
820  return -1;
821  }
822 
823  GtransfoPoly conditionner = shiftAndNormalize(starMatchList);
824 
825  computeFit(starMatchList, conditionner, false); // get a rough solution
826  computeFit(starMatchList, conditionner, true); // weight with it
827  double chi2 = computeFit(starMatchList, conditionner, true); // once more
828 
829  (*this) = (*this) * conditionner;
830  if (starMatchList.size() == _nterms) return 0;
831  return chi2;
832 }
833 
835  const GtransfoPoly *p = dynamic_cast<const GtransfoPoly *>(right);
836  if (p) {
837  if (getDegree() == 1 && p->getDegree() == 1)
838  return std::unique_ptr<Gtransfo>(new GtransfoLin((*this) * (*p))); // does the composition
839  else
840  return std::unique_ptr<Gtransfo>(new GtransfoPoly((*this) * (*p))); // does the composition
841  } else
842  return std::unique_ptr<Gtransfo>(nullptr);
843 }
844 
845 /* PolyXY the class used to perform polynomial algebra (and in
846  particular composition) at the coefficient level. This class
847  handles a single polynomial, while a GtransfoPoly is a couple of
848  polynomials. This class does not have any routine to evaluate
849  polynomials. Efficiency is not a concern since these routines are
850  seldom used. There is no need to expose this tool class to
851  Gtransfo users.
852 */
853 
854 class PolyXY {
855  unsigned degree;
856  unsigned nterms;
857  vector<long double> coeffs;
858 
859 public:
860  PolyXY(const int degree) : degree(degree), nterms((degree + 1) * (degree + 2) / 2) {
861  coeffs.reserve(nterms);
862  coeffs.insert(coeffs.begin(), nterms, 0L); // fill & initialize to 0.
863  }
864 
865  unsigned getDegree() const { return degree; }
866 
867  PolyXY(const GtransfoPoly &gtransfoPoly, const unsigned whichCoord)
868  : degree(gtransfoPoly.getDegree()), nterms((degree + 1) * (degree + 2) / 2), coeffs(nterms, 0L) {
869  for (unsigned px = 0; px <= degree; ++px)
870  for (unsigned py = 0; py <= degree - px; ++py)
871  coeff(px, py) = gtransfoPoly.coeff(px, py, whichCoord);
872  }
873 
874  long double coeff(const unsigned powX, const unsigned powY) const {
875  assert(powX + powY <= degree);
876  return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
877  }
878 
879  long double &coeff(const unsigned powX, const unsigned powY) {
880  assert(powX + powY <= degree);
881  return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
882  }
883 };
884 
885 /* ===================== PolyXY Algebra routines ================== */
886 
887 static void operator+=(PolyXY &left, const PolyXY &right) {
888  unsigned rdeg = right.getDegree();
889  assert(left.getDegree() >= rdeg);
890  for (unsigned i = 0; i <= rdeg; ++i)
891  for (unsigned j = 0; j <= rdeg - i; ++j) left.coeff(i, j) += right.coeff(i, j);
892 }
893 
894 /* multiplication by a scalar */
895 static PolyXY operator*(const long double &a, const PolyXY &polyXY) {
896  PolyXY result(polyXY);
897  // no direct access to coefficients: do it the soft way
898  unsigned degree = polyXY.getDegree();
899  for (unsigned i = 0; i <= degree; ++i)
900  for (unsigned j = 0; j <= degree - i; ++j) result.coeff(i, j) *= a;
901  return result;
902 }
903 
905 static PolyXY product(const PolyXY &p1, const PolyXY &p2) {
906  unsigned deg1 = p1.getDegree();
907  unsigned deg2 = p2.getDegree();
908  PolyXY result(deg1 + deg2);
909  for (unsigned i1 = 0; i1 <= deg1; ++i1)
910  for (unsigned j1 = 0; j1 <= deg1 - i1; ++j1)
911  for (unsigned i2 = 0; i2 <= deg2; ++i2)
912  for (unsigned j2 = 0; j2 <= deg2 - i2; ++j2)
913  result.coeff(i1 + i2, j1 + j2) += p1.coeff(i1, j1) * p2.coeff(i2, j2);
914  return result;
915 }
916 
917 /* powers[k](x,y) = polyXY(x,y)**k, 0 <= k <= maxP */
918 static void computePowers(const PolyXY &polyXY, const unsigned maxP, vector<PolyXY> &powers) {
919  powers.reserve(maxP + 1);
920  powers.push_back(PolyXY(0));
921  powers[0].coeff(0, 0) = 1L;
922  for (unsigned k = 1; k <= maxP; ++k) powers.push_back(product(powers[k - 1], polyXY));
923 }
924 
926 static PolyXY composition(const PolyXY &polyXY, const PolyXY &polyX, const PolyXY &polyY) {
927  unsigned pdeg = polyXY.getDegree();
928  PolyXY result(pdeg * max(polyX.getDegree(), polyY.getDegree()));
929  vector<PolyXY> pXPowers;
930  vector<PolyXY> pYPowers;
931  computePowers(polyX, pdeg, pXPowers);
932  computePowers(polyY, pdeg, pYPowers);
933  for (unsigned px = 0; px <= pdeg; ++px)
934  for (unsigned py = 0; py <= pdeg - px; ++py)
935  result += polyXY.coeff(px, py) * product(pXPowers.at(px), pYPowers.at(py));
936  return result;
937 }
938 
939 /* ===================== end of PolyXY Algebra routines ============= */
940 
941 /* reducing polynomial composition is the reason for PolyXY stuff : */
942 
944  // split each transfo into 2d polynomials
945  PolyXY plx(*this, 0);
946  PolyXY ply(*this, 1);
947  PolyXY prx(right, 0);
948  PolyXY pry(right, 1);
949 
950  // compute the compositions
951  PolyXY rx(composition(plx, prx, pry));
952  PolyXY ry(composition(ply, prx, pry));
953 
954  // copy the results the hard way.
955  GtransfoPoly result(_degree * right._degree);
956  for (unsigned px = 0; px <= result._degree; ++px)
957  for (unsigned py = 0; py <= result._degree - px; ++py) {
958  result.coeff(px, py, 0) = rx.coeff(px, py);
959  result.coeff(px, py, 1) = ry.coeff(px, py);
960  }
961  return result;
962 }
963 
965  if (_degree >= right._degree) {
966  GtransfoPoly res(*this);
967  for (unsigned i = 0; i <= right._degree; ++i)
968  for (unsigned j = 0; j <= right._degree - i; ++j) {
969  res.coeff(i, j, 0) += right.coeff(i, j, 0);
970  res.coeff(i, j, 1) += right.coeff(i, j, 1);
971  }
972  return res;
973  } else
974  return (right + (*this));
975 }
976 
978  GtransfoPoly res(std::max(_degree, right._degree));
979  for (unsigned i = 0; i <= res._degree; ++i)
980  for (unsigned j = 0; j <= res._degree - i; ++j) {
981  res.coeff(i, j, 0) = coeffOrZero(i, j, 0) - right.coeffOrZero(i, j, 0);
982  res.coeff(i, j, 1) = coeffOrZero(i, j, 1) - right.coeffOrZero(i, j, 1);
983  }
984  return res;
985 }
986 
987 void GtransfoPoly::write(ostream &s) const {
988  s << " GtransfoPoly 1" << endl;
989  s << "degree " << _degree << endl;
990  int oldprec = s.precision();
991  s << setprecision(12);
992  for (unsigned k = 0; k < 2 * _nterms; ++k) s << _coeffs[k] << ' ';
993  s << endl;
994  s << setprecision(oldprec);
995 }
996 
998  int format;
999  s >> format;
1000  if (format != 1)
1001  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, " GtransfoPoly::read : format is not 1 ");
1002 
1003  string degree;
1004  s >> degree >> _degree;
1005  if (degree != "degree")
1007  " GtransfoPoly::read : expecting \"degree\" and found " + degree);
1008  setDegree(_degree);
1009  for (unsigned k = 0; k < 2 * _nterms; ++k) s >> _coeffs[k];
1010 }
1011 
1013  const double precision) {
1014  StarMatchList sm;
1015  unsigned nx = 50;
1016  double stepx = frame.getWidth() / (nx + 1);
1017  unsigned ny = 50;
1018  double stepy = frame.getHeight() / (ny + 1);
1019  for (unsigned i = 0; i < nx; ++i)
1020  for (unsigned j = 0; j < ny; ++j) {
1021  Point in((i + 0.5) * stepx, (j + 0.5) * stepy);
1022  Point out(direct.apply(in));
1023  sm.push_back(StarMatch(out, in, nullptr, nullptr));
1024  }
1025  unsigned npairs = sm.size();
1026  int maxdeg = 9;
1027  int degree;
1029  for (degree = 1; degree <= maxdeg; ++degree) {
1030  poly.reset(new GtransfoPoly(degree));
1031  poly->fit(sm);
1032  // compute the chi2 ignoring errors:
1033  double chi2 = 0;
1034  for (auto const &i : sm) chi2 += i.point2.computeDist2(poly->apply((i.point1)));
1035  if (chi2 / npairs < precision * precision) break;
1036  }
1037  if (degree > maxdeg)
1038  LOGLS_WARN(_log, "inversePolyTransfo: Reached max degree without reaching requested precision: "
1039  << precision);
1040  return poly;
1041 }
1042 
1043 /**************** GtransfoLin ***************************************/
1044 /* GtransfoLin is a specialized constructor of GtransfoPoly
1045  May be it could just disappear ??
1046 */
1047 
1048 GtransfoLin::GtransfoLin(const double Dx, const double Dy, const double A11, const double A12,
1049  const double A21, const double A22)
1050  : GtransfoPoly(1) {
1051  dx() = Dx;
1052  a11() = A11;
1053  a12() = A12;
1054  dy() = Dy;
1055  a21() = A21;
1056  a22() = A22;
1057 }
1058 
1060  if (gtransfoPoly.getDegree() != 1)
1062  "Trying to build a GtransfoLin from a higher order transfo. Aborting. ");
1063  (GtransfoPoly &)(*this) = gtransfoPoly;
1064 }
1065 
1067  // There is a general routine in GtransfoPoly that would do the job:
1068  // return GtransfoLin(GtransfoPoly::operator*(right));
1069  // however, we are using this composition of linear stuff heavily in
1070  // Gtransfo::linearApproximation, itself used in inverseTransfo::apply.
1071  // So, for sake of efficiency, and since it is easy, we take a shortcut:
1072  GtransfoLin result;
1073  apply(right.Dx(), right.Dy(), result.dx(), result.dy());
1074  result.a11() = A11() * right.A11() + A12() * right.A21();
1075  result.a12() = A11() * right.A12() + A12() * right.A22();
1076  result.a21() = A21() * right.A11() + A22() * right.A21();
1077  result.a22() = A21() * right.A12() + A22() * right.A22();
1078  return result;
1079 }
1080 
1081 void GtransfoLin::computeDerivative(const Point &, GtransfoLin &derivative, const double) const {
1082  derivative = *this;
1083  derivative.coeff(0, 0, 0) = 0;
1084  derivative.coeff(0, 0, 1) = 0;
1085 }
1086 
1087 GtransfoLin GtransfoLin::linearApproximation(const Point &, const double) const { return *this; }
1088 
1090  //
1091  // (T1,M1) * (T2,M2) = 1 i.e (0,1) implies
1092  // T1 = -M1 * T2
1093  // M1 = M2^(-1)
1094  //
1095 
1096  double a11 = A11();
1097  double a12 = A12();
1098  double a21 = A21();
1099  double a22 = A22();
1100  double d = (a11 * a22 - a12 * a21);
1101  if (d == 0) {
1102  LOGL_FATAL(_log,
1103  "GtransfoLin::invert singular transformation: transfo contents will be dumped to stderr.");
1104  dump(cerr);
1105  }
1106 
1107  GtransfoLin result(0, 0, a22 / d, -a12 / d, -a21 / d, a11 / d);
1108  double rdx, rdy;
1109  result.apply(Dx(), Dy(), rdx, rdy);
1110  result.dx() = -rdx;
1111  result.dy() = -rdy;
1112  return result;
1113 }
1114 
1117 }
1118 
1120  throw pexExcept::NotFoundError("GTransfoLinRot::fit not implemented! aborting");
1121 }
1122 
1123 double GtransfoLinShift::fit(const StarMatchList &starMatchList) {
1124  int npairs = starMatchList.size();
1125  if (npairs < 3) {
1126  LOGLS_FATAL(_log, "GtransfoLinShift::fit trying to fit a linear transfo with only " << npairs
1127  << " matches.");
1128  return -1;
1129  }
1130 
1131  double sumr2 = 0; /* used to compute chi2 without relooping */
1132  /* loop on pairs and fill */
1133  Eigen::VectorXd B(2);
1134  B.setZero();
1135  Eigen::MatrixXd A(2, 2);
1136  A.setZero();
1137 
1138  for (auto const &it : starMatchList) {
1139  const FatPoint &point1 = it.point1;
1140  const FatPoint &point2 = it.point2;
1141  double deltax = point2.x - point1.x;
1142  double deltay = point2.y - point1.y;
1143  double vxx = point1.vx + point2.vx;
1144  double vyy = point1.vy + point2.vy;
1145  double vxy = point1.vxy + point2.vxy;
1146  double det = vxx * vyy - vxy * vxy;
1147  double wxx = vyy / det;
1148  double wyy = vxx / det;
1149  double wxy = -vxy / det;
1150  B(0) += deltax * wxx + wxy * deltay;
1151  B(1) += deltay * wyy + wxy * deltax;
1152  A(0, 0) += wxx;
1153  A(1, 1) += wyy;
1154  A(0, 1) += wxy;
1155  sumr2 += deltax * deltax * wxx + deltay * deltay * wyy + 2. * wxy * deltax * deltay;
1156  }
1157  double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
1158  if (det <= 0) return -1;
1159  double tmp = A(0, 0);
1160  A(0, 0) = A(1, 1) / det;
1161  A(1, 1) = tmp / det;
1162  A(0, 1) = A(1, 0) = -A(0, 1) / det;
1163  Eigen::VectorXd sol = A * B;
1164  (*this) = GtransfoLinShift(sol(0), sol(1));
1165  return (sumr2 - sol.dot(B)); // chi2 compact form
1166 }
1167 
1168 GtransfoLinRot::GtransfoLinRot(const double angleRad, const Point *center, const double scaleFactor) {
1169  double c = scaleFactor * cos(angleRad);
1170  double s = scaleFactor * sin(angleRad);
1171  a11() = a22() = c;
1172  a21() = s;
1173  a12() = -s;
1174 
1175  // we want that the center does not move : gtransfo+M*C = C ==> gtransfo = C - M*C
1176  Point a_point(0., 0.);
1177  if (center) a_point = *center;
1178 
1179  dx() = dy() = 0;
1180  GtransfoPoly::apply(a_point.x, a_point.y, dx(), dy()); // compute M*C
1181  dx() = a_point.x - Dx();
1182  dy() = a_point.y - dy();
1183 }
1184 
1185 static double deg2rad(double degree) { return degree * M_PI / 180.; }
1186 
1187 static double rad2deg(double rad) { return rad * 180. / M_PI; }
1188 
1189 /************* WCS transfo ******************/
1190 /************** LinPix2Tan *******************/
1191 
1192 /* Implementation note : it seemed wise to incorporate
1193  the radians to degrees convertion into the linPix2Tan
1194  part (right in the constructor), and to do the
1195  opposite operation in the LinPart routine.
1196  When I was coding the fit, I realized that it was a
1197  bad idea. then I realized that the fitting routine
1198  itself was probably useless. Finaly, for a persistor,
1199  it seems bizarre that the stored data is different
1200  from what convention (angles in degrees for WCS's)
1201  would expect.
1202  So, no more "automatic" degrees to radians and
1203  radians to degrees conversion. They are explicitely
1204  done in apply (for TanPix2RaDec and TanRaDec2Pix).
1205  This is a minor concern though....
1206 */
1207 BaseTanWcs::BaseTanWcs(const GtransfoLin &pix2Tan, const Point &tangentPoint,
1208  const GtransfoPoly *corrections) {
1209  /* the angles returned by linPix2Tan should be in
1210  degrees. */
1211  linPix2Tan = pix2Tan;
1212  ra0 = deg2rad(tangentPoint.x);
1213  dec0 = deg2rad(tangentPoint.y);
1214  cos0 = cos(dec0);
1215  sin0 = sin(dec0);
1216  corr = nullptr;
1217  if (corrections) corr.reset(new GtransfoPoly(*corrections));
1218 }
1219 
1220 /* with some sort of smart pointer ro handle "corr", we could remove the
1221  copy constructor, the operator = and the destructor */
1222 
1223 // ": Gtransfo" suppresses a warning
1225  corr = nullptr;
1226  *this = original;
1227 }
1228 
1229 void BaseTanWcs::operator=(const BaseTanWcs &original) {
1230  linPix2Tan = original.linPix2Tan;
1231  ra0 = original.ra0;
1232  dec0 = original.dec0;
1233  cos0 = cos(dec0);
1234  sin0 = sin(dec0);
1235  corr = nullptr;
1236  if (original.corr) corr.reset(new GtransfoPoly(*original.corr));
1237 }
1238 
1239 void BaseTanWcs::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1240  double l, m; // radians in the tangent plane
1241  pix2TP(xIn, yIn, l, m); // l, m in degrees.
1242  l = deg2rad(l);
1243  m = deg2rad(m); // now in radians
1244  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1245  /* At variance with wcslib, it collapses the projection to a plane
1246  and expression of sidereal cooordinates into a single set of
1247  operations. */
1248  double dect = cos0 - m * sin0;
1249  if (dect == 0) {
1250  LOGL_WARN(_log, "No sidereal coordinates at pole!");
1251  xOut = 0;
1252  yOut = 0;
1253  return;
1254  }
1255  double rat = ra0 + atan2(l, dect);
1256  dect = atan(cos(rat - ra0) * (m * cos0 + sin0) / dect);
1257  if (rat - ra0 > M_PI) rat -= (2. * M_PI);
1258  if (rat - ra0 < -M_PI) rat += (2. * M_PI);
1259  if (rat < 0.0) rat += (2. * M_PI);
1260  // convert to degree
1261  xOut = rad2deg(rat);
1262  yOut = rad2deg(dect);
1263 }
1264 
1265 Point BaseTanWcs::getTangentPoint() const { return Point(rad2deg(ra0), rad2deg(dec0)); }
1266 
1268 
1270 
1272  /* CRPIX's are defined by:
1273  ( CD1_1 CD1_2 ) (x - crpix1)
1274  transformed = ( ) * ( )
1275  ( CD2_1 CD2_2 ) (y - crpix2)
1276 
1277  so that CrPix is the point which transforms to (0,0)
1278  */
1279  const GtransfoLin inverse = linPix2Tan.invert();
1280  return Point(inverse.Dx(), inverse.Dy());
1281 }
1282 
1284 
1285 /*************************** TanPix2RaDec ***************/
1286 
1287 TanPix2RaDec::TanPix2RaDec(const GtransfoLin &pix2Tan, const Point &tangentPoint,
1288  const GtransfoPoly *corrections)
1289  : BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1290 
1291 // ": Gtransfo" suppresses a warning
1293 
1295  const GtransfoLin *lin = dynamic_cast<const GtransfoLin *>(right);
1296  if (lin && lin->getDegree() == 1) return std::unique_ptr<Gtransfo>(new TanPix2RaDec((*this) * (*lin)));
1297  return std::unique_ptr<Gtransfo>(nullptr);
1298 }
1299 
1301  TanPix2RaDec result(*this);
1302  result.linPix2Tan = result.linPix2Tan * right;
1303  return result;
1304 }
1305 
1307  if (corr != nullptr) {
1308  LOGL_WARN(_log, "You are inverting a TanPix2RaDec with corrections.");
1309  LOGL_WARN(_log, "The inverse you get ignores the corrections!");
1310  }
1312 }
1313 
1316 }
1317 
1318 std::unique_ptr<Gtransfo> TanPix2RaDec::inverseTransfo(const double precision, const Frame &region) const {
1319  if (!corr)
1321  else
1322  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
1323 }
1324 
1326  if (corr)
1327  return (*corr) * linPix2Tan;
1328  else
1329  return linPix2Tan;
1330 }
1331 
1332 void TanPix2RaDec::pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const {
1333  // xTangentPlane, yTangentPlane in degrees.
1334  linPix2Tan.apply(xPixel, yPixel, xTangentPlane, yTangentPlane);
1335  if (corr) {
1336  double xtmp = xTangentPlane;
1337  double ytmp = yTangentPlane;
1338  corr->apply(xtmp, ytmp, xTangentPlane, yTangentPlane); // still in degrees.
1339  }
1340 }
1341 
1344 }
1345 
1346 void TanPix2RaDec::dump(ostream &stream) const {
1347  stream << " TanPix2RaDec, lin part :" << endl << linPix2Tan;
1348  Point tp = getTangentPoint();
1349  stream << " tangent point " << tp.x << ' ' << tp.y << endl;
1350  Point crpix = getCrPix();
1351  stream << " crpix " << crpix.x << ' ' << crpix.y << endl;
1352  if (corr) stream << "PV correction: " << endl << *corr;
1353 }
1354 
1356  /* OK we could implement this routine, but it is
1357  probably useless since to do the match, we have to
1358  project from sky to tangent plane. When a match is
1359  found, it is easier to carry out the fit in the
1360  tangent plane, rather than going back to the celestial
1361  sphere (and reproject to fit...). Anyway if this
1362  message shows up, we'll think about it.
1363  */
1365  "TanPix2RaDec::fit is NOT implemented (although it is doable)) ");
1366  return -1;
1367 }
1368 
1369 /*************************** TanSipPix2RaDec ***************/
1370 
1371 TanSipPix2RaDec::TanSipPix2RaDec(const GtransfoLin &pix2Tan, const Point &tangentPoint,
1372  const GtransfoPoly *corrections)
1373  : BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1374 
1375 // ": Gtransfo" suppresses a warning
1377 
1378 /* Would require some checks before cooking up something more efficient
1379  than just a linear approximation */
1380 #if 0
1382 {
1383  if (&region) {}
1385 }
1386 #endif
1387 
1389  const { /* We have not implemented (yet) the reverse corrections available in SIP */
1390  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
1391 }
1392 
1394  if (corr)
1395  return GtransfoPoly(linPix2Tan) * (*corr);
1396  else
1397  return linPix2Tan;
1398 }
1399 
1400 void TanSipPix2RaDec::pix2TP(double xPixel, double yPixel, double &xTangentPlane,
1401  double &yTangentPlane) const {
1402  // xTangentPlane, yTangentPlane returned in degrees
1403  if (corr) {
1404  double xtmp, ytmp;
1405  corr->apply(xPixel, yPixel, xtmp, ytmp);
1406  linPix2Tan.apply(xtmp, ytmp, xTangentPlane, yTangentPlane);
1407  } else
1408  linPix2Tan.apply(xPixel, yPixel, xTangentPlane, yTangentPlane);
1409 }
1410 
1413 }
1414 
1415 void TanSipPix2RaDec::dump(ostream &stream) const {
1416  stream << " TanSipPix2RaDec, lin part :" << endl << linPix2Tan;
1417  Point tp = getTangentPoint();
1418  stream << " tangent point " << tp.x << ' ' << tp.y << endl;
1419  Point crpix = getCrPix();
1420  stream << " crpix " << crpix.x << ' ' << crpix.y << endl;
1421  if (corr) stream << "PV correction: " << endl << *corr;
1422 }
1423 
1425  /* OK we could implement this routine, but it is
1426  probably useless since to do the match, we have to
1427  project from sky to tangent plane. When a match is
1428  found, it is easier to carry out the fit in the
1429  tangent plane, rather than going back to the celestial
1430  sphere (and reproject to fit...). Anyway if this
1431  message shows up, we'll think about it.
1432  */
1434  "TanSipPix2RaDec::fit is NOT implemented (although it is doable)) ");
1435  return -1;
1436 }
1437 
1438 /*************** reverse transfo of TanPix2RaDec: TanRaDec2Pix ********/
1439 
1440 TanRaDec2Pix::TanRaDec2Pix(const GtransfoLin &tan2Pix, const Point &tangentPoint) : linTan2Pix(tan2Pix) {
1441  setTangentPoint(tangentPoint);
1442 }
1443 
1444 void TanRaDec2Pix::setTangentPoint(const Point &tangentPoint) {
1445  /* the radian to degrees conversion after projection
1446  is handled in apply */
1447  ra0 = deg2rad(tangentPoint.x);
1448  dec0 = deg2rad(tangentPoint.y);
1449  cos0 = cos(dec0);
1450  sin0 = sin(dec0);
1451 }
1452 
1453 TanRaDec2Pix::TanRaDec2Pix() : linTan2Pix() {
1454  ra0 = dec0 = 0;
1455  cos0 = 1;
1456  sin0 = 0;
1457 }
1458 
1459 Point TanRaDec2Pix::getTangentPoint() const { return Point(rad2deg(ra0), rad2deg(dec0)); }
1460 
1461 GtransfoLin TanRaDec2Pix::getLinPart() const { return linTan2Pix; }
1462 
1463 // Use analytic derivatives, computed at the same time as the transform itself
1465  /* this routine is very similar to apply, but also propagates errors.
1466  The deg2rad and rad2deg are ignored for errors because they act as
1467  2 global scalings that cancel each other.
1468  Derivatives were computed using maple:
1469 
1470  l1 := sin(a - a0)*cos(d);
1471  m1 := sin(d)*sin(d0)+cos(d)*cos(d0)*cos(a-a0);
1472  l2 := sin(d)*cos(d0)-cos(d)*sin(d0)*cos(a-a0);
1473  simplify(diff(l1/m1,a));
1474  simplify(diff(l1/m1,d));
1475  simplify(diff(l2/m1,a));
1476  simplify(diff(l2/m1,d));
1477 
1478  Checked against Gtransfo::transformPosAndErrors (dec 09)
1479  */
1480  double ra = deg2rad(in.x);
1481  double dec = deg2rad(in.y);
1482  if (ra - ra0 > M_PI) ra -= (2. * M_PI);
1483  if (ra - ra0 < -M_PI) ra += (2. * M_PI);
1484  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1485  // The same code is copied in ::apply()
1486 
1487  double coss = cos(dec);
1488  double sins = sin(dec);
1489  double sinda = sin(ra - ra0);
1490  double cosda = cos(ra - ra0);
1491  double l = sinda * coss;
1492  double m = sins * sin0 + coss * cos0 * cosda;
1493  l = l / m;
1494  m = (sins * cos0 - coss * sin0 * cosda) / m;
1495 
1496  // derivatives
1497  double deno =
1498  sq(sin0) - sq(coss) + sq(coss * cos0) * (1 + sq(cosda)) + 2 * sins * sin0 * coss * cos0 * cosda;
1499  double a11 = coss * (cosda * sins * sin0 + coss * cos0) / deno;
1500  double a12 = -sinda * sin0 / deno;
1501  double a21 = coss * sinda * sins / deno;
1502  double a22 = cosda / deno;
1503 
1504  FatPoint tmp;
1505  tmp.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
1506  tmp.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
1507  tmp.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
1508 
1509  // l and m are now coordinates in the tangent plane, in radians.
1510  tmp.x = rad2deg(l);
1511  tmp.y = rad2deg(m);
1512 
1513  linTan2Pix.transformPosAndErrors(tmp, out);
1514 }
1515 
1516 void TanRaDec2Pix::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1517  double ra = deg2rad(xIn);
1518  double dec = deg2rad(yIn);
1519  if (ra - ra0 > M_PI) ra -= (2. * M_PI);
1520  if (ra - ra0 < -M_PI) ra += (2. * M_PI);
1521  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1522  // The same code is copied in ::transformPosAndErrors()
1523  double coss = cos(dec);
1524  double sins = sin(dec);
1525  double l = sin(ra - ra0) * coss;
1526  double m = sins * sin0 + coss * cos0 * cos(ra - ra0);
1527  l = l / m;
1528  m = (sins * cos0 - coss * sin0 * cos(ra - ra0)) / m;
1529  // l and m are now coordinates in the tangent plane, in radians.
1530  l = rad2deg(l);
1531  m = rad2deg(m);
1532  linTan2Pix.apply(l, m, xOut, yOut);
1533 }
1534 
1536 
1537 void TanRaDec2Pix::dump(ostream &stream) const {
1538  Point tp = getTangentPoint();
1539  stream << " tan2pix " << linTan2Pix << " tangent point " << tp.x << ' ' << tp.y << endl;
1540 }
1541 
1544 }
1545 
1548 }
1549 
1551  return std::unique_ptr<Gtransfo>(new TanRaDec2Pix(*this));
1552 }
1553 
1556  "TanRaDec2Pix::fit is NOT implemented (although it is doable)) ");
1557  return -1;
1558 }
1559 
1560 /************* a "run-time" transfo, that does not require to
1561 modify this file */
1562 
1563 UserTransfo::UserTransfo(GtransfoFun &userFun, const void *userData)
1564  : _userFun(userFun), _userData(userData) {}
1565 
1566 void UserTransfo::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1567  _userFun(xIn, yIn, xOut, yOut, _userData);
1568 }
1569 
1570 void UserTransfo::dump(ostream &stream) const {
1571  stream << "UserTransfo with user function @ " << _userFun << "and userData@ " << _userData << endl;
1572 }
1573 
1576  "UserTransfo::fit is NOT implemented (and will never be)) ");
1577  return -1;
1578 }
1579 
1581  return std::unique_ptr<Gtransfo>(new UserTransfo(*this));
1582 }
1583 
1584 /*************************************************************/
1585 
1587  ifstream s(fileName.c_str());
1588  if (!s)
1589  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, " gtransfoRead : cannot open " + fileName);
1590  try {
1592  s.close();
1593  return res;
1596  std::string(e.what()) + " in file " + fileName);
1597  }
1598 }
1599 
1601  std::string type;
1602  s >> type;
1603  if (s.fail())
1605  "gtransfoRead : could not find a Gtransfotype");
1606  if (type == "GtransfoIdentity") {
1608  res->read(s);
1609  return std::move(res);
1610  } else if (type == "GtransfoPoly") {
1612  res->read(s);
1613  return std::move(res);
1614  } else
1616  " gtransfoRead : No reader for Gtransfo type " + type);
1617 }
1618 } // namespace jointcal
1619 } // namespace lsst
#define LOGLS_WARN(logger, message)
std::unique_ptr< Gtransfo > roughInverse(const Frame &region) const
Overload the "generic routine" (available for all Gtransfo types.
Definition: Gtransfo.cc:1314
int y
void write(std::ostream &s) const
Definition: Gtransfo.cc:394
virtual std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:306
void dump(std::ostream &stream=std::cout) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1570
implements the linear transformations (6 real coefficients).
Definition: Gtransfo.h:292
TanPix2RaDec invert() const
exact typed inverse:
Definition: Gtransfo.cc:1535
T atan(T... args)
TanRaDec2Pix invert() const
approximate inverse : it ignores corrections;
Definition: Gtransfo.cc:1306
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanPix2RaDec.
Definition: Gtransfo.cc:1546
std::unique_ptr< Gtransfo > gtransfoCompose(const Gtransfo *left, const Gtransfo *right)
Returns a pointer to a composition.
Definition: Gtransfo.cc:367
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
specialised analytic routine
Definition: Gtransfo.cc:508
PolyXY(const int degree)
Definition: Gtransfo.cc:860
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:485
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:302
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
Computes the local Derivative of a transfo, w.r.t.
Definition: Gtransfo.cc:385
A hanger for star associations.
Definition: StarMatch.h:31
A point in a plane.
Definition: Point.h:13
void() GtransfoFun(const double, const double, double &, double &, const void *)
signature of the user-provided routine that actually does the coordinate transfo for UserTransfo...
Definition: Gtransfo.h:564
double getArea() const
Definition: Frame.cc:102
the transformation that handles pix to sideral transfos (Gnomonic, possibly with polynomial distortio...
Definition: Gtransfo.h:443
void setCorrections(std::unique_ptr< GtransfoPoly > corrections)
Assign the correction polynomial (what it means is left to derived classes)
Definition: Gtransfo.cc:1269
long double & coeff(const unsigned powX, const unsigned powY)
Definition: Gtransfo.cc:879
GtransfoLin invert() const
returns the inverse: T1 = T2.invert();
Definition: Gtransfo.cc:1089
T fail(T... args)
std::unique_ptr< Gtransfo > roughInverse(const Frame &) const
Overload the "generic routine".
Definition: Gtransfo.cc:240
FatPoint point2
2 points
Definition: StarMatch.h:37
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
implements an iterative (Gauss-Newton) solver.
Definition: Gtransfo.cc:275
T endl(T... args)
void paramDerivatives(const Point &where, double *dx, double *dy) const
Derivative w.r.t parameters.
Definition: Gtransfo.cc:682
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:1566
void write(std::ostream &s) const
Definition: Gtransfo.cc:987
bool isIdentity(const Gtransfo *gtransfo)
Shorthand test to tell if a transfo belongs to the GtransfoIdentity class.
Definition: Gtransfo.cc:29
T left(T... args)
int min
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
return second(first(xIn,yIn))
Definition: Gtransfo.cc:341
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:1239
STL namespace.
T precision(T... args)
double fit(const StarMatchList &starMatchList)
Not implemented yet, because we do it otherwise.
Definition: Gtransfo.cc:1424
double xMin
coordinate of boundary.
Definition: Frame.h:22
GtransfoLin linearApproximation(const Point &where, const double step=0.01) const
linear (local) approximation.
Definition: Gtransfo.cc:1087
T end(T... args)
#define LOGL_ERROR(logger, message...)
T floor(T... args)
double paramRef(const int i) const
Definition: Gtransfo.cc:672
std::ostream & operator<<(std::ostream &stream, const Gtransfo &gtransfo)
allows &#39;stream << Transfo;&#39; (by calling gtransfo.dump(stream)).
Definition: Gtransfo.cc:193
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1415
Polynomial transformation class.
Definition: Gtransfo.h:193
GtransfoLin normalizeCoordinatesTransfo(const Frame &frame)
Returns the transformation that maps the input frame along both axes to [-1,1].
Definition: Gtransfo.cc:724
GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region)
Definition: Gtransfo.cc:255
STL class.
GtransfoPoly getPix2TangentPlane() const
the transformation from pixels to tangent plane (coordinates in degrees)
Definition: Gtransfo.cc:1393
A Point with uncertainties.
Definition: FatPoint.h:11
#define M_PI
Definition: ListMatch.cc:7
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:1554
T resize(T... args)
int const step
T atan2(T... args)
STL class.
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1342
double x
coordinate
Definition: Point.h:18
pairs of points
T sin(T... args)
GtransfoComposition(const Gtransfo *second, const Gtransfo *first)
will pipe transfos
Definition: Gtransfo.cc:336
T at(T... args)
std::unique_ptr< Gtransfo > roughInverse(const Frame &region) const
Overload the "generic routine" (available for all Gtransfo types.
Definition: Gtransfo.cc:1542
GtransfoPoly operator*(const GtransfoPoly &right) const
Composition (internal stuff in quadruple precision)
Definition: Gtransfo.cc:943
GtransfoLin()
the default constructor constructs the do-nothing transformation.
Definition: Gtransfo.h:297
bool isIntegerShift(const Gtransfo *gtransfo)
Shorthand test to tell if a transfo is a simple integer shift.
Definition: Gtransfo.cc:33
std::unique_ptr< GtransfoPoly > inversePolyTransfo(const Gtransfo &Direct, const Frame &frame, const double Prec)
approximates the inverse by a polynomial, up to required precision.
Definition: Gtransfo.cc:1012
double getHeight() const
size along y axis
Definition: Frame.h:38
T push_back(T... args)
STL class.
double coeffOrZero(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
read access, zero if beyond degree
Definition: Gtransfo.cc:663
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
specialised analytic routine
Definition: Gtransfo.cc:1081
first
#define LOGL_FATAL(logger, message...)
unsigned getDegree() const
Definition: Gtransfo.cc:865
void read(std::istream &s)
Definition: Gtransfo.cc:396
rectangle with sides parallel to axes.
Definition: Frame.h:19
Class for a simple mapping implementing a generic Gtransfo.
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1550
GtransfoPoly operator-(const GtransfoPoly &right) const
Subtraction.
Definition: Gtransfo.cc:977
Point getCrPix() const
the CRPIX values (this is WCS jargon), in 0-based coordinates
Definition: Gtransfo.cc:1271
GtransfoLin getLinPart() const
The Linear part (corresponding to CD&#39;s and CRPIX&#39;s)
Definition: Gtransfo.cc:1267
std::unique_ptr< Gtransfo > reduceCompo(const Gtransfo *right) const
to be overloaded by derived classes if they can really "reduce" the composition (e.g.
Definition: Gtransfo.cc:1294
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
returns an inverse transfo. Numerical if not overloaded.
Definition: Gtransfo.cc:1115
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1411
double fit(const StarMatchList &starMatchList)
guess what
Definition: Gtransfo.cc:1119
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:1516
int max
void dump(ostream &stream=cout) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:347
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
void read(std::istream &s)
Definition: Gtransfo.cc:997
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:352
Point getTangentPoint() const
tangent point coordinates (in degrees)
Definition: Gtransfo.cc:1459
T str(T... args)
double fit(const StarMatchList &starMatchList)
Not implemented yet, because we do it otherwise.
Definition: Gtransfo.cc:1355
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1537
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:357
T reset(T... args)
T cos(T... args)
void dump(std::ostream &stream=std::cout) const
print out of coefficients in a readable form.
Definition: Gtransfo.cc:702
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:1574
#define LOGL_WARN(logger, message...)
T fabs(T... args)
T max(T... args)
T move(T... args)
double getWidth() const
size along x axis
Definition: Frame.h:35
T count(T... args)
unsigned getDegree() const
returns degree
Definition: Gtransfo.h:216
virtual void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
a mix of apply and Derivative
Definition: Gtransfo.cc:566
PolyXY(const GtransfoPoly &gtransfoPoly, const unsigned whichCoord)
Definition: Gtransfo.cc:867
T insert(T... args)
Extent< double, N > & operator+=(Extent< double, N > &lhs, Extent< int, N > const &rhs)
Point getCenter() const
Center of the frame.
Definition: Frame.h:41
A do-nothing transformation. It anyway has dummy routines to mimick a Gtransfo.
Definition: Gtransfo.h:150
second
void setTangentPoint(const Point &tangentPoint)
Resets the projection (or tangent) point.
Definition: Gtransfo.cc:1444
T size(T... args)
#define LSST_EXCEPT(type,...)
virtual char const * what(void) const
just here to provide a specialized constructor, and fit.
Definition: Gtransfo.h:356
virtual std::unique_ptr< Gtransfo > reduceCompo(const Gtransfo *right) const
to be overloaded by derived classes if they can really "reduce" the composition (e.g.
Definition: Gtransfo.cc:52
STL class.
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanRaDec2Pix if there are no corrections, or the iterative solver if there...
Definition: Gtransfo.cc:1318
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1580
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane) ...
Definition: Gtransfo.h:517
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanRaDec2Pix if there are no corrections, or the iterative solver if there...
Definition: Gtransfo.cc:1388
T begin(T... args)
friend class GtransfoPoly
Definition: Gtransfo.h:347
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
Definition: Gtransfo.cc:1464
T pow(T... args)
GtransfoPoly operator+(const GtransfoPoly &right) const
Addition.
Definition: Gtransfo.cc:964
GtransfoLin getLinPart() const
The Linear part (corresponding to CD&#39;s and CRPIX&#39;s)
Definition: Gtransfo.cc:1461
just here to provide specialized constructors. GtransfoLin fit routine.
Definition: Gtransfo.h:383
T c_str(T... args)
a virtual (interface) class for geometric transformations.
Definition: Gtransfo.h:39
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1346
GtransfoPoly(const unsigned degree=1)
Default transfo : identity for all degrees (>=1 ).
Definition: Gtransfo.cc:408
void setDegree(const unsigned degree)
Definition: Gtransfo.cc:464
Point getTangentPoint() const
The tangent point (in degrees)
Definition: Gtransfo.cc:1265
double x
std::unique_ptr< GtransfoPoly > corr
Definition: Gtransfo.h:435
T dec(T... args)
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane
Definition: Gtransfo.cc:1400
double coeff(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
access to coefficients (read only)
Definition: Gtransfo.cc:650
Private class to handle Gtransfo compositions (i.e.
Definition: Gtransfo.cc:317
BaseTanWcs(const GtransfoLin &pix2Tan, const Point &tangentPoint, const GtransfoPoly *corrections=nullptr)
Definition: Gtransfo.cc:1207
T sqrt(T... args)
GtransfoPoly getPix2TangentPlane() const
the transformation from pixels to tangent plane (coordinates in degrees)
Definition: Gtransfo.cc:1325
m
double fit(const StarMatchList &starMatchList)
guess what
Definition: Gtransfo.cc:1123
virtual GtransfoLin linearApproximation(const Point &where, const double step=0.01) const
linear approximation.
Definition: Gtransfo.cc:389
TanPix2RaDec operator*(const GtransfoLin &right) const
composition with GtransfoLin
Definition: Gtransfo.cc:1300
#define LOGLS_FATAL(logger, message)
long double coeff(const unsigned powX, const unsigned powY) const
Definition: Gtransfo.cc:874
#define LOG_GET(logger)
std::unique_ptr< Gtransfo > reduceCompo(const Gtransfo *right) const
to be overloaded by derived classes if they can really "reduce" the composition (e.g.
Definition: Gtransfo.cc:834
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane
Definition: Gtransfo.cc:1332
std::unique_ptr< Gtransfo > gtransfoRead(const std::string &fileName)
The virtual constructor from a file.
Definition: Gtransfo.cc:1586
T setprecision(T... args)
virtual std::unique_ptr< Gtransfo > clone() const =0
returns a copy (allocated by new) of the transformation.
double fit(const StarMatchList &starMatchList)
guess what
Definition: Gtransfo.cc:816
virtual void dump(std::ostream &stream=std::cout) const =0
dumps the transfo coefficients to stream.
STL class.
UserTransfo(GtransfoFun &fun, const void *userData)
the transfo routine and extra data that it may need.
Definition: Gtransfo.cc:1563
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const =0
Transforms from pixel space to tangent plane. deferred to actual implementations. ...
clone
STL class.
virtual void apply(const double xIn, const double yIn, double &xOut, double &yOut) const =0
void dump(ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:298
void operator=(const BaseTanWcs &original)
Definition: Gtransfo.cc:1229
T reserve(T... args)
std::unique_ptr< Gtransfo > inverseTransfo(double, const Frame &) const
Inverse transfo: returns the direct one!
Definition: Gtransfo.cc:243
double determinant() const
Definition: Gtransfo.cc:718
GtransfoLin operator*(const GtransfoLin &right) const
enables to combine linear tranformations: T1=T2*T3 is legal.
Definition: Gtransfo.cc:1066
table::PointKey< double > crpix