Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
273 lines
11 KiB
C++
273 lines
11 KiB
C++
#include "glfw_view.hpp"
|
|
#include "glfw_renderer_frontend.hpp"
|
|
#include "settings_json.hpp"
|
|
|
|
#include <mln/gfx/backend.hpp>
|
|
#include <mln/renderer/renderer.hpp>
|
|
#include <mln/storage/database_file_source.hpp>
|
|
#include <mln/storage/file_source_manager.hpp>
|
|
#include <mln/style/style.hpp>
|
|
#include <mln/util/action_journal.hpp>
|
|
#include <mln/util/action_journal_options.hpp>
|
|
#include <mln/util/logging.hpp>
|
|
#include <mln/util/platform.hpp>
|
|
#include <mln/util/string.hpp>
|
|
|
|
#include <args.hxx>
|
|
|
|
#include <csignal>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <array>
|
|
|
|
namespace {
|
|
|
|
GLFWView* view = nullptr;
|
|
|
|
} // namespace
|
|
|
|
void quit_handler(int) {
|
|
if (view) {
|
|
mln::Log::Info(mln::Event::Setup, "waiting for quit...");
|
|
view->setShouldClose();
|
|
} else {
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
args::ArgumentParser argumentParser("MapLibre Native GLFW example");
|
|
args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"});
|
|
|
|
args::Flag fullscreenFlag(argumentParser, "fullscreen", "Toggle fullscreen", {'f', "fullscreen"});
|
|
args::Flag benchmarkFlag(argumentParser, "benchmark", "Toggle benchmark", {'b', "benchmark"});
|
|
args::Flag offlineFlag(argumentParser, "offline", "Toggle offline", {'o', "offline"});
|
|
|
|
args::ValueFlag<std::string> testDirValue(
|
|
argumentParser, "directory", "Root directory for test generation", {"testDir"});
|
|
args::ValueFlag<std::string> backendValue(argumentParser, "backend", "Rendering backend", {"backend"});
|
|
args::ValueFlag<std::string> apikeyValue(argumentParser, "key", "API key", {'t', "apikey"});
|
|
args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"});
|
|
args::ValueFlag<std::string> cacheDBValue(argumentParser, "file", "Cache database file name", {'c', "cache"});
|
|
args::ValueFlag<std::string> actionJournalDirValue(
|
|
argumentParser, "directory", "Action journal log directory", {"actionJournalDir"});
|
|
args::ValueFlag<double> lonValue(argumentParser, "degrees", "Longitude", {'x', "lon"});
|
|
args::ValueFlag<double> latValue(argumentParser, "degrees", "Latitude", {'y', "lat"});
|
|
args::ValueFlag<double> altValue(argumentParser, "degrees", "Altitude", {'a', "alt"});
|
|
args::ValueFlag<double> fovValue(argumentParser, "degrees", "FOV", {'F', "fov"});
|
|
args::ValueFlag<double> zoomValue(argumentParser, "number", "Zoom level", {'z', "zoom"});
|
|
args::ValueFlag<double> bearingValue(argumentParser, "degrees", "Bearing", {'b', "bearing"});
|
|
args::ValueFlag<double> pitchValue(argumentParser, "degrees", "Pitch", {'p', "pitch"});
|
|
args::ValueFlag<double> rollValue(argumentParser, "degrees", "Roll", {'r', "roll"});
|
|
args::ValueFlag<double> maxPitchValue(argumentParser, "degrees", "Max Pitch", {'P', "maxPitch"});
|
|
|
|
try {
|
|
argumentParser.ParseCLI(argc, argv);
|
|
} catch (const args::Help&) {
|
|
std::cout << argumentParser;
|
|
exit(0);
|
|
} catch (const args::ParseError& e) {
|
|
std::cerr << e.what() << std::endl;
|
|
std::cerr << argumentParser;
|
|
exit(1);
|
|
} catch (const args::ValidationError& e) {
|
|
std::cerr << e.what() << std::endl;
|
|
std::cerr << argumentParser;
|
|
exit(2);
|
|
}
|
|
|
|
// Load settings
|
|
mln::Settings_JSON settings;
|
|
settings.online = !offlineFlag;
|
|
if (lonValue) settings.longitude = args::get(lonValue);
|
|
if (latValue) settings.latitude = args::get(latValue);
|
|
if (altValue) settings.altitude = args::get(altValue);
|
|
if (fovValue) settings.fov = args::get(fovValue);
|
|
if (maxPitchValue) settings.maxPitch = args::get(maxPitchValue);
|
|
if (zoomValue) settings.zoom = args::get(zoomValue);
|
|
if (bearingValue) settings.bearing = args::get(bearingValue);
|
|
if (pitchValue) settings.pitch = args::get(pitchValue);
|
|
if (rollValue) settings.roll = args::get(rollValue);
|
|
|
|
const bool fullscreen = fullscreenFlag ? args::get(fullscreenFlag) : false;
|
|
const bool benchmark = benchmarkFlag ? args::get(benchmarkFlag) : false;
|
|
std::string style = styleValue ? args::get(styleValue) : "";
|
|
const std::string cacheDB = cacheDBValue ? args::get(cacheDBValue) : "/tmp/mbgl-cache.db";
|
|
|
|
// sigint handling
|
|
#ifdef WIN32
|
|
signal(SIGINT, &quit_handler);
|
|
#else
|
|
struct sigaction sigIntHandler;
|
|
sigIntHandler.sa_handler = quit_handler;
|
|
sigemptyset(&sigIntHandler.sa_mask);
|
|
sigIntHandler.sa_flags = 0;
|
|
sigaction(SIGINT, &sigIntHandler, nullptr);
|
|
#endif
|
|
|
|
if (benchmark) {
|
|
mln::Log::Info(mln::Event::General, "BENCHMARK MODE: Some optimizations are disabled.");
|
|
}
|
|
|
|
// Set access token if present
|
|
const char* apikeyEnv = getenv("MLN_API_KEY");
|
|
const std::string apikey = apikeyValue ? args::get(apikeyValue) : (apikeyEnv ? apikeyEnv : std::string());
|
|
|
|
auto mapTilerConfiguration = mln::TileServerOptions::MapTilerConfiguration();
|
|
mln::ResourceOptions resourceOptions;
|
|
resourceOptions.withCachePath(cacheDB).withApiKey(apikey).withTileServerOptions(mapTilerConfiguration);
|
|
mln::ClientOptions clientOptions;
|
|
auto orderedStyles = mapTilerConfiguration.defaultStyles();
|
|
|
|
GLFWView backend(fullscreen, benchmark, resourceOptions, clientOptions);
|
|
view = &backend;
|
|
|
|
std::shared_ptr<mln::FileSource> onlineFileSource = mln::FileSourceManager::get()->getFileSource(
|
|
mln::FileSourceType::Network, resourceOptions, clientOptions);
|
|
if (!settings.online) {
|
|
if (onlineFileSource) {
|
|
onlineFileSource->setProperty("online-status", false);
|
|
mln::Log::Warning(mln::Event::Setup, "Application is offline. Press `O` to toggle online status.");
|
|
} else {
|
|
mln::Log::Warning(mln::Event::Setup,
|
|
"Network resource provider is not available, only local "
|
|
"requests are supported.");
|
|
}
|
|
}
|
|
|
|
GLFWRendererFrontend rendererFrontend{
|
|
std::make_unique<mln::Renderer>(view->getRendererBackend(), view->getPixelRatio()), *view};
|
|
|
|
// Configure action journal options if directory is specified
|
|
mln::util::ActionJournalOptions actionJournalOptions;
|
|
if (actionJournalDirValue) {
|
|
const std::string actionJournalDir = args::get(actionJournalDirValue);
|
|
actionJournalOptions.enable(true).withPath(actionJournalDir);
|
|
mln::Log::Info(mln::Event::General, "Action journal enabled. Logs will be written to: " + actionJournalDir);
|
|
}
|
|
|
|
mln::Map map(rendererFrontend,
|
|
*view,
|
|
mln::MapOptions().withSize(view->getSize()).withPixelRatio(view->getPixelRatio()),
|
|
resourceOptions,
|
|
clientOptions,
|
|
actionJournalOptions);
|
|
|
|
map.setBounds(mln::BoundOptions().withMaxPitch(settings.maxPitch));
|
|
|
|
backend.setMap(&map);
|
|
|
|
if (!style.empty() && style.find("://") == std::string::npos) {
|
|
style = std::string("file://") + style;
|
|
}
|
|
|
|
map.jumpTo(mln::CameraOptions()
|
|
.withCenter(mln::LatLng{settings.latitude, settings.longitude})
|
|
.withCenterAltitude(settings.altitude)
|
|
.withZoom(settings.zoom)
|
|
.withBearing(settings.bearing)
|
|
.withPitch(settings.pitch)
|
|
.withRoll(settings.roll)
|
|
.withFov(settings.fov));
|
|
map.setDebug(mln::MapDebugOptions(settings.debug));
|
|
|
|
if (testDirValue) view->setTestDirectory(args::get(testDirValue));
|
|
|
|
view->setOnlineStatusCallback([&settings, onlineFileSource]() {
|
|
if (!onlineFileSource) {
|
|
mln::Log::Warning(mln::Event::Setup,
|
|
"Cannot change online status. Network resource provider is not "
|
|
"available.");
|
|
return;
|
|
}
|
|
settings.online = !settings.online;
|
|
onlineFileSource->setProperty("online-status", settings.online);
|
|
mln::Log::Info(mln::Event::Setup,
|
|
std::string("Application is ") + (settings.online ? "online" : "offline") +
|
|
". Press `O` to toggle online status.");
|
|
});
|
|
|
|
view->setChangeStyleCallback([&map, &orderedStyles]() {
|
|
static uint8_t currentStyleIndex;
|
|
|
|
if (++currentStyleIndex == orderedStyles.size()) {
|
|
currentStyleIndex = 0;
|
|
}
|
|
|
|
mln::util::DefaultStyle newStyle = orderedStyles[currentStyleIndex];
|
|
map.getStyle().loadURL(newStyle.getUrl());
|
|
view->setWindowTitle(newStyle.getName());
|
|
|
|
mln::Log::Info(mln::Event::Setup, "Changed style to: " + newStyle.getName());
|
|
});
|
|
|
|
// Resource loader controls top-level request processing and can resume /
|
|
// pause all managed sources simultaneously.
|
|
std::shared_ptr<mln::FileSource> resourceLoader = mln::FileSourceManager::get()->getFileSource(
|
|
mln::FileSourceType::ResourceLoader, resourceOptions, clientOptions);
|
|
view->setPauseResumeCallback([resourceLoader]() {
|
|
static bool isPaused = false;
|
|
|
|
if (isPaused) {
|
|
resourceLoader->resume();
|
|
} else {
|
|
resourceLoader->pause();
|
|
}
|
|
|
|
isPaused = !isPaused;
|
|
});
|
|
|
|
// Database file source.
|
|
auto databaseFileSource = std::static_pointer_cast<mln::DatabaseFileSource>(std::shared_ptr<mln::FileSource>(
|
|
mln::FileSourceManager::get()->getFileSource(mln::FileSourceType::Database, resourceOptions, clientOptions)));
|
|
view->setResetCacheCallback([databaseFileSource]() {
|
|
databaseFileSource->resetDatabase([](const std::exception_ptr& ex) {
|
|
if (ex) {
|
|
mln::Log::Error(mln::Event::Database, "Failed to reset cache: " + mln::util::toString(ex));
|
|
}
|
|
});
|
|
});
|
|
|
|
// Load style
|
|
if (style.empty()) {
|
|
const char* url = getenv("MLN_STYLE_URL");
|
|
if (url == nullptr) {
|
|
style = "https://tiles.openfreemap.org/styles/liberty";
|
|
view->setWindowTitle("OpenFreeMap Liberty");
|
|
} else {
|
|
style = url;
|
|
view->setWindowTitle(url);
|
|
}
|
|
}
|
|
|
|
map.getStyle().loadURL(style);
|
|
|
|
view->run();
|
|
|
|
// Save settings
|
|
mln::CameraOptions camera = map.getCameraOptions();
|
|
settings.latitude = camera.center->latitude();
|
|
settings.longitude = camera.center->longitude();
|
|
settings.altitude = *camera.centerAltitude;
|
|
settings.zoom = *camera.zoom;
|
|
settings.bearing = *camera.bearing;
|
|
settings.pitch = *camera.pitch;
|
|
settings.roll = *camera.roll;
|
|
settings.fov = *camera.fov;
|
|
settings.debug = mln::EnumType(map.getDebug());
|
|
settings.save();
|
|
mln::Log::Info(mln::Event::General,
|
|
"Exit location: --lat=\"" + std::to_string(settings.latitude) + "\" --lon=\"" +
|
|
std::to_string(settings.longitude) + "\" --alt=\"" + std::to_string(settings.altitude) +
|
|
"\" --zoom=\"" + std::to_string(settings.zoom) + "\" --bearing=\"" +
|
|
std::to_string(settings.bearing) + "\" --roll=\"" + std::to_string(settings.roll) +
|
|
"\" --fov=\"" + std::to_string(settings.fov) + "\" --maxPitch=\"" +
|
|
std::to_string(settings.maxPitch) + "\"");
|
|
|
|
view = nullptr;
|
|
|
|
return 0;
|
|
}
|