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