1 | #-------------------------------------------------------------------------------
|
---|
2 | # Name: SpectralGraph
|
---|
3 | # Purpose:
|
---|
4 | #
|
---|
5 | # Author: Kelvin
|
---|
6 | #
|
---|
7 | # Created: 02/01/2014
|
---|
8 | #-------------------------------------------------------------------------------
|
---|
9 |
|
---|
10 | import matplotlib.pyplot as plt
|
---|
11 | import matplotlib.widgets as mwidgets
|
---|
12 | import matplotlib.cm as cm
|
---|
13 |
|
---|
14 | from matplotlib.figure import Figure
|
---|
15 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
---|
16 |
|
---|
17 | from numpy import *
|
---|
18 |
|
---|
19 | from PyQt4 import QtCore
|
---|
20 |
|
---|
21 | import pywcs
|
---|
22 |
|
---|
23 | class SpectralGraph(FigureCanvas):
|
---|
24 |
|
---|
25 | def __init__(self, Results, maskFits, realFits, maskImage):
|
---|
26 |
|
---|
27 | # Create the figure containing our final image
|
---|
28 | self.fig = Figure()
|
---|
29 |
|
---|
30 | self.channelText = self.fig.text(0.9, 0.15, '')
|
---|
31 | self.velocityText = self.fig.text(0.9, 0.30, '')
|
---|
32 | self.intensityText = self.fig.text(0.9, 0.45, '')
|
---|
33 |
|
---|
34 | self.descriptionText = self.fig.text(0.65, 0.95, '')
|
---|
35 |
|
---|
36 | self.ax = self.fig.add_subplot(111)
|
---|
37 | self.ax.cla()
|
---|
38 | #self.ax.plot(arange(0, self.lenZ), zeros(self.lenZ))
|
---|
39 | self.ax.set_xlabel('Frequency Channels (GHz)')
|
---|
40 | self.ax.set_ylabel('Intensity (mJ)')
|
---|
41 | self.spectralTitle = self.fig.suptitle("Spectral Characteristic")
|
---|
42 |
|
---|
43 | # Initialise the parent class
|
---|
44 | FigureCanvas.__init__(self, self.fig)
|
---|
45 |
|
---|
46 | self.Results = Results
|
---|
47 |
|
---|
48 | # Those are our data cubes
|
---|
49 | self.maskCube = maskFits[0].data
|
---|
50 | self.realCube = realFits[0].data
|
---|
51 |
|
---|
52 | self.maskImage = maskImage
|
---|
53 |
|
---|
54 | # Those are the size of our data cubes
|
---|
55 | [self.lenZ, self.lenY, self.lenX] = self.maskCube.shape
|
---|
56 |
|
---|
57 | self.maskHeader = maskFits[0].header
|
---|
58 |
|
---|
59 | hdr = bytes(self.maskHeader.tostring(), 'utf-8')
|
---|
60 |
|
---|
61 | imwcs = pywcs.WCS(header = hdr)
|
---|
62 |
|
---|
63 | self.freq = arange(0, self.lenZ)
|
---|
64 |
|
---|
65 | self.vel = imwcs.wcs.crval[2] + imwcs.wcs.cdelt[2]*(self.freq - imwcs.wcs.crpix[2])
|
---|
66 |
|
---|
67 | self.velunit = str(imwcs.wcs.cunit[2], 'utf-8')
|
---|
68 |
|
---|
69 | self.zAxis = 'vel'
|
---|
70 |
|
---|
71 | fx = maskFits[0].header.get('BMAJ')
|
---|
72 | fy = maskFits[0].header.get('BMIN')
|
---|
73 |
|
---|
74 | self.C = pi*fx*fy/(4*log(2))
|
---|
75 |
|
---|
76 | self.zmin = 0
|
---|
77 | self.zmax = self.lenZ
|
---|
78 |
|
---|
79 | self.currentObjID = 0
|
---|
80 |
|
---|
81 | self.spectralType = 'max'
|
---|
82 |
|
---|
83 | self.trackCursorLink = self.mpl_connect('motion_notify_event', self.trackCursor)
|
---|
84 | self.changeZaxisLink = self.mpl_connect('button_press_event', self.changeZaxis)
|
---|
85 |
|
---|
86 | self.intensityUnit = 'Jy/beam'
|
---|
87 | self.draw()
|
---|
88 | self.show()
|
---|
89 |
|
---|
90 | def plotMax(self):
|
---|
91 |
|
---|
92 | self.spectralType = 'max'
|
---|
93 | self.intensityUnit = 'Jy/beam'
|
---|
94 | self.plotSource()
|
---|
95 |
|
---|
96 | def plotSum(self):
|
---|
97 |
|
---|
98 | self.spectralType = 'sum'
|
---|
99 | self.intensityUnit = 'Jy'
|
---|
100 | self.plotSource()
|
---|
101 |
|
---|
102 | def changeZaxis(self, event):
|
---|
103 |
|
---|
104 | if event.button != 1:
|
---|
105 | return
|
---|
106 |
|
---|
107 | if self.zAxis == 'vel':
|
---|
108 | self.zAxis = 'freq'
|
---|
109 | else:
|
---|
110 | self.zAxis = 'vel'
|
---|
111 |
|
---|
112 | self.plotSource()
|
---|
113 |
|
---|
114 | def setCurrentObjID(self, ObjID):
|
---|
115 |
|
---|
116 | self.currentObjID = ObjID
|
---|
117 |
|
---|
118 | def trackCursor(self, event):
|
---|
119 |
|
---|
120 | if not event.inaxes:
|
---|
121 | return
|
---|
122 |
|
---|
123 | self.channelText.set_text('Freq. Channel: %d'%(int(event.xdata)))
|
---|
124 | self.velocityText.set_text('Velocity: %.3f k%s'%(self.vel[int(event.xdata)]/1000, self.velunit))
|
---|
125 | self.intensityText.set_text('Intensity: %.3f %s'%(event.ydata, self.intensityUnit))
|
---|
126 | self.draw()
|
---|
127 |
|
---|
128 | def plotSource(self):
|
---|
129 |
|
---|
130 | self.ax.cla()
|
---|
131 |
|
---|
132 | if self.currentObjID <= 0 or (self.spectralType != 'max' and self.spectralType != 'sum'):
|
---|
133 | self.draw()
|
---|
134 | return
|
---|
135 |
|
---|
136 |
|
---|
137 | ObjStats = self.Results.SkyObjects[self.currentObjID].stats
|
---|
138 |
|
---|
139 | if self.spectralType == 'max':
|
---|
140 |
|
---|
141 | x_peak = ObjStats['X_peak']
|
---|
142 | y_peak = ObjStats['Y_peak']
|
---|
143 | flux = self.realCube[:, y_peak, x_peak]
|
---|
144 |
|
---|
145 | self.descriptionText.set_text('Peak Intensity occurs at (x, y) = (%d, %d)'%(x_peak, y_peak))
|
---|
146 |
|
---|
147 | self.intensityUnit = 'Jy/beam'
|
---|
148 | self.ax.set_ylabel('Intensity (%s)'%self.intensityUnit)
|
---|
149 |
|
---|
150 | elif self.spectralType == 'sum':
|
---|
151 |
|
---|
152 | flux = zeros(self.freq.shape)
|
---|
153 |
|
---|
154 | x1 = ObjStats['X1']
|
---|
155 | x2 = ObjStats['X2']
|
---|
156 | y1 = ObjStats['Y1']
|
---|
157 | y2 = ObjStats['Y2']
|
---|
158 |
|
---|
159 | for x in range(x1, x2 + 1):
|
---|
160 | for y in range(y1, y2 + 1):
|
---|
161 |
|
---|
162 | if self.maskImage[y][x] == self.currentObjID:
|
---|
163 |
|
---|
164 | flux += self.realCube[:, y, x]
|
---|
165 |
|
---|
166 | flux = self.C*flux
|
---|
167 |
|
---|
168 | self.intensityUnit = 'Jy'
|
---|
169 | self.ax.set_ylabel('Intensity (%s)'%self.intensityUnit)
|
---|
170 |
|
---|
171 | self.spectralTitle.set_text("Spectral Characteristic of Source " + str(self.currentObjID))
|
---|
172 |
|
---|
173 | self.linef1 = self.ax.axvline(x = self.Results.zf1, color = "r")
|
---|
174 | self.linef2 = self.ax.axvline(x = self.Results.zf2, color = "r")
|
---|
175 |
|
---|
176 | z1 = ObjStats['Z1']
|
---|
177 | z2 = ObjStats['Z2']
|
---|
178 |
|
---|
179 | self.linef1 = self.ax.axvline(x = z1, color = "g", linestyle = "--")
|
---|
180 | self.linef2 = self.ax.axvline(x = z2, color = "g", linestyle = "--")
|
---|
181 |
|
---|
182 | self.minflux = flux[0]
|
---|
183 | self.maxflux = flux[0]
|
---|
184 |
|
---|
185 | for z in range(self.lenZ):
|
---|
186 |
|
---|
187 | if z >= self.Results.zf1 and z <= self.Results.zf2:
|
---|
188 | pass
|
---|
189 | else:
|
---|
190 |
|
---|
191 | if flux[z] < self.minflux:
|
---|
192 | self.minflux = flux[z]
|
---|
193 |
|
---|
194 | if flux[z] > self.maxflux:
|
---|
195 | self.maxflux = flux[z]
|
---|
196 |
|
---|
197 | self.fluxrange = self.maxflux - self.minflux
|
---|
198 | self.fluxstep = 0.1*self.fluxrange
|
---|
199 | self.fluxbottom = self.minflux - self.fluxstep
|
---|
200 | self.fluxtop = self.maxflux + self.fluxstep
|
---|
201 | self.fluxsteps = arange(self.fluxbottom, self.fluxtop, self.fluxstep)
|
---|
202 |
|
---|
203 | zf1_ratio = self.Results.zf1/self.lenZ
|
---|
204 | zf2_ratio = self.Results.zf2/self.lenZ
|
---|
205 |
|
---|
206 | for f in self.fluxsteps:
|
---|
207 |
|
---|
208 | self.ax.axhline(y = f, xmin = zf1_ratio, xmax = zf2_ratio, color = "r")
|
---|
209 |
|
---|
210 | zspace = 10
|
---|
211 |
|
---|
212 | self.zmin = max(0, z1 - zspace)
|
---|
213 | self.zmax = min(z2 + zspace, self.lenZ)
|
---|
214 |
|
---|
215 | self.ax.plot(self.freq, flux)
|
---|
216 | self.ax.set_xlim(0, self.lenZ)
|
---|
217 |
|
---|
218 | if self.zAxis == 'freq':
|
---|
219 |
|
---|
220 | self.ax.set_xlabel('Frequency Channels (Channel Number)')
|
---|
221 | else:
|
---|
222 |
|
---|
223 | xticks = self.ax.get_xticks()
|
---|
224 |
|
---|
225 | velticklabels = []
|
---|
226 | for xtick in xticks:
|
---|
227 |
|
---|
228 | if xtick >= 0 and xtick < self.lenZ:
|
---|
229 |
|
---|
230 | velticklabel = '%.3f'%(self.vel[int(xtick)]/1000)
|
---|
231 |
|
---|
232 | velticklabels += [velticklabel]
|
---|
233 |
|
---|
234 | self.ax.set_xticklabels(velticklabels)
|
---|
235 | self.ax.set_xlabel('Velocity (%s%s)'%('k',self.velunit))
|
---|
236 |
|
---|
237 |
|
---|
238 | self.ax.set_ylim(self.fluxbottom, self.fluxtop)
|
---|
239 | self.draw()
|
---|
240 |
|
---|
241 |
|
---|
242 |
|
---|
243 | class ZoomGraph(SpectralGraph):
|
---|
244 |
|
---|
245 | trackCursorSignal = QtCore.pyqtSignal(object)
|
---|
246 |
|
---|
247 | def __init__(self, Results, maskFits, realFits, maskImage):
|
---|
248 |
|
---|
249 | SpectralGraph.__init__(self, Results, maskFits, realFits, maskImage)
|
---|
250 |
|
---|
251 | def trackCursor(self, event):
|
---|
252 |
|
---|
253 | self.trackCursorSignal.emit(event)
|
---|
254 |
|
---|
255 | def plotSource(self):
|
---|
256 |
|
---|
257 | self.ax.cla()
|
---|
258 |
|
---|
259 | if self.currentObjID <= 0 or (self.spectralType != 'max' and self.spectralType != 'sum'):
|
---|
260 | self.draw()
|
---|
261 | return
|
---|
262 |
|
---|
263 |
|
---|
264 | ObjStats = self.Results.SkyObjects[self.currentObjID].stats
|
---|
265 |
|
---|
266 | z1 = ObjStats['Z1']
|
---|
267 | z2 = ObjStats['Z2']
|
---|
268 |
|
---|
269 | zspace = 10
|
---|
270 |
|
---|
271 | zmin = max(0, z1 - zspace)
|
---|
272 | zmax = min(z2 + zspace, self.lenZ - 1)
|
---|
273 |
|
---|
274 | zrange = arange(zmin, zmax + 1)
|
---|
275 |
|
---|
276 | if self.spectralType == 'max':
|
---|
277 |
|
---|
278 | x_peak = ObjStats['X_peak']
|
---|
279 | y_peak = ObjStats['Y_peak']
|
---|
280 |
|
---|
281 | flux = self.realCube[zrange, y_peak, x_peak]
|
---|
282 |
|
---|
283 | self.intensityUnit = 'Jy/beam'
|
---|
284 | self.ax.set_ylabel('Intensity (%s)'%self.intensityUnit)
|
---|
285 |
|
---|
286 | elif self.spectralType == 'sum':
|
---|
287 |
|
---|
288 | flux = zeros(zrange.shape)
|
---|
289 |
|
---|
290 | x1 = ObjStats['X1']
|
---|
291 | x2 = ObjStats['X2']
|
---|
292 | y1 = ObjStats['Y1']
|
---|
293 | y2 = ObjStats['Y2']
|
---|
294 |
|
---|
295 | for x in range(x1, x2 + 1):
|
---|
296 | for y in range(y1, y2 + 1):
|
---|
297 |
|
---|
298 | if self.maskImage[y][x] == self.currentObjID:
|
---|
299 |
|
---|
300 | flux += self.realCube[zrange, y, x]
|
---|
301 |
|
---|
302 |
|
---|
303 | flux = self.C*flux
|
---|
304 | self.intensityUnit = 'Jy'
|
---|
305 | self.ax.set_ylabel('Intensity (%s)'%self.intensityUnit)
|
---|
306 |
|
---|
307 | self.linef1 = self.ax.axvline(x = z1, color = "g", linestyle = "--")
|
---|
308 | self.linef2 = self.ax.axvline(x = z2, color = "g", linestyle = "--")
|
---|
309 |
|
---|
310 |
|
---|
311 |
|
---|
312 | self.minflux = flux[0]
|
---|
313 | self.maxflux = flux[0]
|
---|
314 |
|
---|
315 | for i in range(len(flux)):
|
---|
316 |
|
---|
317 | if zrange[i] >= self.Results.zf1 and zrange[i] <= self.Results.zf2:
|
---|
318 | pass
|
---|
319 | else:
|
---|
320 |
|
---|
321 | if flux[i] < self.minflux:
|
---|
322 | self.minflux = flux[i]
|
---|
323 |
|
---|
324 | if flux[i] > self.maxflux:
|
---|
325 | self.maxflux = flux[i]
|
---|
326 |
|
---|
327 | self.fluxrange = self.maxflux - self.minflux
|
---|
328 | self.fluxstep = 0.1*self.fluxrange
|
---|
329 | self.fluxbottom = self.minflux - self.fluxstep
|
---|
330 | self.fluxtop = self.maxflux + self.fluxstep
|
---|
331 | self.fluxsteps = arange(self.fluxbottom, self.fluxtop, self.fluxstep)
|
---|
332 |
|
---|
333 | self.ax.plot(zrange, flux)
|
---|
334 | self.ax.set_xlim(zmin, zmax)
|
---|
335 |
|
---|
336 | if self.zAxis == 'freq':
|
---|
337 |
|
---|
338 | self.ax.set_xlabel('Frequency Channels (Channel Number)')
|
---|
339 | else:
|
---|
340 |
|
---|
341 | xticks = self.ax.get_xticks()
|
---|
342 |
|
---|
343 | velticklabels = []
|
---|
344 | for xtick in xticks:
|
---|
345 |
|
---|
346 | if xtick >= 0 and xtick < self.lenZ:
|
---|
347 |
|
---|
348 | velticklabel = velticklabel = '%4.0f'%(self.vel[int(xtick)]/1000)
|
---|
349 |
|
---|
350 | velticklabels += [velticklabel]
|
---|
351 |
|
---|
352 | self.ax.set_xticklabels(velticklabels)
|
---|
353 | self.ax.set_xlabel('Velocity (%s%s)'%('k',self.velunit))
|
---|
354 |
|
---|
355 | self.ax.set_ylim(self.fluxbottom, self.fluxtop)
|
---|
356 | self.spectralTitle.set_text("Source " + str(self.currentObjID))
|
---|
357 | self.draw() |
---|