374 """Make an array of generic point data
376 The data will be suitable for spherical points
381 Number of points in the array
383 Number of axes in the point
387 np.array of floats with shape (nAxes, nPoints)
388 The values are as follows; if nAxes != 2:
389 The first point has values `[0, delta, 2*delta, ..., (nAxes-1)*delta]`
390 The Nth point has those values + N
391 if nAxes == 2 then the data is scaled so that the max value of axis 1
392 is a bit less than pi/2
396 oneAxis = np.arange(nPoints, dtype=float)
398 rawData = np.array([j * delta + oneAxis
for j
in range(nAxes)], dtype=float)
402 maxLatitude = np.max(rawData[1])
403 rawData *= math.pi * 0.4999 / maxLatitude
509 The FrameSet will contain 4 frames and three transforms connecting them.
510 The idenity of each frame is provided by self.frameIdentDict
512 Frame Index Mapping from this frame to the next
513 `baseFrame` 1 `ast.UnitMap(nIn)`
514 Frame(nIn) 2 `polyMap`
515 Frame(nOut) 3 `ast.UnitMap(nOut)`
519 - `nIn` = `baseFrame.nAxes`
520 - `nOut` = `currFrame.nAxes`
521 - `polyMap` = `makeTwoWayPolyMap(nIn, nOut)`
526 The FrameSet as described above
530 baseFrame : `ast.Frame`
532 currFrame : `ast.Frame`
535 nIn = baseFrame.nAxes
536 nOut = currFrame.nAxes
537 polyMap = makeTwoWayPolyMap(nIn, nOut)
541 baseFrame = baseFrame.copy()
543 currFrame = currFrame.copy()
549 frameSet.addFrame(ast.FrameSet.CURRENT,
ast.UnitMap(nIn), frame2)
552 frameSet.addFrame(ast.FrameSet.CURRENT, polyMap, frame3)
553 frameSet.addFrame(ast.FrameSet.CURRENT,
ast.UnitMap(nOut), currFrame)
580 """Make a Jacobian matrix for the equation described by
586 the dimensions of the input and output data; see makeTwoWayPolyMap
587 inPoint : `numpy.ndarray`
588 an array of size `nIn` representing the point at which the Jacobian
594 an `nOut` x `nIn` array of first derivatives
596 basePolyMapCoeff = 0.001
597 baseCoeff = 2.0 * basePolyMapCoeff
598 coeffs = np.empty((nOut, nIn))
599 for iOut
in range(nOut):
600 coeffOffset = baseCoeff * iOut
601 for iIn
in range(nIn):
602 coeffs[iOut, iIn] = baseCoeff * (iIn + 1) + coeffOffset
603 coeffs[iOut, iIn] *= inPoint[iIn]
604 assert coeffs.ndim == 2
606 assert coeffs.shape == (nOut, nIn)
610 """Check applyForward and applyInverse for a transform
614 transform : `lsst.afw.geom.Transform`
615 The transform to check
616 mapping : `ast.Mapping`
617 The mapping the transform should use. This mapping
618 must contain valid forward or inverse transformations,
619 but they need not match if both present. Hence the
620 mappings returned by make*PolyMap are acceptable.
622 Error message suffix describing test parameters
624 fromEndpoint = transform.fromEndpoint
625 toEndpoint = transform.toEndpoint
626 mappingFromTransform = transform.getMapping()
630 self.assertEqual(nIn, fromEndpoint.nAxes, msg=msg)
631 self.assertEqual(nOut, toEndpoint.nAxes, msg=msg)
635 inPoint = fromEndpoint.pointFromData(rawInPoint)
640 inArray = fromEndpoint.arrayFromData(rawInArray)
642 if mapping.hasForward:
643 self.assertTrue(transform.hasForward)
644 outPoint = transform.applyForward(inPoint)
645 rawOutPoint = toEndpoint.dataFromPoint(outPoint)
646 assert_allclose(rawOutPoint, mapping.applyForward(rawInPoint), err_msg=msg)
647 assert_allclose(rawOutPoint, mappingFromTransform.applyForward(rawInPoint), err_msg=msg)
649 outArray = transform.applyForward(inArray)
650 rawOutArray = toEndpoint.dataFromArray(outArray)
651 self.assertFloatsAlmostEqual(rawOutArray, mapping.applyForward(rawInArray), msg=msg)
652 self.assertFloatsAlmostEqual(rawOutArray, mappingFromTransform.applyForward(rawInArray), msg=msg)
656 outPoint = toEndpoint.pointFromData(rawOutPoint)
658 outArray = toEndpoint.arrayFromData(rawOutArray)
660 self.assertFalse(transform.hasForward)
662 if mapping.hasInverse:
663 self.assertTrue(transform.hasInverse)
667 inversePoint = transform.applyInverse(outPoint)
668 rawInversePoint = fromEndpoint.dataFromPoint(inversePoint)
669 assert_allclose(rawInversePoint, mapping.applyInverse(rawOutPoint), err_msg=msg)
670 assert_allclose(rawInversePoint, mappingFromTransform.applyInverse(rawOutPoint), err_msg=msg)
675 inverseArray = transform.applyInverse(outArray)
676 rawInverseArray = fromEndpoint.dataFromArray(inverseArray)
677 self.assertFloatsAlmostEqual(rawInverseArray, mapping.applyInverse(rawOutArray), msg=msg)
678 self.assertFloatsAlmostEqual(rawInverseArray, mappingFromTransform.applyInverse(rawOutArray),
681 self.assertFalse(transform.hasInverse)
684 """Check that two Transforms are each others' inverses.
688 forward : `lsst.afw.geom.Transform`
689 the reference Transform to test
690 inverse : `lsst.afw.geom.Transform`
691 the transform that should be the inverse of `forward`
693 error message suffix describing test parameters
695 fromEndpoint = forward.fromEndpoint
696 toEndpoint = forward.toEndpoint
697 forwardMapping = forward.getMapping()
698 inverseMapping = inverse.getMapping()
701 self.assertEqual(forward.fromEndpoint,
702 inverse.toEndpoint, msg=msg)
703 self.assertEqual(forward.toEndpoint,
704 inverse.fromEndpoint, msg=msg)
705 self.assertEqual(forward.hasForward, inverse.hasInverse, msg=msg)
706 self.assertEqual(forward.hasInverse, inverse.hasForward, msg=msg)
712 inPoint = fromEndpoint.pointFromData(rawInPoint)
714 outPoint = toEndpoint.pointFromData(rawOutPoint)
719 inArray = fromEndpoint.arrayFromData(rawInArray)
721 outArray = toEndpoint.arrayFromData(rawOutArray)
723 if forward.hasForward:
724 self.assertEqual(forward.applyForward(inPoint),
725 inverse.applyInverse(inPoint), msg=msg)
726 self.assertEqual(forwardMapping.applyForward(rawInPoint),
727 inverseMapping.applyInverse(rawInPoint), msg=msg)
729 assert_array_equal(forward.applyForward(inArray),
730 inverse.applyInverse(inArray),
732 assert_array_equal(forwardMapping.applyForward(rawInArray),
733 inverseMapping.applyInverse(rawInArray),
736 if forward.hasInverse:
737 self.assertEqual(forward.applyInverse(outPoint),
738 inverse.applyForward(outPoint), msg=msg)
739 self.assertEqual(forwardMapping.applyInverse(rawOutPoint),
740 inverseMapping.applyForward(rawOutPoint), msg=msg)
741 assert_array_equal(forward.applyInverse(outArray),
742 inverse.applyForward(outArray),
744 assert_array_equal(forwardMapping.applyInverse(rawOutArray),
745 inverseMapping.applyForward(rawOutArray),
749 """Check Transform_<fromName>_<toName> using the Mapping constructor
753 fromName, toName : `str`
754 Endpoint name prefix for "from" and "to" endpoints, respectively,
755 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
756 fromAxes, toAxes : `int`
757 number of axes in fromFrame and toFrame, respectively
759 transformClassName =
"Transform{}To{}".format(fromName, toName)
760 TransformClass = getattr(afwGeom, transformClassName)
761 baseMsg =
"TransformClass={}".format(TransformClass.__name__)
764 for nIn, nOut
in itertools.product(self.
goodNAxes[fromName],
766 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
767 polyMap = makeTwoWayPolyMap(nIn, nOut)
768 transform = TransformClass(polyMap)
771 desStr =
"{}[{}->{}]".format(transformClassName, nIn, nOut)
772 self.assertEqual(
"{}".format(transform), desStr)
773 self.assertEqual(repr(transform),
"lsst.afw.geom." + desStr)
778 polyMap = makeForwardPolyMap(nIn, nOut)
779 transform = TransformClass(polyMap)
783 polyMap = makeForwardPolyMap(nOut, nIn).inverted()
784 transform = TransformClass(polyMap)
788 for nIn, badNOut
in itertools.product(self.
goodNAxes[fromName],
790 badPolyMap = makeTwoWayPolyMap(nIn, badNOut)
791 msg =
"{}, nIn={}, badNOut={}".format(baseMsg, nIn, badNOut)
792 with self.assertRaises(InvalidParameterError, msg=msg):
793 TransformClass(badPolyMap)
796 for badNIn, nOut
in itertools.product(self.
badNAxes[fromName],
798 badPolyMap = makeTwoWayPolyMap(badNIn, nOut)
799 msg =
"{}, badNIn={}, nOut={}".format(baseMsg, nIn, nOut)
800 with self.assertRaises(InvalidParameterError, msg=msg):
801 TransformClass(badPolyMap)
804 """Check Transform_<fromName>_<toName> using the FrameSet constructor
808 fromName, toName : `str`
809 Endpoint name prefix for "from" and "to" endpoints, respectively,
810 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
812 transformClassName =
"Transform{}To{}".format(fromName, toName)
813 TransformClass = getattr(afwGeom, transformClassName)
814 baseMsg =
"TransformClass={}".format(TransformClass.__name__)
815 for nIn, nOut
in itertools.product(self.
goodNAxes[fromName],
817 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
822 self.assertEqual(frameSet.nFrame, 4)
826 badFrameSet = self.
makeFrameSet(badBaseFrame, currFrame)
827 with self.assertRaises(InvalidParameterError):
828 TransformClass(badFrameSet)
830 reallyBadFrameSet = self.
makeFrameSet(badBaseFrame, badCurrFrame)
831 with self.assertRaises(InvalidParameterError):
832 TransformClass(reallyBadFrameSet)
834 badFrameSet = self.
makeFrameSet(baseFrame, badCurrFrame)
835 with self.assertRaises(InvalidParameterError):
836 TransformClass(badFrameSet)
838 transform = TransformClass(frameSet)
840 desStr =
"{}[{}->{}]".format(transformClassName, nIn, nOut)
841 self.assertEqual(
"{}".format(transform), desStr)
842 self.assertEqual(repr(transform),
"lsst.afw.geom." + desStr)
846 mappingFromTransform = transform.getMapping()
847 transformCopy = TransformClass(mappingFromTransform)
848 self.assertEqual(type(transform), type(transformCopy))
849 self.assertEqual(transform.getMapping(), mappingFromTransform)
851 polyMap = makeTwoWayPolyMap(nIn, nOut)
861 if permutedFS.isBaseSkyFrame:
862 baseFrame = permutedFS.frameSet.getFrame(ast.FrameSet.BASE)
864 desBaseLonAxis = 2
if permutedFS.isBasePermuted
else 1
865 self.assertEqual(baseFrame.lonAxis, desBaseLonAxis)
866 if permutedFS.isCurrSkyFrame:
867 currFrame = permutedFS.frameSet.getFrame(ast.FrameSet.CURRENT)
869 desCurrLonAxis = 2
if permutedFS.isCurrPermuted
else 1
870 self.assertEqual(currFrame.lonAxis, desCurrLonAxis)
872 permTransform = TransformClass(permutedFS.frameSet)
876 """Test Transform<fromName>To<toName>.inverted
880 fromName, toName : `str`
881 Endpoint name prefix for "from" and "to" endpoints, respectively,
882 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
884 transformClassName =
"Transform{}To{}".format(fromName, toName)
885 TransformClass = getattr(afwGeom, transformClassName)
886 baseMsg =
"TransformClass={}".format(TransformClass.__name__)
887 for nIn, nOut
in itertools.product(self.
goodNAxes[fromName],
889 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
892 makeTwoWayPolyMap(nIn, nOut),
893 "{}, Map={}".format(msg,
"TwoWay"))
896 makeForwardPolyMap(nIn, nOut),
897 "{}, Map={}".format(msg,
"Forward"))
900 makeForwardPolyMap(nOut, nIn).inverted(),
901 "{}, Map={}".format(msg,
"Inverse"))
904 """Test Transform<fromName>To<toName>.inverted for a specific
907 Also check that inverted() and getInverted() return the same
912 TransformClass : `type`
913 The class of transform to test, such as TransformPoint2ToPoint2
914 mapping : `ast.Mapping`
915 The mapping to use for the transform
919 transform = TransformClass(mapping)
920 inverse = transform.inverted()
921 inverseInverse = inverse.inverted()
928 """Test Transform<fromName>To<toName>.getJacobian
932 fromName, toName : `str`
933 Endpoint name prefix for "from" and "to" endpoints, respectively,
934 e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
936 transformClassName =
"Transform{}To{}".format(fromName, toName)
937 TransformClass = getattr(afwGeom, transformClassName)
938 baseMsg =
"TransformClass={}".format(TransformClass.__name__)
939 for nIn, nOut
in itertools.product(self.
goodNAxes[fromName],
941 msg =
"{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
942 polyMap = makeForwardPolyMap(nIn, nOut)
943 transform = TransformClass(polyMap)
944 fromEndpoint = transform.fromEndpoint
948 inPoint = fromEndpoint.pointFromData(rawInPoint)
949 jacobian = transform.getJacobian(inPoint)
950 assert_allclose(jacobian, self.
makeJacobian(nIn, nOut, rawInPoint),
954 inPoint = fromEndpoint.pointFromData(rawInPoint)
955 jacobian = transform.getJacobian(inPoint)
956 assert_allclose(jacobian, self.
makeJacobian(nIn, nOut, rawInPoint),
960 """Test Transform<fromName>To<midName>.then(Transform<midName>To<toName>)
965 the prefix of the starting endpoint (e.g., "Point2" for a
966 Point2Endpoint) for the final, concatenated Transform
968 the prefix for the shared endpoint where two Transforms will be
971 the prefix of the ending endpoint for the final, concatenated
974 TransformClass1 = getattr(afwGeom,
975 "Transform{}To{}".format(fromName, midName))
976 TransformClass2 = getattr(afwGeom,
977 "Transform{}To{}".format(midName, toName))
978 baseMsg =
"{}.then({})".format(TransformClass1.__name__,
979 TransformClass2.__name__)
980 for nIn, nMid, nOut
in itertools.product(self.
goodNAxes[fromName],
983 msg =
"{}, nIn={}, nMid={}, nOut={}".format(
984 baseMsg, nIn, nMid, nOut)
985 polyMap1 = makeTwoWayPolyMap(nIn, nMid)
986 transform1 = TransformClass1(polyMap1)
987 polyMap2 = makeTwoWayPolyMap(nMid, nOut)
988 transform2 = TransformClass2(polyMap2)
989 transform = transform1.then(transform2)
991 fromEndpoint = transform1.fromEndpoint
992 toEndpoint = transform2.toEndpoint
995 outPointMerged = transform.applyForward(inPoint)
996 outPointSeparate = transform2.applyForward(
997 transform1.applyForward(inPoint))
998 assert_allclose(toEndpoint.dataFromPoint(outPointMerged),
999 toEndpoint.dataFromPoint(outPointSeparate),
1003 inPointMerged = transform.applyInverse(outPoint)
1004 inPointSeparate = transform1.applyInverse(
1005 transform2.applyInverse(outPoint))
1007 fromEndpoint.dataFromPoint(inPointMerged),
1008 fromEndpoint.dataFromPoint(inPointSeparate),
1012 if midName ==
"Generic":
1015 polyMap = makeTwoWayPolyMap(nIn, 3)
1016 transform1 = TransformClass1(polyMap)
1017 polyMap = makeTwoWayPolyMap(2, nOut)
1018 transform2 = TransformClass2(polyMap)
1019 with self.assertRaises(InvalidParameterError):
1020 transform = transform1.then(transform2)
1023 if fromName != midName:
1026 joinNAxes = set(self.
goodNAxes[fromName]).intersection(
1029 for nIn, nMid, nOut
in itertools.product(self.
goodNAxes[fromName],
1032 polyMap = makeTwoWayPolyMap(nIn, nMid)
1033 transform1 = TransformClass1(polyMap)
1034 polyMap = makeTwoWayPolyMap(nMid, nOut)
1035 transform2 = TransformClass1(polyMap)
1036 with self.assertRaises(InvalidParameterError):
1037 transform = transform1.then(transform2)
1040 """Assert that two transforms are equal"""
1041 self.assertEqual(type(transform1), type(transform2))
1042 self.assertEqual(transform1.fromEndpoint, transform2.fromEndpoint)
1043 self.assertEqual(transform1.toEndpoint, transform2.toEndpoint)
1044 self.assertEqual(transform1.getMapping(), transform2.getMapping())
1046 fromEndpoint = transform1.fromEndpoint
1047 toEndpoint = transform1.toEndpoint
1048 mapping = transform1.getMapping()
1052 if mapping.hasForward:
1055 inArray = fromEndpoint.arrayFromData(rawInArray)
1056 outArray = transform1.applyForward(inArray)
1057 outData = toEndpoint.dataFromArray(outArray)
1058 outArrayRoundTrip = transform2.applyForward(inArray)
1059 outDataRoundTrip = toEndpoint.dataFromArray(outArrayRoundTrip)
1060 assert_allclose(outData, outDataRoundTrip)
1062 if mapping.hasInverse:
1065 outArray = toEndpoint.arrayFromData(rawOutArray)
1066 inArray = transform1.applyInverse(outArray)
1067 inData = fromEndpoint.dataFromArray(inArray)
1068 inArrayRoundTrip = transform2.applyInverse(outArray)
1069 inDataRoundTrip = fromEndpoint.dataFromArray(inArrayRoundTrip)
1070 assert_allclose(inData, inDataRoundTrip)
1073 """Check persistence of a transform
1075 className = type(transform).__name__
1078 transformStr = transform.writeString()
1079 serialVersion, serialClassName, serialRest = transformStr.split(
" ", 2)
1080 self.assertEqual(int(serialVersion), 1)
1081 self.assertEqual(serialClassName, className)
1082 badStr1 =
" ".join([
"2", serialClassName, serialRest])
1084 transform.readString(badStr1)
1085 badClassName =
"x" + serialClassName
1086 badStr2 =
" ".join([
"1", badClassName, serialRest])
1088 transform.readString(badStr2)
1089 transformFromStr1 = transform.readString(transformStr)
1093 transformFromStr2 = afwGeom.transformFromString(transformStr)
1100 with lsst.utils.tests.getTempFilePath(
".fits")
as filename:
1101 transform.writeFits(filename)