Skip to content

Commit

Permalink
Merge pull request #3180 from rouault/fix_3176
Browse files Browse the repository at this point in the history
Make it possible to determine which grid files were actually used during a transformation (fixes #3176)
  • Loading branch information
rouault committed May 8, 2022
2 parents 98e5ef0 + 5298fc0 commit 96a5936
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 62 deletions.
15 changes: 15 additions & 0 deletions docs/source/development/reference/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,21 @@ Coordinate transformation
:returns: :c:type:`PJ_COORD`
.. c:function:: PJ* proj_trans_get_last_used_operation(PJ *P)
.. versionadded:: 9.1.0
Return the operation used during the last invokation of proj_trans().
This is especially useful when P has been created with proj_create_crs_to_crs()
and has several alternative operations.
The returned object must be freed with proj_destroy().
:param P: Transformation object
:type P: :c:type:`PJ` *
:returns: :c:type:`PJ` *
.. c:function:: size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction, \
double *x, size_t sx, size_t nx, \
double *y, size_t sy, size_t ny, \
Expand Down
1 change: 1 addition & 0 deletions scripts/reference_exported_symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ proj_trans
proj_trans_array
proj_trans_bounds
proj_trans_generic
proj_trans_get_last_used_operation
proj_unit_list_destroy
proj_uom_get_info_from_database
proj_xy_dist
Expand Down
34 changes: 26 additions & 8 deletions src/4D_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,28 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
return proj_coord_error ();
}

P->iCurCoordOp = 0; // dummy value, to be used by proj_trans_get_last_used_operation()
if (direction == PJ_FWD)
return pj_fwd4d (coord, P);
else
return pj_inv4d (coord, P);
}


/*****************************************************************************/
PJ* proj_trans_get_last_used_operation(PJ* P)
/******************************************************************************
Return the operation used during the last invokation of proj_trans().
This is especially useful when P has been created with proj_create_crs_to_crs()
and has several alternative operations.
The returned object must be freed with proj_destroy().
******************************************************************************/
{
if( nullptr==P || P->iCurCoordOp < 0 )
return nullptr;
if( P->alternativeCoordinateOperations.empty() )
return proj_clone(P->ctx, P);
return proj_clone(P->ctx, P->alternativeCoordinateOperations[P->iCurCoordOp].pj);
}

/*****************************************************************************/
int proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) {
Expand Down Expand Up @@ -2200,13 +2215,16 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) {
return pjinfo;

/* coordinate operation description */
if( P->iCurCoordOp >= 0 ) {
P = P->alternativeCoordinateOperations[P->iCurCoordOp].pj;
} else if( !P->alternativeCoordinateOperations.empty() ) {
pjinfo.id = "unknown";
pjinfo.description = "unavailable until proj_trans is called";
pjinfo.definition = "unavailable until proj_trans is called";
return pjinfo;
if( !P->alternativeCoordinateOperations.empty() )
{
if( P->iCurCoordOp >= 0 ) {
P = P->alternativeCoordinateOperations[P->iCurCoordOp].pj;
} else {
pjinfo.id = "unknown";
pjinfo.description = "unavailable until proj_trans is called";
pjinfo.definition = "unavailable until proj_trans is called";
return pjinfo;
}
}

/* projection id */
Expand Down
59 changes: 25 additions & 34 deletions src/filemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1633,15 +1633,18 @@ static NS_PROJ::io::DatabaseContextPtr getDBcontext(PJ_CONTEXT *ctx) {
/************************************************************************/

std::unique_ptr<NS_PROJ::File>
NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name) {
NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name,
char *out_full_filename,
size_t out_full_filename_size) {

if (ctx == nullptr) {
ctx = pj_get_default_ctx();
}

auto file = std::unique_ptr<NS_PROJ::File>(
reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
ctx, name, "rb", pj_open_file_with_manager, nullptr, 0)));
auto file =
std::unique_ptr<NS_PROJ::File>(reinterpret_cast<NS_PROJ::File *>(
pj_open_lib_internal(ctx, name, "rb", pj_open_file_with_manager,
out_full_filename, out_full_filename_size)));

// Retry with the new proj grid name if the file name doesn't end with .tif
std::string tmpString; // keep it in this upper scope !
Expand All @@ -1657,8 +1660,9 @@ NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name) {
if (!filename.empty()) {
file.reset(reinterpret_cast<NS_PROJ::File *>(
pj_open_lib_internal(ctx, filename.c_str(), "rb",
pj_open_file_with_manager, nullptr,
0)));
pj_open_file_with_manager,
out_full_filename,
out_full_filename_size)));
if (file) {
proj_context_errno_set(ctx, 0);
} else {
Expand Down Expand Up @@ -1687,8 +1691,9 @@ NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name) {
if (!filename.empty()) {
file.reset(reinterpret_cast<NS_PROJ::File *>(
pj_open_lib_internal(ctx, filename.c_str(), "rb",
pj_open_file_with_manager, nullptr,
0)));
pj_open_file_with_manager,
out_full_filename,
out_full_filename_size)));
if (file) {
proj_context_errno_set(ctx, 0);
}
Expand All @@ -1713,6 +1718,11 @@ NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name) {
file =
open(ctx, remote_file.c_str(), NS_PROJ::FileAccess::READ_ONLY);
if (file) {
if (out_full_filename) {
strncpy(out_full_filename, remote_file.c_str(),
out_full_filename_size);
out_full_filename[out_full_filename_size - 1] = '\0';
}
pj_log(ctx, PJ_LOG_DEBUG, "Using %s", remote_file.c_str());
proj_context_errno_set(ctx, 0);
}
Expand All @@ -1739,32 +1749,13 @@ NS_PROJ::FileManager::open_resource_file(PJ_CONTEXT *ctx, const char *name) {
*/
int pj_find_file(PJ_CONTEXT *ctx, const char *short_filename,
char *out_full_filename, size_t out_full_filename_size) {
auto file = std::unique_ptr<NS_PROJ::File>(
reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
ctx, short_filename, "rb", pj_open_file_with_manager,
out_full_filename, out_full_filename_size)));

// Retry with the old proj grid name if the file name ends with .tif
if (file == nullptr && strstr(short_filename, ".tif") != nullptr) {

auto dbContext = getDBcontext(ctx);
if (dbContext) {
try {
auto filename = dbContext->getOldProjGridName(short_filename);
if (!filename.empty()) {
file.reset(reinterpret_cast<NS_PROJ::File *>(
pj_open_lib_internal(ctx, filename.c_str(), "rb",
pj_open_file_with_manager,
out_full_filename,
out_full_filename_size)));
}
} catch (const std::exception &e) {
pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
return false;
}
}
}

const bool old_network_enabled = proj_context_is_network_enabled(ctx);
if (old_network_enabled)
proj_context_set_enable_network(ctx, false);
auto file = NS_PROJ::FileManager::open_resource_file(
ctx, short_filename, out_full_filename, out_full_filename_size);
if (old_network_enabled)
proj_context_set_enable_network(ctx, true);
return file != nullptr;
}

Expand Down
6 changes: 4 additions & 2 deletions src/filemanager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ class FileManager {
static std::string getProjLibEnvVar(PJ_CONTEXT *ctx);

// "High-level" interface, honoring PROJ_LIB and the like.
static std::unique_ptr<File> open_resource_file(PJ_CONTEXT *ctx,
const char *name);
static std::unique_ptr<File>
open_resource_file(PJ_CONTEXT *ctx, const char *name,
char *out_full_filename = nullptr,
size_t out_full_filename_size = 0);

static void fillDefaultNetworkInterface(PJ_CONTEXT *ctx);

Expand Down
34 changes: 17 additions & 17 deletions src/iso19111/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3260,20 +3260,16 @@ bool DatabaseContext::lookForGridInfo(
openLicense = false;
directDownload = false;

if (considerKnownGridsAsAvailable) {
fullFilename = projFilename;
} else {
fullFilename.resize(2048);
if (d->pjCtxt() == nullptr) {
d->setPjCtxt(pj_get_default_ctx());
}
int errno_before = proj_context_errno(d->pjCtxt());
gridAvailable =
pj_find_file(d->pjCtxt(), projFilename.c_str(), &fullFilename[0],
fullFilename.size() - 1) != 0;
proj_context_errno_set(d->pjCtxt(), errno_before);
fullFilename.resize(strlen(fullFilename.c_str()));
fullFilename.resize(2048);
if (d->pjCtxt() == nullptr) {
d->setPjCtxt(pj_get_default_ctx());
}
int errno_before = proj_context_errno(d->pjCtxt());
gridAvailable =
pj_find_file(d->pjCtxt(), projFilename.c_str(), &fullFilename[0],
fullFilename.size() - 1) != 0;
proj_context_errno_set(d->pjCtxt(), errno_before);
fullFilename.resize(strlen(fullFilename.c_str()));

auto res =
d->run("SELECT "
Expand Down Expand Up @@ -3305,10 +3301,7 @@ bool DatabaseContext::lookForGridInfo(
old_proj_grid_name == projFilename) {
std::string fullFilenameNewName;
fullFilenameNewName.resize(2048);
if (d->pjCtxt() == nullptr) {
d->setPjCtxt(pj_get_default_ctx());
}
int errno_before = proj_context_errno(d->pjCtxt());
errno_before = proj_context_errno(d->pjCtxt());
bool gridAvailableWithNewName =
pj_find_file(d->pjCtxt(), proj_grid_name.c_str(),
&fullFilenameNewName[0],
Expand All @@ -3328,6 +3321,13 @@ bool DatabaseContext::lookForGridInfo(

info.fullFilename = fullFilename;
info.packageName = packageName;
std::string endpoint(proj_context_get_url_endpoint(d->pjCtxt()));
if (!endpoint.empty() && starts_with(url, "https://cdn.proj.org/")) {
if (endpoint.back() != '/') {
endpoint += '/';
}
url = endpoint + url.substr(strlen("https://cdn.proj.org/"));
}
info.url = url;
info.directDownload = directDownload;
info.openLicense = openLicense;
Expand Down
1 change: 1 addition & 0 deletions src/proj.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ int PROJ_DLL proj_degree_input (PJ *P, enum PJ_DIRECTION dir);
int PROJ_DLL proj_degree_output (PJ *P, enum PJ_DIRECTION dir);

PJ_COORD PROJ_DLL proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coord);
PJ PROJ_DLL* proj_trans_get_last_used_operation(PJ* P);
int PROJ_DLL proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord);
size_t PROJ_DLL proj_trans_generic (
PJ *P,
Expand Down
23 changes: 22 additions & 1 deletion test/unit/test_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,9 @@ TEST_F(CApi, proj_coordoperation_get_grid_used) {
ASSERT_NE(op, nullptr);
ObjectKeeper keeper(op);

const std::string old_endpoint = proj_context_get_url_endpoint(m_ctxt);
proj_context_set_url_endpoint(m_ctxt, "https://example.com");

EXPECT_EQ(proj_coordoperation_get_grid_used_count(m_ctxt, op), 1);
const char *shortName = nullptr;
const char *fullName = nullptr;
Expand Down Expand Up @@ -1569,9 +1572,11 @@ TEST_F(CApi, proj_coordoperation_get_grid_used) {
EXPECT_EQ(shortName, std::string("ca_nrc_ntv1_can.tif"));
// EXPECT_EQ(fullName, std::string(""));
EXPECT_EQ(packageName, std::string(""));
EXPECT_EQ(std::string(url), "https://cdn.proj.org/ca_nrc_ntv1_can.tif");
EXPECT_EQ(std::string(url), "https://example.com/ca_nrc_ntv1_can.tif");
EXPECT_EQ(directDownload, 1);
EXPECT_EQ(openLicense, 1);

proj_context_set_url_endpoint(m_ctxt, old_endpoint.c_str());
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -2426,12 +2431,24 @@ TEST_F(CApi, check_coord_op_obj_can_be_used_with_proj_trans) {
ObjectKeeper keeper_projCRS(projCRS);
ASSERT_NE(projCRS, nullptr);

{
PJ *pj_used = proj_trans_get_last_used_operation(projCRS);
ASSERT_EQ(pj_used, nullptr);
}

PJ_COORD coord;
coord.xyzt.x = proj_torad(3.0);
coord.xyzt.y = 0;
coord.xyzt.z = 0;
coord.xyzt.t = 0;
EXPECT_NEAR(proj_trans(projCRS, PJ_FWD, coord).xyzt.x, 500000.0, 1e-9);

{
PJ *pj_used = proj_trans_get_last_used_operation(projCRS);
ASSERT_TRUE(
proj_is_equivalent_to(pj_used, projCRS, PJ_COMP_STRICT));
proj_destroy(pj_used);
}
}
}

Expand Down Expand Up @@ -3005,6 +3022,10 @@ TEST_F(CApi, proj_clone_of_obj_with_alternative_operations) {
EXPECT_NEAR(c_trans_ref.xyzt.x, c.xyzt.x, 1e-3);
EXPECT_NEAR(c_trans_ref.xyzt.y, c.xyzt.y, 1e-3);

PJ *pj_used = proj_trans_get_last_used_operation(obj);
ASSERT_NE(pj_used, nullptr);
proj_destroy(pj_used);

auto clone = proj_clone(m_ctxt, obj);
ObjectKeeper keeperClone(clone);
ASSERT_NE(clone, nullptr);
Expand Down

0 comments on commit 96a5936

Please sign in to comment.