diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/imports/location/qdeclarativegeomap.cpp | 105 | ||||
-rw-r--r-- | src/location/maps/qgeomap_p.h | 2 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmap.cpp | 14 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmap_p.h | 2 |
4 files changed, 91 insertions, 32 deletions
diff --git a/src/imports/location/qdeclarativegeomap.cpp b/src/imports/location/qdeclarativegeomap.cpp index 7647a70b..082eaf98 100644 --- a/src/imports/location/qdeclarativegeomap.cpp +++ b/src/imports/location/qdeclarativegeomap.cpp @@ -168,6 +168,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), @@ -771,32 +773,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: @@ -805,28 +838,36 @@ 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(); - - qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); + // position camera to the center of bounding box + setCenter(centerCoordinate); - newZoom = std::floor(qMax(minimumZoomLevel(), (m_map->mapController()->zoom() + newZoom))); - setProperty("zoomLevel", QVariant::fromValue(newZoom)); + // if the shape is empty we just change center position, not zoom + if (bboxHeight == 0 && bboxWidth == 0) + return; + 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(), m_map->mapController()->zoom() + - zoomRatio); + setZoomLevel(newZoom); m_validRegion = true; } diff --git a/src/location/maps/qgeomap_p.h b/src/location/maps/qgeomap_p.h index acc928ea..9f3043aa 100644 --- a/src/location/maps/qgeomap_p.h +++ b/src/location/maps/qgeomap_p.h @@ -83,6 +83,8 @@ public: virtual QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport = true) const = 0; virtual QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const = 0; + virtual QDoubleVector2D referenceCoordinateToItemPosition(const QGeoCoordinate &coordinate) const = 0; + virtual QGeoCoordinate referenceItemPositionToCoordinate(const QDoubleVector2D &pos) const = 0; virtual void prefetchData(); virtual void clearData(); diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp index 97747049..869a6f08 100644 --- a/src/location/maps/qgeotiledmap.cpp +++ b/src/location/maps/qgeotiledmap.cpp @@ -154,6 +154,20 @@ QDoubleVector2D QGeoTiledMap::coordinateToItemPosition(const QGeoCoordinate &coo return pos; } +QDoubleVector2D QGeoTiledMap::referenceCoordinateToItemPosition(const QGeoCoordinate &coordinate) const +{ + Q_D(const QGeoTiledMap); + QDoubleVector2D point = QGeoProjection::coordToMercator(coordinate); + return point * std::pow(2.0, d->m_cameraData.zoomLevel()) * d->m_cameraTiles->tileSize(); +} + +QGeoCoordinate QGeoTiledMap::referenceItemPositionToCoordinate(const QDoubleVector2D &pos) const +{ + Q_D(const QGeoTiledMap); + QDoubleVector2D point = pos / (std::pow(2.0, d->m_cameraData.zoomLevel()) * d->m_cameraTiles->tileSize()); + return QGeoProjection::mercatorToCoord(point); +} + QGeoTiledMapPrivate::QGeoTiledMapPrivate(QGeoTiledMappingManagerEngine *engine) : QGeoMapPrivate(engine), m_cache(engine->tileCache()), diff --git a/src/location/maps/qgeotiledmap_p.h b/src/location/maps/qgeotiledmap_p.h index 87dac5d1..0eb1574c 100644 --- a/src/location/maps/qgeotiledmap_p.h +++ b/src/location/maps/qgeotiledmap_p.h @@ -85,6 +85,8 @@ public: QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport = true) const Q_DECL_OVERRIDE; QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const Q_DECL_OVERRIDE; + QDoubleVector2D referenceCoordinateToItemPosition(const QGeoCoordinate &coordinate) const Q_DECL_OVERRIDE; + QGeoCoordinate referenceItemPositionToCoordinate(const QDoubleVector2D &pos) const Q_DECL_OVERRIDE; void prefetchData() Q_DECL_OVERRIDE; void clearData() Q_DECL_OVERRIDE; |