diff options
30 files changed, 559 insertions, 156 deletions
diff --git a/.travis.yml b/.travis.yml index 42924cd2..4f15edd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,4 +92,5 @@ notifications: irc: channels: - "irc.freenode.org#gdal" + - "irc.freenode.org#proj" use_notice: true diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b9cff123..23be1f23 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -39,9 +39,6 @@ install(FILES # Make information about the cmake targets (the library and the tools) # available. install(EXPORT targets - FILE ${PROJECT_NAME_LOWER}-targets.cmake - DESTINATION "${CMAKECONFIGDIR}") -install(EXPORT targets NAMESPACE ${PROJECT_NAME}:: - FILE ${PROJECT_NAME_LOWER}-namespace-targets.cmake + FILE ${PROJECT_NAME_LOWER}-targets.cmake DESTINATION "${CMAKECONFIGDIR}") diff --git a/cmake/project-config.cmake.in b/cmake/project-config.cmake.in index c62ecb47..fcb0698f 100644 --- a/cmake/project-config.cmake.in +++ b/cmake/project-config.cmake.in @@ -23,7 +23,6 @@ set (@PROJECT_NAME@_BINARY_DIRS "${_ROOT}/@BINDIR@") set (@PROJECT_NAME@_LIBRARIES @PROJECT_NAME@::proj) # Read in the exported definition of the library include ("${_DIR}/@PROJECT_NAME_LOWER@-targets.cmake") -include ("${_DIR}/@PROJECT_NAME_LOWER@-namespace-targets.cmake") unset (_ROOT) unset (_DIR) diff --git a/configure.ac b/configure.ac index 2abf5684..28e9067d 100644 --- a/configure.ac +++ b/configure.ac @@ -89,7 +89,6 @@ dnl C++ specific stuff AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Wunused-private-field], [CXX_WFLAGS="$CXX_WFLAGS -Wunused-private-field"],,[$ERROR_ON_UNKNOWN_OPTIONS]) -AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CXX_WFLAGS="$CXX_WFLAGS -Wmissing-prototypes"],,[$ERROR_ON_UNKNOWN_OPTIONS]) AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CXX_WFLAGS="$CXX_WFLAGS -Wmissing-declarations"],,[$ERROR_ON_UNKNOWN_OPTIONS]) AX_CHECK_COMPILE_FLAG([-Wnon-virtual-dtor], [CXX_WFLAGS="$CXX_WFLAGS -Wnon-virtual-dtor" NO_NON_VIRTUAL_DTOR_FLAG="-Wno-non-virtual-dtor"],,[$ERROR_ON_UNKNOWN_OPTIONS]) AX_CHECK_COMPILE_FLAG([-Wold-style-cast], [WARN_OLD_STYLE_CAST="-Wold-style-cast"],,[$ERROR_ON_UNKNOWN_OPTIONS]) @@ -255,8 +254,8 @@ dnl --------------------------------------------------------------------------- dnl Check for libtiff dnl --------------------------------------------------------------------------- -AC_ARG_ENABLE([tiff-is-strongly-discouraged], - AS_HELP_STRING([--disable-tiff-is-strongly-discouraged], +AC_ARG_ENABLE([tiff], + AS_HELP_STRING([--disable-tiff], [Disable TIFF support. Strongly discouraged !]), [enable_tiff=no], [enable_tiff=yes]) @@ -284,7 +283,7 @@ CURL_CFLAGS= CURL_LIB= AC_ARG_WITH(curl, - [ --with-curl[=ARG] Enable curl support (ARG=path to curl-config.)],,,) + [ --with-curl[=ARG] Enable curl support (ARG=path to curl-config.)],,,) dnl Clear some cache variables unset ac_cv_path_LIBCURL diff --git a/data/Makefile.am b/data/Makefile.am index 564bbeed..ede26453 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -86,6 +86,7 @@ EXTRA_DIST = proj.ini GL27 nad.lst nad27 nad83 \ tests/nkgrf03vel_realigned_xy_extract.ct2 \ tests/nkgrf03vel_realigned_z_extract.gtx \ tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif \ + tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif \ null \ generate_all_sql_in.cmake sql_filelist.cmake \ $(SQL_ORDERED_LIST) diff --git a/data/tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif b/data/tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif Binary files differnew file mode 100644 index 00000000..2c01759c --- /dev/null +++ b/data/tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif diff --git a/docs/images/foss4g2020.png b/docs/images/foss4g2020.png Binary files differnew file mode 100644 index 00000000..3371bebe --- /dev/null +++ b/docs/images/foss4g2020.png diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index 47deaaeb..0b65b8a0 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -26,7 +26,8 @@ Synopsis | [--main-db-path path] [--aux-db-path path]* | [--identify] [--3d] | [--c-ify] [--single-line] - | {object_definition} | {object_reference} | (-s {srs_def} -t {srs_def}) + | --searchpaths | --remote-data | {object_definition} | + | {object_reference} | (-s {srs_def} -t {srs_def}) | where {object_definition} or {srs_def} is @@ -248,6 +249,21 @@ The following control parameters can appear in any order: Output WKT or PROJJSON strings on a single line, instead of multiple intended lines by default. +.. option:: --searchpaths + + .. versionadded:: 7.0 + + Output the directories into which PROJ resources will be looked for + (if not using C API such as :cpp:func:`proj_context_set_search_paths` + that will override them. + +.. option:: --remote-data + + .. versionadded:: 7.0 + + Display information regarding if :ref:`network` is enabled, and the + related URL. + Examples ******** diff --git a/docs/source/community/index.rst b/docs/source/community/index.rst index b0d1ce3a..0ae69d7e 100644 --- a/docs/source/community/index.rst +++ b/docs/source/community/index.rst @@ -16,3 +16,17 @@ contributor the community is always very welcoming. code_contributions code_of_conduct rfc/index + +Conference +---------- + +.. image:: ../../images/foss4g2020.png + :alt: FOSS4G 2020 + :target: https://2020.foss4g.org/ + +`FOSS4G 2020 <https://2020.foss4g.org/>`_ is the leading annual conference for +free and open source geospatial software. It will include presentations related +to PROJ, and some of the PROJ development community will be attending. It is the +event for those interested in PROJ, other FOSS geospatial technologies and the +community around them. The conference will be held in Calgary, Canada, +August 24th - August 29th, 2020. diff --git a/docs/source/community/rfc/index.rst b/docs/source/community/rfc/index.rst index a41dfe7d..ced162e2 100644 --- a/docs/source/community/rfc/index.rst +++ b/docs/source/community/rfc/index.rst @@ -15,3 +15,4 @@ the project. rfc-2 rfc-3 rfc-4 + rfc-5 diff --git a/docs/source/community/rfc/rfc-5.rst b/docs/source/community/rfc/rfc-5.rst new file mode 100644 index 00000000..cab4687f --- /dev/null +++ b/docs/source/community/rfc/rfc-5.rst @@ -0,0 +1,135 @@ +.. _rfc5: + +==================================================================== +PROJ RFC 5: Adopt GeoTIFF-based grids for grids delivered with PROJ +==================================================================== + +:Author: Even Rouault +:Contact: even.rouault@spatialys.com +:Status: Adopted +:Implementation target: PROJ 7 +:Last Updated: 2020-01-28 + +Motivation +------------------------------------------------------------------------------- + +This RFC is a continuation of :ref:`rfc4`. With RFC4, PROJ can, upon request +of the user, download grids from a CDN in a progressive way. There is also API, +such as :cpp:func:`proj_download_file` to be able to download a GeoTIFF grid in +the user writable directory. The content of the CDN at https://cdn.proj.org +is https://github.com/OSGeo/PROJ-data , which has the same content +as https://github.com/OSGeo/proj-datumgrid converted in GeoTIFF files. In the +current state, we could have a somewhat inconsistency between users relying on +the proj-datumgrid, proj-datumgrid-[world,northamerica,oceania,europe] packages +of mostly NTv2 and GTX files, and what is shipped through the CDN. Maintaining +two repositories is also a maintaince burden in the long term. + +It is thus desirable to have a single source of truth, and we propose it to be +based on the GeoTIFF grids. + +Summary of work planned by this RFC and related decisions +------------------------------------------------------------------------------- + +- https://github.com/OSGeo/PROJ-data/ will be used, starting with + PROJ 7.0, to create "static" grid packages. + +- For now, a single package of, mostly GeoTIFF grids (a few text files for + PROJ init style files, as well as a few edge cases for deformation models where + grids have not been converted), will be delivered. + Its size at the time of writing is 486 MB (compared to 1.5 GB of uncompressed + NTv2 + GTX content, compressed to ~ 700 MB currently) + +- The content of this archive will be flat, i.e. no subdirectories + +- Each file will be named according to the following pattern + ``${agency_name}_${filename}[.ext]``. For example fr_ign_ntf_r93.tif + This convention should allow packagers, if the need arise, to be able to + split the monolothic package in smaller ones, based on criterion related to + the country. + + The agency name is the one you can see from the directory names at + https://github.com/OSGeo/PROJ-data/. + ``${agency_name}`` itself is structure like ``${two_letter_country_code_of_agency_nationality}_${some_abbreviation}`` + (with the exception of eur_nkg, for the Nordic Geodetic Commission which + isn't affiliated to a single country but to some european countries, and + follows the general scheme) + +- https://github.com/OSGeo/proj-datumgrid and related packages will only be + maintained during the remaining lifetime of PROJ 6.x. After that, the + repository will no longer receive any update and will be put in archiving + state (see https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-archiving-repositories) + +- PROJ database ``grid_alternatives`` table will be updated to point to the new + TIFF filenames. It will also maintain the old names as used by current + proj-datumgrid packages to be able to provide backward compatibility when + a PROJ string refers to a grid by its previous name. + +- Upon adoption of this RFC, new grids referenced by PROJ database will only + point to GeoTIFF grid names. + +- Related to the above point, if a PROJ string refers to a grid name, let's + say foo.gsb. This grid will first be looked for in all the relevant locations + under this name. If no match is found, then a lookup in the + ``grid_alternatives`` table will be done to retrieve the potential new name + (GeoTIFF file), and if there's such match, a new look-up in the file system + will be done with the name of this GeoTIFF file. + +- The ``package_name`` column of grid_alternatives will no longer be filled. + And ``url`` will be filled with the direct URL to the grid in the CDN, for + example: https://cdn.proj.org/fr_ign_ntf_r93.tif + +- The Python scripts to convert grids (NTv2, GTX) to GeoTIFF currently available at + https://github.com/rouault/sample_proj_gtiff_grids/ will be moved to a + grid_tools/ subdirectories of https://github.com/OSGeo/PROJ-data/ + Documentation for those utilities will be added to PROJ documentation. + +- Obviously, all the above assumes PROJ builds to have libtiff enabled. + Non-libtiff builds are not considered as nominal PROJ builds (if a PROJ master + build is attempted and libtiff is not detected, it fails. The user has to + explictly ask to disable TIFF support), and users deciding to go through that + route will have to deal with the consequences (that is that + grid-based transformations generated by PROJ will likely be non working) + +Backward compatibility +------------------------------------------------------------------------------- + +This change is considered to be *mostly* backward compatible. There might be +impacts for software using :cpp:func:`proj_coordoperation_get_grid_used` and +assuming that the url returned is one of the proj-datumgrid-xxx files at +https://download.osgeo.org. As mentionned in +https://lists.osgeo.org/pipermail/proj/2020-January/009274.html , this +assumption was not completely bullet-proof either. +There will be impacts on software checking the value of PROJ pipeline strings +resulting :cpp:func:`proj_create_crs_to_crs`. The new grid names will now +be returned (the most impacted software will likely be PROJ's own test suite) + +Although discouraged, people not using the new proj-datumgrid-geotiff-XXX.zip +archives, should still be able to use the old archives made of NTv2/GTX files, +at least as long as the PROJ database does not only point to a GeoTIFF grid. +So this might be a short-term partly working solution, but at time goes, it +will become increasingly non-working. The nominal combination will be +PROJ 7.0 + proj-datumgrid-geotiff-1.0.zip + +Testing +------------------------------------------------------------------------------- + +PROJ test suite will have to be adapted for the new TIFF based filenames. + +Mechanism to auto-promote existing NTv2/GTX names to TIFF ones will be exercised. + +Proposed implementation +------------------------------------------------------------------------------- + +https://github.com/OSGeo/PROJ/pull/1891 and https://github.com/OSGeo/PROJ-data/pull/5 + +Adoption status +------------------------------------------------------------------------------- + +The RFC was adopted on 2020-01-28 with +1's from the following PSC members + +* Kristian Evers +* Even Rouault +* Thomas Knudsen +* Howard Butler +* Kurt Schwehr + diff --git a/docs/source/development/bindings.rst b/docs/source/development/bindings.rst index 8f3b44a3..26becb92 100644 --- a/docs/source/development/bindings.rst +++ b/docs/source/development/bindings.rst @@ -28,6 +28,11 @@ Go (Golang) `go-proj <https://github.com/everystreet/go-proj>`_: Go bindings and idiomatic wrapper for PROJ +Julia +===== +`Proj4.jl <https://github.com/JuliaGeo/Proj4.jl>`_" +Low-level bindings and a Julian API over PROJ. + TCL ======== `proj4tcl <http://wiki.tcl.tk/41270>`_: diff --git a/docs/source/operations/projections/cea.rst b/docs/source/operations/projections/cea.rst index 4542fe71..80fe1df3 100644 --- a/docs/source/operations/projections/cea.rst +++ b/docs/source/operations/projections/cea.rst @@ -29,3 +29,9 @@ Parameters .. include:: ../options/x_0.rst .. include:: ../options/y_0.rst + +.. note:: + + ``lat_ts`` and ``k_0`` are mutually exclusive. If ``lat_ts`` + is specified, it is equivalent to setting ``k_0`` to + :math:`\frac{\cos \phi_{ts}}{\sqrt{1 - e^2 \sin^2 \phi_{ts}}}`
\ No newline at end of file diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 07043504..ee174da9 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -739,6 +739,7 @@ pj_cleanup_lock pj_clear_initcache pj_compare_datums 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_ctx_alloc diff --git a/src/4D_api.cpp b/src/4D_api.cpp index dabd44a0..91510c35 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -1484,23 +1484,10 @@ PJ_INFO proj_info (void) { /* build search path string */ auto ctx = pj_get_default_ctx(); if (!ctx || ctx->search_paths.empty()) { - // Env var mostly for testing purposes and being independent from - // an existing installation - const char* ignoreUserWritableDirectory = - getenv("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY"); - if( ignoreUserWritableDirectory == nullptr || - ignoreUserWritableDirectory[0] == '\0' ) { - buf = path_append(buf, - pj_context_get_user_writable_directory(ctx, false).c_str(), - &buf_size); - } - const std::string envPROJ_LIB = NS_PROJ::FileManager::getProjLibEnvVar(ctx); - buf = path_append(buf, envPROJ_LIB.empty() ? nullptr : envPROJ_LIB.c_str(), &buf_size); -#ifdef PROJ_LIB - if (envPROJ_LIB.empty()) { - buf = path_append(buf, PROJ_LIB, &buf_size); + const auto searchpaths = pj_get_default_searchpaths(ctx); + for( const auto& path: searchpaths ) { + buf = path_append(buf, path.c_str(), &buf_size); } -#endif } else { for (const auto &path : ctx->search_paths) { buf = path_append(buf, path.c_str(), &buf_size); diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index 27ea278a..9cd9b2ee 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -98,8 +98,9 @@ static void usage() { << std::endl << " [--identify] [--3d]" << std::endl << " [--c-ify] [--single-line]" << std::endl - << " {object_definition} | (-s {srs_def} -t {srs_def})" - << std::endl; + << " --searchpaths | --remote-data | " + "{object_definition} |" + << " (-s {srs_def} -t {srs_def})" << std::endl; std::cerr << std::endl; std::cerr << "-o: formats is a comma separated combination of: " "all,default,PROJ,WKT_ALL,WKT2:2015,WKT2:2019,WKT1:GDAL," @@ -1057,6 +1058,34 @@ int main(int argc, char **argv) { outputOpt.strict = false; } else if (ci_equal(arg, "--3d")) { promoteTo3D = true; + } else if (ci_equal(arg, "--searchpaths")) { +#ifdef _WIN32 + constexpr char delim = ';'; +#else + constexpr char delim = ':'; +#endif + const auto paths = split(proj_info().searchpath, delim); + for (const auto &path : paths) { + std::cout << path << std::endl; + } + std::exit(0); + } else if (ci_equal(arg, "--remote-data")) { +#ifdef CURL_ENABLED + if (pj_context_is_network_enabled(nullptr)) { + std::cout << "Status: enabled" << std::endl; + std::cout << "URL: " << pj_context_get_url_endpoint(nullptr) + << std::endl; + } else { + std::cout << "Status: disabled" << std::endl; + std::cout << "Reason: not enabled in proj.ini or " + "PROJ_NETWORK=ON not specified" + << std::endl; + } +#else + std::cout << "Status: disabled" << std::endl; + std::cout << "Reason: build without Curl support" << std::endl; +#endif + std::exit(0); } else if (arg == "-?" || arg == "--help") { usage(); } else if (arg[0] == '-') { diff --git a/src/filemanager.cpp b/src/filemanager.cpp index 75476974..ac753012 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -1254,9 +1254,8 @@ static bool is_rel_or_absolute_filename(const char *name) { // --------------------------------------------------------------------------- #ifdef _WIN32 -static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, - const char *name, - std::string &out) { + +static std::string pj_get_win32_projlib() { /* Check if proj.db lieves in a share/proj dir parallel to bin/proj.dll */ /* Based in * https://stackoverflow.com/questions/9112893/how-to-get-path-to-executable-in-c-running-on-windows @@ -1272,10 +1271,10 @@ static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, DWORD last_error = GetLastError(); if (result == 0) { - return nullptr; + return std::string(); } else if (result == path_size - 1) { if (ERROR_INSUFFICIENT_BUFFER != last_error) { - return nullptr; + return std::string(); } path_size = path_size * 2; } else { @@ -1285,18 +1284,33 @@ static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, // Now remove the program's name. It was (example) // "C:\programs\gmt6\bin\gdal_translate.exe" wout.resize(wcslen(wout.c_str())); - out = NS_PROJ::WStringToUTF8(wout); + std::string out = NS_PROJ::WStringToUTF8(wout); size_t k = out.size(); while (k > 0 && out[--k] != '\\') { } out.resize(k); - out += "/../share/proj/"; + out += "/../share/proj"; + return out; +} + +// --------------------------------------------------------------------------- + +static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, + const char *name, + std::string &out) { + + out = pj_get_win32_projlib(); + if (out.empty()) { + return nullptr; + } + out += '/'; out += name; return NS_PROJ::FileManager::exists(ctx, out.c_str()) ? out.c_str() : nullptr; } + #endif /************************************************************************/ @@ -1460,6 +1474,41 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode, } /************************************************************************/ +/* pj_get_default_searchpaths() */ +/************************************************************************/ + +std::vector<std::string> pj_get_default_searchpaths(PJ_CONTEXT *ctx) { + std::vector<std::string> ret; + + // Env var mostly for testing purposes and being independent from + // an existing installation + const char *ignoreUserWritableDirectory = + getenv("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY"); + if (ignoreUserWritableDirectory == nullptr || + ignoreUserWritableDirectory[0] == '\0') { + ret.push_back(pj_context_get_user_writable_directory(ctx, false)); + } + const std::string envPROJ_LIB = NS_PROJ::FileManager::getProjLibEnvVar(ctx); + if (!envPROJ_LIB.empty()) { + ret.push_back(envPROJ_LIB); + } +#ifdef _WIN32 + if (envPROJ_LIB.empty()) { + const std::string win32Dir = pj_get_win32_projlib(); + if (!win32Dir.empty()) { + ret.push_back(win32Dir); + } + } +#endif +#ifdef PROJ_LIB + if (envPROJ_LIB.empty()) { + ret.push_back(PROJ_LIB); + } +#endif + return ret; +} + +/************************************************************************/ /* pj_open_file_with_manager() */ /************************************************************************/ @@ -1642,6 +1691,9 @@ int pj_find_file(projCtx ctx, const char *short_filename, /************************************************************************/ std::string pj_context_get_url_endpoint(PJ_CONTEXT *ctx) { + if (ctx == nullptr) { + ctx = pj_get_default_ctx(); + } if (!ctx->endpoint.empty()) { return ctx->endpoint; } diff --git a/src/filemanager.hpp b/src/filemanager.hpp index 2761b83c..bbd12b7e 100644 --- a/src/filemanager.hpp +++ b/src/filemanager.hpp @@ -29,6 +29,8 @@ #define FILEMANAGER_HPP_INCLUDED #include <memory> +#include <string> +#include <vector> #include "proj.h" #include "proj/util.hpp" @@ -95,9 +97,10 @@ class File { 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); + //! @endcond Doxygen_Suppress #endif // FILEMANAGER_HPP_INCLUDED diff --git a/src/grids.cpp b/src/grids.cpp index 68bf8600..b641f088 100644 --- a/src/grids.cpp +++ b/src/grids.cpp @@ -1403,13 +1403,28 @@ bool VerticalShiftGridSet::reopen(PJ_CONTEXT *ctx) { // --------------------------------------------------------------------------- +static bool isPointInExtent(double lon, double lat, const ExtentAndRes &extent, + double eps = 0) { + if (!(lat + eps >= extent.southLat && lat - eps <= extent.northLat)) + return false; + if (extent.fullWorldLongitude()) + return true; + if (lon + eps < extent.westLon) + lon += 2 * M_PI; + else if (lon - eps > extent.eastLon) + lon -= 2 * M_PI; + if (!(lon + eps >= extent.westLon && lon - eps <= extent.eastLon)) + return false; + return true; +} + +// --------------------------------------------------------------------------- + const VerticalShiftGrid *VerticalShiftGrid::gridAt(double lon, double lat) const { for (const auto &child : m_children) { const auto &extentChild = child->extentAndRes(); - if ((extentChild.fullWorldLongitude() || - (lon >= extentChild.westLon && lon <= extentChild.eastLon)) && - lat >= extentChild.southLat && lat <= extentChild.northLat) { + if (isPointInExtent(lon, lat, extentChild)) { return child->gridAt(lon, lat); } } @@ -1424,9 +1439,7 @@ const VerticalShiftGrid *VerticalShiftGridSet::gridAt(double lon, return grid.get(); } const auto &extent = grid->extentAndRes(); - if ((extent.fullWorldLongitude() || - (lon >= extent.westLon && lon <= extent.eastLon)) && - lat >= extent.southLat && lat <= extent.northLat) { + if (isPointInExtent(lon, lat, extent)) { return grid->gridAt(lon, lat); } } @@ -2373,11 +2386,7 @@ const HorizontalShiftGrid *HorizontalShiftGrid::gridAt(double lon, const auto &extentChild = child->extentAndRes(); const double epsilon = (extentChild.resLon + extentChild.resLat) * REL_TOLERANCE_HGRIDSHIFT; - if ((extentChild.fullWorldLongitude() || - (lon + epsilon >= extentChild.westLon && - lon - epsilon <= extentChild.eastLon)) && - lat + epsilon >= extentChild.southLat && - lat - epsilon <= extentChild.northLat) { + if (isPointInExtent(lon, lat, extentChild, epsilon)) { return child->gridAt(lon, lat); } } @@ -2394,11 +2403,7 @@ const HorizontalShiftGrid *HorizontalShiftGridSet::gridAt(double lon, const auto &extent = grid->extentAndRes(); const double epsilon = (extent.resLon + extent.resLat) * REL_TOLERANCE_HGRIDSHIFT; - if ((extent.fullWorldLongitude() || - (lon + epsilon >= extent.westLon && - lon - epsilon <= extent.eastLon)) && - lat + epsilon >= extent.southLat && - lat - epsilon <= extent.northLat) { + if (isPointInExtent(lon, lat, extent, epsilon)) { return grid->gridAt(lon, lat); } } @@ -2712,9 +2717,7 @@ bool GenericShiftGridSet::reopen(PJ_CONTEXT *ctx) { const GenericShiftGrid *GenericShiftGrid::gridAt(double lon, double lat) const { for (const auto &child : m_children) { const auto &extentChild = child->extentAndRes(); - if ((extentChild.fullWorldLongitude() || - (lon >= extentChild.westLon && lon <= extentChild.eastLon)) && - lat >= extentChild.southLat && lat <= extentChild.northLat) { + if (isPointInExtent(lon, lat, extentChild)) { return child->gridAt(lon, lat); } } @@ -2730,9 +2733,7 @@ const GenericShiftGrid *GenericShiftGridSet::gridAt(double lon, return grid.get(); } const auto &extent = grid->extentAndRes(); - if ((extent.fullWorldLongitude() || - (lon >= extent.westLon && lon <= extent.eastLon)) && - lat >= extent.southLat && lat <= extent.northLat) { + if (isPointInExtent(lon, lat, extent)) { return grid->gridAt(lon, lat); } } @@ -2948,7 +2949,13 @@ static PJ_LP pj_hgrid_apply_internal(projCtx ctx, PJ_LP in, /* normalize input to ll origin */ tb = in; const auto *extent = &(grid->extentAndRes()); + const double epsilon = + (extent->resLon + extent->resLat) * REL_TOLERANCE_HGRIDSHIFT; tb.lam -= extent->westLon; + if (tb.lam + epsilon < 0) + tb.lam += 2 * M_PI; + else if (tb.lam - epsilon > extent->eastLon - extent->westLon) + tb.lam -= 2 * M_PI; tb.phi -= extent->southLat; t = pj_hgrid_interpolate(tb, grid, true); @@ -2992,6 +2999,10 @@ static PJ_LP pj_hgrid_apply_internal(projCtx ctx, PJ_LP in, t.phi = lp.phi - extent->southLat; tb = in; tb.lam -= extent->westLon; + if (tb.lam + epsilon < 0) + tb.lam += 2 * M_PI; + else if (tb.lam - epsilon > extent->eastLon - extent->westLon) + tb.lam -= 2 * M_PI; tb.phi -= extent->southLat; dif.lam = std::numeric_limits<double>::max(); dif.phi = std::numeric_limits<double>::max(); @@ -3077,11 +3088,15 @@ PJ_LP pj_hgrid_value(PJ *P, const ListOfHGrids &grids, PJ_LP lp) { /* normalize input to ll origin */ const auto &extent = grid->extentAndRes(); + const double epsilon = + (extent.resLon + extent.resLat) * REL_TOLERANCE_HGRIDSHIFT; lp.lam -= extent.westLon; + if (lp.lam + epsilon < 0) + lp.lam += 2 * M_PI; + else if (lp.lam - epsilon > extent.eastLon - extent.westLon) + lp.lam -= 2 * M_PI; lp.phi -= extent.southLat; - lp.lam = adjlon(lp.lam - M_PI) + M_PI; - out = pj_hgrid_interpolate(lp, grid, false); if (grid->hasChanged()) { if (gridset->reopen(P->ctx)) { @@ -3119,23 +3134,43 @@ static double read_vgrid_value(PJ_CONTEXT *ctx, const ListOfVGrids &grids, } } if (!grid) { + pj_ctx_set_errno(ctx, PJD_ERR_GRID_AREA); return HUGE_VAL; } const auto &extent = grid->extentAndRes(); - /* Interpolation a location within the grid */ + /* Interpolation of a location within the grid */ double grid_x = (input.lam - extent.westLon) / extent.resLon; - if (extent.fullWorldLongitude()) { - // The first fmod goes to ]-lim, lim[ range - // So we add lim again to be in ]0, 2*lim[ and fmod again - grid_x = - fmod(fmod(grid_x + grid->width(), grid->width()) + grid->width(), - grid->width()); + if (input.lam < extent.westLon) { + if (extent.fullWorldLongitude()) { + // The first fmod goes to ]-lim, lim[ range + // So we add lim again to be in ]0, 2*lim[ and fmod again + grid_x = fmod(fmod(grid_x + grid->width(), grid->width()) + + grid->width(), + grid->width()); + } else { + grid_x = (input.lam + 2 * M_PI - extent.westLon) / extent.resLon; + } + } else if (input.lam > extent.eastLon) { + if (extent.fullWorldLongitude()) { + // The first fmod goes to ]-lim, lim[ range + // So we add lim again to be in ]0, 2*lim[ and fmod again + grid_x = fmod(fmod(grid_x + grid->width(), grid->width()) + + grid->width(), + grid->width()); + } else { + grid_x = (input.lam - 2 * M_PI - extent.westLon) / extent.resLon; + } } double grid_y = (input.phi - extent.southLat) / extent.resLat; int grid_ix = static_cast<int>(lround(floor(grid_x))); - assert(grid_ix >= 0 && grid_ix < grid->width()); + if (!(grid_ix >= 0 && grid_ix < grid->width())) { + // in the unlikely case we end up here... + pj_log(ctx, PJ_LOG_ERROR, "grid_ix not in grid"); + pj_ctx_set_errno(ctx, PJD_ERR_GRID_AREA); + return HUGE_VAL; + } int grid_iy = static_cast<int>(lround(floor(grid_y))); assert(grid_iy >= 0 && grid_iy < grid->height()); grid_x -= grid_ix; @@ -3304,6 +3339,11 @@ bool pj_bilinear_interpolation_three_samples(const GenericShiftGrid *grid, const auto &extent = grid->extentAndRes(); double grid_x = (lp.lam - extent.westLon) / extent.resLon; + if (lp.lam < extent.westLon) { + grid_x = (lp.lam + 2 * M_PI - extent.westLon) / extent.resLon; + } else if (lp.lam > extent.eastLon) { + grid_x = (lp.lam - 2 * M_PI - extent.westLon) / extent.resLon; + } double grid_y = (lp.phi - extent.southLat) / extent.resLat; int ix = static_cast<int>(grid_x); int iy = static_cast<int>(grid_y); diff --git a/src/init.cpp b/src/init.cpp index ba3d86e4..9c7b7b19 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -49,25 +49,23 @@ static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) { Convert a string (presumably originating from get_init_string) to a paralist. ***************************************************************************************/ const char *c = definition; - paralist *first = nullptr, *next = nullptr; + paralist *first = nullptr, *last = nullptr; while (*c) { /* Keep a handle to the start of the list, so we have something to return */ - if (nullptr==first) - first = next = pj_mkparam_ws (c, &c); - else - next = next->next = pj_mkparam_ws (c, &c); - if (nullptr==next) { + auto param = pj_mkparam_ws (c, &c); + if (nullptr==param) { pj_dealloc_params (ctx, first, ENOMEM); return nullptr; } + if (nullptr==last) { + first = param; + } + else { + last->next = param; + } + last = param; } - - if( next == nullptr ) - return nullptr; - - /* Terminate list and return */ - next->next = nullptr; return first; } diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 6d97adc3..8ca95223 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -6238,7 +6238,7 @@ void Conversion::_exportToPROJString( if (!param->proj_name) { continue; } - const auto value = + const auto &value = parameterValueMeasure(param->wkt2_name, param->epsg_code); double valueConverted = 0; if (value == nullMeasure) { diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index d88581b4..e4b159bd 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -3526,7 +3526,7 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); - } else if (paramMapping) { + } else if (mapping && paramMapping) { for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { if (mapping->params[idx] == paramMapping) { foundParameters[idx] = true; @@ -4542,7 +4542,7 @@ class JSONParser { static Measure getMeasure(const json &j); IdentifierNNPtr buildId(const json &j, bool removeInverseOf); - ObjectDomainPtr buildObjectDomain(const json &j); + static ObjectDomainPtr buildObjectDomain(const json &j); PropertyMap buildProperties(const json &j, bool removeInverseOf = false); GeographicCRSNNPtr buildGeographicCRS(const json &j); @@ -8736,22 +8736,43 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( } else if (param->unit_type == UnitOfMeasure::Type::SCALE) { value = 1; - } else { - // For omerc, if gamma is missing, the default value is - // alpha - if (step.name == "omerc" && proj_name == "gamma") { - paramValue = &getParamValue(step, "alpha"); - if (!paramValue->empty()) { - value = getAngularValue(*paramValue); + } + // For omerc, if gamma is missing, the default value is + // alpha + else if (step.name == "omerc" && proj_name == "gamma") { + paramValue = &getParamValue(step, "alpha"); + if (!paramValue->empty()) { + value = getAngularValue(*paramValue); + } + } else if (step.name == "krovak") { + if (param->epsg_code == + EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS) { + value = 30.28813975277777776; + } else if ( + param->epsg_code == + EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL) { + value = 78.5; + } + } else if (step.name == "cea" && proj_name == "lat_ts") { + paramValue = &getParamValueK(step); + if (!paramValue->empty()) { + bool hasError = false; + const double k = getNumericValue(*paramValue, &hasError); + if (hasError) { + throw ParsingException("invalid value for k/k_0"); } - } else if (step.name == "krovak") { - if (param->epsg_code == - EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS) { - value = 30.28813975277777776; - } else if ( - param->epsg_code == - EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL) { - value = 78.5; + if (k >= 0 && k <= 1) { + const double es = + geogCRS->ellipsoid()->squaredEccentricity(); + if (es < 0) { + throw ParsingException("Invalid flattening"); + } + value = + Angle(acos(k * sqrt((1 - es) / (1 - k * k * es))), + UnitOfMeasure::RADIAN) + .convertToUnit(UnitOfMeasure::DEGREE); + } else { + throw ParsingException("k/k_0 should be in [0,1]"); } } } diff --git a/src/proj_internal.h b/src/proj_internal.h index a587c037..69d583fc 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -863,7 +863,8 @@ PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv); // For use by projinfo bool PROJ_DLL pj_context_is_network_enabled(PJ_CONTEXT* ctx); -std::string pj_context_get_url_endpoint(PJ_CONTEXT* ctx); +// For use by projinfo +std::string PROJ_DLL pj_context_get_url_endpoint(PJ_CONTEXT* ctx); void pj_load_ini(PJ_CONTEXT* ctx); diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo index b9c452fb..4ec1bdbb 100755 --- a/test/cli/testprojinfo +++ b/test/cli/testprojinfo @@ -175,6 +175,32 @@ echo 'Testing -k operation EPSG:8457 -o PROJ -q' >> ${OUT} $EXE -k operation EPSG:8457 -o PROJ -q >>${OUT} 2>&1 echo "" >>${OUT} +# Testing --searchpaths +if ! $EXE --searchpaths > testprojinfo_out_searchpaths.txt; then + echo "--searchpaths failed" + exit 100 +fi +# Hard to test content of testprojinfo_out_searchpaths.txt +rm testprojinfo_out_searchpaths.txt + +# Testing --remote-data +if ! $EXE --remote-data > testprojinfo_out_remotedata.txt; then + echo "--remote-data failed" + exit 100 +fi +# Hard to test content of testprojinfo_out_remotedata.txt +rm testprojinfo_out_remotedata.txt + +# Testing --remote-data +export PROJ_NETWORK=ON +if ! $EXE --remote-data > testprojinfo_out_remotedata.txt; then + echo "--remote-data failed" + exit 100 +fi +# Hard to test content of testprojinfo_out_remotedata.txt +rm testprojinfo_out_remotedata.txt +unset PROJ_NETWORK + # do 'diff' with distribution results echo "diff ${OUT} with testprojinfo_out.dist" diff -u ${OUT} ${TEST_CLI_DIR}/testprojinfo_out.dist diff --git a/test/fuzzers/build.sh b/test/fuzzers/build.sh new file mode 100755 index 00000000..c971a739 --- /dev/null +++ b/test/fuzzers/build.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# This script is meant to be run by +# https://github.com/google/oss-fuzz/blob/master/projects/proj.4/Dockerfile + +set -e + +if [ "$SRC" == "" ]; then + echo "SRC env var not defined" + exit 1 +fi + +if [ "$OUT" == "" ]; then + echo "OUT env var not defined" + exit 1 +fi + +if [ "$CXX" == "" ]; then + echo "CXX env var not defined" + exit 1 +fi + +if [ "$LIB_FUZZING_ENGINE" = "" ]; then + export LIB_FUZZING_ENGINE=-lFuzzingEngine +fi + +I386_PACKAGES="zlib1g-dev:i386 libssl-dev:i386 libsqlite3-dev:i386 \ + libtiff5-dev:i386" +X64_PACKAGES="zlib1g-dev libssl-dev libsqlite3-dev \ + libtiff5-dev" + +if [ "$ARCHITECTURE" = "i386" ]; then + apt-get install -y $I386_PACKAGES +else + apt-get install -y $X64_PACKAGES +fi + +# build libcurl.a (builing against Ubuntu libcurl.a doesn't work easily) +cd curl +./buildconf +./configure --disable-shared --prefix=$SRC/install +make clean -s +make -j$(nproc) -s +make install +cd .. + +./autogen.sh +SQLITE3_CFLAGS=-I/usr/include SQLITE3_LIBS=-lsqlite3 TIFF_CFLAGS=-I/usr/include TIFF_LIBS=-ltiff ./configure --disable-shared --with-curl=$SRC/install/bin/curl-config +make clean -s +make -j$(nproc) -s + +EXTRA_LIBS="-lpthread -lsqlite3 -ltiff -Wl,-Bstatic -L$SRC/install/lib -lcurl -lssl -lcrypto -lz -Wl,-Bdynamic" + +build_fuzzer() +{ + fuzzerName=$1 + sourceFilename=$2 + shift + shift + echo "Building fuzzer $fuzzerName" + $CXX $CXXFLAGS -std=c++11 -fvisibility=hidden -Isrc -Iinclude \ + $sourceFilename $* -o $OUT/$fuzzerName \ + $LIB_FUZZING_ENGINE src/.libs/libproj.a $EXTRA_LIBS +} + +build_fuzzer standard_fuzzer test/fuzzers/standard_fuzzer.cpp +build_fuzzer proj_crs_to_crs_fuzzer test/fuzzers/proj_crs_to_crs_fuzzer.cpp + +echo "[libfuzzer]" > $OUT/standard_fuzzer.options +echo "max_len = 10000" >> $OUT/standard_fuzzer.options + +echo "[libfuzzer]" > $OUT/proj_crs_to_crs_fuzzer.options +echo "max_len = 10000" >> $OUT/proj_crs_to_crs_fuzzer.options + +cp -r data/* $OUT diff --git a/test/fuzzers/build_google_oss_fuzzers.sh b/test/fuzzers/build_google_oss_fuzzers.sh deleted file mode 100755 index cd9fed49..00000000 --- a/test/fuzzers/build_google_oss_fuzzers.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -set -e - -if [ "$SRC" == "" ]; then - echo "SRC env var not defined" - exit 1 -fi - -if [ "$OUT" == "" ]; then - echo "OUT env var not defined" - exit 1 -fi - -if [ "$CXX" == "" ]; then - echo "CXX env var not defined" - exit 1 -fi - -SRC_DIR=$(dirname $0)/../.. - -build_fuzzer() -{ - fuzzerName=$1 - sourceFilename=$2 - shift - shift - echo "Building fuzzer $fuzzerName" - $CXX $CXXFLAGS -std=c++11 -fvisibility=hidden -I$SRC_DIR/src -I$SRC_DIR/include \ - $sourceFilename $* -o $OUT/$fuzzerName \ - -lFuzzingEngine $SRC_DIR/src/.libs/libproj.a -lpthread /usr/lib/x86_64-linux-gnu/libsqlite3.a $EXTRA_LIBS -} - -build_fuzzer standard_fuzzer $(dirname $0)/standard_fuzzer.cpp -build_fuzzer proj_crs_to_crs_fuzzer $(dirname $0)/proj_crs_to_crs_fuzzer.cpp - -echo "[libfuzzer]" > $OUT/standard_fuzzer.options -echo "max_len = 10000" >> $OUT/standard_fuzzer.options - -echo "[libfuzzer]" > $OUT/proj_crs_to_crs_fuzzer.options -echo "max_len = 10000" >> $OUT/proj_crs_to_crs_fuzzer.options diff --git a/test/fuzzers/build_seed_corpus.sh b/test/fuzzers/build_seed_corpus.sh deleted file mode 100755 index cdd3cfc3..00000000 --- a/test/fuzzers/build_seed_corpus.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e - -if [ "$OUT" == "" ]; then - echo "OUT env var not defined" - exit 1 -fi - -SRC_DIR=$(dirname $0)/../.. - -cp -r $SRC_DIR/data/* $OUT diff --git a/test/fuzzers/proj_crs_to_crs_fuzzer.cpp b/test/fuzzers/proj_crs_to_crs_fuzzer.cpp index 9b4b1ed9..a23cc0d9 100644 --- a/test/fuzzers/proj_crs_to_crs_fuzzer.cpp +++ b/test/fuzzers/proj_crs_to_crs_fuzzer.cpp @@ -34,7 +34,6 @@ #include <sys/types.h> #include <unistd.h> -#include "proj_internal.h" // For pj_gc_unloadall() #include "proj.h" /* Standalone build: @@ -47,7 +46,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len); int LLVMFuzzerInitialize(int* /*argc*/, char*** argv) { const char* argv0 = (*argv)[0]; - char* path = pj_strdup(argv0); + char* path = strdup(argv0); char* lastslash = strrchr(path, '/'); if( lastslash ) { @@ -96,8 +95,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) proj_create_crs_to_crs(nullptr, first_line, second_line, nullptr)); free(buf_dup); - pj_gc_unloadall(pj_get_default_ctx()); - pj_deallocate_grids(); + proj_cleanup(); return 0; } diff --git a/test/gie/geotiff_grids.gie b/test/gie/geotiff_grids.gie index 62a5b16d..a0d2a05b 100644 --- a/test/gie/geotiff_grids.gie +++ b/test/gie/geotiff_grids.gie @@ -160,7 +160,40 @@ operation +proj=vgridshift +grids=tests/test_vgrid_unsupported_byte.tif +multi expect failure errno failed_to_load_grid ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif +multiplier=1 +------------------------------------------------------------------------------- +tolerance 1 mm + +accept 179.99 54.5 0 +expect 179.99 54.5 -2.2226 + +accept -179.99 54.5 0 +expect -179.99 54.5 -2.3488 + +accept 179.999999 54.5 0 +expect 179.999999 54.5 -2.2872 + +accept -179.999999 54.5 0 +expect -179.999999 54.5 -2.2872 + +accept 179.8 54.5 0 +expect 179.8 54.5 -0.7011 + +accept 179.799 54.5 0 +expect failure errno grid_area + +accept 180.1833333 54.5 0 +expect -179.8166667 54.5 -3.1933 + +accept -179.8166667 54.5 0 +expect -179.8166667 54.5 -3.1933 + +accept 180.184 54.5 0 +expect failure errno grid_area +accept -179.816 54.5 0 +expect failure errno grid_area ------------------------------------------------------------------------------- operation +proj=hgridshift +grids=tests/test_hgrid.tif diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index fd38847c..cc780be1 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -7968,6 +7968,24 @@ TEST(io, projparse_cea_ellipsoidal) { // --------------------------------------------------------------------------- +TEST(io, projparse_cea_ellipsoidal_with_k_0) { + auto obj = PROJStringParser().createFromPROJString( + "+proj=cea +ellps=GRS80 +k_0=0.99 +type=crs"); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + WKTFormatterNNPtr f(WKTFormatter::create()); + f->simulCurNodeHasId(); + f->setMultiLine(false); + crs->exportToWKT(f.get()); + auto wkt = f->toString(); + EXPECT_TRUE( + wkt.find("PARAMETER[\"Latitude of 1st standard parallel\",8.1365") != + std::string::npos) + << wkt; +} + +// --------------------------------------------------------------------------- + TEST(io, projparse_geos_sweep_x) { auto obj = PROJStringParser().createFromPROJString( "+proj=geos +sweep=x +h=1 +type=crs"); |
