22"""Code to convert jointcal's output WCS models to distortion maps that can be
23used by afw CameraGeom.
31from lsst.geom import SpherePoint, Point2D, radians
37 """Convert a jointcal `~lsst.afw.geom.SkyWcs` into a distortion model and
40 Because this code only operates on the WCS, it is independent of the
41 format of the persisted output (e.g. gen2 separate files vs. gen3 bundled
47 The WCS to use to compute the distortion model
from, preferably
from
48 multiple visits on the same tract.
49 detectors : `list` [`int`]
50 Detector ids that correspond one-to-one
with ``wcsList``.
52 The camera these WCS were fit
for.
54 Number of points to compute the pixel scale at, along the +y axis.
56 def __init__(self, wcsList, detectors, camera, n=100):
74 self.
loglog = _LOG.getChild(
"CameraModel")
77 """Calculate the afw cameraGeom distortion model to be included in an
80 PLACEHOLDER: This may be as simple
as running `computePixelScale`
and
81 then doing a numpy polynomial fit to it
for the cameraGeom input.
82 However, we need to check details of how that distortion model
is
83 stored
in a Camera. e.g.:
86 raise NotImplementedError(
"not yet!")
89 """Compute the radial and tangential pixel scale by averaging over
90 multiple jointcal WCS models.
92 Also computes the standard deviation and logs any WCS that are
94 The calculations are stored
in the ``fieldAngle[s]``,
95 ``radialScale[s]``,
and ``tangentialScale[s]`` member variables.
111 self.
loglog.warning(
"Large stddev in computed field angles between visits (max: %s degree).",
117 self.
loglog.warning(
"Large stddev in computed radial scales between visits (max: %s arcsec).",
122 self.
loglog.warning(
"Large stddev in computed tangential scales between visits (max: %s arcsec).",
126 """Compute the radial and tangential pixel scales using the distortion
127 model supplied with the camera.
129 This
is designed to be directly comparable
with the results of
130 `~CameraModel.computePixelScale`.
135 Detector identifier
for the detector_id to use
for the calculation.
139 fieldAngle : `numpy.ndarray`
140 Field angles
in degrees.
141 radialScale : `numpy.ndarray`
142 Radial direction pixel scales
in arcseconds/pixel.
143 tangentialScale : `numpy.ndarray`
144 Tangential direction pixel scales
in arcseconds/pixel.
149 iwcToSkyMap = iwcToSkyWcs.getFrameDict().getMapping(
"PIXELS",
"SKY")
150 skyFrame = iwcToSkyWcs.getFrameDict().getFrame(
"SKY")
153 pixSys = self.
cameracamera[detector_id].makeCameraSys(cameraGeom.PIXELS)
154 pixelsToFocal = self.
cameracamera.getTransform(pixSys, cameraGeom.FOCAL_PLANE)
155 focalToField = self.
cameracamera.getTransform(cameraGeom.FOCAL_PLANE, cameraGeom.FIELD_ANGLE)
158 pixelFrame =
ast.Frame(2,
"Domain=PIXELS")
159 focalFrame =
ast.Frame(2,
"Domain=FOCAL")
162 frameDict.addFrame(
"PIXELS", pixelsToFocal.getMapping(), focalFrame)
163 frameDict.addFrame(
"FOCAL", focalToField.getMapping(), iwcFrame)
164 frameDict.addFrame(
"IWC", iwcToSkyMap, skyFrame)
169 def _computeDetectorPixelScale(self, detector_id, wcs):
170 """Compute pixel scale in radial and tangential directions as a
171 function of field angle.
176 Detector identifier for the detector of this wcs.
178 Full focal-plane model to compute pixel scale on.
182 fieldAngle : `numpy.ndarray`
183 Field angles
in degrees.
184 radialScale : `numpy.ndarray`
185 Radial direction pixel scales
in arcseconds/pixel.
186 tangentialScale : `numpy.ndarray`
187 Tangential direction pixel scales
in arcseconds/pixel.
191 Pixel scales are calculated
from finite differences only along the +y
192 focal plane direction.
194 focalToSky = wcs.getFrameDict().getMapping('FOCAL',
'SKY')
195 mmPerPixel = self.
cameracamera[detector_id].getPixelSize()
197 focalToPixels = wcs.getFrameDict().getMapping(
'FOCAL',
'PIXELS')
198 trans = wcs.getTransform()
199 boresight = trans.applyForward(Point2D(focalToPixels.applyForward([0, 0])))
202 fieldAngle = np.zeros_like(rs)
203 radialScale = np.zeros_like(rs)
204 tangentialScale = np.zeros_like(rs)
205 for i, r
in enumerate(rs):
207 sp1 =
SpherePoint(*focalToSky.applyForward(Point2D([0, r])), radians)
209 sp2 =
SpherePoint(*focalToSky.applyForward(Point2D([0, r + mmPerPixel.getY()])), radians)
211 sp3 =
SpherePoint(*focalToSky.applyForward(Point2D([mmPerPixel.getX(), r])), radians)
212 fieldAngle[i] = boresight.separation(sp1).asDegrees()
213 radialScale[i] = sp1.separation(sp2).asArcseconds()
214 tangentialScale[i] = sp1.separation(sp3).asArcseconds()
215 return fieldAngle, radialScale, tangentialScale
def computeCameraPixelScale(self, detector_id=30)
def computePixelScale(self)
def computeDistortionModel(self)
def __init__(self, wcsList, detectors, camera, n=100)
def _computeDetectorPixelScale(self, detector_id, wcs)
static Log getLogger(std::string const &loggername)
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const &scale, lsst::geom::Angle const &orientation=0 *lsst::geom::degrees, bool flipX=false)