Search code examples
pythonkmlgoogle-earth

Circular polygon in KML only showing in preview


Background:

As GE has no built in circle function, I have generated some code in Python to create a list of coordinates (in decimal degrees) about a centre point - which should form a circle. In the same Python script, I have then created a KML file (aptly named 'circles'), in which the coordinate points generated are written to in a format which should allow them to be the vertices of the circular polygon I am trying to create.

This is the Python script which generates the coordinate points for the circle and exports them to the KML file:

import math

# opens a file for writing/ creates it if it does not exist
file = open ('circles.kml', 'w+')


def generate_circle(file, lat_deg, lon_deg, radius_km):

    # Mean Earth radius (needed for calculation).
    earth_radius_km = 6371
    earth_radius_m = earth_radius_km * 1000

    # Distance is entered in km, convert to meters.
    radius_m = radius_km * 1000
    angular_distance = radius_m/earth_radius_m

    # Convert coordinates from degrees to radians.
    lat_rad = math.radians(lat_deg)
    lon_rad = math.radians(lon_deg)

    # start generating KML code in file
    file.write ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" )
    file.write ("<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\">")
    file.write ("\n<Document>\n\t<Placemark>\n\t\t<name>Circle</name>\n" )
    file.write ("\t\t<Polygon>\n\t\t\t<extrude>1</extrude>\n\t\t\t<altitudeMode>relativeToGround</altitudeMode>\n\t\t\t<outerBoundaryIs>\n\t\t\t\t<LinearRing>\n\t\t\t\t\t<coordinates>\n")

    # Create a list of angles at which to create points (how many points will the circle consist of).
    numPoints = range(0, 360, 10)
    angles = []
    for x in numPoints:
        angles.append(float(x))
    angles.append(float(0))

    # Calculate and file.write out the list of coordinates.
    for angle in angles:

        # Convert bearing to radians and calculates new lat/lon values
        bearing = math.radians(angle)

        new_lat = math.asin(math.sin(lat_rad) * math.cos(angular_distance) + math.cos(lat_rad) * math.sin(angular_distance) * math.cos(bearing))

        new_lon = lon_rad + math.atan2(math.sin(bearing) * math.sin(angular_distance) * math.cos(lat_rad), math.cos(angular_distance) - math.sin(lat_rad) * math.sin(new_lat))

        # Convert new lat and lon to degrees
        new_lat_deg = math.degrees(new_lat)
        new_lon_deg = math.degrees(new_lon)

        # Print them out
        file.write ('\t\t\t\t\t\t{0}, {1}\n'.format (str(new_lat_deg), str(new_lon_deg)))

    # generates KML code to end file
    file.write ("\n\t\t\t\t\t</coordinates>\n\t\t\t\t</LinearRing>\n\t\t\t</outerBoundaryIs>\n\t\t</Polygon>")
    file.write ("\n\t</Placemark>\n</Document>\n</kml>")
    file.close ()

generate_circle(file, 51.13046, -0.18433, 3)

This script then generates the 'circles.kml' file with the following code:

<?xml version="1.0" encoding="UTF-8"?>
<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">
<Document>
    <Placemark>
        <name>Circle</name>
        <Polygon>
            <extrude>1</extrude>
            <altitudeMode>relativeToGround</altitudeMode>
            <outerBoundaryIs>
                <LinearRing>
                    <coordinates>
                        51.15743964817757, -0.18433
                        51.15702952889536, -0.17686020516372258
                        51.155811653802246, -0.16961776449158056
                        51.15382308927849, -0.1628230771806833
                        51.15112435160843, -0.15668284950397615
                        51.14779755677011, -0.15138378358603402
                        51.14394391134279, -0.14708688836809072
                        51.13968062247034, -0.14392258756569956
                        51.13513732257409, -0.14198677322557574
                        51.1304521191404, -0.14133792278558727
                        51.12576739098043, -0.1419953635060592
                        51.121225459580394, -0.1439387320133288
                        51.11696426736606, -0.14710863972615704
                        51.11311319387112, -0.1514085183214467
                        51.10978913602661, -0.15670758424327305
                        51.10709297028997, -0.16284482854858487
                        51.105106502425905, -0.16963390895039396
                        51.1038899958304, -0.176868795451506
                        51.10348035182245, -0.18433
                        51.1038899958304, -0.191791204548494
                        51.105106502425905, -0.19902609104960603
                        51.10709297028997, -0.20581517145141515
                        51.10978913602661, -0.21195241575672694
                        51.11311319387112, -0.2172514816785533
                        51.11696426736606, -0.22155136027384292
                        51.121225459580394, -0.2247212679866712
                        51.12576739098043, -0.22666463649394078
                        51.1304521191404, -0.22732207721441272
                        51.13513732257409, -0.22667322677442425
                        51.13968062247034, -0.22473741243430043
                        51.14394391134279, -0.22157311163190926
                        51.14779755677011, -0.21727621641396597
                        51.15112435160843, -0.21197715049602384
                        51.15382308927849, -0.20583692281931668
                        51.155811653802246, -0.19904223550841943
                        51.15702952889536, -0.1917997948362774
                        51.15743964817757, -0.18433
                    </coordinates>
                </LinearRing>
            </outerBoundaryIs>
        </Polygon>
    </Placemark>
</Document>
</kml>

The problem:

When the KML file is imported into GE, the file itself and the polygon are visible in 'My Places' but the polygon does not appear on the map whatsoever - instead defaulting to a 0,0 position.

As I'm aware that KML is very particular about the order of coordinates for polygons being listed in an anti-clockwise order, I tried reformatting my KML file to allow for this - but the same thing happened again and the polygon still wasn't being displayed. As well as this, the opacity in the properties box in GE is showing the polygon as having a 100% opacity, so the polygon should definitely be visible.

I'm still relatively new to KML and would love to learn how to create circles by generating coordinate points rather than simply using the available online KML Circle Generator tools. I've found that there's very little (if any) documentation on this issue, so any help would be greatly appreciated!!


Solution

  • Your KML file has an extra space after the comma in each coordinate pair. If you remove those spaces, the polygon shows up (in the Indian Ocean). Looks like an oval in this case, which may be a projection issue?

    The coordinates should look like: 51.15743964817757,-0.18433 There should not be a space after the comma (inside the coordinate pair), but there can be a space (or line return like you have) separating coordinate pairs. I'm don't know much about Python, but I think you just need to change one line to remove the space, like this:

    # Print them out    
    file.write ('\t\t\t\t\t\t{0},{1}\n'.format (str(new_lat_deg), str(new_lon_deg)))
    

    Also, note that you have your <altitudeMode> set to "relativeToGround", which is normally used to raise features above the ground... but your coordinates don't include any altitude value (it would be a 3rd term in each coordinate, such as: 51.15743,-0.18433,100 for 100m high). You can either add altitude numbers to each coordinate pair, or just change your altitudeMode to be "clampToGround", which is the default and will put the feature on the terrain over land, or on the sea surface over water.