From a78198c12374724857f33fd8c7771984d4b7ae4e Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 May 2015 09:32:41 +0200 Subject: Add visible region support to Map Change-Id: I340f1ad3a623a04a1846c5a57ad2bbcb9e253556 Reviewed-by: Michal Klocek Reviewed-by: Alex Blasche --- examples/location/planespotter/planespotter.qml | 10 +- src/imports/location/qdeclarativegeomap.cpp | 128 +++++++++++++++++++++++- src/imports/location/qdeclarativegeomap_p.h | 12 ++- 3 files changed, 136 insertions(+), 14 deletions(-) diff --git a/examples/location/planespotter/planespotter.qml b/examples/location/planespotter/planespotter.qml index 59b8b00b..91d9e056 100644 --- a/examples/location/planespotter/planespotter.qml +++ b/examples/location/planespotter/planespotter.qml @@ -185,15 +185,7 @@ Window { } //! [CppPlane2] - onWidthChanged: { - if (width > 0) - mapOfEurope.fitViewportToGeoShape(viewOfEurope) - } - - onHeightChanged: { - if (height > 0) - mapOfEurope.fitViewportToGeoShape(viewOfEurope) - } + visibleRegion: viewOfEurope } Rectangle { diff --git a/src/imports/location/qdeclarativegeomap.cpp b/src/imports/location/qdeclarativegeomap.cpp index 3924a476..a8cdd25d 100644 --- a/src/imports/location/qdeclarativegeomap.cpp +++ b/src/imports/location/qdeclarativegeomap.cpp @@ -180,7 +180,8 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) m_error(QGeoServiceProvider::NoError), m_zoomLevel(8.0), m_componentCompleted(false), - m_mappingManagerInitialized(false) + m_mappingManagerInitialized(false), + m_pendingFitViewport(false) { setAcceptHoverEvents(false); setAcceptedMouseButtons(Qt::LeftButton); @@ -692,6 +693,113 @@ QGeoCoordinate QDeclarativeGeoMap::center() const return m_center; } + +/*! + \qmlproperty geoshape QtLocation::Map::visibleRegion + + This property holds the region which occupies the viewport of + the map. The camera is positioned in the center of the shape, and + at the largest integral zoom level possible which allows the + whole shape to be visible on the screen. This implies that + reading this property back shortly after having been set the + returned area is equal or larger than the set area. + + Setting this property implicitly changes the \l center and + \l zoomLevel of the map. Any previously set value to those + properties will be overridden. + + This property does not provide any change notifications. + + \since 5.5 +*/ +void QDeclarativeGeoMap::setVisibleRegion(const QGeoShape &shape) +{ + if (shape == m_region) + return; + + m_region = shape; + if (!shape.isValid()) { + // shape invalidated -> nothing to fit anymore + m_pendingFitViewport = false; + return; + } + + if (!width() || !height()) { + m_pendingFitViewport = true; + return; + } + + fitViewportToGeoShape(); +} + +QGeoShape QDeclarativeGeoMap::visibleRegion() const +{ + if (!width() || !height()) + return QGeoShape(); + + QGeoCoordinate tl = m_map->itemPositionToCoordinate(QDoubleVector2D(0, 0)); + QGeoCoordinate br = m_map->itemPositionToCoordinate(QDoubleVector2D(width(), height())); + + return QGeoRectangle(tl, br); +} + +void QDeclarativeGeoMap::fitViewportToGeoShape() +{ + double bboxWidth; + double bboxHeight; + QGeoCoordinate centerCoordinate; + + switch (m_region.type()) { + case QGeoShape::RectangleType: + { + QGeoRectangle rect = m_region; + QDoubleVector2D topLeftPoint = m_map->coordinateToItemPosition(rect.topLeft(), false); + QDoubleVector2D botRightPoint = m_map->coordinateToItemPosition(rect.bottomRight(), false); + bboxWidth = qAbs(topLeftPoint.x() - botRightPoint.x()); + bboxHeight = qAbs(topLeftPoint.y() - botRightPoint.y()); + centerCoordinate = rect.center(); + break; + } + case QGeoShape::CircleType: + { + QGeoCircle circle = m_region; + centerCoordinate = circle.center(); + QGeoCoordinate edge = centerCoordinate.atDistanceAndAzimuth(circle.radius(), 90); + QDoubleVector2D centerPoint = m_map->coordinateToItemPosition(centerCoordinate, false); + QDoubleVector2D edgePoint = m_map->coordinateToItemPosition(edge, false); + bboxWidth = qAbs(centerPoint.x() - edgePoint.x()) * 2; + bboxHeight = bboxWidth; + break; + } + case QGeoShape::UnknownType: + //Fallthrough to default + default: + return; + } + + // position camera to the center of bounding box + setProperty("center", QVariant::fromValue(centerCoordinate)); + + //If the shape is empty we just change centerposition, not zoom + if (bboxHeight == 0 && bboxWidth == 0) + return; + + // adjust zoom + double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); + double mapWidthRatio = width() / (width() + height()); + double zoomRatio; + + if (bboxWidthRatio > mapWidthRatio) + zoomRatio = bboxWidth / width(); + else + zoomRatio = bboxHeight / height(); + + qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); + + newZoom = std::floor(qMax(minimumZoomLevel(), (m_map->mapController()->zoom() + newZoom))); + setProperty("zoomLevel", QVariant::fromValue(newZoom)); +} + /*! \internal */ @@ -1035,11 +1143,28 @@ void QDeclarativeGeoMap::geometryChanged(const QRectF &newGeometry, const QRectF m_map->resize(newGeometry.width(), newGeometry.height()); QQuickItem::geometryChanged(newGeometry, oldGeometry); + + /*! + The fitViewportTo*() functions depend on a valid map geometry. + If they were called prior to the first resize they cause + the zoomlevel to jump to 0 (showing the world). Therefore the + calls were queued up until now. + + Multiple fitViewportTo*() calls replace each other. + */ + if (m_pendingFitViewport && width() && height()) { + fitViewportToGeoShape(); + m_pendingFitViewport = false; + } + } +// TODO Remove this function -> BC break /*! \qmlmethod QtLocation::Map::fitViewportToGeoShape(QGeoShape shape) + \internal + Fits the current viewport to the boundary of the shape. The camera is positioned in the center of the shape, and at the largest integral zoom level possible which allows the whole shape to be visible on screen @@ -1112,6 +1237,7 @@ void QDeclarativeGeoMap::fitViewportToGeoShape(const QVariant &variantShape) zoomRatio = bboxHeight / height(); qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); + newZoom = std::floor(qMax(minimumZoomLevel(), (m_map->mapController()->zoom() + newZoom))); setProperty("zoomLevel", QVariant::fromValue(newZoom)); } diff --git a/src/imports/location/qdeclarativegeomap_p.h b/src/imports/location/qdeclarativegeomap_p.h index a36f2991..93f9426b 100644 --- a/src/imports/location/qdeclarativegeomap_p.h +++ b/src/imports/location/qdeclarativegeomap_p.h @@ -44,10 +44,7 @@ #include #include #include - - - - +#include QT_BEGIN_NAMESPACE @@ -70,6 +67,7 @@ class QDeclarativeGeoMap : public QQuickItem Q_PROPERTY(QList mapItems READ mapItems NOTIFY mapItemsChanged) Q_PROPERTY(QGeoServiceProvider::Error error READ error NOTIFY errorChanged) Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(QGeoShape visibleRegion READ visibleRegion WRITE setVisibleRegion) Q_INTERFACES(QQmlParserStatus) public: @@ -95,6 +93,9 @@ public: void setCenter(const QGeoCoordinate ¢er); QGeoCoordinate center() const; + void setVisibleRegion(const QGeoShape &shape); + QGeoShape visibleRegion() const; + QQmlListProperty supportedMapTypes(); Q_INVOKABLE void removeMapItem(QDeclarativeGeoMapItemBase *item); @@ -161,6 +162,7 @@ private: void setupMapView(QDeclarativeGeoMapItemView *view); void populateMap(); void fitViewportToMapItemsRefine(bool refine); + void fitViewportToGeoShape(); bool isInteractive(); private: @@ -181,6 +183,8 @@ private: qreal m_zoomLevel; bool m_componentCompleted; bool m_mappingManagerInitialized; + QGeoShape m_region; + bool m_pendingFitViewport; friend class QDeclarativeGeoMapItem; friend class QDeclarativeGeoMapItemView; -- cgit v1.2.1