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