changeset 7:254195d0bac2

partial implementation of autocomplete using jqueryui; easyautocomplete.com may be more what we want
author Jeff Hammel <k0scist@gmail.com>
date Sun, 25 Jun 2017 09:13:48 -0700
parents 316e1d54ffd4
children e3d6919130ca
files globalneighbors/distance.py globalneighbors/grid.py globalneighbors/templates/index.html globalneighbors/web.py tests/test_distance.py tests/test_grid.py
diffstat 6 files changed, 87 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/globalneighbors/distance.py	Sat Jun 24 15:47:59 2017 -0700
+++ b/globalneighbors/distance.py	Sun Jun 25 09:13:48 2017 -0700
@@ -38,6 +38,9 @@
     keeping distances in order with maximum of size `k`
     """
 
+    if len(distances) == k and new_distance >= distances[-1][-1]:
+        return
+
      # TODO: Binary Search Tree
     for _index, (geoid, old_distance) in enumerate(distances):
         if new_distance < old_distance:
@@ -48,14 +51,18 @@
     else:
         distances.append((i, new_distance))
 
-# def insert_distance(distances, i, new_distance, k):
-#     if not distances:
-#          distances.append((i, new_distance))
-#          return
-#     if new_distance >= distances[-1][-1]:
-#            if len(distances) < k:
-#                distances.append((i, new_distance))
-#            return
+
+def insert_distance_bisect(distances, i, new_distance, k):
+    if not distances:
+        distances.append((i, new_distance))
+        return
+    if new_distance >= distances[-1][-1]:
+        if len(distances) < k:
+            distances.append((i, new_distance))
+        return
+    indices = [0, len(distances)]
+    while True:
+        midpoint = int((indices[-1] - indices[0])/2)
 
 
 def calculate_distances(locations, r=Rearth):
@@ -124,8 +131,6 @@
             # insert in order
             for i in (id1, id2):
                 distances = neighbors.setdefault(i, [])
-                if len(distances) == k and new_distance >= distances[-1][-1]:
-                    continue
 
                 insert_distance(distances, i, new_distance, k)
 
--- a/globalneighbors/grid.py	Sat Jun 24 15:47:59 2017 -0700
+++ b/globalneighbors/grid.py	Sun Jun 25 09:13:48 2017 -0700
@@ -35,10 +35,24 @@
         """
         return neighbors of points i, j
         """
-        imat = []
-        jmat = []
+        imat = [i]
+        jmat = [j]
 
         # latitude
         if i:
             imat.append(i-1)
-            raise NotImplementedError('TODO')
+        if i < self.n[0]-1:
+            imat.append(i+1)
+
+        # longitude: wraps around
+        if j:
+            jmat.append(j-1)
+        else:
+            jmat.append(self.n[-1] - 1)
+        if j == self.n[-1] - 1:
+            jmat.append(0)
+        else:
+            jmat.append(j+1)
+
+        return [(_i, _j) for _i in imat for _j in jmat
+                if (_i,_j) != (i,j)]
--- a/globalneighbors/templates/index.html	Sat Jun 24 15:47:59 2017 -0700
+++ b/globalneighbors/templates/index.html	Sun Jun 25 09:13:48 2017 -0700
@@ -2,9 +2,27 @@
 <html>
   <head>
     <title>Global Neighbors</title>
+    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
+    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
+    <script>
+      $( function() {
+      $( "#cities" ).autocomplete({
+      source: "/cities",
+      autoFocus: true,
+      minLength: 3,
+      delay: 3000
+      });
+      });
+    </script>
   </head>
   <body>
     <h1>Global Neighbors</h1>
     <h2>Serving {{ n_cities }} cities</h2>
+
+    <div class="ui-widget">
+      <label for="cities">Enter a city: </label>
+      <input id="cities"/>
+    </div>
+
   </body>
 </html>
--- a/globalneighbors/web.py	Sat Jun 24 15:47:59 2017 -0700
+++ b/globalneighbors/web.py	Sun Jun 25 09:13:48 2017 -0700
@@ -22,21 +22,20 @@
 from .template import TemplateLoader
 
 
-def autocomplete(cities, startswith=None):
+def autocomplete(cities, startswith=None, limit=None):
     """autocomplete function for city names"""
-    ### TODO: sort once, ahead of time
+    ### TODO:
+    # - sort once, ahead of time
+    # - return most populous cities
 
     if startswith:
         retval = []
         for i in cities:
-            try:
-                if i[name].startswith(startswith):
+            if i[name].startswith(startswith):
                     retval.append(i[name])
-            except Exception as e:
-                import pdb; pdb.set_trace()
-        return sorted(retval)
+        return sorted(retval)[:limit]
     else:
-        return sorted([i[name] for i in cities])
+        return sorted([i[name] for i in cities])[:limit]
 
 
 class Handler(object):
@@ -71,7 +70,7 @@
     def GET(self, request):
         return Response(content_type=self.content_type,
                         body=json.dumps(self.cities(
-                            startswith=request.GET.get('q'))))
+                            startswith=request.GET.get('term'))))
 
 
 class GlobalHandler(Handler):
--- a/tests/test_distance.py	Sat Jun 24 15:47:59 2017 -0700
+++ b/tests/test_distance.py	Sun Jun 25 09:13:48 2017 -0700
@@ -6,6 +6,7 @@
 
 import math
 import os
+import random
 import unittest
 from globalneighbors import distance
 from globalneighbors.constants import Rearth
@@ -127,6 +128,23 @@
                 distances = [i[-1] for i in value]
                 assert distances == sorted(distances)
 
+    def test_insert_distances(self):
+        """test insert distances algorithm"""
+
+        values = [(i, random.random())
+                  for i in range(1500)]
+        for k in (10, 100, 1000):
+            _distances = []
+            for i, value in values:
+                distance.insert_distance(_distances,
+                                         i,
+                                         value,
+                                         k)
+            # since k is < 1500
+            assert len(_distances) == k
+            ordered = [value[-1] for value in _distances]
+            assert sorted(ordered) == ordered
+
 
 if __name__ == '__main__':
     unittest.main()
--- a/tests/test_grid.py	Sat Jun 24 15:47:59 2017 -0700
+++ b/tests/test_grid.py	Sun Jun 25 09:13:48 2017 -0700
@@ -54,6 +54,17 @@
             city_locations = locations(read_cities(f))
         self.grid_locations(city_locations)
 
+    def test_neighbors(self):
+        """test grid neighbor indexing"""
+
+        grid = LatLonGrid(9, 9)
+
+        neighbors = grid.neighbors(5,5)
+        expected = [(4,4), (4,5), (4,6),
+                    (5,4), (5,6),
+                    (6,4), (6,5), (6,6)]
+        assert sorted(neighbors) == sorted(expected)
+
 
     ### generic (utility) functions