#include "okapi.h" #include "gpx.h" #include "cache.h" #include "debug.h" #include "heat.h" #include "ocdb.h" #include "common.h" #include "region.h" #include #include #include #include void show_usage() { std::cout << "Usage: geostat [options]\n"; std::cout << "Generate stats from Opencaching data or GPX files.\n\n"; std::cout << " * Data sources:\n"; std::cout << "\t-o[user]\tuse Opencaching; if specified user will be a user name used for all OC instances\n"; std::cout << "\t-p user\t\tuser for opencaching.pl\n"; std::cout << "\t-d user\t\tuser for opencaching.de\n"; std::cout << "\t-u user\t\tuser for opencaching.us\n"; std::cout << "\t-n user\t\tuser for opencaching.nl\n"; std::cout << "\t-r user\t\tuser for opencaching.ro\n"; std::cout << "\t-k user\t\tuser for opencaching.uk\n"; std::cout << "\t-g file\t\tuse specified gpx file\n"; std::cout << "\t-q\t\tuse local SQLite file with dump of OC database\n"; std::cout << " * Output:\n"; std::cout << "\t-N\t\tcompute stats only for unfound caches (works only with SQLite)\n"; std::cout << "\t-M\t\tprint geographically extreme caches\n"; std::cout << "\t-D\t\tprint furthest and closest caches\n"; std::cout << "\t-C\t\tprint calendar related stats\n"; std::cout << "\t-H file\t\trender a heat map to a file\n"; std::cout << "\t-s n\t\tstamp size for a heat map (default = 15)\n"; std::cout << "\t-e\t\tuse exponential to flatten the heat map\n"; std::cout << "\t-t\t\tdraw trail instead of heat map\n"; std::cout << "\t-m map\t\tchosen map: Poland, Poland_relief, Poland_big, Europe, World or a name of voivodeship (default = Poland)\n"; std::cout << "\t-L\t\tprint all caches\n"; std::cout << "\t-T\t\tprint D/T matrix\n"; std::cout << "\t-O\t\tprint owner count for found caches\n"; std::cout << "\t-Y\t\tprint types count for found caches\n"; std::cout << "\t-h\t\tdisplay this help screen\n"; std::exit(EXIT_FAILURE); } 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_calendar = 0; std::string heat_file; int heat_stamp_size = 15; std::string heat_map = "Poland"; bool heat_exp = 0; bool trail = 0; bool use_oc = 0; bool use_ocpl_db = 0; bool get_not_found = 0; bool get_owned = 0; std::string ocpl_user; std::string ocde_user; std::string ocus_user; std::string ocnl_user; std::string ocro_user; std::string ocuk_user; std::string ocpl_url = "https://opencaching.pl/okapi/"; std::string ocde_url = "https://www.opencaching.de/okapi/"; std::string ocus_url = "http://www.opencaching.us/okapi/"; std::string ocnl_url = "http://www.opencaching.nl/okapi/"; std::string ocro_url = "http://www.opencaching.ro/okapi/"; std::string ocuk_url = "https://opencache.uk/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, "qNOg:o::p:d:u:n:r:k:MDCH:s:m:etLTYh?")) != -1) switch (o) { // case 'd': // Debug::set_debug_level(get_num('d',optarg)); // break; case 'g': gpx_file = optarg; break; case 'o': use_oc = 1; if (optarg) { ocpl_user = optarg; ocde_user = optarg; ocus_user = optarg; ocnl_user = optarg; ocro_user = optarg; ocuk_user = optarg; } break; case 'p': ocpl_user = optarg; break; case 'd': ocde_user = optarg; break; case 'u': ocus_user = optarg; break; case 'n': ocnl_user = optarg; break; case 'r': ocro_user = optarg; break; case 'k': ocuk_user = optarg; break; case 'q': use_ocpl_db = 1; break; case 'N': get_not_found = 1; break; case 'O': get_owned = 1; break; case 'M': show_minmax = 1; break; case 'D': show_dist = 1; break; case 'C': show_calendar = 1; break; // case 'O': // show_owners = 1; // break; case 'Y': show_types = 1; break; case 'H': heat_file = optarg; break; case 's': heat_stamp_size = get_num('s', optarg); break; case 'm': heat_map = optarg; break; case 'e': heat_exp = 1; break; case 't': trail = 1; break; case 'L': show_list = 1; break; case 'T': show_dt = 1; break; case 'h': case '?': default: show_usage(); } Caches cc; if (get_not_found || get_owned) { use_oc = 0; use_ocpl_db = 1; // trail = 0; if (ocpl_user_uuid.empty() && ocpl_user.empty()) { std::cout << "Options \"-N\" or \"-O\" work only with OCpl\n"; std::exit(EXIT_FAILURE); } if (get_not_found && get_owned) { std::cout << "Options \"-N\" and \"-O\" are mutually exclusive.\n"; std::exit(EXIT_FAILURE); } } if (use_oc) { if (!ocpl_user_uuid.empty() || !ocpl_user.empty()) { Okapi OCpl(ocpl_url, ocpl_key); if (!ocpl_user.empty()) ocpl_user_uuid = OCpl.get_uuid(ocpl_user); Caches tmp = OCpl.get_user_caches(ocpl_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (!ocde_user_uuid.empty() || !ocde_user.empty()) { Okapi OCde(ocde_url, ocde_key); if (!ocde_user.empty()) ocde_user_uuid = OCde.get_uuid(ocde_user); Caches tmp = OCde.get_user_caches(ocde_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (!ocus_user_uuid.empty() || !ocus_user.empty()) { Okapi OCus(ocus_url, ocus_key); if (!ocus_user.empty()) ocus_user_uuid = OCus.get_uuid(ocus_user); Caches tmp = OCus.get_user_caches(ocus_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (!ocnl_user_uuid.empty() || !ocnl_user.empty()) { Okapi OCnl(ocnl_url, ocnl_key); if (!ocnl_user.empty()) ocnl_user_uuid = OCnl.get_uuid(ocnl_user); Caches tmp = OCnl.get_user_caches(ocnl_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (!ocro_user_uuid.empty() || !ocro_user.empty()) { Okapi OCro(ocro_url, ocro_key); if (!ocro_user.empty()) ocro_user_uuid = OCro.get_uuid(ocro_user); Caches tmp = OCro.get_user_caches(ocro_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (!ocuk_user_uuid.empty() || !ocuk_user.empty()) { Okapi OCuk(ocuk_url, ocuk_key); if (!ocuk_user.empty()) ocuk_user_uuid = OCuk.get_uuid(ocuk_user); Caches tmp = OCuk.get_user_caches(ocuk_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } } if (!gpx_file.empty()) { GPX gpxfile(gpx_file); Caches tmp = gpxfile.get_user_caches(); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } if (use_ocpl_db) { OCdb db(Database); if (!ocpl_user.empty()) { Okapi OCpl(ocpl_url, ocpl_key); ocpl_user_uuid = OCpl.get_uuid(ocpl_user); } if (get_not_found) { Caches tmp = db.get_user_caches_not_found(ocpl_user_uuid); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } else if (get_owned) { Caches tmp = db.get_user_caches_owned(ocpl_user_uuid); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } else { Caches tmp = db.get_user_caches(ocpl_user_uuid, 0); std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); } } // 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); } // Prepare a set of regions Country poland({ "dolnoslaskie", "gornoslaskie", "kujawsko-pomorskie", "lodzkie", "lubelskie", "lubuskie", "malopolskie", "mazowieckie", "opolskie", "podkarpackie", "podlaskie", "pomorskie", "swietokrzyskie", "warminsko-mazurskie", "wielkopolskie", "zachodniopomorskie" }); // Prepare sorted list of caches, excluding moving caches std::map dates; Date_Caches sorted_caches; pCaches fcc; if (!get_not_found) { for (auto& i : cc) { poland.locate(i); dates[i.date]++; if (i.type != "Moving" && i.type != "Own") { sorted_caches.insert({ i.date_t, &i }); fcc.push_back(&i); } } } else { for (auto& i : cc) { if (i.type != "Moving" && i.type != "Own") { fcc.push_back(&i); } } } 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(chosen_map); if (trail) hmap.generate_path(heat_file, sorted_caches); else hmap.generate(heat_file, fcc, heat_stamp_size, (heat_exp == 1 ? "exp" : "soft")); } if (!get_not_found) { if (show_list) { for (auto el : sorted_caches) el.second->show(); } if (show_minmax) { auto N = *std::max_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->pos.lat < b->pos.lat; }); auto S = *std::min_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->pos.lat < b->pos.lat; }); auto E = *std::max_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->pos.lon < b->pos.lon; }); auto W = *std::min_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->pos.lon < b->pos.lon; }); 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(); } if (show_dist) { auto far = *std::max_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->distance() < b->distance(); }); auto near = *std::min_element(fcc.begin(), fcc.end(), [&](const Cache* a, const Cache* b) { return a->distance() < b->distance(); }); std::cout << "Nearest cache: " << near->distance() << " km\n"; near->show(); std::cout << "Furthest cache: " << far->distance() << " km\n"; far->show(); } if (show_owners) { show_histogram(cc, &Cache::owner, "Cache owners"); } if (show_types) { show_histogram(cc, &Cache::type, "Cache types"); show_histogram(cc, &Cache::size, "Cache sizes"); show_histogram(cc, &Cache::origin, "Services"); show_histogram(cc, &Cache::region, "Regions"); } if (show_calendar) { show_histogram(cc, &Cache::day_of_week, "Days of the week", 0, 0); show_histogram(cc, &Cache::mon, "Months", 0, 0); show_histogram(cc, &Cache::year, "Years", 0, 0); } 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'; } } } else { if (show_list) { for (auto el : cc) el.show(); } } }