LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
display.py
Go to the documentation of this file.
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 
24 import math
25 
26 import numpy as np
27 
28 import lsst.afw.display as afwDisplay
29 from lsst.afw.image import ExposureF
30 from lsst.afw.table import Point2DKey
31 
32 
33 def 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 
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()
daf::base::PropertyList * list
Definition: fits.cc:913
daf::base::PropertySet * set
Definition: fits.cc:912
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
PointKey< double > Point2DKey
Definition: aggregates.h:118
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
Definition: display.py:34
def plotAstrometry(matches, refCat=None, sourceCat=None, refMarker="x", refColor="r", sourceMarker="+", sourceColor="g", matchColor="y")
Definition: display.py:126
afw::image::Exposure< float > ExposureF
Definition: VeresModel.cc:36