def test_geo(self): _populate_db() results = PublicSchool.bounding_box_fetch( PublicSchool.query().order(PublicSchool.school_id), geotypes.Box(39, -82, 41, -84), max_results=10) self.assertEqual(len(results), 2) self.assertEqual(results[0].school_id, '390000104683') self.assertEqual(results[1].school_id, '390000204684') results = PublicSchool.bounding_box_fetch( PublicSchool.query().order(PublicSchool.school_id), geotypes.Box(38, -83, 40, -85), max_results=10) self.assertEqual(len(results), 1) self.assertEqual(results[0].school_id, '390002001515') results = PublicSchool.proximity_fetch( PublicSchool.query(), geotypes.Point(40, -83), max_results=10, max_distance=50000) # Within 50km. self.assertEqual(len(results), 2) self.assertEqual(results[0].school_id, '390000104683') self.assertEqual(results[1].school_id, '390000204684') results = PublicSchool.proximity_fetch( PublicSchool.query(), geotypes.Point(34, -117), max_results=10, max_distance=50000) # Within 50km. self.assertEqual(len(results), 2) self.assertEqual(results[0].school_id, '590019700136') self.assertEqual(results[1].school_id, '590010500138')
def compute_box(cell): """Computes the rectangular boundaries (bounding box) of the given geocell. Args: cell: The geocell string whose boundaries are to be computed. Returns: A geotypes.Box corresponding to the rectangular boundaries of the geocell. """ if cell is None: return None bbox = geotypes.Box(90.0, 180.0, -90.0, -180.0) while len(cell) > 0: subcell_lon_span = (bbox.east - bbox.west) / _GEOCELL_GRID_SIZE subcell_lat_span = (bbox.north - bbox.south) / _GEOCELL_GRID_SIZE x, y = _subdiv_xy(cell[0]) bbox = geotypes.Box(bbox.south + subcell_lat_span * (y + 1), bbox.west + subcell_lon_span * (x + 1), bbox.south + subcell_lat_span * y, bbox.west + subcell_lon_span * x) cell = cell[1:] return bbox
def distance_sorted_edges(cells, point): """Returns the edges of the rectangular region containing all of the given geocells, sorted by distance from the given point, along with the actual distances from the point to these edges. Args: cells: The cells (should be adjacent) defining the rectangular region whose edge distances are requested. point: The point that should determine the edge sort order. Returns: A list of (direction, distance) tuples, where direction is the edge and distance is the distance from the point to that edge. A direction value of (0,-1), for example, corresponds to the South edge of the rectangular region containing all of the given geocells. """ # TODO(romannurik): Assert that lat,lon are actually inside the geocell. boxes = [geocell.compute_box(cell) for cell in cells] max_box = geotypes.Box(max([box.north for box in boxes]), max([box.east for box in boxes]), min([box.south for box in boxes]), min([box.west for box in boxes])) return zip(*sorted( [((0, -1), geomath.distance(geotypes.Point(max_box.south, point.lon), point)), ((0, 1), geomath.distance(geotypes.Point(max_box.north, point.lon), point)), ((-1, 0), geomath.distance(geotypes.Point(point.lat, max_box.west), point)), ((1, 0), geomath.distance(geotypes.Point(point.lat, max_box.east), point) )], lambda x, y: cmp(x[1], y[1])))
def test_Box(self): # an invalid box self.assertRaises(ValueError, geotypes.Box, 95, 0, 0, 0) # a valid box box = geotypes.Box(37, -122, 34, -125) self.assertEquals(37, box.north) self.assertEquals(34, box.south) self.assertEquals(-122, box.east) self.assertEquals(-125, box.west) # assert north can't be under south self.assertRaises(ValueError, box._set_north, 32) self.assertRaises(ValueError, box._set_south, 39) self.assertTrue(isinstance(box.__str__(), str)) # valid boxes self.assertEquals(geotypes.Box(37, -122, 34, -125), geotypes.Box(34, -122, 37, -125))
def max_box(cells): """Returns the rectangular region containing all of the given geocells. Args: cells: A list of adjacent geocells. Returns: A geotypes.Box representing the maximum bounds of the set of adjacent cells. """ boxes = [geocell.compute_box(cell) for cell in cells] return geotypes.Box(max([box.north for box in boxes]), max([box.east for box in boxes]), min([box.south for box in boxes]), min([box.west for box in boxes]))
def best_bbox_search_cells(bbox, cost_function): """Returns an efficient set of geocells to search in a bounding box query. This method is guaranteed to return a set of geocells having the same resolution. Args: bbox: A geotypes.Box indicating the bounding box being searched. cost_function: A function that accepts two arguments: * num_cells: the number of cells to search * resolution: the resolution of each cell to search and returns the 'cost' of querying against this number of cells at the given resolution. Returns: A list of geocell strings that contain the given box. """ cell_ne = compute(bbox.north_east, resolution=MAX_GEOCELL_RESOLUTION) cell_sw = compute(bbox.south_west, resolution=MAX_GEOCELL_RESOLUTION) # The current lowest BBOX-search cost found; start with practical infinity. min_cost = 1e10000 # The set of cells having the lowest calculated BBOX-search cost. min_cost_cell_set = None # First find the common prefix, if there is one.. this will be the base # resolution.. i.e. we don't have to look at any higher resolution cells.\ common_prefix = os.path.commonprefix([cell_sw, cell_ne]) min_resolution = len(common_prefix) #ancho del viewport vpw = bbox.north_east.lon - bbox.south_west.lon deep = 1 if vpw > 0: xx = log(360.0 / vpw) if xx > 0: deep = int(ceil(xx / log(_GEOCELL_GRID_SIZE))) - 1 if deep > MAX_GEOCELL_RESOLUTION: deep = MAX_GEOCELL_RESOLUTION logging.debug( 'best_bbox_search_cells() deep:[%s]; cell_sw:[%s]; cell_ne:[%s]; common_prefix:[%s]; min_resolution:[%s]; MAX_GEOCELL_RESOLUTION[%s]' % (str(deep), cell_sw, cell_ne, common_prefix, str(min_resolution), str(MAX_GEOCELL_RESOLUTION))) if min_resolution == deep or deep < 6: logging.debug( ' (if min_resolution == deep or deep<6:) ES VERDADERO!!!') return common_prefix, str(compute_box(common_prefix)) #buscamos un primo sobre min_resolution (la caja grande) p0 = geotypes.Point(bbox.north_east.lat, bbox.south_west.lon) p0cell_gorda = compute(p0, MAX_GEOCELL_RESOLUTION, False) new_deep = deep p0cell = p0cell_gorda[:new_deep] p0New = compute_box(p0cell) dx = 360.0 / pow(_GEOCELL_GRID_SIZE, new_deep) / 4.0 dy = 180.0 / pow(_GEOCELL_GRID_SIZE, new_deep) / 4.0 primo_prefix = '0' primo_width = 4.0 if vpw > (3 * dx): primo_prefix = '1' primo_width = 5.0 #dx => 1 de los chiquitos (deep+1) #dy => 1 de los chiquitos (deep+1) #p0 => view port (top,left) #p0New => Box del gordo (deep-1) primoX0 = abs(int((p0.lon - p0New.south_west.lon) / dx)) primoY0 = abs(int((p0.lat - p0New.north_east.lat) / dy)) boxprimo = geotypes.Box( p0New.north_east.lat - (dy * primoY0), p0New.south_west.lon + (dx * primoX0) + (dx * primo_width), p0New.north_east.lat - (dy * primoY0) - (dy * primo_width), p0New.south_west.lon + (dx * primoX0)) the_primo = p0cell + '.' + primo_prefix + str(primoX0) + str(primoY0) #logging.error('SIEMPRE ENCUENTRO EL PRIMO %s' % the_primo) return the_primo, boxprimo
def compute(point, resolution=MAX_GEOCELL_RESOLUTION, get_primos=False): """Computes the geocell containing the given point to the given resolution. This is a simple 16-tree lookup to an arbitrary depth (resolution). Args: point: The geotypes.Point to compute the cell for. resolution: An int indicating the resolution of the cell to compute. Returns: The geocell string containing the given point, of length <resolution>. """ north = 90.0 south = -90.0 east = 180.0 west = -180.0 primos = [] cell = '' #print 'point: '+str(point) while len(cell) < resolution: subcell_lon_span = (east - west) / _GEOCELL_GRID_SIZE subcell_lat_span = (north - south) / _GEOCELL_GRID_SIZE x = min(int(_GEOCELL_GRID_SIZE * (point.lon - west) / (east - west)), _GEOCELL_GRID_SIZE - 1) y = min( int(_GEOCELL_GRID_SIZE * (point.lat - south) / (north - south)), _GEOCELL_GRID_SIZE - 1) cell += _subdiv_char((x, y)) if get_primos and len(cell) > 4: #ancho del level dividido 4 dx = subcell_lon_span / 4 dy = subcell_lat_span / 4 #celdas: 0 es en la que estamos, tenemos que probar en 1 y 2 # [2] # [1][0] cells = [ cell, adjacent(cell, (-1, 0)), adjacent(cell, (0, 1)), adjacent(cell, (-1, 1)) ] # cell: The geocell string whose neighbor is being calculated. # dir: An (x, y) tuple indicating direction, where x and y can be -1, 0, or 1. # -1 corresponds to West for x and South for y, and # 1 corresponds to East for x and North for y. # boxes=[] #print ' dx (dLon): '+str(dx) #print ' dy (dLat): '+str(dy) #iteramos longitud for k in range(len(cells)): cur_cell = cells[k] #Skipeamos si es la misma (caso borde del mundo) if cur_cell == cell and k > 0: continue #Calculamos la caja de la celda actual box = compute_box(cur_cell) #Armamos el punto superior-izquierdo p0 = geotypes.Point(box.north_east.lat, box.south_west.lon) # print ' width: '+str(box.north_east.lon-box.south_west.lon) # print 'box: '+str(box) # print 'box point: '+str(p0) for xx in [0, 1]: for i in range(0, 4): for j in range(0, 4): if xx == 0 and i == 0 and j == 0: continue bbox = geotypes.Box( box.north_east.lat - (dy * j), box.north_east.lon + (dx * i) + (dx * xx), box.south_west.lat - (dy * j) - (dy * xx), box.south_west.lon + (dx * i)) # bbox = geotypes.Box( p0.lat-(dy*j) # north # , p0.lon+(dy*j)+(dy*xx) # east # , p0.lat-(dx*i)-(dx*xx) # south # , p0.lon+(dx*i) # west # ) # print ' '+cur_cell +'.'+str(xx) +str(i)+str(j)+str(bbox) # print cur_cell +'.'+str(xx) +str(i)+str(j) + '|-|'+str(box)+'-'+str(bbox) if is_in_box(bbox, point): the_cell = cur_cell + '.' + str(xx) + str( i) + str(j) primos.append(the_cell) # boxes.append(bbox) # print 'k:'+str(k)+' boxes:'+ str(map(lambda x:str(x),boxes)) # boxes=[] # exit(0) south += subcell_lat_span * y north = south + subcell_lat_span west += subcell_lon_span * x east = west + subcell_lon_span if get_primos: return (cell, primos) return cell