42 #include "Eigen/Cholesky" 49 LOG_LOGGER _log =
LOG_GET(
"jointcal.AstrometryTransform");
58 if (shift ==
nullptr)
return false;
60 static const double eps = 1e-5;
62 double dx = shift->
coeff(0, 0, 0);
63 double dy = shift->
coeff(0, 0, 1);
65 static Point dumb(4000, 4000);
67 fabs(dumb.
x + dx - shift->
apply(dumb).x) < eps &&
fabs(dumb.
y + dy - shift->
apply(dumb).y) < eps)
75 Frame AstrometryTransform::apply(
Frame const &inputframe,
bool inscribed)
const {
77 double xtmin1, xtmax1, ytmin1, ytmax1;
78 apply(inputframe.
xMin, inputframe.
yMin, xtmin1, ytmin1);
79 apply(inputframe.
xMax, inputframe.
yMax, xtmax1, ytmax1);
83 double xtmin2, xtmax2, ytmin2, ytmax2;
84 apply(inputframe.
xMin, inputframe.
yMax, xtmin2, ytmax2);
85 apply(inputframe.
xMax, inputframe.
yMin, xtmax2, ytmin2);
89 if (inscribed)
return fr1 * fr2;
98 double AstrometryTransform::getJacobian(
const double x,
const double y)
const {
100 double eps = x * 0.01;
101 if (eps == 0) eps = 0.01;
104 apply(x + eps, y, dxdx, dydx);
108 apply(x, y + eps, dxdy, dydy);
111 return ((dxdx * dydy - dxdy * dydx) / (eps * eps));
118 const double step)
const {
122 apply(x, y, xp0, yp0);
125 apply(x + step, y, xp, yp);
126 derivative.
a11() = (xp - xp0) / step;
127 derivative.
a21() = (yp - yp0) / step;
128 apply(x, y + step, xp, yp);
129 derivative.
a12() = (xp - xp0) / step;
130 derivative.
a22() = (yp - yp0) / step;
136 const double step)
const {
137 Point outwhere = apply(where);
139 computeDerivative(where, der, step);
150 computeDerivative(in, der, 0.01);
151 double a11 = der.
A11();
152 double a22 = der.
A22();
153 double a21 = der.
A21();
154 double a12 = der.
A12();
155 res.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
156 res.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
157 res.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
161 void AstrometryTransform::transformErrors(
Point const &where,
const double *vIn,
double *vOut)
const {
163 computeDerivative(where, der, 0.01);
164 double a11 = der.
A11();
165 double a22 = der.
A22();
166 double a21 = der.
A21();
167 double a12 = der.
A12();
182 double b11 = a11 * vIn[xx] + a12 * vIn[xy];
183 double b22 = a21 * vIn[xy] + a22 * vIn[yy];
184 double b12 = a11 * vIn[xy] + a12 * vIn[yy];
185 double b21 = a21 * vIn[xx] + a22 * vIn[xy];
189 vOut[xx] = b11 * a11 + b12 * a12;
190 vOut[xy] = b11 * a21 + b12 * a22;
191 vOut[yy] = b21 * a21 + b22 * a22;
197 Point centerIn = apply(centerOut);
215 void AstrometryTransform::getParams(
double *params)
const {
216 int npar = getNpar();
217 for (
int i = 0; i < npar; ++i) params[i] = paramRef(i);
220 void AstrometryTransform::offsetParams(Eigen::VectorXd
const &delta) {
221 int npar = getNpar();
222 for (
int i = 0; i < npar; ++i) paramRef(i) += delta[i];
225 double AstrometryTransform::paramRef(
const int)
const {
227 std::string(
"AstrometryTransform::paramRef should never be called "));
230 double &AstrometryTransform::paramRef(
const int) {
232 "AstrometryTransform::paramRef should never be called ");
235 void AstrometryTransform::paramDerivatives(
Point const &,
double *,
double *)
const {
237 "AstrometryTransform::paramDerivatives() should never be called ");
241 transform.
dump(stream);
245 void AstrometryTransform::write(
const std::string &fileName)
const {
252 "AstrometryTransform::write, something went wrong for file " + fileName);
255 void AstrometryTransform::write(
ostream &stream)
const {
258 "AstrometryTransform::write(ostream), should never be called. MEans that it is missing in some " 274 const Frame ®ion);
278 void apply(
const double xIn,
const double yIn,
double &xOut,
double &yOut)
const;
280 void dump(
ostream &stream)
const;
293 return _direct->clone();
303 const Frame ®ion)
const {
308 const double precision,
const Frame ®ion) {
309 _direct = direct->
clone();
310 _roughInverse = _direct->roughInverse(region);
311 precision2 = precision * precision;
316 _direct = model._direct->clone();
317 _roughInverse = model._roughInverse->clone();
318 precision2 = model.precision2;
324 _direct = model._direct->clone();
325 _roughInverse = model._roughInverse->clone();
326 precision2 = model.precision2;
331 Point outGuess = _roughInverse->apply(in);
338 Point inGuess = _direct->apply(outGuess);
339 _direct->computeDerivative(outGuess, directDer);
341 double xShift, yShift;
342 reverseDer.
apply(xIn - inGuess.
x, yIn - inGuess.
y, xShift, yShift);
343 outGuess.
x += xShift;
344 outGuess.
y += yShift;
345 move2 = xShift * xShift + yShift * yShift;
346 }
while ((move2 > precision2) && (loop < maxloop));
347 if (loop == maxloop)
LOGLS_WARN(_log,
"Problems applying AstrometryTransformInverse at " << in);
353 stream <<
" AstrometryTransformInverse of :" <<
endl << *_direct <<
endl;
358 "Cannot fit a AstrometryTransformInverse. Use StarMatchList::inverseTransform instead.");
381 void apply(
const double xIn,
const double yIn,
double &xOut,
double &yOut)
const;
393 _first = first.
clone();
394 _second = second.
clone();
398 double &yOut)
const {
400 _first->apply(xIn, yIn, xout, yout);
401 _second->apply(xout, yout, xOut, yOut);
405 _first->dump(stream);
406 _second->dump(stream);
411 return _first->fit(starMatchList);
415 return std::make_unique<AstrometryTransformComposition>(*_second, *_first);
431 if (composition ==
nullptr)
432 return std::make_unique<AstrometryTransformComposition>(left, right);
439 const double)
const {
444 const double)
const {
450 return std::make_shared<ast::UnitMap>(2);
454 stream <<
"AstrometryTransformIdentity 1" <<
endl;
462 " AstrometryTransformIdentity::read : format is not 1 ");
470 _nterms = (order + 1) * (order + 2) / 2;
473 _coeffs.
resize(2 * _nterms, 0.);
483 const Frame &frame,
unsigned order,
488 for (
double x = frame.
xMin + step / 2;
x <= frame.
xMax;
x += step)
489 for (
double y = frame.
yMin + step / 2;
y <= frame.
yMax;
y += step) {
490 auto pix = std::make_shared<BaseStar>(
x,
y, 0, 0);
492 transform->
apply(x, y, xtr, ytr);
493 auto tp = std::make_shared<BaseStar>(xtr, ytr, 0, 0);
506 unsigned const order,
unsigned const nSteps) {
508 double xStart = domain.
xMin;
509 double yStart = domain.
yMin;
510 double xStep = domain.
getWidth() / (nSteps + 1);
511 double yStep = domain.
getHeight() / (nSteps + 1);
512 for (
unsigned i = 0; i < nSteps; ++i) {
513 for (
unsigned j = 0; j < nSteps; ++j) {
516 afw::geom::Point2D inAfw(in.
x, in.
y);
517 afw::geom::Point2D outAfw = transform->applyForward(inAfw);
523 poly.
fit(starMatchList);
527 void AstrometryTransformPolynomial::computeMonomials(
double xIn,
double yIn,
double *monomial)
const {
538 for (
unsigned ix = 0; ix <= _order; ++ix) {
540 unsigned k = ix * (ix + 1) / 2;
541 for (
unsigned iy = 0; iy <= _order - ix; ++iy) {
542 monomial[k] = xx * yy;
552 unsigned old_nterms = _nterms;
553 _nterms = (_order + 1) * (_order + 2) / 2;
558 _coeffs.
resize(2 * _nterms);
561 for (
unsigned k = 0; k < _nterms; ++k) _coeffs[k] = 0;
563 unsigned kmax =
min(old_nterms, _nterms);
564 for (
unsigned k = 0; k < kmax; ++k) {
565 _coeffs[k] = old_coeffs[k];
566 _coeffs[k + _nterms] = old_coeffs[k + old_nterms];
572 double &yOut)
const {
582 double monomials[_nterms];
583 computeMonomials(xIn, yIn, monomials);
587 const double *c = &_coeffs[0];
588 const double *pm = &monomials[0];
590 for (
int k = _nterms; k--;) xOut += (*(pm++)) * (*(c++));
592 for (
int k = _nterms; k--;) yOut += (*(pm++)) * (*(c++));
601 derivative.
dx() = derivative.
dy() = 0;
605 double dermx[2 * _nterms];
606 double *dermy = dermx + _nterms;
607 double xin = where.
x;
608 double yin = where.
y;
612 for (
unsigned ix = 0; ix <= _order; ++ix) {
613 unsigned k = (ix) * (ix + 1) / 2;
615 dermx[k] = ix * xxm1;
619 for (
unsigned iy = 1; iy <= _order - ix; ++iy) {
620 dermx[k] = ix * xxm1 * yym1 * yin;
621 dermy[k] = iy * xx * yym1;
626 if (ix >= 1) xxm1 *= xin;
632 const double *mx = &dermx[0];
633 const double *my = &dermy[0];
634 const double *c = &_coeffs[0];
636 double a11 = 0, a12 = 0;
637 for (
int k = _nterms; k--;) {
638 a11 += (*(mx++)) * (*c);
639 a12 += (*(my++)) * (*(c++));
641 derivative.
a11() = a11;
642 derivative.
a12() = a12;
644 double a21 = 0, a22 = 0;
647 for (
int k = _nterms; k--;) {
648 a21 += (*(mx++)) * (*c);
649 a22 += (*(my++)) * (*(c++));
651 derivative.
a21() = a21;
652 derivative.
a22() = a22;
668 double monomials[_nterms];
672 double dermx[2 * _nterms];
673 double *dermy = dermx + _nterms;
679 for (
unsigned ix = 0; ix <= _order; ++ix) {
680 unsigned k = (ix) * (ix + 1) / 2;
682 dermx[k] = ix * xxm1;
688 for (
unsigned iy = 1; iy <= _order - ix; ++iy) {
689 monomials[k] = xx * yy;
690 dermx[k] = ix * xxm1 * yy;
691 dermy[k] = iy * xx * yym1;
697 if (ix >= 1) xxm1 *= xin;
701 double xout = 0, yout = 0;
702 const double *c = &_coeffs[0];
703 const double *pm = &monomials[0];
704 for (
int k = _nterms; k--;) xout += (*(pm++)) * (*(c++));
706 for (
int k = _nterms; k--;) yout += (*(pm++)) * (*(c++));
712 const double *mx = &dermx[0];
713 const double *my = &dermy[0];
714 double a11 = 0, a12 = 0;
715 for (
int k = _nterms; k--;) {
716 a11 += (*(mx++)) * (*c);
717 a12 += (*(my++)) * (*(c++));
720 double a21 = 0, a22 = 0;
723 for (
int k = _nterms; k--;) {
724 a21 += (*(mx++)) * (*c);
725 a22 += (*(my++)) * (*(c++));
729 res.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
730 res.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
731 res.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
740 const unsigned whichCoord)
const {
741 assert((degX + degY <= _order) && whichCoord < 2);
745 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
749 const unsigned whichCoord) {
750 assert((degX + degY <= _order) && whichCoord < 2);
751 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
755 const unsigned whichCoord)
const {
757 assert(whichCoord < 2);
758 if (degX + degY <= _order)
759 return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
765 assert(
unsigned(i) < 2 * _nterms);
770 assert(
unsigned(i) < 2 * _nterms);
776 computeMonomials(where.
x, where.
y, dx);
777 for (
unsigned k = 0; k < _nterms; ++k) {
778 dy[_nterms + k] = dx[k];
779 dx[_nterms + k] = dy[k] = 0;
784 static string monomialString(
const unsigned powX,
const unsigned powY) {
786 if (powX + powY) ss <<
"*";
787 if (powX > 0) ss <<
"x";
788 if (powX > 1) ss <<
"^" << powX;
789 if (powY > 0) ss <<
"y";
790 if (powY > 1) ss <<
"^" << powY;
797 for (
unsigned ic = 0; ic < 2; ++ic) {
802 for (
unsigned p = 0; p <= _order; ++p)
803 for (
unsigned py = 0; py <= p; ++py) {
804 if (p + py != 0) stream <<
" + ";
805 stream <<
coeff(p - py, py, ic) << monomialString(p - py, py);
809 if (_order > 0) stream <<
" Linear determinant = " <<
determinant() <<
endl;
832 for (
auto it = starMatchList.
begin(); it != starMatchList.
end(); ++it) {
851 static double sq(
double x) {
return x *
x; }
853 double AstrometryTransformPolynomial::computeFit(
StarMatchList const &starMatchList,
855 const bool useErrors) {
856 Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
858 Eigen::VectorXd B(2 * _nterms);
861 double monomials[_nterms];
862 for (
auto it = starMatchList.
begin(); it != starMatchList.
end(); ++it) {
867 double wxx, wyy, wxy;
869 computeMonomials(point1.
x, point1.
y, monomials);
872 double vxx = (tr1.
vx + point2.
vx);
873 double vyy = (tr1.
vy + point2.
vy);
874 double vxy = (tr1.
vxy + point2.
vxy);
875 double det = vxx * vyy - vxy * vxy;
884 double resx = point2.
x - tr1.
x;
885 double resy = point2.
y - tr1.
y;
886 sumr2 += wxx * sq(resx) + wyy * sq(resy) + 2 * wxy * resx * resy;
888 double bxcoeff = wxx * resx + wxy * resy;
889 double bycoeff = wyy * resy + wxy * resx;
890 for (
unsigned j = 0; j < _nterms; ++j) {
891 for (
unsigned i = j; i < _nterms; ++i) {
892 A(i, j) += wxx * monomials[i] * monomials[j];
893 A(i + _nterms, j + _nterms) += wyy * monomials[i] * monomials[j];
894 A(j, i + _nterms) = A(i, j + _nterms) += wxy * monomials[i] * monomials[j];
896 B(j) += bxcoeff * monomials[j];
897 B(j + _nterms) += bycoeff * monomials[j];
900 Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
902 if (factor.info() != Eigen::Success) {
903 LOGL_ERROR(_log,
"AstrometryTransformPolynomial::fit could not factorize");
907 Eigen::VectorXd sol = factor.solve(B);
908 for (
unsigned k = 0; k < 2 * _nterms; ++k) _coeffs[k] += sol(k);
909 if (starMatchList.
size() == _nterms)
return 0;
910 return (sumr2 - B.dot(sol));
914 if (starMatchList.
size() < _nterms) {
915 LOGLS_FATAL(_log,
"AstrometryTransformPolynomial::fit trying to fit a polynomial transform of order " 916 << _order <<
" with only " << starMatchList.
size() <<
" matches.");
922 computeFit(starMatchList, conditionner,
false);
923 computeFit(starMatchList, conditionner,
true);
924 double chi2 = computeFit(starMatchList, conditionner,
true);
926 (*this) = (*this) * conditionner;
927 if (starMatchList.
size() == _nterms)
return 0;
934 return std::make_unique<AstrometryTransformLinear>((*
this) * (right));
936 return std::make_unique<AstrometryTransformPolynomial>((*this) * (right));
954 PolyXY(
const int order) : order(order), nterms((order + 1) * (order + 2) / 2) {
962 : order(transform.
getOrder()), nterms((order + 1) * (order + 2) / 2), coeffs(nterms, 0L) {
963 for (
unsigned px = 0; px <= order; ++px)
964 for (
unsigned py = 0; py <= order - px; ++py)
coeff(px, py) = transform.
coeff(px, py, whichCoord);
967 long double coeff(
const unsigned powX,
const unsigned powY)
const {
968 assert(powX + powY <= order);
969 return coeffs.
at((powX + powY) * (powX + powY + 1) / 2 + powY);
972 long double &
coeff(
const unsigned powX,
const unsigned powY) {
973 assert(powX + powY <= order);
974 return coeffs.
at((powX + powY) * (powX + powY + 1) / 2 + powY);
983 for (
unsigned i = 0; i <= rdeg; ++i)
984 for (
unsigned j = 0; j <= rdeg - i; ++j) left.
coeff(i, j) += right.
coeff(i, j);
992 for (
unsigned i = 0; i <= order; ++i)
993 for (
unsigned j = 0; j <= order - i; ++j) result.
coeff(i, j) *= a;
1002 for (
unsigned i1 = 0; i1 <= deg1; ++i1)
1003 for (
unsigned j1 = 0; j1 <= deg1 - i1; ++j1)
1004 for (
unsigned i2 = 0; i2 <= deg2; ++i2)
1005 for (
unsigned j2 = 0; j2 <= deg2 - i2; ++j2)
1006 result.
coeff(i1 + i2, j1 + j2) += p1.
coeff(i1, j1) * p2.
coeff(i2, j2);
1014 powers[0].coeff(0, 0) = 1L;
1015 for (
unsigned k = 1; k <= maxP; ++k) powers.
push_back(product(powers[k - 1], polyXY));
1024 computePowers(polyX, pdeg, pXPowers);
1025 computePowers(polyY, pdeg, pYPowers);
1026 for (
unsigned px = 0; px <= pdeg; ++px)
1027 for (
unsigned py = 0; py <= pdeg - px; ++py)
1028 result += polyXY.
coeff(px, py) * product(pXPowers.
at(px), pYPowers.
at(py));
1045 PolyXY rx(composition(plx, prx, pry));
1046 PolyXY ry(composition(ply, prx, pry));
1050 for (
unsigned px = 0; px <= result._order; ++px)
1051 for (
unsigned py = 0; py <= result._order - px; ++py) {
1060 if (_order >= right._order) {
1062 for (
unsigned i = 0; i <= right._order; ++i)
1063 for (
unsigned j = 0; j <= right._order - i; ++j) {
1069 return (right + (*
this));
1075 for (
unsigned i = 0; i <= res._order; ++i)
1076 for (
unsigned j = 0; j <= res._order - i; ++j) {
1085 return std::make_shared<ast::PolyMap>(toAstPolyMapCoefficients(), inverse->toAstPolyMapCoefficients());
1089 s <<
" AstrometryTransformPolynomial 1" <<
endl;
1090 s <<
"order " << _order << endl;
1093 for (
unsigned k = 0; k < 2 * _nterms; ++k) s << _coeffs[k] <<
' ';
1095 s << setprecision(oldprec);
1103 " AstrometryTransformPolynomial::read : format is not 1 ");
1106 s >> order >> _order;
1107 if (order !=
"order")
1109 " AstrometryTransformPolynomial::read : expecting \"order\" and found " + order);
1111 for (
unsigned k = 0; k < 2 * _nterms; ++k) s >> _coeffs[k];
1114 ndarray::Array<double, 2, 2> AstrometryTransformPolynomial::toAstPolyMapCoefficients()
const {
1115 int nCoeffs = _coeffs.size();
1116 ndarray::Array<double, 2, 2>
result = ndarray::allocate(ndarray::makeVector(nCoeffs, 4));
1118 ndarray::Size k = 0;
1119 for (
unsigned iCoord = 0; iCoord < 2; ++iCoord) {
1120 for (
unsigned p = 0; p <= _order; ++p) {
1121 for (
unsigned py = 0; py <= p; ++py, ++k) {
1122 result[k][0] =
coeff(p - py, py, iCoord);
1123 result[k][1] = iCoord + 1;
1124 result[k][2] = p - py;
1134 Frame const &domain,
1135 double const precision,
1137 unsigned const nSteps) {
1139 double xStart = domain.
xMin;
1140 double yStart = domain.
yMin;
1141 double xStep = domain.
getWidth() / (nSteps - 1);
1142 double yStep = domain.
getHeight() / (nSteps - 1);
1143 for (
unsigned i = 0; i < nSteps; ++i) {
1144 for (
unsigned j = 0; j < nSteps; ++j) {
1145 Point in(xStart + i * xStep, yStart + j * yStep);
1150 unsigned npairs = sm.
size();
1156 for (order = 1; order <= maxOrder; ++order) {
1158 auto success = poly->fit(sm);
1159 if (success == -1) {
1161 errMsg <<
"Cannot fit a polynomial of order " << order <<
" with " << nSteps <<
"^2 points";
1166 for (
auto const &i : sm) chi2 += i.point2.computeDist2(poly->apply((i.point1)));
1167 LOGLS_TRACE(_log,
"inversePoly order " << order <<
": " << chi2 <<
" / " << npairs <<
" = " 1168 << chi2 / npairs <<
" < " << precision * precision);
1170 if (chi2 / npairs < precision * precision)
break;
1173 if (chi2 > oldChi2) {
1174 LOGLS_WARN(_log,
"inversePolyTransform: chi2 increases (" 1175 << chi2 <<
" > " << oldChi2 <<
"); ending fit with order: " << order);
1176 LOGLS_WARN(_log,
"inversePolyTransform: requested precision not reached: " 1177 << chi2 <<
" / " << npairs <<
" = " << chi2 / npairs <<
" < " 1178 << precision * precision);
1189 if (order > maxOrder)
1190 LOGLS_WARN(_log,
"inversePolyTransform: Reached max order without reaching requested precision: " 1191 << chi2 <<
" / " << npairs <<
" = " << chi2 / npairs <<
" < " 1192 << precision * precision);
1202 const double A12,
const double A21,
const double A22)
1216 "Trying to build a AstrometryTransformLinear from a higher order transform. Aborting. ");
1236 const double)
const {
1238 derivative.
coeff(0, 0, 0) = 0;
1239 derivative.
coeff(0, 0, 1) = 0;
1257 double d = (a11 * a22 - a12 *
a21);
1260 "AstrometryTransformLinear::invert singular transformation: transform contents will be " 1261 "dumped to stderr.");
1274 const Frame &)
const {
1283 int npairs = starMatchList.
size();
1285 LOGLS_FATAL(_log,
"AstrometryTransformLinearShift::fit trying to fit a linear transform with only " 1286 << npairs <<
" matches.");
1292 Eigen::VectorXd B(2);
1294 Eigen::MatrixXd A(2, 2);
1297 for (
auto const &it : starMatchList) {
1298 FatPoint const &point1 = it.point1;
1299 FatPoint const &point2 = it.point2;
1300 double deltax = point2.
x - point1.
x;
1301 double deltay = point2.
y - point1.
y;
1302 double vxx = point1.
vx + point2.
vx;
1303 double vyy = point1.
vy + point2.
vy;
1304 double vxy = point1.
vxy + point2.
vxy;
1305 double det = vxx * vyy - vxy * vxy;
1306 double wxx = vyy / det;
1307 double wyy = vxx / det;
1308 double wxy = -vxy / det;
1309 B(0) += deltax * wxx + wxy * deltay;
1310 B(1) += deltay * wyy + wxy * deltax;
1314 sumr2 += deltax * deltax * wxx + deltay * deltay * wyy + 2. * wxy * deltax * deltay;
1316 double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
1317 if (det <= 0)
return -1;
1318 double tmp = A(0, 0);
1319 A(0, 0) = A(1, 1) / det;
1320 A(1, 1) = tmp / det;
1321 A(0, 1) = A(1, 0) = -A(0, 1) / det;
1322 Eigen::VectorXd sol = A * B;
1324 return (sumr2 - sol.dot(B));
1328 const double scaleFactor) {
1329 double c = scaleFactor *
std::cos(angleRad);
1330 double s = scaleFactor *
std::sin(angleRad);
1336 Point a_point(0., 0.);
1337 if (center) a_point = *center;
1341 dx() = a_point.
x -
Dx();
1342 dy() = a_point.
y -
dy();
1345 static double deg2rad(
double degree) {
return degree *
M_PI / 180.; }
1347 static double rad2deg(
double rad) {
return rad * 180. /
M_PI; }
1370 linPixelToTan = pixToTan;
1371 ra0 = deg2rad(tangentPoint.
x);
1372 dec0 = deg2rad(tangentPoint.
y);
1409 LOGL_WARN(_log,
"No sidereal coordinates at pole!");
1418 if (rat < 0.0) rat += (2. *
M_PI);
1420 xOut = rad2deg(rat);
1421 yOut = rad2deg(dect);
1441 return Point(inverse.
Dx(), inverse.
Dy());
1447 : _skyWcs(skyWcs) {}
1450 auto const outCoord = _skyWcs->pixelToSky(afw::geom::Point2D(xIn, yIn));
1451 xOut = outCoord[0].asDegrees();
1452 yOut = outCoord[1].asDegrees();
1456 stream <<
"AstrometryTransformSkyWcs(" << *_skyWcs <<
")";
1471 :
BaseTanWcs(pixToTan, tangentPoint, corrections) {}
1479 return std::make_unique<TanPixelToRaDec>((*this) * (right));
1492 if (
corr !=
nullptr) {
1493 LOGL_WARN(_log,
"You are inverting a TanPixelToRaDec with corrections.");
1494 LOGL_WARN(_log,
"The inverse you get ignores the corrections!");
1505 const Frame ®ion)
const {
1521 double &yTangentPlane)
const {
1525 double xtmp = xTangentPlane;
1526 double ytmp = yTangentPlane;
1527 corr->apply(xtmp, ytmp, xTangentPlane, yTangentPlane);
1539 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y <<
endl;
1541 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y <<
endl;
1542 if (
corr) stream <<
"PV correction: " <<
endl << *
corr;
1555 "TanPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1563 :
BaseTanWcs(pixToTan, tangentPoint, corrections) {}
1579 const Frame ®ion)
1592 double &yTangentPlane)
const {
1596 corr->apply(xPixel, yPixel, xtmp, ytmp);
1610 stream <<
" tangent point " << tp.
x <<
' ' << tp.
y <<
endl;
1612 stream <<
" crpix " << crpix.
x <<
' ' << crpix.
y <<
endl;
1613 if (
corr) stream <<
"PV correction: " <<
endl << *
corr;
1626 "TanSipPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1633 : linTan2Pix(tan2Pix) {
1640 ra0 = deg2rad(tangentPoint.
x);
1641 dec0 = deg2rad(tangentPoint.
y);
1673 double ra = deg2rad(in.
x);
1674 double dec = deg2rad(in.
y);
1675 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1676 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1684 double l = sinda * coss;
1685 double m = sins * sin0 + coss * cos0 * cosda;
1687 m = (sins * cos0 - coss * sin0 * cosda) / m;
1691 sq(sin0) - sq(coss) + sq(coss * cos0) * (1 + sq(cosda)) + 2 * sins * sin0 * coss * cos0 * cosda;
1692 double a11 = coss * (cosda * sins * sin0 + coss * cos0) / deno;
1693 double a12 = -sinda * sin0 / deno;
1694 double a21 = coss * sinda * sins / deno;
1695 double a22 = cosda / deno;
1698 tmp.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
1699 tmp.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
1700 tmp.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
1710 double ra = deg2rad(xIn);
1711 double dec = deg2rad(yIn);
1712 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1713 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1718 double l =
std::sin(ra - ra0) * coss;
1719 double m = sins * sin0 + coss * cos0 *
std::cos(ra - ra0);
1721 m = (sins * cos0 - coss * sin0 *
std::cos(ra - ra0)) / m;
1725 linTan2Pix.
apply(l, m, xOut, yOut);
1734 stream <<
" tan2pix " << linTan2Pix <<
" tangent point " << tp.
x <<
' ' << tp.
y <<
endl;
1753 "TanRaDecToPixel::fit is NOT implemented (although it is doable)) ");
1760 : _userFun(userFun), _userData(userData) {}
1763 _userFun(xIn, yIn, xOut, yOut, _userData);
1767 stream <<
"UserTransform with user function @ " << _userFun <<
"and userData@ " << _userData <<
endl;
1772 "UserTransform::fit is NOT implemented (and will never be)) ");
1786 " astrometryTransformRead : cannot open " + fileName);
1802 "astrometryTransformRead : could not find a AstrometryTransformtype");
1803 if (type ==
"AstrometryTransformIdentity") {
1807 }
else if (type ==
"AstrometryTransformPolynomial") {
1813 " astrometryTransformRead : No reader for AstrometryTransform type " + type);
std::unique_ptr< AstrometryTransform > inverseTransform(const double precision, const Frame ®ion) const
Inverse transform: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if ...
#define LOGL_ERROR(logger, message...)
std::unique_ptr< AstrometryTransform > inverseTransform(const double precision, const Frame ®ion) const
Inverse transform: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if ...
void() AstrometryTransformFun(const double, const double, double &, double &, const void *)
signature of the user-provided routine that actually does the coordinate transform for UserTransform...
the transformation that handles pix to sideral transfos (Gnomonic, possibly with polynomial distortio...
PolyXY(AstrometryTransformPolynomial const &transform, const unsigned whichCoord)
A hanger for star associations.
AstrometryTransformLinear linPixelToTan
virtual char const * what(void) const noexcept
long double & coeff(const unsigned powX, const unsigned powY)
std::unique_ptr< AstrometryTransform > composeAndReduce(AstrometryTransformLinear const &right) const
Return a reduced composition of newTransform = this(right()), or nullptr if it cannot be reduced...
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
bool isIntegerShift(const AstrometryTransform *transform)
Shorthand test to tell if a transform is a simple integer shift.
std::ostream & operator<<(std::ostream &stream, AstrometryTransform const &transform)
Delegates to transform.dump()
double fit(StarMatchList const &starMatchList)
fits a transform to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
table::PointKey< double > crpix
AstrometryTransformPolynomial getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
double fit(StarMatchList const &starMatchList)
Not implemented yet, because we do it otherwise.
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const =0
Transform from pixels to tangent plane (degrees)
#define LOGLS_TRACE(logger, message)
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Transform pixels to ICRS RA, Dec in degrees.
double xMin
coordinate of boundary.
TanPixelToRaDec operator*(AstrometryTransformLinear const &right) const
composition with AstrometryTransformLinear
Extent< double, N > & operator+=(Extent< double, N > &lhs, Extent< int, N > const &rhs) noexcept
#define LOGLS_FATAL(logger, message)
A Point with uncertainties.
void setCorrections(std::unique_ptr< AstrometryTransformPolynomial > corrections)
Assign the correction polynomial (what it means is left to derived classes)
std::unique_ptr< AstrometryTransform > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all AstrometryTransform types.
std::unique_ptr< AstrometryTransformPolynomial > corr
AstrometryTransformLinear getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
double getHeight() const
size along y axis
TanPixelToRaDec inverted() const
exact typed inverse:
void setTangentPoint(Point const &tangentPoint)
Resets the projection (or tangent) point.
std::unique_ptr< AstrometryTransform > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all AstrometryTransform types.
AstrometryTransformLinear getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane (degrees)
rectangle with sides parallel to axes.
AstrometryTransformLinear normalizeCoordinatesTransform(const Frame &frame)
Returns the transformation that maps the input frame along both axes to [-1,1].
void dump(std::ostream &stream) const
dumps the transform coefficients to stream.
Class for a simple mapping implementing a generic AstrometryTransform.
AstrometryTransformPolynomial getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
#define LOGL_FATAL(logger, message...)
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
Point getCrPix() const
Get the pixel origin of the WCS (CRPIX in FITS WCS terminology, but zero-based)
void dump(std::ostream &stream) const
dumps the transform coefficients to stream.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
std::unique_ptr< AstrometryTransform > compose(AstrometryTransform const &left, AstrometryTransform const &right)
Returns a pointer to a composition of transforms, representing left(right()).
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane) ...
T dynamic_pointer_cast(T... args)
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
double fit(StarMatchList const &starMatchList)
Not implemented yet, because we do it otherwise.
std::unique_ptr< SchemaItem< U > > result
double getWidth() const
size along x axis
std::unique_ptr< AstrometryTransform > inverseTransform(const double precision, const Frame ®ion) const
Inverse transform: returns a TanPixelToRaDec.
BaseTanWcs(AstrometryTransformLinear const &pixToTan, Point const &tangentPoint, const AstrometryTransformPolynomial *corrections=nullptr)
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
std::unique_ptr< AstrometryTransform > astrometryTransformRead(const std::string &fileName)
The virtual constructor from a file.
std::shared_ptr< AstrometryTransformPolynomial > inversePolyTransform(AstrometryTransform 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.
Point getCenter() const
Center of the frame.
#define LSST_EXCEPT(type,...)
void dump(std::ostream &stream) const
dumps the transform coefficients to stream.
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane (degrees)
Point getTangentPoint() const
Get the sky origin (CRVAL in FITS WCS terminology) in degrees.
TanRaDecToPixel inverted() const
approximate inverse : it ignores corrections;
long double coeff(const unsigned powX, const unsigned powY) const
T setprecision(T... args)
#define LOGL_WARN(logger, message...)
unsigned getOrder() const
void operator=(const BaseTanWcs &original)
#define LOGLS_WARN(logger, message)
Point getTangentPoint() const
tangent point coordinates (degrees)
T emplace_back(T... args)