lib.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import json
  2. import math
  3. import requests
  4. import haversine
  5. # Google API key, with access to Street View Static API
  6. google_api_key = "AIzaSyAqjCYR6Szph0X0H_iD6O1HenFhL9jySOo"
  7. metadata_url = "https://maps.googleapis.com/maps/api/streetview/metadata"
  8. mapcrunch_url = "http://www.mapcrunch.com/_r/"
  9. def generate_coord(max_retries=100, only_america=False):
  10. """
  11. Returns (latitude, longitude) of usable coord (where google has data).
  12. This function will attempt at most max_retries calls to map crunch to fetch
  13. candidate points, and will exit as soon as a suitable candidate is found.
  14. This function calls the streetview metadata endpoint - there is no quota consumed
  15. """
  16. mc_url = mapcrunch_url + ("?c=21" if only_america else "")
  17. for _ in range(max_retries):
  18. points_res = requests.get(mc_url).text
  19. points_js = json.loads(points_res.strip("while(1); "))
  20. if "c=" not in mc_url:
  21. mc_url += f"?c={points_js['country']}" # lock to the first country randomed
  22. for lat, lng in points_js["points"]:
  23. params = {
  24. "key": google_api_key,
  25. "location": f"{lat},{lng}",
  26. }
  27. js = requests.get(metadata_url, params=params).json()
  28. if js["status"] == "OK":
  29. return (lat, lng)
  30. mean_earth_radius_km = (6378 + 6357) / 2
  31. # if you're more than 1/4 of the Earth's circumfrence away, you get 0
  32. max_dist_km = (math.pi * mean_earth_radius_km) / 2 # this is about 10,000 km
  33. # if you're within 1/16 of the Earth's circumfrence away, you get at least 1000 points
  34. quarter_of_max_km = max_dist_km / 4 # this is about 2,500 km
  35. # https://www.wolframalpha.com/input/?i=sqrt%28%28%28land+mass+of+earth%29+%2F+7%29%29+%2F+pi%29+in+kilometers
  36. # this is the average "radius" of a continent
  37. # within this radius, you get at least 2000 points
  38. avg_continental_rad_km = 1468.0
  39. # somewhat arbitrarily, if you're within 1000 km, you get at least 3000 points
  40. one_thousand = 1000.0
  41. # https://www.wolframalpha.com/input/?i=sqrt%28%28%28land+mass+of+earth%29+%2F+%28number+of+countries+on+earth%29%29+%2F+pi%29+in+kilometers
  42. # this is the average "radius" of a country
  43. # within this radius, you get at least 4000 points
  44. avg_country_rad_km = 479.7
  45. # if you're within 150m, you get a perfect score of 5000
  46. min_dist_km = 0.15
  47. def score_within(raw_dist, min_dist, max_dist):
  48. """
  49. Gives a score between 0 and 1000, with 1000 for the min_dist and 0 for the max_dist
  50. """
  51. # scale the distance down to [0.0, 1.0], then multiply it by 2 for easing
  52. pd2 = 2 * (raw_dist - min_dist) / (max_dist - min_dist)
  53. # perform a quadratic ease-in-out on pd2
  54. r = (pd2 ** 2) / 2 if pd2 < 1 else 1 - (((2 - pd2) ** 2) / 2)
  55. # use this to ease between 1000 and 0
  56. return int(1000 * (1 - r))
  57. def score(target, guess):
  58. """
  59. Takes in two (latitude, longitude) pairs and produces an int score.
  60. Score is in the (inclusive) range [0, 5000]
  61. Higher scores are closer.
  62. Returns (score, distance in km)
  63. """
  64. dist_km = haversine.haversine(target, guess)
  65. if dist_km <= min_dist_km:
  66. point_score = 5000
  67. elif dist_km <= avg_country_rad_km:
  68. point_score = 4000 + score_within(dist_km, min_dist_km, avg_country_rad_km)
  69. elif dist_km <= one_thousand:
  70. point_score = 3000 + score_within(dist_km, avg_country_rad_km, one_thousand)
  71. elif dist_km <= avg_continental_rad_km:
  72. point_score = 2000 + score_within(dist_km, one_thousand, avg_continental_rad_km)
  73. elif dist_km <= quarter_of_max_km:
  74. point_score = 1000 + score_within(dist_km, avg_continental_rad_km, quarter_of_max_km)
  75. elif dist_km <= max_dist_km:
  76. point_score = score_within(dist_km, quarter_of_max_km, max_dist_km)
  77. else: # dist_km > max_dist_km
  78. point_score = 0
  79. return point_score, dist_km