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

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

In the middle of debugging of classes in WMSCapabilities.py.

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