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