File indexing completed on 2024-05-05 03:49:53
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2014 Torsten Rahn <rahn@kde.org> 0004 // 0005 0006 // Local 0007 #include "VerticalPerspectiveProjection.h" 0008 #include "AbstractProjection_p.h" 0009 0010 #include "MarbleDebug.h" 0011 0012 // Marble 0013 #include "ViewportParams.h" 0014 #include "GeoDataPoint.h" 0015 #include "GeoDataLineString.h" 0016 #include "GeoDataCoordinates.h" 0017 #include "MarbleGlobal.h" 0018 #include "AzimuthalProjection_p.h" 0019 0020 #include <QIcon> 0021 #include <qmath.h> 0022 0023 #define SAFE_DISTANCE 0024 0025 namespace Marble 0026 { 0027 0028 class VerticalPerspectiveProjectionPrivate : public AzimuthalProjectionPrivate 0029 { 0030 public: 0031 explicit VerticalPerspectiveProjectionPrivate( VerticalPerspectiveProjection * parent ); 0032 0033 void calculateConstants(qreal radius) const; 0034 0035 mutable qreal m_P; ///< Distance of the point of perspective in earth diameters 0036 mutable qreal m_previousRadius; 0037 mutable qreal m_altitudeToPixel; 0038 mutable qreal m_perspectiveRadius; 0039 mutable qreal m_pPfactor; 0040 0041 Q_DECLARE_PUBLIC( VerticalPerspectiveProjection ) 0042 }; 0043 0044 VerticalPerspectiveProjection::VerticalPerspectiveProjection() 0045 : AzimuthalProjection( new VerticalPerspectiveProjectionPrivate( this ) ) 0046 { 0047 setMinLat( minValidLat() ); 0048 setMaxLat( maxValidLat() ); 0049 } 0050 0051 VerticalPerspectiveProjection::VerticalPerspectiveProjection( VerticalPerspectiveProjectionPrivate *dd ) 0052 : AzimuthalProjection( dd ) 0053 { 0054 setMinLat( minValidLat() ); 0055 setMaxLat( maxValidLat() ); 0056 } 0057 0058 VerticalPerspectiveProjection::~VerticalPerspectiveProjection() 0059 { 0060 } 0061 0062 0063 VerticalPerspectiveProjectionPrivate::VerticalPerspectiveProjectionPrivate( VerticalPerspectiveProjection * parent ) 0064 : AzimuthalProjectionPrivate( parent ), 0065 m_P(1), 0066 m_previousRadius(1), 0067 m_altitudeToPixel(1), 0068 m_perspectiveRadius(1), 0069 m_pPfactor(1) 0070 { 0071 } 0072 0073 0074 QString VerticalPerspectiveProjection::name() const 0075 { 0076 return QObject::tr( "Vertical Perspective Projection" ); 0077 } 0078 0079 QString VerticalPerspectiveProjection::description() const 0080 { 0081 return QObject::tr( "<p><b>Vertical Perspective Projection</b> (\"orthogonal\")</p><p> Shows the earth as it appears from a relatively short distance above the surface. Applications: Used for Virtual Globes.</p>" ); 0082 } 0083 0084 QIcon VerticalPerspectiveProjection::icon() const 0085 { 0086 return QIcon(QStringLiteral(":/icons/map-globe.png")); 0087 } 0088 0089 void VerticalPerspectiveProjectionPrivate::calculateConstants(qreal radius) const 0090 { 0091 if (radius == m_previousRadius) return; 0092 m_previousRadius = radius; 0093 m_P = 1.5 + 3 * 1000 * 0.4 / radius / qTan(0.5 * 110 * DEG2RAD); 0094 m_altitudeToPixel = radius / (EARTH_RADIUS * qSqrt((m_P-1)/(m_P+1))); 0095 m_perspectiveRadius = radius / qSqrt((m_P-1)/(m_P+1)); 0096 m_pPfactor = (m_P+1)/(m_perspectiveRadius*m_perspectiveRadius*(m_P-1)); 0097 } 0098 0099 qreal VerticalPerspectiveProjection::clippingRadius() const 0100 { 0101 return 1; 0102 } 0103 0104 bool VerticalPerspectiveProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0105 const ViewportParams *viewport, 0106 qreal &x, qreal &y, bool &globeHidesPoint ) const 0107 { 0108 Q_D(const VerticalPerspectiveProjection); 0109 d->calculateConstants(viewport->radius()); 0110 const qreal P = d->m_P; 0111 const qreal deltaLambda = coordinates.longitude() - viewport->centerLongitude(); 0112 const qreal phi = coordinates.latitude(); 0113 const qreal phi1 = viewport->centerLatitude(); 0114 0115 qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( deltaLambda ); 0116 0117 // Don't display placemarks that are below 10km altitude and 0118 // are on the Earth's backside (where cosC < 1/P) 0119 if (cosC < 1/P && coordinates.altitude() < 10000) { 0120 globeHidesPoint = true; 0121 return false; 0122 } 0123 0124 // Let (x, y) be the position on the screen of the placemark .. 0125 // First determine the position in unit coordinates: 0126 qreal k = (P - 1) / (P - cosC); // scale factor 0127 x = ( qCos( phi ) * qSin( deltaLambda ) ) * k; 0128 y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( deltaLambda ) ) * k; 0129 0130 // Transform to screen coordinates 0131 qreal pixelAltitude = (coordinates.altitude() + EARTH_RADIUS) * d->m_altitudeToPixel; 0132 x *= pixelAltitude; 0133 y *= pixelAltitude; 0134 0135 0136 // Don't display satellites that are on the Earth's backside: 0137 if (cosC < 1/P && x*x+y*y < viewport->radius() * viewport->radius()) { 0138 globeHidesPoint = true; 0139 return false; 0140 } 0141 // The remaining placemarks are definitely not on the Earth's backside 0142 globeHidesPoint = false; 0143 0144 x += viewport->width() / 2; 0145 y = viewport->height() / 2 - y; 0146 0147 // Skip placemarks that are outside the screen area 0148 return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); 0149 } 0150 0151 bool VerticalPerspectiveProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0152 const ViewportParams *viewport, 0153 qreal *x, qreal &y, 0154 int &pointRepeatNum, 0155 const QSizeF& size, 0156 bool &globeHidesPoint ) const 0157 { 0158 pointRepeatNum = 0; 0159 globeHidesPoint = false; 0160 0161 bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); 0162 0163 // Skip placemarks that are outside the screen area 0164 if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 0165 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) 0166 { 0167 return false; 0168 } 0169 0170 // This projection doesn't have any repetitions, 0171 // so the number of screen points referring to the geopoint is one. 0172 pointRepeatNum = 1; 0173 return visible; 0174 } 0175 0176 0177 bool VerticalPerspectiveProjection::geoCoordinates( const int x, const int y, 0178 const ViewportParams *viewport, 0179 qreal& lon, qreal& lat, 0180 GeoDataCoordinates::Unit unit ) const 0181 { 0182 Q_D(const VerticalPerspectiveProjection); 0183 d->calculateConstants(viewport->radius()); 0184 const qreal P = d->m_P; 0185 const qreal rx = ( - viewport->width() / 2 + x ); 0186 const qreal ry = ( viewport->height() / 2 - y ); 0187 const qreal p2 = rx*rx + ry*ry; 0188 0189 if (p2 == 0) { 0190 lon = viewport->centerLongitude(); 0191 lat = viewport->centerLatitude(); 0192 return true; 0193 } 0194 0195 const qreal pP = p2*d->m_pPfactor; 0196 0197 if ( pP > 1) return false; 0198 0199 const qreal p = qSqrt(p2); 0200 const qreal fract = d->m_perspectiveRadius*(P-1)/p; 0201 const qreal c = qAsin((P-qSqrt(1-pP))/(fract+1/fract)); 0202 const qreal sinc = qSin(c); 0203 0204 const qreal centerLon = viewport->centerLongitude(); 0205 const qreal centerLat = viewport->centerLatitude(); 0206 lon = centerLon + qAtan2(rx*sinc, (p*qCos(centerLat)*qCos(c) - ry*qSin(centerLat)*sinc)); 0207 0208 while ( lon < -M_PI ) lon += 2 * M_PI; 0209 while ( lon > M_PI ) lon -= 2 * M_PI; 0210 0211 lat = qAsin(qCos(c)*qSin(centerLat) + (ry*sinc*qCos(centerLat))/p); 0212 0213 if ( unit == GeoDataCoordinates::Degree ) { 0214 lon *= RAD2DEG; 0215 lat *= RAD2DEG; 0216 } 0217 0218 return true; 0219 } 0220 0221 }