File indexing completed on 2024-04-14 03:48:11
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org> 0004 // SPDX-FileCopyrightText: 2008 Jens-Michael Hoffmann <jensmh@gmx.de> 0005 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0006 // 0007 0008 0009 #include "ViewportParams.h" 0010 0011 #include <QRect> 0012 0013 #include <QPainterPath> 0014 #include <QRegion> 0015 0016 #include "MarbleDebug.h" 0017 #include "GeoDataLatLonAltBox.h" 0018 #include "SphericalProjection.h" 0019 #include "EquirectProjection.h" 0020 #include "MercatorProjection.h" 0021 #include "GnomonicProjection.h" 0022 #include "LambertAzimuthalProjection.h" 0023 #include "AzimuthalEquidistantProjection.h" 0024 #include "StereographicProjection.h" 0025 #include "VerticalPerspectiveProjection.h" 0026 0027 0028 namespace Marble 0029 { 0030 0031 class ViewportParamsPrivate 0032 { 0033 public: 0034 ViewportParamsPrivate( Projection projection, 0035 qreal centerLongitude, qreal centerLatitude, 0036 int radius, 0037 const QSize &size ); 0038 0039 static const AbstractProjection *abstractProjection( Projection projection ); 0040 0041 // These two go together. m_currentProjection points to one of 0042 // the static Projection classes at the bottom. 0043 Projection m_projection; 0044 const AbstractProjection *m_currentProjection; 0045 0046 // Parameters that determine the painting 0047 qreal m_centerLongitude; 0048 qreal m_centerLatitude; 0049 qreal m_heading; 0050 Quaternion m_planetAxis; // Position, coded in a quaternion 0051 matrix m_planetAxisMatrix; 0052 int m_radius; // Zoom level (pixels / globe radius) 0053 qreal m_angularResolution; 0054 0055 QSize m_size; // width, height 0056 0057 0058 bool m_dirtyBox; 0059 GeoDataLatLonAltBox m_viewLatLonAltBox; 0060 0061 static const SphericalProjection s_sphericalProjection; 0062 static const EquirectProjection s_equirectProjection; 0063 static const MercatorProjection s_mercatorProjection; 0064 static const GnomonicProjection s_gnomonicProjection; 0065 static const StereographicProjection s_stereographicProjection; 0066 static const LambertAzimuthalProjection s_lambertAzimuthalProjection; 0067 static const AzimuthalEquidistantProjection s_azimuthalEquidistantProjection; 0068 static const VerticalPerspectiveProjection s_verticalPerspectiveProjection; 0069 0070 GeoDataCoordinates m_focusPoint; 0071 }; 0072 0073 const SphericalProjection ViewportParamsPrivate::s_sphericalProjection; 0074 const EquirectProjection ViewportParamsPrivate::s_equirectProjection; 0075 const MercatorProjection ViewportParamsPrivate::s_mercatorProjection; 0076 const GnomonicProjection ViewportParamsPrivate::s_gnomonicProjection; 0077 const StereographicProjection ViewportParamsPrivate::s_stereographicProjection; 0078 const LambertAzimuthalProjection ViewportParamsPrivate::s_lambertAzimuthalProjection; 0079 const AzimuthalEquidistantProjection ViewportParamsPrivate::s_azimuthalEquidistantProjection; 0080 const VerticalPerspectiveProjection ViewportParamsPrivate::s_verticalPerspectiveProjection; 0081 0082 ViewportParamsPrivate::ViewportParamsPrivate( Projection projection, 0083 qreal centerLongitude, qreal centerLatitude, 0084 int radius, 0085 const QSize &size ) 0086 : m_projection( projection ), 0087 m_currentProjection( abstractProjection( projection ) ), 0088 m_centerLongitude( centerLongitude ), 0089 m_centerLatitude( centerLatitude ), 0090 m_heading( 0 ), 0091 m_planetAxis(), 0092 m_planetAxisMatrix(), 0093 m_radius( radius ), 0094 m_angularResolution(4.0 / abs(m_radius)), 0095 m_size( size ), 0096 m_dirtyBox( true ), 0097 m_viewLatLonAltBox() 0098 { 0099 } 0100 0101 const AbstractProjection *ViewportParamsPrivate::abstractProjection(Projection projection) 0102 { 0103 switch ( projection ) { 0104 case Spherical: 0105 return &s_sphericalProjection; 0106 case Equirectangular: 0107 return &s_equirectProjection; 0108 case Mercator: 0109 return &s_mercatorProjection; 0110 case Gnomonic: 0111 return &s_gnomonicProjection; 0112 case Stereographic: 0113 return &s_stereographicProjection; 0114 case LambertAzimuthal: 0115 return &s_lambertAzimuthalProjection; 0116 case AzimuthalEquidistant: 0117 return &s_azimuthalEquidistantProjection; 0118 case VerticalPerspective: 0119 return &s_verticalPerspectiveProjection; 0120 } 0121 0122 return nullptr; 0123 } 0124 0125 0126 ViewportParams::ViewportParams() 0127 : d( new ViewportParamsPrivate( Spherical, 0, 0, 2000, QSize( 100, 100 ) ) ) 0128 { 0129 centerOn( d->m_centerLongitude, d->m_centerLatitude ); 0130 } 0131 0132 ViewportParams::ViewportParams( Projection projection, 0133 qreal centerLongitude, qreal centerLatitude, 0134 int radius, 0135 const QSize &size ) 0136 : d( new ViewportParamsPrivate( projection, centerLongitude, centerLatitude, radius, size ) ) 0137 { 0138 centerOn( d->m_centerLongitude, d->m_centerLatitude ); 0139 } 0140 0141 ViewportParams::~ViewportParams() 0142 { 0143 delete d; 0144 } 0145 0146 0147 // ================================================================ 0148 // Getters and setters 0149 0150 0151 Projection ViewportParams::projection() const 0152 { 0153 return d->m_projection; 0154 } 0155 0156 const AbstractProjection *ViewportParams::currentProjection() const 0157 { 0158 return d->m_currentProjection; 0159 } 0160 0161 void ViewportParams::setProjection(Projection newProjection) 0162 { 0163 d->m_projection = newProjection; 0164 d->m_currentProjection = ViewportParamsPrivate::abstractProjection( newProjection ); 0165 0166 // We now need to reset the planetAxis to make sure 0167 // that it's a valid axis orientation! 0168 // So this line is important (although it might look odd) ! : 0169 centerOn( d->m_centerLongitude, d->m_centerLatitude ); 0170 } 0171 0172 int ViewportParams::polarity() const 0173 { 0174 // For mercator this just gives the extreme latitudes 0175 // instead of the actual poles but it works fine as well: 0176 GeoDataCoordinates northPole( 0.0, +currentProjection()->maxLat() ); 0177 GeoDataCoordinates southPole( 0.0, -currentProjection()->maxLat() ); 0178 0179 bool globeHidesN, globeHidesS; 0180 qreal x; 0181 qreal yN, yS; 0182 0183 currentProjection()->screenCoordinates( northPole, this, 0184 x, yN, globeHidesN ); 0185 currentProjection()->screenCoordinates( southPole, this, 0186 x, yS, globeHidesS ); 0187 0188 int polarity = 0; 0189 0190 // case of the flat map: 0191 if ( !globeHidesN && !globeHidesS ) { 0192 if ( yN < yS ) { 0193 polarity = +1; 0194 } 0195 if ( yS < yN ) { 0196 polarity = -1; 0197 } 0198 } 0199 else { 0200 if ( !globeHidesN && yN < height() / 2 ) { 0201 polarity = +1; 0202 } 0203 if ( !globeHidesN && yN > height() / 2 ) { 0204 polarity = -1; 0205 } 0206 if ( !globeHidesS && yS > height() / 2 ) { 0207 polarity = +1; 0208 } 0209 if ( !globeHidesS && yS < height() / 2 ) { 0210 polarity = -1; 0211 } 0212 } 0213 0214 return polarity; 0215 } 0216 0217 int ViewportParams::radius() const 0218 { 0219 return d->m_radius; 0220 } 0221 0222 void ViewportParams::setRadius(int newRadius) 0223 { 0224 if ( newRadius > 0 ) { 0225 d->m_dirtyBox = true; 0226 0227 d->m_radius = newRadius; 0228 d->m_angularResolution = 4.0 / d->m_radius; 0229 } 0230 } 0231 0232 void ViewportParams::centerOn( qreal lon, qreal lat ) 0233 { 0234 if ( !d->m_currentProjection->traversablePoles() ) { 0235 if ( lat > d->m_currentProjection->maxLat() ) 0236 lat = d->m_currentProjection->maxLat(); 0237 0238 if ( lat < d->m_currentProjection->minLat() ) 0239 lat = d->m_currentProjection->minLat(); 0240 } else { 0241 while ( lat > M_PI ) 0242 lat -= 2 * M_PI; 0243 while ( lat < -M_PI ) 0244 lat += 2 * M_PI; 0245 } 0246 0247 while ( lon > M_PI ) 0248 lon -= 2 * M_PI; 0249 while ( lon < -M_PI ) 0250 lon += 2 * M_PI; 0251 0252 d->m_centerLongitude = lon; 0253 d->m_centerLatitude = lat; 0254 0255 const Quaternion roll = Quaternion::fromEuler( 0, 0, d->m_heading ); 0256 const Quaternion quat = Quaternion::fromEuler( -lat, lon, 0.0 ); 0257 0258 d->m_planetAxis = quat * roll; 0259 d->m_planetAxis.normalize(); 0260 0261 d->m_dirtyBox = true; 0262 d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix ); 0263 d->m_planetAxis.normalize(); 0264 } 0265 0266 void ViewportParams::setHeading( qreal heading ) 0267 { 0268 d->m_heading = heading; 0269 0270 const Quaternion roll = Quaternion::fromEuler( 0, 0, heading ); 0271 0272 const qreal centerLat = centerLatitude(); 0273 const qreal centerLon = centerLongitude(); 0274 0275 const Quaternion quat = Quaternion::fromEuler( -centerLat, centerLon, 0 ); 0276 0277 d->m_planetAxis = quat * roll; 0278 d->m_planetAxis.normalize(); 0279 0280 d->m_dirtyBox = true; 0281 d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix ); 0282 d->m_planetAxis.normalize(); 0283 } 0284 0285 qreal ViewportParams::heading() const 0286 { 0287 return d->m_heading; 0288 } 0289 0290 Quaternion ViewportParams::planetAxis() const 0291 { 0292 return d->m_planetAxis; 0293 } 0294 0295 const matrix &ViewportParams::planetAxisMatrix() const 0296 { 0297 return d->m_planetAxisMatrix; 0298 } 0299 0300 int ViewportParams::width() const 0301 { 0302 return d->m_size.width(); 0303 } 0304 0305 int ViewportParams::height() const 0306 { 0307 return d->m_size.height(); 0308 } 0309 0310 QSize ViewportParams::size() const 0311 { 0312 return d->m_size; 0313 } 0314 0315 0316 void ViewportParams::setWidth(int newWidth) 0317 { 0318 setSize( QSize( newWidth, height() ) ); 0319 } 0320 0321 void ViewportParams::setHeight(int newHeight) 0322 { 0323 setSize( QSize( width(), newHeight ) ); 0324 } 0325 0326 void ViewportParams::setSize(const QSize& newSize) 0327 { 0328 if ( newSize == d->m_size ) 0329 return; 0330 0331 d->m_dirtyBox = true; 0332 0333 d->m_size = newSize; 0334 } 0335 0336 // ================================================================ 0337 // Other functions 0338 0339 qreal ViewportParams::centerLongitude() const 0340 { 0341 return d->m_centerLongitude; 0342 } 0343 0344 qreal ViewportParams::centerLatitude() const 0345 { 0346 return d->m_centerLatitude; 0347 } 0348 0349 const GeoDataLatLonAltBox& ViewportParams::viewLatLonAltBox() const 0350 { 0351 if (d->m_dirtyBox) { 0352 d->m_viewLatLonAltBox = d->m_currentProjection->latLonAltBox( QRect( QPoint( 0, 0 ), 0353 d->m_size ), 0354 this ); 0355 d->m_dirtyBox = false; 0356 } 0357 0358 return d->m_viewLatLonAltBox; 0359 } 0360 0361 GeoDataLatLonAltBox ViewportParams::latLonAltBox( const QRect &screenRect ) const 0362 { 0363 return d->m_currentProjection->latLonAltBox( screenRect, this ); 0364 } 0365 0366 qreal ViewportParams::angularResolution() const 0367 { 0368 // We essentially divide the diameter by 180 deg and 0369 // take half of the result as a guess for the angle per pixel resolution. 0370 // d->m_angularResolution = 0.25 * M_PI / fabs( (qreal)(d->m_radius); 0371 return d->m_angularResolution; 0372 } 0373 0374 bool ViewportParams::resolves ( const GeoDataLatLonBox &latLonBox, qreal pixel ) const 0375 { 0376 return latLonBox.width() + latLonBox.height() > pixel * d->m_angularResolution; 0377 } 0378 0379 0380 bool ViewportParams::resolves ( const GeoDataLatLonAltBox &latLonAltBox, qreal pixel, qreal altitude ) const 0381 { 0382 return latLonAltBox.width() + latLonAltBox.height() > pixel * d->m_angularResolution 0383 || latLonAltBox.maxAltitude() - latLonAltBox.minAltitude() > altitude; 0384 } 0385 0386 bool ViewportParams::resolves ( const GeoDataCoordinates &coord1, 0387 const GeoDataCoordinates &coord2 ) const 0388 { 0389 qreal lon1, lat1; 0390 coord1.geoCoordinates( lon1, lat1 ); 0391 0392 qreal lon2, lat2; 0393 coord2.geoCoordinates( lon2, lat2 ); 0394 0395 // We take the manhattan length as an approximation for the distance 0396 return ( fabs( lon2 - lon1 ) + fabs( lat2 - lat1 ) > d->m_angularResolution ); 0397 } 0398 0399 0400 bool ViewportParams::screenCoordinates( const qreal lon, const qreal lat, 0401 qreal &x, qreal &y ) const 0402 { 0403 return d->m_currentProjection->screenCoordinates( lon, lat, this, x, y ); 0404 } 0405 0406 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint, 0407 qreal &x, qreal &y, 0408 bool &globeHidesPoint ) const 0409 { 0410 return d->m_currentProjection->screenCoordinates( geopoint, this, x, y, globeHidesPoint ); 0411 } 0412 0413 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint, 0414 qreal &x, qreal &y ) const 0415 { 0416 return d->m_currentProjection->screenCoordinates( geopoint, this, x, y ); 0417 } 0418 0419 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &coordinates, 0420 qreal *x, qreal &y, int &pointRepeatNum, 0421 const QSizeF& size, 0422 bool &globeHidesPoint ) const 0423 { 0424 return d->m_currentProjection->screenCoordinates( coordinates, this, x, y, pointRepeatNum, size, globeHidesPoint ); 0425 } 0426 0427 0428 bool ViewportParams::screenCoordinates( const GeoDataLineString &lineString, 0429 QVector<QPolygonF*> &polygons ) const 0430 { 0431 return d->m_currentProjection->screenCoordinates( lineString, this, polygons ); 0432 } 0433 0434 bool ViewportParams::geoCoordinates( const int x, const int y, 0435 qreal &lon, qreal &lat, 0436 GeoDataCoordinates::Unit unit ) const 0437 { 0438 return d->m_currentProjection->geoCoordinates( x, y, this, lon, lat, unit ); 0439 } 0440 0441 bool ViewportParams::mapCoversViewport() const 0442 { 0443 return d->m_currentProjection->mapCoversViewport( this ); 0444 } 0445 0446 QPainterPath ViewportParams::mapShape() const 0447 { 0448 return d->m_currentProjection->mapShape( this ); 0449 } 0450 0451 QRegion ViewportParams::mapRegion() const 0452 { 0453 return d->m_currentProjection->mapRegion( this ); 0454 } 0455 0456 GeoDataCoordinates ViewportParams::focusPoint() const 0457 { 0458 if (d->m_focusPoint.isValid()) { 0459 return d->m_focusPoint; 0460 } 0461 else { 0462 const qreal lon = d->m_centerLongitude; 0463 const qreal lat = d->m_centerLatitude; 0464 0465 return GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Radian); 0466 } 0467 0468 } 0469 0470 void ViewportParams::setFocusPoint(const GeoDataCoordinates &focusPoint) 0471 { 0472 d->m_focusPoint = focusPoint; 0473 } 0474 0475 void ViewportParams::resetFocusPoint() 0476 { 0477 d->m_focusPoint = GeoDataCoordinates(); 0478 } 0479 0480 }