# # Implementation of MapCompare algorithm. # Author: James P. Biagioni (jbiagi1@uic.edu) # Company: University of Illinois at Chicago # Created: 6/7/11 # # Author: Mahmuda Ahmed # Company: The University of Texas at San Antonio # Modified: 2013 # # Author: Jorren Hendriks (j.a.m.hendriks@student.tue.nl) # Institute: Eindhoven University of Technology (TU/e) # Modified: 4/30/2020 # # Author: Erfan Hosseini Sereshgi (shosseinisereshgi@tulane.edu) # Company: Tulane University # Modified: 6/10/2021 # # Author: Jordi Aguilar Larruy (jordi.aguilar.larruy@estudiantat.upc.edu) # Company: Universitat Politècnica de Catalunya # Modified: 5/31/2021 # import math import os import random import csv import sys import getopt import time from streetmap import StreetMap import spatialfunclib from rtree import Rtree import networkx as nx # global parameters breadcrumb_interval = 5.0 # meters breadcrumb_max_distance = float('infinity') # meters match_distance_threshold = 15.0 # meters debug_mode = False bearing_limit = 45.0 # degrees compare_mode = "knn" class Breadcrumb: def __init__(self, id, latitude, longitude, bearing): self.id = id self.latitude = latitude self.longitude = longitude self.bearing = bearing class BreadcrumbPath: def __init__(self): self.breadcrumbs = {} self.breadcrumb_index = Rtree() def add_breadcrumb(self, breadcrumb): self.breadcrumbs[breadcrumb.id] = breadcrumb self.breadcrumb_index.insert(breadcrumb.id, (breadcrumb.longitude, breadcrumb.latitude)) def delete_breadcrumb(self, breadcrumb): del self.breadcrumbs[breadcrumb.id] self.breadcrumb_index.delete(breadcrumb.id, [breadcrumb.longitude, breadcrumb.latitude, breadcrumb.longitude, breadcrumb.latitude]) class MatchedCrumbs: def __init__(self, map1_breadcrumb, map2_breadcrumb, distance): self.map1_breadcrumb = map1_breadcrumb self.map2_breadcrumb = map2_breadcrumb self.distance = distance class MapCompare: def __init__(self): self.map1 = None self.map2 = None def load_maps(self, map1_filename, map2_filename): map1_loaded = False map2_loaded = False # create new StreetMap object self.map1 = StreetMap() # load map1 OSMDB if self.map1.load_textdb_osm(map1_filename): map1_loaded = True # create new StreetMap object self.map2 = StreetMap() # load map2 GraphDB if self.map2.load_textdb_algo(map2_filename): map2_loaded = True return map1_loaded, map2_loaded def generate_root_locations(self, root_locations_filename): print("generating root locations...") # output newline for formatting sys.stdout.write("\n") sys.stdout.flush() # open evaluation output file root_locations_file = open_mkdir(root_locations_filename, 'w') # select root location from map1 and map2 root_location1 = self._find_components(self.map1) root_location2 = self._find_components(self.map2) #root_location1 = root_location1 + root_location2 #for rl in range(len(root_location1)): # root_locations_file.write(str(rl) + "," + str(root_location1[rl][0]) + "," + str(root_location1[rl][1]) + "\n") for rl in range(len(root_location1)): root_locations_file.write("1," + str(rl) + "," + str(root_location1[rl][0]) + "," + str(root_location1[rl][1]) + "\n") for rl in range(len(root_location2)): root_locations_file.write("2," + str(rl) + "," + str(root_location2[rl][0]) + "," + str(root_location2[rl][1]) + "\n") root_locations_file.flush() root_locations_file.close() print("done.") def compare_maps(self, eval_filename, root_locations_filename): # if there are no nodes or edges in map2 if ((len(self.map2.nodes) == 0) or (len(self.map2.edges) == 0)): # open evaluation output file eval_file = open(eval_filename, 'w') # write nothing into evaluation output file eval_file.write("") # close evaluation output file eval_file.close() # terminate comparison return # output newline for formatting sys.stdout.write("\n") sys.stdout.flush() # output start of map comparison sys.stdout.write("\nComparing maps...\n") sys.stdout.flush() print("reading root locations...") rootlocations1 = {} rootlocations2 = {} # open evaluation output file with open_mkdir(root_locations_filename, 'r') as f: c = csv.reader(f, delimiter=',', skipinitialspace=True) # iterate through all query results for map, id, lat, lon in c: if int(map) == 1: rootlocations1[int(id)] = (float(lat), float(lon)) if int(map) == 2: rootlocations2[int(id)] = (float(lat), float(lon)) # clear evaluation output file open_mkdir(eval_filename, 'w').close() print("Seeds on map1: "+str(len(rootlocations1))) print("Seeds on map2: "+str(len(rootlocations2))) # get breadcrumb edges of both maps print("Getting breadcrumb edges") map1_breadcrumb_edges = [] map2_breadcrumb_edges = [] for i in rootlocations1.values(): map1_breadcrumb_edges += self._get_breadcrumb_edges(self.map1, i, match_distance_threshold) for i in rootlocations2.values(): map2_breadcrumb_edges += self._get_breadcrumb_edges(self.map2, i, match_distance_threshold) print("Getting breadcrumb paths") # get breadcrumb paths of both maps map1_breadcrumb_path = self._get_breadcrumb_path(self.map1, rootlocations1[0], map1_breadcrumb_edges, None, None) map2_breadcrumb_path = self._get_breadcrumb_path(self.map2, rootlocations2[0], map2_breadcrumb_edges, None, None) if (debug_mode): print("root location: " + str(rootlocations1[0][0]) + "," + str(rootlocations1[0][1])) self._write_breadcrumb_path_to_file(map1_breadcrumb_path, "map1_breadcrumb_path.txt") self._write_breadcrumb_path_to_file(map2_breadcrumb_path, "map2_breadcrumb_path.txt") print("Comparing breadcrumb paths using "+compare_mode) # compare breadcrumb paths for map1 and map2 if compare_mode == "knn": self._compare_breadcrumb_paths(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) elif compare_mode == "wmm": self._weighted_maximum_matching(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) elif compare_mode == "mm": self._maximum_matching(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) elif compare_mode == "g": self._compare_breadcrumb_paths_greedy(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) elif compare_mode == "gs": self._compare_breadcrumb_paths_greedy_shortest(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) elif compare_mode == "gsb": self._compare_breadcrumb_paths_greedy_shortest_bearing(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) else: print("Not a valid comparing option --- using the default k-nearest neighbors matching algorithm") self._compare_breadcrumb_paths(map1_breadcrumb_path, map2_breadcrumb_path, eval_filename) # reset edge visited flags in both maps self.map1.reset_edge_visited_flags() self.map2.reset_edge_visited_flags() # finished with current evaluation print("'\rEvaluation run " + "... " + "done.", end="") # DEBUG -- terminate after one evaluation if (debug_mode): exit() print(flush=True) def compute_results(self, eval_file_name): counter = { "gTot": 0, "hTot": 0, "pos": 0 } # open evaluation output file with open_mkdir(eval_file_name, 'r') as f: c = csv.reader(f, delimiter=',', skipinitialspace=True) # iterate through all results for d, x, y, u, v in c: dist = int(float(d)) if dist == 1000000: counter["hTot"] += 1 elif dist == 2000000: counter["gTot"] += 1 else: for k in counter.keys(): counter[k] += 1 m, n, k = counter["gTot"], counter["hTot"], counter["pos"] print(("-[ Sample Counts ]" + "-" * 42 + "\n" "m = #samples in G | n = #samples in H | k = #matched samples\n" "{m:17d} | {n:17d} | {k:20d}\n" + "-" * 60) .format(m=m, n=n, k=k)) print(("-[ Sample Statistics ]" + "-" * 11 + "\n" "precision | recall | F-score\n" "{p:1.7f} | {r:1.7f} | {f:1.7f}\n" + "-" * 33) .format(p=k / m, r=k / n, f=2 * k / (n + m))) def _find_components(self, curr_map): rootlocations = [] bfs_queue = [] list_of_edges = list(curr_map.edges.values()) connected_component_nodes = [] connected_component = [] list_of_nodes = list(curr_map.nodes.values()) while len(list_of_nodes) > 0: source_node = list_of_nodes[0] if len(source_node.out_nodes) == 0: list_of_nodes.remove(source_node) continue bfs_queue.append(source_node) while (len(bfs_queue) > 0): curr_node = bfs_queue.pop(0) connected_component_nodes.append(curr_node) list_of_nodes.remove(curr_node) for out_node in curr_node.out_nodes: if out_node not in bfs_queue and out_node in list_of_nodes: bfs_queue.append(out_node) for node in connected_component_nodes: for edge in list_of_edges: if edge.in_node.id == node.id or edge.out_node.id == node.id: connected_component.append(edge) list_of_edges.remove(edge) rootlocations.append(self._select_map_root_location(connected_component)) connected_component = [] connected_component_nodes = [] return rootlocations def _maximum_matching(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: graph = nx.Graph() graph.add_nodes_from(map1_breadcrumb_path.breadcrumbs) graph.add_nodes_from(map2_breadcrumb_path.breadcrumbs) for node1 in map1_breadcrumb_path.breadcrumbs.values(): for node2 in map2_breadcrumb_path.breadcrumbs.values(): breadcrumb_distance = self._distance(node1,node2) if breadcrumb_distance <= match_distance_threshold: bearing_difference = spatialfunclib.bearing_difference(node1.bearing,node2.bearing) if bearing_difference < bearing_limit: graph.add_edge(node1,node2) print("The graph is loaded - starting the matching algorithm") matching = nx.algorithms.matching.max_weight_matching(graph, maxcardinality=True) print("Matching is done - writing files") for edge in matching: breadcrumb_distance = self._distance(edge[0],edge[1]) if (edge[0] in map1_breadcrumb_path.breadcrumbs.values()) and (edge[1] in map2_breadcrumb_path.breadcrumbs.values()): matched_breadcrumb = MatchedCrumbs(edge[0], edge[1], breadcrumb_distance) else: matched_breadcrumb = MatchedCrumbs(edge[1], edge[0], breadcrumb_distance) eval_file.write(str(matched_breadcrumb.distance) + "," + str( matched_breadcrumb.map1_breadcrumb.latitude) + "," + str( matched_breadcrumb.map1_breadcrumb.longitude) + "," + str( matched_breadcrumb.map2_breadcrumb.latitude) + "," + str( matched_breadcrumb.map2_breadcrumb.longitude) + "\n") matched += 1 if matched_breadcrumb.map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map1_breadcrumb) elif matched_breadcrumb.map1_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map1_breadcrumb) if matched_breadcrumb.map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map2_breadcrumb) elif matched_breadcrumb.map2_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map2_breadcrumb) for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") eval_file.flush() def _weighted_maximum_matching(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: graph = nx.Graph() graph.add_nodes_from(map1_breadcrumb_path.breadcrumbs) graph.add_nodes_from(map2_breadcrumb_path.breadcrumbs) for node1 in map1_breadcrumb_path.breadcrumbs.values(): for node2 in map2_breadcrumb_path.breadcrumbs.values(): breadcrumb_distance = self._distance(node1,node2) if breadcrumb_distance <= match_distance_threshold: bearing_difference = spatialfunclib.bearing_difference(node1.bearing,node2.bearing) if bearing_difference < bearing_limit: graph.add_edge(node1,node2, weight=match_distance_threshold - breadcrumb_distance) print("The graph is loaded - starting the matching algorithm") matching = nx.algorithms.matching.max_weight_matching(graph) print("Matching is done - writing files") for edge in matching: breadcrumb_distance = self._distance(edge[0],edge[1]) if (edge[0] in map1_breadcrumb_path.breadcrumbs.values()) and (edge[1] in map2_breadcrumb_path.breadcrumbs.values()): matched_breadcrumb = MatchedCrumbs(edge[0], edge[1], breadcrumb_distance) else: matched_breadcrumb = MatchedCrumbs(edge[1], edge[0], breadcrumb_distance) eval_file.write(str(matched_breadcrumb.distance) + "," + str( matched_breadcrumb.map1_breadcrumb.latitude) + "," + str( matched_breadcrumb.map1_breadcrumb.longitude) + "," + str( matched_breadcrumb.map2_breadcrumb.latitude) + "," + str( matched_breadcrumb.map2_breadcrumb.longitude) + "\n") matched += 1 if matched_breadcrumb.map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map1_breadcrumb) elif matched_breadcrumb.map1_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map1_breadcrumb) if matched_breadcrumb.map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map2_breadcrumb) elif matched_breadcrumb.map2_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumb.map2_breadcrumb) for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") eval_file.flush() def _compare_breadcrumb_paths_greedy_shortest_bearing(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: # iterate through map1 breadcrumbs, find nearest neighbor for each, and store in matched_breadcrumbs # This computes a many-to-many matching bearing_flag = 0 if (len(map2_breadcrumb_path.breadcrumbs) > 0): for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): while bearing_flag >= 0: # find closest breadcrumb in map2. Make sure there are some breadcrumbs left closest_map2_breadcrumb_id = list(map2_breadcrumb_path.breadcrumb_index.nearest((map1_breadcrumb.longitude, map1_breadcrumb.latitude), bearing_flag+1))[bearing_flag] closest_map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id] breadcrumb_distance = self._distance(map1_breadcrumb, closest_map2_breadcrumb) bearing_difference = spatialfunclib.bearing_difference(map1_breadcrumb.bearing, closest_map2_breadcrumb.bearing) if bearing_difference < bearing_limit: # create matched crumbs object matched_breadcrumb = MatchedCrumbs(map1_breadcrumb, closest_map2_breadcrumb, breadcrumb_distance) matched_breadcrumbs.append(matched_breadcrumb) bearing_flag = -1 else: bearing_flag+=1 bearing_flag = 0 # Now filter this matching to create a 1-to-1 matching, prioritizing shortest distances # 1. Sort list of matched breadcrumbs in ascending order according to distance matched_breadcrumbs.sort(key=lambda x: x.distance) # 2. Continue while there are matched breadcrumbs to be processed while (len(matched_breadcrumbs) > 0): # if distance between closest matched breadcrumbs is greater than the threshold if (matched_breadcrumbs[0].distance > match_distance_threshold): # Exit loop early. All nearest neighbor matches are too far away. break # compute bearing difference between breadcrumbs #bearing_difference = spatialfunclib.bearing_difference(matched_breadcrumbs[0].map1_breadcrumb.bearing, matched_breadcrumbs[0].map2_breadcrumb.bearing) #print(bearing_difference) # if map2 breadcrumb is still available if ( (matched_breadcrumbs[0].map2_breadcrumb.id in map2_breadcrumb_path.breadcrumbs.keys())): # Fix this matching. # remove map1 breadcrumb from path map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumbs[0].map1_breadcrumb) # remove map2 breadcrumb from path map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumbs[0].map2_breadcrumb) matched += 1 # output distance between breadcrumbs for evaluation output file eval_file.write(str(matched_breadcrumbs[0].distance) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.longitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.longitude) + "\n") matched_breadcrumbs.pop(0) # If map2 breadcrumb is no longer available, find a new nearest neighbor else: if (len(map2_breadcrumb_path.breadcrumbs) > 0): bearing_flag = 0 while bearing_flag >= 0: # find closest breadcrumb in map2. closest_map2_breadcrumb_id = list(map2_breadcrumb_path.breadcrumb_index.nearest((matched_breadcrumbs[0].map1_breadcrumb.longitude, matched_breadcrumbs[0].map1_breadcrumb.latitude), bearing_flag+1))[bearing_flag] bearing_difference = spatialfunclib.bearing_difference(map1_breadcrumb.bearing, map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id].bearing) if bearing_difference < bearing_limit: matched_breadcrumbs[0].map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id] matched_breadcrumbs[0].distance = self._distance(matched_breadcrumbs[0].map1_breadcrumb, matched_breadcrumbs[0].map2_breadcrumb) # sort list of matched breadcrumbs in ascending order according to distance # Sadly, very inefficient matched_breadcrumbs.sort(key=lambda x: x.distance) bearing_flag = -1 else: bearing_flag += 1 else: # Can't find a match map1_breadcrumb_path.delete_breadcrumb(map1_breadcrumb) # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") # iterate through remaining map1 breadcrumbs for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") #plt.scatter(map1_breadcrumb.latitude,map1_breadcrumb.longitude, c='#3FBFBF', s=2) # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") #plt.scatter(map2_breadcrumb.latitude,map2_breadcrumb.longitude, c='#BF3F3F', s=2) #eval_file.write("0,0,0,0,0\n") # flush results to file eval_file.flush() def _compare_breadcrumb_paths_greedy_shortest(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: # iterate through map1 breadcrumbs, find nearest neighbor for each, and store in matched_breadcrumbs # This computes a many-to-many matching if (len(map2_breadcrumb_path.breadcrumbs) > 0): for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # find closest breadcrumb in map2. Make sure there are some breadcrumbs left closest_map2_breadcrumb_id = list(map2_breadcrumb_path.breadcrumb_index.nearest((map1_breadcrumb.longitude, map1_breadcrumb.latitude), 1))[0] closest_map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id] breadcrumb_distance = self._distance(map1_breadcrumb, closest_map2_breadcrumb) # create matched crumbs object matched_breadcrumb = MatchedCrumbs(map1_breadcrumb, closest_map2_breadcrumb, breadcrumb_distance) matched_breadcrumbs.append(matched_breadcrumb) # Now filter this matching to create a 1-to-1 matching, prioritizing shortest distances # 1. Sort list of matched breadcrumbs in ascending order according to distance matched_breadcrumbs.sort(key=lambda x: x.distance) # 2. Continue while there are matched breadcrumbs to be processed while (len(matched_breadcrumbs) > 0): # if distance between closest matched breadcrumbs is greater than the threshold if (matched_breadcrumbs[0].distance > match_distance_threshold): # Exit loop early. All nearest neighbor matches are too far away. break # if map2 breadcrumb is still available if (matched_breadcrumbs[0].map2_breadcrumb.id in map2_breadcrumb_path.breadcrumbs.keys()): # Fix this matching. # remove map1 breadcrumb from path map1_breadcrumb_path.delete_breadcrumb(matched_breadcrumbs[0].map1_breadcrumb) # remove map2 breadcrumb from path map2_breadcrumb_path.delete_breadcrumb(matched_breadcrumbs[0].map2_breadcrumb) matched += 1 # output distance between breadcrumbs for evaluation output file eval_file.write(str(matched_breadcrumbs[0].distance) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.longitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.longitude) + "\n") matched_breadcrumbs.pop(0) # If map2 breadcrumb is no longer available, find a new nearest neighbor else: if (len(map2_breadcrumb_path.breadcrumbs) > 0): # find closest breadcrumb in map2. closest_map2_breadcrumb_id = list(map2_breadcrumb_path.breadcrumb_index.nearest((matched_breadcrumbs[0].map1_breadcrumb.longitude, matched_breadcrumbs[0].map1_breadcrumb.latitude), 1))[0] matched_breadcrumbs[0].map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id] matched_breadcrumbs[0].distance = self._distance(matched_breadcrumbs[0].map1_breadcrumb, matched_breadcrumbs[0].map2_breadcrumb) # sort list of matched breadcrumbs in ascending order according to distance # Sadly, very inefficient matched_breadcrumbs.sort(key=lambda x: x.distance) else: # Can't find a match map1_breadcrumb_path.delete_breadcrumb(map1_breadcrumb) # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") # iterate through remaining map1 breadcrumbs for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") #plt.scatter(map1_breadcrumb.latitude,map1_breadcrumb.longitude, c='#3FBFBF', s=2) # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") #plt.scatter(map2_breadcrumb.latitude,map2_breadcrumb.longitude, c='#BF3F3F', s=2) #eval_file.write("0,0,0,0,0\n") # flush results to file eval_file.flush() def _compare_breadcrumb_paths_greedy(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: # iterate through map1 breadcrumbs map1_breadcrumb = None while len(map1_breadcrumb_path.breadcrumbs.values())>0: map1_breadcrumb = list(map1_breadcrumb_path.breadcrumbs.items())[0][1] # Are there breadcrumbs left to map to? if(len(map2_breadcrumb_path.breadcrumbs) == 0): break # find closest breadcrumb in map2. Make sure there are some breadcrumbs left closest_map2_breadcrumb_id = list(map2_breadcrumb_path.breadcrumb_index.nearest((map1_breadcrumb.longitude, map1_breadcrumb.latitude), 1))[0] closest_map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[closest_map2_breadcrumb_id] breadcrumb_distance = self._distance(map1_breadcrumb, closest_map2_breadcrumb) # Nearest neighbor is too far away. Can't find a match if breadcrumb_distance > match_distance_threshold: map1_breadcrumb_path.delete_breadcrumb(map1_breadcrumb) # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") else: # create matched crumbs object # In this method, matched_breadcrumbs only contains one-to-one matched breadcrumbs matched_breadcrumb = MatchedCrumbs(map1_breadcrumb, closest_map2_breadcrumb, breadcrumb_distance) matched_breadcrumbs.append(matched_breadcrumb) map1_breadcrumb_path.delete_breadcrumb(map1_breadcrumb) map2_breadcrumb_path.delete_breadcrumb(closest_map2_breadcrumb) matched += 1 # output distance between breadcrumbs for evaluation output file eval_file.write(str(matched_breadcrumb.distance) + "," + str( matched_breadcrumb.map1_breadcrumb.latitude) + "," + str( matched_breadcrumb.map1_breadcrumb.longitude) + "," + str( matched_breadcrumb.map2_breadcrumb.latitude) + "," + str( matched_breadcrumb.map2_breadcrumb.longitude) + "\n") # iterate through remaining map1 breadcrumbs for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") #plt.scatter(map1_breadcrumb.latitude,map1_breadcrumb.longitude, c='#3FBFBF', s=2) # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") #plt.scatter(map2_breadcrumb.latitude,map2_breadcrumb.longitude, c='#BF3F3F', s=2) #eval_file.write("0,0,0,0,0\n") # flush results to file eval_file.flush() def _compare_breadcrumb_paths(self, map1_breadcrumb_path, map2_breadcrumb_path, eval_file_name): # storage for list of matched breadcrumbs matched_breadcrumbs = [] matched = 0 # DEBUG -- output number of breadcrumbs if (debug_mode): print("map1 breadcrumbs: " + str(len(map1_breadcrumb_path.breadcrumbs))) print("map2 breadcrumbs: " + str(len(map2_breadcrumb_path.breadcrumbs))) # append results to eval file with open_mkdir(eval_file_name, 'a+') as eval_file: # if there are some map2 breadcrumbs if (len(map2_breadcrumb_path.breadcrumbs) > 0): # iterate through map1 breadcrumbs for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # find closest breadcrumb in map2 # closest_map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[list(map2_breadcrumb_path.breadcrumb_index.nearest((map1_breadcrumb.longitude, map1_breadcrumb.latitude), 1))[0]] # find closest breadcrumb ids in map2 closest_map2_breadcrumb_ids = map2_breadcrumb_path.breadcrumb_index.nearest( (map1_breadcrumb.longitude, map1_breadcrumb.latitude), 10) # iterate through all breadcrumb ids for map2_breadcrumb_id in closest_map2_breadcrumb_ids: # grab map2 breadcrumb map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[map2_breadcrumb_id] # compute bearing difference between breadcrumbs bearing_difference = spatialfunclib.bearing_difference(map1_breadcrumb.bearing, map2_breadcrumb.bearing) # if bearing difference is less than 45 degrees if (bearing_difference < bearing_limit): # find distance to breadcrumb in map2 map2_breadcrumb_distance = self._distance(map1_breadcrumb, map2_breadcrumb) # create matched crumbs object matched_breadcrumbs.append( MatchedCrumbs(map1_breadcrumb, map2_breadcrumb, map2_breadcrumb_distance)) # exit loop break else: continue # sort list of matched breadcrumbs in ascending order according to distance matched_breadcrumbs.sort(key=lambda x: x.distance) # continue while there are matched breadcrumbs to be processed while (len(matched_breadcrumbs) > 0): # if distance between closest matched breadcrumbs is greater than the threshold if (matched_breadcrumbs[0].distance > match_distance_threshold): # exit loop early break # if map2 breadcrumb is still available if (matched_breadcrumbs[0].map2_breadcrumb.id in map2_breadcrumb_path.breadcrumbs.keys()): # remove map1 breadcrumb from path del map1_breadcrumb_path.breadcrumbs[matched_breadcrumbs[0].map1_breadcrumb.id] # remove map2 breadcrumb from path del map2_breadcrumb_path.breadcrumbs[matched_breadcrumbs[0].map2_breadcrumb.id] matched += 1 # output distance between breadcrumbs for evaluation output file eval_file.write(str(matched_breadcrumbs[0].distance) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map1_breadcrumb.longitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.latitude) + "," + str( matched_breadcrumbs[0].map2_breadcrumb.longitude) + "\n") #plt.scatter(matched_breadcrumbs[0].map1_breadcrumb.latitude,matched_breadcrumbs[0].map1_breadcrumb.longitude, c='blue', s=2) #plt.scatter(matched_breadcrumbs[0].map2_breadcrumb.latitude,matched_breadcrumbs[0].map2_breadcrumb.longitude, c='red', s=2) #plt.plot([matched_breadcrumbs[0].map1_breadcrumb.latitude,matched_breadcrumbs[0].map2_breadcrumb.latitude],[matched_breadcrumbs[0].map1_breadcrumb.longitude,matched_breadcrumbs[0].map2_breadcrumb.longitude], c='green') # remove matched breadcrumbs from list matched_breadcrumbs.pop(0) # else, if map2 breadcrumb is no longer available else: # set map2 matched breadcrumb to None matched_breadcrumbs[0].map2_breadcrumb = None # get list of all_closest breadcrumbs in map2 all_closest_map2_breadcrumbs = list(map2_breadcrumb_path.breadcrumb_index.nearest( (matched_breadcrumbs[0].map1_breadcrumb.longitude, matched_breadcrumbs[0].map1_breadcrumb.latitude), 10)) # iterate through list of all closest breadcrumbs in map2 for map2_breadcrumb_id in all_closest_map2_breadcrumbs: # if map2 breadcrumb is still available if (map2_breadcrumb_id in map2_breadcrumb_path.breadcrumbs.keys()): # grab map2 breadcrumb map2_breadcrumb = map2_breadcrumb_path.breadcrumbs[map2_breadcrumb_id] # compute bearing difference between breadcrumbs bearing_difference = spatialfunclib.bearing_difference( matched_breadcrumbs[0].map1_breadcrumb.bearing, map2_breadcrumb.bearing) # if bearing difference is less than 45 degrees if (bearing_difference < bearing_limit): # update map2 breadcrumb in matched crumbs object matched_breadcrumbs[0].map2_breadcrumb = map2_breadcrumb # update distance in matched crumbs object matched_breadcrumbs[0].distance = self._distance(matched_breadcrumbs[0].map1_breadcrumb, matched_breadcrumbs[0].map2_breadcrumb) # break out of loop break # if there were no map2 breadcrumbs to match with if (matched_breadcrumbs[0].map2_breadcrumb is None): # remove matched breadcrumbs from list matched_breadcrumbs.pop(0) # otherwise, ... else: # sort list of matched breadcrumbs matched_breadcrumbs.sort(key=lambda x: x.distance) # iterate through remaining map1 breadcrumbs for map1_breadcrumb in map1_breadcrumb_path.breadcrumbs.values(): # output map1 unmatched edge value eval_file.write( "1000000" + "," + str(map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "," + str( map1_breadcrumb.latitude) + "," + str(map1_breadcrumb.longitude) + "\n") #plt.scatter(map1_breadcrumb.latitude,map1_breadcrumb.longitude, c='#3FBFBF', s=2) # iterate through remaining map2 breadcrumbs for map2_breadcrumb in map2_breadcrumb_path.breadcrumbs.values(): # output map2 spurious edge value eval_file.write( "2000000" + "," + str(map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "," + str( map2_breadcrumb.latitude) + "," + str(map2_breadcrumb.longitude) + "\n") #plt.scatter(map2_breadcrumb.latitude,map2_breadcrumb.longitude, c='#BF3F3F', s=2) #eval_file.write("0,0,0,0,0\n") # flush results to file eval_file.flush() def _get_breadcrumb_path(self, curr_map, root_location, breadcrumb_edges, valid_edges, valid_edge_pairs): # iterate through all edges in the current map for edge in curr_map.edges.values(): # intialize breadcrumb storage for edge edge.breadcrumbs = [] # create storage for breadcrumb path breadcrumb_path = BreadcrumbPath() # initialize breadcrumb id breadcrumb_id = 0 # iterate through all breadcrumb edges for breadcrumb_edge in breadcrumb_edges: # if valid edges list is supplied, and the current breadcrumb edge is not on it if ((valid_edges is not None) and (breadcrumb_edge[0] not in valid_edges)): continue # create breadcrumb path starting with current breadcrumb edge (breadcrumb_path, breadcrumb_id) = self._create_breadcrumb_path(curr_map, root_location, breadcrumb_edge[0], breadcrumb_edge[1], valid_edge_pairs, breadcrumb_path, breadcrumb_id) # return breadcrumb path return breadcrumb_path def _create_breadcrumb_path(self, curr_map, root_location, source_edge, source_location, valid_edge_pairs, breadcrumb_path, breadcrumb_id): # initialize start distance for breadcrumbing #source_edge.start_distance = self._distance_coords(source_edge.in_node.latitude, source_edge.in_node.longitude,source_location[0], source_location[1]) distance_to_source_location = self._distance_coords(source_edge.in_node.latitude, source_edge.in_node.longitude, source_location[0], source_location[1]) if(distance_to_source_location > breadcrumb_max_distance): source_edge.start_distance = distance_to_source_location - breadcrumb_max_distance else: source_edge.start_distance = 0 #source_edge.start_distance = 0 # initialize maximum distance for breadcrumbing source_edge.max_distance = 0.0 # create breadth-first edge queue for searching map bfs_queue = [] # enqueue source edge bfs_queue.append(source_edge) # append edges adjacent to the beginning of the source edge reciprocal_edge = curr_map.edge_lookup_table[(source_edge.out_node, source_edge.in_node)] dist_from_root = self._distance_coords(root_location[0], root_location[1], reciprocal_edge.out_node.latitude, reciprocal_edge.out_node.longitude) if dist_from_root < breadcrumb_max_distance: for out_edge in self._find_valid_out_edges(curr_map, reciprocal_edge): out_edge.start_distance = breadcrumb_interval out_edge.max_distance = 0.0 bfs_queue.append(out_edge) # while the edge queue is not empty while (len(bfs_queue) > 0): # dequeue first edge in the queue curr_edge = bfs_queue.pop(0) # if the current edge has no length if (curr_edge.length == 0.0): if ((valid_edge_pairs is not None) and (curr_edge in valid_edge_pairs)): for out_edge in valid_edge_pairs[curr_edge]: if (out_edge not in bfs_queue): out_edge.start_distance = curr_edge.start_distance out_edge.max_distance = curr_edge.max_distance bfs_queue.append(out_edge) else: for out_edge in self._find_valid_out_edges(curr_map, curr_edge): if (out_edge not in bfs_queue): out_edge.start_distance = curr_edge.start_distance out_edge.max_distance = curr_edge.max_distance bfs_queue.append(out_edge) # continue to next edge in queue continue # determine breadcrumb start location #breadcrumb_start_location = self._point_along_line(curr_edge.in_node, curr_edge.out_node,(curr_edge.start_distance / curr_edge.length)) # see if there are any breadcrumbs nearby on the current edge #closest_breadcrumb_distance = self._find_closest_breadcrumb_distance(curr_edge, breadcrumb_start_location) # if the closest breadcrumb is further than (breadcrumb_interval + 1.0 meters) away #if (closest_breadcrumb_distance > (breadcrumb_interval + 1.0)): #if (len(curr_edge.breadcrumbs) == 0): if (not curr_edge.visited): curr_edge.visited = True # grab reciprocal edge reciprocal_edge = curr_map.edge_lookup_table[(curr_edge.out_node, curr_edge.in_node)] reciprocal_edge.visited = True # drop breadcrumbs along current edge (breadcrumb_id, breadcrumb_start_distance, breadcrumb_max_distance_from_source) = self._drop_breadcrumbs_along_edge(curr_edge, breadcrumb_path, breadcrumb_id, root_location) # if we should breadcrumb the successor edges if (breadcrumb_start_distance is not None): if ((valid_edge_pairs is not None) and (curr_edge in valid_edge_pairs)): for out_edge in valid_edge_pairs[curr_edge]: if (out_edge not in bfs_queue): out_edge.start_distance = breadcrumb_start_distance out_edge.max_distance = breadcrumb_max_distance_from_source bfs_queue.append(out_edge) else: for out_edge in self._find_valid_out_edges(curr_map, curr_edge): if (out_edge not in bfs_queue): out_edge.start_distance = breadcrumb_start_distance out_edge.max_distance = breadcrumb_max_distance_from_source bfs_queue.append(out_edge) # return breadcrumb path and id return (breadcrumb_path, breadcrumb_id) def _find_closest_breadcrumb_distance(self, edge, location): # storage for closest breadcrumb closest_breadcrumb_distance = float('infinity') # if the edge has breadcrumbs if (len(edge.breadcrumbs) > 0): # iterate through all breadcrumbs for curr_breadcrumb in edge.breadcrumbs: # compute distance to current breadcrumb curr_breadcrumb_distance = self._distance_coords(location[0], location[1], curr_breadcrumb.latitude, curr_breadcrumb.longitude) # if the current breadcrumb is closer than any seen before if (curr_breadcrumb_distance < closest_breadcrumb_distance): # store current breadcrumb as closest closest_breadcrumb_distance = curr_breadcrumb_distance # return closest breadcrumb distance return closest_breadcrumb_distance def _drop_breadcrumbs_along_edge(self, curr_edge, breadcrumb_path, breadcrumb_id, root_location): # initialize current distance along edge curr_distance_along_edge = curr_edge.start_distance # initialize maximum breadcrumb distance from root location breadcrumb_max_distance_from_root = curr_edge.max_distance # initialize previous breadcrumb distance. This will tell us if we are approaching the "allowed" circle. previous_breadcrumb_distance_from_root = float('infinity') # drop breadcrumbs along current edge while (curr_distance_along_edge <= curr_edge.length): # determine point along current edge to drop breadcrumb breadcrumb_location = self._point_along_line(curr_edge.in_node, curr_edge.out_node, (curr_distance_along_edge / curr_edge.length)) # determine breadcrumb distance from root location breadcrumb_distance_from_root = self._distance_coords(root_location[0], root_location[1], breadcrumb_location[0], breadcrumb_location[1]) # if the current breadcrumb distance exceeds the limit, or is closer to the root than the furthest placed breadcrumb if ((breadcrumb_distance_from_root > breadcrumb_max_distance) and (previous_breadcrumb_distance_from_root < breadcrumb_distance_from_root)): # return updated breadcrumb id and None start and maximum distance for child edges return (breadcrumb_id, None, None) # if the current breadcrumb distance exceeds the limit BUT we are getting closer to the allowed circle by the root location elif ((breadcrumb_distance_from_root > breadcrumb_max_distance) and (previous_breadcrumb_distance_from_root > breadcrumb_distance_from_root)): # do not place breadcrumb but increase the curr_distance_along_edge. We are getting closer to the "allowed" area and we may eventually drop one! # update previous_breadcrumb_distance_from_root previous_breadcrumb_distance_from_root = breadcrumb_distance_from_root # increment current distance along edge curr_distance_along_edge += breadcrumb_interval continue # create new breadcrumb new_breadcrumb = Breadcrumb(breadcrumb_id, breadcrumb_location[0], breadcrumb_location[1], curr_edge.bearing) # increment breadcrumb id breadcrumb_id += 1 # add breadcrumb to path breadcrumb_path.add_breadcrumb(new_breadcrumb) # add breadcrumb to current edge curr_edge.breadcrumbs.append(new_breadcrumb) # increment current distance along edge curr_distance_along_edge += breadcrumb_interval # if the current breadcrumb distance from source location is greater than the maximum seen so far if (breadcrumb_distance_from_root > breadcrumb_max_distance_from_root): # update maximum breadcrumb distance from source location breadcrumb_max_distance_from_root = breadcrumb_distance_from_root # compute breadcrumb start distance for next edge breadcrumb_start_distance = (curr_distance_along_edge - curr_edge.length) # return updated breadcrumb id, start and maximum distance return (breadcrumb_id, breadcrumb_start_distance, breadcrumb_max_distance_from_root) def _find_valid_out_edges(self, curr_map, curr_edge): # storage for valid out edges valid_out_edges = curr_edge.out_edges # if the current edge has a reciprocal edge if ((curr_edge.out_node, curr_edge.in_node) in curr_map.edge_lookup_table): # grab reciprocal edge reciprocal_edge = curr_map.edge_lookup_table[(curr_edge.out_node, curr_edge.in_node)] # if the reciprocal edge is in the valid out edges list if (reciprocal_edge in valid_out_edges): # remove it from the valid out edges list valid_out_edges.remove(reciprocal_edge) # return valid out edges return valid_out_edges def _get_breadcrumb_edges(self, curr_map, root_location, match_distance_threshold): # storage for breadcrumb edges breadcrumb_edges = [] # iterate through closest map edge ids for curr_map_edge_id in curr_map.edge_spatial_index.nearest((root_location[1], root_location[0]), 1): # grab current map edge curr_map_edge = curr_map.edges[curr_map_edge_id] # project root location onto current map edge (projected_point, projection_fraction, projection_distance) = self._projection_onto_line_coords( curr_map_edge.in_node.latitude, curr_map_edge.in_node.longitude, curr_map_edge.out_node.latitude, curr_map_edge.out_node.longitude, root_location[0], root_location[1]) # if projection is beyond the out node if (projection_fraction >= 1.0): # set current map location to the edge out node curr_map_edge_location = [curr_map_edge.out_node.latitude, curr_map_edge.out_node.longitude] # else, if the projection is beyond the in node elif (projection_fraction <= 0.0): # set current map2 location to the edge in node curr_map_edge_location = [curr_map_edge.in_node.latitude, curr_map_edge.in_node.longitude] # else, if the projection is onto the line else: # set current map2 location to projected point curr_map_edge_location = [projected_point[0], projected_point[1]] # determine distance to current map location curr_map_edge_distance = self._distance_coords(root_location[0], root_location[1], curr_map_edge_location[0], curr_map_edge_location[1]) # store current edge and its distance in breadcrumb edges list breadcrumb_edges.append((curr_map_edge, curr_map_edge_location, curr_map_edge_distance)) # filter out breadcrumb edges with distance greater than the match distance threshold breadcrumb_edges = list(filter(lambda x: x[2] <= match_distance_threshold, breadcrumb_edges)) # sort remaining breadcrumb edges by distance breadcrumb_edges.sort(key=lambda x: x[2]) # return sorted breadcrumb edges return breadcrumb_edges def _select_map_root_location(self, map_valid_edges): # get map1 ordered edges and distances (map_ordered_edges, map_ordered_edge_distances) = self._get_ordered_edges_and_distances(map_valid_edges) # determine maximum distance along ordered edges max_distance = map_ordered_edges[-1].end_distance # select a distance along ordered edges distance_along = (1/2 * max_distance) # find closest key in distance lookup table closest_key = min((abs(distance_along - i), i) for i in map_ordered_edge_distances)[1] # grab candidate edges candidate_edges = map_ordered_edge_distances[closest_key] # storage for selected map1 edge map_edge = None # iterate through candidate edges for candidate_edge in candidate_edges: # if selected distance along falls on the edge if ((distance_along >= candidate_edge.start_distance) and (distance_along <= candidate_edge.end_distance)): # we have found our map1 edge map_edge = candidate_edge # and we're done! break # determine the fraction along the edge if map_edge.length == 0: fraction_along = 0 else: fraction_along = ((distance_along - map_edge.start_distance) / map_edge.length) # determine the location along the edge map_location = self._point_along_line(map_edge.in_node, map_edge.out_node, fraction_along) # return map1 root location return map_location def _get_ordered_edges_and_distances(self, valid_edges): # list storage for ordered edges ordered_edges = [] # storage for cumulative distance cumulative_distance = 0.0 # iterate through valid edges for valid_edge in valid_edges: # add start distance attribute to edge valid_edge.start_distance = cumulative_distance # add edge length to cumulative distance cumulative_distance += valid_edge.length # add end distance attribute to edge valid_edge.end_distance = cumulative_distance # add edge to ordered edges list ordered_edges.append(valid_edge) # storage for ordered edge distances ordered_edge_distances = {} # iterate through valid edges for valid_edge in valid_edges: # if start distance bucket does not exist if (valid_edge.start_distance not in ordered_edge_distances): # create bucket ordered_edge_distances[valid_edge.start_distance] = [] # add edge to bucket ordered_edge_distances[valid_edge.start_distance].append(valid_edge) # if end distance bucket does not exist if (valid_edge.end_distance not in ordered_edge_distances): # create bucket ordered_edge_distances[valid_edge.end_distance] = [] # add edge to bucket ordered_edge_distances[valid_edge.end_distance].append(valid_edge) # return ordered edges and distances return (ordered_edges, ordered_edge_distances) def _write_breadcrumb_path_to_file(self, breadcrumb_path, breadcrumb_path_filename="breadcrumb_path.txt"): # output that we are starting the writing process sys.stdout.write("\nWriting breadcrumb path to file " + str(breadcrumb_path_filename) + "... ") sys.stdout.flush() # open breadcrumb path file breadcrumb_path_file = open(breadcrumb_path_filename, 'w') # iterate through all breadcrumbs on path for curr_breadcrumb in breadcrumb_path.breadcrumbs.values(): # output current breadcrumb to file breadcrumb_path_file.write(str(curr_breadcrumb.latitude) + "," + str(curr_breadcrumb.longitude) + "," + str( curr_breadcrumb.id) + "\n") # close breadcrumb path file breadcrumb_path_file.close() print("done.") def _projection_onto_line(self, location1, location2, location3): return spatialfunclib.projection_onto_line(location1.latitude, location1.longitude, location2.latitude, location2.longitude, location3.latitude, location3.longitude) def _projection_onto_line_coords(self, location1_latitude, location1_longitude, location2_latitude, location2_longitude, location3_latitude, location3_longitude): return spatialfunclib.projection_onto_line(location1_latitude, location1_longitude, location2_latitude, location2_longitude, location3_latitude, location3_longitude) def _point_along_line(self, location1, location2, fraction_along): return spatialfunclib.point_along_line(location1.latitude, location1.longitude, location2.latitude, location2.longitude, fraction_along) def _path_bearing(self, location1, location2): return spatialfunclib.path_bearing(location1.latitude, location1.longitude, location2.latitude, location2.longitude) def _distance(self, location1, location2): return spatialfunclib.euclideandistance(location1.latitude, location1.longitude, location2.latitude, location2.longitude) def _distance_coords(self, location1_latitude, location1_longitude, location2_latitude, location2_longitude): return spatialfunclib.euclideandistance(location1_latitude, location1_longitude, location2_latitude, location2_longitude) def _fast_distance(self, location1, location2): return spatialfunclib.euclideandistance(location1.latitude, location1.longitude, location2.latitude, location2.longitude) def open_mkdir(filename, *args, **kwargs): """ Open a file, creating directories if necessary. Wraps the default python 'open' function """ dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) return open(filename, *args, **kwargs) if __name__ == '__main__': help_string = ("Usage: python graphsampling.py <data_folder> <dataset name> <solution name> <mode>" "[-i <breadcrumb_interval>] " "[-b <bearing_limit>] " "[-c <compare_mode>] " "[-t <match_distance_threshold>] " "[-f] [-h]\n") if len(sys.argv) < 4: print(help_string) exit() (opts, args) = getopt.getopt(sys.argv[5:], "i:b:t:c:fh") force_computation = False for o, a in opts: if o == "-i": breadcrumb_interval = float(a) if o == "-b": bearing_limit = float(a) if o == "-t": match_distance_threshold = float(a) if o == "-c": compare_mode = str(a) if o == "-f": force_computation = True if o == "-h": print(help_string) exit() start_time = time.time() data_path = sys.argv[1].strip("/\\") dataset = sys.argv[2] solution = sys.argv[3] compute_mode = sys.argv[4] print("[MapCompare] Comparing {} against ground-truth on the {} dataset.\n".format(solution, dataset)) f_data = "{folder}/{dataset}".format(folder=data_path, dataset=dataset) f_ground_truth = "{folder}/groundtruth/{dataset}_{{type}}.txt".format(folder=f_data, dataset=dataset) f_dataset = "{folder}/algorithm/{algorithm}/{dataset}_{algorithm}_{{type}}.txt".format(folder=f_data, dataset=dataset, algorithm=solution) f_results = "{folder}/evals/{dataset}_{{file}}.txt".format(folder=f_data, dataset=dataset) m = MapCompare() has_ground_truth, has_solution = m.load_maps(f_ground_truth, f_dataset) #valid_edges = f_ground_truth.format(type=valid_edge_file) #has_ground_truth = has_ground_truth and os.path.isfile(valid_edges) if "l" in compute_mode.lower(): if has_ground_truth: if force_computation or input("Please confirm you want to compute and override root locations on {}. " "(Y/n) ".format(dataset)).lower() == "y": m.generate_root_locations(f_results.format(file=solution + "_rootlocations")) else: print("Tried to generate root locations without complete ground truth", file=sys.stderr, flush=True) if "c" in compute_mode.lower(): if has_ground_truth and has_solution: if force_computation or input("Please confirm you want to compare {} to {} and overwrite previous results. " "(Y/n) ".format(solution, dataset)).lower() == "y": root_locations = f_results.format(file=solution + "_rootlocations") if os.path.isfile(root_locations): m.compare_maps(f_results.format(file=solution + "_" + compare_mode + "{t:.0f}".format(t=match_distance_threshold)), root_locations) else: print("Tried to compare maps without root location file", file=sys.stderr, flush=True) else: print("Tried to compare {}ground truth against {}solution\n".format('' if has_ground_truth else 'a missing ', '' if has_solution else 'a missing '), file=sys.stderr, flush=True) if "r" in compute_mode.lower(): m.compute_results(f_results.format(file=solution + "_" + compare_mode + "{t:.0f}".format(t=match_distance_threshold))) print("[MapCompare] Operations completed in {sec:.0f} seconds.\n".format(sec=time.time() - start_time))