source: DPPP/kml/csml2kml/python/csml2kml/csml2kml/WMSCapabilities.py @ 3508

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/DPPP/kml/csml2kml/python/csml2kml/csml2kml/WMSCapabilities.py@3508
Revision 3508, 11.8 KB checked in by mkochan, 12 years ago (diff)

testWMSCapabilities.py runs successfully, but produces unsatisfactory output.

Line 
1import os
2from pylab import dates     # a very good date/time module from matplotviz -- allows years < 1970
3from KML import *
4
5# [TODO]
6#        * Resolve maxDirDepth
7
8wmsXmlNamespace = 'http://www.opengis.net/wms'
9
10
11def wmsLayerFactory(layerElement):
12    '''
13    [DOC]
14    '''
15    name = layerElement.find('{%s}Name' % wmsXmlNamespace).text
16    title = layerElement.find('{%s}Title' % wmsXmlNamespace).text
17    abstract = layerElement.find('{%s}Abstract' % wmsXmlNamespace).text
18    childElements = layerElement.findall('{%s}Layer' % wmsXmlNamespace)
19    childWmsLayers = []
20    for childElement in childElements:
21        childWmsLayer = wmsLayerFactory(childElement)
22        childWmsLayers.append(childWmsLayer)
23    if childElements != []:
24        return WMSLayer(name, title, abstract, childWmsLayers)       
25    else:
26        dimensionElements = layerElement.findall('{%s}Dimension' % wmsXmlNamespace)
27        for dimensionElement in dimensionElements:
28            if dimensionElement.get('name') == 'time':
29                timesteps = map( dates.dateutil.parser.parse, dimensionElement.text.split(',') )
30        return BottomWMSLayer(name, title, abstract, timesteps)
31
32class WMSLayer:
33    '''
34    [DOC]
35    '''
36
37    def __init__(self, name, title, abstract, children):
38        self.name = name
39        self.title = title
40        self.abstract = abstract
41        self.children = children
42
43    def __repr__(self):
44        return str(vars(self))
45
46    def parseXML(self, layerElement):
47        raise NotImplementedError('Use the wmsLayerFactory() function instead.')
48
49    def toKML(self, wmsRequestConfigElement, views):
50        # ignore wmsRequestConfigElement, views
51        kmlLayerFolder = KMLFolder(self.name, [], opened = False, visible = False)
52        for childWmsLayer in self.children:
53            kmlLayerFolder.children.append( childWmsLayer.toKML(wmsRequestConfigElement, views) )
54        return kmlLayerFolder
55
56class BottomWMSLayer(WMSLayer):
57
58    '''[DOC]'''
59   
60    def __init__(self, name, title, abstract, timesteps):
61
62        self.name = name
63        self.title = title
64        self.abstract = abstract
65        # but no self.children
66        self.timesteps = timesteps
67
68    def _parseName(self):
69        (modelName, scenarioName, rest) = self.name.split(':')
70        timespanDesignator = rest.split('/')[0]
71
72        if timespanDesignator == 'clim_20' or timespanDesignator == 'change_20':
73            sampleTimespan = 20
74        elif timespanDesignator == 'clim_30' or timespanDesignator == 'change_30':
75            sampleTimespan = 30
76        else:
77            raise ValueError('Sample timespan designation is incorrect')
78
79        return (modelName, scenarioName, sampleTimespan)
80
81    def getModelName(self):
82        return self._parseName()[0]
83
84    def getScenarioName(self):
85        return self._parseName()[1]
86
87    def getSampleTimespan(self):
88        return self._parseName()[2]
89
90    def toKML(self, wmsRequestConfigElement, views):
91        '''
92        @param views: A list of View objects, which define how we are going to look at the data.
93        @return: a KML.KMLFolder object representing a <kml:Folder> element with lots of <kml:GroundOverlay>
94        elements, each standing for a different time segment.
95        [NOTE] Should replace timeStep with a timedate object?
96        [NOTE] Please! Use dates.datetime all around here -- the self.times are of such type!
97        '''
98
99        def buildWMSRequest(layerName, timestep):
100            ''' Build a WMS request '''
101
102            # We will be using configuration for WMS request
103            c = wmsRequestConfigElement
104
105            # Set request configuration parameters
106            url = c.find('URL').text
107            serviceVersion = c.find('ServiceVersion').text
108            imageFormat = c.find('ImageFormat').text
109            imageWidth = c.find('ImageWidth').text
110            imageHeight = c.find('ImageHeight').text
111            crs = c.find('CRS').text
112
113            bBox = '-180,-90,180,90'
114           
115            wmsRequest = '%s?request=GetMap&amp;SERVICE=%s&amp;FORMAT=%s&amp;LAYERS=%s&amp;BBOX=%s&amp;WIDTH=%s&amp;HEIGHT=%s&amp;CRS=%s&TIME=%s' % (url, serviceVersion, imageFormat, layerName, bBox, imageWidth, imageHeight, crs, timestep.isoformat())
116
117            return wmsRequest
118
119        def buildKmlGroundOverlay(view, timestep):
120            (timespanStart, timespanEnd) = view.getLogicalTimespan(timestep)
121            return KMLGroundOverlay(
122                self.name + ' ' + timestep.isoformat(),
123                buildWMSRequest(self.name, timestep),
124                timespanStart, timespanEnd,
125                -180, -90, 180, 90,
126                visible = False
127                )
128
129        def buildLayerViewFolder(view):
130
131            kmlLayerViewFolder = KMLFolder(view.name, [], visible = False, opened = False)           
132
133            # Create a categorisation dictionary, dict, which will contain categories (as returned by
134            # view.getCategory()) as keys, and timesteps belonging into those categories as values.
135            dict = {}
136            for timestep in self.timesteps:
137                category = view.getCategory(timestep)
138                if not dict.has_key(category):
139                    dict[category] = []
140                dict[category].append(timestep)
141            categories = dict.keys()
142            categories.sort()
143
144            for category in categories:
145                categoryName = view.getCategoryDescription(timestep)
146                kmlCategoryFolder = KMLFolder(categoryName, [], visible = False, opened = False)
147                for timestep in dict[category]:
148                    kmlGroundOverlay = buildKmlGroundOverlay(view, timestep)
149                    kmlCategoryFolder.children.append(kmlGroundOverlay)
150                kmlLayerViewFolder.children.append(kmlCategoryFolder)
151
152            return kmlLayerViewFolder
153       
154        # ------------
155
156        kmlLayerFolder = KMLFolder(self.name, [], visible = False, opened = False)
157        for view in views:
158            kmlLayerFolder.children.append( buildLayerViewFolder(view) )
159        return kmlLayerFolder
160   
161class View:
162    '''
163    Determines how data can be viewed. That is, in practice, how it can be converted into KML so it can be viewed
164    in Google Earth. In particular, it defines logical transforms of time-points into time-spans.
165    '''
166
167    def __init__(self):
168        pass
169
170    def getLogicalTimespan(self, timestep):
171        '''
172        Abstract method, defined in derived classes.
173        Translates a single time step into a time span.
174        @param timestep: The date step (a datetime object)
175        @return: The (timespanStart, timespanEnd) tuple (both are datetime objects)
176        '''
177        pass
178
179    def getCategory(self, timestep):
180        pass
181
182    def getCategoryDescription(self, timestep):
183        '''
184        Abstract method, defined in derived classes.
185        Get a human-readable description of the category that timestep belongs to.
186        '''
187        pass
188
189    def _getSameDate(self, timestep):
190        return timestep
191
192    def _getFirstDayOfMonth(self, timestep):
193        return timestep.replace(day=1)
194
195    def _get20thCenturyDecade(self, timestep):
196        replYear = (timestep.year - 1900) / 10 + 1       # get decade as a "logical" year
197        return timestep.replace(year=replYear, day=1)    # start a month on the 1st of the month
198
199    def _getMonthHence(self, timestep):
200        if timestep.month+1 <= 12:
201            return timestep.replace(month=timestep.month+1)
202        else:
203            return timestep.replace(year=timestep.year+1, month=1)
204   
205    def _getYearHence(self, timestep):
206        return timestep.replace(year=timestep.year+1)
207
208    def _getDecadeHence(self, timestep):
209        return timestep.replace(year=timestep.year+10)
210
211class ViewWholeCentury(View):
212
213    def __init__(self):
214        self.name = 'Whole century'
215
216    def getLogicalTimespan(self, timestep):
217        timespanStart = self._get20thCenturyDecade(timestep)
218        timespanEnd = self._getMonthHence(timespanStart)
219        return (timespanStart, timespanEnd)
220
221    def getCategory(self, timestep):
222        return timestep.isoformat()
223
224    def getCategoryDescription(self, timestep):
225        return self.getCategory(timestep)
226
227class ViewSplittedByMonth(View):
228
229    def __init__(self):
230        self.name = 'Compare months'
231
232    def getLogicalTimespan(self, timestep):
233        timespanStart = self._getFirstDayOfMonth(timestep)
234        timespanEnd = self._getDecadeHence(timespanStart)
235        return (timespanStart, timespanEnd)
236
237    def getCategory(self, timestep):
238        return timestep.month
239
240    def getCategoryDescription(self, timestep):
241        '''
242        Get a human-readable description of the category that timestep belongs to.
243        For instance, for timestep being 2000-02-14T10:11:12Z (which belongs into category 2), the result is 'February'.
244        '''
245        monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 
246                      'July', 'August', 'September', 'October', 'November', 'December']
247        return monthNames[self.getCategory(timestep)-1]
248
249class ViewSplittedByDecade(View):
250
251    def __init__(self):
252        self.name = 'Compare decades'
253
254    def getLogicalTimespan(self, timestep):
255        timespanStart = self._getFirstDayOfMonth(timestep)
256        timespanEnd = self._getMonthHence(timespanStart)
257        return (timespanStart, timespanEnd)
258
259    def getCategory(self, timestep):
260        return timestep.year
261
262    def getCategoryDescription(self, timestep):
263        '''
264        Get a human-readable description of the category that timestep belongs to.
265        For instance, for timestep being 1990-05-17T02:05:17Z, the result would be 'Decade of 1990'.
266        '''
267        return 'Decade of ' + str(self.getCategory(timestep))
268
269class WMSCapabilities:
270
271    '''[DOC]'''
272
273    def __init__(self):
274        self.topWmsLayer = None
275
276    def parseXML(self, wmsCapabilitiesElement):
277        topLayerElement = wmsCapabilitiesElement.find('{%s}Capability/{%s}Layer' % (wmsXmlNamespace, wmsXmlNamespace))
278        self.topWmsLayer = wmsLayerFactory(topLayerElement)
279
280    def __repr__(self):
281        if self.topWmsLayer:
282            return '--- WMSCapabilities object with top layer as follows): ' + repr(self.topWmsLayer) + ' ---'
283        else:
284            return '--- WMSCapabilities object with no top layer ---'
285
286class WMSLayersConvertor:
287   
288    def __init__(self, topWmsLayer, wmsRequestConfigElement, baseKmlOutputDirectory):
289        self.topWmsLayer = topWmsLayer
290        self.wmsRequestConfigElement = wmsRequestConfigElement
291        self.baseKmlOutputDirectory = baseKmlOutputDirectory
292        self.maxDirDepth = 1
293        # [a:D,r:DEBUG] self.maxDirDepth = self.config(...)
294       
295    def convert(self):
296
297        def _convertToKML(wmsLayer):
298            views = [ViewWholeCentury(), ViewSplittedByMonth(), ViewSplittedByDecade()]
299            return wmsLayer.toKML(self.wmsRequestConfigElement, views)
300       
301        def _convertToDirectory(wmsLayer, parentDir, currentLevel):
302            '''recursive'''
303            if currentLevel < self.maxDirDepth:
304                currentDir = parentDir + '/' + wmsLayer.name
305                os.mkdir(currentDir)
306                print 'Created directory "%s"' % currentDir
307                if not isinstance(wmsLayer, BottomWMSLayer):
308                    for childWmsLayer in wmsLayer.children:
309                        _convertToDirectory(childWmsLayer, currentDir, currentLevel+1)
310            elif currentLevel == self.maxDirDepth:
311                # Create a KML document with no styles
312                kmlDocument = KMLDocument(wmsLayer.name, []) 
313                kmlDocument.elements.append( _convertToKML(wmsLayer) )
314                filename = parentDir + '/' + wmsLayer.name + '.kml'
315                kmlDocument.save(filename)
316                print 'Saved file "%s"' % filename
317            else:
318                pass
319
320        #### topWmsLayer = wmsLayerFactory(topLayerElement)
321        _convertToDirectory(self.topWmsLayer, self.baseKmlOutputDirectory, 0)
Note: See TracBrowser for help on using the repository browser.