summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2017-08-04 13:22:43 +0200
committerPaolo Angelelli <paolo.angelelli@qt.io>2017-08-09 14:16:46 +0000
commit602cd4845dff2bbe5a8118899f1dfbcca9d614f1 (patch)
treea2e6c52c7d3982d883e6643a798b4c483c30b3d3
parentb6294cfca675bd5aa9a34e15c93692a0f1b8fdb2 (diff)
downloadqtlocation-602cd4845dff2bbe5a8118899f1dfbcca9d614f1.tar.gz
Fix visible region computation in QGeoProjectionWebMercator
Currently the visible region is calculated as the intersection between the viewing frustum and the map plane clipped against the map extended rectangle in mercator space (from -1 to 2 in x coords, 0 to 1 in y coords). The result is correct in the extended mercator space. However, this may lead to overlapping coordinates when converted back to latitude and longitude. For this reason, this patch changes the clipping geometry to be the map un-extended rectangle centered around the current map center. The result is a geometry that never wraps around or overlaps, thus removing the need for handling separately the case when the map is fully visible when returning the visible region. Task-number: QTBUG-57690 Change-Id: I8396c40a123ce94bff4388dfefbd8a694657b8bd Reviewed-by: BogDan Vatra <bogdan@kdab.com>
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap.cpp23
-rw-r--r--src/location/maps/qgeoprojection.cpp20
-rw-r--r--src/positioning/qlocationutils_p.h2
-rw-r--r--tests/auto/declarative_ui/tst_map.qml41
4 files changed, 70 insertions, 16 deletions
diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp
index 90ae2b34..dedb590e 100644
--- a/src/location/declarativemaps/qdeclarativegeomap.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomap.cpp
@@ -1323,20 +1323,21 @@ QGeoShape QDeclarativeGeoMap::visibleRegion() const
const QList<QDoubleVector2D> &visibleRegion = m_map->geoProjection().visibleRegion();
QGeoPath path;
- for (const QDoubleVector2D &c: visibleRegion)
+ for (int i = 0; i < visibleRegion.size(); ++i) {
+ const QDoubleVector2D &c = visibleRegion.at(i);
+ // If a segment spans more than half of the map longitudinally, split in 2.
+ if (i && qAbs(visibleRegion.at(i-1).x() - c.x()) >= 0.5) { // This assumes a segment is never >= 1.0 (whole map span)
+ QDoubleVector2D extraPoint = (visibleRegion.at(i-1) + c) * 0.5;
+ path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(extraPoint));
+ }
path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(c));
-
- QGeoRectangle vr = path.boundingGeoRectangle();
-
- bool empty = vr.topLeft().latitude() == vr.bottomRight().latitude() ||
- qFuzzyCompare(vr.topLeft().longitude(), vr.bottomRight().longitude()); // QTBUG-57690
-
- if (empty) {
- vr.setTopLeft(QGeoCoordinate(vr.topLeft().latitude(), -180));
- vr.setBottomRight(QGeoCoordinate(vr.bottomRight().latitude(), 180));
+ }
+ if (visibleRegion.size() >= 2 && qAbs(visibleRegion.last().x() - visibleRegion.first().x()) >= 0.5) {
+ QDoubleVector2D extraPoint = (visibleRegion.last() + visibleRegion.first()) * 0.5;
+ path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(extraPoint));
}
- return vr;
+ return path.boundingGeoRectangle();
}
/*!
diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp
index 609fb934..013a8c33 100644
--- a/src/location/maps/qgeoprojection.cpp
+++ b/src/location/maps/qgeoprojection.cpp
@@ -491,11 +491,17 @@ void QGeoProjectionWebMercator::updateVisibleRegion()
QDoubleVector2D bl = viewportToWrappedMapProjection(QDoubleVector2D(-1, 1 ));
QDoubleVector2D br = viewportToWrappedMapProjection(QDoubleVector2D( 1, 1 ));
+ // To make sure that what is returned can be safely converted back to lat/lon without risking overlaps
+ double mapLeftLongitude = QLocationUtils::mapLeftLongitude(m_cameraData.center().longitude());
+ double mapRightLongitude = QLocationUtils::mapRightLongitude(m_cameraData.center().longitude());
+ double leftX = geoToWrappedMapProjection(QGeoCoordinate(0, mapLeftLongitude)).x();
+ double rightX = geoToWrappedMapProjection(QGeoCoordinate(0, mapRightLongitude)).x();
+
QList<QDoubleVector2D> mapRect;
- mapRect.push_back(QDoubleVector2D(-1.0, 1.0));
- mapRect.push_back(QDoubleVector2D( 2.0, 1.0));
- mapRect.push_back(QDoubleVector2D( 2.0, 0.0));
- mapRect.push_back(QDoubleVector2D(-1.0, 0.0));
+ mapRect.push_back(QDoubleVector2D(leftX, 1.0));
+ mapRect.push_back(QDoubleVector2D(rightX, 1.0));
+ mapRect.push_back(QDoubleVector2D(rightX, 0.0));
+ mapRect.push_back(QDoubleVector2D(leftX, 0.0));
QList<QDoubleVector2D> viewportRect;
viewportRect.push_back(bl);
@@ -514,6 +520,12 @@ void QGeoProjectionWebMercator::updateVisibleRegion()
m_visibleRegion = QClipperUtils::pathToQList(res[0]); // Intersection between two convex quadrilaterals should always be a single polygon
m_projectableRegion.clear();
+ mapRect.clear();
+ // The full map rectangle in extended mercator space
+ mapRect.push_back(QDoubleVector2D(-1.0, 1.0));
+ mapRect.push_back(QDoubleVector2D( 2.0, 1.0));
+ mapRect.push_back(QDoubleVector2D( 2.0, 0.0));
+ mapRect.push_back(QDoubleVector2D(-1.0, 0.0));
if (m_cameraData.tilt() == 0) {
m_projectableRegion = mapRect;
} else {
diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h
index 75c4b7f4..69cf0fee 100644
--- a/src/positioning/qlocationutils_p.h
+++ b/src/positioning/qlocationutils_p.h
@@ -55,7 +55,7 @@
#include <qmath.h>
#include <QtPositioning/QGeoCoordinate>
-static const double offsetEpsilon = 0.0000000000001;
+static const double offsetEpsilon = 1e-12; // = 0.000000000001
static const double leftOffset = -180.0 + offsetEpsilon;
static const double rightOffset = 180.0 - offsetEpsilon;
diff --git a/tests/auto/declarative_ui/tst_map.qml b/tests/auto/declarative_ui/tst_map.qml
index 846a621f..408bf96e 100644
--- a/tests/auto/declarative_ui/tst_map.qml
+++ b/tests/auto/declarative_ui/tst_map.qml
@@ -30,6 +30,7 @@ import QtQuick 2.0
import QtTest 1.0
import QtPositioning 5.5
import QtLocation 5.9
+import QtLocation.Test 5.6
Item {
width:100
@@ -55,6 +56,10 @@ Item {
property variant coordinate3: QtPositioning.coordinate(50, 50, 0)
property variant coordinate4: QtPositioning.coordinate(80, 80, 0)
property variant coordinate5: QtPositioning.coordinate(20, 180)
+ property variant coordinateCenterVisibleRegion: QtPositioning.coordinate(27, 77)
+ property variant coordinateVisible1: QtPositioning.coordinate(28, 77)
+ property variant coordinateVisible2: QtPositioning.coordinate(33, 79.1)
+ property variant coordinateVisible3: QtPositioning.coordinate(27, 80.5)
property variant invalidCoordinate: QtPositioning.coordinate()
property variant altitudelessCoordinate: QtPositioning.coordinate(50, 50)
property bool allMapsReady: mapZoomOnCompleted.mapReady
@@ -84,6 +89,9 @@ Item {
}
}
+ Map { id: mapVisibleRegion; width: 800; height: 600;
+ center: coordinateCenterVisibleRegion; plugin: testPlugin; zoomLevel: 1.0 }
+
Map {id: map; plugin: testPlugin; center: coordinate1; width: 100; height: 100}
SignalSpy {id: mapCenterSpy; target: map; signalName: 'centerChanged'}
@@ -148,6 +156,39 @@ Item {
compare(map.center.latitude, 12)
}
+ function test_map_visible_region()
+ {
+ mapVisibleRegion.zoomLevel = 1.0
+ wait(50)
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3))
+
+ mapVisibleRegion.zoomLevel = 1.88
+ verify(LocationTestHelper.waitForPolished(mapVisibleRegion))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3))
+
+ mapVisibleRegion.zoomLevel = 2.12
+ verify(LocationTestHelper.waitForPolished(mapVisibleRegion))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3))
+
+ mapVisibleRegion.zoomLevel = 2.5
+ verify(LocationTestHelper.waitForPolished(mapVisibleRegion))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3))
+
+ mapVisibleRegion.zoomLevel = 2.7
+ verify(LocationTestHelper.waitForPolished(mapVisibleRegion))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2))
+ verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3))
+ }
+
function test_map_parameters()
{
// coordinate is set at map element declaration