GNSSFormatConversions.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "GNSSFormatConversions.h"
  9. constexpr double earthSemimajorAxis = 6378137.0;
  10. constexpr double reciprocalFlattening = 1.0 / 298.257223563;
  11. constexpr double earthSemiminorAxis = earthSemimajorAxis * (1.0 - reciprocalFlattening);
  12. constexpr double firstEccentricitySquared = 2.0 * reciprocalFlattening - reciprocalFlattening * reciprocalFlattening;
  13. constexpr double secondEccentrictySquared =
  14. reciprocalFlattening * (2.0 - reciprocalFlattening) / ((1.0 - reciprocalFlattening) * (1.0 - reciprocalFlattening));
  15. // Based on http://wiki.gis.com/wiki/index.php/Geodetic_system
  16. namespace Georeferencing::Utils::GeodeticConversions
  17. {
  18. inline double DegToRad(double degrees)
  19. {
  20. return degrees * M_PI / 180.0;
  21. }
  22. inline double RadToDeg(double radians)
  23. {
  24. return radians * 180.0 / M_PI;
  25. }
  26. WGS::Vector3d WGS84ToECEF(const WGS::WGS84Coordinate& latitudeLongitudeAltitude)
  27. {
  28. const double latitudeRad = DegToRad(latitudeLongitudeAltitude.m_latitude);
  29. const double longitudeRad = DegToRad(latitudeLongitudeAltitude.m_longitude);
  30. const double altitude = latitudeLongitudeAltitude.m_altitude;
  31. const double helper = std::sqrt(1.0f - firstEccentricitySquared * std::sin(latitudeRad) * std::sin(latitudeRad));
  32. const double X = (earthSemimajorAxis / helper + altitude) * std::cos(latitudeRad) * std::cos(longitudeRad);
  33. const double Y = (earthSemimajorAxis / helper + altitude) * std::cos(latitudeRad) * std::sin(longitudeRad);
  34. const double Z = (earthSemimajorAxis * (1.0 - firstEccentricitySquared) / helper + altitude) * std::sin(latitudeRad);
  35. return { X, Y, Z };
  36. }
  37. WGS::Vector3d ECEFToENU(const WGS::WGS84Coordinate& referenceLatitudeLongitudeAltitude, const WGS::Vector3d& ECEFPoint)
  38. {
  39. const WGS::Vector3d referencePointInECEF = WGS84ToECEF(referenceLatitudeLongitudeAltitude);
  40. const WGS::Vector3d pointToReferencePointECEF = ECEFPoint - referencePointInECEF;
  41. const double referenceLatitudeRad = DegToRad(referenceLatitudeLongitudeAltitude.m_latitude);
  42. const double referenceLongitudeRad = DegToRad(referenceLatitudeLongitudeAltitude.m_longitude);
  43. return { -sin(referenceLongitudeRad) * pointToReferencePointECEF.m_x + cos(referenceLongitudeRad) * pointToReferencePointECEF.m_y,
  44. -sin(referenceLatitudeRad) * cos(referenceLongitudeRad) * pointToReferencePointECEF.m_x -
  45. sin(referenceLatitudeRad) * sin(referenceLongitudeRad) * pointToReferencePointECEF.m_y +
  46. cos(referenceLatitudeRad) * pointToReferencePointECEF.m_z,
  47. cos(referenceLatitudeRad) * cos(referenceLongitudeRad) * pointToReferencePointECEF.m_x +
  48. cos(referenceLatitudeRad) * sin(referenceLongitudeRad) * pointToReferencePointECEF.m_y +
  49. sin(referenceLatitudeRad) * pointToReferencePointECEF.m_z };
  50. }
  51. WGS::Vector3d ENUToECEF(const WGS::WGS84Coordinate& referenceLatitudeLongitudeAltitude, const WGS::Vector3d& ENUPoint)
  52. {
  53. const auto referenceECEF = WGS84ToECEF(referenceLatitudeLongitudeAltitude);
  54. const double referenceLatitudeRad = DegToRad(referenceLatitudeLongitudeAltitude.m_latitude);
  55. const double referenceLongitudeRad = DegToRad(referenceLatitudeLongitudeAltitude.m_longitude);
  56. const double& e = ENUPoint.m_x;
  57. const double& n = ENUPoint.m_y;
  58. const double& u = ENUPoint.m_z;
  59. return { -std::sin(referenceLongitudeRad) * e - std::cos(referenceLongitudeRad) * std::sin(referenceLatitudeRad) * n +
  60. std::cos(referenceLongitudeRad) * std::cos(referenceLatitudeRad) * u + referenceECEF.m_x,
  61. std::cos(referenceLongitudeRad) * e - std::sin(referenceLongitudeRad) * std::sin(referenceLatitudeRad) * n +
  62. std::cos(referenceLatitudeRad) * std::sin(referenceLongitudeRad) * u + referenceECEF.m_y,
  63. std::cos(referenceLatitudeRad) * n + std::sin(referenceLatitudeRad) * u + referenceECEF.m_z };
  64. }
  65. WGS::WGS84Coordinate ECEFToWGS84(const WGS::Vector3d& ECFEPoint)
  66. {
  67. const double& x = ECFEPoint.m_x;
  68. const double& y = ECFEPoint.m_y;
  69. const double& z = ECFEPoint.m_z;
  70. const double radiusSquared = x * x + y * y;
  71. const double radius = std::sqrt(radiusSquared);
  72. const double E2 = earthSemimajorAxis * earthSemimajorAxis - earthSemiminorAxis * earthSemiminorAxis;
  73. const double F = 54.0 * earthSemiminorAxis * earthSemiminorAxis * z * z;
  74. const double G = radiusSquared + (1.0 - firstEccentricitySquared) * z * z - firstEccentricitySquared * E2;
  75. const double c = (firstEccentricitySquared * firstEccentricitySquared * F * radiusSquared) / (G * G * G);
  76. const double s = std::pow(1. + c + std::sqrt(c * c + 2. * c), 1. / 3);
  77. const double P = F / (3.0 * (s + 1.0 / s + 1.0) * (s + 1.0 / s + 1.0) * G * G);
  78. const double Q = std::sqrt(1.0 + 2.0 * firstEccentricitySquared * firstEccentricitySquared * P);
  79. const double ro = -(firstEccentricitySquared * P * radius) / (1.0 + Q) +
  80. std::sqrt(
  81. (earthSemimajorAxis * earthSemimajorAxis / 2.0) * (1.0 + 1.0 / Q) -
  82. ((1.0 - firstEccentricitySquared) * P * z * z) / (Q * (1.0 + Q)) - P * radiusSquared / 2.0);
  83. const double tmp = (radius - firstEccentricitySquared * ro) * (radius - firstEccentricitySquared * ro);
  84. const double U = std::sqrt(tmp + z * z);
  85. const double V = std::sqrt(tmp + (1.0 - firstEccentricitySquared) * z * z);
  86. const double zo = (earthSemiminorAxis * earthSemiminorAxis * z) / (earthSemimajorAxis * V);
  87. const double latitude = std::atan((z + secondEccentrictySquared * zo) / radius);
  88. const double longitude = std::atan2(y, x);
  89. const double altitude = U * (1.0 - earthSemiminorAxis * earthSemiminorAxis / (earthSemimajorAxis * V));
  90. return { RadToDeg(latitude), RadToDeg(longitude), altitude };
  91. }
  92. } // namespace Georeferencing::Utils::GeodeticConversions