diff options
Diffstat (limited to 'src/location/maps/qgeoprojection.cpp')
-rw-r--r-- | src/location/maps/qgeoprojection.cpp | 141 |
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 |