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