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