#include #include #include #include #include #include #include #include #include using namespace std::literals::chrono_literals; int main(int argc, char *argv[]) { args::ArgumentParser argumentParser("Mapbox GL offline tool"); args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"}); args::ValueFlag tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"}); args::ValueFlag styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"}); args::ValueFlag outputValue(argumentParser, "file", "Output database file name", {'o', "output"}); args::ValueFlag northValue(argumentParser, "degrees", "North latitude", {"north"}); args::ValueFlag westValue(argumentParser, "degrees", "West longitude", {"west"}); args::ValueFlag southValue(argumentParser, "degrees", "South latitude", {"south"}); args::ValueFlag eastValue(argumentParser, "degrees", "East longitude", {"east"}); args::ValueFlag minZoomValue(argumentParser, "number", "Min zoom level", {"minZoom"}); args::ValueFlag maxZoomValue(argumentParser, "number", "Max zoom level", {"maxZoom"}); args::ValueFlag pixelRatioValue(argumentParser, "number", "Pixel ratio", {"pixelRatio"}); try { argumentParser.ParseCLI(argc, argv); } catch (args::Help) { std::cout << argumentParser; exit(0); } catch (args::ParseError e) { std::cerr << e.what() << std::endl; std::cerr << argumentParser; exit(1); } catch (args::ValidationError e) { std::cerr << e.what() << std::endl; std::cerr << argumentParser; exit(2); } std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url; // Bay area double north = northValue ? args::get(northValue) : 37.2; double west = westValue ? args::get(westValue) : -122.8; double south = southValue ? args::get(southValue) : 38.1; double east = eastValue ? args::get(eastValue) : -121.7; double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0; double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0; double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0; std::string output = outputValue ? args::get(outputValue) : "offline.db"; const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN"); std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string()); using namespace mbgl; util::RunLoop loop; DefaultFileSource fileSource(output, "."); std::unique_ptr region; fileSource.setAccessToken(token); LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); OfflineTilePyramidRegionDefinition definition(style, boundingBox, minZoom, maxZoom, pixelRatio); OfflineRegionMetadata metadata; class Observer : public OfflineRegionObserver { public: Observer(OfflineRegion& region_, DefaultFileSource& fileSource_, util::RunLoop& loop_) : region(region_), fileSource(fileSource_), loop(loop_), start(util::now()) { } void statusChanged(OfflineRegionStatus status) override { if (status.downloadState == OfflineRegionDownloadState::Inactive) { std::cout << "stopped" << std::endl; loop.stop(); return; } std::string bytesPerSecond = "-"; auto elapsedSeconds = (util::now() - start) / 1s; if (elapsedSeconds != 0) { bytesPerSecond = util::toString(status.completedResourceSize / elapsedSeconds); } std::cout << status.completedResourceCount << " / " << status.requiredResourceCount << " resources" << (status.requiredResourceCountIsPrecise ? "; " : " (indeterminate); ") << status.completedResourceSize << " bytes downloaded" << " (" << bytesPerSecond << " bytes/sec)" << std::endl; if (status.complete()) { std::cout << "Finished" << std::endl; loop.stop(); } } void responseError(Response::Error error) override { std::cerr << error.reason << " downloading resource: " << error.message << std::endl; } void mapboxTileCountLimitExceeded(uint64_t limit) override { std::cerr << "Error: reached limit of " << limit << " offline tiles" << std::endl; } OfflineRegion& region; DefaultFileSource& fileSource; util::RunLoop& loop; Timestamp start; }; static auto stop = [&] { if (region) { std::cout << "Stopping download... "; fileSource.setOfflineRegionDownloadState(*region, OfflineRegionDownloadState::Inactive); } }; std::signal(SIGINT, [] (int) { stop(); }); fileSource.createOfflineRegion(definition, metadata, [&] (std::exception_ptr error, optional region_) { if (error) { std::cerr << "Error creating region: " << util::toString(error) << std::endl; loop.stop(); exit(1); } else { assert(region_); region = std::make_unique(std::move(*region_)); fileSource.setOfflineRegionObserver(*region, std::make_unique(*region, fileSource, loop)); fileSource.setOfflineRegionDownloadState(*region, OfflineRegionDownloadState::Active); } }); loop.run(); return 0; }