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