File indexing completed on 2024-04-21 03:49:41

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0006 // SPDX-FileCopyrightText: 2012 Mohammed Nafees <nafees.technocool@gmail.com>
0007 // SPDX-FileCopyrightText: 2014 Adam Dabrowski <adamdbrw@gmail.com>
0008 //
0009 
0010 #include <MarbleAbstractPresenter.h>
0011 #include <QtMath>
0012 #include <Quaternion.h>
0013 #include <ViewportParams.h>
0014 #include <MarbleLocale.h>
0015 #include "MarbleMap.h"
0016 #include "MarbleModel.h"
0017 #include <Planet.h>
0018 #include "GeoDataGeometry.h"
0019 #include "GeoDataLatLonAltBox.h"
0020 #include <GeoDataPlacemark.h>
0021 #include <GeoDataLookAt.h>
0022 #include <MarbleClock.h>
0023 #include <MarbleDebug.h>
0024 
0025 namespace Marble
0026 {
0027     MarbleAbstractPresenter::MarbleAbstractPresenter(MarbleMap *map, QObject *parent) :
0028         QObject(parent)
0029         ,m_map(map)
0030         ,m_physics(this)
0031         ,m_animationsEnabled(false)
0032         ,m_logzoom(0)
0033         ,m_zoomStep(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 60 : 40)
0034         ,m_viewAngle(110)
0035     {
0036     }
0037 
0038     MarbleAbstractPresenter::~MarbleAbstractPresenter()
0039     {
0040     }
0041 
0042     qreal MarbleAbstractPresenter::zoom(qreal radius) const
0043     {
0044         return (200.0 * log(radius));
0045     }
0046 
0047     qreal MarbleAbstractPresenter::radius(qreal zoom) const
0048     {
0049         return pow(M_E, (zoom / 200.0));
0050     }
0051 
0052     void MarbleAbstractPresenter::rotateBy(const qreal deltaLon, const qreal deltaLat, FlyToMode mode)
0053     {
0054 
0055         GeoDataLookAt target = lookAt();
0056         GeoDataCoordinates coords(map()->viewport()->centerLongitude(), map()->viewport()->centerLatitude());
0057         GeoDataCoordinates movedCoords = coords.moveByBearing(-map()->heading() * DEG2RAD, -deltaLat * DEG2RAD);
0058         movedCoords = movedCoords.moveByBearing((-map()->heading() - 90) * DEG2RAD, -deltaLon * DEG2RAD * map()->viewport()->polarity());
0059 
0060         target.setLongitude(movedCoords.longitude());
0061         target.setLatitude(movedCoords.latitude());
0062 
0063         flyTo(target, mode);
0064     }
0065 
0066     void MarbleAbstractPresenter::flyTo(const GeoDataLookAt &newLookAt, FlyToMode mode)
0067     {
0068         if (!m_animationsEnabled || mode == Instant)
0069         {
0070             const int radius = qRound(radiusFromDistance(newLookAt.range() * METER2KM));
0071             qreal const zoomVal = zoom(radius);
0072 
0073             // Prevent exceeding zoom range. Note: Bounding to range is not useful here
0074             if (qRound(zoomVal) >= minimumZoom() && qRound(zoomVal) <= maximumZoom())
0075             {
0076                 map()->setRadius(radius);
0077                 m_logzoom = qRound(zoom(radius));
0078 
0079                 GeoDataCoordinates::Unit deg = GeoDataCoordinates::Degree;
0080                 map()->centerOn(newLookAt.longitude(deg), newLookAt.latitude(deg));
0081 
0082                 emit zoomChanged(m_logzoom);
0083                 emit distanceChanged(distanceString());
0084             }
0085         }
0086         else
0087         {
0088             m_physics.flyTo(newLookAt, mode);
0089         }
0090     }
0091 
0092     QString MarbleAbstractPresenter::distanceString() const
0093     {
0094         // distance() returns data in km, so translating to meters
0095         qreal dist = distance() * KM2METER, convertedDistance;
0096 
0097         MarbleLocale::MeasureUnit unit;
0098         MarbleLocale *locale = MarbleGlobal::getInstance()->locale();
0099         locale->meterToTargetUnit(dist, locale->measurementSystem(),
0100                                   convertedDistance, unit);
0101         QString unitString = locale->unitAbbreviation(unit);
0102 
0103         return QString("%L1 %2").arg(convertedDistance, 8, 'f', 1, QLatin1Char(' '))
0104                                 .arg(unitString);
0105     }
0106 
0107     GeoDataLookAt MarbleAbstractPresenter::lookAt() const
0108     {
0109         GeoDataLookAt result;
0110 
0111         result.setLongitude(map()->viewport()->centerLongitude());
0112         result.setLatitude(map()->viewport()->centerLatitude());
0113         result.setAltitude(0.0);
0114         result.setRange(distance() * KM2METER);
0115 
0116         return result;
0117     }
0118 
0119     qreal MarbleAbstractPresenter::distance() const
0120     {
0121         return distanceFromRadius(radius());
0122     }
0123 
0124     qreal MarbleAbstractPresenter::distanceFromRadius(qreal radius) const
0125     {
0126         // Due to Marble's orthographic projection ("we have no focus")
0127         // it's actually not possible to calculate a "real" distance.
0128         // Additionally the viewing angle of the earth doesn't adjust to
0129         // the window's size.
0130         //
0131         // So the only possible workaround is to come up with a distance
0132         // definition which gives a reasonable approximation of
0133         // reality. Therefore we assume that the average window width
0134         // (about 800 pixels) equals the viewing angle of a human being.
0135 
0136         return (model()->planet()->radius() * 0.4
0137                 / radius / tan(0.5 * m_viewAngle * DEG2RAD));
0138     }
0139 
0140     qreal MarbleAbstractPresenter::radiusFromDistance(qreal distance) const
0141     {
0142         return model()->planet()->radius() /
0143                 (distance * tan(0.5 * m_viewAngle * DEG2RAD) / 0.4 );
0144     }
0145 
0146     int MarbleAbstractPresenter::polarity() const
0147     {
0148         return map()->viewport()->polarity();
0149     }
0150 
0151     int MarbleAbstractPresenter::zoom() const
0152     {
0153         return m_logzoom;
0154     }
0155 
0156     int MarbleAbstractPresenter::minimumZoom() const
0157     {
0158         return map()->minimumZoom();
0159     }
0160 
0161     int MarbleAbstractPresenter::maximumZoom() const
0162     {
0163         return map()->maximumZoom();
0164     }
0165 
0166     void MarbleAbstractPresenter::setZoom(int newZoom, FlyToMode mode)
0167     {
0168         // It won't fly anyway. So we should do everything to keep the zoom value.
0169         if (!m_animationsEnabled || mode == Instant)
0170         {
0171             // Check for under and overflow.
0172             if (newZoom < minimumZoom())
0173                 newZoom = minimumZoom();
0174             else if (newZoom > maximumZoom())
0175                 newZoom = maximumZoom();
0176 
0177             // Prevent infinite loops.
0178             if (newZoom == m_logzoom)
0179                 return;
0180 
0181             map()->setRadius(radius(newZoom));
0182             m_logzoom = newZoom;
0183 
0184             emit zoomChanged(m_logzoom);
0185             emit distanceChanged(distanceString());
0186         }
0187         else
0188         {
0189             GeoDataLookAt target = lookAt();
0190             target.setRange(KM2METER * distanceFromZoom(newZoom));
0191             flyTo(target, mode);
0192         }
0193     }
0194 
0195     void MarbleAbstractPresenter::zoomView(int zoom, FlyToMode mode)
0196     {
0197         setZoom(zoom, mode);
0198     }
0199 
0200     void MarbleAbstractPresenter::zoomViewBy(int zoomStep, FlyToMode mode)
0201     {
0202         setZoom(zoom() + zoomStep, mode);
0203     }
0204 
0205     void MarbleAbstractPresenter::zoomIn(FlyToMode mode)
0206     {
0207         if (map()->tileZoomLevel() < 0)
0208         {
0209             zoomViewBy(m_zoomStep, mode);
0210         }
0211         else
0212         {
0213             qreal radiusVal = map()->preferredRadiusCeil(map()->radius() / 0.95);
0214             radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
0215 
0216             GeoDataLookAt target = lookAt();
0217             target.setRange(KM2METER * distanceFromRadius(radiusVal));
0218 
0219             flyTo(target, mode);
0220         }
0221     }
0222 
0223     void MarbleAbstractPresenter::zoomOut(FlyToMode mode)
0224     {
0225         if (map()->tileZoomLevel() <= 0)
0226         {
0227             zoomViewBy(-m_zoomStep, mode);
0228         }
0229         else
0230         {
0231             qreal radiusVal = map()->preferredRadiusFloor(map()->radius() * 0.95);
0232             radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
0233 
0234             GeoDataLookAt target = lookAt();
0235             target.setRange(KM2METER * distanceFromRadius(radiusVal));
0236 
0237             flyTo(target, mode);
0238         }
0239     }
0240 
0241     void MarbleAbstractPresenter::zoomAtBy(const QPoint &pos, int zoomStep)
0242         {
0243             qreal radiusVal;
0244             if (map()->tileZoomLevel() <= 0) {
0245                 radiusVal = radius(zoom() + zoomStep);
0246             } else {
0247 
0248                 radiusVal = zoomStep > 0 ? map()->preferredRadiusCeil(map()->radius() / 0.95) :
0249                                            map()->preferredRadiusFloor(map()->radius() * 0.95);
0250                 radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
0251             }
0252 
0253             zoomAt(pos, distanceFromRadius(radiusVal));
0254         }
0255 
0256     qreal MarbleAbstractPresenter::distanceFromZoom(qreal zoom) const
0257     {
0258         return distanceFromRadius(radius(zoom));
0259     }
0260 
0261     qreal MarbleAbstractPresenter::zoomFromDistance(qreal distance) const
0262     {
0263         return zoom(radiusFromDistance(distance));
0264     }
0265 
0266     void MarbleAbstractPresenter::goHome(FlyToMode mode)
0267     {
0268         qreal homeLon = 0;
0269         qreal homeLat = 0;
0270         int homeZoom = 0;
0271         model()->home(homeLon, homeLat, homeZoom);
0272 
0273         GeoDataLookAt target;
0274         target.setLongitude(homeLon, GeoDataCoordinates::Degree);
0275         target.setLatitude(homeLat, GeoDataCoordinates::Degree);
0276         target.setRange(1000 * distanceFromZoom(homeZoom));
0277 
0278         flyTo(target, mode);
0279     }
0280 
0281     void MarbleAbstractPresenter::moveByStep(int stepsRight, int stepsDown, FlyToMode mode)
0282     {
0283         int polarity = map()->viewport()->polarity();
0284         qreal left = polarity * stepsRight * moveStep();
0285         qreal down = stepsDown * moveStep();
0286         rotateBy(left, down, mode);
0287     }
0288 
0289     qreal MarbleAbstractPresenter::moveStep() const
0290     {
0291         int width = map()->width();
0292         int height = map()->height();
0293 
0294         if (radius() < qSqrt((qreal)(width * width + height * height)))
0295             return 180.0 * 0.1;
0296         else
0297             return 180.0 * qAtan((qreal)width
0298                          / (qreal)(2 * radius())) * 0.2;
0299     }
0300 
0301     int MarbleAbstractPresenter::radius() const
0302     {
0303         return map()->radius();
0304     }
0305 
0306     void MarbleAbstractPresenter::setRadius(int radiusVal)
0307     {
0308         Q_ASSERT(radiusVal >= 0);
0309         bool adjustRadius = radiusVal != map()->radius();
0310 
0311         qreal const zoomVal = zoom(radiusVal);
0312 
0313         // Prevent exceeding zoom range
0314         if (zoomVal < minimumZoom())
0315         {
0316             radiusVal = radius(minimumZoom());
0317             adjustRadius = true;
0318         }
0319         else if (zoomVal > maximumZoom())
0320         {
0321             radiusVal = radius(maximumZoom());
0322             adjustRadius = true;
0323         }
0324 
0325         if (adjustRadius)
0326         {
0327             map()->setRadius(radiusVal);
0328             m_logzoom = qRound(zoomVal);
0329 
0330             emit zoomChanged(m_logzoom);
0331             emit distanceChanged(distanceString());
0332         }
0333     }
0334 
0335 
0336     //Moved from MarbleWidgetInputHandlerPrivate - fits more here now
0337     void MarbleAbstractPresenter::zoomAt(const QPoint &pos, qreal newDistance)
0338     {
0339         Q_ASSERT(newDistance > 0.0);
0340 
0341         qreal destLat;
0342         qreal destLon;
0343         if (!map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Degree))
0344         {
0345             return;
0346         }
0347 
0348         ViewportParams* now = map()->viewport();
0349         qreal x(0), y(0);
0350         if (!now->screenCoordinates(destLon * DEG2RAD, destLat * DEG2RAD, x, y))
0351         {
0352             return;
0353         }
0354 
0355         ViewportParams soon;
0356         soon.setProjection(now->projection());
0357         soon.centerOn(now->centerLongitude(), now->centerLatitude());
0358         soon.setSize(now->size());
0359 
0360         qreal newRadius = radiusFromDistance(newDistance);
0361         soon.setRadius(newRadius);
0362 
0363         qreal mouseLon, mouseLat;
0364         if (!soon.geoCoordinates(int(x), int(y), mouseLon, mouseLat, GeoDataCoordinates::Degree ))
0365         {
0366             return;
0367         }
0368 
0369         const qreal lon = destLon - (mouseLon - map()->centerLongitude());
0370         const qreal lat = destLat - (mouseLat - map()->centerLatitude());
0371 
0372         GeoDataLookAt lookAt;
0373         lookAt.setLongitude(lon, GeoDataCoordinates::Degree);
0374         lookAt.setLatitude(lat, GeoDataCoordinates::Degree);
0375         lookAt.setAltitude(0.0);
0376         lookAt.setRange(newDistance * KM2METER);
0377 
0378         map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat, 0, GeoDataCoordinates::Degree));
0379         flyTo(lookAt, Linear);
0380     }
0381 
0382     void MarbleAbstractPresenter::moveTo(const QPoint &pos, qreal factor)
0383     {
0384         Q_ASSERT(factor > 0.0);
0385 
0386         qreal destLat;
0387         qreal destLon;
0388         map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Radian);
0389 
0390         GeoDataLookAt lookAt;
0391         lookAt.setLongitude(destLon);
0392         lookAt.setLatitude(destLat);
0393         lookAt.setAltitude(0.0);
0394         lookAt.setRange(distance() * factor * KM2METER);
0395 
0396         flyTo(lookAt);
0397     }
0398 
0399     void MarbleAbstractPresenter::centerOn(const qreal lon, const qreal lat, bool animated)
0400     {
0401         GeoDataCoordinates target(lon, lat, 0.0, GeoDataCoordinates::Degree);
0402         centerOn(target, animated);
0403     }
0404 
0405     void MarbleAbstractPresenter::centerOn(const GeoDataCoordinates &position, bool animated)
0406     {
0407         GeoDataLookAt target = lookAt();
0408         target.setCoordinates(position);
0409         flyTo(target, animated ? Automatic : Instant);
0410     }
0411 
0412     void MarbleAbstractPresenter::centerOn(const GeoDataLatLonBox &box, bool animated)
0413     {
0414         if (box.isEmpty())
0415         {
0416             return;
0417         }
0418 
0419         int newRadius = radius();
0420         ViewportParams* viewparams = map()->viewport();
0421         //prevent divide by zero
0422         if(box.height() && box.width())
0423         {
0424             //work out the needed zoom level
0425             int const horizontalRadius = ( 0.25 * M_PI ) * (viewparams->height() / box.height());
0426             int const verticalRadius = ( 0.25 * M_PI ) * (viewparams->width() / box.width());
0427             newRadius = qMin<int>(horizontalRadius, verticalRadius );
0428             newRadius = qMax<int>(radius(minimumZoom()), qMin<int>(newRadius, radius(maximumZoom())));
0429         }
0430 
0431         //move the map
0432         GeoDataLookAt target;
0433         target.setCoordinates(box.center());
0434         target.setAltitude(box.center().altitude());
0435         target.setRange(KM2METER * distanceFromRadius(newRadius));
0436         flyTo(target, animated ? Automatic : Instant);
0437     }
0438 
0439     void MarbleAbstractPresenter::centerOn(const GeoDataPlacemark& placemark, bool animated)
0440     {
0441         const GeoDataLookAt *lookAt(placemark.lookAt());
0442         if (lookAt)
0443         {
0444             flyTo(*lookAt, animated ? Automatic : Instant);
0445         }
0446         else
0447         {
0448             bool icon;
0449             GeoDataCoordinates coords = placemark.coordinate(model()->clock()->dateTime(), &icon);
0450             if (icon)
0451             {
0452                 centerOn(coords, animated);
0453             }
0454             else
0455             {
0456                 centerOn(placemark.geometry()->latLonAltBox(), animated);
0457             }
0458         }
0459     }
0460 
0461     void MarbleAbstractPresenter::headingOn(qreal heading)
0462     {
0463         map()->setHeading(heading);
0464     }
0465 
0466     void MarbleAbstractPresenter::setCenterLatitude(qreal lat, FlyToMode mode)
0467     {
0468         centerOn(centerLongitude(), lat, mode);
0469     }
0470 
0471     void MarbleAbstractPresenter::setCenterLongitude(qreal lon, FlyToMode mode)
0472     {
0473         centerOn(lon, centerLatitude(), mode);
0474     }
0475 
0476     qreal MarbleAbstractPresenter::centerLatitude() const
0477     {
0478         return map()->centerLatitude();
0479     }
0480 
0481     qreal MarbleAbstractPresenter::centerLongitude() const
0482     {
0483         return map()->centerLongitude();
0484     }
0485 
0486     ViewContext MarbleAbstractPresenter::viewContext() const
0487     {
0488         return map()->viewContext();
0489     }
0490 
0491     void MarbleAbstractPresenter::setViewContext(ViewContext viewContext)
0492     {
0493         map()->setViewContext(viewContext);
0494     }
0495 
0496     bool MarbleAbstractPresenter::animationsEnabled() const
0497     {
0498         return m_animationsEnabled;
0499     }
0500 
0501     void MarbleAbstractPresenter::setAnimationsEnabled(bool enabled)
0502     {
0503         m_animationsEnabled = enabled;
0504     }
0505 
0506     int MarbleAbstractPresenter::logzoom() const
0507     {
0508         return m_logzoom;
0509     }
0510 
0511     void MarbleAbstractPresenter::setLogzoom(int value)
0512     {
0513         m_logzoom = value;
0514     }
0515 
0516     int MarbleAbstractPresenter::zoomStep() const
0517     {
0518         return m_zoomStep;
0519     }
0520 
0521     qreal MarbleAbstractPresenter::viewAngle() const
0522     {
0523         return m_viewAngle;
0524     }
0525 
0526     MarbleMap* MarbleAbstractPresenter::map()
0527     {
0528         return m_map;
0529     }
0530 
0531     const MarbleMap* MarbleAbstractPresenter::map() const
0532     {
0533         return m_map;
0534     }
0535 
0536     MarbleModel* MarbleAbstractPresenter::model()
0537     {
0538         return m_map->model();
0539     }
0540 
0541     const MarbleModel* MarbleAbstractPresenter::model() const
0542     {
0543         return m_map->model();
0544     }
0545 
0546     ViewportParams* MarbleAbstractPresenter::viewport()
0547     {
0548         return map()->viewport();
0549     }
0550 
0551     const ViewportParams* MarbleAbstractPresenter::viewport() const
0552     {
0553         return map()->viewport();
0554     }
0555 
0556     void MarbleAbstractPresenter::setDistance(qreal newDistance)
0557     {
0558         qreal minDistance = 0.001;
0559 
0560         if (newDistance <= minDistance)
0561         {
0562             mDebug() << "Invalid distance: 0 m";
0563             newDistance = minDistance;
0564         }
0565 
0566         int newRadius = radiusFromDistance(newDistance);
0567         setRadius(newRadius);
0568     }
0569 
0570     void MarbleAbstractPresenter::setSelection(const QRect& region)
0571     {
0572         QPoint tl = region.topLeft();
0573         QPoint br = region.bottomRight();
0574         mDebug() << "Selection region: (" << tl.x() << ", " <<  tl.y() << ") ("
0575             << br.x() << ", " << br.y() << ")" << endl;
0576 
0577         const GeoDataLatLonAltBox box = viewport()->latLonAltBox(region);
0578 
0579         emit regionSelected(box);
0580      }
0581 
0582 }
0583 
0584 #include "moc_MarbleAbstractPresenter.cpp"
0585