diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-01-29 12:51:49 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-01-29 12:51:49 +0100 |
| commit | bf6b1a889b12d7ecdcb190cd14155e09e132095f (patch) | |
| tree | f742983990ca914ff3044352efd4e01eef5bd01d /src | |
| parent | 74a10a8de03deb823690f143e191087bf7c4821f (diff) | |
| parent | b113d0825cf82f66f738746db0f21745017552e9 (diff) | |
| download | PROJ-bf6b1a889b12d7ecdcb190cd14155e09e132095f.tar.gz PROJ-bf6b1a889b12d7ecdcb190cd14155e09e132095f.zip | |
Merge pull request #1891 from rouault/rfc5
Implement RFC5: Adopt GeoTIFF-based grids for grids delivered with PROJ
Diffstat (limited to 'src')
| -rw-r--r-- | src/filemanager.cpp | 202 | ||||
| -rw-r--r-- | src/filemanager.hpp | 3 | ||||
| -rw-r--r-- | src/init.cpp | 48 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 74 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 27 | ||||
| -rw-r--r-- | src/proj_constants.h | 2 |
6 files changed, 264 insertions, 92 deletions
diff --git a/src/filemanager.cpp b/src/filemanager.cpp index 9263e9fe..ac753012 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -40,6 +40,8 @@ #include "filemanager.hpp" #include "proj.h" #include "proj/internal/internal.hpp" +#include "proj/internal/io_internal.hpp" +#include "proj/io.hpp" #include "proj_internal.h" #include <sys/stat.h> @@ -68,6 +70,62 @@ File::File(const std::string &name) : name_(name) {} File::~File() = default; +// --------------------------------------------------------------------------- + +std::string File::read_line(size_t maxLen, bool &maxLenReached, + bool &eofReached) { + constexpr size_t MAX_MAXLEN = 1024 * 1024; + maxLen = std::min(maxLen, MAX_MAXLEN); + while (true) { + // Consume existing lines in buffer + size_t pos = readLineBuffer_.find_first_of("\r\n"); + if (pos != std::string::npos) { + if (pos > maxLen) { + std::string ret(readLineBuffer_.substr(0, maxLen)); + readLineBuffer_ = readLineBuffer_.substr(maxLen); + maxLenReached = true; + eofReached = false; + return ret; + } + std::string ret(readLineBuffer_.substr(0, pos)); + if (readLineBuffer_[pos] == '\r' && + readLineBuffer_[pos + 1] == '\n') { + pos += 1; + } + readLineBuffer_ = readLineBuffer_.substr(pos + 1); + maxLenReached = false; + eofReached = false; + return ret; + } + + const size_t prevSize = readLineBuffer_.size(); + if (maxLen <= prevSize) { + std::string ret(readLineBuffer_.substr(0, maxLen)); + readLineBuffer_ = readLineBuffer_.substr(maxLen); + maxLenReached = true; + eofReached = false; + return ret; + } + + if (eofReadLine_) { + std::string ret = readLineBuffer_; + readLineBuffer_.clear(); + maxLenReached = false; + eofReached = ret.empty(); + return ret; + } + + readLineBuffer_.resize(maxLen); + const size_t nRead = + read(&readLineBuffer_[prevSize], maxLen - prevSize); + if (nRead < maxLen - prevSize) + eofReadLine_ = true; + readLineBuffer_.resize(prevSize + nRead); + } +} + +// --------------------------------------------------------------------------- + #ifdef _WIN32 /* The bulk of utf8towc()/utf8fromwc() is derived from the utf.c module from @@ -1460,6 +1518,20 @@ static void *pj_open_file_with_manager(projCtx ctx, const char *name, .release(); } +// --------------------------------------------------------------------------- + +static NS_PROJ::io::DatabaseContextPtr getDBcontext(PJ_CONTEXT *ctx) { + try { + if (ctx->cpp_context == nullptr) { + ctx->cpp_context = new projCppContext(ctx); + } + return ctx->cpp_context->getDatabaseContext().as_nullable(); + } catch (const std::exception &e) { + pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what()); + return nullptr; + } +} + /************************************************************************/ /* FileManager::open_resource_file() */ /************************************************************************/ @@ -1475,27 +1547,54 @@ NS_PROJ::FileManager::open_resource_file(projCtx ctx, const char *name) { reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal( ctx, name, "rb", pj_open_file_with_manager, nullptr, 0))); - // Retry with a .tif extension if the file name doesn't end with .tif + // Retry with the new proj grid name if the file name doesn't end with .tif if (file == nullptr && !is_tilde_slash(name) && !is_rel_or_absolute_filename(name) && !starts_with(name, "http://") && !starts_with(name, "https://") && strcmp(name, "proj.db") != 0 && strstr(name, ".tif") == nullptr) { - std::string filename(name); - auto pos = filename.rfind('.'); - if (pos + 4 == filename.size()) { - filename = filename.substr(0, pos) + ".tif"; - file.reset(reinterpret_cast<NS_PROJ::File *>( - pj_open_lib_internal(ctx, filename.c_str(), "rb", - pj_open_file_with_manager, nullptr, 0))); - } else { - // For example for resource files like 'alaska' - filename += ".tif"; - file.reset(reinterpret_cast<NS_PROJ::File *>( - pj_open_lib_internal(ctx, filename.c_str(), "rb", - pj_open_file_with_manager, nullptr, 0))); + + auto dbContext = getDBcontext(ctx); + if (dbContext) { + try { + auto filename = dbContext->getProjGridName(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))); + if (file) { + pj_ctx_set_errno(ctx, 0); + } + } + } catch (const std::exception &e) { + pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what()); + return nullptr; + } } - if (file) { - pj_ctx_set_errno(ctx, 0); + } + // Retry with the old proj grid name if the file name ends with .tif + else if (file == nullptr && !is_tilde_slash(name) && + !is_rel_or_absolute_filename(name) && + !starts_with(name, "http://") && !starts_with(name, "https://") && + strstr(name, ".tif") != nullptr) { + + auto dbContext = getDBcontext(ctx); + if (dbContext) { + try { + auto filename = dbContext->getOldProjGridName(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))); + if (file) { + pj_ctx_set_errno(ctx, 0); + } + } + } catch (const std::exception &e) { + pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what()); + return nullptr; + } } } @@ -1508,35 +1607,12 @@ NS_PROJ::FileManager::open_resource_file(projCtx ctx, const char *name) { remote_file += '/'; } 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(), - NS_PROJ::FileAccess::READ_ONLY); - if (file) { - pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s", - remote_file.c_str()); - pj_ctx_set_errno(ctx, 0); - } - } else { - // For example for resource files like 'alaska' - auto remote_file_tif = remote_file + ".tif"; - file = open(ctx, remote_file_tif.c_str(), - NS_PROJ::FileAccess::READ_ONLY); - if (file) { - pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s", - remote_file_tif.c_str()); - pj_ctx_set_errno(ctx, 0); - } else { - // Init files - file = open(ctx, remote_file.c_str(), - NS_PROJ::FileAccess::READ_ONLY); - if (file) { - pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s", - remote_file.c_str()); - pj_ctx_set_errno(ctx, 0); - } - } + file = + open(ctx, remote_file.c_str(), NS_PROJ::FileAccess::READ_ONLY); + if (file) { + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s", + remote_file.c_str()); + pj_ctx_set_errno(ctx, 0); } } } @@ -1571,7 +1647,8 @@ PAFile pj_open_lib(projCtx ctx, const char *name, const char *mode) { * as a short filename. * * @param ctx context. - * @param short_filename short filename (e.g. egm96_15.gtx). Must not be NULL. + * @param short_filename short filename (e.g. us_nga_egm96_15.tif). + * Must not be NULL. * @param out_full_filename output buffer, of size out_full_filename_size, that * will receive the full filename on success. * Will be zero-terminated. @@ -1580,14 +1657,33 @@ PAFile pj_open_lib(projCtx ctx, const char *name, const char *mode) { */ int pj_find_file(projCtx ctx, const char *short_filename, char *out_full_filename, size_t out_full_filename_size) { - auto f = 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)); - if (f != nullptr) { - delete f; - return 1; + 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; + } + } } - return 0; + + return file != nullptr; } /************************************************************************/ diff --git a/src/filemanager.hpp b/src/filemanager.hpp index 787af342..bbd12b7e 100644 --- a/src/filemanager.hpp +++ b/src/filemanager.hpp @@ -76,6 +76,8 @@ class FileManager { class File { protected: std::string name_; + std::string readLineBuffer_{}; + bool eofReadLine_ = false; explicit File(const std::string &name); public: @@ -86,6 +88,7 @@ class File { virtual unsigned long long tell() = 0; virtual void reassign_context(PJ_CONTEXT *ctx) = 0; virtual bool hasChanged() const = 0; + std::string read_line(size_t maxLen, bool &maxLenReached, bool &eofReached); const std::string &name() const { return name_; } }; diff --git a/src/init.cpp b/src/init.cpp index c1683285..9c7b7b19 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -39,6 +39,7 @@ #include "geodesic.h" #include "proj.h" #include "proj_internal.h" +#include "filemanager.hpp" #include <math.h> @@ -81,18 +82,10 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { char *fname, *section; const char *key; char *buffer = nullptr; - char *line = nullptr; - PAFile fid; size_t n; - - line = static_cast<char*>(pj_malloc (MAX_LINE_LENGTH + 1)); - if (nullptr==line) - return nullptr; - fname = static_cast<char*>(pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3)); if (nullptr==fname) { - pj_dealloc (line); return nullptr; } @@ -104,7 +97,6 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { key += 5; if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) { pj_dealloc (fname); - pj_dealloc (line); return nullptr; } memmove (fname, key, strlen (key) + 1); @@ -114,7 +106,6 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { if (nullptr==section) { proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING); pj_dealloc (fname); - pj_dealloc (line); return nullptr; } *section = 0; @@ -124,36 +115,36 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { "get_init_string: searching for section [%s] in init file [%s]", section, fname); - fid = pj_open_lib (ctx, fname, "rt"); - if (nullptr==fid) { + auto file = NS_PROJ::FileManager::open_resource_file(ctx, fname); + if (nullptr==file) { pj_dealloc (fname); - pj_dealloc (line); proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); return nullptr; } /* Search for section in init file */ + std::string line; for (;;) { + bool eofReached = false; + bool maxLenReached = false; + line = file->read_line(MAX_LINE_LENGTH, maxLenReached, eofReached); /* End of file? */ - if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) { - pj_dealloc (buffer); + if (maxLenReached || eofReached) { pj_dealloc (fname); - pj_dealloc (line); - pj_ctx_fclose (ctx, fid); proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); return nullptr; } /* At start of right section? */ - pj_chomp (line); + pj_chomp (&line[0]); if ('<'!=line[0]) continue; - if (strlen (line) < n + 2) + if (strlen (line.c_str()) < n + 2) continue; if (line[n + 1] != '>') continue; - if (0==strncmp (line + 1, section, n)) + if (0==strncmp (line.data() + 1, section, n)) break; } @@ -161,13 +152,11 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { buffer = static_cast<char*>(pj_malloc (current_buffer_size)); if (nullptr==buffer) { pj_dealloc (fname); - pj_dealloc (line); - pj_ctx_fclose (ctx, fid); return nullptr; } /* Skip the "<section>" indicator, and copy the rest of the line over */ - strcpy (buffer, line + strlen (section) + 2); + strcpy (buffer, line.data() + strlen (section) + 2); /* Copy the remaining lines of the section to buffer */ for (;;) { @@ -181,15 +170,18 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { break; } + bool eofReached = false; + bool maxLenReached = false; + line = file->read_line(MAX_LINE_LENGTH, maxLenReached, eofReached); /* End of file? - done! */ - if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) + if (maxLenReached || eofReached) break; /* Otherwise, handle the line. It MAY be the start of the next section, */ /* but that will be handled at the start of next trip through the loop */ buffer_length = strlen (buffer); - pj_chomp (line); /* Remove '#' style comments */ - next_length = strlen (line) + buffer_length + 2; + pj_chomp (&line[0]); /* Remove '#' style comments */ + next_length = strlen (line.data()) + buffer_length + 2; if (next_length > current_buffer_size) { char *b = static_cast<char*>(pj_malloc (2 * current_buffer_size)); if (nullptr==b) { @@ -203,12 +195,10 @@ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { buffer = b; } buffer[buffer_length] = ' '; - strcpy (buffer + buffer_length + 1, line); + strcpy (buffer + buffer_length + 1, line.data()); } - pj_ctx_fclose (ctx, fid); pj_dealloc (fname); - pj_dealloc (line); if (nullptr==buffer) return nullptr; pj_shrink (buffer); diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 6eef6de8..8ca95223 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -8448,6 +8448,29 @@ static const std::string &_getCTABLE2Filename(const Transformation *op, //! @cond Doxygen_Suppress static const std::string & +_getHorizontalShiftGTIFFFilename(const Transformation *op, bool allowInverse) { + const auto &l_method = op->method(); + const auto &methodName = l_method->nameStr(); + if (ci_equal(methodName, PROJ_WKT2_NAME_METHOD_HORIZONTAL_SHIFT_GTIFF) || + (allowInverse && + ci_equal(methodName, + INVERSE_OF + PROJ_WKT2_NAME_METHOD_HORIZONTAL_SHIFT_GTIFF))) { + const auto &fileParameter = op->parameterValue( + EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE, + EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + return fileParameter->valueFile(); + } + } + return nullString; +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress +static const std::string & _getHeightToGeographic3DFilename(const Transformation *op, bool allowInverse) { const auto &methodName = op->method()->nameStr(); @@ -8658,7 +8681,30 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames( const auto &l_sourceCRS = sourceCRS(); const auto &l_targetCRS = targetCRS(); const auto &l_accuracies = coordinateOperationAccuracies(); - if (projGridFormat == "NTv1") { + if (projGridFormat == "GTiff") { + auto parameters = + std::vector<OperationParameterNNPtr>{createOpParamNameEPSGCode( + EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)}; + auto methodProperties = util::PropertyMap().set( + common::IdentifiedObject::NAME_KEY, + PROJ_WKT2_NAME_METHOD_HORIZONTAL_SHIFT_GTIFF); + auto values = std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename(projFilename)}; + if (inverseDirection) { + return create(createPropertiesForInverse( + self.as_nullable().get(), true, false), + l_targetCRS, l_sourceCRS, nullptr, + methodProperties, parameters, values, + l_accuracies) + ->inverseAsTransformation(); + + } else { + return create(createSimilarPropertiesTransformation(self), + l_sourceCRS, l_targetCRS, nullptr, + methodProperties, parameters, values, + l_accuracies); + } + } else if (projGridFormat == "NTv1") { if (inverseDirection) { return createNTv1(createPropertiesForInverse( self.as_nullable().get(), true, false), @@ -9336,10 +9382,14 @@ void Transformation::_exportToPROJString( const auto &NTv1Filename = _getNTv1Filename(this, true); const auto &NTv2Filename = _getNTv2Filename(this, true); const auto &CTABLE2Filename = _getCTABLE2Filename(this, true); + const auto &HorizontalShiftGTIFFFilename = + _getHorizontalShiftGTIFFFilename(this, true); const auto &hGridShiftFilename = - !NTv1Filename.empty() ? NTv1Filename : !NTv2Filename.empty() - ? NTv2Filename - : CTABLE2Filename; + !HorizontalShiftGTIFFFilename.empty() + ? HorizontalShiftGTIFFFilename + : !NTv1Filename.empty() ? NTv1Filename : !NTv2Filename.empty() + ? NTv2Filename + : CTABLE2Filename; if (!hGridShiftFilename.empty()) { auto sourceCRSGeog = dynamic_cast<const crs::GeographicCRS *>(sourceCRS().get()); @@ -9417,11 +9467,15 @@ void Transformation::_exportToPROJString( if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { formatter->addStep("vgridshift"); - // The vertcon grids go from NGVD 29 to NAVD 88, with units - // in millimeter (see - // https://github.com/OSGeo/proj.4/issues/1071) formatter->addParam("grids", fileParameter->valueFile()); - formatter->addParam("multiplier", 0.001); + if (fileParameter->valueFile().find(".tif") != std::string::npos) { + formatter->addParam("multiplier", 1.0); + } else { + // The vertcon grids go from NGVD 29 to NAVD 88, with units + // in millimeter (see + // https://github.com/OSGeo/proj.4/issues/1071), for gtx files + formatter->addParam("multiplier", 0.001); + } return; } } @@ -11155,7 +11209,9 @@ struct FilterResults { !gridDesc.available) { gridsAvailable = false; } - if (gridDesc.packageName.empty() && !gridDesc.available) { + if (gridDesc.packageName.empty() && + !(!gridDesc.url.empty() && gridDesc.openLicense) && + !gridDesc.available) { gridsKnown = false; } } diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 7e680d31..54aa993f 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -987,7 +987,8 @@ bool DatabaseContext::lookForGridInfo( openLicense = (row[3].empty() ? row[4] : row[3]) == "1"; directDownload = (row[5].empty() ? row[6] : row[5]) == "1"; - if (considerKnownGridsAsAvailable && !packageName.empty()) { + if (considerKnownGridsAsAvailable && + (!packageName.empty() || (!url.empty() && openLicense))) { gridAvailable = true; } @@ -1012,6 +1013,30 @@ bool DatabaseContext::isKnownName(const std::string &name, sql += "\" WHERE name = ? LIMIT 1"; return !d->run(sql, {name}).empty(); } +// --------------------------------------------------------------------------- + +std::string +DatabaseContext::getProjGridName(const std::string &oldProjGridName) { + auto res = d->run("SELECT proj_grid_name FROM grid_alternatives WHERE " + "old_proj_grid_name = ?", + {oldProjGridName}); + if (res.empty()) { + return std::string(); + } + return res.front()[0]; +} + +// --------------------------------------------------------------------------- + +std::string DatabaseContext::getOldProjGridName(const std::string &gridName) { + auto res = d->run("SELECT old_proj_grid_name FROM grid_alternatives WHERE " + "proj_grid_name = ?", + {gridName}); + if (res.empty()) { + return std::string(); + } + return res.front()[0]; +} // --------------------------------------------------------------------------- diff --git a/src/proj_constants.h b/src/proj_constants.h index d5b484c6..54dc7873 100644 --- a/src/proj_constants.h +++ b/src/proj_constants.h @@ -508,6 +508,8 @@ #define PROJ_WKT2_NAME_METHOD_CTABLE2 "CTABLE2" +#define PROJ_WKT2_NAME_METHOD_HORIZONTAL_SHIFT_GTIFF "HORIZONTAL_SHIFT_GTIFF" + /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_VERTCON 9658 |
