def distance_sorted_edges(box, point): """Returns the edge directions of the box, sorted by distance from a point. Returns the edge directions (i.e. NORTH, SOUTH, etc.) of the box, sorted by distance from the given point, along with the actual distances from the point to these edges. Args: box: The geotypes.Box 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 direction and distance is the distance from the point to that edge. Direction values will be NORTH, SOUTH, EAST, or WEST """ # TODO(romannurik): Assert that lat,lon are actually inside the box. return zip(*sorted([ (geocell.NORTH, geomath.distance(geotypes.Point(box.north, point.lon), point)), (geocell.SOUTH, geomath.distance(geotypes.Point(box.south, point.lon), point)), (geocell.WEST, geomath.distance(geotypes.Point(point.lat, box.west), point)), (geocell.EAST, geomath.distance(geotypes.Point(point.lat, box.east), point))], lambda x, y: cmp(x[1], y[1])))
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_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 test_distance(self): # known distances using GLatLng from the Maps API calc_dist = geomath.distance(geotypes.Point(37, -122), geotypes.Point(42, -75)) known_dist = 4024365 # make sure the calculated distance is within +/- 1% of known distance self.assertTrue(abs((calc_dist - known_dist) / known_dist) <= 0.01)
def test_distance_rounding(self): # Test location that can cause math domain error (due to rounding) unless # the distance function clamps the spherical law of cosines value between # -1.0 and 1.0. calc_dist = geomath.distance(geotypes.Point(47.291288, 8.56613), geotypes.Point(47.291288, 8.56613)) known_dist = 0.0 self.assertTrue(calc_dist == known_dist)
def test_Point(self): # an invalid point self.assertRaises(ValueError, geotypes.Point, 95, 0) self.assertRaises(ValueError, geotypes.Point, 0, 185) # a valid point point = geotypes.Point(37, -122) self.assertEquals(37, point.lat) self.assertEquals(-122, point.lon) self.assertTrue(isinstance(point.__str__(), str)) self.assertEquals(geotypes.Point(37, -122), geotypes.Point(37, -122)) self.assertNotEquals(geotypes.Point(37, -122), geotypes.Point(0, 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_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 mid_point(p1, p2): p1lat, p1lon = math.radians(p1.lat), math.radians(p1.lon) p2lat, p2lon = math.radians(p2.lat), math.radians(p2.lon) delta_lon = p2lon - p1lon bx = math.cos(p2lat) * math.cos(delta_lon) by = math.cos(p2lat) * math.sin(delta_lon) p3lat = math.atan2( math.sin(p1lat) + math.sin(p2lat), math.sqrt((math.cos(p1lat) + bx) * (math.cos(p1lat) + bx) + by * by)) p3lon = p1lon + math.atan2(by, math.cos(p1lat) + bx) return geotypes.Point(math.degrees(p3lat), math.degrees(p3lon))
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))
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 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