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