import numpy as np
import os
from qtpy import QtGui, QtCore, QtWidgets
import sharppy.sharptab as tab
import sharppy.databases.inset_data as inset_data
from sharppy.sharptab.constants import *
import platform
## routine written by Kelton Halbert and Greg Blumberg
## keltonhalbert@ou.edu and wblumberg@ou.edu
__all__ = ['backgroundSTP', 'plotSTP']
[docs]class backgroundSTP(QtWidgets.QFrame):
'''
Draw the background frame and lines for the STP plot frame
'''
def __init__(self):
super(backgroundSTP, self).__init__()
self.initUI()
[docs] def initUI(self):
## window configuration settings,
## sich as padding, width, height, and
## min/max plot axes
self.setStyleSheet("QFrame {"
" background-color: rgb(0, 0, 0);"
" border-width: 1px;"
" border-style: solid;"
" border-color: #3399CC;}")
self.textpad = 5
self.font_ratio = 0.0512
fsize1 = round(self.size().height() * self.font_ratio) + 2
fsize2 = round(self.size().height() * self.font_ratio)
self.plot_font = QtGui.QFont('Helvetica', fsize1 )
self.box_font = QtGui.QFont('Helvetica', fsize2)
self.plot_metrics = QtGui.QFontMetrics( self.plot_font )
self.box_metrics = QtGui.QFontMetrics(self.box_font)
if platform.system() == "Windows":
fsize1 -= self.plot_metrics.descent()
fsize2 -= self.box_metrics.descent()
self.plot_font = QtGui.QFont('Helvetica', fsize1 )
self.box_font = QtGui.QFont('Helvetica', fsize2)
self.plot_metrics = QtGui.QFontMetrics( self.plot_font )
self.box_metrics = QtGui.QFontMetrics(self.box_font)
self.plot_height = self.plot_metrics.xHeight()# + self.textpad
self.box_height = self.box_metrics.xHeight() + self.textpad
self.tpad = self.plot_height + 15;
self.bpad = self.plot_height + 2
self.lpad = 0.; self.rpad = 0.
self.wid = self.size().width() - self.rpad
self.hgt = self.size().height() - self.bpad
self.tlx = self.rpad; self.tly = self.tpad;
self.brx = self.wid;
self.bry = self.hgt - self.bpad#+ round(self.font_ratio * self.hgt)
self.stpmax = 11.; self.stpmin = 0.
self.plotBitMap = QtGui.QPixmap(self.width()-2, self.height()-2)
self.plotBitMap.fill(self.bg_color)
self.plotBackground()
[docs] def resizeEvent(self, e):
'''
Handles the event the window is resized
'''
self.initUI()
[docs] def plotBackground(self):
'''
Handles painting the frame.
'''
## initialize a painter object and draw the frame
qp = QtGui.QPainter()
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
self.draw_frame(qp)
qp.end()
[docs] def draw_frame(self, qp):
'''
Draw the background frame.
qp: QtGui.QPainter object
'''
## set a new pen to draw with
pen = QtGui.QPen(self.fg_color, 2, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setFont(self.plot_font)
# Left, top, width, height
rect1 = QtCore.QRectF(0,5, self.brx, self.plot_height)
qp.drawText(rect1, QtCore.Qt.TextDontClip | QtCore.Qt.AlignCenter,
'Effective Layer STP (with CIN)')
pen = QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.DashLine)
qp.setPen(pen)
ytick_fontsize = round(self.font_ratio * self.hgt) + 1
y_ticks_font = QtGui.QFont('Helvetica', ytick_fontsize)
qp.setFont(y_ticks_font)
stp_inset_data = inset_data.stpData()
texts = stp_inset_data['stp_ytexts']
# Plot the y-tick labels for the box and whisker plots
for i in range(len(texts)):
pen = QtGui.QPen(self.line_color, 1, QtCore.Qt.DashLine)
qp.setPen(pen)
tick_pxl = self.stp_to_pix(int(texts[i]))
qp.drawLine(self.tlx, tick_pxl, self.brx, tick_pxl)
color = QtGui.QColor(self.bg_color)
pen = QtGui.QPen(color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
ypos = tick_pxl - ytick_fontsize/2
rect = QtCore.QRect(self.tlx, ypos, 20, ytick_fontsize)
pen = QtGui.QPen(self.fg_color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.drawText(rect, QtCore.Qt.TextDontClip | QtCore.Qt.AlignCenter, texts[i])
# Draw the box and whisker plots and the x-tick labels
ef = stp_inset_data['ef']
width = self.brx / 14
spacing = self.brx / 7
center = np.arange(spacing, self.brx, spacing)
texts = stp_inset_data['stp_xtexts']
ef = self.stp_to_pix(ef)
qp.setFont(QtGui.QFont('Helvetica', round(self.font_ratio * self.hgt)))
for i in range(ef.shape[0]):
# Set green pen to draw box and whisker plots
pen = QtGui.QPen(self.box_color, 2, QtCore.Qt.SolidLine)
qp.setPen(pen)
# Draw lower whisker
qp.drawLine(center[i], ef[i,0], center[i], ef[i,1])
# Draw box
qp.drawLine(center[i] - width/2., ef[i,3], center[i] + width/2., ef[i,3])
qp.drawLine(center[i] - width/2., ef[i,1], center[i] + width/2., ef[i,1])
qp.drawLine(center[i] - width/2., ef[i,1], center[i] - width/2., ef[i,3])
qp.drawLine(center[i] + width/2., ef[i,1], center[i] + width/2., ef[i,3])
# Draw median
qp.drawLine(center[i] - width/2., ef[i,2], center[i] + width/2., ef[i,2])
# Draw upper whisker
qp.drawLine(center[i], ef[i,3], center[i], ef[i,4])
# Set black transparent pen to draw a rectangle
color = QtGui.QColor(self.bg_color)
color.setAlpha(0)
pen = QtGui.QPen(color, 1, QtCore.Qt.SolidLine)
rect = QtCore.QRectF(center[i] - width/2., self.bry + round(self.bpad/2), width, self.bpad)
# Change to a white pen to draw the text below the box and whisker plot
pen = QtGui.QPen(self.fg_color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.drawText(rect, QtCore.Qt.TextDontClip | QtCore.Qt.AlignCenter, texts[i])
#qp.drawText(rect, QtCore.Qt.AlignCenter, texts[i])
[docs] def stp_to_pix(self, stp):
scl1 = self.stpmax - self.stpmin
scl2 = self.stpmin + stp
return self.bry - (scl2 / scl1) * (self.bry - self.tpad)
[docs]class plotSTP(backgroundSTP):
'''
Plot the data on the frame. Inherits the background class that
plots the frame.
'''
def __init__(self):
self.bg_color = QtGui.QColor('#000000')
self.fg_color = QtGui.QColor('#ffffff')
self.box_color = QtGui.QColor('#00ff00')
self.line_color = QtGui.QColor('#0080ff')
self.alert_colors = [
QtGui.QColor('#775000'),
QtGui.QColor('#996600'),
QtGui.QColor('#ffffff'),
QtGui.QColor('#ffff00'),
QtGui.QColor('#ff0000'),
QtGui.QColor('#e700df'),
]
self.use_left = False
super(plotSTP, self).__init__()
self.prof = None
[docs] def setProf(self, prof):
self.prof = prof
self.mlcape = prof.mlpcl.bplus
self.mllcl = prof.mlpcl.lclhght
self.ebwd = prof.ebwspd
if self.use_left:
self.esrh = prof.left_esrh[0]
self.stpc = prof.left_stp_cin
self.stpf = prof.left_stp_fixed
else:
self.esrh = prof.right_esrh[0]
self.stpc = prof.right_stp_cin
self.stpf = prof.right_stp_fixed
if self.prof.latitude < 0:
self.esrh = -self.esrh
self.stpc = -self.stpc
self.stpf = -self.stpf
## get the probabilities
self.cape_p, self.cape_c = self.cape_prob(self.mlcape)
self.lcl_p, self.lcl_c = self.lcl_prob(self.mllcl)
self.esrh_p, self.esrh_c = self.esrh_prob(self.esrh)
self.ebwd_p, self.ebwd_c = self.ebwd_prob(self.ebwd)
self.stpc_p, self.stpc_c = self.stpc_prob(self.stpc)
self.stpf_p, self.stpf_c = self.stpf_prob(self.stpf)
#self.ylast = self.tpad
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def setPreferences(self, update_gui=True, **prefs):
self.bg_color = QtGui.QColor(prefs['bg_color'])
self.fg_color = QtGui.QColor(prefs['fg_color'])
self.box_color = QtGui.QColor(prefs['stp_box_color'])
self.line_color = QtGui.QColor(prefs['stp_line_color'])
self.alert_colors = [
QtGui.QColor(prefs['alert_l1_color']),
QtGui.QColor(prefs['alert_l2_color']),
QtGui.QColor(prefs['alert_l3_color']),
QtGui.QColor(prefs['alert_l4_color']),
QtGui.QColor(prefs['alert_l5_color']),
QtGui.QColor(prefs['alert_l6_color']),
]
if update_gui:
if self.use_left:
self.esrh = self.prof.left_esrh[0]
self.stpc = self.prof.left_stp_cin
self.stpf = self.prof.left_stp_fixed
else:
self.esrh = self.prof.right_esrh[0]
self.stpc = self.prof.right_stp_cin
self.stpf = self.prof.right_stp_fixed
if self.prof is not None and self.prof.latitude < 0:
self.esrh = -self.esrh
self.stpc = -self.stpc
self.stpf = -self.stpf
self.cape_p, self.cape_c = self.cape_prob(self.mlcape)
self.lcl_p, self.lcl_c = self.lcl_prob(self.mllcl)
self.esrh_p, self.esrh_c = self.esrh_prob(self.esrh)
self.ebwd_p, self.ebwd_c = self.ebwd_prob(self.ebwd)
self.stpc_p, self.stpc_c = self.stpc_prob(self.stpc)
self.stpf_p, self.stpf_c = self.stpf_prob(self.stpf)
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def setDeviant(self, deviant):
self.use_left = deviant == 'left'
if self.use_left:
self.esrh = self.prof.left_esrh[0]
self.stpc = self.prof.left_stp_cin
self.stpf = self.prof.left_stp_fixed
else:
self.esrh = self.prof.right_esrh[0]
self.stpc = self.prof.right_stp_cin
self.stpf = self.prof.right_stp_fixed
if self.prof.latitude < 0:
self.esrh = -self.esrh
self.stpc = -self.stpc
self.stpf = -self.stpf
self.esrh_p, self.esrh_c = self.esrh_prob(self.esrh)
self.stpc_p, self.stpc_c = self.stpc_prob(self.stpc)
self.stpf_p, self.stpf_c = self.stpf_prob(self.stpf)
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def cape_prob(self, cape):
if cape == 0.:
prob = 0.00
color = self.alert_colors[0]
elif cape > 0. and cape < 250.:
prob = .12
color = self.alert_colors[1]
elif cape >= 250. and cape < 500.:
prob = .14
color = self.alert_colors[2]
elif cape >= 500. and cape < 1000.:
prob = .16
color = self.alert_colors[2]
elif cape >= 1000. and cape < 1500.:
prob = .15
color = self.alert_colors[2]
elif cape >= 1500. and cape < 2000.:
prob = .13
color = self.alert_colors[2]
elif cape >= 2000. and cape < 2500.:
prob = .14
color = self.alert_colors[2]
elif cape >= 2500. and cape < 3000.:
prob = .18
color = self.alert_colors[3]
elif cape >= 3000. and cape < 4000.:
prob = .20
color = self.alert_colors[3]
elif cape >= 4000.:
prob = .16
color = self.alert_colors[3]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def lcl_prob(self, lcl):
if lcl < 750.:
prob = .19
color = self.alert_colors[3]
elif lcl >= 750. and lcl < 1000.:
prob = .19
color = self.alert_colors[3]
elif lcl >= 1000. and lcl < 1250.:
prob = .15
color = self.alert_colors[2]
elif lcl >= 1250. and lcl < 1500.:
prob = .10
color = self.alert_colors[1]
elif lcl >= 1500. and lcl < 1750:
prob = .06
color = self.alert_colors[0]
elif lcl >= 1750. and lcl < 2000.:
prob = .06
color = self.alert_colors[0]
elif lcl >= 2000. and lcl < 2500.:
prob = .02
color = self.alert_colors[0]
elif lcl >= 2500:
prob = 0.0
color = self.alert_colors[0]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def esrh_prob(self, esrh):
if esrh < 50.:
prob = .06
color = self.alert_colors[0]
elif esrh >= 50. and esrh < 100.:
prob = .06
color = self.alert_colors[0]
elif esrh >= 100. and esrh < 200.:
prob = .08
color = self.alert_colors[1]
elif esrh >= 200. and esrh < 300:
prob = .14
color = self.alert_colors[2]
elif esrh >= 300. and esrh < 400.:
prob = .20
color = self.alert_colors[3]
elif esrh >= 400. and esrh < 500.:
prob = .27
color = self.alert_colors[3]
elif esrh >= 500. and esrh < 600:
prob = .38
color = self.alert_colors[4]
elif esrh >= 600. and esrh < 700.:
prob = .37
color = self.alert_colors[4]
elif esrh >= 700:
prob = .42
color = self.alert_colors[4]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def ebwd_prob(self, ebwd):
if ebwd == 0.:
prob = 0.0
color = self.alert_colors[0]
elif ebwd >= .01 and ebwd < 20.:
prob = .03
color = self.alert_colors[0]
elif ebwd >= 20. and ebwd < 30.:
prob = .05
color = self.alert_colors[0]
elif ebwd >= 30. and ebwd < 40.:
prob = .06
color = self.alert_colors[0]
elif ebwd >= 40. and ebwd < 50.:
prob = .12
color = self.alert_colors[1]
elif ebwd >= 50. and ebwd < 60.:
prob = .19
color = self.alert_colors[3]
elif ebwd >= 60. and ebwd < 70.:
prob = .27
color = self.alert_colors[3]
elif ebwd >= 70. and ebwd < 80.:
prob = .36
color = self.alert_colors[4]
elif ebwd >= 80.:
prob = .26
color = self.alert_colors[3]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def stpc_prob(self, stpc):
if stpc < .1:
prob = .06
color = self.alert_colors[0]
elif stpc >= .1 and stpc < .50:
prob = .08
color = self.alert_colors[1]
elif stpc >= .5 and stpc < 1.0:
prob = .12
color = self.alert_colors[1]
elif stpc >= 1. and stpc < 2.:
prob = .17
color = self.alert_colors[2]
elif stpc >= 2. and stpc < 4.:
prob = .25
color = self.alert_colors[3]
elif stpc >= 4. and stpc < 6.:
prob = .32
color = self.alert_colors[4]
elif stpc >= 6. and stpc < 8.:
prob = .34
color = self.alert_colors[4]
elif stpc >= 8. and stpc < 10.:
prob = .55
color = self.alert_colors[5]
elif stpc >= 10.:
prob = .58
color = self.alert_colors[5]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def stpf_prob(self, stpf):
if stpf < .1:
prob = .05
color = self.alert_colors[0]
elif stpf >= .1 and stpf < .5:
prob = .06
color = self.alert_colors[0]
elif stpf >= .5 and stpf < 1.:
prob = .11
color = self.alert_colors[1]
elif stpf >= 1. and stpf < 2.:
prob = .17
color = self.alert_colors[2]
elif stpf >= 2. and stpf < 3.:
prob = .25
color = self.alert_colors[3]
elif stpf >= 3. and stpf < 5.:
prob = .25
color = self.alert_colors[3]
elif stpf >= 5. and stpf < 7.:
prob = .39
color = self.alert_colors[4]
elif stpf >= 7. and stpf < 9.:
prob = .55
color = self.alert_colors[5]
elif stpf >= 9.:
prob = .59
color = self.alert_colors[5]
else:
prob = np.ma.masked
color = self.alert_colors[0]
return prob, color
[docs] def resizeEvent(self, e):
'''
Handles when the window is resized
'''
super(plotSTP, self).resizeEvent(e)
self.plotData()
[docs] def paintEvent(self, e):
super(plotSTP, self).paintEvent(e)
qp = QtGui.QPainter()
qp.begin(self)
qp.drawPixmap(1, 1, self.plotBitMap)
qp.end()
[docs] def clearData(self):
'''
Handles the clearing of the pixmap
in the frame.
'''
self.plotBitMap = QtGui.QPixmap(self.width(), self.height())
self.plotBitMap.fill(self.bg_color)
[docs] def plotData(self):
'''
Handles painting on the frame
'''
if self.prof is None:
return
## this function handles painting the plot
## create a new painter obkect
qp = QtGui.QPainter()
self.draw_stp(qp)
self.draw_box(qp)
[docs] def draw_stp(self, qp):
if not tab.utils.QC(self.stpc):
return
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
if self.stpc < 0:
self.stpc = 0
elif self.stpc > 11.:
self.stpc = 11.
prob, color = self.stpc_prob(self.stpc)
ef = self.stp_to_pix(self.stpc)
pen = QtGui.QPen(color, 1.5, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.drawLine(0, ef, self.wid, ef)
qp.end()
[docs] def draw_box(self, qp):
qp.begin(self.plotBitMap)
width = self.brx / 14.
left_x = width * 7
right_x = self.brx - 5.
top_y = self.stp_to_pix(11.)
vspace = self.box_height + 1
if platform.system() == "Windows":
vspace += self.box_metrics.descent()
bot_y = top_y + vspace * 8
## fill the box with a black background
brush = QtGui.QBrush(self.bg_color, QtCore.Qt.SolidPattern)
pen = QtGui.QPen(self.bg_color, 0, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setBrush(brush)
qp.drawRect(left_x, top_y, right_x - left_x, bot_y - top_y)
## draw the borders of the box
pen = QtGui.QPen(self.fg_color, 2, QtCore.Qt.SolidLine)
brush = QtGui.QBrush(QtCore.Qt.NoBrush)
qp.setPen(pen)
qp.setBrush(brush)
qp.drawLine( left_x, top_y, right_x, top_y )
qp.drawLine( left_x, bot_y, right_x, bot_y )
qp.drawLine( left_x, top_y, left_x, bot_y )
qp.drawLine( right_x, top_y, right_x, bot_y)
## set the font and line width for the rest of the plotting
qp.setFont(self.box_font)
## plot the left column of text
width = right_x - left_x - 3
y1 = top_y + 2
x1 = left_x+3
x2 = x1 + (width * .75)
## start with the header/title
texts = ['Prob EF2+ torn with supercell', 'Sample CLIMO = .15 sigtor']
for text in texts:
rect = QtCore.QRectF(x1, y1, width, self.box_height)
qp.drawText(rect, QtCore.Qt.TextDontClip | QtCore.Qt.AlignLeft, text)
vspace = self.box_height + 1
if platform.system() == "Windows":
vspace += self.box_metrics.descent()
y1 += vspace
vspace = y1 - 1
if platform.system() == "Windows":
vspace += 2
qp.drawLine(left_x, vspace, right_x, vspace)
## draw the variable names
texts = ['based on CAPE: ', 'based on LCL:', 'based on ESRH:', 'based on EBWD:',
'based on STPC:', 'based on STP_fixed:' ]
probs = [self.cape_p, self.lcl_p, self.esrh_p, self.ebwd_p, self.stpc_p, self.stpf_p]
colors = [self.cape_c, self.lcl_c, self.esrh_c, self.ebwd_c, self.stpc_c, self.stpf_c]
for text, p, c in zip(texts, probs, colors):
pen = QtGui.QPen(c, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
rect = QtCore.QRectF(x1, y1, width, self.box_height)
rect2 = QtCore.QRectF(x2, y1, width, self.box_height)
qp.drawText(rect, QtCore.Qt.TextDontClip | QtCore.Qt.AlignLeft, text)
qp.drawText(rect2, QtCore.Qt.TextDontClip | QtCore.Qt.AlignLeft, tab.utils.FLOAT2STR(p,2) )
vspace = self.box_height
if platform.system() == "Windows":
vspace += self.box_metrics.descent()
y1 += vspace
qp.end()
if __name__ == '__main__':
app_frame = QtGui.QApplication([])
tester = plotSTP()
tester.setGeometry(50,50,293,195)
#tester.plotBitMap.save('test.png', format='png')
tester.show()
app_frame.exec_()