source: TI02-CSML/trunk/csml/csmllibs/csmldataiface.py @ 3279

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/csml/csmllibs/csmldataiface.py@3279
Revision 3279, 29.3 KB checked in by domlowe, 12 years ago (diff)

docstrings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/env python
2
3'''
4**************************************************************************************
5csmldataIface.py
6contains classes for interfacing with various files
7currently supports cdunif (NetCDF, PP, Grib(untested)) And Nappy (NASAAmes)
8use by instantiating the factory class: DataInterface
9last updated 28 January 2008
10Dominic Lowe, BADC
11**************************************************************************************
12'''
13
14import pdb
15import cdms 
16import Numeric
17
18try:
19    import nappy 
20except ImportError:
21    print "NASAAmes interface not available. CSML will still work, but won't support NASA Ames files"
22import string
23import sys
24import csml.csmllibs.csmltime
25# This is required to prevent Numeric arrays being truncated when printed.
26import MA
27MA.set_print_limit(0)
28
29# Generic mathematical expression solver, required by the raw and image
30# interfaces
31import NumericTransform
32
33try:
34    # Part of the PIL. Required for ImageFileInterface:
35    import Image
36except ImportError:
37    print "Python Image Library not available. CSML will still work, but won't support image files"
38
39try:
40    # Required for RawFileInterface:
41    import struct
42except ImportError:
43    print 'could not import struct'
44    pass
45
46
47class DataInterface(object):
48        '''DataInterface is the entry point for all data types. Use DataInterface and setInterfaceType to instantiate the correct
49        subclass for data''' 
50        def __init__(self):
51                self.iface ='None'
52               
53        def setInterfaceType(self,interfaceType):
54                '''returns approprate data interface
55                @param interfaceType:     should correspond to available datainterface subclasses
56                @return:    a subclass of DataInterface e.g. a ImageFileInterface'''
57                self.iface =interfaceType
58                if self.iface == 'nappy':
59                        return NappyInterface()
60                elif self.iface == 'cdunif':
61                        return cdunifInterface()
62                elif self.iface == 'raw':
63                        return RawFileInterface()
64                elif self.iface == 'image':
65                        return ImageFileInterface()
66               
67               
68        def getUnknownInterfaceType(self, filename):
69                '''if the interface type is not known at the time of instantiation, then use
70                this method to examine the file and return the correct interface (if it exists).
71                @param filename:      name of data file'''
72                fileExtension = str(filename)[-3:]
73                if fileExtension == '.nc':
74                        return cdunifInterface()
75                if fileExtension == 'qxf':
76                        return cdunifInterface()
77                elif fileExtension == '.pp':
78                        return cdunifInterface()
79                elif fileExtension == 'ctl':
80                        return cdunifInterface()
81                elif fileExtension == 'xml':
82                        return cdmlInterface()
83                elif fileExtensions == 'png' or fileExtension == 'gif':
84                        return ImageFileInterface()
85                else:
86                        try:
87                                nappy.readFFI(filename) in [1001,1010,1020,2010,2110,2160,2310,3010,4010]
88                                return NappyInterface()
89                        except:
90                                print "Could not establish file type"
91                                print "See csmldataiface.py"
92                       
93       
94                                                       
95class AbstractDI(object):               
96        '''Abstract data interface class
97        does nothing, but contains templates for methods required for a data interface class
98        individual interfaces (e.g NappyInterface) should override these methods. This is the minimum
99        set required for data access and subsetting.
100        Note that if scanning of data (for CSML creation) is required more methods may be needed - see cdunif interface'''
101
102        def __init__(self):
103                ''' When subclassed: unique 'type'  and 'prefix' must be defined'''
104                self.extractType=''
105                self.extractPrefix = ''
106                                       
107        def openFile(self, filename):
108           ''' When subclassed: opens file, sets self.file'''
109           raise NotImplementedError 
110               
111        def closeFile(self):
112           '''closes file, probably needs to be overwritten by subclass'''
113           try:
114               self.file.close()
115           except:
116               raise NotImplementedError
117         
118        def setAxis(self,axis):
119               '''set' the name of the current axis , must be overwritten by subclass
120           this may just involve a stub (see NASAAmes interface) or may involve
121           calling a real set method of the underlying api (see cdunif Interface)'''
122               raise NotImplementedError 
123                       
124        def getDataForAxis(self):
125               ''' return all data for axis, must be overwritten by subclass '''
126               raise NotImplementedError       
127       
128        def setVariable(self,varname):
129             '''As for setAxis, 'set' the name of the current axis , must be overwritten by subclass'''
130             raise NotImplementedError
131       
132        def getDataForVar(self):
133               '''return all data for variable, must be overwritten by subclass'''
134               raise NotImplementedError       
135       
136        def getSubsetOfDataForVar(self,**kwargs):
137            '''return subset of data for variable, must be overwritten by subclass'''
138            raise NotImplementedError
139       
140       
141class NappyInterface(AbstractDI):       
142        ''' Data Interface for Nappy (NASA Ames Processing in Python)'''
143   
144        def __init__(self):
145                self.extractType='NASAAmesExtract'
146                self.extractPrefix = '_naextract_'
147                                         
148        def openFile(self, filename):
149                ''' open file'''
150                self.file=nappy.openNAFile(filename)
151
152        def getListOfAxes(self):
153                ''' gets list of axes in file'''
154                axes=self.file.XNAME
155                #print 'before units stripped' + str(axes)
156                axes=self.stripunits(axes)
157                #print 'after units stripped' + str(axes)
158                return axes
159
160        def setAxis(self,axis):
161                '''sets selected axis in Data Interface'''
162                axes = self.getListOfAxes()
163                self.axisstub=axes.index(axis)
164
165        def getAxisAttribute(self, att):
166                ''' not implemented yet -should return axis attribute'''
167                #need to do something here...? maybe
168                attValue=None
169                return attValue
170
171        def getTimeUnits(self):
172                ''' returns units of time axis'''
173                axes = self.getListOfAxes()
174                for axis in axes:
175                        if string.find(string.upper(axis),'SECONDS SINCE') != -1:
176                                #found possible time axis.
177                                if axis[-3:]=='UTC':
178                                    units =string.lower(axis[:-4]) #hack!
179                                    units=units.replace('/','-') #try and clean it up
180                                else:
181                                    units=string.lower(axis)
182                                break
183                        elif string.find(string.upper(axis),'HOURS SINCE') != -1:
184                                #found possible time axis.
185                                units =(str(axis))
186                                break
187                        elif string.find(string.upper(axis),'DAYS SINCE') != -1:
188                                #found possible time axis.
189                                units =(str(axis))
190                                break
191                       
192                #revisit with udunits python library?
193                return units
194
195
196        def getDataForAxis(self):           
197            ''' gets data for an axis'''
198            if self.file.X == None:
199                    #print 'reading data....'
200                    self.file.readData()
201            for x in self.file.X:
202                pass
203                #print type(x)
204           
205           
206            d=Numeric.array(self.file.X)
207           
208            if type(self.file.X[1])==list:
209            #if len(self.file.X) > 0:
210                    data = self.file.X[self.axisstub]
211            else:
212                    data =self.file.X
213            #print data
214            data=Numeric.array(data)
215            return data
216
217        def getSizeOfAxis(self,axis):
218                ''' gets size of an axis'''
219                #check this function is okay
220                #is time always the first dimension in NA??
221                axes = self.getListOfAxes()
222                axisPosition=axes.index(axis)
223                #print "axis position" + str( axisPosition)
224                #print "NX" + str(self.file.NX)
225                try :
226                        axisSize=self.file.NX[axisPosition-1]
227                except:
228                        axisSize ='Unknown axis size'
229                return axisSize
230
231        def getListofVariables(self):
232                '''gets list of variables in the file'''
233                variableList=self.stripunits(self.file.VNAME)
234                return variableList
235
236        def setVariable(self,varname):
237                ''' sets the specified variable to be the selected variable'''
238                vlist=self.getListofVariables()
239                self.varstub=vlist.index(varname)
240
241        def getVariableAxes(self):
242                ''' Get axis names for a  variable '''
243                #hmm, now with Nasa Ames the Axis will be the same for all variables.
244                #so just use the getListOfAxes function again
245                #I think... check this!
246                varAxesList=self.getListOfAxes()
247                return varAxesList
248
249        def getVariableAttribute(self,attName):
250                ''' get an attribute for a variable'''
251                if attName =='units':
252                        #strip the units (attribute) from the variable
253                        unitslist=self.getUnits(self.file.VNAME)
254                        attribValue = unitslist[self.varstub]
255                        try:
256                                attribValue = unitslist[self.varstub]
257                        except:
258                                attribValue = 'unknown'
259                else:
260                        attribValue = 'unknown'
261                return attribValue
262
263        def getDataForVar(self):
264            ''' get data for a  variable'''
265            #NOTE TO SELF:
266            #Review this function (and in fact all of nasa ames data interface...)
267            if self.file.V == None:
268                    #print 'reading data....'
269                    self.file.readData()
270
271            try:
272                if type(self.file.V[1])==list:
273                    data = self.file.V[self.varstub]
274            #else:
275            #   data =self.file.X
276            #   print data
277                return data
278            except:
279                data = self.file.X
280                # print data
281                return data
282
283        def getArraySizeOfVar(self):
284            '''iterates through all dimensions in variable to get array size i.e a 3x3x3 array would have a size of 27'''
285
286            dimlist=self.file.NX
287            varsize =1
288            for item in dimlist:
289                    varsize = varsize * item
290                    #print "VARSISZE" + str(varsize)
291            return varsize
292
293        def getShapeOfVar(self):
294            '''this returns a list with the shape of the variable'''
295            varShape = []
296            for item in self.file.NX:
297                varShape.append(item)
298            return varShape
299
300        def getLowLimits(self):
301            ''' gets low limits of the data'''
302            lowlims = ""
303            for i in range (0, len(self.file.NX)):
304                    #for now, assume low limit is always of form 1 1 1 ..
305                    lowlims =lowlims + str(1)  +' '
306            return lowlims
307
308        def getHighLimits(self):
309            ''' gets high limits of the data'''
310            highlims = ""
311            for i in range (0, len(self.file.NX)):
312                    dimValue = self.file.NX[i]
313                    highlims =highlims  + str(dimValue) +' '
314            return highlims
315
316
317        def stripunits(self,listtostrip):
318            ''' strips units of measure from list
319            eg ['Universal time (hours)', 'Altitude (km)', 'Latitude (degrees)', 'Longitude (degrees)']
320            becomes ['Universal time', 'Altitude', 'Latitude', 'Longitude']'''
321            cleanlist = []
322            for item in listtostrip:
323                    openbracket=string.find(item,'(')
324                    if openbracket != -1:
325                            #if brackets exist, strip units.
326                            item=item[:openbracket-1]
327                    cleanlist.append(item)
328            return cleanlist
329
330        def getUnits(self,listwithunits):
331            '''gets units from list
332            eg ['Universal time (hours)', 'Altitude (km)', 'Latitude (degrees)', 'Longitude (degrees)']
333            becomes ['hours', 'km', 'degrees', 'degrees']'''
334            unitlist=[]
335            for item in listwithunits:
336                    openbracket=string.find(item,'(')
337                    item = item[openbracket+1:-1]
338                    unitlist.append(item)
339            return unitlist
340
341        def getTimes(self):
342            '''This function attempts to determine the time axis and read the time data
343            it may well not manage it.'''
344            axes = self.getListOfAxes()
345            for axis in axes:
346                if string.find(string.upper(axis),'TIME') != -1:
347                        #found possible time axis.
348                        self.setAxis(axis)
349                        times=self.getDataForAxis()
350                        break
351                elif string.find(string.upper(axis),'SECONDS SINCE') != -1:
352                        #found possible time axis.
353                        self.setAxis(axis)
354                        times=self.getDataForAxis()
355                        break
356                elif string.find(string.upper(axis),'HOURS SINCE') != -1:
357                        #found possible time axis.
358                        self.setAxis(axis)
359                        times=self.getDataForAxis()
360                        break
361                elif string.find(string.upper(axis),'DAYS SINCE') != -1:
362                        #found possible time axis.
363                        self.setAxis(axis)
364                        times=self.getDataForAxis()
365                        break
366            times=Numeric.array(times)
367            return times
368
369
370
371class cdunifInterface(AbstractDI):
372    '''Data Interface for cdunif (netcdf & pp formats & grib (not tested with grib)'''
373
374    def __init__(self):
375        '''these are just temporary values until we can determine whether the
376        file is netcdf pp or grib'''
377        self.extractType='cdunifExtract'
378        self.extractPrefix = '_cdunifextract_'
379   
380    def openFile(self, filename):
381        ''' open file'''
382        self._filename=filename
383        self.file=cdms.open(filename)
384       
385        #now we have the file name can properly determine extractType/Prefix
386        fileExtension = str(filename)[-3:]
387        if fileExtension == '.nc':
388            self.extractType = 'NetCDFExtract'
389            self.extractPrefix = '_ncextract_'
390        elif fileExtension == '.qxf':
391            self.extractType = 'NetCDFExtract'
392            self.extractPrefix = '_ncextract_'
393        elif fileExtension == '.pp':
394            self.extractType  = 'PPExtract'
395            self.extractPrefix = '_ppextract_'
396        elif fileExtension == 'ctl':
397            self.extractType = 'GRIBExtract'
398            self.extractPrefix = '_gribextract_'
399        elif fileExtension == 'xml': 
400            self.extractType = 'NetCDFExtract'  #okay this isn't true, but ok for testing
401            self.extractPrefix = '_ncextract__' 
402   
403    def getListOfAxes(self):
404        ''' returns list of dimensions/axes'''
405        axes=self.file.dimensions.keys()
406        return axes
407
408    def getSizeOfAxis(self,axis):
409        ''' returns size of axis'''
410        axisSize=self.file.dimensions[axis]
411        return axisSize
412
413    def getListofVariables(self):
414        ''' returns list of variables'''
415        variableList=self.file.variables.keys()
416
417        # Hack to test if removing climatology_bounds fixes pywms bug
418        if 'climatology_bounds' in variableList:
419            variableList.remove('climatology_bounds')
420
421        return variableList
422
423    def setAxis(self,axis):
424        ''' set axis'''
425        if not hasattr(self, 'file'):
426            raise Exception, 'Could not open/find underlying file: %s  -  If you are the system maintainer check file paths and permissions'%self._filename
427        self.axisobj=self.file.getAxis(axis)
428
429           
430
431    def getAxisAttribute(self, att):
432        ''' get attribute of set axis'''
433        attValue=self.axisobj.attributes[att]
434        return attValue
435   
436    def getTimeUnits(self):
437        ''' get time units - only works if set axis is time...'''
438        #this does the same as getAxisAttribute, but is a separate function as different formats handle time differently.
439        return self.getAxisAttribute('units')
440
441    def getDataForAxis(self):
442        ''' get data for entire axis'''
443        data = self.axisobj.getValue()
444        return data
445
446    def setVariable(self,varname):
447        ''' sets variable '''
448        self.varobj=self.file.variables[varname]
449
450    def getVariableAxes(self):
451        ''' gets axes for set variable'''
452        varAxesList=self.varobj.getAxisIds()
453        return varAxesList
454
455    def getVariableAttribute(self,attName):
456        ''' gets requested attribute for set variable'''
457        if attName == '_FillValue':
458            try:
459                attribValue=self.varobj._FillValue
460                attribValue=attribValue.toscalar()
461            except:
462                try:
463                    attribValue=self.varobj.missing_value
464                    attribValue=attribValue.toscalar()
465                except:
466                    attribValue = None 
467        else:
468            attribValue = self.varobj.getattribute(attName)
469        return attribValue
470
471    def getDataForVar(self):
472        ''' get data for set variable'''
473        data = self.varobj.getValue()
474        return data
475   
476    def _fixLongitudeRequest(self, **kwargs):
477        ''' fixes problem of longitude requests taking different forms'''
478        lonkey='longitude'
479        if lonkey in kwargs.keys():   #this test needs to be much more robust...!
480            if  type(kwargs[lonkey]) is tuple:
481                newkws=[]
482                for val in kwargs[lonkey]:
483                    newval=((val + 180) % 360) - 180
484                    newkws.append(newval)
485               
486                 #lon = ((t_lon + 180) % 360) - 180
487                #lonmin=kwargs[lonkey][0] - (kwargs[lonkey][0]/180)*360.0
488                #lonmax=kwargs[lonkey][1] - (kwargs[lonkey][1]/180)*360.0
489               
490                #if newkws[0] > newkws[1]:
491                    #flip=[newkws[1], newkws[0]]
492                    #newkws=flip
493                kwargs[lonkey]=tuple(newkws)
494        return kwargs
495               
496    def _componentTimes(self, times):
497        ''' handles time formats'''
498        if type(times) is tuple:
499            comptimes=[]
500            for t in times:
501                compt=csml.csmllibs.csmltime.getCDtime(t)
502                comptimes.append(compt)
503            result=tuple(comptimes)
504        else: #just a single time
505            result=csml.csmllibs.csmltime.getCDtime(times)
506        return result
507       
508   
509    def getSubsetOfDataForVar(self, **kwargs):     
510        ''' Main subsetting method. Kwargs contains dictionary of requested subset.'''
511        #put any slicing indices aside for later and use names       
512        try:
513            upper=kwargs['upper']
514            del kwargs['upper']
515        except:
516            pass
517        try:
518            lower=kwargs['lower']
519            del kwargs['lower']
520        except:
521            pass
522        if 'time' in kwargs:
523            kwargs['time']=self._componentTimes(kwargs['time'])
524       
525        #kwargs=self._fixLongitudeRequest(**kwargs)
526        #return self.file(self.varobj.id,**kwargs)
527        try:
528            #takes keyword args defining subset eg
529            #subset=getSubsetOfDataForVar(latitude=(0.,10.0), longitude=(90, 100.0))
530            subset=None
531            lonkey='longitude'
532            if lonkey in kwargs.keys():   #this test needs to be much more robust...!
533                if  type(kwargs[lonkey]) is tuple:
534                    if kwargs[lonkey][0] > kwargs[lonkey][1]:
535                        #subsetting greenwich meridian around 0
536                        lonMin = kwargs[lonkey][0]
537                        lonMax =kwargs[lonkey][1]
538                        kwargs[lonkey]=(0.1, lonMax)
539                        subset1=self.file(self.varobj.id,**kwargs)
540                        kwargs[lonkey]=(lonMin,360)
541                        try:
542                            subset2=self.file(self.varobj.id,**kwargs)
543                            longitudeAxis=subset1.getAxisIndex(lonkey)
544                            #concatenate arrays along longitude             
545                            subset = cdms.MV.concatenate([subset2,subset1],axis=longitudeAxis)
546                        except:
547                            subset=subset1
548            if type(subset) is not cdms.tvariable.TransientVariable:
549                subset=self.file(self.varobj.id,**kwargs)
550        except:             
551                return None
552                #try and slice with indices instead
553        return subset
554   
555   
556    def getArraySizeOfVar(self):
557        '''iterates through all dimensions in variable to get array size i.e a 3x3x3 array would have a size of 27'''
558        var = self.varobj
559        size = var.shape
560        varsize = 1
561        for item in size:
562            varsize = item *varsize
563        return varsize
564
565    def getShapeOfVar(self):
566        ''' get shape of set variable '''
567        varShape = []
568        for item in self.varobj.shape:
569            varShape.append(item)
570        return varShape
571
572    def getLowLimits(self):
573        ''' get gml low limits of set variable'''
574        dimNames = self.varobj.getAxisIds()
575        lowlims = ""
576        for i in range (1, len(dimNames)):
577            #for now, assume low limit is always of form 1 1 1 ..
578            lowlims =lowlims + str(1)  +' '
579        return lowlims
580
581    def getHighLimits(self):
582        ''' get gml high limits of set variable'''
583        dimNames = self.varobj.getAxisIds()
584        highlims = ""
585        for i in range (1, len(dimNames)):
586            dimValue = self.file.dimensions[dimNames[i]]
587            highlims =highlims  + str(dimValue) +' '
588        return highlims
589       
590   
591class cdmlInterface(cdunifInterface):
592    ''' this is more  or less the cdunif interface but a few methods have been overwritten to read CDML'''
593    def __init__(self):
594        #this all needs to be revisited in csml v2.
595        self.extractType='cdmlExtract'
596        self.extractPrefix = '_cdmlextract_'
597       
598    def getListOfAxes(self):
599        ''' get list of axis in file'''
600        axes=self.file.axes.keys() 
601        return axes
602
603    def getSizeOfAxis(self,axis):
604        ''' get size of set axis'''
605        axisSize=self.file.axes[axis].length
606        return axisSize
607
608
609class RawFileInterface(AbstractDI):
610    ''' interface for raw file types'''
611    def __init__(self):
612        self.extractType   = 'rawExtract'
613        self.extractPrefix = '_rawextract_'
614 
615 
616    def openFile(self, filename):
617        self.file = open(filename, "rb")
618
619    def closeFile(self):
620            self.file.close()
621
622
623    # Read the data from the raw file into a multidimensional Numeric array
624    def readFile(self, **kwargs):
625        # Determine the numeric type:
626        if 'numericType' in kwargs:
627            try:
628                numericTypeCode = {
629                    'uint8':Numeric.UInt8,
630                    'uint16':Numeric.UInt16,
631                    'uint32':Numeric.UInt32,
632                    'int8':Numeric.Int8,
633                    'int16':Numeric.Int16,
634                    'int32':Numeric.Int32,
635                    'float':Numeric.Float32,
636                    'double':Numeric.Float64
637                }[kwargs['numericType']]
638            except KeyError:
639                raise TypeError("Invalid numericType: " + str(kwargs['numericType']))
640        else:
641            raise KeyError("numericType is mandatory.")
642       
643        # Read the file into a numpy array:
644        self.data = Numeric.fromstring(self.file.read(), numericTypeCode)
645        # If necessary, swap the byte order:
646        if 'endianess' in kwargs:
647            if ((kwargs['endianness'] == 'little' and not Numeric.LittleEndian) or (kwargs['endianness'] == 'big' and Numeric.LittleEndian)):
648                self.data = self.data.byteswapped()   
649        # Declare the shape of the array:
650        dimensions = map(int,kwargs['dimensions'])
651        dimensions.reverse()
652        self.data.shape = tuple(dimensions)
653        # If numericTransform or fillValue were provided, store them as
654        # attributes.
655        if 'numericTransform' in kwargs:
656            self.numericTransform = NumericTransform.infixExpression(kwargs['numericTransform'])
657        if 'fillValue' in kwargs:
658            self.fillValue = kwargs['fillValue']
659   
660   # Return the fill value, if set, and transform if necessary:
661    def getFillValue(self):
662      # Both fillValue and numericTransform attributes may or may
663      # not exist...
664        try:
665            return self.numericTransform.solve( n = float(self.fillValue) )
666        except AttributeError:
667            try:
668                return self.fillValue
669            except AttributeError:
670                return None
671
672
673   # This is a just a special case of getSubsetOfDataForVar. To avoid
674   # duplication of code, just subset the entire array. (getSubset.. is
675   # optimised for this case)
676    def getDataForVar(self):
677        return self.getSubsetOfDataForVar(lower = (0,)*len(self.data.shape),
678                                        upper = self.data.shape)
679
680
681   # Subset the n-dimensional file based on array indices. Accepts parameters
682   # in the form of e.g.
683   #
684   # getSubsetOfDataForVar( lower=(0,0), upper=(1,2) )
685   #
686   # Note: The rank of the required subset, must exactly match the
687   # rank of the original data: len(lower) == len(upper) == rank of file
688   #
689    def getSubsetOfDataForVar(self, **kwargs):
690        # Assume subset parameters are passed as: lower=(0,0) upper=(512,512)
691        if 'lower' not in kwargs or 'upper' not in kwargs:
692            # Have not specified any subset parameters that we recognise, so raise
693            # an exception:
694            raise NotImplementedError("Only supports subsetting with lower+upper array indices")
695        elif not len(kwargs['lower']) == len(kwargs['upper']) == len(self.data.shape):
696            # Rank of subset is not the same as rank of full data array. so raise
697            # an exception:
698            raise NotImplementedError("Only supports subsets of same rank as full dataset")
699        elif Numeric.sometrue(Numeric.greater(kwargs['upper'], self.data.shape)):
700            # Requested upper bound of subset is beyond the size of the the full
701            # data array, so raise an exception
702            raise IndexError("Subset out of range")
703        elif Numeric.sometrue(Numeric.less( kwargs['upper'], Numeric.zeros(len(self.data.shape)))):
704            # Requested lower bound of subset is beyond the size of the the full
705            # data array, so raise an exception
706            raise IndexError("Subset out of range")
707        elif Numeric.sometrue(Numeric.less_equal(kwargs['upper'], kwargs['lower'])):
708            # lower bound <= upper_bound for at least one dimension, so raise an
709            # exception
710            raise IndexError("Upper bound less than lower bound")
711        elif tuple(kwargs['lower']) == (0,)*len(self.data.shape) and tuple(kwargs['upper']) == self.data.shape:
712            # Special case of requested subset == entire data file.
713            subset = self.data
714        else:
715            # We are okay to subset.
716   
717            # I cant see any nice (and speedy) way of subsetting a Numeric
718            # array of arbitrary rank without the use of eval. By doing it
719            # this way, we can make use of the (possible) acceleration provided
720            # by Numeric/NumPy.
721            slices = []
722            for i in range(len(self.data.shape)):
723                lower = int(kwargs['lower'][i])
724                upper = int(kwargs['upper'][i]) 
725                slices.append(str(lower)+':'+str(upper))
726            subset = eval("self.data["+','.join(slices)+"]")
727   
728        # Attempt to perform the numericTransform on the data array, if we get
729        # AttributeError, it most likely means that numericTransform was not
730        # specified, so return the data as-is
731        try:
732            return self.numericTransform.solve( n = subset )
733        except AttributeError:
734            return subset.copy()
735
736class ImageFileInterface(RawFileInterface):
737    '''Interface for reading data from image files. Requires PIL Image module.'''
738    def __init__(self):
739        self.extractType   = 'imageExtract'
740        self.extractPrefix = '_imageextract_'
741   
742    def image2array(self,im):
743        #Adapted from code by Fredrik Lundh, http://www.pythonware.com
744        #http://effbot.org/zone/pil-numpy.htm
745            if im.mode not in ("L", "F"):
746                raise ValueError, "can only convert single-layer images"
747            if im.mode == "L":
748                a = Numeric.fromstring(im.tostring(), Numeric.UnsignedInt8)
749            else:
750                a = Numeric.fromstring(im.tostring(), Numeric.Float32)
751            a.shape = im.size[1], im.size[0]
752            return a
753
754    def openFile(self, filename):
755        self.file = Image.open(filename)
756
757    def closeFile(self):
758        self.file = None #...Image does not seem to have a close() method.
759
760    def readFile(self, **kwargs):
761        # Convert the image to a Numeric array
762     
763        self.data=self.image2array(self.file)
764        #slower method:
765        #self.data = Numeric.array(self.file.getdata())
766
767        if 'numericTransform' in kwargs:
768            # numericTransform was specified, so compile the expression:
769            self.numericTransform = NumericTransform.infixExpression(kwargs['numericTransform'])
770        if 'fillValue' in kwargs:
771            self.fillValue = kwargs['fillValue']
772
773class HDF5Interface(AbstractDI):
774    '''Data Interface for HDF5'''
775    def __init__(self):
776        self.extractType='hdfExtract'
777        self.extractPrefix='_hdfextract_'
778   
779    def openFile(self, filename):
780        '''some code to open the file'''
781        pass
782
783    def setAxis(self,axis):
784        '''some code to set an axis to be queried, may not need to do much, depending on your format'''
785        pass
786       
787    def getDataForAxis(self):
788        '''some code to return the values for an axis'''
789        return data
790
791    def setVariable(self,varname):
792        '''some code to set a variable to be queried, may not need to do much, depending on your format'''
793        pass
794
795    def getDataForVar(self):
796        '''some code to return all values for a variable'''
797        return data
798
799    def getSubsetOfDataForVar(self, **kwargs):
800        '''takes keyword args defining subset eg
801        subset=getSubsetOfDataForVar(latitude=(0.,10.0), longitude=(90, 100.0), ...)
802        and returns a subset of data for tha variable '''
803        return data
804
805    def closeFile(self):
806        '''some code to close the file'''
807        pass
Note: See TracBrowser for help on using the repository browser.