diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/ctx.cpp | 8 | ||||
| -rw-r--r-- | src/datum_set.cpp | 12 | ||||
| -rw-r--r-- | src/ell_set.cpp | 8 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 44 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 88 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 83 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 66 | ||||
| -rw-r--r-- | src/pipeline.cpp | 7 | ||||
| -rw-r--r-- | src/proj.h | 2 | ||||
| -rw-r--r-- | src/proj_api.h | 2 | ||||
| -rw-r--r-- | src/proj_internal.h | 2 | ||||
| -rw-r--r-- | src/projections/ob_tran.cpp | 4 | ||||
| -rw-r--r-- | src/release.cpp | 2 |
14 files changed, 259 insertions, 71 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index fe7f2572..0a868412 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,7 +42,7 @@ geodtest_LDADD = libproj.la lib_LTLIBRARIES = libproj.la -libproj_la_LDFLAGS = -no-undefined -version-info 17:0:2 +libproj_la_LDFLAGS = -no-undefined -version-info 17:1:2 libproj_la_LIBADD = @SQLITE3_LIBS@ libproj_la_SOURCES = \ diff --git a/src/ctx.cpp b/src/ctx.cpp index 622a814d..bcb6e1cc 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -60,6 +60,14 @@ void pj_set_ctx( projPJ pj, projCtx ctx ) if (pj==nullptr) return; pj->ctx = ctx; + if( pj->is_pipeline ) + { + pj_pipeline_assign_context_to_steps(pj, ctx); + } + for( const auto &alt: pj->alternativeCoordinateOperations ) + { + pj_set_ctx(alt.pj, ctx); + } } /************************************************************************/ diff --git a/src/datum_set.cpp b/src/datum_set.cpp index c1cb4cb9..873d7be5 100644 --- a/src/datum_set.cpp +++ b/src/datum_set.cpp @@ -85,10 +85,22 @@ int pj_datum_set(projCtx ctx, paralist *pl, PJ *projdef) entry[ sizeof(entry) - 1 ] = '\0'; curr = curr->next = pj_mkparam(entry); + if (nullptr == curr) + { + pj_ctx_set_errno(ctx, ENOMEM); + return 1; + } } if( pj_datums[i].defn && strlen(pj_datums[i].defn) > 0 ) + { curr = curr->next = pj_mkparam(pj_datums[i].defn); + if (nullptr == curr) + { + pj_ctx_set_errno(ctx, ENOMEM); + return 1; + } + } (void)curr; /* make clang static analyzer happy */ } diff --git a/src/ell_set.cpp b/src/ell_set.cpp index d2930ca4..bb46b3a4 100644 --- a/src/ell_set.cpp +++ b/src/ell_set.cpp @@ -1,5 +1,6 @@ /* set ellipsoid parameters a and es */ +#include <errno.h> #include <math.h> #include <stddef.h> #include <string.h> @@ -156,7 +157,14 @@ static int ellps_ellps (PJ *P) { err = proj_errno_reset (P); paralist* new_params = pj_mkparam (ellps->major); + if (nullptr == new_params) + return proj_errno_set (P, ENOMEM); new_params->next = pj_mkparam (ellps->ell); + if (nullptr == new_params->next) + { + pj_dealloc(new_params); + return proj_errno_set (P, ENOMEM); + } paralist* old_params = P->params; P->params = new_params; diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 1e5c106a..fb3b7f40 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -498,7 +498,7 @@ template <class T> static PROJ_STRING_LIST to_string_list(T &&set) { * proj_string_list_destroy(). * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL. * If provided, *out_grammar_errors will contain a list of errors regarding the - * WKT grammaer. It must be freed with proj_string_list_destroy(). + * WKT grammar. It must be freed with proj_string_list_destroy(). * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ @@ -522,6 +522,7 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, if (dbContext) { parser.attachDatabaseContext(NN_NO_CHECK(dbContext)); } + parser.setStrict(false); for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "STRICT="))) { @@ -536,10 +537,19 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, auto obj = nn_dynamic_pointer_cast<IdentifiedObject>( parser.createFromWKT(wkt)); + std::vector<std::string> warningsFromParsing; if (out_grammar_errors) { - auto warnings = parser.warningList(); - if (!warnings.empty()) { - *out_grammar_errors = to_string_list(warnings); + auto rawWarnings = parser.warningList(); + std::vector<std::string> grammarWarnings; + for (const auto &msg : rawWarnings) { + if (msg.find("Default it to") != std::string::npos) { + warningsFromParsing.push_back(msg); + } else { + grammarWarnings.push_back(msg); + } + } + if (!grammarWarnings.empty()) { + *out_grammar_errors = to_string_list(grammarWarnings); } } @@ -548,6 +558,8 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, if (derivedCRS) { auto warnings = derivedCRS->derivingConversionRef()->validateParameters(); + warnings.insert(warnings.end(), warningsFromParsing.begin(), + warningsFromParsing.end()); if (!warnings.empty()) { *out_warnings = to_string_list(warnings); } @@ -2089,14 +2101,26 @@ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) { * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. - * - * 100% means that the name of the reference entry + * <ul> + * <li>100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. - * 90% means that CRS are equivalent, but the names are not exactly the same. - * 70% means that CRS are equivalent), but the names do not match at all. - * 25% means that the CRS are not equivalent, but there is some similarity in - * the names. + * Note: in the case of a GeographicCRS whose axis + * order is implicit in the input definition (for example ESRI WKT), then axis + * order is ignored for the purpose of identification. That is the CRS built + * from + * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], + * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] + * will be identified to EPSG:4326, but will not pass a + * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, + * but rather isEquivalentTo(EPSG_4326, + * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) + * </li> + * <li>90% means that CRS are equivalent, but the names are not exactly the same.</li> + * <li>70% means that CRS are equivalent), but the names do not match at all.</li> + * <li>25% means that the CRS are not equivalent, but there is some similarity in + * the names.</li> + * </ul> * Other confidence values may be returned by some specialized implementations. * * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 3ba5a5a2..0eaba883 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -5927,24 +5927,31 @@ void Conversion::_exportToPROJString( if (!param->proj_name) { continue; } - auto value = + const auto value = parameterValueMeasure(param->wkt2_name, param->epsg_code); + double valueConverted = 0; + if (value == nullMeasure) { + // Deal with missing values. In an ideal world, this would + // not happen + if (param->epsg_code == + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { + valueConverted = 1.0; + } + } else if (param->unit_type == + common::UnitOfMeasure::Type::ANGULAR) { + valueConverted = + value.convertToUnit(common::UnitOfMeasure::DEGREE); + } else { + valueConverted = value.getSIValue(); + } + if (mapping->epsg_code == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP && strcmp(param->proj_name, "lat_1") == 0) { - formatter->addParam( - param->proj_name, - value.convertToUnit(common::UnitOfMeasure::DEGREE)); - formatter->addParam( - "lat_0", - value.convertToUnit(common::UnitOfMeasure::DEGREE)); - } else if (param->unit_type == - common::UnitOfMeasure::Type::ANGULAR) { - formatter->addParam( - param->proj_name, - value.convertToUnit(common::UnitOfMeasure::DEGREE)); + formatter->addParam(param->proj_name, valueConverted); + formatter->addParam("lat_0", valueConverted); } else { - formatter->addParam(param->proj_name, value.getSIValue()); + formatter->addParam(param->proj_name, valueConverted); } } @@ -6506,6 +6513,24 @@ static void getTransformationType(const crs::CRSNNPtr &sourceCRSIn, isGeog2D = nSrcAxisCount == 2 && nTargetAxisCount == 2; isGeog3D = !isGeog2D && nSrcAxisCount >= 2 && nTargetAxisCount >= 2; } + +// --------------------------------------------------------------------------- + +static int +useOperationMethodEPSGCodeIfPresent(const util::PropertyMap &properties, + int nDefaultOperationMethodEPSGCode) { + const auto *operationMethodEPSGCode = + properties.get("OPERATION_METHOD_EPSG_CODE"); + if (operationMethodEPSGCode) { + const auto boxedValue = dynamic_cast<const util::BoxedValue *>( + (*operationMethodEPSGCode).get()); + if (boxedValue && + boxedValue->type() == util::BoxedValue::Type::INTEGER) { + return boxedValue->integerValue(); + } + } + return nDefaultOperationMethodEPSGCode; +} //! @endcond // --------------------------------------------------------------------------- @@ -6534,12 +6559,13 @@ TransformationNNPtr Transformation::createGeocentricTranslations( isGeog3D); return create( properties, sourceCRSIn, targetCRSIn, nullptr, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D)), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), @@ -6592,11 +6618,12 @@ TransformationNNPtr Transformation::createPositionVector( isGeog3D); return createSevenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); @@ -6641,11 +6668,12 @@ TransformationNNPtr Transformation::createCoordinateFrameRotation( isGeog3D); return createSevenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); @@ -6784,12 +6812,13 @@ TransformationNNPtr Transformation::createTimeDependentPositionVector( isGeog3D); return createFifteenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, @@ -6861,12 +6890,13 @@ TransformationNNPtr Transformation::createTimeDependentCoordinateFrameRotation( isGeog3D); return createFifteenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, @@ -7448,6 +7478,14 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom, addModifiedIdentifier(map, op, true, derivedFrom); + const auto so = dynamic_cast<const SingleOperation *>(op); + if (so) { + const int soMethodEPSGCode = so->method()->getEPSGCode(); + if (soMethodEPSGCode > 0) { + map.set("OPERATION_METHOD_EPSG_CODE", soMethodEPSGCode); + } + } + return map; } @@ -12619,9 +12657,7 @@ CoordinateOperationFactory::Private::createOperations( auto vertCRSOfBaseOfBoundSrc = dynamic_cast<const crs::VerticalCRS *>(boundSrc->baseCRS().get()); - if (vertCRSOfBaseOfBoundSrc && hubSrcGeog && - hubSrcGeog->coordinateSystem()->axisList().size() == 3 && - geogDst->coordinateSystem()->axisList().size() == 3) { + if (vertCRSOfBaseOfBoundSrc && hubSrcGeog) { auto opsFirst = createOperations(sourceCRS, hubSrc, context); if (context.skipHorizontalTransformation) { if (!opsFirst.empty()) diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index eea7cce8..e044c0c7 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -237,7 +237,7 @@ CRSNNPtr CRS::alterGeodeticCRS(const GeodeticCRSNNPtr &newGeodCRS) const { auto projCRS = dynamic_cast<const ProjectedCRS *>(this); if (projCRS) { return ProjectedCRS::create(createPropertyMap(this), newGeodCRS, - projCRS->derivingConversionRef(), + projCRS->derivingConversion(), projCRS->coordinateSystem()); } @@ -264,7 +264,7 @@ CRSNNPtr CRS::alterCSLinearUnit(const common::UnitOfMeasure &unit) const { if (projCRS) { return ProjectedCRS::create( createPropertyMap(this), projCRS->baseCRS(), - projCRS->derivingConversionRef(), + projCRS->derivingConversion(), projCRS->coordinateSystem()->alterUnit(unit)); } } @@ -675,9 +675,8 @@ CRSNNPtr CRS::normalizeForVisualization() const { axisList[0]) : cs::CartesianCS::create(util::PropertyMap(), axisList[1], axisList[0], axisList[2]); - return util::nn_static_pointer_cast<CRS>( - ProjectedCRS::create(props, projCRS->baseCRS(), - projCRS->derivingConversionRef(), cs)); + return util::nn_static_pointer_cast<CRS>(ProjectedCRS::create( + props, projCRS->baseCRS(), projCRS->derivingConversion(), cs)); } } @@ -697,14 +696,26 @@ CRSNNPtr CRS::normalizeForVisualization() const { * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. - * - * 100% means that the name of the reference entry + * <ul> + * <li>100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. - * 90% means that CRS are equivalent, but the names are not exactly the same. - * 70% means that CRS are equivalent), but the names do not match at all. - * 25% means that the CRS are not equivalent, but there is some similarity in - * the names. + * Note: in the case of a GeographicCRS whose axis + * order is implicit in the input definition (for example ESRI WKT), then axis + * order is ignored for the purpose of identification. That is the CRS built + * from + * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], + * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] + * will be identified to EPSG:4326, but will not pass a + * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, + * but rather isEquivalentTo(EPSG_4326, + * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) + * </li> + * <li>90% means that CRS are equivalent, but the names are not exactly the same.</li> + * <li>70% means that CRS are equivalent), but the names do not match at all.</li> + * <li>25% means that the CRS are not equivalent, but there is some similarity in + * the names.</li> + * </ul> * Other confidence values may be returned by some specialized implementations. * * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and @@ -829,7 +840,7 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, !newName.empty() ? newName : nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast<GeodeticCRS>(base3DCRS)), - projCRS->derivingConversionRef(), cs)); + projCRS->derivingConversion(), cs)); } } @@ -1554,17 +1565,30 @@ static bool hasCodeCompatibleOfAuthorityFactory( * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage - * (0-100) of confidence in the match. - * 100% means that the name of the reference entry + * (0-100) of confidence in the match: + * <ul> + * <li>100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. - * 90% means that CRS are equivalent, but the names are not exactly the same. - * 70% means that CRS are equivalent (equivalent datum and coordinate system), - * but the names do not match at all. - * 60% means that ellipsoid, prime meridian and coordinate systems are - * equivalent, but the CRS and datum names do not match. - * 25% means that the CRS are not equivalent, but there is some similarity in - * the names. + * Note: in the case of a GeographicCRS whose axis + * order is implicit in the input definition (for example ESRI WKT), then axis + * order is ignored for the purpose of identification. That is the CRS built + * from + * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], + * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] + * will be identified to EPSG:4326, but will not pass a + * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, + * but rather isEquivalentTo(EPSG_4326, + * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) + * </li> + * <li>90% means that CRS are equivalent, but the names are not exactly the same. + * <li>70% means that CRS are equivalent (equivalent datum and coordinate system), + * but the names do not match at all.</li> + * <li>60% means that ellipsoid, prime meridian and coordinate systems are + * equivalent, but the CRS and datum names do not match.</li> + * <li>25% means that the CRS are not equivalent, but there is some similarity in + * the names.</li> + * </ul> * * @param authorityFactory Authority factory (or null, but degraded * functionality) @@ -3218,10 +3242,10 @@ bool ProjectedCRS::_isEquivalentTo( ProjectedCRSNNPtr ProjectedCRS::alterParametersLinearUnit(const common::UnitOfMeasure &unit, bool convertToNewUnit) const { - return create(createPropertyMap(this), baseCRS(), - derivingConversionRef()->alterParametersLinearUnit( - unit, convertToNewUnit), - coordinateSystem()); + return create( + createPropertyMap(this), baseCRS(), + derivingConversion()->alterParametersLinearUnit(unit, convertToNewUnit), + coordinateSystem()); } //! @endcond @@ -3239,13 +3263,16 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter, if (!formatter->getCRSExport()) { formatter->addStep("unitconvert"); formatter->addParam("xy_in", "m"); - formatter->addParam("z_in", "m"); + if (!formatter->omitZUnitConversion()) + formatter->addParam("z_in", "m"); if (projUnit.empty()) { formatter->addParam("xy_out", toSI); - formatter->addParam("z_out", toSI); + if (!formatter->omitZUnitConversion()) + formatter->addParam("z_out", toSI); } else { formatter->addParam("xy_out", projUnit); - formatter->addParam("z_out", projUnit); + if (!formatter->omitZUnitConversion()) + formatter->addParam("z_out", projUnit); } } else { if (projUnit.empty()) { diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index a9607247..c0628270 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -3185,7 +3185,16 @@ ConversionNNPtr WKTParser::Private::buildProjectionFromESRI( } } - const auto *wkt2_mapping = getMapping(esriMapping->wkt2_name); + const char *projectionMethodWkt2Name = esriMapping->wkt2_name; + if (ci_equal(esriProjectionName, "Krovak")) { + const std::string projCRSName = + stripQuotes(projCRSNode->GP()->children()[0]); + if (projCRSName.find("_East_North") != std::string::npos) { + projectionMethodWkt2Name = EPSG_NAME_METHOD_KROVAK_NORTH_ORIENTED; + } + } + + const auto *wkt2_mapping = getMapping(projectionMethodWkt2Name); if (ci_equal(esriProjectionName, "Stereographic")) { try { if (std::fabs(io::asDouble( @@ -3467,6 +3476,14 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( } propertiesMethod.set(IdentifiedObject::NAME_KEY, projectionName); + std::vector<bool> foundParameters; + if (mapping) { + size_t countParams = 0; + while (mapping->params[countParams] != nullptr) { + ++countParams; + } + foundParameters.resize(countParams); + } for (const auto &childNode : projCRSNode->GP()->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { const auto &childNodeChildren = childNode->GP()->children(); @@ -3487,11 +3504,18 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( continue; } } - const auto *paramMapping = + auto *paramMapping = mapping ? getMappingFromWKT1(mapping, parameterName) : nullptr; if (mapping && mapping->epsg_code == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && ci_equal(parameterName, "latitude_of_origin")) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (mapping->params[idx]->epsg_code == + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) { + foundParameters[idx] = true; + break; + } + } parameterName = EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN; propertiesParameter.set( Identifier::CODE_KEY, @@ -3499,6 +3523,12 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } else if (paramMapping) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (mapping->params[idx] == paramMapping) { + foundParameters[idx] = true; + break; + } + } parameterName = paramMapping->wkt2_name; if (paramMapping->epsg_code != 0) { propertiesParameter.set(Identifier::CODE_KEY, @@ -3522,6 +3552,38 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( } } + // Add back important parameters that should normally be present, but + // are sometimes missing. Currently we only deal with Scale factor at + // natural origin. This is to avoid a default value of 0 to slip in later. + // But such WKT should be considered invalid. + if (mapping) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (!foundParameters[idx] && + mapping->params[idx]->epsg_code == + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { + + emitRecoverableWarning( + "The WKT string lacks a value " + "for " EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN + ". Default it to 1."); + + PropertyMap propertiesParameter; + propertiesParameter.set( + Identifier::CODE_KEY, + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); + propertiesParameter.set(Identifier::CODESPACE_KEY, + Identifier::EPSG); + propertiesParameter.set( + IdentifiedObject::NAME_KEY, + EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); + parameters.push_back( + OperationParameter::create(propertiesParameter)); + values.push_back(ParameterValue::create( + Measure(1.0, UnitOfMeasure::SCALE_UNITY))); + } + } + } + return Conversion::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), propertiesMethod, parameters, values) diff --git a/src/pipeline.cpp b/src/pipeline.cpp index afa3b19a..76ffc98e 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -105,6 +105,7 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 #include "geodesic.h" #include "proj.h" #include "proj_internal.h" +#include "proj_experimental.h" PROJ_HEAD(pipeline, "Transformation pipeline manager"); PROJ_HEAD(pop, "Retrieve coordinate value from pipeline stack"); @@ -136,7 +137,11 @@ static PJ_LPZ pipeline_reverse_3d (PJ_XYZ xyz, PJ *P); static PJ_XY pipeline_forward (PJ_LP lp, PJ *P); static PJ_LP pipeline_reverse (PJ_XY xy, PJ *P); - +void pj_pipeline_assign_context_to_steps( PJ* P, PJ_CONTEXT* ctx ) +{ + for (int i = 1; i <= static_cast<struct pj_opaque*>(P->opaque)->steps; i++) + proj_assign_context(static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], ctx); +} static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { @@ -154,7 +154,7 @@ extern "C" { /* The version numbers should be updated with every release! **/ #define PROJ_VERSION_MAJOR 6 #define PROJ_VERSION_MINOR 2 -#define PROJ_VERSION_PATCH 0 +#define PROJ_VERSION_PATCH 1 extern char const PROJ_DLL pj_release[]; /* global release id string */ diff --git a/src/proj_api.h b/src/proj_api.h index b933fc6b..23eecfa7 100644 --- a/src/proj_api.h +++ b/src/proj_api.h @@ -38,7 +38,7 @@ #endif #ifndef PJ_VERSION -#define PJ_VERSION 620 +#define PJ_VERSION 621 #endif #ifdef PROJ_RENAME_SYMBOLS diff --git a/src/proj_internal.h b/src/proj_internal.h index 4a126e98..77c4ba17 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -926,6 +926,8 @@ std::string pj_double_quote_string_param_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); +void pj_pipeline_assign_context_to_steps( PJ* P, PJ_CONTEXT* ctx ); + /* classic public API */ #include "proj_api.h" diff --git a/src/projections/ob_tran.cpp b/src/projections/ob_tran.cpp index 4ae8dbe7..badc6dd8 100644 --- a/src/projections/ob_tran.cpp +++ b/src/projections/ob_tran.cpp @@ -31,8 +31,10 @@ static PJ_XY o_forward(PJ_LP lp, PJ *P) { /* spheroid */ coslam = cos(lp.lam); sinphi = sin(lp.phi); cosphi = cos(lp.phi); + /* Formula (5-8b) of Snyder's "Map projections: a working manual" */ lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam + Q->cphip * sinphi) + Q->lamp); + /* Formula (5-7) */ lp.phi = aasin(P->ctx,Q->sphip * sinphi - Q->cphip * cosphi * coslam); return Q->link->fwd(lp, Q->link); @@ -62,7 +64,9 @@ static PJ_LP o_inverse(PJ_XY xy, PJ *P) { /* spheroid */ coslam = cos(lp.lam -= Q->lamp); sinphi = sin(lp.phi); cosphi = cos(lp.phi); + /* Formula (5-9) */ lp.phi = aasin(P->ctx,Q->sphip * sinphi + Q->cphip * cosphi * coslam); + /* Formula (5-10b) */ lp.lam = aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam - Q->cphip * sinphi); } diff --git a/src/release.cpp b/src/release.cpp index 13ed45f0..7f62da3f 100644 --- a/src/release.cpp +++ b/src/release.cpp @@ -11,7 +11,7 @@ char const pj_release[] = STR(PROJ_VERSION_MAJOR)"." STR(PROJ_VERSION_MINOR)"." STR(PROJ_VERSION_PATCH)", " - "September 1st, 2019"; + "November 1st, 2019"; const char *pj_get_release() { return pj_release; |
