#include "okapi.h" #include "gpx.h" #include "cache.h" #include "debug.h" #include "heat.h" #include "ocdb.h" #include #include #include #include #include const int HIST_MAX = 20; void show_usage() { std::cout << "Usage: [-p] [-g file] [-qn] [-MDHLTOYh] [-s size] [-m map]\n"; std::cout << "Generate stats from Opencaching data or GPX files.\n\n"; std::cout << " * Data source:\n"; std::cout << "\t-p\tuse Opencaching.pl\n"; std::cout << "\t-u user\tuser for Opencaching\n"; std::cout << "\t-g file\tuse specified gpx file\n"; std::cout << "\t-q\tuse local SQLite file\n"; std::cout << "\t-n\tget only unfound caches (works only with SQLite)\n"; std::cout << " * Output:\n"; std::cout << "\t-M\tprint geographically extreme caches\n"; std::cout << "\t-D\tprint furthest and closest caches\n"; std::cout << "\t-H file\trender a heat map to a file\n"; std::cout << "\t-s n\tstamp size for a heat map (default = 15)\n"; std::cout << "\t-e\tuse exponential to flatten the heat map\n"; std::cout << "\t-m map\tchosen map: Poland, Poland_relief, Poland_big, Europe or a name of voivodeship (default = Poland)\n"; std::cout << "\t-L\tprint all caches\n"; std::cout << "\t-T\tprint D/T matrix\n"; std::cout << "\t-O\tprint owner count for found caches\n"; std::cout << "\t-Y\tprint types count for found caches\n"; std::cout << "\t-W\toutput all data in HTML format\n"; std::cout << "\t-h\tdisplay this help screen\n"; std::exit(EXIT_FAILURE); } void show_histogram(Caches* cc, std::string Cache::*ptr, std::string caption, bool html = 0) { std::map histogram; std::vector> pairs; for (auto el : *cc) histogram[el.*ptr]++; for (auto el : histogram) pairs.push_back(el); sort(pairs.begin(), pairs.end(), [=](std::pair& a, std::pair& b) { return a.second > b.second; }); if (html) { int max = pairs[0].second; int i = 0; std::cout << "

" << caption << "

\n"; std::cout << "
\n"; for (auto own : pairs) { if (i < HIST_MAX) std::cout << "
" << own.first << ": " << own.second << "
\n"; else if (i == HIST_MAX) { std::cout << "
\n"; std::cout << "
Other: " << own.first; } if (i > HIST_MAX) std::cout << ", " << own.first; i++; } if (i < HIST_MAX) std::cout << "\n"; else std::cout << "
\n"; } else { std::cout << caption << '\n'; for (auto own : pairs) std::cout << own.first << ": " << own.second << '\n'; } } int main(int argc, char** argv) { bool show_minmax = 0; bool show_dist = 0; bool show_list = 0; bool show_dt = 0; bool show_owners = 0; bool show_types = 0; bool show_html = 0; std::string heat_file; int heat_stamp_size = 15; std::string heat_map = "Poland"; bool heat_exp = 0; bool use_ocpl = 0; bool use_ocpl_db = 0; bool get_not_found = 0; std::string ocpl_user; std::string ocpl_url = "https://opencaching.pl/okapi/"; const std::string Database = "ocpl.sqlite"; std::string gpx_file; #include "config_user.h" if (argc == 1) show_usage(); int o; while ((o = getopt(argc, argv, "qng:d:pu:MDOH:s:m:eLTYWh?")) != -1) switch (o) { case 'd': try { if (std::stoi(optarg) > 0) { // Debug(1) << "Setting debug level to " << optarg; Debug::set_debug_level(std::stoi(optarg)); } } catch (...) { std::cout << "Option \"-d\" requires a valid number as an argument\n"; std::exit(EXIT_FAILURE); } break; case 'g': gpx_file = optarg; break; case 'p': use_ocpl = 1; break; case 'u': ocpl_user = optarg; break; case 'q': use_ocpl_db = 1; break; case 'n': get_not_found = 1; break; case 'M': show_minmax = 1; break; case 'D': show_dist = 1; break; case 'O': show_owners = 1; break; case 'Y': show_types = 1; break; case 'W': show_html = 1; break; case 'H': heat_file = optarg; break; case 's': try { if (std::stoi(optarg) > 0) { heat_stamp_size = std::stoi(optarg); } } catch (...) { std::cout << "Option \"-s\" requires a valid number as an argument\n"; std::exit(EXIT_FAILURE); } break; case 'm': heat_map = optarg; break; case 'e': heat_exp = 1; break; case 'L': show_list = 1; break; case 'T': show_dt = 1; break; case 'h': case '?': default: show_usage(); } if (show_html) { show_dist = 0; show_list = 0; show_dt = 0; show_owners = 0; show_types = 0; } Caches cc; if (use_ocpl) { Okapi OCpl(ocpl_url, ocpl_key); if (!ocpl_user.empty()) { ocpl_user_uuid = OCpl.get_uuid(ocpl_user); } cc.merge(OCpl.get_user_caches(ocpl_user_uuid, 0)); } if (!gpx_file.empty()) { GPX gpxfile(gpx_file); cc.merge(gpxfile.get_user_caches()); } if (use_ocpl_db) { OCdb db(Database); Okapi OCpl(ocpl_url, ocpl_key); if (!ocpl_user.empty()) { ocpl_user_uuid = OCpl.get_uuid(ocpl_user); } if (get_not_found) { cc.merge(db.get_user_caches_not_found(ocpl_user_uuid)); } else { cc.merge(db.get_user_caches(ocpl_user_uuid, 0)); } } // TODO: some cache deduplication is needed Debug(2) << "Caches read: " << cc.size() << '\n'; if (cc.size() == 0) { std::cout << "No caches found, aborting.\n"; std::exit(EXIT_FAILURE); } if (show_html) { std::cout << "\n"; std::cout << "\n"; std::cout << " \n"; std::cout << " \n"; std::cout << " \n"; std::cout << " Geocaching stats\n"; std::cout << "\n"; std::cout << "\n"; std::cout << "

Geocaching stats for user " << ocpl_user << "
Powered by GeoStat

\n"; } if (!heat_file.empty()) { const Map* chosen_map; if (maps.count(heat_map) > 0) chosen_map = &maps.at(heat_map); else { std::cout << "Map " << heat_map << " not found.\n"; std::exit(EXIT_FAILURE); } Heat hmap(&cc, chosen_map); hmap.generate(heat_file, heat_stamp_size, (heat_exp == 1 ? "exp" : "soft")); if (show_html) std::cout << "\"heat\n"; } if (show_minmax || show_html) { auto N = std::max_element(cc.begin(), cc.end(), CacheCmpNS); auto S = std::min_element(cc.begin(), cc.end(), CacheCmpNS); auto E = std::max_element(cc.begin(), cc.end(), CacheCmpEW); auto W = std::min_element(cc.begin(), cc.end(), CacheCmpEW); if (show_minmax) { std::cout << "Most N:\n"; N->show(); std::cout << "Most S:\n"; S->show(); std::cout << "Most E:\n"; E->show(); std::cout << "Most W:\n"; W->show(); } else { std::cout << "

Geographically extreme caches

\n"; std::cout << "\n"; std::cout << "\n"; std::cout << " \n"; std::cout << "\n"; std::cout << " \n"; std::cout << "\n"; std::cout << " \n"; std::cout << "\n"; std::cout << " \n"; std::cout << "\n"; std::cout << "
North:link() << "\"" << N->name << " (" << N->code << ")" << N->pos.lat << "
South:link() << "\"" << S->name << " (" << S->code << ")" << S->pos.lat << "
East: link() << "\"" << E->name << " (" << E->code << ")" << E->pos.lon << "
West: link() << "\"" << W->name << " (" << W->code << ")" << W->pos.lon << "
\n"; } } if (show_dist) { auto far = std::max_element(cc.begin(), cc.end(), CacheCmpDist); auto near = std::min_element(cc.begin(), cc.end(), CacheCmpDist); std::cout << "Nearest cache: " << near->distance() << " km\n"; near->show(); std::cout << "Furthest cache: " << far->distance() << " km\n"; far->show(); } if (show_list) { for (auto el : cc) el.show(); } if (show_owners || show_html) { show_histogram(&cc, &Cache::owner, "Cache owners", show_html); } if (show_types || show_html) { show_histogram(&cc, &Cache::type, "Cache types", show_html); if (!show_html) show_histogram(&cc, &Cache::origin, "Services", 0); show_histogram(&cc, &Cache::region, "Regions", show_html); } if (show_dt) { short dt_table[11][11]; std::cout << std::setw(5) << "D\\T"; for (int j = 2; j <= 10; j++) { // print table terr headers std::cout << std::setw(5) << j / 2.0; } std::cout << '\n'; for (int i = 2; i <= 10; i++) { // i -> diff in rows std::cout << std::setw(5) << i / 2.0; for (int j = 2; j <= 10; j++) { // j -> terr in cols dt_table[i][j] = std::count_if(cc.begin(), cc.end(), [i, j](Cache c) { return (c.diff == i / 2.0 && c.terr == j / 2.0); }); std::cout << std::setw(5) << dt_table[i][j]; } std::cout << '\n'; } } if (show_html) { short dt_table[11][11]; std::cout << "

Difficulty / terrain matrix

\n"; std::cout << "\n"; std::cout << ""; for (int j = 2; j <= 10; j++) { // print table terr headers std::cout << ""; } std::cout << "\n"; for (int i = 2; i <= 10; i++) { // i -> diff in rows std::cout << " "; for (int j = 2; j <= 10; j++) { // j -> terr in cols dt_table[i][j] = std::count_if(cc.begin(), cc.end(), [i, j](Cache c) { return (c.diff == i / 2.0 && c.terr == j / 2.0); }); if (dt_table[i][j] == 0) std::cout << ""; else std::cout << ""; } std::cout << "\n"; } std::cout << "
" << j / 2.0 << "
" << i / 2.0 << "" << dt_table[i][j] << "" << dt_table[i][j] << "
\n"; } if (show_html) { std::cout << "\n\n"; } }