9 #include "lsst/log/Log.h"
13 #include "lsst/pex/exceptions.h"
14 #include "Eigen/Cholesky"
16 namespace pexExcept = lsst::pex::exceptions;
21 LOG_LOGGER _log = LOG_GET(
"jointcal.Gtransfo");
28 return (dynamic_cast<const GtransfoIdentity *>(gtransfo) !=
nullptr);
31 static double sqr(
double x) {
return x * x; }
35 if (shift ==
nullptr)
return false;
37 static const double eps = 1e-5;
39 double dx = shift->
coeff(0, 0, 0);
40 double dy = shift->
coeff(0, 0, 1);
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)
52 std::unique_ptr<Gtransfo> Gtransfo::reduceCompo(
const Gtransfo *)
const {
53 return std::unique_ptr<Gtransfo>(
nullptr);
56 double Gtransfo::getJacobian(
const double x,
const double y)
const {
58 double eps = x * 0.01;
59 if (eps == 0) eps = 0.01;
62 apply(x + eps, y, dxdx, dydx);
66 apply(x, y + eps, dxdy, dydy);
69 return ((dxdx * dydy - dxdy * dydx) / (eps * eps));
75 void Gtransfo::computeDerivative(
const Point &where,
GtransfoLin &derivative,
const double step)
const {
79 apply(x, y, xp0, yp0);
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;
92 GtransfoLin Gtransfo::linearApproximation(
const Point &where,
const double step)
const {
93 Point outwhere = apply(where);
95 computeDerivative(where, der, step);
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;
116 void Gtransfo::transformErrors(
const Point &where,
const double *vIn,
double *vOut)
const {
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();
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];
144 vOut[xx] = b11 * a11 + b12 * a12;
145 vOut[xy] = b11 * a21 + b12 * a22;
146 vOut[yy] = b21 * a21 + b22 * a22;
149 std::unique_ptr<Gtransfo> Gtransfo::roughInverse(
const Frame ®ion)
const {
152 Point centerIn = apply(centerOut);
154 computeDerivative(centerOut, der, sqrt(region.
getArea()) / 5.);
157 return std::unique_ptr<Gtransfo>(
new GtransfoLin(der));
169 void Gtransfo::getParams(
double *params)
const {
170 int npar = getNpar();
171 for (
int i = 0; i < npar; ++i) params[i] = paramRef(i);
174 void Gtransfo::offsetParams(
const double *params) {
175 int npar = getNpar();
176 for (
int i = 0; i < npar; ++i) paramRef(i) += params[i];
179 double Gtransfo::paramRef(
const int)
const {
180 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
181 std::string(
"Gtransfo::paramRef should never be called "));
184 double &Gtransfo::paramRef(
const int) {
185 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Gtransfo::paramRef should never be called ");
188 void Gtransfo::paramDerivatives(
const Point &,
double *,
double *)
const {
189 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
190 "Gtransfo::paramDerivatives() should never be called ");
194 gtransfo.
dump(stream);
198 void Gtransfo::write(
const std::string &fileName)
const {
199 ofstream s(fileName.c_str());
204 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
205 "Gtransfo::write, something went wrong for file " + fileName);
208 void Gtransfo::write(ostream &stream)
const {
209 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
210 "Gtransfo::write(ostream), should never be called. MEans that it is missing in some "
220 std::unique_ptr<Gtransfo> _direct;
221 std::unique_ptr<Gtransfo> _roughInverse;
229 void apply(
const double xIn,
const double yIn,
double &xOut,
double &yOut)
const;
231 void dump(ostream &stream)
const;
235 virtual std::unique_ptr<Gtransfo> clone()
const;
251 std::unique_ptr<Gtransfo> Gtransfo::inverseTransfo(
const double precision,
const Frame ®ion)
const {
252 return std::unique_ptr<Gtransfo>(
new GtransfoInverse(
this, precision, region));
255 GtransfoInverse::GtransfoInverse(
const Gtransfo *direct,
const double precision,
const Frame ®ion) {
256 _direct = direct->
clone();
257 _roughInverse = _direct->roughInverse(region);
258 precision2 = precision * precision;
262 _direct = model._direct->clone();
263 _roughInverse = model._roughInverse->clone();
264 precision2 = model.precision2;
270 _direct = model._direct->clone();
271 _roughInverse = model._roughInverse->clone();
272 precision2 = model.precision2;
277 Point outGuess = _roughInverse->apply(in);
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);
299 stream <<
" GtransfoInverse of :" << endl << *_direct << endl;
303 throw pexExcept::RuntimeError(
"Cannot fit a GtransfoInverse. Use StarMatchList::inverseTransfo instead.");
319 std::unique_ptr<Gtransfo> _first, _second;
326 void apply(
const double xIn,
const double yIn,
double &xOut,
double &yOut)
const;
327 void dump(ostream &stream = cout)
const;
332 std::unique_ptr<Gtransfo>
clone()
const;
337 _first = first->
clone();
338 _second = second->
clone();
343 _first->apply(xIn, yIn, xout, yout);
344 _second->apply(xout, yout, xOut, yOut);
348 _first->dump(stream);
349 _second->dump(stream);
354 return _first->fit(starMatchList);
370 return left->
clone();
375 std::unique_ptr<Gtransfo> composition(left->
reduceCompo(right));
378 if (composition ==
nullptr)
400 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
401 " GtransfoIdentity::read : format is not 1 ");
409 _nterms = (degree + 1) * (degree + 2) / 2;
412 _coeffs.resize(2 * _nterms, 0.);
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);
429 gtransfo->
apply(x, y, xtr, ytr);
430 auto tp = std::make_shared<BaseStar>(xtr, ytr, 0, 0);
433 sm.push_back(
StarMatch(*pix, *tp, pix, tp));
441 void GtransfoPoly::computeMonomials(
double xIn,
double yIn,
double *monomial)
const {
452 for (
unsigned ix = 0; ix <= _degree; ++ix) {
454 unsigned k = ix * (ix + 1) / 2;
455 for (
unsigned iy = 0; iy <= _degree - ix; ++iy) {
456 monomial[k] = xx * yy;
466 unsigned old_nterms = _nterms;
467 _nterms = (_degree + 1) * (_degree + 2) / 2;
470 vector<double> old_coeffs = _coeffs;
472 _coeffs.resize(2 * _nterms);
475 for (
unsigned k = 0; k < _nterms; ++k) _coeffs[k] = 0;
477 unsigned kmax = min(old_nterms, _nterms);
478 for (
unsigned k = 0; k < kmax; ++k) {
479 _coeffs[k] = old_coeffs[k];
480 _coeffs[k + _nterms] = old_coeffs[k + old_nterms];
495 double monomials[_nterms];
496 computeMonomials(xIn, yIn, monomials);
500 const double *c = &_coeffs[0];
501 const double *pm = &monomials[0];
503 for (
int k = _nterms; k--;) xOut += (*(pm++)) * (*(c++));
505 for (
int k = _nterms; k--;) yOut += (*(pm++)) * (*(c++));
512 derivative.
dx() = derivative.
dy() = 0;
516 double dermx[2 * _nterms];
517 double *dermy = dermx + _nterms;
518 double xin = where.
x;
519 double yin = where.
y;
523 for (
unsigned ix = 0; ix <= _degree; ++ix) {
524 unsigned k = (ix) * (ix + 1) / 2;
526 dermx[k] = ix * xxm1;
530 for (
unsigned iy = 1; iy <= _degree - ix; ++iy) {
531 dermx[k] = ix * xxm1 * yym1 * yin;
532 dermy[k] = iy * xx * yym1;
537 if (ix >= 1) xxm1 *= xin;
543 const double *mx = &dermx[0];
544 const double *my = &dermy[0];
545 const double *c = &_coeffs[0];
547 double a11 = 0, a12 = 0;
548 for (
int k = _nterms; k--;) {
549 a11 += (*(mx++)) * (*c);
550 a12 += (*(my++)) * (*(c++));
552 derivative.
a11() = a11;
553 derivative.
a12() = a12;
555 double a21 = 0, a22 = 0;
558 for (
int k = _nterms; k--;) {
559 a21 += (*(mx++)) * (*c);
560 a22 += (*(my++)) * (*(c++));
562 derivative.
a21() = a21;
563 derivative.
a22() = a22;
579 double monomials[_nterms];
583 double dermx[2 * _nterms];
584 double *dermy = dermx + _nterms;
590 for (
unsigned ix = 0; ix <= _degree; ++ix) {
591 unsigned k = (ix) * (ix + 1) / 2;
593 dermx[k] = ix * xxm1;
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;
608 if (ix >= 1) xxm1 *= xin;
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++));
617 for (
int k = _nterms; k--;) yout += (*(pm++)) * (*(c++));
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++));
631 double a21 = 0, a22 = 0;
634 for (
int k = _nterms; k--;) {
635 a21 += (*(mx++)) * (*c);
636 a22 += (*(my++)) * (*(c++));
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;
651 assert((degX + degY <= _degree) && whichCoord < 2);
655 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
659 assert((degX + degY <= _degree) && whichCoord < 2);
660 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
665 assert(whichCoord < 2);
666 if (degX + degY <= _degree)
667 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
673 assert(
unsigned(i) < 2 * _nterms);
678 assert(
unsigned(i) < 2 * _nterms);
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;
692 static string monomialString(
const unsigned powX,
const unsigned powY) {
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;
703 for (
unsigned ic = 0; ic < 2; ++ic) {
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);
715 if (_degree > 0) stream <<
" Linear determinant = " <<
determinant() << endl;
731 static GtransfoLin shiftAndNormalize(
const StarMatchList &starMatchList) {
737 for (
auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
738 const StarMatch &a_match = *it;
739 const Point &point1 = a_match.point1;
746 if (count == 0)
return GtransfoLin();
750 double xspan = 3.5 * sqrt(x2 / count - sqr(xav));
751 double yspan = 3.5 * sqrt(y2 / count - sqr(yav));
752 return GtransfoLinScale(2. / xspan, 2. / yspan) * GtransfoLinShift(-xav, -yav);
755 static double sq(
double x) {
return x * x; }
757 double GtransfoPoly::computeFit(
const StarMatchList &starMatchList,
const Gtransfo &shiftToCenter,
758 const bool useErrors) {
759 Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
761 Eigen::VectorXd B(2 * _nterms);
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;
772 computeMonomials(point1.x, point1.y, 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;
785 apply(point1.x, point1.y, tr1.x, tr1.y);
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;
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];
799 B(j) += bxcoeff * monomials[j];
800 B(j + _nterms) += bycoeff * monomials[j];
803 Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
805 if (factor.info() != Eigen::Success) {
806 LOGL_ERROR(_log,
"GtransfoPoly::fit could not factorize");
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));
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.");
823 GtransfoPoly conditionner = shiftAndNormalize(starMatchList);
825 computeFit(starMatchList, conditionner,
false);
826 computeFit(starMatchList, conditionner,
true);
827 double chi2 = computeFit(starMatchList, conditionner,
true);
829 (*this) = (*this) * conditionner;
830 if (starMatchList.size() == _nterms)
return 0;
838 return std::unique_ptr<Gtransfo>(
new GtransfoLin((*
this) * (*p)));
840 return std::unique_ptr<Gtransfo>(
new GtransfoPoly((*
this) * (*p)));
842 return std::unique_ptr<Gtransfo>(
nullptr);
857 vector<long double> coeffs;
860 PolyXY(
const int degree) : degree(degree), nterms((degree + 1) * (degree + 2) / 2) {
861 coeffs.reserve(nterms);
862 coeffs.insert(coeffs.begin(), nterms, 0L);
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);
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);
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);
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);
895 static PolyXY operator*(
const long double &a,
const PolyXY &polyXY) {
896 PolyXY result(polyXY);
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;
905 static PolyXY product(
const PolyXY &p1,
const PolyXY &p2) {
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);
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));
926 static PolyXY composition(
const PolyXY &polyXY,
const PolyXY &polyX,
const PolyXY &polyY) {
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));
951 PolyXY rx(composition(plx, prx, pry));
952 PolyXY ry(composition(ply, prx, pry));
956 for (
unsigned px = 0; px <= result._degree; ++px)
957 for (
unsigned py = 0; py <= result._degree - px; ++py) {
965 if (_degree >= right._degree) {
967 for (
unsigned i = 0; i <= right._degree; ++i)
968 for (
unsigned j = 0; j <= right._degree - i; ++j) {
974 return (right + (*
this));
979 for (
unsigned i = 0; i <= res._degree; ++i)
980 for (
unsigned j = 0; j <= res._degree - i; ++j) {
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] <<
' ';
994 s << setprecision(oldprec);
1001 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
" GtransfoPoly::read : format is not 1 ");
1004 s >> degree >> _degree;
1005 if (degree !=
"degree")
1006 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1007 " GtransfoPoly::read : expecting \"degree\" and found " + degree);
1009 for (
unsigned k = 0; k < 2 * _nterms; ++k) s >> _coeffs[k];
1013 const double precision) {
1016 double stepx = frame.
getWidth() / (nx + 1);
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);
1023 sm.push_back(
StarMatch(out, in,
nullptr,
nullptr));
1025 unsigned npairs = sm.size();
1028 std::unique_ptr<GtransfoPoly> poly;
1029 for (degree = 1; degree <= maxdeg; ++degree) {
1034 for (
auto const &i : sm) chi2 += i.point2.computeDist2(poly->
apply((i.point1)));
1035 if (chi2 / npairs < precision * precision)
break;
1037 if (degree > maxdeg)
1038 LOGLS_WARN(_log,
"inversePolyTransfo: Reached max degree without reaching requested precision: "
1049 const double A21,
const double A22)
1061 throw pexExcept::InvalidParameterError(
1062 "Trying to build a GtransfoLin from a higher order transfo. Aborting. ");
1083 derivative.
coeff(0, 0, 0) = 0;
1084 derivative.
coeff(0, 0, 1) = 0;
1100 double d = (a11 * a22 - a12 *
a21);
1103 "GtransfoLin::invert singular transformation: transfo contents will be dumped to stderr.");
1107 GtransfoLin result(0, 0, a22 / d, -a12 / d, -a21 / d, a11 / d);
1120 throw pexExcept::NotFoundError(
"GTransfoLinRot::fit not implemented! aborting");
1124 int npairs = starMatchList.size();
1126 LOGLS_FATAL(_log,
"GtransfoLinShift::fit trying to fit a linear transfo with only " << npairs
1133 Eigen::VectorXd B(2);
1135 Eigen::MatrixXd A(2, 2);
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;
1155 sumr2 += deltax * deltax * wxx + deltay * deltay * wyy + 2. * wxy * deltax * deltay;
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;
1165 return (sumr2 - sol.dot(B));
1169 double c = scaleFactor * cos(angleRad);
1170 double s = scaleFactor * sin(angleRad);
1176 Point a_point(0., 0.);
1177 if (center) a_point = *center;
1181 dx() = a_point.
x -
Dx();
1182 dy() = a_point.
y -
dy();
1185 static double deg2rad(
double degree) {
return degree *
M_PI / 180.; }
1187 static double rad2deg(
double rad) {
return rad * 180. /
M_PI; }
1212 ra0 = deg2rad(tangentPoint.
x);
1213 dec0 = deg2rad(tangentPoint.
y);
1250 LOGL_WARN(_log,
"No sidereal coordinates at pole!");
1255 double rat =
ra0 + atan2(l, dect);
1256 dect = atan(cos(rat -
ra0) * (m *
cos0 + sin0) / dect);
1259 if (rat < 0.0) rat += (2. *
M_PI);
1261 xOut = rad2deg(rat);
1262 yOut = rad2deg(dect);
1280 return Point(inverse.
Dx(), inverse.
Dy());
1289 :
BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1296 if (lin && lin->
getDegree() == 1)
return std::unique_ptr<Gtransfo>(
new TanPix2RaDec((*
this) * (*lin)));
1297 return std::unique_ptr<Gtransfo>(
nullptr);
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!");
1322 return std::unique_ptr<Gtransfo>(
new GtransfoInverse(
this, precision, region));
1336 double xtmp = xTangentPlane;
1337 double ytmp = yTangentPlane;
1338 corr->apply(xtmp, ytmp, xTangentPlane, yTangentPlane);
1347 stream <<
" TanPix2RaDec, lin part :" << endl <<
linPix2Tan;
1349 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1351 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y << endl;
1352 if (
corr) stream <<
"PV correction: " << endl << *
corr;
1364 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1365 "TanPix2RaDec::fit is NOT implemented (although it is doable)) ");
1373 :
BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1390 return std::unique_ptr<Gtransfo>(
new GtransfoInverse(
this, precision, region));
1401 double &yTangentPlane)
const {
1405 corr->apply(xPixel, yPixel, xtmp, ytmp);
1416 stream <<
" TanSipPix2RaDec, lin part :" << endl <<
linPix2Tan;
1418 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1420 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y << endl;
1421 if (
corr) stream <<
"PV correction: " << endl << *
corr;
1433 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1434 "TanSipPix2RaDec::fit is NOT implemented (although it is doable)) ");
1447 ra0 = deg2rad(tangentPoint.
x);
1448 dec0 = deg2rad(tangentPoint.
y);
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);
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;
1494 m = (sins * cos0 - coss * sin0 * cosda) / m;
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;
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;
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);
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);
1528 m = (sins * cos0 - coss * sin0 * cos(ra - ra0)) / m;
1532 linTan2Pix.
apply(l, m, xOut, yOut);
1539 stream <<
" tan2pix " << linTan2Pix <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1551 return std::unique_ptr<Gtransfo>(
new TanRaDec2Pix(*
this));
1555 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1556 "TanRaDec2Pix::fit is NOT implemented (although it is doable)) ");
1564 : _userFun(userFun), _userData(userData) {}
1567 _userFun(xIn, yIn, xOut, yOut, _userData);
1571 stream <<
"UserTransfo with user function @ " << _userFun <<
"and userData@ " << _userData << endl;
1575 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1576 "UserTransfo::fit is NOT implemented (and will never be)) ");
1581 return std::unique_ptr<Gtransfo>(
new UserTransfo(*
this));
1587 ifstream s(fileName.c_str());
1589 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
" gtransfoRead : cannot open " + fileName);
1594 }
catch (pex::exceptions::InvalidParameterError &e) {
1595 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1596 std::string(e.what()) +
" in file " + fileName);
1604 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1605 "gtransfoRead : could not find a Gtransfotype");
1606 if (type ==
"GtransfoIdentity") {
1609 return std::move(res);
1610 }
else if (type ==
"GtransfoPoly") {
1613 return std::move(res);
1615 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1616 " gtransfoRead : No reader for Gtransfo type " + type);
double getWidth() const
size along x axis
GtransfoPoly operator-(const GtransfoPoly &right) const
Subtraction.
void dump(ostream &stream=cout) const
dumps the transfo coefficients to stream.
GtransfoLin linearApproximation(const Point &where, const double step=0.01) const
linear (local) approximation.
void write(std::ostream &s) const
implements the linear transformations (6 real coefficients).
GtransfoPoly getPix2TangentPlane() const
the transformation from pixels to tangent plane (coordinates in degrees)
virtual void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
a mix of apply and Derivative
GtransfoLin getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
GtransfoPoly getPix2TangentPlane() const
the transformation from pixels to tangent plane (coordinates in degrees)
std::unique_ptr< Gtransfo > gtransfoCompose(const Gtransfo *left, const Gtransfo *right)
Returns a pointer to a composition.
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
A hanger for star associations.
unsigned getDegree() const
returns degree
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
GtransfoPoly operator+(const GtransfoPoly &right) const
Addition.
the transformation that handles pix to sideral transfos (Gnomonic, possibly with polynomial distortio...
void setCorrections(std::unique_ptr< GtransfoPoly > corrections)
Assign the correction polynomial (what it means is left to derived classes)
GtransfoLinShift(double ox=0., double oy=0.)
Add ox and oy.
long double & coeff(const unsigned powX, const unsigned powY)
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane
double coeff(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
access to coefficients (read only)
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
specialised analytic routine
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame ®ion) const
Inverse transfo: returns a TanRaDec2Pix if there are no corrections, or the iterative solver if there...
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Point getTangentPoint() const
tangent point coordinates (in degrees)
unsigned getDegree() const
std::unique_ptr< Gtransfo > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all Gtransfo types.
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame ®ion) const
returns an inverse transfo. Numerical if not overloaded.
bool isIdentity(const Gtransfo *gtransfo)
Shorthand test to tell if a transfo belongs to the GtransfoIdentity class.
std::unique_ptr< Gtransfo > inverseTransfo(double, const Frame &) const
Inverse transfo: returns the direct one!
GtransfoLin operator*(const GtransfoLin &right) const
enables to combine linear tranformations: T1=T2*T3 is legal.
double fit(const StarMatchList &starMatchList)
Not implemented yet, because we do it otherwise.
double xMin
coordinate of boundary.
void dump(std::ostream &stream=std::cout) const
dumps the transfo coefficients to stream.
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
virtual GtransfoLin linearApproximation(const Point &where, const double step=0.01) const
linear approximation.
std::ostream & operator<<(std::ostream &stream, const Gtransfo >ransfo)
allows 'stream << Transfo;' (by calling gtransfo.dump(stream)).
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Polynomial transformation class.
GtransfoLin normalizeCoordinatesTransfo(const Frame &frame)
Returns the transformation that maps the input frame along both axes to [-1,1].
GtransfoInverse(const Gtransfo *direct, const double precision, const Frame ®ion)
A Point with uncertainties.
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
specialised analytic routine
long double coeff(const unsigned powX, const unsigned powY) const
GtransfoComposition(const Gtransfo *second, const Gtransfo *first)
will pipe transfos
double coeffOrZero(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
read access, zero if beyond degree
GtransfoLin getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
GtransfoLin()
the default constructor constructs the do-nothing transformation.
bool isIntegerShift(const Gtransfo *gtransfo)
Shorthand test to tell if a transfo is a simple integer shift.
std::unique_ptr< GtransfoPoly > inversePolyTransfo(const Gtransfo &Direct, const Frame &frame, const double Prec)
approximates the inverse by a polynomial, up to required precision.
void write(std::ostream &s) const
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
return second(first(xIn,yIn))
double determinant() const
void dump(ostream &stream) const
dumps the transfo coefficients to stream.
void read(std::istream &s)
rectangle with sides parallel to axes.
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame ®ion) const
Inverse transfo: returns a TanRaDec2Pix if there are no corrections, or the iterative solver if there...
TanRaDec2Pix invert() const
approximate inverse : it ignores corrections;
double fit(const StarMatchList &starMatchList)
guess what
GtransfoPoly operator*(const GtransfoPoly &right) const
Composition (internal stuff in quadruple precision)
void read(std::istream &s)
TanPix2RaDec operator*(const GtransfoLin &right) const
composition with GtransfoLin
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
double fit(const StarMatchList &starMatchList)
Not implemented yet, because we do it otherwise.
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
double fit(const StarMatchList &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
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.
TanPix2RaDec invert() const
exact typed inverse:
virtual std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
void paramDerivatives(const Point &where, double *dx, double *dy) const
Derivative w.r.t parameters.
PolyXY(const GtransfoPoly >ransfoPoly, const unsigned whichCoord)
A do-nothing transformation. It anyway has dummy routines to mimick a Gtransfo.
void setTangentPoint(const Point &tangentPoint)
Resets the projection (or tangent) point.
just here to provide a specialized constructor, and fit.
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane) ...
void dump(std::ostream &stream=std::cout) const
print out of coefficients in a readable form.
std::unique_ptr< Gtransfo > reduceCompo(const Gtransfo *right) const
to be overloaded by derived classes if they can really "reduce" the composition (e.g.
Point getTangentPoint() const
The tangent point (in degrees)
std::unique_ptr< Gtransfo > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all Gtransfo types.
just here to provide specialized constructors. GtransfoLin fit routine.
a virtual (interface) class for geometric transformations.
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
implements an iterative (Gauss-Newton) solver.
GtransfoPoly(const unsigned degree=1)
Default transfo : identity for all degrees (>=1 ).
Point getCenter() const
Center of the frame.
void setDegree(const unsigned degree)
std::unique_ptr< GtransfoPoly > corr
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Private class to handle Gtransfo compositions (i.e.
void computeDerivative(const Point &where, GtransfoLin &derivative, const double step=0.01) const
Computes the local Derivative of a transfo, w.r.t.
BaseTanWcs(const GtransfoLin &pix2Tan, const Point &tangentPoint, const GtransfoPoly *corrections=nullptr)
double fit(const StarMatchList &starMatchList)
guess what
double paramRef(const int i) const
GtransfoLin invert() const
returns the inverse: T1 = T2.invert();
std::unique_ptr< Gtransfo > gtransfoRead(const std::string &fileName)
The virtual constructor from a file.
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
virtual std::unique_ptr< Gtransfo > clone() const =0
returns a copy (allocated by new) of the transformation.
double fit(const StarMatchList &starMatchList)
guess what
virtual void dump(std::ostream &stream=std::cout) const =0
dumps the transfo coefficients to stream.
std::unique_ptr< Gtransfo > roughInverse(const Frame &) const
Overload the "generic routine".
double getHeight() const
size along y axis
UserTransfo(GtransfoFun &fun, const void *userData)
the transfo routine and extra data that it may need.
virtual void pix2TP(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const =0
Transforms from pixel space to tangent plane. deferred to actual implementations. ...
void( GtransfoFun)(const double, const double, double &, double &, const void *)
signature of the user-provided routine that actually does the coordinate transfo for UserTransfo...
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame ®ion) const
Inverse transfo: returns a TanPix2RaDec.
std::unique_ptr< Gtransfo > reduceCompo(const Gtransfo *right) const
to be overloaded by derived classes if they can really "reduce" the composition (e.g.
virtual void apply(const double xIn, const double yIn, double &xOut, double &yOut) const =0
Point getCrPix() const
the CRPIX values (this is WCS jargon), in 0-based coordinates
void operator=(const BaseTanWcs &original)
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.