Upgrade origin-src to google transit feed 1.2.6
[bus.git] / origin-src / transitfeed-1.2.6 / transitfeed / transfer.py
blob:a/origin-src/transitfeed-1.2.6/transitfeed/transfer.py -> blob:b/origin-src/transitfeed-1.2.6/transitfeed/transfer.py
--- a/origin-src/transitfeed-1.2.6/transitfeed/transfer.py
+++ b/origin-src/transitfeed-1.2.6/transitfeed/transfer.py
@@ -1,1 +1,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)
+