source: DPPP/kml/csml2kml/python/kmlfeatures.py @ 3214

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/DPPP/kml/csml2kml/python/kmlfeatures.py@3214
Revision 3214, 6.7 KB checked in by mkochan, 13 years ago (diff)

[UNTESTED] Added multiple views of GridSeries? data -- added splitting into folders by month and year. Made this configurable in the CSML2KML config file.

Line 
1'''KML representatitions of various CSML features'''
2
3import string
4import re
5from cElementTree import ElementTree, Element, SubElement
6from configuration import Configuration
7
8
9class GridSeriesKML(object):
10    ''' Represents a CSML GridSeriesFeature in KML (via WMS requests).'''
11   
12    def __init__(self, config, parentFilename, name, description, bBox, timeSteps, timeBounds):
13        ''' Initialise with the key information content'''
14
15        # Feature conversion configuration
16        self.config = config
17
18        # Name of the CSML file in which feature is contained
19        self.parentFilename = parentFilename
20
21        # A short name for the feature:
22        self.name=name
23
24        # A descriptive name for the feature:
25        self.description=description
26
27        # The latitude/longitude bounding box as a csmlwrappers.BBox object
28        self.bBox=bBox
29
30        # The time steps
31        self.timeSteps=timeSteps.split()
32
33        # Valid bounds of the individual timesteps
34        self.timeBounds=timeBounds.split()
35   
36    def _getYMD(self, timestep):
37        matchObject = re.match("(\d+)\-(\d+)\-(\d+)T", timestep)
38        (sYear, sMonth, sDay) = matchObject.groups()
39        return (sYear, sMonth, sDay)
40
41    def _getLogicalTimeSpan(self, timestep, logicalYearTransform):
42        '''Translate timestep to a timespan that covers a full month in a year
43           corresponding to the decade (e.g. 1900s -> 0, 1980s -> 8
44        '''
45        matchObject = re.match("(\d+)\-(\d+)\-(\d+)T", timestep)
46        (sYear, sMonth, sDay) = matchObject.groups()
47        year = int(sYear)
48        year = logicalYearTransform(year)
49        month = int(sMonth)
50        startDate = "%04d-%02d-%02d" % (year, month, 1)
51        if month+1 <= 12:
52            endDate = "%04d-%02d-%02d" % (year, month+1, 1)
53        else:
54            endDate = "%04d-%02d-%02d" % (year+1, 1, 1)
55     
56        return (startDate, endDate)
57
58    def _longitudeWithinMinusPlus180(self, x):
59        ''' Put the x coordinate within bounds of the period (-180, 180)'''
60        if x != 180:
61            x = ((x + 180) % 360) - 180
62        return x
63   
64    def _buildWMSRequest(self, timeStep):
65        ''' Build a WMS request '''
66
67        # Set request configuration parameters
68        c = self.config
69        url = c.get('URL')
70        serviceVersion = c.get('ServiceVersion')
71        imageFormat = c.get('ImageFormat')
72        imageWidth = c.get('ImageWidth')
73        imageHeight = c.get('ImageHeight')
74        crs = c.get('CRS')
75        layerName = c.get('LayerName')
76        filenameWithoutPath = self.parentFilename.split('/')[-1]
77        filenameExcludingSuffix = string.join(filenameWithoutPath.split('.')[:-1], '.')
78        layerName = layerName.replace('#FILENAME_EXCL_SUFFIX#', filenameExcludingSuffix)
79        layerName = layerName.replace('#FEATURE_NAME#', self.name)
80
81        # If timeStep contains a time part, make sure that the "Z" for "Zulu" is appended, as required by WMS
82        if re.search("T\d+\:\d+:\d+(\.\d+)$", timeStep):
83            timeStep = timeStep + 'Z'
84
85        # If required, make sure the longitude part of the bounding box is within (-180, 180)
86        if c.get('LongitudeBounds') == 'MINUS_180_TO_PLUS_180':
87            self.bBox.east = self._longitudeWithinMinusPlus180(self.bBox.east)
88            self.bBox.west = self._longitudeWithinMinusPlus180(self.bBox.west)
89       
90        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, self.bBox.str(), imageWidth, imageHeight, crs, timeStep)
91
92        return wmsRequest
93
94    def outputKML(self, viewConfig):
95        ''' method to output KML as ElementTree instance - returns a 'Folder' element which can be put into a KML Document element '''
96       
97        # Analyze view configuration -- this determines how data will be presented
98        sbText = viewConfig.find('SplitBy').text
99        lytText = viewConfig.find('LogicalYearTransform').text
100
101        viewElement=Element('Folder')
102        SubElement(viewElement, 'name').text='View_' + sbText
103        SubElement(viewElement, 'open').text='0'
104        SubElement(viewElement, 'visibility').text='0'
105       
106        if lytText == 'DECADES_SINCE_1900':
107            logicalYearTransform = getDecadeSince1900
108        else:
109            logicalYearTransform = getSameYear
110
111        def split_by(timeStep):
112            (sYear, sMonth, sDay) = self._getYMD(timestep)
113            if sbText == '':
114                return timeStep
115            elif sbText == 'years':
116                return sYear
117            elif sbText == 'months':
118                return sMonth
119
120        def g(acc, x):
121            category = split_by(x)
122            if not acc.has_key(category):
123                acc[category] = []
124            acc[category].append(x)
125
126        dict = reduce(g, timeSteps)
127       
128        for category in dict:
129           
130            # Build XML document structure -- call _buildWMSRequest to get individual request strings.
131     
132            folderElement=Element('Folder')
133            SubElement(folderElement, 'name').text='Folder_' + category
134            SubElement(folderElement, 'open').text='0'
135            SubElement(folderElement, 'visibility').text='0'
136       
137            for timestep in dict[category]:
138
139                goElement=SubElement(folderElement, 'GroundOverlay')     
140                SubElement(goElement, 'name').text=self.name + ' ' + timestep
141                SubElement(goElement, 'open').text='0'
142                SubElement(goElement, 'visibility').text='0'
143
144                timespanElement=SubElement(goElement, 'TimeSpan')
145                (startTime, endTime) = self._getLogicalTimeSpan(timestep, logicalYearTransform)
146                SubElement(timespanElement, 'begin').text=startTime
147                SubElement(timespanElement, 'end').text=endTime
148
149                # Include the WMS service call address
150                iconElement=SubElement(goElement,'icon')
151                SubElement(iconElement, 'href').text=self._buildWMSRequest(timestep)
152                SubElement(iconElement, 'refreshMode').text='onExpire'
153
154                latlonboxElement=SubElement(goElement, 'LatLonBox')
155                SubElement(latlonboxElement, 'north').text=str(self.bBox.north)
156                SubElement(latlonboxElement, 'south').text=str(self.bBox.south)
157                SubElement(latlonboxElement, 'east' ).text=str(self.bBox.east)
158                SubElement(latlonboxElement, 'west' ).text=str(self.bBox.west)
159       
160            SubElement(viewElement, folderElement)
161
162        return viewElement
163
164# ---------- These are defined *outside* the class -- i.e. they are plain functions
165
166def getDecadeSince1900(year):
167    return (year - 1900) / 10  # get decade as a "logical" year
168
169def getSameYear(year):
170    return year
171
172# -----------------
Note: See TracBrowser for help on using the repository browser.