The Tiny HTM Library
src/tree_count.c
Go to the documentation of this file.
00001 
00007 #include <errno.h>
00008 #include <stdarg.h>
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <string.h>
00012 #include <getopt.h>
00013 
00014 #include "tinyhtm/geometry.h"
00015 #include "tinyhtm/tree.h"
00016 
00017 #ifdef __cplusplus
00018 extern "C" {
00019 #endif
00020 
00023 /* Should output be in JSON format or in IPAC SVC format? */
00024 static int json = 0;
00025 /* Should the count be estimated, or determined exactly? */
00026 static int estimate = 0;
00027 
00028 /* Performs string escaping for the JSON/IPAC SVC formats. */
00029 static const char * esc(const char *s) {
00030     static char buf[8192];
00031     char *e = buf;
00032     if (s == NULL) {
00033         return "null";
00034     } else {
00035         *e++ = '"';
00036         for (; *s != '\0' && e < buf + (sizeof(buf) - 2); ++s) {
00037             switch (*s) {
00038                 case  '"': e[0] = '\\'; e[1] =  '"'; e += 2; break;
00039                 case '\\': e[0] = '\\'; e[1] = '\\'; e += 2; break;
00040                 case '\b': e[0] = '\\'; e[1] =  'b'; e += 2; break;
00041                 case '\f': e[0] = '\\'; e[1] =  'f'; e += 2; break;
00042                 case '\n': e[0] = '\\'; e[1] =  'n'; e += 2; break;
00043                 case '\r': e[0] = '\\'; e[1] =  'r'; e += 2; break;
00044                 case '\t': e[0] = '\\'; e[1] =  't'; e += 2; break;
00045                 default:
00046                     if (*s > 0x1f && *s < 0x7f) {
00047                         *e++ = *s;
00048                     }
00049                     break;
00050             }
00051         }
00052         if (*s != '\0' && e == buf + (sizeof(buf) - 2)) {
00053             strcpy(buf + (sizeof(buf) - 6), " ...\"");
00054         } else {
00055             e[0] = '"';
00056             e[1] = '\0';
00057         }
00058     }
00059     return buf;
00060 }
00061 
00062 
00063 static void err(const char *fmt, ...)
00064 {
00065     char buf[4095];
00066     int len;
00067 
00068     va_list ap;
00069     va_start(ap, fmt);
00070     len = vsnprintf(buf, sizeof(buf), fmt, ap);
00071     va_end(ap);
00072     if (len >= (int) sizeof(buf)) {
00073         strcpy(buf + (sizeof(buf) - 5), " ...");
00074     }
00075     if (json) {
00076         printf("{\"stat\":\"ERROR\", \"msg\":%s}\n", esc(buf));
00077     } else {
00078         printf("[struct stat=\"ERROR\", msg=%s]\n", esc(buf));
00079     }
00080     fflush(stdout);
00081     exit(EXIT_FAILURE);
00082 }
00083 
00084 
00085 static double get_double(const char *s)
00086 {
00087     char *endptr;
00088     double d = strtod(s, &endptr);
00089     if (errno != 0 || endptr == s) {
00090         err("failed to convert argument `%s' to a double", s);
00091     }
00092     return d;
00093 }
00094 
00095 
00096 static void print_count(int64_t count)
00097 {
00098     if (json) {
00099         printf("{\"stat\":\"OK\", \"count\":%lld}\n", (long long) count);
00100     } else {
00101         printf("[struct stat=\"OK\", count=\"%lld\"]\n", (long long) count);
00102     }
00103 }
00104 
00105 static void print_range(const struct htm_range *range)
00106 {
00107     if (json) {
00108         printf("{\"stat\":\"OK\", \"min\":%lld, \"max\":%lld}\n",
00109                (long long) range->min, (long long) range->max);
00110     } else {
00111         printf("[struct stat=\"OK\", min=\"%lld\", max=\"%lld\"]\n",
00112                (long long) range->min, (long long) range->max);
00113     }
00114 }
00115 
00116 static void circle_count(const char * const treefile,
00117                          const char * const datafile,
00118                          char **argv)
00119 {
00120     struct htm_tree tree;
00121     struct htm_sc sc;
00122     struct htm_v3 cen;
00123     double r;
00124     enum htm_errcode ec;
00125 
00126     ec = htm_sc_init(&sc, get_double(argv[0]), get_double(argv[1]));
00127     if (ec != HTM_OK) {
00128         err("Invalid circle center coordinates: %s", htm_errmsg(ec));
00129     }
00130     ec = htm_sc_tov3(&cen, &sc);
00131     if (ec != HTM_OK) {
00132         err("Failed to convert spherical coordinates to a unit vector: %s",
00133             htm_errmsg(ec));
00134     }
00135     r = get_double(argv[2]);
00136     ec = htm_tree_init(&tree, treefile, datafile);
00137     if (ec != HTM_OK) {
00138         err("Failed to load tree and/or data file: %s", htm_errmsg(ec));
00139     }
00140     if (estimate != 0) {
00141         struct htm_range range = htm_tree_s2circle_range(&tree, &cen, r, &ec);
00142         htm_tree_destroy(&tree);
00143         if (ec != HTM_OK) {
00144             err("Failed to estimate points in circle: %s", htm_errmsg(ec));
00145         }
00146         print_range(&range);
00147     } else {
00148         int64_t count = htm_tree_s2circle_count(&tree, &cen, r, &ec);
00149         htm_tree_destroy(&tree);
00150         if (ec != HTM_OK) {
00151             err("Failed to count points in circle: %s", htm_errmsg(ec));
00152         }
00153         print_count(count);
00154     }
00155 }
00156 
00157 
00158 static void ellipse_count(const char * const treefile,
00159                           const char * const datafile,
00160                           char **argv)
00161 {
00162     struct htm_tree tree;
00163     struct htm_s2ellipse ellipse;
00164     struct htm_sc sc;
00165     struct htm_v3 cen;
00166     double a, b, angle;
00167     enum htm_errcode ec;
00168 
00169     ec = htm_sc_init(&sc, get_double(argv[0]), get_double(argv[1]));
00170     if (ec != HTM_OK) {
00171         err("Invalid ellipse center coordinates: %s", htm_errmsg(ec));
00172     }
00173     ec = htm_sc_tov3(&cen, &sc);
00174     if (ec != HTM_OK) {
00175         err("Failed to convert spherical coordinates to a unit vector: %s",
00176             htm_errmsg(ec));
00177     }
00178     a = get_double(argv[2]);
00179     b = get_double(argv[3]);
00180     angle = get_double(argv[4]);
00181     ec = htm_s2ellipse_init2(&ellipse, &cen, a, b, angle);
00182     if (ec != HTM_OK) {
00183         err("Invalid ellipse parameters: %s", htm_errmsg(ec));
00184     }
00185     ec = htm_tree_init(&tree, treefile, datafile);
00186     if (ec != HTM_OK) {
00187         err("Failed to load tree and/or data file: %s", htm_errmsg(ec));
00188     }
00189     if (estimate != 0) {
00190         struct htm_range range = htm_tree_s2ellipse_range(&tree, &ellipse, &ec);
00191         htm_tree_destroy(&tree);
00192         if (ec != HTM_OK) {
00193             err("Failed to estimate points in ellipse: %s", htm_errmsg(ec));
00194         }
00195         print_range(&range);
00196     } else {
00197         int64_t count = htm_tree_s2ellipse_count(&tree, &ellipse, &ec); 
00198         htm_tree_destroy(&tree);
00199         if (ec != HTM_OK) {
00200             err("Failed to count points in ellipse: %s", htm_errmsg(ec));
00201         }
00202         print_count(count);
00203     }
00204 }
00205 
00206 
00207 static void hull_count(const char * const treefile,
00208                        const char * const datafile,
00209                        const int argc,
00210                        char **argv)
00211 {
00212     struct htm_tree tree;
00213     struct htm_s2cpoly *poly;
00214     struct htm_sc sc;
00215     struct htm_v3 *verts;
00216     int i;
00217     enum htm_errcode ec = HTM_OK;
00218 
00219     verts = (struct htm_v3 *) malloc(sizeof(struct htm_v3) * (size_t) argc/2);
00220     if (verts == NULL) {
00221         err(htm_errmsg(HTM_ENOMEM));
00222     }
00223     for (i = 0; i < argc/2; ++i) {
00224         ec = htm_sc_init(&sc, get_double(argv[2*i]), get_double(argv[2*i + 1]));
00225         if (ec != HTM_OK) {
00226             free(verts);
00227             err("Invalid vertex coordinates: %s", htm_errmsg(ec));
00228         }
00229         ec = htm_sc_tov3(&verts[i], &sc);
00230         if (ec != HTM_OK) {
00231             free(verts);
00232             err("Failed to convert spherical coordinates to a unit vector: %s",
00233                 htm_errmsg(ec));
00234         }
00235     }
00236     poly = htm_s2cpoly_hull(verts, (size_t) i, &ec);
00237     if (poly == NULL || ec != HTM_OK) {
00238         free(verts);
00239         err("Failed to compute convex hull: %s", htm_errmsg(ec));
00240     }
00241     free(verts);
00242     ec = htm_tree_init(&tree, treefile, datafile);
00243     if (ec != HTM_OK) {
00244         free(poly);
00245         err("Failed to load tree and/or data file: %s", htm_errmsg(ec));
00246     }
00247     if (estimate != 0) {
00248         struct htm_range range = htm_tree_s2cpoly_range(&tree, poly, &ec);
00249         htm_tree_destroy(&tree);
00250         free(poly);
00251         if (ec != HTM_OK) {
00252             err("Failed to estimate points in hull: %s", htm_errmsg(ec));
00253         }
00254         print_range(&range);
00255     } else {
00256         int64_t count = htm_tree_s2cpoly_count(&tree, poly, &ec);
00257         htm_tree_destroy(&tree);
00258         free(poly);
00259         if (ec != HTM_OK) {
00260             err("Failed to count points in hull: %s", htm_errmsg(ec));
00261         }
00262         print_count(count);
00263     }
00264 }
00265 
00266 
00267 static void test_tree(const char * const treefile,
00268                       const char * const datafile,
00269                       char **argv)
00270 {
00271     struct htm_tree tree;
00272     double r;
00273     unsigned long long i;
00274     enum htm_errcode ec;
00275 
00276     r = get_double(argv[0]);
00277     ec = htm_tree_init(&tree, treefile, datafile);
00278     if (ec != HTM_OK) {
00279         err("Failed to load tree and/or data file: %s", htm_errmsg(ec));
00280     }
00281     for (i = 0; i < tree.count; ++i) {
00282         int64_t c = htm_tree_s2circle_count(&tree, &tree.entries[i].v, r, &ec);
00283         if (c != 1) {
00284             err("Circle of radius %g around %llu:(%.18g, %.18g, %.18g) "
00285                 "contains %lld points (expecting 1).", r, i,
00286                 tree.entries[i].v.x, tree.entries[i].v.y, tree.entries[i].v.z,
00287                 (long long) c);
00288         }
00289     }
00290     htm_tree_destroy(&tree);
00291 }
00292 
00293 
00294 static void usage(const char *prog)
00295 {
00296     printf("%1$s [options] <file> circle <ra> <dec> <radius>\n"
00297            "\n"
00298            "%1$s [options] <file> ellipse <ra> <dec> <axis1> <axis2> <angle>\n"
00299            "\n"
00300            "%1$s [options] <file> hull <ra_1> <dec_1> <ra_2> <dec_2> ...<ra_n> <dec_n>\n"
00301            "\n"
00302            "%1$s [options] <file> test <radius>\n"
00303            "\n"
00304            "    This utility counts the number of points from <file> that\n"
00305            "lie in the given spherical region. Note one particularly important\n"
00306            "option: --tree (-t). This option specifies a tree index file that\n"
00307            "can be used to greatly speed up point counting. Three different\n"
00308            "types of spherical region are supported:\n"
00309            "\n"
00310            " Circle\n"
00311            "    A circle is specified by the spherical coordinates of its\n"
00312            "    center and its radius. All parameters are assumed to be in\n"
00313            "    degrees.\n"
00314            "\n"
00315            " Ellipse\n"
00316            "    An ellipse is specified by the spherical coordinates of its\n"
00317            "    center, two axis angles, and a position angle giving the\n"
00318            "    the orientation (east of north) of the first axis. All\n"
00319            "    parameters are assumed to be in degrees.\n"
00320            "\n"
00321            " Convex Hull\n"
00322            "    A convex hull is given by a hemispherical set of at least\n"
00323            "    3 points. Points are specified by their spherical coordinates;\n"
00324            "    coordinate values are assumed to be in degrees.\n"
00325            "\n"
00326            "    Finally, the utility supports a tree file integrity test.\n"
00327            "This mode constructs circles of the given radius around every\n"
00328            "point in <file>, and checks that the point count for that circle\n"
00329            "is exactly one. Note that this is useful only when points are\n"
00330            "unique and one knows their minimum separation.\n"
00331            "\n"
00332            "== Options ====\n"
00333            "\n"
00334            "--help     | -h              :  Prints usage information.\n"
00335            "--estimate | -e              :  Estimate count instead of determining\n"
00336            "                                the exact value. Has no effect unless\n"
00337            "                                used in conjunction with --tree.\n"
00338            "--json     | -j              :  Print results in JSON format.\n"
00339            "--tree     | -t <tree_file>  :  File name of tree index over\n"
00340            "                                the input points.\n",
00341            prog);
00342 }
00343 
00344 
00345 int main(int argc, char **argv)
00346 {
00347     char *treefile = NULL;
00348     char *datafile = NULL;
00349 
00350     opterr = 0;
00351     while (1) {
00352         static struct option long_options[] = {
00353               { "help", no_argument,       0, 'h' },
00354               { "json", no_argument,       0, 'j' },
00355               { "estimate", no_argument,   0, 'e' },
00356               { "tree", required_argument, 0, 't' },
00357               { 0, 0, 0, 0 }
00358         };
00359         int option_index = 0;
00360         int c = getopt_long(argc, argv, "+ehjt:", long_options, &option_index);
00361         if (c == -1) {
00362             break; /* no more options */
00363         }
00364         switch (c) {
00365             case 'e':
00366                 estimate = 1;
00367                 break;
00368             case 'h':
00369                 usage(argv[0]);
00370                 return EXIT_SUCCESS;
00371             case 'j':
00372                 json = 1;
00373                 break;
00374             case 't':
00375                 treefile = optarg;
00376                 break;
00377             case '?':
00378                 if (optopt == 't') {
00379                     err("Option --tree/-t requires an argument. "
00380                         "Pass --help for usage instructions");
00381                 } else {
00382                     err("Unknown option. Pass --help for usage instructions");
00383                 }
00384             default:
00385                 abort();
00386         }
00387     }
00388     if (argc - optind < 2) {
00389         err("Missing arguments. Pass --help for usage instructions.");
00390     }
00391     datafile = argv[optind];
00392     if (strcmp(argv[optind + 1], "circle") == 0) {
00393         optind += 2;
00394         if (argc - optind != 3) {
00395             err("Missing arguments. Pass --help for usage instructions.");
00396         }
00397         circle_count(treefile, datafile, argv + optind);
00398     } else if (strcmp(argv[optind + 1], "ellipse") == 0) {
00399         optind += 2;
00400         if (argc - optind != 5) {
00401             err("Missing arguments. Pass --help for usage instructions.");
00402         }
00403         ellipse_count(treefile, datafile, argv + optind);
00404     } else if (strcmp(argv[optind + 1], "hull") == 0) {
00405         optind += 2;
00406         if (argc - optind < 6 || (argc - optind) % 2 != 0) {
00407             err("Missing arguments. Pass --help for usage instructions.");
00408         }
00409         hull_count(treefile, datafile, argc - optind, argv + optind);
00410     } else if (strcmp(argv[optind + 1], "test") == 0) {
00411         optind += 2;
00412         if (argc - optind != 1) {
00413             err("Missing arguments. Pass --help for usage instructions.");
00414         }
00415         if (treefile == NULL) {
00416             err("Please specify a tree file with --tree (-t).");
00417         }
00418         test_tree(treefile, datafile, argv + optind);
00419         if (json) {
00420             printf("{\"stat\":\"OK\"}\n");
00421         } else {
00422             printf("[struct stat=\"OK\"]\n");
00423         }
00424         fflush(stdout);
00425         return EXIT_SUCCESS;
00426     } else {
00427         err("Invalid region type '%s' - expecting 'circle', "
00428             "'ellipse', or 'hull'", argv[optind + 1]);
00429     }
00430     fflush(stdout);
00431     return EXIT_SUCCESS;
00432 }
00433 
00436 #ifdef __cplusplus
00437 }
00438 #endif