Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 134 additions & 16 deletions fpsdk_common/include/fpsdk_common/trafo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

/* LIBC/STL */
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>

/* EXTERNAL */
#include "fpsdk_common/ext/eigen_core.hpp"
Expand Down Expand Up @@ -211,9 +214,104 @@ Eigen::Vector3d LlhDegToRad(const Eigen::Vector3d& llh_deg);
Eigen::Vector3d LlhRadToDeg(const Eigen::Vector3d& llh_rad);

#if FPSDK_USE_PROJ || defined(_DOXYGEN_)
/**
* @brief Coordinate reference system (CRS) types
*
* (Like PROJ's PJ_TYPE_GEOCENTRIC_CRS etc., see their docu for details)
*/
enum class CrsType : int
{ // clang-format off
UNKNOWN = 0, //!< Unknown (or not initialised)
GEOCENTRIC = 1, //!< Geocentric (ECEF x/y/z), e.g. EPSG:4978
GEOGRAPHIC_2D = 2, //!< Geographic 2D (lat/lon), e.g. EPSG:4326
GEOGRAPHIC_3D = 3, //!< Geographic 3D (lat/lon/height), e.g. EPSG:4979
PROJECTED = 4, //!< Projected (east/north), e.g. EPSG:2056
VERTICAL = 5, //!< Vertical (height), e.g. EPSG:5703
COMPOUND = 6, //!< Compound (horizontal + vertical), e.g. EPSG:4258+5703
OTHER = 7, //!< Other type (engineering, temporal, ...)
}; // clang-format on

/**
* @brief Stringify CRS type
*
* @param[in] type The CRS type
*
* @returns a unique identifier string for the CRS type
*/
const char* CrsTypeStr(const CrsType type);

/**
* @brief Coordinate reference system (CRS) information
*/
struct CrsInfo
{
CrsType type_ = CrsType::UNKNOWN; //!< CRS type
std::string name_; //!< CRS name, e.g. "WGS 84"
std::vector<CrsType> sub_types_; //!< Types of the parts of a COMPOUND CRS, e.g. { GEOGRAPHIC_2D, VERTICAL }
};

/**
* @brief Transformer options
*
* The defaults select the most suitable operation PROJ can find, which may be an approximate ("ballpark") one.
*/
struct TransformerOpts
{
/**
* @brief Require a precise transformation
*
* Do not allow approximate ("ballpark") operations (ALLOW_BALLPARK=NO), fail if the best operation is not
* available, e.g. because of missing grids (ONLY_BEST=YES), and check that the operation can be instantiated.
*/
bool precise_ = false;

/**
* @brief Minimal accuracy [m] of the operation (ACCURACY=x), 0.0 = any accuracy
*
* Operations with unknown accuracy, such as ballpark operations, are not used if this is set.
*/
double min_accuracy_ = 0.0;

/**
* @brief Use "traditional GIS" axis order
*
* Use longitude/latitude (and east/north) order for all CRS, regardless of the axis order of the CRS (see
* proj_normalize_for_visualization()). For example, EPSG:4979 natively uses latitude/longitude order.
*/
bool normalize_axes_ = false;

/**
* @brief Allow network access
*
* Allow PROJ to download missing grids from the PROJ content delivery network (see
* https://proj.org/en/9.4/usage/network.html). This overrides the PROJ configuration (proj.ini, PROJ_NETWORK).
*/
bool allow_network_ = false;

/**
* @brief Area of interest (bounding box)
*/
struct Area
{
double west_ = 0.0; //!< Western-most longitude [deg]
double south_ = 0.0; //!< Southern-most latitude [deg]
double east_ = 0.0; //!< Eastern-most longitude [deg]
double north_ = 0.0; //!< Northern-most latitude [deg]
};

/**
* @brief Area of interest, used to select the operation
*
* If unset PROJ may select a different operation for each coordinate, depending on the coordinate's position.
*/
std::optional<Area> area_;
};

/**
* @brief "Universal" coordinate transformer, backed by PROJ
*
* All methods are thread-safe. Only one thread at a time can transform, other threads wait.
*
* @note This is only available if compiled with PROJ, see @ref FPSDK_BUILD_DEPS.
*/
class Transformer : private types::NoCopyNoMove
Expand All @@ -231,27 +329,42 @@ class Transformer : private types::NoCopyNoMove
/**
* @brief Initialise transformer
*
* The source and target CRS specification can be anything that proj_create_crs_to_crs() understands (see
* https://proj.org/en/9.4/development/reference/functions.html#c.proj_create_crs_to_crs). You may have to change
* the PROJ configuration (proj.ini) and/or update some PROJ data yourself to support some CRS. See the PROJ
* documentation for details (see https://proj.org/en/9.4/resource_files.html).
* The source and target CRS specification can be anything that proj_create() understands (see
* https://proj.org/en/9.4/development/reference/functions.html#c.proj_create). You may have to change the PROJ
* configuration (proj.ini) and/or update some PROJ data yourself to support some CRS. See the PROJ documentation
* for details (see https://proj.org/en/9.4/resource_files.html).
*
* @param[in] source_crs Source coordinate reference system, e.g. "EPSG:4326"
* @param[in] target_crs Target coordinate reference system, e.g. "EPSG:2056"
* @param[in] opts Options
*
* @returns true on success, false otherwise (bad params, missing data, ...)
* @returns true on success, false otherwise (bad params, missing data, already initialised, no PROJ, ...)
*/
bool Init(const std::string& source_crs, const std::string& target_crs);
bool Init(const std::string& source_crs, const std::string& target_crs, const TransformerOpts& opts = {});

/**
* @brief Get source CRS information
*
* @returns the source CRS information (type UNKNOWN if not initialised)
*/
CrsInfo SourceCrs() const;

/**
* @brief Get target CRS information
*
* @returns the target CRS information (type UNKNOWN if not initialised)
*/
CrsInfo TargetCrs() const;

/**
* @brief Transform coordinates
*
* @param[in,out] inout Coordinates to transform, replaced with result
* @param[in] inv Do the inverse transformation (true), default is forward (false)
*
* @returns true on success, false otherwise
* @returns true on success, false otherwise (not initialised, transformation failed)
*/
bool Transform(Eigen::Vector3d& inout, const bool inv = false);
bool Transform(Eigen::Vector3d& inout, const bool inv = false) const;

/**
* @brief Transform coordinates
Expand All @@ -260,9 +373,9 @@ class Transformer : private types::NoCopyNoMove
* @param[out] out Transformed coordinates
* @param[in] inv Do the inverse transformation (true), default is forward (false)
*
* @returns true on success, false otherwise
* @returns true on success, false otherwise (not initialised, transformation failed)
*/
bool Transform(const Eigen::Vector3d& in, Eigen::Vector3d& out, const bool inv = false);
bool Transform(const Eigen::Vector3d& in, Eigen::Vector3d& out, const bool inv = false) const;

/**
* @brief Transform coordinates (with time)
Expand All @@ -271,15 +384,20 @@ class Transformer : private types::NoCopyNoMove
* @param[out] out Transformed coordinates (with time)
* @param[in] inv Do the inverse transformation (true), default is forward (false)
*
* @returns true on success, false otherwise
* @returns true on success, false otherwise (not initialised, transformation failed)
*/
bool Transform(const Eigen::Vector4d& in, Eigen::Vector4d& out, const bool inv = false);
bool Transform(const Eigen::Vector4d& in, Eigen::Vector4d& out, const bool inv = false) const;

private:
const std::string name_; //!< Name, for debugging
bool pj_init_; //!< Proj is initialised
void* pj_ctx_; //!< Proj context
void* pj_tf_; //!< Proj transformation object
const std::string name_; //!< Name, for debugging
mutable std::mutex mutex_; //!< One thread at a time uses PROJ
bool pj_init_; //!< Proj is initialised
void* pj_ctx_; //!< Proj context
void* pj_tf_; //!< Proj transformation object
CrsInfo source_crs_; //!< Source CRS information
CrsInfo target_crs_; //!< Target CRS information

bool _Transform(const Eigen::Vector4d& in, Eigen::Vector4d& out, const bool inv) const;
};
#endif // FPSDK_USE_PROJ

Expand Down
Loading
Loading