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

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

Progressed on implementing WMS Layers -> KML conversion (before testing).

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