22 __all__ = [
"BoxGrid",
"makeSipIwcToPixel",
"makeSipPixelToIwc"]
31 from numpy.testing
import assert_allclose, assert_array_equal
32 from astshim.test import makeForwardPolyMap, makeTwoWayPolyMap
33 from ._geom
import getCdMatrixFromMetadata
43 """Divide a box into nx by ny sub-boxes that tile the region
45 The sub-boxes will be of the same type as `box` and will exactly tile `box`;
46 they will also all be the same size, to the extent possible (some variation
47 is inevitable for integer boxes that cannot be evenly divided.
51 box : `lsst.geom.Box2I` or `lsst.geom.Box2D`
52 the box to subdivide; the boxes in the grid will be of the same type
53 numColRow : pair of `int`
54 number of columns and rows
58 if len(numColRow) != 2:
59 raise RuntimeError(f
"numColRow={numColRow!r}; must be a sequence of two integers")
60 self.
_numColRow_numColRow = tuple(int(val)
for val
in numColRow)
67 raise RuntimeError(f
"Unknown class {type(box)} of box {box}")
71 minPoint = box.getMin()
73 dtype = np.array(minPoint).dtype
75 self.
_divList_divList = [np.linspace(start=box.getMin()[i],
76 stop=box.getMax()[i] + self.
stopDeltastopDelta,
79 dtype=dtype)
for i
in range(2)]
86 """Return the box at the specified x,y index
90 indXY : pair of `ints`
91 the x,y index to return
95 subBox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
97 beg = self.
pointClasspointClass(*[self.
_divList_divList[i][indXY[i]]
for i
in range(2)])
99 *[self.
_divList_divList[i][indXY[i] + 1] - self.
stopDeltastopDelta
for i
in range(2)])
100 return self.
boxClassboxClass(beg, end)
103 return self.shape[0]*self.shape[1]
106 """Return an iterator over all boxes, where column varies most quickly
108 for row
in range(self.
numColRownumColRow[1]):
109 for col
in range(self.
numColRownumColRow[0]):
114 """Information about a FrameSet
118 frameSet : `ast.FrameSet`
119 The FrameSet about which you want information
128 Index of current frame
129 isBaseSkyFrame : `bool`
130 Is the base frame an `ast.SkyFrame`?
131 isCurrSkyFrame : `bool`
132 Is the current frame an `ast.SkyFrame`?
142 """Return a list of ast.PolyMap coefficients for the specified SIP matrix
144 The returned list of coefficients for an ast.PolyMap
145 that computes the following function:
147 f(dxy) = dxy + sipPolynomial(dxy))
148 where dxy = pixelPosition - pixelOrigin
149 and sipPolynomial is a polynomial with terms `<name>n_m for x^n y^m`
150 (e.g. A2_0 is the coefficient for x^2 y^0)
154 metadata : lsst.daf.base.PropertySet
155 FITS metadata describing a WCS with the specified SIP coefficients
157 The desired SIP terms: one of A, B, AP, BP
162 A list of coefficients for an ast.PolyMap that computes
163 the specified SIP polynomial, including a term for out = in
167 This is an internal function for use by makeSipIwcToPixel
168 and makeSipPixelToIwc
170 outAxisDict = dict(A=1, B=2, AP=1, BP=2)
171 outAxis = outAxisDict.get(name)
173 raise RuntimeError(f
"{name} not a supported SIP name")
174 width = metadata.getAsInt(f
"{name}_ORDER") + 1
179 coeffs.append([1.0, outAxis, 1, 0])
181 coeffs.append([1.0, outAxis, 0, 1])
183 for xPower
in range(width):
184 for yPower
in range(width):
185 coeffName = f
"{name}_{xPower}_{yPower}"
186 if not metadata.exists(coeffName):
189 coeff = metadata.getAsDouble(coeffName)
190 coeffs.append([coeff, outAxis, xPower, yPower])
192 raise RuntimeError(f
"No {name} coefficients found")
197 """Make an IWC to pixel transform with SIP distortion from FITS-WCS metadata
199 This function is primarily intended for unit tests.
200 IWC is intermediate world coordinates, as described in the FITS papers.
204 metadata : lsst.daf.base.PropertySet
205 FITS metadata describing a WCS with inverse SIP coefficients
209 lsst.afw.geom.TransformPoint2ToPoint2
210 Transform from IWC position to pixel position (zero-based)
211 in the forward direction. The inverse direction is not defined.
216 The inverse SIP terms APn_m, BPn_m are polynomial coefficients x^n y^m
217 for computing transformed x, y respectively. If we call the resulting
218 polynomial inverseSipPolynomial, the returned transformation is:
220 pixelPosition = pixel origin + uv + inverseSipPolynomial(uv)
221 where uv = inverseCdMatrix * iwcPosition
223 crpix = (metadata.getScalar(
"CRPIX1") - 1, metadata.getScalar(
"CRPIX2") - 1)
228 coeffArr = np.array(coeffList, dtype=float)
229 sipPolyMap =
ast.PolyMap(coeffArr, 2,
"IterInverse=0")
231 iwcToPixelMap = cdMatrixMap.inverted().
then(sipPolyMap).
then(pixelRelativeToAbsoluteMap)
236 """Make a pixel to IWC transform with SIP distortion from FITS-WCS metadata
238 This function is primarily intended for unit tests.
239 IWC is intermediate world coordinates, as described in the FITS papers.
243 metadata : lsst.daf.base.PropertySet
244 FITS metadata describing a WCS with forward SIP coefficients
248 lsst.afw.geom.TransformPoint2ToPoint2
249 Transform from pixel position (zero-based) to IWC position
250 in the forward direction. The inverse direction is not defined.
255 The forward SIP terms An_m, Bn_m are polynomial coefficients x^n y^m
256 for computing transformed x, y respectively. If we call the resulting
257 polynomial sipPolynomial, the returned transformation is:
259 iwcPosition = cdMatrix * (dxy + sipPolynomial(dxy))
260 where dxy = pixelPosition - pixelOrigin
262 crpix = (metadata.getScalar(
"CRPIX1") - 1, metadata.getScalar(
"CRPIX2") - 1)
263 pixelAbsoluteToRelativeMap =
ast.ShiftMap(crpix).inverted()
267 coeffArr = np.array(coeffList, dtype=float)
268 sipPolyMap =
ast.PolyMap(coeffArr, 2,
"IterInverse=0")
269 pixelToIwcMap = pixelAbsoluteToRelativeMap.then(sipPolyMap).
then(cdMatrixMap)
274 """A FrameSet with base or current frame possibly permuted, with associated
277 Only two-axis frames will be permuted.
281 frameSet : `ast.FrameSet`
282 The FrameSet you wish to permute. A deep copy is made.
284 Permute the base frame's axes?
286 Permute the current frame's axes?
291 If you try to permute a frame that does not have 2 axes
297 frameSet : `ast.FrameSet`
298 The FrameSet that may be permuted. A local copy is made.
299 isBaseSkyFrame : `bool`
300 Is the base frame an `ast.SkyFrame`?
301 isCurrSkyFrame : `bool`
302 Is the current frame an `ast.SkyFrame`?
303 isBasePermuted : `bool`
304 Are the base frame axes permuted?
305 isCurrPermuted : `bool`
306 Are the current frame axes permuted?
308 def __init__(self, frameSet, permuteBase, permuteCurr):
316 raise RuntimeError(
"Base frame has {} axes; 2 required to permute".
format(baseNAxes))
317 self.
frameSetframeSet.current = fsInfo.baseInd
318 self.
frameSetframeSet.permAxes([2, 1])
319 self.
frameSetframeSet.current = fsInfo.currInd
323 raise RuntimeError(
"Current frame has {} axes; 2 required to permute".
format(currNAxes))
325 self.
frameSetframeSet.permAxes([2, 1])
331 """Base class for unit tests of Transform<X>To<Y>
333 Subclasses must call `TransformTestBaseClass.setUp(self)`
334 if they provide their own version.
336 If a package other than afw uses this class then it must
337 override the `getTestDir` method to avoid writing into
338 afw's test directory.
342 """Return a directory where temporary test files can be written
344 The default implementation returns the test directory of the `afw`
347 If this class is used by a test in a package other than `afw`
348 then the subclass must override this method.
350 return os.path.join(lsst.utils.getPackageDir(
"afw"),
"tests")
355 Subclasses should call this method if they override setUp.
367 "Generic": (1, 2, 3, 4),
377 "SpherePoint": (1, 3, 4),
390 """Make an array of generic point data
392 The data will be suitable for spherical points
397 Number of points in the array
399 Number of axes in the point
403 np.array of floats with shape (nAxes, nPoints)
404 The values are as follows; if nAxes != 2:
405 The first point has values `[0, delta, 2*delta, ..., (nAxes-1)*delta]`
406 The Nth point has those values + N
407 if nAxes == 2 then the data is scaled so that the max value of axis 1
408 is a bit less than pi/2
412 oneAxis = np.arange(nPoints, dtype=float)
414 rawData = np.array([j * delta + oneAxis
for j
in range(nAxes)], dtype=float)
418 maxLatitude = np.max(rawData[1])
419 rawData *= math.pi * 0.4999 / maxLatitude
424 """Make one generic point
429 Number of axes in the point
431 Increment between axis values
435 A list of `nAxes` floats with values `[0, delta, ..., (nAxes-1)*delta]
437 return [i*delta
for i
in range(nAxes)]
446 Endpoint class name prefix; the full class name is name + "Endpoint"
447 nAxes : `int` or `None`, optional
448 number of axes; an int is required if `name` == "Generic";
453 subclass of `lsst.afw.geom.BaseEndpoint`
454 The constructed endpoint
459 If `name` == "Generic" and `nAxes` is None or <= 0
461 EndpointClassName = name +
"Endpoint"
462 EndpointClass = getattr(afwGeom, EndpointClassName)
463 if name ==
"Generic":
465 raise TypeError(
"nAxes must be an integer for GenericEndpoint")
466 return EndpointClass(nAxes)
467 return EndpointClass()
471 """Return the appropriate frame for the given name and nAxes
476 Endpoint class name prefix; the full class name is name + "Endpoint"
477 nAxes : `int` or `None`, optional
478 number of axes; an int is required if `name` == "Generic";
484 The constructed frame
489 If `name` == "Generic" and `nAxes` is `None` or <= 0
491 return cls.
makeEndpointmakeEndpoint(name, nAxes).makeFrame()
495 """Return a list of 0 or more frames that are not a valid match for the
501 Endpoint class name prefix; the full class name is name + "Endpoint"
505 Collection of `ast.Frame`
506 A collection of 0 or more frames
525 The FrameSet will contain 4 frames and three transforms connecting them.
526 The idenity of each frame is provided by self.frameIdentDict
528 Frame Index Mapping from this frame to the next
529 `baseFrame` 1 `ast.UnitMap(nIn)`
530 Frame(nIn) 2 `polyMap`
531 Frame(nOut) 3 `ast.UnitMap(nOut)`
535 - `nIn` = `baseFrame.nAxes`
536 - `nOut` = `currFrame.nAxes`
537 - `polyMap` = `makeTwoWayPolyMap(nIn, nOut)`
542 The FrameSet as described above
546 baseFrame : `ast.Frame`
548 currFrame : `ast.Frame`
551 nIn = baseFrame.nAxes
552 nOut = currFrame.nAxes
557 baseFrame = baseFrame.copy()
559 currFrame = currFrame.copy()
565 frameSet.addFrame(ast.FrameSet.CURRENT,
ast.UnitMap(nIn), frame2)
568 frameSet.addFrame(ast.FrameSet.CURRENT, polyMap, frame3)
569 frameSet.addFrame(ast.FrameSet.CURRENT,
ast.UnitMap(nOut), currFrame)
574 """Iterator over 0 or more frameSets with SkyFrames axes permuted
576 Only base and current SkyFrames are permuted. If neither the base nor
577 the current frame is a SkyFrame then no frames are returned.
581 iterator over `PermutedFrameSet`
585 if not (fsInfo.isBaseSkyFrame
or fsInfo.isCurrSkyFrame):
588 permuteBaseList = [
False,
True]
if fsInfo.isBaseSkyFrame
else [
False]
589 permuteCurrList = [
False,
True]
if fsInfo.isCurrSkyFrame
else [
False]
590 for permuteBase
in permuteBaseList:
591 for permuteCurr
in permuteCurrList:
596 """Make a Jacobian matrix for the equation described by
602 the dimensions of the input and output data; see makeTwoWayPolyMap
603 inPoint : `numpy.ndarray`
604 an array of size `nIn` representing the point at which the Jacobian
610 an `nOut` x `nIn` array of first derivatives
612 basePolyMapCoeff = 0.001
613 baseCoeff = 2.0 * basePolyMapCoeff
614 coeffs = np.empty((nOut, nIn))
615 for iOut
in range(nOut):
616 coeffOffset = baseCoeff * iOut
617 for iIn
in range(nIn):
618 coeffs[iOut, iIn] = baseCoeff * (iIn + 1) + coeffOffset
619 coeffs[iOut, iIn] *= inPoint[iIn]
620 assert coeffs.ndim == 2
622 assert coeffs.shape == (nOut, nIn)
626 """Check applyForward and applyInverse for a transform
630 transform : `lsst.afw.geom.Transform`
631 The transform to check
632 mapping : `ast.Mapping`
633 The mapping the transform should use. This mapping
634 must contain valid forward or inverse transformations,
635 but they need not match if both present. Hence the
636 mappings returned by make*PolyMap are acceptable.
638 Error message suffix describing test parameters
640 fromEndpoint = transform.fromEndpoint
641 toEndpoint = transform.toEndpoint
642 mappingFromTransform = transform.getMapping()
646 self.assertEqual(nIn, fromEndpoint.nAxes, msg=msg)
647 self.assertEqual(nOut, toEndpoint.nAxes, msg=msg)
651 inPoint = fromEndpoint.pointFromData(rawInPoint)
656 inArray = fromEndpoint.arrayFromData(rawInArray)
658 if mapping.hasForward:
659 self.assertTrue(transform.hasForward)
660 outPoint = transform.applyForward(inPoint)
661 rawOutPoint = toEndpoint.dataFromPoint(outPoint)
662 assert_allclose(rawOutPoint, mapping.applyForward(rawInPoint), err_msg=msg)
663 assert_allclose(rawOutPoint, mappingFromTransform.applyForward(rawInPoint), err_msg=msg)
665 outArray = transform.applyForward(inArray)
666 rawOutArray = toEndpoint.dataFromArray(outArray)
667 self.assertFloatsAlmostEqual(rawOutArray, mapping.applyForward(rawInArray), msg=msg)
668 self.assertFloatsAlmostEqual(rawOutArray, mappingFromTransform.applyForward(rawInArray), msg=msg)
672 outPoint = toEndpoint.pointFromData(rawOutPoint)
674 outArray = toEndpoint.arrayFromData(rawOutArray)
676 self.assertFalse(transform.hasForward)
678 if mapping.hasInverse:
679 self.assertTrue(transform.hasInverse)
683 inversePoint = transform.applyInverse(outPoint)
684 rawInversePoint = fromEndpoint.dataFromPoint(inversePoint)
685 assert_allclose(rawInversePoint, mapping.applyInverse(rawOutPoint), err_msg=msg)
686 assert_allclose(rawInversePoint, mappingFromTransform.applyInverse(rawOutPoint), err_msg=msg)
691 inverseArray = transform.applyInverse(outArray)
692 rawInverseArray = fromEndpoint.dataFromArray(inverseArray)
693 self.assertFloatsAlmostEqual(rawInverseArray, mapping.applyInverse(rawOutArray), msg=msg)
694 self.assertFloatsAlmostEqual(rawInverseArray, mappingFromTransform.applyInverse(rawOutArray),
697 self.assertFalse(transform.hasInverse)
700 """Check that two Transforms are each others' inverses.
704 forward : `lsst.afw.geom.Transform`
705 the reference Transform to test
706 inverse : `lsst.afw.geom.Transform`
707 the transform that should be the inverse of `forward`
709 error message suffix describing test parameters
711 fromEndpoint = forward.fromEndpoint
712 toEndpoint = forward.toEndpoint
713 forwardMapping = forward.getMapping()
714 inverseMapping = inverse.getMapping()
717 self.assertEqual(forward.fromEndpoint,
718 inverse.toEndpoint, msg=msg)
719 self.assertEqual(forward.toEndpoint,
720 inverse.fromEndpoint, msg=msg)
721 self.assertEqual(forward.hasForward, inverse.hasInverse, msg=msg)
722 self.assertEqual(forward.hasInverse, inverse.hasForward, msg=msg)
728 inPoint = fromEndpoint.pointFromData(rawInPoint)
730 outPoint = toEndpoint.pointFromData(rawOutPoint)
734 rawInArray = self.
makeRawArrayDatamakeRawArrayData(nPoints, fromEndpoint.nAxes)
735 inArray = fromEndpoint.arrayFromData(rawInArray)
736 rawOutArray = self.
makeRawArrayDatamakeRawArrayData(nPoints, toEndpoint.nAxes)
737 outArray = toEndpoint.arrayFromData(rawOutArray)
739 if forward.hasForward:
740 self.assertEqual(forward.applyForward(inPoint),
741 inverse.applyInverse(inPoint), msg=msg)
742 self.assertEqual(forwardMapping.applyForward(rawInPoint),
743 inverseMapping.applyInverse(rawInPoint), msg=msg)
745 assert_array_equal(forward.applyForward(inArray),
746 inverse.applyInverse(inArray),
748 assert_array_equal(forwardMapping.applyForward(rawInArray),
749 inverseMapping.applyInverse(rawInArray),
752 if forward.hasInverse:
753 self.assertEqual(forward.applyInverse(outPoint),
754 inverse.applyForward(outPoint), msg=msg)
755 self.assertEqual(forwardMapping.applyInverse(rawOutPoint),
756 inverseMapping.applyForward(rawOutPoint), msg=msg)
757 assert_array_equal(forward.applyInverse(outArray),
758 inverse.applyForward(outArray),
760 assert_array_equal(forwardMapping.applyInverse(rawOutArray),
761 inverseMapping.applyForward(rawOutArray),
765 """Check Transform_<fromName>_<toName> using the Mapping constructor
769 fromName, toName : `str`
770 Endpoint name prefix for "from" and "to" endpoints, respectively,
771 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
772 fromAxes, toAxes : `int`
773 number of axes in fromFrame and toFrame, respectively
775 transformClassName =
"Transform{}To{}".
format(fromName, toName)
776 TransformClass = getattr(afwGeom, transformClassName)
777 baseMsg =
"TransformClass={}".
format(TransformClass.__name__)
780 for nIn, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
782 msg =
"{}, nIn={}, nOut={}".
format(baseMsg, nIn, nOut)
787 desStr =
"{}[{}->{}]".
format(transformClassName, nIn, nOut)
788 self.assertEqual(
"{}".
format(transform), desStr)
789 self.assertEqual(repr(transform),
"lsst.afw.geom." + desStr)
804 for nIn, badNOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
807 msg =
"{}, nIn={}, badNOut={}".
format(baseMsg, nIn, badNOut)
808 with self.assertRaises(InvalidParameterError, msg=msg):
812 for badNIn, nOut
in itertools.product(self.
badNAxesbadNAxes[fromName],
815 msg =
"{}, badNIn={}, nOut={}".
format(baseMsg, nIn, nOut)
816 with self.assertRaises(InvalidParameterError, msg=msg):
820 """Check Transform_<fromName>_<toName> using the FrameSet constructor
824 fromName, toName : `str`
825 Endpoint name prefix for "from" and "to" endpoints, respectively,
826 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
828 transformClassName =
"Transform{}To{}".
format(fromName, toName)
829 TransformClass = getattr(afwGeom, transformClassName)
830 baseMsg =
"TransformClass={}".
format(TransformClass.__name__)
831 for nIn, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
833 msg =
"{}, nIn={}, nOut={}".
format(baseMsg, nIn, nOut)
837 frameSet = self.
makeFrameSetmakeFrameSet(baseFrame, currFrame)
838 self.assertEqual(frameSet.nFrame, 4)
841 for badBaseFrame
in self.
makeBadFramesmakeBadFrames(fromName):
842 badFrameSet = self.
makeFrameSetmakeFrameSet(badBaseFrame, currFrame)
843 with self.assertRaises(InvalidParameterError):
846 reallyBadFrameSet = self.
makeFrameSetmakeFrameSet(badBaseFrame, badCurrFrame)
847 with self.assertRaises(InvalidParameterError):
850 badFrameSet = self.
makeFrameSetmakeFrameSet(baseFrame, badCurrFrame)
851 with self.assertRaises(InvalidParameterError):
856 desStr =
"{}[{}->{}]".
format(transformClassName, nIn, nOut)
857 self.assertEqual(
"{}".
format(transform), desStr)
858 self.assertEqual(repr(transform),
"lsst.afw.geom." + desStr)
862 mappingFromTransform = transform.getMapping()
864 self.assertEqual(
type(transform),
type(transformCopy))
865 self.assertEqual(transform.getMapping(), mappingFromTransform)
877 if permutedFS.isBaseSkyFrame:
878 baseFrame = permutedFS.frameSet.getFrame(ast.FrameSet.BASE)
880 desBaseLonAxis = 2
if permutedFS.isBasePermuted
else 1
881 self.assertEqual(baseFrame.lonAxis, desBaseLonAxis)
882 if permutedFS.isCurrSkyFrame:
883 currFrame = permutedFS.frameSet.getFrame(ast.FrameSet.CURRENT)
885 desCurrLonAxis = 2
if permutedFS.isCurrPermuted
else 1
886 self.assertEqual(currFrame.lonAxis, desCurrLonAxis)
892 """Test Transform<fromName>To<toName>.inverted
896 fromName, toName : `str`
897 Endpoint name prefix for "from" and "to" endpoints, respectively,
898 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
900 transformClassName =
"Transform{}To{}".
format(fromName, toName)
901 TransformClass = getattr(afwGeom, transformClassName)
902 baseMsg =
"TransformClass={}".
format(TransformClass.__name__)
903 for nIn, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
905 msg =
"{}, nIn={}, nOut={}".
format(baseMsg, nIn, nOut)
909 "{}, Map={}".
format(msg,
"TwoWay"))
913 "{}, Map={}".
format(msg,
"Forward"))
917 "{}, Map={}".
format(msg,
"Inverse"))
920 """Test Transform<fromName>To<toName>.inverted for a specific
923 Also check that inverted() and getInverted() return the same
928 TransformClass : `type`
929 The class of transform to test, such as TransformPoint2ToPoint2
930 mapping : `ast.Mapping`
931 The mapping to use for the transform
936 inverse = transform.inverted()
937 inverseInverse = inverse.inverted()
944 """Test Transform<fromName>To<toName>.getJacobian
948 fromName, toName : `str`
949 Endpoint name prefix for "from" and "to" endpoints, respectively,
950 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
952 transformClassName =
"Transform{}To{}".
format(fromName, toName)
953 TransformClass = getattr(afwGeom, transformClassName)
954 baseMsg =
"TransformClass={}".
format(TransformClass.__name__)
955 for nIn, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
957 msg =
"{}, nIn={}, nOut={}".
format(baseMsg, nIn, nOut)
960 fromEndpoint = transform.fromEndpoint
964 inPoint = fromEndpoint.pointFromData(rawInPoint)
965 jacobian = transform.getJacobian(inPoint)
966 assert_allclose(jacobian, self.
makeJacobianmakeJacobian(nIn, nOut, rawInPoint),
970 inPoint = fromEndpoint.pointFromData(rawInPoint)
971 jacobian = transform.getJacobian(inPoint)
972 assert_allclose(jacobian, self.
makeJacobianmakeJacobian(nIn, nOut, rawInPoint),
976 """Test Transform<fromName>To<midName>.then(Transform<midName>To<toName>)
981 the prefix of the starting endpoint (e.g., "Point2" for a
982 Point2Endpoint) for the final, concatenated Transform
984 the prefix for the shared endpoint where two Transforms will be
987 the prefix of the ending endpoint for the final, concatenated
990 TransformClass1 = getattr(afwGeom,
991 "Transform{}To{}".
format(fromName, midName))
992 TransformClass2 = getattr(afwGeom,
993 "Transform{}To{}".
format(midName, toName))
994 baseMsg =
"{}.then({})".
format(TransformClass1.__name__,
995 TransformClass2.__name__)
996 for nIn, nMid, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
999 msg =
"{}, nIn={}, nMid={}, nOut={}".
format(
1000 baseMsg, nIn, nMid, nOut)
1002 transform1 = TransformClass1(polyMap1)
1004 transform2 = TransformClass2(polyMap2)
1005 transform = transform1.then(transform2)
1007 fromEndpoint = transform1.fromEndpoint
1008 toEndpoint = transform2.toEndpoint
1010 inPoint = fromEndpoint.pointFromData(self.
makeRawPointDatamakeRawPointData(nIn))
1011 outPointMerged = transform.applyForward(inPoint)
1012 outPointSeparate = transform2.applyForward(
1013 transform1.applyForward(inPoint))
1014 assert_allclose(toEndpoint.dataFromPoint(outPointMerged),
1015 toEndpoint.dataFromPoint(outPointSeparate),
1018 outPoint = toEndpoint.pointFromData(self.
makeRawPointDatamakeRawPointData(nOut))
1019 inPointMerged = transform.applyInverse(outPoint)
1020 inPointSeparate = transform1.applyInverse(
1021 transform2.applyInverse(outPoint))
1023 fromEndpoint.dataFromPoint(inPointMerged),
1024 fromEndpoint.dataFromPoint(inPointSeparate),
1028 if midName ==
"Generic":
1029 nIn = self.
goodNAxesgoodNAxes[fromName][0]
1030 nOut = self.
goodNAxesgoodNAxes[toName][0]
1032 transform1 = TransformClass1(polyMap)
1034 transform2 = TransformClass2(polyMap)
1035 with self.assertRaises(InvalidParameterError):
1036 transform = transform1.then(transform2)
1039 if fromName != midName:
1042 joinNAxes =
set(self.
goodNAxesgoodNAxes[fromName]).intersection(
1045 for nIn, nMid, nOut
in itertools.product(self.
goodNAxesgoodNAxes[fromName],
1049 transform1 = TransformClass1(polyMap)
1051 transform2 = TransformClass1(polyMap)
1052 with self.assertRaises(InvalidParameterError):
1053 transform = transform1.then(transform2)
1056 """Assert that two transforms are equal"""
1057 self.assertEqual(
type(transform1),
type(transform2))
1058 self.assertEqual(transform1.fromEndpoint, transform2.fromEndpoint)
1059 self.assertEqual(transform1.toEndpoint, transform2.toEndpoint)
1060 self.assertEqual(transform1.getMapping(), transform2.getMapping())
1062 fromEndpoint = transform1.fromEndpoint
1063 toEndpoint = transform1.toEndpoint
1064 mapping = transform1.getMapping()
1068 if mapping.hasForward:
1071 inArray = fromEndpoint.arrayFromData(rawInArray)
1072 outArray = transform1.applyForward(inArray)
1073 outData = toEndpoint.dataFromArray(outArray)
1074 outArrayRoundTrip = transform2.applyForward(inArray)
1075 outDataRoundTrip = toEndpoint.dataFromArray(outArrayRoundTrip)
1076 assert_allclose(outData, outDataRoundTrip)
1078 if mapping.hasInverse:
1081 outArray = toEndpoint.arrayFromData(rawOutArray)
1082 inArray = transform1.applyInverse(outArray)
1083 inData = fromEndpoint.dataFromArray(inArray)
1084 inArrayRoundTrip = transform2.applyInverse(outArray)
1085 inDataRoundTrip = fromEndpoint.dataFromArray(inArrayRoundTrip)
1086 assert_allclose(inData, inDataRoundTrip)
1089 """Check persistence of a transform
1091 className =
type(transform).__name__
1094 transformStr = transform.writeString()
1095 serialVersion, serialClassName, serialRest = transformStr.split(
" ", 2)
1096 self.assertEqual(int(serialVersion), 1)
1097 self.assertEqual(serialClassName, className)
1098 badStr1 =
" ".join([
"2", serialClassName, serialRest])
1100 transform.readString(badStr1)
1101 badClassName =
"x" + serialClassName
1102 badStr2 =
" ".join([
"1", badClassName, serialRest])
1104 transform.readString(badStr2)
1105 transformFromStr1 = transform.readString(transformStr)
1109 transformFromStr2 = afwGeom.transformFromString(transformStr)
1116 with lsst.utils.tests.getTempFilePath(
".fits")
as filename:
1117 transform.writeFits(filename)
Frame is used to represent a coordinate system.
A FrameSet consists of a set of one or more Frames (which describe coordinate systems),...
MatrixMap is a form of Mapping which performs a general linear transformation.
PolyMap is a Mapping which performs a general polynomial transformation.
ShiftMap is a linear Mapping which shifts each axis by a specified constant value.
SkyFrame is a specialised form of Frame which describes celestial longitude/latitude coordinate syste...
A UnitMap is a unit (null) Mapping that has no effect on the coordinates supplied to it.
def __init__(self, box, numColRow)
def __getitem__(self, indXY)
def __init__(self, frameSet)
def __init__(self, frameSet, permuteBase, permuteCurr)
A floating-point coordinate rectangle geometry.
An integer coordinate rectangle.
Reports invalid arguments.
daf::base::PropertySet * set
def makeForwardPolyMap(nIn, nOut)
def makeTwoWayPolyMap(nIn, nOut)
def makeSipPolyMapCoeffs(metadata, name)
def makeSipIwcToPixel(metadata)
def makeSipPixelToIwc(metadata)
Eigen::Matrix2d getCdMatrixFromMetadata(daf::base::PropertySet &metadata)
Read a CD matrix from FITS WCS metadata.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)