summaryrefslogtreecommitdiff
path: root/src/imports/location/qdeclarativegeomap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/location/qdeclarativegeomap.cpp')
-rw-r--r--src/imports/location/qdeclarativegeomap.cpp105
1 files changed, 73 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;
}