from math import *
import lxml.etree as etree

KML = 'http://earth.google.com/kml/2.1'
NSD = {'kml': KML}
VERTFREQ = 5
    
# horizontal map accuracy in inches
HACC = {'large': 0.03333, 'small': 0.02}
    
PI = 3.1459
    
def circle(ctr_lat, ctr_lon, alt, radius):
    """Calculate a lattitude-appropriate circle with center at ctr_lat/ctrl_lon and radius. 
    Return KML-style vertices"""
    ctr_lat_rad = deg2rad(ctr_lat)
    ctr_lon_rad = deg2rad(ctr_lon)
    radius_earth = 6378137 * (1 - 0.0033493 * sin(ctr_lat) ** 2)
    radius_surface = radius/radius_earth
    vertices = []
    
    angles = [deg2rad(angle) for angle in range(361) if angle % VERTFREQ == 0]
    for angle in angles:
        vertex_lat_rad = asin(sin(ctr_lat_rad) * cos(radius_surface) + cos(ctr_lat_rad) * sin (radius_surface) * cos(angle))
        dlon_rad = atan2(sin(angle) * sin(radius_surface) * cos(ctr_lat_rad), cos(radius_surface)-sin(ctr_lat_rad)*sin(vertex_lat_rad))
        vertex_lon_rad = fmod((ctr_lon_rad+dlon_rad+math.pi), 2*math.pi) - math.pi
        #print "%s, %s, 0 " % (rad2deg(vertex_lon_rad), rad2deg(vertex_lat_rad))
        vertices.append((rad2deg(vertex_lon_rad), rad2deg(vertex_lat_rad), alt))
    return vertices
        
def kmlcirclfy(infilename, outfilename, scale):
    hac = calchac(scale)
    
    f = open(infilename)
    infile = f.read()
    f.close()
    kml = etree.XML(infile)
    pmarks = kml.xpath('//kml:Placemark', NSD)
    for pm in pmarks:
        # add style information
        s = etree.Element("styleUrl")
        s.text = '#defaultStyles'
        pm.append(s)
        # read and remove the existing Point
        p = pm.xpath('kml:Point', NSD)[0]
        x,y,z = p.xpath('kml:coordinates', NSD)[0].text.split(',')
        pm.remove(p)
        # create a MultiGeometry bundle
        multi = etree.Element("MultiGeometry")
        pm.append(multi)
        # put in a new version of the same old Point
        n = etree.Element("Point")
        multi.append(n)
        n = etree.Element("coordinates")
        n.text = "%s, %s, %s" % (x,y,z)
        multi[-1].append(n)
        # add a polygonal circle corresponding to the horizontal map accuracy bubble for this scale
        n = etree.XML("<Polygon><outerBoundaryIs><LinearRing></LinearRing></outerBoundaryIs></Polygon>")
        multi.append(n)
        appendcirc(n.xpath("descendant::*[local-name() = 'LinearRing']")[0], x, y, z, hac)
        # add another polygonal circle for the supposed max error
        n = etree.XML("<Polygon><outerBoundaryIs><LinearRing></LinearRing></outerBoundaryIs></Polygon>")
        multi.append(n)
        appendcirc(n.xpath("descendant::*[local-name() = 'LinearRing']")[0], x, y, z, hac * 3)
        
    outfile = etree.tostring(kml).encode('utf-8')
    f = open(outfilename, 'w')
    f.write(outfile)
    f.close()
    
    
def appendcirc(pnode, x, y, z, r):
    vertices = circle(eval(y), eval(x), eval(z), r)
    n = etree.Element("coordinates")
    n.text = ""
    for v in vertices:
        n.text = n.text + " %s, %s, %s " % v
    pnode.append(n)
    
    
def deg2rad(d):
    return d * math.pi / 180.0
    
def rad2deg(r):
    return r * 180.0 / math.pi
    
def calchac(scale):
    if scale < 20000:
        hac = HACC['large']
    else:
        hac = HACC['small']
    hac_m = hac / 12 * 0.3048 * scale
    return hac_m
    
        
    
    