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.cpp114
1 files changed, 98 insertions, 16 deletions
diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp
index 7fefba27..218d806b 100644
--- a/src/location/maps/qgeoprojection.cpp
+++ b/src/location/maps/qgeoprojection.cpp
@@ -212,14 +212,25 @@ QDoubleVector2D QGeoProjectionWebMercator::wrappedMapProjectionToItemPosition(co
QDoubleVector2D QGeoProjectionWebMercator::itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
{
QDoubleVector2D pos = itemPosition;
- // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera
- if (pos.y() < m_minimumUnprojectableY)
- pos.setY(m_minimumUnprojectableY);
pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight);
pos *= 2.0;
pos -= QDoubleVector2D(1.0,1.0);
- return viewportToWrappedMapProjection(pos);
+ double s;
+ QDoubleVector2D res = viewportToWrappedMapProjection(pos, s);
+
+ // a positive s means a point behind the camera. So do it again, after clamping Y. See QTBUG-61813
+ if (s > 0.0) {
+ pos = itemPosition;
+ // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera
+ pos.setY(m_minimumUnprojectableY);
+ pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight);
+ pos *= 2.0;
+ pos -= QDoubleVector2D(1.0,1.0);
+ res = viewportToWrappedMapProjection(pos, s);
+ }
+
+ return res;
}
/* Default implementations */
@@ -317,10 +328,23 @@ QList<QDoubleVector2D> QGeoProjectionWebMercator::visibleRegion() const
return m_visibleRegion;
}
+QList<QDoubleVector2D> QGeoProjectionWebMercator::projectableRegion() const
+{
+ if (m_visibleRegionDirty)
+ const_cast<QGeoProjectionWebMercator *>(this)->updateVisibleRegion();
+ return m_projectableRegion;
+}
+
+QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
+{
+ double s;
+ return viewportToWrappedMapProjection(itemPosition, s);
+}
+
/*
actual implementation of itemPositionToWrappedMapProjection
*/
-QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
+QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition, double &s) const
{
QDoubleVector2D pos = itemPosition;
pos *= QDoubleVector2D(m_halfWidth, m_halfHeight);
@@ -332,7 +356,7 @@ QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const
QDoubleVector3D ray = p - m_eye;
ray.normalize();
- return (xyPlane.lineIntersection(m_eye, ray) / m_sideLength).toVector2D();
+ return (xyPlane.lineIntersection(m_eye, ray, s) / m_sideLength).toVector2D();
}
void QGeoProjectionWebMercator::setupCamera()
@@ -344,6 +368,8 @@ void QGeoProjectionWebMercator::setupCamera()
int intZoomLevel = static_cast<int>(std::floor(m_cameraData.zoomLevel()));
m_sideLength = (1 << intZoomLevel) * defaultTileSize;
m_center = m_centerMercator * m_sideLength;
+ //aperture(90 / 2) = 1
+ m_aperture = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
double f = m_viewportHeight;
double z = std::pow(2.0, m_cameraData.zoomLevel() - intZoomLevel) * defaultTileSize;
@@ -352,15 +378,13 @@ void QGeoProjectionWebMercator::setupCamera()
double z_mercator = std::pow(2.0, m_cameraData.zoomLevel()) * defaultTileSize;
double altitude_mercator = f / (2.0 * z_mercator);
- //aperture(90 / 2) = 1
- m_aperture = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
// calculate eye
m_eye = m_center;
m_eye.setZ(altitude * defaultTileSize / m_aperture);
// And in mercator space
m_eyeMercator = m_centerMercator;
- m_eyeMercator.setZ(altitude_mercator);
+ m_eyeMercator.setZ(altitude_mercator / m_aperture);
m_view = m_eye - m_center;
QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0));
@@ -396,7 +420,7 @@ void QGeoProjectionWebMercator::setupCamera()
m_eyeMercator = mTiltMercator * m_viewMercator + m_centerMercator;
}
- m_view = m_eye - m_center;
+ m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow
m_viewNormalized = m_view.normalized();
m_up = QDoubleVector3D::normal(m_view, m_side);
@@ -449,7 +473,7 @@ void QGeoProjectionWebMercator::setupCamera()
m_transformation.scale(m_sideLength, m_sideLength, 1.0);
m_centerNearPlane = m_eye + m_viewNormalized;
- m_centerNearPlaneMercator = m_eyeMercator + m_viewNormalized * m_nearPlaneMercator;
+ m_centerNearPlaneMercator = m_eyeMercator - m_viewNormalized * m_nearPlaneMercator;
// The method does not support tilting angles >= 90.0 or < 0.
@@ -478,11 +502,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);
@@ -499,6 +529,52 @@ void QGeoProjectionWebMercator::updateVisibleRegion()
m_visibleRegion.clear();
if (res.size())
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 {
+ QGeoProjectionWebMercator::Plane nearPlane(m_centerNearPlaneMercator, m_viewNormalized);
+ Line2D nearPlaneXYIntersection = nearPlane.planeXYIntersection();
+ double squareHalfSide = qMax(5.0, nearPlaneXYIntersection.m_point.length());
+ QDoubleVector2D viewDirectionProjected = -m_viewNormalized.toVector2D().normalized();
+
+
+ QDoubleVector2D tl = nearPlaneXYIntersection.m_point
+ - squareHalfSide * nearPlaneXYIntersection.m_direction
+ + 2 * squareHalfSide * viewDirectionProjected;
+ QDoubleVector2D tr = nearPlaneXYIntersection.m_point
+ + squareHalfSide * nearPlaneXYIntersection.m_direction
+ + 2 * squareHalfSide * viewDirectionProjected;
+ QDoubleVector2D bl = nearPlaneXYIntersection.m_point
+ - squareHalfSide * nearPlaneXYIntersection.m_direction;
+ QDoubleVector2D br = nearPlaneXYIntersection.m_point
+ + squareHalfSide * nearPlaneXYIntersection.m_direction;
+
+ QList<QDoubleVector2D> projectableRect;
+ projectableRect.push_back(bl);
+ projectableRect.push_back(br);
+ projectableRect.push_back(tr);
+ projectableRect.push_back(tl);
+
+
+ c2t::clip2tri clipperProjectable;
+ clipperProjectable.clearClipper();
+ clipperProjectable.addSubjectPath(QClipperUtils::qListToPath(mapRect), true);
+ clipperProjectable.addClipPolygon(QClipperUtils::qListToPath(projectableRect));
+
+ Paths resProjectable = clipperProjectable.execute(c2t::clip2tri::Intersection);
+ if (resProjectable.size())
+ m_projectableRegion = QClipperUtils::pathToQList(resProjectable[0]); // Intersection between two convex quadrilaterals should always be a single polygon
+ else
+ m_projectableRegion = viewportRect;
+ }
}
QGeoCameraData QGeoProjectionWebMercator::cameraData() const
@@ -544,9 +620,15 @@ QGeoProjectionWebMercator::Plane::Plane(const QDoubleVector3D &planePoint, const
QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const
{
+ double s;
+ return lineIntersection(linePoint, lineDirection, s);
+}
+
+QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection, double &s) const
+{
QDoubleVector3D w = linePoint - m_point;
// s = -n.dot(w) / n.dot(u). p = p0 + su; u is lineDirection
- double s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection);
+ s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection);
return linePoint + lineDirection * s;
}