From 8ae7e0d2b6e853432ce988fea62527240c529871 Mon Sep 17 00:00:00 2001 From: Owen Rudge Date: Wed, 4 Dec 2019 16:34:11 +0000 Subject: Build: Only export symbols if building DLL --- src/geodesic.h | 2 +- src/lib_proj.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/geodesic.h b/src/geodesic.h index 5d230531..e2265c89 100644 --- a/src/geodesic.h +++ b/src/geodesic.h @@ -158,7 +158,7 @@ GEODESIC_VERSION_PATCH) #if !defined(GEOD_DLL) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && defined(PROJ_MSVC_DLL_EXPORT) #define GEOD_DLL __declspec(dllexport) #elif defined(__GNUC__) #define GEOD_DLL __attribute__ ((visibility("default"))) diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index c6a6e111..34c26ed3 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -443,7 +443,7 @@ endif() include_directories(${SQLITE3_INCLUDE_DIR}) target_link_libraries(${PROJ_CORE_TARGET} ${SQLITE3_LIBRARY}) -if(MSVC) +if(MSVC AND BUILD_LIBPROJ_SHARED) target_compile_definitions(${PROJ_CORE_TARGET} PRIVATE PROJ_MSVC_DLL_EXPORT=1) endif() -- cgit v1.2.3 From 5cc1095970127f8df5c4f636b1ce782829510fa0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 9 Dec 2019 09:08:13 +0100 Subject: CRS identification: use case insensitive comparison for authority name (fixes #1779) --- src/iso19111/factory.cpp | 14 +++++++++++--- src/iso19111/io.cpp | 5 +---- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 57850303..0ef07337 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -1433,9 +1433,17 @@ AuthorityFactory::AuthorityFactory(const DatabaseContextNNPtr &context, AuthorityFactoryNNPtr AuthorityFactory::create(const DatabaseContextNNPtr &context, const std::string &authorityName) { - - auto factory = AuthorityFactory::nn_make_shared( - context, authorityName); + const auto getFactory = [&context, &authorityName]() { + for (const auto &knownName : {"EPSG", "ESRI", "PROJ"}) { + if (ci_equal(authorityName, knownName)) { + return AuthorityFactory::nn_make_shared( + context, knownName); + } + } + return AuthorityFactory::nn_make_shared( + context, authorityName); + }; + auto factory = getFactory(); factory->d->setThis(factory); return factory; } diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 2aec5fac..e0e6152a 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -5669,10 +5669,7 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, DatabaseContextNNPtr dbContextNNPtr(NN_NO_CHECK(dbContext)); const auto &authName = tokens[0]; const auto &code = tokens[1]; - static const std::string epsg_lowercase("epsg"); - auto factory = AuthorityFactory::create( - dbContextNNPtr, - authName == epsg_lowercase ? Identifier::EPSG : authName); + auto factory = AuthorityFactory::create(dbContextNNPtr, authName); try { return factory->createCoordinateReferenceSystem(code); } catch (...) { -- cgit v1.2.3 From 2c3a7c8bcc711cf34b79885edbf1e5dccacc5757 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 10 Dec 2019 14:36:07 +0100 Subject: ob_tran: restore traditional handling of +to_meter with pj_transform() and proj utility (fixes #1782) Fixes side-effect of https://github.com/OSGeo/PROJ/issues/1525 that went in 6.1.1 The correction is horribly hacky. Sorry... --- src/apps/proj.cpp | 8 ++++++++ src/transform.cpp | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'src') diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp index 7fe08023..09c8a81d 100644 --- a/src/apps/proj.cpp +++ b/src/apps/proj.cpp @@ -490,6 +490,14 @@ int main(int argc, char **argv) { exit(0); } + // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 + if( Proj->right == PJ_IO_UNITS_WHATEVER && Proj->descr && + strncmp(Proj->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + Proj->right = PJ_IO_UNITS_PROJECTED; + } + if (inverse) { if (!Proj->inv) emess(3,"inverse projection not available"); diff --git a/src/transform.cpp b/src/transform.cpp index d111d835..781c0061 100644 --- a/src/transform.cpp +++ b/src/transform.cpp @@ -231,6 +231,14 @@ static int geographic_to_projected (PJ *P, long n, int dist, double *x, double * return 0; } + // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 + if( P->right == PJ_IO_UNITS_WHATEVER && P->descr && + strncmp(P->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + P->right = PJ_IO_UNITS_PROJECTED; + } + for( i = 0; i right == PJ_IO_UNITS_WHATEVER && P->descr && + strncmp(P->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + P->right = PJ_IO_UNITS_PROJECTED; + } + /* Fallback to the original PROJ.4 API 2d inversion - inv */ for( i = 0; i < n; i++ ) { PJ_XY projected_loc; -- cgit v1.2.3 From 38de84f38bceed4030710648334729d96f7c2204 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 10 Dec 2019 23:51:03 +0100 Subject: Database: update to IGNF v3.1.0 --- src/iso19111/coordinateoperation.cpp | 14 +++++++++++++- src/iso19111/factory.cpp | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 6120c768..791bdcb0 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -12572,6 +12572,18 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( // Start in priority with candidates that have exactly the same name as // the sourcCRS and targetCRS. Typically for the case of init=IGNF:XXXX + + // Transformation from IGNF:NTFP to IGNF:RGF93G, + // using + // NTF geographiques Paris (gr) vers NTF GEOGRAPHIQUES GREENWICH (DMS) + + // NOUVELLE TRIANGULATION DE LA FRANCE (NTF) vers RGF93 (ETRS89) + // that is using ntf_r93.gsb, is horribly dependent + // of IGNF:RGF93G being returned before IGNF:RGF93GEO in candidatesDstGeod. + // If RGF93GEO is returned before then we go through WGS84 and use + // instead a Helmert transformation. + // The below logic is thus quite fragile, and attempts at changing it + // result in degraded results for other use cases... + for (const auto &candidateSrcGeod : candidatesSrcGeod) { if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) { for (const auto &candidateDstGeod : candidatesDstGeod) { @@ -12625,7 +12637,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( #endif createTransformations(candidateSrcGeod, candidateDstGeod, opsFirst[0], isNullFirst); - if (!res.empty() && hasResultSetOnlyResultsWithPROJStep(res)) { + if (!res.empty() && !hasResultSetOnlyResultsWithPROJStep(res)) { return; } } diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 0ef07337..f0ad157e 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -5609,6 +5609,7 @@ std::list AuthorityFactory::createGeodeticCRSFromDatum( sql += " AND type = ?"; params.emplace_back(geodetic_crs_type); } + sql += " ORDER BY auth_name, code"; auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { -- cgit v1.2.3 From 0b5fb1239f93e93971e28c95df6b039156c65ffb Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 11 Dec 2019 11:46:34 +0100 Subject: createOperations(): make filtering out of 'uninteresting' operations less aggressive (refs #1787) 'EPSG:1304, Indian 1975 to WGS 84 (2), 5.0 m, Thailand - onshore and Gulf of Thailand' was removed because considered useless w.r.t first result 'EPSG:1812, Indian 1975 to WGS 84 (4), 3.0 m, Thailand - onshore' However the name of the area of use is not completely the same, so might be worth keeping it. --- src/iso19111/coordinateoperation.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 791bdcb0..0469cae1 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -11301,9 +11301,17 @@ struct FilterResults { setOfSetOfGrids.end()) { continue; } + + const bool sameNameOrEmptyName = + ((!curExtent && !lastExtent) || + (curExtent && lastExtent && + !curExtent->description()->empty() && + *(curExtent->description()) == + *(lastExtent->description()))); + // If we have already found a operation without grids for // that extent, no need to add any lower accuracy operation - if (!lastHasGrids) { + if (!lastHasGrids && sameNameOrEmptyName) { continue; } // If we had only operations involving grids, but one -- cgit v1.2.3 From 3a035e99f786b61837efcdf56a86748828e1f155 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 11 Dec 2019 00:13:57 +0100 Subject: Make EPSG:102100 resolve to ESRI:102100 (fixes #1730) --- src/iso19111/io.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index e0e6152a..a35c4cd6 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -5673,6 +5673,14 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, try { return factory->createCoordinateReferenceSystem(code); } catch (...) { + + // Convenience for well-known misused code + // See https://github.com/OSGeo/PROJ/issues/1730 + if (ci_equal(authName, "EPSG") && code == "102100") { + factory = AuthorityFactory::create(dbContextNNPtr, "ESRI"); + return factory->createCoordinateReferenceSystem(code); + } + const auto authorities = dbContextNNPtr->getAuthorities(); for (const auto &authCandidate : authorities) { if (ci_equal(authCandidate, authName)) { -- cgit v1.2.3 From c86bd69147a289ead837e5400776393bd9072a82 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Dec 2019 00:38:18 +0100 Subject: WKTParser: fix assertion that can trigger on corrupted input. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=19367 --- src/iso19111/io.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index e0e6152a..b57d1de5 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -2683,7 +2683,9 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) { auto cs = buildCS(csNode, node, angularUnit); auto ellipsoidalCS = nn_dynamic_pointer_cast(cs); if (ellipsoidalCS) { - assert(!ci_equal(nodeName, WKTConstants::GEOCCS)); + if (ci_equal(nodeName, WKTConstants::GEOCCS)) { + throw ParsingException("ellipsoidal CS not expected in GEOCCS"); + } try { auto crs = GeographicCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(ellipsoidalCS)); -- cgit v1.2.3 From 2fd6f7fc0b2db4f5f7d54271e48577f3db7564bf Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Dec 2019 01:29:42 +0100 Subject: PROJStringFormatter: make startInversion/stopInversion properly deal with omit_fwd/omit_inv --- src/iso19111/io.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index b57d1de5..c704e1c1 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -7156,6 +7156,12 @@ void PROJStringFormatter::stopInversion() { // the current end of steps for (auto iter = startIter; iter != d->steps_.end(); ++iter) { iter->inverted = !iter->inverted; + for (auto ¶mValue : iter->paramValues) { + if (paramValue.key == "omit_fwd") + paramValue.key = "omit_inv"; + else if (paramValue.key == "omit_inv") + paramValue.key = "omit_fwd"; + } } // And reverse the order of steps in that range as well. std::reverse(startIter, d->steps_.end()); -- cgit v1.2.3 From a2b2663d2a35c9ff6ff56e9348f166a99cc618a4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Dec 2019 11:04:00 +0100 Subject: normalizeForVisualization(): address EPSG:32661 "WGS 84 / UPS North (N,E)" and EPSG:32761 "WGS 84 / UPS South (N,E)" Fixes https://github.com/qgis/QGIS/issues/33077 --- src/iso19111/crs.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 49cc050f..ad4728b8 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -595,12 +595,44 @@ CRSNNPtr CRS::alterId(const std::string &authName, //! @cond Doxygen_Suppress -static bool isAxisListNorthEast( +static bool mustAxisOrderBeSwitchedForVisualizationInternal( const std::vector &axisList) { const auto &dir0 = axisList[0]->direction(); const auto &dir1 = axisList[1]->direction(); - return (&dir0 == &cs::AxisDirection::NORTH && - &dir1 == &cs::AxisDirection::EAST); + if (&dir0 == &cs::AxisDirection::NORTH && + &dir1 == &cs::AxisDirection::EAST) { + return true; + } + + // Address EPSG:32661 "WGS 84 / UPS North (N,E)" + if (&dir0 == &cs::AxisDirection::SOUTH && + &dir1 == &cs::AxisDirection::SOUTH) { + const auto &meridian0 = axisList[0]->meridian(); + const auto &meridian1 = axisList[1]->meridian(); + return meridian0 != nullptr && meridian1 != nullptr && + fabs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 180.0) < 1e-10 && + fabs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; + } + + // Address EPSG:32761 "WGS 84 / UPS South (N,E)" + if (&dir0 == &cs::AxisDirection::NORTH && + &dir1 == &cs::AxisDirection::NORTH) { + const auto &meridian0 = axisList[0]->meridian(); + const auto &meridian1 = axisList[1]->meridian(); + return meridian0 != nullptr && meridian1 != nullptr && + fabs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 0.0) < 1e-10 && + fabs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; + } + + return false; } // --------------------------------------------------------------------------- @@ -616,12 +648,14 @@ bool CRS::mustAxisOrderBeSwitchedForVisualization() const { const GeographicCRS *geogCRS = dynamic_cast(this); if (geogCRS) { - return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList()); + return mustAxisOrderBeSwitchedForVisualizationInternal( + geogCRS->coordinateSystem()->axisList()); } const ProjectedCRS *projCRS = dynamic_cast(this); if (projCRS) { - return isAxisListNorthEast(projCRS->coordinateSystem()->axisList()); + return mustAxisOrderBeSwitchedForVisualizationInternal( + projCRS->coordinateSystem()->axisList()); } return false; @@ -655,7 +689,7 @@ CRSNNPtr CRS::normalizeForVisualization() const { const GeographicCRS *geogCRS = dynamic_cast(this); if (geogCRS) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); - if (isAxisListNorthEast(axisList)) { + if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1], axisList[0]) @@ -670,7 +704,7 @@ CRSNNPtr CRS::normalizeForVisualization() const { const ProjectedCRS *projCRS = dynamic_cast(this); if (projCRS) { const auto &axisList = projCRS->coordinateSystem()->axisList(); - if (isAxisListNorthEast(axisList)) { + if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::CartesianCS::create(util::PropertyMap(), axisList[1], -- cgit v1.2.3 From c9d6d364a86054f1dddbeb772660375be47aa23b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Dec 2019 11:15:03 +0100 Subject: Fix compilation breakage of previous commit with some compilers --- src/iso19111/crs.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index ad4728b8..46aeee20 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -610,12 +611,12 @@ static bool mustAxisOrderBeSwitchedForVisualizationInternal( const auto &meridian0 = axisList[0]->meridian(); const auto &meridian1 = axisList[1]->meridian(); return meridian0 != nullptr && meridian1 != nullptr && - fabs(meridian0->longitude().convertToUnit( - common::UnitOfMeasure::DEGREE) - - 180.0) < 1e-10 && - fabs(meridian1->longitude().convertToUnit( - common::UnitOfMeasure::DEGREE) - - 90.0) < 1e-10; + std::abs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 180.0) < 1e-10 && + std::abs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; } // Address EPSG:32761 "WGS 84 / UPS South (N,E)" @@ -624,12 +625,12 @@ static bool mustAxisOrderBeSwitchedForVisualizationInternal( const auto &meridian0 = axisList[0]->meridian(); const auto &meridian1 = axisList[1]->meridian(); return meridian0 != nullptr && meridian1 != nullptr && - fabs(meridian0->longitude().convertToUnit( - common::UnitOfMeasure::DEGREE) - - 0.0) < 1e-10 && - fabs(meridian1->longitude().convertToUnit( - common::UnitOfMeasure::DEGREE) - - 90.0) < 1e-10; + std::abs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 0.0) < 1e-10 && + std::abs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; } return false; -- cgit v1.2.3 From ccaebae3cb9424a9b21d0c58237d7e95c9e16b1b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 14 Dec 2019 10:15:05 +1000 Subject: Add proj_coordoperation_create_inverse to C API (#1795) --- src/iso19111/c_api.cpp | 28 ++++++++++++++++++++++++++++ src/proj.h | 3 +++ 2 files changed, 31 insertions(+) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 9db9e5b3..5e2ac522 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -7769,6 +7769,34 @@ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) { // --------------------------------------------------------------------------- +/** \brief Returns a PJ* coordinate operation object which represents the + * inverse operation of the specified coordinate operation. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object of type CoordinateOperation (must not be NULL) + * @return a new PJ* object to free with proj_destroy() in case of success, or + * nullptr in case of error + * @since 6.3 + */ +PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) { + + SANITIZE_CTX(ctx); + auto co = dynamic_cast(obj->iso_obj.get()); + if (!co) { + proj_log_error(ctx, __FUNCTION__, + "Object is not a CoordinateOperation"); + return nullptr; + } + try { + return pj_obj_create(ctx, co->inverse()); + } catch (const std::exception &e) { + proj_log_debug(ctx, __FUNCTION__, e.what()); + return nullptr; + } +} + +// --------------------------------------------------------------------------- + /** \brief Returns the number of steps of a concatenated operation. * * The input object must be a concatenated operation. diff --git a/src/proj.h b/src/proj.h index fc309542..b8b90277 100644 --- a/src/proj.h +++ b/src/proj.h @@ -1091,6 +1091,9 @@ int PROJ_DLL proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx, int value_count, int emit_error_if_incompatible); +PJ PROJ_DLL *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj); + + int PROJ_DLL proj_concatoperation_get_step_count(PJ_CONTEXT *ctx, const PJ *concatoperation); -- cgit v1.2.3 From 6a4f9cc673e247e1ca76e94d7b1aaaf3e0648c13 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 14 Dec 2019 15:40:17 +0100 Subject: Horizontal grid shift: fix issue on iterative inverse computation when switching between (sub)grids (fixes #1663) Given in.txt with 53.999759140 5.144478208 252.6995 Before the fix, cct -t 0 -d 4 +proj=pipeline +step +proj=axisswap +order=2,1,3,4 +step +proj=hgridshift +inv +grids=rdtrans2018.gsb +step +proj=vgridshift +grids=naptrans2018.gtx +step +proj=sterea +lat_0=52.156160556 +lon_0=5.387638889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel in.txt returned: 139079.8814 668306.0302 212.1724 0.0000 It now returns: 139079.8850 668306.0458 212.1724 0.0000 which meets with the 1mm accuracy the expected result of test point ``` 30010049 53.999759140 5.144478208 252.6995 139079.8850 668306.0460 212.1723 ``` --- src/apply_gridshift.cpp | 6 +++--- src/gridcatalog.cpp | 4 ++-- src/nad_cvt.cpp | 43 ++++++++++++++++++++++++++++++++----------- src/proj_internal.h | 4 +++- 4 files changed, 40 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/apply_gridshift.cpp b/src/apply_gridshift.cpp index e6c7b2b1..39e7c3b5 100644 --- a/src/apply_gridshift.cpp +++ b/src/apply_gridshift.cpp @@ -115,7 +115,7 @@ int pj_apply_gridshift_2( PJ *defn, int inverse, /* Determine which grid is the correct given an input coordinate. */ /************************************************************************/ -static struct CTABLE* find_ctable(projCtx ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables) { +struct CTABLE* find_ctable(projCtx ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables) { int itable; /* keep trying till we find a table that works */ @@ -210,7 +210,7 @@ int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_coun ct = find_ctable(ctx, input, gridlist_count, gridlist); if( ct != nullptr ) { - output = nad_cvt( input, inverse, ct ); + output = nad_cvt( ctx, input, inverse, ct, gridlist_count, gridlist); if ( output.lam != HUGE_VAL && debug_count++ < 20 ) pj_log( ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id ); @@ -356,7 +356,7 @@ PJ_LP proj_hgrid_apply(PJ *P, PJ_LP lp, PJ_DIRECTION direction) { } inverse = direction == PJ_FWD ? 0 : 1; - out = nad_cvt(lp, inverse, ct); + out = nad_cvt(P->ctx, lp, inverse, ct, P->gridlist_count, P->gridlist); if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); diff --git a/src/gridcatalog.cpp b/src/gridcatalog.cpp index 15d81dd7..9b94fef8 100644 --- a/src/gridcatalog.cpp +++ b/src/gridcatalog.cpp @@ -164,7 +164,7 @@ int pj_gc_apply_gridshift( PJ *defn, int inverse, return PJD_ERR_FAILED_TO_LOAD_GRID; } - output_after = nad_cvt( input, inverse, gi->ct ); + output_after = nad_cvt( defn->ctx, input, inverse, gi->ct, 0, nullptr ); if( output_after.lam == HUGE_VAL ) { if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) @@ -213,7 +213,7 @@ int pj_gc_apply_gridshift( PJ *defn, int inverse, return PJD_ERR_FAILED_TO_LOAD_GRID; } - output_before = nad_cvt( input, inverse, gi->ct ); + output_before = nad_cvt( defn->ctx, input, inverse, gi->ct, 0, nullptr ); if( output_before.lam == HUGE_VAL ) { if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) diff --git a/src/nad_cvt.cpp b/src/nad_cvt.cpp index 79441d0a..e8b8e9b7 100644 --- a/src/nad_cvt.cpp +++ b/src/nad_cvt.cpp @@ -7,10 +7,12 @@ #include "proj_internal.h" #include +#include + #define MAX_ITERATIONS 10 #define TOL 1e-12 -PJ_LP nad_cvt(PJ_LP in, int inverse, struct CTABLE *ct) { +PJ_LP nad_cvt(projCtx ctx, PJ_LP in, int inverse, struct CTABLE *ct, int grid_count, PJ_GRIDINFO **tables) { PJ_LP t, tb,del, dif; int i = MAX_ITERATIONS; const double toltol = TOL*TOL; @@ -40,18 +42,37 @@ PJ_LP nad_cvt(PJ_LP in, int inverse, struct CTABLE *ct) { do { del = nad_intr(t, ct); - /* This case used to return failure, but I have - changed it to return the first order approximation - of the inverse shift. This avoids cases where the - grid shift *into* this grid came from another grid. - While we aren't returning optimally correct results - I feel a close result in this case is better than - no result. NFW - To demonstrate use -112.5839956 49.4914451 against - the NTv2 grid shift file from Canada. */ - if (del.lam == HUGE_VAL) + /* In case we are (or have switched) on the null grid, exit now */ + if( del.lam == 0 && del.phi == 0 ) break; + /* We can possibly go outside of the initial guessed grid, so try */ + /* to fetch a new grid into which iterate... */ + if (del.lam == HUGE_VAL) + { + if( grid_count == 0 ) + break; + PJ_LP lp; + lp.lam = t.lam + ct->ll.lam; + lp.phi = t.phi + ct->ll.phi; + auto newCt = find_ctable(ctx, lp, grid_count, tables); + if( newCt == nullptr || newCt == ct ) + break; + pj_log(ctx, PJ_LOG_DEBUG_MINOR, "Switching from grid %s to grid %s", + ct->id, + newCt->id); + ct = newCt; + t.lam = lp.lam - ct->ll.lam; + t.phi = lp.phi - ct->ll.phi; + tb = in; + tb.lam -= ct->ll.lam; + tb.phi -= ct->ll.phi; + tb.lam = adjlon (tb.lam - M_PI) + M_PI; + dif.lam = std::numeric_limits::max(); + dif.phi = std::numeric_limits::max(); + continue; + } + dif.lam = t.lam - del.lam - tb.lam; dif.phi = t.phi + del.phi - tb.phi; t.lam -= dif.lam; diff --git a/src/proj_internal.h b/src/proj_internal.h index 3ca927a3..3e219682 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -863,8 +863,10 @@ int pj_deriv(PJ_LP, double, const PJ *, struct DERIVS *); int pj_factors(PJ_LP, const PJ *, double, struct FACTORS *); /* nadcon related protos */ +struct CTABLE* find_ctable(projCtx_t *ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables); + PJ_LP nad_intr(PJ_LP, struct CTABLE *); -PJ_LP nad_cvt(PJ_LP, int, struct CTABLE *); +PJ_LP nad_cvt(projCtx_t *ctx, PJ_LP in, int inverse, struct CTABLE *ct, int grid_count, PJ_GRIDINFO **tables); struct CTABLE *nad_init(projCtx_t *ctx, char *); struct CTABLE *nad_ctable_init( projCtx_t *ctx, struct projFileAPI_t* fid ); int nad_ctable_load( projCtx_t *ctx, struct CTABLE *, struct projFileAPI_t* fid ); -- cgit v1.2.3 From a68c146d7f3c1efb0f42b46c708a0a195e51a2ff Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 16 Dec 2019 13:24:11 +0100 Subject: BoundCRS::identify(): improvements to discard CRS that aren't relevant (fixes #1801) Fix for ``` projinfo --identify "+proj=utm +zone=48 +a=6377276.345 +b=6356075.41314024 +towgs84=198,881,317,0,0,0,0 +units=m +no_defs +type=crs" ``` to only return BoundCRS of EPSG:3148: 70 % Previously it also returned EPSG:23948 and EPSG:24048 whose projected CRS-only parts where likely matches, but those 2 CRSs don't have a +towgs84=198,881,317,0,0,0,0, so discard them. --- src/iso19111/coordinateoperation.cpp | 4 +- src/iso19111/crs.cpp | 72 +++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 0469cae1..2c3e38ac 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -10189,7 +10189,9 @@ bool ConcatenatedOperation::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion) const { auto otherCO = dynamic_cast(other); - if (otherCO == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion)) { + if (otherCO == nullptr || + (criterion == util::IComparable::Criterion::STRICT && + !ObjectUsage::_isEquivalentTo(other, criterion))) { return false; } const auto &steps = operations(); diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 46aeee20..f231d072 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -4553,10 +4553,32 @@ std::list> BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; + std::list resMatchOfTransfToWGS84; if (authorityFactory && d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), util::IComparable::Criterion::EQUIVALENT)) { auto resTemp = d->baseCRS_->identify(authorityFactory); + + std::string refTransfPROJString; + bool refTransfPROJStringValid = false; + auto refTransf = d->transformation_->normalizeForVisualization(); + try { + refTransfPROJString = refTransf->exportToPROJString( + io::PROJStringFormatter::create().get()); + refTransfPROJString = replaceAll( + refTransfPROJString, + " +rx=0 +ry=0 +rz=0 +s=0 +convention=position_vector", ""); + refTransfPROJStringValid = true; + } catch (const std::exception &) { + } + bool refIsNullTransform = false; + if (isTOWGS84Compatible()) { + auto params = transformation()->getTOWGS84Parameters(); + if (params == std::vector{0, 0, 0, 0, 0, 0, 0}) { + refIsNullTransform = true; + } + } + for (const auto &pair : resTemp) { const auto &candidateBaseCRS = pair.first; auto projCRS = @@ -4567,54 +4589,46 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (geodCRS) { auto context = operation::CoordinateOperationContext::create( authorityFactory, nullptr, 0.0); + context->setSpatialCriterion( + operation::CoordinateOperationContext::SpatialCriterion:: + PARTIAL_INTERSECTION); auto ops = operation::CoordinateOperationFactory::create() ->createOperations(NN_NO_CHECK(geodCRS), GeographicCRS::EPSG_4326, context); - std::string refTransfPROJString; - bool refTransfPROJStringValid = false; - try { - refTransfPROJString = - d->transformation_->exportToPROJString( - io::PROJStringFormatter::create().get()); - refTransfPROJStringValid = true; - if (refTransfPROJString == "+proj=axisswap +order=2,1") { - refTransfPROJString.clear(); - } - } catch (const std::exception &) { - } + bool foundOp = false; for (const auto &op : ops) { + auto opNormalized = op->normalizeForVisualization(); std::string opTransfPROJString; bool opTransfPROJStringValid = false; if (op->nameStr().find("Ballpark geographic") == 0) { - if (isTOWGS84Compatible()) { - auto params = - transformation()->getTOWGS84Parameters(); - if (params == - std::vector{0, 0, 0, 0, 0, 0, 0}) { - res.emplace_back(create(candidateBaseCRS, - d->hubCRS_, - transformation()), - pair.second); - foundOp = true; - break; - } + if (refIsNullTransform) { + res.emplace_back(create(candidateBaseCRS, + d->hubCRS_, + transformation()), + pair.second); + foundOp = true; + break; } continue; } try { - opTransfPROJString = op->exportToPROJString( + opTransfPROJString = opNormalized->exportToPROJString( io::PROJStringFormatter::create().get()); opTransfPROJStringValid = true; + opTransfPROJString = replaceAll( + opTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 " + "+convention=position_vector", + ""); } catch (const std::exception &) { } if ((refTransfPROJStringValid && opTransfPROJStringValid && refTransfPROJString == opTransfPROJString) || - op->_isEquivalentTo( - d->transformation_.get(), + opNormalized->_isEquivalentTo( + refTransf.get(), util::IComparable::Criterion::EQUIVALENT)) { - res.emplace_back( + resMatchOfTransfToWGS84.emplace_back( create(candidateBaseCRS, d->hubCRS_, NN_NO_CHECK(util::nn_dynamic_pointer_cast< operation::Transformation>(op))), @@ -4631,7 +4645,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { } } } - return res; + return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res; } // --------------------------------------------------------------------------- -- cgit v1.2.3 From bd9ecbacf575a1926fb8e223c4add1a4cc45d7f3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 16 Dec 2019 15:23:22 +0100 Subject: identify(): take into datum name aliases (fixes #1800) --- src/iso19111/c_api.cpp | 57 ++++++-- src/iso19111/common.cpp | 33 +++-- src/iso19111/coordinateoperation.cpp | 91 ++++++------ src/iso19111/coordinatesystem.cpp | 15 +- src/iso19111/crs.cpp | 259 ++++++++++++++++++++--------------- src/iso19111/datum.cpp | 115 +++++++++++----- src/iso19111/factory.cpp | 62 ++++++++- src/iso19111/metadata.cpp | 21 ++- src/iso19111/util.cpp | 8 +- src/proj.h | 4 + 10 files changed, 439 insertions(+), 226 deletions(-) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 5e2ac522..5a5e97f6 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -1142,15 +1142,9 @@ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) { // --------------------------------------------------------------------------- -/** \brief Return whether two objects are equivalent. - * - * @param obj Object (must not be NULL) - * @param other Other object (must not be NULL) - * @param criterion Comparison criterion - * @return TRUE if they are equivalent - */ -int proj_is_equivalent_to(const PJ *obj, const PJ *other, - PJ_COMPARISON_CRITERION criterion) { +static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj, + const PJ *other, + PJ_COMPARISON_CRITERION criterion) { assert(obj); assert(other); if (!obj->iso_obj) { @@ -1172,7 +1166,50 @@ int proj_is_equivalent_to(const PJ *obj, const PJ *other, return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS; })(criterion); - return obj->iso_obj->isEquivalentTo(other->iso_obj.get(), cppCriterion); + int res = obj->iso_obj->isEquivalentTo( + other->iso_obj.get(), cppCriterion, + ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx") + : nullptr); + if (ctx && ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } + return res; +} + +// --------------------------------------------------------------------------- + +/** \brief Return whether two objects are equivalent. + * + * Use proj_is_equivalent_to_with_ctx() to be able to use database information. + * + * @param obj Object (must not be NULL) + * @param other Other object (must not be NULL) + * @param criterion Comparison criterion + * @return TRUE if they are equivalent + */ +int proj_is_equivalent_to(const PJ *obj, const PJ *other, + PJ_COMPARISON_CRITERION criterion) { + return proj_is_equivalent_to_internal(nullptr, obj, other, criterion); +} + +// --------------------------------------------------------------------------- + +/** \brief Return whether two objects are equivalent + * + * Possibly using database to check for name aliases. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object (must not be NULL) + * @param other Other object (must not be NULL) + * @param criterion Comparison criterion + * @return TRUE if they are equivalent + * @since 6.3 + */ +int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj, + const PJ *other, + PJ_COMPARISON_CRITERION criterion) { + SANITIZE_CTX(ctx); + return proj_is_equivalent_to_internal(ctx, obj, other, criterion); } // --------------------------------------------------------------------------- diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp index 97900bda..f2e4de4c 100644 --- a/src/iso19111/common.cpp +++ b/src/iso19111/common.cpp @@ -901,19 +901,19 @@ void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const { // --------------------------------------------------------------------------- bool IdentifiedObject::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherIdObj = dynamic_cast(other); if (!otherIdObj) return false; - return _isEquivalentTo(otherIdObj, criterion); + return _isEquivalentTo(otherIdObj, criterion, dbContext); } // --------------------------------------------------------------------------- -bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj, - util::IComparable::Criterion criterion) - PROJ_PURE_DEFN { +bool IdentifiedObject::_isEquivalentTo( + const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN { if (criterion == util::IComparable::Criterion::STRICT) { if (!ci_equal(nameStr(), otherIdObj->nameStr())) { return false; @@ -922,12 +922,19 @@ bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj, } else { if (!metadata::Identifier::isEquivalentName( nameStr().c_str(), otherIdObj->nameStr().c_str())) { - return false; + return hasEquivalentNameToUsingAlias(otherIdObj, dbContext); } } return true; } +// --------------------------------------------------------------------------- + +bool IdentifiedObject::hasEquivalentNameToUsingAlias( + const IdentifiedObject *, const io::DatabaseContextPtr &) const { + return false; +} + //! @endcond // --------------------------------------------------------------------------- @@ -1092,8 +1099,8 @@ void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const { //! @cond Doxygen_Suppress bool ObjectDomain::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDomain = dynamic_cast(other); if (!otherDomain) return false; @@ -1106,7 +1113,7 @@ bool ObjectDomain::_isEquivalentTo( return false; return domainOfValidity().get() == nullptr || domainOfValidity()->_isEquivalentTo( - otherDomain->domainOfValidity().get(), criterion); + otherDomain->domainOfValidity().get(), criterion, dbContext); } //! @endcond @@ -1249,14 +1256,14 @@ void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const { //! @cond Doxygen_Suppress bool ObjectUsage::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherObjUsage = dynamic_cast(other); if (!otherObjUsage) return false; // TODO: incomplete - return IdentifiedObject::_isEquivalentTo(other, criterion); + return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } //! @endcond diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 2c3e38ac..62d72e65 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -1068,11 +1068,11 @@ void OperationMethod::_exportToJSON( //! @cond Doxygen_Suppress bool OperationMethod::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOM = dynamic_cast(other); if (otherOM == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // TODO test formula and formulaCitation @@ -1084,7 +1084,8 @@ bool OperationMethod::_isEquivalentTo( } if (criterion == util::IComparable::Criterion::STRICT) { for (size_t i = 0; i < paramsSize; i++) { - if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion)) { + if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion, + dbContext)) { return false; } } @@ -1094,8 +1095,8 @@ bool OperationMethod::_isEquivalentTo( bool found = false; for (size_t j = 0; j < paramsSize; j++) { if (candidateIndices[j] && - params[i]->_isEquivalentTo(otherParams[j].get(), - criterion)) { + params[i]->_isEquivalentTo(otherParams[j].get(), criterion, + dbContext)) { candidateIndices[j] = false; found = true; break; @@ -1341,14 +1342,14 @@ bool OperationParameterValue::convertFromAbridged( //! @cond Doxygen_Suppress bool OperationParameterValue::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOPV = dynamic_cast(other); if (otherOPV == nullptr) { return false; } - if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), - criterion)) { + if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), criterion, + dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { @@ -1356,7 +1357,7 @@ bool OperationParameterValue::_isEquivalentTo( otherOPV->d->parameterValue.get(), criterion); } if (d->parameterValue->_isEquivalentTo(otherOPV->d->parameterValue.get(), - criterion)) { + criterion, dbContext)) { return true; } if (d->parameter->getEPSGCode() == @@ -1446,16 +1447,16 @@ OperationParameter::create(const util::PropertyMap &properties) { //! @cond Doxygen_Suppress bool OperationParameter::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOP = dynamic_cast(other); if (otherOP == nullptr) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { - return IdentifiedObject::_isEquivalentTo(other, criterion); + return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } - if (IdentifiedObject::_isEquivalentTo(other, criterion)) { + if (IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return true; } auto l_epsgCode = getEPSGCode(); @@ -1759,19 +1760,20 @@ static SingleOperationNNPtr createPROJBased( //! @cond Doxygen_Suppress bool SingleOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { - return _isEquivalentTo(other, criterion, false); + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { + return _isEquivalentTo(other, criterion, dbContext, false); } bool SingleOperation::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext, bool inOtherDirection) const { auto otherSO = dynamic_cast(other); if (otherSO == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } @@ -1781,7 +1783,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, bool equivalentMethods = (criterion == util::IComparable::Criterion::EQUIVALENT && methodEPSGCode != 0 && methodEPSGCode == otherMethodEPSGCode) || - d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion); + d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion, + dbContext); if (!equivalentMethods && criterion == util::IComparable::Criterion::EQUIVALENT) { if ((methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA && @@ -1854,7 +1857,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { // Convert from 2SP to 1SP as the other direction has more // degree of liberties. - return otherSO->_isEquivalentTo(this, criterion); + return otherSO->_isEquivalentTo(this, criterion, dbContext); } else if ((methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A && otherMethodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B) || @@ -1870,7 +1873,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, auto eqConv = conv->convertToOtherMethod(otherMethodEPSGCode); if (eqConv) { - return eqConv->_isEquivalentTo(other, criterion); + return eqConv->_isEquivalentTo(other, criterion, + dbContext); } } } @@ -1888,7 +1892,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, return false; } for (size_t i = 0; i < valuesSize; i++) { - if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion)) { + if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion, + dbContext)) { return false; } } @@ -1909,7 +1914,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, bool sameNameDifferentValue = false; for (size_t j = 0; j < otherValuesSize; j++) { if (candidateIndices[j] && - values[i]->_isEquivalentTo(otherValues[j].get(), criterion)) { + values[i]->_isEquivalentTo(otherValues[j].get(), criterion, + dbContext)) { candidateIndices[j] = false; equivalent = true; break; @@ -1921,7 +1927,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, return false; sameNameDifferentValue = opParamvalue->parameter()->_isEquivalentTo( - otherOpParamvalue->parameter().get(), criterion); + otherOpParamvalue->parameter().get(), criterion, + dbContext); if (sameNameDifferentValue) { candidateIndices[j] = false; break; @@ -1949,13 +1956,13 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) .get(), - criterion) && + criterion, dbContext) && value_2nd->_isEquivalentTo( otherSO ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL) .get(), - criterion); + criterion, dbContext); } } } @@ -1987,7 +1994,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, // In the case the arguments don't perfectly match, try the reverse // check. if (equivalent && foundMissingArgs && !inOtherDirection) { - return otherSO->_isEquivalentTo(this, criterion, true); + return otherSO->_isEquivalentTo(this, criterion, dbContext, true); } // Equivalent formulations of 2SP can have different parameters @@ -2002,8 +2009,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, auto otherAs1SP = otherConv->convertToOtherMethod( EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); if (thisAs1SP && otherAs1SP) { - equivalent = - thisAs1SP->_isEquivalentTo(otherAs1SP.get(), criterion); + equivalent = thisAs1SP->_isEquivalentTo(otherAs1SP.get(), + criterion, dbContext); } } } @@ -2412,9 +2419,9 @@ void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -bool ParameterValue::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { +bool ParameterValue::_isEquivalentTo(const util::IComparable *other, + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &) const { auto otherPV = dynamic_cast(other); if (otherPV == nullptr) { return false; @@ -10186,12 +10193,12 @@ void ConcatenatedOperation::_exportToPROJString( //! @cond Doxygen_Suppress bool ConcatenatedOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCO = dynamic_cast(other); if (otherCO == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &steps = operations(); @@ -10200,7 +10207,8 @@ bool ConcatenatedOperation::_isEquivalentTo( return false; } for (size_t i = 0; i < steps.size(); i++) { - if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion)) { + if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion, + dbContext)) { return false; } } @@ -14841,14 +14849,15 @@ void InverseCoordinateOperation::_exportToPROJString( // --------------------------------------------------------------------------- bool InverseCoordinateOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherICO = dynamic_cast(other); if (otherICO == nullptr || - !ObjectUsage::_isEquivalentTo(other, criterion)) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } - return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion); + return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion, + dbContext); } // --------------------------------------------------------------------------- diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp index 6769b486..163f0f43 100644 --- a/src/iso19111/coordinatesystem.cpp +++ b/src/iso19111/coordinatesystem.cpp @@ -429,8 +429,8 @@ void CoordinateSystemAxis::_exportToJSON( //! @cond Doxygen_Suppress bool CoordinateSystemAxis::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCSA = dynamic_cast(other); if (otherCSA == nullptr) { return false; @@ -441,7 +441,7 @@ bool CoordinateSystemAxis::_isEquivalentTo( return false; } if (criterion == util::IComparable::Criterion::STRICT) { - if (!IdentifiedObject::_isEquivalentTo(other, criterion)) { + if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (abbreviation() != otherCSA->abbreviation()) { @@ -599,11 +599,11 @@ void CoordinateSystem::_exportToJSON( //! @cond Doxygen_Suppress bool CoordinateSystem::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCS = dynamic_cast(other); if (otherCS == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } const auto &list = axisList(); @@ -615,7 +615,8 @@ bool CoordinateSystem::_isEquivalentTo( return false; } for (size_t i = 0; i < list.size(); i++) { - if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion)) { + if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion, + dbContext)) { return false; } } diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index f231d072..cf90c388 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -391,7 +391,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( if (boundCRS) { if (boundCRS->hubCRS()->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { return NN_NO_CHECK(boundCRS); } } @@ -400,16 +400,16 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( auto geogCRS = extractGeographicCRS(); auto hubCRS = util::nn_static_pointer_cast(GeographicCRS::EPSG_4326); if (geodCRS && !geogCRS) { - if (geodCRS->_isEquivalentTo( - GeographicCRS::EPSG_4978.get(), - util::IComparable::Criterion::EQUIVALENT)) { + if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(), + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { return thisAsCRS; } hubCRS = util::nn_static_pointer_cast(GeodeticCRS::EPSG_4978); } else if (!geogCRS || geogCRS->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { return thisAsCRS; } else { geodCRS = geogCRS; @@ -1022,18 +1022,19 @@ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- bool SingleCRS::baseIsEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherSingleCRS = dynamic_cast(other); if (otherSingleCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &thisDatum = d->datum; const auto &otherDatum = otherSingleCRS->d->datum; if (thisDatum) { - if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion)) { + if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion, + dbContext)) { return false; } } else { @@ -1044,7 +1045,8 @@ bool SingleCRS::baseIsEquivalentTo( // TODO test DatumEnsemble return d->coordinateSystem->_isEquivalentTo( - otherSingleCRS->d->coordinateSystem.get(), criterion) && + otherSingleCRS->d->coordinateSystem.get(), criterion, + dbContext) && getExtensionProj4() == otherSingleCRS->getExtensionProj4(); } @@ -1579,13 +1581,13 @@ getStandardCriterion(util::IComparable::Criterion criterion) { //! @cond Doxygen_Suppress bool GeodeticCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { const auto standardCriterion = getStandardCriterion(criterion); auto otherGeodCRS = dynamic_cast(other); // TODO test velocityModel return otherGeodCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, standardCriterion); + SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext); } //! @endcond @@ -1686,6 +1688,9 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { std::list res; const auto &thisName(nameStr()); + io::DatabaseContextPtr dbContext = + authorityFactory ? authorityFactory->databaseContext().as_nullable() + : nullptr; const bool l_implicitCS = CRS::getPrivate()->implicitCS_; const auto crsCriterion = l_implicitCS @@ -1699,7 +1704,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const bool nameEquivalent = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); const bool nameEqual = thisName == crs->nameStr(); - const bool isEq = _isEquivalentTo(crs.get(), crsCriterion); + const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext); if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) { res.emplace_back(util::nn_static_pointer_cast(crs), nameEqual ? 100 : 90); @@ -1734,13 +1739,14 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisDatum(datum()); auto searchByDatum = [this, &authorityFactory, &res, &thisDatum, - &geodetic_crs_type, crsCriterion]() { + &geodetic_crs_type, crsCriterion, &dbContext]() { for (const auto &id : thisDatum->identifiers()) { try { auto tempRes = authorityFactory->createGeodeticCRSFromDatum( *id->codeSpace(), id->code(), geodetic_crs_type); for (const auto &crs : tempRes) { - if (_isEquivalentTo(crs.get(), crsCriterion)) { + if (_isEquivalentTo(crs.get(), crsCriterion, + dbContext)) { res.emplace_back(crs, 70); } } @@ -1752,7 +1758,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisEllipsoid(ellipsoid()); auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum, &thisEllipsoid, &geodetic_crs_type, - l_implicitCS]() { + l_implicitCS, &dbContext]() { const auto ellipsoids = thisEllipsoid->identifiers().empty() ? authorityFactory->createEllipsoidFromExisting( @@ -1770,15 +1776,17 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (crsDatum && crsDatum->ellipsoid()->_isEquivalentTo( ellps.get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && crsDatum->primeMeridian()->_isEquivalentTo( thisDatum->primeMeridian().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && (!l_implicitCS || coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), - util::IComparable::Criterion:: - EQUIVALENT))) { + util::IComparable::Criterion::EQUIVALENT, + dbContext))) { res.emplace_back(crs, 60); } } @@ -1811,7 +1819,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { authorityFactory->databaseContext(), *id->codeSpace()) ->createGeodeticCRS(id->code()); - bool match = _isEquivalentTo(crs.get(), crsCriterion); + bool match = + _isEquivalentTo(crs.get(), crsCriterion, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -1829,7 +1838,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { auto crs = util::nn_dynamic_pointer_cast(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); - if (_isEquivalentTo(crs.get(), crsCriterion)) { + if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -1859,8 +1868,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisCS(coordinateSystem()); // Sort results - res.sort([&thisName, &thisDatum, &thisCS](const Pair &a, - const Pair &b) { + res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a, + const Pair &b) { // First consider confidence if (a.second > b.second) { return true; @@ -1884,9 +1893,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &bDatum(b.first->datum()); if (thisDatum && aDatum && bDatum) { const auto thisEquivADatum(thisDatum->_isEquivalentTo( - aDatum.get(), util::IComparable::Criterion::EQUIVALENT)); + aDatum.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); const auto thisEquivBDatum(thisDatum->_isEquivalentTo( - bDatum.get(), util::IComparable::Criterion::EQUIVALENT)); + bDatum.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); if (thisEquivADatum && !thisEquivBDatum) { return true; @@ -1900,9 +1911,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &aCS(a.first->coordinateSystem()); const auto &bCS(b.first->coordinateSystem()); const auto thisEquivACs(thisCS->_isEquivalentTo( - aCS.get(), util::IComparable::Criterion::EQUIVALENT)); + aCS.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); const auto thisEquivBCs(thisCS->_isEquivalentTo( - bCS.get(), util::IComparable::Criterion::EQUIVALENT)); + bCS.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); if (thisEquivACs && !thisEquivBCs) { return true; } @@ -2119,14 +2132,14 @@ bool GeographicCRS::is2DPartOf3D(util::nn other) //! @cond Doxygen_Suppress bool GeographicCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherGeogCRS = dynamic_cast(other); if (otherGeogCRS == nullptr) { return false; } const auto standardCriterion = getStandardCriterion(criterion); - if (GeodeticCRS::_isEquivalentTo(other, standardCriterion)) { + if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) { return true; } if (criterion != @@ -2145,7 +2158,7 @@ bool GeographicCRS::_isEquivalentTo( cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH ? cs::EllipsoidalCS::createLatitudeLongitude(unit) : cs::EllipsoidalCS::createLongitudeLatitude(unit)) - ->GeodeticCRS::_isEquivalentTo(other, standardCriterion); + ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext); } return false; } @@ -2717,12 +2730,12 @@ VerticalCRS::create(const util::PropertyMap &properties, //! @cond Doxygen_Suppress bool VerticalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherVertCRS = dynamic_cast(other); // TODO test geoidModel and velocityModel return otherVertCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } //! @endcond @@ -2757,6 +2770,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisName(nameStr()); if (authorityFactory) { + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || @@ -2768,12 +2783,11 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( - authorityFactory->databaseContext(), - *id->codeSpace()) + dbContext, *id->codeSpace()) ->createVerticalCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT); + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -2791,8 +2805,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { assert(crs); auto crsNN = NN_NO_CHECK(crs); if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -2957,19 +2971,20 @@ DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- -bool DerivedCRS::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { +bool DerivedCRS::_isEquivalentTo( + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); const auto standardCriterion = getStandardCriterion(criterion); if (otherDerivedCRS == nullptr || - !SingleCRS::baseIsEquivalentTo(other, standardCriterion)) { + !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) { return false; } return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(), - criterion) && + criterion, dbContext) && d->derivingConversion_->_isEquivalentTo( - otherDerivedCRS->d->derivingConversion_.get(), - standardCriterion); + otherDerivedCRS->d->derivingConversion_.get(), standardCriterion, + dbContext); } // --------------------------------------------------------------------------- @@ -3402,11 +3417,11 @@ ProjectedCRS::create(const util::PropertyMap &properties, // --------------------------------------------------------------------------- bool ProjectedCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherProjCRS = dynamic_cast(other); return otherProjCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -3580,14 +3595,19 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &conv = derivingConversionRef(); const auto &cs = coordinateSystem(); + io::DatabaseContextPtr dbContext = + authorityFactory ? authorityFactory->databaseContext().as_nullable() + : nullptr; + if (baseRes.size() == 1 && baseRes.front().second >= 70 && conv->isUTM(zone, north) && cs->_isEquivalentTo( cs::CartesianCS::createEastingNorthing(common::UnitOfMeasure::METRE) - .get())) { + .get(), + util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("WGS 84 / UTM zone ", zone, north)); res.emplace_back( @@ -3601,7 +3621,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4267.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD27 / UTM zone ", zone, north)); res.emplace_back( @@ -3616,7 +3636,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4269.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD83 / UTM zone ", zone, north)); res.emplace_back( @@ -3648,9 +3668,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { *id->codeSpace()) ->createProjectedCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS); + crs.get(), util::IComparable::Criterion:: + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -3671,9 +3691,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) { + crs.get(), util::IComparable::Criterion:: + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -3685,10 +3705,12 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { l_baseCRS->_isEquivalentTo( crs->baseCRS().get(), util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) && + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && objects.size() == 1) { res.clear(); res.emplace_back(crsNN, 100); @@ -3755,17 +3777,21 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (_isEquivalentTo(crs.get(), util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) { + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else if (ellipsoid->_isEquivalentTo( crs->baseCRS()->ellipsoid().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { res.emplace_back(crs, 70); } else { res.emplace_back(crs, 50); @@ -3995,12 +4021,12 @@ void CompoundCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool CompoundCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCompoundCRS = dynamic_cast(other); if (otherCompoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &components = componentReferenceSystems(); @@ -4009,8 +4035,8 @@ bool CompoundCRS::_isEquivalentTo( return false; } for (size_t i = 0; i < components.size(); i++) { - if (!components[i]->_isEquivalentTo(otherComponents[i].get(), - criterion)) { + if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion, + dbContext)) { return false; } } @@ -4050,6 +4076,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisName(nameStr()); if (authorityFactory) { + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || @@ -4063,12 +4091,11 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( - authorityFactory->databaseContext(), - *id->codeSpace()) + dbContext, *id->codeSpace()) ->createCompoundCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT); + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -4089,8 +4116,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -4157,7 +4184,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { } if (_isEquivalentTo(crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else { res.emplace_back(crs, 25); @@ -4529,20 +4557,22 @@ void BoundCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool BoundCRS::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherBoundCRS = dynamic_cast(other); if (otherBoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto standardCriterion = getStandardCriterion(criterion); return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(), - criterion) && + criterion, dbContext) && d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(), - criterion) && + criterion, dbContext) && d->transformation_->_isEquivalentTo( - otherBoundCRS->d->transformation_.get(), standardCriterion); + otherBoundCRS->d->transformation_.get(), standardCriterion, + dbContext); } // --------------------------------------------------------------------------- @@ -4553,10 +4583,14 @@ std::list> BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; + if (!authorityFactory) + return res; std::list resMatchOfTransfToWGS84; - if (authorityFactory && - d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); + if (d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { auto resTemp = d->baseCRS_->identify(authorityFactory); std::string refTransfPROJString; @@ -4627,7 +4661,8 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { refTransfPROJString == opTransfPROJString) || opNormalized->_isEquivalentTo( refTransf.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { resMatchOfTransfToWGS84.emplace_back( create(candidateBaseCRS, d->hubCRS_, NN_NO_CHECK(util::nn_dynamic_pointer_cast< @@ -4807,11 +4842,11 @@ void DerivedGeodeticCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedGeodeticCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -4957,11 +4992,11 @@ void DerivedGeographicCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedGeographicCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5104,11 +5139,11 @@ void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- bool DerivedProjectedCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5234,11 +5269,11 @@ void TemporalCRS::_exportToJSON( // --------------------------------------------------------------------------- bool TemporalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTemporalCRS = dynamic_cast(other); return otherTemporalCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5372,11 +5407,11 @@ void EngineeringCRS::_exportToJSON( // --------------------------------------------------------------------------- bool EngineeringCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherEngineeringCRS = dynamic_cast(other); return otherEngineeringCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5504,11 +5539,11 @@ void ParametricCRS::_exportToJSON( // --------------------------------------------------------------------------- bool ParametricCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherParametricCRS = dynamic_cast(other); return otherParametricCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5609,11 +5644,11 @@ void DerivedVerticalCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedVerticalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5730,11 +5765,11 @@ void DerivedCRSTemplate::_exportToWKT( template bool DerivedCRSTemplate::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } //! @endcond diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index 321fe93f..255f9822 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -213,11 +213,13 @@ void Datum::setProperties( // --------------------------------------------------------------------------- -bool Datum::__isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { +//! @cond Doxygen_Suppress +bool Datum::_isEquivalentTo(const util::IComparable *other, + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDatum = dynamic_cast(other); if (otherDatum == nullptr || - !ObjectUsage::_isEquivalentTo(other, criterion)) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { @@ -248,12 +250,13 @@ bool Datum::__isEquivalentTo(const util::IComparable *other, } if (conventionalRS() && otherDatum->conventionalRS() && conventionalRS()->_isEquivalentTo( - otherDatum->conventionalRS().get(), criterion)) { + otherDatum->conventionalRS().get(), criterion, dbContext)) { return false; } } return true; } +//! @endcond // --------------------------------------------------------------------------- @@ -450,11 +453,11 @@ void PrimeMeridian::_exportToPROJString( //! @cond Doxygen_Suppress bool PrimeMeridian::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherPM = dynamic_cast(other); if (otherPM == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // In MapInfo, the Paris prime meridian is returned as 2.3372291666667 @@ -984,11 +987,12 @@ EllipsoidNNPtr Ellipsoid::identify() const { //! @cond Doxygen_Suppress bool Ellipsoid::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherEllipsoid = dynamic_cast(other); if (otherEllipsoid == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !IdentifiedObject::_isEquivalentTo(other, criterion))) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext))) { return false; } @@ -1324,20 +1328,63 @@ void GeodeticReferenceFrame::_exportToJSON( //! @cond Doxygen_Suppress bool GeodeticReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherGRF = dynamic_cast(other); - if (otherGRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherGRF == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return primeMeridian()->_isEquivalentTo(otherGRF->primeMeridian().get(), - criterion) && - ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion); + criterion, dbContext) && + ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion, + dbContext); } //! @endcond // --------------------------------------------------------------------------- +bool GeodeticReferenceFrame::hasEquivalentNameToUsingAlias( + const IdentifiedObject *other, + const io::DatabaseContextPtr &dbContext) const { + if (dbContext) { + if (!identifiers().empty()) { + const auto &id = identifiers().front(); + auto aliases = + dbContext->getAliases(*(id->codeSpace()), id->code(), nameStr(), + "geodetic_datum", std::string()); + const char *otherName = other->nameStr().c_str(); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName(otherName, + alias.c_str())) { + return true; + } + } + return false; + } else if (!other->identifiers().empty()) { + auto otherGRF = dynamic_cast(other); + if (otherGRF) { + return otherGRF->hasEquivalentNameToUsingAlias(this, dbContext); + } + return false; + } + + auto aliases = + dbContext->getAliases(std::string(), std::string(), nameStr(), + "geodetic_datum", std::string()); + const char *otherName = other->nameStr().c_str(); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName(otherName, + alias.c_str())) { + return true; + } + } + } + return false; +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress struct DynamicGeodeticReferenceFrame::Private { common::Measure frameReferenceEpoch{}; @@ -1407,11 +1454,11 @@ DynamicGeodeticReferenceFrame::deformationModelName() const { //! @cond Doxygen_Suppress bool DynamicGeodeticReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast(other); if (otherDGRF == nullptr || - !GeodeticReferenceFrame::_isEquivalentTo(other, criterion)) { + !GeodeticReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( @@ -1842,10 +1889,11 @@ void VerticalReferenceFrame::_exportToJSON( //! @cond Doxygen_Suppress bool VerticalReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherVRF = dynamic_cast(other); - if (otherVRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherVRF == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } if ((realizationMethod().has_value() ^ @@ -1932,11 +1980,11 @@ DynamicVerticalReferenceFrame::deformationModelName() const { //! @cond Doxygen_Suppress bool DynamicVerticalReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast(other); if (otherDGRF == nullptr || - !VerticalReferenceFrame::_isEquivalentTo(other, criterion)) { + !VerticalReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( @@ -2131,10 +2179,11 @@ void TemporalDatum::_exportToJSON( //! @cond Doxygen_Suppress bool TemporalDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return temporalOrigin().toString() == @@ -2223,10 +2272,11 @@ void EngineeringDatum::_exportToJSON( //! @cond Doxygen_Suppress bool EngineeringDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; @@ -2308,10 +2358,11 @@ void ParametricDatum::_exportToJSON( //! @cond Doxygen_Suppress bool ParametricDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index f0ad157e..0bd44308 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -262,6 +262,9 @@ struct DatabaseContext::Private { std::map> cacheAllowedAuthorities_{}; + lru11::Cache> cacheAliasNames_{ + CACHE_SIZE}; + static void insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj); @@ -1100,7 +1103,7 @@ bool DatabaseContext::isKnownName(const std::string &name, /** \brief Gets the alias name from an official name. * - * @param officialName Official name. + * @param officialName Official name. Mandatory * @param tableName Table name/category. Mandatory * @param source Source of the alias. Mandatory * @return Alias name (or empty if not found). @@ -1132,6 +1135,63 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName, // --------------------------------------------------------------------------- +/** \brief Gets the alias names for an object. + * + * Either authName + code or officialName must be non empty. + * + * @param authName Authority. + * @param code Code. + * @param officialName Official name. + * @param tableName Table name/category. Mandatory + * @param source Source of the alias. May be empty. + * @return Aliases + */ +std::list DatabaseContext::getAliases( + const std::string &authName, const std::string &code, + const std::string &officialName, const std::string &tableName, + const std::string &source) const { + + std::list res; + const auto key(authName + code + officialName + tableName + source); + if (d->cacheAliasNames_.tryGet(key, res)) { + return res; + } + + std::string resolvedAuthName(authName); + std::string resolvedCode(code); + if (authName.empty() || code.empty()) { + std::string sql("SELECT auth_name, code FROM \""); + sql += replaceAll(tableName, "\"", "\"\""); + sql += "\" WHERE name = ?"; + if (tableName == "geodetic_crs") { + sql += " AND type = " GEOG_2D_SINGLE_QUOTED; + } + auto resSql = d->run(sql, {officialName}); + if (resSql.empty()) { + d->cacheAliasNames_.insert(key, res); + return res; + } + const auto &row = resSql.front(); + resolvedAuthName = row[0]; + resolvedCode = row[1]; + } + std::string sql("SELECT alt_name FROM alias_name WHERE table_name = ? AND " + "auth_name = ? AND code = ?"); + ListOfParams params{tableName, resolvedAuthName, resolvedCode}; + if (!source.empty()) { + sql += " AND source = ?"; + params.emplace_back(source); + } + auto resSql = d->run(sql, params); + for (const auto &row : resSql) { + res.emplace_back(row[0]); + } + d->cacheAliasNames_.insert(key, res); + return res; +} + +// --------------------------------------------------------------------------- + /** \brief Return the 'text_definition' column of a table for an object * * @param tableName Table name/category. diff --git a/src/iso19111/metadata.cpp b/src/iso19111/metadata.cpp index 41653b32..6266a86d 100644 --- a/src/iso19111/metadata.cpp +++ b/src/iso19111/metadata.cpp @@ -237,7 +237,8 @@ GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west, //! @cond Doxygen_Suppress bool GeographicBoundingBox::_isEquivalentTo( - const util::IComparable *other, util::IComparable::Criterion) const { + const util::IComparable *other, util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; @@ -502,7 +503,8 @@ VerticalExtent::create(double minimumIn, double maximumIn, //! @cond Doxygen_Suppress bool VerticalExtent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion) const { + util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; @@ -587,7 +589,8 @@ TemporalExtentNNPtr TemporalExtent::create(const std::string &start, //! @cond Doxygen_Suppress bool TemporalExtent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion) const { + util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; @@ -734,7 +737,8 @@ Extent::createFromBBOX(double west, double south, double east, double north, //! @cond Doxygen_Suppress bool Extent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherExtent = dynamic_cast(other); bool ret = (otherExtent && @@ -749,15 +753,18 @@ bool Extent::_isEquivalentTo(const util::IComparable *other, if (ret) { for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) { ret = d->geographicElements_[i]->_isEquivalentTo( - otherExtent->d->geographicElements_[i].get(), criterion); + otherExtent->d->geographicElements_[i].get(), criterion, + dbContext); } for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) { ret = d->verticalElements_[i]->_isEquivalentTo( - otherExtent->d->verticalElements_[i].get(), criterion); + otherExtent->d->verticalElements_[i].get(), criterion, + dbContext); } for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) { ret = d->temporalElements_[i]->_isEquivalentTo( - otherExtent->d->temporalElements_[i].get(), criterion); + otherExtent->d->temporalElements_[i].get(), criterion, + dbContext); } } return ret; diff --git a/src/iso19111/util.cpp b/src/iso19111/util.cpp index 25207d5c..2a6178e2 100644 --- a/src/iso19111/util.cpp +++ b/src/iso19111/util.cpp @@ -687,11 +687,13 @@ IComparable::~IComparable() = default; /** \brief Returns whether an object is equivalent to another one. * @param other other object to compare to * @param criterion comparaison criterion. + * @param dbContext Database context, or nullptr. * @return true if objects are equivalent. */ -bool IComparable::isEquivalentTo(const IComparable *other, - Criterion criterion) const { - return _isEquivalentTo(other, criterion); +bool IComparable::isEquivalentTo( + const IComparable *other, Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { + return _isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- diff --git a/src/proj.h b/src/proj.h index b8b90277..f31c778b 100644 --- a/src/proj.h +++ b/src/proj.h @@ -836,6 +836,10 @@ PJ_OBJ_LIST PROJ_DLL *proj_get_non_deprecated(PJ_CONTEXT *ctx, int PROJ_DLL proj_is_equivalent_to(const PJ *obj, const PJ *other, PJ_COMPARISON_CRITERION criterion); +int PROJ_DLL proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, + const PJ *obj, const PJ *other, + PJ_COMPARISON_CRITERION criterion); + int PROJ_DLL proj_is_crs(const PJ *obj); const char PROJ_DLL* proj_get_name(const PJ *obj); -- cgit v1.2.3 From 1433adcc673928a092c16f6555a0fcd701fdbc26 Mon Sep 17 00:00:00 2001 From: Chris Crook Date: Wed, 4 Dec 2019 09:48:08 +1300 Subject: Adding support for NZLVD vertical height transformation --- src/iso19111/coordinateoperation.cpp | 16 +++++++++++++++- src/proj_constants.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 62d72e65..28f8b41c 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -8767,7 +8767,8 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames( } } - if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON) { + if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON || + methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD ) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); @@ -9441,6 +9442,19 @@ void Transformation::_exportToPROJString( } } + if (methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) { + auto fileParameter = + parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, + EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + formatter->addStep("vgridshift"); + formatter->addParam("grids", fileParameter->valueFile()); + formatter->addParam("multiplier", 1.0); + return; + } + } + if (isLongitudeRotation()) { double offsetDeg = parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET, diff --git a/src/proj_constants.h b/src/proj_constants.h index 5c642862..21cd87fb 100644 --- a/src/proj_constants.h +++ b/src/proj_constants.h @@ -513,6 +513,9 @@ #define EPSG_CODE_METHOD_VERTCON 9658 #define EPSG_NAME_METHOD_VERTCON "VERTCON" +#define EPSG_CODE_METHOD_VERTICALGRID_NZLVD 1071 +#define EPSG_NAME_METHOD_VERTICALGRID_NZLVD "Vertical Offset by Grid Interpolation (NZLVD)" + #define EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE "Vertical offset file" #define EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE 8732 -- cgit v1.2.3 From 5f36c9ebde150e7ece7d153bf1dc7bd213de6016 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 20 Dec 2019 10:40:44 +0100 Subject: test: transformation_NZLVD_to_PROJ_string: attach database context to get filename substitution --- src/iso19111/coordinateoperation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 28f8b41c..aaa0c413 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -8768,7 +8768,7 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames( } if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON || - methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD ) { + methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); -- cgit v1.2.3 From 73a65df7421d5287db2a9f7a3854a68e9310c77a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 24 Dec 2019 12:52:13 +0100 Subject: proj_trans: add retry logic to select other transformation if the best one fails. Relates to https://github.com/OSGeo/PROJ/issues/1808 --- src/4D_api.cpp | 102 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/4D_api.cpp b/src/4D_api.cpp index f37594b5..127ca71e 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -193,47 +193,64 @@ similarly, but prefers the 2D resp. 3D interfaces if available. direction = opposite_direction(direction); if( !P->alternativeCoordinateOperations.empty() ) { - // Do a first pass and select the operations that match the area of use - // and has the best accuracy. - int iBest = -1; - double bestAccuracy = std::numeric_limits::max(); - int i = 0; - for( const auto &alt: P->alternativeCoordinateOperations ) { - 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) { - spatialCriterionOK = true; + constexpr int N_MAX_RETRY = 2; + int iExcluded[N_MAX_RETRY] = {-1, -1}; + + 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 + // 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 = -1; + double bestAccuracy = std::numeric_limits::max(); + for( int i = 0; i < nOperations; i++ ) { + if( i == iExcluded[0] || i == iExcluded[1] ) { + continue; } - } else { - if( coord.xyzt.x >= alt.minxDst && - coord.xyzt.y >= alt.minyDst && - coord.xyzt.x <= alt.maxxDst && - coord.xyzt.y <= alt.maxyDst ) { - spatialCriterionOK = true; + const auto &alt = P->alternativeCoordinateOperations[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) { + 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; + } } - } - 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 && - !alt.isOffshore) ) { - iBest = i; - bestAccuracy = alt.accuracy; + 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 && + !alt.isOffshore) ) { + iBest = i; + bestAccuracy = alt.accuracy; + } } } - i ++; - } + if( iBest < 0 ) { + break; + } - if( iBest >= 0 ) { const auto& alt = P->alternativeCoordinateOperations[iBest]; if( P->iCurCoordOp != iBest ) { std::string msg("Using coordinate operation "); @@ -241,14 +258,23 @@ similarly, but prefers the 2D resp. 3D interfaces if available. pj_log(P->ctx, PJ_LOG_TRACE, msg.c_str()); P->iCurCoordOp = iBest; } - return direction == PJ_FWD ? + PJ_COORD res = direction == PJ_FWD ? pj_fwd4d( coord, alt.pj ) : pj_inv4d( coord, alt.pj ); + if( res.xyzt.x != HUGE_VAL ) { + return res; + } + pj_log(P->ctx, PJ_LOG_TRACE, + "Did not result in valid result. " + "Attempting a retry with another operation."); + if( iRetry == N_MAX_RETRY ) { + break; + } + iExcluded[iRetry] = iBest; } // In case we did not find an operation whose area of use is compatible // with the input coordinate, then goes through again the list, and // use the first operation that does not require grids. - i = 0; NS_PROJ::io::DatabaseContextPtr dbContext; try { @@ -257,7 +283,8 @@ similarly, but prefers the 2D resp. 3D interfaces if available. } } catch( const std::exception& ) {} - for( const auto &alt: P->alternativeCoordinateOperations ) { + 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 ) { @@ -276,7 +303,6 @@ similarly, but prefers the 2D resp. 3D interfaces if available. } } } - i++; } proj_errno_set (P, EINVAL); -- cgit v1.2.3 From 46e28887e19e82488ff0659ff1f127c908fd4637 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 25 Dec 2019 10:11:01 +0100 Subject: projinfo: no longer call createBoundCRSToWGS84IfPossible() for WKT1:GDAL To align with GDAL 3.0.3 behaviour, no longer automatically try to create a boundCRS to WGS84 when exporting to WKT1:GDAL. The user has to explicitly specify --boundcrs-to-wgs84 if he wishes this behaviour. --- src/apps/projinfo.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src') diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index fd9b2f46..c1139564 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -423,24 +423,13 @@ static void outputObject( std::cout << "WKT1:GDAL string:" << std::endl; } - auto crs = nn_dynamic_pointer_cast(obj); - std::shared_ptr objToExport; - if (crs) { - objToExport = nn_dynamic_pointer_cast( - crs->createBoundCRSToWGS84IfPossible( - dbContext, allowUseIntermediateCRS)); - } - if (!objToExport) { - objToExport = wktExportable; - } - auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); - auto wkt = objToExport->exportToWKT(formatter.get()); + auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } -- cgit v1.2.3 From 90875d7616facf04a1836be833fb810142f3d8e1 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 25 Dec 2019 10:45:20 +0100 Subject: Modify verbosity level of some debug/trace messages - unitconvert, ell_set and helmert were using debug level, which is too verbose. Using trace instead - proj_trans() was using trace to indicate the operation it selects. Changing it to debug --- src/4D_api.cpp | 18 +++++++++++------- src/conversions/unitconvert.cpp | 12 ++++++------ src/ell_set.cpp | 4 ++-- src/transformations/helmert.cpp | 28 ++++++++++++++-------------- 4 files changed, 33 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 127ca71e..087cac5c 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -253,9 +253,11 @@ similarly, but prefers the 2D resp. 3D interfaces if available. const auto& alt = P->alternativeCoordinateOperations[iBest]; if( P->iCurCoordOp != iBest ) { - std::string msg("Using coordinate operation "); - msg += alt.name; - pj_log(P->ctx, PJ_LOG_TRACE, msg.c_str()); + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + std::string msg("Using coordinate operation "); + msg += alt.name; + pj_log(P->ctx, PJ_LOG_DEBUG, msg.c_str()); + } P->iCurCoordOp = iBest; } PJ_COORD res = direction == PJ_FWD ? @@ -263,7 +265,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available. if( res.xyzt.x != HUGE_VAL ) { return res; } - pj_log(P->ctx, PJ_LOG_TRACE, + pj_log(P->ctx, PJ_LOG_DEBUG, "Did not result in valid result. " "Attempting a retry with another operation."); if( iRetry == N_MAX_RETRY ) { @@ -290,9 +292,11 @@ similarly, but prefers the 2D resp. 3D interfaces if available. if( coordOperation ) { if( coordOperation->gridsNeeded(dbContext).empty() ) { if( P->iCurCoordOp != i ) { - std::string msg("Using coordinate operation "); - msg += alt.name; - pj_log(P->ctx, PJ_LOG_TRACE, msg.c_str()); + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + std::string msg("Using coordinate operation "); + msg += alt.name; + pj_log(P->ctx, PJ_LOG_DEBUG, msg.c_str()); + } P->iCurCoordOp = i; } if( direction == PJ_FWD ) { diff --git a/src/conversions/unitconvert.cpp b/src/conversions/unitconvert.cpp index 377f384e..8472355d 100644 --- a/src/conversions/unitconvert.cpp +++ b/src/conversions/unitconvert.cpp @@ -470,7 +470,7 @@ PJ *CONVERSION(unitconvert,0) { const char* normalized_name = nullptr; f = get_unit_conversion_factor(name, &xy_in_is_linear, &normalized_name); if (f != 0.0) { - proj_log_debug(P, "xy_in unit: %s", normalized_name); + proj_log_trace(P, "xy_in unit: %s", normalized_name); } else { f = pj_param (P->ctx, P->params, "dxy_in").f; if (f == 0.0 || 1.0 / f == 0.0) @@ -485,7 +485,7 @@ PJ *CONVERSION(unitconvert,0) { const char* normalized_name = nullptr; f = get_unit_conversion_factor(name, &xy_out_is_linear, &normalized_name); if (f != 0.0) { - proj_log_debug(P, "xy_out unit: %s", normalized_name); + proj_log_trace(P, "xy_out unit: %s", normalized_name); } else { f = pj_param (P->ctx, P->params, "dxy_out").f; if (f == 0.0 || 1.0 / f == 0.0) @@ -506,7 +506,7 @@ PJ *CONVERSION(unitconvert,0) { const char* normalized_name = nullptr; f = get_unit_conversion_factor(name, &z_in_is_linear, &normalized_name); if (f != 0.0) { - proj_log_debug(P, "z_in unit: %s", normalized_name); + proj_log_trace(P, "z_in unit: %s", normalized_name); } else { f = pj_param (P->ctx, P->params, "dz_in").f; if (f == 0.0 || 1.0 / f == 0.0) @@ -519,7 +519,7 @@ PJ *CONVERSION(unitconvert,0) { const char* normalized_name = nullptr; f = get_unit_conversion_factor(name, &z_out_is_linear, &normalized_name); if (f != 0.0) { - proj_log_debug(P, "z_out unit: %s", normalized_name); + proj_log_trace(P, "z_out unit: %s", normalized_name); } else { f = pj_param (P->ctx, P->params, "dz_out").f; if (f == 0.0 || 1.0 / f == 0.0) @@ -540,7 +540,7 @@ PJ *CONVERSION(unitconvert,0) { if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ Q->t_in_id = i; - proj_log_debug(P, "t_in unit: %s", time_units[i].name); + proj_log_trace(P, "t_in unit: %s", time_units[i].name); } s = nullptr; @@ -550,7 +550,7 @@ PJ *CONVERSION(unitconvert,0) { if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ Q->t_out_id = i; - proj_log_debug(P, "t_out unit: %s", time_units[i].name); + proj_log_trace(P, "t_out unit: %s", time_units[i].name); } return P; diff --git a/src/ell_set.cpp b/src/ell_set.cpp index bb46b3a4..899ec1bf 100644 --- a/src/ell_set.cpp +++ b/src/ell_set.cpp @@ -116,9 +116,9 @@ int pj_ellipsoid (PJ *P) { if (0 != ellps_spherification (P)) return 4; - proj_log_debug (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d", + proj_log_trace (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d", P->a, P->f!=0? 1/P->f: 0, proj_errno (P)); - proj_log_debug (P, "pj_ellipsoid - final: %s %s %s %s", + proj_log_trace (P, "pj_ellipsoid - final: %s %s %s %s", P->def_size? P->def_size: empty, P->def_shape? P->def_shape: empty, P->def_spherification? P->def_spherification: empty, diff --git a/src/transformations/helmert.cpp b/src/transformations/helmert.cpp index 7a3e64d0..2045dea2 100644 --- a/src/transformations/helmert.cpp +++ b/src/transformations/helmert.cpp @@ -665,18 +665,18 @@ PJ *TRANSFORMATION(helmert, 0) { } /* Let's help with debugging */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - proj_log_debug(P, "Helmert parameters:"); - proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); - proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { + proj_log_trace(P, "Helmert parameters:"); + proj_log_trace(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); + proj_log_trace(P, "rx= %8.5f ry= %8.5f rz= %8.5f", Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); - proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, + proj_log_trace(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, Q->no_rotation ? "" : Q->is_position_vector ? " convention=position_vector" : " convention=coordinate_frame"); - proj_log_debug(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); - proj_log_debug(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); - proj_log_debug(P, "ds= %8.5f t_epoch=%8.5f", Q->dscale, Q->t_epoch); + proj_log_trace(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); + proj_log_trace(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); + proj_log_trace(P, "ds= %8.5f t_epoch=%8.5f", Q->dscale, Q->t_epoch); } if (Q->no_rotation) { @@ -729,15 +729,15 @@ PJ *TRANSFORMATION(molobadekas, 0) { /* Let's help with debugging */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - proj_log_debug(P, "Molodensky-Badekas parameters:"); - proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z); - proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { + proj_log_trace(P, "Molodensky-Badekas parameters:"); + proj_log_trace(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z); + proj_log_trace(P, "rx= %8.5f ry= %8.5f rz= %8.5f", Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); - proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, + proj_log_trace(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, Q->is_position_vector ? " convention=position_vector" : " convention=coordinate_frame"); - proj_log_debug(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z); + proj_log_trace(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z); } /* as an optimization, we incorporate the refp in the translation terms */ -- cgit v1.2.3 From 37da5e243191c04607597f6b8a77acfa017a5c99 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 25 Dec 2019 22:31:50 +0100 Subject: cct: revise end-of-line handling in logging, and always output debug and trace messages to stderr --- src/apps/cct.cpp | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/apps/cct.cpp b/src/apps/cct.cpp index d29b58fb..cebf74da 100644 --- a/src/apps/cct.cpp +++ b/src/apps/cct.cpp @@ -161,19 +161,13 @@ static void logger(void *data, int level, const char *msg) { /* if we use PJ_LOG_NONE we always want to print stuff to stream */ if (level == PJ_LOG_NONE) { - fprintf(stream, "%s", msg); + fprintf(stream, "%s\n", msg); return; } - /* should always print to stderr if level == PJ_LOG_ERROR */ - if (level == PJ_LOG_ERROR) { - fprintf(stderr, "%s", msg); - return; - } - - /* otherwise only print if log level set by user is high enough */ - if (level <= log_tell) - fprintf(stream, "%s", msg); + /* otherwise only print if log level set by user is high enough or error */ + if (level <= log_tell || level == PJ_LOG_ERROR) + fprintf(stderr, "%s\n", msg); } FILE *fout; @@ -244,19 +238,19 @@ int main(int argc, char **argv) { proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); if (opt_given (o, "version")) { - print (PJ_LOG_NONE, "%s: %s\n", o->progname, pj_get_release ()); + 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\n", o->progname, opt_arg (o, "output")); + 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\n", 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")); @@ -287,7 +281,7 @@ int main(int argc, char **argv) { /* cppcheck-suppress invalidscanf */ 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'\n", o->progname, opt_arg (o, "c")); + print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'", o->progname, opt_arg (o, "c")); free (o); if (stdout != fout) fclose (fout); @@ -298,7 +292,7 @@ int main(int argc, char **argv) { /* Setup transformation */ P = proj_create_argv (nullptr, o->pargc, o->pargv); if ((nullptr==P) || (0==o->pargc)) { - print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help\n", + print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help", o->progname, pj_strerrno (proj_errno(P)), o->progname); free (o); if (stdout != fout) @@ -307,12 +301,12 @@ int main(int argc, char **argv) { } info = proj_pj_info (P); - print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d\n", info.definition, argc, o->pargc); + print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d", info.definition, argc, o->pargc); if (direction== PJ_INV) { /* fail if an inverse operation is not available */ if (!info.has_inverse) { - print (PJ_LOG_ERROR, "Inverse operation not available\n"); + print (PJ_LOG_ERROR, "Inverse operation not available"); if (stdout != fout) fclose (fout); return 1; @@ -325,7 +319,7 @@ int main(int argc, char **argv) { /* Allocate input buffer */ buf = static_cast(calloc (1, 10000)); if (nullptr==buf) { - print (PJ_LOG_ERROR, "%s: Out of memory\n", o->progname); + print (PJ_LOG_ERROR, "%s: Out of memory", o->progname); pj_free (P); free (o); if (stdout != fout) @@ -341,7 +335,7 @@ int main(int argc, char **argv) { char *c = column (buf, 1); opt_eof_handler (o); if (nullptr==ret) { - print (PJ_LOG_ERROR, "Read error in record %d\n", (int) o->record_index); + print (PJ_LOG_ERROR, "Read error in record %d", (int) o->record_index); continue; } point = parse_input_line (buf, columns_xyzt, fixed_z, fixed_time); @@ -359,7 +353,7 @@ int main(int argc, char **argv) { 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\n", o->progname, opt_filename (o), opt_record (o)); + print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d", o->progname, opt_filename (o), opt_record (o)); continue; } @@ -395,7 +389,7 @@ int main(int argc, char **argv) { 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\n", + 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, @@ -403,7 +397,7 @@ int main(int argc, char **argv) { ); } else - print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s\n", + 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, -- cgit v1.2.3 From d71a6ce9094daac790daf828e6148c99c30c3ed3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 25 Dec 2019 22:35:28 +0100 Subject: cct: make it honour PROJ_DEBUG if -v/-vv/-vvv are not specified --- src/apps/cct.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/apps/cct.cpp b/src/apps/cct.cpp index cebf74da..f7413872 100644 --- a/src/apps/cct.cpp +++ b/src/apps/cct.cpp @@ -234,7 +234,9 @@ int main(int argc, char **argv) { 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 */ - proj_log_level (PJ_DEFAULT_CTX, static_cast(verbose)); + if( verbose > 0 ) { + proj_log_level (PJ_DEFAULT_CTX, static_cast(verbose)); + } proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); if (opt_given (o, "version")) { -- cgit v1.2.3 From ae3cd5df4d7e20dd92494b8b3181416e3c69c835 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sat, 28 Dec 2019 11:15:50 +0100 Subject: Update ABI numbers for 6.3.0 release --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 2e14d1dd..79ef17d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,7 +42,7 @@ geodtest_LDADD = libproj.la lib_LTLIBRARIES = libproj.la -libproj_la_LDFLAGS = -no-undefined -version-info 17:0:2 +libproj_la_LDFLAGS = -no-undefined -version-info 18:0:3 libproj_la_LIBADD = @SQLITE3_LIBS@ libproj_la_SOURCES = \ -- cgit v1.2.3 From fd377df0d12d4127ed277f4f3c11a4122eab3ccf Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 1 Jan 2020 22:02:54 +0100 Subject: Bump version numbers in preparation for 7.0.0 release --- src/proj.h | 4 ++-- src/proj_api.h | 2 +- src/release.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/proj.h b/src/proj.h index f31c778b..410f1e71 100644 --- a/src/proj.h +++ b/src/proj.h @@ -152,8 +152,8 @@ extern "C" { #endif /* The version numbers should be updated with every release! **/ -#define PROJ_VERSION_MAJOR 6 -#define PROJ_VERSION_MINOR 3 +#define PROJ_VERSION_MAJOR 7 +#define PROJ_VERSION_MINOR 0 #define PROJ_VERSION_PATCH 0 extern char const PROJ_DLL pj_release[]; /* global release id string */ diff --git a/src/proj_api.h b/src/proj_api.h index 852cb14c..24046026 100644 --- a/src/proj_api.h +++ b/src/proj_api.h @@ -38,7 +38,7 @@ #endif #ifndef PJ_VERSION -#define PJ_VERSION 630 +#define PJ_VERSION 700 #endif #ifdef PROJ_RENAME_SYMBOLS diff --git a/src/release.cpp b/src/release.cpp index 425bdb9a..648c5398 100644 --- a/src/release.cpp +++ b/src/release.cpp @@ -11,7 +11,7 @@ char const pj_release[] = STR(PROJ_VERSION_MAJOR)"." STR(PROJ_VERSION_MINOR)"." STR(PROJ_VERSION_PATCH)", " - "January 1st, 2020"; + "March 1st, 2020"; const char *pj_get_release() { return pj_release; -- cgit v1.2.3 From ea311993551f0019f38de361a5bc47fc630ee871 Mon Sep 17 00:00:00 2001 From: snowman2 Date: Wed, 1 Jan 2020 20:29:16 -0600 Subject: fix exporting CoordinateSystem to PROJ JSON with ID --- src/iso19111/coordinatesystem.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp index 6769b486..688351f4 100644 --- a/src/iso19111/coordinatesystem.cpp +++ b/src/iso19111/coordinatesystem.cpp @@ -581,11 +581,13 @@ void CoordinateSystem::_exportToJSON( writer.Add(getWKT2Type(true)); writer.AddObjKey("axis"); - auto axisContext(writer.MakeArrayContext(false)); - const auto &l_axisList = axisList(); - for (auto &axis : l_axisList) { - formatter->setOmitTypeInImmediateChild(); - axis->_exportToJSON(formatter); + { + auto axisContext(writer.MakeArrayContext(false)); + const auto &l_axisList = axisList(); + for (auto &axis : l_axisList) { + formatter->setOmitTypeInImmediateChild(); + axis->_exportToJSON(formatter); + } } if (formatter->outputId()) { -- cgit v1.2.3 From ff15ab6c2d709e6ac230fd0815431d2618e0faef Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Mon, 6 Jan 2020 21:46:56 +0100 Subject: Remove deprecated JNI bindings Closes #1757 --- src/Makefile.am | 6 +- src/jniproj.cpp | 469 ----------------------------------------------------- src/lib_proj.cmake | 21 --- src/org_proj4_PJ.h | 135 --------------- 4 files changed, 3 insertions(+), 628 deletions(-) delete mode 100644 src/jniproj.cpp delete mode 100644 src/org_proj4_PJ.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 79ef17d1..62821ff9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,11 +7,11 @@ TESTS = geodtest check_PROGRAMS = geodtest AM_CPPFLAGS = -DPROJ_LIB=\"$(pkgdatadir)\" \ - -DMUTEX_@MUTEX_SETTING@ @JNI_INCLUDE@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ + -DMUTEX_@MUTEX_SETTING@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ 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 + proj_symbol_rename.h EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \ bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \ @@ -199,7 +199,7 @@ libproj_la_SOURCES = \ nad_cvt.cpp nad_init.cpp nad_intr.cpp \ apply_gridshift.cpp datums.cpp datum_set.cpp transform.cpp \ geocent.cpp geocent.h utils.cpp gridinfo.cpp gridlist.cpp \ - jniproj.cpp mutex.cpp initcache.cpp apply_vgridshift.cpp geodesic.c \ + mutex.cpp initcache.cpp apply_vgridshift.cpp geodesic.c \ strtod.cpp \ \ 4D_api.cpp pipeline.cpp \ diff --git a/src/jniproj.cpp b/src/jniproj.cpp deleted file mode 100644 index 6f441529..00000000 --- a/src/jniproj.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Java/JNI wrappers for PROJ API. - * Author: Antonello Andrea - * Martin Desruisseaux - * - ****************************************************************************** - * Copyright (c) 2005, Andrea Antonello - * Copyright (c) 2011, Martin Desruisseaux - * Copyright (c) 2018, 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. - *****************************************************************************/ - -/*! - * \file jniproj.c - * - * \brief - * Functions used by the Java Native Interface (JNI) wrappers of PROJ. - * - * - * \author Antonello Andrea - * \date Wed Oct 20 23:10:24 CEST 2004 - * - * \author Martin Desruisseaux - * \date August 2011 - */ - -#include "proj_config.h" - -#ifdef JNI_ENABLED - -#include -#include -#include "proj_internal.h" -#include "org_proj4_PJ.h" -#include - -#define PJ_FIELD_NAME "ptr" -#define PJ_FIELD_TYPE "J" - -/*! - * \brief - * Internal method returning the address of the PJ structure wrapped by the given Java object. - * This function looks for a field named "ptr" and of type "long" (Java signature "J") in the - * given object. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The address of the PJ structure, or nullptr if the operation fails (for example - * because the "ptr" field was not found). - */ -static PJ *getPJ(JNIEnv *env, jobject object) -{ - jfieldID id = env->GetFieldID(env->GetObjectClass(object), PJ_FIELD_NAME, PJ_FIELD_TYPE); - return (id) ? (PJ*) env->GetLongField(object, id) : nullptr; -} - -/*! - * \brief - * Internal method returning the java.lang.Double.NaN constant value. - * Efficiency is no a high concern for this particular method, because it - * is used mostly when the user wrongly attempt to use a disposed PJ object. - * - * \param env - The JNI environment. - * \return The java.lang.Double.NaN constant value. - */ -static jdouble javaNaN(JNIEnv *env) -{ - jclass c = env->FindClass("java/lang/Double"); - if (c) { // Should never be nullptr, but let be paranoiac. - jfieldID id = env->GetStaticFieldID(c, "NaN", "D"); - if (id) { // Should never be nullptr, but let be paranoiac. - return env->GetStaticDoubleField(c, id); - } - } - return 0.0; // Should never happen. -} - -/*! - * \brief - * Returns the Proj4 release number. - * - * \param env - The JNI environment. - * \return The Proj4 release number, or nullptr. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion - (JNIEnv *env, jclass) -{ - const char *desc = pj_get_release(); - return (desc) ? env->NewStringUTF(desc) : nullptr; -} - -/*! - * \brief - * Allocates a new PJ structure from a definition string. - * - * \param env - The JNI environment. - * \param definition - The string definition to be given to Proj4. - * \return The address of the new PJ structure, or 0 in case of failure. - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ - (JNIEnv *env, jclass, jstring definition) -{ - const char *def_utf = env->GetStringUTFChars(definition, nullptr); - if (!def_utf) return 0; /* OutOfMemoryError already thrown. */ - PJ *pj = pj_init_plus(def_utf); - env->ReleaseStringUTFChars(definition, def_utf); - return (jlong) pj; -} - -/*! - * \brief - * Allocates a new geographic PJ structure from an existing one. - * - * \param env - The JNI environment. - * \param projected - The PJ object from which to derive a new one. - * \return The address of the new PJ structure, or 0 in case of failure. - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ - (JNIEnv *env, jclass, jobject projected) -{ - PJ *pj = getPJ(env, projected); - return (pj) ? (jlong) pj_latlong_from_proj(pj) : 0; -} - -/*! - * \brief - * Returns the definition string. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The definition string. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - char *desc = pj_get_def(pj, 0); - if (desc) { - jstring str = env->NewStringUTF(desc); - pj_dalloc(desc); - return str; - } - } - return nullptr; -} - -/*! - * \brief - * Returns the description associated to the PJ structure. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The description associated to the PJ structure. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - const char *desc = pj->descr; - if (desc) { - return env->NewStringUTF(desc); - } - } - return nullptr; -} - -/*! - * \brief - * Returns the CRS type as one of the PJ.Type enum: GEOGRAPHIC, GEOCENTRIC or PROJECTED. - * This function should never return nullptr, unless class or fields have been renamed in - * such a way that we can not find anymore the expected enum values. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The CRS type as one of the PJ.Type enum. - */ -JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - const char *type; - if (pj_is_latlong(pj)) { - type = "GEOGRAPHIC"; - } else if (pj_is_geocent(pj)) { - type = "GEOCENTRIC"; - } else { - type = "PROJECTED"; - } - jclass c = env->FindClass("org/proj4/PJ$Type"); - if (c) { - jfieldID id = env->GetStaticFieldID(c, type, "Lorg/proj4/PJ$Type;"); - if (id) { - return env->GetStaticObjectField(c, id); - } - } - } - return nullptr; -} - -/*! - * \brief - * Returns the semi-major axis length. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The semi-major axis length. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return pj ? pj->a_orig : javaNaN(env); -} - -/*! - * \brief - * Computes the semi-minor axis length from the semi-major axis length and the eccentricity - * squared. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The semi-minor axis length. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (!pj) return javaNaN(env); - double a = pj->a_orig; - return sqrt(a*a * (1.0 - pj->es_orig)); -} - -/*! - * \brief - * Returns the eccentricity squared. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The eccentricity. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return pj ? pj->es_orig : javaNaN(env); -} - -/*! - * \brief - * Returns an array of character indicating the direction of each axis. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The axis directions. - */ -JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - int length = static_cast(strlen(pj->axis)); - jcharArray array = env->NewCharArray(length); - if (array) { - jchar* axis = env->GetCharArrayElements(array, nullptr); - if (axis) { - /* Don't use memcp because the type may not be the same. */ - int i; - for (i=0; iaxis[i]; - } - env->ReleaseCharArrayElements(array, axis, 0); - } - return array; - } - } - return nullptr; -} - -/*! - * \brief - * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The prime meridian longitude, in degrees. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return (pj) ? (pj->from_greenwich)*(180/M_PI) : javaNaN(env); -} - -/*! - * \brief - * Returns the conversion factor from linear units to metres. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \param vertical - JNI_FALSE for horizontal axes, or JNI_TRUE for the vertical axis. - * \return The conversion factor to metres. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre - (JNIEnv *env, jobject object, jboolean vertical) -{ - PJ *pj = getPJ(env, object); - if (pj) { - return (vertical) ? pj->vto_meter : pj->to_meter; - } - return javaNaN(env); -} - -/*! - * \brief - * Converts input values from degrees to radians before coordinate operation, or the output - * values from radians to degrees after the coordinate operation. - * - * \param pj - The PROJ.4 PJ structure. - * \param data - The coordinate array to transform. - * \param numPts - Number of points to transform. - * \param dimension - Dimension of points in the coordinate array. - * \param factor - The scale factor to apply: M_PI/180 for inputs or 180/M_PI for outputs. - */ -static void convertAngularOrdinates(PJ *pj, double* data, jint numPts, int dimension, double factor) { - int dimToSkip; - if (pj_is_latlong(pj)) { - /* Convert only the 2 first ordinates and skip all the other dimensions. */ - dimToSkip = dimension - 2; - } else { - /* Not a geographic CRS: nothing to convert. */ - return; - } - double *stop = data + dimension*numPts; - if (dimToSkip > 0) { - while (data != stop) { - (*data++) *= factor; - (*data++) *= factor; - data += dimToSkip; - } - } else { - while (data != stop) { - (*data++) *= factor; - } - } -} - -/*! - * \brief - * Transforms in-place the coordinates in the given array. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \param target - The target CRS. - * \param dimension - The dimension of each coordinate value. Must be equals or greater than 2. - * \param coordinates - The coordinates to transform, as a sequence of (x,y,,...) tuples. - * \param offset - Offset of the first coordinate in the given array. - * \param numPts - Number of points to transform. - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_transform - (JNIEnv *env, jobject object, jobject target, jint dimension, jdoubleArray coordinates, jint offset, jint numPts) -{ - if (!target || !coordinates) { - jclass c = env->FindClass("java/lang/NullPointerException"); - if (c) env->ThrowNew(c, "The target CRS and the coordinates array can not be null."); - return; - } - if (dimension < 2 || dimension > org_proj4_PJ_DIMENSION_MAX) { /* Arbitrary upper value for catching potential misuse. */ - jclass c = env->FindClass("java/lang/IllegalArgumentException"); - if (c) env->ThrowNew(c, "Illegal number of dimensions."); - return; - } - if ((offset < 0) || (numPts < 0) || (offset + dimension*numPts) > env->GetArrayLength(coordinates)) { - jclass c = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); - if (c) env->ThrowNew(c, "Illegal offset or illegal number of points."); - return; - } - PJ *src_pj = getPJ(env, object); - PJ *dst_pj = getPJ(env, target); - if (src_pj && dst_pj) { - /* Using GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical rather than - GetDoubleArrayElements/ReleaseDoubleArrayElements increase the chances that - the JVM returns direct reference to its internal array without copying data. - However we must promise to run the "critical" code fast, to not make any - system call that may wait for the JVM and to not invoke any other JNI method. */ - double *data = static_cast(env->GetPrimitiveArrayCritical(coordinates, nullptr)); - if (data) { - double *x = data + offset; - double *y = x + 1; - double *z = (dimension >= 3) ? y+1 : nullptr; - convertAngularOrdinates(src_pj, x, numPts, dimension, M_PI/180); - int err = pj_transform(src_pj, dst_pj, numPts, dimension, x, y, z); - convertAngularOrdinates(dst_pj, x, numPts, dimension, 180/M_PI); - env->ReleasePrimitiveArrayCritical(coordinates, data, 0); - if (err) { - jclass c = env->FindClass("org/proj4/PJException"); - if (c) env->ThrowNew(c, pj_strerrno(err)); - } - } - } -} - -/*! - * \brief - * Returns a description of the last error that occurred, or nullptr if none. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - * \return The last error, or nullptr. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - int err = pj_ctx_get_errno(pj->ctx); - if (err) { - return env->NewStringUTF(pj_strerrno(err)); - } - } - return nullptr; -} - -/*! - * \brief - * Deallocate the PJ structure. This method is invoked by the garbage collector exactly once. - * This method will also set the Java "ptr" final field to 0 as a safety. In theory we are not - * supposed to change the value of a final field. But no Java code should use this field, and - * the PJ object is being garbage collected anyway. We set the field to 0 as a safety in case - * some user invoked the finalize() method explicitly despite our warning in the Javadoc to - * never do such thing. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize - (JNIEnv *env, jobject object) -{ - jfieldID id = env->GetFieldID(env->GetObjectClass(object), PJ_FIELD_NAME, PJ_FIELD_TYPE); - if (id) { - PJ *pj = (PJ*) env->GetLongField(object, id); - if (pj) { - env->SetLongField(object, id, (jlong) 0); - pj_free(pj); - } - } -} - -#endif diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index bc27bafe..2e470683 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -245,7 +245,6 @@ set(SRC_LIBPROJ_CORE initcache.cpp internal.cpp inv.cpp - jniproj.cpp list.cpp log.cpp malloc.cpp @@ -320,26 +319,6 @@ source_group("CMake Files" FILES CMakeLists.txt) # Embed PROJ_LIB data files location add_definitions(-DPROJ_LIB="${CMAKE_INSTALL_PREFIX}/${DATADIR}") -################################################# -## java wrapping with jni -################################################# -option(JNI_SUPPORT "Build support of java/jni wrapping for proj library" OFF) -find_package(JNI QUIET) -if(JNI_SUPPORT AND NOT JNI_FOUND) - message(FATAL_ERROR "jni support is required but jni is not found") -endif() -boost_report_value(JNI_SUPPORT) -if(JNI_SUPPORT) - set(SRC_LIBPROJ_CORE - ${SRC_LIBPROJ_CORE} jniproj.cpp) - set(HEADERS_LIBPROJ - ${HEADERS_LIBPROJ} org_proj4_PJ.h) - source_group("Source Files\\JNI" FILES ${SRC_LIBPROJ_JNI}) - add_definitions(-DJNI_ENABLED) - include_directories(${JNI_INCLUDE_DIRS}) - boost_report_value(JNI_INCLUDE_DIRS) -endif() - ################################################# ## targets: libproj and proj_config.h ################################################# diff --git a/src/org_proj4_PJ.h b/src/org_proj4_PJ.h deleted file mode 100644 index be5d3f58..00000000 --- a/src/org_proj4_PJ.h +++ /dev/null @@ -1,135 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_proj4_PJ */ - -#ifndef _Included_org_proj4_PJ -#define _Included_org_proj4_PJ -#ifdef __cplusplus -extern "C" { -#endif -#undef org_proj4_PJ_DIMENSION_MAX -#define org_proj4_PJ_DIMENSION_MAX 100L -/* - * Class: org_proj4_PJ - * Method: allocatePJ - * Signature: (Ljava/lang/String;)J - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ - (JNIEnv *, jclass, jstring); - -/* - * Class: org_proj4_PJ - * Method: allocateGeoPJ - * Signature: (Lorg/proj4/PJ;)J - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ - (JNIEnv *, jclass, jobject); - -/* - * Class: org_proj4_PJ - * Method: getVersion - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion - (JNIEnv *, jclass); - -/* - * Class: org_proj4_PJ - * Method: getDefinition - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getType - * Signature: ()Lorg/proj4/PJ/Type; - */ -JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getSemiMajorAxis - * Signature: ()D - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getSemiMinorAxis - * Signature: ()D - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getEccentricitySquared - * Signature: ()D - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getAxisDirections - * Signature: ()[C - */ -JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getGreenwichLongitude - * Signature: ()D - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: getLinearUnitToMetre - * Signature: (Z)D - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_proj4_PJ - * Method: transform - * Signature: (Lorg/proj4/PJ;I[DII)V - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_transform - (JNIEnv *, jobject, jobject, jint, jdoubleArray, jint, jint); - -/* - * Class: org_proj4_PJ - * Method: getLastError - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: toString - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString - (JNIEnv *, jobject); - -/* - * Class: org_proj4_PJ - * Method: finalize - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize - (JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif -- cgit v1.2.3 From 699b92250a90e5c333572245cf3d09c9e00e50e5 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 6 Jan 2020 23:34:09 +0100 Subject: createObjectsFromName(): make it look up in the alias_name table too (fixes #1823) --- src/iso19111/c_api.cpp | 23 +++-- src/iso19111/factory.cpp | 252 ++++++++++++++++++++++++++++------------------- src/iso19111/io.cpp | 30 +++--- 3 files changed, 181 insertions(+), 124 deletions(-) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 5a5e97f6..7d4f5eba 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -2760,16 +2760,19 @@ static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame( if (metadata::Identifier::isEquivalentName( datumName.c_str(), refDatum->nameStr().c_str())) { datumName = refDatum->nameStr(); - } - } else { - std::string outTableName; - std::string authNameFromAlias; - std::string codeFromAlias; - auto officialName = authFactory->getOfficialNameFromAlias( - datumName, "geodetic_datum", std::string(), true, - outTableName, authNameFromAlias, codeFromAlias); - if (!officialName.empty()) { - datumName = officialName; + } else if (refDatum->identifiers().size() == 1) { + const auto &id = refDatum->identifiers()[0]; + const auto aliases = + authFactory->databaseContext()->getAliases( + *id->codeSpace(), id->code(), refDatum->nameStr(), + "geodetic_datum", std::string()); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName( + datumName.c_str(), alias.c_str())) { + datumName = refDatum->nameStr(); + break; + } + } } } } diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 0bd44308..33fce593 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -5248,13 +5248,12 @@ static void addToListString(std::string &out, const char *in) { out += in; } -static void addToListStringWithOR(std::string &out, const char *in) { +static void addToListStringWithOR(std::string &out, const std::string &in) { if (!out.empty()) { out += " OR "; } out += in; } - //! @endcond // --------------------------------------------------------------------------- @@ -5291,6 +5290,7 @@ AuthorityFactory::createObjectsFromName( } std::string sql( + "SELECT table_name, auth_name, code, name, deprecated FROM (" "SELECT table_name, auth_name, code, name, deprecated FROM object_view " "WHERE "); if (deprecated) { @@ -5306,108 +5306,141 @@ AuthorityFactory::createObjectsFromName( params.emplace_back(d->authority()); } - if (allowedObjectTypes.empty()) { - sql += "table_name IN (" - "'prime_meridian','ellipsoid','geodetic_datum'," - "'vertical_datum','geodetic_crs','projected_crs'," - "'vertical_crs','compound_crs','conversion'," - "'helmert_transformation','grid_transformation'," - "'other_transformation','concatenated_operation'" - ")"; - } else { - std::string tableNameList; - std::string otherConditions; - for (const auto type : allowedObjectTypes) { - switch (type) { - case ObjectType::PRIME_MERIDIAN: - addToListString(tableNameList, "'prime_meridian'"); - break; - case ObjectType::ELLIPSOID: - addToListString(tableNameList, "'ellipsoid'"); - break; - case ObjectType::DATUM: - addToListString(tableNameList, - "'geodetic_datum','vertical_datum'"); - break; - case ObjectType::GEODETIC_REFERENCE_FRAME: - addToListString(tableNameList, "'geodetic_datum'"); - break; - case ObjectType::VERTICAL_REFERENCE_FRAME: - addToListString(tableNameList, "'vertical_datum'"); - break; - case ObjectType::CRS: - addToListString(tableNameList, "'geodetic_crs','projected_crs'," - "'vertical_crs','compound_crs'"); - break; - case ObjectType::GEODETIC_CRS: - addToListString(tableNameList, "'geodetic_crs'"); - break; - case ObjectType::GEOCENTRIC_CRS: - addToListStringWithOR(otherConditions, - "(table_name = " GEOCENTRIC_SINGLE_QUOTED - " AND " - "type = " GEOCENTRIC_SINGLE_QUOTED ")"); - break; - case ObjectType::GEOGRAPHIC_CRS: - addToListStringWithOR(otherConditions, - "(table_name = 'geodetic_crs' AND " - "type IN (" GEOG_2D_SINGLE_QUOTED - "," GEOG_3D_SINGLE_QUOTED "))"); - break; - case ObjectType::GEOGRAPHIC_2D_CRS: - addToListStringWithOR(otherConditions, - "(table_name = 'geodetic_crs' AND " - "type = " GEOG_2D_SINGLE_QUOTED ")"); - break; - case ObjectType::GEOGRAPHIC_3D_CRS: - addToListStringWithOR(otherConditions, - "(table_name = 'geodetic_crs' AND " - "type = " GEOG_3D_SINGLE_QUOTED ")"); - break; - case ObjectType::PROJECTED_CRS: - addToListString(tableNameList, "'projected_crs'"); - break; - case ObjectType::VERTICAL_CRS: - addToListString(tableNameList, "'vertical_crs'"); - break; - case ObjectType::COMPOUND_CRS: - addToListString(tableNameList, "'compound_crs'"); - break; - case ObjectType::COORDINATE_OPERATION: - addToListString(tableNameList, - "'conversion','helmert_transformation'," - "'grid_transformation','other_transformation'," - "'concatenated_operation'"); - break; - case ObjectType::CONVERSION: - addToListString(tableNameList, "'conversion'"); - break; - case ObjectType::TRANSFORMATION: - addToListString(tableNameList, - "'helmert_transformation'," - "'grid_transformation','other_transformation'"); - break; - case ObjectType::CONCATENATED_OPERATION: - addToListString(tableNameList, "'concatenated_operation'"); - break; + const auto getTableNameConstraint = [&allowedObjectTypes]( + const std::string &colName) { + if (allowedObjectTypes.empty()) { + return colName + " IN (" + "'prime_meridian','ellipsoid','geodetic_datum'," + "'vertical_datum','geodetic_crs','projected_crs'," + "'vertical_crs','compound_crs','conversion'," + "'helmert_transformation','grid_transformation'," + "'other_transformation','concatenated_operation'" + ")"; + } else { + std::string tableNameList; + std::string otherConditions; + for (const auto type : allowedObjectTypes) { + switch (type) { + case ObjectType::PRIME_MERIDIAN: + addToListString(tableNameList, "'prime_meridian'"); + break; + case ObjectType::ELLIPSOID: + addToListString(tableNameList, "'ellipsoid'"); + break; + case ObjectType::DATUM: + addToListString(tableNameList, + "'geodetic_datum','vertical_datum'"); + break; + case ObjectType::GEODETIC_REFERENCE_FRAME: + addToListString(tableNameList, "'geodetic_datum'"); + break; + case ObjectType::VERTICAL_REFERENCE_FRAME: + addToListString(tableNameList, "'vertical_datum'"); + break; + case ObjectType::CRS: + addToListString(tableNameList, + "'geodetic_crs','projected_crs'," + "'vertical_crs','compound_crs'"); + break; + case ObjectType::GEODETIC_CRS: + addToListString(tableNameList, "'geodetic_crs'"); + break; + case ObjectType::GEOCENTRIC_CRS: + addToListStringWithOR( + otherConditions, + "(" + colName + " = " GEOCENTRIC_SINGLE_QUOTED " AND " + "type = " GEOCENTRIC_SINGLE_QUOTED ")"); + break; + case ObjectType::GEOGRAPHIC_CRS: + addToListStringWithOR(otherConditions, + "(" + colName + + " = 'geodetic_crs' AND " + "type IN (" GEOG_2D_SINGLE_QUOTED + "," GEOG_3D_SINGLE_QUOTED "))"); + break; + case ObjectType::GEOGRAPHIC_2D_CRS: + addToListStringWithOR( + otherConditions, + "(" + colName + " = 'geodetic_crs' AND " + "type = " GEOG_2D_SINGLE_QUOTED ")"); + break; + case ObjectType::GEOGRAPHIC_3D_CRS: + addToListStringWithOR( + otherConditions, + "(" + colName + " = 'geodetic_crs' AND " + "type = " GEOG_3D_SINGLE_QUOTED ")"); + break; + case ObjectType::PROJECTED_CRS: + addToListString(tableNameList, "'projected_crs'"); + break; + case ObjectType::VERTICAL_CRS: + addToListString(tableNameList, "'vertical_crs'"); + break; + case ObjectType::COMPOUND_CRS: + addToListString(tableNameList, "'compound_crs'"); + break; + case ObjectType::COORDINATE_OPERATION: + addToListString( + tableNameList, + "'conversion','helmert_transformation'," + "'grid_transformation','other_transformation'," + "'concatenated_operation'"); + break; + case ObjectType::CONVERSION: + addToListString(tableNameList, "'conversion'"); + break; + case ObjectType::TRANSFORMATION: + addToListString( + tableNameList, + "'helmert_transformation'," + "'grid_transformation','other_transformation'"); + break; + case ObjectType::CONCATENATED_OPERATION: + addToListString(tableNameList, "'concatenated_operation'"); + break; + } } - } - if (!tableNameList.empty()) { - sql += "((table_name IN ("; - sql += tableNameList; - sql += "))"; - if (!otherConditions.empty()) { - sql += " OR "; - sql += otherConditions; + std::string l_sql; + if (!tableNameList.empty()) { + l_sql = "((" + colName + " IN ("; + l_sql += tableNameList; + l_sql += "))"; + if (!otherConditions.empty()) { + l_sql += " OR "; + l_sql += otherConditions; + } + l_sql += ')'; + } else if (!otherConditions.empty()) { + l_sql = "("; + l_sql += otherConditions; + l_sql += ')'; } - sql += ')'; - } else if (!otherConditions.empty()) { - sql += "("; - sql += otherConditions; - sql += ')'; + return l_sql; } + }; + + sql += getTableNameConstraint("table_name"); + + sql += " UNION SELECT ov.table_name AS table_name, " + "ov.auth_name AS auth_name, " + "ov.code AS code, a.alt_name AS name, " + "ov.deprecated AS deprecated FROM object_view ov " + "JOIN alias_name a ON ov.table_name = a.table_name AND " + "ov.auth_name = a.auth_name AND ov.code = a.code WHERE "; + if (deprecated) { + sql += "ov.deprecated = 1 AND "; + } + if (!approximateMatch) { + sql += "a.alt_name LIKE ? AND "; + params.push_back(searchedNameWithoutDeprecated); + } + if (d->hasAuthorityRestriction()) { + sql += "ov.auth_name = ? AND "; + params.emplace_back(d->authority()); } - sql += " ORDER BY deprecated, length(name), name"; + sql += getTableNameConstraint("ov.table_name"); + + sql += ") ORDER BY deprecated, length(name), name"; if (limitResultCount > 0 && limitResultCount < static_cast(std::numeric_limits::max()) && @@ -5417,6 +5450,7 @@ AuthorityFactory::createObjectsFromName( } std::list res; + std::set> setIdentified; // Querying geodetic datum is a super hot path when importing from WKT1 // so cache results. @@ -5444,6 +5478,12 @@ AuthorityFactory::createObjectsFromName( for (const auto &row : listOfRow) { const auto &auth_name = row[1]; const auto &code = row[2]; + const auto key = + std::pair(auth_name, code); + if (setIdentified.find(key) != setIdentified.end()) { + continue; + } + setIdentified.insert(key); auto factory = d->createFactory(auth_name); res.emplace_back(factory->createGeodeticDatum(code)); if (limitResultCount > 0 && res.size() == limitResultCount) { @@ -5469,6 +5509,12 @@ AuthorityFactory::createObjectsFromName( const auto &auth_name = row[1]; const auto &code = row[2]; + const auto key = + std::pair(auth_name, code); + if (setIdentified.find(key) != setIdentified.end()) { + continue; + } + setIdentified.insert(key); auto factory = d->createFactory(auth_name); res.emplace_back(factory->createGeodeticDatum(code)); if (limitResultCount > 0 && @@ -5504,6 +5550,12 @@ AuthorityFactory::createObjectsFromName( const auto &table_name = row[0]; const auto &auth_name = row[1]; const auto &code = row[2]; + const auto key = + std::pair(auth_name, code); + if (setIdentified.find(key) != setIdentified.end()) { + continue; + } + setIdentified.insert(key); const auto &deprecatedStr = row[4]; if (isFirst) { firstIsDeprecated = deprecatedStr == "1"; diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index a67238d9..c013ce24 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -2051,12 +2051,27 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame( auto res = authFactory->createObjectsFromName( name, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true, 1); - bool foundDatumName = false; if (!res.empty()) { + bool foundDatumName = false; const auto &refDatum = res.front(); if (metadata::Identifier::isEquivalentName( name.c_str(), refDatum->nameStr().c_str())) { foundDatumName = true; + } else if (refDatum->identifiers().size() == 1) { + const auto &id = refDatum->identifiers()[0]; + const auto aliases = + authFactory->databaseContext()->getAliases( + *id->codeSpace(), id->code(), refDatum->nameStr(), + "geodetic_datum", std::string()); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName( + name.c_str(), alias.c_str())) { + foundDatumName = true; + break; + } + } + } + if (foundDatumName) { properties.set(IdentifiedObject::NAME_KEY, refDatum->nameStr()); if (!properties.get(Identifier::CODESPACE_KEY) && @@ -2083,25 +2098,12 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame( NN_NO_CHECK(dbContext_), *id->codeSpace()); auto dbDatum = authFactory2->createGeodeticDatum(id->code()); - foundDatumName = true; properties.set(IdentifiedObject::NAME_KEY, dbDatum->nameStr()); } catch (const std::exception &) { } } } - - if (!foundDatumName) { - std::string outTableName; - std::string authNameFromAlias; - std::string codeFromAlias; - auto officialName = authFactory->getOfficialNameFromAlias( - name, "geodetic_datum", std::string(), true, outTableName, - authNameFromAlias, codeFromAlias); - if (!officialName.empty()) { - properties.set(IdentifiedObject::NAME_KEY, officialName); - } - } } } -- cgit v1.2.3 From 6426bcbd3605bf8cd6ae5c7869931fa89a26d641 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 7 Jan 2020 15:32:47 +0100 Subject: projinfo: support -k datum --- src/apps/projinfo.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index c1139564..09b17b31 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -78,7 +78,7 @@ struct OutputOptions { static void usage() { std::cerr - << "usage: projinfo [-o formats] [-k crs|operation|ellipsoid] " + << "usage: projinfo [-o formats] [-k crs|operation|datum|ellipsoid] " "[--summary] [-q]" << std::endl << " ([--area name_or_code] | " @@ -184,6 +184,9 @@ static BaseObjectNNPtr buildObject( } else if (kind == "ellipsoid" && tokens.size() == 2) { auto urn = "urn:ogc:def:ellipsoid:" + tokens[0] + "::" + tokens[1]; obj = createFromUserInput(urn, dbContext).as_nullable(); + } else if (kind == "datum" && tokens.size() == 2) { + auto urn = "urn:ogc:def:datum:" + tokens[0] + "::" + tokens[1]; + obj = createFromUserInput(urn, dbContext).as_nullable(); } else { // Convenience to be able to use C escaped strings... if (l_user_string.size() > 2 && l_user_string[0] == '"' && @@ -205,6 +208,59 @@ static BaseObjectNNPtr buildObject( } } } + } else if (dbContext && !kind.empty() && kind != "crs" && + l_user_string.find(':') == std::string::npos) { + std::vector allowedTypes; + if (kind == "operation") + allowedTypes.push_back( + AuthorityFactory::ObjectType::COORDINATE_OPERATION); + else if (kind == "ellipsoid") + allowedTypes.push_back( + AuthorityFactory::ObjectType::ELLIPSOID); + else if (kind == "datum") + allowedTypes.push_back(AuthorityFactory::ObjectType::DATUM); + constexpr size_t limitResultCount = 10; + auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), + std::string()); + for (int pass = 0; pass <= 1; ++pass) { + const bool approximateMatch = (pass == 1); + auto res = factory->createObjectsFromName( + l_user_string, allowedTypes, approximateMatch, + limitResultCount); + if (res.size() == 1) { + obj = res.front().as_nullable(); + } else { + for (const auto &l_obj : res) { + if (Identifier::isEquivalentName( + l_obj->nameStr().c_str(), + l_user_string.c_str())) { + obj = l_obj.as_nullable(); + break; + } + } + if (obj) { + break; + } + } + if (res.size() > 1) { + std::string msg("several objects matching this name: "); + bool first = true; + for (const auto &l_obj : res) { + if (msg.size() > 200) { + msg += ", ..."; + break; + } + if (!first) { + msg += ", "; + } + first = false; + msg += l_obj->nameStr(); + } + std::cerr << context << ": " << msg << std::endl; + std::exit(1); + } + } + } else { obj = createFromUserInput(l_user_string, dbContext).as_nullable(); @@ -863,6 +919,8 @@ int main(int argc, char **argv) { objectKind = "operation"; } else if (ci_equal(kind, "ellipsoid")) { objectKind = "ellipsoid"; + } else if (ci_equal(kind, "datum")) { + objectKind = "datum"; } else { std::cerr << "Unrecognized value for option -k: " << kind << std::endl; -- cgit v1.2.3 From 18ba7b3dfcf1142227eacd8e76d4086a3d8292f4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 7 Jan 2020 21:38:05 +0100 Subject: projinfo: use No. abbreviation instead of UTF-8 character (fixes #1828) --- src/apps/projinfo.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index c1139564..8ce9eefe 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -686,9 +686,7 @@ static void outputOperations( } first = false; std::cout << "-------------------------------------" << std::endl; - std::cout << "Operation n" - "\xC2\xB0" - << (i + 1) << ":" << std::endl + std::cout << "Operation No. " << (i + 1) << ":" << std::endl << std::endl; outputOperationSummary(op, dbContext); std::cout << std::endl; -- cgit v1.2.3 From acb2bd68bcfda90eb1f1d0bc12debaf9d051d6b9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 10 Jan 2020 00:34:36 +0100 Subject: Doc of CRS::identify(): make it clear that this is a best-effort attempt only --- src/iso19111/c_api.cpp | 4 ++++ src/iso19111/crs.cpp | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 7d4f5eba..9f73f8e9 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -2192,6 +2192,10 @@ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) { * The candidate CRSs are either hard-coded, or looked in the database when * it is available. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index cf90c388..a24508d4 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -730,6 +730,10 @@ CRSNNPtr CRS::normalizeForVisualization() const { * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. @@ -1648,6 +1652,10 @@ static bool hasCodeCompatibleOfAuthorityFactory( * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match: *
    @@ -2746,6 +2754,10 @@ bool VerticalCRS::_isEquivalentTo( * The candidate CRSs are looked in the database when * authorityFactory is not null. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. * 100% means that the name of the reference entry @@ -3529,6 +3541,10 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter, * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. @@ -4050,6 +4066,10 @@ bool CompoundCRS::_isEquivalentTo( * The candidate CRSs are looked in the database when * authorityFactory is not null. * + * Note that the implementation uses a set of heuristics to have a good + * compromise of successful identifications over execution time. It might miss + * legitimate matches in some circumstances. + * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. -- cgit v1.2.3 From 863d118c6d49783ed60792b4fd021a59988adbe4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 10 Jan 2020 18:31:31 +0100 Subject: createOperations(): fix dealing with projected 3D CRS whose Z units != metre --- src/iso19111/crs.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index a24508d4..d71d527e 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -3456,23 +3456,33 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter, bool axisSpecFound) const { const auto &axisList = d->coordinateSystem()->axisList(); const auto &unit = axisList[0]->unit(); + const auto *zUnit = axisList.size() == 3 ? &(axisList[2]->unit()) : nullptr; if (!unit._isEquivalentTo(common::UnitOfMeasure::METRE, - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT) || + (zUnit && + !zUnit->_isEquivalentTo(common::UnitOfMeasure::METRE, + util::IComparable::Criterion::EQUIVALENT))) { auto projUnit = unit.exportToPROJString(); const double toSI = unit.conversionToSI(); if (!formatter->getCRSExport()) { formatter->addStep("unitconvert"); formatter->addParam("xy_in", "m"); - if (!formatter->omitZUnitConversion()) + if (zUnit) formatter->addParam("z_in", "m"); + if (projUnit.empty()) { formatter->addParam("xy_out", toSI); - if (!formatter->omitZUnitConversion()) - formatter->addParam("z_out", toSI); } else { formatter->addParam("xy_out", projUnit); - if (!formatter->omitZUnitConversion()) - formatter->addParam("z_out", projUnit); + } + if (zUnit) { + auto projZUnit = zUnit->exportToPROJString(); + const double zToSI = zUnit->conversionToSI(); + if (projZUnit.empty()) { + formatter->addParam("z_out", zToSI); + } else { + formatter->addParam("z_out", projZUnit); + } } } else { if (projUnit.empty()) { -- cgit v1.2.3 From 30205fef7c4a73342a384eca40893a52ea9d6794 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 10 Jan 2020 19:04:32 +0100 Subject: CompoundCRS::identify(): avoid exception when horiz/vertical part is a BoundCRS The exception only affects C++ users. It was caught by the C layer. --- src/iso19111/factory.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 33fce593..0c7692ad 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -5803,7 +5803,9 @@ static std::string buildSqlLookForAuthNameCode( std::set authorities; for (const auto &crs : list) { - const auto &ids = crs.first->identifiers(); + auto boundCRS = dynamic_cast(crs.first.get()); + const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers() + : crs.first->identifiers(); if (!ids.empty()) { authorities.insert(*(ids[0]->codeSpace())); } @@ -5822,7 +5824,9 @@ static std::string buildSqlLookForAuthNameCode( params.emplace_back(auth_name); bool firstGeodCRSForAuth = true; for (const auto &crs : list) { - const auto &ids = crs.first->identifiers(); + auto boundCRS = dynamic_cast(crs.first.get()); + const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers() + : crs.first->identifiers(); if (!ids.empty() && *(ids[0]->codeSpace()) == auth_name) { if (!firstGeodCRSForAuth) { sql += ','; -- cgit v1.2.3 From d2674e044505d0fcee920f5c0315d8db6b842caa Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 11 Jan 2020 00:25:51 +0100 Subject: WKT1_GDAL export: limit datum name massaging to names matching EPSG (fixes #1835) --- src/iso19111/datum.cpp | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index 255f9822..d9d9c261 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -1201,7 +1201,8 @@ void GeodeticReferenceFrame::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; - formatter->startNode(io::WKTConstants::DATUM, !identifiers().empty()); + const auto &ids = identifiers(); + formatter->startNode(io::WKTConstants::DATUM, !ids.empty()); auto l_name = nameStr(); if (l_name.empty()) { l_name = "unnamed"; @@ -1236,10 +1237,35 @@ void GeodeticReferenceFrame::_exportToWKT( } } } - // Replace spaces by underscore, except if it is a special MapInfo - // datum name - } else if (!starts_with(l_name, "MIF ")) { - l_name = io::WKTFormatter::morphNameToESRI(l_name); + } else { + // Replace spaces by underscore for datum names coming from EPSG + // so as to emulate GDAL < 3 importFromEPSG() + if (ids.size() == 1 && *(ids.front()->codeSpace()) == "EPSG") { + l_name = io::WKTFormatter::morphNameToESRI(l_name); + } else if (ids.empty()) { + const auto &dbContext = formatter->databaseContext(); + if (dbContext) { + auto factory = io::AuthorityFactory::create( + NN_NO_CHECK(dbContext), std::string()); + // We use anonymous autority and approximate matching, so + // as to trigger the caching done in createObjectsFromName() + // in that case. + auto matches = factory->createObjectsFromName( + l_name, {io::AuthorityFactory::ObjectType:: + GEODETIC_REFERENCE_FRAME}, + true, 2); + if (matches.size() == 1) { + const auto &match = matches.front(); + const auto &matchId = match->identifiers(); + if (matchId.size() == 1 && + *(matchId.front()->codeSpace()) == "EPSG" && + metadata::Identifier::isEquivalentName( + l_name.c_str(), match->nameStr().c_str())) { + l_name = io::WKTFormatter::morphNameToESRI(l_name); + } + } + } + } if (l_name == "World_Geodetic_System_1984") { l_name = "WGS_1984"; } -- cgit v1.2.3 From 3f3af95e0a7f421c84617ce21a2bae1c01368397 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 13 Jan 2020 18:09:35 +0100 Subject: unitconvert with mjd time format: avoid potential integer overflow Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20072 --- src/conversions/unitconvert.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/conversions/unitconvert.cpp b/src/conversions/unitconvert.cpp index 8472355d..f8439aee 100644 --- a/src/conversions/unitconvert.cpp +++ b/src/conversions/unitconvert.cpp @@ -164,7 +164,8 @@ static double decimalyear_to_mjd(double decimalyear) { double fractional_year; double mjd; - if( decimalyear < -10000 || decimalyear > 10000 ) + // Written this way to deal with NaN input + if( !(decimalyear >= -10000 && decimalyear <= 10000) ) return 0; year = lround(floor(decimalyear)); -- cgit v1.2.3 From a955947c1f3279e51d1272df0cb6aa61ce4b85ee Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Mon, 13 Jan 2020 22:04:01 +0100 Subject: Remove -ld option from proj and cs2cs It promotes use of deprecated paramters +datum and +towgs84 which we don't want to encourage. Closes #1308 --- src/apps/cs2cs.cpp | 13 ------------- src/apps/proj.cpp | 11 ----------- 2 files changed, 24 deletions(-) (limited to 'src') diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index 273233a7..6c85d4aa 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -430,19 +430,6 @@ int main(int argc, char **argv) { for (lu = proj_list_units(); lu->id; ++lu) (void)printf("%12s %-20s %s\n", lu->id, lu->to_meter, lu->name); - } else if (arg[1] == 'd') { /* list datums */ - const struct PJ_DATUMS *ld; - - printf("__datum_id__ __ellipse___ " - "__definition/" - "comments______________________________\n"); - for (ld = pj_get_datums_ref(); ld->id; ++ld) { - printf("%12s %-12s %-30s\n", ld->id, ld->ellipse_id, - ld->defn); - if (ld->comments != nullptr && - strlen(ld->comments) > 0) - printf("%25s %s\n", " ", ld->comments); - } } else if (arg[1] == 'm') { /* list prime meridians */ const struct PJ_PRIME_MERIDIANS *lpm; diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp index 09c8a81d..852cea04 100644 --- a/src/apps/proj.cpp +++ b/src/apps/proj.cpp @@ -385,17 +385,6 @@ int main(int argc, char **argv) { for (lu = proj_list_units(); lu->id ; ++lu) (void)printf("%12s %-20s %s\n", lu->id, lu->to_meter, lu->name); - } else if (arg[1] == 'd') { /* list datums */ - const struct PJ_DATUMS *ld; - - printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" ); - for (ld = pj_get_datums_ref(); ld->id ; ++ld) - { - printf("%12s %-12s %-30s\n", - ld->id, ld->ellipse_id, ld->defn); - if( ld->comments != nullptr && strlen(ld->comments) > 0 ) - printf( "%25s %s\n", " ", ld->comments ); - } } else emess(1,"invalid list option: l%c",arg[1]); exit(0); -- cgit v1.2.3