# -------------------------------------------------------------------
# - NAME: synopsymbol.py
# - AUTHOR: Reto Stauffer
# - DATE: 2015-12-12
# -------------------------------------------------------------------
# - DESCRIPTION: Small library handling the plot for synop symbol.s
# Main class is synopsymbol.
# -------------------------------------------------------------------
# - EDITORIAL: 2015-12-12, RS: Created file on pc24-c707.
# -------------------------------------------------------------------
# - L@ST MODIFIED: 2018-01-21 18:14 on marvin
# -------------------------------------------------------------------
import logging
log = logging.getLogger(__name__)
import drawbarb
[docs]class synopsymbol( drawbarb.drawbarb ):
"""Synopsymbol class extending the drawbarb class drawing the
vector wind barbs onto the figure."""
[docs] def __init__( self, config ):
"""Initialize a synopsymbol class. Inputs needed: config from
@see readconfig.readconfig. It contains the fonts and other stuff
we need later.
@return Returns the initialized class itself."""
self.config = config
self.data = {}
# ----------------------------------------------------------------
# Appending data
# ----------------------------------------------------------------
def addValue( self, key, value ):
key=key.lower()
if not type(value) == type(int()) and not type(value) == type(float()):
self.exit("Only integer and float arguments allowed for addValues")
if key in self.data.keys():
log.warning("\"%s\" has already been added. Overwrite." % key)
self.data[key] = value
# ----------------------------------------------------------------
# Shows stored values, development/logging function.
# ----------------------------------------------------------------
def showValues(self):
log.info("Values added to the class:")
for key,val in self.data.iteritems():
log.info(" - %-10s %6.2f" % (key,val))
# ----------------------------------------------------------------
# Draw the symbol now
# ----------------------------------------------------------------
def drawSymbol( self, file="out.png" ):
import os, sys
outfile = os.path.join( self.config.outdir, file )
log.info("[+] Create new figure: %s" % outfile)
# Open figure
self._open_figure_()
# day and hour
if "day" in self.data:
self._draw_day_value_( self.data['day'] )
if "hour" in self.data:
self._draw_hour_value_( self.data['hour'] )
# Now adding the different things. There are small subfunctions
# handling the different elements.
if "cc" in self.data:
self._draw_cloudcover_symbol_( self.data['cc'] )
else:
self._draw_cloudcover_noobs_()
if "ch" in self.data:
self._draw_cloudhigh_symbol_( self.data['ch'] )
if "cm" in self.data:
self._draw_cloudmid_symbol_( self.data['cm'] )
if "cl" in self.data:
self._draw_cloudlow_symbol_( self.data['cl'] )
if "t" in self.data:
self._draw_temperature_value_( self.data['t'] )
if "td" in self.data:
self._draw_dewpoint_value_( self.data['td'] )
if "pmsl" in self.data:
self._draw_pressure_value_( self.data['pmsl'] )
if "pch" in self.data:
self._draw_pressurechange_value_( self.data['pch'] )
if "ptend" in self.data:
self._draw_pressurechange_symbol_( self.data['ptend'] )
if "ww" in self.data:
self._draw_currentweather_symbol_( self.data['ww'] )
if "dd" in self.data and "ff" in self.data:
# FF in METERS PER SEC PLEASE
self._draw_windbarb_(0,0,self.data['ff'],self.data['dd'])
# Save figure
self._save_figure_(outfile)
self._create_current_(outfile)
# ----------------------------------------------------------------
# Draw temperature and dew point temperature
# ----------------------------------------------------------------
def _draw_day_value_(self,value):
string = "%02d" % value
opts = {"verticalalignment":"bottom","horizontalalignment":"right",
"color":"gray","fontsize":self.config.fontsize*0.6}
self._place_symbol_(.9,-.95,string,"xxx",opts)
def _draw_hour_value_(self,value):
string = "%02d" % value
opts = {"verticalalignment":"bottom","horizontalalignment":"right",
"color":"gray","fontsize":self.config.fontsize*0.6}
self._place_symbol_(.9,-.7,string,"xxx",opts)
# ----------------------------------------------------------------
# Placing cloud symbol
# ----------------------------------------------------------------
def _draw_cloudcover_symbol_(self,value):
# In database this shit is in percent Compute octa values.
import numpy as np
value = int(value)
if value > 0:
value = int(np.floor( float(value) / 100. * 8. )) + 1
if value > 8: value = 8
string = "%d" % value ###int(np.round(float(value)/100*8))
opts = {"verticalalignment":"center","horizontalalignment":"center"}
self._place_symbol_(0,0,string,"cc",opts)
def _draw_cloudcover_noobs_(self):
opts = {"verticalalignment":"center","horizontalalignment":"center","color":"gray"}
self._place_symbol_(0,0,"X","xxx",opts)
def _draw_cloudhigh_symbol_(self,value):
string = "%d" % int(value)
opts = {"verticalalignment":"center","horizontalalignment":"center"}
self._place_symbol_(0,.8,string,"ch",opts)
def _draw_cloudmid_symbol_(self,value):
string = "%d" % int(value)
opts = {"verticalalignment":"center","horizontalalignment":"center"}
self._place_symbol_(0,.6,string,"cm",opts)
def _draw_cloudlow_symbol_(self,value):
string = "%d" % int(value)
opts = {"verticalalignment":"center","horizontalalignment":"center"}
self._place_symbol_(0,-.8,string,"cl",opts)
# ----------------------------------------------------------------
# Current weather
# ----------------------------------------------------------------
def _draw_currentweather_symbol_(self,value):
string = "%02d" % int(value)
opts = {"verticalalignment":"center","horizontalalignment":"center"}
# No reported weather
if value == 508: return
self._place_symbol_(-.8,0,string,"ww",opts)
# Setting background color if needed
font_color,bg_color = self._currentweather_get_colors_("%d" % value)
if bg_color is not None:
import matplotlib.patches as patches
self.ax.add_patch( patches.Rectangle((-1,-1), 2, 2, \
facecolor=bg_color, linewidth=0) )
# ----------------------------------------------------------------
# Draw temperature and dew point temperature
# ----------------------------------------------------------------
def _draw_temperature_value_(self,value):
import numpy as np
value = float(value)/10
string = ["%d" % np.floor(value),"%d" % np.round(value*10-(np.floor(value)*10))]
opts = {"verticalalignment":"bottom","horizontalalignment":"left"}
self._place_symbol_(-.9,.55,string,"t",opts)
def _draw_dewpoint_value_(self,value):
import numpy as np
value = float(value)/10
string = ["%d" % np.floor(value),"%d" % np.round(value*10-(np.floor(value)*10))]
opts = {"verticalalignment":"bottom","horizontalalignment":"left"}
self._place_symbol_(-.9,-.95,string,"td",opts)
# ----------------------------------------------------------------
# Current pressure value
# ----------------------------------------------------------------
def _draw_pressure_value_(self,value):
import numpy as np
string = "%d" % np.round(float(value)/10.)
string = string[-3:]
opts = {"verticalalignment":"bottom","horizontalalignment":"right"}
self._place_symbol_(.9,.55,string,"pmsl",opts)
def _draw_pressurechange_value_(self,value):
import numpy as np
string = "%03d" % np.round(float(value)/10.)
string = string[-2:]
opts = {"verticalalignment":"top","horizontalalignment":"right",
"fontsize":float(self.config.fontsize)*0.5}
self._place_symbol_(.9,.55,string,"pch",opts)
def _draw_pressurechange_symbol_(self,value):
import numpy as np
string = "%d" % value
opts = {"verticalalignment":"center","horizontalalignment":"center"}
self._place_symbol_(.8,0,string,"ptend",opts)
# ----------------------------------------------------------------
# Used for current weather. Returns (depending on the value)
# font color, and background color. Or None,None if not needed.
# ----------------------------------------------------------------
def _currentweather_get_colors_(self,orig_string):
# Default no color
font_color = None
bg_color = None
# Yellow for fog
if orig_string[0] == "4" or orig_string in ["10","11","12","28"]:
font_color = "#aca20d"
bg_color = "#f2f1da"
# Green for rain
elif orig_string[0] in ["5","6","7"] or orig_string in ["20","21","22","23"]:
font_color = "#009933"
bg_color = "#d9efe0"
return font_color,bg_color
# ----------------------------------------------------------------
# Helper function to place test/symbols (depending on the font
# it will be either a string (text) or a symbol (as we are using
# special fonts to draw the symbols).
# ----------------------------------------------------------------
def _place_symbol_(self,x,y,string,what,opts):
# First setting default font options and overwrite with the
# ones specified by the user. If set.
fontopts = dict(verticalalignment='top',
horizontalalignment='center',
color="black",fontsize=self.config.fontsize)
for key in fontopts.keys():
if key in opts: fontopts[key] = opts[key]
from matplotlib.font_manager import FontProperties
font = FontProperties()
font.set_style('normal')
font.set_size( fontopts['fontsize'] )
# For current weather we have to "select" the font
if what == "ww":
# We need a font called wX where X is the first character
# of the string we got. If not existing, return.
orig_string = string
what = "w%1s" % string[0]
string = "%1s" % string[1]
if not what in self.config.fonts: return
fontopts['color'] = self.config.fonts[what]['color']
font.set_file(self.config.fonts[what]['font'])
font_color,bg_color = self._currentweather_get_colors_(orig_string)
if font_color is not None:
fontopts['color'] = font_color
elif what in self.config.fonts:
fontopts['color'] = self.config.fonts[what]['color']
font.set_file(self.config.fonts[what]['font'])
else:
font.set_family("monospace")
# - Draw symbol
self.test = []
if type(string) == type(list()):
self.ax.text(x,y,string[0],fontdict=fontopts,fontproperties=font)
# Rendering text, compute offset for superscript
import matplotlib
bb = matplotlib.textpath.TextPath((0,0), string[0],
size=fontopts['fontsize'])
bb = bb.get_extents()
x2 = x+bb.width/self.config.dpi*4.5
###########x2 = x + 0.1*len(string[0])
font.set_size( font.get_size() * 0.7 )
self.ax.text(x2,y,string[1],fontdict=fontopts,fontproperties=font)
else:
self.ax.text(x,y,string,fontdict=fontopts,fontproperties=font)
# ----------------------------------------------------------------
# Initializes new matplotlib figure
# ----------------------------------------------------------------
# ----------------------------------------------------------------
# Saving figure
# ----------------------------------------------------------------
# ----------------------------------------------------------------
# Copy to current (last produced == current synop)
# ----------------------------------------------------------------
def _create_current_(self,file):
import re
# Searching for this string. Only valid if it occurring
# once, and only once!
m = re.findall(".*(synop_[0-9]{8}_[0-9]{4}_).*",file)
if not len(m) == 1:
log.warning("Cannot create link - could not find proper string")
return
new = file.replace(m[0],"synop_current_")
log.info("Create copy from %s to %s" % (file,new))
import shutil
shutil.copy(file,new)
# ----------------------------------------------------------------
# Simple exit handler
# ----------------------------------------------------------------
def exit( self, msg, level=9 ):
log.error(msg); import sys; sys.exit(level)