diff options
Diffstat (limited to 'src/iso19111/c_api.cpp')
| -rw-r--r-- | src/iso19111/c_api.cpp | 275 |
1 files changed, 187 insertions, 88 deletions
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index b3f200fe..5d2216e8 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -30,10 +30,12 @@ #define FROM_PROJ_CPP #endif +#include <algorithm> #include <cassert> #include <cstdarg> #include <cstring> #include <map> +#include <memory> #include <new> #include <utility> #include <vector> @@ -77,6 +79,11 @@ static void PROJ_NO_INLINE proj_log_error(PJ_CONTEXT *ctx, const char *function, msg += ": "; msg += text; ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str()); + auto previous_errno = pj_ctx_get_errno(ctx); + if (previous_errno == 0) { + // only set errno if it wasn't set deeper down the call stack + pj_ctx_set_errno(ctx, PJD_ERR_GENERIC_ERROR); + } } // --------------------------------------------------------------------------- @@ -134,8 +141,7 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { auto formatter = PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, dbContext); auto projString = coordop->exportToPROJString(formatter.get()); - auto pj = pj_create_internal( - ctx, projString.empty() ? "+proj=affine" : projString.c_str()); + auto pj = pj_create_internal(ctx, projString.c_str()); if (pj) { pj->iso_obj = objIn; return pj; @@ -147,6 +153,7 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { } auto pj = pj_new(); if (pj) { + pj->ctx = ctx; pj->descr = "ISO-19111 object"; pj->iso_obj = objIn; } @@ -975,29 +982,18 @@ int proj_is_equivalent_to(const PJ *obj, const PJ *other, return false; } - // Make sure that the C and C++ enumerations match - static_assert(static_cast<int>(PJ_COMP_STRICT) == - static_cast<int>(IComparable::Criterion::STRICT), - ""); - static_assert(static_cast<int>(PJ_COMP_EQUIVALENT) == - static_cast<int>(IComparable::Criterion::EQUIVALENT), - ""); - static_assert( - static_cast<int>(PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) == - static_cast<int>( - IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS), - ""); + const auto cppCriterion = ([](PJ_COMPARISON_CRITERION l_criterion) { + switch (l_criterion) { + case PJ_COMP_STRICT: + return IComparable::Criterion::STRICT; + case PJ_COMP_EQUIVALENT: + return IComparable::Criterion::EQUIVALENT; + case PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS: + break; + } + return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS; + })(criterion); - // Make sure we enumerate all values. If adding a new value, as we - // don't have a default clause, the compiler will warn. - switch (criterion) { - case PJ_COMP_STRICT: - case PJ_COMP_EQUIVALENT: - case PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS: - break; - } - const IComparable::Criterion cppCriterion = - static_cast<IComparable::Criterion>(criterion); return obj->iso_obj->isEquivalentTo(other->iso_obj.get(), cppCriterion); } @@ -1121,40 +1117,24 @@ const char *proj_as_wkt(PJ_CONTEXT *ctx, const PJ *obj, PJ_WKT_TYPE type, return nullptr; } - // Make sure that the C and C++ enumerations match - static_assert(static_cast<int>(PJ_WKT2_2015) == - static_cast<int>(WKTFormatter::Convention::WKT2_2015), - ""); - static_assert( - static_cast<int>(PJ_WKT2_2015_SIMPLIFIED) == - static_cast<int>(WKTFormatter::Convention::WKT2_2015_SIMPLIFIED), - ""); - static_assert(static_cast<int>(PJ_WKT2_2018) == - static_cast<int>(WKTFormatter::Convention::WKT2_2018), - ""); - static_assert( - static_cast<int>(PJ_WKT2_2018_SIMPLIFIED) == - static_cast<int>(WKTFormatter::Convention::WKT2_2018_SIMPLIFIED), - ""); - static_assert(static_cast<int>(PJ_WKT1_GDAL) == - static_cast<int>(WKTFormatter::Convention::WKT1_GDAL), - ""); - static_assert(static_cast<int>(PJ_WKT1_ESRI) == - static_cast<int>(WKTFormatter::Convention::WKT1_ESRI), - ""); - // Make sure we enumerate all values. If adding a new value, as we - // don't have a default clause, the compiler will warn. - switch (type) { - case PJ_WKT2_2015: - case PJ_WKT2_2015_SIMPLIFIED: - case PJ_WKT2_2018: - case PJ_WKT2_2018_SIMPLIFIED: - case PJ_WKT1_GDAL: - case PJ_WKT1_ESRI: - break; - } - const WKTFormatter::Convention convention = - static_cast<WKTFormatter::Convention>(type); + const auto convention = ([](PJ_WKT_TYPE l_type) { + switch (l_type) { + case PJ_WKT2_2015: + return WKTFormatter::Convention::WKT2_2015; + case PJ_WKT2_2015_SIMPLIFIED: + return WKTFormatter::Convention::WKT2_2015_SIMPLIFIED; + case PJ_WKT2_2018: + return WKTFormatter::Convention::WKT2_2018; + case PJ_WKT2_2018_SIMPLIFIED: + return WKTFormatter::Convention::WKT2_2018_SIMPLIFIED; + case PJ_WKT1_GDAL: + return WKTFormatter::Convention::WKT1_GDAL; + case PJ_WKT1_ESRI: + break; + } + return WKTFormatter::Convention::WKT1_ESRI; + })(type); + try { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); auto formatter = WKTFormatter::create(convention, dbContext); @@ -1365,7 +1345,7 @@ static const GeodeticCRS *extractGeodeticCRS(PJ_CONTEXT *ctx, const PJ *crs, * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type CRS (must not be NULL) + * @param crs Object of type CRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ @@ -1389,7 +1369,7 @@ PJ *proj_crs_get_geodetic_crs(PJ_CONTEXT *ctx, const PJ *crs) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type CRS (must not be NULL) + * @param crs Object of type CRS (must not be NULL) * @param index Index of the CRS component (typically 0 = horizontal, 1 = * vertical) * @return Object that must be unreferenced with proj_destroy(), or NULL @@ -1471,7 +1451,7 @@ PJ *proj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ *base_crs, * osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible() * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type CRS (must not be NULL) + * @param crs Object of type CRS (must not be NULL) * @param options null-terminated list of options, or NULL. Currently * supported options are: * <ul> @@ -1530,7 +1510,7 @@ PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs, * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param obj Objet of type CRS or GeodeticReferenceFrame (must not be NULL) + * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ @@ -1562,7 +1542,7 @@ PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type CRS (must not be NULL) + * @param crs Object of type CRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ @@ -1645,7 +1625,7 @@ int proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, const PJ *ellipsoid, * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param obj Objet of type CRS or GeodeticReferenceFrame (must not be NULL) + * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ @@ -1720,7 +1700,7 @@ int proj_prime_meridian_get_parameters(PJ_CONTEXT *ctx, * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param obj Objet of type BoundCRS or CoordinateOperation (must not be NULL) + * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error, or missing source CRS. */ @@ -1763,7 +1743,7 @@ PJ *proj_get_source_crs(PJ_CONTEXT *ctx, const PJ *obj) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param obj Objet of type BoundCRS or CoordinateOperation (must not be NULL) + * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error, or missing target CRS. */ @@ -1797,7 +1777,7 @@ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) { /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are either hard-coded, or looked in the database when - * authorityFactory is not null. + * it is available. * * 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 @@ -1950,7 +1930,7 @@ void proj_string_list_destroy(PROJ_STRING_LIST list) { // --------------------------------------------------------------------------- -/** \brief Instanciate a default set of parameters to be used by +/** \brief Instantiate a default set of parameters to be used by * proj_get_crs_list(). * * @return a new object to free with proj_get_crs_list_parameters_destroy() */ @@ -1987,7 +1967,7 @@ void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) { * entry is NULL. This array should be freed with proj_crs_info_list_destroy() * * When no filter parameters are set, this is functionnaly equivalent to - * proj_get_crs_info_list_from_database(), instanciating a PJ* object for each + * proj_get_crs_info_list_from_database(), instantiating a PJ* object for each * of the proj_create_from_database() and retrieving information with the * various getters. However this function will be much faster. * @@ -2167,7 +2147,7 @@ void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type DerivedCRS or BoundCRSs (must not be NULL) + * @param crs Object of type DerivedCRS or BoundCRSs (must not be NULL) * @return Object of type SingleOperation that must be unreferenced with * proj_destroy(), or NULL in case of error. */ @@ -2198,7 +2178,7 @@ PJ *proj_crs_get_coordoperation(PJ_CONTEXT *ctx, const PJ *crs) { /** \brief Return information on the operation method of the SingleOperation. * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type SingleOperation (typically a Conversion + * @param coordoperation Object of type SingleOperation (typically a Conversion * or Transformation) (must not be NULL) * @param out_method_name Pointer to a string value to store the method * (projection) name. or NULL @@ -5729,12 +5709,12 @@ PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long, * available. * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type CoordinateOperation or derived classes + * @param coordoperation Object of type CoordinateOperation or derived classes * (must not be NULL) * @return TRUE or FALSE. */ -int proj_coordoperation_is_instanciable(PJ_CONTEXT *ctx, +int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx, const PJ *coordoperation) { assert(coordoperation); auto op = dynamic_cast<const CoordinateOperation *>( @@ -5746,7 +5726,7 @@ int proj_coordoperation_is_instanciable(PJ_CONTEXT *ctx, } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { - return op->isPROJInstanciable(dbContext) ? 1 : 0; + return op->isPROJInstantiable(dbContext) ? 1 : 0; } catch (const std::exception &) { return 0; } @@ -5754,10 +5734,40 @@ int proj_coordoperation_is_instanciable(PJ_CONTEXT *ctx, // --------------------------------------------------------------------------- +/** \brief Return whether a coordinate operation has a "ballpark" + * transformation, + * that is a very approximate one, due to lack of more accurate transformations. + * + * Typically a null geographic offset between two horizontal datum, or a + * null vertical offset (or limited to unit changes) between two vertical + * datum. Errors of several tens to one hundred meters might be expected, + * compared to more accurate transformations. + * + * @param ctx PROJ context, or NULL for default context + * @param coordoperation Object of type CoordinateOperation or derived classes + * (must not be NULL) + * @return TRUE or FALSE. + */ + +int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx, + const PJ *coordoperation) { + assert(coordoperation); + auto op = dynamic_cast<const CoordinateOperation *>( + coordoperation->iso_obj.get()); + if (!op) { + proj_log_error(ctx, __FUNCTION__, + "Object is not a CoordinateOperation"); + return 0; + } + return op->hasBallparkTransformation(); +} + +// --------------------------------------------------------------------------- + /** \brief Return the number of parameters of a SingleOperation * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type SingleOperation or derived classes + * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) */ @@ -5779,7 +5789,7 @@ int proj_coordoperation_get_param_count(PJ_CONTEXT *ctx, /** \brief Return the index of a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type SingleOperation or derived classes + * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param name Parameter name. Must not be NULL * @return index (>=0), or -1 in case of error. @@ -5812,7 +5822,7 @@ int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx, /** \brief Return a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type SingleOperation or derived classes + * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param index Parameter index. * @param out_name Pointer to a string value to store the parameter name. or @@ -5952,10 +5962,14 @@ int proj_coordoperation_get_param( * values. * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type Transformation, that can be represented + * @param coordoperation Object of type Transformation, that can be represented * as a WKT1 TOWGS84 node (must not be NULL) * @param out_values Pointer to an array of value_count double values. - * @param value_count Size of out_values array. + * @param value_count Size of out_values array. The suggested size is 7 to get + * translation, rotation and scale difference parameters. Rotation and scale + * difference terms might be zero if the transformation only includes + * translation + * parameters. In that case, value_count could be set to 3. * @param emit_error_if_incompatible Boolean to inicate if an error must be * logged if coordoperation is not compatible with a WKT1 TOWGS84 * representation. @@ -5997,7 +6011,7 @@ int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx, /** \brief Return the number of grids used by a CoordinateOperation * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type CoordinateOperation or derived classes + * @param coordoperation Object of type CoordinateOperation or derived classes * (must not be NULL) */ @@ -6033,7 +6047,7 @@ int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx, /** \brief Return a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context - * @param coordoperation Objet of type SingleOperation or derived classes + * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param index Parameter index. * @param out_short_name Pointer to a string value to store the grid short name. @@ -6508,7 +6522,7 @@ proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs, /** \brief Return the number of objects in the result set * - * @param result Objet of type PJ_OBJ_LIST (must not be NULL) + * @param result Object of type PJ_OBJ_LIST (must not be NULL) */ int proj_list_get_count(const PJ_OBJ_LIST *result) { assert(result); @@ -6524,7 +6538,7 @@ int proj_list_get_count(const PJ_OBJ_LIST *result) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param result Objet of type PJ_OBJ_LIST (must not be NULL) + * @param result Object of type PJ_OBJ_LIST (must not be NULL) * @param index Index * @return a new object that must be unreferenced with proj_destroy(), * or nullptr in case of error. @@ -6590,7 +6604,7 @@ double proj_coordoperation_get_accuracy(PJ_CONTEXT *ctx, * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type SingleCRS (must not be NULL) + * @param crs Object of type SingleCRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error (or if there is no datum) */ @@ -6618,7 +6632,7 @@ PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) { * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context - * @param crs Objet of type SingleCRS (must not be NULL) + * @param crs Object of type SingleCRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ @@ -6638,7 +6652,7 @@ PJ *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs) { /** \brief Returns the type of the coordinate system. * * @param ctx PROJ context, or NULL for default context - * @param cs Objet of type CoordinateSystem (must not be NULL) + * @param cs Object of type CoordinateSystem (must not be NULL) * @return type, or PJ_CS_TYPE_UNKNOWN in case of error. */ PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) { @@ -6684,7 +6698,7 @@ PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) { /** \brief Returns the number of axis of the coordinate system. * * @param ctx PROJ context, or NULL for default context - * @param cs Objet of type CoordinateSystem (must not be NULL) + * @param cs Object of type CoordinateSystem (must not be NULL) * @return number of axis, or -1 in case of error. */ int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) { @@ -6703,7 +6717,7 @@ int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) { /** \brief Returns information on an axis * * @param ctx PROJ context, or NULL for default context - * @param cs Objet of type CoordinateSystem (must not be NULL) + * @param cs Object of type CoordinateSystem (must not be NULL) * @param index Index of the coordinate system (between 0 and * proj_cs_get_axis_count() - 1) * @param out_name Pointer to a string value to store the axis name. or NULL @@ -6764,3 +6778,88 @@ int proj_cs_get_axis_info(PJ_CONTEXT *ctx, const PJ *cs, int index, } return true; } + +// --------------------------------------------------------------------------- + +/** \brief Returns a PJ* object whose axis order is the one expected for + * visualization purposes. + * + * The input object must be a coordinate operation, that has been created with + * proj_create_crs_to_crs(). + * If the axis order of its source or target CRS is northing,easting, then an + * axis swap operation will be inserted. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object of type CoordinateOperation, + * created with proj_create_crs_to_crs() (must not be NULL) + * @return a new PJ* object to free with proj_destroy() in case of success, or + * nullptr in case of error + */ +PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) { + + SANITIZE_CTX(ctx); + if (!obj->alternativeCoordinateOperations.empty()) { + try { + auto pjNew = std::unique_ptr<PJ>(pj_new()); + if (!pjNew) + return nullptr; + pjNew->ctx = ctx; + for (const auto &alt : obj->alternativeCoordinateOperations) { + auto co = dynamic_cast<const CoordinateOperation *>( + alt.pj->iso_obj.get()); + if (co) { + double minxSrc = alt.minxSrc; + double minySrc = alt.minySrc; + double maxxSrc = alt.maxxSrc; + double maxySrc = alt.maxySrc; + double minxDst = alt.minxDst; + double minyDst = alt.minyDst; + double maxxDst = alt.maxxDst; + double maxyDst = alt.maxyDst; + + auto l_sourceCRS = co->sourceCRS(); + auto l_targetCRS = co->targetCRS(); + if (l_sourceCRS && l_targetCRS) { + const bool swapSource = + l_sourceCRS + ->mustAxisOrderBeSwitchedForVisualization(); + if (swapSource) { + std::swap(minxSrc, minySrc); + std::swap(maxxSrc, maxySrc); + } + const bool swapTarget = + l_targetCRS + ->mustAxisOrderBeSwitchedForVisualization(); + if (swapTarget) { + std::swap(minxDst, minyDst); + std::swap(maxxDst, maxyDst); + } + } + pjNew->alternativeCoordinateOperations.emplace_back( + minxSrc, minySrc, maxxSrc, maxySrc, minxDst, minyDst, + maxxDst, maxyDst, + pj_obj_create(ctx, co->normalizeForVisualization()), + co->nameStr()); + } + } + return pjNew.release(); + } catch (const std::exception &e) { + proj_log_debug(ctx, __FUNCTION__, e.what()); + return nullptr; + } + } + + auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get()); + if (!co) { + proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation " + "created with " + "proj_create_crs_to_crs"); + return nullptr; + } + try { + return pj_obj_create(ctx, co->normalizeForVisualization()); + } catch (const std::exception &e) { + proj_log_debug(ctx, __FUNCTION__, e.what()); + return nullptr; + } +} |
