aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/sql/customizations.sql18
-rw-r--r--data/sql/proj_db_table_defs.sql9
-rw-r--r--include/proj/io.hpp5
-rw-r--r--src/c_api.cpp10
-rw-r--r--src/coordinateoperation.cpp100
-rw-r--r--src/crs.cpp135
-rw-r--r--src/factory.cpp62
-rw-r--r--test/cli/testprojinfo_out.dist53
-rw-r--r--test/unit/test_c_api.cpp6
-rw-r--r--test/unit/test_crs.cpp5
-rw-r--r--test/unit/test_operation.cpp8
11 files changed, 265 insertions, 146 deletions
diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql
index 2181cc2b..8e31c233 100644
--- a/data/sql/customizations.sql
+++ b/data/sql/customizations.sql
@@ -6,3 +6,21 @@ INSERT INTO "other_transformation" VALUES('PROJ','CRS84_TO_EPSG_4326','OGC:CRS84
-- alias of EPSG:3857
INSERT INTO "projected_crs" VALUES('EPSG','900913','Google Maps Global Mercator',NULL,NULL,'EPSG','4499','EPSG','4326','EPSG','3856','EPSG','3544',NULL,1);
+
+-- Define the allowed authorities, and their precedence, when researching a
+-- coordinate operation
+
+INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES
+ ('any', 'EPSG', 'PROJ,EPSG,any' );
+
+INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES
+ ('EPSG', 'EPSG', 'PROJ,EPSG' );
+
+INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES
+ ('PROJ', 'EPSG', 'PROJ,EPSG' );
+
+INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES
+ ('IGNF', 'EPSG', 'PROJ,IGNF,EPSG' );
+
+INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES
+ ('ESRI', 'EPSG', 'PROJ,ESRI,EPSG' ); \ No newline at end of file
diff --git a/data/sql/proj_db_table_defs.sql b/data/sql/proj_db_table_defs.sql
index 553dab38..c63637cf 100644
--- a/data/sql/proj_db_table_defs.sql
+++ b/data/sql/proj_db_table_defs.sql
@@ -1048,3 +1048,12 @@ CREATE VIEW authority_list AS
UNION
SELECT DISTINCT auth_name FROM coordinate_operation_view
;
+
+-- Define the allowed authorities, and their precedence, when researching a
+-- coordinate operation
+CREATE TABLE authority_to_authority_preference(
+ source_auth_name TEXT NOT NULL, -- 'any' for any source
+ target_auth_name TEXT NOT NULL, -- 'any' for any target
+ allowed_authorities TEXT NOT NULL, -- for example 'PROJ,EPSG,any'
+ CONSTRAINT unique_authority_to_authority_preference UNIQUE (source_auth_name, target_auth_name)
+);
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index 26420150..f511bf5b 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -731,6 +731,11 @@ class PROJ_GCC_DLL DatabaseContext {
PROJ_INTERNAL std::string getTextDefinition(const std::string &tableName,
const std::string &authName,
const std::string &code) const;
+
+ PROJ_INTERNAL std::vector<std::string>
+ getAllowedAuthorities(const std::string &sourceAuthName,
+ const std::string &targetAuthName) const;
+
//! @endcond
protected:
diff --git a/src/c_api.cpp b/src/c_api.cpp
index fed91750..e1f34012 100644
--- a/src/c_api.cpp
+++ b/src/c_api.cpp
@@ -5354,9 +5354,17 @@ struct PJ_OPERATION_FACTORY_CONTEXT {
* The returned object must be unreferenced with
* proj_operation_factory_context_unref() after use.
*
+ * If authority is NULL or the empty string, then coordinate
+ * operations from any authority will be searched, with the restrictions set
+ * in the authority_to_authority_preference database table.
+ * If authority is set to "any", then coordinate
+ * operations from any authority will be searched
+ * If authority is a non-empty string different of "any",
+ * then coordinate operatiosn will be searched only in that authority namespace.
+ *
* @param ctx Context, or NULL for default context.
* @param authority Name of authority to which to restrict the search of
- * canidate operations. Or NULL to allow any authority.
+ * candidate operations.
* @return Object that must be unreferenced with
* proj_operation_factory_context_unref(), or NULL in
* case of error.
diff --git a/src/coordinateoperation.cpp b/src/coordinateoperation.cpp
index 04f9bc9a..d187f2cc 100644
--- a/src/coordinateoperation.cpp
+++ b/src/coordinateoperation.cpp
@@ -8937,6 +8937,14 @@ CoordinateOperationContext::getIntermediateCRS() const {
* If a non null authorityFactory is provided, the resulting context should
* not be used simultaneously by more than one thread.
*
+ * If authorityFactory->getAuthority() is the empty string, then coordinate
+ * operations from any authority will be searched, with the restrictions set
+ * in the authority_to_authority_preference database table.
+ * If authorityFactory->getAuthority() is set to "any", then coordinate
+ * operations from any authority will be searched
+ * If authorityFactory->getAuthority() is a non-empty string different of "any",
+ * then coordinate operatiosn will be searched only in that authority namespace.
+ *
* @param authorityFactory Authority factory, or null if no database lookup
* is allowed.
* Use io::authorityFactory::create(context, std::string()) to allow all
@@ -9663,6 +9671,8 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS,
const CoordinateOperationContextNNPtr &context) {
const auto &authFactory = context->getAuthorityFactory();
assert(authFactory);
+ const auto &authFactoryName = authFactory->getAuthority();
+
for (const auto &idSrc : sourceCRS->identifiers()) {
const auto &srcAuthName = *(idSrc->codeSpace());
const auto &srcCode = idSrc->code();
@@ -9671,17 +9681,39 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS,
const auto &targetAuthName = *(idTarget->codeSpace());
const auto &targetCode = idTarget->code();
if (!targetAuthName.empty()) {
- auto res =
- authFactory->createFromCoordinateReferenceSystemCodes(
- srcAuthName, srcCode, targetAuthName, targetCode,
- context->getUsePROJAlternativeGridNames(),
- context->getGridAvailabilityUse() ==
- CoordinateOperationContext::
- GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
- context->getDiscardSuperseded());
- if (!res.empty()) {
- return res;
+ std::vector<std::string> authorities;
+ if (authFactoryName == "any") {
+ authorities.emplace_back();
+ }
+ if (authFactoryName.empty()) {
+ authorities = authFactory->databaseContext()
+ ->getAllowedAuthorities(
+ srcAuthName, targetAuthName);
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ } else {
+ authorities.emplace_back(authFactoryName);
+ }
+ for (const auto &authority : authorities) {
+ const auto tmpAuthFactory =
+ io::AuthorityFactory::create(
+ authFactory->databaseContext(),
+ authority == "any" ? std::string() : authority);
+ auto res =
+ tmpAuthFactory
+ ->createFromCoordinateReferenceSystemCodes(
+ srcAuthName, srcCode, targetAuthName,
+ targetCode,
+ context->getUsePROJAlternativeGridNames(),
+ context->getGridAvailabilityUse() ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID,
+ context->getDiscardSuperseded());
+ if (!res.empty()) {
+ return res;
+ }
}
}
}
@@ -9706,6 +9738,8 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
const auto &authFactory = context->getAuthorityFactory();
assert(authFactory);
+ const auto &authFactoryName = authFactory->getAuthority();
+
for (const auto &idSrc : sourceCRS->identifiers()) {
const auto &srcAuthName = *(idSrc->codeSpace());
const auto &srcCode = idSrc->code();
@@ -9714,16 +9748,40 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
const auto &targetAuthName = *(idTarget->codeSpace());
const auto &targetCode = idTarget->code();
if (!targetAuthName.empty()) {
- auto res = authFactory->createFromCRSCodesWithIntermediates(
- srcAuthName, srcCode, targetAuthName, targetCode,
- context->getUsePROJAlternativeGridNames(),
- context->getGridAvailabilityUse() ==
- CoordinateOperationContext::GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
- context->getDiscardSuperseded(),
- context->getIntermediateCRS());
- if (!res.empty()) {
- return res;
+ std::vector<std::string> authorities;
+ if (authFactoryName == "any") {
+ authorities.emplace_back();
+ }
+ if (authFactoryName.empty()) {
+ authorities = authFactory->databaseContext()
+ ->getAllowedAuthorities(
+ srcAuthName, targetAuthName);
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ } else {
+ authorities.emplace_back(authFactoryName);
+ }
+ for (const auto &authority : authorities) {
+ const auto tmpAuthFactory =
+ io::AuthorityFactory::create(
+ authFactory->databaseContext(),
+ authority == "any" ? std::string() : authority);
+
+ auto res =
+ tmpAuthFactory->createFromCRSCodesWithIntermediates(
+ srcAuthName, srcCode, targetAuthName,
+ targetCode,
+ context->getUsePROJAlternativeGridNames(),
+ context->getGridAvailabilityUse() ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID,
+ context->getDiscardSuperseded(),
+ context->getIntermediateCRS());
+ if (!res.empty()) {
+ return res;
+ }
}
}
}
diff --git a/src/crs.cpp b/src/crs.cpp
index 546cfb0a..6212f561 100644
--- a/src/crs.cpp
+++ b/src/crs.cpp
@@ -409,74 +409,97 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
} else {
geodCRS = geogCRS;
}
- auto l_domains = domains();
+
+ if (!dbContext) {
+ return thisAsCRS;
+ }
+
+ const auto &l_domains = domains();
metadata::ExtentPtr extent;
if (!l_domains.empty()) {
extent = l_domains[0]->domainOfValidity();
}
- try {
- auto authFactory = dbContext
- ? io::AuthorityFactory::create(
- NN_NO_CHECK(dbContext), std::string())
- .as_nullable()
- : nullptr;
- auto ctxt = operation::CoordinateOperationContext::create(authFactory,
- extent, 0.0);
- // ctxt->setSpatialCriterion(
- // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
- auto list =
- operation::CoordinateOperationFactory::create()->createOperations(
- NN_NO_CHECK(geodCRS), hubCRS, ctxt);
- for (const auto &op : list) {
- auto transf =
- util::nn_dynamic_pointer_cast<operation::Transformation>(op);
- if (transf) {
- try {
- transf->getTOWGS84Parameters();
- } catch (const std::exception &) {
- continue;
- }
- return util::nn_static_pointer_cast<CRS>(
- BoundCRS::create(thisAsCRS, hubCRS, NN_NO_CHECK(transf)));
- } else {
- auto concatenated =
- dynamic_cast<const operation::ConcatenatedOperation *>(
- op.get());
- if (concatenated) {
- // Case for EPSG:4807 / "NTF (Paris)" that is made of a
- // longitude rotation followed by a Helmert
- // The prime meridian shift will be accounted elsewhere
- const auto &subops = concatenated->operations();
- if (subops.size() == 2) {
- auto firstOpIsTransformation =
- dynamic_cast<const operation::Transformation *>(
- subops[0].get());
- auto firstOpIsConversion =
- dynamic_cast<const operation::Conversion *>(
- subops[0].get());
- if ((firstOpIsTransformation &&
- firstOpIsTransformation->isLongitudeRotation()) ||
- (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) &&
- firstOpIsConversion)) {
- transf = util::nn_dynamic_pointer_cast<
- operation::Transformation>(subops[1]);
- if (transf) {
- try {
- transf->getTOWGS84Parameters();
- } catch (const std::exception &) {
- continue;
+ std::string crs_authority;
+ const auto &l_identifiers = identifiers();
+ // If the object has an authority, restrict the transformations to
+ // come from that codespace too. This avoids for example EPSG:4269
+ // (NAD83) to use a (dubious) ESRI transformation.
+ if (!l_identifiers.empty()) {
+ crs_authority = *(l_identifiers[0]->codeSpace());
+ }
+
+ auto authorities = dbContext->getAllowedAuthorities(crs_authority, "EPSG");
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ for (const auto &authority : authorities) {
+ try {
+
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext),
+ authority == "any" ? std::string() : authority);
+ auto ctxt = operation::CoordinateOperationContext::create(
+ authFactory, extent, 0.0);
+ // ctxt->setSpatialCriterion(
+ // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
+ auto list =
+ operation::CoordinateOperationFactory::create()
+ ->createOperations(NN_NO_CHECK(geodCRS), hubCRS, ctxt);
+ for (const auto &op : list) {
+ auto transf =
+ util::nn_dynamic_pointer_cast<operation::Transformation>(
+ op);
+ if (transf && !starts_with(transf->nameStr(), "Null geo")) {
+ try {
+ transf->getTOWGS84Parameters();
+ } catch (const std::exception &) {
+ continue;
+ }
+ return util::nn_static_pointer_cast<CRS>(BoundCRS::create(
+ thisAsCRS, hubCRS, NN_NO_CHECK(transf)));
+ } else {
+ auto concatenated =
+ dynamic_cast<const operation::ConcatenatedOperation *>(
+ op.get());
+ if (concatenated) {
+ // Case for EPSG:4807 / "NTF (Paris)" that is made of a
+ // longitude rotation followed by a Helmert
+ // The prime meridian shift will be accounted elsewhere
+ const auto &subops = concatenated->operations();
+ if (subops.size() == 2) {
+ auto firstOpIsTransformation =
+ dynamic_cast<const operation::Transformation *>(
+ subops[0].get());
+ auto firstOpIsConversion =
+ dynamic_cast<const operation::Conversion *>(
+ subops[0].get());
+ if ((firstOpIsTransformation &&
+ firstOpIsTransformation
+ ->isLongitudeRotation()) ||
+ (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) &&
+ firstOpIsConversion)) {
+ transf = util::nn_dynamic_pointer_cast<
+ operation::Transformation>(subops[1]);
+ if (transf &&
+ !starts_with(transf->nameStr(),
+ "Null geo")) {
+ try {
+ transf->getTOWGS84Parameters();
+ } catch (const std::exception &) {
+ continue;
+ }
+ return util::nn_static_pointer_cast<CRS>(
+ BoundCRS::create(thisAsCRS, hubCRS,
+ NN_NO_CHECK(transf)));
}
- return util::nn_static_pointer_cast<CRS>(
- BoundCRS::create(thisAsCRS, hubCRS,
- NN_NO_CHECK(transf)));
}
}
}
}
}
+ } catch (const std::exception &) {
}
- } catch (const std::exception &) {
}
return thisAsCRS;
}
diff --git a/src/factory.cpp b/src/factory.cpp
index e24cee58..20701def 100644
--- a/src/factory.cpp
+++ b/src/factory.cpp
@@ -880,6 +880,44 @@ std::string DatabaseContext::getTextDefinition(const std::string &tableName,
return res[0][0];
}
+// ---------------------------------------------------------------------------
+
+/** \brief Return the allowed authorities when researching transformations
+ * between different authorities.
+ *
+ * @throw FactoryException
+ */
+std::vector<std::string> DatabaseContext::getAllowedAuthorities(
+ const std::string &sourceAuthName,
+ const std::string &targetAuthName) const {
+ auto res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = ? AND target_auth_name = ?",
+ {sourceAuthName, targetAuthName});
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = ? AND target_auth_name = 'any'",
+ {sourceAuthName});
+ }
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = 'any' AND target_auth_name = ?",
+ {targetAuthName});
+ }
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = 'any' AND target_auth_name = 'any'",
+ {});
+ }
+ if (res.empty()) {
+ return std::vector<std::string>();
+ }
+ return split(res[0][0], ',');
+}
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -947,6 +985,10 @@ struct AuthorityFactory::Private {
SQLResultSet runWithCodeParam(const char *sql, const std::string &code);
+ bool hasAuthorityRestriction() const {
+ return !authority_.empty() && authority_ != "any";
+ }
+
private:
DatabaseContextNNPtr context_;
std::string authority_;
@@ -3097,7 +3139,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
"= ? AND auth_name = ? AND code = ? AND deprecated != 1");
auto params = std::vector<SQLValues>{sourceCRSAuthName, sourceCRSCode,
targetCRSAuthName, targetCRSCode};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND conversion_auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -3134,7 +3176,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
}
params = {sourceCRSAuthName, sourceCRSCode, targetCRSAuthName,
targetCRSCode};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND cov.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -3361,7 +3403,7 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
"AND v1.deprecated = 0 AND v2.deprecated = 0 "
"AND intersects_bbox(south_lat1, west_lon1, north_lat1, east_lon1, "
"south_lat2, west_lon2, north_lat2, east_lon2) == 1 ");
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? ";
params.emplace_back(getAuthority());
params.emplace_back(getAuthority());
@@ -3852,7 +3894,7 @@ AuthorityFactory::createObjectsFromName(
sql += "name LIKE ? AND ";
params.push_back(searchedNameWithoutDeprecated);
}
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " auth_name = ? AND ";
params.emplace_back(getAuthority());
}
@@ -4152,7 +4194,7 @@ AuthorityFactory::listAreaOfUseFromName(const std::string &name,
std::string sql(
"SELECT auth_name, code FROM area WHERE deprecated = 0 AND ");
std::vector<SQLValues> params;
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " auth_name = ? AND ";
params.emplace_back(getAuthority());
}
@@ -4206,7 +4248,7 @@ std::list<crs::GeodeticCRSNNPtr> AuthorityFactory::createGeodeticCRSFromDatum(
"SELECT auth_name, code FROM geodetic_crs WHERE "
"datum_auth_name = ? AND datum_code = ? AND deprecated = 0");
std::vector<SQLValues> params{datum_auth_name, datum_code};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4242,7 +4284,7 @@ AuthorityFactory::createGeodeticCRSFromEllipsoid(
"geodetic_datum.deprecated = 0 AND "
"geodetic_crs.deprecated = 0");
std::vector<SQLValues> params{ellipsoid_auth_name, ellipsoid_code};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND geodetic_crs.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4363,7 +4405,7 @@ AuthorityFactory::createProjectedCRSFromExisting(
sql += "conversion.method_auth_name = 'EPSG' AND "
"conversion.method_code = ?";
params.emplace_back(toString(methodEPSGCode));
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND projected_crs.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4533,7 +4575,7 @@ AuthorityFactory::createProjectedCRSFromExisting(
params.emplace_back(patternVal);
}
sql += ")";
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4597,7 +4639,7 @@ AuthorityFactory::createCompoundCRSFromExisting(
"vertical_crs_");
addAnd = true;
}
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
if (addAnd) {
sql += " AND ";
}
diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist
index f3d4854a..4a760c67 100644
--- a/test/cli/testprojinfo_out.dist
+++ b/test/cli/testprojinfo_out.dist
@@ -110,12 +110,11 @@ CONVERSION["UTM zone 31N",
ID["EPSG",16031]]
Testing projinfo -s NAD27 -t NAD83 --grid-check none --spatial-test intersects --summary
-Candidate operations found: 8
+Candidate operations found: 7
DERIVED_FROM(EPSG):1312, NAD27 to NAD83 (3), 1.0 m, Canada
DERIVED_FROM(EPSG):1313, NAD27 to NAD83 (4), 1.5 m, Canada - NAD27
DERIVED_FROM(EPSG):1241, NAD27 to NAD83 (1), 0.15 m, USA - CONUS including EEZ
DERIVED_FROM(EPSG):1243, NAD27 to NAD83 (2), 0.5 m, USA - Alaska including EEZ
-ESRI:108003, NAD_1927_To_NAD_1983_PR_VI, 0.05 m, Caribbean - Puerto Rico and Virgin Islands - onshore
unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World
EPSG:1462, NAD27 to NAD83 (5), 1.0 m, Canada - Quebec
EPSG:1573, NAD27 to NAD83 (6), 1.5 m, Canada - Quebec
@@ -306,52 +305,6 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)",
-------------------------------------
Operation n°5:
-ESRI:108003, NAD_1927_To_NAD_1983_PR_VI, 0.05 m, Caribbean - Puerto Rico and Virgin Islands - onshore
-
-PROJ string:
-+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=prvi +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
-
-WKT2_2015 string:
-COORDINATEOPERATION["NAD_1927_To_NAD_1983_PR_VI",
- SOURCECRS[
- GEODCRS["NAD27",
- DATUM["North American Datum 1927",
- ELLIPSOID["Clarke 1866",6378206.4,294.978698213898,
- LENGTHUNIT["metre",1]]],
- PRIMEM["Greenwich",0,
- ANGLEUNIT["degree",0.0174532925199433]],
- CS[ellipsoidal,2],
- AXIS["geodetic latitude (Lat)",north,
- ORDER[1],
- ANGLEUNIT["degree",0.0174532925199433]],
- AXIS["geodetic longitude (Lon)",east,
- ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
- TARGETCRS[
- GEODCRS["NAD83",
- DATUM["North American Datum 1983",
- ELLIPSOID["GRS 1980",6378137,298.257222101,
- LENGTHUNIT["metre",1]]],
- PRIMEM["Greenwich",0,
- ANGLEUNIT["degree",0.0174532925199433]],
- CS[ellipsoidal,2],
- AXIS["geodetic latitude (Lat)",north,
- ORDER[1],
- ANGLEUNIT["degree",0.0174532925199433]],
- AXIS["geodetic longitude (Lon)",east,
- ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
- METHOD["NTv2",
- ID["EPSG",9615]],
- PARAMETERFILE["Latitude and longitude difference file","prvi"],
- OPERATIONACCURACY[0.05],
- AREA["Caribbean - Puerto Rico and Virgin Islands - onshore"],
- BBOX[17.62,-67.97,18.78,-64.25],
- ID["ESRI",108003]]
-
--------------------------------------
-Operation n°6:
-
unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World
PROJ string:
@@ -405,7 +358,7 @@ COORDINATEOPERATION["Null geographic offset from NAD27 to NAD83",
BBOX[-90,-180,90,180]]
-------------------------------------
-Operation n°7:
+Operation n°6:
EPSG:1462, NAD27 to NAD83 (5), 1.0 m, Canada - Quebec
@@ -451,7 +404,7 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)",
ID["EPSG",1462]]
-------------------------------------
-Operation n°8:
+Operation n°7:
EPSG:1573, NAD27 to NAD83 (6), 1.5 m, Canada - Quebec
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index 42d00fa6..20f4180b 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -1146,7 +1146,7 @@ TEST_F(CApi, proj_obj_create_operations) {
ASSERT_NE(res, nullptr);
ObjListKeeper keeper_res(res);
- EXPECT_EQ(proj_obj_list_get_count(res), 8);
+ EXPECT_EQ(proj_obj_list_get_count(res), 7);
EXPECT_EQ(proj_obj_list_get(m_ctxt, res, -1), nullptr);
EXPECT_EQ(proj_obj_list_get(m_ctxt, res, proj_obj_list_get_count(res)),
@@ -1247,7 +1247,7 @@ TEST_F(CApi, proj_obj_create_operations_with_pivot) {
// Restrict pivot to JGD2000
{
- auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr);
+ auto ctxt = proj_create_operation_factory_context(m_ctxt, "any");
ASSERT_NE(ctxt, nullptr);
ContextKeeper keeper_ctxt(ctxt);
@@ -1263,7 +1263,7 @@ TEST_F(CApi, proj_obj_create_operations_with_pivot) {
proj_obj_create_operations(m_ctxt, source_crs, target_crs, ctxt);
ASSERT_NE(res, nullptr);
ObjListKeeper keeper_res(res);
- // includes 2 results from ESRI
+ // includes results from ESRI
EXPECT_EQ(proj_obj_list_get_count(res), 5);
auto op = proj_obj_list_get(m_ctxt, res, 0);
ASSERT_NE(op, nullptr);
diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp
index 44b84401..9c11ac4c 100644
--- a/test/unit/test_crs.cpp
+++ b/test/unit/test_crs.cpp
@@ -4841,6 +4841,11 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) {
"+proj=geocent +ellps=intl "
"+towgs84=324.8,153.6,172.1,0,0,0,0 +units=m +no_defs");
}
+ {
+ auto crs = factory->createCoordinateReferenceSystem("4269"); // NAD83
+ auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext);
+ EXPECT_EQ(bound, crs);
+ }
}
// ---------------------------------------------------------------------------
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 057f1717..e5734cd1 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5941,7 +5941,7 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) {
AuthorityFactory::create(DatabaseContext::create(), "EPSG")
->createCoordinateReferenceSystem("4326"),
ctxt);
- ASSERT_GE(list2.size(), 4U);
+ ASSERT_GE(list2.size(), 3U);
EXPECT_EQ(replaceAll(list2[0]->exportToPROJString(
PROJStringFormatter::create().get()),
@@ -5950,10 +5950,8 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) {
// The second entry in list2 (list2[1]) uses the
// weird +pm=2.33720833333333 from "NTF (Paris) to NTF (2)"
- // and the third one uses the ESRI geographic offset method with another
- // value
- // so skip to the 4th method
- EXPECT_EQ(replaceAll(list2[3]->exportToPROJString(
+ // so skip to the 3th method
+ EXPECT_EQ(replaceAll(list2[2]->exportToPROJString(
PROJStringFormatter::create().get()),
"0.999877341", "0.99987734"),
list[1]->exportToPROJString(PROJStringFormatter::create().get()));