这个文件不难,主要是实现:geoadd, georadius等命令功能
- #include "geo.h"
- #include "geohash_helper.h"
- #include "debugmacro.h"
- #include "pqsort.h"
-  
- /* Things exported from t_zset.c only for geo.c, since it is the only other
-  * part of Redis that requires close zset introspection. */
- unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
- int zslValueLteMax(double value, zrangespec *spec);
-  
- /* ====================================================================
-  * This file implements the following commands:
-  *
-  *   - geoadd - add coordinates for value to geoset
-  *   - georadius - search radius by coordinates in geoset
-  *   - georadiusbymember - search radius based on geoset member position
-  * ==================================================================== */
-  
- /* ====================================================================
-  * geoArray implementation
-  * ==================================================================== */
-  
- /* Create a new array of geoPoints. */
- geoArray *geoArrayCreate(void) {
-     geoArray *ga = zmalloc(sizeof(*ga));
-     /* It gets allocated on first geoArrayAppend() call. */
-     ga->array = NULL;
-     ga->buckets = 0;
-     ga->used = 0;
-     return ga;
- }
-  
- /* Add a new entry and return its pointer so that the caller can populate
-  * it with data. */
- geoPoint *geoArrayAppend(geoArray *ga) {
-     if (ga->used == ga->buckets) {
-         ga->buckets = (ga->buckets == 0) ? 8 : ga->buckets*2;
-         ga->array = zrealloc(ga->array,sizeof(geoPoint)*ga->buckets);
-     }
-     geoPoint *gp = ga->array+ga->used;
-     ga->used++;
-     return gp;
- }
-  
- /* Destroy a geoArray created with geoArrayCreate(). */
- void geoArrayFree(geoArray *ga) {
-     size_t i;
-     for (i = 0; i < ga->used; i++) sdsfree(ga->array[i].member);
-     zfree(ga->array);
-     zfree(ga);
- }
-  
- /* ====================================================================
-  * Helpers
-  * ==================================================================== */
- int decodeGeohash(double bits, double *xy) {
-     GeoHashBits hash = { .bits = (uint64_t)bits, .step = GEO_STEP_MAX };
-     return geohashDecodeToLongLatWGS84(hash, xy);
- }
-  
- /* Input Argument Helper */
- /* Take a pointer to the latitude arg then use the next arg for longitude.
-  * On parse error C_ERR is returned, otherwise C_OK. */
- int extractLongLatOrReply(client *c, robj **argv, double *xy) {
-     int i;
-     for (i = 0; i < 2; i++) {
-         if (getDoubleFromObjectOrReply(c, argv[i], xy + i, NULL) !=
-             C_OK) {
-             return C_ERR;
-         }
-     }
-     if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX ||
-         xy[1] < GEO_LAT_MIN  || xy[1] > GEO_LAT_MAX) {
-         addReplyErrorFormat(c,
-             "-ERR invalid longitude,latitude pair %f,%f\r\n",xy[0],xy[1]);
-         return C_ERR;
-     }
-     return C_OK;
- }
-  
- /* Input Argument Helper */
- /* Decode lat/long from a zset member's score.
-  * Returns C_OK on successful decoding, otherwise C_ERR is returned. */
- int longLatFromMember(robj *zobj, robj *member, double *xy) {
-     double score = 0;
-  
-     if (zsetScore(zobj, member->ptr, &score) == C_ERR) return C_ERR;
-     if (!decodeGeohash(score, xy)) return C_ERR;
-     return C_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(client *c, robj *unit) {
-     char *u = unit->ptr;
-  
-     if (!strcmp(u, "m")) {
-         return 1;
-     } else if (!strcmp(u, "km")) {
-         return 1000;
-     } else if (!strcmp(u, "ft")) {
-         return 0.3048;
-     } else if (!strcmp(u, "mi")) {
-         return 1609.34;
-     } else {
-         addReplyError(c,
-             "unsupported unit provided. please use m, km, ft, mi");
-         return -1;
-     }
- }
-  
- /* Input Argument Helper.
-  * Extract the distance from the specified two arguments starting at 'argv'
-  * that should be in the form: <number> <unit>, and return C_OK or C_ERR means success or failure
-  * *conversions is populated with the coefficient to use in order to convert meters to the unit.*/
- int extractDistanceOrReply(client *c, robj **argv,
-                               double *conversion, double *radius) {
-     double distance;
-     if (getDoubleFromObjectOrReply(c, argv[0], &distance,
-                                    "need numeric radius") != C_OK) {
-         return C_ERR;
-     }
-  
-     if (distance < 0) {
-         addReplyError(c,"radius cannot be negative");
-         return C_ERR;
-     }
-     if (radius) *radius = distance;
-  
-     double to_meters = extractUnitOrReply(c,argv[1]);
-     if (to_meters < 0) {
-         return C_ERR;
-     }
-  
-     if (conversion) *conversion = to_meters;
-     return C_OK;
- }
-  
- /* Input Argument Helper.
-  * Extract height and width from the specified three arguments starting at 'argv'
-  * that should be in the form: <number> <number> <unit>, and return C_OK or C_ERR means success or failure
-  * *conversions is populated with the coefficient to use in order to convert meters to the unit.*/
- int extractBoxOrReply(client *c, robj **argv, double *conversion,
-                          double *width, double *height) {
-     double h, w;
-     if ((getDoubleFromObjectOrReply(c, argv[0], &w, "need numeric width") != C_OK) ||
-         (getDoubleFromObjectOrReply(c, argv[1], &h, "need numeric height") != C_OK)) {
-         return C_ERR;
-     }
-  
-     if (h < 0 || w < 0) {
-         addReplyError(c, "height or width cannot be negative");
-         return C_ERR;
-     }
-     if (height) *height = h;
-     if (width) *width = w;
-  
-     double to_meters = extractUnitOrReply(c,argv[2]);
-     if (to_meters < 0) {
-         return C_ERR;
-     }
-  
-     if (conversion) *conversion = to_meters;
-     return C_OK;
- }
-  
- /* The default addReplyDouble has too much accuracy.  We use this
-  * for returning location distances. "5.2145 meters away" is nicer
-  * than "5.2144992818115 meters away." We provide 4 digits after the dot
-  * so that the returned value is decently accurate even when the unit is
-  * the kilometer. */
- void addReplyDoubleDistance(client *c, double d) {
-     char dbuf[128];
-     int dlen = snprintf(dbuf, sizeof(dbuf), "%.4f", d);
-     addReplyBulkCBuffer(c, dbuf, dlen);
- }
-  
- /* Helper function for geoGetPointsInRange(): given a sorted set score
-  * representing a point, and a GeoShape, appends this entry as a geoPoint
-  * into the specified geoArray only if the point is within the search area.
-  *
-  * returns C_OK if the point is included, or REIDS_ERR if it is outside. */
- int geoAppendIfWithinShape(geoArray *ga, GeoShape *shape, double score, sds member) {
-     double distance = 0, xy[2];
-  
-     if (!decodeGeohash(score,xy)) return C_ERR; /* Can't decode. */
-     /* Note that geohashGetDistanceIfInRadiusWGS84() takes arguments in
-      * reverse order: longitude first, latitude later. */
-     if (shape->type == CIRCULAR_TYPE) {
-         if (!geohashGetDistanceIfInRadiusWGS84(shape->xy[0], shape->xy[1], xy[0], xy[1],
-                                                shape->t.radius*shape->conversion, &distance)) return C_ERR;
-     } else if (shape->type == RECTANGLE_TYPE) {
-         if (!geohashGetDistanceIfInRectangle(shape->t.r.width * shape->conversion,
-                                              shape->t.r.height * shape->conversion,
-                                              shape->xy[0], shape->xy[1], xy[0], xy[1], &distance))
-             return C_ERR;
-     }
-  
-     /* Append the new element. */
-     geoPoint *gp = geoArrayAppend(ga);
-     gp->longitude = xy[0];
-     gp->latitude = xy[1];
-     gp->dist = distance;
-     gp->member = member;
-     gp->score = score;
-     return C_OK;
- }

 
                

















