44#include "Eigen/Cholesky"
60 if (shift ==
nullptr)
return false;
62 static const double eps = 1
e-5;
64 double dx = shift->getCoefficient(0, 0, 0);
65 double dy = shift->getCoefficient(0, 0, 1);
102 double eps =
x * 0.01;
120 const double step)
const {
132 derivative.
a22() = (
yp -
yp0) / step;
138 const double step)
const {
153 double a11 =
der.A11();
154 double a22 =
der.A22();
155 double a21 =
der.A21();
156 double a12 =
der.A12();
157 res.vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
158 res.vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
159 res.vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
166 double a11 =
der.A11();
167 double a22 =
der.A22();
168 double a21 =
der.A21();
169 double a12 =
der.A12();
184 double b11 = a11 *
vIn[xx] + a12 *
vIn[xy];
185 double b22 = a21 *
vIn[xy] + a22 *
vIn[yy];
186 double b12 = a11 *
vIn[xy] + a12 *
vIn[yy];
187 double b21 = a21 *
vIn[xx] + a22 *
vIn[xy];
229 std::string(
"AstrometryTransform::paramRef should never be called "));
234 "AstrometryTransform::paramRef should never be called ");
239 "AstrometryTransform::paramDerivatives() should never be called ");
254 "AstrometryTransform::write, something went wrong for file " +
fileName);
260 "AstrometryTransform::write(ostream), should never be called. MEans that it is missing in some "
276 const Frame ®ion);
295 return _direct->clone();
305 const Frame ®ion)
const {
310 const double precision,
const Frame ®ion) {
312 _roughInverse = _direct->roughInverse(region);
313 precision2 = precision * precision;
318 _direct =
model._direct->clone();
319 _roughInverse =
model._roughInverse->clone();
320 precision2 =
model.precision2;
326 _direct =
model._direct->clone();
327 _roughInverse =
model._roughInverse->clone();
328 precision2 =
model.precision2;
355 stream <<
" AstrometryTransformInverse of :" <<
endl << *_direct <<
endl;
360 "Cannot fit a AstrometryTransformInverse. Use StarMatchList::inverseTransform instead.");
394 _first = first.clone();
395 _second = second.clone();
399 double &
yOut)
const {
406 stream <<
"Composed AstrometryTransform consisting of:" <<
std::endl;
407 _first->print(stream);
408 _second->print(stream);
433 if (composition ==
nullptr)
441 const double)
const {
446 const double)
const {
456 stream <<
"AstrometryTransformIdentity 1" <<
endl;
464 " AstrometryTransformIdentity::read : format is not 1 ");
475 _coeffs.
resize(2 * _nterms, 0.);
511 double yStart =
domain.yMin;
529void AstrometryTransformPolynomial::computeMonomials(
double xIn,
double yIn,
double *
monomial)
const {
555 _nterms = (_order + 1) * (_order + 2) / 2;
560 _coeffs.
resize(2 * _nterms);
563 for (
std::size_t k = 0; k < _nterms; ++k) _coeffs[k] = 0;
574 double &
yOut)
const {
589 const double *
c = &_coeffs[0];
592 for (
int k = _nterms; k--;)
xOut += (*(
pm++)) * (*(
c++));
594 for (
int k = _nterms; k--;)
yOut += (*(
pm++)) * (*(
c++));
603 derivative.
dx() = derivative.
dy() = 0;
607 double dermx[2 * _nterms];
609 double xin = where.
x;
610 double yin = where.
y;
636 const double *
c = &_coeffs[0];
638 double a11 = 0, a12 = 0;
639 for (
int k = _nterms; k--;) {
640 a11 += (*(
mx++)) * (*c);
641 a12 += (*(
my++)) * (*(
c++));
643 derivative.
a11() = a11;
644 derivative.
a12() = a12;
646 double a21 = 0, a22 = 0;
649 for (
int k = _nterms; k--;) {
650 a21 += (*(
mx++)) * (*c);
651 a22 += (*(
my++)) * (*(
c++));
653 derivative.
a21() = a21;
654 derivative.
a22() = a22;
674 double dermx[2 * _nterms];
704 const double *
c = &_coeffs[0];
706 for (
int k = _nterms; k--;)
xout += (*(
pm++)) * (*(
c++));
708 for (
int k = _nterms; k--;)
yout += (*(
pm++)) * (*(
c++));
716 double a11 = 0, a12 = 0;
717 for (
int k = _nterms; k--;) {
718 a11 += (*(
mx++)) * (*c);
719 a12 += (*(
my++)) * (*(
c++));
722 double a21 = 0, a22 = 0;
725 for (
int k = _nterms; k--;) {
726 a21 += (*(
mx++)) * (*c);
727 a22 += (*(
my++)) * (*(
c++));
731 res.vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
732 res.vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
733 res.vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
767 assert(
i < 2 * Eigen::Index(_nterms));
772 assert(
i < 2 * Eigen::Index(_nterms));
778 computeMonomials(where.
x, where.
y, dx);
780 dy[_nterms + k] = dx[k];
781 dx[_nterms + k] = dy[k] = 0;
811 if (
printed && p +
py != 0) stream <<
" + ";
822 if (_order > 0) stream <<
"Linear determinant = " <<
determinant();
841static AstrometryTransformLinear shiftAndNormalize(StarMatchList
const &
starMatchList) {
848 Point
const &point1 =
a_match.point1;
855 if (
count == 0)
return AstrometryTransformLinear();
861 return AstrometryTransformLinearScale(2. /
xspan, 2. /
yspan) *
862 AstrometryTransformLinearShift(-
xav, -
yav);
865static double sq(
double x) {
return x *
x; }
867double AstrometryTransformPolynomial::computeFit(StarMatchList
const &
starMatchList,
870 Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
872 Eigen::VectorXd B(2 * _nterms);
879 FatPoint
const &point2 =
a_match.point2;
882 computeMonomials(point1.x, point1.y,
monomials);
885 double vxx = (
tr1.vx + point2.vx);
886 double vyy = (
tr1.vy + point2.vy);
887 double vxy = (
tr1.vxy + point2.vxy);
897 double resx = point2.x -
tr1.x;
898 double resy = point2.y -
tr1.y;
913 Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
915 if (factor.info() != Eigen::Success) {
916 LOGL_ERROR(_log,
"AstrometryTransformPolynomial::fit could not factorize");
920 Eigen::VectorXd
sol = factor.solve(B);
921 for (
std::size_t k = 0; k < 2 * _nterms; ++k) _coeffs[k] +=
sol(k);
928 LOGLS_FATAL(_log,
"AstrometryTransformPolynomial::fit trying to fit a polynomial transform of order "
929 << _order <<
" with only " <<
starMatchList.size() <<
" matches.");
968 coeffs.reserve(nterms);
969 coeffs.insert(coeffs.begin(), nterms, 0
L);
995static void operator+=(PolyXY &
left,
const PolyXY &
right) {
1003static PolyXY operator*(
const long double &a,
const PolyXY &
polyXY) {
1013static PolyXY product(
const PolyXY &
p1,
const PolyXY &
p2) {
1030 powers[0].getCoefficient(0, 0) = 1L;
1035static PolyXY composition(
const PolyXY &
polyXY,
const PolyXY &
polyX,
const PolyXY &
polyY) {
1068 result.getCoefficient(
px,
py, 0) =
rx.getCoefficient(
px,
py);
1069 result.getCoefficient(
px,
py, 1) =
ry.getCoefficient(
px,
py);
1076 if (_order >=
right._order) {
1080 res.getCoefficient(
i,
j, 0) +=
right.getCoefficient(
i,
j, 0);
1081 res.getCoefficient(
i,
j, 1) +=
right.getCoefficient(
i,
j, 1);
1085 return (
right + (*
this));
1105 s <<
" AstrometryTransformPolynomial 1" <<
endl;
1106 s <<
"order " << _order <<
endl;
1109 for (
std::size_t k = 0; k < 2 * _nterms; ++k) s << _coeffs[k] <<
' ';
1119 " AstrometryTransformPolynomial::read : format is not 1 ");
1122 s >>
order >> _order;
1123 if (
order !=
"order")
1125 " AstrometryTransformPolynomial::read : expecting \"order\" and found " +
order);
1130ndarray::Array<double, 2, 2> AstrometryTransformPolynomial::toAstPolyMapCoefficients()
const {
1132 ndarray::Array<double, 2, 2> result = ndarray::allocate(ndarray::makeVector(
nCoeffs,
std::size_t(4)));
1134 ndarray::Size k = 0;
1139 result[k][1] =
iCoord + 1;
1140 result[k][2] = p -
py;
1151 double const precision,
1156 double yStart =
domain.yMin;
1163 sm.push_back(
StarMatch(out, in,
nullptr,
nullptr));
1177 errMsg <<
"Cannot fit a polynomial of order " <<
order <<
" with " <<
nSteps <<
"^2 points";
1182 for (
auto const &
i :
sm) chi2 +=
i.point2.computeDist2(poly->apply((
i.point1)));
1184 << chi2 /
npairs <<
" < " << precision * precision);
1186 if (chi2 /
npairs < precision * precision)
break;
1190 LOGLS_WARN(_log,
"inversePolyTransform: chi2 increases ("
1191 << chi2 <<
" > " <<
oldChi2 <<
"); ending fit with order: " <<
order);
1192 LOGLS_WARN(_log,
"inversePolyTransform: requested precision not reached: "
1193 << chi2 <<
" / " <<
npairs <<
" = " << chi2 /
npairs <<
" < "
1194 << precision * precision);
1206 LOGLS_WARN(_log,
"inversePolyTransform: Reached max order without reaching requested precision: "
1207 << chi2 <<
" / " <<
npairs <<
" = " << chi2 /
npairs <<
" < "
1208 << precision * precision);
1218 const double A12,
const double A21,
const double A22)
1232 "Trying to build a AstrometryTransformLinear from a higher order transform. Aborting. ");
1252 const double)
const {
1276 "AstrometryTransformLinear::invert singular transformation: transform contents will be "
1277 "dumped to stderr.");
1290 const Frame &)
const {
1297 stream <<
"Linear AstrometryTransform:" <<
std::endl;
1312 LOGLS_FATAL(_log,
"AstrometryTransformLinearShift::fit trying to fit a linear transform with only "
1313 <<
npairs <<
" matches.");
1319 Eigen::VectorXd B(2);
1321 Eigen::MatrixXd A(2, 2);
1327 double deltax = point2.
x - point1.
x;
1328 double deltay = point2.
y - point1.
y;
1329 double vxx = point1.
vx + point2.
vx;
1330 double vyy = point1.
vy + point2.
vy;
1331 double vxy = point1.
vxy + point2.
vxy;
1343 double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
1344 if (
det <= 0)
return -1;
1345 double tmp = A(0, 0);
1346 A(0, 0) = A(1, 1) /
det;
1347 A(1, 1) = tmp /
det;
1348 A(0, 1) = A(1, 0) = -A(0, 1) /
det;
1349 Eigen::VectorXd
sol = A * B;
1364 if (center)
a_point = *center;
1374static double rad2deg(
double rad) {
return rad * 180. /
M_PI; }
1437 LOGL_WARN(_log,
"No sidereal coordinates at pole!");
1469 return Point(inverse.
Dx(), inverse.
Dy());
1475 : _skyWcs(
std::
move(skyWcs)) {}
1484 out <<
"AstrometryTransformSkyWcs(" << *_skyWcs <<
")";
1506 if (
right.getOrder() == 1) {
1515 result.linPixelToTan = result.linPixelToTan *
right;
1520 if (
corr !=
nullptr) {
1521 LOGL_WARN(_log,
"You are inverting a TanPixelToRaDec with corrections.");
1522 LOGL_WARN(_log,
"The inverse you get ignores the corrections!");
1533 const Frame ®ion)
const {
1567 stream <<
" tangent point " <<
tp.x <<
' ' <<
tp.y <<
endl;
1570 if (
corr) stream <<
"PV correction: " <<
endl << *
corr;
1583 "TanPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1607 const Frame ®ion)
1638 stream <<
" tangent point " <<
tp.x <<
' ' <<
tp.y <<
endl;
1641 if (
corr) stream <<
"PV correction: " <<
endl << *
corr;
1654 "TanSipPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1701 double ra = deg2rad(in.
x);
1702 double dec = deg2rad(in.
y);
1703 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1704 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1726 tmp.
vx = a11 * (a11 * in.
vx + 2 * a12 * in.
vxy) + a12 * a12 * in.
vy;
1727 tmp.
vy = a21 * a21 * in.
vx + a22 * a22 * in.
vy + 2. * a21 * a22 * in.
vxy;
1728 tmp.
vxy = a21 * a11 * in.
vx + a22 * a12 * in.
vy + (a21 * a12 + a11 * a22) * in.
vxy;
1738 double ra = deg2rad(
xIn);
1739 double dec = deg2rad(
yIn);
1740 if (ra - ra0 >
M_PI) ra -= (2. *
M_PI);
1741 if (ra - ra0 < -
M_PI) ra += (2. *
M_PI);
1762 stream <<
"tan2pix " << linTan2Pix <<
std::endl;
1763 stream <<
"tangent point " <<
tp.x <<
' ' <<
tp.y <<
endl;
1782 "TanRaDecToPixel::fit is NOT implemented (although it is doable)) ");
1796 stream <<
"UserTransform with user function @ " << _userFun <<
"and userData@ " << _userData <<
endl;
1801 "UserTransform::fit is NOT implemented (and will never be)) ");
1815 " astrometryTransformRead : cannot open " +
fileName);
1831 "astrometryTransformRead : could not find a AstrometryTransformtype");
1832 if (
type ==
"AstrometryTransformIdentity") {
1836 }
else if (
type ==
"AstrometryTransformPolynomial") {
1842 " astrometryTransformRead : No reader for AstrometryTransform type " +
type);
#define LOGLS_WARN(logger, message)
#define LOGL_WARN(logger, message...)
#define LOGLS_FATAL(logger, message)
#define LOGL_ERROR(logger, message...)
#define LOGLS_TRACE(logger, message)
#define LOGL_FATAL(logger, message...)
#define LSST_EXCEPT(type,...)
table::PointKey< double > crpix
std::unique_ptr< AstrometryTransformPolynomial > corr
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const =0
Transform from pixels to tangent plane (degrees)
Point getCrPix() const
Get the pixel origin of the WCS (CRPIX in FITS WCS terminology, but zero-based)
BaseTanWcs & operator=(const BaseTanWcs &original)
BaseTanWcs(AstrometryTransformLinear const &pixToTan, Point const &tangentPoint, const AstrometryTransformPolynomial *corrections=nullptr)
AstrometryTransformLinear getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
Point getTangentPoint() const
Get the sky origin (CRVAL in FITS WCS terminology) in degrees.
void apply(double xIn, double yIn, double &xOut, double &yOut) const
Transform pixels to ICRS RA, Dec in degrees.
AstrometryTransformLinear linPixelToTan
void setCorrections(std::unique_ptr< AstrometryTransformPolynomial > corrections)
Assign the correction polynomial (what it means is left to derived classes)
A Point with uncertainties.
rectangle with sides parallel to axes.
Point getCenter() const
Center of the frame.
double getHeight() const
size along y axis
double xMin
coordinate of boundary.
double getWidth() const
size along x axis
PolyXY(AstrometryTransformPolynomial const &transform, std::size_t whichCoord)
long double getCoefficient(std::size_t powX, std::size_t powY) const
std::size_t getOrder() const
long double & getCoefficient(std::size_t powX, std::size_t powY)
A hanger for star associations.
The transformation that handles pixels to sideral transformations (Gnomonic, possibly with polynomial...
TanPixelToRaDec operator*(AstrometryTransformLinear const &right) const
composition with AstrometryTransformLinear
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
TanRaDecToPixel inverted() const
approximate inverse : it ignores corrections;
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
transforms from pixel space to tangent plane (degrees)
void print(std::ostream &out) const
prints the transform coefficients to stream.
std::unique_ptr< AstrometryTransform > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all AstrometryTransform types.
AstrometryTransformPolynomial getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
std::unique_ptr< AstrometryTransform > inverseTransform(double precision, const Frame ®ion) const
Inverse transform: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if ...
std::unique_ptr< AstrometryTransform > composeAndReduce(AstrometryTransformLinear const &right) const
Return a reduced composition of newTransform = this(right()), or nullptr if it cannot be reduced.
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane)
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
Point getTangentPoint() const
tangent point coordinates (degrees)
double fit(StarMatchList const &starMatchList)
fits a transform to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
void setTangentPoint(Point const &tangentPoint)
Resets the projection (or tangent) point.
std::unique_ptr< AstrometryTransform > inverseTransform(double precision, const Frame ®ion) const
Inverse transform: returns a TanPixelToRaDec.
TanPixelToRaDec inverted() const
exact typed inverse:
void apply(double xIn, double yIn, double &xOut, double &yOut) const
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
AstrometryTransformLinear getLinPart() const
The Linear part (corresponding to CD's and CRPIX's)
std::unique_ptr< AstrometryTransform > roughInverse(const Frame ®ion) const
Overload the "generic routine" (available for all AstrometryTransform types.
void print(std::ostream &out) const
prints the transform coefficients to stream.
std::unique_ptr< AstrometryTransform > inverseTransform(double precision, const Frame ®ion) const
Inverse transform: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if ...
void print(std::ostream &out) const
prints the transform coefficients to stream.
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
transforms from pixel space to tangent plane (degrees)
std::unique_ptr< AstrometryTransform > clone() const
returns a copy (allocated by new) of the transformation.
AstrometryTransformPolynomial getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
AstrometryTransformLinear normalizeCoordinatesTransform(const Frame &frame)
Returns the transformation that maps the input frame along both axes to [-1,1].
void(const double, const double, double &, double &, const void *) AstrometryTransformFun
signature of the user-provided routine that actually does the coordinate transform for UserTransform.
std::shared_ptr< AstrometryTransformPolynomial > inversePolyTransform(AstrometryTransform const &forward, Frame const &domain, double precision, std::size_t maxOrder=9, std::size_t nSteps=50)
Approximate the inverse by a polynomial, to some precision.
bool isIntegerShift(const AstrometryTransform *transform)
Shorthand test to tell if a transform is a simple integer shift.
std::unique_ptr< AstrometryTransform > compose(AstrometryTransform const &left, AstrometryTransform const &right)
Returns a pointer to a composition of transforms, representing left(right()).
std::unique_ptr< AstrometryTransform > astrometryTransformRead(const std::string &fileName)
The virtual constructor from a file.
std::ostream & operator<<(std::ostream &stream, AstrometryMapping const &mapping)
T setprecision(T... args)