diff options
author | 杨博东 <bodong.ybd@alibaba-inc.com> | 2020-12-12 08:21:05 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-12 02:21:05 +0200 |
commit | 4d06d99bf8fc91289c79ae07110c92525ab1c1f8 (patch) | |
tree | 343f512de0c1b0d83c29e99189ba2b02a8f837cb /src/geohash_helper.c | |
parent | 8c291b97b95f2e011977b522acf77ead23e26f55 (diff) | |
download | redis-4d06d99bf8fc91289c79ae07110c92525ab1c1f8.tar.gz |
Add GEOSEARCH / GEOSEARCHSTORE commands (#8094)
Add commands to query geospatial data with bounding box.
Two new commands that replace the existing 4 GEORADIUS* commands.
GEOSEARCH key [FROMMEMBER member] [FROMLOC long lat] [BYRADIUS radius
unit] [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT
count] [ASC|DESC]
GEOSEARCHSTORE dest_key src_key [FROMMEMBER member] [FROMLOC long lat]
[BYRADIUS radius unit] [BYBOX width height unit] [WITHCORD] [WITHDIST]
[WITHASH] [COUNT count] [ASC|DESC] [STOREDIST]
- Add two types of CIRCULAR_TYPE and RECTANGLE_TYPE to achieve different searches
- Judge whether the point is within the rectangle, refer to:
geohashGetDistanceIfInRectangle
Diffstat (limited to 'src/geohash_helper.c')
-rw-r--r-- | src/geohash_helper.c | 71 |
1 files changed, 47 insertions, 24 deletions
diff --git a/src/geohash_helper.c b/src/geohash_helper.c index 01fb2cb88..a1ddf5d31 100644 --- a/src/geohash_helper.c +++ b/src/geohash_helper.c @@ -82,10 +82,9 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) { return step; } -/* Return the bounding box of the search area centered at latitude,longitude - * having a radius of radius_meter. bounds[0] - bounds[2] is the minimum - * and maximum longitude, while bounds[1] - bounds[3] is the minimum and - * maximum latitude. +/* Return the bounding box of the search area by shape (see geohash.h GeoShape) + * bounds[0] - bounds[2] is the minimum and maximum longitude + * while bounds[1] - bounds[3] is the minimum and maximum latitude. * * This function does not behave correctly with very large radius values, for * instance for the coordinates 81.634948934258375 30.561509253718668 and a @@ -100,34 +99,51 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) { * Since this function is currently only used as an optimization, the * optimization is not used for very big radiuses, however the function * should be fixed. */ -int geohashBoundingBox(double longitude, double latitude, double radius_meters, - double *bounds) { +int geohashBoundingBox(GeoShape *shape, double *bounds) { if (!bounds) return 0; + double longitude = shape->xy[0]; + double latitude = shape->xy[1]; + double height = shape->conversion * (shape->type == CIRCULAR_TYPE ? shape->t.radius : shape->t.r.height/2); + double width = shape->conversion * (shape->type == CIRCULAR_TYPE ? shape->t.radius : shape->t.r.width/2); - bounds[0] = longitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude))); - bounds[2] = longitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude))); - bounds[1] = latitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS); - bounds[3] = latitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS); + const double long_delta = rad_deg(width/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude))); + const double lat_delta = rad_deg(height/EARTH_RADIUS_IN_METERS); + bounds[0] = longitude - long_delta; + bounds[2] = longitude + long_delta; + bounds[1] = latitude - lat_delta; + bounds[3] = latitude + lat_delta; return 1; } -/* Return a set of areas (center + 8) that are able to cover a range query - * for the specified position and radius. */ -GeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double radius_meters) { +/* Calculate a set of areas (center + 8) that are able to cover a range query + * for the specified position and shape (see geohash.h GeoShape). + * the bounding box saved in shaple.bounds */ +GeoHashRadius geohashCalculateAreasByShapeWGS84(GeoShape *shape) { GeoHashRange long_range, lat_range; GeoHashRadius radius; GeoHashBits hash; GeoHashNeighbors neighbors; GeoHashArea area; double min_lon, max_lon, min_lat, max_lat; - double bounds[4]; int steps; - geohashBoundingBox(longitude, latitude, radius_meters, bounds); - min_lon = bounds[0]; - min_lat = bounds[1]; - max_lon = bounds[2]; - max_lat = bounds[3]; + geohashBoundingBox(shape, shape->bounds); + min_lon = shape->bounds[0]; + min_lat = shape->bounds[1]; + max_lon = shape->bounds[2]; + max_lat = shape->bounds[3]; + + double longitude = shape->xy[0]; + double latitude = shape->xy[1]; + /* radius_meters is calculated differently in different search types: + * 1) CIRCULAR_TYPE, just use radius. + * 2) RECTANGLE_TYPE, in order to calculate accurately, we should use + * sqrt((width/2)^2 + (height/2)^2), so that the box is bound by a circle, + * But the current code a simpler approach resulting in a smaller circle, + * which is safe because we search the 8 nearby boxes anyway. */ + double radius_meters = shape->type == CIRCULAR_TYPE ? shape->t.radius : + shape->t.r.width > shape->t.r.height ? shape->t.r.width/2 : shape->t.r.height/2; + radius_meters *= shape->conversion; steps = geohashEstimateStepsByRadius(radius_meters,latitude); @@ -196,11 +212,6 @@ GeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double return radius; } -GeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude, - double radius_meters) { - return geohashGetAreasByRadius(longitude, latitude, radius_meters); -} - GeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash) { uint64_t bits = hash.bits; bits <<= (52 - hash.step * 2); @@ -233,3 +244,15 @@ int geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2, double *distance) { return geohashGetDistanceIfInRadius(x1, y1, x2, y2, radius, distance); } + +/* Judge whether a point is in the axis-aligned rectangle. + * bounds : see geohash.h GeoShape::bounds + * x1, y1 : the center of the box + * x2, y2 : the point to be searched + */ +int geohashGetDistanceIfInRectangle(double *bounds, double x1, double y1, + double x2, double y2, double *distance) { + if (x2 < bounds[0] || x2 > bounds[2] || y2 < bounds[1] || y2 > bounds[3]) return 0; + *distance = geohashGetDistance(x1, y1, x2, y2); + return 1; +} |