"""
Utility classes for working with the Keyhole Markup Language (KML).
"""
import sys
from xml.sax.saxutils import escape
class KmlObject(object):
"""
The base class of all KML elements, according to the reference:
http://code.google.com/apis/kml/documentation/kmlreference.html
Every other class in this module should derive from
KmlObject. This class provides a default constructor which creates
some necessary attributes, and default implementations of to_kml()
and render().
The to_kml() methods of our subclasses will, in general, generate
an opening tag, render themselves (whatever that means), and then
generate a closing tag. A call to render() generally returns the
element's text, and renders its children recursively.
"""
OPEN_TAG = ''
CLOSE_TAG = ''
def __init__(self, initial_text=''):
self.children = []
self.text = initial_text
def to_kml(self):
kml = self.OPEN_TAG
kml += self.render()
kml += self.CLOSE_TAG + "\n"
return kml
def render(self):
kml = escape(self.text)
for c in self.children:
kml += c.to_kml()
return kml
def print_kml(self):
print self.OPEN_TAG
self.render_to_stdout()
print self.CLOSE_TAG
def render_to_stdout(self):
if (len(self.text) > 0):
print escape(self.text)
for c in self.children:
c.print_kml()
class Color(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class Document(KmlObject):
OPEN_TAG = """
"""
CLOSE_TAG = """
"""
def __init__(self, initial_text=''):
super(Document, self).__init__(initial_text)
self.styles = []
def render(self):
kml = ''
for s in self.styles:
kml += s.to_kml()
for c in self.children:
kml += c.to_kml()
return kml
def render_to_stdout(self):
for s in self.styles:
s.print_kml()
for c in self.children:
c.print_kml()
class Description(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class Coordinates(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class LineString(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
@classmethod
def parse_linestrings(self, kml):
"""
Parse each ... block from the KML.
"""
linestrings = []
search_idx = kml.find(self.OPEN_TAG, 0)
while (search_idx != -1):
# No reason to keep the tag around.
ls_start = search_idx + len(self.OPEN_TAG)
ls_tag_end = kml.find(self.CLOSE_TAG, ls_start)
ls = kml[ ls_start : ls_tag_end ]
linestrings.append(ls)
search_idx = kml.find(self.OPEN_TAG, (ls_tag_end + len(self.CLOSE_TAG)))
return linestrings
@classmethod
def parse_coordinates_from_linestrings(self, linestrings):
coords = []
for ls in linestrings:
c_tag_start = ls.find(Coordinates.OPEN_TAG)
c_start = c_tag_start + len(Coordinates.OPEN_TAG)
c_end = ls.find(Coordinates.CLOSE_TAG)
c = ls[ c_start : c_end ]
coords.append(c)
return coords
@classmethod
def tuples_from_kml(self, kml):
"""
Parse one or more linestrings from a KML document.
Return a list of tuples.
"""
ls = self.parse_linestrings(kml)
cs = self.parse_coordinates_from_linestrings(ls)
tuples = []
for c in cs:
pointstrings = c.strip().split()
for point in pointstrings:
components = point.strip().split(',')
if (len(components) >= 2):
# Project the three-dimensional vector onto the
# x-y plane. I don't think we're going to run
# in to any linestrings in 3d.
tuples.append( (float(components[0]), float(components[1])) )
return tuples
class Name(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class Placemark(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class PolyStyle(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class Style(KmlObject):
OPEN_TAG = "'
def __init__(self, initial_text='', initial_id=''):
super(Style, self).__init__(initial_text)
self.id = initial_id
def to_kml(self):
kml = ''
kml += (self.OPEN_TAG % self.id + "\n")
kml += self.render()
kml += self.CLOSE_TAG + "\n"
return kml
def print_kml(self):
print (self.OPEN_TAG % self.id)
self.render_to_stdout()
print self.CLOSE_TAG
class StyleUrl(KmlObject):
OPEN_TAG = ''
CLOSE_TAG = ''
class RawText(KmlObject):
def to_kml(self):
return self.text
def print_kml(self):
if (len(self.text) > 0):
print self.text