File indexing completed on 2024-05-05 03:49:52

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