##// END OF EJS Templates
Optimize plots
jespinoza -
r1269:ad3b2d103f49
parent child
Show More
@@ -1,810 +1,803
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 self.data = None
166 166
167 167 def __fmtTime(self, x, pos):
168 168 '''
169 169 '''
170 170
171 171 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
172 172
173 173 def __setup(self, **kwargs):
174 174 '''
175 175 Initialize variables
176 176 '''
177 177
178 178 self.figures = []
179 179 self.axes = []
180 180 self.cb_axes = []
181 181 self.localtime = kwargs.pop('localtime', True)
182 182 self.show = kwargs.get('show', True)
183 183 self.save = kwargs.get('save', False)
184 184 self.save_period = kwargs.get('save_period', 1)
185 185 self.ftp = kwargs.get('ftp', False)
186 186 self.colormap = kwargs.get('colormap', self.colormap)
187 187 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
188 188 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
189 189 self.colormaps = kwargs.get('colormaps', None)
190 190 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
191 191 self.showprofile = kwargs.get('showprofile', False)
192 192 self.title = kwargs.get('wintitle', self.CODE.upper())
193 193 self.cb_label = kwargs.get('cb_label', None)
194 194 self.cb_labels = kwargs.get('cb_labels', None)
195 195 self.labels = kwargs.get('labels', None)
196 196 self.xaxis = kwargs.get('xaxis', 'frequency')
197 197 self.zmin = kwargs.get('zmin', None)
198 198 self.zmax = kwargs.get('zmax', None)
199 199 self.zlimits = kwargs.get('zlimits', None)
200 200 self.xmin = kwargs.get('xmin', None)
201 201 self.xmax = kwargs.get('xmax', None)
202 202 self.xrange = kwargs.get('xrange', 24)
203 203 self.xscale = kwargs.get('xscale', None)
204 204 self.ymin = kwargs.get('ymin', None)
205 205 self.ymax = kwargs.get('ymax', None)
206 206 self.yscale = kwargs.get('yscale', None)
207 207 self.xlabel = kwargs.get('xlabel', None)
208 self.attr_time = kwargs.get('attr_time', 'utctime')
208 209 self.decimation = kwargs.get('decimation', None)
209 210 self.showSNR = kwargs.get('showSNR', False)
210 211 self.oneFigure = kwargs.get('oneFigure', True)
211 212 self.width = kwargs.get('width', None)
212 213 self.height = kwargs.get('height', None)
213 214 self.colorbar = kwargs.get('colorbar', True)
214 215 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
215 216 self.channels = kwargs.get('channels', None)
216 217 self.titles = kwargs.get('titles', [])
217 218 self.polar = False
218 219 self.type = kwargs.get('type', 'iq')
219 220 self.grid = kwargs.get('grid', False)
220 221 self.pause = kwargs.get('pause', False)
221 222 self.save_labels = kwargs.get('save_labels', None)
222 223 self.realtime = kwargs.get('realtime', True)
223 224 self.buffering = kwargs.get('buffering', True)
224 225 self.throttle = kwargs.get('throttle', 2)
225 226 self.exp_code = kwargs.get('exp_code', None)
226 227 self.plot_server = kwargs.get('plot_server', False)
227 228 self.sender_period = kwargs.get('sender_period', 1)
228 229 self.__throttle_plot = apply_throttle(self.throttle)
229 230 self.data = PlotterData(
230 231 self.CODE, self.throttle, self.exp_code, self.buffering, snr=self.showSNR)
231 232
232 233 if self.plot_server:
233 234 if not self.plot_server.startswith('tcp://'):
234 235 self.plot_server = 'tcp://{}'.format(self.plot_server)
235 236 log.success(
236 237 'Sending to server: {}'.format(self.plot_server),
237 238 self.name
238 239 )
239 240 if 'plot_name' in kwargs:
240 241 self.plot_name = kwargs['plot_name']
241 242
242 243 def __setup_plot(self):
243 244 '''
244 245 Common setup for all figures, here figures and axes are created
245 246 '''
246 247
247 248 self.setup()
248 249
249 250 self.time_label = 'LT' if self.localtime else 'UTC'
250 251
251 252 if self.width is None:
252 253 self.width = 8
253 254
254 255 self.figures = []
255 256 self.axes = []
256 257 self.cb_axes = []
257 258 self.pf_axes = []
258 259 self.cmaps = []
259 260
260 261 size = '15%' if self.ncols == 1 else '30%'
261 262 pad = '4%' if self.ncols == 1 else '8%'
262 263
263 264 if self.oneFigure:
264 265 if self.height is None:
265 266 self.height = 1.4 * self.nrows + 1
266 267 fig = plt.figure(figsize=(self.width, self.height),
267 268 edgecolor='k',
268 269 facecolor='w')
269 270 self.figures.append(fig)
270 271 for n in range(self.nplots):
271 272 ax = fig.add_subplot(self.nrows, self.ncols,
272 273 n + 1, polar=self.polar)
273 274 ax.tick_params(labelsize=8)
274 275 ax.firsttime = True
275 276 ax.index = 0
276 277 ax.press = None
277 278 self.axes.append(ax)
278 279 if self.showprofile:
279 280 cax = self.__add_axes(ax, size=size, pad=pad)
280 281 cax.tick_params(labelsize=8)
281 282 self.pf_axes.append(cax)
282 283 else:
283 284 if self.height is None:
284 285 self.height = 3
285 286 for n in range(self.nplots):
286 287 fig = plt.figure(figsize=(self.width, self.height),
287 288 edgecolor='k',
288 289 facecolor='w')
289 290 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
290 291 ax.tick_params(labelsize=8)
291 292 ax.firsttime = True
292 293 ax.index = 0
293 294 ax.press = None
294 295 self.figures.append(fig)
295 296 self.axes.append(ax)
296 297 if self.showprofile:
297 298 cax = self.__add_axes(ax, size=size, pad=pad)
298 299 cax.tick_params(labelsize=8)
299 300 self.pf_axes.append(cax)
300 301
301 302 for n in range(self.nrows):
302 303 if self.colormaps is not None:
303 304 cmap = plt.get_cmap(self.colormaps[n])
304 305 else:
305 306 cmap = plt.get_cmap(self.colormap)
306 307 cmap.set_bad(self.bgcolor, 1.)
307 308 self.cmaps.append(cmap)
308 309
309 310 for fig in self.figures:
310 311 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
311 312 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
312 313 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
313 314 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
314 315 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
315 316
316 317 def OnKeyPress(self, event):
317 318 '''
318 319 Event for pressing keys (up, down) change colormap
319 320 '''
320 321 ax = event.inaxes
321 322 if ax in self.axes:
322 323 if event.key == 'down':
323 324 ax.index += 1
324 325 elif event.key == 'up':
325 326 ax.index -= 1
326 327 if ax.index < 0:
327 328 ax.index = len(CMAPS) - 1
328 329 elif ax.index == len(CMAPS):
329 330 ax.index = 0
330 331 cmap = CMAPS[ax.index]
331 332 ax.cbar.set_cmap(cmap)
332 333 ax.cbar.draw_all()
333 334 ax.plt.set_cmap(cmap)
334 335 ax.cbar.patch.figure.canvas.draw()
335 336 self.colormap = cmap.name
336 337
337 338 def OnBtnScroll(self, event):
338 339 '''
339 340 Event for scrolling, scale figure
340 341 '''
341 342 cb_ax = event.inaxes
342 343 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
343 344 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
344 345 pt = ax.cbar.ax.bbox.get_points()[:, 1]
345 346 nrm = ax.cbar.norm
346 347 vmin, vmax, p0, p1, pS = (
347 348 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
348 349 scale = 2 if event.step == 1 else 0.5
349 350 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
350 351 ax.cbar.norm.vmin = point - scale * (point - vmin)
351 352 ax.cbar.norm.vmax = point - scale * (point - vmax)
352 353 ax.plt.set_norm(ax.cbar.norm)
353 354 ax.cbar.draw_all()
354 355 ax.cbar.patch.figure.canvas.draw()
355 356
356 357 def onBtnPress(self, event):
357 358 '''
358 359 Event for mouse button press
359 360 '''
360 361 cb_ax = event.inaxes
361 362 if cb_ax is None:
362 363 return
363 364
364 365 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
365 366 cb_ax.press = event.x, event.y
366 367 else:
367 368 cb_ax.press = None
368 369
369 370 def onMotion(self, event):
370 371 '''
371 372 Event for move inside colorbar
372 373 '''
373 374 cb_ax = event.inaxes
374 375 if cb_ax is None:
375 376 return
376 377 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
377 378 return
378 379 if cb_ax.press is None:
379 380 return
380 381
381 382 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
382 383 xprev, yprev = cb_ax.press
383 384 dx = event.x - xprev
384 385 dy = event.y - yprev
385 386 cb_ax.press = event.x, event.y
386 387 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
387 388 perc = 0.03
388 389
389 390 if event.button == 1:
390 391 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
391 392 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
392 393 elif event.button == 3:
393 394 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
394 395 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
395 396
396 397 ax.cbar.draw_all()
397 398 ax.plt.set_norm(ax.cbar.norm)
398 399 ax.cbar.patch.figure.canvas.draw()
399 400
400 401 def onBtnRelease(self, event):
401 402 '''
402 403 Event for mouse button release
403 404 '''
404 405 cb_ax = event.inaxes
405 406 if cb_ax is not None:
406 407 cb_ax.press = None
407 408
408 409 def __add_axes(self, ax, size='30%', pad='8%'):
409 410 '''
410 411 Add new axes to the given figure
411 412 '''
412 413 divider = make_axes_locatable(ax)
413 414 nax = divider.new_horizontal(size=size, pad=pad)
414 415 ax.figure.add_axes(nax)
415 416 return nax
416 417
417 418 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
418 419 '''
419 420 Create a masked array for missing data
420 421 '''
421 422 if x_buffer.shape[0] < 2:
422 423 return x_buffer, y_buffer, z_buffer
423 424
424 425 deltas = x_buffer[1:] - x_buffer[0:-1]
425 426 x_median = numpy.median(deltas)
426 427
427 428 index = numpy.where(deltas > 5 * x_median)
428 429
429 430 if len(index[0]) != 0:
430 431 z_buffer[::, index[0], ::] = self.__missing
431 432 z_buffer = numpy.ma.masked_inside(z_buffer,
432 433 0.99 * self.__missing,
433 434 1.01 * self.__missing)
434 435
435 436 return x_buffer, y_buffer, z_buffer
436 437
437 438 def decimate(self):
438 439
439 440 # dx = int(len(self.x)/self.__MAXNUMX) + 1
440 441 dy = int(len(self.y) / self.decimation) + 1
441 442
442 443 # x = self.x[::dx]
443 444 x = self.x
444 445 y = self.y[::dy]
445 446 z = self.z[::, ::, ::dy]
446 447
447 448 return x, y, z
448 449
449 450 def format(self):
450 451 '''
451 452 Set min and max values, labels, ticks and titles
452 453 '''
453 454
454 455 if self.xmin is None:
455 456 xmin = self.data.min_time
456 457 else:
457 458 if self.xaxis is 'time':
458 459 dt = self.getDateTime(self.data.min_time)
459 460 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
460 461 datetime.datetime(1970, 1, 1)).total_seconds()
461 462 if self.data.localtime:
462 463 xmin += time.timezone
463 464 else:
464 465 xmin = self.xmin
465 466
466 467 if self.xmax is None:
467 468 xmax = xmin + self.xrange * 60 * 60
468 469 else:
469 470 if self.xaxis is 'time':
470 471 dt = self.getDateTime(self.data.max_time)
471 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
472 xmax = self.xmax - 1
473 xmax = (dt.replace(hour=int(xmax), minute=59, second=59) -
472 474 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
473 475 if self.data.localtime:
474 476 xmax += time.timezone
475 477 else:
476 478 xmax = self.xmax
477 479
478 480 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
479 481 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
480 #Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000])
481 482
482 #i = 1 if numpy.where(
483 # abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
484 #ystep = Y[i] / 10.
483 for n, ax in enumerate(self.axes):
484 if ax.firsttime:
485
485 486 dig = int(numpy.log10(ymax))
486 487 if dig == 0:
487 488 digD = len(str(ymax)) - 2
488 489 ydec = ymax*(10**digD)
489 490
490 491 dig = int(numpy.log10(ydec))
491 492 ystep = ((ydec + (10**(dig)))//10**(dig))*(10**(dig))
492 493 ystep = ystep/5
493 494 ystep = ystep/(10**digD)
494 495
495 496 else:
496 497 ystep = ((ymax + (10**(dig)))//10**(dig))*(10**(dig))
497 498 ystep = ystep/5
498 499
499 500 if self.xaxis is not 'time':
500 501
501 502 dig = int(numpy.log10(xmax))
502 503
503 504 if dig <= 0:
504 505 digD = len(str(xmax)) - 2
505 506 xdec = xmax*(10**digD)
506 507
507 508 dig = int(numpy.log10(xdec))
508 509 xstep = ((xdec + (10**(dig)))//10**(dig))*(10**(dig))
509 510 xstep = xstep*0.5
510 511 xstep = xstep/(10**digD)
511 512
512 513 else:
513 514 xstep = ((xmax + (10**(dig)))//10**(dig))*(10**(dig))
514 515 xstep = xstep/5
515 516
516 for n, ax in enumerate(self.axes):
517 if ax.firsttime:
518 517 ax.set_facecolor(self.bgcolor)
519 518 ax.yaxis.set_major_locator(MultipleLocator(ystep))
520 519 if self.xscale:
521 520 ax.xaxis.set_major_formatter(FuncFormatter(
522 521 lambda x, pos: '{0:g}'.format(x*self.xscale)))
523 522 if self.xscale:
524 523 ax.yaxis.set_major_formatter(FuncFormatter(
525 524 lambda x, pos: '{0:g}'.format(x*self.yscale)))
526 525 if self.xaxis is 'time':
527 526 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
528 527 ax.xaxis.set_major_locator(LinearLocator(9))
529 528 else:
530 529 ax.xaxis.set_major_locator(MultipleLocator(xstep))
531 530 if self.xlabel is not None:
532 531 ax.set_xlabel(self.xlabel)
533 532 ax.set_ylabel(self.ylabel)
534 533 ax.firsttime = False
535 534 if self.showprofile:
536 535 self.pf_axes[n].set_ylim(ymin, ymax)
537 536 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
538 537 self.pf_axes[n].set_xlabel('dB')
539 538 self.pf_axes[n].grid(b=True, axis='x')
540 539 [tick.set_visible(False)
541 540 for tick in self.pf_axes[n].get_yticklabels()]
542 541 if self.colorbar:
543 542 ax.cbar = plt.colorbar(
544 543 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
545 544 ax.cbar.ax.tick_params(labelsize=8)
546 545 ax.cbar.ax.press = None
547 546 if self.cb_label:
548 547 ax.cbar.set_label(self.cb_label, size=8)
549 548 elif self.cb_labels:
550 549 ax.cbar.set_label(self.cb_labels[n], size=8)
551 550 else:
552 551 ax.cbar = None
553 552 if self.grid:
554 553 ax.grid(True)
555 554
556 555 if not self.polar:
557 556 ax.set_xlim(xmin, xmax)
558 557 ax.set_ylim(ymin, ymax)
559 558 ax.set_title('{} {} {}'.format(
560 559 self.titles[n],
561 560 self.getDateTime(self.data.max_time).strftime(
562 561 '%Y-%m-%d %H:%M:%S'),
563 562 self.time_label),
564 563 size=8)
565 564 else:
566 565 ax.set_title('{}'.format(self.titles[n]), size=8)
567 566 ax.set_ylim(0, 90)
568 567 ax.set_yticks(numpy.arange(0, 90, 20))
569 568 ax.yaxis.labelpad = 40
570 569
571 570 def clear_figures(self):
572 571 '''
573 572 Reset axes for redraw plots
574 573 '''
575 574
576 575 for ax in self.axes:
577 576 ax.clear()
578 577 ax.firsttime = True
579 578 if ax.cbar:
580 579 ax.cbar.remove()
581 580
582 581 def __plot(self):
583 582 '''
584 583 Main function to plot, format and save figures
585 584 '''
586 585
587 586 try:
588 587 self.plot()
589 588 self.format()
590 589 except Exception as e:
591 590 log.warning('{} Plot could not be updated... check data'.format(
592 591 self.CODE), self.name)
593 592 log.error(str(e), '')
594 593 return
595 594
596 595 for n, fig in enumerate(self.figures):
597 596 if self.nrows == 0 or self.nplots == 0:
598 597 log.warning('No data', self.name)
599 598 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
600 599 fig.canvas.manager.set_window_title(self.CODE)
601 600 continue
602 601
603 602 fig.tight_layout()
604 603 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
605 604 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
606 605 fig.canvas.draw()
607 606 if self.show:
608 607 fig.show()
609 figpause(0.1)
608 # figpause(0.1)
610 609
611 610 if self.save:
612 611 self.save_figure(n)
613 612
614 613 if self.plot_server:
615 614 self.send_to_server()
616 # t = Thread(target=self.send_to_server)
617 # t.start()
618 615
619 616 def save_figure(self, n):
620 617 '''
621 618 '''
622 619
623 620 if self.save_counter < self.save_period:
624 621 self.save_counter += 1
625 622 return
626 623
627 624 self.save_counter = 1
628 625
629 626 fig = self.figures[n]
630 627
631 628 if self.save_labels:
632 629 labels = self.save_labels
633 630 else:
634 631 labels = list(range(self.nrows))
635 632
636 633 if self.oneFigure:
637 634 label = ''
638 635 else:
639 636 label = '-{}'.format(labels[n])
640 637 figname = os.path.join(
641 638 self.save,
642 639 self.CODE,
643 640 '{}{}_{}.png'.format(
644 641 self.CODE,
645 642 label,
646 643 self.getDateTime(self.data.max_time).strftime(
647 644 '%Y%m%d_%H%M%S'
648 645 ),
649 646 )
650 647 )
651 648 log.log('Saving figure: {}'.format(figname), self.name)
652 649 if not os.path.isdir(os.path.dirname(figname)):
653 650 os.makedirs(os.path.dirname(figname))
654 651 fig.savefig(figname)
655 652
656 653 if self.realtime:
657 654 figname = os.path.join(
658 655 self.save,
659 656 '{}{}_{}.png'.format(
660 657 self.CODE,
661 658 label,
662 659 self.getDateTime(self.data.min_time).strftime(
663 660 '%Y%m%d'
664 661 ),
665 662 )
666 663 )
667 664 fig.savefig(figname)
668 665
669 666 def send_to_server(self):
670 667 '''
671 668 '''
672 669
673 670 if self.sender_counter < self.sender_period:
674 671 self.sender_counter += 1
675 672 return
676 673
677 674 self.sender_counter = 1
678 675 self.data.meta['titles'] = self.titles
679 676 retries = 2
680 677 while True:
681 678 self.socket.send_string(self.data.jsonify(self.plot_name, self.plot_type))
682 679 socks = dict(self.poll.poll(5000))
683 680 if socks.get(self.socket) == zmq.POLLIN:
684 681 reply = self.socket.recv_string()
685 682 if reply == 'ok':
686 683 log.log("Response from server ok", self.name)
687 684 break
688 685 else:
689 686 log.warning(
690 687 "Malformed reply from server: {}".format(reply), self.name)
691 688
692 689 else:
693 690 log.warning(
694 691 "No response from server, retrying...", self.name)
695 692 self.socket.setsockopt(zmq.LINGER, 0)
696 693 self.socket.close()
697 694 self.poll.unregister(self.socket)
698 695 retries -= 1
699 696 if retries == 0:
700 697 log.error(
701 698 "Server seems to be offline, abandoning", self.name)
702 699 self.socket = self.context.socket(zmq.REQ)
703 700 self.socket.connect(self.plot_server)
704 701 self.poll.register(self.socket, zmq.POLLIN)
705 702 time.sleep(1)
706 703 break
707 704 self.socket = self.context.socket(zmq.REQ)
708 705 self.socket.connect(self.plot_server)
709 706 self.poll.register(self.socket, zmq.POLLIN)
710 707 time.sleep(0.5)
711 708
712 709 def setup(self):
713 710 '''
714 711 This method should be implemented in the child class, the following
715 712 attributes should be set:
716 713
717 714 self.nrows: number of rows
718 715 self.ncols: number of cols
719 716 self.nplots: number of plots (channels or pairs)
720 717 self.ylabel: label for Y axes
721 718 self.titles: list of axes title
722 719
723 720 '''
724 721 raise NotImplementedError
725 722
726 723 def plot(self):
727 724 '''
728 725 Must be defined in the child class
729 726 '''
730 727 raise NotImplementedError
731 728
732 729 def run(self, dataOut, **kwargs):
733 730 '''
734 731 Main plotting routine
735 732 '''
736 733
737 734 if self.isConfig is False:
738 735 self.__setup(**kwargs)
739 if dataOut.type == 'Parameters':
740 t = dataOut.utctimeInit
741 else:
742 t = dataOut.utctime
736
737 t = getattr(dataOut, self.attr_time)
743 738
744 739 if dataOut.useLocalTime:
745 740 self.getDateTime = datetime.datetime.fromtimestamp
746 741 if not self.localtime:
747 742 t += time.timezone
748 743 else:
749 744 self.getDateTime = datetime.datetime.utcfromtimestamp
750 745 if self.localtime:
751 746 t -= time.timezone
752 747
753 748 if 'buffer' in self.plot_type:
754 749 if self.xmin is None:
755 750 self.tmin = t
751 self.xmin = self.getDateTime(t).hour
756 752 else:
757 753 self.tmin = (
758 754 self.getDateTime(t).replace(
759 hour=self.xmin,
755 hour=int(self.xmin),
760 756 minute=0,
761 757 second=0) - self.getDateTime(0)).total_seconds()
762 758
763 759 self.data.setup()
764 760 self.isConfig = True
765 761 if self.plot_server:
766 762 self.context = zmq.Context()
767 763 self.socket = self.context.socket(zmq.REQ)
768 764 self.socket.connect(self.plot_server)
769 765 self.poll = zmq.Poller()
770 766 self.poll.register(self.socket, zmq.POLLIN)
771 767
772 if dataOut.type == 'Parameters':
773 tm = dataOut.utctimeInit
774 else:
775 tm = dataOut.utctime
768 tm = getattr(dataOut, self.attr_time)
776 769
777 770 if not dataOut.useLocalTime and self.localtime:
778 771 tm -= time.timezone
779 772 if dataOut.useLocalTime and not self.localtime:
780 773 tm += time.timezone
781 774
782 775 if self.xaxis is 'time' and self.data and (tm - self.tmin) >= self.xrange*60*60:
783 776 self.save_counter = self.save_period
784 777 self.__plot()
785 778 self.xmin += self.xrange
786 779 if self.xmin >= 24:
787 780 self.xmin -= 24
788 781 self.tmin += self.xrange*60*60
789 782 self.data.setup()
790 783 self.clear_figures()
791 784
792 785 self.data.update(dataOut, tm)
793 786
794 787 if self.isPlotConfig is False:
795 788 self.__setup_plot()
796 789 self.isPlotConfig = True
797 790
798 791 if self.realtime:
799 792 self.__plot()
800 793 else:
801 794 self.__throttle_plot(self.__plot)#, coerce=coerce)
802 795
803 796 def close(self):
804 797
805 798 if self.data:
806 799 self.save_counter = self.save_period
807 800 self.__plot()
808 801 if self.data and self.pause:
809 802 figpause(10)
810 803
General Comments 0
You need to be logged in to leave comments. Login now