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 }