File indexing completed on 2025-01-05 03:59:32
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 #include "AbstractProjection_p.h" 0011 0012 #include <QRegion> 0013 #include <QPainterPath> 0014 0015 // Marble 0016 #include "GeoDataLineString.h" 0017 #include "GeoDataLinearRing.h" 0018 #include "GeoDataLatLonAltBox.h" 0019 #include "ViewportParams.h" 0020 0021 #include "digikam_debug.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 qCDebug(DIGIKAM_MARBLE_LOG) << Q_FUNC_INFO << "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 qCDebug(DIGIKAM_MARBLE_LOG) << Q_FUNC_INFO << "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 }