Skip to content

Commit

Permalink
kml: make kmlread more robust
Browse files Browse the repository at this point in the history
this allows it to handle more complex kmz files from google earth
  • Loading branch information
tridge committed Aug 11, 2023
1 parent 67839c7 commit 2ca8c5a
Showing 1 changed file with 55 additions and 34 deletions.
89 changes: 55 additions & 34 deletions MAVProxy/modules/lib/kmlread.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#!/usr/bin/env python

import lxml.etree as etree
from io import BytesIO as SIO
from zipfile import ZipFile
from xml.dom.minidom import parseString

namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

def readkmz(filename):
'''reads in a kmz file and returns xml nodes'''
#Strip quotation marks if neccessary
filename.strip('"')
#Open the zip file (as applicable)
if filename[-4:] == '.kml':
fo = open(filename, "r")
fo = open(filename, "rb")
fstring = fo.read()
fo.close()
elif filename[-4:] == '.kmz':
Expand All @@ -22,38 +25,56 @@ def readkmz(filename):
raise Exception("Could not find kml file in %s" % filename)
else:
raise Exception("Is not a valid kml or kmz file in %s" % filename)

#send into the xml parser
kmlstring = parseString(fstring)

#get all the placenames
nodes=kmlstring.getElementsByTagName('Placemark')

return nodes


parser = etree.XMLParser(encoding='utf-8', recover=True)
tree = etree.parse(SIO(fstring), parser)
xpath = xpath = ".//kml:Placemark"

return tree.findall(xpath, namespaces)

def find_tag(node, tagname):
for c in node.getchildren():
if c.tag == "{" + namespaces['kml'] + "}" + tagname:
return c
return None

def find_tag_recursive(node, tagname):
for c in node.getchildren():
if c.tag == "{" + namespaces['kml'] + "}" + tagname:
return c
if hasattr(c, 'getchildren'):
ret = find_tag_recursive(c, tagname)
if ret is not None:
return ret
return None


def readObject(innode):
'''reads in a node and returns as a tuple: (type, name, points[])'''
#get name
names=innode.getElementsByTagName('name')[0].childNodes[0].data.strip()

#get type
pointType = 'Unknown'
if len(innode.getElementsByTagName('LineString')) == 0 and len(innode.getElementsByTagName('Point')) == 0:
pointType = 'Polygon'
elif len(innode.getElementsByTagName('Polygon')) == 0 and len(innode.getElementsByTagName('Point')) == 0:
pointType = 'Polygon'
elif len(innode.getElementsByTagName('LineString')) == 0 and len(innode.getElementsByTagName('Polygon')) == 0:
pointType = 'Point'

#get coords
coords = innode.getElementsByTagName('coordinates')[0].childNodes[0].data.strip()
coordsSplit = coords.split()
ret_s = []
for j in coordsSplit:
jcoord = j.split(',')
if len(jcoord) == 3 and jcoord[0] != '' and jcoord[1] != '':
#print("Got lon " + jcoord[0] + " and lat " + jcoord[1])
ret_s.append((float(jcoord[1]), float(jcoord[0])))

#return tuple
return (str(pointType), str(names), ret_s)
name = find_tag(innode, 'name')
if name is None:
return None
point = find_tag(innode, 'Point')
if point is not None:
coordinates = find_tag(point, 'coordinates')
if coordinates is None:
return None
s = coordinates.text.split(',')
return ("Point", name.text, [(float(s[1]), float(s[0]))])

coordinates = find_tag_recursive(innode, 'coordinates')
if coordinates is not None:
latlon = []
for c in coordinates.text.split():
s = c.split(',')
latlon.append((float(s[1]), float(s[0])))
return ("Polygon", name.text, latlon)

return ('Unknown', None, None)

if __name__ == '__main__':
import sys
nodes = readkmz(sys.argv[1])
for n in nodes:
print(readObject(n))

0 comments on commit 2ca8c5a

Please sign in to comment.