| from math import *
RADIUS_EARTH_METERS = 6366197.
def move(lat, lon, d, tc):
""" Moves point to d meters. tc is azimut of moving """
lat = radians(lat)
lon = radians(lon)
d = d/RADIUS_EARTH_METERS
tc = radians(-tc)
def mod(y,x):
return y - x*floor(y/x)
la = asin(sin(lat)*cos(d)+cos(lat)*sin(d)*cos(tc))
if cos(la) == 0:
lo = lon
else:
lo = lon-asin(sin(tc)*sin(d)/cos(la))
lo = mod(lo+pi,2*pi)-pi
return (degrees(la),degrees(lo))
class KMLElement(object):
def __init__(self, name = None, attributes = None, content = None):
if name is None:
name = self.__class__.__name__
self._name = name
self._attributes = attributes
self._content = None
if isinstance(content, dict):
for k,v in content.items():
self.add(KMLElement(k, None, v))
else:
self._content = content
def is_simple(self):
return not isinstance(self._content, list)
def add(self, element):
if self._content is None:
self._content = [element]
return element
if isinstance(self._content, str):
raise Exception("Can't add new subelemet to '%s', bacause it already has string content '%s'" % (self._name, self._content))
self._content.append(element)
return element
@staticmethod
def _special_handling(name, value):
if name == 'styleUrl':
if isinstance(value, str):
return value
if isinstance(value, Style):
return "#"+value._attributes['id']
else:
raise Exception('invalid values used as Style')
return value
def __setattr__(self, name, value):
if name.startswith('_'):
self.__dict__[name] = value
else:
e = KMLElement(name, None, self._special_handling(name, value))
self.add(e)
return e
def __str__(self):
if self._content is None:
c = None
elif isinstance(self._content, list):
c = ";".join([str(e) for e in self._content])
else:
c = str(self._content)
return "Element %s attributes: %s, content: [%s]" % (self._name, self._attributes, c)
def find(self, name):
if not isinstance(self._content, list):
return
for e in self._content:
if e._name == name:
yield e
for ee in e.find(name):
yield ee
class Document(KMLElement):
def __init__(self):
super(Document, self).__init__()
class Folder(KMLElement):
def __init__(self):
super(Folder, self).__init__()
class Style(KMLElement):
def __init__(self, id):
super(Style, self).__init__(attributes={'id':id})
class _StyleBase(KMLElement):
def __init__(self, kw):
attributes = None
if 'id' in kw:
attributes=dict(id=kw['id'])
del kw['id']
super(_StyleBase, self).__init__(attributes=attributes, content=kw)
class IconStyle(_StyleBase):
def __init__(self, href = None, **kw):
super(IconStyle, self).__init__(kw)
if href:
self.add(Icon(href))
class Icon(KMLElement):
def __init__(self, href):
super(Icon, self).__init__()
self.href = href
class LabelStyle(_StyleBase):
def __init__(self, **kw):
super(LabelStyle, self).__init__(kw)
class LineStyle(_StyleBase):
def __init__(self, **kw):
super(LineStyle, self).__init__(kw)
class PolyStyle(_StyleBase):
def __init__(self, **kw):
super(PolyStyle, self).__init__(kw)
class BalloonStyle(_StyleBase):
def __init__(self, **kw):
super(BalloonStyle, self).__init__(kw)
class ListStyle(_StyleBase):
def __init__(self, **kw):
super(ListStyle, self).__init__(kw)
class Placemark(KMLElement):
def __init__(self):
super(Placemark, self).__init__()
class Point(KMLElement):
def __init__(self, lat, lon, alt = 0):
super(Point, self).__init__()
self.coordinates = "%f,%f,%f" % (lon, lat, alt)
class LookAt(KMLElement):
def __init__(self, lat, lon, range, **kw):
super(LookAt, self).__init__(content=kw)
self.latitude = lat
self.longitude= lon
self.range = range
def _add_alt(p, alt):
if len(p) == 2:
return (p[1], p[0], alt)
else:
return (p[1], p[0], p[2])
class LineString(KMLElement):
def __init__(self, points, alt = 0):
super(LineString, self).__init__('LineString', None, None)
self.coordinates = "\n".join("%f,%f,%f" % _add_alt(p, alt) for p in points)
class LinearRing(KMLElement):
def __init__(self, coordinates, alt = 0):
super(LinearRing, self).__init__('LinearRing', None, None)
self.coordinates = "\n".join(["%f,%f,%f" % _add_alt(p, alt) for p in coordinates])
class Polygon(KMLElement):
def __init__(self, outerboundary, innerboundary = None, alt = 0):
super(Polygon, self).__init__('Polygon', None, None)
self.extrude = 0
self.altitudeMode = 'clampToGround'
if innerboundary:
boundary = self.add(KMLElement('innerBoundaryIs', None, None))
boundary.add(LinearRing(innerboundary, alt))
if outerboundary:
boundary = self.add(KMLElement('outerBoundaryIs', None, None))
boundary.add(LinearRing(outerboundary, alt))
def circle_polygon(lat, lon, radius, n = 36, alt = 0):
points = []
for angle in xrange(0, 360, 360/n):
points.append(move(lat, lon, radius, angle))
return Polygon(points, alt = alt)
def sector_polygon(lat, lon, radius, angle1, angle2, n, alt = 0):
points = [(lat,lon,alt)]
d = angle2-angle1
for x in xrange(0, n):
angle = angle1 + d*x/(n-1)
points.append(move(lat, lon, radius, angle))
points.append((lat,lon))
return Polygon(points, alt = alt)
class CDATA(object):
def __init__(self, s):
self.s = s
def get(self):
return "<![CDATA[" + self.s + "]]>"
class KMLWriter:
_filename = None
_file = None
_encoding = "UTF-8"
_escapes = [('&', '&'), ('"', '"'), ('<', '<'), ('>', '>>'), ("'", ''')]
_content = None
def __init__(self, filename):
self._filename = filename
def __del__(self):
if self._file:
self._file.close()
self._file = None
def _write(self, line):
self._file.write(line.encode(self._encoding))
def _current_indention(self):
return ' ' * (self._indention*4)
def _writeln(self, line):
line = self._current_indention() + line + '\n'
self._write(line)
def set_data(self, data):
self._content = data
return data
def save(self):
self._file = open(self._filename, "w")
self._indention = 0
self._writeln('<?xml version="1.0" encoding="%s"?>' % self._encoding)
self._writeln('<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">')
if self._content:
self._save_element(self._content)
self._writeln('</kml>')
self._file.close()
del(self._indention)
def escape(self, line):
if isinstance(line, CDATA):
return line.get()
else:
line = str(line)
for c,r in self._escapes:
line = line.replace(c, r)
return line
def reindent(self, s):
s = s.split('\n')
s = [self._current_indention() + line.lstrip() for line in s]
s[0] = s[0].lstrip()
return '\n'.join(s)
def _save_simple_element(self, name, content):
s = self.escape(content)
if s.find('\n') != -1:
self._writeln("<%s>" % name)
self._indention += 1
self._writeln(self.reindent(s))
self._indention -= 1
self._writeln("</%s>" % name)
else:
s = "<%s>%s</%s>" % (name, s, name)
self._writeln(s)
def _start_tag(self, name, attributes):
if attributes:
if isinstance(attributes, str):
attr = attributes
elif isinstance(attributes, dict):
attr = " ".join(['%s="%s"' % (k,self.escape(v)) for k,v in attributes.iteritems()])
else:
raise Exception("attributes should be str or dict, not %s" % type(attributes).__name__)
return "<%s %s>" % (name, attr)
else:
return "<%s>" % name
def _save_element(self, element):
if element._content is None:
raise Exception("Element %s has empty content" % element)
if element.is_simple():
self._save_simple_element(element._name, element._content)
return
self._writeln(self._start_tag(element._name, element._attributes))
self._indention += 1
for e in element._content:
self._save_element(e)
self._indention -= 1
self._writeln("</%s>" % element._name)
def find(self, name):
for f in self._content.find(name):
yield f
|