diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-01-22 14:36:16 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-01-22 14:36:16 +0100 |
| commit | b172b29fa0e1dac18270b41dde81df2759521c16 (patch) | |
| tree | cab080da91a3f7733ed44cf952eb32d8f61f67a4 /src | |
| parent | c048292f3b8e408e6a81700a74d9b44d532227ac (diff) | |
| download | PROJ-b172b29fa0e1dac18270b41dde81df2759521c16.tar.gz PROJ-b172b29fa0e1dac18270b41dde81df2759521c16.zip | |
ISO19111 PROJ string parser: discard parameters not recognized by PROJ
Diffstat (limited to 'src')
| -rw-r--r-- | src/4D_api.cpp | 7 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 319 |
2 files changed, 216 insertions, 110 deletions
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<char*>(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<char*>(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<char*>(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<Step::KeyValue> globalParamValues_{}; std::string title_{}; + bool ignoreNadgrids_ = false; + template <class T> // 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 <class T> // 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,24 +5770,26 @@ struct PROJStringParser::Private { template <class T> // 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; } } @@ -5789,10 +5797,22 @@ struct PROJStringParser::Private { } // 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<CoordinateSystemAxisNNPtr> - 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<CoordinateSystemAxisNNPtr> -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<double> 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<CRSNNPtr>{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<Logger *>(user_data)->setMessage(msg); - } + 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; + // 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<Step::KeyValue> newParamValues{}; + std::set<std::string> 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<size_t>(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<BaseObject>(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<BaseObject>(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<BaseObject>(obj); + } + } } if (iProjStep < 0 && iHelmert > 0 && iMolodensky < 0 && |
