93 lines
2.7 KiB
Python
Executable file
93 lines
2.7 KiB
Python
Executable file
#!/home/enigma/.virtualenvs/gpx_clean/bin/python
|
|
|
|
import sys
|
|
|
|
import numpy as np
|
|
import gpxpy
|
|
|
|
|
|
def clean_segment(segment, badness=4):
|
|
"""Lazily strip out anything not in IQR"""
|
|
new_points = []
|
|
speeds = []
|
|
for index, point in enumerate(segment.points[1:]):
|
|
prev = segment.points[index-1]
|
|
if point.time == prev.time:
|
|
continue
|
|
speeds.append(point.distance_2d(prev) / (point.time - prev.time).total_seconds())
|
|
# Calculate IQR
|
|
if not speeds:
|
|
return
|
|
speeds.sort()
|
|
speed_mean = np.mean(speeds)
|
|
speed_mu = np.std(speeds)
|
|
if speed_mean * 3.6 < 5:
|
|
speed_mean = 5 / 3.6
|
|
def Z(speed):
|
|
return (speed - speed_mean) / speed_mu
|
|
on_bad = False
|
|
new_points = [segment.points[0]]
|
|
bad_set = []
|
|
last_good = None
|
|
for index, point in enumerate(segment.points[1:]):
|
|
if len(bad_set) > 10:
|
|
new_points.extend(bad_set)
|
|
bad_set = []
|
|
on_bad = False
|
|
if on_bad:
|
|
prev = bad_set[0]
|
|
else:
|
|
prev = segment.points[index-1]
|
|
speed = point.distance_2d(prev) / (point.time - prev.time).total_seconds()
|
|
this_bad = Z(speed)
|
|
if on_bad:
|
|
if this_bad >= badness:
|
|
# See if this large shift puts us back on track
|
|
new_speed = last_good.distance_2d(point) / (last_good.time - point.time).total_seconds()
|
|
if this_bad < badness:
|
|
# We're going to go back, so discard this bad set
|
|
bad_set = []
|
|
on_bad = False
|
|
else:
|
|
bad_set.append(point)
|
|
else:
|
|
if this_bad >= badness:
|
|
bad_set.append(point)
|
|
on_bad = True
|
|
last_good = prev
|
|
else:
|
|
new_points.append(point)
|
|
#print(f"Starting with {len(segment.points)} pts, now have {len(new_points)}")
|
|
segment.points = new_points
|
|
|
|
|
|
def clean_track(track):
|
|
for segment in track.segments:
|
|
clean_segment(segment)
|
|
|
|
|
|
def show_moving_data(pre, md):
|
|
speed = md.moving_distance / md.moving_time * 3.6
|
|
print(f"{pre}: Moved at {speed:.2f} km/h")
|
|
|
|
|
|
def clean_gpx(gpx):
|
|
for track in gpx.tracks:
|
|
clean_track(track)
|
|
|
|
|
|
def clean_file(filename):
|
|
with open(filename, 'r') as infile:
|
|
gpx = gpxpy.parse(infile)
|
|
md = gpx.get_moving_data()
|
|
show_moving_data("Before clean", md)
|
|
clean_gpx(gpx)
|
|
md = gpx.get_moving_data()
|
|
show_moving_data("After clean", md)
|
|
with open(f"{filename[:-4]}_cleaned.gpx", "w") as outfile:
|
|
outfile.write(gpx.to_xml())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for filename in sys.argv[1:]:
|
|
clean_file(filename)
|