diff options
Diffstat (limited to 'src/imports/location/qdeclarativegeomap.cpp')
-rw-r--r-- | src/imports/location/qdeclarativegeomap.cpp | 147 |
1 files changed, 90 insertions, 57 deletions
diff --git a/src/imports/location/qdeclarativegeomap.cpp b/src/imports/location/qdeclarativegeomap.cpp index 69efef39..5d209bd7 100644 --- a/src/imports/location/qdeclarativegeomap.cpp +++ b/src/imports/location/qdeclarativegeomap.cpp @@ -133,31 +133,17 @@ QT_BEGIN_NAMESPACE \section2 Example Usage The following snippet shows a simple Map and the necessary Plugin type - to use it. The map is centered near Brisbane, Australia, zoomed out to the - minimum zoom level, with gesture interaction enabled. + to use it. The map is centered over Oslo, Norway, with zoom level 10. - \code - Plugin { - id: somePlugin - // code here to choose the plugin as necessary - } - - Map { - id: map - - plugin: somePlugin - - center { - latitude: -27 - longitude: 153 - } - zoomLevel: map.minimumZoomLevel - - gesture.enabled: true - } - \endcode + \quotefromfile minimal_map/main.qml + \skipto import + \printuntil } + \printline } + \skipto Map + \printuntil } + \printline } - \image api-map.png + \image minimal_map.png */ /*! @@ -167,6 +153,8 @@ QT_BEGIN_NAMESPACE application should open the link in a browser or display its contents to the user. */ +static const qreal EARTH_MEAN_RADIUS = 6371007.2; + QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) : QQuickItem(parent), m_plugin(0), @@ -181,7 +169,8 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) m_pendingFitViewport(false), m_copyrightsVisible(true), m_maximumViewportLatitude(0.0), - m_initialized(false) + m_initialized(false), + m_validRegion(false) { setAcceptHoverEvents(false); setAcceptedMouseButtons(Qt::LeftButton); @@ -544,7 +533,6 @@ void QDeclarativeGeoMap::mappingManagerInitialized() m_copyrights.data(), SLOT(copyrightsChanged(QString))); connect(m_copyrights.data(), SIGNAL(linkActivated(QString)), this, SIGNAL(copyrightLinkActivated(QString))); - connect(m_map, &QGeoMap::sgNodeChanged, this, &QQuickItem::update); // set visibility of copyright notice @@ -700,6 +688,8 @@ void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) m_cameraData.setZoomLevel(zoomLevel); } + m_validRegion = false; + if (centerHasChanged) emit centerChanged(m_cameraData.center()); emit zoomLevelChanged(m_cameraData.zoomLevel()); @@ -735,6 +725,7 @@ void QDeclarativeGeoMap::setCenter(const QGeoCoordinate ¢er) m_cameraData.setCenter(center); } + m_validRegion = false; emit centerChanged(m_cameraData.center()); } @@ -764,7 +755,7 @@ QGeoCoordinate QDeclarativeGeoMap::center() const */ void QDeclarativeGeoMap::setVisibleRegion(const QGeoShape &shape) { - if (shape == m_region) + if (shape == m_region && m_validRegion) return; m_region = shape; @@ -846,32 +837,63 @@ QColor QDeclarativeGeoMap::color() const void QDeclarativeGeoMap::fitViewportToGeoShape() { - if (!m_map) return; + int margins = 10; + if (!m_map || width() <= margins || height() <= margins) + return; - double bboxWidth; - double bboxHeight; - QGeoCoordinate centerCoordinate; + QGeoCoordinate topLeft; + QGeoCoordinate bottomRight; 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(); + topLeft = rect.topLeft(); + bottomRight = rect.bottomRight(); break; } case QGeoShape::CircleType: { + const double pi = M_PI; 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; + QGeoCoordinate centerCoordinate = circle.center(); + + // calculate geo bounding box of the circle + // circle tangential points with meridians and the north pole create + // spherical triangle, we use spherical law of sines + // sin(lon_delta_in_rad)/sin(r_in_rad) = + // sin(alpha_in_rad)/sin(pi/2 - lat_in_rad), where: + // * lon_delta_in_rad - delta of longitudes of circle center + // and tangential points + // * r_in_rad - angular radius of the circle + // * lat_in_rad - latitude of circle center + // * alpha_in_rad - angle between meridian and radius to the circle => + // this is tangential point => sin(alpha) = 1 + // * lat_delta_in_rad - delta of latitudes of circle center and + // latitude of points where great circle (going through circle + // center) crosses circle and the pole + + double r_in_rad = circle.radius() / EARTH_MEAN_RADIUS; // angular r + double lat_delta_in_deg = r_in_rad * 180 / pi; + double lon_delta_in_deg = std::asin(std::sin(r_in_rad) / + std::cos(centerCoordinate.latitude() * pi / 180)) * 180 / pi; + + topLeft.setLatitude(centerCoordinate.latitude() + lat_delta_in_deg); + topLeft.setLongitude(centerCoordinate.longitude() - lon_delta_in_deg); + bottomRight.setLatitude(centerCoordinate.latitude() + - lat_delta_in_deg); + bottomRight.setLongitude(centerCoordinate.longitude() + + lon_delta_in_deg); + + // adjust if circle reaches poles => cross all meridians and + // fit into Mercator projection bounds + if (topLeft.latitude() > 90 || bottomRight.latitude() < -90) { + topLeft.setLatitude(qMin(topLeft.latitude(), 85.05113)); + topLeft.setLongitude(-180.0); + bottomRight.setLatitude(qMax(bottomRight.latitude(), + -85.05113)); + bottomRight.setLongitude(180.0); + } break; } case QGeoShape::UnknownType: @@ -880,29 +902,40 @@ void QDeclarativeGeoMap::fitViewportToGeoShape() return; } - // position camera to the center of bounding box - setProperty("center", QVariant::fromValue(centerCoordinate)); + // adjust zoom, use reference world to keep things simple + // otherwise we would need to do the error prone longitudes + // wrapping + QDoubleVector2D topLeftPoint = + m_map->referenceCoordinateToItemPosition(topLeft); + QDoubleVector2D bottomRightPoint = + m_map->referenceCoordinateToItemPosition(bottomRight); - //If the shape is empty we just change centerposition, not zoom - if (bboxHeight == 0 && bboxWidth == 0) - return; + double bboxWidth = bottomRightPoint.x() - topLeftPoint.x(); + double bboxHeight = bottomRightPoint.y() - topLeftPoint.y(); - // adjust zoom - double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); - double mapWidthRatio = width() / (width() + height()); - double zoomRatio; + // find center of the bounding box + QGeoCoordinate centerCoordinate = + m_map->referenceItemPositionToCoordinate( + (topLeftPoint + bottomRightPoint)/2); - if (bboxWidthRatio > mapWidthRatio) - zoomRatio = bboxWidth / width(); - else - zoomRatio = bboxHeight / height(); + // position camera to the center of bounding box + setCenter(centerCoordinate); - qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); + // if the shape is empty we just change center position, not zoom + if (bboxHeight == 0 && bboxWidth == 0) + return; - newZoom = std::floor(qMax(minimumZoomLevel(), (zoomLevel() + newZoom))); - setProperty("zoomLevel", QVariant::fromValue(newZoom)); + double zoomRatio = qMax(bboxWidth / (width() - margins), + bboxHeight / (height() - margins)); + // fixme: use log2 with c++11 + zoomRatio = std::log(zoomRatio) / std::log(2.0); + double newZoom = qMax(minimumZoomLevel(), zoomLevel() + - zoomRatio); + setZoomLevel(newZoom); + m_validRegion = true; } + /*! \qmlproperty list<MapType> QtLocation::Map::supportedMapTypes |