diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-02-02 22:34:56 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2020-02-04 21:43:42 +0100 |
| commit | 87318283eedef680356ef272a6af39ac3140d333 (patch) | |
| tree | de858fc84d84e0ac6eec8d121ee5f2b76b55e0ea | |
| parent | 42035701c02d15d586e762afa1db5c8b5c9eac39 (diff) | |
| download | PROJ-87318283eedef680356ef272a6af39ac3140d333.tar.gz PROJ-87318283eedef680356ef272a6af39ac3140d333.zip | |
Add projsync utility
Fixes #1750
| -rw-r--r-- | configure.ac | 1 | ||||
| -rw-r--r-- | docs/source/apps/cct.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/cs2cs.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/geod.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/gie.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/index.rst | 1 | ||||
| -rw-r--r-- | docs/source/apps/proj.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/projinfo.rst | 2 | ||||
| -rw-r--r-- | docs/source/apps/projsync.rst | 160 | ||||
| -rw-r--r-- | docs/source/conf.py | 7 | ||||
| -rw-r--r-- | docs/source/usage/network.rst | 5 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 5 | ||||
| -rwxr-xr-x | scripts/reformat_cpp.sh | 4 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | src/Makefile.am | 16 | ||||
| -rw-r--r-- | src/apps/projsync.cpp | 555 | ||||
| -rw-r--r-- | src/bin_projsync.cmake | 16 | ||||
| -rw-r--r-- | src/filemanager.cpp | 11 | ||||
| -rw-r--r-- | src/filemanager.hpp | 12 | ||||
| -rw-r--r-- | src/networkfilemanager.cpp | 12 | ||||
| -rw-r--r-- | src/proj_internal.h | 3 | ||||
| -rw-r--r-- | test/cli/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | test/cli/Makefile.am | 14 | ||||
| -rwxr-xr-x | test/cli/test_projsync | 194 |
24 files changed, 1021 insertions, 22 deletions
diff --git a/configure.ac b/configure.ac index 28e9067d..fba41302 100644 --- a/configure.ac +++ b/configure.ac @@ -321,6 +321,7 @@ fi AC_SUBST(CURL_CFLAGS,$CURL_CFLAGS) AC_SUBST(CURL_LIBS,$CURL_LIBS) AC_SUBST(CURL_ENABLED_FLAGS,$CURL_ENABLED_FLAGS) +AM_CONDITIONAL(HAVE_CURL, [test "x$FOUND_CURL" = "xyes"]) dnl --------------------------------------------------------------------------- dnl Check for external Google Test diff --git a/docs/source/apps/cct.rst b/docs/source/apps/cct.rst index 96b5f13b..a9d9e61e 100644 --- a/docs/source/apps/cct.rst +++ b/docs/source/apps/cct.rst @@ -190,7 +190,7 @@ Hence, in honour of *cct* (the geodesist) this is :program:`cct` (the program). See also ******** - **proj(1)**, **cs2cs(1)**, **geod(1)**, **gie(1)**, **projinfo(1)** + **proj(1)**, **cs2cs(1)**, **geod(1)**, **gie(1)**, **projinfo(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index c95bbaed..1a1e785d 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -230,7 +230,7 @@ outputs See also ******** - **proj(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **projinfo(1)** + **proj(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **projinfo(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/geod.rst b/docs/source/apps/geod.rst index c03dbb74..5267c184 100644 --- a/docs/source/apps/geod.rst +++ b/docs/source/apps/geod.rst @@ -200,7 +200,7 @@ Further reading See also ******** - **proj(1)**, **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)** + **proj(1)**, **cs2cs(1)**, **cct(1)**, **gie(1)**, **projinfo(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/gie.rst b/docs/source/apps/gie.rst index ac197d44..af6f528e 100644 --- a/docs/source/apps/gie.rst +++ b/docs/source/apps/gie.rst @@ -366,7 +366,7 @@ So in honour, and hopefully also in the spirit, of Gerald Ian Evenden See also ******** - **proj(1)**, **cs2cs(1)**, **cct(1)**, **geod(1)** + **proj(1)**, **cs2cs(1)**, **cct(1)**, **geod(1)**, **projinfo(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/index.rst b/docs/source/apps/index.rst index 381b8933..751be591 100644 --- a/docs/source/apps/index.rst +++ b/docs/source/apps/index.rst @@ -24,3 +24,4 @@ operations. gie proj projinfo + projsync diff --git a/docs/source/apps/proj.rst b/docs/source/apps/proj.rst index 21c2ea61..488d1b36 100644 --- a/docs/source/apps/proj.rst +++ b/docs/source/apps/proj.rst @@ -214,7 +214,7 @@ data will appear as three lines of:: See also ******** - **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **projinfo(1)** + **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **projinfo(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index 0b65b8a0..91d09941 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -416,7 +416,7 @@ Output: See also ******** - **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **proj(1)** + **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **proj(1)**, **projsync(1)** Bugs **** diff --git a/docs/source/apps/projsync.rst b/docs/source/apps/projsync.rst new file mode 100644 index 00000000..9756e0c4 --- /dev/null +++ b/docs/source/apps/projsync.rst @@ -0,0 +1,160 @@ +.. _projsync: + +================================================================================ +projsync +================================================================================ + +.. Index:: projsync + +.. only:: html + + .. versionadded:: 7.0.0 + + Tool for synchronizing PROJ datum and transformation support data. + +Synopsis +******** + + | **projsync** + | [--endpoint URL] + | [--local-geojson-file FILENAME] + | ([--user-writable-directory] | [--system-directory] | [--target-dir DIRNAME]) + | [--bbox west_long,south_lat,east_long,north_lat] + | [--spatial-test contains|intersects] + | [--source-id ID] [--area-of-use NAME] + | [--file NAME] + | [--all] [--exclude-world-coverage] + | [--quiet] [--dry-run] [--list-files] + +Description +*********** + +:program:`projsync` is a program that downloads remote resource files +into a local directory. This is an alternative to downloading a proj-data-X.Y.Z +archive file, or using the on-demand :ref:`networking capabilities <network>` of PROJ. + +The following control parameters can appear in any order: + +.. program:: projsync + +.. option:: --endpoint URL + + Defines the URL where to download the master ``files.geojson`` file and then + the resource files. Defaults to the value set in :ref:`proj-ini` + +.. option:: --local-geojson-file FILENAME + + Defines the filename for the master GeoJSON files that references resources. + Defaults to ${endpoint}/files.geojson + +.. option:: --user-writable-directory + + Specifies that resource files must be downloaded in the + :ref:`user writable directory <user_writable_directory>`. This is the default. + +.. option:: --system-directory + + Specifies that resource files must be downloaded in the + ${installation_prefix}/share/proj directory. The user launching projsync + should make sure it has writing rights in that directory. + +.. option:: --target-dir DIRNAME + + Directory into which resource files must be downloaded. + +.. option:: --bbox west_long,south_lat,east_long,north_lat + + Specify an area of interest to restrict the resources to download. + The area of interest is specified as a + bounding box with geographic coordinates, expressed in degrees in a + unspecified geographic CRS. + `west_long` and `east_long` should be in the [-180,180] range, and + `south_lat` and `north_lat` in the [-90,90]. `west_long` is generally lower than + `east_long`, except in the case where the area of interest crosses the antimeridian. + +.. option:: --spatial-test contains|intersects + + Specify how the extent of the resource files + are compared to the area of use specified explicitly with :option:`--bbox`. + By default, any resource files whose extent intersects the value specified + by :option:`--bbox` will be selected. + If using the ``contains`` strategy, only resource files whose extent is + contained in the value specified by :option:`--bbox` will be selected. + +.. option:: --source-id ID + + Restrict resource files to be downloaded to those whose source_id property + contains the ID value. Specifying ``?`` as ID will list all possible values. + +.. option:: --area-of-use NAME + + Restrict resource files to be downloaded to those whose area_of_use property + contains the NAME value. Specifying ``?`` as NAME will list all possible values. + +.. option:: --file NAME + + Restrict resource files to be downloaded to those whose name property + contains the NAME value. Specifying ``?`` as NAME will list all possible values. + +.. option:: --all + + Ask to download all files. + +.. option:: --exclude-world-coverage + + Exclude files which have world coverage. + +.. option:: -q / --quiet + + Quiet mode + +.. option:: --dry-run + + Simulate the behaviour of the tool without downloading resource files. + +.. option:: --list-files + + List file names, with the source_id and area_of_use properties. + + +At least one of :option:`--list-files`, :option:`--file`, :option:`--source-id`, +:option:`--area-of-use`, :option:`--bbox` or :option:`--all` must be specified. + +Options :option:`--file`, :option:`--source-id`, :option:`--area-of-use` and +:option:`--bbox` are combined with a AND logic. + +Examples +******** + +1. Download all resource files + +.. code-block:: console + + projsync --all + +2. Download resource files covering specified point and attributing to an agency + +.. code-block:: console + + projsync --source-id fr_ign --bbox 2,49,2,49 + + +.. only:: man + + See also + ******** + + **cs2cs(1)**, **cct(1)**, **geod(1)**, **gie(1)**, **proj(1)**, **projinfo(1)** + + Bugs + **** + + A list of know bugs can be found at https://github.com/OSGeo/PROJ/issues + where new bug reports can be submitted to. + Bugs specific to resource files should be submitted to + https://github.com/OSGeo/PROJ-data/issues + + Home page + ********* + + https://proj.org/ diff --git a/docs/source/conf.py b/docs/source/conf.py index f2fa3961..081b0a94 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -346,6 +346,13 @@ man_pages = [ ['Even Rouault'], 1 ), + ( + 'apps/projsync', + 'projsync', + u'Downloading tool of resource files', + ['Even Rouault'], + 1 + ), ] # If true, show URL addresses after external links. diff --git a/docs/source/usage/network.rst b/docs/source/usage/network.rst index 295232b0..1b42d0aa 100644 --- a/docs/source/usage/network.rst +++ b/docs/source/usage/network.rst @@ -76,6 +76,11 @@ capability to download whole grids in the :ref:`PROJ user writable directory <user_writable_directory>` by using the :cpp:func:`proj_is_download_needed` and :cpp:func:`proj_download_file` functions. +Download utility +---------------- + +:ref:`projsync` is a tool for downloading resource files. + Mirroring --------- diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index ee174da9..d624a893 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -281,6 +281,9 @@ osgeo::proj::datum::TemporalDatum::temporalOrigin() const osgeo::proj::datum::VerticalReferenceFrame::create(osgeo::proj::util::PropertyMap const&, osgeo::proj::util::optional<std::string> const&, osgeo::proj::util::optional<osgeo::proj::datum::RealizationMethod> const&) osgeo::proj::datum::VerticalReferenceFrame::realizationMethod() const osgeo::proj::datum::VerticalReferenceFrame::~VerticalReferenceFrame() +osgeo::proj::File::~File() +osgeo::proj::FileManager::open(projCtx_t*, char const*, osgeo::proj::FileAccess) +osgeo::proj::File::read_line(unsigned long, bool&, bool&) osgeo::proj::GenericShiftGrid::~GenericShiftGrid() osgeo::proj::GenericShiftGrid::GenericShiftGrid(std::string const&, int, int, osgeo::proj::ExtentAndRes const&) osgeo::proj::GenericShiftGrid::gridAt(double, double) const @@ -742,6 +745,7 @@ pj_context_get_grid_cache_filename(projCtx_t*) pj_context_get_url_endpoint(projCtx_t*) pj_context_get_user_writable_directory(projCtx_t*, bool) pj_context_is_network_enabled(projCtx_t*) +pj_context_set_user_writable_directory(projCtx_t*, std::string const&) pj_ctx_alloc pj_ctx_fclose pj_ctx_fgets @@ -774,6 +778,7 @@ pj_get_datums_ref() pj_get_def pj_get_default_ctx pj_get_default_fileapi +pj_get_default_searchpaths(projCtx_t*) pj_get_errno_ref pj_get_release pj_get_spheroid_defn diff --git a/scripts/reformat_cpp.sh b/scripts/reformat_cpp.sh index 20c32b29..89b8e4e6 100755 --- a/scripts/reformat_cpp.sh +++ b/scripts/reformat_cpp.sh @@ -16,9 +16,11 @@ esac TOPDIR="$SCRIPT_DIR/.." for i in "$TOPDIR"/include/proj/*.hpp "$TOPDIR"/include/proj/internal/*.hpp \ - "$TOPDIR"/src/iso19111/*.cpp "$TOPDIR"/test/unit/*.cpp "$TOPDIR"/src/apps/projinfo.cpp \ + "$TOPDIR"/src/iso19111/*.cpp "$TOPDIR"/test/unit/*.cpp \ + "$TOPDIR"/src/apps/projinfo.cpp "$TOPDIR"/src/apps/projsync.cpp \ "$TOPDIR"/src/tracing.cpp "$TOPDIR"/src/grids.hpp "$TOPDIR"/src/grids.cpp \ "$TOPDIR"/src/filemanager.hpp "$TOPDIR"/src/filemanager.cpp \ + "$TOPDIR"/src/networkfilemanager.cpp \ "$TOPDIR"/src/sqlite3_utils.hpp "$TOPDIR"/src/sqlite3_utils.cpp ; do if ! echo "$i" | grep -q "lru_cache.hpp"; then "$SCRIPT_DIR"/reformat.sh "$i"; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cf57306..534bc311 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,8 @@ option(BUILD_PROJ "Build proj (cartographic projection tool)" ON) option(BUILD_PROJINFO "Build projinfo (SRS and coordinate operation metadata/query tool)" ON) +option(BUILD_PROJSYNC + "Build projsync (synchronize transformation support data)" ON) if(NOT MSVC) @@ -72,6 +74,15 @@ if(BUILD_GIE) set(BIN_TARGETS ${BIN_TARGETS} gie) endif() +if(BUILD_PROJSYNC) + if(NOT ENABLE_CURL) + message(SEND_ERROR "projsync requires Curl") + endif() + include(bin_projsync.cmake) + set(BIN_TARGETS ${BIN_TARGETS} bin_projsync) +endif() + + if(MSVC OR CMAKE_CONFIGURATION_TYPES) if(BIN_TARGETS) # Add _d suffix for your debug versions of the tools diff --git a/src/Makefile.am b/src/Makefile.am index de35a754..291bc21e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,5 @@ AM_CFLAGS = @C_WFLAGS@ -bin_PROGRAMS = proj geod cs2cs gie cct projinfo EXTRA_PROGRAMS = multistresstest test228 TESTS = geodtest @@ -15,8 +14,11 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesi EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \ bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \ - lib_proj.cmake CMakeLists.txt bin_geodtest.cmake tests/geodtest.cpp \ - wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h + lib_proj.cmake CMakeLists.txt bin_geodtest.cmake \ + bin_projsync.cmake \ + tests/geodtest.cpp \ + wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h \ + apps/projsync.cpp proj_SOURCES = apps/proj.cpp apps/emess.cpp apps/utils.cpp projinfo_SOURCES = apps/projinfo.cpp @@ -24,6 +26,14 @@ cs2cs_SOURCES = apps/cs2cs.cpp apps/emess.cpp apps/utils.cpp cct_SOURCES = apps/cct.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h geod_SOURCES = apps/geod.cpp apps/geod_set.cpp apps/geod_interface.cpp apps/geod_interface.h apps/emess.cpp +if HAVE_CURL +projsync_SOURCES = apps/projsync.cpp +projsync_LDADD = libproj.la +PROJSYNC_BIN = projsync +endif + +bin_PROGRAMS = proj geod cs2cs gie cct projinfo $(PROJSYNC_BIN) + gie_SOURCES = apps/gie.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h multistresstest_SOURCES = tests/multistresstest.cpp test228_SOURCES = tests/test228.cpp diff --git a/src/apps/projsync.cpp b/src/apps/projsync.cpp new file mode 100644 index 00000000..3fab38e1 --- /dev/null +++ b/src/apps/projsync.cpp @@ -0,0 +1,555 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: Downloader tool + * Author: Even Rouault, <even.rouault at spatialys.com> + * + ****************************************************************************** + * Copyright (c) 2020, Even Rouault, <even.rouault at spatialys.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +//! @cond Doxygen_Suppress + +#define FROM_PROJ_CPP + +#include <cstdlib> +#include <iostream> +#include <limits> +#include <set> +#include <string> + +#include "filemanager.hpp" +#include "proj.h" +#include "proj_internal.h" + +#include "proj/internal/include_nlohmann_json.hpp" +#include "proj/internal/internal.hpp" + +using json = nlohmann::json; +using namespace NS_PROJ::internal; + +// --------------------------------------------------------------------------- + +namespace { +class ParsingException : public std::exception { + std::string msg_; + + public: + ParsingException(const char *msg) : msg_(msg) {} + const char *what() const noexcept override { return msg_.c_str(); } +}; +} + +// --------------------------------------------------------------------------- + +static void usage() { + std::cerr << "usage: projsync " << std::endl; + std::cerr << " [--endpoint URL]" << std::endl; + std::cerr << " [--local-geojson-file FILENAME]" << std::endl; + std::cerr << " ([--user-writable-directory] | " + "[--system-directory] | [--target-dir DIRNAME])" + << std::endl; + std::cerr << " [--bbox west_long,south_lat,east_long,north_lat]" + << std::endl; + std::cerr << " [--spatial-test contains|intersects]" << std::endl; + std::cerr << " [--source-id ID] [--area-of-use NAME]" << std::endl; + std::cerr << " [--file NAME]" << std::endl; + std::cerr << " [--all] [--exclude-world-coverage]" << std::endl; + std::cerr << " [--quiet] [--dry-run] [--list-files]" << std::endl; + std::exit(1); +} + +// --------------------------------------------------------------------------- + +static std::vector<double> get_bbox(const json &j) { + std::vector<double> res; + if (j.size() == 2 && j[0].is_number() && j[1].is_number()) { + res.push_back(j[0].get<double>()); + res.push_back(j[1].get<double>()); + res.push_back(j[0].get<double>()); + res.push_back(j[1].get<double>()); + } else { + for (const auto &obj : j) { + if (obj.is_array()) { + const auto subres = get_bbox(obj); + if (subres.size() == 4) { + if (res.empty()) { + res = subres; + } else { + res[0] = std::min(res[0], subres[0]); + res[1] = std::min(res[1], subres[1]); + res[2] = std::max(res[2], subres[2]); + res[3] = std::max(res[3], subres[3]); + } + } + } + } + } + return res; +} + +// --------------------------------------------------------------------------- + +int main(int argc, char *argv[]) { + auto ctx = pj_get_default_ctx(); + + std::string targetDir; + std::string endpoint(pj_context_get_url_endpoint(ctx)); + const std::string geojsonFile("files.geojson"); + std::string queriedSourceId; + std::string queriedAreaOfUse; + bool listFiles = false; + bool dryRun = false; + bool hasQueriedBbox = false; + double queried_west = 0.0; + double queried_south = 0.0; + double queried_east = 0.0; + double queried_north = 0.0; + bool intersects = true; + bool quiet = false; + bool includeWorldCoverage = true; + bool queryAll = false; + std::string queriedFilename; + std::string files_geojson_local; + + for (int i = 1; i < argc; i++) { + std::string arg(argv[i]); + if (arg == "--endpoint" && i + 1 < argc) { + i++; + endpoint = argv[i]; + } else if (arg == "--user-writable-directory") { + // do nothing + } else if (arg == "--system-directory") { + targetDir = PROJ_LIB; + } else if (arg == "--target-dir" && i + 1 < argc) { + i++; + targetDir = argv[i]; + } else if (arg == "--local-geojson-file" && i + 1 < argc) { + i++; + files_geojson_local = argv[i]; + } else if (arg == "--list-files") { + listFiles = true; + } else if (arg == "--source-id" && i + 1 < argc) { + i++; + queriedSourceId = argv[i]; + } else if (arg == "--area-of-use" && i + 1 < argc) { + i++; + queriedAreaOfUse = argv[i]; + } else if (arg == "--file" && i + 1 < argc) { + i++; + queriedFilename = argv[i]; + } else if (arg == "--bbox" && i + 1 < argc) { + i++; + auto bboxStr(argv[i]); + auto bbox(split(bboxStr, ',')); + if (bbox.size() != 4) { + std::cerr << "Incorrect number of values for option --bbox: " + << bboxStr << std::endl; + usage(); + } + try { + queried_west = c_locale_stod(bbox[0]); + queried_south = c_locale_stod(bbox[1]); + queried_east = c_locale_stod(bbox[2]); + queried_north = c_locale_stod(bbox[3]); + } catch (const std::exception &e) { + std::cerr << "Invalid value for option --bbox: " << bboxStr + << ", " << e.what() << std::endl; + usage(); + } + if (queried_west > 180 && queried_east > queried_west) { + queried_west -= 360; + queried_east -= 360; + } else if (queried_west < -180 && queried_east > queried_west) { + queried_west += 360; + queried_east += 360; + } else if (fabs(queried_west) < 180 && fabs(queried_east) < 180 && + queried_east < queried_west) { + queried_east += 360; + } + hasQueriedBbox = true; + } else if (arg == "--spatial-test" && i + 1 < argc) { + i++; + const std::string value(argv[i]); + if (ci_equal(value, "contains")) { + intersects = false; + } else if (ci_equal(value, "intersects")) { + intersects = true; + } else { + std::cerr << "Unrecognized value for option --spatial-test: " + << value << std::endl; + usage(); + } + } else if (arg == "--dry-run") { + dryRun = true; + } else if (arg == "--exclude-world-coverage") { + includeWorldCoverage = false; + } else if (arg == "--all") { + queryAll = true; + } else if (arg == "-q" || arg == "--quiet") { + quiet = true; + } else { + usage(); + } + } + if (!listFiles && queriedFilename.empty() && queriedSourceId.empty() && + queriedAreaOfUse.empty() && !hasQueriedBbox && !queryAll) { + std::cerr << "At least one of --list-files, --file, --source-id, " + "--area-of-use, --bbox or --all must be specified." + << std::endl + << std::endl; + usage(); + } + + if (targetDir.empty()) { + targetDir = pj_context_get_user_writable_directory(ctx, true); + } else { + if (targetDir.back() == '/') { + targetDir.resize(targetDir.size() - 1); + } + + // This is used by projsync() to determine where to write files. + pj_context_set_user_writable_directory(ctx, targetDir); + } + + if (!endpoint.empty() && endpoint.back() == '/') { + endpoint.resize(endpoint.size() - 1); + } + + if (!quiet) { + std::cout << "Downloading from " << endpoint << " into " << targetDir + << std::endl; + } + + proj_context_set_enable_network(ctx, true); + if (files_geojson_local.empty()) { + const std::string files_geojson_url(endpoint + '/' + geojsonFile); + if (!proj_download_file(ctx, files_geojson_url.c_str(), false, nullptr, + nullptr)) { + std::cerr << "Cannot download " << geojsonFile << std::endl; + std::exit(1); + } + + files_geojson_local = targetDir + '/' + geojsonFile; + } + + auto file = NS_PROJ::FileManager::open(ctx, files_geojson_local.c_str(), + NS_PROJ::FileAccess::READ_ONLY); + if (!file) { + std::cerr << "Cannot open " << files_geojson_local << std::endl; + std::exit(1); + } + + std::string text; + while (true) { + bool maxLenReached = false; + bool eofReached = false; + text += file->read_line(1000000, maxLenReached, eofReached); + if (maxLenReached) { + std::cerr << "Error while parsing " << geojsonFile + << " : too long line" << std::endl; + std::exit(1); + } + if (eofReached) + break; + } + file.reset(); + + if (listFiles) { + std::cout << "filename,source_id,area_of_use,file_size" << std::endl; + } + + try { + const auto j = json::parse(text); + bool foundMatchSourceIdCriterion = false; + std::set<std::string> source_ids; + bool foundMatchAreaOfUseCriterion = false; + std::set<std::string> areas_of_use; + bool foundMatchFileCriterion = false; + std::set<std::string> files; + if (!j.is_object() || !j.contains("features")) { + throw ParsingException("no features member"); + } + std::vector<std::string> to_download; + unsigned long long total_size_to_download = 0; + for (const auto feat : j["features"]) { + if (!feat.is_object()) { + continue; + } + if (!feat.contains("properties")) { + continue; + } + const auto properties = feat["properties"]; + if (!properties.is_object()) { + continue; + } + + if (!properties.contains("name")) { + continue; + } + const auto j_name = properties["name"]; + if (!j_name.is_string()) { + continue; + } + const auto name(j_name.get<std::string>()); + files.insert(name); + + if (!properties.contains("source_id")) { + continue; + } + const auto j_source_id = properties["source_id"]; + if (!j_source_id.is_string()) { + continue; + } + const auto source_id(j_source_id.get<std::string>()); + source_ids.insert(source_id); + + std::string area_of_use; + if (properties.contains("area_of_use")) { + const auto j_area_of_use = properties["area_of_use"]; + if (j_area_of_use.is_string()) { + area_of_use = j_area_of_use.get<std::string>(); + areas_of_use.insert(area_of_use); + } + } + + unsigned long long file_size = 0; + if (properties.contains("file_size")) { + const auto j_file_size = properties["file_size"]; + if (j_file_size.type() == json::value_t::number_unsigned) { + file_size = j_file_size.get<unsigned long long>(); + } + } + + if (listFiles) { + std::cout << name << "," << area_of_use << "," << source_id + << "," << file_size << std::endl; + continue; + } + + const bool matchSourceId = + queryAll || queriedSourceId.empty() || + source_id.find(queriedSourceId) != std::string::npos; + if (!queriedSourceId.empty() && + source_id.find(queriedSourceId) != std::string::npos) { + foundMatchSourceIdCriterion = true; + } + + const bool matchAreaOfUse = + queryAll || queriedAreaOfUse.empty() || + area_of_use.find(queriedAreaOfUse) != std::string::npos; + if (!queriedAreaOfUse.empty() && + area_of_use.find(queriedAreaOfUse) != std::string::npos) { + foundMatchAreaOfUseCriterion = true; + } + + const bool matchFile = + queryAll || queriedFilename.empty() || + name.find(queriedFilename) != std::string::npos; + if (!queriedFilename.empty() && + name.find(queriedFilename) != std::string::npos) { + foundMatchFileCriterion = true; + } + + bool matchBbox = true; + if (queryAll || hasQueriedBbox) { + matchBbox = false; + do { + if (!feat.contains("geometry")) { + if (queryAll) { + matchBbox = true; + } + break; + } + const auto j_geometry = feat["geometry"]; + if (!j_geometry.is_object()) { + if (queryAll) { + matchBbox = true; + } + break; + } + if (!j_geometry.contains("coordinates")) { + break; + } + const auto j_coordinates = j_geometry["coordinates"]; + if (!j_coordinates.is_array()) { + break; + } + if (!j_geometry.contains("type")) { + break; + } + const auto j_geometry_type = j_geometry["type"]; + if (!j_geometry_type.is_string()) { + break; + } + const auto geometry_type( + j_geometry_type.get<std::string>()); + std::vector<double> grid_bbox; + if (geometry_type == "MultiPolygon") { + std::vector<std::vector<double>> grid_bboxes; + bool foundMinus180 = false; + bool foundPlus180 = false; + for (const auto obj : j_coordinates) { + if (obj.is_array()) { + const auto tmp = get_bbox(obj); + if (tmp.size() == 4) { + if (tmp[0] == -180) + foundMinus180 = true; + else if (tmp[2] == 180) + foundPlus180 = true; + grid_bboxes.push_back(tmp); + } + } + } + for (auto &bbox : grid_bboxes) { + if (foundMinus180 && foundPlus180 && + bbox[0] == -180) { + bbox[0] = 180; + bbox[2] += 360; + } + if (grid_bbox.empty()) { + grid_bbox = bbox; + } else { + grid_bbox[0] = std::min(grid_bbox[0], bbox[0]); + grid_bbox[1] = std::min(grid_bbox[1], bbox[1]); + grid_bbox[2] = std::max(grid_bbox[2], bbox[2]); + grid_bbox[3] = std::max(grid_bbox[3], bbox[3]); + } + } + } else { + grid_bbox = get_bbox(j_coordinates); + } + if (grid_bbox.size() != 4) { + break; + } + double grid_w = grid_bbox[0]; + const double grid_s = grid_bbox[1]; + double grid_e = grid_bbox[2]; + const double grid_n = grid_bbox[3]; + if (grid_e - grid_w > 359 && grid_n - grid_s > 179) { + if (!includeWorldCoverage) { + break; + } + grid_w = -std::numeric_limits<double>::max(); + grid_e = std::numeric_limits<double>::max(); + } else if (grid_e > 180 && queried_west < -180) { + grid_w -= 360; + grid_e -= 360; + } + if (queryAll) { + matchBbox = true; + break; + } + + if (intersects) { + if (queried_west < grid_e && grid_w < queried_east && + queried_south < grid_n && grid_s < queried_north) { + matchBbox = true; + } + } else { + if (grid_w >= queried_west && grid_s >= queried_south && + grid_e <= queried_east && grid_n <= queried_north) { + matchBbox = true; + } + } + + } while (false); + } + + if (matchFile & matchSourceId && matchAreaOfUse && matchBbox) { + const std::string resource_url(endpoint + '/' + name); + if (proj_is_download_needed(ctx, resource_url.c_str(), false)) { + total_size_to_download += file_size; + to_download.push_back(resource_url); + } else { + if (!quiet) { + std::cout << resource_url << " already downloaded." + << std::endl; + } + } + } + } + + if (!quiet && total_size_to_download > 0) { + if (total_size_to_download > 1024 * 1024) + std::cout << "Total size to download: " + << total_size_to_download / (1024 * 1024) << " MB" + << std::endl; + else + std::cout << "Total to download: " << total_size_to_download + << " bytes" << std::endl; + } + for (size_t i = 0; i < to_download.size(); ++i) { + const auto &url = to_download[i]; + if (!quiet) { + if (dryRun) { + std::cout << "Would download "; + } else { + std::cout << "Downloading "; + } + std::cout << url << "... (" << i + 1 << " / " + << to_download.size() << ")" << std::endl; + } + if (!dryRun && + !proj_download_file(ctx, url.c_str(), false, nullptr, + nullptr)) { + std::cerr << "Cannot download " << url << std::endl; + std::exit(1); + } + } + + if (!queriedSourceId.empty() && !foundMatchSourceIdCriterion) { + std::cerr << "Warning: '" << queriedSourceId + << "' is a unknown value for --source-id." << std::endl; + std::cerr << "Known values are:" << std::endl; + for (const auto &v : source_ids) { + std::cerr << " " << v << std::endl; + } + std::exit(1); + } + + if (!queriedAreaOfUse.empty() && !foundMatchAreaOfUseCriterion) { + std::cerr << "Warning: '" << queriedAreaOfUse + << "' is a unknown value for --area-of-use." << std::endl; + std::cerr << "Known values are:" << std::endl; + for (const auto &v : areas_of_use) { + std::cerr << " " << v << std::endl; + } + std::exit(1); + } + + if (!queriedFilename.empty() && !foundMatchFileCriterion) { + std::cerr << "Warning: '" << queriedFilename + << "' is a unknown value for --file." << std::endl; + std::cerr << "Known values are:" << std::endl; + for (const auto &v : files) { + std::cerr << " " << v << std::endl; + } + std::exit(1); + } + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + std::exit(1); + } + + return 0; +} + +//! @endcond diff --git a/src/bin_projsync.cmake b/src/bin_projsync.cmake new file mode 100644 index 00000000..07891730 --- /dev/null +++ b/src/bin_projsync.cmake @@ -0,0 +1,16 @@ +set(PROJSYNC_SRC apps/projsync.cpp) + +source_group("Source Files\\Bin" FILES ${PROJSYNC_SRC}) + +add_executable(bin_projsync ${PROJSYNC_SRC}) +set_target_properties(bin_projsync + PROPERTIES + OUTPUT_NAME projsync) +target_link_libraries(bin_projsync ${PROJ_LIBRARIES}) +target_compile_options(bin_projsync PRIVATE ${PROJ_CXX_WARN_FLAGS}) +install(TARGETS bin_projsync + RUNTIME DESTINATION ${BINDIR}) + +if(MSVC AND BUILD_LIBPROJ_SHARED) + target_compile_definitions(bin_projsync PRIVATE PROJ_MSVC_DLL_IMPORT=1) +endif() diff --git a/src/filemanager.cpp b/src/filemanager.cpp index 17e09b21..aea9b11d 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -1178,6 +1178,8 @@ static void CreateDirectoryRecursively(PJ_CONTEXT *ctx, std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, bool create) { + if (!ctx) + ctx = pj_get_default_ctx(); if (ctx->user_writable_directory.empty()) { // For testing purposes only const char *env_var_PROJ_USER_WRITABLE_DIRECTORY = @@ -1234,6 +1236,15 @@ std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, // --------------------------------------------------------------------------- +void pj_context_set_user_writable_directory(PJ_CONTEXT *ctx, + const std::string &path) { + if (!ctx) + ctx = pj_get_default_ctx(); + ctx->user_writable_directory = path; +} + +// --------------------------------------------------------------------------- + #ifdef WIN32 static const char dir_chars[] = "/\\"; #else diff --git a/src/filemanager.hpp b/src/filemanager.hpp index bbd12b7e..9446a0bc 100644 --- a/src/filemanager.hpp +++ b/src/filemanager.hpp @@ -53,8 +53,8 @@ class FileManager { public: // "Low-level" interface. - static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename, - FileAccess access); + static PROJ_DLL std::unique_ptr<File> + open(PJ_CONTEXT *ctx, const char *filename, FileAccess access); static bool exists(PJ_CONTEXT *ctx, const char *filename); static bool mkdir(PJ_CONTEXT *ctx, const char *filename); static bool unlink(PJ_CONTEXT *ctx, const char *filename); @@ -81,14 +81,15 @@ class File { explicit File(const std::string &name); public: - virtual ~File(); + virtual PROJ_DLL ~File(); virtual size_t read(void *buffer, size_t sizeBytes) = 0; virtual size_t write(const void *buffer, size_t sizeBytes) = 0; virtual bool seek(unsigned long long offset, int whence = SEEK_SET) = 0; virtual unsigned long long tell() = 0; virtual void reassign_context(PJ_CONTEXT *ctx) = 0; virtual bool hasChanged() const = 0; - std::string read_line(size_t maxLen, bool &maxLenReached, bool &eofReached); + std::string PROJ_DLL read_line(size_t maxLen, bool &maxLenReached, + bool &eofReached); const std::string &name() const { return name_; } }; @@ -99,7 +100,8 @@ std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT *ctx, const char *filename); NS_PROJ_END -std::vector<std::string> pj_get_default_searchpaths(PJ_CONTEXT *ctx); +// Exported for projsync +std::vector<std::string> PROJ_DLL pj_get_default_searchpaths(PJ_CONTEXT *ctx); //! @endcond Doxygen_Suppress diff --git a/src/networkfilemanager.cpp b/src/networkfilemanager.cpp index c4cb41d9..d252fd84 100644 --- a/src/networkfilemanager.cpp +++ b/src/networkfilemanager.cpp @@ -193,7 +193,7 @@ class DiskChunkCache { std::unique_ptr<SQLite3VFS> vfs_{}; explicit DiskChunkCache(PJ_CONTEXT *ctx, const std::string &path); - + bool initialize(); void commitAndClose(); @@ -552,7 +552,8 @@ bool DiskChunkCache::checkConsistency() { void DiskChunkCache::commitAndClose() { if (hDB_) { - if( sqlite3_exec(hDB_, "COMMIT", nullptr, nullptr, nullptr) != SQLITE_OK ) { + if (sqlite3_exec(hDB_, "COMMIT", nullptr, nullptr, nullptr) != + SQLITE_OK) { pj_log(ctx_, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB_)); } sqlite3_close(hDB_); @@ -562,9 +563,7 @@ void DiskChunkCache::commitAndClose() { // --------------------------------------------------------------------------- -DiskChunkCache::~DiskChunkCache() { - commitAndClose(); -} +DiskChunkCache::~DiskChunkCache() { commitAndClose(); } // --------------------------------------------------------------------------- @@ -1336,7 +1335,8 @@ std::unique_ptr<File> NetworkFile::open(PJ_CONTEXT *ctx, const char *filename) { // --------------------------------------------------------------------------- -std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT* ctx, const char* filename) { +std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT *ctx, + const char *filename) { return NetworkFile::open(ctx, filename); } diff --git a/src/proj_internal.h b/src/proj_internal.h index 69d583fc..7a777027 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -870,7 +870,10 @@ void pj_load_ini(PJ_CONTEXT* ctx); // Exported for testing purposes only std::string PROJ_DLL pj_context_get_grid_cache_filename(PJ_CONTEXT *ctx); + +// For use by projsync std::string PROJ_DLL pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, bool create); +void PROJ_DLL pj_context_set_user_writable_directory(PJ_CONTEXT* ctx, const std::string& path); /* classic public API */ #include "proj_api.h" diff --git a/test/cli/CMakeLists.txt b/test/cli/CMakeLists.txt index feef5bf0..cf4192d7 100644 --- a/test/cli/CMakeLists.txt +++ b/test/cli/CMakeLists.txt @@ -5,6 +5,7 @@ set(CS2CS_BIN "cs2cs") set(PROJ_BIN "proj") set(PROJINFO_BIN "projinfo") set(CCT_BIN "cct") +set(PROJSYNC_BIN "projsync") proj_add_test_script_sh("test27" PROJ_BIN) proj_add_test_script_sh("test83" PROJ_BIN) proj_add_test_script_sh("testproj" PROJ_BIN) @@ -14,3 +15,6 @@ proj_add_test_script_sh("testIGNF" CS2CS_BIN "ntf_r93.gsb") proj_add_test_script_sh("testntv2" CS2CS_BIN "ntv2_0.gsb") proj_add_test_script_sh("testprojinfo" PROJINFO_BIN) proj_add_test_script_sh("testcct" CCT_BIN) +if(BUILD_PROJSYNC_DATA) +proj_add_test_script_sh("test_projsync" PROJSYNC_BIN) +endif() diff --git a/test/cli/Makefile.am b/test/cli/Makefile.am index 417ec137..7ff42016 100644 --- a/test/cli/Makefile.am +++ b/test/cli/Makefile.am @@ -6,6 +6,7 @@ PROJEXE = $(EXEPATH)/proj CS2CSEXE = $(EXEPATH)/cs2cs PROJINFOEXE = $(EXEPATH)/projinfo CCTEXE = $(EXEPATH)/cct +PROJSYNC_EXE = $(EXEPATH)/projsync # PROJ.4 test scripts TEST27 = $(THIS_DIR)/test27 @@ -18,6 +19,7 @@ TESTDATUMFILE = $(THIS_DIR)/testdatumfile TESTIGN = $(THIS_DIR)/testIGNF TESTPROJINFO = $(THIS_DIR)/testprojinfo TESTCCT = $(THIS_DIR)/testcct +TESTPROJSYNC = $(THIS_DIR)/test_projsync EXTRA_DIST = pj_out27.dist pj_out83.dist td_out.dist \ test27 test83 tv_out.dist tf_out.dist \ @@ -26,6 +28,7 @@ EXTRA_DIST = pj_out27.dist pj_out83.dist td_out.dist \ testprojinfo testprojinfo_out.dist \ testcct testcct_out.dist \ testproj testproj_out.dist \ + test_projsync \ CMakeLists.txt testprojinfo-check: @@ -61,4 +64,13 @@ testntv2-check: testcct-check: PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTCCT) $(CCTEXE) -check-local: testprojinfo-check test27-check test83-check testproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check + +if HAVE_CURL +testprojsync-check: + PROJ_LIB=$(PROJ_LIB) $(TESTPROJSYNC) $(PROJSYNC_EXE) +else +testprojsync-check: + echo "Skipping testprojsync-check" +endif + +check-local: testprojinfo-check test27-check test83-check testproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check testprojsync-check diff --git a/test/cli/test_projsync b/test/cli/test_projsync new file mode 100755 index 00000000..0292ceaa --- /dev/null +++ b/test/cli/test_projsync @@ -0,0 +1,194 @@ +: +# Test projsync + +TEST_CLI_DIR=`dirname $0` +EXE=$1 + +usage() +{ + echo "Usage: ${0} <path to 'projsync' program>" + echo + exit 1 +} + +if test -z "${EXE}"; then + EXE=../../src/projsync +fi + +if test ! -x ${EXE}; then + echo "*** ERROR: Can not find '${EXE}' program!" + exit 1 +fi + +echo "============================================" +echo "Running ${0} using ${EXE}:" +echo "============================================" + +if ! curl -s https://cdn.proj.org/files.geojson >/dev/null 2>/dev/null; then + if ! wget https://cdn.proj.org/files.geojson -O /dev/null 2>/dev/null; then + echo "Cannot download https://cdn.proj.org/files.geojson. Skipping test" + exit 0 + fi +fi + +TMP_OUT=test_projsync_out.txt + +rm -rf tmp_user_writable_directory +mkdir -p tmp_user_writable_directory + +echo "Testing $EXE --list-files --target-dir tmp_user_writable_directory" +if ! $EXE --list-files --target-dir tmp_user_writable_directory > ${TMP_OUT}; then + echo "--list-files failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "at_bev_README.txt,,at_bev" >/dev/null || (cat ${TMP_OUT}; exit 100) + +export PROJ_USER_WRITABLE_DIRECTORY=tmp_user_writable_directory + +if test ! -f ${PROJ_USER_WRITABLE_DIRECTORY}/cache.db; then + echo "*** ERROR: Can not find ${PROJ_USER_WRITABLE_DIRECTORY}/cache.db!" + exit 100 +fi +if test ! -f ${PROJ_USER_WRITABLE_DIRECTORY}/files.geojson; then + echo "*** ERROR: Can not find ${PROJ_USER_WRITABLE_DIRECTORY}/files.geojson!" + exit 100 +fi + +echo "Testing $EXE --all" +if ! $EXE --all --dry-run > ${TMP_OUT}; then + echo "--all failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "Would download https://cdn.proj.org/at_bev_README.txt" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --source-id fr_ign" +if ! $EXE --source-id fr_ign --dry-run > ${TMP_OUT}; then + echo "--source-id fr_ign failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "fr_ign_README.txt" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --source-id non_existing" +if $EXE --source-id non_existing >${TMP_OUT} 2>&1 ; then + echo "--source-id non_existing failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "Warning: 'non_existing' is a unknown value for --source-id" >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "fr_ign" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --area-of-use France" +if ! $EXE --area-of-use France --dry-run > ${TMP_OUT}; then + echo "--area-of-use France failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "fr_ign_ntf_r93.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --area-of-use non_existing" +if $EXE --area-of-use non_existing >${TMP_OUT} 2>&1 ; then + echo "--area-of-use non_existing failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "Warning: 'non_existing' is a unknown value for --area-of-use." >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "Australia" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --file ntf_r93" +if ! $EXE --file ntf_r93 > ${TMP_OUT}; then + echo "--file ntf_r93 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "Downloading https://cdn.proj.org/fr_ign_ntf_r93.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + +if test ! -f ${PROJ_USER_WRITABLE_DIRECTORY}/fr_ign_ntf_r93.tif; then + echo "*** ERROR: Can not find ${PROJ_USER_WRITABLE_DIRECTORY}/fr_ign_ntf_r93.tif!" + exit 100 +fi + +echo "Testing $EXE --file ntf_r93 a second time" +if ! $EXE --file ntf_r93 > ${TMP_OUT}; then + echo "--file ntf_r93 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "https://cdn.proj.org/fr_ign_ntf_r93.tif already downloaded" >/dev/null || (cat ${TMP_OUT}; exit 100) + +rm -f ${PROJ_USER_WRITABLE_DIRECTORY}/fr_ign_ntf_r93.tif + + +echo "Testing $EXE --file non_existing" +if $EXE --file non_existing >${TMP_OUT} 2>&1 ; then + echo "--file non_existing failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "Warning: 'non_existing' is a unknown value for --file." >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "fr_ign_ntf_r93.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --bbox 2,49,3,50" +if ! $EXE --bbox 2,49,3,50 --dry-run > ${TMP_OUT}; then + echo "--bbox 2,49,3,50 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "fr_ign_ntf_r93.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "us_nga_egm96_15.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --bbox 2,49,3,50 --exclude-world-coverage" +if ! $EXE --bbox 2,49,3,50 --exclude-world-coverage --dry-run > ${TMP_OUT}; then + echo "--bbox 2,49,3,50 --exclude-world-coverage failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "fr_ign_ntf_r93.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) +if cat ${TMP_OUT} | grep "us_nga_egm96_15.tif" >/dev/null; then + cat ${TMP_OUT} + exit 100 +fi + + +echo "Testing $EXE --bbox 170,-90,-170,90" +if ! $EXE --bbox 170,-90,-170,90 --dry-run > ${TMP_OUT}; then + echo "--bbox 170,-90,-170,90 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "nz_linz_nzgeoid2009.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "us_noaa_alaska.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --bbox 170,-90,190,90" +if ! $EXE --bbox 170,-90,190,90 --dry-run > ${TMP_OUT}; then + echo "--bbox 170,-90,190,90 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "nz_linz_nzgeoid2009.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "us_noaa_alaska.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + + +echo "Testing $EXE --bbox -190,-90,-170,90" +if ! $EXE --bbox -190,-90,-170,90 --dry-run > ${TMP_OUT}; then + echo "--bbox -190,-90,-170,90 failed" + cat ${TMP_OUT} + exit 100 +fi +cat ${TMP_OUT} | grep "nz_linz_nzgeoid2009.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) +cat ${TMP_OUT} | grep "us_noaa_alaska.tif" >/dev/null || (cat ${TMP_OUT}; exit 100) + +rm -rf ${PROJ_USER_WRITABLE_DIRECTORY} +rm ${TMP_OUT} + +exit 0 |
