[Groonga-commit] groonga/groonga [master] [geo] geo_in_circle() without index supports approximate type.

Zurück zum Archiv-Index

null+****@clear***** null+****@clear*****
2011年 12月 28日 (水) 15:46:47 JST


Kouhei Sutou	2011-12-28 15:46:47 +0900 (Wed, 28 Dec 2011)

  New Revision: bfcc1ad209db71a72cecedc0f7aa2afe803e76d6

  Log:
    [geo] geo_in_circle() without index supports approximate type.
    
    fixes #1226

  Modified files:
    lib/geo.c
    lib/geo.h
    lib/proc.c
    test/unit/core/test-command-select-geo.c
    test/unit/core/test-geo.c

  Modified: lib/geo.c (+56 -32)
===================================================================
--- lib/geo.c    2011-12-28 15:19:27 +0900 (27c96c7)
+++ lib/geo.c    2011-12-28 15:46:47 +0900 (0f05b13)
@@ -701,6 +701,34 @@ grn_selector_geo_in_circle(grn_ctx *ctx, grn_obj *obj, grn_obj **args, int nargs
   return ctx->rc;
 }
 
+static grn_geo_distance_raw_func
+grn_geo_resolve_distance_raw_func (grn_ctx *ctx,
+                                   grn_geo_approximate_type approximate_type,
+                                   grn_id domain)
+{
+  grn_geo_distance_raw_func distance_raw_func = NULL;
+
+  switch (approximate_type) {
+  case GRN_GEO_APPROXIMATE_RECTANGLE :
+    distance_raw_func = grn_geo_distance_rectangle_raw;
+    break;
+  case GRN_GEO_APPROXIMATE_SPHERE :
+    distance_raw_func = grn_geo_distance_sphere_raw;
+    break;
+  case GRN_GEO_APPROXIMATE_ELLIPSOID :
+    if (domain == GRN_DB_WGS84_GEO_POINT) {
+      distance_raw_func = grn_geo_distance_ellipsoid_raw_wgs84;
+    } else {
+      distance_raw_func = grn_geo_distance_ellipsoid_raw_tokyo;
+    }
+    break;
+  default :
+    break;
+  }
+
+  return distance_raw_func;
+}
+
 grn_rc
 grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
                          grn_obj *center_point, grn_obj *distance,
@@ -743,25 +771,13 @@ grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
   center_longitude = GRN_GEO_INT2RAD(center->longitude);
   center_latitude = GRN_GEO_INT2RAD(center->latitude);
 
-  switch (approximate_type) {
-  case GRN_GEO_APPROXIMATE_RECTANGLE :
-    distance_raw_func = grn_geo_distance_rectangle_raw;
-    break;
-  case GRN_GEO_APPROXIMATE_SPHERE :
-    distance_raw_func = grn_geo_distance_sphere_raw;
-    break;
-  case GRN_GEO_APPROXIMATE_ELLIPSOID :
-    if (domain == GRN_DB_WGS84_GEO_POINT) {
-      distance_raw_func = grn_geo_distance_ellipsoid_raw_wgs84;
-    } else {
-      distance_raw_func = grn_geo_distance_ellipsoid_raw_tokyo;
-    }
-    break;
-  default :
+  distance_raw_func = grn_geo_resolve_distance_raw_func(ctx,
+                                                        approximate_type,
+                                                        domain);
+  if (!distance_raw_func) {
     ERR(GRN_INVALID_ARGUMENT,
         "unknown approximate type: <%d>", approximate_type);
     goto exit;
-    break;
   }
 
   switch (distance->header.domain) {
@@ -1700,38 +1716,47 @@ exit :
 
 grn_bool
 grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
-                  grn_obj *radius_or_point)
+                  grn_obj *radius_or_point,
+                  grn_geo_approximate_type approximate_type)
 {
   grn_bool r = GRN_FALSE;
   grn_obj center_, radius_or_point_;
   grn_id domain = point->header.domain;
   if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
-    double lng0, lat0, lng1, lat1, lng2, lat2, x, y, d;
+    grn_geo_distance_raw_func distance_raw_func;
+    double d;
     if (center->header.domain != domain) {
       GRN_OBJ_INIT(&center_, GRN_BULK, 0, domain);
       if (grn_obj_cast(ctx, center, &center_, 0)) { goto exit; }
       center = &center_;
     }
-    GRN_GEO_POINT_VALUE_RADIUS(point, lat0, lng0);
-    GRN_GEO_POINT_VALUE_RADIUS(center, lat1, lng1);
-    x = (lng1 - lng0) * cos((lat0 + lat1) * 0.5);
-    y = (lat1 - lat0);
-    d = (x * x) + (y * y);
+
+    distance_raw_func = grn_geo_resolve_distance_raw_func(ctx,
+                                                          approximate_type,
+                                                          domain);
+    if (!distance_raw_func) {
+      ERR(GRN_INVALID_ARGUMENT,
+          "unknown approximate type: <%d>", approximate_type);
+      goto exit;
+    }
+    d = distance_raw_func(ctx,
+                          GRN_GEO_POINT_VALUE_RAW(point),
+                          GRN_GEO_POINT_VALUE_RAW(center));
     switch (radius_or_point->header.domain) {
     case GRN_DB_INT32 :
-      r = (sqrt(d) * GRN_GEO_RADIUS) <= GRN_INT32_VALUE(radius_or_point);
+      r = d <= GRN_INT32_VALUE(radius_or_point);
       break;
     case GRN_DB_UINT32 :
-      r = (sqrt(d) * GRN_GEO_RADIUS) <= GRN_UINT32_VALUE(radius_or_point);
+      r = d <= GRN_UINT32_VALUE(radius_or_point);
       break;
     case GRN_DB_INT64 :
-      r = (sqrt(d) * GRN_GEO_RADIUS) <= GRN_INT64_VALUE(radius_or_point);
+      r = d <= GRN_INT64_VALUE(radius_or_point);
       break;
     case GRN_DB_UINT64 :
-      r = (sqrt(d) * GRN_GEO_RADIUS) <= GRN_UINT64_VALUE(radius_or_point);
+      r = d <= GRN_UINT64_VALUE(radius_or_point);
       break;
     case GRN_DB_FLOAT :
-      r = (sqrt(d) * GRN_GEO_RADIUS) <= GRN_FLOAT_VALUE(radius_or_point);
+      r = d <= GRN_FLOAT_VALUE(radius_or_point);
       break;
     case GRN_DB_SHORT_TEXT :
     case GRN_DB_TEXT :
@@ -1743,10 +1768,9 @@ grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
     case GRN_DB_TOKYO_GEO_POINT :
     case GRN_DB_WGS84_GEO_POINT :
       if (domain != radius_or_point->header.domain) { /* todo */ goto exit; }
-      GRN_GEO_POINT_VALUE_RADIUS(radius_or_point, lat2, lng2);
-      x = (lng2 - lng1) * cos((lat1 + lat2) * 0.5);
-      y = (lat2 - lat1);
-      r = d <= (x * x) + (y * y);
+      r = d <= distance_raw_func(ctx,
+                          GRN_GEO_POINT_VALUE_RAW(radius_or_point),
+                          GRN_GEO_POINT_VALUE_RAW(center));
       break;
     default :
       goto exit;

  Modified: lib/geo.h (+2 -1)
===================================================================
--- lib/geo.h    2011-12-28 15:19:27 +0900 (0a0ee5d)
+++ lib/geo.h    2011-12-28 15:46:47 +0900 (a299771)
@@ -139,7 +139,8 @@ grn_rc grn_selector_geo_in_rectangle(grn_ctx *ctx, grn_obj *obj, grn_obj **args,
                                      int nargs, grn_obj *res, grn_operator op);
 
 grn_bool grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
-                           grn_obj *radius_or_point);
+                           grn_obj *radius_or_point,
+                           grn_geo_approximate_type approximate_type);
 grn_bool grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
                               grn_obj *top_left, grn_obj *bottom_right);
 grn_bool grn_geo_in_rectangle_raw(grn_ctx *ctx, grn_geo_point *point,

  Modified: lib/proc.c (+12 -2)
===================================================================
--- lib/proc.c    2011-12-28 15:19:27 +0900 (3990b36)
+++ lib/proc.c    2011-12-28 15:46:47 +0900 (68e15a9)
@@ -2500,8 +2500,18 @@ func_geo_in_circle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_
 {
   grn_obj *obj;
   unsigned char r = GRN_FALSE;
-  if (nargs == 3) {
-    r = grn_geo_in_circle(ctx, args[0], args[1], args[2]);
+  grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+  switch (nargs) {
+  case 4 :
+    if (grn_geo_resolve_approximate_type(ctx, args[3], &type) != GRN_SUCCESS) {
+      break;
+    }
+    /* fallthru */
+  case 3 :
+    r = grn_geo_in_circle(ctx, args[0], args[1], args[2], type);
+    break;
+  default :
+    break;
   }
   if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) {
     GRN_UINT32_SET(ctx, obj, r);

  Modified: test/unit/core/test-command-select-geo.c (+43 -15)
===================================================================
--- test/unit/core/test-command-select-geo.c    2011-12-28 15:19:27 +0900 (c2ed3da)
+++ test/unit/core/test-command-select-geo.c    2011-12-28 15:46:47 +0900 (a3a5ddf)
@@ -21,7 +21,8 @@
 
 #include "../lib/grn-assertions.h"
 
-void test_default(void);
+void data_default(void);
+void test_default(gconstpointer data);
 void data_rectangle(void);
 void test_rectangle(gconstpointer data);
 void data_sphere(void);
@@ -94,7 +95,21 @@ cut_teardown(void)
 }
 
 void
-test_default(void)
+data_default(void)
+{
+#define ADD_DATA(label, use_index)                              \
+  gcut_add_datum(label,                                         \
+                 "use-index", G_TYPE_BOOLEAN, use_index,        \
+                 NULL)
+
+  ADD_DATA("use index", TRUE);
+  ADD_DATA("no index", FALSE);
+
+#undef ADD_DATA
+}
+
+void
+test_default(gconstpointer data)
 {
   gdouble yurakucho_latitude = 35.67487;
   gdouble yurakucho_longitude = 139.76352;
@@ -117,24 +132,28 @@ test_default(void)
         "select Shops "
         "--sortby '+_score, +name' "
         "--output_columns 'name, _score, location' "
-        "--filter 'geo_in_circle(location, \"%s\", %d)' "
+        "--filter 'geo_in_circle(location, \"%s\", %d)%s' "
         "--scorer "
           "'_score = geo_distance(location, \"%s\") * 1000 * 1000'",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         distance,
+        gcut_data_get_boolean(data, "use-index") ? "" : " > 0",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude))));
 }
 
 void
 data_rectangle(void)
 {
-#define ADD_DATA(label, type)                                   \
+#define ADD_DATA(label, type, use_index)                        \
   gcut_add_datum(label,                                         \
                  "approximate-type", G_TYPE_STRING, type,       \
+                 "use-index", G_TYPE_BOOLEAN, use_index,        \
                  NULL)
 
-  ADD_DATA("full", "rectangle");
-  ADD_DATA("abbreviation", "rect");
+  ADD_DATA("full - use index", "rectangle", TRUE);
+  ADD_DATA("full - no index", "rectangle", FALSE);
+  ADD_DATA("abbreviation - use index", "rect", TRUE);
+  ADD_DATA("abbreviation - no index", "rect", FALSE);
 
 #undef ADD_DATA
 }
@@ -165,12 +184,13 @@ test_rectangle(gconstpointer data)
         "select Shops "
         "--sortby '+_score, +name' "
         "--output_columns 'name, _score, location' "
-        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")' "
+        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")%s' "
         "--scorer "
           "'_score = geo_distance(location, \"%s\", \"%s\") * 1000 * 1000'",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         distance,
         approximate_type,
+        gcut_data_get_boolean(data, "use-index") ? "" : " > 0",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         approximate_type)));
 }
@@ -178,13 +198,16 @@ test_rectangle(gconstpointer data)
 void
 data_sphere(void)
 {
-#define ADD_DATA(label, type)                                   \
+#define ADD_DATA(label, type, use_index)                        \
   gcut_add_datum(label,                                         \
                  "approximate-type", G_TYPE_STRING, type,       \
+                 "use-index", G_TYPE_BOOLEAN, use_index,        \
                  NULL)
 
-  ADD_DATA("full", "sphere");
-  ADD_DATA("abbreviation", "sphr");
+  ADD_DATA("full - use index", "sphere", TRUE);
+  ADD_DATA("full - no index", "sphere", FALSE);
+  ADD_DATA("abbreviation - use index", "sphr", TRUE);
+  ADD_DATA("abbreviation - no index", "sphr", FALSE);
 
 #undef ADD_DATA
 }
@@ -215,12 +238,13 @@ test_sphere(gconstpointer data)
         "select Shops "
         "--sortby '+_score, +name' "
         "--output_columns 'name, _score, location' "
-        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")' "
+        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")%s' "
         "--scorer "
           "'_score = geo_distance(location, \"%s\", \"%s\") * 1000 * 1000'",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         distance,
         approximate_type,
+        gcut_data_get_boolean(data, "use-index") ? "" : " > 0",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         approximate_type)));
 }
@@ -228,13 +252,16 @@ test_sphere(gconstpointer data)
 void
 data_ellipsoid(void)
 {
-#define ADD_DATA(label, type)                                   \
+#define ADD_DATA(label, type, use_index)                        \
   gcut_add_datum(label,                                         \
                  "approximate-type", G_TYPE_STRING, type,       \
+                 "use-index", G_TYPE_BOOLEAN, use_index,        \
                  NULL)
 
-  ADD_DATA("full", "ellipsoid");
-  ADD_DATA("abbreviation", "ellip");
+  ADD_DATA("full - use index", "ellipsoid", TRUE);
+  ADD_DATA("full - no index", "ellipsoid", FALSE);
+  ADD_DATA("abbreviation - use index", "ellip", TRUE);
+  ADD_DATA("abbreviation - no index", "ellip", FALSE);
 
 #undef ADD_DATA
 }
@@ -265,12 +292,13 @@ test_ellipsoid(gconstpointer data)
         "select Shops "
         "--sortby '+_score, +name' "
         "--output_columns 'name, _score, location' "
-        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")' "
+        "--filter 'geo_in_circle(location, \"%s\", %d, \"%s\")%s' "
         "--scorer "
           "'_score = geo_distance(location, \"%s\", \"%s\") * 1000 * 1000'",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         distance,
         approximate_type,
+        gcut_data_get_boolean(data, "use-index") ? "" : " > 0",
         grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
         approximate_type)));
 }

  Modified: test/unit/core/test-geo.c (+4 -2)
===================================================================
--- test/unit/core/test-geo.c    2011-12-28 15:19:27 +0900 (5065d91)
+++ test/unit/core/test-geo.c    2011-12-28 15:46:47 +0900 (3060ced)
@@ -185,11 +185,13 @@ test_in_circle(void)
   cut_assert_true(grn_geo_in_circle(context,
                                     hiiragi_wgs84,
                                     shinjuku_wgs84,
-                                    tokyo_wgs84));
+                                    tokyo_wgs84,
+                                    GRN_GEO_APPROXIMATE_RECTANGLE));
   cut_assert_false(grn_geo_in_circle(context,
                                      takane_wgs84,
                                      shinjuku_wgs84,
-                                     tokyo_wgs84));
+                                     tokyo_wgs84,
+                                     GRN_GEO_APPROXIMATE_RECTANGLE));
 }
 
 void




Groonga-commit メーリングリストの案内
Zurück zum Archiv-Index