diff --git a/src/lib/marble/MarbleAbstractPresenter.cpp b/src/lib/marble/MarbleAbstractPresenter.cpp index 1e930115c..8613d89e4 100644 --- a/src/lib/marble/MarbleAbstractPresenter.cpp +++ b/src/lib/marble/MarbleAbstractPresenter.cpp @@ -1,598 +1,603 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2010-2012 Bernhard Beschow // Copyright 2012 Mohammed Nafees // Copyright 2014 Adam Dabrowski // #include #include #include #include #include #include "MarbleMap.h" #include "MarbleModel.h" #include #include "GeoDataGeometry.h" #include "GeoDataLatLonAltBox.h" #include #include #include #include namespace Marble { MarbleAbstractPresenter::MarbleAbstractPresenter(MarbleMap *map, QObject *parent) : QObject(parent) ,m_map(map) ,m_physics(this) ,m_animationsEnabled(false) ,m_logzoom(0) ,m_zoomStep(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 60 : 40) ,m_viewAngle(110) { } MarbleAbstractPresenter::~MarbleAbstractPresenter() { } qreal MarbleAbstractPresenter::zoom(qreal radius) const { return (200.0 * log(radius)); } qreal MarbleAbstractPresenter::radius(qreal zoom) const { return pow(M_E, (zoom / 200.0)); } void MarbleAbstractPresenter::rotateBy(const qreal deltaLon, const qreal deltaLat, FlyToMode mode) { Quaternion rotPhi(1.0, deltaLat / 180.0, 0.0, 0.0); Quaternion rotTheta(1.0, 0.0, deltaLon / 180.0, 0.0); Quaternion axis = map()->viewport()->planetAxis(); axis = rotTheta * axis; axis *= rotPhi; axis.normalize(); const qreal lat = -axis.pitch(); const qreal lon = axis.yaw(); GeoDataLookAt target = lookAt(); target.setLongitude(lon); target.setLatitude(lat); flyTo(target, mode); } void MarbleAbstractPresenter::flyTo(const GeoDataLookAt &newLookAt, FlyToMode mode) { if (!m_animationsEnabled || mode == Instant) { const int radius = qRound(radiusFromDistance(newLookAt.range() * METER2KM)); qreal const zoomVal = zoom(radius); // Prevent exceeding zoom range. Note: Bounding to range is not useful here if (qRound(zoomVal) >= minimumZoom() && qRound(zoomVal) <= maximumZoom()) { map()->setRadius(radius); m_logzoom = qRound(zoom(radius)); GeoDataCoordinates::Unit deg = GeoDataCoordinates::Degree; map()->centerOn(newLookAt.longitude(deg), newLookAt.latitude(deg)); emit zoomChanged(m_logzoom); emit distanceChanged(distanceString()); } } else { m_physics.flyTo(newLookAt, mode); } } QString MarbleAbstractPresenter::distanceString() const { // distance() returns data in km, so translating to meters qreal dist = distance() * KM2METER, convertedDistance; MarbleLocale::MeasureUnit unit; MarbleLocale *locale = MarbleGlobal::getInstance()->locale(); locale->meterToTargetUnit(dist, locale->measurementSystem(), convertedDistance, unit); QString unitString = locale->unitAbbreviation(unit); return QString("%L1 %2").arg(convertedDistance, 8, 'f', 1, QLatin1Char(' ')) .arg(unitString); } GeoDataLookAt MarbleAbstractPresenter::lookAt() const { GeoDataLookAt result; result.setLongitude(map()->viewport()->centerLongitude()); result.setLatitude(map()->viewport()->centerLatitude()); result.setAltitude(0.0); result.setRange(distance() * KM2METER); return result; } qreal MarbleAbstractPresenter::distance() const { return distanceFromRadius(radius()); } qreal MarbleAbstractPresenter::distanceFromRadius(qreal radius) const { // Due to Marble's orthographic projection ("we have no focus") // it's actually not possible to calculate a "real" distance. // Additionally the viewing angle of the earth doesn't adjust to // the window's size. // // So the only possible workaround is to come up with a distance // definition which gives a reasonable approximation of // reality. Therefore we assume that the average window width // (about 800 pixels) equals the viewing angle of a human being. return (model()->planet()->radius() * 0.4 / radius / tan(0.5 * m_viewAngle * DEG2RAD)); } qreal MarbleAbstractPresenter::radiusFromDistance(qreal distance) const { return model()->planet()->radius() / (distance * tan(0.5 * m_viewAngle * DEG2RAD) / 0.4 ); } int MarbleAbstractPresenter::polarity() const { return map()->viewport()->polarity(); } int MarbleAbstractPresenter::zoom() const { return m_logzoom; } int MarbleAbstractPresenter::minimumZoom() const { return map()->minimumZoom(); } int MarbleAbstractPresenter::maximumZoom() const { return map()->maximumZoom(); } void MarbleAbstractPresenter::setZoom(int newZoom, FlyToMode mode) { // It won't fly anyway. So we should do everything to keep the zoom value. if (!m_animationsEnabled || mode == Instant) { // Check for under and overflow. if (newZoom < minimumZoom()) newZoom = minimumZoom(); else if (newZoom > maximumZoom()) newZoom = maximumZoom(); // Prevent infinite loops. if (newZoom == m_logzoom) return; map()->setRadius(radius(newZoom)); m_logzoom = newZoom; emit zoomChanged(m_logzoom); emit distanceChanged(distanceString()); } else { GeoDataLookAt target = lookAt(); target.setRange(KM2METER * distanceFromZoom(newZoom)); flyTo(target, mode); } } void MarbleAbstractPresenter::zoomView(int zoom, FlyToMode mode) { setZoom(zoom, mode); } void MarbleAbstractPresenter::zoomViewBy(int zoomStep, FlyToMode mode) { setZoom(zoom() + zoomStep, mode); } void MarbleAbstractPresenter::zoomIn(FlyToMode mode) { if (map()->tileZoomLevel() < 0) { zoomViewBy(m_zoomStep, mode); } else { qreal radiusVal = map()->preferredRadiusCeil(map()->radius() / 0.95); radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) ); GeoDataLookAt target = lookAt(); target.setRange(KM2METER * distanceFromRadius(radiusVal)); flyTo(target, mode); } } void MarbleAbstractPresenter::zoomOut(FlyToMode mode) { if (map()->tileZoomLevel() <= 0) { zoomViewBy(-m_zoomStep, mode); } else { qreal radiusVal = map()->preferredRadiusFloor(map()->radius() * 0.95); radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) ); GeoDataLookAt target = lookAt(); target.setRange(KM2METER * distanceFromRadius(radiusVal)); flyTo(target, mode); } } void MarbleAbstractPresenter::zoomAtBy(const QPoint &pos, int zoomStep) { qreal radiusVal; if (map()->tileZoomLevel() <= 0) { radiusVal = radius(zoom() + zoomStep); } else { radiusVal = zoomStep > 0 ? map()->preferredRadiusCeil(map()->radius() / 0.95) : map()->preferredRadiusFloor(map()->radius() * 0.95); radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) ); } zoomAt(pos, distanceFromRadius(radiusVal)); } qreal MarbleAbstractPresenter::distanceFromZoom(qreal zoom) const { return distanceFromRadius(radius(zoom)); } qreal MarbleAbstractPresenter::zoomFromDistance(qreal distance) const { return zoom(radiusFromDistance(distance)); } void MarbleAbstractPresenter::goHome(FlyToMode mode) { qreal homeLon = 0; qreal homeLat = 0; int homeZoom = 0; model()->home(homeLon, homeLat, homeZoom); GeoDataLookAt target; target.setLongitude(homeLon, GeoDataCoordinates::Degree); target.setLatitude(homeLat, GeoDataCoordinates::Degree); target.setRange(1000 * distanceFromZoom(homeZoom)); flyTo(target, mode); } void MarbleAbstractPresenter::moveByStep(int stepsRight, int stepsDown, FlyToMode mode) { int polarity = map()->viewport()->polarity(); qreal left = polarity * stepsRight * moveStep(); qreal down = stepsDown * moveStep(); rotateBy(left, down, mode); } qreal MarbleAbstractPresenter::moveStep() const { int width = map()->width(); int height = map()->height(); if (radius() < qSqrt((qreal)(width * width + height * height))) return 180.0 * 0.1; else return 180.0 * qAtan((qreal)width / (qreal)(2 * radius())) * 0.2; } int MarbleAbstractPresenter::radius() const { return map()->radius(); } void MarbleAbstractPresenter::setRadius(int radiusVal) { Q_ASSERT(radiusVal >= 0); bool adjustRadius = radiusVal != map()->radius(); qreal const zoomVal = zoom(radiusVal); // Prevent exceeding zoom range if (zoomVal < minimumZoom()) { radiusVal = radius(minimumZoom()); adjustRadius = true; } else if (zoomVal > maximumZoom()) { radiusVal = radius(maximumZoom()); adjustRadius = true; } if (adjustRadius) { map()->setRadius(radiusVal); m_logzoom = qRound(zoomVal); emit zoomChanged(m_logzoom); emit distanceChanged(distanceString()); } } //Moved from MarbleWidgetInputHandlerPrivate - fits more here now void MarbleAbstractPresenter::zoomAt(const QPoint &pos, qreal newDistance) { Q_ASSERT(newDistance > 0.0); qreal destLat; qreal destLon; if (!map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Degree)) { return; } ViewportParams* now = map()->viewport(); qreal x(0), y(0); if (!now->screenCoordinates(destLon * DEG2RAD, destLat * DEG2RAD, x, y)) { return; } ViewportParams soon; soon.setProjection(now->projection()); soon.centerOn(now->centerLongitude(), now->centerLatitude()); soon.setSize(now->size()); qreal newRadius = radiusFromDistance(newDistance); soon.setRadius(newRadius); qreal mouseLon, mouseLat; if (!soon.geoCoordinates(int(x), int(y), mouseLon, mouseLat, GeoDataCoordinates::Degree )) { return; } const qreal lon = destLon - (mouseLon - map()->centerLongitude()); const qreal lat = destLat - (mouseLat - map()->centerLatitude()); GeoDataLookAt lookAt; lookAt.setLongitude(lon, GeoDataCoordinates::Degree); lookAt.setLatitude(lat, GeoDataCoordinates::Degree); lookAt.setAltitude(0.0); lookAt.setRange(newDistance * KM2METER); map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat, 0, GeoDataCoordinates::Degree)); flyTo(lookAt, Linear); } void MarbleAbstractPresenter::moveTo(const QPoint &pos, qreal factor) { Q_ASSERT(factor > 0.0); qreal destLat; qreal destLon; map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Radian); GeoDataLookAt lookAt; lookAt.setLongitude(destLon); lookAt.setLatitude(destLat); lookAt.setAltitude(0.0); lookAt.setRange(distance() * factor * KM2METER); flyTo(lookAt); } void MarbleAbstractPresenter::centerOn(const qreal lon, const qreal lat, bool animated) { GeoDataCoordinates target(lon, lat, 0.0, GeoDataCoordinates::Degree); centerOn(target, animated); } void MarbleAbstractPresenter::centerOn(const GeoDataCoordinates &position, bool animated) { GeoDataLookAt target = lookAt(); target.setCoordinates(position); flyTo(target, animated ? Automatic : Instant); } void MarbleAbstractPresenter::centerOn(const GeoDataLatLonBox &box, bool animated) { if (box.isEmpty()) { return; } int newRadius = radius(); ViewportParams* viewparams = map()->viewport(); //prevent divide by zero if(box.height() && box.width()) { //work out the needed zoom level int const horizontalRadius = ( 0.25 * M_PI ) * (viewparams->height() / box.height()); int const verticalRadius = ( 0.25 * M_PI ) * (viewparams->width() / box.width()); newRadius = qMin(horizontalRadius, verticalRadius ); newRadius = qMax(radius(minimumZoom()), qMin(newRadius, radius(maximumZoom()))); } //move the map GeoDataLookAt target; target.setCoordinates(box.center()); target.setAltitude(box.center().altitude()); target.setRange(KM2METER * distanceFromRadius(newRadius)); flyTo(target, animated ? Automatic : Instant); } void MarbleAbstractPresenter::centerOn(const GeoDataPlacemark& placemark, bool animated) { const GeoDataLookAt *lookAt(placemark.lookAt()); if (lookAt) { flyTo(*lookAt, animated ? Automatic : Instant); } else { bool icon; GeoDataCoordinates coords = placemark.coordinate(model()->clock()->dateTime(), &icon); if (icon) { centerOn(coords, animated); } else { centerOn(placemark.geometry()->latLonAltBox(), animated); } } } + void MarbleAbstractPresenter::headingOn(qreal heading) + { + map()->setHeading(heading); + } + void MarbleAbstractPresenter::setCenterLatitude(qreal lat, FlyToMode mode) { centerOn(centerLongitude(), lat, mode); } void MarbleAbstractPresenter::setCenterLongitude(qreal lon, FlyToMode mode) { centerOn(lon, centerLatitude(), mode); } qreal MarbleAbstractPresenter::centerLatitude() const { return map()->centerLatitude(); } qreal MarbleAbstractPresenter::centerLongitude() const { return map()->centerLongitude(); } ViewContext MarbleAbstractPresenter::viewContext() const { return map()->viewContext(); } void MarbleAbstractPresenter::setViewContext(ViewContext viewContext) { map()->setViewContext(viewContext); } bool MarbleAbstractPresenter::animationsEnabled() const { return m_animationsEnabled; } void MarbleAbstractPresenter::setAnimationsEnabled(bool enabled) { m_animationsEnabled = enabled; } int MarbleAbstractPresenter::logzoom() const { return m_logzoom; } void MarbleAbstractPresenter::setLogzoom(int value) { m_logzoom = value; } int MarbleAbstractPresenter::zoomStep() const { return m_zoomStep; } qreal MarbleAbstractPresenter::viewAngle() const { return m_viewAngle; } MarbleMap* MarbleAbstractPresenter::map() { return m_map; } const MarbleMap* MarbleAbstractPresenter::map() const { return m_map; } MarbleModel* MarbleAbstractPresenter::model() { return m_map->model(); } const MarbleModel* MarbleAbstractPresenter::model() const { return m_map->model(); } ViewportParams* MarbleAbstractPresenter::viewport() { return map()->viewport(); } const ViewportParams* MarbleAbstractPresenter::viewport() const { return map()->viewport(); } void MarbleAbstractPresenter::setDistance(qreal newDistance) { qreal minDistance = 0.001; if (newDistance <= minDistance) { mDebug() << "Invalid distance: 0 m"; newDistance = minDistance; } int newRadius = radiusFromDistance(newDistance); setRadius(newRadius); } void MarbleAbstractPresenter::setSelection(const QRect& region) { QPoint tl = region.topLeft(); QPoint br = region.bottomRight(); mDebug() << "Selection region: (" << tl.x() << ", " << tl.y() << ") (" << br.x() << ", " << br.y() << ")" << endl; GeoDataLatLonAltBox box = viewport()->latLonAltBox(region); // NOTE: coordinates as lon1, lat1, lon2, lat2 (or West, North, East, South) // as left/top, right/bottom rectangle. QList coordinates; coordinates << box.west(GeoDataCoordinates::Degree) << box.north(GeoDataCoordinates::Degree) << box.east(GeoDataCoordinates::Degree) << box.south(GeoDataCoordinates::Degree); mDebug() << "West: " << coordinates[0] << " North: " << coordinates[1] << " East: " << coordinates[2] << " South: " << coordinates[3] << endl; emit regionSelected(coordinates); } } #include "moc_MarbleAbstractPresenter.cpp" diff --git a/src/lib/marble/MarbleAbstractPresenter.h b/src/lib/marble/MarbleAbstractPresenter.h index ca3ad6ce6..8ec1f2c2a 100644 --- a/src/lib/marble/MarbleAbstractPresenter.h +++ b/src/lib/marble/MarbleAbstractPresenter.h @@ -1,154 +1,155 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2008 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2014 Adam Dabrowski // #ifndef MARBLEABSTRACTPRESENTER_H #define MARBLEABSTRACTPRESENTER_H #include #include "GeoDataLatLonBox.h" #include "MarblePhysics.h" #include namespace Marble { class GeoDataPlacemark; class GeoDataLookAt; class MarbleMap; class MarbleModel; class ViewportParams; class MARBLE_EXPORT MarbleAbstractPresenter : public QObject { Q_OBJECT Q_SIGNALS: void zoomChanged(int zoom); void distanceChanged(const QString& distanceString); /** This signal is emitted when a new rectangle region is selected over the map * The list of double values includes coordinates in degrees using the following: * lon1, lat1, lon2, lat2 (or West, North, East, South) as left/top, right/bottom rectangle. */ void regionSelected(const QList&); public: explicit MarbleAbstractPresenter(MarbleMap *map, QObject *parent = 0); ~MarbleAbstractPresenter() override; qreal moveStep() const; int radius() const; GeoDataLookAt lookAt() const; QString distanceString() const; /** * @brief Approximated altitude of the camera in km */ qreal distance() const; /** * @brief An approximate distance from @p radius * @param radius radius of planet disc in screen pixels */ qreal distanceFromRadius(qreal radius) const; /** * @brief The radius of the rendered planet disc derived from the approximate apparent @p distance */ qreal radiusFromDistance(qreal distance) const; /** * @brief Rotate the globe in the given direction in discrete steps * @param stepsRight Number of steps to go right. Negative values go left. * @param stepsDown Number of steps to go down. Negative values go up. * @param mode Interpolation mode to use when traveling to the target */ void moveByStep(int stepsRight, int stepsDown, FlyToMode mode = Automatic); int polarity() const; int zoom() const; int minimumZoom() const; int maximumZoom() const; qreal distanceFromZoom(qreal zoom) const; qreal zoomFromDistance(qreal distance) const; void zoomAt(const QPoint &pos, qreal newDistance); void moveTo(const QPoint &pos, qreal factor); qreal centerLongitude() const; qreal centerLatitude() const; ViewContext viewContext() const; qreal zoom(qreal radius) const; qreal radius(qreal zoom) const; MarbleMap* map(); MarbleModel* model(); const MarbleMap* map() const; const MarbleModel* model() const; int logzoom() const; void setLogzoom(int value); int zoomStep() const; qreal viewAngle() const; bool animationsEnabled() const; ViewportParams *viewport(); const ViewportParams* viewport() const; public Q_SLOTS: void rotateBy(const qreal deltaLon, const qreal deltaLat, FlyToMode mode = Instant); void flyTo(const GeoDataLookAt &newLookAt, FlyToMode mode = Automatic); void goHome(FlyToMode mode = Automatic); void setZoom(int newZoom, FlyToMode mode = Instant); void zoomView(int zoom, FlyToMode mode = Instant); void zoomViewBy(int zoomStep, FlyToMode mode = Instant); void zoomIn(FlyToMode mode = Automatic); void zoomOut(FlyToMode mode = Automatic); void zoomAtBy(const QPoint &pos, int zoomStep); void setViewContext(ViewContext viewContext); void centerOn(const qreal lon, const qreal lat, bool animated = false); void centerOn(const GeoDataCoordinates &point, bool animated = false); void centerOn(const GeoDataLatLonBox& box, bool animated = false); void centerOn(const GeoDataPlacemark& placemark, bool animated = false); + void headingOn(qreal heading); void setCenterLatitude(qreal lat, FlyToMode mode); void setCenterLongitude(qreal lon, FlyToMode mode); void setAnimationsEnabled(bool enabled); void setRadius(int radius); void setDistance(qreal newDistance); void setSelection(const QRect& region); private: MarbleMap *const m_map; MarblePhysics m_physics; bool m_animationsEnabled; int m_logzoom; int m_zoomStep; const qreal m_viewAngle; }; } #endif // MARBLEABSTRACTPRESENTER_H diff --git a/src/lib/marble/MarbleInputHandler.cpp b/src/lib/marble/MarbleInputHandler.cpp index 837f725d7..218268f56 100644 --- a/src/lib/marble/MarbleInputHandler.cpp +++ b/src/lib/marble/MarbleInputHandler.cpp @@ -1,947 +1,990 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2014 Adam Dabrowski // #include "MarbleInputHandler.h" #include #include #include #include #include #include #include #include #include "kineticmodel.h" #include "MarbleGlobal.h" #include "MarbleDebug.h" #include "MarbleMap.h" #include "GeoDataCoordinates.h" #include "MarbleAbstractPresenter.h" #include "ViewportParams.h" #include "AbstractFloatItem.h" #include "AbstractDataPluginItem.h" #include "RenderPlugin.h" namespace Marble { const int TOOLTIP_START_INTERVAL = 1000; class Q_DECL_HIDDEN MarbleInputHandler::Protected { public: Protected(MarbleAbstractPresenter *marblePresenter); MarbleAbstractPresenter *const m_marblePresenter; bool m_positionSignalConnected; QTimer *m_mouseWheelTimer; Qt::MouseButtons m_disabledMouseButtons; qreal m_wheelZoomTargetDistance; bool m_panViaArrowsEnabled; bool m_inertialEarthRotation; int m_steps; const int m_discreteZoomSteps = 120; }; MarbleInputHandler::Protected::Protected(MarbleAbstractPresenter *marblePresenter) : m_marblePresenter( marblePresenter ), m_positionSignalConnected( false ), m_mouseWheelTimer( 0 ), m_disabledMouseButtons( Qt::NoButton ), m_wheelZoomTargetDistance( 0.0 ), m_panViaArrowsEnabled( true ), m_inertialEarthRotation( true ), m_steps(0) { } MarbleInputHandler::MarbleInputHandler(MarbleAbstractPresenter *marblePresenter) : d(new Protected(marblePresenter)) { d->m_mouseWheelTimer = new QTimer( this ); connect(d->m_mouseWheelTimer, SIGNAL(timeout()), this, SLOT(restoreViewContext())); connect(d->m_marblePresenter->map(), SIGNAL(renderPluginInitialized(RenderPlugin*)), this, SLOT(installPluginEventFilter(RenderPlugin*))); } MarbleInputHandler::~MarbleInputHandler() { delete d->m_mouseWheelTimer; delete d; } void MarbleInputHandler::setPositionSignalConnected(bool connected) { d->m_positionSignalConnected = connected; } bool MarbleInputHandler::isPositionSignalConnected() const { return d->m_positionSignalConnected; } void MarbleInputHandler::setMouseButtonPopupEnabled(Qt::MouseButton mouseButton, bool enabled) { if (enabled) { d->m_disabledMouseButtons &= ~Qt::MouseButtons(mouseButton); } else { d->m_disabledMouseButtons |= mouseButton; } } bool MarbleInputHandler::isMouseButtonPopupEnabled(Qt::MouseButton mouseButton) const { return !(d->m_disabledMouseButtons & mouseButton); } void MarbleInputHandler::setPanViaArrowsEnabled(bool enabled) { d->m_panViaArrowsEnabled = enabled; } bool MarbleInputHandler::panViaArrowsEnabled() const { return d->m_panViaArrowsEnabled; } void MarbleInputHandler::setInertialEarthRotationEnabled(bool enabled) { d->m_inertialEarthRotation = enabled; } bool MarbleInputHandler::inertialEarthRotationEnabled() const { return d->m_inertialEarthRotation; } class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private { public: Private(); ~Private(); QPixmap m_curpmtl; QPixmap m_curpmtc; QPixmap m_curpmtr; QPixmap m_curpmcr; QPixmap m_curpmcl; QPixmap m_curpmbl; QPixmap m_curpmbc; QPixmap m_curpmbr; QCursor m_arrowCur[3][3]; // Indicates if the left mouse button has been pressed already. bool m_leftPressed; - // Indicates whether the drag was started by a click above or below the visible pole. - int m_leftPressedDirection; // Indicates if the middle mouse button has been pressed already. bool m_midPressed; // The mouse pointer x position when the left mouse button has been pressed. int m_leftPressedX; // The mouse pointer y position when the left mouse button has been pressed. int m_leftPressedY; // The mouse pointer y position when the middle mouse button has been pressed. int m_midPressedY; int m_startingRadius; + // Indicates if the right mouse button has been pressed already. + bool m_rightPressed; + // Point where the right mouse button has been pressed on. + QPoint m_rightOrigin; + // Position to calculate the heading. + // Indicates previous position since mouse has been moved. + QPoint m_rightPosition; + // Indicates the heading when the right mouse button has been pressed + // and mouse is moving. + qreal m_heading; + // The center longitude in radian when the left mouse button has been pressed. qreal m_leftPressedLon; // The center latitude in radian when the left mouse button has been pressed. qreal m_leftPressedLat; int m_dragThreshold; QTimer m_lmbTimer; QTimer m_pressAndHoldTimer; // Models to handle the kinetic spinning. KineticModel m_kineticSpinning; QPoint m_selectionOrigin; QPointer m_lastToolTipItem; QTimer m_toolTipTimer; QPoint m_toolTipPosition; }; MarbleDefaultInputHandler::Private::Private() : m_leftPressed(false), m_midPressed(false), + m_rightPressed(false), + m_heading(0), m_dragThreshold(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 15 : 3) { m_curpmtl.load(QStringLiteral(":/marble/cursor/tl.png")); m_curpmtc.load(QStringLiteral(":/marble/cursor/tc.png")); m_curpmtr.load(QStringLiteral(":/marble/cursor/tr.png")); m_curpmcr.load(QStringLiteral(":/marble/cursor/cr.png")); m_curpmcl.load(QStringLiteral(":/marble/cursor/cl.png")); m_curpmbl.load(QStringLiteral(":/marble/cursor/bl.png")); m_curpmbc.load(QStringLiteral(":/marble/cursor/bc.png")); m_curpmbr.load(QStringLiteral(":/marble/cursor/br.png")); m_arrowCur[0][0] = QCursor( m_curpmtl, 2, 2 ); m_arrowCur[1][0] = QCursor( m_curpmtc, 10, 3 ); m_arrowCur[2][0] = QCursor( m_curpmtr, 19, 2 ); m_arrowCur[0][1] = QCursor( m_curpmcl, 3, 10 ); m_arrowCur[1][1] = QCursor( Qt::OpenHandCursor ); m_arrowCur[2][1] = QCursor( m_curpmcr, 18, 10 ); m_arrowCur[0][2] = QCursor( m_curpmbl, 2, 19 ); m_arrowCur[1][2] = QCursor( m_curpmbc, 11, 18 ); m_arrowCur[2][2] = QCursor( m_curpmbr, 19, 19 ); } MarbleDefaultInputHandler::Private::~Private() { } MarbleDefaultInputHandler::MarbleDefaultInputHandler(MarbleAbstractPresenter *marblePresenter) : MarbleInputHandler(marblePresenter), d(new Private()) { d->m_toolTipTimer.setSingleShot(true); d->m_toolTipTimer.setInterval(TOOLTIP_START_INTERVAL); connect(&d->m_toolTipTimer, SIGNAL(timeout()), this, SLOT(openItemToolTip())); d->m_lmbTimer.setSingleShot(true); connect(&d->m_lmbTimer, SIGNAL(timeout()), this, SLOT(lmbTimeout())); d->m_kineticSpinning.setUpdateInterval(35); connect(&d->m_kineticSpinning, SIGNAL(positionChanged(qreal,qreal)), MarbleInputHandler::d->m_marblePresenter, SLOT(centerOn(qreal,qreal))); + connect(&d->m_kineticSpinning, SIGNAL(headingChanged(qreal)), + MarbleInputHandler::d->m_marblePresenter, SLOT(headingOn(qreal))); connect(&d->m_kineticSpinning, SIGNAL(finished()), SLOT(restoreViewContext())); // Left and right mouse button signals. connect(this, SIGNAL(rmbRequest(int,int)), this, SLOT(showRmbMenu(int,int))); connect(this, SIGNAL(lmbRequest(int,int)), this, SLOT(showLmbMenu(int,int))); d->m_pressAndHoldTimer.setInterval(800); d->m_pressAndHoldTimer.setSingleShot(true); connect(&d->m_pressAndHoldTimer, SIGNAL(timeout()), this, SLOT(handlePressAndHold())); } MarbleDefaultInputHandler::~MarbleDefaultInputHandler() { delete d; } void MarbleDefaultInputHandler::lmbTimeout() { if (!selectionRubber()->isVisible()) { qreal clickedLon = 0; qreal clickedLat = 0; bool isPointOnGlobe = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates( d->m_leftPressedX, d->m_leftPressedY, clickedLon, clickedLat, GeoDataCoordinates::Degree ); emit lmbRequest(d->m_leftPressedX, d->m_leftPressedY); /** * emit mouse click only when the clicked * position is within the globe. */ if ( isPointOnGlobe ) { emit mouseClickGeoPosition( clickedLon, clickedLat, GeoDataCoordinates::Degree ); } } } void MarbleInputHandler::restoreViewContext() { // Needs to stop the timer since it repeats otherwise. d->m_mouseWheelTimer->stop(); // Redraw the map with the quality set for Still (if necessary). d->m_marblePresenter->setViewContext(Still); d->m_marblePresenter->map()->viewport()->resetFocusPoint(); d->m_wheelZoomTargetDistance = 0.0; } void MarbleDefaultInputHandler::hideSelectionIfCtrlReleased(QEvent *e) { if (selectionRubber()->isVisible() && e->type() == QEvent::MouseMove) { QMouseEvent *event = static_cast(e); if (!(event->modifiers() & Qt::ControlModifier)) { selectionRubber()->hide(); } } } bool MarbleDefaultInputHandler::handleDoubleClick(QMouseEvent *event) { qreal mouseLon; qreal mouseLat; const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(), mouseLon, mouseLat, GeoDataCoordinates::Radian); if(isMouseAboveMap) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); MarbleInputHandler::d->m_marblePresenter->moveTo(event->pos(), 0.67); } return acceptMouse(); } bool MarbleDefaultInputHandler::handleWheel(QWheelEvent *wheelevt) { MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; marblePresenter->setViewContext(Animation); if( (MarbleInputHandler::d->m_steps > 0 && wheelevt->delta() < 0) || (MarbleInputHandler::d->m_steps < 0 && wheelevt->delta() > 0) ) { MarbleInputHandler::d->m_steps = wheelevt->delta(); } else { MarbleInputHandler::d->m_steps += wheelevt->delta(); } if (marblePresenter->map()->discreteZoom()) { if(qAbs(MarbleInputHandler::d->m_steps) >= MarbleInputHandler::d->m_discreteZoomSteps) { marblePresenter->zoomAtBy(wheelevt->pos(), MarbleInputHandler::d->m_steps); MarbleInputHandler::d->m_steps = 0; } } else { qreal zoom = marblePresenter->zoom(); qreal target = MarbleInputHandler::d->m_wheelZoomTargetDistance; if (marblePresenter->animationsEnabled() && target > 0.0) { // Do not use intermediate (interpolated) distance values caused by animations zoom = marblePresenter->zoomFromDistance(target); } qreal newDistance = marblePresenter->distanceFromZoom(zoom + MarbleInputHandler::d->m_steps); MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance; marblePresenter->zoomAt(wheelevt->pos(), newDistance); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.jumpToPosition(MarbleInputHandler::d->m_marblePresenter->centerLongitude(), MarbleInputHandler::d->m_marblePresenter->centerLatitude()); } MarbleInputHandler::d->m_steps = 0; } MarbleInputHandler::d->m_mouseWheelTimer->start(400); return true; } bool MarbleDefaultInputHandler::handlePinch(const QPointF ¢er, qreal scaleFactor, Qt::GestureState state) { qreal destLat; qreal destLon; MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; bool isValid = marblePresenter->map()->geoCoordinates(center.x(), center.y(), destLon, destLat, GeoDataCoordinates::Radian ); if (isValid) { marblePresenter->map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat)); } qreal zoom, target, newDistance; qreal zoomDelta = scaleFactor > 1.0 ? scaleFactor : -1.0/scaleFactor; switch (state) { case Qt::NoGesture: break; case Qt::GestureStarted: marblePresenter->setViewContext(Animation); d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); d->m_midPressed = false; d->m_leftPressed = false; break; case Qt::GestureUpdated: zoom = marblePresenter->zoom(); target = MarbleInputHandler::d->m_wheelZoomTargetDistance; if (marblePresenter->animationsEnabled() && target > 0.0) { // Do not use intermediate (interpolated) distance values caused by animations zoom = marblePresenter->zoomFromDistance(target); } newDistance = marblePresenter->distanceFromZoom(zoom + 20 * zoomDelta); MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance; marblePresenter->zoomAt(center.toPoint(), newDistance); break; case Qt::GestureFinished: marblePresenter->map()->viewport()->resetFocusPoint(); marblePresenter->setViewContext(Still); break; case Qt::GestureCanceled: marblePresenter->map()->viewport()->resetFocusPoint(); marblePresenter->setViewContext(Still); break; } return true; } bool MarbleDefaultInputHandler::handleGesture(QGestureEvent *ge) { QPinchGesture *pinch = static_cast(ge->gesture(Qt::PinchGesture)); if (!pinch) { return false; } qreal scaleFactor = pinch->scaleFactor(); QPointF center = pinch->centerPoint(); return handlePinch(center, scaleFactor, pinch->state()); } void MarbleDefaultInputHandler::checkReleasedMove(QMouseEvent *event) { // To prevent error from lost MouseButtonRelease events if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::LeftButton)) { if (d->m_leftPressed) { d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } else { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } } } if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::MidButton)) { d->m_midPressed = false; } } void MarbleDefaultInputHandler::handleMouseButtonPress(QMouseEvent *event) { if (event->button() == Qt::LeftButton ) { handleLeftMouseButtonPress(event); } if ( event->button() == Qt::MidButton ) { handleMiddleMouseButtonPress(event); } if ( event->button() == Qt::RightButton ) { handleRightMouseButtonPress(event); } } void MarbleDefaultInputHandler::handleLeftMouseButtonPress(QMouseEvent *event) { // silently enable the animation context without triggering a repaint MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(true); MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(false); if (isMouseButtonPopupEnabled(Qt::LeftButton)) { d->m_lmbTimer.start(400); } d->m_leftPressed = true; d->m_midPressed = false; selectionRubber()->hide(); // On the single event of a mouse button press these // values get stored, to enable us to e.g. calculate the // distance of a mouse drag while the mouse button is // still down. d->m_leftPressedX = event->x(); d->m_leftPressedY = event->y(); // Calculate translation of center point d->m_leftPressedLon = MarbleInputHandler::d->m_marblePresenter->centerLongitude(); d->m_leftPressedLat = MarbleInputHandler::d->m_marblePresenter->centerLatitude(); - d->m_leftPressedDirection = 1; - if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.stop(); d->m_kineticSpinning.setPosition(d->m_leftPressedLon, d->m_leftPressedLat); } - // Choose spin direction by taking into account whether we - // drag above or below the visible pole. - if (MarbleInputHandler::d->m_marblePresenter->map()->projection() == Spherical) - { - if (d->m_leftPressedLat >= 0) - { // The visible pole is the north pole - qreal northPoleX, northPoleY; - MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(0.0, 90.0, northPoleX, northPoleY); - if (event->y() < northPoleY) - { - d->m_leftPressedDirection = -1; - } - } - else - { // The visible pole is the south pole - qreal southPoleX, southPoleY; - MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(0.0, -90.0, southPoleX, southPoleY); - if (event->y() > southPoleY) - { - d->m_leftPressedDirection = -1; - } - } - } - if (event->modifiers() & Qt::ControlModifier) { mDebug() << Q_FUNC_INFO << "Starting selection"; d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); d->m_selectionOrigin = event->pos(); selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, QSize())); selectionRubber()->show(); } } void MarbleDefaultInputHandler::handleMiddleMouseButtonPress(QMouseEvent *event) { d->m_midPressed = true; d->m_leftPressed = false; d->m_startingRadius = MarbleInputHandler::d->m_marblePresenter->radius(); d->m_midPressedY = event->y(); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } selectionRubber()->hide(); MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); } void MarbleDefaultInputHandler::handleRightMouseButtonPress(QMouseEvent *event) { - emit rmbRequest(event->x(), event->y()); + d->m_rightPressed = true; + d->m_rightOrigin = event->pos(); + d->m_rightPosition = event->pos(); + d->m_heading = MarbleInputHandler::d->m_marblePresenter->map()->heading(); + if (MarbleInputHandler::d->m_inertialEarthRotation) + { + d->m_kineticSpinning.stop(); + d->m_kineticSpinning.setHeading(d->m_heading); + } } void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { //emit current coordinates to be interpreted //as requested emit mouseClickScreenPosition(d->m_leftPressedX, d->m_leftPressedY); d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } else { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } } if (event->button() == Qt::MidButton) { d->m_midPressed = false; MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::RightButton) { + if (d->m_rightOrigin == event->pos()) + { + emit rmbRequest(event->x(), event->y()); + } + d->m_rightPressed = false; + + if (MarbleInputHandler::d->m_inertialEarthRotation) + { + d->m_kineticSpinning.start(); + } + else + { + MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); + } } if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::LeftButton && selectionRubber()->isVisible()) { mDebug() << Q_FUNC_INFO << "Leaving selection"; MarbleInputHandler::d->m_marblePresenter->setSelection(selectionRubber()->geometry()); selectionRubber()->hide(); } } void MarbleDefaultInputHandler::notifyPosition(bool isMouseAboveMap, qreal mouseLon, qreal mouseLat) { // emit the position string only if the signal got attached if (MarbleInputHandler::d->m_positionSignalConnected) { if (!isMouseAboveMap) { emit mouseMoveGeoPosition(QCoreApplication::translate( "Marble", NOT_AVAILABLE)); } else { QString position = GeoDataCoordinates(mouseLon, mouseLat).toString(); emit mouseMoveGeoPosition(position); } } } void MarbleDefaultInputHandler::adjustCursorShape(const QPoint &mousePosition, const QPoint &mouseDirection) { // Find out if there are data items and if one has defined an action QList dataItems = MarbleInputHandler::d->m_marblePresenter->map()->whichItemAt(mousePosition); bool dataAction = false; QPointer toolTipItem; QList::iterator it = dataItems.begin(); QList::iterator const end = dataItems.end(); for (; it != end && dataAction == false && toolTipItem.isNull(); ++it) { if ((*it)->action()) { dataAction = true; } if (!(*it)->toolTip().isNull() && toolTipItem.isNull()) { toolTipItem = (*it); } } if (toolTipItem.isNull()) { d->m_toolTipTimer.stop(); } else if (!( d->m_lastToolTipItem.data() == toolTipItem.data())) { d->m_toolTipTimer.start(); d->m_lastToolTipItem = toolTipItem; d->m_toolTipPosition = mousePosition; } else { if (!d->m_toolTipTimer.isActive()) { d->m_toolTipTimer.start(); } d->m_toolTipPosition = mousePosition; } if (!dataAction && !MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) { if (!d->m_leftPressed) { d->m_arrowCur [1][1] = QCursor(Qt::OpenHandCursor); } else { d->m_arrowCur [1][1] = QCursor(Qt::ClosedHandCursor); } } else { if (!d->m_leftPressed) { d->m_arrowCur [1][1] = QCursor(Qt::PointingHandCursor); } } if (panViaArrowsEnabled()) { setCursor(d->m_arrowCur[mouseDirection.x()+1][mouseDirection.y()+1]); } else { setCursor(d->m_arrowCur[1][1]); } } QPoint MarbleDefaultInputHandler::mouseMovedOutside(QMouseEvent *event) { //Returns a 2d vector representing the direction in which the mouse left int dirX = 0; int dirY = 0; int polarity = MarbleInputHandler::d->m_marblePresenter->viewport()->polarity(); if (d->m_leftPressed) { d->m_leftPressed = false; if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.start(); } } QRect boundingRect = MarbleInputHandler::d->m_marblePresenter->viewport()->mapRegion().boundingRect(); if (boundingRect.width() != 0) { dirX = (int)( 3 * (event->x() - boundingRect.left()) / boundingRect.width()) - 1; } if (dirX > 1) { dirX = 1; } if (dirX < -1) { dirX = -1; } if (boundingRect.height() != 0) { dirY = (int)(3 * (event->y() - boundingRect.top()) / boundingRect.height()) - 1; } if (dirY > 1) { dirY = 1; } if (dirY < -1) { dirY = -1; } if (event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress && panViaArrowsEnabled() && !d->m_kineticSpinning.hasVelocity()) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); qreal moveStep = MarbleInputHandler::d->m_marblePresenter->moveStep(); if (polarity < 0) { MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(+dirX), moveStep * (qreal)(+dirY)); } else { MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(-dirX), moveStep * (qreal)(+dirY)); } } if (!MarbleInputHandler::d->m_inertialEarthRotation) { MarbleInputHandler::d->m_marblePresenter->setViewContext(Still); } return QPoint(dirX, dirY); } bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event) { QPoint direction; checkReleasedMove(event); // Do not handle (and therefore eat) mouse press and release events // that occur above visible float items. Mouse motion events are still // handled, however. if (event->type() != QEvent::MouseMove && !selectionRubber()->isVisible()) { auto const floatItems = MarbleInputHandler::d->m_marblePresenter->map()->floatItems(); for (AbstractFloatItem *floatItem: floatItems) { if ( floatItem->enabled() && floatItem->visible() && floatItem->contains( event->pos() ) ) { d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); return false; } } } qreal mouseLon; qreal mouseLat; const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(), mouseLon, mouseLat, GeoDataCoordinates::Radian); notifyPosition(isMouseAboveMap, mouseLon, mouseLat); QPoint mousePosition(event->x(), event->y()); if (isMouseAboveMap || selectionRubber()->isVisible() || MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) { if (event->type() == QEvent::MouseButtonPress) { d->m_pressAndHoldTimer.start(); handleMouseButtonPress(event); } if (event->type() == QEvent::MouseButtonRelease) { d->m_pressAndHoldTimer.stop(); handleMouseButtonRelease(event); } // Regarding all kinds of mouse moves: if (d->m_leftPressed && !selectionRubber()->isVisible()) { qreal radius = (qreal)(MarbleInputHandler::d->m_marblePresenter->radius()); - int deltax = event->x() - d->m_leftPressedX; - int deltay = event->y() - d->m_leftPressedY; + qreal deltax = event->x() - d->m_leftPressedX; + qreal deltay = event->y() - d->m_leftPressedY; - if (abs(deltax) > d->m_dragThreshold - || abs(deltay) > d->m_dragThreshold + if (qAbs(deltax) > d->m_dragThreshold + || qAbs(deltay) > d->m_dragThreshold || !d->m_lmbTimer.isActive()) { MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation); d->m_pressAndHoldTimer.stop(); d->m_lmbTimer.stop(); - - const qreal posLon = d->m_leftPressedLon - 90.0 * d->m_leftPressedDirection * deltax / radius; - const qreal posLat = d->m_leftPressedLat + 90.0 * deltay / radius; + const Quaternion rotation = Quaternion::fromEuler( 0, 0, MarbleInputHandler::d->m_marblePresenter->map()->heading() * DEG2RAD ); + Quaternion quat = Quaternion::fromSpherical( - M_PI/2 * deltax / radius, + M_PI/2 * deltay / radius ); + quat.rotateAroundAxis( rotation ); + qreal lon, lat; + quat.getSpherical( lon, lat ); + const qreal posLon = d->m_leftPressedLon + RAD2DEG * lon; + const qreal posLat = d->m_leftPressedLat + RAD2DEG * lat; MarbleInputHandler::d->m_marblePresenter->centerOn(posLon, posLat); if (MarbleInputHandler::d->m_inertialEarthRotation) { d->m_kineticSpinning.setPosition(posLon, posLat); } } } if (d->m_midPressed) { int eventy = event->y(); int dy = d->m_midPressedY - eventy; MarbleInputHandler::d->m_marblePresenter->setRadius(d->m_startingRadius * pow(1.005, dy)); } + if (d->m_rightPressed) + { + qreal centerX, centerY; + MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates( + MarbleInputHandler::d->m_marblePresenter->centerLongitude(), + MarbleInputHandler::d->m_marblePresenter->centerLatitude(), centerX, centerY); + + // Deltas from previous position. + int dx = event->x() - d->m_rightPosition.x(); + int dy = event->y() - d->m_rightPosition.y(); + + d->m_rightPosition = event->pos(); + + // Moving on the bottom should be opposite direction. + int sign = event->y() > centerY ? -1 : 1; + // Left top and right bottom sides for y axis should be opposite direction. + if ((event->x() < centerX && event->y() < centerY) || (event->x() > centerX && event->y() > centerY)) + { + dy *= -1; + } + + const qreal speedFactor = 0.3; + d->m_heading += (dx + dy) * sign * speedFactor; + MarbleInputHandler::d->m_marblePresenter->map()->setHeading(d->m_heading); + if (MarbleInputHandler::d->m_inertialEarthRotation) + { + d->m_kineticSpinning.setHeading(d->m_heading); + } + } + if (selectionRubber()->isVisible()) { // We change selection. selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, event->pos()).normalized()); } } else { direction = mouseMovedOutside(event); } if (MarbleInputHandler::d->m_marblePresenter->viewContext() != Animation) { adjustCursorShape(mousePosition, direction); } return acceptMouse(); } bool MarbleDefaultInputHandler::acceptMouse() { // let others, especially float items, still process the event // Note: This caused a bug in combination with oxygen, see https://bugs.kde.org/show_bug.cgi?id=242414 // and changing it a related regression, see https://bugs.kde.org/show_bug.cgi?id=324862 return false; } bool MarbleDefaultInputHandler::eventFilter(QObject* o, QEvent* e) { Q_UNUSED(o); if (layersEventFilter(o, e)) { return true; } hideSelectionIfCtrlReleased(e); switch (e->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: return handleTouch(static_cast(e)); case QEvent::KeyPress: return handleKeyPress(static_cast(e)); case QEvent::Gesture: return handleGesture(static_cast(e)); case QEvent::Wheel: return handleWheel(static_cast(e)); case QEvent::MouseButtonDblClick: return handleDoubleClick(static_cast(e)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: return handleMouseEvent(static_cast(e)); default: return false; } } bool MarbleDefaultInputHandler::handleTouch(QTouchEvent*) { return false; //reimplement to handle in cases of QML and PinchArea element } bool MarbleDefaultInputHandler::handleKeyPress(QKeyEvent* event) { if ( event->type() == QEvent::KeyPress ) { MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter; bool handled = true; switch ( event->key() ) { case Qt::Key_Left: marblePresenter->moveByStep(-1, 0); break; case Qt::Key_Right: marblePresenter->moveByStep(1, 0); break; case Qt::Key_Up: marblePresenter->moveByStep(0, -1); break; case Qt::Key_Down: marblePresenter->moveByStep(0, 1); break; case Qt::Key_Plus: marblePresenter->zoomIn(); break; case Qt::Key_Minus: marblePresenter->zoomOut(); break; case Qt::Key_Home: marblePresenter->goHome(); break; default: handled = false; break; } return handled; } return false; } void MarbleDefaultInputHandler::handleMouseButtonPressAndHold(const QPoint &) { // Default implementation does nothing } void MarbleDefaultInputHandler::handlePressAndHold() { handleMouseButtonPressAndHold(QPoint(d->m_leftPressedX, d->m_leftPressedY)); } QPointer MarbleDefaultInputHandler::lastToolTipItem() { return d->m_lastToolTipItem; } QTimer* MarbleDefaultInputHandler::toolTipTimer() { return &d->m_toolTipTimer; } QPoint MarbleDefaultInputHandler::toolTipPosition() { return d->m_toolTipPosition; } } #include "moc_MarbleInputHandler.cpp" diff --git a/src/lib/marble/MarbleMap.cpp b/src/lib/marble/MarbleMap.cpp index d3b6333e5..ec2ee3c18 100644 --- a/src/lib/marble/MarbleMap.cpp +++ b/src/lib/marble/MarbleMap.cpp @@ -1,1449 +1,1462 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2009 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2008 Carlos Licea // Copyright 2009 Jens-Michael Hoffmann // Copyright 2010-2012 Bernhard Beschow // // Own #include "MarbleMap.h" // Posix #include // Qt #include #include // Marble #include "layers/FloatItemsLayer.h" #include "layers/FogLayer.h" #include "layers/FpsLayer.h" #include "layers/GeometryLayer.h" #include "layers/GroundLayer.h" #include "layers/MarbleSplashLayer.h" #include "layers/PlacemarkLayer.h" #include "layers/TextureLayer.h" #include "layers/VectorTileLayer.h" #include "AbstractFloatItem.h" #include "DgmlAuxillaryDictionary.h" #include "FileManager.h" #include "GeoDataTreeModel.h" #include "GeoPainter.h" #include "GeoSceneDocument.h" #include "GeoSceneFilter.h" #include "GeoSceneGeodata.h" #include "GeoSceneHead.h" #include "GeoSceneLayer.h" #include "GeoSceneMap.h" #include "GeoScenePalette.h" #include "GeoSceneSettings.h" #include "GeoSceneVector.h" #include "GeoSceneVectorTileDataset.h" #include "GeoSceneTextureTileDataset.h" #include "GeoSceneZoom.h" #include "GeoDataDocument.h" #include "GeoDataFeature.h" #include "GeoDataStyle.h" #include "GeoDataStyleMap.h" #include "LayerManager.h" #include "MapThemeManager.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "MarbleModel.h" #include "PluginManager.h" #include "RenderPlugin.h" #include "StyleBuilder.h" #include "SunLocator.h" #include "TileId.h" #include "TileCoordsPyramid.h" #include "TileCreator.h" #include "TileCreatorDialog.h" #include "TileLoader.h" #include "ViewParams.h" #include "ViewportParams.h" #include "RenderState.h" #include "BookmarkManager.h" namespace Marble { class MarbleMap::CustomPaintLayer : public LayerInterface { public: explicit CustomPaintLayer( MarbleMap *map ) : m_map( map ) { } QStringList renderPosition() const override { return QStringList() << "USER_TOOLS"; } bool render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) override { Q_UNUSED( viewport ); Q_UNUSED( renderPos ); Q_UNUSED( layer ); m_map->customPaint( painter ); return true; } qreal zValue() const override { return 1.0e6; } RenderState renderState() const override { return RenderState(QStringLiteral("Custom Map Paint")); } QString runtimeTrace() const override { return QStringLiteral("CustomPaint"); } private: MarbleMap *const m_map; }; class MarbleMapPrivate { friend class MarbleWidget; public: explicit MarbleMapPrivate( MarbleMap *parent, MarbleModel *model ); void updateMapTheme(); void updateProperty( const QString &, bool ); void setDocument( const QString& key ); void updateTileLevel(); void addPlugins(); MarbleMap *const q; // The model we are showing. MarbleModel *const m_model; bool m_modelIsOwned; // Parameters for the maps appearance. ViewParams m_viewParams; ViewportParams m_viewport; bool m_showFrameRate; bool m_showDebugPolygons; bool m_showDebugBatchRender; GeoDataRelation::RelationTypes m_visibleRelationTypes; StyleBuilder m_styleBuilder; QList m_renderPlugins; LayerManager m_layerManager; MarbleSplashLayer m_marbleSplashLayer; MarbleMap::CustomPaintLayer m_customPaintLayer; GeometryLayer m_geometryLayer; FloatItemsLayer m_floatItemsLayer; FogLayer m_fogLayer; GroundLayer m_groundLayer; TextureLayer m_textureLayer; PlacemarkLayer m_placemarkLayer; VectorTileLayer m_vectorTileLayer; bool m_isLockedToSubSolarPoint; bool m_isSubSolarPointIconVisible; RenderState m_renderState; }; MarbleMapPrivate::MarbleMapPrivate( MarbleMap *parent, MarbleModel *model ) : q( parent ), m_model( model ), m_viewParams(), m_showFrameRate( false ), m_showDebugPolygons( false ), m_showDebugBatchRender( false ), m_visibleRelationTypes(GeoDataRelation::RouteFerry), m_styleBuilder(), m_layerManager( parent ), m_customPaintLayer( parent ), m_geometryLayer(model->treeModel(), &m_styleBuilder), m_floatItemsLayer(parent), m_textureLayer( model->downloadManager(), model->pluginManager(), model->sunLocator(), model->groundOverlayModel() ), m_placemarkLayer( model->placemarkModel(), model->placemarkSelectionModel(), model->clock(), &m_styleBuilder ), m_vectorTileLayer( model->downloadManager(), model->pluginManager(), model->treeModel() ), m_isLockedToSubSolarPoint( false ), m_isSubSolarPointIconVisible( false ) { m_layerManager.addLayer(&m_floatItemsLayer); m_layerManager.addLayer( &m_fogLayer ); m_layerManager.addLayer( &m_groundLayer ); m_layerManager.addLayer( &m_geometryLayer ); m_layerManager.addLayer( &m_placemarkLayer ); m_layerManager.addLayer( &m_customPaintLayer ); m_model->bookmarkManager()->setStyleBuilder(&m_styleBuilder); QObject::connect( m_model, SIGNAL(themeChanged(QString)), parent, SLOT(updateMapTheme()) ); QObject::connect( m_model->fileManager(), SIGNAL(fileAdded(QString)), parent, SLOT(setDocument(QString)) ); QObject::connect( &m_placemarkLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded())); QObject::connect ( &m_layerManager, SIGNAL(pluginSettingsChanged()), parent, SIGNAL(pluginSettingsChanged()) ); QObject::connect ( &m_layerManager, SIGNAL(repaintNeeded(QRegion)), parent, SIGNAL(repaintNeeded(QRegion)) ); QObject::connect ( &m_layerManager, SIGNAL(renderPluginInitialized(RenderPlugin*)), parent, SIGNAL(renderPluginInitialized(RenderPlugin*)) ); QObject::connect ( &m_layerManager, SIGNAL(visibilityChanged(QString,bool)), parent, SLOT(setPropertyValue(QString,bool)) ); QObject::connect( &m_geometryLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded())); QObject::connect(&m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), &m_geometryLayer, SLOT(setTileLevel(int))); QObject::connect(&m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), &m_placemarkLayer, SLOT(setTileLevel(int))); QObject::connect(&m_textureLayer, SIGNAL(tileLevelChanged(int)), &m_placemarkLayer, SLOT(setTileLevel(int))); /* * Slot handleHighlight finds all placemarks * that contain the clicked point. * The placemarks under the clicked position may * have their styleUrl set to a style map which * doesn't specify any highlight styleId. Such * placemarks will be fletered out in GeoGraphicsScene * and will not be highlighted. */ QObject::connect( parent, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &m_geometryLayer, SLOT(handleHighlight(qreal,qreal,GeoDataCoordinates::Unit)) ); QObject::connect(&m_floatItemsLayer, SIGNAL(repaintNeeded(QRegion)), parent, SIGNAL(repaintNeeded(QRegion))); QObject::connect(&m_floatItemsLayer, SIGNAL(renderPluginInitialized(RenderPlugin*)), parent, SIGNAL(renderPluginInitialized(RenderPlugin*))); QObject::connect(&m_floatItemsLayer, SIGNAL(visibilityChanged(QString,bool)), parent, SLOT(setPropertyValue(QString,bool))); QObject::connect(&m_floatItemsLayer, SIGNAL(pluginSettingsChanged()), parent, SIGNAL(pluginSettingsChanged())); QObject::connect( &m_textureLayer, SIGNAL(tileLevelChanged(int)), parent, SLOT(updateTileLevel()) ); QObject::connect( &m_vectorTileLayer, SIGNAL(tileLevelChanged(int)), parent, SLOT(updateTileLevel()) ); QObject::connect( &m_textureLayer, SIGNAL(repaintNeeded()), parent, SIGNAL(repaintNeeded()) ); QObject::connect( parent, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), parent, SIGNAL(repaintNeeded()) ); addPlugins(); QObject::connect(model->pluginManager(), SIGNAL(renderPluginsChanged()), parent, SLOT(addPlugins())); } void MarbleMapPrivate::updateProperty( const QString &name, bool show ) { // earth if (name == QLatin1String("places")) { m_placemarkLayer.setShowPlaces( show ); } else if (name == QLatin1String("cities")) { m_placemarkLayer.setShowCities( show ); } else if (name == QLatin1String("terrain")) { m_placemarkLayer.setShowTerrain( show ); } else if (name == QLatin1String("otherplaces")) { m_placemarkLayer.setShowOtherPlaces( show ); } // other planets else if (name == QLatin1String("landingsites")) { m_placemarkLayer.setShowLandingSites( show ); } else if (name == QLatin1String("craters")) { m_placemarkLayer.setShowCraters( show ); } else if (name == QLatin1String("maria")) { m_placemarkLayer.setShowMaria( show ); } else if (name == QLatin1String("relief")) { m_textureLayer.setShowRelief( show ); } for(RenderPlugin *renderPlugin: m_renderPlugins) { if ( name == renderPlugin->nameId() ) { if ( renderPlugin->visible() == show ) { break; } renderPlugin->setVisible( show ); break; } } } void MarbleMapPrivate::addPlugins() { for (const RenderPlugin *factory: m_model->pluginManager()->renderPlugins()) { bool alreadyCreated = false; for(const RenderPlugin *existing: m_renderPlugins) { if (existing->nameId() == factory->nameId()) { alreadyCreated = true; break; } } if (alreadyCreated) { continue; } RenderPlugin *const renderPlugin = factory->newInstance(m_model); Q_ASSERT(renderPlugin && "Plugin must not return null when requesting a new instance."); m_renderPlugins << renderPlugin; if (AbstractFloatItem *const floatItem = qobject_cast(renderPlugin)) { m_floatItemsLayer.addFloatItem(floatItem); } else { m_layerManager.addRenderPlugin(renderPlugin); } } } // ---------------------------------------------------------------- MarbleMap::MarbleMap() : d( new MarbleMapPrivate( this, new MarbleModel( this ) ) ) { // nothing to do } MarbleMap::MarbleMap(MarbleModel *model) : d( new MarbleMapPrivate( this, model ) ) { d->m_modelIsOwned = false; } MarbleMap::~MarbleMap() { MarbleModel *model = d->m_modelIsOwned ? d->m_model : 0; d->m_layerManager.removeLayer( &d->m_customPaintLayer ); d->m_layerManager.removeLayer( &d->m_geometryLayer ); d->m_layerManager.removeLayer(&d->m_floatItemsLayer); d->m_layerManager.removeLayer( &d->m_fogLayer ); d->m_layerManager.removeLayer( &d->m_placemarkLayer ); d->m_layerManager.removeLayer( &d->m_textureLayer ); d->m_layerManager.removeLayer( &d->m_groundLayer ); qDeleteAll(d->m_renderPlugins); delete d; delete model; // delete the model after private data } MarbleModel *MarbleMap::model() const { return d->m_model; } ViewportParams *MarbleMap::viewport() { return &d->m_viewport; } const ViewportParams *MarbleMap::viewport() const { return &d->m_viewport; } void MarbleMap::setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ) { d->m_viewParams.setMapQualityForViewContext( quality, viewContext ); // Update texture map during the repaint that follows: d->m_textureLayer.setNeedsUpdate(); } MapQuality MarbleMap::mapQuality( ViewContext viewContext ) const { return d->m_viewParams.mapQuality( viewContext ); } MapQuality MarbleMap::mapQuality() const { return d->m_viewParams.mapQuality(); } void MarbleMap::setViewContext( ViewContext viewContext ) { if ( d->m_viewParams.viewContext() == viewContext ) { return; } const MapQuality oldQuality = d->m_viewParams.mapQuality(); d->m_viewParams.setViewContext( viewContext ); emit viewContextChanged( viewContext ); if ( d->m_viewParams.mapQuality() != oldQuality ) { // Update texture map during the repaint that follows: d->m_textureLayer.setNeedsUpdate(); emit repaintNeeded(); } } ViewContext MarbleMap::viewContext() const { return d->m_viewParams.viewContext(); } void MarbleMap::setSize( int width, int height ) { setSize( QSize( width, height ) ); } void MarbleMap::setSize( const QSize& size ) { d->m_viewport.setSize( size ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } QSize MarbleMap::size() const { return QSize( d->m_viewport.width(), d->m_viewport.height() ); } int MarbleMap::width() const { return d->m_viewport.width(); } int MarbleMap::height() const { return d->m_viewport.height(); } int MarbleMap::radius() const { return d->m_viewport.radius(); } void MarbleMap::setRadius( int radius ) { const int oldRadius = d->m_viewport.radius(); d->m_viewport.setRadius( radius ); if ( oldRadius != d->m_viewport.radius() ) { emit radiusChanged( radius ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } } int MarbleMap::preferredRadiusCeil( int radius ) { return d->m_textureLayer.preferredRadiusCeil( radius ); } int MarbleMap::preferredRadiusFloor( int radius ) { return d->m_textureLayer.preferredRadiusFloor( radius ); } int MarbleMap::tileZoomLevel() const { return qMax(d->m_textureLayer.tileZoomLevel(), d->m_vectorTileLayer.tileZoomLevel()); } qreal MarbleMap::centerLatitude() const { // Calculate translation of center point const qreal centerLat = d->m_viewport.centerLatitude(); return centerLat * RAD2DEG; } bool MarbleMap::hasFeatureAt(const QPoint &position) const { return d->m_placemarkLayer.hasPlacemarkAt(position) || d->m_geometryLayer.hasFeatureAt(position, viewport()); } qreal MarbleMap::centerLongitude() const { // Calculate translation of center point const qreal centerLon = d->m_viewport.centerLongitude(); return centerLon * RAD2DEG; } int MarbleMap::minimumZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->minimum(); return 950; } int MarbleMap::maximumZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->maximum(); return 2100; } bool MarbleMap::discreteZoom() const { if ( d->m_model->mapTheme() ) return d->m_model->mapTheme()->head()->zoom()->discrete(); return false; } QVector MarbleMap::whichFeatureAt( const QPoint& curpos ) const { return d->m_placemarkLayer.whichPlacemarkAt( curpos ) + d->m_geometryLayer.whichFeatureAt( curpos, viewport() ); } void MarbleMap::reload() { d->m_textureLayer.reload(); d->m_vectorTileLayer.reload(); } void MarbleMap::downloadRegion( QVector const & pyramid ) { Q_ASSERT( textureLayer() ); Q_ASSERT( !pyramid.isEmpty() ); QTime t; t.start(); // When downloading a region (the author of these lines thinks) most users probably expect // the download to begin with the low resolution tiles and then procede level-wise to // higher resolution tiles. In order to achieve this, we start requesting downloads of // high resolution tiles and request the low resolution tiles at the end because // DownloadQueueSet (silly name) is implemented as stack. int const first = 0; int tilesCount = 0; for ( int level = pyramid[first].bottomLevel(); level >= pyramid[first].topLevel(); --level ) { QSet tileIdSet; for( int i = 0; i < pyramid.size(); ++i ) { QRect const coords = pyramid[i].coords( level ); mDebug() << "MarbleMap::downloadRegion level:" << level << "tile coords:" << coords; int x1, y1, x2, y2; coords.getCoords( &x1, &y1, &x2, &y2 ); for ( int x = x1; x <= x2; ++x ) { for ( int y = y1; y <= y2; ++y ) { TileId const stackedTileId( 0, level, x, y ); tileIdSet.insert( stackedTileId ); // FIXME: use lazy evaluation to not generate up to 100k tiles in one go // this can take considerable time even on very fast systems // in contrast generating the TileIds on the fly when they are needed // does not seem to affect download speed. } } } QSetIterator i( tileIdSet ); while( i.hasNext() ) { TileId const tileId = i.next(); d->m_textureLayer.downloadStackedTile( tileId ); } tilesCount += tileIdSet.count(); } // Needed for downloading unique tiles only. Much faster than if tiles for each level is downloaded separately int const elapsedMs = t.elapsed(); mDebug() << "MarbleMap::downloadRegion:" << tilesCount << "tiles, " << elapsedMs << "ms"; } void MarbleMap::highlightRouteRelation(qint64 osmId, bool enabled) { d->m_geometryLayer.highlightRouteRelation(osmId, enabled); } bool MarbleMap::propertyValue( const QString& name ) const { bool value; if ( d->m_model->mapTheme() ) { d->m_model->mapTheme()->settings()->propertyValue( name, value ); } else { value = false; mDebug() << "WARNING: Failed to access a map theme! Property: " << name; } return value; } bool MarbleMap::showOverviewMap() const { return propertyValue(QStringLiteral("overviewmap")); } bool MarbleMap::showScaleBar() const { return propertyValue(QStringLiteral("scalebar")); } bool MarbleMap::showCompass() const { return propertyValue(QStringLiteral("compass")); } bool MarbleMap::showGrid() const { return propertyValue(QStringLiteral("coordinate-grid")); } bool MarbleMap::showClouds() const { return d->m_viewParams.showClouds(); } bool MarbleMap::showSunShading() const { return d->m_textureLayer.showSunShading(); } bool MarbleMap::showCityLights() const { return d->m_textureLayer.showCityLights(); } bool MarbleMap::isLockedToSubSolarPoint() const { return d->m_isLockedToSubSolarPoint; } bool MarbleMap::isSubSolarPointIconVisible() const { return d->m_isSubSolarPointIconVisible; } bool MarbleMap::showAtmosphere() const { return d->m_viewParams.showAtmosphere(); } bool MarbleMap::showCrosshairs() const { bool visible = false; QList pluginList = renderPlugins(); QList::const_iterator i = pluginList.constBegin(); QList::const_iterator const end = pluginList.constEnd(); for (; i != end; ++i ) { if ((*i)->nameId() == QLatin1String("crosshairs")) { visible = (*i)->visible(); } } return visible; } bool MarbleMap::showPlaces() const { return propertyValue(QStringLiteral("places")); } bool MarbleMap::showCities() const { return propertyValue(QStringLiteral("cities")); } bool MarbleMap::showTerrain() const { return propertyValue(QStringLiteral("terrain")); } bool MarbleMap::showOtherPlaces() const { return propertyValue(QStringLiteral("otherplaces")); } bool MarbleMap::showRelief() const { return propertyValue(QStringLiteral("relief")); } bool MarbleMap::showIceLayer() const { return propertyValue(QStringLiteral("ice")); } bool MarbleMap::showBorders() const { return propertyValue(QStringLiteral("borders")); } bool MarbleMap::showRivers() const { return propertyValue(QStringLiteral("rivers")); } bool MarbleMap::showLakes() const { return propertyValue(QStringLiteral("lakes")); } bool MarbleMap::showFrameRate() const { return d->m_showFrameRate; } bool MarbleMap::showBackground() const { return d->m_layerManager.showBackground(); } GeoDataRelation::RelationTypes MarbleMap::visibleRelationTypes() const { return d->m_visibleRelationTypes; } quint64 MarbleMap::volatileTileCacheLimit() const { return d->m_textureLayer.volatileCacheLimit(); } void MarbleMap::rotateBy(qreal deltaLon, qreal deltaLat) { centerOn( d->m_viewport.centerLongitude() * RAD2DEG + deltaLon, d->m_viewport.centerLatitude() * RAD2DEG + deltaLat ); } void MarbleMap::centerOn( const qreal lon, const qreal lat ) { d->m_viewport.centerOn( lon * DEG2RAD, lat * DEG2RAD ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } void MarbleMap::setCenterLatitude( qreal lat ) { centerOn( centerLongitude(), lat ); } void MarbleMap::setCenterLongitude( qreal lon ) { centerOn( lon, centerLatitude() ); } Projection MarbleMap::projection() const { return d->m_viewport.projection(); } void MarbleMap::setProjection( Projection projection ) { if ( d->m_viewport.projection() == projection ) return; emit projectionChanged( projection ); d->m_viewport.setProjection( projection ); d->m_textureLayer.setProjection( projection ); emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); } bool MarbleMap::screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const { return d->m_viewport.screenCoordinates( lon * DEG2RAD, lat * DEG2RAD, x, y ); } bool MarbleMap::geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { return d->m_viewport.geoCoordinates( x, y, lon, lat, unit ); } void MarbleMapPrivate::setDocument( const QString& key ) { if ( !m_model->mapTheme() ) { // Happens if no valid map theme is set or at application startup // if a file is passed via command line parameters and the last // map theme has not been loaded yet /** * @todo Do we need to queue the document and process it once a map * theme becomes available? */ return; } GeoDataDocument* doc = m_model->fileManager()->at( key ); for ( const GeoSceneLayer *layer: m_model->mapTheme()->map()->layers() ) { if ( layer->backend() != dgml::dgmlValue_geodata && layer->backend() != dgml::dgmlValue_vector ) continue; // look for documents for ( const GeoSceneAbstractDataset *dataset: layer->datasets() ) { const GeoSceneGeodata *data = static_cast( dataset ); QString containername = data->sourceFile(); QString colorize = data->colorize(); if( key == containername ) { if (colorize == QLatin1String("land")) { m_textureLayer.addLandDocument( doc ); } if (colorize == QLatin1String("sea")) { m_textureLayer.addSeaDocument( doc ); } // set visibility according to theme property if( !data->property().isEmpty() ) { bool value; m_model->mapTheme()->settings()->propertyValue( data->property(), value ); doc->setVisible( value ); m_model->treeModel()->updateFeature( doc ); } } } } } void MarbleMapPrivate::updateTileLevel() { emit q->tileLevelChanged(q->tileZoomLevel()); } // Used to be paintEvent() void MarbleMap::paint( GeoPainter &painter, const QRect &dirtyRect ) { Q_UNUSED( dirtyRect ); if (d->m_showDebugPolygons ) { if (viewContext() == Animation) { painter.setDebugPolygonsLevel(1); } else { painter.setDebugPolygonsLevel(2); } } painter.setDebugBatchRender(d->m_showDebugBatchRender); if ( !d->m_model->mapTheme() ) { mDebug() << "No theme yet!"; d->m_marbleSplashLayer.render( &painter, &d->m_viewport ); return; } QTime t; t.start(); RenderStatus const oldRenderStatus = d->m_renderState.status(); d->m_layerManager.renderLayers( &painter, &d->m_viewport ); d->m_renderState = d->m_layerManager.renderState(); bool const parsing = d->m_model->fileManager()->pendingFiles() > 0; d->m_renderState.addChild(RenderState(QStringLiteral("Files"), parsing ? WaitingForData : Complete)); RenderStatus const newRenderStatus = d->m_renderState.status(); if ( oldRenderStatus != newRenderStatus ) { emit renderStatusChanged( newRenderStatus ); } emit renderStateChanged( d->m_renderState ); if ( d->m_showFrameRate ) { FpsLayer fpsPainter( &t ); fpsPainter.paint( &painter ); } const qreal fps = 1000.0 / (qreal)( t.elapsed() ); emit framesPerSecond( fps ); } void MarbleMap::customPaint( GeoPainter *painter ) { Q_UNUSED( painter ); } QString MarbleMap::mapThemeId() const { return d->m_model->mapThemeId(); } void MarbleMap::setMapThemeId( const QString& mapThemeId ) { d->m_model->setMapThemeId( mapThemeId ); } void MarbleMapPrivate::updateMapTheme() { m_layerManager.removeLayer( &m_textureLayer ); // FIXME Find a better way to do this reset. Maybe connect to themeChanged SIGNAL? m_vectorTileLayer.reset(); m_layerManager.removeLayer( &m_vectorTileLayer ); m_layerManager.removeLayer( &m_groundLayer ); QObject::connect( m_model->mapTheme()->settings(), SIGNAL(valueChanged(QString,bool)), q, SLOT(updateProperty(QString,bool)) ); QObject::connect( m_model->mapTheme()->settings(), SIGNAL(valueChanged(QString,bool)), m_model, SLOT(updateProperty(QString,bool)) ); q->setPropertyValue(QStringLiteral("clouds_data"), m_viewParams.showClouds()); m_groundLayer.setColor( m_model->mapTheme()->map()->backgroundColor() ); // Check whether there is a texture layer and vectortile layer available: if ( m_model->mapTheme()->map()->hasTextureLayers() ) { const GeoSceneSettings *const settings = m_model->mapTheme()->settings(); const GeoSceneGroup *const textureLayerSettings = settings ? settings->group( "Texture Layers" ) : 0; const GeoSceneGroup *const vectorTileLayerSettings = settings ? settings->group( "VectorTile Layers" ) : 0; bool textureLayersOk = true; bool vectorTileLayersOk = true; // textures will contain texture layers and // vectorTiles vectortile layers QVector textures; QVector vectorTiles; for( GeoSceneLayer* layer: m_model->mapTheme()->map()->layers() ){ if ( layer->backend() == dgml::dgmlValue_texture ){ for ( const GeoSceneAbstractDataset *pos: layer->datasets() ) { const GeoSceneTextureTileDataset *const texture = dynamic_cast( pos ); if ( !texture ) continue; const QString sourceDir = texture->sourceDir(); const QString installMap = texture->installMap(); const QString role = layer->role(); // If the tiles aren't already there, put up a progress dialog // while creating them. if ( !TileLoader::baseTilesAvailable( *texture ) && !installMap.isEmpty() ) { mDebug() << "Base tiles not available. Creating Tiles ... \n" << "SourceDir: " << sourceDir << "InstallMap:" << installMap; TileCreator *tileCreator = new TileCreator( sourceDir, installMap, (role == QLatin1String("dem")) ? "true" : "false" ); tileCreator->setTileFormat( texture->fileFormat().toLower() ); QPointer tileCreatorDlg = new TileCreatorDialog( tileCreator, 0 ); tileCreatorDlg->setSummary( m_model->mapTheme()->head()->name(), m_model->mapTheme()->head()->description() ); tileCreatorDlg->exec(); if ( TileLoader::baseTilesAvailable( *texture ) ) { mDebug() << "Base tiles for" << sourceDir << "successfully created."; } else { qWarning() << "Some or all base tiles for" << sourceDir << "could not be created."; } delete tileCreatorDlg; } if ( TileLoader::baseTilesAvailable( *texture ) ) { textures.append( texture ); } else { qWarning() << "Base tiles for" << sourceDir << "not available. Skipping all texture layers."; textureLayersOk = false; } } } else if ( layer->backend() == dgml::dgmlValue_vectortile ){ for ( const GeoSceneAbstractDataset *pos: layer->datasets() ) { const GeoSceneVectorTileDataset *const vectorTile = dynamic_cast( pos ); if ( !vectorTile ) continue; const QString sourceDir = vectorTile->sourceDir(); const QString installMap = vectorTile->installMap(); const QString role = layer->role(); // If the tiles aren't already there, put up a progress dialog // while creating them. if ( !TileLoader::baseTilesAvailable( *vectorTile ) && !installMap.isEmpty() ) { mDebug() << "Base tiles not available. Creating Tiles ... \n" << "SourceDir: " << sourceDir << "InstallMap:" << installMap; TileCreator *tileCreator = new TileCreator( sourceDir, installMap, (role == QLatin1String("dem")) ? "true" : "false" ); tileCreator->setTileFormat( vectorTile->fileFormat().toLower() ); QPointer tileCreatorDlg = new TileCreatorDialog( tileCreator, 0 ); tileCreatorDlg->setSummary( m_model->mapTheme()->head()->name(), m_model->mapTheme()->head()->description() ); tileCreatorDlg->exec(); if ( TileLoader::baseTilesAvailable( *vectorTile ) ) { qDebug() << "Base tiles for" << sourceDir << "successfully created."; } else { qDebug() << "Some or all base tiles for" << sourceDir << "could not be created."; } delete tileCreatorDlg; } if ( TileLoader::baseTilesAvailable( *vectorTile ) ) { vectorTiles.append( vectorTile ); } else { qWarning() << "Base tiles for" << sourceDir << "not available. Skipping all texture layers."; vectorTileLayersOk = false; } } } } QString seafile, landfile; if( !m_model->mapTheme()->map()->filters().isEmpty() ) { const GeoSceneFilter *filter= m_model->mapTheme()->map()->filters().first(); if (filter->type() == QLatin1String("colorize")) { //no need to look up with MarbleDirs twice so they are left null for now QList palette = filter->palette(); for (const GeoScenePalette *curPalette: palette ) { if (curPalette->type() == QLatin1String("sea")) { seafile = MarbleDirs::path( curPalette->file() ); } else if (curPalette->type() == QLatin1String("land")) { landfile = MarbleDirs::path( curPalette->file() ); } } //look up locations if they are empty if( seafile.isEmpty() ) seafile = MarbleDirs::path(QStringLiteral("seacolors.leg")); if( landfile.isEmpty() ) landfile = MarbleDirs::path(QStringLiteral("landcolors.leg")); } } m_textureLayer.setMapTheme( textures, textureLayerSettings, seafile, landfile ); m_textureLayer.setProjection( m_viewport.projection() ); m_textureLayer.setShowRelief( q->showRelief() ); m_vectorTileLayer.setMapTheme( vectorTiles, vectorTileLayerSettings ); if (m_textureLayer.textureLayerCount() == 0) { m_layerManager.addLayer( &m_groundLayer ); } if ( textureLayersOk ) m_layerManager.addLayer( &m_textureLayer ); if ( vectorTileLayersOk && !vectorTiles.isEmpty() ) m_layerManager.addLayer( &m_vectorTileLayer ); } else { m_layerManager.addLayer( &m_groundLayer ); m_textureLayer.setMapTheme( QVector(), 0, "", "" ); m_vectorTileLayer.setMapTheme( QVector(), 0 ); } // earth m_placemarkLayer.setShowPlaces( q->showPlaces() ); m_placemarkLayer.setShowCities( q->showCities() ); m_placemarkLayer.setShowTerrain( q->showTerrain() ); m_placemarkLayer.setShowOtherPlaces( q->showOtherPlaces() ); m_placemarkLayer.setShowLandingSites(q->propertyValue(QStringLiteral("landingsites"))); m_placemarkLayer.setShowCraters(q->propertyValue(QStringLiteral("craters"))); m_placemarkLayer.setShowMaria(q->propertyValue(QStringLiteral("maria"))); m_styleBuilder.setDefaultLabelColor(m_model->mapTheme()->map()->labelColor()); m_placemarkLayer.requestStyleReset(); for (RenderPlugin *renderPlugin: m_renderPlugins) { bool propertyAvailable = false; m_model->mapTheme()->settings()->propertyAvailable( renderPlugin->nameId(), propertyAvailable ); bool propertyValue = false; m_model->mapTheme()->settings()->propertyValue( renderPlugin->nameId(), propertyValue ); if ( propertyAvailable ) { renderPlugin->setVisible( propertyValue ); } } emit q->themeChanged( m_model->mapTheme()->head()->mapThemeId() ); } void MarbleMap::setPropertyValue( const QString& name, bool value ) { mDebug() << "In MarbleMap the property " << name << "was set to " << value; if ( d->m_model->mapTheme() ) { d->m_model->mapTheme()->settings()->setPropertyValue( name, value ); d->m_textureLayer.setNeedsUpdate(); } else { mDebug() << "WARNING: Failed to access a map theme! Property: " << name; } if (d->m_textureLayer.textureLayerCount() == 0) { d->m_layerManager.addLayer( &d->m_groundLayer ); } else { d->m_layerManager.removeLayer( &d->m_groundLayer ); } } void MarbleMap::setShowOverviewMap( bool visible ) { setPropertyValue(QStringLiteral("overviewmap"), visible); } void MarbleMap::setShowScaleBar( bool visible ) { setPropertyValue(QStringLiteral("scalebar"), visible); } void MarbleMap::setShowCompass( bool visible ) { setPropertyValue(QStringLiteral("compass"), visible); } void MarbleMap::setShowAtmosphere( bool visible ) { for ( RenderPlugin *plugin: renderPlugins() ) { if (plugin->nameId() == QLatin1String("atmosphere")) { plugin->setVisible( visible ); } } d->m_viewParams.setShowAtmosphere( visible ); } void MarbleMap::setShowCrosshairs( bool visible ) { QList pluginList = renderPlugins(); QList::const_iterator i = pluginList.constBegin(); QList::const_iterator const end = pluginList.constEnd(); for (; i != end; ++i ) { if ((*i)->nameId() == QLatin1String("crosshairs")) { (*i)->setVisible( visible ); } } } void MarbleMap::setShowClouds( bool visible ) { d->m_viewParams.setShowClouds( visible ); setPropertyValue(QStringLiteral("clouds_data"), visible); } void MarbleMap::setShowSunShading( bool visible ) { d->m_textureLayer.setShowSunShading( visible ); } void MarbleMap::setShowCityLights( bool visible ) { d->m_textureLayer.setShowCityLights( visible ); setPropertyValue(QStringLiteral("citylights"), visible); } void MarbleMap::setLockToSubSolarPoint( bool visible ) { disconnect( d->m_model->sunLocator(), SIGNAL(positionChanged(qreal,qreal)), this, SLOT(centerOn(qreal,qreal)) ); if( isLockedToSubSolarPoint() != visible ) { d->m_isLockedToSubSolarPoint = visible; } if ( isLockedToSubSolarPoint() ) { connect( d->m_model->sunLocator(), SIGNAL(positionChanged(qreal,qreal)), this, SLOT(centerOn(qreal,qreal)) ); centerOn( d->m_model->sunLocator()->getLon(), d->m_model->sunLocator()->getLat() ); } else if ( visible ) { mDebug() << "Ignoring centering on sun, since the sun plugin is not loaded."; } } void MarbleMap::setSubSolarPointIconVisible( bool visible ) { if ( isSubSolarPointIconVisible() != visible ) { d->m_isSubSolarPointIconVisible = visible; } } void MarbleMap::setShowTileId( bool visible ) { d->m_textureLayer.setShowTileId( visible ); } void MarbleMap::setShowGrid( bool visible ) { setPropertyValue(QStringLiteral("coordinate-grid"), visible); } void MarbleMap::setShowPlaces( bool visible ) { setPropertyValue(QStringLiteral("places"), visible); } void MarbleMap::setShowCities( bool visible ) { setPropertyValue(QStringLiteral("cities"), visible); } void MarbleMap::setShowTerrain( bool visible ) { setPropertyValue(QStringLiteral("terrain"), visible); } void MarbleMap::setShowOtherPlaces( bool visible ) { setPropertyValue(QStringLiteral("otherplaces"), visible); } void MarbleMap::setShowRelief( bool visible ) { setPropertyValue(QStringLiteral("relief"), visible); } void MarbleMap::setShowIceLayer( bool visible ) { setPropertyValue(QStringLiteral("ice"), visible); } void MarbleMap::setShowBorders( bool visible ) { setPropertyValue(QStringLiteral("borders"), visible); } void MarbleMap::setShowRivers( bool visible ) { setPropertyValue(QStringLiteral("rivers"), visible); } void MarbleMap::setShowLakes( bool visible ) { setPropertyValue(QStringLiteral("lakes"), visible); } void MarbleMap::setShowFrameRate( bool visible ) { d->m_showFrameRate = visible; } void MarbleMap::setShowRuntimeTrace( bool visible ) { if (visible != d->m_layerManager.showRuntimeTrace()) { d->m_layerManager.setShowRuntimeTrace(visible); emit repaintNeeded(); } } bool MarbleMap::showRuntimeTrace() const { return d->m_layerManager.showRuntimeTrace(); } void MarbleMap::setShowDebugPolygons( bool visible) { if (visible != d->m_showDebugPolygons) { d->m_showDebugPolygons = visible; emit repaintNeeded(); } } bool MarbleMap::showDebugPolygons() const { return d->m_showDebugPolygons; } void MarbleMap::setShowDebugBatchRender( bool visible) { qDebug() << Q_FUNC_INFO << visible; if (visible != d->m_showDebugBatchRender) { d->m_showDebugBatchRender = visible; emit repaintNeeded(); } } bool MarbleMap::showDebugBatchRender() const { return d->m_showDebugBatchRender; } void MarbleMap::setShowDebugPlacemarks( bool visible) { if (visible != d->m_placemarkLayer.isDebugModeEnabled()) { d->m_placemarkLayer.setDebugModeEnabled(visible); emit repaintNeeded(); } } bool MarbleMap::showDebugPlacemarks() const { return d->m_placemarkLayer.isDebugModeEnabled(); } void MarbleMap::setShowBackground( bool visible ) { d->m_layerManager.setShowBackground( visible ); } void MarbleMap::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes) { if (d->m_visibleRelationTypes != relationTypes) { d->m_visibleRelationTypes = relationTypes; d->m_geometryLayer.setVisibleRelationTypes(relationTypes); emit visibleRelationTypesChanged(d->m_visibleRelationTypes); } } void MarbleMap::notifyMouseClick( int x, int y ) { qreal lon = 0; qreal lat = 0; const bool valid = geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); if ( valid ) { emit mouseClickGeoPosition( lon, lat, GeoDataCoordinates::Radian ); } } void MarbleMap::clearVolatileTileCache() { d->m_vectorTileLayer.reset(); d->m_textureLayer.reset(); mDebug() << "Cleared Volatile Cache!"; } void MarbleMap::setVolatileTileCacheLimit( quint64 kilobytes ) { mDebug() << "kiloBytes" << kilobytes; d->m_textureLayer.setVolatileCacheLimit( kilobytes ); } AngleUnit MarbleMap::defaultAngleUnit() const { if ( GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::Decimal ) { return DecimalDegree; } else if ( GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::UTM ) { return UTM; } return DMSDegree; } void MarbleMap::setDefaultAngleUnit( AngleUnit angleUnit ) { if ( angleUnit == DecimalDegree ) { GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::Decimal ); return; } else if ( angleUnit == UTM ) { GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::UTM ); return; } GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::DMS ); } QFont MarbleMap::defaultFont() const { return d->m_styleBuilder.defaultFont(); } void MarbleMap::setDefaultFont( const QFont& font ) { d->m_styleBuilder.setDefaultFont(font); d->m_placemarkLayer.requestStyleReset(); } QList MarbleMap::renderPlugins() const { return d->m_renderPlugins; } QList MarbleMap::floatItems() const { return d->m_floatItemsLayer.floatItems(); } AbstractFloatItem * MarbleMap::floatItem( const QString &nameId ) const { for ( AbstractFloatItem * floatItem: floatItems() ) { if ( floatItem && floatItem->nameId() == nameId ) { return floatItem; } } return 0; // No item found } QList MarbleMap::dataPlugins() const { return d->m_layerManager.dataPlugins(); } QList MarbleMap::whichItemAt( const QPoint& curpos ) const { return d->m_layerManager.whichItemAt( curpos ); } void MarbleMap::addLayer( LayerInterface *layer ) { d->m_layerManager.addLayer(layer); } void MarbleMap::removeLayer( LayerInterface *layer ) { d->m_layerManager.removeLayer(layer); } RenderStatus MarbleMap::renderStatus() const { return d->m_layerManager.renderState().status(); } RenderState MarbleMap::renderState() const { return d->m_layerManager.renderState(); } QString MarbleMap::addTextureLayer(GeoSceneTextureTileDataset *texture) { return textureLayer()->addTextureLayer(texture); } void MarbleMap::removeTextureLayer(const QString &key) { textureLayer()->removeTextureLayer(key); } // this method will only temporarily "pollute" the MarbleModel class TextureLayer *MarbleMap::textureLayer() const { return &d->m_textureLayer; } const StyleBuilder* MarbleMap::styleBuilder() const { return &d->m_styleBuilder; } +qreal MarbleMap::heading() const +{ + return d->m_viewport.heading() * RAD2DEG; +} + +void MarbleMap::setHeading( qreal heading ) +{ + d->m_viewport.setHeading( heading * DEG2RAD ); + d->m_textureLayer.setNeedsUpdate(); + + emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() ); +} + } #include "moc_MarbleMap.cpp" diff --git a/src/lib/marble/MarbleMap.h b/src/lib/marble/MarbleMap.h index 3e1636e0a..2ffec6941 100644 --- a/src/lib/marble/MarbleMap.h +++ b/src/lib/marble/MarbleMap.h @@ -1,790 +1,794 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2008 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2009 Jens-Michael Hoffmann // #ifndef MARBLE_MARBLEMAP_H #define MARBLE_MARBLEMAP_H /** @file * This file contains the headers for MarbleMap. * * @author Torsten Rahn * @author Inge Wallin */ #include "marble_export.h" #include "GeoDataCoordinates.h" // In geodata/data/ #include "GeoDataRelation.h" // Qt #include #include class QFont; class QString; namespace Marble { // MarbleMap class MarbleMapPrivate; // Marble class GeoDataLatLonAltBox; class GeoDataFeature; class MarbleModel; class ViewportParams; class GeoPainter; class LayerInterface; class RenderPlugin; class RenderState; class AbstractDataPlugin; class AbstractDataPluginItem; class AbstractFloatItem; class TextureLayer; class TileCoordsPyramid; class GeoSceneTextureTileDataset; class StyleBuilder; /** * @short A class that can paint a view of the earth. * * FIXME: Change this description when we are done. * * This class can paint a view of the earth or any other globe, * depending on which dataset is used. It can be used to show the * globe in a widget like MarbleWidget does, or on any other * QPaintDevice. * * The projection and other view parameters that control how MarbleMap * paints the map is given through the class ViewParams. If the * programmer wants to allow the user to control the map, he/she has * to provide a way for the user to interact with it. An example of * this can be seen in the class MarbleWidgetInputHandler, that lets * the user control a MarbleWidget that uses MarbleMap internally. * * The MarbleMap needs to be provided with a data model to * work. This model is contained in the MarbleModel class. The widget * can also construct its own model if none is given to the * constructor. This data model contains 3 separate datatypes: * tiles which provide the background, vectors which * provide things like country borders and coastlines and * placemarks which can show points of interest, such as * cities, mountain tops or the poles. * * @see MarbleWidget * @see MarbleModel */ class MARBLE_EXPORT MarbleMap : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.MarbleMap") public: friend class MarbleWidget; /** * @brief Construct a new MarbleMap. * * This constructor should be used when you will only use one * MarbleMap. The widget will create its own MarbleModel when * created. */ MarbleMap(); /** * @brief Construct a new MarbleMap. * @param model the data model for the widget. * * This constructor should be used when you plan to use more than * one MarbleMap for the same MarbleModel (not yet supported, * but will be soon). */ explicit MarbleMap( MarbleModel *model ); ~MarbleMap() override; /** * @brief Return the model that this view shows. */ MarbleModel *model() const; // Accessors to internal objects; ViewportParams *viewport(); const ViewportParams *viewport() const; /** * @brief Get the Projection used for the map * @return @c Spherical a Globe * @return @c Equirectangular a flat map * @return @c Mercator another flat map */ Projection projection() const; /** * @brief Get the ID of the current map theme * To ensure that a unique identifier is being used the theme does NOT * get represented by its name but the by relative location of the file * that specifies the theme: * * Example: * maptheme = "earth/bluemarble/bluemarble.dgml" */ QString mapThemeId() const; void setMapQualityForViewContext( MapQuality qualityForViewContext, ViewContext viewContext ); MapQuality mapQuality( ViewContext viewContext ) const; /** * @brief Return the current map quality. */ MapQuality mapQuality() const; void setViewContext( ViewContext viewContext ); ViewContext viewContext() const; void setSize( int width, int height ); void setSize( const QSize& size ); QSize size() const; int width() const; int height() const; /** * @brief Return the radius of the globe in pixels. */ int radius() const; int preferredRadiusCeil( int radius ); int preferredRadiusFloor( int radius ); int tileZoomLevel() const; /** * @brief return the minimum zoom value for the current map theme. */ int minimumZoom() const; /** * @brief return the minimum zoom value for the current map theme. */ int maximumZoom() const; bool discreteZoom() const; /** * @brief Get the screen coordinates corresponding to geographical coordinates in the map. * @param lon the lon coordinate of the requested pixel position * @param lat the lat coordinate of the requested pixel position * @param x the x coordinate of the pixel is returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen */ bool screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const; /** * @brief Get the earth coordinates corresponding to a pixel in the map. * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @param lon the longitude angle is returned through this parameter * @param lat the latitude angle is returned through this parameter * @return @c true if the pixel (x, y) is within the globe * @c false if the pixel (x, y) is outside the globe, i.e. in space. */ bool geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit = GeoDataCoordinates::Degree ) const; /** * @brief Return the longitude of the center point. * @return The longitude of the center point in degree. */ qreal centerLongitude() const; /** * @brief Return the latitude of the center point. * @return The latitude of the center point in degree. */ qreal centerLatitude() const; + qreal heading() const; + /** * @since 0.26.0 */ bool hasFeatureAt(const QPoint&) const; QVector whichFeatureAt( const QPoint& ) const; /** * @brief Return the property value by name. * @return The property value (usually: visibility). */ bool propertyValue( const QString& name ) const; /** * @brief Return whether the overview map is visible. * @return The overview map visibility. */ bool showOverviewMap() const; /** * @brief Return whether the scale bar is visible. * @return The scale bar visibility. */ bool showScaleBar() const; /** * @brief Return whether the compass bar is visible. * @return The compass visibility. */ bool showCompass() const; /** * @brief Return whether the cloud cover is visible. * @return The cloud cover visibility. */ bool showClouds() const; /** * @brief Return whether the night shadow is visible. * @return visibility of night shadow */ bool showSunShading() const; /** * @brief Return whether the city lights are shown instead of the night shadow. * @return visibility of city lights */ bool showCityLights() const; /** * @brief Return whether the globe is locked to the sub solar point * @return if globe is locked to sub solar point */ bool isLockedToSubSolarPoint() const; /** * @brief Return whether the sun icon is shown in the sub solar point. * @return visibility of the sun icon in the sub solar point */ bool isSubSolarPointIconVisible() const; /** * @brief Return whether the atmospheric glow is visible. * @return The cloud cover visibility. */ bool showAtmosphere() const; /** * @brief Return whether the crosshairs are visible. * @return The crosshairs' visibility. */ bool showCrosshairs() const; /** * @brief Return whether the coordinate grid is visible. * @return The coordinate grid visibility. */ bool showGrid() const; /** * @brief Return whether the place marks are visible. * @return The place mark visibility. */ bool showPlaces() const; /** * @brief Return whether the city place marks are visible. * @return The city place mark visibility. */ bool showCities() const; /** * @brief Return whether the terrain place marks are visible. * @return The terrain place mark visibility. */ bool showTerrain() const; /** * @brief Return whether other places are visible. * @return The visibility of other places. */ bool showOtherPlaces() const; /** * @brief Return whether the relief is visible. * @return The relief visibility. */ bool showRelief() const; /** * @brief Return whether the ice layer is visible. * @return The ice layer visibility. */ bool showIceLayer() const; /** * @brief Return whether the borders are visible. * @return The border visibility. */ bool showBorders() const; /** * @brief Return whether the rivers are visible. * @return The rivers' visibility. */ bool showRivers() const; /** * @brief Return whether the lakes are visible. * @return The lakes' visibility. */ bool showLakes() const; /** * @brief Return whether the frame rate gets displayed. * @return the frame rates visibility */ bool showFrameRate() const; bool showBackground() const; GeoDataRelation::RelationTypes visibleRelationTypes() const; /** * @brief Returns the limit in kilobytes of the volatile (in RAM) tile cache. * @return the limit of volatile tile cache in kilobytes. */ quint64 volatileTileCacheLimit() const; /** * @brief Returns a list of all RenderPlugins in the model, this includes float items * @return the list of RenderPlugins */ QList renderPlugins() const; QList floatItems() const; /** * @brief Returns a list of all FloatItems in the model * @return the list of the floatItems */ AbstractFloatItem * floatItem( const QString &nameId ) const; /** * @brief Returns a list of all DataPlugins on the layer * @return the list of DataPlugins */ QList dataPlugins() const; /** * @brief Returns all widgets of dataPlugins on the position curpos */ QList whichItemAt( const QPoint& curpos ) const; AngleUnit defaultAngleUnit() const; QFont defaultFont() const; TextureLayer *textureLayer() const; /** * @brief Add a layer to be included in rendering. */ void addLayer( LayerInterface *layer ); /** * @brief Adds a texture sublayer * @return Returns a key that identifies the texture sublayer */ QString addTextureLayer(GeoSceneTextureTileDataset *texture); /** * @brief Removes a texture sublayer * @param Key that was returned from corresponding addTextureLayer */ void removeTextureLayer(const QString &key); /** * @brief Remove a layer from being included in rendering. */ void removeLayer( LayerInterface *layer ); RenderStatus renderStatus() const; RenderState renderState() const; /** * @since 0.26.0 */ const StyleBuilder* styleBuilder() const; public Q_SLOTS: /** * @brief Paint the map using a give painter. * @param painter The painter to use. * @param dirtyRect the rectangle that actually needs repainting. */ void paint( GeoPainter &painter, const QRect &dirtyRect ); /** * @brief Set the radius of the globe in pixels. * @param radius The new globe radius value in pixels. */ void setRadius( int radius ); + void setHeading( qreal heading ); + /** * @brief Rotate the view by the two angles phi and theta. * @param deltaLon an angle that specifies the change in terms of longitude * @param deltaLat an angle that specifies the change in terms of latitude * * This function rotates the view by two angles, * deltaLon ("theta") and deltaLat ("phi"). * If we start at (0, 0), the result will be the exact equivalent * of (lon, lat), otherwise the resulting angle will be the sum of * the previous position and the two offsets. */ void rotateBy(qreal deltaLon, qreal deltaLat); /** * @brief Center the view on a geographical point * @param lat an angle parallel to the latitude lines * +90(N) - -90(S) * @param lon an angle parallel to the longitude lines * +180(W) - -180(E) */ void centerOn( const qreal lon, const qreal lat ); /** * @brief Set the latitude for the center point * @param lat the new value for the latitude in degree */ void setCenterLatitude( qreal lat ); /** * @brief Set the longitude for the center point * @param lon the new value for the longitude in degree */ void setCenterLongitude( qreal lon ); /** * @brief Set the Projection used for the map * @param projection projection type (e.g. Spherical, Equirectangular, Mercator) */ void setProjection( Projection projection ); /** * @brief Set a new map theme * @param maptheme The ID of the new maptheme. To ensure that a unique * identifier is being used the theme does NOT get represented by its * name but the by relative location of the file that specifies the theme: * * Example: * maptheme = "earth/bluemarble/bluemarble.dgml" */ void setMapThemeId( const QString& maptheme ); /** * @brief Sets the value of a map theme property * @param value value of the property (usually: visibility) * * Later on we might add a "setPropertyType and a QVariant * if needed. */ void setPropertyValue( const QString& name, bool value ); /** * @brief Set whether the overview map overlay is visible * @param visible visibility of the overview map */ void setShowOverviewMap( bool visible ); /** * @brief Set whether the scale bar overlay is visible * @param visible visibility of the scale bar */ void setShowScaleBar( bool visible ); /** * @brief Set whether the compass overlay is visible * @param visible visibility of the compass */ void setShowCompass( bool visible ); /** * @brief Set whether the cloud cover is visible * @param visible visibility of the cloud cover */ void setShowClouds( bool visible ); /** * @brief Set whether the night shadow is visible. * @param visibile visibility of shadow */ void setShowSunShading( bool visible ); /** * @brief Set whether city lights instead of night shadow are visible. * @param visible visibility of city lights */ void setShowCityLights( bool visible ); /** * @brief Set the globe locked to the sub solar point * @param vsible if globe is locked to the sub solar point */ void setLockToSubSolarPoint( bool visible ); /** * @brief Set whether the sun icon is shown in the sub solar point * @param visible if the sun icon is shown in the sub solar point */ void setSubSolarPointIconVisible( bool visible ); /** * @brief Set whether the is tile is visible * NOTE: This is part of the transitional debug API * and might be subject to changes until Marble 0.8 * @param visible visibility of the tile */ void setShowTileId( bool visible ); /** * @brief Set whether the atmospheric glow is visible * @param visible visibility of the atmospheric glow */ void setShowAtmosphere( bool visible ); /** * @brief Set whether the crosshairs are visible * @param visible visibility of the crosshairs */ void setShowCrosshairs( bool visible ); /** * @brief Set whether the coordinate grid overlay is visible * @param visible visibility of the coordinate grid */ void setShowGrid( bool visible ); /** * @brief Set whether the place mark overlay is visible * @param visible visibility of the place marks */ void setShowPlaces( bool visible ); /** * @brief Set whether the city place mark overlay is visible * @param visible visibility of the city place marks */ void setShowCities( bool visible ); /** * @brief Set whether the terrain place mark overlay is visible * @param visible visibility of the terrain place marks */ void setShowTerrain( bool visible ); /** * @brief Set whether the other places overlay is visible * @param visible visibility of other places */ void setShowOtherPlaces( bool visible ); /** * @brief Set whether the relief is visible * @param visible visibility of the relief */ void setShowRelief( bool visible ); /** * @brief Set whether the ice layer is visible * @param visible visibility of the ice layer */ void setShowIceLayer( bool visible ); /** * @brief Set whether the borders visible * @param visible visibility of the borders */ void setShowBorders( bool visible ); /** * @brief Set whether the rivers are visible * @param visible visibility of the rivers */ void setShowRivers( bool visible ); /** * @brief Set whether the lakes are visible * @param visible visibility of the lakes */ void setShowLakes( bool visible ); /** * @brief Set whether the frame rate gets shown * @param visible visibility of the frame rate */ void setShowFrameRate( bool visible ); void setShowRuntimeTrace( bool visible ); bool showRuntimeTrace() const; /** * @brief Set whether to enter the debug mode for * polygon node drawing * @param visible visibility of the node debug mode */ void setShowDebugPolygons( bool visible); bool showDebugPolygons() const; /** * @brief Set whether to enter the debug mode for * visualizing batch rendering * @param visible visibility of the batch rendering */ void setShowDebugBatchRender( bool visible); bool showDebugBatchRender() const; /** * @brief Set whether to enter the debug mode for * placemark drawing * @param visible visibility of the node debug mode */ void setShowDebugPlacemarks(bool visible); bool showDebugPlacemarks() const; void setShowBackground( bool visible ); void setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes); /** * @brief used to notify about the position of the mouse click */ void notifyMouseClick( int x, int y ); void clearVolatileTileCache(); /** * @brief Set the limit of the volatile (in RAM) tile cache. * @param bytes The limit in kilobytes. */ void setVolatileTileCacheLimit( quint64 kiloBytes ); void setDefaultAngleUnit( AngleUnit angleUnit ); void setDefaultFont( const QFont& font ); /** * @brief Reload the currently displayed map by reloading texture tiles * from the Internet. In the future this should be extended to all * kinds of data which is used in the map. */ void reload(); void downloadRegion( QVector const & ); void highlightRouteRelation(qint64 osmId, bool enabled); Q_SIGNALS: void tileLevelChanged( int level ); /** * @brief Signal that the theme has changed * @param theme Name of the new theme. */ void themeChanged( const QString& theme ); void projectionChanged( Projection ); void radiusChanged( int radius ); void mouseMoveGeoPosition( const QString& ); void mouseClickGeoPosition( qreal lon, qreal lat, GeoDataCoordinates::Unit ); void framesPerSecond( qreal fps ); /** * This signal is emitted when the repaint of the view was requested. * If available with the @p dirtyRegion which is the region the view will change in. * If dirtyRegion.isEmpty() returns true, the whole viewport has to be repainted. */ void repaintNeeded( const QRegion& dirtyRegion = QRegion() ); /** * This signal is emitted when the visible region of the map changes. This typically happens * when the user moves the map around or zooms. */ void visibleLatLonAltBoxChanged( const GeoDataLatLonAltBox& visibleLatLonAltBox ); /** * @brief This signal is emit when the settings of a plugin changed. */ void pluginSettingsChanged(); /** * @brief Signal that a render item has been initialized */ void renderPluginInitialized( RenderPlugin *renderPlugin ); /** * @brief Emitted when the layer rendering status has changed * @param status New render status */ void renderStatusChanged( RenderStatus status ); void renderStateChanged( const RenderState &state ); void highlightedPlacemarksChanged( qreal, qreal, GeoDataCoordinates::Unit ); void viewContextChanged(ViewContext viewContext); void visibleRelationTypesChanged(GeoDataRelation::RelationTypes relationTypes); protected: /** * @brief Enables custom drawing onto the MarbleMap straight after * @brief the globe and before all other layers have been rendered. * @param painter * * @deprecated implement LayerInterface and add it using @p addLayer() */ virtual void customPaint( GeoPainter *painter ); private: Q_PRIVATE_SLOT( d, void updateMapTheme() ) Q_PRIVATE_SLOT( d, void updateProperty( const QString &, bool ) ) Q_PRIVATE_SLOT( d, void setDocument(QString) ) Q_PRIVATE_SLOT( d, void updateTileLevel() ) Q_PRIVATE_SLOT(d, void addPlugins()) private: Q_DISABLE_COPY( MarbleMap ) MarbleMapPrivate * const d; friend class MarbleMapPrivate; class CustomPaintLayer; friend class CustomPaintLayer; }; } #endif diff --git a/src/lib/marble/MarbleWidget.cpp b/src/lib/marble/MarbleWidget.cpp index e23f6fd9b..3c2f31967 100644 --- a/src/lib/marble/MarbleWidget.cpp +++ b/src/lib/marble/MarbleWidget.cpp @@ -1,1222 +1,1232 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2010-2012 Bernhard Beschow // Copyright 2012 Mohammed Nafees // #include "MarbleWidget.h" #include #include #include #include #include #include #include #include #include #include "DataMigration.h" #include "FpsLayer.h" #include "FileManager.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataPlacemark.h" #include "GeoDataLookAt.h" #include "GeoPainter.h" #include "MarbleClock.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "MarbleLocale.h" #include "MarbleMap.h" #include "MarbleModel.h" #include "MarbleWidgetInputHandler.h" #include "MarbleWidgetPopupMenu.h" #include "Planet.h" #include "PopupLayer.h" #include "RenderState.h" #include "RenderPlugin.h" #include "SunLocator.h" #include "TileCreatorDialog.h" #include "ViewportParams.h" #include "routing/RoutingLayer.h" #include "MarbleAbstractPresenter.h" #include "StyleBuilder.h" namespace Marble { class MarbleWidget::CustomPaintLayer : public LayerInterface { public: explicit CustomPaintLayer( MarbleWidget *widget ) : m_widget( widget ) { } QStringList renderPosition() const override { return QStringList() << "USER_TOOLS"; } bool render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) override { Q_UNUSED( viewport ); Q_UNUSED( renderPos ); Q_UNUSED( layer ); painter->setPen( Qt::black ); m_widget->customPaint( painter ); return true; } qreal zValue() const override { return 1.0e7; } RenderState renderState() const override { return RenderState(QStringLiteral("Custom Widget Paint")); } QString runtimeTrace() const override { return QStringLiteral("MarbleWidget::CustomPaintLayer"); } private: MarbleWidget *const m_widget; }; class MarbleWidgetPrivate { public: explicit MarbleWidgetPrivate( MarbleWidget *parent ) : m_widget( parent ), m_model(), m_map( &m_model ), m_presenter( &m_map ), m_inputhandler( 0 ), m_routingLayer( 0 ), m_mapInfoDialog( 0 ), m_customPaintLayer( parent ), m_popupmenu( 0 ), m_showFrameRate( false ) { } ~MarbleWidgetPrivate() { m_map.removeLayer( &m_customPaintLayer ); m_map.removeLayer( m_mapInfoDialog ); delete m_mapInfoDialog; delete m_popupmenu; } void construct(); void updateMapTheme(); void setInputHandler(); void setInputHandler( MarbleWidgetInputHandler *handler ); /** * @brief Update widget flags and cause a full repaint * * The background of the widget only needs to be redrawn in certain cases. This * method sets the widget flags accordingly and triggers a repaint. */ void updateSystemBackgroundAttribute(); MarbleWidget *const m_widget; MarbleModel m_model; MarbleMap m_map; MarbleAbstractPresenter m_presenter; MarbleWidgetInputHandler *m_inputhandler; RoutingLayer *m_routingLayer; PopupLayer *m_mapInfoDialog; MarbleWidget::CustomPaintLayer m_customPaintLayer; MarbleWidgetPopupMenu *m_popupmenu; bool m_showFrameRate; }; MarbleWidget::MarbleWidget(QWidget *parent) : QWidget( parent ), d( new MarbleWidgetPrivate( this ) ) { // setAttribute( Qt::WA_PaintOnScreen, true ); d->construct(); } MarbleWidget::~MarbleWidget() { // Remove and delete an existing InputHandler // initialized in d->construct() setInputHandler( 0 ); delete d; } void MarbleWidgetPrivate::construct() { QPointer dataMigration = new DataMigration( m_widget ); dataMigration->exec(); delete dataMigration; // Widget settings m_widget->setMinimumSize( 200, 300 ); m_widget->setFocusPolicy( Qt::WheelFocus ); m_widget->setFocus( Qt::OtherFocusReason ); // Set background: black. m_widget->setPalette( QPalette ( Qt::black ) ); // Set whether the black space gets displayed or the earth gets simply // displayed on the widget background. m_widget->setAutoFillBackground( true ); // Initialize the map and forward some signals. m_map.setSize( m_widget->width(), m_widget->height() ); m_map.setShowFrameRate( false ); // never let the map draw the frame rate, // we do this differently here in the widget m_widget->connect( &m_presenter, SIGNAL(regionSelected(QList)), m_widget, SIGNAL(regionSelected(QList)) ); m_widget->connect( &m_presenter, SIGNAL(zoomChanged(int)), m_widget, SIGNAL(zoomChanged(int)) ); m_widget->connect( &m_presenter, SIGNAL(distanceChanged(QString)), m_widget, SIGNAL(distanceChanged(QString)) ); // forward some signals of m_map m_widget->connect( &m_map, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)) ); m_widget->connect( &m_map, SIGNAL(projectionChanged(Projection)), m_widget, SIGNAL(projectionChanged(Projection)) ); m_widget->connect( &m_map, SIGNAL(tileLevelChanged(int)), m_widget, SIGNAL(tileLevelChanged(int)) ); m_widget->connect( &m_map, SIGNAL(framesPerSecond(qreal)), m_widget, SIGNAL(framesPerSecond(qreal)) ); m_widget->connect( &m_map, SIGNAL(viewContextChanged(ViewContext)), m_widget, SLOT(setViewContext(ViewContext)) ); m_widget->connect( &m_map, SIGNAL(pluginSettingsChanged()), m_widget, SIGNAL(pluginSettingsChanged()) ); m_widget->connect( &m_map, SIGNAL(renderPluginInitialized(RenderPlugin*)), m_widget, SIGNAL(renderPluginInitialized(RenderPlugin*)) ); // react to some signals of m_map m_widget->connect( &m_map, SIGNAL(themeChanged(QString)), m_widget, SLOT(updateMapTheme()) ); m_widget->connect( &m_map, SIGNAL(viewContextChanged(ViewContext)), m_widget, SIGNAL(viewContextChanged(ViewContext)) ); m_widget->connect( &m_map, SIGNAL(repaintNeeded(QRegion)), m_widget, SLOT(update()) ); m_widget->connect( &m_map, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), m_widget, SLOT(updateSystemBackgroundAttribute()) ); m_widget->connect( &m_map, SIGNAL(renderStatusChanged(RenderStatus)), m_widget, SIGNAL(renderStatusChanged(RenderStatus)) ); m_widget->connect( &m_map, SIGNAL(renderStateChanged(RenderState)), m_widget, SIGNAL(renderStateChanged(RenderState)) ); m_widget->connect( m_model.fileManager(), SIGNAL(centeredDocument(GeoDataLatLonBox)), m_widget, SLOT(centerOn(GeoDataLatLonBox)) ); // Show a progress dialog when the model calculates new map tiles. m_widget->connect( &m_model, SIGNAL( creatingTilesStart( TileCreator*, const QString&, const QString& ) ), m_widget, SLOT( creatingTilesStart( TileCreator*, const QString&, const QString& ) ) ); m_popupmenu = new MarbleWidgetPopupMenu( m_widget, &m_model ); m_routingLayer = new RoutingLayer( m_widget, m_widget ); m_routingLayer->setPlacemarkModel( 0 ); QObject::connect( m_routingLayer, SIGNAL(repaintNeeded(QRect)), m_widget, SLOT(update()) ); m_mapInfoDialog = new PopupLayer( m_widget, m_widget ); m_mapInfoDialog->setVisible( false ); m_widget->connect( m_mapInfoDialog, SIGNAL(repaintNeeded()), m_widget, SLOT(update()) ); m_map.addLayer( m_mapInfoDialog ); setInputHandler(); m_widget->setMouseTracking( true ); m_map.addLayer( &m_customPaintLayer ); m_widget->connect( m_inputhandler, SIGNAL(mouseClickGeoPosition(qreal,qreal,GeoDataCoordinates::Unit)), m_widget, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)) ); m_widget->setHighlightEnabled( true ); } void MarbleWidgetPrivate::setInputHandler() { setInputHandler( new MarbleWidgetInputHandler( &m_presenter, m_widget ) ); } void MarbleWidgetPrivate::setInputHandler( MarbleWidgetInputHandler *handler ) { delete m_inputhandler; m_inputhandler = handler; if ( m_inputhandler ) { m_widget->installEventFilter( m_inputhandler ); QObject::connect( m_inputhandler, SIGNAL(mouseClickScreenPosition(int,int)), m_widget, SLOT(notifyMouseClick(int,int)) ); QObject::connect( m_inputhandler, SIGNAL(mouseMoveGeoPosition(QString)), m_widget, SIGNAL(mouseMoveGeoPosition(QString)) ); } } void MarbleWidgetPrivate::updateSystemBackgroundAttribute() { // We only have to repaint the background every time if the earth // doesn't cover the whole image. const bool isOn = m_map.viewport()->mapCoversViewport() && !m_model.mapThemeId().isEmpty(); m_widget->setAttribute( Qt::WA_NoSystemBackground, isOn ); } // ---------------------------------------------------------------- MarbleModel *MarbleWidget::model() { return &d->m_model; } const MarbleModel *MarbleWidget::model() const { return &d->m_model; } ViewportParams* MarbleWidget::viewport() { return d->m_map.viewport(); } const ViewportParams* MarbleWidget::viewport() const { return d->m_map.viewport(); } MarbleWidgetPopupMenu *MarbleWidget::popupMenu() { return d->m_popupmenu; } void MarbleWidget::setInputHandler( MarbleWidgetInputHandler *handler ) { d->setInputHandler(handler); } MarbleWidgetInputHandler *MarbleWidget::inputHandler() const { return d->m_inputhandler; } int MarbleWidget::radius() const { return d->m_map.radius(); } void MarbleWidget::setRadius( int radius ) { d->m_map.setRadius( radius ); } qreal MarbleWidget::moveStep() const { return d->m_presenter.moveStep(); } int MarbleWidget::zoom() const { return d->m_presenter.logzoom(); } int MarbleWidget::tileZoomLevel() const { return d->m_map.tileZoomLevel(); } int MarbleWidget::minimumZoom() const { return d->m_map.minimumZoom(); } int MarbleWidget::maximumZoom() const { return d->m_map.maximumZoom(); } QVector MarbleWidget::whichFeatureAt( const QPoint &curpos ) const { return d->m_map.whichFeatureAt( curpos ); } QList MarbleWidget::whichItemAt( const QPoint &curpos ) const { return d->m_map.whichItemAt( curpos ); } void MarbleWidget::addLayer( LayerInterface *layer ) { d->m_map.addLayer( layer ); } void MarbleWidget::removeLayer( LayerInterface *layer ) { d->m_map.removeLayer( layer ); } Marble::TextureLayer* MarbleWidget::textureLayer() const { return d->m_map.textureLayer(); } QPixmap MarbleWidget::mapScreenShot() { return QPixmap::grabWidget( this ); } RenderStatus MarbleWidget::renderStatus() const { return d->m_map.renderStatus(); } RenderState MarbleWidget::renderState() const { return d->m_map.renderState(); } void MarbleWidget::setHighlightEnabled(bool enabled) { if ( enabled ) { connect( this, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &d->m_map, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), Qt::UniqueConnection ); } else { disconnect( this, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)), &d->m_map, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)) ); } } bool MarbleWidget::showOverviewMap() const { return d->m_map.showOverviewMap(); } bool MarbleWidget::showScaleBar() const { return d->m_map.showScaleBar(); } bool MarbleWidget::showCompass() const { return d->m_map.showCompass(); } bool MarbleWidget::showClouds() const { return d->m_map.showClouds(); } bool MarbleWidget::showSunShading() const { return d->m_map.showSunShading(); } bool MarbleWidget::showCityLights() const { return d->m_map.showCityLights(); } bool MarbleWidget::isLockedToSubSolarPoint() const { return d->m_map.isLockedToSubSolarPoint(); } bool MarbleWidget::isSubSolarPointIconVisible() const { return d->m_map.isSubSolarPointIconVisible(); } bool MarbleWidget::showAtmosphere() const { return d->m_map.showAtmosphere(); } bool MarbleWidget::showCrosshairs() const { return d->m_map.showCrosshairs(); } bool MarbleWidget::showGrid() const { return d->m_map.showGrid(); } bool MarbleWidget::showPlaces() const { return d->m_map.showPlaces(); } bool MarbleWidget::showCities() const { return d->m_map.showCities(); } bool MarbleWidget::showTerrain() const { return d->m_map.showTerrain(); } bool MarbleWidget::showOtherPlaces() const { return d->m_map.showOtherPlaces(); } bool MarbleWidget::showRelief() const { return d->m_map.showRelief(); } bool MarbleWidget::showIceLayer() const { return d->m_map.showIceLayer(); } bool MarbleWidget::showBorders() const { return d->m_map.showBorders(); } bool MarbleWidget::showRivers() const { return d->m_map.showRivers(); } bool MarbleWidget::showLakes() const { return d->m_map.showLakes(); } bool MarbleWidget::showFrameRate() const { return d->m_showFrameRate; } bool MarbleWidget::showBackground() const { return d->m_map.showBackground(); } quint64 MarbleWidget::volatileTileCacheLimit() const { return d->m_map.volatileTileCacheLimit(); } void MarbleWidget::setZoom( int newZoom, FlyToMode mode ) { d->m_presenter.setZoom( newZoom, mode ); } void MarbleWidget::zoomView( int zoom, FlyToMode mode ) { d->m_presenter.zoomView( zoom, mode ); } void MarbleWidget::zoomViewBy( int zoomStep, FlyToMode mode ) { d->m_presenter.zoomViewBy( zoomStep, mode ); } void MarbleWidget::zoomIn( FlyToMode mode ) { d->m_presenter.zoomIn( mode ); } void MarbleWidget::zoomOut( FlyToMode mode ) { d->m_presenter.zoomOut( mode ); } void MarbleWidget::rotateBy( const qreal deltaLon, const qreal deltaLat, FlyToMode mode ) { d->m_presenter.rotateBy( deltaLon, deltaLat, mode ); } void MarbleWidget::centerOn( const qreal lon, const qreal lat, bool animated ) { d->m_presenter.centerOn( lon, lat, animated ); } void MarbleWidget::centerOn( const GeoDataCoordinates &position, bool animated ) { d->m_presenter.centerOn( position, animated ); } void MarbleWidget::centerOn( const GeoDataLatLonBox &box, bool animated ) { d->m_presenter.centerOn( box, animated ); } void MarbleWidget::centerOn( const GeoDataPlacemark& placemark, bool animated ) { d->m_presenter.centerOn( placemark, animated ); } void MarbleWidget::setCenterLatitude( qreal lat, FlyToMode mode ) { d->m_presenter.setCenterLatitude( lat, mode ); } void MarbleWidget::setCenterLongitude( qreal lon, FlyToMode mode ) { d->m_presenter.setCenterLongitude( lon, mode ); } Projection MarbleWidget::projection() const { return d->m_map.projection(); } void MarbleWidget::setProjection( Projection projection ) { d->m_map.setProjection( projection ); } void MarbleWidget::setProjection( int projection ) { setProjection( Projection( qAbs( projection ) % (Mercator+1) ) ); } void MarbleWidget::moveLeft( FlyToMode mode ) { d->m_presenter.moveByStep( -1, 0, mode ); } void MarbleWidget::moveRight( FlyToMode mode ) { d->m_presenter.moveByStep( 1, 0, mode ); } void MarbleWidget::moveUp( FlyToMode mode ) { d->m_presenter.moveByStep( 0, -1, mode ); } void MarbleWidget::moveDown( FlyToMode mode ) { d->m_presenter.moveByStep( 0, 1, mode ); } void MarbleWidget::leaveEvent( QEvent* ) { emit mouseMoveGeoPosition( QCoreApplication::translate( "Marble", NOT_AVAILABLE ) ); } void MarbleWidget::resizeEvent( QResizeEvent *event ) { setUpdatesEnabled( false ); d->m_map.setSize( event->size() ); setUpdatesEnabled( true ); QWidget::resizeEvent( event ); } void MarbleWidget::connectNotify( const QMetaMethod &signal ) { if ( d->m_inputhandler && signal == QMetaMethod::fromSignal( &MarbleWidget::mouseMoveGeoPosition ) ) { d->m_inputhandler->setPositionSignalConnected( true ); } } void MarbleWidget::disconnectNotify( const QMetaMethod &signal ) { if ( d->m_inputhandler && signal == QMetaMethod::fromSignal( &MarbleWidget::mouseMoveGeoPosition ) ) { d->m_inputhandler->setPositionSignalConnected( false ); } } bool MarbleWidget::screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const { return d->m_map.screenCoordinates( lon, lat, x, y ); } bool MarbleWidget::geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { return d->m_map.geoCoordinates( x, y, lon, lat, unit ); } qreal MarbleWidget::centerLatitude() const { return d->m_map.centerLatitude(); } qreal MarbleWidget::centerLongitude() const { return d->m_map.centerLongitude(); } QRegion MarbleWidget::mapRegion() const { return viewport()->mapRegion(); } void MarbleWidget::paintEvent( QPaintEvent *evt ) { QTime t; t.start(); QPaintDevice *paintDevice = this; QImage image; if (!isEnabled()) { // If the globe covers fully the screen then we can use the faster // RGB32 as there are no translucent areas involved. QImage::Format imageFormat = ( d->m_map.viewport()->mapCoversViewport() ) ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; // Paint to an intermediate image image = QImage( rect().size(), imageFormat ); image.fill( Qt::transparent ); paintDevice = ℑ } { // FIXME: Better way to get the GeoPainter // Create a painter that will do the painting. GeoPainter geoPainter( paintDevice, d->m_map.viewport(), d->m_map.mapQuality() ); d->m_map.paint( geoPainter, evt->rect() ); } if ( !isEnabled() ) { // Draw a grayscale version of the intermediate image QRgb* pixel = reinterpret_cast( image.scanLine( 0 )); for (int i=0; im_showFrameRate ) { QPainter painter( this ); FpsLayer fpsPainter( &t ); fpsPainter.paint( &painter ); const qreal fps = 1000.0 / (qreal)( t.elapsed() + 1 ); emit framesPerSecond( fps ); } } void MarbleWidget::customPaint( GeoPainter *painter ) { Q_UNUSED( painter ); /* This is a NOOP in the base class*/ } void MarbleWidget::goHome( FlyToMode mode ) { d->m_presenter.goHome( mode ); } QString MarbleWidget::mapThemeId() const { return d->m_model.mapThemeId(); } void MarbleWidget::setMapThemeId( const QString& mapThemeId ) { d->m_map.setMapThemeId( mapThemeId ); } void MarbleWidgetPrivate::updateMapTheme() { m_map.removeLayer( m_routingLayer ); m_widget->setRadius( m_widget->radius() ); // Corrects zoom range, if needed if (m_model.planetId() == QLatin1String("earth")) { m_map.addLayer( m_routingLayer ); } emit m_widget->themeChanged( m_map.mapThemeId() ); // Now we want a full repaint as the atmosphere might differ m_widget->setAttribute( Qt::WA_NoSystemBackground, false ); m_widget->update(); } GeoSceneDocument *MarbleWidget::mapTheme() const { return d->m_model.mapTheme(); } void MarbleWidget::setPropertyValue( const QString& name, bool value ) { mDebug() << "In MarbleWidget the property " << name << "was set to " << value; d->m_map.setPropertyValue( name, value ); } void MarbleWidget::setShowOverviewMap( bool visible ) { d->m_map.setShowOverviewMap( visible ); } void MarbleWidget::setShowScaleBar( bool visible ) { d->m_map.setShowScaleBar( visible ); } void MarbleWidget::setShowCompass( bool visible ) { d->m_map.setShowCompass( visible ); } void MarbleWidget::setShowClouds( bool visible ) { d->m_map.setShowClouds( visible ); } void MarbleWidget::setShowSunShading( bool visible ) { d->m_map.setShowSunShading( visible ); } void MarbleWidget::setShowCityLights( bool visible ) { d->m_map.setShowCityLights( visible ); } void MarbleWidget::setLockToSubSolarPoint( bool visible ) { if ( d->m_map.isLockedToSubSolarPoint() != visible ) { // Toggling input modifies event filters, so avoid that if not needed d->m_map.setLockToSubSolarPoint( visible ); setInputEnabled( !d->m_map.isLockedToSubSolarPoint() ); } } void MarbleWidget::setSubSolarPointIconVisible( bool visible ) { if ( d->m_map.isSubSolarPointIconVisible() != visible ) { d->m_map.setSubSolarPointIconVisible( visible ); } QList pluginList = renderPlugins(); QList::const_iterator i = pluginList.constBegin(); QList::const_iterator const end = pluginList.constEnd(); for (; i != end; ++i ) { if ((*i)->nameId() == QLatin1String("sun")) { (*i)->setVisible( visible ); } } } void MarbleWidget::setShowAtmosphere( bool visible ) { d->m_map.setShowAtmosphere( visible ); } void MarbleWidget::setShowCrosshairs( bool visible ) { d->m_map.setShowCrosshairs( visible ); } void MarbleWidget::setShowGrid( bool visible ) { d->m_map.setShowGrid( visible ); } void MarbleWidget::setShowPlaces( bool visible ) { d->m_map.setShowPlaces( visible ); } void MarbleWidget::setShowCities( bool visible ) { d->m_map.setShowCities( visible ); } void MarbleWidget::setShowTerrain( bool visible ) { d->m_map.setShowTerrain( visible ); } void MarbleWidget::setShowOtherPlaces( bool visible ) { d->m_map.setShowOtherPlaces( visible ); } void MarbleWidget::setShowRelief( bool visible ) { d->m_map.setShowRelief( visible ); } void MarbleWidget::setShowIceLayer( bool visible ) { d->m_map.setShowIceLayer( visible ); } void MarbleWidget::setShowBorders( bool visible ) { d->m_map.setShowBorders( visible ); } void MarbleWidget::setShowRivers( bool visible ) { d->m_map.setShowRivers( visible ); } void MarbleWidget::setShowLakes( bool visible ) { d->m_map.setShowLakes( visible ); } void MarbleWidget::setShowFrameRate( bool visible ) { d->m_showFrameRate = visible; update(); } void MarbleWidget::setShowBackground( bool visible ) { d->m_map.setShowBackground( visible ); } void MarbleWidget::setShowRuntimeTrace( bool visible ) { d->m_map.setShowRuntimeTrace( visible ); } bool MarbleWidget::showRuntimeTrace() const { return d->m_map.showRuntimeTrace(); } void MarbleWidget::setShowDebugPolygons( bool visible) { d->m_map.setShowDebugPolygons( visible ); } bool MarbleWidget::showDebugPolygons() const { return d->m_map.showDebugPolygons(); } void MarbleWidget::setShowDebugBatchRender( bool visible) { d->m_map.setShowDebugBatchRender( visible ); } bool MarbleWidget::showDebugBatchRender() const { return d->m_map.showDebugBatchRender(); } void MarbleWidget::setShowDebugPlacemarks( bool visible) { d->m_map.setShowDebugPlacemarks( visible ); } bool MarbleWidget::showDebugPlacemarks() const { return d->m_map.showDebugPlacemarks(); } void MarbleWidget::setShowTileId( bool visible ) { d->m_map.setShowTileId( visible ); } void MarbleWidget::notifyMouseClick( int x, int y) { qreal lon = 0; qreal lat = 0; bool const valid = geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); if ( valid ) { emit mouseClickGeoPosition( lon, lat, GeoDataCoordinates::Radian ); } } void MarbleWidget::clearVolatileTileCache() { mDebug() << "About to clear VolatileTileCache"; d->m_map.clearVolatileTileCache(); } void MarbleWidget::setVolatileTileCacheLimit( quint64 kiloBytes ) { d->m_map.setVolatileTileCacheLimit( kiloBytes ); } // This slot will called when the Globe starts to create the tiles. void MarbleWidget::creatingTilesStart( TileCreator *creator, const QString &name, const QString &description ) { QPointer dialog = new TileCreatorDialog( creator, this ); dialog->setSummary( name, description ); dialog->exec(); delete dialog; } MapQuality MarbleWidget::mapQuality( ViewContext viewContext ) const { return d->m_map.mapQuality( viewContext ); } void MarbleWidget::setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ) { d->m_map.setMapQualityForViewContext( quality, viewContext ); } ViewContext MarbleWidget::viewContext() const { return d->m_map.viewContext(); } void MarbleWidget::setViewContext( ViewContext viewContext ) { // Inform routing layer about view context change. If not done, // the routing layer causes severe performance problems when dragging the // map. So either do not remove this line, or keep a similar call in place // when you refactor it and test your changes wrt drag performance at // high zoom level with long routes! d->m_routingLayer->setViewContext( viewContext ); d->m_map.setViewContext( viewContext ); } bool MarbleWidget::animationsEnabled() const { return d->m_presenter.animationsEnabled(); } void MarbleWidget::setAnimationsEnabled( bool enabled ) { d->m_presenter.setAnimationsEnabled( enabled ); } AngleUnit MarbleWidget::defaultAngleUnit() const { return d->m_map.defaultAngleUnit(); } void MarbleWidget::setDefaultAngleUnit( AngleUnit angleUnit ) { d->m_map.setDefaultAngleUnit( angleUnit ); } QFont MarbleWidget::defaultFont() const { return d->m_map.defaultFont(); } void MarbleWidget::setDefaultFont( const QFont& font ) { d->m_map.setDefaultFont( font ); } void MarbleWidget::setSelection( const QRect& region ) { d->m_presenter.setSelection( region ); } qreal MarbleWidget::distance() const { return d->m_presenter.distance(); } void MarbleWidget::setDistance( qreal newDistance ) { d->m_presenter.setDistance( newDistance ); } QString MarbleWidget::distanceString() const { return d->m_presenter.distanceString(); } void MarbleWidget::setInputEnabled( bool enabled ) { //if input is set as enabled if ( enabled ) { if ( !d->m_inputhandler ) { d->setInputHandler(); } else { installEventFilter( d->m_inputhandler ); } } else // input is disabled { mDebug() << "MarbleWidget::disableInput"; removeEventFilter( d->m_inputhandler ); setCursor( Qt::ArrowCursor ); } } QList MarbleWidget::renderPlugins() const { return d->m_map.renderPlugins(); } void MarbleWidget::readPluginSettings( QSettings& settings ) { for( RenderPlugin *plugin: renderPlugins() ) { settings.beginGroup(QLatin1String("plugin_") + plugin->nameId()); QHash hash; for ( const QString& key: settings.childKeys() ) { hash.insert( key, settings.value( key ) ); } plugin->setSettings( hash ); settings.endGroup(); } } void MarbleWidget::writePluginSettings( QSettings& settings ) const { for( RenderPlugin *plugin: renderPlugins() ) { settings.beginGroup(QLatin1String("plugin_") + plugin->nameId()); QHash hash = plugin->settings(); QHash::iterator it = hash.begin(); while( it != hash.end() ) { settings.setValue( it.key(), it.value() ); ++it; } settings.endGroup(); } } QList MarbleWidget::floatItems() const { return d->m_map.floatItems(); } AbstractFloatItem * MarbleWidget::floatItem( const QString &nameId ) const { return d->m_map.floatItem( nameId ); } void MarbleWidget::changeEvent( QEvent * event ) { if ( event->type() == QEvent::EnabledChange ) { setInputEnabled(isEnabled()); } QWidget::changeEvent(event); } void MarbleWidget::flyTo( const GeoDataLookAt &newLookAt, FlyToMode mode ) { d->m_presenter.flyTo( newLookAt, mode ); } void MarbleWidget::reloadMap() { d->m_map.reload(); } void MarbleWidget::downloadRegion( QVector const & pyramid ) { d->m_map.downloadRegion( pyramid ); } GeoDataLookAt MarbleWidget::lookAt() const { return d->m_presenter.lookAt(); } GeoDataCoordinates MarbleWidget::focusPoint() const { return d->m_map.viewport()->focusPoint(); } void MarbleWidget::setFocusPoint( const GeoDataCoordinates &focusPoint ) { d->m_map.viewport()->setFocusPoint( focusPoint ); } void MarbleWidget::resetFocusPoint() { d->m_map.viewport()->resetFocusPoint(); } qreal MarbleWidget::radiusFromDistance( qreal distance ) const { return d->m_presenter.radiusFromDistance( distance ); } qreal MarbleWidget::distanceFromRadius( qreal radius ) const { return d->m_presenter.distanceFromRadius( radius ); } qreal MarbleWidget::zoomFromDistance( qreal distance ) const { return d->m_presenter.zoomFromDistance( distance ); } qreal MarbleWidget::distanceFromZoom( qreal zoom ) const { return d->m_presenter.distanceFromZoom( zoom ); } RoutingLayer* MarbleWidget::routingLayer() { return d->m_routingLayer; } PopupLayer *MarbleWidget::popupLayer() { return d->m_mapInfoDialog; } const StyleBuilder* MarbleWidget::styleBuilder() const { return d->m_map.styleBuilder(); } +void MarbleWidget::setHeading( qreal heading ) +{ + d->m_map.setHeading( heading ); +} + +qreal MarbleWidget::heading() const +{ + return d->m_map.heading(); +} + } #include "moc_MarbleWidget.cpp" diff --git a/src/lib/marble/MarbleWidget.h b/src/lib/marble/MarbleWidget.h index 6f80fc609..4fdc4981d 100644 --- a/src/lib/marble/MarbleWidget.h +++ b/src/lib/marble/MarbleWidget.h @@ -1,1153 +1,1157 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2008 Torsten Rahn // Copyright 2007 Inge Wallin // #ifndef MARBLE_MARBLEWIDGET_H #define MARBLE_MARBLEWIDGET_H /** @file * This file contains the headers for MarbleWidget. * * @author Torsten Rahn * @author Inge Wallin */ #include #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" // types needed in all of marble. #include "marble_export.h" // Qt class QSettings; class QPixmap; namespace Marble { class AbstractDataPluginItem; class AbstractFloatItem; class GeoDataLatLonAltBox; class GeoDataLatLonBox; class GeoDataFeature; class GeoDataPlacemark; class GeoDataLookAt; class GeoPainter; class GeoSceneDocument; class LayerInterface; class MarbleModel; class MarbleWidgetPopupMenu; class MarbleWidgetInputHandler; class MarbleWidgetPrivate; class RenderPlugin; class RenderState; class RoutingLayer; class TextureLayer; class TileCoordsPyramid; class TileCreator; class ViewportParams; class PopupLayer; class StyleBuilder; /** * @short A widget class that displays a view of the earth. * * This widget displays a view of the earth or any other globe, * depending on which dataset is used. The user can navigate the * globe using either a control widget, e.g. the MarbleNavigator, or * the mouse. The mouse and keyboard control is done through a * MarbleWidgetInputHandler. Only some aspects of the widget can be * controlled by the mouse and/or keyboard. * * By clicking on the globe and moving the mouse, the position can be * changed. The user can also zoom by using the scroll wheel of the * mouse in the widget. The zoom value is not tied to any units, but * is an abstract value without any physical meaning. A value around * 1000 shows the full globe in a normal-sized window. Higher zoom * values give a more zoomed-in view. * * The MarbleWidget owns a data model to work. This model is contained * in the MarbleModel class, and it is painted by using a MarbleMap. * The widget takes care of creating the map and model. A MarbleModel * contains several datatypes, among them tiles which provide the * background, vectors which provide things like country * borders and coastlines and placemarks which can show points * of interest, such as cities, mountain tops or the poles. * * In addition to navigating with the mouse, you can also use it to * get information about items on the map. You can either click on a * placemark with the left mouse button or with the right mouse button * anywhere on the map. * * The left mouse button opens up a menu with all the placemarks * within a certain distance from the mouse pointer. When you choose * one item from the menu, Marble will open up a dialog window with * some information about the placemark and also try to connect to * Wikipedia to retrieve an article about it. If there is such an * article, you will get a mini-browser window with the article in a tab. * * @see MarbleNavigator * @see MarbleMap * @see MarbleModel */ class MARBLE_EXPORT MarbleWidget : public QWidget { Q_OBJECT #ifdef MARBLE_DBUS Q_CLASSINFO("D-Bus Interface", "org.kde.MarbleWidget") #endif Q_PROPERTY(int zoom READ zoom WRITE setZoom) Q_PROPERTY(QString mapThemeId READ mapThemeId WRITE setMapThemeId) Q_PROPERTY(int projection READ projection WRITE setProjection) Q_PROPERTY(qreal longitude READ centerLongitude WRITE setCenterLongitude) Q_PROPERTY(qreal latitude READ centerLatitude WRITE setCenterLatitude) Q_PROPERTY(bool showOverviewMap READ showOverviewMap WRITE setShowOverviewMap) Q_PROPERTY(bool showScaleBar READ showScaleBar WRITE setShowScaleBar) Q_PROPERTY(bool showCompass READ showCompass WRITE setShowCompass) Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid) Q_PROPERTY(bool showClouds READ showClouds WRITE setShowClouds) Q_PROPERTY(bool showSunShading READ showSunShading WRITE setShowSunShading) Q_PROPERTY(bool showCityLights READ showCityLights WRITE setShowCityLights) Q_PROPERTY(bool isLockedToSubSolarPoint READ isLockedToSubSolarPoint WRITE setLockToSubSolarPoint) Q_PROPERTY(bool isSubSolarPointIconVisible READ isSubSolarPointIconVisible WRITE setSubSolarPointIconVisible) Q_PROPERTY(bool showAtmosphere READ showAtmosphere WRITE setShowAtmosphere) Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs) Q_PROPERTY(bool showPlaces READ showPlaces WRITE setShowPlaces) Q_PROPERTY(bool showCities READ showCities WRITE setShowCities) Q_PROPERTY(bool showTerrain READ showTerrain WRITE setShowTerrain) Q_PROPERTY(bool showOtherPlaces READ showOtherPlaces WRITE setShowOtherPlaces) Q_PROPERTY(bool showRelief READ showRelief WRITE setShowRelief) Q_PROPERTY(bool showIceLayer READ showIceLayer WRITE setShowIceLayer) Q_PROPERTY(bool showBorders READ showBorders WRITE setShowBorders) Q_PROPERTY(bool showRivers READ showRivers WRITE setShowRivers) Q_PROPERTY(bool showLakes READ showLakes WRITE setShowLakes) Q_PROPERTY(ViewContext viewContext READ viewContext WRITE setViewContext NOTIFY viewContextChanged) Q_PROPERTY( RenderStatus renderStatus READ renderStatus NOTIFY renderStatusChanged ) Q_PROPERTY(quint64 volatileTileCacheLimit READ volatileTileCacheLimit WRITE setVolatileTileCacheLimit) public: /** * @brief Construct a new MarbleWidget. * @param parent the parent widget * * This constructor should be used when you will only use one * MarbleWidget. The widget will create its own MarbleModel when * created. */ explicit MarbleWidget( QWidget *parent = 0 ); ~MarbleWidget() override; /// @name Access to helper objects //@{ /** * @brief Return the model that this view shows. */ MarbleModel *model(); const MarbleModel *model() const; ViewportParams *viewport(); const ViewportParams *viewport() const; MarbleWidgetPopupMenu *popupMenu(); /** * Returns the current input handler */ MarbleWidgetInputHandler *inputHandler() const; /** * @brief Set the input handler */ void setInputHandler( MarbleWidgetInputHandler *handler ); /** * @brief Returns a list of all RenderPlugins on the widget, this includes float items * @return the list of RenderPlugins */ QList renderPlugins() const; /** * @brief Returns a list of all FloatItems on the widget * @return the list of the floatItems */ QList floatItems() const; /** * @brief Returns the FloatItem with the given id * @return The pointer to the requested floatItem, * * If no item is found the null pointer is returned. */ AbstractFloatItem * floatItem( const QString &nameId ) const; /** * Reads the plugin settings from the passed QSettings. * You shouldn't use this in a KDE application as these use KConfig. Here you could * use MarblePart which is handling this automatically. * @param settings The QSettings object to be used. */ void readPluginSettings( QSettings& settings ); /** * Writes the plugin settings in the passed QSettings. * You shouldn't use this in a KDE application as these use KConfig. Here you could * use MarblePart which is handling this automatically. * @param settings The QSettings object to be used. */ void writePluginSettings( QSettings& settings ) const; /** * @brief Retrieve the view context (i.e. still or animated map) */ ViewContext viewContext() const; /** * @brief Get the GeoSceneDocument object of the current map theme */ GeoSceneDocument * mapTheme() const; /** * @brief Returns all widgets of dataPlugins on the position curpos */ QList whichItemAt( const QPoint& curpos ) const; /** * @brief Add a layer to be included in rendering. */ void addLayer( LayerInterface *layer ); /** * @brief Remove a layer from being included in rendering. */ void removeLayer( LayerInterface *layer ); RoutingLayer* routingLayer(); PopupLayer* popupLayer(); /** * @since 0.26.0 */ const StyleBuilder* styleBuilder() const; /** * @brief Get the Projection used for the map * @return @c Spherical a Globe * @return @c Equirectangular a flat map * @return @c Mercator another flat map */ Projection projection() const; // int projection() const; //@} /// @name Visible map area //@{ /** * @brief Get the ID of the current map theme * To ensure that a unique identifier is being used the theme does NOT * get represented by its name but the by relative location of the file * that specifies the theme: * * Example: * mapThemeId = "earth/bluemarble/bluemarble.dgml" */ QString mapThemeId() const; /** * @brief Return the projected region which describes the (shape of the) projected surface. */ QRegion mapRegion() const; /** * @brief Return the radius of the globe in pixels. */ int radius() const; /** * @brief Return the current zoom amount. */ int zoom() const; int tileZoomLevel() const; /** * @brief Return the current distance. */ qreal distance() const; /** * @brief Return the current distance string. */ QString distanceString() const; /** * @brief Return the minimum zoom value for the current map theme. */ int minimumZoom() const; /** * @brief Return the minimum zoom value for the current map theme. */ int maximumZoom() const; //@} /// @name Position management //@{ /** * @brief Get the screen coordinates corresponding to geographical coordinates in the widget. * @param lon the lon coordinate of the requested pixel position * @param lat the lat coordinate of the requested pixel position * @param x the x coordinate of the pixel is returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen */ bool screenCoordinates( qreal lon, qreal lat, qreal& x, qreal& y ) const; /** * @brief Get the earth coordinates corresponding to a pixel in the widget. * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @param lon the longitude angle is returned through this parameter * @param lat the latitude angle is returned through this parameter * @return @c true if the pixel (x, y) is within the globe * @c false if the pixel (x, y) is outside the globe, i.e. in space. */ bool geoCoordinates( int x, int y, qreal& lon, qreal& lat, GeoDataCoordinates::Unit = GeoDataCoordinates::Degree ) const; /** * @brief Return the longitude of the center point. * @return The longitude of the center point in degree. */ qreal centerLongitude() const; /** * @brief Return the latitude of the center point. * @return The latitude of the center point in degree. */ qreal centerLatitude() const; + qreal heading() const; + /** * @brief Return how much the map will move if one of the move slots are called. * @return The move step. */ qreal moveStep() const; /** * @brief Return the lookAt */ GeoDataLookAt lookAt() const; /** * @return The current point of focus, e.g. the point that is not moved * when changing the zoom level. If not set, it defaults to the * center point. * @see centerLongitude centerLatitude setFocusPoint resetFocusPoint */ GeoDataCoordinates focusPoint() const; /** * @brief Change the point of focus, overridding any previously set focus point. * @param focusPoint New focus point * @see focusPoint resetFocusPoint */ void setFocusPoint( const GeoDataCoordinates &focusPoint ); /** * @brief Invalidate any focus point set with @ref setFocusPoint. * @see focusPoint setFocusPoint */ void resetFocusPoint(); /** * @brief Return the globe radius (pixel) for the given distance (km) */ qreal radiusFromDistance( qreal distance ) const; /** * @brief Return the distance (km) at the given globe radius (pixel) */ qreal distanceFromRadius( qreal radius ) const; /** * Returns the zoom value (no unit) corresponding to the given camera distance (km) */ qreal zoomFromDistance( qreal distance ) const; /** * Returns the distance (km) corresponding to the given zoom value */ qreal distanceFromZoom( qreal zoom ) const; //@} /// @name Placemark management //@{ QVector whichFeatureAt( const QPoint& ) const; //@} /// @name Float items and map appearance //@{ /** * @brief Return whether the overview map is visible. * @return The overview map visibility. */ bool showOverviewMap() const; /** * @brief Return whether the scale bar is visible. * @return The scale bar visibility. */ bool showScaleBar() const; /** * @brief Return whether the compass bar is visible. * @return The compass visibility. */ bool showCompass() const; /** * @brief Return whether the cloud cover is visible. * @return The cloud cover visibility. */ bool showClouds() const; /** * @brief Return whether the night shadow is visible. * @return visibility of night shadow */ bool showSunShading() const; /** * @brief Return whether the city lights are shown instead of the night shadow. * @return visibility of city lights */ bool showCityLights() const; /** * @brief Return whether the globe is locked to the sub solar point * @return if globe is locked to sub solar point */ bool isLockedToSubSolarPoint() const; /** * @brief Return whether the sun icon is shown in the sub solar point. * @return visibility of the sun icon in the sub solar point */ bool isSubSolarPointIconVisible() const; /** * @brief Return whether the atmospheric glow is visible. * @return The cloud cover visibility. */ bool showAtmosphere() const; /** * @brief Return whether the crosshairs are visible. * @return The crosshairs' visibility. */ bool showCrosshairs() const; /** * @brief Return whether the coordinate grid is visible. * @return The coordinate grid visibility. */ bool showGrid() const; /** * @brief Return whether the place marks are visible. * @return The place mark visibility. */ bool showPlaces() const; /** * @brief Return whether the city place marks are visible. * @return The city place mark visibility. */ bool showCities() const; /** * @brief Return whether the terrain place marks are visible. * @return The terrain place mark visibility. */ bool showTerrain() const; /** * @brief Return whether other places are visible. * @return The visibility of other places. */ bool showOtherPlaces() const; /** * @brief Return whether the relief is visible. * @return The relief visibility. */ bool showRelief() const; /** * @brief Return whether the ice layer is visible. * @return The ice layer visibility. */ bool showIceLayer() const; /** * @brief Return whether the borders are visible. * @return The border visibility. */ bool showBorders() const; /** * @brief Return whether the rivers are visible. * @return The rivers' visibility. */ bool showRivers() const; /** * @brief Return whether the lakes are visible. * @return The lakes' visibility. */ bool showLakes() const; /** * @brief Return whether the frame rate gets displayed. * @return the frame rates visibility */ bool showFrameRate() const; bool showBackground() const; /** * @brief Retrieve the map quality depending on the view context */ MapQuality mapQuality( ViewContext = Still ) const; /** * @brief Retrieve whether travels to a point should get animated */ bool animationsEnabled() const; AngleUnit defaultAngleUnit() const; void setDefaultAngleUnit( AngleUnit angleUnit ); QFont defaultFont() const; void setDefaultFont( const QFont& font ); //@} /// @name Tile management //@{ /** * @brief Returns the limit in kilobytes of the volatile (in RAM) tile cache. * @return the limit of volatile tile cache */ quint64 volatileTileCacheLimit() const; //@} /// @name Miscellaneous //@{ /** * @brief Return a QPixmap with the current contents of the widget. */ QPixmap mapScreenShot(); //@} /// @todo Enable this instead of the zoomView slot below for proper deprecation warnings /// around Marble 1.8 // @deprecated Please use setZoom //MARBLE_DEPRECATED( void zoomView( int zoom, FlyToMode mode = Instant ) ); /** * Summarized render status of the current map view * @see renderState */ RenderStatus renderStatus() const; /** * Detailed render status of the current map view */ RenderState renderState() const; /** * Toggle whether regions are highlighted when user selects them */ void setHighlightEnabled( bool enabled ); public Q_SLOTS: /// @name Position management slots //@{ /** * @brief Set the radius of the globe in pixels. * @param radius The new globe radius value in pixels. */ void setRadius( int radius ); /** * @brief Zoom the view to a certain zoomlevel * @param zoom the new zoom level. * * The zoom level is an abstract value without physical * interpretation. A zoom value around 1000 lets the viewer see * all of the earth in the default window. */ void setZoom( int zoom, FlyToMode mode = Instant ); /** * @deprecated To be removed soon. Please use setZoom instead. Same parameters. */ void zoomView( int zoom, FlyToMode mode = Instant ); /** * @brief Zoom the view by a certain step * @param zoomStep the difference between the old zoom and the new */ void zoomViewBy( int zoomStep, FlyToMode mode = Instant ); /** * @brief Zoom in by the amount zoomStep. */ void zoomIn( FlyToMode mode = Automatic ); /** * @brief Zoom out by the amount zoomStep. */ void zoomOut( FlyToMode mode = Automatic ); /** * @brief Set the distance of the observer to the globe in km. * @param distance The new distance in km. */ void setDistance( qreal distance ); /** * @brief Rotate the view by the two angles phi and theta. * @param deltaLon an angle that specifies the change in terms of longitude * @param deltaLat an angle that specifies the change in terms of latitude * * This function rotates the view by two angles, * deltaLon ("theta") and deltaLat ("phi"). * If we start at (0, 0), the result will be the exact equivalent * of (lon, lat), otherwise the resulting angle will be the sum of * the previous position and the two offsets. */ void rotateBy( const qreal deltaLon, const qreal deltaLat, FlyToMode mode = Instant ); /** * @brief Center the view on a geographical point * @param lat an angle in degrees parallel to the latitude lines * +90(N) - -90(S) * @param lon an angle in degrees parallel to the longitude lines * +180(W) - -180(E) */ void centerOn( const qreal lon, const qreal lat, bool animated = false ); /** * @brief Center the view on a point * This method centers the Marble map on the point described by the latitude * and longitude in the GeoDataCoordinate parameter @c point. It also zooms * the map to be at the elevation described by the altitude. If this is * not the desired functionality or you do not have an accurate altitude * then use @see centerOn(qreal, qreal, bool) * @param point the point in 3 dimensions above the globe to move the view * to. It will always be looking vertically down. */ void centerOn( const GeoDataCoordinates &point, bool animated = false ); /** * @brief Center the view on a bounding box so that it completely fills the viewport * This method not only centers on the center of the GeoDataLatLon box but it also * adjusts the zoom of the marble widget so that the LatLon box provided fills * the viewport. * @param box The GeoDataLatLonBox to zoom and move the MarbleWidget to. */ void centerOn( const GeoDataLatLonBox& box, bool animated = false ); /** * @brief Center the view on a placemark according to the following logic: * - if the placemark has a lookAt, zoom and center on that lookAt * - otherwise use the placemark geometry's latLonAltBox * @param box The GeoDataPlacemark to zoom and move the MarbleWidget to. */ void centerOn( const GeoDataPlacemark& placemark, bool animated = false ); /** * @brief Set the latitude for the center point * @param lat the new value for the latitude in degree. * @param mode the FlyToMode that will be used. */ void setCenterLatitude( qreal lat, FlyToMode mode = Instant ); /** * @brief Set the longitude for the center point * @param lon the new value for the longitude in degree. * @param mode the FlyToMode that will be used. */ void setCenterLongitude( qreal lon, FlyToMode mode = Instant ); + void setHeading( qreal heading ); + /** * @brief Move left by the moveStep. */ void moveLeft( FlyToMode mode = Automatic ); /** * @brief Move right by the moveStep. */ void moveRight( FlyToMode mode = Automatic ); /** * @brief Move up by the moveStep. */ void moveUp( FlyToMode mode = Automatic ); /** * @brief Move down by the moveStep. */ void moveDown( FlyToMode mode = Automatic ); /** * @brief Center the view on the default start point with the default zoom. */ void goHome( FlyToMode mode = Automatic ); /** * @brief Change the camera position to the given position. * @param lookAt New camera position. Changing the camera position means * that both the current center position as well as the zoom value may change * @param mode Interpolation type for intermediate camera positions. Automatic * (default) chooses a suitable interpolation among Instant, Lenar and Jump. * Instant will directly set the new zoom and position values, while * Linear results in a linear interpolation of intermediate center coordinates * along the sphere and a linear interpolation of changes in the camera distance * to the ground. Finally, Jump will behave the same as Linear with regard to * the center position interpolation, but use a parabolic height increase * towards the middle point of the intermediate positions. This appears * like a jump of the camera. */ void flyTo( const GeoDataLookAt &lookAt, FlyToMode mode = Automatic ); //@} /// @name Float items and map appearance slots //@{ /** * @brief Set the Projection used for the map * @param projection projection type (e.g. Spherical, Equirectangular, Mercator) */ void setProjection( int projection ); void setProjection( Projection projection ); /** * @brief Set a new map theme * @param maptheme The ID of the new maptheme. To ensure that a unique * identifier is being used the theme does NOT get represented by its * name but the by relative location of the file that specifies the theme: * * Example: * maptheme = "earth/bluemarble/bluemarble.dgml" */ void setMapThemeId( const QString& maptheme ); /** * @brief Sets the value of a map theme property * @param value value of the property (usually: visibility) * * Later on we might add a "setPropertyType and a QVariant * if needed. */ void setPropertyValue( const QString& name, bool value ); /** * @brief Set whether the overview map overlay is visible * @param visible visibility of the overview map */ void setShowOverviewMap( bool visible ); /** * @brief Set whether the scale bar overlay is visible * @param visible visibility of the scale bar */ void setShowScaleBar( bool visible ); /** * @brief Set whether the compass overlay is visible * @param visible visibility of the compass */ void setShowCompass( bool visible ); /** * @brief Set whether the cloud cover is visible * @param visible visibility of the cloud cover */ void setShowClouds( bool visible ); /** * @brief Set whether the night shadow is visible. * @param visibile visibility of shadow */ void setShowSunShading( bool visible ); /** * @brief Set whether city lights instead of night shadow are visible. * @param visible visibility of city lights */ void setShowCityLights( bool visible ); /** * @brief Set the globe locked to the sub solar point * @param vsible if globe is locked to the sub solar point */ void setLockToSubSolarPoint( bool visible ); /** * @brief Set whether the sun icon is shown in the sub solar point * @param visible if the sun icon is shown in the sub solar point */ void setSubSolarPointIconVisible( bool visible ); /** * @brief Set whether the atmospheric glow is visible * @param visible visibility of the atmospheric glow */ void setShowAtmosphere( bool visible ); /** * @brief Set whether the crosshairs are visible * @param visible visibility of the crosshairs */ void setShowCrosshairs( bool visible ); /** * @brief Set whether the coordinate grid overlay is visible * @param visible visibility of the coordinate grid */ void setShowGrid( bool visible ); /** * @brief Set whether the place mark overlay is visible * @param visible visibility of the place marks */ void setShowPlaces( bool visible ); /** * @brief Set whether the city place mark overlay is visible * @param visible visibility of the city place marks */ void setShowCities( bool visible ); /** * @brief Set whether the terrain place mark overlay is visible * @param visible visibility of the terrain place marks */ void setShowTerrain( bool visible ); /** * @brief Set whether the other places overlay is visible * @param visible visibility of other places */ void setShowOtherPlaces( bool visible ); /** * @brief Set whether the relief is visible * @param visible visibility of the relief */ void setShowRelief( bool visible ); /** * @brief Set whether the ice layer is visible * @param visible visibility of the ice layer */ void setShowIceLayer( bool visible ); /** * @brief Set whether the borders visible * @param visible visibility of the borders */ void setShowBorders( bool visible ); /** * @brief Set whether the rivers are visible * @param visible visibility of the rivers */ void setShowRivers( bool visible ); /** * @brief Set whether the lakes are visible * @param visible visibility of the lakes */ void setShowLakes( bool visible ); /** * @brief Set whether the frame rate gets shown * @param visible visibility of the frame rate */ void setShowFrameRate( bool visible ); void setShowBackground( bool visible ); /** * @brief Set whether the is tile is visible * NOTE: This is part of the transitional debug API * and might be subject to changes until Marble 0.8 * @param visible visibility of the tile */ void setShowTileId( bool visible ); /** * @brief Set whether the runtime tracing for layers gets shown * @param visible visibility of the runtime tracing */ void setShowRuntimeTrace( bool visible ); bool showRuntimeTrace() const; /** * @brief Set whether to enter the debug mode for * polygon node drawing * @param visible visibility of the node debug mode */ void setShowDebugPolygons( bool visible); bool showDebugPolygons() const; /** * @brief Set whether to enter the debug mode for * batch rendering * @param visible visibility of the batch rendering */ void setShowDebugBatchRender( bool visible); bool showDebugBatchRender() const; /** * @brief Set whether to enter the debug mode for * placemark drawing * @param visible visibility of the node debug mode */ void setShowDebugPlacemarks(bool visible); bool showDebugPlacemarks() const; /** * @brief Set the map quality for the specified view context. * * @param quality map quality for the specified view context * @param viewContext view context whose map quality should be set */ void setMapQualityForViewContext( MapQuality quality, ViewContext viewContext ); /** * @brief Set the view context (i.e. still or animated map) */ void setViewContext( ViewContext viewContext ); /** * @brief Set whether travels to a point should get animated */ void setAnimationsEnabled( bool enabled ); //@} /// @name Tile management slots //@{ void clearVolatileTileCache(); /** * @brief Set the limit of the volatile (in RAM) tile cache. * @param kilobytes The limit in kilobytes. */ void setVolatileTileCacheLimit( quint64 kiloBytes ); /** * @brief A slot that is called when the model starts to create new tiles. * @param creator the tile creator object. * @param name the name of the created theme. * @param description a descriptive text that can be shown in a dialog. * @see creatingTilesProgress * * This function is connected to the models signal with the same * name. When the model needs to create a cache of tiles in * several different resolutions, it will emit creatingTilesStart * once with a name of the theme and a descriptive text. The * widget can then pop up a dialog to explain why there is a * delay. The model will then call creatingTilesProgress several * times until the parameter reaches 100 (100%), after which the * creation process is finished. After this there will be no more * calls to creatingTilesProgress, and the poup dialog can then be * closed. */ void creatingTilesStart( TileCreator *creator, const QString& name, const QString& description ); /** * @brief Re-download all visible tiles. */ void reloadMap(); void downloadRegion( QVector const & ); //@} /// @name Miscellaneous slots //@{ /** * @brief Used to notify about the position of the mouse click */ void notifyMouseClick( int x, int y ); void setSelection( const QRect& region ); void setInputEnabled( bool ); TextureLayer *textureLayer() const; //@} Q_SIGNALS: /** * @brief Signal that the zoom has changed, and to what. * @param zoom The new zoom value. * @see setZoom() */ void zoomChanged( int zoom ); void distanceChanged( const QString& distanceString ); void tileLevelChanged( int level ); void viewContextChanged(ViewContext newViewContext); /** * @brief Signal that the theme has changed * @param theme Name of the new theme. */ void themeChanged( const QString& theme ); void projectionChanged( Projection ); void mouseMoveGeoPosition( const QString& ); void mouseClickGeoPosition( qreal lon, qreal lat, GeoDataCoordinates::Unit ); void framesPerSecond( qreal fps ); /** This signal is emit when a new rectangle region is selected over the map * The list of double values include coordinates in degrees using this order: * lon1, lat1, lon2, lat2 (or West, North, East, South) as left/top, right/bottom rectangle. */ void regionSelected( const QList& ); /** * This signal is emit when the settings of a plugin changed. */ void pluginSettingsChanged(); /** * @brief Signal that a render item has been initialized */ void renderPluginInitialized( RenderPlugin *renderPlugin ); /** * This signal is emitted when the visible region of the map changes. This typically happens * when the user moves the map around or zooms. */ void visibleLatLonAltBoxChanged( const GeoDataLatLonAltBox& visibleLatLonAltBox ); /** * @brief Emitted when the layer rendering status has changed * @param status New render status */ void renderStatusChanged( RenderStatus status ); void renderStateChanged( const RenderState &state ); void highlightedPlacemarksChanged( qreal lon, qreal lat, GeoDataCoordinates::Unit unit ); protected: /** * @brief Reimplementation of the leaveEvent() function in QWidget. */ void leaveEvent( QEvent *event ) override; /** * @brief Reimplementation of the paintEvent() function in QWidget. */ void paintEvent( QPaintEvent *event ) override; /** * @brief Reimplementation of the resizeEvent() function in QWidget. */ void resizeEvent( QResizeEvent *event ) override; void connectNotify(const QMetaMethod &signal) override; void disconnectNotify(const QMetaMethod &signal) override; /** * @brief Reimplementation of the changeEvent() function in QWidget to * react to changes of the enabled state */ void changeEvent( QEvent * event ) override; /** * @brief Enables custom drawing onto the MarbleWidget straight after * @brief the globe and before all other layers has been rendered. * @param painter * * @deprecated implement LayerInterface and add it using @p addLayer() */ virtual void customPaint( GeoPainter *painter ); private: Q_PRIVATE_SLOT( d, void updateMapTheme() ) Q_PRIVATE_SLOT( d, void updateSystemBackgroundAttribute() ) private: Q_DISABLE_COPY( MarbleWidget ) MarbleWidgetPrivate * const d; friend class MarbleWidgetPrivate; class CustomPaintLayer; friend class CustomPaintLayer; friend class MarbleWidgetDefaultInputHandler; }; } #endif diff --git a/src/lib/marble/ViewportParams.cpp b/src/lib/marble/ViewportParams.cpp index 4aeee83b6..58715f5a8 100644 --- a/src/lib/marble/ViewportParams.cpp +++ b/src/lib/marble/ViewportParams.cpp @@ -1,455 +1,485 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2007 Inge Wallin // Copyright 2008 Jens-Michael Hoffmann // Copyright 2010-2013 Bernhard Beschow // #include "ViewportParams.h" #include #include #include #include "MarbleDebug.h" #include "GeoDataLatLonAltBox.h" #include "SphericalProjection.h" #include "EquirectProjection.h" #include "MercatorProjection.h" #include "GnomonicProjection.h" #include "LambertAzimuthalProjection.h" #include "AzimuthalEquidistantProjection.h" #include "StereographicProjection.h" #include "VerticalPerspectiveProjection.h" namespace Marble { class ViewportParamsPrivate { public: ViewportParamsPrivate( Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size ); static const AbstractProjection *abstractProjection( Projection projection ); // These two go together. m_currentProjection points to one of // the static Projection classes at the bottom. Projection m_projection; const AbstractProjection *m_currentProjection; // Parameters that determine the painting qreal m_centerLongitude; qreal m_centerLatitude; + qreal m_heading; Quaternion m_planetAxis; // Position, coded in a quaternion matrix m_planetAxisMatrix; int m_radius; // Zoom level (pixels / globe radius) qreal m_angularResolution; QSize m_size; // width, height bool m_dirtyBox; GeoDataLatLonAltBox m_viewLatLonAltBox; static const SphericalProjection s_sphericalProjection; static const EquirectProjection s_equirectProjection; static const MercatorProjection s_mercatorProjection; static const GnomonicProjection s_gnomonicProjection; static const StereographicProjection s_stereographicProjection; static const LambertAzimuthalProjection s_lambertAzimuthalProjection; static const AzimuthalEquidistantProjection s_azimuthalEquidistantProjection; static const VerticalPerspectiveProjection s_verticalPerspectiveProjection; GeoDataCoordinates m_focusPoint; }; const SphericalProjection ViewportParamsPrivate::s_sphericalProjection; const EquirectProjection ViewportParamsPrivate::s_equirectProjection; const MercatorProjection ViewportParamsPrivate::s_mercatorProjection; const GnomonicProjection ViewportParamsPrivate::s_gnomonicProjection; const StereographicProjection ViewportParamsPrivate::s_stereographicProjection; const LambertAzimuthalProjection ViewportParamsPrivate::s_lambertAzimuthalProjection; const AzimuthalEquidistantProjection ViewportParamsPrivate::s_azimuthalEquidistantProjection; const VerticalPerspectiveProjection ViewportParamsPrivate::s_verticalPerspectiveProjection; ViewportParamsPrivate::ViewportParamsPrivate( Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size ) : m_projection( projection ), m_currentProjection( abstractProjection( projection ) ), m_centerLongitude( centerLongitude ), m_centerLatitude( centerLatitude ), + m_heading( 0 ), m_planetAxis(), m_planetAxisMatrix(), m_radius( radius ), m_angularResolution(4.0 / abs(m_radius)), m_size( size ), m_dirtyBox( true ), m_viewLatLonAltBox() { } const AbstractProjection *ViewportParamsPrivate::abstractProjection(Projection projection) { switch ( projection ) { case Spherical: return &s_sphericalProjection; case Equirectangular: return &s_equirectProjection; case Mercator: return &s_mercatorProjection; case Gnomonic: return &s_gnomonicProjection; case Stereographic: return &s_stereographicProjection; case LambertAzimuthal: return &s_lambertAzimuthalProjection; case AzimuthalEquidistant: return &s_azimuthalEquidistantProjection; case VerticalPerspective: return &s_verticalPerspectiveProjection; } return 0; } ViewportParams::ViewportParams() : d( new ViewportParamsPrivate( Spherical, 0, 0, 2000, QSize( 100, 100 ) ) ) { centerOn( d->m_centerLongitude, d->m_centerLatitude ); } ViewportParams::ViewportParams( Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size ) : d( new ViewportParamsPrivate( projection, centerLongitude, centerLatitude, radius, size ) ) { centerOn( d->m_centerLongitude, d->m_centerLatitude ); } ViewportParams::~ViewportParams() { delete d; } // ================================================================ // Getters and setters Projection ViewportParams::projection() const { return d->m_projection; } const AbstractProjection *ViewportParams::currentProjection() const { return d->m_currentProjection; } void ViewportParams::setProjection(Projection newProjection) { d->m_projection = newProjection; d->m_currentProjection = ViewportParamsPrivate::abstractProjection( newProjection ); // We now need to reset the planetAxis to make sure // that it's a valid axis orientation! // So this line is important (although it might look odd) ! : centerOn( d->m_centerLongitude, d->m_centerLatitude ); } int ViewportParams::polarity() const { // For mercator this just gives the extreme latitudes // instead of the actual poles but it works fine as well: GeoDataCoordinates northPole( 0.0, +currentProjection()->maxLat() ); GeoDataCoordinates southPole( 0.0, -currentProjection()->maxLat() ); bool globeHidesN, globeHidesS; qreal x; qreal yN, yS; currentProjection()->screenCoordinates( northPole, this, x, yN, globeHidesN ); currentProjection()->screenCoordinates( southPole, this, x, yS, globeHidesS ); int polarity = 0; // case of the flat map: if ( !globeHidesN && !globeHidesS ) { if ( yN < yS ) { polarity = +1; } if ( yS < yN ) { polarity = -1; } } else { if ( !globeHidesN && yN < height() / 2 ) { polarity = +1; } if ( !globeHidesN && yN > height() / 2 ) { polarity = -1; } if ( !globeHidesS && yS > height() / 2 ) { polarity = +1; } if ( !globeHidesS && yS < height() / 2 ) { polarity = -1; } } return polarity; } int ViewportParams::radius() const { return d->m_radius; } void ViewportParams::setRadius(int newRadius) { if ( newRadius > 0 ) { d->m_dirtyBox = true; d->m_radius = newRadius; d->m_angularResolution = 4.0 / d->m_radius; } } void ViewportParams::centerOn( qreal lon, qreal lat ) { if ( !d->m_currentProjection->traversablePoles() ) { if ( lat > d->m_currentProjection->maxLat() ) lat = d->m_currentProjection->maxLat(); if ( lat < d->m_currentProjection->minLat() ) lat = d->m_currentProjection->minLat(); } else { while ( lat > M_PI ) lat -= 2 * M_PI; while ( lat < -M_PI ) lat += 2 * M_PI; } while ( lon > M_PI ) lon -= 2 * M_PI; while ( lon < -M_PI ) lon += 2 * M_PI; d->m_centerLongitude = lon; d->m_centerLatitude = lat; - d->m_planetAxis = Quaternion::fromEuler( -lat, lon, 0.0 ); + const Quaternion roll = Quaternion::fromEuler( 0, 0, d->m_heading ); + const Quaternion quat = Quaternion::fromEuler( -lat, lon, 0.0 ); + + d->m_planetAxis = quat * roll; d->m_planetAxis.normalize(); d->m_dirtyBox = true; d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix ); + d->m_planetAxis.normalize(); +} + +void ViewportParams::setHeading( qreal heading ) +{ + d->m_heading = heading; + + const Quaternion roll = Quaternion::fromEuler( 0, 0, heading ); + + const qreal centerLat = centerLatitude(); + const qreal centerLon = centerLongitude(); + + const Quaternion quat = Quaternion::fromEuler( -centerLat, centerLon, 0 ); + + d->m_planetAxis = quat * roll; + d->m_planetAxis.normalize(); + + d->m_dirtyBox = true; + d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix ); + d->m_planetAxis.normalize(); +} + +qreal ViewportParams::heading() const +{ + return d->m_heading; } Quaternion ViewportParams::planetAxis() const { return d->m_planetAxis; } const matrix &ViewportParams::planetAxisMatrix() const { return d->m_planetAxisMatrix; } int ViewportParams::width() const { return d->m_size.width(); } int ViewportParams::height() const { return d->m_size.height(); } QSize ViewportParams::size() const { return d->m_size; } void ViewportParams::setWidth(int newWidth) { setSize( QSize( newWidth, height() ) ); } void ViewportParams::setHeight(int newHeight) { setSize( QSize( width(), newHeight ) ); } void ViewportParams::setSize(const QSize& newSize) { if ( newSize == d->m_size ) return; d->m_dirtyBox = true; d->m_size = newSize; } // ================================================================ // Other functions qreal ViewportParams::centerLongitude() const { return d->m_centerLongitude; } qreal ViewportParams::centerLatitude() const { return d->m_centerLatitude; } const GeoDataLatLonAltBox& ViewportParams::viewLatLonAltBox() const { if (d->m_dirtyBox) { d->m_viewLatLonAltBox = d->m_currentProjection->latLonAltBox( QRect( QPoint( 0, 0 ), d->m_size ), this ); d->m_dirtyBox = false; } return d->m_viewLatLonAltBox; } GeoDataLatLonAltBox ViewportParams::latLonAltBox( const QRect &screenRect ) const { return d->m_currentProjection->latLonAltBox( screenRect, this ); } qreal ViewportParams::angularResolution() const { // We essentially divide the diameter by 180 deg and // take half of the result as a guess for the angle per pixel resolution. // d->m_angularResolution = 0.25 * M_PI / fabs( (qreal)(d->m_radius); return d->m_angularResolution; } bool ViewportParams::resolves ( const GeoDataLatLonBox &latLonBox, qreal pixel ) const { return latLonBox.width() + latLonBox.height() > pixel * d->m_angularResolution; } bool ViewportParams::resolves ( const GeoDataLatLonAltBox &latLonAltBox, qreal pixel, qreal altitude ) const { return latLonAltBox.width() + latLonAltBox.height() > pixel * d->m_angularResolution || latLonAltBox.maxAltitude() - latLonAltBox.minAltitude() > altitude; } bool ViewportParams::resolves ( const GeoDataCoordinates &coord1, const GeoDataCoordinates &coord2 ) const { qreal lon1, lat1; coord1.geoCoordinates( lon1, lat1 ); qreal lon2, lat2; coord2.geoCoordinates( lon2, lat2 ); // We take the manhattan length as an approximation for the distance return ( fabs( lon2 - lon1 ) + fabs( lat2 - lat1 ) > d->m_angularResolution ); } bool ViewportParams::screenCoordinates( const qreal lon, const qreal lat, qreal &x, qreal &y ) const { return d->m_currentProjection->screenCoordinates( lon, lat, this, x, y ); } bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint, qreal &x, qreal &y, bool &globeHidesPoint ) const { return d->m_currentProjection->screenCoordinates( geopoint, this, x, y, globeHidesPoint ); } bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint, qreal &x, qreal &y ) const { return d->m_currentProjection->screenCoordinates( geopoint, this, x, y ); } bool ViewportParams::screenCoordinates( const GeoDataCoordinates &coordinates, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { return d->m_currentProjection->screenCoordinates( coordinates, this, x, y, pointRepeatNum, size, globeHidesPoint ); } bool ViewportParams::screenCoordinates( const GeoDataLineString &lineString, QVector &polygons ) const { return d->m_currentProjection->screenCoordinates( lineString, this, polygons ); } bool ViewportParams::geoCoordinates( const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit ) const { return d->m_currentProjection->geoCoordinates( x, y, this, lon, lat, unit ); } bool ViewportParams::mapCoversViewport() const { return d->m_currentProjection->mapCoversViewport( this ); } QPainterPath ViewportParams::mapShape() const { return d->m_currentProjection->mapShape( this ); } QRegion ViewportParams::mapRegion() const { return d->m_currentProjection->mapRegion( this ); } GeoDataCoordinates ViewportParams::focusPoint() const { if (d->m_focusPoint.isValid()) { return d->m_focusPoint; } else { const qreal lon = d->m_centerLongitude; const qreal lat = d->m_centerLatitude; return GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Radian); } } void ViewportParams::setFocusPoint(const GeoDataCoordinates &focusPoint) { d->m_focusPoint = focusPoint; } void ViewportParams::resetFocusPoint() { d->m_focusPoint = GeoDataCoordinates(); } } diff --git a/src/lib/marble/ViewportParams.h b/src/lib/marble/ViewportParams.h index 3571d8bed..0595bf17f 100644 --- a/src/lib/marble/ViewportParams.h +++ b/src/lib/marble/ViewportParams.h @@ -1,223 +1,225 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2007 Inge Wallin // Copyright 2008 Jens-Michael Hoffmann // #ifndef MARBLE_VIEWPORTPARAMS_H #define MARBLE_VIEWPORTPARAMS_H /** @file * This file contains the headers for ViewportParams. * * @author Inge Wallin */ #include #include "GeoDataCoordinates.h" #include "Quaternion.h" #include "MarbleGlobal.h" #include "marble_export.h" class QPolygonF; class QPainterPath; namespace Marble { class GeoDataLatLonAltBox; class GeoDataLatLonBox; class GeoDataLineString; class AbstractProjection; class ViewportParamsPrivate; /** * @short A public class that controls what is visible in the viewport of a Marble map. * */ class MARBLE_EXPORT ViewportParams { public: ViewportParams( ); explicit ViewportParams( Projection projection, qreal centerLongitude = 0, qreal centerLatitude = 0, int radius = 2000, const QSize &size = QSize( 100, 100 ) ); ~ViewportParams(); // Getters and setters Projection projection() const; const AbstractProjection *currentProjection() const; void setProjection(Projection newProjection); int polarity() const; const GeoDataLatLonAltBox& viewLatLonAltBox() const; GeoDataLatLonAltBox latLonAltBox( const QRect &screenRect ) const; // Calculates an educated guess for the average angle in radians covered per pixel. // Given a certain resolution it doesn't make much sense // - to display an object that covers an angle that is smaller than that. // - to display two points as distinct points if they are separated by a // an angular distance that is smaller. Instead only one point should be shown. // So this method helps to filter out details. // It's somewhat related to http://en.wikipedia.org/wiki/Angular_resolution qreal angularResolution() const; // Determines whether a geographical feature is big enough so that it should // represent a single point on the screen already. // See angularResolution() bool resolves ( const GeoDataLatLonBox &latLonBox, qreal pixel = 2.0 ) const; bool resolves ( const GeoDataLatLonAltBox &latLonAltBox, qreal pixel = 2.0, qreal altitude = 10000.0 ) const; // Determines whether two points are located enough apart so that it makes // sense to display them as distinct points. If this is not the case // calculation and drawing of one point can be skipped as only a single // point will be displayed on the screen. bool resolves ( const GeoDataCoordinates &coord1, const GeoDataCoordinates &coord2 ) const; int radius() const; /** * @brief Change the radius of the planet * @param radius Size of the planet radius in pixel. Non-positive values are ignored. */ void setRadius(int radius); void centerOn( qreal lon, qreal lat ); + void setHeading( qreal heading ); Quaternion planetAxis() const; const matrix &planetAxisMatrix() const; int width() const; int height() const; QSize size() const; void setWidth(int newWidth); void setHeight(int newHeight); void setSize(const QSize& newSize); qreal centerLongitude() const; qreal centerLatitude() const; /** * @brief Get the screen coordinates corresponding to geographical coordinates in the map. * @param lon the lon coordinate of the requested pixel position in radians * @param lat the lat coordinate of the requested pixel position in radians * @param x the x coordinate of the pixel is returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen * * @see ViewportParams */ bool screenCoordinates( const qreal lon, const qreal lat, qreal &x, qreal &y ) const; /** * @brief Get the screen coordinates corresponding to geographical coordinates in the map. * * @param geopoint the point on earth, including altitude, that we want the coordinates for. * @param x the x coordinate of the pixel is returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @param globeHidesPoint whether the point gets hidden on the far side of the earth * * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen * * @see ViewportParams */ bool screenCoordinates( const GeoDataCoordinates &geopoint, qreal &x, qreal &y, bool &globeHidesPoint ) const; // Will just call the virtual version with a dummy globeHidesPoint. bool screenCoordinates( const GeoDataCoordinates &geopoint, qreal &x, qreal &y ) const; /** * @brief Get the coordinates of screen points for geographical coordinates in the map. * * @param coordinates the point on earth, including altitude, that we want the coordinates for. * @param x the x coordinates of the pixels are returned through this parameter * @param y the y coordinate of the pixel is returned through this parameter * @param pointRepeatNum the amount of times that a single geographical point gets represented on the map * @param globeHidesPoint whether the point gets hidden on the far side of the earth * * @return @c true if the geographical coordinates are visible on the screen * @c false if the geographical coordinates are not visible on the screen * * @see ViewportParams */ bool screenCoordinates( const GeoDataCoordinates &coordinates, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const; bool screenCoordinates( const GeoDataLineString &lineString, QVector &polygons ) const; /** * @brief Get the earth coordinates corresponding to a pixel in the map. * @param x the x coordinate of the pixel * @param y the y coordinate of the pixel * @param lon the longitude angle is returned through this parameter * @param lat the latitude angle is returned through this parameter * @param unit the unit of the angles for lon and lat. * @return @c true if the pixel (x, y) is within the globe * @c false if the pixel (x, y) is outside the globe, i.e. in space. */ bool geoCoordinates( const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit = GeoDataCoordinates::Degree ) const; + qreal heading() const; bool mapCoversViewport() const; QPainterPath mapShape() const; QRegion mapRegion() const; /** * @return The current point of focus, e.g. the point that is not moved * when changing the zoom level. If not set, it defaults to the * center point. * @see centerCoordinates setFocusPoint resetFocusPoint */ GeoDataCoordinates focusPoint() const; /** * @brief Change the point of focus, overridding any previously set focus point. * @param focusPoint New focus point * @see focusPoint resetFocusPoint */ void setFocusPoint(const GeoDataCoordinates &focusPoint); /** * @brief Invalidate any focus point set with @ref setFocusPoint. * @see focusPoint setFocusPoint */ void resetFocusPoint(); private: Q_DISABLE_COPY( ViewportParams ) ViewportParamsPrivate * const d; }; } #endif diff --git a/src/lib/marble/kineticmodel.cpp b/src/lib/marble/kineticmodel.cpp index a20fdc7dd..70a3fbd97 100644 --- a/src/lib/marble/kineticmodel.cpp +++ b/src/lib/marble/kineticmodel.cpp @@ -1,216 +1,263 @@ /* This file is part of the Ofi Labs X2 project. Copyright (C) 2010 Ariya Hidayat Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "kineticmodel.h" #include #include static const int KineticModelDefaultUpdateInterval = 15; // ms class KineticModelPrivate { public: QTimer ticker; int duration; QPointF position; + qreal heading; QPointF velocity; + qreal velocityHeading; QPointF deacceleration; + qreal deaccelerationHeading; QTime timestamp; QPointF lastPosition; + qreal lastHeading; + bool changingPosition; KineticModelPrivate(); }; KineticModelPrivate::KineticModelPrivate() : duration(1403) , position(0, 0) + , heading(0) , velocity(0, 0) + , velocityHeading(0) , deacceleration(0, 0) + , deaccelerationHeading(0) , lastPosition(0, 0) + , lastHeading(0) + , changingPosition(true) { } KineticModel::KineticModel(QObject *parent) : QObject(parent) , d_ptr(new KineticModelPrivate) { connect(&d_ptr->ticker, SIGNAL(timeout()), SLOT(update())); d_ptr->ticker.setInterval(KineticModelDefaultUpdateInterval); } KineticModel::~KineticModel() { } bool KineticModel::hasVelocity() const { return !d_ptr->velocity.isNull(); } int KineticModel::duration() const { return d_ptr->duration; } void KineticModel::setDuration(int ms) { d_ptr->duration = ms; } QPointF KineticModel::position() const { return d_ptr->position; } void KineticModel::setPosition(const QPointF& position) { setPosition( position.x(), position.y() ); } void KineticModel::setPosition(qreal posX, qreal posY) { d_ptr->position.setX( posX ); d_ptr->position.setY( posY ); int elapsed = d_ptr->timestamp.elapsed(); // too fast gives less accuracy if (elapsed < d_ptr->ticker.interval() / 2) { return; } qreal delta = static_cast( elapsed ) / 1000.0; QPointF lastSpeed = d_ptr->velocity; QPointF currentSpeed = ( d_ptr->position - d_ptr->lastPosition ) / delta; d_ptr->velocity = 0.2 * lastSpeed + 0.8 * currentSpeed; d_ptr->lastPosition = d_ptr->position; + d_ptr->changingPosition = true; + d_ptr->timestamp.start(); +} + +void KineticModel::setHeading(qreal heading) +{ + d_ptr->heading = heading; + + int elapsed = d_ptr->timestamp.elapsed(); + qreal delta = static_cast( elapsed ) / 1000.0; + + qreal lastSpeed = d_ptr->velocityHeading; + qreal currentSpeed = delta ? ( d_ptr->heading - d_ptr->lastHeading ) / delta : 0; + d_ptr->velocityHeading = 0.5 * lastSpeed + 0.2 * currentSpeed; + d_ptr->lastHeading = d_ptr->heading; + + d_ptr->changingPosition = false; d_ptr->timestamp.start(); } void KineticModel::jumpToPosition(const QPointF& position) { jumpToPosition( position.x(), position.y() ); } void KineticModel::jumpToPosition(qreal posX, qreal posY) { d_ptr->position.setX( posX ); d_ptr->position.setY( posY ); } int KineticModel::updateInterval() const { return d_ptr->ticker.interval(); } void KineticModel::setUpdateInterval(int ms) { d_ptr->ticker.setInterval(ms); } void KineticModel::stop() { Q_D(KineticModel); d->ticker.stop(); d->timestamp.start(); d->velocity = QPointF(0, 0); + d->velocityHeading = 0; } void KineticModel::start() { Q_D(KineticModel); // prevent kinetic spinning if last mouse move is too long ago const int elapsed = d->timestamp.elapsed(); if ( elapsed > 2 * d->ticker.interval() ) { d->ticker.stop(); emit finished(); return; } d->deacceleration = d->velocity * 1000 / ( 1 + d_ptr->duration ); if (d->deacceleration.x() < 0) { d->deacceleration.setX( -d->deacceleration.x() ); } if (d->deacceleration.y() < 0) { d->deacceleration.setY( -d->deacceleration.y() ); } + d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / ( 1 + d_ptr->duration ); + if (!d->ticker.isActive()) d->ticker.start(); } void KineticModel::update() { Q_D(KineticModel); int elapsed = qMin( d->timestamp.elapsed(), 100 ); // limit to 100msec to reduce catapult effect (bug 294608) qreal delta = static_cast(elapsed) / 1000.0; - d->position += d->velocity * delta; - QPointF vstep = d->deacceleration * delta; - - if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) { - d->velocity.setX( 0 ); + bool stop = false; + if (d->changingPosition) { + d->position += d->velocity * delta; + QPointF vstep = d->deacceleration * delta; + + if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) { + d->velocity.setX( 0 ); + } else { + if (d->velocity.x() > 0) + d->velocity.setX( d->velocity.x() - vstep.x() ); + else + d->velocity.setX( d->velocity.x() + vstep.x() ); + } + + if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) { + d->velocity.setY( 0 ); + } else { + if (d->velocity.y() > 0) + d->velocity.setY( d->velocity.y() - vstep.y() ); + else + d->velocity.setY( d->velocity.y() + vstep.y() ); + } + + stop = d->velocity.isNull(); + + emit positionChanged( d->position.x(), d->position.y() ); } else { - if (d->velocity.x() > 0) - d->velocity.setX( d->velocity.x() - vstep.x() ); - else - d->velocity.setX( d->velocity.x() + vstep.x() ); - } + d->heading += d->velocityHeading * delta; + qreal vstep = d->deaccelerationHeading * delta; // Always positive. + if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) { + d->velocityHeading = 0; + } else { + d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep; + } - if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) { - d->velocity.setY( 0 ); - } else { - if (d->velocity.y() > 0) - d->velocity.setY( d->velocity.y() - vstep.y() ); - else - d->velocity.setY( d->velocity.y() + vstep.y() ); - } + stop = !d->velocityHeading; - emit positionChanged( d->position.x(), d->position.y() ); + emit headingChanged( d->heading ); + } - if (d->velocity.isNull()) { + if (stop) { emit finished(); d->ticker.stop(); } d->timestamp.start(); } #include "moc_kineticmodel.cpp" diff --git a/src/lib/marble/kineticmodel.h b/src/lib/marble/kineticmodel.h index e95b6d440..b9433aca1 100644 --- a/src/lib/marble/kineticmodel.h +++ b/src/lib/marble/kineticmodel.h @@ -1,78 +1,80 @@ /* This file is part of the Ofi Labs X2 project. Copyright (C) 2010 Ariya Hidayat Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OFILABS_KINETICMODEL #define OFILABS_KINETICMODEL #include #include #include class KineticModelPrivate; class KineticModel: public QObject { Q_OBJECT Q_PROPERTY(int duration READ duration WRITE setDuration) Q_PROPERTY(QPointF position READ position NOTIFY positionChanged) Q_PROPERTY(int updateInterval READ updateInterval WRITE setUpdateInterval) public: explicit KineticModel(QObject *parent = 0); ~KineticModel() override; int duration() const; QPointF position() const; int updateInterval() const; bool hasVelocity() const; public Q_SLOTS: void setDuration(int ms); void setPosition(const QPointF& position); void setPosition(qreal posX, qreal posY); + void setHeading(qreal heading); void jumpToPosition(const QPointF& position); void jumpToPosition(qreal posX, qreal posY); void setUpdateInterval(int ms); void stop(); void start(); Q_SIGNALS: void positionChanged( qreal lon, qreal lat ); + void headingChanged( qreal heading ); void finished(); private Q_SLOTS: void update(); private: QScopedPointer d_ptr; Q_DECLARE_PRIVATE(KineticModel); Q_DISABLE_COPY(KineticModel); }; #endif