geostat/geostat_cli.cpp

404 wiersze
11 KiB
C++

#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 <iostream>
#include <iomanip>
#include <algorithm>
#include <unistd.h>
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-a\t\tdraw animated map 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 anim = 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:etaLTYh?")) != -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 'a':
anim = 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 (trail && anim) {
std::cout << "Options \"-a\" and \"-t\" 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<std::string, int> 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 if (anim)
hmap.generate_anim(heat_file, sorted_caches, 2);
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();
}
}
}