aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-12-25 18:44:45 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-12-27 11:14:16 +0100
commitc4589fbe42e5fea07a03919d3484164f5fb70dd3 (patch)
tree37d526da7460deead544ebcfd0ed37db1945a0fc /src
parent0a1f1fe469029ae31591dc8b51d20f5617496128 (diff)
downloadPROJ-c4589fbe42e5fea07a03919d3484164f5fb70dd3.tar.gz
PROJ-c4589fbe42e5fea07a03919d3484164f5fb70dd3.zip
Network: automatically use CDN resources when local resources not available, and networking enabled
Diffstat (limited to 'src')
-rw-r--r--src/4D_api.cpp7
-rw-r--r--src/apps/projinfo.cpp30
-rw-r--r--src/filemanager.cpp15
-rw-r--r--src/filemanager.hpp5
-rw-r--r--src/grids.cpp26
-rw-r--r--src/iso19111/c_api.cpp20
-rw-r--r--src/iso19111/coordinateoperation.cpp131
-rw-r--r--src/iso19111/factory.cpp77
-rw-r--r--src/open_lib.cpp59
-rw-r--r--src/proj.h6
-rw-r--r--src/proj_internal.h4
-rw-r--r--src/transformations/hgridshift.cpp40
-rw-r--r--src/transformations/vgridshift.cpp65
13 files changed, 355 insertions, 130 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index efb4a86a..cee8262e 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -262,7 +262,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
auto coordOperation = dynamic_cast<
NS_PROJ::operation::CoordinateOperation*>(alt.pj->iso_obj.get());
if( coordOperation ) {
- if( coordOperation->gridsNeeded(dbContext).empty() ) {
+ if( coordOperation->gridsNeeded(dbContext, true).empty() ) {
if( P->iCurCoordOp != i ) {
std::string msg("Using coordinate operation ");
msg += alt.name;
@@ -1117,7 +1117,10 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons
proj_operation_factory_context_set_spatial_criterion(
ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION);
proj_operation_factory_context_set_grid_availability_use(
- ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
+ ctx, operation_ctx,
+ pj_context_is_network_enabled(ctx) ?
+ PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE:
+ PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
auto op_list = proj_create_operations(ctx, source_crs, target_crs, operation_ctx);
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index fd9b2f46..f13e526b 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -87,7 +87,8 @@ static void usage() {
<< " [--spatial-test contains|intersects]" << std::endl
<< " [--crs-extent-use none|both|intersection|smallest]"
<< std::endl
- << " [--grid-check none|discard_missing|sort] "
+ << " [--grid-check "
+ "none|discard_missing|sort|known_available] "
"[--show-superseded]"
<< std::endl
<< " [--pivot-crs always|if_no_direct_transformation|"
@@ -509,7 +510,7 @@ static void outputObject(
auto op = dynamic_cast<CoordinateOperation *>(obj.get());
if (op && dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
try {
- auto setGrids = op->gridsNeeded(dbContext);
+ auto setGrids = op->gridsNeeded(dbContext, false);
bool firstWarning = true;
for (const auto &grid : setGrids) {
if (!grid.available) {
@@ -525,6 +526,7 @@ static void outputObject(
if (!grid.url.empty()) {
std::cout << " at " << grid.url;
}
+ std::cout << ", or on CDN";
} else if (!grid.url.empty()) {
std::cout << " Can be obtained at " << grid.url;
}
@@ -539,8 +541,9 @@ static void outputObject(
// ---------------------------------------------------------------------------
-static void outputOperationSummary(const CoordinateOperationNNPtr &op,
- const DatabaseContextPtr &dbContext) {
+static void outputOperationSummary(
+ const CoordinateOperationNNPtr &op, const DatabaseContextPtr &dbContext,
+ CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse) {
auto ids = op->identifiers();
if (!ids.empty()) {
std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code();
@@ -586,10 +589,16 @@ static void outputOperationSummary(const CoordinateOperationNNPtr &op,
if (dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
try {
- auto setGrids = op->gridsNeeded(dbContext);
+ auto setGrids = op->gridsNeeded(dbContext, false);
for (const auto &grid : setGrids) {
if (!grid.available) {
std::cout << ", at least one grid missing";
+ if (gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE &&
+ !grid.packageName.empty()) {
+ std::cout << " on the system, but available on CDN";
+ }
break;
}
}
@@ -686,7 +695,7 @@ static void outputOperations(
}
if (summary) {
for (const auto &op : list) {
- outputOperationSummary(op, dbContext);
+ outputOperationSummary(op, dbContext, gridAvailabilityUse);
}
} else {
bool first = true;
@@ -701,7 +710,7 @@ static void outputOperations(
"\xC2\xB0"
<< (i + 1) << ":" << std::endl
<< std::endl;
- outputOperationSummary(op, dbContext);
+ outputOperationSummary(op, dbContext, gridAvailabilityUse);
std::cout << std::endl;
outputObject(dbContext, op, allowUseIntermediateCRS, outputOpt);
}
@@ -734,7 +743,9 @@ int main(int argc, char **argv) {
CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST;
bool buildBoundCRSToWGS84 = false;
CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse =
- CoordinateOperationContext::GridAvailabilityUse::USE_FOR_SORTING;
+ pj_context_is_network_enabled(nullptr)
+ ? CoordinateOperationContext::GridAvailabilityUse::KNOWN_AVAILABLE
+ : CoordinateOperationContext::GridAvailabilityUse::USE_FOR_SORTING;
CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS =
CoordinateOperationContext::IntermediateCRSUse::
IF_NO_DIRECT_TRANSFORMATION;
@@ -947,6 +958,9 @@ int main(int argc, char **argv) {
} else if (ci_equal(value, "sort")) {
gridAvailabilityUse = CoordinateOperationContext::
GridAvailabilityUse::USE_FOR_SORTING;
+ } else if (ci_equal(value, "known_available")) {
+ gridAvailabilityUse = CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE;
} else {
std::cerr << "Unrecognized value for option --grid-check: "
<< value << std::endl;
diff --git a/src/filemanager.cpp b/src/filemanager.cpp
index cd738d5e..d9a02632 100644
--- a/src/filemanager.cpp
+++ b/src/filemanager.cpp
@@ -69,7 +69,7 @@ NS_PROJ_START
// ---------------------------------------------------------------------------
-File::File() = default;
+File::File(const std::string &name) : name_(name) {}
// ---------------------------------------------------------------------------
@@ -85,7 +85,8 @@ class FileStdio : public File {
FileStdio &operator=(const FileStdio &) = delete;
protected:
- FileStdio(PJ_CONTEXT *ctx, FILE *fp) : m_ctx(ctx), m_fp(fp) {}
+ FileStdio(const std::string &name, PJ_CONTEXT *ctx, FILE *fp)
+ : File(name), m_ctx(ctx), m_fp(fp) {}
public:
~FileStdio() override;
@@ -130,7 +131,8 @@ unsigned long long FileStdio::tell() {
std::unique_ptr<File> FileStdio::open(PJ_CONTEXT *ctx, const char *filename) {
auto fp = fopen(filename, "rb");
- return std::unique_ptr<File>(fp ? new FileStdio(ctx, fp) : nullptr);
+ return std::unique_ptr<File>(fp ? new FileStdio(filename, ctx, fp)
+ : nullptr);
}
// ---------------------------------------------------------------------------
@@ -145,7 +147,8 @@ class FileLegacyAdapter : public File {
FileLegacyAdapter &operator=(const FileLegacyAdapter &) = delete;
protected:
- FileLegacyAdapter(PJ_CONTEXT *ctx, PAFile fp) : m_ctx(ctx), m_fp(fp) {}
+ FileLegacyAdapter(const std::string &name, PJ_CONTEXT *ctx, PAFile fp)
+ : File(name), m_ctx(ctx), m_fp(fp) {}
public:
~FileLegacyAdapter() override;
@@ -189,7 +192,7 @@ unsigned long long FileLegacyAdapter::tell() {
std::unique_ptr<File> FileLegacyAdapter::open(PJ_CONTEXT *ctx,
const char *filename) {
auto fid = pj_ctx_fopen(ctx, filename, "rb");
- return std::unique_ptr<File>(fid ? new FileLegacyAdapter(ctx, fid)
+ return std::unique_ptr<File>(fid ? new FileLegacyAdapter(filename, ctx, fid)
: nullptr);
}
@@ -292,7 +295,7 @@ class NetworkFile : public File {
PROJ_NETWORK_HANDLE *handle,
unsigned long long lastDownloadOffset,
unsigned long long filesize)
- : m_ctx(ctx), m_url(url), m_handle(handle),
+ : File(url), m_ctx(ctx), m_url(url), m_handle(handle),
m_lastDownloadedOffset(lastDownloadOffset), m_filesize(filesize) {}
public:
diff --git a/src/filemanager.hpp b/src/filemanager.hpp
index 949b223f..46391597 100644
--- a/src/filemanager.hpp
+++ b/src/filemanager.hpp
@@ -60,13 +60,16 @@ class FileManager {
class File {
protected:
- File();
+ std::string name_;
+ explicit File(const std::string &name);
public:
virtual ~File();
virtual size_t read(void *buffer, size_t sizeBytes) = 0;
virtual bool seek(unsigned long long offset, int whence = SEEK_SET) = 0;
virtual unsigned long long tell() = 0;
+
+ const std::string &name() const { return name_; }
};
//! @endcond Doxygen_Suppress
diff --git a/src/grids.cpp b/src/grids.cpp
index 91e51016..45e7e8b0 100644
--- a/src/grids.cpp
+++ b/src/grids.cpp
@@ -1299,14 +1299,15 @@ VerticalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
- if (ends_with(filename, "gtx") || ends_with(filename, "GTX")) {
- auto grid = GTXVerticalShiftGrid::open(ctx, std::move(fp), filename);
+ const auto actualName(fp->name());
+ if (ends_with(actualName, "gtx") || ends_with(actualName, "GTX")) {
+ auto grid = GTXVerticalShiftGrid::open(ctx, std::move(fp), actualName);
if (!grid) {
return nullptr;
}
auto set =
std::unique_ptr<VerticalShiftGridSet>(new VerticalShiftGridSet());
- set->m_name = filename;
+ set->m_name = actualName;
set->m_format = "gtx";
set->m_grids.push_back(std::unique_ptr<VerticalShiftGrid>(grid));
return set;
@@ -1324,7 +1325,7 @@ VerticalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
if (IsTIFF(header_size, header)) {
#ifdef TIFF_ENABLED
- auto set = GTiffVGridShiftSet::open(ctx, std::move(fp), filename);
+ auto set = GTiffVGridShiftSet::open(ctx, std::move(fp), actualName);
if (!set)
pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
return set;
@@ -2130,6 +2131,7 @@ HorizontalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
+ const auto actualName(fp->name());
char header[160];
/* -------------------------------------------------------------------- */
@@ -2151,35 +2153,35 @@ HorizontalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
if (header_size >= 144 + 16 && strncmp(header + 0, "HEADER", 6) == 0 &&
strncmp(header + 96, "W GRID", 6) == 0 &&
strncmp(header + 144, "TO NAD83 ", 16) == 0) {
- auto grid = NTv1Grid::open(ctx, std::move(fp), filename);
+ auto grid = NTv1Grid::open(ctx, std::move(fp), actualName);
if (!grid) {
return nullptr;
}
auto set = std::unique_ptr<HorizontalShiftGridSet>(
new HorizontalShiftGridSet());
- set->m_name = filename;
+ set->m_name = actualName;
set->m_format = "ntv1";
set->m_grids.push_back(std::unique_ptr<HorizontalShiftGrid>(grid));
return set;
} else if (header_size >= 9 && strncmp(header + 0, "CTABLE V2", 9) == 0) {
- auto grid = CTable2Grid::open(ctx, std::move(fp), filename);
+ auto grid = CTable2Grid::open(ctx, std::move(fp), actualName);
if (!grid) {
return nullptr;
}
auto set = std::unique_ptr<HorizontalShiftGridSet>(
new HorizontalShiftGridSet());
- set->m_name = filename;
+ set->m_name = actualName;
set->m_format = "ctable2";
set->m_grids.push_back(std::unique_ptr<HorizontalShiftGrid>(grid));
return set;
} else if (header_size >= 48 + 7 &&
strncmp(header + 0, "NUM_OREC", 8) == 0 &&
strncmp(header + 48, "GS_TYPE", 7) == 0) {
- return NTv2GridSet::open(ctx, std::move(fp), filename);
+ return NTv2GridSet::open(ctx, std::move(fp), actualName);
} else if (IsTIFF(header_size,
reinterpret_cast<const unsigned char *>(header))) {
#ifdef TIFF_ENABLED
- auto set = GTiffHGridShiftSet::open(ctx, std::move(fp), filename);
+ auto set = GTiffHGridShiftSet::open(ctx, std::move(fp), actualName);
if (!set)
pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
return set;
@@ -2450,6 +2452,7 @@ GenericShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
+ const auto actualName(fp->name());
/* -------------------------------------------------------------------- */
/* Load a header, to determine the file type. */
@@ -2463,7 +2466,8 @@ GenericShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
if (IsTIFF(header_size, header)) {
#ifdef TIFF_ENABLED
- auto set = GTiffGenericGridShiftSet::open(ctx, std::move(fp), filename);
+ auto set =
+ GTiffGenericGridShiftSet::open(ctx, std::move(fp), actualName);
if (!set)
pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
return set;
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 9db9e5b3..5df4c513 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -177,7 +177,11 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) {
auto formatter = PROJStringFormatter::create(
PROJStringFormatter::Convention::PROJ_5, dbContext);
auto projString = coordop->exportToPROJString(formatter.get());
+ if (pj_context_is_network_enabled(ctx)) {
+ ctx->defer_grid_opening = true;
+ }
auto pj = pj_create_internal(ctx, projString.c_str());
+ ctx->defer_grid_opening = false;
if (pj) {
pj->iso_obj = objIn;
if (ctx->cpp_context) {
@@ -766,7 +770,7 @@ int PROJ_DLL proj_grid_get_info_from_database(
bool open_license;
bool available;
if (!db_context->lookForGridInfo(
- grid_name, ctx->cpp_context->lastGridFullName_,
+ grid_name, false, ctx->cpp_context->lastGridFullName_,
ctx->cpp_context->lastGridPackageName_,
ctx->cpp_context->lastGridUrl_, direct_download, open_license,
available)) {
@@ -6571,7 +6575,10 @@ int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
}
auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
try {
- auto ret = op->isPROJInstantiable(dbContext) ? 1 : 0;
+ auto ret = op->isPROJInstantiable(dbContext,
+ pj_context_is_network_enabled(ctx))
+ ? 1
+ : 0;
if (ctx->cpp_context) {
ctx->cpp_context->autoCloseDbIfNeeded();
}
@@ -6883,7 +6890,8 @@ int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx,
try {
if (!coordoperation->gridsNeededAsked) {
coordoperation->gridsNeededAsked = true;
- const auto gridsNeeded = co->gridsNeeded(dbContext);
+ const auto gridsNeeded =
+ co->gridsNeeded(dbContext, pj_context_is_network_enabled(ctx));
for (const auto &gridDesc : gridsNeeded) {
coordoperation->gridsNeeded.emplace_back(gridDesc);
}
@@ -7220,6 +7228,12 @@ void PROJ_DLL proj_operation_factory_context_set_grid_availability_use(
CoordinateOperationContext::GridAvailabilityUse::
IGNORE_GRID_AVAILABILITY);
break;
+
+ case PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE:
+ factory_ctx->operationContext->setGridAvailabilityUse(
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE);
+ break;
}
} catch (const std::exception &e) {
proj_log_error(ctx, __FUNCTION__, e.what());
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 6120c768..7c0515c7 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -788,13 +788,15 @@ void CoordinateOperation::setAccuracies(
* available.
*/
bool CoordinateOperation::isPROJInstantiable(
- const io::DatabaseContextPtr &databaseContext) const {
+ const io::DatabaseContextPtr &databaseContext,
+ bool considerKnownGridsAsAvailable) const {
try {
exportToPROJString(io::PROJStringFormatter::create().get());
} catch (const std::exception &) {
return false;
}
- for (const auto &gridDesc : gridsNeeded(databaseContext)) {
+ for (const auto &gridDesc :
+ gridsNeeded(databaseContext, considerKnownGridsAsAvailable)) {
if (!gridDesc.available) {
return false;
}
@@ -2013,8 +2015,9 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
// ---------------------------------------------------------------------------
-std::set<GridDescription> SingleOperation::gridsNeeded(
- const io::DatabaseContextPtr &databaseContext) const {
+std::set<GridDescription>
+SingleOperation::gridsNeeded(const io::DatabaseContextPtr &databaseContext,
+ bool considerKnownGridsAsAvailable) const {
std::set<GridDescription> res;
for (const auto &genOpParamvalue : parameterValues()) {
auto opParamvalue = dynamic_cast<const OperationParameterValue *>(
@@ -2026,9 +2029,9 @@ std::set<GridDescription> SingleOperation::gridsNeeded(
desc.shortName = value->valueFile();
if (databaseContext) {
databaseContext->lookForGridInfo(
- desc.shortName, desc.fullName, desc.packageName,
- desc.url, desc.directDownload, desc.openLicense,
- desc.available);
+ desc.shortName, considerKnownGridsAsAvailable,
+ desc.fullName, desc.packageName, desc.url,
+ desc.directDownload, desc.openLicense, desc.available);
}
res.insert(desc);
}
@@ -10209,10 +10212,12 @@ bool ConcatenatedOperation::_isEquivalentTo(
// ---------------------------------------------------------------------------
std::set<GridDescription> ConcatenatedOperation::gridsNeeded(
- const io::DatabaseContextPtr &databaseContext) const {
+ const io::DatabaseContextPtr &databaseContext,
+ bool considerKnownGridsAsAvailable) const {
std::set<GridDescription> res;
for (const auto &operation : operations()) {
- const auto l_gridsNeeded = operation->gridsNeeded(databaseContext);
+ const auto l_gridsNeeded = operation->gridsNeeded(
+ databaseContext, considerKnownGridsAsAvailable);
for (const auto &gridDesc : l_gridsNeeded) {
res.insert(gridDesc);
}
@@ -11132,7 +11137,10 @@ struct FilterResults {
bool gridsKnown = true;
if (context->getAuthorityFactory()) {
const auto gridsNeeded = op->gridsNeeded(
- context->getAuthorityFactory()->databaseContext());
+ context->getAuthorityFactory()->databaseContext(),
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE);
for (const auto &gridDesc : gridsNeeded) {
hasGrids = true;
if (gridAvailabilityUse ==
@@ -11254,6 +11262,7 @@ struct FilterResults {
CoordinateOperationPtr lastOp;
bool first = true;
+ const auto gridAvailabilityUse = context->getGridAvailabilityUse();
for (const auto &op : res) {
const auto curAccuracy = getAccuracy(op);
bool dummy = false;
@@ -11266,7 +11275,10 @@ struct FilterResults {
if (context->getAuthorityFactory()) {
const auto gridsNeeded = op->gridsNeeded(
- context->getAuthorityFactory()->databaseContext());
+ context->getAuthorityFactory()->databaseContext(),
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE);
for (const auto &gridDesc : gridsNeeded) {
curHasGrids = true;
curSetOfGrids.insert(gridDesc.shortName);
@@ -11571,6 +11583,7 @@ CoordinateOperationFactory::Private::findOpsInRegistryDirect(
buildCRSIds(sourceCRS, context, sourceIds);
buildCRSIds(targetCRS, context, targetIds);
+ const auto gridAvailabilityUse = context.context->getGridAvailabilityUse();
for (const auto &idSrc : sourceIds) {
const auto &srcAuthName = idSrc.first;
const auto &srcCode = idSrc.second;
@@ -11590,9 +11603,16 @@ CoordinateOperationFactory::Private::findOpsInRegistryDirect(
tmpAuthFactory->createFromCoordinateReferenceSystemCodes(
srcAuthName, srcCode, targetAuthName, targetCode,
context.context->getUsePROJAlternativeGridNames(),
- context.context->getGridAvailabilityUse() ==
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID ||
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE,
+ gridAvailabilityUse ==
CoordinateOperationContext::GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
+ KNOWN_AVAILABLE,
context.context->getDiscardSuperseded(), true, false,
context.extent1, context.extent2);
res.insert(res.end(), resTmp.begin(), resTmp.end());
@@ -11635,6 +11655,7 @@ CoordinateOperationFactory::Private::findOpsInRegistryDirectTo(
std::list<std::pair<std::string, std::string>> ids;
buildCRSIds(targetCRS, context, ids);
+ const auto gridAvailabilityUse = context.context->getGridAvailabilityUse();
for (const auto &id : ids) {
const auto &targetAuthName = id.first;
const auto &targetCode = id.second;
@@ -11648,9 +11669,15 @@ CoordinateOperationFactory::Private::findOpsInRegistryDirectTo(
auto res = tmpAuthFactory->createFromCoordinateReferenceSystemCodes(
std::string(), std::string(), targetAuthName, targetCode,
context.context->getUsePROJAlternativeGridNames(),
- context.context->getGridAvailabilityUse() ==
- CoordinateOperationContext::GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
+
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID ||
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE,
+ gridAvailabilityUse == CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE,
context.context->getDiscardSuperseded(), true, true,
context.extent1, context.extent2);
if (!res.empty()) {
@@ -11698,6 +11725,7 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
buildCRSIds(sourceCRS, context, sourceIds);
buildCRSIds(targetCRS, context, targetIds);
+ const auto gridAvailabilityUse = context.context->getGridAvailabilityUse();
for (const auto &idSrc : sourceIds) {
const auto &srcAuthName = idSrc.first;
const auto &srcCode = idSrc.second;
@@ -11717,21 +11745,28 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
std::vector<CoordinateOperationNNPtr> res;
if (useCreateBetweenGeodeticCRSWithDatumBasedIntermediates) {
- res = tmpAuthFactory
- ->createBetweenGeodeticCRSWithDatumBasedIntermediates(
- sourceCRS, srcAuthName, srcCode, targetCRS,
- targetAuthName, targetCode,
- context.context->getUsePROJAlternativeGridNames(),
- context.context->getGridAvailabilityUse() ==
- CoordinateOperationContext::
- GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
- context.context->getDiscardSuperseded(),
- authFactory->getAuthority() != "any" &&
- authorities.size() > 1
- ? authorities
- : std::vector<std::string>(),
- context.extent1, context.extent2);
+ res =
+ tmpAuthFactory
+ ->createBetweenGeodeticCRSWithDatumBasedIntermediates(
+ sourceCRS, srcAuthName, srcCode, targetCRS,
+ targetAuthName, targetCode,
+ context.context->getUsePROJAlternativeGridNames(),
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID ||
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE,
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE,
+ context.context->getDiscardSuperseded(),
+ authFactory->getAuthority() != "any" &&
+ authorities.size() > 1
+ ? authorities
+ : std::vector<std::string>(),
+ context.extent1, context.extent2);
} else {
io::AuthorityFactory::ObjectType intermediateObjectType =
io::AuthorityFactory::ObjectType::CRS;
@@ -11749,9 +11784,15 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
res = tmpAuthFactory->createFromCRSCodesWithIntermediates(
srcAuthName, srcCode, targetAuthName, targetCode,
context.context->getUsePROJAlternativeGridNames(),
- context.context->getGridAvailabilityUse() ==
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID ||
+ gridAvailabilityUse ==
+ CoordinateOperationContext::GridAvailabilityUse::
+ KNOWN_AVAILABLE,
+ gridAvailabilityUse ==
CoordinateOperationContext::GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
+ KNOWN_AVAILABLE,
context.context->getDiscardSuperseded(),
context.context->getIntermediateCRS(),
intermediateObjectType,
@@ -14167,15 +14208,21 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
componentsSrc[1],
targetCRS->promoteTo3D(std::string(), dbContext), context);
bool foundRegisteredTransformWithAllGridsAvailable = false;
+ const auto gridAvailabilityUse =
+ context.context->getGridAvailabilityUse();
const bool ignoreMissingGrids =
- context.context->getGridAvailabilityUse() ==
+ gridAvailabilityUse ==
CoordinateOperationContext::GridAvailabilityUse::
IGNORE_GRID_AVAILABILITY;
for (const auto &op : verticalTransforms) {
if (hasIdentifiers(op) && dbContext) {
bool missingGrid = false;
if (!ignoreMissingGrids) {
- const auto gridsNeeded = op->gridsNeeded(dbContext);
+ const auto gridsNeeded = op->gridsNeeded(
+ dbContext,
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE);
for (const auto &gridDesc : gridsNeeded) {
if (!gridDesc.available) {
missingGrid = true;
@@ -14204,7 +14251,11 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
if (hasIdentifiers(op) && dbContext) {
bool missingGrid = false;
if (!ignoreMissingGrids) {
- const auto gridsNeeded = op->gridsNeeded(dbContext);
+ const auto gridsNeeded = op->gridsNeeded(
+ dbContext,
+ gridAvailabilityUse ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::KNOWN_AVAILABLE);
for (const auto &gridDesc : gridsNeeded) {
if (!gridDesc.available) {
missingGrid = true;
@@ -15034,8 +15085,9 @@ CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const {
// ---------------------------------------------------------------------------
-std::set<GridDescription> PROJBasedOperation::gridsNeeded(
- const io::DatabaseContextPtr &databaseContext) const {
+std::set<GridDescription>
+PROJBasedOperation::gridsNeeded(const io::DatabaseContextPtr &databaseContext,
+ bool considerKnownGridsAsAvailable) const {
std::set<GridDescription> res;
try {
@@ -15048,7 +15100,8 @@ std::set<GridDescription> PROJBasedOperation::gridsNeeded(
desc.shortName = shortName;
if (databaseContext) {
databaseContext->lookForGridInfo(
- desc.shortName, desc.fullName, desc.packageName, desc.url,
+ desc.shortName, considerKnownGridsAsAvailable,
+ desc.fullName, desc.packageName, desc.url,
desc.directDownload, desc.openLicense, desc.available);
}
res.insert(desc);
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 57850303..dae8680c 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -1018,14 +1018,14 @@ bool DatabaseContext::lookForGridAlternative(const std::string &officialName,
// ---------------------------------------------------------------------------
-bool DatabaseContext::lookForGridInfo(const std::string &projFilename,
- std::string &fullFilename,
- std::string &packageName,
- std::string &url, bool &directDownload,
- bool &openLicense,
- bool &gridAvailable) const {
+bool DatabaseContext::lookForGridInfo(
+ const std::string &projFilename, bool considerKnownGridsAsAvailable,
+ std::string &fullFilename, std::string &packageName, std::string &url,
+ bool &directDownload, bool &openLicense, bool &gridAvailable) const {
Private::GridInfoCache info;
- if (d->getGridInfoFromCache(projFilename, info)) {
+ const std::string key(projFilename +
+ (considerKnownGridsAsAvailable ? "true" : "false"));
+ if (d->getGridInfoFromCache(key, info)) {
fullFilename = info.fullFilename;
packageName = info.packageName;
url = info.url;
@@ -1041,16 +1041,20 @@ bool DatabaseContext::lookForGridInfo(const std::string &projFilename,
openLicense = false;
directDownload = false;
- fullFilename.resize(2048);
- if (d->pjCtxt() == nullptr) {
- d->setPjCtxt(pj_get_default_ctx());
+ 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()));
}
- 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 "
@@ -1074,6 +1078,10 @@ bool DatabaseContext::lookForGridInfo(const std::string &projFilename,
openLicense = (row[3].empty() ? row[4] : row[3]) == "1";
directDownload = (row[5].empty() ? row[6] : row[5]) == "1";
+ if (considerKnownGridsAsAvailable && !packageName.empty()) {
+ gridAvailable = true;
+ }
+
info.fullFilename = fullFilename;
info.packageName = packageName;
info.url = url;
@@ -1082,7 +1090,7 @@ bool DatabaseContext::lookForGridInfo(const std::string &projFilename,
}
info.gridAvailable = gridAvailable;
info.found = ret;
- d->cache(projFilename, info);
+ d->cache(key, info);
return ret;
}
@@ -1264,8 +1272,8 @@ struct AuthorityFactory::Private {
return AuthorityFactory::create(context_, auth_name);
}
- bool
- rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op);
+ bool rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op,
+ bool considerKnownGridsAsAvailable);
UnitOfMeasure createUnitOfMeasure(const std::string &auth_name,
const std::string &code);
@@ -1392,8 +1400,10 @@ util::PropertyMap AuthorityFactory::Private::createProperties(
// ---------------------------------------------------------------------------
bool AuthorityFactory::Private::rejectOpDueToMissingGrid(
- const operation::CoordinateOperationNNPtr &op) {
- for (const auto &gridDesc : op->gridsNeeded(context())) {
+ const operation::CoordinateOperationNNPtr &op,
+ bool considerKnownGridsAsAvailable) {
+ for (const auto &gridDesc :
+ op->gridsNeeded(context(), considerKnownGridsAsAvailable)) {
if (!gridDesc.available) {
return true;
}
@@ -3381,7 +3391,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
const std::string &sourceCRSCode, const std::string &targetCRSCode) const {
return createFromCoordinateReferenceSystemCodes(
d->authority(), sourceCRSCode, d->authority(), targetCRSCode, false,
- false, false);
+ false, false, false);
}
// ---------------------------------------------------------------------------
@@ -3410,6 +3420,8 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
* should be substituted to the official grid names.
* @param discardIfMissingGrid Whether coordinate operations that reference
* missing grids should be removed from the result set.
+ * @param considerKnownGridsAsAvailable Whether known grids should be considered
+ * as available (typically when network is enabled).
* @param discardSuperseded Whether cordinate operations that are superseded
* (but not deprecated) should be removed from the result set.
* @param tryReverseOrder whether to search in the reverse order too (and thus
@@ -3430,8 +3442,8 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
const std::string &sourceCRSAuthName, const std::string &sourceCRSCode,
const std::string &targetCRSAuthName, const std::string &targetCRSCode,
bool usePROJAlternativeGridNames, bool discardIfMissingGrid,
- bool discardSuperseded, bool tryReverseOrder,
- bool reportOnlyIntersectingTransformations,
+ bool considerKnownGridsAsAvailable, bool discardSuperseded,
+ bool tryReverseOrder, bool reportOnlyIntersectingTransformations,
const metadata::ExtentPtr &intersectingExtent1,
const metadata::ExtentPtr &intersectingExtent2) const {
@@ -3442,6 +3454,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
cacheKey += targetCRSCode;
cacheKey += (usePROJAlternativeGridNames ? '1' : '0');
cacheKey += (discardIfMissingGrid ? '1' : '0');
+ cacheKey += (considerKnownGridsAsAvailable ? '1' : '0');
cacheKey += (discardSuperseded ? '1' : '0');
cacheKey += (tryReverseOrder ? '1' : '0');
cacheKey += (reportOnlyIntersectingTransformations ? '1' : '0');
@@ -3680,7 +3693,8 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
target_crs_code != targetCRSCode))) {
op = op->inverse();
}
- if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) {
+ if (!discardIfMissingGrid ||
+ !d->rejectOpDueToMissingGrid(op, considerKnownGridsAsAvailable)) {
list.emplace_back(op);
}
}
@@ -3745,6 +3759,8 @@ static bool useIrrelevantPivot(const operation::CoordinateOperationNNPtr &op,
* should be substituted to the official grid names.
* @param discardIfMissingGrid Whether coordinate operations that reference
* missing grids should be removed from the result set.
+ * @param considerKnownGridsAsAvailable Whether known grids should be considered
+ * as available (typically when network is enabled).
* @param discardSuperseded Whether cordinate operations that are superseded
* (but not deprecated) should be removed from the result set.
* @param intermediateCRSAuthCodes List of (auth_name, code) of CRS that can be
@@ -3773,7 +3789,7 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
const std::string &sourceCRSAuthName, const std::string &sourceCRSCode,
const std::string &targetCRSAuthName, const std::string &targetCRSCode,
bool usePROJAlternativeGridNames, bool discardIfMissingGrid,
- bool discardSuperseded,
+ bool considerKnownGridsAsAvailable, bool discardSuperseded,
const std::vector<std::pair<std::string, std::string>>
&intermediateCRSAuthCodes,
ObjectType allowedIntermediateObjectType,
@@ -4221,7 +4237,8 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
std::vector<operation::CoordinateOperationNNPtr> list;
for (const auto &op : listTmp) {
- if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) {
+ if (!discardIfMissingGrid ||
+ !d->rejectOpDueToMissingGrid(op, considerKnownGridsAsAvailable)) {
list.emplace_back(op);
}
}
@@ -4239,7 +4256,8 @@ AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates(
const std::string &sourceCRSCode, const crs::CRSNNPtr &targetCRS,
const std::string &targetCRSAuthName, const std::string &targetCRSCode,
bool usePROJAlternativeGridNames, bool discardIfMissingGrid,
- bool discardSuperseded, const std::vector<std::string> &allowedAuthorities,
+ bool considerKnownGridsAsAvailable, bool discardSuperseded,
+ const std::vector<std::string> &allowedAuthorities,
const metadata::ExtentPtr &intersectingExtent1,
const metadata::ExtentPtr &intersectingExtent2) const {
@@ -4822,7 +4840,8 @@ AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates(
std::vector<operation::CoordinateOperationNNPtr> list;
for (const auto &op : listTmp) {
- if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) {
+ if (!discardIfMissingGrid ||
+ !d->rejectOpDueToMissingGrid(op, considerKnownGridsAsAvailable)) {
list.emplace_back(op);
}
}
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
index 6bdd5510..926505ba 100644
--- a/src/open_lib.cpp
+++ b/src/open_lib.cpp
@@ -203,6 +203,27 @@ static const char *get_path_from_win32_projlib(const char *name, std::string& ou
/* pj_open_lib_internal() */
/************************************************************************/
+#ifdef WIN32
+static const char dir_chars[] = "/\\";
+static const char dirSeparator = ';';
+#else
+static const char dir_chars[] = "/";
+static const char dirSeparator = ':';
+#endif
+
+static bool is_tilde_slash(const char* name)
+{
+ return *name == '~' && strchr(dir_chars,name[1]);
+}
+
+static bool is_rel_or_absolute_filename(const char *name)
+{
+ return strchr(dir_chars,*name)
+ || (*name == '.' && strchr(dir_chars,name[1]))
+ || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
+ || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2]));
+}
+
static void*
pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
void* (*open_file)(projCtx, const char*, const char*),
@@ -211,13 +232,6 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
std::string fname;
const char *sysname = nullptr;
void* fid = nullptr;
-#ifdef WIN32
- static const char dir_chars[] = "/\\";
- const char dirSeparator = ';';
-#else
- static const char dir_chars[] = "/";
- const char dirSeparator = ':';
-#endif
if( ctx == nullptr ) {
ctx = pj_get_default_ctx();
@@ -227,7 +241,7 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
out_full_filename[0] = '\0';
/* check if ~/name */
- if (*name == '~' && strchr(dir_chars,name[1]) )
+ if (is_tilde_slash(name))
if ((sysname = getenv("HOME")) != nullptr) {
fname = sysname;
fname += DIR_CHAR;
@@ -236,11 +250,8 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
} else
return nullptr;
- /* or fixed path: /name, ./name or ../name */
- else if (strchr(dir_chars,*name)
- || (*name == '.' && strchr(dir_chars,name[1]))
- || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
- || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2]))
+ /* or fixed path: /name, ./name or ../name or http[s]:// */
+ else if (is_rel_or_absolute_filename(name)
|| starts_with(name, "http://")
|| starts_with(name, "https://"))
sysname = name;
@@ -344,11 +355,31 @@ static void* pj_open_file_with_manager(projCtx ctx, const char *name,
std::unique_ptr<NS_PROJ::File> NS_PROJ::FileManager::open_resource_file(
projCtx ctx, const char *name)
{
- return std::unique_ptr<NS_PROJ::File>(
+ 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)));
+ if( file == nullptr &&
+ !is_tilde_slash(name) &&
+ !is_rel_or_absolute_filename(name) &&
+ !starts_with(name, "http://") &&
+ !starts_with(name, "https://") &&
+ pj_context_is_network_enabled(ctx) ) {
+ std::string remote_file("https://cdn.proj.org/");
+ remote_file += name;
+ auto pos = remote_file.rfind('.');
+ if( pos + 4 == remote_file.size() ) {
+ remote_file = remote_file.substr(0, pos) + ".tif";
+ file = open(ctx, remote_file.c_str());
+ if( file ) {
+ pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
+ "Using %s", remote_file.c_str() );
+ pj_ctx_set_errno( ctx, 0 );
+ }
+ }
+ }
+ return file;
}
/************************************************************************/
diff --git a/src/proj.h b/src/proj.h
index 4f7878a4..7210318c 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -697,6 +697,12 @@ typedef enum {
/** Ignore grid availability at all. Results will be presented as if
* all grids were available. */
PROJ_GRID_AVAILABILITY_IGNORED,
+
+ /** Results will be presented as if grids known to PROJ (that is
+ * registered in the grid_alternatives table of its database) were
+ * available. Used typically when networking is enabled.
+ */
+ PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE,
} PROJ_GRID_AVAILABILITY_USE;
/** \brief PROJ string version. */
diff --git a/src/proj_internal.h b/src/proj_internal.h
index d54d8fb9..983f1c07 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -697,6 +697,7 @@ struct projCtx_t {
void* file_finder_user_data = nullptr;
projNetworkCallbacksAndData networking{};
+ bool defer_grid_opening = false; // set by pj_obj_create()
int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString()
@@ -826,7 +827,8 @@ PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv);
void pj_pipeline_assign_context_to_steps( PJ* P, PJ_CONTEXT* ctx );
-bool pj_context_is_network_enabled(PJ_CONTEXT* ctx);
+// For use by projinfo
+bool PROJ_DLL pj_context_is_network_enabled(PJ_CONTEXT* ctx);
/* classic public API */
#include "proj_api.h"
diff --git a/src/transformations/hgridshift.cpp b/src/transformations/hgridshift.cpp
index e9983df6..3b6e366f 100644
--- a/src/transformations/hgridshift.cpp
+++ b/src/transformations/hgridshift.cpp
@@ -17,6 +17,7 @@ struct hgridshiftData {
double t_final = 0;
double t_epoch = 0;
ListOfHGrids grids{};
+ bool defer_grid_opening = false;
};
} // anonymous namespace
@@ -25,6 +26,14 @@ static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) {
PJ_COORD point = {{0,0,0,0}};
point.lpz = lpz;
+ if ( Q->defer_grid_opening ) {
+ Q->defer_grid_opening = false;
+ Q->grids = proj_hgrid_init(P, "grids");
+ if ( proj_errno(P) ) {
+ return proj_coord_error().xyz;
+ }
+ }
+
if (!Q->grids.empty()) {
/* Only try the gridshift if at least one grid is loaded,
* otherwise just pass the coordinate through unchanged. */
@@ -40,6 +49,14 @@ static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) {
PJ_COORD point = {{0,0,0,0}};
point.xyz = xyz;
+ if ( Q->defer_grid_opening ) {
+ Q->defer_grid_opening = false;
+ Q->grids = proj_hgrid_init(P, "grids");
+ if ( proj_errno(P) ) {
+ return proj_coord_error().lpz;
+ }
+ }
+
if (!Q->grids.empty()) {
/* Only try the gridshift if at least one grid is loaded,
* otherwise just pass the coordinate through unchanged. */
@@ -114,9 +131,9 @@ PJ *TRANSFORMATION(hgridshift,0) {
return destructor (P, PJD_ERR_NO_ARGS);
}
- /* TODO: Refactor into shared function that can be used */
- /* by both vgridshift and hgridshift */
- if (pj_param(P->ctx, P->params, "tt_final").i) {
+ /* TODO: Refactor into shared function that can be used */
+ /* by both vgridshift and hgridshift */
+ if (pj_param(P->ctx, P->params, "tt_final").i) {
Q->t_final = pj_param (P->ctx, P->params, "dt_final").f;
if (Q->t_final == 0) {
/* a number wasn't passed to +t_final, let's see if it was "now" */
@@ -131,16 +148,21 @@ PJ *TRANSFORMATION(hgridshift,0) {
}
}
- if (pj_param(P->ctx, P->params, "tt_epoch").i)
+ if (pj_param(P->ctx, P->params, "tt_epoch").i)
Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f;
- Q->grids = proj_hgrid_init(P, "grids");
- /* Was gridlist compiled properly? */
- if ( proj_errno(P) ) {
- proj_log_error(P, "hgridshift: could not find required grid(s).");
- return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
+ if( P->ctx->defer_grid_opening ) {
+ Q->defer_grid_opening = true;
}
+ else {
+ Q->grids = proj_hgrid_init(P, "grids");
+ /* Was gridlist compiled properly? */
+ if ( proj_errno(P) ) {
+ proj_log_error(P, "hgridshift: could not find required grid(s).");
+ return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
+ }
+ }
return P;
}
diff --git a/src/transformations/vgridshift.cpp b/src/transformations/vgridshift.cpp
index b964f45b..f35832e1 100644
--- a/src/transformations/vgridshift.cpp
+++ b/src/transformations/vgridshift.cpp
@@ -18,14 +18,51 @@ struct vgridshiftData {
double t_epoch = 0;
double forward_multiplier = 0;
ListOfVGrids grids{};
+ bool defer_grid_opening = false;
};
} // anonymous namespace
+static void deal_with_vertcon_gtx_hack(PJ *P)
+{
+ struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque;
+ // The .gtx VERTCON files stored millimetres, but the .tif files
+ // are in metres.
+ if( Q->forward_multiplier != 0.001 ) {
+ return;
+ }
+ const char* gridname = pj_param(P->ctx, P->params, "sgrids").s;
+ if( !gridname ) {
+ return;
+ }
+ if( strcmp(gridname, "vertconw.gtx") != 0 &&
+ strcmp(gridname, "vertconc.gtx") != 0 &&
+ strcmp(gridname, "vertcone.gtx") != 0 ) {
+ return;
+ }
+ if( Q->grids.empty() ) {
+ return;
+ }
+ const auto& grids = Q->grids[0]->grids();
+ if( !grids.empty() &&
+ grids[0]->name().find(".tif") != std::string::npos ) {
+ Q->forward_multiplier = 1.0;
+ }
+}
+
static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) {
struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque;
PJ_COORD point = {{0,0,0,0}};
point.lpz = lpz;
+ if ( Q->defer_grid_opening ) {
+ Q->defer_grid_opening = false;
+ Q->grids = proj_vgrid_init(P, "grids");
+ deal_with_vertcon_gtx_hack(P);
+ if ( proj_errno(P) ) {
+ return proj_coord_error().xyz;
+ }
+ }
+
if (!Q->grids.empty()) {
/* Only try the gridshift if at least one grid is loaded,
* otherwise just pass the coordinate through unchanged. */
@@ -41,6 +78,15 @@ static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) {
PJ_COORD point = {{0,0,0,0}};
point.xyz = xyz;
+ if ( Q->defer_grid_opening ) {
+ Q->defer_grid_opening = false;
+ Q->grids = proj_vgrid_init(P, "grids");
+ deal_with_vertcon_gtx_hack(P);
+ if ( proj_errno(P) ) {
+ return proj_coord_error().lpz;
+ }
+ }
+
if (!Q->grids.empty()) {
/* Only try the gridshift if at least one grid is loaded,
* otherwise just pass the coordinate through unchanged. */
@@ -132,13 +178,18 @@ PJ *TRANSFORMATION(vgridshift,0) {
Q->forward_multiplier = pj_param(P->ctx, P->params, "dmultiplier").f;
}
- /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */
- Q->grids = proj_vgrid_init(P, "grids");
-
- /* Was gridlist compiled properly? */
- if ( proj_errno(P) ) {
- proj_log_error(P, "vgridshift: could not find required grid(s).");
- return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
+ if( P->ctx->defer_grid_opening ) {
+ Q->defer_grid_opening = true;
+ }
+ else {
+ /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */
+ Q->grids = proj_vgrid_init(P, "grids");
+
+ /* Was gridlist compiled properly? */
+ if ( proj_errno(P) ) {
+ proj_log_error(P, "vgridshift: could not find required grid(s).");
+ return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
+ }
}
P->fwd4d = forward_4d;