summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2017-01-17 18:16:41 +0100
committerPaolo Angelelli <paolo.angelelli@qt.io>2017-01-26 14:45:37 +0000
commitf586ea00feb414fb0776aa2bc6fbbb356ac61c32 (patch)
tree181045b22dbfd1fbb64c9ffb9cea2f87d218d9bf
parent05c4125cd65e34d64befeef4c4c2aba1f3b86665 (diff)
downloadqtlocation-f586ea00feb414fb0776aa2bc6fbbb356ac61c32.tar.gz
Add rotation/tilting support to QGeoTiledMapScene/QGeoCameraTiles
This patch adds rotation and tilting support to QGeoTiledMapScene and QGeoCameraTiles. It also adds the related capabilities to the geoservice plugins mapping managers QML Api is added in a separate patch Change-Id: I3de1b52a9928c4856f6ee57ad39191acebe0f770 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/location/maps/qgeocameratiles.cpp188
-rw-r--r--src/location/maps/qgeotiledmapscene.cpp140
-rw-r--r--src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp7
-rw-r--r--src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp6
-rw-r--r--src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp7
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp6
-rw-r--r--tests/auto/declarative_ui/tst_map.qml8
7 files changed, 243 insertions, 119 deletions
diff --git a/src/location/maps/qgeocameratiles.cpp b/src/location/maps/qgeocameratiles.cpp
index 0eaa4668..27044ee0 100644
--- a/src/location/maps/qgeocameratiles.cpp
+++ b/src/location/maps/qgeocameratiles.cpp
@@ -41,12 +41,25 @@
#include <QtPositioning/private/qwebmercator_p.h>
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QtPositioning/private/qdoublevector3d_p.h>
+#include <QtPositioning/private/qlocationutils_p.h>
+#include <QtGui/QMatrix4x4>
#include <QVector>
#include <QMap>
#include <QPair>
#include <QSet>
#include <QSize>
#include <cmath>
+#include <limits>
+
+static QVector3D toVector3D(const QDoubleVector3D& in)
+{
+ return QVector3D(in.x(), in.y(), in.z());
+}
+
+static QDoubleVector3D toDoubleVector3D(const QVector3D& in)
+{
+ return QDoubleVector3D(in.x(), in.y(), in.z());
+}
QT_BEGIN_NAMESPACE
@@ -88,7 +101,7 @@ public:
void updateMetadata();
void updateGeometry();
- Frustum createFrustum(double fieldOfViewGradient) const;
+ Frustum createFrustum(double viewExpansion) const;
class LengthSorter
{
@@ -100,11 +113,21 @@ public:
}
};
+ struct ClippedFootprint
+ {
+ ClippedFootprint(const PolygonVector &left_, const PolygonVector &mid_, const PolygonVector &right_)
+ : left(left_), mid(mid_), right(right_)
+ {}
+ PolygonVector left;
+ PolygonVector mid;
+ PolygonVector right;
+ };
+
void appendZIntersects(const QDoubleVector3D &start, const QDoubleVector3D &end, double z, QVector<QDoubleVector3D> &results) const;
PolygonVector frustumFootprint(const Frustum &frustum) const;
QPair<PolygonVector, PolygonVector> splitPolygonAtAxisValue(const PolygonVector &polygon, int axis, double value) const;
- QPair<PolygonVector, PolygonVector> clipFootprintToMap(const PolygonVector &footprint) const;
+ ClippedFootprint clipFootprintToMap(const PolygonVector &footprint) const;
QList<QPair<double, int> > tileIntersections(double p1, int t1, double p2, int t2) const;
QSet<QGeoTileSpec> tilesFromPolygon(const PolygonVector &polygon) const;
@@ -259,29 +282,36 @@ void QGeoCameraTilesPrivate::updateGeometry()
PolygonVector footprint = frustumFootprint(f);
// Clip the polygon to the map, split it up if it cross the dateline
- QPair<PolygonVector, PolygonVector> polygons = clipFootprintToMap(footprint);
+ ClippedFootprint polygons = clipFootprintToMap(footprint);
- if (!polygons.first.isEmpty()) {
- QSet<QGeoTileSpec> tilesLeft = tilesFromPolygon(polygons.first);
+ if (!polygons.left.isEmpty()) {
+ QSet<QGeoTileSpec> tilesLeft = tilesFromPolygon(polygons.left);
m_tiles.unite(tilesLeft);
}
- if (!polygons.second.isEmpty()) {
- QSet<QGeoTileSpec> tilesRight = tilesFromPolygon(polygons.second);
+ if (!polygons.right.isEmpty()) {
+ QSet<QGeoTileSpec> tilesRight = tilesFromPolygon(polygons.right);
+ m_tiles.unite(tilesRight);
+ }
+
+ if (!polygons.mid.isEmpty()) {
+ QSet<QGeoTileSpec> tilesRight = tilesFromPolygon(polygons.mid);
m_tiles.unite(tilesRight);
}
}
-Frustum QGeoCameraTilesPrivate::createFrustum(double fieldOfViewGradient) const
+Frustum QGeoCameraTilesPrivate::createFrustum(double viewExpansion) const
{
+ double apertureSize = 1.0;
+ if (m_camera.fieldOfView() != 90.0) //aperture(90 / 2) = 1
+ apertureSize = tan(QLocationUtils::radians(m_camera.fieldOfView()) * 0.5);
QDoubleVector3D center = m_sideLength * QWebMercator::coordToMercator(m_camera.center());
- center.setZ(0.0);
double f = qMin(m_screenSize.width(), m_screenSize.height());
- double z = std::pow(2.0, m_camera.zoomLevel() - m_intZoomLevel) * m_tileSize;
+ double z = std::pow(2.0, m_camera.zoomLevel() - m_intZoomLevel) * m_tileSize; // between 1 and 2 * m_tileSize
- double altitude = f / (2.0 * z);
+ double altitude = (f / (2.0 * z)) / apertureSize;
QDoubleVector3D eye = center;
eye.setZ(altitude);
@@ -289,28 +319,48 @@ Frustum QGeoCameraTilesPrivate::createFrustum(double fieldOfViewGradient) const
QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
QDoubleVector3D up = QDoubleVector3D::normal(side, view);
+ QMatrix4x4 mBearing;
+ // The rotation direction here is the opposite of QGeoTiledMapScene::setupCamera,
+ // as this is basically rotating the map against a fixed view frustum.
+ mBearing.rotate(1.0 * m_camera.bearing(), toVector3D(view));
+ up = toDoubleVector3D(mBearing * toVector3D(up));
+
+ // same for tilting
+ QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
+ QMatrix4x4 mTilt;
+ mTilt.rotate(-1.0 * m_camera.tilt(), toVector3D(side2));
+ eye = toDoubleVector3D((mTilt * toVector3D(view)) + toVector3D(center));
+
+ view = eye - center;
+ side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
+ up = QDoubleVector3D::normal(view, side2);
+
double nearPlane = 1 / (4.0 * m_tileSize );
- double farPlane = altitude + 1.0;
+ // farPlane plays a role on how much gets clipped when the map gets tilted. It used to be altitude + 1.0
+ // The value of 8.0 has been chosen as an acceptable compromise.
+ // TODO: use m_camera.clipDistance(); when this will be introduced
+ double farPlane = altitude + 8.0;
double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height();
- double hn,wn,hf,wf = 0.0;
-
- // fixes field of view at 45 degrees
- // this assumes that viewSize = 2*nearPlane x 2*nearPlane
+ // Half values. Half width near, far, height near, far.
+ double hhn,hwn,hhf,hwf = 0.0;
+ // This used to fix the (half) field of view at 45 degrees
+ // half because this assumed that viewSize = 2*nearPlane x 2*nearPlane
+ viewExpansion *= apertureSize;
if (aspectRatio > 1.0) {
- hn = 2 * fieldOfViewGradient * nearPlane;
- wn = hn * aspectRatio;
+ hhn = viewExpansion * nearPlane;
+ hwn = hhn * aspectRatio;
- hf = 2 * fieldOfViewGradient * farPlane;
- wf = hf * aspectRatio;
+ hhf = viewExpansion * farPlane;
+ hwf = hhf * aspectRatio;
} else {
- wn = 2 * fieldOfViewGradient * nearPlane;
- hn = wn / aspectRatio;
+ hwn = viewExpansion * nearPlane;
+ hhn = hwn / aspectRatio;
- wf = 2 * fieldOfViewGradient * farPlane;
- hf = wf / aspectRatio;
+ hwf = viewExpansion * farPlane;
+ hhf = hwf / aspectRatio;
}
QDoubleVector3D d = center - eye;
@@ -323,15 +373,15 @@ Frustum QGeoCameraTilesPrivate::createFrustum(double fieldOfViewGradient) const
Frustum frustum;
- frustum.topLeftFar = cf + (up * hf / 2) - (right * wf / 2);
- frustum.topRightFar = cf + (up * hf / 2) + (right * wf / 2);
- frustum.bottomLeftFar = cf - (up * hf / 2) - (right * wf / 2);
- frustum.bottomRightFar = cf - (up * hf / 2) + (right * wf / 2);
+ frustum.topLeftFar = cf + (up * hhf) - (right * hwf);
+ frustum.topRightFar = cf + (up * hhf) + (right * hwf);
+ frustum.bottomLeftFar = cf - (up * hhf) - (right * hwf);
+ frustum.bottomRightFar = cf - (up * hhf) + (right * hwf);
- frustum.topLeftNear = cn + (up * hn / 2) - (right * wn / 2);
- frustum.topRightNear = cn + (up * hn / 2) + (right * wn / 2);
- frustum.bottomLeftNear = cn - (up * hn / 2) - (right * wn / 2);
- frustum.bottomRightNear = cn - (up * hn / 2) + (right * wn / 2);
+ frustum.topLeftNear = cn + (up * hhn) - (right * hwn);
+ frustum.topRightNear = cn + (up * hhn) + (right * hwn);
+ frustum.bottomLeftNear = cn - (up * hhn) - (right * hwn);
+ frustum.bottomRightNear = cn - (up * hhn) + (right * hwn);
return frustum;
}
@@ -579,8 +629,13 @@ QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::splitPolygonAtAxisVa
return QPair<PolygonVector, PolygonVector>(polygonBelow, polygonAbove);
}
+static void addXOffset(PolygonVector &footprint, double xoff)
+{
+ for (QDoubleVector3D &v: footprint)
+ v.setX(v.x() + xoff);
+}
-QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::clipFootprintToMap(const PolygonVector &footprint) const
+QGeoCameraTilesPrivate::ClippedFootprint QGeoCameraTilesPrivate::clipFootprintToMap(const PolygonVector &footprint) const
{
bool clipX0 = false;
bool clipX1 = false;
@@ -588,20 +643,13 @@ QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::clipFootprintToMap(c
bool clipY1 = false;
double side = 1.0 * m_sideLength;
+ double minX = std::numeric_limits<double>::max();
+ double maxX = std::numeric_limits<double>::lowest();
- typedef PolygonVector::const_iterator const_iter;
-
- const_iter i = footprint.constBegin();
- const_iter end = footprint.constEnd();
- for (; i != end; ++i) {
- QDoubleVector3D p = *i;
- if ((p.x() < 0.0) || (qFuzzyIsNull(p.x())))
- clipX0 = true;
- if ((side < p.x()) || (qFuzzyCompare(side, p.x())))
- clipX1 = true;
+ for (const QDoubleVector3D &p: footprint) {
if (p.y() < 0.0)
clipY0 = true;
- if (side < p.y())
+ if (p.y() > side)
clipY1 = true;
}
@@ -615,11 +663,39 @@ QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::clipFootprintToMap(c
results = splitPolygonAtAxisValue(results, 1, side).first;
}
+ for (const QDoubleVector3D &p: results) {
+ if ((p.x() < 0.0) || (qFuzzyIsNull(p.x())))
+ clipX0 = true;
+ if ((p.x() > side) || (qFuzzyCompare(side, p.x())))
+ clipX1 = true;
+ }
+
+ for (const QDoubleVector3D &v : results) {
+ minX = qMin(v.x(), minX);
+ maxX = qMax(v.x(), maxX);
+ }
+
+ double footprintWidth = maxX - minX;
+
if (clipX0) {
if (clipX1) {
- results = splitPolygonAtAxisValue(results, 0, 0.0).second;
- results = splitPolygonAtAxisValue(results, 0, side).first;
- return QPair<PolygonVector, PolygonVector>(results, PolygonVector());
+ if (footprintWidth > side) {
+ PolygonVector rightPart = splitPolygonAtAxisValue(results, 0, side).second;
+ addXOffset(rightPart, -side);
+ rightPart = splitPolygonAtAxisValue(rightPart, 0, side).first; // clip it again, should it tend to infinite or so
+
+ PolygonVector leftPart = splitPolygonAtAxisValue(results, 0, 0).first;
+ addXOffset(leftPart, side);
+ leftPart = splitPolygonAtAxisValue(leftPart, 0, 0).second; // same here
+
+ results = splitPolygonAtAxisValue(results, 0, 0.0).second;
+ results = splitPolygonAtAxisValue(results, 0, side).first;
+ return ClippedFootprint(leftPart, results, rightPart);
+ } else { // fitting the WebMercator square exactly?
+ results = splitPolygonAtAxisValue(results, 0, 0.0).second;
+ results = splitPolygonAtAxisValue(results, 0, side).first;
+ return ClippedFootprint(PolygonVector(), results, PolygonVector());
+ }
} else {
QPair<PolygonVector, PolygonVector> pair = splitPolygonAtAxisValue(results, 0, 0.0);
if (pair.first.isEmpty()) {
@@ -649,11 +725,11 @@ QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::clipFootprintToMap(c
pair.first.append(QDoubleVector3D(side, y - 0.001, 0.0));
}
} else {
- for (int i = 0; i < pair.first.size(); ++i) {
- pair.first[i].setX(pair.first.at(i).x() + side);
- }
+ addXOffset(pair.first, side);
+ if (footprintWidth > side)
+ pair.first = splitPolygonAtAxisValue(pair.first, 0, 0).second;
}
- return pair;
+ return ClippedFootprint(pair.first, pair.second, PolygonVector());
}
} else {
if (clipX1) {
@@ -685,13 +761,13 @@ QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::clipFootprintToMap(c
pair.second.append(QDoubleVector3D(0.0, y + 0.001, 0.0));
}
} else {
- for (int i = 0; i < pair.second.size(); ++i) {
- pair.second[i].setX(pair.second.at(i).x() - side);
- }
+ addXOffset(pair.second, -side);
+ if (footprintWidth > side)
+ pair.second = splitPolygonAtAxisValue(pair.second, 0, side).first;
}
- return pair;
+ return ClippedFootprint(PolygonVector(), pair.first, pair.second);
} else {
- return QPair<PolygonVector, PolygonVector>(results, PolygonVector());
+ return ClippedFootprint(PolygonVector(), results, PolygonVector());
}
}
diff --git a/src/location/maps/qgeotiledmapscene.cpp b/src/location/maps/qgeotiledmapscene.cpp
index 3e75cece..fbd05645 100644
--- a/src/location/maps/qgeotiledmapscene.cpp
+++ b/src/location/maps/qgeotiledmapscene.cpp
@@ -43,7 +43,16 @@
#include <QtCore/private/qobject_p.h>
#include <QtQuick/QSGImageNode>
#include <QtQuick/QQuickWindow>
+#include <QtGui/QVector3D>
#include <cmath>
+#include <QtPositioning/private/qlocationutils_p.h>
+#include <QtPositioning/private/qdoublematrix4x4_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
+
+static QVector3D toVector3D(const QDoubleVector3D& in)
+{
+ return QVector3D(in.x(), in.y(), in.z());
+}
QT_BEGIN_NAMESPACE
@@ -75,6 +84,7 @@ public:
// the number of tiles in each direction for the whole map (earth) at the current zoom level.
// it is 1<<zoomLevel
int m_sideLength;
+ double m_mapEdgeSize;
QHash<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > m_textures;
@@ -85,31 +95,16 @@ public:
int m_maxTileY;
int m_tileXWrapsBelow; // the wrap point as a tile index
- // cameraToScreen transform
- double m_screenWidth; // in pixels
- double m_screenHeight; // in pixels
bool m_linearScaling;
bool m_dropTextures;
void addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture);
- QDoubleVector2D itemPositionToMercator(const QDoubleVector2D &pos) const;
- QDoubleVector2D mercatorToItemPosition(const QDoubleVector2D &mercator) const;
-
- QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const;
- QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const;
-
- QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const;
- QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const;
-
- QDoubleVector2D wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const;
- QDoubleVector2D itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const;
-
- void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
+ void setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles);
void removeTiles(const QSet<QGeoTileSpec> &oldTiles);
bool buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode);
- void setTileBounds(const QSet<QGeoTileSpec> &tiles);
+ void updateTileBounds(const QSet<QGeoTileSpec> &tiles);
void setupCamera();
inline bool isTiltedOrRotated() { return (m_cameraData.tilt() > 0.0) || (m_cameraData.bearing() > 0.0); }
};
@@ -143,6 +138,7 @@ void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData)
float delta = cameraData.zoomLevel() - d->m_intZoomLevel;
d->m_linearScaling = qAbs(delta) > 0.05 || d->isTiltedOrRotated();
d->m_sideLength = 1 << d->m_intZoomLevel;
+ d->m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * d->m_tileSize;
}
void QGeoTiledMapScene::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
@@ -191,8 +187,6 @@ QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate()
m_maxTileX(-1),
m_maxTileY(-1),
m_tileXWrapsBelow(0),
- m_screenWidth(0.0),
- m_screenHeight(0.0),
m_linearScaling(false),
m_dropTextures(false)
{
@@ -245,19 +239,19 @@ void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<
m_textures.insert(spec, texture);
}
-void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
+void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles)
{
// work out the tile bounds for the new scene
- setTileBounds(tiles);
+ updateTileBounds(visibleTiles);
// set up the gl camera for the new scene
setupCamera();
- QSet<QGeoTileSpec> toRemove = m_visibleTiles - tiles;
+ QSet<QGeoTileSpec> toRemove = m_visibleTiles - visibleTiles;
if (!toRemove.isEmpty())
removeTiles(toRemove);
- m_visibleTiles = tiles;
+ m_visibleTiles = visibleTiles;
}
void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
@@ -272,7 +266,7 @@ void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
}
}
-void QGeoTiledMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
+void QGeoTiledMapScenePrivate::updateTileBounds(const QSet<QGeoTileSpec> &tiles)
{
if (tiles.isEmpty()) {
m_minTileX = -1;
@@ -354,26 +348,24 @@ void QGeoTiledMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
void QGeoTiledMapScenePrivate::setupCamera()
{
+ // NOTE: The following instruction is correct only because WebMercator is a square projection!
double f = 1.0 * qMin(m_screenSize.width(), m_screenSize.height());
- // fraction of zoom level
+ // Using fraction of zoom level, z varies between [ m_tileSize , 2 * m_tileSize [
double z = std::pow(2.0, m_cameraData.zoomLevel() - m_intZoomLevel) * m_tileSize;
- // Maps smaller than screen size are NOT supported. But we can't simply return here, as this call
- // might be invoked also before a valid zoom level is set.
-
// calculate altitude that allows the visible map tiles
- // to fit in the screen correctly (note that a larger f would cause
- // the camera be higher, resulting in empty areas displayed around
- // the tiles or repeated tiles)
- double altitude = f / (2.0 * z) ;
+ // to fit in the screen correctly (note that a larger f will cause
+ // the camera be higher, resulting in gray areas displayed around
+ // the tiles)
+ double altitude = f / (2.0 * z);
// calculate center
double edge = m_scaleFactor * m_tileSize;
// first calculate the camera center in map space in the range of 0 <-> sideLength (2^z)
QDoubleVector2D camCenterMercator = QWebMercator::coordToMercator(m_cameraData.center());
- QDoubleVector3D center = m_sideLength * camCenterMercator;
+ QDoubleVector3D center = (m_sideLength * camCenterMercator);
// wrap the center if necessary (due to dateline crossing)
if (center.x() < m_tileXWrapsBelow)
@@ -383,16 +375,15 @@ void QGeoTiledMapScenePrivate::setupCamera()
center.setX(center.x() - 1.0 * m_minTileX);
center.setY(1.0 * m_minTileY - center.y());
- m_screenHeight = m_screenSize.height();
- m_screenWidth = m_screenSize.width();
-
// apply necessary scaling to the camera center
center *= edge;
// calculate eye
-
+ double apertureSize = 1.0;
+ if (m_cameraData.fieldOfView() != 90.0) //aperture(90 / 2) = 1
+ apertureSize = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
QDoubleVector3D eye = center;
- eye.setZ(altitude * edge);
+ eye.setZ(altitude * edge / apertureSize);
// calculate up
@@ -400,19 +391,24 @@ void QGeoTiledMapScenePrivate::setupCamera()
QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
QDoubleVector3D up = QDoubleVector3D::normal(side, view);
- // old bearing, tilt and roll code
- // QMatrix4x4 mBearing;
- // mBearing.rotate(-1.0 * camera.bearing(), view);
- // up = mBearing * up;
-
- // QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
- // QMatrix4x4 mTilt;
- // mTilt.rotate(camera.tilt(), side2);
- // eye = (mTilt * view) + center;
+ // old bearing, tilt and roll code.
+ // Now using double matrices until distilling the transformation to QMatrix4x4
+ QDoubleMatrix4x4 mBearing;
+ // -1.0 * bearing removed, now map north goes in the bearing direction
+ mBearing.rotate(-1.0 * m_cameraData.bearing(), view);
+ up = mBearing * up;
+
+ QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
+ if (m_cameraData.tilt() > 0.01) {
+ QDoubleMatrix4x4 mTilt;
+ mTilt.rotate(m_cameraData.tilt(), side2);
+ eye = mTilt * view + center;
+ }
- // view = eye - center;
- // side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
- // up = QDoubleVector3D::normal(view, side2);
+ view = eye - center;
+ view.normalize();
+ side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
+ up = QDoubleVector3D::normal(view, side2);
// QMatrix4x4 mRoll;
// mRoll.rotate(camera.roll(), view);
@@ -421,15 +417,19 @@ void QGeoTiledMapScenePrivate::setupCamera()
// near plane and far plane
double nearPlane = 1.0;
- double farPlane = (altitude + 1.0) * edge;
+ // Clip plane. Used to be (altitude + 1.0) * edge. This does not affect the perspective. minimum value would be > 0.0
+ // Since, for some reasons possibly related to how QSG works, this clipping plane is unable to clip part of tiles,
+ // Instead of farPlane = (altitude + m_cameraData.clipDistance()) * edge , we use a fixed large clipDistance, and
+ // leave the clipping only in QGeoCameraTiles::createFrustum
+ double farPlane = (altitude + 10000.0) * edge;
m_cameraUp = up;
m_cameraCenter = center;
m_cameraEye = eye;
double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height();
- float halfWidth = 1;
- float halfHeight = 1;
+ float halfWidth = 1 * apertureSize;
+ float halfHeight = 1 * apertureSize;
if (aspectRatio > 1.0) {
halfWidth *= aspectRatio;
} else if (aspectRatio > 0.0f && aspectRatio < 1.0f) {
@@ -503,14 +503,33 @@ public:
QHash<QGeoTileSpec, QSGTexture *> textures;
};
-static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix) {
+static bool qgeotiledmapscene_isTileInViewport_Straight(const QRectF &tileRect, const QMatrix4x4 &matrix)
+{
const QRectF boundingRect = QRectF(matrix * tileRect.topLeft(), matrix * tileRect.bottomRight());
return QRectF(-1, -1, 2, 2).intersects(boundingRect);
}
-static QVector3D toVector3D(const QDoubleVector3D& in)
+static bool qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF &tileRect, const QMatrix4x4 &matrix)
{
- return QVector3D(in.x(), in.y(), in.z());
+ // Transformed corners
+ const QPointF tlt = matrix * tileRect.topLeft();
+ const QPointF trt = matrix * tileRect.topRight();
+ const QPointF blt = matrix * tileRect.bottomLeft();
+ const QPointF brt = matrix * tileRect.bottomRight();
+
+ const QRectF boundingRect = QRectF(QPointF(qMin(qMin(qMin(tlt.x(), trt.x()), blt.x()), brt.x())
+ ,qMax(qMax(qMax(tlt.y(), trt.y()), blt.y()), brt.y()))
+ ,QPointF(qMax(qMax(qMax(tlt.x(), trt.x()), blt.x()), brt.x())
+ ,qMin(qMin(qMin(tlt.y(), trt.y()), blt.y()), brt.y()))
+ );
+ return QRectF(-1, -1, 2, 2).intersects(boundingRect);
+}
+
+static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix, const bool straight)
+{
+ if (straight)
+ return qgeotiledmapscene_isTileInViewport_Straight(tileRect, matrix);
+ return qgeotiledmapscene_isTileInViewport_rotationTilt(tileRect, matrix);
}
void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
@@ -533,11 +552,13 @@ void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
foreach (const QGeoTileSpec &s, toRemove)
delete root->tiles.take(s);
+ bool straight = !d->isTiltedOrRotated();
for (QHash<QGeoTileSpec, QSGImageNode *>::iterator it = root->tiles.begin();
it != root->tiles.end(); ) {
QSGImageNode *node = it.value();
- bool ok = d->buildGeometry(it.key(), node) && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix());
+ bool ok = d->buildGeometry(it.key(), node)
+ && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix(), straight);
QSGNode::DirtyState dirtyBits = 0;
@@ -567,7 +588,8 @@ void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
QSGImageNode *tileNode = window->createImageNode();
// note: setTexture will update coordinates so do it here, before we buildGeometry
tileNode->setTexture(textures.value(s));
- if (d->buildGeometry(s, tileNode) && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix())) {
+ if (d->buildGeometry(s, tileNode)
+ && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix(), straight)) {
if (tileNode->texture()->textureSize().width() > d->m_tileSize) {
tileNode->setFiltering(QSGTexture::Linear); // with mipmapping QSGTexture::Nearest generates artifacts
tileNode->setMipmapFiltering(QSGTexture::Linear);
@@ -596,7 +618,7 @@ QSGNode *QGeoTiledMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *win
mapRoot = new QGeoTiledMapRootNode();
// Setting clip rect to fullscreen, as now the map can never be smaller than the viewport.
- mapRoot->setClipRect(QRect(0, 0, d->m_screenWidth, d->m_screenHeight));
+ mapRoot->setClipRect(QRect(0, 0, w, h));
QMatrix4x4 itemSpaceMatrix;
itemSpaceMatrix.scale(w / 2, h / 2);
diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
index abcb3779..a0f00615 100644
--- a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
+++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
@@ -95,7 +95,12 @@ GeoTiledMappingManagerEngineEsri::GeoTiledMappingManagerEngineEsri(const QVarian
cameraCaps.setMinimumZoomLevel(minimumZoomLevel);
cameraCaps.setMaximumZoomLevel(maximumZoomLevel);
-
+ cameraCaps.setSupportsBearing(true);
+ cameraCaps.setSupportsTilting(true);
+ cameraCaps.setMinimumTilt(0);
+ cameraCaps.setMaximumTilt(80);
+ cameraCaps.setMinimumFieldOfView(20.0);
+ cameraCaps.setMaximumFieldOfView(120.0);
setCameraCapabilities(cameraCaps);
setTileSize(QSize(256, 256));
diff --git a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp
index 5404eb30..4b62aece 100644
--- a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp
+++ b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp
@@ -50,6 +50,12 @@ QGeoTiledMappingManagerEngineMapbox::QGeoTiledMappingManagerEngineMapbox(const Q
QGeoCameraCapabilities cameraCaps;
cameraCaps.setMinimumZoomLevel(0.0);
cameraCaps.setMaximumZoomLevel(19.0);
+ cameraCaps.setSupportsBearing(true);
+ cameraCaps.setSupportsTilting(true);
+ cameraCaps.setMinimumTilt(0);
+ cameraCaps.setMaximumTilt(80);
+ cameraCaps.setMinimumFieldOfView(20.0);
+ cameraCaps.setMaximumFieldOfView(120.0);
setCameraCapabilities(cameraCaps);
setTileSize(QSize(256, 256));
diff --git a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp
index 3a9ac277..e5809018 100644
--- a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp
+++ b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp
@@ -67,7 +67,12 @@ QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia(
capabilities.setMinimumZoomLevel(0.0);
capabilities.setMaximumZoomLevel(20.0);
-
+ capabilities.setSupportsBearing(true);
+ capabilities.setSupportsTilting(true);
+ capabilities.setMinimumTilt(0);
+ capabilities.setMaximumTilt(80);
+ capabilities.setMinimumFieldOfView(20.0);
+ capabilities.setMaximumFieldOfView(120.0);
setCameraCapabilities(capabilities);
setTileSize(QSize(256, 256));
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
index 3228b31b..e9e48008 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
@@ -57,6 +57,12 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
QGeoCameraCapabilities cameraCaps;
cameraCaps.setMinimumZoomLevel(0.0);
cameraCaps.setMaximumZoomLevel(19.0);
+ cameraCaps.setSupportsBearing(true);
+ cameraCaps.setSupportsTilting(true);
+ cameraCaps.setMinimumTilt(0);
+ cameraCaps.setMaximumTilt(80);
+ cameraCaps.setMinimumFieldOfView(20.0);
+ cameraCaps.setMaximumFieldOfView(120.0);
setCameraCapabilities(cameraCaps);
setTileSize(QSize(256, 256));
diff --git a/tests/auto/declarative_ui/tst_map.qml b/tests/auto/declarative_ui/tst_map.qml
index 1c0f4719..ab1ddd8c 100644
--- a/tests/auto/declarative_ui/tst_map.qml
+++ b/tests/auto/declarative_ui/tst_map.qml
@@ -361,8 +361,8 @@ Item {
compare(map.tilt, 0.0)
compare(mapTiltBearing.bearing, 45.0)
compare(mapTiltBearing.tilt, 25.0)
- compare(mapTiltBearingHere.bearing, 0.0)
- compare(mapTiltBearingHere.tilt, 0.0)
+ compare(mapTiltBearingHere.bearing, 45.0)
+ compare(mapTiltBearingHere.tilt, 25.0)
mapTiltBearing.bearing = 0.0
mapTiltBearing.tilt = 0.0
@@ -379,6 +379,10 @@ Item {
mapTiltBearingHere.bearing = 45.0
mapTiltBearingHere.tilt = 25.0
+ compare(mapTiltBearingHere.bearing, 45.0)
+ compare(mapTiltBearingHere.tilt, 25.0)
+ mapTiltBearingHere.bearing = 0.0
+ mapTiltBearingHere.tilt = 0.0
compare(mapTiltBearingHere.bearing, 0.0)
compare(mapTiltBearingHere.tilt, 0.0)