Package sabx10 :: Package oxm :: Module ride
[hide private]
[frames] | no frames]

Source Code for Module sabx10.oxm.ride

  1  ############################################################################### 
  2  # 
  3  # sabx10.oxm - an SABX file manipulation library 
  4  # Copyright (C) 2009, 2010 Jay Farrimond (jay@sabikerides.com) 
  5  # 
  6  # This file is part of sabx10.oxm. 
  7  # 
  8  # sabx10.oxm is free software: you can redistribute it and/or modify it under 
  9  # the terms of the GNU Lesser General Public License as published by the Free 
 10  # Software Foundation, either version 3 of the License, or (at your option) any 
 11  # later version. 
 12  # 
 13  # sabx10.oxm is distributed in the hope that it will be useful, but WITHOUT ANY 
 14  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 15  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 16  # details. 
 17  # 
 18  # You should have received a copy of the GNU Lesser General Public License 
 19  # along with sabx10.oxm.  If not, see <http://www.gnu.org/licenses/>. 
 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  # XML 
 61  ############################################################ 
 62   
63 -class Ride(object):
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
105 -def _parse_ride_parking(xml_ride):
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
118 -def _parse_ride_turns(xml_ride):
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
130 -def _parse_ride_segments(xml_ride):
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
142 -def _parse_ride_xml(xml_ride):
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
160 -def parse_rides(xml_tree):
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 # RIDES 183 ############################################################ 184
185 -def _create_combined_list(ride):
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
204 -def _process_bounds(ride, seg_bounds, stop_bounds, poi_bounds):
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
279 -def process_rides(xml_tree, correct_ele=True):
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