[Groonga-commit] groonga/groonga [master] support geo_in_rectangle() with index.

Back to archive index

null+****@clear***** null+****@clear*****
2010年 8月 12日 (木) 11:48:01 JST


Kouhei Sutou	2010-08-12 02:48:01 +0000 (Thu, 12 Aug 2010)

  New Revision: d9fecde584b5339c17a326f875d21f23c864f3ed

  Log:
    support geo_in_rectangle() with index.

  Modified files:
    lib/expr.c
    lib/geo.c
    lib/geo.h
    test/unit/story/test-taiyaki.c

  Modified: lib/expr.c (+13 -4)
===================================================================
--- lib/expr.c    2010-08-12 02:14:07 +0000 (f7b1fd5)
+++ lib/expr.c    2010-08-12 02:48:01 +0000 (b61a919)
@@ -3983,12 +3983,21 @@ grn_table_select(grn_ctx *ctx, grn_obj *table, grn_obj *expr,
               }
               break;
             case GRN_OP_CALL :
-              /* geo_in_circle only */
               if (si->flags & SCAN_ACCESSOR) {
               } else {
-                grn_geo_search_in_circle(ctx, index, si->args, si->nargs, res,
-                                         si->logical_op);
-                done++;
+                char buf[GRN_TABLE_MAX_KEY_SIZE];
+                int len = grn_obj_name(ctx, si->args[0], buf,
+                                       GRN_TABLE_MAX_KEY_SIZE);
+                /* geo_in_circle and geo_in_rectangle only */
+                if (len == 13 && !memcmp(buf, "geo_in_circle", 13)) {
+                  grn_geo_search_in_circle(ctx, index, si->args, si->nargs, res,
+                                           si->logical_op);
+                  done++;
+                } else if (len == 16 && !memcmp(buf, "geo_in_rectangle", 16)) {
+                  grn_geo_search_in_rectangle(ctx, index, si->args, si->nargs,
+                                              res, si->logical_op);
+                  done++;
+                }
               }
               break;
             default :

  Modified: lib/geo.c (+140 -15)
===================================================================
--- lib/geo.c    2010-08-12 02:14:07 +0000 (b354237)
+++ lib/geo.c    2010-08-12 02:48:01 +0000 (a152e94)
@@ -160,24 +160,22 @@ typedef struct
   int key_size;
 } mesh_entry;
 
-/* #define GEO_SORT_DEBUG */
+/* #define GEO_DEBUG */
 
-#ifdef GEO_SORT_DEBUG
+#ifdef GEO_DEBUG
 #include <stdio.h>
 
 static void
-inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
+inspect_mesh(grn_ctx *ctx, grn_geo_point *key, int key_size, int n)
 {
-  mesh_entry *entry;
   grn_geo_point min, max;
 
-  entry = entries + n;
-  printf("mesh: %d:%d\n", n, entry->key_size);
+  printf("mesh: %d:%d\n", n, key_size);
 
   printf("key: ");
-  grn_p_geo_point(ctx, &(entry->key));
+  grn_p_geo_point(ctx, key);
 
-  compute_min_and_max(&(entry->key), entry->key_size, &min, &max);
+  compute_min_and_max(key, key_size, &min, &max);
   printf("min: ");
   grn_p_geo_point(ctx, &min);
   printf("max: ");
@@ -185,12 +183,22 @@ inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
 }
 
 static void
+inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
+{
+  mesh_entry *entry;
+
+  entry = entries + n;
+  inspect_mesh(ctx, &(entry->key), entry->key_size, n);
+}
+
+static void
 inspect_tid(grn_ctx *ctx, grn_id tid, grn_geo_point *point, double d)
 {
   printf("tid: %d:%g", tid, d);
   grn_p_geo_point(ctx, point);
 }
 #else
+#  define inspect_mesh(...)
 #  define inspect_mesh_entry(...)
 #  define inspect_tid(...)
 #endif
@@ -262,7 +270,7 @@ grn_geo_get_meshes_for_circle(grn_ctx *ctx, grn_geo_point *base_point,
     grn_min + lat_diff -> the right mesh.
     grn_min + lng_diff -> the top mesh.
    */
-#ifdef GEO_SORT_DEBUG
+#ifdef GEO_DEBUG
   grn_p_geo_point(ctx, base_point);
   printf("base: ");
   grn_p_geo_point(ctx, &geo_base);
@@ -608,6 +616,115 @@ exit :
   return ctx->rc;
 }
 
+typedef enum {
+  MESH_LATITUDE,
+  MESH_LONGITUDE
+} mesh_direction;
+
+grn_rc
+grn_geo_search_in_rectangle(grn_ctx *ctx, grn_obj *obj, grn_obj **args, int nargs,
+                            grn_obj *res, grn_operator op)
+{
+  grn_id domain;
+  grn_obj *proc, *pat, *pos1, *pos2, pos1_, pos2_;
+  grn_geo_point *geo_point1, *geo_point2;
+  if (nargs != 4) { goto exit; }
+  pat = grn_ctx_at(ctx, obj->header.domain);
+  proc = args[0];
+  pos1 = args[2];
+  pos2 = args[3];
+  domain = pat->header.domain;
+  if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) { goto exit; }
+  if (pos1->header.domain != domain) {
+    GRN_OBJ_INIT(&pos1_, GRN_BULK, 0, domain);
+    if (grn_obj_cast(ctx, pos1, &pos1_, 0)) { goto exit; }
+    pos1 = &pos1_;
+  }
+  geo_point1 = GRN_GEO_POINT_VALUE_RAW(pos1);
+  if (pos2->header.domain != domain) {
+    GRN_OBJ_INIT(&pos2_, GRN_BULK, 0, domain);
+    if (grn_obj_cast(ctx, pos2, &pos2_, 0)) { goto exit; }
+    pos2 = &pos2_;
+  }
+  geo_point2 = GRN_GEO_POINT_VALUE_RAW(pos2);
+  {
+    mesh_direction direction;
+    int distance, latitude_distance, longitude_distance;
+    int i, start, end, diff_bit;
+    grn_geo_point geo_point_base, geo_point_min, geo_point_max;
+    uint8_t geo_key1[sizeof(grn_geo_point)];
+    uint8_t geo_key_base[sizeof(grn_geo_point)];
+
+    latitude_distance = geo_point1->latitude - geo_point2->latitude;
+    longitude_distance = geo_point2->longitude - geo_point1->longitude;
+    if (latitude_distance > longitude_distance) {
+      direction = MESH_LATITUDE;
+      distance = latitude_distance;
+      geo_point_base.latitude = geo_point1->latitude;
+      geo_point_base.longitude = geo_point1->longitude + distance;
+      end = geo_point2->latitude;
+    } else {
+      direction = MESH_LONGITUDE;
+      distance = longitude_distance;
+      geo_point_base.latitude = geo_point1->latitude + distance;
+      geo_point_base.longitude = geo_point1->longitude;
+      end = geo_point2->longitude;
+    }
+    grn_gton(geo_key1, geo_point1, sizeof(grn_geo_point));
+    grn_gton(geo_key_base, &geo_point_base, sizeof(grn_geo_point));
+    diff_bit = compute_diff_bit(geo_key1, geo_key_base);
+    compute_min_and_max(geo_point1, diff_bit + 2, &geo_point_min, &geo_point_max);
+    if (direction == MESH_LATITUDE) {
+      start = geo_point_max.latitude;
+    } else {
+      start = geo_point_max.longitude;
+    }
+    memcpy(&geo_point_base, &geo_point_max, sizeof(grn_geo_point));
+#ifdef GEO_DEBUG
+    printf("direction: %s\n",
+           direction == MESH_LATITUDE ? "latitude" : "longitude");
+    printf("base:         ");
+    grn_p_geo_point(ctx, &geo_point_base);
+    printf("top-left:     ");
+    grn_p_geo_point(ctx, geo_point1);
+    printf("bottom-right: ");
+    grn_p_geo_point(ctx, geo_point2);
+#endif
+
+    for (i = start; i < end + distance; i += distance) {
+      grn_table_cursor *tc;
+      if (direction == MESH_LATITUDE) {
+        geo_point_base.latitude += direction;
+      } else {
+        geo_point_base.longitude += direction;
+      }
+      tc = grn_table_cursor_open(ctx, pat,
+                                 &geo_point_base, diff_bit + 1,
+                                 NULL, 0,
+                                 0, -1,
+                                 GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
+      inspect_mesh(ctx, &geo_point_base, diff_bit, (i - start) / distance);
+      if (tc) {
+        grn_id tid;
+        grn_geo_point pos;
+        while ((tid = grn_table_cursor_next(ctx, tc))) {
+          if (i == start || end < i) {
+            grn_table_get_key(ctx, pat, tid, &pos, sizeof(grn_geo_point));
+            if (!grn_geo_in_rectangle_raw(ctx, &pos, geo_point1, geo_point2)) {
+              continue;
+            }
+          }
+          grn_ii_at(ctx, (grn_ii *)obj, tid, (grn_hash *)res, op);
+        }
+        grn_table_cursor_close(ctx, tc);
+      }
+    }
+  }
+exit :
+  grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+  return ctx->rc;
+}
+
 unsigned
 grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
                   grn_obj *radius_or_point)
@@ -669,6 +786,16 @@ exit :
 }
 
 unsigned
+grn_geo_in_rectangle_raw(grn_ctx *ctx, grn_geo_point *point,
+                         grn_geo_point *top_left, grn_geo_point *bottom_right)
+{
+    return ((top_left->longitude <= point->longitude) &&
+            (point->longitude <= bottom_right->longitude) &&
+            (bottom_right->latitude <= point->latitude) &&
+            (point->latitude <= top_left->latitude));
+}
+
+unsigned
 grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
                      grn_obj *top_left, grn_obj *bottom_right)
 {
@@ -676,7 +803,6 @@ grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
   grn_obj top_left_, bottom_right_;
   grn_id domain = point->header.domain;
   if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
-    int lat0, lng0, lat1, lng1, lat2, lng2;
     if (top_left->header.domain != domain) {
       GRN_OBJ_INIT(&top_left_, GRN_BULK, 0, domain);
       if (grn_obj_cast(ctx, top_left, &top_left_, 0)) { goto exit; }
@@ -687,11 +813,10 @@ grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
       if (grn_obj_cast(ctx, bottom_right, &bottom_right_, 0)) { goto exit; }
       bottom_right = &bottom_right_;
     }
-    GRN_GEO_POINT_VALUE(point, lat0, lng0);
-    GRN_GEO_POINT_VALUE(top_left, lat1, lng1);
-    GRN_GEO_POINT_VALUE(bottom_right, lat2, lng2);
-    r = ((lng1 <= lng0) && (lng0 <= lng2) &&
-         (lat2 <= lat0) && (lat0 <= lat1));
+    r = grn_geo_in_rectangle_raw(ctx,
+                                 GRN_GEO_POINT_VALUE_RAW(point),
+                                 GRN_GEO_POINT_VALUE_RAW(top_left),
+                                 GRN_GEO_POINT_VALUE_RAW(bottom_right));
   } else {
     /* todo */
   }

  Modified: lib/geo.h (+5 -0)
===================================================================
--- lib/geo.h    2010-08-12 02:14:07 +0000 (aa9f973)
+++ lib/geo.h    2010-08-12 02:48:01 +0000 (d9a1bbe)
@@ -47,6 +47,8 @@ extern "C" {
 
 grn_rc grn_geo_search_in_circle(grn_ctx *ctx, grn_obj *obj, grn_obj **args,
                                 int nargs, grn_obj *res, grn_operator op);
+grn_rc grn_geo_search_in_rectangle(grn_ctx *ctx, grn_obj *obj, grn_obj **args,
+                                   int nargs, grn_obj *res, grn_operator op);
 int grn_geo_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
                        grn_obj *result, grn_table_sort_key *keys, int n_keys);
 
@@ -54,6 +56,9 @@ unsigned grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
                            grn_obj *radius_or_point);
 unsigned grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
                               grn_obj *top_left, grn_obj *bottom_right);
+unsigned grn_geo_in_rectangle_raw(grn_ctx *ctx, grn_geo_point *point,
+                                  grn_geo_point *top_left,
+                                  grn_geo_point *bottom_right);
 double grn_geo_distance(grn_ctx *ctx, grn_obj *point1, grn_obj *point2);
 double grn_geo_distance2(grn_ctx *ctx, grn_obj *point1, grn_obj *point2);
 double grn_geo_distance3(grn_ctx *ctx, grn_obj *point1, grn_obj *point2);

  Modified: test/unit/story/test-taiyaki.c (+1 -1)
===================================================================
--- test/unit/story/test-taiyaki.c    2010-08-12 02:14:07 +0000 (cae8268)
+++ test/unit/story/test-taiyaki.c    2010-08-12 02:48:01 +0000 (b5b40b4)
@@ -144,7 +144,7 @@ test_in_rectangle(void)
         "select Shops "
         "--sortby '+_score, +name' "
         "--output_columns 'name, _score' "
-        "--filter 'geo_in_rectangle(location, \"%s\", \"%s\") > 0' "
+        "--filter 'geo_in_rectangle(location, \"%s\", \"%s\")' "
         "--scorer '_score=geo_distance(location, \"%s\")'",
         grn_test_location_string(takada_no_baba_latitude,
                                  takada_no_baba_longitude),




Groonga-commit メーリングリストの案内
Back to archive index