Add geopo to timing points master
[bus.git] / origin-src / transitfeed-1.2.6 / transitfeed / trip.py
blob:a/origin-src/transitfeed-1.2.6/transitfeed/trip.py -> blob:b/origin-src/transitfeed-1.2.6/transitfeed/trip.py
#!/usr/bin/python2.5 #!/usr/bin/python2.5
   
# Copyright (C) 2007 Google Inc. # Copyright (C) 2007 Google Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
   
import warnings import warnings
   
from gtfsobjectbase import GtfsObjectBase from gtfsobjectbase import GtfsObjectBase
import problems as problems_module import problems as problems_module
import util import util
   
class Trip(GtfsObjectBase): class Trip(GtfsObjectBase):
_REQUIRED_FIELD_NAMES = ['route_id', 'service_id', 'trip_id'] _REQUIRED_FIELD_NAMES = ['route_id', 'service_id', 'trip_id']
_FIELD_NAMES = _REQUIRED_FIELD_NAMES + [ _FIELD_NAMES = _REQUIRED_FIELD_NAMES + [
'trip_headsign', 'direction_id', 'block_id', 'shape_id' 'trip_headsign', 'direction_id', 'block_id', 'shape_id'
] ]
_TABLE_NAME= "trips" _TABLE_NAME= "trips"
   
def __init__(self, headsign=None, service_period=None, def __init__(self, headsign=None, service_period=None,
route=None, trip_id=None, field_dict=None): route=None, trip_id=None, field_dict=None):
self._schedule = None self._schedule = None
self._headways = [] # [(start_time, end_time, headway_secs)] self._headways = [] # [(start_time, end_time, headway_secs)]
if not field_dict: if not field_dict:
field_dict = {} field_dict = {}
if headsign is not None: if headsign is not None:
field_dict['trip_headsign'] = headsign field_dict['trip_headsign'] = headsign
if route: if route:
field_dict['route_id'] = route.route_id field_dict['route_id'] = route.route_id
if trip_id is not None: if trip_id is not None:
field_dict['trip_id'] = trip_id field_dict['trip_id'] = trip_id
if service_period is not None: if service_period is not None:
field_dict['service_id'] = service_period.service_id field_dict['service_id'] = service_period.service_id
# Earlier versions of transitfeed.py assigned self.service_period here # Earlier versions of transitfeed.py assigned self.service_period here
# and allowed the caller to set self.service_id. Schedule.Validate # and allowed the caller to set self.service_id. Schedule.Validate
# checked the service_id attribute if it was assigned and changed it to a # checked the service_id attribute if it was assigned and changed it to a
# service_period attribute. Now only the service_id attribute is used and # service_period attribute. Now only the service_id attribute is used and
# it is validated by Trip.Validate. # it is validated by Trip.Validate.
if service_period is not None: if service_period is not None:
# For backwards compatibility # For backwards compatibility
self.service_id = service_period.service_id self.service_id = service_period.service_id
self.__dict__.update(field_dict) self.__dict__.update(field_dict)
   
def GetFieldValuesTuple(self): def GetFieldValuesTuple(self):
return [getattr(self, fn) or '' for fn in self._FIELD_NAMES] return [getattr(self, fn) or '' for fn in self._FIELD_NAMES]
   
def AddStopTime(self, stop, problems=None, schedule=None, **kwargs): def AddStopTime(self, stop, problems=None, schedule=None, **kwargs):
"""Add a stop to this trip. Stops must be added in the order visited. """Add a stop to this trip. Stops must be added in the order visited.
   
Args: Args:
stop: A Stop object stop: A Stop object
kwargs: remaining keyword args passed to StopTime.__init__ kwargs: remaining keyword args passed to StopTime.__init__
   
Returns: Returns:
None None
""" """
if problems is None: if problems is None:
# TODO: delete this branch when StopTime.__init__ doesn't need a # TODO: delete this branch when StopTime.__init__ doesn't need a
# ProblemReporter # ProblemReporter
problems = problems_module.default_problem_reporter problems = problems_module.default_problem_reporter
stoptime = self.GetGtfsFactory().StopTime( stoptime = self.GetGtfsFactory().StopTime(
problems=problems, stop=stop, **kwargs) problems=problems, stop=stop, **kwargs)
self.AddStopTimeObject(stoptime, schedule) self.AddStopTimeObject(stoptime, schedule)
   
def _AddStopTimeObjectUnordered(self, stoptime, schedule): def _AddStopTimeObjectUnordered(self, stoptime, schedule):
"""Add StopTime object to this trip. """Add StopTime object to this trip.
   
The trip isn't checked for duplicate sequence numbers so it must be The trip isn't checked for duplicate sequence numbers so it must be
validated later.""" validated later."""
stop_time_class = self.GetGtfsFactory().StopTime stop_time_class = self.GetGtfsFactory().StopTime
cursor = schedule._connection.cursor() cursor = schedule._connection.cursor()
insert_query = "INSERT INTO stop_times (%s) VALUES (%s);" % ( insert_query = "INSERT INTO stop_times (%s) VALUES (%s);" % (
','.join(stop_time_class._SQL_FIELD_NAMES), ','.join(stop_time_class._SQL_FIELD_NAMES),
','.join(['?'] * len(stop_time_class._SQL_FIELD_NAMES))) ','.join(['?'] * len(stop_time_class._SQL_FIELD_NAMES)))
cursor = schedule._connection.cursor() cursor = schedule._connection.cursor()
cursor.execute( cursor.execute(
insert_query, stoptime.GetSqlValuesTuple(self.trip_id)) insert_query, stoptime.GetSqlValuesTuple(self.trip_id))
   
def ReplaceStopTimeObject(self, stoptime, schedule=None): def ReplaceStopTimeObject(self, stoptime, schedule=None):
"""Replace a StopTime object from this trip with the given one. """Replace a StopTime object from this trip with the given one.
   
Keys the StopTime object to be replaced by trip_id, stop_sequence Keys the StopTime object to be replaced by trip_id, stop_sequence
and stop_id as 'stoptime', with the object 'stoptime'. and stop_id as 'stoptime', with the object 'stoptime'.
""" """
   
if schedule is None: if schedule is None:
schedule = self._schedule schedule = self._schedule
   
new_secs = stoptime.GetTimeSecs() new_secs = stoptime.GetTimeSecs()
cursor = schedule._connection.cursor() cursor = schedule._connection.cursor()
cursor.execute("DELETE FROM stop_times WHERE trip_id=? and " cursor.execute("DELETE FROM stop_times WHERE trip_id=? and "
"stop_sequence=? and stop_id=?", "stop_sequence=? and stop_id=?",
(self.trip_id, stoptime.stop_sequence, stoptime.stop_id)) (self.trip_id, stoptime.stop_sequence, stoptime.stop_id))
if cursor.rowcount == 0: if cursor.rowcount == 0:
raise problems_module.Error, 'Attempted replacement of StopTime object which does not exist' raise problems_module.Error, 'Attempted replacement of StopTime object which does not exist'
self._AddStopTimeObjectUnordered(stoptime, schedule) self._AddStopTimeObjectUnordered(stoptime, schedule)
   
def AddStopTimeObject(self, stoptime, schedule=None, problems=None): def AddStopTimeObject(self, stoptime, schedule=None, problems=None):
"""Add a StopTime object to the end of this trip. """Add a StopTime object to the end of this trip.
   
Args: Args:
stoptime: A StopTime object. Should not be reused in multiple trips. stoptime: A StopTime object. Should not be reused in multiple trips.
schedule: Schedule object containing this trip which must be schedule: Schedule object containing this trip which must be
passed to Trip.__init__ or here passed to Trip.__init__ or here
problems: ProblemReporter object for validating the StopTime in its new problems: ProblemReporter object for validating the StopTime in its new
home home
   
Returns: Returns:
None None
""" """
if schedule is None: if schedule is None:
schedule = self._schedule schedule = self._schedule
if schedule is None: if schedule is None:
warnings.warn("No longer supported. _schedule attribute is used to get " warnings.warn("No longer supported. _schedule attribute is used to get "
"stop_times table", DeprecationWarning) "stop_times table", DeprecationWarning)
if problems is None: if problems is None:
problems = schedule.problem_reporter problems = schedule.problem_reporter
   
new_secs = stoptime.GetTimeSecs() new_secs = stoptime.GetTimeSecs()
cursor = schedule._connection.cursor() cursor = schedule._connection.cursor()
cursor.execute("SELECT max(stop_sequence), max(arrival_secs), " cursor.execute("SELECT max(stop_sequence), max(arrival_secs), "
"max(departure_secs) FROM stop_times WHERE trip_id=?", "max(departure_secs) FROM stop_times WHERE trip_id=?",
(self.trip_id,)) (self.trip_id,))
row = cursor.fetchone() row = cursor.fetchone()
if row[0] is None: if row[0] is None:
# This is the first stop_time of the trip # This is the first stop_time of the trip
stoptime.stop_sequence = 1 stoptime.stop_sequence = 1
if new_secs == None: if new_secs == None:
problems.OtherProblem( problems.OtherProblem(
'No time for first StopTime of trip_id "%s"' % (self.trip_id,)) 'No time for first StopTime of trip_id "%s"' % (self.trip_id,))
else: else:
stoptime.stop_sequence = row[0] + 1 stoptime.stop_sequence = row[0] + 1
prev_secs = max(row[1], row[2]) prev_secs = max(row[1], row[2])
if new_secs != None and new_secs < prev_secs: if new_secs != None and new_secs < prev_secs:
problems.OtherProblem( problems.OtherProblem(
'out of order stop time for stop_id=%s trip_id=%s %s < %s' % 'out of order stop time for stop_id=%s trip_id=%s %s < %s' %
(util.EncodeUnicode(stoptime.stop_id), (util.EncodeUnicode(stoptime.stop_id),
util.EncodeUnicode(self.trip_id), util.EncodeUnicode(self.trip_id),
util.FormatSecondsSinceMidnight(new_secs), util.FormatSecondsSinceMidnight(new_secs),
util.FormatSecondsSinceMidnight(prev_secs))) util.FormatSecondsSinceMidnight(prev_secs)))
self._AddStopTimeObjectUnordered(stoptime, schedule) self._AddStopTimeObjectUnordered(stoptime, schedule)
   
def GetTimeStops(self): def GetTimeStops(self):
"""Return a list of (arrival_secs, departure_secs, stop) tuples. """Return a list of (arrival_secs, departure_secs, stop) tuples.
   
Caution: arrival_secs and departure_secs may be 0, a false value meaning a Caution: arrival_secs and departure_secs may be 0, a false value meaning a
stop at midnight or None, a false value meaning the stop is untimed.""" stop at midnight or None, a false value meaning the stop is untimed."""
return [(st.arrival_secs, st.departure_secs, st.stop) for st in return [(st.arrival_secs, st.departure_secs, st.stop) for st in
self.GetStopTimes()] self.GetStopTimes()]
   
def GetCountStopTimes(self): def GetCountStopTimes(self):
"""Return the number of stops made by this trip.""" """Return the number of stops made by this trip."""
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute( cursor.execute(
'SELECT count(*) FROM stop_times WHERE trip_id=?', (self.trip_id,)) 'SELECT count(*) FROM stop_times WHERE trip_id=?', (self.trip_id,))
return cursor.fetchone()[0] return cursor.fetchone()[0]
   
def GetTimeInterpolatedStops(self): def GetTimeInterpolatedStops(self):
"""Return a list of (secs, stoptime, is_timepoint) tuples. """Return a list of (secs, stoptime, is_timepoint) tuples.
   
secs will always be an int. If the StopTime object does not have explict secs will always be an int. If the StopTime object does not have explict
times this method guesses using distance. stoptime is a StopTime object and times this method guesses using distance. stoptime is a StopTime object and
is_timepoint is a bool. is_timepoint is a bool.
   
Raises: Raises:
ValueError if this trip does not have the times needed to interpolate ValueError if this trip does not have the times needed to interpolate
""" """
rv = [] rv = []
   
stoptimes = self.GetStopTimes() stoptimes = self.GetStopTimes()
# If there are no stoptimes [] is the correct return value but if the start # If there are no stoptimes [] is the correct return value but if the start
# or end are missing times there is no correct return value. # or end are missing times there is no correct return value.
if not stoptimes: if not stoptimes:
return [] return []
if (stoptimes[0].GetTimeSecs() is None or if (stoptimes[0].GetTimeSecs() is None or
stoptimes[-1].GetTimeSecs() is None): stoptimes[-1].GetTimeSecs() is None):
raise ValueError("%s must have time at first and last stop" % (self)) raise ValueError("%s must have time at first and last stop" % (self))
   
cur_timepoint = None cur_timepoint = None
next_timepoint = None next_timepoint = None
distance_between_timepoints = 0 distance_between_timepoints = 0
distance_traveled_between_timepoints = 0 distance_traveled_between_timepoints = 0
   
for i, st in enumerate(stoptimes): for i, st in enumerate(stoptimes):
if st.GetTimeSecs() != None: if st.GetTimeSecs() != None:
cur_timepoint = st cur_timepoint = st
distance_between_timepoints = 0 distance_between_timepoints = 0
distance_traveled_between_timepoints = 0 distance_traveled_between_timepoints = 0
if i + 1 < len(stoptimes): if i + 1 < len(stoptimes):
k = i + 1 k = i + 1
distance_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[k-1].stop, stoptimes[k].stop) distance_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[k-1].stop, stoptimes[k].stop)
while stoptimes[k].GetTimeSecs() == None: while stoptimes[k].GetTimeSecs() == None:
k += 1 k += 1
distance_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[k-1].stop, stoptimes[k].stop) distance_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[k-1].stop, stoptimes[k].stop)
next_timepoint = stoptimes[k] next_timepoint = stoptimes[k]
rv.append( (st.GetTimeSecs(), st, True) ) rv.append( (st.GetTimeSecs(), st, True) )
else: else:
distance_traveled_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[i-1].stop, st.stop) distance_traveled_between_timepoints += util.ApproximateDistanceBetweenStops(stoptimes[i-1].stop, st.stop)
distance_percent = distance_traveled_between_timepoints / distance_between_timepoints distance_percent = distance_traveled_between_timepoints / distance_between_timepoints
total_time = next_timepoint.GetTimeSecs() - cur_timepoint.GetTimeSecs() total_time = next_timepoint.GetTimeSecs() - cur_timepoint.GetTimeSecs()
time_estimate = distance_percent * total_time + cur_timepoint.GetTimeSecs() time_estimate = distance_percent * total_time + cur_timepoint.GetTimeSecs()
rv.append( (int(round(time_estimate)), st, False) ) rv.append( (int(round(time_estimate)), st, False) )
   
return rv return rv
   
def ClearStopTimes(self): def ClearStopTimes(self):
"""Remove all stop times from this trip. """Remove all stop times from this trip.
   
StopTime objects previously returned by GetStopTimes are unchanged but are StopTime objects previously returned by GetStopTimes are unchanged but are
no longer associated with this trip. no longer associated with this trip.
""" """
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute('DELETE FROM stop_times WHERE trip_id=?', (self.trip_id,)) cursor.execute('DELETE FROM stop_times WHERE trip_id=?', (self.trip_id,))
   
def GetStopTimes(self, problems=None): def GetStopTimes(self, problems=None):
"""Return a sorted list of StopTime objects for this trip.""" """Return a sorted list of StopTime objects for this trip."""
# In theory problems=None should be safe because data from database has been # In theory problems=None should be safe because data from database has been
# validated. See comment in _LoadStopTimes for why this isn't always true. # validated. See comment in _LoadStopTimes for why this isn't always true.
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute( cursor.execute(
'SELECT arrival_secs,departure_secs,stop_headsign,pickup_type,' 'SELECT arrival_secs,departure_secs,stop_headsign,pickup_type,'
'drop_off_type,shape_dist_traveled,stop_id,stop_sequence FROM ' 'drop_off_type,shape_dist_traveled,stop_id,stop_sequence FROM '
'stop_times WHERE ' 'stop_times WHERE '
'trip_id=? ORDER BY stop_sequence', (self.trip_id,)) 'trip_id=? ORDER BY stop_sequence', (self.trip_id,))
stop_times = [] stop_times = []
stoptime_class = self.GetGtfsFactory().StopTime stoptime_class = self.GetGtfsFactory().StopTime
for row in cursor.fetchall(): for row in cursor.fetchall():
stop = self._schedule.GetStop(row[6]) stop = self._schedule.GetStop(row[6])
stop_times.append(stoptime_class(problems=problems, stop_times.append(stoptime_class(problems=problems,
stop=stop, stop=stop,
arrival_secs=row[0], arrival_secs=row[0],
departure_secs=row[1], departure_secs=row[1],
stop_headsign=row[2], stop_headsign=row[2],
pickup_type=row[3], pickup_type=row[3],
drop_off_type=row[4], drop_off_type=row[4],
shape_dist_traveled=row[5], shape_dist_traveled=row[5],
stop_sequence=row[7])) stop_sequence=row[7]))
return stop_times return stop_times
   
def GetHeadwayStopTimes(self, problems=None): def GetHeadwayStopTimes(self, problems=None):
"""Deprecated. Please use GetFrequencyStopTimes instead.""" """Deprecated. Please use GetFrequencyStopTimes instead."""
warnings.warn("No longer supported. The HeadwayPeriod class was renamed to " warnings.warn("No longer supported. The HeadwayPeriod class was renamed to "
"Frequency, and all related functions were renamed " "Frequency, and all related functions were renamed "
"accordingly.", DeprecationWarning) "accordingly.", DeprecationWarning)
return self.GetFrequencyStopTimes(problems) return self.GetFrequencyStopTimes(problems)
   
def GetFrequencyStopTimes(self, problems=None): def GetFrequencyStopTimes(self, problems=None):
"""Return a list of StopTime objects for each headway-based run. """Return a list of StopTime objects for each headway-based run.
   
Returns: Returns:
a list of list of StopTime objects. Each list of StopTime objects a list of list of StopTime objects. Each list of StopTime objects
represents one run. If this trip doesn't have headways returns an empty represents one run. If this trip doesn't have headways returns an empty
list. list.
""" """
stoptimes_list = [] # list of stoptime lists to be returned stoptimes_list = [] # list of stoptime lists to be returned
stoptime_pattern = self.GetStopTimes() stoptime_pattern = self.GetStopTimes()
first_secs = stoptime_pattern[0].arrival_secs # first time of the trip first_secs = stoptime_pattern[0].arrival_secs # first time of the trip
stoptime_class = self.GetGtfsFactory().StopTime stoptime_class = self.GetGtfsFactory().StopTime
# for each start time of a headway run # for each start time of a headway run
for run_secs in self.GetFrequencyStartTimes(): for run_secs in self.GetFrequencyStartTimes():
# stop time list for a headway run # stop time list for a headway run
stoptimes = [] stoptimes = []
# go through the pattern and generate stoptimes # go through the pattern and generate stoptimes
for st in stoptime_pattern: for st in stoptime_pattern:
arrival_secs, departure_secs = None, None # default value if the stoptime is not timepoint arrival_secs, departure_secs = None, None # default value if the stoptime is not timepoint
if st.arrival_secs != None: if st.arrival_secs != None:
arrival_secs = st.arrival_secs - first_secs + run_secs arrival_secs = st.arrival_secs - first_secs + run_secs
if st.departure_secs != None: if st.departure_secs != None:
departure_secs = st.departure_secs - first_secs + run_secs departure_secs = st.departure_secs - first_secs + run_secs
# append stoptime # append stoptime
stoptimes.append(stoptime_class(problems=problems, stop=st.stop, stoptimes.append(stoptime_class(problems=problems, stop=st.stop,
arrival_secs=arrival_secs, arrival_secs=arrival_secs,
departure_secs=departure_secs, departure_secs=departure_secs,
stop_headsign=st.stop_headsign, stop_headsign=st.stop_headsign,
pickup_type=st.pickup_type, pickup_type=st.pickup_type,
drop_off_type=st.drop_off_type, drop_off_type=st.drop_off_type,
shape_dist_traveled= \ shape_dist_traveled= \
st.shape_dist_traveled, st.shape_dist_traveled,
stop_sequence=st.stop_sequence)) stop_sequence=st.stop_sequence))
# add stoptimes to the stoptimes_list # add stoptimes to the stoptimes_list
stoptimes_list.append ( stoptimes ) stoptimes_list.append ( stoptimes )
return stoptimes_list return stoptimes_list
   
def GetStartTime(self, problems=problems_module.default_problem_reporter): def GetStartTime(self, problems=problems_module.default_problem_reporter):
"""Return the first time of the trip. TODO: For trips defined by frequency """Return the first time of the trip. TODO: For trips defined by frequency
return the first time of the first trip.""" return the first time of the first trip."""
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute( cursor.execute(
'SELECT arrival_secs,departure_secs FROM stop_times WHERE ' 'SELECT arrival_secs,departure_secs FROM stop_times WHERE '
'trip_id=? ORDER BY stop_sequence LIMIT 1', (self.trip_id,)) 'trip_id=? ORDER BY stop_sequence LIMIT 1', (self.trip_id,))
  print self.trip_id
(arrival_secs, departure_secs) = cursor.fetchone() (arrival_secs, departure_secs) = cursor.fetchone()
if arrival_secs != None: if arrival_secs != None:
return arrival_secs return arrival_secs
elif departure_secs != None: elif departure_secs != None:
return departure_secs return departure_secs
else: else:
problems.InvalidValue('departure_time', '', problems.InvalidValue('departure_time', '',
'The first stop_time in trip %s is missing ' 'The first stop_time in trip %s is missing '
'times.' % self.trip_id) 'times.' % self.trip_id)
   
def GetHeadwayStartTimes(self): def GetHeadwayStartTimes(self):
"""Deprecated. Please use GetFrequencyStartTimes instead.""" """Deprecated. Please use GetFrequencyStartTimes instead."""
warnings.warn("No longer supported. The HeadwayPeriod class was renamed to " warnings.warn("No longer supported. The HeadwayPeriod class was renamed to "
"Frequency, and all related functions were renamed " "Frequency, and all related functions were renamed "
"accordingly.", DeprecationWarning) "accordingly.", DeprecationWarning)
return self.GetFrequencyStartTimes() return self.GetFrequencyStartTimes()
   
def GetFrequencyStartTimes(self): def GetFrequencyStartTimes(self):
"""Return a list of start time for each headway-based run. """Return a list of start time for each headway-based run.
   
Returns: Returns:
a sorted list of seconds since midnight, the start time of each run. If a sorted list of seconds since midnight, the start time of each run. If
this trip doesn't have headways returns an empty list.""" this trip doesn't have headways returns an empty list."""
start_times = [] start_times = []
# for each headway period of the trip # for each headway period of the trip
for start_secs, end_secs, headway_secs in self.GetFrequencyTuples(): for start_secs, end_secs, headway_secs in self.GetFrequencyTuples():
# reset run secs to the start of the timeframe # reset run secs to the start of the timeframe
run_secs = start_secs run_secs = start_secs
while run_secs < end_secs: while run_secs < end_secs:
start_times.append(run_secs) start_times.append(run_secs)
# increment current run secs by headway secs # increment current run secs by headway secs
run_secs += headway_secs run_secs += headway_secs
return start_times return start_times
   
def GetEndTime(self, problems=problems_module.default_problem_reporter): def GetEndTime(self, problems=problems_module.default_problem_reporter):
"""Return the last time of the trip. TODO: For trips defined by frequency """Return the last time of the trip. TODO: For trips defined by frequency
return the last time of the last trip.""" return the last time of the last trip."""
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute( cursor.execute(
'SELECT arrival_secs,departure_secs FROM stop_times WHERE ' 'SELECT arrival_secs,departure_secs FROM stop_times WHERE '
'trip_id=? ORDER BY stop_sequence DESC LIMIT 1', (self.trip_id,)) 'trip_id=? ORDER BY stop_sequence DESC LIMIT 1', (self.trip_id,))
(arrival_secs, departure_secs) = cursor.fetchone() (arrival_secs, departure_secs) = cursor.fetchone()
if departure_secs != None: if departure_secs != None:
return departure_secs return departure_secs
elif arrival_secs != None: elif arrival_secs != None:
return arrival_secs return arrival_secs
else: else:
problems.InvalidValue('arrival_time', '', problems.InvalidValue('arrival_time', '',
'The last stop_time in trip %s is missing ' 'The last stop_time in trip %s is missing '
'times.' % self.trip_id) 'times.' % self.trip_id)
   
def _GenerateStopTimesTuples(self): def _GenerateStopTimesTuples(self):
"""Generator for rows of the stop_times file""" """Generator for rows of the stop_times file"""
stoptimes = self.GetStopTimes() stoptimes = self.GetStopTimes()
for i, st in enumerate(stoptimes): for i, st in enumerate(stoptimes):
yield st.GetFieldValuesTuple(self.trip_id) yield st.GetFieldValuesTuple(self.trip_id)
   
def GetStopTimesTuples(self): def GetStopTimesTuples(self):
results = [] results = []
for time_tuple in self._GenerateStopTimesTuples(): for time_tuple in self._GenerateStopTimesTuples():
results.append(time_tuple) results.append(time_tuple)
return results return results
   
def GetPattern(self): def GetPattern(self):
"""Return a tuple of Stop objects, in the order visited""" """Return a tuple of Stop objects, in the order visited"""
stoptimes = self.GetStopTimes() stoptimes = self.GetStopTimes()
return tuple(st.stop for st in stoptimes) return tuple(st.stop for st in stoptimes)
   
def AddHeadwayPeriodObject(self, headway_period, problem_reporter): def AddHeadwayPeriodObject(self, headway_period, problem_reporter):
"""Deprecated. Please use AddFrequencyObject instead.""" """Deprecated. Please use AddFrequencyObject instead."""
warnings.warn("No longer supported. The HeadwayPeriod class was renamed to " warnings.warn("No longer supported. The HeadwayPeriod class was renamed to "
"Frequency, and all related functions were renamed " "Frequency, and all related functions were renamed "
"accordingly.", DeprecationWarning) "accordingly.", DeprecationWarning)
self.AddFrequencyObject(frequency, problem_reporter) self.AddFrequencyObject(frequency, problem_reporter)
   
def AddFrequencyObject(self, frequency, problem_reporter): def AddFrequencyObject(self, frequency, problem_reporter):
"""Add a Frequency object to this trip's list of Frequencies.""" """Add a Frequency object to this trip's list of Frequencies."""
if frequency is not None: if frequency is not None:
self.AddFrequency(frequency.StartTime(), self.AddFrequency(frequency.StartTime(),
frequency.EndTime(), frequency.EndTime(),
frequency.HeadwaySecs(), frequency.HeadwaySecs(),
problem_reporter) problem_reporter)
   
def AddHeadwayPeriod(self, start_time, end_time, headway_secs, def AddHeadwayPeriod(self, start_time, end_time, headway_secs,
problem_reporter=problems_module.default_problem_reporter): problem_reporter=problems_module.default_problem_reporter):
"""Deprecated. Please use AddFrequency instead.""" """Deprecated. Please use AddFrequency instead."""
warnings.warn("No longer supported. The HeadwayPeriod class was renamed to " warnings.warn("No longer supported. The HeadwayPeriod class was renamed to "
"Frequency, and all related functions were renamed " "Frequency, and all related functions were renamed "
"accordingly.", DeprecationWarning) "accordingly.", DeprecationWarning)
self.AddFrequency(start_time, end_time, headway_secs, problem_reporter) self.AddFrequency(start_time, end_time, headway_secs, problem_reporter)
   
def AddFrequency(self, start_time, end_time, headway_secs, def AddFrequency(self, start_time, end_time, headway_secs,
problem_reporter=problems_module.default_problem_reporter): problem_reporter=problems_module.default_problem_reporter):
"""Adds a period to this trip during which the vehicle travels """Adds a period to this trip during which the vehicle travels
at regular intervals (rather than specifying exact times for each stop). at regular intervals (rather than specifying exact times for each stop).
   
Args: Args:
start_time: The time at which this headway period starts, either in start_time: The time at which this headway period starts, either in
numerical seconds since midnight or as "HH:MM:SS" since midnight. numerical seconds since midnight or as "HH:MM:SS" since midnight.
end_time: The time at which this headway period ends, either in end_time: The time at which this headway period ends, either in
numerical seconds since midnight or as "HH:MM:SS" since midnight. numerical seconds since midnight or as "HH:MM:SS" since midnight.
This value should be larger than start_time. This value should be larger than start_time.
headway_secs: The amount of time, in seconds, between occurences of headway_secs: The amount of time, in seconds, between occurences of
this trip. this trip.
problem_reporter: Optional parameter that can be used to select problem_reporter: Optional parameter that can be used to select
how any errors in the other input parameters will be reported. how any errors in the other input parameters will be reported.
Returns: Returns:
None None
""" """
if start_time == None or start_time == '': # 0 is OK if start_time == None or start_time == '': # 0 is OK
problem_reporter.MissingValue('start_time') problem_reporter.MissingValue('start_time')
return return
if isinstance(start_time, basestring): if isinstance(start_time, basestring):
try: try:
start_time = util.TimeToSecondsSinceMidnight(start_time) start_time = util.TimeToSecondsSinceMidnight(start_time)
except problems_module.Error: except problems_module.Error:
problem_reporter.InvalidValue('start_time', start_time) problem_reporter.InvalidValue('start_time', start_time)
return return
elif start_time < 0: elif start_time < 0:
problem_reporter.InvalidValue('start_time', start_time) problem_reporter.InvalidValue('start_time', start_time)
   
if end_time == None or end_time == '': if end_time == None or end_time == '':
problem_reporter.MissingValue('end_time') problem_reporter.MissingValue('end_time')
return return
if isinstance(end_time, basestring): if isinstance(end_time, basestring):
try: try:
end_time = util.TimeToSecondsSinceMidnight(end_time) end_time = util.TimeToSecondsSinceMidnight(end_time)
except problems_module.Error: except problems_module.Error:
problem_reporter.InvalidValue('end_time', end_time) problem_reporter.InvalidValue('end_time', end_time)
return return
elif end_time < 0: elif end_time < 0:
problem_reporter.InvalidValue('end_time', end_time) problem_reporter.InvalidValue('end_time', end_time)
return return
   
if not headway_secs: if not headway_secs:
problem_reporter.MissingValue('headway_secs') problem_reporter.MissingValue('headway_secs')
return return
try: try:
headway_secs = int(headway_secs) headway_secs = int(headway_secs)
except ValueError: except ValueError:
problem_reporter.InvalidValue('headway_secs', headway_secs) problem_reporter.InvalidValue('headway_secs', headway_secs)
return return
   
if headway_secs <= 0: if headway_secs <= 0:
problem_reporter.InvalidValue('headway_secs', headway_secs) problem_reporter.InvalidValue('headway_secs', headway_secs)
return return
   
if end_time <= start_time: if end_time <= start_time:
problem_reporter.InvalidValue('end_time', end_time, problem_reporter.InvalidValue('end_time', end_time,
'should be greater than start_time') 'should be greater than start_time')
   
self._headways.append((start_time, end_time, headway_secs)) self._headways.append((start_time, end_time, headway_secs))
   
def ClearFrequencies(self): def ClearFrequencies(self):
self._headways = [] self._headways = []
   
def _HeadwayOutputTuple(self, headway): def _HeadwayOutputTuple(self, headway):
return (self.trip_id, return (self.trip_id,
util.FormatSecondsSinceMidnight(headway[0]), util.FormatSecondsSinceMidnight(headway[0]),
util.FormatSecondsSinceMidnight(headway[1]), util.FormatSecondsSinceMidnight(headway[1]),
unicode(headway[2])) unicode(headway[2]))
   
def GetFrequencyOutputTuples(self): def GetFrequencyOutputTuples(self):
tuples = [] tuples = []
for headway in self._headways: for headway in self._headways:
tuples.append(self._HeadwayOutputTuple(headway)) tuples.append(self._HeadwayOutputTuple(headway))
return tuples return tuples
   
def GetFrequencyTuples(self): def GetFrequencyTuples(self):
return self._headways return self._headways
   
def __getattr__(self, name): def __getattr__(self, name):
if name == 'service_period': if name == 'service_period':
assert self._schedule, "Must be in a schedule to get service_period" assert self._schedule, "Must be in a schedule to get service_period"
return self._schedule.GetServicePeriod(self.service_id) return self._schedule.GetServicePeriod(self.service_id)
elif name == 'pattern_id': elif name == 'pattern_id':
if '_pattern_id' not in self.__dict__: if '_pattern_id' not in self.__dict__:
self.__dict__['_pattern_id'] = hash(self.GetPattern()) self.__dict__['_pattern_id'] = hash(self.GetPattern())
return self.__dict__['_pattern_id'] return self.__dict__['_pattern_id']
else: else:
return GtfsObjectBase.__getattr__(self, name) return GtfsObjectBase.__getattr__(self, name)
   
def ValidateRouteId(self, problems): def ValidateRouteId(self, problems):
if util.IsEmpty(self.route_id): if util.IsEmpty(self.route_id):
problems.MissingValue('route_id') problems.MissingValue('route_id')
def ValidateServicePeriod(self, problems): def ValidateServicePeriod(self, problems):
if 'service_period' in self.__dict__: if 'service_period' in self.__dict__:
# Some tests assign to the service_period attribute. Patch up self before # Some tests assign to the service_period attribute. Patch up self before
# proceeding with validation. See also comment in Trip.__init__. # proceeding with validation. See also comment in Trip.__init__.
self.service_id = self.__dict__['service_period'].service_id self.service_id = self.__dict__['service_period'].service_id
del self.service_period del self.service_period
if util.IsEmpty(self.service_id): if util.IsEmpty(self.service_id):
problems.MissingValue('service_id') problems.MissingValue('service_id')
def ValidateTripId(self, problems): def ValidateTripId(self, problems):
if util.IsEmpty(self.trip_id): if util.IsEmpty(self.trip_id):
problems.MissingValue('trip_id') problems.MissingValue('trip_id')
def ValidateDirectionId(self, problems): def ValidateDirectionId(self, problems):
if hasattr(self, 'direction_id') and (not util.IsEmpty(self.direction_id)) \ if hasattr(self, 'direction_id') and (not util.IsEmpty(self.direction_id)) \
and (self.direction_id != '0') and (self.direction_id != '1'): and (self.direction_id != '0') and (self.direction_id != '1'):
problems.InvalidValue('direction_id', self.direction_id, problems.InvalidValue('direction_id', self.direction_id,
'direction_id must be "0" or "1"') 'direction_id must be "0" or "1"')
   
def ValidateShapeIdsExistInShapeList(self, problems): def ValidateShapeIdsExistInShapeList(self, problems):
if self._schedule: if self._schedule:
if self.shape_id and self.shape_id not in self._schedule._shapes: if self.shape_id and self.shape_id not in self._schedule._shapes:
problems.InvalidValue('shape_id', self.shape_id) problems.InvalidValue('shape_id', self.shape_id)
def ValidateRouteIdExistsInRouteList(self, problems): def ValidateRouteIdExistsInRouteList(self, problems):
if self._schedule: if self._schedule:
if self.route_id and self.route_id not in self._schedule.routes: if self.route_id and self.route_id not in self._schedule.routes:
problems.InvalidValue('route_id', self.route_id) problems.InvalidValue('route_id', self.route_id)
def ValidateServiceIdExistsInServiceList(self, problems): def ValidateServiceIdExistsInServiceList(self, problems):
if self._schedule: if self._schedule:
if (self.service_id and if (self.service_id and
self.service_id not in self._schedule.service_periods): self.service_id not in self._schedule.service_periods):
problems.InvalidValue('service_id', self.service_id) problems.InvalidValue('service_id', self.service_id)
   
def Validate(self, problems, validate_children=True): def Validate(self, problems, validate_children=True):
"""Validate attributes of this object. """Validate attributes of this object.
   
Check that this object has all required values set to a valid value without Check that this object has all required values set to a valid value without
reference to the rest of the schedule. If the _schedule attribute is set reference to the rest of the schedule. If the _schedule attribute is set
then check that references such as route_id and service_id are correct. then check that references such as route_id and service_id are correct.
   
Args: Args:
problems: A ProblemReporter object problems: A ProblemReporter object
validate_children: if True and the _schedule attribute is set than call validate_children: if True and the _schedule attribute is set than call
ValidateChildren ValidateChildren
""" """
self.ValidateRouteId(problems) self.ValidateRouteId(problems)
self.ValidateServicePeriod(problems) self.ValidateServicePeriod(problems)
self.ValidateDirectionId(problems) self.ValidateDirectionId(problems)
self.ValidateTripId(problems) self.ValidateTripId(problems)
self.ValidateShapeIdsExistInShapeList(problems) self.ValidateShapeIdsExistInShapeList(problems)
self.ValidateRouteIdExistsInRouteList(problems) self.ValidateRouteIdExistsInRouteList(problems)
self.ValidateServiceIdExistsInServiceList(problems) self.ValidateServiceIdExistsInServiceList(problems)
if self._schedule and validate_children: if self._schedule and validate_children:
self.ValidateChildren(problems) self.ValidateChildren(problems)
   
def ValidateNoDuplicateStopSequences(self, problems): def ValidateNoDuplicateStopSequences(self, problems):
cursor = self._schedule._connection.cursor() cursor = self._schedule._connection.cursor()
cursor.execute("SELECT COUNT(stop_sequence) AS a FROM stop_times " cursor.execute("SELECT COUNT(stop_sequence) AS a FROM stop_times "
"WHERE trip_id=? GROUP BY stop_sequence HAVING a > 1", "WHERE trip_id=? GROUP BY stop_sequence HAVING a > 1",
(self.trip_id,)) (self.trip_id,))
for row in cursor: for row in cursor:
problems.InvalidValue('stop_sequence', row[0], problems.InvalidValue('stop_sequence', row[0],
'Duplicate stop_sequence in trip_id %s' % 'Duplicate stop_sequence in trip_id %s' %
self.trip_id) self.trip_id)
   
def ValidateTripStartAndEndTimes(self, problems, stoptimes): def ValidateTripStartAndEndTimes(self, problems, stoptimes):
if stoptimes: if stoptimes:
if stoptimes[0].arrival_time is None and stoptimes[0].departure_time is None: if stoptimes[0].arrival_time is None and stoptimes[0].departure_time is None:
problems.OtherProblem( problems.OtherProblem(
'No time for start of trip_id "%s""' % (self.trip_id)) 'No time for start of trip_id "%s""' % (self.trip_id))
if stoptimes[-1].arrival_time is None and stoptimes[-1].departure_time is None: if stoptimes[-1].arrival_time is None and stoptimes[-1].departure_time is None:
problems.OtherProblem( problems.OtherProblem(
'No time for end of trip_id "%s""' % (self.trip_id)) 'No time for end of trip_id "%s""' % (self.trip_id))
   
def ValidateStopTimesSequenceHasIncreasingTimeAndDistance(self, def ValidateStopTimesSequenceHasIncreasingTimeAndDistance(self,
problems, problems,
stoptimes): stoptimes):
if stoptimes: if stoptimes:
route_class = self.GetGtfsFactory().Route route_class = self.GetGtfsFactory().Route
# Checks that the arrival time for each time point is after the departure # Checks that the arrival time for each time point is after the departure
# time of the previous. Assumes a stoptimes sorted by sequence # time of the previous. Assumes a stoptimes sorted by sequence
prev_departure = 0 prev_departure = 0
prev_stop = None prev_stop = None
prev_distance = None prev_distance = None
try: try:
route_type = self._schedule.GetRoute(self.route_id).route_type route_type = self._schedule.GetRoute(self.route_id).route_type
max_speed = route_class._ROUTE_TYPES[route_type]['max_speed'] max_speed = route_class._ROUTE_TYPES[route_type]['max_speed']
except KeyError, e: except KeyError, e:
# If route_type cannot be found, assume it is 0 (Tram) for checking # If route_type cannot be found, assume it is 0 (Tram) for checking
# speeds between stops. # speeds between stops.
max_speed = route_class._ROUTE_TYPES[0]['max_speed'] max_speed = route_class._ROUTE_TYPES[0]['max_speed']
for timepoint in stoptimes: for timepoint in stoptimes:
# Distance should be a nonnegative float number, so it should be # Distance should be a nonnegative float number, so it should be
# always larger than None. # always larger than None.
distance = timepoint.shape_dist_traveled distance = timepoint.shape_dist_traveled
if distance is not None: if distance is not None:
if distance > prev_distance and distance >= 0: if distance > prev_distance and distance >= 0:
prev_distance = distance prev_distance = distance
else: else:
if distance == prev_distance: if distance == prev_distance:
type = problems_module.TYPE_WARNING type = problems_module.TYPE_WARNING
else: else:
type = problems_module.TYPE_ERROR type = problems_module.TYPE_ERROR
problems.InvalidValue('stoptimes.shape_dist_traveled', distance, problems.InvalidValue('stoptimes.shape_dist_traveled', distance,
'For the trip %s the stop %s has shape_dist_traveled=%s, ' 'For the trip %s the stop %s has shape_dist_traveled=%s, '
'which should be larger than the previous ones. In this ' 'which should be larger than the previous ones. In this '
'case, the previous distance was %s.' % 'case, the previous distance was %s.' %
(self.trip_id, timepoint.stop_id, distance, prev_distance), (self.trip_id, timepoint.stop_id, distance, prev_distance),
type=type) type=type)
   
if timepoint.arrival_secs is not None: if timepoint.arrival_secs is not None:
self._CheckSpeed(prev_stop, timepoint.stop, prev_departure, self._CheckSpeed(prev_stop, timepoint.stop, prev_departure,
timepoint.arrival_secs, max_speed, problems) timepoint.arrival_secs, max_speed, problems)
   
if timepoint.arrival_secs >= prev_departure: if timepoint.arrival_secs >= prev_departure:
prev_departure = timepoint.departure_secs prev_departure = timepoint.departure_secs
prev_stop = timepoint.stop prev_stop = timepoint.stop
else: else:
problems.OtherProblem('Timetravel detected! Arrival time ' problems.OtherProblem('Timetravel detected! Arrival time '
'is before previous departure ' 'is before previous departure '
'at sequence number %s in trip %s' % 'at sequence number %s in trip %s' %
(timepoint.stop_sequence, self.trip_id)) (timepoint.stop_sequence, self.trip_id))
   
def ValidateShapeDistTraveledSmallerThanMaxShapeDistance(self, def ValidateShapeDistTraveledSmallerThanMaxShapeDistance(self,
problems, problems,
stoptimes): stoptimes):
if stoptimes: if stoptimes:
if self.shape_id and self.shape_id in self._schedule._shapes: if self.shape_id and self.shape_id in self._schedule._shapes:
shape = self._schedule.GetShape(self.shape_id) shape = self._schedule.GetShape(self.shape_id)
max_shape_dist = shape.max_distance max_shape_dist = shape.max_distance
st = stoptimes[-1] st = stoptimes[-1]
if (st.shape_dist_traveled and if (st.shape_dist_traveled and
st.shape_dist_traveled > max_shape_dist): st.shape_dist_traveled > max_shape_dist):
problems.OtherProblem( problems.OtherProblem(
'In stop_times.txt, the stop with trip_id=%s and ' 'In stop_times.txt, the stop with trip_id=%s and '
'stop_sequence=%d has shape_dist_traveled=%f, which is larger ' 'stop_sequence=%d has shape_dist_traveled=%f, which is larger '
'than the max shape_dist_traveled=%f of the corresponding ' 'than the max shape_dist_traveled=%f of the corresponding '
'shape (shape_id=%s)' % 'shape (shape_id=%s)' %
(self.trip_id, st.stop_sequence, st.shape_dist_traveled, (self.trip_id, st.stop_sequence, st.shape_dist_traveled,
max_shape_dist, self.shape_id), max_shape_dist, self.shape_id),
type=problems_module.TYPE_WARNING) type=problems_module.TYPE_WARNING)
   
def ValidateDistanceFromStopToShape(self, problems, stoptimes): def ValidateDistanceFromStopToShape(self, problems, stoptimes):
if stoptimes: if stoptimes:
if self.shape_id and self.shape_id in self._schedule._shapes: if self.shape_id and self.shape_id in self._schedule._shapes:
shape = self._schedule.GetShape(self.shape_id) shape = self._schedule.GetShape(self.shape_id)
max_shape_dist = shape.max_distance max_shape_dist = shape.max_distance
st = stoptimes[-1] st = stoptimes[-1]
# shape_dist_traveled is valid in shape if max_shape_dist larger than # shape_dist_traveled is valid in shape if max_shape_dist larger than
# 0. # 0.
if max_shape_dist > 0: if max_shape_dist > 0:
for st in stoptimes: for st in stoptimes:
if st.shape_dist_traveled is None: if st.shape_dist_traveled is None:
continue continue
pt = shape.GetPointWithDistanceTraveled(st.shape_dist_traveled) pt = shape.GetPointWithDistanceTraveled(st.shape_dist_traveled)
if pt: if pt:
stop = self._schedule.GetStop(st.stop_id) stop = self._schedule.GetStop(st.stop_id)
distance = util.ApproximateDistance(stop.stop_lat, stop.stop_lon, distance = util.ApproximateDistance(stop.stop_lat, stop.stop_lon,
pt[0], pt[1]) pt[0], pt[1])
if distance > problems_module.MAX_DISTANCE_FROM_STOP_TO_SHAPE: if distance > problems_module.MAX_DISTANCE_FROM_STOP_TO_SHAPE:
problems.StopTooFarFromShapeWithDistTraveled( problems.StopTooFarFromShapeWithDistTraveled(
self.trip_id, stop.stop_name, stop.stop_id, pt[2], self.trip_id, stop.stop_name, stop.stop_id, pt[2],
self.shape_id, distance, self.shape_id, distance,
problems_module.MAX_DISTANCE_FROM_STOP_TO_SHAPE) problems_module.MAX_DISTANCE_FROM_STOP_TO_SHAPE)
   
def ValidateFrequencies(self, problems): def ValidateFrequencies(self, problems):
# O(n^2), but we don't anticipate many headway periods per trip # O(n^2), but we don't anticipate many headway periods per trip
for headway_index, headway in enumerate(self._headways[0:-1]): for headway_index, headway in enumerate(self._headways[0:-1]):
for other in self._headways[headway_index + 1:]: for other in self._headways[headway_index + 1:]:
if (other[0] < headway[1]) and (other[1] > headway[0]): if (other[0] < headway[1]) and (other[1] > headway[0]):
problems.OtherProblem('Trip contains overlapping headway periods ' problems.OtherProblem('Trip contains overlapping headway periods '
'%s and %s' % '%s and %s' %
(self._HeadwayOutputTuple(headway), (self._HeadwayOutputTuple(headway),
self._HeadwayOutputTuple(other))) self._HeadwayOutputTuple(other)))
   
def ValidateChildren(self, problems): def ValidateChildren(self, problems):
"""Validate StopTimes and headways of this trip.""" """Validate StopTimes and headways of this trip."""
assert self._schedule, "Trip must be in a schedule to ValidateChildren" assert self._schedule, "Trip must be in a schedule to ValidateChildren"
# TODO: validate distance values in stop times (if applicable) # TODO: validate distance values in stop times (if applicable)
   
self.ValidateNoDuplicateStopSequences(problems) self.ValidateNoDuplicateStopSequences(problems)
stoptimes = self.GetStopTimes(problems) stoptimes = self.GetStopTimes(problems)
stoptimes.sort(key=lambda x: x.stop_sequence) stoptimes.sort(key=lambda x: x.stop_sequence)
self.ValidateTripStartAndEndTimes(problems, stoptimes) self.ValidateTripStartAndEndTimes(problems, stoptimes)
self.ValidateStopTimesSequenceHasIncreasingTimeAndDistance(problems, self.ValidateStopTimesSequenceHasIncreasingTimeAndDistance(problems,
stoptimes) stoptimes)
self.ValidateShapeDistTraveledSmallerThanMaxShapeDistance(problems, self.ValidateShapeDistTraveledSmallerThanMaxShapeDistance(problems,
stoptimes) stoptimes)
self.ValidateDistanceFromStopToShape(problems, stoptimes) self.ValidateDistanceFromStopToShape(problems, stoptimes)
self.ValidateFrequencies(problems) self.ValidateFrequencies(problems)
def ValidateBeforeAdd(self, problems): def ValidateBeforeAdd(self, problems):
return True return True
   
def ValidateAfterAdd(self, problems): def ValidateAfterAdd(self, problems):
self.Validate(problems) self.Validate(problems)
   
def _CheckSpeed(self, prev_stop, next_stop, depart_time, def _CheckSpeed(self, prev_stop, next_stop, depart_time,
arrive_time, max_speed, problems): arrive_time, max_speed, problems):
# Checks that the speed between two stops is not faster than max_speed # Checks that the speed between two stops is not faster than max_speed
if prev_stop != None: if prev_stop != None:
try: try:
time_between_stops = arrive_time - depart_time time_between_stops = arrive_time - depart_time
except TypeError: except TypeError:
return return
   
try: try:
dist_between_stops = \ dist_between_stops = \
util.ApproximateDistanceBetweenStops(next_stop, prev_stop) util.ApproximateDistanceBetweenStops(next_stop, prev_stop)
except TypeError, e: except TypeError, e:
return return
   
if time_between_stops == 0: if time_between_stops == 0:
# HASTUS makes it hard to output GTFS with times to the nearest second; # HASTUS makes it hard to output GTFS with times to the nearest second;
# it rounds times to the nearest minute. Therefore stop_times at the # it rounds times to the nearest minute. Therefore stop_times at the
# same time ending in :00 are fairly common. These times off by no more # same time ending in :00 are fairly common. These times off by no more
# than 30 have not caused a problem. See # than 30 have not caused a problem. See
# http://code.google.com/p/googletransitdatafeed/issues/detail?id=193 # http://code.google.com/p/googletransitdatafeed/issues/detail?id=193
# Show a warning if times are not rounded to the nearest minute or # Show a warning if times are not rounded to the nearest minute or
# distance is more than max_speed for one minute. # distance is more than max_speed for one minute.
if depart_time % 60 != 0 or dist_between_stops / 1000 * 60 > max_speed: if depart_time % 60 != 0 or dist_between_stops / 1000 * 60 > max_speed:
problems.TooFastTravel(self.trip_id, problems.TooFastTravel(self.trip_id,
prev_stop.stop_name, prev_stop.stop_name,
next_stop.stop_name, next_stop.stop_name,
dist_between_stops, dist_between_stops,
time_between_stops, time_between_stops,
speed=None, speed=None,
type=problems_module.TYPE_WARNING) type=problems_module.TYPE_WARNING)
return return
# This needs floating point division for precision. # This needs floating point division for precision.
speed_between_stops = ((float(dist_between_stops) / 1000) / speed_between_stops = ((float(dist_between_stops) / 1000) /
(float(time_between_stops) / 3600)) (float(time_between_stops) / 3600))
if speed_between_stops > max_speed: if speed_between_stops > max_speed:
problems.TooFastTravel(self.trip_id, problems.TooFastTravel(self.trip_id,
prev_stop.stop_name, prev_stop.stop_name,
next_stop.stop_name, next_stop.stop_name,
dist_between_stops, dist_between_stops,
time_between_stops, time_between_stops,
speed_between_stops, speed_between_stops,
type=problems_module.TYPE_WARNING) type=problems_module.TYPE_WARNING)
   
def AddToSchedule(self, schedule, problems): def AddToSchedule(self, schedule, problems):
schedule.AddTripObject(self, problems) schedule.AddTripObject(self, problems)