kopia lustrzana https://gitlab.com/tomaszg/geostat
Initial commit
commit
54e240f910
|
@ -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
|
||||
...
|
|
@ -0,0 +1,12 @@
|
|||
*.[oa]
|
||||
*~
|
||||
*.backup
|
||||
*.l[ao]
|
||||
*.kdev4
|
||||
.deps
|
||||
.libs
|
||||
.kdev4
|
||||
*.kate-swp
|
||||
pli
|
||||
/build*
|
||||
config_user.h
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 162 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 1.4 MiB |
|
@ -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.
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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 };
|
|
@ -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; }
|
|
@ -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);
|
||||
};
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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");
|
||||
}
|
|
@ -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 = "");
|
||||
};
|
|
@ -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)
|
|
@ -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"];
|
||||
}
|
|
@ -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);
|
||||
};
|
Ładowanie…
Reference in New Issue