summaryrefslogtreecommitdiff
path: root/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2020-03-31 03:03:41 +0200
committerAlex Blasche <alexander.blasche@qt.io>2020-04-02 08:20:37 +0200
commitb06a07cf9fd474e11fbe467047e5fe0322b677f0 (patch)
treeba8b1dd84c3d263b27a1865fff84a659b5273091 /src/location/declarativemaps/qdeclarativecirclemapitem.cpp
parent4cfed13377ababcfaa7dacb055bcd3dd0f2cf7d4 (diff)
parent29816a3aaa3f368422a3b19983add62673bb6960 (diff)
downloadqtlocation-b06a07cf9fd474e11fbe467047e5fe0322b677f0.tar.gz
Merge 5.15 to dev and fix resulting compile issues
Conflicts: src/imports/location/location.cpp The change fixes the bare minimum of what needs to be done to compile and run. This includes the following issues: 1. Fix build failures as a result of QMetaType changes in qtbase moc now stores the QMetaType of properties as a result of 46f407126ef3e94d59254012cdc34d6a4ad2faf2 in qtbase, which requires full type information about the property type inside the moc generated source file. Many of the property types were forward-declared, and this resulted in build errors like: "invalid application of 'sizeof' to an incomplete type 'QDeclarativeGeoMap'" 2. Adopts QtQML API changes. A private QJSValue ctor was removed. The "replacement" is QJSValuePrivate::fromReturnedValue(..). 3. The mapboxgl 3rdparty backend does not compile at this point in time and seems unmaintained. For the time being, the mapboxgl backend is disabled in the interest of keeping qtlocation closer to dev HEAD of other Qt modules. Change-Id: I756e1c2effb29eaaf96a61a28c1c17338774b77c
Diffstat (limited to 'src/location/declarativemaps/qdeclarativecirclemapitem.cpp')
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem.cpp391
1 files changed, 182 insertions, 209 deletions
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
index 846fccbf..f0ba122c 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
@@ -57,6 +57,7 @@
#include <sweep/cdt.h>
#include <QtPositioning/private/qclipperutils_p.h>
+#include "qdeclarativecirclemapitem_p_p.h"
QT_BEGIN_NAMESPACE
@@ -274,83 +275,36 @@ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList<QDoubleVector2D
sourceBounds_ = screenBounds_;
}
-bool QDeclarativeCircleMapItem::crossEarthPole(const QGeoCoordinate &center, qreal distance)
+struct CircleBackendSelector
{
- qreal poleLat = 90;
- QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
- QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
- // approximate using great circle distance
- qreal distanceToNorthPole = center.distanceTo(northPole);
- qreal distanceToSouthPole = center.distanceTo(southPole);
- if (distanceToNorthPole < distance || distanceToSouthPole < distance)
- return true;
- return false;
-}
-
-void QDeclarativeCircleMapItem::calculatePeripheralPoints(QList<QGeoCoordinate> &path,
- const QGeoCoordinate &center,
- qreal distance,
- int steps,
- QGeoCoordinate &leftBound)
-{
- // Calculate points based on great-circle distance
- // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
- // but tweaked here for computing multiple points
-
- // pre-calculations
- steps = qMax(steps, 3);
- qreal centerLon = center.longitude();
- qreal minLon = centerLon;
- qreal latRad = QLocationUtils::radians(center.latitude());
- qreal lonRad = QLocationUtils::radians(centerLon);
- qreal cosLatRad = std::cos(latRad);
- qreal sinLatRad = std::sin(latRad);
- qreal ratio = (distance / QLocationUtils::earthMeanRadius());
- qreal cosRatio = std::cos(ratio);
- qreal sinRatio = std::sin(ratio);
- qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
- qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
- int idx = 0;
- for (int i = 0; i < steps; ++i) {
- qreal azimuthRad = 2 * M_PI * i / steps;
- qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
- + cosLatRad_x_sinRatio * std::cos(azimuthRad));
- qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
- cosRatio - sinLatRad * std::sin(resultLatRad));
- qreal lat2 = QLocationUtils::degrees(resultLatRad);
- qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
-
- path << QGeoCoordinate(lat2, lon2, center.altitude());
- // Consider only points in the left half of the circle for the left bound.
- if (azimuthRad > M_PI) {
- if (lon2 > centerLon) // if point and center are on different hemispheres
- lon2 -= 360;
- if (lon2 < minLon) {
- minLon = lon2;
- idx = i;
- }
- }
+ CircleBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeCircleMapItem::OpenGL : QDeclarativeCircleMapItem::Software;
}
- leftBound = path.at(idx);
-}
+ QDeclarativeCircleMapItem::Backend backend = QDeclarativeCircleMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(CircleBackendSelector, mapCircleBackendSelector)
QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true),
- updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+ m_updatingGeometry(false)
+ , m_d(new QDeclarativeCircleMapItemPrivateCPU(*this))
{
+ // ToDo: handle envvar, and switch implementation.
m_itemType = QGeoMap::MapCircle;
setFlag(ItemHasContents, true);
- QObject::connect(&border_, SIGNAL(colorChanged(QColor)),
- this, SLOT(markSourceDirtyAndUpdate()));
- QObject::connect(&border_, SIGNAL(widthChanged(qreal)),
- this, SLOT(markSourceDirtyAndUpdate()));
+ QObject::connect(&m_border, SIGNAL(colorChanged(QColor)),
+ this, SLOT(onLinePropertiesChanged()));
+ QObject::connect(&m_border, SIGNAL(widthChanged(qreal)),
+ this, SLOT(onLinePropertiesChanged()));
// assume that circles are not self-intersecting
// to speed up processing
// FIXME: unfortunately they self-intersect at the poles due to current drawing method
// so the line is commented out until fixed
//geometry_.setAssumeSimple(true);
-
+ setBackend(mapCircleBackendSelector->backend);
}
QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem()
@@ -371,23 +325,24 @@ QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem()
*/
QDeclarativeMapLineProperties *QDeclarativeCircleMapItem::border()
{
- return &border_;
+ return &m_border;
}
void QDeclarativeCircleMapItem::markSourceDirtyAndUpdate()
{
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
+}
+
+void QDeclarativeCircleMapItem::onLinePropertiesChanged()
+{
+ m_d->onLinePropertiesChanged();
}
void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
{
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
- if (!map)
- return;
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ if (map)
+ m_d->onMapSet();
}
/*!
@@ -399,18 +354,18 @@ void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *ma
*/
void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate &center)
{
- if (circle_.center() == center)
+ if (m_circle.center() == center)
return;
- circle_.setCenter(center);
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), center, m_circle.radius());
+ m_circle.setCenter(center);
+ m_d->onGeoGeometryChanged();
emit centerChanged(center);
}
QGeoCoordinate QDeclarativeCircleMapItem::center()
{
- return circle_.center();
+ return m_circle.center();
}
/*!
@@ -421,17 +376,17 @@ QGeoCoordinate QDeclarativeCircleMapItem::center()
*/
void QDeclarativeCircleMapItem::setColor(const QColor &color)
{
- if (color_ == color)
+ if (m_color == color)
return;
- color_ = color;
- dirtyMaterial_ = true;
+ m_color = color;
+ m_dirtyMaterial = true;
update();
- emit colorChanged(color_);
+ emit colorChanged(m_color);
}
QColor QDeclarativeCircleMapItem::color() const
{
- return color_;
+ return m_color;
}
/*!
@@ -443,18 +398,18 @@ QColor QDeclarativeCircleMapItem::color() const
*/
void QDeclarativeCircleMapItem::setRadius(qreal radius)
{
- if (circle_.radius() == radius)
+ if (m_circle.radius() == radius)
return;
- circle_.setRadius(radius);
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), m_circle.center(), radius);
+ m_circle.setRadius(radius);
+ m_d->onGeoGeometryChanged();
emit radiusChanged(radius);
}
qreal QDeclarativeCircleMapItem::radius() const
{
- return circle_.radius();
+ return m_circle.radius();
}
/*!
@@ -472,23 +427,7 @@ qreal QDeclarativeCircleMapItem::radius() const
*/
QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
-
- MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode);
-
- if (!node)
- node = new MapPolygonNode();
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) {
- node->update(color_, border_.color(), &geometry_, &borderGeometry_);
- geometry_.setPreserveGeometry(false);
- borderGeometry_.setPreserveGeometry(false);
- geometry_.markClean();
- borderGeometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
/*!
@@ -498,111 +437,41 @@ void QDeclarativeCircleMapItem::updatePolish()
{
if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- if (!circle_.isValid()) {
- geometry_.clear();
- borderGeometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- QList<QDoubleVector2D> circlePath = circlePath_;
-
- int pathCount = circlePath.size();
- bool preserve = preserveCircleGeometry(circlePath, circle_.center(), circle_.radius(), p);
- // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
- // to fix QTBUG-62154
- geometry_.setPreserveGeometry(true, leftBound_); // to set the geoLeftBound_
- geometry_.setPreserveGeometry(preserve, leftBound_);
-
- bool invertedCircle = false;
- if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath.size() == pathCount) {
- geometry_.updateScreenPointsInvert(circlePath, *map()); // invert fill area for really huge circles
- invertedCircle = true;
- } else {
- geometry_.updateSourcePoints(*map(), circlePath);
- geometry_.updateScreenPoints(*map(), border_.width());
- }
-
- borderGeometry_.clear();
- QList<QGeoMapItemGeometry *> geoms;
- geoms << &geometry_;
-
- if (border_.color() != Qt::transparent && border_.width() > 0) {
- QList<QDoubleVector2D> closedPath = circlePath;
- closedPath << closedPath.first();
-
- if (invertedCircle) {
- closedPath = circlePath_;
- closedPath << closedPath.first();
- std::reverse(closedPath.begin(), closedPath.end());
- }
-
- borderGeometry_.setPreserveGeometry(true, leftBound_);
- borderGeometry_.setPreserveGeometry(preserve, leftBound_);
-
- // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail.
- const QGeoCoordinate &geometryOrigin = geometry_.origin();
-
- borderGeometry_.srcPoints_.clear();
- borderGeometry_.srcPointTypes_.clear();
-
- QDoubleVector2D borderLeftBoundWrapped;
- QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped);
- if (clippedPaths.size()) {
- borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
- borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped);
- borderGeometry_.updateScreenPoints(*map(), border_.width());
- geoms << &borderGeometry_;
- } else {
- borderGeometry_.clear();
- }
- }
-
- QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
-
- if (invertedCircle || !preserve) {
- setWidth(combined.width());
- setHeight(combined.height());
- setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset());
- } else {
- setWidth(combined.width() + 2 * border_.width());
- setHeight(combined.height() + 2 * border_.width());
- }
-
- // No offsetting here, even in normal case, because first point offset is already translated
- setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset());
+ m_d->updatePolish();
}
/*!
\internal
+
+ The OpenGL backend doesn't do circles crossing poles yet.
+ So if that backend is selected and the circle crosses the poles, use the CPU backend instead.
*/
-void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
+void QDeclarativeCircleMapItem::possiblySwitchBackend(const QGeoCoordinate &oldCenter, qreal oldRadius, const QGeoCoordinate &newCenter, qreal newRadius)
{
- if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
+ if (m_backend != QDeclarativeCircleMapItem::OpenGL)
return;
- markSourceDirtyAndUpdate();
+ // if old does not cross and new crosses, move to CPU.
+ if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius)
+ && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) {
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this)));
+ m_d.swap(d);
+ } else if (QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius)
+ && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) { // else if old crosses and new does not cross, move back to OpenGL
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ }
}
/*!
\internal
*/
-void QDeclarativeCircleMapItem::updateCirclePath()
+void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ if (event.mapSize.isEmpty())
return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- QList<QGeoCoordinate> path;
- calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples, leftBound_);
- circlePath_.clear();
- for (const QGeoCoordinate &c : path)
- circlePath_ << p.geoToMapProjection(c);
+ m_d->afterViewportChanged();
}
/*!
@@ -610,30 +479,69 @@ void QDeclarativeCircleMapItem::updateCirclePath()
*/
bool QDeclarativeCircleMapItem::contains(const QPointF &point) const
{
- return (geometry_.contains(point) || borderGeometry_.contains(point));
+ return m_d->contains(point);
+ //
}
const QGeoShape &QDeclarativeCircleMapItem::geoShape() const
{
- return circle_;
+ return m_circle;
}
void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape)
{
- if (shape == circle_)
+ if (shape == m_circle)
return;
const QGeoCircle circle(shape); // if shape isn't a circle, circle will be created as a default-constructed circle
- const bool centerHasChanged = circle.center() != circle_.center();
- const bool radiusHasChanged = circle.radius() != circle_.radius();
- circle_ = circle;
+ const bool centerHasChanged = circle.center() != m_circle.center();
+ const bool radiusHasChanged = circle.radius() != m_circle.radius();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), circle.center(), circle.radius());
+ m_circle = circle;
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
if (centerHasChanged)
- emit centerChanged(circle_.center());
+ emit centerChanged(m_circle.center());
if (radiusHasChanged)
- emit radiusChanged(circle_.radius());
+ emit radiusChanged(m_circle.radius());
+}
+
+/*!
+ \qmlproperty MapCircle.Backend QtLocation::MapCircle::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapCircle.Software and \b{MapCircle.OpenGL}.
+ The default value is \b{MapCircle.Software}.
+
+ \note \b{The release of this API with Qt 5.15 is a Technology Preview}.
+ Ideally, as the OpenGL backends for map items mature, there will be
+ no more need to also offer the legacy software-projection backend.
+ So this property will likely disappear at some later point.
+ To select OpenGL-accelerated item backends without using this property,
+ it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS
+ to \b{1}.
+ Also note that all current OpenGL backends won't work as expected when enabling
+ layers on the individual item, or when running on OpenGL core profiles greater than 2.x.
+
+ \since 5.15
+*/
+
+QDeclarativeCircleMapItem::Backend QDeclarativeCircleMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativeCircleMapItem::setBackend(QDeclarativeCircleMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this))
+ : static_cast<QDeclarativeCircleMapItemPrivate * >(new QDeclarativeCircleMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
}
/*!
@@ -641,21 +549,27 @@ void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape)
*/
void QDeclarativeCircleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !circle_.isValid() || updatingGeometry_ || newGeometry == oldGeometry) {
+ if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
- QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) / 2;
+ QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5;
QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false);
if (newCoordinate.isValid())
- setCenter(newCoordinate);
+ setCenter(newCoordinate); // ToDo: this is incorrect. setting such center might yield to another geometry changed.
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
// call to this function.
}
-bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &path,
+QDeclarativeCircleMapItemPrivate::~QDeclarativeCircleMapItemPrivate() {}
+
+QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {}
+
+QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {}
+
+bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &path,
const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p)
{
// if circle crosses north/south pole, then don't preserve circular shape,
@@ -664,7 +578,6 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &
return false;
}
return true;
-
}
/*
@@ -684,7 +597,7 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &
* | ____ |
* \__/ \__/
*/
-void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector2D> &path,
+void QDeclarativeCircleMapItemPrivate::updateCirclePathForRendering(QList<QDoubleVector2D> &path,
const QGeoCoordinate &center,
qreal distance, const QGeoProjectionWebMercator &p)
{
@@ -743,6 +656,66 @@ void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector
}
}
+bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate &center, qreal distance)
+{
+ qreal poleLat = 90;
+ QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
+ QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
+ // approximate using great circle distance
+ qreal distanceToNorthPole = center.distanceTo(northPole);
+ qreal distanceToSouthPole = center.distanceTo(southPole);
+ if (distanceToNorthPole < distance || distanceToSouthPole < distance)
+ return true;
+ return false;
+}
+
+void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoordinate> &path,
+ const QGeoCoordinate &center,
+ qreal distance,
+ int steps,
+ QGeoCoordinate &leftBound)
+{
+ // Calculate points based on great-circle distance
+ // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
+ // but tweaked here for computing multiple points
+
+ // pre-calculations
+ steps = qMax(steps, 3);
+ qreal centerLon = center.longitude();
+ qreal minLon = centerLon;
+ qreal latRad = QLocationUtils::radians(center.latitude());
+ qreal lonRad = QLocationUtils::radians(centerLon);
+ qreal cosLatRad = std::cos(latRad);
+ qreal sinLatRad = std::sin(latRad);
+ qreal ratio = (distance / QLocationUtils::earthMeanRadius());
+ qreal cosRatio = std::cos(ratio);
+ qreal sinRatio = std::sin(ratio);
+ qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
+ qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
+ int idx = 0;
+ for (int i = 0; i < steps; ++i) {
+ qreal azimuthRad = 2 * M_PI * i / steps;
+ qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
+ + cosLatRad_x_sinRatio * std::cos(azimuthRad));
+ qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
+ cosRatio - sinLatRad * std::sin(resultLatRad));
+ qreal lat2 = QLocationUtils::degrees(resultLatRad);
+ qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
+
+ path << QGeoCoordinate(lat2, lon2, center.altitude());
+ // Consider only points in the left half of the circle for the left bound.
+ if (azimuthRad > M_PI) {
+ if (lon2 > centerLon) // if point and center are on different hemispheres
+ lon2 -= 360;
+ if (lon2 < minLon) {
+ minLon = lon2;
+ idx = i;
+ }
+ }
+ }
+ leftBound = path.at(idx);
+}
+
//////////////////////////////////////////////////////////////////////
QT_END_NAMESPACE