sql-rework
Tomasz Golinski 2019-09-08 18:42:10 +02:00
commit 54e240f910
19 zmienionych plików z 910 dodań i 0 usunięć

117
.clang-format 100644
Wyświetl plik

@ -0,0 +1,117 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Always
...

12
.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,12 @@
*.[oa]
*~
*.backup
*.l[ao]
*.kdev4
.deps
.libs
.kdev4
*.kate-swp
pli
/build*
config_user.h

BIN
Poland.png 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 162 KiB

BIN
Poland_relief.png 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.4 MiB

43
README.md 100644
Wyświetl plik

@ -0,0 +1,43 @@
GEOSTAT
=======
Tools for computing various statistics related to Geocaching/Opencaching
------------------------------------------------------------------------
### Usage
```
Usage: [-p] [-g file] [-MDHh]
Generate stats from Opencaching data or GPX files.
* Data source:
-p use Opencaching.pl
-u user user for Opencaching
-g file use specified gpx file
* Output:
-M print geographically extreme caches
-D print furthest and closest caches
-H file render a heat map to a file
-s n stamp size for a heat map (default = 15)
-L print all caches
-T print D/T matrix
-h display this help screen
```
### Installation
To compile it requires:
* C++20 compliler (e.g. GCC-9 or Clang-8).
* meson (at least 0.47.0) + ninja build system
* libcurl
* JSON for Modern C++: https://github.com/nlohmann/json
* gpxlib: http://irdvo.nl/gpxlib/
* ImageMagick or GraphicsMagick Magick++ library
To build the project run `meson build; cd build; ninja`. You might need to set `CXX` variable to point to a correct compiler.
### Credits
Map of Poland `Poland.png` comes from `https://pl.wikipedia.org/wiki/Plik:Poland_location_map.svg` and is licenced under CC-BY-SA and GNU FDL.
Map of Poland `Poland_relief.png` comes from `https://commons.wikimedia.org/wiki/File:Relief_Map_of_Poland.svg` and is licenced under CC-BY-SA.

15
api.h 100644
Wyświetl plik

@ -0,0 +1,15 @@
#pragma once
#include "cache.h"
#include <vector>
class Api {
public:
Api() {}
virtual ~Api() {}
// virtual Cache get_cache(std::string code) = 0;
// virtual Caches get_caches(std::vector<std::string> codes) = 0;
virtual Caches get_user_caches(std::string uuid, int count = 0) = 0;
};

30
cache.cpp 100644
Wyświetl plik

@ -0,0 +1,30 @@
#include "cache.h"
#include <cmath>
const static int Earth_radius = 6378;
Position Cache::home;
static float degtorad(float x) {
return x * M_PI / 180;
}
void Cache::show() const {
std::cout << "Cache:\t" << code << ' ' << name << '\n';
std::cout << '\t' << pos.lat << " " << pos.lon << "\t\t D/T: " << diff << '/' << terr << '\n';
}
float Cache::distance() const {
return 2 * Earth_radius * asin(sqrt(pow(sin(degtorad((pos.lat - home.lat) / 2)), 2) + cos(degtorad(pos.lat)) * cos(degtorad(home.lat)) * pow(sin(degtorad((pos.lon - home.lon) / 2)), 2)));
};
bool CacheCmpNS(const Cache& lhs, const Cache& rhs) {
return lhs.pos.lat < rhs.pos.lat;
}
bool CacheCmpEW(const Cache& lhs, const Cache& rhs) {
return lhs.pos.lon < rhs.pos.lon;
}
bool CacheCmpDist(const Cache& lhs, const Cache& rhs) {
return lhs.distance() < rhs.distance();
}

65
cache.h 100644
Wyświetl plik

@ -0,0 +1,65 @@
#pragma once
#include <string>
#include <set>
#include <iostream>
enum Service {
gc,
ocpl,
ocde,
ocna,
ocro,
ocuk,
ocnl,
gcsu
};
// enum Type {
// traditional,
// multi,
// quiz, // also known as "Mystery"
// moving, // a geocache with changing coordinates
// virt,
// webcam,
// other, // also dubbed "unknown type"; allows OC users to create special caches which don't fit into the scheme of well-known types
// event, // a peculiar type of geocache which is NOT a geocache at all, but it is stored as a geocache in OC database. Just keep in mind, that in case of Event Caches, some fields may have a little different meaning than you would tell by their name
// own, // a moving geocache which is carried by the owner
// podcast //a geocache with attached MP3 file(s). The MP3 data is not accessible via OKAPI. This type is only in use at Opencaching.US
// };
class Position {
public:
float lat;
float lon;
};
class Cache {
public:
std::string code;
Position pos;
std::string name;
std::string size;
float diff;
float terr;
std::string type;
Service origin;
static Position home;
void show() const;
float distance() const;
};
class CacheCmp {
public:
bool operator()(const Cache& lhs, const Cache& rhs) const {
return lhs.code < rhs.code;
}
};
bool CacheCmpNS(const Cache& lhs, const Cache& rhs);
bool CacheCmpEW(const Cache& lhs, const Cache& rhs);
bool CacheCmpDist(const Cache& lhs, const Cache& rhs);
typedef std::set<Cache, CacheCmp> Caches;

Wyświetl plik

@ -0,0 +1,10 @@
#pragma once
// Default OCPL user to use when -u is not used
std::string ocpl_user_uuid = "";
// Consumer key for OCPL API
std::string ocpl_key = "";
// Home location
Cache::home = { 0, 0 };

19
debug.cpp 100644
Wyświetl plik

@ -0,0 +1,19 @@
#include "debug.h"
int Debug::debug_level = 1;
Debug::Debug(int n) : lvl(n) {
if (lvl <= debug_level) {
std::string indent;
indent.append(lvl - 1, '\t');
if (lvl > 1) indent.append("*** ");
std::cout << indent;
}
}
Debug::~Debug() {
if (lvl <= debug_level) std::cout << '\n';
}
void Debug::set_debug_level(int n) { debug_level = n; }

27
debug.h 100644
Wyświetl plik

@ -0,0 +1,27 @@
#pragma once
#include <iostream>
#include <string>
#include <sstream>
class Debug {
private:
int lvl; // debug level of the particular instance of Debug stream
static int debug_level; // debug level set globally
public:
Debug(int n);
~Debug();
// Copy constructor and assignment operators are deleted to prevent extra destructor and constructor calls when chaining << operator
Debug(const Debug&) = delete;
Debug& operator=(const Debug&) = delete;
template <typename T>
Debug& operator<<(T const& value) {
if (lvl <= debug_level)
std::cout << value;
return *this;
}
static void set_debug_level(int n);
};

174
geostat.cpp 100644
Wyświetl plik

@ -0,0 +1,174 @@
#include "okapi.h"
#include "gpx.h"
#include "cache.h"
#include "debug.h"
#include "heat.h"
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <unistd.h>
int main(int argc, char** argv) {
bool show_minmax = 0;
bool show_dist = 0;
bool show_list = 0;
bool show_dt = 0;
std::string heat_file;
int heat_stamp_size = 15;
bool heat_relief = 0;
bool use_ocpl = 0;
std::string ocpl_user;
std::string ocpl_url = "https://opencaching.pl/okapi/";
std::string gpx_file;
#include "config_user.h"
int o;
while ((o = getopt(argc, argv, "g:d:pu:MDH:s:rLTh")) != -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 'M':
show_minmax = 1;
break;
case 'D':
show_dist = 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 'r':
heat_relief = 1;
break;
case 'L':
show_list = 1;
break;
case 'T':
show_dt = 1;
break;
case 'h':
case '?':
default:
std::cout << "Usage: [-p] [-g file] [-MDHh]\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 << " * 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-L\tprint all caches\n";
std::cout << "\t-T\tprint D/T matrix\n";
std::cout << "\t-h\tdisplay this help screen\n";
std::exit(EXIT_FAILURE);
}
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());
}
// TODO: some cache deduplication is needed
Debug(2) << "Caches read: " << cc.size() << '\n';
if (!heat_file.empty()) {
Heat hmap(&cc);
hmap.generate(heat_file, heat_stamp_size, heat_relief);
}
if (show_minmax) {
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);
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(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();
std::cout << "Dist:\t" << far->distance() << " km\n";
}
if (show_list) {
for (auto el : cc)
el.show();
}
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';
}
}
}

76
gpx.cpp 100644
Wyświetl plik

@ -0,0 +1,76 @@
#include "gpx.h"
#include "cache.h"
#include "debug.h"
#include <iostream>
#include <fstream>
#include <string>
#include <gpx/GPX.h>
#include "gpx/Parser.h"
#include "gpx/Report.h"
class ReportDebug : public gpx::Report {
public:
ReportDebug() {}
~ReportDebug() {}
void report(const gpx::Node* node, gpx::Report::Warning warning, const std::string& extra) {
std::string msg;
if (node != nullptr) {
msg += (node->getType() == gpx::Node::ATTRIBUTE ? "Attribute " : "Element ") + node->getName() + " : ";
}
msg += gpx::Report::text(warning);
if (!extra.empty()) {
msg += ": " + extra;
}
Debug(3) << msg + ".\n";
}
};
GPX::GPX(std::string path) {
std::ifstream stream(path);
if (stream.is_open()) {
ReportDebug report;
gpx::Parser parser(&report);
root = parser.parse(stream);
if (root == nullptr) {
Debug(1) << "Parsing of file " << path << " failed due to " << parser.errorText() << " on line " << parser.errorLineNumber() << " and column " << parser.errorColumnNumber() << '\n';
} else {
items = root->wpts().list();
}
}
}
GPX::~GPX() {
delete root;
}
Caches GPX::get_user_caches(std::string uuid, int count) {
Caches list;
for (auto& el : items) {
if (el->sym().getValue() == "Geocache Found") {
Cache c;
c.code = el->name().getValue();
c.name = el->desc().getValue();
c.pos.lat = stof(el->lat().getValue());
c.pos.lon = stof(el->lon().getValue());
if (c.code.starts_with("GC"))
c.origin = gc;
else if (c.code.starts_with("OP"))
c.origin = ocpl;
else if (c.code.starts_with("TR") || c.code.starts_with("VI") || c.code.starts_with("MV"))
c.origin = gcsu;
list.insert(c);
}
}
Debug(2) << "Caches read from GPX file: " << list.size() << '\n';
return list;
}

24
gpx.h 100644
Wyświetl plik

@ -0,0 +1,24 @@
#pragma once
#include "api.h"
#include <string>
#include <list>
namespace gpx {
class WPT;
class GPX;
} // namespace gpx
class GPX : Api {
private:
std::list<gpx::WPT*> items;
gpx::GPX* root;
public:
GPX(std::string path);
~GPX();
Cache get_cache(std::string code);
Caches get_user_caches(std::string uuid = "", int count = 0);
};

43
heat.cpp 100644
Wyświetl plik

@ -0,0 +1,43 @@
#include "heat.h"
#include "cache.h"
#include <heatmap.h>
#include <colorschemes/Spectral.h>
#include <Magick++.h>
#include <set>
#include <vector>
#include <string>
Heat::Heat(Caches* cc) : points(cc) {
#ifdef graphicsmagick
Magick::InitializeMagick(nullptr);
#endif
}
void Heat::generate(std::string filename, int size, bool relief, std::string theme) {
heatmap_t* hm = heatmap_new(size_x, size_y);
heatmap_stamp_t* stamp = heatmap_stamp_gen(size);
std::vector<unsigned char> image(size_x * size_y * 4);
for (auto el : *points) {
if (el.pos.lon >= lon_min && el.pos.lon <= lon_max && el.pos.lat >= lat_min && el.pos.lat <= lat_max)
heatmap_add_point_with_stamp(hm, (el.pos.lon - lon_min) / (lon_max - lon_min) * size_x, size_y - static_cast<int>((el.pos.lat - lat_min) / (lat_max - lat_min) * size_y), stamp);
// Debug(2) << static_cast<int>((el.pos.lon - lon_min) / (lon_max - lon_min) * size_x) << " " << size_y - static_cast<int>((el.pos.lat - lat_min) / (lat_max - lat_min) * size_y) << "\n";
}
heatmap_render_to(hm, heatmap_cs_Spectral_soft, &image[0]);
heatmap_free(hm);
Magick::Image contour;
if (relief)
contour.read(relief_file);
else
contour.read(contour_file);
Magick::Image heatmap(size_x, size_y, "RGBA", Magick::CharPixel, &image[0]);
contour.composite(heatmap, 0, 0, Magick::OverCompositeOp);
contour.write(filename);
// heatmap.write("geostat_heat.png");
}

25
heat.h 100644
Wyświetl plik

@ -0,0 +1,25 @@
#pragma once
#include "cache.h"
#include <set>
#include <string>
class Heat {
private:
Caches* points;
const int size_x = 1000;
const int size_y = 972;
const float lat_max = 55.2;
const float lat_min = 48.7;
const float lon_max = 24.5;
const float lon_min = 13.8;
const std::string contour_file = "Poland.png";
const std::string relief_file = "Poland_relief.png";
public:
Heat(Caches* cc);
void generate(std::string filename, int size, bool relief, std::string theme = "");
};

22
meson.build 100644
Wyświetl plik

@ -0,0 +1,22 @@
project('geostat', 'cpp', default_options : ['cpp_std=c++2a'])
curl_dep = dependency('libcurl')
json_dep = dependency('nlohmann_json')
#tinyxml_dep = dependency('tinyxml2')
magick_dep = dependency('Magick++', required : false)
if not magick_dep.found()
magick_dep = dependency('GraphicsMagick++', required : false)
endif
if not magick_dep.found()
error('ImageMagick++ or GraphicsMagick++ not found.')
else
add_global_arguments('-Dgraphicsmagick', language : 'cpp')
endif
link = ['-lgpx', '-lheatmap']
src = ['geostat.cpp', 'okapi.cpp', 'gpx.cpp', 'cache.cpp', 'debug.cpp', 'heat.cpp']
executable('geostat', src, dependencies : [curl_dep, json_dep, magick_dep], link_args: link)
configure_file(input: 'Poland.png', output: 'Poland.png', copy: true)
configure_file(input: 'Poland_relief.png', output: 'Poland_relief.png', copy: true)

182
okapi.cpp 100644
Wyświetl plik

@ -0,0 +1,182 @@
#include "okapi.h"
#include "cache.h"
#include "debug.h"
#include <iostream>
#include <vector>
#include <curl/curl.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
static const std::string OKAPI_logs = "services/logs/userlogs";
static const std::string OKAPI_cache = "services/caches/geocache";
static const std::string OKAPI_caches = "services/caches/geocaches";
static const std::string OKAPI_username = "services/users/by_username";
size_t Okapi::write_cb(char* ptr, size_t size, size_t nmemb, void* userdata) {
std::string* str = reinterpret_cast<std::string*>(userdata);
str->append(ptr, nmemb);
return nmemb;
}
std::string Okapi::curl_post(std::string url, std::string post) {
CURL* curl;
CURLcode res;
std::string output;
Debug(5) << "API query: " << post;
curl = curl_easy_init();
if (!curl) {
curl_global_cleanup();
throw 0;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&output);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
Debug(1) << "curl_easy_perform() failed: " << curl_easy_strerror(res) << '\n';
curl_easy_cleanup(curl);
std::exit(EXIT_FAILURE);
}
curl_easy_cleanup(curl);
Debug(5) << "API query result: " << output;
if (output.starts_with("{\"error\"")) {
json j = json::parse(output);
Debug(1) << "OKAPI error: " << j["error"]["developer_message"];
std::exit(EXIT_FAILURE);
}
return output;
}
std::string Okapi::get_user_caches_json(std::string uuid, int count, int offset) {
std::string service = url + OKAPI_logs;
std::string query = "consumer_key=" + key + "&user_uuid=" + uuid + "&fields=cache_code|type&limit=" + std::to_string(count) + "&offset=" + std::to_string(offset);
return curl_post(service, query);
}
// std::string Okapi::get_cache_json(std::string code) {
// std::string service = url + OKAPI_cache;
// std::string query = "consumer_key=" + key + "&cache_code=" + code + "&fields=code|name|location|type|status|difficulty|terrain";
// return curl_post(service, query);
// }
std::string Okapi::get_caches_json(std::string codes) {
std::string service = url + OKAPI_caches;
std::string query = "consumer_key=" + key + "&cache_codes=" + codes + "&fields=code|name|location|type|status|difficulty|terrain";
return curl_post(service, query);
}
// Cache Okapi::get_cache(std::string code) {
// std::string output = get_cache_json(code);
// json j = json::parse(output);
//
// Cache c;
// c.code = code;
// c.name = j["name"];
// c.type = j["type"];
// c.diff = j["difficulty"].get<float>();
// c.terr = j["terrain"].get<float>();
// // std::cout << j["difficulty"] << '\n';
// // std::cout << j["difficulty"].get<float>() << '\n';
// // std::cout << j["difficulty"].get<double>() << '\n';
// // std::cout << j["difficulty"].get<int>() << '\n';
//
// std::string loc = j["location"];
// int pos = loc.find("|");
// c.pos.lat = stof(loc.substr(0, pos));
// c.pos.lon = stof(loc.substr(pos + 1));
//
// return c;
// }
Caches Okapi::get_caches(std::vector<std::string> codes) {
Caches cc;
Cache c;
while (codes.size() > 0) {
std::string codes_list;
int n = (codes.size() > 500) ? 500 : codes.size();
for (int i = 0; i < n; i++) {
codes_list += codes.back();
codes_list += '|';
codes.pop_back();
}
codes_list.pop_back(); // remove trailing '|'
std::string output = get_caches_json(codes_list);
json j = json::parse(output);
for (auto& el : j.items()) {
c.code = el.value()["code"];
c.name = el.value()["name"];
c.type = el.value()["type"];
c.diff = el.value()["difficulty"];
c.terr = el.value()["terrain"];
;
std::string loc = el.value()["location"];
int pos = loc.find("|");
c.pos.lat = stof(loc.substr(0, pos));
c.pos.lon = stof(loc.substr(pos + 1));
c.origin = ocpl;
cc.insert(c);
}
}
return cc;
}
Caches Okapi::get_user_caches(std::string uuid, int count) {
Caches cc;
std::vector<std::string> codes;
json j;
int off = 0;
if (count == 0)
do {
std::string output = get_user_caches_json(uuid, 1000, off);
j = json::parse(output);
for (auto& el : j.items()) {
if (el.value()["type"] == "Found it") {
codes.emplace_back(el.value()["cache_code"]);
}
}
off += j.size();
} while (j.size() > 0);
else {
int count_req = (count > 1000) ? 1000 : count;
do {
std::string output = get_user_caches_json(uuid, count_req, off);
j = json::parse(output);
for (auto& el : j.items()) {
if (el.value()["type"] == "Found it") {
codes.emplace_back(el.value()["cache_code"]);
}
}
off += j.size();
count -= count_req;
} while (j.size() > 0 && count > 0);
}
// Debug(3) << codes;
cc = get_caches(codes);
Debug(2) << "Caches read from OC: " << cc.size() << '\n';
return cc;
}
std::string Okapi::get_uuid(std::string username) {
std::string service = url + OKAPI_username;
std::string query = "consumer_key=" + key + "&username=" + username + "&fields=uuid";
json j = json::parse(curl_post(service, query));
return j["uuid"];
}

26
okapi.h 100644
Wyświetl plik

@ -0,0 +1,26 @@
#pragma once
#include "api.h"
#include <string>
class Okapi : Api {
private:
std::string url;
std::string key;
static size_t write_cb(char* ptr, size_t size, size_t nmemb, void* userdata);
std::string curl_post(std::string url, std::string post);
std::string get_user_caches_json(std::string uuid, int count = 0, int offset = 0);
// std::string get_cache_json(std::string code);
std::string get_caches_json(std::string codes);
public:
Okapi(std::string server_url, std::string consumer_key) : url(server_url), key(consumer_key) {}
// Cache get_cache(std::string code);
Caches get_caches(std::vector<std::string> codes);
Caches get_user_caches(std::string uuid, int count = 0);
std::string get_uuid(std::string username);
};