diff options
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | Dockerfile | 17 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | docs/source/apps/projinfo.rst | 17 | ||||
| -rw-r--r-- | docs/source/development/reference/functions.rst | 17 | ||||
| -rw-r--r-- | docs/source/resource_files.rst | 2 | ||||
| -rw-r--r-- | include/proj/io.hpp | 8 | ||||
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/apps/cs2cs.cpp | 8 | ||||
| -rw-r--r-- | src/apps/proj.cpp | 9 | ||||
| -rw-r--r-- | src/apps/utils.cpp | 58 | ||||
| -rw-r--r-- | src/apps/utils.h | 29 | ||||
| -rw-r--r-- | src/bin_cs2cs.cmake | 1 | ||||
| -rw-r--r-- | src/bin_proj.cmake | 1 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 3 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 31 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 5 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 138 | ||||
| -rw-r--r-- | src/projections/airy.cpp | 4 | ||||
| -rw-r--r-- | src/projections/aitoff.cpp | 31 | ||||
| -rw-r--r-- | src/projections/mod_ster.cpp | 7 | ||||
| -rw-r--r-- | src/projections/omerc.cpp | 2 | ||||
| -rw-r--r-- | test/gie/builtins.gie | 21 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 58 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 45 | ||||
| -rwxr-xr-x | travis/install.sh | 19 | ||||
| -rwxr-xr-x | travis/osx/before_install.sh | 3 |
27 files changed, 472 insertions, 74 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 12a24b7b..02f49711 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,8 +19,6 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.1) cmake_policy(SET CMP0054 NEW) endif() -add_definitions(-DPROJ_COMPILATION=1) - # Set C++ version # Make CMAKE_CXX_STANDARD available as cache option overridable by user set(CMAKE_CXX_STANDARD 11 @@ -34,19 +34,18 @@ RUN apt-get update; \ libsqlite3-0 \ curl unzip -COPY --from=builder /build/usr/bin/ /usr/bin/ -COPY --from=builder /build/usr/lib/ /usr/lib/ -COPY --from=builder /build/usr/include/ /usr/include/ -COPY --from=builder /build/usr/share/proj/ /usr/share/proj/ - +# Put this first as this is rarely changing RUN \ + mkdir -p /usr/share/proj; \ curl -LOs http://download.osgeo.org/proj/proj-datumgrid-1.8.zip && unzip -j -u -o proj-datumgrid-1.8.zip -d /usr/share/proj; \ curl -LOs http://download.osgeo.org/proj/proj-datumgrid-europe-1.2.zip && unzip -j -u -o proj-datumgrid-europe-1.2.zip -d /usr/share/proj; \ curl -LOs http://download.osgeo.org/proj/proj-datumgrid-oceania-1.0.zip && unzip -j -u -o proj-datumgrid-oceania-1.0.zip -d /usr/share/proj; \ curl -LOs http://download.osgeo.org/proj/proj-datumgrid-world-1.0.zip && unzip -j -u -o proj-datumgrid-world-1.0.zip -d /usr/share/proj; \ - curl -LOs http://download.osgeo.org/proj/proj-datumgrid-north-america-1.2.zip && unzip -j -u -o proj-datumgrid-north-america-1.2.zip -d /usr/share/proj; - - - + curl -LOs http://download.osgeo.org/proj/proj-datumgrid-north-america-1.2.zip && unzip -j -u -o proj-datumgrid-north-america-1.2.zip -d /usr/share/proj; \ + rm *.zip +COPY --from=builder /build/usr/share/proj/ /usr/share/proj/ +COPY --from=builder /build/usr/include/ /usr/include/ +COPY --from=builder /build/usr/bin/ /usr/bin/ +COPY --from=builder /build/usr/lib/ /usr/lib/ @@ -4,7 +4,7 @@ [](https://ci.appveyor.com/project/OSGeo/proj-4) [](https://coveralls.io/github/OSGeo/proj.4?branch=master) [](https://gitter.im/OSGeo/proj.4) -[](http://lists.maptools.org/mailman/listinfo/proj) +[](http://lists.osgeo.org/mailman/listinfo/proj) PROJ is a generic coordinate transformation software, that transforms coordinates from one coordinate reference system (CRS) to another. diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index c8219838..955b9a18 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -29,12 +29,17 @@ Synopsis | {object_definition} | (-s {srs_def} -t {srs_def}) | - where {object_definition} or {object_definition} is a PROJ string, a - WKT string, an object name, AUTHORITY:CODE - (where AUTHORITY is the name of a CRS authority and CODE the code of a CRS - found in the proj.db database) or a OGC URN (such as "urn:ogc:def:crs:EPSG::4326", - "urn:ogc:def:coordinateOperation:EPSG::1671", "urn:ogc:def:ellipsoid:EPSG::7001" - or "urn:ogc:def:datum:EPSG::6326") + where {object_definition} or {srs_def} is + + - a proj-string, + - a WKT string, + - an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", + "urn:ogc:def:coordinateOperation:EPSG::1671"), + - a OGC URN combining references for compound coordinate reference systems + (e.g "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" or custom abbreviated + syntax "EPSG:2393+5717"), + - a OGC URN combining references for concatenated operations + (e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618") Description *********** diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index 319face3..64a4e8ca 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -29,9 +29,17 @@ paragraph for more details. .. c:function:: PJ* proj_create(PJ_CONTEXT *ctx, const char *definition) - Create a transformation object, or a CRS object, from a proj-string, - a WKT string, or object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", - "urn:ogc:def:coordinateOperation:EPSG::1671"). + Create a transformation object, or a CRS object, from: + + - a proj-string, + - a WKT string, + - an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", + "urn:ogc:def:coordinateOperation:EPSG::1671"), + - a OGC URN combining references for compound coordinate reference systems + (e.g "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" or custom abbreviated + syntax "EPSG:2393+5717"), + - a OGC URN combining references for concatenated operations + (e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618") Example call: @@ -110,7 +118,8 @@ paragraph for more details. - the name of a CRS as found in the PROJ database, e.g "WGS84", "NAD27", etc. - - more generally any string accepted by :c:func:`proj_create` + - more generally any string accepted by :c:func:`proj_create` representing + a CRS An "area of use" can be specified in area. When it is supplied, the more accurate transformation between two given systems can be chosen. diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst index 53843571..d6f64175 100644 --- a/docs/source/resource_files.rst +++ b/docs/source/resource_files.rst @@ -106,7 +106,7 @@ Brazil Netherlands ................................................................................ -`Dutch grid <https://www.kadaster.nl/web/Themas/Registraties/Rijksdriehoeksmeting/Transformatie-van-coordinaten.htm>`__ (Registration required before download) +`Dutch grid <https://zakelijk.kadaster.nl/transformatie-van-coordinaten>`__ (Registration required before download) Portugal ................................................................................ diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 5386ca6c..a603533e 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -993,16 +993,16 @@ class PROJ_GCC_DLL AuthorityFactory { PROJ_INTERNAL std::list<crs::CompoundCRSNNPtr> createCompoundCRSFromExisting(const crs::CompoundCRSNNPtr &crs) const; + + PROJ_INTERNAL crs::CRSNNPtr + createCoordinateReferenceSystem(const std::string &code, + bool allowCompound) const; //! @endcond protected: PROJ_INTERNAL AuthorityFactory(const DatabaseContextNNPtr &context, const std::string &authorityName); - PROJ_INTERNAL crs::CRSNNPtr - createCoordinateReferenceSystem(const std::string &code, - bool allowCompound) const; - PROJ_INTERNAL crs::GeodeticCRSNNPtr createGeodeticCRS(const std::string &code, bool geographicOnly) const; diff --git a/src/Makefile.am b/src/Makefile.am index 9858d78f..aa1787c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,7 @@ check_PROGRAMS = geodtest AM_CPPFLAGS = -DPROJ_LIB=\"$(pkgdatadir)\" \ -DMUTEX_@MUTEX_SETTING@ @JNI_INCLUDE@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ -AM_CXXFLAGS = @CXX_WFLAGS@ @FLTO_FLAG@ -DPROJ_COMPILATION +AM_CXXFLAGS = @CXX_WFLAGS@ @FLTO_FLAG@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesic.h \ org_proj4_PJ.h proj_symbol_rename.h @@ -16,11 +16,11 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesi EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \ bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \ lib_proj.cmake CMakeLists.txt bin_geodtest.cmake tests/geodtest.cpp \ - wkt1_grammar.y wkt2_grammar.y apps/emess.h + wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h -proj_SOURCES = apps/proj.cpp apps/emess.cpp +proj_SOURCES = apps/proj.cpp apps/emess.cpp apps/utils.cpp projinfo_SOURCES = apps/projinfo.cpp -cs2cs_SOURCES = apps/cs2cs.cpp apps/emess.cpp +cs2cs_SOURCES = apps/cs2cs.cpp apps/emess.cpp apps/utils.cpp cct_SOURCES = apps/cct.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h geod_SOURCES = apps/geod.cpp apps/geod_set.cpp apps/geod_interface.cpp apps/geod_interface.h apps/emess.cpp diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index 877a68ff..40b0d584 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -45,6 +45,7 @@ #include "proj.h" #include "proj_internal.h" #include "emess.h" +#include "utils.h" // clang-format on #define MAX_LINE 1000 @@ -522,6 +523,13 @@ int main(int argc, char **argv) { if (eargc == 0) /* if no specific files force sysin */ eargv[eargc++] = const_cast<char *>("-"); + if( oform ) { + if( !validate_form_string_for_numbers(oform) ) { + emess(3, "invalid format string"); + exit(0); + } + } + /* * If the user has requested inverse, then just reverse the * coordinate systems. diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp index 2af49c34..888d723f 100644 --- a/src/apps/proj.cpp +++ b/src/apps/proj.cpp @@ -7,6 +7,7 @@ #include <string.h> #include <math.h> #include "emess.h" +#include "utils.h" #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__) # include <fcntl.h> @@ -461,6 +462,13 @@ int main(int argc, char **argv) { if (eargc == 0) /* if no specific files force sysin */ eargv[eargc++] = const_cast<char*>("-"); + if( oform ) { + if( !validate_form_string_for_numbers(oform) ) { + emess(3, "invalid format string"); + exit(0); + } + } + /* done with parameter and control input */ if (inverse && postscale) { prescale = 1; @@ -487,7 +495,6 @@ int main(int argc, char **argv) { proj.inv = pj_inv; } else proj.fwd = pj_fwd; - /* set input formatting control */ if (mon) { pj_pr_list(Proj); diff --git a/src/apps/utils.cpp b/src/apps/utils.cpp new file mode 100644 index 00000000..7dc809c9 --- /dev/null +++ b/src/apps/utils.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Utilities for command line arguments + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "utils.h" + +#include <string.h> + +bool validate_form_string_for_numbers(const char* formatString) { + /* Only accepts '%[+]?[number]?[.]?[number]?[e|E|f|F|g|G]' */ + bool valid = true; + if( formatString[0] != '%' ) + valid = false; + else { + auto oformLen = strlen(formatString); + for( int i = 1; i < static_cast<int>(oformLen) - 1; i++ ) { + if( !(formatString[i] == '.' || + formatString[i] == '+' || + (formatString[i] >= '0' && formatString[i] <= '9')) ) { + valid = false; + break; + } + } + if( valid ) { + valid = formatString[oformLen-1] == 'e' || + formatString[oformLen-1] == 'E' || + formatString[oformLen-1] == 'f' || + formatString[oformLen-1] == 'F' || + formatString[oformLen-1] == 'g' || + formatString[oformLen-1] == 'G'; + } + } + return valid; +} diff --git a/src/apps/utils.h b/src/apps/utils.h new file mode 100644 index 00000000..99c14091 --- /dev/null +++ b/src/apps/utils.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Utilities for command line arguments + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +bool validate_form_string_for_numbers(const char* formatString); diff --git a/src/bin_cs2cs.cmake b/src/bin_cs2cs.cmake index 7ee26673..d2bb3b97 100644 --- a/src/bin_cs2cs.cmake +++ b/src/bin_cs2cs.cmake @@ -1,6 +1,7 @@ set(CS2CS_SRC apps/cs2cs.cpp apps/emess.cpp + apps/utils.cpp ) source_group("Source Files\\Bin" FILES ${CS2CS_SRC}) diff --git a/src/bin_proj.cmake b/src/bin_proj.cmake index b9ae03e5..ce282fc6 100644 --- a/src/bin_proj.cmake +++ b/src/bin_proj.cmake @@ -1,6 +1,7 @@ set(PROJ_SRC apps/proj.cpp apps/emess.cpp + apps/utils.cpp ) source_group("Source Files\\Bin" FILES ${PROJ_SRC}) diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 8fea51e9..6a2a1ae0 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -5964,7 +5964,8 @@ int proj_coordoperation_get_param( * @param out_values Pointer to an array of value_count double values. * @param value_count Size of out_values array. The suggested size is 7 to get * translation, rotation and scale difference parameters. Rotation and scale - * difference terms might be zero if the transformation only includes translation + * difference terms might be zero if the transformation only includes + * translation * parameters. In that case, value_count could be set to 3. * @param emit_error_if_incompatible Boolean to inicate if an error must be * logged if coordoperation is not compatible with a WKT1 TOWGS84 diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 7eab849c..9c393aba 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -7484,6 +7484,8 @@ Transformation::Private::registerInv(util::BaseObjectNNPtr thisIn, TransformationNNPtr invTransform) { invTransform->d->forwardOperation_ = util::nn_dynamic_pointer_cast<Transformation>(thisIn); + invTransform->setHasBallparkTransformation( + invTransform->d->forwardOperation_->hasBallparkTransformation()); return invTransform; } //! @endcond @@ -9490,6 +9492,7 @@ CoordinateOperationNNPtr ConcatenatedOperation::inverse() const { auto op = create(properties, inversedOperations, coordinateOperationAccuracies()); op->d->computedName_ = d->computedName_; + op->setHasBallparkTransformation(hasBallparkTransformation()); return op; } @@ -12584,11 +12587,11 @@ getResolvedCRS(const crs::CRSNNPtr &crs, const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), *ids.front()->codeSpace()); try { - crs::CRSNNPtr resolvedCrs( + auto resolvedCrs( tmpAuthFactory->createProjectedCRS(ids.front()->code())); - if (resolvedCrs->_isEquivalentTo( + if (resolvedCrs->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { - return resolvedCrs; + return util::nn_static_pointer_cast<crs::CRS>(resolvedCrs); } } catch (const std::exception &) { } @@ -12616,12 +12619,13 @@ getResolvedCRS(const crs::CRSNNPtr &crs, const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), *ids.front()->codeSpace()); try { - crs::CRSNNPtr resolvedCrs( + auto resolvedCrs( tmpAuthFactory->createCompoundCRS(ids.front()->code())); - if (resolvedCrs->_isEquivalentTo( + if (resolvedCrs->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { - return resolvedCrs; + return util::nn_static_pointer_cast<crs::CRS>( + resolvedCrs); } } catch (const std::exception &) { } @@ -12665,6 +12669,17 @@ CoordinateOperationFactory::createOperations( auto l_resolvedTargetCRS = getResolvedCRS(l_targetCRS, context); Private::Context contextPrivate(l_resolvedSourceCRS, l_resolvedTargetCRS, context); + + if (context->getSourceAndTargetCRSExtentUse() == + CoordinateOperationContext::SourceTargetCRSExtentUse::INTERSECTION) { + auto sourceCRSExtent(getExtent(l_resolvedSourceCRS)); + auto targetCRSExtent(getExtent(l_resolvedTargetCRS)); + if (sourceCRSExtent && targetCRSExtent && + !sourceCRSExtent->intersects(NN_NO_CHECK(targetCRSExtent))) { + return std::vector<CoordinateOperationNNPtr>(); + } + } + return filterAndSort(Private::createOperations(l_resolvedSourceCRS, l_resolvedTargetCRS, contextPrivate), @@ -12703,6 +12718,8 @@ void InverseCoordinateOperation::setPropertiesFromForward() { if (forwardOperation_->sourceCRS() && forwardOperation_->targetCRS()) { setCRSs(forwardOperation_.get(), true); } + setHasBallparkTransformation( + forwardOperation_->hasBallparkTransformation()); } // --------------------------------------------------------------------------- @@ -12785,7 +12802,7 @@ PROJBasedOperationNNPtr PROJBasedOperation::create( auto method = OperationMethod::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, - "PROJ-based operation method (approximate) : " + + "PROJ-based operation method (approximate): " + projString), std::vector<GeneralOperationParameterNNPtr>{}); auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method); diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index f24b3457..9f7467a2 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2484,6 +2484,8 @@ crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem( return createCoordinateReferenceSystem(code, true); } +//! @cond Doxygen_Suppress + crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, bool allowCompound) const { @@ -2513,6 +2515,9 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, } throw FactoryException("unhandled CRS type: " + type); } + +//! @endcond + // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 360d55a2..0a32bb7c 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -1342,7 +1342,7 @@ struct WKTParser::Private { CRSPtr buildCRS(const WKTNodeNNPtr &node); - CoordinateOperationNNPtr buildCoordinateOperation(const WKTNodeNNPtr &node); + TransformationNNPtr buildCoordinateOperation(const WKTNodeNNPtr &node); ConcatenatedOperationNNPtr buildConcatenatedOperation(const WKTNodeNNPtr &node); @@ -2940,7 +2940,7 @@ WKTParser::Private::buildConversion(const WKTNodeNNPtr &node, // --------------------------------------------------------------------------- -CoordinateOperationNNPtr +TransformationNNPtr WKTParser::Private::buildCoordinateOperation(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &methodNode = nodeP->lookForChild(WKTConstants::METHOD); @@ -2991,11 +2991,10 @@ WKTParser::Private::buildCoordinateOperation(const WKTNodeNNPtr &node) { stripQuotes(accuracyNode->GP()->children()[0]))); } - return util::nn_static_pointer_cast<CoordinateOperation>( - Transformation::create(buildProperties(node), NN_NO_CHECK(sourceCRS), - NN_NO_CHECK(targetCRS), interpolationCRS, - buildProperties(methodNode), parameters, values, - accuracies)); + return Transformation::create(buildProperties(node), NN_NO_CHECK(sourceCRS), + NN_NO_CHECK(targetCRS), interpolationCRS, + buildProperties(methodNode), parameters, + values, accuracies); } // --------------------------------------------------------------------------- @@ -4307,16 +4306,31 @@ BaseObjectNNPtr WKTParser::Private::build(const WKTNodeNNPtr &node) { } if (ci_equal(name, WKTConstants::COORDINATEOPERATION)) { - return util::nn_static_pointer_cast<BaseObject>( - buildCoordinateOperation(node)); + auto transf = buildCoordinateOperation(node); + + const char *prefixes[] = { + "PROJ-based operation method: ", + "PROJ-based operation method (approximate): "}; + for (const char *prefix : prefixes) { + if (starts_with(transf->method()->nameStr(), prefix)) { + auto projString = + transf->method()->nameStr().substr(strlen(prefix)); + return util::nn_static_pointer_cast<BaseObject>( + PROJBasedOperation::create( + PropertyMap(), projString, transf->sourceCRS(), + transf->targetCRS(), + transf->coordinateOperationAccuracies())); + } + } + + return util::nn_static_pointer_cast<BaseObject>(transf); } if (ci_equal(name, WKTConstants::CONVERSION)) { auto conv = buildConversion(node, UnitOfMeasure::METRE, UnitOfMeasure::DEGREE); - if (conv->nameStr() == "PROJ-based coordinate operation" && - starts_with(conv->method()->nameStr(), + if (starts_with(conv->method()->nameStr(), "PROJ-based operation method: ")) { auto projString = conv->method()->nameStr().substr( strlen("PROJ-based operation method: ")); @@ -4397,15 +4411,97 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, const auto authorities = dbContextNNPtr->getAuthorities(); for (const auto &authCandidate : authorities) { if (ci_equal(authCandidate, authName)) { - return AuthorityFactory::create(dbContextNNPtr, - authCandidate) - ->createCoordinateReferenceSystem(code); + factory = + AuthorityFactory::create(dbContextNNPtr, authCandidate); + try { + return factory->createCoordinateReferenceSystem(code); + } catch (...) { + // EPSG:4326+3855 + auto tokensCode = split(code, '+'); + if (tokensCode.size() == 2) { + auto crs1(factory->createCoordinateReferenceSystem( + tokensCode[0], false)); + auto crs2(factory->createCoordinateReferenceSystem( + tokensCode[1], false)); + return CompoundCRS::create( + util::PropertyMap().set( + IdentifiedObject::NAME_KEY, + crs1->nameStr() + " + " + crs2->nameStr()), + {crs1, crs2}); + } + throw; + } } } throw; } } + // OGC 07-092r2: para 7.5.2 + // URN combined references for compound coordinate reference systems + if (starts_with(text, "urn:ogc:def:crs,")) { + if (!dbContext) { + throw ParsingException("no database context specified"); + } + auto tokensComma = split(text, ','); + std::vector<CRSNNPtr> components; + std::string name; + for (size_t i = 1; i < tokensComma.size(); i++) { + tokens = split(tokensComma[i], ':'); + if (tokens.size() != 4) { + throw ParsingException( + concat("invalid crs component: ", tokensComma[i])); + } + const auto &type = tokens[0]; + auto factory = + AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); + const auto &code = tokens[3]; + if (type == "crs") { + auto crs(factory->createCoordinateReferenceSystem(code, false)); + components.emplace_back(crs); + if (!name.empty()) { + name += " + "; + } + name += crs->nameStr(); + } else { + throw ParsingException( + concat("unexpected object type: ", type)); + } + } + return CompoundCRS::create( + util::PropertyMap().set(IdentifiedObject::NAME_KEY, name), + components); + } + + // OGC 07-092r2: para 7.5.3 + // 7.5.3 URN combined references for concatenated operations + if (starts_with(text, "urn:ogc:def:coordinateOperation,")) { + if (!dbContext) { + throw ParsingException("no database context specified"); + } + auto tokensComma = split(text, ','); + std::vector<CoordinateOperationNNPtr> components; + for (size_t i = 1; i < tokensComma.size(); i++) { + tokens = split(tokensComma[i], ':'); + if (tokens.size() != 4) { + throw ParsingException(concat( + "invalid coordinateOperation component: ", tokensComma[i])); + } + const auto &type = tokens[0]; + auto factory = + AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); + const auto &code = tokens[3]; + if (type == "coordinateOperation") { + auto op(factory->createCoordinateOperation(code, false)); + components.emplace_back(op); + } else { + throw ParsingException( + concat("unexpected object type: ", type)); + } + } + return ConcatenatedOperation::createComputeMetadata(components, true); + } + // urn:ogc:def:crs:EPSG::4326 if (tokens.size() == 7) { if (!dbContext) { @@ -4510,6 +4606,13 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"</li> + * <li> OGC URN combining references for compound coordinate reference systems + * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" + * We also accept a custom abbreviated syntax EPSG:2393+5717 + * </li> + * <li> OGC URN combining references for concatenated operations + * e.g. + * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li> * <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.</li> @@ -4546,6 +4649,13 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"</li> + * <li> OGC URN combining references for compound coordinate reference systems + * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" + * We also accept a custom abbreviated syntax EPSG:2393+5717 + * </li> + * <li> OGC URN combining references for concatenated operations + * e.g. + * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li> * <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.</li> diff --git a/src/projections/airy.cpp b/src/projections/airy.cpp index f7068061..ba6a40ff 100644 --- a/src/projections/airy.cpp +++ b/src/projections/airy.cpp @@ -79,6 +79,10 @@ static PJ_XY s_forward (PJ_LP lp, PJ *P) { /* Spheroidal, forward */ } if (fabs(s = 1. - cosz) > EPS) { t = 0.5 * (1. + cosz); + if(t == 0) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } Krho = -log(t)/s - Q->Cb / t; } else Krho = 0.5 - Q->Cb; diff --git a/src/projections/aitoff.cpp b/src/projections/aitoff.cpp index 127841ff..23554605 100644 --- a/src/projections/aitoff.cpp +++ b/src/projections/aitoff.cpp @@ -116,15 +116,20 @@ static PJ_LP s_inverse (PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5); sp = sin(lp.phi); cp = cos(lp.phi); D = cp * cl; - C = 1. - D * D; - D = acos(D) / pow(C, 1.5); - f1 = 2. * D * C * cp * sl; - f2 = D * C * sp; - f1p = 2.* (sl * cl * sp * cp / C - D * sp * sl); - f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp; - f2p = sp * sp * cl / C + D * sl * sl * cp; - f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl); - if (Q->mode == WINKEL_TRIPEL) { + C = 1. - D * D; + const double denom = pow(C, 1.5); + if( denom == 0 ) { + proj_errno_set(P, PJD_ERR_NON_CONVERGENT); + return lp; + } + D = acos(D) / denom; + f1 = 2. * D * C * cp * sl; + f2 = D * C * sp; + f1p = 2.* (sl * cl * sp * cp / C - D * sp * sl); + f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp; + f2p = sp * sp * cl / C + D * sl * sl * cp; + f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl); + if (Q->mode == WINKEL_TRIPEL) { f1 = 0.5 * (f1 + lp.lam * Q->cosphi1); f2 = 0.5 * (f2 + lp.phi); f1p *= 0.5; @@ -156,10 +161,10 @@ static PJ_LP s_inverse (PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ } while (((fabs(xy.x-x) > EPSILON) || (fabs(xy.y-y) > EPSILON)) && (round++ < MAXROUND)); if (iter == MAXITER && round == MAXROUND) - { - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - /* fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); */ - } + { + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + /* fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); */ + } return lp; } diff --git a/src/projections/mod_ster.cpp b/src/projections/mod_ster.cpp index b26ea289..50f66839 100644 --- a/src/projections/mod_ster.cpp +++ b/src/projections/mod_ster.cpp @@ -35,7 +35,12 @@ static PJ_XY e_forward (PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */ pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI; schi = sin(chi); cchi = cos(chi); - s = 2. / (1. + Q->schio * schi + Q->cchio * cchi * coslon); + const double denom = 1. + Q->schio * schi + Q->cchio * cchi * coslon; + if( denom == 0 ) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + s = 2. / denom; p.r = s * cchi * sinlon; p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon); p = pj_zpoly1(p, Q->zcoeff, Q->n); diff --git a/src/projections/omerc.cpp b/src/projections/omerc.cpp index e07c209e..0de3aa7d 100644 --- a/src/projections/omerc.cpp +++ b/src/projections/omerc.cpp @@ -154,6 +154,8 @@ PJ *PROJECTION(omerc) { phi1 = pj_param(P->ctx, P->params, "rlat_1").f; lam2 = pj_param(P->ctx, P->params, "rlon_2").f; phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + if (fabs(phi1) > M_HALFPI || fabs(phi2) > M_HALFPI) + return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90); if (fabs(phi1 - phi2) <= TOL || (con = fabs(phi1)) <= TOL || fabs(con - M_HALFPI) <= TOL || diff --git a/test/gie/builtins.gie b/test/gie/builtins.gie index 235771d5..08b8c9b2 100644 --- a/test/gie/builtins.gie +++ b/test/gie/builtins.gie @@ -324,7 +324,7 @@ Airy =============================================================================== ------------------------------------------------------------------------------- -operation +proj=airy +a=6400000 +lat_1=0 +lat_2=2 +operation +proj=airy +a=6400000 ------------------------------------------------------------------------------- tolerance 0.1 mm accept 2 1 @@ -404,6 +404,11 @@ expect 0 0 accept 25 25 expect 0.3821 0.4216 +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +no_cut +------------------------------------------------------------------------------- +accept -180 0 +expect failure errno tolerance_condition =============================================================================== Aitoff @@ -1848,6 +1853,10 @@ expect 4030931.833981509 1323687.864777399 accept -80.000000000 36.000000000 expect 3450764.261536101 -175619.041820732 +# For some reason, does not fail on MacOSX +#accept 60 -45 +#expect failure errno tolerance_condition + direction inverse accept -1800000.000000000 2600000.000000000 expect -157.989285000 64.851559610 @@ -4013,6 +4022,16 @@ operation +proj=omerc +lat_1=0.8 +a=6400000 +b=.4 ------------------------------------------------------------------------------- expect failure errno invalid_eccentricity +------------------------------------------------------------------------------- +operation +proj=omerc +lat_1=91 +------------------------------------------------------------------------------- +expect failure errno lat_larger_than_90 + +------------------------------------------------------------------------------- +operation +proj=omerc +lat_2=91 +------------------------------------------------------------------------------- +expect failure errno lat_larger_than_90 + =============================================================================== Ortelius Oval diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 30e0b427..df2481e6 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -8775,6 +8775,39 @@ TEST(io, createFromUserInput) { EXPECT_THROW( createFromUserInput("urn:ogc:def:unhandled:EPSG::4326", dbContext), ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:non_existing_auth::4326", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,unhandled_type:EPSG::5717", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393::extra_element,crs:EPSG::EPSG", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "unhandled_type:EPSG::1618", + dbContext), + ParsingException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895::extra_element," + "coordinateOperation:EPSG::1618", + dbContext), + ParsingException); EXPECT_NO_THROW(createFromUserInput("+proj=longlat", nullptr)); EXPECT_NO_THROW(createFromUserInput("EPSG:4326", dbContext)); @@ -8789,6 +8822,31 @@ TEST(io, createFromUserInput) { createFromUserInput("urn:ogc:def:meridian:EPSG::8901", dbContext)); EXPECT_NO_THROW( createFromUserInput("urn:ogc:def:ellipsoid:EPSG::7030", dbContext)); + { + auto obj = createFromUserInput("EPSG:2393+5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::1618", + dbContext); + auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj); + ASSERT_TRUE(concat != nullptr); + EXPECT_EQ(concat->nameStr(), + "MGI (Ferro) to MGI (1) + MGI to WGS 84 (3)"); + } EXPECT_NO_THROW(createFromUserInput( "GEOGCRS[\"WGS 84\",\n" " DATUM[\"World Geodetic System 1984\",\n" diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index b7b87d76..a38e9df2 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -5396,6 +5396,39 @@ TEST(operation, projCRS_to_projCRS_context_incompatible_areas) { // --------------------------------------------------------------------------- +TEST(operation, projCRS_to_projCRS_context_incompatible_areas_ballpark) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("26711"), // UTM 11 NAD27 + authFactory->createCoordinateReferenceSystem( + "3034"), // ETRS89 / LCC Europe + ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_TRUE(list[0]->hasBallparkTransformation()); +} + +// --------------------------------------------------------------------------- + +TEST( + operation, + projCRS_to_projCRS_context_incompatible_areas_crs_extent_use_intersection) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSourceAndTargetCRSExtentUse( + CoordinateOperationContext::SourceTargetCRSExtentUse::INTERSECTION); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("26711"), // UTM 11 NAD27 + authFactory->createCoordinateReferenceSystem( + "3034"), // ETRS89 / LCC Europe + ctxt); + ASSERT_GE(list.size(), 0U); +} + +// --------------------------------------------------------------------------- + TEST(operation, projCRS_to_projCRS_north_pole_inverted_axis) { auto authFactory = @@ -6444,6 +6477,18 @@ TEST(operation, compoundCRS_to_compoundCRS_context) { "+step +proj=hgridshift +grids=conus +step " "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " "+order=2,1"); + { + // Test that we can round-trip this through WKT and still get the same + // PROJ string. + auto wkt = list[0]->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + auto obj = WKTParser().createFromWKT(wkt); + auto co = nn_dynamic_pointer_cast<CoordinateOperation>(obj); + ASSERT_TRUE(co != nullptr); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + co->exportToPROJString(PROJStringFormatter::create().get())); + } bool foundApprox = false; for (size_t i = 0; i < list.size(); i++) { diff --git a/travis/install.sh b/travis/install.sh index c8da9046..3c1e92da 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -2,6 +2,17 @@ set -e +UNAME="$(uname)" || UNAME="" +if test "${UNAME}" = "Linux" ; then + NPROC=$(nproc); +elif test "${UNAME}" = "Darwin" ; then + NPROC=$(sysctl -n hw.ncpu); +fi +if test "x${NPROC}" = "x"; then + NPROC=2; +fi +echo "NPROC=${NPROC}" + # Download grid files wget http://download.osgeo.org/proj/proj-datumgrid-1.8.zip @@ -27,7 +38,7 @@ if [ -f /usr/lib/jvm/java-7-openjdk-amd64/include/jni.h ]; then else ../configure --prefix=/tmp/proj_autoconf_install_from_dist_all fi -make -j2 +make -j${NPROC} make check make install find /tmp/proj_autoconf_install_from_dist_all @@ -37,7 +48,7 @@ cd .. mkdir build_cmake cd build_cmake cmake .. -DCMAKE_INSTALL_PREFIX=/tmp/proj_cmake_install -VERBOSE=1 make -j2 +VERBOSE=1 make -j${NPROC} make install # The cmake build is not able to generate the null file, so copy it at hand cp /tmp/proj_autoconf_install_from_dist_all/share/proj/null /tmp/proj_cmake_install/share/proj @@ -55,7 +66,7 @@ cd ../.. mkdir build_autoconf_grids cd build_autoconf_grids ../configure --prefix=/tmp/proj_autoconf_install_grids -make -j2 +make -j${NPROC} make check (cd src && make multistresstest && make test228) PROJ_LIB=../data src/multistresstest @@ -74,7 +85,7 @@ if [ "$BUILD_NAME" != "linux_clang" ]; then else ./configure fi -make -j2 +make -j${NPROC} make check # Rerun tests without grids not included in proj-datumgrid diff --git a/travis/osx/before_install.sh b/travis/osx/before_install.sh index 3e9d8d46..a5d40af5 100755 --- a/travis/osx/before_install.sh +++ b/travis/osx/before_install.sh @@ -10,6 +10,7 @@ brew install sqlite3 brew install doxygen export PATH=$HOME/Library/Python/2.7/bin:$PATH -pip install --user sphinx sphinx-rtd-theme sphinxcontrib-bibtex breathe +# breathe=4.12.0 is the last version to work for us with sphinx 1.8.5 / Python 2 +pip install --user sphinx sphinx-rtd-theme sphinxcontrib-bibtex breathe==4.12.0 which sphinx-build (cd docs; make html) |
