source: TI04-geosplat/trunk/pygss/OutputManager.py @ 1660

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI04-geosplat/trunk/pygss/OutputManager.py@1660
Revision 1660, 18.2 KB checked in by astephen, 14 years ago (diff)

Version with overlay of trajectories on top of model fields draft version working before any rigourous testing.

Line 
1#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
2#   This software may be distributed under the terms of the
3#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
4
5"""
6OutputManager.py
7================
8
9Contains the class OutputManager class that controls all generation of
10graphical outputs.
11
12""" 
13
14# Import library modules
15import os, sys, re,time
16
17# Import package modules
18from serverConfig import *
19from common import *
20from localRules import *
21from GSErrors import *
22from PlotClasses import *
23from Animator import * 
24
25# Import external packages
26import cdms
27import vcs
28
29
30class OutputManager:
31    """
32    Manages generation of visual outputs.
33    """             
34       
35    def __init__(self, sessionObject):
36        """
37        Sets up instance variables and gets things ready to plot.
38        """
39        self.bag=sessionObject
40        self.textOnly=None
41        self._prepareData()
42        if self.textOnly:
43            # This should be checked by external caller
44            pass
45        else:
46            self._decidePlotClass()
47
48
49    def _prepareData(self):
50        """
51        Sets up variables to be worked with.
52        """
53        # Start with list of fileURIs - sorted
54        fileURIs=self.bag["fileURIList"]
55        fileURIs.sort()
56
57        gpattern=re.compile(r"(%s)_(\d+)\.?(\d+)?-?(\d+)?\.?(\d+)?" % GRAPHICAL_OUTPUT_TYPES_OR_STRING)
58       
59        match=gpattern.match(self.bag["graphicalOutputType"])
60        if not match:
61            raise GSOptionHandlingError, "Cannot find a match for graphical output type."
62         
63        (self.graphicalOutputType, fileIndex1, varIndex1, fileIndex2, varIndex2)=match.groups()
64
65        """if fileIndex2:
66            raise GSOptionHandlingError, "Cannot yet handle multiple feature graphical outputs, sorry!"  """
67       
68        fileIndices=[fileIndex1, fileIndex2]
69        varIndices=[varIndex1, varIndex2]
70       
71        varIDs=[]
72        self.infilePaths=[]
73        self.infiles=[]
74        self.vars=[]
75        self.varCodes=[]
76       
77        for count in range(len(fileIndices)):
78            varIndex=varIndices[count]
79            if varIndex==None: continue
80            varIndex=int(varIndex)
81            fileIndex=int(fileIndices[count])
82            varID=self.bag["fileVariable_%s.%s" % (fileIndex, varIndex)]
83           
84            varIDs.append(varID)
85            infilePath=fileURIs[fileIndex-1]
86            self.infilePaths.append(infilePath)
87            self.infiles.append(cdms.open(infilePath))
88            self.vars.append(self.infiles[count](varID))
89               
90            # Set a flag for combined variables so you can get the varIndices
91            self.varCodes.append("%s.%s" % (fileIndex, varIndex))
92       
93        """fileIndex1=int(fileIndex1)
94        varIndex1=int(varIndex1)
95        varID=self.bag["fileVariable_%s.%s" % (fileIndex1, varIndex1)]
96       
97        self.infilePath=fileURIs[fileIndex1-1]
98        infile=cdms.open(self.infilePath)
99        self.infile=infile
100        self.var=self.infile(varID)
101       
102        self.suffix="%s.%s" % (fileIndex1, varIndex1)"""
103       
104        if self.graphicalOutputType=="PointValueAndMetadata":
105            self.textOnly=self._createTextOutputString(self.infilePaths[0], varIDs[0]) 
106
107        # Close infiles for tidiness
108        for infile in self.infiles:
109            infile.close()
110
111
112    def _createTextOutputString(self, fname, varID):
113        """
114        Returns a text string including the variable value and suitable
115        metadata.
116        """
117        cdmsFile=cdms.open(fname)
118        var=cdmsFile(varID)
119       
120        axisinfo="<B>Axis Information:</B>"
121        for ax in var.getAxisList():
122            axisinfo="""%s
123
124<B>ID:</B> %s
125<B>Units:</B> %s
126<B>Start:</B> %s
127<B>End:</B> %s""" % (axisinfo, ax.id, ax.units, ax[0], ax[-1])
128
129            if "bounds_"+ax.id in cdmsFile.listvariables():
130                bounds=cdmsFile('bounds_'+ax.id)
131                axisinfo="""%s
132<B>Bounds:</B> %s""" % (axisinfo, bounds.tolist())
133                   
134        textOutput="""
135
136<B>Your data contains only a single value</B>
137
138<B>Variable name:</B> %s
139
140<B>Variable value:</B> %s
141<B>Variable units:</B> %s
142
143%s""" % (getBestName(var), var.flat[0], getattr(var, "units", "-"), axisinfo)   
144       
145        print textOutput
146        return textOutput
147       
148
149    def _decidePlotClass(self):
150        """
151        Decides on plot class to use.
152        infile=cdms.open(fileURIs[fileIndex1-1])
153        self.var=infile(varID)
154        infile.close()
155       
156        """
157        plotType=self.bag["plotType"]   
158       
159        if self.bag.has_key("continentsSwitch"):
160            continentsSwitch=self.bag["continentsSwitch"]
161        else:
162            continentsSwitch="off"
163       
164        if self.bag.has_key("projection"):   
165            projection=self.bag["projection"]
166        else: 
167            projection="standard"
168       
169        projMap={"standard":"Linear",
170            "mollweide":"Mollweide",
171            "northPolar":"PolarNorth",
172            "southPolar":"PolarSouth",
173            "robinson":"Robinson"}         
174       
175        # set bounding box off unless overridden below
176        self.bbox=None
177       
178        if plotType in ["YvsXGraph", "MultipleTrajectory"]:
179            self.plotClass=plotType   
180        elif plotType=="isolineAndFill":
181            self.plotClass="IsolineAndFill"+projMap[projection] 
182        elif plotType=="MultipleTrajectoryOverBoxfillLinear":
183            self.plotClass="MultipleTrajectoryOVERBoxfillLinear"
184            continentsSwitch="on"
185            varCodeString="-".join(self.varCodes)
186           
187            if self.bag["mapArea"]=="surroundingArea":
188                self.bbox=self.bag["varDict"][varCodeString]["domain"]["bbox"]
189            elif self.bag["mapArea"]=="global":
190                # bbox lat/lon needed even if global to ensure start
191                # longitude is same for both layers
192                bbox=self.bag["varDict"][varCodeString]["domain"]["bbox"]
193                (lat0, lon0, lat1, lon1)=bbox
194                self.bbox=(-90, lon0, 90, lon1)
195        else:
196            self.plotClass=plotType.title()+projMap[projection] 
197       
198        self.contSwitch={"off":0, "on":1}[continentsSwitch]
199       
200       
201    def createOutputs(self):
202        """
203        Creates the visualisations and writes them to a file.
204        """
205        self.fileFormat=self.bag["fileFormat"] 
206        formatMap={"postscript":"ps", "gif":"gif"}
207        ext=formatMap[self.fileFormat]
208       
209        timestamp= time.strftime("%Y%m%d%H%M%S", time.gmtime(time.time()))
210        fileName="output_%s.%s" % (timestamp, ext)
211
212        self.localPath=os.path.join(OUTPUT_DIR, fileName)
213        self.URLPath=os.path.join(OUTPUT_DIR_URL, fileName)     
214
215        # Delete output file if already there
216        if os.path.isfile(self.localPath): os.unlink(self.localPath)
217
218        self.imageSize=self.bag["imageSize"]
219       
220        print "Creating plot using class: ", self.plotClass
221       
222        vcsCanvas=vcs.init()
223       
224        if self.graphicalOutputType=="Animation":
225            self._createAnimation(vcsCanvas)               
226        else:
227            self._createPlot(vcsCanvas)
228
229        print "Plot produced successfully:", self.localPath,"\n\n"   
230        return
231
232
233    def _createPlot(self, vcsCanvas):
234        """
235        Creates a single frame plot - optionally including overlays.
236        """
237        try:
238            if self.plotClass.find("IsolineAndFill")>-1:
239                proj=self.plotClass[14:]
240                plotClassList=["Isofill"+proj, "Isoline"+proj]     
241                # Need to patch up here to ensure that correct file and var are used
242                self.vars.append(self.vars[0])
243                self.infilePaths.append(self.infilePaths[0])
244            elif self.plotClass.find("OVER")>-1:
245                plotClassList=self.plotClass.split("OVER")
246                # Reverse lists because 'OVER' means display last
247                plotClassList.reverse()
248                self.vars.reverse()
249                self.infilePaths.reverse()
250            else:
251                plotClassList=[self.plotClass]
252       
253            # Loop through for overlays
254            for count in range(len(plotClassList)):
255                thisPlotClass=plotClassList[count]
256                print thisPlotClass
257                infilePath=self.infilePaths[count]
258                var=self.vars[count]
259               
260                if thisPlotClass.find("MultipleTrajectory")>-1:
261                    area=self.bag.get("mapArea", None)
262                    # Need special case for trajectories where whole file is sent
263                    print "\n%s(vcsCanvas, infilePath, var.id, area, bbox=self.bbox)" % thisPlotClass
264                    plotter=eval("%s(vcsCanvas, infilePath, var.id, area, bbox=self.bbox)" % thisPlotClass)
265                else:
266                    print "\n%s(vcsCanvas, var(squeeze=1), bbox=self.bbox)" % thisPlotClass
267                    plotter=eval("%s(vcsCanvas, var(squeeze=1), bbox=self.bbox)" % thisPlotClass)
268               
269                if thisPlotClass in ["YvsXGraph", "MultipleTrajectory"]:
270                    plotter.createPlot()
271                else:
272                    plotter.createPlot(continentsSwitch=self.contSwitch)
273               
274            # Plot appropriate output format
275            if self.fileFormat=="gif": 
276                vcsCanvas.gif(self.localPath, geometry=self.imageSize) 
277                # Brand output if GIF
278                print "<P>Branding switched off for now...<P>"
279
280            elif self.fileFormat=="postscript": 
281                vcsCanvas.postscript(self.localPath) 
282                       
283        except Exception, error:
284            raise GSPlottingError, error
285
286
287    def _createAnimation(self, vcsCanvas):
288        """
289        If animation is selected then control it here.
290        """
291        self.var=self.vars[0]
292        plotter=eval("%s(vcsCanvas, self.var)" % self.plotClass)       
293               
294        plotType=plotter.plot_type   
295       
296        if self.plotClass.find("Boxfill")>-1:
297            plotType.boxfill_type="custom"
298
299        (valueList, colourList)=self._setUpColourRange()
300        plotType.levels=valueList
301       
302        if self.plotClass.find("Isoline")>-1 and self.plotClass.find("AndFill")<0:
303            plotType.label="y"
304        else:   
305            plotType.fillareacolors=colourList
306        print (valueList, colourList)
307       
308        print plotType.list()
309                       
310        #(loopAxisID, nLoops)=self._getAnimationAxesInfo()
311        (loopAxisIndex, axisSelectionIndices, nLoops)=self._getAnimationAxesInfo()
312
313        indexSelectionString=""
314        for indxTuple in axisSelectionIndices:
315            indexSelectionString=indexSelectionString+("%s:%s," % indxTuple)
316           
317        indexSelectionString="[%s]" % indexSelectionString[:-1]
318        print indexSelectionString
319                       
320        for i in range(nLoops): 
321             vcsCanvas.clear() 
322             indexString=indexSelectionString.replace("LOWER:HIGHER", "%s:%s" % (i, i+1))
323             print "indexString:", indexString
324             plotter.createPlot(animationArgs=indexString, continentsSwitch=self.contSwitch)
325             #plotter.createPlot(animationArgs=(loopAxisID, (i,i+1)), continentsSwitch=self.contSwitch)
326             print "Plotted:", (i,i+1)
327             vcsCanvas.gif(self.localPath, merge="a", geometry=self.imageSize) 
328       
329        print "Animation complete!"
330       
331       
332    def _getAnimationAxesInfo(self):
333        """
334        Works out and returns the index of the axis for looping over,
335        the start and end indices of each axis selection and the number
336        of loops.
337        """
338        axes=self.var.getAxisList()   
339        (xID, yID, loopID)=(self.bag["axisXForAnimation"], self.bag["axisYForAnimation"], self.bag["axisLoopForAnimation"])
340
341        axisSelectionIndices=[]
342           
343        counter=0 
344         
345        for axis in axes:
346            if axis.id==loopID:
347                loopAxisIndex=counter
348                nLoops=len(axis)
349                axisSelectionIndices.append(("LOWER", "HIGHER"))
350            elif axis.id==yID:
351                axisSelectionIndices.append((0, len(axis)))
352                yIndex=counter
353            elif axis.id==xID:
354                axisSelectionIndices.append((0, len(axis)))
355                xIndex=counter
356            else:
357                axisSelectionIndices.append((0, 1)) # only the first value
358       
359            counter=counter+1
360       
361        # Reorder x and y axes if needed
362        if yIndex>xIndex:
363            print "Reordering x and y axes..."
364            axisOrder=range(len(axes))
365            axisOrder[yIndex]=xIndex
366            axisOrder[xIndex]=yIndex
367            axisOrderString="".join([str(i) for i in axisOrder])
368            self.var.reorder(axisOrderString)
369       
370        return (loopAxisIndex, axisSelectionIndices, nLoops)
371       
372        #return (axes[0].id, len(axes[0]))
373       
374       
375        time_axis = self.data.getTime() 
376        lev_axis = self.data.getLevel() 
377        lat_axis = self.data.getLatitude() 
378        lon_axis = self.data.getLongitude()
379 
380        loop_num=1 
381        if time_axis!=None: 
382            loop_flag = 'time' 
383            loop_num = len(time_axis) 
384        if loop_num==1 and lev_axis!=None: 
385            loop_num = len(lev_axis) 
386            loop_flag = 'level' 
387        if loop_num==1 and lat_axis!=None: 
388            loop_num=len(lat_axis) 
389            loop_flag='lat' 
390        if loop_num==1 and lon_axis!=None: 
391            loop_num=len(lon_axis) 
392            loop_flag='lon' 
393       
394        self.loop_num=loop_num
395        self.loop_flag=loop_flag
396       
397         
398    def _setUpColourRange(self):
399        """
400        Creates the appropriate colour range for multiple frames to
401        share a common legend/scale.
402        """
403        # Get the colour range for the legend
404        (mn,mx)=vcs.minmax(self.var)
405        scaleValues=vcs.mkscale(mn, mx, 16)
406        valueList=[] 
407        colourList=[] 
408       
409        try: 
410            d=int(222/(len(scaleValues)-1)) 
411        except: 
412            d=1 
413                   
414        for a in range(len(scaleValues)):
415            colourList.append(16 + a*d)
416            valueList.append(scaleValues[a])
417       
418        return (valueList, colourList)
419
420                       
421if __name__=="__main__":
422    print "Trying to plot trajectory over model field...\n"
423    b=OutputManager({'status': 'complete', 'username': 'undefined', 'outputURLPath': 'http://localhost/gs/output/output_20061101192149.gif', 'graphicalOutputType': 'MultipleTrajectoriesOver2DLatLonPlot_2.2-3.1', 'projection': 'standard', 'proceed': 'Proceed', 'fileURIList': ['/data/trajbadc.nc', '/data/trajcf.nc', '/data/var2.nc'], 'userRoles': [], 'varDict': {'2.2': {'domain': {'grots': [['TrajectoriesOverMap', "Trajectories plotted on map for variable 'o3' from file: '/data/trajcf.nc'"], ['YvsXGraph', "Y-vs-X Graph for variable 'o3' from file: '/data/trajcf.nc'"]], 'time': ['time', 'time', [1.0, 432000.0]], 'ordered_axes': ['time'], 'bbox': (42.299999237060547, 22.5, 67.399986267089844, 126.36080169677734)}, 'id': 'o3'}, '2.2-3.1': {'domain': {'grots': [['MultipleTrajectoriesOver2DLatLonPlot', "Multiple Trajectories for variable 'o3' plotted over 2D Plot for variable 'var2' (files: /data/trajcf.nc, /data/var2.nc)"]], 'bbox': (40.299999237060547, 0, 69.399986267089844, 355)}}, '3.1': {'domain': {'latitude': ['latitude', 'latitude', [-90, 90]], 'grots': [['2DLatLonPlot', "2D Lat-Lon Plot for variable 'var2' from file: '/data/var2.nc'"]], 'ordered_axes': ['time', 'latitude', 'longitude'], 'longitude': ['longitude', 'longitude', [0, 355]], 'time': ['time', 'time', [0, 0]]}, 'id': 'var2'}}, 'fileVariable_2.2': 'o3', 'sessionID': 'session_20061101192119146', 'plotType': 'MultipleTrajectoryOverBoxfillLinear', 'mapArea': 'global', 'fileVariable_3.1': 'var2', 'outputFilePath': '/home/as56/apache/htdocs/gs/output/output_20061101192149.gif', 'fileFormat': 'gif', 'accessTime': 1162408909.777725, 'imageSize': '800x600'})
424    b.createOutputs()
425    sys.exit()   
426   
427    b=OutputManager({'status': 'constructing', 'username': 'undefined', 'imageSize': '800x600', 'graphicalOutputType': 'MultipleTrajectoriesOver2DLatLonPlot_1.5-2.1', 'proceed': 'Proceed', 'fileVariable_1.5': 'temp', 'fileURIList': ['/data/traj.nc', '/data/var2.nc'], 'userRoles': [], 'varDict': {'1.5': {'domain': {'grots': [['TrajectoriesOverMap', "Trajectories plotted on map for variable 'temp' from file: '/data/traj.nc'"], ['2DPlot', "2D Plot for variable 'temp' from file: '/data/traj.nc'"]], 'parcel': ['parcel', 'unrecognised', [0.0, 1.0]], 'bbox': (42.299999237060547, 22.500001907348633, 67.399993896484375, 126.36080169677734), 'ordered_axes': ['time', 'parcel'], 'time': ['time', 'time', [0.0, 432000.0]]}, 'id': 'temp'}, '2.1': {'domain': {'latitude': ['latitude', 'latitude', [-90, 90]], 'grots': [['2DLatLonPlot', "2D Lat-Lon Plot for variable 'var2' from file: '/data/var2.nc'"]], 'ordered_axes': ['time', 'latitude', 'longitude'], 'longitude': ['longitude', 'longitude', [0, 355]], 'time': ['time', 'time', [0, 0]]}, 'id': 'var2'}, '1.5-2.1': {'domain': {'grots': [['MultipleTrajectoriesOver2DLatLonPlot', "Multiple Trajectories for variable 'temp' plotted over 2D Plot for variable 'var2'"]], 'bbox': (42.299999237060547, 0, 67.399993896484375, 355)}}}, 'sessionID': 'session_20061101181119418', 'fileVariable_2.1': 'var2', 'plotType': 'MultipleTrajectoryOverBoxfillLinear', 'mapArea': 'global', 'fileFormat': 'gif', 'accessTime': 1162404696.146235, 'projection': 'standard'}
428)
429    b.createOutputs()
430    sys.exit()
431   
432    b=OutputManager({'mapArea':"global", 'outputURLPath': 'http://localhost/gs/output/output_20061004002742.gif', 'graphicalOutputType': 'TrajectoriesOverMap_1.1', 'projection': 'standard', 'getOutput':"getOutput", 'fileURIList': ['/data/traj.nc'], 'fileVariable_1.1': 'lat', 'sessionID': 'session_20061004002735159', 'plotType': 'MultipleTrajectory', 'outputFilePath': '/home/as56/apache/htdocs/gs/output/output_20061004002742.gif', 'fileFormat': 'gif', 'imageSize': '800x600'})
433    b.createOutputs()
434    b=OutputManager({'fileVariable_1.1':'t', 'status': 'constructing', 'username': 'jane', 'accessTime': 1142557842.2275701, 'fileURIList': ['/data/dx/0d.nc'], 'userRoles': [], 'sessionID': 'session_20060317011041792', 'graphicalOutputType': 'PointValueAndMetadata_1.1', 'getOutput': 'getOutput'})
435
436    b=OutputManager({'status': 'constructing', 'username': None, 'outputURLPath': 'http://localhost/output/test.gif', 'fileURIList': ['/data/var2.nc'], 'imageSize': '300x200', 'confirm': 'Confirm', 'accessTime': 1142551623.3794341, 'userRoles': [], 'sessionID': 'session_20060316232622350', 'graphicalOutputType': '2DLatLonPlot_1.1', 'plotType': 'boxfill', 'proceed': 'Proceed', 'action': 'createOutput', 'outputFilePath': '/srv/www/htdocs/output/test.gif', 'fileFormat': 'postscript', 'getOutput': 'getOutput', 'fileVariable_1.1': 'var2'})
437    b.createOutputs()
438    sys.exit()
439
440    a=OutputManager({'status': 'constructing', 'username': 'jane', 'accessTime': 1142325307.7825251, 'imageSize': '800x600', 'fileVariable_1.3': 'pqn', 'fileURIList': ['/tmp/ani.nc'], 'userRoles': [], 'sessionID': 'session_20060314083506222', 'graphicalOutputType': 'Animation_1.3', 'plotType': 'isofill', 'continentsSwitch': 'on', 'fileFormat': 'gif', 'getOutput': 'getOutput', 'projection': 'standard'})
441    a.createOutputs()
442    sys.exit()
443    a=OutputManager({'status': 'constructing', 'username': 'jane', 'accessTime': 1142325307.7825251, 'imageSize': '800x600', 'fileVariable_1.2': 'var1', 'fileURIList': ['/tmp/tmp.nc'], 'userRoles': [], 'sessionID': 'session_20060314083506222', 'graphicalOutputType': '2DPlot_1.2', 'plotType': 'boxfill', 'continentsSwitch': 'on', 'fileFormat': 'gif', 'getOutput': 'getOutput', 'projection': 'standard'})
444    a.createOutputs()   
445    sys.exit()
446
447    o=OutputManager({"plotType":"isolineAndFill", "projection":"standard",
448                     "continentsSwitch":"on", "imageSize":"800x600",
449                     "fileFormat":"postscript", "fileURIList":["/tmp/tmp.nc"],
450                     "fileVariable_1.1":"var1", "graphicalOutputType":"2DPlot_1.1"})
451
452    o.createOutputs()
453    print o.__dict__   
454    if o.localPath.split(".")[-1]=="gif":
455        util="gifview"
456    else:
457        util="gv"
458    os.system("%s %s&" % (util, o.localPath))
459    import sys; sys.exit()
460    o=OutputManager({"plotType":"isofill", "projection":"standard",
461                     "continentsSwitch":"off", "imageSize":"600x400",
462                     "fileFormat":"gif", "fileURIList":["/tmp/tmp2.nc"],
463                     "fileVariable_1.1":"myway", "graphicalOutputType":"2DPlot_1.1"})
464    o.createOutputs()
465    print o.__dict__   
466    os.system("gifview %s&" % o.localPath)   
Note: See TracBrowser for help on using the repository browser.