Add and Remove operations for between stops
[bus.git] / origin-src / transitfeed-1.2.6 / transitfeed / stoptime.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/usr/bin/python2.5
 
# Copyright (C) 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import problems as problems_module
from stop import Stop
import util
 
class StopTime(object):
  """
  Represents a single stop of a trip. StopTime contains most of the columns
  from the stop_times.txt file. It does not contain trip_id, which is implied
  by the Trip used to access it.
 
  See the Google Transit Feed Specification for the semantic details.
 
  stop: A Stop object
  arrival_time: str in the form HH:MM:SS; readonly after __init__
  departure_time: str in the form HH:MM:SS; readonly after __init__
  arrival_secs: int number of seconds since midnight
  departure_secs: int number of seconds since midnight
  stop_headsign: str
  pickup_type: int
  drop_off_type: int
  shape_dist_traveled: float
  stop_id: str; readonly
  stop_time: The only time given for this stop.  If present, it is used
             for both arrival and departure time.
  stop_sequence: int
  """
  _REQUIRED_FIELD_NAMES = ['trip_id', 'arrival_time', 'departure_time',
                           'stop_id', 'stop_sequence']
  _OPTIONAL_FIELD_NAMES = ['stop_headsign', 'pickup_type',
                           'drop_off_type', 'shape_dist_traveled']
  _FIELD_NAMES = _REQUIRED_FIELD_NAMES + _OPTIONAL_FIELD_NAMES
  _SQL_FIELD_NAMES = ['trip_id', 'arrival_secs', 'departure_secs',
                      'stop_id', 'stop_sequence', 'stop_headsign',
                      'pickup_type', 'drop_off_type', 'shape_dist_traveled']
  _STOP_CLASS = Stop
 
  __slots__ = ('arrival_secs', 'departure_secs', 'stop_headsign', 'stop',
               'stop_headsign', 'pickup_type', 'drop_off_type',
               'shape_dist_traveled', 'stop_sequence')
  def __init__(self, problems, stop,
               arrival_time=None, departure_time=None,
               stop_headsign=None, pickup_type=None, drop_off_type=None,
               shape_dist_traveled=None, arrival_secs=None,
               departure_secs=None, stop_time=None, stop_sequence=None):
    # Implementation note from Andre, July 22, 2010:
    # The checks performed here should be in their own Validate* methods to
    # keep consistency. Unfortunately the performance degradation is too great,
    # so the validation was left in __init__.
    # Performance is also the reason why we don't use the GtfsFactory, but
    # have StopTime._STOP_CLASS instead. If a Stop class that does not inherit
    # from transitfeed.Stop is used, the extension should also provide a
    # StopTime class that updates _STOP_CLASS accordingly.
    #
    # For more details see the discussion at
    # http://codereview.appspot.com/1713041
    if stop_time != None:
      arrival_time = departure_time = stop_time
 
    if arrival_secs != None:
      self.arrival_secs = arrival_secs
    elif arrival_time in (None, ""):
      self.arrival_secs = None  # Untimed
      arrival_time = None
    else:
      try:
        self.arrival_secs = util.TimeToSecondsSinceMidnight(arrival_time)
      except problems_module.Error:
        problems.InvalidValue('arrival_time', arrival_time)
        self.arrival_secs = None
 
    if departure_secs != None:
      self.departure_secs = departure_secs
    elif departure_time in (None, ""):
      self.departure_secs = None
      departure_time = None
    else:
      try:
        self.departure_secs = util.TimeToSecondsSinceMidnight(departure_time)
      except problems_module.Error:
        problems.InvalidValue('departure_time', departure_time)
        self.departure_secs = None
 
    if not isinstance(stop, self._STOP_CLASS):
      # Not quite correct, but better than letting the problem propagate
      problems.InvalidValue('stop', stop)
    self.stop = stop
    self.stop_headsign = stop_headsign
 
    if pickup_type in (None, ""):
      self.pickup_type = None
    else:
      try:
        pickup_type = int(pickup_type)
      except ValueError:
        problems.InvalidValue('pickup_type', pickup_type)
      else:
        if pickup_type < 0 or pickup_type > 3:
          problems.InvalidValue('pickup_type', pickup_type)
      self.pickup_type = pickup_type
 
    if drop_off_type in (None, ""):
      self.drop_off_type = None
    else:
      try:
        drop_off_type = int(drop_off_type)
      except ValueError:
        problems.InvalidValue('drop_off_type', drop_off_type)
      else:
        if drop_off_type < 0 or drop_off_type > 3:
          problems.InvalidValue('drop_off_type', drop_off_type)
      self.drop_off_type = drop_off_type
 
    if (self.pickup_type == 1 and self.drop_off_type == 1 and
        self.arrival_secs == None and self.departure_secs == None):
      problems.OtherProblem('This stop time has a pickup_type and '
                            'drop_off_type of 1, indicating that riders '
                            'can\'t get on or off here.  Since it doesn\'t '
                            'define a timepoint either, this entry serves no '
                            'purpose and should be excluded from the trip.',
                            type=problems_module.TYPE_WARNING)
 
    if ((self.arrival_secs != None) and (self.departure_secs != None) and
        (self.departure_secs < self.arrival_secs)):
      problems.InvalidValue('departure_time', departure_time,
                            'The departure time at this stop (%s) is before '
                            'the arrival time (%s).  This is often caused by '
                            'problems in the feed exporter\'s time conversion')
 
    # If the caller passed a valid arrival time but didn't attempt to pass a
    # departure time complain
    if (self.arrival_secs != None and
        self.departure_secs == None and departure_time == None):
      # self.departure_secs might be None because departure_time was invalid,
      # so we need to check both
      problems.MissingValue('departure_time',
                            'arrival_time and departure_time should either '
                            'both be provided or both be left blank.  '
                            'It\'s OK to set them both to the same value.')
    # If the caller passed a valid departure time but didn't attempt to pass a
    # arrival time complain
    if (self.departure_secs != None and
        self.arrival_secs == None and arrival_time == None):
      problems.MissingValue('arrival_time',
                            'arrival_time and departure_time should either '
                            'both be provided or both be left blank.  '
                            'It\'s OK to set them both to the same value.')
 
    if shape_dist_traveled in (None, ""):
      self.shape_dist_traveled = None
    else:
      try:
        self.shape_dist_traveled = float(shape_dist_traveled)
      except ValueError:
        problems.InvalidValue('shape_dist_traveled', shape_dist_traveled)
 
    if stop_sequence is not None:
      self.stop_sequence = stop_sequence
 
  def GetFieldValuesTuple(self, trip_id):
    """Return a tuple that outputs a row of _FIELD_NAMES to be written to a
       GTFS file.
 
    Arguments:
        trip_id: The trip_id of the trip to which this StopTime corresponds.
                 It must be provided, as it is not stored in StopTime.
    """
    result = []
    for fn in self._FIELD_NAMES:
      if fn == 'trip_id':
        result.append(trip_id)
      else:
        # Since we'll be writting to an output file, we want empty values to be
        # outputted as an empty string
        result.append(getattr(self, fn) or '' )
    return tuple(result)
 
  def GetSqlValuesTuple(self, trip_id):
    """Return a tuple that outputs a row of _FIELD_NAMES to be written to a
       SQLite database.
 
    Arguments:
        trip_id: The trip_id of the trip to which this StopTime corresponds.
                 It must be provided, as it is not stored in StopTime.
    """
 
    result = []
    for fn in self._SQL_FIELD_NAMES:
      if fn == 'trip_id':
        result.append(trip_id)
      else:
        # Since we'll be writting to SQLite, we want empty values to be
        # outputted as NULL string (contrary to what happens in
        # GetFieldValuesTuple)
        result.append(getattr(self, fn))
    return tuple(result)
 
  def GetTimeSecs(self):
    """Return the first of arrival_secs and departure_secs that is not None.
    If both are None return None."""
    if self.arrival_secs != None:
      return self.arrival_secs
    elif self.departure_secs != None:
      return self.departure_secs
    else:
      return None
 
  def __getattr__(self, name):
    if name == 'stop_id':
      return self.stop.stop_id
    elif name == 'arrival_time':
      return (self.arrival_secs != None and
          util.FormatSecondsSinceMidnight(self.arrival_secs) or '')
    elif name == 'departure_time':
      return (self.departure_secs != None and
          util.FormatSecondsSinceMidnight(self.departure_secs) or '')
    elif name == 'shape_dist_traveled':
      return ''
    raise AttributeError(name)