Coverage for python/lsst/meas/astrom/display.py: 6%

85 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-30 10:37 +0000

1# This file is part of meas_astrom. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["displayAstrometry", "plotAstrometry"] 

23 

24import math 

25 

26import numpy as np 

27 

28import lsst.afw.display as afwDisplay 

29from lsst.afw.image import ExposureF 

30from lsst.afw.table import Point2DKey 

31 

32 

33def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, 

34 matches=None, frame=1, title="", pause=True): 

35 """Show an astrometry debug image. 

36 

37 Parameters 

38 ---------- 

39 refCat : `lsst.afw.table.SimpleCatalog` 

40 reference object catalog; must have fields "centroid_x" an 

41 "centroid_y" 

42 sourceCat : `lsst.afw.table.SourceCatalg` 

43 source catalog; must have field "slot_Centroid_x" and "slot_Centroid_y" 

44 distortedCentroidKey : `lsst.afw.table.Key` 

45 key for sourceCat with field to use for distorted positions 

46 exposure : `lsst.afw.image.Exposure` 

47 exposure to display 

48 bbox : `lsst.geom.Box2I` 

49 bounding box of exposure; Used if the exposure is `None` 

50 matches : `list` of `lsst.afw.table.ReferenceMatch` 

51 List of matched objects 

52 frame : `int` 

53 frame number for display 

54 title : `str` 

55 title for display 

56 pause : `bool` 

57 pause for inspection of display? This is done by dropping into pdb. 

58 

59 Notes 

60 ----- 

61 

62 - reference objects in refCat are shown as red X 

63 - sources in sourceCat are shown as green + 

64 - distorted sources in sourceCat (position given by distortedCentroidKey) 

65 are shown as green o 

66 - matches are shown as a yellow circle around the source and a yellow line 

67 connecting the reference object and source 

68 - if both exposure and bbox are `None`, no image is displayed 

69 

70 """ 

71 disp = afwDisplay.getDisplay(frame=frame) 

72 

73 if exposure is not None: 

74 disp.mtv(exposure, title=title) 

75 elif bbox is not None: 

76 disp.mtv(exposure=ExposureF(bbox), title=title) 

77 

78 with disp.Buffering(): 

79 if refCat is not None: 

80 refCentroidKey = Point2DKey(refCat.schema["centroid"]) 

81 for refObj in refCat: 

82 rx, ry = refObj.get(refCentroidKey) 

83 disp.dot("x", rx, ry, size=10, ctype=afwDisplay.RED) 

84 

85 if sourceCat is not None: 

86 sourceCentroidKey = Point2DKey(sourceCat.schema["slot_Centroid"]) 

87 for source in sourceCat: 

88 sx, sy = source.get(sourceCentroidKey) 

89 disp.dot("+", sx, sy, size=10, ctype=afwDisplay.GREEN) 

90 if distortedCentroidKey is not None: 

91 dx, dy = source.get(distortedCentroidKey) 

92 disp.dot("o", dx, dy, size=10, ctype=afwDisplay.GREEN) 

93 disp.line([(sx, sy), (dx, dy)], ctype=afwDisplay.GREEN) 

94 

95 if matches is not None: 

96 refCentroidKey = Point2DKey(matches[0].first.schema["centroid"]) 

97 sourceCentroidKey = Point2DKey(matches[0].second.schema["slot_Centroid"]) 

98 radArr = np.ndarray(len(matches)) 

99 

100 for i, m in enumerate(matches): 

101 refCentroid = m.first.get(refCentroidKey) 

102 sourceCentroid = m.second.get(sourceCentroidKey) 

103 radArr[i] = math.hypot(*(refCentroid - sourceCentroid)) 

104 sx, sy = sourceCentroid 

105 disp.dot("o", sx, sy, size=10, ctype=afwDisplay.YELLOW) 

106 disp.line([refCentroid, sourceCentroid], ctype=afwDisplay.YELLOW) 

107 

108 print("<match radius> = %.4g +- %.4g [%d matches]" % 

109 (radArr.mean(), radArr.std(), len(matches))) 

110 

111 if pause: 

112 print("Dropping into debugger to allow inspection of display. Type 'continue' when done.") 

113 import pdb 

114 pdb.set_trace() 

115 

116 

117def plotAstrometry( 

118 matches, 

119 refCat=None, 

120 sourceCat=None, 

121 refMarker="x", 

122 refColor="r", 

123 sourceMarker="+", 

124 sourceColor="g", 

125 matchColor="y" 

126): 

127 """Plot reference objects, sources and matches 

128 

129 Parameters 

130 ---------- 

131 matches : `list` of `lsst.afw.table.ReferenceMatch` 

132 list of matches 

133 refCat : `lsst.afw.table.SimpleCatalog` 

134 reference object catalog, or None to not plot reference objects 

135 sourceCat : `lsst.afw.table.SourceCatalog` 

136 source catalog, or None to not plot sources 

137 refMarker : `str` 

138 pyplot marker for reference objects 

139 refColor : `str` 

140 pyplot color for reference objects 

141 sourceMarker : `str` 

142 pyplot marker for sources 

143 sourceColor : `str` 

144 pyplot color for sources 

145 matchColor : `str` 

146 color for matches; can be a constant 

147 or a function taking one match and returning a string 

148 

149 Notes 

150 ----- 

151 By default: 

152 

153 - reference objects in refCat are shown as red X 

154 - sources in sourceCat are shown as green + 

155 - matches are shown as a yellow circle around the source and a line 

156 connecting the reference object to the source 

157 """ 

158 # delay importing plt to give users a chance to set the backend before calling this function 

159 import matplotlib.pyplot as plt 

160 refSchema = matches[0][0].schema 

161 refCentroidKey = Point2DKey(refSchema["centroid"]) 

162 srcSchema = matches[0][1].schema 

163 srcCentroidKey = Point2DKey(srcSchema["slot_Centroid"]) 

164 

165 if refCat is not None: 

166 refXArr, refYArr = list(zip(*[ref.get(refCentroidKey) for ref in refCat])) 

167 plt.plot(refXArr, refYArr, marker=refMarker, color=refColor, linestyle="") 

168 

169 if sourceCat is not None: 

170 srcXArr, srcYArr = list(zip(*[src.get(srcCentroidKey) for src in sourceCat])) 

171 plt.plot(srcXArr, srcYArr, marker=sourceMarker, color=sourceColor, linestyle="") 

172 

173 def makeLineSegmentData(refXYArr, srcXYArr, colorArr): 

174 """Make a list of line segement data 

175 

176 This is used to draw line segements between ref and src positions in the specified color 

177 

178 Notes 

179 ----- 

180 The returned data has the format: 

181 [(refX0, srcX0), (refY0, srcY0), color0, (refX1, srcX1), (refY1, srcY1), color1,...] 

182 """ 

183 if len(refXYArr) != len(srcXYArr): 

184 raise RuntimeError("len(refXYArr) = %d != %d = len(srcXYArr)" % 

185 (len(refXYArr), len(srcXYArr))) 

186 if len(refXYArr) != len(colorArr): 

187 raise RuntimeError("len(refXYArr) = %d != %d = len(colorArr)" % 

188 (len(refXYArr), len(colorArr))) 

189 

190 refXArr, refYArr = list(zip(*refXYArr)) 

191 srcXArr, srcYArr = list(zip(*srcXYArr)) 

192 refSrcXArr = list(zip(refXArr, srcXArr)) 

193 refSrcYArr = list(zip(refYArr, srcYArr)) 

194 dataList = [] 

195 for xycolor in zip(refSrcXArr, refSrcYArr, colorArr): 

196 for val in xycolor: 

197 dataList.append(val) 

198 return dataList 

199 

200 refXYArr, srcXYArr = \ 

201 list(zip(*[(match[0].get(refCentroidKey), match[1].get(srcCentroidKey)) for match in matches])) 

202 

203 def plotSourceCircles(matches, color): 

204 srcXYArr = [match[1].get(srcCentroidKey) for match in matches] 

205 srcXArr, srcYArr = list(zip(*srcXYArr)) 

206 plt.plot(srcXArr, srcYArr, "o", mec=color, mfc="none", ms=10,) 

207 

208 if callable(matchColor): 

209 # different matches have different colors 

210 matchColorArr = [matchColor(match) for match in matches] 

211 

212 # plot circles for each color separately 

213 matchColorSet = set(matchColorArr) 

214 for color in matchColorSet: 

215 subMatches = [match for match in matches if matchColor(match) == color] 

216 plotSourceCircles(subMatches, color=color) 

217 else: 

218 matchColorArr = [matchColor]*len(refXYArr) 

219 plotSourceCircles(matches, color=matchColor) 

220 

221 lineSegData = makeLineSegmentData(refXYArr, srcXYArr, matchColorArr) 

222 plt.plot(*lineSegData) 

223 

224 plt.show()