diff options
| -rw-r--r-- | docs/source/development/reference/functions.rst | 10 | ||||
| -rw-r--r-- | include/proj/internal/io_internal.hpp | 22 | ||||
| -rw-r--r-- | include/proj/io.hpp | 8 | ||||
| -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 | ||||
| -rw-r--r-- | test/unit/pj_transform_test.cpp | 1 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 79 | ||||
| -rw-r--r-- | test/unit/test_crs.cpp | 2 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 255 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 13 |
18 files changed, 570 insertions, 501 deletions
diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index 351d2ed3..ef84d489 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -579,17 +579,17 @@ Various C API for ISO-19111 functionality +++++++++++++++++++++++++++++++++ -The PJ* objects returned by :c:func:`proj_create_from_user_input`, -:c:func:`proj_create_from_wkt`, :c:func:`proj_create_from_proj_string`, -:c:func:`proj_create_from_database` and other functions +The PJ* objects returned by :c:func:`proj_create_from_wkt`, +:c:func:`proj_create_from_database` and other functions in that section will have generally minimal interaction with the functions declared in the previous sections (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, objects will work for example with :c:func:`proj_trans_generic`. -Conversely, objects returned by :c:func:`proj_create` and :c:func:`proj_create_argv` will -return an error when used with functions of this section. +Conversely, objects returned by :c:func:`proj_create` and :c:func:`proj_create_argv`, +which are not of type CRS (can be tested with :c:func:`proj_is_crs`), +will return an error when used with functions of this section. .. doxygengroup:: iso19111_functions :project: cpp_stuff diff --git a/include/proj/internal/io_internal.hpp b/include/proj/internal/io_internal.hpp index 62196fef..be6a11bd 100644 --- a/include/proj/internal/io_internal.hpp +++ b/include/proj/internal/io_internal.hpp @@ -36,6 +36,7 @@ #include <string> #include <vector> +#include "proj/io.hpp" #include "proj/util.hpp" //! @cond Doxygen_Suppress @@ -159,6 +160,27 @@ class WKTConstants { NS_PROJ_END +// --------------------------------------------------------------------------- + +/** Auxiliary structure to PJ_CONTEXT storing C++ context stuff. */ +struct projCppContext { + NS_PROJ::io::DatabaseContextNNPtr databaseContext; + std::string lastUOMName_{}; + + explicit projCppContext(PJ_CONTEXT *ctx, const char *dbPath = nullptr, + const char *const *auxDbPaths = nullptr) + : databaseContext(NS_PROJ::io::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; + } +}; + //! @endcond #endif // IO_INTERNAL_HH_INCLUDED diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 813b4bdb..18852cce 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -602,6 +602,9 @@ createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules = false); +PROJ_DLL util::BaseObjectNNPtr createFromUserInput(const std::string &text, + PJ_CONTEXT *ctx); + // --------------------------------------------------------------------------- /** \brief Parse a WKT string into the appropriate suclass of util::BaseObject. @@ -664,6 +667,11 @@ class PROJ_GCC_DLL PROJStringParser { PROJ_DLL util::BaseObjectNNPtr createFromPROJString( const std::string &projString); // throw(ParsingException) + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + PROJStringParser & + attachContext(PJ_CONTEXT *ctx); + //! @endcond private: PROJ_OPAQUE_PRIVATE_DATA }; 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 diff --git a/test/unit/pj_transform_test.cpp b/test/unit/pj_transform_test.cpp index 22f7afca..9d8439ee 100644 --- a/test/unit/pj_transform_test.cpp +++ b/test/unit/pj_transform_test.cpp @@ -572,6 +572,7 @@ TEST(pj_transform_test, init_epsg) { auto src = pj_init_plus("+init=epsg:4326"); ASSERT_TRUE(src != nullptr); auto dst = pj_init_plus("+init=epsg:32631"); + ASSERT_TRUE(dst != nullptr); double x = 3 * DEG_TO_RAD; double y = 0 * DEG_TO_RAD; EXPECT_EQ(pj_transform(src, dst, 1, 0, &x, &y, nullptr), 0); diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 4c13a5e2..ff29185f 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -148,15 +148,14 @@ class CApi : public ::testing::Test { // --------------------------------------------------------------------------- -TEST_F(CApi, proj_create_from_user_input) { +TEST_F(CApi, proj_create) { proj_destroy(nullptr); - EXPECT_EQ(proj_create_from_user_input(m_ctxt, "invalid", nullptr), nullptr); + EXPECT_EQ(proj_create(m_ctxt, "invalid"), nullptr); { - auto obj = proj_create_from_user_input( - m_ctxt, - GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); + auto obj = + proj_create(m_ctxt, GeographicCRS::EPSG_4326 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); ObjectKeeper keeper(obj); EXPECT_NE(obj, nullptr); @@ -182,7 +181,7 @@ TEST_F(CApi, proj_create_from_user_input) { EXPECT_EQ(info.definition, std::string("")); } { - auto obj = proj_create_from_user_input(m_ctxt, "EPSG:4326", nullptr); + auto obj = proj_create(m_ctxt, "EPSG:4326"); ObjectKeeper keeper(obj); EXPECT_NE(obj, nullptr); } @@ -318,17 +317,6 @@ TEST_F(CApi, proj_create_from_wkt) { // --------------------------------------------------------------------------- -TEST_F(CApi, proj_create_from_proj_string) { - - EXPECT_EQ(proj_create_from_proj_string(m_ctxt, "invalid", nullptr), - nullptr); - auto obj = proj_create_from_proj_string(m_ctxt, "+proj=longlat", nullptr); - ObjectKeeper keeper(obj); - EXPECT_NE(obj, nullptr); -} - -// --------------------------------------------------------------------------- - TEST_F(CApi, proj_as_wkt) { auto obj = proj_create_from_wkt( m_ctxt, @@ -520,8 +508,7 @@ TEST_F(CApi, proj_as_proj_string_incompatible_WKT1) { // --------------------------------------------------------------------------- TEST_F(CApi, proj_as_proj_string_etmerc_option_yes) { - auto obj = - proj_create_from_proj_string(m_ctxt, "+proj=tmerc +type=crs", nullptr); + auto obj = proj_create(m_ctxt, "+proj=tmerc +type=crs"); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -536,8 +523,7 @@ TEST_F(CApi, proj_as_proj_string_etmerc_option_yes) { // --------------------------------------------------------------------------- TEST_F(CApi, proj_as_proj_string_etmerc_option_no) { - auto obj = proj_create_from_proj_string( - m_ctxt, "+proj=utm +zone=31 +type=crs", nullptr); + auto obj = proj_create(m_ctxt, "+proj=utm +zone=31 +type=crs"); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -1698,8 +1684,7 @@ TEST_F(CApi, proj_get_area_of_use) { EXPECT_EQ(std::string(name), "World"); } { - auto obj = - proj_create_from_user_input(m_ctxt, "+proj=longlat", nullptr); + auto obj = proj_create(m_ctxt, "+proj=longlat +type=crs"); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_FALSE(proj_get_area_of_use(m_ctxt, obj, nullptr, nullptr, @@ -1726,8 +1711,7 @@ TEST_F(CApi, proj_coordoperation_get_accuracy) { EXPECT_EQ(proj_coordoperation_get_accuracy(m_ctxt, obj), 16.0); } { - auto obj = - proj_create_from_user_input(m_ctxt, "+proj=helmert", nullptr); + auto obj = proj_create(m_ctxt, "+proj=helmert"); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_coordoperation_get_accuracy(m_ctxt, obj), -1.0); @@ -1750,11 +1734,10 @@ TEST_F(CApi, proj_create_geographic_crs) { ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); - auto objRef = proj_create_from_user_input( - m_ctxt, - GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); + auto objRef = + proj_create(m_ctxt, GeographicCRS::EPSG_4326 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); ObjectKeeper keeperobjRef(objRef); EXPECT_NE(objRef, nullptr); @@ -1787,11 +1770,10 @@ TEST_F(CApi, proj_create_geographic_crs) { ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); - auto objRef = proj_create_from_user_input( - m_ctxt, - GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); + auto objRef = + proj_create(m_ctxt, GeographicCRS::EPSG_4326 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); ObjectKeeper keeperobjRef(objRef); EXPECT_NE(objRef, nullptr); @@ -1806,11 +1788,10 @@ TEST_F(CApi, proj_create_geographic_crs) { ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); - auto objRef = proj_create_from_user_input( - m_ctxt, - GeographicCRS::EPSG_4269->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); + auto objRef = + proj_create(m_ctxt, GeographicCRS::EPSG_4269 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); ObjectKeeper keeperobjRef(objRef); EXPECT_NE(objRef, nullptr); @@ -1865,11 +1846,10 @@ TEST_F(CApi, proj_create_geocentric_crs) { ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); - auto objRef = proj_create_from_user_input( - m_ctxt, - GeographicCRS::EPSG_4978->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); + auto objRef = + proj_create(m_ctxt, GeographicCRS::EPSG_4978 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); ObjectKeeper keeperobjRef(objRef); EXPECT_NE(objRef, nullptr); @@ -2415,7 +2395,7 @@ TEST_F(CApi, proj_context_get_database_metadata) { // --------------------------------------------------------------------------- TEST_F(CApi, proj_clone) { - auto obj = proj_create_from_proj_string(m_ctxt, "+proj=longlat", nullptr); + auto obj = proj_create(m_ctxt, "+proj=longlat"); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -2436,8 +2416,7 @@ TEST_F(CApi, proj_crs_alter_geodetic_crs) { ObjectKeeper keeper(projCRS); ASSERT_NE(projCRS, nullptr); - auto newGeodCRS = proj_create_from_proj_string( - m_ctxt, "+proj=longlat +type=crs", nullptr); + auto newGeodCRS = proj_create(m_ctxt, "+proj=longlat +type=crs"); ObjectKeeper keeper_newGeodCRS(newGeodCRS); ASSERT_NE(newGeodCRS, nullptr); diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index b71fd248..69cec01b 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -1977,7 +1977,7 @@ TEST(crs, projectedCRS_identify_db) { { // Identify from a PROJ string auto obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=omerc " "+no_uoff +lat_0=4 +lonc=102.25 +alpha=323.025796466667 " "+gamma=323.130102361111 +k=0.99984 +x_0=804671 +y_0=0 " diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 7b75cdc8..15ba30f1 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1276,7 +1276,8 @@ TEST(wkt_parse, wkt1_krovak_south_west) { expectedPROJString); obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+type=crs +proj=pipeline +step +proj=unitconvert +xy_in=deg " + "+xy_out=rad " "+step +proj=krovak +lat_0=49.5 " "+lon_0=24.8333333333333 +alpha=30.2881397222222 " "+k=0.9999 +x_0=0 +y_0=0 +ellps=bessel " @@ -6915,8 +6916,8 @@ TEST(io, projparse_longlat_complex) { "+step +proj=longlat +ellps=clrk80ign " "+pm=paris +step +proj=unitconvert +xy_in=rad +xy_out=grad +step " "+proj=axisswap +order=2,1"; - auto obj = - PROJStringParser().createFromPROJString("+proj=pipeline " + input); + auto obj = PROJStringParser().createFromPROJString( + "+type=crs +proj=pipeline " + input); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); auto op = CoordinateOperationFactory::create()->createOperation( @@ -7194,7 +7195,7 @@ TEST(io, projparse_longlat_axis_swu) { TEST(io, projparse_longlat_unitconvert_deg) { auto obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=longlat +ellps=GRS80 +step " + "+type=crs +proj=pipeline +step +proj=longlat +ellps=GRS80 +step " "+proj=unitconvert +xy_in=rad +xy_out=deg"); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); @@ -7210,7 +7211,7 @@ TEST(io, projparse_longlat_unitconvert_deg) { TEST(io, projparse_longlat_unitconvert_grad) { auto obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=longlat +ellps=GRS80 +step " + "+type=crs +proj=pipeline +step +proj=longlat +ellps=GRS80 +step " "+proj=unitconvert +xy_in=rad +xy_out=grad"); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); @@ -7228,7 +7229,7 @@ TEST(io, projparse_longlat_unitconvert_grad) { TEST(io, projparse_longlat_unitconvert_rad) { auto obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=longlat +ellps=GRS80 +step " + "+type=crs +proj=pipeline +step +proj=longlat +ellps=GRS80 +step " "+proj=unitconvert +xy_in=rad +xy_out=rad"); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); @@ -7248,9 +7249,10 @@ TEST(io, projparse_longlat_axisswap) { for (auto order2 : {"1", "-1", "2", "-2"}) { if (std::abs(atoi(order1) * atoi(order2)) == 2 && !(atoi(order1) == 1 && atoi(order2) == 2)) { - auto str = "+proj=pipeline +step +proj=longlat +ellps=GRS80 " - "+step +proj=axisswap +order=" + - std::string(order1) + "," + order2; + auto str = + "+type=crs +proj=pipeline +step +proj=longlat +ellps=GRS80 " + "+step +proj=axisswap +order=" + + std::string(order1) + "," + order2; auto obj = PROJStringParser().createFromPROJString(str); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); @@ -7363,9 +7365,11 @@ TEST(io, projparse_tmerc_south_oriented) { EXPECT_EQ(f->toString(), expected); obj = PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=tmerc +x_0=1 +lat_0=1 +k_0=2 +step " + "+type=crs +proj=pipeline +step +proj=tmerc +x_0=1 +lat_0=1 +k_0=2 " + "+step " "+proj=axisswap +order=-1,-2"); crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); EXPECT_EQ(crs->derivingConversion()->method()->nameStr(), "Transverse Mercator (South Orientated)"); } @@ -7917,7 +7921,7 @@ TEST(io, projparse_non_earth_ellipsoid) { TEST(io, projparse_axisswap_unitconvert_longlat_proj) { std::string input = - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " "+ellps=clrk80ign +pm=paris +step +proj=lcc +lat_1=49.5 " "+lat_0=49.5 +lon_0=0 +k_0=0.999877341 +x_0=600000 +y_0=200000 " @@ -7939,7 +7943,7 @@ TEST(io, projparse_axisswap_unitconvert_longlat_proj) { TEST(io, projparse_axisswap_unitconvert_proj_axisswap) { std::string input = - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh " "+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=axisswap +order=2,1"; auto obj = PROJStringParser().createFromPROJString(input); @@ -7959,7 +7963,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_axisswap) { TEST(io, projparse_axisswap_unitconvert_proj_unitconvert) { std::string input = - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh " "+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert +xy_in=m " "+z_in=m +xy_out=ft +z_out=ft"; @@ -7980,7 +7984,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_unitconvert) { TEST(io, projparse_axisswap_unitconvert_proj_unitconvert_numeric_axisswap) { std::string input = - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh " "+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert +xy_in=m " "+z_in=m +xy_out=2.5 +z_out=2.5 +step +proj=axisswap +order=-2,-1"; @@ -8294,7 +8298,7 @@ TEST(io, projparse_projected_wktext) { TEST(io, projparse_ob_tran_longlat) { std::string input( - "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=ob_tran " "+o_proj=longlat +o_lat_p=52 +o_lon_p=-30 +lon_0=-25 +ellps=WGS84 " "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " @@ -8316,7 +8320,11 @@ TEST(io, projparse_ob_tran_longlat) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_translation) { - std::string projString("+proj=helmert +x=1 +y=2 +z=3"); + std::string projString("+proj=pipeline +step +proj=unitconvert +xy_in=deg " + "+xy_out=rad +step +proj=cart +ellps=GRS80 +step " + "+proj=helmert +x=1 +y=2 +z=3 +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8330,8 +8338,11 @@ TEST(io, projparse_helmert_translation) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_translation_inv) { - std::string projString( - "+proj=pipeline +step +inv +proj=helmert +x=1 +y=2 +z=3"); + std::string projString("+proj=pipeline +step +proj=unitconvert +xy_in=deg " + "+xy_out=rad +step +proj=cart +ellps=GRS80 +step " + "+inv +proj=helmert +x=1 +y=2 +z=3 +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8339,14 +8350,20 @@ TEST(io, projparse_helmert_translation_inv) { transf->exportToPROJString( PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) .get()), - "+proj=helmert +x=-1 +y=-2 +z=-3"); + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=cart +ellps=GRS80 +step +proj=helmert +x=-1 +y=-2 +z=-3 +step " + "+inv +proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); } // --------------------------------------------------------------------------- TEST(io, projparse_helmert_position_vector) { - std::string projString("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " - "+s=7 +convention=position_vector"); + std::string projString( + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8360,9 +8377,11 @@ TEST(io, projparse_helmert_position_vector) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_position_vector_inv) { - std::string projString("+proj=pipeline +step +inv +proj=helmert +x=1 +y=2 " - "+z=3 +rx=4 +ry=5 +rz=6 " - "+s=7 +convention=position_vector"); + std::string projString( + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=cart +ellps=GRS80 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<CoordinateOperation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8376,10 +8395,14 @@ TEST(io, projparse_helmert_position_vector_inv) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_time_dependent_position_vector) { - std::string projString("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " + std::string projString("+proj=pipeline +step +proj=unitconvert +xy_in=deg " + "+xy_out=rad +step +proj=cart +ellps=GRS80 +step " + "+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " "+s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 +dry=0.5 " "+drz=0.6 +ds=0.7 +t_epoch=2018.5 " - "+convention=position_vector"); + "+convention=position_vector +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8393,8 +8416,11 @@ TEST(io, projparse_helmert_time_dependent_position_vector) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_coordinate_frame) { - std::string projString("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " - "+s=7 +convention=coordinate_frame"); + std::string projString( + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=coordinate_frame +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8408,10 +8434,14 @@ TEST(io, projparse_helmert_coordinate_frame) { // --------------------------------------------------------------------------- TEST(io, projparse_helmert_time_dependent_coordinate_frame) { - std::string projString("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " + std::string projString("+proj=pipeline +step +proj=unitconvert +xy_in=deg " + "+xy_out=rad +step +proj=cart +ellps=GRS80 +step " + "+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 " "+s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 +dry=0.5 " "+drz=0.6 +ds=0.7 +t_epoch=2018.5 " - "+convention=coordinate_frame"); + "+convention=coordinate_frame +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8426,28 +8456,6 @@ TEST(io, projparse_helmert_time_dependent_coordinate_frame) { TEST(io, projparse_helmert_complex_pipeline) { std::string projString( - "+proj=pipeline +step +proj=cart " - "+ellps=WGS84 +step +proj=helmert +x=-1 +y=-2 +z=-3 +rx=-4 " - "+ry=-5 +rz=-6 +s=-7 +convention=position_vector +step +inv " - "+proj=cart +ellps=clrk80ign"); - auto obj = PROJStringParser().createFromPROJString(projString); - auto transf = nn_dynamic_pointer_cast<Transformation>(obj); - ASSERT_TRUE(transf != nullptr); - EXPECT_EQ( - transf->exportToPROJString( - PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) - .get()), - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=cart +ellps=WGS84 +step +proj=helmert +x=-1 +y=-2 " - "+z=-3 +rx=-4 +ry=-5 +rz=-6 +s=-7 +convention=position_vector " - "+step +inv +proj=cart +ellps=clrk80ign +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg"); -} - -// --------------------------------------------------------------------------- - -TEST(io, projparse_helmert_complex_pipeline_2) { - std::string projString( "+proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " "+ellps=WGS84 +step +proj=helmert +x=-1 +y=-2 +z=-3 +rx=-4 " @@ -8472,21 +8480,18 @@ TEST(io, projparse_helmert_errors) { // Missing convention EXPECT_THROW(PROJStringParser().createFromPROJString("+proj=helmert +rx=4"), ParsingException); - - EXPECT_THROW(PROJStringParser().createFromPROJString( - "+proj=helmert +convention=unhandled"), - ParsingException); - - EXPECT_THROW(PROJStringParser().createFromPROJString( - "+proj=helmert +unhandled_keyword"), - ParsingException); } // --------------------------------------------------------------------------- TEST(io, projparse_molodensky) { - std::string projString("+proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05"); + std::string projString( + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+proj=longlat +ellps=WGS84 +step +proj=molodensky +ellps=WGS84 " + "+dx=84.87 +dy=96.49 " + "+dz=116.95 +da=251 +df=1.41927e-05 +step +proj=longlat " + "+ellps=GRS80 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8505,12 +8510,13 @@ TEST(io, projparse_molodensky) { "ellipsoidal,2],AXIS[\"longitude\",east,ORDER[1],ANGLEUNIT[\"degree\"," "0.0174532925199433]],AXIS[\"latitude\",north,ORDER[2],ANGLEUNIT[" "\"degree\",0.0174532925199433]]]],TARGETCRS[GEODCRS[\"unknown\",DATUM[" - "\"unknown\",ELLIPSOID[\"unknown\",6378388,297.000000198989,LENGTHUNIT[" - "\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0." - "0174532925199433]],CS[ellipsoidal,2],AXIS[\"longitude\",east,ORDER[1]," - "ANGLEUNIT[\"degree\",0.0174532925199433]],AXIS[\"latitude\",north," - "ORDER[2],ANGLEUNIT[\"degree\",0.0174532925199433]]]],METHOD[" - "\"Molodensky\",ID[\"EPSG\",9604]],PARAMETER[\"X-axis " + "\"Unknown based on GRS80 ellipsoid\",ELLIPSOID[\"GRS " + "1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1]]],PRIMEM[" + "\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],CS[" + "ellipsoidal,2],AXIS[\"longitude\",east,ORDER[1],ANGLEUNIT[\"degree\"," + "0.0174532925199433]],AXIS[\"latitude\",north,ORDER[2],ANGLEUNIT[" + "\"degree\",0.0174532925199433]]]],METHOD[\"Molodensky\",ID[\"EPSG\"," + "9604]],PARAMETER[\"X-axis " "translation\",84.87,LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8605]]," "PARAMETER[\"Y-axis " "translation\",96.49,LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8606]]," @@ -8524,19 +8530,21 @@ TEST(io, projparse_molodensky) { transf->exportToPROJString( PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) .get()), - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05 +step +proj=longlat " - "+a=6378388 +rf=297.000000198989 +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg"); + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 +dz=116.95 +da=251 " + "+df=1.41927e-05 +step +proj=unitconvert +xy_in=rad +xy_out=deg"); } // --------------------------------------------------------------------------- TEST(io, projparse_molodensky_inv) { - std::string projString("+proj=pipeline +step +inv +proj=molodensky " - "+ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05"); + std::string projString( + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+proj=longlat +ellps=WGS84 +step +inv +proj=molodensky " + "+ellps=WGS84 +dx=84.87 +dy=96.49 " + "+dz=116.95 +da=251 +df=1.41927e-05 +step +proj=longlat " + "+ellps=GRS80 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<CoordinateOperation>(obj); ASSERT_TRUE(transf != nullptr); @@ -8544,66 +8552,33 @@ TEST(io, projparse_molodensky_inv) { transf->exportToPROJString( PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) .get()), - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +inv +proj=longlat +a=6378388 +rf=297.000000198989 +step " - "+proj=molodensky +a=6378388 +rf=297.000000198989 +dx=-84.87 " - "+dy=-96.49 +dz=-116.95 +da=-251 +df=-1.41927e-05 +step " - "+proj=unitconvert +xy_in=rad +xy_out=deg"); + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=molodensky +ellps=GRS80 +dx=-84.87 +dy=-96.49 +dz=-116.95 " + "+da=-251 +df=-1.41927e-05 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); } // --------------------------------------------------------------------------- TEST(io, projparse_molodensky_abridged) { - std::string projString("+proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05 +abridged"); - auto obj = PROJStringParser().createFromPROJString(projString); - auto transf = nn_dynamic_pointer_cast<Transformation>(obj); - ASSERT_TRUE(transf != nullptr); - EXPECT_EQ( - transf->exportToPROJString( - PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) - .get()), - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05 +abridged +step +proj=longlat " - "+a=6378388 +rf=297.000000198989 +step " - "+proj=unitconvert +xy_in=rad +xy_out=deg"); -} - -// --------------------------------------------------------------------------- - -TEST(io, projparse_molodensky_complex_pipeline) { std::string projString( "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " "+proj=longlat +ellps=WGS84 +step +proj=molodensky +ellps=WGS84 " "+dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05 +step +proj=longlat " + "+dz=116.95 +da=251 +df=1.41927e-05 +abridged +step +proj=longlat " "+ellps=GRS80 +step +proj=unitconvert " "+xy_in=rad +xy_out=deg"); auto obj = PROJStringParser().createFromPROJString(projString); auto transf = nn_dynamic_pointer_cast<Transformation>(obj); ASSERT_TRUE(transf != nullptr); - - WKTFormatterNNPtr f(WKTFormatter::create()); - f->simulCurNodeHasId(); - f->setMultiLine(false); - transf->exportToWKT(f.get()); - auto wkt = f->toString(); - EXPECT_TRUE(wkt.find("SOURCECRS[GEODCRS[\"unknown\",DATUM[\"Unknown based " - "on WGS84 ellipsoid\"") != std::string::npos) - << wkt; - EXPECT_TRUE(wkt.find("TARGETCRS[GEODCRS[\"unknown\",DATUM[\"Unknown based " - "on GRS80 ellipsoid\"") != std::string::npos) - << wkt; - EXPECT_EQ( transf->exportToPROJString( PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) .get()), - "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 " - "+dz=116.95 +da=251 +df=1.41927e-05 +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg"); + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=molodensky +ellps=WGS84 +dx=84.87 +dy=96.49 +dz=116.95 +da=251 " + "+df=1.41927e-05 +abridged +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg"); } // --------------------------------------------------------------------------- @@ -8665,12 +8640,12 @@ TEST(io, projparse_init) { { // EPSG:4326 is normally latitude-longitude order with degree, - // but in compatibillity mode it will be long-lat radian + // but in compatibillity mode it will be long-lat auto obj = createFromUserInput("init=epsg:4326", dbContext, true); auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); ASSERT_TRUE(crs != nullptr); EXPECT_TRUE(crs->coordinateSystem()->isEquivalentTo( - EllipsoidalCS::createLongitudeLatitude(UnitOfMeasure::RADIAN) + EllipsoidalCS::createLongitudeLatitude(UnitOfMeasure::DEGREE) .get())); } @@ -8691,20 +8666,19 @@ TEST(io, projparse_init) { auto co = nn_dynamic_pointer_cast<CoordinateOperation>(obj); ASSERT_TRUE(co != nullptr); EXPECT_EQ(co->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=helmert +x=-0.0001 +y=0.0008 +z=0.0058 +rx=0 +ry=0 " - "+rz=0 +s=-0.0004 +dx=0.0002 +dy=-0.0001 +dz=0.0018 +drx=0 " - "+dry=0 +drz=0 +ds=-8e-06 +t_epoch=2000 " - "+convention=position_vector"); + "+proj=helmert +x=-0.0001 +y=0.0008 +z=0.0058 +s=-0.0004 " + "+dx=0.0002 +dy=-0.0001 +dz=0.0018 +ds=-0.000008 " + "+t_epoch=2000.0 +convention=position_vector"); } { - auto obj = createFromUserInput( - "title=mytitle init=epsg:4326 ellps=WGS84", dbContext, true); - auto co = nn_dynamic_pointer_cast<CoordinateOperation>(obj); - ASSERT_TRUE(co != nullptr); - EXPECT_EQ(co->nameStr(), "mytitle"); - EXPECT_EQ(co->exportToPROJString(PROJStringFormatter::create().get()), - "+init=epsg:4326 +ellps=WGS84"); + auto obj = createFromUserInput("+title=mytitle +geoc +init=epsg:4326", + dbContext, true); + auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), "mytitle"); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=longlat +geoc +datum=WGS84 +no_defs +type=crs"); } { @@ -8717,16 +8691,6 @@ TEST(io, projparse_init) { "+proj=pipeline +step +init=epsg:4326 +step +proj=longlat " "+ellps=WGS84"); } - - { - auto obj = createFromUserInput( - "init=epsg:4326 proj=longlat ellps=GRS80 +type=crs", dbContext, - true); - auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); - ASSERT_TRUE(crs != nullptr); - EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=longlat +ellps=GRS80 +no_defs +type=crs"); - } } // --------------------------------------------------------------------------- @@ -8850,10 +8814,11 @@ TEST(io, projparse_projected_errors) { "+proj=tmerc +lat_0=foo +type=crs"), ParsingException); // Inconsistent pm values between geogCRS and projectedCRS - EXPECT_THROW(PROJStringParser().createFromPROJString( - "+proj=pipeline +step +proj=longlat +ellps=WGS84 " - "+proj=tmerc +ellps=WGS84 +lat_0=foo +pm=paris"), - ParsingException); + EXPECT_THROW( + PROJStringParser().createFromPROJString( + "+type=crs +proj=pipeline +step +proj=longlat +ellps=WGS84 " + "+step +proj=tmerc +ellps=WGS84 +lat_0=0 +pm=paris"), + ParsingException); } // --------------------------------------------------------------------------- diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 3cf633b6..d134f086 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -4116,14 +4116,11 @@ TEST(operation, PROJ_based) { ->exportToPROJString(PROJStringFormatter::create().get()), str); - EXPECT_THROW(SingleOperation::createPROJBased(PropertyMap(), "+inv", - nullptr, nullptr) + EXPECT_THROW(SingleOperation::createPROJBased( + PropertyMap(), "+proj=pipeline +step +proj=pipeline", + nullptr, nullptr) ->exportToPROJString(PROJStringFormatter::create().get()), FormattingException); - EXPECT_THROW( - SingleOperation::createPROJBased(PropertyMap(), "foo", nullptr, nullptr) - ->exportToPROJString(PROJStringFormatter::create().get()), - FormattingException); } // --------------------------------------------------------------------------- @@ -4690,7 +4687,9 @@ TEST(operation, geogCRS_to_geogCRS_init_IGNF_to_init_IGNF_context) { EXPECT_EQ(list[0]->nameStr(), "NOUVELLE TRIANGULATION DE LA FRANCE (NTF) vers RGF93 (ETRS89)"); EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=hgridshift +grids=ntf_r93.gsb"); + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=hgridshift +grids=ntf_r93.gsb +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg"); } // --------------------------------------------------------------------------- |
