Exemple #1
0
    def test_adjacent(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        box = geocell.compute_box(cell)

        # adjacency tests using bounding boxes
        self.assertEquals(
            box.north,
            geocell.compute_box(geocell.adjacent(cell, (0, 1))).south)
        self.assertEquals(
            box.south,
            geocell.compute_box(geocell.adjacent(cell, (0, -1))).north)
        self.assertEquals(
            box.east,
            geocell.compute_box(geocell.adjacent(cell, (1, 0))).west)
        self.assertEquals(
            box.west,
            geocell.compute_box(geocell.adjacent(cell, (-1, 0))).east)

        self.assertEquals(8, len(geocell.all_adjacents(cell)))

        # also test collinearity
        self.assertTrue(
            geocell.collinear(cell, geocell.adjacent(cell, (0, 1)), True))
        self.assertFalse(
            geocell.collinear(cell, geocell.adjacent(cell, (0, 1)), False))
        self.assertTrue(
            geocell.collinear(cell, geocell.adjacent(cell, (1, 0)), False))
        self.assertFalse(
            geocell.collinear(cell, geocell.adjacent(cell, (1, 0)), True))
    def test_adjacent(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        box = geocell.compute_box(cell)

        # adjacency tests using bounding boxes
        self.assertEquals(box.north,
                geocell.compute_box(geocell.adjacent(cell, (0, 1))).south)
        self.assertEquals(box.south,
                geocell.compute_box(geocell.adjacent(cell, (0, -1))).north)
        self.assertEquals(box.east,
                geocell.compute_box(geocell.adjacent(cell, (1, 0))).west)
        self.assertEquals(box.west,
                geocell.compute_box(geocell.adjacent(cell, (-1, 0))).east)

        self.assertEquals(8, len(geocell.all_adjacents(cell)))

        # also test collinearity
        self.assertTrue(
                geocell.collinear(cell, geocell.adjacent(cell, (0, 1)), True))
        self.assertFalse(
                geocell.collinear(cell, geocell.adjacent(cell, (0, 1)), False))
        self.assertTrue(
                geocell.collinear(cell, geocell.adjacent(cell, (1, 0)), False))
        self.assertFalse(
                geocell.collinear(cell, geocell.adjacent(cell, (1, 0)), True))
Exemple #3
0
    def test_compute(self):
        # a valid geocell
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        self.assertEqual(14, len(cell))
        self.assertTrue(geocell.is_valid(cell))
        self.assertTrue(geocell.contains_point(cell, geotypes.Point(37, -122)))

        # a lower resolution cell should be a prefix to a higher resolution
        # cell containing the same point
        lowres_cell = geocell.compute(geotypes.Point(37, -122), 8)
        self.assertTrue(cell.startswith(lowres_cell))
        self.assertTrue(
            geocell.contains_point(lowres_cell, geotypes.Point(37, -122)))

        # an invalid geocell
        cell = geocell.compute(geotypes.Point(0, 0), 0)
        self.assertEqual(0, len(cell))
        self.assertFalse(geocell.is_valid(cell))
    def test_compute(self):
        # a valid geocell
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        self.assertEqual(14, len(cell))
        self.assertTrue(geocell.is_valid(cell))
        self.assertTrue(geocell.contains_point(cell, geotypes.Point(37, -122)))

        # a lower resolution cell should be a prefix to a higher resolution
        # cell containing the same point
        lowres_cell = geocell.compute(geotypes.Point(37, -122), 8)
        self.assertTrue(cell.startswith(lowres_cell))
        self.assertTrue(geocell.contains_point(lowres_cell,
                                                                                    geotypes.Point(37, -122)))

        # an invalid geocell
        cell = geocell.compute(geotypes.Point(0, 0), 0)
        self.assertEqual(0, len(cell))
        self.assertFalse(geocell.is_valid(cell))
Exemple #5
0
  def update_location(self):
    """Syncs underlying geocell properties with the entity's location.

    Updates the underlying geocell properties of the entity to match the
    entity's location property. A put() must occur after this call to save
    the changes to App Engine."""
    max_res_geocell = geocell.compute(self.location)
    for res in range(1, geocell.MAX_GEOCELL_RESOLUTION + 1):
      setattr(self, 'location_geocell_%d' % res, max_res_geocell[:res])
Exemple #6
0
  def update_location(self):
    """Syncs underlying geocell properties with the entity's location.

    Updates the underlying geocell properties of the entity to match the
    entity's location property. A put() must occur after this call to save
    the changes to App Engine."""
    max_res_geocell = geocell.compute(self.location)
    self.location_geocells = [max_res_geocell[:res]
                              for res in
                              range(1, geocell.MAX_GEOCELL_RESOLUTION + 1)]
Exemple #7
0
  def update_location(self, location=None):
    """Syncs underlying geocell properties with the entity's location.

    Updates the underlying geocell properties of the entity to match the
    entity's location property. A put() must occur after this call to save
    the changes to App Engine."""
    if location:
        self.location = location
    max_res_geocell = geocell.compute(self.location)
    self.location_geocells = [max_res_geocell[:res]
                              for res in
                              range(1, geocell.MAX_GEOCELL_RESOLUTION + 1)]
    def test_interpolation(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        sw_adjacent = geocell.adjacent(cell, (-1, -1))
        sw_adjacent2 = geocell.adjacent(sw_adjacent, (-1, -1))

        # interpolate between a cell and south-west adjacent, should return
        # 4 total cells
        self.assertEquals(4, len(geocell.interpolate(cell, sw_adjacent)))
        self.assertEquals(4, geocell.interpolation_count(cell, sw_adjacent))

        # interpolate between a cell and the cell SW-adjacent twice over,
        # should return 9 total cells
        self.assertEquals(9, len(geocell.interpolate(cell, sw_adjacent2)))
        self.assertEquals(9, geocell.interpolation_count(cell, sw_adjacent2))
Exemple #9
0
    def test_interpolation(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        sw_adjacent = geocell.adjacent(cell, (-1, -1))
        sw_adjacent2 = geocell.adjacent(sw_adjacent, (-1, -1))

        # interpolate between a cell and south-west adjacent, should return
        # 4 total cells
        self.assertEquals(4, len(geocell.interpolate(cell, sw_adjacent)))
        self.assertEquals(4, geocell.interpolation_count(cell, sw_adjacent))

        # interpolate between a cell and the cell SW-adjacent twice over,
        # should return 9 total cells
        self.assertEquals(9, len(geocell.interpolate(cell, sw_adjacent2)))
        self.assertEquals(9, geocell.interpolation_count(cell, sw_adjacent2))
Exemple #10
0
  def update_location(self):
    """Syncs underlying geocell properties with the entity's location.

    Updates the underlying geocell properties of the entity to match the
    entity's location property. A put() must occur after this call to save
    the changes to App Engine."""
    if self.location:
      logging.info("Updating Location: %s" % self.location)
      max_res_geocell = geocell.compute(self.location)
      self.location_geocells = [max_res_geocell[:res]
                                for res in
                                range(1, geocell.MAX_GEOCELL_RESOLUTION + 1)]
    else:
      logging.warn("Not updating location, not enough data!")
      self.location_geocells = []
Exemple #11
0
    def update_location(self):
        """Syncs underlying geocell properties with the entity's location.

    Updates the underlying geocell properties of the entity to match the
    entity's location property. A put() must occur after this call to save
    the changes to App Engine."""
        if self.location:
            logging.info("Updating Location: %s" % self.location)
            max_res_geocell = geocell.compute(self.location)
            self.location_geocells = [
                max_res_geocell[:res]
                for res in range(1, geocell.MAX_GEOCELL_RESOLUTION + 1)
            ]
        else:
            logging.warn("Not updating location, not enough data!")
            self.location_geocells = []
Exemple #12
0
 prop.price_rent_currency     = currency
 prop.price_rent_computed     = calculate_price(prop.price_rent, currency, 'ARS')
   
 prop.headline = u'%s en %s' % (  config_array['cells']['prop_type_id']['short_descriptions'][alphabet.index(prop.prop_type_id)]
                               , config_array['multiple_values_properties']['prop_operation_id']['descriptions'][prop.prop_operation_id])
 
 
 
 # geoPT.lat           = y0 + dy*j  #float(random.randrange(-34636033, -34552942)) / 1000000
 # geoPT.latitude      = geoPT.lat
 # geoPT.lon           = x0 + dx*i #float(random.randrange(-58499794, -58357315)) / 1000000
 # geoPT.longitude     = geoPT.lon
 
 point               = Point(y0 + dy*j, x0 + dx*i)
 REQ_location              = '"%f, %f"' % (point.lat, point.lon)
 max_res_geocell, primos   = compute(point, MAX_GEOCELL_RESOLUTION, True)
     
 tmp                       = [max_res_geocell[:res]
                               for res in
                                 range(1, MAX_GEOCELL_RESOLUTION + 1)]
 
 location_geocells         = sorted(tmp+primos)
 
 if len(location_geocells) > max_geocell_len:
   max_geocell_len  = len(location_geocells)
 
 location_geocells = location_geocells+prop.check_options_ex()
 
 if len(location_geocells) > max_allcell_len:
   max_allcell_len  = len(location_geocells)
   
Exemple #13
0
  def proximity_fetch(query, center, max_results=10, max_distance=0):
    """Performs a proximity/radius fetch on the given query.

    Fetches at most <max_results> entities matching the given query,
    ordered by ascending distance from the given center point, and optionally
    limited by the given maximum distance.

    This method uses a greedy algorithm that starts by searching high-resolution
    geocells near the center point and gradually looking in lower and lower
    resolution cells until max_results entities have been found matching the
    given query and no closer possible entities can be found.

    Args:
      query: A db.Query on entities of this kind.
      center: A geotypes.Point or db.GeoPt indicating the center point around
          which to search for matching entities.
      max_results: An int indicating the maximum number of desired results.
          The default is 10, and the larger this number, the longer the fetch
          will take.
      max_distance: An optional number indicating the maximum distance to
          search, in meters.

    Returns:
      The fetched entities, sorted in ascending order by distance to the search
      center.

    Raises:
      Any exceptions that google.appengine.ext.db.Query.fetch() can raise.
    """
    # TODO(romannurik): check for GqlQuery
    results = []

    searched_cells = set()

    # The current search geocell containing the lat,lon.
    cur_containing_geocell = geocell.compute(center)

    # The currently-being-searched geocells.
    # NOTES:
    #     * Start with max possible.
    #     * Must always be of the same resolution.
    #     * Must always form a rectangular region.
    #     * One of these must be equal to the cur_containing_geocell.
    cur_geocells = [cur_containing_geocell]

    closest_possible_next_result_dist = 0

    # Assumes both a and b are lists of (entity, dist) tuples, *sorted by dist*.
    # NOTE: This is an in-place merge, and there are guaranteed
    # no duplicates in the resulting list.
    def _merge_results_in_place(a, b):
      util.merge_in_place(a, b,
                        cmp_fn=lambda x, y: cmp(x[1], y[1]),
                        dup_fn=lambda x, y: x[0].key() == y[0].key())

    sorted_edges = [(0,0)]
    sorted_edge_distances = [0]

    while cur_geocells:
      closest_possible_next_result_dist = sorted_edge_distances[0]
      if max_distance and closest_possible_next_result_dist > max_distance:
        break

      cur_geocells_unique = list(set(cur_geocells).difference(searched_cells))

      # Run query on the next set of geocells.
      cur_resolution = len(cur_geocells[0])
      temp_query = copy.deepcopy(query)  # TODO(romannurik): is this safe?
      temp_query.filter('location_geocells IN', cur_geocells_unique)

      # Update results and sort.
      new_results = temp_query.fetch(1000)
      if DEBUG:
        logging.info('fetch complete for %s' % (','.join(cur_geocells_unique),))

      searched_cells.update(cur_geocells)

      # Begin storing distance from the search result entity to the
      # search center along with the search result itself, in a tuple.
      new_results = [(entity, geomath.distance(center, entity.location))
                     for entity in new_results]
      new_results = sorted(new_results, lambda dr1, dr2: cmp(dr1[1], dr2[1]))
      new_results = new_results[:max_results]

      # Merge new_results into results or the other way around, depending on
      # which is larger.
      if len(results) > len(new_results):
        _merge_results_in_place(results, new_results)
      else:
        _merge_results_in_place(new_results, results)
        results = new_results

      results = results[:max_results]

      sorted_edges, sorted_edge_distances = \
          util.distance_sorted_edges(cur_geocells, center)

      if len(results) == 0 or len(cur_geocells) == 4:
        # Either no results (in which case we optimize by not looking at
        # adjacents, go straight to the parent) or we've searched 4 adjacent
        # geocells, in which case we should now search the parents of those
        # geocells.
        cur_containing_geocell = cur_containing_geocell[:-1]
        cur_geocells = list(set([cell[:-1] for cell in cur_geocells]))
        if not cur_geocells or not cur_geocells[0]:
          break  # Done with search, we've searched everywhere.

      elif len(cur_geocells) == 1:
        # Get adjacent in one direction.
        # TODO(romannurik): Watch for +/- 90 degree latitude edge case geocells.
        nearest_edge = sorted_edges[0]
        cur_geocells.append(geocell.adjacent(cur_geocells[0], nearest_edge))

      elif len(cur_geocells) == 2:
        # Get adjacents in perpendicular direction.
        nearest_edge = util.distance_sorted_edges([cur_containing_geocell],
                                                   center)[0][0]
        if nearest_edge[0] == 0:
          # Was vertical, perpendicular is horizontal.
          perpendicular_nearest_edge = [x for x in sorted_edges if x[0] != 0][0]
        else:
          # Was horizontal, perpendicular is vertical.
          perpendicular_nearest_edge = [x for x in sorted_edges if x[0] == 0][0]

        cur_geocells.extend(
            [geocell.adjacent(cell, perpendicular_nearest_edge)
             for cell in cur_geocells])

      # We don't have enough items yet, keep searching.
      if len(results) < max_results:
        if DEBUG:
          logging.debug('have %d results but want %d results, '
                        'continuing search' % (len(results), max_results))
        continue

      if DEBUG:
        logging.debug('have %d results' % (len(results),))

      # If the currently max_results'th closest item is closer than any
      # of the next test geocells, we're done searching.
      current_farthest_returnable_result_dist = \
          geomath.distance(center, results[max_results - 1][0].location)
      if (closest_possible_next_result_dist >=
          current_farthest_returnable_result_dist):
        if DEBUG:
          logging.debug('DONE next result at least %f away, '
                        'current farthest is %f dist' %
                        (closest_possible_next_result_dist,
                         current_farthest_returnable_result_dist))
        break

      if DEBUG:
        logging.debug('next result at least %f away, '
                      'current farthest is %f dist' %
                      (closest_possible_next_result_dist,
                       current_farthest_returnable_result_dist))

    if DEBUG:
      logging.info('proximity query looked '
                   'in %d geocells' % len(searched_cells))

    return [entity for (entity, dist) in results[:max_results]
            if not max_distance or dist < max_distance]
    def test_compute_box(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        box = geocell.compute_box(cell)

        self.assertTrue(box.south <= 37 and 37 <= box.north and
                                        box.west <= -122 and -122 <= box.east)
    def proximity_fetch(query,
                        center,
                        max_results=10,
                        max_distance=0,
                        start_resolution=geocell.MAX_GEOCELL_RESOLUTION):
        """Performs a proximity/radius fetch on the given query.

    Fetches at most max_results entities matching the given query,
    ordered by ascending distance from the given center point, and optionally
    limited by the given maximum distance.

    This method uses a greedy algorithm that starts by searching high-resolution
    geocells near the center point and gradually looking in lower and lower
    resolution cells until max_results entities have been found matching the
    given query and no closer possible entities can be found.

    Args:
      query: A db.Query on entities of this kind.
      center: A geotypes.Point or db.GeoPt indicating the center point around
          which to search for matching entities.
      max_results: An int indicating the maximum number of desired results.
          The default is 10, and the larger this number, the longer the fetch
          will take.
      max_distance: An optional number indicating the maximum distance to
          search, in meters.
      start_resolution: An optional number indicating the geocell resolution
          to begin the search at. Larger values will result in longer response
          times on average, but are more efficient for extremely dense data.
          The default is geocell.MAX_GEOCELL_RESOLUTION

    Returns:
      The fetched entities, sorted in ascending order by distance to the search
      center.

    Raises:
      Any exceptions that google.appengine.ext.db.Query.fetch() can raise.
    """
        # TODO(romannurik): check for GqlQuery
        results = []

        searched_cells = set()

        # The current search geocell containing the lat,lon.
        cur_containing_geocell = geocell.compute(center, start_resolution)

        # The currently-being-searched geocells.
        # NOTES:
        #     * Start with max possible.
        #     * Must always be of the same resolution.
        #     * Must always form a rectangular region.
        #     * One of these must be equal to the cur_containing_geocell.
        cur_geocells = [cur_containing_geocell]

        closest_possible_next_result_dist = 0

        sorted_edge_dirs = [(0, 0)]
        sorted_edge_distances = [0]

        # Assumes both a and b are lists of (entity, dist) tuples, *sorted by dist*.
        # NOTE: This is an in-place merge, and there are guaranteed
        # no duplicates in the resulting list.
        def _merge_results_in_place(a, b):
            util.merge_in_place(a,
                                b,
                                cmp_fn=lambda x, y: cmp(x[1], y[1]),
                                dup_fn=lambda x, y: x[0].key() == y[0].key())

        def _first_horz(edges):
            return [x for x in edges if x[0] != 0][0]

        def _first_vert(edges):
            return [x for x in edges if x[0] == 0][0]

        while cur_geocells:
            cur_geocells_unique = list(
                set(cur_geocells).difference(searched_cells))
            cur_resolution = len(cur_geocells[0])

            # Run query on the next set of geocells.
            new_results = util.async_in_query_fetch(query,
                                                    'location_geocells',
                                                    cur_geocells_unique,
                                                    max_results=max_results *
                                                    len(cur_geocells_unique),
                                                    debug=DEBUG)

            # Update results and sort.
            searched_cells.update(cur_geocells)

            # Begin storing distance from the search result entity to the
            # search center along with the search result itself, in a tuple.
            new_results = [(entity, geomath.distance(center, entity.location))
                           for entity in new_results]
            new_results = sorted(new_results,
                                 lambda dr1, dr2: cmp(dr1[1], dr2[1]))
            new_results = new_results[:max_results]

            # Merge new_results into results or the other way around, depending on
            # which is larger.
            if len(results) > len(new_results):
                _merge_results_in_place(results, new_results)
            else:
                _merge_results_in_place(new_results, results)
                results = new_results

            results = results[:max_results]

            if DEBUG:
                logging.debug(('GeoModel Proximity Query: '
                               'Have %d results') % (len(results), ))

            if len(results) >= max_results:
                if DEBUG:
                    logging.debug(
                        ('GeoModel Proximity Query: '
                         'Wanted %d results, ending search') % (max_results, ))
                break

            # Determine the next set of geocells to search.
            sorted_edge_dirs, _ = \
                util.distance_sorted_edges(util.max_box(cur_geocells), center)

            if len(results) == 0 or len(cur_geocells) == 4:
                # Either no results (in which case we optimize by not looking at
                # adjacents, go straight to the parent) or we've searched 4 adjacent
                # geocells, in which case we should now search the parents of those
                # geocells.
                cur_containing_geocell = cur_containing_geocell[:-1]
                cur_geocells = list(set([cell[:-1] for cell in cur_geocells]))

                if not cur_geocells or not cur_geocells[0]:
                    break  # Done with search, we've searched everywhere.

                if len(cur_geocells) == 2:
                    # There are two parents for the 4 just-searched cells; get 2
                    # perpendicular adjacent parents to search a full set of 4 cells.
                    perp_dir = (_first_vert(sorted_edge_dirs)
                                if geocell.collinear(cur_geocells[0],
                                                     cur_geocells[1], False)
                                else _first_horz(sorted_edge_dirs))

                    cur_geocells.extend(
                        filter(lambda x: x is not None, [
                            geocell.adjacent(cur_geocells[0], perp_dir),
                            geocell.adjacent(cur_geocells[1], perp_dir)
                        ]))

            elif len(cur_geocells) == 1:
                # Searched one geocell, now search its 3 adjacents.
                horz_dir = _first_horz(sorted_edge_dirs)
                vert_dir = _first_vert(sorted_edge_dirs)
                diag_dir = (horz_dir[0], vert_dir[1])

                cur_geocells.extend(
                    filter(lambda x: x is not None, [
                        geocell.adjacent(cur_geocells[0], horz_dir),
                        geocell.adjacent(cur_geocells[0], vert_dir),
                        geocell.adjacent(cur_geocells[0], diag_dir)
                    ]))

            # Stop the search if the next closest possible search result is farther
            # than max_distance or, if we have max_results results already, farther
            # than the last result.
            _, sorted_edge_distances = \
                util.distance_sorted_edges(util.max_box(cur_geocells), center)
            closest_possible_next_result_dist = sorted_edge_distances[0]

            if DEBUG:
                logging.debug(('GeoModel Proximity Query: '
                               'Next result at least %f meters away') %
                              (closest_possible_next_result_dist, ))

            if max_distance and closest_possible_next_result_dist > max_distance:
                if DEBUG:
                    logging.debug(
                        ('GeoModel Proximity Query: '
                         'Done! Next result at least %f meters away, '
                         'max disance is %f meters') %
                        (closest_possible_next_result_dist, max_distance))
                break

            if len(results) >= max_results:
                current_farthest_returnable_result_dist = \
                    geomath.distance(center, results[max_results - 1][0].location)
                if (closest_possible_next_result_dist >=
                        current_farthest_returnable_result_dist):
                    if DEBUG:
                        logging.debug(
                            ('GeoModel Proximity Query: '
                             'Done! Next result at least %f meters away, '
                             'current farthest is %f meters away') %
                            (closest_possible_next_result_dist,
                             current_farthest_returnable_result_dist))
                    break

                if DEBUG:
                    logging.debug(('GeoModel Proximity Query: '
                                   'Next result at least %f meters away, '
                                   'current farthest is %f meters away') %
                                  (closest_possible_next_result_dist,
                                   current_farthest_returnable_result_dist))

        if DEBUG:
            logging.debug(('GeoModel Proximity Query: '
                           'Looked in %d geocells') % (len(searched_cells), ))

        return [
            entity for (entity, dist) in results[:max_results]
            if not max_distance or dist < max_distance
        ]
Exemple #16
0
    def test_compute_box(self):
        cell = geocell.compute(geotypes.Point(37, -122), 14)
        box = geocell.compute_box(cell)

        self.assertTrue(box.south <= 37 and 37 <= box.north
                        and box.west <= -122 and -122 <= box.east)
Exemple #17
0
  def proximity_fetch(query, center, max_results=10, max_distance=0,
                      start_resolution=geocell.MAX_GEOCELL_RESOLUTION):
    """Performs a proximity/radius fetch on the given query.

    Fetches at most max_results entities matching the given query,
    ordered by ascending distance from the given center point, and optionally
    limited by the given maximum distance.

    This method uses a greedy algorithm that starts by searching high-resolution
    geocells near the center point and gradually looking in lower and lower
    resolution cells until max_results entities have been found matching the
    given query and no closer possible entities can be found.

    Args:
      query: A db.Query on entities of this kind.
      center: A geotypes.Point or db.GeoPt indicating the center point around
          which to search for matching entities.
      max_results: An int indicating the maximum number of desired results.
          The default is 10, and the larger this number, the longer the fetch
          will take.
      max_distance: An optional number indicating the maximum distance to
          search, in meters.
      start_resolution: An optional number indicating the geocell resolution
          to begin the search at. Larger values will result in longer response
          times on average, but are more efficient for extremely dense data.
          The default is geocell.MAX_GEOCELL_RESOLUTION

    Returns:
      The fetched entities, sorted in ascending order by distance to the search
      center.

    Raises:
      Any exceptions that google.appengine.ext.db.Query.fetch() can raise.
    """
    # TODO(romannurik): check for GqlQuery
    results = []

    searched_cells = set()

    # The current search geocell containing the lat,lon.
    cur_containing_geocell = geocell.compute(center, start_resolution)

    # The currently-being-searched geocells.
    # NOTES:
    #     * Start with max possible.
    #     * Must always be of the same resolution.
    #     * Must always form a rectangular region.
    #     * One of these must be equal to the cur_containing_geocell.
    cur_geocells = [cur_containing_geocell]

    closest_possible_next_result_dist = 0
    
    sorted_edge_dirs = [(0,0)]
    sorted_edge_distances = [0]

    # Assumes both a and b are lists of (entity, dist) tuples, *sorted by dist*.
    # NOTE: This is an in-place merge, and there are guaranteed
    # no duplicates in the resulting list.
    def _merge_results_in_place(a, b):
      util.merge_in_place(a, b,
                        cmp_fn=lambda x, y: cmp(x[1], y[1]),
                        dup_fn=lambda x, y: x[0].key() == y[0].key())
    
    def _first_horz(edges):
      return [x for x in edges if x[0] != 0][0]
    
    def _first_vert(edges):
      return [x for x in edges if x[0] == 0][0]

    while cur_geocells:
      cur_geocells_unique = list(set(cur_geocells).difference(searched_cells))
      cur_resolution = len(cur_geocells[0])

      # Run query on the next set of geocells.
      new_results = util.async_in_query_fetch(
          query, 'location_geocells', cur_geocells_unique,
          max_results=max_results * len(cur_geocells_unique),
          debug=DEBUG)

      # Update results and sort.
      searched_cells.update(cur_geocells)

      # Begin storing distance from the search result entity to the
      # search center along with the search result itself, in a tuple.
      new_results = [(entity, geomath.distance(center, entity.location))
                     for entity in new_results]
      new_results = sorted(new_results, lambda dr1, dr2: cmp(dr1[1], dr2[1]))
      new_results = new_results[:max_results]

      # Merge new_results into results or the other way around, depending on
      # which is larger.
      logging.info("#Results :")
      logging.info(results)
      logging.info("#new_results :")
      logging.info(new_results)
      if len(results) > len(new_results):
        _merge_results_in_place(results, new_results)
      else:
        _merge_results_in_place(new_results, results)
        results = new_results

      results = results[:max_results]

      if DEBUG:
        logging.debug(('GeoModel Proximity Query: '
                       'Have %d results') %
                      (len(results),))
      
      if len(results) >= max_results:
        if DEBUG:
          logging.info(('GeoModel Proximity Query: '
                         'Wanted %d results, ending search') %
                        (max_results,))
        break
      
      # Determine the next set of geocells to search.
      sorted_edge_dirs, _ = \
          util.distance_sorted_edges(util.max_box(cur_geocells), center)

      if len(results) == 0 or len(cur_geocells) == 4:
        # Either no results (in which case we optimize by not looking at
        # adjacents, go straight to the parent) or we've searched 4 adjacent
        # geocells, in which case we should now search the parents of those
        # geocells.
        cur_containing_geocell = cur_containing_geocell[:-1]
        cur_geocells = list(set([cell[:-1] for cell in cur_geocells]))
        
        if not cur_geocells or not cur_geocells[0]:
          break  # Done with search, we've searched everywhere.
        
        if len(cur_geocells) == 2:
          # There are two parents for the 4 just-searched cells; get 2
          # perpendicular adjacent parents to search a full set of 4 cells.
          perp_dir = (_first_vert(sorted_edge_dirs)
                      if geocell.collinear(cur_geocells[0],
                                           cur_geocells[1], False)
                      else _first_horz(sorted_edge_dirs))
          
          cur_geocells.extend(
              filter(lambda x: x is not None, [
                     geocell.adjacent(cur_geocells[0], perp_dir),
                     geocell.adjacent(cur_geocells[1], perp_dir)]))

      elif len(cur_geocells) == 1:
        # Searched one geocell, now search its 3 adjacents.
        horz_dir = _first_horz(sorted_edge_dirs)
        vert_dir = _first_vert(sorted_edge_dirs)
        diag_dir = (horz_dir[0], vert_dir[1])
        
        cur_geocells.extend(
            filter(lambda x: x is not None, [
                   geocell.adjacent(cur_geocells[0], horz_dir),
                   geocell.adjacent(cur_geocells[0], vert_dir),
                   geocell.adjacent(cur_geocells[0], diag_dir)]))

      # Stop the search if the next closest possible search result is farther
      # than max_distance or, if we have max_results results already, farther
      # than the last result.
      _, sorted_edge_distances = \
          util.distance_sorted_edges(util.max_box(cur_geocells), center)
      closest_possible_next_result_dist = sorted_edge_distances[0]
      
      if DEBUG:
        logging.info(('GeoModel Proximity Query: '
                       'Next result at least %f meters away') %
                      (closest_possible_next_result_dist,))
      
      if max_distance and closest_possible_next_result_dist > max_distance:
        if DEBUG:
          logging.info(('GeoModel Proximity Query: '
                         'Done! Next result at least %f meters away, '
                         'max disance is %f meters') %
                        (closest_possible_next_result_dist, max_distance))
        break
      
      if len(results) >= max_results:
        current_farthest_returnable_result_dist = \
            geomath.distance(center, results[max_results - 1][0].location)
        if (closest_possible_next_result_dist >=
            current_farthest_returnable_result_dist):
          if DEBUG:
            logging.info(('GeoModel Proximity Query: '
                           'Done! Next result at least %f meters away, '
                           'current farthest is %f meters away') %
                          (closest_possible_next_result_dist,
                           current_farthest_returnable_result_dist))
          break
        
        if DEBUG:
          logging.info(('GeoModel Proximity Query: '
                         'Next result at least %f meters away, '
                         'current farthest is %f meters away') %
                        (closest_possible_next_result_dist,
                         current_farthest_returnable_result_dist))

    if DEBUG:
      logging.info(('GeoModel Proximity Query: '
                     'Looked in %d geocells') %
                    (len(searched_cells),))

    return [entity for (entity, dist) in results[:max_results]
            if not max_distance or dist < max_distance]