def plot_route(map_components, route_ends, segments, path_indices): """ Plot a route on top of a (already) plotted map Inputs: map_components is a pd.DataFrame with the raw NYC data route_ends is a list of 2 str with the form lon_lat each segments is a pd.DataFrame with the processed segments path indices is a list of the rows in segments that are part of the route """ # plotting the map subplot_axes = plot_raw_data(map_components) # , # xlim=[-73.985, -73.955], # ylim=[40.760, 40.785]) # plots starting and end point lon_start, lat_start = names.name_to_lon_lat(route_ends[0]) pl.plot(lon_start, lat_start, 's', color='#ff33cc', markersize=12) lon_end, lat_end = names.name_to_lon_lat(route_ends[1]) pl.plot(lon_end, lat_end, 'o', color='#ff33cc', markersize=12) # plotting the route if path_indices is not None: segments_gpd = gpd.GeoDataFrame(segments) route = segments_gpd.iloc[np.array(path_indices)] route.plot(ax=subplot_axes, color='k', linewidth=4) pl.savefig('path_run.png')
def select_random_point_pairs(segments, target_distance, n_pairs=1): """ Fill me """ # list of possible vertexes vertex_list = [ v for i, v in enumerate(segments['vertex_start'].values) if segments['type'].iloc[i] == 'street' ] vertex_list = [ v for i, v in enumerate(segments['vertex_end'].values) if segments['type'].iloc[i] == 'street' ] all_vertex = set(vertex_list) all_vertex = np.array([i for i in all_vertex]) # select n_pairs random indexes start_indexes = np.floor(np.random.rand(n_pairs) * len(all_vertex)) start_indexes = start_indexes.astype(int) # find the points corresponding to the indices start_points = all_vertex[start_indexes] # getting lon and lat for all vertex all_vertex_lon_lat = {'lon': [], 'lat': []} for index, vertex in enumerate(all_vertex): lon, lat = names.name_to_lon_lat(vertex) all_vertex_lon_lat['lon'].append(lon) all_vertex_lon_lat['lat'].append(lat) all_vertex_lon_lat['lon'] = np.array(all_vertex_lon_lat['lon']) all_vertex_lon_lat['lat'] = np.array(all_vertex_lon_lat['lat']) end_points = [] for start_point in start_points: lon, lat = names.name_to_lon_lat(start_point) ang_dist_to_pt = angles.ang_dist(lon, lat, all_vertex_lon_lat['lon'], all_vertex_lon_lat['lat']) dist_to_pt = angles.convert_distance_to_physical(ang_dist_to_pt, 'km') valid = np.logical_and(dist_to_pt > 0., dist_to_pt < target_distance) index = int(np.floor(np.random.rand(1) * valid.sum())[0]) end_points.append(all_vertex[valid][index]) # points pts = [] for i in range(n_pairs): pts.append((start_points[i], end_points[i])) return pts
def select_random_point(segments, start_point, target_distance, units): """ Select a random point in segments withing a distance Input: segments is a pd.DataFrame with all the segments to consider start_point is a string 'lon_lat' that we are centered on target_distance is a radius to consider, the point must be inside Output: end_point is the string 'lon_lat' that was selected """ # list of possible vertexes vertex_list = [ v for i, v in enumerate(segments['vertex_start'].values) if segments['type'].iloc[i] == 'street' ] vertex_list = [ v for i, v in enumerate(segments['vertex_end'].values) if segments['type'].iloc[i] == 'street' ] all_vertex = set(vertex_list) all_vertex = np.array([i for i in all_vertex]) # getting lon and lat for all vertex all_vertex_lon_lat = {'lon': [], 'lat': []} for index, vertex in enumerate(all_vertex): lon, lat = names.name_to_lon_lat(vertex) all_vertex_lon_lat['lon'].append(lon) all_vertex_lon_lat['lat'].append(lat) all_vertex_lon_lat['lon'] = np.array(all_vertex_lon_lat['lon']) all_vertex_lon_lat['lat'] = np.array(all_vertex_lon_lat['lat']) # randmomly selectend point lon, lat = names.name_to_lon_lat(start_point) ang_dist_to_pt = angles.ang_dist(lon, lat, all_vertex_lon_lat['lon'], all_vertex_lon_lat['lat']) dist_to_pt = angles.convert_distance_to_physical(ang_dist_to_pt, units) valid = np.logical_and(dist_to_pt > 0., dist_to_pt < target_distance) index = int(np.floor(np.random.rand(1) * valid.sum())[0]) end_point = all_vertex[valid][index] return end_point
def get_closest_point_to(lon0, lat0, intersection_list): """ from a list of intersections (['lon_lat', ...]), returns the closest point to (lon, lat) Assumes all angles in degrees """ locations = {'lon': [], 'lat': [], 'd': []} for name_ in intersection_list: lon, lat = names.name_to_lon_lat(name_) locations['lon'].append(lon) locations['lat'].append(lat) locations['d'].append( angles.ang_dist(angles.deg_to_rad(lon0), angles.deg_to_rad(lat0), angles.deg_to_rad(lon), angles.deg_to_rad(lat))) index_closest_point = np.argmin(np.array(locations['d'])) return intersection_list[index_closest_point]
def run(self, route_ends, target_dist, units='km', algorithm_type='dijkstra', cost_weights=None): """ """ segments = self.data_hand.load_processed_data() # list of all possible intersections intersection_names = list(segments['vertex_start'].values) intersection_names += list(segments['vertex_end'].values) intersection_names = list(set(intersection_names)) # get closest intersection to provided points start_point_lon, start_point_lat = names.name_to_lon_lat(route_ends[0]) end_point_lon, end_point_lat = names.name_to_lon_lat(route_ends[1]) start_point = locations.get_closest_point_to(start_point_lon, start_point_lat, intersection_names) end_point = locations.get_closest_point_to(end_point_lon, end_point_lat, intersection_names) route_ends = [start_point, end_point] print("Optimizing route from {0:s} to {1:s}".format( start_point, end_point)) # load data for plotting dfs = {} processed_path = os.path.join(self.running_heaven_path, 'data', 'processed') for key_ in ['park', 'street', 'sidewalk']: geojson_file_name = '{0:s}.geojson'.format(key_) dfs[key_] = gpd.read_file( os.path.join(processed_path, geojson_file_name)) dfs['tree'] = pd.read_csv(os.path.join(processed_path, 'tree.csv')) # updating dataframe segments['tree_density_weight'] = 1. - segments['tree_density'] target_dist_deg = angles.convert_distance_to_degree(target_dist, units) park_weight = self.define_park_weight( segments, # dfs['park'], target_dist_deg) segments['park_weight'] = park_weight # distribution of features for debugging # self.feature_distributions(tree_density_norm, park_weight) # linear programming solution if algorithm_type == 'integer_programming': path_indices, d_path = self.integer_programming( segments, units, (start_point, end_point), target_dist) elif algorithm_type == 'dijkstra': # problem information for Dijkstra's algorithm # add the segments in the reverse direction as well edges = [] segments_copy = copy.deepcopy(segments) vertex_start = copy.deepcopy(segments_copy['vertex_start']) vertex_end = copy.deepcopy(segments_copy['vertex_end']) segments_copy['vertex_start'] = vertex_end segments_copy['vertex_end'] = vertex_start segments = segments.append(segments_copy) segments.reset_index(drop=True, inplace=True) for index in segments.index.astype(int): # distance - shortest path tree_weight = segments['tree_density_weight'].iloc[index] park_weight = segments['park_weight'].iloc[index] intersection = int(segments['type'].iloc[index] == 'street') segment_info = { 'distance': segments['distance'].iloc[index], 'tree_density_weight': tree_weight, 'park_weight': park_weight, 'intersection': intersection } # (starting point, end point, cost, segment_info edges.append( (segments['vertex_start'].iloc[index], segments['vertex_end'].iloc[index], 0., segment_info)) # try different cost term weights choices = [0.1, 10.] weights = [ np.array(p) for p in itertools.product(choices, repeat=5) ] # set fixed values if provides # distance, spiral, tree, park, intersection if cost_weights is not None: fixed = ~np.isnan(cost_weights) for weight in weights: weight[fixed] = np.array(cost_weights)[fixed] weights = [list(i) for i in weights] # remove duplicates for i in range(len(weights) - 1, -1, -1): if weights.count(weights[i]) > 1: weights.pop(i) # iterate on the different weights path_indices_list = [] d_path_list = [] cost_list = [] for weight in weights: opt_path = self.dijkstra(edges, start_point, end_point, target_dist_deg, weight) if opt_path[0] == 0.: print('Warning: impossible route') return None, None, None # get indices from path path_indices, d_path = self.get_indices_from_path( opt_path, start_point, segments) d_path = angles.convert_distance_to_physical(d_path, units) path_indices_list.append(path_indices) d_path_list.append(d_path) cost_list.append(opt_path[0]) # print(weight, cost_list[-1], d_path) distance_difference = abs(np.array(d_path_list) - target_dist) index_closest_distance = np.argmin(distance_difference) d_path = d_path_list[index_closest_distance] path_indices = path_indices_list[index_closest_distance] else: exit('Analysis types 1 and 2 defined so far.') # plotting the data and route if self.show: map_plotter.plot_route(dfs, route_ends, segments, path_indices) # resulting distance print('Total distance is : {0:f} {1:s}'.format(d_path, units)) print('Taget distance was: {0:f} {1:s}'.format(target_dist, units)) if self.show: pl.show() route_lon_lat = self.get_route(segments, path_indices, start_point, end_point) return d_path, route_lon_lat, segments.iloc[path_indices]
def update_costs(self, object_, target_d, d_done, current_point, end_point, weight): """ updates the costs in the defaultdict object_ is g in the dijkstra function target_d is the length of the intended run d_done is the dictionary of lengths so far for the routes cost function: ((d_left - d0) / d0)^2 * cos(theta_between_pt_and end) ( 1 - (d_left - d0) / d0)^2 * tree_density_normalized Right now, sets all to 0 """ lon_end, lat_end = names.name_to_lon_lat(end_point) lon_current, lat_current = names.name_to_lon_lat(current_point) theta_end = locations.get_angle(lon_current, lat_current, lon_end, lat_end) for key_ in [current_point]: for i in range(len(object_[key_])): temp = list(object_[key_][i]) lon, lat = names.name_to_lon_lat(object_[key_][i][1]) # cost function dist_ran = temp[2]['distance'] + d_done[current_point] theta_pt = locations.get_angle(lon_current, lat_current, lon, lat) theta_diff = theta_end - theta_pt d_direct = angles.ang_dist(lon_current, lat_current, lon, lat) dist_left = target_d - dist_ran # cost function increases as run ends and is directed towards # the end point, has lower cost towards the end point # r_factor should be between 0 (start, dist_ran=0) and # 1 (end, dist_ran=target_d) if dist_ran < target_d: dist_frac = 0.5 if dist_ran < dist_frac * target_d: r_factor = (dist_ran / (dist_frac * target_d / 2.)) else: r_factor = 1. # no cost as long as we have not reached the proper length if dist_left > d_direct: cost_dist = 0. else: cost_dist = r_factor**2 cost_dist *= ((1. + np.cos(np.pi + theta_diff)) / 2.)**2 # spiral term when far cost_dist2 = (1. - r_factor)**2 # cost_dist2 *= ((1. + np.cos(2.*theta_diff))/2.)**2 cost_dist2 *= ((1. + np.cos(theta_diff)) / 2.)**2 # tree weight cost_tree = (1. - r_factor)**2 cost_tree *= (temp[2]['tree_density_weight'])**2 # less cost for routes towards parks cost_park = (1. - r_factor)**2 * temp[2]['park_weight']**2 # cost_intersection = (1. - r_factor)**2 * # temp[2]['intersection']**2 cost_intersection = temp[2]['intersection']**2 cost_terms = [ weight[0] * cost_dist, weight[1] * cost_dist2, weight[2] * cost_tree, weight[3] * cost_park, weight[4] * cost_intersection ] temp[0] = copy.deepcopy(np.sum(cost_terms)) object_[key_][i] = temp return object_