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