aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKristian Evers <kristianevers@gmail.com>2019-01-18 07:10:42 +0100
committerGitHub <noreply@github.com>2019-01-18 07:10:42 +0100
commit8584dcd50777355b460b38418cae0db05dcf91bc (patch)
treeb7335182c91737ceda8518a638ac5a79bd2bc5b2 /src
parent3fc48e6146e020b86a0ef87749cc645f9b4fa113 (diff)
parent25f8ad123dc5fb7de0ac2fb10b55ae9efd2723a3 (diff)
downloadPROJ-8584dcd50777355b460b38418cae0db05dcf91bc.tar.gz
PROJ-8584dcd50777355b460b38418cae0db05dcf91bc.zip
Merge pull request #1223 from rouault/unify_proj_create
Unify proj_create(), proj_create_from_user_input() and proj_create_from_proj_string() (fixes #1214)
Diffstat (limited to 'src')
-rw-r--r--src/4D_api.cpp197
-rw-r--r--src/apps/cs2cs.cpp16
-rw-r--r--src/init.cpp10
-rw-r--r--src/internal.cpp2
-rw-r--r--src/iso19111/c_api.cpp97
-rw-r--r--src/iso19111/crs.cpp9
-rw-r--r--src/iso19111/factory.cpp6
-rw-r--r--src/iso19111/io.cpp482
-rw-r--r--src/pipeline.cpp2
-rw-r--r--src/proj.h16
-rw-r--r--src/proj_internal.h5
-rw-r--r--src/proj_symbol_rename.h4
12 files changed, 498 insertions, 348 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index ba292262..5502c96f 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -27,6 +27,8 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+#define FROM_PROJ_CPP
+
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
@@ -44,7 +46,9 @@
#include "proj/common.hpp"
#include "proj/coordinateoperation.hpp"
+#include "proj/internal/internal.hpp"
+using namespace NS_PROJ::internal;
/* Initialize PJ_COORD struct */
PJ_COORD proj_coord (double x, double y, double z, double t) {
@@ -458,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;
@@ -473,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;
@@ -488,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;
@@ -520,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;
@@ -546,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);
@@ -564,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
@@ -619,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) {
/**************************************************************************************
@@ -653,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)));
@@ -726,6 +757,20 @@ int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_
return from_legacy_code_path;
}
+/** Adds a " +type=crs" suffix to a PROJ string (if it is a PROJ string) */
+std::string pj_add_type_crs_if_needed(const std::string& str)
+{
+ std::string ret(str);
+ if( (starts_with(str, "proj=") ||
+ starts_with(str, "+proj=") ||
+ starts_with(str, "+init=") ||
+ starts_with(str, "+title=")) &&
+ str.find("type=crs") == std::string::npos )
+ {
+ ret += " +type=crs";
+ }
+ return ret;
+}
/*****************************************************************************/
PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char *target_crs, PJ_AREA *area) {
@@ -743,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.
@@ -754,88 +799,94 @@ 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, optionsImportCRS);
- if( !src ) {
- proj_context_log_debug(ctx, "Cannot instantiate source_crs");
- return nullptr;
- }
+ 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, optionsImportCRS);
- if( !dst ) {
- proj_context_log_debug(ctx, "Cannot instantiate target_crs");
- proj_destroy(src);
- return nullptr;
- }
+ auto dst = proj_create(ctx, target_crs_modified.c_str());
+ if( !dst ) {
+ proj_context_log_debug(ctx, "Cannot instantiate target_crs");
+ proj_destroy(src);
+ return nullptr;
+ }
- auto operation_ctx = proj_create_operation_factory_context(ctx, nullptr);
- if( !operation_ctx ) {
- proj_destroy(src);
- proj_destroy(dst);
- return nullptr;
- }
+ auto operation_ctx = proj_create_operation_factory_context(ctx, nullptr);
+ if( !operation_ctx ) {
+ proj_destroy(src);
+ proj_destroy(dst);
+ return nullptr;
+ }
- if( area && area->bbox_set ) {
- proj_operation_factory_context_set_area_of_interest(
- ctx,
- operation_ctx,
- area->west_lon_degree,
- area->south_lat_degree,
- area->east_lon_degree,
- area->north_lat_degree);
- }
+ if( area && area->bbox_set ) {
+ proj_operation_factory_context_set_area_of_interest(
+ ctx,
+ operation_ctx,
+ area->west_lon_degree,
+ area->south_lat_degree,
+ area->east_lon_degree,
+ area->north_lat_degree);
+ }
- proj_operation_factory_context_set_grid_availability_use(
- ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
+ proj_operation_factory_context_set_grid_availability_use(
+ ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
- auto op_list = proj_create_operations(ctx, src, dst, operation_ctx);
+ auto op_list = proj_create_operations(ctx, src, dst, operation_ctx);
- proj_operation_factory_context_destroy(operation_ctx);
- proj_destroy(src);
- proj_destroy(dst);
+ proj_operation_factory_context_destroy(operation_ctx);
+ proj_destroy(src);
+ proj_destroy(dst);
- if( !op_list ) {
- return nullptr;
- }
+ if( !op_list ) {
+ return nullptr;
+ }
+
+ if( proj_list_get_count(op_list) == 0 ) {
+ proj_list_destroy(op_list);
+ proj_context_log_debug(ctx, "No operation found matching criteria");
+ return nullptr;
+ }
- if( proj_list_get_count(op_list) == 0 ) {
+ auto op = proj_list_get(ctx, op_list, 0);
proj_list_destroy(op_list);
- proj_context_log_debug(ctx, "No operation found matching criteria");
- return nullptr;
- }
+ if( !op ) {
+ return nullptr;
+ }
- auto op = proj_list_get(ctx, op_list, 0);
- proj_list_destroy(op_list);
- if( !op ) {
- return nullptr;
- }
+ proj_string = proj_as_proj_string(ctx, op, PJ_PROJ_5, nullptr);
+ if( !proj_string) {
+ proj_destroy(op);
+ proj_context_log_debug(ctx, "Cannot export operation as a PROJ string");
+ return nullptr;
+ }
+
+ PJ* P;
+ if( proj_string[0] == '\0' ) {
+ /* Null transform ? */
+ P = proj_create(ctx, "proj=affine");
+ } else {
+ P = proj_create(ctx, proj_string);
+ }
- proj_string = proj_as_proj_string(ctx, op, PJ_PROJ_5, nullptr);
- if( !proj_string) {
proj_destroy(op);
- proj_context_log_debug(ctx, "Cannot export operation as a PROJ string");
- return nullptr;
- }
- PJ* P;
- if( proj_string[0] == '\0' ) {
- /* Null transform ? */
- P = proj_create(ctx, "proj=affine");
- } else {
- P = proj_create(ctx, proj_string);
+ return P;
+ }
+ catch( const std::exception& )
+ {
+ return nullptr;
}
-
- proj_destroy(op);
-
- return P;
}
PJ *proj_destroy (PJ *P) {
diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp
index af53a051..1c49965a 100644
--- a/src/apps/cs2cs.cpp
+++ b/src/apps/cs2cs.cpp
@@ -210,11 +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, definition.c_str(),
- optionsImportCRS);
+ PJ *crs = proj_create(nullptr,
+ pj_add_type_crs_if_needed(definition).c_str());
if (!crs) {
return nullptr;
}
@@ -535,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");
@@ -553,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/crs.cpp b/src/iso19111/crs.cpp
index 7bf40e0c..74b94a1f 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -1176,7 +1176,8 @@ void GeodeticCRS::_exportToPROJString(
{
const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_;
if (!extensionProj4.empty()) {
- formatter->ingestPROJString(extensionProj4);
+ formatter->ingestPROJString(
+ replaceAll(extensionProj4, " +type=crs", ""));
formatter->addNoDefs(false);
return;
}
@@ -1907,7 +1908,8 @@ void GeographicCRS::_exportToPROJString(
{
const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_;
if (!extensionProj4.empty()) {
- formatter->ingestPROJString(extensionProj4);
+ formatter->ingestPROJString(
+ replaceAll(extensionProj4, " +type=crs", ""));
formatter->addNoDefs(false);
return;
}
@@ -2719,7 +2721,8 @@ void ProjectedCRS::_exportToPROJString(
{
const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_;
if (!extensionProj4.empty()) {
- formatter->ingestPROJString(extensionProj4);
+ formatter->ingestPROJString(
+ replaceAll(extensionProj4, " +type=crs", ""));
formatter->addNoDefs(false);
return;
}
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index fb75b7b6..54fc3fd3 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -2086,7 +2086,8 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
if (!text_definition.empty()) {
DatabaseContext::Private::RecursionDetector detector(d->context());
- auto obj = createFromUserInput(text_definition, d->context());
+ auto obj = createFromUserInput(
+ pj_add_type_crs_if_needed(text_definition), d->context());
auto geodCRS = util::nn_dynamic_pointer_cast<crs::GeodeticCRS>(obj);
if (geodCRS) {
return cloneWithProps(NN_NO_CHECK(geodCRS), props);
@@ -2333,7 +2334,8 @@ AuthorityFactory::createProjectedCRS(const std::string &code) const {
if (!text_definition.empty()) {
DatabaseContext::Private::RecursionDetector detector(d->context());
- auto obj = createFromUserInput(text_definition, d->context());
+ auto obj = createFromUserInput(
+ pj_add_type_crs_if_needed(text_definition), d->context());
auto projCRS = dynamic_cast<const crs::ProjectedCRS *>(obj.get());
if (projCRS) {
const auto &conv = projCRS->derivingConversionRef();
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 52c484b9..bac42d5b 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -3344,6 +3344,9 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
ci_equal(stripQuotes(extensionChildren[0]), "PROJ4")) {
std::string projString = stripQuotes(extensionChildren[1]);
if (starts_with(projString, "+proj=")) {
+ if (projString.find(" +type=crs") == std::string::npos) {
+ projString += " +type=crs";
+ }
try {
auto projObj =
PROJStringParser().createFromPROJString(projString);
@@ -4281,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);
}
@@ -4449,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);
+}
// ---------------------------------------------------------------------------
@@ -4619,6 +4671,9 @@ std::string IPROJStringExportable::exportToPROJString(
}
}
if (bIsCRS) {
+ if (!formatter->hasParam("type")) {
+ formatter->addParam("type", "crs");
+ }
formatter->setCRSExport(false);
}
return formatter->toString();
@@ -5217,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);
@@ -5242,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)
@@ -5298,18 +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 (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());
+ }
}
// ---------------------------------------------------------------------------
@@ -5319,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());
}
@@ -5596,6 +5674,7 @@ const DatabaseContextPtr &PROJStringFormatter::databaseContext() const {
struct PROJStringParser::Private {
DatabaseContextPtr dbContext_{};
+ PJ_CONTEXT *ctx_{};
bool usePROJ4InitRules_ = false;
std::vector<std::string> warningList_{};
@@ -5623,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)) {
@@ -5704,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,
@@ -6902,7 +7001,7 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS(
param.key == "towgs84" || param.key == "nadgrids" ||
param.key == "geoidgrids" || param.key == "units" ||
param.key == "to_meter" || param.key == "vunits" ||
- param.key == "vto_meter") {
+ param.key == "vto_meter" || param.key == "type") {
continue;
}
if (param.value.empty()) {
@@ -7252,22 +7351,24 @@ static const metadata::ExtentPtr &getExtent(const crs::CRS *crs) {
// ---------------------------------------------------------------------------
/** \brief Instantiate a sub-class of BaseObject from a PROJ string.
+ *
+ * The projString must contain +type=crs for the object to be detected as a
+ * CRS instead of a CoordinateOperation.
+ *
* @throw ParsingException
*/
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()) {
@@ -7288,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")
@@ -7300,7 +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->steps_[0].inverted) {
// Those used to come from a text init file
// We only support them in compatibility mode
@@ -7331,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(
@@ -7342,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) {
@@ -7363,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;
}
}
@@ -7372,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;) {
@@ -7403,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);
@@ -7420,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)) {
@@ -7483,9 +7626,82 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
}
}
+ 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;
@@ -7515,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 ||
@@ -7541,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)) &&
@@ -7565,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) {
diff --git a/src/proj.h b/src/proj.h
index 2b4531ec..552f4b26 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -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 bbadb376..dcfd6797 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -899,6 +899,11 @@ void pj_freeup_plain (PJ *P);
PJ* pj_init_ctx_with_allow_init_epsg( projCtx_t *ctx, int argc, char **argv, int allow_init_epsg );
+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