diff --git a/src/geo.c b/src/geo.c index 87cd88cf..9811970e 100644 --- a/src/geo.c +++ b/src/geo.c @@ -635,3 +635,58 @@ void geoEncodeCommand(redisClient *c) { addReplyDouble(c, lat); addReplyDouble(c, lon); } + +/* GEOHASH key ele1 ele2 ... eleN + * + * Returns an array with an 11 characters geohash representation of the + * position of the specified elements. */ +void geoHashCommand(redisClient *c) { + char *geoalphabet= "0123456789bcdefghjkmnpqrstuvwxyz"; + int j; + + /* Look up the requested zset */ + robj *zobj = NULL; + if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.emptymultibulk)) + == NULL || checkType(c, zobj, REDIS_ZSET)) return; + + /* Geohash elements one after the other, using a null bulk reply for + * missing elements. */ + addReplyMultiBulkLen(c,c->argc-2); + for (j = 2; j < c->argc; j++) { + double score; + if (zsetScore(zobj, c->argv[j], &score) == REDIS_ERR) { + addReply(c,shared.nullbulk); + } else { + /* The internal format we use for geocoding is a bit different + * than the standard, since we use as initial latitude range + * -85,85, while the normal geohashing algorithm uses -90,90. + * So we have to decode our position and re-encode using the + * standard ranges in order to output a valid geohash string. */ + + /* Decode... */ + double latlong[2]; + if (!decodeGeohash(score,latlong)) { + addReply(c,shared.nullbulk); + continue; + } + + /* Re-encode */ + GeoHashRange r[2]; + GeoHashBits hash; + r[0].min = -90; + r[0].max = 90; + r[1].min = -180; + r[1].max = 180; + geohashEncode(&r[0],&r[1],latlong[0],latlong[1],26,&hash); + + char buf[12]; + int i; + for (i = 0; i < 11; i++) { + int idx = (hash.bits >> (52-((i+1)*5))) & 0x1f; + buf[i] = geoalphabet[idx]; + } + buf[11] = '\0'; + addReplyBulkCBuffer(c,buf,11); + } + } +} diff --git a/src/redis.c b/src/redis.c index e0561183..2221e128 100644 --- a/src/redis.c +++ b/src/redis.c @@ -287,6 +287,7 @@ struct redisCommand redisCommandTable[] = { {"georadiusbymember",geoRadiusByMemberCommand,-5,"r",0,NULL,1,1,1,0,0}, {"geoencode",geoEncodeCommand,-3,"r",0,NULL,0,0,0,0,0}, {"geodecode",geoDecodeCommand,2,"r",0,NULL,0,0,0,0,0}, + {"geohash",geoHashCommand,-2,"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}, diff --git a/src/redis.h b/src/redis.h index 3e7641a9..4fd643ac 100644 --- a/src/redis.h +++ b/src/redis.h @@ -1563,6 +1563,7 @@ void geoDecodeCommand(redisClient *c); void geoRadiusByMemberCommand(redisClient *c); void geoRadiusCommand(redisClient *c); void geoAddCommand(redisClient *c); +void geoHashCommand(redisClient *c); void pfselftestCommand(redisClient *c); void pfaddCommand(redisClient *c); void pfcountCommand(redisClient *c);