Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-39053: Add WCS metadata stripping code that doesn't create a WCS. #687

Merged
merged 3 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/lsst/afw/geom/detail/frameSetUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ All frames are instances of ast::Frame except the SKY frame. All have 2 axes.
*/
std::shared_ptr<ast::FrameDict> readLsstSkyWcs(daf::base::PropertySet& metadata, bool strip = true);

/**
Strip all WCS metadata from a header.

@param[in,out] metadata FITS header cards
*/
void stripWcsMetadata(daf::base::PropertySet &metadata);

/**
Copy values from an AST FitsChan into a PropertyList

Expand Down
2 changes: 2 additions & 0 deletions include/lsst/afw/geom/wcsUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ std::shared_ptr<daf::base::PropertyList> makeTanSipMetadata(
Eigen::Matrix2d const& cdMatrix, Eigen::MatrixXd const& sipA, Eigen::MatrixXd const& sipB,
Eigen::MatrixXd const& sipAp, Eigen::MatrixXd const& sipBp);

void stripWcsMetadata(daf::base::PropertySet &metadata);

} // namespace geom
} // namespace afw
} // namespace lsst
Expand Down
1 change: 1 addition & 0 deletions python/lsst/afw/geom/_wcsUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void wrapWcsUtils(lsst::utils::python::WrapperCollection &wrappers) {
Eigen::MatrixXd const &, Eigen::MatrixXd const &, Eigen::MatrixXd const &,
Eigen::MatrixXd const &))makeTanSipMetadata,
"crpix"_a, "crval"_a, "cdMatrix"_a, "sipA"_a, "sipB"_a, "sipAp"_a, "sipBp"_a);
mod.def("stripWcsMetadata", stripWcsMetadata, "metadata"_a);
});
}

Expand Down
45 changes: 45 additions & 0 deletions src/geom/detail/frameSetUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,51 @@ std::shared_ptr<ast::FrameSet> readFitsWcs(daf::base::PropertySet& metadata, boo
return frameSet;
}

void stripWcsMetadata(daf::base::PropertySet &metadata) {
// Exclude WCS A keywords because LSST uses them to store XY0
auto wcsANames = createTrivialWcsMetadata("A", lsst::geom::Point2I(0, 0))->names();
std::set<std::string> excludeNames(wcsANames.begin(), wcsANames.end());
// Ignore NAXIS1, NAXIS2 because if they are zero then AST will fail to read a WCS
// Ignore LTV1/2 because LSST adds it and this code should ignore it and not strip it
// Exclude comments and history to reduce clutter
std::set<std::string> moreNames{"NAXIS1", "NAXIS2", "LTV1", "LTV2", "COMMENT", "HISTORY"};
excludeNames.insert(moreNames.begin(), moreNames.end());

// Replace RADECSYS with RADESYS if only the former is present
if (metadata.exists("RADECSYS") && !metadata.exists("RADESYS")) {
metadata.set("RADESYS", metadata.getAsString("RADECSYS"));
metadata.remove("RADECSYS");
}

auto channel = getFitsChanFromPropertyList(metadata, excludeNames,
"Encoding=FITS-WCS, IWC=1, SipReplace=0, ReportLevel=3");
auto const initialNames = setFromVector(channel.getAllCardNames());

std::shared_ptr<ast::Object> obj;
try {
obj = channel.read();
} catch (std::runtime_error const&) {
return;
}

auto const finalNames = setFromVector(channel.getAllCardNames());

// FITS keywords that FitsChan stripped
std::set<std::string> namesChannelStripped;
std::set_difference(initialNames.begin(), initialNames.end(), finalNames.begin(), finalNames.end(),
std::inserter(namesChannelStripped, namesChannelStripped.begin()));

// FITS keywords that FitsChan may strip that we want to keep in `metadata`
std::set<std::string> const namesToKeep = {"DATE-OBS", "MJD-OBS"};

std::set<std::string> namesToStrip; // names to strip from metadata
std::set_difference(namesChannelStripped.begin(), namesChannelStripped.end(), namesToKeep.begin(),
namesToKeep.end(), std::inserter(namesToStrip, namesToStrip.begin()));
for (auto const& name : namesToStrip) {
metadata.remove(name);
}
}

std::shared_ptr<ast::FrameDict> readLsstSkyWcs(daf::base::PropertySet& metadata, bool strip) {
// Record CRPIX in GRID coordinates
// so we can compute CRVAL after standardizing the SkyFrame to ICRS
Expand Down
4 changes: 4 additions & 0 deletions src/geom/wcsUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ std::shared_ptr<daf::base::PropertyList> makeTanSipMetadata(
return metadata;
}

void stripWcsMetadata(daf::base::PropertySet &metadata) {
detail::stripWcsMetadata(metadata);
}

} // namespace geom
} // namespace afw
} // namespace lsst
12 changes: 11 additions & 1 deletion tests/test_skyWcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
TransformPoint2ToPoint2, TransformPoint2ToSpherePoint, makeRadialTransform, \
SkyWcs, makeSkyWcs, makeCdMatrix, makeWcsPairTransform, \
makeFlippedWcs, makeModifiedWcs, makeTanSipWcs, \
getIntermediateWorldCoordsToSky, getPixelToIntermediateWorldCoords
getIntermediateWorldCoordsToSky, getPixelToIntermediateWorldCoords, \
stripWcsMetadata
from lsst.afw.geom import getCdMatrixFromMetadata, getSipMatrixFromMetadata, makeSimpleWcsMetadata
from lsst.afw.geom.testUtils import makeSipIwcToPixel, makeSipPixelToIwc
from lsst.afw.fits import makeLimitedFitsHeader
Expand Down Expand Up @@ -694,6 +695,15 @@ def testBasics(self):
makeSkyWcs(self.metadata, strip=True)
self.assertEqual(len(self.metadata.names(False)), 0)

def testBasicsStrip(self):
stripWcsMetadata(self.metadata)
self.assertEqual(len(self.metadata.names(False)), 0)
erykoff marked this conversation as resolved.
Show resolved Hide resolved
# The metadata should be unchanged if we attempt to strip it again
metadataCopy = self.metadata.deepCopy()
stripWcsMetadata(self.metadata)
for key in self.metadata.keys():
self.assertEqual(self.metadata[key], metadataCopy[key])

def testNormalizationFk5(self):
"""Test that readLsstSkyWcs correctly normalizes FK5 1975 to ICRS
"""
Expand Down