Selaa lähdekoodia

A first pass at the PointSource interface for keeping points stocked lazily to speed up game creation

Kirk Trombley 5 vuotta sitten
vanhempi
commit
178b941c43
1 muutettua tiedostoa jossa 91 lisäystä ja 3 poistoa
  1. 91 3
      server/lib.py

+ 91 - 3
server/lib.py

@@ -1,6 +1,8 @@
 import json
 import math
 import random
+import threading
+import collections
 
 import requests
 import haversine
@@ -20,7 +22,6 @@ with open("./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())))
-urban_usa_chance = 0.1
 
 
 def point_has_streetview(lat, lng):
@@ -93,7 +94,7 @@ def random_street_view_generator(only_america=False):
             yield points.pop()
 
 
-def urban_coord(max_retries=10, retries_per_point=30, max_dist_km=25, only_america=False):
+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
@@ -104,7 +105,7 @@ def urban_coord(max_retries=10, retries_per_point=30, max_dist_km=25, only_ameri
     This function calls the streetview metadata endpoint - there is no quota consumed.
     """
 
-    src = urban_centers_usa if only_america or random.random() <= urban_usa_chance else urban_centers_non_usa
+    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
@@ -129,6 +130,93 @@ def urban_coord(max_retries=10, retries_per_point=30, max_dist_km=25, only_ameri
                 return (pt_lat, pt_lng)
 
 
+class PointSource:
+    def __init__(self, stock_target):
+        self.stock = collections.deque()
+        self.stock_target = stock_target
+
+    def _restock_impl(self, n):
+        """
+        Returns a list of new points to add to the stock.
+        Implementations of this method should try to return at least n points for performance.
+        """
+        raise NotImplementedError("Subclasses must implement this")
+    
+    def restock(self, n=None):
+        n = n if n is not None else self.stock_target - len(self.stock)
+        if n > 0:
+            pts = self._restock_impl(n)
+            self.stock.extend(pts)
+            diff = n - len(pts)
+            if diff > 0:
+                # if implementations of _restock_impl are well behaved, this will
+                # never actually need to recurse to finish the job.
+                self.restock(n=diff)
+
+    def get_points(self, n=1):
+        if len(self.stock) >= n:
+            pts = []
+            for _ in range(n):
+                pts.append(self.stock.popleft())
+            threading.Thread(target=self.restock).start()
+            return pts
+        self.restock(n=n)
+        # this is safe as long as restock does actually add enough new points.
+        # unless this object is being rapidly drained by another thread,
+        # this will recur at most once.
+        return self.get_points(n=n)
+
+
+class MapCrunchPointSource(PointSource):
+    def __init__(self, stock_target=20, max_retries=100, only_america=False):
+        super().__init__(stock_target=stock_target)
+        self.max_retries = max_retries
+        self.only_america = only_america
+
+    def _restock_impl(self, n):
+        points = []
+        while len(points) < n:
+            pt = generate_coord(
+                max_retries=self.max_retries,
+                only_america=self.only_america
+            )
+            if pt is not None:
+                points.append(pt)
+
+
+class RSVPointSource(PointSource):
+    def __init__(self, stock_target=20, only_america=False):
+        super().__init__(stock_target=stock_target)
+        self.only_america = only_america
+    
+    def _restock_impl(self, n):
+        points = []
+        while len(points) < n:
+            points.extend(call_random_street_view(only_america=self.only_america))
+        return points
+
+
+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
+    
+    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)
+
+
 mean_earth_radius_km = (6378 + 6357) / 2
 
 # if you're more than 1/4 of the Earth's circumfrence away, you get 0