9
9
from io import BytesIO as SIO
10
10
from zipfile import ZipFile
11
11
import pathlib
12
+ import re
12
13
13
14
namespaces = {'kml' : 'http://www.opengis.net/kml/2.2' }
14
15
15
16
17
+ class Style ():
18
+ def __init__ (self , id ):
19
+ self .id = id
20
+ self .line_colour = None
21
+
22
+
23
+ class StyleMap ():
24
+ def __init__ (self , id , otherstyle ):
25
+ self .id = id
26
+ self .otherstyle = otherstyle
27
+
28
+
16
29
class Polygon ():
17
- def __init__ (self , name , latlon ):
30
+ def __init__ (self , name , latlon , line_colour = None ):
18
31
self .name = name
19
32
self .vertexes = latlon
33
+ self .line_colour = line_colour
20
34
21
35
22
36
class Point ():
@@ -27,6 +41,15 @@ def __init__(self, name, latlon):
27
41
28
42
def readkmz (filename ):
29
43
'''reads in a kmz file and returns xml nodes'''
44
+ xpath = xpath = ".//kml:Placemark"
45
+
46
+ tree = etree_for_filepath (filename )
47
+
48
+ return tree .findall (xpath , namespaces )
49
+
50
+
51
+ def etree_for_filepath (filename ):
52
+ '''reads in a kmz file and returns lxml.etree.ElementTree'''
30
53
# Strip quotation marks if neccessary
31
54
filename .strip ('"' )
32
55
# Open the zip file (as applicable)
@@ -48,10 +71,7 @@ def readkmz(filename):
48
71
raise Exception (f"load expects a .kml or .kmz file, got ({ suffix } ) from ({ filename } )" )
49
72
50
73
parser = etree .XMLParser (encoding = 'utf-8' , recover = True )
51
- tree = etree .parse (SIO (fstring ), parser )
52
- xpath = xpath = ".//kml:Placemark"
53
-
54
- return tree .findall (xpath , namespaces )
74
+ return etree .parse (SIO (fstring ), parser )
55
75
56
76
57
77
def find_tag (node , tagname ):
@@ -92,13 +112,108 @@ def readObject(innode):
92
112
for c in coordinates .text .split ():
93
113
s = c .split (',' )
94
114
latlon .append ((float (s [1 ]), float (s [0 ])))
115
+
95
116
return Polygon (name .text , latlon )
96
117
97
118
return ('Unknown' , None , None )
98
119
99
120
121
+ class KMLRead ():
122
+ def __init__ (self , filepath ):
123
+ self .filepath = filepath
124
+
125
+ def placemark_nodes (self ):
126
+ return self .tree .findall (".//kml:Placemark" , namespaces )
127
+
128
+ def readObject (self , innode ):
129
+ '''reads in a node and returns as a tuple: (type, name, points[])'''
130
+ # get name
131
+ name = find_tag (innode , 'name' )
132
+ if name is None :
133
+ return None
134
+ point = find_tag (innode , 'Point' )
135
+ if point is not None :
136
+ coordinates = find_tag (point , 'coordinates' )
137
+ if coordinates is None :
138
+ return None
139
+ s = coordinates .text .split (',' )
140
+ return Point (name .text , (float (s [1 ]), float (s [0 ])))
141
+
142
+ coordinates = find_tag_recursive (innode , 'coordinates' )
143
+ if coordinates is not None :
144
+ # a Polygon
145
+ latlon = []
146
+ for c in coordinates .text .split ():
147
+ s = c .split (',' )
148
+ latlon .append ((float (s [1 ]), float (s [0 ])))
149
+
150
+ styleURL = find_tag_recursive (innode , 'styleUrl' )
151
+ line_colour = None
152
+ if styleURL is not None :
153
+ styleurl_name = styleURL .text .lstrip ('#' )
154
+ if styleurl_name in self .stylemap :
155
+ stylemap = self .stylemap [styleurl_name ]
156
+ otherstyle = stylemap .otherstyle
157
+ if otherstyle in self .style :
158
+ style = self .style [otherstyle ]
159
+ line_colour = style .line_colour
160
+
161
+ return Polygon (name .text , latlon , line_colour = line_colour )
162
+
163
+ return ('Unknown' , None , None )
164
+
165
+ def parse (self ):
166
+ self .tree = etree_for_filepath (self .filepath )
167
+
168
+ # extract styles:
169
+ self .style = {}
170
+ for s in self .tree .findall (".//kml:Style" , namespaces ):
171
+ _id = s .get ("id" )
172
+ style = Style (_id )
173
+ self .style [_id ] = style
174
+ line_style = find_tag_recursive (s , 'LineStyle' )
175
+ if line_style is None :
176
+ continue
177
+ colour = find_tag_recursive (line_style , 'color' )
178
+ g = re .match ("(?P<alpha>..)(?P<r>..)(?P<g>..)(?P<b>..)" , colour .text )
179
+ if g is None :
180
+ continue
181
+ style .line_colour = (
182
+ int (g .group ("r" ), 16 ),
183
+ int (g .group ("g" ), 16 ),
184
+ int (g .group ("b" ), 16 )
185
+ )
186
+
187
+ # extract stylemaps:
188
+ self .stylemap = {}
189
+ for s in self .tree .findall (".//kml:StyleMap" , namespaces ):
190
+ _id = s .get ("id" )
191
+ styleurl_obj = None
192
+ for pair in s .getchildren ():
193
+ if pair is None :
194
+ continue
195
+ if find_tag_recursive (pair , "key" ).text == "normal" :
196
+ styleurl_obj = find_tag_recursive (pair , "styleUrl" )
197
+ if styleurl_obj is not None :
198
+ break
199
+ break
200
+
201
+ if styleurl_obj is None :
202
+ continue
203
+ stylemap = StyleMap (_id , styleurl_obj .text .lstrip ('#' ))
204
+ self .stylemap [_id ] = stylemap
205
+
206
+
100
207
if __name__ == '__main__' :
101
208
import sys
102
- nodes = readkmz (sys .argv [1 ])
103
- for n in nodes :
104
- print (readObject (n ))
209
+
210
+ kml = KMLRead (sys .argv [1 ])
211
+ kml .parse ()
212
+
213
+ for n in kml .placemark_nodes ():
214
+ obj = kml .readObject (n )
215
+ if obj is None :
216
+ continue
217
+
218
+ # for n in readkmz(sys.argv[1]):
219
+ # print(readObject(n))
0 commit comments