summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2017-07-03 19:38:31 +0200
committerantirez <antirez@gmail.com>2017-07-03 19:38:31 +0200
commitb2cd9fcab6122ccbf8b08f71f59a0af01931083b (patch)
tree4b361361dbbf250a003e08e3533df06ca4afa55b /src
parent26e638a8e917608d8e75b07e5e941c7a24462c4d (diff)
downloadredis-b2cd9fcab6122ccbf8b08f71f59a0af01931083b.tar.gz
Fix GEORADIUS edge case with huge radius.
This commit closes issue #3698, at least for now, since the root cause was not fixed: the bounding box function, for huge radiuses, does not return a correct bounding box, there are points still within the radius that are left outside. So when using GEORADIUS queries with radiuses in the order of 5000 km or more, it was possible to see, at the edge of the area, certain points not correctly reported. Because the bounding box for now was used just as an optimization, and such huge radiuses are not common, for now the optimization is just switched off when the radius is near such magnitude. Three test cases found by the Continuous Integration test were added, so that we can easily trigger the bug again, both for regression testing and in order to properly fix it as some point in the future.
Diffstat (limited to 'src')
-rw-r--r--src/geohash_helper.c56
1 files changed, 36 insertions, 20 deletions
diff --git a/src/geohash_helper.c b/src/geohash_helper.c
index 77d8ab392..e23f17b4e 100644
--- a/src/geohash_helper.c
+++ b/src/geohash_helper.c
@@ -85,7 +85,21 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
/* 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 maxium longitude, while bounds[1] - bounds[3] is the minimum and
- * maximum latitude. */
+ * maximum latitude.
+ *
+ * This function does not behave correctly with very large radius values, for
+ * instance for the coordinates 81.634948934258375 30.561509253718668 and a
+ * radius of 7083 kilometers, it reports as bounding boxes:
+ *
+ * min_lon 7.680495, min_lat -33.119473, max_lon 155.589402, max_lat 94.242491
+ *
+ * However, for instance, a min_lon of 7.680495 is not correct, because the
+ * point -1.27579540014266968 61.33421815228281559 is at less than 7000
+ * kilometers away.
+ *
+ * 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) {
if (!bounds) return 0;
@@ -154,25 +168,27 @@ GeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double
}
/* Exclude the search areas that are useless. */
- if (area.latitude.min < min_lat) {
- GZERO(neighbors.south);
- GZERO(neighbors.south_west);
- GZERO(neighbors.south_east);
- }
- if (area.latitude.max > max_lat) {
- GZERO(neighbors.north);
- GZERO(neighbors.north_east);
- GZERO(neighbors.north_west);
- }
- if (area.longitude.min < min_lon) {
- GZERO(neighbors.west);
- GZERO(neighbors.south_west);
- GZERO(neighbors.north_west);
- }
- if (area.longitude.max > max_lon) {
- GZERO(neighbors.east);
- GZERO(neighbors.south_east);
- GZERO(neighbors.north_east);
+ if (steps >= 2) {
+ if (area.latitude.min < min_lat) {
+ GZERO(neighbors.south);
+ GZERO(neighbors.south_west);
+ GZERO(neighbors.south_east);
+ }
+ if (area.latitude.max > max_lat) {
+ GZERO(neighbors.north);
+ GZERO(neighbors.north_east);
+ GZERO(neighbors.north_west);
+ }
+ if (area.longitude.min < min_lon) {
+ GZERO(neighbors.west);
+ GZERO(neighbors.south_west);
+ GZERO(neighbors.north_west);
+ }
+ if (area.longitude.max > max_lon) {
+ GZERO(neighbors.east);
+ GZERO(neighbors.south_east);
+ GZERO(neighbors.north_east);
+ }
}
radius.hash = hash;
radius.neighbors = neighbors;