Coverage for python/lsst/analysis/drp/plotUtils.py: 9%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
2import matplotlib.pyplot as plt
3import scipy.odr as scipyODR
5from lsst.geom import Box2D
8def parsePlotInfo(dataId, runName, tableName):
9 """Parse plot info from the dataId
11 Parameters
12 ----------
13 dataId : `lsst.daf.butler.core.dimensions.`
14 `_coordinate._ExpandedTupleDataCoordinate`
15 runName : `str`
17 Returns
18 -------
19 plotInfo : `dict`
20 """
21 plotInfo = {"run": runName, "tractTableType": tableName}
23 for dataInfo in dataId:
24 plotInfo[dataInfo.name] = dataId[dataInfo.name]
26 if "filter" not in plotInfo.keys():
27 plotInfo["filter"] = "N/A"
29 return plotInfo
32def generateSummaryStats(cat, colName, skymap, plotInfo):
33 """Generate a summary statistic in each patch
35 Parameters
36 ----------
37 cat : `pandas.core.frame.DataFrame`
38 colName : `str`
39 skymap : `lsst.skymap.ringsSkyMap.RingsSkyMap`
40 plotInfo : `dict`
42 Returns
43 -------
44 patchInfoDict : `dict`
45 """
47 # TODO: what is the more generic type of skymap?
48 tractInfo = skymap.generateTract(plotInfo["tract"])
49 tractWcs = tractInfo.getWcs()
51 # For now also convert the gen 2 patchIds to gen 3
53 patchInfoDict = {}
54 for patch in cat.patch.unique():
55 if patch is None:
56 continue
57 # Once the objectTable_tract catalogues are using gen 3 patches
58 # this will go away
59 onPatch = (cat["patch"] == patch)
60 stat = np.nanmedian(cat[colName].values[onPatch])
61 try:
62 patchTuple = (int(patch.split(",")[0]), int(patch.split(",")[-1]))
63 patchInfo = tractInfo.getPatchInfo(patchTuple)
64 gen3PatchId = tractInfo.getSequentialPatchIndex(patchInfo)
65 except AttributeError:
66 # For native gen 3 tables the patches don't need converting
67 # When we are no longer looking at the gen 2 -> gen 3
68 # converted repos we can tidy this up
69 gen3PatchId = patch
70 patchInfo = tractInfo.getPatchInfo(patch)
72 corners = Box2D(patchInfo.getInnerBBox()).getCorners()
73 skyCoords = tractWcs.pixelToSky(corners)
75 patchInfoDict[gen3PatchId] = (skyCoords, stat)
77 tractCorners = Box2D(tractInfo.getBBox()).getCorners()
78 skyCoords = tractWcs.pixelToSky(tractCorners)
79 patchInfoDict["tract"] = (skyCoords, np.nan)
81 return patchInfoDict
84def addPlotInfo(fig, plotInfo):
85 """Add useful information to the plot
87 Parameters
88 ----------
89 fig : `matplotlib.figure.Figure`
90 plotInfo : `dict`
92 Returns
93 -------
94 fig : `matplotlib.figure.Figure`
95 """
97 # TO DO: figure out how to get this information
98 photocalibDataset = "None"
99 astroDataset = "None"
101 plt.text(0.02, 0.98, "Run:" + plotInfo["run"], fontsize=8, alpha=0.8, transform=fig.transFigure)
102 datasetType = ("Datasets used: photocalib: " + photocalibDataset + ", astrometry: " + astroDataset)
103 tableType = "Table: " + plotInfo["tractTableType"]
104 dataIdText = "Tract: " + str(plotInfo["tract"]) + " , Filter: " + plotInfo["filter"]
105 plt.text(0.02, 0.95, datasetType, fontsize=8, alpha=0.8, transform=fig.transFigure)
107 plt.text(0.02, 0.92, tableType, fontsize=8, alpha=0.8, transform=fig.transFigure)
108 plt.text(0.02, 0.89, dataIdText, fontsize=8, alpha=0.8, transform=fig.transFigure)
109 return fig
112def stellarLocusFit(xs, ys, paramDict):
113 """Make a fit to the stellar locus
115 Parameters
116 ----------
117 xs : `numpy.ndarray`
118 The color on the xaxis
119 ys : `numpy.ndarray`
120 The color on the yaxis
121 paramDict : lsst.pex.config.dictField.Dict
122 A dictionary of parameters for line fitting
123 xMin : `float`
124 The minimum x edge of the box to use for initial fitting
125 xMax : `float`
126 The maximum x edge of the box to use for initial fitting
127 yMin : `float`
128 The minimum y edge of the box to use for initial fitting
129 yMax : `float`
130 The maximum y edge of the box to use for initial fitting
131 mHW : `float`
132 The hardwired gradient for the fit
133 bHW : `float`
134 The hardwired intercept of the fit
136 Returns
137 -------
138 paramsOut : `dict`
139 A dictionary of the calculated fit parameters
140 xMin : `float`
141 The minimum x edge of the box to use for initial fitting
142 xMax : `float`
143 The maximum x edge of the box to use for initial fitting
144 yMin : `float`
145 The minimum y edge of the box to use for initial fitting
146 yMax : `float`
147 The maximum y edge of the box to use for initial fitting
148 mHW : `float`
149 The hardwired gradient for the fit
150 bHW : `float`
151 The hardwired intercept of the fit
152 mODR : `float`
153 The gradient calculated by the ODR fit
154 bODR : `float`
155 The intercept calculated by the ODR fit
156 yBoxMin : `float`
157 The y value of the fitted line at xMin
158 yBoxMax : `float`
159 The y value of the fitted line at xMax
160 bPerpMin : `float`
161 The intercept of the perpendicular line that goes through xMin
162 bPerpMax : `float`
163 The intercept of the perpendicular line that goes through xMax
164 mODR2 : `float`
165 The gradient from the second round of fitting
166 bODR2 : `float`
167 The intercept from the second round of fitting
168 mPerp : `float`
169 The gradient of the line perpendicular to the line from the
170 second fit
172 Notes
173 -----
174 The code does two rounds of fitting, the first is initiated using the
175 hardwired values given in the `paramDict` parameter and is done using
176 an Orthogonal Distance Regression fit to the points defined by the
177 box of xMin, xMax, yMin and yMax. Once this fitting has been done a
178 perpendicular bisector is calculated at either end of the line and
179 only points that fall within these lines are used to recalculate the fit.
180 """
182 # Points to use for the fit
183 fitPoints = np.where((xs > paramDict["xMin"]) & (xs < paramDict["xMax"])
184 & (ys > paramDict["yMin"]) & (ys < paramDict["yMax"]))[0]
186 linear = scipyODR.polynomial(1)
188 data = scipyODR.Data(xs[fitPoints], ys[fitPoints])
189 odr = scipyODR.ODR(data, linear, beta0=[paramDict["bHW"], paramDict["mHW"]])
190 params = odr.run()
191 mODR = float(params.beta[1])
192 bODR = float(params.beta[0])
194 paramsOut = {"xMin": paramDict["xMin"], "xMax": paramDict["xMax"], "yMin": paramDict["yMin"],
195 "yMax": paramDict["yMax"], "mHW": paramDict["mHW"], "bHW": paramDict["bHW"],
196 "mODR": mODR, "bODR": bODR}
198 # Having found the initial fit calculate perpendicular ends
199 mPerp = -1.0/mODR
200 # When the gradient is really steep we need to use
201 # the y limits of the box rather than the x ones
203 if np.fabs(mODR) > 1:
204 yBoxMin = paramDict["yMin"]
205 xBoxMin = (yBoxMin - bODR)/mODR
206 yBoxMax = paramDict["yMax"]
207 xBoxMax = (yBoxMax - bODR)/mODR
208 else:
209 yBoxMin = mODR*paramDict["xMin"] + bODR
210 xBoxMin = paramDict["xMin"]
211 yBoxMax = mODR*paramDict["xMax"] + bODR
212 xBoxMax = paramDict["xMax"]
214 bPerpMin = yBoxMin - mPerp*xBoxMin
216 paramsOut["yBoxMin"] = yBoxMin
217 paramsOut["bPerpMin"] = bPerpMin
219 bPerpMax = yBoxMax - mPerp*xBoxMax
221 paramsOut["yBoxMax"] = yBoxMax
222 paramsOut["bPerpMax"] = bPerpMax
224 # Use these perpendicular lines to chose the data and refit
225 fitPoints = ((ys > mPerp*xs + bPerpMin) & (ys < mPerp*xs + bPerpMax))
226 data = scipyODR.Data(xs[fitPoints], ys[fitPoints])
227 odr = scipyODR.ODR(data, linear, beta0=[bODR, mODR])
228 params = odr.run()
229 mODR = float(params.beta[1])
230 bODR = float(params.beta[0])
232 paramsOut["mODR2"] = float(params.beta[1])
233 paramsOut["bODR2"] = float(params.beta[0])
235 paramsOut["mPerp"] = -1.0/paramsOut["mODR2"]
237 return paramsOut
240def perpDistance(p1, p2, points):
241 """Calculate the perpendicular distance to a line from a point
243 Parameters
244 ----------
245 p1 : `numpy.ndarray`
246 A point on the line
247 p2 : `numpy.ndarray`
248 Another point on the line
249 points : `zip`
250 The points to calculate the distance to
252 Returns
253 -------
254 dists : `list`
255 The distances from the line to the points. Uses the cross
256 product to work this out.
257 """
258 dists = []
259 for point in points:
260 point = np.array(point)
261 distToLine = np.cross(p1 - point, p2 - point)/np.linalg.norm(p2 - point)
262 dists.append(distToLine)
264 return dists