关键词搜索

源码搜索 ×
×

漫话Redis源码之六十一

发布2022-01-16浏览704次

详情内容

这个文件不难,主要是实现:geoadd, georadius等命令功能

  1. #include "geo.h"
  2. #include "geohash_helper.h"
  3. #include "debugmacro.h"
  4. #include "pqsort.h"
  5. /* Things exported from t_zset.c only for geo.c, since it is the only other
  6. * part of Redis that requires close zset introspection. */
  7. unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
  8. int zslValueLteMax(double value, zrangespec *spec);
  9. /* ====================================================================
  10. * This file implements the following commands:
  11. *
  12. * - geoadd - add coordinates for value to geoset
  13. * - georadius - search radius by coordinates in geoset
  14. * - georadiusbymember - search radius based on geoset member position
  15. * ==================================================================== */
  16. /* ====================================================================
  17. * geoArray implementation
  18. * ==================================================================== */
  19. /* Create a new array of geoPoints. */
  20. geoArray *geoArrayCreate(void) {
  21. geoArray *ga = zmalloc(sizeof(*ga));
  22. /* It gets allocated on first geoArrayAppend() call. */
  23. ga->array = NULL;
  24. ga->buckets = 0;
  25. ga->used = 0;
  26. return ga;
  27. }
  28. /* Add a new entry and return its pointer so that the caller can populate
  29. * it with data. */
  30. geoPoint *geoArrayAppend(geoArray *ga) {
  31. if (ga->used == ga->buckets) {
  32. ga->buckets = (ga->buckets == 0) ? 8 : ga->buckets*2;
  33. ga->array = zrealloc(ga->array,sizeof(geoPoint)*ga->buckets);
  34. }
  35. geoPoint *gp = ga->array+ga->used;
  36. ga->used++;
  37. return gp;
  38. }
  39. /* Destroy a geoArray created with geoArrayCreate(). */
  40. void geoArrayFree(geoArray *ga) {
  41. size_t i;
  42. for (i = 0; i < ga->used; i++) sdsfree(ga->array[i].member);
  43. zfree(ga->array);
  44. zfree(ga);
  45. }
  46. /* ====================================================================
  47. * Helpers
  48. * ==================================================================== */
  49. int decodeGeohash(double bits, double *xy) {
  50. GeoHashBits hash = { .bits = (uint64_t)bits, .step = GEO_STEP_MAX };
  51. return geohashDecodeToLongLatWGS84(hash, xy);
  52. }
  53. /* Input Argument Helper */
  54. /* Take a pointer to the latitude arg then use the next arg for longitude.
  55. * On parse error C_ERR is returned, otherwise C_OK. */
  56. int extractLongLatOrReply(client *c, robj **argv, double *xy) {
  57. int i;
  58. for (i = 0; i < 2; i++) {
  59. if (getDoubleFromObjectOrReply(c, argv[i], xy + i, NULL) !=
  60. C_OK) {
  61. return C_ERR;
  62. }
  63. }
  64. if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX ||
  65. xy[1] < GEO_LAT_MIN || xy[1] > GEO_LAT_MAX) {
  66. addReplyErrorFormat(c,
  67. "-ERR invalid longitude,latitude pair %f,%f\r\n",xy[0],xy[1]);
  68. return C_ERR;
  69. }
  70. return C_OK;
  71. }
  72. /* Input Argument Helper */
  73. /* Decode lat/long from a zset member's score.
  74. * Returns C_OK on successful decoding, otherwise C_ERR is returned. */
  75. int longLatFromMember(robj *zobj, robj *member, double *xy) {
  76. double score = 0;
  77. if (zsetScore(zobj, member->ptr, &score) == C_ERR) return C_ERR;
  78. if (!decodeGeohash(score, xy)) return C_ERR;
  79. return C_OK;
  80. }
  81. /* Check that the unit argument matches one of the known units, and returns
  82. * the conversion factor to meters (you need to divide meters by the conversion
  83. * factor to convert to the right unit).
  84. *
  85. * If the unit is not valid, an error is reported to the client, and a value
  86. * less than zero is returned. */
  87. double extractUnitOrReply(client *c, robj *unit) {
  88. char *u = unit->ptr;
  89. if (!strcmp(u, "m")) {
  90. return 1;
  91. } else if (!strcmp(u, "km")) {
  92. return 1000;
  93. } else if (!strcmp(u, "ft")) {
  94. return 0.3048;
  95. } else if (!strcmp(u, "mi")) {
  96. return 1609.34;
  97. } else {
  98. addReplyError(c,
  99. "unsupported unit provided. please use m, km, ft, mi");
  100. return -1;
  101. }
  102. }
  103. /* Input Argument Helper.
  104. * Extract the distance from the specified two arguments starting at 'argv'
  105. * that should be in the form: <number> <unit>, and return C_OK or C_ERR means success or failure
  106. * *conversions is populated with the coefficient to use in order to convert meters to the unit.*/
  107. int extractDistanceOrReply(client *c, robj **argv,
  108. double *conversion, double *radius) {
  109. double distance;
  110. if (getDoubleFromObjectOrReply(c, argv[0], &distance,
  111. "need numeric radius") != C_OK) {
  112. return C_ERR;
  113. }
  114. if (distance < 0) {
  115. addReplyError(c,"radius cannot be negative");
  116. return C_ERR;
  117. }
  118. if (radius) *radius = distance;
  119. double to_meters = extractUnitOrReply(c,argv[1]);
  120. if (to_meters < 0) {
  121. return C_ERR;
  122. }
  123. if (conversion) *conversion = to_meters;
  124. return C_OK;
  125. }
  126. /* Input Argument Helper.
  127. * Extract height and width from the specified three arguments starting at 'argv'
  128. * that should be in the form: <number> <number> <unit>, and return C_OK or C_ERR means success or failure
  129. * *conversions is populated with the coefficient to use in order to convert meters to the unit.*/
  130. int extractBoxOrReply(client *c, robj **argv, double *conversion,
  131. double *width, double *height) {
  132. double h, w;
  133. if ((getDoubleFromObjectOrReply(c, argv[0], &w, "need numeric width") != C_OK) ||
  134. (getDoubleFromObjectOrReply(c, argv[1], &h, "need numeric height") != C_OK)) {
  135. return C_ERR;
  136. }
  137. if (h < 0 || w < 0) {
  138. addReplyError(c, "height or width cannot be negative");
  139. return C_ERR;
  140. }
  141. if (height) *height = h;
  142. if (width) *width = w;
  143. double to_meters = extractUnitOrReply(c,argv[2]);
  144. if (to_meters < 0) {
  145. return C_ERR;
  146. }
  147. if (conversion) *conversion = to_meters;
  148. return C_OK;
  149. }
  150. /* The default addReplyDouble has too much accuracy. We use this
  151. * for returning location distances. "5.2145 meters away" is nicer
  152. * than "5.2144992818115 meters away." We provide 4 digits after the dot
  153. * so that the returned value is decently accurate even when the unit is
  154. * the kilometer. */
  155. void addReplyDoubleDistance(client *c, double d) {
  156. char dbuf[128];
  157. int dlen = snprintf(dbuf, sizeof(dbuf), "%.4f", d);
  158. addReplyBulkCBuffer(c, dbuf, dlen);
  159. }
  160. /* Helper function for geoGetPointsInRange(): given a sorted set score
  161. * representing a point, and a GeoShape, appends this entry as a geoPoint
  162. * into the specified geoArray only if the point is within the search area.
  163. *
  164. * returns C_OK if the point is included, or REIDS_ERR if it is outside. */
  165. int geoAppendIfWithinShape(geoArray *ga, GeoShape *shape, double score, sds member) {
  166. double distance = 0, xy[2];
  167. if (!decodeGeohash(score,xy)) return C_ERR; /* Can't decode. */
  168. /* Note that geohashGetDistanceIfInRadiusWGS84() takes arguments in
  169. * reverse order: longitude first, latitude later. */
  170. if (shape->type == CIRCULAR_TYPE) {
  171. if (!geohashGetDistanceIfInRadiusWGS84(shape->xy[0], shape->xy[1], xy[0], xy[1],
  172. shape->t.radius*shape->conversion, &distance)) return C_ERR;
  173. } else if (shape->type == RECTANGLE_TYPE) {
  174. if (!geohashGetDistanceIfInRectangle(shape->t.r.width * shape->conversion,
  175. shape->t.r.height * shape->conversion,
  176. shape->xy[0], shape->xy[1], xy[0], xy[1], &distance))
  177. return C_ERR;
  178. }
  179. /* Append the new element. */
  180. geoPoint *gp = geoArrayAppend(ga);
  181. gp->longitude = xy[0];
  182. gp->latitude = xy[1];
  183. gp->dist = distance;
  184. gp->member = member;
  185. gp->score = score;
  186. return C_OK;
  187. }

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载