diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-01-17 17:01:20 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-01-17 21:57:27 +0100 |
| commit | c0ac895d77e823fe9a9458d013eb0f6378f39060 (patch) | |
| tree | 9e7641cda61a75c3a6209d17dad6fa7c8448cd44 /src | |
| parent | 3121d9bc309b439adcc2ab9743a3d2b3a8f48296 (diff) | |
| download | PROJ-c0ac895d77e823fe9a9458d013eb0f6378f39060.tar.gz PROJ-c0ac895d77e823fe9a9458d013eb0f6378f39060.zip | |
Remove proj_create_from_proj_string() and proj_create_from_user_input(), and make proj_create() do more or less what proj_create_from_user_input() did before (fixes #1214)
Diffstat (limited to 'src')
| -rw-r--r-- | src/4D_api.cpp | 57 | ||||
| -rw-r--r-- | src/apps/cs2cs.cpp | 17 | ||||
| -rw-r--r-- | src/init.cpp | 10 | ||||
| -rw-r--r-- | src/internal.cpp | 2 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 97 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 473 | ||||
| -rw-r--r-- | src/pipeline.cpp | 2 | ||||
| -rw-r--r-- | src/proj.h | 16 | ||||
| -rw-r--r-- | src/proj_internal.h | 3 | ||||
| -rw-r--r-- | src/proj_symbol_rename.h | 4 |
10 files changed, 388 insertions, 293 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 710b59e3..5502c96f 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -462,7 +462,7 @@ Returns 1 on success, 0 on failure if (nullptr==def) return 0; sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); free (def); if (nullptr==Q) return 0; @@ -477,7 +477,7 @@ Returns 1 on success, 0 on failure if (nullptr==def) return 0; sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); free (def); if (nullptr==Q) return 0; @@ -492,7 +492,7 @@ Returns 1 on success, 0 on failure if (nullptr==def) return 0; sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); free (def); if (nullptr==Q) return 0; @@ -524,7 +524,7 @@ Returns 1 on success, 0 on failure if (nullptr==def) return 0; sprintf (def, "break_cs2cs_recursion proj=helmert exact %s convention=position_vector", s); - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); free(def); if (nullptr==Q) return 0; @@ -550,14 +550,14 @@ Returns 1 on success, 0 on failure *next_pos = '.'; } } - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); if (nullptr==Q) return 0; P->cart = skip_prep_fin (Q); if (!P->is_geocent) { sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); - Q = proj_create (P->ctx, def); + Q = pj_create_internal (P->ctx, def); if (nullptr==Q) return 0; P->cart_wgs84 = skip_prep_fin (Q); @@ -568,9 +568,10 @@ Returns 1 on success, 0 on failure } - /*************************************************************************************/ -PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { +PJ *pj_create_internal (PJ_CONTEXT *ctx, const char *definition) { +/*************************************************************************************/ + /************************************************************************************** Create a new PJ object in the context ctx, using the given definition. If ctx==0, the default context is used, if definition==0, or invalid, a null-pointer is @@ -623,8 +624,6 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { return P; } - - /*************************************************************************************/ PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv) { /************************************************************************************** @@ -657,6 +656,34 @@ indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", return P; } +/*************************************************************************************/ +PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv) { +/************************************************************************************** +Same as proj_create_argv() but calls pj_create_internal() instead of proj_create() internally +**************************************************************************************/ + PJ *P; + const char *c; + + if (nullptr==ctx) + ctx = pj_get_default_ctx (); + if (nullptr==argv) { + proj_context_errno_set(ctx, PJD_ERR_NO_ARGS); + return nullptr; + } + + /* We assume that free format is used, and build a full proj_create compatible string */ + c = pj_make_args (argc, argv); + if (nullptr==c) { + proj_context_errno_set(ctx, ENOMEM); + return nullptr; + } + + P = pj_create_internal (ctx, c); + + pj_dealloc ((char *) c); + return P; +} + /** Create an area of use */ PJ_AREA * proj_area_create(void) { return static_cast<PJ_AREA*>(pj_calloc(1, sizeof(PJ_AREA))); @@ -761,7 +788,7 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char - a PROJ string, like "+proj=longlat +datum=WGS84". When using that syntax, the axis order and unit for geographic CRS will be longitude, latitude, and the unit degrees. - - more generally any string accepted by proj_create_from_user_input() + - more generally any string accepted by proj_create() An "area of use" can be specified in area. When it is supplied, the more accurate transformation between two given systems can be chosen. @@ -772,27 +799,23 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char ******************************************************************************/ const char* proj_string; - const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", nullptr }; if( !ctx ) { ctx = pj_get_default_ctx(); } - const char* const* optionsImportCRS = - proj_context_get_use_proj4_init_rules(ctx, FALSE) ? optionsProj4Mode : nullptr; - try { std::string source_crs_modified(pj_add_type_crs_if_needed(source_crs)); std::string target_crs_modified(pj_add_type_crs_if_needed(target_crs)); - auto src = proj_create_from_user_input(ctx, source_crs_modified.c_str(), optionsImportCRS); + auto src = proj_create(ctx, source_crs_modified.c_str()); if( !src ) { proj_context_log_debug(ctx, "Cannot instantiate source_crs"); return nullptr; } - auto dst = proj_create_from_user_input(ctx, target_crs_modified.c_str(), optionsImportCRS); + auto dst = proj_create(ctx, target_crs_modified.c_str()); if( !dst ) { proj_context_log_debug(ctx, "Cannot instantiate target_crs"); proj_destroy(src); diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index bde4f813..1c49965a 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -210,12 +210,10 @@ static void process(FILE *fid) /************************************************************************/ static PJ *instanciate_crs(const std::string &definition, - const char *const *optionsImportCRS, bool &isGeog, double &toRadians, bool &isLatFirst) { - PJ *crs = proj_create_from_user_input(nullptr, - pj_add_type_crs_if_needed(definition).c_str(), - optionsImportCRS); + PJ *crs = proj_create(nullptr, + pj_add_type_crs_if_needed(definition).c_str()); if (!crs) { return nullptr; } @@ -536,16 +534,13 @@ int main(int argc, char **argv) { emess(3, "missing source and target coordinate systems"); } - const char *const optionsProj4Mode[] = {"USE_PROJ4_INIT_RULES=YES", - nullptr}; - const char *const *optionsImportCRS = - proj_context_get_use_proj4_init_rules(nullptr, TRUE) ? optionsProj4Mode - : nullptr; + proj_context_use_proj4_init_rules(nullptr, + proj_context_get_use_proj4_init_rules(nullptr, TRUE)); PJ *src = nullptr; if (!fromStr.empty()) { bool ignored; - src = instanciate_crs(fromStr, optionsImportCRS, srcIsGeog, + src = instanciate_crs(fromStr, srcIsGeog, srcToRadians, ignored); if (!src) { emess(3, "cannot instantiate source coordinate system"); @@ -554,7 +549,7 @@ int main(int argc, char **argv) { PJ *dst = nullptr; if (!toStr.empty()) { - dst = instanciate_crs(toStr, optionsImportCRS, destIsGeog, + dst = instanciate_crs(toStr, destIsGeog, destToRadians, destIsLatLong); if (!dst) { emess(3, "cannot instantiate target coordinate system"); diff --git a/src/init.cpp b/src/init.cpp index 2ed34a09..8af3d26c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -238,6 +238,10 @@ Expand key from buffer or (if not in buffer) from init file char *definition = nullptr; paralist *init_items = nullptr; + if( !ctx ) { + ctx = pj_get_default_ctx(); + } + /* support "init=file:section", "+init=file:section", and "file:section" format */ xkey = strstr (key, "init="); if (nullptr==xkey) @@ -270,7 +274,6 @@ Expand key from buffer or (if not in buffer) from init file } if( !exists ) { - const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", nullptr }; char szInitStr[7 + 64]; PJ* src; const char* proj_string; @@ -287,7 +290,10 @@ Expand key from buffer or (if not in buffer) from init file strcpy(szInitStr, "+init="); strcat(szInitStr, xkey); - src = proj_create_from_user_input(ctx, szInitStr, optionsProj4Mode); + auto old_proj4_init_rules = ctx->use_proj4_init_rules; + ctx->use_proj4_init_rules = true; + src = proj_create(ctx, szInitStr); + ctx->use_proj4_init_rules = old_proj4_init_rules; if( !src ) { return nullptr; } diff --git a/src/internal.cpp b/src/internal.cpp index fbc7c9a1..84db5d82 100644 --- a/src/internal.cpp +++ b/src/internal.cpp @@ -207,6 +207,8 @@ consuming their surrounding whitespace. pj_chomp (c); n = strlen (c); + if (n==0) + return c; /* First collapse repeated whitespace (including +/;) */ i = 0; diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index d13c33c7..06a3c02e 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -47,6 +47,7 @@ #include "proj/util.hpp" #include "proj/internal/internal.hpp" +#include "proj/internal/io_internal.hpp" // PROJ include order is sensitive // clang-format off @@ -91,25 +92,6 @@ static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function, //! @cond Doxygen_Suppress -/** Auxiliary structure to PJ_CONTEXT storing C++ context stuff. */ -struct projCppContext { - DatabaseContextNNPtr databaseContext; - std::string lastUOMName_{}; - - explicit projCppContext(PJ_CONTEXT *ctx, const char *dbPath = nullptr, - const char *const *auxDbPaths = nullptr) - : databaseContext(DatabaseContext::create( - dbPath ? dbPath : std::string(), toVector(auxDbPaths), ctx)) {} - - static std::vector<std::string> toVector(const char *const *auxDbPaths) { - std::vector<std::string> res; - for (auto iter = auxDbPaths; iter && *iter; ++iter) { - res.emplace_back(std::string(*iter)); - } - return res; - } -}; - // --------------------------------------------------------------------------- void proj_context_delete_cpp_context(struct projCppContext *cppContext) { @@ -150,8 +132,9 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { try { auto formatter = PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, dbContext); - auto pj = proj_create( - ctx, coordop->exportToPROJString(formatter.get()).c_str()); + auto projString = coordop->exportToPROJString(formatter.get()); + auto pj = pj_create_internal( + ctx, projString.empty() ? "+proj=affine" : projString.c_str()); if (pj) { pj->iso_obj = objIn; return pj; @@ -346,41 +329,20 @@ PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) { * * @param ctx PROJ context, or NULL for default context * @param text String (must not be NULL) - * @param options null-terminated list of options, or NULL. Currently - * supported options are: - * <ul> - * <li>USE_PROJ4_INIT_RULES=YES/NO. Defaults to NO. When set to YES, - * init=epsg:XXXX syntax will be allowed and will be interpreted according to - * PROJ.4 and PROJ.5 rules, that is geodeticCRS will have longitude, latitude - * order and will expect/output coordinates in radians. ProjectedCRS will have - * easting, northing axis order (except the ones with Transverse Mercator South - * Orientated projection). In that mode, the epsg:XXXX syntax will be also - * interprated the same way.</li> - * </ul> * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ -PJ *proj_create_from_user_input(PJ_CONTEXT *ctx, const char *text, - const char *const *options) { +PJ *proj_create(PJ_CONTEXT *ctx, const char *text) { SANITIZE_CTX(ctx); assert(text); - (void)options; - auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + + // Only connect to proj.db if needed + if( strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr ) { + getDBcontextNoException(ctx, __FUNCTION__); + } try { - bool usePROJ4InitRules = false; - for (auto iter = options; iter && iter[0]; ++iter) { - const char *value; - if ((value = getOptionValue(*iter, "USE_PROJ4_INIT_RULES="))) { - usePROJ4InitRules = ci_equal(value, "YES"); - } else { - std::string msg("Unknown option :"); - msg += *iter; - proj_log_error(ctx, __FUNCTION__, msg.c_str()); - return nullptr; - } - } auto identifiedObject = nn_dynamic_pointer_cast<IdentifiedObject>( - createFromUserInput(text, dbContext, usePROJ4InitRules)); + createFromUserInput(text, ctx)); if (identifiedObject) { return pj_obj_create(ctx, NN_NO_CHECK(identifiedObject)); } @@ -520,43 +482,6 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, // --------------------------------------------------------------------------- -/** \brief Instantiate an object from a PROJ string. - * - * This function calls osgeo::proj::io::PROJStringParser::createFromPROJString() - * - * The returned object must be unreferenced with proj_destroy() after use. - * It should be used by at most one thread at a time. - * - * @param ctx PROJ context, or NULL for default context - * @param proj_string PROJ string (must not be NULL) - * @param options should be set to NULL for now - * @return Object that must be unreferenced with proj_destroy(), or NULL in - * case of error. - */ -PJ *proj_create_from_proj_string(PJ_CONTEXT *ctx, const char *proj_string, - const char *const *options) { - SANITIZE_CTX(ctx); - (void)options; - assert(proj_string); - try { - PROJStringParser parser; - auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); - if (dbContext) { - parser.attachDatabaseContext(NN_NO_CHECK(dbContext)); - } - auto identifiedObject = nn_dynamic_pointer_cast<IdentifiedObject>( - parser.createFromPROJString(proj_string)); - if (identifiedObject) { - return pj_obj_create(ctx, NN_NO_CHECK(identifiedObject)); - } - } catch (const std::exception &e) { - proj_log_error(ctx, __FUNCTION__, e.what()); - } - return nullptr; -} - -// --------------------------------------------------------------------------- - /** \brief Instantiate an object from a database lookup. * * The returned object must be unreferenced with proj_destroy() after use. diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index d403495b..b75d9fd6 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4284,58 +4284,44 @@ BaseObjectNNPtr WKTParser::Private::build(const WKTNodeNNPtr &node) { throw ParsingException(concat("unhandled keyword: ", name)); } -//! @endcond // --------------------------------------------------------------------------- -/** \brief Instantiate a sub-class of BaseObject from a user specified text. - * - * The text can be a: - * <ul> - * <li>WKT string</li> - * <li>PROJ string</li> - * <li>database code, prefixed by its authoriy. e.g. "EPSG:4326"</li> - * <li>URN. e.g. "urn:ogc:def:crs:EPSG::4326", - * "urn:ogc:def:coordinateOperation:EPSG::1671"</li> - * <li>an objet name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as - * uniqueness is not guaranteed, the function may apply heuristics to - * determine the appropriate best match.</li> - * </ul> - * - * @param text One of the above mentioned text format - * @param dbContext Database context, or nullptr (in which case database - * lookups will not work) - * @param usePROJ4InitRules When set to true, - * init=epsg:XXXX syntax will be allowed and will be interpreted according to - * PROJ.4 and PROJ.5 rules, that is geodeticCRS will have longitude, latitude - * order and will expect/output coordinates in radians. ProjectedCRS will have - * easting, northing axis order (except the ones with Transverse Mercator South - * Orientated projection). In that mode, the epsg:XXXX syntax will be also - * interprated the same way. - * @throw ParsingException - */ -BaseObjectNNPtr createFromUserInput(const std::string &text, - const DatabaseContextPtr &dbContext, - bool usePROJ4InitRules) { +static BaseObjectNNPtr createFromUserInput(const std::string &text, + const DatabaseContextPtr &dbContext, + bool usePROJ4InitRules, + PJ_CONTEXT *ctx) { - for (const auto &wktConstants : WKTConstants::constants()) { - if (ci_starts_with(text, wktConstants)) { - return WKTParser() - .attachDatabaseContext(dbContext) - .setStrict(false) - .createFromWKT(text); + if (!ci_starts_with(text, "step proj=") && + !ci_starts_with(text, "step +proj=")) { + for (const auto &wktConstants : WKTConstants::constants()) { + if (ci_starts_with(text, wktConstants)) { + return WKTParser() + .attachDatabaseContext(dbContext) + .setStrict(false) + .createFromWKT(text); + } } } + const char *textWithoutPlusPrefix = text.c_str(); if (textWithoutPlusPrefix[0] == '+') textWithoutPlusPrefix++; if (strncmp(textWithoutPlusPrefix, "proj=", strlen("proj=")) == 0 || + text.find(" +proj=") != std::string::npos || + text.find(" proj=") != std::string::npos || strncmp(textWithoutPlusPrefix, "init=", strlen("init=")) == 0 || + text.find(" +init=") != std::string::npos || + text.find(" init=") != std::string::npos || strncmp(textWithoutPlusPrefix, "title=", strlen("title=")) == 0) { return PROJStringParser() .attachDatabaseContext(dbContext) - .setUsePROJ4InitRules(usePROJ4InitRules) + .attachContext(ctx) + .setUsePROJ4InitRules(ctx != nullptr + ? (proj_context_get_use_proj4_init_rules( + ctx, false) == TRUE) + : usePROJ4InitRules) .createFromPROJString(text); } @@ -4452,6 +4438,69 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, throw ParsingException("unrecognized format / unknown name"); } +//! @endcond + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a sub-class of BaseObject from a user specified text. + * + * The text can be a: + * <ul> + * <li>WKT string</li> + * <li>PROJ string</li> + * <li>database code, prefixed by its authoriy. e.g. "EPSG:4326"</li> + * <li>URN. e.g. "urn:ogc:def:crs:EPSG::4326", + * "urn:ogc:def:coordinateOperation:EPSG::1671"</li> + * <li>an objet name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as + * uniqueness is not guaranteed, the function may apply heuristics to + * determine the appropriate best match.</li> + * </ul> + * + * @param text One of the above mentioned text format + * @param dbContext Database context, or nullptr (in which case database + * lookups will not work) + * @param usePROJ4InitRules When set to true, + * init=epsg:XXXX syntax will be allowed and will be interpreted according to + * PROJ.4 and PROJ.5 rules, that is geodeticCRS will have longitude, latitude + * order and will expect/output coordinates in radians. ProjectedCRS will have + * easting, northing axis order (except the ones with Transverse Mercator South + * Orientated projection). In that mode, the epsg:XXXX syntax will be also + * interprated the same way. + * @throw ParsingException + */ +BaseObjectNNPtr createFromUserInput(const std::string &text, + const DatabaseContextPtr &dbContext, + bool usePROJ4InitRules) { + return createFromUserInput(text, dbContext, usePROJ4InitRules, nullptr); +} + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a sub-class of BaseObject from a user specified text. + * + * The text can be a: + * <ul> + * <li>WKT string</li> + * <li>PROJ string</li> + * <li>database code, prefixed by its authoriy. e.g. "EPSG:4326"</li> + * <li>URN. e.g. "urn:ogc:def:crs:EPSG::4326", + * "urn:ogc:def:coordinateOperation:EPSG::1671"</li> + * <li>an objet name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as + * uniqueness is not guaranteed, the function may apply heuristics to + * determine the appropriate best match.</li> + * </ul> + * + * @param text One of the above mentioned text format + * @param ctx PROJ context + * @throw ParsingException + */ +BaseObjectNNPtr createFromUserInput(const std::string &text, PJ_CONTEXT *ctx) { + return createFromUserInput( + text, ctx != nullptr && ctx->cpp_context + ? ctx->cpp_context->databaseContext.as_nullable() + : nullptr, + false, ctx); +} // --------------------------------------------------------------------------- @@ -5223,17 +5272,68 @@ void PROJStringFormatter::Private::appendToResult(const char *str) { static void PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, std::vector<Step::KeyValue> &globalParamValues, - std::string &title, std::string &vunits, - std::string &vto_meter) { + std::string &title) { std::string word; std::istringstream iss(projString, std::istringstream::in); - bool inverted = false; - bool prevWasStep = false; - bool inProj = false; - bool inPipeline = false; bool prevWasTitle = false; - bool prevWasInit = false; + if (projString.find("proj=pipeline") == std::string::npos) { + const bool hasProj = projString.find("proj=") == 0 || + projString.find("+proj=") == 0 || + projString.find(" proj=") != std::string::npos || + projString.find(" +proj=") != std::string::npos; + const bool hasInit = projString.find("init=") == 0 || + projString.find("+init=") == 0 || + projString.find(" init=") != std::string::npos || + projString.find(" +init=") != std::string::npos; + if (hasProj || hasInit) { + steps.push_back(Step()); + } + + while (iss >> word) { + if (word[0] == '+') { + word = word.substr(1); + } else if (prevWasTitle && word.find('=') == std::string::npos) { + title += " "; + title += word; + continue; + } + + prevWasTitle = false; + if (starts_with(word, "proj=") && !hasInit) { + assert(hasProj); + auto stepName = word.substr(strlen("proj=")); + steps.back().name = stepName; + } else if (starts_with(word, "init=")) { + assert(hasInit); + auto initName = word.substr(strlen("init=")); + steps.back().name = initName; + steps.back().isInit = true; + } else if (word == "inv") { + if (!steps.empty()) { + steps.back().inverted = true; + } + } else if (starts_with(word, "title=")) { + title = word.substr(strlen("title=")); + prevWasTitle = true; + } else if (word != "step") { + const auto pos = word.find('='); + auto key = word.substr(0, pos); + auto pair = (pos != std::string::npos) + ? Step::KeyValue(key, word.substr(pos + 1)) + : Step::KeyValue(key); + if (steps.empty()) { + globalParamValues.push_back(pair); + } else { + steps.back().paramValues.push_back(pair); + } + } + } + return; + } + + bool inPipeline = false; + bool invGlobal = false; while (iss >> word) { if (word[0] == '+') { word = word.substr(1); @@ -5248,52 +5348,31 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, if (inPipeline) { throw ParsingException("nested pipeline not supported"); } - inverted = false; - prevWasStep = false; - inProj = true; inPipeline = true; } else if (word == "step") { if (!inPipeline) { throw ParsingException("+step found outside pipeline"); } - inverted = false; - prevWasStep = true; - prevWasInit = false; + steps.push_back(Step()); } else if (word == "inv") { - if (prevWasStep) { - inverted = true; + if (steps.empty()) { + invGlobal = true; } else { - if (steps.empty()) { - throw ParsingException("+inv found at unexpected place"); - } steps.back().inverted = true; } - prevWasStep = false; - } else if (starts_with(word, "proj=")) { + } else if (inPipeline && !steps.empty() && starts_with(word, "proj=") && + steps.back().name.empty()) { auto stepName = word.substr(strlen("proj=")); - if (prevWasInit) { - steps.back() = Step(); - prevWasInit = false; - } else { - steps.push_back(Step()); - } steps.back().name = stepName; - steps.back().inverted = inverted; - prevWasStep = false; - inProj = true; - } else if (starts_with(word, "init=")) { - if (prevWasInit) { - throw ParsingException("+init= found at unexpected place"); - } + } else if (inPipeline && !steps.empty() && starts_with(word, "init=") && + steps.back().name.empty()) { auto initName = word.substr(strlen("init=")); - steps.push_back(Step()); steps.back().name = initName; steps.back().isInit = true; - steps.back().inverted = inverted; - prevWasStep = false; - prevWasInit = true; - inProj = true; - } else if (inProj) { + } else if (!inPipeline && starts_with(word, "title=")) { + title = word.substr(strlen("title=")); + prevWasTitle = true; + } else { const auto pos = word.find('='); auto key = word.substr(0, pos); auto pair = (pos != std::string::npos) @@ -5304,21 +5383,14 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, } else { steps.back().paramValues.push_back(pair); } - prevWasStep = false; - } else if (starts_with(word, "vunits=")) { - vunits = word.substr(strlen("vunits=")); - } else if (starts_with(word, "vto_meter=")) { - vto_meter = word.substr(strlen("vto_meter=")); - } else if (word == "type=crs" && - (!vunits.empty() || !vto_meter.empty())) { - // ok - } else if (starts_with(word, "title=")) { - title = word.substr(strlen("title=")); - prevWasTitle = true; - } else { - throw ParsingException("Unexpected token: " + word); } } + if (invGlobal) { + for (auto &step : steps) { + step.inverted = !step.inverted; + } + std::reverse(steps.begin(), steps.end()); + } } // --------------------------------------------------------------------------- @@ -5328,10 +5400,7 @@ void PROJStringFormatter::ingestPROJString( { std::vector<Step> steps; std::string title; - std::string vunits; - std::string vto_meter; - PROJStringSyntaxParser(str, steps, d->globalParamValues_, title, vunits, - vto_meter); + PROJStringSyntaxParser(str, steps, d->globalParamValues_, title); d->steps_.insert(d->steps_.end(), steps.begin(), steps.end()); } @@ -5605,6 +5674,7 @@ const DatabaseContextPtr &PROJStringFormatter::databaseContext() const { struct PROJStringParser::Private { DatabaseContextPtr dbContext_{}; + PJ_CONTEXT *ctx_{}; bool usePROJ4InitRules_ = false; std::vector<std::string> warningList_{}; @@ -5632,6 +5702,17 @@ struct PROJStringParser::Private { template <class T> // cppcheck-suppress functionStatic + const std::string &getGlobalParamValue(const T key) { + for (const auto &pair : globalParamValues_) { + if (ci_equal(pair.key, key)) { + return pair.value; + } + } + return emptyString; + } + + template <class T> + // cppcheck-suppress functionStatic const std::string &getParamValue(const Step &step, const T key) { for (const auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { @@ -5713,6 +5794,15 @@ PROJStringParser::attachDatabaseContext(const DatabaseContextPtr &dbContext) { // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +PROJStringParser &PROJStringParser::attachContext(PJ_CONTEXT *ctx) { + d->ctx_ = ctx; + return *this; +} +//! @endcond + +// --------------------------------------------------------------------------- + /** \brief Set how init=epsg:XXXX syntax should be interpreted. * * @param enable When set to true, @@ -7269,18 +7359,16 @@ static const metadata::ExtentPtr &getExtent(const crs::CRS *crs) { */ BaseObjectNNPtr PROJStringParser::createFromPROJString(const std::string &projString) { - std::string vunits; - std::string vto_meter; - d->steps_.clear(); d->title_.clear(); d->globalParamValues_.clear(); d->projString_ = projString; PROJStringSyntaxParser(projString, d->steps_, d->globalParamValues_, - d->title_, vunits, vto_meter); + d->title_); if (d->steps_.empty()) { - + const auto &vunits = d->getGlobalParamValue("vunits"); + const auto &vto_meter = d->getGlobalParamValue("vto_meter"); if (!vunits.empty() || !vto_meter.empty()) { Step fakeStep; if (!vunits.empty()) { @@ -7301,9 +7389,10 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } } - if ((d->steps_.size() == 1 || + if (((d->steps_.size() == 1 && + d->getParamValue(d->steps_[0], "type") == "crs") || (d->steps_.size() == 2 && d->steps_[1].name == "unitconvert")) && - isGeocentricStep(d->steps_[0].name)) { + !d->steps_[0].inverted && isGeocentricStep(d->steps_[0].name)) { return d->buildBoundOrCompoundCRSIfNeeded( 0, d->buildGeocentricCRS(0, (d->steps_.size() == 2 && d->steps_[1].name == "unitconvert") @@ -7313,8 +7402,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) { // +init=xxxx:yyyy syntax if (d->steps_.size() == 1 && d->steps_[0].isInit && - (d->steps_[0].paramValues.size() == 0 || - d->getParamValue(d->steps_[0], "type") == "crs")) { + !d->steps_[0].inverted) { // Those used to come from a text init file // We only support them in compatibility mode @@ -7345,9 +7433,14 @@ PROJStringParser::createFromPROJString(const std::string &projString) { if (!file_found) { auto obj = createFromUserInput(stepName, d->dbContext_, true); auto crs = dynamic_cast<CRS *>(obj.get()); - if (crs) { + if (crs && + (d->steps_[0].paramValues.empty() || + (d->steps_[0].paramValues.size() == 1 && + d->getParamValue(d->steps_[0], "type") == "crs"))) { PropertyMap properties; - properties.set(IdentifiedObject::NAME_KEY, crs->nameStr()); + properties.set(IdentifiedObject::NAME_KEY, + d->title_.empty() ? crs->nameStr() + : d->title_); const auto &extent = getExtent(crs); if (extent) { properties.set( @@ -7356,12 +7449,12 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } auto geogCRS = dynamic_cast<GeographicCRS *>(crs); if (geogCRS) { - // Override with longitude latitude in radian + // Override with longitude latitude in degrees return GeographicCRS::create( properties, geogCRS->datum(), geogCRS->datumEnsemble(), EllipsoidalCS::createLongitudeLatitude( - UnitOfMeasure::RADIAN)); + UnitOfMeasure::DEGREE)); } auto projCRS = dynamic_cast<ProjectedCRS *>(crs); if (projCRS) { @@ -7377,8 +7470,31 @@ PROJStringParser::createFromPROJString(const std::string &projString) { ->unit())); } } + return obj; + } + auto projStringExportable = + dynamic_cast<IPROJStringExportable *>(crs); + if (projStringExportable) { + std::string expanded; + if (!d->title_.empty()) { + expanded = "title="; + expanded += d->title_; + } + for (const auto &pair : d->steps_[0].paramValues) { + if (!expanded.empty()) + expanded += ' '; + expanded += '+'; + expanded += pair.key; + if (!pair.value.empty()) { + expanded += '='; + expanded += pair.value; + } + } + expanded += ' '; + expanded += projStringExportable->exportToPROJString( + PROJStringFormatter::create().get()); + return createFromPROJString(expanded); } - return obj; } } @@ -7386,18 +7502,23 @@ PROJStringParser::createFromPROJString(const std::string &projString) { if (!init) { throw ParsingException("out of memory"); } - PJ_CONTEXT *ctx = proj_context_create(); + PJ_CONTEXT *ctx = d->ctx_ ? d->ctx_ : proj_context_create(); if (!ctx) { pj_dealloc(init); throw ParsingException("out of memory"); } paralist *list = pj_expand_init(ctx, init); - proj_context_destroy(ctx); + if (ctx != d->ctx_) { + proj_context_destroy(ctx); + } if (!list) { pj_dealloc(init); throw ParsingException("cannot expand " + projString); } std::string expanded; + if (!d->title_.empty()) { + expanded = "title=" + d->title_; + } bool first = true; bool has_init_term = false; for (auto t = list; t;) { @@ -7417,6 +7538,14 @@ PROJStringParser::createFromPROJString(const std::string &projString) { pj_dealloc(t); t = n; } + for (const auto &pair : d->steps_[0].paramValues) { + expanded += " +"; + expanded += pair.key; + if (!pair.value.empty()) { + expanded += '='; + expanded += pair.value; + } + } if (!has_init_term) { return createFromPROJString(expanded); @@ -7434,7 +7563,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) { int iFirstCart = -1; int iSecondCart = -1; int iMolodensky = -1; - bool unexpectedStructure = false; + bool unexpectedStructure = d->steps_.empty(); for (int i = 0; i < static_cast<int>(d->steps_.size()); i++) { const auto &stepName = d->steps_[i].name; if (isGeographicStep(stepName)) { @@ -7497,14 +7626,82 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } } - if (d->steps_.size() == 1 && iHelmert < 0 && iMolodensky < 0 && - d->getParamValue(d->steps_[0], "type") != "crs") { + if (iHelmert < 0 && iMolodensky < 0 && !d->steps_.empty()) { + // CRS candidate + if ((d->steps_.size() == 1 && + d->getParamValue(d->steps_[0], "type") != "crs") || + (d->steps_.size() > 1 && d->getGlobalParamValue("type") != "crs")) { + unexpectedStructure = true; + } + } else if (iHelmert >= 0 && + (d->hasParamValue(d->steps_[iHelmert], "theta") || + d->hasParamValue(d->steps_[iHelmert], "exact") || + d->hasParamValue(d->steps_[iHelmert], "transpose") || + d->hasParamValue(d->steps_[iHelmert], "towgs84"))) { unexpectedStructure = true; } + if (unexpectedStructure || iHelmert >= 0 || iMolodensky >= 0) { + struct Logger { + std::string msg{}; + + // cppcheck-suppress functionStatic + void setMessage(const char *msgIn) noexcept { + try { + msg = msgIn; + } catch (const std::exception &) { + } + } + + static void log(void *user_data, int level, const char *msg) { + if (level == PJ_LOG_ERROR) { + static_cast<Logger *>(user_data)->setMessage(msg); + } + } + }; + + // If the structure is not recognized, then try to instantiate the + // pipeline, and if successful, wrap it in a PROJBasedOperation + Logger logger; + bool valid; + + auto pj_context = d->ctx_ ? d->ctx_ : proj_context_create(); + if (!pj_context) { + throw ParsingException("out of memory"); + } + if (pj_context != d->ctx_) { + proj_log_func(pj_context, &logger, Logger::log); + proj_context_use_proj4_init_rules(pj_context, + d->usePROJ4InitRules_); + } + auto pj = pj_create_internal(pj_context, projString.c_str()); + valid = pj != nullptr; + proj_destroy(pj); + + if (!valid) { + std::string prefix("Error " + + toString(proj_context_errno(pj_context)) + ": "); + if (logger.msg.empty()) { + logger.msg = + prefix + proj_errno_string(proj_context_errno(pj_context)); + } else { + logger.msg = prefix + logger.msg; + } + } + + if (pj_context != d->ctx_) { + proj_context_destroy(pj_context); + } + + if (!valid) { + throw ParsingException(logger.msg); + } + } + if (!unexpectedStructure) { - if (iFirstGeogStep == 0 && iSecondGeogStep < 0 && iProjStep < 0 && - iHelmert < 0 && iFirstCart < 0 && iMolodensky < 0 && + if (iFirstGeogStep == 0 && !d->steps_[iFirstGeogStep].inverted && + iSecondGeogStep < 0 && iProjStep < 0 && iHelmert < 0 && + iFirstCart < 0 && iMolodensky < 0 && (iFirstUnitConvert < 0 || iSecondUnitConvert < 0) && (iFirstAxisSwap < 0 || iSecondAxisSwap < 0)) { const bool ignoreVUnits = false; @@ -7534,9 +7731,6 @@ PROJStringParser::createFromPROJString(const std::string &projString) { iFirstAxisSwap < iFirstGeogStep ? iSecondAxisSwap : iFirstAxisSwap)); } - if (d->steps_.size() == 1 && iHelmert == 0) { - return d->buildHelmertTransformation(iHelmert); - } if (iProjStep < 0 && iHelmert > 0 && iMolodensky < 0 && (iFirstGeogStep < 0 || iFirstGeogStep == iFirstCart - 1 || @@ -7560,10 +7754,6 @@ PROJStringParser::createFromPROJString(const std::string &projString) { iSecondAxisSwap, iSecondUnitConvert); } - if (d->steps_.size() == 1 && iMolodensky == 0) { - return d->buildMolodenskyTransformation(iMolodensky); - } - if (iProjStep < 0 && iHelmert < 0 && iMolodensky > 0 && (iFirstGeogStep < 0 || iFirstGeogStep == iMolodensky - 1 || (iFirstGeogStep == iMolodensky + 1 && iSecondGeogStep < 0)) && @@ -7584,47 +7774,6 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } } - struct Logger { - std::string msg{}; - - // cppcheck-suppress functionStatic - void setMessage(const char *msgIn) noexcept { - try { - msg = msgIn; - } catch (const std::exception &) { - } - } - - static void log(void *user_data, int level, const char *msg) { - if (level == PJ_LOG_ERROR) { - static_cast<Logger *>(user_data)->setMessage(msg); - } - } - }; - - // If the structure is not recognized, then try to instantiate the - // pipeline, and if successful, wrap it in a PROJBasedOperation - Logger logger; - auto pj_context = proj_context_create(); - if (!pj_context) { - throw ParsingException("out of memory"); - } - proj_log_func(pj_context, &logger, Logger::log); - proj_context_use_proj4_init_rules(pj_context, d->usePROJ4InitRules_); - auto pj = proj_create(pj_context, projString.c_str()); - bool valid = pj != nullptr; - proj_destroy(pj); - - if (!valid && logger.msg.empty()) { - logger.setMessage(proj_errno_string(proj_context_errno(pj_context))); - } - - proj_context_destroy(pj_context); - - if (!valid) { - throw ParsingException(logger.msg); - } - auto props = PropertyMap(); if (!d->title_.empty()) { props.set(IdentifiedObject::NAME_KEY, d->title_); diff --git a/src/pipeline.cpp b/src/pipeline.cpp index b24f71e7..c4a9d2c0 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -456,7 +456,7 @@ PJ *OPERATION(pipeline,0) { err = proj_errno_reset (P); - next_step = proj_create_argv (P->ctx, current_argc, current_argv); + next_step = pj_create_argv_internal (P->ctx, current_argc, current_argv); proj_log_trace (P, "Pipeline: Step %d (%s) at %p", i, current_argv[0], next_step); if (nullptr==next_step) { @@ -653,16 +653,16 @@ typedef enum * \defgroup iso19111_functions Binding in C of basic methods from the C++ API * Functions for ISO19111 C API * - * The PJ* objects returned by proj_create_from_user_input(), proj_create_from_wkt(), - * proj_create_from_proj_string(), proj_create_from_database() and other functions + * The PJ* objects returned by proj_create_from_wkt(), + * proj_create_from_database() and other functions in that section * will have generally minimal interaction with the functions declared in the * upper section of this header file (calling those functions on those objects * will either return an error or default/non-sensical values). The exception is * for ISO19111 objects of type CoordinateOperation that can be exported as a * valid PROJ pipeline. In this case, the PJ objects will work for example with * proj_trans_generic(). - * Conversely, objects returned by proj_create() and proj_create_argv() will - * return an error when used with functions of this section. + * Conversely, objects returned by proj_create() and proj_create_argv(), which + * are not of type CRS, will return an error when used with functions of this section. * @{ */ @@ -686,19 +686,11 @@ const char PROJ_DLL *proj_context_get_database_metadata(PJ_CONTEXT* ctx, PJ_GUESSED_WKT_DIALECT PROJ_DLL proj_context_guess_wkt_dialect(PJ_CONTEXT *ctx, const char *wkt); -PJ PROJ_DLL *proj_create_from_user_input(PJ_CONTEXT *ctx, - const char *text, - const char* const *options); - PJ PROJ_DLL *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, const char* const *options, PROJ_STRING_LIST *out_warnings, PROJ_STRING_LIST *out_grammar_errors); -PJ PROJ_DLL *proj_create_from_proj_string(PJ_CONTEXT *ctx, - const char *proj_string, - const char* const *options); - PJ PROJ_DLL *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name, const char *code, diff --git a/src/proj_internal.h b/src/proj_internal.h index 0cc99c81..dcfd6797 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -901,6 +901,9 @@ PJ* pj_init_ctx_with_allow_init_epsg( projCtx_t *ctx, int argc, char **argv, int std::string PROJ_DLL pj_add_type_crs_if_needed(const std::string& str); +PJ *pj_create_internal (PJ_CONTEXT *ctx, const char *definition); +PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv); + /* classic public API */ #include "proj_api.h" diff --git a/src/proj_symbol_rename.h b/src/proj_symbol_rename.h index a7628e35..0ce342fd 100644 --- a/src/proj_symbol_rename.h +++ b/src/proj_symbol_rename.h @@ -102,6 +102,8 @@ #define proj_context_get_use_proj4_init_rules internal_proj_context_get_use_proj4_init_rules #define proj_context_guess_wkt_dialect internal_proj_context_guess_wkt_dialect #define proj_context_set_database_path internal_proj_context_set_database_path +#define proj_context_set_file_finder internal_proj_context_set_file_finder +#define proj_context_set_search_paths internal_proj_context_set_search_paths #define proj_context_use_proj4_init_rules internal_proj_context_use_proj4_init_rules #define proj_convert_conversion_to_other_method internal_proj_convert_conversion_to_other_method #define proj_coord internal_proj_coord @@ -190,8 +192,6 @@ #define proj_create_engineering_crs internal_proj_create_engineering_crs #define proj_create_from_database internal_proj_create_from_database #define proj_create_from_name internal_proj_create_from_name -#define proj_create_from_proj_string internal_proj_create_from_proj_string -#define proj_create_from_user_input internal_proj_create_from_user_input #define proj_create_from_wkt internal_proj_create_from_wkt #define proj_create_geocentric_crs internal_proj_create_geocentric_crs #define proj_create_geocentric_crs_from_datum internal_proj_create_geocentric_crs_from_datum |
