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
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
553 polyMap = makeTwoWayPolyMap(nIn, nOut)
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)
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)
735 inArray = fromEndpoint.arrayFromData(rawInArray)
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.
goodNAxes[fromName],
782 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
783 polyMap = makeTwoWayPolyMap(nIn, nOut)
784 transform = TransformClass(polyMap)
787 desStr =
"{}[{}->{}]".format(transformClassName, nIn, nOut)
788 self.assertEqual(
"{}".format(transform), desStr)
789 self.assertEqual(repr(transform),
"lsst.afw.geom." + desStr)
794 polyMap = makeForwardPolyMap(nIn, nOut)
795 transform = TransformClass(polyMap)
799 polyMap = makeForwardPolyMap(nOut, nIn).inverted()
800 transform = TransformClass(polyMap)
804 for nIn, badNOut
in itertools.product(self.
goodNAxes[fromName],
806 badPolyMap = makeTwoWayPolyMap(nIn, badNOut)
807 msg =
"{}, nIn={}, badNOut={}".format(baseMsg, nIn, badNOut)
808 with self.assertRaises(InvalidParameterError, msg=msg):
809 TransformClass(badPolyMap)
812 for badNIn, nOut
in itertools.product(self.
badNAxes[fromName],
814 badPolyMap = makeTwoWayPolyMap(badNIn, nOut)
815 msg =
"{}, badNIn={}, nOut={}".format(baseMsg, nIn, nOut)
816 with self.assertRaises(InvalidParameterError, msg=msg):
817 TransformClass(badPolyMap)
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.
goodNAxes[fromName],
833 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
838 self.assertEqual(frameSet.nFrame, 4)
842 badFrameSet = self.
makeFrameSet(badBaseFrame, currFrame)
843 with self.assertRaises(InvalidParameterError):
844 TransformClass(badFrameSet)
846 reallyBadFrameSet = self.
makeFrameSet(badBaseFrame, badCurrFrame)
847 with self.assertRaises(InvalidParameterError):
848 TransformClass(reallyBadFrameSet)
850 badFrameSet = self.
makeFrameSet(baseFrame, badCurrFrame)
851 with self.assertRaises(InvalidParameterError):
852 TransformClass(badFrameSet)
854 transform = TransformClass(frameSet)
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()
863 transformCopy = TransformClass(mappingFromTransform)
864 self.assertEqual(
type(transform),
type(transformCopy))
865 self.assertEqual(transform.getMapping(), mappingFromTransform)
867 polyMap = makeTwoWayPolyMap(nIn, nOut)
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)
888 permTransform = TransformClass(permutedFS.frameSet)
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.
goodNAxes[fromName],
905 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
908 makeTwoWayPolyMap(nIn, nOut),
909 "{}, Map={}".format(msg,
"TwoWay"))
912 makeForwardPolyMap(nIn, nOut),
913 "{}, Map={}".format(msg,
"Forward"))
916 makeForwardPolyMap(nOut, nIn).inverted(),
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
935 transform = TransformClass(mapping)
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.
goodNAxes[fromName],
957 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
958 polyMap = makeForwardPolyMap(nIn, nOut)
959 transform = TransformClass(polyMap)
960 fromEndpoint = transform.fromEndpoint
964 inPoint = fromEndpoint.pointFromData(rawInPoint)
965 jacobian = transform.getJacobian(inPoint)
966 assert_allclose(jacobian, self.
makeJacobian(nIn, nOut, rawInPoint),
970 inPoint = fromEndpoint.pointFromData(rawInPoint)
971 jacobian = transform.getJacobian(inPoint)
972 assert_allclose(jacobian, self.
makeJacobian(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.
goodNAxes[fromName],
999 msg =
"{}, nIn={}, nMid={}, nOut={}".format(
1000 baseMsg, nIn, nMid, nOut)
1001 polyMap1 = makeTwoWayPolyMap(nIn, nMid)
1002 transform1 = TransformClass1(polyMap1)
1003 polyMap2 = makeTwoWayPolyMap(nMid, nOut)
1004 transform2 = TransformClass2(polyMap2)
1005 transform = transform1.then(transform2)
1007 fromEndpoint = transform1.fromEndpoint
1008 toEndpoint = transform2.toEndpoint
1011 outPointMerged = transform.applyForward(inPoint)
1012 outPointSeparate = transform2.applyForward(
1013 transform1.applyForward(inPoint))
1014 assert_allclose(toEndpoint.dataFromPoint(outPointMerged),
1015 toEndpoint.dataFromPoint(outPointSeparate),
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":
1031 polyMap = makeTwoWayPolyMap(nIn, 3)
1032 transform1 = TransformClass1(polyMap)
1033 polyMap = makeTwoWayPolyMap(2, nOut)
1034 transform2 = TransformClass2(polyMap)
1035 with self.assertRaises(InvalidParameterError):
1036 transform = transform1.then(transform2)
1039 if fromName != midName:
1042 joinNAxes =
set(self.
goodNAxes[fromName]).intersection(
1045 for nIn, nMid, nOut
in itertools.product(self.
goodNAxes[fromName],
1048 polyMap = makeTwoWayPolyMap(nIn, nMid)
1049 transform1 = TransformClass1(polyMap)
1050 polyMap = makeTwoWayPolyMap(nMid, nOut)
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)