LSST Applications 30.0.7,g0e76e35be5+e8e946ae08,g19811a7679+138f7293ba,g199a45376c+5e234f8357,g1fd858c14a+2f48dbc4c4,g262e1987ae+fb36cac54d,g29ae962dfc+d9108a0941,g2c21b0017a+4f59a27f16,g31e44d4a5c+b0138be388,g33ac35c1f1+28b9f72785,g35bb328faa+b0138be388,g40c9b15c53+823ad735c1,g47891489e3+bcc48a0b46,g53246c7159+b0138be388,g64539dfbff+e8e946ae08,g67b6fd64d1+bcc48a0b46,g74acd417e5+422380537a,g76965917b2+a5ca99c4d9,g786e29fd12+796b79145d,g7aefaa3e3d+dc0c200193,g86b635cae8+734fe384f0,g87389fa792+d8b5378923,g89139ef638+bcc48a0b46,g8bbb235e95+3f4f7f9447,g8ea07a8fe4+78a4c88802,g9290983e33+ffdc83c6f7,g92c671f44c+e8e946ae08,gaa753fd333+03f406da14,gbf99507273+b0138be388,gc49b57b85e+8df26ee1f0,gca7fc764a6+bcc48a0b46,gd7ef33dd92+bcc48a0b46,gdab6d2f7ff+422380537a,ge1c02a5578+b0138be388,ge410e46f29+bcc48a0b46,ge80df9fc40+e6db5413d1,geaed405ab2+1de65a85c6,gf5dcc679e7+35a0ce2edd,gf5f1c85443+e8e946ae08
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 the bounding box provided.
60// For more information,
61// see FitsTol in the AST manual http://starlink.eao.hawaii.edu/devdocs/sun211.htx/sun211.html
62double const TIGHT_FITS_TOL = 0.0001;
63
64class SkyWcsPersistenceHelper {
65public:
69
70 // No copying
71 SkyWcsPersistenceHelper(const SkyWcsPersistenceHelper&) = delete;
72 SkyWcsPersistenceHelper& operator=(const SkyWcsPersistenceHelper&) = delete;
73
74 // No moving
75 SkyWcsPersistenceHelper(SkyWcsPersistenceHelper&&) = delete;
76 SkyWcsPersistenceHelper& operator=(SkyWcsPersistenceHelper&&) = delete;
77
78 explicit SkyWcsPersistenceHelper(bool hasFitsApproximation)
79 : schema(),
80 wcs(schema.addField<table::Array<std::uint8_t>>("wcs", "wcs string representation", "")) {
81 if (hasFitsApproximation) {
82 approx = schema.addField<table::Array<std::uint8_t>>(
83 "approx", "wcs string representation of FITS approximation", "");
84 }
85 }
86
87 explicit SkyWcsPersistenceHelper(table::Schema const& schema)
88 : schema(schema), wcs(schema["wcs"]), approx() {
89 try {
90 approx = schema["approx"];
91 } catch (pex::exceptions::NotFoundError&) {
92 }
93 }
94};
95
96class SkyWcsFactory : public table::io::PersistableFactory {
97public:
98 explicit SkyWcsFactory(std::string const& name) : table::io::PersistableFactory(name) {}
99
100 std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
101 CatalogVector const& catalogs) const override {
102 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
103 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
104 SkyWcsPersistenceHelper keys(catalogs.front().getSchema());
105 table::BaseRecord const& record = catalogs.front().front();
106 std::string stringRep = formatters::bytesToString(record.get(keys.wcs));
107 auto result = SkyWcs::readString(stringRep);
108 if (keys.approx.isValid()) {
109 auto bytes = record.get(keys.approx);
110 if (!bytes.isEmpty()) {
111 auto approxStringRep = formatters::bytesToString(bytes);
112 result = result->copyWithFitsApproximation(SkyWcs::readString(approxStringRep));
113 }
114 }
115 return result;
116 }
117};
118
119std::string getSkyWcsPersistenceName() { return "SkyWcs"; }
120
121SkyWcsFactory registration(getSkyWcsPersistenceName());
122
123ast::FrameDict makeSkyWcsFrameDict(TransformPoint2ToPoint2 const& pixelsToFieldAngle,
124 lsst::geom::Angle const& orientation, bool flipX,
125 lsst::geom::SpherePoint const& crval,
126 std::string const& projection = "TAN") {
127 auto const orientationAndFlipXMatrix = makeCdMatrix(1 * lsst::geom::degrees, orientation, flipX);
128 auto const initialWcs =
129 makeSkyWcs(lsst::geom::Point2D(0, 0), crval, orientationAndFlipXMatrix, projection);
130 auto const initialFrameDict = initialWcs->getFrameDict();
131 auto const iwcToSkyMap = initialFrameDict->getMapping("IWC", "SKY");
132 auto const pixelFrame = initialFrameDict->getFrame("PIXELS");
133 auto const iwcFrame = initialFrameDict->getFrame("IWC");
134 auto const skyFrame = initialFrameDict->getFrame("SKY");
135 // Field angle is in radians and is aligned to focal plane x and y;
136 // IWC is basically the same thing, but in degrees and with rotation and flipX applied
137 ndarray::Array<double, 2, 2> fieldAngleToIwcNdArray = ndarray::allocate(2, 2);
138 asEigenMatrix(fieldAngleToIwcNdArray) = orientationAndFlipXMatrix * 180.0 / lsst::geom::PI;
139 auto const pixelsToFieldAngleMap = pixelsToFieldAngle.getMapping();
140 auto const fieldAngleToIwcMap = ast::MatrixMap(fieldAngleToIwcNdArray);
141 auto const pixelsToIwcMap = pixelsToFieldAngleMap->then(fieldAngleToIwcMap);
142 auto finalFrameDict = ast::FrameDict(*pixelFrame, pixelsToIwcMap, *iwcFrame);
143 finalFrameDict.addFrame("IWC", *iwcToSkyMap, *skyFrame);
144 return finalFrameDict;
145}
146
147} // namespace
148
149Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const& scale, lsst::geom::Angle const& orientation,
150 bool flipX) {
151 Eigen::Matrix2d cdMatrix;
152 double orientRad = orientation.asRadians();
153 double scaleDeg = scale.asDegrees();
154 double xmult = flipX ? 1.0 : -1.0;
155 cdMatrix(0, 0) = std::cos(orientRad) * scaleDeg * xmult;
156 cdMatrix(0, 1) = std::sin(orientRad) * scaleDeg;
157 cdMatrix(1, 0) = -std::sin(orientRad) * scaleDeg * xmult;
158 cdMatrix(1, 1) = std::cos(orientRad) * scaleDeg;
159 return cdMatrix;
160}
161
163 auto const dstInverse = dst.getTransform()->inverted();
164 return src.getTransform()->then(*dstInverse);
165}
166
168 : SkyWcs(detail::readLsstSkyWcs(metadata, strip)) {}
169
170SkyWcs::SkyWcs(ast::FrameDict const& frameDict) : SkyWcs(_checkFrameDict(frameDict)) {}
171
172bool SkyWcs::operator==(SkyWcs const& other) const { return writeString() == other.writeString(); }
173
175 // Compute pixVec containing the pixel position and two nearby points
176 // (use a vector so all three points can be converted to sky in a single call)
177 double const side = 1.0;
179 pixel,
180 pixel + lsst::geom::Extent2D(side, 0),
181 pixel + lsst::geom::Extent2D(0, side),
182 };
183
184 auto skyVec = pixelToSky(pixVec);
185
186 // Work in 3-space to avoid RA wrapping and pole issues
187 auto skyLL = skyVec[0].getVector();
188 auto skyDx = skyVec[1].getVector() - skyLL;
189 auto skyDy = skyVec[2].getVector() - skyLL;
190
191 // Compute pixel scale in radians = sqrt(pixel area in radians^2)
192 // pixel area in radians^2 = area of parallelogram with sides skyDx, skyDy = |skyDx cross skyDy|
193 // Use squared norm to avoid two square roots
194 double skyAreaSq = skyDx.cross(skyDy).getSquaredNorm();
195 return (std::pow(skyAreaSq, 0.25) / side) * lsst::geom::radians;
196}
197
199 // CRVAL is stored as the SkyRef property of the sky frame (the current frame of the SkyWcs)
201 getFrameDict()->getFrame(ast::FrameDict::CURRENT, false)); // false: do not copy
202 if (!skyFrame) {
203 throw LSST_EXCEPT(pex::exceptions::LogicError, "Current frame is not a SkyFrame");
204 }
205 auto const crvalRad = skyFrame->getSkyRef();
206 return lsst::geom::SpherePoint(crvalRad[0] * lsst::geom::radians, crvalRad[1] * lsst::geom::radians);
207}
208
209Eigen::Matrix2d SkyWcs::getCdMatrix(lsst::geom::Point2D const& pixel) const {
210 auto const pixelToIwc = getFrameDict()->getMapping(ast::FrameSet::BASE, "IWC");
211 auto const pixelToIwcTransform = TransformPoint2ToPoint2(*pixelToIwc);
212 return pixelToIwcTransform.getJacobian(pixel);
213}
214
215Eigen::Matrix2d SkyWcs::getCdMatrix() const { return getCdMatrix(getPixelOrigin()); }
216
218 auto const crval = pixelToSky(pixel);
219 auto const cdMatrix = getCdMatrix(pixel);
220 auto metadata = makeSimpleWcsMetadata(pixel, crval, cdMatrix);
221 return std::make_shared<SkyWcs>(*metadata);
222}
223
225 auto newToOldPixel = TransformPoint2ToPoint2(ast::ShiftMap({-shift[0], -shift[1]}));
226 return makeModifiedWcs(newToOldPixel, *this, true);
227}
228
229std::shared_ptr<daf::base::PropertyList> SkyWcs::_getDirectFitsMetadata(
230 lsst::geom::Box2I const & bbox
231) const {
232 // Make a FrameSet that maps from GRID to SKY; GRID = the base frame (PIXELS or ACTUAL_PIXELS) + 1
233 auto const gridToPixel = ast::ShiftMap({bbox.getBeginX() - 1.0, bbox.getBeginY() - 1.0});
234 auto thisDict = getFrameDict();
235 auto const pixelToIwc = thisDict->getMapping(ast::FrameSet::BASE, "IWC");
236 auto const iwcToSky = thisDict->getMapping("IWC", "SKY");
237 auto const gridToSky = gridToPixel.then(*pixelToIwc).then(*iwcToSky);
238 ast::FrameSet frameSet(ast::Frame(2, "Domain=GRID"), gridToSky, *thisDict->getFrame("SKY", false));
239
240 // Write frameSet to a FitsChan and extract the metadata. We pass NAXIS
241 // to tell AST the region we care about in case it needs to do any
242 // calculations that are not exact (e.g. SIP inverse polynomials).
244 os << "Encoding=FITS-WCS, CDMatrix=1, FitsAxisOrder=<copy>, FitsTol=" << TIGHT_FITS_TOL;
245 ast::StringStream strStream;
246 ast::FitsChan fitsChan(strStream, os.str());
247 fitsChan.setFitsI("NAXIS1", bbox.getWidth());
248 fitsChan.setFitsI("NAXIS2", bbox.getHeight());
249 int const nObjectsWritten = fitsChan.write(frameSet);
250 if (nObjectsWritten == 0) {
251 return nullptr;
252 }
254
255 // Drop NAXIS1 and NAXIS2, as we want to leave that to the image to set.
256 header->remove("NAXIS1");
257 header->remove("NAXIS2");
258
259 // Remove DATE-OBS, MJD-OBS: AST writes these if the EQUINOX is set, but we set them via other mechanisms.
260 header->remove("DATE-OBS");
261 header->remove("MJD-OBS");
262
263 // If CD matrix is present, explicitly set any missing entries to zero, as a convenience to the user
264 bool const hasCd11 = header->exists("CD1_1");
265 bool const hasCd12 = header->exists("CD1_2");
266 bool const hasCd21 = header->exists("CD2_1");
267 bool const hasCd22 = header->exists("CD2_2");
268 if (hasCd11 || hasCd12 || hasCd21 || hasCd22) {
269 if (!hasCd11) header->set("CD1_1", 0.0, "Transformation matrix element");
270 if (!hasCd12) header->set("CD1_2", 0.0, "Transformation matrix element");
271 if (!hasCd21) header->set("CD2_1", 0.0, "Transformation matrix element");
272 if (!hasCd22) header->set("CD2_2", 0.0, "Transformation matrix element");
273 }
274
275 return header;
276}
277
279 bool precise, lsst::geom::Box2I const & bbox
280) const {
281 if (!precise && hasFitsApproximation()) {
282 return getFitsApproximation()->_getDirectFitsMetadata(bbox);
283 }
284 auto result = _getDirectFitsMetadata(bbox);
285 if (!result) {
287 precise ? "WCS is not directly FITS-compatible."
288 : "WCS does not have an attached FITS approximation.");
289 }
290 return result;
291}
292
294
296 if (fitsApproximation && fitsApproximation->hasFitsApproximation()) {
298 "Cannot add a FITS approximation that itself already has a FITS approximation.");
299 }
300 auto result = std::make_shared<SkyWcs>(*this);
301 result->_fitsApproximation = fitsApproximation;
302 return result;
303}
304
305bool SkyWcs::isFits() const {
306 return bool(_getDirectFitsMetadata(
308 ));
309}
310
312 lsst::geom::AngleUnit const& skyUnit) const {
313 return _linearizePixelToSky(skyToPixel(coord), coord, skyUnit);
314}
316 lsst::geom::AngleUnit const& skyUnit) const {
317 return _linearizePixelToSky(pix, pixelToSky(pix), skyUnit);
318}
319
321 lsst::geom::AngleUnit const& skyUnit) const {
322 return _linearizeSkyToPixel(skyToPixel(coord), coord, skyUnit);
323}
324
326 lsst::geom::AngleUnit const& skyUnit) const {
327 return _linearizeSkyToPixel(pix, pixelToSky(pix), skyUnit);
328}
329
331
332bool SkyWcs::isFlipped() const {
333 double det = getCdMatrix().determinant();
334 if (det == 0) {
335 throw(LSST_EXCEPT(pex::exceptions::RuntimeError, "CD matrix is singular"));
336 }
337 return (det > 0);
338}
339
341 int version;
342 is >> version;
343 if (version != 1) {
344 throw LSST_EXCEPT(pex::exceptions::TypeError, "Unsupported version " + std::to_string(version));
345 }
346 std::string shortClassName;
347 is >> shortClassName;
348 if (shortClassName != SkyWcs::getShortClassName()) {
350 os << "Class name in stream " << shortClassName << " != " << SkyWcs::getShortClassName();
352 }
355 auto astStream = ast::Stream(&is, nullptr);
356 auto astObjectPtr = ast::Channel(astStream).read();
357 auto frameSet = std::dynamic_pointer_cast<ast::FrameSet>(astObjectPtr);
358 if (!frameSet) {
360 os << "The AST serialization was read as a " << astObjectPtr->getClassName()
361 << " instead of a FrameSet";
363 }
364 ast::FrameDict frameDict(*frameSet);
365 return std::make_shared<SkyWcs>(frameDict);
366}
367
372
374 os << SERIALIZATION_VERSION << " " << SkyWcs::getShortClassName() << " " << hasFitsApproximation() << " ";
375 getFrameDict()->show(os, false); // false = do not write comments
376}
377
380 writeStream(os);
381 return os.str();
382}
383
385 return std::make_unique<SkyWcs>(*this);
386}
387
390 if (isFits()) {
391 os << "FITS standard SkyWcs:";
392 } else {
393 os << "Non-standard SkyWcs (Frames: ";
394 // Print the frames in index order (frames are numbered from 1).
395 std::string delimiter = "";
396 for (size_t i = 1; i <= getFrameDict()->getAllDomains().size(); ++i) {
397 os << delimiter << getFrameDict()->getFrame(i)->getDomain();
398 delimiter = ", ";
399 }
400 os << "): ";
401 }
402 std::string delimiter = "\n";
403 os << delimiter << "Sky Origin: " << getSkyOrigin();
404 os << delimiter << "Pixel Origin: " << getPixelOrigin();
405 os << delimiter << "Pixel Scale: " << getPixelScale().asArcseconds() << " arcsec/pixel";
406 return os.str();
407}
408
409bool SkyWcs::equals(typehandling::Storable const& other) const noexcept {
410 return singleClassEquals(*this, other);
411}
412
413std::string SkyWcs::getPersistenceName() const { return getSkyWcsPersistenceName(); }
414
415std::string SkyWcs::getPythonModule() const { return "lsst.afw.geom"; }
416
418 SkyWcsPersistenceHelper const keys(hasFitsApproximation());
419 table::BaseCatalog cat = handle.makeCatalog(keys.schema);
421 record->set(keys.wcs, formatters::stringToBytes(writeString()));
422 if (hasFitsApproximation()) {
423 record->set(keys.approx, formatters::stringToBytes(getFitsApproximation()->writeString()));
424 }
425 handle.saveCatalog(cat);
426}
427
429 : _frameDict(frameDict), _transform(), _pixelOrigin(), _pixelScaleAtOrigin(0 * lsst::geom::radians) {
430 _computeCache();
431};
432
433std::shared_ptr<ast::FrameDict> SkyWcs::_checkFrameDict(ast::FrameDict const& frameDict) const {
434 // Check that each frame is present and has the right type and number of axes
435 std::vector<std::string> const domainNames = {"ACTUAL_PIXELS", "PIXELS", "IWC", "SKY"};
436 for (auto const& domainName : domainNames) {
437 if (frameDict.hasDomain(domainName)) {
438 auto const frame = frameDict.getFrame(domainName, false);
439 if (frame->getNAxes() != 2) {
441 "Frame " + domainName + " has " + std::to_string(frame->getNAxes()) +
442 " axes instead of 2");
443 }
444 auto desiredClassName = domainName == "SKY" ? "SkyFrame" : "Frame";
445 if (frame->getClassName() != desiredClassName) {
447 "Frame " + domainName + " is of type " + frame->getClassName() +
448 " instead of " + desiredClassName);
449 }
450 } else if (domainName != "ACTUAL_PIXELS") {
451 // This is a required frame
452 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
453 "No frame with domain " + domainName + " found");
454 }
455 }
456
457 // The base frame must have domain "PIXELS" or "ACTUAL_PIXELS"
458 auto baseDomain = frameDict.getFrame(ast::FrameSet::BASE, false)->getDomain();
459 if (baseDomain != "ACTUAL_PIXELS" && baseDomain != "PIXELS") {
460 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
461 "Base frame has domain " + baseDomain + " instead of PIXELS or ACTUAL_PIXELS");
462 }
463
464 // The current frame must have domain "SKY"
465 auto currentDomain = frameDict.getFrame(ast::FrameSet::CURRENT, false)->getDomain();
466 if (currentDomain != "SKY") {
467 throw LSST_EXCEPT(lsst::pex::exceptions::TypeError,
468 "Current frame has domain " + currentDomain + " instead of SKY");
469 }
470
471 return frameDict.copy();
472}
473
474lsst::geom::AffineTransform SkyWcs::_linearizePixelToSky(lsst::geom::Point2D const& pix00,
475 lsst::geom::SpherePoint const& coord,
476 lsst::geom::AngleUnit const& skyUnit) const {
477 // Figure out the (0, 0), (0, 1), and (1, 0) ra/dec coordinates of the corners
478 // of a square drawn in pixel. It'd be better to center the square at sky00,
479 // but that would involve another conversion between sky and pixel coordinates
480 const double side = 1.0; // length of the square's sides in pixels
481 auto const sky00 = coord.getPosition(skyUnit);
482 auto const dsky10 = coord.getTangentPlaneOffset(pixelToSky(pix00 + lsst::geom::Extent2D(side, 0)));
483 auto const dsky01 = coord.getTangentPlaneOffset(pixelToSky(pix00 + lsst::geom::Extent2D(0, side)));
484
485 Eigen::Matrix2d m;
486 m(0, 0) = dsky10.first.asAngularUnits(skyUnit) / side;
487 m(0, 1) = dsky01.first.asAngularUnits(skyUnit) / side;
488 m(1, 0) = dsky10.second.asAngularUnits(skyUnit) / side;
489 m(1, 1) = dsky01.second.asAngularUnits(skyUnit) / side;
490
491 Eigen::Vector2d sky00v;
492 sky00v << sky00.getX(), sky00.getY();
493 Eigen::Vector2d pix00v;
494 pix00v << pix00.getX(), pix00.getY();
495 // return lsst::geom::AffineTransform(m, lsst::geom::Extent2D(sky00v - m * pix00v));
496 return lsst::geom::AffineTransform(m, (sky00v - m * pix00v));
497}
498
499lsst::geom::AffineTransform SkyWcs::_linearizeSkyToPixel(lsst::geom::Point2D const& pix00,
500 lsst::geom::SpherePoint const& coord,
501 lsst::geom::AngleUnit const& skyUnit) const {
502 lsst::geom::AffineTransform inverse = _linearizePixelToSky(pix00, coord, skyUnit);
503 return inverse.inverted();
504}
505
506std::shared_ptr<SkyWcs> makeFlippedWcs(SkyWcs const& wcs, bool flipLR, bool flipTB,
507 lsst::geom::Point2D const& center) {
508 double const dx = 1000; // any "reasonable" number of pixels will do
509 std::vector<double> inLL = {center[0] - dx, center[1] - dx};
510 std::vector<double> inUR = {center[0] + dx, center[1] + dx};
511 std::vector<double> outLL(inLL);
512 std::vector<double> outUR(inUR);
513 if (flipLR) {
514 outLL[0] = inUR[0];
515 outUR[0] = inLL[0];
516 }
517 if (flipTB) {
518 outLL[1] = inUR[1];
519 outUR[1] = inLL[1];
520 }
521 auto const flipPix = TransformPoint2ToPoint2(ast::WinMap(inLL, inUR, outLL, outUR));
522 return makeModifiedWcs(flipPix, wcs, true);
523}
524
526 bool modifyActualPixels) {
527 auto const pixelMapping = pixelTransform.getMapping();
528 auto oldFrameDict = wcs.getFrameDict();
529 bool const hasActualPixels = oldFrameDict->hasDomain("ACTUAL_PIXELS");
530 auto const pixelFrame = oldFrameDict->getFrame("PIXELS", false);
531 auto const iwcFrame = oldFrameDict->getFrame("IWC", false);
532 auto const skyFrame = oldFrameDict->getFrame("SKY", false);
533 auto const oldPixelToIwc = oldFrameDict->getMapping("PIXELS", "IWC");
534 auto const iwcToSky = oldFrameDict->getMapping("IWC", "SKY");
535
537 std::shared_ptr<ast::Mapping> newPixelToIwc;
538 if (hasActualPixels) {
539 auto const actualPixelFrame = oldFrameDict->getFrame("ACTUAL_PIXELS", false);
540 auto const oldActualPixelToPixels = oldFrameDict->getMapping("ACTUAL_PIXELS", "PIXELS");
541 std::shared_ptr<ast::Mapping> newActualPixelsToPixels;
542 if (modifyActualPixels) {
543 newActualPixelsToPixels = pixelMapping->then(*oldActualPixelToPixels).simplified();
544 newPixelToIwc = oldPixelToIwc;
545 } else {
546 newActualPixelsToPixels = oldActualPixelToPixels;
547 newPixelToIwc = pixelMapping->then(*oldPixelToIwc).simplified();
548 }
549 newFrameDict =
550 std::make_shared<ast::FrameDict>(*actualPixelFrame, *newActualPixelsToPixels, *pixelFrame);
551 newFrameDict->addFrame("PIXELS", *newPixelToIwc, *iwcFrame);
552 } else {
553 newPixelToIwc = pixelMapping->then(*oldPixelToIwc).simplified();
554 newFrameDict = std::make_shared<ast::FrameDict>(*pixelFrame, *newPixelToIwc, *iwcFrame);
555 }
556 newFrameDict->addFrame("IWC", *iwcToSky, *skyFrame);
557 return std::make_shared<SkyWcs>(*newFrameDict);
558}
559
561 return std::make_shared<SkyWcs>(metadata, strip);
562}
563
565 Eigen::Matrix2d const& cdMatrix, std::string const& projection) {
566 auto metadata = makeSimpleWcsMetadata(crpix, crval, cdMatrix, projection);
567 return std::make_shared<SkyWcs>(*metadata);
568}
569
571 lsst::geom::Angle const& orientation, bool flipX,
572 lsst::geom::SpherePoint const& boresight, std::string const& projection) {
573 auto frameDict = makeSkyWcsFrameDict(pixelsToFieldAngle, orientation, flipX, boresight, projection);
574 return std::make_shared<SkyWcs>(frameDict);
575}
576
578 Eigen::Matrix2d const& cdMatrix, Eigen::MatrixXd const& sipA,
579 Eigen::MatrixXd const& sipB) {
580 auto metadata = makeTanSipMetadata(crpix, crval, cdMatrix, sipA, sipB);
581 return std::make_shared<SkyWcs>(*metadata);
582}
583
585 Eigen::Matrix2d const& cdMatrix, Eigen::MatrixXd const& sipA,
586 Eigen::MatrixXd const& sipB, Eigen::MatrixXd const& sipAp,
587 Eigen::MatrixXd const& sipBp) {
588 auto metadata = makeTanSipMetadata(crpix, crval, cdMatrix, sipA, sipB, sipAp, sipBp);
589 return std::make_shared<SkyWcs>(*metadata);
590}
591
593 bool simplify) {
594 auto iwcToSky = wcs.getFrameDict()->getMapping("IWC", "SKY");
595 return std::make_shared<TransformPoint2ToSpherePoint>(*iwcToSky, simplify);
596}
597
599 auto pixelToIwc = wcs.getFrameDict()->getMapping(ast::FrameSet::BASE, "IWC");
600 return std::make_shared<TransformPoint2ToPoint2>(*pixelToIwc, simplify);
601}
602
604 os << wcs.toString();
605 return os;
606};
607
608} // namespace geom
609} // namespace afw
610} // 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:160
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:119
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:224
static std::shared_ptr< SkyWcs > readStream(std::istream &is)
Deserialize a SkyWcs from an input stream.
Definition SkyWcs.cc:340
std::shared_ptr< const ast::FrameDict > getFrameDict() const
Get the contained FrameDict.
Definition SkyWcs.cc:293
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
Definition SkyWcs.h:344
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:320
std::string toString() const override
Create a string representation of this object.
Definition SkyWcs.cc:388
bool isFlipped() const
Does the WCS follow the convention of North=Up, East=Left?
Definition SkyWcs.cc:332
std::shared_ptr< typehandling::Storable > cloneStorable() const override
Create a new SkyWcs that is a copy of this one.
Definition SkyWcs.cc:384
lsst::geom::Angle getPixelScale() const
Get the pixel scale at the pixel origin.
Definition SkyWcs.h:218
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:217
lsst::geom::Point2D getPixelOrigin() const
Get the pixel origin, in pixels, using the LSST convention.
Definition SkyWcs.h:225
void writeStream(std::ostream &os) const
Serialize this SkyWcs to an output stream.
Definition SkyWcs.cc:373
std::string writeString() const
Serialize this SkyWcs to a string, using the same format as writeStream.
Definition SkyWcs.cc:378
Eigen::Matrix2d getCdMatrix() const
Get the 2x2 CD matrix at the pixel origin.
Definition SkyWcs.cc:215
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:311
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:267
lsst::geom::Point2D skyToPixel(lsst::geom::SpherePoint const &sky) const
Compute pixel position(s) from sky position(s)
Definition SkyWcs.h:359
static std::string getShortClassName()
Definition SkyWcs.cc:330
bool isFits() const
Return true getFitsMetadata(true) will succeed with no arguments, false if not.
Definition SkyWcs.cc:305
bool hasFitsApproximation() const
Does this SkyWcs have an approximate SkyWcs that can be represented as standard FITS WCS?
Definition SkyWcs.h:372
std::shared_ptr< SkyWcs > getFitsApproximation() const
Return FITS SkyWcs that approximates this one.
Definition SkyWcs.h:379
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:415
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
Definition SkyWcs.cc:413
std::shared_ptr< daf::base::PropertyList > getFitsMetadata(bool precise=false, lsst::geom::Box2I const &bbox=lsst::geom::Box2I(lsst::geom::Point2I(0, 0), lsst::geom::Extent2I(100, 100))) const
Return the WCS as FITS WCS metadata.
Definition SkyWcs.cc:278
lsst::geom::SpherePoint getSkyOrigin() const
Get the sky origin, the celestial fiducial point.
Definition SkyWcs.cc:198
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:295
static std::shared_ptr< SkyWcs > readString(std::string &str)
Deserialize a SkyWcs from a string, using the same format as readStream.
Definition SkyWcs.cc:368
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Definition SkyWcs.cc:417
bool equals(typehandling::Storable const &other) const noexcept override
Compare this object to another Storable.
Definition SkyWcs.cc:409
bool operator==(SkyWcs const &other) const
Equality is based on the string representations being equal.
Definition SkyWcs.cc:172
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
An integer coordinate rectangle.
Definition Box.h:55
int getBeginX() const noexcept
Definition Box.h:172
int getHeight() const noexcept
Definition Box.h:188
int getWidth() const noexcept
Definition Box.h:187
int getBeginY() const noexcept
Definition Box.h:173
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:560
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:598
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:577
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:149
std::shared_ptr< TransformPoint2ToSpherePoint > getIntermediateWorldCoordsToSky(SkyWcs const &wcs, bool simplify=true)
Return a transform from intermediate world coordinates to sky.
Definition SkyWcs.cc:592
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:525
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:162
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:506
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
Extent< int, 2 > Extent2I
Definition Extent.h:397
Point< int, 2 > Point2I
Definition Point.h:321
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