parent
							
								
									cb2fe8277d
								
							
						
					
					
						commit
						ed69033e66
					
				
				 5 changed files with 0 additions and 1005 deletions
			
			
		| @ -1,106 +0,0 @@ | ||||
| { | ||||
|   "_comment": "These speeds are from https://wiki.openstreetmap.org/wiki/Speed_limits  Special cases have been stripped", | ||||
|   "AR:urban": "40", | ||||
|   "AR:urban:primary": "60", | ||||
|   "AR:urban:secondary": "60", | ||||
|   "AR:rural": "110", | ||||
|   "AT:urban": "50", | ||||
|   "AT:rural": "100", | ||||
|   "AT:trunk": "100", | ||||
|   "AT:motorway": "130", | ||||
|   "BE:urban": "50", | ||||
|   "BE-VLG:rural": "70", | ||||
|   "BE-WAL:rural": "90", | ||||
|   "BE:trunk": "120", | ||||
|   "BE:motorway": "120", | ||||
|   "CH:urban[1]": "50", | ||||
|   "CH:rural": "80", | ||||
|   "CH:trunk": "100", | ||||
|   "CH:motorway": "120", | ||||
|   "CZ:pedestrian_zone": "20", | ||||
|   "CZ:living_street": "20", | ||||
|   "CZ:urban": "50", | ||||
|   "CZ:urban_trunk": "80", | ||||
|   "CZ:urban_motorway": "80", | ||||
|   "CZ:rural": "90", | ||||
|   "CZ:trunk": "110", | ||||
|   "CZ:motorway": "130", | ||||
|   "DK:urban": "50", | ||||
|   "DK:rural": "80", | ||||
|   "DK:motorway": "130", | ||||
|   "DE:living_street": "7", | ||||
|   "DE:residential": "30", | ||||
|   "DE:urban": "50", | ||||
|   "DE:rural": "100", | ||||
|   "DE:trunk": "none", | ||||
|   "DE:motorway": "none", | ||||
|   "FI:urban": "50", | ||||
|   "FI:rural": "80", | ||||
|   "FI:trunk": "100", | ||||
|   "FI:motorway": "120", | ||||
|   "FR:urban": "50", | ||||
|   "FR:rural": "80", | ||||
|   "FR:trunk": "110", | ||||
|   "FR:motorway": "130", | ||||
|   "GR:urban": "50", | ||||
|   "GR:rural": "90", | ||||
|   "GR:trunk": "110", | ||||
|   "GR:motorway": "130", | ||||
|   "HU:urban": "50", | ||||
|   "HU:rural": "90", | ||||
|   "HU:trunk": "110", | ||||
|   "HU:motorway": "130", | ||||
|   "IT:urban": "50", | ||||
|   "IT:rural": "90", | ||||
|   "IT:trunk": "110", | ||||
|   "IT:motorway": "130", | ||||
|   "JP:national": "60", | ||||
|   "JP:motorway": "100", | ||||
|   "LT:living_street": "20", | ||||
|   "LT:urban": "50", | ||||
|   "LT:rural": "90", | ||||
|   "LT:trunk": "120", | ||||
|   "LT:motorway": "130", | ||||
|   "PL:living_street": "20", | ||||
|   "PL:urban": "50", | ||||
|   "PL:rural": "90", | ||||
|   "PL:trunk": "100", | ||||
|   "PL:motorway": "140", | ||||
|   "RO:urban": "50", | ||||
|   "RO:rural": "90", | ||||
|   "RO:trunk": "100", | ||||
|   "RO:motorway": "130", | ||||
|   "RU:living_street": "20", | ||||
|   "RU:urban": "60", | ||||
|   "RU:rural": "90", | ||||
|   "RU:motorway": "110", | ||||
|   "SK:urban": "50", | ||||
|   "SK:rural": "90", | ||||
|   "SK:trunk": "90", | ||||
|   "SK:motorway": "90", | ||||
|   "SI:urban": "50", | ||||
|   "SI:rural": "90", | ||||
|   "SI:trunk": "110", | ||||
|   "SI:motorway": "130", | ||||
|   "ES:living_street": "20", | ||||
|   "ES:urban": "50", | ||||
|   "ES:rural": "50", | ||||
|   "ES:trunk": "90", | ||||
|   "ES:motorway": "120", | ||||
|   "SE:urban": "50", | ||||
|   "SE:rural": "70", | ||||
|   "SE:trunk": "90", | ||||
|   "SE:motorway": "110", | ||||
|   "GB:nsl_restricted": "30 mph", | ||||
|   "GB:nsl_single": "60 mph", | ||||
|   "GB:nsl_dual": "70 mph", | ||||
|   "GB:motorway": "70 mph", | ||||
|   "UA:urban": "50", | ||||
|   "UA:rural": "90", | ||||
|   "UA:trunk": "110", | ||||
|   "UA:motorway": "130", | ||||
|   "UZ:living_street": "30", | ||||
|   "UZ:urban": "70", | ||||
|   "UZ:rural": "100", | ||||
|   "UZ:motorway": "110" | ||||
| } | ||||
| @ -1,240 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import json | ||||
| 
 | ||||
| DEFAULT_OUTPUT_FILENAME = "default_speeds_by_region.json" | ||||
| 
 | ||||
| def main(filename = DEFAULT_OUTPUT_FILENAME): | ||||
|   countries = [] | ||||
| 
 | ||||
|   """ | ||||
|   -------------------------------------------------- | ||||
|       US - United State of America | ||||
|   -------------------------------------------------- | ||||
|   """ | ||||
|   US = Country("US") # First step, create the country using the ISO 3166 two letter code | ||||
|   countries.append(US) # Second step, add the country to countries list | ||||
| 
 | ||||
|   """ Default rules """ | ||||
|   # Third step, add some default rules for the country | ||||
|   # Speed limit rules are based on OpenStreetMaps (OSM) tags. | ||||
|   # The dictionary {...} defines the tag_name: value | ||||
|   # if a road in OSM has a tag with the name tag_name and this value, the speed limit listed below will be applied. | ||||
|   # The text at the end is the speed limit (use no unit for km/h) | ||||
|   # Rules apply in the order in which they are written for each country | ||||
|   # Rules for specific regions (states) take priority over country rules | ||||
|   # If you modify existing country rules, you must update all existing states without that rule to use the old rule | ||||
|   US.add_rule({"highway": "motorway"}, "65 mph") # On US roads with the tag highway and value motorway, the speed limit will default to  65 mph | ||||
|   US.add_rule({"highway": "trunk"}, "55 mph") | ||||
|   US.add_rule({"highway": "primary"}, "55 mph") | ||||
|   US.add_rule({"highway": "secondary"}, "45 mph") | ||||
|   US.add_rule({"highway": "tertiary"}, "35 mph") | ||||
|   US.add_rule({"highway": "unclassified"}, "55 mph") | ||||
|   US.add_rule({"highway": "residential"}, "25 mph") | ||||
|   US.add_rule({"highway": "service"}, "25 mph") | ||||
|   US.add_rule({"highway": "motorway_link"}, "55 mph") | ||||
|   US.add_rule({"highway": "trunk_link"}, "55 mph") | ||||
|   US.add_rule({"highway": "primary_link"}, "55 mph") | ||||
|   US.add_rule({"highway": "secondary_link"}, "45 mph") | ||||
|   US.add_rule({"highway": "tertiary_link"}, "35 mph") | ||||
|   US.add_rule({"highway": "living_street"}, "15 mph") | ||||
| 
 | ||||
|   """ States """ | ||||
|   new_york = US.add_region("New York") # Fourth step, add a state/region to country | ||||
|   new_york.add_rule({"highway": "primary"}, "45 mph") # Fifth step , add rules to the state. See the text above for how to write rules | ||||
|   new_york.add_rule({"highway": "secondary"}, "55 mph") | ||||
|   new_york.add_rule({"highway": "tertiary"}, "55 mph") | ||||
|   new_york.add_rule({"highway": "residential"}, "30 mph") | ||||
|   new_york.add_rule({"highway": "primary_link"}, "45 mph") | ||||
|   new_york.add_rule({"highway": "secondary_link"}, "55 mph") | ||||
|   new_york.add_rule({"highway": "tertiary_link"}, "55 mph") | ||||
|   # All if not written by the state, the rules will default to the country rules | ||||
| 
 | ||||
|   #california = US.add_region("California") | ||||
|   # California uses only the default US rules | ||||
| 
 | ||||
|   michigan = US.add_region("Michigan") | ||||
|   michigan.add_rule({"highway": "motorway"}, "70 mph") | ||||
| 
 | ||||
|   oregon = US.add_region("Oregon") | ||||
|   oregon.add_rule({"highway": "motorway"}, "55 mph") | ||||
|   oregon.add_rule({"highway": "secondary"}, "35 mph") | ||||
|   oregon.add_rule({"highway": "tertiary"}, "30 mph") | ||||
|   oregon.add_rule({"highway": "service"}, "15 mph") | ||||
|   oregon.add_rule({"highway": "secondary_link"}, "35 mph") | ||||
|   oregon.add_rule({"highway": "tertiary_link"}, "30 mph") | ||||
| 
 | ||||
|   south_dakota = US.add_region("South Dakota") | ||||
|   south_dakota.add_rule({"highway": "motorway"}, "80 mph") | ||||
|   south_dakota.add_rule({"highway": "trunk"}, "70 mph") | ||||
|   south_dakota.add_rule({"highway": "primary"}, "65 mph") | ||||
|   south_dakota.add_rule({"highway": "trunk_link"}, "70 mph") | ||||
|   south_dakota.add_rule({"highway": "primary_link"}, "65 mph") | ||||
| 
 | ||||
|   wisconsin = US.add_region("Wisconsin") | ||||
|   wisconsin.add_rule({"highway": "trunk"}, "65 mph") | ||||
|   wisconsin.add_rule({"highway": "tertiary"}, "45 mph") | ||||
|   wisconsin.add_rule({"highway": "unclassified"}, "35 mph") | ||||
|   wisconsin.add_rule({"highway": "trunk_link"}, "65 mph") | ||||
|   wisconsin.add_rule({"highway": "tertiary_link"}, "45 mph") | ||||
| 
 | ||||
|   """ | ||||
|   -------------------------------------------------- | ||||
|       AU - Australia | ||||
|   -------------------------------------------------- | ||||
|   """ | ||||
|   AU = Country("AU") | ||||
|   countries.append(AU) | ||||
| 
 | ||||
|   """ Default rules """ | ||||
|   AU.add_rule({"highway": "motorway"}, "100") | ||||
|   AU.add_rule({"highway": "trunk"}, "80") | ||||
|   AU.add_rule({"highway": "primary"}, "80") | ||||
|   AU.add_rule({"highway": "secondary"}, "50") | ||||
|   AU.add_rule({"highway": "tertiary"}, "50") | ||||
|   AU.add_rule({"highway": "unclassified"}, "80") | ||||
|   AU.add_rule({"highway": "residential"}, "50") | ||||
|   AU.add_rule({"highway": "service"}, "40") | ||||
|   AU.add_rule({"highway": "motorway_link"}, "90") | ||||
|   AU.add_rule({"highway": "trunk_link"}, "80") | ||||
|   AU.add_rule({"highway": "primary_link"}, "80") | ||||
|   AU.add_rule({"highway": "secondary_link"}, "50") | ||||
|   AU.add_rule({"highway": "tertiary_link"}, "50") | ||||
|   AU.add_rule({"highway": "living_street"}, "30") | ||||
| 
 | ||||
|   """ | ||||
|   -------------------------------------------------- | ||||
|       CA - Canada | ||||
|   -------------------------------------------------- | ||||
|   """ | ||||
|   CA = Country("CA") | ||||
|   countries.append(CA) | ||||
| 
 | ||||
|   """ Default rules """ | ||||
|   CA.add_rule({"highway": "motorway"}, "100") | ||||
|   CA.add_rule({"highway": "trunk"}, "80") | ||||
|   CA.add_rule({"highway": "primary"}, "80") | ||||
|   CA.add_rule({"highway": "secondary"}, "50") | ||||
|   CA.add_rule({"highway": "tertiary"}, "50") | ||||
|   CA.add_rule({"highway": "unclassified"}, "80") | ||||
|   CA.add_rule({"highway": "residential"}, "40") | ||||
|   CA.add_rule({"highway": "service"}, "40") | ||||
|   CA.add_rule({"highway": "motorway_link"}, "90") | ||||
|   CA.add_rule({"highway": "trunk_link"}, "80") | ||||
|   CA.add_rule({"highway": "primary_link"}, "80") | ||||
|   CA.add_rule({"highway": "secondary_link"}, "50") | ||||
|   CA.add_rule({"highway": "tertiary_link"}, "50") | ||||
|   CA.add_rule({"highway": "living_street"}, "20") | ||||
| 
 | ||||
| 
 | ||||
|   """ | ||||
|   -------------------------------------------------- | ||||
|       DE - Germany | ||||
|   -------------------------------------------------- | ||||
|   """ | ||||
|   DE = Country("DE") | ||||
|   countries.append(DE) | ||||
| 
 | ||||
|   """ Default rules """ | ||||
|   DE.add_rule({"highway": "motorway"}, "none") | ||||
|   DE.add_rule({"highway": "living_street"}, "10") | ||||
|   DE.add_rule({"highway": "residential"}, "30") | ||||
|   DE.add_rule({"zone:traffic": "DE:rural"}, "100") | ||||
|   DE.add_rule({"zone:traffic": "DE:urban"}, "50") | ||||
|   DE.add_rule({"zone:maxspeed": "DE:30"}, "30") | ||||
|   DE.add_rule({"zone:maxspeed": "DE:urban"}, "50") | ||||
|   DE.add_rule({"zone:maxspeed": "DE:rural"}, "100") | ||||
|   DE.add_rule({"zone:maxspeed": "DE:motorway"}, "none") | ||||
|   DE.add_rule({"bicycle_road": "yes"}, "30") | ||||
|    | ||||
| 
 | ||||
|   """ | ||||
|   -------------------------------------------------- | ||||
|       EE - Estonia | ||||
|   -------------------------------------------------- | ||||
|   """ | ||||
|   EE = Country("EE") | ||||
|   countries.append(EE) | ||||
| 
 | ||||
|   """ Default rules """ | ||||
|   EE.add_rule({"highway": "motorway"}, "90") | ||||
|   EE.add_rule({"highway": "trunk"}, "90") | ||||
|   EE.add_rule({"highway": "primary"}, "90") | ||||
|   EE.add_rule({"highway": "secondary"}, "50") | ||||
|   EE.add_rule({"highway": "tertiary"}, "50") | ||||
|   EE.add_rule({"highway": "unclassified"}, "90") | ||||
|   EE.add_rule({"highway": "residential"}, "40") | ||||
|   EE.add_rule({"highway": "service"}, "40") | ||||
|   EE.add_rule({"highway": "motorway_link"}, "90") | ||||
|   EE.add_rule({"highway": "trunk_link"}, "70") | ||||
|   EE.add_rule({"highway": "primary_link"}, "70") | ||||
|   EE.add_rule({"highway": "secondary_link"}, "50") | ||||
|   EE.add_rule({"highway": "tertiary_link"}, "50") | ||||
|   EE.add_rule({"highway": "living_street"}, "20") | ||||
| 
 | ||||
| 
 | ||||
|   """ --- DO NOT MODIFY CODE BELOW THIS LINE --- """ | ||||
|   """ --- ADD YOUR COUNTRY OR STATE ABOVE --- """ | ||||
| 
 | ||||
|   # Final step | ||||
|   write_json(countries, filename) | ||||
| 
 | ||||
| def write_json(countries, filename = DEFAULT_OUTPUT_FILENAME): | ||||
|   out_dict = {} | ||||
|   for country in countries: | ||||
|     out_dict.update(country.jsonify()) | ||||
|   json_string = json.dumps(out_dict, indent=2) | ||||
|   with open(filename, "wb") as f: | ||||
|     f.write(json_string) | ||||
| 
 | ||||
| 
 | ||||
| class Region(): | ||||
|   ALLOWABLE_TAG_KEYS = ["highway", "zone:traffic", "bicycle_road", "zone:maxspeed"] | ||||
|   ALLOWABLE_HIGHWAY_TYPES = ["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "service", "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link", "living_street"] | ||||
|   def __init__(self, name): | ||||
|     self.name = name | ||||
|     self.rules = [] | ||||
| 
 | ||||
|   def add_rule(self, tag_conditions, speed): | ||||
|     new_rule = {} | ||||
|     if not isinstance(tag_conditions, dict): | ||||
|       raise TypeError("Rule tag conditions must be dictionary") | ||||
|     if not all(tag_key in self.ALLOWABLE_TAG_KEYS for tag_key in tag_conditions): | ||||
|       raise ValueError("Rule tag keys must be in allowable tag kesy") # If this is by mistake, please update ALLOWABLE_TAG_KEYS | ||||
|     if 'highway' in tag_conditions: | ||||
|       if not tag_conditions['highway'] in self.ALLOWABLE_HIGHWAY_TYPES: | ||||
|         raise ValueError("Invalid Highway type {}".format(tag_conditions["highway"])) | ||||
|     new_rule['tags'] = tag_conditions | ||||
|     try: | ||||
|       new_rule['speed'] = str(speed) | ||||
|     except ValueError: | ||||
|       raise ValueError("Rule speed must be string") | ||||
|     self.rules.append(new_rule) | ||||
| 
 | ||||
|   def jsonify(self): | ||||
|     ret_dict = {} | ||||
|     ret_dict[self.name] = self.rules | ||||
|     return ret_dict | ||||
| 
 | ||||
| class Country(Region): | ||||
|   ALLOWABLE_COUNTRY_CODES = ["AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ","BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO","BN","BG","BF","BI","KH","CM","CA","CV","KY","CF","TD","CL","CN","CX","CC","CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV","GQ","ER","EE","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MK","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SZ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW"] | ||||
|   def __init__(self, ISO_3166_alpha_2): | ||||
|     Region.__init__(self, ISO_3166_alpha_2) | ||||
|     if ISO_3166_alpha_2 not in self.ALLOWABLE_COUNTRY_CODES: | ||||
|       raise ValueError("Not valid IOS 3166 country code") | ||||
|     self.regions = {} | ||||
| 
 | ||||
|   def add_region(self, name): | ||||
|     self.regions[name] = Region(name) | ||||
|     return self.regions[name] | ||||
| 
 | ||||
|   def jsonify(self): | ||||
|     ret_dict = {} | ||||
|     ret_dict[self.name] = {} | ||||
|     for r_name, region in self.regions.items(): | ||||
|       ret_dict[self.name].update(region.jsonify()) | ||||
|     ret_dict[self.name]['Default'] = self.rules | ||||
|     return ret_dict | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|   main() | ||||
| @ -1,295 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Add phonelibs openblas to LD_LIBRARY_PATH if import fails | ||||
| from common.basedir import BASEDIR | ||||
| try: | ||||
|   from scipy import spatial | ||||
| except ImportError as e: | ||||
|   import os | ||||
|   import sys | ||||
| 
 | ||||
| 
 | ||||
|   openblas_path = os.path.join(BASEDIR, "phonelibs/openblas/") | ||||
|   os.environ['LD_LIBRARY_PATH'] += ':' + openblas_path | ||||
| 
 | ||||
|   args = [sys.executable] | ||||
|   args.extend(sys.argv) | ||||
|   os.execv(sys.executable, args) | ||||
| 
 | ||||
| DEFAULT_SPEEDS_BY_REGION_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds_by_region.json" | ||||
| from selfdrive.mapd import default_speeds_generator | ||||
| default_speeds_generator.main(DEFAULT_SPEEDS_BY_REGION_JSON_FILE) | ||||
| 
 | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| import zmq | ||||
| import threading | ||||
| import numpy as np | ||||
| import overpy | ||||
| from collections import defaultdict | ||||
| 
 | ||||
| from common.params import Params | ||||
| from common.transformations.coordinates import geodetic2ecef | ||||
| from cereal.services import service_list | ||||
| import cereal.messaging as messaging | ||||
| from selfdrive.mapd.mapd_helpers import MAPS_LOOKAHEAD_DISTANCE, Way, circle_through_points | ||||
| import selfdrive.crash as crash | ||||
| from selfdrive.version import version, dirty | ||||
| 
 | ||||
| 
 | ||||
| OVERPASS_API_URL = "https://overpass.kumi.systems/api/interpreter" | ||||
| OVERPASS_HEADERS = { | ||||
|     'User-Agent': 'NEOS (comma.ai)', | ||||
|     'Accept-Encoding': 'gzip' | ||||
| } | ||||
| 
 | ||||
| last_gps = None | ||||
| query_lock = threading.Lock() | ||||
| last_query_result = None | ||||
| last_query_pos = None | ||||
| cache_valid = False | ||||
| 
 | ||||
| def build_way_query(lat, lon, radius=50): | ||||
|   """Builds a query to find all highways within a given radius around a point""" | ||||
|   pos = "  (around:%f,%f,%f)" % (radius, lat, lon) | ||||
|   lat_lon = "(%f,%f)" % (lat, lon) | ||||
|   q = """( | ||||
|   way | ||||
|   """ + pos + """ | ||||
|   [highway][highway!~"^(footway|path|bridleway|steps|cycleway|construction|bus_guideway|escape)$"]; | ||||
|   >;);out;""" + """is_in""" + lat_lon + """;area._[admin_level~"[24]"]; | ||||
|   convert area ::id = id(), admin_level = t['admin_level'], | ||||
|   name = t['name'], "ISO3166-1:alpha2" = t['ISO3166-1:alpha2'];out; | ||||
|   """ | ||||
|   return q | ||||
| 
 | ||||
| 
 | ||||
| def query_thread(): | ||||
|   global last_query_result, last_query_pos, cache_valid | ||||
|   api = overpy.Overpass(url=OVERPASS_API_URL, headers=OVERPASS_HEADERS, timeout=10.) | ||||
| 
 | ||||
|   while True: | ||||
|     time.sleep(1) | ||||
|     if last_gps is not None: | ||||
|       fix_ok = last_gps.flags & 1 | ||||
|       if not fix_ok: | ||||
|         continue | ||||
| 
 | ||||
|       if last_query_pos is not None: | ||||
|         cur_ecef = geodetic2ecef((last_gps.latitude, last_gps.longitude, last_gps.altitude)) | ||||
|         prev_ecef = geodetic2ecef((last_query_pos.latitude, last_query_pos.longitude, last_query_pos.altitude)) | ||||
|         dist = np.linalg.norm(cur_ecef - prev_ecef) | ||||
|         if dist < 1000: #updated when we are 1km from the edge of the downloaded circle | ||||
|           continue | ||||
| 
 | ||||
|         if dist > 3000: | ||||
|           cache_valid = False | ||||
| 
 | ||||
|       q = build_way_query(last_gps.latitude, last_gps.longitude, radius=3000) | ||||
|       try: | ||||
|         new_result = api.query(q) | ||||
| 
 | ||||
|         # Build kd-tree | ||||
|         nodes = [] | ||||
|         real_nodes = [] | ||||
|         node_to_way = defaultdict(list) | ||||
|         location_info = {} | ||||
| 
 | ||||
|         for n in new_result.nodes: | ||||
|           nodes.append((float(n.lat), float(n.lon), 0)) | ||||
|           real_nodes.append(n) | ||||
| 
 | ||||
|         for way in new_result.ways: | ||||
|           for n in way.nodes: | ||||
|             node_to_way[n.id].append(way) | ||||
| 
 | ||||
|         for area in new_result.areas: | ||||
|           if area.tags.get('admin_level', '') == "2": | ||||
|             location_info['country'] = area.tags.get('ISO3166-1:alpha2', '') | ||||
|           if area.tags.get('admin_level', '') == "4": | ||||
|             location_info['region'] = area.tags.get('name', '') | ||||
| 
 | ||||
|         nodes = np.asarray(nodes) | ||||
|         nodes = geodetic2ecef(nodes) | ||||
|         tree = spatial.cKDTree(nodes) | ||||
| 
 | ||||
|         query_lock.acquire() | ||||
|         last_query_result = new_result, tree, real_nodes, node_to_way, location_info | ||||
|         last_query_pos = last_gps | ||||
|         cache_valid = True | ||||
|         query_lock.release() | ||||
| 
 | ||||
|       except Exception as e: | ||||
|         print(e) | ||||
|         query_lock.acquire() | ||||
|         last_query_result = None | ||||
|         query_lock.release() | ||||
| 
 | ||||
| 
 | ||||
| def mapsd_thread(): | ||||
|   global last_gps | ||||
| 
 | ||||
|   gps_sock = messaging.sub_sock('gpsLocation', conflate=True) | ||||
|   gps_external_sock = messaging.sub_sock('gpsLocationExternal', conflate=True) | ||||
|   map_data_sock = messaging.pub_sock('liveMapData') | ||||
| 
 | ||||
|   cur_way = None | ||||
|   curvature_valid = False | ||||
|   curvature = None | ||||
|   upcoming_curvature = 0. | ||||
|   dist_to_turn = 0. | ||||
|   road_points = None | ||||
| 
 | ||||
|   while True: | ||||
|     gps = messaging.recv_one(gps_sock) | ||||
|     gps_ext = messaging.recv_one_or_none(gps_external_sock) | ||||
| 
 | ||||
|     if gps_ext is not None: | ||||
|       gps = gps_ext.gpsLocationExternal | ||||
|     else: | ||||
|       gps = gps.gpsLocation | ||||
| 
 | ||||
|     last_gps = gps | ||||
| 
 | ||||
|     fix_ok = gps.flags & 1 | ||||
|     if not fix_ok or last_query_result is None or not cache_valid: | ||||
|       cur_way = None | ||||
|       curvature = None | ||||
|       curvature_valid = False | ||||
|       upcoming_curvature = 0. | ||||
|       dist_to_turn = 0. | ||||
|       road_points = None | ||||
|       map_valid = False | ||||
|     else: | ||||
|       map_valid = True | ||||
|       lat = gps.latitude | ||||
|       lon = gps.longitude | ||||
|       heading = gps.bearing | ||||
|       speed = gps.speed | ||||
| 
 | ||||
|       query_lock.acquire() | ||||
|       cur_way = Way.closest(last_query_result, lat, lon, heading, cur_way) | ||||
|       if cur_way is not None: | ||||
|         pnts, curvature_valid = cur_way.get_lookahead(lat, lon, heading, MAPS_LOOKAHEAD_DISTANCE) | ||||
| 
 | ||||
|         xs = pnts[:, 0] | ||||
|         ys = pnts[:, 1] | ||||
|         road_points = [float(x) for x in xs], [float(y) for y in ys] | ||||
| 
 | ||||
|         if speed < 10: | ||||
|           curvature_valid = False | ||||
|         if curvature_valid and pnts.shape[0] <= 3: | ||||
|           curvature_valid = False | ||||
| 
 | ||||
|         # The curvature is valid when at least MAPS_LOOKAHEAD_DISTANCE of road is found | ||||
|         if curvature_valid: | ||||
|           # Compute the curvature for each point | ||||
|           with np.errstate(divide='ignore'): | ||||
|             circles = [circle_through_points(*p) for p in zip(pnts, pnts[1:], pnts[2:])] | ||||
|             circles = np.asarray(circles) | ||||
|             radii = np.nan_to_num(circles[:, 2]) | ||||
|             radii[radii < 10] = np.inf | ||||
|             curvature = 1. / radii | ||||
| 
 | ||||
|           # Index of closest point | ||||
|           closest = np.argmin(np.linalg.norm(pnts, axis=1)) | ||||
|           dist_to_closest = pnts[closest, 0]  # We can use x distance here since it should be close | ||||
| 
 | ||||
|           # Compute distance along path | ||||
|           dists = list() | ||||
|           dists.append(0) | ||||
|           for p, p_prev in zip(pnts, pnts[1:, :]): | ||||
|             dists.append(dists[-1] + np.linalg.norm(p - p_prev)) | ||||
|           dists = np.asarray(dists) | ||||
|           dists = dists - dists[closest] + dist_to_closest | ||||
|           dists = dists[1:-1] | ||||
| 
 | ||||
|           close_idx = np.logical_and(dists > 0, dists < 500) | ||||
|           dists = dists[close_idx] | ||||
|           curvature = curvature[close_idx] | ||||
| 
 | ||||
|           if len(curvature): | ||||
|             # TODO: Determine left or right turn | ||||
|             curvature = np.nan_to_num(curvature) | ||||
| 
 | ||||
|             # Outlier rejection | ||||
|             new_curvature = np.percentile(curvature, 90, interpolation='lower') | ||||
| 
 | ||||
|             k = 0.6 | ||||
|             upcoming_curvature = k * upcoming_curvature + (1 - k) * new_curvature | ||||
|             in_turn_indices = curvature > 0.8 * new_curvature | ||||
| 
 | ||||
|             if np.any(in_turn_indices): | ||||
|               dist_to_turn = np.min(dists[in_turn_indices]) | ||||
|             else: | ||||
|               dist_to_turn = 999 | ||||
|           else: | ||||
|             upcoming_curvature = 0. | ||||
|             dist_to_turn = 999 | ||||
| 
 | ||||
|       query_lock.release() | ||||
| 
 | ||||
|     dat = messaging.new_message('liveMapData') | ||||
| 
 | ||||
|     if last_gps is not None: | ||||
|       dat.liveMapData.lastGps = last_gps | ||||
| 
 | ||||
|     if cur_way is not None: | ||||
|       dat.liveMapData.wayId = cur_way.id | ||||
| 
 | ||||
|       # Speed limit | ||||
|       max_speed = cur_way.max_speed() | ||||
|       if max_speed is not None: | ||||
|         dat.liveMapData.speedLimitValid = True | ||||
|         dat.liveMapData.speedLimit = max_speed | ||||
| 
 | ||||
|         # TODO: use the function below to anticipate upcoming speed limits | ||||
|         #max_speed_ahead, max_speed_ahead_dist = cur_way.max_speed_ahead(max_speed, lat, lon, heading, MAPS_LOOKAHEAD_DISTANCE) | ||||
|         #if max_speed_ahead is not None and max_speed_ahead_dist is not None: | ||||
|         #  dat.liveMapData.speedLimitAheadValid = True | ||||
|         #  dat.liveMapData.speedLimitAhead = float(max_speed_ahead) | ||||
|         #  dat.liveMapData.speedLimitAheadDistance = float(max_speed_ahead_dist) | ||||
| 
 | ||||
| 
 | ||||
|       advisory_max_speed = cur_way.advisory_max_speed() | ||||
|       if advisory_max_speed is not None: | ||||
|         dat.liveMapData.speedAdvisoryValid = True | ||||
|         dat.liveMapData.speedAdvisory = advisory_max_speed | ||||
| 
 | ||||
|       # Curvature | ||||
|       dat.liveMapData.curvatureValid = curvature_valid | ||||
|       dat.liveMapData.curvature = float(upcoming_curvature) | ||||
|       dat.liveMapData.distToTurn = float(dist_to_turn) | ||||
|       if road_points is not None: | ||||
|         dat.liveMapData.roadX, dat.liveMapData.roadY = road_points | ||||
|       if curvature is not None: | ||||
|         dat.liveMapData.roadCurvatureX = [float(x) for x in dists] | ||||
|         dat.liveMapData.roadCurvature = [float(x) for x in curvature] | ||||
| 
 | ||||
|     dat.liveMapData.mapValid = map_valid | ||||
| 
 | ||||
|     map_data_sock.send(dat.to_bytes()) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|   params = Params() | ||||
|   dongle_id = params.get("DongleId") | ||||
|   crash.bind_user(id=dongle_id) | ||||
|   crash.bind_extra(version=version, dirty=dirty, is_eon=True) | ||||
|   crash.install() | ||||
| 
 | ||||
|   main_thread = threading.Thread(target=mapsd_thread) | ||||
|   main_thread.daemon = True | ||||
|   main_thread.start() | ||||
| 
 | ||||
|   q_thread = threading.Thread(target=query_thread) | ||||
|   q_thread.daemon = True | ||||
|   q_thread.start() | ||||
| 
 | ||||
|   while True: | ||||
|     time.sleep(0.1) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   main() | ||||
| @ -1,364 +0,0 @@ | ||||
| import math | ||||
| import json | ||||
| import numpy as np | ||||
| from datetime import datetime | ||||
| from common.basedir import BASEDIR | ||||
| from selfdrive.config import Conversions as CV | ||||
| from common.transformations.coordinates import LocalCoord, geodetic2ecef | ||||
| 
 | ||||
| LOOKAHEAD_TIME = 10. | ||||
| MAPS_LOOKAHEAD_DISTANCE = 50 * LOOKAHEAD_TIME | ||||
| 
 | ||||
| DEFAULT_SPEEDS_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds.json" | ||||
| DEFAULT_SPEEDS = {} | ||||
| with open(DEFAULT_SPEEDS_JSON_FILE, "rb") as f: | ||||
|   DEFAULT_SPEEDS = json.loads(f.read()) | ||||
| 
 | ||||
| DEFAULT_SPEEDS_BY_REGION_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds_by_region.json" | ||||
| DEFAULT_SPEEDS_BY_REGION = {} | ||||
| with open(DEFAULT_SPEEDS_BY_REGION_JSON_FILE, "rb") as f: | ||||
|   DEFAULT_SPEEDS_BY_REGION = json.loads(f.read()) | ||||
| 
 | ||||
| def circle_through_points(p1, p2, p3): | ||||
|   """Fits a circle through three points | ||||
|   Formulas from: http://www.ambrsoft.com/trigocalc/circle3d.htm""" | ||||
|   x1, y1, _ = p1 | ||||
|   x2, y2, _ = p2 | ||||
|   x3, y3, _ = p3 | ||||
| 
 | ||||
|   A = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2 | ||||
|   B = (x1**2 + y1**2) * (y3 - y2) + (x2**2 + y2**2) * (y1 - y3) + (x3**2 + y3**2) * (y2 - y1) | ||||
|   C = (x1**2 + y1**2) * (x2 - x3) + (x2**2 + y2**2) * (x3 - x1) + (x3**2 + y3**2) * (x1 - x2) | ||||
|   D = (x1**2 + y1**2) * (x3 * y2 - x2 * y3) + (x2**2 + y2**2) * (x1 * y3 - x3 * y1) + (x3**2 + y3**2) * (x2 * y1 - x1 * y2) | ||||
| 
 | ||||
|   return (-B / (2 * A), - C / (2 * A), np.sqrt((B**2 + C**2 - 4 * A * D) / (4 * A**2))) | ||||
| 
 | ||||
| def parse_speed_unit(max_speed): | ||||
|   """Converts a maxspeed string to m/s based on the unit present in the input. | ||||
|   OpenStreetMap defaults to kph if no unit is present. """ | ||||
| 
 | ||||
|   if not max_speed: | ||||
|     return None | ||||
| 
 | ||||
|   conversion = CV.KPH_TO_MS | ||||
|   if 'mph' in max_speed: | ||||
|     max_speed = max_speed.replace(' mph', '') | ||||
|     conversion = CV.MPH_TO_MS | ||||
|   try: | ||||
|     return float(max_speed) * conversion | ||||
|   except ValueError: | ||||
|     return None | ||||
| 
 | ||||
| def parse_speed_tags(tags): | ||||
|   """Parses tags on a way to find the maxspeed string""" | ||||
|   max_speed = None | ||||
| 
 | ||||
|   if 'maxspeed' in tags: | ||||
|     max_speed = tags['maxspeed'] | ||||
| 
 | ||||
|   if 'maxspeed:conditional' in tags: | ||||
|     try: | ||||
|       max_speed_cond, cond = tags['maxspeed:conditional'].split(' @ ') | ||||
|       cond = cond[1:-1] | ||||
| 
 | ||||
|       start, end = cond.split('-') | ||||
|       now = datetime.now()  # TODO: Get time and timezone from gps fix so this will work correctly on replays | ||||
|       start = datetime.strptime(start, "%H:%M").replace(year=now.year, month=now.month, day=now.day) | ||||
|       end = datetime.strptime(end, "%H:%M").replace(year=now.year, month=now.month, day=now.day) | ||||
| 
 | ||||
|       if start <= now <= end: | ||||
|         max_speed = max_speed_cond | ||||
|     except ValueError: | ||||
|       pass | ||||
| 
 | ||||
|   if not max_speed and 'source:maxspeed' in tags: | ||||
|     max_speed = DEFAULT_SPEEDS.get(tags['source:maxspeed'], None) | ||||
|   if not max_speed and 'maxspeed:type' in tags: | ||||
|     max_speed = DEFAULT_SPEEDS.get(tags['maxspeed:type'], None) | ||||
| 
 | ||||
|   max_speed = parse_speed_unit(max_speed) | ||||
|   return max_speed | ||||
| 
 | ||||
| def geocode_maxspeed(tags, location_info): | ||||
|   max_speed = None | ||||
|   try: | ||||
|     geocode_country = location_info.get('country', '') | ||||
|     geocode_region = location_info.get('region', '') | ||||
| 
 | ||||
|     country_rules = DEFAULT_SPEEDS_BY_REGION.get(geocode_country, {}) | ||||
|     country_defaults = country_rules.get('Default', []) | ||||
|     for rule in country_defaults: | ||||
|       rule_valid = all( | ||||
|         tag_name in tags | ||||
|         and tags[tag_name] == value | ||||
|         for tag_name, value in rule['tags'].items() | ||||
|       ) | ||||
|       if rule_valid: | ||||
|         max_speed = rule['speed'] | ||||
|         break #stop searching country | ||||
| 
 | ||||
|     region_rules = country_rules.get(geocode_region, []) | ||||
|     for rule in region_rules: | ||||
|       rule_valid = all( | ||||
|         tag_name in tags | ||||
|         and tags[tag_name] == value | ||||
|         for tag_name, value in rule['tags'].items() | ||||
|       ) | ||||
|       if rule_valid: | ||||
|         max_speed = rule['speed'] | ||||
|         break #stop searching region | ||||
|   except KeyError: | ||||
|     pass | ||||
|   max_speed = parse_speed_unit(max_speed) | ||||
|   return max_speed | ||||
| 
 | ||||
| class Way: | ||||
|   def __init__(self, way, query_results): | ||||
|     self.id = way.id | ||||
|     self.way = way | ||||
|     self.query_results = query_results | ||||
| 
 | ||||
|     points = list() | ||||
| 
 | ||||
|     for node in self.way.get_nodes(resolve_missing=False): | ||||
|       points.append((float(node.lat), float(node.lon), 0.)) | ||||
| 
 | ||||
|     self.points = np.asarray(points) | ||||
| 
 | ||||
|   @classmethod | ||||
|   def closest(cls, query_results, lat, lon, heading, prev_way=None): | ||||
|     results, tree, real_nodes, node_to_way, location_info = query_results | ||||
| 
 | ||||
|     cur_pos = geodetic2ecef((lat, lon, 0)) | ||||
|     nodes = tree.query_ball_point(cur_pos, 500) | ||||
| 
 | ||||
|     # If no nodes within 500m, choose closest one | ||||
|     if not nodes: | ||||
|       nodes = [tree.query(cur_pos)[1]] | ||||
| 
 | ||||
|     ways = [] | ||||
|     for n in nodes: | ||||
|       real_node = real_nodes[n] | ||||
|       ways += node_to_way[real_node.id] | ||||
|     ways = set(ways) | ||||
| 
 | ||||
|     closest_way = None | ||||
|     best_score = None | ||||
|     for way in ways: | ||||
|       way = Way(way, query_results) | ||||
|       points = way.points_in_car_frame(lat, lon, heading) | ||||
| 
 | ||||
|       on_way = way.on_way(lat, lon, heading, points) | ||||
|       if not on_way: | ||||
|         continue | ||||
| 
 | ||||
|       # Create mask of points in front and behind | ||||
|       x = points[:, 0] | ||||
|       y = points[:, 1] | ||||
|       angles = np.arctan2(y, x) | ||||
|       front = np.logical_and((-np.pi / 2) < angles, | ||||
|                                 angles < (np.pi / 2)) | ||||
|       behind = np.logical_not(front) | ||||
| 
 | ||||
|       dists = np.linalg.norm(points, axis=1) | ||||
| 
 | ||||
|       # Get closest point behind the car | ||||
|       dists_behind = np.copy(dists) | ||||
|       dists_behind[front] = np.NaN | ||||
|       closest_behind = points[np.nanargmin(dists_behind)] | ||||
| 
 | ||||
|       # Get closest point in front of the car | ||||
|       dists_front = np.copy(dists) | ||||
|       dists_front[behind] = np.NaN | ||||
|       closest_front = points[np.nanargmin(dists_front)] | ||||
| 
 | ||||
|       # fit line: y = a*x + b | ||||
|       x1, y1, _ = closest_behind | ||||
|       x2, y2, _ = closest_front | ||||
|       a = (y2 - y1) / max((x2 - x1), 1e-5) | ||||
|       b = y1 - a * x1 | ||||
| 
 | ||||
|       # With a factor of 60 a 20m offset causes the same error as a 20 degree heading error | ||||
|       # (A 20 degree heading offset results in an a of about 1/3) | ||||
|       score = abs(a) * 60. + abs(b) | ||||
| 
 | ||||
|       # Prefer same type of road | ||||
|       if prev_way is not None: | ||||
|         if way.way.tags.get('highway', '') == prev_way.way.tags.get('highway', ''): | ||||
|           score *= 0.5 | ||||
| 
 | ||||
|       if closest_way is None or score < best_score: | ||||
|         closest_way = way | ||||
|         best_score = score | ||||
| 
 | ||||
|     # Normal score is < 5 | ||||
|     if best_score > 50: | ||||
|       return None | ||||
| 
 | ||||
|     return closest_way | ||||
| 
 | ||||
|   def __str__(self): | ||||
|     return "%s %s" % (self.id, self.way.tags) | ||||
| 
 | ||||
|   def max_speed(self): | ||||
|     """Extracts the (conditional) speed limit from a way""" | ||||
|     if not self.way: | ||||
|       return None | ||||
| 
 | ||||
|     max_speed = parse_speed_tags(self.way.tags) | ||||
|     if not max_speed: | ||||
|       location_info = self.query_results[4] | ||||
|       max_speed = geocode_maxspeed(self.way.tags, location_info) | ||||
| 
 | ||||
|     return max_speed | ||||
| 
 | ||||
|   def max_speed_ahead(self, current_speed_limit, lat, lon, heading, lookahead): | ||||
|     """Look ahead for a max speed""" | ||||
|     if not self.way: | ||||
|       return None | ||||
| 
 | ||||
|     speed_ahead = None | ||||
|     speed_ahead_dist = None | ||||
|     lookahead_ways = 5 | ||||
|     way = self | ||||
|     for i in range(lookahead_ways): | ||||
|       way_pts = way.points_in_car_frame(lat, lon, heading) | ||||
| 
 | ||||
|       # Check current lookahead distance | ||||
|       max_dist = np.linalg.norm(way_pts[-1, :]) | ||||
| 
 | ||||
|       if max_dist > 2 * lookahead: | ||||
|         break | ||||
| 
 | ||||
|       if 'maxspeed' in way.way.tags: | ||||
|         spd = parse_speed_tags(way.way.tags) | ||||
|         if not spd: | ||||
|           location_info = self.query_results[4] | ||||
|           spd = geocode_maxspeed(way.way.tags, location_info) | ||||
|         if spd < current_speed_limit: | ||||
|           speed_ahead = spd | ||||
|           min_dist = np.linalg.norm(way_pts[1, :]) | ||||
|           speed_ahead_dist = min_dist | ||||
|           break | ||||
|       # Find next way | ||||
|       way = way.next_way() | ||||
|       if not way: | ||||
|         break | ||||
| 
 | ||||
|     return speed_ahead, speed_ahead_dist | ||||
| 
 | ||||
|   def advisory_max_speed(self): | ||||
|     if not self.way: | ||||
|       return None | ||||
| 
 | ||||
|     tags = self.way.tags | ||||
|     adv_speed = None | ||||
| 
 | ||||
|     if 'maxspeed:advisory' in tags: | ||||
|       adv_speed = tags['maxspeed:advisory'] | ||||
|       adv_speed = parse_speed_unit(adv_speed) | ||||
|     return adv_speed | ||||
| 
 | ||||
|   def on_way(self, lat, lon, heading, points=None): | ||||
|     if points is None: | ||||
|       points = self.points_in_car_frame(lat, lon, heading) | ||||
|     x = points[:, 0] | ||||
|     return np.min(x) < 0. and np.max(x) > 0. | ||||
| 
 | ||||
|   def closest_point(self, lat, lon, heading, points=None): | ||||
|     if points is None: | ||||
|       points = self.points_in_car_frame(lat, lon, heading) | ||||
|     i = np.argmin(np.linalg.norm(points, axis=1)) | ||||
|     return points[i] | ||||
| 
 | ||||
|   def distance_to_closest_node(self, lat, lon, heading, points=None): | ||||
|     if points is None: | ||||
|       points = self.points_in_car_frame(lat, lon, heading) | ||||
|     return np.min(np.linalg.norm(points, axis=1)) | ||||
| 
 | ||||
|   def points_in_car_frame(self, lat, lon, heading): | ||||
|     lc = LocalCoord.from_geodetic([lat, lon, 0.]) | ||||
| 
 | ||||
|     # Build rotation matrix | ||||
|     heading = math.radians(-heading + 90) | ||||
|     c, s = np.cos(heading), np.sin(heading) | ||||
|     rot = np.array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]]) | ||||
| 
 | ||||
|     # Convert to local coordinates | ||||
|     points_carframe = lc.geodetic2ned(self.points).T | ||||
| 
 | ||||
|     # Rotate with heading of car | ||||
|     points_carframe = np.dot(rot, points_carframe[(1, 0, 2), :]).T | ||||
| 
 | ||||
|     return points_carframe | ||||
| 
 | ||||
|   def next_way(self, backwards=False): | ||||
|     results, tree, real_nodes, node_to_way, location_info = self.query_results | ||||
| 
 | ||||
|     if backwards: | ||||
|       node = self.way.nodes[0] | ||||
|     else: | ||||
|       node = self.way.nodes[-1] | ||||
| 
 | ||||
|     ways = node_to_way[node.id] | ||||
| 
 | ||||
|     way = None | ||||
|     try: | ||||
|       # Simple heuristic to find next way | ||||
|       ways = [w for w in ways if w.id != self.id] | ||||
|       ways = [w for w in ways if w.nodes[0] == node] | ||||
| 
 | ||||
|       # Filter on highway tag | ||||
|       acceptable_tags = list() | ||||
|       cur_tag = self.way.tags['highway'] | ||||
|       acceptable_tags.append(cur_tag) | ||||
|       if cur_tag == 'motorway_link': | ||||
|         acceptable_tags.append('motorway') | ||||
|         acceptable_tags.append('trunk') | ||||
|         acceptable_tags.append('primary') | ||||
|       ways = [w for w in ways if w.tags['highway'] in acceptable_tags] | ||||
| 
 | ||||
|       # Filter on number of lanes | ||||
|       cur_num_lanes = int(self.way.tags['lanes']) | ||||
|       if len(ways) > 1: | ||||
|         ways_same_lanes = [w for w in ways if int(w.tags['lanes']) == cur_num_lanes] | ||||
|         if len(ways_same_lanes) == 1: | ||||
|           ways = ways_same_lanes | ||||
|       if len(ways) > 1: | ||||
|         ways = [w for w in ways if int(w.tags['lanes']) > cur_num_lanes] | ||||
|       if len(ways) == 1: | ||||
|         way = Way(ways[0], self.query_results) | ||||
| 
 | ||||
|     except (KeyError, ValueError): | ||||
|       pass | ||||
| 
 | ||||
|     return way | ||||
| 
 | ||||
|   def get_lookahead(self, lat, lon, heading, lookahead): | ||||
|     pnts = None | ||||
|     way = self | ||||
|     valid = False | ||||
| 
 | ||||
|     for i in range(5): | ||||
|       # Get new points and append to list | ||||
|       new_pnts = way.points_in_car_frame(lat, lon, heading) | ||||
| 
 | ||||
|       if pnts is None: | ||||
|         pnts = new_pnts | ||||
|       else: | ||||
|         pnts = np.vstack([pnts, new_pnts]) | ||||
| 
 | ||||
|       # Check current lookahead distance | ||||
|       max_dist = np.linalg.norm(pnts[-1, :]) | ||||
|       if max_dist > lookahead: | ||||
|         valid = True | ||||
| 
 | ||||
|       if max_dist > 2 * lookahead: | ||||
|         break | ||||
| 
 | ||||
|       # Find next way | ||||
|       way = way.next_way() | ||||
|       if not way: | ||||
|         break | ||||
| 
 | ||||
|     return pnts, valid | ||||
					Loading…
					
					
				
		Reference in new issue