Files
ThingsBoard/Code Snippets/rrig_kmz/breakout_kml.py
2026-02-04 18:00:22 -06:00

142 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
KML coordinate extractor
------------------------
* Every <Polygon> element → list of [lat, lon] pairs, written to *output_polygons*.
* Every <LineString> element → list of [lat, lon] pairs, written to *output_linestrings*.
* Every <Placemark> that contains a <name> and a <Point> → “Marker Name: lat, lon”
written to *output_markers*.
The coordinates in a KML file are stored as `lon,lat[,alt]`.
The script converts them to `lat,lon` because thats the format you asked for.
"""
import xml.etree.ElementTree as ET
import sys
from pathlib import Path
# ----------------------------------------------------------------------
# Helpers
# ----------------------------------------------------------------------
def parse_kml(file_path: Path) -> ET.ElementTree:
"""Parse the KML file; exit with a message on failure."""
try:
return ET.parse(file_path)
except ET.ParseError as exc:
print(f"ERROR: Failed to parse {file_path}: {exc}", file=sys.stderr)
sys.exit(1)
def _lon_lat_to_list(coord_str: str) -> list[list[float]]:
"""
Convert a KML coordinate string (lon,lat[,alt] …) into a list of [lat, lon].
Ignores malformed entries.
"""
coords = []
for pair in coord_str.strip().split():
parts = pair.split(',')
if len(parts) < 2:
continue
lon, lat = float(parts[0]), float(parts[1])
coords.append([lat, lon])
return coords
# ----------------------------------------------------------------------
# Main extraction routine
# ----------------------------------------------------------------------
def extract_kml(
kml_file: Path,
out_polygons: Path,
out_linestrings: Path,
out_markers: Path,
) -> None:
tree = parse_kml(kml_file)
root = tree.getroot()
# Namespace handling (most KML files use the same namespace)
ns = {'kml': root.tag.split('}')[0].strip('{')}
# ------------------------------------------------------------------
# 1) Polygons
# ------------------------------------------------------------------
polygons = [] # List[ List[ [lat, lon] ] ]
for poly in root.findall('.//kml:Polygon', ns):
coord_el = poly.find('.//kml:coordinates', ns)
if coord_el is None or not coord_el.text:
continue
polygons.append(_lon_lat_to_list(coord_el.text))
# Write polygons
with out_polygons.open('w', encoding='utf-8') as fp:
for i, poly in enumerate(polygons, start=1):
fp.write(f"Polygon {i}:\n")
fp.write(str(poly) + "\n\n")
# ------------------------------------------------------------------
# 2) LineStrings
# ------------------------------------------------------------------
linestrings = [] # List[ List[ [lat, lon] ] ]
for ls in root.findall('.//kml:LineString', ns):
coord_el = ls.find('.//kml:coordinates', ns)
if coord_el is None or not coord_el.text:
continue
linestrings.append(_lon_lat_to_list(coord_el.text))
# Write LineStrings
with out_linestrings.open('w', encoding='utf-8') as fp:
for i, ls in enumerate(linestrings, start=1):
fp.write(f"LineString {i}:\n")
fp.write(str(ls) + "\n\n")
# ------------------------------------------------------------------
# 3) Markers (Point placemarks with a name)
# ------------------------------------------------------------------
markers = [] # List[ (name, [lat, lon]) ]
for pm in root.findall('.//kml:Placemark', ns):
name_el = pm.find('kml:name', ns)
point_el = pm.find('.//kml:Point/kml:coordinates', ns)
if name_el is None or point_el is None:
continue
name = name_el.text.strip()
coord_list = _lon_lat_to_list(point_el.text)
if not coord_list:
continue
markers.append((name, coord_list[0])) # a point has exactly one pair
# Write markers
with out_markers.open('w', encoding='utf-8') as fp:
for name, (lat, lon) in markers:
fp.write(f"{name}: {lat}, {lon}\n")
# ------------------------------------------------------------------
# Summary
# ------------------------------------------------------------------
print(f"Polygons written to: {out_polygons}")
print(f"LineStrings written to: {out_linestrings}")
print(f"Markers written to: {out_markers}")
# ----------------------------------------------------------------------
# Entrypoint
# ----------------------------------------------------------------------
def main() -> None:
if len(sys.argv) != 5:
print(
"Usage: python breakout_kml.py <input_file> "
"<output_polygons> <output_linestrings> <output_markers>"
)
sys.exit(1)
input_file = Path(sys.argv[1]).expanduser().resolve()
out_polygons = Path(sys.argv[2]).expanduser().resolve()
out_linestrings = Path(sys.argv[3]).expanduser().resolve()
out_markers = Path(sys.argv[4]).expanduser().resolve()
if not input_file.exists():
print(f"ERROR: Input file not found: {input_file}", file=sys.stderr)
sys.exit(1)
extract_kml(input_file, out_polygons, out_linestrings, out_markers)
if __name__ == "__main__":
main()