File indexing completed on 2025-01-05 03:59:33
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 <QIcon> 0014 0015 #include <klocalizedstring.h> 0016 0017 // Marble 0018 #include "ViewportParams.h" 0019 #include "GeoDataPoint.h" 0020 #include "GeoDataLineString.h" 0021 #include "GeoDataCoordinates.h" 0022 #include "MarbleGlobal.h" 0023 #include "AzimuthalProjection_p.h" 0024 0025 #include "digikam_debug.h" 0026 0027 #define SAFE_DISTANCE 0028 0029 namespace Marble 0030 { 0031 0032 class SphericalProjectionPrivate : public AzimuthalProjectionPrivate 0033 { 0034 public: 0035 0036 explicit SphericalProjectionPrivate( SphericalProjection * parent ); 0037 0038 Q_DECLARE_PUBLIC( SphericalProjection ) 0039 }; 0040 0041 SphericalProjection::SphericalProjection() 0042 : AzimuthalProjection( new SphericalProjectionPrivate( this ) ) 0043 { 0044 setMinLat( minValidLat() ); 0045 setMaxLat( maxValidLat() ); 0046 } 0047 0048 SphericalProjection::SphericalProjection( SphericalProjectionPrivate *dd ) 0049 : AzimuthalProjection( dd ) 0050 { 0051 setMinLat( minValidLat() ); 0052 setMaxLat( maxValidLat() ); 0053 } 0054 0055 SphericalProjection::~SphericalProjection() 0056 { 0057 } 0058 0059 SphericalProjectionPrivate::SphericalProjectionPrivate( SphericalProjection * parent ) 0060 : AzimuthalProjectionPrivate( parent ) 0061 { 0062 } 0063 0064 QString SphericalProjection::name() const 0065 { 0066 return i18n( "Globe" ); 0067 } 0068 0069 QString SphericalProjection::description() const 0070 { 0071 return i18n( "<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>" ); 0072 } 0073 0074 QIcon SphericalProjection::icon() const 0075 { 0076 return QIcon::fromTheme(QStringLiteral("map-globe")); 0077 } 0078 0079 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0080 const ViewportParams *viewport, 0081 qreal &x, qreal &y, bool &globeHidesPoint ) const 0082 { 0083 const qreal altitude = coordinates.altitude(); 0084 const qreal absoluteAltitude = altitude + EARTH_RADIUS; 0085 Quaternion qpos = coordinates.quaternion(); 0086 0087 qpos.rotateAroundAxis( viewport->planetAxisMatrix() ); 0088 0089 const qreal radius = viewport->radius(); 0090 const qreal pixelAltitude = (radius / EARTH_RADIUS * absoluteAltitude); 0091 if (altitude < 10000) { 0092 // Skip placemarks at the other side of the earth. 0093 if ( qpos.v[Q_Z] < 0 ) { 0094 globeHidesPoint = true; 0095 return false; 0096 } 0097 } 0098 else { 0099 qreal earthCenteredX = pixelAltitude * qpos.v[Q_X]; 0100 qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y]; 0101 0102 // Don't draw high placemarks (e.g. satellites) that aren't visible. 0103 if ( qpos.v[Q_Z] < 0 0104 && ( ( earthCenteredX * earthCenteredX 0105 + earthCenteredY * earthCenteredY ) 0106 < radius * radius ) ) { 0107 globeHidesPoint = true; 0108 return false; 0109 } 0110 } 0111 0112 const qreal width = viewport->width(); 0113 const qreal height = viewport->height(); 0114 0115 // Let (x, y) be the position on the screen of the placemark.. 0116 x = (width / 2 + pixelAltitude * qpos.v[Q_X]); 0117 y = (height / 2 - pixelAltitude * qpos.v[Q_Y]); 0118 0119 // Skip placemarks that are outside the screen area 0120 if (x < 0 || x >= width || y < 0 || y >= height) { 0121 globeHidesPoint = false; 0122 return false; 0123 } 0124 0125 globeHidesPoint = false; 0126 return true; 0127 } 0128 0129 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0130 const ViewportParams *viewport, 0131 qreal *x, qreal &y, 0132 int &pointRepeatNum, 0133 const QSizeF& size, 0134 bool &globeHidesPoint ) const 0135 { 0136 pointRepeatNum = 0; 0137 bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); 0138 0139 // Skip placemarks that are outside the screen area 0140 if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 0141 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) 0142 { 0143 globeHidesPoint = false; 0144 return false; 0145 } 0146 0147 // This projection doesn't have any repetitions, 0148 // so the number of screen points referring to the geopoint is one. 0149 pointRepeatNum = 1; 0150 return visible; 0151 } 0152 0153 0154 bool SphericalProjection::geoCoordinates( const int x, const int y, 0155 const ViewportParams *viewport, 0156 qreal& lon, qreal& lat, 0157 GeoDataCoordinates::Unit unit ) const 0158 { 0159 const qreal inverseRadius = 1.0 / (qreal)(viewport->radius()); 0160 0161 const qreal qx = +(qreal)( x - viewport->width() / 2 ) * inverseRadius; 0162 const qreal qy = -(qreal)( y - viewport->height() / 2 ) * inverseRadius; 0163 0164 if ( 1 <= qx * qx + qy * qy ) { 0165 return false; 0166 } 0167 0168 const qreal qz = sqrt( 1 - qx * qx - qy * qy ); 0169 0170 Quaternion qpos( 0.0, qx, qy, qz ); 0171 qpos.rotateAroundAxis( viewport->planetAxis() ); 0172 qpos.getSpherical( lon, lat ); 0173 0174 if ( unit == GeoDataCoordinates::Degree ) { 0175 lon *= RAD2DEG; 0176 lat *= RAD2DEG; 0177 } 0178 0179 return true; 0180 } 0181 0182 }