1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Ride handling.
24
25 When I'm using SABX data to create maps, I like to process the XML data into a
26 more useful object hierarchy. I de-reference the parking, turn, and seg ids
27 for each ride and create full object hierarchies for each. This results in
28 lots of duplication, but gives data that is easy to manipulate and reference.
29 I also do some analysis of the rides and add things like distances and bounding
30 boxes to the relevant objects, and modify some of the standard objects to be
31 more programmer-friendly.
32
33 The entry point for this is the L{process_rides} function. It orchestrates all
34 the processing and calculating and returns a ride list that looks like this:
35
36 - C{list} of L{Ride}
37 - index
38 - parking: L{Parking}
39 - turns: C{list} of L{Turn}
40 - segs: C{list} of L{Segment}
41 - C{list} of L{Waypoint}
42 - stops: C{list} of L{Stop}
43 - pois: C{list} of L{Poi}
44 - combined: distance sorted C{list} of all turns, segments, stops, and pois
45 - bounds: C{Box}
46 - distance (length of ride)
47
48 Make note of the fact that all C{comment} and C{description} fields are
49 stripped of extra spaces and linefeeds during processing.
50 """
51 from geom import Box
52 from utils import check_version
53 from parking import process_ride_parking
54 from segment import process_ride_segments
55 from stop import process_ride_stops
56 from poi import process_ride_pois
57 from turn import process_ride_turns
58
59
60
61
62
64 """
65 A C{Ride} object describes a ride and keeps track of all relevant ride
66 information. This information can vary depending on what created the ride.
67 If it was created by parsing XML, then parking, turn list, and segment list
68 are the id's of the relevant items. If this was created while processing a
69 ride, then parking, turn list, and segment list are L{Parking}, L{Turn},
70 and L{Segment} objects instead. The contents can therefore be infered from
71 context.
72
73 @ivar id: id of ride
74 @type id: C{string}
75 @ivar description: description of ride
76 @type description: C{string}
77 @ivar parking: C{id} of parking place for ride or L{Parking} object
78 @type parking: C{string} or L{Parking}
79 @ivar turns: C{list} of turn C{id}s or L{Turn} objects
80 @type turns: C{list} of C{string} or L{Turn}
81 @ivar segments: C{list} of segment C{id}s or L{Segment} objects
82 @type segments: C{list} of C{string} or L{Segment}
83 """
84 - def __init__(self, id, description, parking, turns, segs):
85 """
86 Save the passed-in information.
87
88 @param id: id of ride
89 @type id: C{string}
90 @param description: description of ride
91 @type description: C{string}
92 @param parking: C{id} of parking place for ride or L{Parking} object
93 @type parking: C{string} or L{Parking}
94 @param turns: C{list} of turn C{id}s or L{Turn} objects
95 @type turns: C{list} of C{string} or L{Turn}
96 @param segs: C{list} of segment C{id}s or L{Segment} objects
97 @type segs: C{list} of C{string} or L{Segment}
98 """
99 self.id = id
100 self.description = description
101 self.parking = parking
102 self.turns = turns
103 self.segs = segs
104
106 """
107 Take the C{Element} for a ride and extract the parking id for the ride.
108
109 @param xml_ride: C{Element} for a ride
110 @type xml_ride: C{Element}
111
112 @return: parking id
113 @rtype: C{string}
114 """
115 xml_parking = xml_ride.find('parking_ref')
116 return xml_parking.attrib['id']
117
119 """
120 Take the C{Element} for a ride and extract the turn id's for the ride.
121
122 @param xml_ride: C{Element} for a ride
123 @type xml_ride: C{Element}
124
125 @return: C{list} of turn id's
126 @rtype: C{list} of C{string}
127 """
128 return [xml_turn.attrib['id'] for xml_turn in xml_ride.findall('turn_ref')]
129
131 """
132 Take the C{Element} for a ride and extract the segment id's for the ride.
133
134 @param xml_ride: C{Element} for a ride
135 @type xml_ride: C{Element}
136
137 @return: C{list} of segment id's
138 @rtype: C{list} of C{string}
139 """
140 return [xml_seg.attrib['id'] for xml_seg in xml_ride.findall('segment_ref')]
141
143 """
144 Take the C{Element} for a ride and turn it into a L{Ride} object. This
145 L{Ride} object will be populated with lists of C{string} id's for the
146 parking, turns, and segs of the ride.
147
148 @param xml_ride: C{Element} for a ride
149 @type xml_ride: C{Element}
150
151 @return: L{Ride} object
152 @rtype: L{Ride}
153 """
154 return Ride(id = xml_ride.attrib['id'],
155 description = xml_ride.findtext('description'),
156 parking = _parse_ride_parking(xml_ride),
157 turns = _parse_ride_turns(xml_ride),
158 segs = _parse_ride_segments(xml_ride))
159
161 """
162 Parse the rides in the given XML tree and turn them into valid L{Ride}
163 objects.
164
165 @param xml_tree: root of C{Element} tree that has rides in it
166 @type xml_tree: C{Element} or C{ElementTree}
167
168 @return: rides in a list and a dictionary
169 @rtype: (C{list} of L{Ride},C{dict} of L{Ride})
170 """
171 xml_rides = xml_tree.findall('ride')
172 ride_list = []
173 ride_dict = {}
174 for xml_ride in xml_rides:
175 new_ride = _parse_ride_xml(xml_ride)
176 ride_list.append(new_ride)
177 ride_dict[new_ride.id] = new_ride
178
179 return ride_list, ride_dict
180
181
182
183
184
186 """
187 Create a list containing all of the turns, segments, stops, and pois for
188 the ride, ordered by distance into ride.
189
190 @param ride: C{Ride} to process
191 @type ride: C{Ride}
192
193 @return: combined C{list}
194 @rtype: C{list}
195 """
196 combined = []
197 combined.extend(ride.turns)
198 combined.extend(ride.segs)
199 combined.extend(ride.stops)
200 combined.extend(ride.pois)
201 combined.sort(key=lambda x: x.distance)
202 return combined
203
205 """
206 Creating a bounding box that includes the bounds for the parking, segments,
207 stops, and pois for this ride.
208
209 @param ride: C{Ride} to process
210 @type ride: C{Ride}
211 @param seg_bounds: bounding C{Box} for segments
212 @type seg_bounds: C{Box}
213 @param stop_bounds: bounding C{Box} for stops
214 @type stop_bounds: C{Box}
215 @param poi_bounds: bounding C{Box} for pois
216 @type poi_bounds: C{Box}
217
218 @return: bounding box containing everything
219 @rtype: L{Box}
220 """
221 bounds = Box()
222 bounds.expand_to_point(ride.parking.lat, ride.parking.lon)
223 bounds.expand_to_box(seg_bounds)
224 bounds.expand_to_box(stop_bounds)
225 bounds.expand_to_box(poi_bounds)
226 return bounds
227
228 -def _process_ride(ride_index, xml_ride, xml_parking_places, xml_segs,
229 xml_turns, xml_stops, xml_pois, correct_ele=True):
230 """
231 Create a L{Ride} object and add pertinent calculated data.
232
233 Calculated data includes:
234 - index: index of ride
235 - stops: C{list} of L{Stop}s for this ride
236 - pois: C{list} of L{Poi}s for this ride
237 - combined: distance sorted list of all turns, segments, stops, and pois
238 - distance: length of entire ride
239 - bounds: bounding box including all parking, segments, stops, and pois
240
241 @param ride_index: index for this ride
242 @type ride_index: C{int}
243 @param xml_ride: C{Element} for a ride
244 @type xml_ride: C{Element}
245 @param xml_parking_places: C{list} of parking place C{Element}s
246 @type xml_parking_places: C{list} of C{Element}
247 @param xml_segs: C{list} of segment C{Element}s
248 @type xml_segs: C{list} of C{Element}
249 @param xml_turns: C{list} of turn C{Element}s
250 @type xml_turns: C{list} of C{Element}
251 @param xml_stops: C{list} of stop C{Element}s
252 @type xml_stops: C{list} of C{Element}
253 @param xml_pois: C{list} of poi C{Element}s
254 @type xml_pois: C{list} of C{Element}
255 @param correct_ele: make sure that the segment elevations match-up?
256 @type correct_ele: C{boolean}
257
258 @return: L{Ride}
259 @rtype: L{Ride}
260 """
261 parking = process_ride_parking(xml_ride, xml_parking_places)
262 segs, seg_bounds = process_ride_segments(xml_ride, xml_segs, correct_ele)
263 stops, stop_bounds = process_ride_stops(segs, xml_segs, xml_stops)
264 pois, poi_bounds = process_ride_pois(segs, xml_segs, xml_pois)
265 turns = process_ride_turns(segs, xml_ride, xml_turns)
266
267 new_ride = Ride(ride_index, xml_ride.findtext('description'),
268 parking, turns, segs)
269 new_ride.index = ride_index
270 new_ride.stops = stops
271 new_ride.pois = pois
272 new_ride.combined = _create_combined_list(new_ride)
273 new_ride.distance = new_ride.segs[-1].end_dist
274 new_ride.bounds = _process_bounds(new_ride, seg_bounds,
275 stop_bounds, poi_bounds)
276
277 return new_ride
278
280 """
281 Process the rides in the XML tree and generate a list of L{Ride} objects
282 and the bounding box for all of the rides.
283
284 @param xml_tree: root of C{Element} tree that has rides in it
285 @type xml_tree: C{Element} or C{ElementTree}
286 @param correct_ele: make sure that the segment elevations match-up?
287 @type correct_ele: C{boolean}
288
289 @return: (C{list} of L{Ride}s,C{bounds} of ride)
290 @rtype: (C{list} of L{Ride},L{Box})
291 """
292 check_version(xml_tree)
293 xml_rides = xml_tree.findall('ride')
294 xml_parking_places = xml_tree.findall('parking')
295 xml_segs = xml_tree.findall('segment')
296 xml_turns = xml_tree.findall('turn')
297 xml_stops = xml_tree.findall('stop')
298 xml_pois = xml_tree.findall('poi')
299
300 bounds = Box()
301 ride_list = []
302 for ride_index, xml_ride in enumerate(xml_rides):
303 new_ride = _process_ride(ride_index, xml_ride, xml_parking_places,
304 xml_segs, xml_turns, xml_stops, xml_pois,
305 correct_ele)
306 bounds.expand_to_box(new_ride.bounds)
307 ride_list.append(new_ride)
308
309 return ride_list, bounds
310