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()