mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 08:08:53 -05:00
Geo: GEODIST and tests.
This commit is contained in:
parent
a12192f5ff
commit
f108c687ad
4
deps/geohash-int/geohash_helper.c
vendored
4
deps/geohash-int/geohash_helper.c
vendored
@ -167,7 +167,7 @@ GeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash) {
|
||||
}
|
||||
|
||||
/* Calculate distance using haversin great circle distance formula. */
|
||||
double distanceEarth(double lon1d, double lat1d, double lon2d, double lat2d) {
|
||||
double geohashGetDistance(double lon1d, double lat1d, double lon2d, double lat2d) {
|
||||
double lat1r, lon1r, lat2r, lon2r, u, v;
|
||||
lat1r = deg_rad(lat1d);
|
||||
lon1r = deg_rad(lon1d);
|
||||
@ -182,7 +182,7 @@ double distanceEarth(double lon1d, double lat1d, double lon2d, double lat2d) {
|
||||
int geohashGetDistanceIfInRadius(double x1, double y1,
|
||||
double x2, double y2, double radius,
|
||||
double *distance) {
|
||||
*distance = distanceEarth(x1, y1, x2, y2);
|
||||
*distance = geohashGetDistance(x1, y1, x2, y2);
|
||||
if (*distance > radius) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
2
deps/geohash-int/geohash_helper.h
vendored
2
deps/geohash-int/geohash_helper.h
vendored
@ -58,6 +58,8 @@ GeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude,
|
||||
GeoHashRadius geohashGetAreasByRadiusMercator(double longitude, double latitude,
|
||||
double radius_meters);
|
||||
GeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash);
|
||||
double geohashGetDistance(double lon1d, double lat1d,
|
||||
double lon2d, double lat2d);
|
||||
int geohashGetDistanceIfInRadius(double x1, double y1,
|
||||
double x2, double y2, double radius,
|
||||
double *distance);
|
||||
|
84
src/geo.c
84
src/geo.c
@ -112,6 +112,30 @@ static int longLatFromMember(robj *zobj, robj *member, double *xy) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Check that the unit argument matches one of the known units, and returns
|
||||
* the conversion factor to meters (you need to divide meters by the conversion
|
||||
* factor to convert to the right unit).
|
||||
*
|
||||
* If the unit is not valid, an error is reported to the client, and a value
|
||||
* less than zero is returned. */
|
||||
double extractUnitOrReply(redisClient *c, robj *unit) {
|
||||
char *u = unit->ptr;
|
||||
|
||||
if (!strcmp(u, "m") || !strncmp(u, "meter", 5)) {
|
||||
return 1;
|
||||
} else if (!strcmp(u, "ft") || !strncmp(u, "feet", 4)) {
|
||||
return 0.3048;
|
||||
} else if (!strcmp(u, "mi") || !strncmp(u, "mile", 4)) {
|
||||
return 1609.34;
|
||||
} else if (!strcmp(u, "km") || !strncmp(u, "kilometer", 9)) {
|
||||
return 1000;
|
||||
} else {
|
||||
addReplyError(c, "unsupported unit provided. please use meters (m), "
|
||||
"kilometers (km), miles (mi), or feet (ft)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Input Argument Helper.
|
||||
* Extract the dinstance from the specified two arguments starting at 'argv'
|
||||
* that shouldbe in the form: <number> <unit> and return the dinstance in the
|
||||
@ -127,25 +151,10 @@ static double extractDistanceOrReply(redisClient *c, robj **argv,
|
||||
return -1;
|
||||
}
|
||||
|
||||
double to_meters;
|
||||
sds units = argv[1]->ptr;
|
||||
if (!strcmp(units, "m") || !strncmp(units, "meter", 5)) {
|
||||
to_meters = 1;
|
||||
} else if (!strcmp(units, "ft") || !strncmp(units, "feet", 4)) {
|
||||
to_meters = 0.3048;
|
||||
} else if (!strcmp(units, "mi") || !strncmp(units, "mile", 4)) {
|
||||
to_meters = 1609.34;
|
||||
} else if (!strcmp(units, "km") || !strncmp(units, "kilometer", 9)) {
|
||||
to_meters = 1000;
|
||||
} else {
|
||||
addReplyError(c, "unsupported unit provided. please use meters (m), "
|
||||
"kilometers (km), miles (mi), or feet (ft)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conversion)
|
||||
*conversion = to_meters;
|
||||
double to_meters = extractUnitOrReply(c,argv[1]);
|
||||
if (to_meters < 0) return -1;
|
||||
|
||||
if (conversion) *conversion = to_meters;
|
||||
return distance * to_meters;
|
||||
}
|
||||
|
||||
@ -742,3 +751,42 @@ void geoposCommand(redisClient *c) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* GEODIST key ele1 ele2 [unit]
|
||||
*
|
||||
* Return the distance, in meters by default, otherwise accordig to "unit",
|
||||
* between points ele1 and ele2. If one or more elements are missing NULL
|
||||
* is returned. */
|
||||
void geodistCommand(redisClient *c) {
|
||||
double to_meter = 1;
|
||||
|
||||
/* Check if there is the unit to extract, otherwise assume meters. */
|
||||
if (c->argc == 5) {
|
||||
to_meter = extractUnitOrReply(c,c->argv[4]);
|
||||
if (to_meter < 0) return;
|
||||
} else if (c->argc > 5) {
|
||||
addReply(c,shared.syntaxerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look up the requested zset */
|
||||
robj *zobj = NULL;
|
||||
if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.emptybulk))
|
||||
== NULL || checkType(c, zobj, REDIS_ZSET)) return;
|
||||
|
||||
/* Get the scores. We need both otherwise NULL is returned. */
|
||||
double score1, score2, xyxy[4];
|
||||
if (zsetScore(zobj, c->argv[2], &score1) == REDIS_ERR ||
|
||||
zsetScore(zobj, c->argv[3], &score2) == REDIS_ERR)
|
||||
{
|
||||
addReply(c,shared.nullbulk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decode & compute the distance. */
|
||||
if (!decodeGeohash(score1,xyxy) || !decodeGeohash(score2,xyxy+2))
|
||||
addReply(c,shared.nullbulk);
|
||||
else
|
||||
addReplyDouble(c,
|
||||
geohashGetDistance(xyxy[0],xyxy[1],xyxy[2],xyxy[3]) / to_meter);
|
||||
}
|
||||
|
@ -289,6 +289,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"geodecode",geodecodeCommand,2,"r",0,NULL,0,0,0,0,0},
|
||||
{"geohash",geohashCommand,-2,"r",0,NULL,0,0,0,0,0},
|
||||
{"geopos",geoposCommand,-2,"r",0,NULL,0,0,0,0,0},
|
||||
{"geodist",geodistCommand,-4,"r",0,NULL,0,0,0,0,0},
|
||||
{"pfselftest",pfselftestCommand,1,"r",0,NULL,0,0,0,0,0},
|
||||
{"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},
|
||||
{"pfcount",pfcountCommand,-2,"r",0,NULL,1,1,1,0,0},
|
||||
|
@ -1565,6 +1565,7 @@ void georadiusCommand(redisClient *c);
|
||||
void geoaddCommand(redisClient *c);
|
||||
void geohashCommand(redisClient *c);
|
||||
void geoposCommand(redisClient *c);
|
||||
void geodistCommand(redisClient *c);
|
||||
void pfselftestCommand(redisClient *c);
|
||||
void pfaddCommand(redisClient *c);
|
||||
void pfcountCommand(redisClient *c);
|
||||
|
@ -109,6 +109,28 @@ start_server {tags {"geo"}} {
|
||||
lindex [r geopos points a x b] 1
|
||||
} {}
|
||||
|
||||
test {GEODIST simple & unit} {
|
||||
r del points
|
||||
r geoadd points 13.361389 38.115556 "Palermo" \
|
||||
15.087269 37.502669 "Catania"
|
||||
set m [r geodist points Palermo Catania]
|
||||
assert {$m > 166274 && $m < 166275}
|
||||
set km [r geodist points Palermo Catania km]
|
||||
assert {$km > 166.2 && $km < 166.3}
|
||||
}
|
||||
|
||||
test {GEODIST missing elements} {
|
||||
r del points
|
||||
r geoadd points 13.361389 38.115556 "Palermo" \
|
||||
15.087269 37.502669 "Catania"
|
||||
set m [r geodist points Palermo Agrigento]
|
||||
assert {$m eq {}}
|
||||
set m [r geodist points Ragusa Agrigento]
|
||||
assert {$m eq {}}
|
||||
set m [r geodist empty_key Palermo Catania]
|
||||
assert {$m eq {}}
|
||||
}
|
||||
|
||||
test {GEOADD + GEORANGE randomized test} {
|
||||
set attempt 10
|
||||
while {[incr attempt -1]} {
|
||||
|
Loading…
Reference in New Issue
Block a user