def test_Infinite_Reach(self): """ Test reach at infinite radius """ compute_centrality(self.graph, self.nodes, True, False, False, False, False, INFINITE_RADIUS, True, 1, [], []) assert eq_tol(getattr(self.graph["A"], REACH), 3) assert eq_tol(getattr(self.graph["B"], REACH), 3) assert eq_tol(getattr(self.graph["C"], REACH), 3) assert eq_tol(getattr(self.graph["D"], REACH), 3)
def test_Closeness(self): """ Test closeness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, False, True, False, INFINITE_RADIUS, True, 1, [], []) assert eq_tol(getattr(self.graph["A"], CLOSENESS), 1.0 / 7) assert eq_tol(getattr(self.graph["B"], CLOSENESS), 1.0 / 7) assert eq_tol(getattr(self.graph["C"], CLOSENESS), 1.0 / 5) assert eq_tol(getattr(self.graph["D"], CLOSENESS), 1.0 / 11)
def test_Gravity(self): """ Test gravity at infinite radius and a beta value of 1 """ compute_centrality(self.graph, self.nodes, False, True, False, False, False, INFINITE_RADIUS, True, 1, [], []) assert eq_tol(getattr(self.graph["A"], GRAVITY), 1) assert eq_tol(getattr(self.graph["B"], GRAVITY), 1) assert eq_tol(getattr(self.graph["C"], GRAVITY), 4.0 / 3) assert eq_tol(getattr(self.graph["D"], GRAVITY), 2.0 / 3)
def test_Betweenness(self): """ Test betweenness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, True, False, False, INFINITE_RADIUS, True, 1, [], []) assert eq_tol(getattr(self.graph["A"], BETWEENNESS), 0) assert eq_tol(getattr(self.graph["B"], BETWEENNESS), 0) assert eq_tol(getattr(self.graph["C"], BETWEENNESS), 6) assert eq_tol(getattr(self.graph["D"], BETWEENNESS), 0)
def test_Degree_Reach(self): """ Test reach at radius 1 - the reach values are no the degrees of the nodes """ compute_centrality(self.graph, self.nodes, True, False, False, False, False, 1, 1, [], []) assert eq_tol(getattr(self.graph["A"], REACH), 2) assert eq_tol(getattr(self.graph["B"], REACH), 2) assert eq_tol(getattr(self.graph["C"], REACH), 3) assert eq_tol(getattr(self.graph["D"], REACH), 1)
def test_Gravity(self): """ Test gravity at infinite radius and a beta value of 1 """ compute_centrality(self.graph, self.nodes, False, True, False, False, False, INFINITE_RADIUS, 1, [], []) assert eq_tol(getattr(self.graph["A"], GRAVITY), 1) assert eq_tol(getattr(self.graph["B"], GRAVITY), 1) assert eq_tol(getattr(self.graph["C"], GRAVITY), 4.0 / 3) assert eq_tol(getattr(self.graph["D"], GRAVITY), 2.0 / 3)
def test_Closeness(self): """ Test closeness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, False, True, False, INFINITE_RADIUS, 1, [], []) assert eq_tol(getattr(self.graph["A"], CLOSENESS), 1.0 / 7) assert eq_tol(getattr(self.graph["B"], CLOSENESS), 1.0 / 7) assert eq_tol(getattr(self.graph["C"], CLOSENESS), 1.0 / 5) assert eq_tol(getattr(self.graph["D"], CLOSENESS), 1.0 / 11)
def test_Infinite_Reach(self): """ Test reach at infinite radius """ compute_centrality(self.graph, self.nodes, True, False, False, False, False, INFINITE_RADIUS, 1, [], []) assert eq_tol(getattr(self.graph["A"], REACH), 3) assert eq_tol(getattr(self.graph["B"], REACH), 3) assert eq_tol(getattr(self.graph["C"], REACH), 3) assert eq_tol(getattr(self.graph["D"], REACH), 3)
def test_Betweenness(self): """ Test betweenness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, True, False, False, INFINITE_RADIUS, 1, [], []) assert eq_tol(getattr(self.graph["A"], BETWEENNESS), 0) assert eq_tol(getattr(self.graph["B"], BETWEENNESS), 0) assert eq_tol(getattr(self.graph["C"], BETWEENNESS), 6) assert eq_tol(getattr(self.graph["D"], BETWEENNESS), 0)
def test_Straightness(self): """ Test straightness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, False, False, True, INFINITE_RADIUS, True, 1, [], []) assert eq_tol(getattr(self.graph["A"], STRAIGHTNESS), 2 + sqrt(5) / (1 + sqrt(2))) assert eq_tol(getattr(self.graph["B"], STRAIGHTNESS), 2 + sqrt(5) / (1 + sqrt(2))) assert eq_tol(getattr(self.graph["C"], STRAIGHTNESS), 3) assert eq_tol(getattr(self.graph["D"], STRAIGHTNESS), 1 + 2 * sqrt(5) / (1 + sqrt(2)))
def test_Straightness(self): """ Test straightness at infinite radius """ compute_centrality(self.graph, self.nodes, False, False, False, False, True, INFINITE_RADIUS, 1, [], []) assert eq_tol(getattr(self.graph["A"], STRAIGHTNESS), 2 + sqrt(5) / (1 + sqrt(2))) assert eq_tol(getattr(self.graph["B"], STRAIGHTNESS), 2 + sqrt(5) / (1 + sqrt(2))) assert eq_tol(getattr(self.graph["C"], STRAIGHTNESS), 3) assert eq_tol(getattr(self.graph["D"], STRAIGHTNESS), 1 + 2 * sqrt(5) / (1 + sqrt(2)))
AddWarning(WARNING_POINTS_NOT_IN_GRAPH(N, point_not_in_graph_count)) AddMessage(STEP_3_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_3_FAILED) success = False # Step 4 if success: AddMessage(STEP_4_STARTED) try: # Compute measures compute_centrality(nodes, selected_features, inputs[COMPUTE_REACH], inputs[COMPUTE_GRAVITY], inputs[COMPUTE_BETWEENNESS], inputs[COMPUTE_CLOSENESS], inputs[COMPUTE_STRAIGHTNESS], inputs[SEARCH_RADIUS], inputs[BETA], inputs[NORMALIZE_RESULTS], accumulator_fields) AddMessage(STEP_4_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_4_FAILED) success = False # Step 5 if success: AddMessage(STEP_5_STARTED) try: # Make output layer MakeFeatureLayer_management(in_features=output_feature_class, out_layer=output_layer_name)
AddWarning(WARNING_POINTS_NOT_IN_GRAPH(N, point_not_in_graph_count)) AddMessage(STEP_3_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_3_FAILED) success = False # Step 4 if success: AddMessage(STEP_4_STARTED) try: # Compute measures compute_centrality(nodes, selected_features, inputs[COMPUTE_REACH], inputs[COMPUTE_GRAVITY], inputs[COMPUTE_BETWEENNESS], inputs[COMPUTE_CLOSENESS], inputs[COMPUTE_STRAIGHTNESS], inputs[SEARCH_RADIUS], inputs[USE_NETWORK_RADIUS], inputs[BETA], inputs[NORMALIZE_RESULTS], accumulator_fields) AddMessage(STEP_4_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_4_FAILED) success = False # Step 5 if success: AddMessage(STEP_5_STARTED) try: # Make output layer MakeFeatureLayer_management(in_features=output_feature_class, out_layer=output_layer_name)
def main(): """ Runs the centrality tool. """ env.overwriteOutput = True # Enable overwritting CheckOutExtension("Network") # Success of the program through the six steps success = True # Inputs to the tool if len(argv) != INPUT_COUNT + 1: raise Exception("Invalid number of inputs") input_number = index() input_number.next() # Skip over sys.argv[0] inputs = {} inputs[INPUT_BUILDINGS] = argv[input_number.next()] inputs[POINT_LOCATION] = ("INSIDE" if argv[input_number.next()] == "true" else "CENTROID") inputs[INPUT_NETWORK] = argv[input_number.next()] inputs[COMPUTE_REACH] = argv[input_number.next()] == "true" inputs[COMPUTE_GRAVITY] = argv[input_number.next()] == "true" inputs[COMPUTE_BETWEENNESS] = argv[input_number.next()] == "true" inputs[COMPUTE_CLOSENESS] = argv[input_number.next()] == "true" inputs[COMPUTE_STRAIGHTNESS] = argv[input_number.next()] == "true" inputs[ID_ATTRIBUTE] = argv[input_number.next()] inputs[NODE_WEIGHT_ATTRIBUTE] = argv[input_number.next()] inputs[IMPEDANCE_ATTRIBUTE] = argv[input_number.next()] try: inputs[SEARCH_RADIUS] = float(argv[input_number.next()]) except: inputs[SEARCH_RADIUS] = INFINITE_RADIUS inputs[USE_NETWORK_RADIUS] = (argv[input_number.next()] == ON_THE_NETWORK_OPTION) try: inputs[BETA] = float(argv[input_number.next()]) except: raise Invalid_Input_Exception("Beta") inputs[NORMALIZE_RESULTS] = [measure for measure in argv[input_number.next()].split(";") if measure != "#"] inputs[OUTPUT_LOCATION] = argv[input_number.next()] inputs[OUTPUT_FILE_NAME] = argv[input_number.next()] inputs[ACCUMULATOR_ATTRIBUTES] = argv[input_number.next()] # Record the origin nodes for centrality measurements # This is important if the user selects a subset of the features to be origins selected_features = all_values_in_column(inputs[INPUT_BUILDINGS], inputs[ID_ATTRIBUTE]) # Clear selection if we got a layer file try: SelectLayerByAttribute_management(inputs[INPUT_BUILDINGS], "CLEAR_SELECTION") except: pass # Adjacency List table name node_locations_needed = (inputs[COMPUTE_STRAIGHTNESS] or not inputs[USE_NETWORK_RADIUS]) adj_dbf_name = ("%s_%s_%s_%s_%s_%s.dbf" % (ADJACENCY_LIST_NAME, basename(inputs[INPUT_BUILDINGS]), basename(inputs[INPUT_NETWORK]), inputs[ID_ATTRIBUTE], inputs[IMPEDANCE_ATTRIBUTE], inputs[ACCUMULATOR_ATTRIBUTES])).replace("#", "None") if len(adj_dbf_name) > MAX_FILE_NAME_LENGTH: AddWarning(WARNING_LARGE_ADJ_FILE_NAME) adj_dbf = join(inputs[OUTPUT_LOCATION], adj_dbf_name) # Output file names output_feature_class_name = feature_class_name(inputs[OUTPUT_FILE_NAME]) output_feature_class = "%s.shp" % join(inputs[OUTPUT_LOCATION], output_feature_class_name) # Create a feature class that is a copy of the input buildings try: AddMessage(INPUT_BUILDINGS_COPY_STARTED) CreateFeatureclass_management(out_path=inputs[OUTPUT_LOCATION], out_name=output_feature_class_name) CopyFeatures_management(in_features=inputs[INPUT_BUILDINGS], out_feature_class=output_feature_class) AddMessage(INPUT_BUILDINGS_COPY_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(INPUT_BUILDINGS_COPY_FAILED) success = False output_layer_name = layer_name(inputs[OUTPUT_FILE_NAME]) output_layer = "%s.lyr" % join(inputs[OUTPUT_LOCATION], output_layer_name) # If output has already been created, don't carry on if Exists(output_layer): AddWarning(WARNING_OUTPUT_ALREADY_EXISTS) success = False # We will convert polygon input buildings to point feature class buildings_description = Describe(output_feature_class) if buildings_description.shapeType == "Point": # Input buildings are already a point shape file inputs[INPUT_POINTS] = output_feature_class elif buildings_description.shapeType == "Polygon": # Input buildings need to be converted to point feature class point_feature_class_name = POINT_FEATURE_CLASS_NAME( basename(output_feature_class), inputs[POINT_LOCATION]) inputs[INPUT_POINTS] = "%s.shp" % join(inputs[OUTPUT_LOCATION], point_feature_class_name) # If FID is used as ID attribute, we need to change it since a point # shapefile will be in use if inputs[ID_ATTRIBUTE] == "FID": inputs[ID_ATTRIBUTE] = ORIGINAL_FID else: # Input buildings need to be either points or polygons raise Invalid_Input_Exception("Input Buildings") # Find the appropriate symbology layer for metric_index in range(len(METRICS)): if inputs[COMPUTE_REACH + metric_index]: first_metric = METRICS[metric_index] break symbology_layer_name = get_symbology_layer_name( buildings_description.shapeType, first_metric) symbology_layer = join(SYMBOLOGY_DIR, symbology_layer_name) def clean_up(): """ Removes all auxiliary files """ auxiliary_dir = join(inputs[OUTPUT_LOCATION], AUXILIARY_DIR_NAME) od_cost_matrix_layer = join(auxiliary_dir, OD_COST_MATRIX_LAYER_NAME) od_cost_matrix_lines = join(auxiliary_dir, OD_COST_MATRIX_LINES) temp_adj_dbf_name = "%s~.dbf" % adj_dbf_name[-4] temp_adj_dbf = join(inputs[OUTPUT_LOCATION], temp_adj_dbf_name) partial_adj_dbf = join(auxiliary_dir, PARTIAL_ADJACENCY_LIST_NAME) polygons = join(auxiliary_dir, POLYGONS_SHAPEFILE_NAME) raster = join(auxiliary_dir, RASTER_NAME) polygons_layer = join(auxiliary_dir, POLYGONS_LAYER_NAME) input_points_layer = join(auxiliary_dir, INPUT_POINTS_LAYER_NAME) for delete_path in [input_points_layer, polygons_layer, raster, polygons, partial_adj_dbf, temp_adj_dbf, od_cost_matrix_lines, od_cost_matrix_layer, auxiliary_dir]: delete(delete_path) try: """ Here we carry out the six steps of the tool """ # Step 1 if success: AddMessage(STEP_1_STARTED) # If necessary, convert input buildings to point feature class if buildings_description.shapeType == "Polygon": AddMessage(POINT_CONVERSION_STARTED) to_point_feature_class(output_feature_class, inputs[INPUT_POINTS], inputs[POINT_LOCATION]) AddMessage(POINT_CONVERSION_FINISHED) if Exists(adj_dbf): AddMessage(ADJACENCY_LIST_COMPUTED) if node_locations_needed: calculate_network_locations(inputs[INPUT_POINTS], inputs[INPUT_NETWORK]) AddMessage(STEP_1_FINISHED) else: try: compute_adjacency_list(inputs[INPUT_POINTS], inputs[INPUT_NETWORK], inputs[ID_ATTRIBUTE], inputs[IMPEDANCE_ATTRIBUTE], inputs[ACCUMULATOR_ATTRIBUTES], inputs[SEARCH_RADIUS], inputs[OUTPUT_LOCATION], adj_dbf_name) AddMessage(STEP_1_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_1_FAILED) success = False # Step 2 if success: AddMessage(STEP_2_STARTED) try: distance_field = trim("Total_%s" % inputs[IMPEDANCE_ATTRIBUTE]) accumulator_fields = set([trim("Total_%s" % accumulator_attribute) for accumulator_attribute in inputs[ACCUMULATOR_ATTRIBUTES].split( ";") if accumulator_attribute != "#"]) # Graph representation: dictionary mapping node id's to Node objects nodes = {} # The number of rows in |adj_dbf| directed_edge_count = int(GetCount_management(adj_dbf).getOutput(0)) graph_progress = Progress_Bar(directed_edge_count, 1, STEP_2) rows = UpdateCursor(adj_dbf) for row in rows: # Get neighboring nodes, and the distance between them origin_id = row.getValue(trim(ORIGIN_ID_FIELD_NAME)) destination_id = row.getValue(trim(DESTINATION_ID_FIELD_NAME)) distance = float(row.getValue(distance_field)) # Make sure the nodes are recorded in the graph for id in [origin_id, destination_id]: if not id in nodes: nodes[id] = Node() # Make sure that the nodes are neighbors in the graph if origin_id != destination_id and distance >= 0: accumulations = {} for field in accumulator_fields: accumulations[field] = float(row.getValue(field)) nodes[origin_id].add_neighbor(destination_id, distance, accumulations) nodes[destination_id].add_neighbor(origin_id, distance, accumulations) graph_progress.step() N = len(nodes) # The number of nodes in the graph if N == 0: AddWarning(WARNING_NO_NODES) success = False AddMessage(STEP_2_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_2_FAILED) success = False # Step 3 if success: AddMessage(STEP_3_STARTED) try: get_weights = inputs[NODE_WEIGHT_ATTRIBUTE] != "#" get_locations = node_locations_needed # Keep track of number nodes in input points not present in the graph point_not_in_graph_count = 0 input_point_count = int( GetCount_management(inputs[INPUT_POINTS]).getOutput(0)) node_attribute_progress = Progress_Bar(input_point_count, 1, STEP_3) rows = UpdateCursor(inputs[INPUT_POINTS]) for row in rows: id = row.getValue(inputs[ID_ATTRIBUTE]) if not id in nodes: point_not_in_graph_count += 1 continue if get_weights: setattr(nodes[id], WEIGHT, row.getValue(trim(inputs[NODE_WEIGHT_ATTRIBUTE]))) if get_locations: snap_x = row.getValue(trim("SnapX")) snap_y = row.getValue(trim("SnapY")) setattr(nodes[id], LOCATION, (snap_x, snap_y)) node_attribute_progress.step() if point_not_in_graph_count: AddWarning(WARNING_POINTS_NOT_IN_GRAPH(N, point_not_in_graph_count)) AddMessage(STEP_3_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_3_FAILED) success = False # Step 4 if success: AddMessage(STEP_4_STARTED) try: # Compute measures compute_centrality(nodes, selected_features, inputs[COMPUTE_REACH], inputs[COMPUTE_GRAVITY], inputs[COMPUTE_BETWEENNESS], inputs[COMPUTE_CLOSENESS], inputs[COMPUTE_STRAIGHTNESS], inputs[SEARCH_RADIUS], inputs[USE_NETWORK_RADIUS], inputs[BETA], inputs[NORMALIZE_RESULTS], accumulator_fields) AddMessage(STEP_4_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_4_FAILED) success = False # Step 5 if success: AddMessage(STEP_5_STARTED) try: # Make output layer MakeFeatureLayer_management(in_features=output_feature_class, out_layer=output_layer_name) # Save output layer SaveToLayerFile_management(output_layer_name, output_layer, "ABSOLUTE") # Use a test node to figure out which metrics were computed test_node_id = selected_features.pop() # Make sure the test node is in the graph while test_node_id not in nodes: test_node_id = selected_features.pop() test_node = nodes[test_node_id] measures = set([measure for measure in dir(test_node) if (measure in FINAL_ATTRIBUTES or is_accumulator_field(measure))]) # Add a field in the output layer for each computed metric for measure in measures: AddField_management(in_table=output_layer, field_name=trim(measure), field_type="DOUBLE", field_is_nullable="NON_NULLABLE") # Figure out the id field to use based on the type of input buildings if (buildings_description.shapeType == "Polygon" and inputs[ID_ATTRIBUTE] == ORIGINAL_FID): id_field = "FID" else: id_field = inputs[ID_ATTRIBUTE] # Fill the layer with the metric values write_progress = Progress_Bar(N, 1, STEP_5) layer_rows = UpdateCursor(output_layer) for row in layer_rows: id = row.getValue(id_field) for measure in measures: # If no value was computed for this node id, set value to 0 value = 0 if id in nodes and hasattr(nodes[id], measure): value = getattr(nodes[id], measure) row.setValue(trim(measure), value) layer_rows.updateRow(row) write_progress.step() # Save to toolbox output SetParameterAsText(OUTPUT_FEATURE_CLASS, output_feature_class) AddMessage(STEP_5_FINISHED) except: AddWarning(GetMessages(2)) AddMessage(STEP_5_FAILED) success = False # Step 6 if success: AddMessage(STEP_6_STARTED) # Apply symbology try: ApplySymbologyFromLayer_management(in_layer=output_layer, in_symbology_layer=symbology_layer) except: AddWarning(WARNING_APPLY_SYMBOLOGY_FAILED) AddWarning(GetMessages(2)) AddMessage(STEP_6_FAILED) # Display try: current_map_document = mapping.MapDocument("CURRENT") data_frame = mapping.ListDataFrames(current_map_document, "Layers")[0] add_layer = mapping.Layer(output_layer) mapping.AddLayer(data_frame, add_layer, "AUTO_ARRANGE") AddMessage(STEP_6_FINISHED) except: AddWarning(WARNING_FAIL_TO_DISPLAY) AddWarning(GetMessages(2)) AddMessage(STEP_6_FAILED) # Clean up clean_up() AddMessage(SUCCESS if success else FAILURE) except ExecuteAbort: clean_up()