#!/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) |
|
|