diff options
Diffstat (limited to 'src/location')
12 files changed, 341 insertions, 28 deletions
diff --git a/src/location/qlocation.cpp b/src/location/qlocation.cpp index 5a32cce8..6733bd4a 100644 --- a/src/location/qlocation.cpp +++ b/src/location/qlocation.cpp @@ -40,6 +40,19 @@ namespace QLocation { When \e searching for places, unspecified means that places of any scope is returned. */ + +/*! + \enum QLocation::ReferenceSurface + + Defines the reference surface on which various map items (e.g. polygons, polylines) are defined. + + \value Map Items are defined on a map. This means, e.g. for a polyline that + nodes are connected with straight lines on the map. + \value Globe Items are defined on the globe. This means, e.g. for a polyine + that nodes are connected with circle sections that represent the + shortest connection between points on a sphere. This connection is + also known as great circle path. +*/ } QT_END_NAMESPACE diff --git a/src/location/qlocationglobal_p.h b/src/location/qlocationglobal_p.h index 7d1befea..33ed3d07 100644 --- a/src/location/qlocationglobal_p.h +++ b/src/location/qlocationglobal_p.h @@ -16,7 +16,27 @@ // #include "qlocationglobal.h" +#include <qqml.h> #include "private/qglobal_p.h" #include <QtLocation/private/qtlocationexports_p.h> +QT_BEGIN_NAMESPACE + +namespace QLocation +{ +Q_NAMESPACE_EXPORT(Q_LOCATION_PRIVATE_EXPORT) +QML_NAMED_ELEMENT(QtLocation) +QML_ADDED_IN_VERSION(6, 6) +Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +enum class ReferenceSurface : uint8_t { + Map, + Globe +}; +Q_ENUM_NS(ReferenceSurface) + +} + +QT_END_NAMESPACE + #endif // QLOCATIONGLOBAL_P_H diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp index 7cf69e59..8d1de1c9 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp @@ -87,6 +87,19 @@ QT_BEGIN_NAMESPACE \since 5.14 */ +/*! + \qmlproperty enum QtLocation::MapCircle::referenceSurface + + This property determines the reference surface of the circle. If it is set to + \l QLocation::ReferenceSurface::Map the circle is drawn as a circe on the map with + \l radius approximated to match the map scale at the center of the circle. + If it is set to \l QLocation::ReferenceSurface::Globe the circle is mapped onto + a sphere and the great circle path is used to determine the coverage of the circle. + Default value is \l QLocation::ReferenceSurface::Map. + + \since 6.5 +*/ + struct Vertex { QVector2D position; @@ -108,6 +121,8 @@ QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) this, &QDeclarativeCircleMapItem::onLinePropertiesChanged); QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged, this, &QDeclarativeCircleMapItem::onLinePropertiesChanged); + QObject::connect(this, &QDeclarativeCircleMapItem::referenceSurfaceChanged, + [=]() {m_d->onGeoGeometryChanged();}); } QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() @@ -392,7 +407,24 @@ int QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate ¢e (distanceToSouthPole < distance? 1 : 0); } -void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QDoubleVector2D> &path, +void QDeclarativeCircleMapItemPrivate::calculatePeripheralPointsSimple(QList<QDoubleVector2D> &path, + const QGeoCoordinate ¢er, + qreal distance, + const QGeoProjectionWebMercator &p, + int steps) +{ + const double lambda = 0.0001; + const QDoubleVector2D c = p.geoToMapProjection(center); + const double lambda_geo = center.distanceTo(p.mapProjectionToGeo(c + QDoubleVector2D(lambda, 0))); + const qreal mapDistance = distance * lambda / lambda_geo; + + for (int i = 0; i < steps; ++i) { + const qreal rad = 2 * M_PI * i / steps; + path << c + QDoubleVector2D(cos(rad), sin(rad)) * mapDistance; + } +} + +void QDeclarativeCircleMapItemPrivate::calculatePeripheralPointsGreatCircle(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p, @@ -459,7 +491,7 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() const qreal &radius = m_circle.m_circle.radius(); // if circle crosses north/south pole, then don't preserve circular shape, - int crossingPoles = crossEarthPole(center, radius); + int crossingPoles = m_circle.referenceSurface() == QLocation::ReferenceSurface::Globe ? crossEarthPole(center, radius) : 0; if (crossingPoles == 1) { // If the circle crosses both poles, we will remove it from a rectangle includeOnePoleInPath(circlePath, center, radius, p); m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce); @@ -486,7 +518,7 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() surroundingRect = {{anchorRect, -0.1}, {anchorRect + 1.0, -0.1}, {anchorRect + 1.0, 1.1}, {anchorRect, 1.1}}; - wrappingMode = QGeoMapPolygonGeometry::WrapAround; + wrappingMode = QGeoMapPolygonGeometry::Duplicate; } m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode); } else { diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h index 117dcb63..ee6e8d57 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h @@ -62,7 +62,10 @@ public: m_circlePath.clear(); const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()); - calculatePeripheralPoints(m_circlePath, m_circle.center(), m_circle.radius(), p, CircleSamples); + if (m_circle.referenceSurface() == QLocation::ReferenceSurface::Map) + calculatePeripheralPointsSimple(m_circlePath, m_circle.center(), m_circle.radius(), p, CircleSamples); + else + calculatePeripheralPointsGreatCircle(m_circlePath, m_circle.center(), m_circle.radius(), p, CircleSamples); } static int crossEarthPole(const QGeoCoordinate ¢er, qreal distance); @@ -70,7 +73,10 @@ public: static void includeOnePoleInPath(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p); - static void calculatePeripheralPoints(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + static void calculatePeripheralPointsSimple(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p, int steps); + + static void calculatePeripheralPointsGreatCircle(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p, int steps); QDeclarativeCircleMapItem &m_circle; diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp index 233ea82d..1e455816 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp @@ -161,6 +161,20 @@ void QDeclarativeGeoMapItemBase::setAutoFadeIn(bool fadeIn) polishAndUpdate(); } +QLocation::ReferenceSurface QDeclarativeGeoMapItemBase::referenceSurface() const +{ + return m_referenceSurface; +} + +void QDeclarativeGeoMapItemBase::setReferenceSurface(QLocation::ReferenceSurface referenceSurface) +{ + if (referenceSurface == m_referenceSurface) + return; + m_referenceSurface = referenceSurface; + emit referenceSurfaceChanged(); + updatePolish(); +} + int QDeclarativeGeoMapItemBase::lodThreshold() const { return m_lodThreshold; diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h index 38eef2db..9f2de62b 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h @@ -20,6 +20,7 @@ #include <QtQuick/QQuickItem> #include <QtPositioning/QGeoShape> +#include <QtLocation/qlocation.h> #include <QtLocation/private/qdeclarativegeomap_p.h> #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qgeomap_p.h> @@ -48,15 +49,18 @@ class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemBase : public QQuickItem QML_NAMED_ELEMENT(GeoMapItemBase) QML_ADDED_IN_VERSION(5, 0) QML_UNCREATABLE("GeoMapItemBase is not intended instantiable by developer.") + Q_ENUMS(ReferenceSurface) Q_PROPERTY(QGeoShape geoShape READ geoShape WRITE setGeoShape STORED false ) Q_PROPERTY(bool autoFadeIn READ autoFadeIn WRITE setAutoFadeIn REVISION(5, 14)) + Q_PROPERTY(QLocation::ReferenceSurface referenceSurface READ referenceSurface WRITE setReferenceSurface NOTIFY referenceSurfaceChanged REVISION(6, 6)) Q_PROPERTY(int lodThreshold READ lodThreshold WRITE setLodThreshold NOTIFY lodThresholdChanged REVISION(5, 15)) public: explicit QDeclarativeGeoMapItemBase(QQuickItem *parent = nullptr); virtual ~QDeclarativeGeoMapItemBase(); + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map); virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset); @@ -68,6 +72,9 @@ public: bool autoFadeIn() const; void setAutoFadeIn(bool fadeIn); + QLocation::ReferenceSurface referenceSurface() const; + void setReferenceSurface(QLocation::ReferenceSurface referenceSurface); + int lodThreshold() const; void setLodThreshold(int lt); unsigned int zoomForLOD(int zoom) const; @@ -101,6 +108,7 @@ Q_SIGNALS: void mapItemOpacityChanged(); Q_REVISION(12) void addTransitionFinished(); Q_REVISION(12) void removeTransitionFinished(); + void referenceSurfaceChanged(); void lodThresholdChanged(); protected Q_SLOTS: @@ -129,6 +137,7 @@ private: std::unique_ptr<QDeclarativeGeoMapItemTransitionManager> m_transitionManager; bool m_autoFadeIn = true; + QLocation::ReferenceSurface m_referenceSurface = QLocation::ReferenceSurface::Map; int m_lodThreshold = 0; friend class QDeclarativeGeoMap; diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp index a1501dd6..6c45e164 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp @@ -191,6 +191,72 @@ QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list) return QRectF(xMin, yMin, xMax - xMin, yMax - yMin); } +QList<QGeoCoordinate> greaterCirclePath(const QList<QGeoCoordinate> &cornerPoints, + greaterCirclePathForm form, int N) +{ + QList<QGeoCoordinate> path; + path.reserve(N); + //If the path has to be closed we include the first coordinate again at the end of the procedure + qsizetype lineCount = cornerPoints.size() - ((form == OpenPath) ? 1 : 0); + for (qsizetype i = 0; i < lineCount; i++) { + path.append(cornerPoints.at(i)); + const double p_lat = cornerPoints.at(i).latitude() / 180.0 * M_PI; //latitude point p in rad + const double p_lon = cornerPoints.at(i).longitude() / 180.0 * M_PI; //longitude point p in rad + const double q_lat = cornerPoints.at((i + 1) % cornerPoints.size()).latitude() / 180.0 * M_PI; //latitude point q in rad + const double q_lon = cornerPoints.at((i + 1) % cornerPoints.size()).longitude() / 180.0 * M_PI;//longitude point q in rad + + const double c_p_lat = cos(p_lat); + const double c_p_lon = cos(p_lon); + const double s_p_lat = sin(p_lat); + const double s_p_lon = sin(p_lon); + const double c_q_lat = cos(q_lat); + const double c_q_lon = cos(q_lon); + const double s_q_lat = sin(q_lat); + const double s_q_lon = sin(q_lon); + + const QDoubleVector3D p(c_p_lat * c_p_lon, + c_p_lat * s_p_lon, + s_p_lat); + const QDoubleVector3D q(c_q_lat * c_q_lon, + c_q_lat * s_q_lon, + s_q_lat); + const double qp = QDoubleVector3D::dotProduct(q, p); //scalar product of q p + + if (qp <= -1.0 || qp >= 1.0) + continue; + + // the path from q to p will be parametrized as a combination of points p and q: + // x = s*p + t*q. + // We will then directly calculate the latitude and longitude of the interpolated point x + // from the latitude and longitude of cornerpoints q and p. + // The parameters s and t depend on each other to ensure that x is located on the + // surface of the earth: s*s + 2*s*t*qp + t*t = 1 + // Their minimum value is 0, as negative values indicate a path that goes around earth + // long way. Their maximum value follows as + const double paramMax = sqrt(1 / (1 - qp * qp)); + + double t = 0.0; + for (int sign = 1; sign > -2.0; sign -= 2.0) { + for (; t <= paramMax && t >= 0.0; t += sign * paramMax / N / 2) { + // dependence between s and t requires a plus/minus + // therefore we solve this equation two times with sign = -1/+1 + const double s = - t * qp + sign * sqrt(t * t * (qp * qp - 1.0) + 1.0); + if (s < 0.0) //s > paramMax will never happen. If s < 0 we are done. + break; + const double lat = asin(s * s_p_lat + t * s_q_lat); + const double lon = atan2(s * c_p_lat * s_p_lon + t * c_q_lat * s_q_lon, + s * c_p_lat * c_p_lon + t * c_q_lat * c_q_lon); + path.append(QGeoCoordinate(lat * 180.0 / M_PI, lon * 180.0 / M_PI)); + } + t -= paramMax / N / 2; + } + } + if (form == OpenPath) + path.append(cornerPoints.last()); + path.squeeze(); + return path; +} + } // namespace QDeclarativeGeoMapItemUtils QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h index 50ece528..3637d1e4 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h @@ -25,6 +25,11 @@ QT_BEGIN_NAMESPACE namespace QDeclarativeGeoMapItemUtils { + enum greaterCirclePathForm { + OpenPath, + ClosedPath + }; + struct vec2 { float x; float y; @@ -90,6 +95,9 @@ namespace QDeclarativeGeoMapItemUtils QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list); + QList<QGeoCoordinate> greaterCirclePath(const QList<QGeoCoordinate> &cornerPoints, + greaterCirclePathForm form = QDeclarativeGeoMapItemUtils::OpenPath, + int N=360); }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp index 0ace1629..40cab843 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp @@ -96,13 +96,26 @@ QT_BEGIN_NAMESPACE \since 5.14 */ +/*! + \qmlproperty enum QtLocation::MapPolygon::referenceSurface + + This property determines the reference surface of the polygon. If it is set to + \l QLocation::ReferenceSurface::Map the polygons vertices are connected with straight + lines on the map. If it is set to \l QLocation::ReferenceSurface::Globe, the vertices + are connected following the great circle path, describing the shortest connection of + two points on a sphere. + Default value is \l QLocation::ReferenceSurface::Map. + + \since 6.5 +*/ + QGeoMapPolygonGeometry::QGeoMapPolygonGeometry() = default; /*! \internal */ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, - const QList<QList <QDoubleVector2D>> &paths, + const QList<QList <QDoubleVector2D>> &basePaths, MapBorderBehaviour wrapping) { // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes @@ -112,17 +125,77 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); srcPath_ = QPainterPath(); srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0.0, 0.0)); //avoid warning of NaN values if function is returned early + const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); + + QList<QList<QDoubleVector2D>> paths; + + if (wrapping == WrapAround) { + // 0.1 Wrap the points around the globe if the path makes more sense that way. + // Ultimately, this is done if it is closer to walk around the day-border than the other direction + paths.reserve(basePaths.size()); + for (qsizetype j = 0; j< basePaths.size(); j++) { + const QList<QDoubleVector2D> &bp = basePaths[j]; + if (bp.isEmpty()) + continue; + paths << QList<QDoubleVector2D>({bp[0]}); + QList<QDoubleVector2D> &pp = paths[j]; + pp.reserve(bp.size()); + for (qsizetype i = 1; i < bp.size(); i++) { + if (bp[i].x() > pp.last().x() + 0.5) + pp << bp[i] - QDoubleVector2D(1.0, 0.0); + else if (bp[i].x() < pp.last().x() - 0.5) + pp << bp[i] + QDoubleVector2D(1.0, 0.0); + else + pp << bp[i]; + } + } + + // 0.2 Check and include one of the poles if necessary to make sense out of the polygon + for (qsizetype j = 0; j < paths.size(); j++) { + QList<QDoubleVector2D> &pp = paths[j]; + + if (pp.last().x() - pp.first().x() < -0.5) { + for (qsizetype i = 0; i < floor(pp.length()/2.); i++) + pp.swapItemsAt(i, pp.length() - i - 1); + } + if (pp.last().x() - pp.first().x() > 0.5) { + + const double leftBorder = cameraRect.left(); + const double rightBorder = cameraRect.right(); + + qsizetype originalPathLength = pp.length(); + + if (pp.last().x() < rightBorder) { + for (qsizetype i = 0; i < originalPathLength; i++) + pp.append(pp[i] + QDoubleVector2D(1.0, 0.0)); + } + if (pp.first().x() > leftBorder) { + for (qsizetype i = 0; i < originalPathLength; i++) + pp.insert(i, pp[2*i] - QDoubleVector2D(1.0, 0.0)); + } + const double newPoleLat = (pp.first().y() + pp.last().y() < 1.0) ? 0.0 : 1.0; //mean of y < 0.5? + const QDoubleVector2D P1 = pp.first(); + const QDoubleVector2D P2 = pp.last(); + pp.push_front(QDoubleVector2D(P1.x(), newPoleLat)); + pp.append(QDoubleVector2D(P2.x(), newPoleLat)); + + wrapping = DrawOnce; + } + } + } else { + paths = basePaths; + } //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible // The viewport is periodic in x-direction in the interval [-1; 1]. // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1; - const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); - QRectF itemRect; - for (const auto &path : paths) - itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path); QList<QList<QDoubleVector2D>> wrappedPaths; - if (wrapping == WrapAround) { + if (wrapping == Duplicate || wrapping == WrapAround) { + QRectF itemRect; + for (const auto &path : paths) + itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path); + for (double xoffset : {-1.0, 0.0, 1.0}) { if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset, 0.0)))) continue; @@ -134,8 +207,9 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, wP.append(coord+QDoubleVector2D(xoffset, 0.0)); } } - } else + } else { wrappedPaths = paths; + } if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle return; @@ -257,7 +331,10 @@ void QDeclarativePolygonMapItemPrivateCPU::updatePolish() QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); m_poly.m_updatingGeometry = true; - m_geometry.updateSourcePoints(*map, m_geopathProjected); + m_geometry.updateSourcePoints(*map, m_geopathProjected, + m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe ? + QGeoMapPolygonGeometry::WrapAround : + QGeoMapPolygonGeometry::Duplicate); const QRectF bb = m_geometry.sourceBoundingBox(); @@ -316,6 +393,8 @@ QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) this, &QDeclarativePolygonMapItem::onLinePropertiesChanged); QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged, this, &QDeclarativePolygonMapItem::onLinePropertiesChanged); + QObject::connect(this, &QDeclarativePolygonMapItem::referenceSurfaceChanged, + [=]() {m_d->onGeoGeometryChanged();}); } QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h index ce0ed18e..acaa92a4 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h @@ -40,6 +40,7 @@ class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeome public: enum MapBorderBehaviour { DrawOnce, + Duplicate, WrapAround }; @@ -49,7 +50,7 @@ public: void updateSourcePoints(const QGeoMap &map, const QList<QList<QDoubleVector2D>> &path, - MapBorderBehaviour wrapping = WrapAround); + MapBorderBehaviour wrapping = Duplicate); QPainterPath srcPath() const { return srcPath_; } qreal maxCoord() const { return maxCoord_; } @@ -108,15 +109,33 @@ public: m_geopathProjected.clear(); m_geopathProjected << QList<QDoubleVector2D>(); QList<QDoubleVector2D> &pP = m_geopathProjected.last(); - pP.reserve(m_poly.m_geopoly.perimeter().size()); - for (const QGeoCoordinate &c : m_poly.m_geopoly.perimeter()) - pP << p.geoToMapProjection(c); + if (m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe) { + const QList<QGeoCoordinate> realPath = QDeclarativeGeoMapItemUtils::greaterCirclePath(m_poly.m_geopoly.perimeter(), + QDeclarativeGeoMapItemUtils::ClosedPath); + pP.reserve(realPath.size()); + for (const QGeoCoordinate &c : realPath) + pP << p.geoToMapProjection(c); + } else { + pP.reserve(m_poly.m_geopoly.perimeter().size()); + const QList<QGeoCoordinate> perimeter = m_poly.m_geopoly.perimeter(); + for (const QGeoCoordinate &c : perimeter) + pP << p.geoToMapProjection(c); + } for (int i = 0; i < m_poly.m_geopoly.holesCount(); i++) { m_geopathProjected << QList<QDoubleVector2D>(); QList<QDoubleVector2D> &pH = m_geopathProjected.last(); - pH.reserve(m_poly.m_geopoly.holePath(i).size()); - for (const QGeoCoordinate &c : m_poly.m_geopoly.holePath(i)) - pH << p.geoToMapProjection(c); + if (m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe) { + const QList<QGeoCoordinate> realPath = QDeclarativeGeoMapItemUtils::greaterCirclePath(m_poly.m_geopoly.holePath(i), + QDeclarativeGeoMapItemUtils::ClosedPath); + pH.reserve(realPath.size()); + for (const QGeoCoordinate &c : realPath) + pH << p.geoToMapProjection(c); + } else { + pH.reserve(m_poly.m_geopoly.holePath(i).size()); + const QList<QGeoCoordinate> holePath = m_poly.m_geopoly.holePath(i); + for (const QGeoCoordinate &c : holePath) + pH << p.geoToMapProjection(c); + } } } void updateCache() @@ -125,7 +144,11 @@ public: return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); QList<QDoubleVector2D> &pP = m_geopathProjected.first(); - pP << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last()); + if (m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe && m_poly.m_geopoly.perimeter().size() > 1) { + regenerateCache(); //giving up here. Too difficult to take back all the interpolated points + } else { + pP << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last()); + } } void afterViewportChanged() override { diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp index 81c135a5..b0e88a8b 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp @@ -240,6 +240,19 @@ static QList<QList<QDoubleVector2D> > clipLine( \since 5.14 */ +/*! + \qmlproperty enum QtLocation::MapPolyline::referenceSurface + + This property determines the reference surface of the polyline. If it is set to + \l QLocation::ReferenceSurface::Map the polylines vertices are connected with straight + lines on the map. If it is set to \l QLocation::ReferenceSurface::Globe, the vertices + are connected following the great circle path, describing the shortest connection of + two points on a sphere. + Default value is \l QLocation::ReferenceSurface::Map. + + \since 6.5 +*/ + QDeclarativeMapLineProperties::QDeclarativeMapLineProperties(QObject *parent) : QObject(parent) { @@ -415,9 +428,17 @@ void QDeclarativePolylineMapItemPrivateCPU::regenerateCache() return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); m_geopathProjected.clear(); - m_geopathProjected.reserve(m_poly.m_geopath.path().size()); - for (const QGeoCoordinate &c : m_poly.m_geopath.path()) - m_geopathProjected << p.geoToMapProjection(c); + if (m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe) { + const QList<QGeoCoordinate> realPath = QDeclarativeGeoMapItemUtils::greaterCirclePath(m_poly.m_geopath.path()); + m_geopathProjected.reserve(realPath.size()); + for (const QGeoCoordinate &c : realPath) + m_geopathProjected << p.geoToMapProjection(c); + } else { + m_geopathProjected.reserve(m_poly.m_geopath.path().size()); + const QList<QGeoCoordinate> path = m_poly.m_geopath.path(); + for (const QGeoCoordinate &c : path) + m_geopathProjected << p.geoToMapProjection(c); + } } void QDeclarativePolylineMapItemPrivateCPU::updateCache() @@ -514,6 +535,8 @@ QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent) this, &QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged); QObject::connect(&m_line, &QDeclarativeMapLineProperties::widthChanged, this, &QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged); + QObject::connect(this, &QDeclarativePolylineMapItem::referenceSurfaceChanged, + [=]() {m_d->onGeoGeometryChanged();}); } QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem() diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp index b1c03fd6..c3c1ea23 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp @@ -3,7 +3,6 @@ #include "qdeclarativerectanglemapitem_p.h" #include "qdeclarativerectanglemapitem_p_p.h" -#include "qdeclarativepolygonmapitem_p.h" #include <QtCore/QScopedValueRollback> #include <QPainterPath> @@ -85,6 +84,19 @@ QT_BEGIN_NAMESPACE \since 5.14 */ +/*! + \qmlproperty enum QtLocation::MapRectangle::referenceSurface + + This property determines the reference surface of the rectangle. If it is set to + \l QLocation::ReferenceSurface::Map the edge points are connected with straight lines + on the map. If it is set to \l QLocation::ReferenceSurface::Globe, the edge points + are connected following the great circle path, describing the shortest connection of + two points on a sphere. + Default value is \l QLocation::ReferenceSurface::Map. + + \since 6.5 +*/ + QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) : QDeclarativeGeoMapItemBase(parent), m_border(this), m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this)) @@ -346,10 +358,18 @@ void QDeclarativeRectangleMapItemPrivateCPU::updatePolish() QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry); m_rect.m_updatingGeometry = true; - const QList<QGeoCoordinate> perimeter = QGeoMapItemGeometry::path(m_rect.m_rectangle); - const QList<QDoubleVector2D> pathMercator_ = QGeoMapItemGeometry::pathMercator(perimeter); - m_geometry.updateSourcePoints(*m_rect.map(), QList<QList<QDoubleVector2D>>{pathMercator_}); + QList<QGeoCoordinate> perimeter = QGeoMapItemGeometry::path(m_rect.m_rectangle); + + if (m_rect.referenceSurface() == QLocation::ReferenceSurface::Globe) { + perimeter = QDeclarativeGeoMapItemUtils::greaterCirclePath(perimeter, + QDeclarativeGeoMapItemUtils::ClosedPath); + } + + const QList<QDoubleVector2D> pathMercator = QGeoMapItemGeometry::pathMercator(perimeter); + m_geometry.updateSourcePoints(*m_rect.map(), QList<QList<QDoubleVector2D>>{pathMercator}, + m_rect.referenceSurface() == QLocation::ReferenceSurface::Globe ? QGeoMapPolygonGeometry::WrapAround : + QGeoMapPolygonGeometry::Duplicate); m_rect.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); const bool hasBorder = m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0; |