Fixposition SDK 0.0.0-heads/main-0-ga33788a
Collection of c++ libraries and apps for use with Fixposition products on Linux
Loading...
Searching...
No Matches
trafo.hpp
Go to the documentation of this file.
1/**
2 * \verbatim
3 * ___ ___
4 * \ \ / /
5 * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors
6 * / /\ \ License: see the LICENSE file
7 * /__/ \__\
8 * \endverbatim
9 *
10 * @file
11 * @brief Fixposition SDK: Transformation utilities
12 *
13 * @page FPSDK_COMMON_TRAFO Transformation utilities
14 *
15 * **API**: fpsdk_common/trafo.hpp and fpsdk::common::trafo
16 *
17 */
18#ifndef __FPSDK_COMMON_TRAFO_HPP__
19#define __FPSDK_COMMON_TRAFO_HPP__
20
21/* LIBC/STL */
22#include <memory>
23#include <mutex>
24#include <optional>
25#include <string>
26#include <vector>
27
28/* EXTERNAL */
29#include "fpsdk_common/ext/eigen_core.hpp"
30#include "fpsdk_common/ext/eigen_geometry.hpp"
31
32/* PACKAGE */
33#include "types.hpp"
34
35namespace fpsdk {
36namespace common {
37/**
38 * @brief Transformation utilities
39 */
40namespace trafo {
41/* ****************************************************************************************************************** */
42
43/**
44 * @name WGS-84 constants
45 *
46 * https://en.wikipedia.org/wiki/World_Geodetic_System#Defining_Parameters
47 *
48 * @{
49 */
50static constexpr double WGS84_A = 6378137.0; //!< WGS-84 Earth radius major axis [m]
51static constexpr double WGS84_B = 6356752.314245; //!< WGS-84 Earth radius minor axis [m]
52static constexpr double WGS84_C = 40075016.6855785; //!< Circumference at equator (= 2π * WGS84_A) [m]
53static constexpr double WGS84_1F = 298.257223563; //!< WGS-84 1/f inverse of flattening parameter
54static constexpr double WGS84_E2 = 6.69437999014e-3; //!< WGS-84 first eccentricity squared
55static constexpr double WGS84_A2 = WGS84_A * WGS84_A; //!< WGS-84 a^2
56static constexpr double WGS84_B2 = WGS84_B * WGS84_B; //!< WGS-84 b^2
57static constexpr double WGS84_EE2 = (WGS84_A2 / WGS84_B2) - 1.0; //!< WGS-84 e'^2 second eccentricity squared
58///@}
59
60/**
61 * @brief Calculate the rotation matrix from ECEF to ENU for a given reference latitude/longitude
62 *
63 * @param[in] lat Reference latitude [rad]
64 * @param[in] lon Reference longitude [rad]
65 *
66 * @returns the rotation matrix from ECEF to ENU
67 *
68 * Reference https://gssc.esa.int/navipedia/index.php/Transformations_between_ECEF_and_ENU_coordinates
69 */
70Eigen::Matrix3d RotEnuEcef(const double lat, const double lon);
71
72/**
73 * @brief Calculate the rotation matrix from ECEF to ENU for a given reference position
74 *
75 * @note The reference position is projected to the WGS84 ellipsoid
76 *
77 * @param[in] ecef Reference position in ECEF [m]
78 *
79 * @returns the rotation matrix from ECEF to ENU
80 */
81Eigen::Matrix3d RotEnuEcef(const Eigen::Vector3d& ecef);
82
83/**
84 * @brief Returns rotation matrix between NED and ENU
85 *
86 * | 0, 1, 0 |
87 * | 1, 0, 0 |
88 * | 0, 0, -1 |
89 *
90 * @returns the rotation matrix between NED and ENU
91 */
92Eigen::Matrix3d RotNedEnu();
93
94/**
95 * @brief Calculate the rotation matrix from ECEF to NED for a given reference latitude/longitude
96 *
97 * @param[in] lat Reference latitude [rad]
98 * @param[in] lon Reference longitude [rad]
99 *
100 * @returns the rotation matrix from ECEF to NED
101 */
102Eigen::Matrix3d RotNedEcef(const double lat, const double lon);
103
104/**
105 * @brief Calculate the rotation matrix from ECEF to NED for a given reference origin
106 *
107 * @note The reference position is projected to the WGS84 ellipsoid
108 *
109 * @param[in] ecef Reference position in ECEF [m]
110 *
111 * @returns the rRotation matrix from ECEF to NED
112 */
113Eigen::Matrix3d RotNedEcef(const Eigen::Vector3d& ecef);
114
115/**
116 * @brief Transform ECEF coordinate to ENU with specified ENU-origin
117 *
118 * @param[in] ecef ECEF position to be transformed [m]
119 * @param[in] wgs84llh_ref ENU-origin in geodetic coordinates (lat [rad], lon [rad], height [m])
120 *
121 * @returns the position in ENU coordinates
122 */
123Eigen::Vector3d TfEnuEcef(const Eigen::Vector3d& ecef, const Eigen::Vector3d& wgs84llh_ref);
124
125/**
126 * @brief Transform ENU coordinate to ECEF with specified ENU-origin
127 *
128 * @param[in] enu ENU position to be transformed [m]
129 * @param[in] wgs84llh_ref ENU-origin in geodetic coordinates (lat [rad], lon [rad], height [m])
130 *
131 * @returns @todo documentation
132 */
133Eigen::Vector3d TfEcefEnu(const Eigen::Vector3d& enu, const Eigen::Vector3d& wgs84llh_ref);
134
135/**
136 * @brief Convert ECEF coordinates to ENU with specified ENU origin
137 *
138 * @param[in] ecef ECEF position to be converted
139 * @param[in] wgs84llh_ref ENU-origin input given in geodetic coordinates
140 *
141 * @returns the position in ENU coordinates
142 */
143Eigen::Vector3d TfNedEcef(const Eigen::Vector3d& ecef, const Eigen::Vector3d& wgs84llh_ref);
144
145/**
146 * @todo documentation
147 */
148Eigen::Vector3d TfEcefNed(const Eigen::Vector3d& ned, const Eigen::Vector3d& wgs84llh_ref);
149
150/**
151 * @brief Convert geodetic coordinates (latitude, longitude, height) to ECEF (x, y, z).
152 *
153 * @param[in] wgs84llh Geodetic coordinates (lat [rad], lon [rad], height [m])
154 *
155 * @returns the ECEF coordinates [m]
156 */
157Eigen::Vector3d TfEcefWgs84Llh(const Eigen::Vector3d& wgs84llh);
158
159/**
160 * @brief Convert ECEF (x, y, z) coordinates to geodetic coordinates (latitude, longitude, height)
161 *
162 * @param[in] ecef ECEF coordinates [m]
163 *
164 * @returns the geodetic coordinates (lat [rad], lon [rad], height [m])
165 */
166Eigen::Vector3d TfWgs84LlhEcef(const Eigen::Vector3d& ecef);
167
168/**
169 * @brief Calculate yaw, pitch and roll in ENU from a given pose in ECEF
170 *
171 * Yaw is -Pi/2 when X points North, because ENU starts with East
172 *
173 * @param[in] ecef_p 3D position vector in ECEF
174 * @param[in] ecef_r 3x3 rotation matrix representing rotation from body to ECEF
175 *
176 * @returns yaw, pitch and roll in ENU
177 */
178Eigen::Vector3d EcefPoseToEnuEul(const Eigen::Vector3d& ecef_p, const Eigen::Matrix3d& ecef_r);
179
180/**
181 * @brief Vector4 quaternion to intrinsic Euler angles in ZYX (yaw, pitch, roll)
182 *
183 * @param[in] quat Quaternion (w, i, j, k)
184 *
185 * @returns the intrinsic Euler angles in ZYX (yaw, pitch, roll) order
186 */
187Eigen::Vector3d QuatToEul(const Eigen::Quaterniond& quat);
188
189/**
190 * @brief Rotation Matrix to intrinsic Euler angles in ZYX (yaw, pitch, roll) order in radian
191 *
192 * @param[in] rot Eigen::Matrix3d
193 *
194 * @returns intrinsic euler angle in ZYX (Yaw-Pitch-Roll) order in radian
195 */
196Eigen::Vector3d RotToEul(const Eigen::Matrix3d& rot);
197
198/**
199 * @brief Convert llh from deg to rad, height component unchanged
200 *
201 * @param[in] llh_deg llh in degrees
202 *
203 * @returns llh in radian
204 */
205Eigen::Vector3d LlhDegToRad(const Eigen::Vector3d& llh_deg);
206
207/**
208 * @brief Convert llh from rad to deg, height component unchanged
209 *
210 * @param[in] llh_rad llh in radian
211 *
212 * @returns llh in degrees
213 */
214Eigen::Vector3d LlhRadToDeg(const Eigen::Vector3d& llh_rad);
215
216#if FPSDK_USE_PROJ || defined(_DOXYGEN_)
217/**
218 * @brief Coordinate reference system (CRS) types
219 *
220 * (Like PROJ's PJ_TYPE_GEOCENTRIC_CRS etc., see their docu for details)
221 */
222enum class CrsType : int
223{ // clang-format off
224 UNKNOWN = 0, //!< Unknown (or not initialised)
225 GEOCENTRIC = 1, //!< Geocentric (ECEF x/y/z), e.g. EPSG:4978
226 GEOGRAPHIC_2D = 2, //!< Geographic 2D (lat/lon), e.g. EPSG:4326
227 GEOGRAPHIC_3D = 3, //!< Geographic 3D (lat/lon/height), e.g. EPSG:4979
228 PROJECTED = 4, //!< Projected (east/north), e.g. EPSG:2056
229 VERTICAL = 5, //!< Vertical (height), e.g. EPSG:5703
230 COMPOUND = 6, //!< Compound (horizontal + vertical), e.g. EPSG:4258+5703
231 OTHER = 7, //!< Other type (engineering, temporal, ...)
232}; // clang-format on
233
234/**
235 * @brief Stringify CRS type
236 *
237 * @param[in] type The CRS type
238 *
239 * @returns a unique identifier string for the CRS type
240 */
241const char* CrsTypeStr(const CrsType type);
242
243/**
244 * @brief Coordinate reference system (CRS) information
245 */
247{
249 std::string name_; //!< CRS name, e.g. "WGS 84"
250 std::vector<CrsType> sub_types_; //!< Types of the parts of a COMPOUND CRS, e.g. { GEOGRAPHIC_2D, VERTICAL }
251};
252
253/**
254 * @brief Transformer options
255 *
256 * The defaults select the most suitable operation PROJ can find, which may be an approximate ("ballpark") one.
257 */
259{
260 /**
261 * @brief Require a precise transformation
262 *
263 * Do not allow approximate ("ballpark") operations (ALLOW_BALLPARK=NO), fail if the best operation is not
264 * available, e.g. because of missing grids (ONLY_BEST=YES), and check that the operation can be instantiated.
265 */
266 bool precise_ = false;
267
268 /**
269 * @brief Minimal accuracy [m] of the operation (ACCURACY=x), 0.0 = any accuracy
270 *
271 * Operations with unknown accuracy, such as ballpark operations, are not used if this is set.
272 */
273 double min_accuracy_ = 0.0;
274
275 /**
276 * @brief Use "traditional GIS" axis order
277 *
278 * Use longitude/latitude (and east/north) order for all CRS, regardless of the axis order of the CRS (see
279 * proj_normalize_for_visualization()). For example, EPSG:4979 natively uses latitude/longitude order.
280 */
281 bool normalize_axes_ = false;
282
283 /**
284 * @brief Allow network access
285 *
286 * Allow PROJ to download missing grids from the PROJ content delivery network (see
287 * https://proj.org/en/9.4/usage/network.html). This overrides the PROJ configuration (proj.ini, PROJ_NETWORK).
288 */
289 bool allow_network_ = false;
290
291 /**
292 * @brief Area of interest (bounding box)
293 */
294 struct Area
295 {
296 double west_ = 0.0; //!< Western-most longitude [deg]
297 double south_ = 0.0; //!< Southern-most latitude [deg]
298 double east_ = 0.0; //!< Eastern-most longitude [deg]
299 double north_ = 0.0; //!< Northern-most latitude [deg]
300 };
301
302 /**
303 * @brief Area of interest, used to select the operation
304 *
305 * If unset PROJ may select a different operation for each coordinate, depending on the coordinate's position.
306 */
307 std::optional<Area> area_;
308};
309
310/**
311 * @brief "Universal" coordinate transformer, backed by PROJ
312 *
313 * All methods are thread-safe. Only one thread at a time can transform, other threads wait.
314 *
315 * @note This is only available if compiled with PROJ, see @ref FPSDK_BUILD_DEPS.
316 */
318{
319 public:
320 /**
321 * @brief Constructor
322 *
323 * @param[in] name Optional name, for debugging
324 */
325 explicit Transformer(const std::string& name = "");
326
327 ~Transformer();
328
329 /**
330 * @brief Initialise transformer
331 *
332 * The source and target CRS specification can be anything that proj_create() understands (see
333 * https://proj.org/en/9.4/development/reference/functions.html#c.proj_create). You may have to change the PROJ
334 * configuration (proj.ini) and/or update some PROJ data yourself to support some CRS. See the PROJ documentation
335 * for details (see https://proj.org/en/9.4/resource_files.html).
336 *
337 * @param[in] source_crs Source coordinate reference system, e.g. "EPSG:4326"
338 * @param[in] target_crs Target coordinate reference system, e.g. "EPSG:2056"
339 * @param[in] opts Options
340 *
341 * @returns true on success, false otherwise (bad params, missing data, already initialised, no PROJ, ...)
342 */
343 bool Init(const std::string& source_crs, const std::string& target_crs, const TransformerOpts& opts = {});
344
345 /**
346 * @brief Get source CRS information
347 *
348 * @returns the source CRS information (type UNKNOWN if not initialised)
349 */
351
352 /**
353 * @brief Get target CRS information
354 *
355 * @returns the target CRS information (type UNKNOWN if not initialised)
356 */
358
359 /**
360 * @brief Transform coordinates
361 *
362 * @param[in,out] inout Coordinates to transform, replaced with result
363 * @param[in] inv Do the inverse transformation (true), default is forward (false)
364 *
365 * @returns true on success, false otherwise (not initialised, transformation failed)
366 */
367 bool Transform(Eigen::Vector3d& inout, const bool inv = false) const;
368
369 /**
370 * @brief Transform coordinates
371 *
372 * @param[in] in Coordinates to transform
373 * @param[out] out Transformed coordinates
374 * @param[in] inv Do the inverse transformation (true), default is forward (false)
375 *
376 * @returns true on success, false otherwise (not initialised, transformation failed)
377 */
378 bool Transform(const Eigen::Vector3d& in, Eigen::Vector3d& out, const bool inv = false) const;
379
380 /**
381 * @brief Transform coordinates (with time)
382 *
383 * @param[in] in Coordinates to transform (with time)
384 * @param[out] out Transformed coordinates (with time)
385 * @param[in] inv Do the inverse transformation (true), default is forward (false)
386 *
387 * @returns true on success, false otherwise (not initialised, transformation failed)
388 */
389 bool Transform(const Eigen::Vector4d& in, Eigen::Vector4d& out, const bool inv = false) const;
390
391 private:
392 const std::string name_; //!< Name, for debugging
393 mutable std::mutex mutex_; //!< One thread at a time uses PROJ
394 bool pj_init_; //!< Proj is initialised
395 void* pj_ctx_; //!< Proj context
396 void* pj_tf_; //!< Proj transformation object
397 CrsInfo source_crs_; //!< Source CRS information
398 CrsInfo target_crs_; //!< Target CRS information
399
400 bool _Transform(const Eigen::Vector4d& in, Eigen::Vector4d& out, const bool inv) const;
401};
402#endif // FPSDK_USE_PROJ
403
404/* ****************************************************************************************************************** */
405} // namespace trafo
406} // namespace common
407} // namespace fpsdk
408#endif // __FPSDK_COMMON_TRAFO_HPP__
Transformer(const std::string &name="")
Constructor.
bool Transform(const Eigen::Vector3d &in, Eigen::Vector3d &out, const bool inv=false) const
Transform coordinates.
bool Transform(Eigen::Vector3d &inout, const bool inv=false) const
Transform coordinates.
bool Transform(const Eigen::Vector4d &in, Eigen::Vector4d &out, const bool inv=false) const
Transform coordinates (with time).
CrsInfo TargetCrs() const
Get target CRS information.
bool Init(const std::string &source_crs, const std::string &target_crs, const TransformerOpts &opts={})
Initialise transformer.
CrsInfo SourceCrs() const
Get source CRS information.
Base class to prevent copy or move.
Definition types.hpp:97
Transformation utilities.
Definition trafo.hpp:40
Eigen::Matrix3d RotEnuEcef(const double lat, const double lon)
Calculate the rotation matrix from ECEF to ENU for a given reference latitude/longitude.
Eigen::Matrix3d RotNedEcef(const double lat, const double lon)
Calculate the rotation matrix from ECEF to NED for a given reference latitude/longitude.
Eigen::Vector3d LlhDegToRad(const Eigen::Vector3d &llh_deg)
Convert llh from deg to rad, height component unchanged.
const char * CrsTypeStr(const CrsType type)
Stringify CRS type.
Eigen::Vector3d LlhRadToDeg(const Eigen::Vector3d &llh_rad)
Convert llh from rad to deg, height component unchanged.
static constexpr double WGS84_1F
WGS-84 1/f inverse of flattening parameter.
Definition trafo.hpp:53
Eigen::Vector3d TfEcefNed(const Eigen::Vector3d &ned, const Eigen::Vector3d &wgs84llh_ref)
static constexpr double WGS84_B2
WGS-84 b^2.
Definition trafo.hpp:56
Eigen::Vector3d TfNedEcef(const Eigen::Vector3d &ecef, const Eigen::Vector3d &wgs84llh_ref)
Convert ECEF coordinates to ENU with specified ENU origin.
static constexpr double WGS84_E2
WGS-84 first eccentricity squared.
Definition trafo.hpp:54
Eigen::Vector3d QuatToEul(const Eigen::Quaterniond &quat)
Vector4 quaternion to intrinsic Euler angles in ZYX (yaw, pitch, roll).
static constexpr double WGS84_C
Circumference at equator (= 2π * WGS84_A) [m].
Definition trafo.hpp:52
Eigen::Vector3d RotToEul(const Eigen::Matrix3d &rot)
Rotation Matrix to intrinsic Euler angles in ZYX (yaw, pitch, roll) order in radian.
static constexpr double WGS84_B
WGS-84 Earth radius minor axis [m].
Definition trafo.hpp:51
Eigen::Matrix3d RotNedEnu()
Returns rotation matrix between NED and ENU.
Eigen::Vector3d TfWgs84LlhEcef(const Eigen::Vector3d &ecef)
Convert ECEF (x, y, z) coordinates to geodetic coordinates (latitude, longitude, height).
Eigen::Vector3d TfEcefEnu(const Eigen::Vector3d &enu, const Eigen::Vector3d &wgs84llh_ref)
Transform ENU coordinate to ECEF with specified ENU-origin.
CrsType
Coordinate reference system (CRS) types.
Definition trafo.hpp:223
@ OTHER
Other type (engineering, temporal, ...).
Definition trafo.hpp:231
@ GEOGRAPHIC_2D
Geographic 2D (lat/lon), e.g. EPSG:4326.
Definition trafo.hpp:226
@ VERTICAL
Vertical (height), e.g. EPSG:5703.
Definition trafo.hpp:229
@ UNKNOWN
Unknown (or not initialised).
Definition trafo.hpp:224
@ COMPOUND
Compound (horizontal + vertical), e.g. EPSG:4258+5703.
Definition trafo.hpp:230
@ GEOCENTRIC
Geocentric (ECEF x/y/z), e.g. EPSG:4978.
Definition trafo.hpp:225
@ GEOGRAPHIC_3D
Geographic 3D (lat/lon/height), e.g. EPSG:4979.
Definition trafo.hpp:227
@ PROJECTED
Projected (east/north), e.g. EPSG:2056.
Definition trafo.hpp:228
static constexpr double WGS84_A
WGS-84 Earth radius major axis [m].
Definition trafo.hpp:50
Eigen::Vector3d EcefPoseToEnuEul(const Eigen::Vector3d &ecef_p, const Eigen::Matrix3d &ecef_r)
Calculate yaw, pitch and roll in ENU from a given pose in ECEF.
Eigen::Vector3d TfEnuEcef(const Eigen::Vector3d &ecef, const Eigen::Vector3d &wgs84llh_ref)
Transform ECEF coordinate to ENU with specified ENU-origin.
Eigen::Vector3d TfEcefWgs84Llh(const Eigen::Vector3d &wgs84llh)
Convert geodetic coordinates (latitude, longitude, height) to ECEF (x, y, z).
static constexpr double WGS84_A2
WGS-84 a^2.
Definition trafo.hpp:55
static constexpr double WGS84_EE2
WGS-84 e'^2 second eccentricity squared.
Definition trafo.hpp:57
Fixposition SDK: Common library.
Definition doc.hpp:21
Fixposition SDK.
Coordinate reference system (CRS) information.
Definition trafo.hpp:247
CrsType type_
CRS type.
Definition trafo.hpp:248
std::string name_
CRS name, e.g. "WGS 84".
Definition trafo.hpp:249
std::vector< CrsType > sub_types_
Types of the parts of a COMPOUND CRS, e.g. { GEOGRAPHIC_2D, VERTICAL }.
Definition trafo.hpp:250
Area of interest (bounding box).
Definition trafo.hpp:295
double north_
Northern-most latitude [deg].
Definition trafo.hpp:299
double east_
Eastern-most longitude [deg].
Definition trafo.hpp:298
double south_
Southern-most latitude [deg].
Definition trafo.hpp:297
double west_
Western-most longitude [deg].
Definition trafo.hpp:296
std::optional< Area > area_
Area of interest, used to select the operation.
Definition trafo.hpp:307
double min_accuracy_
Minimal accuracy [m] of the operation (ACCURACY=x), 0.0 = any accuracy.
Definition trafo.hpp:273
bool normalize_axes_
Use "traditional GIS" axis order.
Definition trafo.hpp:281
bool allow_network_
Allow network access.
Definition trafo.hpp:289
bool precise_
Require a precise transformation.
Definition trafo.hpp:266
Fixposition SDK: Common types and type helpers.