diff options
Diffstat (limited to 'src/kml_utils.py')
| -rw-r--r-- | src/kml_utils.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/src/kml_utils.py b/src/kml_utils.py new file mode 100644 index 0000000..70319f8 --- /dev/null +++ b/src/kml_utils.py @@ -0,0 +1,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) |
