LSST Applications g00d0e8bbd7+edbf708997,g03191d30f7+9ce8016dbd,g1955dfad08+0bd186d245,g199a45376c+5137f08352,g1fd858c14a+a888a50aa2,g262e1987ae+45f9aba685,g29ae962dfc+1c7d47a24f,g2cef7863aa+73c82f25e4,g35bb328faa+edbf708997,g3fd5ace14f+eed17d2c67,g47891489e3+6dc8069a4c,g53246c7159+edbf708997,g64539dfbff+c4107e45b5,g67b6fd64d1+6dc8069a4c,g74acd417e5+f452e9c21a,g786e29fd12+af89c03590,g7ae74a0b1c+a25e60b391,g7aefaa3e3d+2025e9ce17,g7cc15d900a+2d158402f9,g87389fa792+a4172ec7da,g89139ef638+6dc8069a4c,g8d4809ba88+c4107e45b5,g8d7436a09f+e96c132b44,g8ea07a8fe4+db21c37724,g98df359435+aae6d409c1,ga2180abaac+edbf708997,gac66b60396+966efe6077,gb632fb1845+88945a90f8,gbaa8f7a6c5+38b34f4976,gbf99507273+edbf708997,gca7fc764a6+6dc8069a4c,gd7ef33dd92+6dc8069a4c,gda68eeecaf+7d1e613a8d,gdab6d2f7ff+f452e9c21a,gdbb4c4dda9+c4107e45b5,ge410e46f29+6dc8069a4c,ge41e95a9f2+c4107e45b5,geaed405ab2+e194be0d2b,w.2025.47
LSST Data Management Base Package
Loading...
Searching...
No Matches
SkyWcs.cc
Go to the documentation of this file.
1/*
2 * LSST Data Management System
3 * Copyright 2017 AURA/LSST.
4 *
5 * This product includes software developed by the
6 * LSST Project (http://www.lsst.org/).
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the LSST License Statement and
19 * the GNU General Public License along with this program. If not,
20 * see <http://www.lsstcorp.org/LegalNotices/>.
21 */
22
23#include <cmath>
24#include <cstdint>
25#include <exception>
26#include <memory>
27#include <vector>
28
29#include "astshim.h"
30
31#include "lsst/geom/Angle.h"
32#include "lsst/geom/Point.h"
41#include "lsst/pex/exceptions.h"
43
44#include "ndarray/eigen.h"
45
46namespace lsst {
47namespace afw {
48
49template std::shared_ptr<geom::SkyWcs> table::io::PersistableFacade<geom::SkyWcs>::dynamicCast(
50 std::shared_ptr<table::io::Persistable> const&);
51
52namespace geom {
53namespace {
54
55int const SERIALIZATION_VERSION = 1;
56
57// TIGHT_FITS_TOL is used by getFitsMetadata to determine if a WCS can accurately be represented as a FITS
58// WCS. It specifies the maximum departure from linearity (in pixels) allowed on either axis of the mapping
59// from pixel coordinates to Intermediate World Coordinates over a range of 100 x 100 pixels
60// (or the region specified by NAXIS[12], if provided, but we do not pass this to AST as of 2018-01-17).
61// For more information,
62// see FitsTol in the AST manual http://starlink.eao.hawaii.edu/devdocs/sun211.htx/sun211.html
63double const TIGHT_FITS_TOL = 0.0001;
64
65class SkyWcsPersistenceHelper {
66public:
70
71 // No copying
72 SkyWcsPersistenceHelper(const SkyWcsPersistenceHelper&) = delete;
73 SkyWcsPersistenceHelper& operator=(const SkyWcsPersistenceHelper&) = delete;
74
75 // No moving
76 SkyWcsPersistenceHelper(SkyWcsPersistenceHelper&&) = delete;
77 SkyWcsPersistenceHelper& operator=(SkyWcsPersistenceHelper&&) = delete;
78
79 explicit SkyWcsPersistenceHelper(bool hasFitsApproximation)
80 : schema(),
81 wcs(schema.addField<table::Array<std::uint8_t>>("wcs", "wcs string representation", "")) {
82 if (hasFitsApproximation) {
83 approx = schema.addField<table::Array<std::uint8_t>>(
84 "approx", "wcs string representation of FITS approximation", "");
85 }
86 }
87
88 explicit SkyWcsPersistenceHelper(table::Schema const& schema)
89 : schema(schema), wcs(schema["wcs"]), approx() {
90 try {
91 approx = schema["approx"];
92 } catch (pex::exceptions::NotFoundError&) {
93 }
94 }
95};
96
97class SkyWcsFactory : public table::io::PersistableFactory {
98public:
99 explicit SkyWcsFactory(std::string const& name) : table::io::PersistableFactory(name) {}
100
101 std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
102 CatalogVector const& catalogs) const override {
103 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
104 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
105 SkyWcsPersistenceHelper keys(catalogs.front().getSchema());
106 table::BaseRecord const& record = catalogs.front().front();
107 std::string stringRep = formatters::bytesToString(record.get(keys.wcs));
108 auto result = SkyWcs::readString(stringRep);
109 if (keys.approx.isValid()) {
110 auto bytes = record.get(keys.approx);
111 if (!bytes.isEmpty()) {
112 auto approxStringRep = formatters::bytesToString(bytes);
113 result = result->copyWithFitsApproximation(SkyWcs::readString(approxStringRep));
114 }
115 }
116 return result;
117 }
118};
119
120std::string getSkyWcsPersistenceName() { return "SkyWcs"; }
121
122SkyWcsFactory registration(getSkyWcsPersistenceName());
123
124ast::FrameDict makeSkyWcsFrameDict(TransformPoint2ToPoint2 const& pixelsToFieldAngle,
125 lsst::geom::Angle const& orientation, bool flipX,
126 lsst::geom::SpherePoint const& crval,
127 std::string const& projection = "TAN") {
128 auto const orientationAndFlipXMatrix = makeCdMatrix(1 * lsst::geom::degrees, orientation, flipX);
129 auto const initialWcs =
130 makeSkyWcs(lsst::geom::Point2D(0, 0), crval, orientationAndFlipXMatrix, projection);
131 auto const initialFrameDict = initialWcs->getFrameDict();
132 auto const iwcToSkyMap = initialFrameDict->getMapping("IWC", "SKY");
133 auto const pixelFrame = initialFrameDict->getFrame("PIXELS");
134 auto const iwcFrame = initialFrameDict->getFrame("IWC");
135 auto const skyFrame = initialFrameDict->getFrame("SKY");
136 // Field angle is in radians and is aligned to focal plane x and y;
137 // IWC is basically the same thing, but in degrees and with rotation and flipX applied
138 ndarray::Array<double, 2, 2> fieldAngleToIwcNdArray = ndarray::allocate(2, 2);
139 asEigenMatrix(fieldAngleToIwcNdArray) = orientationAndFlipXMatrix * 180.0 / lsst::geom::PI;
140 auto const pixelsToFieldAngleMap = pixelsToFieldAngle.getMapping();
141 auto const fieldAngleToIwcMap = ast::MatrixMap(fieldAngleToIwcNdArray);
142 auto const pixelsToIwcMap = pixelsToFieldAngleMap->then(fieldAngleToIwcMap);
143 auto finalFrameDict = ast::FrameDict(*pixelFrame, pixelsToIwcMap, *iwcFrame);
144 finalFrameDict.addFrame("IWC", *iwcToSkyMap, *skyFrame);
145 return finalFrameDict;
146}
147
148} // namespace
149
150Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const& scale, lsst::geom::Angle const& orientation,
151 bool flipX) {
152 Eigen::Matrix2d cdMatrix;
153 double orientRad = orientation.asRadians();
154 double scaleDeg = scale.asDegrees();
155 double xmult = flipX ? 1.0 : -1.0;
156 cdMatrix(0, 0) = std::cos(orientRad) * scaleDeg * xmult;
157 cdMatrix(0, 1) = std::sin(orientRad) * scaleDeg;
158 cdMatrix(1, 0) = -std::sin(orientRad) * scaleDeg * xmult;
159 cdMatrix(1, 1) = std::cos(orientRad) * scaleDeg;
160 return cdMatrix;
161}
162
164 auto const dstInverse = dst.getTransform()->inverted();
165 return src.getTransform()->then(*dstInverse);
166}
167
169 : SkyWcs(detail::readLsstSkyWcs(metadata, strip)) {}
170
171SkyWcs::SkyWcs(ast::FrameDict const& frameDict) : SkyWcs(_checkFrameDict(frameDict)) {}
172
173bool SkyWcs::operator==(SkyWcs const& other) const { return writeString() == other.writeString(); }
174
176 // Compute pixVec containing the pixel position and two nearby points
177 // (use a vector so all three points can be converted to sky in a single call)
178 double const side = 1.0;
180 pixel,
181 pixel + lsst::geom::Extent2D(side, 0),
182 pixel + lsst::geom::Extent2D(0, side),
183 };
184
185 auto skyVec = pixelToSky(pixVec);
186
187 // Work in 3-space to avoid RA wrapping and pole issues
188 auto skyLL = skyVec[0].getVector();
189 auto skyDx = skyVec[1].getVector() - skyLL;
190 auto skyDy = skyVec[2].getVector() - skyLL;
191
192 // Compute pixel scale in radians = sqrt(pixel area in radians^2)
193 // pixel area in radians^2 = area of parallelogram with sides skyDx, skyDy = |skyDx cross skyDy|
194 // Use squared norm to avoid two square roots
195 double skyAreaSq = skyDx.cross(skyDy).getSquaredNorm();
196 return (std::pow(skyAreaSq, 0.25) / side) * lsst::geom::radians;
197}
198
200 // CRVAL is stored as the SkyRef property of the sky frame (the current frame of the SkyWcs)
202 getFrameDict()->getFrame(ast::FrameDict::CURRENT, false)); // false: do not copy
203 if (!skyFrame) {
204 throw LSST_EXCEPT(pex::exceptions::LogicError, "Current frame is not a SkyFrame");
205 }
206 auto const crvalRad = skyFrame->getSkyRef();
207 return lsst::geom::SpherePoint(crvalRad[0] * lsst::geom::radians, crvalRad[1] * lsst::geom::radians);
208}
209
210Eigen::Matrix2d SkyWcs::getCdMatrix(lsst::geom::Point2D const& pixel) const {
211 auto const pixelToIwc = getFrameDict()->getMapping(ast::FrameSet::BASE, "IWC");
212 auto const pixelToIwcTransform = TransformPoint2ToPoint2(*pixelToIwc);
213 return pixelToIwcTransform.getJacobian(pixel);
214}
215
216Eigen::Matrix2d SkyWcs::getCdMatrix() const { return getCdMatrix(getPixelOrigin()); }
217
219 auto const crval = pixelToSky(pixel);
220 auto const cdMatrix = getCdMatrix(pixel);
221 auto metadata = makeSimpleWcsMetadata(pixel, crval, cdMatrix);
222 return std::make_shared<SkyWcs>(*metadata);
223}
224
226 auto newToOldPixel = TransformPoint2ToPoint2(ast::ShiftMap({-shift[0], -shift[1]}));
227 return makeModifiedWcs(newToOldPixel, *this, true);
228}
229
230std::shared_ptr<daf::base::PropertyList> SkyWcs::_getDirectFitsMetadata() const {
231 // Make a FrameSet that maps from GRID to SKY; GRID = the base frame (PIXELS or ACTUAL_PIXELS) + 1
232 auto const gridToPixel = ast::ShiftMap({-1.0, -1.0});
233 auto thisDict = getFrameDict();
234 auto const pixelToIwc = thisDict->getMapping(ast::FrameSet::BASE, "IWC");
235 auto const iwcToSky = thisDict->getMapping("IWC", "SKY");
236 auto const gridToSky = gridToPixel.then(*pixelToIwc).then(*iwcToSky);
237 ast::FrameSet frameSet(ast::Frame(2, "Domain=GRID"), gridToSky, *thisDict->getFrame("SKY", false));
238
239 // Write frameSet to a FitsChan and extract the metadata
241 os << "Encoding=FITS-WCS, CDMatrix=1, FitsAxisOrder=<copy>, FitsTol=" << TIGHT_FITS_TOL;
242 ast::StringStream strStream;
243 ast::FitsChan fitsChan(strStream, os.str());
244 int const nObjectsWritten = fitsChan.write(frameSet);
245 if (nObjectsWritten == 0) {
246 return nullptr;
247 }
249
250 // Remove DATE-OBS, MJD-OBS: AST writes these if the EQUINOX is set, but we set them via other mechanisms.
251 header->remove("DATE-OBS");
252 header->remove("MJD-OBS");
253
254 // If CD matrix is present, explicitly set any missing entries to zero, as a convenience to the user
255 bool const hasCd11 = header->exists("CD1_1");
256 bool const hasCd12 = header->exists("CD1_2");
257 bool const hasCd21 = header->exists("CD2_1");
258 bool const hasCd22 = header->exists("CD2_2");
259 if (hasCd11 || hasCd12 || hasCd21 || hasCd22) {
260 if (!hasCd11) header->set("CD1_1", 0.0, "Transformation matrix element");
261 if (!hasCd12) header->set("CD1_2", 0.0, "Transformation matrix element");
262 if (!hasCd21) header->set("CD2_1", 0.0, "Transformation matrix element");
263 if (!hasCd22) header->set("CD2_2", 0.0, "Transformation matrix element");
264 }
265
266 return header;
267}
268
270 if (!precise && hasFitsApproximation()) {
271 return getFitsApproximation()->_getDirectFitsMetadata();
272 }
273 auto result = _getDirectFitsMetadata();
274 if (!result) {
276 precise ? "WCS is not directly FITS-compatible."
277 : "WCS does not have an attached FITS approximation.");
278 }
279 return result;
280}
281
283
285 if (fitsApproximation && fitsApproximation->hasFitsApproximation()) {
287 "Cannot add a FITS approximation that itself already has a FITS approximation.");
288 }
289 auto result = std::make_shared<SkyWcs>(*this);
290 result->_fitsApproximation = fitsApproximation;
291 return result;
292}
293
294bool SkyWcs::isFits() const { return bool(_getDirectFitsMetadata()); }
295
297 lsst::geom::AngleUnit const& skyUnit) const {
298 return _linearizePixelToSky(skyToPixel(coord), coord, skyUnit);
299}
301 lsst::geom::AngleUnit const& skyUnit) const {
302 return _linearizePixelToSky(pix, pixelToSky(pix), skyUnit);
303}
304
306 lsst::geom::AngleUnit const& skyUnit) const {
307 return _linearizeSkyToPixel(skyToPixel(coord), coord, skyUnit);
308}
309
311 lsst::geom::AngleUnit const& skyUnit) const {
312 return _linearizeSkyToPixel(pix, pixelToSky(pix), skyUnit);
313}
314
316
317bool SkyWcs::isFlipped() const {
318 double det = getCdMatrix().determinant();
319 if (det == 0) {
320 throw(LSST_EXCEPT(pex::exceptions::RuntimeError, "CD matrix is singular"));
321 }
322 return (det > 0);
323}
324
326 int version;
327 is >> version;
328 if (version != 1) {
329 throw LSST_EXCEPT(pex::exceptions::TypeError, "Unsupported version " + std::to_string(version));
330 }
331 std::string shortClassName;
332 is >> shortClassName;
333 if (shortClassName != SkyWcs::getShortClassName()) {
335 os << "Class name in stream " << shortClassName << " != " << SkyWcs::getShortClassName();
337 }
340 auto astStream = ast::Stream(&is, nullptr);
341 auto astObjectPtr = ast::Channel(astStream).read();
342 auto frameSet = std::dynamic_pointer_cast<ast::FrameSet>(astObjectPtr);
343 if (!frameSet) {
345 os << "The AST serialization was read as a " << astObjectPtr->getClassName()
346 << " instead of a FrameSet";
348 }
349 ast::FrameDict frameDict(*frameSet);
350 return std::make_shared<SkyWcs>(frameDict);
351}
352
357
359 os << SERIALIZATION_VERSION << " " << SkyWcs::getShortClassName() << " " << hasFitsApproximation() << " ";
360 getFrameDict()->show(os, false); // false = do not write comments
361}
362
365 writeStream(os);
366 return os.str();
367}
368
370 return std::make_unique<SkyWcs>(*this);
371}
372
375 if (isFits()) {
376 os << "FITS standard SkyWcs:";
377 } else {
378 os << "Non-standard SkyWcs (Frames: ";
379 // Print the frames in index order (frames are numbered from 1).
380 std::string delimiter = "";
381 for (size_t i = 1; i <= getFrameDict()->getAllDomains().size(); ++i) {
382 os << delimiter << getFrameDict()->getFrame(i)->getDomain();
383 delimiter = ", ";
384 }
385 os << "): ";
386 }
387 std::string delimiter = "\n";
388 os << delimiter << "Sky Origin: " << getSkyOrigin();
389 os << delimiter << "Pixel Origin: " << getPixelOrigin();
390 os << delimiter << "Pixel Scale: " << getPixelScale().asArcseconds() << " arcsec/pixel";
391 return os.str();
392}
393
394bool SkyWcs::equals(typehandling::Storable const& other) const noexcept {
395 return singleClassEquals(*this, other);
396}
397
398std::string SkyWcs::getPersistenceName() const { return getSkyWcsPersistenceName(); }
399
400std::string SkyWcs::getPythonModule() const { return "lsst.afw.geom"; }
401
403 SkyWcsPersistenceHelper const keys(hasFitsApproximation());
404 table::BaseCatalog cat = handle.makeCatalog(keys.schema);
406 record->set(keys.wcs, formatters::stringToBytes(writeString()));
407 if (hasFitsApproximation()) {
408 record->set(keys.approx, formatters::stringToBytes(getFitsApproximation()->writeString()));
409 }
410 handle.saveCatalog(cat);
411}
412
414 : _frameDict(frameDict), _transform(), _pixelOrigin(), _pixelScaleAtOrigin(0 * lsst::geom::radians) {
415 _computeCache();
416};
417
418std::shared_ptr<ast::FrameDict> SkyWcs::_checkFrameDict(ast::FrameDict const& frameDict) const {
419 // Check that each frame is present and has the right type and number of axes
420 std::vector<std::string> const domainNames = {"ACTUAL_PIXELS", "PIXELS", "IWC", "SKY"};
421 for (auto const& domainName : domainNames) {
422 if (frameDict.hasDomain(domainName)) {
423 auto const frame = frameDict.getFrame(domainName, false);
424 if (frame->getNAxes() != 2) {
426 "Frame " + domainName + " has " + std::to_string(frame->getNAxes()) +
427 " axes instead of 2");
428 }
429 auto desiredClassName = domainName == "SKY" ? "SkyFrame" : "Frame";
430 if (frame->getClassName() != desiredClassName) {
432 "Frame " + domainName + " is of type " + frame->getClassName() +
433 " instead of " + desiredClassName);
434 }
435 } else if (domainName != "ACTUAL_PIXELS") {
436 // This is a required frame
437 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
438 "No frame with domain " + domainName + " found");
439 }
440 }
441
442 // The base frame must have domain "PIXELS" or "ACTUAL_PIXELS"
443 auto baseDomain = frameDict.getFrame(ast::FrameSet::BASE, false)->getDomain();
444 if (baseDomain != "ACTUAL_PIXELS" && baseDomain != "PIXELS") {
445 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
446 "Base frame has domain " + baseDomain + " instead of PIXELS or ACTUAL_PIXELS");
447 }
448
449 // The current frame must have domain "SKY"
450 auto currentDomain = frameDict.getFrame(ast::FrameSet::CURRENT, false)->getDomain();
451 if (currentDomain != "SKY") {
452 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
453 "Current frame has domain " + currentDomain + " instead of SKY");
454 }
455
456 return frameDict.copy();
457}
458
459lsst::geom::AffineTransform SkyWcs::_linearizePixelToSky(lsst::geom::Point2D const& pix00,
460 lsst::geom::SpherePoint const& coord,
461 lsst::geom::AngleUnit const& skyUnit) const {
462 // Figure out the (0, 0), (0, 1), and (1, 0) ra/dec coordinates of the corners
463 // of a square drawn in pixel. It'd be better to center the square at sky00,
464 // but that would involve another conversion between sky and pixel coordinates
465 const double side = 1.0; // length of the square's sides in pixels
466 auto const sky00 = coord.getPosition(skyUnit);
467 auto const dsky10 = coord.getTangentPlaneOffset(pixelToSky(pix00 + lsst::geom::Extent2D(side, 0)));
468 auto const dsky01 = coord.getTangentPlaneOffset(pixelToSky(pix00 + lsst::geom::Extent2D(0, side)));
469
470 Eigen::Matrix2d m;
471 m(0, 0) = dsky10.first.asAngularUnits(skyUnit) / side;
472 m(0, 1) = dsky01.first.asAngularUnits(skyUnit) / side;
473 m(1, 0) = dsky10.second.asAngularUnits(skyUnit) / side;
474 m(1, 1) = dsky01.second.asAngularUnits(skyUnit) / side;
475
476 Eigen::Vector2d sky00v;
477 sky00v << sky00.getX(), sky00.getY();
478 Eigen::Vector2d pix00v;
479 pix00v << pix00.getX(), pix00.getY();
480 // return lsst::geom::AffineTransform(m, lsst::geom::Extent2D(sky00v - m * pix00v));
481 return lsst::geom::AffineTransform(m, (sky00v - m * pix00v));
482}
483
484lsst::geom::AffineTransform SkyWcs::_linearizeSkyToPixel(lsst::geom::Point2D const& pix00,
485 lsst::geom::SpherePoint const& coord,
486 lsst::geom::AngleUnit const& skyUnit) const {
487 lsst::geom::AffineTransform inverse = _linearizePixelToSky(pix00, coord, skyUnit);
488 return inverse.inverted();
489}
490
491std::shared_ptr<SkyWcs> makeFlippedWcs(SkyWcs const& wcs, bool flipLR, bool flipTB,
492 lsst::geom::Point2D const& center) {
493 double const dx = 1000; // any "reasonable" number of pixels will do
494 std::vector<double> inLL = {center[0] - dx, center[1] - dx};
495 std::vector<double> inUR = {center[0] + dx, center[1] + dx};
496 std::vector<double> outLL(inLL);
497 std::vector<double> outUR(inUR);
498 if (flipLR) {
499 outLL[0] = inUR[0];
500 outUR[0] = inLL[0];
501 }
502 if (flipTB) {
503 outLL[1] = inUR[1];
504 outUR[1] = inLL[1];
505 }
506 auto const flipPix = TransformPoint2ToPoint2(ast::WinMap(inLL, inUR, outLL, outUR));
507 return makeModifiedWcs(flipPix, wcs, true);
508}
509
511 bool modifyActualPixels) {
512 auto const pixelMapping = pixelTransform.getMapping();
513 auto oldFrameDict = wcs.getFrameDict();
514 bool const hasActualPixels = oldFrameDict->hasDomain("ACTUAL_PIXELS");
515 auto const pixelFrame = oldFrameDict->getFrame("PIXELS", false);
516 auto const iwcFrame = oldFrameDict->getFrame("IWC", false);
517 auto const skyFrame = oldFrameDict->getFrame("SKY", false);
518 auto const oldPixelToIwc = oldFrameDict->getMapping("PIXELS", "IWC");
519 auto const iwcToSky = oldFrameDict->getMapping("IWC", "SKY");
520
522 std::shared_ptr<ast::Mapping> newPixelToIwc;
523 if (hasActualPixels) {
524 auto const actualPixelFrame = oldFrameDict->getFrame("ACTUAL_PIXELS", false);
525 auto const oldActualPixelToPixels = oldFrameDict->getMapping("ACTUAL_PIXELS", "PIXELS");
526 std::shared_ptr<ast::Mapping> newActualPixelsToPixels;
527 if (modifyActualPixels) {
528 newActualPixelsToPixels = pixelMapping->then(*oldActualPixelToPixels).simplified();
529 newPixelToIwc = oldPixelToIwc;
530 } else {
531 newActualPixelsToPixels = oldActualPixelToPixels;
532 newPixelToIwc = pixelMapping->then(*oldPixelToIwc).simplified();
533 }
534 newFrameDict =
535 std::make_shared<ast::FrameDict>(*actualPixelFrame, *newActualPixelsToPixels, *pixelFrame);
536 newFrameDict->addFrame("PIXELS", *newPixelToIwc, *iwcFrame);
537 } else {
538 newPixelToIwc = pixelMapping->then(*oldPixelToIwc).simplified();
539 newFrameDict = std::make_shared<ast::FrameDict>(*pixelFrame, *newPixelToIwc, *iwcFrame);
540 }
541 newFrameDict->addFrame("IWC", *iwcToSky, *skyFrame);
542 return std::make_shared<SkyWcs>(*newFrameDict);
543}
544
546 return std::make_shared<SkyWcs>(metadata, strip);
547}
548
550 Eigen::Matrix2d const& cdMatrix, std::string const& projection) {
551 auto metadata = makeSimpleWcsMetadata(crpix, crval, cdMatrix, projection);
552 return std::make_shared<SkyWcs>(*metadata);
553}
554
556 lsst::geom::Angle const& orientation, bool flipX,
557 lsst::geom::SpherePoint const& boresight, std::string const& projection) {
558 auto frameDict = makeSkyWcsFrameDict(pixelsToFieldAngle, orientation, flipX, boresight, projection);
559 return std::make_shared<SkyWcs>(frameDict);
560}
561
563 Eigen::Matrix2d const& cdMatrix, Eigen::MatrixXd const& sipA,
564 Eigen::MatrixXd const& sipB) {
565 auto metadata = makeTanSipMetadata(crpix, crval, cdMatrix, sipA, sipB);
566 return std::make_shared<SkyWcs>(*metadata);
567}
568
570 Eigen::Matrix2d const& cdMatrix, Eigen::MatrixXd const& sipA,
571 Eigen::MatrixXd const& sipB, Eigen::MatrixXd const& sipAp,
572 Eigen::MatrixXd const& sipBp) {
573 auto metadata = makeTanSipMetadata(crpix, crval, cdMatrix, sipA, sipB, sipAp, sipBp);
574 return std::make_shared<SkyWcs>(*metadata);
575}
576
578 bool simplify) {
579 auto iwcToSky = wcs.getFrameDict()->getMapping("IWC", "SKY");
580 return std::make_shared<TransformPoint2ToSpherePoint>(*iwcToSky, simplify);
581}
582
584 auto pixelToIwc = wcs.getFrameDict()->getMapping(ast::FrameSet::BASE, "IWC");
585 return std::make_shared<TransformPoint2ToPoint2>(*pixelToIwc, simplify);
586}
587
589 os << wcs.toString();
590 return os;
591};
592
593} // namespace geom
594} // namespace afw
595} // namespace lsst
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition Persistable.h:48
Channel provides input/output of AST objects.
Definition Channel.h:60
std::shared_ptr< Object > read()
Read an object from a channel.
Definition Channel.cc:52
A specialized form of Channel which reads and writes FITS header cards.
Definition FitsChan.h:202
A FrameSet whose frames can be referenced by domain name.
Definition FrameDict.h:67
bool hasDomain(std::string const &domain) const
Return True if a frame in this FrameDict has the specified domain.
Definition FrameDict.h:201
std::shared_ptr< Mapping > getMapping(int from, std::string const &to) const
Variant of FrameSet::getMapping with the second frame specified by domain.
Definition FrameDict.h:162
std::shared_ptr< FrameDict > copy() const
Return a deep copy of this object.
Definition FrameDict.h:123
std::set< std::string > getAllDomains() const
Get the domain names for all contained Frames (excluding frames with empty or defaulted domain names)...
Definition FrameDict.cc:45
std::shared_ptr< Frame > getFrame(std::string const &domain, bool copy=true) const
Variant of getFrame(int, bool) where the frame is specified by domain name.
Definition FrameDict.h:151
Frame is used to represent a coordinate system.
Definition Frame.h:157
A FrameSet consists of a set of one or more Frames (which describe coordinate systems),...
Definition FrameSet.h:99
static int constexpr CURRENT
index of current frame
Definition FrameSet.h:105
static int constexpr BASE
index of base frame
Definition FrameSet.h:104
SeriesMap then(Mapping const &next) const
Return a series compound mapping this(first(input)) containing shallow copies of the original.
Definition Mapping.cc:37
void show(std::ostream &os, bool showComments=true) const
Print a textual description the object to an ostream.
Definition Object.cc:158
ShiftMap is a linear Mapping which shifts each axis by a specified constant value.
Definition ShiftMap.h:40
A stream for ast::Channel.
Definition Stream.h:41
String-based source and sink for channels.
Definition Stream.h:180
A WinMap is a linear Mapping which transforms a rectangular window in one coordinate system into a si...
Definition WinMap.h:45
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition SkyWcs.h:118
std::shared_ptr< SkyWcs > copyAtShiftedPixelOrigin(lsst::geom::Extent2D const &shift) const
Return a copy of this SkyWcs with the pixel origin shifted by the specified amount.
Definition SkyWcs.cc:225
static std::shared_ptr< SkyWcs > readStream(std::istream &is)
Deserialize a SkyWcs from an input stream.
Definition SkyWcs.cc:325
std::shared_ptr< const ast::FrameDict > getFrameDict() const
Get the contained FrameDict.
Definition SkyWcs.cc:282
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
Definition SkyWcs.h:334
lsst::geom::AffineTransform linearizeSkyToPixel(lsst::geom::SpherePoint const &coord, lsst::geom::AngleUnit const &skyUnit) const
Return the local linear approximation to skyToPixel at a point given in sky coordinates.
Definition SkyWcs.cc:305
std::string toString() const override
Create a string representation of this object.
Definition SkyWcs.cc:373
bool isFlipped() const
Does the WCS follow the convention of North=Up, East=Left?
Definition SkyWcs.cc:317
std::shared_ptr< typehandling::Storable > cloneStorable() const override
Create a new SkyWcs that is a copy of this one.
Definition SkyWcs.cc:369
lsst::geom::Angle getPixelScale() const
Get the pixel scale at the pixel origin.
Definition SkyWcs.h:208
std::shared_ptr< SkyWcs > getTanWcs(lsst::geom::Point2D const &pixel) const
Get a local TAN WCS approximation to this WCS at the specified pixel position.
Definition SkyWcs.cc:218
lsst::geom::Point2D getPixelOrigin() const
Get the pixel origin, in pixels, using the LSST convention.
Definition SkyWcs.h:215
void writeStream(std::ostream &os) const
Serialize this SkyWcs to an output stream.
Definition SkyWcs.cc:358
std::string writeString() const
Serialize this SkyWcs to a string, using the same format as writeStream.
Definition SkyWcs.cc:363
Eigen::Matrix2d getCdMatrix() const
Get the 2x2 CD matrix at the pixel origin.
Definition SkyWcs.cc:216
SkyWcs(SkyWcs const &)=default
lsst::geom::AffineTransform linearizePixelToSky(lsst::geom::SpherePoint const &coord, lsst::geom::AngleUnit const &skyUnit) const
Return the local linear approximation to pixelToSky at a point given in sky coordinates.
Definition SkyWcs.cc:296
std::shared_ptr< const TransformPoint2ToSpherePoint > getTransform() const
Get a TransformPoint2ToSpherePoint that transforms pixels to sky in the forward direction and sky to ...
Definition SkyWcs.h:257
lsst::geom::Point2D skyToPixel(lsst::geom::SpherePoint const &sky) const
Compute pixel position(s) from sky position(s)
Definition SkyWcs.h:349
static std::string getShortClassName()
Definition SkyWcs.cc:315
bool isFits() const
Return true getFitsMetadata(true) will succeed, false if not.
Definition SkyWcs.cc:294
bool hasFitsApproximation() const
Does this SkyWcs have an approximate SkyWcs that can be represented as standard FITS WCS?
Definition SkyWcs.h:362
std::shared_ptr< SkyWcs > getFitsApproximation() const
Return FITS SkyWcs that approximates this one.
Definition SkyWcs.h:369
std::string getPythonModule() const override
Return the fully-qualified Python module that should be imported to guarantee that its factory is reg...
Definition SkyWcs.cc:400
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
Definition SkyWcs.cc:398
lsst::geom::SpherePoint getSkyOrigin() const
Get the sky origin, the celestial fiducial point.
Definition SkyWcs.cc:199
std::shared_ptr< SkyWcs > copyWithFitsApproximation(std::shared_ptr< SkyWcs > fitsApproximation) const
Return a copy of this SkyWcs with the given FITS approximation.
Definition SkyWcs.cc:284
static std::shared_ptr< SkyWcs > readString(std::string &str)
Deserialize a SkyWcs from a string, using the same format as readStream.
Definition SkyWcs.cc:353
std::shared_ptr< daf::base::PropertyList > getFitsMetadata(bool precise=false) const
Return the WCS as FITS WCS metadata.
Definition SkyWcs.cc:269
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Definition SkyWcs.cc:402
bool equals(typehandling::Storable const &other) const noexcept override
Compare this object to another Storable.
Definition SkyWcs.cc:394
bool operator==(SkyWcs const &other) const
Equality is based on the string representations being equal.
Definition SkyWcs.cc:173
std::shared_ptr< const ast::Mapping > getMapping() const
Get the contained mapping.
Definition Transform.h:135
Tag types used to declare specialized field types.
Definition misc.h:31
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Definition Catalog.h:490
A class used as a handle to a particular field in a table.
Definition Key.h:53
Defines the fields and offsets for a table.
Definition Schema.h:51
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
A base class for factory classes used to reconstruct objects from records.
io::OutputArchiveHandle OutputArchiveHandle
Interface supporting iteration over heterogenous containers.
Definition Storable.h:58
static bool singleClassEquals(T const &lhs, Storable const &rhs)
Test if a Storable is of a particular class and equal to another object.
Definition Storable.h:151
Class for storing generic metadata.
Definition PropertySet.h:67
An affine coordinate transformation consisting of a linear transformation and an offset.
AffineTransform const inverted() const
Return the inverse transform.
A class representing an angle.
Definition Angle.h:128
constexpr double asArcseconds() const noexcept
Return an Angle's value in arcseconds.
Definition Angle.h:185
A class used to convert scalar POD types such as double to Angle.
Definition Angle.h:71
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
std::pair< Angle, Angle > getTangentPlaneOffset(SpherePoint const &other) const
Get the offset from a tangent plane centered at this point to another point.
Point2D getPosition(AngleUnit unit) const noexcept
Return longitude, latitude as a Point2D object.
Reports errors in the logical structure of the program.
Definition Runtime.h:46
Reports errors that are due to events beyond the control of the program.
Definition Runtime.h:104
Reports errors from accepting an object of an unexpected or inappropriate type.
Definition Runtime.h:167
T cos(T... args)
T make_shared(T... args)
ndarray::Array< std::uint8_t, 1, 1 > stringToBytes(std::string const &str)
Encode a std::string as a vector of uint8.
Definition Utils.cc:43
std::shared_ptr< daf::base::PropertyList > getPropertyListFromFitsChan(ast::FitsChan &fitsChan)
Copy values from an AST FitsChan into a PropertyList.
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition SkyWcs.cc:545
std::shared_ptr< daf::base::PropertyList > makeSimpleWcsMetadata(lsst::geom::Point2D const &crpix, lsst::geom::SpherePoint const &crval, Eigen::Matrix2d const &cdMatrix, std::string const &projection="TAN")
Make FITS metadata for a simple FITS WCS (one with no distortion).
Definition wcsUtils.cc:169
std::shared_ptr< TransformPoint2ToPoint2 > getPixelToIntermediateWorldCoords(SkyWcs const &wcs, bool simplify=true)
Return a transform from pixel coordinates to intermediate world coordinates.
Definition SkyWcs.cc:583
std::shared_ptr< SkyWcs > makeTanSipWcs(lsst::geom::Point2D const &crpix, lsst::geom::SpherePoint const &crval, Eigen::Matrix2d const &cdMatrix, Eigen::MatrixXd const &sipA, Eigen::MatrixXd const &sipB)
Construct a TAN-SIP SkyWcs with forward SIP distortion terms and an iterative inverse.
Definition SkyWcs.cc:562
std::ostream & operator<<(std::ostream &os, GenericEndpoint const &endpoint)
Print "GenericEndpoint(_n_)" to the ostream where _n_ is the number of axes, e.g. "GenericAxes(4)".
Definition Endpoint.cc:239
Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const &scale, lsst::geom::Angle const &orientation=0 *lsst::geom::degrees, bool flipX=false)
Make a WCS CD matrix.
Definition SkyWcs.cc:150
std::shared_ptr< TransformPoint2ToSpherePoint > getIntermediateWorldCoordsToSky(SkyWcs const &wcs, bool simplify=true)
Return a transform from intermediate world coordinates to sky.
Definition SkyWcs.cc:577
std::shared_ptr< daf::base::PropertyList > makeTanSipMetadata(lsst::geom::Point2D const &crpix, lsst::geom::SpherePoint const &crval, Eigen::Matrix2d const &cdMatrix, Eigen::MatrixXd const &sipA, Eigen::MatrixXd const &sipB)
Make metadata for a TAN-SIP WCS without inverse matrices.
Definition wcsUtils.cc:194
std::shared_ptr< SkyWcs > makeModifiedWcs(TransformPoint2ToPoint2 const &pixelTransform, SkyWcs const &wcs, bool modifyActualPixels)
Create a new SkyWcs whose pixels are transformed by pixelTransform, as described below.
Definition SkyWcs.cc:510
Transform< Point2Endpoint, Point2Endpoint > TransformPoint2ToPoint2
Definition Transform.h:300
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
Definition SkyWcs.cc:163
std::shared_ptr< SkyWcs > makeFlippedWcs(SkyWcs const &wcs, bool flipLR, bool flipTB, lsst::geom::Point2D const &center)
Return a copy of a FITS-WCS with pixel positions flipped around a specified center.
Definition SkyWcs.cc:491
CatalogT< BaseRecord > BaseCatalog
Definition fwd.h:72
AngleUnit constexpr degrees
constant with units of degrees
Definition Angle.h:110
Extent< double, 2 > Extent2D
Definition Extent.h:400
AngleUnit constexpr radians
constant with units of radians
Definition Angle.h:109
Point< double, 2 > Point2D
Definition Point.h:324
double constexpr PI
The ratio of a circle's circumference to diameter.
Definition Angle.h:40
T dynamic_pointer_cast(T... args)
T pow(T... args)
T sin(T... args)
T size(T... args)
T str(T... args)
T to_string(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override