From c048292f3b8e408e6a81700a74d9b44d532227ac Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 22 Jan 2019 10:58:13 +0100 Subject: Coordinate operation computation with boundcrs / wktext: drop useless early bindins terms in generated pipeline (fixes #1232) --- src/iso19111/c_api.cpp | 2 +- src/iso19111/common.cpp | 3 +-- src/iso19111/coordinateoperation.cpp | 1 + src/iso19111/crs.cpp | 8 ++++-- src/iso19111/io.cpp | 49 ++++++++++++++++++++++++------------ 5 files changed, 42 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 06a3c02e..de11f181 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -337,7 +337,7 @@ PJ *proj_create(PJ_CONTEXT *ctx, const char *text) { assert(text); // Only connect to proj.db if needed - if( strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr ) { + if (strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr) { getDBcontextNoException(ctx, __FUNCTION__); } try { diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp index ca9d3b3e..57654d84 100644 --- a/src/iso19111/common.cpp +++ b/src/iso19111/common.cpp @@ -1122,8 +1122,7 @@ struct DataEpoch::Private { // --------------------------------------------------------------------------- -DataEpoch::DataEpoch() - : d(internal::make_unique(Measure())) {} +DataEpoch::DataEpoch() : d(internal::make_unique(Measure())) {} // --------------------------------------------------------------------------- diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 723fddec..6f9b6283 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -11100,6 +11100,7 @@ CoordinateOperationFactory::Private::createOperations( } auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); + projFormatter->setDropEarlyBindingsTerms(true); projFormatter->startInversion(); sourceProjExportable->_exportToPROJString(projFormatter.get()); auto geogSrc = diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 74b94a1f..adb441cd 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -1653,8 +1653,7 @@ CRSNNPtr GeographicCRS::_shallowClone() const { * * @return a EllipsoidalCS. */ -const cs::EllipsoidalCSNNPtr & -GeographicCRS::coordinateSystem() PROJ_PURE_DEFN { +const cs::EllipsoidalCSNNPtr &GeographicCRS::coordinateSystem() PROJ_PURE_DEFN { return d->coordinateSystem_; } @@ -3767,6 +3766,11 @@ void BoundCRS::_exportToPROJString( "baseCRS of BoundCRS cannot be exported as a PROJ string"); } + if (formatter->getDropEarlyBindingsTerms()) { + crs_exportable->_exportToPROJString(formatter); + return; + } + auto vdatumProj4GridName = getVDatumPROJ4GRIDS(); if (!vdatumProj4GridName.empty()) { formatter->setVDatumExtension(vdatumProj4GridName); diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 4c417585..0722c22a 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4745,6 +4745,7 @@ struct PROJStringFormatter::Private { bool addNoDefs_ = true; bool coordOperationOptimizations_ = false; bool crsExport_ = false; + bool dropEarlyBindingsTerms_ = false; std::string result_{}; @@ -5274,7 +5275,7 @@ void PROJStringFormatter::Private::appendToResult(const char *str) { static void PROJStringSyntaxParser(const std::string &projString, std::vector &steps, std::vector &globalParamValues, - std::string &title) { + std::string &title, bool dropEarlyBindingsTerms) { const char *c_str = projString.c_str(); std::vector tokens; @@ -5352,13 +5353,17 @@ PROJStringSyntaxParser(const std::string &projString, std::vector &steps, } 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); + if (!(dropEarlyBindingsTerms && + (key == "towgs84" || key == "nadgrids" || + key == "geoidgrids" || key == "wktext"))) { + 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); + } } } } @@ -5433,7 +5438,8 @@ void PROJStringFormatter::ingestPROJString( { std::vector steps; std::string title; - PROJStringSyntaxParser(str, steps, d->globalParamValues_, title); + PROJStringSyntaxParser(str, steps, d->globalParamValues_, title, + d->dropEarlyBindingsTerms_); d->steps_.insert(d->steps_.end(), steps.begin(), steps.end()); } @@ -5695,6 +5701,18 @@ bool PROJStringFormatter::omitZUnitConversion() const { // --------------------------------------------------------------------------- +void PROJStringFormatter::setDropEarlyBindingsTerms(bool drop) { + d->dropEarlyBindingsTerms_ = drop; +} + +// --------------------------------------------------------------------------- + +bool PROJStringFormatter::getDropEarlyBindingsTerms() const { + return d->dropEarlyBindingsTerms_; +} + +// --------------------------------------------------------------------------- + const DatabaseContextPtr &PROJStringFormatter::databaseContext() const { return d->dbContext_; } @@ -6673,8 +6691,12 @@ CRSNNPtr PROJStringParser::Private::buildBoundOrCompoundCRSIfNeeded(int iStep, CRSNNPtr crs) { const auto &step = steps_[iStep]; + const auto &nadgrids = getParamValue(step, "nadgrids"); const auto &towgs84 = getParamValue(step, "towgs84"); - if (!towgs84.empty()) { + // nadgrids has the priority over towgs84 + if (!nadgrids.empty()) { + crs = BoundCRS::createFromNadgrids(crs, nadgrids); + } else if (!towgs84.empty()) { std::vector towgs84Values; const auto tokens = split(towgs84, ','); for (const auto &str : tokens) { @@ -6687,11 +6709,6 @@ PROJStringParser::Private::buildBoundOrCompoundCRSIfNeeded(int iStep, crs = BoundCRS::createFromTOWGS84(crs, towgs84Values); } - const auto &nadgrids = getParamValue(step, "nadgrids"); - if (!nadgrids.empty()) { - crs = BoundCRS::createFromNadgrids(crs, nadgrids); - } - const auto &geoidgrids = getParamValue(step, "geoidgrids"); if (!geoidgrids.empty()) { auto vdatum = @@ -7397,7 +7414,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) { d->globalParamValues_.clear(); d->projString_ = projString; PROJStringSyntaxParser(projString, d->steps_, d->globalParamValues_, - d->title_); + d->title_, false); if (d->steps_.empty()) { const auto &vunits = d->getGlobalParamValue("vunits"); -- cgit v1.2.3 From b172b29fa0e1dac18270b41dde81df2759521c16 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 22 Jan 2019 14:36:16 +0100 Subject: ISO19111 PROJ string parser: discard parameters not recognized by PROJ --- src/4D_api.cpp | 7 +- src/iso19111/io.cpp | 319 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 216 insertions(+), 110 deletions(-) (limited to 'src') diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 642fbb1f..9f88730b 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -456,6 +456,9 @@ Returns 1 on success, 0 on failure /* Swap axes? */ p = pj_param_exists (P->params, "axis"); + const bool disable_grid_presence_check = pj_param_exists ( + P->params, "disable_grid_presence_check") != nullptr; + /* Don't axisswap if data are already in "enu" order */ if (p && (0!=strcmp ("enu", p->param))) { char *def = static_cast(malloc (100+strlen(P->axis))); @@ -471,7 +474,7 @@ Returns 1 on success, 0 on failure /* Geoid grid(s) given? */ p = pj_param_exists (P->params, "geoidgrids"); - if (p && strlen (p->param) > strlen ("geoidgrids=")) { + if (!disable_grid_presence_check && p && strlen (p->param) > strlen ("geoidgrids=")) { char *gridnames = p->param + strlen ("geoidgrids="); char *def = static_cast(malloc (100+2*strlen(gridnames))); if (nullptr==def) @@ -487,7 +490,7 @@ Returns 1 on success, 0 on failure /* Datum shift grid(s) given? */ p = pj_param_exists (P->params, "nadgrids"); - if (p && strlen (p->param) > strlen ("nadgrids=")) { + if (!disable_grid_presence_check && p && strlen (p->param) > strlen ("nadgrids=")) { char *gridnames = p->param + strlen ("nadgrids="); char *def = static_cast(malloc (100+2*strlen(gridnames))); if (nullptr==def) diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 0722c22a..b3cd83d6 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4692,6 +4692,7 @@ struct Step { struct KeyValue { std::string key{}; std::string value{}; + bool usedByParser = false; // only for PROJStringParser used explicit KeyValue(const std::string &keyIn) : key(keyIn) {} @@ -5265,7 +5266,7 @@ void PROJStringFormatter::setCoordinateOperationOptimizations(bool enable) { void PROJStringFormatter::Private::appendToResult(const char *str) { if (!result_.empty()) { - result_ += " "; + result_ += ' '; } result_ += str; } @@ -5735,16 +5736,20 @@ struct PROJStringParser::Private { std::vector globalParamValues_{}; std::string title_{}; + bool ignoreNadgrids_ = false; + template // cppcheck-suppress functionStatic - bool hasParamValue(const Step &step, const T key) { - for (const auto &pair : globalParamValues_) { + bool hasParamValue(Step &step, const T key) { + for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { + pair.usedByParser = true; return true; } } - for (const auto &pair : step.paramValues) { + for (auto &pair : step.paramValues) { if (ci_equal(pair.key, key)) { + pair.usedByParser = true; return true; } } @@ -5753,9 +5758,10 @@ struct PROJStringParser::Private { template // cppcheck-suppress functionStatic - const std::string &getGlobalParamValue(const T key) { - for (const auto &pair : globalParamValues_) { + const std::string &getGlobalParamValue(T key) { + for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { + pair.usedByParser = true; return pair.value; } } @@ -5764,35 +5770,49 @@ struct PROJStringParser::Private { template // cppcheck-suppress functionStatic - const std::string &getParamValue(const Step &step, const T key) { - for (const auto &pair : globalParamValues_) { + const std::string &getParamValue(Step &step, const T key) { + for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { + pair.usedByParser = true; return pair.value; } } - for (const auto &pair : step.paramValues) { + for (auto &pair : step.paramValues) { if (ci_equal(pair.key, key)) { + pair.usedByParser = true; return pair.value; } } return emptyString; } - // cppcheck-suppress functionStatic - const std::string &getParamValueK(const Step &step) { - for (const auto &pair : step.paramValues) { + static const std::string &getParamValueK(Step &step) { + for (auto &pair : step.paramValues) { if (ci_equal(pair.key, "k") || ci_equal(pair.key, "k_0")) { + pair.usedByParser = true; return pair.value; } } return emptyString; } + // cppcheck-suppress functionStatic + bool hasUnusedParameters(const Step &step) const { + if (steps_.size() == 1) { + for (const auto &pair : step.paramValues) { + if (pair.key != "no_defs" && !pair.usedByParser) { + return true; + } + } + } + return false; + } + // cppcheck-suppress functionStatic std::string guessBodyName(double a); - PrimeMeridianNNPtr buildPrimeMeridian(const Step &step); - GeodeticReferenceFrameNNPtr buildDatum(const Step &step, + PrimeMeridianNNPtr buildPrimeMeridian(Step &step); + GeodeticReferenceFrameNNPtr buildDatum(Step &step, const std::string &title); GeographicCRSNNPtr buildGeographicCRS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, @@ -5801,7 +5821,7 @@ struct PROJStringParser::Private { CRSNNPtr buildProjectedCRS(int iStep, GeographicCRSNNPtr geogCRS, int iUnitConvert, int iAxisSwap); CRSNNPtr buildBoundOrCompoundCRSIfNeeded(int iStep, CRSNNPtr crs); - UnitOfMeasure buildUnit(const Step &step, const std::string &unitsParamName, + UnitOfMeasure buildUnit(Step &step, const std::string &unitsParamName, const std::string &toMeterParamName); CoordinateOperationNNPtr buildHelmertTransformation( int iStep, int iFirstAxisSwap = -1, int iFirstUnitConvert = -1, @@ -5815,7 +5835,7 @@ struct PROJStringParser::Private { enum class AxisType { REGULAR, NORTH_POLE, SOUTH_POLE }; std::vector - processAxisSwap(const Step &step, const UnitOfMeasure &unit, int iAxisSwap, + processAxisSwap(Step &step, const UnitOfMeasure &unit, int iAxisSwap, AxisType axisType, bool ignorePROJAxis); EllipsoidalCSNNPtr buildEllipsoidalCS(int iStep, int iUnitConvert, @@ -5958,7 +5978,7 @@ static UnitOfMeasure _buildUnit(double to_meter_value) { // --------------------------------------------------------------------------- UnitOfMeasure -PROJStringParser::Private::buildUnit(const Step &step, +PROJStringParser::Private::buildUnit(Step &step, const std::string &unitsParamName, const std::string &toMeterParamName) { UnitOfMeasure unit = UnitOfMeasure::METRE; @@ -6067,8 +6087,7 @@ static PropertyMap createMapWithUnknownName() { // --------------------------------------------------------------------------- -PrimeMeridianNNPtr -PROJStringParser::Private::buildPrimeMeridian(const Step &step) { +PrimeMeridianNNPtr PROJStringParser::Private::buildPrimeMeridian(Step &step) { PrimeMeridianNNPtr pm = PrimeMeridian::GREENWICH; const auto &pmStr = getParamValue(step, "pm"); @@ -6116,8 +6135,7 @@ std::string PROJStringParser::Private::guessBodyName(double a) { // --------------------------------------------------------------------------- GeodeticReferenceFrameNNPtr -PROJStringParser::Private::buildDatum(const Step &step, - const std::string &title) { +PROJStringParser::Private::buildDatum(Step &step, const std::string &title) { const auto &ellpsStr = getParamValue(step, "ellps"); const auto &datumStr = getParamValue(step, "datum"); @@ -6434,7 +6452,7 @@ createAxis(const std::string &name, const std::string &abbreviation, } std::vector -PROJStringParser::Private::processAxisSwap(const Step &step, +PROJStringParser::Private::processAxisSwap(Step &step, const UnitOfMeasure &unit, int iAxisSwap, AxisType axisType, bool ignorePROJAxis) { @@ -6511,7 +6529,7 @@ PROJStringParser::Private::processAxisSwap(const Step &step, throw ParsingException("Unhandled axis=" + axisStr); } } else if (iAxisSwap >= 0) { - const auto &stepAxisSwap = steps_[iAxisSwap]; + auto &stepAxisSwap = steps_[iAxisSwap]; const auto &orderStr = getParamValue(stepAxisSwap, "order"); auto orderTab = split(orderStr, ','); if (orderTab.size() != 2) { @@ -6544,13 +6562,13 @@ EllipsoidalCSNNPtr PROJStringParser::Private::buildEllipsoidalCS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis) { - const auto &step = steps_[iStep]; + auto &step = steps_[iStep]; assert(iUnitConvert < 0 || ci_equal(steps_[iUnitConvert].name, "unitconvert")); UnitOfMeasure angularUnit = UnitOfMeasure::DEGREE; if (iUnitConvert >= 0) { - const auto &stepUnitConvert = steps_[iUnitConvert]; + auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); if (stepUnitConvert.inverted) { @@ -6606,32 +6624,36 @@ GeographicCRSNNPtr PROJStringParser::Private::buildGeographicCRS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis) { - const auto &step = steps_[iStep]; + auto &step = steps_[iStep]; const bool l_isGeographicStep = isGeographicStep(step.name); const auto &title = l_isGeographicStep ? title_ : emptyString; + // units=m is often found in the wild. + // No need to create a extension string for this + hasParamValue(step, "units"); + auto datum = buildDatum(step, title); auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); + auto cs = buildEllipsoidalCS(iStep, iUnitConvert, iAxisSwap, ignoreVUnits, + ignorePROJAxis); + if (l_isGeographicStep && - (hasParamValue(step, "wktext") || - hasParamValue(step, "lon_wrap") | hasParamValue(step, "geoc") || + (hasUnusedParameters(step) || getNumericValue(getParamValue(step, "lon_0")) != 0.0)) { props.set("EXTENSION_PROJ4", projString_); } - return GeographicCRS::create( - props, datum, buildEllipsoidalCS(iStep, iUnitConvert, iAxisSwap, - ignoreVUnits, ignorePROJAxis)); + return GeographicCRS::create(props, datum, cs); } // --------------------------------------------------------------------------- GeodeticCRSNNPtr PROJStringParser::Private::buildGeocentricCRS(int iStep, int iUnitConvert) { - const auto &step = steps_[iStep]; + auto &step = steps_[iStep]; assert(isGeocentricStep(step.name)); assert(iUnitConvert < 0 || @@ -6643,7 +6665,7 @@ PROJStringParser::Private::buildGeocentricCRS(int iStep, int iUnitConvert) { UnitOfMeasure unit = UnitOfMeasure::METRE; if (iUnitConvert >= 0) { - const auto &stepUnitConvert = steps_[iUnitConvert]; + auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); const std::string *z_in = &getParamValue(stepUnitConvert, "z_in"); @@ -6677,11 +6699,12 @@ PROJStringParser::Private::buildGeocentricCRS(int iStep, int iUnitConvert) { auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); - if (hasParamValue(step, "wktext")) { + auto cs = CartesianCS::createGeocentric(unit); + + if (hasUnusedParameters(step)) { props.set("EXTENSION_PROJ4", projString_); } - auto cs = CartesianCS::createGeocentric(unit); return GeodeticCRS::create(props, datum, cs); } @@ -6690,11 +6713,11 @@ PROJStringParser::Private::buildGeocentricCRS(int iStep, int iUnitConvert) { CRSNNPtr PROJStringParser::Private::buildBoundOrCompoundCRSIfNeeded(int iStep, CRSNNPtr crs) { - const auto &step = steps_[iStep]; + auto &step = steps_[iStep]; const auto &nadgrids = getParamValue(step, "nadgrids"); const auto &towgs84 = getParamValue(step, "towgs84"); // nadgrids has the priority over towgs84 - if (!nadgrids.empty()) { + if (!ignoreNadgrids_ && !nadgrids.empty()) { crs = BoundCRS::createFromNadgrids(crs, nadgrids); } else if (!towgs84.empty()) { std::vector towgs84Values; @@ -6847,7 +6870,7 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR); for (size_t i = 0; i < step.paramValues.size(); ++i) { if (ci_equal(step.paramValues[i].key, "nadgrids")) { - step.paramValues.erase(step.paramValues.begin() + i); + ignoreNadgrids_ = true; break; } } @@ -6909,7 +6932,7 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( UnitOfMeasure unit = buildUnit(step, "units", "to_meter"); if (iUnitConvert >= 0) { - const auto &stepUnitConvert = steps_[iUnitConvert]; + auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); if (stepUnitConvert.inverted) { @@ -7102,8 +7125,7 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); - - if (hasParamValue(step, "wktext")) { + if (hasUnusedParameters(step)) { props.set("EXTENSION_PROJ4", projString_); } @@ -7122,7 +7144,6 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( crs = CompoundCRS::create(mapWithUnknownName, std::vector{crs, vcrs}); } - return crs; } @@ -7439,16 +7460,11 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } } - if (((d->steps_.size() == 1 && + const bool isGeocentricCRS = + ((d->steps_.size() == 1 && d->getParamValue(d->steps_[0], "type") == "crs") || (d->steps_.size() == 2 && d->steps_[1].name == "unitconvert")) && - !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") - ? 1 - : -1)); - } + !d->steps_[0].inverted && isGeocentricStep(d->steps_[0].name); // +init=xxxx:yyyy syntax if (d->steps_.size() == 1 && d->steps_[0].isInit && @@ -7691,60 +7707,133 @@ PROJStringParser::createFromPROJString(const std::string &projString) { unexpectedStructure = true; } - if (unexpectedStructure || iHelmert >= 0 || iMolodensky >= 0) { - struct Logger { - std::string msg{}; + struct Logger { + std::string msg{}; - // cppcheck-suppress functionStatic - void setMessage(const char *msgIn) noexcept { - try { - msg = msgIn; - } catch (const std::exception &) { - } + // 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(user_data)->setMessage(msg); - } + static void log(void *user_data, int level, const char *msg) { + if (level == PJ_LOG_ERROR) { + static_cast(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; + // 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.find("type=crs") != std::string::npos + ? projString + " +disable_grid_presence_check" + : projString) + .c_str()); + valid = pj != nullptr; + + // Remove parameters not understood by PROJ. + if (valid && d->steps_.size() == 1) { + std::vector newParamValues{}; + std::set foundKeys; + auto &step = d->steps_[0]; + + for (auto &kv : step.paramValues) { + bool recognizedByPROJ = false; + if (foundKeys.find(kv.key) != foundKeys.end()) { + continue; + } + foundKeys.insert(kv.key); + if (step.name == "krovak" && kv.key == "alpha") { + // We recognize it in our CRS parsing code + recognizedByPROJ = true; + } else { + for (auto cur = pj->params; cur; cur = cur->next) { + const char *equal = strchr(cur->param, '='); + if (equal && + static_cast(equal - cur->param) == + kv.key.size()) { + if (memcmp(cur->param, kv.key.c_str(), kv.key.size()) == + 0) { + recognizedByPROJ = (cur->used == 1); + break; + } + } else if (strcmp(cur->param, kv.key.c_str()) == 0) { + recognizedByPROJ = (cur->used == 1); + break; + } + } + } + if (recognizedByPROJ) { + newParamValues.emplace_back(kv); + } + } + step.paramValues = newParamValues; - auto pj_context = d->ctx_ ? d->ctx_ : proj_context_create(); - if (!pj_context) { - throw ParsingException("out of memory"); + d->projString_.clear(); + if (!step.name.empty()) { + d->projString_ += step.isInit ? "+init=" : "+proj="; + d->projString_ += step.name; } - 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; + for (const auto ¶mValue : step.paramValues) { + if (!d->projString_.empty()) { + d->projString_ += ' '; + } + d->projString_ += '+'; + d->projString_ += paramValue.key; + if (!paramValue.value.empty()) { + d->projString_ += '='; + d->projString_ += + pj_double_quote_string_param_if_needed(paramValue.value); } } + } + + proj_destroy(pj); - if (pj_context != d->ctx_) { - proj_context_destroy(pj_context); + 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 (!valid) { + throw ParsingException(logger.msg); + } + + if (isGeocentricCRS) { + // First run is dry run to mark all recognized/unrecognized tokens + for (int iter = 0; iter < 2; iter++) { + auto obj = d->buildBoundOrCompoundCRSIfNeeded( + 0, d->buildGeocentricCRS(0, (d->steps_.size() == 2 && + d->steps_[1].name == "unitconvert") + ? 1 + : -1)); + if (iter == 1) { + return nn_static_pointer_cast(obj); + } } } @@ -7755,9 +7844,16 @@ PROJStringParser::createFromPROJString(const std::string &projString) { (iFirstUnitConvert < 0 || iSecondUnitConvert < 0) && (iFirstAxisSwap < 0 || iSecondAxisSwap < 0)) { const bool ignoreVUnits = false; - return d->buildBoundOrCompoundCRSIfNeeded( - 0, d->buildGeographicCRS(iFirstGeogStep, iFirstUnitConvert, - iFirstAxisSwap, ignoreVUnits, false)); + // First run is dry run to mark all recognized/unrecognized tokens + for (int iter = 0; iter < 2; iter++) { + auto obj = d->buildBoundOrCompoundCRSIfNeeded( + 0, + d->buildGeographicCRS(iFirstGeogStep, iFirstUnitConvert, + iFirstAxisSwap, ignoreVUnits, false)); + if (iter == 1) { + return nn_static_pointer_cast(obj); + } + } } if (iProjStep >= 0 && !d->steps_[iProjStep].inverted && (iFirstGeogStep < 0 || iFirstGeogStep + 1 == iProjStep) && @@ -7766,20 +7862,27 @@ PROJStringParser::createFromPROJString(const std::string &projString) { if (iFirstGeogStep < 0) iFirstGeogStep = iProjStep; const bool ignoreVUnits = true; - return d->buildBoundOrCompoundCRSIfNeeded( - iProjStep, - d->buildProjectedCRS( + // First run is dry run to mark all recognized/unrecognized tokens + for (int iter = 0; iter < 2; iter++) { + auto obj = d->buildBoundOrCompoundCRSIfNeeded( iProjStep, - d->buildGeographicCRS( - iFirstGeogStep, - iFirstUnitConvert < iFirstGeogStep ? iFirstUnitConvert - : -1, - iFirstAxisSwap < iFirstGeogStep ? iFirstAxisSwap : -1, - ignoreVUnits, true), - iFirstUnitConvert < iFirstGeogStep ? iSecondUnitConvert - : iFirstUnitConvert, - iFirstAxisSwap < iFirstGeogStep ? iSecondAxisSwap - : iFirstAxisSwap)); + d->buildProjectedCRS( + iProjStep, + d->buildGeographicCRS( + iFirstGeogStep, iFirstUnitConvert < iFirstGeogStep + ? iFirstUnitConvert + : -1, + iFirstAxisSwap < iFirstGeogStep ? iFirstAxisSwap + : -1, + ignoreVUnits, true), + iFirstUnitConvert < iFirstGeogStep ? iSecondUnitConvert + : iFirstUnitConvert, + iFirstAxisSwap < iFirstGeogStep ? iSecondAxisSwap + : iFirstAxisSwap)); + if (iter == 1) { + return nn_static_pointer_cast(obj); + } + } } if (iProjStep < 0 && iHelmert > 0 && iMolodensky < 0 && -- cgit v1.2.3 From 7e11edaa2d1abc09478f3bd3fb9926cb0ef3fdda Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 22 Jan 2019 15:51:33 +0100 Subject: Coordinate operation computation with CRS with extension string: replace datum by ellps --- src/iso19111/crs.cpp | 2 +- src/iso19111/io.cpp | 56 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index adb441cd..ebbed7a1 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -1209,7 +1209,7 @@ void GeodeticCRS::addDatumInfoToPROJString( const auto &nadgrids = formatter->getHDatumExtension(); const auto &l_datum = datum(); if (formatter->getCRSExport() && l_datum && TOWGS84Params.empty() && - nadgrids.empty()) { + nadgrids.empty() && !formatter->getDropEarlyBindingsTerms()) { if (l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6326.get(), util::IComparable::Criterion::EQUIVALENT)) { diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index b3cd83d6..a82f24c7 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -5280,34 +5280,36 @@ PROJStringSyntaxParser(const std::string &projString, std::vector &steps, const char *c_str = projString.c_str(); std::vector tokens; - size_t i = 0; - while (true) { - for (; isspace(c_str[i]); i++) { - } - std::string token; - bool in_string = false; - for (; c_str[i]; i++) { - if (in_string) { - if (c_str[i] == '"' && c_str[i + 1] == '"') { + { + size_t i = 0; + while (true) { + for (; isspace(c_str[i]); i++) { + } + std::string token; + bool in_string = false; + for (; c_str[i]; i++) { + if (in_string) { + if (c_str[i] == '"' && c_str[i + 1] == '"') { + i++; + } else if (c_str[i] == '"') { + in_string = false; + continue; + } + } else if (c_str[i] == '=' && c_str[i + 1] == '"') { + in_string = true; + token += c_str[i]; i++; - } else if (c_str[i] == '"') { - in_string = false; continue; + } else if (isspace(c_str[i])) { + break; } - } else if (c_str[i] == '=' && c_str[i + 1] == '"') { - in_string = true; token += c_str[i]; - i++; - continue; - } else if (isspace(c_str[i])) { + } + if (token.empty()) { break; } - token += c_str[i]; + tokens.emplace_back(token); } - if (token.empty()) { - break; - } - tokens.emplace_back(token); } bool prevWasTitle = false; @@ -5356,10 +5358,20 @@ PROJStringSyntaxParser(const std::string &projString, std::vector &steps, auto key = word.substr(0, pos); if (!(dropEarlyBindingsTerms && (key == "towgs84" || key == "nadgrids" || - key == "geoidgrids" || key == "wktext"))) { + key == "geoidgrids"))) { auto pair = (pos != std::string::npos) ? Step::KeyValue(key, word.substr(pos + 1)) : Step::KeyValue(key); + if (dropEarlyBindingsTerms && key == "datum") { + const auto datums = pj_get_datums_ref(); + for (int i = 0; datums[i].id != nullptr; i++) { + if (pair.value == datums[i].id) { + pair.key = "ellps"; + pair.value = datums[i].ellipse_id; + break; + } + } + } if (steps.empty()) { globalParamValues.push_back(pair); } else { -- cgit v1.2.3