from __future__ import print_function
__all__ = ['mapplot', 'profile', 'tileplot',
'presslon', 'presslat', 'timeseries', 'OptionDict']
from warnings import warn
import os
import numpy as np
import PseudoNetCDF.coordutil as cu
_pre_code = 'plt.close(); plt.rcParams["image.cmap"] = "jet"'
_before_code = 'plt.figure(); plt.interactive(True); '
_after_code = 'ax.set_title(varkey); plt.draw()'
_post_code = ''
_coastlines_opt = True
_countries_opt = True
_states_opt = True
_counties_opt = False
_coordkeys = ('time', 'latitude', 'longitude', 'latitude_bounds',
'longitude_bounds', 'time_bounds', 'tau0', 'tau1',
'TFLAG')
[docs]
class OptionDict(dict):
def __init__(self, *args, **kwds):
"""
coastlines = %(_coastlines_opt)s
countries = %(_countries_opt)s
states = %(_states_opt)s
counties = %(_counties_opt)s
pre_txt = Code to run before any plots (%(_pre_code)s)
before_txt = Code to run before each variable-plot (%(_before_code)s)
after_txt = Code to run after each plot (%(_after_code)s)
post_txt = Code to run after all plots (%(_post_code)s)
""" % globals()
dict.__init__(self, coastlines=_coastlines_opt,
countries=_countries_opt, states=_states_opt,
counties=_counties_opt, pre_txt=_pre_code,
before_txt=_before_code, after_txt=_after_code,
post_txt=_post_code)
dict.__init__(self, *args, **kwds)
if 'outpath' not in self:
raise KeyError('outpath is a required option')
def __getattr__(self, key):
return self.get(key, False)
defaultoption = OptionDict(outpath='pncview')
class TkApp:
def __init__(self, ncffile, options):
try:
from Tkinter import Checkbutton, Frame, Label, Scrollbar
from Tkinter import Listbox, Button, IntVar, Tk, VERTICAL
from Tkinter import EXTENDED, END, N, S, SINGLE, Entry
from Tkinter import StringVar, Text, DISABLED, LEFT
except Exception:
try:
from tkinter import Checkbutton, Frame, Label, Scrollbar
from tkinter import Listbox, Button, IntVar, Tk, VERTICAL
from tkinter import EXTENDED, END, N, S, SINGLE, Entry
from tkinter import StringVar, Text, DISABLED, LEFT
except Exception:
warn('tkinter unavailable')
master = self.root = Tk()
self.ncffile = ncffile
self.options = options
self.plotted_variables = set()
frame = Frame(master)
frame.grid(row=0)
codeframe = Frame(master)
codeframe.grid(row=1)
metaframe = Frame(master)
metaframe.grid(row=2)
goframe = Frame(frame)
goframe.grid(column=3, row=1)
var_label = Label(frame, text='Select Variable')
var_label.grid(column=0, row=0)
var_scrollbar = Scrollbar(frame, orient=VERTICAL)
var_scrollbar.grid(column=1, row=1, sticky=N + S)
self.var = Listbox(frame, selectmode=EXTENDED,
exportselection=0,
yscrollcommand=var_scrollbar.set)
self.var.grid(column=0, row=1)
var_scrollbar.config(command=self.var.yview)
what_to_do = Label(frame, text='Execute')
what_to_do.grid(column=2, row=0)
self.method_list = Listbox(frame, selectmode=SINGLE, exportselection=0)
self.method_list.grid(column=2, row=1)
self.pre_txt = StringVar()
pre_label = Label(codeframe, text='Before any figures, execute code')
self.pre_txt.set(_pre_code)
pre_label.grid(row=2, sticky='W')
self.pre = Entry(codeframe, width=120, textvariable=self.pre_txt)
self.pre.grid(row=3, sticky='E')
self.before_txt = StringVar()
self.before_txt.set(_before_code)
before_label = Label(
codeframe, text='Before each figure, execute code')
before_label.grid(row=4, sticky='W')
self.before = Entry(codeframe, width=120, textvariable=self.before_txt)
self.before.grid(row=5, sticky='E')
self.after_txt = StringVar()
self.after_txt.set(_after_code)
after_label = Label(codeframe, text='After each figure, execute code')
after_label.grid(row=6, sticky='W')
self.after = Entry(codeframe, width=120, textvariable=self.after_txt)
self.after.grid(row=7, sticky='E')
self.post_txt = StringVar()
self.post_txt.set(_post_code)
post_label = Label(codeframe, text='After all figures, execute code')
post_label.grid(row=8, sticky='W')
self.post = Entry(codeframe, width=120, textvariable=self.post_txt)
self.post.grid(row=9, sticky='E')
options_label = Label(goframe, text='Options:')
options_label.grid(column=0, row=1, sticky='W')
self.logscale = IntVar()
self.logscale.set(0)
c = Checkbutton(goframe, text="log-scale?", variable=self.logscale)
c.grid(column=0, row=2, sticky='W')
self.coastlines = IntVar()
self.coastlines.set(_coastlines_opt)
coastlines = Checkbutton(
goframe, text="coastlines?", variable=self.coastlines,
justify=LEFT)
coastlines.grid(column=0, row=3, sticky='W')
self.countries = IntVar()
self.countries.set(_countries_opt)
countries = Checkbutton(
goframe, text="countries?", variable=self.countries, justify=LEFT)
countries.grid(column=0, row=4, sticky='W')
self.states = IntVar()
self.states.set(_states_opt)
states = Checkbutton(goframe, text="states?",
variable=self.states, justify=LEFT)
states.grid(column=0, row=5, sticky='W')
self.counties = IntVar()
self.counties.set(_counties_opt)
counties = Checkbutton(goframe, text="counties?",
variable=self.counties, justify=LEFT)
counties.grid(column=0, row=6, sticky='W')
self.execute_button = Button(
goframe, text="Make Figure", command=self.execute)
self.execute_button.grid(row=0, column=0, sticky='W')
self.methods = ['mapplot', 'presslat', 'presslon', 'time-lat',
'profile', 'timeseries', 'pressx', 'tileplot', 'plot']
method_labels = ['lat-lon', 'press-lat', 'press-lon', 'time-lat',
'Vertical Profile', 'Time Series', 'press-? (2-D)',
'Tile Plot (2-D)', 'Plot (1-D)']
for method in method_labels:
self.method_list.insert(END, method)
var_keys = [_k for _k, v in self.ncffile.variables.items()
if _k not in _coordkeys]
var_keys.sort()
self.vars = []
for spc in var_keys:
self.var.insert(END, spc)
self.vars.append(spc)
meta_label = Label(metaframe, text='Common Data Language Header:')
meta_label.grid(column=0, row=0, sticky='W')
meta_scrollbar = Scrollbar(metaframe, orient=VERTICAL)
meta_scrollbar.grid(column=1, row=1, sticky=N + S)
self.meta = Text(metaframe, height=10, width=118, bg='white',
relief='flat', yscrollcommand=meta_scrollbar.set)
self.meta.grid(column=0, row=1, sticky='W')
from PseudoNetCDF.pncdump import pncdump
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
pdump = StringIO("")
pncdump(self.ncffile, header=True, outfile=pdump, )
pdump.seek(0, 0)
self.meta.insert(END, pdump.read())
self.meta.config(state=DISABLED)
help = Button(goframe, text='Help', command=self.help)
help.grid(column=0, row=7)
quit = Button(goframe, text='Quit', command=self.quit)
quit.grid(column=0, row=8)
master.mainloop()
def help(self):
print("pl is pylab: details at matplotlib;")
def quit(self):
self.root.destroy()
def _get_var(self, list):
items = list.curselection()
try:
items = map(int, items)
except Exception:
pass
items = [self.vars[i] for i in items]
return items
def get_var(self):
return self._get_var(self.var)
def get_methods(self):
items = self.method_list.curselection()
try:
items = map(int, items)
except Exception:
pass
items = [self.methods[i] for i in items]
return items
def execute(self):
os.system('clear')
vars = self.get_var()
self.plotted_variables = self.plotted_variables.union(vars)
methods, = self.get_methods()
self.options.logscale = bool(self.logscale.get())
self.options.coastlines = bool(self.coastlines.get())
self.options.countries = bool(self.countries.get())
self.options.states = bool(self.states.get())
self.options.counties = bool(self.counties.get())
self.options.pre_txt = self.pre_txt.get()
self.options.before_txt = self.before_txt.get()
self.options.after_txt = self.after_txt.get()
self.options.post_txt = self.post_txt.get()
plotwithopts(self.ncffile, methods, vars, self.options)
def plotwithopts(ifile, method, vars, options=defaultoption):
from PseudoNetCDF.sci_var import getvarpnc
from PseudoNetCDF.pncgen import pncgen
import matplotlib.pyplot as plt
# dummy assignment so that flake8 sees plt as used
# plt is loaded into the environment for exec
varkey = plt
exec(options.pre_txt)
for varkey in vars:
figpath = eval(method)(ifile=ifile, varkey=varkey, options=options,
before=options.before_txt,
after=options.after_txt)
pncgen(getvarpnc(ifile, list(vars) + list(_coordkeys)),
figpath + '.nc', verbose=0)
exec(options.post_txt)
def StartTk(ncffile, options):
TkApp(ncffile, options)
def pncview(ifile, options):
# add a gui for plotting
return StartTk(ifile, options)
def gettime(ifile):
from PseudoNetCDF import PseudoNetCDFVariable
from datetime import datetime, timedelta
if 'time' in ifile.variables:
time = ifile.variables['time']
unit = time.units
tunit, datestr = unit.split(' since ')
sdate = datetime.strptime(datestr.replace(
' UTC', ''), '%Y-%m-%d %H:%M:%S')
time = np.array([sdate + timedelta(**{tunit: t}) for t in time[:]])
unit = 'time'
elif 'TFLAG' in ifile.variables:
x = ifile.variables['TFLAG'][:].copy()
for axi in range(1, x.ndim - 1):
x = x[:, 0]
time = np.array(
[(datetime.strptime('%07d %06d' % (d, t), '%Y%j %H%M%S'))
for d, t in x])
unit = 'time'
elif 'time' in ifile.dimensions:
time = np.arange(len(ifile.dimensions['time']))
unit = 'steps'
elif 'TSTEP' in ifile.dimensions:
time = np.arange(1, len(ifile.dimensions['TSTEP']) + 1)
unit = 'steps'
else:
raise KeyError('No time found')
return PseudoNetCDFVariable(None, 'time', 'f', ('time',),
values=time[:], units=unit)
[docs]
def timeseries(ifile, varkey, options, before='', after=''):
import pylab as pl
outpath = getattr(options, 'outpath', '.')
time = gettime(ifile)
var = ifile.variables[varkey]
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 1:
raise ValueError(
'Time series can have 1 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
exec(before)
ax = pl.gca()
print(varkey, end='')
if options.logscale:
ax.set_yscale('log')
ax.plot_date(time[:].squeeze(), var[:].squeeze())
ax.set_xlabel(time.units.strip())
ax.set_ylabel(getattr(var, 'standard_name',
varkey).strip() + ' ' + var.units.strip())
fmt = 'png'
figpath = os.path.join(outpath + '_ts_' + varkey + '.' + fmt)
exec(after)
pl.savefig(figpath)
print('Saved fig', figpath)
return figpath
def plot(ifile, varkey, options, before='', after=''):
import pylab as pl
outpath = getattr(options, 'outpath', '.')
var = ifile.variables[varkey]
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 1:
raise ValueError(
'Plots can have only 1 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
exec(before)
ax = pl.gca()
print(varkey, end='')
if options.logscale:
ax.set_yscale('log')
ax.plot(var[:].squeeze())
ax.set_xlabel('unknown')
ax.set_ylabel(getattr(var, 'standard_name',
varkey).strip() + ' ' + var.units.strip())
fmt = 'png'
figpath = os.path.join(outpath + '_1d_' + varkey + '.' + fmt)
exec(after)
pl.savefig(figpath)
print('Saved fig', figpath)
return figpath
def pressx(ifile, varkey, options, before='', after=''):
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LogNorm
outpath = getattr(options, 'outpath', '.')
vert = cu.getpresbnds(ifile)
var = ifile.variables[varkey]
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 2:
raise ValueError(
'Press-x can have 2 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
if options.logscale:
norm = LogNorm()
else:
norm = Normalize()
exec(before)
ax = plt.gca()
print(varkey, end='')
vals = var[:].squeeze()
x = np.arange(vals.shape[1])
patches = ax.pcolor(x, vert, vals, norm=norm)
# ax.set_xlabel(X.units.strip())
# ax.set_ylabel(Y.units.strip())
plt.colorbar(patches)
ax.set_ylim(vert.max(), vert.min())
ax.set_xlim(x.min(), x.max())
fmt = 'png'
figpath = os.path.join(outpath + '_PRESX_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
[docs]
def presslat(ifile, varkey, options, before='', after=''):
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LogNorm
outpath = getattr(options, 'outpath', '.')
vert = cu.getpresbnds(ifile)
lat, latunit = cu.getlatbnds(ifile)
lat = np.append(lat.squeeze()[..., :2].mean(
1), lat.squeeze()[-1, 2:].mean(0))
var = ifile.variables[varkey]
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 2:
raise ValueError(
'Press-lat can have 2 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
if options.logscale:
norm = LogNorm()
else:
norm = Normalize()
exec(before)
ax = plt.gca()
print(varkey, end='')
patches = ax.pcolor(lat, vert, var[:].squeeze(), norm=norm)
# ax.set_xlabel(X.units.strip())
# ax.set_ylabel(Y.units.strip())
cbar = plt.colorbar(patches)
vunit = getattr(var, 'units', 'unknown').strip()
cbar.set_label(varkey + ' (' + vunit + ')')
cbar.ax.text(.5, 1, '%.2g' % var[:].max(
), horizontalalignment='center', verticalalignment='bottom')
cbar.ax.text(.5, 0, '%.2g' % var[:].min(
), horizontalalignment='center', verticalalignment='top')
ax.set_ylim(vert.max(), vert.min())
ax.set_xlim(lat.min(), lat.max())
fmt = 'png'
figpath = os.path.join(outpath + '_PRESSLAT_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
[docs]
def presslon(ifile, varkey, options, before='', after=''):
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LogNorm
outpath = getattr(options, 'outpath', '.')
vert = cu.getpresbnds(ifile)
lon, lonunit = cu.getlonbnds(ifile)
lon = np.append(lon.squeeze()[..., [0, 3]].mean(
1), lon.squeeze()[-1, [1, 2]].mean(0))
var = ifile.variables[varkey]
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 2:
raise ValueError(
'Press-lon plots can have 2 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
if options.logscale:
norm = LogNorm()
else:
norm = Normalize()
exec(before)
ax = plt.gca()
print(varkey, end='')
patches = ax.pcolor(lon, vert, var[:].squeeze(), norm=norm)
# ax.set_xlabel(X.units.strip())
# ax.set_ylabel(Y.units.strip())
cbar = plt.colorbar(patches)
vunit = getattr(var, 'units', 'unknown').strip()
cbar.set_label(varkey + ' (' + vunit + ')')
cbar.ax.text(.5, 1, '%.2g' % var[:].max(
), horizontalalignment='center', verticalalignment='bottom')
cbar.ax.text(.5, 0, '%.2g' % var[:].min(
), horizontalalignment='center', verticalalignment='top')
ax.set_ylim(vert.max(), vert.min())
ax.set_xlim(lon.min(), lon.max())
fmt = 'png'
figpath = os.path.join(outpath + '_PRESLON_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
[docs]
def tileplot(ifile, varkey, options, before='', after=''):
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LogNorm
outpath = getattr(options, 'outpath', '.')
var = ifile.variables[varkey]
if options.logscale:
norm = LogNorm()
else:
norm = Normalize()
exec(before)
ax = plt.gca()
print(varkey, end='')
dims = [(_k, _l) for _l, _k in zip(var[:].shape, var.dimensions) if _l > 1]
if len(dims) > 2:
raise ValueError(
'Tile plots can have 2 non-unity dimensions; got %d - %s' %
(len(dims), str(dims)))
patches = ax.pcolor(var[:].squeeze(), norm=norm)
ax.set_xlim(0, var[:].squeeze().shape[1])
ax.set_ylim(0, var[:].squeeze().shape[0])
ax.set_xlabel(dims[1][0])
ax.set_ylabel(dims[0][0])
# ax.set_xlabel(X.units.strip())
# ax.set_ylabel(Y.units.strip())
cbar = plt.colorbar(patches)
vunit = getattr(var, 'units', 'unknown').strip()
cbar.set_label(varkey + ' (' + vunit + ')')
cbar.ax.text(.5, 1, '%.2g' % var[:].max(
), horizontalalignment='center', verticalalignment='bottom')
cbar.ax.text(.5, 0, '%.2g' % var[:].min(
), horizontalalignment='center', verticalalignment='top')
fmt = 'png'
figpath = os.path.join(outpath + '_2D_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
def minmaxmean(ax, vals, vertcrd, **kwds):
minval = vals.min(1)
meanval = vals.mean(1)
maxval = vals.max(1)
kwds.setdefault('edgecolor', '_k')
kwds.setdefault('facecolor', 'grey')
kwds.setdefault('alpha', 1)
kwds.setdefault('ls', 'solid')
linekwds = kwds.copy()
linekwds['color'] = linekwds.pop('edgecolor')
linekwds.pop('facecolor')
linekwds.pop('alpha')
linekwds.pop('ls')
fillkwds = kwds.copy()
line, = ax.plot(meanval, vertcrd, **linekwds)
x = np.ma.concatenate([minval[:vertcrd.size], maxval[:vertcrd.size][::-1]])
y = np.ma.concatenate([vertcrd[:], vertcrd[::-1]])
mask = x.mask | y.mask
x = np.ma.masked_where(mask, x).compressed()
y = np.ma.masked_where(mask, y).compressed()
range, = ax.fill(x, y, **fillkwds)
return line, range
[docs]
def profile(ifile, varkey, options, before='', after=''):
import matplotlib.pyplot as plt
print(varkey, end='')
outpath = getattr(options, 'outpath', '.')
try:
vert = cu.getpresmid(ifile)
vunit = 'Pa'
except Exception:
vert = cu.getsigmamid(ifile)
vunit = r'\sigma'
var = ifile.variables[varkey]
dims = list(var.dimensions)
for knownvert in ['layer', 'LAY'] + ['layer%d' % i for i in range(72)]:
if knownvert in dims:
vidx = dims.index(knownvert)
break
else:
raise KeyError("No known vertical coordinate; got %s" % str(dims))
vert = vert[:var[:].shape[vidx]]
units = var.units.strip()
vals = np.rollaxis(var[:], vidx, start=0).view(
np.ma.MaskedArray).reshape(vert.size, -1)
ax = plt.gca()
minmaxmean(ax, vals, vert)
ax.set_xlabel(varkey + ' (' + units + ')')
ax.set_ylabel(vunit)
ax.set_ylim(vert.max(), vert.min())
if options.logscale:
ax.set_xscale('log')
fmt = 'png'
figpath = os.path.join(outpath + '_profile_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
[docs]
def mapplot(ifile, varkey, options, before='', after=''):
"""
ifile - a pseudonetcdf file
varkey - the variable to plot
options - argparse name space with mapping options
"""
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LogNorm
outpath = getattr(options, 'outpath', '.')
map = cu.getmap(ifile)
if map.projection == 'cyl':
latb, latunit = cu.getlatbnds(ifile)[:]
lonb, lonunit = cu.getlonbnds(ifile)[:]
else:
latb, latunit = cu.getybnds(ifile)[:]
lonb, lonunit = cu.getxbnds(ifile)[:]
if latb.ndim == lonb.ndim and lonb.ndim == 2:
LON, LAT = lonb, latb
else:
LON, LAT = np.meshgrid(lonb, latb)
ax = plt.gca()
if options.logscale:
norm = LogNorm()
else:
norm = Normalize()
var = ifile.variables[varkey]
exec(before)
ax = plt.gca()
vunit = getattr(var, 'units', 'unknown').strip()
print(varkey, end='')
try:
if options.coastlines:
map.drawcoastlines(ax=ax)
if options.countries:
map.drawcountries(ax=ax)
if options.states:
map.drawstates(ax=ax)
if options.counties:
map.drawcounties(ax=ax)
except Exception:
print('nomap')
pass
patches = map.pcolor(LON, LAT, var[:].squeeze(), norm=norm, ax=ax)
if lonunit == 'x (LCC m)':
ax.xaxis.get_major_formatter().set_scientific(True)
ax.xaxis.get_major_formatter().set_powerlimits((-3, 3))
if latunit == 'y (LCC m)':
ax.yaxis.get_major_formatter().set_scientific(True)
ax.yaxis.get_major_formatter().set_powerlimits((-3, 3))
ax.set_xlabel(lonunit)
ax.set_ylabel(latunit)
height = LAT.max() - LAT.min()
width = LON.max() - LON.min()
if width > height:
orientation = 'horizontal'
else:
orientation = 'vertical'
cbar = plt.gcf().colorbar(patches, orientation=orientation)
cbar.set_label(varkey + ' (' + vunit + ')')
if orientation == 'vertical':
cbar.ax.text(.5, 1, '%.2g' % var[:].max(
), horizontalalignment='center', verticalalignment='bottom')
cbar.ax.text(.5, 0, '%.2g' % var[:].min(
), horizontalalignment='center', verticalalignment='top')
else:
cbar.ax.text(1, .5, '%.2g' % var[:].max(
), verticalalignment='center', horizontalalignment='left')
cbar.ax.text(0, .5, '%.2g' % var[:].min(
), verticalalignment='center', horizontalalignment='right')
try:
cbar.formatter.set_scientific(True)
cbar.formatter.set_powerlimits((-3, 3))
except Exception:
pass
cbar.update_ticks()
fmt = 'png'
figpath = os.path.join(outpath + '_map_' + varkey + '.' + fmt)
exec(after)
plt.savefig(figpath)
print('Saved fig', figpath)
return figpath
def main():
import matplotlib.pyplot as plt
from .pncparse import pncparse
ifiles, options = pncparse(
has_ofile=True, plot_options=True, interactive=False)
if len(ifiles) != 1:
raise IOError(
'pncview can operate on only 1 file; user requested %d' %
len(ifiles))
ifile, = ifiles
plt.interactive(True)
for method_vars in options.plotcommands:
pieces = method_vars.split(',')
plotargs = [p for p in pieces if '=' not in p]
plotkwds = [p for p in pieces if '=' in p]
method, = plotargs[:1]
vars = plotargs[1:]
plotoptions = eval('OptionDict(outpath="%s",%s)' %
(options.outpath, ','.join(plotkwds)))
print(plotoptions.logscale)
plotwithopts(ifile, method, vars, plotoptions)
plt.interactive(False)
if len(options.plotcommands) == 0:
pncview(ifile, options)
if __name__ == '__main__':
main()