Add files via upload

This commit is contained in:
Atakan Doğan Özban
2025-10-23 16:34:45 +03:00
committed by GitHub
parent 0d86c28494
commit 85e5839f99
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
import sys
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta, timezone
from copy import deepcopy
NS = {"gpx": "http://www.topografix.com/GPX/1/1"}
ET.register_namespace("", NS["gpx"])
def parse_time(t: str) -> datetime:
# GPX ISO8601 times end with Z (UTC)
return datetime.fromisoformat(t.replace("Z", "+00:00")).astimezone(timezone.utc)
def fmt_time(dt: datetime) -> str:
return dt.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def get_ele(tp):
el = tp.find("gpx:ele", NS)
if el is not None and el.text not in (None, ""):
try:
return float(el.text)
except ValueError:
return None
return None
def set_ele(tp, value):
if value is None:
return
el = tp.find("gpx:ele", NS)
if el is None:
el = ET.SubElement(tp, f"{{{NS['gpx']}}}ele")
el.text = f"{value:.2f}"
def interpolate_segment(seg):
"""Return a NEW <trkseg> with gaps >1s filled at 1s steps (linear lat/lon/ele)."""
pts = seg.findall("gpx:trkpt", NS)
if len(pts) < 2:
return deepcopy(seg)
new_seg = ET.Element(f"{{{NS['gpx']}}}trkseg")
inserted_total = 0
gaps = 0
for i in range(len(pts) - 1):
cur, nxt = pts[i], pts[i + 1]
new_seg.append(deepcopy(cur))
t1_el = cur.find("gpx:time", NS)
t2_el = nxt.find("gpx:time", NS)
if t1_el is None or t2_el is None or not t1_el.text or not t2_el.text:
continue
t1, t2 = parse_time(t1_el.text), parse_time(t2_el.text)
dt = int((t2 - t1).total_seconds())
if dt <= 1:
continue
gaps += 1
lat1, lon1 = float(cur.get("lat")), float(cur.get("lon"))
lat2, lon2 = float(nxt.get("lat")), float(nxt.get("lon"))
ele1, ele2 = get_ele(cur), get_ele(nxt)
# Fill each missing second with linear interpolation
for s in range(1, dt):
alpha = s / dt
lat = lat1 + (lat2 - lat1) * alpha
lon = lon1 + (lon2 - lon1) * alpha
ele = None if (ele1 is None or ele2 is None) else (ele1 + (ele2 - ele1) * alpha)
new_tp = ET.Element(f"{{{NS['gpx']}}}trkpt", attrib={
"lat": f"{lat:.7f}",
"lon": f"{lon:.7f}",
})
if ele is not None:
ele_el = ET.SubElement(new_tp, f"{{{NS['gpx']}}}ele")
ele_el.text = f"{ele:.2f}"
time_el = ET.SubElement(new_tp, f"{{{NS['gpx']}}}time")
time_el.text = fmt_time(t1 + timedelta(seconds=s))
new_seg.append(new_tp)
inserted_total += 1
# append last original point
new_seg.append(deepcopy(pts[-1]))
return new_seg
def main(in_path, out_path):
tree = ET.parse(in_path)
root = tree.getroot()
# keep original structure, only replace each trkseg content with interpolated version
changed = False
for trk in root.findall(".//gpx:trk", NS):
for seg in trk.findall("gpx:trkseg", NS):
new_seg = interpolate_segment(seg)
if new_seg is not None:
trk.remove(seg)
trk.append(new_seg)
changed = True
if not changed:
print("No <trkseg> found or no changes made.", file=sys.stderr)
tree.write(out_path, encoding="utf-8", xml_declaration=True)
print(f"Interpolated GPX saved to: {out_path}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python interpolate_gpx.py input.gpx output.gpx", file=sys.stderr)
sys.exit(1)
main(sys.argv[1], sys.argv[2])