null+****@clear*****
null+****@clear*****
2011年 12月 28日 (水) 15:00:14 JST
Kouhei Sutou 2011-12-28 15:00:14 +0900 (Wed, 28 Dec 2011)
New Revision: 591f6c306292d6ab3667aea0dd9e7cb720259aa6
Log:
[geo] geo_in_circle() supports approximate type. refs #1226
geo_in_circle() without index isn't supported yet.
Modified files:
include/groonga.h
lib/geo.c
lib/geo.h
lib/proc.c
test/unit/core/test-command-select-geo.c
Modified: include/groonga.h (+0 -25)
===================================================================
--- include/groonga.h 2011-12-28 14:44:11 +0900 (acfed1b)
+++ include/groonga.h 2011-12-28 15:00:14 +0900 (ba3c6a7)
@@ -1826,31 +1826,6 @@ typedef struct {
/**
- * grn_geo_select_in_circle:
- * @index: the index column for TokyoGeoPoint or WGS84GeoPpoint type.
- * @center_point: the center point of the target circle. (ShortText, Text,
- * LongText, TokyoGeoPoint or WGS84GeoPoint)
- * @distance: the radius of the target circle (Int32,
- * UInt32, Int64, UInt64 or Float) or the point
- * on the circumference of the target circle. (ShortText, Text, LongText,
- * TokyoGeoPoint or WGS84GeoPoint)
- * @res: the table to store found record IDs. It must be
- * GRN_TABLE_HASH_KEY type table.
- * @op: the operator for matched records.
- *
- * It selects records that are in the circle specified by
- * @center_point and @distance from @center_point. Records
- * are searched by @index. Found records are added to @res
- * table with @op operation.
- **/
-GRN_API grn_rc grn_geo_select_in_circle(grn_ctx *ctx,
- grn_obj *index,
- grn_obj *center_point,
- grn_obj *distance,
- grn_obj *res,
- grn_operator op);
-
-/**
* grn_geo_select_in_rectangle:
* @index: the index column for TokyoGeoPoint or WGS84GeoPpoint type.
* @top_left_point: the top left point of the target
Modified: lib/geo.c (+128 -50)
===================================================================
--- lib/geo.c 2011-12-28 14:44:11 +0900 (f0b1e9f)
+++ lib/geo.c 2011-12-28 15:00:14 +0900 (27c96c7)
@@ -632,33 +632,87 @@ grn_geo_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
}
grn_rc
+grn_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;
+}
+
+typedef double (*grn_geo_distance_raw_func)(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+
+grn_rc
grn_selector_geo_in_circle(grn_ctx *ctx, grn_obj *obj, grn_obj **args, int nargs,
grn_obj *res, grn_operator op)
{
- if (nargs == 4) {
- grn_obj *center_point, *distance;
- center_point = args[2];
- distance = args[3];
- grn_geo_select_in_circle(ctx, obj, center_point, distance, res, op);
- } else {
+ grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+
+ switch (nargs) {
+ case 5 :
+ if (grn_geo_resolve_approximate_type(ctx, args[4], &type) != GRN_SUCCESS) {
+ break;
+ }
+ /* fallthru */
+ case 4 :
+ {
+ grn_obj *center_point, *distance;
+ center_point = args[2];
+ distance = args[3];
+ grn_geo_select_in_circle(ctx, obj, center_point, distance, type, res, op);
+ }
+ break;
+ default :
ERR(GRN_INVALID_ARGUMENT,
- "geo_in_circle(): requires 3 arguments but was <%d> arguments",
+ "geo_in_circle(): requires 3 or 4 arguments but was <%d> arguments",
nargs - 1);
+ break;
}
+
return ctx->rc;
}
grn_rc
grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
grn_obj *center_point, grn_obj *distance,
+ grn_geo_approximate_type approximate_type,
grn_obj *res, grn_operator op)
{
grn_id domain;
double center_longitude, center_latitude;
- double on_circle_longitude, on_circle_latitude;
- double x, y, d;
+ double d;
grn_obj *pat, *point_on_circle = NULL, center_point_, point_on_circle_;
grn_geo_point *center, on_circle;
+ grn_geo_distance_raw_func distance_raw_func;
pat = grn_ctx_at(ctx, index->header.domain);
domain = pat->header.domain;
if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
@@ -688,36 +742,53 @@ grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
center = GRN_GEO_POINT_VALUE_RAW(center_point);
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 :
+ ERR(GRN_INVALID_ARGUMENT,
+ "unknown approximate type: <%d>", approximate_type);
+ goto exit;
+ break;
+ }
+
switch (distance->header.domain) {
case GRN_DB_INT32 :
- d = GRN_INT32_VALUE(distance) / (double)GRN_GEO_RADIUS;
- on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d);
+ d = GRN_INT32_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
on_circle.longitude = center->longitude;
- d = d * d;
break;
case GRN_DB_UINT32 :
- d = GRN_UINT32_VALUE(distance) / (double)GRN_GEO_RADIUS;
- on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d);
+ d = GRN_UINT32_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
on_circle.longitude = center->longitude;
- d = d * d;
break;
case GRN_DB_INT64 :
- d = GRN_INT64_VALUE(distance) / (double)GRN_GEO_RADIUS;
- on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d);
+ d = GRN_INT64_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
on_circle.longitude = center->longitude;
- d = d * d;
break;
case GRN_DB_UINT64 :
- d = GRN_UINT64_VALUE(distance) / (double)GRN_GEO_RADIUS;
- on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d);
+ d = GRN_UINT64_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
on_circle.longitude = center->longitude;
- d = d * d;
break;
case GRN_DB_FLOAT :
- d = GRN_FLOAT_VALUE(distance) / (double)GRN_GEO_RADIUS;
- on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d);
+ d = GRN_FLOAT_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
on_circle.longitude = center->longitude;
- d = d * d;
break;
case GRN_DB_SHORT_TEXT :
case GRN_DB_TEXT :
@@ -732,12 +803,7 @@ grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
if (!point_on_circle) { point_on_circle = distance; }
GRN_GEO_POINT_VALUE(point_on_circle,
on_circle.latitude, on_circle.longitude);
- on_circle_longitude = GRN_GEO_INT2RAD(on_circle.longitude);
- on_circle_latitude = GRN_GEO_INT2RAD(on_circle.latitude);
- x = (on_circle_longitude - center_longitude) *
- cos((center_latitude + on_circle_latitude) * 0.5);
- y = (on_circle_latitude - center_latitude);
- d = ((x * x) + (y * y));
+ d = distance_raw_func(ctx, center, &on_circle);
if (point_on_circle == &point_on_circle_) {
grn_obj_unlink(ctx, point_on_circle);
}
@@ -781,16 +847,12 @@ grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
if (tc) {
grn_id tid;
grn_geo_point point;
- double longitude, latitude;
while ((tid = grn_table_cursor_next(ctx, tc))) {
+ double point_distance;
grn_table_get_key(ctx, pat, tid, &point, sizeof(grn_geo_point));
- longitude = GRN_GEO_INT2RAD(point.longitude);
- latitude = GRN_GEO_INT2RAD(point.latitude);
- x = (center_longitude - longitude) *
- cos((latitude + center_latitude) * 0.5);
- y = (center_latitude - latitude);
- if (((x * x) + (y * y)) <= d) {
- inspect_tid(ctx, tid, &point, (x * x) + (y * y));
+ point_distance = distance_raw_func(ctx, &point, center);
+ if (point_distance <= d) {
+ inspect_tid(ctx, tid, &point, point_distance);
grn_ii_at(ctx, (grn_ii *)index, tid, (grn_hash *)res, op);
}
}
@@ -1787,6 +1849,28 @@ grn_geo_distance_ellipsoid_raw(grn_ctx *ctx,
}
double
+grn_geo_distance_ellipsoid_raw_tokyo(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2)
+{
+ return grn_geo_distance_ellipsoid_raw(ctx, point1, point2,
+ GRN_GEO_BES_C1,
+ GRN_GEO_BES_C2,
+ GRN_GEO_BES_C3);
+}
+
+double
+grn_geo_distance_ellipsoid_raw_wgs84(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2)
+{
+ return grn_geo_distance_ellipsoid_raw(ctx, point1, point2,
+ GRN_GEO_GRS_C1,
+ GRN_GEO_GRS_C2,
+ GRN_GEO_GRS_C3);
+}
+
+double
grn_geo_distance(grn_ctx *ctx, grn_obj *point1, grn_obj *point2,
grn_geo_approximate_type type)
{
@@ -1900,19 +1984,13 @@ grn_geo_distance_ellipsoid(grn_ctx *ctx, grn_obj *point1, grn_obj *point2)
point2 = &point2_;
}
if (domain == GRN_DB_TOKYO_GEO_POINT) {
- d = grn_geo_distance_ellipsoid_raw(ctx,
- GRN_GEO_POINT_VALUE_RAW(point1),
- GRN_GEO_POINT_VALUE_RAW(point2),
- GRN_GEO_BES_C1,
- GRN_GEO_BES_C2,
- GRN_GEO_BES_C3);
+ d = grn_geo_distance_ellipsoid_raw_tokyo(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
} else {
- d = grn_geo_distance_ellipsoid_raw(ctx,
- GRN_GEO_POINT_VALUE_RAW(point1),
- GRN_GEO_POINT_VALUE_RAW(point2),
- GRN_GEO_GRS_C1,
- GRN_GEO_GRS_C2,
- GRN_GEO_GRS_C3);
+ d = grn_geo_distance_ellipsoid_raw_wgs84(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
}
} else {
/* todo */
Modified: lib/geo.h (+37 -0)
===================================================================
--- lib/geo.h 2011-12-28 14:44:11 +0900 (d2d285e)
+++ lib/geo.h 2011-12-28 15:00:14 +0900 (0a0ee5d)
@@ -102,6 +102,37 @@ grn_rc grn_geo_cursor_close(grn_ctx *ctx, grn_obj *geo_cursor);
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);
+grn_rc grn_geo_resolve_approximate_type(grn_ctx *ctx, grn_obj *type_name,
+ grn_geo_approximate_type *type);
+
+/**
+ * grn_geo_select_in_circle:
+ * @index: the index column for TokyoGeoPoint or WGS84GeoPpoint type.
+ * @center_point: the center point of the target circle. (ShortText, Text,
+ * LongText, TokyoGeoPoint or WGS84GeoPoint)
+ * @distance: the radius of the target circle (Int32,
+ * UInt32, Int64, UInt64 or Float) or the point
+ * on the circumference of the target circle. (ShortText, Text, LongText,
+ * TokyoGeoPoint or WGS84GeoPoint)
+ * @approximate_type: the approximate type to compute
+ * distance.
+ * @res: the table to store found record IDs. It must be
+ * GRN_TABLE_HASH_KEY type table.
+ * @op: the operator for matched records.
+ *
+ * It selects records that are in the circle specified by
+ * @center_point and @distance from @center_point. Records
+ * are searched by @index. Found records are added to @res
+ * table with @op operation.
+ **/
+grn_rc grn_geo_select_in_circle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *center_point,
+ grn_obj *distance,
+ grn_geo_approximate_type approximate_type,
+ grn_obj *res,
+ grn_operator op);
+
grn_rc grn_selector_geo_in_circle(grn_ctx *ctx, grn_obj *obj, grn_obj **args,
int nargs, grn_obj *res, grn_operator op);
grn_rc grn_selector_geo_in_rectangle(grn_ctx *ctx, grn_obj *obj, grn_obj **args,
@@ -129,6 +160,12 @@ double grn_geo_distance_ellipsoid_raw(grn_ctx *ctx,
grn_geo_point *point1,
grn_geo_point *point2,
int c1, int c2, double c3);
+double grn_geo_distance_ellipsoid_raw_tokyo(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+double grn_geo_distance_ellipsoid_raw_wgs84(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
#ifdef __cplusplus
}
Modified: lib/proc.c (+1 -37)
===================================================================
--- lib/proc.c 2011-12-28 14:44:11 +0900 (8b07cda)
+++ lib/proc.c 2011-12-28 15:00:14 +0900 (3990b36)
@@ -2495,42 +2495,6 @@ 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)
{
@@ -2567,7 +2531,7 @@ func_geo_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_d
grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
switch (nargs) {
case 3 :
- if (geo_resolve_approximate_type(ctx, args[2], &type) != GRN_SUCCESS) {
+ if (grn_geo_resolve_approximate_type(ctx, args[2], &type) != GRN_SUCCESS) {
break;
}
/* fallthru */
Modified: test/unit/core/test-command-select-geo.c (+15 -6)
===================================================================
--- test/unit/core/test-command-select-geo.c 2011-12-28 14:44:11 +0900 (c4fc270)
+++ test/unit/core/test-command-select-geo.c 2011-12-28 15:00:14 +0900 (c2ed3da)
@@ -145,7 +145,9 @@ test_rectangle(gconstpointer data)
gdouble yurakucho_latitude = 35.67487;
gdouble yurakucho_longitude = 139.76352;
gint distance = 3 * 1000;
+ const gchar *approximate_type;
+ approximate_type = gcut_data_get_string(data, "approximate-type");
cut_assert_equal_string(
"[[[7],"
"[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
@@ -163,13 +165,14 @@ test_rectangle(gconstpointer data)
"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\", \"%s\") * 1000 * 1000'",
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
distance,
+ approximate_type,
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
- gcut_data_get_string(data, "approximate-type"))));
+ approximate_type)));
}
void
@@ -192,7 +195,9 @@ test_sphere(gconstpointer data)
gdouble yurakucho_latitude = 35.67487;
gdouble yurakucho_longitude = 139.76352;
gint distance = 3 * 1000;
+ const gchar *approximate_type;
+ approximate_type = gcut_data_get_string(data, "approximate-type");
cut_assert_equal_string(
"[[[7],"
"[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
@@ -210,13 +215,14 @@ test_sphere(gconstpointer data)
"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\", \"%s\") * 1000 * 1000'",
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
distance,
+ approximate_type,
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
- gcut_data_get_string(data, "approximate-type"))));
+ approximate_type)));
}
void
@@ -239,7 +245,9 @@ test_ellipsoid(gconstpointer data)
gdouble yurakucho_latitude = 35.67487;
gdouble yurakucho_longitude = 139.76352;
gint distance = 3 * 1000;
+ const gchar *approximate_type;
+ approximate_type = gcut_data_get_string(data, "approximate-type");
cut_assert_equal_string(
"[[[7],"
"[[\"name\",\"ShortText\"],[\"_score\",\"Int32\"],"
@@ -257,11 +265,12 @@ test_ellipsoid(gconstpointer data)
"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\", \"%s\") * 1000 * 1000'",
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
distance,
+ approximate_type,
grn_test_location_string(yurakucho_latitude, yurakucho_longitude),
- gcut_data_get_string(data, "approximate-type"))));
+ approximate_type)));
}