diff --git a/plugins/feature/map/czml.cpp b/plugins/feature/map/czml.cpp index fc1e42f63..ed865ebd5 100644 --- a/plugins/feature/map/czml.cpp +++ b/plugins/feature/map/czml.cpp @@ -21,7 +21,7 @@ #include "mapsettings.h" #include "mapmodel.h" -#include "util/units.h" +#include "util/coordinates.h" CZML::CZML(const MapSettings *settings) : m_settings(settings) @@ -48,149 +48,6 @@ QJsonObject CZML::init() return doc; } -// Convert a position specified in longitude, latitude in degrees and height in metres above WGS84 ellipsoid in to -// Earth Centered Earth Fixed frame cartesian coordinates -// See Cesium.Cartesian3.fromDegrees -QVector3D CZML::cartesian3FromDegrees(double longitude, double latitude, double height) const -{ - return cartesianFromRadians(Units::degreesToRadians(longitude), Units::degreesToRadians(latitude), height); -} - -// FIXME: QVector3D is only float! -// See Cesium.Cartesian3.fromRadians -QVector3D CZML::cartesianFromRadians(double longitude, double latitude, double height) const -{ - QVector3D wgs84RadiiSquared(6378137.0 * 6378137.0, 6378137.0 * 6378137.0, 6356752.3142451793 * 6356752.3142451793); - - double cosLatitude = cos(latitude); - QVector3D n; - n.setX(cosLatitude * cos(longitude)); - n.setY(cosLatitude * sin(longitude)); - n.setZ(sin(latitude)); - n.normalize(); - QVector3D k; - k = wgs84RadiiSquared * n; - double gamma = sqrt(QVector3D::dotProduct(n, k)); - k = k / gamma; - n = n * height; - return k + n; -} - -// Convert heading, pitch and roll in degrees to a quaternoin -// See: Cesium.Quaternion.fromHeadingPitchRoll -QQuaternion CZML::fromHeadingPitchRoll(double heading, double pitch, double roll) const -{ - QVector3D xAxis(1, 0, 0); - QVector3D yAxis(0, 1, 0); - QVector3D zAxis(0, 0, 1); - - QQuaternion rollQ = QQuaternion::fromAxisAndAngle(xAxis, roll); - - QQuaternion pitchQ = QQuaternion::fromAxisAndAngle(yAxis, -pitch); - - QQuaternion headingQ = QQuaternion::fromAxisAndAngle(zAxis, -heading); - - QQuaternion temp = rollQ * pitchQ; - - return headingQ * temp; -} - -// Calculate a transformation matrix from a East, North, Up frame at the given position to Earth Centered Earth Fixed frame -// See: Cesium.Transforms.eastNorthUpToFixedFrame -QMatrix4x4 CZML::eastNorthUpToFixedFrame(QVector3D origin) const -{ - // TODO: Handle special case at centre of earth and poles - QVector3D up = origin.normalized(); - QVector3D east(-origin.y(), origin.x(), 0.0); - east.normalize(); - QVector3D north = QVector3D::crossProduct(up, east); - QMatrix4x4 result( - east.x(), north.x(), up.x(), origin.x(), - east.y(), north.y(), up.y(), origin.y(), - east.z(), north.z(), up.z(), origin.z(), - 0.0, 0.0, 0.0, 1.0 - ); - return result; -} - -// Convert 3x3 rotation matrix to a quaternoin -// Although there is a method for this in Qt: QQuaternion::fromRotationMatrix, it seems to -// result in different signs, so the following is based on Cesium code -QQuaternion CZML::fromRotation(QMatrix3x3 mat) const -{ - QQuaternion q; - - double trace = mat(0, 0) + mat(1, 1) + mat(2, 2); - - if (trace > 0.0) - { - double root = sqrt(trace + 1.0); - q.setScalar(0.5 * root); - root = 0.5 / root; - - q.setX((mat(2,1) - mat(1,2)) * root); - q.setY((mat(0,2) - mat(2,0)) * root); - q.setZ((mat(1,0) - mat(0,1)) * root); - } - else - { - double next[] = {1, 2, 0}; - int i = 0; - if (mat(1,1) > mat(0,0)) { - i = 1; - } - if (mat(2,2) > mat(0,0) && mat(2,2) > mat(1,1)) { - i = 2; - } - int j = next[i]; - int k = next[j]; - - double root = sqrt(mat(i,i) - mat(j,j) - mat(k,k) + 1); - double quat[] = {0.0, 0.0, 0.0}; - quat[i] = 0.5 * root; - root = 0.5 / root; - - q.setScalar((mat(j,k) - mat(k,j)) * root); - quat[j] = (mat(i,j) + mat(j,i)) * root; - quat[k] = (mat(i,k) + mat(k,i)) * root; - q.setX(-quat[0]); - q.setY(-quat[1]); - q.setZ(-quat[2]); - } - return q; -} - -// Calculate orientation quaternion for a model (such as an aircraft) based on position and (HPR) heading, pitch and roll (in degrees) -// While Cesium supports specifying orientation as HPR, CZML doesn't currently. See https://github.com/CesiumGS/cesium/issues/5184 -// CZML requires the orientation to be in the Earth Centered Earth Fixed (geocentric) reference frame (https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates) -// The orientation therefore depends not only on HPR but also on position -// -// glTF uses a right-handed axis convention; that is, the cross product of right and forward yields up. glTF defines +Y as up, +Z as forward, and -X as right. -// Cesium.Quaternion.fromHeadingPitchRoll Heading is the rotation about the negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about the positive x axis. -QQuaternion CZML::orientation(double longitude, double latitude, double altitude, double heading, double pitch, double roll) const -{ - // Forward direction for gltf models in Cesium seems to be Eastward, rather than Northward, so we adjust heading by -90 degrees - heading = -90 + heading; - - // Convert position to Earth Centered Earth Fixed (ECEF) frame - QVector3D positionECEF = cartesian3FromDegrees(longitude, latitude, altitude); - - // Calculate matrix to transform from East, North, Up (ENU) frame to ECEF frame - QMatrix4x4 enuToECEFTransform = eastNorthUpToFixedFrame(positionECEF); - - // Calculate rotation based on HPR in ENU frame - QQuaternion hprENU = fromHeadingPitchRoll(heading, pitch, roll); - - // Transform rotation from ENU to ECEF - QMatrix3x3 hprENU3 = hprENU.toRotationMatrix(); - QMatrix4x4 hprENU4(hprENU3); - QMatrix4x4 transform = enuToECEFTransform * hprENU4; - - // Convert from 4x4 matrix to 3x3 matrix then to a quaternion - QQuaternion oq = fromRotation(transform.toGenericMatrix<3,3>()); - - return oq; -} QJsonObject CZML::update(MapItem *mapItem, bool isTarget, bool isSelected) { @@ -316,8 +173,8 @@ QJsonObject CZML::update(MapItem *mapItem, bool isTarget, bool isSelected) } } - QQuaternion q = orientation(mapItem->m_longitude, mapItem->m_latitude, mapItem->m_altitude, - mapItem->m_heading, mapItem->m_pitch, mapItem->m_roll); + QQuaternion q = Coordinates::orientation(mapItem->m_longitude, mapItem->m_latitude, mapItem->m_altitude, + mapItem->m_heading, mapItem->m_pitch, mapItem->m_roll); QJsonArray quaternion; if (!fixedPosition && mapItem->m_orientationDateTime.isValid()) { quaternion.push_back(mapItem->m_orientationDateTime.toString(Qt::ISODateWithMs)); diff --git a/plugins/feature/map/czml.h b/plugins/feature/map/czml.h index 6a3f5cd38..f97be9095 100644 --- a/plugins/feature/map/czml.h +++ b/plugins/feature/map/czml.h @@ -20,10 +20,6 @@ #include <QHash> #include <QJsonArray> -#include <QMatrix3x3> -#include <QMatrix4x4> -#include <QQuaternion> -#include <QVector3D> #include <QJsonObject> struct MapSettings; @@ -42,14 +38,6 @@ public: QJsonObject init(); QJsonObject update(MapItem *mapItem, bool isTarget, bool isSelected); -protected: - QVector3D cartesian3FromDegrees(double longitude, double latitude, double height=0.0) const; - QVector3D cartesianFromRadians(double longitude, double latitude, double height=0.0) const; - QQuaternion fromHeadingPitchRoll(double heading, double pitch, double roll) const; - QMatrix4x4 eastNorthUpToFixedFrame(QVector3D origin) const; - QQuaternion fromRotation(QMatrix3x3 mat) const; - QQuaternion orientation(double longitude, double latitude, double altitude, double heading, double pitch, double roll) const; - signals: void connected(); };