Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgeoprojection.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qgeoprojection_p.h"
5
6#include <QSize>
7#include <QtGui/QMatrix4x4>
8#include <QtPositioning/QGeoPolygon>
9#include <QtPositioning/QGeoRectangle>
10
11#include <QtPositioning/private/qwebmercator_p.h>
12#include <QtPositioning/private/qlocationutils_p.h>
13#include <QtPositioning/private/qclipperutils_p.h>
14
15#include <cmath>
16
17namespace {
18 static const double defaultTileSize = 256.0;
19 static const QDoubleVector3D xyNormal(0.0, 0.0, 1.0);
20 static const QGeoProjectionWebMercator::Plane xyPlane(QDoubleVector3D(0,0,0), QDoubleVector3D(0,0,1));
21 static const QList<QDoubleVector2D> mercatorGeometry = {
22 QDoubleVector2D(-1.0,0.0),
23 QDoubleVector2D( 2.0,0.0),
24 QDoubleVector2D( 2.0,1.0),
25 QDoubleVector2D(-1.0,1.0) };
26}
27
29{
30 return QMatrix4x4(m(0,0), m(0,1), m(0,2), m(0,3),
31 m(1,0), m(1,1), m(1,2), m(1,3),
32 m(2,0), m(2,1), m(2,2), m(2,3),
33 m(3,0), m(3,1), m(3,2), m(3,3));
34}
35
36static QPointF centerOffset(const QSizeF &screenSize, const QRectF &visibleArea)
37{
38 QRectF va = visibleArea;
39 if (va.isNull())
40 va = QRectF(0, 0, screenSize.width(), screenSize.height());
41
42 QRectF screen = QRectF(QPointF(0,0),screenSize);
43 QPointF vaCenter = va.center();
44
45 QPointF screenCenter = screen.center();
46 QPointF diff = screenCenter - vaCenter;
47
48 return diff;
49}
50
51static QPointF marginsOffset(const QSizeF &screenSize, const QRectF &visibleArea)
52{
53 QPointF diff = centerOffset(screenSize, visibleArea);
54 qreal xdiffpct = diff.x() / qMax<double>(screenSize.width() - 1, 1);
55 qreal ydiffpct = diff.y() / qMax<double>(screenSize.height() - 1, 1);
56
57 return QPointF(-xdiffpct, -ydiffpct);
58}
59
61
66
71
73{
74 Q_UNUSED(coordinate);
75 Q_UNUSED(anchorPoint);
76 return QGeoCoordinate();
77}
78
83
84bool QGeoProjection::setBearing(qreal bearing, const QGeoCoordinate &coordinate)
85{
86 Q_UNUSED(bearing);
87 Q_UNUSED(coordinate);
88 return false;
89}
90
98
103
104
105/*
106 * QGeoProjectionWebMercator implementation
107*/
108
110{
111 // Approach: find the displacement in (wrapped) mercator space, and apply that to the center
113 QDoubleVector2D coordProj = geoToWrappedMapProjection(coordinate);
114
116 // Y-clamping done in mercatorToCoord
117 return wrappedMapProjectionToGeo(centerProj + coordProj - anchorProj);
118}
119
121{
122 const QDoubleVector2D coordWrapped = geoToWrappedMapProjection(coordinate);
123 if (!isProjectable(coordWrapped))
124 return false;
125 const QPointF rotationPoint = wrappedMapProjectionToItemPosition(coordWrapped).toPointF();
126
128 // first set bearing
129 camera.setBearing(bearing);
131 camera = cameraData();
132
133 // then reanchor
134 const QGeoCoordinate center = anchorCoordinateToPoint(coordinate, rotationPoint);
135 camera.setCenter(center);
137 return true;
138}
139
141 : QGeoProjection(),
142 m_mapEdgeSize(256), // at zl 0
143 m_minimumZoom(0),
144 m_cameraCenterXMercator(0),
145 m_cameraCenterYMercator(0),
146 m_viewportWidth(1),
147 m_viewportHeight(1),
148 m_1_viewportWidth(0),
149 m_1_viewportHeight(0),
150 m_sideLengthPixels(256),
151 m_aperture(0.0),
152 m_nearPlane(0.0),
153 m_farPlane(0.0),
154 m_halfWidth(0.0),
155 m_halfHeight(0.0),
156 m_minimumUnprojectableY(0.0),
157 m_verticalEstateToSkip(0.0),
158 m_visibleRegionDirty(false)
159{
160}
161
166
167// This method returns the minimum zoom level that this specific qgeomap type allows
168// at the current viewport size and for the default tile size of 256^2.
170{
171 return m_minimumZoom;
172}
173
178
183
185{
187 m_qsgTransformDirty = false;
189// qDebug() << "QGeoProjectionWebMercator::qsgTransform" << m_itemToWindowTransform << toMatrix4x4(m_transformation0);
190 }
191 return m_qsgTransform;
192}
193
198
199// This method recalculates the "no-trespassing" limits for the map center.
200// This has to be used when:
201// 1) the map is resized, because the meters per pixel remain the same, but
202// the amount of pixels between the center and the borders changes
203// 2) when the zoom level changes, because the amount of pixels between the center
204// and the borders stays the same, but the meters per pixel change
206{
207 double mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize;
208
209 // At init time weird things happen
210 int clampedWindowHeight = (m_viewportHeight > mapEdgeSize) ? mapEdgeSize : m_viewportHeight;
212 double hpct = offsetPct.y() / qMax<double>(m_viewportHeight - 1, 1);
213
214 // Use the window height divided by 2 as the topmost allowed center, with respect to the map size in pixels
215 double mercatorTopmost = (clampedWindowHeight * (0.5 - hpct)) / mapEdgeSize ;
216 QGeoCoordinate topMost = QWebMercator::mercatorToCoord(QDoubleVector2D(0.0, mercatorTopmost));
217 return topMost.latitude();
218}
219
221{
222 double mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize;
223
224 // At init time weird things happen
225 int clampedWindowHeight = (m_viewportHeight > mapEdgeSize) ? mapEdgeSize : m_viewportHeight;
227 double hpct = offsetPct.y() / qMax<double>(m_viewportHeight - 1, 1);
228
229 // Use the window height divided by 2 as the topmost allowed center, with respect to the map size in pixels
230 double mercatorTopmost = (clampedWindowHeight * (0.5 + hpct)) / mapEdgeSize ;
231 QGeoCoordinate topMost = QWebMercator::mercatorToCoord(QDoubleVector2D(0.0, mercatorTopmost));
232 return -topMost.latitude();
233}
234
236{
237 m_visibleArea = visibleArea;
238 setupCamera();
239}
240
242{
243 return m_mapEdgeSize;
244}
245
247{
248 return m_mapEdgeSize;
249}
250
252{
253 if (int(m_viewportWidth) == size.width() && int(m_viewportHeight) == size.height())
254 return;
255
256 m_viewportWidth = size.width();
257 m_viewportHeight = size.height();
260 m_minimumZoom = std::log(qMax(m_viewportWidth, m_viewportHeight) / defaultTileSize) / std::log(2.0);
261 setupCamera();
262}
263
265{
266 if (m_cameraData == cameraData && !force)
267 return;
268
270 m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize;
271 setupCamera();
272}
273
278
283
285{
286 const double &x = projection.x();
287 if (m_cameraCenterXMercator < 0.5) {
288 if (x - m_cameraCenterXMercator > 0.5 )
289 return -1;
290 } else if (m_cameraCenterXMercator > 0.5) {
291 if (x - m_cameraCenterXMercator < -0.5 )
292 return 1;
293 }
294 return 0;
295}
296
297//wraps around center
299{
300 return QDoubleVector2D(projection.x() + double(projectionWrapFactor(projection)), projection.y());
301}
302
304{
305 double x = wrappedProjection.x();
306 if (x > 1.0)
307 return QDoubleVector2D(x - 1.0, wrappedProjection.y());
308 if (x <= 0.0)
309 return QDoubleVector2D(x + 1.0, wrappedProjection.y());
310 return wrappedProjection;
311}
312
314{
315 return (m_transformation * wrappedProjection).toVector2D();
316}
317
319{
321 QDoubleVector2D pos = itemPosition + QDoubleVector2D(centerOff);
323 pos *= 2.0;
324 pos -= QDoubleVector2D(1.0,1.0);
325
326 double s;
328
329 // a positive s means a point behind the camera. So do it again, after clamping Y. See QTBUG-61813
330 if (s > 0.0) {
331 pos = itemPosition;
332 // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera
335 pos *= 2.0;
336 pos -= QDoubleVector2D(1.0,1.0);
338 }
339
340 return res;
341}
342
343/* Default implementations */
345{
346 if (qIsNaN(pos.x()) || qIsNaN(pos.y()))
347 return QGeoCoordinate();
348
349 if (clipToViewport) {
350 int w = m_viewportWidth;
351 int h = m_viewportHeight;
352
353 if ((pos.x() < 0) || (w < pos.x()) || (pos.y() < 0) || (h < pos.y()))
354 return QGeoCoordinate();
355 }
356
358 // With rotation/tilting, a screen position might end up outside the projection space.
359 if (!isProjectable(wrappedMapProjection))
360 return QGeoCoordinate();
361 return mapProjectionToGeo(unwrapMapProjection(wrappedMapProjection));
362}
363
365{
366 if (!coordinate.isValid())
367 return QDoubleVector2D(qQNaN(), qQNaN());
368
369 QDoubleVector2D wrappedProjection = wrapMapProjection(geoToMapProjection(coordinate));
370 if (!isProjectable(wrappedProjection))
371 return QDoubleVector2D(qQNaN(), qQNaN());
372
374
375 if (clipToViewport) {
376 int w = m_viewportWidth;
377 int h = m_viewportHeight;
378 double x = pos.x();
379 double y = pos.y();
380 if ((x < -0.5) || (x > w + 0.5) || (y < -0.5) || (y > h + 0.5) || qIsNaN(x) || qIsNaN(y))
381 return QDoubleVector2D(qQNaN(), qQNaN());
382 }
383 return pos;
384}
385
390
395
396QMatrix4x4 QGeoProjectionWebMercator::quickItemTransformation(const QGeoCoordinate &coordinate, const QPointF &anchorPoint, qreal zoomLevel) const
397{
398 const QDoubleVector2D coordWrapped = geoToWrappedMapProjection(coordinate);
399 double scale = std::pow(0.5, zoomLevel - m_cameraData.zoomLevel());
400 const QDoubleVector2D anchorScaled = QDoubleVector2D(anchorPoint.x(), anchorPoint.y()) * scale;
401 const QDoubleVector2D anchorMercator = anchorScaled / mapWidth();
402
403 const QDoubleVector2D coordAnchored = coordWrapped - anchorMercator;
404 const QDoubleVector2D coordAnchoredScaled = coordAnchored * m_sideLengthPixels;
405 QDoubleMatrix4x4 matTranslateScale;
406 matTranslateScale.translate(coordAnchoredScaled.x(), coordAnchoredScaled.y(), 0.0);
407
408 scale = std::pow(0.5, (zoomLevel - std::floor(zoomLevel)) +
409 (std::floor(zoomLevel) - std::floor(m_cameraData.zoomLevel())));
410 matTranslateScale.scale(scale);
411
412 /*
413 * The full transformation chain for quickItemTransformation() would be:
414 * matScreenShift * m_quickItemTransformation * matTranslate * matScale
415 * where:
416 * matScreenShift = translate(-coordOnScreen.x(), -coordOnScreen.y(), 0)
417 * matTranslate = translate(coordAnchoredScaled.x(), coordAnchoredScaled.y(), 0.0)
418 * matScale = scale(scale)
419 *
420 * However, matScreenShift is removed, as setPosition(0,0) is used in place of setPositionOnScreen.
421 */
422
423 return toMatrix4x4(m_quickItemTransformation * matTranslateScale);
424}
425
427{
428 if (m_cameraData.tilt() == 0.0)
429 return true;
430
431 QDoubleVector3D pos = wrappedProjection * m_sideLengthPixels;
432 // use m_centerNearPlane in order to add an offset to m_eye.
435
436 if (dot < 0.0) // behind the near plane
437 return false;
438 return true;
439}
440
441QList<QDoubleVector2D> QGeoProjectionWebMercator::visibleGeometry() const
442{
444 const_cast<QGeoProjectionWebMercator *>(this)->updateVisibleRegion();
445 return m_visibleRegion;
446}
447
449{
451 const_cast<QGeoProjectionWebMercator *>(this)->updateVisibleRegion();
453}
454
456{
458 const_cast<QGeoProjectionWebMercator *>(this)->updateVisibleRegion();
459 return m_projectableRegion;
460}
461
463{
464 const QList<QDoubleVector2D> &visibleRegion = visibleGeometry();
465 QGeoPolygon poly;
466 for (qsizetype i = 0; i < visibleRegion.size(); ++i) {
467 const QDoubleVector2D &c = visibleRegion.at(i);
468 // If a segment spans more than half of the map longitudinally, split in 2.
469 if (i && qAbs(visibleRegion.at(i - 1).x() - c.x()) >= 0.5) { // This assumes a segment is never >= 1.0 (whole map span)
470 QDoubleVector2D extraPoint = (visibleRegion.at(i - 1) + c) * 0.5;
471 poly.addCoordinate(wrappedMapProjectionToGeo(extraPoint));
472 }
473 poly.addCoordinate(wrappedMapProjectionToGeo(c));
474 }
475 if (visibleRegion.size() >= 2 && qAbs(visibleRegion.last().x() - visibleRegion.first().x()) >= 0.5) {
476 QDoubleVector2D extraPoint = (visibleRegion.last() + visibleRegion.first()) * 0.5;
477 poly.addCoordinate(wrappedMapProjectionToGeo(extraPoint));
478 }
479
480 return poly;
481}
482
484{
485 double s;
486 return viewportToWrappedMapProjection(itemPosition, s);
487}
488
489/*
490 actual implementation of itemPositionToWrappedMapProjection
491*/
493{
494 QDoubleVector2D pos = itemPosition;
496
497 // determine itemPosition on the near plane
499 p += m_up * pos.y();
500 p += m_side * pos.x();
501
502 // compute the ray using the eye position
503 QDoubleVector3D ray = m_eye - p;
504 ray.normalize();
505
506 return (xyPlane.lineIntersection(m_eye, ray, s) / m_sideLengthPixels).toVector2D();
507}
508
509/*
510 Returns a pair of <newCenter, newZoom>
511*/
512QPair<QGeoCoordinate, qreal> QGeoProjectionWebMercator::fitViewportToGeoRectangle(const QGeoRectangle &rectangle,
513 const QMargins &m) const
514{
515 QPair<QGeoCoordinate, qreal> res;
516 res.second = qQNaN();
517 if (m_viewportWidth <= m.left() + m.right() || m_viewportHeight <= m.top() + m.bottom())
518 return res;
519
520 QDoubleVector2D topLeftPoint = geoToMapProjection(rectangle.topLeft());
521 QDoubleVector2D bottomRightPoint = geoToMapProjection(rectangle.bottomRight());
522 if (bottomRightPoint.x() < topLeftPoint.x()) // crossing the dateline
523 bottomRightPoint.setX(bottomRightPoint.x() + 1.0);
524
525 // find center of the bounding box
526 QDoubleVector2D center = (topLeftPoint + bottomRightPoint) * 0.5;
527 center.setX(center.x() > 1.0 ? center.x() - 1.0 : center.x());
528 res.first = mapProjectionToGeo(center);
529
530 // if the shape is empty we just change center position, not zoom
531 double bboxWidth = (bottomRightPoint.x() - topLeftPoint.x()) * mapWidth();
532 double bboxHeight = (bottomRightPoint.y() - topLeftPoint.y()) * mapHeight();
533
534 if (bboxHeight == 0.0 && bboxWidth == 0.0)
535 return res;
536
537 double zoomRatio = qMax(bboxWidth / (m_viewportWidth - m.left() - m.right()),
538 bboxHeight / (m_viewportHeight - m.top() - m.bottom()));
539 zoomRatio = std::log(zoomRatio) / std::log(2.0);
540 res.second = m_cameraData.zoomLevel() - zoomRatio;
541
542 return res;
543}
544
549
554
559
560void QGeoProjectionWebMercator::setupCamera()
561{
562 m_qsgTransformDirty = true;
566
567 int intZoomLevel = static_cast<int>(std::floor(m_cameraData.zoomLevel()));
568 m_sideLengthPixels = (1 << intZoomLevel) * defaultTileSize;
570 //aperture(90 / 2) = 1
572
573 double f = m_viewportHeight;
574 double z = std::pow(2.0, m_cameraData.zoomLevel() - intZoomLevel) * defaultTileSize;
575 double altitude = f / (2.0 * z);
576 // Also in mercator space
577 double z_mercator = std::pow(2.0, m_cameraData.zoomLevel()) * defaultTileSize;
578 double altitude_mercator = f / (2.0 * z_mercator);
579
580 // calculate eye
581 m_eye = m_center;
582 m_eye.setZ(altitude * defaultTileSize / m_aperture);
583
584 // And in mercator space
586 m_eyeMercator.setZ(altitude_mercator / m_aperture);
588 m_eyeMercator0.setZ(altitude_mercator / m_aperture);
589 QDoubleVector3D eye0(0,0,0);
590 eye0.setZ(altitude * defaultTileSize / m_aperture);
591
595
596 // In mercator space too
600
601 if (m_cameraData.bearing() > 0.0) {
602 QDoubleMatrix4x4 mBearing;
603 mBearing.rotate(m_cameraData.bearing(), m_view);
604 m_up = mBearing * m_up;
605
606 // In mercator space too
607 QDoubleMatrix4x4 mBearingMercator;
608 mBearingMercator.rotate(m_cameraData.bearing(), m_viewMercator);
609 m_upMercator = mBearingMercator * m_upMercator;
610 }
611
614
615 if (m_cameraData.tilt() > 0.0) { // tilt has been already thresholded by QGeoCameraData::setTilt
616 QDoubleMatrix4x4 mTilt;
617 mTilt.rotate(-m_cameraData.tilt(), m_side);
618 m_eye = mTilt * m_view + m_center;
619 eye0 = mTilt * m_view;
620
621 // In mercator space too
622 QDoubleMatrix4x4 mTiltMercator;
623 mTiltMercator.rotate(-m_cameraData.tilt(), m_sideMercator);
625 m_eyeMercator0 = mTiltMercator * m_viewMercator;
626 }
627
628 m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow
631
632 m_nearPlane = 1.0;
633 // At ZL 20 the map has 2^20 tiles per side. That is 1048576.
634 // Placing the camera on one corner of the map, rotated toward the opposite corner, and tilted
635 // at almost 90 degrees would require a frustum that can span the whole size of this map.
636 // For this reason, the far plane is set to 2 * 2^20 * defaultTileSize.
637 // That is, in order to make sure that the whole map would fit in the frustum at this ZL.
638 // Since we are using a double matrix, and since the largest value in the matrix is going to be
639 // 2 * m_farPlane (as near plane is 1.0), there should be sufficient precision left.
640 //
641 // TODO: extend this to support clip distance.
642 m_farPlane = (altitude + 2097152.0) * defaultTileSize;
643
646 m_nearPlaneMercator = 0.000002; // this value works until ZL 18. Above that, a better progressive formula is needed, or
647 // else, this clips too much.
648
649 double aspectRatio = 1.0 * m_viewportWidth / m_viewportHeight;
650
651 m_halfWidth = m_aperture * aspectRatio;
653
654 double verticalHalfFOV = QLocationUtils::degrees(atan(m_aperture));
655
660
661 QDoubleMatrix4x4 projectionMatrix;
663
664 /*
665 * The full transformation chain for m_transformation is:
666 * matScreen * matScreenFit * matShift * projectionMatrix * cameraMatrix * matZoomLevelScale
667 * where:
668 * matZoomLevelScale = scale(m_sideLength, m_sideLength, 1.0)
669 * matShift = translate(1.0, 1.0, 0.0)
670 * matScreenFit = scale(0.5, 0.5, 1.0)
671 * matScreen = scale(m_viewportWidth, m_viewportHeight, 1.0)
672 */
673
675 QDoubleMatrix4x4 matScreenTransformation;
676 matScreenTransformation.scale(0.5 * m_viewportWidth, 0.5 * m_viewportHeight, 1.0);
677 matScreenTransformation(0,3) = (0.5 + offsetPct.x()) * m_viewportWidth;
678 matScreenTransformation(1,3) = (0.5 + offsetPct.y()) * m_viewportHeight;
679
680 m_transformation = matScreenTransformation * projectionMatrix * m_cameraMatrix;
683
684 m_transformation0 = matScreenTransformation * projectionMatrix * m_cameraMatrix0;
686
689
690 // The method does not support tilting angles >= 90.0 or < 0.
691
692 // The following formula is used to have a growing epsilon with the zoom level,
693 // in order not to have too large values at low zl, which would overflow when converted to Clipper::cInt.
694 const double upperBoundEpsilon = 1.0 / std::pow(10, 1.0 + m_cameraData.zoomLevel() / 5.0);
695 const double elevationUpperBound = 90.0 - upperBoundEpsilon;
696 const double maxRayElevation = qMin(elevationUpperBound - m_cameraData.tilt(), verticalHalfFOV);
697 double maxHalfAperture = 0;
699 if (maxRayElevation < verticalHalfFOV) {
700 maxHalfAperture = tan(QLocationUtils::radians(maxRayElevation));
701 m_verticalEstateToSkip = 1.0 - maxHalfAperture / m_aperture;
702 }
703
704 m_minimumUnprojectableY = m_verticalEstateToSkip * 0.5 * m_viewportHeight; // m_verticalEstateToSkip is relative to half aperture
706}
707
708void QGeoProjectionWebMercator::updateVisibleRegion()
709{
710 m_visibleRegionDirty = false;
711
712 double viewportHalfWidth = (!m_visibleArea.isEmpty()) ? m_visibleArea.width() / m_viewportWidth : 1.0;
713 double viewportHalfHeight = (!m_visibleArea.isEmpty()) ? m_visibleArea.height() / m_viewportHeight : 1.0;
714
715 double top = qMax<double>(-viewportHalfHeight, -1 + m_verticalEstateToSkip);
716 double bottom = viewportHalfHeight;
717 double left = -viewportHalfWidth;
718 double right = viewportHalfWidth;
719
724
725 // To make sure that what is returned can be safely converted back to lat/lon without risking overlaps
726 double mapLeftLongitude = QLocationUtils::mapLeftLongitude(m_cameraData.center().longitude());
727 double mapRightLongitude = QLocationUtils::mapRightLongitude(m_cameraData.center().longitude());
728 double leftX = geoToWrappedMapProjection(QGeoCoordinate(0, mapLeftLongitude)).x();
729 double rightX = geoToWrappedMapProjection(QGeoCoordinate(0, mapRightLongitude)).x();
730
731 QList<QDoubleVector2D> mapRect;
732 mapRect.push_back(QDoubleVector2D(leftX, 1.0));
733 mapRect.push_back(QDoubleVector2D(rightX, 1.0));
734 mapRect.push_back(QDoubleVector2D(rightX, 0.0));
735 mapRect.push_back(QDoubleVector2D(leftX, 0.0));
736
737 QList<QDoubleVector2D> viewportRect;
738 viewportRect.push_back(bl);
739 viewportRect.push_back(br);
740 viewportRect.push_back(tr);
741 viewportRect.push_back(tl);
742
743 QClipperUtils clipper;
744 clipper.clearClipper();
745 clipper.addSubjectPath(mapRect, true);
746 clipper.addClipPolygon(viewportRect);
747
748 const auto res = clipper.execute(QClipperUtils::Intersection);
750 if (res.size())
751 m_visibleRegion = res[0]; // Intersection between two convex quadrilaterals should always be a single polygon
752
754 mapRect.clear();
755 // The full map rectangle in extended mercator space
756 mapRect.push_back(QDoubleVector2D(-1.0, 1.0));
757 mapRect.push_back(QDoubleVector2D( 2.0, 1.0));
758 mapRect.push_back(QDoubleVector2D( 2.0, 0.0));
759 mapRect.push_back(QDoubleVector2D(-1.0, 0.0));
760 if (m_cameraData.tilt() == 0) {
761 m_projectableRegion = mapRect;
762 } else {
764 Line2D nearPlaneXYIntersection = nearPlane.planeXYIntersection();
765 double squareHalfSide = qMax(5.0, nearPlaneXYIntersection.m_point.length());
766 QDoubleVector2D viewDirectionProjected = -m_viewNormalized.toVector2D().normalized();
767
768
769 QDoubleVector2D tl = nearPlaneXYIntersection.m_point
770 - squareHalfSide * nearPlaneXYIntersection.m_direction
771 + 2 * squareHalfSide * viewDirectionProjected;
772 QDoubleVector2D tr = nearPlaneXYIntersection.m_point
773 + squareHalfSide * nearPlaneXYIntersection.m_direction
774 + 2 * squareHalfSide * viewDirectionProjected;
775 QDoubleVector2D bl = nearPlaneXYIntersection.m_point
776 - squareHalfSide * nearPlaneXYIntersection.m_direction;
777 QDoubleVector2D br = nearPlaneXYIntersection.m_point
778 + squareHalfSide * nearPlaneXYIntersection.m_direction;
779
780 QList<QDoubleVector2D> projectableRect;
781 projectableRect.push_back(bl);
782 projectableRect.push_back(br);
783 projectableRect.push_back(tr);
784 projectableRect.push_back(tl);
785
786
787 QClipperUtils clipperProjectable;
788 clipperProjectable.clearClipper();
789 clipperProjectable.addSubjectPath(mapRect, true);
790 clipperProjectable.addClipPolygon(projectableRect);
791
792 const auto resProjectable = clipperProjectable.execute(QClipperUtils::Intersection);
793 if (resProjectable.size())
794 m_projectableRegion = resProjectable[0]; // Intersection between two convex quadrilaterals should always be a single polygon
795 else
796 m_projectableRegion = viewportRect;
797 }
798
799 // Compute m_visibleRegionExpanded as a clipped expanded version of m_visibleRegion
800 QDoubleVector2D centroid;
801 for (const QDoubleVector2D &v: std::as_const(m_visibleRegion))
802 centroid += v;
803 centroid /= m_visibleRegion.size();
804
806 for (const QDoubleVector2D &v: std::as_const(m_visibleRegion)) {
807 const QDoubleVector2D vc = v - centroid;
808 m_visibleRegionExpanded.push_back(centroid + vc * 1.2); // fixing expansion factor to 1.2
809 }
810
811}
812
817
818/*
819 *
820 * Line implementation
821 *
822 */
823
828
830 : m_point(linePoint), m_direction(lineDirection.normalized())
831{
832
833}
834
836{
837 return (m_direction.length() > 0.5);
838}
839
840/*
841 *
842 * Plane implementation
843 *
844 */
845
850
852 : m_point(planePoint), m_normal(planeNormal.normalized()) { }
853
855{
856 double s;
857 return lineIntersection(linePoint, lineDirection, s);
858}
859
861{
862 QDoubleVector3D w = linePoint - m_point;
863 // s = -n.dot(w) / n.dot(u). p = p0 + su; u is lineDirection
864 s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection);
865 return linePoint + lineDirection * s;
866}
867
869{
870 // cross product of the two normals for the line direction
871 QDoubleVector3D lineDirection = QDoubleVector3D::crossProduct(m_normal, xyNormal);
872 lineDirection.setZ(0.0);
873 lineDirection.normalize();
874
875 // cross product of the line direction and the plane normal to find the direction on the plane
876 // intersecting the xy plane
877 QDoubleVector3D directionToXY = QDoubleVector3D::crossProduct(m_normal, lineDirection);
878 QDoubleVector3D p = xyPlane.lineIntersection(m_point, directionToXY);
879 return Line2D(p.toVector2D(), lineDirection.toVector2D());
880}
881
883{
884 return (m_normal.length() > 0.5);
885}
886
void translate(const QDoubleVector3D &vector)
void frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane)
void scale(const QDoubleVector3D &vector)
void lookAt(const QDoubleVector3D &eye, const QDoubleVector3D &center, const QDoubleVector3D &up)
void rotate(double angle, const QDoubleVector3D &vector)
Q_DECL_CONSTEXPR QPointF toPointF() const
QDoubleVector2D normalized() const
QDoubleVector3D toVector3D() const
Q_DECL_CONSTEXPR double x() const
void setY(double y)
Q_DECL_CONSTEXPR double y() const
static Q_DECL_CONSTEXPR double dotProduct(const QDoubleVector3D &v1, const QDoubleVector3D &v2)
Q_DECL_CONSTEXPR double x() const
Q_DECL_CONSTEXPR double y() const
void setZ(double z)
Q_DECL_CONSTEXPR QDoubleVector2D toVector2D() const
static QDoubleVector3D normal(const QDoubleVector3D &v1, const QDoubleVector3D &v2)
QDoubleVector3D normalized() const
static Q_DECL_CONSTEXPR QDoubleVector3D crossProduct(const QDoubleVector3D &v1, const QDoubleVector3D &v2)
double zoomLevel() const
double tilt() const
double fieldOfView() const
double bearing() const
QGeoCoordinate center() const
\inmodule QtPositioning
double longitude
This property holds the longitude in decimal degrees.
bool isValid
This property holds the validity of this geo coordinate.
\inmodule QtPositioning
Definition qgeopolygon.h:16
void setViewportSize(const QSize &size) override
QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport=true) const override
QList< QDoubleVector2D > m_visibleRegionExpanded
QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport=true) const override
QDoubleVector3D centerMercator() const override
QMatrix4x4 projectionTransformation_centered() const override
QDoubleVector3D m_centerNearPlane
QList< QDoubleVector2D > m_visibleRegion
QMatrix4x4 projectionTransformation() const override
QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const
QDoubleVector2D viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
QGeoProjection::ProjectionGroup projectionGroup() const override
QDoubleMatrix4x4 m_transformation0
QGeoProjection::ProjectionType projectionType() const override
QDoubleVector3D m_centerNearPlaneMercator
QDoubleVector2D geoToWrappedMapProjection(const QGeoCoordinate &coordinate) const
QList< QDoubleVector2D > visibleGeometryExpanded() const
QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const
QPair< QGeoCoordinate, qreal > fitViewportToGeoRectangle(const QGeoRectangle &rectangle, const QMargins &margins) const
QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const
QDoubleMatrix4x4 m_transformation
QDoubleMatrix4x4 m_cameraMatrix
const QMatrix4x4 & qsgTransform() const override
QGeoCoordinate wrappedMapProjectionToGeo(const QDoubleVector2D &wrappedProjection) const
QDoubleMatrix4x4 m_cameraMatrix0
double minimumZoom() const override
void setVisibleArea(const QRectF &visibleArea) override
bool setBearing(qreal bearing, const QGeoCoordinate &coordinate) override
QGeoProjection::Datum datum() const override
double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override
QGeoCameraData cameraData() const override
void setCameraData(const QGeoCameraData &cameraData, bool force=true) override
QList< QDoubleVector2D > visibleGeometry() const
QDoubleVector2D itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
QList< QDoubleVector2D > projectableGeometry() const
QList< QDoubleVector2D > m_projectableRegion
int projectionWrapFactor(const QDoubleVector2D &projection) const
QDoubleMatrix4x4 m_quickItemTransformation
QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const
bool isProjectable(const QDoubleVector2D &wrappedProjection) const
double minimumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override
QDoubleVector2D wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const
QGeoShape visibleRegion() const override
QGeoCoordinate anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const override
QMatrix4x4 quickItemTransformation(const QGeoCoordinate &coordinate, const QPointF &anchorPoint, qreal zoomLevel) const
virtual QTransform itemToWindowTransform() const
virtual QGeoShape visibleRegion() const
QTransform m_itemToWindowTransform
virtual bool setBearing(qreal bearing, const QGeoCoordinate &coordinate)
void setItemToWindowTransform(const QTransform &itemToWindowTransform)
virtual ~QGeoProjection()
virtual QGeoCoordinate anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const
\inmodule QtPositioning
QGeoCoordinate topLeft
This property holds the top left coordinate of this geo rectangle.
QGeoCoordinate bottomRight
This property holds the bottom right coordinate of this geo rectangle.
\inmodule QtPositioning
Definition qgeoshape.h:17
qsizetype size() const noexcept
Definition qlist.h:397
void push_back(parameter_type t)
Definition qlist.h:675
void clear()
Definition qlist.h:434
static double radians(double degrees)
static double mapRightLongitude(double centerLongitude)
static double degrees(double radians)
static double mapLeftLongitude(double centerLongitude)
\inmodule QtCore
Definition qmargins.h:24
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:661
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:658
constexpr QPointF center() const noexcept
Returns the center point of the rectangle.
Definition qrect.h:699
\inmodule QtCore
Definition qsize.h:208
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:332
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:335
\inmodule QtCore
Definition qsize.h:25
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
static QDoubleVector2D coordToMercator(const QGeoCoordinate &coord)
static QGeoCoordinate mercatorToCoord(const QDoubleVector2D &mercator)
QCamera * camera
Definition camera.cpp:19
constexpr QColor tan
Definition qcolor.h:450
Combined button and popup list for selecting options.
bool qIsNaN(qfloat16 f) noexcept
Definition qfloat16.h:284
static QPointF centerOffset(const QSizeF &screenSize, const QRectF &visibleArea)
static QPointF marginsOffset(const QSizeF &screenSize, const QRectF &visibleArea)
static QMatrix4x4 toMatrix4x4(const QDoubleMatrix4x4 &m)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN()
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLfloat GLfloat f
GLint left
GLint GLint bottom
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
const GLubyte * c
GLint GLenum GLboolean normalized
Definition qopenglext.h:752
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
static qreal dot(const QPointF &a, const QPointF &b)
QScreen * screen
[1]
Definition main.cpp:29
#define tr(X)
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
QDoubleVector3D lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const