Beispiel #1
0
class TrafficAssignmentDialog(QDialog, FORM_CLASS):
    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.path = standard_path()
        self.output_path = None
        self.temp_path = None
        self.error = None
        self.output = None
        self.report = None
        self.method = {}

        self.matrix = None
        self.graph = Graph()
        self.results = AssignmentResults()

        # Signals for the matrix tab
        self.but_load_new_matrix.clicked.connect(self.find_matrices)
        self.display_matrix.stateChanged.connect(self.display_matrix_or_not)

        # Signals from the Network tab
        self.load_graph_from_file.clicked.connect(self.load_graph)

        # Signals for the algorithm tab
        self.progressbar0.setVisible(False)
        self.progressbar0.setValue(0)
        self.progress_label0.setVisible(False)

        self.do_assignment.clicked.connect(self.run)
        self.cancel_all.clicked.connect(self.exit_procedure)
        self.select_output_folder.clicked.connect(
            self.choose_folder_for_outputs)

        self.cb_choose_algorithm.addItem('All-Or-Nothing')
        self.cb_choose_algorithm.currentIndexChanged.connect(
            self.changing_algorithm)

        # slots for skim tab
        self.add_skim.clicked.connect(self.select_skim)

        self.changing_algorithm()

        # path file
        self.path_file = OutputType()

        # Queries
        tables = [self.select_link_list, self.list_link_extraction]
        for table in tables:
            table.setColumnWidth(0, 280)
            table.setColumnWidth(1, 40)
            table.setColumnWidth(2, 150)
            table.setColumnWidth(3, 40)

        self.graph_properties_table.setColumnWidth(0, 190)
        self.graph_properties_table.setColumnWidth(1, 240)
        #critical link
        self.but_build_query.clicked.connect(
            partial(self.build_query, 'select link'))
        self.do_select_link.stateChanged.connect(
            self.set_behavior_special_analysis)
        self.tot_crit_link_queries = 0
        self.critical_output = OutputType()

        # link flow extraction
        self.but_build_query_extract.clicked.connect(
            partial(self.build_query, 'Link flow extraction'))
        self.do_extract_link_flows.stateChanged.connect(
            self.set_behavior_special_analysis)
        self.tot_link_flow_extract = 0
        self.link_extract = OutputType()

        # Disabling resources not yet implemented
        self.do_output_to_csv.setEnabled(False)
        self.do_select_link.setEnabled(False)
        self.but_build_query.setEnabled(False)
        self.select_link_list.setEnabled(False)

        self.do_extract_link_flows.setEnabled(False)
        self.but_build_query_extract.setEnabled(False)
        self.list_link_extraction.setEnabled(False)

    def build_query(self, purpose):
        if purpose == 'select link':
            button = self.but_build_query
            message = 'Select Link Analysis'
            table = self.select_link_list
            counter = self.tot_crit_link_queries

        if purpose == 'Link flow extraction':
            button = self.but_build_query_extract
            message = 'Link flow extraction'
            table = self.list_link_extraction
            counter = self.tot_link_flow_extract

        button.setEnabled(False)
        dlg2 = LoadSelectLinkQueryBuilderDialog(self.iface, self.graph.graph,
                                                message)
        dlg2.exec_()

        if dlg2.links is not None:
            table.setRowCount(counter + 1)
            text = ''
            for i in dlg2.links:
                text = text + ', (' + i[0].encode(
                    'utf-8') + ', "' + i[1].encode('utf-8') + '")'
            text = text[2:]
            table.setItem(counter, 0, QTableWidgetItem(text))
            table.setItem(counter, 1, QTableWidgetItem(dlg2.query_type))
            table.setItem(counter, 2, QTableWidgetItem(dlg2.query_name))
            del_button = QPushButton('X')
            del_button.clicked.connect(
                partial(self.click_button_inside_the_list, purpose))
            table.setCellWidget(counter, 3, del_button)
            counter += 1

        if purpose == 'select link':
            self.tot_crit_link_queries = counter

        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract = counter

        button.setEnabled(True)

    def click_button_inside_the_list(self, purpose):
        if purpose == 'select link':
            table = self.select_link_list
        elif purpose == 'Link flow extraction':
            table = self.list_link_extraction

        button = self.sender()
        index = self.select_link_list.indexAt(button.pos())
        row = index.row()
        table.removeRow(row)

        if purpose == 'select link':
            self.tot_crit_link_queries -= 1
        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract -= 1

    def choose_folder_for_outputs(self):
        new_name = GetOutputFolderName(self.path,
                                       'Output folder for traffic assignment')
        if new_name:
            self.output_path = new_name
            self.lbl_output.setText(new_name)
        else:
            self.output_path = None
            self.lbl_output.setText(new_name)

    def select_skim(self):
        pass

    def load_graph(self):
        self.lbl_graphfile.setText('')

        file_types = ["AequilibraE graph(*.aeg)"]
        default_type = '.aeg'
        box_name = 'Traffic Assignment'
        graph_file, type = GetOutputFileName(self, box_name, file_types,
                                             default_type, self.path)

        if graph_file is not None:
            self.graph.load_from_disk(graph_file)

            not_considering_list = self.graph.required_default_fields
            not_considering_list.pop(-1)
            not_considering_list.append('id')
            not_considering_list.append('direction')

            for i in list(self.graph.graph.dtype.names):
                if i not in not_considering_list:
                    self.minimizing_field.addItem(i)
            self.lbl_graphfile.setText(graph_file)

            self.results.prepare(self.graph)
            cores = get_parameter_chain(['system', 'cpus'])
            self.results.set_cores(cores)

            # show graph properties
            def centers_item(item):
                cell_widget = QWidget()
                lay_out = QHBoxLayout(cell_widget)
                lay_out.addWidget(item)
                lay_out.setAlignment(Qt.AlignCenter)
                lay_out.setContentsMargins(0, 0, 0, 0)
                cell_widget.setLayout(lay_out)
                return cell_widget

            self.graph_properties_table.clearContents()
            self.graph_properties_table.setRowCount(5)

            self.graph_properties_table.setItem(0, 0,
                                                QTableWidgetItem('Graph ID'))
            self.graph_properties_table.setItem(
                0, 1, QTableWidgetItem(self.graph.__id__))

            self.graph_properties_table.setItem(
                1, 0, QTableWidgetItem('Number of links'))
            self.graph_properties_table.setItem(
                1, 1, QTableWidgetItem(str(self.graph.num_links)))

            self.graph_properties_table.setItem(
                2, 0, QTableWidgetItem('Number of nodes'))
            self.graph_properties_table.setItem(
                2, 1, QTableWidgetItem(str(self.graph.num_nodes)))

            self.graph_properties_table.setItem(
                3, 0, QTableWidgetItem('Number of centroids'))
            self.graph_properties_table.setItem(
                3, 1, QTableWidgetItem(str(self.graph.centroids)))

            self.graph_properties_table.setItem(
                4, 0, QTableWidgetItem('Block flows through centroids'))
            chb2 = QCheckBox()
            chb2.setChecked(self.graph.block_centroid_flows)
            self.graph_properties_table.setCellWidget(4, 1, centers_item(chb2))

        else:
            self.graph = Graph()
        self.set_behavior_special_analysis()

    def set_behavior_special_analysis(self):
        if self.graph.num_links < 1:
            behavior = False
        else:
            behavior = True

        self.do_path_file.setEnabled(behavior)

        # This line of code turns off the features of select link analysis and link flow extraction while these
        #features are still being developed
        behavior = False

        self.do_select_link.setEnabled(behavior)
        self.do_extract_link_flows.setEnabled(behavior)

        self.but_build_query.setEnabled(behavior *
                                        self.do_select_link.isChecked())
        self.select_link_list.setEnabled(behavior *
                                         self.do_select_link.isChecked())

        self.list_link_extraction.setEnabled(
            behavior * self.do_extract_link_flows.isChecked())
        self.but_build_query_extract.setEnabled(
            behavior * self.do_extract_link_flows.isChecked())

    def changing_algorithm(self):
        if self.cb_choose_algorithm.currentText() == 'All-Or-Nothing':
            self.method['algorithm'] = 'AoN'

    def run_thread(self):
        self.progress_label0.setVisible(True)
        QObject.connect(self.worker_thread,
                        SIGNAL("ProgressValue( PyQt_PyObject )"),
                        self.progress_value_from_thread)
        QObject.connect(self.worker_thread,
                        SIGNAL("ProgressText( PyQt_PyObject )"),
                        self.progress_text_from_thread)
        QObject.connect(self.worker_thread,
                        SIGNAL("ProgressMaxValue( PyQt_PyObject )"),
                        self.progress_range_from_thread)
        QObject.connect(self.worker_thread,
                        SIGNAL("finished_threaded_procedure( PyQt_PyObject )"),
                        self.job_finished_from_thread)
        self.worker_thread.start()
        self.exec_()

    def find_matrices(self):
        dlg2 = LoadMatrixDialog(self.iface)
        dlg2.show()
        dlg2.exec_()
        if dlg2.matrix is not None:
            self.matrix = dlg2.matrix
            self.display_matrix_or_not()
        else:
            self.matrix = None

    def display_matrix_or_not(self):
        if self.display_matrix.isChecked() and self.matrix is not None:
            row_headers = []
            col_headers = []
            for i in range(self.matrix.shape[0]):
                row_headers.append(str(i))

            for j in range(self.matrix.shape[1]):
                col_headers.append(str(j))

            m = NumpyModel(self.matrix, col_headers, row_headers)
            self.matrix_viewer.setModel(m)
        else:
            self.matrix_viewer.clearSpans()

    def job_finished_from_thread(self, success):
        if self.worker_thread.report:
            self.report = self.worker_thread.report
        else:
            self.output = self.results.link_loads
            self.report = self.worker_thread.report

            self.produce_all_outputs()
        self.exit_procedure()

    def run(self):
        if self.check_data():
            self.set_output_names()
            self.progressbar0.setVisible(True)
            self.worker_thread = TrafficAssignmentProcedure(
                qgis.utils.iface.mainWindow(), self.matrix, self.graph,
                self.results, self.method)
            self.run_thread()
        else:
            qgis.utils.iface.messageBar().pushMessage("Input error",
                                                      self.error,
                                                      level=3)

    def set_output_names(self):
        self.path_file.temp_file = os.path.join(self.temp_path, 'path_file')
        self.path_file.output_name = os.path.join(self.output_path,
                                                  'path_file')
        self.path_file.extension = 'aed'

        if self.do_path_file.isChecked():
            self.results.setSavePathFile(save=True,
                                         path_result=self.path_file.temp_file)

        self.link_extract.temp_file = os.path.join(self.temp_path,
                                                   'link_extract')
        self.link_extract.output_name = os.path.join(self.output_path,
                                                     'link_extract')
        self.link_extract.extension = 'aed'

        self.critical_output.temp_file = os.path.join(self.temp_path,
                                                      'critical_output')
        self.critical_output.output_name = os.path.join(
            self.output_path, 'critical_output')
        self.critical_output.extension = 'aed'

    def check_data(self):
        self.error = None

        if self.matrix is None:
            self.error = 'Demand matrix missing'

        if not self.graph.num_links:
            self.error = 'Graph was not loaded'

        if self.output_path is None:
            self.error = 'Parameters for output missing'

        if self.results.zones != np.max(self.matrix.shape[:]):
            self.error = 'Number of zones in the graph ({0}) does not match the number of ' \
                         'zones in your matrix ({1})'.format(self.results.zones, np.max(self.matrix.shape[:]))

        if self.error is not None:
            return False

        else:
            self.temp_path = os.path.join(self.output_path, 'temp')
            if not os.path.exists(self.temp_path):
                os.makedirs(self.temp_path)
            return True

    def load_assignment_queries(self):
        # First we load the assignment queries
        query_labels = []
        query_elements = []
        query_types = []
        if self.tot_crit_link_queries:
            for i in range(self.tot_crit_link_queries):
                links = eval(self.select_link_list.item(i, 0).text())
                query_type = self.select_link_list.item(i, 1).text()
                query_name = self.select_link_list.item(i, 2).text()

                for l in links:
                    d = directions_dictionary[l[1]]
                    lk = self.graph.ids[
                        (self.graph.graph['link_id'] == int(l[0]))
                        & (self.graph.graph['direction'] == d)]

                query_labels.append(query_name)
                query_elements.append(lk)
                query_types(query_type)

        self.critical_queries = {
            'labels': query_labels,
            'elements': query_elements,
            ' type': query_types
        }

    def progress_range_from_thread(self, val):
        self.progressbar0.setRange(0, val)

    def progress_value_from_thread(self, val):
        self.progressbar0.setValue(val)

    def progress_text_from_thread(self, val):
        self.progress_label0.setText(val)

    def produce_all_outputs(self):

        # Save link flows to disk
        if self.do_output_to_sqlite.isChecked():
            self.results.save_to_disk(output='loads',
                                      output_file_name=os.path.join(
                                          self.output_path, 'link_flows.db'),
                                      file_type='sqlite')
        else:
            self.results.save_to_disk(output='loads',
                                      output_file_name=os.path.join(
                                          self.output_path, 'link_flows.csv'),
                                      file_type='csv')

        # save Path file if that is the case
        if self.do_path_file.isChecked():
            if self.method['algorithm'] == 'AoN':
                if self.do_output_to_sqlite.isChecked():
                    self.results.save_to_disk(output='path_file',
                                              output_file_name=os.path.join(
                                                  self.output_path,
                                                  'path_file.db'),
                                              file_type='sqlite')
                else:
                    shutil.move(self.path_file.temp_file + '.aep',
                                self.path_file.output_name + '.aep')
                    shutil.move(self.path_file.temp_file + '.aed',
                                self.path_file.output_name + '.aed')

        # if self.do_select_link.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.critical_links['results'])
        #         self.results.critical_links = None
        #
        #         shutil.move(self.critical_output.temp_file + '.aep', self.critical_output.output_name)
        #         shutil.move(self.critical_output.temp_file + '.aed', self.critical_output.output_name[:-3] + 'aed')
        #
        # if self.do_extract_link_flows.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.link_extraction['results'])
        #         self.results.link_extraction = None
        #
        #         shutil.move(self.link_extract.temp_file + '.aep', self.link_extract.output_name)
        #         shutil.move(self.link_extract.temp_file + '.aed', self.link_extract.output_name[:-3] + 'aed')

        self.exit_procedure()

    def exit_procedure(self):
        self.close()
        if self.report:
            dlg2 = ReportDialog(self.iface, self.report)
            dlg2.show()
            dlg2.exec_()
Beispiel #2
0
class TrafficAssignmentDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface):
        QtWidgets.QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.path = standard_path()
        self.output_path = None
        self.temp_path = None
        self.error = None
        self.report = None
        self.method = {}
        self.matrices = OrderedDict()
        self.skims = []
        self.matrix = None
        self.graph = Graph()
        self.results = AssignmentResults()
        self.block_centroid_flows = None
        self.worker_thread = None

        # Signals for the matrix_procedures tab
        self.but_load_new_matrix.clicked.connect(self.find_matrices)

        # Signals from the Network tab
        self.load_graph_from_file.clicked.connect(self.load_graph)

        # Signals for the algorithm tab
        self.progressbar0.setVisible(False)
        self.progressbar0.setValue(0)
        self.progress_label0.setVisible(False)

        self.do_assignment.clicked.connect(self.run)
        self.cancel_all.clicked.connect(self.exit_procedure)
        self.select_output_folder.clicked.connect(self.choose_folder_for_outputs)

        self.cb_choose_algorithm.addItem('All-Or-Nothing')
        self.cb_choose_algorithm.currentIndexChanged.connect(self.changing_algorithm)

        # slots for skim tab
        self.but_build_query.clicked.connect(partial(self.build_query, 'select link'))

        self.changing_algorithm()

        # path file
        self.path_file = OutputType()

        # Queries
        tables = [self.select_link_list, self.list_link_extraction]
        for table in tables:
            table.setColumnWidth(0, 280)
            table.setColumnWidth(1, 40)
            table.setColumnWidth(2, 150)
            table.setColumnWidth(3, 40)

        self.graph_properties_table.setColumnWidth(0, 190)
        self.graph_properties_table.setColumnWidth(1, 240)

        # critical link
        self.but_build_query.clicked.connect(partial(self.build_query, 'select link'))
        self.do_select_link.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_crit_link_queries = 0
        self.critical_output = OutputType()

        # link flow extraction
        self.but_build_query_extract.clicked.connect(partial(self.build_query, 'Link flow extraction'))
        self.do_extract_link_flows.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_link_flow_extract = 0
        self.link_extract = OutputType()

        # Disabling resources not yet implemented
        self.do_select_link.setEnabled(False)
        self.but_build_query.setEnabled(False)
        self.select_link_list.setEnabled(False)
        self.skim_list_table.setEnabled(False)

        self.do_extract_link_flows.setEnabled(False)
        self.but_build_query_extract.setEnabled(False)
        self.list_link_extraction.setEnabled(False)
        self.new_matrix_to_assign()

        self.table_matrix_list.setColumnWidth(0, 135)
        self.table_matrix_list.setColumnWidth(1, 135)
        self.table_matrices_to_assign.setColumnWidth(0, 125)
        self.table_matrices_to_assign.setColumnWidth(1, 125)
        self.skim_list_table.setColumnWidth(0, 70)
        self.skim_list_table.setColumnWidth(1, 490)

    def choose_folder_for_outputs(self):
        new_name = GetOutputFolderName(self.path, 'Output folder for traffic assignment')
        if new_name:
            self.output_path = new_name
            self.lbl_output.setText(new_name)
        else:
            self.output_path = None
            self.lbl_output.setText(new_name)

    def load_graph(self):
        self.lbl_graphfile.setText('')

        file_types = ["AequilibraE graph(*.aeg)"]
        default_type = '.aeg'
        box_name = 'Traffic Assignment'
        graph_file, _ = GetOutputFileName(self, box_name, file_types, default_type, self.path)

        if graph_file is not None:
            self.graph.load_from_disk(graph_file)

            fields = list(set(self.graph.graph.dtype.names) - set(self.graph.required_default_fields))
            self.minimizing_field.addItems(fields)
            self.update_skim_list(fields)
            self.lbl_graphfile.setText(graph_file)

            cores = get_parameter_chain(['system', 'cpus'])
            self.results.set_cores(cores)

            # show graph properties
            def centers_item(qt_item):
                cell_widget = QWidget()
                lay_out = QHBoxLayout(cell_widget)
                lay_out.addWidget(qt_item)
                lay_out.setAlignment(Qt.AlignCenter)
                lay_out.setContentsMargins(0, 0, 0, 0)
                cell_widget.setLayout(lay_out)
                return cell_widget

            items = [['Graph ID', self.graph.__id__],
                     ['Number of links', self.graph.num_links],
                     ['Number of nodes', self.graph.num_nodes],
                     ['Number of centroids', self.graph.num_zones]]

            self.graph_properties_table.clearContents()
            self.graph_properties_table.setRowCount(5)
            for i, item in enumerate(items):
                self.graph_properties_table.setItem(i, 0, QTableWidgetItem(item[0]))
                self.graph_properties_table.setItem(i, 1, QTableWidgetItem(str(item[1])))

            self.graph_properties_table.setItem(4, 0, QTableWidgetItem('Block flows through centroids'))
            self.block_centroid_flows = QCheckBox()
            self.block_centroid_flows.setChecked(self.graph.block_centroid_flows)
            self.graph_properties_table.setCellWidget(4, 1, centers_item(self.block_centroid_flows))
        else:
            self.graph = Graph()
        self.set_behavior_special_analysis()

    def changing_algorithm(self):
        if self.cb_choose_algorithm.currentText() == 'All-Or-Nothing':
            self.method['algorithm'] = 'AoN'

    def run_thread(self):
        self.worker_thread.assignment.connect(self.signal_handler)
        # QObject.connect(self.worker_thread, SIGNAL("assignment"), self.signal_handler)
        self.worker_thread.start()
        self.exec_()

    def job_finished_from_thread(self):
        self.report = self.worker_thread.report
        self.produce_all_outputs()

        self.exit_procedure()

    def run(self):
        if self.check_data():
            self.set_output_names()
            self.progress_label0.setVisible(True)
            self.progressbar0.setVisible(True)
            self.progressbar0.setRange(0, self.graph.num_zones)
            try:
                if self.method['algorithm'] == 'AoN':
                    self.worker_thread = allOrNothing(self.matrix, self.graph, self.results)
                self.run_thread()
            except ValueError as error:
                qgis.utils.iface.messageBar().pushMessage("Input error", error.message, level=3)
        else:
            qgis.utils.iface.messageBar().pushMessage("Input error", self.error, level=3)

    def set_output_names(self):
        self.path_file.temp_file = os.path.join(self.temp_path, 'path_file.aed')
        self.path_file.output_name = os.path.join(self.output_path, 'path_file')
        self.path_file.extension = 'aed'

        if self.do_path_file.isChecked():
            self.results.setSavePathFile(save=True, path_result=self.path_file.temp_file)

        self.link_extract.temp_file = os.path.join(self.temp_path, 'link_extract')
        self.link_extract.output_name = os.path.join(self.output_path, 'link_extract')
        self.link_extract.extension = 'aed'

        self.critical_output.temp_file = os.path.join(self.temp_path, 'critical_output')
        self.critical_output.output_name = os.path.join(self.output_path, 'critical_output')
        self.critical_output.extension = 'aed'

    def check_data(self):
        self.error = None

        self.change_graph_settings()

        if not self.graph.num_links:
            self.error = 'Graph was not loaded'
            return False

        self.matrix = None
        if not self.prepare_assignable_matrices():
            return False

        if self.matrix is None:
            self.error = 'Demand matrix missing'
            return False

        if self.output_path is None:
            self.error = 'Parameters for output missing'
            return False

        self.temp_path = os.path.join(self.output_path, 'temp')
        if not os.path.exists(self.temp_path):
            os.makedirs(self.temp_path)

        self.results.prepare(self.graph, self.matrix)
        return True

    def load_assignment_queries(self):
        # First we load the assignment queries
        query_labels = []
        query_elements = []
        query_types = []
        if self.tot_crit_link_queries:
            for i in range(self.tot_crit_link_queries):
                links = eval(self.select_link_list.item(i, 0).text())
                query_type = self.select_link_list.item(i, 1).text()
                query_name = self.select_link_list.item(i, 2).text()

                for l in links:
                    d = directions_dictionary[l[1]]
                    lk = self.graph.ids[(self.graph.graph['link_id'] == int(l[0])) &
                                        (self.graph.graph['direction'] == d)]

                query_labels.append(query_name)
                query_elements.append(lk)
                query_types.append(query_type)

        self.critical_queries = {'labels': query_labels,
                                 'elements': query_elements,
                                 ' type': query_types}

    def signal_handler(self, val):
        if val[0] == 'zones finalized':
            self.progressbar0.setValue(val[1])
        elif val[0] == 'text AoN':
            self.progress_label0.setText(val[1])
        elif val[0] == 'finished_threaded_procedure':
            self.job_finished_from_thread()

    # TODO: Write code to export skims
    def produce_all_outputs(self):

        extension = 'aed'
        if not self.do_output_to_aequilibrae.isChecked():
            extension = 'csv'
            if self.do_output_to_sqlite.isChecked():
                extension = 'sqlite'

        # Save link flows to disk
        self.results.save_to_disk(os.path.join(self.output_path, 'link_flows.' + extension), output='loads')

        # save Path file if that is the case
        if self.do_path_file.isChecked():
            if self.method['algorithm'] == 'AoN':
                if self.do_output_to_sqlite.isChecked():
                    self.results.save_to_disk(file_name=os.path.join(self.output_path, 'path_file.' + extension),
                                              output='path_file')

        # Saves output skims
        if self.skim_list_table.rowCount() > 0:
            self.results.skims.copy(os.path.join(self.output_path, 'skims.aem'))

        # if self.do_select_link.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.critical_links['results'])
        #         self.results.critical_links = None
        #
        #         shutil.move(self.critical_output.temp_file + '.aep', self.critical_output.output_name)
        #         shutil.move(self.critical_output.temp_file + '.aed', self.critical_output.output_name[:-3] + 'aed')
        #
        # if self.do_extract_link_flows.isChecked():
        #     if self.method['algorithm'] == 'AoN':
        #         del(self.results.link_extraction['results'])
        #         self.results.link_extraction = None
        #
        #         shutil.move(self.link_extract.temp_file + '.aep', self.link_extract.output_name)
        #         shutil.move(self.link_extract.temp_file + '.aed', self.link_extract.output_name[:-3] + 'aed')

    # Procedures related to critical analysis. Not yet fully implemented
    def build_query(self, purpose):
        if purpose == 'select link':
            button = self.but_build_query
            message = 'Select Link Analysis'
            table = self.select_link_list
            counter = self.tot_crit_link_queries
        else:
            button = self.but_build_query_extract
            message = 'Link flow extraction'
            table = self.list_link_extraction
            counter = self.tot_link_flow_extract

        button.setEnabled(False)
        dlg2 = LoadSelectLinkQueryBuilderDialog(self.iface, self.graph.graph, message)
        dlg2.exec_()

        if dlg2.links is not None:
            table.setRowCount(counter + 1)
            text = ''
            for i in dlg2.links:
                text = text + ', (' + only_str(i[0]) + ', "' + only_str(i[1]) + '")'
            text = text[2:]
            table.setItem(counter, 0, QTableWidgetItem(text))
            table.setItem(counter, 1, QTableWidgetItem(dlg2.query_type))
            table.setItem(counter, 2, QTableWidgetItem(dlg2.query_name))
            del_button = QPushButton('X')
            del_button.clicked.connect(partial(self.click_button_inside_the_list, purpose))
            table.setCellWidget(counter, 3, del_button)
            counter += 1

        if purpose == 'select link':
            self.tot_crit_link_queries = counter

        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract = counter

        button.setEnabled(True)

    def click_button_inside_the_list(self, purpose):
        if purpose == 'select link':
            table = self.select_link_list
        else:
            table = self.list_link_extraction

        button = self.sender()
        index = self.select_link_list.indexAt(button.pos())
        row = index.row()
        table.removeRow(row)

        if purpose == 'select link':
            self.tot_crit_link_queries -= 1
        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract -= 1

    def set_behavior_special_analysis(self):
        if self.graph.num_links < 1:
            behavior = False
        else:
            behavior = True

        self.do_path_file.setEnabled(behavior)

        # This line of code turns off the features of select link analysis and link flow extraction while these
        # features are still being developed
        behavior = False

        self.do_select_link.setEnabled(behavior)
        self.do_extract_link_flows.setEnabled(behavior)

        self.but_build_query.setEnabled(behavior * self.do_select_link.isChecked())
        self.select_link_list.setEnabled(behavior * self.do_select_link.isChecked())

        self.list_link_extraction.setEnabled(behavior * self.do_extract_link_flows.isChecked())
        self.but_build_query_extract.setEnabled(behavior * self.do_extract_link_flows.isChecked())

    def update_skim_list(self, skims):
        self.skim_list_table.clearContents()
        self.skim_list_table.setRowCount(len(skims))

        for i, skm in enumerate(skims):
            self.skim_list_table.setItem(i, 1, QTableWidgetItem(skm))
            chb = QCheckBox()
            my_widget = QWidget()
            lay_out = QHBoxLayout(my_widget)
            lay_out.addWidget(chb)
            lay_out.setAlignment(Qt.AlignCenter)
            lay_out.setContentsMargins(0, 0, 0, 0)
            my_widget.setLayout(lay_out)

            self.skim_list_table.setCellWidget(i, 0, my_widget)

    # All Matrix loading and assignables selection
    def update_matrix_list(self):
        self.table_matrix_list.clearContents()
        self.table_matrix_list.clearContents()
        self.table_matrix_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table_matrix_list.setRowCount(len(self.matrices.keys()))

        for i, data_name in enumerate(self.matrices.keys()):
            self.table_matrix_list.setItem(i, 0, QTableWidgetItem(data_name))

            cbox = QComboBox()
            for idx in self.matrices[data_name].index_names:
                cbox.addItem(str(idx))
            self.table_matrix_list.setCellWidget(i, 1, cbox)

    def find_matrices(self):
        dlg2 = LoadMatrixDialog(self.iface)
        dlg2.show()
        dlg2.exec_()
        if dlg2.matrix is not None:
            matrix_name = dlg2.matrix.file_path
            matrix_name = os.path.splitext(os.path.basename(matrix_name))[0]
            matrix_name = self.find_non_conflicting_name(matrix_name, self.matrices)
            self.matrices[matrix_name] = dlg2.matrix
            self.update_matrix_list()

            row_count = self.table_matrices_to_assign.rowCount()
            new_matrix = list(self.matrices.keys())[-1]

            for i in range(row_count):
                cb = self.table_matrices_to_assign.cellWidget(i, 0)
                cb.insertItem(-1, new_matrix)

    def find_non_conflicting_name(self, data_name, dictio):
        if data_name in dictio:
            i = 1
            new_data_name = data_name + '_' + str(i)
            while new_data_name in dictio:
                i += 1
                new_data_name = data_name + '_' + str(i)
            data_name = new_data_name
        return data_name

    def changed_assignable_matrix(self, mi):
        chb = self.sender()
        mat_name = chb.currentText()

        table = self.table_matrices_to_assign
        for row in range(table.rowCount()):
            if table.cellWidget(row, 0) == chb:
                break

        if len(mat_name) == 0:
            if row + 1 < table.rowCount():
                self.table_matrices_to_assign.removeRow(row)
        else:
            mat_cores = self.matrices[mat_name].names
            cbox2 = QComboBox()
            cbox2.addItems(mat_cores)
            self.table_matrices_to_assign.setCellWidget(row, 1, cbox2)

            if row + 1 == table.rowCount():
                self.new_matrix_to_assign()

    def new_matrix_to_assign(self):
        # We edit ALL the combo boxes to have the current list of matrices
        row_count = self.table_matrices_to_assign.rowCount()
        self.table_matrices_to_assign.setRowCount(row_count + 1)

        cbox = QComboBox()
        cbox.addItems(list(self.matrices.keys()))
        cbox.addItem('')
        cbox.setCurrentIndex(cbox.count() - 1)
        cbox.currentIndexChanged.connect(self.changed_assignable_matrix)
        self.table_matrices_to_assign.setCellWidget(row_count, 0, cbox)

    def prepare_assignable_matrices(self):
        table = self.table_matrices_to_assign
        idx = self.graph.centroids
        mat_names = []
        if table.rowCount() > 1:
            for row in range(table.rowCount() - 1):
                mat = table.cellWidget(row, 0).currentText()
                core = table.cellWidget(row, 1).currentText()

                mat_index = self.matrices[mat].index
                if not np.array_equal(idx, mat_index):
                    no_zones = [item for item in mat_index if item not in idx]
                    # We only return an error if the matrix has too many centroids
                    if no_zones:
                        self.error = 'Assignable matrix has centroids that do not exist in the network: {}'.format(
                            ','.join([str(x) for x in no_zones]))
                        return False
                if core in mat_names:
                    self.error = 'Assignable matrices cannot have same names'
                    return False
                mat_names.append(only_str(core))

            self.matrix = AequilibraeMatrix()
            self.matrix.create_empty(file_name=self.matrix.random_name(),
                                     zones=idx.shape[0],
                                     matrix_names=mat_names)
            self.matrix.index[:] = idx[:]

            for row in range(table.rowCount() - 1):
                mat = table.cellWidget(row, 0).currentText()
                core = table.cellWidget(row, 1).currentText()
                src_mat = self.matrices[mat].matrix[core]
                dest_mat = self.matrix.matrix[core]

                rows = src_mat.shape[0]
                cols = src_mat.shape[1]
                dest_mat[:rows, :cols] = src_mat[:, :]

                # Inserts cols and rows that don;t exist
                if rows != self.matrix.zones:
                    src_index = list(self.matrices[mat].index[:])
                    for i, row in enumerate(idx):
                        if row not in src_index:
                            dest_mat[i + 1:, :] = dest_mat[i:-1, :]
                            dest_mat[i, :] = 0

                if cols != self.matrix.zones:
                    for j, col in enumerate(idx):
                        if col not in src_index:
                            dest_mat[:, j + 1:] = dest_mat[:, j:-1]
                            dest_mat[:, j] = 0

            self.matrix.computational_view()
        else:
            self.error = 'You need to have at least one matrix to assign'
            return False

        return True

    def change_graph_settings(self):
        skims = []
        table = self.skim_list_table
        for i in range(table.rowCount()):
            for chb in table.cellWidget(i, 0).findChildren(QCheckBox):
                if chb.isChecked():
                    skims.append(only_str(table.item(i, 1).text()))

        if len(skims) == 0:
            skims = False

        self.graph.set_graph(cost_field=self.minimizing_field.currentText(),
                             skim_fields=skims,
                             block_centroid_flows=self.block_centroid_flows.isChecked())

    def exit_procedure(self):
        self.close()
        if self.report:
            dlg2 = ReportDialog(self.iface, self.report)
            dlg2.show()
            dlg2.exec_()
class TrafficAssignmentDialog(QDialog, Ui_traffic_assignment):
    def __init__(self, iface):
        class OutputType:
            def __init__(self):
                self.temp_file = None
                self.extension = None
                self.output_name = None

        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.path = standard_path()
        self.temp_path = tempPath() + '/'

        self.error = None
        self.outname = None
        self.output = None
        self.report = None
        self.method = {}

        self.matrix = None
        self.graph = Graph()
        self.results = AssignmentResults()

        # Signals for the matrix tab
        self.but_load_new_matrix.clicked.connect(self.find_matrices)
        self.display_matrix.stateChanged.connect(self.display_matrix_or_not)

        # Signals from the Network tab
        self.load_graph_from_file.clicked.connect(self.load_graph)
        self.network_layer.setVisible(False)
        self.network_field.setVisible(False)
        self.lblnodematch_11.setVisible(False)
        self.lblnodematch_14.setVisible(False)
        self.chb_check_consistency.setVisible(False)
        self.network_layer.setFilters(QgsMapLayerProxyModel.LineLayer)
        self.network_layer.layerChanged.connect(self.add_fields_to_cboxes)
        self.add_fields_to_cboxes()

        # Signals for the algorithm tab
        self.progressbar0.setVisible(False)
        self.progressbar0.setValue(0)
        self.progress_label0.setVisible(False)

        self.do_assignment.clicked.connect(self.run)
        self.cancel_all.clicked.connect(self.exit_procedure)
        self.select_result.clicked.connect(self.browse_outfile)

        self.cb_choose_algorithm.addItem('All-Or-Nothing')
        self.cb_choose_algorithm.currentIndexChanged.connect(self.changing_algorithm)

        # slots for skim tab
        self.add_skim.clicked.connect(self.select_skim)

        self.changing_algorithm()

        # critical analysis and path file saving
        self.group_outputs = False
        self.do_group_outputs.setEnabled(False)

        # path file
        self.do_path_file.stateChanged.connect(self.change_status_for_path_file)
        self.select_path_file_name.clicked.connect(self.choose_output_for_path_file)
        self.do_path_file.setEnabled(False)
        self.path_file = OutputType
        self.change_status_for_path_file()

        # Queries
        tables = [self.select_link_list, self.list_link_extraction]
        for table in tables:
            table.setColumnWidth(0, 280)
            table.setColumnWidth(1, 40)
            table.setColumnWidth(2, 150)
            table.setColumnWidth(3, 40)

        #critical link
        self.but_build_query.clicked.connect(partial(self.build_query, 'select link'))
        self.do_select_link.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_crit_link_queries = 0
        self.critical_output = OutputType

        # link flow extraction
        self.but_build_query_extract.clicked.connect(partial(self.build_query, 'Link flow extraction'))
        self.do_extract_link_flows.stateChanged.connect(self.set_behavior_special_analysis)
        self.tot_link_flow_extract = 0
        self.link_extract = OutputType

    def build_query(self, purpose):
        if purpose == 'select link':
            button = self.but_build_query
            message = 'Select Link Analysis'
            table = self.select_link_list
            counter = self.tot_crit_link_queries

        if purpose == 'Link flow extraction':
            button = self.but_build_query_extract
            message = 'Link flow extraction'
            table = self.list_link_extraction
            counter = self.tot_link_flow_extract

        button.setEnabled(False)
        dlg2 = LoadSelectLinkQueryBuilder(self.iface, self.graph.graph, message)
        dlg2.exec_()

        if dlg2.links is not None:
            table.setRowCount(counter + 1)
            text = ''
            for i in dlg2.links:
                text = text + ', (' + i[0].encode('utf-8') + ', "' + i[1].encode('utf-8') + '")'
            text = text[2:]
            table.setItem(counter, 0, QTableWidgetItem(text))
            table.setItem(counter, 1, QTableWidgetItem(dlg2.query_type))
            table.setItem(counter, 2, QTableWidgetItem(dlg2.query_name))
            del_button = QPushButton('X')
            del_button.clicked.connect(partial(self.click_button_inside_the_list, purpose))
            table.setCellWidget(counter, 3, del_button)
            counter += 1

        if purpose == 'select link':
            self.tot_crit_link_queries = counter

        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract = counter

        button.setEnabled(True)

    def click_button_inside_the_list(self, purpose):
        if purpose == 'select link':
            table = self.select_link_list
        elif purpose == 'Link flow extraction':
            table = self.list_link_extraction

        button = self.sender()
        index = self.select_link_list.indexAt(button.pos())
        row = index.row()
        table.removeRow(row)

        if purpose == 'select link':
            self.tot_crit_link_queries -= 1
        elif purpose == 'Link flow extraction':
            self.tot_link_flow_extract -= 1

    def choose_output_for_path_file(self):
        new_name, file_type = GetOutputFileName(self, 'Path File', ["AequilibraE Path File(*.aep)"], ".aep", self.path)

        if new_name:
            self.path_file.extension = file_type
            self.path_file.output_name = new_name
            self.path_file_display.setText(new_name)
            self.path_file.temp_file = self.temp_path + uuid.uuid4().hex
            self.results.setSavePathFile(True, self.path_file.temp_file + '.aep')
        else:
            self.path_file.output_name = None
            self.path_file_display.setText('...')

    def choose_output_for_critical_link(self):
        new_name, type = GetOutputFileName(self, 'Select Link analysis', ["Select Link Analysis Matrix(*.aes)",
                                                 "NumPy Array(*.npy)", "SQLite(*.sqlite)"], ".aes", self.path)

        if new_name:
            self.critical_link_output_file = new_name
            self.critical_matrix_path.setText(new_name)
            self.critical_link_temp_file = self.temp_path + uuid.uuid4().hex

            self.results.setCriticalLinks(True, self.critical_queries, self.critical_link_temp_file + '.aes')
        else:
            self.critical_link_output_file = None
            self.critical_matrix_path.setText('...')

    def change_status_for_path_file(self):
        if self.do_path_file.isChecked():
            self.select_path_file_name.setEnabled(True)
            self.path_file_display.setVisible(True)
        else:
            self.select_path_file_name.setEnabled(False)
            self.path_file_display.setVisible(False)
            self.path_file.output_name = None
            self.path_file_display.setText('...')

    def select_skim(self):
        pass

    def load_graph(self):
        self.lbl_graphfile.setText('')

        file_types = ["AequilibraE graph(*.aeg)"]
        default_type = '.aeg'
        box_name = 'Traffic Assignment'
        graph_file, type = GetOutputFileName(self, box_name, file_types, default_type, self.path)

        if graph_file is not None:
            self.graph.load_from_disk(graph_file)

            not_considering_list = self.graph.required_default_fields
            not_considering_list.pop(-1)
            not_considering_list.append('id')

            for i in list(self.graph.graph.dtype.names):
                if i not in not_considering_list:
                    self.minimizing_field.addItem(i)
            self.lbl_graphfile.setText(graph_file)
            self.results.prepare(self.graph)
            cores = get_parameter_chain(['system', 'cpus'])
            self.results.set_cores(cores)
        else:
            self.graph = Graph()
        self.change_status_for_path_file()
        self.set_behavior_special_analysis()

    def browse_outfile(self):
        file_types = ["Comma-separated files(*.csv)", "Numpy Binnary Array(*.npy)"]
        default_type = '.csv'
        box_name = 'Result Matrix'
        new_name, type = GetOutputFileName(self, box_name, file_types, default_type, self.path)

        if new_name:
            self.outname = new_name
            self.lbl_output.setText(self.outname)
        else:
            self.outname = None
            self.lbl_output.setText('')

    def set_behavior_special_analysis(self):
        if self.graph.num_links < 1:
            behavior = False
        else:
            behavior = True

        self.do_path_file.setEnabled(behavior)

        # This line of code turns off the features of select link analysis and link flow extraction while these
        #features are still being developed
        behavior = False

        self.do_select_link.setEnabled(behavior)
        self.do_extract_link_flows.setEnabled(behavior)

        self.but_build_query.setEnabled(behavior * self.do_select_link.isChecked())
        self.select_link_list.setEnabled(behavior * self.do_select_link.isChecked())

        self.list_link_extraction.setEnabled(behavior * self.do_extract_link_flows.isChecked())
        self.but_build_query_extract.setEnabled(behavior * self.do_extract_link_flows.isChecked())

    def add_fields_to_cboxes(self):
        l = get_vector_layer_by_name(self.network_layer.currentText())
        if l is not None:
            self.layer = l
            self.network_field.clear()
            for field in self.layer.dataProvider().fields().toList():
                if field.type() in integer_types:
                    self.network_field.addItem(field.name())

    def changing_algorithm(self):
        pass
        if self.cb_choose_algorithm.currentText() == 'All-Or-Nothing':
            self.method['algorithm'] = 'AoN'

    def run_thread(self):
        QObject.connect(self.worker_thread, SIGNAL("ProgressValue( PyQt_PyObject )"), self.progress_value_from_thread)
        QObject.connect(self.worker_thread, SIGNAL("ProgressText( PyQt_PyObject )"), self.progress_text_from_thread)
        QObject.connect(self.worker_thread, SIGNAL("ProgressMaxValue( PyQt_PyObject )"), self.progress_range_from_thread)
        QObject.connect(self.worker_thread, SIGNAL("finished_threaded_procedure( PyQt_PyObject )"),
                        self.job_finished_from_thread)
        self.worker_thread.start()
        self.exec_()

    def find_matrices(self):
        dlg2 = LoadMatrixDialog(self.iface)
        dlg2.show()
        dlg2.exec_()
        if dlg2.matrix is not None:
            self.matrix = dlg2.matrix
            self.display_matrix_or_not()
        else:
            self.matrix = None

    def display_matrix_or_not(self):
        if self.display_matrix.isChecked() and self.matrix is not None:
            row_headers = []
            col_headers = []
            for i in range(self.matrix.shape[0]):
                row_headers.append(str(i))

            for j in range(self.matrix.shape[1]):
                col_headers.append(str(j))

            m = NumpyModel(self.matrix, col_headers, row_headers)
            self.matrix_viewer.setModel(m)
        else:
            self.matrix_viewer.clearSpans()

    def job_finished_from_thread(self, success):
        if self.worker_thread.error is not None:
            qgis.utils.iface.messageBar().pushMessage("Procedure error: ", self.worker_thread.error, level=3)
        else:
            self.output = self.results.results()
            self.report = self.worker_thread.report

            if self.outname[-3:].upper() == 'NPY':
                np.save(self.outname, self.output)

            else:
                self.produce_all_outputs()

    def run(self):
        if self.check_data():
            self.progressbar0.setVisible(True)
            self.worker_thread = TrafficAssignmentProcedure(qgis.utils.iface.mainWindow(), self.matrix, self.graph,
                                                       self.results, self.method)
            self.run_thread()
        else:
            qgis.utils.iface.messageBar().pushMessage("Input error", self.error, level=3)

    def check_data(self):
        self.error = None

        if self.matrix is None:
            self.error = 'Demand matrix missing'

        if not self.graph.num_links:
            self.error = 'Graph was not loaded'

        # if not len(self.network_layer.currentText()):
        #     self.error = 'No line layer selected'

        # if not len(self.network_field.currentText()):
        #     self.error = 'No link ID field selected'

        if self.outname is None:
            self.error = 'Parameters for output missing'

        if self.do_path_file.isChecked() and self.path_file.output_name is None:
            self.error = 'No output file name for the path file selected'

        if self.error is not None:
            return False
        else:
            return True

    def load_assignment_queries(self):
        # First we load the assignment queries
        query_labels=[]
        query_elements = []
        query_types=[]
        if self.tot_crit_link_queries:
            for i in range(self.tot_crit_link_queries):
                links = eval(self.select_link_list.item(i, 0).text())
                query_type = self.select_link_list.item(i, 1).text()
                query_name = self.select_link_list.item(i, 2).text()

                for l in links:
                    d = directions_dictionary[l[1]]
                    lk = self.graph.ids[(self.graph.graph['link_id'] == int(l[0])) & (self.graph.graph['direction'] == d)]

                query_labels.append(query_name)
                query_elements.append(lk)
                query_types(query_type)

        self.critical_queries = {'labels': query_labels,
                                 'elements': query_elements,
                                 ' type': query_types}

    def progress_range_from_thread(self, val):
        self.progressbar0.setRange(0, val)

    def progress_value_from_thread(self, val):
        self.progressbar0.setValue(val)

    def progress_text_from_thread(self, val):
        self.progress_label0.setText(val)

    def produce_all_outputs(self):

        # Save link flows to disk
        self.results.save_loads_to_disk(self.outname)

        # Path file
        if self.do_path_file.isChecked():
            if self.method['algorithm'] == 'AoN':
                del(self.results.path_file['results'])
                self.results.path_file = None

                shutil.move(self.path_file.temp_file + '.aep', self.path_file.output_name)
                shutil.move(self.path_file.temp_file + '.aed', self.path_file.output_name[:-3] + 'aed')

        # select link analysis
        if self.do_path_file.isChecked():
            if self.method['algorithm'] == 'AoN':
                del(self.results.critical_links['results'])
                self.results.critical_links = None

                shutil.move(self.critical_output.temp_file + '.aep', self.critical_output.output_name)
                shutil.move(self.critical_output.temp_file + '.aed', self.critical_output.output_name[:-3] + 'aed')

        if self.do_select_link.isChecked():
            if self.method['algorithm'] == 'AoN':
                del(self.results.critical_links['results'])
                self.results.critical_links = None

                shutil.move(self.critical_output.temp_file + '.aep', self.critical_output.output_name)
                shutil.move(self.critical_output.temp_file + '.aed', self.critical_output.output_name[:-3] + 'aed')

        if self.do_extract_link_flows.isChecked():
            if self.method['algorithm'] == 'AoN':
                del(self.results.link_extraction['results'])
                self.results.link_extraction = None

                shutil.move(self.link_extract.temp_file + '.aep', self.link_extract.output_name)
                shutil.move(self.link_extract.temp_file + '.aed', self.link_extract.output_name[:-3] + 'aed')

        self.exit_procedure()

    def exit_procedure(self):
        self.close()
        if len(self.report) > 0:
            dlg2 = ReportDialog(self.iface, self.report)
            dlg2.show()
            dlg2.exec_()