diff --git a/Packages/vcs/vcs/Canvas.py b/Packages/vcs/vcs/Canvas.py index 4fe29ea2b5..903e22a7a0 100644 --- a/Packages/vcs/vcs/Canvas.py +++ b/Packages/vcs/vcs/Canvas.py @@ -68,7 +68,6 @@ plot_2_1D_options gui_canvas_closed = 0 canvas_closed = 0 -import vcsaddons # noqa import vcs.manageElements # noqa import configurator # noqa from projection import no_deformation_projections # noqa @@ -127,7 +126,7 @@ def dictionarytovcslist(dictionary, name): def _determine_arg_list(g_name, actual_args): "Determine what is in the argument list for plotting graphics methods" - + import vcsaddons itemplate_name = 2 igraphics_method = 3 igraphics_option = 4 @@ -2449,14 +2448,15 @@ def __new_elts(self, original, new): return new def __plot(self, arglist, keyargs): + import vcsaddons - # This routine has five arguments in arglist from _determine_arg_list - # It adds one for bg and passes those on to Canvas.plot as its sixth - # arguments. + # This routine has five arguments in arglist from _determine_arg_list + # It adds one for bg and passes those on to Canvas.plot as its sixth + # arguments. - # First of all let's remember which elets we have before comin in here - # so that anything added (temp objects) can be removed at clear - # time + # First of all let's remember which elets we have before comin in here + # so that anything added (temp objects) can be removed at clear + # time original_elts = {} new_elts = {} for k in vcs.elements.keys(): @@ -3493,9 +3493,14 @@ def set_convert_labels(copy_mthd, test=0): tp = "boxfill" elif tp in ("xvsy", "xyvsy", "yxvsx", "scatter"): tp = "1d" - gm = vcs.elements[tp][arglist[4]] + if tp in vcsaddons.gms: + gm = vcsaddons.gms[tp][arglist[4]] + arglist[3] = gm + else: + gm = vcs.elements[tp][arglist[4]] if hasattr(gm, "priority") and gm.priority == 0: return + p = self.getprojection(gm.projection) if p.type in no_deformation_projections and ( doratio == "0" or doratio[:4] == "auto"): @@ -3710,20 +3715,22 @@ def set_convert_labels(copy_mthd, test=0): del(keyargs["bg"]) if isinstance(arglist[3], vcsaddons.core.VCSaddon): if arglist[1] is None: - dn = arglist[3].plot( + dn = arglist[3].plot_internal( arglist[0], template=arglist[2], bg=bg, x=self, **keyargs) else: - dn = arglist[3].plot( + dn = arglist[3].plot_internal( arglist[0], arglist[1], template=arglist[2], bg=bg, x=self, **keyargs) + self.display_names.append(dn.name) + return dn else: returned_kargs = self.backend.plot(*arglist, **keyargs) if not keyargs.get("donotstoredisplay", False): @@ -4913,7 +4920,6 @@ def _compute_width_height(self, width, height, units, ps=False): H = tmp return W, H - def postscript(self, file, mode='r', orientation=None, width=None, height=None, units='inches', textAsPaths=True): """ diff --git a/Packages/vcs/vcs/displayplot.py b/Packages/vcs/vcs/displayplot.py index 1f00450ba1..80638f2260 100755 --- a/Packages/vcs/vcs/displayplot.py +++ b/Packages/vcs/vcs/displayplot.py @@ -209,9 +209,10 @@ def _getg_type(self): return self._g_type def _setg_type(self, value): + import vcsaddons value = VCS_validation_functions.checkString(self, 'g_type', value) value = value.lower() - if value not in vcs.elements and value != "text": + if value not in vcs.elements and value != "text" and value not in vcsaddons.gms: raise ValueError( "invalid g_type '%s' must be one of: %s " % (value, vcs.elements.keys())) @@ -259,6 +260,7 @@ def __init__(self, Dp_name, Dp_name_src='default', parent=None): self._g_name = "default" self._array = [] self._continents = 1 + self._continents_line = "default" self.ratio = None else: src = vcs.elements["display"][Dp_name_src] @@ -269,6 +271,7 @@ def __init__(self, Dp_name, Dp_name_src='default', parent=None): self.g_type = src.g_type self.g_name = src.g_name self.continents = src.continents + self.continents_line = src.continents_line self.priority = src.priority self.ratio = src.ratio diff --git a/Packages/vcs/vcs/dv3d.py b/Packages/vcs/vcs/dv3d.py index 2afae29f2c..4a30aed8e6 100644 --- a/Packages/vcs/vcs/dv3d.py +++ b/Packages/vcs/vcs/dv3d.py @@ -158,7 +158,6 @@ def __init__(self, Gfdv3d_name, Gfdv3d_name_src='default'): self.plot_attributes['name'] = self.g_name self.plot_attributes['template'] = Gfdv3d_name - def setProvenanceHandler(self, provenanceHandler): self.provenanceHandler = provenanceHandler diff --git a/Packages/vcs/vcs/queries.py b/Packages/vcs/vcs/queries.py index dc4ffa418c..3974ff90a8 100644 --- a/Packages/vcs/vcs/queries.py +++ b/Packages/vcs/vcs/queries.py @@ -44,7 +44,6 @@ import displayplot import projection import vcs -import vcsaddons from error import vcsError @@ -69,6 +68,7 @@ def isgraphicsmethod(gobj): if vcs.isgraphicsmethod(box): box.list() """ + import vcsaddons if (isinstance(gobj, boxfill.Gfb)): return 1 elif (isinstance(gobj, isofill.Gfi)): @@ -134,6 +134,7 @@ def graphicsmethodtype(gobj): print vcs.graphicsmethodtype(ln) # Will print None, because ln is not a # graphics method """ + import vcsaddons if (isinstance(gobj, boxfill.Gfb)): return 'boxfill' elif (isinstance(gobj, isofill.Gfi)): diff --git a/Packages/vcs/vcs/utils.py b/Packages/vcs/vcs/utils.py index db4db640e5..fa72c9254b 100644 --- a/Packages/vcs/vcs/utils.py +++ b/Packages/vcs/vcs/utils.py @@ -20,7 +20,6 @@ import json import os import tempfile -import vcsaddons import cdms2 import genutil import vtk @@ -1616,6 +1615,7 @@ def monotonic(x): def getgraphicsmethod(type, name): + import vcsaddons if type == "default": type = "boxfill" if isinstance(type, vcsaddons.core.VCSaddon): @@ -1630,6 +1630,7 @@ def getgraphicsmethod(type, name): def creategraphicsmethod(gtype, gname='default', name=None): + import vcsaddons if gtype in ['isoline', 'Gi']: func = vcs.createisoline elif gtype in ['isofill', 'Gfi']: diff --git a/Packages/vcs/vcs/vcs2vtk.py b/Packages/vcs/vcs/vcs2vtk.py index 686b1a6774..d07ca44cc2 100644 --- a/Packages/vcs/vcs/vcs2vtk.py +++ b/Packages/vcs/vcs/vcs2vtk.py @@ -1823,10 +1823,10 @@ def generateVectorArray(data1, data2, vtk_grid): def vtkIterate(iterator): iterator.InitTraversal() - obj = iterator.GetNextItem() + obj = iterator.GetNextItemAsObject() while obj is not None: yield obj - obj = iterator.GetNextItem() + obj = iterator.GetNextItemAsObject() def getPlottingBounds(gmbounds, databounds, geo): diff --git a/Packages/vcsaddons/Lib/__init__.py b/Packages/vcsaddons/Lib/__init__.py index eadaa435c1..7136d36aa3 100644 --- a/Packages/vcsaddons/Lib/__init__.py +++ b/Packages/vcsaddons/Lib/__init__.py @@ -1,12 +1,129 @@ gms = {} import histograms +import polar import EzTemplate import yxvsxfill import continents +import vcs + def createyxvsxfill(name=None,source='default',x=None,template=None): return yxvsxfill.Gyf(name,source=source,x=x,template=template) + + def createhistogram(name=None,source='default',x=None,template=None): return histograms.Ghg(name,source=source,x=x,template=template) + + def createusercontinents(name=None,source="default",x=None,template=None): return continents.Guc(name,source=source,x=x,template=template) + + +def createpolar(name=None, source="default", x=None, template=None): + if "polar_oned" not in gms: + init_polar() + return polar.Gpo(name, source=source, x=x, template=template) + + +def getpolar(name=None): + if "polar_oned" not in gms: + init_polar() + if name in gms["polar_oned"]: + return gms["polar_oned"][name] + raise KeyError("No Polar GM exists with name '%s'" % name) + + +def init_polar(): + # Create nice polar template + try: + t = vcs.createtemplate("polar_oned") + t.data.x1 = .2 + t.data.x2 = .8 + t.data.y1 = .2 + t.data.y2 = .8 + + t.legend.x1 = .85 + t.legend.x2 = 1 + t.legend.y1 = .15 + t.legend.y2 = .85 + + dash = vcs.createline() + dash.type = "dash" + dot = vcs.createline() + dot.type = "dot" + t.xtic1.line = dash + t.ytic1.line = dot + + left_aligned = vcs.createtextorientation() + left_aligned.halign = "left" + left_aligned.valign = "half" + t.legend.textorientation = left_aligned + except vcs.vcsError: + # Template already exists + pass + # Create some nice default polar GMs + degree_polar = polar.Gpo("degrees", template="polar_oned") + degree_polar.datawc_x1 = 0 + degree_polar.datawc_x2 = 360 + degree_polar.xticlabels1 = { + i: str(i) for i in range(0, 360, 45) + } + + clock_24 = polar.Gpo("diurnal", template="polar_oned") + clock_24.datawc_x1 = 0 + clock_24.datawc_x2 = 24 + clock_24.clockwise = True + # 6 AM on the right + clock_24.theta_offset = -6 + clock_24.xticlabels1 = { + i: str(i) for i in range(0, 24, 3) + } + + clock_24_meridiem = polar.Gpo("diurnal_12_hour", source="diurnal", template="polar_oned") + clock_24_meridiem.xticlabels1 = { + 0: "12 AM", + 3: "3 AM", + 6: "6 AM", + 9: "9 AM", + 12: "12 PM", + 15: "3 PM", + 18: "6 PM", + 21: "9 PM" + } + + clock_12 = polar.Gpo("semidiurnal", source="diurnal", template="polar_oned") + clock_12.datawc_x2 = 12 + clock_12.xticlabels1 = { + i: str(i) for i in range(3, 13, 3) + } + # 3 on the right + clock_12.theta_offset = -3 + + annual_cycle = polar.Gpo("annual_cycle", template="polar_oned") + annual_cycle.datawc_x1 = 1 + annual_cycle.datawc_x2 = 13 + annual_cycle.clockwise = True + annual_cycle.xticlabels1 = { + 1: "Jan", + 2: "Feb", + 3: "Mar", + 4: "Apr", + 5: "May", + 6: "Jun", + 7: "Jul", + 8: "Aug", + 9: "Sep", + 10: "Oct", + 11: "Nov", + 12: "Dec" + } + # Put December on the top + annual_cycle.theta_offset = -2 + + seasonal = polar.Gpo("seasonal", template="polar_oned") + seasonal.datawc_x1 = 0 + seasonal.datawc_x2 = 4 + seasonal.xticlabels1 = {0: "DJF", 1: "MAM", 2: "JJA", 3: "SON"} + seasonal.clockwise = True + # DJF on top + seasonal.theta_offset = -1 diff --git a/Packages/vcsaddons/Lib/core.py b/Packages/vcsaddons/Lib/core.py index 38932c4869..0a75da1257 100644 --- a/Packages/vcsaddons/Lib/core.py +++ b/Packages/vcsaddons/Lib/core.py @@ -1,18 +1,18 @@ import vcsaddons,vcs import numpy -class VCSaddon: +class VCSaddon(object): def __init__(self,name=None,source='default',x=None,template=None): self._saves={} self.g_nslabs=1 - if not self.g_name in vcsaddons.gms.keys(): - vcsaddons.gms[self.g_name]={} + if not self.g_type in vcsaddons.gms.keys(): + vcsaddons.gms[self.g_type]={} if name is None: cont = True while cont: num= numpy.random.randint(1000) - nm = 'Ghg_'+str(num) - if not nm in vcsaddons.gms[self.g_name].keys(): + nm = self.g_type + '_'+str(num) + if not nm in vcsaddons.gms[self.g_type].keys(): name = nm cont = False @@ -20,7 +20,7 @@ def __init__(self,name=None,source='default',x=None,template=None): self.x=vcs.init() else: self.x=x - + if template is None: self.template = self.x.gettemplate() elif isinstance(template,str): @@ -30,7 +30,7 @@ def __init__(self,name=None,source='default',x=None,template=None): else: raise "Error did not know what to do with template: %s" % template - if name in vcsaddons.gms[self.g_name].keys(): + if name in vcsaddons.gms[self.g_type].keys(): raise "Error graphic method %s already exists" % name if source=='default': @@ -38,26 +38,33 @@ def __init__(self,name=None,source='default',x=None,template=None): self.datawc_x2=1.e20 self.datawc_y1=1.e20 self.datawc_y2=1.e20 - self.xmtics1='*' - self.xmtics2='*' - self.ymtics1='*' - self.ymtics2='*' + self.colormap="default" + self.xmtics1='' + self.xmtics2='' + self.ymtics1='' + self.ymtics2='' self.xticlabels1='*' self.xticlabels2='*' self.yticlabels1='*' self.yticlabels2='*' self.xaxisconvert= 'linear' self.yaxisconvert= 'linear' + self.color_1 = 16 + self.color_2 = 239 self.legend = None self.projection='linear' else: - gm = vcsaddons.gms[self.g_name].get(source,None) - if gm is None: - raise "error could not find graphic method %s (of type %s)" % (source, self.g_name) + if isinstance(source, (str, unicode)): + gm = vcsaddons.gms[self.g_type].get(source,None) + if gm is None: + raise "error could not find graphic method %s (of type %s)" % (source, self.g_type) + else: + gm = source self.datawc_x1=gm.datawc_x1 self.datawc_x2=gm.datawc_x2 self.datawc_y1=gm.datawc_y1 - self.datawc_y2=gm.datawc_x2 + self.datawc_y2=gm.datawc_y2 + self.colormap=gm.colormap self.xmtics1=gm.xmtics1 self.xmtics2=gm.xmtics2 self.ymtics1=gm.ymtics1 @@ -68,11 +75,13 @@ def __init__(self,name=None,source='default',x=None,template=None): self.yticlabels2=gm.yticlabels2 self.xaxisconvert=gm.xaxisconvert self.yaxisconvert= gm.yaxisconvert + self.color_1 = gm.color_1 + self.color_2 = gm.color_2 self.legend = gm.legend self.projection=gm.projection self.name = name - vcsaddons.gms[self.g_name][name]=self - + vcsaddons.gms[self.g_type][name]=self + def list(self): print 'graphics method = ',self.g_name @@ -97,9 +106,9 @@ def plot(self): raise "Plot function not implemented for graphic method type: %s" % self.g_name def prep_plot(self,xmn,xmx,ymn,ymx): - + self.save() - + if self.datawc_x1!=1.e20: xmn = self.datawc_x1 if self.datawc_x2!=1.e20: @@ -122,6 +131,30 @@ def prep_plot(self,xmn,xmx,ymn,ymx): setattr(self,axes+sec+n,vcs.mklabels(sc)) return xmn,xmx,ymn,ymx + def plot_internal(self, slab=None, slab2=None, template=None, bg=0, x=None, **kwargs): + """ + Used by vcs to properly build a display plot for this graphics method. + """ + if x is None: + x = self.x + + if slab2 is not None: + displays = self.plot(slab, slab2, template, bg, x, **kwargs) + else: + displays = self.plot(slab, template, bg, x, **kwargs) + + for display in displays: + # Remove the display from the canvas + if display.name in x.display_names: + x.display_names.remove(display.name) + nm, src = x.check_name_source(None, "default", "display") + display = vcs.displayplot.Dp(nm) + display.g_name = self.name + display.g_type = self.g_type + display.array = [slab, slab2] + return display + + def save(self,attribute = None): if attribute is not None: self._saves[attribute] = getattr(self,attribute) @@ -139,15 +172,15 @@ def restore(self,cleanup=True): self._saves={} - def getgm(self,name): + def getgm(self,source="default"): gm = None - for nm in vcsaddons.gms[self.g_name].keys(): - if name == nm: - return vcsaddons.gms[self.g_name][nm] + for nm in vcsaddons.gms[self.g_type].keys(): + if source == nm: + return vcsaddons.gms[self.g_type][nm] if gm is None: - raise "Could not find graphic method %s named: %s" % (self.g_type, name) + raise "Could not find graphic method %s named: %s" % (self.g_type, source) def creategm(self,name,source='default'): return self.__init__(name,source=source,x=self.x,template=self.template) - + diff --git a/Packages/vcsaddons/Lib/histograms.py b/Packages/vcsaddons/Lib/histograms.py index 6e974f2a63..0273529cdd 100644 --- a/Packages/vcsaddons/Lib/histograms.py +++ b/Packages/vcsaddons/Lib/histograms.py @@ -1,116 +1,218 @@ from core import VCSaddon -import cdms2,MV2,vcs,vcsaddons +import cdms2 +import MV2 +import numpy +import vcs +import vcsaddons + class Ghg(VCSaddon): - def __init__(self,name=None,source='default',x=None,template = None): - self.g_name='Ghg' - self.g_type='histogram' - VCSaddon.__init__(self,name,source,x,template) + + def __init__(self, name=None, source='default', x=None, template=None): + self.g_name = 'Ghg' + self.g_type = 'histogram' + VCSaddon.__init__(self, name, source, x, template) if source == 'default': - self.fillareastyles = ['solid',] - self.fillareaindices = [1,] - self.fillareacolors = [252,] - self.line = ['solid',] - self.linewidth=[1.0,] - self.linecolors=[241,] + self.line = [] + self.linewidth = [] + self.linecolors = [] + self.fillareastyles = [] + self.fillareaindices = [] + self.fillareacolors = [] + self.bins = [] else: - gm = vcsaddons.gms[self.g_name][source] - self.fillareastyle= gm.fillareastyles - self.fillareaindices = gm.fillareaindices - self.fillareacolors = gm.fillareacolors + if isinstance(source, (str, unicode)): + gm = vcsaddons.gms[self.g_type][source] + else: + gm = source self.line = gm.line self.linewidth = gm.linewidth self.linecolors = gm.linecolors - + self.fillareastyles = gm.fillareastyles + self.fillareaindices = gm.fillareaindices + self.fillareacolors = gm.fillareacolors + self.bins = gm.bins def list(self): - print '---------- Histogram (Ghg) member (attribute) listings ----------' - print 'Canvas Mode = ',self.x.mode - VCSaddon.list(self) - print 'fillareastyles = ', self.fillareastyles - print 'fillareaindices = ', self.fillareaindices - print 'fillareacolors = ', self.fillareacolors - print 'line = ', self.line - print 'linewidth = ', self.linewidth - print 'linecolors = ', self.linecolors - - - def plot(self,data,template = None, bg=0, x=None): + print '---------- Histogram (Ghg) member (attribute) listings ----------' # pragma: no cover + print 'Canvas Mode = ', self.x.mode # pragma: no cover + VCSaddon.list(self) # pragma: no cover + print 'fillareastyles = ', self.fillareastyles # pragma: no cover + print 'fillareaindices = ', self.fillareaindices # pragma: no cover + print 'fillareacolors = ', self.fillareacolors # pragma: no cover + print 'line = ', self.line # pragma: no cover + print 'linewidth = ', self.linewidth # pragma: no cover + print 'linecolors = ', self.linecolors # pragma: no cover + print 'bins = ', self.bins # pragma: no cover + + def plot(self, data, template=None, bg=0, x=None, **kwargs): if x is None: x = self.x if template is None: template = self.template - elif isinstance(template,str): + elif isinstance(template, str): template = x.gettemplate(template) - elif not vcs.istemplate(template): - raise "Error did not know what to do with template: %s" % template - - if not isinstance(data,cdms2.tvariable.TransientVariable): - mode= cdms2.getAutoBounds() - cdms2.setAutoBounds("on") - data = MV2.array(data) - data.getAxis(-1).getBounds() - cdms2.setAutoBounds(mode) - - while data.rank()>1: - data = data[0] + elif not vcs.istemplate(template): # pragma: no cover + raise ValueError("Error did not know what to do with template: %s" % template) # pragma: no cover + try: + data_name = data.title + except AttributeError: + try: + data_name = data.long_name + except AttributeError: + try: + data_name = data.id + data.units + except AttributeError: + try: + data_name = data.id + except AttributeError: + data_name = "array" + + # We'll just flatten the data... if they want to be more precise, should pass in more precise data + if isinstance(data, cdms2.avariable.AbstractVariable): + data = data.asma() + data = data.flatten() # ok now we have a good x and a good data - nbars = len(data) + if not self.bins: + self.bins = vcs.utils.mkscale(*vcs.minmax(data)) + + # Sort the bins + self.bins.sort() + + # Prune duplicates + pruned_bins = [] + for bin in self.bins: + if pruned_bins and numpy.allclose(bin, pruned_bins[-1]): + continue + pruned_bins.append(bin) + self.bins = pruned_bins + data_bins = numpy.digitize(data, self.bins) - 1 + binned = [data[data_bins==i] for i in range(len(self.bins))] + means = [] + stds = [] + + max_possible_deviance = 0 + + for ind, databin in enumerate(binned): + if len(databin) > 0: + means.append(databin.mean()) + stds.append(databin.std()) + else: + means.append(0) + stds.append(0) + if len(self.bins) > ind + 1: + max_possible_deviance = max(means[ind] - self.bins[ind], self.bins[ind + 1] - means[ind], max_possible_deviance) + else: + max_possible_deviance = max(means[ind] - self.bins[ind], max_possible_deviance) + color_values = [std / max_possible_deviance for std in stds] + y_values = [len(databin) for databin in binned] + nbars = len(self.bins) - 1 # create the primitive fill = x.createfillarea() line = x.createline() - fill.viewport = [template.data.x1,template.data.x2,template.data.y1,template.data.y2] - line.viewport = [template.data.x1,template.data.x2,template.data.y1,template.data.y2] - axb = data.getAxis(0).getBounds() - xmn,xmx = vcs.minmax(axb) - ymn,ymx = vcs.minmax(data) - - xmn,xmx,ymn,ymx = self.prep_plot(xmn,xmx,ymn,ymx) - - fill.worldcoordinate=[xmn,xmx,ymn,ymx] - line.worldcoordinate=[xmn,xmx,ymn,ymx] - - styles =[] + fill.viewport = [ + template.data.x1, template.data.x2, template.data.y1, template.data.y2] + line.viewport = [ + template.data.x1, template.data.x2, template.data.y1, template.data.y2] + + vcs_min_max = vcs.minmax(self.bins) + if numpy.allclose(self.datawc_x1, 1e20): + xmn = vcs_min_max[0] + else: + xmn = self.datawc_x1 + + if numpy.allclose(self.datawc_x2, 1e20): + xmx = vcs_min_max[1] + else: + xmx = self.datawc_x2 + + if numpy.allclose(self.datawc_y2, 1e20): + # Make the y scale be slightly larger than the largest bar + ymx = max(y_values) * 1.25 + else: + ymx = self.datawc_y2 + + if numpy.allclose(self.datawc_y1, 1e20): + ymn = 0 + else: + ymn = self.datawc_y1 + + fill.worldcoordinate = [xmn, xmx, ymn, ymx] + line.worldcoordinate = [xmn, xmx, ymn, ymx] + + styles = [] cols = [] indices = [] lt = [] - lw =[] + lw = [] lc = [] xs = [] ys = [] - + + levels = [.1 * i for i in range(11)] + + # Extend fillarea and line attrs to levels + if self.fillareastyles: + while len(self.fillareastyles) < (len(levels) - 1): + self.fillareastyles.append(self.fillareastyles[-1]) + else: + self.fillareastyles = ["solid"] * (len(levels) - 1) + + if self.fillareacolors: + while len(self.fillareacolors) < (len(levels) - 1): + self.fillareacolors.append(self.fillareacolors[-1]) + else: + for lev in levels[:-1]: + self.fillareacolors.append(int((self.color_2 - self.color_1) * lev) + self.color_1) + + if self.fillareaindices: + while len(self.fillareaindices) < (len(levels) - 1): + self.fillareaindices.append(self.fillareaindices[-1]) + else: + self.fillareaindices = [1] * (len(levels) - 1) + + if self.line: + while len(self.line) < (len(levels) - 1): + self.line.append(self.line[-1]) + else: + self.line = ["solid"] * (len(levels) - 1) + + if self.linewidth: + while len(self.linewidth) < (len(levels) - 1): + self.linewidth.append(self.linewidth[-1]) + else: + self.linewidth = [1] * (len(levels) - 1) + + if self.linecolors: + while len(self.linecolors) < (len(levels) - 1): + self.linecolors.append(self.linecolors[-1]) + else: + self.linecolors = ["black"] * (len(levels) - 1) for i in range(nbars): - if i < len(self.fillareastyles): - styles.append(self.fillareastyles[i]) - else: - styles.append(self.fillareastyles[-1]) - if i < len(self.fillareacolors): - cols.append(self.fillareacolors[i]) - else: - cols.append(self.fillareacolors[-1]) - if i < len(self.fillareaindices): - indices.append(self.fillareaindices[i]) - else: - indices.append(self.fillareaindices[-1]) - if i < len(self.line): - lt.append( self.line[i]) - else: - lt.append(self.line[-1]) - if i < len(self.linewidth): - lw.append( self.linewidth[i]) + # Calculate level for bar + value = color_values[i] + for lev_ind in range(len(levels)): + if levels[lev_ind] > value: + if lev_ind > 0: + lev_ind -= 1 + break + else: + # Shouldn't ever get here since level 0 is 0 + assert False # pragma: no cover else: - lw.append(self.linewidth[-1]) - if i < len(self.line): - lc.append( self.linecolors[i]) - else: - lc.append(self.linecolors[-1]) - - xs.append( [axb[i][0],axb[i][1],axb[i][1],axb[i][0],axb[i][0]]) - ys.append( [0,0,data[i],data[i],0]) + assert False # pragma: no cover + styles.append(self.fillareastyles[lev_ind]) + cols.append(self.fillareacolors[lev_ind]) + indices.append(self.fillareaindices[lev_ind]) + lt.append(self.line[lev_ind]) + lw.append(self.linewidth[lev_ind]) + lc.append(self.linecolors[lev_ind]) + xs.append([self.bins[i], self.bins[i], self.bins[i + 1], self.bins[i + 1]]) + ys.append([0, y_values[i], y_values[i], 0]) fill.style = styles fill.x = xs @@ -118,20 +220,43 @@ def plot(self,data,template = None, bg=0, x=None): fill.style fill.index = indices fill.color = cols + fill.colormap = self.colormap line.x = xs line.y = ys line.type = lt line.width = lw line.color = lc - displays = [] - displays.append(x.plot(fill,bg=bg)) - displays.append(x.plot(line,bg=bg)) - x.worldcoordinate = fill.worldcoordinate - dsp = template.plot(data,self,bg=bg) + x_axis = cdms2.createAxis(self.bins, id=data_name) + y_axis = cdms2.createAxis(vcs.mkscale(ymn, ymx), id="bin_size") + + displays.append(x.plot(fill, bg=bg, render=False)) + arr = MV2.masked_array(y_values) + arr.setAxis(0, x_axis) + dsp = template.plot(x, arr, self, bg=bg, X=x_axis, Y=y_axis) for d in dsp: - displays.append(d) + if d is not None: + displays.append(d) + legend_labels = {0: "No Variance", + .1: "", + .2: "", + .3: "", + .4: "", + .5: "", + .6: "", + .7: "", + .8: "", + .9: "", + 1: "High Variance"} + template.drawColorBar(self.fillareacolors, levels, + legend=legend_labels, x=x, + style=self.fillareastyles, + index=self.fillareaindices) + + displays.append(x.plot(line, bg=bg)) + + x.worldcoordinate = fill.worldcoordinate self.restore() return displays diff --git a/Packages/vcsaddons/Lib/polar.py b/Packages/vcsaddons/Lib/polar.py new file mode 100644 index 0000000000..900b349168 --- /dev/null +++ b/Packages/vcsaddons/Lib/polar.py @@ -0,0 +1,555 @@ +import vcs +import numpy +import vcsaddons + + +def circle_points(center, radius, points=75, ratio=1): + """ + Generates the coordinates of a circle in x list and y list. + """ + x = [] + y = [] + if ratio > 1: + ymul = ratio + xmul = 1 + else: + xmul = ratio + ymul = 1 + for i in range(points): + x.append(center[0] + xmul * radius * numpy.cos(float(i) / points * numpy.pi * 2)) + y.append(center[1] + ymul * radius * numpy.sin(float(i) / points * numpy.pi * 2)) + x.append(x[0]) + y.append(y[0]) + return x, y + + +def convert_arrays(var, theta): + """ + Normalizes valid input options to two lists of lists of values and a list of names. + + Handles: + list/tuple of list/tuples/arrays + (X,N,2) array + (N,2) array + list/tuple, list/tuple + """ + magnitudes = [] + thetas = [] + names = [] + if theta is None: + # var must be list/tuple of arrays or an array + if isinstance(var, (list, tuple)): + for arr in var: + if isinstance(arr, numpy.ndarray): + if len(arr.shape) == 2 and arr.shape[1] == 2: + magnitudes.append(arr[..., 0].tolist()) + thetas.append(arr[..., 1].tolist()) + try: + names.append(arr.id) + except AttributeError: + names.append(None) + else: + raise ValueError("Array is wrong shape; expected 2d array of 2-long elements," + " got %dd array of %d-long elements." % (len(arr.shape), arr.shape[-1])) + else: + if len(arr) == 2: + # Might be just a pair + if not isinstance(arr[0], (list, tuple)): + magnitudes.append([arr[0]]) + thetas.append([arr[1]]) + names.append(None) + continue + mag_group = [] + theta_group = [] + for val in arr: + if len(val) != 2: + raise ValueError("List is wrong shape; expected list/tuple of 2 element list/tuples," + " got %s of %d elements." % (type(val).__name__, len(val))) + mag_group.append(val[0]) + theta_group.append(val[1]) + names.append(None) + magnitudes.append(mag_group) + thetas.append(theta_group) + else: + if len(var.shape) == 3: + for i in range(var.shape[0]): + magnitudes.append(var[i, ..., 0].tolist()) + thetas.append(var[i, ..., 1].tolist()) + try: + names.append(var[i].id) + except AttributeError: + names.append(None) + else: + magnitudes = [var[..., 0].tolist()] + thetas = [var[..., 1].tolist()] + try: + names.append(var.id) + except AttributeError: + names.append(None) + else: + magnitudes = [] + if isinstance(var, (list, tuple)): + if isinstance(var[0], (list, tuple, numpy.ndarray)): + for v in var: + magnitudes.append(list(v)) + try: + names.append(v.id) + except AttributeError: + names.append(None) + else: + magnitudes = [var] + names.append(None) + elif isinstance(var, numpy.ndarray): + if len(var.shape) == 1: + magnitudes = [list(var)] + try: + names.append(var.id) + except AttributeError: + names.append(None) + elif len(var.shape) == 2: + for i in range(var.shape[0]): + magnitudes.append(list(var[i])) + try: + names.append(var[i].id) + except AttributeError: + names.append(None) + else: + raise ValueError("Array is wrong shape; expected 1d array or 2d array," + " got %dd array." % len(var.shape)) + + thetas = [] + if isinstance(theta, (list, tuple)): + if isinstance(theta[0], (list, tuple, numpy.ndarray)): + thetas = [list(v) for v in theta] + else: + thetas = [theta] + elif isinstance(theta, numpy.ndarray): + if len(theta.shape) == 1: + thetas = [list(theta)] + elif len(theta.shape) == 2: + thetas = [list(theta[i]) for i in range(theta.shape[0])] + else: + raise ValueError("Array is wrong shape; expected 1d array or 2d array," + " got %dd array." % len(var.shape)) + if not names: + names = [None] * len(var) + return magnitudes, thetas, names + + +class Gpo(vcsaddons.core.VCSaddon): + def __init__(self, name=None, source="default", x=None, template=None): + self.g_name = "Gpo" + self.g_type = "polar_oned" + super(Gpo, self).__init__(name, source, x, template) + self.x = None + if source == "default": + self.markersizes = [3] + self.markercolors = ["black"] + self.markers = ["dot"] + self.markercolorsource = "group" + self.clockwise = False + self.theta_offset = 0 + self.magnitude_ticks = "*" + self.magnitude_mintics = None + self.magnitude_tick_angle = 0 + self.theta_tick_count = 6 + self.group_names = [] + self.draw_lines = False + self.connect_groups = False + self.linecolors = ["black"] + self.lines = ["solid"] + self.linewidths = [1] + self.markerpriority = 2 + self.linepriority = 1 + # Nice default labels + self.xticlabels1 = { + 0: "0 (2pi)", + numpy.pi / 4: "pi/4", + numpy.pi / 2: "pi/2", + numpy.pi * 3 / 4.: "3pi/4", + numpy.pi: "pi", + numpy.pi * 5 / 4.: "5pi/4", + numpy.pi * 3 / 2.: "3pi/2", + numpy.pi * 7 / 4.: "7pi/4", + } + else: + if isinstance(source, (str, unicode)): + gm = vcsaddons.gms[self.g_type][source] + else: + gm = source + self.markersizes = gm.markersizes + self.markercolors = gm.markercolors + self.markers = gm.markers + self.markercolorsource = gm.markercolorsource + self.markerpriority = gm.markerpriority + self.clockwise = gm.clockwise + self.draw_lines = gm.draw_lines + self.linecolors = gm.linecolors + self.linewidths = gm.linewidths + self.linepriority = gm.linepriority + self.lines = gm.lines + self.connect_groups = gm.connect_groups + self.theta_offset = gm.theta_offset + self.magnitude_ticks = gm.magnitude_ticks + self.magnitude_mintics = gm.magnitude_mintics + self.magnitude_tick_angle = gm.magnitude_tick_angle + self.theta_tick_count = gm.theta_tick_count + self.group_names = gm.group_names + self.to_cleanup = [] + + def create_text(self, tt, to): + tc = vcs.createtext(Tt_source=tt, To_source=to) + self.to_cleanup.append(tc.Tt) + self.to_cleanup.append(tc.To) + return tc + + def text_orientation_for_angle(self, theta, source="default"): + """ + Generates a text orientation that will align text to look good depending on quadrant. + """ + # Normalize to [0, 2*pi) + while 0 > theta: + theta += 2 * numpy.pi + while 2 * numpy.pi <= theta: + theta -= 2 * numpy.pi + + if 0 < theta < numpy.pi: + valign = "bottom" + elif 0 == theta or numpy.pi == theta: + valign = "half" + else: + valign = "top" + + if numpy.pi / 2 > theta or numpy.pi * 3 / 2 < theta: + halign = "left" + elif numpy.allclose(numpy.pi / 2, theta) or numpy.allclose(numpy.pi * 3 / 2, theta): + halign = "center" + else: + halign = "right" + + # Build new text table + to = vcs.createtextorientation(source=source) + to.valign = valign + to.halign = halign + self.to_cleanup.append(to) + return to + + def magnitude_from_value(self, value, minmax): + if numpy.allclose((self.datawc_y1, self.datawc_y2), 1e20): + min, max = minmax + else: + min, max = self.datawc_y1, self.datawc_y2 + + return (value - min) / float(max - min) + + def theta_from_value(self, value): + if numpy.allclose((self.datawc_x1, self.datawc_x2), 1e20): + # No scale specified, just use the value as theta + return value + self.theta_offset + + minval = self.datawc_x1 + maxval = self.datawc_x2 + offset = self.theta_offset / float(maxval - minval) + + pct_val = (value - minval) / float(maxval - minval) + offset + rad_val = numpy.pi * 2 * pct_val + if self.clockwise: + # Reflect the value + rad_val *= -1 + return rad_val + + def plot(self, var, theta=None, template=None, bg=0, x=None): + """ + Plots a polar plot of your data. + + If var is an ndarray with the second dimension being 2, it will use the first value + as magnitude and the second as theta. + + Otherwise, if theta is provided, it uses var as magnitude and the theta given. + """ + if x is None: + if self.x is None: + self.x = vcs.init() + x = self.x + if template is None: + template = self.template + + if self.markercolorsource.lower() not in ("group", "magnitude", "theta"): + raise ValueError("polar.markercolorsource must be one of: 'group', 'magnitude', 'theta'") + + magnitudes, thetas, names = convert_arrays(var, theta) + if self.group_names: + names = self.group_names + while len(names) < len(magnitudes): + names.append(None) + + flat_magnitude = [] + for i in magnitudes: + flat_magnitude.extend(i) + flat_theta = [] + for i in thetas: + flat_theta.extend(i) + + canvas = x + # Determine aspect ratio for plotting the circle + canvas_info = canvas.canvasinfo() + # Calculate aspect ratio of window + window_aspect = canvas_info["width"] / float(canvas_info["height"]) + if window_aspect > 1: + ymul = window_aspect + xmul = 1 + else: + ymul = 1 + xmul = window_aspect + # Use window_aspect to adjust size of template.data + x0, x1 = template.data.x1, template.data.x2 + y0, y1 = template.data.y1, template.data.y2 + + xdiff = abs(x1 - x0) + ydiff = abs(y1 - y0) + + center = x0 + xdiff / 2., y0 + ydiff / 2. + diameter = min(xdiff, ydiff) + radius = diameter / 2. + plot_kwargs = {"render": False, "bg": bg, "donotstoredisplay": True} + # Outer line + if template.box1.priority > 0: + outer = vcs.createline(source=template.box1.line) + x, y = circle_points(center, radius, ratio=window_aspect) + outer.x = x + outer.y = y + canvas.plot(outer, **plot_kwargs) + del vcs.elements["line"][outer.name] + + if numpy.allclose((self.datawc_y1, self.datawc_y2), 1e20): + if self.magnitude_ticks == "*": + m_scale = vcs.mkscale(*vcs.minmax(flat_magnitude)) + else: + if isinstance(self.magnitude_ticks, (str, unicode)): + ticks = vcs.elements["list"][self.magnitude_ticks] + else: + ticks = self.magnitude_ticks + m_scale = ticks + else: + m_scale = vcs.mkscale(self.datawc_y1, self.datawc_y2) + + if template.ytic1.priority > 0: + m_ticks = vcs.createline(source=template.ytic1.line) + m_ticks.x = [] + m_ticks.y = [] + + if template.ylabel1.priority > 0: + to = self.text_orientation_for_angle(self.magnitude_tick_angle, + source=template.ylabel1.textorientation) + m_labels = self.create_text(template.ylabel1.texttable, to) + m_labels.x = [] + m_labels.y = [] + m_labels.string = [] + if self.yticlabels1 == "*": + mag_labels = vcs.mklabels(m_scale) + else: + mag_labels = self.yticlabels1 + else: + m_labels = None + + for lev in m_scale: + lev_radius = radius * self.magnitude_from_value(lev, (m_scale[0], m_scale[-1])) + x, y = circle_points(center, lev_radius, ratio=window_aspect) + if m_labels is not None: + if lev in mag_labels: + m_labels.string.append(mag_labels[lev]) + m_labels.x.append(xmul * lev_radius * numpy.cos(self.magnitude_tick_angle) + center[0]) + m_labels.y.append(ymul * lev_radius * numpy.sin(self.magnitude_tick_angle) + center[1]) + m_ticks.x.append(x) + m_ticks.y.append(y) + canvas.plot(m_ticks, **plot_kwargs) + del vcs.elements["line"][m_ticks.name] + if m_labels is not None: + canvas.plot(m_labels, **plot_kwargs) + del vcs.elements["textcombined"][m_labels.name] + + if template.ymintic1.priority > 0 and self.magnitude_mintics is not None: + mag_mintics = vcs.createline(source=template.ymintic1.line) + mag_mintics.x = [] + mag_mintics.y = [] + + mintics = self.magnitude_mintics + if isinstance(mintics, (str, unicode)): + mintics = vcs.elements["list"][mintics] + + for mag in mintics: + mintic_radius = radius * self.magnitude_from_value(mag, (m_scale[0], m_scale[-1])) + x, y = circle_points(center, mintic_radius, ratio=window_aspect) + mag_mintics.x.append(x) + mag_mintics.y.append(y) + canvas.plot(mag_mintics, **plot_kwargs) + del vcs.elements["line"][mag_mintics.name] + + if self.xticlabels1 == "*": + if numpy.allclose((self.datawc_x1, self.datawc_x2), 1e20): + tick_thetas = list(numpy.arange(0, numpy.pi * 2, numpy.pi / 4)) + tick_labels = {t: str(t) for t in tick_thetas} + else: + d_theta = (self.datawc_x2 - self.datawc_x1) / float(self.theta_tick_count) + tick_thetas = numpy.arange(self.datawc_x1, self.datawc_x2 + .0001, d_theta) + tick_labels = vcs.mklabels(tick_thetas) + else: + tick_thetas = self.xticlabels1.keys() + tick_labels = self.xticlabels1 + + if template.xtic1.priority > 0: + t_ticks = vcs.createline(source=template.xtic1.line) + t_ticks.x = [] + t_ticks.y = [] + + if template.xlabel1.priority > 0: + t_labels = [] + theta_labels = tick_labels + else: + t_labels = None + + for t in tick_thetas: + angle = self.theta_from_value(t) + x0 = center[0] + (xmul * radius * numpy.cos(angle)) + x1 = center[0] + y0 = center[1] + (ymul * radius * numpy.sin(angle)) + y1 = center[1] + if t_labels is not None: + label = self.create_text(template.xlabel1.texttable, + self.text_orientation_for_angle(angle, + source=template.xlabel1.textorientation)) + label.string = [theta_labels[t]] + label.x = [x0] + label.y = [y0] + t_labels.append(label) + t_ticks.x.append([x0, x1]) + t_ticks.y.append([y0, y1]) + canvas.plot(t_ticks, **plot_kwargs) + del vcs.elements["line"][t_ticks.name] + if t_labels is not None: + for l in t_labels: + canvas.plot(l, **plot_kwargs) + del vcs.elements["textcombined"][l.name] + + values = vcs.createmarker() + values.type = self.markers + values.size = self.markersizes + values.color = self.markercolors + values.colormap = self.colormap + values.priority = self.markerpriority + values.x = [] + values.y = [] + + if template.legend.priority > 0: + # Only labels that are set will show up in the legend + label_count = len(names) - len([i for i in names if i is None]) + labels = self.create_text(template.legend.texttable, template.legend.textorientation) + labels.x = [] + labels.y = [] + labels.string = [] + + if self.draw_lines: + line = vcs.createline() + line.x = [] + line.y = [] + line.type = self.lines + line.color = self.linecolors if self.linecolors is not None else self.markercolors + line.width = self.linewidths + line.priority = self.linepriority + + # This is up here because when it's part of the main loop, we can lose "order" of points when we flatten them. + for mag, theta in zip(magnitudes, thetas): + x = [] + y = [] + + for m, t in zip(mag, theta): + t = self.theta_from_value(t) + r = self.magnitude_from_value(m, (m_scale[0], m_scale[-1])) * radius + x.append(xmul * numpy.cos(t) * r + center[0]) + y.append(ymul * numpy.sin(t) * r + center[1]) + + if self.connect_groups: + line.x.extend(x) + line.y.extend(y) + else: + line.x.append(x) + line.y.append(y) + + if self.markercolorsource.lower() in ('magnitude', "theta"): + # Regroup the values using the appropriate metric + + mag_flat = numpy.array(magnitudes).flatten() + theta_flat = numpy.array(thetas).flatten() + + if self.markercolorsource.lower() == "magnitude": + scale = m_scale + vals = mag_flat + else: + scale = theta_ticks + vals = theta_flat + + indices = [numpy.where(numpy.logical_and(vals >= scale[i], vals <= scale[i + 1])) + for i in range(len(scale) - 1)] + magnitudes = [mag_flat[inds] for inds in indices] + thetas = [theta_flat[inds] for inds in indices] + names = vcs.mklabels(scale, output="list") + names = [names[i] + " - " + names[i + 1] for i in range(len(names) - 1)] + label_count = len(names) + + for mag, theta, name in zip(magnitudes, thetas, names): + x = [] + y = [] + for m, t in zip(mag, theta): + t = self.theta_from_value(t) + r = self.magnitude_from_value(m, (m_scale[0], m_scale[-1])) * radius + x.append(xmul * numpy.cos(t) * r + center[0]) + y.append(ymul * numpy.sin(t) * r + center[1]) + + if template.legend.priority > 0 and name is not None: + y_offset = len(labels.x) / float(label_count) * (template.legend.y2 - template.legend.y1) + lx, ly = template.legend.x1, template.legend.y1 + y_offset + x.append(lx) + y.append(ly) + labels.x.append(lx + .01) + labels.y.append(ly) + labels.string.append(str(name)) + values.x.append(x) + values.y.append(y) + + if template.legend.priority > 0: + canvas.plot(labels, **plot_kwargs) + del vcs.elements["textcombined"][labels.name] + if self.draw_lines: + canvas.plot(line, **plot_kwargs) + del vcs.elements["line"][line.name] + + for el in self.to_cleanup: + if vcs.istexttable(el): + if el.name in vcs.elements["texttable"]: + del vcs.elements["texttable"][el.name] + else: + if el.name in vcs.elements["textorientation"]: + del vcs.elements["textorientation"][el.name] + self.to_cleanup = [] + + # Prune unneeded levels from values + to_prune = [] + for ind, (x, y) in enumerate(zip(values.x, values.y)): + if x and y: + continue + else: + to_prune.append(ind) + + for prune_ind in to_prune[::-1]: + del values.x[prune_ind] + del values.y[prune_ind] + if len(values.color) > prune_ind and len(values.color) > 1: + del values.color[prune_ind] + if len(values.size) > prune_ind and len(values.size) > 1: + del values.size[prune_ind] + if len(values.type) > prune_ind and len(values.type) > 1: + del values.type[prune_ind] + + canvas.plot(values, bg=bg, donotstoredisplay=True) + del vcs.elements["marker"][values.name] + return canvas diff --git a/testing/vcs/test_vcs_read_old_scr.py b/testing/vcs/test_vcs_read_old_scr.py index 0a61df61e5..4ae04d8491 100644 --- a/testing/vcs/test_vcs_read_old_scr.py +++ b/testing/vcs/test_vcs_read_old_scr.py @@ -71,3 +71,4 @@ assert(gm.ymtics1=="lat5") assert(gm.fillareastyle == "solid") assert(gm.fillareacolors == [30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 35, 36]) +sys.exit(0) diff --git a/testing/vcsaddons/CMakeLists.txt b/testing/vcsaddons/CMakeLists.txt index 64b8d1755b..d6b382fafb 100644 --- a/testing/vcsaddons/CMakeLists.txt +++ b/testing/vcsaddons/CMakeLists.txt @@ -35,6 +35,55 @@ cdat_add_test(vcs_addons_test_EzTemplate_12_plots_spacing ${cdat_SOURCE_DIR}/testing/vcsaddons/test_EzTemplate_12_plots_spacing.py ${BASELINE_DIR}/test_EzTemplate_12_plots_spacing.png ) +cdat_add_test(vcs_addons_test_histogram_defaults + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_histogram_defaults.py + ${BASELINE_DIR}/test_vcs_addons_histogram_defaults.png +) +cdat_add_test(vcs_addons_test_histogram_inherit + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_histogram_inherit.py + ${BASELINE_DIR}/test_vcs_addons_histogram_inherit.png +) +cdat_add_test(vcs_addons_test_polar + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar.py + ${BASELINE_DIR}/test_vcs_addons_polar.png +) +cdat_add_test(vcs_addons_test_polar_inherit + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_inherit.py + ${BASELINE_DIR}/test_vcs_addons_polar_inherit.png +) +cdat_add_test(vcs_addons_test_convert_arrays + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_convert_arrays.py +) +cdat_add_test(vcs_addons_test_polar_degrees + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_degrees.py + ${BASELINE_DIR}/test_vcs_addons_polar_degrees.png +) +cdat_add_test(vcs_addons_test_polar_annual + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_annual.py + ${BASELINE_DIR}/test_vcs_addons_polar_annual.png +) +cdat_add_test(vcs_addons_test_polar_diurnal + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_diurnal.py + ${BASELINE_DIR}/test_vcs_addons_polar_diurnal.png +) +cdat_add_test(vcs_addons_test_polar_seasonal + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_seasonal.py + ${BASELINE_DIR}/test_vcs_addons_polar_seasonal.png +) +cdat_add_test(vcs_addons_test_polar_semidiurnal + "${PYTHON_EXECUTABLE}" + ${cdat_SOURCE_DIR}/testing/vcsaddons/test_vcs_addons_polar_semidiurnal.py + ${BASELINE_DIR}/test_vcs_addons_polar_semidiurnal.png +) if (CDAT_DOWNLOAD_SAMPLE_DATA) cdat_add_test(vcs_addons_EzTemplate_2x2 diff --git a/testing/vcsaddons/test_vcs_addons_convert_arrays.py b/testing/vcsaddons/test_vcs_addons_convert_arrays.py new file mode 100644 index 0000000000..6e784e10b8 --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_convert_arrays.py @@ -0,0 +1,70 @@ +import vcsaddons +import numpy + +magnitudes = [1, 2, 3, 4] +thetas = [5, 6, 7, 8] +zipped_input = zip(magnitudes, thetas) +grouped_zipped = [zipped_input[:2], zipped_input[2:]] + +one_array = numpy.array(zip(magnitudes, thetas)) +three_d_array = numpy.array(grouped_zipped) +two_arrays = numpy.array(magnitudes), numpy.array(thetas) +two_array_groups = numpy.array([magnitudes[:2], magnitudes[2:]]), numpy.array([thetas[:2], thetas[2:]]) +list_and_array = two_arrays[0], thetas +two_lists = magnitudes, thetas +lists_of_arrays = [two_arrays[0]], [two_arrays[1]] +array_and_list = magnitudes, two_arrays[1] +one_list_tuples = zip(magnitudes, thetas) +one_list_grouped_tuples = [zip(magnitudes[:2], thetas[:2]), zip(magnitudes[2:], thetas[2:])] +one_list_of_arrays = [numpy.array(zip(magnitudes[:2], thetas[:2])), numpy.array(zip(magnitudes[2:], thetas[2:]))] + + +def compare(input, expected): + result = vcsaddons.polar.convert_arrays(*input) + print "Checking", result[0:2], "vs", expected + assert result[0] == expected[0] + assert result[1] == expected[1] + +grouped = ([magnitudes[:2], magnitudes[2:]], [thetas[:2], thetas[2:]]) + +compare((one_array, None), ([magnitudes], [thetas])) +compare(two_arrays, ([magnitudes], [thetas])) +compare(two_array_groups, grouped) +three_d_expected = ([[1, 2], [3, 4]], [[5, 6], [7, 8]]) +compare((three_d_array, None), three_d_expected) +compare(list_and_array, ([magnitudes], [thetas])) +compare(two_lists, ([magnitudes], [thetas])) +compare(lists_of_arrays, ([magnitudes], [thetas])) +compare(array_and_list, ([magnitudes], [thetas])) +compare((one_list_tuples, None), ([[i] for i in magnitudes], [[i] for i in thetas])) +compare((one_list_grouped_tuples, None), grouped) +compare((one_list_of_arrays, None), grouped) + + +def test_error(input, error): + try: + vcsaddons.polar.convert_arrays(*input) + except: + print "Got", error + else: + assert False, "Should have raised a %s" % error + +# Test error conditions + +# Single arg: + +# List of 3d arrays +test_error(([numpy.array([[[1, 2]]])], None), "ValueError for list of 3d arrays") +# >2 element arrays +test_error(([numpy.array([[1, 2, 3]])], None), "ValueError for list of 3-element arrays") +# <2 element arrays +test_error(([numpy.array([[1]])], None), "ValueError for list of 1-element arrays") +# Wrong-sized lists +test_error(([[(1, 2, 3)]], None), "ValueError for wrong sized lists.") + + +# Two args: + +# Too many dimensions +test_error((numpy.array([[[1, 2]]]), numpy.array([[1, 2]])), "ValueError for too many dimensions for magnitude.") +test_error((numpy.array([[1, 2]]), numpy.array([[[1, 2]]])), "ValueError for too many dimensions for magnitude.") diff --git a/testing/vcsaddons/test_vcs_addons_histogram_defaults.py b/testing/vcsaddons/test_vcs_addons_histogram_defaults.py new file mode 100644 index 0000000000..090aaf33d8 --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_histogram_defaults.py @@ -0,0 +1,17 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy + +x = regression.init() + +numpy.random.seed(seed=12345) +vals = numpy.random.random_sample(2000) * 100 +histo = vcsaddons.histograms.Ghg() +histo.plot(vals, bg=True, x=x) + +fnm = "test_vcs_addons_histogram_defaults.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_histogram_inherit.py b/testing/vcsaddons/test_vcs_addons_histogram_inherit.py new file mode 100644 index 0000000000..a977a68bbe --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_histogram_inherit.py @@ -0,0 +1,58 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs, cdms2 +import vcsaddons, numpy + +x = regression.init() + +cdmsfile = cdms2.open(vcs.sample_data + "/clt.nc") +clt = cdmsfile("clt") + +levels = [10, 20, 30, 40, 60, 70, 80, 90, 100] +histo = vcsaddons.histograms.Ghg() +histo.bins = levels +histo.line = ["solid", "dash", "dash-dot"] +histo.linewidth = [1, 2, 3] +histo.linecolors = ["red", "green", "blue"] +histo.fillareastyles = ["solid", "hatch", "pattern", "solid"] +histo.fillareaindices = [1, 2, 3, 4] +histo.fillareacolors = ["blue", "green", "red", "orange"] + +histo2 = vcsaddons.createhistogram(source=histo) + +print "Checking all inherited attributes..." +assert histo2.bins == histo.bins +assert histo2.line == histo.line +assert histo2.linewidth == histo.linewidth +assert histo2.linecolors == histo.linecolors +assert histo2.fillareastyles == histo.fillareastyles +assert histo2.fillareacolors == histo.fillareacolors +assert histo2.fillareaindices == histo.fillareaindices +print "Inherited all values." + +histo2.levels = [10, 20, 10, 100, 110, 50, 20] +histo3 = vcsaddons.createhistogram(source=histo2.name, x=x) + +print "Checking name-based inheritance" +assert histo3.bins == histo2.bins +assert histo3.line == histo2.line +assert histo3.linewidth == histo2.linewidth +assert histo3.linecolors == histo2.linecolors +assert histo3.fillareastyles == histo2.fillareastyles +assert histo3.fillareacolors == histo2.fillareacolors +assert histo3.fillareaindices == histo2.fillareaindices +print "Inherited all values." + +histo3.datawc_y1 = -1 +histo3.datawc_y2 = 200000 +histo3.datawc_x1 = 0 +histo3.datawc_x2 = 100 + +histo3.bins = None +histo3.plot(clt, template="default", bg=True) + +fnm = "test_vcs_addons_histogram_inherit.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar.py b/testing/vcsaddons/test_vcs_addons_polar.py new file mode 100644 index 0000000000..5fcdc6c952 --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar.py @@ -0,0 +1,23 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy + +x = regression.init() + +polar = vcsaddons.polar.Gpo() +polar.markers = ["dot", "circle"] +polar.markersizes = [3, 5] + +polar.magnitude_tick_angle = numpy.pi / 6 + +theta = list(numpy.arange(0, 4 * numpy.pi + .01, numpy.pi / 24)) +magnitude = list(numpy.sin(theta)) + +polar.plot(magnitude, theta, bg=True, x=x) + +fnm = "test_vcs_addons_polar.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_annual.py b/testing/vcsaddons/test_vcs_addons_polar_annual.py new file mode 100644 index 0000000000..5cea2bfc10 --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_annual.py @@ -0,0 +1,36 @@ +import sys,os +import testing.regression as regression +import vcs +import vcsaddons, numpy + +src = sys.argv[1] + +x = regression.init() + +polar = vcsaddons.getpolar("annual_cycle") +polar.markers = ["dot"] +polar.markersizes = [3] + +polar.magnitude_tick_angle = numpy.pi / 8 + +import cdms2, cdutil + +f = cdms2.open(os.path.join(vcs.sample_data, "clt.nc")) +clt = f("clt") +cdutil.setAxisTimeBoundsMonthly(clt.getTime()) +averaged_time = cdutil.averager(clt, axis="t") +averaged_time = averaged_time.reshape((1, averaged_time.shape[0], averaged_time.shape[1])) +averaged_time_for_departures = numpy.repeat(averaged_time, len(clt), axis=0) + +clt_departures = clt - averaged_time_for_departures +clt_departures.setAxisList(clt.getAxisList()) +avg_departures = cdutil.averager(clt_departures, axis="xy") + +theta = range(1, len(clt) + 1) +magnitude = avg_departures +polar.plot(magnitude, theta, bg=True, x=x) + +fnm = "test_vcs_addons_polar_annual.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_degrees.py b/testing/vcsaddons/test_vcs_addons_polar_degrees.py new file mode 100644 index 0000000000..1a44b6b0e6 --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_degrees.py @@ -0,0 +1,24 @@ +import sys +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy + +x = regression.init() + +polar = vcsaddons.getpolar("degrees") +polar.markers = ["dot", "circle"] +polar.markersizes = [3, 5] + +polar.magnitude_tick_angle = numpy.pi / 6 + +theta = numpy.array(range(0, 720, 2)) +magnitude = 9 * numpy.sin(5 * 2 * numpy.pi * theta / 360) +polar.datawc_y1 = 0 +polar.datawc_y2 = max(magnitude) +polar.plot(magnitude, theta, bg=True, x=x) + +fnm = "test_vcs_addons_polar_degrees.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_diurnal.py b/testing/vcsaddons/test_vcs_addons_polar_diurnal.py new file mode 100644 index 0000000000..24a6f832bc --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_diurnal.py @@ -0,0 +1,39 @@ +import sys,os +src = sys.argv[1] +import vcs +import vcsaddons, numpy +import cdms2, cdutil, cdtime +import testing.regression as regression + +x = regression.init() + +f = cdms2.open(os.path.join(vcs.sample_data, "thermo.nc")) +temp = f('t') +levels = temp.getLevel() +time = temp.getTime() +# Break up temp by level +magnitudes = [temp[:,i] for i in range(temp.shape[1])] +for i, mag in enumerate(magnitudes): + mag.id = "%0.f %s" % (levels[i], levels.units) + +times = [] +for t in time: + reltime = cdtime.relativetime(t, time.units) + comptime = reltime.tocomponent() + times.append(comptime.hour) + +thetas = [times] * len(magnitudes) + +polar = vcsaddons.getpolar("diurnal") +polar.markers = ["dot"] +polar.markersizes = [3] +polar.markercolors = vcs.getcolors(list(levels)) + +polar.magnitude_tick_angle = numpy.pi / 8 + +polar.plot(magnitudes, thetas, bg=True, x=x) + +fnm = "test_vcs_addons_polar_diurnal.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_inherit.py b/testing/vcsaddons/test_vcs_addons_polar_inherit.py new file mode 100644 index 0000000000..2eb10b7d8d --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_inherit.py @@ -0,0 +1,45 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy + +x = regression.init() + +gm = vcsaddons.polar.Gpo() +gm.markers = ["dot", "circle"] +gm.markersizes = [3, 5] +gm.markercolors = ["red", "blue"] +gm.clockwise = True +gm.theta_offset = numpy.pi / 4 +gm.magnitude_ticks = [.2 * i for i in range(6)] +gm.magnitude_tick_angle = numpy.pi / 10 +gm.theta_tick_count = 10 +gm.group_names = ["First", "Second"] + +polar = vcsaddons.polar.Gpo(source=gm) + +assert polar.markersizes == gm.markersizes +assert polar.markercolors == gm.markercolors +assert polar.markers == gm.markers +assert polar.clockwise == gm.clockwise +assert polar.theta_offset == gm.theta_offset +assert polar.magnitude_ticks == gm.magnitude_ticks +assert polar.magnitude_tick_angle == gm.magnitude_tick_angle +assert polar.theta_tick_count == gm.theta_tick_count +assert polar.group_names == gm.group_names + +polar.magnitude_tick_angle = numpy.pi / 6 + +theta = list(numpy.arange(0, 4 * numpy.pi + .01, numpy.pi / 24)) +magnitude = list(numpy.sin(theta)) + +theta = [theta[:len(theta) / 2], theta[len(theta) / 2:]] +magnitude = [magnitude[:len(magnitude)/ 2], magnitude[len(magnitude) / 2:]] + +polar.plot(magnitude, theta, bg=True, x=x) + +fnm = "test_vcs_addons_polar_inherit.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_seasonal.py b/testing/vcsaddons/test_vcs_addons_polar_seasonal.py new file mode 100644 index 0000000000..0f5693a35f --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_seasonal.py @@ -0,0 +1,53 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy, MV2 +import cdms2, cdutil, cdtime + +x = regression.init() + +f = cdms2.open(os.path.join(vcs.sample_data, "clt.nc")) +# Trim first few months and last month so we have even number of seasons +cloudiness = f('clt', time=(11, 119)) +cdutil.setAxisTimeBoundsMonthly(cloudiness.getTime()) +cloudiness_time_axis = cloudiness.getTime() +averaged_seasons = MV2.zeros((36, 46, 72)) +# Average the seasons in cloudiness +for i in range(36): + averaged_seasons[i] = cdutil.averager(cloudiness(time=(cloudiness_time_axis[i * 3], cloudiness_time_axis[(i+1) * 3])), axis="t") + +averaged_seasons.setAxis(1, cloudiness.getLatitude()) +averaged_seasons.setAxis(2, cloudiness.getLongitude()) + +regions = { + "north_polar": (66, 90), + "north_temperate": (22, 66), + "tropics": (-22, 22), + "south_temperate": (-66, -22), + "south_polar": (-90, -66) +} + +def get_region_avg(var, r, axis="xy"): + avg = cdutil.averager(var(latitude=regions[r]), axis=axis) + avg.id = r + return avg + +magnitudes = [get_region_avg(averaged_seasons, region) for region in regions] +thetas = [range(4) * 27] * 5 + +polar = vcsaddons.getpolar("seasonal") +polar.datawc_y1 = 0 +polar.datawc_y2 = 100 +polar.markers = ["dot"] +polar.markersizes = [3] +polar.markercolors = vcs.getcolors([-90, -66, -22, 22, 66, 90], split=False) + +polar.magnitude_tick_angle = numpy.pi / 4 + +polar.plot(magnitudes, thetas, bg=True, x=x) + +fnm = "test_vcs_addons_polar_seasonal.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret) diff --git a/testing/vcsaddons/test_vcs_addons_polar_semidiurnal.py b/testing/vcsaddons/test_vcs_addons_polar_semidiurnal.py new file mode 100644 index 0000000000..1ce3f21efa --- /dev/null +++ b/testing/vcsaddons/test_vcs_addons_polar_semidiurnal.py @@ -0,0 +1,39 @@ +import sys,os +src = sys.argv[1] +import testing.regression as regression +import vcs +import vcsaddons, numpy +import cdms2, cdutil, cdtime + +x = regression.init() + +f = cdms2.open(os.path.join(vcs.sample_data, "thermo.nc")) +temp = f('t') +levels = temp.getLevel() +time = temp.getTime() +# Break up temp by level +magnitudes = [temp[:,i] for i in range(temp.shape[1])] +for i, mag in enumerate(magnitudes): + mag.id = "%0.f %s" % (levels[i], levels.units) + +times = [] +for t in time: + reltime = cdtime.relativetime(t, time.units) + comptime = reltime.tocomponent() + times.append(comptime.hour % 12) + +thetas = [times] * len(magnitudes) + +polar = vcsaddons.getpolar("semidiurnal") +polar.markers = ["dot"] +polar.markersizes = [3] +polar.markercolors = vcs.getcolors(list(levels)) + +polar.magnitude_tick_angle = numpy.pi / 8 + +polar.plot(magnitudes, thetas, bg=True, x=x) + +fnm = "test_vcs_addons_polar_semidiurnal.png" +x.png(fnm) +ret = regression.check_result_image(fnm, src) +sys.exit(ret)