]> gitweb.michael.orlitzky.com - dead/census-tools.git/blob - src/KML.py
Added a Coordinates class, and updated the LineString class to refer to Coordinates...
[dead/census-tools.git] / src / KML.py
1 """
2 Utility classes for working with the Keyhole Markup Language (KML).
3 """
4
5 import sys
6 from xml.sax.saxutils import escape
7
8
9 class KmlObject(object):
10 """
11 The base class of all KML elements, according to the reference:
12
13 http://code.google.com/apis/kml/documentation/kmlreference.html
14
15 Every other class in this module should derive from
16 KmlObject. This class provides a default constructor which creates
17 some necessary attributes, and default implementations of to_kml()
18 and render().
19
20 The to_kml() methods of our subclasses will, in general, generate
21 an opening tag, render themselves (whatever that means), and then
22 generate a closing tag. A call to render() generally returns the
23 element's text, and renders its children recursively.
24 """
25
26 OPEN_TAG = ''
27 CLOSE_TAG = ''
28
29 def __init__(self, initial_text=''):
30 self.children = []
31 self.text = initial_text
32
33
34 def to_kml(self):
35 kml = self.OPEN_TAG
36 kml += self.render()
37 kml += self.CLOSE_TAG + "\n"
38 return kml
39
40
41 def render(self):
42 kml = escape(self.text)
43
44 for c in self.children:
45 kml += c.to_kml()
46
47 return kml
48
49
50 def print_kml(self):
51 print self.OPEN_TAG
52 self.render_to_stdout()
53 print self.CLOSE_TAG
54
55
56 def render_to_stdout(self):
57 if (len(self.text) > 0):
58 print escape(self.text)
59
60 for c in self.children:
61 c.print_kml()
62
63
64
65 class Color(KmlObject):
66
67 OPEN_TAG = '<color>'
68 CLOSE_TAG = '</color>'
69
70
71 class Document(KmlObject):
72
73 OPEN_TAG = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
74 <kml xmlns=\"http://www.opengis.net/kml/2.2\">
75 <Document>"""
76
77 CLOSE_TAG = """</Document>
78 </kml>"""
79
80 def __init__(self, initial_text=''):
81 super(Document, self).__init__(initial_text)
82 self.styles = []
83
84
85 def render(self):
86 kml = ''
87
88 for s in self.styles:
89 kml += s.to_kml()
90
91 for c in self.children:
92 kml += c.to_kml()
93
94 return kml
95
96
97 def render_to_stdout(self):
98 for s in self.styles:
99 s.print_kml()
100
101 for c in self.children:
102 c.print_kml()
103
104
105
106 class Description(KmlObject):
107
108 OPEN_TAG = '<description>'
109 CLOSE_TAG = '</description>'
110
111
112
113 class Coordinates(KmlObject):
114
115 OPEN_TAG = '<coordinates>'
116 CLOSE_TAG = '</coordinates>'
117
118
119 class LineString(KmlObject):
120
121 OPEN_TAG = '<LineString>'
122 CLOSE_TAG = '</LineString>'
123
124 @classmethod
125 def parse_linestrings(self, kml):
126 """
127 Parse each <LineString>...</LineString> block from the KML.
128 """
129 linestrings = []
130
131 search_idx = kml.find(self.OPEN_TAG, 0)
132
133 while (search_idx != -1):
134 # No reason to keep the tag around.
135 ls_start = search_idx + len(self.OPEN_TAG)
136 ls_tag_end = kml.find(self.CLOSE_TAG, ls_start)
137 ls = kml[ ls_start : ls_tag_end ]
138 linestrings.append(ls)
139 search_idx = kml.find(self.OPEN_TAG, (ls_tag_end + len(self.CLOSE_TAG)))
140
141 return linestrings
142
143
144 @classmethod
145 def parse_coordinates_from_linestrings(self, linestrings):
146 coords = []
147
148 for ls in linestrings:
149 c_tag_start = ls.find(Coordinates.OPEN_TAG)
150 c_start = c_tag_start + len(Coordinates.OPEN_TAG)
151 c_end = ls.find(Coordinates.CLOSE_TAG)
152 c = ls[ c_start : c_end ]
153 coords.append(c)
154
155 return coords
156
157
158 @classmethod
159 def tuples_from_kml(self, kml):
160 """
161 Parse one or more linestrings from a KML document.
162 Return a list of tuples.
163 """
164 ls = self.parse_linestrings(kml)
165 cs = self.parse_coordinates_from_linestrings(ls)
166
167 tuples = []
168
169 for c in cs:
170 pointstrings = c.strip().split()
171 for point in pointstrings:
172 components = point.strip().split(',')
173 if (len(components) >= 2):
174 # Project the three-dimensional vector onto the
175 # x-y plane. I don't think we're going to run
176 # in to any linestrings in 3d.
177 tuples.append( (float(components[0]), float(components[1])) )
178
179 return tuples
180
181
182 class Name(KmlObject):
183
184 OPEN_TAG = '<name>'
185 CLOSE_TAG = '</name>'
186
187
188
189 class Placemark(KmlObject):
190
191 OPEN_TAG = '<Placemark>'
192 CLOSE_TAG = '</Placemark>'
193
194
195
196 class PolyStyle(KmlObject):
197
198 OPEN_TAG = '<PolyStyle>'
199 CLOSE_TAG = '</PolyStyle>'
200
201
202
203 class Style(KmlObject):
204
205 OPEN_TAG = "<Style id=\"%s\">"
206 CLOSE_TAG = '</Style>'
207
208 def __init__(self, initial_text='', initial_id=''):
209 super(Style, self).__init__(initial_text)
210 self.id = initial_id
211
212
213 def to_kml(self):
214 kml = ''
215 kml += (self.OPEN_TAG % self.id + "\n")
216 kml += self.render()
217 kml += self.CLOSE_TAG + "\n"
218
219 return kml
220
221
222 def print_kml(self):
223 print (self.OPEN_TAG % self.id)
224 self.render_to_stdout()
225 print self.CLOSE_TAG
226
227
228
229 class StyleUrl(KmlObject):
230
231 OPEN_TAG = '<styleUrl>'
232 CLOSE_TAG = '</styleUrl>'
233
234
235
236 class RawText(KmlObject):
237
238 def to_kml(self):
239 return self.text
240
241 def print_kml(self):
242 if (len(self.text) > 0):
243 print self.text