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 223 224 225 226 227 228 229 230 231 232 233 234 235 | #!/usr/bin/python2.5 # Copyright (C) 2007 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import problems as problems_module from stop import Stop import util class StopTime(object): """ Represents a single stop of a trip. StopTime contains most of the columns from the stop_times.txt file. It does not contain trip_id, which is implied by the Trip used to access it. See the Google Transit Feed Specification for the semantic details. stop: A Stop object arrival_time: str in the form HH:MM:SS; readonly after __init__ departure_time: str in the form HH:MM:SS; readonly after __init__ arrival_secs: int number of seconds since midnight departure_secs: int number of seconds since midnight stop_headsign: str pickup_type: int drop_off_type: int shape_dist_traveled: float stop_id: str; readonly stop_time: The only time given for this stop. If present, it is used for both arrival and departure time. stop_sequence: int """ _REQUIRED_FIELD_NAMES = ['trip_id', 'arrival_time', 'departure_time', 'stop_id', 'stop_sequence'] _OPTIONAL_FIELD_NAMES = ['stop_headsign', 'pickup_type', 'drop_off_type', 'shape_dist_traveled'] _FIELD_NAMES = _REQUIRED_FIELD_NAMES + _OPTIONAL_FIELD_NAMES _SQL_FIELD_NAMES = ['trip_id', 'arrival_secs', 'departure_secs', 'stop_id', 'stop_sequence', 'stop_headsign', 'pickup_type', 'drop_off_type', 'shape_dist_traveled'] _STOP_CLASS = Stop __slots__ = ('arrival_secs', 'departure_secs', 'stop_headsign', 'stop', 'stop_headsign', 'pickup_type', 'drop_off_type', 'shape_dist_traveled', 'stop_sequence') def __init__(self, problems, stop, arrival_time=None, departure_time=None, stop_headsign=None, pickup_type=None, drop_off_type=None, shape_dist_traveled=None, arrival_secs=None, departure_secs=None, stop_time=None, stop_sequence=None): # Implementation note from Andre, July 22, 2010: # The checks performed here should be in their own Validate* methods to # keep consistency. Unfortunately the performance degradation is too great, # so the validation was left in __init__. # Performance is also the reason why we don't use the GtfsFactory, but # have StopTime._STOP_CLASS instead. If a Stop class that does not inherit # from transitfeed.Stop is used, the extension should also provide a # StopTime class that updates _STOP_CLASS accordingly. # # For more details see the discussion at # http://codereview.appspot.com/1713041 if stop_time != None: arrival_time = departure_time = stop_time if arrival_secs != None: self.arrival_secs = arrival_secs elif arrival_time in (None, ""): self.arrival_secs = None # Untimed arrival_time = None else: try: self.arrival_secs = util.TimeToSecondsSinceMidnight(arrival_time) except problems_module.Error: problems.InvalidValue('arrival_time', arrival_time) self.arrival_secs = None if departure_secs != None: self.departure_secs = departure_secs elif departure_time in (None, ""): self.departure_secs = None departure_time = None else: try: self.departure_secs = util.TimeToSecondsSinceMidnight(departure_time) except problems_module.Error: problems.InvalidValue('departure_time', departure_time) self.departure_secs = None if not isinstance(stop, self._STOP_CLASS): # Not quite correct, but better than letting the problem propagate problems.InvalidValue('stop', stop) self.stop = stop self.stop_headsign = stop_headsign if pickup_type in (None, ""): self.pickup_type = None else: try: pickup_type = int(pickup_type) except ValueError: problems.InvalidValue('pickup_type', pickup_type) else: if pickup_type < 0 or pickup_type > 3: problems.InvalidValue('pickup_type', pickup_type) self.pickup_type = pickup_type if drop_off_type in (None, ""): self.drop_off_type = None else: try: drop_off_type = int(drop_off_type) except ValueError: problems.InvalidValue('drop_off_type', drop_off_type) else: if drop_off_type < 0 or drop_off_type > 3: problems.InvalidValue('drop_off_type', drop_off_type) self.drop_off_type = drop_off_type if (self.pickup_type == 1 and self.drop_off_type == 1 and self.arrival_secs == None and self.departure_secs == None): problems.OtherProblem('This stop time has a pickup_type and ' 'drop_off_type of 1, indicating that riders ' 'can\'t get on or off here. Since it doesn\'t ' 'define a timepoint either, this entry serves no ' 'purpose and should be excluded from the trip.', type=problems_module.TYPE_WARNING) if ((self.arrival_secs != None) and (self.departure_secs != None) and (self.departure_secs < self.arrival_secs)): problems.InvalidValue('departure_time', departure_time, 'The departure time at this stop (%s) is before ' 'the arrival time (%s). This is often caused by ' 'problems in the feed exporter\'s time conversion') # If the caller passed a valid arrival time but didn't attempt to pass a # departure time complain if (self.arrival_secs != None and self.departure_secs == None and departure_time == None): # self.departure_secs might be None because departure_time was invalid, # so we need to check both problems.MissingValue('departure_time', 'arrival_time and departure_time should either ' 'both be provided or both be left blank. ' 'It\'s OK to set them both to the same value.') # If the caller passed a valid departure time but didn't attempt to pass a # arrival time complain if (self.departure_secs != None and self.arrival_secs == None and arrival_time == None): problems.MissingValue('arrival_time', 'arrival_time and departure_time should either ' 'both be provided or both be left blank. ' 'It\'s OK to set them both to the same value.') if shape_dist_traveled in (None, ""): self.shape_dist_traveled = None else: try: self.shape_dist_traveled = float(shape_dist_traveled) except ValueError: problems.InvalidValue('shape_dist_traveled', shape_dist_traveled) if stop_sequence is not None: self.stop_sequence = stop_sequence def GetFieldValuesTuple(self, trip_id): """Return a tuple that outputs a row of _FIELD_NAMES to be written to a GTFS file. Arguments: trip_id: The trip_id of the trip to which this StopTime corresponds. It must be provided, as it is not stored in StopTime. """ result = [] for fn in self._FIELD_NAMES: if fn == 'trip_id': result.append(trip_id) else: # Since we'll be writting to an output file, we want empty values to be # outputted as an empty string result.append(getattr(self, fn) or '' ) return tuple(result) def GetSqlValuesTuple(self, trip_id): """Return a tuple that outputs a row of _FIELD_NAMES to be written to a SQLite database. Arguments: trip_id: The trip_id of the trip to which this StopTime corresponds. It must be provided, as it is not stored in StopTime. """ result = [] for fn in self._SQL_FIELD_NAMES: if fn == 'trip_id': result.append(trip_id) else: # Since we'll be writting to SQLite, we want empty values to be # outputted as NULL string (contrary to what happens in # GetFieldValuesTuple) result.append(getattr(self, fn)) return tuple(result) def GetTimeSecs(self): """Return the first of arrival_secs and departure_secs that is not None. If both are None return None.""" if self.arrival_secs != None: return self.arrival_secs elif self.departure_secs != None: return self.departure_secs else: return None def __getattr__(self, name): if name == 'stop_id': return self.stop.stop_id elif name == 'arrival_time': return (self.arrival_secs != None and util.FormatSecondsSinceMidnight(self.arrival_secs) or '') elif name == 'departure_time': return (self.departure_secs != None and util.FormatSecondsSinceMidnight(self.departure_secs) or '') elif name == 'shape_dist_traveled': return '' raise AttributeError(name) |