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);
429 gtransfo->
apply(x, y, xtr, ytr);
430 auto tp = std::make_shared<BaseStar>(xtr, ytr, 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) {
457 monomial[k] = xx * yy;
467 unsigned old_nterms = _nterms;
468 _nterms = (_degree + 1) * (_degree + 2) / 2;
471 vector<double> old_coeffs = _coeffs;
473 _coeffs.resize(2 * _nterms);
476 for (
unsigned k = 0; k < _nterms; ++k) _coeffs[k] = 0;
478 unsigned kmax = min(old_nterms, _nterms);
479 for (
unsigned k = 0; k < kmax; ++k) {
480 _coeffs[k] = old_coeffs[k];
481 _coeffs[k + _nterms] = old_coeffs[k + old_nterms];
496 double monomials[_nterms];
497 computeMonomials(xIn, yIn, monomials);
501 const double *c = &_coeffs[0];
502 const double *pm = &monomials[0];
504 for (
int k = _nterms; k--;) xOut += (*(pm++)) * (*(c++));
506 for (
int k = _nterms; k--;) yOut += (*(pm++)) * (*(c++));
513 derivative.
dx() = derivative.
dy() = 0;
517 double dermx[2 * _nterms];
518 double *dermy = dermx + _nterms;
519 double xin = where.
x;
520 double yin = where.
y;
524 for (
unsigned ix = 0; ix <= _degree; ++ix) {
525 unsigned k = (ix) * (ix + 1) / 2;
527 dermx[k] = ix * xxm1;
531 for (
unsigned iy = 1; iy <= _degree - ix; ++iy) {
532 dermx[k] = ix * xxm1 * yym1 * yin;
533 dermy[k] = iy * xx * yym1;
538 if (ix >= 1) xxm1 *= xin;
544 const double *mx = &dermx[0];
545 const double *my = &dermy[0];
546 const double *c = &_coeffs[0];
548 double a11 = 0, a12 = 0;
549 for (
int k = _nterms; k--;) {
550 a11 += (*(mx++)) * (*c);
551 a12 += (*(my++)) * (*(c++));
553 derivative.
a11() = a11;
554 derivative.
a12() = a12;
556 double a21 = 0, a22 = 0;
559 for (
int k = _nterms; k--;) {
560 a21 += (*(mx++)) * (*c);
561 a22 += (*(my++)) * (*(c++));
563 derivative.
a21() = a21;
564 derivative.
a22() = a22;
580 double monomials[_nterms];
584 double dermx[2 * _nterms];
585 double *dermy = dermx + _nterms;
591 for (
unsigned ix = 0; ix <= _degree; ++ix) {
592 unsigned k = (ix) * (ix + 1) / 2;
594 dermx[k] = ix * xxm1;
600 for (
unsigned iy = 1; iy <= _degree - ix; ++iy) {
601 monomials[k] = xx * yy;
602 dermx[k] = ix * xxm1 * yy;
603 dermy[k] = iy * xx * yym1;
609 if (ix >= 1) xxm1 *= xin;
613 double xout = 0, yout = 0;
614 const double *c = &_coeffs[0];
615 const double *pm = &monomials[0];
616 for (
int k = _nterms; k--;) xout += (*(pm++)) * (*(c++));
618 for (
int k = _nterms; k--;) yout += (*(pm++)) * (*(c++));
624 const double *mx = &dermx[0];
625 const double *my = &dermy[0];
626 double a11 = 0, a12 = 0;
627 for (
int k = _nterms; k--;) {
628 a11 += (*(mx++)) * (*c);
629 a12 += (*(my++)) * (*(c++));
632 double a21 = 0, a22 = 0;
635 for (
int k = _nterms; k--;) {
636 a21 += (*(mx++)) * (*c);
637 a22 += (*(my++)) * (*(c++));
641 res.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
642 res.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
643 res.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
652 assert((degX + degY <= _degree) && whichCoord < 2);
656 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
660 assert((degX + degY <= _degree) && whichCoord < 2);
661 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
666 assert(whichCoord < 2);
667 if (degX + degY <= _degree)
668 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
674 assert(
unsigned(i) < 2 * _nterms);
679 assert(
unsigned(i) < 2 * _nterms);
685 computeMonomials(where.
x, where.
y, dx);
686 for (
unsigned k = 0; k < _nterms; ++k) {
687 dy[_nterms + k] = dx[k];
688 dx[_nterms + k] = dy[k] = 0;
693 static string monomialString(
const unsigned powX,
const unsigned powY) {
695 if (powX + powY) ss <<
"*";
696 if (powX > 0) ss <<
"x";
697 if (powX > 1) ss <<
"^" << powX;
698 if (powY > 0) ss <<
"y";
699 if (powY > 1) ss <<
"^" << powY;
704 for (
unsigned ic = 0; ic < 2; ++ic) {
709 for (
unsigned p = 0; p <= _degree; ++p)
710 for (
unsigned py = 0; py <= p; ++py) {
711 if (p + py != 0) stream <<
" + ";
712 stream <<
coeff(p - py, py, ic) << monomialString(p - py, py);
716 if (_degree > 0) stream <<
" Linear determinant = " <<
determinant() << endl;
732 static GtransfoLin shiftAndNormalize(
const StarMatchList &starMatchList) {
738 for (
auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
739 const StarMatch &a_match = *it;
740 const Point &point1 = a_match.point1;
747 if (count == 0)
return GtransfoLin();
751 double xspan = 3.5 * sqrt(x2 / count - sqr(xav));
752 double yspan = 3.5 * sqrt(y2 / count - sqr(yav));
753 return GtransfoLinScale(2. / xspan, 2. / yspan) * GtransfoLinShift(-xav, -yav);
756 static double sq(
double x) {
return x * x; }
758 double GtransfoPoly::computeFit(
const StarMatchList &starMatchList,
const Gtransfo &shiftToCenter,
759 const bool useErrors) {
760 Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
762 Eigen::VectorXd B(2 * _nterms);
765 double monomials[_nterms];
766 for (
auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
767 const StarMatch &a_match = *it;
768 Point tmp = shiftToCenter.apply(a_match.point1);
769 FatPoint point1(tmp, a_match.point1.vx, a_match.point1.vy, a_match.point1.vxy);
770 const FatPoint &point2 = a_match.point2;
771 double wxx, wyy, wxy;
773 computeMonomials(point1.x, point1.y, monomials);
776 double vxx = (tr1.vx + point2.vx);
777 double vyy = (tr1.vy + point2.vy);
778 double vxy = (tr1.vxy + point2.vxy);
779 double det = vxx * vyy - vxy * vxy;
786 apply(point1.x, point1.y, tr1.x, tr1.y);
788 double resx = point2.x - tr1.x;
789 double resy = point2.y - tr1.y;
790 sumr2 += wxx * sq(resx) + wyy * sq(resy) + 2 * wxy * resx * resy;
792 double bxcoeff = wxx * resx + wxy * resy;
793 double bycoeff = wyy * resy + wxy * resx;
794 for (
unsigned j = 0; j < _nterms; ++j) {
795 for (
unsigned i = j; i < _nterms; ++i) {
796 A(i, j) += wxx * monomials[i] * monomials[j];
797 A(i + _nterms, j + _nterms) += wyy * monomials[i] * monomials[j];
798 A(j, i + _nterms) = A(i, j + _nterms) += wxy * monomials[i] * monomials[j];
800 B(j) += bxcoeff * monomials[j];
801 B(j + _nterms) += bycoeff * monomials[j];
804 Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
806 if (factor.info() != Eigen::Success) {
807 LOGL_ERROR(_log,
"GtransfoPoly::fit could not factorize");
811 Eigen::VectorXd sol = factor.solve(B);
812 for (
unsigned k = 0; k < 2 * _nterms; ++k) _coeffs[k] += sol(k);
813 if (starMatchList.size() == _nterms)
return 0;
814 return (sumr2 - B.dot(sol));
818 if (starMatchList.size() < _nterms) {
819 LOGLS_FATAL(_log,
"GtransfoPoly::fit trying to fit a polynomial transfo of degree "
820 << _degree <<
" with only " << starMatchList.size() <<
" matches.");
824 GtransfoPoly conditionner = shiftAndNormalize(starMatchList);
826 computeFit(starMatchList, conditionner,
false);
827 computeFit(starMatchList, conditionner,
true);
828 double chi2 = computeFit(starMatchList, conditionner,
true);
830 (*this) = (*this) * conditionner;
831 if (starMatchList.size() == _nterms)
return 0;
839 return std::unique_ptr<Gtransfo>(
new GtransfoLin((*
this) * (*p)));
841 return std::unique_ptr<Gtransfo>(
new GtransfoPoly((*
this) * (*p)));
843 return std::unique_ptr<Gtransfo>(
nullptr);
858 vector<long double> coeffs;
861 PolyXY(
const int degree) : degree(degree), nterms((degree + 1) * (degree + 2) / 2) {
862 coeffs.reserve(nterms);
863 coeffs.insert(coeffs.begin(), nterms, 0L);
869 : degree(gtransfoPoly.
getDegree()), nterms((degree + 1) * (degree + 2) / 2), coeffs(nterms, 0L) {
870 for (
unsigned px = 0; px <= degree; ++px)
871 for (
unsigned py = 0; py <= degree - px; ++py)
872 coeff(px, py) = gtransfoPoly.
coeff(px, py, whichCoord);
875 long double coeff(
const unsigned powX,
const unsigned powY)
const {
876 assert(powX + powY <= degree);
877 return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
880 long double &
coeff(
const unsigned powX,
const unsigned powY) {
881 assert(powX + powY <= degree);
882 return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
888 static void operator+=(PolyXY &left,
const PolyXY &right) {
889 unsigned rdeg = right.getDegree();
890 assert(left.getDegree() >= rdeg);
891 for (
unsigned i = 0; i <= rdeg; ++i)
892 for (
unsigned j = 0; j <= rdeg - i; ++j) left.coeff(i, j) += right.coeff(i, j);
896 static PolyXY operator*(
const long double &a,
const PolyXY &polyXY) {
897 PolyXY result(polyXY);
899 unsigned degree = polyXY.getDegree();
900 for (
unsigned i = 0; i <= degree; ++i)
901 for (
unsigned j = 0; j <= degree - i; ++j) result.coeff(i, j) *= a;
906 static PolyXY product(
const PolyXY &p1,
const PolyXY &p2) {
908 unsigned deg2 = p2.getDegree();
909 PolyXY result(deg1 + deg2);
910 for (
unsigned i1 = 0; i1 <= deg1; ++i1)
911 for (
unsigned j1 = 0; j1 <= deg1 - i1; ++j1)
912 for (
unsigned i2 = 0; i2 <= deg2; ++i2)
913 for (
unsigned j2 = 0; j2 <= deg2 - i2; ++j2)
914 result.coeff(i1 + i2, j1 + j2) += p1.coeff(i1, j1) * p2.coeff(i2, j2);
919 static void computePowers(
const PolyXY &polyXY,
const unsigned maxP, vector<PolyXY> &powers) {
920 powers.reserve(maxP + 1);
921 powers.push_back(PolyXY(0));
922 powers[0].coeff(0, 0) = 1L;
923 for (
unsigned k = 1; k <= maxP; ++k) powers.push_back(product(powers[k - 1], polyXY));
927 static PolyXY composition(
const PolyXY &polyXY,
const PolyXY &polyX,
const PolyXY &polyY) {
929 PolyXY result(pdeg * max(polyX.getDegree(), polyY.getDegree()));
930 vector<PolyXY> pXPowers;
931 vector<PolyXY> pYPowers;
932 computePowers(polyX, pdeg, pXPowers);
933 computePowers(polyY, pdeg, pYPowers);
934 for (
unsigned px = 0; px <= pdeg; ++px)
935 for (
unsigned py = 0; py <= pdeg - px; ++py)
936 result += polyXY.coeff(px, py) * product(pXPowers.at(px), pYPowers.at(py));
952 PolyXY rx(composition(plx, prx, pry));
953 PolyXY ry(composition(ply, prx, pry));
957 for (
unsigned px = 0; px <= result._degree; ++px)
958 for (
unsigned py = 0; py <= result._degree - px; ++py) {
966 if (_degree >= right._degree) {
968 for (
unsigned i = 0; i <= right._degree; ++i)
969 for (
unsigned j = 0; j <= right._degree - i; ++j) {
975 return (right + (*
this));
980 for (
unsigned i = 0; i <= res._degree; ++i)
981 for (
unsigned j = 0; j <= res._degree - i; ++j) {
989 s <<
" GtransfoPoly 1" << endl;
990 s <<
"degree " << _degree << endl;
991 int oldprec = s.precision();
992 s << setprecision(12);
993 for (
unsigned k = 0; k < 2 * _nterms; ++k) s << _coeffs[k] <<
' ';
995 s << setprecision(oldprec);
1002 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
" GtransfoPoly::read : format is not 1 ");
1005 s >> degree >> _degree;
1006 if (degree !=
"degree")
1007 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1008 " GtransfoPoly::read : expecting \"degree\" and found " + degree);
1010 for (
unsigned k = 0; k < 2 * _nterms; ++k) s >> _coeffs[k];
1014 const double precision) {
1017 double stepx = frame.
getWidth() / (nx + 1);
1019 double stepy = frame.
getHeight() / (ny + 1);
1020 for (
unsigned i = 0; i < nx; ++i)
1021 for (
unsigned j = 0; j < ny; ++j) {
1022 Point in((i + 0.5) * stepx, (j + 0.5) * stepy);
1024 sm.push_back(
StarMatch(out, in,
nullptr,
nullptr));
1026 unsigned npairs = sm.size();
1029 std::unique_ptr<GtransfoPoly> poly;
1030 for (degree = 1; degree <= maxdeg; ++degree) {
1035 for (
auto const &i : sm) chi2 += i.point2.computeDist2(poly->
apply((i.point1)));
1036 if (chi2 / npairs < precision * precision)
break;
1038 if (degree > maxdeg)
1039 LOGLS_WARN(_log,
"inversePolyTransfo: Reached max degree without reaching requested precision: "
1050 const double A21,
const double A22)
1062 throw pexExcept::InvalidParameterError(
1063 "Trying to build a GtransfoLin from a higher order transfo. Aborting. ");
1084 derivative.
coeff(0, 0, 0) = 0;
1085 derivative.
coeff(0, 0, 1) = 0;
1101 double d = (a11 * a22 - a12 *
a21);
1104 "GtransfoLin::invert singular transformation: transfo contents will be dumped to stderr.");
1108 GtransfoLin result(0, 0, a22 / d, -a12 / d, -a21 / d, a11 / d);
1121 throw pexExcept::NotFoundError(
"GTransfoLinRot::fit not implemented! aborting");
1125 int npairs = starMatchList.size();
1127 LOGLS_FATAL(_log,
"GtransfoLinShift::fit trying to fit a linear transfo with only " << npairs
1134 Eigen::VectorXd B(2);
1136 Eigen::MatrixXd A(2, 2);
1139 for (
auto const &it : starMatchList) {
1140 const FatPoint &point1 = it.point1;
1141 const FatPoint &point2 = it.point2;
1142 double deltax = point2.
x - point1.
x;
1143 double deltay = point2.
y - point1.
y;
1144 double vxx = point1.
vx + point2.
vx;
1145 double vyy = point1.
vy + point2.
vy;
1146 double vxy = point1.
vxy + point2.
vxy;
1147 double det = vxx * vyy - vxy * vxy;
1148 double wxx = vyy / det;
1149 double wyy = vxx / det;
1150 double wxy = -vxy / det;
1151 B(0) += deltax * wxx + wxy * deltay;
1152 B(1) += deltay * wyy + wxy * deltax;
1156 sumr2 += deltax * deltax * wxx + deltay * deltay * wyy + 2. * wxy * deltax * deltay;
1158 double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
1159 if (det <= 0)
return -1;
1160 double tmp = A(0, 0);
1161 A(0, 0) = A(1, 1) / det;
1162 A(1, 1) = tmp / det;
1163 A(0, 1) = A(1, 0) = -A(0, 1) / det;
1164 Eigen::VectorXd sol = A * B;
1166 return (sumr2 - sol.dot(B));
1170 double c = scaleFactor * cos(angleRad);
1171 double s = scaleFactor * sin(angleRad);
1177 Point a_point(0., 0.);
1178 if (center) a_point = *center;
1182 dx() = a_point.
x -
Dx();
1183 dy() = a_point.
y -
dy();
1186 static double deg2rad(
double degree) {
return degree *
M_PI / 180.; }
1188 static double rad2deg(
double rad) {
return rad * 180. /
M_PI; }
1213 ra0 = deg2rad(tangentPoint.
x);
1214 dec0 = deg2rad(tangentPoint.
y);
1251 LOGL_WARN(_log,
"No sidereal coordinates at pole!");
1256 double rat =
ra0 + atan2(l, dect);
1257 dect = atan(cos(rat -
ra0) * (m *
cos0 + sin0) / dect);
1260 if (rat < 0.0) rat += (2. *
M_PI);
1262 xOut = rad2deg(rat);
1263 yOut = rad2deg(dect);
1281 return Point(inverse.
Dx(), inverse.
Dy());
1290 :
BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1297 if (lin && lin->
getDegree() == 1)
return std::unique_ptr<Gtransfo>(
new TanPix2RaDec((*
this) * (*lin)));
1298 return std::unique_ptr<Gtransfo>(
nullptr);
1308 if (
corr !=
nullptr) {
1309 LOGL_WARN(_log,
"You are inverting a TanPix2RaDec with corrections.");
1310 LOGL_WARN(_log,
"The inverse you get ignores the corrections!");
1323 return std::unique_ptr<Gtransfo>(
new GtransfoInverse(
this, precision, region));
1337 double xtmp = xTangentPlane;
1338 double ytmp = yTangentPlane;
1339 corr->apply(xtmp, ytmp, xTangentPlane, yTangentPlane);
1348 stream <<
" TanPix2RaDec, lin part :" << endl <<
linPix2Tan;
1350 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1352 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y << endl;
1353 if (
corr) stream <<
"PV correction: " << endl << *
corr;
1365 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1366 "TanPix2RaDec::fit is NOT implemented (although it is doable)) ");
1374 :
BaseTanWcs(pix2Tan, tangentPoint, corrections) {}
1391 return std::unique_ptr<Gtransfo>(
new GtransfoInverse(
this, precision, region));
1402 double &yTangentPlane)
const {
1406 corr->apply(xPixel, yPixel, xtmp, ytmp);
1417 stream <<
" TanSipPix2RaDec, lin part :" << endl <<
linPix2Tan;
1419 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1421 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y << endl;
1422 if (
corr) stream <<
"PV correction: " << endl << *
corr;
1434 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1435 "TanSipPix2RaDec::fit is NOT implemented (although it is doable)) ");
1448 ra0 = deg2rad(tangentPoint.
x);
1449 dec0 = deg2rad(tangentPoint.
y);
1481 double ra = deg2rad(in.
x);
1482 double dec = deg2rad(in.
y);
1483 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1484 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1488 double coss = cos(dec);
1489 double sins = sin(dec);
1490 double sinda = sin(ra - ra0);
1491 double cosda = cos(ra - ra0);
1492 double l = sinda * coss;
1493 double m = sins * sin0 + coss * cos0 * cosda;
1495 m = (sins * cos0 - coss * sin0 * cosda) / m;
1499 sq(sin0) - sq(coss) + sq(coss * cos0) * (1 + sq(cosda)) + 2 * sins * sin0 * coss * cos0 * cosda;
1500 double a11 = coss * (cosda * sins * sin0 + coss * cos0) / deno;
1501 double a12 = -sinda * sin0 / deno;
1502 double a21 = coss * sinda * sins / deno;
1503 double a22 = cosda / deno;
1506 tmp.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
1507 tmp.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
1508 tmp.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
1518 double ra = deg2rad(xIn);
1519 double dec = deg2rad(yIn);
1520 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1521 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1524 double coss = cos(dec);
1525 double sins = sin(dec);
1526 double l = sin(ra - ra0) * coss;
1527 double m = sins * sin0 + coss * cos0 * cos(ra - ra0);
1529 m = (sins * cos0 - coss * sin0 * cos(ra - ra0)) / m;
1533 linTan2Pix.
apply(l, m, xOut, yOut);
1540 stream <<
" tan2pix " << linTan2Pix <<
" tangent point " << tp.
x <<
' ' << tp.
y << endl;
1552 return std::unique_ptr<Gtransfo>(
new TanRaDec2Pix(*
this));
1556 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1557 "TanRaDec2Pix::fit is NOT implemented (although it is doable)) ");
1565 : _userFun(userFun), _userData(userData) {}
1568 _userFun(xIn, yIn, xOut, yOut, _userData);
1572 stream <<
"UserTransfo with user function @ " << _userFun <<
"and userData@ " << _userData << endl;
1576 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1577 "UserTransfo::fit is NOT implemented (and will never be)) ");
1582 return std::unique_ptr<Gtransfo>(
new UserTransfo(*
this));
1588 ifstream s(fileName.c_str());
1590 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
" gtransfoRead : cannot open " + fileName);
1595 }
catch (pex::exceptions::InvalidParameterError e) {
1596 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1597 std::string(e.what()) +
" in file " + fileName);
1605 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1606 "gtransfoRead : could not find a Gtransfotype");
1607 if (type ==
"GtransfoIdentity") {
1610 return std::move(res);
1611 }
else if (type ==
"GtransfoPoly") {
1614 return std::move(res);
1616 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
1617 " 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. Step is used for numerical derivation.
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.