source: DPPP/kml/csml2kml/python/csml2kml/KML.py @ 3434

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

Added display of multiple CSML features (plots) per station.

Line 
1from cElementTree import ElementTree, Element, SubElement, XML
2
3class KMLElement:
4    '''
5    Abstract class, representing any element of a KML document -- i.e. anything contained *within*
6    the <kml:Document> element.
7    '''
8
9    def build(self):
10        '''
11        It is required that each KMLElement instance is able to build() itself into an ElementTree.Element
12        instance of KML, which it represents.
13        '''
14        raise NotImplementedError("Abstract method, to be overriden by child classes")
15
16class KMLDocument:
17    '''
18    Wraps around a whole KML document and makes it possible to save it to a file directly.
19    Represent the <kml:Document>, so that the method build() builds the kml:Document element,
20    but also contains the method save() that allows saving that to a file, wrapped within the <kml> header.
21    '''
22
23    def __init__(self, name, styles):
24
25        self.name = name
26        self.styles = styles
27        self.elements = []
28
29    def build(self):
30
31        # Create the <Document> an element to hold the document
32        documentElement = Element('Document')
33        SubElement(documentElement, 'name').text = self.name
34        SubElement(documentElement, 'open').text = '0'
35
36        # Build the associated styles
37        for style in self.styles:
38            documentElement.append( style.build() )
39
40        # Build the sub-elements
41        for element in self.elements:
42            documentElement.append( element.build() )
43       
44        return documentElement
45       
46    def save(self, outputFilename):
47        '''
48        Save the document to file <code>outputFilename</code>.
49        '''
50
51        # Auxiliary function, indents XML
52        def _indentXML(elem, level=0):
53            i = "\n" + level * "  "
54            if len(elem):
55                if not elem.text or not elem.text.strip():
56                    elem.text = i + "  "
57                for child in elem:
58                    _indentXML(child, level+1)
59                if not child.tail or not child.tail.strip():
60                    child.tail = i
61                if not elem.tail or not elem.tail.strip():
62                    elem.tail = i
63            else:
64                if level and (not elem.tail or not elem.tail.strip()):
65                    elem.tail = i
66
67        # Build the Document element
68        documentElement = self.build()
69
70        # Attach the Document element as a subelement of a root 'kml' element
71        rootElement=Element('kml', xmlns='http://earth.google.com/kml/2.2')
72        rootElement.append(documentElement)
73        _indentXML(rootElement)
74
75        # Write the KML document to a file
76        elementTree = ElementTree(rootElement)       
77        kmlFile = open(outputFilename, 'w')
78        elementTree.write(kmlFile)
79        kmlFile.close()
80
81class KMLStyle(KMLElement):
82    '''
83    Represents the <kml:Style> tag.
84    '''
85
86    def __init__(self, id, iconURL = None, balloonTemplate = None, listItemType = None):
87        self.id = id
88        self.iconURL = iconURL
89        self.balloonTemplate = balloonTemplate
90        if listItemType:
91            allowedValues =  ['check', 'checkOffOnly', 'checkHideChildren', 'radioFolder']
92            if not listItemType in allowedValues:
93                raise ValueError('listItemType not among allowed values: ' + str(allowedValues))
94        self.listItemType = listItemType
95
96    def build(self):
97
98        styleElement = Element('Style')
99        styleElement.set('id', self.id)
100
101        # If specified, build the IconStyle element -- for assigning an icon to station placemarks
102        if self.iconURL:
103            iconStyleElement = SubElement(styleElement, 'IconStyle')
104            SubElement(iconStyleElement, 'scale').text = '1.2'
105            iconElement = SubElement(iconStyleElement, 'Icon')
106            SubElement(iconElement, 'href').text = self.iconURL
107
108        # If specified, build the BalloonStyle sub-element -- an HTML template for the balloons
109        if self.balloonTemplate:
110            balloonStyleElement = SubElement(styleElement, 'BalloonStyle')
111            SubElement(balloonStyleElement, 'text').text = self.balloonTemplate
112
113        # If specified, build the ListStyle sub-element -- which determines how the associated lists
114        # in the left-hand side of the Google Earth screen are going to display/behave
115        if self.listItemType:
116            listStyleElement = SubElement(styleElement, 'ListStyle')
117            SubElement(listStyleElement, 'listItemType').text = self.listItemType
118       
119        return styleElement
120
121def createDefaultPlacemarKMLStyle(id = 'default_placemark_style',
122                                  iconURL = 'http://maps.google.com/mapfiles/kml/shapes/target.png',
123                                  balloonTemplate = ''):
124    '''
125    A factory method defined for convenience. Creates a style readily usable for adding styles to KML placemarks.
126    '''
127    return KMLStyle(id, iconURL, balloonTemplate)
128
129class KMLPlacemark(KMLElement):
130    '''
131    Represents the <kml:Placemark> tag.
132    '''
133   
134    def __init__(self, id, name, lon, lat, featuresHtml, styleID = None, data=None):
135        self.id = id
136        self.name = name
137        self.lon = lon
138        self.lat = lat
139        self.featuresHtml = featuresHtml
140        self.styleID = styleID
141        self.data = data
142
143    def build(self):
144        placemarkElement = Element('Placemark')
145        SubElement(placemarkElement, 'name').text = self.name
146        SubElement(placemarkElement, 'open').text = '0'
147        if self.styleID:
148            SubElement(placemarkElement, 'styleUrl').text = '#' + self.styleID
149
150        lookAtElement = SubElement(placemarkElement, 'LookAt')
151        SubElement(lookAtElement, 'longitude').text = str(self.lon)
152        SubElement(lookAtElement, 'latitude').text = str(self.lat)
153
154        pointElement = SubElement(placemarkElement, 'Point')
155        SubElement(pointElement, 'coordinates').text = '%f,%f,%f' % (self.lon, self.lat, 0.)
156
157        # If the "data" dictionary is provided, create the additional <ExtendedData> element,
158        # which contains specific data items, which will be automatically substituted
159        # for placemarks in the balloon template when the user views the document in Google Earth.
160        if self.data:
161            extendedDataElement = SubElement(placemarkElement, 'ExtendedData')
162            for key in self.data:
163                dataElement = SubElement(extendedDataElement, 'Data')
164                dataElement.set('name', key)
165                value = self.data[key]
166                value = value.replace('#ID#', self.id)
167                value = value.replace('#NAME#', self.name)
168                value = value.replace('#LON#', str(self.lon))
169                value = value.replace('#LAT#', str(self.lat))
170                value = value.replace('#FEATS#', self.featuresHtml)
171                SubElement(dataElement, 'value').text = value
172               
173        return placemarkElement
174
175class KMLFolder(KMLElement):
176    '''
177    Represents the <kml:Folder> tag.
178    '''
179
180    def __init__(self, name, children, styleID = None, region = None):
181        self.name = name
182        self.children = children
183        self.styleID = styleID
184        self.region = region
185
186    def build(self):
187        folderElement = Element('Folder')
188        if self.styleID:
189            SubElement(folderElement, 'styleUrl').text = '#' + self.styleID
190        SubElement(folderElement, 'name').text = self.name
191        if self.region:
192            if not isinstance(self.region, KMLRegion):
193                raise TypeError('Region not a KMLRegion')
194            folderElement.append( self.region.build() )
195        for child in self.children:
196            if not isinstance(child, KMLElement):
197                raise TypeError('Child does not have a KMLElement base class')
198            folderElement.append( child.build() )
199        return folderElement
200
201class KMLRegion(KMLElement):
202    '''
203    Represents the <kml:Region> tag.
204    '''
205   
206    def __init__(self, west, south, east, north, minLodPixels = 64, maxLodPixels = -1):
207        self.west = west
208        self.south = south
209        self.east = east
210        self.north = north
211        self.minLodPixels = minLodPixels
212        self.maxLodPixels = maxLodPixels
213
214    def build(self):
215
216        llabElement = Element('LatLonAltBox')
217        SubElement(llabElement, 'west').text = str(self.west)
218        SubElement(llabElement, 'south').text = str(self.south)
219        SubElement(llabElement, 'east').text = str(self.east)
220        SubElement(llabElement, 'north').text = str(self.north)
221
222        lodElement = Element('Lod')
223        SubElement(lodElement, 'minLodPixels').text = str(self.minLodPixels)
224        SubElement(lodElement, 'maxLodPixels').text = str(self.maxLodPixels)
225
226        regionElement = Element('Region')
227        regionElement.append(llabElement)
228        regionElement.append(lodElement)
229        return regionElement
Note: See TracBrowser for help on using the repository browser.