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

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0004 // SPDX-FileCopyrightText: 2007-2012 Torsten Rahn <rahn@kde.org>
0005 // SPDX-FileCopyrightText: 2012 Cezar Mocan <mocancezar@gmail.com>
0006 //
0007 
0008 // Local
0009 #include "AbstractProjection.h"
0010 
0011 #include "AbstractProjection_p.h"
0012 
0013 #include "MarbleDebug.h"
0014 #include <QRegion>
0015 #include <QPainterPath>
0016 
0017 // Marble
0018 #include "GeoDataLineString.h"
0019 #include "GeoDataLinearRing.h"
0020 #include "GeoDataLatLonAltBox.h"
0021 #include "ViewportParams.h"
0022 
0023 using namespace Marble;
0024 
0025 AbstractProjection::AbstractProjection()
0026     : d_ptr( new AbstractProjectionPrivate( this ) )
0027 {
0028 }
0029 
0030 AbstractProjection::AbstractProjection( AbstractProjectionPrivate* dd )
0031     : d_ptr( dd )
0032 {
0033 }
0034 
0035 AbstractProjection::~AbstractProjection()
0036 {
0037 }
0038 
0039 AbstractProjectionPrivate::AbstractProjectionPrivate( AbstractProjection * parent )
0040     : m_maxLat(0),
0041       m_minLat(0),
0042       m_previousResolution(-1),
0043       m_level(-1),
0044       q_ptr( parent)
0045 {
0046 }
0047 
0048 int AbstractProjectionPrivate::levelForResolution(qreal resolution) const {
0049     if (m_previousResolution == resolution) return m_level;
0050 
0051     m_previousResolution = resolution;
0052 
0053     if (resolution < 0.0000005) m_level = 17;
0054     else if (resolution < 0.0000010) m_level = 16;
0055     else if (resolution < 0.0000020) m_level = 15;
0056     else if (resolution < 0.0000040) m_level = 14;
0057     else if (resolution < 0.0000080) m_level = 13;
0058     else if (resolution < 0.0000160) m_level = 12;
0059     else if (resolution < 0.0000320) m_level = 11;
0060     else if (resolution < 0.0000640) m_level = 10;
0061     else if (resolution < 0.0001280) m_level = 9;
0062     else if (resolution < 0.0002560) m_level = 8;
0063     else if (resolution < 0.0005120) m_level = 7;
0064     else if (resolution < 0.0010240) m_level = 6;
0065     else if (resolution < 0.0020480) m_level = 5;
0066     else if (resolution < 0.0040960) m_level = 4;
0067     else if (resolution < 0.0081920) m_level = 3;
0068     else if (resolution < 0.0163840) m_level = 2;
0069     else m_level =  1;
0070 
0071     return m_level;
0072 }
0073 
0074 qreal AbstractProjection::maxValidLat() const
0075 {
0076     return +90.0 * DEG2RAD;
0077 }
0078 
0079 qreal AbstractProjection::maxLat() const
0080 {
0081     Q_D(const AbstractProjection );
0082     return d->m_maxLat;
0083 }
0084 
0085 void AbstractProjection::setMaxLat( qreal maxLat )
0086 {
0087     if ( maxLat < maxValidLat() ) {
0088         mDebug() << "Trying to set maxLat to a value that is out of the valid range.";
0089         return;
0090     }
0091 
0092     Q_D( AbstractProjection );
0093     d->m_maxLat = maxLat;
0094 }
0095 
0096 qreal AbstractProjection::minValidLat() const
0097 {
0098     return -90.0 * DEG2RAD;
0099 }
0100 
0101 qreal AbstractProjection::minLat() const
0102 {
0103     Q_D( const AbstractProjection );
0104     return d->m_minLat;
0105 }
0106 
0107 void AbstractProjection::setMinLat( qreal minLat )
0108 {
0109     if ( minLat < minValidLat() ) {
0110         mDebug() << "Trying to set minLat to a value that is out of the valid range.";
0111         return;
0112     }
0113 
0114     Q_D( AbstractProjection );
0115     d->m_minLat = minLat;
0116 }
0117 
0118 bool AbstractProjection::repeatableX() const
0119 {
0120     return false;
0121 }
0122 
0123 bool AbstractProjection::traversablePoles() const
0124 {
0125     return false;
0126 }
0127 
0128 bool AbstractProjection::traversableDateLine() const
0129 {
0130     return false;
0131 }
0132 
0133 AbstractProjection::PreservationType AbstractProjection::preservationType() const
0134 {
0135     return NoPreservation;
0136 }
0137 
0138 bool AbstractProjection::isOrientedNormal() const
0139 {
0140     return true;
0141 }
0142 
0143 bool AbstractProjection::isClippedToSphere() const
0144 {
0145     return false;
0146 }
0147 
0148 qreal AbstractProjection::clippingRadius() const
0149 {
0150     return 0;
0151 }
0152 
0153 
0154 bool AbstractProjection::screenCoordinates( const qreal lon, const qreal lat,
0155                                             const ViewportParams *viewport,
0156                                             qreal &x, qreal &y ) const
0157 {
0158     bool globeHidesPoint;
0159     GeoDataCoordinates geopoint(lon, lat);
0160     return screenCoordinates( geopoint, viewport, x, y, globeHidesPoint );
0161 }
0162 
0163 bool AbstractProjection::screenCoordinates( const GeoDataCoordinates &geopoint,
0164                                             const ViewportParams *viewport,
0165                                             qreal &x, qreal &y ) const
0166 {
0167     bool globeHidesPoint;
0168 
0169     return screenCoordinates( geopoint, viewport, x, y, globeHidesPoint );
0170 }
0171 
0172 GeoDataLatLonAltBox AbstractProjection::latLonAltBox( const QRect& screenRect,
0173                                                       const ViewportParams *viewport ) const
0174 {
0175     // For the case where the whole viewport gets covered there is a 
0176     // pretty dirty and generic detection algorithm:
0177 
0178     // Move along the screenborder and save the highest and lowest lon-lat values.
0179     QRect projectedRect = mapRegion( viewport ).boundingRect();
0180     QRect mapRect = screenRect.intersected( projectedRect );
0181 
0182     GeoDataLineString boundingLineString;
0183 
0184     qreal lon, lat;
0185 
0186     for ( int x = mapRect.left(); x < mapRect.right(); x += latLonAltBoxSamplingRate ) {
0187         if ( geoCoordinates( x, mapRect.bottom(), viewport, lon, lat,
0188                              GeoDataCoordinates::Radian ) ) {
0189             boundingLineString << GeoDataCoordinates( lon, lat );
0190         }
0191 
0192         if ( geoCoordinates( x, mapRect.top(),
0193                              viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
0194             boundingLineString << GeoDataCoordinates( lon, lat );
0195         }
0196     }
0197 
0198     if ( geoCoordinates( mapRect.right(), mapRect.top(), viewport, lon, lat,
0199                          GeoDataCoordinates::Radian ) ) {
0200         boundingLineString << GeoDataCoordinates( lon, lat );
0201     }
0202 
0203     if ( geoCoordinates( mapRect.right(), mapRect.bottom(),
0204                          viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
0205         boundingLineString << GeoDataCoordinates( lon, lat );
0206     }
0207 
0208     for ( int y = mapRect.bottom(); y < mapRect.top(); y += latLonAltBoxSamplingRate ) {
0209         if ( geoCoordinates( mapRect.left(), y, viewport, lon, lat,
0210                              GeoDataCoordinates::Radian ) ) {
0211             boundingLineString << GeoDataCoordinates( lon, lat );
0212         }
0213 
0214         if ( geoCoordinates( mapRect.right(), y,
0215                              viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
0216             boundingLineString << GeoDataCoordinates( lon, lat );
0217         }
0218     }
0219 
0220     GeoDataLatLonAltBox latLonAltBox = boundingLineString.latLonAltBox();
0221     
0222     // Now we need to check whether maxLat (e.g. the north pole) gets displayed
0223     // inside the viewport.
0224 
0225     // We need a point on the screen at maxLat that definitely gets displayed:
0226 
0227     // FIXME: Some of the following code can be safely removed as soon as we properly handle
0228     //        GeoDataLinearRing::latLonAltBox().
0229     qreal averageLongitude = ( latLonAltBox.west() + latLonAltBox.east() ) / 2.0;
0230 
0231     GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian );
0232     GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian );
0233 
0234     qreal dummyX, dummyY; // not needed
0235 
0236     if ( latLonAltBox.north() > maxLat() ||
0237          screenCoordinates( maxLatPoint, viewport, dummyX, dummyY ) ) {
0238         latLonAltBox.setNorth( maxLat() );
0239     }
0240     if ( latLonAltBox.north() < minLat() ||
0241          screenCoordinates( minLatPoint, viewport, dummyX, dummyY ) ) {
0242         latLonAltBox.setSouth( minLat() );
0243     }
0244 
0245     latLonAltBox.setMinAltitude(      -100000000.0 );
0246     latLonAltBox.setMaxAltitude( 100000000000000.0 );
0247 
0248     return latLonAltBox;
0249 }
0250 
0251 
0252 QRegion AbstractProjection::mapRegion( const ViewportParams *viewport ) const
0253 {
0254     return QRegion( mapShape( viewport ).toFillPolygon().toPolygon() );
0255 }