diff --git a/bin/offline.cpp b/bin/offline.cpp index 603f0b848a5..2da47a44767 100644 --- a/bin/offline.cpp +++ b/bin/offline.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -11,9 +12,47 @@ #include #include #include +#include using namespace std::literals::chrono_literals; +std::string readFile(const std::string& fileName) { + std::ifstream stream(fileName.c_str()); + if (!stream.good()) { + throw std::runtime_error("Cannot read file: " + fileName); + } + + std::stringstream buffer; + buffer << stream.rdbuf(); + stream.close(); + + return buffer.str(); +} + +mapbox::geometry::geometry parseGeometry(const std::string& json) { + using namespace mapbox::geojson; + auto geojson = parse(json); + return geojson.match( + [](const geometry& geom) { + return geom; + }, + [](const feature& feature) { + return feature.geometry; + }, + [](const feature_collection& featureCollection) { + if (featureCollection.size() < 1) { + throw std::runtime_error("No features in feature collection"); + } + geometry_collection geometries; + + for (auto feature : featureCollection) { + geometries.push_back(feature.geometry); + } + + return geometries; + }); +} + int main(int argc, char *argv[]) { args::ArgumentParser argumentParser("Mapbox GL offline tool"); args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"}); @@ -22,11 +61,19 @@ int main(int argc, char *argv[]) { args::ValueFlag styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"}); args::ValueFlag outputValue(argumentParser, "file", "Output database file name", {'o', "output"}); args::ValueFlag apiBaseValue(argumentParser, "URL", "API Base URL", {'a', "apiBaseURL"}); - - 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"}); + + // LatLngBounds + args::Group latLngBoundsGroup(argumentParser, "LatLng bounds:", args::Group::Validators::AllOrNone); + args::ValueFlag northValue(latLngBoundsGroup, "degrees", "North latitude", {"north"}); + args::ValueFlag westValue(latLngBoundsGroup, "degrees", "West longitude", {"west"}); + args::ValueFlag southValue(latLngBoundsGroup, "degrees", "South latitude", {"south"}); + args::ValueFlag eastValue(latLngBoundsGroup, "degrees", "East longitude", {"east"}); + + // Geometry + args::Group geoJSONGroup(argumentParser, "GeoJson geometry:", args::Group::Validators::AllOrNone); + args::ValueFlag geometryValue(geoJSONGroup, "file", "GeoJSON file containing the region geometry", {"geojson"}); + + 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"}); @@ -48,23 +95,39 @@ int main(int argc, char *argv[]) { std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url; - // Bay area - const double north = northValue ? args::get(northValue) : 37.2; - const double west = westValue ? args::get(westValue) : -122.8; - const double south = southValue ? args::get(southValue) : 38.1; - const double east = eastValue ? args::get(eastValue) : -121.7; - const double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0; const double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0; const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0; const std::string output = outputValue ? args::get(outputValue) : "offline.db"; + + using namespace mbgl; + + OfflineRegionDefinition definition = [&]() { + if (geometryValue) { + try { + std::string json = readFile(geometryValue.Get()); + auto geometry = parseGeometry(json); + return OfflineRegionDefinition{ OfflineGeometryRegionDefinition(style, geometry, minZoom, maxZoom, pixelRatio) }; + } catch(std::runtime_error e) { + std::cerr << "Could not parse geojson file " << geometryValue.Get() << ": " << e.what() << std::endl; + exit(1); + } + } else { + // Bay area + const double north = northValue ? args::get(northValue) : 37.2; + const double west = westValue ? args::get(westValue) : -122.8; + const double south = southValue ? args::get(southValue) : 38.1; + const double east = eastValue ? args::get(eastValue) : -121.7; + LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); + return OfflineRegionDefinition{ OfflineTilePyramidRegionDefinition(style, boundingBox, minZoom, maxZoom, pixelRatio) }; + } + }(); const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN"); const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string()); const std::string apiBaseURL = apiBaseValue ? args::get(apiBaseValue) : mbgl::util::API_BASE_URL; - using namespace mbgl; util::RunLoop loop; DefaultFileSource fileSource(output, "."); @@ -73,8 +136,7 @@ int main(int argc, char *argv[]) { fileSource.setAccessToken(token); fileSource.setAPIBaseURL(apiBaseURL); - LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); - OfflineTilePyramidRegionDefinition definition(style, boundingBox, minZoom, maxZoom, pixelRatio); + OfflineRegionMetadata metadata; class Observer : public OfflineRegionObserver { diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index c1eb4bfe129..b55cedcacbc 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -81,6 +81,7 @@ macro(mbgl_platform_core) target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo) target_add_mason_package(mbgl-core PUBLIC webp) target_add_mason_package(mbgl-core PRIVATE icu) + target_add_mason_package(mbgl-core PUBLIC geojson) target_link_libraries(mbgl-core PRIVATE nunicode