[Groonga-commit] groonga/groonga [master] [geo] geo_distance() supports approximate type. refs #1226

Back to archive index

null+****@clear***** null+****@clear*****
2011年 12月 28日 (水) 13:06:41 JST


Kouhei Sutou	2011-12-28 13:06:41 +0900 (Wed, 28 Dec 2011)

  New Revision: 14e1f7f1fc9379355ae6e6f10560fe8995377a26

  Log:
    [geo] geo_distance() supports approximate type. refs #1226
    
    abbreviation isn't supported yet.

  Added files:
    test/unit/core/test-command-select-geo.c
  Modified files:
    doc/source/functions/geo_distance.txt
    lib/proc.c
    test/unit/core/Makefile.am

  Modified: doc/source/functions/geo_distance.txt (+42 -3)
===================================================================
--- doc/source/functions/geo_distance.txt    2011-12-28 13:03:30 +0900 (f220f31)
+++ doc/source/functions/geo_distance.txt    2011-12-28 13:06:41 +0900 (3421451)
@@ -14,7 +14,7 @@ geo_distance - 指定した2点の距離を計算する
 ----
 ::
 
- geo_distance(point1, point2)
+ geo_distance(point1, point2[, approximate_type])
 
 説明
 ----
@@ -22,7 +22,10 @@ geo_distance - 指定した2点の距離を計算する
 groonga組込関数の一つであるgeo_distanceについて説明します。組込関数は、script形式のgrn_expr中で呼び出すことができます。
 
 geo_distance() 関数は、point1に指定した座標とpoint2に指定した座標の間の距離(近似値)を求めます。
-geo_distance()の他に、距離計算アルゴリズムの異なる、geo_distance2()、geo_distance3()が使用できます。それぞれ、長方形近似、球面近似、ヒュベニの距離計算式によって距離を算出します。
+
+... note::
+
+    geo_distance()の他に、距離計算アルゴリズムの異なる、geo_distance2()、geo_distance3()がありましたが、1.2.9から非推奨になりました。 ``geo_distance2()`` の代わりに ``geo_distance(point1, point2, "sphere")`` を、 ``geo_distance3()`` の代わりに ``geo_distance(point1, point2, "ellipsoid")`` を使ってください。
 
 引数
 ----
@@ -35,6 +38,30 @@ geo_distance()の他に、距離計算アルゴリズムの異なる、geo_dista
 
   距離を求める2点のうちもう一つを指定します。GeoPoint型の値、あるいは座標を示す文字列を指定できます。
 
+``approximate_type``
+
+  距離を求めるために地形をどのように近似するかを指定します。指定できる値は以下の通りです。
+
+  ``"rectangle"``
+
+    方形近似で近似します。単純な計算式で距離を求めることができるため高速ですが、極付近では誤差が大きくなります。
+
+    ``"rect"`` と省略して指定することもできます。
+
+    この近似方法がデフォルト値です。 ``approximate_type`` を省略した場合は方形近似になります。
+
+  ``"sphere"``
+
+    球面近似で近似します。 ``"rectangle"`` よりも遅くなりますが、誤差は小さいです。
+
+    ``"sphr"`` と省略して指定することもできます。
+
+  ``"ellipsoid"``
+
+    楕円体近似で近似します。距離の計算にはヒュベニの距離計算式を用います。 ``"sphere"`` よりも遅くなりますが、誤差は小さくなります。
+
+    ``"ellip"`` と省略して指定することもできます。
+
 
 返値
 ----
@@ -46,7 +73,19 @@ geo_distance()の他に、距離計算アルゴリズムの異なる、geo_dista
 ::
 
  geo_distance(pos, "150x150")
- 100
+ 100.0
+
+ # 方形近似を利用
+ geo_distance(pos, "150x150", "rectangle")
+ 100.0
+
+ # 球面近似を利用
+ geo_distance(pos, "150x150", "sphere")
+ 100.0
+
+ # 楕円体近似を利用
+ geo_distance(pos, "150x150", "ellipsoid")
+ 100.0
 
 .. rubric:: 脚注
 

  Modified: lib/proc.c (+53 -3)
===================================================================
--- lib/proc.c    2011-12-28 13:03:30 +0900 (1b8fb2f)
+++ lib/proc.c    2011-12-28 13:06:41 +0900 (9b16044)
@@ -2482,6 +2482,42 @@ func_now(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
   return obj;
 }
 
+static grn_rc
+geo_resolve_approximate_type(grn_ctx *ctx, grn_obj *type_name,
+                             grn_geo_approximate_type *type)
+{
+  grn_rc rc;
+  grn_obj approximate_type;
+
+  GRN_TEXT_INIT(&approximate_type, 0);
+  rc = grn_obj_cast(ctx, type_name, &approximate_type, GRN_FALSE);
+  if (rc == GRN_SUCCESS) {
+    const char *name;
+    unsigned int size;
+    name = GRN_TEXT_VALUE(&approximate_type);
+    size = GRN_TEXT_LEN(&approximate_type);
+    if ((strncmp("rectangle", name, size) == 0) ||
+        (strncmp("rect", name, size) == 0)) {
+      *type = GRN_GEO_APPROXIMATE_RECTANGLE;
+    } else if ((strncmp("sphere", name, size) == 0) ||
+               (strncmp("sphr", name, size) == 0)) {
+      *type = GRN_GEO_APPROXIMATE_SPHERE;
+    } else if ((strncmp("ellipsoid", name, size) == 0) ||
+               (strncmp("ellip", name, size) == 0)) {
+      *type = GRN_GEO_APPROXIMATE_ELLIPSOID;
+    } else {
+      ERR(GRN_INVALID_ARGUMENT,
+          "geo distance approximate type must be one of "
+          "[rectangle, rect, sphere, sphr, ellipsoid, ellip]"
+          ": <%.*s>",
+          size, name);
+    }
+  }
+  GRN_OBJ_FIN(ctx, &approximate_type);
+
+  return rc;
+}
+
 static grn_obj *
 func_geo_in_circle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
 {
@@ -2514,9 +2550,19 @@ static grn_obj *
 func_geo_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
 {
   grn_obj *obj;
-  double d = 0;
-  if (nargs == 2) {
-    d = grn_geo_distance(ctx, args[0], args[1]);
+  double d = 0.0;
+  grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+  switch (nargs) {
+  case 3 :
+    if (geo_resolve_approximate_type(ctx, args[2], &type) != GRN_SUCCESS) {
+      break;
+    }
+    /* fallthru */
+  case 2 :
+    d = grn_geo_distance(ctx, args[0], args[1], type);
+    break;
+  default:
+    break;
   }
   if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) {
     GRN_FLOAT_SET(ctx, obj, d);
@@ -2524,6 +2570,7 @@ func_geo_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_d
   return obj;
 }
 
+/* deprecated. */
 static grn_obj *
 func_geo_distance2(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
 {
@@ -2538,6 +2585,7 @@ func_geo_distance2(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_
   return obj;
 }
 
+/* deprecated. */
 static grn_obj *
 func_geo_distance3(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
 {
@@ -2742,9 +2790,11 @@ grn_db_init_builtin_query(grn_ctx *ctx)
   grn_proc_create(ctx, "geo_distance", 12, GRN_PROC_FUNCTION,
                   func_geo_distance, NULL, NULL, 0, NULL);
 
+  /* deprecated. */
   grn_proc_create(ctx, "geo_distance2", 13, GRN_PROC_FUNCTION,
                   func_geo_distance2, NULL, NULL, 0, NULL);
 
+  /* deprecated. */
   grn_proc_create(ctx, "geo_distance3", 13, GRN_PROC_FUNCTION,
                   func_geo_distance3, NULL, NULL, 0, NULL);
 

  Modified: test/unit/core/Makefile.am (+2 -0)
===================================================================
--- test/unit/core/Makefile.am    2011-12-28 13:03:30 +0900 (9cd5e56)
+++ test/unit/core/Makefile.am    2011-12-28 13:06:41 +0900 (4427e88)
@@ -54,6 +54,7 @@ noinst_LTLIBRARIES =				\
 	test-command-select-query.la		\
 	test-command-select-filter.la		\
 	test-command-select-query-expansion.la	\
+	test-command-select-geo.la		\
 	test-command-define-selector.la		\
 	test-command-cache-limit.la		\
 	test-command-delete.la			\
@@ -139,6 +140,7 @@ test_command_select_filter_invalid_la_SOURCES	= test-command-select-filter-inval
 test_command_select_query_la_SOURCES	= test-command-select-query.c
 test_command_select_filter_la_SOURCES	= test-command-select-filter.c
 test_command_select_query_expansion_la_SOURCES	= test-command-select-query-expansion.c
+test_command_select_geo_la_SOURCES	= test-command-select-geo.c
 test_command_define_selector_la_SOURCES	= test-command-define-selector.c
 test_command_cache_limit_la_SOURCES	= test-command-cache-limit.c
 test_command_delete_la_SOURCES		= test-command-delete.c

  Added: test/unit/core/test-command-select-geo.c (+219 -0) 100644
===================================================================
--- /dev/null
+++ test/unit/core/test-command-select-geo.c    2011-12-28 13:06:41 +0900 (7e2c7ec)
@@ -0,0 +1,219 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+  Copyright (C) 2011  Kouhei Sutou <kou****@clear*****>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <gcutter.h>
+#include <glib/gstdio.h>
+
+#include "../lib/grn-assertions.h"
+
+void test_default(void);
+void test_rectangle(void);
+void test_sphere(void);
+void test_ellipsoid(void);
+
+static gchar *tmp_directory;
+
+static grn_ctx *context;
+static grn_obj *database;
+
+void
+cut_startup(void)
+{
+  tmp_directory = g_build_filename(grn_test_get_tmp_dir(),
+                                   "command-select-geo-in-rectangle",
+                                   NULL);
+}
+
+void
+cut_shutdown(void)
+{
+  g_free(tmp_directory);
+}
+
+static void
+remove_tmp_directory(void)
+{
+  cut_remove_path(tmp_directory, NULL);
+}
+
+void
+cut_setup(void)
+{
+  const gchar *database_path;
+
+  cut_set_fixture_data_dir(grn_test_get_base_dir(),
+                           "fixtures",
+                           "story",
+                           "taiyaki",
+                           NULL);
+
+  remove_tmp_directory();
+  g_mkdir_with_parents(tmp_directory, 0700);
+
+  context = g_new0(grn_ctx, 1);
+  grn_ctx_init(context, 0);
+
+  database_path = cut_build_path(tmp_directory, "database.groonga", NULL);
+  database = grn_db_create(context, database_path, NULL);
+
+  assert_send_commands(cut_get_fixture_data_string("ddl.grn", NULL));
+  assert_send_command(cut_get_fixture_data_string("areas.grn", NULL));
+  assert_send_command(cut_get_fixture_data_string("categories.grn", NULL));
+  assert_send_command(cut_get_fixture_data_string("shops.grn", NULL));
+  assert_send_command(cut_get_fixture_data_string("synonyms.grn", NULL));
+}
+
+void
+cut_teardown(void)
+{
+  if (context) {
+    grn_obj_unlink(context, database);
+    grn_ctx_fin(context);
+    g_free(context);
+  }
+
+  remove_tmp_directory();
+}
+
+void
+test_default(void)
+{
+  gdouble yurakucho_latitude = 35.67487;
+  gdouble yurakucho_longitude = 139.76352;
+  gint distance = 3 * 1000;
+
+  cut_assert_equal_string(
+    "[[[7],"
+    "[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
+    "[\"location\",\"WGS84GeoPoint\"]],"
+    "[\"柳屋 たい焼き\",-2147483648,\"128467228x503222332\"],"
+    "[\"銀座 かずや\",280743810,\"128424629x503139222\"],"
+    "[\"たい焼き鉄次 大丸東京店\",810303031,\"128451283x503166852\"],"
+    "[\"たいやき神田達磨 八重洲店\",970517026,\"128453260x503174156\"],"
+    "[\"にしみや 甘味処\",1056698886,\"128418570x503188661\"],"
+    "[\"築地 さのきや\",1186376492,\"128397312x503174596\"],"
+    "[\"しげ田\",1530425643,\"128421454x503208983\"]"
+    "]]",
+    send_command(
+      cut_take_printf(
+        "select Shops "
+        "--sortby '+_score, +name' "
+        "--output_columns 'name, _score, location' "
+        "--filter 'geo_in_circle(location, \"%s\", %d)' "
+        "--scorer "
+          "'_score = geo_distance(location, \"%s\") * 1000 * 1000'",
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
+        distance,
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude))));
+}
+
+void
+test_rectangle(void)
+{
+  gdouble yurakucho_latitude = 35.67487;
+  gdouble yurakucho_longitude = 139.76352;
+  gint distance = 3 * 1000;
+
+  cut_assert_equal_string(
+    "[[[7],"
+    "[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
+    "[\"location\",\"WGS84GeoPoint\"]],"
+    "[\"柳屋 たい焼き\",-2147483648,\"128467228x503222332\"],"
+    "[\"銀座 かずや\",280743810,\"128424629x503139222\"],"
+    "[\"たい焼き鉄次 大丸東京店\",810303031,\"128451283x503166852\"],"
+    "[\"たいやき神田達磨 八重洲店\",970517026,\"128453260x503174156\"],"
+    "[\"にしみや 甘味処\",1056698886,\"128418570x503188661\"],"
+    "[\"築地 さのきや\",1186376492,\"128397312x503174596\"],"
+    "[\"しげ田\",1530425643,\"128421454x503208983\"]"
+    "]]",
+    send_command(
+      cut_take_printf(
+        "select Shops "
+        "--sortby '+_score, +name' "
+        "--output_columns 'name, _score, location' "
+        "--filter 'geo_in_circle(location, \"%s\", %d)' "
+        "--scorer "
+          "'_score = geo_distance(location, \"%s\", \"rectangle\") * 1000 * 1000'",
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
+        distance,
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude))));
+}
+
+void
+test_sphere(void)
+{
+  gdouble yurakucho_latitude = 35.67487;
+  gdouble yurakucho_longitude = 139.76352;
+  gint distance = 3 * 1000;
+
+  cut_assert_equal_string(
+    "[[[7],"
+    "[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
+    "[\"location\",\"WGS84GeoPoint\"]],"
+    "[\"柳屋 たい焼き\",-2147483648,\"128467228x503222332\"],"
+    "[\"銀座 かずや\",280743810,\"128424629x503139222\"],"
+    "[\"たい焼き鉄次 大丸東京店\",810303031,\"128451283x503166852\"],"
+    "[\"たいやき神田達磨 八重洲店\",970517025,\"128453260x503174156\"],"
+    "[\"にしみや 甘味処\",1056698886,\"128418570x503188661\"],"
+    "[\"築地 さのきや\",1186376491,\"128397312x503174596\"],"
+    "[\"しげ田\",1530425641,\"128421454x503208983\"]"
+    "]]",
+    send_command(
+      cut_take_printf(
+        "select Shops "
+        "--sortby '+_score, +name' "
+        "--output_columns 'name, _score, location' "
+        "--filter 'geo_in_circle(location, \"%s\", %d)' "
+        "--scorer "
+          "'_score = geo_distance(location, \"%s\", \"sphere\") * 1000 * 1000'",
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
+        distance,
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude))));
+}
+
+void
+test_ellipsoid(void)
+{
+  gdouble yurakucho_latitude = 35.67487;
+  gdouble yurakucho_longitude = 139.76352;
+  gint distance = 3 * 1000;
+
+  cut_assert_equal_string(
+    "[[[7],"
+    "[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
+    "[\"location\",\"WGS84GeoPoint\"]],"
+    "[\"柳屋 たい焼き\",-2147483648,\"128467228x503222332\"],"
+    "[\"銀座 かずや\",281623827,\"128424629x503139222\"],"
+    "[\"たい焼き鉄次 大丸東京店\",811420890,\"128451283x503166852\"],"
+    "[\"たいやき神田達磨 八重洲店\",972359708,\"128453260x503174156\"],"
+    "[\"にしみや 甘味処\",1060891168,\"128418570x503188661\"],"
+    "[\"築地 さのきや\",1187926579,\"128397312x503174596\"],"
+    "[\"しげ田\",1537012099,\"128421454x503208983\"]"
+    "]]",
+    send_command(
+      cut_take_printf(
+        "select Shops "
+        "--sortby '+_score, +name' "
+        "--output_columns 'name, _score, location' "
+        "--filter 'geo_in_circle(location, \"%s\", %d)' "
+        "--scorer "
+          "'_score = geo_distance(location, \"%s\", \"ellipsoid\") * 1000 * 1000'",
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
+        distance,
+        grn_test_location_string(yurakucho_latitude, yurakucho_longitude))));
+}




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