summaryrefslogtreecommitdiff
path: root/src/location/maps/qgeoprojection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/maps/qgeoprojection.cpp')
-rw-r--r--src/location/maps/qgeoprojection.cpp141
1 files changed, 111 insertions, 30 deletions
diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp
index ed85fc67..dc7e78f8 100644
--- a/src/location/maps/qgeoprojection.cpp
+++ b/src/location/maps/qgeoprojection.cpp
@@ -36,11 +36,14 @@
#include "qgeoprojection_p.h"
#include <QtPositioning/private/qwebmercator_p.h>
+#include <QtPositioning/private/qlocationutils_p.h>
#include <QSize>
#include <cmath>
QT_BEGIN_NAMESPACE
+static const double defaultTileSize = 256.0;
+
QGeoProjection::QGeoProjection()
{
@@ -62,14 +65,17 @@ QGeoProjectionWebMercator::QGeoProjectionWebMercator()
m_minimumZoom(0),
m_cameraCenterXMercator(0),
m_cameraCenterYMercator(0),
- m_cameraWidthMercator(1),
- m_cameraHeightMercator(1),
- m_1_cameraWidthMercator(0),
- m_1_cameraHeightMercator(0),
m_viewportWidth(1),
m_viewportHeight(1),
m_1_viewportWidth(0),
- m_1_viewportHeight(0)
+ m_1_viewportHeight(0),
+ m_sideLength(256),
+ m_aperture(0.0),
+ m_nearPlane(0.0),
+ m_farPlane(0.0),
+ m_halfWidth(0.0),
+ m_halfHeight(0.0),
+ m_plane(QDoubleVector3D(0,0,0), QDoubleVector3D(0,0,1))
{
}
@@ -94,7 +100,7 @@ double QGeoProjectionWebMercator::minimumZoom() const
// and the borders stays the same, but the meters per pixel change
double QGeoProjectionWebMercator::maximumCenterLatitudeAtZoom(double zoomLevel) const
{
- double mapEdgeSize = std::pow(2.0, zoomLevel) * 256.0;
+ double mapEdgeSize = std::pow(2.0, zoomLevel) * defaultTileSize;
// At init time weird things happen
int clampedWindowHeight = (m_viewportHeight > mapEdgeSize) ? mapEdgeSize : m_viewportHeight;
@@ -122,14 +128,14 @@ void QGeoProjectionWebMercator::setViewportSize(const QSize &size)
m_viewportHeight = size.height();
m_1_viewportWidth = 1.0 / m_viewportWidth;
m_1_viewportHeight = 1.0 / m_viewportHeight;
- m_minimumZoom = std::log(qMax(m_viewportWidth, m_viewportHeight) / 256.0) / std::log(2.0);
+ m_minimumZoom = std::log(qMax(m_viewportWidth, m_viewportHeight) / defaultTileSize) / std::log(2.0);
setupCamera();
}
void QGeoProjectionWebMercator::setCameraData(const QGeoCameraData &cameraData)
{
m_cameraData = cameraData;
- m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * 256.0;
+ m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize;
setupCamera();
}
@@ -170,28 +176,30 @@ QDoubleVector2D QGeoProjectionWebMercator::unwrapMapProjection(const QDoubleVect
QDoubleVector2D QGeoProjectionWebMercator::wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const
{
- // TODO: Support tilt/bearing through a projection matrix.
- double x = ((wrappedProjection.x() - m_cameraCenterXMercator) * m_1_cameraWidthMercator + 0.5) * m_viewportWidth;
- double y = ((wrappedProjection.y() - m_cameraCenterYMercator) * m_1_cameraHeightMercator + 0.5) * m_viewportHeight;
- return QDoubleVector2D(x, y);
+ QDoubleVector3D pos = wrappedProjection * m_sideLength;
+ QDoubleVector2D res = (m_transformation * pos).toVector2D();
+ res += QDoubleVector2D(1.0,1.0);
+ res *= 0.5;
+ res *= QDoubleVector2D(m_viewportWidth, m_viewportHeight);
+ return res;
}
QDoubleVector2D QGeoProjectionWebMercator::itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
{
- // TODO: Support tilt/bearing through an inverse projection matrix.
- double x = itemPosition.x();
- x *= m_1_viewportWidth;
- x -= 0.5;
- x *= m_cameraWidthMercator;
- x += m_cameraCenterXMercator;
+ QDoubleVector2D pos = itemPosition;
+ pos /= QDoubleVector2D(m_viewportWidth, m_viewportHeight);
+ pos *= 2.0;
+ pos -= QDoubleVector2D(1.0,1.0);
+ pos *= QDoubleVector2D(m_halfWidth, m_halfHeight);
+
+ QDoubleVector3D p = m_centerNearPlane;
+ p -= m_up * pos.y();
+ p -= m_side * pos.x();
- double y = itemPosition.y();
- y *= m_1_viewportHeight;
- y -= 0.5;
- y *= m_cameraHeightMercator;
- y += m_cameraCenterYMercator;
+ QDoubleVector3D ray = p - m_eye;
+ ray.normalize();
- return QDoubleVector2D(x, y);
+ return (m_plane.lineIntersection(m_eye, ray) / m_sideLength).toVector2D();
}
/* Default implementations */
@@ -207,7 +215,8 @@ QGeoCoordinate QGeoProjectionWebMercator::itemPositionToCoordinate(const QDouble
QDoubleVector2D wrappedMapProjection = itemPositionToWrappedMapProjection(pos);
// With rotation/tilting, a screen position might end up outside the projection space.
- // TODO: test for it
+ if (!isProjectable(wrappedMapProjection))
+ return QGeoCoordinate();
return mapProjectionToGeo(unwrapMapProjection(wrappedMapProjection));
}
@@ -226,15 +235,87 @@ QDoubleVector2D QGeoProjectionWebMercator::coordinateToItemPosition(const QGeoCo
return pos;
}
+bool QGeoProjectionWebMercator::isProjectable(const QDoubleVector2D &wrappedProjection) const
+{
+ QDoubleVector3D pos = wrappedProjection * m_sideLength;
+
+ // TODO: add an offset to the eye
+ QDoubleVector3D p = m_eye - pos;
+ double dot = QDoubleVector3D::dotProduct(p , m_viewNormalized);
+
+ if (dot < 0.0) // behind the eye
+ return false;
+ return true;
+}
+
void QGeoProjectionWebMercator::setupCamera()
{
- m_cameraWidthMercator = m_viewportWidth / m_mapEdgeSize;
- m_cameraHeightMercator = m_viewportHeight / m_mapEdgeSize;
- m_1_cameraWidthMercator = 1.0 / m_cameraWidthMercator;
- m_1_cameraHeightMercator = 1.0 / m_cameraHeightMercator;
- QDoubleVector2D camCenterMercator = QWebMercator::coordToMercator(m_cameraData.center());
+ QDoubleVector2D camCenterMercator = geoToMapProjection(m_cameraData.center());
m_cameraCenterXMercator = camCenterMercator.x();
m_cameraCenterYMercator = camCenterMercator.y();
+
+ int intZoomLevel = static_cast<int>(std::floor(m_cameraData.zoomLevel()));
+ m_sideLength = (1 << intZoomLevel) * defaultTileSize;
+ m_center = camCenterMercator * m_sideLength;
+
+ double f = 1.0 * qMin(m_viewportWidth, m_viewportHeight);
+ double z = std::pow(2.0, m_cameraData.zoomLevel() - intZoomLevel) * defaultTileSize;
+ double altitude = f / (2.0 * z) ;
+
+ // calculate eye
+ // TODO: support field of view with apertureSize = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
+ double m_aperture = 1.0; //aperture(90 / 2) = 1
+
+ m_eye = m_center;
+ m_eye.setZ(altitude * defaultTileSize / m_aperture);
+
+ m_view = m_eye - m_center;
+ QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0));
+ m_up = QDoubleVector3D::normal(side, m_view);
+
+ if (m_cameraData.bearing() > 0.0) {
+ // old bearing, tilt and roll code
+ QDoubleMatrix4x4 mBearing;
+ mBearing.rotate(m_cameraData.bearing(), m_view);
+ m_up = mBearing * m_up;
+ }
+
+ m_side = QDoubleVector3D::normal(m_up, m_view);
+
+ if (m_cameraData.tilt() > 0.0) {
+ QDoubleMatrix4x4 mTilt;
+ mTilt.rotate(-m_cameraData.tilt(), m_side);
+ m_eye = mTilt * m_view + m_center;
+ }
+
+ m_view = m_eye - m_center;
+ m_viewNormalized = m_view.normalized();
+ m_up = QDoubleVector3D::normal(m_view, m_side);
+
+ m_nearPlane = 1;
+ // 10000.0 to make sure that everything fits in the frustum even at high zoom levels
+ // (that is, with a very large map)
+ // TODO: extend this to support clip distance
+ m_farPlane = (altitude + 10000.0) * defaultTileSize;
+
+ double aspectRatio = 1.0 * m_viewportWidth / m_viewportHeight;
+
+ m_halfWidth = 1 * m_aperture;
+ m_halfHeight = 1 * m_aperture;
+ if (aspectRatio > 1.0) {
+ m_halfWidth *= aspectRatio;
+ } else if (aspectRatio > 0.0 && aspectRatio < 1.0) {
+ m_halfHeight /= aspectRatio;
+ }
+
+ QDoubleMatrix4x4 cameraMatrix;
+ cameraMatrix.lookAt(m_eye, m_center, m_up);
+
+ QDoubleMatrix4x4 projectionMatrix;
+ projectionMatrix.frustum(-m_halfWidth, m_halfWidth, -m_halfHeight, m_halfHeight, m_nearPlane, m_farPlane);
+
+ m_transformation = projectionMatrix * cameraMatrix;
+ m_centerNearPlane = m_eye + m_view.normalized();
}
QT_END_NAMESPACE