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

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

Updated the notes.

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 = '1'
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, visible = True):
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        self.visible = visible
143
144    def build(self):
145        placemarkElement = Element('Placemark')
146        SubElement(placemarkElement, 'name').text = self.name
147
148        if self.visible:
149            SubElement(placemarkElement, 'visibility').text = '1'
150        else:
151            SubElement(placemarkElement, 'visibility').text = '0'
152
153        if self.styleID:
154            SubElement(placemarkElement, 'styleUrl').text = '#' + self.styleID
155
156        lookAtElement = SubElement(placemarkElement, 'LookAt')
157        SubElement(lookAtElement, 'longitude').text = str(self.lon)
158        SubElement(lookAtElement, 'latitude').text = str(self.lat)
159
160        pointElement = SubElement(placemarkElement, 'Point')
161        SubElement(pointElement, 'coordinates').text = '%f,%f,%f' % (self.lon, self.lat, 0.)
162
163        # If the "data" dictionary is provided, create the additional <ExtendedData> element,
164        # which contains specific data items, which will be automatically substituted
165        # for placemarks in the balloon template when the user views the document in Google Earth.
166        if self.data:
167            extendedDataElement = SubElement(placemarkElement, 'ExtendedData')
168            for key in self.data:
169                dataElement = SubElement(extendedDataElement, 'Data')
170                dataElement.set('name', key)
171                value = self.data[key]
172                value = value.replace('#ID#', self.id)
173                value = value.replace('#NAME#', self.name)
174                value = value.replace('#LON#', str(self.lon))
175                value = value.replace('#LAT#', str(self.lat))
176                value = value.replace('#FEATURES#', self.featuresHtml)
177                SubElement(dataElement, 'value').text = value
178               
179        return placemarkElement
180
181class KMLFolder(KMLElement):
182    '''
183    Represents the <kml:Folder> tag.
184    '''
185
186    def __init__(self, name, children, styleID = None, region = None, opened = True, visible = True, description = None):
187        self.name = name
188        self.children = children
189        self.styleID = styleID
190        self.region = region
191        self.opened = opened
192        self.visible = visible
193        self.description = description
194
195    def build(self):
196        folderElement = Element('Folder')
197        if self.styleID:
198            SubElement(folderElement, 'styleUrl').text = '#' + self.styleID
199        SubElement(folderElement, 'name').text = self.name
200        if self.description:
201            SubElement(folderElement, 'description').text = self.description
202        if self.visible:
203            SubElement(folderElement, 'visibility').text = '1'
204        else:
205            SubElement(folderElement, 'visibility').text = '0'
206        if self.opened:
207            SubElement(folderElement, 'open').text = '1'
208        else:
209            SubElement(folderElement, 'open').text = '0'
210        if self.region:
211            if not isinstance(self.region, KMLRegion):
212                raise TypeError('Region not a KMLRegion')
213            folderElement.append( self.region.build() )
214        for child in self.children:
215            if not isinstance(child, KMLElement):
216                raise TypeError('Child does not have a KMLElement base class')
217            folderElement.append( child.build() )
218        return folderElement
219
220class KMLRegion(KMLElement):
221    '''
222    Represents the <kml:Region> tag.
223    '''
224   
225    def __init__(self, west, south, east, north, minLodPixels = 64, maxLodPixels = -1):
226        self.west = west
227        self.south = south
228        self.east = east
229        self.north = north
230        self.minLodPixels = minLodPixels
231        self.maxLodPixels = maxLodPixels
232
233    def build(self):
234        llabElement = Element('LatLonAltBox')
235        SubElement(llabElement, 'west').text = str(self.west)
236        SubElement(llabElement, 'south').text = str(self.south)
237        SubElement(llabElement, 'east').text = str(self.east)
238        SubElement(llabElement, 'north').text = str(self.north)
239
240        lodElement = Element('Lod')
241        SubElement(lodElement, 'minLodPixels').text = str(self.minLodPixels)
242        SubElement(lodElement, 'maxLodPixels').text = str(self.maxLodPixels)
243
244        regionElement = Element('Region')
245        regionElement.append(llabElement)
246        regionElement.append(lodElement)
247        return regionElement
248
249class KMLGroundOverlay(KMLElement):
250    '''Represents the <kml:GroundOverlay> tag.'''
251
252    def __init__(self, name, sourceUrl, timespanStart, timespanEnd, west, south, east, north, visible = True):
253        self.name = name
254        self.sourceUrl = sourceUrl
255        self.timespanStart = timespanStart
256        self.timespanEnd = timespanEnd
257        self.west = west
258        self.south = south
259        self.east = east
260        self.north = north
261        self.visible = visible
262
263    def build(self):
264
265        groundOverlayElement = Element('GroundOverlay')
266
267        SubElement(groundOverlayElement, 'name').text = self.name
268
269        if self.visible:
270            SubElement(groundOverlayElement, 'visibility').text = '1'
271        else:
272            SubElement(groundOverlayElement, 'visibility').text = '0'
273
274        timespanElement = SubElement(groundOverlayElement, 'TimeSpan')
275        SubElement(timespanElement, 'begin').text = ('%04d-%02d-%02d') % self.timespanStart.utctimetuple()[0:3]
276        SubElement(timespanElement, 'end').text = ('%04d-%02d-%02d') % self.timespanEnd.utctimetuple()[0:3]
277
278        # Include the WMS service call address
279        iconElement = SubElement(groundOverlayElement, 'icon')
280        SubElement(iconElement, 'href').text = self.sourceUrl
281        SubElement(iconElement, 'refreshMode').text = 'onExpire'
282
283        latlonboxElement=SubElement(groundOverlayElement, 'LatLonBox')
284        SubElement(latlonboxElement, 'north').text = str(self.north)
285        SubElement(latlonboxElement, 'south').text = str(self.south)
286        SubElement(latlonboxElement, 'east' ).text = str(self.east)
287        SubElement(latlonboxElement, 'west' ).text = str(self.west)
288
289        return groundOverlayElement
290
291class KMLNetworkLink(KMLElement):
292    def __init__(self):
293        pass
294
295    def build(self):
296        pass
Note: See TracBrowser for help on using the repository browser.