#!/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 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()