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