123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import math
- import random
- from .shared import point_has_streetview, PointSource
- from ..scoring import mean_earth_radius_km
- initialized = False
- urban_centers_usa = []
- urban_centers_non_usa = []
- def init():
- """
- Read in the urban centers data files. Should be called before trying to generate points.
- """
- global initialized
- if initialized:
- return
- with open("./data/urban-centers-usa.csv") as infile:
- for line in infile:
- lat, lng = line.split(",")
- urban_centers_usa.append((float(lat.strip()), float(lng.strip())))
- with open("./data/urban-centers-non-usa.csv") as infile:
- for line in infile:
- lat, lng = line.split(",")
- urban_centers_non_usa.append((float(lat.strip()), float(lng.strip())))
- initialized = True
- def urban_coord(max_retries=10, retries_per_point=30, max_dist_km=25, usa_chance=0.1):
- """
- Returns (latitude, longitude) of usable coord (where google has data) that is near
- a known urban center. Points will be at most max_dist_km kilometers away. This function will
- generate at most retries_per_point points around an urban center, and will try at most
- max_retries urban centers. If none of the generated points have street view data,
- this will return None. Otherwise, it will exit as soon as suitable point is found.
- This function calls the streetview metadata endpoint - there is no quota consumed.
- """
- src = urban_centers_usa if random.random() <= usa_chance else urban_centers_non_usa
- for _ in range(max_retries):
- # logic adapted from https://stackoverflow.com/a/7835325
- # start in a city
- (city_lat, city_lng) = random.choice(src)
- city_lat_rad = math.radians(city_lat)
- sin_lat = math.sin(city_lat_rad)
- cos_lat = math.cos(city_lat_rad)
- city_lng_rad = math.radians(city_lng)
- for _ in range(retries_per_point):
- # turn a random direction, and go random distance
- dist_km = random.random() * max_dist_km
- angle_rad = random.random() * 2 * math.pi
- d_over_radius = dist_km / mean_earth_radius_km
- sin_dor = math.sin(d_over_radius)
- cos_dor = math.cos(d_over_radius)
- pt_lat_rad = math.asin(sin_lat * cos_dor + cos_lat * sin_dor * math.cos(angle_rad))
- pt_lng_rad = city_lng_rad + math.atan2(math.sin(angle_rad) * sin_dor * cos_lat, cos_dor - sin_lat * math.sin(pt_lat_rad))
- pt_lat = math.degrees(pt_lat_rad)
- pt_lng = math.degrees(pt_lng_rad)
- if point_has_streetview(pt_lat, pt_lng):
- return (pt_lat, pt_lng)
- class UrbanPointSource(PointSource):
- def __init__(self, stock_target=20, max_retries=10, retries_per_point=30, max_dist_km=25, usa_chance=0.1):
- super().__init__(stock_target=stock_target)
- self.max_retries = max_retries
- self.retries_per_point = retries_per_point
- self.max_dist_km = max_dist_km
- self.usa_chance = usa_chance
- if not initialized:
- init()
-
- def _restock_impl(self, n):
- points = []
- while len(points) < n:
- pt = urban_coord(
- max_retries=self.max_retries,
- retries_per_point=self.retries_per_point,
- max_dist_km=self.max_dist_km,
- usa_chance=self.usa_chance
- )
- if pt is not None:
- points.append(pt)
- return points
|