Package sabx10 :: Package utils :: Module sabx_csv
[hide private]
[frames] | no frames]

Source Code for Module sabx10.utils.sabx_csv

  1  ############################################################################### 
  2  # 
  3  # sabx10 - an SABX file manipulation library 
  4  # Copyright (C) 2009, 2010 Jay Farrimond (jay@sabikerides.com) 
  5  # 
  6  # This file is part of sabx10. 
  7  # 
  8  # sabx10 is free software: you can redistribute it and/or modify it under the 
  9  # terms of the GNU General Public License as published by the Free Software 
 10  # Foundation, either version 3 of the License, or (at your option) any later 
 11  # version. 
 12  # 
 13  # sabx10 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 General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License along with 
 18  # sabx10.  If not, see <http://www.gnu.org/licenses/>. 
 19  # 
 20  ############################################################################### 
 21  """ 
 22  Handle merging CSV files into SABX files. 
 23  """ 
 24   
 25  import csv 
 26  import sys 
 27   
 28  from sabx10.oxm import Point, Ride, Parking, Turn, Segment, Stop, Poi 
 29  from sabx10.templating import SabxProcessor 
 30   
31 -class BadPointError(Exception):
32 """ 33 This is raised when the CsvProcessor._process_lat_lon function can't 34 interpret a point passed to it. 35 """ 36 pass
37
38 -class CsvProcessor(SabxProcessor):
39 """ 40 Merge an SABX 1.0 file and a CSV file. Process it through an SABX 1.0 41 template and output it into a SABX 1.0 file. 42 """ 43
44 - def __init__(self, template_file=None, man=None):
45 """ 46 Adds the id, csv, and generate options to the standard SABX 1.0 47 template processor. 48 49 @param template_file: (optional) file name of template file 50 @type template_file: C{string} 51 @param man: (optional) extended program help 52 @type man: C{string} 53 """ 54 SabxProcessor.__init__(self, template_file, man) 55 56 self.parser.usage = "%s csv_file" % self.parser.usage 57 self.parser.add_option("-d", "--id", dest="id", 58 help="id of segment to merge into", 59 default="1", metavar="ID") 60 self.parser.add_option("-b", "--blank", dest="blank", 61 action="store_true", 62 help="generate a blank CSV file", 63 default=False, metavar="BLANK")
64
65 - def _generate_blank(self):
66 csv = open(self.csv, "w") 67 csv.write("TYPE,START POINT,END POINT,TURN COMMENTS,TURN CUE," 68 "ROAD,ROAD COMMENTS,LANES,SHOULDER,SPEED,TRAFFIC\n") 69 csv.close()
70
71 - def process_options(self):
72 """ 73 Get the CSV file name. It's expected to be the last command-line 74 argument. Print a blank CSV file and exit if asked to. 75 """ 76 SabxProcessor.process_options(self, 1) 77 self.csv = self.args[-1] 78 79 if self.options.blank: 80 self._generate_blank() 81 sys.exit(0)
82
83 - def _process_lat_lon(self, lat, lon):
84 """ 85 Get the lat, lon, and waypoint corresponding to the lat and lon values 86 passed in. If lat contains a point id, then the corresponding point is 87 found. Otherwise, the point closest to (lat, lon) is found. 88 89 @raises: L{BadPointError} 90 91 @param lat: latitude or point to search for 92 @type lat: C{string} 93 @param lon: longitude to search for 94 @type lon: C{string} 95 """ 96 waypoint = None 97 if lat.find('.') == -1: 98 for pt in self.template_data['seg_dict'][self.options.id].waypoints: 99 if pt.id == lat: 100 waypoint = pt 101 lat = pt.lat 102 lon = pt.lon 103 break 104 else: 105 raise BadPointError( 106 "Matching point for '%s' not found!" % lat) 107 else: 108 dist = 100000.0 109 check_pt = Point(lat, lon) 110 for pt in self.template_data['seg_dict'][self.options.id].waypoints: 111 t_dist = check_pt.calculate_distance(pt) 112 if t_dist < dist: 113 waypoint = pt 114 dist = t_dist 115 116 return lat, lon, waypoint
117
118 - def _parse_parking(self, id, item):
119 """ 120 Parse the CSV line as if it's a PARK item. 121 122 @raises: L{BadPointError} 123 124 @param id: id for this item 125 @type id: C{int} 126 @param item: CSV line item to parse 127 @type item: C{dict} 128 """ 129 try: 130 lat, lon, wypt = self._process_lat_lon(item['START POINT'], 131 item['END POINT']) 132 except BadPointError as bpe: 133 raise BadPointError("Bad parking point: %s" % bpe) 134 135 return Parking(str(id), item['ROAD'], lat, lon)
136
137 - def _parse_turn(self, id, fromto, seg):
138 """ 139 Parse the CSV line as if it's a ROAD item, and remove the turn data 140 from it. 141 142 @param id: id for this item 143 @type id: C{int} 144 @param fromto: fromto part of the turn 145 @type fromto: C{string} 146 @param seg: CSV ROAD item to parse 147 @type seg: C{dict} 148 """ 149 return Turn(str(id), "%s to %s" % (fromto, seg['ROAD']), 150 "%s %s" % (seg['TURN CUE'], seg['ROAD']), 151 seg['TURN COMMENTS'])
152
153 - def _get_segment_waypoints(self, seg):
154 """ 155 Get the waypoints for the corresponding segment. These are the points 156 from START POINT to END POINT. 157 158 @param seg: segment to get points for 159 @type seg: SABX 1.0 L{Segment} 160 """ 161 seg_pts = [] 162 in_pts = False 163 for pt in self.template_data['seg_dict'][self.options.id].waypoints: 164 if pt.id == seg['START POINT']: 165 in_pts = True 166 167 if in_pts: 168 seg_pts.append(pt) 169 170 if pt.id == seg['END POINT']: 171 in_pts = False 172 173 return seg_pts
174
175 - def _parse_segment(self, id, fromto, seg):
176 """ 177 Parse the CSV line as if it's a ROAD item, and remove the segment data 178 from it. 179 180 @param id: id for this item 181 @type id: C{int} 182 @param fromto: fromto part of the turn 183 @type fromto: C{string} 184 @param seg: CSV ROAD item to parse 185 @type seg: C{dict} 186 """ 187 return Segment(str(id), seg['ROAD'], fromto, seg['ROAD COMMENTS'], 188 seg['LANES'], seg['SHOULDER'], seg['TRAFFIC'], 189 seg['SPEED'], self._get_segment_waypoints(seg))
190
191 - def _update_segment_fromto(self, seg, tofrom):
192 """ 193 Update the fromto field in a segment. 194 195 @param seg: segment to update 196 @type seg: SABX 1.0 L{Segment} 197 @param tofrom: value to update with 198 @type tofrom: C{string} 199 """ 200 seg.fromto = "%s to %s" % (seg.fromto, tofrom)
201
202 - def _parse_stop(self, id, item):
203 """ 204 Parse the CSV line as if it's a STOP item. 205 206 @raises: L{BadPointError} 207 208 @param id: id for this item 209 @type id: C{int} 210 @param item: CSV line item to parse 211 @type item: C{dict} 212 """ 213 try: 214 lat, lon, wypt = self._process_lat_lon(item['START POINT'], 215 item['END POINT']) 216 except BadPointError as bpe: 217 raise BadPointError("Bad stop point: %s" % bpe) 218 219 if wypt.stop is None: 220 wypt.stop = str(id) 221 else: 222 wypt.stop = "%s,%s" % (wypt.stop, str(id)) 223 return Stop(str(id), item['TURN CUE'], item['ROAD'], lat, lon)
224
225 - def _parse_poi(self, id, item):
226 """ 227 Parse the CSV line as if it's a POI item. 228 229 @raises: L{BadPointError} 230 231 @param id: id for this item 232 @type id: C{int} 233 @param item: CSV line item to parse 234 @type item: C{dict} 235 """ 236 try: 237 lat, lon, wypt = self._process_lat_lon(item['START POINT'], 238 item['END POINT']) 239 except BadPointError as bpe: 240 raise BadPointError("Bad poi point: %s" % bpe) 241 242 if wypt.poi is None: 243 wypt.poi = str(id) 244 else: 245 wypt.poi = "%s,%s" % (wypt.poi, str(id)) 246 return Poi(str(id), item['ROAD'], lat, lon)
247
248 - def _parse_csv(self):
249 """ 250 Parse the CSV file and merge it with the SABX 1.0 template data. 251 """ 252 new_sabx = {} 253 new_sabx['park_list'] = [] 254 new_sabx['park_dict'] = {} 255 256 new_sabx['turn_list'] = [] 257 new_sabx['turn_dict'] = {} 258 259 new_sabx['seg_list'] = [] 260 new_sabx['seg_dict'] = {} 261 262 new_sabx['stop_list'] = [] 263 new_sabx['stop_dict'] = {} 264 265 new_sabx['poi_list'] = [] 266 new_sabx['poi_dict'] = {} 267 268 new_ride = Ride('1', '', '', [], []) 269 new_sabx['ride_list'] = [new_ride] 270 new_sabx['ride_dict'] = {'1': new_ride} 271 272 fromto = "parking" 273 prev_seg = None 274 275 # TYPE, START POINT, END POINT, TURN COMMENTS, TURN CUE, 276 # ROAD, ROAD COMMENTS, LANES, SHOULDER, SPEED, TRAFFIC 277 for index, row in enumerate( 278 csv.DictReader(open(self.csv)), start=1): 279 280 # ride 281 if row['TYPE'] == 'RIDE': 282 new_ride.description = row['ROAD'] 283 284 # parking 285 if row['TYPE'] == 'PARK': 286 new_parking = self._parse_parking(index, row) 287 new_sabx['park_list'].append(new_parking) 288 new_sabx['park_dict'][new_parking.id] = new_parking 289 new_ride.parking = new_parking.id 290 291 # road 292 elif row['TYPE'] == 'ROAD': 293 new_turn = self._parse_turn(index, fromto, row) 294 new_sabx['turn_list'].append(new_turn) 295 new_sabx['turn_dict'][new_turn.id] = new_turn 296 new_ride.turns.append(new_turn.id) 297 298 new_seg = self._parse_segment(index, fromto, row) 299 new_sabx['seg_list'].append(new_seg) 300 new_sabx['seg_dict'][new_seg.id] = new_seg 301 new_ride.segs.append(new_seg.id) 302 303 fromto = new_seg.road 304 if prev_seg: 305 self._update_segment_fromto(prev_seg, fromto) 306 prev_seg = new_seg 307 308 # stop 309 elif row['TYPE'] == 'STOP': 310 new_stop = self._parse_stop(index, row) 311 new_sabx['stop_list'].append(new_stop) 312 new_sabx['stop_dict'][new_stop.id] = new_stop 313 314 # poi 315 elif row['TYPE'] == 'POI': 316 new_poi = self._parse_poi(index, row) 317 new_sabx['poi_list'].append(new_poi) 318 new_sabx['poi_dict'][new_poi.id] = new_poi 319 320 return new_sabx
321
322 - def get_template_data(self):
323 """ 324 Update the template data by merging it with the specified CSV data. 325 """ 326 SabxProcessor.get_template_data(self) 327 new_sabx = self._parse_csv() 328 self.template_data['park_list'], self.template_data['park_dict'] = \ 329 new_sabx['park_list'], new_sabx['park_dict'] 330 self.template_data['turn_list'], self.template_data['turn_dict'] = \ 331 new_sabx['turn_list'], new_sabx['turn_dict'] 332 self.template_data['seg_list'], self.template_data['seg_dict'] = \ 333 new_sabx['seg_list'], new_sabx['seg_dict'] 334 self.template_data['stop_list'], self.template_data['stop_dict'] = \ 335 new_sabx['stop_list'], new_sabx['stop_dict'] 336 self.template_data['poi_list'], self.template_data['poi_dict'] = \ 337 new_sabx['poi_list'], new_sabx['poi_dict'] 338 self.template_data['ride_list'], self.template_data['ride_dict'] = \ 339 new_sabx['ride_list'], new_sabx['ride_dict']
340