[Groonga-commit] groonga/groonga [master] move grn_table_sort_geo() to geo.c.

Back to archive index

null+****@clear***** null+****@clear*****
2010年 8月 9日 (月) 14:21:17 JST


Kouhei Sutou	2010-08-09 05:21:17 +0000 (Mon, 09 Aug 2010)

  New Revision: be60ac31368122b5a2b91597470c28f8e6e7d237

  Log:
    move grn_table_sort_geo() to geo.c.

  Modified files:
    lib/db.c
    lib/geo.c
    lib/geo.h

  Modified: lib/db.c (+1 -452)
===================================================================
--- lib/db.c    2010-08-09 01:43:43 +0000 (bef5a31)
+++ lib/db.c    2010-08-09 05:21:17 +0000 (90d5f09)
@@ -6502,457 +6502,6 @@ range_is_idp(grn_obj *obj)
   return 0;
 }
 
-#define GEO_RESOLUTION   3600000
-#define GEO_RADIOUS      6357303
-#define GEO_BES_C1       6334834
-#define GEO_BES_C2       6377397
-#define GEO_BES_C3       0.006674
-#define GEO_GRS_C1       6335439
-#define GEO_GRS_C2       6378137
-#define GEO_GRS_C3       0.006694
-#define GEO_INT2RAD(x)   ((M_PI / (GEO_RESOLUTION * 180)) * (x))
-
-typedef struct {
-  grn_id id;
-  double d;
-} geo_entry;
-
-static int
-compute_diff_bit(uint8_t *geo_key1, uint8_t *geo_key2)
-{
-  int i, diff_bit = 0;
-
-  for (i = 0; i < sizeof(grn_geo_point); i++) {
-    if (geo_key1[i] == geo_key2[i]) {
-      continue;
-    }
-
-    if ((geo_key1[i] & 0xc0) != (geo_key2[i] & 0xc0)) {
-      diff_bit = 0;
-      break;
-    } else if ((geo_key1[i] & 0x30) != (geo_key2[i] & 0x30)) {
-      diff_bit = 2;
-      break;
-    } else if ((geo_key1[i] & 0x0c) != (geo_key2[i] & 0x0c)) {
-      diff_bit = 4;
-      break;
-    } else if ((geo_key1[i] & 0x03) != (geo_key2[i] & 0x03)) {
-      diff_bit = 6;
-      break;
-    }
-  }
-
-  return i * 8 + diff_bit;
-}
-
-static void
-compute_min_and_max(grn_geo_point *base_point, int diff_bit,
-                    grn_geo_point *geo_min, grn_geo_point *geo_max)
-{
-  int diff_byte, diff_bit_mask;
-  uint8_t geo_key_base[sizeof(grn_geo_point)];
-  uint8_t geo_key_min[sizeof(grn_geo_point)];
-  uint8_t geo_key_max[sizeof(grn_geo_point)];
-
-  diff_byte = diff_bit / 8;
-  diff_bit_mask = 0xff >> (diff_bit % 8);
-  grn_gton(geo_key_base, base_point, sizeof(grn_geo_point));
-
-  memcpy(geo_key_min, geo_key_base, diff_byte + 1);
-  geo_key_min[diff_byte] &= ~diff_bit_mask;
-  memset(geo_key_min + diff_byte + 1, 0,
-         sizeof(grn_geo_point) - diff_byte - 1);
-
-  memcpy(geo_key_max, geo_key_base, diff_byte + 1);
-  geo_key_max[diff_byte] |= diff_bit_mask;
-  memset(geo_key_max + diff_byte + 1, 0xff,
-         sizeof(grn_geo_point) - diff_byte - 1);
-
-  grn_ntog((uint8_t *)geo_min, geo_key_min, sizeof(grn_geo_point));
-  grn_ntog((uint8_t *)geo_max, geo_key_max, sizeof(grn_geo_point));
-}
-
-static int
-grn_table_sort_geo_detect_far_point(grn_ctx *ctx, grn_obj *table, grn_obj *index,
-                                    grn_pat *pat, geo_entry *entries,
-                                    grn_pat_cursor *pc, int n, int accessorp,
-                                    grn_geo_point *base_point,
-                                    double *d_far, int *diff_bit)
-{
-  int i = 0, diff_bit_prev, diff_bit_current;
-  grn_id tid;
-  geo_entry *ep, *p;
-  double d;
-  uint8_t geo_key_prev[sizeof(grn_geo_point)];
-  uint8_t geo_key_curr[sizeof(grn_geo_point)];
-  grn_geo_point point;
-
-  *d_far = 0.0;
-  grn_gton(geo_key_curr, base_point, sizeof(grn_geo_point));
-  *diff_bit = sizeof(grn_geo_point) * 8;
-  diff_bit_current = sizeof(grn_geo_point) * 8;
-  memcpy(&point, base_point, sizeof(grn_geo_point));
-  ep = entries;
-  while ((tid = grn_pat_cursor_next(ctx, pc))) {
-    grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
-    if (ic) {
-      grn_ii_posting *posting;
-      grn_gton(geo_key_prev, &point, sizeof(grn_geo_point));
-      grn_pat_get_key(ctx, pat, tid, &point, sizeof(grn_geo_point));
-      grn_gton(geo_key_curr, &point, sizeof(grn_geo_point));
-      d = grn_geo_distance_raw(ctx, base_point, &point);
-
-      diff_bit_prev = diff_bit_current;
-      diff_bit_current = compute_diff_bit(geo_key_curr, geo_key_prev);
-      if (diff_bit_current < diff_bit_prev && *diff_bit > diff_bit_current) {
-        if (i == n) {
-          break;
-        }
-        *diff_bit = diff_bit_current;
-      }
-
-      if (d > *d_far) {
-        *d_far = d;
-      }
-      while ((posting = grn_ii_cursor_next(ctx, ic))) {
-        grn_id rid = accessorp
-          ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
-          : posting->rid;
-        if (rid) {
-          for (p = ep; entries < p && p[-1].d > d; p--) {
-            p->id = p[-1].id;
-            p->d = p[-1].d;
-          }
-          p->id = rid;
-          p->d = d;
-          if (i < n) {
-            ep++;
-            i++;
-          }
-        }
-      }
-      grn_ii_cursor_close(ctx, ic);
-    }
-  }
-
-  return i;
-}
-
-typedef struct
-{
-  grn_geo_point key;
-  int key_size;
-} mesh_entry;
-
-/* #define GEO_SORT_DEBUG */
-
-#ifdef GEO_SORT_DEBUG
-#include <stdio.h>
-
-static void
-inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
-{
-  mesh_entry *entry;
-  grn_geo_point min, max;
-
-  entry = entries + n;
-  printf("mesh: %d:%d\n", n, entry->key_size);
-
-  printf("key: ");
-  grn_p_geo_point(ctx, &(entry->key));
-
-  compute_min_and_max(&(entry->key), entry->key_size, &min, &max);
-  printf("min: ");
-  grn_p_geo_point(ctx, &min);
-  printf("max: ");
-  grn_p_geo_point(ctx, &max);
-}
-
-static void
-inspect_tid(grn_ctx *ctx, grn_id tid, grn_geo_point *point,
-            double x, double y, double d)
-{
-  printf("tid: %d:%g (%g,%g)", tid, d, x, y);
-  grn_p_geo_point(ctx, point);
-}
-#else
-#  define inspect_mesh_entry(...)
-#  define inspect_tid(...)
-#endif
-
-typedef enum {
-  MESH_LEFT_TOP,
-  MESH_RIGHT_TOP,
-  MESH_RIGHT_BOTTOM,
-  MESH_LEFT_BOTTOM
-} mesh_position;
-
-static int
-grn_table_sort_geo_collect_points(grn_ctx *ctx, grn_obj *table, grn_obj *index,
-                                  grn_pat *pat,
-                                  geo_entry *entries, int n_entries,
-                                  int n, int accessorp,
-                                  grn_geo_point *base_point,
-                                  double d_far, int diff_bit)
-{
-  int n_meshes;
-  grn_geo_point geo_base, geo_min, geo_max;
-  mesh_entry meshes[19];
-  int lat_diff, lng_diff;
-  double d;
-  geo_entry *ep, *p;
-  mesh_position position;
-
-  compute_min_and_max(base_point, diff_bit, &geo_min, &geo_max);
-
-  lat_diff = geo_max.latitude - geo_min.latitude + 1;
-  lng_diff = geo_max.longitude - geo_min.longitude + 1;
-  if (base_point->latitude >= geo_min.latitude + lat_diff / 2) {
-    geo_base.latitude = geo_max.latitude + 1;
-    if (base_point->longitude >= geo_min.longitude + lng_diff / 2) {
-      geo_base.longitude = geo_max.longitude + 1;
-      position = MESH_LEFT_BOTTOM;
-    } else {
-      geo_base.longitude = geo_min.longitude;
-      position = MESH_RIGHT_BOTTOM;
-    }
-  } else {
-    geo_base.latitude = geo_min.latitude;
-    if (base_point->longitude >= geo_min.longitude + lng_diff / 2) {
-      geo_base.longitude = geo_max.longitude + 1;
-      position = MESH_LEFT_TOP;
-    } else {
-      geo_base.longitude = geo_min.longitude;
-      position = MESH_RIGHT_TOP;
-    }
-  }
-  /*
-    base_point: b
-    geo_min: i
-    geo_max: a
-    geo_base: x: must be at the left bottom in the top right mesh.
-
-    e.g.: base_point is at the left bottom mesh case:
-              +------+------+
-              |      |      |
-              |      |x     |
-             ^+------+------+
-             ||     a|      |
-    lng_diff || b    |      |
-            \/i------+------+
-              <------>
-              lat_diff
-
-    grn_min + lat_diff -> the right mesh.
-    grn_min + lng_diff -> the top mesh.
-   */
-#ifdef GEO_SORT_DEBUG
-  grn_p_geo_point(ctx, base_point);
-  printf("base: ");
-  grn_p_geo_point(ctx, &geo_base);
-  printf("min:  ");
-  grn_p_geo_point(ctx, geo_min);
-  printf("max:  ");
-  grn_p_geo_point(ctx, geo_max);
-  printf("diff: %d (%d, %d)\n", diff_bit, lat_diff, lng_diff);
-#endif
-
-  n_meshes = 0;
-
-#define add_mesh(lat_diff_,lng_diff_,key_size_)\
-  meshes[n_meshes].key.latitude = geo_base.latitude + (lat_diff_);\
-  meshes[n_meshes].key.longitude = geo_base.longitude + (lng_diff_);\
-  meshes[n_meshes].key_size = key_size_;\
-  n_meshes++;
-
-  if (position != MESH_LEFT_TOP) {
-    add_mesh(0, -lng_diff, diff_bit);
-  }
-  if (position != MESH_RIGHT_TOP) {
-    add_mesh(0, 0, diff_bit);
-  }
-  if (position != MESH_RIGHT_BOTTOM) {
-    add_mesh(-lat_diff, 0, diff_bit);
-  }
-  if (position != MESH_LEFT_BOTTOM) {
-    add_mesh(-lat_diff, -lng_diff, diff_bit);
-  }
-
-#define add_sub_mesh(lat_diff_cmp,lng_diff_cmp,lat_diff_base,lng_diff_base)\
-  meshes[n_meshes].key.latitude = geo_base.latitude + (lat_diff_cmp);\
-  meshes[n_meshes].key.longitude = geo_base.longitude + (lng_diff_cmp);\
-  d = grn_geo_distance_raw(ctx, base_point, &(meshes[n_meshes].key));\
-  if (d < d_far) {\
-    add_mesh(lat_diff_base, lng_diff_base, diff_bit + 2);\
-  }
-
-  /*
-    b: base_point
-    1-16: sub meshes. 1-16 are added order.
-
-        +---+---+---+---+
-        | 1 | 2 | 3 | 4 |
-    +---+---+---+---+---+---+
-    |16 |       |       | 5 |
-    +---+       |       +---+
-    |15 |       |b      | 6 |
-    +---+-------+-------+---+
-    |14 |       |       | 7 |
-    +---+  base meshes  +---+
-    |13 |       |       | 8 |
-    +---+---+---+---+---+---+
-        |12 |11 |10 | 9 |
-        +---+---+---+---+
-   */
-  add_sub_mesh(lat_diff, -(lng_diff + 1) / 2 - 1,
-               lat_diff, -lng_diff);
-  add_sub_mesh(lat_diff, -1,
-               lat_diff, -(lng_diff + 1) / 2);
-  add_sub_mesh(lat_diff, 0,
-               lat_diff, 0);
-  add_sub_mesh(lat_diff, (lng_diff + 1) / 2,
-               lat_diff, (lng_diff + 1) / 2);
-
-  add_sub_mesh((lat_diff + 1) / 2, lng_diff,
-               (lat_diff + 1) / 2, lng_diff);
-  add_sub_mesh(0, lng_diff,
-               0, lng_diff);
-  add_sub_mesh(-1, lng_diff,
-               -(lat_diff + 1) / 2, lng_diff);
-  add_sub_mesh(-(lat_diff + 1) / 2 - 1, lng_diff,
-               -lat_diff, lng_diff);
-
-  add_sub_mesh(-lat_diff - 1, (lng_diff + 1) / 2,
-               -lat_diff * 2, (lng_diff + 1) / 2);
-  add_sub_mesh(-lat_diff - 1, 0,
-               -lat_diff * 2, 0);
-  add_sub_mesh(-lat_diff - 1, -1,
-               -lat_diff * 2, -(lng_diff + 1) / 2);
-  add_sub_mesh(-lat_diff - 1, -(lng_diff + 1) / 2 - 1,
-               -lat_diff * 2, -lng_diff);
-
-  add_sub_mesh(-(lat_diff + 1) / 2 - 1, -lng_diff - 1,
-               -lat_diff, -lng_diff * 2);
-  add_sub_mesh(-1, -lng_diff - 1,
-               -(lat_diff + 1) / 2, -lng_diff * 2);
-  add_sub_mesh(0, -lng_diff - 1,
-               0, -lng_diff * 2);
-  add_sub_mesh((lat_diff + 1) / 2, -lng_diff - 1,
-               (lat_diff + 1) / 2, -lng_diff * 2);
-
-#undef add_sub_mesh
-#undef add_mesh
-
-  ep = entries + n_entries;
-  while (n_meshes--) {
-    grn_id tid;
-    grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat,
-                                             &(meshes[n_meshes].key),
-                                             meshes[n_meshes].key_size,
-                                             NULL, 0,
-                                             0, -1,
-                                             GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
-    inspect_mesh_entry(ctx, meshes, n_meshes);
-    if (pc) {
-      while ((tid = grn_pat_cursor_next(ctx, pc))) {
-        grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
-        if (ic) {
-          grn_geo_point pos;
-          grn_ii_posting *posting;
-          grn_pat_get_key(ctx, pat, tid, &pos, sizeof(grn_geo_point));
-          d = grn_geo_distance_raw(ctx, base_point, &pos);
-          inspect_tid(ctx, tid, &pos, x, y, d);
-          while ((posting = grn_ii_cursor_next(ctx, ic))) {
-            grn_id rid = accessorp
-              ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
-              : posting->rid;
-            if (rid) {
-              for (p = ep; entries < p && p[-1].d > d; p--) {
-                p->id = p[-1].id;
-                p->d = p[-1].d;
-              }
-              p->id = rid;
-              p->d = d;
-              if (n_entries < n) {
-                ep++;
-                n_entries++;
-              }
-            }
-          }
-          grn_ii_cursor_close(ctx, ic);
-        }
-      }
-      grn_pat_cursor_close(ctx, pc);
-    }
-  }
-  return n_entries;
-}
-
-static int
-grn_table_sort_geo(grn_ctx *ctx, grn_obj *table, int offset, int limit,
-                   grn_obj *result, grn_table_sort_key *keys, int n_keys)
-{
-  grn_obj *index;
-  int i = 0, e = offset + limit, sid, accessorp = GRN_ACCESSORP(keys->key);
-  if (n_keys == 2 && grn_column_index(ctx, keys->key, GRN_OP_LESS, &index, 1, &sid)) {
-    grn_id tid;
-    grn_obj *arg = keys[1].key;
-    grn_pat *pat = (grn_pat *)grn_ctx_at(ctx, index->header.domain);
-    grn_id domain = pat->obj.header.domain;
-    grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat, NULL, 0,
-                                             GRN_BULK_HEAD(arg), GRN_BULK_VSIZE(arg),
-                                             0, -1, GRN_CURSOR_PREFIX);
-    if (pc) {
-      if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
-        while (i < e && (tid = grn_pat_cursor_next(ctx, pc))) {
-          grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
-          if (ic) {
-            grn_ii_posting *posting;
-            while (i < e && (posting = grn_ii_cursor_next(ctx, ic))) {
-              if (offset <= i) {
-                grn_id *v;
-                if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
-                *v = posting->rid;
-              }
-              i++;
-            }
-            grn_ii_cursor_close(ctx, ic);
-          }
-        }
-        grn_pat_cursor_close(ctx, pc);
-      } else {
-        geo_entry *entries;
-
-        if ((entries = GRN_MALLOC(sizeof(geo_entry) * (e + 1)))) {
-          int n, diff_bit;
-          double d_far;
-          grn_id *v;
-          grn_geo_point *base_point;
-          geo_entry *ep;
-
-          base_point = (grn_geo_point *)GRN_BULK_HEAD(arg);
-          n = grn_table_sort_geo_detect_far_point(ctx, table, index, pat,
-                                                  entries, pc, e, accessorp,
-                                                  base_point,
-                                                  &d_far, &diff_bit);
-          grn_pat_cursor_close(ctx, pc);
-          if (diff_bit > 0) {
-            n += grn_table_sort_geo_collect_points(ctx, table, index, pat,
-                                                   entries, n, e, accessorp,
-                                                   base_point, d_far, diff_bit);
-          }
-          for (i = 0, ep = entries + offset; i < limit && ep < entries + n; i++, ep++) {
-            if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
-            *v = ep->id;
-          }
-          GRN_FREE(entries);
-        }
-      }
-    }
-  }
-  return i;
-}
-
 int
 grn_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
                grn_obj *result, grn_table_sort_key *keys, int n_keys)
@@ -6986,7 +6535,7 @@ grn_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
     e = offset + limit;
   }
   if (keys->flags & GRN_TABLE_SORT_GEO) {
-    i = grn_table_sort_geo(ctx, table, offset, limit, result, keys, n_keys);
+    i = grn_geo_table_sort(ctx, table, offset, limit, result, keys, n_keys);
     goto exit;
   }
   if (n_keys == 1 && grn_column_index(ctx, keys->key, GRN_OP_LESS, &index, 1, NULL)) {

  Modified: lib/geo.c (+452 -0)
===================================================================
--- lib/geo.c    2010-08-09 01:43:43 +0000 (f1a0d8b)
+++ lib/geo.c    2010-08-09 05:21:17 +0000 (d93ef20)
@@ -20,6 +20,458 @@
 #include "geo.h"
 #include "ii.h"
 #include "db.h"
+#include "pat.h"
+
+#define GEO_RESOLUTION   3600000
+#define GEO_RADIOUS      6357303
+#define GEO_BES_C1       6334834
+#define GEO_BES_C2       6377397
+#define GEO_BES_C3       0.006674
+#define GEO_GRS_C1       6335439
+#define GEO_GRS_C2       6378137
+#define GEO_GRS_C3       0.006694
+#define GEO_INT2RAD(x)   ((M_PI / (GEO_RESOLUTION * 180)) * (x))
+
+typedef struct {
+  grn_id id;
+  double d;
+} geo_entry;
+
+static int
+compute_diff_bit(uint8_t *geo_key1, uint8_t *geo_key2)
+{
+  int i, diff_bit = 0;
+
+  for (i = 0; i < sizeof(grn_geo_point); i++) {
+    if (geo_key1[i] == geo_key2[i]) {
+      continue;
+    }
+
+    if ((geo_key1[i] & 0xc0) != (geo_key2[i] & 0xc0)) {
+      diff_bit = 0;
+      break;
+    } else if ((geo_key1[i] & 0x30) != (geo_key2[i] & 0x30)) {
+      diff_bit = 2;
+      break;
+    } else if ((geo_key1[i] & 0x0c) != (geo_key2[i] & 0x0c)) {
+      diff_bit = 4;
+      break;
+    } else if ((geo_key1[i] & 0x03) != (geo_key2[i] & 0x03)) {
+      diff_bit = 6;
+      break;
+    }
+  }
+
+  return i * 8 + diff_bit;
+}
+
+static void
+compute_min_and_max(grn_geo_point *base_point, int diff_bit,
+                    grn_geo_point *geo_min, grn_geo_point *geo_max)
+{
+  int diff_byte, diff_bit_mask;
+  uint8_t geo_key_base[sizeof(grn_geo_point)];
+  uint8_t geo_key_min[sizeof(grn_geo_point)];
+  uint8_t geo_key_max[sizeof(grn_geo_point)];
+
+  diff_byte = diff_bit / 8;
+  diff_bit_mask = 0xff >> (diff_bit % 8);
+  grn_gton(geo_key_base, base_point, sizeof(grn_geo_point));
+
+  memcpy(geo_key_min, geo_key_base, diff_byte + 1);
+  geo_key_min[diff_byte] &= ~diff_bit_mask;
+  memset(geo_key_min + diff_byte + 1, 0,
+         sizeof(grn_geo_point) - diff_byte - 1);
+
+  memcpy(geo_key_max, geo_key_base, diff_byte + 1);
+  geo_key_max[diff_byte] |= diff_bit_mask;
+  memset(geo_key_max + diff_byte + 1, 0xff,
+         sizeof(grn_geo_point) - diff_byte - 1);
+
+  grn_ntog((uint8_t *)geo_min, geo_key_min, sizeof(grn_geo_point));
+  grn_ntog((uint8_t *)geo_max, geo_key_max, sizeof(grn_geo_point));
+}
+
+static int
+grn_table_sort_geo_detect_far_point(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+                                    grn_pat *pat, geo_entry *entries,
+                                    grn_pat_cursor *pc, int n, int accessorp,
+                                    grn_geo_point *base_point,
+                                    double *d_far, int *diff_bit)
+{
+  int i = 0, diff_bit_prev, diff_bit_current;
+  grn_id tid;
+  geo_entry *ep, *p;
+  double d;
+  uint8_t geo_key_prev[sizeof(grn_geo_point)];
+  uint8_t geo_key_curr[sizeof(grn_geo_point)];
+  grn_geo_point point;
+
+  *d_far = 0.0;
+  grn_gton(geo_key_curr, base_point, sizeof(grn_geo_point));
+  *diff_bit = sizeof(grn_geo_point) * 8;
+  diff_bit_current = sizeof(grn_geo_point) * 8;
+  memcpy(&point, base_point, sizeof(grn_geo_point));
+  ep = entries;
+  while ((tid = grn_pat_cursor_next(ctx, pc))) {
+    grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+    if (ic) {
+      grn_ii_posting *posting;
+      grn_gton(geo_key_prev, &point, sizeof(grn_geo_point));
+      grn_pat_get_key(ctx, pat, tid, &point, sizeof(grn_geo_point));
+      grn_gton(geo_key_curr, &point, sizeof(grn_geo_point));
+      d = grn_geo_distance_raw(ctx, base_point, &point);
+
+      diff_bit_prev = diff_bit_current;
+      diff_bit_current = compute_diff_bit(geo_key_curr, geo_key_prev);
+      if (diff_bit_current < diff_bit_prev && *diff_bit > diff_bit_current) {
+        if (i == n) {
+          break;
+        }
+        *diff_bit = diff_bit_current;
+      }
+
+      if (d > *d_far) {
+        *d_far = d;
+      }
+      while ((posting = grn_ii_cursor_next(ctx, ic))) {
+        grn_id rid = accessorp
+          ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
+          : posting->rid;
+        if (rid) {
+          for (p = ep; entries < p && p[-1].d > d; p--) {
+            p->id = p[-1].id;
+            p->d = p[-1].d;
+          }
+          p->id = rid;
+          p->d = d;
+          if (i < n) {
+            ep++;
+            i++;
+          }
+        }
+      }
+      grn_ii_cursor_close(ctx, ic);
+    }
+  }
+
+  return i;
+}
+
+typedef struct
+{
+  grn_geo_point key;
+  int key_size;
+} mesh_entry;
+
+/* #define GEO_SORT_DEBUG */
+
+#ifdef GEO_SORT_DEBUG
+#include <stdio.h>
+
+static void
+inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
+{
+  mesh_entry *entry;
+  grn_geo_point min, max;
+
+  entry = entries + n;
+  printf("mesh: %d:%d\n", n, entry->key_size);
+
+  printf("key: ");
+  grn_p_geo_point(ctx, &(entry->key));
+
+  compute_min_and_max(&(entry->key), entry->key_size, &min, &max);
+  printf("min: ");
+  grn_p_geo_point(ctx, &min);
+  printf("max: ");
+  grn_p_geo_point(ctx, &max);
+}
+
+static void
+inspect_tid(grn_ctx *ctx, grn_id tid, grn_geo_point *point,
+            double x, double y, double d)
+{
+  printf("tid: %d:%g (%g,%g)", tid, d, x, y);
+  grn_p_geo_point(ctx, point);
+}
+#else
+#  define inspect_mesh_entry(...)
+#  define inspect_tid(...)
+#endif
+
+typedef enum {
+  MESH_LEFT_TOP,
+  MESH_RIGHT_TOP,
+  MESH_RIGHT_BOTTOM,
+  MESH_LEFT_BOTTOM
+} mesh_position;
+
+static int
+grn_table_sort_geo_collect_points(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+                                  grn_pat *pat,
+                                  geo_entry *entries, int n_entries,
+                                  int n, int accessorp,
+                                  grn_geo_point *base_point,
+                                  double d_far, int diff_bit)
+{
+  int n_meshes;
+  grn_geo_point geo_base, geo_min, geo_max;
+  mesh_entry meshes[19];
+  int lat_diff, lng_diff;
+  double d;
+  geo_entry *ep, *p;
+  mesh_position position;
+
+  compute_min_and_max(base_point, diff_bit, &geo_min, &geo_max);
+
+  lat_diff = geo_max.latitude - geo_min.latitude + 1;
+  lng_diff = geo_max.longitude - geo_min.longitude + 1;
+  if (base_point->latitude >= geo_min.latitude + lat_diff / 2) {
+    geo_base.latitude = geo_max.latitude + 1;
+    if (base_point->longitude >= geo_min.longitude + lng_diff / 2) {
+      geo_base.longitude = geo_max.longitude + 1;
+      position = MESH_LEFT_BOTTOM;
+    } else {
+      geo_base.longitude = geo_min.longitude;
+      position = MESH_RIGHT_BOTTOM;
+    }
+  } else {
+    geo_base.latitude = geo_min.latitude;
+    if (base_point->longitude >= geo_min.longitude + lng_diff / 2) {
+      geo_base.longitude = geo_max.longitude + 1;
+      position = MESH_LEFT_TOP;
+    } else {
+      geo_base.longitude = geo_min.longitude;
+      position = MESH_RIGHT_TOP;
+    }
+  }
+  /*
+    base_point: b
+    geo_min: i
+    geo_max: a
+    geo_base: x: must be at the left bottom in the top right mesh.
+
+    e.g.: base_point is at the left bottom mesh case:
+              +------+------+
+              |      |      |
+              |      |x     |
+             ^+------+------+
+             ||     a|      |
+    lng_diff || b    |      |
+            \/i------+------+
+              <------>
+              lat_diff
+
+    grn_min + lat_diff -> the right mesh.
+    grn_min + lng_diff -> the top mesh.
+   */
+#ifdef GEO_SORT_DEBUG
+  grn_p_geo_point(ctx, base_point);
+  printf("base: ");
+  grn_p_geo_point(ctx, &geo_base);
+  printf("min:  ");
+  grn_p_geo_point(ctx, geo_min);
+  printf("max:  ");
+  grn_p_geo_point(ctx, geo_max);
+  printf("diff: %d (%d, %d)\n", diff_bit, lat_diff, lng_diff);
+#endif
+
+  n_meshes = 0;
+
+#define add_mesh(lat_diff_,lng_diff_,key_size_)\
+  meshes[n_meshes].key.latitude = geo_base.latitude + (lat_diff_);\
+  meshes[n_meshes].key.longitude = geo_base.longitude + (lng_diff_);\
+  meshes[n_meshes].key_size = key_size_;\
+  n_meshes++;
+
+  if (position != MESH_LEFT_TOP) {
+    add_mesh(0, -lng_diff, diff_bit);
+  }
+  if (position != MESH_RIGHT_TOP) {
+    add_mesh(0, 0, diff_bit);
+  }
+  if (position != MESH_RIGHT_BOTTOM) {
+    add_mesh(-lat_diff, 0, diff_bit);
+  }
+  if (position != MESH_LEFT_BOTTOM) {
+    add_mesh(-lat_diff, -lng_diff, diff_bit);
+  }
+
+#define add_sub_mesh(lat_diff_cmp,lng_diff_cmp,lat_diff_base,lng_diff_base)\
+  meshes[n_meshes].key.latitude = geo_base.latitude + (lat_diff_cmp);\
+  meshes[n_meshes].key.longitude = geo_base.longitude + (lng_diff_cmp);\
+  d = grn_geo_distance_raw(ctx, base_point, &(meshes[n_meshes].key));\
+  if (d < d_far) {\
+    add_mesh(lat_diff_base, lng_diff_base, diff_bit + 2);\
+  }
+
+  /*
+    b: base_point
+    1-16: sub meshes. 1-16 are added order.
+
+        +---+---+---+---+
+        | 1 | 2 | 3 | 4 |
+    +---+---+---+---+---+---+
+    |16 |       |       | 5 |
+    +---+       |       +---+
+    |15 |       |b      | 6 |
+    +---+-------+-------+---+
+    |14 |       |       | 7 |
+    +---+  base meshes  +---+
+    |13 |       |       | 8 |
+    +---+---+---+---+---+---+
+        |12 |11 |10 | 9 |
+        +---+---+---+---+
+   */
+  add_sub_mesh(lat_diff, -(lng_diff + 1) / 2 - 1,
+               lat_diff, -lng_diff);
+  add_sub_mesh(lat_diff, -1,
+               lat_diff, -(lng_diff + 1) / 2);
+  add_sub_mesh(lat_diff, 0,
+               lat_diff, 0);
+  add_sub_mesh(lat_diff, (lng_diff + 1) / 2,
+               lat_diff, (lng_diff + 1) / 2);
+
+  add_sub_mesh((lat_diff + 1) / 2, lng_diff,
+               (lat_diff + 1) / 2, lng_diff);
+  add_sub_mesh(0, lng_diff,
+               0, lng_diff);
+  add_sub_mesh(-1, lng_diff,
+               -(lat_diff + 1) / 2, lng_diff);
+  add_sub_mesh(-(lat_diff + 1) / 2 - 1, lng_diff,
+               -lat_diff, lng_diff);
+
+  add_sub_mesh(-lat_diff - 1, (lng_diff + 1) / 2,
+               -lat_diff * 2, (lng_diff + 1) / 2);
+  add_sub_mesh(-lat_diff - 1, 0,
+               -lat_diff * 2, 0);
+  add_sub_mesh(-lat_diff - 1, -1,
+               -lat_diff * 2, -(lng_diff + 1) / 2);
+  add_sub_mesh(-lat_diff - 1, -(lng_diff + 1) / 2 - 1,
+               -lat_diff * 2, -lng_diff);
+
+  add_sub_mesh(-(lat_diff + 1) / 2 - 1, -lng_diff - 1,
+               -lat_diff, -lng_diff * 2);
+  add_sub_mesh(-1, -lng_diff - 1,
+               -(lat_diff + 1) / 2, -lng_diff * 2);
+  add_sub_mesh(0, -lng_diff - 1,
+               0, -lng_diff * 2);
+  add_sub_mesh((lat_diff + 1) / 2, -lng_diff - 1,
+               (lat_diff + 1) / 2, -lng_diff * 2);
+
+#undef add_sub_mesh
+#undef add_mesh
+
+  ep = entries + n_entries;
+  while (n_meshes--) {
+    grn_id tid;
+    grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat,
+                                             &(meshes[n_meshes].key),
+                                             meshes[n_meshes].key_size,
+                                             NULL, 0,
+                                             0, -1,
+                                             GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
+    inspect_mesh_entry(ctx, meshes, n_meshes);
+    if (pc) {
+      while ((tid = grn_pat_cursor_next(ctx, pc))) {
+        grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+        if (ic) {
+          grn_geo_point pos;
+          grn_ii_posting *posting;
+          grn_pat_get_key(ctx, pat, tid, &pos, sizeof(grn_geo_point));
+          d = grn_geo_distance_raw(ctx, base_point, &pos);
+          inspect_tid(ctx, tid, &pos, x, y, d);
+          while ((posting = grn_ii_cursor_next(ctx, ic))) {
+            grn_id rid = accessorp
+              ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
+              : posting->rid;
+            if (rid) {
+              for (p = ep; entries < p && p[-1].d > d; p--) {
+                p->id = p[-1].id;
+                p->d = p[-1].d;
+              }
+              p->id = rid;
+              p->d = d;
+              if (n_entries < n) {
+                ep++;
+                n_entries++;
+              }
+            }
+          }
+          grn_ii_cursor_close(ctx, ic);
+        }
+      }
+      grn_pat_cursor_close(ctx, pc);
+    }
+  }
+  return n_entries;
+}
+
+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_obj *index;
+  int i = 0, e = offset + limit, sid, accessorp = GRN_ACCESSORP(keys->key);
+  if (n_keys == 2 && grn_column_index(ctx, keys->key, GRN_OP_LESS, &index, 1, &sid)) {
+    grn_id tid;
+    grn_obj *arg = keys[1].key;
+    grn_pat *pat = (grn_pat *)grn_ctx_at(ctx, index->header.domain);
+    grn_id domain = pat->obj.header.domain;
+    grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat, NULL, 0,
+                                             GRN_BULK_HEAD(arg), GRN_BULK_VSIZE(arg),
+                                             0, -1, GRN_CURSOR_PREFIX);
+    if (pc) {
+      if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
+        while (i < e && (tid = grn_pat_cursor_next(ctx, pc))) {
+          grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+          if (ic) {
+            grn_ii_posting *posting;
+            while (i < e && (posting = grn_ii_cursor_next(ctx, ic))) {
+              if (offset <= i) {
+                grn_id *v;
+                if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+                *v = posting->rid;
+              }
+              i++;
+            }
+            grn_ii_cursor_close(ctx, ic);
+          }
+        }
+        grn_pat_cursor_close(ctx, pc);
+      } else {
+        geo_entry *entries;
+
+        if ((entries = GRN_MALLOC(sizeof(geo_entry) * (e + 1)))) {
+          int n, diff_bit;
+          double d_far;
+          grn_id *v;
+          grn_geo_point *base_point;
+          geo_entry *ep;
+
+          base_point = (grn_geo_point *)GRN_BULK_HEAD(arg);
+          n = grn_table_sort_geo_detect_far_point(ctx, table, index, pat,
+                                                  entries, pc, e, accessorp,
+                                                  base_point,
+                                                  &d_far, &diff_bit);
+          grn_pat_cursor_close(ctx, pc);
+          if (diff_bit > 0) {
+            n += grn_table_sort_geo_collect_points(ctx, table, index, pat,
+                                                   entries, n, e, accessorp,
+                                                   base_point, d_far, diff_bit);
+          }
+          for (i = 0, ep = entries + offset; i < limit && ep < entries + n; i++, ep++) {
+            if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+            *v = ep->id;
+          }
+          GRN_FREE(entries);
+        }
+      }
+    }
+  }
+  return i;
+}
 
 grn_rc
 grn_geo_search(grn_ctx *ctx, grn_obj *obj, grn_obj **args, int nargs,

  Modified: lib/geo.h (+2 -0)
===================================================================
--- lib/geo.h    2010-08-09 01:43:43 +0000 (6db6309)
+++ lib/geo.h    2010-08-09 05:21:17 +0000 (d3654dc)
@@ -46,6 +46,8 @@ extern "C" {
 
 grn_rc grn_geo_search(grn_ctx *ctx, grn_obj *obj, grn_obj **args, int nargs,
                       grn_obj *res, grn_operator op);
+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);
 
 unsigned grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
                            grn_obj *radius_or_point);




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