File indexing completed on 2024-05-05 03:49:52
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org> 0004 // SPDX-FileCopyrightText: 2007-2014 Torsten Rahn <rahn@kde.org> 0005 // SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de> 0006 // SPDX-FileCopyrightText: 2012 Cezar Mocan <mocancezar@gmail.com> 0007 // 0008 0009 // Local 0010 #include "SphericalProjection.h" 0011 #include "AbstractProjection_p.h" 0012 0013 #include "MarbleDebug.h" 0014 0015 // Marble 0016 #include "ViewportParams.h" 0017 #include "GeoDataPoint.h" 0018 #include "GeoDataLineString.h" 0019 #include "GeoDataCoordinates.h" 0020 #include "MarbleGlobal.h" 0021 #include "AzimuthalProjection_p.h" 0022 0023 #include <QIcon> 0024 0025 #define SAFE_DISTANCE 0026 0027 namespace Marble 0028 { 0029 0030 class SphericalProjectionPrivate : public AzimuthalProjectionPrivate 0031 { 0032 public: 0033 0034 explicit SphericalProjectionPrivate( SphericalProjection * parent ); 0035 0036 Q_DECLARE_PUBLIC( SphericalProjection ) 0037 }; 0038 0039 SphericalProjection::SphericalProjection() 0040 : AzimuthalProjection( new SphericalProjectionPrivate( this ) ) 0041 { 0042 setMinLat( minValidLat() ); 0043 setMaxLat( maxValidLat() ); 0044 } 0045 0046 SphericalProjection::SphericalProjection( SphericalProjectionPrivate *dd ) 0047 : AzimuthalProjection( dd ) 0048 { 0049 setMinLat( minValidLat() ); 0050 setMaxLat( maxValidLat() ); 0051 } 0052 0053 SphericalProjection::~SphericalProjection() 0054 { 0055 } 0056 0057 SphericalProjectionPrivate::SphericalProjectionPrivate( SphericalProjection * parent ) 0058 : AzimuthalProjectionPrivate( parent ) 0059 { 0060 } 0061 0062 QString SphericalProjection::name() const 0063 { 0064 return QObject::tr( "Globe" ); 0065 } 0066 0067 QString SphericalProjection::description() const 0068 { 0069 return QObject::tr( "<p><b>Orthographic Projection</b> (\"orthogonal\")</p><p>Applications: A perspective projection that is used to display the hemisphere of a globe as it appears from outer space.</p>" ); 0070 } 0071 0072 QIcon SphericalProjection::icon() const 0073 { 0074 return QIcon(QStringLiteral(":/icons/map-globe.png")); 0075 } 0076 0077 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0078 const ViewportParams *viewport, 0079 qreal &x, qreal &y, bool &globeHidesPoint ) const 0080 { 0081 const qreal altitude = coordinates.altitude(); 0082 const qreal absoluteAltitude = altitude + EARTH_RADIUS; 0083 Quaternion qpos = coordinates.quaternion(); 0084 0085 qpos.rotateAroundAxis( viewport->planetAxisMatrix() ); 0086 0087 const qreal radius = viewport->radius(); 0088 const qreal pixelAltitude = (radius / EARTH_RADIUS * absoluteAltitude); 0089 if (altitude < 10000) { 0090 // Skip placemarks at the other side of the earth. 0091 if ( qpos.v[Q_Z] < 0 ) { 0092 globeHidesPoint = true; 0093 return false; 0094 } 0095 } 0096 else { 0097 qreal earthCenteredX = pixelAltitude * qpos.v[Q_X]; 0098 qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y]; 0099 0100 // Don't draw high placemarks (e.g. satellites) that aren't visible. 0101 if ( qpos.v[Q_Z] < 0 0102 && ( ( earthCenteredX * earthCenteredX 0103 + earthCenteredY * earthCenteredY ) 0104 < radius * radius ) ) { 0105 globeHidesPoint = true; 0106 return false; 0107 } 0108 } 0109 0110 const qreal width = viewport->width(); 0111 const qreal height = viewport->height(); 0112 0113 // Let (x, y) be the position on the screen of the placemark.. 0114 x = (width / 2 + pixelAltitude * qpos.v[Q_X]); 0115 y = (height / 2 - pixelAltitude * qpos.v[Q_Y]); 0116 0117 // Skip placemarks that are outside the screen area 0118 if (x < 0 || x >= width || y < 0 || y >= height) { 0119 globeHidesPoint = false; 0120 return false; 0121 } 0122 0123 globeHidesPoint = false; 0124 return true; 0125 } 0126 0127 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0128 const ViewportParams *viewport, 0129 qreal *x, qreal &y, 0130 int &pointRepeatNum, 0131 const QSizeF& size, 0132 bool &globeHidesPoint ) const 0133 { 0134 pointRepeatNum = 0; 0135 bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); 0136 0137 // Skip placemarks that are outside the screen area 0138 if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 0139 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) 0140 { 0141 globeHidesPoint = false; 0142 return false; 0143 } 0144 0145 // This projection doesn't have any repetitions, 0146 // so the number of screen points referring to the geopoint is one. 0147 pointRepeatNum = 1; 0148 return visible; 0149 } 0150 0151 0152 bool SphericalProjection::geoCoordinates( const int x, const int y, 0153 const ViewportParams *viewport, 0154 qreal& lon, qreal& lat, 0155 GeoDataCoordinates::Unit unit ) const 0156 { 0157 const qreal inverseRadius = 1.0 / (qreal)(viewport->radius()); 0158 0159 const qreal qx = +(qreal)( x - viewport->width() / 2 ) * inverseRadius; 0160 const qreal qy = -(qreal)( y - viewport->height() / 2 ) * inverseRadius; 0161 0162 if ( 1 <= qx * qx + qy * qy ) { 0163 return false; 0164 } 0165 0166 const qreal qz = sqrt( 1 - qx * qx - qy * qy ); 0167 0168 Quaternion qpos( 0.0, qx, qy, qz ); 0169 qpos.rotateAroundAxis( viewport->planetAxis() ); 0170 qpos.getSpherical( lon, lat ); 0171 0172 if ( unit == GeoDataCoordinates::Degree ) { 0173 lon *= RAD2DEG; 0174 lat *= RAD2DEG; 0175 } 0176 0177 return true; 0178 } 0179 0180 }