import json import math import requests import haversine # Google API key, with access to Street View Static API google_api_key = "AIzaSyAqjCYR6Szph0X0H_iD6O1HenFhL9jySOo" metadata_url = "https://maps.googleapis.com/maps/api/streetview/metadata" mapcrunch_url = "http://www.mapcrunch.com/_r/" def generate_coord(max_retries=100000): """ Returns (latitude, longitude) of usable coord (where google has data). This function will attempt at most max_retries calls to map crunch to fetch candidate points, and will exit as soon as a suitable candidate is found. This function calls the streetview metadata endpoint - there is no quota consumed """ for _ in range(max_retries): points_res = requests.get(mapcrunch_url).text points_js = json.loads(points_res.strip("while(1); ")) for lat, lng in points_js["points"]: params = { "key": google_api_key, "location": f"{lat},{lng}", } js = requests.get(metadata_url, params=params).json() if js["status"] == "OK": return (lat, lng) mean_earth_radius_km = (6378 + 6357) / 2 # if you're more than 1/4 of the Earth's circumfrence away, you get 0 max_dist_km = (math.pi * mean_earth_radius_km) / 2 # this is about 10,000 km # if you're within 1/16 of the Earth's circumfrence away, you get at least 1000 points quarter_of_max_km = max_dist_km / 4 # this is about 2,500 km # https://www.wolframalpha.com/input/?i=sqrt%28%28%28land+mass+of+earth%29+%2F+7%29%29+%2F+pi%29+in+kilometers # this is the average "radius" of a continent # within this radius, you get at least 2000 points avg_continental_rad_km = 1468.0 # somewhat arbitrarily, if you're within 1000 km, you get at least 3000 points one_thousand = 1000.0 # 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 # this is the average "radius" of a country # within this radius, you get at least 4000 points avg_country_rad_km = 479.7 # if you're within 150m, you get a perfect score of 5000 min_dist_km = 0.15 def score_within(raw_dist, min_dist, max_dist): """ Gives a score between 0 and 1000, with 1000 for the min_dist and 0 for the max_dist """ # scale the distance down to [0.0, 1.0], then multiply it by 2 for easing pd2 = 2 * (raw_dist - min_dist) / (max_dist - min_dist) # perform a quadratic ease-in-out on pd2 r = (pd2 ** 2) / 2 if pd2 < 1 else 1 - (((2 - pd2) ** 2) / 2) # use this to ease between 1000 and 0 return int(1000 * (1 - r)) def score(target, guess): """ Takes in two (latitude, longitude) pairs and produces an int score. Score is in the (inclusive) range [0, 5000] Higher scores are closer. Returns (score, distance in km) """ dist_km = haversine.haversine(target, guess) if dist_km <= min_dist_km: point_score = 5000 elif dist_km <= avg_country_rad_km: point_score = 4000 + score_within(dist_km, min_dist_km, avg_country_rad_km) elif dist_km <= one_thousand: point_score = 3000 + score_within(dist_km, avg_country_rad_km, one_thousand) elif dist_km <= avg_continental_rad_km: point_score = 2000 + score_within(dist_km, one_thousand, avg_continental_rad_km) elif dist_km <= quarter_of_max_km: point_score = 1000 + score_within(dist_km, avg_continental_rad_km, quarter_of_max_km) elif dist_km <= max_dist_km: point_score = score_within(dist_km, quarter_of_max_km, max_dist_km) else: # dist_km > max_dist_km point_score = 0 return point_score, dist_km