From 3eda4ead2f042cf710c26c57e0469e4fa17b7338 Mon Sep 17 00:00:00 2001 From: wdiogu <88385256+wdiogu@users.noreply.github.com> Date: Thu, 7 Jul 2022 20:32:21 -0400 Subject: [PATCH] fixed -ve lfare issues (#191) --- ...ate_hypernetwork_from_schema_multiclass.py | 1415 ++++++++--------- 1 file changed, 688 insertions(+), 727 deletions(-) diff --git a/TMGToolbox/src/network_editing/transit_fare_hypernetworks/generate_hypernetwork_from_schema_multiclass.py b/TMGToolbox/src/network_editing/transit_fare_hypernetworks/generate_hypernetwork_from_schema_multiclass.py index af672600..c3ec621d 100644 --- a/TMGToolbox/src/network_editing/transit_fare_hypernetworks/generate_hypernetwork_from_schema_multiclass.py +++ b/TMGToolbox/src/network_editing/transit_fare_hypernetworks/generate_hypernetwork_from_schema_multiclass.py @@ -1,5 +1,5 @@ -#---LICENSE---------------------- -''' +# ---LICENSE---------------------- +""" Copyright 2015 Travel Modelling Group, Department of Civil Engineering, University of Toronto This file is part of the TMG Toolbox. @@ -16,9 +16,9 @@ You should have received a copy of the GNU General Public License along with the TMG Toolbox. If not, see . -''' -#---METADATA--------------------- -''' +""" +# ---METADATA--------------------- +""" Fare-Based Transit Network (FBTN) From Schema Authors: pkucirek @@ -28,9 +28,9 @@ [Description] -''' -#---VERSION HISTORY -''' +""" +# ---VERSION HISTORY +""" 0.0.1 Created on 2014-03-09 by pkucirek 1.0.0 Debugged and deployed by 2014-05-23 @@ -70,7 +70,7 @@ method allows for finer control of centroids, but cannot handle multiple operators at a station. -''' +""" from copy import copy from contextlib import contextmanager from itertools import combinations as get_combinations @@ -83,37 +83,40 @@ import inro.modeller as _m from inro.emme.core.exception import ModuleError -_MODELLER = _m.Modeller() #Instantiate Modeller once. -_util = _MODELLER.module('tmg.common.utilities') -_tmgTPB = _MODELLER.module('tmg.common.TMG_tool_page_builder') -_geolib = _MODELLER.module('tmg.common.geometry') -_editing = _MODELLER.module('tmg.common.network_editing') -_spindex = _MODELLER.module('tmg.common.spatial_index') +_MODELLER = _m.Modeller() # Instantiate Modeller once. +_util = _MODELLER.module("tmg.common.utilities") +_tmgTPB = _MODELLER.module("tmg.common.TMG_tool_page_builder") +_geolib = _MODELLER.module("tmg.common.geometry") +_editing = _MODELLER.module("tmg.common.network_editing") +_spindex = _MODELLER.module("tmg.common.spatial_index") Shapely2ESRI = _geolib.Shapely2ESRI GridIndex = _spindex.GridIndex TransitLineProxy = _editing.TransitLineProxy NullPointerException = _util.NullPointerException -EMME_VERSION = _util.getEmmeVersion(tuple) +EMME_VERSION = _util.getEmmeVersion(tuple) # import six library for python2 to python3 conversion -import six +import six + # initalize python3 types _util.initalizeModellerTypes(_m) from six.moves import xrange -########################################################################################################## +########################################################################################################## + class XmlValidationError(Exception): pass -class grid(): - ''' + +class grid: + """ Grid class to support tuple indexing (just for coding convenience). - + Upon construction, it copies the default value into each of its cells. - ''' - - def __init__(self, x_size, y_size, default= None): + """ + + def __init__(self, x_size, y_size, default=None): x_size, y_size = int(x_size), int(y_size) self._data = [] self.x = x_size @@ -123,85 +126,83 @@ def __init__(self, x_size, y_size, default= None): while i < total: self._data.append(copy(default)) i += 1 - + def __getitem__(self, key): - x,y = key + x, y = key x, y = int(x), int(y) index = x * self.y + y return self._data[index] - + def __setitem__(self, key, val): - x,y = key + x, y = key x, y = int(x), int(y) index = x * self.y + y self._data[index] = val -class NodeSpatialProxy(): + +class NodeSpatialProxy: def __init__(self, id, x, y): self.id = id self.x = x self.y = y self.zone = 0 - self.geometry = _geolib.Point(x,y) - + self.geometry = _geolib.Point(x, y) + def __str__(self): return str(self.id) - -#--- -#---MAIN MODELLER TOOL-------------------------------------------------------------------------------- + +# --- +# ---MAIN MODELLER TOOL-------------------------------------------------------------------------------- + class FBTNFromSchemaMulticlass(_m.Tool()): - - version = '0.0.1' + + version = "0.0.1" tool_run_msg = "" - number_of_tasks = 5 # For progress reporting, enter the integer number of tasks here - + number_of_tasks = 5 # For progress reporting, enter the integer number of tasks here + # Tool Input Parameters # Only those parameters neccessary for Modeller and/or XTMF to dock with # need to be placed here. Internal parameters (such as lists and dicts) # get intitialized during construction (__init__) - + XMLBaseSchemaFile = _m.Attribute(str) XMLFareRules = _m.Attribute(str) VirtualNodeDomain = _m.Attribute(int) - - xtmf_BaseScenarioNumber = _m.Attribute(int) # parameter used by XTMF only - BaseScenario = _m.Attribute(_m.InstanceType) # common variable or parameter + + xtmf_BaseScenarioNumber = _m.Attribute(int) # parameter used by XTMF only + BaseScenario = _m.Attribute(_m.InstanceType) # common variable or parameter NewScenarioNumber = _m.Attribute(int) NewScenarioTitle = _m.Attribute(str) - - TransferModeId = _m.Attribute(str) + + TransferModeId = _m.Attribute(str) SegmentFareAttributeId = _m.Attribute(str) LinkFareAttributeId = _m.Attribute(str) SegmentINodeAttributeId = _m.Attribute(str) StationConnectorFlag = _m.Attribute(bool) - - __ZONE_TYPES = ['node_selection', 'from_shapefile'] - __RULE_TYPES = ['initial_boarding', - 'transfer', - 'in_vehicle_distance', - 'zone_crossing'] - __BOOL_PARSER = {'TRUE': True, 'T': True, 'FALSE': False, 'F': False} - + + __ZONE_TYPES = ["node_selection", "from_shapefile"] + __RULE_TYPES = ["initial_boarding", "transfer", "in_vehicle_distance", "zone_crossing"] + __BOOL_PARSER = {"TRUE": True, "T": True, "FALSE": False, "F": False} + def __init__(self): - #---Init internal variables - self.TRACKER = _util.ProgressTracker(self.number_of_tasks) #init the ProgressTracker - - #---Set the defaults of parameters used by Modeller - self.BaseScenario = _MODELLER.scenario #Default is primary scenario + # ---Init internal variables + self.TRACKER = _util.ProgressTracker(self.number_of_tasks) # init the ProgressTracker + # ---Set the defaults of parameters used by Modeller + self.BaseScenario = _MODELLER.scenario # Default is primary scenario self.VirtualNodeDomain = 100000 self.NewScenarioTitle = "" - - #self.LinkFareAttributeId = "@lfare" - #self.SegmentFareAttributeId = "@sfare" - + # self.LinkFareAttributeId = "@lfare" + # self.SegmentFareAttributeId = "@sfare" self.StationConnectorFlag = True - + def page(self): - pb = _tmgTPB.TmgToolPageBuilder(self, title="FBTN From Schema v%s" %self.version, - description="Generates a hyper-network to support fare-based transit \ + pb = _tmgTPB.TmgToolPageBuilder( + self, + title="FBTN From Schema v%s" % self.version, + description="Generates a hyper-network to support fare-based transit \ assignment (FBTA), from an XML schema file. Links and segments with negative\ fare values will be reported to the Logbook for further inspection. \ For fare schema specification, \ @@ -209,8 +210,9 @@ def page(self):

Temporary storage requirements: one transit line extra \ attribute, one node extra attribute.\

Not Callable from Modeller. Please use XTMF", - branding_text="- TMG Toolbox") - + branding_text="- TMG Toolbox", + ) + '''if self.tool_run_msg != "": # to display messages in the page pb.tool_run_status(self.tool_run_msg_status) @@ -317,130 +319,128 @@ def page(self): }); }); """ % pb.tool_proxy_tag)''' - + return pb.render() - + ########################################################################################################## - + def run(self): self.tool_run_msg = "" self.TRACKER.reset() - - if not self.XMLSchemaFile: raise NullPointerException("Fare Schema file not specified") - if not self.VirtualNodeDomain: raise NullPointerException("Virtual Node Domain not specified") - if not self.LinkFareAttributeId: raise NullPointerException("Link fare attribute not specified") - if not self.SegmentFareAttributeId: raise NullPointerException("Segment fare attribute not specified") - + if not self.XMLSchemaFile: + raise NullPointerException("Fare Schema file not specified") + if not self.VirtualNodeDomain: + raise NullPointerException("Virtual Node Domain not specified") + if not self.LinkFareAttributeId: + raise NullPointerException("Link fare attribute not specified") + if not self.SegmentFareAttributeId: + raise NullPointerException("Segment fare attribute not specified") try: self._Execute() except Exception as e: - self.tool_run_msg = _m.PageBuilder.format_exception( - e, _traceback.format_exc()) + self.tool_run_msg = _m.PageBuilder.format_exception(e, _traceback.format_exc()) raise - self.tool_run_msg = _m.PageBuilder.format_info("Done.") - - - @_m.method(return_type= bool) + + @_m.method(return_type=bool) def check_415(self): - if EMME_VERSION >= (4,1,5): + if EMME_VERSION >= (4, 1, 5): return True return False - - def __call__(self, XMLBaseSchemaFile, xtmf_BaseScenarioNumber, NewScenarioNumber, - TransferModeId, VirtualNodeDomain, StationConnectorFlag, XMLFareRules): - - #---1 Set up scenario + + def __call__( + self, + XMLBaseSchemaFile, + xtmf_BaseScenarioNumber, + NewScenarioNumber, + TransferModeId, + VirtualNodeDomain, + StationConnectorFlag, + XMLFareRules, + ): + # ---1 Set up scenario self.BaseScenario = _MODELLER.emmebank.scenario(xtmf_BaseScenarioNumber) - if (self.BaseScenario is None): - raise Exception("Base scenario %s was not found!" %xtmf_BaseScenarioNumber) - XMLFareRulesJSON = json.loads(XMLFareRules.replace("\'","\"").replace("\\","/")) - self.XMLFareRulesFile = [] + if self.BaseScenario is None: + raise Exception("Base scenario %s was not found!" % xtmf_BaseScenarioNumber) + XMLFareRulesJSON = json.loads(XMLFareRules.replace("'", '"').replace("\\", "/")) + self.XMLFareRulesFile = [] self.SegmentFareAttributeId = [] self.LinkFareAttributeId = [] - FareClasses = XMLFareRulesJSON['FareClasses'] - for i in range(len(XMLFareRulesJSON['FareClasses'])): - self.XMLFareRulesFile.append(FareClasses[i]['SchemaFile']) - self.SegmentFareAttributeId.append(FareClasses[i]['SegmentFareAttribute']) - self.LinkFareAttributeId.append(FareClasses[i]['LinkFareAttribute']) + FareClasses = XMLFareRulesJSON["FareClasses"] + for i in range(len(XMLFareRulesJSON["FareClasses"])): + self.XMLFareRulesFile.append(FareClasses[i]["SchemaFile"]) + self.SegmentFareAttributeId.append(FareClasses[i]["SegmentFareAttribute"]) + self.LinkFareAttributeId.append(FareClasses[i]["LinkFareAttribute"]) for i in range(len(self.SegmentFareAttributeId)): if self.BaseScenario.extra_attribute(self.SegmentFareAttributeId[i]) is None: - att = self.BaseScenario.create_extra_attribute('TRANSIT_SEGMENT', - self.SegmentFareAttributeId[i]) + att = self.BaseScenario.create_extra_attribute("TRANSIT_SEGMENT", self.SegmentFareAttributeId[i]) att.description = "SEGMENT transit fare" - _m.logbook_write("Created segment fare attribute %s" %self.SegmentFareAttributeId[i]) - + _m.logbook_write("Created segment fare attribute %s" % self.SegmentFareAttributeId[i]) for i in range(len(self.LinkFareAttributeId)): if self.BaseScenario.extra_attribute(self.LinkFareAttributeId[i]) is None: - att = self.BaseScenario.create_extra_attribute('LINK', - self.LinkFareAttributeId[i]) + att = self.BaseScenario.create_extra_attribute("LINK", self.LinkFareAttributeId[i]) att.description = "LINK transit fare" - _m.logbook_write("Created link fare attribute %s" %self.LinkFareAttributeId[i]) - + _m.logbook_write("Created link fare attribute %s" % self.LinkFareAttributeId[i]) self.XMLBaseSchemaFile = XMLBaseSchemaFile self.NewScenarioNumber = NewScenarioNumber self.NewScenarioTitle = self.BaseScenario.title + " - FBTN" self.TransferModeId = TransferModeId self.VirtualNodeDomain = VirtualNodeDomain self.StationConnectorFlag = StationConnectorFlag - + try: self._Execute() except Exception as e: msg = str(e) + "\n" + _traceback.format_exc() raise Exception(msg) - - ########################################################################################################## - - + + ########################################################################################################## + def _Execute(self): - with _m.logbook_trace(name="{classname} v{version}".format(classname=(self.__class__.__name__), version=self.version), - attributes=self._GetAtts()): - + with _m.logbook_trace( + name="{classname} v{version}".format(classname=(self.__class__.__name__), version=self.version), + attributes=self._GetAtts(), + ): self._nextNodeId = self.VirtualNodeDomain - - rootBase = _ET.parse(self.XMLBaseSchemaFile).getroot() - - #Validate the XML Schema File + rootBase = _ET.parse(self.XMLBaseSchemaFile).getroot() + # Validate the XML Schema File nGroups, nZones, nStationGroups = self._ValidateBaseSchemaFile(rootBase) nRules = [] rootFare = [] for i in range(len(self.XMLFareRulesFile)): - rootFare.append(_ET.parse(self.XMLFareRulesFile[i]).getroot()) + rootFare.append(_ET.parse(self.XMLFareRulesFile[i]).getroot()) nRules.append(self._ValidateFareSchemaFile(rootFare[i])) nRules = sum(nRules) self.TRACKER.completeTask() - - #Load the line groups and zones - version = rootBase.find('version').attrib['number'] - _m.logbook_write("Loading Base Schema File version %s" %version) - print("Loading Base Schema File version %s" %version) - + # Load the line groups and zones + version = rootBase.find("version").attrib["number"] + _m.logbook_write("Loading Base Schema File version %s" % version) + print("Loading Base Schema File version %s" % version) self.TRACKER.startProcess(nGroups + nZones) - with _util.tempExtraAttributeMANAGER(self.BaseScenario, 'TRANSIT_LINE', description= "Line Group") as lineGroupAtt,\ - _util.tempExtraAttributeMANAGER(self.BaseScenario, 'NODE', description= "Fare Zone") as zoneAtt: - + with _util.tempExtraAttributeMANAGER( + self.BaseScenario, "TRANSIT_LINE", description="Line Group" + ) as lineGroupAtt, _util.tempExtraAttributeMANAGER( + self.BaseScenario, "NODE", description="Fare Zone" + ) as zoneAtt: + with _m.logbook_trace("Transit Line Groups"): - groupsElement = rootBase.find('groups') + groupsElement = rootBase.find("groups") groupIds2Int, int2groupIds = self._LoadGroups(groupsElement, lineGroupAtt.id) print("Loaded groups.") - - stationGroupsElement = rootBase.find('station_groups') + stationGroupsElement = rootBase.find("station_groups") if stationGroupsElement is not None: with _m.logbook_trace("Station Groups"): stationGroups = self._LoadStationGroups(stationGroupsElement) print("Loaded station groups") - - zonesElement = rootBase.find('zones') + zonesElement = rootBase.find("zones") if zonesElement is not None: with _m.logbook_trace("Fare Zones"): zoneId2Int, int2ZoneId, nodeProxies = self._LoadZones(zonesElement, zoneAtt.id) print("Loaded zones.") else: zoneId2Int, int2ZoneId, nodeProxies = {}, {}, {} - self.TRACKER.completeTask() #Complete the group/zone loading task - - #Load and prepare the network. + self.TRACKER.completeTask() # Complete the group/zone loading task + # Load and prepare the network. self.TRACKER.startProcess(2) network = self.BaseScenario.get_network() print("Loaded network.") @@ -448,695 +448,669 @@ def _Execute(self): self._PrepareNetwork(network, nodeProxies, lineGroupAtt.id) self.TRACKER.completeTask() print("Prepared base network.") - - #Transform the network + # Transform the network with _m.logbook_trace("Transforming hyper network"): transferGrid, zoneCrossingGrid = self._TransformNetwork(network, nGroups, nZones) - #print transferGrid[0,1] + # print transferGrid[0,1] if nStationGroups > 0: self._IndexStationConnectors(network, transferGrid, stationGroups, groupIds2Int) print("Hyper network generated.") - - #Apply fare rules to network. + # Apply fare rules to network. with _m.logbook_trace("Applying fare rules"): self.TRACKER.startProcess(nRules + 1) for i in range(len(self.XMLFareRulesFile)): - fareRulesElement = rootFare[i].find('fare_rules') - self._ApplyFareRules(network, fareRulesElement, transferGrid, zoneCrossingGrid, - groupIds2Int, zoneId2Int, self.SegmentFareAttributeId[i], self.LinkFareAttributeId[i]) + fareRulesElement = rootFare[i].find("fare_rules") + self._ApplyFareRules( + network, + fareRulesElement, + transferGrid, + zoneCrossingGrid, + groupIds2Int, + zoneId2Int, + self.SegmentFareAttributeId[i], + self.LinkFareAttributeId[i], + ) self._CheckForNegativeFares(network, self.SegmentFareAttributeId[i], self.LinkFareAttributeId[i]) self.TRACKER.completeTask() print("Applied fare rules to network.") - - #Publish the network + # Publish the network bank = _MODELLER.emmebank if bank.scenario(self.NewScenarioNumber) is not None: bank.delete_scenario(self.NewScenarioNumber) - newSc = bank.copy_scenario(self.BaseScenario.id, self.NewScenarioNumber, \ - copy_path_files=False, copy_strat_files=False) + newSc = bank.copy_scenario( + self.BaseScenario.id, self.NewScenarioNumber, copy_path_files=False, copy_strat_files=False + ) newSc.title = self.NewScenarioTitle - newSc.publish_network(network, resolve_attributes= True) - - _MODELLER.desktop.refresh_needed(True) #Tell the desktop app that a data refresh is required + newSc.publish_network(network, resolve_attributes=True) + + _MODELLER.desktop.refresh_needed(True) # Tell the desktop app that a data refresh is required + + ########################################################################################################## - ########################################################################################################## - def _GetAtts(self): - atts = { - "Scenario" : str(self.BaseScenario), - "Version": self.version, - "self": self.__MODELLER_NAMESPACE__} - + atts = {"Scenario": str(self.BaseScenario), "Version": self.version, "self": self.__MODELLER_NAMESPACE__} return atts - - #--- - #---SCHEMA LOADING----------------------------------------------------------------------------------- - + + # --- + # ---SCHEMA LOADING----------------------------------------------------------------------------------- + def _ValidateBaseSchemaFile(self, root): - - #Check the top-level of the file - versionElem = root.find('version') + # Check the top-level of the file + versionElem = root.find("version") if versionElem is None: raise XmlValidationError("Base schema must specify a 'version' element.") - - groupsElement = root.find('groups') + groupsElement = root.find("groups") if groupsElement is None: raise XmlValidationError("Base schema must specify a 'groups' element.") - - zonesElement = root.find('zones') - - - #Validate version + zonesElement = root.find("zones") + # Validate version try: - version = versionElem.attrib['number'] + version = versionElem.attrib["number"] except KeyError: raise XmlValidationError("Version element must specify a 'number' attribute.") - - #Validate groups - groupElements = groupsElement.findall('group') + # Validate groups + groupElements = groupsElement.findall("group") self.validGroupIds = set() if len(groupElements) == 0: raise XmlValidationError("Scehma must specify at least one group elements") for i, groupElement in enumerate(groupElements): - if not 'id' in groupElement.attrib: - raise XmlValidationError("Group element #%s must specify an 'id' attribute" %i) - id = groupElement.attrib['id'] + if not "id" in groupElement.attrib: + raise XmlValidationError("Group element #%s must specify an 'id' attribute" % i) + id = groupElement.attrib["id"] if id in self.validGroupIds: - raise XmlValidationError("Group id '%s' found more than once. Each id must be unique." %id) + raise XmlValidationError("Group id '%s' found more than once. Each id must be unique." % id) self.validGroupIds.add(id) - - selectionElements = groupElement.findall('selection') + selectionElements = groupElement.findall("selection") if len(selectionElements) == 0: - raise XmlValidationError("Group element '%s' does not specify any 'selection' sub-elements" %id) - - #Validate zones, if required + raise XmlValidationError("Group element '%s' does not specify any 'selection' sub-elements" % id) + + # Validate zones, if required self.validZoneIds = set() if zonesElement is not None: - shapeFileElements = zonesElement.findall('shapefile') - zoneElements = zonesElement.findall('zone') - + shapeFileElements = zonesElement.findall("shapefile") + zoneElements = zonesElement.findall("zone") shapeFileIds = set() for i, shapefileElement in enumerate(shapeFileElements): - if not 'id' in shapefileElement.attrib: - raise XmlValidationError("Shapefile #%s element must specify an 'id' attribute" %i) - - id = shapefileElement.attrib['id'] + if not "id" in shapefileElement.attrib: + raise XmlValidationError("Shapefile #%s element must specify an 'id' attribute" % i) + id = shapefileElement.attrib["id"] if id in shapeFileIds: - raise XmlValidationError("Shapefile id '%' found more than once. Each id must be unique" %id) + raise XmlValidationError("Shapefile id '%' found more than once. Each id must be unique" % id) shapeFileIds.add(id) - - if not 'path' in shapefileElement.attrib: - raise XmlValidationError("Sahpefile '%s' must specify a 'path' attribute" %id) - p = shapefileElement.attrib['path'] - p = self._GetAbsoluteFilepath(p) #Joins the path if it is relative. - + if not "path" in shapefileElement.attrib: + raise XmlValidationError("Sahpefile '%s' must specify a 'path' attribute" % id) + p = shapefileElement.attrib["path"] + p = self._GetAbsoluteFilepath(p) # Joins the path if it is relative. if not path.exists(p): - raise XmlValidationError("File not found for id '%s' at %s" %(id, p)) - + raise XmlValidationError("File not found for id '%s' at %s" % (id, p)) for i, zoneElement in enumerate(zoneElements): - if not 'id' in zoneElement.attrib: - raise XmlValidationError("Zone element #%s must specify an 'id' attribute" %i) - id = zoneElement.attrib['id'] + if not "id" in zoneElement.attrib: + raise XmlValidationError("Zone element #%s must specify an 'id' attribute" % i) + id = zoneElement.attrib["id"] if id in self.validZoneIds: - raise XmlValidationError("Zone id '%s' found more than once. Each id must be unique" %id) + raise XmlValidationError("Zone id '%s' found more than once. Each id must be unique" % id) self.validZoneIds.add(id) - - if not 'type' in zoneElement.attrib: - raise XmlValidationError("Zone '%s' must specify a 'type' attribute" %id) - zoneType = zoneElement.attrib['type'] + if not "type" in zoneElement.attrib: + raise XmlValidationError("Zone '%s' must specify a 'type' attribute" % id) + zoneType = zoneElement.attrib["type"] if not zoneType in self.__ZONE_TYPES: - raise XmlValidationError("Zone type '%s' for zone '%s' is not recognized." %(zoneType, id)) - - if zoneType == 'node_selection': - if len(zoneElement.findall('node_selector')) == 0: - raise XmlValidationError("Zone type 'node_selection' for zone '%s' must specify at least one 'node_selector' element." %id) - elif zoneType == 'from_shapefile': - childElement = zoneElement.find('from_shapefile') + raise XmlValidationError("Zone type '%s' for zone '%s' is not recognized." % (zoneType, id)) + if zoneType == "node_selection": + if len(zoneElement.findall("node_selector")) == 0: + raise XmlValidationError( + "Zone type 'node_selection' for zone '%s' must specify at least one 'node_selector' element." + % id + ) + elif zoneType == "from_shapefile": + childElement = zoneElement.find("from_shapefile") if childElement is None: - raise XmlValidationError("Zone type 'from_shapefile' for zone '%s' must specify exactly one 'from_shapefile' element." %id) - - if not 'id' in childElement.attrib: + raise XmlValidationError( + "Zone type 'from_shapefile' for zone '%s' must specify exactly one 'from_shapefile' element." + % id + ) + if not "id" in childElement.attrib: raise XmlValidationError("from_shapefile element must specify an 'id' attribute.") - if not 'FID' in childElement.attrib: + if not "FID" in childElement.attrib: raise XmlValidationError("from_shapefile element must specify a 'FID' attribute.") - - sid = childElement.attrib['id'] + sid = childElement.attrib["id"] if not sid in shapeFileIds: - raise XmlValidationError("Could not find a shapefile with the id '%s' for zone '%s'." %(sid, id)) - + raise XmlValidationError( + "Could not find a shapefile with the id '%s' for zone '%s'." % (sid, id) + ) try: - FID = int(childElement.attrib['FID']) - if FID < 0: raise Exception() + FID = int(childElement.attrib["FID"]) + if FID < 0: + raise Exception() except: raise XmlValidationError("FID attribute must be a positive integer.") else: zoneElements = [] - + nStationGroups = 0 - stationGroupsElement = root.find('station_groups') + stationGroupsElement = root.find("station_groups") if stationGroupsElement is not None: - stationGroupElements = stationGroupsElement.findall('station_group') - + stationGroupElements = stationGroupsElement.findall("station_group") + for element in stationGroupElements: - forGroup = element.attrib['for'] + forGroup = element.attrib["for"] if not forGroup in self.validGroupIds: - raise XmlValidationError("Could not find a group '%s' for to associate with a station group" %forGroup) + raise XmlValidationError( + "Could not find a group '%s' for to associate with a station group" % forGroup + ) nStationGroups += 1 - def checkGroupId(group, name): if not group in self.validGroupIds: - raise XmlValidationError("Could not find a group with id '%s' for element '%s'" %(group, name)) - + raise XmlValidationError("Could not find a group with id '%s' for element '%s'" % (group, name)) + def checkZoneId(zone, name): if not zone in self.validZoneIds: - raise XmlValidationError("Could not find a zone with id '%s' for element '%s'" %(zone, name)) - + raise XmlValidationError("Could not find a zone with id '%s' for element '%s'" % (zone, name)) + def checkIsBool(val, name): - if not val.upper() in ['TRUE', 'T', 'FALSE', 'F']: - raise XmlValidationError("Value '%s' for element '%s' must be True or False." %(val, name)) - + if not val.upper() in ["TRUE", "T", "FALSE", "F"]: + raise XmlValidationError("Value '%s' for element '%s' must be True or False." % (val, name)) + return len(groupElements), len(zoneElements), nStationGroups def _ValidateFareSchemaFile(self, root): - - - fareRulesElement = root.find('fare_rules') + fareRulesElement = root.find("fare_rules") if fareRulesElement is None: raise XmlValidationError("Fare schema must specify a 'fare_rules' element.") + fareElements = fareRulesElement.findall("fare") - - fareElements = fareRulesElement.findall('fare') - def checkGroupId(group, name): if not group in self.validGroupIds: - raise XmlValidationError("Could not find a group with id '%s' for element '%s'" %(group, name)) - + raise XmlValidationError("Could not find a group with id '%s' for element '%s'" % (group, name)) + def checkZoneId(zone, name): if not zone in self.validZoneIds: - raise XmlValidationError("Could not find a zone with id '%s' for element '%s'" %(zone, name)) - + raise XmlValidationError("Could not find a zone with id '%s' for element '%s'" % (zone, name)) + def checkIsBool(val, name): - if not val.upper() in ['TRUE', 'T', 'FALSE', 'F']: - raise XmlValidationError("Value '%s' for element '%s' must be True or False." %(val, name)) - + if not val.upper() in ["TRUE", "T", "FALSE", "F"]: + raise XmlValidationError("Value '%s' for element '%s' must be True or False." % (val, name)) + for i, fareElement in enumerate(fareElements): - if not 'cost' in fareElement.attrib: - raise XmlValidationError("Fare element #%s must specify a 'cost' attribute" %i) - if not 'type' in fareElement.attrib: - raise XmlValidationError("Fare element #%s must specify a 'type' attribute" %i) - + if not "cost" in fareElement.attrib: + raise XmlValidationError("Fare element #%s must specify a 'cost' attribute" % i) + if not "type" in fareElement.attrib: + raise XmlValidationError("Fare element #%s must specify a 'type' attribute" % i) + try: - cost = float(fareElement.attrib['cost']) + cost = float(fareElement.attrib["cost"]) except ValueError: - raise XmlValidationError("Fare element #%s attribute 'cost' must be valid decimal number." %i) - - ruleType = fareElement.attrib['type'] - if ruleType == 'initial_boarding': - requiredChildren = {'group': checkGroupId} - optionalChildren = {'in_zone': checkZoneId, - 'include_all_groups': checkIsBool} - elif ruleType == 'transfer': - requiredChildren = {'from_group': checkGroupId, - 'to_group': checkGroupId} - optionalChildren = {'bidirectional': checkIsBool} - elif ruleType == 'zone_crossing': - requiredChildren = {'group': checkGroupId, - 'from_zone': checkZoneId, - 'to_zone': checkZoneId} - optionalChildren = {'bidirectional': checkIsBool} - elif ruleType == 'distance_in_vehicle': - requiredChildren = {'group': checkGroupId} + raise XmlValidationError("Fare element #%s attribute 'cost' must be valid decimal number." % i) + + ruleType = fareElement.attrib["type"] + if ruleType == "initial_boarding": + requiredChildren = {"group": checkGroupId} + optionalChildren = {"in_zone": checkZoneId, "include_all_groups": checkIsBool} + elif ruleType == "transfer": + requiredChildren = {"from_group": checkGroupId, "to_group": checkGroupId} + optionalChildren = {"bidirectional": checkIsBool} + elif ruleType == "zone_crossing": + requiredChildren = {"group": checkGroupId, "from_zone": checkZoneId, "to_zone": checkZoneId} + optionalChildren = {"bidirectional": checkIsBool} + elif ruleType == "distance_in_vehicle": + requiredChildren = {"group": checkGroupId} optionalChildren = {} else: - raise XmlValidationError("Fare rule type '%s' not recognized." %ruleType) - - #Check required children + raise XmlValidationError("Fare rule type '%s' not recognized." % ruleType) + + # Check required children for name, checkFunc in six.iteritems(requiredChildren): child = fareElement.find(name) if child is None: - raise XmlValidationError("Fare element #%s of type '%s' must specify a '%s' element" %(i, ruleType, name)) - + raise XmlValidationError( + "Fare element #%s of type '%s' must specify a '%s' element" % (i, ruleType, name) + ) + text = child.text checkFunc(text, name) - - #Check optional children + + # Check optional children for name, checkFunc in six.iteritems(optionalChildren): child = fareElement.find(name) - if child is None: continue - + if child is None: + continue + text = child.text checkFunc(text, name) - + return len(fareElements) - + def _LoadGroups(self, groupsElement, lineGroupAttId): groupIds2Int = {} - int2groupIds ={} - - tool = _MODELLER.tool('inro.emme.network_calculation.network_calculator') + int2groupIds = {} + + tool = _MODELLER.tool("inro.emme.network_calculation.network_calculator") + def getSpec(number, selection): return { "result": lineGroupAttId, "expression": str(number), "aggregation": None, - "selections": { - "transit_line": selection - }, - "type": "NETWORK_CALCULATION" + "selections": {"transit_line": selection}, + "type": "NETWORK_CALCULATION", } - - for i, groupElement in enumerate(groupsElement.findall('group')): + + for i, groupElement in enumerate(groupsElement.findall("group")): groupNumber = i + 1 - - id = groupElement.attrib['id'] + + id = groupElement.attrib["id"] groupIds2Int[id] = groupNumber int2groupIds[groupNumber] = id - - for selectionElement in groupElement.findall('selection'): + + for selectionElement in groupElement.findall("selection"): selector = selectionElement.text spec = getSpec(groupNumber, selector) try: - tool(spec, scenario= self.BaseScenario) + tool(spec, scenario=self.BaseScenario) except ModuleError: - msg = "Emme runtime error processing line group '%s'." %id + msg = "Emme runtime error processing line group '%s'." % id _m.logbook_write(msg) print(msg) - - msg = "Loaded group %s: %s" %(groupNumber, id) + + msg = "Loaded group %s: %s" % (groupNumber, id) print(msg) _m.logbook_write(msg) - + self.TRACKER.completeSubtask() - + return groupIds2Int, int2groupIds - + def _LoadStationGroups(self, stationGroupsElement): - tool = _MODELLER.tool('inro.emme.network_calculation.network_calculator') - + tool = _MODELLER.tool("inro.emme.network_calculation.network_calculator") + stationGroups, ids = {}, [] - with _util.tempExtraAttributeMANAGER(self.BaseScenario, 'NODE', returnId=True) as attr: - - for i, stationGroupElement in enumerate(stationGroupsElement.findall('station_group')): - forGroup = stationGroupElement.attrib['for'] - selector = stationGroupElement.attrib['selection'] - + with _util.tempExtraAttributeMANAGER(self.BaseScenario, "NODE", returnId=True) as attr: + + for i, stationGroupElement in enumerate(stationGroupsElement.findall("station_group")): + forGroup = stationGroupElement.attrib["for"] + selector = stationGroupElement.attrib["selection"] + spec = { - "result": attr, - "expression": str(i + 1), #Plus one since the attribute is initialized to 0 - "aggregation": None, - "selections": { - "node": selector - }, - "type": "NETWORK_CALCULATION" - } - tool(spec, scenario= self.BaseScenario) + "result": attr, + "expression": str(i + 1), # Plus one since the attribute is initialized to 0 + "aggregation": None, + "selections": {"node": selector}, + "type": "NETWORK_CALCULATION", + } + tool(spec, scenario=self.BaseScenario) stationGroups[forGroup] = set() ids.append(forGroup) - - indices, table = self.BaseScenario.get_attribute_values('NODE', [attr]) + + indices, table = self.BaseScenario.get_attribute_values("NODE", [attr]) for nodeNumber, index in six.iteritems(indices): value = int(table[index]) - if value == 0: continue + if value == 0: + continue stationGroups[ids[value - 1]].add(nodeNumber) - - return stationGroups - - + + return stationGroups + def _LoadZones(self, zonesElement, zoneAttributeId): - ''' + """ Loads node zone numbers. This is a convoluted process in order to allow users to apply zones by BOTH selectors AND geometry. The first method applies changes directly to the base scenario, which the second requires - knowing the node coordindates to work. - + knowing the node coordindates to work. + Much of this method (and associated sub-methods) is BLACK MAGIC - ''' + """ zoneId2Int = {} int2ZoneId = {} - - tool = _MODELLER.tool('inro.emme.network_calculation.network_calculator') - + + tool = _MODELLER.tool("inro.emme.network_calculation.network_calculator") + shapefiles = self._LoadShapefiles(zonesElement) spatialIndex, nodes = self._IndexNodeGeometries() - + try: - for number, zoneElement in enumerate(zonesElement.findall('zone')): - id = zoneElement.attrib['id'] - typ = zoneElement.attrib['type'] - + for number, zoneElement in enumerate(zonesElement.findall("zone")): + id = zoneElement.attrib["id"] + typ = zoneElement.attrib["type"] + number += 1 - + zoneId2Int[id] = number int2ZoneId[number] = id - - if typ == 'node_selection': + + if typ == "node_selection": self._LoadZoneFromSelection(zoneElement, zoneAttributeId, tool, number, nodes) - elif typ == 'from_shapefile': + elif typ == "from_shapefile": self._LoadZoneFromGeometry(zoneElement, spatialIndex, shapefiles, number, nodes) - - msg = "Loaded zone %s: %s" %(number, id) + + msg = "Loaded zone %s: %s" % (number, id) _m.logbook_write(msg) print(msg) - + self.TRACKER.completeSubtask() - finally: #Close the shapefile readers + finally: # Close the shapefile readers for reader in six.itervalues(shapefiles): reader.close() - + return zoneId2Int, int2ZoneId, nodes - - + def _LoadShapefiles(self, zonesElement): shapefiles = {} try: - for shapefileElement in zonesElement.findall('shapefile'): - id = shapefileElement.attrib['id'] - pth = shapefileElement.attrib['path'] - pth = self._GetAbsoluteFilepath(pth) #Join the path if it is relative - - reader = Shapely2ESRI(pth, 'r') + for shapefileElement in zonesElement.findall("shapefile"): + id = shapefileElement.attrib["id"] + pth = shapefileElement.attrib["path"] + pth = self._GetAbsoluteFilepath(pth) # Join the path if it is relative + + reader = Shapely2ESRI(pth, "r") reader.open() - if reader.getGeometryType() != 'POLYGON': - raise IOError("Shapefile %s does not contain POLYGONS" %pth) - + if reader.getGeometryType() != "POLYGON": + raise IOError("Shapefile %s does not contain POLYGONS" % pth) + shapefiles[id] = reader except: for reader in six.itervalues(shapefiles): reader.close() raise - + return shapefiles - - - + def _IndexNodeGeometries(self): - ''' + """ Uses get_attribute_values() (Scenario function) to create proxy objects for Emme nodes. - + This is done to allow node locations to be loaded IN THE ORDER SPECIFIED BY THE FILE, - regardless of whether those nodes are specified by a selector or by geometry. - ''' - indices, xtable, ytable = self.BaseScenario.get_attribute_values('NODE', ['x', 'y']) - + regardless of whether those nodes are specified by a selector or by geometry. + """ + indices, xtable, ytable = self.BaseScenario.get_attribute_values("NODE", ["x", "y"]) + extents = min(xtable), min(ytable), max(xtable), max(ytable) - - spatialIndex = GridIndex(extents, marginSize= 1.0) + + spatialIndex = GridIndex(extents, marginSize=1.0) proxies = {} - + for nodeNumber, index in six.iteritems(indices): x = xtable[index] y = ytable[index] - - #Using a proxy class defined in THIS file, because we don't yet - #have the full network loaded. + + # Using a proxy class defined in THIS file, because we don't yet + # have the full network loaded. nodeProxy = NodeSpatialProxy(nodeNumber, x, y) spatialIndex.insertPoint(nodeProxy) proxies[nodeNumber] = nodeProxy - + return spatialIndex, proxies - + def _LoadZoneFromSelection(self, zoneElement, zoneAttributeId, tool, number, nodes): - id = zoneElement.attrib['id'] - - for selectionElement in zoneElement.findall('node_selector'): + id = zoneElement.attrib["id"] + + for selectionElement in zoneElement.findall("node_selector"): spec = { - "result": zoneAttributeId, - "expression": str(number), - "aggregation": None, - "selections": { - "node": selectionElement.text - }, - "type": "NETWORK_CALCULATION" - } - + "result": zoneAttributeId, + "expression": str(number), + "aggregation": None, + "selections": {"node": selectionElement.text}, + "type": "NETWORK_CALCULATION", + } + try: - tool(spec, scenario= self.BaseScenario) + tool(spec, scenario=self.BaseScenario) except ModuleError as me: - raise IOError("Error loading zone '%s': %s" %(id, me)) - - #Update the list of proxy nodes with the network's newly-loaded zones attribute - indices, table = self.BaseScenario.get_attribute_values('NODE', [zoneAttributeId]) + raise IOError("Error loading zone '%s': %s" % (id, me)) + + # Update the list of proxy nodes with the network's newly-loaded zones attribute + indices, table = self.BaseScenario.get_attribute_values("NODE", [zoneAttributeId]) for number, index in six.iteritems(indices): nodes[number].zone = table[index] - + def _LoadZoneFromGeometry(self, zoneElement, spatialIndex, shapefiles, number, nodes): - id = zoneElement.attrib['id'] - - for fromShapefileElement in zoneElement.findall('from_shapefile'): - sid = fromShapefileElement.attrib['id'] - fid = int(fromShapefileElement.attrib['FID']) - + id = zoneElement.attrib["id"] + + for fromShapefileElement in zoneElement.findall("from_shapefile"): + sid = fromShapefileElement.attrib["id"] + fid = int(fromShapefileElement.attrib["FID"]) + reader = shapefiles[sid] polygon = reader.readFrom(fid) - + nodesToCheck = spatialIndex.queryPolygon(polygon) for proxy in nodesToCheck: point = proxy.geometry - + if polygon.intersects(point): proxy.zone = number - + def _GetAbsoluteFilepath(self, otherPath): - ''' + """ For the shapefile path, this function checks if it is a relative path or not. If it is a relative path, it returns a valid absolute path based on the location of the XML Schema File. - ''' + """ if path.isabs(otherPath): return otherPath - + return path.join(path.dirname(self.XMLBaseSchemaFile), otherPath) - - - #--- - #---HYPER NETWORK GENERATION-------------------------------------------------------------------------- - + + # --- + # ---HYPER NETWORK GENERATION-------------------------------------------------------------------------- + def _PrepareNetwork(self, network, nodeProxies, lineGroupAttId): - ''' + """ Prepares network attributes for transformation - ''' - - network.create_attribute('TRANSIT_LINE', 'group', 0) - network.create_attribute('NODE', 'passing_groups', None) #Set of groups passing through but not stopping at the node - network.create_attribute('NODE', 'stopping_groups', None) #Set of groups stopping at the node - network.create_attribute('NODE', 'fare_zone', 0) #The number of the fare zone - network.create_attribute('NODE', 'to_hyper_node', None) #Dictionary to get from the node to its hyper nodes - network.create_attribute('LINK', 'role', 0) #Link topological role - network.create_attribute('NODE', 'role', 0) #Node topological role - - #Initialize node attributes (incl. copying node zone) - #Also, copy the zones loaded into the proxies + """ + + network.create_attribute("TRANSIT_LINE", "group", 0) + network.create_attribute( + "NODE", "passing_groups", None + ) # Set of groups passing through but not stopping at the node + network.create_attribute("NODE", "stopping_groups", None) # Set of groups stopping at the node + network.create_attribute("NODE", "fare_zone", 0) # The number of the fare zone + network.create_attribute("NODE", "to_hyper_node", None) # Dictionary to get from the node to its hyper nodes + network.create_attribute("LINK", "role", 0) # Link topological role + network.create_attribute("NODE", "role", 0) # Node topological role + + # Initialize node attributes (incl. copying node zone) + # Also, copy the zones loaded into the proxies for node in network.regular_nodes(): node.passing_groups = set() node.stopping_groups = set() node.to_hyper_node = {} - + if node.number in nodeProxies: proxy = nodeProxies[node.number] node.fare_zone = proxy.zone - - #Determine stops & assign operators to nodes + + # Determine stops & assign operators to nodes for line in network.transit_lines(): group = int(line[lineGroupAttId]) line.group = group - + for segment in line.segments(True): iNode = segment.i_node if segment.allow_boardings or segment.allow_alightings: iNode.stopping_groups.add(group) - if group in iNode.passing_groups: iNode.passing_groups.remove(group) + if group in iNode.passing_groups: + iNode.passing_groups.remove(group) else: - if not group in iNode.stopping_groups: iNode.passing_groups.add(group) - - #Put this into a function to be able to break from deep loops using return + if not group in iNode.stopping_groups: + iNode.passing_groups.add(group) + + # Put this into a function to be able to break from deep loops using return def applyNodeRole(node): if not node.stopping_groups and not node.passing_groups: if node.is_centroid == False: - node.role = 1 # Surface node without transit - return #Skip nodes without an incident transit segment - + node.role = 1 # Surface node without transit + return # Skip nodes without an incident transit segment + for link in node.outgoing_links(): - if link.i_node.is_centroid or link.j_node.is_centroid: continue + if link.i_node.is_centroid or link.j_node.is_centroid: + continue for mode in link.modes: - if mode.type == 'AUTO': - node.role = 1 #Surface node + if mode.type == "AUTO": + node.role = 1 # Surface node return for link in node.incoming_links(): - if link.i_node.is_centroid or link.j_node.is_centroid: continue + if link.i_node.is_centroid or link.j_node.is_centroid: + continue for mode in link.modes: - if mode.type == 'AUTO': - node.role = 1 #Surface node + if mode.type == "AUTO": + node.role = 1 # Surface node return - node.role = 2 #Station node is a transit stop, but does NOT connect to any auto links + node.role = 2 # Station node is a transit stop, but does NOT connect to any auto links - #Determine node role. This needs to be done AFTER stops have been identified - for node in network.regular_nodes(): applyNodeRole(node) - - #Determine link role. Needs to happen after node role's have been identified + # Determine node role. This needs to be done AFTER stops have been identified + for node in network.regular_nodes(): + applyNodeRole(node) + + # Determine link role. Needs to happen after node role's have been identified for link in network.links(): i, j = link.i_node, link.j_node - if i.is_centroid or j.is_centroid: continue #Link is a centroid connector - + if i.is_centroid or j.is_centroid: + continue # Link is a centroid connector + permitsWalk = False for mode in link.modes: - if mode.type == 'AUX_TRANSIT': + if mode.type == "AUX_TRANSIT": permitsWalk = True break - - if i.role == 1 and j.role == 2 and permitsWalk: link.role = 1 #Station connector (access) - elif i.role == 2 and j.role == 1 and permitsWalk: link.role = 1 #Station connector (egress) - elif i.role == 2 and j.role == 2 and permitsWalk: link.role = 2 #Station transfer + + if i.role == 1 and j.role == 2 and permitsWalk: + link.role = 1 # Station connector (access) + elif i.role == 2 and j.role == 1 and permitsWalk: + link.role = 1 # Station connector (egress) + elif i.role == 2 and j.role == 2 and permitsWalk: + link.role = 2 # Station transfer def _TransformNetwork(self, network, numberOfGroups, numberOfZones): - - totalNodes0 = network.element_totals['regular_nodes'] - totalLinks0 = network.element_totals['links'] - + + totalNodes0 = network.element_totals["regular_nodes"] + totalLinks0 = network.element_totals["links"] + baseSurfaceNodes = [] baseStationNodes = [] for node in network.regular_nodes(): - if node.role == 1: baseSurfaceNodes.append(node) - elif node.role == 2: baseStationNodes.append(node) - + if node.role == 1: + baseSurfaceNodes.append(node) + elif node.role == 2: + baseStationNodes.append(node) + transferGrid = grid(numberOfGroups + 1, numberOfGroups + 1, set()) zoneCrossingGrid = grid(numberOfZones + 1, numberOfZones + 1, set()) - + transferMode = network.mode(self.TransferModeId) - + lineIds = [line.id for line in network.transit_lines()] - + nTasks = 2 * (len(baseSurfaceNodes) + len(baseStationNodes)) + len(lineIds) self.TRACKER.startProcess(nTasks) for i, node in enumerate(baseSurfaceNodes): self._TransformSurfaceNode(node, transferGrid, transferMode) self.TRACKER.completeSubtask() - + print("Processed surface nodes") - totalNodes1 = network.element_totals['regular_nodes'] - totalLinks1 = network.element_totals['links'] - _m.logbook_write("Created %s virtual road nodes." %(totalNodes1 - totalNodes0)) - _m.logbook_write("Created %s access links to virtual road nodes" %(totalLinks1 - totalLinks0)) - + totalNodes1 = network.element_totals["regular_nodes"] + totalLinks1 = network.element_totals["links"] + _m.logbook_write("Created %s virtual road nodes." % (totalNodes1 - totalNodes0)) + _m.logbook_write("Created %s access links to virtual road nodes" % (totalLinks1 - totalLinks0)) + for i, node in enumerate(baseStationNodes): self._TransformStationNode(node, transferGrid, transferMode) self.TRACKER.completeSubtask() - + print("Processed station nodes") - totalNodes2 = network.element_totals['regular_nodes'] - totalLinks2 = network.element_totals['links'] - _m.logbook_write("Created %s virtual transit nodes." %(totalNodes2 - totalNodes1)) - _m.logbook_write("Created %s access links to virtual transit nodes" %(totalLinks2 - totalLinks1)) - + totalNodes2 = network.element_totals["regular_nodes"] + totalLinks2 = network.element_totals["links"] + _m.logbook_write("Created %s virtual transit nodes." % (totalNodes2 - totalNodes1)) + _m.logbook_write("Created %s access links to virtual transit nodes" % (totalLinks2 - totalLinks1)) for node in baseSurfaceNodes: self._ConnectSurfaceOrStationNode(node, transferGrid) self.TRACKER.completeSubtask() for node in baseStationNodes: self._ConnectSurfaceOrStationNode(node, transferGrid) self.TRACKER.completeSubtask() - print("Connected surface and station nodes") - totalLinks3 = network.element_totals['links'] - _m.logbook_write("Created %s road-to-transit connector links" %(totalLinks3 - totalLinks2)) - + totalLinks3 = network.element_totals["links"] + _m.logbook_write("Created %s road-to-transit connector links" % (totalLinks3 - totalLinks2)) if self.SegmentINodeAttributeId is not None: + def saveFunction(segment, iNodeId): segment[self.SegmentINodeAttributeId] = iNodeId + else: + def saveFunction(segment, iNodeId): pass - + for lineId in lineIds: self._ProcessTransitLine(lineId, network, zoneCrossingGrid, saveFunction) self.TRACKER.completeSubtask() - print("Processed transit lines") - totalLinks4 = network.element_totals['links'] - _m.logbook_write("Created %s in-line virtual links" %(totalLinks4 - totalLinks3)) - + totalLinks4 = network.element_totals["links"] + _m.logbook_write("Created %s in-line virtual links" % (totalLinks4 - totalLinks3)) self.TRACKER.completeTask() - return transferGrid, zoneCrossingGrid - + def _GetNewNodeNumber(self, network, baseNodeNumber): testNode = network.node(self._nextNodeId) while testNode is not None: self._nextNodeId += 1 testNode = network.node(self._nextNodeId) return self._nextNodeId - + def _TransformSurfaceNode(self, baseNode, transferGrid, transferMode): network = baseNode.network - - ''' + """ NOTE TO SELF: When copying attributes to new nodes, REMEMBER that the the "special" attributes created in _PrepareNetwork(...) get copied as well! This includes pointers to objects - specifically Dictionaries - so UNDER NO CIRCUMSTANCES modify a copy's 'to_hyper_network' attribute since that modifies the base's dictionary as well. - ''' - + """ createdNodes = [] linksCreated = 0 - - #Create the virtual nodes for stops + # Create the virtual nodes for stops for groupNumber in baseNode.stopping_groups: newNode = network.create_regular_node(self._GetNewNodeNumber(network, baseNode.number)) - - #Copy the node attributes, including x, y coordinates - for att in network.attributes('NODE'): + # Copy the node attributes, including x, y coordinates + for att in network.attributes("NODE"): newNode[att] = baseNode[att] - #newNode.label = "RS%s" %int(groupNumber) + # newNode.label = "RS%s" %int(groupNumber) newNode.label = baseNode.label - - - #Attach the new node to the base node for later + # Attach the new node to the base node for later baseNode.to_hyper_node[groupNumber] = newNode createdNodes.append((newNode, groupNumber)) - - #Connect base node to operator node + # Connect base node to operator node inBoundLink = network.create_link(baseNode.number, newNode.number, [transferMode]) outBoundLink = network.create_link(newNode.number, baseNode.number, [transferMode]) linksCreated += 2 - - #Attach the transfer links to the grid for indexing + # Attach the transfer links to the grid for indexing transferGrid[0, groupNumber].add(inBoundLink) transferGrid[groupNumber, 0].add(outBoundLink) - - #Connect the virtual nodes to each other - for tup_a, tup_b in get_combinations(createdNodes, 2): #Iterate through unique pairs of nodes + # Connect the virtual nodes to each other + for tup_a, tup_b in get_combinations(createdNodes, 2): # Iterate through unique pairs of nodes node_a, group_a = tup_a node_b, group_b = tup_b link_ab = network.create_link(node_a.number, node_b.number, [transferMode]) link_ba = network.create_link(node_b.number, node_a.number, [transferMode]) linksCreated += 2 - transferGrid[group_a, group_b].add(link_ab) transferGrid[group_b, group_a].add(link_ba) - - #Create any virtual non-stop nodes + # Create any virtual non-stop nodes for groupNumber in baseNode.passing_groups: newNode = network.create_regular_node(self._GetNewNodeNumber(network, baseNode.number)) - - #Copy the node attributes, including x, y coordinates - for att in network.attributes('NODE'): + # Copy the node attributes, including x, y coordinates + for att in network.attributes("NODE"): newNode[att] = baseNode[att] - #newNode.label = "RP%s" %int(groupNumber) + # newNode.label = "RP%s" %int(groupNumber) newNode.label = baseNode.label - - - #Attach the new node to the base node for later + # Attach the new node to the base node for later baseNode.to_hyper_node[groupNumber] = newNode - - #Don't need to connect the new node to anything right now - - def _TransformStationNode(self, baseNode, transferGrid, transferMode): + # Don't need to connect the new node to anything right now + + def _TransformStationNode(self, baseNode, transferGrid, transferMode): network = baseNode.network - virtualNodes = [] - - #Catalog and classify inbound and outbound links for copying + # Catalog and classify inbound and outbound links for copying outgoingLinks = [] incomingLinks = [] outgoingConnectors = [] @@ -1144,7 +1118,7 @@ def _TransformStationNode(self, baseNode, transferGrid, transferMode): for link in baseNode.outgoing_links(): if link.role == 1: outgoingLinks.append(link) - elif link.j_node.is_centroid: + elif link.j_node.is_centroid: if self.StationConnectorFlag: outgoingLinks.append(link) else: @@ -1157,337 +1131,325 @@ def _TransformStationNode(self, baseNode, transferGrid, transferMode): incomingLinks.append(link) else: incomingConnectors.append(link) - first = True for groupNumber in baseNode.stopping_groups: if first: - #Assign the existing node to the first group + # Assign the existing node to the first group baseNode.to_hyper_node[groupNumber] = baseNode virtualNodes.append((baseNode, groupNumber)) - - #Index the incoming and outgoing links to the Grid - for link in incomingLinks: transferGrid[0, groupNumber].add(link) - for link in outgoingLinks: transferGrid[groupNumber, 0].add(link) - + # Index the incoming and outgoing links to the Grid + for link in incomingLinks: + transferGrid[0, groupNumber].add(link) + for link in outgoingLinks: + transferGrid[groupNumber, 0].add(link) first = False - #baseNode.label = "TS%s" %int(groupNumber) - + # baseNode.label = "TS%s" %int(groupNumber) else: virtualNode = network.create_regular_node(self._GetNewNodeNumber(network, baseNode.number)) - - #Copy the node attributes, including x, y coordinates - for att in network.attributes('NODE'): virtualNode[att] = baseNode[att] - #virtualNode.label = "TS%s" %int(groupNumber) + # Copy the node attributes, including x, y coordinates + for att in network.attributes("NODE"): + virtualNode[att] = baseNode[att] + # virtualNode.label = "TS%s" %int(groupNumber) virtualNode.label = baseNode.label - - #Assign the new node to its group number - baseNode.to_hyper_node[groupNumber] = virtualNode + # Assign the new node to its group number + baseNode.to_hyper_node[groupNumber] = virtualNode virtualNodes.append((virtualNode, groupNumber)) - - #Copy the base node's existing centroid connectors to the new virtual node + # Copy the base node's existing centroid connectors to the new virtual node if not self.StationConnectorFlag: for connector in outgoingConnectors: newLink = network.create_link(virtualNode.number, connector.j_node.number, connector.modes) - for att in network.attributes('LINK'): newLink[att] = connector[att] + for att in network.attributes("LINK"): + newLink[att] = connector[att] for connector in incomingConnectors: newLink = network.create_link(connector.i_node.number, virtualNode.number, connector.modes) - for att in network.attributes('LINK'): newLink[att] = connector[att] - - #Copy the base node's existing station connectors to the new virtual node + for att in network.attributes("LINK"): + newLink[att] = connector[att] + # Copy the base node's existing station connectors to the new virtual node for connector in outgoingLinks: newLink = network.create_link(virtualNode.number, connector.j_node.number, connector.modes) - for att in network.attributes('LINK'): newLink[att] = connector[att] - - transferGrid[groupNumber, 0].add(newLink) #Index the new connector to the Grid - + for att in network.attributes("LINK"): + newLink[att] = connector[att] + transferGrid[groupNumber, 0].add(newLink) # Index the new connector to the Grid for connector in incomingLinks: newLink = network.create_link(connector.i_node.number, virtualNode.number, connector.modes) - for att in network.attributes('LINK'): newLink[att] = connector[att] - - transferGrid[0, groupNumber].add(newLink) #Index the new connector to the Grid - - #Connect the virtual nodes to each other - for tup_a, tup_b in get_combinations(virtualNodes, 2): #Iterate through unique pairs of nodes + for att in network.attributes("LINK"): + newLink[att] = connector[att] + + transferGrid[0, groupNumber].add(newLink) # Index the new connector to the Grid + # Connect the virtual nodes to each other + for tup_a, tup_b in get_combinations(virtualNodes, 2): # Iterate through unique pairs of nodes node_a, group_a = tup_a node_b, group_b = tup_b - link_ab = network.create_link(node_a.number, node_b.number, [transferMode]) link_ba = network.create_link(node_b.number, node_a.number, [transferMode]) - - transferGrid[group_a, group_b].add( link_ab ) - transferGrid[group_b, group_a].add( link_ba ) - + transferGrid[group_a, group_b].add(link_ab) + transferGrid[group_b, group_a].add(link_ba) for group in baseNode.passing_groups: newNode = network.create_regular_node(self._GetNewNodeNumber(network, baseNode.number)) - - for att in network.attributes('NODE'): newNode[att] = baseNode[att] - #newNode.label = "TP%s" %int(group) + for att in network.attributes("NODE"): + newNode[att] = baseNode[att] + # newNode.label = "TP%s" %int(group) newNode.label = baseNode.label - + baseNode.to_hyper_node[group] = newNode - + def _ConnectSurfaceOrStationNode(self, baseNode1, transferGrid): network = baseNode1.network - - #Theoretically, we should only need to look at outgoing links, - #since one node's outgoing link is another node's incoming link. + # Theoretically, we should only need to look at outgoing links, + # since one node's outgoing link is another node's incoming link. for link in baseNode1.outgoing_links(): - if link.role == 0: continue #Skip non-connector links - + if link.role == 0: + continue # Skip non-connector links baseNode2 = link.j_node - for groupNumber1 in baseNode1.stopping_groups: virtualNode1 = baseNode1.to_hyper_node[groupNumber1] - for groupNumber2 in baseNode2.stopping_groups: virtualNode2 = baseNode2.to_hyper_node[groupNumber2] - if network.link(virtualNode1.number, virtualNode2.number) is not None: - #Link already exists. Index it just in case + # Link already exists. Index it just in case if groupNumber1 != groupNumber2: - transferGrid[groupNumber1, groupNumber2].add(network.link(virtualNode1.number, virtualNode2.number)) - continue - + transferGrid[groupNumber1, groupNumber2].add( + network.link(virtualNode1.number, virtualNode2.number) + ) + continue newLink = network.create_link(virtualNode1.number, virtualNode2.number, link.modes) - for att in network.attributes('LINK'): newLink[att] = link[att] - - #Only index if the group numbers are different. Otherwise, this is the only - #part of the code where intra-group transfers are identified, so DON'T do - #it to have the matrix be consistent. + for att in network.attributes("LINK"): + newLink[att] = link[att] + # Only index if the group numbers are different. Otherwise, this is the only + # part of the code where intra-group transfers are identified, so DON'T do + # it to have the matrix be consistent. if groupNumber1 != groupNumber2: transferGrid[groupNumber1, groupNumber2].add(newLink) - + def _ProcessTransitLine(self, lineId, network, zoneTransferGrid, saveFunction): line = network.transit_line(lineId) group = line.group lineMode = set([line.mode]) - baseLinks = [segment.link for segment in line.segments(False)] newItinerary = [baseLinks[0].i_node.to_hyper_node[group].number] for baseLink in baseLinks: iv = baseLink.i_node.to_hyper_node[group].number jv = baseLink.j_node.to_hyper_node[group].number - newItinerary.append(jv) - vLink = network.link(iv, jv) if vLink is None: vLink = network.create_link(iv, jv, lineMode) - for att in network.attributes('LINK'): vLink[att] = baseLink[att] + for att in network.attributes("LINK"): + vLink[att] = baseLink[att] else: vLink.modes |= lineMode - - newLine = network.create_transit_line('temp', line.vehicle.id, newItinerary) - for att in network.attributes('TRANSIT_LINE'): newLine[att] = line[att] - + newLine = network.create_transit_line("temp", line.vehicle.id, newItinerary) + for att in network.attributes("TRANSIT_LINE"): + newLine[att] = line[att] for segment in line.segments(True): newSegment = newLine.segment(segment.number) - for att in network.attributes('TRANSIT_SEGMENT'): newSegment[att] = segment[att] - + for att in network.attributes("TRANSIT_SEGMENT"): + newSegment[att] = segment[att] saveFunction(newSegment, segment.i_node.number) - link = segment.link if link is not None: fzi = link.i_node.fare_zone fzj = link.j_node.fare_zone - if fzi != fzj and fzi != 0 and fzj != 0: - #Add the segment's identifier, since changeTransitLineId de-references - #the line copy. + # Add the segment's identifier, since changeTransitLineId de-references + # the line copy. zoneTransferGrid[fzi, fzj].add((lineId, segment.number)) - network.delete_transit_line(lineId) _editing.changeTransitLineId(newLine, lineId) - + def _IndexStationConnectors(self, network, transferGrid, stationGroups, groupIds2Int): print("Indexing station connectors") for lineGroupId, stationCentroids in six.iteritems(stationGroups): idx = groupIds2Int[lineGroupId] - for nodeId in stationCentroids: centroid = network.node(nodeId) - if not centroid.is_centroid: continue #Skip non-zones - + if not centroid.is_centroid: + continue # Skip non-zones for link in centroid.outgoing_links(): if idx in link.j_node.stopping_groups: transferGrid[0, idx].add(link) for link in centroid.incoming_links(): if idx in link.i_node.stopping_groups: transferGrid[idx, 0].add(link) - print("Indexed connectors for group %s" %lineGroupId) - - - #--- - #---LOAD FARE RULES----------------------------------------------------------------------------------- - - def _ApplyFareRules(self, network, fareRulesElement, - groupTransferGrid, zoneCrossingGrid, - groupIds2Int, zoneId2sInt, segmentFareAttribute, linkFareAttribute): - + print("Indexed connectors for group %s" % lineGroupId) + + # --- + # ---LOAD FARE RULES----------------------------------------------------------------------------------- + def _ApplyFareRules( + self, + network, + fareRulesElement, + groupTransferGrid, + zoneCrossingGrid, + groupIds2Int, + zoneId2sInt, + segmentFareAttribute, + linkFareAttribute, + ): linesIdexedByGroup = {} for line in network.transit_lines(): group = line.group - + if group in linesIdexedByGroup: linesIdexedByGroup[group].append(line) else: linesIdexedByGroup[group] = [line] - - for fareElement in fareRulesElement.findall('fare'): - typ = fareElement.attrib['type'] - - if typ == 'initial_boarding': - self._ApplyInitialBoardingFare(fareElement, groupIds2Int, zoneId2sInt, groupTransferGrid, linkFareAttribute) - elif typ == 'transfer': - self._ApplyTransferBoardingFare(fareElement, groupIds2Int, groupTransferGrid, linkFareAttribute) - elif typ == 'distance_in_vehicle': + for fareElement in fareRulesElement.findall("fare"): + typ = fareElement.attrib["type"] + if typ == "initial_boarding": + self._ApplyInitialBoardingFare( + fareElement, groupIds2Int, zoneId2sInt, groupTransferGrid, linkFareAttribute + ) + elif typ == "transfer": + self._ApplyTransferBoardingFare( + fareElement, groupIds2Int, groupTransferGrid, linkFareAttribute, zoneId2sInt + ) + elif typ == "distance_in_vehicle": self._ApplyFareByDistance(fareElement, groupIds2Int, linesIdexedByGroup, segmentFareAttribute) - elif typ == 'zone_crossing': - self._ApplyZoneCrossingFare(fareElement, groupIds2Int, zoneId2sInt, zoneCrossingGrid, network, segmentFareAttribute) - + elif typ == "zone_crossing": + self._ApplyZoneCrossingFare( + fareElement, groupIds2Int, zoneId2sInt, zoneCrossingGrid, network, segmentFareAttribute + ) self.TRACKER.completeSubtask() - - + def _ApplyInitialBoardingFare(self, fareElement, groupIds2Int, zoneId2sInt, transferGrid, linkFareAttribute): - cost = float(fareElement.attrib['cost']) - - with _m.logbook_trace("Initial Boarding Fare of %s" %cost): - groupId = fareElement.find('group').text - _m.logbook_write("Group: %s" %groupId) - + cost = float(fareElement.attrib["cost"]) + with _m.logbook_trace("Initial Boarding Fare of %s" % cost): + groupId = fareElement.find("group").text + _m.logbook_write("Group: %s" % groupId) groupNumber = groupIds2Int[groupId] - - inZoneElement = fareElement.find('in_zone') + inZoneElement = fareElement.find("in_zone") if inZoneElement is not None: zoneId = inZoneElement.text zoneNumber = zoneId2sInt[zoneId] - _m.logbook_write("In zone: %s" %zoneId) - + _m.logbook_write("In zone: %s" % zoneId) checkLink = lambda link: link.i_node.fare_zone == zoneNumber else: checkLink = lambda link: True - - includeAllElement = fareElement.find('include_all_groups') + includeAllElement = fareElement.find("include_all_groups") if includeAllElement is not None: includeAll = self.__BOOL_PARSER[includeAllElement.text] - _m.logbook_write("Include all groups: %s" %includeAll) + _m.logbook_write("Include all groups: %s" % includeAll) else: includeAll = True - count = 0 if includeAll: for xIndex in xrange(transferGrid.x): for link in transferGrid[xIndex, groupNumber]: - if checkLink(link): + if checkLink(link): link[linkFareAttribute] += cost count += 1 else: for link in transferGrid[0, groupNumber]: if checkLink(link): link[linkFareAttribute] += cost - count += 1 - _m.logbook_write("Applied to %s links." %count) - - def _ApplyTransferBoardingFare(self, fareElement, groupIds2Int, transferGrid, linkFareAttribute): - cost = float(fareElement.attrib['cost']) - - with _m.logbook_trace("Transfer Boarding Fare of %s" %cost): - fromGroupId = fareElement.find('from_group').text + count += 1 + _m.logbook_write("Applied to %s links." % count) + + def _ApplyTransferBoardingFare(self, fareElement, groupIds2Int, transferGrid, linkFareAttribute, zoneId2sInt): + cost = float(fareElement.attrib["cost"]) + with _m.logbook_trace("Transfer Boarding Fare of %s" % cost): + fromGroupId = fareElement.find("from_group").text fromNumber = groupIds2Int[fromGroupId] - _m.logbook_write("From Group: %s" %fromGroupId) - - toGroupId = fareElement.find('to_group').text + _m.logbook_write("From Group: %s" % fromGroupId) + toGroupId = fareElement.find("to_group").text toNumber = groupIds2Int[toGroupId] - _m.logbook_write("To Group: %s" %toGroupId) - - bidirectionalElement = fareElement.find('bidirectional') + _m.logbook_write("To Group: %s" % toGroupId) + bidirectionalElement = fareElement.find("bidirectional") if bidirectionalElement is not None: bidirectional = self.__BOOL_PARSER[bidirectionalElement.text.upper()] - _m.logbook_write("Bidirectional: %s" %bidirectional) + _m.logbook_write("Bidirectional: %s" % bidirectional) else: bidirectional = False - + inZoneElement = fareElement.find("in_zone") + if inZoneElement is not None: + zoneId = inZoneElement.text + zoneNumber = zoneId2sInt[zoneId] + _m.logbook_write("In zone: %s" % zoneId) + checkLink = lambda link: link.i_node.fare_zone == zoneNumber + else: + checkLink = lambda link: True + links_changed = {} count = 0 for link in transferGrid[fromNumber, toNumber]: - link[linkFareAttribute] += cost - count += 1 - - if bidirectional: - for link in transferGrid[toNumber, fromNumber]: + if checkLink(link): link[linkFareAttribute] += cost count += 1 - _m.logbook_write("Applied to %s links." %count) - + links_changed[str(link.id)] = str(link[linkFareAttribute]) + if bidirectional: + for link in transferGrid[toNumber, fromNumber]: + if checkLink(link): + link[linkFareAttribute] += cost + count += 1 + links_changed[str(link.id)] = str(link[linkFareAttribute]) + _m.logbook_write("Applied to %s links." % count) + _m.logbook_write(name="Links that have been changed", attributes=links_changed) + def _ApplyFareByDistance(self, fareElement, groupIds2Int, linesIdexedByGroup, segmentFareAttribute): - cost = float(fareElement.attrib['cost']) - - with _m.logbook_trace("Fare by Distance of %s" %cost): - groupId = fareElement.find('group').text + cost = float(fareElement.attrib["cost"]) + with _m.logbook_trace("Fare by Distance of %s" % cost): + groupId = fareElement.find("group").text groupNumber = groupIds2Int[groupId] - _m.logbook_write("Group: %s" %groupId) - + _m.logbook_write("Group: %s" % groupId) count = 0 for line in linesIdexedByGroup[groupNumber]: for segment in line.segments(False): segment[segmentFareAttribute] += segment.link.length * cost count += 1 - _m.logbook_write("Applied to %s segments." %count) - - def _ApplyZoneCrossingFare(self, fareElement, groupIds2Int, zoneId2sInt, crossingGrid, network, segmentFareAttribute): - cost = float(fareElement.attrib['cost']) - - with _m.logbook_trace("Zone Crossing Fare of %s" %cost): - groupId = fareElement.find('group').text + _m.logbook_write("Applied to %s segments." % count) + + def _ApplyZoneCrossingFare( + self, fareElement, groupIds2Int, zoneId2sInt, crossingGrid, network, segmentFareAttribute + ): + cost = float(fareElement.attrib["cost"]) + with _m.logbook_trace("Zone Crossing Fare of %s" % cost): + groupId = fareElement.find("group").text groupNumber = groupIds2Int[groupId] - _m.logbook_write("Group: %s" %groupId) - - fromZoneId = fareElement.find('from_zone').text + _m.logbook_write("Group: %s" % groupId) + fromZoneId = fareElement.find("from_zone").text fromNumber = zoneId2sInt[fromZoneId] - _m.logbook_write("From Zone: %s" %fromZoneId) - - toZoneId = fareElement.find('to_zone').text + _m.logbook_write("From Zone: %s" % fromZoneId) + toZoneId = fareElement.find("to_zone").text toNumber = zoneId2sInt[toZoneId] - _m.logbook_write("To Zone: %s" %toZoneId) - - bidirectionalElement = fareElement.find('bidirectional') + _m.logbook_write("To Zone: %s" % toZoneId) + bidirectionalElement = fareElement.find("bidirectional") if bidirectionalElement is not None: bidirectional = self.__BOOL_PARSER[bidirectionalElement.text.upper()] - _m.logbook_write("Bidirectional: %s" %bidirectional) + _m.logbook_write("Bidirectional: %s" % bidirectional) else: bidirectional = False - count = 0 for lineId, segmentNumber in crossingGrid[fromNumber, toNumber]: line = network.transit_line(lineId) - if line.group != groupNumber: continue + if line.group != groupNumber: + continue line.segment(segmentNumber)[segmentFareAttribute] += cost count += 1 - if bidirectional: for lineId, segmentNumber in crossingGrid[toNumber, fromNumber]: line = network.transit_line(lineId) - if line.group != groupNumber: continue + if line.group != groupNumber: + continue line.segment(segmentNumber)[segmentFareAttribute] += cost count += 1 - - _m.logbook_write("Applied to %s segments." %count) - + _m.logbook_write("Applied to %s segments." % count) + def _CheckForNegativeFares(self, network, segmentFareAttribute, linkFareAttribute): negativeLinks = [] negativeSegments = [] - for link in network.links(): cost = link[linkFareAttribute] - if cost < 0.0: negativeLinks.append(link) - + if cost < 0.0: + negativeLinks.append(link) for segment in network.transit_segments(): cost = segment[segmentFareAttribute] - if cost < 0.0: negativeSegments.append(segment) - - + if cost < 0.0: + negativeSegments.append(segment) if (len(negativeLinks) + len(negativeSegments)) > 0: - print("WARNING: Found %s links and %s segments with negative fares" %(len(negativeLinks), len(negativeSegments))) - + print( + "WARNING: Found %s links and %s segments with negative fares" + % (len(negativeLinks), len(negativeSegments)) + ) t = "\n" t += " \n" t += " \n" @@ -1497,7 +1459,6 @@ def _CheckForNegativeFares(self, network, segmentFareAttribute, linkFareAttribut t += "\n".format(str(link).strip()) t += "\n".format(str(link[linkFareAttribute]).strip()) t += "
link{0}{0}
" - t = "\n" t += " \n" t += " \n" @@ -1507,29 +1468,29 @@ def _CheckForNegativeFares(self, network, segmentFareAttribute, linkFareAttribut t += "\n".format(str(segment.id).strip()) t += "\n".format(str(segment[segmentFareAttribute]).strip()) t += "
segment{0}{0}
" - _m.logbook_write("LINKS AND SEGMENTS WITH NEGATIVE FARES", value=t) - #--- - #---MODELLER INTERFACE FUNCTIONS---------------------------------------------------------------------- - + # --- + # ---MODELLER INTERFACE FUNCTIONS---------------------------------------------------------------------- + @_m.method(return_type=six.text_type) def preload_auxtr_modes(self): options = [] h = HTML() - for id, type, description in _util.getScenarioModes(self.BaseScenario, ['AUX_TRANSIT']): - text = "%s - %s" %(id, description) - options.append(str(h.option(text, value= id))) + for id, type, description in _util.getScenarioModes(self.BaseScenario, ["AUX_TRANSIT"]): + text = "%s - %s" % (id, description) + options.append(str(h.option(text, value=id))) return "\n".join(options) - + @_m.method(return_type=six.text_type) def preload_scenario_link_attributes(self): options = [] h = HTML() for exatt in self.BaseScenario.extra_attributes(): - if exatt.type != 'LINK': continue - text = "%s - %s" %(exatt.name, exatt.description) - options.append(str(h.option(text, value= exatt.name))) + if exatt.type != "LINK": + continue + text = "%s - %s" % (exatt.name, exatt.description) + options.append(str(h.option(text, value=exatt.name))) return "\n".join(options) @_m.method(return_type=six.text_type) @@ -1537,16 +1498,16 @@ def preload_scenario_segment_attributes(self): options = [] h = HTML() for exatt in self.BaseScenario.extra_attributes(): - if exatt.type != 'TRANSIT_SEGMENT': continue - text = "%s - %s" %(exatt.name, exatt.description) - options.append(str(h.option(text, value= exatt.name))) + if exatt.type != "TRANSIT_SEGMENT": + continue + text = "%s - %s" % (exatt.name, exatt.description) + options.append(str(h.option(text, value=exatt.name))) return "\n".join(options) - + @_m.method(return_type=_m.TupleType) def percent_completed(self): return self.TRACKER.getProgress() - + @_m.method(return_type=six.text_type) def tool_run_msg_status(self): return self.tool_run_msg - \ No newline at end of file