diff --git a/CHANGELOG.md b/CHANGELOG.md index e69520a4a..c5e0417c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - GDAL 3.8.0 with new `JSONFG`, `PMTiles` and `S102` drivers + - PROJ 9.3.0 - GEOS 3.12.1 - HDF5 1.14.3 - NetCDF 4.9.2 diff --git a/deps/libproj.sh b/deps/libproj.sh index def0cf192..5dcdc65b9 100755 --- a/deps/libproj.sh +++ b/deps/libproj.sh @@ -5,7 +5,7 @@ set -eu DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$DIR/libproj" -PROJ_VERSION=9.0.0 +PROJ_VERSION=9.3.0 dir_proj=./proj # diff --git a/deps/libproj/proj/CITATION b/deps/libproj/proj/CITATION index 0626644a8..9f840f738 100644 --- a/deps/libproj/proj/CITATION +++ b/deps/libproj/proj/CITATION @@ -1,6 +1,6 @@ To cite PROJ in publications use: - PROJ contributors (2020). PROJ coordinate transformation software + PROJ contributors (2023). PROJ coordinate transformation software library. Open Source Geospatial Foundation. URL https://proj.org/. DOI: 10.5281/zenodo.5884394 @@ -12,7 +12,7 @@ A BibTeX entry for LaTeX users is title = {{PROJ} coordinate transformation software library}, author = {{PROJ contributors}}, organization = {Open Source Geospatial Foundation}, - year = {2022}, + year = {2023}, url = {https://proj.org/}, doi = {10.5281/zenodo.5884394}, } diff --git a/deps/libproj/proj/CITATION.cff b/deps/libproj/proj/CITATION.cff new file mode 100644 index 000000000..36fbb3473 --- /dev/null +++ b/deps/libproj/proj/CITATION.cff @@ -0,0 +1,70 @@ +cff-version: 1.2.0 +message: Please cite this software using these metadata or in the CITATION file. +type: software +title: PROJ +version: 9.2.0 +date-released: 2023-03-01 +doi: 10.5281/zenodo.5884394 +abstract: PROJ is a generic coordinate transformation software that transforms + geospatial coordinates from one coordinate reference system (CRS) to another. + This includes cartographic projections as well as geodetic transformations. +url: https://proj.org +repository-code: https://github.com/OSGeo/PROJ/ +license: MIT +authors: + - given-names: Gerald I. + family-names: Evenden + affiliation: United States Geological Survey + - given-names: Even + family-names: Rouault + affiliation: Spatialys + orcid: https://orcid.org/0000-0002-5068-0476 + - given-names: Frank + family-names: Warmerdam + affiliation: Planet Labs Inc + - given-names: Kristian + family-names: Evers + affiliation: Danish Agency for Data Supply and Infrastructure + orcid: https://orcid.org/0000-0002-1310-4576 + - given-names: Thomas + family-names: Knudsen + affiliation: Danish Agency for Data Supply and Infrastructure + - given-names: Howard + family-names: Butler + affiliation: Hobu Inc + orcid: https://orcid.org/0000-0002-5340-1380 + - given-names: Mike W. + family-names: Taves + affiliation: GNS Science + orcid: https://orcid.org/0000-0003-3657-7963 + - given-names: Kurt + family-names: Schwehr + affiliation: Google Inc + orcid: https://orcid.org/0000-0002-5624-8190 + - given-names: Elliott + family-names: Sales de Andrade + orcid: https://orcid.org/0000-0001-7310-8942 + - given-names: Charles + family-names: Karney + affiliation: SRI International + orcid: https://orcid.org/0000-0002-5006-5836 + - given-names: Sebastiaan + family-names: Couwenberg + - given-names: Nyall + family-names: Dawson + affiliation: North Road + orcid: https://orcid.org/0000-0001-9812-7584 + - given-names: Alan D. + family-names: Snow + affiliation: Corteva + orcid: https://orcid.org/0000-0002-7333-3100 + - given-names: Javier + family-names: Jimenez Shaw + orcid: https://orcid.org/0000-0002-7227-9173 +keywords: + - PROJ + - geodesy + - cartography + - coordinates + - projections + - conversion diff --git a/deps/libproj/proj/CMakeLists.txt b/deps/libproj/proj/CMakeLists.txt index 9076f955c..2c2b54d7b 100644 --- a/deps/libproj/proj/CMakeLists.txt +++ b/deps/libproj/proj/CMakeLists.txt @@ -73,6 +73,11 @@ elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") /wd4706 # Suppress warning about assignment within conditional expression /wd4996 # Suppress warning about sprintf, etc., being unsafe ) + if("$ENV{VSCMD_ARG_TGT_ARCH}" STREQUAL "arm64") + # Suppress an inaccurate warning when compiling for an MSVC/ARM64 platform + # It incorrectly assumes a division by zero is possible despite a check + set(PROJ_C_WARN_FLAGS ${PROJ_C_WARN_FLAGS} /wd4723) + endif() set(PROJ_CXX_WARN_FLAGS /EHsc ${PROJ_C_WARN_FLAGS}) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") if(MSVC) @@ -110,7 +115,7 @@ message(STATUS "Configuring PROJ:") #PROJ version information ################################################################################ include(ProjVersion) -proj_version(MAJOR 9 MINOR 0 PATCH 0) +proj_version(MAJOR 9 MINOR 3 PATCH 0) set(PROJ_SOVERSION 25) set(PROJ_BUILD_VERSION "${PROJ_SOVERSION}.${PROJ_VERSION}") @@ -213,6 +218,16 @@ if(ENABLE_CURL) find_package(CURL REQUIRED) if(CURL_FOUND) set(CURL_ENABLED TRUE) + + # Target CURL::libcurl only defined since CMake 3.12 + # TODO: remove this when CMake >= 3.12 required + if(NOT TARGET CURL::libcurl) + add_library(CURL::libcurl INTERFACE IMPORTED) + set_target_properties(CURL::libcurl PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${CURL_LIBRARIES}" + ) + endif() else() message(SEND_ERROR "curl dependency not found!") endif() @@ -220,9 +235,14 @@ endif() ################################################################################ -option(PROJ_LIB_ENV_VAR_TRIED_LAST "Whether the PROJ_LIB environment variable should be tried after the hardcoded location" OFF) -if(PROJ_LIB_ENV_VAR_TRIED_LAST) - add_definitions(-DPROJ_LIB_ENV_VAR_TRIED_LAST) +if(DEFINED PROJ_LIB_ENV_VAR_TRIED_LAST) + set(PROJ_DATA_ENV_VAR_TRIED_LAST ${PROJ_LIB_ENV_VAR_TRIED_LAST}) + message(WARNING "PROJ_LIB_ENV_VAR_TRIED_LAST option has been renamed to PROJ_DATA_ENV_VAR_TRIED_LAST. PROJ_LIB_ENV_VAR_TRIED_LAST is still working for now, but may be completely replaced by PROJ_DATA_ENV_VAR_TRIED_LAST in a future release") +else() + option(PROJ_DATA_ENV_VAR_TRIED_LAST "Whether the PROJ_DATA environment variable should be tried after the hardcoded location" OFF) +endif() +if(PROJ_DATA_ENV_VAR_TRIED_LAST) + add_definitions(-DPROJ_DATA_ENV_VAR_TRIED_LAST) endif() ################################################################################ @@ -230,6 +250,10 @@ endif() ################################################################################ set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads) +if(Threads_FOUND AND CMAKE_USE_PTHREADS_INIT) + set(CMAKE_REQUIRED_LIBRARIES + "${CMAKE_REQUIRED_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}") +endif() include(CheckCSourceCompiles) if(MSVC) @@ -239,23 +263,6 @@ else() set(CMAKE_REQUIRED_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall") endif() -if(Threads_FOUND AND CMAKE_USE_PTHREADS_INIT) - set(CMAKE_REQUIRED_LIBRARIES - "${CMAKE_REQUIRED_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}") - check_c_source_compiles(" -#include - -int main(int argc, char* argv[]) { - (void)PTHREAD_MUTEX_RECURSIVE; - (void)argv; - return argc; -} - " HAVE_PTHREAD_MUTEX_RECURSIVE_DEFN) - if(HAVE_PTHREAD_MUTEX_RECURSIVE_DEFN) - add_definitions(-DHAVE_PTHREAD_MUTEX_RECURSIVE=1) - endif() -endif() - # Set a default build type for single-configuration cmake generators if # no build type is set. if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) @@ -281,18 +288,21 @@ link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) # Installation ################################################################################ include(ProjInstallPath) -set(BINDIR "${DEFAULT_BINDIR}" - CACHE PATH "The directory to install binaries into.") -set(LIBDIR "${DEFAULT_LIBDIR}" - CACHE PATH "The directory to install libraries into.") -set(DATADIR "${DEFAULT_DATADIR}" - CACHE PATH "The directory to install data files into.") -set(DOCDIR "${DEFAULT_DOCDIR}" - CACHE PATH "The directory to install doc files into.") -set(INCLUDEDIR "${DEFAULT_INCLUDEDIR}" - CACHE PATH "The directory to install includes into.") -set(CMAKECONFIGDIR "${DEFAULT_CMAKEDIR}" - CACHE PATH "Parent of the directory to install cmake config files into.") + +# By default GNUInstallDirs will use the upper case project name +# for CMAKE_INSTALL_DOCDIR, resulting in something like share/doc/PROJ +# instead of share/doc/proj which historically have been the path used +# by the project. +# Here force the use of a lower case project name and reset after +# GNUInstallDirs has done its thing +set(PROJECT_NAME_ORIGINAL "${PROJECT_NAME}") +string(TOLOWER "${PROJECT_NAME}" PROJECT_NAME) + +include(GNUInstallDirs) + +set(PROJECT_NAME "${PROJECT_NAME_ORIGINAL}") + +set(PROJ_DATA_PATH "${CMAKE_INSTALL_FULL_DATADIR}/proj") ################################################################################ # Tests @@ -319,9 +329,14 @@ if(BUILD_TESTING) add_subdirectory(test) endif() +option(BUILD_EXAMPLES "Whether to build example programs" OFF) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + set(docfiles COPYING NEWS AUTHORS) install(FILES ${docfiles} - DESTINATION "${DOCDIR}") + DESTINATION ${CMAKE_INSTALL_DOCDIR}) ################################################################################ # pkg-config support @@ -330,7 +345,7 @@ configure_proj_pc() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/proj.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") ################################################################################ # "make dist" workalike @@ -354,10 +369,10 @@ set(CPACK_SOURCE_IGNORE_FILES /autom4te\\.cache /CODE_OF_CONDUCT.md /CONTRIBUTING.md + /disabled_workflows/ /Dockerfile /docs/ /Doxyfile - /examples/ /HOWTO-RELEASE /m4/lt* /m4/libtool* @@ -388,3 +403,6 @@ if(NOT _is_multi_config_generator) ) message(STATUS "PROJ: Configured 'dist' target") endif() + +configure_file(cmake/uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/proj_uninstall.cmake @ONLY) +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/proj_uninstall.cmake) diff --git a/deps/libproj/proj/README.md b/deps/libproj/proj/README.md index 46b4a9dbe..ae6486e82 100644 --- a/deps/libproj/proj/README.md +++ b/deps/libproj/proj/README.md @@ -1,7 +1,6 @@ # PROJ -[![Travis Status](https://travis-ci.com/OSGeo/PROJ.svg?branch=master)](https://travis-ci.com/OSGeo/PROJ) -[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/OSGeo/PROJ?branch=master&svg=true)](https://ci.appveyor.com/project/OSGeo/PROJ?branch=master) +[![Travis Status](https://app.travis-ci.com/OSGeo/PROJ.svg?branch=master)](https://app.travis-ci.com/github/OSGeo/PROJ) [![Docker build Status](https://img.shields.io/docker/cloud/build/osgeo/proj)](https://hub.docker.com/r/osgeo/proj/builds) [![Coveralls Status](https://coveralls.io/repos/github/OSGeo/PROJ/badge.svg?branch=master)](https://coveralls.io/github/OSGeo/PROJ?branch=master) [![Gitter](https://badges.gitter.im/OSGeo/proj.4.svg)](https://gitter.im/OSGeo/proj.4) diff --git a/deps/libproj/proj/cmake/CMakeLists.txt b/deps/libproj/proj/cmake/CMakeLists.txt index c790fa4a8..191fd074b 100644 --- a/deps/libproj/proj/cmake/CMakeLists.txt +++ b/deps/libproj/proj/cmake/CMakeLists.txt @@ -64,9 +64,6 @@ string (TOLOWER "${PROJECT_LEGACY_NAME}" PROJECT_LEGACY_LOWER) # # At some more distant date, the PROJ4::proj target will be retired. # -# To support this change, the CACHE variables PROJ_CMAKE_SUBDIR (and -# CMAKECONFIGDIR) is now the "Parent of directory to install cmake -# config files into". # # All this messiness is confined to # @@ -85,17 +82,20 @@ else () set (MSVC_TOOLSET_VERSION 0) set (MSVC_TOOLSET_MAJOR 0) endif () -if (CMAKE_CROSSCOMPILING) - # Ensure that all "true" (resp. "false") settings are represented by - # the same string. - set (CMAKE_CROSSCOMPILING_STR "ON") -else () - set (CMAKE_CROSSCOMPILING_STR "OFF") -endif () -foreach (PROJECT_VARIANT_NAME ${PROJECT_NAME} ${PROJECT_LEGACY_NAME}) +# If this is a regular build (PROJECT_SOURCE_DIR == CMAKE_SOURCE_DIR), export legacy files by default +# Otherwise, this is embedded in another project, only export PROJ target +# cf https://github.com/OSGeo/gdal/issues/5646 +string(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}" DEFAULT_VAL) +option(INSTALL_LEGACY_CMAKE_FILES "Install PROJ4 legacy target CMake files" ${DEFAULT_VAL}) + +set(PROJECT_LIST ${PROJECT_NAME}) +if (INSTALL_LEGACY_CMAKE_FILES) + set(PROJECT_LIST "${PROJECT_LIST}" "${PROJECT_LEGACY_NAME}") +endif () +foreach (PROJECT_VARIANT_NAME IN LISTS PROJECT_LIST) string (TOLOWER "${PROJECT_VARIANT_NAME}" PROJECT_VARIANT_LOWER) - set (CMAKECONFIGSUBDIR "${CMAKECONFIGDIR}/${PROJECT_VARIANT_LOWER}") + set (CMAKECONFIGSUBDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_VARIANT_LOWER}") # proj-config.cmake for the install tree. It's installed in # ${CMAKECONFIGSUBDIR} and @PROJECT_ROOT_DIR@ is the relative # path to the root from there. (Note that the whole install tree can @@ -120,9 +120,11 @@ foreach (PROJECT_VARIANT_NAME ${PROJECT_NAME} ${PROJECT_LEGACY_NAME}) NAMESPACE ${PROJECT_NAME}:: FILE ${PROJECT_NAME_LOWER}-targets.cmake DESTINATION "${CMAKECONFIGSUBDIR}") - install(EXPORT targets - NAMESPACE ${PROJECT_LEGACY_NAME}:: - FILE ${PROJECT_LEGACY_LOWER}-targets.cmake - DESTINATION "${CMAKECONFIGSUBDIR}") + if (INSTALL_LEGACY_CMAKE_FILES) + install(EXPORT targets + NAMESPACE ${PROJECT_LEGACY_NAME}:: + FILE ${PROJECT_LEGACY_LOWER}-targets.cmake + DESTINATION "${CMAKECONFIGSUBDIR}") + endif() endforeach () diff --git a/deps/libproj/proj/data/CH b/deps/libproj/proj/data/CH index 794d5b94e..725328f40 100644 --- a/deps/libproj/proj/data/CH +++ b/deps/libproj/proj/data/CH @@ -3,8 +3,7 @@ # See: https://shop.swisstopo.admin.ch/en/products/geo_software/GIS_info # # You'll need to download the grids separately and put in a directory -# scanned by libproj. Directories may be added to the scan list through -# the PROJ_LIB environment variable +# scanned by libproj. # # Note that an independent effort was made to derive an usable grid # from the CH1903->CH1903+ grid initially available from the Swisstopo diff --git a/deps/libproj/proj/data/ITRF2014 b/deps/libproj/proj/data/ITRF2014 index 26c2cefff..e16fb88cf 100644 --- a/deps/libproj/proj/data/ITRF2014 +++ b/deps/libproj/proj/data/ITRF2014 @@ -9,9 +9,9 @@ +proj=helmert +x=0.0074 +y=-0.0005 +z=-0.0628 +d=0.0038 +rz=0.00026 +dx0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector - +proj=helmert +x=0.0074 +y=-0.0005 +z=-0.0628 +s=0.0038 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +t_epoch=2010.0 +convention=position_vector + +proj=helmert +x=0.0074 +y=-0.0005 +z=-0.0628 +s=0.0038 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector - +proj=helmert +x=0.0074 +y=-0.0005 +z=-0.0628 +s=0.0038 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +t_epoch=2010.0 +convention=position_vector + +proj=helmert +x=0.0074 +y=-0.0005 +z=-0.0628 +s=0.0038 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector +proj=helmert +x=-0.0504 +y=0.0033 +z=-0.0602 +s=0.00429 +rx=-0.00281 +ry=-0.00338 +rz=0.0004 +dx=-0.0028 +dy=-0.0001 +dz=-0.0025 +ds=0.00012 +drx=-0.00011 +dry=-0.00019 +drz=0.00007 +t_epoch=2010.0 +convention=position_vector @@ -23,7 +23,7 @@ +proj=helmert +x=0.0304 +y=0.0355 +z=-0.1308 +s=0.00819 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector - +proj=helmert +x=0.0254 +y=-0.0005 +z=-0.1548 +s=0.01129 +rx=0.0001 +rz= +dx=0.00026 +dy=0.0001 +dx=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector + +proj=helmert +x=0.0254 +y=-0.0005 +z=-0.1548 +s=0.01129 +rx=0.0001 +rz=0.00026 +dx=0.0001 +dy=-0.0005 +dz=-0.0033 +ds=0.00012 +drz=0.00002 +t_epoch=2010.0 +convention=position_vector # ITRF2014 Plate Motion Model parameters # diff --git a/deps/libproj/proj/data/proj.db b/deps/libproj/proj/data/proj.db index 95aa99f3a..331b91815 100644 Binary files a/deps/libproj/proj/data/proj.db and b/deps/libproj/proj/data/proj.db differ diff --git a/deps/libproj/proj/examples/EPSG_4326_to_32631.cpp b/deps/libproj/proj/examples/EPSG_4326_to_32631.cpp new file mode 100644 index 000000000..ad6b3502d --- /dev/null +++ b/deps/libproj/proj/examples/EPSG_4326_to_32631.cpp @@ -0,0 +1,91 @@ +/******************************************************************************* + Example code that illustrates how to convert between two CRS + + Note: This file is in-lined in the documentation. Any changes must be + reflected in docs/source/development/quickstart.rst + +*******************************************************************************/ + +#include +#include // for HUGE_VAL +#include // for std::setprecision() + +#include + +#include "proj/coordinateoperation.hpp" +#include "proj/crs.hpp" +#include "proj/io.hpp" +#include "proj/util.hpp" // for nn_dynamic_pointer_cast + +using namespace NS_PROJ::crs; +using namespace NS_PROJ::io; +using namespace NS_PROJ::operation; +using namespace NS_PROJ::util; + +int main(void) { + auto dbContext = DatabaseContext::create(); + + // Instantiate a generic authority factory, that is not tied to a particular + // authority, to be able to get transformations registered by different + // authorities. This can only be used for CoordinateOperationContext. + auto authFactory = AuthorityFactory::create(dbContext, std::string()); + + // Create a coordinate operation context, that can be customized to ammend + // the way coordinate operations are computed. Here we ask for default + // settings. + auto coord_op_ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + + // Instantiate a authority factory for EPSG related objects. + auto authFactoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + + // Instantiate source CRS from EPSG code + auto sourceCRS = authFactoryEPSG->createCoordinateReferenceSystem("4326"); + + // Instantiate target CRS from PROJ.4 string (commented out, the equivalent + // from the EPSG code) + // auto targetCRS = + // authFactoryEPSG->createCoordinateReferenceSystem("32631"); + auto targetCRS = + NN_CHECK_THROW(nn_dynamic_pointer_cast(createFromUserInput( + "+proj=utm +zone=31 +datum=WGS84 +type=crs", dbContext))); + + // List operations available to transform from EPSG:4326 + // (WGS 84 latitude/longitude) to EPSG:32631 (WGS 84 / UTM zone 31N). + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCRS, targetCRS, coord_op_ctxt); + + // Check that we got a non-empty list of operations + // The list is sorted from the most relevant to the less relevant one. + // Cf + // https://proj.org/operations/operations_computation.html#filtering-and-sorting-of-coordinate-operations + // for more details on the sorting of those operations. + // For a transformation between a projected CRS and its base CRS, like + // we do here, there will be only one operation. + assert(!list.empty()); + + // Create an execution context (must only be used by one thread at a time) + PJ_CONTEXT *ctx = proj_context_create(); + + // Create a coordinate transformer from the first operation of the list + auto transformer = list[0]->coordinateTransformer(ctx); + + // Perform the coordinate transformation. + PJ_COORD c = {{ + 49.0, // latitude in degree + 2.0, // longitude in degree + 0.0, // z ordinate. unused + HUGE_VAL // time ordinate. unused + }}; + c = transformer->transform(c); + + // Display result + std::cout << std::fixed << std::setprecision(3); + std::cout << "Easting: " << c.v[0] << std::endl; // should be 426857.988 + std::cout << "Northing: " << c.v[1] << std::endl; // should be 5427937.523 + + // Destroy execution context + proj_context_destroy(ctx); + + return 0; +} diff --git a/deps/libproj/proj/examples/crs_to_geodetic.c b/deps/libproj/proj/examples/crs_to_geodetic.c new file mode 100644 index 000000000..005260b15 --- /dev/null +++ b/deps/libproj/proj/examples/crs_to_geodetic.c @@ -0,0 +1,64 @@ +/******************************************************************************* + Example code that illustrates how to convert between a CRS and + geodetic coordnates for that CRS. + + Note: This file is in-lined in the documentation. Any changes must be + reflected in docs/source/development/quickstart.rst + +*******************************************************************************/ +#include +#include +#include + +int main(void) { + + /* Create the context. */ + /* You may set C=PJ_DEFAULT_CTX if you are sure you will */ + /* use PJ objects from only one thread */ + PJ_CONTEXT *C = proj_context_create(); + + /* Create a projection. */ + PJ *P = proj_create(C, "+proj=utm +zone=32 +datum=WGS84 +type=crs"); + + if (0 == P) { + fprintf(stderr, "Failed to create transformation object.\n"); + return 1; + } + + /* Get the geodetic CRS for that projection. */ + PJ *G = proj_crs_get_geodetic_crs(C, P); + + /* Create the transform from geodetic to projected coordinates.*/ + PJ_AREA *A = NULL; + const char *const *options = NULL; + PJ *G2P = proj_create_crs_to_crs_from_pj(C, G, P, A, options); + + /* Longitude and latitude of Copenhagen, in degrees. */ + double lon = 12.0, lat = 55.0; + + /* Prepare the input */ + PJ_COORD c_in; + c_in.lpzt.z = 0.0; + c_in.lpzt.t = HUGE_VAL; // important only for time-dependent projections + c_in.lp.lam = lon; + c_in.lp.phi = lat; + printf("Input longitude: %g, latitude: %g (degrees)\n", c_in.lp.lam, + c_in.lp.phi); + + /* Compute easting and northing */ + PJ_COORD c_out = proj_trans(G2P, PJ_FWD, c_in); + printf("Output easting: %g, northing: %g (meters)\n", c_out.enu.e, + c_out.enu.n); + + /* Apply the inverse transform */ + PJ_COORD c_inv = proj_trans(G2P, PJ_INV, c_out); + printf("Inverse applied. Longitude: %g, latitude: %g (degrees)\n", + c_inv.lp.lam, c_inv.lp.phi); + + /* Clean up */ + proj_destroy(P); + proj_destroy(G); + proj_destroy(G2P); + proj_context_destroy(C); /* may be omitted in the single threaded case */ + return 0; +} diff --git a/deps/libproj/proj/examples/pj_obs_api_mini_demo.c b/deps/libproj/proj/examples/pj_obs_api_mini_demo.c new file mode 100644 index 000000000..ba399fee5 --- /dev/null +++ b/deps/libproj/proj/examples/pj_obs_api_mini_demo.c @@ -0,0 +1,60 @@ +/******************************************************************************* + Simple example code demonstrating use of the proj.h API for 2D coordinate + transformations. + + Note: This file is in-lined in the documentation. Any changes must be + reflected in docs/source/development/quickstart.rst + + Thomas Knudsen, 2016-10-30/2017-07-06 +*******************************************************************************/ +#include +#include + +int main(void) { + PJ_CONTEXT *C; + PJ *P; + PJ *norm; + PJ_COORD a, b; + + /* or you may set C=PJ_DEFAULT_CTX if you are sure you will */ + /* use PJ objects from only one thread */ + C = proj_context_create(); + + P = proj_create_crs_to_crs( + C, "EPSG:4326", "+proj=utm +zone=32 +datum=WGS84", /* or EPSG:32632 */ + NULL); + + if (0 == P) { + fprintf(stderr, "Failed to create transformation object.\n"); + return 1; + } + + /* This will ensure that the order of coordinates for the input CRS */ + /* will be longitude, latitude, whereas EPSG:4326 mandates latitude, */ + /* longitude */ + norm = proj_normalize_for_visualization(C, P); + if (0 == norm) { + fprintf(stderr, "Failed to normalize transformation object.\n"); + return 1; + } + proj_destroy(P); + P = norm; + + /* a coordinate union representing Copenhagen: 55d N, 12d E */ + /* Given that we have used proj_normalize_for_visualization(), the order */ + /* of coordinates is longitude, latitude, and values are expressed in */ + /* degrees. */ + a = proj_coord(12, 55, 0, 0); + + /* transform to UTM zone 32, then back to geographical */ + b = proj_trans(P, PJ_FWD, a); + printf("easting: %.3f, northing: %.3f\n", b.enu.e, b.enu.n); + + b = proj_trans(P, PJ_INV, b); + printf("longitude: %g, latitude: %g\n", b.lp.lam, b.lp.phi); + + /* Clean up */ + proj_destroy(P); + proj_context_destroy(C); /* may be omitted in the single threaded case */ + return 0; +} diff --git a/deps/libproj/proj/examples/s45b.pol b/deps/libproj/proj/examples/s45b.pol new file mode 100644 index 000000000..4cdf806a9 --- /dev/null +++ b/deps/libproj/proj/examples/s45b.pol @@ -0,0 +1,402 @@ +################################################################################ +# +# Horner polynomial evaluation demo data +# +################################################################################ +# +# These are the polynomial coefficients used for transforming to and from +# the Danish legacy system s45b "System 45 Bornholm" +# +################################################################################ + + +proj=pipeline + +step init=./s45b.pol:s45b_tc32 +# step init=./s45b.pol:tc32_utm32 + +step proj=utm inv ellps=intl zone=32 +step proj=cart ellps=intl + +step proj=helmert ellps=GRS80 + x=-81.0703 y=-89.3603 z=-115.7526 + rx=-484.88 ry=-24.36 rz=-413.21 s=-0.540645 + +step proj=cart inv ellps=GRS80 +step proj=utm ellps=GRS80 zone=32 + + + + +################################################################################ +# +# S 4 5 B -> T C 3 2 +# +################################################################################ + + +proj=horner +ellps=intl +range=500000 + +fwd_origin=47022.563745,51779.260103 +inv_origin=878354.943082,6125305.175366 +deg=6 + + +# static double C_ttb[] +# tc32_ed50 -> s45b +# m_lim_gen: 0.153 red = 0 OBS = 1074 +# m = 1.51 cm my_loss = +3 y_enp = +8.4 +# m = 1.53 cm mx_loss = +4 x_enp = +8.4 + +# mht C_ttb er +# fwd-inv ombyttet ifht original Poder/Engsager-kode +# For at opnå at to fwd transform fører fra s45b->tc32->utm32 (->ETRS89) + +inv_v= +# Poly NORTH :: e-degree = 0 : n-degree = 6 + 5.1779004699e+04, + 9.9508320295e-01, + -2.9453823207e-10, + 1.9995084102e-14, + -1.4895751366e-18, + -9.9734812211e-23, + 1.1194218845e-26, + +# Poly NORTH :: e-degree = 1 : n-degree = 5 + -8.4285679515e-02, + -7.9623049286e-09, + -3.7190046062e-14, + -2.3324127411e-18, + -1.1150449763e-22, + 2.8703154270e-27, + +# Poly NORTH :: e-degree = 2 : n-degree = 4 + 8.7160434140e-10, + -3.3634602927e-14, + -5.5718245313e-18, + 6.2611750909e-23, + -2.1011243838e-26, + +# Poly NORTH :: e-degree = 3 : n-degree = 3 + 1.0905463989e-14, + -4.3960034360e-18, + 3.6121595001e-22, + -1.3493066011e-27, + +# Poly NORTH :: e-degree = 4 : n-degree = 2 + -1.3360171462e-18, + 1.0780850646e-22, + 4.5118286607e-26, + +# Poly NORTH :: e-degree = 5 : n-degree = 1 + -1.3718883973e-22, + 1.6263920750e-26, + +# Poly NORTH :: e-degree = 6 : n-degree = 0 + -5.1004217526e-27 + +# tcy 6125305.175366 + +inv_u= +# Poly EAST :: n-degree = 0 : e-degree = 6 + 4.7022495967e+04, + -9.9508282498e-01, + 3.2436283039e-09, + -2.6276394334e-15, + 8.6318533291e-18, + -3.8327518550e-23, + -2.5704924282e-26, + +# Poly EAST :: n-degree = 1 : e-degree = 5 + -8.4285975934e-02, + 5.7098765263e-10, + -6.0863955939e-14, + 2.3608788740e-18, + 6.8899581969e-24, + -1.1429511179e-26, + +# Poly EAST :: n-degree = 2 : e-degree = 4 + -4.6079778412e-09, + 1.5072604543e-14, + 5.4063862378e-18, + 1.2591327827e-22, + 7.9336388691e-27, + +# Poly EAST :: n-degree = 3 : e-degree = 3 + -2.9479268638e-14, + 1.7090049434e-18, + 2.8413337985e-22, + -3.3577391552e-27, + +# Poly EAST :: n-degree = 4 : e-degree = 2 + 3.0434879273e-18, + -1.8081673510e-22, + -2.3651419850e-26, + +# Poly EAST :: n-degree = 5 : e-degree = 1 + 9.2060044804e-23, + 3.7807953325e-27, + +# Poly EAST :: n-degree = 6 : e-degree = 0 + -4.9415665221e-27 + +# tcx 878354.943082 + + +# static double C_btt[] +# s45b -> tc32_ed50 +# m_lim_gen: 0.154 red = 0 OBS = 1074 +# m = 1.50 cm my_loss = +3 y_enp = +8.5 +# m = 1.54 cm mx_loss = +4 x_enp = +8.3 + +fwd_v= +# Poly NORTH :: e-degree = 0 : n-degree = 6 + 6.1253054245e+06, + 9.9778251908e-01, + -7.7346152025e-10, + -2.5359789369e-14, + 1.5614918228e-18, + 9.8091134295e-23, + -1.1092581145e-26, + +# Poly NORTH :: e-degree = 1 : n-degree = 5 + -8.4514352088e-02, + -7.9847579284e-09, + -2.6865560962e-14, + -2.0731372756e-18, + -1.3660341123e-22, + 1.1244836340e-26, + +# Poly NORTH :: e-degree = 2 : n-degree = 4 + 8.0551988135e-11, + 3.6661500679e-14, + 5.4247705403e-18, + 8.4494604807e-23, + 1.3334858516e-26, + +# Poly NORTH :: e-degree = 3 : n-degree = 3 + 8.3889821184e-15, + -4.8124202237e-18, + 2.9088188830e-22, + -2.0129874264e-26, + +# Poly NORTH :: e-degree = 4 : n-degree = 2 + 2.4716463766e-18, + -2.1717177513e-22, + -3.2828537638e-26, + +# Poly NORTH :: e-degree = 5 : n-degree = 1 + -1.2080655753e-22, + 2.5050435391e-26, + +# Poly NORTH :: e-degree = 6 : n-degree = 0 + 1.1383483826e-27 + +# tcy 51779.260103, + +fwd_u= +# Poly EAST :: n-degree = 0 : e-degree = 6 + 8.7835485387e+05, + -9.9778289691e-01, + 3.2537215213e-09, + 6.9217640616e-15, + 8.6268883840e-18, + 4.6748156909e-23, + -2.6492402009e-26, + +# Poly EAST :: n-degree = 1 : e-degree = 5 + -8.4514648771e-02, + 1.4399520180e-09, + -6.0423329711e-14, + 6.9816167332e-20, + 6.7729233542e-23, + -5.3308251880e-27, + +# Poly EAST :: n-degree = 2 : e-degree = 4 + -4.5697800099e-09, + -1.5194038814e-14, + 5.1112653016e-18, + -2.0307532869e-22, + 1.0374125432e-26, + +# Poly EAST :: n-degree = 3 : e-degree = 3 + -2.8983003841e-14, + -1.6414425785e-18, + 1.7874983379e-22, + 1.5492164174e-26, + +# Poly EAST :: n-degree = 4 : e-degree = 2 + 2.7919197366e-18, + 1.9218613279e-22, + -2.1007264634e-26, + +# Poly EAST :: n-degree = 5 : e-degree = 1 + 1.0032412389e-22, + -5.9007997846e-27, + +# Poly EAST :: n-degree = 6 : e-degree = 0 + -4.4410970979e-27 + +# tcx 47022.563745 + + + + + +################################################################################ +# +# T C 3 2 -> U T M 3 2 +# +################################################################################ + + + +proj=horner +ellps=intl +range=500000 + + +fwd_origin=877605.269066,6125810.306769 +inv_origin=877605.760036,6125811.281773 + + +# tc32_ed50 -> utm32_ed50 : Bornholm + +deg=4 + +# ttu_n and ttu_e are based on static double C_ttu_b[] +# m_lim_gen: 0.086 red = 0 OBS = 852 +# m = 1.38 cm my_loss = +2 y_enp = +10.5 +# m = 1.44 cm mx_loss = +2 x_enp = +10.4 +# static double ttu_n[] + +fwd_v= +# Poly NORTH :: e-degree = 0 : n-degree = 0..4 + 6.1258112678e+06, + 9.9999971567e-01, + 1.5372750011e-10, + 5.9300860915e-15, + 2.2609497633e-19, + +# Poly NORTH :: e-degree = 1 : n-degree = 0..3 + 4.3188227445e-05, + 2.8225130416e-10, + 7.8740007114e-16, + -1.7453997279e-19, + +# Poly NORTH :: e-degree = 2 : n-degree = 0..2 + 1.6877465415e-10, + -1.1234649773e-14, + -1.7042333358e-18, + +# Poly NORTH :: e-degree = 3 : n-degree = 0..1 + -7.9303467953e-15, + -5.2906832535e-19, + +# Poly NORTH :: e-degree = 4 : n-degree = 0 + 3.9984284847e-19 + +# tcy 6125810.306769 + + + +# static double ttu_e[] +fwd_u= +# Poly EAST :: n-degree = 0 : e-degree = 0..4 + 8.7760574982e+05, + 9.9999752475e-01, + 2.8817299305e-10, + 5.5641310680e-15, + -1.5544700949e-18, + +# Poly EAST :: n-degree = 1 : e-degree = 3 + -4.1357045890e-05, + 4.2106213519e-11, + 2.8525551629e-14, + -1.9107771273e-18, + +# Poly EAST :: n-degree = 2 : e-degree = 2 + 3.3615590093e-10, + 2.4380247154e-14, + -2.0241230315e-18, + +# Poly EAST :: n-degree = 3 : e-degree = 1 + 1.2429019719e-15, + 5.3886155968e-19, + +# Poly EAST :: n-degree = 4 : e-degree = 0 + -1.0167505000e-18 + +# tcx 877605.760036 + + +# utt_n and utt_e are based on static double C_utt_b[] +# utm32_ed50 -> tc32_ed50 : Bornholm +# m_lim_gen: 0.086 red = 0 OBS = 852 +# m = 1.38 cm my_loss = +2 y_enp = +10.8 +# m = 1.44 cm mx_loss = +2 x_enp = +10.7 +# static double utt_n[] + +inv_v= +# Poly NORTH :: e-degree = 0 : n-degree = 4 + 6.1258103208e+06, + 1.0000002826e+00, + -1.5372762184e-10, + -5.9304261011e-15, + -2.2612705361e-19, + +# Poly NORTH :: e-degree = 1 : n-degree = 3 + -4.3188331419e-05, + -2.8225549995e-10, + -7.8529116371e-16, + 1.7476576773e-19, + +# Poly NORTH :: e-degree = 2 : n-degree = 2 + -1.6875687989e-10, + 1.1236475299e-14, + 1.7042518057e-18, + +# Poly NORTH :: e-degree = 3 : n-degree = 1 + 7.9300735257e-15, + 5.2881862699e-19, + +# Poly NORTH :: e-degree = 4 : n-degree = 0 + -3.9990736798e-19 + +# tcy 6125811.281773 + + + + +# static double utt_e[] +inv_u= +# Poly EAST :: n-degree = 0 : e-degree = 0..4 + 8.7760527928e+05, + 1.0000024735e+00, + -2.8817540032e-10, + -5.5627059451e-15, + 1.5543637570e-18, + +# Poly EAST :: n-degree = 1 : e-degree = 0..3 + 4.1357152105e-05, + -4.2114813612e-11, + -2.8523713454e-14, + 1.9109017837e-18, + +# Poly EAST :: n-degree = 2 : e-degree = 0..2 + -3.3616407783e-10, + -2.4382678126e-14, + 2.0245020199e-18, + +# Poly EAST :: n-degree = 3 : e-degree = 0..1 + -1.2441377565e-15, + -5.3885232238e-19, + +# Poly EAST :: n-degree = 4 : e-degree = 0 + 1.0167203661e-18 + +# tcx 877605.760036 + +<> diff --git a/deps/libproj/proj/examples/val_def.demo b/deps/libproj/proj/examples/val_def.demo new file mode 100644 index 000000000..315219fd8 --- /dev/null +++ b/deps/libproj/proj/examples/val_def.demo @@ -0,0 +1,85 @@ + +----------------------------------------------------------------------- + + INTEGRATING DEFINITION AND VALIDATION OF GEODETIC SYSTEMS + +----------------------------------------------------------------------- + Thomas Knudsen, thokn@sdfe.dk, 2017-12-06 +----------------------------------------------------------------------- + +This demo shows how to use the free format definition strings, +introduced in PROJ version 5.0.0, to integrate system definition +information with system validation data. + +The system definition parts are used when doing actual transformations, +e.g. using the cct 4D transformation program: + + echo 9 55 0 0 | cct +init=val_def.demo:DKTM1 + +The system validation parts are used when validating the systems +defined in the file. This is done using the gie test program: +Place val_def.demo in your PROJ_LIB directory +(or set PROJ_LIB=) and say: + + gie val_def.demo + +This will result in a report detailing how many tests succeeded, +resp. failed. + +The syntax of proj init files is orthogonal to the syntax of gie +integrity evaluation files. This makes it possible to interleave +init and gie blocks in the same file. + +#----------------------------------------------------------------------- + + + +#----------------------------------------------------------------------- +# Danish Transverse Mercator, zone 1 +#----------------------------------------------------------------------- + proj = etmerc + lat_0 = 0 lon_0 = 9 + x_0 = 200000 y_0 = -5000000 + + k = 0.99998 + + ellps = GRS80 + units = m + + no_defs +#----------------------------------------------------------------------- + +operation init = val_def.demo:DKTM1 +tolerance 100 um +accept 9 55 +expect 200000.0000 1097108.3684 +roundtrip 1000 1 nm + +#----------------------------------------------------------------------- + + + +#----------------------------------------------------------------------- +# Danish Transverse Mercator, zone 2 +#----------------------------------------------------------------------- + proj = etmerc + lat_0 = 0 lon_0 = 10 + x_0 = 400000 y_0 = -5000000 + + k = 0.99998 + + ellps = GRS80 + units = m + + no_defs +#----------------------------------------------------------------------- + +operation init = val_def.demo:DKTM2 +tolerance 100 um +accept 10 55 +expect 400000.0000 1097108.3684 +accept 10.5 55.5 +expect 431597.1668 1152884.9398 +roundtrip 1000 100 um + +#----------------------------------------------------------------------- diff --git a/deps/libproj/proj/include/proj/common.hpp b/deps/libproj/proj/include/proj/common.hpp index 4cc1d63da..6a5fd5962 100644 --- a/deps/libproj/proj/include/proj/common.hpp +++ b/deps/libproj/proj/include/proj/common.hpp @@ -77,10 +77,11 @@ class PROJ_GCC_DLL UnitOfMeasure : public util::BaseObject { PARAMETRIC, }; - PROJ_DLL UnitOfMeasure(const std::string &nameIn = std::string(), - double toSIIn = 1.0, Type typeIn = Type::UNKNOWN, - const std::string &codeSpaceIn = std::string(), - const std::string &codeIn = std::string()); + PROJ_DLL explicit UnitOfMeasure( + const std::string &nameIn = std::string(), double toSIIn = 1.0, + Type typeIn = Type::UNKNOWN, + const std::string &codeSpaceIn = std::string(), + const std::string &codeIn = std::string()); //! @cond Doxygen_Suppress PROJ_DLL UnitOfMeasure(const UnitOfMeasure &other); @@ -147,12 +148,13 @@ class PROJ_GCC_DLL UnitOfMeasure : public util::BaseObject { /** \brief Numeric value associated with a UnitOfMeasure. */ class Measure : public util::BaseObject { public: + // cppcheck-suppress noExplicitConstructor PROJ_DLL Measure(double valueIn = 0.0, const UnitOfMeasure &unitIn = UnitOfMeasure()); //! @cond Doxygen_Suppress PROJ_DLL Measure(const Measure &other); - PROJ_DLL ~Measure(); + PROJ_DLL ~Measure() override; //! @endcond PROJ_DLL const UnitOfMeasure &unit() PROJ_PURE_DECL; diff --git a/deps/libproj/proj/include/proj/coordinateoperation.hpp b/deps/libproj/proj/include/proj/coordinateoperation.hpp index fc1d5b8a7..158df31d2 100644 --- a/deps/libproj/proj/include/proj/coordinateoperation.hpp +++ b/deps/libproj/proj/include/proj/coordinateoperation.hpp @@ -38,6 +38,8 @@ #include "io.hpp" #include "metadata.hpp" +#include "proj.h" + NS_PROJ_START namespace crs { @@ -49,6 +51,16 @@ class DerivedCRS; class ProjectedCRS; } // namespace crs +namespace io { +class JSONParser; +} // namespace io + +namespace coordinates { +class CoordinateMetadata; +using CoordinateMetadataPtr = std::shared_ptr; +using CoordinateMetadataNNPtr = util::nn; +} // namespace coordinates + /** osgeo.proj.operation namespace \brief Coordinate operations (relationship between any two coordinate @@ -93,6 +105,51 @@ using CoordinateOperationPtr = std::shared_ptr; /** Non-null shared pointer of CoordinateOperation */ using CoordinateOperationNNPtr = util::nn; +// --------------------------------------------------------------------------- + +class CoordinateTransformer; +/** Shared pointer of CoordinateTransformer */ +using CoordinateTransformerPtr = std::unique_ptr; +/** Non-null shared pointer of CoordinateTransformer */ +using CoordinateTransformerNNPtr = util::nn; + +/** \brief Coordinate transformer. + * + * Performs coordinate transformation of coordinate tuplies. + * + * @since 9.3 + */ +class PROJ_GCC_DLL CoordinateTransformer { + public: + //! @cond Doxygen_Suppress + PROJ_DLL ~CoordinateTransformer(); + //! @endcond + + PROJ_DLL PJ_COORD transform(PJ_COORD coord); + + protected: + PROJ_FRIEND(CoordinateOperation); + + PROJ_INTERNAL CoordinateTransformer(); + + PROJ_INTERNAL static CoordinateTransformerNNPtr + create(const CoordinateOperationNNPtr &op, PJ_CONTEXT *ctx); + + private: + PROJ_OPAQUE_PRIVATE_DATA + INLINED_MAKE_UNIQUE + CoordinateTransformer & + operator=(const CoordinateTransformer &other) = delete; +}; + +// --------------------------------------------------------------------------- + +class Transformation; +/** Shared pointer of Transformation */ +using TransformationPtr = std::shared_ptr; +/** Non-null shared pointer of Transformation */ +using TransformationNNPtr = util::nn; + /** \brief Abstract class for a mathematical operation on coordinates. * * A mathematical operation: @@ -135,7 +192,8 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage, PROJ_DLL const util::optional & targetCoordinateEpoch() const; - // virtual void transform(...) = 0; TODO + PROJ_DLL CoordinateTransformerNNPtr + coordinateTransformer(PJ_CONTEXT *ctx) const; /** \brief Return the inverse of the coordinate operation. * @throw util::UnsupportedOperationException @@ -171,12 +229,16 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage, PROJ_FRIEND(io::AuthorityFactory); PROJ_FRIEND(CoordinateOperationFactory); PROJ_FRIEND(ConcatenatedOperation); + PROJ_FRIEND(io::WKTParser); + PROJ_FRIEND(io::JSONParser); PROJ_INTERNAL void setWeakSourceTargetCRS(std::weak_ptr sourceCRSIn, std::weak_ptr targetCRSIn); PROJ_INTERNAL void setCRSs(const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn); + PROJ_INTERNAL void + setInterpolationCRS(const crs::CRSPtr &interpolationCRSIn); PROJ_INTERNAL void setCRSs(const CoordinateOperation *in, bool inverseSourceTarget); PROJ_INTERNAL @@ -184,6 +246,11 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage, const std::vector &accuracies); PROJ_INTERNAL void setHasBallparkTransformation(bool b); + PROJ_INTERNAL void + setSourceCoordinateEpoch(const util::optional &epoch); + PROJ_INTERNAL void + setTargetCoordinateEpoch(const util::optional &epoch); + PROJ_INTERNAL void setProperties(const util::PropertyMap &properties); // throw(InvalidValueTypeException) @@ -608,6 +675,9 @@ class PROJ_GCC_DLL SingleOperation : virtual public CoordinateOperation { PROJ_DLL std::list validateParameters() const; + PROJ_DLL TransformationNNPtr substitutePROJAlternativeGridNames( + io::DatabaseContextNNPtr databaseContext) const; + PROJ_PRIVATE : //! @cond Doxygen_Suppress @@ -628,6 +698,9 @@ class PROJ_GCC_DLL SingleOperation : virtual public CoordinateOperation { util::IComparable::Criterion criterion = util::IComparable::Criterion::STRICT, const io::DatabaseContextPtr &dbContext = nullptr) const override; + + PROJ_INTERNAL bool isLongitudeRotation() const; + //! @endcond protected: @@ -649,6 +722,10 @@ class PROJ_GCC_DLL SingleOperation : virtual public CoordinateOperation { const io::DatabaseContextPtr &dbContext, bool inOtherDirection) const; + PROJ_INTERNAL static GeneralParameterValueNNPtr + createOperationParameterValueFromInterpolationCRS(int methodEPSGCode, + int crsEPSGCode); + private: PROJ_OPAQUE_PRIVATE_DATA SingleOperation &operator=(const SingleOperation &other) = delete; @@ -930,6 +1007,11 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing); + PROJ_DLL static ConversionNNPtr createTunisiaMiningGrid( + const util::PropertyMap &properties, const common::Angle ¢erLat, + const common::Angle ¢erLong, const common::Length &falseEasting, + const common::Length &falseNorthing); + PROJ_DLL static ConversionNNPtr createAlbersEqualArea(const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, @@ -945,6 +1027,14 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { const common::Length &falseEasting, const common::Length &falseNorthing); + PROJ_DLL static ConversionNNPtr createLambertConicConformal_1SP_VariantB( + const util::PropertyMap &properties, + const common::Angle &latitudeNatOrigin, const common::Scale &scale, + const common::Angle &latitudeFalseOrigin, + const common::Angle &longitudeFalseOrigin, + const common::Length &eastingFalseOrigin, + const common::Length &northingFalseOrigin); + PROJ_DLL static ConversionNNPtr createLambertConicConformal_2SP(const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, @@ -1015,12 +1105,12 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { PROJ_DLL static ConversionNNPtr createEquidistantConic(const util::PropertyMap &properties, - const common::Angle ¢erLat, - const common::Angle ¢erLong, + const common::Angle &latitudeFalseOrigin, + const common::Angle &longitudeFalseOrigin, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, - const common::Length &falseEasting, - const common::Length &falseNorthing); + const common::Length &eastingFalseOrigin, + const common::Length &northingFalseOrigin); PROJ_DLL static ConversionNNPtr createEckertI(const util::PropertyMap &properties, @@ -1202,6 +1292,11 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing); + PROJ_DLL static ConversionNNPtr createMercatorSpherical( + const util::PropertyMap &properties, const common::Angle ¢erLat, + const common::Angle ¢erLong, const common::Length &falseEasting, + const common::Length &falseNorthing); + PROJ_DLL static ConversionNNPtr createMollweide(const util::PropertyMap &properties, const common::Angle ¢erLong, @@ -1361,6 +1456,23 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { PROJ_DLL static ConversionNNPtr createGeographicGeocentric(const util::PropertyMap &properties); + PROJ_DLL static ConversionNNPtr + createGeographic2DOffsets(const util::PropertyMap &properties, + const common::Angle &offsetLat, + const common::Angle &offsetLong); + + PROJ_DLL static ConversionNNPtr createGeographic3DOffsets( + const util::PropertyMap &properties, const common::Angle &offsetLat, + const common::Angle &offsetLong, const common::Length &offsetHeight); + + PROJ_DLL static ConversionNNPtr createGeographic2DWithHeightOffsets( + const util::PropertyMap &properties, const common::Angle &offsetLat, + const common::Angle &offsetLong, const common::Length &offsetHeight); + + PROJ_DLL static ConversionNNPtr + createVerticalOffset(const util::PropertyMap &properties, + const common::Length &offsetHeight); + PROJ_DLL ConversionPtr convertToOtherMethod(int targetEPSGCode) const; PROJ_PRIVATE : @@ -1418,12 +1530,6 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { // --------------------------------------------------------------------------- -class Transformation; -/** Shared pointer of Transformation */ -using TransformationPtr = std::shared_ptr; -/** Non-null shared pointer of Transformation */ -using TransformationNNPtr = util::nn; - /** \brief A mathematical operation on coordinates in which parameters are * empirically derived from data containing the coordinates of a series of * points in both coordinate reference systems. @@ -1557,19 +1663,19 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { PROJ_DLL static TransformationNNPtr createGeographic2DOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, - const common::Angle &offsetLon, + const common::Angle &offsetLong, const std::vector &accuracies); PROJ_DLL static TransformationNNPtr createGeographic3DOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, - const common::Angle &offsetLon, const common::Length &offsetHeight, + const common::Angle &offsetLong, const common::Length &offsetHeight, const std::vector &accuracies); PROJ_DLL static TransformationNNPtr createGeographic2DWithHeightOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, - const common::Angle &offsetLon, const common::Length &offsetHeight, + const common::Angle &offsetLong, const common::Length &offsetHeight, const std::vector &accuracies); PROJ_DLL static TransformationNNPtr createVerticalOffset( @@ -1577,9 +1683,6 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { const crs::CRSNNPtr &targetCRSIn, const common::Length &offsetHeight, const std::vector &accuracies); - PROJ_DLL TransformationNNPtr substitutePROJAlternativeGridNames( - io::DatabaseContextNNPtr databaseContext) const; - PROJ_DLL static TransformationNNPtr createChangeVerticalUnit( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Scale &factor, @@ -1595,8 +1698,6 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { PROJ_INTERNAL const std::string &getHeightToGeographic3DFilename() const; - PROJ_INTERNAL bool isLongitudeRotation() const; - PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) @@ -1613,6 +1714,9 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { demoteTo2D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const; + PROJ_INTERNAL static bool + isGeographic3DToGravityRelatedHeight(const OperationMethodNNPtr &method, + bool allowInverse); //! @endcond protected: @@ -1629,6 +1733,7 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { const override; // throw(FormattingException) PROJ_FRIEND(CoordinateOperationFactory); + PROJ_FRIEND(SingleOperation); PROJ_INTERNAL TransformationNNPtr inverseAsTransformation() const; PROJ_INTERNAL CoordinateOperationNNPtr _shallowClone() const override; @@ -1725,7 +1830,8 @@ class PROJ_GCC_DLL ConcatenatedOperation final : public CoordinateOperation { PROJ_INTERNAL static void fixStepsDirection(const crs::CRSNNPtr &concatOpSourceCRS, const crs::CRSNNPtr &concatOpTargetCRS, - std::vector &operationsInOut); + std::vector &operationsInOut, + const io::DatabaseContextPtr &dbContext); //! @endcond protected: @@ -1874,12 +1980,28 @@ class PROJ_GCC_DLL CoordinateOperationContext { PROJ_DLL const std::vector> & getIntermediateCRS() const; + PROJ_DLL void + setSourceCoordinateEpoch(const util::optional &epoch); + + PROJ_DLL const util::optional & + getSourceCoordinateEpoch() const; + + PROJ_DLL void + setTargetCoordinateEpoch(const util::optional &epoch); + + PROJ_DLL const util::optional & + getTargetCoordinateEpoch() const; + PROJ_DLL static CoordinateOperationContextNNPtr create(const io::AuthorityFactoryPtr &authorityFactory, const metadata::ExtentPtr &extent, double accuracy); + PROJ_DLL CoordinateOperationContextNNPtr clone() const; + protected: PROJ_INTERNAL CoordinateOperationContext(); + PROJ_INTERNAL + CoordinateOperationContext(const CoordinateOperationContext &); INLINED_MAKE_UNIQUE private: @@ -1917,6 +2039,16 @@ class PROJ_GCC_DLL CoordinateOperationFactory { const crs::CRSNNPtr &targetCRS, const CoordinateOperationContextNNPtr &context) const; + PROJ_DLL std::vector createOperations( + const coordinates::CoordinateMetadataNNPtr &sourceCoordinateMetadata, + const crs::CRSNNPtr &targetCRS, + const CoordinateOperationContextNNPtr &context) const; + + PROJ_DLL std::vector createOperations( + const crs::CRSNNPtr &sourceCRS, + const coordinates::CoordinateMetadataNNPtr &targetCoordinateMetadata, + const CoordinateOperationContextNNPtr &context) const; + PROJ_DLL static CoordinateOperationFactoryNNPtr create(); protected: diff --git a/deps/libproj/proj/include/proj/coordinates.hpp b/deps/libproj/proj/include/proj/coordinates.hpp new file mode 100644 index 000000000..87ca479e3 --- /dev/null +++ b/deps/libproj/proj/include/proj/coordinates.hpp @@ -0,0 +1,108 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: ISO19111:2019 implementation + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2023, Even Rouault + * + * 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. + ****************************************************************************/ + +#ifndef COORDINATES_HH_INCLUDED +#define COORDINATES_HH_INCLUDED + +#include + +#include "common.hpp" +#include "crs.hpp" +#include "io.hpp" +#include "util.hpp" + +NS_PROJ_START + +/** osgeo.proj.coordinates namespace + + \brief Coordinates package +*/ +namespace coordinates { + +class CoordinateMetadata; +/** Shared pointer of CoordinateMetadata */ +using CoordinateMetadataPtr = std::shared_ptr; +/** Non-null shared pointer of CoordinateMetadata */ +using CoordinateMetadataNNPtr = util::nn; + +// --------------------------------------------------------------------------- + +/** \brief Associates a CRS with a coordinate epoch. + * + * \remark Implements CoordinateMetadata from \ref ISO_19111_2019 + * \since 9.2 + */ + +class PROJ_GCC_DLL CoordinateMetadata : public util::BaseObject, + public io::IWKTExportable, + public io::IJSONExportable { + public: + //! @cond Doxygen_Suppress + PROJ_DLL ~CoordinateMetadata() override; + //! @endcond + + PROJ_DLL const crs::CRSNNPtr &crs() PROJ_PURE_DECL; + PROJ_DLL const util::optional & + coordinateEpoch() PROJ_PURE_DECL; + PROJ_DLL double coordinateEpochAsDecimalYear() PROJ_PURE_DECL; + + PROJ_DLL static CoordinateMetadataNNPtr create(const crs::CRSNNPtr &crsIn); + PROJ_DLL static CoordinateMetadataNNPtr + create(const crs::CRSNNPtr &crsIn, double coordinateEpochAsDecimalYear); + + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + + PROJ_INTERNAL void + _exportToWKT(io::WKTFormatter *formatter) + const override; // throw(io::FormattingException) + + PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) + const override; // throw(FormattingException) + + //! @endcond + + protected: + PROJ_INTERNAL explicit CoordinateMetadata(const crs::CRSNNPtr &crsIn); + PROJ_INTERNAL CoordinateMetadata(const crs::CRSNNPtr &crsIn, + double coordinateEpochAsDecimalYear); + + INLINED_MAKE_SHARED + + private: + PROJ_OPAQUE_PRIVATE_DATA + CoordinateMetadata &operator=(const CoordinateMetadata &other) = delete; +}; + +// --------------------------------------------------------------------------- + +} // namespace coordinates + +NS_PROJ_END + +#endif // COORDINATES_HH_INCLUDED diff --git a/deps/libproj/proj/include/proj/coordinatesystem.hpp b/deps/libproj/proj/include/proj/coordinatesystem.hpp index b40b038d5..0dc0702e2 100644 --- a/deps/libproj/proj/include/proj/coordinatesystem.hpp +++ b/deps/libproj/proj/include/proj/coordinatesystem.hpp @@ -110,6 +110,34 @@ class AxisDirection : public util::CodeList { // --------------------------------------------------------------------------- +/** \brief Meaning of the axis value range specified through minimumValue and + * maximumValue + * + * \remark Implements RangeMeaning from \ref ISO_19111_2019 + * \since 9.2 + */ +class RangeMeaning : public util::CodeList { + public: + //! @cond Doxygen_Suppress + PROJ_DLL static const RangeMeaning * + valueOf(const std::string &nameIn) noexcept; + //! @endcond + + PROJ_DLL static const RangeMeaning EXACT; + PROJ_DLL static const RangeMeaning WRAPAROUND; + + protected: + friend class util::optional; + RangeMeaning(); + + private: + explicit RangeMeaning(const std::string &nameIn); + + static std::map registry; +}; + +// --------------------------------------------------------------------------- + class Meridian; /** Shared pointer of Meridian. */ using MeridianPtr = std::shared_ptr; @@ -123,7 +151,8 @@ using MeridianNNPtr = util::nn; * * \remark Implements MERIDIAN from \ref WKT2 */ -class PROJ_GCC_DLL Meridian : public common::IdentifiedObject { +class PROJ_GCC_DLL Meridian : public common::IdentifiedObject, + public io::IJSONExportable { public: //! @cond Doxygen_Suppress PROJ_DLL ~Meridian() override; @@ -137,6 +166,9 @@ class PROJ_GCC_DLL Meridian : public common::IdentifiedObject { //! @cond Doxygen_Suppress PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) + + PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) + const override; // throw(FormattingException) //! @endcond protected: @@ -177,6 +209,7 @@ class PROJ_GCC_DLL CoordinateSystemAxis final : public common::IdentifiedObject, PROJ_DLL const common::UnitOfMeasure &unit() PROJ_PURE_DECL; PROJ_DLL const util::optional &minimumValue() PROJ_PURE_DECL; PROJ_DLL const util::optional &maximumValue() PROJ_PURE_DECL; + PROJ_DLL const util::optional &rangeMeaning() PROJ_PURE_DECL; PROJ_DLL const MeridianPtr &meridian() PROJ_PURE_DECL; // Non-standard @@ -186,6 +219,15 @@ class PROJ_GCC_DLL CoordinateSystemAxis final : public common::IdentifiedObject, const common::UnitOfMeasure &unitIn, const MeridianPtr &meridianIn = nullptr); + PROJ_DLL static CoordinateSystemAxisNNPtr + create(const util::PropertyMap &properties, + const std::string &abbreviationIn, const AxisDirection &directionIn, + const common::UnitOfMeasure &unitIn, + const util::optional &minimumValueIn, + const util::optional &maximumValueIn, + const util::optional &rangeMeaningIn, + const MeridianPtr &meridianIn = nullptr); + PROJ_PRIVATE : //! @cond Doxygen_Suppress @@ -540,6 +582,56 @@ class PROJ_GCC_DLL CartesianCS final : public CoordinateSystem { // --------------------------------------------------------------------------- +class AffineCS; +/** Shared pointer of AffineCS. */ +using AffineCSPtr = std::shared_ptr; +/** Non-null shared pointer of AffineCS. */ +using AffineCSNNPtr = util::nn; + +/** \brief A two- or three-dimensional coordinate system in Euclidean space + * with straight axes that are not necessarily orthogonal. + * + * \remark Implements AffineCS from \ref ISO_19111_2019 + * \since 9.2 + */ +class PROJ_GCC_DLL AffineCS final : public CoordinateSystem { + public: + //! @cond Doxygen_Suppress + PROJ_DLL ~AffineCS() override; + //! @endcond + + PROJ_DLL static AffineCSNNPtr + create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2); + PROJ_DLL static AffineCSNNPtr + create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2, + const CoordinateSystemAxisNNPtr &axis3); + + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + PROJ_INTERNAL AffineCSNNPtr + alterUnit(const common::UnitOfMeasure &unit) const; + + //! @endcond + + protected: + PROJ_INTERNAL explicit AffineCS( + const std::vector &axisIn); + INLINED_MAKE_SHARED + + PROJ_INTERNAL std::string getWKT2Type(bool) const override { + return "affine"; + } + + private: + AffineCS(const AffineCS &other) = delete; +}; + +// --------------------------------------------------------------------------- + class OrdinalCS; /** Shared pointer of OrdinalCS. */ using OrdinalCSPtr = std::shared_ptr; diff --git a/deps/libproj/proj/include/proj/crs.hpp b/deps/libproj/proj/include/proj/crs.hpp index 593bfc4b0..d9c384657 100644 --- a/deps/libproj/proj/include/proj/crs.hpp +++ b/deps/libproj/proj/include/proj/crs.hpp @@ -96,6 +96,7 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage, // Non-standard + PROJ_DLL bool isDynamic(bool considerWGS84AsDynamic = false) const; PROJ_DLL GeodeticCRSPtr extractGeodeticCRS() const; PROJ_DLL GeographicCRSPtr extractGeographicCRS() const; PROJ_DLL VerticalCRSPtr extractVerticalCRS() const; @@ -350,6 +351,10 @@ class PROJ_GCC_DLL GeodeticCRS : virtual public SingleCRS, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; + PROJ_INTERNAL void _exportToJSONInternal( + io::JSONFormatter *formatter, + const char *objectName) const; // throw(FormattingException) + PROJ_INTERNAL std::list> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -657,6 +662,10 @@ class PROJ_GCC_DLL ProjectedCRS final : public DerivedCRS, addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter, bool axisSpecFound) const; + PROJ_INTERNAL static void addUnitConvertAndAxisSwap( + const std::vector &axisListIn, + io::PROJStringFormatter *formatter, bool axisSpecFound); + PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) @@ -1056,7 +1065,9 @@ class PROJ_GCC_DLL BoundCRS final : public CRS, PROJ_INTERNAL BoundCRSNNPtr shallowCloneAsBoundCRS() const; PROJ_INTERNAL bool isTOWGS84Compatible() const; PROJ_INTERNAL std::string getHDatumPROJ4GRIDS() const; - PROJ_INTERNAL std::string getVDatumPROJ4GRIDS() const; + PROJ_INTERNAL std::string + getVDatumPROJ4GRIDS(const crs::GeographicCRS *geogCRSOfCompoundCRS, + const char **outGeoidCRSValue) const; PROJ_INTERNAL std::list> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -1263,10 +1274,17 @@ class PROJ_GCC_DLL DerivedProjectedCRS final : public DerivedCRS { const operation::ConversionNNPtr &derivingConversionIn, const cs::CoordinateSystemNNPtr &csIn); + PROJ_DLL DerivedProjectedCRSNNPtr + demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const; + //! @cond Doxygen_Suppress PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) - //! @endcond + + PROJ_INTERNAL void + addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter) const; + //! @endcond protected: PROJ_INTERNAL diff --git a/deps/libproj/proj/include/proj/datum.hpp b/deps/libproj/proj/include/proj/datum.hpp index bf3dbcb7a..5e81330f6 100644 --- a/deps/libproj/proj/include/proj/datum.hpp +++ b/deps/libproj/proj/include/proj/datum.hpp @@ -69,6 +69,7 @@ class PROJ_GCC_DLL Datum : public common::ObjectUsage, //! @endcond PROJ_DLL const util::optional &anchorDefinition() const; + PROJ_DLL const util::optional &anchorEpoch() const; PROJ_DLL const util::optional &publicationDate() const; PROJ_DLL const common::IdentifiedObjectPtr &conventionalRS() const; @@ -91,6 +92,8 @@ class PROJ_GCC_DLL Datum : public common::ObjectUsage, protected: PROJ_INTERNAL void setAnchor(const util::optional &anchor); + PROJ_INTERNAL void + setAnchorEpoch(const util::optional &anchorEpoch); PROJ_INTERNAL void setProperties(const util::PropertyMap @@ -414,6 +417,12 @@ class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum { const util::optional &anchor, const PrimeMeridianNNPtr &primeMeridian); + PROJ_DLL static GeodeticReferenceFrameNNPtr + create(const util::PropertyMap &properties, const EllipsoidNNPtr &ellipsoid, + const util::optional &anchor, + const util::optional &anchorEpoch, + const PrimeMeridianNNPtr &primeMeridian); + PROJ_DLL static const GeodeticReferenceFrameNNPtr EPSG_6267; // North American Datum 1927 PROJ_DLL static const GeodeticReferenceFrameNNPtr @@ -432,6 +441,10 @@ class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum { util::IComparable::Criterion criterion = util::IComparable::Criterion::STRICT, const io::DatabaseContextPtr &dbContext = nullptr) const override; + + PROJ_INTERNAL bool isEquivalentToNoExactTypeCheck( + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const; //! @endcond protected: @@ -580,6 +593,13 @@ class PROJ_GCC_DLL VerticalReferenceFrame : public Datum { const util::optional &realizationMethodIn = util::optional()); + PROJ_DLL static VerticalReferenceFrameNNPtr + create(const util::PropertyMap &properties, + const util::optional &anchor, + const util::optional &anchorEpoch, + const util::optional &realizationMethodIn = + util::optional()); + //! @cond Doxygen_Suppress PROJ_INTERNAL bool _isEquivalentTo( const util::IComparable *other, @@ -587,6 +607,10 @@ class PROJ_GCC_DLL VerticalReferenceFrame : public Datum { util::IComparable::Criterion::STRICT, const io::DatabaseContextPtr &dbContext = nullptr) const override; + PROJ_INTERNAL bool isEquivalentToNoExactTypeCheck( + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const; + PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) diff --git a/deps/libproj/proj/include/proj/internal/mutex.hpp b/deps/libproj/proj/include/proj/internal/datum_internal.hpp similarity index 66% rename from deps/libproj/proj/include/proj/internal/mutex.hpp rename to deps/libproj/proj/include/proj/internal/datum_internal.hpp index a378be409..899679f66 100644 --- a/deps/libproj/proj/include/proj/internal/mutex.hpp +++ b/deps/libproj/proj/include/proj/internal/datum_internal.hpp @@ -1,11 +1,11 @@ /****************************************************************************** * * Project: PROJ - * Purpose: std::mutex emulation + * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** - * Copyright (c) 2021, Even Rouault + * Copyright (c) 2023, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -26,36 +26,13 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ -#include "proj/util.hpp" -#include "proj_internal.h" - -#ifndef __MINGW32__ -#include +#ifndef FROM_PROJ_CPP +#error This file should only be included from a PROJ cpp file #endif -NS_PROJ_START - -#ifdef __MINGW32__ -// mingw32-win32 doesn't implement std::mutex -class mutex { - public: - // cppcheck-suppress functionStatic - void lock() { pj_acquire_lock(); } - // cppcheck-suppress functionStatic - void unlock() { pj_release_lock(); } -}; - -template struct lock_guard { - Lock &lock_; - lock_guard(Lock &lock) : lock_(lock) { lock_.lock(); } - ~lock_guard() { lock_.unlock(); } -}; +#ifndef DATUM_INTERNAL_HH_INCLUDED +#define DATUM_INTERNAL_HH_INCLUDED -#else - -typedef std::mutex mutex; -template using lock_guard = std::lock_guard; - -#endif +#define UNKNOWN_ENGINEERING_DATUM "Unknown engineering datum" -NS_PROJ_END +#endif // DATUM_INTERNAL_HH_INCLUDED diff --git a/deps/libproj/proj/include/proj/internal/internal.hpp b/deps/libproj/proj/include/proj/internal/internal.hpp index 2222a264c..6a7b91257 100644 --- a/deps/libproj/proj/include/proj/internal/internal.hpp +++ b/deps/libproj/proj/include/proj/internal/internal.hpp @@ -50,23 +50,24 @@ //! @cond Doxygen_Suppress -#if ((defined(__clang__) && \ - (__clang_major__ > 3 || \ - (__clang_major__ == 3 && __clang_minor__ >= 7))) || \ - (__GNUC__ >= 7 && !__INTEL_COMPILER)) -/** Macro for fallthrough in a switch case construct */ -#define PROJ_FALLTHROUGH [[clang::fallthrough]]; -#else -/** Macro for fallthrough in a switch case construct */ -#define PROJ_FALLTHROUGH +// Use "PROJ_FALLTHROUGH;" to annotate deliberate fall-through in switches, +// use it analogously to "break;". The trailing semi-colon is required. +#if !defined(PROJ_FALLTHROUGH) && defined(__has_cpp_attribute) +#if __cplusplus >= 201703L && __has_cpp_attribute(fallthrough) +#define PROJ_FALLTHROUGH [[fallthrough]] +#elif __cplusplus >= 201103L && __has_cpp_attribute(gnu::fallthrough) +#define PROJ_FALLTHROUGH [[gnu::fallthrough]] +#elif __cplusplus >= 201103L && __has_cpp_attribute(clang::fallthrough) +#define PROJ_FALLTHROUGH [[clang::fallthrough]] +#endif #endif -#if defined(__clang__) || defined(_MSC_VER) -#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT +#ifndef PROJ_FALLTHROUGH +#define PROJ_FALLTHROUGH ((void)0) #endif -#if !(defined(__clang__) && __clang_major__ < 5) && !defined(__INTEL_COMPILER) -#define SUPPORT_DELETED_FUNCTION +#if defined(__clang__) || defined(_MSC_VER) +#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT #endif NS_PROJ_START @@ -96,7 +97,7 @@ template inline To down_cast(From *f) { /* Borrowed from C++14 */ template -std::unique_ptr make_unique(Args &&... args) { +std::unique_ptr make_unique(Args &&...args) { return std::unique_ptr(new T(std::forward(args)...)); } @@ -127,7 +128,7 @@ inline bool starts_with(const std::string &str, const char *prefix) noexcept { bool ci_less(const std::string &a, const std::string &b) noexcept; -bool ci_starts_with(const char *str, const char *prefix) noexcept; +PROJ_DLL bool ci_starts_with(const char *str, const char *prefix) noexcept; bool ci_starts_with(const std::string &str, const std::string &prefix) noexcept; @@ -145,9 +146,7 @@ PROJ_FOR_TEST std::vector split(const std::string &osStr, bool ci_equal(const char *a, const char *b) noexcept; -#ifdef SUPPORT_DELETED_FUNCTION bool ci_equal(const char *a, const std::string &b) = delete; -#endif PROJ_FOR_TEST bool ci_equal(const std::string &a, const char *b) noexcept; @@ -163,18 +162,16 @@ PROJ_FOR_TEST std::string toString(double val, int precision = 15); PROJ_FOR_TEST double c_locale_stod(const std::string &s); // throw(std::invalid_argument) -#ifdef SUPPORT_DELETED_FUNCTION +// Variant of above that doesn't emit exceptions +double c_locale_stod(const std::string &s, bool &success); + std::string concat(const std::string &, const std::string &) = delete; std::string concat(const char *, const char *) = delete; -#endif std::string concat(const char *a, const std::string &b); -#ifdef SUPPORT_DELETED_FUNCTION std::string concat(const std::string &, const char *) = delete; std::string concat(const char *, const char *, const char *) = delete; std::string concat(const char *, const char *, const std::string &) = delete; -#endif std::string concat(const char *a, const std::string &b, const char *c); -#ifdef SUPPORT_DELETED_FUNCTION std::string concat(const char *, const std::string &, const std::string &) = delete; std::string concat(const std::string &, const char *, const char *) = delete; @@ -184,8 +181,6 @@ std::string concat(const std::string &, const std::string &, const char *) = delete; std::string concat(const std::string &, const std::string &, const std::string &) = delete; -#endif - } // namespace internal NS_PROJ_END diff --git a/deps/libproj/proj/include/proj/internal/io_internal.hpp b/deps/libproj/proj/include/proj/internal/io_internal.hpp index e0426b59d..4973f6870 100644 --- a/deps/libproj/proj/include/proj/internal/io_internal.hpp +++ b/deps/libproj/proj/include/proj/internal/io_internal.hpp @@ -69,6 +69,7 @@ class WKTConstants { static const std::string EXTENSION; // WKT1 only - GDAL specific static const std::string LOCAL_CS; // WKT1 only static const std::string LOCAL_DATUM; // WKT1 only + static const std::string LINUNIT; // WKT1 ESRI (ArcGIS Pro >= 2.7) // WKT2 preferred static const std::string GEODCRS; @@ -85,6 +86,7 @@ class WKTConstants { static const std::string MERIDIAN; static const std::string ORDER; static const std::string ANCHOR; + static const std::string ANCHOREPOCH; // WKT2-2019 static const std::string CONVERSION; static const std::string METHOD; static const std::string REMARK; @@ -135,7 +137,12 @@ class WKTConstants { static const std::string BASEPARAMCRS; static const std::string BASETIMECRS; static const std::string VERSION; - static const std::string GEOIDMODEL; // WKT2-2019 + static const std::string GEOIDMODEL; // WKT2-2019 + static const std::string COORDINATEMETADATA; // WKT2-2019 + static const std::string EPOCH; // WKT2-2019 + static const std::string AXISMINVALUE; // WKT2-2019 + static const std::string AXISMAXVALUE; // WKT2-2019 + static const std::string RANGEMEANING; // WKT2-2019 // WKT2 alternate (longer or shorter) static const std::string GEODETICCRS; diff --git a/deps/libproj/proj/include/proj/internal/lru_cache.hpp b/deps/libproj/proj/include/proj/internal/lru_cache.hpp index b2e997b36..e9f177c71 100644 --- a/deps/libproj/proj/include/proj/internal/lru_cache.hpp +++ b/deps/libproj/proj/include/proj/internal/lru_cache.hpp @@ -46,35 +46,34 @@ namespace lru11 { * a noop lockable concept that can be used in place of std::mutex */ class NullLock { - public: - // cppcheck-suppress functionStatic - void lock() {} - // cppcheck-suppress functionStatic - void unlock() {} - // cppcheck-suppress functionStatic - bool try_lock() { return true; } + public: + // cppcheck-suppress functionStatic + void lock() {} + // cppcheck-suppress functionStatic + void unlock() {} + // cppcheck-suppress functionStatic + bool try_lock() { return true; } }; /** * error raised when a key not in cache is passed to get() */ class KeyNotFound : public std::invalid_argument { - public: - KeyNotFound() : std::invalid_argument("key_not_found") {} - ~KeyNotFound() override; + public: + KeyNotFound() : std::invalid_argument("key_not_found") {} + ~KeyNotFound() override; }; #ifndef LRU11_DO_NOT_DEFINE_OUT_OF_CLASS_METHODS KeyNotFound::~KeyNotFound() = default; #endif -template -struct KeyValuePair { - public: - K key; - V value; +template struct KeyValuePair { + public: + K key; + V value; - KeyValuePair(const K& k, const V& v) : key(k), value(v) {} + KeyValuePair(const K &k, const V &v) : key(k), value(v) {} }; /** @@ -93,146 +92,143 @@ template >::iterator>> class Cache { - public: - typedef KeyValuePair node_type; - typedef std::list> list_type; - typedef Map map_type; - typedef Lock lock_type; - using Guard = std::lock_guard; - /** - * the max size is the hard limit of keys and (maxSize + elasticity) is the - * soft limit - * the cache is allowed to grow till maxSize + elasticity and is pruned back - * to maxSize keys - * set maxSize = 0 for an unbounded cache (but in that case, you're better off - * using a std::unordered_map - * directly anyway! :) - */ - explicit Cache(size_t maxSize = 64, size_t elasticity = 10) - : maxSize_(maxSize), elasticity_(elasticity) {} - virtual ~Cache() = default; - size_t size() const { - Guard g(lock_); - return cache_.size(); - } - bool empty() const { - Guard g(lock_); - return cache_.empty(); - } - void clear() { - Guard g(lock_); - cache_.clear(); - keys_.clear(); - } - void insert(const Key& k, const Value& v) { - Guard g(lock_); - const auto iter = cache_.find(k); - if (iter != cache_.end()) { - iter->second->value = v; - keys_.splice(keys_.begin(), keys_, iter->second); - return; + public: + typedef KeyValuePair node_type; + typedef std::list> list_type; + typedef Map map_type; + typedef Lock lock_type; + using Guard = std::lock_guard; + /** + * the max size is the hard limit of keys and (maxSize + elasticity) is the + * soft limit + * the cache is allowed to grow till maxSize + elasticity and is pruned back + * to maxSize keys + * set maxSize = 0 for an unbounded cache (but in that case, you're better + * off using a std::unordered_map directly anyway! :) + */ + explicit Cache(size_t maxSize = 64, size_t elasticity = 10) + : maxSize_(maxSize), elasticity_(elasticity) {} + virtual ~Cache() = default; + size_t size() const { + Guard g(lock_); + return cache_.size(); } - - keys_.emplace_front(k, v); - cache_[k] = keys_.begin(); - prune(); - } - bool tryGet(const Key& kIn, Value& vOut) { - Guard g(lock_); - const auto iter = cache_.find(kIn); - if (iter == cache_.end()) { - return false; + bool empty() const { + Guard g(lock_); + return cache_.empty(); + } + void clear() { + Guard g(lock_); + cache_.clear(); + keys_.clear(); + } + void insert(const Key &k, const Value &v) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter != cache_.end()) { + iter->second->value = v; + keys_.splice(keys_.begin(), keys_, iter->second); + return; + } + + keys_.emplace_front(k, v); + cache_[k] = keys_.begin(); + prune(); } - keys_.splice(keys_.begin(), keys_, iter->second); - vOut = iter->second->value; - return true; - } - /** - * The const reference returned here is only - * guaranteed to be valid till the next insert/delete - */ - const Value& get(const Key& k) { - Guard g(lock_); - const auto iter = cache_.find(k); - if (iter == cache_.end()) { - throw KeyNotFound(); + bool tryGet(const Key &kIn, Value &vOut) { + Guard g(lock_); + const auto iter = cache_.find(kIn); + if (iter == cache_.end()) { + return false; + } + keys_.splice(keys_.begin(), keys_, iter->second); + vOut = iter->second->value; + return true; } - keys_.splice(keys_.begin(), keys_, iter->second); - return iter->second->value; - } - - /** - * The const reference returned here is only - * guaranteed to be valid till the next insert/delete - */ - const Value* getPtr(const Key& k) { - Guard g(lock_); - const auto iter = cache_.find(k); - if (iter == cache_.end()) { - return nullptr; + /** + * The const reference returned here is only + * guaranteed to be valid till the next insert/delete + */ + const Value &get(const Key &k) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter == cache_.end()) { + throw KeyNotFound(); + } + keys_.splice(keys_.begin(), keys_, iter->second); + return iter->second->value; } - keys_.splice(keys_.begin(), keys_, iter->second); - return &(iter->second->value); - } - - /** - * returns a copy of the stored object (if found) - */ - Value getCopy(const Key& k) { - return get(k); - } - bool remove(const Key& k) { - Guard g(lock_); - auto iter = cache_.find(k); - if (iter == cache_.end()) { - return false; + + /** + * The const reference returned here is only + * guaranteed to be valid till the next insert/delete + */ + const Value *getPtr(const Key &k) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter == cache_.end()) { + return nullptr; + } + keys_.splice(keys_.begin(), keys_, iter->second); + return &(iter->second->value); } - keys_.erase(iter->second); - cache_.erase(iter); - return true; - } - bool contains(const Key& k) { - Guard g(lock_); - return cache_.find(k) != cache_.end(); - } - - size_t getMaxSize() const { return maxSize_; } - size_t getElasticity() const { return elasticity_; } - size_t getMaxAllowedSize() const { return maxSize_ + elasticity_; } - template - void cwalk(F& f) const { - Guard g(lock_); - std::for_each(keys_.begin(), keys_.end(), f); - } - - protected: - size_t prune() { - size_t maxAllowed = maxSize_ + elasticity_; - if (maxSize_ == 0 || cache_.size() <= maxAllowed) { /* ERO: changed < to <= */ - return 0; + + /** + * returns a copy of the stored object (if found) + */ + Value getCopy(const Key &k) { return get(k); } + bool remove(const Key &k) { + Guard g(lock_); + auto iter = cache_.find(k); + if (iter == cache_.end()) { + return false; + } + keys_.erase(iter->second); + cache_.erase(iter); + return true; } - size_t count = 0; - while (cache_.size() > maxSize_) { - cache_.erase(keys_.back().key); - keys_.pop_back(); - ++count; + bool contains(const Key &k) { + Guard g(lock_); + return cache_.find(k) != cache_.end(); } - return count; - } - - private: - // Disallow copying. - Cache(const Cache&) = delete; - Cache& operator=(const Cache&) = delete; - - mutable Lock lock_{}; - Map cache_{}; - list_type keys_{}; - size_t maxSize_; - size_t elasticity_; + + size_t getMaxSize() const { return maxSize_; } + size_t getElasticity() const { return elasticity_; } + size_t getMaxAllowedSize() const { return maxSize_ + elasticity_; } + template void cwalk(F &f) const { + Guard g(lock_); + std::for_each(keys_.begin(), keys_.end(), f); + } + + protected: + size_t prune() { + size_t maxAllowed = maxSize_ + elasticity_; + if (maxSize_ == 0 || + cache_.size() <= maxAllowed) { /* ERO: changed < to <= */ + return 0; + } + size_t count = 0; + while (cache_.size() > maxSize_) { + cache_.erase(keys_.back().key); + keys_.pop_back(); + ++count; + } + return count; + } + + private: + // Disallow copying. + Cache(const Cache &) = delete; + Cache &operator=(const Cache &) = delete; + + mutable Lock lock_{}; + Map cache_{}; + list_type keys_{}; + size_t maxSize_; + size_t elasticity_; }; -} // namespace LRUCache11 +} // namespace lru11 NS_PROJ_END /*! @endcond */ diff --git a/deps/libproj/proj/include/proj/internal/vendor/nlohmann/json.hpp b/deps/libproj/proj/include/proj/internal/vendor/nlohmann/json.hpp index a70aaf8cb..85f16dbe8 100644 --- a/deps/libproj/proj/include/proj/internal/vendor/nlohmann/json.hpp +++ b/deps/libproj/proj/include/proj/internal/vendor/nlohmann/json.hpp @@ -5142,7 +5142,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) } // This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitely casted +// It's required so that expressions like {ptr, len} can be implicitly casted // to the correct adapter. class span_input_adapter { @@ -9916,7 +9916,7 @@ class binary_reader @return whether conversion completed - @note This function needs to respect the system's endianess, because + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -10088,7 +10088,7 @@ class binary_reader /// the number of characters read std::size_t chars_read = 0; - /// whether we can assume little endianess + /// whether we can assume little endianness const bool is_little_endian = little_endianess(); /// the SAX parser @@ -14227,7 +14227,7 @@ class binary_writer @tparam OutputIsLittleEndian Set to true if output data is required to be little endian - @note This function needs to respect the system's endianess, because bytes + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -14310,7 +14310,7 @@ class binary_writer } private: - /// whether we can assume little endianess + /// whether we can assume little endianness const bool is_little_endian = little_endianess(); /// the output @@ -17385,7 +17385,7 @@ class basic_json - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. - The subtype is then added as singed 8-bit integer. + The subtype is then added as signed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. @@ -21337,7 +21337,7 @@ class basic_json `key()` returns an empty string. @warning Using `items()` on temporary objects is dangerous. Make sure the - object's lifetime exeeds the iteration. See + object's lifetime exceeds the iteration. See for more information. diff --git a/deps/libproj/proj/include/proj/io.hpp b/deps/libproj/proj/include/proj/io.hpp index 235688cbb..26ac8612f 100644 --- a/deps/libproj/proj/include/proj/io.hpp +++ b/deps/libproj/proj/include/proj/io.hpp @@ -247,6 +247,9 @@ class PROJ_GCC_DLL WKTFormatter { setAllowEllipsoidalHeightAsVerticalCRS(bool allow) noexcept; PROJ_DLL bool isAllowedEllipsoidalHeightAsVerticalCRS() const noexcept; + PROJ_DLL WKTFormatter &setAllowLINUNITNode(bool allow) noexcept; + PROJ_DLL bool isAllowedLINUNITNode() const noexcept; + PROJ_DLL const std::string &toString() const; PROJ_PRIVATE : @@ -311,6 +314,10 @@ class PROJ_GCC_DLL WKTFormatter { PROJ_INTERNAL void setHDatumExtension(const std::string &filename); PROJ_INTERNAL const std::string &getHDatumExtension() const; + PROJ_INTERNAL void + setGeogCRSOfCompoundCRS(const crs::GeographicCRSPtr &crs); + PROJ_INTERNAL const crs::GeographicCRSPtr &getGeogCRSOfCompoundCRS() const; + PROJ_INTERNAL static std::string morphNameToESRI(const std::string &name); #ifdef unused @@ -440,12 +447,18 @@ class PROJ_GCC_DLL PROJStringFormatter { PROJ_INTERNAL void setTOWGS84Parameters(const std::vector ¶ms); PROJ_INTERNAL const std::vector &getTOWGS84Parameters() const; - PROJ_INTERNAL void setVDatumExtension(const std::string &filename); + PROJ_INTERNAL void setVDatumExtension(const std::string &filename, + const std::string &geoidCRSValue); PROJ_INTERNAL const std::string &getVDatumExtension() const; + PROJ_INTERNAL const std::string &getGeoidCRSValue() const; PROJ_INTERNAL void setHDatumExtension(const std::string &filename); PROJ_INTERNAL const std::string &getHDatumExtension() const; + PROJ_INTERNAL void + setGeogCRSOfCompoundCRS(const crs::GeographicCRSPtr &crs); + PROJ_INTERNAL const crs::GeographicCRSPtr &getGeogCRSOfCompoundCRS() const; + PROJ_INTERNAL void setOmitProjLongLatIfPossible(bool omit); PROJ_INTERNAL bool omitProjLongLatIfPossible() const; @@ -466,6 +479,8 @@ class PROJ_GCC_DLL PROJStringFormatter { PROJ_INTERNAL Convention convention() const; + PROJ_INTERNAL size_t getStepCount() const; + //! @endcond protected: @@ -538,13 +553,16 @@ class PROJ_GCC_DLL JSONFormatter { PROJ_INTERNAL void setAbridgedTransformation(bool abriged); PROJ_INTERNAL bool abridgedTransformation() const; + PROJ_INTERNAL void setAbridgedTransformationWriteSourceCRS(bool writeCRS); + PROJ_INTERNAL bool abridgedTransformationWriteSourceCRS() const; + // cppcheck-suppress functionStatic PROJ_INTERNAL bool outputId() const; PROJ_INTERNAL bool outputUsage(bool calledBeforeObjectContext = false) const; - PROJ_INTERNAL static const char *PROJJSON_v0_4; + PROJ_INTERNAL static const char *PROJJSON_v0_7; //! @endcond @@ -579,7 +597,7 @@ class PROJ_GCC_DLL IJSONExportable { PROJ_INTERNAL virtual void _exportToJSON( JSONFormatter *formatter) const = 0; // throw(FormattingException) - //! @endcond + //! @endcond }; // --------------------------------------------------------------------------- @@ -632,7 +650,7 @@ class PROJ_GCC_DLL IWKTExportable { PROJ_INTERNAL virtual void _exportToWKT( WKTFormatter *formatter) const = 0; // throw(FormattingException) - //! @endcond + //! @endcond }; // --------------------------------------------------------------------------- @@ -698,7 +716,7 @@ class PROJ_GCC_DLL IPROJStringExportable { PROJ_INTERNAL virtual void _exportToPROJString(PROJStringFormatter *formatter) const = 0; // throw(FormattingException) - //! @endcond + //! @endcond }; // --------------------------------------------------------------------------- @@ -763,6 +781,8 @@ class PROJ_GCC_DLL WKTParser { PROJ_DLL WKTParser &setStrict(bool strict); PROJ_DLL std::list warningList() const; + PROJ_DLL WKTParser &setUnsetIdentifiersIfIncompatibleDef(bool unset); + PROJ_DLL util::BaseObjectNNPtr createFromWKT(const std::string &wkt); // throw(ParsingException) @@ -953,7 +973,7 @@ using AuthorityFactoryNNPtr = util::nn; * A AuthorityFactory should be used only by one thread at a time. * * \remark Implements [AuthorityFactory] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/AuthorityFactory.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/referencing/AuthorityFactory.html) * from \ref GeoAPI */ class PROJ_GCC_DLL AuthorityFactory { diff --git a/deps/libproj/proj/include/proj/metadata.hpp b/deps/libproj/proj/include/proj/metadata.hpp index 605ad1914..42b392549 100644 --- a/deps/libproj/proj/include/proj/metadata.hpp +++ b/deps/libproj/proj/include/proj/metadata.hpp @@ -58,7 +58,7 @@ namespace metadata { * A citation contains a title. * * \remark Simplified version of [Citation] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/citation/Citation.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/citation/Citation.html) * from \ref GeoAPI */ class PROJ_GCC_DLL Citation : public util::BaseObject { @@ -67,7 +67,7 @@ class PROJ_GCC_DLL Citation : public util::BaseObject { //! @cond Doxygen_Suppress PROJ_DLL Citation(); PROJ_DLL Citation(const Citation &other); - PROJ_DLL ~Citation(); + PROJ_DLL ~Citation() override; //! @endcond PROJ_DLL const util::optional &title() PROJ_PURE_DECL; @@ -91,7 +91,7 @@ using GeographicExtentNNPtr = util::nn; /** \brief Base interface for geographic area of the dataset. * * \remark Simplified version of [GeographicExtent] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/extent/GeographicExtent.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/extent/GeographicExtent.html) * from \ref GeoAPI */ class PROJ_GCC_DLL GeographicExtent : public util::BaseObject, @@ -145,7 +145,7 @@ using GeographicBoundingBoxNNPtr = util::nn; * unnecessary. * * \remark Implements [GeographicBoundingBox] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/extent/GeographicBoundingBox.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/extent/GeographicBoundingBox.html) * from \ref GeoAPI */ class PROJ_GCC_DLL GeographicBoundingBox : public GeographicExtent { @@ -199,7 +199,7 @@ using TemporalExtentNNPtr = util::nn; /** \brief Time period covered by the content of the dataset. * * \remark Simplified version of [TemporalExtent] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/extent/TemporalExtent.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/extent/TemporalExtent.html) * from \ref GeoAPI */ class PROJ_GCC_DLL TemporalExtent : public util::BaseObject, @@ -247,7 +247,7 @@ using VerticalExtentNNPtr = util::nn; /** \brief Vertical domain of dataset. * * \remark Simplified version of [VerticalExtent] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/extent/VerticalExtent.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/extent/VerticalExtent.html) * from \ref GeoAPI */ class PROJ_GCC_DLL VerticalExtent : public util::BaseObject, @@ -297,7 +297,7 @@ using ExtentNNPtr = util::nn; /** \brief Information about spatial, vertical, and temporal extent. * * \remark Simplified version of [Extent] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/extent/Extent.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/extent/Extent.html) * from \ref GeoAPI */ class PROJ_GCC_DLL Extent : public util::BaseObject, public util::IComparable { @@ -440,7 +440,7 @@ using PositionalAccuracyNNPtr = util::nn; /** \brief Accuracy of the position of features. * * \remark Simplified version of [PositionalAccuracy] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/metadata/quality/PositionalAccuracy.html) + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/metadata/quality/PositionalAccuracy.html) * from \ref GeoAPI, which originates from \ref ISO_19115 */ class PROJ_GCC_DLL PositionalAccuracy : public util::BaseObject { diff --git a/deps/libproj/proj/include/proj/util.hpp b/deps/libproj/proj/include/proj/util.hpp index abe289e9c..de7357a3c 100644 --- a/deps/libproj/proj/include/proj/util.hpp +++ b/deps/libproj/proj/include/proj/util.hpp @@ -33,7 +33,7 @@ #error Must have C++11 or newer. #endif -// windows.h can confict with Criterion::STRICT +// windows.h can conflict with Criterion::STRICT #ifdef STRICT #undef STRICT #endif @@ -130,11 +130,11 @@ namespace proj {} // to be able to call make_shared on a protected/private constructor #define INLINED_MAKE_SHARED \ template \ - static std::shared_ptr make_shared(Args &&... args) { \ + static std::shared_ptr make_shared(Args &&...args) { \ return std::shared_ptr(new T(std::forward(args)...)); \ } \ template \ - static util::nn_shared_ptr nn_make_shared(Args &&... args) { \ + static util::nn_shared_ptr nn_make_shared(Args &&...args) { \ return util::nn_shared_ptr( \ util::i_promise_i_checked_for_null, \ std::shared_ptr(new T(std::forward(args)...))); \ @@ -144,7 +144,7 @@ namespace proj {} // to be able to call make_unique on a protected/private constructor #define INLINED_MAKE_UNIQUE \ template \ - static std::unique_ptr make_unique(Args &&... args) { \ + static std::unique_ptr make_unique(Args &&...args) { \ return std::unique_ptr(new T(std::forward(args)...)); \ } @@ -556,8 +556,8 @@ using GenericNameNNPtr = util::nn; /** \brief A sequence of identifiers rooted within the context of a namespace. * * \remark Simplified version of [GenericName] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/util/GenericName.html) from - * \ref GeoAPI + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/util/GenericName.html) + * from \ref GeoAPI */ class GenericName : public BaseObject { public: @@ -591,8 +591,8 @@ class GenericName : public BaseObject { /** \brief A domain in which names given by strings are defined. * * \remark Simplified version of [NameSpace] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/util/NameSpace.html) from \ref - * GeoAPI + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/util/NameSpace.html) + * from \ref GeoAPI */ class NameSpace { public: @@ -628,8 +628,8 @@ class NameSpace { * NameSpace within which they are local, indicated by the scope. * * \remark Simplified version of [LocalName] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/util/LocalName.html) from \ref - * GeoAPI + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/util/LocalName.html) + * from \ref GeoAPI */ class LocalName : public GenericName { public: @@ -659,8 +659,8 @@ class LocalName : public GenericName { /** \brief Factory for generic names. * * \remark Simplified version of [NameFactory] - * (http://www.geoapi.org/3.0/javadoc/org/opengis/util/NameFactory.html) from - * \ref GeoAPI + * (http://www.geoapi.org/3.0/javadoc/org.opengis.geoapi/org/opengis/util/NameFactory.html) + * from \ref GeoAPI */ class NameFactory { public: diff --git a/deps/libproj/proj/src/4D_api.cpp b/deps/libproj/proj/src/4D_api.cpp index a7baaf93e..d112a657d 100644 --- a/deps/libproj/proj/src/4D_api.cpp +++ b/deps/libproj/proj/src/4D_api.cpp @@ -42,13 +42,14 @@ #include #include +#include "filemanager.hpp" +#include "geodesic.h" +#include "grids.hpp" #include "proj.h" #include "proj_experimental.h" #include "proj_internal.h" +#include /* for isnan */ #include -#include "geodesic.h" -#include "grids.hpp" -#include "filemanager.hpp" #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" @@ -58,7 +59,7 @@ using namespace NS_PROJ::internal; /* Initialize PJ_COORD struct */ -PJ_COORD proj_coord (double x, double y, double z, double t) { +PJ_COORD proj_coord(double x, double y, double z, double t) { PJ_COORD res; res.v[0] = x; res.v[1] = y; @@ -72,187 +73,275 @@ static PJ_DIRECTION opposite_direction(PJ_DIRECTION dir) { } /*****************************************************************************/ -int proj_angular_input (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P expects angular input coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - if (PJ_FWD==dir) - return pj_left (P)==PJ_IO_UNITS_RADIANS; - return pj_right (P)==PJ_IO_UNITS_RADIANS; +int proj_angular_input(PJ *P, enum PJ_DIRECTION dir) { + /****************************************************************************** + Returns 1 if the operator P expects angular input coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} + ******************************************************************************/ + if (PJ_FWD == dir) + return pj_left(P) == PJ_IO_UNITS_RADIANS; + return pj_right(P) == PJ_IO_UNITS_RADIANS; } /*****************************************************************************/ -int proj_angular_output (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P provides angular output coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - return proj_angular_input (P, opposite_direction(dir)); +int proj_angular_output(PJ *P, enum PJ_DIRECTION dir) { + /****************************************************************************** + Returns 1 if the operator P provides angular output coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} + ******************************************************************************/ + return proj_angular_input(P, opposite_direction(dir)); } /*****************************************************************************/ -int proj_degree_input (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P expects degree input coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - if (PJ_FWD==dir) - return pj_left (P)==PJ_IO_UNITS_DEGREES; - return pj_right (P)==PJ_IO_UNITS_DEGREES; +int proj_degree_input(PJ *P, enum PJ_DIRECTION dir) { + /****************************************************************************** + Returns 1 if the operator P expects degree input coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} + ******************************************************************************/ + if (PJ_FWD == dir) + return pj_left(P) == PJ_IO_UNITS_DEGREES; + return pj_right(P) == PJ_IO_UNITS_DEGREES; } /*****************************************************************************/ -int proj_degree_output (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P provides degree output coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - return proj_degree_input (P, opposite_direction(dir)); +int proj_degree_output(PJ *P, enum PJ_DIRECTION dir) { + /****************************************************************************** + Returns 1 if the operator P provides degree output coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} + ******************************************************************************/ + return proj_degree_input(P, opposite_direction(dir)); } -/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the ellipsoid */ -PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b) { +/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the + * ellipsoid */ +PJ_COORD proj_geod(const PJ *P, PJ_COORD a, PJ_COORD b) { PJ_COORD c; - if( !P->geod ) { + if (!P->geod) { return proj_coord_error(); } /* Note: the geodesic code takes arguments in degrees */ - geod_inverse (P->geod, - PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), - PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), - c.v, c.v+1, c.v+2 - ); + geod_inverse(P->geod, PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), c.v, c.v + 1, + c.v + 2); // cppcheck-suppress uninitvar return c; } - -/* Geodesic distance (in meter) between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { +/* Geodesic distance (in meter) between two points with angular 2D coordinates + */ +double proj_lp_dist(const PJ *P, PJ_COORD a, PJ_COORD b) { double s12, azi1, azi2; /* Note: the geodesic code takes arguments in degrees */ - if( !P->geod ) { + if (!P->geod) { return HUGE_VAL; } - geod_inverse (P->geod, - PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), - PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), - &s12, &azi1, &azi2 - ); + geod_inverse(P->geod, PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), &s12, &azi1, &azi2); return s12; } /* The geodesic distance AND the vertical offset */ -double proj_lpz_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { - if (HUGE_VAL==a.lpz.lam || HUGE_VAL==b.lpz.lam) +double proj_lpz_dist(const PJ *P, PJ_COORD a, PJ_COORD b) { + if (HUGE_VAL == a.lpz.lam || HUGE_VAL == b.lpz.lam) return HUGE_VAL; - return hypot (proj_lp_dist (P, a, b), a.lpz.z - b.lpz.z); + return hypot(proj_lp_dist(P, a, b), a.lpz.z - b.lpz.z); } /* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (PJ_COORD a, PJ_COORD b) { - return hypot (a.xy.x - b.xy.x, a.xy.y - b.xy.y); +double proj_xy_dist(PJ_COORD a, PJ_COORD b) { + return hypot(a.xy.x - b.xy.x, a.xy.y - b.xy.y); } /* Euclidean distance between two points with linear 3D coordinates */ -double proj_xyz_dist (PJ_COORD a, PJ_COORD b) { - return hypot (proj_xy_dist (a, b), a.xyz.z - b.xyz.z); +double proj_xyz_dist(PJ_COORD a, PJ_COORD b) { + return hypot(proj_xy_dist(a, b), a.xyz.z - b.xyz.z); } +static bool inline coord_is_all_nans(PJ_COORD coo) { + return std::isnan(coo.v[0]) && std::isnan(coo.v[1]) && + std::isnan(coo.v[2]) && std::isnan(coo.v[3]); +} +static bool inline coord_has_nans(PJ_COORD coo) { + return std::isnan(coo.v[0]) || std::isnan(coo.v[1]) || + std::isnan(coo.v[2]) || std::isnan(coo.v[3]); +} /* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ -double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) { +double proj_roundtrip(PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) { int i; PJ_COORD t, org; - if (nullptr==P) + if (nullptr == P) return HUGE_VAL; if (n < 1) { proj_log_error(P, _("n should be >= 1")); - proj_errno_set (P, PROJ_ERR_OTHER_API_MISUSE); + proj_errno_set(P, PROJ_ERR_OTHER_API_MISUSE); return HUGE_VAL; } /* in the first half-step, we generate the output value */ - org = *coord; - *coord = proj_trans (P, direction, org); + org = *coord; + *coord = proj_trans(P, direction, org); t = *coord; /* now we take n-1 full steps in inverse direction: We are */ /* out of phase due to the half step already taken */ - for (i = 0; i < n - 1; i++) - t = proj_trans (P, direction, proj_trans (P, opposite_direction(direction), t) ); + for (i = 0; i < n - 1; i++) + t = proj_trans(P, direction, + proj_trans(P, opposite_direction(direction), t)); /* finally, we take the last half-step */ - t = proj_trans (P, opposite_direction(direction), t); + t = proj_trans(P, opposite_direction(direction), t); + + /* if we start with any NaN, we expect all NaN as output */ + if (coord_has_nans(org) && coord_is_all_nans(t)) { + return 0.0; + } - /* checking for angular *input* since we do a roundtrip, and end where we begin */ - if (proj_angular_input (P, direction)) - return proj_lpz_dist (P, org, t); + /* checking for angular *input* since we do a roundtrip, and end where we + * begin */ + if (proj_angular_input(P, direction)) + return proj_lpz_dist(P, org, t); - return proj_xyz_dist (org, t); + return proj_xyz_dist(org, t); } /**************************************************************************************/ -int pj_get_suggested_operation(PJ_CONTEXT*, - const std::vector& opList, - const int iExcluded[2], - PJ_DIRECTION direction, - PJ_COORD coord) +int pj_get_suggested_operation(PJ_CONTEXT *, + const std::vector &opList, + const int iExcluded[2], bool skipNonInstantiable, + PJ_DIRECTION direction, PJ_COORD coord) /**************************************************************************************/ { + const auto normalizeLongitude = [](double x) { + if (x > 180.0) { + x -= 360.0; + if (x > 180.0) + x = fmod(x + 180.0, 360.0) - 180.0; + } else if (x < -180.0) { + x += 360.0; + if (x < -180.0) + x = fmod(x + 180.0, 360.0) - 180.0; + } + return x; + }; + // Select the operations that match the area of use // and has the best accuracy. int iBest = -1; double bestAccuracy = std::numeric_limits::max(); const int nOperations = static_cast(opList.size()); - for( int i = 0; i < nOperations; i++ ) { - if( i == iExcluded[0] || i == iExcluded[1] ) { + for (int i = 0; i < nOperations; i++) { + if (i == iExcluded[0] || i == iExcluded[1]) { continue; } const auto &alt = opList[i]; bool spatialCriterionOK = false; - if( direction == PJ_FWD ) { - if( coord.xyzt.x >= alt.minxSrc && - coord.xyzt.y >= alt.minySrc && - coord.xyzt.x <= alt.maxxSrc && - coord.xyzt.y <= alt.maxySrc) { + if (direction == PJ_FWD) { + if (alt.pjSrcGeocentricToLonLat) { + if (alt.minxSrc == -180 && alt.minySrc == -90 && + alt.maxxSrc == 180 && alt.maxySrc == 90) { + spatialCriterionOK = true; + } else { + PJ_COORD tmp = coord; + pj_fwd4d(tmp, alt.pjSrcGeocentricToLonLat); + if (tmp.xyzt.x >= alt.minxSrc && + tmp.xyzt.y >= alt.minySrc && + tmp.xyzt.x <= alt.maxxSrc && + tmp.xyzt.y <= alt.maxySrc) { + spatialCriterionOK = true; + } + } + } else if (coord.xyzt.x >= alt.minxSrc && + coord.xyzt.y >= alt.minySrc && + coord.xyzt.x <= alt.maxxSrc && + coord.xyzt.y <= alt.maxySrc) { spatialCriterionOK = true; + } else if (alt.srcIsLonLatDegree && coord.xyzt.y >= alt.minySrc && + coord.xyzt.y <= alt.maxySrc) { + const double normalizedLon = normalizeLongitude(coord.xyzt.x); + if (normalizedLon >= alt.minxSrc && + normalizedLon <= alt.maxxSrc) { + spatialCriterionOK = true; + } + } else if (alt.srcIsLatLonDegree && coord.xyzt.x >= alt.minxSrc && + coord.xyzt.x <= alt.maxxSrc) { + const double normalizedLon = normalizeLongitude(coord.xyzt.y); + if (normalizedLon >= alt.minySrc && + normalizedLon <= alt.maxySrc) { + spatialCriterionOK = true; + } } } else { - if( coord.xyzt.x >= alt.minxDst && - coord.xyzt.y >= alt.minyDst && - coord.xyzt.x <= alt.maxxDst && - coord.xyzt.y <= alt.maxyDst ) { + if (alt.pjDstGeocentricToLonLat) { + if (alt.minxDst == -180 && alt.minyDst == -90 && + alt.maxxDst == 180 && alt.maxyDst == 90) { + spatialCriterionOK = true; + } else { + PJ_COORD tmp = coord; + pj_fwd4d(tmp, alt.pjDstGeocentricToLonLat); + if (tmp.xyzt.x >= alt.minxDst && + tmp.xyzt.y >= alt.minyDst && + tmp.xyzt.x <= alt.maxxDst && + tmp.xyzt.y <= alt.maxyDst) { + spatialCriterionOK = true; + } + } + } else if (coord.xyzt.x >= alt.minxDst && + coord.xyzt.y >= alt.minyDst && + coord.xyzt.x <= alt.maxxDst && + coord.xyzt.y <= alt.maxyDst) { spatialCriterionOK = true; + } else if (alt.dstIsLonLatDegree && coord.xyzt.y >= alt.minyDst && + coord.xyzt.y <= alt.maxyDst) { + const double normalizedLon = normalizeLongitude(coord.xyzt.x); + if (normalizedLon >= alt.minxDst && + normalizedLon <= alt.maxxDst) { + spatialCriterionOK = true; + } + } else if (alt.dstIsLatLonDegree && coord.xyzt.x >= alt.minxDst && + coord.xyzt.x <= alt.maxxDst) { + const double normalizedLon = normalizeLongitude(coord.xyzt.y); + if (normalizedLon >= alt.minyDst && + normalizedLon <= alt.maxyDst) { + spatialCriterionOK = true; + } } } - if( spatialCriterionOK ) { + if (spatialCriterionOK) { // The offshore test is for the "Test bug 245 (use +datum=carthage)" // of testvarious. The long=10 lat=34 point belongs both to the // onshore and offshore Tunisia area of uses, but is slightly // onshore. So in a general way, prefer a onshore area to a // offshore one. - if( iBest < 0 || - (alt.accuracy >= 0 && - (alt.accuracy < bestAccuracy || - // If two operations have the same accuracy, use the one that - // is contained within a larger one - (alt.accuracy == bestAccuracy && - alt.minxSrc > opList[iBest].minxSrc && - alt.minySrc > opList[iBest].minySrc && - alt.maxxSrc < opList[iBest].maxxSrc && - alt.maxySrc < opList[iBest].maxySrc)) && - !alt.isOffshore) ) { + if (iBest < 0 || (alt.accuracy >= 0 && + (alt.accuracy < bestAccuracy || + // If two operations have the same accuracy, use + // the one that is contained within a larger one + (alt.accuracy == bestAccuracy && + alt.minxSrc >= opList[iBest].minxSrc && + alt.minySrc >= opList[iBest].minySrc && + alt.maxxSrc <= opList[iBest].maxxSrc && + alt.maxySrc <= opList[iBest].maxySrc && + // check that this is not equality + !(alt.minxSrc == opList[iBest].minxSrc && + alt.minySrc == opList[iBest].minySrc && + alt.maxxSrc == opList[iBest].maxxSrc && + alt.maxySrc == opList[iBest].maxySrc) && + !opList[iBest].isPriorityOp)) && + !alt.isOffshore)) { + + if (skipNonInstantiable && !alt.isInstantiable()) { + continue; + } iBest = i; bestAccuracy = alt.accuracy; } @@ -262,56 +351,116 @@ int pj_get_suggested_operation(PJ_CONTEXT*, return iBest; } +//! @cond Doxygen_Suppress +/**************************************************************************************/ +PJCoordOperation::~PJCoordOperation() { + /**************************************************************************************/ + proj_destroy(pj); + proj_destroy(pjSrcGeocentricToLonLat); + proj_destroy(pjDstGeocentricToLonLat); +} + +/**************************************************************************************/ +bool PJCoordOperation::isInstantiable() const { + /**************************************************************************************/ + if (isInstantiableCached == INSTANTIABLE_STATUS_UNKNOWN) + isInstantiableCached = proj_coordoperation_is_instantiable(pj->ctx, pj); + return (isInstantiableCached == 1); +} +//! @endcond + +/**************************************************************************************/ +static void warnAboutMissingGrid(PJ *P) +/**************************************************************************************/ +{ + std::string msg("Attempt to use coordinate operation "); + msg += proj_get_name(P); + msg += " failed."; + int gridUsed = proj_coordoperation_get_grid_used_count(P->ctx, P); + for (int i = 0; i < gridUsed; ++i) { + const char *gridName = ""; + int available = FALSE; + if (proj_coordoperation_get_grid_used(P->ctx, P, i, &gridName, nullptr, + nullptr, nullptr, nullptr, + nullptr, &available) && + !available) { + msg += " Grid "; + msg += gridName; + msg += " is not available. " + "Consult https://proj.org/resource_files.html for guidance."; + } + } + if (!P->errorIfBestTransformationNotAvailable && + P->warnIfBestTransformationNotAvailable) { + msg += " This might become an error in a future PROJ major release. " + "Set the ONLY_BEST option to YES or NO. " + "This warning will no longer be emitted (for the current " + "transformation instance)."; + P->warnIfBestTransformationNotAvailable = false; + } + pj_log(P->ctx, + P->errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR + : PJ_LOG_DEBUG, + msg.c_str()); +} + /**************************************************************************************/ -PJ_COORD proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { -/*************************************************************************************** -Apply the transformation P to the coordinate coord, preferring the 4D interfaces if -available. - -See also pj_approx_2D_trans and pj_approx_3D_trans in pj_internal.c, which work -similarly, but prefers the 2D resp. 3D interfaces if available. -***************************************************************************************/ - if (nullptr==P || direction == PJ_IDENT) +PJ_COORD proj_trans(PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { + /*************************************************************************************** + Apply the transformation P to the coordinate coord, preferring the 4D + interfaces if available. + + See also pj_approx_2D_trans and pj_approx_3D_trans in pj_internal.c, which + work similarly, but prefers the 2D resp. 3D interfaces if available. + ***************************************************************************************/ + if (nullptr == P || direction == PJ_IDENT) return coord; if (P->inverted) direction = opposite_direction(direction); - if( !P->alternativeCoordinateOperations.empty() ) { + if (P->iso_obj != nullptr && !P->iso_obj_is_coordinate_operation) { + pj_log(P->ctx, PJ_LOG_ERROR, "Object is not a coordinate operation"); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + return proj_coord_error(); + } + + if (!P->alternativeCoordinateOperations.empty()) { constexpr int N_MAX_RETRY = 2; int iExcluded[N_MAX_RETRY] = {-1, -1}; - const int nOperations = static_cast( - P->alternativeCoordinateOperations.size()); + bool skipNonInstantiable = P->skipNonInstantiable && + !P->warnIfBestTransformationNotAvailable && + !P->errorIfBestTransformationNotAvailable; + const int nOperations = + static_cast(P->alternativeCoordinateOperations.size()); // We may need several attempts. For example the point at - // lon=-111.5 lat=45.26 falls into the bounding box of the Canadian + // long=-111.5 lat=45.26 falls into the bounding box of the Canadian // ntv2_0.gsb grid, except that it is not in any of the subgrids, being // in the US. We thus need another retry that will select the conus // grid. - for( int iRetry = 0; iRetry <= N_MAX_RETRY; iRetry++ ) - { - // Do a first pass and select the operations that match the area of use - // and has the best accuracy. - int iBest = pj_get_suggested_operation(P->ctx, - P->alternativeCoordinateOperations, - iExcluded, - direction, - coord); - if( iBest < 0 ) { + for (int iRetry = 0; iRetry <= N_MAX_RETRY; iRetry++) { + // Do a first pass and select the operations that match the area of + // use and has the best accuracy. + int iBest = pj_get_suggested_operation( + P->ctx, P->alternativeCoordinateOperations, iExcluded, + skipNonInstantiable, direction, coord); + if (iBest < 0) { break; } - if( iRetry > 0 ) { + if (iRetry > 0) { const int oldErrno = proj_errno_reset(P); if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - pj_log(P->ctx, PJ_LOG_DEBUG, proj_context_errno_string(P->ctx, oldErrno)); + pj_log(P->ctx, PJ_LOG_DEBUG, + proj_context_errno_string(P->ctx, oldErrno)); } pj_log(P->ctx, PJ_LOG_DEBUG, - "Did not result in valid result. " - "Attempting a retry with another operation."); + "Did not result in valid result. " + "Attempting a retry with another operation."); } - const auto& alt = P->alternativeCoordinateOperations[iBest]; - if( P->iCurCoordOp != iBest ) { + const auto &alt = P->alternativeCoordinateOperations[iBest]; + if (P->iCurCoordOp != iBest) { if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { std::string msg("Using coordinate operation "); msg += alt.name; @@ -319,15 +468,29 @@ similarly, but prefers the 2D resp. 3D interfaces if available. } P->iCurCoordOp = iBest; } - PJ_COORD res = direction == PJ_FWD ? - pj_fwd4d( coord, alt.pj ) : pj_inv4d( coord, alt.pj ); - if( proj_errno(alt.pj) == PROJ_ERR_OTHER_NETWORK_ERROR ) { - return proj_coord_error (); + PJ_COORD res = coord; + if (alt.pj->hasCoordinateEpoch) + coord.xyzt.t = alt.pj->coordinateEpoch; + if (direction == PJ_FWD) + pj_fwd4d(res, alt.pj); + else + pj_inv4d(res, alt.pj); + if (proj_errno(alt.pj) == PROJ_ERR_OTHER_NETWORK_ERROR) { + return proj_coord_error(); } - if( res.xyzt.x != HUGE_VAL ) { + if (res.xyzt.x != HUGE_VAL) { return res; + } else if (P->errorIfBestTransformationNotAvailable || + P->warnIfBestTransformationNotAvailable) { + warnAboutMissingGrid(alt.pj); + if (P->errorIfBestTransformationNotAvailable) { + proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_NO_OPERATION); + return res; + } + P->warnIfBestTransformationNotAvailable = false; + skipNonInstantiable = true; } - if( iRetry == N_MAX_RETRY ) { + if (iRetry == N_MAX_RETRY) { break; } iExcluded[iRetry] = iBest; @@ -337,21 +500,23 @@ similarly, but prefers the 2D resp. 3D interfaces if available. // with the input coordinate, then goes through again the list, and // use the first operation that does not require grids. NS_PROJ::io::DatabaseContextPtr dbContext; - try - { - if( P->ctx->cpp_context ) { - dbContext = P->ctx->cpp_context->getDatabaseContext().as_nullable(); + try { + if (P->ctx->cpp_context) { + dbContext = + P->ctx->cpp_context->getDatabaseContext().as_nullable(); } + } catch (const std::exception &) { } - catch( const std::exception& ) {} - for( int i = 0; i < nOperations; i++ ) { + for (int i = 0; i < nOperations; i++) { const auto &alt = P->alternativeCoordinateOperations[i]; - auto coordOperation = dynamic_cast< - NS_PROJ::operation::CoordinateOperation*>(alt.pj->iso_obj.get()); - if( coordOperation ) { - if( coordOperation->gridsNeeded(dbContext, true).empty() ) { - if( P->iCurCoordOp != i ) { - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + auto coordOperation = + dynamic_cast( + alt.pj->iso_obj.get()); + if (coordOperation) { + if (coordOperation->gridsNeeded(dbContext, true).empty()) { + if (P->iCurCoordOp != i) { + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= + PJ_LOG_DEBUG) { std::string msg("Using coordinate operation "); msg += alt.name; msg += " as a fallback due to lack of more " @@ -360,175 +525,206 @@ similarly, but prefers the 2D resp. 3D interfaces if available. } P->iCurCoordOp = i; } - if( direction == PJ_FWD ) { - return pj_fwd4d( coord, alt.pj ); - } - else { - return pj_inv4d( coord, alt.pj ); + if (direction == PJ_FWD) { + pj_fwd4d(coord, alt.pj); + } else { + pj_inv4d(coord, alt.pj); } + return coord; } } } - proj_errno_set (P, PROJ_ERR_COORD_TRANSFM_NO_OPERATION); - return proj_coord_error (); + proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_NO_OPERATION); + return proj_coord_error(); } - if (direction == PJ_FWD) - return pj_fwd4d (coord, P); + P->iCurCoordOp = + 0; // dummy value, to be used by proj_trans_get_last_used_operation() + if (P->hasCoordinateEpoch) + coord.xyzt.t = P->coordinateEpoch; + if (coord_has_nans(coord)) + coord.v[0] = coord.v[1] = coord.v[2] = coord.v[3] = + std::numeric_limits::quiet_NaN(); + else if (direction == PJ_FWD) + pj_fwd4d(coord, P); else - return pj_inv4d (coord, P); + pj_inv4d(coord, P); + return coord; } - - /*****************************************************************************/ -int proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) { +PJ *proj_trans_get_last_used_operation(PJ *P) /****************************************************************************** - Batch transform an array of PJ_COORD. + Return the operation used during the last invocation of proj_trans(). + This is especially useful when P has been created with +proj_create_crs_to_crs() and has several alternative operations. The returned +object must be freed with proj_destroy(). +******************************************************************************/ +{ + if (nullptr == P || P->iCurCoordOp < 0) + return nullptr; + if (P->alternativeCoordinateOperations.empty()) + return proj_clone(P->ctx, P); + return proj_clone(P->ctx, + P->alternativeCoordinateOperations[P->iCurCoordOp].pj); +} - Performs transformation on all points, even if errors occur on some points. +/*****************************************************************************/ +int proj_trans_array(PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) { + /****************************************************************************** + Batch transform an array of PJ_COORD. - Individual points that fail to transform will have their components set to - HUGE_VAL + Performs transformation on all points, even if errors occur on some + points. - Returns 0 if all coordinates are transformed without error, otherwise - returns a precise error number if all coordinates that fail to transform - for the same reason, or a generic error code if they fail for different - reasons. -******************************************************************************/ + Individual points that fail to transform will have their components set + to HUGE_VAL + + Returns 0 if all coordinates are transformed without error, otherwise + returns a precise error number if all coordinates that fail to transform + for the same reason, or a generic error code if they fail for different + reasons. + ******************************************************************************/ size_t i; int retErrno = 0; bool hasSetRetErrno = false; bool sameRetErrno = true; - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) { proj_context_errno_set(P->ctx, 0); - coord[i] = proj_trans (P, direction, coord[i]); + coord[i] = proj_trans(P, direction, coord[i]); int thisErrno = proj_errno(P); - if( thisErrno != 0 ) - { - if( !hasSetRetErrno ) - { + if (thisErrno != 0) { + if (!hasSetRetErrno) { retErrno = thisErrno; hasSetRetErrno = true; - } - else if( sameRetErrno && retErrno != thisErrno ) - { + } else if (sameRetErrno && retErrno != thisErrno) { sameRetErrno = false; retErrno = PROJ_ERR_COORD_TRANSFM; } } - } + } - proj_context_errno_set(P->ctx, retErrno); + proj_context_errno_set(P->ctx, retErrno); - return retErrno; + return retErrno; } - - /*************************************************************************************/ -size_t proj_trans_generic ( - PJ *P, - PJ_DIRECTION direction, - double *x, size_t sx, size_t nx, - double *y, size_t sy, size_t ny, - double *z, size_t sz, size_t nz, - double *t, size_t st, size_t nt -) { -/************************************************************************************** - - Transform a series of coordinates, where the individual coordinate dimension - may be represented by an array that is either - - 1. fully populated - 2. a null pointer and/or a length of zero, which will be treated as a - fully populated array of zeroes - 3. of length one, i.e. a constant, which will be treated as a fully - populated array of that constant value - - The strides, sx, sy, sz, st, represent the step length, in bytes, between - consecutive elements of the corresponding array. This makes it possible for - proj_transform to handle transformation of a large class of application - specific data structures, without necessarily understanding the data structure - format, as in: - - typedef struct {double x, y; int quality_level; char surveyor_name[134];} XYQS; - XYQS survey[345]; - double height = 23.45; - PJ *P = {...}; - size_t stride = sizeof (XYQS); - ... - proj_transform ( - P, PJ_INV, sizeof(XYQS), - &(survey[0].x), stride, 345, (* We have 345 eastings *) - &(survey[0].y), stride, 345, (* ...and 345 northings. *) - &height, 1, (* The height is the constant 23.45 m *) - 0, 0 (* and the time is the constant 0.00 s *) - ); - - This is similar to the inner workings of the pj_transform function, but the - stride functionality has been generalized to work for any size of basic unit, - not just a fixed number of doubles. - - In most cases, the stride will be identical for x, y,z, and t, since they will - typically be either individual arrays (stride = sizeof(double)), or strided - views into an array of application specific data structures (stride = sizeof (...)). - - But in order to support cases where x, y, z, and t come from heterogeneous - sources, individual strides, sx, sy, sz, st, are used. - - Caveat: Since proj_transform does its work *in place*, this means that even the - supposedly constants (i.e. length 1 arrays) will return from the call in altered - state. Hence, remember to reinitialize between repeated calls. - - Return value: Number of transformations completed. - -**************************************************************************************/ - PJ_COORD coord = {{0,0,0,0}}; +size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction, double *x, size_t sx, + size_t nx, double *y, size_t sy, size_t ny, double *z, + size_t sz, size_t nz, double *t, size_t st, + size_t nt) { + /************************************************************************************** + + Transform a series of coordinates, where the individual coordinate + dimension may be represented by an array that is either + + 1. fully populated + 2. a null pointer and/or a length of zero, which will be treated as + a fully populated array of zeroes + 3. of length one, i.e. a constant, which will be treated as a fully + populated array of that constant value + + The strides, sx, sy, sz, st, represent the step length, in bytes, + between consecutive elements of the corresponding array. This makes it + possible for proj_transform to handle transformation of a large class of + application specific data structures, without necessarily understanding the + data structure format, as in: + + typedef struct {double x, y; int quality_level; char + surveyor_name[134];} XYQS; XYQS survey[345]; double height = 23.45; PJ *P = + {...}; size_t stride = sizeof (XYQS); + ... + proj_transform ( + P, PJ_INV, sizeof(XYQS), + &(survey[0].x), stride, 345, (* We have 345 eastings *) + &(survey[0].y), stride, 345, (* ...and 345 northings. *) + &height, 1, (* The height is the + constant 23.45 m *) 0, 0 (* and the time is the + constant 0.00 s *) + ); + + This is similar to the inner workings of the pj_transform function, but + the stride functionality has been generalized to work for any size of basic + unit, not just a fixed number of doubles. + + In most cases, the stride will be identical for x, y,z, and t, since + they will typically be either individual arrays (stride = sizeof(double)), + or strided views into an array of application specific data structures + (stride = sizeof (...)). + + But in order to support cases where x, y, z, and t come from + heterogeneous sources, individual strides, sx, sy, sz, st, are used. + + Caveat: Since proj_transform does its work *in place*, this means that + even the supposedly constants (i.e. length 1 arrays) will return from the + call in altered state. Hence, remember to reinitialize between repeated + calls. + + Return value: Number of transformations completed. + + **************************************************************************************/ + PJ_COORD coord = {{0, 0, 0, 0}}; size_t i, nmin; double null_broadcast = 0; double invalid_time = HUGE_VAL; - if (nullptr==P) + if (nullptr == P) return 0; if (P->inverted) direction = opposite_direction(direction); /* ignore lengths of null arrays */ - if (nullptr==x) nx = 0; - if (nullptr==y) ny = 0; - if (nullptr==z) nz = 0; - if (nullptr==t) nt = 0; - - /* and make the nullities point to some real world memory for broadcasting nulls */ - if (0==nx) x = &null_broadcast; - if (0==ny) y = &null_broadcast; - if (0==nz) z = &null_broadcast; - if (0==nt) t = &invalid_time; + if (nullptr == x) + nx = 0; + if (nullptr == y) + ny = 0; + if (nullptr == z) + nz = 0; + if (nullptr == t) + nt = 0; + + /* and make the nullities point to some real world memory for broadcasting + * nulls */ + if (0 == nx) + x = &null_broadcast; + if (0 == ny) + y = &null_broadcast; + if (0 == nz) + z = &null_broadcast; + if (0 == nt) + t = &invalid_time; /* nothing to do? */ - if (0==nx+ny+nz+nt) + if (0 == nx + ny + nz + nt) return 0; - /* arrays of length 1 are constants, which we broadcast along the longer arrays */ - /* so we need to find the length of the shortest non-unity array to figure out */ + /* arrays of length 1 are constants, which we broadcast along the longer + * arrays */ + /* so we need to find the length of the shortest non-unity array to figure + * out + */ /* how many coordinate pairs we must transform */ - nmin = (nx > 1)? nx: (ny > 1)? ny: (nz > 1)? nz: (nt > 1)? nt: 1; - if ((nx > 1) && (nx < nmin)) nmin = nx; - if ((ny > 1) && (ny < nmin)) nmin = ny; - if ((nz > 1) && (nz < nmin)) nmin = nz; - if ((nt > 1) && (nt < nmin)) nmin = nt; + nmin = (nx > 1) ? nx : (ny > 1) ? ny : (nz > 1) ? nz : (nt > 1) ? nt : 1; + if ((nx > 1) && (nx < nmin)) + nmin = nx; + if ((ny > 1) && (ny < nmin)) + nmin = ny; + if ((nz > 1) && (nz < nmin)) + nmin = nz; + if ((nt > 1) && (nt < nmin)) + nmin = nt; /* Check validity of direction flag */ switch (direction) { - case PJ_FWD: - case PJ_INV: - break; - case PJ_IDENT: - return nmin; + case PJ_FWD: + case PJ_INV: + break; + case PJ_IDENT: + return nmin; } /* Arrays of length==0 are broadcast as the constant 0 */ @@ -536,7 +732,7 @@ size_t proj_trans_generic ( /* Arrays of length >1 are iterated over (for the first nmin values) */ /* The slightly convolved incremental indexing is used due */ /* to the stride, which may be any size supported by the platform */ - for (i = 0; i < nmin; i++) { + for (i = 0; i < nmin; i++) { coord.xyzt.x = *x; coord.xyzt.y = *y; coord.xyzt.z = *z; @@ -548,347 +744,372 @@ size_t proj_trans_generic ( /* and step on to the next element. */ /* The casts are somewhat funky, but they compile down to no-ops and */ /* they tell compilers and static analyzers that we know what we do */ - if (nx > 1) { - *x = coord.xyzt.x; - x = (double *) ((void *) ( ((char *) x) + sx)); + if (nx > 1) { + *x = coord.xyzt.x; + x = (double *)((void *)(((char *)x) + sx)); } - if (ny > 1) { - *y = coord.xyzt.y; - y = (double *) ((void *) ( ((char *) y) + sy)); + if (ny > 1) { + *y = coord.xyzt.y; + y = (double *)((void *)(((char *)y) + sy)); } - if (nz > 1) { - *z = coord.xyzt.z; - z = (double *) ((void *) ( ((char *) z) + sz)); + if (nz > 1) { + *z = coord.xyzt.z; + z = (double *)((void *)(((char *)z) + sz)); } - if (nt > 1) { - *t = coord.xyzt.t; - t = (double *) ((void *) ( ((char *) t) + st)); + if (nt > 1) { + *t = coord.xyzt.t; + t = (double *)((void *)(((char *)t) + st)); } } - /* Last time around, we update the length 1 cases with their transformed alter egos */ - if (nx==1) + /* Last time around, we update the length 1 cases with their transformed + * alter egos */ + if (nx == 1) *x = coord.xyzt.x; - if (ny==1) + if (ny == 1) *y = coord.xyzt.y; - if (nz==1) + if (nz == 1) *z = coord.xyzt.z; - if (nt==1) + if (nt == 1) *t = coord.xyzt.t; return i; } - /*************************************************************************************/ -PJ_COORD pj_geocentric_latitude (const PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { -/************************************************************************************** - Convert geographical latitude to geocentric (or the other way round if - direction = PJ_INV) - - The conversion involves a call to the tangent function, which goes through the - roof at the poles, so very close (the last centimeter) to the poles no - conversion takes place and the input latitude is copied directly to the output. - - Fortunately, the geocentric latitude converges to the geographical at the - poles, so the difference is negligible. - - For the spherical case, the geographical latitude equals the geocentric, and - consequently, the input is copied directly to the output. -**************************************************************************************/ +PJ_COORD pj_geocentric_latitude(const PJ *P, PJ_DIRECTION direction, + PJ_COORD coord) { + /************************************************************************************** + Convert geographical latitude to geocentric (or the other way round if + direction = PJ_INV) + + The conversion involves a call to the tangent function, which goes + through the roof at the poles, so very close (the last centimeter) to the + poles no conversion takes place and the input latitude is copied directly to + the output. + + Fortunately, the geocentric latitude converges to the geographical at + the poles, so the difference is negligible. + + For the spherical case, the geographical latitude equals the geocentric, + and consequently, the input is copied directly to the output. + **************************************************************************************/ const double limit = M_HALFPI - 1e-9; PJ_COORD res = coord; - if ((coord.lp.phi > limit) || (coord.lp.phi < -limit) || (P->es==0)) + if ((coord.lp.phi > limit) || (coord.lp.phi < -limit) || (P->es == 0)) return res; - if (direction==PJ_FWD) - res.lp.phi = atan (P->one_es * tan (coord.lp.phi) ); + if (direction == PJ_FWD) + res.lp.phi = atan(P->one_es * tan(coord.lp.phi)); else - res.lp.phi = atan (P->rone_es * tan (coord.lp.phi) ); + res.lp.phi = atan(P->rone_es * tan(coord.lp.phi)); return res; } -double proj_torad (double angle_in_degrees) { return PJ_TORAD (angle_in_degrees);} -double proj_todeg (double angle_in_radians) { return PJ_TODEG (angle_in_radians);} +double proj_torad(double angle_in_degrees) { + return PJ_TORAD(angle_in_degrees); +} +double proj_todeg(double angle_in_radians) { + return PJ_TODEG(angle_in_radians); +} + +double proj_dmstor(const char *is, char **rs) { return dmstor(is, rs); } -double proj_dmstor(const char *is, char **rs) { - return dmstor(is, rs); +char *proj_rtodms(char *s, double r, int pos, int neg) { + // 40 is the size used for the buffer in proj.cpp + size_t arbitrary_size = 40; + return rtodms(s, arbitrary_size, r, pos, neg); } -char* proj_rtodms(char *s, double r, int pos, int neg) { - return rtodms(s, r, pos, neg); +char *proj_rtodms2(char *s, size_t sizeof_s, double r, int pos, int neg) { + return rtodms(s, sizeof_s, r, pos, neg); } /*************************************************************************************/ -static PJ* skip_prep_fin(PJ *P) { -/************************************************************************************** -Skip prepare and finalize function for the various "helper operations" added to P when -in cs2cs compatibility mode. -**************************************************************************************/ - P->skip_fwd_prepare = 1; +static PJ *skip_prep_fin(PJ *P) { + /************************************************************************************** + Skip prepare and finalize function for the various "helper operations" added + to P when in cs2cs compatibility mode. + **************************************************************************************/ + P->skip_fwd_prepare = 1; P->skip_fwd_finalize = 1; - P->skip_inv_prepare = 1; + P->skip_inv_prepare = 1; P->skip_inv_finalize = 1; return P; } /*************************************************************************************/ -static int cs2cs_emulation_setup (PJ *P) { -/************************************************************************************** -If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the 4D API -equivalent operations, so the preparation and finalization steps in the pj_inv/pj_fwd -invocators can emulate the behavior of pj_transform and the cs2cs app. - -Returns 1 on success, 0 on failure -**************************************************************************************/ +static int cs2cs_emulation_setup(PJ *P) { + /************************************************************************************** + If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the + 4D API equivalent operations, so the preparation and finalization steps in + the pj_inv/pj_fwd invocators can emulate the behavior of pj_transform and + the cs2cs app. + + Returns 1 on success, 0 on failure + **************************************************************************************/ PJ *Q; paralist *p; int do_cart = 0; - if (nullptr==P) + if (nullptr == P) return 0; /* Don't recurse when calling proj_create (which calls us back) */ - if (pj_param_exists (P->params, "break_cs2cs_recursion")) + if (pj_param_exists(P->params, "break_cs2cs_recursion")) return 1; /* Swap axes? */ - p = pj_param_exists (P->params, "axis"); + p = pj_param_exists(P->params, "axis"); - const bool disable_grid_presence_check = pj_param_exists ( - P->params, "disable_grid_presence_check") != nullptr; + const bool disable_grid_presence_check = + pj_param_exists(P->params, "disable_grid_presence_check") != nullptr; /* Don't axisswap if data are already in "enu" order */ - if (p && (0!=strcmp ("enu", p->param))) { - char *def = static_cast(malloc (100+strlen(P->axis))); - if (nullptr==def) + if (p && (0 != strcmp("enu", p->param))) { + size_t def_size = 100 + strlen(P->axis); + char *def = static_cast(malloc(def_size)); + if (nullptr == def) return 0; - sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); - Q = pj_create_internal (P->ctx, def); - free (def); - if (nullptr==Q) + snprintf(def, def_size, + "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); + Q = pj_create_internal(P->ctx, def); + free(def); + if (nullptr == Q) return 0; P->axisswap = skip_prep_fin(Q); } /* Geoid grid(s) given? */ - p = pj_param_exists (P->params, "geoidgrids"); - if (!disable_grid_presence_check && p && strlen (p->param) > strlen ("geoidgrids=")) { - char *gridnames = p->param + strlen ("geoidgrids="); - char *def = static_cast(malloc (100+2*strlen(gridnames))); - if (nullptr==def) + p = pj_param_exists(P->params, "geoidgrids"); + if (!disable_grid_presence_check && p && + strlen(p->param) > strlen("geoidgrids=")) { + char *gridnames = p->param + strlen("geoidgrids="); + size_t def_size = 100 + 2 * strlen(gridnames); + char *def = static_cast(malloc(def_size)); + if (nullptr == def) return 0; - sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", + snprintf(def, def_size, + "break_cs2cs_recursion proj=vgridshift grids=%s", pj_double_quote_string_param_if_needed(gridnames).c_str()); - Q = pj_create_internal (P->ctx, def); - free (def); - if (nullptr==Q) + Q = pj_create_internal(P->ctx, def); + free(def); + if (nullptr == Q) return 0; P->vgridshift = skip_prep_fin(Q); } /* Datum shift grid(s) given? */ - p = pj_param_exists (P->params, "nadgrids"); - if (!disable_grid_presence_check && p && strlen (p->param) > strlen ("nadgrids=")) { - char *gridnames = p->param + strlen ("nadgrids="); - char *def = static_cast(malloc (100+2*strlen(gridnames))); - if (nullptr==def) + p = pj_param_exists(P->params, "nadgrids"); + if (!disable_grid_presence_check && p && + strlen(p->param) > strlen("nadgrids=")) { + char *gridnames = p->param + strlen("nadgrids="); + size_t def_size = 100 + 2 * strlen(gridnames); + char *def = static_cast(malloc(def_size)); + if (nullptr == def) return 0; - sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", + snprintf(def, def_size, + "break_cs2cs_recursion proj=hgridshift grids=%s", pj_double_quote_string_param_if_needed(gridnames).c_str()); - Q = pj_create_internal (P->ctx, def); - free (def); - if (nullptr==Q) + Q = pj_create_internal(P->ctx, def); + free(def); + if (nullptr == Q) return 0; P->hgridshift = skip_prep_fin(Q); } /* We ignore helmert if we have grid shift */ - p = P->hgridshift ? nullptr : pj_param_exists (P->params, "towgs84"); + p = P->hgridshift ? nullptr : pj_param_exists(P->params, "towgs84"); while (p) { - char *def; - char *s = p->param; - double *d = P->datum_params; - size_t n = strlen (s); + const char *const s = p->param; + const double *const d = P->datum_params; - /* We ignore null helmert shifts (common in auto-translated resource files, e.g. epsg) */ - if (0==d[0] && 0==d[1] && 0==d[2] && 0==d[3] && 0==d[4] && 0==d[5] && 0==d[6]) { + /* We ignore null helmert shifts (common in auto-translated resource + * files, e.g. epsg) */ + if (0 == d[0] && 0 == d[1] && 0 == d[2] && 0 == d[3] && 0 == d[4] && + 0 == d[5] && 0 == d[6]) { /* If the current ellipsoid is not WGS84, then make sure the */ /* change in ellipsoid is still done. */ - if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { + if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && + fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { do_cart = 1; } break; } + const size_t n = strlen(s); if (n <= 8) /* 8==strlen ("towgs84=") */ return 0; - def = static_cast(malloc (100+n)); - if (nullptr==def) - return 0; - sprintf (def, "break_cs2cs_recursion proj=helmert exact %s convention=position_vector", s); - Q = pj_create_internal (P->ctx, def); - free(def); - if (nullptr==Q) + const size_t def_max_size = 100 + n; + std::string def; + def.reserve(def_max_size); + def += "break_cs2cs_recursion proj=helmert exact "; + def += s; + def += " convention=position_vector"; + Q = pj_create_internal(P->ctx, def.c_str()); + if (nullptr == Q) return 0; - pj_inherit_ellipsoid_def (P, Q); - P->helmert = skip_prep_fin (Q); + pj_inherit_ellipsoid_def(P, Q); + P->helmert = skip_prep_fin(Q); break; } - /* We also need cartesian/geographical transformations if we are working in */ - /* geocentric/cartesian space or we need to do a Helmert transform. */ + /* We also need cartesian/geographical transformations if we are working in + */ + /* geocentric/cartesian space or we need to do a Helmert transform. */ if (P->is_geocent || P->helmert || do_cart) { char def[150]; - sprintf (def, "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", P->a_orig, P->es_orig); + snprintf(def, sizeof(def), + "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", + P->a_orig, P->es_orig); { - /* In case the current locale does not use dot but comma as decimal */ + /* In case the current locale does not use dot but comma as decimal + */ /* separator, replace it with dot, so that proj_atof() behaves */ /* correctly. */ - /* TODO later: use C++ ostringstream with imbue(std::locale::classic()) */ + /* TODO later: use C++ ostringstream with + * imbue(std::locale::classic()) */ /* to be locale unaware */ - char* next_pos; - for (next_pos = def; (next_pos = strchr (next_pos, ',')) != nullptr; next_pos++) { + char *next_pos; + for (next_pos = def; (next_pos = strchr(next_pos, ',')) != nullptr; + next_pos++) { *next_pos = '.'; } } - Q = pj_create_internal (P->ctx, def); - if (nullptr==Q) + Q = pj_create_internal(P->ctx, def); + if (nullptr == Q) return 0; - P->cart = skip_prep_fin (Q); + P->cart = skip_prep_fin(Q); if (!P->is_geocent) { - sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); - Q = pj_create_internal (P->ctx, def); - if (nullptr==Q) + snprintf(def, sizeof(def), + "break_cs2cs_recursion proj=cart ellps=WGS84"); + Q = pj_create_internal(P->ctx, def); + if (nullptr == Q) return 0; - P->cart_wgs84 = skip_prep_fin (Q); + P->cart_wgs84 = skip_prep_fin(Q); } } return 1; } - -/*************************************************************************************/ -PJ *pj_create_internal (PJ_CONTEXT *ctx, const char *definition) { /*************************************************************************************/ - -/************************************************************************************** - Create a new PJ object in the context ctx, using the given definition. If ctx==0, - the default context is used, if definition==0, or invalid, a null-pointer is - returned. The definition may use '+' as argument start indicator, as in - "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32". - - It may even use free formatting "proj = utm; zone =32 ellps= GRS80". - Note that the semicolon separator is allowed, but not required. -**************************************************************************************/ - char *args, **argv; +PJ *pj_create_internal(PJ_CONTEXT *ctx, const char *definition) { + /*************************************************************************************/ + + /************************************************************************************** + Create a new PJ object in the context ctx, using the given definition. + If ctx==0, the default context is used, if definition==0, or invalid, a + null-pointer is returned. The definition may use '+' as argument start + indicator, as in + "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32". + + It may even use free formatting "proj = utm; zone =32 ellps= + GRS80". Note that the semicolon separator is allowed, but not required. + **************************************************************************************/ + char *args, **argv; size_t argc, n; - if (nullptr==ctx) - ctx = pj_get_default_ctx (); + if (nullptr == ctx) + ctx = pj_get_default_ctx(); /* Make a copy that we can manipulate */ - n = strlen (definition); - args = (char *) malloc (n + 1); - if (nullptr==args) { + n = strlen(definition); + args = (char *)malloc(n + 1); + if (nullptr == args) { proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/); return nullptr; } - strcpy (args, definition); + strcpy(args, definition); - argc = pj_trim_argc (args); - if (argc==0) { - free (args); + argc = pj_trim_argc(args); + if (argc == 0) { + free(args); proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); return nullptr; } - argv = pj_trim_argv (argc, args); + argv = pj_trim_argv(argc, args); if (!argv) { free(args); proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/); return nullptr; } - PJ* P = pj_create_argv_internal (ctx, (int) argc, argv); + PJ *P = pj_create_argv_internal(ctx, (int)argc, argv); - free (argv); - free (args); + free(argv); + free(args); return P; } /*************************************************************************************/ -PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv) { -/************************************************************************************** -Create a new PJ object in the context ctx, using the given definition argument -array argv. If ctx==0, the default context is used, if definition==0, or invalid, -a null-pointer is returned. The definition arguments may use '+' as argument start -indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", -"zone=32"}. -**************************************************************************************/ - PJ *P; - const char *c; - - if (nullptr==ctx) - ctx = pj_get_default_ctx (); - if (nullptr==argv) { +PJ *proj_create_argv(PJ_CONTEXT *ctx, int argc, char **argv) { + /************************************************************************************** + Create a new PJ object in the context ctx, using the given definition + argument array argv. If ctx==0, the default context is used, if + definition==0, or invalid, a null-pointer is returned. The definition + arguments may use '+' as argument start indicator, as in {"+proj=utm", + "+zone=32"}, or leave it out, as in {"proj=utm", "zone=32"}. + **************************************************************************************/ + + if (nullptr == ctx) + ctx = pj_get_default_ctx(); + if (nullptr == argv) { proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); return nullptr; } - /* We assume that free format is used, and build a full proj_create compatible string */ - c = pj_make_args (argc, argv); - if (nullptr==c) { + /* We assume that free format is used, and build a full proj_create + * compatible string */ + char *c = pj_make_args(argc, argv); + if (nullptr == c) { proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP /* ENOMEM */); return nullptr; } - P = proj_create (ctx, c); + PJ *P = proj_create(ctx, c); - free ((char *) c); + free((char *)c); return P; } /*************************************************************************************/ -PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv) { -/************************************************************************************** -For use by pipeline init function. -**************************************************************************************/ - if (nullptr==ctx) - ctx = pj_get_default_ctx (); - if (nullptr==argv) { +PJ *pj_create_argv_internal(PJ_CONTEXT *ctx, int argc, char **argv) { + /************************************************************************************** + For use by pipeline init function. + **************************************************************************************/ + if (nullptr == ctx) + ctx = pj_get_default_ctx(); + if (nullptr == argv) { proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); return nullptr; } /* ...and let pj_init_ctx do the hard work */ /* New interface: forbid init=epsg:XXXX syntax by default */ - const int allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, FALSE); - PJ* P = pj_init_ctx_with_allow_init_epsg (ctx, argc, argv, allow_init_epsg); + const int allow_init_epsg = + proj_context_get_use_proj4_init_rules(ctx, FALSE); + PJ *P = pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg); /* Support cs2cs-style modifiers */ - int ret = cs2cs_emulation_setup (P); - if (0==ret) - return proj_destroy (P); + int ret = cs2cs_emulation_setup(P); + if (0 == ret) + return proj_destroy(P); return P; } /** Create an area of use */ -PJ_AREA * proj_area_create(void) { - return static_cast(calloc(1, sizeof(PJ_AREA))); -} +PJ_AREA *proj_area_create(void) { return new PJ_AREA(); } /** Assign a bounding box to an area of use. */ -void proj_area_set_bbox(PJ_AREA *area, - double west_lon_degree, - double south_lat_degree, - double east_lon_degree, - double north_lat_degree) { +void proj_area_set_bbox(PJ_AREA *area, double west_lon_degree, + double south_lat_degree, double east_lon_degree, + double north_lat_degree) { area->bbox_set = TRUE; area->west_lon_degree = west_lon_degree; area->south_lat_degree = south_lat_degree; @@ -896,17 +1117,18 @@ void proj_area_set_bbox(PJ_AREA *area, area->north_lat_degree = north_lat_degree; } +/** Assign the name of an area of use. */ +void proj_area_set_name(PJ_AREA *area, const char *name) { area->name = name; } + /** Free an area of use */ -void proj_area_destroy(PJ_AREA* area) { - free(area); -} +void proj_area_destroy(PJ_AREA *area) { delete area; } /************************************************************************/ /* proj_context_use_proj4_init_rules() */ /************************************************************************/ void proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable) { - if( ctx == nullptr ) { + if (ctx == nullptr) { ctx = pj_get_default_ctx(); } ctx->use_proj4_init_rules = enable; @@ -916,7 +1138,7 @@ void proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable) { /* EQUAL() */ /************************************************************************/ -static int EQUAL(const char* a, const char* b) { +static int EQUAL(const char *a, const char *b) { #ifdef _MSC_VER return _stricmp(a, b) == 0; #else @@ -928,82 +1150,78 @@ static int EQUAL(const char* a, const char* b) { /* proj_context_get_use_proj4_init_rules() */ /************************************************************************/ -int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path) { - const char* val = getenv("PROJ_USE_PROJ4_INIT_RULES"); +int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, + int from_legacy_code_path) { + const char *val = getenv("PROJ_USE_PROJ4_INIT_RULES"); - if( ctx == nullptr ) { + if (ctx == nullptr) { ctx = pj_get_default_ctx(); } - if( val ) { - if( EQUAL(val, "yes") || EQUAL(val, "on") || EQUAL(val, "true") ) { + if (val) { + if (EQUAL(val, "yes") || EQUAL(val, "on") || EQUAL(val, "true")) { return TRUE; } - if( EQUAL(val, "no") || EQUAL(val, "off") || EQUAL(val, "false") ) { + if (EQUAL(val, "no") || EQUAL(val, "off") || EQUAL(val, "false")) { return FALSE; } - pj_log(ctx, PJ_LOG_ERROR, "Invalid value for PROJ_USE_PROJ4_INIT_RULES"); + pj_log(ctx, PJ_LOG_ERROR, + "Invalid value for PROJ_USE_PROJ4_INIT_RULES"); } - if( ctx->use_proj4_init_rules >= 0 ) { + if (ctx->use_proj4_init_rules >= 0) { return ctx->use_proj4_init_rules; } return from_legacy_code_path; } /** Adds a " +type=crs" suffix to a PROJ string (if it is a PROJ string) */ -std::string pj_add_type_crs_if_needed(const std::string& str) -{ +std::string pj_add_type_crs_if_needed(const std::string &str) { std::string ret(str); - if( (starts_with(str, "proj=") || - starts_with(str, "+proj=") || - starts_with(str, "+init=") || - starts_with(str, "+title=")) && - str.find("type=crs") == std::string::npos ) - { + if ((starts_with(str, "proj=") || starts_with(str, "+proj=") || + starts_with(str, "+init=") || starts_with(str, "+title=")) && + str.find("type=crs") == std::string::npos) { ret += " +type=crs"; } return ret; } - // --------------------------------------------------------------------------- -static double simple_min(const double* data, const int arr_len) { +static double simple_min(const double *data, const int arr_len) { double min_value = data[0]; - for( int iii = 1; iii < arr_len; iii++ ) { + for (int iii = 1; iii < arr_len; iii++) { if (data[iii] < min_value) min_value = data[iii]; } return min_value; } - // --------------------------------------------------------------------------- -static double simple_max(const double* data, const int arr_len) { +static double simple_max(const double *data, const int arr_len) { double max_value = data[0]; - for( int iii = 1; iii < arr_len; iii++ ) { - if ((data[iii] > max_value || max_value == HUGE_VAL) && data[iii] != HUGE_VAL) + for (int iii = 1; iii < arr_len; iii++) { + if ((data[iii] > max_value || max_value == HUGE_VAL) && + data[iii] != HUGE_VAL) max_value = data[iii]; } return max_value; - } - +} // --------------------------------------------------------------------------- -static int _find_previous_index(const int iii, const double* data, const int arr_len) { +static int find_previous_index(const int iii, const double *data, + const int arr_len) { // find index of nearest valid previous value if exists int prev_iii = iii - 1; - if (prev_iii == -1) // handle wraparound + if (prev_iii == -1) // handle wraparound prev_iii = arr_len - 1; while (data[prev_iii] == HUGE_VAL && prev_iii != iii) { - prev_iii --; - if (prev_iii == -1) // handle wraparound + prev_iii--; + if (prev_iii == -1) // handle wraparound prev_iii = arr_len - 1; } return prev_iii; } - // --------------------------------------------------------------------------- /****************************************************************************** Handles the case when longitude values cross the antimeridian @@ -1049,31 +1267,32 @@ However, even though the spacing was even in the source projection, it isn't guaranteed in the target geographic projection. So, instead of 240, 200 is used as it significantly larger than 120 to be sure that the antimeridian was crossed but smalller than 240 to account for possible irregularities in distances -when re-projecting. Also, 200 ensures latitudes are ignored for axis order handling. +when re-projecting. Also, 200 ensures latitudes are ignored for axis order +handling. ******************************************************************************/ -static double antimeridian_min(const double* data, const int arr_len) { +static double antimeridian_min(const double *data, const int arr_len) { double positive_min = HUGE_VAL; double min_value = HUGE_VAL; int crossed_meridian_count = 0; bool positive_meridian = false; - for( int iii = 0; iii < arr_len; iii++ ) { + for (int iii = 0; iii < arr_len; iii++) { if (data[iii] == HUGE_VAL) continue; - int prev_iii = _find_previous_index(iii, data, arr_len); + int prev_iii = find_previous_index(iii, data, arr_len); // check if crossed meridian double delta = data[prev_iii] - data[iii]; // 180 -> -180 if (delta >= 200 && delta != HUGE_VAL) { if (crossed_meridian_count == 0) positive_min = min_value; - crossed_meridian_count ++; + crossed_meridian_count++; positive_meridian = false; - // -180 -> 180 + // -180 -> 180 } else if (delta <= -200 && delta != HUGE_VAL) { if (crossed_meridian_count == 0) positive_min = data[iii]; - crossed_meridian_count ++; + crossed_meridian_count++; positive_meridian = true; } // positive meridian side min @@ -1092,7 +1311,6 @@ static double antimeridian_min(const double* data, const int arr_len) { return min_value; } - // --------------------------------------------------------------------------- // Handles the case when longitude values cross the antimeridian // when calculating the minimum. @@ -1100,39 +1318,39 @@ static double antimeridian_min(const double* data, const int arr_len) { // Note: This requires a densified ring with at least 2 additional // points per edge to correctly handle global extents. // See antimeridian_min docstring for reasoning. -static double antimeridian_max(const double* data, const int arr_len) { +static double antimeridian_max(const double *data, const int arr_len) { double negative_max = -HUGE_VAL; double max_value = -HUGE_VAL; bool negative_meridian = false; int crossed_meridian_count = 0; - for( int iii = 0; iii < arr_len; iii++ ) { + for (int iii = 0; iii < arr_len; iii++) { if (data[iii] == HUGE_VAL) continue; - int prev_iii = _find_previous_index(iii, data, arr_len); + int prev_iii = find_previous_index(iii, data, arr_len); // check if crossed meridian double delta = data[prev_iii] - data[iii]; // 180 -> -180 if (delta >= 200 && delta != HUGE_VAL) { if (crossed_meridian_count == 0) negative_max = data[iii]; - crossed_meridian_count ++; + crossed_meridian_count++; negative_meridian = true; - // -180 -> 180 - } else if (delta <= -200 && delta != HUGE_VAL){ + // -180 -> 180 + } else if (delta <= -200 && delta != HUGE_VAL) { if (crossed_meridian_count == 0) negative_max = max_value; negative_meridian = false; crossed_meridian_count++; } // negative meridian side max - if (negative_meridian - && (data[iii] > negative_max || negative_max == HUGE_VAL) - && data[iii] != HUGE_VAL - ) + if (negative_meridian && + (data[iii] > negative_max || negative_max == HUGE_VAL) && + data[iii] != HUGE_VAL) negative_max = data[iii]; // track general max value - if ((data[iii] > max_value || max_value == HUGE_VAL) && data[iii] != HUGE_VAL) + if ((data[iii] > max_value || max_value == HUGE_VAL) && + data[iii] != HUGE_VAL) max_value = data[iii]; } if (crossed_meridian_count == 2) @@ -1143,113 +1361,79 @@ static double antimeridian_max(const double* data, const int arr_len) { return max_value; } - // --------------------------------------------------------------------------- // Check if the original projected bounds contains // the north pole. // This assumes that the destination CRS is geographic. -static bool contains_north_pole( - PJ* projobj, - PJ_DIRECTION pj_direction, - const double xmin, - const double ymin, - const double xmax, - const double ymax, - bool lon_lat_order -) { +static bool contains_north_pole(PJ *projobj, PJ_DIRECTION pj_direction, + const double xmin, const double ymin, + const double xmax, const double ymax, + bool lon_lat_order) { double pole_y = 90; double pole_x = 0; if (!lon_lat_order) { pole_y = 0; pole_x = 90; } - proj_trans_generic( - projobj, - opposite_direction(pj_direction), - &pole_x, sizeof(double), 1, - &pole_y, sizeof(double), 1, - nullptr, sizeof(double), 0, - nullptr, sizeof(double), 0 - ); + proj_trans_generic(projobj, opposite_direction(pj_direction), &pole_x, + sizeof(double), 1, &pole_y, sizeof(double), 1, nullptr, + sizeof(double), 0, nullptr, sizeof(double), 0); if (xmin < pole_x && pole_x < xmax && ymax > pole_y && pole_y > ymin) return true; return false; } - // --------------------------------------------------------------------------- // Check if the original projected bounds contains // the south pole. // This assumes that the destination CRS is geographic. -static bool contains_south_pole( - PJ* projobj, - PJ_DIRECTION pj_direction, - const double xmin, - const double ymin, - const double xmax, - const double ymax, - bool lon_lat_order -) { +static bool contains_south_pole(PJ *projobj, PJ_DIRECTION pj_direction, + const double xmin, const double ymin, + const double xmax, const double ymax, + bool lon_lat_order) { double pole_y = -90; double pole_x = 0; if (!lon_lat_order) { pole_y = 0; pole_x = -90; } - proj_trans_generic( - projobj, - opposite_direction(pj_direction), - &pole_x, sizeof(double), 1, - &pole_y, sizeof(double), 1, - nullptr, sizeof(double), 0, - nullptr, sizeof(double), 0 - ); + proj_trans_generic(projobj, opposite_direction(pj_direction), &pole_x, + sizeof(double), 1, &pole_y, sizeof(double), 1, nullptr, + sizeof(double), 0, nullptr, sizeof(double), 0); if (xmin < pole_x && pole_x < xmax && ymax > pole_y && pole_y > ymin) return true; return false; } - // --------------------------------------------------------------------------- // Check if the target CRS of the transformation // has the longitude latitude axis order. // This assumes that the destination CRS is geographic. -static int target_crs_lon_lat_order( - PJ_CONTEXT* transformer_ctx, - PJ* transformer_pj, - PJ_DIRECTION pj_direction -) { - PJ* target_crs = nullptr; +static int target_crs_lon_lat_order(PJ_CONTEXT *transformer_ctx, + PJ *transformer_pj, + PJ_DIRECTION pj_direction) { + PJ *target_crs = nullptr; if (pj_direction == PJ_FWD) target_crs = proj_get_target_crs(transformer_ctx, transformer_pj); else if (pj_direction == PJ_INV) target_crs = proj_get_source_crs(transformer_ctx, transformer_pj); if (target_crs == nullptr) { - proj_context_log_debug(transformer_ctx, "Unable to retrieve target CRS"); + proj_context_log_debug(transformer_ctx, + "Unable to retrieve target CRS"); return -1; } - PJ* coord_system_pj = proj_crs_get_coordinate_system( - transformer_ctx, - target_crs - ); + PJ *coord_system_pj = + proj_crs_get_coordinate_system(transformer_ctx, target_crs); proj_destroy(target_crs); if (coord_system_pj == nullptr) { - proj_context_log_debug(transformer_ctx, "Unable to get target CRS coordinate system."); + proj_context_log_debug(transformer_ctx, + "Unable to get target CRS coordinate system."); return -1; } - const char* abbrev = nullptr; - int success = proj_cs_get_axis_info( - transformer_ctx, - coord_system_pj, - 0, - nullptr, - &abbrev, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr - ); + const char *abbrev = nullptr; + int success = proj_cs_get_axis_info(transformer_ctx, coord_system_pj, 0, + nullptr, &abbrev, nullptr, nullptr, + nullptr, nullptr, nullptr); proj_destroy(coord_system_pj); if (success != 1) return -1; @@ -1265,15 +1449,15 @@ static int target_crs_lon_lat_order( * * If the destination CRS is geographic, the first axis is longitude, * and xmax < xmin then the bounds crossed the antimeridian. - * In this scenario there are two polygons, one on each side of the antimeridian. - * The first polygon should be constructed with (xmin, ymin, 180, ymax) - * and the second with (-180, ymin, xmax, ymax). + * In this scenario there are two polygons, one on each side of the + * antimeridian. The first polygon should be constructed with (xmin, ymin, 180, + * ymax) and the second with (-180, ymin, xmax, ymax). * * If the destination CRS is geographic, the first axis is latitude, * and ymax < ymin then the bounds crossed the antimeridian. - * In this scenario there are two polygons, one on each side of the antimeridian. - * The first polygon should be constructed with (ymin, xmin, ymax, 180) - * and the second with (ymin, -180, ymax, xmax). + * In this scenario there are two polygons, one on each side of the + * antimeridian. The first polygon should be constructed with (ymin, xmin, ymax, + * 180) and the second with (ymin, -180, ymax, xmax). * * @param context The PJ_CONTEXT object. * @param P The PJ object representing the transformation. @@ -1299,19 +1483,11 @@ static int target_crs_lon_lat_order( * @return an integer. 1 if successful. 0 if failures encountered. * @since 8.2 */ -int proj_trans_bounds(PJ_CONTEXT* context, - PJ *P, - PJ_DIRECTION direction, - const double xmin, - const double ymin, - const double xmax, - const double ymax, - double* out_xmin, - double* out_ymin, - double* out_xmax, - double* out_ymax, - const int densify_pts -) { +int proj_trans_bounds(PJ_CONTEXT *context, PJ *P, PJ_DIRECTION direction, + const double xmin, const double ymin, const double xmax, + const double ymax, double *out_xmin, double *out_ymin, + double *out_xmax, double *out_ymax, + const int densify_pts) { *out_xmin = HUGE_VAL; *out_ymin = HUGE_VAL; *out_xmax = HUGE_VAL; @@ -1319,19 +1495,19 @@ int proj_trans_bounds(PJ_CONTEXT* context, if (P == nullptr) { proj_log_error(P, _("NULL P object not allowed.")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } if (densify_pts < 0 || densify_pts > 10000) { proj_log_error(P, _("densify_pts must be between 0-10000.")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } PJ_PROJ_INFO pj_info = proj_pj_info(P); if (pj_info.id == nullptr) { proj_log_error(P, _("NULL transformation not allowed,")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } if (strcmp(pj_info.id, "noop") == 0 || direction == PJ_IDENT) { @@ -1345,24 +1521,24 @@ int proj_trans_bounds(PJ_CONTEXT* context, bool degree_output = proj_degree_output(P, direction) != 0; bool degree_input = proj_degree_input(P, direction) != 0; if (degree_output && densify_pts < 2) { - proj_log_error(P, _("densify_pts must be at least 2 if the output is geograpic.")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_log_error( + P, + _("densify_pts must be at least 2 if the output is geographic.")); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } - int side_pts = densify_pts + 1; // add one because we are densifying + int side_pts = densify_pts + 1; // add one because we are densifying const int boundary_len = side_pts * 4; std::vector x_boundary_array; std::vector y_boundary_array; - try - { + try { x_boundary_array.resize(boundary_len); y_boundary_array.resize(boundary_len); - } - catch( const std::exception & e ) // memory allocation failure + } catch (const std::exception &e) // memory allocation failure { proj_log_error(P, e.what()); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } double delta_x = 0; @@ -1372,9 +1548,8 @@ int proj_trans_bounds(PJ_CONTEXT* context, bool input_lon_lat_order = false; bool output_lon_lat_order = false; if (degree_input) { - int in_order_lon_lat = target_crs_lon_lat_order( - context, P, opposite_direction(direction) - ); + int in_order_lon_lat = + target_crs_lon_lat_order(context, P, opposite_direction(direction)); if (in_order_lon_lat == -1) return false; input_lon_lat_order = in_order_lon_lat != 0; @@ -1385,29 +1560,15 @@ int proj_trans_bounds(PJ_CONTEXT* context, return false; output_lon_lat_order = out_order_lon_lat != 0; north_pole_in_bounds = contains_north_pole( - P, - direction, - xmin, - ymin, - xmax, - ymax, - output_lon_lat_order - ); + P, direction, xmin, ymin, xmax, ymax, output_lon_lat_order); south_pole_in_bounds = contains_south_pole( - P, - direction, - xmin, - ymin, - xmax, - ymax, - output_lon_lat_order - ); + P, direction, xmin, ymin, xmax, ymax, output_lon_lat_order); } if (degree_input && xmax < xmin) { if (!input_lon_lat_order) { proj_log_error(P, _("latitude max < latitude min.")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } // handle antimeridian @@ -1418,7 +1579,7 @@ int proj_trans_bounds(PJ_CONTEXT* context, if (degree_input && ymax < ymin) { if (input_lon_lat_order) { proj_log_error(P, _("latitude max < latitude min.")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return false; } // handle antimeridian @@ -1427,11 +1588,9 @@ int proj_trans_bounds(PJ_CONTEXT* context, delta_y = (ymax - ymin) / side_pts; } - // build densified bounding box // Note: must be a linear ring for antimeridian logic - for( int iii = 0; iii < side_pts; iii++ ) - { + for (int iii = 0; iii < side_pts; iii++) { // xmin boundary y_boundary_array[iii] = ymax - iii * delta_y; x_boundary_array[iii] = xmin; @@ -1445,14 +1604,9 @@ int proj_trans_bounds(PJ_CONTEXT* context, y_boundary_array[iii + side_pts * 3] = ymax; x_boundary_array[iii + side_pts * 3] = xmax - iii * delta_x; } - proj_trans_generic ( - P, - direction, - &x_boundary_array[0], sizeof(double), boundary_len, - &y_boundary_array[0], sizeof(double), boundary_len, - nullptr, 0, 0, - nullptr, 0, 0 - ); + proj_trans_generic(P, direction, &x_boundary_array[0], sizeof(double), + boundary_len, &y_boundary_array[0], sizeof(double), + boundary_len, nullptr, 0, 0, nullptr, 0, 0); if (!degree_output) { *out_xmin = simple_min(&x_boundary_array[0], boundary_len); @@ -1493,58 +1647,46 @@ int proj_trans_bounds(PJ_CONTEXT* context, return true; } - -/*****************************************************************************/ -static void reproject_bbox(PJ* pjGeogToCrs, - double west_lon, double south_lat, - double east_lon, double north_lat, - double& minx, - double& miny, - double& maxx, - double& maxy) { /*****************************************************************************/ +static void reproject_bbox(PJ *pjGeogToCrs, double west_lon, double south_lat, + double east_lon, double north_lat, double &minx, + double &miny, double &maxx, double &maxy) { + /*****************************************************************************/ minx = -std::numeric_limits::max(); miny = -std::numeric_limits::max(); maxx = std::numeric_limits::max(); maxy = std::numeric_limits::max(); - if( !(west_lon == -180.0 && east_lon == 180.0 && - south_lat == -90.0 && north_lat == 90.0) ) - { + if (!(west_lon == -180.0 && east_lon == 180.0 && south_lat == -90.0 && + north_lat == 90.0)) { minx = -minx; miny = -miny; maxx = -maxx; maxy = -maxy; constexpr int N_STEPS = 20; - constexpr int N_STEPS_P1 = N_STEPS+1; + constexpr int N_STEPS_P1 = N_STEPS + 1; constexpr int XY_SIZE = N_STEPS_P1 * 4; std::vector x(XY_SIZE); std::vector y(XY_SIZE); const double step_lon = (east_lon - west_lon) / N_STEPS; const double step_lat = (north_lat - south_lat) / N_STEPS; - for( int j = 0; j <= N_STEPS; j++ ) - { + for (int j = 0; j <= N_STEPS; j++) { x[j] = west_lon + j * step_lon; y[j] = south_lat; - x[N_STEPS_P1+j] = x[j]; - y[N_STEPS_P1+j] = north_lat; - x[N_STEPS_P1*2+j] = west_lon; - y[N_STEPS_P1*2+j] = south_lat + j * step_lat; - x[N_STEPS_P1*3+j] = east_lon; - y[N_STEPS_P1*3+j] = y[N_STEPS_P1*2+j]; + x[N_STEPS_P1 + j] = x[j]; + y[N_STEPS_P1 + j] = north_lat; + x[N_STEPS_P1 * 2 + j] = west_lon; + y[N_STEPS_P1 * 2 + j] = south_lat + j * step_lat; + x[N_STEPS_P1 * 3 + j] = east_lon; + y[N_STEPS_P1 * 3 + j] = y[N_STEPS_P1 * 2 + j]; } - proj_trans_generic ( - pjGeogToCrs, PJ_FWD, - &x[0], sizeof(double), XY_SIZE, - &y[0], sizeof(double), XY_SIZE, - nullptr, 0, 0, - nullptr, 0, 0); - for( int j = 0; j < XY_SIZE; j++ ) - { - if( x[j] != HUGE_VAL && y[j] != HUGE_VAL ) - { + proj_trans_generic(pjGeogToCrs, PJ_FWD, &x[0], sizeof(double), XY_SIZE, + &y[0], sizeof(double), XY_SIZE, nullptr, 0, 0, + nullptr, 0, 0); + for (int j = 0; j < XY_SIZE; j++) { + if (x[j] != HUGE_VAL && y[j] != HUGE_VAL) { minx = std::min(minx, x[j]); miny = std::min(miny, y[j]); maxx = std::max(maxx, x[j]); @@ -1554,18 +1696,13 @@ static void reproject_bbox(PJ* pjGeogToCrs, } } - -/*****************************************************************************/ -static PJ* add_coord_op_to_list( - int idxInOriginalList, - PJ* op, - double west_lon, double south_lat, - double east_lon, double north_lat, - PJ* pjGeogToSrc, - PJ* pjGeogToDst, - bool isOffshore, - std::vector& altCoordOps) { /*****************************************************************************/ +static PJ *add_coord_op_to_list( + int idxInOriginalList, PJ *op, double west_lon, double south_lat, + double east_lon, double north_lat, PJ *pjGeogToSrc, PJ *pjGeogToDst, + const PJ *pjSrcGeocentricToLonLat, const PJ *pjDstGeocentricToLonLat, + bool isOffshore, std::vector &altCoordOps) { + /*****************************************************************************/ double minxSrc; double minySrc; @@ -1576,67 +1713,97 @@ static PJ* add_coord_op_to_list( double maxxDst; double maxyDst; - reproject_bbox(pjGeogToSrc, west_lon, south_lat, east_lon, north_lat, - minxSrc, minySrc, maxxSrc, maxySrc); - reproject_bbox(pjGeogToDst, west_lon, south_lat, east_lon, north_lat, - minxDst, minyDst, maxxDst, maxyDst); + if (pjSrcGeocentricToLonLat) { + minxSrc = west_lon; + minySrc = south_lat; + maxxSrc = east_lon; + maxySrc = north_lat; + } else { + reproject_bbox(pjGeogToSrc, west_lon, south_lat, east_lon, north_lat, + minxSrc, minySrc, maxxSrc, maxySrc); + } + + if (pjDstGeocentricToLonLat) { + minxDst = west_lon; + minyDst = south_lat; + maxxDst = east_lon; + maxyDst = north_lat; + } else { + reproject_bbox(pjGeogToDst, west_lon, south_lat, east_lon, north_lat, + minxDst, minyDst, maxxDst, maxyDst); + } - if( minxSrc <= maxxSrc && minxDst <= maxxDst ) - { - const char* c_name = proj_get_name(op); + if (minxSrc <= maxxSrc && minxDst <= maxxDst) { + const char *c_name = proj_get_name(op); std::string name(c_name ? c_name : ""); const double accuracy = proj_coordoperation_get_accuracy(op->ctx, op); - altCoordOps.emplace_back(idxInOriginalList, - minxSrc, minySrc, maxxSrc, maxySrc, - minxDst, minyDst, maxxDst, maxyDst, - op, name, accuracy, isOffshore); + altCoordOps.emplace_back( + idxInOriginalList, minxSrc, minySrc, maxxSrc, maxySrc, minxDst, + minyDst, maxxDst, maxyDst, op, name, accuracy, isOffshore, + pjSrcGeocentricToLonLat, pjDstGeocentricToLonLat); op = nullptr; } return op; } +namespace { +struct ObjectKeeper { + PJ *m_obj = nullptr; + explicit ObjectKeeper(PJ *obj) : m_obj(obj) {} + ~ObjectKeeper() { proj_destroy(m_obj); } + ObjectKeeper(const ObjectKeeper &) = delete; + ObjectKeeper &operator=(const ObjectKeeper &) = delete; +}; +} // namespace + /*****************************************************************************/ -static PJ* create_operation_to_geog_crs(PJ_CONTEXT* ctx, const PJ* crs) { -/*****************************************************************************/ +static PJ *create_operation_to_geog_crs(PJ_CONTEXT *ctx, const PJ *crs) { + /*****************************************************************************/ + + std::unique_ptr keeper; + if (proj_get_type(crs) == PJ_TYPE_COORDINATE_METADATA) { + auto tmp = proj_get_source_crs(ctx, crs); + assert(tmp); + keeper.reset(new ObjectKeeper(tmp)); + crs = tmp; + } + (void)keeper; + // Create a geographic 2D long-lat degrees CRS that is related to the // CRS auto geodetic_crs = proj_crs_get_geodetic_crs(ctx, crs); - if( !geodetic_crs ) { + if (!geodetic_crs) { proj_context_log_debug(ctx, "Cannot find geodetic CRS matching CRS"); return nullptr; } auto geodetic_crs_type = proj_get_type(geodetic_crs); - if( geodetic_crs_type == PJ_TYPE_GEOCENTRIC_CRS || + if (geodetic_crs_type == PJ_TYPE_GEOCENTRIC_CRS || geodetic_crs_type == PJ_TYPE_GEOGRAPHIC_2D_CRS || - geodetic_crs_type == PJ_TYPE_GEOGRAPHIC_3D_CRS ) - { + geodetic_crs_type == PJ_TYPE_GEOGRAPHIC_3D_CRS) { auto datum = proj_crs_get_datum_forced(ctx, geodetic_crs); - assert( datum ); + assert(datum); auto cs = proj_create_ellipsoidal_2D_cs( ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, nullptr, 0); auto ellps = proj_get_ellipsoid(ctx, datum); proj_destroy(datum); double semi_major_metre = 0; double inv_flattening = 0; - proj_ellipsoid_get_parameters(ctx, ellps, &semi_major_metre, - nullptr, nullptr, &inv_flattening); + proj_ellipsoid_get_parameters(ctx, ellps, &semi_major_metre, nullptr, + nullptr, &inv_flattening); // It is critical to set the prime meridian to 0 auto temp = proj_create_geographic_crs( - ctx, "unnamed crs", "unnamed datum", - proj_get_name(ellps), - semi_major_metre, inv_flattening, - "Reference prime meridian", 0, nullptr, 0, - cs); + ctx, "unnamed crs", "unnamed datum", proj_get_name(ellps), + semi_major_metre, inv_flattening, "Reference prime meridian", 0, + nullptr, 0, cs); proj_destroy(ellps); proj_destroy(cs); proj_destroy(geodetic_crs); geodetic_crs = temp; geodetic_crs_type = proj_get_type(geodetic_crs); } - if( geodetic_crs_type != PJ_TYPE_GEOGRAPHIC_2D_CRS ) - { + if (geodetic_crs_type != PJ_TYPE_GEOGRAPHIC_2D_CRS) { // Shouldn't happen proj_context_log_debug(ctx, "Cannot find geographic CRS matching CRS"); proj_destroy(geodetic_crs); @@ -1648,37 +1815,36 @@ static PJ* create_operation_to_geog_crs(PJ_CONTEXT* ctx, const PJ* crs) { proj_operation_factory_context_set_spatial_criterion( ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); proj_operation_factory_context_set_grid_availability_use( - ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); + ctx, operation_ctx, + PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); auto target_crs_2D = proj_crs_demote_to_2D(ctx, nullptr, crs); - auto op_list_to_geodetic = proj_create_operations( - ctx, geodetic_crs, target_crs_2D, operation_ctx); + auto op_list_to_geodetic = + proj_create_operations(ctx, geodetic_crs, target_crs_2D, operation_ctx); proj_destroy(target_crs_2D); proj_operation_factory_context_destroy(operation_ctx); proj_destroy(geodetic_crs); - const int nOpCount = op_list_to_geodetic == nullptr ? 0 : - proj_list_get_count(op_list_to_geodetic); - if( nOpCount == 0 ) - { - proj_context_log_debug(ctx, "Cannot compute transformation from geographic CRS to CRS"); + const int nOpCount = op_list_to_geodetic == nullptr + ? 0 + : proj_list_get_count(op_list_to_geodetic); + if (nOpCount == 0) { + proj_context_log_debug( + ctx, "Cannot compute transformation from geographic CRS to CRS"); proj_list_destroy(op_list_to_geodetic); return nullptr; } - PJ* opGeogToCrs = nullptr; + PJ *opGeogToCrs = nullptr; // Use in priority operations *without* grids - for(int i = 0; i < nOpCount; i++ ) - { + for (int i = 0; i < nOpCount; i++) { auto op = proj_list_get(ctx, op_list_to_geodetic, i); assert(op); - if( proj_coordoperation_get_grid_used_count(ctx, op) == 0 ) - { + if (proj_coordoperation_get_grid_used_count(ctx, op) == 0) { opGeogToCrs = op; break; } proj_destroy(op); } - if( opGeogToCrs == nullptr ) - { + if (opGeogToCrs == nullptr) { opGeogToCrs = proj_list_get(ctx, op_list_to_geodetic, 0); assert(opGeogToCrs); } @@ -1687,40 +1853,89 @@ static PJ* create_operation_to_geog_crs(PJ_CONTEXT* ctx, const PJ* crs) { } /*****************************************************************************/ -PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char *target_crs, PJ_AREA *area) { -/****************************************************************************** - Create a transformation pipeline between two known coordinate reference - systems. +static PJ *create_operation_geocentric_crs_to_geog_crs(PJ_CONTEXT *ctx, + const PJ *geocentric_crs) +/*****************************************************************************/ +{ + assert(proj_get_type(geocentric_crs) == PJ_TYPE_GEOCENTRIC_CRS); + + auto datum = proj_crs_get_datum_forced(ctx, geocentric_crs); + assert(datum); + auto cs = proj_create_ellipsoidal_2D_cs(ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, + nullptr, 0); + auto ellps = proj_get_ellipsoid(ctx, datum); + proj_destroy(datum); + double semi_major_metre = 0; + double inv_flattening = 0; + proj_ellipsoid_get_parameters(ctx, ellps, &semi_major_metre, nullptr, + nullptr, &inv_flattening); + // It is critical to set the prime meridian to 0 + auto lon_lat_crs = proj_create_geographic_crs( + ctx, "unnamed crs", "unnamed datum", proj_get_name(ellps), + semi_major_metre, inv_flattening, "Reference prime meridian", 0, + nullptr, 0, cs); + proj_destroy(ellps); + proj_destroy(cs); + + // Create the transformation from this geocentric CRS to the lon-lat one + auto operation_ctx = proj_create_operation_factory_context(ctx, nullptr); + proj_operation_factory_context_set_spatial_criterion( + ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); + proj_operation_factory_context_set_grid_availability_use( + ctx, operation_ctx, + PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); + auto op_list = + proj_create_operations(ctx, geocentric_crs, lon_lat_crs, operation_ctx); + proj_operation_factory_context_destroy(operation_ctx); + proj_destroy(lon_lat_crs); - See docs/source/development/reference/functions.rst + const int nOpCount = op_list == nullptr ? 0 : proj_list_get_count(op_list); + if (nOpCount != 1) { + proj_context_log_debug(ctx, "Cannot compute transformation from " + "geocentric CRS to geographic CRS"); + proj_list_destroy(op_list); + return nullptr; + } -******************************************************************************/ - if( !ctx ) { + auto op = proj_list_get(ctx, op_list, 0); + assert(op); + proj_list_destroy(op_list); + return op; +} + +/*****************************************************************************/ +PJ *proj_create_crs_to_crs(PJ_CONTEXT *ctx, const char *source_crs, + const char *target_crs, PJ_AREA *area) { + /****************************************************************************** + Create a transformation pipeline between two known coordinate reference + systems. + + See docs/source/development/reference/functions.rst + + ******************************************************************************/ + if (!ctx) { ctx = pj_get_default_ctx(); } - PJ* src; - PJ* dst; - try - { + PJ *src; + PJ *dst; + try { std::string source_crs_modified(pj_add_type_crs_if_needed(source_crs)); std::string target_crs_modified(pj_add_type_crs_if_needed(target_crs)); src = proj_create(ctx, source_crs_modified.c_str()); - if( !src ) { + if (!src) { proj_context_log_debug(ctx, "Cannot instantiate source_crs"); return nullptr; } dst = proj_create(ctx, target_crs_modified.c_str()); - if( !dst ) { + if (!dst) { proj_context_log_debug(ctx, "Cannot instantiate target_crs"); proj_destroy(src); return nullptr; } - } - catch( const std::exception& ) - { + } catch (const std::exception &) { return nullptr; } @@ -1730,40 +1945,61 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char return ret; } - /*****************************************************************************/ -std::vector pj_create_prepared_operations(PJ_CONTEXT *ctx, - const PJ *source_crs, - const PJ *target_crs, - PJ_OBJ_LIST* op_list) +std::vector +pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs, + const PJ *target_crs, PJ_OBJ_LIST *op_list) /*****************************************************************************/ { - auto pjGeogToSrc = create_operation_to_geog_crs(ctx, source_crs); - if( !pjGeogToSrc ) - { - proj_context_log_debug(ctx, - "Cannot create transformation from geographic CRS of source CRS to source CRS"); - return {}; + PJ *pjGeogToSrc = nullptr; + PJ *pjSrcGeocentricToLonLat = nullptr; + if (proj_get_type(source_crs) == PJ_TYPE_GEOCENTRIC_CRS) { + pjSrcGeocentricToLonLat = + create_operation_geocentric_crs_to_geog_crs(ctx, source_crs); + if (!pjSrcGeocentricToLonLat) { + // shouldn't happen + return {}; + } + } else { + pjGeogToSrc = create_operation_to_geog_crs(ctx, source_crs); + if (!pjGeogToSrc) { + proj_context_log_debug( + ctx, "Cannot create transformation from geographic " + "CRS of source CRS to source CRS"); + return {}; + } } - auto pjGeogToDst = create_operation_to_geog_crs(ctx, target_crs); - if( !pjGeogToDst ) - { - proj_context_log_debug(ctx, - "Cannot create transformation from geographic CRS of target CRS to target CRS"); - proj_destroy(pjGeogToSrc); - return {}; + PJ *pjGeogToDst = nullptr; + PJ *pjDstGeocentricToLonLat = nullptr; + if (proj_get_type(target_crs) == PJ_TYPE_GEOCENTRIC_CRS) { + pjDstGeocentricToLonLat = + create_operation_geocentric_crs_to_geog_crs(ctx, target_crs); + if (!pjDstGeocentricToLonLat) { + // shouldn't happen + proj_destroy(pjSrcGeocentricToLonLat); + proj_destroy(pjGeogToSrc); + return {}; + } + } else { + pjGeogToDst = create_operation_to_geog_crs(ctx, target_crs); + if (!pjGeogToDst) { + proj_context_log_debug( + ctx, "Cannot create transformation from geographic " + "CRS of target CRS to target CRS"); + proj_destroy(pjSrcGeocentricToLonLat); + proj_destroy(pjGeogToSrc); + return {}; + } } - try - { + try { std::vector preparedOpList; // Iterate over source->target candidate transformations and reproject // their long-lat bounding box into the source CRS. const auto op_count = proj_list_get_count(op_list); - for( int i = 0; i < op_count; i++ ) - { + for (int i = 0; i < op_count; i++) { auto op = proj_list_get(ctx, op_list, i); assert(op); double west_lon = 0.0; @@ -1771,33 +2007,33 @@ std::vector pj_create_prepared_operations(PJ_CONTEXT *ctx, double east_lon = 0.0; double north_lat = 0.0; - const char* areaName = nullptr; - if( proj_get_area_of_use(ctx, op, - &west_lon, &south_lat, &east_lon, &north_lat, &areaName) ) - { - const bool isOffshore = - areaName && strstr(areaName, "- offshore"); - if( west_lon <= east_lon ) - { - op = add_coord_op_to_list(i, op, - west_lon, south_lat, east_lon, north_lat, - pjGeogToSrc, pjGeogToDst, isOffshore, - preparedOpList); - } - else - { - auto op_clone = proj_clone(ctx, op); - - op = add_coord_op_to_list(i, op, - west_lon, south_lat, 180, north_lat, - pjGeogToSrc, pjGeogToDst, isOffshore, - preparedOpList); - op_clone = add_coord_op_to_list(i, op_clone, - -180, south_lat, east_lon, north_lat, - pjGeogToSrc, pjGeogToDst, isOffshore, - preparedOpList); - proj_destroy(op_clone); - } + const char *areaName = nullptr; + if (!proj_get_area_of_use(ctx, op, &west_lon, &south_lat, &east_lon, + &north_lat, &areaName)) { + west_lon = -180; + south_lat = -90; + east_lon = 180; + north_lat = 90; + } + + const bool isOffshore = areaName && strstr(areaName, "- offshore"); + if (west_lon <= east_lon) { + op = add_coord_op_to_list( + i, op, west_lon, south_lat, east_lon, north_lat, + pjGeogToSrc, pjGeogToDst, pjSrcGeocentricToLonLat, + pjDstGeocentricToLonLat, isOffshore, preparedOpList); + } else { + auto op_clone = proj_clone(ctx, op); + + op = add_coord_op_to_list( + i, op, west_lon, south_lat, 180, north_lat, pjGeogToSrc, + pjGeogToDst, pjSrcGeocentricToLonLat, + pjDstGeocentricToLonLat, isOffshore, preparedOpList); + op_clone = add_coord_op_to_list( + i, op_clone, -180, south_lat, east_lon, north_lat, + pjGeogToSrc, pjGeogToDst, pjSrcGeocentricToLonLat, + pjDstGeocentricToLonLat, isOffshore, preparedOpList); + proj_destroy(op_clone); } proj_destroy(op); @@ -1805,12 +2041,14 @@ std::vector pj_create_prepared_operations(PJ_CONTEXT *ctx, proj_destroy(pjGeogToSrc); proj_destroy(pjGeogToDst); + proj_destroy(pjSrcGeocentricToLonLat); + proj_destroy(pjDstGeocentricToLonLat); return preparedOpList; - } - catch( const std::exception& ) - { + } catch (const std::exception &) { proj_destroy(pjGeogToSrc); proj_destroy(pjGeogToDst); + proj_destroy(pjSrcGeocentricToLonLat); + proj_destroy(pjDstGeocentricToLonLat); return {}; } } @@ -1828,22 +2066,30 @@ static const char *getOptionValue(const char *option, //! @endcond /*****************************************************************************/ -PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, const PJ *target_crs, PJ_AREA *area, const char* const * options) { -/****************************************************************************** - Create a transformation pipeline between two known coordinate reference - systems. +PJ *proj_create_crs_to_crs_from_pj(PJ_CONTEXT *ctx, const PJ *source_crs, + const PJ *target_crs, PJ_AREA *area, + const char *const *options) { + /****************************************************************************** + Create a transformation pipeline between two known coordinate reference + systems. - See docs/source/development/reference/functions.rst + See docs/source/development/reference/functions.rst -******************************************************************************/ - if( !ctx ) { + ******************************************************************************/ + if (!ctx) { ctx = pj_get_default_ctx(); } + pj_load_ini( + ctx); // to set ctx->errorIfBestTransformationNotAvailableDefault - const char* authority = nullptr; + const char *authority = nullptr; double accuracy = -1; bool allowBallparkTransformations = true; bool forceOver = false; + bool warnIfBestTransformationNotAvailable = + ctx->warnIfBestTransformationNotAvailableDefault; + bool errorIfBestTransformationNotAvailable = + ctx->errorIfBestTransformationNotAvailableDefault; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "AUTHORITY="))) { @@ -1851,22 +2097,31 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons } else if ((value = getOptionValue(*iter, "ACCURACY="))) { accuracy = pj_atof(value); } else if ((value = getOptionValue(*iter, "ALLOW_BALLPARK="))) { - if( ci_equal(value, "yes") ) + if (ci_equal(value, "yes")) allowBallparkTransformations = true; - else if( ci_equal(value, "no") ) + else if (ci_equal(value, "no")) allowBallparkTransformations = false; else { ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, "Invalid value for ALLOW_BALLPARK option."); return nullptr; } - } - else if ((value = getOptionValue(*iter, "FORCE_OVER="))) { + } else if ((value = getOptionValue(*iter, "ONLY_BEST="))) { + warnIfBestTransformationNotAvailable = false; + if (ci_equal(value, "yes")) + errorIfBestTransformationNotAvailable = true; + else if (ci_equal(value, "no")) + errorIfBestTransformationNotAvailable = false; + else { + ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, + "Invalid value for ONLY_BEST option."); + return nullptr; + } + } else if ((value = getOptionValue(*iter, "FORCE_OVER="))) { if (ci_equal(value, "yes")) { forceOver = true; } - } - else { + } else { std::string msg("Unknown option :"); msg += *iter; ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str()); @@ -1875,45 +2130,49 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons } auto operation_ctx = proj_create_operation_factory_context(ctx, authority); - if( !operation_ctx ) { + if (!operation_ctx) { return nullptr; } proj_operation_factory_context_set_allow_ballpark_transformations( ctx, operation_ctx, allowBallparkTransformations); - if( accuracy >= 0 ) { + if (accuracy >= 0) { proj_operation_factory_context_set_desired_accuracy(ctx, operation_ctx, accuracy); } - if( area && area->bbox_set ) { + if (area && area->bbox_set) { proj_operation_factory_context_set_area_of_interest( - ctx, - operation_ctx, - area->west_lon_degree, - area->south_lat_degree, - area->east_lon_degree, - area->north_lat_degree); + ctx, operation_ctx, area->west_lon_degree, area->south_lat_degree, + area->east_lon_degree, area->north_lat_degree); + + if (!area->name.empty()) { + proj_operation_factory_context_set_area_of_interest_name( + ctx, operation_ctx, area->name.c_str()); + } } proj_operation_factory_context_set_spatial_criterion( ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); proj_operation_factory_context_set_grid_availability_use( ctx, operation_ctx, - proj_context_is_network_enabled(ctx) ? - PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE: - PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); - - auto op_list = proj_create_operations(ctx, source_crs, target_crs, operation_ctx); + (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable || + proj_context_is_network_enabled(ctx)) + ? PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE + : PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); + + auto op_list = + proj_create_operations(ctx, source_crs, target_crs, operation_ctx); proj_operation_factory_context_destroy(operation_ctx); - if( !op_list ) { + if (!op_list) { return nullptr; } auto op_count = proj_list_get_count(op_list); - if( op_count == 0 ) { + if (op_count == 0) { proj_list_destroy(op_list); proj_context_log_debug(ctx, "No operation found matching criteria"); @@ -1922,32 +2181,190 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->forceOver = forceOver; - PJ* P = proj_list_get(ctx, op_list, 0); + const int old_debug_level = ctx->debug_level; + if (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable) + ctx->debug_level = PJ_LOG_NONE; + PJ *P = proj_list_get(ctx, op_list, 0); + ctx->debug_level = old_debug_level; assert(P); - if( P == nullptr || op_count == 1 || (area && area->bbox_set) || - proj_get_type(source_crs) == PJ_TYPE_GEOCENTRIC_CRS || - proj_get_type(target_crs) == PJ_TYPE_GEOCENTRIC_CRS ) { + if (P != nullptr) { + P->errorIfBestTransformationNotAvailable = + errorIfBestTransformationNotAvailable; + P->warnIfBestTransformationNotAvailable = + warnIfBestTransformationNotAvailable; + P->skipNonInstantiable = warnIfBestTransformationNotAvailable; + } + + const bool mayNeedToReRunWithDiscardMissing = + (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable) && + !proj_context_is_network_enabled(ctx); + int singleOpIsInstanciable = -1; + if (P != nullptr && op_count == 1 && mayNeedToReRunWithDiscardMissing) { + singleOpIsInstanciable = proj_coordoperation_is_instantiable(ctx, P); + } + + const auto backup_errno = proj_context_errno(ctx); + if (P == nullptr || + (op_count == 1 && (!mayNeedToReRunWithDiscardMissing || + errorIfBestTransformationNotAvailable || + singleOpIsInstanciable == static_cast(true)))) { proj_list_destroy(op_list); ctx->forceOver = false; + + if (P != nullptr && (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable)) { + if (singleOpIsInstanciable < 0) { + singleOpIsInstanciable = + proj_coordoperation_is_instantiable(ctx, P); + } + if (!singleOpIsInstanciable) { + warnAboutMissingGrid(P); + if (errorIfBestTransformationNotAvailable) { + proj_destroy(P); + return nullptr; + } + } + } + + if (P != nullptr) { + P->over = forceOver; + } return P; + } else if (op_count == 1 && mayNeedToReRunWithDiscardMissing && + !singleOpIsInstanciable) { + warnAboutMissingGrid(P); } - auto preparedOpList = pj_create_prepared_operations(ctx, source_crs, target_crs, - op_list); + if (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable) + ctx->debug_level = PJ_LOG_NONE; + auto preparedOpList = + pj_create_prepared_operations(ctx, source_crs, target_crs, op_list); + ctx->debug_level = old_debug_level; ctx->forceOver = false; proj_list_destroy(op_list); - if( preparedOpList.empty() ) - { + if (preparedOpList.empty()) { proj_destroy(P); return nullptr; } + bool foundInstanciableAndNonBallpark = false; + + for (auto &op : preparedOpList) { + op.pj->over = forceOver; + op.pj->errorIfBestTransformationNotAvailable = + errorIfBestTransformationNotAvailable; + op.pj->warnIfBestTransformationNotAvailable = + warnIfBestTransformationNotAvailable; + if (mayNeedToReRunWithDiscardMissing && + !foundInstanciableAndNonBallpark) { + if (!proj_coordoperation_has_ballpark_transformation(op.pj->ctx, + op.pj) && + op.isInstantiable()) { + foundInstanciableAndNonBallpark = true; + } + } + } + if (mayNeedToReRunWithDiscardMissing && !foundInstanciableAndNonBallpark) { + // Re-run proj_create_operations with + // PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID + // Can happen for example for NAD27->NAD83 transformation when we + // have no grid and thus have fallback to Helmert transformation and + // a WGS84 intermediate. + operation_ctx = proj_create_operation_factory_context(ctx, authority); + if (operation_ctx) { + proj_operation_factory_context_set_allow_ballpark_transformations( + ctx, operation_ctx, allowBallparkTransformations); + + if (accuracy >= 0) { + proj_operation_factory_context_set_desired_accuracy( + ctx, operation_ctx, accuracy); + } + + if (area && area->bbox_set) { + proj_operation_factory_context_set_area_of_interest( + ctx, operation_ctx, area->west_lon_degree, + area->south_lat_degree, area->east_lon_degree, + area->north_lat_degree); + + if (!area->name.empty()) { + proj_operation_factory_context_set_area_of_interest_name( + ctx, operation_ctx, area->name.c_str()); + } + } + + proj_operation_factory_context_set_spatial_criterion( + ctx, operation_ctx, + PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); + proj_operation_factory_context_set_grid_availability_use( + ctx, operation_ctx, + PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); + + op_list = proj_create_operations(ctx, source_crs, target_crs, + operation_ctx); + proj_operation_factory_context_destroy(operation_ctx); + + if (op_list) { + ctx->forceOver = forceOver; + ctx->debug_level = PJ_LOG_NONE; + auto preparedOpList2 = pj_create_prepared_operations( + ctx, source_crs, target_crs, op_list); + ctx->debug_level = old_debug_level; + ctx->forceOver = false; + proj_list_destroy(op_list); + + if (!preparedOpList2.empty()) { + // Append new lists of operations to previous one + std::vector newOpList; + for (auto &&op : preparedOpList) { + if (!proj_coordoperation_has_ballpark_transformation( + op.pj->ctx, op.pj)) { + newOpList.emplace_back(std::move(op)); + } + } + for (auto &&op : preparedOpList2) { + op.pj->over = forceOver; + op.pj->errorIfBestTransformationNotAvailable = + errorIfBestTransformationNotAvailable; + op.pj->warnIfBestTransformationNotAvailable = + warnIfBestTransformationNotAvailable; + newOpList.emplace_back(std::move(op)); + } + preparedOpList = std::move(newOpList); + } else { + // We get there in "cs2cs --only-best --no-ballpark + // EPSG:4326+3855 EPSG:4979" use case, where the initial + // create_operations returned 1 operation, and the retry + // with + // PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID + // returned 0. + if (op_count == 1 && + (errorIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable)) { + if (singleOpIsInstanciable < 0) { + singleOpIsInstanciable = + proj_coordoperation_is_instantiable(ctx, P); + } + if (!singleOpIsInstanciable) { + if (errorIfBestTransformationNotAvailable) { + proj_destroy(P); + proj_context_errno_set(ctx, backup_errno); + return nullptr; + } + } + } + } + } + } + } + // If there's finally juste a single result, return it directly - if( preparedOpList.size() == 1 ) - { + if (preparedOpList.size() == 1) { auto retP = preparedOpList[0].pj; preparedOpList[0].pj = nullptr; proj_destroy(P); @@ -1957,6 +2374,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons P->alternativeCoordinateOperations = std::move(preparedOpList); // The returned P is rather dummy P->descr = "Set of coordinate operations"; + P->over = forceOver; P->iso_obj = nullptr; P->fwd = nullptr; P->inv = nullptr; @@ -1968,126 +2386,119 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return P; } - /*****************************************************************************/ -int proj_errno (const PJ *P) { -/****************************************************************************** - Read an error level from the context of a PJ. -******************************************************************************/ - return proj_context_errno (pj_get_ctx ((PJ *) P)); +int proj_errno(const PJ *P) { + /****************************************************************************** + Read an error level from the context of a PJ. + ******************************************************************************/ + return proj_context_errno(pj_get_ctx((PJ *)P)); } /*****************************************************************************/ -int proj_context_errno (PJ_CONTEXT *ctx) { -/****************************************************************************** - Read an error directly from a context, without going through a PJ - belonging to that context. -******************************************************************************/ - if (nullptr==ctx) +int proj_context_errno(PJ_CONTEXT *ctx) { + /****************************************************************************** + Read an error directly from a context, without going through a PJ + belonging to that context. + ******************************************************************************/ + if (nullptr == ctx) ctx = pj_get_default_ctx(); return ctx->last_errno; } /*****************************************************************************/ -int proj_errno_set (const PJ *P, int err) { -/****************************************************************************** - Set context-errno, bubble it up to the thread local errno, return err -******************************************************************************/ +int proj_errno_set(const PJ *P, int err) { + /****************************************************************************** + Set context-errno, bubble it up to the thread local errno, return err + ******************************************************************************/ /* Use proj_errno_reset to explicitly clear the error status */ - if (0==err) + if (0 == err) return 0; /* For P==0 err goes to the default context */ - proj_context_errno_set (pj_get_ctx ((PJ *) P), err); + proj_context_errno_set(pj_get_ctx((PJ *)P), err); errno = err; return err; } /*****************************************************************************/ -int proj_errno_restore (const PJ *P, int err) { -/****************************************************************************** - Use proj_errno_restore when the current function succeeds, but the - error flag was set on entry, and stored/reset using proj_errno_reset - in order to monitor for new errors. - - See usage example under proj_errno_reset () -******************************************************************************/ - if (0==err) +int proj_errno_restore(const PJ *P, int err) { + /****************************************************************************** + Use proj_errno_restore when the current function succeeds, but the + error flag was set on entry, and stored/reset using proj_errno_reset + in order to monitor for new errors. + + See usage example under proj_errno_reset () + ******************************************************************************/ + if (0 == err) return 0; - proj_errno_set (P, err); + proj_errno_set(P, err); return 0; } /*****************************************************************************/ -int proj_errno_reset (const PJ *P) { -/****************************************************************************** - Clears errno in the context and thread local levels - through the low level pj_ctx interface. +int proj_errno_reset(const PJ *P) { + /****************************************************************************** + Clears errno in the context and thread local levels + through the low level pj_ctx interface. - Returns the previous value of the errno, for convenient reset/restore - operations: + Returns the previous value of the errno, for convenient reset/restore + operations: - int foo (PJ *P) { - // errno may be set on entry, but we need to reset it to be able to - // check for errors from "do_something_with_P(P)" - int last_errno = proj_errno_reset (P); + int foo (PJ *P) { + // errno may be set on entry, but we need to reset it to be able to + // check for errors from "do_something_with_P(P)" + int last_errno = proj_errno_reset (P); - // local failure - if (0==P) - return proj_errno_set (P, 42); + // local failure + if (0==P) + return proj_errno_set (P, 42); - // call to function that may fail - do_something_with_P (P); + // call to function that may fail + do_something_with_P (P); - // failure in do_something_with_P? - keep latest error status - if (proj_errno(P)) - return proj_errno (P); + // failure in do_something_with_P? - keep latest error status + if (proj_errno(P)) + return proj_errno (P); - // success - restore previous error status, return 0 - return proj_errno_restore (P, last_errno); - } -******************************************************************************/ + // success - restore previous error status, return 0 + return proj_errno_restore (P, last_errno); + } + ******************************************************************************/ int last_errno; - last_errno = proj_errno (P); + last_errno = proj_errno(P); - proj_context_errno_set (pj_get_ctx ((PJ *) P), 0); + proj_context_errno_set(pj_get_ctx((PJ *)P), 0); errno = 0; return last_errno; } - /* Create a new context based on the default context */ -PJ_CONTEXT *proj_context_create (void) { +PJ_CONTEXT *proj_context_create(void) { return new (std::nothrow) pj_ctx(*pj_get_default_ctx()); } - -PJ_CONTEXT *proj_context_destroy (PJ_CONTEXT *ctx) { - if (nullptr==ctx) +PJ_CONTEXT *proj_context_destroy(PJ_CONTEXT *ctx) { + if (nullptr == ctx) return nullptr; - /* Trying to free the default context is a no-op (since it is statically allocated) */ - if (pj_get_default_ctx ()==ctx) + /* Trying to free the default context is a no-op (since it is statically + * allocated) */ + if (pj_get_default_ctx() == ctx) return nullptr; delete ctx; return nullptr; } - - - - - /*****************************************************************************/ -static char *path_append (char *buf, const char *app, size_t *buf_size) { -/****************************************************************************** - Helper for proj_info() below. Append app to buf, separated by a - semicolon. Also handle allocation of longer buffer if needed. +static char *path_append(char *buf, const char *app, size_t *buf_size) { + /****************************************************************************** + Helper for proj_info() below. Append app to buf, separated by a + semicolon. Also handle allocation of longer buffer if needed. - Returns buffer and adjusts *buf_size through provided pointer arg. -******************************************************************************/ + Returns buffer and adjusts *buf_size through provided pointer arg. + ******************************************************************************/ char *p; size_t len, applen = 0, buflen = 0; #ifdef _WIN32 @@ -2099,70 +2510,70 @@ static char *path_append (char *buf, const char *app, size_t *buf_size) { /* Nothing to do? */ if (nullptr == app) return buf; - applen = strlen (app); + applen = strlen(app); if (0 == applen) return buf; /* Start checking whether buf is long enough */ if (nullptr != buf) - buflen = strlen (buf); - len = buflen+applen+strlen (delim) + 1; + buflen = strlen(buf); + len = buflen + applen + strlen(delim) + 1; /* "pj_realloc", so to speak */ if (*buf_size < len) { - p = static_cast(calloc (2 * len, sizeof (char))); - if (nullptr==p) { - free (buf); + p = static_cast(calloc(2 * len, sizeof(char))); + if (nullptr == p) { + free(buf); return nullptr; } *buf_size = 2 * len; if (buf != nullptr) - strcpy (p, buf); - free (buf); + strcpy(p, buf); + free(buf); buf = p; } assert(buf); /* Only append a delimiter if something's already there */ if (0 != buflen) - strcat (buf, delim); - strcat (buf, app); + strcat(buf, delim); + strcat(buf, app); return buf; } static const char *empty = {""}; -static char version[64] = {""}; +static char version[64] = {""}; static PJ_INFO info = {0, 0, 0, nullptr, nullptr, nullptr, nullptr, 0}; /*****************************************************************************/ -PJ_INFO proj_info (void) { -/****************************************************************************** - Basic info about the current instance of the PROJ.4 library. +PJ_INFO proj_info(void) { + /****************************************************************************** + Basic info about the current instance of the PROJ.4 library. - Returns PJ_INFO struct. -******************************************************************************/ - size_t buf_size = 0; - char *buf = nullptr; + Returns PJ_INFO struct. + ******************************************************************************/ + size_t buf_size = 0; + char *buf = nullptr; - pj_acquire_lock (); + pj_acquire_lock(); info.major = PROJ_VERSION_MAJOR; info.minor = PROJ_VERSION_MINOR; info.patch = PROJ_VERSION_PATCH; - /* This is a controlled environment, so no risk of sprintf buffer - overflow. A normal version string is xx.yy.zz which is 8 characters + /* A normal version string is xx.yy.zz which is 8 characters long and there is room for 64 bytes in the version string. */ - sprintf (version, "%d.%d.%d", info.major, info.minor, info.patch); + snprintf(version, sizeof(version), "%d.%d.%d", info.major, info.minor, + info.patch); - info.version = version; - info.release = pj_get_release (); + info.version = version; + info.release = pj_get_release(); /* build search path string */ auto ctx = pj_get_default_ctx(); if (ctx->search_paths.empty()) { const auto searchpaths = pj_get_default_searchpaths(ctx); - for( const auto& path: searchpaths ) { + for (const auto &path : searchpaths) { buf = path_append(buf, path.c_str(), &buf_size); } } else { @@ -2171,24 +2582,24 @@ PJ_INFO proj_info (void) { } } - free(const_cast(info.searchpath)); + if (info.searchpath != empty) + free(const_cast(info.searchpath)); info.searchpath = buf ? buf : empty; info.paths = ctx->c_compat_paths; info.path_count = static_cast(ctx->search_paths.size()); - pj_release_lock (); + pj_release_lock(); return info; } - /*****************************************************************************/ PJ_PROJ_INFO proj_pj_info(PJ *P) { -/****************************************************************************** - Basic info about a particular instance of a projection object. + /****************************************************************************** + Basic info about a particular instance of a projection object. - Returns PJ_PROJ_INFO struct. -******************************************************************************/ + Returns PJ_PROJ_INFO struct. + ******************************************************************************/ PJ_PROJ_INFO pjinfo; char *def; @@ -2196,42 +2607,68 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) { pjinfo.accuracy = -1.0; - if (nullptr==P) + if (nullptr == P) return pjinfo; /* coordinate operation description */ - if( P->iCurCoordOp >= 0 ) { - P = P->alternativeCoordinateOperations[P->iCurCoordOp].pj; - } else if( !P->alternativeCoordinateOperations.empty() ) { - pjinfo.id = "unknown"; - pjinfo.description = "unavailable until proj_trans is called"; - pjinfo.definition = "unavailable until proj_trans is called"; - return pjinfo; + if (!P->alternativeCoordinateOperations.empty()) { + if (P->iCurCoordOp >= 0) { + P = P->alternativeCoordinateOperations[P->iCurCoordOp].pj; + } else { + PJ *candidateOp = nullptr; + // If there's just a single coordinate operation which is + // instanciable, use it. + for (const auto &op : P->alternativeCoordinateOperations) { + if (op.isInstantiable()) { + if (candidateOp == nullptr) { + candidateOp = op.pj; + } else { + candidateOp = nullptr; + break; + } + } + } + if (candidateOp) { + P = candidateOp; + } else { + pjinfo.id = "unknown"; + pjinfo.description = "unavailable until proj_trans is called"; + pjinfo.definition = "unavailable until proj_trans is called"; + return pjinfo; + } + } } /* projection id */ if (pj_param(P->ctx, P->params, "tproj").i) pjinfo.id = pj_param(P->ctx, P->params, "sproj").s; - if( P->iso_obj ) { - pjinfo.description = P->iso_obj->nameStr().c_str(); - } else { - pjinfo.description = P->descr; + pjinfo.description = P->descr; + if (P->iso_obj) { + auto identifiedObj = + dynamic_cast(P->iso_obj.get()); + if (identifiedObj) { + pjinfo.description = identifiedObj->nameStr().c_str(); + } } // accuracy - if( P->iso_obj ) { - auto conv = dynamic_cast(P->iso_obj.get()); - if( conv ) { + if (P->iso_obj) { + auto conv = dynamic_cast( + P->iso_obj.get()); + if (conv) { pjinfo.accuracy = 0.0; } else { - auto op = dynamic_cast(P->iso_obj.get()); - if( op ) { - const auto& accuracies = op->coordinateOperationAccuracies(); - if( !accuracies.empty() ) { + auto op = + dynamic_cast( + P->iso_obj.get()); + if (op) { + const auto &accuracies = op->coordinateOperationAccuracies(); + if (!accuracies.empty()) { try { pjinfo.accuracy = std::stod(accuracies[0]->value()); - } catch ( const std::exception& ) {} + } catch (const std::exception &) { + } } } } @@ -2242,10 +2679,10 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) { def = P->def_full; else def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ - if (nullptr==def) + if (nullptr == def) pjinfo.definition = empty; else - pjinfo.definition = pj_shrink (def); + pjinfo.definition = pj_shrink(def); /* Make proj_destroy clean this up eventually */ P->def_full = def; @@ -2253,58 +2690,58 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) { return pjinfo; } - /*****************************************************************************/ PJ_GRID_INFO proj_grid_info(const char *gridname) { -/****************************************************************************** - Information about a named datum grid. + /****************************************************************************** + Information about a named datum grid. - Returns PJ_GRID_INFO struct. -******************************************************************************/ + Returns PJ_GRID_INFO struct. + ******************************************************************************/ PJ_GRID_INFO grinfo; /*PJ_CONTEXT *ctx = proj_context_create(); */ PJ_CONTEXT *ctx = pj_get_default_ctx(); memset(&grinfo, 0, sizeof(PJ_GRID_INFO)); - const auto fillGridInfo = [&grinfo, ctx, gridname] - (const NS_PROJ::Grid& grid, const std::string& format) - { - const auto& extent = grid.extentAndRes(); + const auto fillGridInfo = [&grinfo, ctx, + gridname](const NS_PROJ::Grid &grid, + const std::string &format) { + const auto &extent = grid.extentAndRes(); /* name of grid */ - strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); + strncpy(grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); /* full path of grid */ - if( pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1) ) - { - /* grid format */ - strncpy (grinfo.format, format.c_str(), sizeof(grinfo.format) - 1); - - /* grid size */ - grinfo.n_lon = grid.width(); - grinfo.n_lat = grid.height(); - - /* cell size */ - grinfo.cs_lon = extent.resX; - grinfo.cs_lat = extent.resY; - - /* bounds of grid */ - grinfo.lowerleft.lam = extent.west; - grinfo.lowerleft.phi = extent.south; - grinfo.upperright.lam = extent.east; - grinfo.upperright.phi = extent.north; + if (!pj_find_file(ctx, gridname, grinfo.filename, + sizeof(grinfo.filename) - 1)) { + // Can happen when using a remote grid + grinfo.filename[0] = 0; } + + /* grid format */ + strncpy(grinfo.format, format.c_str(), sizeof(grinfo.format) - 1); + + /* grid size */ + grinfo.n_lon = grid.width(); + grinfo.n_lat = grid.height(); + + /* cell size */ + grinfo.cs_lon = extent.resX; + grinfo.cs_lat = extent.resY; + + /* bounds of grid */ + grinfo.lowerleft.lam = extent.west; + grinfo.lowerleft.phi = extent.south; + grinfo.upperright.lam = extent.east; + grinfo.upperright.phi = extent.north; }; { const auto gridSet = NS_PROJ::VerticalShiftGridSet::open(ctx, gridname); - if( gridSet ) - { - const auto& grids = gridSet->grids(); - if( !grids.empty() ) - { - const auto& grid = grids.front(); + if (gridSet) { + const auto &grids = gridSet->grids(); + if (!grids.empty()) { + const auto &grid = grids.front(); fillGridInfo(*grid, gridSet->format()); return grinfo; } @@ -2312,13 +2749,12 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) { } { - const auto gridSet = NS_PROJ::HorizontalShiftGridSet::open(ctx, gridname); - if( gridSet ) - { - const auto& grids = gridSet->grids(); - if( !grids.empty() ) - { - const auto& grid = grids.front(); + const auto gridSet = + NS_PROJ::HorizontalShiftGridSet::open(ctx, gridname); + if (gridSet) { + const auto &grids = gridSet->grids(); + if (!grids.empty()) { + const auto &grid = grids.front(); fillGridInfo(*grid, gridSet->format()); return grinfo; } @@ -2328,23 +2764,21 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) { return grinfo; } - - /*****************************************************************************/ -PJ_INIT_INFO proj_init_info(const char *initname){ -/****************************************************************************** - Information about a named init file. +PJ_INIT_INFO proj_init_info(const char *initname) { + /****************************************************************************** + Information about a named init file. - Maximum length of initname is 64. + Maximum length of initname is 64. - Returns PJ_INIT_INFO struct. + Returns PJ_INIT_INFO struct. - If the init file is not found all members of the return struct are set - to the empty string. + If the init file is not found all members of the return struct are set + to the empty string. - If the init file is found, but the metadata is missing, the value is - set to "Unknown". -******************************************************************************/ + If the init file is found, but the metadata is missing, the value is + set to "Unknown". + ******************************************************************************/ int file_found; char param[80], key[74]; paralist *start, *next; @@ -2353,39 +2787,40 @@ PJ_INIT_INFO proj_init_info(const char *initname){ memset(&ininfo, 0, sizeof(PJ_INIT_INFO)); - file_found = pj_find_file(ctx, initname, ininfo.filename, sizeof(ininfo.filename)); + file_found = + pj_find_file(ctx, initname, ininfo.filename, sizeof(ininfo.filename)); if (!file_found || strlen(initname) > 64) { - if( strcmp(initname, "epsg") == 0 || strcmp(initname, "EPSG") == 0 ) { - const char* val; + if (strcmp(initname, "epsg") == 0 || strcmp(initname, "EPSG") == 0) { + const char *val; - proj_context_errno_set( ctx, 0 ); + proj_context_errno_set(ctx, 0); - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strncpy(ininfo.name, initname, sizeof(ininfo.name) - 1); strcpy(ininfo.origin, "EPSG"); val = proj_context_get_database_metadata(ctx, "EPSG.VERSION"); - if( val ) { + if (val) { strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); } val = proj_context_get_database_metadata(ctx, "EPSG.DATE"); - if( val ) { + if (val) { strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); } return ininfo; } - if( strcmp(initname, "IGNF") == 0 ) { - const char* val; + if (strcmp(initname, "IGNF") == 0) { + const char *val; - proj_context_errno_set( ctx, 0 ); + proj_context_errno_set(ctx, 0); - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strncpy(ininfo.name, initname, sizeof(ininfo.name) - 1); strcpy(ininfo.origin, "IGNF"); val = proj_context_get_database_metadata(ctx, "IGNF.VERSION"); - if( val ) { + if (val) { strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); } val = proj_context_get_database_metadata(ctx, "IGNF.DATE"); - if( val ) { + if (val) { strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); } return ininfo; @@ -2395,66 +2830,69 @@ PJ_INIT_INFO proj_init_info(const char *initname){ } /* The initial memset (0) makes strncpy safe here */ - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strncpy(ininfo.name, initname, sizeof(ininfo.name) - 1); strcpy(ininfo.origin, "Unknown"); strcpy(ininfo.version, "Unknown"); strcpy(ininfo.lastupdate, "Unknown"); - strncpy (key, initname, 64); /* make room for ":metadata\0" at the end */ + strncpy(key, initname, 64); /* make room for ":metadata\0" at the end */ key[64] = 0; memcpy(key + strlen(key), ":metadata", 9 + 1); strcpy(param, "+init="); /* The +strlen(param) avoids a cppcheck false positive warning */ - strncat(param + strlen(param), key, sizeof(param)-1-strlen(param)); + strncat(param + strlen(param), key, sizeof(param) - 1 - strlen(param)); start = pj_mkparam(param); pj_expand_init(ctx, start); if (pj_param(ctx, start, "tversion").i) - strncpy(ininfo.version, pj_param(ctx, start, "sversion").s, sizeof(ininfo.version) - 1); + strncpy(ininfo.version, pj_param(ctx, start, "sversion").s, + sizeof(ininfo.version) - 1); if (pj_param(ctx, start, "torigin").i) - strncpy(ininfo.origin, pj_param(ctx, start, "sorigin").s, sizeof(ininfo.origin) - 1); + strncpy(ininfo.origin, pj_param(ctx, start, "sorigin").s, + sizeof(ininfo.origin) - 1); if (pj_param(ctx, start, "tlastupdate").i) - strncpy(ininfo.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(ininfo.lastupdate) - 1); + strncpy(ininfo.lastupdate, pj_param(ctx, start, "slastupdate").s, + sizeof(ininfo.lastupdate) - 1); - for ( ; start; start = next) { + for (; start; start = next) { next = start->next; free(start); } - return ininfo; + return ininfo; } - - /*****************************************************************************/ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { -/****************************************************************************** - Cartographic characteristics at point lp. + /****************************************************************************** + Cartographic characteristics at point lp. - Characteristics include meridian, parallel and areal scales, angular - distortion, meridian/parallel, meridian convergence and scale error. + Characteristics include meridian, parallel and areal scales, angular + distortion, meridian/parallel, meridian convergence and scale error. - returns PJ_FACTORS. If unsuccessful, error number is set and the - struct returned contains NULL data. -******************************************************************************/ - PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0, 0,0,0,0}; + returns PJ_FACTORS. If unsuccessful, error number is set and the + struct returned contains NULL data. + ******************************************************************************/ + PJ_FACTORS factors = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct FACTORS f; - if (nullptr==P) + if (nullptr == P) return factors; const auto type = proj_get_type(P); - if( type == PJ_TYPE_PROJECTED_CRS ) - { + if (type == PJ_TYPE_PROJECTED_CRS) { // If it is a projected CRS, then compute the factors on the conversion // associated to it. We need to start from a temporary geographic CRS // using the same datum as the one of the projected CRS, and with // input coordinates being in longitude, latitude order in radian, // to be consistent with the expectations of the lp input parameter. + // We also need to create a modified projected CRS with a normalized + // easting/northing axis order in metre, so the resulting operation is + // just a single step pipeline with no axisswap or unitconvert steps. auto ctx = P->ctx; auto geodetic_crs = proj_get_source_crs(ctx, P); @@ -2463,44 +2901,53 @@ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { auto datum_ensemble = proj_crs_get_datum_ensemble(ctx, geodetic_crs); auto cs = proj_create_ellipsoidal_2D_cs( ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, "Radian", 1.0); - auto temp = proj_create_geographic_crs_from_datum( - ctx, "unnamed crs", datum ? datum : datum_ensemble, - cs); + auto geogCRSNormalized = proj_create_geographic_crs_from_datum( + ctx, "unnamed crs", datum ? datum : datum_ensemble, cs); proj_destroy(datum); proj_destroy(datum_ensemble); proj_destroy(cs); + auto conversion = proj_crs_get_coordoperation(ctx, P); + auto projCS = proj_create_cartesian_2D_cs( + ctx, PJ_CART2D_EASTING_NORTHING, "metre", 1.0); + auto projCRSNormalized = proj_create_projected_crs( + ctx, nullptr, geodetic_crs, conversion, projCS); + assert(projCRSNormalized); proj_destroy(geodetic_crs); - auto newOp = proj_create_crs_to_crs_from_pj(ctx, temp, P, nullptr, nullptr); - proj_destroy(temp); + proj_destroy(conversion); + proj_destroy(projCS); + auto newOp = proj_create_crs_to_crs_from_pj( + ctx, geogCRSNormalized, projCRSNormalized, nullptr, nullptr); + proj_destroy(geogCRSNormalized); + proj_destroy(projCRSNormalized); assert(newOp); + // For debugging: + // printf("%s\n", proj_as_proj_string(ctx, newOp, PJ_PROJ_5, nullptr)); auto ret = proj_factors(newOp, lp); proj_destroy(newOp); return ret; } - if( type != PJ_TYPE_CONVERSION && - type != PJ_TYPE_TRANSFORMATION && + if (type != PJ_TYPE_CONVERSION && type != PJ_TYPE_TRANSFORMATION && type != PJ_TYPE_CONCATENATED_OPERATION && - type != PJ_TYPE_OTHER_COORDINATE_OPERATION ) - { + type != PJ_TYPE_OTHER_COORDINATE_OPERATION) { proj_log_error(P, _("Invalid type for P object")); - proj_errno_set (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); + proj_errno_set(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); return factors; } if (pj_factors(lp.lp, P, 0.0, &f)) return factors; - factors.meridional_scale = f.h; - factors.parallel_scale = f.k; - factors.areal_scale = f.s; + factors.meridional_scale = f.h; + factors.parallel_scale = f.k; + factors.areal_scale = f.s; - factors.angular_distortion = f.omega; - factors.meridian_parallel_angle = f.thetap; - factors.meridian_convergence = f.conv; + factors.angular_distortion = f.omega; + factors.meridian_parallel_angle = f.thetap; + factors.meridian_convergence = f.conv; - factors.tissot_semimajor = f.a; - factors.tissot_semiminor = f.b; + factors.tissot_semimajor = f.a; + factors.tissot_semiminor = f.b; /* Raw derivatives, for completeness's sake */ factors.dx_dlam = f.der.x_l; @@ -2511,3 +2958,88 @@ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { return factors; } +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress + +// Returns true if the passed operation uses NADCON5 grids for NAD83 to +// NAD83(HARN) +static bool isSpecialCaseForNAD83_to_NAD83HARN(const std::string &opName) { + return opName.find("NAD83 to NAD83(HARN) (47)") != std::string::npos || + opName.find("NAD83 to NAD83(HARN) (48)") != std::string::npos || + opName.find("NAD83 to NAD83(HARN) (49)") != std::string::npos || + opName.find("NAD83 to NAD83(HARN) (50)") != std::string::npos; +} + +// Returns true if the passed operation uses "GDA94 to WGS 84 (1)", which +// is the null transformation +static bool isSpecialCaseForGDA94_to_WGS84(const std::string &opName) { + return opName.find("GDA94 to WGS 84 (1)") != std::string::npos; +} + +// Returns true if the passed operation uses "GDA2020 to WGS 84 (2)", which +// is the null transformation +static bool isSpecialCaseForWGS84_to_GDA2020(const std::string &opName) { + return opName.find("GDA2020 to WGS 84 (2)") != std::string::npos; +} + +PJCoordOperation::PJCoordOperation( + int idxInOriginalListIn, double minxSrcIn, double minySrcIn, + double maxxSrcIn, double maxySrcIn, double minxDstIn, double minyDstIn, + double maxxDstIn, double maxyDstIn, PJ *pjIn, const std::string &nameIn, + double accuracyIn, bool isOffshoreIn, const PJ *pjSrcGeocentricToLonLatIn, + const PJ *pjDstGeocentricToLonLatIn) + : idxInOriginalList(idxInOriginalListIn), minxSrc(minxSrcIn), + minySrc(minySrcIn), maxxSrc(maxxSrcIn), maxySrc(maxySrcIn), + minxDst(minxDstIn), minyDst(minyDstIn), maxxDst(maxxDstIn), + maxyDst(maxyDstIn), pj(pjIn), name(nameIn), accuracy(accuracyIn), + isOffshore(isOffshoreIn), + isPriorityOp(isSpecialCaseForNAD83_to_NAD83HARN(name) || + isSpecialCaseForGDA94_to_WGS84(name) || + isSpecialCaseForWGS84_to_GDA2020(name)), + pjSrcGeocentricToLonLat(pjSrcGeocentricToLonLatIn + ? proj_clone(pjSrcGeocentricToLonLatIn->ctx, + pjSrcGeocentricToLonLatIn) + : nullptr), + pjDstGeocentricToLonLat(pjDstGeocentricToLonLatIn + ? proj_clone(pjDstGeocentricToLonLatIn->ctx, + pjDstGeocentricToLonLatIn) + : nullptr) { + const auto IsLonLatOrLatLon = [](const PJ *crs, bool &isLonLatDegreeOut, + bool &isLatLonDegreeOut) { + const auto eType = proj_get_type(crs); + if (eType == PJ_TYPE_GEOGRAPHIC_2D_CRS || + eType == PJ_TYPE_GEOGRAPHIC_3D_CRS) { + const auto cs = proj_crs_get_coordinate_system(crs->ctx, crs); + const char *direction = ""; + double conv_factor = 0; + constexpr double EPS = 1e-14; + if (proj_cs_get_axis_info(crs->ctx, cs, 0, nullptr, nullptr, + &direction, &conv_factor, nullptr, + nullptr, nullptr) && + ci_equal(direction, "East")) { + isLonLatDegreeOut = fabs(conv_factor - M_PI / 180) < EPS; + } else if (proj_cs_get_axis_info(crs->ctx, cs, 1, nullptr, nullptr, + &direction, &conv_factor, nullptr, + nullptr, nullptr) && + ci_equal(direction, "East")) { + isLatLonDegreeOut = fabs(conv_factor - M_PI / 180) < EPS; + } + proj_destroy(cs); + } + }; + + const auto source = proj_get_source_crs(pj->ctx, pj); + if (source) { + IsLonLatOrLatLon(source, srcIsLonLatDegree, srcIsLatLonDegree); + proj_destroy(source); + } + + const auto target = proj_get_target_crs(pj->ctx, pj); + if (target) { + IsLonLatOrLatLon(target, dstIsLonLatDegree, dstIsLatLonDegree); + proj_destroy(target); + } +} + +//! @endcond diff --git a/deps/libproj/proj/src/CMakeLists.txt b/deps/libproj/proj/src/CMakeLists.txt index 01e069794..589893d3f 100644 --- a/deps/libproj/proj/src/CMakeLists.txt +++ b/deps/libproj/proj/src/CMakeLists.txt @@ -5,9 +5,12 @@ add_compile_options("$<$:${PROJ_CXX_WARN_FLAGS}>") # First configure proj library include(lib_proj.cmake) add_subdirectory(apps) -add_subdirectory(tests) + +if(BUILD_TESTING) + add_subdirectory(tests) +endif() if(APPLE) # Directory name for installed targets - set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") + set(CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}) endif() diff --git a/deps/libproj/proj/src/aasincos.cpp b/deps/libproj/proj/src/aasincos.cpp index ca33663bb..d77132ddf 100644 --- a/deps/libproj/proj/src/aasincos.cpp +++ b/deps/libproj/proj/src/aasincos.cpp @@ -5,35 +5,33 @@ #include "proj.h" #include "proj_internal.h" -#define ONE_TOL 1.00000000000001 +#define ONE_TOL 1.00000000000001 #define ATOL 1e-50 - double -aasin(PJ_CONTEXT *ctx,double v) { - double av; +double aasin(PJ_CONTEXT *ctx, double v) { + double av; - if ((av = fabs(v)) >= 1.) { - if (av > ONE_TOL) - proj_context_errno_set( ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN ); - return (v < 0. ? -M_HALFPI : M_HALFPI); - } - return asin(v); + if ((av = fabs(v)) >= 1.) { + if (av > ONE_TOL) + proj_context_errno_set( + ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); + return (v < 0. ? -M_HALFPI : M_HALFPI); + } + return asin(v); } - double -aacos(PJ_CONTEXT *ctx, double v) { - double av; +double aacos(PJ_CONTEXT *ctx, double v) { + double av; - if ((av = fabs(v)) >= 1.) { - if (av > ONE_TOL) - proj_context_errno_set( ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN ); - return (v < 0. ? M_PI : 0.); - } - return acos(v); + if ((av = fabs(v)) >= 1.) { + if (av > ONE_TOL) + proj_context_errno_set( + ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); + return (v < 0. ? M_PI : 0.); + } + return acos(v); } - double -asqrt(double v) { return ((v <= 0) ? 0. : sqrt(v)); } - double -aatan2(double n, double d) { - return ((fabs(n) < ATOL && fabs(d) < ATOL) ? 0. : atan2(n,d)); +double asqrt(double v) { return ((v <= 0) ? 0. : sqrt(v)); } +double aatan2(double n, double d) { + return ((fabs(n) < ATOL && fabs(d) < ATOL) ? 0. : atan2(n, d)); } diff --git a/deps/libproj/proj/src/adjlon.cpp b/deps/libproj/proj/src/adjlon.cpp index 112a91526..692b60d5d 100644 --- a/deps/libproj/proj/src/adjlon.cpp +++ b/deps/libproj/proj/src/adjlon.cpp @@ -4,19 +4,20 @@ #include "proj.h" #include "proj_internal.h" -double adjlon (double lon) { - /* Let lon slightly overshoot, to avoid spurious sign switching at the date line */ - if (fabs (lon) < M_PI + 1e-12) - return lon; +double adjlon(double longitude) { + /* Let longitude slightly overshoot, to avoid spurious sign switching at the + * date line */ + if (fabs(longitude) < M_PI + 1e-12) + return longitude; /* adjust to 0..2pi range */ - lon += M_PI; + longitude += M_PI; /* remove integral # of 'revolutions'*/ - lon -= M_TWOPI * floor(lon / M_TWOPI); + longitude -= M_TWOPI * floor(longitude / M_TWOPI); /* adjust back to -pi..pi range */ - lon -= M_PI; + longitude -= M_PI; - return lon; + return longitude; } diff --git a/deps/libproj/proj/src/apps/cct.cpp b/deps/libproj/proj/src/apps/cct.cpp index ca62c5709..0d9c6a6d7 100644 --- a/deps/libproj/proj/src/apps/cct.cpp +++ b/deps/libproj/proj/src/apps/cct.cpp @@ -73,47 +73,57 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 #include #include +#include #include #include #include -#include +#include #include // std::ifstream #include +#include "optargpm.h" #include "proj.h" #include "proj_internal.h" #include "proj_strtod.h" -#include "optargpm.h" - static void logger(void *data, int level, const char *msg); static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...); /* Prototypes from functions in this file */ -char *column (char *buf, int n); -PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time); - +static const char *column(const char *buf, int n); +static char *column(char *buf, int n); +PJ_COORD parse_input_line(const char *buf, int *columns, double fixed_height, + double fixed_time); static const char usage[] = { - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Usage: %s [-options]... [+operator_specs]... infile...\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Options:\n" - "--------------------------------------------------------------------------------\n" - " -c x,y,z,t Specify input columns for (up to) 4 input parameters.\n" + "--------------------------------------------------------------------------" + "------\n" + " -c x,y,z,t Specify input columns for (up to) 4 input " + "parameters.\n" " Defaults to 1,2,3,4\n" " -d n Specify number of decimals in output.\n" " -I Do the inverse transformation\n" " -o /path/to/file Specify output file name\n" - " -t value Provide a fixed t value for all input data (e.g. -t 0)\n" - " -z value Provide a fixed z value for all input data (e.g. -z 0)\n" + " -t value Provide a fixed t value for all input data (e.g. -t " + "0)\n" + " -z value Provide a fixed z value for all input data (e.g. -z " + "0)\n" " -s n Skip n first lines of a infile\n" - " -v Verbose: Provide non-essential informational output.\n" + " -v Verbose: Provide non-essential informational " + "output.\n" " Repeat -v for more verbosity (e.g. -vv)\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Long Options:\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" " --output Alias for -o\n" " --columns Alias for -c\n" " --decimals Alias for -d\n" @@ -124,14 +134,17 @@ static const char usage[] = { " --skip-lines Alias for -s\n" " --help Alias for -h\n" " --version Print version number\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Operator Specs:\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "The operator specs describe the action to be performed by cct, e.g:\n" "\n" " +proj=utm +ellps=GRS80 +zone=32\n" "\n" - "instructs cct to convert input data to Universal Transverse Mercator, zone 32\n" + "instructs cct to convert input data to Universal Transverse Mercator, " + "zone 32\n" "coordinates, based on the GRS80 ellipsoid.\n" "\n" "Hence, the command\n" @@ -141,26 +154,29 @@ static const char usage[] = { "Should give results comparable to the classic proj command\n" "\n" " echo 12 55 | proj +proj=utm +zone=32 +ellps=GRS80\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Examples:\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "1. convert geographical input to UTM zone 32 on the GRS80 ellipsoid:\n" " cct +proj=utm +ellps=GRS80 +zone=32\n" "2. roundtrip accuracy check for the case above:\n" " cct +proj=pipeline +proj=utm +ellps=GRS80 +zone=32 +step +step +inv\n" - "3. as (1) but specify input columns for longitude, latitude, height and time:\n" + "3. as (1) but specify input columns for longitude, latitude, height and " + "time:\n" " cct -c 5,2,1,4 +proj=utm +ellps=GRS80 +zone=32\n" - "4. as (1) but specify fixed height and time, hence needing only 2 cols in input:\n" + "4. as (1) but specify fixed height and time, hence needing only 2 cols in " + "input:\n" " cct -t 0 -z 0 +proj=utm +ellps=GRS80 +zone=32\n" - "--------------------------------------------------------------------------------\n" -}; - + "--------------------------------------------------------------------------" + "------\n"}; static void logger(void *data, int level, const char *msg) { FILE *stream; int log_tell = proj_log_level(PJ_DEFAULT_CTX, PJ_LOG_TELL); - stream = (FILE *) data; + stream = (FILE *)data; /* if we use PJ_LOG_NONE we always want to print stuff to stream */ if (level == PJ_LOG_NONE) { @@ -180,20 +196,21 @@ static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...) { va_list args; char *msg_buf; - va_start( args, fmt ); + va_start(args, fmt); - msg_buf = (char *) malloc(100000); - if( msg_buf == nullptr ) { - va_end( args ); + const size_t msg_buf_size = 100000; + msg_buf = (char *)malloc(msg_buf_size); + if (msg_buf == nullptr) { + va_end(args); return; } - vsprintf( msg_buf, fmt, args ); + vsnprintf(msg_buf, msg_buf_size, fmt, args); - logger((void *) fout, log_level, msg_buf); + logger((void *)fout, log_level, msg_buf); - va_end( args ); - free( msg_buf ); + va_end(args); + free(msg_buf); } int main(int argc, char **argv) { @@ -203,98 +220,97 @@ int main(int argc, char **argv) { OPTARGS *o; char blank_comment[] = ""; char whitespace[] = " "; - char *comment; - char *comment_delimiter; - char *buf; int i, nfields = 4, skip_lines = 0, verbose; double fixed_z = HUGE_VAL, fixed_time = HUGE_VAL; int decimals_angles = 10; int decimals_distances = 4; int columns_xyzt[] = {1, 2, 3, 4}; - const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", nullptr}; - const char *longkeys[] = { - "o=output", - "c=columns", - "d=decimals", - "z=height", - "t=time", - "s=skip-lines", - nullptr}; + const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", + nullptr}; + const char *longkeys[] = {"o=output", "c=columns", "d=decimals", + "z=height", "t=time", "s=skip-lines", + nullptr}; fout = stdout; + pj_stderr_proj_lib_deprecation_warning(); + /* coverity[tainted_data] */ - o = opt_parse (argc, argv, "hvI", "cdozts", longflags, longkeys); - if (nullptr==o) + o = opt_parse(argc, argv, "hvI", "cdozts", longflags, longkeys); + if (nullptr == o) return 0; - if (opt_given (o, "h") || argc==1) { - printf (usage, o->progname); + if (opt_given(o, "h") || argc == 1) { + printf(usage, o->progname); return 0; } - PJ_DIRECTION direction = opt_given (o, "I")? PJ_INV: PJ_FWD; + PJ_DIRECTION direction = opt_given(o, "I") ? PJ_INV : PJ_FWD; - verbose = MIN(opt_given (o, "v"), 3); /* log level can't be larger than 3 */ - if( verbose > 0 ) { - proj_log_level (PJ_DEFAULT_CTX, static_cast(verbose)); + verbose = + std::min(opt_given(o, "v"), 3); /* log level can't be larger than 3 */ + if (verbose > 0) { + proj_log_level(PJ_DEFAULT_CTX, static_cast(verbose)); } - proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); + proj_log_func(PJ_DEFAULT_CTX, (void *)fout, logger); - if (opt_given (o, "version")) { - print (PJ_LOG_NONE, "%s: %s", o->progname, pj_get_release ()); + if (opt_given(o, "version")) { + print(PJ_LOG_NONE, "%s: %s", o->progname, pj_get_release()); return 0; } - if (opt_given (o, "o")) - fout = fopen (opt_arg (o, "output"), "wt"); - if (nullptr==fout) { - print (PJ_LOG_ERROR, "%s: Cannot open '%s' for output", o->progname, opt_arg (o, "output")); - free (o); + if (opt_given(o, "o")) + fout = fopen(opt_arg(o, "output"), "wt"); + if (nullptr == fout) { + print(PJ_LOG_ERROR, "%s: Cannot open '%s' for output", o->progname, + opt_arg(o, "output")); + free(o); return 1; } - print (PJ_LOG_TRACE, "%s: Running in very verbose mode", o->progname); + print(PJ_LOG_TRACE, "%s: Running in very verbose mode", o->progname); - if (opt_given (o, "z")) { - fixed_z = proj_atof (opt_arg (o, "z")); + if (opt_given(o, "z")) { + fixed_z = proj_atof(opt_arg(o, "z")); nfields--; } - if (opt_given (o, "t")) { - fixed_time = proj_atof (opt_arg (o, "t")); + if (opt_given(o, "t")) { + fixed_time = proj_atof(opt_arg(o, "t")); nfields--; } - if (opt_given (o, "d")) { - int dec = atoi (opt_arg (o, "d")); + if (opt_given(o, "d")) { + int dec = atoi(opt_arg(o, "d")); decimals_angles = dec; decimals_distances = dec; } - if (opt_given (o, "s")) { - skip_lines = atoi (opt_arg(o, "s")); + if (opt_given(o, "s")) { + skip_lines = atoi(opt_arg(o, "s")); } - if (opt_given (o, "c")) { + if (opt_given(o, "c")) { int ncols; /* reset column numbers to ease comment output later on */ - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) columns_xyzt[i] = 0; /* cppcheck-suppress invalidscanf */ - ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+2, columns_xyzt+3); + ncols = sscanf(opt_arg(o, "c"), "%d,%d,%d,%d", columns_xyzt, + columns_xyzt + 1, columns_xyzt + 2, columns_xyzt + 3); if (ncols != nfields) { - print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'", o->progname, opt_arg (o, "c")); - free (o); + print(PJ_LOG_ERROR, "%s: Too few input columns given: '%s'", + o->progname, opt_arg(o, "c")); + free(o); if (stdout != fout) - fclose (fout); + fclose(fout); return 1; } } /* Setup transformation */ - if (o-> pargc == 0 && o->fargc > 0) { + if (o->pargc == 0 && o->fargc > 0) { std::string input(o->fargv[0]); if (!input.empty() && input[0] == '@') { @@ -322,187 +338,223 @@ int main(int argc, char **argv) { /* Assume we got a auth:code combination */ auto n = input.find(":"); if (n > 0) { - std::string auth = input.substr(0,n); - std::string code = input.substr(n+1, input.length()); + std::string auth = input.substr(0, n); + std::string code = input.substr(n + 1, input.length()); // Check that the authority matches one of the known ones auto authorityList = proj_get_authorities_from_database(nullptr); - if( authorityList ) - { - for( auto iter = authorityList; *iter; iter++ ) - { - if( *iter == auth ) { + if (authorityList) { + for (auto iter = authorityList; *iter; iter++) { + if (*iter == auth) { P = proj_create_from_database( - nullptr, auth.c_str(), code.c_str(), - PJ_CATEGORY_COORDINATE_OPERATION, 0, nullptr); + nullptr, auth.c_str(), code.c_str(), + PJ_CATEGORY_COORDINATE_OPERATION, 0, nullptr); break; } } proj_string_list_destroy(authorityList); } } - if( P == nullptr ) { - /* if we didn't get a auth:code combo we try to see if the input matches */ + if (P == nullptr) { + /* if we didn't get a auth:code combo we try to see if the input + * matches + */ /* anything else */ P = proj_create(nullptr, input.c_str()); + if (P) { + const auto type = proj_get_type(P); + switch (type) { + case PJ_TYPE_CONVERSION: + case PJ_TYPE_TRANSFORMATION: + case PJ_TYPE_CONCATENATED_OPERATION: + case PJ_TYPE_OTHER_COORDINATE_OPERATION: + // ok; + break; + default: + print(PJ_LOG_ERROR, + "%s: Input object is not a coordinate operation%s.", + o->progname, proj_is_crs(P) ? ", but a CRS" : ""); + free(o); + proj_destroy(P); + if (stdout != fout) + fclose(fout); + return 1; + } + } } - /* If instantiating operation without +-options optargpm thinks the input is */ - /* a file, hence we move all o->fargv entries one place closer to the start */ - /* of the array. This effectively overwrites the input and only leaves a list */ - /* of files in o->fargv. */ - o->fargc = o->fargc-1; - for (int j=0; j < o->fargc; j++) { - o->fargv[j] = o->fargv[j+1]; + /* If instantiating operation without +-options optargpm thinks the + * input is + */ + /* a file, hence we move all o->fargv entries one place closer to the + * start + */ + /* of the array. This effectively overwrites the input and only leaves a + * list */ + /* of files in o->fargv. */ + o->fargc = o->fargc - 1; + for (int j = 0; j < o->fargc; j++) { + o->fargv[j] = o->fargv[j + 1]; } } else { - P = proj_create_argv (nullptr, o->pargc, o->pargv); + P = proj_create_argv(nullptr, o->pargc, o->pargv); } - if (nullptr==P) { - print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help", - o->progname, proj_errno_string (proj_errno(P)), o->progname); - free (o); + if (nullptr == P) { + print(PJ_LOG_ERROR, + "%s: Bad transformation arguments - (%s)\n '%s -h' for help", + o->progname, proj_errno_string(proj_errno(P)), o->progname); + free(o); if (stdout != fout) - fclose (fout); + fclose(fout); return 1; } - info = proj_pj_info (P); - print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d", info.definition, argc, o->pargc); + info = proj_pj_info(P); + print(PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d", info.definition, argc, + o->pargc); - if (direction== PJ_INV) { + if (direction == PJ_INV) { /* fail if an inverse operation is not available */ if (!info.has_inverse) { - print (PJ_LOG_ERROR, "Inverse operation not available"); + print(PJ_LOG_ERROR, "Inverse operation not available"); if (stdout != fout) - fclose (fout); + fclose(fout); return 1; } - /* We have no API call for inverting an operation, so we brute force it. */ + /* We have no API call for inverting an operation, so we brute force it. + */ P->inverted = !(P->inverted); } direction = PJ_FWD; /* Allocate input buffer */ - buf = static_cast(calloc (1, 10000)); - if (nullptr==buf) { - print (PJ_LOG_ERROR, "%s: Out of memory", o->progname); - proj_destroy (P); - free (o); + constexpr int BUFFER_SIZE = 10000; + char *buf = static_cast(calloc(1, BUFFER_SIZE)); + if (nullptr == buf) { + print(PJ_LOG_ERROR, "%s: Out of memory", o->progname); + proj_destroy(P); + free(o); if (stdout != fout) - fclose (fout); + fclose(fout); return 1; } - /* Loop over all records of all input files */ - while (opt_input_loop (o, optargs_file_format_text)) { + int previous_index = -1; + while (opt_input_loop(o, optargs_file_format_text)) { int err; - void *ret = fgets (buf, 10000, o->input); - char *c = column (buf, 1); - opt_eof_handler (o); - if (nullptr==ret) { - print (PJ_LOG_ERROR, "Read error in record %d", (int) o->record_index); + char *bufptr = fgets(buf, BUFFER_SIZE - 1, o->input); + if (opt_eof(o)) { continue; } - point = parse_input_line (buf, columns_xyzt, fixed_z, fixed_time); + if (nullptr == bufptr) { + print(PJ_LOG_ERROR, "Read error in record %d", + (int)o->record_index); + continue; + } + + const bool bFirstLine = o->input_index != previous_index; + previous_index = o->input_index; + if (bFirstLine && static_cast(bufptr[0]) == 0xEF && + static_cast(bufptr[1]) == 0xBB && + static_cast(bufptr[2]) == 0xBF) { + // Skip UTF-8 Byte Order Marker (BOM) + bufptr += 3; + } + + point = parse_input_line(bufptr, columns_xyzt, fixed_z, fixed_time); if (skip_lines > 0) { skip_lines--; continue; } /* if it's a comment or blank line, we reflect it */ - if (c && ((*c=='\0') || (*c=='#'))) { - fprintf (fout, "%s", buf); + const char *c = column(bufptr, 1); + if (c && ((*c == '\0') || (*c == '#'))) { + fprintf(fout, "%s", bufptr); continue; } - if (HUGE_VAL==point.xyzt.x) { + if (HUGE_VAL == point.xyzt.x) { /* otherwise, it must be a syntax error */ - print (PJ_LOG_NONE, "# Record %d UNREADABLE: %s", (int) o->record_index, buf); - print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d", o->progname, opt_filename (o), opt_record (o)); + print(PJ_LOG_NONE, "# Record %d UNREADABLE: %s", + (int)o->record_index, bufptr); + print(PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d", + o->progname, opt_filename(o), opt_record(o)); continue; } - if (proj_angular_input (P, direction)) { - point.lpzt.lam = proj_torad (point.lpzt.lam); - point.lpzt.phi = proj_torad (point.lpzt.phi); + if (proj_angular_input(P, direction)) { + point.lpzt.lam = proj_torad(point.lpzt.lam); + point.lpzt.phi = proj_torad(point.lpzt.phi); } - err = proj_errno_reset (P); + err = proj_errno_reset(P); /* coverity[returned_value] */ - point = proj_trans (P, direction, point); + point = proj_trans(P, direction, point); - if (HUGE_VAL==point.xyzt.x) { + if (HUGE_VAL == point.xyzt.x) { /* transformation error */ - print (PJ_LOG_NONE, "# Record %d TRANSFORMATION ERROR: %s (%s)", - (int) o->record_index, buf, proj_errno_string (proj_errno(P))); - proj_errno_restore (P, err); + print(PJ_LOG_NONE, "# Record %d TRANSFORMATION ERROR: %s (%s)", + (int)o->record_index, bufptr, + proj_errno_string(proj_errno(P))); + proj_errno_restore(P, err); continue; } - proj_errno_restore (P, err); + proj_errno_restore(P, err); /* handle comment string */ - comment = column(buf, nfields+1); + char *comment = column(bufptr, nfields + 1); if (opt_given(o, "c")) { /* what number is the last coordinate column in the input data? */ int colmax = 0; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) colmax = MAX(colmax, columns_xyzt[i]); - comment = column(buf, colmax+1); + comment = column(bufptr, colmax + 1); } /* remove the line feed from comment, as logger() above, invoked by print() below (output), will add one */ size_t len = strlen(comment); if (len >= 1) comment[len - 1] = '\0'; - comment_delimiter = *comment ? whitespace : blank_comment; + const char *comment_delimiter = *comment ? whitespace : blank_comment; /* Time to print the result */ /* use same arguments to printf format string for both radians and degrees; convert radians to degrees before printing */ - if (proj_angular_output (P, direction) || - proj_degree_output (P, direction)) { - if (proj_angular_output (P, direction)) { - point.lpzt.lam = proj_todeg (point.lpzt.lam); - point.lpzt.phi = proj_todeg (point.lpzt.phi); + if (proj_angular_output(P, direction) || + proj_degree_output(P, direction)) { + if (proj_angular_output(P, direction)) { + point.lpzt.lam = proj_todeg(point.lpzt.lam); + point.lpzt.phi = proj_todeg(point.lpzt.phi); } - print (PJ_LOG_NONE, "%14.*f %14.*f %12.*f %12.4f%s%s", - decimals_angles, point.xyzt.x, - decimals_angles, point.xyzt.y, - decimals_distances, point.xyzt.z, - point.xyzt.t, comment_delimiter, comment - ); - } - else - print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s", - decimals_distances, point.xyzt.x, - decimals_distances, point.xyzt.y, - decimals_distances, point.xyzt.z, - point.xyzt.t, comment_delimiter, comment - ); - if( fout == stdout ) + print(PJ_LOG_NONE, "%14.*f %14.*f %12.*f %12.4f%s%s", + decimals_angles, point.xyzt.x, decimals_angles, point.xyzt.y, + decimals_distances, point.xyzt.z, point.xyzt.t, + comment_delimiter, comment); + } else + print(PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s", + decimals_distances, point.xyzt.x, decimals_distances, + point.xyzt.y, decimals_distances, point.xyzt.z, point.xyzt.t, + comment_delimiter, comment); + if (fout == stdout) fflush(stdout); } proj_destroy(P); if (stdout != fout) - fclose (fout); - free (o); - free (buf); + fclose(fout); + free(o); + free(buf); return 0; } - - - - /* return a pointer to the n'th column of buf */ -char *column (char *buf, int n) { +static const char *column(const char *buf, int n) { int i; if (n <= 0) return buf; - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) { while (isspace(*buf)) buf++; if (i == n - 1) @@ -513,34 +565,38 @@ char *column (char *buf, int n) { return buf; } +static char *column(char *buf, int n) { + return const_cast(column(const_cast(buf), n)); +} + /* column to double */ -static double cold (char *args, int col) { +static double cold(const char *args, int col) { char *endp; - char *target; double d; - target = column (args, col); - d = proj_strtod (target, &endp); - if (endp==target) + const char *target = column(args, col); + d = proj_strtod(target, &endp); + if (endp == target) return HUGE_VAL; return d; } -PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time) { - PJ_COORD err = proj_coord (HUGE_VAL, HUGE_VAL, HUGE_VAL, HUGE_VAL); +PJ_COORD parse_input_line(const char *buf, int *columns, double fixed_height, + double fixed_time) { + PJ_COORD err = proj_coord(HUGE_VAL, HUGE_VAL, HUGE_VAL, HUGE_VAL); PJ_COORD result = err; int prev_errno = errno; errno = 0; result.xyzt.z = fixed_height; result.xyzt.t = fixed_time; - result.xyzt.x = cold (buf, columns[0]); - result.xyzt.y = cold (buf, columns[1]); - if (result.xyzt.z==HUGE_VAL) - result.xyzt.z = cold (buf, columns[2]); - if (result.xyzt.t==HUGE_VAL) - result.xyzt.t = cold (buf, columns[3]); - - if (0!=errno) + result.xyzt.x = cold(buf, columns[0]); + result.xyzt.y = cold(buf, columns[1]); + if (result.xyzt.z == HUGE_VAL) + result.xyzt.z = cold(buf, columns[2]); + if (result.xyzt.t == HUGE_VAL) + result.xyzt.t = cold(buf, columns[3]); + + if (0 != errno) return err; errno = prev_errno; diff --git a/deps/libproj/proj/src/apps/cs2cs.cpp b/deps/libproj/proj/src/apps/cs2cs.cpp index a8e152a79..d4485fbe5 100644 --- a/deps/libproj/proj/src/apps/cs2cs.cpp +++ b/deps/libproj/proj/src/apps/cs2cs.cpp @@ -77,8 +77,11 @@ static char oform_buffer[16]; /* buffer for oform when using -d */ static const char *oterr = "*\t*"; /* output line for unprojectable input */ static const char *usage = "%s\nusage: %s [-dDeEfIlrstvwW [args]]\n" - " [[--area name_or_code] | [--bbox west_long,south_lat,east_long,north_lat]]\n" - " [--authority {name}] [--accuracy {accuracy}] [--no-ballpark]\n" + " [[--area name_or_code] | [--bbox " + "west_long,south_lat,east_long,north_lat]]\n" + " [--authority {name}] [--3d]\n" + " [--accuracy {accuracy}] [--only-best[=yes|=no]] " + "[--no-ballpark]\n" " [+opt[=arg] ...] [+to +opt[=arg] ...] [file ...]\n"; static double (*informat)(const char *, @@ -99,13 +102,23 @@ static void process(FILE *fid) { char line[MAX_LINE + 3], *s, pline[40]; PJ_UV data; + int nLineNumber = 0; - for (;;) { + while (true) { double z; - + ++nLineNumber; ++emess_dat.File_line; if (!(s = fgets(line, MAX_LINE, fid))) break; + + if (nLineNumber == 1 && static_cast(s[0]) == 0xEF && + static_cast(s[1]) == 0xBB && + static_cast(s[2]) == 0xBF) { + // Skip UTF-8 Byte Order Marker (BOM) + s += 3; + } + const char *pszLineAfterBOM = s; + if (!strchr(s, '\n')) { /* overlong line */ int c; (void)strcat(s, "\n"); @@ -137,7 +150,7 @@ static void process(FILE *fid) /* is forward verbatim from the input. */ char *before_time = s; double t = strtod(s, &s); - if( s == before_time ) + if (s == before_time) t = HUGE_VAL; s = before_time; @@ -151,7 +164,7 @@ static void process(FILE *fid) char temp; temp = *s; *s = '\0'; - (void)fputs(line, stdout); + (void)fputs(pszLineAfterBOM, stdout); *s = temp; putchar('\t'); } @@ -187,22 +200,26 @@ static void process(FILE *fid) if (destIsLatLong) { if (reverseout) { - fputs(rtodms(pline, data.v, 'E', 'W'), stdout); + fputs(rtodms(pline, sizeof(pline), data.v, 'E', 'W'), + stdout); putchar('\t'); - fputs(rtodms(pline, data.u, 'N', 'S'), stdout); + fputs(rtodms(pline, sizeof(pline), data.u, 'N', 'S'), + stdout); } else { - fputs(rtodms(pline, data.u, 'N', 'S'), stdout); + fputs(rtodms(pline, sizeof(pline), data.u, 'N', 'S'), + stdout); putchar('\t'); - fputs(rtodms(pline, data.v, 'E', 'W'), stdout); + fputs(rtodms(pline, sizeof(pline), data.v, 'E', 'W'), + stdout); } } else if (reverseout) { - fputs(rtodms(pline, data.v, 'N', 'S'), stdout); + fputs(rtodms(pline, sizeof(pline), data.v, 'N', 'S'), stdout); putchar('\t'); - fputs(rtodms(pline, data.u, 'E', 'W'), stdout); + fputs(rtodms(pline, sizeof(pline), data.u, 'E', 'W'), stdout); } else { - fputs(rtodms(pline, data.u, 'E', 'W'), stdout); + fputs(rtodms(pline, sizeof(pline), data.u, 'E', 'W'), stdout); putchar('\t'); - fputs(rtodms(pline, data.v, 'N', 'S'), stdout); + fputs(rtodms(pline, sizeof(pline), data.v, 'N', 'S'), stdout); } } else { /* x-y or decimal degree ascii output */ @@ -238,11 +255,10 @@ static void process(FILE *fid) /* instantiate_crs() */ /************************************************************************/ -static PJ *instantiate_crs(const std::string &definition, - bool &isLongLatCS, double &toRadians, - bool &isLatFirst) { - PJ *crs = proj_create(nullptr, - pj_add_type_crs_if_needed(definition).c_str()); +static PJ *instantiate_crs(const std::string &definition, bool &isLongLatCS, + double &toRadians, bool &isLatFirst) { + PJ *crs = + proj_create(nullptr, pj_add_type_crs_if_needed(definition).c_str()); if (!crs) { return nullptr; } @@ -259,27 +275,26 @@ static PJ *instantiate_crs(const std::string &definition, type = proj_get_type(crs); } if (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || - type == PJ_TYPE_GEOGRAPHIC_3D_CRS || - type == PJ_TYPE_GEODETIC_CRS) { + type == PJ_TYPE_GEOGRAPHIC_3D_CRS || type == PJ_TYPE_GEODETIC_CRS) { auto cs = proj_crs_get_coordinate_system(nullptr, crs); assert(cs); const char *axisName = ""; proj_cs_get_axis_info(nullptr, cs, 0, - &axisName, // name, - nullptr, // abbrev - nullptr, // direction - &toRadians, - nullptr, // unit name - nullptr, // unit authority - nullptr // unit code - ); + &axisName, // name, + nullptr, // abbrev + nullptr, // direction + &toRadians, + nullptr, // unit name + nullptr, // unit authority + nullptr // unit code + ); isLatFirst = NS_PROJ::internal::ci_find(std::string(axisName), "latitude") != std::string::npos; - isLongLatCS = isLatFirst || - NS_PROJ::internal::ci_find(std::string(axisName), "longitude") != - std::string::npos; + isLongLatCS = isLatFirst || NS_PROJ::internal::ci_find( + std::string(axisName), "longitude") != + std::string::npos; proj_destroy(cs); } @@ -313,14 +328,14 @@ static std::string get_geog_crs_proj_string_from_proj_crs(PJ *src, const char *axisName = ""; proj_cs_get_axis_info(nullptr, cs, 0, - &axisName, // name, - nullptr, // abbrev - nullptr, // direction - &toRadians, - nullptr, // unit name - nullptr, // unit authority - nullptr // unit code - ); + &axisName, // name, + nullptr, // abbrev + nullptr, // direction + &toRadians, + nullptr, // unit name + nullptr, // unit authority + nullptr // unit code + ); isLatFirst = NS_PROJ::internal::ci_find(std::string(axisName), "latitude") != std::string::npos; @@ -332,6 +347,25 @@ static std::string get_geog_crs_proj_string_from_proj_crs(PJ *src, return ret; } +// --------------------------------------------------------------------------- + +static bool is3DCRS(const PJ *crs) { + auto type = proj_get_type(crs); + if (type == PJ_TYPE_COMPOUND_CRS) + return true; + if (type == PJ_TYPE_GEOGRAPHIC_3D_CRS) + return true; + if (type == PJ_TYPE_GEODETIC_CRS || type == PJ_TYPE_PROJECTED_CRS || + type == PJ_TYPE_DERIVED_PROJECTED_CRS) { + auto cs = proj_crs_get_coordinate_system(nullptr, crs); + assert(cs); + const bool ret = proj_cs_get_axis_count(nullptr, cs) == 3; + proj_destroy(cs); + return ret; + } + return false; +} + /************************************************************************/ /* main() */ /************************************************************************/ @@ -346,7 +380,9 @@ int main(int argc, char **argv) { int have_to_flag = 0, inverse = 0; int use_env_locale = 0; - if( argc == 0 ) { + pj_stderr_proj_lib_deprecation_warning(); + + if (argc == 0) { exit(1); } @@ -370,7 +406,8 @@ int main(int argc, char **argv) { exit(0); } - // First pass to check if we have "cs2cs [-bla]* []" syntax + // First pass to check if we have "cs2cs [-bla]* []" + // syntax bool isProj4StyleSyntax = false; for (int i = 1; i < argc; i++) { if (argv[i][0] == '+') { @@ -381,26 +418,28 @@ int main(int argc, char **argv) { ExtentPtr bboxFilter; std::string area; - const char* authority = nullptr; + const char *authority = nullptr; double accuracy = -1; bool allowBallpark = true; + bool onlyBestSet = false; + bool errorIfBestTransformationNotAvailable = false; + bool promoteTo3D = false; /* process run line arguments */ while (--argc > 0) { /* collect run line arguments */ ++argv; - if (strcmp(*argv, "--area") == 0 ) { + if (strcmp(*argv, "--area") == 0) { ++argv; --argc; - if( argc == 0 ) { + if (argc == 0) { emess(1, "missing argument for --area"); std::exit(1); } area = *argv; - } - else if (strcmp(*argv, "--bbox") == 0) { + } else if (strcmp(*argv, "--bbox") == 0) { ++argv; --argc; - if( argc == 0 ) { + if (argc == 0) { emess(1, "missing argument for --bbox"); std::exit(1); } @@ -421,35 +460,40 @@ int main(int argc, char **argv) { << ", " << e.what() << std::endl; std::exit(1); } - } - else if (strcmp(*argv, "--accuracy") == 0 ) { + } else if (strcmp(*argv, "--accuracy") == 0) { ++argv; --argc; - if( argc == 0 ) { + if (argc == 0) { emess(1, "missing argument for --accuracy"); std::exit(1); } try { accuracy = c_locale_stod(*argv); } catch (const std::exception &e) { - std::cerr << "Invalid value for option --accuracy: " - << e.what() << std::endl; + std::cerr << "Invalid value for option --accuracy: " << e.what() + << std::endl; std::exit(1); } - } - else if (strcmp(*argv, "--authority") == 0 ) { + } else if (strcmp(*argv, "--authority") == 0) { ++argv; --argc; - if( argc == 0 ) { + if (argc == 0) { emess(1, "missing argument for --authority"); std::exit(1); } authority = *argv; - } - else if (strcmp(*argv, "--no-ballpark") == 0 ) { + } else if (strcmp(*argv, "--no-ballpark") == 0) { allowBallpark = false; - } - else if (**argv == '-') { + } else if (strcmp(*argv, "--only-best") == 0 || + strcmp(*argv, "--only-best=yes") == 0) { + onlyBestSet = true; + errorIfBestTransformationNotAvailable = true; + } else if (strcmp(*argv, "--only-best=no") == 0) { + onlyBestSet = true; + errorIfBestTransformationNotAvailable = false; + } else if (strcmp(*argv, "--3d") == 0) { + promoteTo3D = true; + } else if (**argv == '-') { for (arg = *argv;;) { switch (*++arg) { case '\0': /* position of "stdin" */ @@ -505,15 +549,14 @@ int main(int argc, char **argv) { (void)printf("%9s %-16s %-16s %s\n", le->id, le->major, le->ell, le->name); } else if (arg[1] == 'u') { /* list units */ - auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr); - for( int i = 0; units && units[i]; i++ ) - { - if( units[i]->proj_short_name ) - { + auto units = proj_get_units_from_database( + nullptr, nullptr, "linear", false, nullptr); + for (int i = 0; units && units[i]; i++) { + if (units[i]->proj_short_name) { (void)printf("%12s %-20.15g %s\n", - units[i]->proj_short_name, - units[i]->conv_factor, - units[i]->name); + units[i]->proj_short_name, + units[i]->conv_factor, + units[i]->name); } } proj_unit_list_destroy(units); @@ -530,18 +573,20 @@ int main(int argc, char **argv) { case 'e': /* error line alternative */ if (--argc <= 0) noargument: - emess(1, "missing argument for -%c", *arg); + emess(1, "missing argument for -%c", *arg); oterr = *++argv; continue; case 'W': /* specify seconds precision */ case 'w': /* -W for constant field width */ { char c = arg[1]; - if (c != 0 && isdigit(c)) { + // Check that the value is in the [0, 8] range + if (c >= '0' && c <= '8' && + ((arg[2] == 0 || !(arg[2] >= '0' && arg[2] <= '9')))) { set_rtodms(c - '0', *arg == 'W'); ++arg; } else - emess(1, "-W argument missing or non-digit"); + emess(1, "-W argument missing or not in range [0,8]"); continue; } case 'f': /* alternate output format degrees or xy */ @@ -576,7 +621,8 @@ int main(int argc, char **argv) { case 'd': if (--argc <= 0) goto noargument; - sprintf(oform_buffer, "%%.%df", atoi(*++argv)); + snprintf(oform_buffer, sizeof(oform_buffer), "%%.%df", + atoi(*++argv)); oform = oform_buffer; break; default: @@ -588,10 +634,10 @@ int main(int argc, char **argv) { } else if (!isProj4StyleSyntax) { if (fromStr.empty()) fromStr = *argv; - else if( toStr.empty() ) + else if (toStr.empty()) toStr = *argv; else { - /* assumed to be input file name(s) */ + /* assumed to be input file name(s) */ eargv[eargc++] = *argv; } } else if (strcmp(*argv, "+to") == 0) { @@ -617,8 +663,8 @@ int main(int argc, char **argv) { if (eargc == 0) /* if no specific files force sysin */ eargv[eargc++] = const_cast("-"); - if( oform ) { - if( !validate_form_string_for_numbers(oform) ) { + if (oform) { + if (!validate_form_string_for_numbers(oform)) { emess(3, "invalid format string"); exit(0); } @@ -629,16 +675,15 @@ int main(int argc, char **argv) { std::exit(1); } - PJ_AREA* pj_area = nullptr; + PJ_AREA *pj_area = nullptr; if (!area.empty()) { DatabaseContextPtr dbContext; try { - dbContext = - DatabaseContext::create().as_nullable(); + dbContext = DatabaseContext::create().as_nullable(); } catch (const std::exception &e) { std::cerr << "ERROR: Cannot create database connection: " - << e.what() << std::endl; + << e.what() << std::endl; std::exit(1); } @@ -651,9 +696,9 @@ int main(int argc, char **argv) { const std::string &areaAuth = tokens[0]; const std::string &areaCode = tokens[1]; bboxFilter = AuthorityFactory::create( - NN_NO_CHECK(dbContext), areaAuth) - ->createExtent(areaCode) - .as_nullable(); + NN_NO_CHECK(dbContext), areaAuth) + ->createExtent(areaCode) + .as_nullable(); } } if (!bboxFilter) { @@ -661,35 +706,34 @@ int main(int argc, char **argv) { NN_NO_CHECK(dbContext), std::string()); auto res = authFactory->listAreaOfUseFromName(area, false); if (res.size() == 1) { - bboxFilter = - AuthorityFactory::create(NN_NO_CHECK(dbContext), - res.front().first) - ->createExtent(res.front().second) - .as_nullable(); + bboxFilter = AuthorityFactory::create( + NN_NO_CHECK(dbContext), res.front().first) + ->createExtent(res.front().second) + .as_nullable(); } else { res = authFactory->listAreaOfUseFromName(area, true); if (res.size() == 1) { bboxFilter = AuthorityFactory::create(NN_NO_CHECK(dbContext), - res.front().first) + res.front().first) ->createExtent(res.front().second) .as_nullable(); } else if (res.empty()) { std::cerr << "No area of use matching provided name" - << std::endl; + << std::endl; std::exit(1); } else { std::cerr << "Several candidates area of use " - "matching provided name :" - << std::endl; + "matching provided name :" + << std::endl; for (const auto &candidate : res) { auto obj = - AuthorityFactory::create( - NN_NO_CHECK(dbContext), candidate.first) + AuthorityFactory::create(NN_NO_CHECK(dbContext), + candidate.first) ->createExtent(candidate.second); std::cerr << " " << candidate.first << ":" - << candidate.second << " : " - << *obj->description() << std::endl; + << candidate.second << " : " + << *obj->description() << std::endl; } std::exit(1); } @@ -697,25 +741,26 @@ int main(int argc, char **argv) { } } catch (const std::exception &e) { std::cerr << "Area of use retrieval failed: " << e.what() - << std::endl; + << std::endl; std::exit(1); } } if (bboxFilter) { auto geogElts = bboxFilter->geographicElements(); - if (geogElts.size() == 1) - { + if (geogElts.size() == 1) { auto bbox = std::dynamic_pointer_cast( geogElts[0].as_nullable()); - if (bbox) - { + if (bbox) { pj_area = proj_area_create(); - proj_area_set_bbox(pj_area, - bbox->westBoundLongitude(), + proj_area_set_bbox(pj_area, bbox->westBoundLongitude(), bbox->southBoundLatitude(), bbox->eastBoundLongitude(), bbox->northBoundLatitude()); + if (bboxFilter->description().has_value()) { + proj_area_set_name(pj_area, + bboxFilter->description()->c_str()); + } } } } @@ -737,14 +782,13 @@ int main(int argc, char **argv) { emess(3, "missing source and target coordinate systems"); } - proj_context_use_proj4_init_rules(nullptr, - proj_context_get_use_proj4_init_rules(nullptr, TRUE)); + proj_context_use_proj4_init_rules( + nullptr, proj_context_get_use_proj4_init_rules(nullptr, TRUE)); PJ *src = nullptr; if (!fromStr.empty()) { bool ignored; - src = instantiate_crs(fromStr, srcIsLongLat, - srcToRadians, ignored); + src = instantiate_crs(fromStr, srcIsLongLat, srcToRadians, ignored); if (!src) { emess(3, "cannot instantiate source coordinate system"); } @@ -752,8 +796,8 @@ int main(int argc, char **argv) { PJ *dst = nullptr; if (!toStr.empty()) { - dst = instantiate_crs(toStr, destIsLongLat, - destToRadians, destIsLatLong); + dst = + instantiate_crs(toStr, destIsLongLat, destToRadians, destIsLatLong); if (!dst) { emess(3, "cannot instantiate target coordinate system"); } @@ -785,40 +829,76 @@ int main(int argc, char **argv) { src = proj_create(nullptr, pj_add_type_crs_if_needed(fromStr).c_str()); dst = proj_create(nullptr, pj_add_type_crs_if_needed(toStr).c_str()); - if( proj_get_type(src) == PJ_TYPE_COMPOUND_CRS || - proj_get_type(dst) == PJ_TYPE_COMPOUND_CRS ) { + if (promoteTo3D) { auto src3D = proj_crs_promote_to_3D(nullptr, nullptr, src); - if( src3D ) { + if (src3D) { proj_destroy(src); src = src3D; } auto dst3D = proj_crs_promote_to_3D(nullptr, nullptr, dst); - if( dst3D ) { + if (dst3D) { proj_destroy(dst); dst = dst3D; } + } else { + // Auto-promote source/target CRS if it is specified by its name, + // if it has a known 3D version of it and that the other CRS is 3D. + // e.g cs2cs "WGS 84 + EGM96 height" "WGS 84" + if (is3DCRS(dst) && !is3DCRS(src) && + proj_get_id_code(src, 0) != nullptr && + Identifier::isEquivalentName(fromStr.c_str(), proj_get_name(src))) { + auto promoted = proj_crs_promote_to_3D(nullptr, nullptr, src); + if (promoted) { + if (proj_get_id_code(promoted, 0) != nullptr) { + proj_destroy(src); + src = promoted; + } else { + proj_destroy(promoted); + } + } + } else if (is3DCRS(src) && !is3DCRS(dst) && + proj_get_id_code(dst, 0) != nullptr && + Identifier::isEquivalentName(toStr.c_str(), + proj_get_name(dst))) { + auto promoted = proj_crs_promote_to_3D(nullptr, nullptr, dst); + if (promoted) { + if (proj_get_id_code(promoted, 0) != nullptr) { + proj_destroy(dst); + dst = promoted; + } else { + proj_destroy(promoted); + } + } + } } std::string authorityOption; /* keep this variable in this outer scope ! */ - std::string accuracyOption; /* keep this variable in this outer scope ! */ - std::vector options; - if( authority ) { + std::string accuracyOption; /* keep this variable in this outer scope ! */ + std::vector options; + if (authority) { authorityOption = "AUTHORITY="; authorityOption += authority; options.push_back(authorityOption.data()); } - if( accuracy >= 0 ) { + if (accuracy >= 0) { accuracyOption = "ACCURACY="; accuracyOption += toString(accuracy); options.push_back(accuracyOption.data()); } - if( !allowBallpark ) { + if (!allowBallpark) { options.push_back("ALLOW_BALLPARK=NO"); } + if (onlyBestSet) { + if (errorIfBestTransformationNotAvailable) { + options.push_back("ONLY_BEST=YES"); + } else { + options.push_back("ONLY_BEST=NO"); + } + } options.push_back(nullptr); - transformation = proj_create_crs_to_crs_from_pj(nullptr, src, dst, - pj_area, options.data()); + transformation = proj_create_crs_to_crs_from_pj(nullptr, src, dst, pj_area, + options.data()); proj_destroy(src); proj_destroy(dst); diff --git a/deps/libproj/proj/src/apps/emess.cpp b/deps/libproj/proj/src/apps/emess.cpp index 5a50cd257..145766050 100644 --- a/deps/libproj/proj/src/apps/emess.cpp +++ b/deps/libproj/proj/src/apps/emess.cpp @@ -1,12 +1,12 @@ /* Error message processing */ #ifdef _MSC_VER -# ifndef _CRT_SECURE_NO_DEPRECATE -# define _CRT_SECURE_NO_DEPRECATE -# endif -# ifndef _CRT_NONSTDC_NO_DEPRECATE -# define _CRT_NONSTDC_NO_DEPRECATE -# endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif #endif #include @@ -15,51 +15,53 @@ #include #include -#include "proj_internal.h" #include "proj_config.h" +#include "proj_internal.h" #define EMESS_ROUTINE #include "emess.h" - void -emess(int code, const char *fmt, ...) { - va_list args; +void emess(int code, const char *fmt, ...) { + va_list args; - va_start(args, fmt); - /* prefix program name, if given */ - if (emess_dat.Prog_name != nullptr) - (void)fprintf(stderr,"%s\n<%s>: ",pj_get_release(), - emess_dat.Prog_name); - /* print file name and line, if given */ - if (emess_dat.File_name != nullptr && *emess_dat.File_name) { - (void)fprintf(stderr,"while processing file: %s", emess_dat.File_name); - if (emess_dat.File_line > 0) - (void)fprintf(stderr,", line %d\n", emess_dat.File_line); - else - (void)fputc('\n', stderr); - } else - putc('\n', stderr); - /* if |code|==2, print errno code data */ - if (code == 2 || code == -2) - { - int my_errno = errno; + va_start(args, fmt); + /* prefix program name, if given */ + if (emess_dat.Prog_name != nullptr) { + // For unit test purposes, allow PROJ_DISPLAY_PROGRAM_NAME=NO + const char *pszDisplayProgramName = getenv("PROJ_DISPLAY_PROGRAM_NAME"); + if (!(pszDisplayProgramName && + strcmp(pszDisplayProgramName, "NO") == 0)) { + (void)fprintf(stderr, "%s\n<%s>: ", pj_get_release(), + emess_dat.Prog_name); + } + } + /* print file name and line, if given */ + if (emess_dat.File_name != nullptr && *emess_dat.File_name) { + (void)fprintf(stderr, "while processing file: %s", emess_dat.File_name); + if (emess_dat.File_line > 0) + (void)fprintf(stderr, ", line %d\n", emess_dat.File_line); + else + (void)fputc('\n', stderr); + } else + putc('\n', stderr); + /* if |code|==2, print errno code data */ + if (code == 2 || code == -2) { + int my_errno = errno; #ifdef HAVE_STRERROR - const char* my_strerror = strerror(my_errno); + const char *my_strerror = strerror(my_errno); #endif #ifndef HAVE_STRERROR - const char* my_strerror = ""; + const char *my_strerror = ""; #endif - (void)fprintf(stderr, "Sys errno: %d: %s\n", - my_errno, my_strerror); - } + (void)fprintf(stderr, "Sys errno: %d: %s\n", my_errno, my_strerror); + } - /* post remainder of call data */ - (void)vfprintf(stderr,fmt,args); - va_end(args); - /* die if code positive */ - if (code > 0) { - (void)fputs("\nprogram abnormally terminated\n", stderr); - exit(code); - } - else - putc('\n', stderr); + /* post remainder of call data */ + (void)vfprintf(stderr, fmt, args); + va_end(args); + /* die if code positive */ + if (code > 0) { + (void)fputs("\nprogram abnormally terminated\n", stderr); + exit(code); + } else + putc('\n', stderr); } diff --git a/deps/libproj/proj/src/apps/emess.h b/deps/libproj/proj/src/apps/emess.h index a0a009f14..b09262e5f 100644 --- a/deps/libproj/proj/src/apps/emess.h +++ b/deps/libproj/proj/src/apps/emess.h @@ -3,17 +3,17 @@ #define EMESS_H struct EMESS { - char *File_name, /* input file name */ - *Prog_name; /* name of program */ - int File_line; /* approximate line read - where error occurred */ + char *File_name, /* input file name */ + *Prog_name; /* name of program */ + int File_line; /* approximate line read + where error occurred */ }; -#ifdef EMESS_ROUTINE /* use type */ +#ifdef EMESS_ROUTINE /* use type */ /* for emess procedure */ -struct EMESS emess_dat = { nullptr, nullptr, 0 }; +struct EMESS emess_dat = {nullptr, nullptr, 0}; -#else /* for for calling procedures */ +#else /* for for calling procedures */ extern struct EMESS emess_dat; diff --git a/deps/libproj/proj/src/apps/geod.cpp b/deps/libproj/proj/src/apps/geod.cpp index 4f48a57a7..f5fd960f0 100644 --- a/deps/libproj/proj/src/apps/geod.cpp +++ b/deps/libproj/proj/src/apps/geod.cpp @@ -1,254 +1,281 @@ /* <<<< Geodesic filter program >>>> */ +#include "emess.h" +#include "geod_interface.h" #include "proj.h" -# include "proj_internal.h" -# include "geod_interface.h" -# include "emess.h" -# include -# include -# include +#include "proj_internal.h" +#include +#include +#include -# define MAXLINE 200 -# define MAX_PARGS 50 -# define TAB putchar('\t') - static int -fullout = 0, /* output full set of geodesic values */ -tag = '#', /* beginning of line tag character */ -pos_azi = 0, /* output azimuths as positive values */ -inverse = 0; /* != 0 then inverse geodesic */ +#define MAXLINE 200 +#define MAX_PARGS 50 +#define TAB putchar('\t') +static int fullout = 0, /* output full set of geodesic values */ + tag = '#', /* beginning of line tag character */ + pos_azi = 0, /* output azimuths as positive values */ + inverse = 0; /* != 0 then inverse geodesic */ static const char *oform = nullptr; /* output format for decimal degrees */ static const char *osform = "%.3f"; /* output format for S */ -static char pline[50]; /* work string */ +static char pline[50]; /* work string */ static const char *usage = -"%s\nusage: %s [-afFIlptwW [args]] [+opt[=arg] ...] [file ...]\n"; + "%s\nusage: %s [-afFIlptwW [args]] [+opt[=arg] ...] [file ...]\n"; - static void -printLL(double p, double l) { - if (oform) { - (void)printf(oform, p * RAD_TO_DEG); TAB; - (void)printf(oform, l * RAD_TO_DEG); - } else { - (void)fputs(rtodms(pline, p, 'N', 'S'),stdout); TAB; - (void)fputs(rtodms(pline, l, 'E', 'W'),stdout); - } +static void printLL(double p, double l) { + if (oform) { + (void)printf(oform, p * RAD_TO_DEG); + TAB; + (void)printf(oform, l * RAD_TO_DEG); + } else { + (void)fputs(rtodms(pline, sizeof(pline), p, 'N', 'S'), stdout); + TAB; + (void)fputs(rtodms(pline, sizeof(pline), l, 'E', 'W'), stdout); + } } - static void -do_arc(void) { - double az; +static void do_arc(void) { + double az; - printLL(phi2, lam2); putchar('\n'); - for (az = al12; n_alpha--; ) { - al12 = az = adjlon(az + del_alpha); - geod_pre(); - geod_for(); - printLL(phi2, lam2); putchar('\n'); - } + printLL(phi2, lam2); + putchar('\n'); + for (az = al12; n_alpha--;) { + al12 = az = adjlon(az + del_alpha); + geod_pre(); + geod_for(); + printLL(phi2, lam2); + putchar('\n'); + } } - static void /* generate intermediate geodesic coordinates */ +static void /* generate intermediate geodesic coordinates */ do_geod(void) { - double phil, laml, del_S; + double phil, laml, del_S; - phil = phi2; - laml = lam2; - printLL(phi1, lam1); putchar('\n'); - for ( geod_S = del_S = geod_S / n_S; --n_S; geod_S += del_S) { - geod_for(); - printLL(phi2, lam2); putchar('\n'); - } - printLL(phil, laml); putchar('\n'); + phil = phi2; + laml = lam2; + printLL(phi1, lam1); + putchar('\n'); + for (geod_S = del_S = geod_S / n_S; --n_S; geod_S += del_S) { + geod_for(); + printLL(phi2, lam2); + putchar('\n'); + } + printLL(phil, laml); + putchar('\n'); } - static void /* file processing function */ +static void /* file processing function */ process(FILE *fid) { - char line[MAXLINE+3], *s; + char line[MAXLINE + 3], *s; - for (;;) { - ++emess_dat.File_line; - if (!(s = fgets(line, MAXLINE, fid))) - break; - if (!strchr(s, '\n')) { /* overlong line */ - int c; - strcat(s, "\n"); - /* gobble up to newline */ - while ((c = fgetc(fid)) != EOF && c != '\n') ; - } - if (*s == tag) { - fputs(line, stdout); - continue; - } - phi1 = dmstor(s, &s); - lam1 = dmstor(s, &s); - if (inverse) { - phi2 = dmstor(s, &s); - lam2 = dmstor(s, &s); - geod_inv(); - } else { - al12 = dmstor(s, &s); - geod_S = strtod(s, &s) * to_meter; - geod_pre(); - geod_for(); - } - if (!*s && (s > line)) --s; /* assumed we gobbled \n */ - if (pos_azi) { - if (al12 < 0.) al12 += M_TWOPI; - if (al21 < 0.) al21 += M_TWOPI; - } - if (fullout) { - printLL(phi1, lam1); TAB; - printLL(phi2, lam2); TAB; - if (oform) { - (void)printf(oform, al12 * RAD_TO_DEG); TAB; - (void)printf(oform, al21 * RAD_TO_DEG); TAB; - (void)printf(osform, geod_S * fr_meter); - } else { - (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; - (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; - (void)printf(osform, geod_S * fr_meter); - } - } else if (inverse) - if (oform) { - (void)printf(oform, al12 * RAD_TO_DEG); TAB; - (void)printf(oform, al21 * RAD_TO_DEG); TAB; - (void)printf(osform, geod_S * fr_meter); - } else { - (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; - (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; - (void)printf(osform, geod_S * fr_meter); - } - else { - printLL(phi2, lam2); TAB; - if (oform) - (void)printf(oform, al21 * RAD_TO_DEG); - else - (void)fputs(rtodms(pline, al21, 0, 0), stdout); - } - (void)fputs(s, stdout); - fflush(stdout); - } + for (;;) { + ++emess_dat.File_line; + if (!(s = fgets(line, MAXLINE, fid))) + break; + if (!strchr(s, '\n')) { /* overlong line */ + int c; + strcat(s, "\n"); + /* gobble up to newline */ + while ((c = fgetc(fid)) != EOF && c != '\n') + ; + } + if (*s == tag) { + fputs(line, stdout); + continue; + } + phi1 = dmstor(s, &s); + lam1 = dmstor(s, &s); + if (inverse) { + phi2 = dmstor(s, &s); + lam2 = dmstor(s, &s); + geod_inv(); + } else { + al12 = dmstor(s, &s); + geod_S = strtod(s, &s) * to_meter; + geod_pre(); + geod_for(); + } + if (!*s && (s > line)) + --s; /* assumed we gobbled \n */ + if (pos_azi) { + if (al12 < 0.) + al12 += M_TWOPI; + if (al21 < 0.) + al21 += M_TWOPI; + } + if (fullout) { + printLL(phi1, lam1); + TAB; + printLL(phi2, lam2); + TAB; + if (oform) { + (void)printf(oform, al12 * RAD_TO_DEG); + TAB; + (void)printf(oform, al21 * RAD_TO_DEG); + TAB; + (void)printf(osform, geod_S * fr_meter); + } else { + (void)fputs(rtodms(pline, sizeof(pline), al12, 0, 0), stdout); + TAB; + (void)fputs(rtodms(pline, sizeof(pline), al21, 0, 0), stdout); + TAB; + (void)printf(osform, geod_S * fr_meter); + } + } else if (inverse) + if (oform) { + (void)printf(oform, al12 * RAD_TO_DEG); + TAB; + (void)printf(oform, al21 * RAD_TO_DEG); + TAB; + (void)printf(osform, geod_S * fr_meter); + } else { + (void)fputs(rtodms(pline, sizeof(pline), al12, 0, 0), stdout); + TAB; + (void)fputs(rtodms(pline, sizeof(pline), al21, 0, 0), stdout); + TAB; + (void)printf(osform, geod_S * fr_meter); + } + else { + printLL(phi2, lam2); + TAB; + if (oform) + (void)printf(oform, al21 * RAD_TO_DEG); + else + (void)fputs(rtodms(pline, sizeof(pline), al21, 0, 0), stdout); + } + (void)fputs(s, stdout); + fflush(stdout); + } } static char *pargv[MAX_PARGS]; -static int pargc = 0; +static int pargc = 0; int main(int argc, char **argv) { - char *arg, **eargv = argv; - FILE *fid; - static int eargc = 0, c; + char *arg, **eargv = argv; + FILE *fid; + static int eargc = 0, c; - if( argc == 0 ) { - exit(1); - } + if (argc == 0) { + exit(1); + } - if ((emess_dat.Prog_name = strrchr(*argv,'/')) != nullptr) ++emess_dat.Prog_name; - else emess_dat.Prog_name = *argv; - inverse = strncmp(emess_dat.Prog_name, "inv", 3) == 0 || - strncmp(emess_dat.Prog_name, "lt-inv", 6) == 0; // older libtool have a lt- prefix - if (argc <= 1 ) { - (void)fprintf(stderr, usage, pj_get_release(), - emess_dat.Prog_name); - exit (0); - } - /* process run line arguments */ - while (--argc > 0) { /* collect run line arguments */ - if(**++argv == '-') for(arg = *argv;;) { - switch(*++arg) { - case '\0': /* position of "stdin" */ - if (arg[-1] == '-') eargv[eargc++] = const_cast("-"); - break; - case 'a': /* output full set of values */ - fullout = 1; - continue; - case 'I': /* alt. inverse spec. */ - inverse = 1; - continue; - case 't': /* set col. one char */ - if (arg[1]) tag = *++arg; - else emess(1,"missing -t col. 1 tag"); - continue; - case 'W': /* specify seconds precision */ - case 'w': /* -W for constant field width */ - if ((c = arg[1]) && isdigit(c)) { - set_rtodms(c - '0', *arg == 'W'); - ++arg; - } else - emess(1,"-W argument missing or non-digit"); - continue; - case 'f': /* alternate output format degrees or xy */ - if (--argc <= 0) -noargument: emess(1,"missing argument for -%c",*arg); - oform = *++argv; - continue; - case 'F': /* alternate output format degrees or xy */ - if (--argc <= 0) goto noargument; - osform = *++argv; - continue; - case 'l': - if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ - const struct PJ_ELLPS *le; - - for (le=proj_list_ellps(); le->id ; ++le) - (void)printf("%9s %-16s %-16s %s\n", - le->id, le->major, le->ell, le->name); - } else if (arg[1] == 'u') { /* list of units */ - auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr); - for( int i = 0; units && units[i]; i++ ) - { - if( units[i]->proj_short_name ) - { - (void)printf("%12s %-20.15g %s\n", - units[i]->proj_short_name, - units[i]->conv_factor, - units[i]->name); - } - } - proj_unit_list_destroy(units); - } else - emess(1,"invalid list option: l%c",arg[1]); - exit( 0 ); - case 'p': /* output azimuths as positive */ - pos_azi = 1; - continue; - default: - emess(1, "invalid option: -%c",*arg); - break; - } - break; - } else if (**argv == '+') /* + argument */ - if (pargc < MAX_PARGS) - pargv[pargc++] = *argv + 1; - else - emess(1,"overflowed + argument table"); - else /* assumed to be input file name(s) */ - eargv[eargc++] = *argv; - } - /* done with parameter and control input */ - geod_set(pargc, pargv); /* setup projection */ - if ((n_alpha || n_S) && eargc) - emess(1,"files specified for arc/geodesic mode"); - if (n_alpha) - do_arc(); - else if (n_S) - do_geod(); - else { /* process input file list */ - if (eargc == 0) /* if no specific files force sysin */ - eargv[eargc++] = const_cast("-"); - for ( ; eargc-- ; ++eargv) { - if (**eargv == '-') { - fid = stdin; - emess_dat.File_name = const_cast(""); - } else { - if ((fid = fopen(*eargv, "r")) == nullptr) { - emess(-2, *eargv, "input file"); - continue; - } - emess_dat.File_name = *eargv; - } - emess_dat.File_line = 0; - process(fid); - (void)fclose(fid); - emess_dat.File_name = (char *)nullptr; - } - } - exit(0); /* normal completion */ + if ((emess_dat.Prog_name = strrchr(*argv, '/')) != nullptr) + ++emess_dat.Prog_name; + else + emess_dat.Prog_name = *argv; + inverse = strncmp(emess_dat.Prog_name, "inv", 3) == 0 || + strncmp(emess_dat.Prog_name, "lt-inv", 6) == + 0; // older libtool have a lt- prefix + if (argc <= 1) { + (void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name); + exit(0); + } + /* process run line arguments */ + while (--argc > 0) { /* collect run line arguments */ + if (**++argv == '-') + for (arg = *argv;;) { + switch (*++arg) { + case '\0': /* position of "stdin" */ + if (arg[-1] == '-') + eargv[eargc++] = const_cast("-"); + break; + case 'a': /* output full set of values */ + fullout = 1; + continue; + case 'I': /* alt. inverse spec. */ + inverse = 1; + continue; + case 't': /* set col. one char */ + if (arg[1]) + tag = *++arg; + else + emess(1, "missing -t col. 1 tag"); + continue; + case 'W': /* specify seconds precision */ + case 'w': /* -W for constant field width */ + if ((c = arg[1]) && isdigit(c)) { + set_rtodms(c - '0', *arg == 'W'); + ++arg; + } else + emess(1, "-W argument missing or non-digit"); + continue; + case 'f': /* alternate output format degrees or xy */ + if (--argc <= 0) + noargument: + emess(1, "missing argument for -%c", *arg); + oform = *++argv; + continue; + case 'F': /* alternate output format degrees or xy */ + if (--argc <= 0) + goto noargument; + osform = *++argv; + continue; + case 'l': + if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ + const struct PJ_ELLPS *le; + + for (le = proj_list_ellps(); le->id; ++le) + (void)printf("%9s %-16s %-16s %s\n", le->id, + le->major, le->ell, le->name); + } else if (arg[1] == 'u') { /* list of units */ + auto units = proj_get_units_from_database( + nullptr, nullptr, "linear", false, nullptr); + for (int i = 0; units && units[i]; i++) { + if (units[i]->proj_short_name) { + (void)printf("%12s %-20.15g %s\n", + units[i]->proj_short_name, + units[i]->conv_factor, + units[i]->name); + } + } + proj_unit_list_destroy(units); + } else + emess(1, "invalid list option: l%c", arg[1]); + exit(0); + case 'p': /* output azimuths as positive */ + pos_azi = 1; + continue; + default: + emess(1, "invalid option: -%c", *arg); + break; + } + break; + } + else if (**argv == '+') /* + argument */ + if (pargc < MAX_PARGS) + pargv[pargc++] = *argv + 1; + else + emess(1, "overflowed + argument table"); + else /* assumed to be input file name(s) */ + eargv[eargc++] = *argv; + } + /* done with parameter and control input */ + geod_set(pargc, pargv); /* setup projection */ + if ((n_alpha || n_S) && eargc) + emess(1, "files specified for arc/geodesic mode"); + if (n_alpha) + do_arc(); + else if (n_S) + do_geod(); + else { /* process input file list */ + if (eargc == 0) /* if no specific files force sysin */ + eargv[eargc++] = const_cast("-"); + for (; eargc--; ++eargv) { + if (**eargv == '-') { + fid = stdin; + emess_dat.File_name = const_cast(""); + } else { + if ((fid = fopen(*eargv, "r")) == nullptr) { + emess(-2, *eargv, "input file"); + continue; + } + emess_dat.File_name = *eargv; + } + emess_dat.File_line = 0; + process(fid); + (void)fclose(fid); + emess_dat.File_name = (char *)nullptr; + } + } + exit(0); /* normal completion */ } diff --git a/deps/libproj/proj/src/apps/geod_interface.cpp b/deps/libproj/proj/src/apps/geod_interface.cpp index a21a89e6e..f4fccd390 100644 --- a/deps/libproj/proj/src/apps/geod_interface.cpp +++ b/deps/libproj/proj/src/apps/geod_interface.cpp @@ -1,34 +1,33 @@ +#include "geod_interface.h" #include "proj.h" #include "proj_internal.h" -#include "geod_interface.h" -void geod_ini(void) { - geod_init(&GlobalGeodesic, geod_a, geod_f); -} +void geod_ini(void) { geod_init(&GlobalGeodesic, geod_a, geod_f); } void geod_pre(void) { - double - lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, - azi1 = al12 / DEG_TO_RAD; - geod_lineinit(&GlobalGeodesicLine, &GlobalGeodesic, lat1, lon1, azi1, 0U); + double lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, + azi1 = al12 / DEG_TO_RAD; + geod_lineinit(&GlobalGeodesicLine, &GlobalGeodesic, lat1, lon1, azi1, 0U); } void geod_for(void) { - double - s12 = geod_S, lat2, lon2, azi2; - geod_position(&GlobalGeodesicLine, s12, &lat2, &lon2, &azi2); - azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ - phi2 = lat2 * DEG_TO_RAD; - lam2 = lon2 * DEG_TO_RAD; - al21 = azi2 * DEG_TO_RAD; + double s12 = geod_S, lat2, lon2, azi2; + geod_position(&GlobalGeodesicLine, s12, &lat2, &lon2, &azi2); + azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ + phi2 = lat2 * DEG_TO_RAD; + lam2 = lon2 * DEG_TO_RAD; + al21 = azi2 * DEG_TO_RAD; } void geod_inv(void) { - double - lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, - lat2 = phi2 / DEG_TO_RAD, lon2 = lam2 / DEG_TO_RAD, - azi1, azi2, s12; - geod_inverse(&GlobalGeodesic, lat1, lon1, lat2, lon2, &s12, &azi1, &azi2); - azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ - al12 = azi1 * DEG_TO_RAD; al21 = azi2 * DEG_TO_RAD; geod_S = s12; + double lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, + lat2 = phi2 / DEG_TO_RAD, lon2 = lam2 / DEG_TO_RAD, azi1, azi2, s12; + geod_inverse(&GlobalGeodesic, lat1, lon1, lat2, lon2, &s12, &azi1, &azi2); + /* Compute back azimuth + * map +/-0 -> -/+180; +/-180 -> -/+0 + * this depends on abs(azi2) <= 180 */ + azi2 = copysign(azi2 + copysign(180.0, -azi2), -azi2); + al12 = azi1 * DEG_TO_RAD; + al21 = azi2 * DEG_TO_RAD; + geod_S = s12; } diff --git a/deps/libproj/proj/src/apps/geod_interface.h b/deps/libproj/proj/src/apps/geod_interface.h index 255d505ae..30aadc693 100644 --- a/deps/libproj/proj/src/apps/geod_interface.h +++ b/deps/libproj/proj/src/apps/geod_interface.h @@ -7,31 +7,31 @@ extern "C" { #endif -#ifndef _IN_GEOD_SET -# define GEOD_EXTERN extern +#ifndef GEOD_IN_GEOD_SET +#define GEOD_EXTERN extern #else -# define GEOD_EXTERN +#define GEOD_EXTERN #endif GEOD_EXTERN struct geodesic { - double A, FLAT, LAM1, PHI1, ALPHA12, LAM2, PHI2, ALPHA21, DIST; + double A, FLAT, LAM1, PHI1, ALPHA12, LAM2, PHI2, ALPHA21, DIST; } GEODESIC; -# define geod_a GEODESIC.A -# define geod_f GEODESIC.FLAT -# define lam1 GEODESIC.LAM1 -# define phi1 GEODESIC.PHI1 -# define al12 GEODESIC.ALPHA12 -# define lam2 GEODESIC.LAM2 -# define phi2 GEODESIC.PHI2 -# define al21 GEODESIC.ALPHA21 -# define geod_S GEODESIC.DIST - +#define geod_a GEODESIC.A +#define geod_f GEODESIC.FLAT +#define lam1 GEODESIC.LAM1 +#define phi1 GEODESIC.PHI1 +#define al12 GEODESIC.ALPHA12 +#define lam2 GEODESIC.LAM2 +#define phi2 GEODESIC.PHI2 +#define al21 GEODESIC.ALPHA21 +#define geod_S GEODESIC.DIST + GEOD_EXTERN struct geod_geodesic GlobalGeodesic; GEOD_EXTERN struct geod_geodesicline GlobalGeodesicLine; GEOD_EXTERN int n_alpha, n_S; GEOD_EXTERN double to_meter, fr_meter, del_alpha; - + void geod_set(int, char **); void geod_ini(void); void geod_pre(void); diff --git a/deps/libproj/proj/src/apps/geod_set.cpp b/deps/libproj/proj/src/apps/geod_set.cpp index d6516f22b..5382e0107 100644 --- a/deps/libproj/proj/src/apps/geod_set.cpp +++ b/deps/libproj/proj/src/apps/geod_set.cpp @@ -1,80 +1,81 @@ -#define _IN_GEOD_SET +#define GEOD_IN_GEOD_SET #include #include #include +#include "emess.h" +#include "geod_interface.h" #include "proj.h" #include "proj_internal.h" -#include "geod_interface.h" -#include "emess.h" - void -geod_set(int argc, char **argv) { - paralist *start = nullptr, *curr; - double es; - char *name; +void geod_set(int argc, char **argv) { + paralist *start = nullptr, *curr; + double es; + char *name; /* put arguments into internal linked list */ - if (argc <= 0) - emess(1, "no arguments in initialization list"); - start = curr = pj_mkparam(argv[0]); - if (!curr) - emess(1, "memory allocation failed"); - for (int i = 1; curr != nullptr && i < argc; ++i) { - curr->next = pj_mkparam(argv[i]); - if (!curr->next) - emess(1, "memory allocation failed"); - curr = curr->next; - } - /* set elliptical parameters */ - if (pj_ell_set(pj_get_default_ctx(),start, &geod_a, &es)) emess(1,"ellipse setup failure"); - /* set units */ - if ((name = pj_param(nullptr,start, "sunits").s) != nullptr) { - bool unit_found = false; - auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr); - for( int i = 0; units && units[i]; i++ ) - { - if( units[i]->proj_short_name && - strcmp(units[i]->proj_short_name, name) == 0 ) { - unit_found = true; - to_meter = units[i]->conv_factor; - fr_meter = 1 / to_meter; - } + if (argc <= 0) + emess(1, "no arguments in initialization list"); + start = curr = pj_mkparam(argv[0]); + if (!curr) + emess(1, "memory allocation failed"); + for (int i = 1; curr != nullptr && i < argc; ++i) { + curr->next = pj_mkparam(argv[i]); + if (!curr->next) + emess(1, "memory allocation failed"); + curr = curr->next; + } + /* set elliptical parameters */ + if (pj_ell_set(pj_get_default_ctx(), start, &geod_a, &es)) + emess(1, "ellipse setup failure"); + /* set units */ + if ((name = pj_param(nullptr, start, "sunits").s) != nullptr) { + bool unit_found = false; + auto units = proj_get_units_from_database(nullptr, nullptr, "linear", + false, nullptr); + for (int i = 0; units && units[i]; i++) { + if (units[i]->proj_short_name && + strcmp(units[i]->proj_short_name, name) == 0) { + unit_found = true; + to_meter = units[i]->conv_factor; + fr_meter = 1 / to_meter; } - proj_unit_list_destroy(units); - if( !unit_found ) - emess(1,"%s unknown unit conversion id", name); - } else - to_meter = fr_meter = 1; - geod_f = es/(1 + sqrt(1 - es)); - geod_ini(); - /* check if line or arc mode */ - if (pj_param(nullptr,start, "tlat_1").i) { - double del_S; - phi1 = pj_param(nullptr,start, "rlat_1").f; - lam1 = pj_param(nullptr,start, "rlon_1").f; - if (pj_param(nullptr,start, "tlat_2").i) { - phi2 = pj_param(nullptr,start, "rlat_2").f; - lam2 = pj_param(nullptr,start, "rlon_2").f; - geod_inv(); - geod_pre(); - } else if ((geod_S = pj_param(nullptr,start, "dS").f) != 0.) { - al12 = pj_param(nullptr,start, "rA").f; - geod_pre(); - geod_for(); - } else emess(1,"incomplete geodesic/arc info"); - if ((n_alpha = pj_param(nullptr,start, "in_A").i) > 0) { - if ((del_alpha = pj_param(nullptr,start, "rdel_A").f) == 0.0) - emess(1,"del azimuth == 0"); - } else if ((del_S = fabs(pj_param(nullptr,start, "ddel_S").f)) != 0.) { - n_S = (int)(geod_S / del_S + .5); - } else if ((n_S = pj_param(nullptr,start, "in_S").i) <= 0) - emess(1,"no interval divisor selected"); - } - /* free up linked list */ - for ( ; start; start = curr) { - curr = start->next; - free(start); - } + } + proj_unit_list_destroy(units); + if (!unit_found) + emess(1, "%s unknown unit conversion id", name); + } else + to_meter = fr_meter = 1; + geod_f = es / (1 + sqrt(1 - es)); + geod_ini(); + /* check if line or arc mode */ + if (pj_param(nullptr, start, "tlat_1").i) { + double del_S; + phi1 = pj_param(nullptr, start, "rlat_1").f; + lam1 = pj_param(nullptr, start, "rlon_1").f; + if (pj_param(nullptr, start, "tlat_2").i) { + phi2 = pj_param(nullptr, start, "rlat_2").f; + lam2 = pj_param(nullptr, start, "rlon_2").f; + geod_inv(); + geod_pre(); + } else if ((geod_S = pj_param(nullptr, start, "dS").f) != 0.) { + al12 = pj_param(nullptr, start, "rA").f; + geod_pre(); + geod_for(); + } else + emess(1, "incomplete geodesic/arc info"); + if ((n_alpha = pj_param(nullptr, start, "in_A").i) > 0) { + if ((del_alpha = pj_param(nullptr, start, "rdel_A").f) == 0.0) + emess(1, "del azimuth == 0"); + } else if ((del_S = fabs(pj_param(nullptr, start, "ddel_S").f)) != 0.) { + n_S = (int)(geod_S / del_S + .5); + } else if ((n_S = pj_param(nullptr, start, "in_S").i) <= 0) + emess(1, "no interval divisor selected"); + } + /* free up linked list */ + for (; start; start = curr) { + curr = start->next; + free(start); + } } diff --git a/deps/libproj/proj/src/apps/gie.cpp b/deps/libproj/proj/src/apps/gie.cpp index 0fb87538e..42b70f179 100644 --- a/deps/libproj/proj/src/apps/gie.cpp +++ b/deps/libproj/proj/src/apps/gie.cpp @@ -115,15 +115,16 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08 #include "proj.h" #include "proj_internal.h" -#include #include "proj_strtod.h" +#include /* for isnan */ +#include #include "optargpm.h" /* Package for flexible format I/O - ffio */ typedef struct ffio { FILE *f; - const char * const *tags; + const char *const *tags; const char *tag; char *args; char *next_args; @@ -134,56 +135,69 @@ typedef struct ffio { size_t lineno, next_lineno; size_t level; bool strict_mode; -} ffio; - -static int get_inp (ffio *G); -static int skip_to_next_tag (ffio *G); -static int step_into_gie_block (ffio *G); -static int nextline (ffio *G); -static int at_end_delimiter (ffio *G); -static const char *at_tag (ffio *G); -static int at_decorative_element (ffio *G); -static ffio *ffio_destroy (ffio *G); -static ffio *ffio_create (const char * const *tags, size_t n_tags, size_t max_record_size); - -static const char * const gie_tags[] = { - "", "operation", "crs_src", "crs_dst", "use_proj4_init_rules", - "accept", "expect", "roundtrip", "banner", "verbose", - "direction", "tolerance", "ignore", "require_grid", "echo", "skip", "", - "", "", +} ffio; + +static int get_inp(ffio *G); +static int skip_to_next_tag(ffio *G); +static int step_into_gie_block(ffio *G); +static int nextline(ffio *G); +static int at_end_delimiter(ffio *G); +static const char *at_tag(ffio *G); +static int at_decorative_element(ffio *G); +static ffio *ffio_destroy(ffio *G); +static ffio *ffio_create(const char *const *tags, size_t n_tags, + size_t max_record_size); + +static const char *const gie_tags[] = { + "", + "operation", + "crs_src", + "crs_dst", + "use_proj4_init_rules", + "accept", + "expect", + "roundtrip", + "banner", + "verbose", + "direction", + "tolerance", + "ignore", + "require_grid", + "echo", + "skip", + "", + "", + "", }; static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; +int main(int argc, char **argv); -int main(int argc, char **argv); - -static int dispatch (const char *cmnd, const char *args); -static int errmsg (int errlev, const char *msg, ...); -static int errno_from_err_const (const char *err_const); -static int list_err_codes (void); -static int process_file (const char *fname); - -static const char *column (const char *buf, int n); -static const char *err_const_from_errno (int err); - +static int dispatch(const char *cmnd, const char *args); +static int errmsg(int errlev, const char *msg, ...); +static int errno_from_err_const(const char *err_const); +static int list_err_codes(void); +static int process_file(const char *fname); +static const char *column(const char *buf, int n); +static const char *err_const_from_errno(int err); #define SKIP -1 #define MAX_OPERATION 10000 typedef struct { - char operation[MAX_OPERATION+1]; - char crs_dst[MAX_OPERATION+1]; - char crs_src[MAX_OPERATION+1]; + char operation[MAX_OPERATION + 1]; + char crs_dst[MAX_OPERATION + 1]; + char crs_src[MAX_OPERATION + 1]; PJ *P; - PJ_COORD a, b, c, e; + PJ_COORD a, b, e; PJ_DIRECTION dir; int verbosity; int skip; int op_id; - int op_ok, op_ko, op_skip; + int op_ok, op_ko, op_skip; int total_ok, total_ko, total_skip; int grand_ok, grand_ko, grand_skip; size_t operation_lineno; @@ -199,50 +213,65 @@ typedef struct { ffio *F = nullptr; static gie_ctx T; -int tests=0, succs=0, succ_fails=0, fail_fails=0, succ_rtps=0, fail_rtps=0; - -static const char delim[] = {"-------------------------------------------------------------------------------\n"}; +int tests = 0, succs = 0, succ_fails = 0, fail_fails = 0, succ_rtps = 0, + fail_rtps = 0; +static const char delim[] = {"-------------------------------------------------" + "------------------------------\n"}; static const char usage[] = { - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Usage: %s [-options]... infile...\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Options:\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" " -h Help: print this usage information\n" " -o /path/to/file Specify output file name\n" - " -v Verbose: Provide non-essential informational output.\n" + " -v Verbose: Provide non-essential informational " + "output.\n" " Repeat -v for more verbosity (e.g. -vv)\n" - " -q Quiet: Opposite of verbose. In quiet mode not even errors\n" - " are reported. Only interaction is through the return code\n" - " (0 on success, non-zero indicates number of FAILED tests)\n" + " -q Quiet: Opposite of verbose. In quiet mode not even " + "errors\n" + " are reported. Only interaction is through the " + "return code\n" + " (0 on success, non-zero indicates number of FAILED " + "tests)\n" " -l List the PROJ internal system error codes\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Long Options:\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" " --output Alias for -o\n" " --verbose Alias for -v\n" " --help Alias for -h\n" " --list Alias for -l\n" " --version Print version number\n" - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------" + "------\n" "Examples:\n" - "--------------------------------------------------------------------------------\n" - "1. Run all tests in file \"corner-cases.gie\", providing much extra information\n" + "--------------------------------------------------------------------------" + "------\n" + "1. Run all tests in file \"corner-cases.gie\", providing much extra " + "information\n" " gie -vvvv corner-cases.gie\n" - "2. Run all tests in files \"foo\" and \"bar\", providing info on failures only\n" + "2. Run all tests in files \"foo\" and \"bar\", providing info on failures " + "only\n" " gie foo bar\n" - "--------------------------------------------------------------------------------\n" -}; + "--------------------------------------------------------------------------" + "------\n"}; -int main (int argc, char **argv) { - int i; - const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", "version", nullptr}; - const char *longkeys[] = {"o=output", nullptr}; +int main(int argc, char **argv) { + int i; + const char *longflags[] = {"v=verbose", "q=quiet", "h=help", + "l=list", "version", nullptr}; + const char *longkeys[] = {"o=output", nullptr}; OPTARGS *o; - memset (&T, 0, sizeof (T)); + memset(&T, 0, sizeof(T)); T.dir = PJ_FWD; T.verbosity = 1; T.tolerance = 5e-4; @@ -250,202 +279,207 @@ int main (int argc, char **argv) { T.use_proj4_init_rules = FALSE; /* coverity[tainted_data] */ - o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); - if (nullptr==o) + o = opt_parse(argc, argv, "hlvq", "o", longflags, longkeys); + if (nullptr == o) return 0; - if (opt_given (o, "h") || argc==1) { - printf (usage, o->progname); - free (o); + if (opt_given(o, "h") || argc == 1) { + printf(usage, o->progname); + free(o); return 0; } - - if (opt_given (o, "version")) { - fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ()); - free (o); + if (opt_given(o, "version")) { + fprintf(stdout, "%s: %s\n", o->progname, pj_get_release()); + free(o); return 0; } - T.verbosity = opt_given (o, "q"); + T.verbosity = opt_given(o, "q"); if (T.verbosity) T.verbosity = -1; if (T.verbosity != -1) - T.verbosity = opt_given (o, "v") + 1; + T.verbosity = opt_given(o, "v") + 1; T.fout = stdout; - if (opt_given (o, "o")) - T.fout = fopen (opt_arg (o, "output"), "rt"); + if (opt_given(o, "o")) + T.fout = fopen(opt_arg(o, "output"), "rt"); - if (nullptr==T.fout) { - fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); - free (o); + if (nullptr == T.fout) { + fprintf(stderr, "%s: Cannot open '%s' for output\n", o->progname, + opt_arg(o, "output")); + free(o); return 1; } - if (opt_given (o, "l")) { - free (o); - return list_err_codes (); + if (opt_given(o, "l")) { + free(o); + return list_err_codes(); } - if (0==o->fargc) { - if (T.verbosity==-1) + if (0 == o->fargc) { + if (T.verbosity == -1) return -1; - fprintf (T.fout, "Nothing to do\n"); - free (o); + fprintf(T.fout, "Nothing to do\n"); + free(o); return 0; } - F = ffio_create (gie_tags, n_gie_tags, 1000); - if (nullptr==F) { - fprintf (stderr, "%s: No memory\n", o->progname); - free (o); + F = ffio_create(gie_tags, n_gie_tags, 1000); + if (nullptr == F) { + fprintf(stderr, "%s: No memory\n", o->progname); + free(o); return 1; } - for (i = 0; i < o->fargc; i++ ) { - FILE* f = fopen (o->fargv[i], "rt"); + for (i = 0; i < o->fargc; i++) { + FILE *f = fopen(o->fargv[i], "rt"); if (f == nullptr) { - fprintf ( - T.fout, - "%sCannot open specified input file '%s' - bye!\n", - delim, - o->fargv[i] - ); + fprintf(T.fout, "%sCannot open specified input file '%s' - bye!\n", + delim, o->fargv[i]); return 1; } fclose(f); } - for (i = 0; i < o->fargc; i++) - process_file (o->fargv[i]); + for (i = 0; i < o->fargc; i++) + process_file(o->fargv[i]); if (T.verbosity > 0) { if (o->fargc > 1) { - fprintf (T.fout, "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n", - delim, T.grand_ok+T.grand_ko+T.grand_skip, T.grand_ok, T.grand_skip, - T.grand_ko); + fprintf( + T.fout, + "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n", + delim, T.grand_ok + T.grand_ko + T.grand_skip, T.grand_ok, + T.grand_skip, T.grand_ko); } - fprintf (T.fout, "%s", delim); + fprintf(T.fout, "%s", delim); if (T.verbosity > 1) { - fprintf (T.fout, "Failing roundtrips: %4d, Succeeding roundtrips: %4d\n", fail_rtps, succ_rtps); - fprintf (T.fout, "Failing failures: %4d, Succeeding failures: %4d\n", fail_fails, succ_fails); - fprintf (T.fout, "Internal counters: %4.4d(%4.4d)\n", tests, succs); - fprintf (T.fout, "%s", delim); + fprintf(T.fout, + "Failing roundtrips: %4d, Succeeding roundtrips: %4d\n", + fail_rtps, succ_rtps); + fprintf(T.fout, + "Failing failures: %4d, Succeeding failures: %4d\n", + fail_fails, succ_fails); + fprintf( + T.fout, + "Internal counters: %4.4d(%4.4d)\n", + tests, succs); + fprintf(T.fout, "%s", delim); } - } - else - if (T.grand_ko) - fprintf (T.fout, "Failures: %d", T.grand_ko); + } else if (T.grand_ko) + fprintf(T.fout, "Failures: %d", T.grand_ko); if (stdout != T.fout) - fclose (T.fout); + fclose(T.fout); - free (o); - ffio_destroy (F); + free(o); + ffio_destroy(F); return T.grand_ko; } -static int another_failure (void) { +static int another_failure(void) { T.op_ko++; T.total_ko++; - proj_errno_reset (T.P); + proj_errno_reset(T.P); return 0; } -static int another_skip (void) { +static int another_skip(void) { T.op_skip++; T.total_skip++; return 0; } -static int another_success (void) { +static int another_success(void) { T.op_ok++; T.total_ok++; - proj_errno_reset (T.P); + proj_errno_reset(T.P); return 0; } -static int another_succeeding_failure (void) { +static int another_succeeding_failure(void) { succ_fails++; - return another_success (); + return another_success(); } -static int another_failing_failure (void) { +static int another_failing_failure(void) { fail_fails++; - return another_failure (); + return another_failure(); } -static int another_succeeding_roundtrip (void) { +static int another_succeeding_roundtrip(void) { succ_rtps++; - return another_success (); + return another_success(); } -static int another_failing_roundtrip (void) { +static int another_failing_roundtrip(void) { fail_rtps++; - return another_failure (); + return another_failure(); } -static int process_file (const char *fname) { +static int process_file(const char *fname) { F->lineno = F->next_lineno = F->level = 0; T.op_ok = T.total_ok = 0; T.op_ko = T.total_ko = 0; T.op_skip = T.total_skip = 0; if (T.skip) { - proj_destroy (T.P); + proj_destroy(T.P); T.P = nullptr; return 0; } /* We have already tested in main that the file exists */ - F->f = fopen (fname, "rt"); + F->f = fopen(fname, "rt"); if (T.verbosity > 0) - fprintf (T.fout, "%sReading file '%s'\n", delim, fname); + fprintf(T.fout, "%sReading file '%s'\n", delim, fname); T.curr_file = fname; while (get_inp(F)) { - if (SKIP==dispatch (F->tag, F->args)) { - proj_destroy (T.P); + if (SKIP == dispatch(F->tag, F->args)) { + proj_destroy(T.P); T.P = nullptr; return 0; } } - fclose (F->f); + fclose(F->f); F->lineno = F->next_lineno = 0; - T.grand_ok += T.total_ok; - T.grand_ko += T.total_ko; + T.grand_ok += T.total_ok; + T.grand_ko += T.total_ko; T.grand_skip += T.grand_skip; if (T.verbosity > 0) { - fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n", - delim, T.total_ok, T.total_skip, T.total_ko, - T.total_ko? "FAILED!": "failed."); + fprintf( + T.fout, + "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n", + delim, T.total_ok, T.total_skip, T.total_ko, + T.total_ko ? "FAILED!" : "failed."); } - if (F->level==0) - return errmsg (-3, "File '%s':Missing '' cmnd - bye!\n", fname); - if (F->level && F->level%2) - { - if( F->strict_mode ) - return errmsg (-4, "File '%s':Missing '' cmnd - bye!\n", fname); + if (F->level == 0) + return errmsg(-3, "File '%s':Missing '' cmnd - bye!\n", fname); + if (F->level && F->level % 2) { + if (F->strict_mode) + return errmsg(-4, "File '%s':Missing '' cmnd - bye!\n", + fname); else - return errmsg (-4, "File '%s':Missing '' cmnd - bye!\n", fname); + return errmsg(-4, "File '%s':Missing '' cmnd - bye!\n", + fname); } return 0; } - /*****************************************************************************/ -const char *column (const char *buf, int n) { -/***************************************************************************** -Return a pointer to the n'th column of buf. Column numbers start at 0. -******************************************************************************/ +const char *column(const char *buf, int n) { + /***************************************************************************** + Return a pointer to the n'th column of buf. Column numbers start at 0. + ******************************************************************************/ int i; if (n <= 0) return buf; - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) { while (isspace(*buf)) buf++; if (i == n - 1) @@ -456,82 +490,78 @@ Return a pointer to the n'th column of buf. Column numbers start at 0. return buf; } - /*****************************************************************************/ -static double strtod_scaled (const char *args, double default_scale) { -/***************************************************************************** -Interpret as a numeric followed by a linear decadal prefix. -Return the properly scaled numeric -******************************************************************************/ - double s; +static double strtod_scaled(const char *args, double default_scale) { + /***************************************************************************** + Interpret as a numeric followed by a linear decadal prefix. + Return the properly scaled numeric + ******************************************************************************/ const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */ - const char *endp = args; - s = proj_strtod (args, (char **) &endp); - if (args==endp) + + char *endp; + double s = proj_strtod(args, &endp); + if (args == endp) return HUGE_VAL; - endp = column (args, 2); + const char *units = column(args, 2); - if (0==strcmp(endp, "km")) + if (0 == strcmp(units, "km")) s *= 1000; - else if (0==strcmp(endp, "m")) + else if (0 == strcmp(units, "m")) s *= 1; - else if (0==strcmp(endp, "dm")) + else if (0 == strcmp(units, "dm")) s /= 10; - else if (0==strcmp(endp, "cm")) + else if (0 == strcmp(units, "cm")) s /= 100; - else if (0==strcmp(endp, "mm")) + else if (0 == strcmp(units, "mm")) s /= 1000; - else if (0==strcmp(endp, "um")) + else if (0 == strcmp(units, "um")) s /= 1e6; - else if (0==strcmp(endp, "nm")) + else if (0 == strcmp(units, "nm")) s /= 1e9; - else if (0==strcmp(endp, "rad")) - s = GRS80_DEG * proj_todeg (s); - else if (0==strcmp(endp, "deg")) + else if (0 == strcmp(units, "rad")) + s = GRS80_DEG * proj_todeg(s); + else if (0 == strcmp(units, "deg")) s = GRS80_DEG * s; else s *= default_scale; return s; } - -static int banner (const char *args) { +static int banner(const char *args) { char dots[] = {"..."}, nodots[] = {""}, *thedots = nodots; if (strlen(args) > 70) thedots = dots; - fprintf (T.fout, "%s%-70.70s%s\n", delim, args, thedots); + fprintf(T.fout, "%s%-70.70s%s\n", delim, args, thedots); return 0; } - -static int tolerance (const char *args) { - T.tolerance = strtod_scaled (args, 1); - if (HUGE_VAL==T.tolerance) { +static int tolerance(const char *args) { + T.tolerance = strtod_scaled(args, 1); + if (HUGE_VAL == T.tolerance) { T.tolerance = 0.0005; return 1; } return 0; } - -static int use_proj4_init_rules (const char *args) { +static int use_proj4_init_rules(const char *args) { T.use_proj4_init_rules = strcmp(args, "true") == 0; return 0; } -static int ignore (const char *args) { - T.ignore = errno_from_err_const (column (args, 1)); +static int ignore(const char *args) { + T.ignore = errno_from_err_const(column(args, 1)); return 0; } -static int require_grid (const char *args) { +static int require_grid(const char *args) { PJ_GRID_INFO grid_info; - const char* grid_filename = column (args, 1); + const char *grid_filename = column(args, 1); grid_info = proj_grid_info(grid_filename); - if( strlen(grid_info.filename) == 0 ) { + if (strlen(grid_info.filename) == 0) { if (T.verbosity > 1) { - fprintf (T.fout, "Test skipped because of missing grid %s\n", + fprintf(T.fout, "Test skipped because of missing grid %s\n", grid_filename); } T.skip_test = 1; @@ -539,75 +569,73 @@ static int require_grid (const char *args) { return 0; } -static int direction (const char *args) { +static int direction(const char *args) { const char *endp = args; - while (isspace (*endp)) + while (isspace(*endp)) endp++; switch (*endp) { - case 'F': - case 'f': + case 'F': + case 'f': T.dir = PJ_FWD; - break; - case 'I': - case 'i': - case 'R': - case 'r': - T.dir = PJ_INV; - break; - default: - return 1; + break; + case 'I': + case 'i': + case 'R': + case 'r': + T.dir = PJ_INV; + break; + default: + return 1; } return 0; } - -static void finish_previous_operation (const char *args) { - if (T.verbosity > 1 && T.op_id > 1 && T.op_ok+T.op_ko) - fprintf (T.fout, "%s %d tests succeeded, %d tests skipped, %d tests %s\n", - delim, T.op_ok, T.op_skip, T.op_ko, T.op_ko? "FAILED!": "failed."); - (void) args; +static void finish_previous_operation(const char *args) { + if (T.verbosity > 1 && T.op_id > 1 && T.op_ok + T.op_ko) + fprintf(T.fout, + "%s %d tests succeeded, %d tests skipped, %d tests %s\n", + delim, T.op_ok, T.op_skip, T.op_ko, + T.op_ko ? "FAILED!" : "failed."); + (void)args; } - - /*****************************************************************************/ -static int operation (char *args) { -/***************************************************************************** -Define the operation to apply to the input data (in ISO 19100 lingo, -an operation is the general term describing something that can be -either a conversion or a transformation) -******************************************************************************/ +static int operation(const char *args) { + /***************************************************************************** + Define the operation to apply to the input data (in ISO 19100 lingo, + an operation is the general term describing something that can be + either a conversion or a transformation) + ******************************************************************************/ T.op_id++; T.operation_lineno = F->lineno; - strncpy (&(T.operation[0]), F->args, MAX_OPERATION); + strncpy(&(T.operation[0]), F->args, MAX_OPERATION); T.operation[MAX_OPERATION] = '\0'; if (T.verbosity > 1) { - finish_previous_operation (F->args); - banner (args); + finish_previous_operation(F->args); + banner(args); } - T.op_ok = 0; T.op_ko = 0; T.op_skip = 0; T.skip_test = 0; - direction ("forward"); - tolerance ("0.5 mm"); - ignore ("pjd_err_dont_skip"); + direction("forward"); + tolerance("0.5 mm"); + ignore("pjd_err_dont_skip"); - proj_errno_reset (T.P); + proj_errno_reset(T.P); if (T.P) - proj_destroy (T.P); - proj_errno_reset (nullptr); + proj_destroy(T.P); + proj_errno_reset(nullptr); proj_context_use_proj4_init_rules(nullptr, T.use_proj4_init_rules); - T.P = proj_create (nullptr, F->args); + T.P = proj_create(nullptr, F->args); /* Checking that proj_create succeeds is first done at "expect" time, */ /* since we want to support "expect"ing specific error codes */ @@ -621,9 +649,9 @@ static int crs_to_crs_operation() { if (T.verbosity > 1) { char buffer[80]; - finish_previous_operation (F->args); + finish_previous_operation(F->args); snprintf(buffer, 80, "%-36.36s -> %-36.36s", T.crs_src, T.crs_dst); - banner (buffer); + banner(buffer); } T.op_ok = 0; @@ -631,18 +659,17 @@ static int crs_to_crs_operation() { T.op_skip = 0; T.skip_test = 0; - direction ("forward"); - tolerance ("0.5 mm"); - ignore ("pjd_err_dont_skip"); + direction("forward"); + tolerance("0.5 mm"); + ignore("pjd_err_dont_skip"); - proj_errno_reset (T.P); + proj_errno_reset(T.P); if (T.P) - proj_destroy (T.P); - proj_errno_reset (nullptr); + proj_destroy(T.P); + proj_errno_reset(nullptr); proj_context_use_proj4_init_rules(nullptr, T.use_proj4_init_rules); - T.P = proj_create_crs_to_crs(nullptr, T.crs_src, T.crs_dst, nullptr); strcpy(T.crs_src, ""); @@ -651,9 +678,9 @@ static int crs_to_crs_operation() { } static int crs_src(const char *args) { - strncpy (&(T.crs_src[0]), F->args, MAX_OPERATION); + strncpy(&(T.crs_src[0]), F->args, MAX_OPERATION); T.crs_src[MAX_OPERATION] = '\0'; - (void) args; + (void)args; if (strcmp(T.crs_src, "") != 0 && strcmp(T.crs_dst, "") != 0) { crs_to_crs_operation(); @@ -663,9 +690,9 @@ static int crs_src(const char *args) { } static int crs_dst(const char *args) { - strncpy (&(T.crs_dst[0]), F->args, MAX_OPERATION); + strncpy(&(T.crs_dst[0]), F->args, MAX_OPERATION); T.crs_dst[MAX_OPERATION] = '\0'; - (void) args; + (void)args; if (strcmp(T.crs_src, "") != 0 && strcmp(T.crs_dst, "") != 0) { crs_to_crs_operation(); @@ -674,48 +701,45 @@ static int crs_dst(const char *args) { return 0; } -static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { +static PJ_COORD torad_coord(PJ *P, PJ_DIRECTION dir, PJ_COORD a) { size_t i, n; const char *axis = "enut"; - paralist *l = pj_param_exists (P->params, "axis"); - if (l && dir==PJ_INV) - axis = l->param + strlen ("axis="); - n = strlen (axis); - for (i = 0; i < n; i++) - if (strchr ("news", axis[i])) - a.v[i] = proj_torad (a.v[i]); + paralist *l = pj_param_exists(P->params, "axis"); + if (l && dir == PJ_INV) + axis = l->param + strlen("axis="); + n = strlen(axis); + for (i = 0; i < n; i++) + if (strchr("news", axis[i])) + a.v[i] = proj_torad(a.v[i]); return a; } - -static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { +static PJ_COORD todeg_coord(PJ *P, PJ_DIRECTION dir, PJ_COORD a) { size_t i, n; const char *axis = "enut"; - paralist *l = pj_param_exists (P->params, "axis"); - if (l && dir==PJ_FWD) - axis = l->param + strlen ("axis="); - n = strlen (axis); - for (i = 0; i < n; i++) - if (strchr ("news", axis[i])) - a.v[i] = proj_todeg (a.v[i]); + paralist *l = pj_param_exists(P->params, "axis"); + if (l && dir == PJ_FWD) + axis = l->param + strlen("axis="); + n = strlen(axis); + for (i = 0; i < n; i++) + if (strchr("news", axis[i])) + a.v[i] = proj_todeg(a.v[i]); return a; } - - /*****************************************************************************/ -static PJ_COORD parse_coord (const char *args) { -/***************************************************************************** -Attempt to interpret args as a PJ_COORD. -******************************************************************************/ +static PJ_COORD parse_coord(const char *args) { + /***************************************************************************** + Attempt to interpret args as a PJ_COORD. + ******************************************************************************/ int i; - const char *endp; - const char *dmsendp; + char *endp; + char *dmsendp; const char *prev = args; - PJ_COORD a = proj_coord (0,0,0,0); + PJ_COORD a = proj_coord(0, 0, 0, 0); T.dimensions_given = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) { /* proj_strtod doesn't read values like 123d45'678W so we need a bit */ /* of help from proj_dmstor. proj_strtod effectively ignores what */ /* comes after "d", so we use that fact that when dms is larger than */ @@ -729,11 +753,11 @@ Attempt to interpret args as a PJ_COORD. // big projected coordinates cause inaccuracies, that can cause // test failures when testing points at edge of grids. // For example 1501000.0 becomes 1501000.000000000233 - double d = proj_strtod(prev, (char **) &endp); - if( *endp != '\0' && !isspace(*endp) ) - { - double dms = PJ_TODEG(proj_dmstor (prev, (char **) &dmsendp)); - /* TODO: When projects.h is removed, call proj_dmstor() in all cases */ + double d = proj_strtod(prev, &endp); + if (!std::isnan(d) && *endp != '\0' && !isspace(*endp)) { + double dms = PJ_TODEG(proj_dmstor(prev, &dmsendp)); + /* TODO: When projects.h is removed, call proj_dmstor() in all cases + */ if (d != dms && fabs(d) < fabs(dms) && fabs(dms) < fabs(d) + 1) { d = dms; endp = dmsendp; @@ -746,8 +770,8 @@ Attempt to interpret args as a PJ_COORD. } /* Break out if there were no more numerals */ - if (prev==endp) - return i > 1? a: proj_coord_error (); + if (prev == endp) + return i > 1 ? a : proj_coord_error(); a.v[i] = d; prev = endp; @@ -757,264 +781,270 @@ Attempt to interpret args as a PJ_COORD. return a; } - /*****************************************************************************/ -static int accept (const char *args) { -/***************************************************************************** -Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. -******************************************************************************/ - T.a = parse_coord (args); +static int accept(const char *args) { + /***************************************************************************** + Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. + ******************************************************************************/ + T.a = parse_coord(args); if (T.verbosity > 3) - fprintf (T.fout, "# %s\n", args); + fprintf(T.fout, "# %s\n", args); T.dimensions_given_at_last_accept = T.dimensions_given; return 0; } - /*****************************************************************************/ -static int roundtrip (const char *args) { -/***************************************************************************** -Check how far we go from the ACCEPTed point when doing successive -back/forward transformation pairs. +static int roundtrip(const char *args) { + /***************************************************************************** + Check how far we go from the ACCEPTed point when doing successive + back/forward transformation pairs. -Without args, roundtrip defaults to 100 iterations: + Without args, roundtrip defaults to 100 iterations: - roundtrip + roundtrip -With one arg, roundtrip will default to a tolerance of T.tolerance: + With one arg, roundtrip will default to a tolerance of T.tolerance: - roundtrip ntrips + roundtrip ntrips -With two args: + With two args: - roundtrip ntrips tolerance + roundtrip ntrips tolerance -Always returns 0. -******************************************************************************/ + Always returns 0. + ******************************************************************************/ int ntrips; double d, r, ans; char *endp; PJ_COORD coo; - if (nullptr==T.P) { + if (nullptr == T.P) { if (T.ignore == proj_errno(T.P)) return another_skip(); - return another_failure (); + return another_failure(); } - ans = proj_strtod (args, &endp); - if (endp==args) { + ans = proj_strtod(args, &endp); + if (endp == args) { /* Default to 100 iterations if not args. */ ntrips = 100; } else { if (ans < 1.0 || ans > 1000000.0) { - errmsg (2, "Invalid number of roundtrips: %lf\n", ans); - return another_failing_roundtrip (); + errmsg(2, "Invalid number of roundtrips: %lf\n", ans); + return another_failing_roundtrip(); } ntrips = (int)ans; } - d = strtod_scaled (endp, 1); - d = d==HUGE_VAL? T.tolerance: d; + d = strtod_scaled(endp, 1); + d = d == HUGE_VAL ? T.tolerance : d; /* input ("accepted") values - probably in degrees */ - coo = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; + coo = proj_angular_input(T.P, T.dir) ? torad_coord(T.P, T.dir, T.a) : T.a; - r = proj_roundtrip (T.P, T.dir, ntrips, &coo); - if (r <= d) - return another_succeeding_roundtrip (); + r = proj_roundtrip(T.P, T.dir, ntrips, &coo); + if ((std::isnan(r) && std::isnan(d)) || r <= d) + return another_succeeding_roundtrip(); if (T.verbosity > -1) { - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d); + if (0 == T.op_ko && T.verbosity < 2) + banner(T.operation); + fprintf(T.fout, "%s", T.op_ko ? " -----\n" : delim); + fprintf(T.fout, " FAILURE in %s(%d):\n", + opt_strip_path(T.curr_file), (int)F->lineno); + fprintf(T.fout, + " roundtrip deviation: %.6f mm, expected: %.6f mm\n", + 1000 * r, 1000 * d); } - return another_failing_roundtrip (); + return another_failing_roundtrip(); } - -static int expect_message (double d, const char *args) { - another_failure (); +static int expect_message(double d, const char *args) { + another_failure(); if (T.verbosity < 0) return 1; if (d > 1e6) d = 999999.999999; - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " expected: %s\n", args); - fprintf (T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y); - if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0) - fprintf (T.fout, " %.9f", T.b.xyz.z); - if (T.b.xyzt.t!=0) - fprintf (T.fout, " %.9f", T.b.xyzt.t); - fprintf (T.fout, "\n"); - fprintf (T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000*d, 1000*T.tolerance); + if (0 == T.op_ko && T.verbosity < 2) + banner(T.operation); + fprintf(T.fout, "%s", T.op_ko ? " -----\n" : delim); + + fprintf(T.fout, " FAILURE in %s(%d):\n", opt_strip_path(T.curr_file), + (int)F->lineno); + fprintf(T.fout, " expected: %s\n", args); + fprintf(T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y); + if (T.b.xyzt.t != 0 || T.b.xyzt.z != 0) + fprintf(T.fout, " %.9f", T.b.xyz.z); + if (T.b.xyzt.t != 0) + fprintf(T.fout, " %.9f", T.b.xyzt.t); + fprintf(T.fout, "\n"); + fprintf(T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000 * d, + 1000 * T.tolerance); return 1; } - -static int expect_message_cannot_parse (const char *args) { - another_failure (); +static int expect_message_cannot_parse(const char *args) { + another_failure(); if (T.verbosity > -1) { - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args); + if (0 == T.op_ko && T.verbosity < 2) + banner(T.operation); + fprintf(T.fout, "%s", T.op_ko ? " -----\n" : delim); + fprintf(T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", + opt_strip_path(T.curr_file), (int)F->lineno, args); } return 1; } -static int expect_failure_with_errno_message (int expected, int got) { - another_failing_failure (); +static int expect_failure_with_errno_message(int expected, int got) { + another_failing_failure(); if (T.verbosity < 0) return 1; - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, proj_errno_string (got)); - fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, proj_errno_string (expected)); - fprintf (T.fout, "\n"); + if (0 == T.op_ko && T.verbosity < 2) + banner(T.operation); + fprintf(T.fout, "%s", T.op_ko ? " -----\n" : delim); + fprintf(T.fout, " FAILURE in %s(%d):\n", opt_strip_path(T.curr_file), + (int)F->lineno); + fprintf(T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), + got, proj_errno_string(got)); + fprintf(T.fout, " expected %s (%d): %s", + err_const_from_errno(expected), expected, + proj_errno_string(expected)); + fprintf(T.fout, "\n"); return 1; } - /* For test purposes, we want to call a transformation of the same */ /* dimensionality as the number of dimensions given in accept */ -static PJ_COORD expect_trans_n_dim (const PJ_COORD& ci) { - if (4==T.dimensions_given_at_last_accept) - return proj_trans (T.P, T.dir, ci); +static PJ_COORD expect_trans_n_dim(const PJ_COORD &ci) { + if (4 == T.dimensions_given_at_last_accept) + return proj_trans(T.P, T.dir, ci); - if (3==T.dimensions_given_at_last_accept) - return pj_approx_3D_trans (T.P, T.dir, ci); + if (3 == T.dimensions_given_at_last_accept) + return pj_approx_3D_trans(T.P, T.dir, ci); - return pj_approx_2D_trans (T.P, T.dir, ci); + return pj_approx_2D_trans(T.P, T.dir, ci); } - /*****************************************************************************/ -static int expect (const char *args) { -/***************************************************************************** -Tell GIE what to expect, when transforming the ACCEPTed input -******************************************************************************/ +static int expect(const char *args) { + /***************************************************************************** + Tell GIE what to expect, when transforming the ACCEPTed input + ******************************************************************************/ PJ_COORD ci, co, ce; double d; int expect_failure = 0; int expect_failure_with_errno = 0; - if (0==strncmp (args, "failure", 7)) { + if (0 == strncmp(args, "failure", 7)) { expect_failure = 1; - /* Option: Fail with an expected errno (syntax: expect failure errno -33) */ - if (0==strncmp (column (args, 2), "errno", 5)) - expect_failure_with_errno = errno_from_err_const (column (args, 3)); + /* Option: Fail with an expected errno (syntax: expect failure errno + * -33) */ + if (0 == strncmp(column(args, 2), "errno", 5)) + expect_failure_with_errno = errno_from_err_const(column(args, 3)); } - if (T.ignore==proj_errno(T.P)) - return another_skip (); + if (T.ignore == proj_errno(T.P)) + return another_skip(); - if (nullptr==T.P) { + if (nullptr == T.P) { /* If we expect failure, and fail, then it's a success... */ if (expect_failure) { /* Failed to fail correctly? */ - if (expect_failure_with_errno && proj_errno (T.P)!=expect_failure_with_errno) - return expect_failure_with_errno_message (expect_failure_with_errno, proj_errno(T.P)); + if (expect_failure_with_errno && + proj_errno(T.P) != expect_failure_with_errno) + return expect_failure_with_errno_message( + expect_failure_with_errno, proj_errno(T.P)); - return another_succeeding_failure (); + return another_succeeding_failure(); } /* Otherwise, it's a true failure */ - banner (T.operation); - errmsg (3, "%sInvalid operation definition in line no. %d:\n %s (errno=%s/%d)\n", - delim, (int) T.operation_lineno, proj_errno_string (proj_errno(T.P)), - err_const_from_errno (proj_errno(T.P)), proj_errno(T.P) - ); - return another_failing_failure (); + banner(T.operation); + errmsg(3, + "%sInvalid operation definition in line no. %d:\n %s " + "(errno=%s/%d)\n", + delim, (int)T.operation_lineno, + proj_errno_string(proj_errno(T.P)), + err_const_from_errno(proj_errno(T.P)), proj_errno(T.P)); + return another_failing_failure(); } /* We may still successfully fail even if the proj_create succeeded */ if (expect_failure) { - proj_errno_reset (T.P); + proj_errno_reset(T.P); /* Try to carry out the operation - and expect failure */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; - co = expect_trans_n_dim (ci); + ci = + proj_angular_input(T.P, T.dir) ? torad_coord(T.P, T.dir, T.a) : T.a; + co = expect_trans_n_dim(ci); if (expect_failure_with_errno) { - if (proj_errno (T.P)==expect_failure_with_errno) - return another_succeeding_failure (); - //fprintf (T.fout, "errno=%d, expected=%d\n", proj_errno (T.P), expect_failure_with_errno); - banner (T.operation); - errmsg (3, "%serrno=%s (%d), expected=%d at line %d\n", - delim, - err_const_from_errno(proj_errno(T.P)), - proj_errno (T.P), - expect_failure_with_errno, - static_cast(F->lineno) - ); - return another_failing_failure (); + if (proj_errno(T.P) == expect_failure_with_errno) + return another_succeeding_failure(); + // fprintf (T.fout, "errno=%d, expected=%d\n", proj_errno (T.P), + // expect_failure_with_errno); + banner(T.operation); + errmsg(3, "%serrno=%s (%d), expected=%d at line %d\n", delim, + err_const_from_errno(proj_errno(T.P)), proj_errno(T.P), + expect_failure_with_errno, static_cast(F->lineno)); + return another_failing_failure(); } - /* Succeeded in failing? - that's a success */ - if (co.xyz.x==HUGE_VAL) - return another_succeeding_failure (); + if (co.xyz.x == HUGE_VAL) + return another_succeeding_failure(); /* Failed to fail? - that's a failure */ - banner (T.operation); - errmsg (3, "%sFailed to fail. Operation definition in line no. %d\n", - delim, (int) T.operation_lineno - ); - return another_failing_failure (); + banner(T.operation); + errmsg(3, "%sFailed to fail. Operation definition in line no. %d\n", + delim, (int)T.operation_lineno); + return another_failing_failure(); } - if (T.verbosity > 3) { - fprintf (T.fout, "%s\n", T.P->inverted? "INVERTED": "NOT INVERTED"); - fprintf (T.fout, "%s\n", T.dir== 1? "forward": "reverse"); - fprintf (T.fout, "%s\n", proj_angular_input (T.P, T.dir)? "angular in": "linear in"); - fprintf (T.fout, "%s\n", proj_angular_output (T.P, T.dir)? "angular out": "linear out"); - fprintf (T.fout, "left: %d right: %d\n", T.P->left, T.P->right); + fprintf(T.fout, "%s\n", T.P->inverted ? "INVERTED" : "NOT INVERTED"); + fprintf(T.fout, "%s\n", T.dir == 1 ? "forward" : "reverse"); + fprintf(T.fout, "%s\n", + proj_angular_input(T.P, T.dir) ? "angular in" : "linear in"); + fprintf(T.fout, "%s\n", + proj_angular_output(T.P, T.dir) ? "angular out" : "linear out"); + fprintf(T.fout, "left: %d right: %d\n", T.P->left, T.P->right); } tests++; - T.e = parse_coord (args); - if (HUGE_VAL==T.e.v[0]) - return expect_message_cannot_parse (args); - + T.e = parse_coord(args); + if (HUGE_VAL == T.e.v[0]) + return expect_message_cannot_parse(args); /* expected angular values, probably in degrees */ - ce = proj_angular_output (T.P, T.dir)? torad_coord (T.P, T.dir, T.e): T.e; + ce = proj_angular_output(T.P, T.dir) ? torad_coord(T.P, T.dir, T.e) : T.e; if (T.verbosity > 3) - fprintf (T.fout, "EXPECTS %.12f %.12f %.12f %.12f\n", - ce.v[0],ce.v[1],ce.v[2],ce.v[3]); + fprintf(T.fout, "EXPECTS %.12f %.12f %.12f %.12f\n", ce.v[0], + ce.v[1], ce.v[2], ce.v[3]); /* input ("accepted") values, also probably in degrees */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; + ci = proj_angular_input(T.P, T.dir) ? torad_coord(T.P, T.dir, T.a) : T.a; if (T.verbosity > 3) - fprintf (T.fout, "ACCEPTS %.12f %.12f %.12f %.12f\n", - ci.v[0],ci.v[1],ci.v[2],ci.v[3]); + fprintf(T.fout, "ACCEPTS %.12f %.12f %.12f %.12f\n", ci.v[0], + ci.v[1], ci.v[2], ci.v[3]); - /* do the transformation, but mask off dimensions not given in expect-ation */ - co = expect_trans_n_dim (ci); + /* do the transformation, but mask off dimensions not given in expect-ation + */ + co = expect_trans_n_dim(ci); if (T.dimensions_given < 4) co.v[3] = 0; if (T.dimensions_given < 3) co.v[2] = 0; /* angular output from proj_trans comes in radians */ - T.b = proj_angular_output (T.P, T.dir)? todeg_coord (T.P, T.dir, co): co; + T.b = proj_angular_output(T.P, T.dir) ? todeg_coord(T.P, T.dir, co) : co; if (T.verbosity > 3) - fprintf (T.fout, "GOT %.12f %.12f %.12f %.12f\n", - co.v[0],co.v[1],co.v[2],co.v[3]); + fprintf(T.fout, "GOT %.12f %.12f %.12f %.12f\n", co.v[0], + co.v[1], co.v[2], co.v[3]); #if 0 /* We need to handle unusual axis orders - that'll be an item for version 5.1 */ @@ -1023,165 +1053,180 @@ Tell GIE what to expect, when transforming the ACCEPTed input co = proj_trans (T.P->axisswap, T.dir, co); } #endif - if (proj_angular_output (T.P, T.dir)) - d = proj_lpz_dist (T.P, ce, co); - else - d = proj_xyz_dist (co, ce); + if (std::isnan(co.v[0]) && std::isnan(ce.v[0])) { + d = 0.0; + } else if (proj_angular_output(T.P, T.dir)) { + d = proj_lpz_dist(T.P, ce, co); + } else { + d = proj_xyz_dist(co, ce); + } // Test written like that to handle NaN if (!(d <= T.tolerance)) - return expect_message (d, args); + return expect_message(d, args); succs++; - another_success (); + another_success(); return 0; } - - /*****************************************************************************/ -static int verbose (const char *args) { -/***************************************************************************** -Tell the system how noisy it should be -******************************************************************************/ - int i = (int) proj_atof (args); +static int verbose(const char *args) { + /***************************************************************************** + Tell the system how noisy it should be + ******************************************************************************/ + int i = (int)proj_atof(args); /* if -q/--quiet flag has been given, we do nothing */ if (T.verbosity < 0) return 0; - if (strlen (args)) + if (strlen(args)) T.verbosity = i; else T.verbosity++; return 0; } - /*****************************************************************************/ -static int echo (const char *args) { -/***************************************************************************** -Add user defined noise to the output stream -******************************************************************************/ -fprintf (T.fout, "%s\n", args); +static int echo(const char *args) { + /***************************************************************************** + Add user defined noise to the output stream + ******************************************************************************/ + fprintf(T.fout, "%s\n", args); return 0; } - - /*****************************************************************************/ -static int skip (const char *args) { -/***************************************************************************** -Indicate that the remaining material should be skipped. Mostly for debugging. -******************************************************************************/ +static int skip(const char *args) { + /***************************************************************************** + Indicate that the remaining material should be skipped. Mostly for + debugging. + ******************************************************************************/ T.skip = 1; - (void) args; + (void)args; F->level = 2; /* Silence complaints about missing element */ return 0; } - -static int dispatch (const char *cmnd, const char *args) { +static int dispatch(const char *cmnd, const char *args) { if (T.skip) return SKIP; - if (0==strcmp (cmnd, "operation")) return operation ((char *) args); - if (0==strcmp (cmnd, "crs_src")) return crs_src (args); - if (0==strcmp (cmnd, "crs_dst")) return crs_dst (args); - if (T.skip_test) - { - if (0==strcmp (cmnd, "expect")) return another_skip(); + if (0 == strcmp(cmnd, "operation")) + return operation(args); + if (0 == strcmp(cmnd, "crs_src")) + return crs_src(args); + if (0 == strcmp(cmnd, "crs_dst")) + return crs_dst(args); + if (T.skip_test) { + if (0 == strcmp(cmnd, "expect")) + return another_skip(); return 0; } - if (0==strcmp (cmnd, "accept")) return accept (args); - if (0==strcmp (cmnd, "expect")) return expect (args); - if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args); - if (0==strcmp (cmnd, "banner")) return banner (args); - if (0==strcmp (cmnd, "verbose")) return verbose (args); - if (0==strcmp (cmnd, "direction")) return direction (args); - if (0==strcmp (cmnd, "tolerance")) return tolerance (args); - if (0==strcmp (cmnd, "ignore")) return ignore (args); - if (0==strcmp (cmnd, "require_grid")) return require_grid (args); - if (0==strcmp (cmnd, "echo")) return echo (args); - if (0==strcmp (cmnd, "skip")) return skip (args); - if (0==strcmp (cmnd, "use_proj4_init_rules")) - return use_proj4_init_rules (args); + if (0 == strcmp(cmnd, "accept")) + return accept(args); + if (0 == strcmp(cmnd, "expect")) + return expect(args); + if (0 == strcmp(cmnd, "roundtrip")) + return roundtrip(args); + if (0 == strcmp(cmnd, "banner")) + return banner(args); + if (0 == strcmp(cmnd, "verbose")) + return verbose(args); + if (0 == strcmp(cmnd, "direction")) + return direction(args); + if (0 == strcmp(cmnd, "tolerance")) + return tolerance(args); + if (0 == strcmp(cmnd, "ignore")) + return ignore(args); + if (0 == strcmp(cmnd, "require_grid")) + return require_grid(args); + if (0 == strcmp(cmnd, "echo")) + return echo(args); + if (0 == strcmp(cmnd, "skip")) + return skip(args); + if (0 == strcmp(cmnd, "use_proj4_init_rules")) + return use_proj4_init_rules(args); return 0; } - - - namespace { // anonymous namespace -static const struct {const char *the_err_const; int the_errno;} lookup[] = { - - { "invalid_op", PROJ_ERR_INVALID_OP }, - { "invalid_op_wrong_syntax", PROJ_ERR_INVALID_OP_WRONG_SYNTAX }, - { "invalid_op_missing_arg", PROJ_ERR_INVALID_OP_MISSING_ARG }, - { "invalid_op_illegal_arg_value", PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE }, - { "invalid_op_mutually_exclusive_args", PROJ_ERR_INVALID_OP_MUTUALLY_EXCLUSIVE_ARGS }, - { "invalid_op_file_not_found_or_invalid", PROJ_ERR_INVALID_OP_FILE_NOT_FOUND_OR_INVALID }, - { "coord_transfm", PROJ_ERR_COORD_TRANSFM }, - { "coord_transfm_invalid_coord", PROJ_ERR_COORD_TRANSFM_INVALID_COORD }, - { "coord_transfm_outside_projection_domain", PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN }, - { "coord_transfm_no_operation", PROJ_ERR_COORD_TRANSFM_NO_OPERATION }, - { "coord_transfm_outside_grid", PROJ_ERR_COORD_TRANSFM_OUTSIDE_GRID }, - { "coord_transfm_grid_at_nodata", PROJ_ERR_COORD_TRANSFM_GRID_AT_NODATA }, - { "other", PROJ_ERR_OTHER }, - { "api_misuse", PROJ_ERR_OTHER_API_MISUSE }, - { "no_inverse_op", PROJ_ERR_OTHER_NO_INVERSE_OP }, - { "network_error", PROJ_ERR_OTHER_NETWORK_ERROR }, +static const struct { + /* cppcheck-suppress unusedStructMember */ + const char *the_err_const; + /* cppcheck-suppress unusedStructMember */ + int the_errno; +} lookup[] = { + + {"invalid_op", PROJ_ERR_INVALID_OP}, + {"invalid_op_wrong_syntax", PROJ_ERR_INVALID_OP_WRONG_SYNTAX}, + {"invalid_op_missing_arg", PROJ_ERR_INVALID_OP_MISSING_ARG}, + {"invalid_op_illegal_arg_value", PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE}, + {"invalid_op_mutually_exclusive_args", + PROJ_ERR_INVALID_OP_MUTUALLY_EXCLUSIVE_ARGS}, + {"invalid_op_file_not_found_or_invalid", + PROJ_ERR_INVALID_OP_FILE_NOT_FOUND_OR_INVALID}, + {"coord_transfm", PROJ_ERR_COORD_TRANSFM}, + {"coord_transfm_invalid_coord", PROJ_ERR_COORD_TRANSFM_INVALID_COORD}, + {"coord_transfm_outside_projection_domain", + PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN}, + {"coord_transfm_no_operation", PROJ_ERR_COORD_TRANSFM_NO_OPERATION}, + {"coord_transfm_outside_grid", PROJ_ERR_COORD_TRANSFM_OUTSIDE_GRID}, + {"coord_transfm_grid_at_nodata", PROJ_ERR_COORD_TRANSFM_GRID_AT_NODATA}, + {"other", PROJ_ERR_OTHER}, + {"api_misuse", PROJ_ERR_OTHER_API_MISUSE}, + {"no_inverse_op", PROJ_ERR_OTHER_NO_INVERSE_OP}, + {"network_error", PROJ_ERR_OTHER_NETWORK_ERROR}, }; } // anonymous namespace -static int list_err_codes (void) { +static int list_err_codes(void) { int i; const int n = sizeof lookup / sizeof lookup[0]; - for (i = 0; i < n; i++) { - fprintf (T.fout, "%25s (%2.2d): %s\n", lookup[i].the_err_const, - lookup[i].the_errno, proj_errno_string (lookup[i].the_errno)); + for (i = 0; i < n; i++) { + fprintf(T.fout, "%25s (%2.2d): %s\n", lookup[i].the_err_const, + lookup[i].the_errno, proj_errno_string(lookup[i].the_errno)); } return 0; } - -static const char *err_const_from_errno (int err) { +static const char *err_const_from_errno(int err) { size_t i; const size_t n = sizeof lookup / sizeof lookup[0]; - for (i = 0; i < n; i++) { - if (err==lookup[i].the_errno) + for (i = 0; i < n; i++) { + if (err == lookup[i].the_errno) return lookup[i].the_err_const; } return "unknown"; } - -static int errno_from_err_const (const char *err_const) { +static int errno_from_err_const(const char *err_const) { const size_t n = sizeof lookup / sizeof lookup[0]; size_t i, len; int ret; char tolower_err_const[100] = {}; /* Make a lower case copy for matching */ - for (i = 0; i < 99; i++) { - if (0==err_const[i] || isspace (err_const[i])) - break; - tolower_err_const[i] = (char) tolower (err_const[i]); + for (i = 0; i < 99; i++) { + if (0 == err_const[i] || isspace(err_const[i])) + break; + tolower_err_const[i] = (char)tolower(err_const[i]); } tolower_err_const[i] = 0; /* If it looks numeric, return that numeric */ - ret = (int) pj_atof (err_const); - if (0!=ret) + ret = (int)pj_atof(err_const); + if (0 != ret) return ret; /* Else try to find a matching identifier */ - len = strlen (tolower_err_const); + len = strlen(tolower_err_const); - for (i = 0; i < n; i++) { - if (0==strncmp (lookup[i].the_err_const, err_const, len)) + for (i = 0; i < n; i++) { + if (0 == strncmp(lookup[i].the_err_const, err_const, len)) return lookup[i].the_errno; } @@ -1189,8 +1234,7 @@ static int errno_from_err_const (const char *err_const) { return 9999; } - -static int errmsg (int errlev, const char *msg, ...) { +static int errmsg(int errlev, const char *msg, ...) { va_list args; va_start(args, msg); vfprintf(stdout, msg, args); @@ -1200,13 +1244,6 @@ static int errmsg (int errlev, const char *msg, ...) { return errlev; } - - - - - - - /**************************************************************************************** FFIO - Flexible format I/O @@ -1226,33 +1263,33 @@ See the PROJ ".gie" test suites for examples of supported formatting. ****************************************************************************************/ - /***************************************************************************************/ -static ffio *ffio_create (const char * const *tags, size_t n_tags, size_t max_record_size) { -/**************************************************************************************** -Constructor for the ffio object. -****************************************************************************************/ - ffio *G = static_cast(calloc (1, sizeof (ffio))); - if (nullptr==G) +static ffio *ffio_create(const char *const *tags, size_t n_tags, + size_t max_record_size) { + /**************************************************************************************** + Constructor for the ffio object. + ****************************************************************************************/ + ffio *G = static_cast(calloc(1, sizeof(ffio))); + if (nullptr == G) return nullptr; - if (0==max_record_size) + if (0 == max_record_size) max_record_size = 1000; - G->args = static_cast(calloc (1, 5*max_record_size)); - if (nullptr==G->args) { - free (G); + G->args = static_cast(calloc(1, 5 * max_record_size)); + if (nullptr == G->args) { + free(G); return nullptr; } - G->next_args = static_cast(calloc (1, max_record_size)); - if (nullptr==G->args) { - free (G->args); - free (G); + G->next_args = static_cast(calloc(1, max_record_size)); + if (nullptr == G->args) { + free(G->args); + free(G); return nullptr; } - G->args_size = 5*max_record_size; + G->args_size = 5 * max_record_size; G->next_args_size = max_record_size; G->tags = tags; @@ -1260,162 +1297,146 @@ Constructor for the ffio object. return G; } - - /***************************************************************************************/ -static ffio *ffio_destroy (ffio *G) { -/**************************************************************************************** -Free all allocated associated memory, then free G itself. For extra RAII compliance, -the file object should also be closed if still open, but this will require additional -control logic, and ffio is a gie tool specific package, so we fall back to asserting that -fclose has been called prior to ffio_destroy. -****************************************************************************************/ - free (G->args); - free (G->next_args); - free (G); +static ffio *ffio_destroy(ffio *G) { + /**************************************************************************************** + Free all allocated associated memory, then free G itself. For extra RAII + compliance, the file object should also be closed if still open, but this + will require additional control logic, and ffio is a gie tool specific + package, so we fall back to asserting that fclose has been called prior to + ffio_destroy. + ****************************************************************************************/ + free(G->args); + free(G->next_args); + free(G); return nullptr; } - - /***************************************************************************************/ -static int at_decorative_element (ffio *G) { -/**************************************************************************************** -A decorative element consists of a line of at least 5 consecutive identical chars, -starting at buffer position 0: -"-----", "=====", "*****", etc. - -A decorative element serves as a end delimiter for the current element, and -continues until a gie command verb is found at the start of a line -****************************************************************************************/ +static int at_decorative_element(ffio *G) { + /**************************************************************************************** + A decorative element consists of a line of at least 5 consecutive identical + chars, starting at buffer position 0: + "-----", "=====", "*****", etc. + + A decorative element serves as a end delimiter for the current element, and + continues until a gie command verb is found at the start of a line + ****************************************************************************************/ int i; char *c; - if (nullptr==G) + if (nullptr == G) return 0; c = G->next_args; - if (nullptr==c) + if (nullptr == c) return 0; - if (0==c[0]) + if (0 == c[0]) return 0; for (i = 1; i < 5; i++) - if (c[i]!=c[0]) + if (c[i] != c[0]) return 0; return 1; } - - /***************************************************************************************/ -static const char *at_tag (ffio *G) { -/**************************************************************************************** -A start of a new command serves as an end delimiter for the current command -****************************************************************************************/ +static const char *at_tag(ffio *G) { + /**************************************************************************************** + A start of a new command serves as an end delimiter for the current command + ****************************************************************************************/ size_t j; - for (j = 0; j < G->n_tags; j++) - if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0) + for (j = 0; j < G->n_tags; j++) + if (strncmp(G->next_args, G->tags[j], strlen(G->tags[j])) == 0) return G->tags[j]; return nullptr; } - - /***************************************************************************************/ -static int at_end_delimiter (ffio *G) { -/**************************************************************************************** -An instruction consists of everything from its introductory tag to its end -delimiter. An end delimiter can be either the introductory tag of the next -instruction, or a "decorative element", i.e. one of the "ascii art" style -block delimiters typically used to mark up block comments in a free format -file. -****************************************************************************************/ - if (G==nullptr) +static int at_end_delimiter(ffio *G) { + /**************************************************************************************** + An instruction consists of everything from its introductory tag to its end + delimiter. An end delimiter can be either the introductory tag of the next + instruction, or a "decorative element", i.e. one of the "ascii art" style + block delimiters typically used to mark up block comments in a free format + file. + ****************************************************************************************/ + if (G == nullptr) return 0; - if (at_decorative_element (G)) + if (at_decorative_element(G)) return 1; - if (at_tag (G)) + if (at_tag(G)) return 1; return 0; } - - /***************************************************************************************/ -static int nextline (ffio *G) { -/**************************************************************************************** -Read next line of input file. Returns 1 on success, 0 on failure. -****************************************************************************************/ +static int nextline(ffio *G) { + /**************************************************************************************** + Read next line of input file. Returns 1 on success, 0 on failure. + ****************************************************************************************/ G->next_args[0] = 0; if (T.skip) return 0; - if (nullptr==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) + if (nullptr == fgets(G->next_args, (int)G->next_args_size - 1, G->f)) return 0; - if (feof (G->f)) + if (feof(G->f)) return 0; - pj_chomp (G->next_args); + pj_chomp(G->next_args); G->next_lineno++; return 1; } - /***************************************************************************************/ -static int step_into_gie_block (ffio *G) { -/**************************************************************************************** -Make sure we're inside a -block. Return 1 on success, 0 otherwise. -****************************************************************************************/ +static int step_into_gie_block(ffio *G) { + /**************************************************************************************** + Make sure we're inside a -block. Return 1 on success, 0 otherwise. + ****************************************************************************************/ /* Already inside */ if (G->level % 2) return 1; - while (strncmp (G->next_args, "", strlen("")) != 0 && - strncmp (G->next_args, "", strlen("")) != 0 ) - { - if (0==nextline (G)) + while (strncmp(G->next_args, "", strlen("")) != 0 && + strncmp(G->next_args, "", strlen("")) != 0) { + if (0 == nextline(G)) return 0; } G->level++; - if( strncmp (G->next_args, "", strlen("")) == 0 ) - { + if (strncmp(G->next_args, "", strlen("")) == 0) { G->strict_mode = true; return 0; - } - else - { + } else { /* We're ready at the start - now step into the block */ - return nextline (G); + return nextline(G); } } - - /***************************************************************************************/ -static int skip_to_next_tag (ffio *G) { -/**************************************************************************************** -Skip forward to the next command tag. Return 1 on success, 0 otherwise. -****************************************************************************************/ +static int skip_to_next_tag(ffio *G) { + /**************************************************************************************** + Skip forward to the next command tag. Return 1 on success, 0 otherwise. + ****************************************************************************************/ const char *c; - if (0==step_into_gie_block (G)) + if (0 == step_into_gie_block(G)) return 0; - c = at_tag (G); + c = at_tag(G); /* If not already there - get there */ while (!c) { - if (0==nextline (G)) + if (0 == nextline(G)) return 0; - c = at_tag (G); + c = at_tag(G); } /* If we reached the end of a block, locate the next and retry */ - if (0==strcmp (c, "")) { + if (0 == strcmp(c, "")) { G->level++; - if (feof (G->f)) + if (feof(G->f)) return 0; - if (0==step_into_gie_block (G)) + if (0 == step_into_gie_block(G)) return 0; G->args[0] = 0; - return skip_to_next_tag (G); + return skip_to_next_tag(G); } G->lineno = G->next_lineno; @@ -1423,76 +1444,69 @@ Skip forward to the next command tag. Return 1 on success, 0 otherwise. } /* Add the most recently read line of input to the block already stored. */ -static int append_args (ffio *G) { +static int append_args(ffio *G) { size_t skip_chars = 0; - size_t next_len = strlen (G->next_args); - size_t args_len = strlen (G->args); - const char *tag = at_tag (G); + size_t next_len = strlen(G->next_args); + size_t args_len = strlen(G->args); + const char *tag = at_tag(G); if (tag) - skip_chars = strlen (tag); + skip_chars = strlen(tag); /* +2: 1 for the space separator and 1 for the NUL termination. */ if (G->args_size < args_len + next_len - skip_chars + 2) { - char *p = static_cast(realloc (G->args, 2 * G->args_size)); - if (nullptr==p) + char *p = static_cast(realloc(G->args, 2 * G->args_size)); + if (nullptr == p) return 0; G->args = p; G->args_size = 2 * G->args_size; } G->args[args_len] = ' '; - strcpy (G->args + args_len + 1, G->next_args + skip_chars); + strcpy(G->args + args_len + 1, G->next_args + skip_chars); G->next_args[0] = 0; return 1; } - - - - /***************************************************************************************/ -static int get_inp (ffio *G) { -/**************************************************************************************** -The primary command reader for gie. Reads a block of gie input, cleans up repeated -whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise. -****************************************************************************************/ +static int get_inp(ffio *G) { + /**************************************************************************************** + The primary command reader for gie. Reads a block of gie input, cleans up + repeated whitespace etc. The block is stored in G->args. Returns 1 on + success, 0 otherwise. + ****************************************************************************************/ G->args[0] = 0; // Special parsing in strict_mode: // - All non-comment/decoration lines must start with a valid tag // - Commands split on several lines should be terminated with " \" - if( G->strict_mode ) - { - while( nextline(G) ) - { + if (G->strict_mode) { + while (nextline(G)) { G->lineno = G->next_lineno; - if( G->next_args[0] == 0 || at_decorative_element(G) ) { + if (G->next_args[0] == 0 || at_decorative_element(G)) { continue; } - G->tag = at_tag (G); - if (nullptr==G->tag) - { + G->tag = at_tag(G); + if (nullptr == G->tag) { another_failure(); - fprintf (T.fout, "unsupported command line %d: %s\n", (int)G->lineno, G->next_args); + fprintf(T.fout, "unsupported command line %d: %s\n", + (int)G->lineno, G->next_args); return 0; } - append_args (G); - pj_shrink (G->args); - while( G->args[0] != '\0' && G->args[strlen(G->args)-1] == '\\' ) - { - G->args[strlen(G->args)-1] = 0; - if( !nextline(G) ) - { + append_args(G); + pj_shrink(G->args); + while (G->args[0] != '\0' && G->args[strlen(G->args) - 1] == '\\') { + G->args[strlen(G->args) - 1] = 0; + if (!nextline(G)) { return 0; } G->lineno = G->next_lineno; - append_args (G); - pj_shrink (G->args); + append_args(G); + pj_shrink(G->args); } - if ( 0==strcmp (G->tag, "")) { + if (0 == strcmp(G->tag, "")) { G->level++; G->strict_mode = false; } @@ -1501,24 +1515,23 @@ whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwis return 0; } - if (0==skip_to_next_tag (G)) - { + if (0 == skip_to_next_tag(G)) { // If we just entered , re-enter to read the first command - if( G->strict_mode ) + if (G->strict_mode) return get_inp(G); return 0; } - G->tag = at_tag (G); + G->tag = at_tag(G); - if (nullptr==G->tag) + if (nullptr == G->tag) return 0; do { - append_args (G); - if (0==nextline (G)) + append_args(G); + if (0 == nextline(G)) return 0; - } while (!at_end_delimiter (G)); + } while (!at_end_delimiter(G)); - pj_shrink (G->args); + pj_shrink(G->args); return 1; } diff --git a/deps/libproj/proj/src/apps/optargpm.h b/deps/libproj/proj/src/apps/optargpm.h index 921b8c0b9..c07f1020f 100644 --- a/deps/libproj/proj/src/apps/optargpm.h +++ b/deps/libproj/proj/src/apps/optargpm.h @@ -69,7 +69,7 @@ opt_given (o, option): opt_arg (o, option): A char pointer to the argument for