##// END OF EJS Templates
Fix plotting bug
Juan C. Espinoza -
r1214:9d8b5d03f906
parent child
Show More
@@ -1,784 +1,802
1 1
2 2 import os
3 3 import sys
4 4 import zmq
5 5 import time
6 6 import numpy
7 7 import datetime
8 8 from functools import wraps
9 9 from threading import Thread
10 10 import matplotlib
11 11
12 12 if 'BACKEND' in os.environ:
13 13 matplotlib.use(os.environ['BACKEND'])
14 14 elif 'linux' in sys.platform:
15 15 matplotlib.use("TkAgg")
16 16 elif 'darwin' in sys.platform:
17 17 matplotlib.use('WxAgg')
18 18 else:
19 19 from schainpy.utils import log
20 20 log.warning('Using default Backend="Agg"', 'INFO')
21 21 matplotlib.use('Agg')
22 22
23 23 import matplotlib.pyplot as plt
24 24 from matplotlib.patches import Polygon
25 25 from mpl_toolkits.axes_grid1 import make_axes_locatable
26 26 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
27 27
28 28 from schainpy.model.data.jrodata import PlotterData
29 29 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
30 30 from schainpy.utils import log
31 31
32 32 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
33 33 blu_values = matplotlib.pyplot.get_cmap(
34 34 'seismic_r', 20)(numpy.arange(20))[10:15]
35 35 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
36 36 'jro', numpy.vstack((blu_values, jet_values)))
37 37 matplotlib.pyplot.register_cmap(cmap=ncmap)
38 38
39 39 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis',
40 40 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
41 41
42 42 EARTH_RADIUS = 6.3710e3
43 43
44 44 def ll2xy(lat1, lon1, lat2, lon2):
45 45
46 46 p = 0.017453292519943295
47 47 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
48 48 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
49 49 r = 12742 * numpy.arcsin(numpy.sqrt(a))
50 50 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
51 51 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
52 52 theta = -theta + numpy.pi/2
53 53 return r*numpy.cos(theta), r*numpy.sin(theta)
54 54
55 55
56 56 def km2deg(km):
57 57 '''
58 58 Convert distance in km to degrees
59 59 '''
60 60
61 61 return numpy.rad2deg(km/EARTH_RADIUS)
62 62
63 63
64 64 def figpause(interval):
65 65 backend = plt.rcParams['backend']
66 66 if backend in matplotlib.rcsetup.interactive_bk:
67 67 figManager = matplotlib._pylab_helpers.Gcf.get_active()
68 68 if figManager is not None:
69 69 canvas = figManager.canvas
70 70 if canvas.figure.stale:
71 71 canvas.draw()
72 72 try:
73 73 canvas.start_event_loop(interval)
74 74 except:
75 75 pass
76 76 return
77 77
78 78
79 79 def popup(message):
80 80 '''
81 81 '''
82 82
83 83 fig = plt.figure(figsize=(12, 8), facecolor='r')
84 84 text = '\n'.join([s.strip() for s in message.split(':')])
85 85 fig.text(0.01, 0.5, text, ha='left', va='center',
86 86 size='20', weight='heavy', color='w')
87 87 fig.show()
88 88 figpause(1000)
89 89
90 90
91 91 class Throttle(object):
92 92 '''
93 93 Decorator that prevents a function from being called more than once every
94 94 time period.
95 95 To create a function that cannot be called more than once a minute, but
96 96 will sleep until it can be called:
97 97 @Throttle(minutes=1)
98 98 def foo():
99 99 pass
100 100
101 101 for i in range(10):
102 102 foo()
103 103 print "This function has run %s times." % i
104 104 '''
105 105
106 106 def __init__(self, seconds=0, minutes=0, hours=0):
107 107 self.throttle_period = datetime.timedelta(
108 108 seconds=seconds, minutes=minutes, hours=hours
109 109 )
110 110
111 111 self.time_of_last_call = datetime.datetime.min
112 112
113 113 def __call__(self, fn):
114 114 @wraps(fn)
115 115 def wrapper(*args, **kwargs):
116 116 coerce = kwargs.pop('coerce', None)
117 117 if coerce:
118 118 self.time_of_last_call = datetime.datetime.now()
119 119 return fn(*args, **kwargs)
120 120 else:
121 121 now = datetime.datetime.now()
122 122 time_since_last_call = now - self.time_of_last_call
123 123 time_left = self.throttle_period - time_since_last_call
124 124
125 125 if time_left > datetime.timedelta(seconds=0):
126 126 return
127 127
128 128 self.time_of_last_call = datetime.datetime.now()
129 129 return fn(*args, **kwargs)
130 130
131 131 return wrapper
132 132
133 133 def apply_throttle(value):
134 134
135 135 @Throttle(seconds=value)
136 136 def fnThrottled(fn):
137 137 fn()
138 138
139 139 return fnThrottled
140 140
141 141
142 142 @MPDecorator
143 143 class Plot(Operation):
144 144 '''
145 145 Base class for Schain plotting operations
146 146 '''
147 147
148 148 CODE = 'Figure'
149 149 colormap = 'jet'
150 150 bgcolor = 'white'
151 151 __missing = 1E30
152 152
153 153 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
154 154 'zlimits', 'xlabel', 'ylabel', 'xaxis', 'cb_label', 'title',
155 155 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
156 156 'showprofile', 'decimation', 'pause']
157 157
158 158 def __init__(self):
159 159
160 160 Operation.__init__(self)
161 161 self.isConfig = False
162 162 self.isPlotConfig = False
163 163 self.save_counter = 1
164 164 self.sender_counter = 1
165 165
166 166 def __fmtTime(self, x, pos):
167 167 '''
168 168 '''
169 169
170 170 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
171 171
172 172 def __setup(self, **kwargs):
173 173 '''
174 174 Initialize variables
175 175 '''
176 176
177 177 self.figures = []
178 178 self.axes = []
179 179 self.cb_axes = []
180 180 self.localtime = kwargs.pop('localtime', True)
181 181 self.show = kwargs.get('show', True)
182 182 self.save = kwargs.get('save', False)
183 183 self.save_period = kwargs.get('save_period', 2)
184 184 self.ftp = kwargs.get('ftp', False)
185 185 self.colormap = kwargs.get('colormap', self.colormap)
186 186 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
187 187 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
188 188 self.colormaps = kwargs.get('colormaps', None)
189 189 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
190 190 self.showprofile = kwargs.get('showprofile', False)
191 191 self.title = kwargs.get('wintitle', self.CODE.upper())
192 192 self.cb_label = kwargs.get('cb_label', None)
193 193 self.cb_labels = kwargs.get('cb_labels', None)
194 194 self.labels = kwargs.get('labels', None)
195 195 self.xaxis = kwargs.get('xaxis', 'frequency')
196 196 self.zmin = kwargs.get('zmin', None)
197 197 self.zmax = kwargs.get('zmax', None)
198 198 self.zlimits = kwargs.get('zlimits', None)
199 199 self.xmin = kwargs.get('xmin', None)
200 200 self.xmax = kwargs.get('xmax', None)
201 201 self.xrange = kwargs.get('xrange', 24)
202 202 self.xscale = kwargs.get('xscale', None)
203 203 self.ymin = kwargs.get('ymin', None)
204 204 self.ymax = kwargs.get('ymax', None)
205 205 self.yscale = kwargs.get('yscale', None)
206 206 self.xlabel = kwargs.get('xlabel', None)
207 207 self.decimation = kwargs.get('decimation', None)
208 208 self.showSNR = kwargs.get('showSNR', False)
209 209 self.oneFigure = kwargs.get('oneFigure', True)
210 210 self.width = kwargs.get('width', None)
211 211 self.height = kwargs.get('height', None)
212 212 self.colorbar = kwargs.get('colorbar', True)
213 213 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
214 214 self.channels = kwargs.get('channels', None)
215 215 self.titles = kwargs.get('titles', [])
216 216 self.polar = False
217 217 self.type = kwargs.get('type', 'iq')
218 218 self.grid = kwargs.get('grid', False)
219 219 self.pause = kwargs.get('pause', False)
220 220 self.save_labels = kwargs.get('save_labels', None)
221 221 self.realtime = kwargs.get('realtime', True)
222 222 self.buffering = kwargs.get('buffering', True)
223 223 self.throttle = kwargs.get('throttle', 2)
224 224 self.exp_code = kwargs.get('exp_code', None)
225 225 self.plot_server = kwargs.get('plot_server', False)
226 226 self.sender_period = kwargs.get('sender_period', 2)
227 227 self.__throttle_plot = apply_throttle(self.throttle)
228 228 self.data = PlotterData(
229 229 self.CODE, self.throttle, self.exp_code, self.buffering)
230 230
231 231 if self.plot_server:
232 232 if not self.plot_server.startswith('tcp://'):
233 233 self.plot_server = 'tcp://{}'.format(self.plot_server)
234 234 log.success(
235 235 'Sending to server: {}'.format(self.plot_server),
236 236 self.name
237 237 )
238 238
239 239 def __setup_plot(self):
240 240 '''
241 241 Common setup for all figures, here figures and axes are created
242 242 '''
243 243
244 244 self.setup()
245 245
246 246 self.time_label = 'LT' if self.localtime else 'UTC'
247 247 if self.data.localtime:
248 248 self.getDateTime = datetime.datetime.fromtimestamp
249 249 else:
250 250 self.getDateTime = datetime.datetime.utcfromtimestamp
251 251
252 252 if self.width is None:
253 253 self.width = 8
254 254
255 255 self.figures = []
256 256 self.axes = []
257 257 self.cb_axes = []
258 258 self.pf_axes = []
259 259 self.cmaps = []
260 260
261 261 size = '15%' if self.ncols == 1 else '30%'
262 262 pad = '4%' if self.ncols == 1 else '8%'
263 263
264 264 if self.oneFigure:
265 265 if self.height is None:
266 266 self.height = 1.4 * self.nrows + 1
267 267 fig = plt.figure(figsize=(self.width, self.height),
268 268 edgecolor='k',
269 269 facecolor='w')
270 270 self.figures.append(fig)
271 271 for n in range(self.nplots):
272 272 ax = fig.add_subplot(self.nrows, self.ncols,
273 273 n + 1, polar=self.polar)
274 274 ax.tick_params(labelsize=8)
275 275 ax.firsttime = True
276 276 ax.index = 0
277 277 ax.press = None
278 278 self.axes.append(ax)
279 279 if self.showprofile:
280 280 cax = self.__add_axes(ax, size=size, pad=pad)
281 281 cax.tick_params(labelsize=8)
282 282 self.pf_axes.append(cax)
283 283 else:
284 284 if self.height is None:
285 285 self.height = 3
286 286 for n in range(self.nplots):
287 287 fig = plt.figure(figsize=(self.width, self.height),
288 288 edgecolor='k',
289 289 facecolor='w')
290 290 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
291 291 ax.tick_params(labelsize=8)
292 292 ax.firsttime = True
293 293 ax.index = 0
294 294 ax.press = None
295 295 self.figures.append(fig)
296 296 self.axes.append(ax)
297 297 if self.showprofile:
298 298 cax = self.__add_axes(ax, size=size, pad=pad)
299 299 cax.tick_params(labelsize=8)
300 300 self.pf_axes.append(cax)
301 301
302 302 for n in range(self.nrows):
303 303 if self.colormaps is not None:
304 304 cmap = plt.get_cmap(self.colormaps[n])
305 305 else:
306 306 cmap = plt.get_cmap(self.colormap)
307 307 cmap.set_bad(self.bgcolor, 1.)
308 308 self.cmaps.append(cmap)
309 309
310 310 for fig in self.figures:
311 311 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
312 312 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
313 313 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
314 314 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
315 315 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
316 316 if self.show:
317 317 fig.show()
318 318
319 319 def OnKeyPress(self, event):
320 320 '''
321 321 Event for pressing keys (up, down) change colormap
322 322 '''
323 323 ax = event.inaxes
324 324 if ax in self.axes:
325 325 if event.key == 'down':
326 326 ax.index += 1
327 327 elif event.key == 'up':
328 328 ax.index -= 1
329 329 if ax.index < 0:
330 330 ax.index = len(CMAPS) - 1
331 331 elif ax.index == len(CMAPS):
332 332 ax.index = 0
333 333 cmap = CMAPS[ax.index]
334 334 ax.cbar.set_cmap(cmap)
335 335 ax.cbar.draw_all()
336 336 ax.plt.set_cmap(cmap)
337 337 ax.cbar.patch.figure.canvas.draw()
338 338 self.colormap = cmap.name
339 339
340 340 def OnBtnScroll(self, event):
341 341 '''
342 342 Event for scrolling, scale figure
343 343 '''
344 344 cb_ax = event.inaxes
345 345 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
346 346 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
347 347 pt = ax.cbar.ax.bbox.get_points()[:, 1]
348 348 nrm = ax.cbar.norm
349 349 vmin, vmax, p0, p1, pS = (
350 350 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
351 351 scale = 2 if event.step == 1 else 0.5
352 352 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
353 353 ax.cbar.norm.vmin = point - scale * (point - vmin)
354 354 ax.cbar.norm.vmax = point - scale * (point - vmax)
355 355 ax.plt.set_norm(ax.cbar.norm)
356 356 ax.cbar.draw_all()
357 357 ax.cbar.patch.figure.canvas.draw()
358 358
359 359 def onBtnPress(self, event):
360 360 '''
361 361 Event for mouse button press
362 362 '''
363 363 cb_ax = event.inaxes
364 364 if cb_ax is None:
365 365 return
366 366
367 367 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
368 368 cb_ax.press = event.x, event.y
369 369 else:
370 370 cb_ax.press = None
371 371
372 372 def onMotion(self, event):
373 373 '''
374 374 Event for move inside colorbar
375 375 '''
376 376 cb_ax = event.inaxes
377 377 if cb_ax is None:
378 378 return
379 379 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
380 380 return
381 381 if cb_ax.press is None:
382 382 return
383 383
384 384 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
385 385 xprev, yprev = cb_ax.press
386 386 dx = event.x - xprev
387 387 dy = event.y - yprev
388 388 cb_ax.press = event.x, event.y
389 389 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
390 390 perc = 0.03
391 391
392 392 if event.button == 1:
393 393 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
394 394 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
395 395 elif event.button == 3:
396 396 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
397 397 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
398 398
399 399 ax.cbar.draw_all()
400 400 ax.plt.set_norm(ax.cbar.norm)
401 401 ax.cbar.patch.figure.canvas.draw()
402 402
403 403 def onBtnRelease(self, event):
404 404 '''
405 405 Event for mouse button release
406 406 '''
407 407 cb_ax = event.inaxes
408 408 if cb_ax is not None:
409 409 cb_ax.press = None
410 410
411 411 def __add_axes(self, ax, size='30%', pad='8%'):
412 412 '''
413 413 Add new axes to the given figure
414 414 '''
415 415 divider = make_axes_locatable(ax)
416 416 nax = divider.new_horizontal(size=size, pad=pad)
417 417 ax.figure.add_axes(nax)
418 418 return nax
419 419
420 420 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
421 421 '''
422 422 Create a masked array for missing data
423 423 '''
424 424 if x_buffer.shape[0] < 2:
425 425 return x_buffer, y_buffer, z_buffer
426 426
427 427 deltas = x_buffer[1:] - x_buffer[0:-1]
428 428 x_median = numpy.median(deltas)
429 429
430 430 index = numpy.where(deltas > 5 * x_median)
431 431
432 432 if len(index[0]) != 0:
433 433 z_buffer[::, index[0], ::] = self.__missing
434 434 z_buffer = numpy.ma.masked_inside(z_buffer,
435 435 0.99 * self.__missing,
436 436 1.01 * self.__missing)
437 437
438 438 return x_buffer, y_buffer, z_buffer
439 439
440 440 def decimate(self):
441 441
442 442 # dx = int(len(self.x)/self.__MAXNUMX) + 1
443 443 dy = int(len(self.y) / self.decimation) + 1
444 444
445 445 # x = self.x[::dx]
446 446 x = self.x
447 447 y = self.y[::dy]
448 448 z = self.z[::, ::, ::dy]
449 449
450 450 return x, y, z
451 451
452 452 def format(self):
453 453 '''
454 454 Set min and max values, labels, ticks and titles
455 455 '''
456 456
457 457 if self.xmin is None:
458 458 xmin = self.data.min_time
459 459 else:
460 460 if self.xaxis is 'time':
461 461 dt = self.getDateTime(self.data.min_time)
462 462 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
463 463 datetime.datetime(1970, 1, 1)).total_seconds()
464 464 if self.data.localtime:
465 465 xmin += time.timezone
466 self.tmin = xmin
467 466 else:
468 467 xmin = self.xmin
469 468
470 469 if self.xmax is None:
471 470 xmax = xmin + self.xrange * 60 * 60
472 471 else:
473 472 if self.xaxis is 'time':
474 473 dt = self.getDateTime(self.data.max_time)
475 474 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
476 475 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
477 476 if self.data.localtime:
478 477 xmax += time.timezone
479 478 else:
480 479 xmax = self.xmax
481 480
482 481 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
483 482 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
484 483 #Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000])
485 484
486 485 #i = 1 if numpy.where(
487 486 # abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
488 487 #ystep = Y[i] / 10.
489 488 dig = int(numpy.log10(ymax))
490 489 if dig == 0:
491 490 digD = len(str(ymax)) - 2
492 491 ydec = ymax*(10**digD)
493 492
494 493 dig = int(numpy.log10(ydec))
495 494 ystep = ((ydec + (10**(dig)))//10**(dig))*(10**(dig))
496 495 ystep = ystep/5
497 496 ystep = ystep/(10**digD)
498 497
499 498 else:
500 499 ystep = ((ymax + (10**(dig)))//10**(dig))*(10**(dig))
501 500 ystep = ystep/5
502 501
503 502 if self.xaxis is not 'time':
504 503
505 504 dig = int(numpy.log10(xmax))
506 505
507 506 if dig <= 0:
508 507 digD = len(str(xmax)) - 2
509 508 xdec = xmax*(10**digD)
510 509
511 510 dig = int(numpy.log10(xdec))
512 511 xstep = ((xdec + (10**(dig)))//10**(dig))*(10**(dig))
513 512 xstep = xstep*0.5
514 513 xstep = xstep/(10**digD)
515 514
516 515 else:
517 516 xstep = ((xmax + (10**(dig)))//10**(dig))*(10**(dig))
518 517 xstep = xstep/5
519 518
520 519 for n, ax in enumerate(self.axes):
521 520 if ax.firsttime:
522 521 ax.set_facecolor(self.bgcolor)
523 522 ax.yaxis.set_major_locator(MultipleLocator(ystep))
524 523 if self.xscale:
525 524 ax.xaxis.set_major_formatter(FuncFormatter(
526 525 lambda x, pos: '{0:g}'.format(x*self.xscale)))
527 526 if self.xscale:
528 527 ax.yaxis.set_major_formatter(FuncFormatter(
529 528 lambda x, pos: '{0:g}'.format(x*self.yscale)))
530 529 if self.xaxis is 'time':
531 530 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
532 531 ax.xaxis.set_major_locator(LinearLocator(9))
533 532 else:
534 533 ax.xaxis.set_major_locator(MultipleLocator(xstep))
535 534 if self.xlabel is not None:
536 535 ax.set_xlabel(self.xlabel)
537 536 ax.set_ylabel(self.ylabel)
538 537 ax.firsttime = False
539 538 if self.showprofile:
540 539 self.pf_axes[n].set_ylim(ymin, ymax)
541 540 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
542 541 self.pf_axes[n].set_xlabel('dB')
543 542 self.pf_axes[n].grid(b=True, axis='x')
544 543 [tick.set_visible(False)
545 544 for tick in self.pf_axes[n].get_yticklabels()]
546 545 if self.colorbar:
547 546 ax.cbar = plt.colorbar(
548 547 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
549 548 ax.cbar.ax.tick_params(labelsize=8)
550 549 ax.cbar.ax.press = None
551 550 if self.cb_label:
552 551 ax.cbar.set_label(self.cb_label, size=8)
553 552 elif self.cb_labels:
554 553 ax.cbar.set_label(self.cb_labels[n], size=8)
555 554 else:
556 555 ax.cbar = None
557 556 if self.grid:
558 557 ax.grid(True)
559 558
560 559 if not self.polar:
561 560 ax.set_xlim(xmin, xmax)
562 561 ax.set_ylim(ymin, ymax)
563 562 ax.set_title('{} {} {}'.format(
564 563 self.titles[n],
565 564 self.getDateTime(self.data.max_time).strftime(
566 '%H:%M:%S'),
565 '%Y-%m-%d %H:%M:%S'),
567 566 self.time_label),
568 567 size=8)
569 568 else:
570 569 ax.set_title('{}'.format(self.titles[n]), size=8)
571 570 ax.set_ylim(0, 90)
572 571 ax.set_yticks(numpy.arange(0, 90, 20))
573 572 ax.yaxis.labelpad = 40
574 573
575 574 def clear_figures(self):
576 575 '''
577 576 Reset axes for redraw plots
578 577 '''
579 578
580 579 for ax in self.axes:
581 580 ax.clear()
582 581 ax.firsttime = True
583 582 if ax.cbar:
584 583 ax.cbar.remove()
585 584
586 585 def __plot(self):
587 586 '''
588 587 Main function to plot, format and save figures
589 588 '''
590 589
591 590 try:
592 591 self.plot()
593 592 self.format()
594 593 except Exception as e:
595 594 log.warning('{} Plot could not be updated... check data'.format(
596 595 self.CODE), self.name)
597 596 log.error(str(e), '')
598 597 return
599 598
600 599 for n, fig in enumerate(self.figures):
601 600 if self.nrows == 0 or self.nplots == 0:
602 601 log.warning('No data', self.name)
603 602 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
604 603 fig.canvas.manager.set_window_title(self.CODE)
605 604 continue
606 605
607 606 fig.tight_layout()
608 607 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
609 608 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
610 609 fig.canvas.draw()
611 610
612 611 if self.save:
613 612 self.save_figure(n)
614 613
615 614 if self.plot_server:
616 615 self.send_to_server()
617 616 # t = Thread(target=self.send_to_server)
618 617 # t.start()
619 618
620 619 def save_figure(self, n):
621 620 '''
622 621 '''
623 622
624 623 if self.save_counter < self.save_period:
625 624 self.save_counter += 1
626 625 return
627 626
628 627 self.save_counter = 1
629 628
630 629 fig = self.figures[n]
631 630
632 631 if self.save_labels:
633 632 labels = self.save_labels
634 633 else:
635 634 labels = list(range(self.nrows))
636 635
637 636 if self.oneFigure:
638 637 label = ''
639 638 else:
640 639 label = '-{}'.format(labels[n])
641 640 figname = os.path.join(
642 641 self.save,
643 642 self.CODE,
644 643 '{}{}_{}.png'.format(
645 644 self.CODE,
646 645 label,
647 646 self.getDateTime(self.data.max_time).strftime(
648 647 '%Y%m%d_%H%M%S'
649 648 ),
650 649 )
651 650 )
652 651 log.log('Saving figure: {}'.format(figname), self.name)
653 652 if not os.path.isdir(os.path.dirname(figname)):
654 653 os.makedirs(os.path.dirname(figname))
655 654 fig.savefig(figname)
656 655
657 656 if self.realtime:
658 657 figname = os.path.join(
659 658 self.save,
660 659 '{}{}_{}.png'.format(
661 660 self.CODE,
662 661 label,
663 662 self.getDateTime(self.data.min_time).strftime(
664 663 '%Y%m%d'
665 664 ),
666 665 )
667 666 )
668 667 fig.savefig(figname)
669 668
670 669 def send_to_server(self):
671 670 '''
672 671 '''
673 672
674 673 if self.sender_counter < self.sender_period:
675 674 self.sender_counter += 1
676 675
677 676 self.sender_counter = 1
678 677
679 678 retries = 2
680 679 while True:
681 680 self.socket.send_string(self.data.jsonify())
682 681 socks = dict(self.poll.poll(5000))
683 682 if socks.get(self.socket) == zmq.POLLIN:
684 683 reply = self.socket.recv_string()
685 684 if reply == 'ok':
686 685 log.log("Response from server ok", self.name)
687 686 break
688 687 else:
689 688 log.warning(
690 689 "Malformed reply from server: {}".format(reply), self.name)
691 690
692 691 else:
693 692 log.warning(
694 693 "No response from server, retrying...", self.name)
695 694 self.socket.setsockopt(zmq.LINGER, 0)
696 695 self.socket.close()
697 696 self.poll.unregister(self.socket)
698 697 retries -= 1
699 698 if retries == 0:
700 699 log.error(
701 700 "Server seems to be offline, abandoning", self.name)
702 701 self.socket = self.context.socket(zmq.REQ)
703 702 self.socket.connect(self.plot_server)
704 703 self.poll.register(self.socket, zmq.POLLIN)
705 704 time.sleep(1)
706 705 break
707 706 self.socket = self.context.socket(zmq.REQ)
708 707 self.socket.connect(self.plot_server)
709 708 self.poll.register(self.socket, zmq.POLLIN)
710 709 time.sleep(0.5)
711 710
712 711 def setup(self):
713 712 '''
714 713 This method should be implemented in the child class, the following
715 714 attributes should be set:
716 715
717 716 self.nrows: number of rows
718 717 self.ncols: number of cols
719 718 self.nplots: number of plots (channels or pairs)
720 719 self.ylabel: label for Y axes
721 720 self.titles: list of axes title
722 721
723 722 '''
724 723 raise NotImplementedError
725 724
726 725 def plot(self):
727 726 '''
728 727 Must be defined in the child class
729 728 '''
730 729 raise NotImplementedError
731 730
732 731 def run(self, dataOut, **kwargs):
733 732
734 733 if dataOut.error:
735 734 coerce = True
736 735 else:
737 736 coerce = False
738 737
739 738 if self.isConfig is False:
740 739 self.__setup(**kwargs)
740 if dataOut.type == 'Parameters':
741 self.tmin = dataOut.utctimeInit
742 else:
743 self.tmin = dataOut.utctime
744
745 if dataOut.useLocalTime:
746 if not self.localtime:
747 self.tmin += time.timezone
748 else:
749 if self.localtime:
750 self.tmin -= time.timezone
741 751 self.data.setup()
742 752 self.isConfig = True
743 753 if self.plot_server:
744 754 self.context = zmq.Context()
745 755 self.socket = self.context.socket(zmq.REQ)
746 756 self.socket.connect(self.plot_server)
747 757 self.poll = zmq.Poller()
748 758 self.poll.register(self.socket, zmq.POLLIN)
749 759
750 760 if dataOut.type == 'Parameters':
751 761 tm = dataOut.utctimeInit
752 762 else:
753 763 tm = dataOut.utctime
754
764
755 765 if dataOut.useLocalTime:
756 766 if not self.localtime:
757 767 tm += time.timezone
758 768 else:
759 769 if self.localtime:
760 770 tm -= time.timezone
761
762 if self.xaxis is 'time' and self.data and (tm - self.tmin) >= self.xrange*60*60:
771
772 if self.xaxis is 'time' and self.data and (tm - self.tmin) >= self.xrange*60*60:
773 self.save_counter = self.save_period
763 774 self.__plot()
775 self.xmin += self.xrange
776 if self.xmin >= 24:
777 self.xmin -= 24
778 self.tmin += self.xrange*60*60
764 779 self.data.setup()
765 780 self.clear_figures()
766 781
767 782 self.data.update(dataOut, tm)
768 783
769 784 if self.isPlotConfig is False:
770 785 self.__setup_plot()
771 786 self.isPlotConfig = True
772 787
773 788 if self.realtime:
774 789 self.__plot()
775 790 else:
776 791 self.__throttle_plot(self.__plot, coerce=coerce)
777 792
778 figpause(0.001)
793 figpause(0.01)
779 794
780 795 def close(self):
781 796
797 if self.data:
798 self.save_counter = self.save_period
799 self.__plot()
782 800 if self.data and self.pause:
783 801 figpause(10)
784 802
General Comments 0
You need to be logged in to leave comments. Login now