def extrapolate_number_of_lanes(path_data, paths): """ Add missing lane data to a path based on the previous path of the same street. Assuming that the number of the trunk lanes are same in the path coming out of the intersection. :param path_data: dictionary :param paths: list of dictionaries :return: None """ if 'tags' in path_data \ and 'lanes' not in path_data['tags'] \ and 'turn:lanes' not in path_data['tags']\ and 'direction' in path_data['tags']\ and path_data['tags']['direction'] == 'from_intersection'\ and 'bearing' in path_data \ and path_data['bearing'] is not None \ and len(path_data['nodes']) > 0\ and 'name' in path_data['tags']: prev_path = [ p for p in paths if 'name' in p['tags'] and p['tags']['name'] == path_data['tags'] ['name'] and ('turn:lanes' in p['tags'] or 'lanes' in p['tags']) and 'bearing' in p and p['bearing'] is not None and abs(get_angle_between_bearings(p['bearing'], path_data['bearing'])) < 60.0 and len(p['nodes']) > 0 and p['nodes'][-1] == path_data['nodes'][0] ] if len(prev_path) == 1: num_of_left_lanes, num_of_right_lanes, num_of_trunk_lanes = count_lanes( prev_path[0]) path_data['tags']['lanes'] = num_of_trunk_lanes path_data['tags']['corrected'] = 'yes' path_data['tags']['correction_source_id'] = prev_path[0]['id']
def shorten_border_for_crosswalk(input_border, street_name, lanes, crosswalk_width=10, destination='from_intersection', exclude_links=True, exclude_parallel=True): """ Remove the portion of the input border overlapping with any crosswalk crossing the input border. Scan all lanes with street names other than the street the input border belongs to, and identify crosswalks related to each lane. :param input_border: list of coordinates :param street_name: string :param lanes: list of dictionaries :param crosswalk_width: float :param destination: string :param exclude_links: True if exclude links, False otherwise :return: list of coordinates """ border = copy.deepcopy(input_border) if destination == 'from_intersection': multi_string_index = -1 else: multi_string_index = 0 for l in lanes: if l['name'] == 'no_name' or l['name'] == street_name: continue if exclude_links and 'link' in l['name']: continue if 'median' in l: border_type = 'median' else: border_type = 'left_border' bearing_delta = abs( get_angle_between_bearings( get_compass(l[border_type][-2], l[border_type][-1]), get_compass(input_border[0], input_border[-1]))) if bearing_delta > 90.0: bearing_delta = (180.0 - bearing_delta) % 180.0 if bearing_delta < 30.0: if exclude_parallel: logger.debug( "Processing %s, excluding %s for shortening: almost parallel %r" % (street_name, l['name'], bearing_delta)) continue lb, rb = add_space_for_crosswalk(l, crosswalk_width=crosswalk_width) coord = lb + rb[::-1] polygon = geom.Polygon(coord) temp = cut_border_by_polygon(border, polygon, multi_string_index) if temp is not None: border = temp border = drop_small_edges(border) return border
def get_destination_lane(lane_data, all_lanes, min_len=21.0): """ Get destination lane for through driving :param lane_data: dictionary :param all_lanes: list of dictionaries :return: dictionary """ # Try common node first res = [l for l in all_lanes if lane_data['nodes'][-1] == l['nodes'][0] and lane_data['lane_id'] == l['lane_id'] and l['direction'] == 'from_intersection' and - 30.0 < get_angle_between_bearings(lane_data['bearing'], l['bearing']) < 30.0 and ("length" not in l or l["length"] > min_len) ] if len(res) > 0: return res[0] # Try same street name res = [l for l in all_lanes if lane_data['name'] == l['name'] and lane_data['lane_id'] == l['lane_id'] and l['direction'] == 'from_intersection' and - 60.0 < get_angle_between_bearings(lane_data['bearing'], l['bearing']) < 60.0 and get_distance_between_points(lane_data['median'][-1], l['median'][0]) < 15.0 and ("length" not in l or l["length"] > min_len) ] if len(res) > 0: return res[0] # Try all other possible options res = [l for l in all_lanes if -30.0 < get_angle_between_bearings(lane_data['bearing'], l['bearing']) < 30.0 and int(lane_data['lane_id'][0]) - 1 == get_lane_index_from_right(l) and l['direction'] == 'from_intersection' and get_distance_between_points(lane_data['median'][-1], l['median'][0]) < 10.0 and ("length" not in l or l["length"] > min_len) ] if len(res) > 0: return res[0] return None
def get_most_left_lane(lanes, name, direction, bearing): """ Get the most left lane for a street for the specified direction :param lanes: list of lane dictionaries :param name: string :param direction: string :param bearing: float :return: lane dictionary """ for l in lanes: if l['name'] == name and l[ 'direction'] == direction and get_lane_index_from_left(l) == 0: if abs(get_angle_between_bearings(bearing, l['bearing'])) < 15: return l return None
def get_destination_lanes_for_u_turn(origin_lane, all_lanes): """ Identifying the destination lane for the u-turn. Assuming that the origin and destination lanes must have the index from left equal to zero. :param origin_lane: lane dictionary of a left turn :param all_lanes: list of dictionaries :return: list of valid lane destinations for the left turn """ if origin_lane['name'] == 'no_name': return [] return [l for l in all_lanes if l['name'] == origin_lane['name'] and l['direction'] == 'from_intersection' and get_lane_index_from_left(l) == 0 and abs(get_angle_between_bearings(origin_lane['bearing'], l['bearing'])) > 150.0 and get_distance_between_points(l['left_border'][0], origin_lane['left_border'][-1]) < 25.0 and get_distance_between_points(l['left_border'][0], l['left_border'][-1]) > 25.0 ]
def get_sorted_lane_subset(lanes, name, bearing, direction, func): """ Get subset of lanes for the specified name, bearing ang direction. Sort the results fy the specified function. :param lanes: list of lanes :param name: string :param bearing: float in degrees :param direction: string :param func: function name to provide sorting value :return: list of lanes """ subset = [ l for l in lanes if l['name'] == name and l['direction'] == direction and abs(get_angle_between_bearings(bearing, l['bearing'])) < 30 ] if subset: return sorted(subset, key=func) else: return []
def construct_u_turn_arc(origin_border, destination_border, number_of_points=12): """ Construct a turn arc with the destination border :param origin_border: list of coordinates :param destination_border: list of coordinates :param number_of_points: integer :return: list of coordinates """ bearing1 = get_compass(origin_border[-2], origin_border[-1]) bearing2 = get_compass(destination_border[0], destination_border[1]) angle = abs(get_angle_between_bearings(bearing1, bearing2)) radius, landing_border = get_u_turn_radius_and_landing_border(origin_border, destination_border) if radius > 50: logger.debug('U-turn bearings %r, %r, angle %r' % (bearing1, bearing2, angle)) logger.warning('Radius is too large %r, landing border %r' % (radius, landing_border)) ob = cut_line_by_relative_distance(destination_border, 0.95) radius, landing_border = get_u_turn_radius_and_landing_border(ob, destination_border) logger.debug('Retry bearings %r, %r, angle %r' % (bearing1, bearing2, angle)) logger.debug('Adjusted radius %r, landing border %r' % (radius, landing_border)) else: ob = origin_border shift = [2.0 * radius * (math.sin(to_rad(angle / 2.0 * i / float(number_of_points)))) ** 2 for i in range(0, number_of_points + 1) ] vec = [ob[-1], extend_vector(ob[-2:], length=30.0, backward=False, relative=True)[-1]] vector = [extend_vector(vec, length=radius * (math.sin(to_rad(angle * i / float(number_of_points)))), backward=False )[1] for i in range(0, number_of_points + 1) ] arc = [shift_by_bearing_and_distance(vector[i], shift[i], ob[-2:], bearing_delta=-90.0) for i in range(0, number_of_points + 1) ] return arc[:-1] + landing_border
def is_opposite_lane_exist(lane_data, lanes): """ Check if an opposite traffic exists for the same street :param lane_data: lane dictionary :param lanes: list of lane dictionaries :return: True or False """ if lane_data['direction'] == 'to_intersection': opposite_direction = 'from_intersection' else: opposite_direction = 'to_intersection' opposite_bearing = (lane_data['bearing'] + 180.0) % 360.0 opposite_lanes = [ l for l in lanes if l['name'] == lane_data['name'] and opposite_direction in l['direction'] and abs(get_angle_between_bearings(opposite_bearing, l['bearing'])) < 30 ] if opposite_lanes: return True else: return False
def merge_lanes(lanes, nodes_dict): """ Merge lanes for same street, direction, lane id :param lanes: list of dictionaries :param nodes_dict: dictionary of nodes :return: list of dictionaries """ if lanes: lane_type = lanes[0]['lane_type'] if lane_type == 'cycleway': lane_type = 'bicycle' elif lane_type == 'footway': lane_type = 'footway' elif lane_type == 'railway': lane_type = 'railway' else: lane_type = 'vehicle' logger.info('Start merging %d lanes of type %s' % (len(lanes), lane_type)) else: return [] merged_lanes = [] set_ids(lanes) for lane in [l for l in lanes if l['name'] == 'no_name']: merged_lanes.append(add_lane(lane, merged_lane=None)) names = sorted(set([l['name'] for l in lanes if l['name'] != 'no_name'])) for name in names: ids = sorted(set([l['lane_id'] for l in lanes if l['name'] == name])) for lane_id in ids: directions = sorted( set([ l['direction'] for l in lanes if l['lane_id'] == lane_id and l['name'] == name ])) for direction in directions: similar_lanes = [ l for l in lanes if l['lane_id'] == lane_id and l['name'] == name and l['direction'] == direction and len(l['nodes']) > 0 ] for similar_lane in similar_lanes: if not isinstance(similar_lane['path'], dict): continue bearing = similar_lane['path']['bearing'] next_lanes = [ l for l in similar_lanes if isinstance(l['path'], dict) and similar_lane['nodes'][-1] == l['nodes'][0] and abs( get_angle_between_bearings(l['path']['bearing'], bearing)) < 60.0 ] if len(next_lanes) == 0: similar_lane['next'] = None else: if next_lanes[0]['path_id'] != similar_lane['path_id']: similar_lane['next'] = next_lanes[0]['path_id'] else: similar_lane['next'] = None prev_lanes = [ l for l in similar_lanes if isinstance(l['path'], dict) and similar_lane['nodes'][0] == l['nodes'][-1] and abs( get_angle_between_bearings(l['path']['bearing'], bearing)) < 60.0 ] if len(prev_lanes) == 0: similar_lane['prev'] = None else: if prev_lanes[0]['path_id'] != similar_lane['path_id']: similar_lane['prev'] = prev_lanes[0]['path_id'] else: similar_lane['prev'] = None for start_lane in [ l for l in similar_lanes if "prev" in l and l['prev'] is None ]: merged_lane = add_lane(start_lane, merged_lane=None) nxt = start_lane['next'] while nxt is not None: next_lane = [ l for l in similar_lanes if l['path_id'] == nxt ][0] merged_lane = add_lane(next_lane, merged_lane=merged_lane) nxt = next_lane['next'] merged_lanes.append(merged_lane) set_lane_bearing(merged_lanes) add_node_tags_to_lanes(merged_lanes, nodes_dict) insert_referenced_nodes_to_lanes(merged_lanes, nodes_dict) logger.info('Total %d merged lanes' % len(merged_lanes)) return merged_lanes
def get_intersection_meta_data(intersection_data): number_of_approaches = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] ])) number_of_exits = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] ])) number_of_railway_approaches = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_tracks'] if 'to_intersection' in l['meta_data']['identification'] ])) number_of_railway_exits = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_tracks'] if 'from_intersection' in l['meta_data']['identification'] ])) if number_of_approaches > 0: max_number_of_lanes_in_approach = max([0] + [ l['meta_data']['max_number_of_lanes'] for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] ]) min_number_of_lanes_in_approach = min([ l['meta_data']['min_number_of_lanes'] for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] ]) else: max_number_of_lanes_in_approach = 0 min_number_of_lanes_in_approach = 0 if number_of_exits > 0: max_number_of_lanes_in_exit = max([0] + [ l['meta_data']['max_number_of_lanes'] for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] ]) min_number_of_lanes_in_exit = min([ l['meta_data']['min_number_of_lanes'] for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] ]) else: max_number_of_lanes_in_exit = 0 min_number_of_lanes_in_exit = 0 # Stop signs if any([ l['meta_data']['stop_sign'] == 'yes' for l in intersection_data['merged_lanes'] if 'stop_sign' in l['meta_data'] ]): stop_sign = 'yes' elif all([ l['meta_data']['stop_sign'] == 'no' for l in intersection_data['merged_lanes'] if 'stop_sign' in l['meta_data'] ]): stop_sign = 'no' else: stop_sign = None # Traffic signals if any([ l['meta_data']['traffic_signals'] == 'yes' for l in intersection_data['merged_lanes'] if 'traffic_signals' in l['meta_data'] ]): signal_present = 'yes' elif all([ l['meta_data']['traffic_signals'] == 'no' for l in intersection_data['merged_lanes'] if 'traffic_signals' in l['meta_data'] ]): signal_present = 'no' else: signal_present = None if signal_present is None and stop_sign == 'yes': signal_present = 'no' elif stop_sign is None and signal_present == 'yes': stop_sign = 'no' # Pedestrian_traffic_signals if any([ l['meta_data']['pedestrian_traffic_signals'] == 'yes' for l in intersection_data['merged_lanes'] ]): pedestrian_traffic_signals = 'yes' elif all([ l['meta_data']['pedestrian_traffic_signals'] == 'no' for l in intersection_data['merged_lanes'] ]): pedestrian_traffic_signals = 'no' else: pedestrian_traffic_signals = None if pedestrian_traffic_signals == 'yes': signal_present = 'yes' number_of_center_bicycle_approaches = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] and l['meta_data']['bicycle_lane_on_the_left'] is not None and 'yes' in l['meta_data']['bicycle_lane_on_the_left'] ])) number_of_right_side_bicycle_approaches = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] and l['meta_data']['bicycle_lane_on_the_right'] is not None and l['meta_data']['bicycle_lane_on_the_right'] != 'no' ])) number_of_center_bicycle_exits = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] and l['meta_data']['bicycle_lane_on_the_left'] is not None and 'yes' in l['meta_data']['bicycle_lane_on_the_left'] ])) number_of_right_side_bicycle_exits = len( set([ l['meta_data']['identification'] + '_' + l['meta_data']['compass'] for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] and l['meta_data']['bicycle_lane_on_the_right'] is not None and l['meta_data']['bicycle_lane_on_the_right'] != 'no' ])) max_angle = 0.0 for l1 in intersection_data['merged_lanes']: if 'from_intersection' in l1['direction']: continue b1 = l1['bearing'] max_angle = max( max_angle, max([0] + [ abs(get_angle_between_bearings(l2['bearing'], b1)) for l2 in intersection_data['merged_lanes'] if 'from_intersection' in l2['direction'] and l1['name'] != l2['name'] ])) if [ n for n in intersection_data['nodes'] if 'subway' in intersection_data['nodes'][n] and intersection_data['nodes'][n]['subway'] == 'yes' ]: subway_station_present = 'yes' else: subway_station_present = 'no' rail_stations = [1 for n in intersection_data['nodes'] if 'light_rail' in intersection_data['nodes'][n] and intersection_data['nodes'][n]['light_rail'] == 'yes'] + \ [1 for n in intersection_data['nodes'] if 'station' in intersection_data['nodes'][n] and intersection_data['nodes'][n]['station'] == 'light_rail'] + \ [1 for n in intersection_data['nodes'] if 'railway' in intersection_data['nodes'][n] and intersection_data['nodes'][n]['railway'] == 'station'] bus_stops = [1 for n in intersection_data['nodes'] if 'highway' in intersection_data['nodes'][n] and intersection_data['nodes'][n]['highway'] == 'bus_stop'] + \ [1 for n in intersection_data['nodes'] if 'highway' in intersection_data['nodes'][n] and 'trolley' in intersection_data['nodes'][n]['highway']] intersection_diameter = get_intersection_diameter(intersection_data) distance_to_next_intersection = get_distance_to_next_intersection( intersection_data, intersection_diameter) approach_street_types = get_list_of_highway_types(intersection_data, "to_intersection") exit_street_types = get_list_of_highway_types(intersection_data, "from_intersection") try: approach_list = [ int(l['meta_data']['maxspeed'].split(' ')[0]) for l in intersection_data['merged_lanes'] if 'to_intersection' in l['meta_data']['identification'] ] if approach_list: approach_max_speed = str(max(approach_list)) + ' ' + 'mph' approach_min_speed = str(min(approach_list)) + ' ' + 'mph' else: approach_max_speed = '25 mph' approach_min_speed = '25 mph' exit_list = [ int(l['meta_data']['maxspeed'].split(' ')[0]) for l in intersection_data['merged_lanes'] if 'from_intersection' in l['meta_data']['identification'] ] if exit_list: exit_max_speed = str(max(exit_list)) + ' ' + 'mph' exit_min_speed = str(min(exit_list)) + ' ' + 'mph' else: exit_max_speed = '25 mph' exit_min_speed = '25 mph' except: approach_max_speed = '25 mph' approach_min_speed = '25 mph' exit_max_speed = '25 mph' exit_min_speed = '25 mph' meta_data = { 'number_of_approaches': number_of_approaches, 'number_of_exits': number_of_exits, 'max_number_of_lanes_in_approach': max_number_of_lanes_in_approach, 'min_number_of_lanes_in_approach': min_number_of_lanes_in_approach, 'number_of_railway_approaches': number_of_railway_approaches, 'number_of_railway_exits': number_of_railway_exits, 'max_number_of_lanes_in_exit': max_number_of_lanes_in_exit, 'min_number_of_lanes_in_exit': min_number_of_lanes_in_exit, 'number_of_center_bicycle_approaches': number_of_center_bicycle_approaches, 'number_of_right_side_bicycle_approaches': number_of_right_side_bicycle_approaches, 'number_of_center_bicycle_exits': number_of_center_bicycle_exits, 'number_of_right_side_bicycle_exits': number_of_right_side_bicycle_exits, 'signal_present': signal_present, 'pedestrian_signal_present': pedestrian_traffic_signals, 'diameter': intersection_diameter, 'max_angle': max_angle, 'max_curvature': max([0] + [ l['meta_data']['curvature'] for l in intersection_data['merged_lanes'] ]), 'min_curvature': min([ l['meta_data']['curvature'] for l in intersection_data['merged_lanes'] ]), 'distance_to_next_intersection': distance_to_next_intersection, 'shortest_distance_to_railway_crossing': get_distance_to_railway_crossing(intersection_data), 'subway_station_present': subway_station_present, 'number_of_tram/train_stops': sum(rail_stations), 'number_of_bus/trolley_stops': sum(bus_stops), 'stop_sign': stop_sign, 'approach_street_types': approach_street_types, 'exit_street_types': exit_street_types, 'approach_max_speed_limit': approach_max_speed, 'approach_min_speed_limit': approach_min_speed, 'exit_max_speed_limit': exit_max_speed, 'exit_min_speed_limit': exit_min_speed, 'approach_counts': count_oneways(intersection_data, 'to_intersection'), 'exit_counts': count_oneways(intersection_data, 'from_intersection'), } meta_data['timestamp'] = str(datetime.datetime.now()) return meta_data