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))));
+}