-
Notifications
You must be signed in to change notification settings - Fork 0
/
gpxmerge.py
168 lines (143 loc) · 6.01 KB
/
gpxmerge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/python
# -*- coding: ascii -*-
__author__ = 'Bob Durie'
__copyright__ = 'Copyright 2019, GPX Merge'
__credits__ = ['{gcdist lifted from https://gist.github.com/dal/4416699}']
__version__ = '1.0'
import xml.etree.ElementTree as ET
import argparse
import collections
import math
import os
# setup our distance calculation functions
Point = collections.namedtuple('Point', ['lat', 'lon'])
nauticalMilePerLat = 60.00721
nauticalMilePerLongitude = 60.10793
milesPerNauticalMile = 1.15078
mPerMile = 1.60934 * 1000.0
def gcDist(pt1, pt2):
'''
Caclulate great circle distance between two lat lons in miles.
@param pt1: The first Point instance.
@param pt2: The second Point instance.
@return: Great circle distance between the two points in miles.
'''
yDistance = (pt2.lat - pt1.lat) * nauticalMilePerLat
xDistance = (math.cos(math.radians(pt1.lat)) + math.cos(math.radians(pt2.lat))) \
* (pt2.lon - pt1.lon) * (nauticalMilePerLongitude / 2.0)
distance = math.sqrt( yDistance**2.0 + xDistance**2.0 )
return distance * milesPerNauticalMile * mPerMile
def elemToPoint(elem):
lat = elem.attrib['lat']
lon = elem.attrib['lon']
return Point(float(lat), float(lon))
def merge(mergeArgs):
# Get our gpx files
files = []
# r=root, d=directories, f = files
for r, d, f in os.walk(mergeArgs.indir):
for file in f:
if '.gpx' in file:
files.append(os.path.join(r, file))
# sort em by name
files.sort()
# Start a new root
mergedRoot = ET.Element("gpx")
# read each file, and parse them
totalTracks = 0
for file in files:
print(file)
tree = ET.parse(open(file, "r"))
root = tree.getroot()
for trk in root:
if trk.tag != "{http://www.topografix.com/GPX/1/1}trk":
print("not merging trk: " + trk.tag)
continue
# add it into our merged doc
mergedRoot.append(trk)
totalTracks = totalTracks + 1
#print("Total Points: " + str(totalPoints))
#print("Legs with points vs legs provided: (" + str(legIndex + 1) + " / " + str(numLegs) + ")")
print("Total Tracks: " + str(totalTracks))
tree = ET.ElementTree(mergedRoot)
tree.write(mergeArgs.outfile)
def foo():
tree = ET.parse(sliceArgs.infile)
root = tree.getroot()
# parse our file that defines how we'd like to split it up
lines = sliceArgs.legDistancesFile.read().strip().split('\n')
# these are in km, so move them in to m
legDistanceMarkers = [ (1000.0 * float(s)) for s in lines ]
legDistances = []
# i updated the way they're fed in from absolute leg distances to
# distance into the total distance, so this just converts back
# (this change made so its easier to adjust the end point of a leg
# without messing up further legs)
for i in range(len(legDistanceMarkers)):
if i == 0:
legDistances.append(legDistanceMarkers[i])
else:
legDistances.append(legDistanceMarkers[i] - legDistanceMarkers[i - 1])
numLegs = len(legDistances)
# main vars
totalDistance = 0.0
legDistance = 0.0
legIndex = 0
totalPoints = 0
firstIteration = True
# first thing, add in all our new legs
legtrksegs = []
for i in range(numLegs):
newtrk = ET.SubElement(root, 'trk')
name = ET.SubElement(newtrk, 'name')
name.text = 'Segment ' + str(i + 1)
trkseg = ET.SubElement(newtrk, 'trkseg')
legtrksegs.append(trkseg)
# Root is assumed to be gpx
for trk in root:
if trk.tag != "{http://www.topografix.com/GPX/1/1}trk":
continue
for trkchild in trk:
if trkchild.tag == "{http://www.topografix.com/GPX/1/1}trkseg":
for trkpt in trkchild:
if trkpt.tag != "{http://www.topografix.com/GPX/1/1}trkpt":
print("didn't find a point: " + trkpt.tag)
continue
try:
curPoint = elemToPoint(trkpt)
except:
print("not a point: " + str(trkpt))
exit(1)
# we're good, add this point to our current leg
legtrksegs[legIndex].append(trkpt)
totalPoints = totalPoints + 1
if firstIteration == False:
incrementalDistance = gcDist(lastPoint, curPoint)
legDistance = legDistance + incrementalDistance
totalDistance = totalDistance + incrementalDistance
if legIndex + 1 < numLegs and totalDistance >= legDistanceMarkers[legIndex]: #legDistance >= legDistances[legIndex]:
legIndex = legIndex + 1
print("On to next leg: " + str(legIndex + 1) + ", last leg distance: " + str(legDistance) + ", total: " + str(totalDistance))
legDistance = 0.0
# We actually need to copy this point into the new leg too!
legtrksegs[legIndex].append(trkpt)
lastPoint = curPoint
firstIteration = False
print("Total Points: " + str(totalPoints))
print("Legs with points vs legs provided: (" + str(legIndex + 1) + " / " + str(numLegs) + ")")
print("Total Distance: " + str(totalDistance))
tree.write(sliceArgs.outfile)
#
#
#
# Register our namespace so the output looks ok
ET.register_namespace('', "http://www.topografix.com/GPX/1/1")
# And parse all the args for our 2 sub commands
parser = argparse.ArgumentParser(description='Tools to help merge a GPX file')
subparsers = parser.add_subparsers(help='sub-command help')
mergeParser = subparsers.add_parser('merge', help='Merge a GPX file')
mergeParser.add_argument("indir", help="Directory of GPX files to process")
mergeParser.add_argument('outfile', help='output file name')
mergeParser.set_defaults(func=merge)
args = parser.parse_args()
args.func(args)