Example #1
0
    def test_save(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        a.computational_view(['distance'])
        new_mat = np.random.rand(a.zones, a.zones)
        a.matrix_view *= new_mat

        res = a.matrix_view.sum()

        a.save('new_name_for_matrix')
        self.assertEqual(res, a.matrix_view.sum(), 'Saved wrong result')

        a.save(['new_name_for_matrix2'])
        self.assertEqual(a.view_names[0], 'new_name_for_matrix2', 'Did not update computational view')
        self.assertEqual(len(a.view_names), 1, 'computational view with the wrong number of matrices')

        a.computational_view(['distance', 'new_name_for_matrix'])

        with self.assertRaises(ValueError):
            a.save(['just_one_name'])

        a.save(['one_name', 'two_names'])

        with self.assertRaises(ValueError):
            a.save('distance')

        b = AequilibraeMatrix()
        b.load(self.name_test)
        b.computational_view("seed")
        b.save()
        b.computational_view(["mat", "seed", "dist"])
        b.save()
Example #2
0
    def test_save(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        a.computational_view(["distance"])
        new_mat = np.random.rand(a.zones, a.zones)
        a.matrix_view *= new_mat

        res = a.matrix_view.sum()

        a.save("new_name_for_matrix")
        self.assertEqual(res, a.matrix_view.sum(), "Saved wrong result")

        a.save(["new_name_for_matrix2"])
        self.assertEqual(a.view_names[0], "new_name_for_matrix2", "Did not update computational view")
        self.assertEqual(len(a.view_names), 1, "computational view with the wrong number of matrices")

        a.computational_view(["distance", "new_name_for_matrix"])

        with self.assertRaises(ValueError):
            a.save(["just_one_name"])

        a.save(["one_name", "two_names"])

        with self.assertRaises(ValueError):
            a.save("distance")

        b = AequilibraeMatrix()
        b.load(self.name_test)
        b.computational_view("seed")
        b.save()
        b.computational_view(["mat", "seed", "dist"])
        b.save()
    def test_skimming_on_assignment(self):
        matrix = AequilibraeMatrix()
        matrix.load(os.path.join(gettempdir(), self.mat_name))
        matrix.computational_view(["cars"])

        res = AssignmentResults()

        res.prepare(self.g, matrix)

        self.g.set_skimming([])
        self.g.set_blocked_centroid_flows(True)
        assig = allOrNothing(matrix, self.g, res)
        assig.execute()

        if res.skims.distance.sum() > 0:
            self.fail(
                "skimming for nothing during assignment returned something different than zero"
            )

        self.g.set_skimming("distance")
        res.prepare(self.g, matrix)

        assig = allOrNothing(matrix, self.g, res)
        assig.execute()
        if res.skims.distance.sum() != 2914644.0:
            self.fail("skimming during assignment returned the wrong value")
        matrix.close()
Example #4
0
    def new_record(self, name: str, file_name: str,
                   matrix=AequilibraeMatrix()) -> MatrixRecord:
        """Creates a new record for a matrix in disk, but does not save it

        If the matrix file is not already on disk, it will fail

        Args:
            *name* (:obj:`str`): Name of the matrix
            *file_name* (:obj:`str`): Name of the file on disk

        Return:
            *matrix_record* (:obj:`MatrixRecord`): A matrix record that can be manipulated in memory before saving
        """

        if name in self.__items:
            raise ValueError(
                f"There is already a matrix of name ({name}). It must be unique."
            )

        for mat in self.__items.values():
            if mat.file_name == file_name:
                raise ValueError(
                    f"There is already a matrix record for file name ({file_name}). It must be unique."
                )

        if matrix.cores > 0:
            if isfile(join(self.fldr, file_name)):
                raise FileExistsError(
                    f"{file_name} already exists. Choose a different name or matrix format"
                )

            mat_format = file_name.split(".")[-1].lower()
            if mat_format not in ["omx", "aem"]:
                raise ValueError(
                    "Matrix needs to be either OMX or native AequilibraE")

            matrix.export(join(self.fldr, file_name))
            cores = matrix.cores
        else:
            if not isfile(join(self.fldr, file_name)):
                raise FileExistsError(
                    f"{file_name} does not exist. Cannot create this matrix record"
                )
            mat = AequilibraeMatrix()
            mat.load(join(self.fldr, file_name))
            cores = mat.cores
            mat.close()
            del mat

        tp = {key: None for key in self.__fields}
        tp["name"] = name
        tp["file_name"] = file_name
        tp["cores"] = cores
        mr = MatrixRecord(tp)
        mr.save()
        self.__items[name.lower()] = mr
        logger.warning("Matrix Record has been saved to the database")
        return mr
    def test_execute(self):
        # Loads and prepares the graph

        car_loads = []
        two_class_loads = []

        for extension in ["omx", "aem"]:
            matrix = AequilibraeMatrix()
            if extension == 'omx':
                mat_name = os.path.join(gettempdir(), "my_matrix." + extension)
            else:
                mat_name = self.mat_name
            matrix.load(mat_name)

            matrix.computational_view(["cars"])

            # Performs assignment
            res = AssignmentResults()
            res.prepare(self.g, matrix)

            assig = allOrNothing(matrix, self.g, res)
            assig.execute()
            car_loads.append(res.link_loads)
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_{}.aed".format(extension)))
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_{}.csv".format(extension)))

            matrix.computational_view()
            # Performs assignment
            res = AssignmentResults()
            res.prepare(self.g, matrix)

            assig = allOrNothing(matrix, self.g, res)
            assig.execute()
            two_class_loads.append(res.link_loads)
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_2_classes_{}.aed".format(extension)))
            res.save_to_disk(
                os.path.join(gettempdir(),
                             "link_loads_2_classes_{}.csv".format(extension)))
            matrix.close()

        load_diff = two_class_loads[0] - two_class_loads[1]
        if load_diff.max() > 0.0000000001 or load_diff.max() < -0.0000000001:
            self.fail(
                "Loads for two classes differ for OMX and AEM matrix types")

        load_diff = car_loads[0] - car_loads[1]
        if load_diff.max() > 0.0000000001 or load_diff.max() < -0.0000000001:
            self.fail(
                "Loads for a single class differ for OMX and AEM matrix types")
    def test_get_matrix(self):
        self.test_load()
        a = AequilibraeMatrix()
        a.load(siouxfalls_skims)

        with self.assertRaises(AttributeError):
            a.get_matrix('does not exist')

        q = a.get_matrix('distance')
        self.assertEqual(q.shape[0], 24)

        a = AequilibraeMatrix()
        a.load(name_test)
        print(np.array_equal(a.get_matrix("seed"), a.matrix["seed"]))
Example #7
0
    def test_get_matrix(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        with self.assertRaises(AttributeError):
            a.get_matrix("does not exist")

        q = a.get_matrix("distance")
        self.assertEqual(q.shape[0], 24)

        a = AequilibraeMatrix()
        a.load(self.name_test)
        print(np.array_equal(a.get_matrix("seed"), a.matrix["seed"]))

        del a
Example #8
0
    def test_calibrate_with_omx(self):
        imped = AequilibraeMatrix()
        imped.load(siouxfalls_skims)
        imped.computational_view(['free_flow_time'])

        mat = AequilibraeMatrix()
        mat.load(siouxfalls_demand)
        mat.computational_view()

        args = {"impedance": imped, "matrix": mat, "function": "power", "nan_to_zero": False}

        distributed_matrix = GravityCalibration(**args)
        distributed_matrix.calibrate()
        if distributed_matrix.gap > 0.0001:
            self.fail("Calibration did not converge")

        args = {"impedance": imped, "matrix": mat, "function": "power", "nan_to_zero": True}

        distributed_matrix = GravityCalibration(**args)
        distributed_matrix.calibrate()
        if distributed_matrix.gap > 0.0001:
            self.fail("Calibration did not converge")
Example #9
0
    def update_database(self) -> None:
        """Adds records to the matrices database for matrix files found on disk"""
        existing_files = os.listdir(self.fldr)
        paths_for_existing = [mat.file_name for mat in self.__items.values()]

        new_files = [x for x in existing_files if x not in paths_for_existing]
        new_files = [
            x for x in new_files
            if os.path.splitext(x.lower())[1] in [".omx", ".aem"]
        ]

        if new_files:
            logger.warning(
                f'New matrix found on disk. Added to the database: {",".join(new_files)}'
            )

        for fl in new_files:
            mat = AequilibraeMatrix()
            mat.load(join(self.fldr, fl))

            name = None
            if not mat.is_omx():
                name = str(mat.name).lower()

            if not name:
                name = fl.lower()

            name = name.replace(".", "_").replace(" ", "_")

            if name in self.__items:
                i = 0
                while f"{name}_{i}" in self.__items:
                    i += 1
                name = f"{name}_{i}"
            rec = self.new_record(name, fl)
            rec.save()
Example #10
0
class TestAequilibraeMatrix(TestCase):
    matrix = None

    def setUp(self) -> None:
        self.sf_skims = f"/Aequilibrae_matrix_{uuid.uuid4()}.omx"
        copyfile(siouxfalls_skims, self.sf_skims)
        temp_folder = gettempdir()
        self.name_test = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.aem"
        self.copy_matrix_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.aem"
        self.csv_export_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.csv"
        self.omx_export_name = temp_folder + f"/Aequilibrae_matrix_{uuid.uuid4()}.omx"

        if self.matrix is not None:
            return
        args = {
            "file_name": self.name_test,
            "zones": zones,
            "matrix_names": ["mat", "seed", "dist"],
            "index_names": ["my indices"],
        }

        self.matrix = AequilibraeMatrix()
        self.matrix.create_empty(**args)

        self.matrix.index[:] = np.arange(self.matrix.zones) + 100
        self.matrix.mat[:, :] = np.random.rand(self.matrix.zones, self.matrix.zones)[:, :]
        self.matrix.mat[:, :] = self.matrix.mat[:, :] * (1000 / np.sum(self.matrix.mat[:, :]))
        self.matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        self.matrix.setDescription("Generated at " + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        self.new_matrix = self.matrix

    def tearDown(self) -> None:
        try:
            del self.matrix
            os.remove(self.name_test) if os.path.exists(self.name_test) else None
            os.remove(self.csv_export_name) if os.path.exists(self.csv_export_name) else None
            os.remove(self.copy_matrix_name) if os.path.exists(self.copy_matrix_name) else None
            os.remove(self.omx_export_name) if os.path.exists(self.omx_export_name) else None
        except Exception as e:
            print(f"Could not delete.  {e.args}")

    def test_load(self):
        self.new_matrix = AequilibraeMatrix()
        # Cannot load OMX file with no indices
        with self.assertRaises(LookupError):
            self.new_matrix.load(no_index_omx)

        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(self.name_test)
        del self.new_matrix

    def test_computational_view(self):
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.mat.fill(0)
        self.new_matrix.seed.fill(0)
        if self.new_matrix.matrix_view.shape[2] != 2:
            self.fail("Computational view returns the wrong number of matrices")

        self.new_matrix.computational_view(["mat"])
        self.new_matrix.matrix_view[:, :] = np.arange(zones ** 2).reshape(zones, zones)
        if np.sum(self.new_matrix.mat) != np.sum(self.new_matrix.matrix_view):
            self.fail("Assigning to matrix view did not work")
        self.new_matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        self.new_matrix.setDescription("Generated at " + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        del self.new_matrix

    def test_computational_view_with_omx(self):
        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(omx_example)

        arrays = ["m1", "m2"]
        self.new_matrix.computational_view(arrays)
        total_mats = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.computational_view([arrays[0]])
        total_m1 = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.close()

        omx_file = omx.open_file(omx_example, "r")

        m1 = np.array(omx_file["m1"]).sum()
        m2 = np.array(omx_file["m2"]).sum()

        self.assertEqual(m1 + m2, total_mats)
        self.assertEqual(m1, total_m1)

        omx_file.close()
        del omx_file

    def test_copy(self):
        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(self.copy_matrix_name, cores=["mat"])

        if not np.array_equal(matrix_copy.mat, self.new_matrix.mat):
            self.fail("Matrix copy was not perfect")
        matrix_copy.close()
        del matrix_copy

    def test_export_to_csv(self):
        self.new_matrix.export(self.csv_export_name)
        df = pd.read_csv(self.csv_export_name)
        df.fillna(0, inplace=True)
        self.assertEqual(df.shape[0], 2500, "Exported wrong size")
        self.assertEqual(df.shape[1], 5, "Exported wrong size")
        self.assertAlmostEqual(df.mat.sum(), np.nansum(self.new_matrix.matrices), 5, "Exported wrong matrix total")

    def test_export_to_omx(self):
        self.new_matrix.export(self.omx_export_name)

        omxfile = omx.open_file(self.omx_export_name, "r")

        # Check if matrices values are compatible
        for m in self.new_matrix.names:
            sm = np.nansum(self.new_matrix.matrix[m])
            sm2 = np.nansum(np.array(omxfile[m]))

            self.assertEqual(sm, sm2, "Matrix {} was exported with the wrong value".format(m))
        del omxfile

    def test_nan_to_num(self):
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.nan_to_num()
        self.new_matrix.mat[1, 1] = np.nan
        self.new_matrix.computational_view(["mat"])
        self.new_matrix.nan_to_num()

        if abs(m - self.new_matrix.mat.sum()) > 0.000000000001:
            self.fail("Total for mat matrix not maintained")
        del self.new_matrix

    def test_copy_from_omx(self):
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()
        a.create_from_omx(temp_file, omx_example)

        omxfile = omx.open_file(omx_example, "r")

        # Check if matrices values are compatible
        for m in ["m1", "m2", "m3"]:
            sm = a.matrix[m].sum()
            sm2 = np.array(omxfile[m]).sum()
            if sm != sm2:
                self.fail("Matrix {} was copied with the wrong value".format(m))

        if np.any(a.index[:] != np.array(list(omxfile.mapping("taz").keys()))):
            self.fail("Index was not created properly")
        a.close()
        del a
        del omxfile

    def test_copy_from_omx_long_name(self):

        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, robust=False)
        del a

    def test_copy_omx_wrong_content(self):
        # Check if we get a result if we try to copy non-existing cores
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, cores=["m1", "m2", "m3", "m4"])

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, mappings=["wrong index"])
        del a

    def test_get_matrix(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        with self.assertRaises(AttributeError):
            a.get_matrix("does not exist")

        q = a.get_matrix("distance")
        self.assertEqual(q.shape[0], 24)

        a = AequilibraeMatrix()
        a.load(self.name_test)
        print(np.array_equal(a.get_matrix("seed"), a.matrix["seed"]))

        del a

    def test_save(self):
        a = AequilibraeMatrix()
        a.load(self.sf_skims)

        a.computational_view(["distance"])
        new_mat = np.random.rand(a.zones, a.zones)
        a.matrix_view *= new_mat

        res = a.matrix_view.sum()

        a.save("new_name_for_matrix")
        self.assertEqual(res, a.matrix_view.sum(), "Saved wrong result")

        a.save(["new_name_for_matrix2"])
        self.assertEqual(a.view_names[0], "new_name_for_matrix2", "Did not update computational view")
        self.assertEqual(len(a.view_names), 1, "computational view with the wrong number of matrices")

        a.computational_view(["distance", "new_name_for_matrix"])

        with self.assertRaises(ValueError):
            a.save(["just_one_name"])

        a.save(["one_name", "two_names"])

        with self.assertRaises(ValueError):
            a.save("distance")

        b = AequilibraeMatrix()
        b.load(self.name_test)
        b.computational_view("seed")
        b.save()
        b.computational_view(["mat", "seed", "dist"])
        b.save()
Example #11
0
class LoadMatrixDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface, **kwargs):
        QtWidgets.QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.path = standard_path()
        self.sparse = kwargs.get('sparse', False)
        self.multiple = kwargs.get('multiple', True)
        self.allow_single_use = kwargs.get('single_use', False)
        self.output_name = None
        self.layer = None
        self.orig = None
        self.dest = None
        self.cells = None
        self.matrix_count = 0
        self.matrices = {}
        self.matrix = None
        self.error = None
        self.__current_name = None
        self.logger = aequilibrae.logger
        self.radio_layer_matrix.clicked.connect(self.change_matrix_type)
        self.radio_npy_matrix.clicked.connect(self.change_matrix_type)
        self.radio_aeq_matrix.clicked.connect(self.change_matrix_type)
        self.radio_omx_matrix.clicked.connect(self.change_matrix_type)

        # For changing the network layer
        self.matrix_layer.currentIndexChanged.connect(self.load_fields_to_combo_boxes)

        # Buttons
        self.but_load.clicked.connect(self.load_the_matrix)

        if self.allow_single_use:
            self.but_save_for_single_use.clicked.connect(self.prepare_final_matrix)
        else:
            self.but_save_for_single_use.setVisible(False)

        self.but_permanent_save.clicked.connect(self.get_name_and_save_to_disk)

        # THIRD, we load layers in the canvas to the combo-boxes
        for layer in all_layers_from_toc():  # We iterate through all layers
            if 'wkbType' in dir(layer):
                if layer.wkbType() == 100:
                    self.matrix_layer.addItem(layer.name())
        if no_omx:
            self.radio_omx_matrix.setEnabled(False)

        self.resizing()

    def resizing(self):
        if self.radio_aeq_matrix.isChecked():
            self.group_combo.setVisible(False)
            self.group_list.setVisible(False)
            self.group_buttons.setVisible(False)
            self.setMaximumSize(127, 176)
            self.resize(127, 176)
            self.but_permanent_save.setVisible(False)
        else:
            self.group_combo.setVisible(True)
            self.group_list.setVisible(True)
            self.group_buttons.setVisible(True)
            self.matrix_list_view.setColumnWidth(0, 180)
            self.matrix_list_view.setColumnWidth(1, 100)
            self.matrix_list_view.setColumnWidth(2, 125)
            self.matrix_list_view.itemChanged.connect(self.change_matrix_name)
            self.matrix_list_view.doubleClicked.connect(self.slot_double_clicked)
            self.setMaximumSize(QtCore.QSize(100000, 100000))
            self.resize(542, 427)
            self.but_permanent_save.setVisible(True)

        self.but_save_for_single_use.setEnabled(False)
        self.but_permanent_save.setEnabled(False)

    def slot_double_clicked(self, mi):
        row = mi.row()
        if row > -1:
            self.matrix_count -= 1
            mat_to_remove = self.matrix_list_view.item(row, 0).text()
            self.matrices.pop(mat_to_remove, None)
            self.update_matrix_list()

    def change_matrix_type(self):
        self.but_load.setEnabled(True)
        members = [self.lbl_matrix, self.lbl_from, self.matrix_layer, self.field_from]
        all_members = members + [self.lbl_to, self.lbl_flow, self.field_to, self.field_cells]

        # Covers the Numpy option (minimizes the code length this way)
        for member in all_members:
            member.setVisible(False)

        if self.radio_layer_matrix.isChecked():
            self.lbl_matrix.setText('Matrix')
            self.lbl_from.setText('From')
            for member in all_members:
                member.setVisible(True)
            self.load_fields_to_combo_boxes()

        if self.radio_omx_matrix.isChecked():
            self.lbl_matrix.setText('Matrix core')
            self.lbl_from.setText('Indices')
            for member in members:
                member.setVisible(True)

        self.resizing()

    def load_fields_to_combo_boxes(self):
        self.but_load.setEnabled(False)
        for combo in [self.field_from, self.field_to, self.field_cells]:
            combo.clear()

        if self.matrix_layer.currentIndex() >= 0:
            self.but_load.setEnabled(True)
            self.layer = get_vector_layer_by_name(self.matrix_layer.currentText())
            for field in self.layer.dataProvider().fields().toList():
                if field.type() in integer_types:
                    self.field_from.addItem(field.name())
                    self.field_to.addItem(field.name())
                    self.field_cells.addItem(field.name())
                if field.type() in float_types:
                    self.field_cells.addItem(field.name())

    def run_thread(self):
        self.worker_thread.ProgressValue.connect(self.progress_value_from_thread)
        self.worker_thread.ProgressMaxValue.connect(self.progress_range_from_thread)
        self.worker_thread.ProgressText.connect(self.progress_text_from_thread)
        self.worker_thread.finished_threaded_procedure.connect(self.finished_threaded_procedure)

        self.but_load.setEnabled(False)
        self.worker_thread.start()
        self.exec_()

    # VAL and VALUE have the following structure: (bar/text ID, value)
    def progress_range_from_thread(self, val):
        self.progressbar.setRange(0, val)

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

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

    def finished_threaded_procedure(self, param):
        self.but_load.setEnabled(True)
        if self.worker_thread.report:
            dlg2 = ReportDialog(self.iface, self.worker_thread.report)
            dlg2.show()
            dlg2.exec_()
        else:
            if param == 'LOADED-MATRIX':
                self.compressed.setVisible(True)
                self.progress_label.setVisible(False)

                if self.__current_name in self.matrices.keys():
                    i = 1
                    while self.__current_name + '_' + str(i) in self.matrices.keys():
                        i += 1
                    self.__current_name = self.__current_name + '_' + str(i)

                self.matrices[self.__current_name] = self.worker_thread.matrix
                self.matrix_count += 1
                self.update_matrix_list()

                if self.multiple == False:
                    self.update_matrix_hashes()

            elif param == 'REBLOCKED MATRICES':
                self.matrix = self.worker_thread.matrix
                if self.compressed.isChecked():
                    pass
                    # compression not implemented yet
                self.exit_procedure()

    def load_the_matrix(self):
        self.error = None
        self.worker_thread = None
        if self.radio_layer_matrix.isChecked():
            if self.field_from.currentIndex() < 0 or self.field_from.currentIndex() < 0 or self.field_cells.currentIndex() < 0:
                self.error = 'Invalid field chosen'

            if self.error is None:
                self.compressed.setVisible(False)
                self.progress_label.setVisible(True)
                self.__current_name = self.field_cells.currentText().lower().replace(' ', '_')
                idx1 = self.layer.dataProvider().fieldNameIndex(self.field_from.currentText())
                idx2 = self.layer.dataProvider().fieldNameIndex(self.field_to.currentText())
                idx3 = self.layer.dataProvider().fieldNameIndex(self.field_cells.currentText())
                idx = [idx1, idx2, idx3]

                self.worker_thread = LoadMatrix(qgis.utils.iface.mainWindow(), type='layer', layer=self.layer, idx=idx,
                                                sparse=self.sparse)

        if self.radio_npy_matrix.isChecked():
            file_types = ["NumPY array(*.npy)"]
            default_type = '.npy'
            box_name = 'Matrix Loader'
            new_name, type = GetOutputFileName(self, box_name, file_types, default_type, self.path)
            self.__current_name = os.path.split(new_name)[1].split('.')[0]

            self.worker_thread = LoadMatrix(qgis.utils.iface.mainWindow(), type='numpy', file_path=new_name)

        if self.radio_aeq_matrix.isChecked():
            file_types = ["AequilibraE Matrix(*.aem)"]
            default_type = '.aem'
            box_name = 'AequilibraE Matrix'
            new_name, type = GetOutputFileName(self, box_name, file_types, default_type, self.path)
            if new_name is not None:
                self.matrix = AequilibraeMatrix()
                self.matrix.load(new_name)
                self.exit_procedure()

        if self.radio_omx_matrix.isChecked():
            pass
            # Still not implemented

        if self.worker_thread is not None:
            self.run_thread()

        if self.error is not None:
            qgis.utils.iface.messageBar().pushMessage("Error:", self.error, level=1)

    def update_matrix_list(self):
        if self.matrix_count > 0:
            self.but_save_for_single_use.setEnabled(True)
            self.but_permanent_save.setEnabled(True)
        else:
            self.but_save_for_single_use.setEnabled(False)
            self.but_permanent_save.setEnabled(False)

        self.matrix_list_view.clearContents()
        self.matrix_list_view.setRowCount(self.matrix_count)

        self.matrix_list_view.blockSignals(True)
        i = 0
        for key, value in self.matrices.items():
            r = np.unique(value['from']).shape[0]
            c = np.unique(value['to']).shape[0]
            dimensions = "{:,}".format(r) + " x " + "{:,}".format(c)
            total = "{:,.2f}".format(float(value['flow'].sum()))
            item_1 = QTableWidgetItem(key)
            self.matrix_list_view.setItem(i, 0, item_1)

            item_2 = QTableWidgetItem(dimensions)
            item_2.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.matrix_list_view.setItem(i, 1, item_2)

            item_3 = QTableWidgetItem(total)
            item_3.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.matrix_list_view.setItem(i, 2, item_3)

            i += 1
        self.matrix_list_view.blockSignals(False)

    def change_matrix_name(self, item):
        self.matrix_list_view.blockSignals(True)
        row = item.row()
        new_name = self.matrix_list_view.item(row, 0).text().lower().replace(' ', '_')
        item_1 = QTableWidgetItem(new_name)
        self.matrix_list_view.setItem(row, 0, item_1)

        current_names = []
        for i in range(self.matrix_count):
            current_names.append(self.matrix_list_view.item(i, 0).text())

        for old_key in self.matrices.keys():
            if old_key not in current_names:
                self.matrices[new_name] = self.matrices.pop(old_key)
        self.matrix_list_view.blockSignals(False)

    def get_name_and_save_to_disk(self):
        self.output_name, _ = GetOutputFileName(self, 'AequilibraE matrix', ["Aequilibrae Matrix(*.aem)"], '.aem',
                                                self.path)
        self.prepare_final_matrix()

    def prepare_final_matrix(self):
        self.compressed.setVisible(False)
        self.progress_label.setVisible(True)

        if self.output_name is None:
            self.worker_thread = MatrixReblocking(qgis.utils.iface.mainWindow(), sparse=self.sparse,
                                                  matrices=self.matrices)
        else:
            self.worker_thread = MatrixReblocking(qgis.utils.iface.mainWindow(), sparse=self.sparse,
                                                  matrices=self.matrices, file_name=self.output_name)
        self.run_thread()

    def exit_procedure(self):
        self.close()
Example #12
0
class TestAequilibraeMatrix(TestCase):
    def test___init__(self):
        os.remove(name_test) if os.path.exists(name_test) else None
        args = {'file_name': name_test,
                'zones': zones,
                'matrix_names': ['mat', 'seed', 'dist'],
                'index_names': ['my indices']}

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = np.arange(matrix.zones) + 100
        matrix.mat[:, :] = np.random.rand(matrix.zones, matrix.zones)[:, :]
        matrix.mat[:, :] = matrix.mat[:, :] * (1000 / np.sum(matrix.mat[:, :]))
        matrix.setName('Test matrix - ' + str(random.randint(1, 10)))
        matrix.setDescription('Generated at ' + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        matrix.close(True)
        del (matrix)

    def test_load(self):
        # self.test___init__()
        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(name_test)

    def test_computational_view(self):
        self.test_load()
        self.new_matrix.computational_view(['mat', 'seed'])
        self.new_matrix.mat.fill(0)
        self.new_matrix.seed.fill(0)
        if self.new_matrix.matrix_view.shape[2] != 2:
            self.fail('Computational view returns the wrong number of matrices')

        self.new_matrix.computational_view(['mat'])
        self.new_matrix.matrix_view[:, :] = np.arange(zones ** 2).reshape(zones, zones)
        if np.sum(self.new_matrix.mat) != np.sum(self.new_matrix.matrix_view):
            self.fail('Assigning to matrix view did not work')
        self.new_matrix.setName('Test matrix - ' + str(random.randint(1, 10)))
        self.new_matrix.setDescription('Generated at ' + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        self.new_matrix.close(True)

    def test_copy(self):
        self.test_load()

        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(copy_matrix_name, cores=['mat'])

        if not np.array_equal(matrix_copy.mat, self.new_matrix.mat):
            self.fail('Matrix copy was not perfect')
        matrix_copy.close(True)
        self.new_matrix.close(True)

    def test_export(self):
        self.test_load()
        self.new_matrix.export(csv_export_name)
        self.new_matrix.close(True)

    def test_nan_to_num(self):
        self.test_load()
        s = self.new_matrix.seed.sum() - self.new_matrix.seed[1, 1]
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.seed[1,1] = np.nan
        self.new_matrix.computational_view(['mat', 'seed'])
        self.new_matrix.nan_to_num()
        self.new_matrix.mat[1,1] = np.nan
        self.new_matrix.computational_view(['mat'])
        self.new_matrix.nan_to_num()

        if s != self.new_matrix.seed.sum():
            self.fail('Total for seed matrix not maintained')

        if m != self.new_matrix.mat.sum():
            self.fail('Total for mat matrix not maintained')
class TestAequilibraeMatrix(TestCase):
    def test___init__(self):
        os.remove(name_test) if os.path.exists(name_test) else None
        args = {
            "file_name": name_test,
            "zones": zones,
            "matrix_names": ["mat", "seed", "dist"],
            "index_names": ["my indices"],
        }

        matrix = AequilibraeMatrix()
        matrix.create_empty(**args)

        matrix.index[:] = np.arange(matrix.zones) + 100
        matrix.mat[:, :] = np.random.rand(matrix.zones, matrix.zones)[:, :]
        matrix.mat[:, :] = matrix.mat[:, :] * (1000 / np.sum(matrix.mat[:, :]))
        matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        matrix.setDescription(
            "Generated at " +
            datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        matrix.close()
        del matrix

    def test_load(self):
        # self.test___init__()
        self.new_matrix = AequilibraeMatrix()
        # Cannot load OMX file with no indices
        with self.assertRaises(LookupError):
            self.new_matrix.load(no_index_omx)

        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(name_test)

    def test_computational_view(self):
        self.test_load()
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.mat.fill(0)
        self.new_matrix.seed.fill(0)
        if self.new_matrix.matrix_view.shape[2] != 2:
            self.fail(
                "Computational view returns the wrong number of matrices")

        self.new_matrix.computational_view(["mat"])
        self.new_matrix.matrix_view[:, :] = np.arange(zones**2).reshape(
            zones, zones)
        if np.sum(self.new_matrix.mat) != np.sum(self.new_matrix.matrix_view):
            self.fail("Assigning to matrix view did not work")
        self.new_matrix.setName("Test matrix - " + str(random.randint(1, 10)))
        self.new_matrix.setDescription(
            "Generated at " +
            datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"))
        self.new_matrix.close()

    def test_computational_view_with_omx(self):
        self.new_matrix = AequilibraeMatrix()
        self.new_matrix.load(omx_example)

        arrays = ["m1", "m2"]
        self.new_matrix.computational_view(arrays)
        total_mats = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.computational_view([arrays[0]])
        total_m1 = np.sum(self.new_matrix.matrix_view)

        self.new_matrix.close()

        omx_file = omx.open_file(omx_example, "r")

        m1 = np.array(omx_file["m1"]).sum()
        m2 = np.array(omx_file["m2"]).sum()

        self.assertEqual(m1 + m2, total_mats)
        self.assertEqual(m1, total_m1)

        omx_file.close()

    def test_copy(self):
        self.test_load()

        # test in-memory matrix_procedures copy

        matrix_copy = self.new_matrix.copy(copy_matrix_name, cores=["mat"])

        if not np.array_equal(matrix_copy.mat, self.new_matrix.mat):
            self.fail("Matrix copy was not perfect")
        matrix_copy.close()
        self.new_matrix.close()

    def test_export_to_csv(self):
        self.test_load()
        self.new_matrix.export(csv_export_name)
        self.new_matrix.close()

    def test_export_to_omx(self):
        self.test_load()
        self.new_matrix.export(omx_export_name)

        omxfile = omx.open_file(omx_export_name, "r")

        # Check if matrices values are compatible
        for m in self.new_matrix.names:
            sm = np.nansum(self.new_matrix.matrix[m])
            sm2 = np.nansum(np.array(omxfile[m]))

            self.assertEqual(
                sm, sm2,
                "Matrix {} was exported with the wrong value".format(m))

        self.new_matrix.close()

    def test_nan_to_num(self):
        self.test_load()
        s = self.new_matrix.seed.sum() - self.new_matrix.seed[1, 1]
        m = self.new_matrix.mat.sum() - self.new_matrix.mat[1, 1]
        self.new_matrix.seed[1, 1] = np.nan
        self.new_matrix.computational_view(["mat", "seed"])
        self.new_matrix.nan_to_num()
        self.new_matrix.mat[1, 1] = np.nan
        self.new_matrix.computational_view(["mat"])
        self.new_matrix.nan_to_num()

        if s != self.new_matrix.seed.sum():
            self.fail("Total for seed matrix not maintained")

        if m != self.new_matrix.mat.sum():
            self.fail("Total for mat matrix not maintained")

    def test_copy_from_omx(self):
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()
        a.create_from_omx(temp_file, omx_example)

        omxfile = omx.open_file(omx_example, "r")

        # Check if matrices values are compatible
        for m in ["m1", "m2", "m3"]:
            sm = a.matrix[m].sum()
            sm2 = np.array(omxfile[m]).sum()
            if sm != sm2:
                self.fail(
                    "Matrix {} was copied with the wrong value".format(m))

        if np.any(a.index[:] != np.array(list(omxfile.mapping("taz").keys()))):
            self.fail("Index was not created properly")
        a.close()

    def test_copy_from_omx_long_name(self):

        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, robust=False)

    def test_copy_omx_wrong_content(self):
        # Check if we get a result if we try to copy non-existing cores
        temp_file = AequilibraeMatrix().random_name()
        a = AequilibraeMatrix()

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file,
                              omx_example,
                              cores=["m1", "m2", "m3", "m4"])

        with self.assertRaises(ValueError):
            a.create_from_omx(temp_file, omx_example, mappings=["wrong index"])
class DisplayAequilibraEFormatsDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface):
        QtWidgets.QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)

        self.error = None

        self.error = None
        self.data_path, self.data_type = GetOutputFileName(
            self, 'AequilibraE custom formats',
            ["Aequilibrae dataset(*.aed)", "Aequilibrae matrix(*.aem)"],
            '.aed', standard_path())

        if self.data_type is None:
            self.error = 'Path provided is not a valid dataset'
            self.exit_with_error()

        self.data_type = self.data_type.upper()

        if self.data_type == 'AED':
            self.data_to_show = AequilibraEData()
        elif self.data_type == 'AEM':
            self.data_to_show = AequilibraeMatrix()

        try:
            self.data_to_show.load(self.data_path)
        except:
            self.error = 'Could not load dataset'
            self.exit_with_error()

        # Elements that will be used during the displaying
        self._layout = QVBoxLayout()
        self.table = QTableView()
        self._layout.addWidget(self.table)

        # Settings for displaying
        self.show_layout = QHBoxLayout()

        # Thousand separator
        self.thousand_separator = QCheckBox()
        self.thousand_separator.setChecked(True)
        self.thousand_separator.setText('Thousands separator')
        self.thousand_separator.toggled.connect(self.format_showing)
        self.show_layout.addWidget(self.thousand_separator)

        self.spacer = QSpacerItem(5, 0, QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Minimum)
        self.show_layout.addItem(self.spacer)

        # Decimals
        txt = QLabel()
        txt.setText('Decimal places')
        self.show_layout.addWidget(txt)
        self.decimals = QSpinBox()
        self.decimals.valueChanged.connect(self.format_showing)
        self.decimals.setMinimum(0)
        self.decimals.setValue(4)
        self.decimals.setMaximum(10)

        self.show_layout.addWidget(self.decimals)
        self._layout.addItem(self.show_layout)

        # differentiates between matrix and dataset
        if self.data_type == 'AEM':
            self.data_to_show.computational_view([self.data_to_show.names[0]])
            # Matrices need cores and indices to be set as well
            self.mat_layout = QHBoxLayout()
            self.mat_list = QComboBox()
            for n in self.data_to_show.names:
                self.mat_list.addItem(n)

            self.mat_list.currentIndexChanged.connect(self.change_matrix_cores)
            self.mat_layout.addWidget(self.mat_list)

            self.idx_list = QComboBox()
            for i in self.data_to_show.index_names:
                self.idx_list.addItem(i)
            self.idx_list.currentIndexChanged.connect(self.change_matrix_cores)
            self.mat_layout.addWidget(self.idx_list)
            self._layout.addItem(self.mat_layout)
            self.change_matrix_cores()

        self.but_export = QPushButton()
        self.but_export.setText('Export')
        self.but_export.clicked.connect(self.export)

        self.but_close = QPushButton()
        self.but_close.clicked.connect(self.exit_procedure)
        self.but_close.setText('Close')

        self.but_layout = QHBoxLayout()
        self.but_layout.addWidget(self.but_export)
        self.but_layout.addWidget(self.but_close)

        self._layout.addItem(self.but_layout)

        # We chose to use QTableView. However, if we want to allow the user to edit the dataset
        # The we need to allow them to switch to the slower QTableWidget
        # Code below

        # self.table = QTableWidget(self.data_to_show.entries, self.data_to_show.num_fields)
        # self.table.setHorizontalHeaderLabels(self.data_to_show.fields)
        # self.table.setObjectName('data_viewer')
        #
        # self.table.setVerticalHeaderLabels([str(x) for x in self.data_to_show.index[:]])
        # self.table.clearContents()
        #
        # for i in range(self.data_to_show.entries):
        #     for j, f in enumerate(self.data_to_show.fields):
        #         item1 = QTableWidgetItem(str(self.data_to_show.data[f][i]))
        #         item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        #         self.table.setItem(i, j, item1)

        self.resize(700, 500)
        self.setLayout(self._layout)
        self.format_showing()

    def format_showing(self):
        decimals = self.decimals.value()
        separator = self.thousand_separator.isChecked()
        if isinstance(self.data_to_show, AequilibraeMatrix):
            m = NumpyModel(self.data_to_show, separator, decimals)
        else:
            m = DatabaseModel(self.data_to_show, separator, decimals)
        self.table.clearSpans()
        self.table.setModel(m)

    def change_matrix_cores(self):
        self.data_to_show.computational_view([self.mat_list.currentText()])
        self.data_to_show.set_index(self.data_to_show.index_names[0])
        self.format_showing()

    def export(self):
        new_name, file_type = GetOutputFileName(
            self, self.data_type, ["Comma-separated file(*.csv)"], ".csv",
            self.data_path)
        if new_name is not None:
            self.data_to_show.export(new_name)

    def exit_with_error(self):
        qgis.utils.iface.messageBar().pushMessage("Error:",
                                                  self.error,
                                                  level=1)
        self.exit_procedure()

    def exit_procedure(self):
        self.close()
Example #15
0
class TestTrafficAssignment(TestCase):
    def setUp(self) -> None:
        self.matrix = AequilibraeMatrix()
        self.matrix.load(siouxfalls_demand)
        self.matrix.computational_view()

        self.project = Project()
        self.project.load(siouxfalls_project)
        self.project.network.build_graphs()
        self.car_graph = self.project.network.graphs['c']  # type: Graph
        self.car_graph.set_graph('free_flow_time')
        self.car_graph.set_blocked_centroid_flows(False)

        self.assignment = TrafficAssignment()
        self.assigclass = TrafficClass(self.car_graph, self.matrix)

    def tearDown(self) -> None:
        self.matrix.close()
        self.project.conn.close()

    def test_set_vdf(self):
        with self.assertRaises(ValueError):
            self.assignment.set_vdf('CQS')

        self.assignment.set_vdf('BPR')

    def test_set_classes(self):
        with self.assertRaises(ValueError):
            self.assignment.set_classes([1, 2])

        # The traffic assignment class is unprotected.
        # Should we protect it?
        # self.assigclass = TrafficClass(self.car_graph, self.matrix)
        # self.assigclass.graph = 1
        # with self.assertRaises(ValueError):
        #     self.assignment.set_classes(self.assigclass)

        self.assignment.set_classes(self.assigclass)
        # self.fail()

    def test_algorithms_available(self):
        algs = self.assignment.algorithms_available()
        real = ['all-or-nothing', 'msa', 'frank-wolfe', 'bfw', 'cfw']

        diff = [x for x in real if x not in algs]
        diff2 = [x for x in algs if x not in real]

        if len(diff) + len(diff2) > 0:
            self.fail('list of algorithms raised is wrong')

    def test_set_cores(self):
        with self.assertRaises(Exception):
            self.assignment.set_cores(3)

        self.assignment.set_classes(self.assigclass)
        with self.assertRaises(ValueError):
            self.assignment.set_cores('q')

        self.assignment.set_cores(3)

    def test_set_algorithm(self):
        with self.assertRaises(AttributeError):
            self.assignment.set_algorithm('not an algo')

        self.assignment.set_classes(self.assigclass)

        with self.assertRaises(Exception):
            self.assignment.set_algorithm('msa')

        self.assignment.set_vdf("BPR")
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_capacity_field("capacity")
        self.assignment.set_time_field("free_flow_time")

        self.assignment.max_iter = 10
        self.assignment.set_algorithm('bfw')

    def test_set_vdf_parameters(self):
        with self.assertRaises(Exception):
            self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_vdf('bpr')
        self.assignment.set_classes(self.assigclass)
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

    def test_set_time_field(self):
        N = random.randint(1, 50)
        val = ''.join(
            random.choices(string.ascii_uppercase + string.digits, k=N))
        self.assignment.set_time_field(val)
        self.assertEqual(self.assignment.time_field, val)

    def test_set_capacity_field(self):
        N = random.randint(1, 50)
        val = ''.join(
            random.choices(string.ascii_uppercase + string.digits, k=N))
        self.assignment.set_capacity_field(val)
        self.assertEqual(self.assignment.capacity_field, val)

    def test_execute(self):

        self.assignment.set_classes(self.assigclass)
        self.assignment.set_vdf("BPR")
        self.assignment.set_vdf_parameters({"alpha": 0.15, "beta": 4.0})
        self.assignment.set_vdf_parameters({"alpha": "b", "beta": "power"})

        self.assignment.set_capacity_field("capacity")
        self.assignment.set_time_field("free_flow_time")

        self.assignment.max_iter = 10
        self.assignment.set_algorithm('msa')
        self.assignment.execute()
        msa10 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.8, correl)

        self.assignment.max_iter = 30
        self.assignment.set_algorithm('msa')
        self.assignment.execute()
        msa25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.95, correl)

        self.assignment.set_algorithm('frank-wolfe')
        self.assignment.execute()
        fw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.97, correl)

        self.assignment.set_algorithm('cfw')
        self.assignment.execute()
        cfw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.98, correl)

        self.assignment.set_algorithm('bfw')
        self.assignment.execute()
        bfw25 = self.assignment.assignment.rgap

        self.assigclass.results.total_flows()
        correl = np.corrcoef(self.assigclass.results.total_link_loads,
                             self.assigclass.graph.graph['volume'])[0, 1]
        self.assertLess(0.99, correl)

        self.assertLess(msa25, msa10)
        self.assertLess(fw25, msa25)
        self.assertLess(cfw25, fw25)
        self.assertLess(bfw25, cfw25)