source: TI02-CSML/trunk/csml/csmllibs/csmlextra.py @ 3917

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

fixed bug in Bounding Box aggregator code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#Stuff with nowhere better to go
2import random, datetime
3import string
4import csml.parser
5import os, sys
6
7def getRandomID():
8    '''returns a random 8 character alphanumeric string that can be used as an internal identifier in a CSML document.
9    the ID only needs to be unique within the document that it occurs, so this is sufficiently random
10    to make a clash extremely unlikely. The string is alphanumeric but always begins with an upper case letter. 
11    Note: tested with 50000 ids and all were different'''
12    randomID=random.choice(string.letters)
13    randomID=string.upper(randomID)
14    for i in range(7):
15        randomID=randomID+random.choice(string.letters+string.digits)
16    return randomID
17
18def _locateByGmlId(elem, idtofind):
19    ''' look for child element with particular id'''
20    it=elem.getiterator()
21    for i in it:
22        if '{http://www.opengis.net/gml}id' in i.attrib:
23            if i.attrib['{http://www.opengis.net/gml}id'] == idtofind:
24                return i
25
26
27def _getEmptyCSMLobj(tag):
28    '''given an elementtree tag name, such as SpatialOrTemporalPositionList
29    return a parser object such as csml.parser.SpatialOrTemporalPosition()'''
30    csmlname=tag.split('}')[1]
31    return csml.parser.__dict__[csmlname]()       
32
33
34def getObjbyGMLID(elem,idtofind):
35    '''Given an element tree object and a gml:id return the fully parsed CSML object
36    that matches the id
37    useful for finding xlink matches'''
38    matchingelement= _locateByGmlId(elem, idtofind)
39    csmlobj=_getEmptyCSMLobj(matchingelement.tag)
40    csmlobj.fromXML(matchingelement)
41    return csmlobj
42
43def strToDate(ymd):
44    '''given a string in form '2006-01-01' returns a datetime object'''
45    return datetime.date(*map(int, ymd.split('-')))
46
47class EnvelopeAggregator(object):
48    def __init__(self,envelope):
49        '''start with aggregated envelope equal to the initialising envelope
50        envelope must be of type csml.parser.EnvelopeWithTimePeriod'''
51        self.minX=envelope.lowerCorner.CONTENT.split()[0]
52        self.minY=envelope.lowerCorner.CONTENT.split()[1]
53        self.maxX=envelope.upperCorner.CONTENT.split()[0]
54        self.maxY=envelope.upperCorner.CONTENT.split()[1]
55        self.t1= strToDate(envelope.beginPosition.CONTENT)
56        self.t2= strToDate(envelope.endPosition.CONTENT)
57 
58    def _compareLowerCorners(self,lowerCorner):
59        minX,minY=lowerCorner.CONTENT.split()[0],lowerCorner.CONTENT.split()[1]
60        if float(minX) < float(self.minX):
61            self.minX=minX
62        if float(minY) < float(self.minY):
63            self.minY=minY
64           
65    def _compareUpperCorners(self,upperCorner):
66        maxX,maxY=upperCorner.CONTENT.split()[0],upperCorner.CONTENT.split()[1]
67        if float(maxX) > float(self.maxX):
68            self.maxX=maxX
69        if float(maxY) > float(self.maxY):
70            self.maxY=maxY
71           
72    def _compareLowerTimes(self,timepos):
73        t=strToDate(timepos)
74        if t<self.t1:
75            self.t1=t
76       
77    def _compareUpperTimes(self,timepos):
78        t=strToDate(timepos)
79        if t>self.t2:
80            self.t2=t
81   
82    def compareEnvelope(self,envtocheck):
83        '''compares new envelope, and if necessary changes the aggregated envelope.'''
84        #if envtocheck.srsName!=self.envelope.srsName:
85            #print 'Skipping envelope with srs %s Can currently only perform aggregation within the same spatio-temporal reference system.'%envtocheck.srsName           
86        #else:
87        self._compareLowerCorners(envtocheck.lowerCorner)
88        self._compareUpperCorners(envtocheck.upperCorner)
89        self._compareLowerTimes(envtocheck.beginPosition.CONTENT)
90        self._compareUpperTimes(envtocheck.endPosition.CONTENT)
91   
92    def getAggregatedEnvelope(self):
93        ''' get aggregation of all envelopes in one aggregated envelope'''
94        newenvelope=csml.parser.EnvelopeWithTimePeriod()
95        newenvelope.beginPosition=csml.parser.csString(str(self.t1)) #convert from datetime types
96        newenvelope.endPosition=csml.parser.csString(str(self.t2)) 
97        newenvelope.lowerCorner=csml.parser.csString(str(self.minX +' '+ self.minY))
98        newenvelope.upperCorner=csml.parser.csString(str(self.maxX +' '+ self.maxY))
99        newenvelope.srsName='WGS84'
100        return newenvelope
101
102
103
104def getSeqRule(nDims):
105        '''returns a sequenceRule
106        *****************very simplified version*****************
107        This really needs more work to see if the +s and -s are correct. and the x/y/z s in right order
108        nDims = spatial dimensions + time dimension '''
109       
110        #TODO CSML2 - rewrite this
111        if nDims == 2:
112                seq = "Linear"
113        elif nDims ==3:
114                seq = "+x+y"
115        elif nDims ==4:
116                seq = "+x+y+z"
117        else:
118                seq="not sure"
119        return seq
120       
121       
122def getMappingRule(nDims):
123        #TODO CSML2 - rewrite this
124        if nDims == 2:
125                mr = "+ gridI + series"
126        elif nDims ==3:
127                mr = "+ gridI + gridJ +series"
128        elif nDims ==4:
129                mr = "+ gridI + gridJ +gridK +series"
130        else:
131                mr="not sure"
132        return mr
133       
134       
135def _getEnvelopeFromLimits(limits, axislabels):
136    env = csml.parser.EnvelopeWithTimePeriod()
137   
138    #simplistic reduction to remove time from CRS.
139    if limits['crs'][-1:] =='t':
140        env.srsName=limits['crs'][:-1]
141    else:
142        env.srsName=limits['crs']
143       
144    if env.srsName == 'ndg:crs:lonlat':
145        limits['crs'] ='WGS84'     
146        env.srsName = 'WGS84'     
147   
148    lowCorner= ''
149    upCorner=''
150    for axis in axislabels.split():
151        if axis != 'time':
152            try:
153                lowCorner = lowCorner + str(limits[axis][0]) + ' '
154                upCorner = upCorner + str(limits[axis][1]) + ' '
155            except:
156                pass
157        else:
158            env.beginPosition=csml.parser.csString(limits[axis][0])
159            env.endPosition=csml.parser.csString(limits[axis][1])
160    env.lowerCorner  = csml.parser.csString(lowCorner)
161    env.upperCorner  = csml.parser.csString(upCorner)
162    return env
163
164def addBoundingBoxes(ds):
165    '''Adds envelope with timeperiod to each feature, currently only works on GridSeriesFeatures but extendable.'''
166    timename='time'
167    cat=csml.csmllibs.csmlcrs.CRSCatalogue()
168    xlinkresolver=csml.csmllibs.csmlxlink.XlinkResolver(ds)
169    for feature in ds.featureCollection.featureMembers:
170        limits={}
171        if type(feature) is csml.parser.GridSeriesFeature:   
172            crsName=feature.value.gridSeriesDomain.srsName
173            limits['crs']=crsName
174            crs=cat.getCRS(feature.value.gridSeriesDomain.srsName)
175            axislabels=feature.value.gridSeriesDomain.axisLabels
176            for ord in feature.value.gridSeriesDomain.coordTransformTable.gridOrdinates:       
177                if ord.coordAxisLabel.CONTENT==timename:
178                    try:                       
179                        tmin= strToDate(ord.coordAxisValues.timePositionList.CONTENT.split()[0].split('T')[0])
180                        tmax=strToDate( ord.coordAxisValues.timePositionList.CONTENT.split()[len(ord.coordAxisValues.timePositionList.CONTENT.split())-1].split('T')[0])
181                    except AttributeError:
182                        sptlist=xlinkresolver.resolveXlink(ord.coordAxisValues.href[1:])                               
183                        try:
184                            tmin= strToDate(sptlist.timePositionList.CONTENT.split()[0].split('T')[0])
185                            tmax=strToDate(sptlist.timePositionList.CONTENT.split()[len(sptlist.timePositionList.CONTENT.split())-1].split('T')[0])
186                        except:
187                            tmin, tmax=0,0
188                    limits[timename]=[tmin,tmax]
189                else:
190                    if hasattr(ord.coordAxisValues, 'insertedExtract'):
191                        data=ord.coordAxisValues.insertedExtract.getData()[0].tolist()
192                    else:
193                        #TODO inline content
194                        #print ord.coordAxisValues.href
195                        fileextract= xlinkresolver.resolveXlink(ord.coordAxisValues.href[1:])                               
196                        data=fileextract.getData()[0].tolist()
197                    data.sort()
198                    #extend the bounding box to take account of cell bounds
199                    minval,maxval=_getMaxMinBounds(ord.coordAxisLabel.CONTENT,data)
200                    limits[ord.coordAxisLabel.CONTENT]=[minval,maxval]
201        feature.boundedBy=_getEnvelopeFromLimits(limits, axislabels)
202    return ds.featureCollection
203
204def _getMaxMinBounds(axisname,data):
205    if len(data) > 1:                   
206        halfcell=abs(0.5*(abs(data[1])-abs(data[0])))
207    else:
208        halfcell=0
209    minval=data[0] - halfcell
210    maxval=data[len(data)-1] + halfcell                   
211    if axisname=='longitude':
212        #deal with the case where the grid point is 0, so the grid goes from -x to +x e.g. -0.5 to 359.5
213        if minval < 0:
214            if maxval - minval > 359.0:
215                minval=0.0
216                maxval=360.0
217    if axisname=='latitude':
218        #deal with latitudes
219        if minval < -90:
220            minval=-90.0
221        if maxval > 90:
222            maxval=90.0
223    return minval,maxval
224
225def addEnvelope(fc):
226    '''adds EnvelopeWithTimePeriod to feature collection, based on the spatial extent of the features within.   '''
227    aggregator=None
228    for feature in fc.featureMembers:
229        env=feature.boundedBy
230        if aggregator is None:
231            aggregator=EnvelopeAggregator(env)
232        else:
233            aggregator.compareEnvelope(env)
234    fcEnvelope=aggregator.getAggregatedEnvelope()
235    fc.boundedBy=fcEnvelope
236    return fc
237       
238               
239       
240# a couple of string cleaning functions:
241#   don't think these are used now
242def cleanString1(messystring):
243        #removes outer brackets and changes commas to spaces
244        cleanstring = messystring[1:-1]
245        cleanstring = cleanstring.replace(',',' ')
246        #strip off first (time) dimension.
247        #note, this might need rethinking for other datasets
248        cleanstring = cleanstring[3:]
249        return cleanstring
250#       
251def cleanString(messystring):
252   # removes outer brackets and 's, but leaves commas.
253    cleanstring = messystring[1:-1]
254    cleanstring = cleanstring.replace("'",'')
255    #remove any carriage returns
256    cleanstring = cleanstring.replace("\n",'')
257    return cleanstring
258       
259def listify(item):
260    ''' listify checks if an item is a list, if it isn't it puts it inside a list and returns it. Always returns a list object'''
261    if type(item) is list:
262        return item
263    else:
264        return [item]       
265
266def stringify(inputlist):
267    '''stringify takes a list and returns a string containing the contents of the list separated by spaces'''
268    strreturn=''
269    for item in inputlist:
270        strreturn=strreturn + item +' '
271    return strreturn[:-1]
272
273def checkDirExists(dirpath):
274    if os.path.isdir(dirpath) != True:
275        try:
276            os.mkdir(dirpath)
277        except:
278            pass
279             
280
281
282def main():
283    #test to check randomness of getRandomID()
284    idlist=[]
285    for j in range (500):
286        for i in range(100):
287            newid= getRandomID()
288            if newid in idlist:
289                print 'ID matched!!!'               
290                sys.exit()
291            else:               
292                idlist.append(newid)
293        print i * j
294    print 'done'
295   
296if __name__=="__main__":
297    main()
Note: See TracBrowser for help on using the repository browser.