Add html/pdf inconsistency fixes
[bus.git] / origin-src / transitfeed-1.2.6 / transitfeed / transfer.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
#!/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.
 
from gtfsobjectbase import GtfsObjectBase
import problems as problems_module
import util
 
class Transfer(GtfsObjectBase):
  """Represents a transfer in a schedule"""
  _REQUIRED_FIELD_NAMES = ['from_stop_id', 'to_stop_id', 'transfer_type']
  _FIELD_NAMES = _REQUIRED_FIELD_NAMES + ['min_transfer_time']
  _TABLE_NAME = 'transfers'
  _ID_COLUMNS = ['from_stop_id', 'to_stop_id']
 
  def __init__(self, schedule=None, from_stop_id=None, to_stop_id=None, transfer_type=None,
               min_transfer_time=None, field_dict=None):
    self._schedule = None
    if field_dict:
      self.__dict__.update(field_dict)
    else:
      self.from_stop_id = from_stop_id
      self.to_stop_id = to_stop_id
      self.transfer_type = transfer_type
      self.min_transfer_time = min_transfer_time
 
    if getattr(self, 'transfer_type', None) in ("", None):
      # Use the default, recommended transfer, if attribute is not set or blank
      self.transfer_type = 0
    else:
      try:
        self.transfer_type = util.NonNegIntStringToInt(self.transfer_type)
      except (TypeError, ValueError):
        pass
 
    if hasattr(self, 'min_transfer_time'):
      try:
        self.min_transfer_time = util.NonNegIntStringToInt(self.min_transfer_time)
      except (TypeError, ValueError):
        pass
    else:
      self.min_transfer_time = None
    if schedule is not None:
      # Note from Tom, Nov 25, 2009: Maybe calling __init__ with a schedule
      # should output a DeprecationWarning. A schedule factory probably won't
      # use it and other GenericGTFSObject subclasses don't support it.
      schedule.AddTransferObject(self)
 
  def ValidateFromStopIdIsPresent(self, problems):
    if util.IsEmpty(self.from_stop_id):
      problems.MissingValue('from_stop_id')
      return False
    return True
 
  def ValidateToStopIdIsPresent(self, problems):
    if util.IsEmpty(self.to_stop_id):
      problems.MissingValue('to_stop_id')
      return False
    return True
 
  def ValidateTransferType(self, problems):
    if not util.IsEmpty(self.transfer_type):
      if (not isinstance(self.transfer_type, int)) or \
          (self.transfer_type not in range(0, 4)):
        problems.InvalidValue('transfer_type', self.transfer_type)
        return False
    return True
 
  def ValidateMinimumTransferTime(self, problems):
    if not util.IsEmpty(self.min_transfer_time):
      if self.transfer_type != 2:
        problems.MinimumTransferTimeSetWithInvalidTransferType(
            self.transfer_type)
 
      # If min_transfer_time is negative, equal to or bigger than 24h, issue
      # an error. If smaller than 24h but bigger than 3h issue a warning.
      # These errors are not blocking, and should not prevent the transfer
      # from being added to the schedule.
      if (isinstance(self.min_transfer_time, int)):
        if self.min_transfer_time < 0:
          problems.InvalidValue('min_transfer_time', self.min_transfer_time,
                                reason="This field cannot contain a negative " \
                                       "value.")
        elif self.min_transfer_time >= 24*3600:
          problems.InvalidValue('min_transfer_time', self.min_transfer_time,
                                reason="The value is very large for a " \
                                       "transfer time and most likely " \
                                       "indicates an error.")
        elif self.min_transfer_time >= 3*3600:
          problems.InvalidValue('min_transfer_time', self.min_transfer_time,
                                type=problems_module.TYPE_WARNING,
                                reason="The value is large for a transfer " \
                                       "time and most likely indicates " \
                                       "an error.")
      else:
        # It has a value, but it is not an integer
        problems.InvalidValue('min_transfer_time', self.min_transfer_time,
                              reason="If present, this field should contain " \
                                "an integer value.")
        return False
    return True
 
  def GetTransferDistance(self):
    from_stop = self._schedule.stops[self.from_stop_id]
    to_stop = self._schedule.stops[self.to_stop_id]
    distance = util.ApproximateDistanceBetweenStops(from_stop, to_stop)
    return distance
 
  def ValidateFromStopIdIsValid(self, problems):
    if self.from_stop_id not in self._schedule.stops.keys():
      problems.InvalidValue('from_stop_id', self.from_stop_id)
      return False
    return True
 
  def ValidateToStopIdIsValid(self, problems):
    if self.to_stop_id not in self._schedule.stops.keys():
      problems.InvalidValue('to_stop_id', self.to_stop_id)
      return False
    return True
 
  def ValidateTransferDistance(self, problems):
    distance = self.GetTransferDistance()
 
    if distance > 10000:
      problems.TransferDistanceTooBig(self.from_stop_id,
                                      self.to_stop_id,
                                      distance)
    elif distance > 2000:
      problems.TransferDistanceTooBig(self.from_stop_id,
                                      self.to_stop_id,
                                      distance,
                                      type=problems_module.TYPE_WARNING)
 
  def ValidateTransferWalkingTime(self, problems):
    if util.IsEmpty(self.min_transfer_time):
      return
 
    if self.min_transfer_time < 0:
      # Error has already been reported, and it does not make sense
      # to calculate walking speed with negative times.
      return
 
    distance = self.GetTransferDistance()
    # If min_transfer_time + 120s isn't enough for someone walking very fast 
    # (2m/s) then issue a warning.
    # 
    # Stops that are close together (less than 240m appart) never trigger this
    # warning, regardless of min_transfer_time.
    FAST_WALKING_SPEED= 2 # 2m/s
    if self.min_transfer_time + 120 < distance / FAST_WALKING_SPEED:
      problems.TransferWalkingSpeedTooFast(from_stop_id=self.from_stop_id,
                                           to_stop_id=self.to_stop_id,
                                           transfer_time=self.min_transfer_time,
                                           distance=distance)
 
  def ValidateBeforeAdd(self, problems):
    result = True
    result = self.ValidateFromStopIdIsPresent(problems) and result
    result = self.ValidateToStopIdIsPresent(problems) and result
    result = self.ValidateTransferType(problems) and result
    result = self.ValidateMinimumTransferTime(problems) and result
    return result
 
  def ValidateAfterAdd(self, problems):
    valid_stop_ids = True
    valid_stop_ids = self.ValidateFromStopIdIsValid(problems) and valid_stop_ids
    valid_stop_ids = self.ValidateToStopIdIsValid(problems) and valid_stop_ids
    # We need both stop IDs to be valid to able to validate their distance and
    # the walking time between them
    if valid_stop_ids:
      self.ValidateTransferDistance(problems)
      self.ValidateTransferWalkingTime(problems)
 
  def Validate(self,
               problems=problems_module.default_problem_reporter):
    if self.ValidateBeforeAdd(problems) and self._schedule:
      self.ValidateAfterAdd(problems)
 
  def _ID(self):
    return tuple(self[i] for i in self._ID_COLUMNS)
 
  def AddToSchedule(self, schedule, problems):
    schedule.AddTransferObject(self, problems)