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

Source Code for Module sabx10.oxm.segment

  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  Segment handling. 
 24  """ 
 25  from geom import Point, Line, Box 
 26  from utils import get_from_id, check_version 
 27   
 28  ############################################################ 
 29  # XML 
 30  ############################################################ 
 31   
32 -class Waypoint(Point):
33 """ 34 A C{Waypoint} object sub-classes L{Point} and adds stop and POI references. 35 36 @ivar stop: space-delimited list of stop id's for this waypoint 37 @type stop: C{string} 38 @ivar poi: space-delimited list of poi id's for this waypoint 39 @type poi: C{string} 40 """
41 - def __init__(self, id, stop, poi, lat, lon, ele, usgs):
42 """ 43 Save the passed-in data. 44 45 @param id: id of the C{Waypoint} 46 @type id: C{string} 47 @param stop: space-delimited list of stop id's for this waypoint 48 @type stop: C{string} 49 @param poi: space-delimited list of poi id's for this waypoint 50 @type poi: C{string} 51 @param lat: latitude of the waypoint 52 @type lat: C{float} 53 @param lon: longitude of the waypoint 54 @type lon: C{float} 55 @param ele: observed elevation for this waypoint (meters above 56 sea level) 57 @type ele: C{float} 58 @param usgs: USGS elevation for this waypoint (meters above sea level) 59 @type usgs: C{float} 60 """ 61 Point.__init__(self, lat, lon, ele, usgs, id) 62 self.stop = stop 63 self.poi = poi
64
65 - def dup_point_data(self):
66 """ 67 Create a new C{Waypoint} with the same data as this one. 68 69 @return: duplicate C{Waypoint} 70 @rtype: C{Waypoint} 71 """ 72 return Waypoint(self.id, None, None, 73 self.lat, self.lon, self.ele, self.usgs)
74
75 -class Segment(Line):
76 """ 77 A C{Segment} object sub-classes L{Line} and adds a host of extra data. 78 79 @ivar id: id of the C{Segment} 80 @type id: C{string} 81 @ivar road: name of road for this C{Segment} 82 @type road: C{string} 83 @ivar fromto: SEGMENT_X to SEGMENT_Y 84 @type fromto: C{string} 85 @ivar comments: Extra information about this C{Segment} 86 @type comments: C{string} 87 @ivar lanes: number of lanes 88 @type lanes: C{string} 89 @ivar shoulder: width of shoulder 90 @type shoulder: C{string} 91 @ivar traffic: amount of traffic 92 @type traffic: C{string} 93 @ivar speed: speed limit 94 @type speed: C{string} 95 """
96 - def __init__(self, id, road, fromto, comments, 97 lanes, shoulder, traffic, speed, waypoints):
98 """ 99 Save the passed-in data. 100 101 @param id: id of the C{Segment} 102 @type id: C{string} 103 @param road: name of road for this C{Segment} 104 @type road: C{string} 105 @param fromto: SEGMENT_X to SEGMENT_Y 106 @type fromto: C{string} 107 @param comments: Extra information about this C{Segment} 108 @type comments: C{string} 109 @param lanes: number of lanes 110 @type lanes: C{string} 111 @param shoulder: width of shoulder 112 @type shoulder: C{string} 113 @param traffic: amount of traffic 114 @type traffic: C{string} 115 @param speed: speed limit 116 @type speed: C{string} 117 """ 118 Line.__init__(self, waypoints) 119 self.id = id 120 self.road = road 121 self.fromto = fromto 122 self.comments = comments 123 self.lanes = lanes 124 self.shoulder = shoulder 125 self.traffic = traffic 126 self.speed = speed
127
128 -def _parse_waypoint(xml_point):
129 """ 130 Take the C{Element} for a waypoint and turn it into a L{Waypoint} object. 131 132 @param xml_point: C{Element} for a waypoint 133 @type xml_point: C{Element} 134 135 @return: L{Waypoint} object 136 @rtype: L{Waypoint} 137 """ 138 stop = None 139 if 'stop' in xml_point.attrib: 140 stop = xml_point.attrib['stop'] 141 poi = None 142 if 'poi' in xml_point.attrib: 143 poi = xml_point.attrib['poi'] 144 return Waypoint(id = xml_point.attrib['id'], 145 stop = stop, 146 poi = poi, 147 lat = xml_point.findtext('lat'), 148 lon = xml_point.findtext('lon'), 149 ele = xml_point.findtext('ele'), 150 usgs = xml_point.findtext('usgs'))
151
152 -def _parse_segment_waypoints(xml_seg):
153 """ 154 Take the C{Element} for a segment and parse all the waypoints it contains. 155 156 @param xml_seg: C{Element} for a segment 157 @type xml_seg: C{Element} 158 159 @return: C{list} of L{Waypoint} objects 160 @rtype: C{list} of L{Waypoint} 161 """ 162 return [_parse_waypoint(xml_pt) 163 for xml_pt in xml_seg.findall('waypoint')]
164
165 -def _parse_segment_xml(xml_seg):
166 """ 167 Take the C{Element} for a segment and turn it into a L{Segment} object. 168 169 @param xml_seg: C{Element} for a segment 170 @type xml_seg: C{Element} 171 172 @return: L{Segment} object 173 @rtype: L{Segment} 174 """ 175 return Segment(id = xml_seg.attrib['id'], 176 road = xml_seg.findtext('road'), 177 fromto = xml_seg.findtext('fromto'), 178 comments = xml_seg.findtext('comments'), 179 lanes = xml_seg.findtext('lanes'), 180 shoulder = xml_seg.findtext('shoulder'), 181 traffic = xml_seg.findtext('traffic'), 182 speed = xml_seg.findtext('speed'), 183 waypoints = _parse_segment_waypoints(xml_seg))
184
185 -def parse_segments(xml_tree):
186 """ 187 Get all the segment elements in the given C{Element} tree and create a list 188 of them with L{Segment} objects. 189 190 @param xml_tree: root of C{Element} tree that has segments in it 191 @type xml_tree: C{Element} or C{ElementTree} 192 193 @return: segments in a list and a dictionary 194 @rtype: (C{list} of L{Segment},C{dict} of L{Segment}) 195 """ 196 xml_segs = xml_tree.findall('segment') 197 seg_list = [] 198 seg_dict = {} 199 for xml_seg in xml_segs: 200 new_seg = _parse_segment_xml(xml_seg) 201 seg_list.append(new_seg) 202 seg_dict[new_seg.id] = new_seg 203 204 return seg_list, seg_dict
205 206 ############################################################ 207 # RIDE 208 ############################################################ 209
210 -def _get_ride_segs(xml_ride, xml_segs):
211 """ 212 Process the ride, creating L{Segment} objects for each of its segment 213 references. 214 215 @param xml_ride: C{Element} for a ride 216 @type xml_ride: C{Element} 217 @param xml_segs: C{list} of C{Element} segment objects for this rideset 218 @type xml_segs: C{list} of C{Element}s 219 220 @return: C{list} of L{Segment} objects 221 @rtype: C{list} of L{Segment} 222 """ 223 return [_parse_segment_xml(get_from_id(xml_segs, seg.attrib['id'])) 224 for seg in xml_ride.findall('segment_ref')]
225
226 -def _correct_points(points, correction):
227 """ 228 Apply an elevation correction to all the points in a list of waypoints. 229 230 @param points: list of points to correct 231 @type points: C{list} of L{Waypoint} 232 @param correction: ammount to add to the L{Waypoint} elevations 233 @type correction: C{float} 234 235 @return: C{None} 236 @rtype: C{None} 237 """ 238 for pt in points: 239 pt.ele += correction
240
241 -def _correct_elevations(ride_segs):
242 """ 243 Process the ride segments, making sure that the rise/fall between segment 244 C{ele} endpoints is the same as the rise/fall of the USGS data. This helps 245 things out because C{ele} data taken from different rides can be off from 246 eachother, though the relative data for the ride is correct. This can make 247 for sudden jumps in the elevation between segments, which makes for funny 248 looking graphs and elevation analysis. 249 250 @param ride_segs: C{list} of L{Segment} objects for the ride 251 @type ride_segs: C{list} of L{Segment} 252 253 @return: C{None} 254 @rtype: C{None} 255 """ 256 correction = 0.0 257 for index in range(len(ride_segs)-1): 258 usgs_rise = ride_segs[index+1].waypoints[0].usgs - \ 259 ride_segs[index].waypoints[-1].usgs 260 ele_rise = ride_segs[index+1].waypoints[0].ele - \ 261 ride_segs[index].waypoints[-1].ele 262 _correct_points(ride_segs[index].waypoints, correction) 263 correction += usgs_rise - ele_rise 264 _correct_points(ride_segs[-1].waypoints, correction)
265
266 -def _connect_segments_to_following_segment(ride_segs):
267 """ 268 Process the ride segments, connecting each to the successive segment by 269 making the final point of each segment be the same as the first point of 270 the following segment. 271 272 @param ride_segs: C{list} of L{Segment} objects for the ride 273 @type ride_segs: C{list} of L{Segment} 274 275 @return: C{None} 276 @rtype: C{None} 277 """ 278 for index in range(len(ride_segs)-1): 279 ride_segs[index].waypoints.append( 280 ride_segs[index+1].waypoints[0].dup_point_data())
281
282 -def _process_segment(seg, index, cum_distance):
283 """ 284 Process a L{Segment}, adding pertinent calculated data. 285 286 Calculated data includes: 287 - length: of segment 288 - distance: in ride of start of segment 289 - start_dist: same as distance 290 - end_dist: distance in ride of end of segment 291 - mid_index: index of middle L{Waypoint} 292 - lat: latitude of starting L{Waypoint} 293 - lon: longitude of starting L{Waypoint} 294 - mid_lat: latitude of middle L{Waypoint} 295 - mid_lon: longitude of middle L{Waypoint} 296 - index: index of segment in ride 297 - bounds: bounding box for all L{Waypoint}s in the box 298 299 @param seg: L{Segment} to process 300 @type seg: L{Segment} 301 @param index: index for this segment 302 @type index: C{int} 303 @param cum_distance: distance so far for this ride 304 @type cum_distance: C{float} 305 306 @return: C{None} 307 @rtype: C{None} 308 """ 309 seg.length = seg.calc_length() 310 seg.distance = cum_distance 311 seg.start_dist = cum_distance 312 seg.end_dist = cum_distance + seg.length 313 seg.comments = " ".join(seg.comments.split()) 314 315 mid_index = (len(seg.waypoints) - 1) / 2 316 seg.lat = seg.waypoints[0].lat 317 seg.lon = seg.waypoints[0].lon 318 seg.mid_lat = seg.waypoints[mid_index].lat 319 seg.mid_lon = seg.waypoints[mid_index].lon 320 321 seg.index = index 322 323 seg.bounds = Box() 324 for pt in seg.waypoints: 325 seg.bounds.expand_to_point(pt.lat, pt.lon)
326
327 -def process_ride_segments(xml_ride, xml_segs, correct_ele=True):
328 """ 329 Process the segment references for the given ride and generate a list of 330 L{Segment} objects for it and the bounding box for all of the segments. 331 332 @param xml_ride: C{Element} for a ride 333 @type xml_ride: C{Element} 334 @param xml_segs: C{list} of C{Element} segment objects for this rideset 335 @type xml_segs: C{list} of C{Element}s 336 @param correct_ele: make sure that the segment elevations match-up? 337 @type correct_ele: C{boolean} 338 339 @return: (C{list} of L{Segment},C{bounds}) 340 @rtype: (C{list},L{Box}) 341 """ 342 """ Process the segments in the ride. """ 343 cum_distance = 0.0 344 bounds = Box() 345 346 ride_segs = _get_ride_segs(xml_ride, xml_segs) 347 if correct_ele: 348 _correct_elevations(ride_segs) 349 _connect_segments_to_following_segment(ride_segs) 350 for index, seg in enumerate(ride_segs): 351 _process_segment(seg, index, cum_distance) 352 cum_distance += seg.length 353 bounds.expand_to_box(seg.bounds) 354 355 return ride_segs, bounds
356