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 }