aboutsummaryrefslogtreecommitdiff
path: root/src/kml_utils.py
blob: 70319f830a645064d1d82029a132fff9aac5a430 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"""Shared helpers for building simple KML documents from polygon data."""

import xml.etree.ElementTree as ET

KML_NS = "http://www.opengis.net/kml/2.2"
ET.register_namespace("", KML_NS)


def new_document(name: str) -> tuple[ET.Element, ET.Element]:
    """Create a <kml><Document> pair and return (root, document).

    Args:
        name (str): Name of the document, shown as the map/layer title.
    Returns:
        tuple[ET.Element, ET.Element]: The root <kml> element and its child <Document> element.
    """
    root = ET.Element("kml", xmlns=KML_NS)
    document = ET.SubElement(root, "Document")
    ET.SubElement(document, "name").text = name
    return root, document


def add_placemark(document: ET.Element, name: str = "", description: str = "") -> ET.Element:
    """Add a <Placemark> to `document` and return it.

    Args:
        document (ET.Element): The <Document> element to attach the placemark to.
        name (str, optional): Placemark name. Defaults to "".
        description (str, optional): Placemark description. Defaults to "".
    Returns:
        ET.Element: The newly created <Placemark> element.
    """
    placemark = ET.SubElement(document, "Placemark")
    ET.SubElement(placemark, "name").text = name
    ET.SubElement(placemark, "description").text = description
    return placemark


def add_polygon(parent: ET.Element, ring: list[tuple[float, float]]) -> ET.Element:
    """Add a single-ring <Polygon> to `parent`.

    Args:
        parent (ET.Element): Element to attach the polygon to (a <Placemark> or <MultiGeometry>).
        ring (list[tuple[float, float]]): Outer boundary points as (longitude, latitude) pairs.
    Returns:
        ET.Element: The newly created <Polygon> element.
    """
    polygon = ET.SubElement(parent, "Polygon")
    outer = ET.SubElement(polygon, "outerBoundaryIs")
    linear_ring = ET.SubElement(outer, "LinearRing")
    coordinates = ET.SubElement(linear_ring, "coordinates")
    coordinates.text = "\n".join(f"{lon},{lat}" for lon, lat in ring)
    return polygon


def add_multi_polygon(placemark: ET.Element, rings: list[list[tuple[float, float]]]) -> ET.Element:
    """Add a <MultiGeometry> made of one <Polygon> per ring to `placemark`.

    Args:
        placemark (ET.Element): The <Placemark> element to attach the geometry to.
        rings (list[list[tuple[float, float]]]): One outer boundary ring per polygon.
    Returns:
        ET.Element: The newly created <MultiGeometry> element.
    """
    multi_geometry = ET.SubElement(placemark, "MultiGeometry")
    for ring in rings:
        add_polygon(multi_geometry, ring)
    return multi_geometry


def write_kml(root: ET.Element, output_file: str) -> None:
    """Indent and write a KML tree to `output_file`.

    Args:
        root (ET.Element): The root <kml> element to write.
        output_file (str): Path to the output KML file.
    """
    tree = ET.ElementTree(root)
    ET.indent(tree)
    tree.write(output_file, encoding="utf-8", xml_declaration=True)