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