File indexing completed on 2025-01-05 03:59:33

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0004 //
0005 
0006 // Local
0007 #include "GnomonicProjection.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 GnomonicProjectionPrivate : public AzimuthalProjectionPrivate
0031 {
0032   public:
0033     explicit GnomonicProjectionPrivate( GnomonicProjection * parent );
0034 
0035     Q_DECLARE_PUBLIC( GnomonicProjection )
0036 };
0037 
0038 GnomonicProjection::GnomonicProjection()
0039     : AzimuthalProjection( new GnomonicProjectionPrivate( this ) )
0040 {
0041     setMinLat( minValidLat() );
0042     setMaxLat( maxValidLat() );
0043 }
0044 
0045 GnomonicProjection::GnomonicProjection( GnomonicProjectionPrivate *dd )
0046         : AzimuthalProjection( dd )
0047 {
0048     setMinLat( minValidLat() );
0049     setMaxLat( maxValidLat() );
0050 }
0051 
0052 GnomonicProjection::~GnomonicProjection()
0053 {
0054 }
0055 
0056 
0057 GnomonicProjectionPrivate::GnomonicProjectionPrivate( GnomonicProjection * parent )
0058         : AzimuthalProjectionPrivate( parent )
0059 {
0060 }
0061 
0062 QString GnomonicProjection::name() const
0063 {
0064     return i18n( "Gnomonic" );
0065 }
0066 
0067 QString GnomonicProjection::description() const
0068 {
0069     return i18n( "<p><b>Gnomonic Projection</b> (\"rectilinear\")</p><p>Applications: Used for displaying panorama photography. Also used for navigation, radio and seismic work.</p>" );
0070 }
0071 
0072 QIcon GnomonicProjection::icon() const
0073 {
0074     return QIcon::fromTheme(QStringLiteral("map-gnomonic"));
0075 }
0076 
0077 qreal GnomonicProjection::clippingRadius() const
0078 {
0079     return 1;
0080 }
0081 
0082 bool GnomonicProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
0083                                              const ViewportParams *viewport,
0084                                              qreal &x, qreal &y, bool &globeHidesPoint ) const
0085 {
0086     const qreal lambda = coordinates.longitude();
0087     const qreal phi = coordinates.latitude();
0088     const qreal lambdaPrime = viewport->centerLongitude();
0089     const qreal phi1 = viewport->centerLatitude();
0090 
0091     qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime );
0092 
0093     if ( cosC <= 0) {
0094         globeHidesPoint = true;
0095         return false;
0096     }
0097 
0098     // Let (x, y) be the position on the screen of the placemark..
0099     x = ( qCos( phi ) * qSin( lambda - lambdaPrime ) ) / cosC;
0100     y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ) ) / cosC;
0101 
0102     x *= viewport->radius() / 2;
0103     y *= viewport->radius() / 2;
0104 
0105     const qint64  radius  = clippingRadius() * viewport->radius();
0106 
0107     if (x*x + y*y > radius * radius) {
0108         globeHidesPoint = true;
0109         return false;
0110     }
0111 
0112     globeHidesPoint = false;
0113 
0114     x += viewport->width() / 2;
0115     y = viewport->height() / 2 - y;
0116 
0117     // Skip placemarks that are outside the screen area
0118     return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height());
0119 }
0120 
0121 bool GnomonicProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
0122                                              const ViewportParams *viewport,
0123                                              qreal *x, qreal &y,
0124                                              int &pointRepeatNum,
0125                                              const QSizeF& size,
0126                                              bool &globeHidesPoint ) const
0127 {
0128     pointRepeatNum = 0;
0129     globeHidesPoint = false;
0130 
0131     bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint );
0132 
0133     // Skip placemarks that are outside the screen area
0134     if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0
0135          || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 )
0136     {
0137         return false;
0138     }
0139 
0140     // This projection doesn't have any repetitions,
0141     // so the number of screen points referring to the geopoint is one.
0142     pointRepeatNum = 1;
0143     return visible;
0144 }
0145 
0146 
0147 bool GnomonicProjection::geoCoordinates( const int x, const int y,
0148                                           const ViewportParams *viewport,
0149                                           qreal& lon, qreal& lat,
0150                                           GeoDataCoordinates::Unit unit ) const
0151 {
0152     const qint64  radius  = viewport->radius();
0153     // Calculate how many degrees are being represented per pixel.
0154     const qreal centerLon = viewport->centerLongitude();
0155     const qreal centerLat = viewport->centerLatitude();
0156     const qreal rx = ( - viewport->width()  / 2 + x );
0157     const qreal ry = (   viewport->height() / 2 - y );
0158     const qreal p = qMax( qSqrt( rx*rx + ry*ry ), qreal(0.0001) ); // ensure we don't divide by zero
0159     const qreal c = qAtan(2 * p / radius);
0160     const qreal sinc = qSin(c);
0161 
0162     lon = centerLon + qAtan2( rx*sinc , ( p*qCos( centerLat )*qCos( c ) - ry*qSin( centerLat )*sinc  ) );
0163 
0164     while ( lon < -M_PI ) lon += 2 * M_PI;
0165     while ( lon >  M_PI ) lon -= 2 * M_PI;
0166 
0167     lat = qAsin( qCos(c)*qSin(centerLat) + ry*sinc*qCos(centerLat)/p );
0168 
0169     if ( unit == GeoDataCoordinates::Degree ) {
0170         lon *= RAD2DEG;
0171         lat *= RAD2DEG;
0172     }
0173 
0174     return true;
0175 }
0176 
0177 }