def test_skimming_single_origin(self): origin = 1 # graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field="distance", skim_fields=None) # g.block_centroid_flows = False # None implies that only the cost field will be skimmed # skimming results res = SkimResults() res.prepare(g) aux_result = MultiThreadedNetworkSkimming() aux_result.prepare(g, res) a = skimming_single_origin(origin, g, res, aux_result, 0) tot = np.sum(res.skims.distance[origin, :]) if tot > 10e10: self.fail( "Skimming was not successful. At least one np.inf returned.") if a != origin: self.fail("Skimming returned an error: " + a)
def test_network_skimming(self): # graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field="distance", skim_fields=None) # None implies that only the cost field will be skimmed # skimming results res = SkimResults() res.prepare(g) aux_res = MultiThreadedNetworkSkimming() aux_res.prepare(g, res) _ = skimming_single_origin(26, g, res, aux_res, 0) skm = NetworkSkimming(g, res) skm.execute() tot = np.nanmax(res.skims.distance[:, :]) if tot > 10e10: self.fail( "Skimming was not successful. At least one np.inf returned.") if skm.report: self.fail("Skimming returned an error:" + str(skm.report))
def test_skimming_single_origin(self): g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field="distance") g.set_skimming("distance") origin = np.random.choice(g.centroids[:-1], 1)[0] # skimming results res = SkimResults() res.prepare(g) aux_result = MultiThreadedNetworkSkimming() aux_result.prepare(g, res) a = skimming_single_origin(origin, g, res, aux_result, 0) tot = np.sum(res.skims.distance[origin, :]) if tot > 10e10: self.fail( "Skimming was not successful. At least one np.inf returned for origin {}." .format(origin)) if a != origin: self.fail("Skimming returned an error: {} for origin {}".format( a, origin))
def test_network_skimming(self): # graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field='distance', skim_fields=None) # None implies that only the cost field will be skimmed # skimming results res = SkimResults() res.prepare(g) aux_res = MultiThreadedNetworkSkimming() aux_res.prepare(g, res) a = skimming_single_origin(26, g, res, aux_res, 0) skm = NetworkSkimming(g, res) skm.execute() tot = np.nanmax(res.skims.distance[:, :]) if tot > 10e10: self.fail('Skimming was not successful. At least one np.inf returned.') if skm.report: self.fail('Skimming returned an error:' + str(skm.report))
def test_set_pce(self): mat_name = AequilibraeMatrix().random_name() g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field="distance") # Creates the matrix for assignment args = { "file_name": os.path.join(gettempdir(), mat_name), "zones": g.num_zones, "matrix_names": ["cars", "trucks"], "index_names": ["my indices"], } matrix = AequilibraeMatrix() matrix.create_empty(**args) matrix.index[:] = g.centroids[:] matrix.cars.fill(1.1) matrix.trucks.fill(2.2) matrix.computational_view() tc = TrafficClass(graph=g, matrix=matrix) self.assertIsInstance(tc.results, AssignmentResults, 'Results have the wrong type') self.assertIsInstance(tc._aon_results, AssignmentResults, 'Results have the wrong type') with self.assertRaises(ValueError): tc.set_pce('not a number') tc.set_pce(1) tc.set_pce(3.9)
def test_execute(self): # Loads and prepares the graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field='distance', skim_fields=None) # None implies that only the cost field will be skimmed # Prepares the matrix for assignment args = { 'file_name': '/tmp/my_matrix.aem', 'zones': g.num_zones, 'matrix_names': ['cars', 'trucks'], 'index_names': ['my indices'] } matrix = AequilibraeMatrix() matrix.create_empty(**args) matrix.index[:] = g.centroids[:] matrix.cars.fill(1) matrix.trucks.fill(2) matrix.computational_view(['cars']) # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk('/tmp/link_loads.aed') res.save_to_disk('/tmp/link_loads.csv') matrix.computational_view() # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk('/tmp/link_loads_2_classes.aed') res.save_to_disk('/tmp/link_loads_2_classes.csv')
def test_execute(self): # Loads and prepares the graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field="distance", skim_fields=None) # None implies that only the cost field will be skimmed # Prepares the matrix for assignment args = { "file_name": os.path.join(gettempdir(), "my_matrix.aem"), "zones": g.num_zones, "matrix_names": ["cars", "trucks"], "index_names": ["my indices"], } matrix = AequilibraeMatrix() matrix.create_empty(**args) matrix.index[:] = g.centroids[:] matrix.cars.fill(1) matrix.trucks.fill(2) matrix.computational_view(["cars"]) # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk(os.path.join(gettempdir(), "link_loads.aed")) res.save_to_disk(os.path.join(gettempdir(), "link_loads.csv")) matrix.computational_view() # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk(os.path.join(gettempdir(), "link_loads_2_classes.aed")) res.save_to_disk(os.path.join(gettempdir(), "link_loads_2_classes.csv"))
def test_execute(self): # Loads and prepares the graph g = Graph() g.load_from_disk(test_graph) g.set_graph(cost_field='distance', skim_fields=None) # None implies that only the cost field will be skimmed # Prepares the matrix for assignment args = {'file_name': os.path.join(gettempdir(),'my_matrix.aem'), 'zones': g.num_zones, 'matrix_names': ['cars', 'trucks'], 'index_names': ['my indices']} matrix = AequilibraeMatrix() matrix.create_empty(**args) matrix.index[:] = g.centroids[:] matrix.cars.fill(1) matrix.trucks.fill(2) matrix.computational_view(['cars']) # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk(os.path.join(gettempdir(),'link_loads.aed')) res.save_to_disk(os.path.join(gettempdir(),'link_loads.csv')) matrix.computational_view() # Performs assignment res = AssignmentResults() res.prepare(g, matrix) assig = allOrNothing(matrix, g, res) assig.execute() res.save_to_disk(os.path.join(gettempdir(),'link_loads_2_classes.aed')) res.save_to_disk(os.path.join(gettempdir(),'link_loads_2_classes.csv'))
class TestPathResults(TestCase): def test_prepare(self): # graph self.g = Graph() self.g.load_from_disk(test_graph) self.g.set_graph(cost_field="distance") self.r = PathResults() try: self.r.prepare(self.g) except Exception as err: self.fail("Path result preparation failed - {}".format( err.__str__())) def test_reset(self): self.test_prepare() try: self.r.reset() except Exception as err: self.fail("Path result resetting failed - {}".format( err.__str__())) def test_update_trace(self): self.test_prepare() try: self.r.reset() except Exception as err: self.fail("Path result resetting failed - {}".format( err.__str__())) path_computation(origin, dest, self.g, self.r) if list(self.r.path) != [53, 52, 13]: self.fail("Path computation failed. Wrong sequence of links") if list(self.r.path_nodes) != [5, 168, 166, 27]: self.fail("Path computation failed. Wrong sequence of path nodes") if list(self.r.milepost) != [0, 341, 1398, 2162]: self.fail("Path computation failed. Wrong milepost results")
class TestPathResults(TestCase): def test_prepare(self): # graph self.g = Graph() self.g.load_from_disk(test_graph) self.g.set_graph(cost_field='distance', skim_fields=None) self.r = PathResults() try: self.r.prepare(self.g) except: self.fail('Path result preparation failed') def test_reset(self): self.test_prepare() try: self.r.reset() except: self.fail('Path result resetting failed') def test_update_trace(self): self.test_prepare() try: self.r.reset() except: self.fail('Path result resetting failed') path_computation(origin, dest, self.g, self.r) if list(self.r.path) != [53, 52, 13]: self.fail('Path computation failed. Wrong sequence of links') if list(self.r.path_nodes) != [5, 168, 166, 27]: self.fail('Path computation failed. Wrong sequence of path nodes') if list(self.r.milepost) != [0, 341, 1398, 2162]: self.fail('Path computation failed. Wrong milepost results')
class DesireLinesProcedure(WorkerThread): def __init__(self, parentThread, layer, id_field, matrix, matrix_hash, dl_type): WorkerThread.__init__(self, parentThread) self.layer = layer self.id_field = id_field self.matrix = matrix self.dl_type = dl_type self.error = None self.matrix_hash = matrix_hash self.report = [] self.nodes_to_indices = {matrix.index[x]: x for x in range(matrix.zones)} self.python_version = (8 * struct.calcsize("P")) if error: self.error = 'Scipy and/or Numpy not installed' self.report.append(self.error) self.procedure = "ASSIGNMENT" def doWork(self): if self.error is None: # In case we have only one class unnasigned = 0 classes = self.matrix.matrix_view.shape[2] layer = get_vector_layer_by_name(self.layer) idx = layer.fieldNameIndex(self.id_field) feature_count = layer.featureCount() self.emit(SIGNAL("desire_lines"), ('job_size_dl', feature_count)) all_centroids = {} P = 0 for feat in layer.getFeatures(): P += 1 self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', P)) self.emit(SIGNAL("desire_lines"), ('text_dl',"Loading Layer Features: " + str(P) + "/" + str(feature_count))) geom = feat.geometry() if geom is not None: point = list(geom.centroid().asPoint()) centroid_id = feat.attributes()[idx] all_centroids[centroid_id] = point #Creating resulting layer EPSG_code = int(layer.crs().authid().split(":")[1]) desireline_layer = QgsVectorLayer("LineString?crs=epsg:" + str(EPSG_code), self.dl_type, "memory") dlpr = desireline_layer.dataProvider() fields = [QgsField("link_id", QVariant.Int), QgsField("A_Node", QVariant.Int), QgsField("B_Node", QVariant.Int), QgsField("direct", QVariant.Int), QgsField("distance", QVariant.Double)] for f in self.matrix.view_names: fields.extend([QgsField(f + '_ab', QVariant.Double), QgsField(f + '_ba', QVariant.Double), QgsField(f + '_tot', QVariant.Double)]) dlpr.addAttributes(fields) desireline_layer.updateFields() if self.dl_type == "DesireLines": self.emit(SIGNAL("desire_lines"), ('text_dl',"Creating Desire Lines")) self.emit(SIGNAL("desire_lines"), ('job_size_dl', self.matrix.zones ** 2 / 2)) desireline_link_id = 1 q = 0 all_features = [] for i in range(self.matrix.zones): a_node = self.matrix.index[i] if a_node in all_centroids.keys(): if np.sum(self.matrix.matrix_view[i, :, :]) + np.sum(self.matrix.matrix_view[:, i, :]) > 0: columns_with_filled_cells = np.nonzero(np.sum(self.matrix.matrix_view[i, :, :], axis=1)) for j in columns_with_filled_cells[0]: if np.sum(self.matrix.matrix_view[i, j, :]) + np.sum(self.matrix.matrix_view[j, i, :]) > 0: b_node = self.matrix.index[j] if a_node in all_centroids.keys() and b_node in all_centroids.keys(): a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) dist = QgsGeometry().fromPoint(a_point).distance(QgsGeometry().fromPoint(b_point)) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolyline([a_point, b_point])) attrs = [desireline_link_id, int(a_node), int(b_node), 0, dist] for c in range(classes): attrs.extend([float(self.matrix.matrix_view[i, j, c]), float(self.matrix.matrix_view[j, i, c]), float(self.matrix.matrix_view[i, j, c]) + float(self.matrix.matrix_view[j, i, c])]) feature.setAttributes(attrs) all_features.append(feature) desireline_link_id += 1 else: tu = (a_node, b_node, np.sum(self.matrix.matrix_view[i, j, :]), np.sum(self.matrix.matrix_view[j, i, :])) self.report.append('No centroids available to depict flow between node {0} and node' '{1}. Total AB flow was equal to {2} and total BA flow was ' 'equal to {3}'.format(*tu)) unnasigned += np.sum(self.matrix.matrix_view[i, j, :]) + \ np.sum(self.matrix.matrix_view[j, i, :]) else: tu = (a_node, np.sum(self.matrix.matrix_view[i, :, :])) self.report.append('No centroids available to depict flows from node {0} to all the others.' 'Total flow from this zone is equal to {1}'.format(*tu)) unnasigned += np.sum(self.matrix.matrix_view[i, :, :]) q += self.matrix.zones self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', q)) if unnasigned > 0: self.report.append('Total non assigned flows (not counting intrazonals):' + str(unnasigned)) if desireline_link_id > 1: a = dlpr.addFeatures(all_features) self.result_layer = desireline_layer else: self.report.append('Nothing to show') elif self.dl_type == "DelaunayLines": self.emit(SIGNAL("desire_lines"), ('text_dl', "Building Delaunay dataset")) points = [] node_id_in_delaunay_results = {} i = 0 self.emit(SIGNAL("desire_lines"), ('job_size_dl', len(all_centroids))) for k, v in all_centroids.iteritems(): self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', i)) points.append(v) node_id_in_delaunay_results[i] = k i += 1 self.emit(SIGNAL("desire_lines"), ('text_dl', "Computing Delaunay Triangles")) tri = Delaunay(np.array(points)) # We process all the triangles to only get each edge once self.emit(SIGNAL("desire_lines"), ('text_dl', "Building Delaunay Network: Collecting Edges")) edges = [] if self.python_version == 32: all_edges = tri.vertices else: all_edges = tri.simplices self.emit(SIGNAL("desire_lines"), ('job_size_dl', len(all_edges))) for j, triangle in enumerate(all_edges): self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', j)) links = list(itertools.combinations(triangle, 2)) for i in links: edges.append([min(i[0],i[1]), max(i[0],i[1])]) self.emit(SIGNAL("desire_lines"), ('text_dl', "Building Delaunay Network: Getting unique edges")) edges = OrderedDict((str(x), x) for x in edges).values() # Writing Delaunay layer self.emit(SIGNAL("desire_lines"), ('text_dl', "Building Delaunay Network: Assembling Layer")) desireline_link_id = 1 data = [] dl_ids_on_links = {} self.emit(SIGNAL("desire_lines"), ('job_size_dl', len(edges))) for j, edge in enumerate(edges): self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', j)) a_node = node_id_in_delaunay_results[edge[0]] a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_node = node_id_in_delaunay_results[edge[1]] b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) dist = QgsGeometry().fromPoint(a_point).distance(QgsGeometry().fromPoint(b_point)) line = [] line.append(desireline_link_id) line.append(a_node) line.append(b_node) line.append(dist) line.append(dist) line.append(0) data.append(line) dl_ids_on_links[desireline_link_id] = [a_node, b_node, 0, dist] desireline_link_id += 1 self.emit(SIGNAL("desire_lines"), ('text_dl', "Building graph")) network = np.asarray(data) del data #types for the network self.graph = Graph() itype = self.graph.default_types('int') ftype = self.graph.default_types('float') all_types = [itype, itype, itype, ftype, ftype, np.int8] all_titles = ['link_id', 'a_node', 'b_node', 'distance_ab', 'distance_ba', 'direction'] dt = [(t, d) for t, d in zip(all_titles, all_types)] self.graph.network = np.zeros(network.shape[0], dtype=dt) for k, t in enumerate(dt): self.graph.network[t[0]] = network[:, k].astype(t[1]) del network self.graph.type_loaded = 'NETWORK' self.graph.status = 'OK' self.graph.network_ok = True try: self.graph.prepare_graph(self.matrix.index.astype(np.int64)) self.graph.set_graph(cost_field='distance', skim_fields=False, block_centroid_flows=False) self.results = AssignmentResults() self.results.prepare(self.graph, self.matrix) self.emit(SIGNAL("desire_lines"), ('text_dl', "Assigning demand")) self.emit(SIGNAL("desire_lines"), ('job_size_dl', self.matrix.index.shape[0])) assigner = allOrNothing(self.matrix, self.graph, self.results) assigner.execute() self.report = assigner.report self.emit(SIGNAL("desire_lines"), ('text_dl', "Collecting results")) link_loads = self.results.save_to_disk() self.emit(SIGNAL("desire_lines"), ('text_dl', "Building resulting layer")) features = [] max_edges = len(edges) self.emit(SIGNAL("desire_lines"), ('job_size_dl', max_edges)) for i, link_id in enumerate(link_loads.index): self.emit(SIGNAL("desire_lines"), ('jobs_done_dl', i)) a_node, b_node, direct, dist = dl_ids_on_links[link_id] attr = [int(link_id), a_node, b_node, direct, dist] a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolyline([a_point, b_point])) for c in self.matrix.view_names: attr.extend([float(link_loads.data[c + '_ab'][i]), float(link_loads.data[c + '_ba'][i]), float(link_loads.data[c + '_tot'][i])]) feature.setAttributes(attr) features.append(feature) a = dlpr.addFeatures(features) self.result_layer = desireline_layer except ValueError as error: self.report = [error.message] self.emit(SIGNAL("desire_lines"), ('finished_desire_lines_procedure', 0))
class TestGraph(TestCase): def test_create_from_geography(self): self.graph = Graph() self.graph.create_from_geography( test_network, "link_id", "dir", "distance", centroids=centroids, skim_fields=[], anode="A_NODE", bnode="B_NODE", ) self.graph.set_graph(cost_field="distance", block_centroid_flows=True) def test_load_network_from_csv(self): pass def test_prepare_graph(self): self.test_create_from_geography() self.graph.prepare_graph(centroids) reference_graph = Graph() reference_graph.load_from_disk(test_graph) if not np.array_equal(self.graph.graph, reference_graph.graph): self.fail("Reference graph and newly-prepared graph are not equal") def test_set_graph(self): self.test_prepare_graph() self.graph.set_graph(cost_field="distance", block_centroid_flows=True) if self.graph.num_zones != centroids.shape[0]: self.fail("Number of centroids not properly set") if self.graph.num_links != 222: self.fail("Number of links not properly set") if self.graph.num_nodes != 93: self.fail("Number of nodes not properly set - " + str(self.graph.num_nodes)) def test_save_to_disk(self): self.test_create_from_geography() self.graph.save_to_disk(join(path_test, "aequilibrae_test_graph.aeg")) self.graph_id = self.graph.__id__ self.graph_version = self.graph.__version__ def test_load_from_disk(self): self.test_save_to_disk() reference_graph = Graph() reference_graph.load_from_disk(test_graph) new_graph = Graph() new_graph.load_from_disk(join(path_test, "aequilibrae_test_graph.aeg")) comparisons = [ ("Graph", new_graph.graph, reference_graph.graph), ("b_nodes", new_graph.b_node, reference_graph.b_node), ("Forward-Star", new_graph.fs, reference_graph.fs), ("cost", new_graph.cost, reference_graph.cost), ("centroids", new_graph.centroids, reference_graph.centroids), ("skims", new_graph.skims, reference_graph.skims), ("link ids", new_graph.ids, reference_graph.ids), ("Network", new_graph.network, reference_graph.network), ("All Nodes", new_graph.all_nodes, reference_graph.all_nodes), ( "Nodes to indices", new_graph.nodes_to_indices, reference_graph.nodes_to_indices, ), ] for comparison, newg, refg in comparisons: if not np.array_equal(newg, refg): self.fail( "Reference %s and %s created and saved to disk are not equal" % (comparison, comparison) ) comparisons = [ ("nodes", new_graph.num_nodes, reference_graph.num_nodes), ("links", new_graph.num_links, reference_graph.num_links), ("zones", new_graph.num_zones, reference_graph.num_zones), ( "block through centroids", new_graph.block_centroid_flows, reference_graph.block_centroid_flows, ), ("Graph ID", new_graph.__id__, self.graph_id), ("Graph Version", new_graph.__version__, self.graph_version), ] for comparison, newg, refg in comparisons: if newg != refg: self.fail( "Reference %s and %s created and saved to disk are not equal" % (comparison, comparison) ) def test_reset_single_fields(self): pass def test_add_single_field(self): pass def test_available_skims(self): self.test_set_graph() if self.graph.available_skims() != ["distance"]: self.fail("Skim availability with problems")
class TestGraph(TestCase): def test_create_from_geography(self): self.graph = Graph() self.graph.create_from_geography( test_network, "link_id", "dir", "distance", centroids=centroids, skim_fields=[], anode="A_NODE", bnode="B_NODE", ) self.graph.set_graph(cost_field="distance") self.graph.set_blocked_centroid_flows(block_centroid_flows=True) self.graph.set_skimming("distance") def test_prepare_graph(self): self.test_create_from_geography() self.graph.prepare_graph(centroids) reference_graph = Graph() reference_graph.load_from_disk(test_graph) if not np.array_equal(self.graph.graph, reference_graph.graph): self.fail("Reference graph and newly-prepared graph are not equal") def test_set_graph(self): self.test_prepare_graph() self.graph.set_graph(cost_field="distance") self.graph.set_blocked_centroid_flows(block_centroid_flows=True) if self.graph.num_zones != centroids.shape[0]: self.fail("Number of centroids not properly set") if self.graph.num_links != 222: self.fail("Number of links not properly set") if self.graph.num_nodes != 93: self.fail("Number of nodes not properly set - " + str(self.graph.num_nodes)) def test_save_to_disk(self): self.test_create_from_geography() self.graph.save_to_disk(join(path_test, "aequilibrae_test_graph.aeg")) self.graph_id = self.graph.__id__ self.graph_version = self.graph.__version__ def test_load_from_disk(self): self.test_save_to_disk() reference_graph = Graph() reference_graph.load_from_disk(test_graph) new_graph = Graph() new_graph.load_from_disk(join(path_test, "aequilibrae_test_graph.aeg")) comparisons = [ ("Graph", new_graph.graph, reference_graph.graph), ("b_nodes", new_graph.b_node, reference_graph.b_node), ("Forward-Star", new_graph.fs, reference_graph.fs), ("cost", new_graph.cost, reference_graph.cost), ("centroids", new_graph.centroids, reference_graph.centroids), ("skims", new_graph.skims, reference_graph.skims), ("link ids", new_graph.ids, reference_graph.ids), ("Network", new_graph.network, reference_graph.network), ("All Nodes", new_graph.all_nodes, reference_graph.all_nodes), ("Nodes to indices", new_graph.nodes_to_indices, reference_graph.nodes_to_indices), ] for comparison, newg, refg in comparisons: if not np.array_equal(newg, refg): self.fail( "Reference %s and %s created and saved to disk are not equal" % (comparison, comparison)) comparisons = [ ("nodes", new_graph.num_nodes, reference_graph.num_nodes), ("links", new_graph.num_links, reference_graph.num_links), ("zones", new_graph.num_zones, reference_graph.num_zones), ("block through centroids", new_graph.block_centroid_flows, reference_graph.block_centroid_flows), ("Graph ID", new_graph.__id__, self.graph_id), ("Graph Version", new_graph.__version__, self.graph_version), ] for comparison, newg, refg in comparisons: if newg != refg: self.fail( "Reference %s and %s created and saved to disk are not equal" % (comparison, comparison)) def test_available_skims(self): self.test_set_graph() if self.graph.available_skims() != ["distance"]: self.fail("Skim availability with problems") def test_exclude_links(self): p = Project() p.load(siouxfalls_project) p.network.build_graphs() g = p.network.graphs['c'] # type: Graph # excludes a link before any setting or preparation g.exclude_links([12]) g.set_graph('distance') r1 = PathResults() r1.prepare(g) r1.compute_path(1, 14) self.assertEqual(list(r1.path), [2, 6, 10, 34]) # We exclude one link that we know was part of the last shortest path g.exclude_links([10]) r2 = PathResults() r2.prepare(g) r2.compute_path(1, 14) self.assertEqual(list(r2.path), [2, 7, 36, 34]) p.conn.close()
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 TestAllOrNothing(TestCase): def setUp(self) -> None: self.mat_name = AequilibraeMatrix().random_name() self.g = Graph() self.g.load_from_disk(test_graph) self.g.set_graph(cost_field="distance") # Creates the matrix for assignment args = { "file_name": os.path.join(gettempdir(), self.mat_name), "zones": self.g.num_zones, "matrix_names": ["cars", "trucks"], "index_names": ["my indices"], } matrix = AequilibraeMatrix() matrix.create_empty(**args) matrix.index[:] = self.g.centroids[:] matrix.cars.fill(1.1) matrix.trucks.fill(2.2) # Exports matrix to OMX in order to have two matrices to work with matrix.export(os.path.join(gettempdir(), "my_matrix.omx")) matrix.close() 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() 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")
class TestPathResults(TestCase): def setUp(self) -> None: # graph self.g = Graph() self.g.load_from_disk(test_graph) self.g.set_graph(cost_field="distance") self.r = PathResults() try: self.r.prepare(self.g) except Exception as err: self.fail("Path result preparation failed - {}".format(err.__str__())) def test_reset(self): self.r.compute_path(dest, origin) self.r.reset() self.assertEqual(self.r.path, None, 'Fail to reset the Path computation object') self.assertEqual(self.r.path_nodes, None, 'Fail to reset the Path computation object') self.assertEqual(self.r.path_link_directions, None, 'Fail to reset the Path computation object') self.assertEqual(self.r.milepost, None, 'Fail to reset the Path computation object') self.assertEqual(self.r.predecessors.max(), -1, 'Fail to reset the Path computation object') self.assertEqual(self.r.predecessors.min(), -1, 'Fail to reset the Path computation object') self.assertEqual(self.r.connectors.max(), -1, 'Fail to reset the Path computation object') self.assertEqual(self.r.connectors.min(), -1, 'Fail to reset the Path computation object') self.assertEqual(self.r.skims.max(), np.inf, 'Fail to reset the Path computation object') self.assertEqual(self.r.skims.min(), np.inf, 'Fail to reset the Path computation object') new_r = PathResults() with self.assertRaises(ValueError): new_r.reset() def test_compute_paths(self): path_computation(origin, dest, self.g, self.r) if list(self.r.path) != [53, 52, 13]: self.fail("Path computation failed. Wrong sequence of links") if list(self.r.path_nodes) != [5, 168, 166, 27]: self.fail("Path computation failed. Wrong sequence of path nodes") if list(self.r.milepost) != [0, 341, 1398, 2162]: self.fail("Path computation failed. Wrong milepost results") self.r.compute_path(origin, dest) if list(self.r.path) != [53, 52, 13]: self.fail("Path computation failed. Wrong sequence of links") if list(self.r.path_nodes) != [5, 168, 166, 27]: self.fail("Path computation failed. Wrong sequence of path nodes") if list(self.r.milepost) != [0, 341, 1398, 2162]: self.fail("Path computation failed. Wrong milepost results") if list(self.r.path_link_directions) != [-1, -1, -1]: self.fail("Path computation failed. Wrong link directions") self.r.compute_path(dest, origin) if list(self.r.path_link_directions) != [1, 1, 1]: self.fail("Path computation failed. Wrong link directions") def test_update_trace(self): self.r.compute_path(origin, dest - 1) self.r.update_trace(dest) if list(self.r.path) != [53, 52, 13]: self.fail("Path computation failed. Wrong sequence of links") if list(self.r.path_nodes) != [5, 168, 166, 27]: self.fail("Path computation failed. Wrong sequence of path nodes") if list(self.r.milepost) != [0, 341, 1398, 2162]: self.fail("Path computation failed. Wrong milepost results") if list(self.r.path_link_directions) != [-1, -1, -1]: self.fail("Path computation failed. Wrong link directions")
class DesireLinesProcedure(WorkerThread): def __init__(self, parentThread, layer, id_field, matrix, dl_type): WorkerThread.__init__(self, parentThread) self.layer = layer self.id_field = id_field self.matrix = matrix self.dl_type = dl_type self.error = None if error: self.error = 'Scipy and/or Numpy not installed' self.procedure = "ASSIGNMENT" def doWork(self): if self.error is None: layer = get_vector_layer_by_name(self.layer) idx = layer.fieldNameIndex(self.id_field) matrix = self.matrix featcount = layer.featureCount() self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, featcount)) P = 0 points = [] point_ids = [] for feat in layer.getFeatures(): P += 1 self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, int(P))) self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Loading Layer Features: " + str(P) + "/" + str(featcount))) geom = feat.geometry() if geom is not None: point = list(geom.centroid().asPoint()) points.append(point) point_ids.append(feat.attributes()[idx]) points = np.array(points) self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, featcount)) self.emit( SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Preparing consistency check Matrix Vs. Zoning layer")) vector1 = np.nonzero(np.sum(matrix, axis=0))[0] vector2 = np.nonzero(np.sum(matrix, axis=1))[0] nonzero = np.hstack((vector1, vector2)) self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, nonzero.shape[0])) for i, zone in enumerate(nonzero): if zone not in point_ids: self.error = 'Zone ' + str( zone) + ' with positive flow not in zoning file' break self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, i + 1)) self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, nonzero.shape[0])) if self.error is None: #Creating resulting layer EPSG_code = int(layer.crs().authid().split(":")[1]) desireline_layer = QgsVectorLayer( "LineString?crs=epsg:" + str(EPSG_code), self.dl_type, "memory") dlpr = desireline_layer.dataProvider() dlpr.addAttributes([ QgsField("link_id", QVariant.Int), QgsField("A_Node", QVariant.Int), QgsField("B_Node", QVariant.Int), QgsField("direct", QVariant.Int), QgsField("length", QVariant.Double), QgsField("AB_FLOW", QVariant.Double), QgsField("BA_FLOW", QVariant.Double), QgsField("TOT_FLOW", QVariant.Double) ]) desireline_layer.updateFields() if self.dl_type == "DesireLines": self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Creating Desire Lines")) self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, self.matrix.shape[0] * self.matrix.shape[1] / 2)) #We create the dictionary with point information all_points = {} point_ids = np.array(point_ids).astype(np.int) for i in range(point_ids.shape[0]): all_points[point_ids[i]] = points[i] # We are assuming that the matrix is square here. Maybe we could add more general code layer desireline_link_id = 1 q = 0 all_features = [] for i in range(self.matrix.shape[0]): for j in xrange(i + 1, self.matrix.shape[1]): q += 1 self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, q)) if self.matrix[i, j] + self.matrix[j, i] > 0: a_node = i a_point = QgsPoint(all_points[a_node][0], all_points[a_node][1]) b_node = j b_point = QgsPoint(all_points[b_node][0], all_points[b_node][1]) dist = QgsGeometry().fromPoint(a_point).distance( QgsGeometry().fromPoint(b_point)) feature = QgsFeature() feature.setGeometry( QgsGeometry.fromPolyline([a_point, b_point])) feature.setAttributes([ desireline_link_id, a_node, b_node, 0, dist, float(self.matrix[i, j]), float(self.matrix[j, i]), float(self.matrix[i, j] + self.matrix[j, i]) ]) all_features.append(feature) desireline_link_id += 1 a = dlpr.addFeatures(all_features) self.result_layer = desireline_layer elif self.dl_type == "DelaunayLines": self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Computing Delaunay Triangles")) tri = Delaunay(points) #We process all the triangles to only get each edge once self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building Delaunay Network: Collecting Edges")) edges = [] for triangle in tri.simplices: links = list(itertools.combinations(triangle, 2)) for i in links: l = [min(i[0], i[1]), max(i[0], i[1])] if l not in edges: edges.append(l) #Writing Delaunay layer self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building Delaunay Network: Assembling Layer")) desireline_link_id = 1 data = [] for edge in edges: a_node = edge[0] a_point = QgsPoint(points[a_node][0], points[a_node][1]) b_node = edge[1] b_point = QgsPoint(points[b_node][0], points[b_node][1]) dist = QgsGeometry().fromPoint(a_point).distance( QgsGeometry().fromPoint(b_point)) line = [] line.append(desireline_link_id) line.append(point_ids[a_node]) line.append(point_ids[b_node]) line.append(dist) line.append(dist) line.append(0) data.append(line) desireline_link_id += 1 self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building graph")) network = np.asarray(data) del data #types for the network all_types = [ np.int64, np.int64, np.int64, np.float64, np.float64, np.int64 ] all_titles = [ 'link_id', 'a_node', 'b_node', 'length_ab', 'length_ba', 'direction' ] dt = [(t, d) for t, d in zip(all_titles, all_types)] self.graph = Graph() self.graph.network = np.zeros(network.shape[0], dtype=dt) for k, t in enumerate(dt): self.graph.network[t[0]] = network[:, k].astype(t[1]) del network # Here we transform the network to go from node 1 to N max_node = max(np.max(self.graph.network['a_node']), np.max(self.graph.network['b_node'])) max_node = max(max_node, self.matrix.shape[0], self.matrix.shape[1]) + 1 self.hash = np.zeros(max_node, np.int) # Checks if any zone from the matrix is not present in the areas/node layer t1 = np.sum(self.matrix, axis=0) t2 = np.sum(self.matrix, axis=1) if t1.shape[0] > t2.shape[0]: t2.resize(t1.shape) elif t2.shape[0] > t1.shape[0]: t1.resize(t2.shape) totals = t1 + t2 all_nodes = np.bincount(self.graph.network['a_node']) for i in range(totals.shape[0]): if totals[i]: if not all_nodes[i]: qgis.utils.iface.messageBar().pushMessage( "Matrix has demand for zones that do not exist " "in the zones/nodes provided. Demand for those" "ones were ignored. e.g. " + str(i), '', level=3) break h = 1 for i in range(self.graph.network.shape[0]): a_node = self.graph.network['a_node'][i] if self.hash[a_node] == 0: self.hash[a_node] = h h += 1 b_node = self.graph.network['b_node'][i] if self.hash[b_node] == 0: self.hash[b_node] = h h += 1 self.graph.network['a_node'][i] = self.hash[a_node] self.graph.network['b_node'][i] = self.hash[b_node] # End of network transformation #Now we transform the matrix appropriately self.matrix = reblocks_matrix(self.matrix, self.hash, h) self.graph.type_loaded = 'NETWORK' self.graph.status = 'OK' self.graph.network_ok = True self.graph.prepare_graph() self.graph.set_graph(h - 1, cost_field='length', block_centroid_flows=False) self.results = AssignmentResults() self.results.prepare(self.graph) self.results.set_cores(1) # Do the assignment all_or_nothing(self.matrix, self.graph, self.results) f = self.results.link_loads[:, 0] link_loads = np.zeros((f.shape[0] + 1, 2)) for i in range(f.shape[0] - 1): direction = self.graph.graph['direction'][i] link_id = self.graph.graph['link_id'][i] flow = f[i] if direction == 1: link_loads[link_id, 0] = flow else: link_loads[link_id, 1] = flow desireline_link_id = 1 for edge in edges: a_node = edge[0] a_point = QgsPoint(points[a_node][0], points[a_node][1]) b_node = edge[1] b_point = QgsPoint(points[b_node][0], points[b_node][1]) dist = QgsGeometry().fromPoint(a_point).distance( QgsGeometry().fromPoint(b_point)) feature = QgsFeature() feature.setGeometry( QgsGeometry.fromPolyline([a_point, b_point])) feature.setAttributes([ desireline_link_id, point_ids[a_node], point_ids[b_node], 0, dist, float(link_loads[desireline_link_id, 0]), float(link_loads[desireline_link_id, 1]), float(link_loads[desireline_link_id, 0] + link_loads[desireline_link_id, 1]) ]) a = dlpr.addFeatures([feature]) desireline_link_id += 1 self.result_layer = desireline_layer self.emit(SIGNAL("finished_threaded_procedure( PyQt_PyObject )"), True)
class TestGraph(TestCase): def test_create_from_geography(self): self.graph = Graph() self.graph.create_from_geography( test_network, 'link_id', 'dir', 'distance', centroids=centroids, skim_fields = [], anode="A_NODE", bnode="B_NODE") self.graph.set_graph(cost_field='distance', block_centroid_flows=True) def test_load_network_from_csv(self): pass def test_prepare_graph(self): self.test_create_from_geography() self.graph.prepare_graph(centroids) reference_graph = Graph() reference_graph.load_from_disk(test_graph) if not np.array_equal(self.graph.graph, reference_graph.graph): self.fail('Reference graph and newly-prepared graph are not equal') def test_set_graph(self): self.test_prepare_graph() self.graph.set_graph(cost_field='distance',block_centroid_flows=True) if self.graph.num_zones != centroids.shape[0]: self.fail('Number of centroids not properly set') if self.graph.num_links != 222: self.fail('Number of links not properly set') if self.graph.num_nodes != 93: self.fail('Number of nodes not properly set - ' + str(self.graph.num_nodes)) def test_save_to_disk(self): self.test_create_from_geography() self.graph.save_to_disk(join(path_test, 'aequilibrae_test_graph.aeg')) self.graph_id = self.graph.__id__ self.graph_version = self.graph.__version__ def test_load_from_disk(self): self.test_save_to_disk() reference_graph = Graph() reference_graph.load_from_disk(test_graph) new_graph = Graph() new_graph.load_from_disk(join(path_test, 'aequilibrae_test_graph.aeg')) comparisons = [('Graph', new_graph.graph, reference_graph.graph), ('b_nodes', new_graph.b_node, reference_graph.b_node), ('Forward-Star', new_graph.fs, reference_graph.fs), ('cost', new_graph.cost, reference_graph.cost), ('centroids', new_graph.centroids, reference_graph.centroids), ('skims', new_graph.skims, reference_graph.skims), ('link ids', new_graph.ids, reference_graph.ids), ('Network', new_graph.network, reference_graph.network), ('All Nodes', new_graph.all_nodes, reference_graph.all_nodes), ('Nodes to indices', new_graph.nodes_to_indices, reference_graph.nodes_to_indices)] for comparison, newg, refg in comparisons: if not np.array_equal(newg, refg): self.fail('Reference %s and %s created and saved to disk are not equal' %(comparison, comparison)) comparisons = [('nodes', new_graph.num_nodes, reference_graph.num_nodes), ('links', new_graph.num_links, reference_graph.num_links), ('zones', new_graph.num_zones, reference_graph.num_zones), ('block through centroids', new_graph.block_centroid_flows, reference_graph.block_centroid_flows), ('Graph ID', new_graph.__id__, self.graph_id), ('Graph Version', new_graph.__version__, self.graph_version)] for comparison, newg, refg in comparisons: if newg != refg: self.fail('Reference %s and %s created and saved to disk are not equal' %(comparison, comparison)) def test_reset_single_fields(self): pass def test_add_single_field(self): pass def test_available_skims(self): self.test_set_graph() if self.graph.available_skims() != ['distance']: self.fail('Skim availability with problems')
class ImpedanceMatrixDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface): QDialog.__init__(self) self.iface = iface self.setupUi(self) self.result = SkimResults() self.validtypes = integer_types + float_types self.tot_skims = 0 self.name_skims = 0 self.graph = None self.skimmeable_fields = [] self.skim_fields = [] self.error = None # FIRST, we connect slot signals # For loading a new graph self.load_graph_from_file.clicked.connect( self.loaded_new_graph_from_file) # For adding skims # self.bt_add_skim.clicked.connect(self.add_to_skim_list) self.but_adds_to_links.clicked.connect(self.append_to_list) self.but_removes_from_links.clicked.connect(self.removes_fields) self.do_dist_matrix.clicked.connect(self.run_skimming) # SECOND, we set visibility for sections that should not be shown when the form opens (overlapping items) # and re-dimension the items that need re-dimensioning self.hide_all_progress_bars() self.available_skims_table.setColumnWidth(0, 245) self.skim_list.setColumnWidth(0, 245) self.available_skims_table.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) self.skim_list.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) # loads default path from parameters self.path = standard_path() def removes_fields(self): table = self.available_skims_table final_table = self.skim_list for i in final_table.selectedRanges(): old_fields = [ final_table.item(row, 0).text() for row in xrange(i.topRow(), i.bottomRow() + 1) ] for row in xrange(i.bottomRow(), i.topRow() - 1, -1): final_table.removeRow(row) counter = table.rowCount() for field in old_fields: table.setRowCount(counter + 1) item1 = QTableWidgetItem(field) item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) table.setItem(counter, 0, item1) counter += 1 def append_to_list(self): table = self.available_skims_table final_table = self.skim_list for i in table.selectedRanges(): new_fields = [ table.item(row, 0).text() for row in xrange(i.topRow(), i.bottomRow() + 1) ] for f in new_fields: self.skim_fields.append(f.encode('utf-8')) for row in xrange(i.bottomRow(), i.topRow() - 1, -1): table.removeRow(row) counter = final_table.rowCount() for field in new_fields: final_table.setRowCount(counter + 1) item1 = QTableWidgetItem(field) item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) final_table.setItem(counter, 0, item1) counter += 1 def hide_all_progress_bars(self): self.progressbar.setVisible(False) self.progress_label.setVisible(False) self.progressbar.setValue(0) self.progress_label.setText('') def loaded_new_graph_from_file(self): file_types = ["AequilibraE graph(*.aeg)"] new_name, file_type = GetOutputFileName(self, 'Graph file', file_types, ".aeg", self.path) self.cb_minimizing.clear() self.available_skims_table.clearContents() self.block_paths.setChecked(False) self.graph = None if new_name is not None: self.graph_file_name.setText(new_name) self.graph = Graph() self.graph.load_from_disk(new_name) self.block_paths.setChecked(self.graph.block_centroid_flows) graph_fields = list(self.graph.graph.dtype.names) self.skimmeable_fields = self.graph.available_skims() self.available_skims_table.setRowCount(len(self.skimmeable_fields)) for q in self.skimmeable_fields: self.cb_minimizing.addItem(q) self.available_skims_table.setItem(0, 0, QTableWidgetItem(q)) def browse_outfile(self): self.imped_results = None new_name, extension = GetOutputFileName( self, 'AequilibraE impedance computation', matrix_export_types, '.aem', self.path) if new_name is not None: self.imped_results = new_name.encode('utf-8') def run_thread(self): self.do_dist_matrix.setVisible(False) self.progressbar.setRange(0, self.graph.num_zones) QObject.connect(self.worker_thread, SIGNAL("skimming"), self.signal_handler) self.worker_thread.start() self.exec_() def signal_handler(self, val): if val[0] == 'zones finalized': self.progressbar.setValue(val[1]) elif val[0] == 'text skimming': self.progress_label.setText(val[1]) elif val[0] == 'finished_threaded_procedure': self.finished_threaded_procedure() def finished_threaded_procedure(self): self.report = self.worker_thread.report self.result.skims.export(self.imped_results) self.exit_procedure() def run_skimming(self): # Saving results if self.error is None: self.browse_outfile() cost_field = self.cb_minimizing.currentText().encode('utf-8') # We prepare the graph to set all nodes as centroids if self.rdo_all_nodes.isChecked(): self.graph.prepare_graph(self.graph.all_nodes) self.graph.set_graph( cost_field=cost_field, skim_fields=self.skim_fields, block_centroid_flows=self.block_paths.isChecked()) self.result.prepare(self.graph) self.funding1.setVisible(False) self.funding2.setVisible(False) self.progressbar.setVisible(True) self.progress_label.setVisible(True) self.worker_thread = NetworkSkimming(self.graph, self.result) try: self.run_thread() except ValueError as error: qgis.utils.iface.messageBar().pushMessage("Input error", error.message, level=3) else: qgis.utils.iface.messageBar().pushMessage("Error:", self.error, level=3) def check_inputs(self): self.error = None if self.rdo_all_nodes.isChecked() and self.block_paths.isChecked(): self.error = 'It is not possible to trace paths between all nodes while blocking flows through centroids' if self.graph is None: self.error = 'No graph loaded' if len(self.skim_fields) < 1: self.error = 'No skim fields provided' def exit_procedure(self): self.close() if self.report: dlg2 = ReportDialog(self.iface, self.report) dlg2.show() dlg2.exec_()
class DesireLinesProcedure(WorkerThread): def __init__(self, parentThread, layer: str, id_field: int, matrix: AequilibraeMatrix, matrix_hash: dict, dl_type: str) -> None: WorkerThread.__init__(self, parentThread) self.layer = layer self.id_field = id_field self.matrix = matrix self.dl_type = dl_type self.error = None self.matrix_hash = matrix_hash self.report = [] self.logger = logging.getLogger('aequilibrae') self.nodes_to_indices = {matrix.index[x]: x for x in range(matrix.zones)} self.python_version = (8 * struct.calcsize("P")) if error: self.error = 'Scipy and/or Numpy not installed' self.report.append(self.error) self.procedure = "ASSIGNMENT" def doWork(self): if self.error is None: # In case we have only one class unnasigned = 0 classes = self.matrix.matrix_view.shape[2] layer = get_vector_layer_by_name(self.layer) idx = layer.dataProvider().fieldNameIndex(self.id_field) feature_count = layer.featureCount() self.desire_lines.emit(('job_size_dl', feature_count)) all_centroids = {} for P, feat in enumerate(layer.getFeatures()): geom = feat.geometry() if geom is not None: point = list(geom.centroid().asPoint()) centroid_id = feat.attributes()[idx] all_centroids[centroid_id] = point self.desire_lines.emit(('jobs_done_dl', P)) self.desire_lines.emit(('text_dl', "Loading Layer Features: " + str(P) + "/" + str(feature_count))) # Creating resulting layer EPSG_code = int(layer.crs().authid().split(":")[1]) desireline_layer = QgsVectorLayer("LineString?crs=epsg:" + str(EPSG_code), self.dl_type, "memory") dlpr = desireline_layer.dataProvider() base_dl_fields = [QgsField("link_id", QVariant.Int), QgsField("A_Node", QVariant.Int), QgsField("B_Node", QVariant.Int), QgsField("direct", QVariant.Int), QgsField("distance", QVariant.Double)] if self.dl_type == "DesireLines": items = [] for i, j in all_centroids.items(): items.append((i, j[0], j[1])) coords = np.array(items) coord_index = np.zeros((self.matrix.index[:].max().astype(np.int64) + 1, 2)) coord_index[coords[:, 0].astype(np.int64), 0] = coords[:, 1] coord_index[coords[:, 0].astype(np.int64), 1] = coords[:, 2] self.desire_lines.emit(('text_dl', "Manipulating matrix indices")) zones = self.matrix.index[:].shape[0] a = np.array(self.matrix.index[:], np.int64) ij, ji = np.meshgrid(a, a, sparse=False, indexing='ij') ij = ij.flatten() ji = ji.flatten() arrays = [ij, ji] self.desire_lines.emit(('text_dl', "Collecting all matrices")) self.desire_lines.emit(('job_size_dl', len(self.matrix.view_names))) total_mat = np.zeros((zones, zones), np.float64) for i, mat in enumerate(self.matrix.view_names): arrays.append(self.matrix.matrix[mat].flatten()) total_mat += self.matrix.matrix[mat] self.desire_lines.emit(('jobs_done_dl', i + 1)) # Eliminates the cells for which we don't have geography self.desire_lines.emit(('text_dl', "Filtering zones with no geography available")) zones_with_no_geography = [x for x in self.matrix.index[:] if x not in all_centroids] if zones_with_no_geography: self.desire_lines.emit(('job_size_dl', len(zones_with_no_geography))) for k, z in enumerate(zones_with_no_geography): i = self.matrix.matrix_hash[z] t = np.nansum(total_mat[i, :]) + np.nansum(total_mat[:, i]) unnasigned += t self.report.append( 'Zone {} does not have a corresponding centroid/zone. Total flow {}'.format(z, t)) total_mat[i, :] = 0 total_mat[:, i] = 0 self.desire_lines.emit(('jobs_done_dl', k + 1)) self.desire_lines.emit(('text_dl', "Filtering down to OD pairs with flows")) field_names = [x for x in self.matrix.view_names] nonzero = np.nonzero(total_mat.flatten()) arrays = np.vstack(arrays).transpose() arrays = arrays[nonzero, :] arrays = arrays.reshape(arrays.shape[1], arrays.shape[2]) base_types = [(x, np.float64) for x in ['from', 'to']] base_types = base_types + [(x + '_AB', np.float64) for x in field_names] dtypes_ab = [(x, np.int64) for x in ['from', 'to']] + [(x + '_AB', float) for x in field_names] dtypes_ba = [(x, np.int64) for x in ['to', 'from']] + [(x + '_BA', float) for x in field_names] ab_mat = np.array(arrays[arrays[:, 0] > arrays[:, 1], :]) ba_mat = np.array(arrays[arrays[:, 0] < arrays[:, 1], :]) flows_ab = ab_mat.view(base_types) flows_ab = flows_ab.reshape(flows_ab.shape[:-1]) flows_ab = flows_ab.astype(dtypes_ab) flows_ba = ba_mat.view(base_types) flows_ba = flows_ba.reshape(flows_ba.shape[:-1]) flows_ba = flows_ba.astype(dtypes_ba) defaults1 = {x + '_AB': 0.0 for x in field_names} defaults = {x + '_BA': 0.0 for x in field_names} defaults = {**defaults, **defaults1} self.desire_lines.emit(('text_dl', "Concatenating AB & BA flows")) flows = rfn.join_by(['from', 'to'], flows_ab, flows_ba, jointype='outer', defaults=defaults, usemask=True, asrecarray=True) flows = flows.filled() flows_ab = 0 flows_ba = 0 for f in flows.dtype.names[2:]: base_dl_fields.extend([QgsField(f, QVariant.Double)]) dlpr.addAttributes(base_dl_fields) desireline_layer.updateFields() self.desire_lines.emit(('text_dl', "Creating Desire Lines")) self.desire_lines.emit(('job_size_dl', flows.shape[0])) all_features = [] for i, rec in enumerate(flows): a_node = rec[0] b_node = rec[1] a_point = QgsPointXY(*all_centroids[a_node]) b_point = QgsPointXY(*all_centroids[b_node]) dist = QgsGeometry().fromPointXY(a_point).distance( QgsGeometry().fromPointXY(b_point)) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolylineXY([a_point, b_point])) attrs = [i + 1, int(a_node), int(b_node), 0, dist] attrs.extend([float(x) for x in list(rec)[2:]]) feature.setAttributes(attrs) all_features.append(feature) self.desire_lines.emit(('jobs_done_dl', i)) if unnasigned > 0: self.report.append('Total non assigned flows (not counting intrazonals):' + str(unnasigned)) if flows.shape[0] > 1: a = dlpr.addFeatures(all_features) self.result_layer = desireline_layer else: self.report.append('Nothing to show') elif self.dl_type == "DelaunayLines": for f in self.matrix.view_names: base_dl_fields.extend([QgsField(f + '_ab', QVariant.Double), QgsField(f + '_ba', QVariant.Double), QgsField(f + '_tot', QVariant.Double)]) dlpr.addAttributes(base_dl_fields) desireline_layer.updateFields() self.desire_lines.emit(('text_dl', "Building Delaunay dataset")) points = [] node_id_in_delaunay_results = {} i = 0 self.desire_lines.emit(('job_size_dl', len(all_centroids))) for k, v in all_centroids.items(): self.desire_lines.emit(('jobs_done_dl', i)) points.append(v) node_id_in_delaunay_results[i] = k i += 1 self.desire_lines.emit(('text_dl', "Computing Delaunay Triangles")) tri = Delaunay(np.array(points)) # We process all the triangles to only get each edge once self.desire_lines.emit(('text_dl', "Building Delaunay Network: Collecting Edges")) edges = [] if self.python_version == 32: all_edges = tri.vertices else: all_edges = tri.simplices self.desire_lines.emit(('job_size_dl', len(all_edges))) for j, triangle in enumerate(all_edges): self.desire_lines.emit(('jobs_done_dl', j)) links = list(itertools.combinations(triangle, 2)) for i in links: edges.append([min(i[0], i[1]), max(i[0], i[1])]) self.desire_lines.emit(('text_dl', "Building Delaunay Network: Getting unique edges")) edges = OrderedDict((str(x), x) for x in edges).values() # Writing Delaunay layer self.desire_lines.emit(('text_dl', "Building Delaunay Network: Assembling Layer")) desireline_link_id = 1 data = [] dl_ids_on_links = {} self.desire_lines.emit(('job_size_dl', len(edges))) for j, edge in enumerate(edges): self.desire_lines.emit(('jobs_done_dl', j)) a_node = node_id_in_delaunay_results[edge[0]] a_point = all_centroids[a_node] a_point = QgsPointXY(a_point[0], a_point[1]) b_node = node_id_in_delaunay_results[edge[1]] b_point = all_centroids[b_node] b_point = QgsPointXY(b_point[0], b_point[1]) dist = QgsGeometry().fromPointXY(a_point).distance(QgsGeometry().fromPointXY(b_point)) line = [] line.append(desireline_link_id) line.append(a_node) line.append(b_node) line.append(dist) line.append(dist) line.append(0) data.append(line) dl_ids_on_links[desireline_link_id] = [a_node, b_node, 0, dist] desireline_link_id += 1 self.desire_lines.emit(('text_dl', "Building graph")) network = np.asarray(data) del data # types for the network self.graph = Graph() itype = self.graph.default_types('int') ftype = self.graph.default_types('float') all_types = [itype, itype, itype, ftype, ftype, np.int8] all_titles = ['link_id', 'a_node', 'b_node', 'distance_ab', 'distance_ba', 'direction'] dt = [(t, d) for t, d in zip(all_titles, all_types)] self.graph.network = np.zeros(network.shape[0], dtype=dt) for k, t in enumerate(dt): self.graph.network[t[0]] = network[:, k].astype(t[1]) del network self.graph.type_loaded = 'NETWORK' self.graph.status = 'OK' self.graph.network_ok = True self.graph.prepare_graph(self.matrix.index.astype(np.int64)) self.graph.set_graph(cost_field='distance', skim_fields=False, block_centroid_flows=False) self.results = AssignmentResults() self.results.prepare(self.graph, self.matrix) self.desire_lines.emit(('text_dl', "Assigning demand")) self.desire_lines.emit(('job_size_dl', self.matrix.index.shape[0])) assigner = allOrNothing(self.matrix, self.graph, self.results) assigner.execute() self.report = assigner.report print(self.results.link_loads) self.desire_lines.emit(('text_dl', "Collecting results")) self.desire_lines.emit(('text_dl', "Building resulting layer")) features = [] max_edges = len(edges) self.desire_lines.emit(('job_size_dl', max_edges)) link_loads = self.results.save_to_disk() for i, link_id in enumerate(link_loads.index): self.desire_lines.emit(('jobs_done_dl', i)) a_node, b_node, direct, dist = dl_ids_on_links[link_id] attr = [int(link_id), a_node, b_node, direct, dist] a_point = all_centroids[a_node] a_point = QgsPointXY(a_point[0], a_point[1]) b_point = all_centroids[b_node] b_point = QgsPointXY(b_point[0], b_point[1]) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolylineXY([a_point, b_point])) for c in self.matrix.view_names: attr.extend([float(link_loads.data[c + '_ab'][i]), float(link_loads.data[c + '_ba'][i]), float(link_loads.data[c + '_tot'][i])]) feature.setAttributes(attr) features.append(feature) a = dlpr.addFeatures(features) self.result_layer = desireline_layer self.desire_lines.emit(('finished_desire_lines_procedure', 0))
class TestGraph(TestCase): def test_create_from_geography(self): self.graph = Graph() self.graph.create_from_geography(test_network, 'link_id', 'dir', 'distance', centroids=centroids, skim_fields=[], anode="A_NODE", bnode="B_NODE") self.graph.set_graph(cost_field='distance', block_centroid_flows=True) def test_load_network_from_csv(self): pass def test_prepare_graph(self): self.test_create_from_geography() self.graph.prepare_graph(centroids) reference_graph = Graph() reference_graph.load_from_disk(test_graph) if not np.array_equal(self.graph.graph, reference_graph.graph): self.fail('Reference graph and newly-prepared graph are not equal') def test_set_graph(self): self.test_prepare_graph() self.graph.set_graph(cost_field='distance', block_centroid_flows=True) if self.graph.num_zones != centroids.shape[0]: self.fail('Number of centroids not properly set') if self.graph.num_links != 222: self.fail('Number of links not properly set') if self.graph.num_nodes != 93: self.fail('Number of nodes not properly set - ' + str(self.graph.num_nodes)) def test_save_to_disk(self): self.test_create_from_geography() self.graph.save_to_disk(join(path_test, 'aequilibrae_test_graph.aeg')) self.graph_id = self.graph.__id__ self.graph_version = self.graph.__version__ def test_load_from_disk(self): self.test_save_to_disk() reference_graph = Graph() reference_graph.load_from_disk(test_graph) new_graph = Graph() new_graph.load_from_disk(join(path_test, 'aequilibrae_test_graph.aeg')) comparisons = [ ('Graph', new_graph.graph, reference_graph.graph), ('b_nodes', new_graph.b_node, reference_graph.b_node), ('Forward-Star', new_graph.fs, reference_graph.fs), ('cost', new_graph.cost, reference_graph.cost), ('centroids', new_graph.centroids, reference_graph.centroids), ('skims', new_graph.skims, reference_graph.skims), ('link ids', new_graph.ids, reference_graph.ids), ('Network', new_graph.network, reference_graph.network), ('All Nodes', new_graph.all_nodes, reference_graph.all_nodes), ('Nodes to indices', new_graph.nodes_to_indices, reference_graph.nodes_to_indices) ] for comparison, newg, refg in comparisons: if not np.array_equal(newg, refg): self.fail( 'Reference %s and %s created and saved to disk are not equal' % (comparison, comparison)) comparisons = [ ('nodes', new_graph.num_nodes, reference_graph.num_nodes), ('links', new_graph.num_links, reference_graph.num_links), ('zones', new_graph.num_zones, reference_graph.num_zones), ('block through centroids', new_graph.block_centroid_flows, reference_graph.block_centroid_flows), ('Graph ID', new_graph.__id__, self.graph_id), ('Graph Version', new_graph.__version__, self.graph_version) ] for comparison, newg, refg in comparisons: if newg != refg: self.fail( 'Reference %s and %s created and saved to disk are not equal' % (comparison, comparison)) def test_reset_single_fields(self): pass def test_add_single_field(self): pass def test_available_skims(self): self.test_set_graph() if self.graph.available_skims() != ['distance']: self.fail('Skim availability with problems')
class DesireLinesProcedure(WorkerThread): def __init__(self, parentThread, layer, id_field, matrix, matrix_hash, dl_type): WorkerThread.__init__(self, parentThread) self.layer = layer self.id_field = id_field self.matrix = matrix self.dl_type = dl_type self.error = None self.matrix_hash = matrix_hash self.report = [] self.python_version = (8 * struct.calcsize("P")) if error: self.error = 'Scipy and/or Numpy not installed' self.procedure = "ASSIGNMENT" def doWork(self): if self.error is None: layer = get_vector_layer_by_name(self.layer) idx = layer.fieldNameIndex(self.id_field) matrix = self.matrix matrix_nodes = max(self.matrix_hash.values()) + 1 featcount = layer.featureCount() self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, featcount)) all_centroids = {} P = 0 for feat in layer.getFeatures(): P += 1 self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, int(P))) self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Loading Layer Features: " + str(P) + "/" + str(featcount))) geom = feat.geometry() if geom is not None: point = list(geom.centroid().asPoint()) centroid_id = feat.attributes()[idx] all_centroids[centroid_id] = point if centroid_id not in self.matrix_hash.keys(): self.matrix_hash[centroid_id] = matrix_nodes matrix_nodes += 1 reverse_hash = {v: k for k, v in self.matrix_hash.iteritems()} #Creating resulting layer EPSG_code = int(layer.crs().authid().split(":")[1]) desireline_layer = QgsVectorLayer( "LineString?crs=epsg:" + str(EPSG_code), self.dl_type, "memory") dlpr = desireline_layer.dataProvider() dlpr.addAttributes([ QgsField("link_id", QVariant.Int), QgsField("A_Node", QVariant.Int), QgsField("B_Node", QVariant.Int), QgsField("direct", QVariant.Int), QgsField("length", QVariant.Double), QgsField("ab_flow", QVariant.Double), QgsField("ba_flow", QVariant.Double), QgsField("tot_flow", QVariant.Double) ]) desireline_layer.updateFields() if self.dl_type == "DesireLines": self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Creating Desire Lines")) self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, self.matrix.shape[0] * self.matrix.shape[1] / 2)) desireline_link_id = 1 q = 0 all_features = [] for i in range(self.matrix.shape[0]): if np.sum(self.matrix[i, :]) > 0: a_node = reverse_hash[i] for j in xrange(i + 1, self.matrix.shape[1]): q += 1 b_node = reverse_hash[j] self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, q)) if self.matrix[i, j] + self.matrix[j, i] > 0: if a_node in all_centroids.keys( ) and b_node in all_centroids.keys(): a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) dist = QgsGeometry().fromPoint( a_point).distance( QgsGeometry().fromPoint(b_point)) feature = QgsFeature() feature.setGeometry( QgsGeometry.fromPolyline( [a_point, b_point])) feature.setAttributes([ desireline_link_id, int(a_node), int(b_node), 0, dist, float(self.matrix[i, j]), float(self.matrix[j, i]), float(self.matrix[i, j] + self.matrix[j, i]) ]) all_features.append(feature) desireline_link_id += 1 else: tu = (a_node, b_node, self.matrix[i, j], self.matrix[j, i]) self.report.append( 'No centroids available to depict flow between node {0} and node {1}. AB flow was equal to {2} and BA flow was equal to {3}' .format(*tu)) else: q += self.matrix.shape[1] self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, q)) if desireline_link_id > 1: a = dlpr.addFeatures(all_features) self.result_layer = desireline_layer else: self.error = 'Nothing to show' elif self.dl_type == "DelaunayLines": self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Computing Delaunay Triangles")) points = [] seccond_relation = {} i = 0 for k, v in all_centroids.iteritems(): points.append(v) seccond_relation[i] = k i += 1 tri = Delaunay(np.array(points)) #We process all the triangles to only get each edge once self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building Delaunay Network: Collecting Edges")) edges = [] if self.python_version == 32: all_edges = tri.vertices else: all_edges = tri.simplices for triangle in all_edges: links = list(itertools.combinations(triangle, 2)) for i in links: l = [min(i[0], i[1]), max(i[0], i[1])] if l not in edges: edges.append(l) #Writing Delaunay layer self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building Delaunay Network: Assembling Layer")) desireline_link_id = 1 data = [] dl_link_ids = {} for edge in edges: a_node = seccond_relation[edge[0]] a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_node = seccond_relation[edge[1]] b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) dist = QgsGeometry().fromPoint(a_point).distance( QgsGeometry().fromPoint(b_point)) line = [] line.append(desireline_link_id) line.append(self.matrix_hash[a_node]) line.append(self.matrix_hash[b_node]) line.append(dist) line.append(dist) line.append(0) data.append(line) if a_node not in dl_link_ids.keys(): dl_link_ids[a_node] = {} dl_link_ids[a_node][b_node] = desireline_link_id desireline_link_id += 1 self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building graph")) network = np.asarray(data) del data #types for the network all_types = [ np.int64, np.int64, np.int64, np.float64, np.float64, np.int64 ] all_titles = [ 'link_id', 'a_node', 'b_node', 'length_ab', 'length_ba', 'direction' ] dt = [(t, d) for t, d in zip(all_titles, all_types)] self.graph = Graph() self.graph.network = np.zeros(network.shape[0], dtype=dt) for k, t in enumerate(dt): self.graph.network[t[0]] = network[:, k].astype(t[1]) del network self.graph.type_loaded = 'NETWORK' self.graph.status = 'OK' self.graph.network_ok = True self.graph.prepare_graph() self.graph.set_graph(matrix_nodes, cost_field='length', block_centroid_flows=False) self.results = AssignmentResults() self.results.prepare(self.graph) # self.results.set_cores(1) self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Assigning demand")) # Do the assignment #self.all_or_nothing(self.matrix, self.graph, self.results) self.report = all_or_nothing(self.matrix, self.graph, self.results) self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Collecting results")) f = self.results.link_loads link_loads = np.zeros((f.shape[0] + 1, 2)) self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, f.shape[0] - 1)) for i in range(f.shape[0] - 1): self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, i)) direction = self.graph.graph['direction'][i] link_id = self.graph.graph['link_id'][i] flow = f[i] if direction == 1: link_loads[link_id, 0] = flow else: link_loads[link_id, 1] = flow self.emit(SIGNAL("ProgressText (PyQt_PyObject)"), (0, "Building resulting layer")) features = [] max_edges = len(edges) self.emit(SIGNAL("ProgressMaxValue(PyQt_PyObject)"), (0, max_edges)) for i, edge in enumerate(edges): self.emit(SIGNAL("ProgressValue(PyQt_PyObject)"), (0, i)) a_node = seccond_relation[edge[0]] a_point = all_centroids[a_node] a_point = QgsPoint(a_point[0], a_point[1]) b_node = seccond_relation[edge[1]] b_point = all_centroids[b_node] b_point = QgsPoint(b_point[0], b_point[1]) dist = QgsGeometry().fromPoint(a_point).distance( QgsGeometry().fromPoint(b_point)) feature = QgsFeature() feature.setGeometry( QgsGeometry.fromPolyline([a_point, b_point])) desireline_link_id = dl_link_ids[a_node][b_node] feature.setAttributes([ desireline_link_id, a_node, b_node, 0, dist, float(link_loads[desireline_link_id, 0]), float(link_loads[desireline_link_id, 1]), float(link_loads[desireline_link_id, 0] + link_loads[desireline_link_id, 1]) ]) features.append(feature) a = dlpr.addFeatures(features) self.result_layer = desireline_layer self.emit(SIGNAL("finished_threaded_procedure( PyQt_PyObject )"), True)
class ImpedanceMatrixDialog(QtGui.QDialog, Ui_Impedance_Matrix): def __init__(self, iface): QDialog.__init__(self) self.iface = iface self.setupUi(self) self.result = PathResults() self.validtypes = integer_types + float_types self.tot_skims = 0 self.name_skims = 0 self.skimmeable_fields = [] self.skim_fields = [] # FIRST, we connect slot signals #For loading a new graph self.load_graph_from_file.clicked.connect( self.loaded_new_graph_from_file) # For adding skims self.bt_add_skim.clicked.connect(self.add_to_skim_list) self.skim_list.doubleClicked.connect(self.slotDoubleClicked) # RUN skims self.select_result.clicked.connect(self.browse_outfile) self.do_dist_matrix.clicked.connect(self.run_skimming) # SECOND, we set visibility for sections that should not be shown when the form opens (overlapping items) # and re-dimension the items that need re-dimensioning self.HideAllProgressBars() self.skim_list.setColumnWidth(0, 567) # loads default path from parameters self.path = standard_path() def HideAllProgressBars(self): self.progressbar.setVisible(False) self.progress_label.setVisible(False) self.progressbar.setValue(0) self.progress_label.setText('') def loaded_new_graph_from_file(self): file_types = "AequilibraE graph(*.aeg)" if len(self.graph_file_name.text()) > 0: newname = QFileDialog.getOpenFileName(None, 'Result file', self.graph_file_name.text(), file_types) else: newname = QFileDialog.getOpenFileName(None, 'Result file', self.path, file_types) self.cb_minimizing.clear() self.cb_skims.clear() self.all_centroids.setText('') self.block_paths.setChecked(False) if newname is not None: self.graph_file_name.setText(newname) self.graph = Graph() self.graph.load_from_disk(newname) self.all_centroids.setText(str(self.graph.centroids)) if self.graph.block_centroid_flows: self.block_paths.setChecked(True) graph_fields = list(self.graph.graph.dtype.names) self.skimmeable_fields = [ x for x in graph_fields if x not in [ 'link_id', 'a_node', 'b_node', 'direction', 'id', ] ] for q in self.skimmeable_fields: self.cb_minimizing.addItem(q) self.cb_skims.addItem(q) def add_to_skim_list(self): if self.cb_skims.currentIndex() >= 0: self.tot_skims += 1 self.skim_list.setRowCount(self.tot_skims) self.skim_list.setItem( self.tot_skims - 1, 0, QtGui.QTableWidgetItem((self.cb_skims.currentText()))) self.skim_fields.append(self.cb_skims.currentText()) self.cb_skims.removeItem(self.cb_skims.currentIndex()) def slotDoubleClicked(self, mi): row = mi.row() if row > -1: self.cb_skims.addItem(self.skim_list.item(row, 0).text()) self.skim_fields.pop(row) self.skim_list.removeRow(row) self.tot_skims -= 1 def browse_outfile(self): file_types = "Comma-Separated files(*.csv)" if self.npy_res.isChecked(): file_types = "Numpy Binnary Array(*.npy)" if len(self.imped_results.text()) > 0: newname = QFileDialog.getSaveFileName(None, 'Result file', self.imped_results.text(), file_types) else: newname = QFileDialog.getSaveFileName(None, 'Result file', self.path, file_types) self.imped_results.setText('') if newname != None: self.imped_results.setText(newname) def runThread(self): QObject.connect(self.workerThread, SIGNAL("ProgressValue( PyQt_PyObject )"), self.ProgressValueFromThread) QObject.connect(self.workerThread, SIGNAL("ProgressText( PyQt_PyObject )"), self.ProgressTextFromThread) QObject.connect(self.workerThread, SIGNAL("ProgressMaxValue( PyQt_PyObject )"), self.ProgressRangeFromThread) QObject.connect(self.workerThread, SIGNAL("FinishedThreadedProcedure( PyQt_PyObject )"), self.FinishedThreadedProcedure) self.workerThread.start() self.exec_() # VAL and VALUE have the following structure: (bar/text ID, value) def ProgressRangeFromThread(self, val): self.progressbar.setRange(0, val[1]) def ProgressValueFromThread(self, val): self.progressbar.setValue(val[1]) def ProgressTextFromThread(self, val): self.progress_label.setText(val[1]) def FinishedThreadedProcedure(self, val): if self.workerThread.error is not None: qgis.utils.iface.messageBar().pushMessage( "Assignment did NOT run correctly", self.workerThread.error, level=3) else: mat = self.workerThread.skim_matrices mat[mat > 1e308] = np.inf # We treat the "infinity" that should have been treated within the Cython code if self.npy_res.isChecked(): np.save(self.imped_results.text(), mat) q = open(self.imped_results.text() + '.csv', 'w') for l in self.skim_fields: print >> q, l q.flush() q.close() if self.csv_res.isChecked(): q = open(self.imped_results.text(), 'w') text = 'Origin,Destination,' + self.cb_minimizing.currentText() for l in self.skim_fields: text = text + ',' + l print >> q, text for i in range(mat.shape[0]): if np.sum(mat[i, :, :]) > 0: for j in range(mat.shape[1]): if np.sum(mat[i, j, :]) > 0: text = str(i) + ',' + str(j) s = 0 for k in range(mat.shape[2]): if mat[i, j, k] != np.inf: s += mat[i, j, k] text = text + ',' + str(mat[i, j, k]) else: text += ',' if s > 0: print >> q, text q.flush() q.close() self.close() def run_skimming(self): # Saving results centroids = int(self.all_centroids.text()) cost_field = self.cb_minimizing.currentText() block_paths = False if self.block_paths.isChecked(): block_paths = True if centroids > 0: self.graph.set_graph(centroids, cost_field, self.skim_fields, block_paths) self.result.prepare(self.graph) else: qgis.utils.iface.messageBar().pushMessage( "Nothing to run.", 'Number of centroids set to zero', level=3) if len(self.imped_results.text()) == 0: qgis.utils.iface.messageBar().pushMessage( "Cannot run.", 'No output file provided', level=3) else: self.funding1.setVisible(False) self.funding2.setVisible(False) self.progressbar.setVisible(True) self.progress_label.setVisible(True) self.workerThread = ComputeDistMatrix( qgis.utils.iface.mainWindow(), self.graph, self.result) self.runThread()