class GetStationAccessFile(_m.Tool()):

    version = '1.0.0'
    tool_run_msg = ""
    number_of_tasks = 6  # 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__)

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    Scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter
    SearchRadius = _m.Attribute(float)
    GoStationSelectorExpression = _m.Attribute(str)
    ExportFile = _m.Attribute(str)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario
        self.SearchRadius = 1000.0
        self.GoStationSelectorExpression = "i=7000,8000"

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Get Station Access File v%s" % self.version,
            description=
            "<p class='tmg_left'> Produces a table indicating which zones are within a 1km \
                         (or other user-specified distance) from subway or GO train stations. \
                         Subway stations are identified as stops of lines with mode = <b>'m'</b>. \
                         GO stations can be identified in one of two ways: \
                         <ol class='tmg_left'><li> Station centroids, \
                         identified by a node selector expression; or</li><li> Stops of lines with mode \
                         = <b>'r'.</b></li></ol> \
                         <p class='tmg_left'> This data is saved into a CSV file with three columns: \
                         <em>'Zone', 'NearSubway' , 'NearGO'</em>. The first column identifies the zone, \
                         the second column indicates whether a zone is within the radius of a subway \
                         station (0 or 1), and the third column indicates whether a zone is within the \
                         radius of a GO Train station (0 or 1). Zones not in the radius of either are not \
                         listed.</p>",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_text_box(tool_attribute_name='SearchRadius',
                        size=10,
                        title="Search Radius",
                        note="In coordinate units (m)")

        basepath = _path.dirname(_MODELLER.desktop.project_file_name())
        pb.add_select_file(tool_attribute_name='ExportFile',
                           window_type='save_file',
                           file_filter="*.csv",
                           start_path=basepath,
                           title="Output File")

        pb.add_text_box(tool_attribute_name='GoStationSelectorExpression',
                        size=100,
                        multi_line=True,
                        title="GO Station Selector",
                        note="<font color='green'><b>Optional:</b></font> \
                            Write a zone filter expression to select GO Station centroids.\
                            <br>If ommitted, this tool will use GO rail line stops."
                        )

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")

    def __call__(self, xtmf_ScenarioNumber, SearchRadius,
                 GoStationSelectorExpression, ExportFile):

        #---1 Set up scenario
        self.Scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        self.SearchRadius = SearchRadius
        self.GoStationSelectorExpression = GoStationSelectorExpression
        self.ExportFile = ExportFile

        try:
            self._Execute()
        except Exception as e:
            raise Exception(_traceback.format_exc())

    ##########################################################################################################

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            #self.ExportFile = _path.splitext(self.ExportFile)[0] + ".csv"

            with self._FlagAttributeMANAGER():
                try:
                    netCalcTool = _MODELLER.tool(
                        'inro.emme.network_calculation.network_calculator')
                except Exception as e:
                    netCalcTool = _MODELLER.tool(
                        'inro.emme.standard.network_calculation.network_calculator'
                    )

                if self.GoStationSelectorExpression:
                    _m.logbook_write("Flagging GO station centroids.")
                    self.TRACKER.runTool(netCalcTool,
                                         self._GetCentroidFlagSpec(),
                                         scenario=self.Scenario)
                else:
                    self.TRACKER.completeTask()

                _m.logbook_write("Building search grid")
                network = self.Scenario.get_network()
                self.TRACKER.completeTask()

                network.create_attribute('NODE', 'subStation', 0)
                network.create_attribute('NODE', 'goStation', 0)

                with _m.logbook_trace("Getting station coordinates"):
                    self._FlagTransitStops(network)
                    subwayStations, goStations = self._GetNodeSet(network)

                with nested(_m.logbook_trace("Performing search"),
                            open(self.ExportFile, 'w')) as (log, writer):

                    #Prepare the search grid
                    extents = _spindex.get_network_extents(network)
                    spatialIndex = _spindex.GridIndex(extents, marginSize=1.0)
                    for zone in network.centroids():
                        spatialIndex.insertPoint(zone)

                    self.TRACKER.startProcess(
                        len(subwayStations) + len(goStations))
                    for station in subwayStations:
                        nearbyNodes = spatialIndex.queryCircle(
                            station.x, station.y, self.SearchRadius)
                        for node in nearbyNodes:
                            dist = sqrt((node.x - station.x)**2 +
                                        (node.y - station.y)**2)
                            if dist > self.SearchRadius: continue

                            node.subStation = 1
                        self.TRACKER.completeSubtask()

                    for station in goStations:
                        nearbyNodes = spatialIndex.queryCircle(
                            station.x, station.y, self.SearchRadius)
                        for node in nearbyNodes:
                            dist = sqrt((node.x - station.x)**2 +
                                        (node.y - station.y)**2)
                            if dist > self.SearchRadius: continue

                            node.goStation = 1
                        self.TRACKER.completeSubtask()

                    #Prepare the file
                    writer.write("Zone,NearSubway,NearGO")
                    for centroid in network.centroids():
                        writer.write("\n%s,%s,%s" %
                                     (centroid.number, centroid.subStation,
                                      centroid.goStation))

                    self.TRACKER.completeTask()

    ##########################################################################################################

    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''

    @contextmanager
    def _FlagAttributeMANAGER(self):
        if self.GoStationSelectorExpression:
            att = self.Scenario.create_extra_attribute('NODE', '@xflag')
        try:
            yield
        finally:
            if self.GoStationSelectorExpression:
                self.Scenario.delete_extra_attribute('@xflag')

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.Scenario.id),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _GetCentroidFlagSpec(self):
        spec = {
            "result": "@xflag",
            "expression": "1",
            "aggregation": None,
            "selections": {
                "node": self.GoStationSelectorExpression
            },
            "type": "NETWORK_CALCULATION"
        }

        return spec

    def _GetCheckMode(self, network, modeId):
        mode = network.mode(modeId)
        if mode is None:
            raise Exception("Scenario %s does not have mode '%s' defined!" %
                            (self.Scenario.id, modeId))
        elif mode.type != 'TRANSIT':
            raise Exception("Scenario %s mode '%s' is not a transit mode!" %
                            (self.Scenario.id, modeId))
        return mode

    def _FlagTransitStops(self, network):
        network.create_attribute('NODE', 'isStop', default_value=0)

        subwayMode = self._GetCheckMode(network, 'm')
        trainMode = self._GetCheckMode(network, 'r')

        self.TRACKER.startProcess(network.element_totals['transit_lines'])
        for line in network.transit_lines():
            if line.mode == subwayMode:
                for segment in line.segments(include_hidden=True):
                    if segment.allow_alightings or segment.allow_boardings:
                        segment.i_node.isStop = 1  #SUBWAY STOP
            elif line.mode == trainMode:
                for segment in line.segments(include_hidden=True):
                    if segment.allow_alightings or segment.allow_boardings:
                        segment.i_node.isStop = 2  #GO TRAIN STOP
            self.TRACKER.completeSubtask()
        self.TRACKER.completeTask()

    def _GetNodeSet(self, network):
        subwayStations = set()
        goStations = set()

        subStopCount = 0
        goStopCount = 0

        self.TRACKER.startProcess(network.element_totals['regular_nodes'])
        for node in network.regular_nodes():
            if node.isStop == 1:
                subwayStations.add(node)
                subStopCount += 1
            elif node.isStop == 2 and not self.GoStationSelectorExpression:
                goStations.add(node)
                goStopCount += 1
            self.TRACKER.completeSubtask()
        self.TRACKER.completeTask()

        if self.GoStationSelectorExpression:
            self.TRACKER.startProcess(network.element_totals['centroids'])
            for centroid in network.centroids():
                if centroid['@xflag'] == 1:
                    goStations.add(centroid)
                    goStopCount += 1
                self.TRACKER.completeSubtask()
        self.TRACKER.completeTask()
        _m.logbook_write("Found %s subway stations and %s GO train stations." %
                         (subStopCount, goStopCount))

        return (subwayStations, goStations)

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
Beispiel #2
0
class AddNodeWeights(_m.Tool()):

    version = '0.1.0'
    tool_run_msg = ""
    report_html = ""
    Scenario = _m.Attribute(_m.InstanceType)
    MassAttribute = _m.Attribute(_m.InstanceType)

    def __init__(self):
        self.Scenario = _MODELLER.scenario
        self._tracker = _util.ProgressTracker(5)

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Add Node Weights v%s" % self.version,
            description="Adds node weights to existing network nodes \
                                for use in centroid generation. Higher weights are \
                                more likely to be connected to centroid connectors in CCGEN.\
                                <br><br>The node weights are assigned as follows: \
                                <br> <ul style=\"list-style-type:none\">\
                                <li><b>1</b>: default value \
                                <li><b>2</b>: nodes at intersections with transit stop(s) \
                                <li><b>3</b>: mid-block nodes without any transit stops \
                                <li><b>4</b>: mid-block nodes with transit stop(s) \
                                <li><b>5</b>: dead-end nodes without any transit stops \
                                <li><b>6</b>: dead-end nodes with transit stop(s)</ul>\
                                ",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name="Scenario",
                               title="Select Scenario",
                               allow_none=False)

        pb.add_select_attribute(
            tool_attribute_name="MassAttribute",
            filter='NODE',
            allow_none=True,
            title="Node weight attribute",
            note="Attribute which node weights will be stored in.")

        return pb.render()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg

    def run(self):
        self.tool_run_msg = ""
        '''Run is called from Modeller.'''

        try:
            self._execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

    def _execute(self):
        network = self.Scenario.get_network()
        print 'loaded network'

        network.create_attribute('NODE', 'is_stop', False)
        for segment in network.transit_segments():
            if segment.allow_boardings or segment.allow_alightings:
                segment.i_node.is_stop = True
        print 'flagged all transit stops'

        network.create_attribute('NODE', 'degree', 0)
        for node in network.regular_nodes():
            neighbours = set()
            for link in node.outgoing_links():
                j_node = link.j_node
                if j_node.is_centroid: continue  #Skip connected centroids
                neighbours.add(j_node.number)
            for link in node.incoming_links():
                i_node = link.i_node
                if i_node.is_centroid: continue  #Skip connected centroids
                neighbours.add(i_node.number)
            node.degree = len(neighbours)
        print "calculated degrees"

        for node in network.regular_nodes():
            weight = 1
            degree = node.degree
            if degree == 1:
                weight = 5
            elif degree == 2:
                weight = 3

            if node.is_stop: weight += 1
            node[self.MassAttribute.id] = weight
        print "processed node weight"

        self.Scenario.publish_network(network, resolve_attributes=True)
        print "published network"

        self.tool_run_msg = _m.PageBuilder.format_info(
            "Node weights have successfully been added to %s." %
            (self.MassAttribute.id))
class ExtractStationBoardingsAlightings(_m.Tool()):

    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    COLON = ':'
    COMMA = ','

    #---PARAMETERS

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    Scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter

    StationNodeFile = _m.Attribute(str)
    ReportFile = _m.Attribute(str)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):

        if EMME_VERSION < (4, 1, 5):
            raise ValueError(
                "Tool not compatible. Please upgrade to version 4.1.5+")

        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Export Station Boardings and Alightings v%s" % self.version,
            description="Extracts total boardings and alightings for a list \
                         of nodes defined in a CSV file.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_select_file(tool_attribute_name='ReportFile',
                           title="Report file",
                           file_filter="*.csv",
                           window_type='save_file')

        pb.add_select_file(
            tool_attribute_name='StationNodeFile',
            title="Station Node file:",
            window_type='file',
            file_filter="*.csv",
            note="Station node file contains the following two columns: \
                            <ul><li>node_id</li>\
                            <li>label</li></ul> \
                            where label is whatever you wish to label the node in the output."
        )
        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            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=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg

    ##########################################################################################################

    def __call__(self, xtmf_ScenarioNumber, ReportFile, StationNodeFile):

        if EMME_VERSION < (4, 1, 5):
            raise ValueError(
                "Tool not compatible. Please upgrade to version 4.1.5+")

        #---1 Set up scenario
        self.Scenario = _MODELLER.emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        if ReportFile:
            self.ReportFile = ReportFile
        else:
            raise Exception("Report file location not indicated!")
        if StationNodeFile:
            self.StationNodeFile = StationNodeFile
        else:
            raise Exception("No station node file selected")

        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()):

            if not self.Scenario.has_transit_results:
                raise Exception("Scenario %s has no transit results" %
                                self.Scenario)

            network = self.Scenario.get_network()
            stations, badNodes = self._LoadStationNodeFile(network)

            if len(badNodes) > 0:
                print "%s node IDs were not found in the network and were skipped." % len(
                    badNodes)
                pb = _m.PageBuilder("NodeIDs not in network")

                pb.add_text_element(
                    "<b>The following node IDs were not found in the network:</b>"
                )

                for id in badNodes:
                    pb.add_text_element(id)

                _m.logbook_write(
                    "Some IDs were not found in the network. Click for details.",
                    value=pb.render())

            nodeValues = self._CalcBoardingAlighting(network, stations)
            self._OutputResults(nodeValues)

    ##########################################################################################################

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.Scenario),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _LoadStationNodeFile(self, network):
        badIds = set()
        nodeDict = {}
        with open(self.StationNodeFile) as reader:
            header = reader.readline()
            cells = header.strip().split(self.COMMA)

            nodeCol = cells.index('node_id')
            labelCol = cells.index('label')

            for num, line in enumerate(reader):
                cells = line.strip().split(self.COMMA)

                id = cells[nodeCol]
                label = cells[labelCol]
                node = network.node(id)

                if node is None:
                    badIds.add(id)
                    continue  #Skip and report
                else:
                    nodeDict[node.id] = [node.id]
                    nodeDict[node.id].append(label)

        return nodeDict, badIds

    def _CalcBoardingAlighting(self, network, nodeIds):
        for id in nodeIds:
            baseNode = network.node(id)
            checkNodes = []
            for node in network.nodes():
                if node.x == baseNode.x:
                    if node.y == baseNode.y:
                        checkNodes.append(node)
            totalBoard = 0
            totalAlight = 0
            initialBoards = 0
            finalAlights = 0
            for node in checkNodes:
                for segment in node.outgoing_segments(include_hidden=True):
                    board = segment.transit_boardings
                    totalBoard += board

                    voltr = segment.transit_volume
                    index = segment.number - 1
                    voltrLast = segment.line.segment(index).transit_volume
                    totalAlight += (voltrLast + board - voltr)
                initialBoards += node.initial_boardings
                finalAlights += node.final_alightings
            nodeIds[id].append(round(totalBoard))
            nodeIds[id].append(round(totalBoard - initialBoards))
            nodeIds[id].append(round(totalAlight - finalAlights))
            nodeIds[id].append(round(totalAlight))

        return nodeIds

    def _OutputResults(self, valueDict):
        with open(self.ReportFile, 'wb') as csvfile:
            nodeWrite = csv.writer(csvfile, delimiter=',')
            nodeWrite.writerow([
                'node_id', 'label', 'boardings', 'transfer_boardings',
                'transfer_alightings', 'alightings'
            ])
            for key, values in sorted(valueDict.iteritems()):
                nodeWrite.writerow(values)
Beispiel #4
0
class TollBasedRoadAssignment(_m.Tool()):

    version = '2.2.1'
    tool_run_msg = ""
    number_of_tasks = 4  # 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__)

    #---Variable definitions
    xtmf_ScenarioNumber = _m.Attribute(int)
    Scenario = _m.Attribute(_m.InstanceType)
    xtmf_DemandMatrixNumber = _m.Attribute(int)
    DemandMatrix = _m.Attribute(_m.InstanceType)

    LinkTollAttributeId = _m.Attribute(str)

    TimesMatrixId = _m.Attribute(str)
    CostMatrixId = _m.Attribute(str)
    TollsMatrixId = _m.Attribute(str)
    RunTitle = _m.Attribute(str)

    PeakHourFactor = _m.Attribute(float)
    LinkCost = _m.Attribute(float)
    TollCost = _m.Attribute(float)
    TollWeight = _m.Attribute(float)
    Iterations = _m.Attribute(int)
    rGap = _m.Attribute(float)
    brGap = _m.Attribute(float)
    normGap = _m.Attribute(float)

    PerformanceFlag = _m.Attribute(bool)
    SOLAFlag = _m.Attribute(bool)

    def __init__(self):
        self._tracker = _util.ProgressTracker(self.number_of_tasks)

        self.Scenario = _MODELLER.scenario

        mf10 = _MODELLER.emmebank.matrix('mf10')
        if mf10 != None:
            self.DemandMatrix = mf10

        self.PeakHourFactor = 0.43
        self.LinkCost = 0
        self.TollCost = 0
        self.TollWeight = 0
        self.Iterations = 100
        self.rGap = 0
        self.brGap = 0.1
        self.normGap = 0.05
        self.PerformanceFlag = False
        self.RunTitle = ""
        self.LinkTollAttributeId = "@toll"

        if EMME_VERSION >= 4.1:
            self.SOLAFlag = True
        else:
            self.SOLAFlag = False

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Toll Based Road Assignment v%s" % self.version,
            description=
            "Executes a standard Emme traffic assignment using tolls for link \
                         costs converted to a time penalty, using a specified link extra attribute \
                         containing the toll value. The actual times and costs are recovered \
                         by running a second 'all-or-nothing' assignment. This version uses a link \
                         extra attribute already containing the link toll cost.\
                         <br><br><b>Temporary Storage Requirements:</b> 1 extra \
                         link attributes, 1 full matrix, 1 scenario.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_header("SCENARIO")

        pb.add_select_scenario(tool_attribute_name="Scenario",
                               title="Select a Scenario",
                               allow_none=False)

        matrixCount = sum(
            [1 for m in _MODELLER.emmebank.matrices() if m.type == 'FULL'])
        demandMatrixNote = ""
        if matrixCount == 0:
            demandMatrixNote = "<font color=red><b>No full matrices in emmebank!</b></font>"
            pb.runnable = False

        pb.add_select_matrix(tool_attribute_name="DemandMatrix",
                             title="Select a demand matrix",
                             filter="FULL",
                             note=demandMatrixNote,
                             allow_none=False)

        keyval = {}
        for att in self.Scenario.extra_attributes():
            if not att.type == 'LINK': continue
            label = "{id} ({domain}) - {name}".format(id=att.name,
                                                      domain=att.type,
                                                      name=att.description)
            keyval[att.name] = label

        pb.add_select(tool_attribute_name='LinkTollAttributeId',
                      keyvalues=keyval,
                      title="Link Toll Attribute")

        pb.add_header("OUTPUT MATRICES")

        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_select_new_matrix(tool_attribute_name='TimesMatrixId',
                                         overwrite_existing=True,
                                         matrix_type='FULL',
                                         note='Create or override',
                                         title='Travel times matrix')

            with t.table_cell():
                pb.add_select_new_matrix(tool_attribute_name="CostMatrixId",
                                         overwrite_existing=True,
                                         matrix_type='FULL',
                                         note='Create or override',
                                         title='Travel costs matrix')

            with t.table_cell():
                pb.add_select_new_matrix(tool_attribute_name="TollsMatrixId",
                                         overwrite_existing=True,
                                         matrix_type='FULL',
                                         note='Create or override',
                                         title='Tolls matrix')

        pb.add_text_box(tool_attribute_name='RunTitle',
                        size=25,
                        title="Run Title",
                        note="25-char run descriptor")

        pb.add_header("PARAMETERS")

        with pb.add_table(visible_border=False) as t:

            with t.table_cell():
                pb.add_html("<b>Peak Hour Factor</b>")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="PeakHourFactor", size=10)

            with t.table_cell():
                pb.add_html(
                    "Converts peak period demand to a single assignment hour.")

            t.new_row()

            with t.table_cell():
                pb.add_html("<b>Link Unit Cost</b>")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="LinkCost", size=10)

            with t.table_cell():
                pb.add_html("Link base cost, in $/km")

            t.new_row()

            with t.table_cell():
                pb.add_html("<b>Toll Perception</b>")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="TollWeight", size=10)

            with t.table_cell():
                pb.add_html("The generalized perception of toll, in $/hr")

        pb.add_header("CONVERGANCE CRITERIA")

        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_text_box(tool_attribute_name="Iterations",
                                size=5,
                                title='Iterations')

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="rGap",
                                size=12,
                                title='Relative gap')

            #t.new_row()

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="brGap",
                                size=12,
                                title='Best relative gap')

            with t.table_cell():
                pb.add_text_box(tool_attribute_name="normGap",
                                size=12,
                                title='Normalized gap')

        pb.add_header("Tool Options")

        pb.add_checkbox(
            tool_attribute_name="PerformanceFlag",
            label="Enable high performance mode?",
            note="This mode will use more cores for assignment,<br>\
                            at the cost of slowing down other processes.")

        if EMME_VERSION >= 4.1:
            pb.add_checkbox(tool_attribute_name='SOLAFlag',
                            label="Use SOLA traffic assignment?")

        pb.add_html("""
<script type="text/javascript">
    $(document).ready( function ()
    {
        var tool = new inro.modeller.util.Proxy(%s) ;

        $("#Scenario").bind('change', function()
        {
            $(this).commit();
            $("#LinkTollAttributeId")
                .empty()
                .append(tool._GetSelectAttributeOptionsHTML())
            inro.modeller.page.preload("#LinkTollAttributeId");
            $("#LinkTollAttributeId").trigger('change');
        });
    });
</script>""" % pb.tool_proxy_tag)

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        '''Run is called from Modeller.'''
        self.isRunningFromXTMF = False

        if self.DemandMatrix == None:
            raise NullPointerException("Demand matrix not specified")
        if self.LinkTollAttributeId == None:
            raise NullPointerException("Link toll attribute not specified")
        if self.PeakHourFactor == None:
            raise NullPointerException("Peak hour factor not specified")
        if self.LinkCost == None:
            raise NullPointerException("Link unit cost not specified")
        if self.TollCost == None:
            raise NullPointerException("Toll unit cost not specified")
        if self.TollWeight == None:
            raise NullPointerException("Toll perception not specified")
        if self.Iterations == None:
            raise NullPointerException("Max iterations not specified")
        if self.rGap == None:
            raise NullPointerException("Relative gap not specified")
        if self.brGap == None:
            raise NullPointerException("Best relative gap not specified")
        if self.normGap == None:
            raise NullPointerException("Normalized gap not specified")

        try:
            self._execute()
        except Exception, e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")
Beispiel #5
0
class ReturnBoardings(_m.Tool()):

    version = '0.1.0'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    # Tool Input Parameters
    #    Only those parameters necessary for Modeller and/or XTMF to dock with
    #    need to be placed here. Internal parameters (such as lists and dicts)
    #    get initialized during construction (__init__)

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    xtmf_LineAggregationFile = _m.Attribute(str)
    xtmf_CheckAggregationFlag = _m.Attribute(bool)

    def __init__(self):
        #---Init internal variables
        self.TRACKER = _util.ProgressTracker(
            self.number_of_tasks)  #init the ProgressTracker

    def page(self):
        pb = _m.ToolPageBuilder(self,
                                title="Return Boardings",
                                description="Cannot be called from Modeller.",
                                runnable=False,
                                branding_text="XTMF")

        return pb.render()

    ##########################################################################################################

    def __call__(self, xtmf_ScenarioNumber, xtmf_LineAggregationFile,
                 xtmf_CheckAggregationFlag):

        _m.logbook_write("Extracting boarding results")

        #---1 Set up scenario
        scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
        if (scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)
        if not scenario.has_transit_results:
            raise Exception(
                "Scenario %s does not have transit assignment results" %
                xtmf_ScenarioNumber)

        self.xtmf_LineAggregationFile = xtmf_LineAggregationFile
        self.xtmf_CheckAggregationFlag = xtmf_CheckAggregationFlag

        try:
            return self._Execute(scenario)
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc()
            raise Exception(msg)

    ##########################################################################################################

    def _Execute(self, scenario):

        lineAggregation = self._LoadLineAggregationFile()

        lineBoardings = self._GetLineResults(scenario)
        netSet = set([key for key in lineBoardings.iterkeys()])
        if self.xtmf_CheckAggregationFlag:
            self._CheckAggregationFile(netSet, lineAggregation)
        self.TRACKER.completeTask()

        results = {}
        self.TRACKER.startProcess(len(lineBoardings))
        for lineId, lineCount in lineBoardings.iteritems():
            if not lineId in lineAggregation:
                self.TRACKER.completeSubtask()
                continue  #Skip unmapped lines
            lineGroupId = lineAggregation[lineId]

            if lineGroupId in results:
                results[lineGroupId] += lineCount
            else:
                results[lineGroupId] = lineCount
            self.TRACKER.completeSubtask()

        print "Extracted results from Emme"
        return str(results)

    def _LoadLineAggregationFile(self):
        mapping = {}
        with open(self.xtmf_LineAggregationFile) as reader:
            reader.readline()
            for line in reader:
                cells = line.strip().split(',')
                key = cells[0].strip()
                val = cells[1].strip()
                mapping[key] = val
        return mapping

    def _GetLineResults(self, scenario):

        results = _util.fastLoadSummedSegmentAttributes(
            scenario, ['transit_boardings'])
        retVal = {}
        for lineId, attributes in results.iteritems():
            id = str(lineId)
            retVal[id] = attributes['transit_boardings']

        return retVal

    def _CheckAggregationFile(self, netSet, lineAggregation):
        aggSet = set([key for key in lineAggregation.iterkeys()])

        linesInNetworkButNotMapped = [id for id in (netSet - aggSet)]
        linesMappedButNotInNetwork = [id for id in (aggSet - netSet)]

        if len(linesMappedButNotInNetwork) > 0:
            msg = "%s lines have been found in the network without a line grouping: " % len(
                linesInNetworkButNotMapped)
            msg += ",".join(linesInNetworkButNotMapped[:10])
            if len(linesInNetworkButNotMapped) > 10:
                msg += "...(%s more)" % (len(linesInNetworkButNotMapped) - 10)
            print msg

        if len(linesMappedButNotInNetwork) > 0:
            msg = "%s lines have been found in the aggregation file but do not exist in the network: " % len(
                linesMappedButNotInNetwork)
            msg += ",".join(linesMappedButNotInNetwork[:10])
            if len(linesMappedButNotInNetwork) > 10:
                msg += "...(%s more)" % (len(linesMappedButNotInNetwork) - 10)
            print msg

    ##########################################################################################################

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
Beispiel #6
0
class OperatorTransferMatrix(_m.Tool()):
    
    version = '1.1.2'
    tool_run_msg = ""
    number_of_tasks = 8 # For progress reporting, enter the integer number of tasks here
    
    #---PARAMETERS
    Scenario = _m.Attribute(_m.InstanceType)
    xtmf_ScenarioNumber = _m.Attribute(int)
    DemandMatrixId = _m.Attribute(str)
    ClassName = _m.Attribute(str)
    
    ExportTransferMatrixFlag = _m.Attribute(bool)
    LineGroupOptionOrAttributeId = _m.Attribute(str)
    TransferMatrixFile = _m.Attribute(str)
    
    ExportWalkAllWayMatrixFlag = _m.Attribute(bool)
    AggregationPartition = _m.Attribute(_m.InstanceType)
    xtmf_AggregationPartition = _m.Attribute(str)
    WalkAllWayExportFile = _m.Attribute(str)

    NumberOfProcessors = _m.Attribute(int)
    
    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.Scenario = _MODELLER.scenario #Default is primary scenario
        self.ExportTransferMatrixFlag = True
        self.ExportWalkAllWayMatrixFlag = False

        self.NumberOfProcessors = cpu_count()
    
    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS
    
    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(self, title="Operator Transfer Matrix v%s" %self.version,
                     description="Extracts and exports a matrix of passenger volumes transferring \
                         from and to each operator, including initial boardings, final alightings \
                         and total walk-all-way volumes. Summing over each column is equivalent \
                         to aggregating the boardings for each operator; but only if the each line\
                         has a non-zero group number (i.e. full coverage).\
                         <br><br>Each operator (or <em>line group</em>) is identified numerically \
                         (e.g. <b>1</b> for the first group, <b>2</b> for the second, etc.) based on \
                         several pre-set schemes. The 0<sup>th</sup> 'group' is special: the 0<sup>th</sup> \
                         row corresponds to initial boardings, and the 0<sup>th</sup> column to \
                         final alightings. The cell at 0,0 contains the total walk-all-way volumes \
                         for the scenario.\
                         <br><br><b>Walk-all-way matrix:</b> This tool also calculates, for each OD \
                         the fraction of trips walking all-way. If desired, this tool can aggregate \
                         this matrix (based on an existing <em>Zone Partition</em>) and export it.\
                         <br><br><b>Temporary storage requirements:</b> 1 full matrix, 2 transit \
                         segment attributes, and 1 transit line attribute (if using a pre-built \
                         grouping scheme). ",
                     branding_text="- TMG Toolbox")
        
        if self.tool_run_msg != "": # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)
        
        if EMME_VERSION >= (4,1):
            pb.add_text_element("<em><b>Performance note for Emme 4.1 and newer:</b> \
                When analyzing the results of a congested transit assignment, this \
                tool will automatically blend the results over all iterations in \
                order to keep the results consistent with those saved to the network. \
                This is a slow operation so allow for additional run time when \
                analyzing many iterations.</em>")
        
        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)
        
        pb.add_select_matrix(tool_attribute_name= 'DemandMatrixId',
                             filter=['FULL'], allow_none=True, id=True,
                             title= "Demand to Analyze",
                             note= "If set to None, the tool will use the demand matrix from the assignment, \
                                 however this will affect run time for this tool.")
        
        if self._GetAssignmentType() == "MULTICLASS_TRANSIT_ASSIGNMENT" or self._GetMulticlass() == True:
            classes = self._LoadClassNames()
            keyval1 = {}
            for className in classes:
                keyval1[className] = className
                #keyval1.append((className, className))
        else:
            keyval1 = {-1: "Multiclass assignment not available"}
        print keyval1
        pb.add_select(tool_attribute_name= 'ClassName',
                      keyvalues= keyval1,
                      title= "Class Name",
                      note= "The name of the assignment class to analyze. \
                          <br>Only required if a multiclass transit assignment has been run \
                          (Emme 4.1 and newer).")
        
        
        pb.add_header("TRANSFER MATRIX")
        
        pb.add_checkbox(tool_attribute_name= 'ExportTransferMatrixFlag',
                        label= "Export Transfer Matrix?")
        
        keyval2 = [(1, "<em>Pre-built:</em> NCS11 operator codes"),
                   (2, "<em>Pre-built:</em> Alphabetical by operator"),
                   (3, "<em>Pre-built:</em> Alphabetical by operator and mode"),
                   (4, "<em>Pre-built:</em> GTAModel V4 line groups"),
                   (5, "<em>Pre-built:</em> GTAModel V4 groups + TTC prem. bus"),
                   ("ut1", "<em>Custom:</em> Group IDs stored in UT1"),
                   ("ut2", "<em>Custom:</em> Group IDs stored in UT2"),
                   ("ut3", "<em>Custom:</em> Group IDs stored in UT3")]
        for exatt in self.Scenario.extra_attributes():
            if exatt.type != "TRANSIT_LINE": continue
            text = "<em>Custom:</em> Group IDs stored in %s - %s" %(exatt.name, exatt.description)
            keyval2.append((exatt.name, text))
        pb.add_select(tool_attribute_name= 'LineGroupOptionOrAttributeId',
                      keyvalues= keyval2,
                      title= "Line Groups \ Grouping Scheme",
                      note= "Select a pre-built option or an existing transit line \
                          attribute with group codes")
        
        pb.add_select_file(tool_attribute_name= 'TransferMatrixFile',
                           window_type= 'save_file',
                           file_filter= "*.csv",
                           title= "Transit Matrix File",
                           note= "CSV file to save the transfer matrix to.")
        
        
        pb.add_header("WALK-ALL-WAY MATRIX")
        
        pb.add_checkbox(tool_attribute_name= 'ExportWalkAllWayMatrixFlag',
                        label= "Export walk-all-way matrix?")
        
        pb.add_select_partition(tool_attribute_name= 'AggregationPartition',
                                allow_none= True,
                                title= "Aggregation Partition",
                                note= "<font color='green'><b>Optional:</b></font> \
                                    Select none to disable exporting the walk all-way matrix.")
        
        pb.add_select_file(tool_attribute_name= 'WalkAllWayExportFile',
                           window_type= 'save_file',
                           file_filter= "*.csv",
                           title= "Walk-all-way Matrix File",
                           note= "<font color='green'><b>Optional</b></font>")
        
        #---JAVASCRIPT
        pb.add_html("""
<script type="text/javascript">
    $(document).ready( function ()
    {
        var tool = new inro.modeller.util.Proxy(%s) ;
        
        if (tool.class_name_is_required())
        {
            $("#ClassName").prop('disabled', false);
        } else {
           // $("#ClassName").prop('disabled', true);
        }
        
        if (! tool.preload_transfer_matrix_flag())
        {
            $("#LineGroupOptionOrAttributeId").prop('disabled', true);
            $("#TransferMatrixFile").prop('disabled', true);
        }
        
        if (! tool.preload_waw_matrix_flag())
        {
            $("#AggregationPartition").prop('disabled', true);
            $("#WalkAllWayExportFile").prop('disabled', true);
        }
        
        $("#Scenario").bind('change', function()
        {
            $(this).commit();
            
            $("#LineGroupOptionOrAttributeId")
                .empty()
                .append(tool.preload_line_group_options());
            inro.modeller.page.preload("#LineGroupOptionOrAttributeId");
            $("#LineGroupOptionOrAttributeId").trigger('change');
            
            if (tool.class_name_is_required())
            {
                $("#ClassName")
                    .empty()
                    .append(tool.preload_class_names());
                inro.modeller.page.preload("#ClassName");
                $("#ClassName").trigger('change');
            
                $("#ClassName").prop('disabled', false);
            } else {
                $("#ClassName")
                    .empty()
                    .append("<option value='-1'>Multiclass assignment not available</option>");
                inro.modeller.page.preload("#ClassName");
                $("#ClassName").trigger('change');
            
                $("#ClassName").prop('disabled', true);
            }
        });
        
        $("#ExportTransferMatrixFlag").bind('change', function()
        {
            $(this).commit();
            
            var flag = ! tool.preload_transfer_matrix_flag();
            $("#LineGroupOptionOrAttributeId").prop('disabled', flag);
            $("#TransferMatrixFile").prop('disabled', flag);
        });
        
        $("#ExportWalkAllWayMatrixFlag").bind('change', function()
        {
            $(this).commit();
            
            var flag = ! tool.preload_waw_matrix_flag();
            $("#AggregationPartition").prop('disabled', flag);
            $("#WalkAllWayExportFile").prop('disabled', flag);
        });
    });
</script>""" % pb.tool_proxy_tag)
        
        return pb.render()
    
    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()
        
        try:
            if self.ExportTransferMatrixFlag or self.ExportWalkAllWayMatrixFlag:
                
                if self.ExportTransferMatrixFlag and not self.TransferMatrixFile:
                    raise IOError("No transfer matrix file specified.")
                
                if self.ExportWalkAllWayMatrixFlag:                    
                    if not self.WalkAllWayExportFile: raise TypeError("No walk-all-way matrix file specified")
                    
                self._Execute()
                _MODELLER.desktop.refresh_needed(False)
        except Exception as e:
            _MODELLER.desktop.refresh_needed(False)
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise
        
        self.tool_run_msg = _m.PageBuilder.format_info("Done.")

    def __call__(self, xtmf_ScenarioNumber, ExportTransferMatrixFlag, ExportWalkAllWayMatrixFlag, TransferMatrixFile, 
                 xtmf_AggregationPartition, WalkAllWayExportFile, LineGroupOptionOrAttributeId):
        self.tool_run_msg = ""
        self.TRACKER.reset()                              

        self.Scenario = _MODELLER.emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" %xtmf_ScenarioNumber)

        if(xtmf_AggregationPartition.lower() == "none"):
            self.AggregationPartition is None;
        else:
            self.AggregationPartition = _MODELLER.emmebank.partition(xtmf_AggregationPartition)                    

        try:
            if self.ExportTransferMatrixFlag or self.ExportWalkAllWayMatrixFlag:
                
                if self.ExportTransferMatrixFlag and not self.TransferMatrixFile:
                    raise IOError("No transfer matrix file specified.")
                
                if self.ExportWalkAllWayMatrixFlag:                    
                    if not self.WalkAllWayExportFile: raise TypeError("No walk-all-way matrix file specified")
                    
                self._Execute()     
                                                           
        except Exception as e:                        
            raise
    
    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()
                
    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
    
    @_m.method(return_type= bool)
    def class_name_is_required(self):
        return self._GetAssignmentType() == 'MULTICLASS_TRANSIT_ASSIGNMENT'
    
    @_m.method(return_type=unicode)
    def preload_class_names(self):
        classInfo = self._LoadClassInfo()
        options = []
        for name, alpha in classInfo:
            options.append("<option value='%s'>%s</option>" %(name, name))
        return "\n".join(options)
    
    @_m.method(return_type=unicode)
    def preload_line_group_options(self):
        options = [(1, "<em>Pre-built:</em> NCS11 operator codes"),
                   (2, "<em>Pre-built:</em> Alphabetical by operator"),
                   (3, "<em>Pre-built:</em> Alphabetical by operator and mode"),
                   (4, "<em>Pre-built:</em> GTAModel V4 line groups"),
                   (5, "<em>Pre-built:</em> GTAModel V4 groups + TTC prem. bus"),
                   ("<em>Custom:</em> ut1", "Group IDs stored in UT1"),
                   ("<em>Custom:</em> ut2", "Group IDs stored in UT2"),
                   ("<em>Custom:</em> ut3", "Group IDs stored in UT3")]
        
        for exatt in self.Scenario.extra_attributes():
            if exatt.type != "TRANSIT_LINE": continue
            text = "<em>Custom:</em> Group IDs stored in %s - %s" %(exatt.name, exatt.description)
            options.append((exatt.name, text))
        
        options = ["<option value=%s>%s</option>" %tup for tup in options]
        return "\n".join(options)
    
    @_m.method(return_type= bool)
    def preload_transfer_matrix_flag(self):
        return self.ExportTransferMatrixFlag
    
    @_m.method(return_type= bool)
    def preload_waw_matrix_flag(self):
        return self.ExportWalkAllWayMatrixFlag
    ##########################################################################################################    
    
    #---
    #---MAIN EXECUTION CODE
    
    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(classname=(self.__class__.__name__), version=self.version),
                                     attributes=self._GetAtts()):
            
            managers = [_util.tempMatrixMANAGER("Walk-all-way matrix"),
                        _util.tempExtraAttributeMANAGER(self.Scenario, 'TRANSIT_SEGMENT',
                                                        description= "Initial boardings attribute"),
                        _util.tempExtraAttributeMANAGER(self.Scenario, 'TRANSIT_SEGMENT',
                                                        description= "Final alightings attribute"),
                        getTemporaryFolder()]
            
            #Handle creating the transit line attribute in which to flag groups (if required). 
            if self.LineGroupOptionOrAttributeId.isdigit():
                managers.insert(0, _util.tempExtraAttributeMANAGER(self.Scenario, 'TRANSIT_LINE',
                                                                   description= "Line group ID",
                                                                   returnId= True))
                lineGroupOption = int(self.LineGroupOptionOrAttributeId)
            else:
                managers.insert(0, blankContextManager(self.LineGroupOptionOrAttributeId))
                lineGroupOption = 0
            
            with nested(*managers) as (lineGroupAtributeID, walkAllWayMatrix, 
                                       boardAttribute, alightAttribute,
                                       tempFolder):
                
                #---1. Flag pre-built groupings, if needed
                if lineGroupOption and self.ExportTransferMatrixFlag:
                    with _m.logbook_trace("Applying pre-built groupings"):
                        self._ApplyPreBuiltCodes(lineGroupOption, lineGroupAtributeID)
                self.TRACKER.completeTask()
                
                #---2. Get the traversal matrix
                if self.ExportTransferMatrixFlag:
                    transferMatrix = self._GetTraversalMatrix(lineGroupAtributeID, tempFolder)
                    
                    
                    #---3. Attach initial boarding and final alighting data to matrix
                    self._GetBoardingsAndAlightings(transferMatrix, lineGroupAtributeID, 
                                                    boardAttribute.id, alightAttribute.id)
                    print "Loaded initial boardings and final alightings"
                else: transferMatrix = None 
                
                #---4. Get the walk-all-way matrix
                self._GetWalkAllWayMatrix(walkAllWayMatrix, transferMatrix)
                print "Loaded walk all-way matrix"
                
                #---5. Export the transfer matrix
                if self.ExportTransferMatrixFlag:
                    self._WriteExportFile(transferMatrix)
                
                #---6. Export the aggregated walk-all-way matrix (if desired)
                if self.ExportWalkAllWayMatrixFlag:
                    self._ExportWalkAllWayMatrix(walkAllWayMatrix)
                

    ##########################################################################################################
    
    
    #----Sub functions
    
    def _GetAtts(self):
        atts = {
                "Scenario" : str(self.Scenario.id),
                "Version": self.version, 
                "self": self.__MODELLER_NAMESPACE__}
            
        return atts
    
    def _GetAssignmentType(self):
        if not self.Scenario.has_transit_results: return None
        
        configPath = dirname(_MODELLER.desktop.project_file_name()) \
                    + "/Database/STRATS_s%s/config" %self.Scenario
        
        if not exists(configPath): return self.Scenario.transit_assignment_type
        
        with open(configPath) as reader:
            config = _parsedict(reader.read())
        
        data = config['data']
        return data['type']
        
    def _GetMulticlass(self):
        if not self.Scenario.has_transit_results: return None
        
        configPath = dirname(_MODELLER.desktop.project_file_name()) \
                    + "/Database/STRATS_s%s/config" %self.Scenario
        
        if not exists(configPath): return self.Scenario.transit_assignment_type
        
        with open(configPath) as reader:
            config = _parsedict(reader.read())
        
        data = config['data']
        if 'multi_class' in data:
            return data['multi_class']
        else:
            return False

    def _LoadClassInfo(self):
        
        if not self.Scenario.has_transit_results: return []
        
        configPath = dirname(_MODELLER.desktop.project_file_name()) \
                    + "/Database/STRATS_s%s/config" %self.Scenario
        
        if not exists(configPath): return []
        
        with open(configPath) as reader:
            config = _parsedict(reader.read())
        
        classes = []
        for info in config['strat_files']:
            className = info['name']

            if(info['data'] is not None):
                if not 'alpha' in info['data']:
                    alpha = 0.0
                else: 
                    alpha = info['data']['alpha']
            classes.append((className, alpha))
        
        return classes

    def _LoadClassNames(self):
        
        if not self.Scenario.has_transit_results: return []
        
        configPath = dirname(_MODELLER.desktop.project_file_name()) \
                    + "/Database/STRATS_s%s/config" %self.Scenario
        
        if not exists(configPath): return []
        
        with open(configPath) as reader:
            config = _parsedict(reader.read())
        
        classes = []
        if self._GetAssignmentType() == 'MULTICLASS_TRANSIT_ASSIGNMENT':
            for classz in config['strat_files']:
                className = classz['name']
                classes.append(className)
        else:
            info = config['data']
            for classz in info['classes']:
                className = classz['name']
                classes.append(className)
        
        return classes
    
    def _ApplyPreBuiltCodes(self, option, attributeId):
        options = {1: LINE_GROUPS_NCS11,
                   2: LINE_GROUPS_ALPHA_OP,
                   3: LINE_GROUPS_ALPHA_OP_MODE,
                   4: LINE_GROUPS_GTAMV4,
                   5: LINE_GROUPS_GTAMV4_PREM}
        
        def flagGroup(value, selector, descr):
                spec= {
                        "result": attributeId,
                        "expression": str(value),
                        "aggregation": None,
                        "selections": {
                            "transit_line": selector
                        },
                        "type": "NETWORK_CALCULATION"
                    }
                with _m.logbook_trace("Flagging %s lines as %s" %(descr, value)):
                    networkCalculator(spec, scenario=self.Scenario)
        
        groupings = options[option]
        self.TRACKER.startProcess(len(groupings))
        for groupId, selector, name in groupings:
            flagGroup(groupId, selector, name)
            self.TRACKER.completeSubtask()
    
    def _GetTraversalMatrix(self, lineGroupAtributeID, tempFolder):
        #---2. Load class / iteration information for traversal analysis
        if self._GetAssignmentType() == "MULTICLASS_TRANSIT_ASSIGNMENT" or self._GetMulticlass() == True:
            classWeights = [(self.ClassName, 1.0)]
        elif self._GetAssignmentType() == "CONGESTED_TRANSIT_ASSIGNMENT":
            if EMME_VERSION >= (4,1,0):
                classWeights = []
            else:
                classWeights = self._LoadClassInfo()
        else:
            classWeights = []
        
        #---3. Run the traversal analysis tool for each class (if needed)
        files = {}
        nTasks = max(len(classWeights), 1)
        self.TRACKER.startProcess(nTasks)
        if classWeights:
            for className, weight in classWeights:
                filepath = _tf.mktemp(".csv", dir= tempFolder)
                files[className] = filepath
                self._RunTraversalAnalysis(lineGroupAtributeID, filepath, className)
                self.TRACKER.completeSubtask()
        else:
            filepath = _tf.mktemp(".csv", dir= tempFolder)
            self._RunTraversalAnalysis(lineGroupAtributeID, filepath)
            files = filepath
        self.TRACKER.completeTask()
        print "Processed traversal matrices"
        
        #---4. Load or load and combine traversal matrices
        self.TRACKER.startProcess(nTasks)
        if classWeights:
            transferMatrix = {}
            for className, weight in classWeights:
                filepath = files[className]
                classMatrix = self._ParseTraversalResults(filepath)
                for key, value in classMatrix.iteritems():
                    weightedValue = weight * value
                    if key in transferMatrix: transferMatrix[key] += weightedValue
                    else: transferMatrix[key] = weightedValue
                self.TRACKER.completeSubtask()
                print "Loaded class %s" %className
        else:
            filepath = files
            transferMatrix = self._ParseTraversalResults(filepath) 
        self.TRACKER.completeTask()
        print "Aggregated transfer matrix."
        
        return transferMatrix
        
    
    def _RunTraversalAnalysis(self, attributeId, filepath, className= None):
        spec = {
                "portion_of_path": "COMPLETE",
                "gates_by_trip_component": {
                    "in_vehicle": None,
                    "aux_transit": None,
                    "initial_boarding": None,
                    "transfer_boarding": attributeId,
                    "transfer_alighting": attributeId,
                    "final_alighting": None
                },
                "analyzed_demand": self.DemandMatrixId,
                "path_analysis": None,
                "type": "EXTENDED_TRANSIT_TRAVERSAL_ANALYSIS"
            }
        
        if self.DemandMatrixId is not None:
            spec['constraint'] = {
                                    "by_value": {
                                        "interval_min": 0,
                                        "interval_max": 0,
                                        "condition": "EXCLUDE",
                                        "od_values": self.DemandMatrixId
                                    },
                                    "by_zone": None
                                }
        else:
            spec['constraint'] = None
        
        print "Running traversal analysis on class %s" %className
        if className is not None:
            traversalAnalysisTool(spec, filepath, 
                                  scenario= self.Scenario,
                                  class_name= className)
        else:
            traversalAnalysisTool(spec, filepath,
                                  scenario= self.Scenario)
            
    def _ParseTraversalResults(self, filepath):
        retval = {}
        
        with open(filepath) as reader:
            line = ""
            while not line.startswith("a"):
                line = reader.readline()
            for line in reader:
                cells = line.strip().split()
                if len(cells) != 3: continue
                o = int(cells[0])
                d = int(cells[1])
                try:
                    val = float(cells[2])
                except ValueError:
                    val = cells[2].replace('u', '.')
                    val = float(val)
                od = (o,d)
                retval[od] = val
        return retval
    
    def _GetBoardingsAndAlightings(self, transferMatrix, lineGroupAtributeID, boardingAttributeId,
                                   alightingAttributeId):
        self._RunNetworkResults(boardingAttributeId, alightingAttributeId)
        
        self._LoadBoardingsAndAlightings(lineGroupAtributeID, boardingAttributeId, alightingAttributeId,
                                         transferMatrix)
    
    def _RunNetworkResults(self, boardingAttributeId, alightingAttributeId):        
        spec= {
                "on_links": None,
                "on_segments": {
                    "initial_boardings": boardingAttributeId,
                    "final_alightings": alightingAttributeId
                },
                "aggregated_from_segments": None,
                "analyzed_demand": self.DemandMatrixId,
                "constraint": None,
                "type": "EXTENDED_TRANSIT_NETWORK_RESULTS"
            }
        
        if self.ClassName:
            self.TRACKER.runTool(networkResultsTool, spec, self.Scenario, class_name= self.ClassName)
        else:
            self.TRACKER.runTool(networkResultsTool, spec, self.Scenario)
    
    def _LoadBoardingsAndAlightings(self, lineGroupAtributeID, boardingAttributeId, alightingAttributeId, 
                                   transferMatrix):
        
        lineGroups = _util.fastLoadTransitLineAttributes(self.Scenario, [lineGroupAtributeID])
        lineActivity = _util.fastLoadSummedSegmentAttributes(self.Scenario, [boardingAttributeId,
                                                                         alightingAttributeId])
        
        groupActivity = {}
        
        for lineId, lineAtts in lineGroups.iteritems():
            groupId = int(lineAtts[lineGroupAtributeID])
            
            if lineId in lineActivity:
                initialBoardings = lineActivity[lineId][boardingAttributeId]
                finalAlightings = lineActivity[lineId][alightingAttributeId]
            else:
                initialBoardings = 0.0
                finalAlightings = 0.0
            
            if groupId in groupActivity:
                activity = groupActivity[groupId]
                activity[0] += initialBoardings
                activity[1] += finalAlightings
            else:
                groupActivity[groupId] = [initialBoardings, finalAlightings]
        
        for groupId, activity in groupActivity.iteritems():
            initialBoardings, finalAlightings = activity
            
            transferMatrix[(0, groupId)] = initialBoardings
            transferMatrix[(groupId, 0)] = finalAlightings
        
        self.TRACKER.completeTask() 
    
    def _GetWalkAllWayMatrix(self, wawMatrix, transferMatrix):
        self._RunStrategyAnalysis(wawMatrix.id)
        
        wawSum = self._SumWalkAllWayMatrix(wawMatrix.id)
        
        if self.ExportTransferMatrixFlag:
            transferMatrix[(0,0)] = wawSum 
    
    def _RunStrategyAnalysis(self, wawMatrixId):
        spec = {
                    "trip_components": {
                        "boarding": None,
                        "in_vehicle": "length",
                        "aux_transit": None,
                        "alighting": None
                    },
                    "sub_path_combination_operator": "+",
                    "sub_strategy_combination_operator": ".min.",
                    "selected_demand_and_transit_volumes": {
                        "sub_strategies_to_retain": "FROM_COMBINATION_OPERATOR",
                        "selection_threshold": {
                            "lower": 0,
                            "upper": 0
                        }
                    },
                    "analyzed_demand": self.DemandMatrixId,
                    "constraint": None,
                    "results": {
                        "strategy_values": None,
                        "selected_demand": wawMatrixId,
                        "transit_volumes": None,
                        "aux_transit_volumes": None,
                        "total_boardings": None,
                        "total_alightings": None
                    },
                    "type": "EXTENDED_TRANSIT_STRATEGY_ANALYSIS"
                }
        
        if self.ClassName:
            strategyAnalysisTool(spec, scenario= self.Scenario, class_name= self.ClassName)
        else:
            strategyAnalysisTool(spec, scenario= self.Scenario)
    
    def _SumWalkAllWayMatrix(self, wawMatrixId):
        spec = {
                "expression": wawMatrixId,
                "result": None,
                "constraint": {
                    "by_value": None,
                    "by_zone": None
                },
                "aggregation": {
                    "origins": "+",
                    "destinations": "+"
                },
                "type": "MATRIX_CALCULATION"
            }
        
        if EMME_VERSION >= (4,2,1):
            return self.TRACKER.runTool(matrixCalculator, spec, scenario=self.Scenario,
                                             num_processors=self.NumberOfProcessors)['result']
        else:
            return self.TRACKER.runTool(matrixCalculator, spec, scenario=self.Scenario)['result']
        
    def _WriteExportFile(self, transferMatrix):
        with open(self.TransferMatrixFile, 'w') as writer:
            headerSet = set()
            for origin, destination in transferMatrix.iterkeys():
                headerSet.add(origin)
                headerSet.add(destination)
            headers = [h for h in headerSet]
            headers.sort()
            
            header = ",".join( [""] + [str(h) for h in headers])
            writer.write(header)
            
            for origin in headers:
                row = [str(origin)]
                for destination in headers:
                    key = (origin, destination)
                    if key in transferMatrix:
                        value = transferMatrix[key]
                    else:
                        value = 0.0
                    row.append(str(value))
                writer.write("\n" + ",".join(row))
    
    def _ExportWalkAllWayMatrix(self, walkAllWayMatrix):
        partSpec = {'origins': self.AggregationPartition.id,
                    'destinations': self.AggregationPartition.id,
                    'operator': 'sum'}
        matrixExportTool([walkAllWayMatrix],
                         partition_aggregation= partSpec,
                         export_file= self.WalkAllWayExportFile,
                         field_separator= ',',
                         full_matrix_line_format= 'SQUARE', scenario = self.Scenario)
class BasicTransitAssignment(_m.Tool()):

    version = '0.1.0'
    tool_run_msg = ""

    #---Variable definitions
    ScenarioNumber = _m.Attribute(int)
    DemandMatrixNumber = _m.Attribute(int)
    ModeString = _m.Attribute(str)

    WaitPerception = _m.Attribute(float)  #
    WalkPerception = _m.Attribute(float)  #
    InVehiclePerception = _m.Attribute(float)  #
    BoardingPerception = _m.Attribute(float)  #

    UseAdditiveDemand = _m.Attribute(bool)  #
    UseEM4Options = _m.Attribute(bool)  # Not yet used. Future proofing.
    WaitFactor = _m.Attribute(float)  #

    #---Special instance types
    scenario = _m.Attribute(_m.InstanceType)  #
    demandMatrix = _m.Attribute(_m.InstanceType)  #
    modes = _m.Attribute(_m.ListType)

    #---Internal variables
    _modeList = []  #

    def __init__(self):
        self._tracker = _util.ProgressTracker(2)
        self.WaitFactor = 0.5
        self.UseAdditiveDemand = False
        self.UseEM4Options = False

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Basic Transit Assignment v%s" % self.version,
            description=
            "Executes a basic transit assignment. Boarding penalties are \
                         assumed to be loaded into <b>UT3</b>.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_header("ASSIGNMENT OPTIONS")
        #----------------------------------
        pb.add_select_scenario(tool_attribute_name='scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_select_matrix(
            tool_attribute_name='demandMatrix',
            title='Demand Matrix:',
            note="If no matrix is selected, a scalar matrix of value '0' will\
                             be assigned.",
            allow_none=True)

        pb.add_select_mode(tool_attribute_name='modes',
                           filter=['TRANSIT', 'AUX_TRANSIT'],
                           allow_none=False,
                           title='Modes:')

        pb.add_checkbox(tool_attribute_name='UseAdditiveDemand',
                        title="Use additive demand?")

        pb.add_header("PERCEPTION FACTORS")
        #----------------------------------
        pb.add_text_box(tool_attribute_name='WaitFactor',
                        size=4,
                        title='Wait factor:',
                        note='Default is 0.5')

        pb.add_text_box(tool_attribute_name='WaitPerception',
                        size=4,
                        title='Wait time perception:')

        pb.add_text_box(tool_attribute_name='WalkPerception',
                        size=4,
                        title='Walk perception:')

        pb.add_text_box(tool_attribute_name='InVehiclePerception',
                        size=4,
                        title='In-vehicle perception:')

        pb.add_text_box(tool_attribute_name='BoardingPerception',
                        size=4,
                        title='Boarding perception:')

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""

        #Run is called from Modeller.
        self.isRunningFromXTMF = False

        # Fix the checkbox problem
        if self.UseAdditiveDemand is None:  #If the checkbox hasn't been clicked, this variable will be set to None by Modeller
            self.UseAdditiveDemand = False

        # Convert the list of mode objects to a list of mode characters
        for m in self.modes:
            self._modeList.append(m.id)

        # Execute
        try:
            self._execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")

    def __call__(self, ScenarioNumber, DemandMatrixNumber, ModeString,
                 WaitPerception, WalkPerception, InVehiclePerception,
                 BoardingPerception, UseAdditiveDemand, WaitFactor,
                 UseEM4Options):

        #---1 Set up scenario
        self.scenario = _m.Modeller().emmebank.scenario(ScenarioNumber)
        if (self.scenario is None):
            raise Exception("Scenario %s was not found!" % ScenarioNumber)

        #---2 Set up Demand matrix
        if DemandMatrixNumber == 0:
            self.demandMatrix = None
        else:
            self.demandMatrix = _m.Modeller().emmebank.matrix(
                "mf%s" % DemandMatrixNumber)
            if self.demandMatrix is None:
                raise Exception("Matrix %s does not exist!" %
                                DemandMatrixNumber)

        #---3 Set up modes
        for i in range(0, len(ModeString)):
            if not self._modeList.__contains__(ModeString[i]):
                self._modeList.append(ModeString[i])

        #---4 Pass in remaining args
        self.WaitPerception = WaitPerception
        self.WalkPerception = WalkPerception
        self.InVehiclePerception = InVehiclePerception
        self.BoardingPerception = BoardingPerception
        self.UseAdditiveDemand = UseAdditiveDemand
        self.WaitFactor = WaitFactor
        self.UseEM4Options = UseEM4Options

        self.isRunningFromXTMF = True

        try:
            self._execute()
        except Exception as e:
            raise Exception(_traceback.format_exc())

    ##########################################################################################################

    def _execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._getAtts()):

            self._tracker.completeTask()

            try:
                transitAssignmentTool = _m.Modeller().tool(
                    "inro.emme.standard.transit_assignment.extended_transit_assignment"
                )
            except Exception as e:
                transitAssignmentTool = _m.Modeller().tool(
                    "inro.emme.transit_assignment.extended_transit_assignment")

            with self._demandMatrixMANAGER():

                self._tracker.runTool(
                    transitAssignmentTool,
                    self._getAssignmentSpec(),  # Specification
                    self.scenario,  # Scenario
                    self.UseAdditiveDemand)  # Use additional volumes
        self._tracker.reset()

    ##########################################################################################################

    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''

    @contextmanager
    def _demandMatrixMANAGER(self):
        #Code here is executed upon entry

        usingScalar = False
        if self.demandMatrix is None:
            _m.logbook_write("Initializing temporary scalar demand matrix.")
            #def initializeMatrix(id=None, default=0, name="", description="", matrix_type='FULL'):
            self.demandMatrix = _util.initializeMatrix(
                matrix_type='SCALAR',
                name='trscal',
                description="Scalar matrix to get transit times")

            if self.demandMatrix is None:
                raise Exception(
                    "Could not create temporary scalar demand matrix!")

            usingScalar = True

        try:
            yield
            # Code here is executed upon clean exit
        finally:
            # Code here is executed in all cases.
            if usingScalar == True:
                _m.logbook_write("Deleting temporary scalar demand matrix.")
                _m.Modeller().emmebank.delete_matrix(self.demandMatrix.id)

    #----SUB FUNCTIONS---------------------------------------------------------------------------------
    '''
    ScenarioNumber = _m.Attribute(int)
    DemandMatrixNumber = _m.Attribute(int)
    ModeString = _m.Attribute(str)
    
    WaitPerception = _m.Attribute(float) #
    WalkPerception = _m.Attribute(float) #
    InVehiclePerception = _m.Attribute(float) #
    BoardingPerception = _m.Attribute(float) #
    
    UseAdditiveDemand = _m.Attribute(bool) #
    UseEM4Options = _m.Attribute(bool) # Not yet used. Future proofing.
    WaitFactor = _m.Attribute(float) #
    
    #---Special instance types
    scenario = _m.Attribute(_m.InstanceType) #
    demandMatrix = _m.Attribute(_m.InstanceType) #
    modes = _m.Attribute(_m.ListType)
    
    #---Internal variables
    _modeList = [] #
    '''

    def _getAtts(self):
        atts = {
            "Scenario": str(self.scenario.id),
            "Version": self.version,
            "Demand Matrix": str(self.demandMatrix),
            "Modes": str(self._modeList),
            "Wait Perception": self.WaitPerception,
            "Walk Perception": self.WalkPerception,
            "In Vehicle Perception": self.InVehiclePerception,
            "Boarding Perception": self.BoardingPerception,
            "Headway Fraction": self.WaitFactor,
            "Use Additive Demand Flag": self.UseAdditiveDemand,
            "Use Emme 4 Options Flag": self.UseEM4Options,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _getAssignmentSpec(self):
        spec = {
            "modes": self._modeList,  #---MODES
            "demand": self.demandMatrix.id,  #---DEMAND MATRIX
            "waiting_time": {
                "headway_fraction": self.WaitFactor,  #---WAIT FACTOR
                "effective_headways": "hdw",
                "spread_factor": 1,
                "perception_factor": self.WaitPerception  #---WAIT PERCEPTION
            },
            "boarding_time": {
                "at_nodes": None,
                "on_lines": {
                    "penalty": "ut3",  #---BOARDING ATTRIBUTE
                    "perception_factor":
                    self.BoardingPerception  #---BOARDING PERCEPTION
                }
            },
            "boarding_cost": {
                "at_nodes": {
                    "penalty":
                    0,  # For some reason, I can't just leave this blank.
                    "perception_factor": 0
                },
                "on_lines": None
            },
            "in_vehicle_time": {
                "perception_factor":
                self.InVehiclePerception  #---IVTT PERCEPTION
            },
            "in_vehicle_cost": None,  #---IN VEHICLE FARES
            "aux_transit_time": {
                "perception_factor": self.WalkPerception  #---WALK PERCEPTION
            },
            "aux_transit_cost": None,
            "flow_distribution_at_origins": {
                "by_time_to_destination": "BEST_CONNECTOR",
                "by_fixed_proportions": None
            },
            "flow_distribution_between_lines": {
                "consider_travel_time": False
            },
            "connector_to_connector_path_prohibition": None,
            "save_strategies": True,
            "od_results": None,
            "type": "EXTENDED_TRANSIT_ASSIGNMENT"
        }
        if EMME_VERSION[0] + 0.1 * EMME_VERSION[1] >= 4.1:
            spec["performance_settings"] = {
                "number_of_processors": cpu_count()
            }
        return spec

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self._tracker.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
class ExportBinaryMatrix(_m.Tool()):

    version = '1.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    MATRIX_TYPES = {1: 'ms', 2: 'mo', 3: 'md', 4: 'mf'}

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):
        return ""

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg

    @_m.method(return_type=bool)
    def scenario_required(self):
        retval = _util.databankHasDifferentZones(_bank)
        print retval
        return retval

    #---
    #---XTMF INTERFACE METHODS

    def run_xtmf(self, parameters):
        # xtmf_MatrixType, xtmf_MatrixNumber, ExportFile, xtmf_ScenarioNumber
        xtmf_MatrixType = parameters["matrix_type"]
        xtmf_MatrixNumber = parameters["matrix_number"]
        self.ExportFile = parameters["file_location"]
        xtmf_ScenarioNumber = parameters["scenario_number"]
        if not xtmf_MatrixType in self.MATRIX_TYPES:
            raise IOError(
                "Matrix type '%s' is not recognized. Valid types are " %
                xtmf_MatrixType +
                "1 for scalar, 2 for origin, 3 for destination, and " +
                "4 for full matrices.")

        self.MatrixId = self.MATRIX_TYPES[xtmf_MatrixType] + str(
            xtmf_MatrixNumber)
        if _bank.matrix(self.MatrixId) == None:
            raise IOError("Matrix %s does not exist." % self.MatrixId)

        if _util.databankHasDifferentZones(_bank):
            self.Scenario = _bank.scenario(xtmf_ScenarioNumber)
            if self.Scenario == None:
                raise Exception(
                    "A valid scenario must be specified as there are " +
                    "multiple zone systems in this Emme project. " +
                    "'%s' is not a valid scenario." % xtmf_ScenarioNumber)

        try:
            self._Execute()
        except Exception, e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            raise Exception(msg)
Beispiel #9
0
class LegacyFBTA(_m.Tool()):

    version = '0.2.2'
    tool_run_msg = ""

    # Variables marked with a '#' are used in the main block, and are assigned by both run and call

    #---Variable definitions
    ScenarioNumber = _m.Attribute(int)
    DemandMatrixNumber = _m.Attribute(int)
    ModeString = _m.Attribute(str)

    WalkSpeed = _m.Attribute(float)  #
    WaitPerception = _m.Attribute(float)  #
    WalkPerception = _m.Attribute(float)  #
    InVehiclePerception = _m.Attribute(float)  #
    BoardingPerception = _m.Attribute(float)  #

    FarePerception = _m.Attribute(float)  #
    '''To disable fare-based impedances, set this parameter to 0.0'''

    UseAdditiveDemand = _m.Attribute(bool)  #
    WaitFactor = _m.Attribute(float)  #

    #---Special instance types, used only from Modeller
    scenario = _m.Attribute(_m.InstanceType)  #
    demandMatrix = _m.Attribute(_m.InstanceType)  #
    modes = _m.Attribute(_m.ListType)

    #---Private vars
    _appliedFareFactor = 0  #
    _modeList = []  #

    def __init__(self):
        # Set up all Emme tools and data structures
        self.databank = _m.Modeller().emmebank
        try:
            self.transitAssignmentTool = _m.Modeller().tool(
                "inro.emme.standard.transit_assignment.extended_transit_assignment"
            )
        except Exception as e:
            self.transitAssignmentTool = _m.Modeller().tool(
                "inro.emme.transit_assignment.extended_transit_assignment")

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Legacy Fare-Based Transit Assignment",
            description=
            "Executes a fare-based transit assignment (FBTA) as described \
                                 in the GTAModel Version 3 documentation: Using special functions \
                                 on centroid connectors and transit time functions. This requires \
                                 a compatible network, which can currently only be created by \
                                 <em>macro_EditNetwork.mac.</em><br><br>\
                                 This Tool can also be used to execute a more standard transit \
                                 assignment procedure by using a fare perception of <b>'0'</b>.\
                                 <br><br>This Tool executes an Extended Transit Assignment, which allows\
                                 for subsequent analyses; such as those that can be found in \
                                 <em>TMG2.Assignment.TransitAnalysis</em>.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_header("ASSIGNMENT SETUP")
        #----------------------------------
        pb.add_select_scenario(tool_attribute_name='scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_select_matrix(
            tool_attribute_name='demandMatrix',
            title='Demand Matrix:',
            note="If no matrix is selected, a scalar matrix of value '0' will\
                             be assigned.",
            allow_none=True)

        pb.add_select_mode(tool_attribute_name='modes',
                           filter=['TRANSIT', 'AUX_TRANSIT'],
                           allow_none=False,
                           title='Modes:')

        pb.add_checkbox(tool_attribute_name='UseAdditiveDemand',
                        title="Use additive demand?")

        pb.add_header("PERCEPTION FACTORS")
        #----------------------------------

        pb.add_text_box(tool_attribute_name='WaitFactor',
                        size=4,
                        title='Wait factor:',
                        note='Default is 0.5')

        pb.add_text_box(tool_attribute_name='WaitPerception',
                        size=4,
                        title='Wait time perception:')

        pb.add_text_box(tool_attribute_name='WalkSpeed',
                        size=6,
                        title='Walk speed:',
                        note='In km/hr')

        pb.add_text_box(tool_attribute_name='WalkPerception',
                        size=4,
                        title='Walk perception:')

        pb.add_text_box(tool_attribute_name='InVehiclePerception',
                        size=4,
                        title='In-vehicle perception:')

        pb.add_text_box(tool_attribute_name='BoardingPerception',
                        size=4,
                        title='Boarding perception:')

        pb.add_text_box(tool_attribute_name='FarePerception',
                        size=6,
                        title='Fare perception:',
                        note="Enter '0' to disable fare-based impedances.")

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""

        #Run is called from Modeller.
        self.isRunningFromXTMF = False

        # Fix the checkbox problem
        if self.UseAdditiveDemand is None:  #If the checkbox hasn't been clicked, this variable will be set to None by Modeller
            self.UseAdditiveDemand = False

        # Convert the list of mode objects to a list of mode characters
        for m in self.modes:
            self._modeList.append(m.id)

        # Execute
        try:
            self._execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")

    def __call__(self, ScenarioNumber, DemandMatrixNumber, ModeString, WalkSpeed, WaitPerception, \
                 WalkPerception, InVehiclePerception, BoardingPerception, FarePerception, \
                 UseAdditiveDemand, WaitFactor):

        #---1 Set up scenario
        self.scenario = _m.Modeller().emmebank.scenario(ScenarioNumber)
        if (self.scenario is None):
            raise Exception("Scenario %s was not found!" % ScenarioNumber)

        #---2 Set up Demand matrix
        if DemandMatrixNumber == 0:
            self.demandMatrix = None
        else:
            self.demandMatrix = self.databank.matrix("mf%s" %
                                                     DemandMatrixNumber)
            if self.demandMatrix is None:
                raise Exception("Matrix %s does not exist!" %
                                DemandMatrixNumber)

        #---3 Set up modes
        for i in range(0, len(ModeString)):
            if not self._modeList.__contains__(ModeString[i]):
                self._modeList.append(ModeString[i])

        #---4 Pass in remaining args
        self.WalkSpeed = WalkSpeed
        self.WaitPerception = WaitPerception
        self.WalkPerception = WalkPerception
        self.InVehiclePerception = InVehiclePerception
        self.BoardingPerception = BoardingPerception
        self.FarePerception = FarePerception
        self.UseAdditiveDemand = UseAdditiveDemand
        self.WaitFactor = WaitFactor

        self.isRunningFromXTMF = True

        #---5 Execute the tool
        try:
            self._execute()
        except Exception as e:
            raise Exception(_traceback.format_exc(e))

    ##########################################################################################################

    def _execute(self):
        with _m.logbook_trace(name="Legacy Fare Based Transit Assignment v%s" %
                              self.version,
                              attributes=self._getAtts()):

            self._calculateFareFactor()

            with nested(self._demandMatrixMANAGER(), self._walkLinksMANAGER(),
                        self._transitFunctionsMANAGER()):
                self._calculateUL1()
                _m.logbook_write(name="Running extended transit assignment")
                self.transitAssignmentTool(
                    self._setUpAssignment(),  # Specification
                    self.scenario,  # Scenario
                    self.UseAdditiveDemand)  # Use additional volumes

            # Cleanup is handled automatically.

    ##########################################################################################################

    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''

    @contextmanager
    def _demandMatrixMANAGER(self):
        #Code here is executed upon entry

        usingScalar = False
        if self.demandMatrix is None:
            _m.logbook_write("Initializing temporary scalar demand matrix.")

            self.demandMatrix = _util.initializeMatrix(
                matrix_type='SCALAR',
                name='trscal',
                description="Scalar matrix to get transit times")

            if self.demandMatrix is None:
                raise Exception(
                    "Could not create temporary scalar demand matrix!")

            usingScalar = True

        try:
            yield
            # Code here is executed upon clean exit
        finally:
            # Code here is executed in all cases.
            if usingScalar == True:
                _m.logbook_write("Deleting temporary scalar demand matrix.")
                self.databank.delete_matrix(self.demandMatrix.id)

    @contextmanager
    def _walkLinksMANAGER(self):
        #Code here is executed upon entry

        with _m.logbook_trace("Changing speed of modes tuv."):
            net = self.scenario.get_network()
            tMode = net.mode('t')
            uMode = net.mode('u')
            vMode = net.mode('v')

            tSpeed = tMode.speed
            uSpeed = uMode.speed
            vSpeed = vMode.speed

            tMode.speed = 'ul1*1.0'
            _m.logbook_write("Changed speed of mode 't' to 'ul1*1.0'.")
            uMode.speed = 'ul1*1.0'
            _m.logbook_write("Changed speed of mode 'u' to 'ul1*1.0'.")
            vMode.speed = 'ul1*1.0'
            _m.logbook_write("Changed speed of mode 'u' to 'ul1*1.0'.")

            self.scenario.publish_network(net)
            _m.logbook_write("Changes saved to databank.")

        try:
            yield
            # Code here is executed upon clean exit
        finally:
            # Code here is executed in all cases.
            with _m.logbook_trace("Resetting modes tuv."):
                net = self.scenario.get_network()
                tMode = net.mode('t')
                uMode = net.mode('u')
                vMode = net.mode('v')

                tMode.speed = str(tSpeed)
                _m.logbook_write("Reset speed of mode 't' to '%s'." % tSpeed)
                uMode.speed = str(uSpeed)
                _m.logbook_write("Reset speed of mode 'u' to '%s'." % uSpeed)
                vMode.speed = str(vSpeed)
                _m.logbook_write("Reset speed of mode 'v' to '%s'." % vSpeed)

                self.scenario.publish_network(net)
                _m.logbook_write("Changes saved to databank.")

    @contextmanager
    def _transitFunctionsMANAGER(self):
        #Code here is executed upon entry

        functionChanger = None  #Use the standard tools; it is faster and more verbose.
        try:
            functionChanger = _m.Modeller().tool(
                "inro.emme.data.function.change_function")
        except Exception as e:
            functionChanger = _m.Modeller().tool(
                "inro.emme.standard.data.function.change_function")

        expressionArchive = {}
        with _m.logbook_trace("Modifying transit time functions."):
            for f in self.databank.functions():
                if f.type == "TRANSIT_TIME":
                    expressionArchive[f.id] = f.expression
                    functionChanger(
                        f, f.expression + " + (us3 * %s)" %
                        (self._appliedFareFactor))
                    #f.expression += " + (us3 * %s)" %(self._appliedFareFactor)

        try:
            yield
            # Code here is executed upon clean exit
        finally:
            # Code here is executed in all cases.
            with _m.logbook_trace("Resetting transit time functions."):
                for item in expressionArchive.iteritems():
                    f = self.databank.function(item[0])
                    functionChanger(f, item[1])
                    #f.expression = item[1]

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _getAtts(self):
        atts = {
            "Scenario": str(self.scenario.id),
            "ModeString": str(self._modeList),
            "Walk Speed": str(self.WalkSpeed),
            "Wait Perception": self.WaitPerception,
            "Walk Perception": self.WalkPerception,
            "IVTT Perception": self.InVehiclePerception,
            "Boarding Perception": self.BoardingPerception,
            "Fare Perception": self.FarePerception,
            "Is using additive demand?": self.UseAdditiveDemand,
            "Wait Factor": self.WaitFactor,
            "Is running from XTMF?": str(self.isRunningFromXTMF),
            "self": self.__MODELLER_NAMESPACE__
        }

        if self.demandMatrix is None:
            atts['Demand Matrix'] = "SCALAR"
        else:
            atts['Demand Matrix'] = "FULL: %s" % self.demandMatrix.id

        return atts

    def _reportProgress(self, current, total):
        if self.isRunningFromXTMF:
            self.XTMFBridge.ReportProgress(float(
                float(current) / float(total)))

    def _calculateFareFactor(self):
        self._appliedFareFactor = 0
        if self.FarePerception != 0:
            self._appliedFareFactor = 60.0 / self.FarePerception

    def _calculateUL1(self):
        _m.logbook_write("Calculating UL1 for tuv links.")

        networkCalculator = None  #Use the standard tools; it is faster and more verbose.
        try:
            networkCalculator = _m.Modeller().tool(
                "inro.emme.network_calculation.network_calculator")
        except Exception as e:
            networkCalculator = _m.Modeller().tool(
                "inro.emme.standard.network_calculation.network_calculator")

        # link.data1 = 60 / link.length + self._appliedFareFactor * link.__getattribute__('@tfare') / self.WalkPerception
        spec = {
            "result":
            "ul1",
            "expression":
            "(60 * length / {0}) + ({1} * @tfare / {2})".format(
                self.WalkSpeed, self._appliedFareFactor, self.WalkPerception),
            "aggregation":
            None,
            "selections": {
                "link": "modes=tuv"
            },
            "type":
            "NETWORK_CALCULATION"
        }

        networkCalculator(spec, scenario=self.scenario)

    def _setUpAssignment(self):

        # Ensure that modes t, u, and v are enabled
        if not self._modeList.__contains__('t'):
            self._modeList.append('t')
        if not self._modeList.__contains__('u'):
            self._modeList.append('u')
        if not self._modeList.__contains__('v'):
            self._modeList.append('v')

        spec = {
            "modes": self._modeList,  #---MODES
            "demand": self.demandMatrix.id,  #---DEMAND MATRIX
            "waiting_time": {
                "headway_fraction": self.WaitFactor,  #---WAIT FACTOR
                "effective_headways": "hdw",
                "spread_factor": 1,
                "perception_factor": self.WaitPerception  #---WAIT PERCEPTION
            },
            "boarding_time": {
                "at_nodes": None,
                "on_lines": {
                    "penalty": "ut3",  #---BOARDING ATTRIBUTE
                    "perception_factor":
                    self.BoardingPerception  #---BOARDING PERCEPTION
                }
            },
            "boarding_cost": {
                "at_nodes": {
                    "penalty":
                    0,  # For some reason, I can't just leave this blank.
                    "perception_factor": 0
                },
                "on_lines": None
            },
            "in_vehicle_time": {
                "perception_factor":
                self.InVehiclePerception  #---IVTT PERCEPTION
            },
            "in_vehicle_cost": None,  #---IN VEHICLE FARES
            "aux_transit_time": {
                "perception_factor": self.WalkPerception  #---WALK PERCEPTION
            },
            "aux_transit_cost": None,  #---WALK FARES
            "flow_distribution_at_origins": {
                "by_time_to_destination": "BEST_CONNECTOR",
                "by_fixed_proportions": None
            },
            "flow_distribution_between_lines": {
                "consider_travel_time": False
            },
            "connector_to_connector_path_prohibition": None,
            "save_strategies": True,
            "od_results": None,
            "type": "EXTENDED_TRANSIT_ASSIGNMENT"
        }
        if EMME_VERSION >= (4, 1, 0):
            spec["performance_settings"] = {
                "number_of_processors": cpu_count()
            }
        return spec

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
Beispiel #10
0
class SupplementalTransitMatrices(_m.Tool()):

    version = '0.0.2'
    tool_run_msg = ""
    number_of_tasks = 1  # 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__)

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    xtmf_PartitionId = _m.Attribute(str)
    xtmf_DemandMatrixId = _m.Attribute(str)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    def page(self):
        pb = _m.ToolPageBuilder(self,
                                title="Return Supplemental Matrices",
                                description="Cannot be called from Modeller.",
                                runnable=False,
                                branding_text="XTMF")

        return pb.render()

    ##########################################################################################################

    def __call__(self, xtmf_ScenarioNumber, xtmf_PartitionId,
                 xtmf_DemandMatrixId):

        database = _MODELLER.emmebank

        #---1 Set up scenario
        scenario = database.scenario(xtmf_ScenarioNumber)
        if (scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        if not scenario.has_transit_results:
            raise Exception("Scenario %s has no transit assignment results" %
                            xtmf_ScenarioNumber)

        partition = database.partition(xtmf_PartitionId)
        if partition is None:
            raise Exception("Partition '%s' does not exist" % xtmf_PartitionId)

        demandMatrix = database.matrix(xtmf_DemandMatrixId)
        if demandMatrix is None:
            raise Exception("Demand matrix '%s' does not exist" %
                            xtmf_DemandMatrixId)

        try:
            return self._Execute(scenario, partition, demandMatrix)
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc()
            raise Exception(msg)

    ##########################################################################################################

    def _Execute(self, scenario, partition, demandMatrix):

        modes = [
            id for id, type, description in _util.getScenarioModes(
                scenario, ['TRANSIT', 'AUX_TRANSIT'])
        ]
        strategyAnalysisTool = _MODELLER.tool(
            'inro.emme.transit_assignment.extended.strategy_based_analysis')
        matrixResultsTool = _MODELLER.tool(
            'inro.emme.transit_assignment.extended.matrix_results')
        partitionAggTool = _MODELLER.tool(
            'inro.emme.matrix_calculation.matrix_partition_aggregation')
        partitionAverageTool = _MODELLER.tool(
            'TMG2.Analysis.ExportPartitionAverage')

        with nested(_util.tempMatrixMANAGER('Avg Boardings'),\
                    _util.tempMatrixMANAGER('In Vehicle Times')) \
                as (avgBoardingsMatrix, walkAllWayMatrix):

            #Extract walk-all-way matrix
            spec = {
                "trip_components": {
                    "boarding": None,
                    "in_vehicle": "length",
                    "aux_transit": None,
                    "alighting": None
                },
                "sub_path_combination_operator": "+",
                "sub_strategy_combination_operator": ".min.",
                "selected_demand_and_transit_volumes": {
                    "sub_strategies_to_retain": "FROM_COMBINATION_OPERATOR",
                    "selection_threshold": {
                        "lower": 0,
                        "upper": 0
                    }
                },
                "analyzed_demand": None,
                "constraint": None,
                "results": {
                    "strategy_values": None,
                    "selected_demand": walkAllWayMatrix.id,
                    "transit_volumes": None,
                    "aux_transit_volumes": None,
                    "total_boardings": None,
                    "total_alightings": None
                },
                "type": "EXTENDED_TRANSIT_STRATEGY_ANALYSIS"
            }
            strategyAnalysisTool(spec, scenario)

            #Extract avg boardings
            spec = {
                "by_mode_subset": {
                    "modes": modes,
                    "avg_boardings": avgBoardingsMatrix.id,
                },
                "type": "EXTENDED_TRANSIT_MATRIX_RESULTS"
            }
            matrixResultsTool(spec, scenario)

            #Get the partition-aggregated results
            #The tool returns the MatrixData object
            walkOnlyResults = partitionAggTool(walkAllWayMatrix,
                                               partition,
                                               partition,
                                               scenario=scenario)
            avgBoardingResults = partitionAverageTool(scenario.id,
                                                      partition.id,
                                                      avgBoardingsMatrix.id,
                                                      demandMatrix.id)

            results = {}
            for i, row in enumerate(avgBoardingResults.raw_data):
                origin = avgBoardingResults.indices[0][i]
                for j, cell in enumerate(row):
                    destination = avgBoardingResults.indices[1][j]
                    key = (origin, destination)
                    results[key] = [cell]
            for i, row in enumerate(walkOnlyResults.raw_data):
                origin = walkOnlyResults.indices[0][i]
                for j, cell in enumerate(row):
                    destination = walkOnlyResults.indices[1][j]
                    key = (origin, destination)
                    if not key in results:
                        results[key] = [0.0, cell]
                    else:
                        results[key].append(cell)
            resultList = []
            for key, val in results.iteritems():
                origin, destination = key
                col1 = val[0]
                col2 = val[1]
                resultList.append("%s %s %s %s" %
                                  (origin, destination, col1, col2))
            resultList.sort()
            return "\n".join(resultList)

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
Beispiel #11
0
class ApplyBatchLineEdits(_m.Tool()):
    
    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1 # For progress reporting, enter the integer number of tasks here

    COLON = ':'
    COMMA = ','
    
    # Tool Input Parameters
    #    Only those parameters necessary for Modeller and/or XTMF to dock with
    #    need to be placed here. Internal parameters (such as lists and dicts)
    #    get initialized during construction (__init__)
    
    xtmf_ScenarioNumber = _m.Attribute(int) # parameter used by XTMF only

    inputFile = _m.Attribute(str) # file should have the following header: 
                                        # filter|x_hdwchange|x_spdchange
                                        # where filter is a network calculator filter expression
                                        # x refers to the scenario number
                                        # the x columns can be multiple (ie. multiple definitions
                                        # in a single file)
                                        # hdwchange and spdchange are factors by which
                                        # to change headways and speeds for the filtered
                                        # lines
    additionalInputFiles = _m.Attribute(str) #Either a string containing 'None' or a list of additional alt files ; separated.

    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.Scenario = _MODELLER.scenario #Default is primary scenario
    
    def page(self):
                
        pb = _m.ToolPageBuilder(self, title="Apply Batch Line Edits",
                     description="Cannot be called from Modeller.",
                     runnable=False,
                     branding_text="XTMF")
        
        return pb.render()
    
    ##########################################################################################################
        
    def __call__(self, xtmf_ScenarioNumber, inputFile, additionalInputFiles = None):
        
        #---1 Set up scenario
        self.Scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" %xtmf_ScenarioNumber)

        #---2 Set up instruction file
        self.InstructionFile = inputFile
        if (self.InstructionFile is None):
            raise Exception("Need to provide an input file.")
        # Process the additional files, if it is the string None then there are no additional files otherwise they are ; separated
        if additionalInputFiles  is None or additionalInputFiles == "None":
            self.InputFiles = []
        else:
            self.InputFiles = additionalInputFiles.split(';')
        # Add the base transaction file to the beginning
        self.InputFiles.insert(0, self.InstructionFile)
        try:
            self._Execute()
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            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()):
            #init the ProgressTracker now that we know how many files we need to load
            self.TRACKER = _util.ProgressTracker(len(self.InputFiles))
            for altFile in self.InputFiles:
                changesToApply = self._LoadFile(altFile)
                print "Instruction file loaded"
                if changesToApply: 
                    self._ApplyLineChanges(changesToApply)
                    print "Headway and speed changes applied"
                else:
                    print "No changes available in this scenario"
                self.TRACKER.completeTask()


    ##########################################################################################################    
    
    #----SUB FUNCTIONS---------------------------------------------------------------------------------  
    
    def _GetAtts(self):
        atts = {
                "Scenario" : str(self.Scenario.id),
                "Version": self.version, 
                "self": self.__MODELLER_NAMESPACE__}
            
        return atts 

    def _LoadFile(self, fileName):
        with open(fileName) as reader:
            header = reader.readline()
            cells = header.strip().split(self.COMMA)
            
            filterCol = cells.index('filter')
            headwayTitle = self.Scenario.id + '_hdwchange'
            speedTitle = self.Scenario.id + '_spdchange'
            try:
                headwayCol = cells.index(headwayTitle)
            except Exception as e:
                msg = "Error. No headway match for specified scenario: '%s'." %self.Scenario.id
                _m.logbook_write(msg)
                print msg
                return
            try:
                speedCol = cells.index(speedTitle)
            except Exception as e:
                msg = "Error. No speed match for specified scenario: '%s'." %self.Scenario.id
                _m.logbook_write(msg)
                print msg
                return

            instructionData = {}
            
            for num, line in enumerate(reader):
                cells = line.strip().split(self.COMMA)
                
                filter = cells[filterCol]
                if cells[headwayCol]:
                    hdw = cells[headwayCol]
                else:
                    hdw = 1 # if the headway column is left blank, carry forward a factor of 1
                if cells[speedCol]:
                    spd = cells[speedCol]
                else:
                    spd = 1
                instructionData[filter] = (float(hdw),float(spd))
        
        return instructionData

    def _ApplyLineChanges(self, inputData):
        for filter, factors in inputData.iteritems():
            if factors[0] != 1:
                spec = {
                    "type": "NETWORK_CALCULATION",
                    "expression": str(factors[0]) + "*hdw",
                    "result": "hdw",
                    "selections": {
                        "transit_line": filter}}
                netCalc(spec, self.Scenario)
            if factors[1] != 1:
                spec = {
                    "type": "NETWORK_CALCULATION",
                    "expression": str(factors[1]) + "*speed",
                    "result": "speed",
                    "selections": {
                        "transit_line": filter}}
                netCalc(spec, self.Scenario)

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()
                
    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
            
Beispiel #12
0
class ExtractStationBoardingsAlightings(_m.Tool()):

    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    COLON = ':'
    COMMA = ','

    #---PARAMETERS

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    Scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter

    StationNodeFile = _m.Attribute(str)
    ReportFile = _m.Attribute(str)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):

        if EMME_VERSION < (4, 1, 5):
            raise ValueError(
                "Tool not compatible. Please upgrade to version 4.1.5+")

        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Export Station Boardings and Alightings v%s" % self.version,
            description="Extracts total boardings and alightings for a list \
                         of nodes defined in a CSV file.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_select_file(tool_attribute_name='ReportFile',
                           title="Report file",
                           file_filter="*.csv",
                           window_type='save_file')

        pb.add_select_file(
            tool_attribute_name='StationNodeFile',
            title="Station Node file:",
            window_type='file',
            file_filter="*.csv",
            note="Station node file contains the following two columns: \
                            <ul><li>node_id</li>\
                            <li>label</li></ul> \
                            where label is whatever you wish to label the node in the output."
        )
        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception, e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")
class MergeFunctions(_m.Tool()):
    version = '1.0.0'
    tool_run_msg = ""
    number_of_tasks = 3  # 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__)

    FunctionFile = _m.Attribute(str)
    RevertOnError = _m.Attribute(bool)

    ConflictOption = _m.Attribute(str)

    RAISE_OPTION = "RAISE"
    PRESERVE_OPTION = "PRESERVE"
    OVERWRITE_OPTION = "OVERWRITE"
    EDIT_OPTION = "EDIT"

    OPTIONS_LIST = [
        (EDIT_OPTION,
         "EDIT - Launch an editor GUI to resolve conflicts manually."),
        (RAISE_OPTION,
         "RAISE - Raise an error if any conflicsts are detected."),
        (PRESERVE_OPTION,
         "PRESERVE - Preserve functional definitions from the current Emme project."
         ),
        (OVERWRITE_OPTION,
         "OVERWRITE - Overwrite functional definitions from the function file."
         )
    ]

    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.RevertOnError = True

        self.ConflictOption = 'EDIT'

        #--- event to block GUI / merge edit
        self.event = None

        self.function_conflicts = None

        self.function_changes = []

        # -- hold dialog reference
        self.dialog = None

        # -- showing dialog
        self.show_edit_dialog = False

        self.is_sub_call = False

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Merge Functions v%s" % self.version,
            description=
            "Merges into this emmebank functions defined in a standard \
                         function transaction file. Delete and modify commands are ignored.\
                         <br><br>Detects conflicts in functional definitions and prompts \
                         user for input. New functions as simply merged in.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        baseFolder = _path.dirname(_MODELLER.desktop.project_file_name())
        pb.add_select_file(tool_attribute_name='FunctionFile',
                           window_type='file',
                           start_path=baseFolder,
                           title="Functions File")

        pb.add_select(tool_attribute_name='ConflictOption',
                      keyvalues=self.OPTIONS_LIST,
                      title="Conflict resolution option",
                      note="Select an option for this tool to perform if \
                      conflicts in functional definitions are detected.")

        pb.add_checkbox(tool_attribute_name='RevertOnError',
                        label="Revert on error?")

        pb.add_html("""
        
       
            <script type="text/javascript">
            
            $(document).ready( function ()
            {
                var tool = new inro.modeller.util.Proxy(%s);

            
            });
        </script>""" % pb.tool_proxy_tag)

        return pb.render()

    ##########################################################################################################

    def run(self, event=None, is_sub_call=False):
        self.tool_run_msg = ""
        self.event = event
        self.TRACKER.reset()

        if self.FunctionFile is None:
            raise IOError("Import file not specified")

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

    ##########################################################################################################

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            file_functions = self._LoadFunctionFile()
            self.TRACKER.completeTask()

            database_functions = self._LoadFunctionsInDatabank()
            self.TRACKER.completeTask()

            newFuncCount, modFuncCount = self._MergeFunctions(
                database_functions, file_functions)
            self.TRACKER.completeTask()

            msg = "Done."
            if newFuncCount > 0:
                msg += " %s functions added." % newFuncCount
            if modFuncCount > 0:
                msg += " %s functions modified." % modFuncCount
            self.tool_run_msg = _m.PageBuilder.format_info(msg)
            _m.logbook_write(msg)

    ##########################################################################################################

    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''

    @contextmanager
    def _NewFunctionMANAGER(self, newFunctions, modifiedFunctions):
        emmebank = _MODELLER.emmebank

        try:
            yield  # Yield return a temporary object
        except Exception as e:
            if self.RevertOnError:
                for id in newFunctions:
                    emmebank.delete_function(id)
                for id, expression in modifiedFunctions.iteritems():
                    emmebank.function(id).expression = expression
            raise

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _GetAtts(self):
        atts = {
            "Functions File": self.FunctionFile,
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _LoadFunctionFile(self):
        functions = {}
        with open(self.FunctionFile) as reader:
            expressionBuffer = ""
            trecord = False
            currentId = None

            for line in reader:
                line = line.rstrip()
                linecode = line[0]
                record = line[2:]

                if linecode == 'c':
                    pass
                elif linecode == 't':
                    if not record.startswith("functions"):
                        raise IOError("Wrong t record!")
                    trecord = True
                elif linecode == 'a':
                    if not trecord: raise IOError("A before T")
                    index = record.index('=')
                    currentId = record[:index].strip()
                    expressionBuffer = record[(index + 1):].replace(' ', '')
                    if currentId is not None:
                        functions[currentId] = expressionBuffer
                elif linecode == ' ':
                    if currentId is not None and trecord:
                        s = record.strip().replace(' ', '')
                        expressionBuffer += s
                        functions[currentId] = expressionBuffer
                elif linecode == 'd' or linecode == 'm':
                    currentId = None
                    expressionBuffer = ""
                else:
                    raise KeyError(linecode)

        return functions

    def _LoadFunctionsInDatabank(self):
        functions = {}
        for func in _MODELLER.emmebank.functions():
            expr = func.expression.replace(' ', '')
            functions[func.id] = expr
        return functions

    def _MergeFunctions(self, databaseFunctions, fileFunctions):
        emmebank = _MODELLER.emmebank

        databaseIds = set([key for key in databaseFunctions.iterkeys()])
        fileIds = set([key for key in fileFunctions.iterkeys()])

        newFunctions = []
        modifiedFunctions = {}
        with self._NewFunctionMANAGER(newFunctions, modifiedFunctions):
            for id in (fileIds -
                       databaseIds):  #Functions in the new source only
                expression = fileFunctions[id]
                emmebank.create_function(id, expression)
                _m.logbook_write("Added %s : %s" % (id, expression))
                newFunctions.append(id)

            conflicts = []
            conflicts_obj = []
            for id in (fileIds & databaseIds):  #Functions in both sources
                database_expression = databaseFunctions[id]
                file_expression = fileFunctions[id]
                if file_expression != database_expression:
                    conflicts.append(
                        (id, database_expression, file_expression))
                    conflicts_obj.append({
                        'id': id,
                        'database_expression': database_expression,
                        'file_expression': file_expression
                    })

            if len(conflicts) > 0:
                conflicts.sort()

                #If the PRESERVE option is selected, do nothing

                if self.ConflictOption == self.OVERWRITE_OPTION:
                    #Overwrite exisiting functions with new ones
                    for fid, database_expression, file_expression in conflicts:
                        func = emmebank.function(fid)
                        func.expression = file_expression
                        modifiedFunctions[fid] = database_expression
                        with _m.logbook_trace("Changed function %s" % fid):
                            _m.logbook_write("Old expression: %s" %
                                             database_expression)
                            _m.logbook_write("New expresion: %s" %
                                             file_expression)
                elif self.ConflictOption == self.EDIT_OPTION:
                    #self.event.clear()
                    conflicts_obj.sort(key=lambda x: x['id'])
                    self.function_conflicts = conflicts_obj

                    # self._LaunchGUI(conflicts,modifiedFunctions)
                    if not self.is_sub_call:
                        self.show_edit_dialog = True
                    else:
                        self._LaunchGUI(conflicts, modifiedFunctions)

                    # self.event.wait()

                elif self.ConflictOption == self.RAISE_OPTION:
                    tup = len(conflicts), ', '.join([t[0] for t in conflicts])
                    msg = "The following %s functions have conflicting definitions: %s" % tup
                    raise Exception(msg)

        return len(newFunctions), len(modifiedFunctions)

    def save_modified_functions(functions):

        return

    def _LaunchGUI(self, conflicts, modifiedFunctions):
        self.dialog = FunctionConflictDialog(conflicts, self)
        #self.dialog = dialog
        result = self.dialog.exec_()

        # self.event.wait()

        #if result == dialog.Accepted:
        #    acceptedChanges = dialog.getFunctionsToChange()
        #    for fid, expression in acceptedChanges.iteritems():
        #        func = _MODELLER.emmebank.function(fid)
        #        oldExpression = func.expression
        #        func.expression = expression
        #        modifiedFunctions[fid] = oldExpression

        #        with _m.logbook_trace("Modified function %s" %fid.upper()):
        #            _m.logbook_write("Old expression: %s" %oldExpression)
        #            _m.logbook_write("New expression: %s" %expression)

        # self.dialog.deleteLater()

    def merge_changes(self, changes):

        for change in changes:
            if change['resolve'] == 'file' or change['resolve'] == 'expression':

                print(change)
                func = _MODELLER.emmebank.function(change['id'])
                oldExpression = func.expression
                func.expression = change['expression']
                # modifiedFunctions[change['id']] = oldExpression

                with _m.logbook_trace("Modified function %s" %
                                      change['id'].upper()):
                    _m.logbook_write("Old expression: %s" % oldExpression)
                    _m.logbook_write("New expression: %s" %
                                     change['expression'])

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=six.u)
    def tool_run_msg_status(self):
        return self.tool_run_msg

    def update_data(self):
        # dialog = self.dialog
        acceptedChanges = self.dialog.getFunctionsToChange()
        for fid, expression in acceptedChanges.iteritems():
            func = _MODELLER.emmebank.function(fid)
            oldExpression = func.expression
            func.expression = expression
            modifiedFunctions[fid] = oldExpression

            with _m.logbook_trace("Modified function %s" % fid.upper()):
                _m.logbook_write("Old expression: %s" % oldExpression)
                _m.logbook_write("New expression: %s" % expression)
Beispiel #14
0
class Station2StationAssignment(_m.Tool()):
    
    version = '0.1.0'
    tool_run_msg = ""
    
    #---Variable definitions
    ScenarioNumber = _m.Attribute(int)
    DemandMatrixNumber = _m.Attribute(int)
    WaitTimeMatrixNumber = _m.Attribute(int) #
    InVehicleTimeMatrixNumber = _m.Attribute(int) #
    
    StationSelectorExpression = _m.Attribute(str) #
    ModeString = _m.Attribute(str)
    
    WaitFactor = _m.Attribute(float) #
    WaitPerception = _m.Attribute(float) #
    WalkPerception = _m.Attribute(float) #
    
    UseAdditiveDemand = _m.Attribute(bool) #
    UseEM4Options = _m.Attribute(bool) # Not yet used. Future proofing.
    
    #---Special instance types
    scenario = _m.Attribute(_m.InstanceType) #
    modes = _m.Attribute(_m.ListType) #
    demandMatrix = _m.Attribute(_m.InstanceType) #
    
    #---Internal variables
    _modeList = []
    
    def __init__(self):
        #ENTER IN THE NUMBER OF TASKS. THIS WILL CRASH OTHERWISE.
        #******************************************************************************
        self._tracker = _util.ProgressTracker(6) # Enter in the correct number of tasks
        #******************************************************************************
        
        # Set up variable defaults
        self.WaitFactor = 0.5
        self.StationSelectorExpression = "7000-8000"
        self.WaitPerception = 2.0
        self.WalkPerception = 2.0
        self.UseAdditiveDemand = False
        self.UseEM4Options = False
    
    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(self, title="Station to Station Assignment v%s" %self.version,
                     description="Assigns a limited matrix of demand to centroids which represent \
                             GO train stations. Can assign a scalar matrix of 0 or a full matrix of \
                             demand (constrained by the station selector). Unlike most other transit \
                             assignment tools, this tool saves the constrained IVTT and wait times \
                             matrices as outputs.",
                     branding_text="- TMG Toolbox")
        
        if self.tool_run_msg != "": # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)
        
        pb.add_header("ASSIGNMENT SETUP")
        #-----------------------------------------------------------------------------
        pb.add_select_scenario(tool_attribute_name='scenario',
                               title='Scenario:',
                               allow_none=False)
        
        pb.add_select_matrix(tool_attribute_name='demandMatrix',
                             title='Demand Matrix:',
                             note="If no matrix is selected, a scalar matrix of value '0' will\
                             be assigned.",
                             allow_none=True)
        
        pb.add_select_mode(tool_attribute_name='modes',
                           title='Assignment Modes',
                           filter=['TRANSIT', 'AUX_TRANSIT'],
                           note="<font color='red'><b>Modes are available for the primary scenario only.</b></font>\
                           <br>Actual assignment is based on mode ids, so this is only a problem\
                           <br>if modes differ across scenarios.")
        
        pb.add_text_box(tool_attribute_name='StationSelectorExpression',
                        size=150,
                        title="Station centroid selection:",
                        note="Write an expression to select which centroids are stations.<br>\
                            A single range is written as '[start]-[stop]' (e.g., '7000-8000') <br>\
                            with multiple ranges separated by ';'",
                        multi_line=True)
        
        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_checkbox(tool_attribute_name='UseAdditiveDemand')
            with t.table_cell():
                pb.add_html("Assign additional demand?")
            with t.table_cell():
                pb.add_checkbox(tool_attribute_name='UseEM4Options')
            with t.table_cell():
                pb.add_html("Use new Emme 4 options?")
        
        pb.add_header("ROUTE CHOICE PARAMETERS")
        #-----------------------------------------------------------------------------
        
        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='WaitPerception',
                        size=6,
                        title="Wait Time Perception")
                
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='WaitFactor',
                        size=6,
                        title="Headway Fraction")
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='WalkPerception',
                                size=6,
                                title="Walk Time Perception")
        
        pb.add_header("ASSIGNMENT OUTPUT")
        #-----------------------------------------------------------------------------
        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='WaitTimeMatrixNumber',
                                size=2,
                                title="Wait Times Matrix Number",
                                note="If left blank, an available matrix will be created.")
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='InVehicleTimeMatrixNumber',
                                size=2,
                                title="In-Vehicle Times Matrix Number",
                                note="If left blank, an available matrix will be created.")
        
        return pb.render()
    
    ##########################################################################################################
        
    def run(self):
        self.tool_run_msg = ""
        
        '''Run is called from Modeller.'''
        self.isRunningFromXTMF = False
        
        #Setup 
        self._modeList = [str(m) for m in self.modes]
        
        try:
            self._execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise
        
        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")
    
    def __call__(self, ScenarioNumber, DemandMatrixNumber, WaitTimeMatrixNumber, InVehicleTimeMatrixNumber, \
                 StationSelectorExpression, ModeString, WaitFactor, WaitPerception, WalkPerception,\
                 UseAdditiveDemand, UseEM4Options):
        
        #---1 Set up scenario
        self.scenario = _m.Modeller().emmebank.scenario(ScenarioNumber)
        if (self.scenario is None):
            raise Exception("Scenario %s was not found!" %ScenarioNumber)
        
        #---2 Set up the demand matrix
        if DemandMatrixNumber == 0:
            self.demandMatrix = None
        else:
            self.demandMatrix = _m.Modeller().emmebank.matrix('mf%s' %DemandMatrixNumber)
            if self.demandMatrix is None:
                raise Exception("Could not find matrix 'mf%s' in the databank!" %DemandMatrixNumber)
            
        #---3 Set up modes
        self._modeList = [c for c in ModeString]
        
        #---4 Pass in remaining args
        self.WaitTimeMatrixNumber = WaitTimeMatrixNumber
        self.InVehicleTimeMatrixNumber = InVehicleTimeMatrixNumber
        self.StationSelectorExpression = StationSelectorExpression
        self.WaitFactor = WaitFactor
        self.WalkPerception = WalkPerception
        self.UseAdditiveDemand = UseAdditiveDemand
        self.UseEM4Options = UseEM4Options
        
        self.isRunningFromXTMF = True
        
        #---Execute
        
        try:
            self._execute()
        except Exception as e:
            raise Exception(_traceback.format_exc(e))
    
    ##########################################################################################################    
    
    
    def _execute(self):
        self._tracker.reset()
        with _m.logbook_trace(name="{classname} v{version}".format(classname=(self.__class__.__name__), version=self.version),
                                     attributes=self._getAtts()):
            
            try:
                transitAssignmentTool = _m.Modeller().tool("inro.emme.standard.transit_assignment.extended_transit_assignment")
                matrixResultsTool = _m.Modeller().tool('inro.emme.standard.transit_assignment.extended.matrix_results')
                matrixCalcTool = _m.Modeller().tool("inro.emme.standard.matrix_calculation.matrix_calculator")
            except Exception as e:
                transitAssignmentTool = _m.Modeller().tool("inro.emme.transit_assignment.extended_transit_assignment")
                matrixResultsTool = _m.Modeller().tool('inro.emme.transit_assignment.extended.matrix_results')
                matrixCalcTool = _m.Modeller().tool("inro.emme.matrix_calculation.matrix_calculator")
            
            with self._demandMatrixMANAGER(): # TASK 1
                
                with _m.logbook_trace("Initializing output matrices"):
                    ivttMatrix = _util.initializeMatrix(id=self.InVehicleTimeMatrixNumber,
                                                   matrix_type='FULL',
                                                   description="Station-station IVTT matrix")
                    
                    waitMatrix = _util.initializeMatrix(id=self.WaitTimeMatrixNumber,
                                                   matrix_type='FULL',
                                                   description="Station-station wait time matrix")
                    
                    self._tracker.completeTask() # TASK 2
                
                with _m.logbook_trace("Running transit assignment"):
                    self._tracker.runTool(transitAssignmentTool, self._getAssignmentSpec(),
                                      scenario=self.scenario,
                                      add_volumes=self.UseAdditiveDemand) #TASK 3
                    
                    # some error with progress reporting is occurring here.
                
                with _m.logbook_trace("Extracting output matrices"):
                    self._tracker.runTool(matrixResultsTool,
                                          self._getMatrixResultSpec(ivttMatrix, waitMatrix),
                                          scenario=self.scenario) # TASK 4
                    
                with _m.logbook_trace("Constraining output matrices"):
                    self._tracker.runTool(matrixCalcTool,
                                          self._getConstraintSpec(ivttMatrix, ivttMatrix),
                                          scenario=self.scenario) # TASK 5
                    
                    self._tracker.runTool(matrixCalcTool,
                                          self._getConstraintSpec(waitMatrix, waitMatrix),
                                          scenario=self.scenario) # TASK 6
        
        
                    
    ##########################################################################################################

    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''
    
    @contextmanager  
    def _demandMatrixMANAGER(self):
        #Code here is executed upon entry
        
        with _m.logbook_trace("Initializing temporary demand matrix"):
            id=None
            if self.demandMatrix is None:
                
                self.demandMatrix = _util.initializeMatrix(id,
                                                      matrix_type='SCALAR',
                                                      name='trscal',
                                                      description='Scalar matrix to get transit times')
                
                self._tracker.completeTask()
                
            else:
                cachedMatrix = self.demandMatrix
                self.demandMatrix = _util.initializeMatrix(matrix_type='FULL', description="Constrained full matrix for station-to-station assignment")
                _m.logbook_write("Created temporary constrained full demand matrix '%s'" %id)
                
                try:
                    matrixCalcTool = _m.Modeller().tool("inro.emme.standard.matrix_calculation.matrix_calculator")
                except Exception as e:
                    matrixCalcTool = _m.Modeller().tool("inro.emme.matrix_calculation.matrix_calculator")
                
                self._tracker.runTool(matrixCalcTool,
                                      self._getConstraintSpec(cachedMatrix, self.demandMatrix), 
                                      scenario=self.scenario) #TASK 1
        try:
            yield
            # Code here is executed upon clean exit
        finally:
            # Code here is executed in all cases.
            id = self.demandMatrix.id
            _m.Modeller().emmebank.delete_matrix(id)
            _m.logbook_write("Temporary matrix %s deleted." %id)
    
    
    #----SUB FUNCTIONS---------------------------------------------------------------------------------  
    
    def _getAtts(self):
        atts = {
                "Scenario" : str(self.scenario.id),
                "Version": self.version, 
                "self": self.__MODELLER_NAMESPACE__}
            
        return atts 
    
    def _getConstraintSpec(self, baseMatrix, resultMatrix):
        return {
                "expression": baseMatrix.id,
                "result": resultMatrix.id,
                "constraint": {
                                "by_value": None,
                                "by_zone": {
                                            "origins": self.StationSelectorExpression,
                                            "destinations": self.StationSelectorExpression
                                            }
                               },
                "aggregation": {
                                "origins": None,
                                "destinations": None
                                },
                "type": "MATRIX_CALCULATION"
                }
    
    def _getAssignmentSpec(self):
        spec = {
                "modes": self._modeList,
                "demand": self.demandMatrix.id,
                "waiting_time": {
                                "headway_fraction": self.WaitFactor,
                                "effective_headways": "hdw",
                                "spread_factor": 1,
                                "perception_factor": self.WaitPerception
                                },
                "boarding_time": {
                                    "at_nodes": None,
                                    "on_lines": {
                                                "penalty": "ut3",
                                                "perception_factor": 1
                                                }
                                  },
                "boarding_cost": {
                                    "at_nodes": {
                                                "penalty": 0,
                                                "perception_factor": 1
                                                },
                                    "on_lines": None
                                    },
                "in_vehicle_time": {
                                    "perception_factor": 1
                                    },
                "in_vehicle_cost": None,
                "aux_transit_time": {
                                    "perception_factor": self.WalkPerception
                                    },
                "aux_transit_cost": None,
                "flow_distribution_at_origins": {
                                                "by_time_to_destination": "BEST_CONNECTOR",
                                                "by_fixed_proportions": None
                                                },
                "flow_distribution_between_lines": {
                                                    "consider_travel_time": False
                                                    },
                "connector_to_connector_path_prohibition": None,
                "save_strategies": True,
                "od_results": None,
                "type": "EXTENDED_TRANSIT_ASSIGNMENT"
                }
        if EMME_VERSION >= (4,1,0):
            spec["performance_settings"] = {
                    "number_of_processors": cpu_count()
                    }
        return spec
    
    def _getMatrixResultSpec(self, ivttMatrix, waitMatrix):
        return {
                "by_mode_subset": {
                                    "modes": self._modeList,
                                    "actual_in_vehicle_times": ivttMatrix.id
                                    },
                "type": "EXTENDED_TRANSIT_MATRIX_RESULTS",
                "actual_total_waiting_times": waitMatrix.id
                }
    
    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        try:
            return self._tracker.getProgress()
        except Exception as e:
            print "Exception occurred during progress reporting."
            print "Tracker progress = %s" %self._tracker._progress
            print  _traceback.format_exc(e)
            raise
                
    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
class CreateNetworkCorrespondenceFile(_m.Tool()):

    version = '0.1.2'
    tool_run_msg = ""
    number_of_tasks = 7  # 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__)

    PrimaryScenario = _m.Attribute(_m.InstanceType)
    SecondaryScenario = _m.Attribute(_m.InstanceType)
    SearchBuffer = _m.Attribute(float)
    MaxSplitLinks = _m.Attribute(int)
    MaxBearingDifference = _m.Attribute(int)
    CorrespondenceFile = _m.Attribute(str)

    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.PrimaryScenario = _MODELLER.scenario  #Default is primary scenario
        self.SearchBuffer = 50.0
        self.MaxSplitLinks = 10
        self.MaxBearingDifference = 5

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Create Network Correspondence File v%s" % self.version,
            description=
            "<p class='tmg_left'>Twins together nodes and links in two networks. \
                         Node correspondence \
                         is based on proximity (Manhattan distance). Link correspondence is based on \
                         node correspondence - e.g., if both ends of a link in the primary correspond \
                         to both ends of another link in the secondary network, then those two links \
                         are twinned. If no link twin is found, this tool attempts to determine if a \
                         split has occurred; based on the bearing of links attached to it's twinned \
                         nodes.\
                         <br><br>The results are saved into a zip file, containing three files: \
                         <ul class='tmg_left'><li><b>config.txt</b> contains the parameters used to \
                         write the file</li>\
                         <li><b>nodes.txt</b> contains the node correspondence</li>\
                         <li><b>links.txt</b> contains the link correspondence</li></ul></p>",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_header("SCENARIOS")
        #------------------------------------------------------------

        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_select_scenario(tool_attribute_name='PrimaryScenario',
                                       title='Primary Scenario',
                                       allow_none=False)

            with t.table_cell():
                pb.add_select_scenario(tool_attribute_name='SecondaryScenario',
                                       title='Secondary Scenario',
                                       allow_none=False)

        pb.add_header("CORRESPONDANCE PARAMETERS")
        #------------------------------------------------------------
        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='SearchBuffer',
                                title="Node Search Buffer",
                                size=10,
                                note="Search radius, in coordinate units.")

            with t.table_cell():
                pb.add_text_box(
                    tool_attribute_name='MaxSplitLinks',
                    size=2,
                    title="Max split links",
                    note="Maximum number of split links to look for.")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='MaxBearingDifference',
                                size=2,
                                title="Max bearing difference",
                                note="In degrees.")

        pb.add_header("OUTPUT FILE")
        #------------------------------------------------------------

        pb.add_select_file(tool_attribute_name='CorrespondenceFile',
                           window_type='save_file',
                           title="Correspondence File",
                           file_filter="*.zip")

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        if self.CorrespondenceFile is None:
            raise IOError("Export file not specified")

        self.CorrespondenceFile = _path.splitext(
            self.CorrespondenceFile)[0] + ".zip"

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info(
            "Run complete.<br>\
                                    <a href=%s target='_top'>%s</a>" %
            (self.CorrespondenceFile, _path.basename(self.CorrespondenceFile)),
            escape=False)

    ##########################################################################################################

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            self._maxBearingDiffRadians = abs(
                2 * _math.pi * self.MaxBearingDifference / 360.0)

            #Load the networks
            self.TRACKER.startProcess(2)
            primaryNetwork = self.PrimaryScenario.get_network()
            self.TRACKER.completeSubtask()
            secondaryNetwork = self.SecondaryScenario.get_network()
            self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()

            with _m.logbook_trace("Associating node twins"):
                self._ConnectTwinNodes(primaryNetwork, secondaryNetwork)

            with _m.logbook_trace("Associating link twins"):
                maxTwins = self._ConnectTwinLinks(primaryNetwork,
                                                  secondaryNetwork)

            with _m.logbook_trace("Writing results to file"):
                self._WriteFile(primaryNetwork, maxTwins)
                _m.logbook_write("Done.")

    ##########################################################################################################
    #----CONTEXT MANAGERS---------------------------------------------------------------------------------
    '''
    Context managers for temporary database modifications.
    '''

    @contextmanager
    def _zipFileMANAGER(self):
        file = _zf.ZipFile(self.CorrespondenceFile, 'w', _zf.ZIP_DEFLATED)
        try:
            yield file
        finally:
            file.close()

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _GetAtts(self):
        atts = {
            "Primary Scenario": self.PrimaryScenario.id,
            "Secondary Scenario": self.SecondaryScenario.id,
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _ConnectTwinNodes(self, primaryNetwork, secondaryNetwork):
        self.TRACKER.startProcess(
            primaryNetwork.element_totals['regular_nodes'])

        primaryNetwork.create_attribute('NODE',
                                        "twin_node",
                                        default_value=None)
        secondaryNetwork.create_attribute('NODE',
                                          "twin_node",
                                          default_value=None)

        extents = _spindex.get_network_extents(secondaryNetwork)
        grid = _spindex.GridIndex(extents, marginSize=1.0)
        for node in secondaryNetwork.regular_nodes():
            grid.insertPoint(node)

        for primaryNode in primaryNetwork.regular_nodes():
            twin = grid.getNearestNode(primaryNode.x, primaryNode.y,
                                       self.SearchBuffer)

            candidates = grid.queryCircle(primaryNode.x, primaryNode.y,
                                          self.SearchBuffer)
            twin = None
            minDistance = float('inf')
            for node in candidates:
                dx = node.x - primaryNode.x
                dy = node.y - primaryNode.y
                d = sqrt(dx * dx + dy * dy)
                if d < minDistance:
                    twin = node
                    minDistance = d

            if twin is None:
                self.TRACKER.completeSubtask()
                continue

            primaryNode['twin_node'] = twin
            twin['twin_node'] = primaryNode
            self.TRACKER.completeSubtask()

        self.TRACKER.completeTask()

    def _ConnectTwinLinks(self, primaryNetwork, secondaryNetwork):
        primaryNetwork.create_attribute('LINK', "twin_links", default_value=[])
        secondaryNetwork.create_attribute('LINK',
                                          "twin_links",
                                          default_value=[])

        maxTwins = 0

        # First pass.
        pLinksTwinned = 0
        sLinksTwinned = 0
        self.TRACKER.startProcess(primaryNetwork.element_totals['links'])
        for primaryLink in primaryNetwork.links():
            if primaryLink.i_node.is_centroid or primaryLink.j_node.is_centroid:
                self.TRACKER.completeSubtask()
                continue  # Skip centroid connectors

            twins = self._GetTwinLinks(primaryLink)

            if twins is None:
                self.TRACKER.completeSubtask()
                continue

            if len(twins) > maxTwins:
                maxTwins = len(twins)

            primaryLink['twin_links'] = twins
            pLinksTwinned += 1
            for secondaryLink in twins:
                secondaryLink['twin_links'] = [primaryLink]
                sLinksTwinned += 1
        self.TRACKER.completeTask()

        _m.logbook_write(
            "Finished first pass. %s primary links twinned to %s secondary links."
            % (pLinksTwinned, sLinksTwinned))

        # Second Pass
        pLinksTwinned = 0
        sLinksTwinned = 0
        self.TRACKER.startProcess(secondaryNetwork.element_totals['links'])
        for secondaryLink in secondaryNetwork.links():
            if secondaryLink.i_node.is_centroid or secondaryLink.j_node.is_centroid:
                self.TRACKER.completeSubtask()
                continue  # Skip centroid connectors

            if len(secondaryLink['twin_links']) > 0:
                continue  # Skip if link is already twinned.

            twins = self._GetTwinLinks(secondaryLink)

            if twins is None:
                self.TRACKER.completeSubtask()
                continue

            if len(twins) > maxTwins:
                maxTwins = len(twins)

            secondaryLink['twin_links'] = twins
            sLinksTwinned += 1
            for primaryLink in twins:
                primaryLink['twin_links'] = [secondaryLink]
                pLinksTwinned += 1
        self.TRACKER.completeTask()

        _m.logbook_write(
            "Finished second pass. %s additional secondary links twinned to %s primary links."
            % (sLinksTwinned, pLinksTwinned))

        return maxTwins

    def _GetTwinLinks(self, originalLink):

        if originalLink.i_node['twin_node'] is None or originalLink.j_node[
                'twin_node'] is None:
            return None  #Cannot ever find a corresponding originalLink for a originalLink with no twin nodes

        # Check for the same link in the other network
        for outLink in originalLink.i_node['twin_node'].outgoing_links():
            if outLink.j_node == originalLink.j_node['twin_node']:
                return [outLink]

        # If not, try and get the collection of links which were created by splitting this link
        # (if such links exist). This function returns None if a valid split path cannot be found.
        return self._GetCorrespondingSplitLinks(originalLink)

    def _GetCorrespondingSplitLinks(self, originalLink):
        #don't call this method unless both node-ends of the original link have twins
        originalBearing = self._GetLinkBearing(originalLink)

        linkSequece = []
        prevNode = originalLink.i_node['twin_node']
        for i in range(0, self.MaxSplitLinks):
            candidateLinks = [
            ]  #tuple of 0. Bearing difference, 1. outgoing link
            outgoingLinks = [link for link in prevNode.outgoing_links()]

            for link in outgoingLinks:
                bearingDiff = abs(self._GetLinkBearing(link) - originalBearing)

                if bearingDiff > self._maxBearingDiffRadians:
                    continue
                candidateLinks.append((bearingDiff, link))

            if len(candidateLinks) == 0:
                return None  # If there are no links within the search arc, since we haven't reached the
                # end node of the original link, therefore no straight path exists.

            bestLink = min(
                candidateLinks
            )[1]  #If there are more than one links within the search arc, pick
            # the closest.
            linkSequece.append(bestLink)

            if bestLink.j_node == originalLink.j_node[
                    'twin_node']:  # We've reached the end node of the original
                return linkSequece  # link. So, return the sequence

            prevNode = bestLink.j_node  #continue searching for candidate links in the sequences

    def _GetLinkBearing(self, link):
        rad = _math.atan2(link.j_node.x - link.i_node.x,
                          link.j_node.y - link.i_node.y)
        if rad < 0:
            return rad + _math.pi * 2
        return rad

    def _WriteFile(self, primaryNetwork, maxLinkTwins):
        folderName = _path.dirname(self.CorrespondenceFile)
        with open("%s/config.txt" % folderName, 'w') as writer:
            s = "project_path: {projPath}\
                \nprimary_scenario: {pSc}\
                \nsecondary_scenario: {sSc}\
                \nsearch_radius: {rad}\
                \nmax_twins: {max}\
                \narc_tolerance: {arc}\
                \ncreated: {date}".format(
                projPath=_MODELLER.desktop.project_file_name(),
                pSc=self.PrimaryScenario.id,
                sSc=self.SecondaryScenario.id,
                rad=str(self.SearchBuffer),
                max=str(self.MaxSplitLinks),
                arc=str(self.MaxBearingDifference),
                date=_dt.now())
            writer.write(s)

        with open("%s/nodes.txt" % folderName, 'w') as writer:
            self.TRACKER.startProcess(
                primaryNetwork.element_totals['regular_nodes'])
            writer.write("primary_node,secondary_node")
            for node in primaryNetwork.nodes():
                twin = node['twin_node']
                if twin is None:
                    writer.write("\n%s,null" % node)
                else:
                    writer.write("\n%s,%s" % (node, twin))
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()

        with open("%s/links.txt" % folderName, 'w') as writer:
            self.TRACKER.startProcess(primaryNetwork.element_totals['links'])
            writer.write("primary_link")
            for i in range(0, maxLinkTwins):
                writer.write(",twin_link_%s" % (i + 1))

            for primaryLink in primaryNetwork.links():
                writer.write("\n(%s-%s)" %
                             (primaryLink.i_node, primaryLink.j_node))
                twins = primaryLink['twin_links']

                if len(twins) == 0:
                    for i in range(0, maxLinkTwins):
                        writer.write(",null")
                    continue

                for secondaryLink in twins:
                    writer.write(",(%s-%s)" %
                                 (secondaryLink.i_node, secondaryLink.j_node))
                for i in range(len(twins), maxLinkTwins):
                    writer.write(",null")
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()

        with self._zipFileMANAGER() as zf:
            zf.write("%s/config.txt" % folderName, arcname="config.txt")
            zf.write("%s/links.txt" % folderName, arcname="links.txt")
            zf.write("%s/nodes.txt" % folderName, arcname="nodes.txt")
            os.remove("%s/config.txt" % folderName)
            os.remove("%s/links.txt" % folderName)
            os.remove("%s/nodes.txt" % folderName)

            self.TRACKER.completeTask()

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
class MatrixSummary(_m.Tool()):
    
    version = '1.0.0'
    tool_run_msg = ""
    number_of_tasks = 8 # 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__)
    
    ValueMatrix = _m.Attribute(_m.InstanceType)
    WeightingMatrix = _m.Attribute(_m.InstanceType)
    Scenario = _m.Attribute(_m.InstanceType)
    ReportFile = _m.Attribute(str)
    
    OriginFilterExpression = _m.Attribute(str)
    DestinationFilterExpression = _m.Attribute(str)
    CellFilterExpression = _m.Attribute(str)
    
    HistogramMin = _m.Attribute(float)
    HistogramMax = _m.Attribute(float)
    HistogramStepSize = _m.Attribute(float)
    
    xtmf_ScenarioNumber = _m.Attribute(int) # parameter used by XTMF only
    xtmf_ValueMatrixNumber = _m.Attribute(int)
    xtmf_WeightingMatrixNumber = _m.Attribute(int)
    xtmf_OriginRangeSetString = _m.Attribute(str)
    xtmf_DestinationRangeSetString = _m.Attribute(str)
    
    
    def __init__(self):
        #---Init internal variables
        self.TRACKER = _util.ProgressTracker(self.number_of_tasks) #init the ProgressTracker
        
        self.HistogramMin = 0.0
        self.HistogramMax = 200.0
        self.HistogramStepSize = 10.0
        
        self.OriginFilterExpression = "return p < 9000"
        self.DestinationFilterExpression = "return q < 9000"
        self.CellFilterExpression = "return pq < 1000.0"
        
    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(self, title="Export Matrix Summary v%s" %self.version,
                     description="Computes using <em>numpy</em> a several aggregate statistics \
                         for a given matrix: average, median, standard deviation, and \
                         a histogram of values. Users can also specify an optional matrix \
                         of weights, and the tool will also compute weighted average, \
                         standard deviation, and histogram values.",
                     branding_text="- TMG Toolbox")
        
        if self.tool_run_msg != "": # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)
        
        pb.add_select_matrix(tool_attribute_name='ValueMatrix',
                             filter=['FULL'], allow_none=False,
                             title="Value Matrix")
        
        pb.add_select_matrix(tool_attribute_name='WeightingMatrix',
                             filter=['FULL'], allow_none=True,
                             title="Weighting Matrix",
                             note="<font color='green'><b>Optional:</b></font> Matrix of weights")
        
        pb.add_select_file(tool_attribute_name='ReportFile',
                           window_type='save_file',
                           file_filter='*.txt',
                           title="Report File",
                           note="<font color='green'><b>Optional.</b></font> Matrix data will be \
                               saved to the logbook.")
        
        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=True,
                               note="<font color='green'><b>Optional:</b></font> Only required if \
                                   scenarios have differing zone systems.")
        
        pb.add_header('FILTERS')
        
        pb.add_text_box(tool_attribute_name='OriginFilterExpression',
                        size=100, multi_line=False,
                        title="def OriginFilter(p):",
                        note="Enter a Python expression. Include the return statement.")
        
        pb.add_text_box(tool_attribute_name='DestinationFilterExpression',
                        size=100, multi_line=False,
                        title="def DestinationFilter(q):",
                        note="Enter a Python expression. Include the return statement")
        
        #TODO: Figure out how to apply the filter expression
        #simultaeneously to both the value and weight matrix
        #using numpy
        '''
        pb.add_text_box(tool_attribute_name='CellFilterExpression',
                        size=100, multi_line=True,
                        title="def CellFilter(pq):",
                        note="Enter a Python expression.")
        '''
        
        pb.add_header("HISTOGRAM")
        
        with pb.add_table(visible_border=False) as t:
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='HistogramMin',
                                title= "Min", size=10)
            
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='HistogramMax',
                                title= "Max", size=10)
            
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='HistogramStepSize',
                                title= "Step Size", size=10)
        
        return pb.render()
    
    ##########################################################################################################
        
    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()
        
        try:
            originFilter = self._GetOriginFilterFunction()
            destinationFilter = self._GetDestinationFilterFunction()
            
            self._Execute(originFilter, destinationFilter)
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise
        
        self.tool_run_msg = _m.PageBuilder.format_info("Done.")
    
    def __call__(self, xtmf_ValueMatrixNumber, xtmf_WeightingMatrixNumber, xtmf_ScenarioNumber,
                 ReportFile, HistogramMin, HistogramMax, HistogramStepSize, xtmf_OriginRangeSetString,
                 xtmf_DestinationRangeSetString):
        
        
        self.ValueMatrix = _MODELLER.emmebank.matrix('mf%s' %xtmf_ValueMatrixNumber)
        if self.ValueMatrix is None:
            raise Exception("Full matrix mf%s was not found!" %xtmf_ValueMatrixNumber)
        
        if xtmf_WeightingMatrixNumber == 0:
            self.WeightingMatrix = None
        else:
            self.WeightingMatrix = _MODELLER.emmebank.matrix('mf%s' %xtmf_WeightingMatrixNumber)
            if self.WeightingMatrix is None:
                raise Exception("Full matrix mf%s was not found!" %xtmf_WeightingMatrixNumber)
        
        if xtmf_ScenarioNumber == 0:
            self.Scenario is None
        else:
            self.Scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
            if (self.Scenario is None):
                raise Exception("Scenario %s was not found!" %xtmf_ScenarioNumber)
        
        originFilter = self._ParseRangeSetString(xtmf_OriginRangeSetString)
        destinationFilter = self._ParseRangeSetString(xtmf_DestinationRangeSetString)
        
        self.ReportFile = ReportFile
        self.HistogramMin = HistogramMin
        self.HistogramMax = HistogramMax
        self.HistogramStepSize = HistogramStepSize
        
        try:
            self._Execute(originFilter, destinationFilter)
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            raise Exception(msg)
    
    ##########################################################################################################    
    
    
    def _Execute(self, originFilter, destinationFilter):
        with _m.logbook_trace(name="{classname} v{version}".format(classname=(self.__class__.__name__), version=self.version),
                                     attributes=self._GetAtts()):
            
            if self.Scenario:
                valueData = self.ValueMatrix.get_data(self.Scenario.number)
            else:
                valueData = self.ValueMatrix.get_data()
            self.TRACKER.completeTask() #1
            
            validIndices = [[p for p in valueData.indices[0] if originFilter(p)],
                            [q for q in valueData.indices[1] if destinationFilter(q)]]
            self.TRACKER.completeTask() #2
            
            valueSubmatrix = get_submatrix(valueData, validIndices)
            valueArray = array(valueSubmatrix.raw_data).flatten()
            self.TRACKER.completeTask() #3
            
            self.TRACKER.startProcess(6)
            #Start getting the relevant data from the matrix
            unweightedAverage = average(valueArray)
            self.TRACKER.completeSubtask()
            minVal = min(valueArray)
            self.TRACKER.completeSubtask()
            maxVal = max(valueArray)
            self.TRACKER.completeSubtask()
            unweightedStdDev = std(valueArray)
            self.TRACKER.completeSubtask()
            unweightedMedian = median(valueArray)
            self.TRACKER.completeSubtask()
            
            #Create the array of bins
            bins = [self.HistogramMin]
            if minVal < self.HistogramMin: bins.insert(0, minVal)
            c = self.HistogramMin + self.HistogramStepSize
            while c < self.HistogramMax:
                bins.append(c)
                c += self.HistogramStepSize
            bins.append(self.HistogramMax)
            if maxVal > self.HistogramMax: bins.append(maxVal)
            
            unweightedHistogram, ranges = histogram(valueArray, bins)
            self.TRACKER.completeSubtask()
            self.TRACKER.completeTask() #4
            
            #Get weighted values if neccessary
            if self.WeightingMatrix is not None:
                if self.Scenario:
                    weightData = self.WeightingMatrix.get_data(self.Scenario.number)
                else:
                    weightData = self.WeightingMatrix.get_data()
                self.TRACKER.completeTask() #5
                
                weightSubmatrix = get_submatrix(weightData, validIndices)
                weightArray = array(weightSubmatrix.raw_data).flatten()
                self.TRACKER.completeTask() #6
                
                self.TRACKER.startProcess(3)
                weightedAverage = average(valueArray, weights= weightArray)
                self.TRACKER.completeSubtask()
                weightedStdDev = self._WtdStdDev(valueArray, weightArray)
                self.TRACKER.completeSubtask()
                weightedHistogram, ranges = histogram(valueArray, weights= weightArray, bins = bins)
                self.TRACKER.completeSubtask()
                self.TRACKER.completeTask() #7
                
                
                if self.ReportFile:
                    self._WriteReportToFile(unweightedAverage,
                                            minVal, maxVal, unweightedStdDev, 
                                            unweightedMedian, unweightedHistogram, 
                                            bins, weightedAverage, weightedStdDev, 
                                            weightedHistogram)
                    print "Report written to %s" %self.ReportFile
                
                self._WriteReportToLogbook(unweightedAverage, minVal, maxVal, unweightedStdDev, 
                                           unweightedMedian, unweightedHistogram, bins, 
                                           weightedAverage, weightedStdDev, weightedHistogram)
                print "Report written to logbook."
                
            elif self.ReportFile:
                for i in range(3): self.TRACKER.completeTask()
                self._WriteReportToFile(unweightedAverage, minVal, 
                                        maxVal, unweightedStdDev, unweightedMedian, 
                                        unweightedHistogram, bins)
                print "Report written to %s" %self.ReportFile
                
                self._WriteReportToLogbook(unweightedAverage, minVal, maxVal, unweightedStdDev, 
                                           unweightedMedian, unweightedHistogram, bins)
                print "Report written to logbook."
                
            else:
                self._WriteReportToLogbook(unweightedAverage, minVal, maxVal, unweightedStdDev, 
                                           unweightedMedian, unweightedHistogram, bins)
                print "Report written to logbook."
            
            
            
            self.TRACKER.completeTask() #8

    ##########################################################################################################
    
    
    #----SUB FUNCTIONS---------------------------------------------------------------------------------  
    
    def _GetAtts(self):
        atts = {
                "Scenario" : str(self.Scenario),
                "Version": self.version, 
                "self": self.__MODELLER_NAMESPACE__}
            
        return atts
    
    def _ParseRangeSetString(self, rss):
        ranges = []
        cells = rss.split(',')
        for c in cells:
            r = c.split('-')
            start = int(r[0])
            end = int(r[1])
            rs = _util.IntRange(start, end)
            ranges.append(rs)
        
        def filter(v):
            for r in ranges:
                if v in r: return True
            return False
        
        return filter
    
    def _GetOriginFilterFunction(self):
        exec('''def filter(p):
    %s''' %self.OriginFilterExpression)
        
        return filter
    
    def _GetDestinationFilterFunction(self):
        exec('''def filter(q):
    %s''' %self.DestinationFilterExpression)
        
        return filter
    
    def _WtdStdDev(self, values, weights):
        avg = average(values, weights=weights)
        variance = average((values-avg)**2, weights=weights)
        return sqrt(variance)
    
    def _WriteReportToLogbook(self, 
                            unweightedAverage,
                            minVal,
                            maxVal,
                            unweightedStdDev,
                            unweightedMedian,
                            unweightedHistogram,
                            bins,
                            weightedAverage=None,
                            weightedStdDev=None,
                            weightedHistogram=None):
        
        #print "CURRENTLY DOES NOT WRITE REPORT TO LOGBOOK"
        #return
        
        pb = _m.PageBuilder(title="Matrix Summary Report")
        
        bodyText = "Summary for matrix: <b>{mtx1!s} - {desc1}</b> ({stamp1!s})\
        <br>Weighting Matrix: <b>{mtx2!s}".format(
                                                  mtx1= self.ValueMatrix, 
                                                  mtx2= self.WeightingMatrix,
                                                  desc1= self.ValueMatrix.description, 
                                                  stamp1= self.ValueMatrix.timestamp)
        if self.WeightingMatrix is not None: bodyText += " - %s" %self.WeightingMatrix.description
        bodyText += "</b><br>"
        
        rows = []
        rows.append("<b>Average:</b> %s" %unweightedAverage)
        rows.append("<b>Minimum:</b> %s" %minVal)
        rows.append("<b>Maximum:</b> %s" %maxVal)
        rows.append("<b>Standard Deviation:</b> %s" %unweightedStdDev)
        rows.append("<b>Median:</b> %s" %unweightedMedian)
        
        if weightedAverage is not None:
            rows.append("<br><br><b>Weighted Average:</b> %s" %weightedAverage)
            rows.append("<b>Weighted Standard Deviation:</b> %s" %weightedStdDev)
        
        bodyText += "<h3>Matrix Statistics</h3>" + "<br>".join(rows)
        pb.add_text_element(bodyText)
        
        #Build the chart data
        uwData = []
        wData = []
        for i, binEdge in enumerate(bins):
            if i == 0:
                prevEdge = binEdge
                continue #Skip the first
            
            if (i - 1) >= len(unweightedHistogram):
                uwVal = 0.0
            else:
                uwVal = unweightedHistogram[i - 1]
            uwData.append((int(prevEdge), float(uwVal)))
            
            if weightedHistogram is not None:
                if (i - 1) >= len(weightedHistogram):
                    wVal = 0.0
                else:
                    wVal = weightedHistogram[i - 1]
                wData.append((int(prevEdge), float(wVal)))
            
            prevEdge = binEdge
        
        cds = [{"title": "Unweighted frequency", 
                "data": uwData,
                "color": "red"}]
        if weightedHistogram is not None:
            cds.append({"title": "Weighted frequency",
                        "data": wData,
                        "color": "blue"})
        
        try:
            pb.add_chart_widget(chart_data_series=cds,
                options= {'table': True,
                          "plot_parameters": {
                               "series": {
                                   "stack": False,
                                   #"points": {"show": False},
                                   #"lines": {"show": False},
                                   "bars": {"show": True},
                                        }
                                    }
                          })
        except Exception as e:
            print cds
            raise
        
        _m.logbook_write("Matrix Summary Report for %s" %self.ValueMatrix,
                         value= pb.render())
    
    def _WriteReportToFile(self,
                            unweightedAverage,
                            minVal,
                            maxVal,
                            unweightedStdDev,
                            unweightedMedian,
                            unweightedHistogram,
                            bins,
                            weightedAverage=None,
                            weightedStdDev=None,
                            weightedHistogram=None):
        
        with open(self.ReportFile, 'w') as writer:
            writer.write('''Matrix Summary Report
#####################

Generated on %s\n\n''' %dt.now())
            
            writer.write("Matrix: {id!s} - {desc!s} ({stamp!s})".format(id= self.ValueMatrix,
                                                                        desc= self.ValueMatrix.description,
                                                                        stamp= self.ValueMatrix.timestamp))
            
            writer.write("\nWeight Matrix: %s" %self.WeightingMatrix)
            if self.WeightingMatrix is not None:
                writer.write(" - {desc!s} ({stamp!s})".format(desc= self.WeightingMatrix.description,
                                                              stamp = self.WeightingMatrix.timestamp))
            
            writer.write("\n\nAverage:\t%s" %unweightedAverage)
            writer.write("\nMinimum:\t%s" %minVal)
            writer.write("\nMaximum:\t%s" %maxVal)
            writer.write("\nStd. Dev:\t%s" %unweightedStdDev)
            writer.write("\n Median:\t%s" %unweightedMedian)
            
            if weightedAverage is not None:
                writer.write("\nWeighted Avg.:\t%s" %weightedAverage)
            if weightedStdDev is not None:
                writer.write("\nWeighted StDv:\t%s" %weightedStdDev)
           
            writer.write('''

-------------------------
HISTOGRAM
BinMin,BinMax,Freq''')
            
            if weightedHistogram is not None: writer.write(",wFreq")
            
            for i, binEdge in enumerate(bins):
                if i == 0:
                    prevEdge = binEdge
                    continue #Skip the first
                
                if (i - 1) >= len(unweightedHistogram):
                    uwVal = 0.0
                else:
                    uwVal = unweightedHistogram[i - 1]
                writer.write("\n%s,%s,%s" %(prevEdge, binEdge, uwVal))
                
                if weightedHistogram is not None:
                    if (i - 1) >= len(weightedHistogram):
                        wVal = 0.0
                    else:
                        wVal = weightedHistogram[i - 1]
                    writer.write(",%s" %wVal)
                    
                prevEdge = binEdge

                
    
    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()
                
    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
        
Beispiel #17
0
class ExtractPathsEMME(_m.Tool()):
    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1

    xtmf_ScenarioNumber = _m.Attribute(int)
    #xtmf_ClassName = _m.Attribute(str)
    #xtmf_IterationNumber = _m.Attribute(str)
    #xtmf_PathDetails = _m.Attribute(str)
    xtmf_OutputPathFile = _m.Attribute(str)

    def __init__(self):
        #---Init internal variables
        self.TRACKER = _util.ProgressTracker(
            self.number_of_tasks)  #init the ProgressTracker

    def page(self):
        pb = _m.ToolPageBuilder(self,
                                title="Path Analysis",
                                description="Cannot be called from Modeller.",
                                runnable=False,
                                branding_text="XTMF")

        return pb.render()

    def __call__(self, xtmf_ScenarioNumber, xtmf_OutputPathFile, xtmf_ODdist):
        self.ScenarioNumber = int(xtmf_ScenarioNumber)
        self.scenario = _m.Modeller().emmebank.scenario(self.ScenarioNumber)
        if (self.scenario == None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)
        if not self.scenario.has_transit_results:
            raise Exception(
                "Scenario %s does not have transit assignment results" %
                xtmf_ScenarioNumber)
        self.OutputPathFile = xtmf_OutputPathFile
        self.ODdist = xtmf_ODdist
        self.NumberOfProcessors = cpu_count()
        '''self.ClassName = xtmf_ClassName
        self.IterationNumber = xtmf_IterationNumber'''

        self.demandMatrices = _util.DetermineAnalyzedTransitDemandId(
            EMME_VERSION, self.scenario)
        configPath = dirname(_MODELLER.desktop.project_file_name()) \
                    + "/Database/STRATS_s%s/config" %self.ScenarioNumber
        with open(configPath) as reader:
            config = _parsedict(reader.readline())
            data = config['data']
            if 'multi_class' in data:
                if data['multi_class'] == True:
                    self.Multiclass = True
                else:
                    self.Multiclass = False
            else:
                if data['type'] == "MULTICLASS_TRANSIT_ASSIGNMENT":

                    self.Multiclass = True
                else:
                    self.Multiclass = False
        '''if self.Multiclass == True:
            self.AnalyzedDemandMatrixID = demandMatrices[self.ClassName]
        else:
            self.AnalyzedDemandMatrixID = demandMatrices
        self.SelectedPaths = xtmf_SelectedPaths
        self.SelectPathsBy = xtmf_SelectPathsBy
        self.SelectedPathsCriteria = xtmf_SelectedPathsCriteria
        self.SelectedPathsThresholdLower = xtmf_SelectedPathsThresholdLower
        self.SelectedPathsThresholdUpper = xtmf_SelectedPathsThresholdUpper
        self.PathDetail = xtmf_PathsDetails.split(',')
        #self.PathsToOutput = self._VerifyNonNullWithError(self.PathDetail[0],"Paths to Output must be specfied")
        self.TotalImpedenceDetails = self._ConvertToBool(self.PathDetail[0])
        self.AverageBoardingDetails = self._ConvertToBool(self.PathDetail[1])
        self.DistanceDetails = self._ConvertToBool(self.PathDetail[2])
        self.TimesAndCostsType = self._VerifyNonNullWithError(self.PathDetail[3],"Times and cost type must be defined")
        self.FirstWaitingTime = self._ConvertToBool(self.PathDetail[4])
        self.TotalWaitingTime = self._ConvertToBool(self.PathDetail[5])
        self.FirstBoardingTime = self._ConvertToBool(self.PathDetail[6])
        self.TotalBoardingTime = self._ConvertToBool(self.PathDetail[7])
        self.InVehicleTime = self._ConvertToBool(self.PathDetail[8])
        self.AuxTransitTime = self._ConvertToBool(self.PathDetail[9])
        self.FirstBoardingCost = self._ConvertToBool(self.PathDetail[10])
        self.TotalBoardingCost = self._ConvertToBool(self.PathDetail[11])
        self.InVehicleCost = self._ConvertToBool(self.PathDetail[12])
        self.AuxTransitCost = self._ConvertToBool(self.PathDetail[13])
        self.ZonesDetails = self._ConvertToBool(self.PathDetail[14])
        self.PathNumberDetails =self._ConvertToBool(self.PathDetail[15])
        self.ProportionDetails = self._ConvertToBool(self.PathDetail[16])
        self.SelectedVolumeDetails = self._ConvertToBool(self.PathDetail[17])
        self.VolumeDetails = self._ConvertToBool(self.PathDetail[18])
        self.PathValueDetails = self._ConvertToBool(self.PathDetail[19])
        self.PathItemDetails = self._ConvertToBool(self.PathDetail[20])
        self.NodesDetails = self._ConvertToBool(self.PathDetail[21])
        self.ModeDetails = self._ConvertToBool(self.PathDetail[22])
        self.TransitLineDetails = self._ConvertToBool(self.PathDetail[23])
        self.AuxTransitSubPathDetails = self._ConvertToBool(self.PathDetail[24])
        self.SubPathDetails = self._ConvertToBool(self.PathDetail[25])
        self.ODZoneDetails = self._ConvertToBool(self.PathDetail[26])
        self.ODPathStatDetails = self._ConvertToBool(self.PathDetail[27])
        self.ODPathNumberDetails = self._ConvertToBool(self.PathDetail[28])
        self.ODSelectedDemandDetails = self._ConvertToBool(self.PathDetail[29])
        self.ODDemandDetails = self._ConvertToBool(self.PathDetail[30])
        self.ODAggPathValue = self._ConvertToBool(self.PathDetail[31])
        self.ODDetails = self._ConvertToBool(self.PathDetail[32])'''

        try:
            self._Execute()
        except Exception, e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            raise Exception(msg)
Beispiel #18
0
class RotateNetwork(_m.Tool()):

    version = '0.1.0'
    tool_run_msg = ""
    number_of_tasks = 6  # 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__)

    Scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter
    ReferenceLinkINode = _m.Attribute(int)
    ReferenceLinkJNode = _m.Attribute(int)
    CorrespondingX0 = _m.Attribute(float)
    CorrespondingX1 = _m.Attribute(float)
    CorrespondingY0 = _m.Attribute(float)
    CorrespondingY1 = _m.Attribute(float)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Rotate Network v%s" % self.version,
            description=
            "Rotates & translates network based on two corresponding links.\
                         Select the node ids of a link in the network you want to rotate and \
                         translate, and then enter in the coordinates of the exact same link \
                         in your reference network.\
                         <br><br>Warning: this tool makes irreversible changes to your scenario! \
                         make sure you copy before running.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_text_box(tool_attribute_name='ReferenceLinkINode',
                        size=7,
                        title='Reference link i-node')

        pb.add_text_box(tool_attribute_name='ReferenceLinkJNode',
                        size=7,
                        title='Reference link j-node')

        with pb.add_table(visible_border=False,
                          title="Corresponding vector") as t:
            with t.table_cell():
                pb.add_html("Coordinate 0: ")

            with t.table_cell():
                pb.add_html("X=")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='CorrespondingX0', size=10)

            with t.table_cell():
                pb.add_html("Y=")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='CorrespondingY0', size=10)

            t.new_row()

            with t.table_cell():
                pb.add_html("Coordinate 1: ")

            with t.table_cell():
                pb.add_html("X=")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='CorrespondingX1', size=10)

            with t.table_cell():
                pb.add_html("Y=")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='CorrespondingY1', size=10)

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")

    ##########################################################################################################

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            network = self.Scenario.get_network()
            self.TRACKER.completeTask()

            anchorVecotr = ((self.CorrespondingX0, self.CorrespondingY0),
                            (self.CorrespondingX1, self.CorrespondingY1))

            refLink = self._GetRefLink(network)
            referenceVector = self._GetLinkVector(refLink)
            _m.logbook_write(
                "Found reference link '%s-%s'" %
                (self.ReferenceLinkINode, self.ReferenceLinkJNode))

            angle = self._GetRotationAngle(anchorVecotr,
                                           referenceVector)  # + math.pi / 2
            _m.logbook_write("Rotation: %s degrees" % math.degrees(angle))
            cosTheta = math.cos(angle)
            sinTheta = math.sin(angle)

            self.TRACKER.startProcess(network.element_totals['centroids'] +
                                      network.element_totals['regular_nodes'])
            for node in network.nodes():
                self._RotateNode(node, cosTheta, sinTheta)
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()
            _m.logbook_write("Finished rotating nodes.")

            self.TRACKER.startProcess(network.element_totals['links'])
            count = 0
            for link in network.links():
                if len(link.vertices) > 0:
                    self._RotateLinkVertices(link, cosTheta, sinTheta)
                    count += 1
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()
            _m.logbook_write("Rotated %s links with vertices." % count)

            referenceVector = self._GetLinkVector(
                refLink)  # Reset the reference vector
            delta = self._GetTranslation(referenceVector, anchorVecotr)
            _m.logbook_write("Translation: %s" % str(delta))

            self.TRACKER.startProcess(network.element_totals['centroids'] +
                                      network.element_totals['regular_nodes'])
            for node in network.nodes():
                self._TranslateNode(node, delta)
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()
            _m.logbook_write("Finished translating nodes.")

            self.TRACKER.startProcess(network.element_totals['links'])
            count = 0
            for link in network.links():
                if len(link.vertices) > 0:
                    self._TranslateLink(link, delta)
                    count += 1
                self.TRACKER.completeSubtask()
            self.TRACKER.completeTask()
            _m.logbook_write("Translated %s links with vertices." % count)

            self.Scenario.publish_network(network, resolve_attributes=True)
            self.TRACKER.completeTask()

    #########################################################################################################

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.Scenario.id),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _GetRefLink(self, network):
        link = network.link(self.ReferenceLinkINode, self.ReferenceLinkJNode)

        if link is None:
            raise Exception(
                "Reference link '%s-%s' does not exist in the network!" %
                (self.ReferenceLinkINode, self.ReferenceLinkJNode))
        return link

    def _GetLinkVector(self, link):
        return ((link.i_node.x, link.i_node.y), (link.j_node.x, link.j_node.y))

    def _GetVectorBearing(self, vector):
        return math.atan2(vector[1][0] - vector[0][0],
                          vector[1][1] - vector[0][1])

    def _GetRotationAngle(self, vector1, vector2):

        bearing1 = self._GetVectorBearing(vector1)
        bearing2 = self._GetVectorBearing(vector2)

        return bearing2 - bearing1

    def _GetTranslation(self, vector1, vector2):
        return (vector2[0][0] - vector1[0][0], vector2[0][1] - vector1[0][1])

    def _RotateNode(self, node, cosTheta, sinTheta):
        # Make copies of the coordinates
        x = node.x
        y = node.y

        node.x = (cosTheta * x) + (-sinTheta * y)
        node.y = (sinTheta * x) + (cosTheta * y)

    def _TranslateNode(self, node, delta):
        node.x += delta[0]
        node.y += delta[1]

    def _RotateLinkVertices(self, link, cosTheta, sinTheta):
        vertices = [link.vertices.pop() for i in range(0, len(link.vertices))]
        vertices.reverse()
        # Link's vertices have been removed and copied in-order to
        #this new list

        for vertex in vertices:
            tup = (cosTheta * vertex[0] - sinTheta * vertex[1],
                   sinTheta * vertex[0] + cosTheta * vertex[1])
            link.vertices.append(tup)

    def _TranslateLink(self, link, delta):
        vertices = [link.vertices.pop() for i in range(0, len(link.vertices))]
        vertices.reverse()
        # Link's vertices have been removed and copied in-order to
        #this new list

        for vertex in vertices:
            tup = (vertex[0] + delta[0], vertex[1] + delta[1])
            link.vertices.append(tup)

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
class ExtractTransitLineBoardings(_m.Tool()):

    version = '1.0.2'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    #---PARAMETERS

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    Scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter

    LineAggregationFile = _m.Attribute(str)
    ReportFile = _m.Attribute(str)

    WriteIndividualRoutesFlag = _m.Attribute(bool)
    ReportErrorsToLogbookFlag = _m.Attribute(bool)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario
        self.WriteIndividualRoutesFlag = True

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Export Boardings v%s" % self.version,
            description=
            "Extracts total boardings for each transit line and exports \
                         them in a CSV file. Optionally, lines can be aggregated using an \
                         external file (two-column CSV).",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_select_file(tool_attribute_name='ReportFile',
                           title="Report file",
                           file_filter="*.csv",
                           window_type='save_file')

        pb.add_header("AGGREGATION FILE")

        pb.add_select_file(tool_attribute_name='LineAggregationFile',
                           title="Line aggregation file:",
                           window_type='file',
                           file_filter="*.csv",
                           note="<font color='green'><b>Optional: \
                            </b></font>Aggregation file contains two columns with no headers, matching transit\
                            <br>line IDs to their aliases or groups in another data source (e.g., TTS line IDs). The\
                            <br>first column must be Emme transit line IDs. Any errors are skipped."
                           )

        pb.add_checkbox(
            tool_attribute_name='WriteIndividualRoutesFlag',
            label="Write individual routes?",
            note=
            "Write individual routes that are not found in the aggregation file. \
                            <br>This is the default behaviour if no aggregation file is specified."
        )

        pb.add_checkbox(
            tool_attribute_name='ReportErrorsToLogbookFlag',
            label="Report errors to the Logbook?",
            note=
            "Write a report if there are lines referenced in the aggregation file but are not in the network."
        )

        #---JAVASCRIPT
        pb.add_html("""
<script type="text/javascript">
    $(document).ready( function ()
    {
        var tool = new inro.modeller.util.Proxy(%s) ;
        
        if (tool.check_agg_file())
        {
            $("#WriteIndividualRoutesFlag").prop('disabled', false);
            $("#ReportErrorsToLogbookFlag").prop('disabled', false);
        } else {
            $("#WriteIndividualRoutesFlag").prop('disabled', true);
            $("#ReportErrorsToLogbookFlag").prop('disabled', true);
        }

        $("#LineAggregationFile").bind('change', function()
        {
            $(this).commit();
            
            $("#WriteIndividualRoutesFlag").prop('disabled', false);
            $("#ReportErrorsToLogbookFlag").prop('disabled', false);
        });
    });
</script>""" % pb.tool_proxy_tag)

        return pb.render()

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            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=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg

    @_m.method(return_type=bool)
    def check_agg_file(self):
        return bool(self.LineAggregationFile)

    #---
    #---XTMF INTERFACE METHODS

    def __call__(self, xtmf_ScenarioNumber, ReportFile, LineAggregationFile,
                 WriteIndividualRoutesFlag):

        #---1 Set up scenario
        self.Scenario = _MODELLER.emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        self.ReportFile = ReportFile
        if LineAggregationFile:
            self.LineAggregationFile = LineAggregationFile
            self.ReportErrorsToLogbookFlag = False
        self.WriteIndividualRoutesFlag = WriteIndividualRoutesFlag

        try:
            self._Execute()
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc()
            raise Exception(msg)

    ##########################################################################################################

    #---
    #---MAIN EXECUTION CODE

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            if not self.Scenario.has_transit_results:
                raise Exception("Scenario %s has no transit results" %
                                self.Scenario)

            if self.LineAggregationFile:
                groupLines = self._LoadAggregationFile()
            else:
                groupLines = {}

            fileKeys, networkKeys = self._CheckAggregation(groupLines)

            self._ExportResults(fileKeys, networkKeys, groupLines)

    ##########################################################################################################

    #----Sub functions

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.Scenario),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _LoadAggregationFile(self):
        with open(self.LineAggregationFile) as reader:
            groupLines = {}

            for line in reader:
                if line.isspace(): continue
                lineId, groupId = line.strip().split(',')

                if groupId in groupLines: groupLines[groupId].add(lineId)
                else: groupLines[groupId] = set([lineId])

            return groupLines

    def _CheckAggregation(self, groupLines):
        fileLineIDs = set()
        for IDs in groupLines.itervalues():
            for id in IDs:
                fileLineIDs.add(id)

        data = _util.fastLoadTransitLineAttributes(self.Scenario, ['headway'])
        networkLineIDs = set(data.keys())

        linesMissingInNetwork = fileLineIDs - networkLineIDs
        linesNotInAggregationFile = networkLineIDs - fileLineIDs

        if self.ReportErrorsToLogbookFlag and len(linesMissingInNetwork) > 0:
            self._WriteErrorReport(linesMissingInNetwork)

        if self.WriteIndividualRoutesFlag:
            return list(groupLines.keys()), list(linesNotInAggregationFile)
        else:
            return list(groupLines.keys()), []

    def _WriteErrorReport(self, linesMissingInNetwork):
        h = HTML()
        t = h.table()
        tr = t.tr()
        tr.th("Line ID")
        for id in linesMissingInNetwork:
            tr = t.tr()
            tr.td(str(id))

        pb = _m.PageBuilder(title="Lines not in network report")

        pb.wrap_html("Lines references in file but not in network",
                     body=str(t))

        _m.logbook_write("Error report", value=pb.render())

    def _ExportResults(self, fileKeys, networkKeys, groupLines):
        fileKeys.sort()
        networkKeys.sort()

        lineBoardings = _util.fastLoadSummedSegmentAttributes(
            self.Scenario, ['transit_boardings'])

        with open(self.ReportFile, 'w') as writer:
            writer.write("Line,Boardings")

            for key in fileKeys:
                boardings = 0.0
                if key in groupLines:
                    lineIDs = groupLines[key]

                    for id in lineIDs:
                        if not id in lineBoardings: continue
                        boardings += lineBoardings[id]['transit_boardings']

                writer.write("\n%s,%s" % (key, boardings))

            for key in networkKeys:
                boardings = 0.0
                if key in lineBoardings:
                    boardings = lineBoardings[key]['transit_boardings']
                writer.write("\n%s,%s" % (key, boardings))
Beispiel #20
0
class ExtractTransitMatrixResults(_m.Tool()):

    version = '1.0.0'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    #---PARAMETERS

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only
    scenario = _m.Attribute(_m.InstanceType)  # common variable or parameter

    xtmf_ModeList = _m.Attribute(str)
    xtmf_MatrixNumbers = _m.Attribute(str)
    xtmf_AnalysisTypes = _m.Attribute(str)
    xtmf_ClassNames = _m.Attribute(str)

    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.scenario = _MODELLER.scenario  #Default is primary scenario

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):
        pb = _m.ToolPageBuilder(self,
                                title="Multi-Class Road Assignment",
                                description="Cannot be called from Modeller.",
                                runnable=False,
                                branding_text="XTMF")
        return pb.render()

    def __call__(self, xtmf_ScenarioNumber, xtmf_ModeList, xtmf_MatrixNumbers,
                 xtmf_AnalysisTypes, xtmf_ClassNames):

        # Set up scenario
        self.scenario = _MODELLER.emmebank.scenario(xtmf_ScenarioNumber)
        if (self.scenario is None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        #set up mode lists
        modes = xtmf_ModeList.strip(" ").split(",")
        self.ModeList = []
        for i in range(0, len(modes)):
            self.ModeList.append([])
            for mode in modes[i]:
                self.ModeList[i].append(mode)
        self.MatrixIDList = xtmf_MatrixNumbers.strip(" ").split(",")

        if len(self.ModeList) != len(self.MatrixIDList):
            raise Exception(
                "Each analysis must have mode(s) and matrices defined")

        self.AnalysisTypeList = []
        types = xtmf_AnalysisTypes.strip(" ").split(",")
        for i in range(0, len(types)):
            if types[i].lower() == "distance":
                self.AnalysisTypeList.append(1)
            elif types[i].lower() == "actualtime":
                self.AnalysisTypeList.append(2)
            elif types[i].lower() == "actualcost":
                self.AnalysisTypeList.append(3)
            elif types[i].lower() == "perceivedtime":
                self.AnalysisTypeList.append(4)
            elif types[i].lower() == "perceivedcost":
                self.AnalysisTypeList.append(5)
            else:
                raise Exception("You must specify a proper analysis type")

        self.ClassNames = xtmf_ClassNames.strip(" ").split(",")
        names = _util.DetermineAnalyzedTransitDemandId(EMME_VERSION,
                                                       self.scenario)

        if isinstance(names, dict):
            self.Multiclass = True
            for name in self.ClassNames:
                if name not in names.keys():
                    raise Exception("Class Name %s is not correct" % name)
        else:
            self.Multiclass = False

        try:
            self._Execute()
        except Exception as e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            raise Exception(msg)

    ##########################################################################################################

    #---
    #---MAIN EXECUTION CODE

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):
            print "Extracting Transit Result Matrices"
            if not self.scenario.has_transit_results:
                raise Exception("Scenario %s has no transit results" %
                                self.scenario)

            #initialize the matrices
            for matrix in self.MatrixIDList:
                _util.initializeMatrix(matrix)

            with self._getTempMatrices():

                #Get Spec and do Analysis

                for i in range(0, len(self.ModeList)):
                    spec = self._GetBaseSpec(self.ModeList[i],
                                             self.TempMatrices,
                                             self.AnalysisTypeList[i])
                    if self.Multiclass == True:
                        report = self.TRACKER.runTool(
                            matrixResultsTool,
                            specification=spec,
                            scenario=self.scenario,
                            class_name=self.ClassNames[i])
                    else:
                        report = self.TRACKER.runTool(matrixResultsTool,
                                                      specification=spec,
                                                      scenario=self.scenario)
                    spec = {
                        "type":
                        "MATRIX_CALCULATION",
                        "result":
                        self.MatrixIDList[i],
                        "expression":
                        str(self.TempMatrices[0].id) + "+" +
                        str(self.TempMatrices[1].id),
                        "constraint":
                        None
                    }
                    report = self.TRACKER.runTool(matrixCalcTool,
                                                  specification=spec,
                                                  scenario=self.scenario)

            print "Finished Extracting Transit Result Matrices"

    ##########################################################################################################

    #----Sub functions

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.scenario),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _GetBaseSpec(self, modeList, matrix, analysisType):
        spec = {
            "type": "EXTENDED_TRANSIT_MATRIX_RESULTS",
            "by_mode_subset": {
                "modes": modeList,
                "distance": None,
                "avg_boardings": None,
                "actual_total_boarding_times": None,
                "actual_in_vehicle_times": None,
                "actual_aux_transit_times": None,
                "actual_total_boarding_costs": None,
                "actual_in_vehicle_costs": None,
                "actual_aux_transit_costs": None,
                "perceived_total_boarding_times": None,
                "perceived_in_vehicle_times": None,
                "perceived_aux_transit_times": None,
                "perceived_total_boarding_costs": None,
                "perceived_in_vehicle_costs": None,
                "perceived_aux_transit_costs": None,
            }
        }
        if analysisType == 1:
            spec["by_mode_subset"]["distance"] = matrix[0].id
        if analysisType == 2:
            spec["by_mode_subset"]["actual_in_vehicle_times"] = matrix[0].id
            spec["by_mode_subset"]["actual_aux_transit_times"] = matrix[1].id
        if analysisType == 3:
            spec["by_mode_subset"]["actual_in_vehicle_costs"] = matrix[0].id
            spec["by_mode_subset"]["actual_aux_transit_costs"] = matrix[1].id
        if analysisType == 4:
            spec["by_mode_subset"]["perceived_in_vehicle_times"] = matrix[0].id
            spec["by_mode_subset"]["perceived_aux_transit_times"] = matrix[
                1].id
        if analysisType == 5:
            spec["by_mode_subset"]["perceived_in_vehicle_costs"] = matrix[0].id
            spec["by_mode_subset"]["perceived_aux_transit_costs"] = matrix[
                1].id
        return spec

    @contextmanager
    def _getTempMatrices(self):
        self.TempMatrices = []
        created = {}
        for i in range(0, 2):
            matrixCreated = True
            mtx = _util.initializeMatrix(default=0.0, description= 'Temporary matrix for matrix results', \
                        matrix_type='FULL')
            self.TempMatrices.append(mtx)
            created[mtx.id] = matrixCreated
        try:
            yield self.TempMatrices
        finally:
            for key in created:
                if created[key] == True:
                    _bank.delete_matrix(key)
class Volume_Extractor(_m.Tool()):

    BaseScenario = _m.Attribute(_m.InstanceType)
    SegmentAttribute = _m.Attribute(str)
    LinkAttribute = _m.Attribute(str)
    tool_run_msg = ""
    number_of_tasks = 4

    def __init__(self):
        self.BaseScenario = _MODELLER.scenario  #Default is primary scenario
        self.SegmentAttribute = "@cvolt"
        self.LinkAttribute = "@volut"
        self.TRACKER = _util.ProgressTracker(self.number_of_tasks)

    def page(self):

        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Transit Volume Extractor from a Hypernetwork",
            description=
            "Extracts the ca_voltr values from the Hypernetwork and assigns it to a Link Extra Attribute,\
                     This allows for the visualization of Transit Volumes in aggregate.\
                     <br><br><em> Requires storage space for two extra attributes. </em>",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_header("SCENARIO")

        pb.add_select_scenario(tool_attribute_name='BaseScenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_header("DEFINING ATTRIBUTES")

        keyval2 = []
        keyval3 = []
        keyval4 = [(-1, "None - Do not save segment base info")]
        for exatt in self.BaseScenario.extra_attributes():
            if exatt.type == 'TRANSIT_SEGMENT':
                val = "%s - %s" % (exatt.name, exatt.description)
                keyval2.append((exatt.name, val))
            elif exatt.type == 'LINK':
                val = "%s - %s" % (exatt.name, exatt.description)
                keyval3.append((exatt.name, val))
                keyval4.append((exatt.name, val))

        pb.add_select(
            tool_attribute_name='SegmentAttribute',
            keyvalues=keyval2,
            title="Segment Attribute Selector",
            note="Select a TRANSIT SEGMENT extra attribute in which to save \
                      Transit Volumes")

        pb.add_select(tool_attribute_name='LinkAttribute',
                      keyvalues=keyval3,
                      title="Link Attribute Selector",
                      note="Select a  LINK extra attribute in which \
                      to the Transit Segment Volumes.")

        return pb.render()

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception, e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")
class ExportCountpostResults(_m.Tool()):
    
    version = '1.1.3'
    tool_run_msg = ""
    number_of_tasks = 1 # 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__)
    
    xtmf_ScenarioNumber = _m.Attribute(int) # parameter used by XTMF only
    Scenario = _m.Attribute(_m.InstanceType) # common variable or parameter
    
    CountpostAttributeId = _m.Attribute(str)
    AlternateCountpostAttributeId = _m.Attribute(str)
    
    ExportFile = _m.Attribute(str)
    version = '1.1.2'
    
    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.Scenario = _MODELLER.scenario #Default is primary scenario
        self.CountpostAttributeId = "@stn1"
        self.AlternateCountpostAttributeId = "@stn2"        
    
    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(self, title="Export Countpost Results v%s" %self.version,
                     description="Exports traffic assignment results on links flagged with \
                         a countpost number.",
                     branding_text="- TMG Toolbox")
        
        if self.tool_run_msg != "": # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)
            
        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)
        
        keyval1 = []
        keyval2 = [(-1, 'None - No attribute')]
        for att in _MODELLER.scenario.extra_attributes():
            if att.type == 'LINK':
                text = "%s - %s" %(att.id, att.description)
                keyval1.append((att.id, text))
                keyval2.append((att.id, text))
        
        pb.add_select(tool_attribute_name='CountpostAttributeId',
                      keyvalues=keyval1,
                      title="Countpost Attribute",
                      note="LINK attribute containing countpost id numbers")
        
        pb.add_select(tool_attribute_name='AlternateCountpostAttributeId',
                      keyvalues=keyval2,
                      title="Alternate Countpost Attribute",
                      note="<font color='green'><b>Optional:</b></font> Alternate countpost attribute \
                      for multiple post per link")
        
        pb.add_select_file(tool_attribute_name='ExportFile',
                           window_type='save_file',
                           file_filter="*.csv",
                           title="Export File")
        
        pb.add_html("""
<script type="text/javascript">
    $(document).ready( function ()
    {
        var tool = new inro.modeller.util.Proxy(%s) ;

        $("#Scenario").bind('change', function()
        {
            $(this).commit();
            
            $("#CountpostAttributeId")
                .empty()
                .append(tool.preload_scenario_attributes())
            inro.modeller.page.preload("#CountpostAttributeId");
            $("#CountpostAttributeId").trigger('change');
            
            $("#AlternateCountpostAttributeId")
                .empty()
                .append("<option value='-1'>None - No attribute</option>")
                .append(tool.preload_scenario_attributes())
            inro.modeller.page.preload("#AlternateCountpostAttributeId");
            $("#AlternateCountpostAttributeId").trigger('change');
        });
    });
</script>""" % pb.tool_proxy_tag)
        
        
        return pb.render()
    
    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()
                
    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
    
    @_m.method(return_type=unicode)
    def preload_scenario_attributes(self):
        list = []
        
        for att in self.Scenario.extra_attributes():
            label = "{id} - {name}".format(id=att.name, name=att.description)
            html = unicode('<option value="{id}">{text}</option>'.format(id=att.name, text=label))
            list.append(html)
        return "\n".join(list)
    
    ##########################################################################################################
        
    def run(self):
        self.tool_run_msg = ""
        
        try:
            if not self.Scenario.has_traffic_results:
                raise Exception("Scenario %s has no traffic assignment results" %self.Scenario.number)
            
            if self.CountpostAttributeId is None: raise NullPointerException("Countpost Attribute not specified")
            if self.ExportFile is None: raise NullPointerException("Export File not specified")
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise    
        
        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise
        
        self.tool_run_msg = _m.PageBuilder.format_info("Done.")
                
    def __call__(self, xtmf_ScenarioNumber, CountpostAttributeId, AlternateCountpostAttributeId,
                 ExportFile):
        
        #---1 Set up scenario
        self.Scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario is None):
            raise Exception("Scenario %s was not found!" %xtmf_ScenarioNumber)
        
        if not self.Scenario.has_traffic_results:
            raise Exception("Scenario %s has no traffic assignment results" %self.Scenario.number)
        
        linkAtts = set([att.id for att in self.Scenario.extra_attributes() if att.type == 'LINK'])
        
        if not CountpostAttributeId in linkAtts:
            raise NullPointerException("'%s' is not a valid link attribute" %CountpostAttributeId)
        if AlternateCountpostAttributeId != "" and not AlternateCountpostAttributeId in linkAtts:
            raise NullPointerException("'%s' is not a valid link attribute" %AlternateCountpostAttributeId)
        
        self.CountpostAttributeId = CountpostAttributeId
        self.AlternateCountpostAttributeId = AlternateCountpostAttributeId
        self.ExportFile = ExportFile
        
        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()):
            self.TRACKER.reset()
            
            linkResults = _util.fastLoadLinkAttributes(self.Scenario, [self.CountpostAttributeId,
                                                                       'auto_volume',
                                                                       'additional_volume',
                                                                       'auto_time'])
            
            alternateLinkResults = {}
            if self.AlternateCountpostAttributeId and self.AlternateCountpostAttributeId != "":
                alternateLinkResults = _util.fastLoadLinkAttributes(self.Scenario, 
                                                                    [self.AlternateCountpostAttributeId])
            
            #Remove entries not flagged with a countpost
            self._CleanResults(linkResults, alternateLinkResults)
            
            #Get the countpost data, sorted
            lines = self._ProcessResults(linkResults, alternateLinkResults)
            
            #Write countpost data to file
            self._WriteReport(lines)
            

    ##########################################################################################################
    
    #----SUB FUNCTIONS---------------------------------------------------------------------------------  
    
    def _GetAtts(self):
        atts = {
                "Scenario" : str(self.Scenario.id),
                "Countpost Attribute": self.CountpostAttributeId,
                "Alternate Countpost Attribute": self.AlternateCountpostAttributeId,
                "Export File": self.ExportFile,
                "Version": self.version, 
                "self": self.__MODELLER_NAMESPACE__}
            
        return atts
    
    def _CleanResults(self, linkResults, alternateLinkResults):
        idsToRemove = []
        for linkId, attributes in linkResults.iteritems():
            post1 = attributes[self.CountpostAttributeId]
            post2 = 0
            if linkId in alternateLinkResults:
                post2 = alternateLinkResults[linkId][self.AlternateCountpostAttributeId] 
            
            if not post1 and not post2:
                idsToRemove.append(linkId)
        for key in idsToRemove:
            linkResults.pop(key) 
    
    def _ProcessResults(self, linkResults, alternateLinkResults):
        lines = []
        
        posts = 0
        self.TRACKER.startProcess(len(linkResults))
        for linkIdTuple, attributes in linkResults.iteritems():
            linkId = "%s-%s" %linkIdTuple

            post1 = attributes[self.CountpostAttributeId]
            post2 = 0
            if linkIdTuple in alternateLinkResults:
                post2 = alternateLinkResults[linkIdTuple][self.AlternateCountpostAttributeId]
            volau = attributes['auto_volume']
            volad = attributes['additional_volume']
            timau = attributes['auto_time']
            
            data = [linkId, volau, volad, timau]
            
            if post1:
                lines.append((post1, linkId, volau, volad, timau))
                posts += 1
            if post2:
                lines.append((post2, linkId, volau, volad, timau))
                posts += 1
            self.TRACKER.completeSubtask()
        _m.logbook_write("Found %s countposts in network" %posts)
        lines.sort()
        return lines
    
    def _WriteReport(self, lines):
        with open(self.ExportFile, 'w') as writer:
            writer.write("Countpost,Link,Auto Volume,Additional Volume,Auto Time")
            for line in lines:
                line = [str(c) for c in line]
                writer.write("\n" + ','.join(line))
        _m.logbook_write("Wrote report to %s" %self.ExportFile)
Beispiel #23
0
class CreateTimePeriodNetworks(_m.Tool()):

    version = '0.1.5'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    COLON = ':'
    COMMA = ','

    # 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__)

    BaseScenario = _m.Attribute(_m.InstanceType)
    NewScenarioNumber = _m.Attribute(int)
    NewScenarioDescription = _m.Attribute(str)

    TransitServiceTableFile = _m.Attribute(str)
    AggTypeSelectionFile = _m.Attribute(str)
    AlternativeDataFile = _m.Attribute(str)

    TimePeriodStart = _m.Attribute(int)
    TimePeriodEnd = _m.Attribute(int)

    DefaultAgg = _m.Attribute(str)

    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
        self.DefaultAgg = 'n'

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Create Time Period Network v%s" % self.version,
            description=
            "Creates a network for use in a given time period, from a \
                         24-hour base network and corresponding transit service table. \
                         Line speeds and headways are calculated from the service table.\
                         Transit lines with no service in the time period are removed.\
                         Headway calculations are performed based on a choice of\
                         aggregation type. Agg type by line is loaded in from a file\
                         which can be generated using the Create Aggregation\
                         Selection File tool.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='BaseScenario',
                               title='Base Scenario',
                               allow_none=False)

        with pb.add_table(False) as t:

            with t.table_cell():
                pb.add_new_scenario_select(
                    tool_attribute_name='NewScenarioNumber',
                    title="New scenario to create")
            with t.table_cell():
                pb.add_text_box(tool_attribute_name='NewScenarioDescription',
                                title="Description",
                                size=40)

        pb.add_header("DATA FILES")

        pb.add_select_file(tool_attribute_name='TransitServiceTableFile',
                           window_type='file',
                           file_filter='*.csv',
                           title="Transit service table",
                           note="Requires three columns:\
                               <ul><li>emme_id</li>\
                               <li>trip_depart</li>\
                               <li>trip_arrive</li></ul>")

        pb.add_select_file(tool_attribute_name='AlternativeDataFile',
                           window_type='file',
                           file_filter='*.csv',
                           title="Data for non-service table lines (optional)",
                           note="Requires columns as follows,\
                               where xxxx corresponds to\
                               the desired time period start:\
                               <ul><li>emme_id</li>\
                               <li>xxxx_hdw</li>\
                               <li>xxxx_spd</li></ul>.\
                               Note: this will override\
                               values calculated from\
                               the service table")

        pb.add_select_file(tool_attribute_name='AggTypeSelectionFile',
                           window_type='file',
                           file_filter='*.csv',
                           title="Aggregation Type Selection",
                           note="Requires two columns:\
                               <ul><li>emme_id</li>\
                               <li>agg_type</li></ul>")

        pb.add_header("TOOL INPUTS")

        keyval1 = {'n': 'Naive', 'a': 'Average'}
        pb.add_radio_group(tool_attribute_name='DefaultAgg',
                           keyvalues=keyval1,
                           title="Default Aggregation Type",
                           note="Used if line not in\
                               agg selection file")

        with pb.add_table(False) as t:

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='TimePeriodStart',
                                size=4,
                                title="Time period start",
                                note="In integer hours e.g. 2:30 PM = 1430")

            with t.table_cell():
                pb.add_text_box(tool_attribute_name='TimePeriodEnd',
                                size=4,
                                title="Time period end",
                                note="In integer hours e.g. 2:30 PM = 1430")

        return pb.render()

    ##########################################################################################################
    # allows for the tool to be called from another tool
    def __call__(self, baseScen, newScenNum, newScenDescrip, serviceFile,
                 aggFile, altFile, defAgg, start, end, additionalAltFiles):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        self.BaseScenario = baseScen
        self.NewScenarioNumber = newScenNum
        self.NewScenarioDescription = newScenDescrip
        self.TransitServiceTableFile = serviceFile
        self.AggTypeSelectionFile = aggFile
        self.AlternativeDataFile = altFile
        # Process the additional files, if it is the string None then there are no additional files otherwise they are ; separated
        if additionalAltFiles is None or additionalAltFiles == "None":
            self.InputFiles = []
        else:
            self.InputFiles = additionalAltFiles.split(';', 1)
        # Add the base transaction file to the beginning
        if altFile:
            self.InputFiles.insert(0, altFile)
        self.DefaultAgg = defAgg
        self.TimePeriodStart = start
        self.TimePeriodEnd = end

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()
        if self.AlternativeDataFile is None:
            self.InputFiles = []
        else:
            self.InputFiles = [self.AlternativeDataFile]
        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc())
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")

    ##########################################################################################################

    def _Execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._GetAtts()):

            network = self.BaseScenario.get_network()
            self.TRACKER.completeTask()
            print("Loaded network")

            start = self._ParseIntTime(self.TimePeriodStart)
            end = self._ParseIntTime(self.TimePeriodEnd)

            badIdSet = self._LoadServiceTable(network, start, end).union(
                self._LoadAggTypeSelect(network))
            self.TRACKER.completeTask()
            print("Loaded service table")
            if len(badIdSet) > 0:
                print(
                    "%s transit line IDs were not found in the network and were skipped."
                    % len(badIdSet))
                pb = _m.PageBuilder("Transit line IDs not in network")

                pb.add_text_element(
                    "<b>The following line IDs were not found in the network:</b>"
                )

                for id in badIdSet:
                    pb.add_text_element(id)

                _m.logbook_write(
                    "Some IDs were not found in the network. Click for details.",
                    value=pb.render())

            if len(self.InputFiles) <= 0:
                self._ProcessTransitLines(network, start, end, None)
            else:
                if self.AlternativeDataFile:
                    altData = self._LoadAltFile(self.InputFiles)
                else:
                    altData = None
                self._ProcessTransitLines(network, start, end, altData)
                if altData:
                    self._ProcessAltLines(network, altData)
            print("Done processing transit lines")

            newScenario = _MODELLER.emmebank.copy_scenario(
                self.BaseScenario.id, self.NewScenarioNumber)
            newScenario.title = self.NewScenarioDescription

            print("Publishing network")
            network.delete_attribute('TRANSIT_LINE', 'trips')
            network.delete_attribute('TRANSIT_LINE', 'aggtype')
            newScenario.publish_network(network)

    ##########################################################################################################

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _GetAtts(self):
        atts = {
            "Scenario": str(self.BaseScenario.id),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _ParseIntTime(self, i):
        try:
            hours = i / 100
            minutes = i % 100

            return hours * 3600.0 + minutes * 60.0
        except Exception as e:
            raise IOError("Error parsing time %s: %s" % (i, e))

    def _ParseStringTime(self, s):
        try:
            hms = s.split(self.COLON)
            if len(hms) != 3: raise IOError()

            hours = int(hms[0])
            minutes = int(hms[1])
            seconds = int(hms[2])

            return hours * 3600.0 + minutes * 60.0 + float(seconds)
        except Exception as e:
            raise IOError("Error parsing time %s: %s" % (s, e))

    def _ParseAggType(self, a):
        choiceSet = ('n', 'a')
        try:
            agg = a[0].lower()
            if agg not in choiceSet: raise IOError()
            else: return agg
        except Exception as e:
            raise IOError(
                "You must select either naive or average as an aggregation type %s: %s"
                % (a, e))

    def _LoadServiceTable(self, network, start, end):
        network.create_attribute('TRANSIT_LINE', 'trips', None)

        bounds = _util.FloatRange(start, end)
        badIds = set()

        if self.TransitServiceTableFile:
            with open(self.TransitServiceTableFile) as reader:
                header = reader.readline()
                cells = header.strip().split(self.COMMA)

                emmeIdCol = cells.index('emme_id')
                departureCol = cells.index('trip_depart')
                arrivalCol = cells.index('trip_arrive')

                for num, line in enumerate(reader):
                    cells = line.strip().split(self.COMMA)

                    id = cells[emmeIdCol]
                    transitLine = network.transit_line(id)

                    if transitLine is None:
                        badIds.add(id)
                        continue  #Skip and report

                    try:
                        departure = self._ParseStringTime(cells[departureCol])
                        arrival = self._ParseStringTime(cells[arrivalCol])
                    except Exception as e:
                        print("Line " + str(num) + " skipped: " + str(e))
                        continue

                    if not departure in bounds:
                        continue  #Skip departures not in the time period

                    trip = (departure, arrival)
                    if transitLine.trips is None: transitLine.trips = [trip]
                    else: transitLine.trips.append(trip)

        return badIds

    def _LoadAggTypeSelect(self, network):
        network.create_attribute('TRANSIT_LINE', 'aggtype', None)

        badIds = set()
        if self.AggTypeSelectionFile:
            with open(self.AggTypeSelectionFile) as reader:
                header = reader.readline()
                cells = header.strip().split(self.COMMA)

                emmeIdCol = cells.index('emme_id')
                aggCol = cells.index('agg_type')

                for num, line in enumerate(reader):
                    cells = line.strip().split(self.COMMA)

                    id = cells[emmeIdCol]
                    transitLine = network.transit_line(id)

                    if transitLine is None:
                        badIds.add(id)
                        continue  #Skip and report

                    try:
                        aggregation = self._ParseAggType(cells[aggCol])
                    except Exception as e:
                        print("Line " + num + " skipped: " + str(e))
                        continue

                    if transitLine.aggtype is None:
                        transitLine.aggtype = aggregation

        return badIds

    def _LoadAltFile(self, fileNames):
        altData = {}
        for fileName in fileNames:
            with open(fileName) as reader:
                header = reader.readline()
                cells = header.strip().split(self.COMMA)

                emmeIdCol = cells.index('emme_id')
                headwayTitle = "{:0>4.0f}".format(
                    self.TimePeriodStart) + '_hdw'
                speedTitle = "{:0>4.0f}".format(self.TimePeriodStart) + '_spd'
                try:
                    headwayCol = cells.index(headwayTitle)
                except Exception as e:
                    msg = "Error. No headway match for specified time period start: '%s'." % self._ParseIntTime(
                        self.TimePeriodStart)
                    _m.logbook_write(msg)
                    print(msg)
                try:
                    speedCol = cells.index(speedTitle)
                except Exception as e:
                    msg = "Error. No speed match for specified time period start: '%s'." % self._ParseIntTime(
                        self.TimePeriodStart)
                    _m.logbook_write(msg)
                    print(msg)

                localAltData = {}

                for num, line in enumerate(reader):
                    cells = line.strip().split(self.COMMA)

                    id = cells[emmeIdCol]
                    hdw = cells[headwayCol]
                    spd = cells[speedCol]
                    if id not in localAltData:
                        localAltData[id] = (float(hdw), float(spd))
                    else:
                        raise ValueError(
                            'Line %s has multiple entries. Please revise your alt file.'
                            % id)
                #now that the file has been loaded in move it into the combined altFile dictionary
            for id, data in six.iteritems(localAltData):
                altData[id] = data
        return altData

    def _ProcessTransitLines(self, network, start, end, altData):
        bounds = _util.FloatRange(0.01, 1000.0)

        toDelete = set()
        if altData is not None:
            for k, v in altData.items(
            ):  #check if any headways or speeds are zero. Allow those lines to be deletable
                if v[0] == 0 or v[1] == 0:
                    del altData[k]
                #if v[0] == 9999: #prep an unused line for deletion
                #    toDelete.add(k)
            doNotDelete = altData.keys()
        else:
            doNotDelete = []
        self.TRACKER.startProcess(network.element_totals['transit_lines'])
        for line in network.transit_lines():
            #Pick aggregation type for given line
            if line.aggtype == 'n':
                aggregator = naiveAggregation
            elif line.aggtype == 'a':
                aggregator = averageAggregation
            elif self.DefaultAgg == 'n':
                aggregator = naiveAggregation
                _m.logbook_write("Default aggregation was used for line %s" %
                                 (line.id))
            else:
                aggregator = averageAggregation
                _m.logbook_write("Default aggregation was used for line %s" %
                                 (line.id))

            if not line.trips:  #Line trips list is empty or None
                if doNotDelete:
                    if line.id not in doNotDelete:  #don't delete lines whose headways we wish to manually set
                        toDelete.add(line.id)
                elif line.id not in toDelete:
                    toDelete.add(line.id)
                self.TRACKER.completeSubtask()
                continue

            #Calc line headway
            departures = [dep for dep, arr in line.trips]
            departures.sort()
            headway = aggregator(departures, start,
                                 end) / 60.0  #Convert from seconds to minutes

            if not headway in bounds:
                print("%s: %s" % (line.id, headway))
            line.headway = headway

            #Calc line speed
            sumTimes = 0
            for dep, arr in line.trips:
                sumTimes += arr - dep
            avgTime = sumTimes / len(
                line.trips) / 3600.0  #Convert from seconds to hours
            length = sum([seg.link.length
                          for seg in line.segments()])  #Given in km
            speed = length / avgTime  #km/hr
            if not speed in bounds:
                print("%s: %s" % (line.id, speed))
            line.speed = speed

            self.TRACKER.completeSubtask()

        for id in toDelete:
            network.delete_transit_line(id)
        self.TRACKER.completeTask()

    def _ProcessAltLines(self, network, altData):
        bounds = _util.FloatRange(0.01, 1000.0)
        for key, data in six.iteritems(altData):
            line = network.transit_line(key)
            if line:
                if data[0] == 9999:  #a headway of 9999 indicates an unused line
                    network.delete_transit_line(line.id)
                    continue
                elif data[
                        0] == 0:  #a headway of 0 allows for a line to be in the alt data file without changing existing headway
                    print("%s: %s" % (line.id, data[0]))
                    _m.logbook_write(
                        "Headway = 0 in alt file. Headway remains as in base.  %s"
                        % line.id)
                elif not data[0] in bounds:
                    print("%s: %s" % (line.id, data[0]))
                    _m.logbook_write(
                        "Headway out of bounds line %s: %s minutes. Line removed from network."
                        % (line.id, data[0]))
                    network.delete_transit_line(line.id)
                    continue
                line.headway = data[0]
                if not data[1] in bounds:
                    print("%s: %s" % (line.id, data[1]))
                    _m.logbook_write(
                        "Speed out of bounds line %s: %s km/h. Speed remains as in base."
                        % (line.id, data[1]))
                    continue
                line.speed = data[1]

    @_m.method(return_type=_m.TupleType)
    def percent_completed(self):
        return self.TRACKER.getProgress()

    @_m.method(return_type=six.u)
    def tool_run_msg_status(self):
        return self.tool_run_msg
Beispiel #24
0
class ExportGtfsStopsAsShapefile(_m.Tool()):

    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # 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__)

    GtfsFolderName = _m.Attribute(str)
    ShapefileName = _m.Attribute(str)

    def __init__(self):
        #---Init internal variables
        self.TRACKER = _util.ProgressTracker(
            self.number_of_tasks)  #init the ProgressTracker

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="Export GTFS Stops As Shapefile v%s" % self.version,
            description=
            "Converts the <b>stops.txt</b> file to a shapefile, flagging which \
                             modes it serves as well.",
            branding_text="- TMG Toolbox 2")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_file(tool_attribute_name="GtfsFolderName",
                           window_type='directory',
                           title="GTFS Folder Directory")

        pb.add_select_file(tool_attribute_name="ShapefileName",
                           window_type='save_file',
                           title="Shapefile Name for Export")

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Tool is completed.")

    ##########################################################################################################

    def run_xtmf(self, parameters):
        self.GtfsFolderName = parameters['gtfs_folder']
        self.ShapefileName = parameters['shapefile_name']
        try:
            self._Execute()
        except Exception, e:
            raise Exception(_traceback.format_exc(e))
Beispiel #25
0
class FlagPremiumBusLines(_m.Tool()):

    version = '0.1.0'
    tool_run_msg = ""

    #---Variable definitions
    ScenarioNumber = _m.Attribute(int)
    FlagGO = _m.Attribute(bool)
    FlagPremTTC = _m.Attribute(bool)
    FlagVIVA = _m.Attribute(bool)
    FlagZum = _m.Attribute(bool)

    #---Special instance types, used only from Modeller
    scenario = _m.Attribute(_m.InstanceType)

    def __init__(self):
        self.counter = 0

    def page(self):
        pb = _m.ToolPageBuilder(
            self,
            title="Flag Premium Bus Lines",
            description=
            "Flags certain premium lines by assigning '1' to line extra attribute '@lflag'. Initializes \
                         @lflag to 0 first.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='scenario',
                               title='Scenario:',
                               allow_none=False)

        pb.add_checkbox(tool_attribute_name='FlagGO',
                        title="Flag GO Bus lines?")

        pb.add_checkbox(tool_attribute_name='FlagPremTTC',
                        title="Flag Premium TTC bus lines?")

        pb.add_checkbox(tool_attribute_name='FlagVIVA',
                        title="Flag VIVA bus lines?",
                        note="Assumes NCS11 line ids.")

        pb.add_checkbox(tool_attribute_name='FlagZum',
                        title="Flag ZUM bus lines?",
                        note="CURRENTLY UNSUPPORTED.")

        return pb.render()

    def run(self):
        '''Run is called from Modeller.'''
        self.tool_run_msg = ""
        self.isRunningFromXTMF = False

        # Run the tool
        try:
            self._execute()
        except Exception, e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info(
            "Tool completed. %s lines were flagged." % self.counter)
class ConvertVehicles(_m.Tool()):

    version = '0.1.2'
    tool_run_msg = ""

    #---Variable definitions
    ScenarioNumber = _m.Attribute(int)

    #---Special instance types
    scenario = _m.Attribute(_m.InstanceType)  #

    def page(self):
        pb = _m.ToolPageBuilder(
            self,
            title="Convert Vehicles v%s" % self.version,
            description="Converts vehicle definitions and properties \
                                    according to NCS11 definitions.",
            branding_text="- TMG Toolbox")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name="scenario",
                               title="Select scenario",
                               allow_none=False)

        return pb.render()

    ##########################################################################################################

    def run(self):
        self.tool_run_msg = ""
        '''Run is called from Modeller.'''
        self.isRunningFromXTMF = False

        try:
            self._execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Run complete.")

    ##########################################################################################################

    def _execute(self):
        with _m.logbook_trace(name="{classname} v{version}".format(
                classname=(self.__class__.__name__), version=self.version),
                              attributes=self._getAtts()):

            network = self.scenario.get_network()

            self._prepVehicleLineMap(network)

            #---Create new vehicle ids
            with _m.logbook_trace("Adding vehicle 19"):
                v19 = network.create_transit_vehicle(19, 'g')  # Unused.
                self._changeVehicleProperties(v19, "DblDeckBus", 2.5, 80, 80)

            with _m.logbook_trace("Adding vehicle 18"):
                v18 = network.create_transit_vehicle(18, 'g')
                self._replaceVehicle(network.transit_vehicle(10),
                                     v18)  #Copy old V10
                self._changeVehicleProperties(v18, "GoBus", 2.5, 55, 55)

            with _m.logbook_trace("Adding vehicle 17"):
                v17 = network.create_transit_vehicle(17, 'q')  # Reserved.
                self._changeVehicleProperties(v17, description="BRT")

            with _m.logbook_trace("Adding vehicle 16"):
                v16 = network.create_transit_vehicle(16, 'b')  #Copy old V7
                self._replaceVehicle(network.transit_vehicle(7), v16)
                self._changeVehicleProperties(v16,
                                              "Bus18",
                                              aeq=3.0,
                                              scap=55,
                                              tcap=85)

            with _m.logbook_trace("Adding vehicle 15"):
                v15 = network.create_transit_vehicle(
                    15, 'b')  # Maybe this should be 'q'?
                self._changeVehicleProperties(v15, "Deluxe18", 3.0, 70, 70)

            with _m.logbook_trace("Adding vehicle 14"):
                v14 = network.create_transit_vehicle(
                    14, 'b')  # Maybe this should be 'q'?
                self._changeVehicleProperties(v14, "Deluxe12", 2.5, 45, 45)

            with _m.logbook_trace("Adding vehicle 13"):
                v13 = network.create_transit_vehicle(13, 'b')  # Copy old V8
                self._replaceVehicle(network.transit_vehicle(8), v13)
                self._changeVehicleProperties(v13, "Bus12", 2.5, 35, 55)

            with _m.logbook_trace("Adding vehicle 12"):
                v12 = network.create_transit_vehicle(12, 'b')
                self._replaceVehicle(network.transit_vehicle(9),
                                     v12)  # Copy old V9
                self._changeVehicleProperties(v12, "Bus9", 2.5, 25, 40)

            with _m.logbook_trace("Adding vehicle 11"):
                v11 = network.create_transit_vehicle(
                    11, 's')  # Unused (new TTC SC)
                self._changeVehicleProperties(v11, "LFLRV30", 3.5, 70, 130)

            #---Move old vehicle ids
            with _m.logbook_trace("Modifying vehicle 10"):
                v10 = network.transit_vehicle(10)
                self._changeVehicleMode(network, v10, 's')
                self._replaceVehicle(network.transit_vehicle(6),
                                     v10)  # Copy old V6
                self._changeVehicleProperties(v10, "ALRV23", 3.5, 60, 110)

            with _m.logbook_trace("Modifying vehicle 9"):
                v9 = network.transit_vehicle(9)
                self._changeVehicleMode(network, v9, 's')
                self._replaceVehicle(network.transit_vehicle(5),
                                     v9)  # Copy old V5
                self._changeVehicleProperties(v9, "CLRV16", 3.0, 45, 75)

            with _m.logbook_trace("Modifying vehicle 8"):
                v8 = network.transit_vehicle(8)
                self._changeVehicleMode(
                    network, v8, 'l')  # Reserved (Eglinton Crosstown LRT)
                self._changeVehicleProperties(v8, "LRV")

            with _m.logbook_trace("Modifying vehicle 7"):
                v7 = network.transit_vehicle(7)
                self._changeVehicleMode(network, v7, 'l')
                self._replaceVehicle(network.transit_vehicle(4),
                                     v7)  # Copy old V4
                self._changeVehicleProperties(v7, "SCxROW", scap=45, tcap=75)

            with _m.logbook_trace("Modifying vehicle 6"):
                v6 = network.transit_vehicle(6)
                self._changeVehicleMode(network, v6,
                                        'm')  # Unused (new subway Rocket cars)
                self._changeVehicleProperties(v6,
                                              "Sub6carRkt",
                                              scap=400,
                                              tcap=1100)

            with _m.logbook_trace("Modifying vehicle 5"):
                v5 = network.transit_vehicle(5)
                self._changeVehicleMode(network, v5, 'm')
                self._replaceVehicle(network.transit_vehicle(2),
                                     v5)  # Copy old V2
                self._changeVehicleProperties(v5,
                                              "Sub6carT1",
                                              scap=400,
                                              tcap=1000)
                '''
                TODO:
                - Hard code the re-coding of Sheppard subway
                '''

            with _m.logbook_trace("Modifying vehicle 4"):
                v4 = network.transit_vehicle(4)
                self._changeVehicleMode(network, v4, 'm')
                self._changeVehicleProperties(v4,
                                              "Sub4carT1",
                                              scap=260,
                                              tcap=670)

            with _m.logbook_trace("Modifying vehicle 3"):
                v3 = network.transit_vehicle(3)
                self._changeVehicleProperties(v3,
                                              "SRT4car",
                                              scap=120,
                                              tcap=220)

            with _m.logbook_trace("Modifying vehicle 2"):
                v2 = network.transit_vehicle(2)
                self._changeVehicleMode(network, v2, 'r')
                self._changeVehicleProperties(v2,
                                              "GoTrain12",
                                              scap=1900,
                                              tcap=1900)

            with _m.logbook_trace("Modifying vehicle 1"):
                #v1 remains unchanged.
                v1 = network.transit_vehicle(1)
                self._changeVehicleProperties(v1,
                                              "GoTrain10",
                                              scap=1600,
                                              tcap=1900)

            self.scenario.publish_network(network)

    ##########################################################################################################

    #----SUB FUNCTIONS---------------------------------------------------------------------------------

    def _getAtts(self):
        atts = {
            "Scenario": str(self.scenario.id),
            "Version": self.version,
            "self": self.__MODELLER_NAMESPACE__
        }

        return atts

    def _prepVehicleLineMap(self, network):
        self.vm = {}  # Set up vehicle map

        for line in network.transit_lines():
            try:
                self.vm[line.vehicle].append(line)
            except KeyError:
                self.vm[line.vehicle] = [line]

    def _checkFixScenario(self, network):
        vehiclesToBeDeleted = []

        for veh in network.transit_vehicles():
            if self.veh.number <= 10:
                continue

            if self.veh in self.vm:
                raise Exception(
                    "A vehicle id not compliant with DMG2001 was used in \
                    the network and cannot be removed as it is being used by %s transit \
                    line(s)." % len(self.vm[veh]))

            vehiclesToBeDeleted.append(veh.number)

        for id in vehiclesToBeDeleted:
            network.delete_transit_vehicle(id)
            _m.logbook_write("Deleted unused vehicle %s" % id)

    def _replaceVehicle(self, oldVehicle, newVehicle):
        if not oldVehicle in self.vm:
            return

        for line in self.vm[oldVehicle]:
            line.vehicle = newVehicle

        _m.logbook_write("Changed {0} line(s) with vehicle \
                    {1} to use {2}.".format(len(self.vm[oldVehicle]),
                                            oldVehicle.number,
                                            newVehicle.number))

    def _changeVehicleProperties(self,
                                 vehicle,
                                 description="",
                                 aeq=0.0,
                                 scap=0,
                                 tcap=0):

        if description != "" and description is not None:
            vehicle.description = description
            _m.logbook_write("Description = '%s'" % description)

        if aeq != 0.0:
            vehicle.auto_equivalent = aeq
            _m.logbook_write("Auto equivalence = %s" % aeq)

        if scap != 0:
            vehicle.seated_capacity = scap
            _m.logbook_write("Seated capacity = %s" % scap)

        if tcap < scap:
            tcap = scap

        if tcap != 0:
            vehicle.total_capacity = tcap
            _m.logbook_write("Total capacity = %s" % tcap)

    def _changeVehicleMode(self, network, vehicle, modeChar):
        oldMode = vehicle.mode.id
        vehicle._mode = network.mode(modeChar)
        _m.logbook_write("Changed mode of vehicle {0} \
                from {1} to {2}.".format(vehicle.id, oldMode, modeChar))

    @_m.method(return_type=unicode)
    def tool_run_msg_status(self):
        return self.tool_run_msg
class GTFStoEmmeMap(_m.Tool()):
    version = '0.0.2'
    tool_run_msg = ""
    number_of_tasks = 1

    #Tool Parameters
    FileName = _m.Attribute(str)
    MappingFileName = _m.Attribute(str)

    def __init__(self):
        #---Init internal variables
        self.TRACKER = _util.ProgressTracker(
            self.number_of_tasks)  #init the ProgressTracker

    def page(self):

        pb = _tmgTPB.TmgToolPageBuilder(
            self,
            title="GTFS Stops to Emme Node File v%s" % self.version,
            description=
            "Takes the <b>stops.txt</b> file or a <b>shapefile</b> to create a mapping file that shows \
                             the node in the EMME network which it corresponds to.",
            branding_text="- TMG Toolbox 2")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_file(
            tool_attribute_name="FileName",
            window_type='file',
            file_filter="*.txt *.shp",
            title=
            "stops.txt file from the GTFS folder or stops file in *.shp format"
        )

        pb.add_select_file(tool_attribute_name="MappingFileName",
                           window_type='save_file',
                           file_filter='*.csv',
                           title="Map file to export")

        return pb.render()

    def __call__(self, StopFileName, MappingFileName):
        self.FileName = StopFileName
        self.MappingFileName = MappingFileName
        self.scenarioNumber = ScenarioNumber

        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception as e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done")

    ##########################################################################################################

    def run_xtmf(self, parameters):
        self.FileName = parameters['input_stop_file']
        self.MappingFileName = parameters['output_mapping_file']
        try:
            self._Execute()
        except Exception, e:
            raise Exception(_traceback.format_exc(e))
class ApplyBatchLineEdits(_m.Tool()):

    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    COLON = ':'
    COMMA = ','

    # Tool Input Parameters
    #    Only those parameters necessary for Modeller and/or XTMF to dock with
    #    need to be placed here. Internal parameters (such as lists and dicts)
    #    get initialized during construction (__init__)

    xtmf_ScenarioNumber = _m.Attribute(int)  # parameter used by XTMF only

    InstructionFile = _m.Attribute(
        str)  # file should have the following header:
    # filter|x_hdwchange|x_spdchange
    # where filter is a network calculator filter expression
    # x refers to the scenario number
    # the x columns can be multiple (ie. multiple definitions
    # in a single file)
    # hdwchange and spdchange are factors by which
    # to change headways and speeds for the filtered
    # lines
    additionalInputFiles = _m.Attribute(
        str
    )  #Either a string containing 'None' or a list of additional alt files ; separated.

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    def page(self):

        pb = _m.ToolPageBuilder(self,
                                title="Apply Batch Line Edits",
                                description="Cannot be called from Modeller.",
                                runnable=False,
                                branding_text="XTMF")

        return pb.render()

    ##########################################################################################################

    def __call__(self,
                 xtmf_ScenarioNumber,
                 inputFile,
                 additionalInputFiles=None):

        #---1 Set up scenario
        self.Scenario = _m.Modeller().emmebank.scenario(xtmf_ScenarioNumber)
        if (self.Scenario == None):
            raise Exception("Scenario %s was not found!" % xtmf_ScenarioNumber)

        #---2 Set up instruction file
        self.InstructionFile = inputFile
        if (self.InstructionFile == None):
            raise Exception("Need to provide an input file.")
        # Process the additional files, if it is the string None then there are no additional files otherwise they are ; separated
        if additionalInputFiles == None or additionalInputFiles == "None":
            self.InputFiles = []
        else:
            self.InputFiles = additionalInputFiles.split(';')
        # Add the base transaction file to the beginning
        self.InputFiles.insert(0, self.InstructionFile)
        try:
            self._Execute()
        except Exception, e:
            msg = str(e) + "\n" + _traceback.format_exc(e)
            raise Exception(msg)
Beispiel #29
0
class ExtractGoInVehicleTime(_m.Tool()):

    version = '0.0.1'
    tool_run_msg = ""
    number_of_tasks = 1  # For progress reporting, enter the integer number of tasks here

    #---PARAMETERS

    xtmf_ScenarioNumber = _m.Attribute(int)
    Scenario = _m.Attribute(_m.InstanceType)
    ResultMatrixId = _m.Attribute(str)

    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.Scenario = _MODELLER.scenario  #Default is primary scenario

    ##########################################################################################################
    #---
    #---MODELLER INTERACE METHODS

    def page(self):
        pb = _tmgTPB.TmgToolPageBuilder(self,
                                        title="[TOOL NAME] v%s" % self.version,
                                        description="[DESCRIPTION]",
                                        branding_text="TMG")

        if self.tool_run_msg != "":  # to display messages in the page
            pb.tool_run_status(self.tool_run_msg_status)

        pb.add_select_scenario(tool_attribute_name='Scenario',
                               title='Scenario:',
                               allow_none=False)
        '''
        def add_select_output_matrix(self, tool_attribute_name,
                                 matrix_types= ['FULL'],
                                 title= "", note= "",
                                 include_none= True,
                                 include_next= True,
                                 include_existing= False,
                                 include_new= False):
        '''

        pb.add_select_output_matrix(tool_attribute_name='ResultMatrixId',
                                    include_none=False,
                                    include_existing=True,
                                    include_new=True,
                                    title="Result Matrix")

        return pb.render()

    def run(self):
        self.tool_run_msg = ""
        self.TRACKER.reset()

        try:
            self._Execute()
        except Exception, e:
            self.tool_run_msg = _m.PageBuilder.format_exception(
                e, _traceback.format_exc(e))
            raise

        self.tool_run_msg = _m.PageBuilder.format_info("Done.")
Beispiel #30
0
class AttachCentriodsToNodes(_m.Tool()):
    version = '0.0.1'
    ScenarioNumber = _m.Attribute(int)
    Centroids = _m.Attribute(str)
    Nodes = _m.Attribute(str)
    
    def page(self):
        pb = _m.ToolPageBuilder(self, title="Attach Centroids To Nodes",
                     runnable=False,
                     description="Cannot be called from Modeller.",
                     branding_text="XTMF")
        
        return pb.render()
    
    def run(self):
        pass

    def __call__(self, ScenarioNumber, Nodes, Centroids):  
        try:
            self._execute(ScenarioNumber, Nodes, Centroids)
        except Exception as e:
            raise Exception(_traceback.format_exc(e))

    def _execute(self, ScenarioNumber, Nodes, Centroids):
        nodesToAttachTo = Nodes.split(";")
        centroidNumbers = Centroids.split(";")
        project = _MODELLER.emmebank
        scenario = project.scenario(str(ScenarioNumber))
        network = scenario.get_network()
        #TODO: Un-hardcode this to read in the modes from XTMF
        linkType = 1
        linkSpeed = 40
        lanes = 2.0

        centroidSet = set([network.mode('c'), 
                                 network.mode('h'), 
                                 network.mode('i'),
                                 network.mode('f'),
                                 network.mode('e'),
                                 network.mode('d'),
                                 network.mode('v')])
        for i in range(len(nodesToAttachTo)):
            nodeToAttachTo = self._get_node(network, nodesToAttachTo[i])
            if nodeToAttachTo is None:
                raise Exception("Unable to find a node with the ID " + nodesToAttachTo[i])
            #check to see if the centroid already exists
            centroidNode = self._get_node(network, centroidNumbers[i])
            if centroidNode is not None:
                network.delete_node(centroidNode.id, True)
            centroidNode = network.create_centroid(centroidNumbers[i])
            centroidNode.x = nodeToAttachTo.x
            centroidNode.y = nodeToAttachTo.y
            linkTo = network.create_link(centroidNumbers[i], nodesToAttachTo[i], centroidSet)
            linkFrom = network.create_link(nodesToAttachTo[i], centroidNumbers[i], centroidSet)
            linkTo.length = 0.0
            linkFrom.length = 0.0
            linkTo.type = linkType
            linkTo.num_lanes = lanes
            linkTo.data2 = linkSpeed
            linkTo.data3 = 9999
            linkFrom.type = linkType
            linkFrom.num_lanes = lanes
            linkFrom.data2 = linkSpeed
            linkFrom.data3 = 9999
        scenario.publish_network(network)

    def _get_node(self, network, nodeString):
        return network.node(nodeString)