source: DPPP/kml/csml2kml/python/csml2kml/KMLDocument.py @ 3302

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

Moved KMLDocument.py to csml2kml

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