Add a conveter for OSMand favorites

This commit is contained in:
Stefan Liebl 2026-04-28 21:46:49 +02:00
parent 5b328e4d63
commit 674f406fba

106
gpx_to_osmand_favorites.py Executable file
View file

@ -0,0 +1,106 @@
#!/usr/bin/env python3
import os
import sys
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
GPX_NS = "http://www.topografix.com/GPX/1/1"
OSMAND_NS = "https://osmand.net"
ET.register_namespace("", GPX_NS)
ET.register_namespace("osmand", OSMAND_NS)
def iso_now_z():
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def localname(tag):
return tag.split("}", 1)[-1] if "}" in tag else tag
def find_child_by_localname(parent, wanted):
for ch in list(parent):
if localname(ch.tag) == wanted:
return ch
return None
def get_metadata_name_anyns(root):
md = find_child_by_localname(root, "metadata")
if md is None:
return None
nm = find_child_by_localname(md, "name")
if nm is None:
return None
txt = (nm.text or "").strip()
return txt or None
def guess_group(in_path, root, prefix):
base = get_metadata_name_anyns(root)
if not base:
base = os.path.splitext(os.path.basename(in_path))[0]
if prefix:
return f"{prefix.rstrip('/')}/{base}"
return base
def main():
if len(sys.argv) not in (3, 4):
print(
"Usage: gpx_to_osmand_favorites_autogroup_fixed.py input.gpx output.gpx [prefix]\n"
"Example: gpx_to_osmand_favorites_autogroup_fixed.py 4x4.gpx 4x4_osmand.gpx Norwegen",
file=sys.stderr
)
sys.exit(2)
in_path = sys.argv[1]
out_path = sys.argv[2]
prefix = sys.argv[3] if len(sys.argv) == 4 else ""
tree = ET.parse(in_path)
root = tree.getroot()
group = guess_group(in_path, root, prefix)
in_wpts = [el for el in root.iter() if localname(el.tag) == "wpt"]
if not in_wpts:
raise SystemExit(f"No <wpt> elements found in {in_path}")
out_root = ET.Element(f"{{{GPX_NS}}}gpx", {
"version": "1.1",
"creator": "gpx_to_osmand_favorites_autogroup_fixed.py",
})
md = ET.SubElement(out_root, f"{{{GPX_NS}}}metadata")
ET.SubElement(md, f"{{{GPX_NS}}}name").text = f"OsmAnd favorites: {group}"
ET.SubElement(md, f"{{{GPX_NS}}}time").text = iso_now_z()
written = 0
for w in in_wpts:
lat = w.attrib.get("lat")
lon = w.attrib.get("lon")
if lat is None or lon is None:
continue
out_wpt = ET.SubElement(out_root, f"{{{GPX_NS}}}wpt", {"lat": str(lat), "lon": str(lon)})
# Copy common fields if present
for ch in list(w):
ln = localname(ch.tag)
if ln in ("name", "cmt", "desc", "ele", "time") and ch.text not in (None, ""):
ET.SubElement(out_wpt, f"{{{GPX_NS}}}{ln}").text = ch.text
# Ensure we have a name
if find_child_by_localname(out_wpt, "name") is None:
ET.SubElement(out_wpt, f"{{{GPX_NS}}}name").text = f"Favorite {written+1}"
# OsmAnd favorites group (key field)
ET.SubElement(out_wpt, f"{{{GPX_NS}}}type").text = group
# Optional OsmAnd hint (safe if ignored)
ext = ET.SubElement(out_wpt, f"{{{GPX_NS}}}extensions")
ET.SubElement(ext, f"{{{OSMAND_NS}}}category").text = group
written += 1
ET.ElementTree(out_root).write(out_path, encoding="UTF-8", xml_declaration=True)
print(f"Wrote {written} waypoint(s) to {out_path} with OsmAnd favorites group '{group}'")
if __name__ == "__main__":
main()