-
Notifications
You must be signed in to change notification settings - Fork 0
/
gpxrecode.py
executable file
·222 lines (181 loc) · 6.45 KB
/
gpxrecode.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/python
# DEPRECATED? I think tcxrecode is better
# Take a gpx xml file from RideWithGPS and convert the route points to something my ETrex can use.
import math
import os
import os.path
import pickle
import re
import string
import sys
import xml.sax
from xml.sax.saxutils import XMLFilterBase, XMLGenerator
waypoints = []
NAME = 0
LAT = 1
LON = 2
def shorten_rtept(rtept):
"""ETrex only supports 6 upper case characters"""
return rtept.upper()[:6].strip()
def comment_to_rtept(comment):
"""Take a ridewithGPS comment and make a nice short routepoint out of it"""
if comment == "Start of route":
return "START"
elif comment == "End of route":
return "END"
elif comment == "Turn right":
return "RIGHT"
elif comment == "Turn left":
return "LEFT"
ONTO_MATCHER = re.compile("onto (.*)$")
STAY_MATCHER = re.compile("to stay on (.*)$")
TOWARD_MATCHER = re.compile("toward (.*)$")
TAKE_MATCHER = re.compile("Take the (.*)$")
matchers = (ONTO_MATCHER, STAY_MATCHER, TOWARD_MATCHER, TAKE_MATCHER)
for m in matchers:
match = m.search(comment)
if match is not None:
return match.group(1)
print "Warning, unhandled comment format:", comment
return comment
class TurnFilter(XMLFilterBase):
"""A filter which reads the comments from a gpx file into a map for later processing"""
def __init__(self, In):
XMLFilterBase.__init__(self, In)
self.TurnMap = {}
self._in_rtept = ""
self._in_cmt = False
def startElement(self, name, attrs):
if name == "rtept":
self._in_rtept = "%s,%s" % (attrs['lat'], attrs['lon'])
elif name == "cmt":
self._in_cmt = True
def endElement(self, name):
if name == "rtept":
self._in_rtept = ""
elif name == "cmt":
self._in_cmt = False
def characters(self, content):
if not self._in_cmt:
return
self.TurnMap[self._in_rtept] = shorten_rtept(comment_to_rtept(content))
class gpxfilter(XMLFilterBase):
"""A filter which mostly passes through the XML, but munges the route point names according
to the comment map.
"""
def __init__(self, In, Out, turn_map):
XMLFilterBase.__init__(self, In)
self._turn_map = turn_map
self.element_stack = []
self.Out = Out
self.last_waypoint = None
self.in_rtept = False
self.in_name = False
self.in_cmt = False
def startElement(self, name, attrs):
if name == "rtept":
self.in_rtept = True
elif name == "name":
self.in_name = True
elif name == "cmt":
self.in_cmt = True
return
self.Out.characters("\n" + " " * len(self.element_stack))
self.Out.startElement(name, attrs)
if attrs.has_key('lat'):
self.element_stack.append([name, attrs['lat'], attrs['lon']])
else:
self.element_stack.append([name, None, None])
def endElement(self, name):
if name == "rtept":
self.in_rtept = False
elif name == "name":
self.in_name = False
elif name == "cmt":
self.in_cmt = False
return
if name != self.element_stack[-1][NAME]:
print "ERROR expected " + \
str(self.element_stack[-1])[0] + " got " + str(name)
self.element_stack.pop(-1)
self.Out.endElement(name)
self.Out.characters("\n" + " "*(len(self.element_stack) - 1))
def characters(self, content):
if self.in_cmt:
return
if not self.in_name or not self.in_rtept:
self.Out.characters(content.strip())
return
# Let it blow up on key error
name = self._turn_map["%s,%s" % (
self.element_stack[-2][LAT], self.element_stack[-2][LON])]
self.last_waypoint = name
basename = name
match_index = -1
# First try original name
# If name is unique, add it to cache and break
# Else if coords are close, break (use dupe name)
# Else loop (try new name)
found = False
for i in range(0, 9999):
if i > 0:
name = basename[0:6 - len(str(i))] + str(i)
try:
match_index = [n[NAME] for n in waypoints].index(name)
except: # didn't find a match
if self.element_stack[-2][LAT] is not None:
waypoints.append([name,
self.element_stack[-2][LAT],
self.element_stack[-2][LON]])
found = True
break
diff_lat = abs(
float(self.element_stack[-2][LAT]) - float(waypoints[match_index][LAT]))
diff_lon = abs(
float(self.element_stack[-2][LON]) - float(waypoints[match_index][LON]))
if diff_lat <= .0003 and diff_lon <= .0003:
found = True
break
if not found:
print "ran out of tries??? (should never happen)"
self.Out.characters(name)
if __name__ == "__main__":
if len(sys.argv) < 3:
print "need input file and output folder"
print "waypoints are cached in ~/.gpswaypoints"
sys.exit(1)
try:
# #try to load waypoint cache
f = open(os.getenv('HOME')+"/.gpswaypoints", "r")
contrib, waypoints = pickle.load(f)
except:
contrib = []
waypoints = []
in_name = sys.argv[1]
out_name = os.path.basename(in_name)
ext_place = out_name.rfind('.')
out_name = os.path.join(
sys.argv[2], out_name[0:ext_place]+"-recode"+out_name[ext_place:])
try:
print "output to", out_name
out = open(out_name, "w")
except Exception, e:
print "couldn't open output file", out_name
print str(e)
sys.exit(1)
In = xml.sax.make_parser()
turn_mapper = TurnFilter(In)
turn_mapper.parse(sys.argv[1])
Out = XMLGenerator(out)
filter_handler = gpxfilter(In, Out, turn_mapper.TurnMap)
try:
filter_handler.parse(sys.argv[1])
except IOError, e:
print "couldn't open input file"
sys.exit(1)
f = open(os.getenv('HOME')+"/.gpswaypoints", "w")
# save the cache
if os.path.split(sys.argv[1])[1] not in contrib:
contrib = contrib+[os.path.split(sys.argv[1])[1]]
pickle.dump([contrib, waypoints], f)
out.close()