diff options
author | Paolo Angelelli <paolo.angelelli@qt.io> | 2016-08-05 22:21:12 +0200 |
---|---|---|
committer | Paolo Angelelli <paolo.angelelli@qt.io> | 2016-11-30 08:54:11 +0000 |
commit | cd8880cc04b22610e835e6cc02cc16ac22ec9fae (patch) | |
tree | 37a5569c4eca55f70d5584f54fa2479dcf9dad3c /src/positioning | |
parent | 0a552f285fdc3760d9755109ceef3b87392a308d (diff) | |
download | qtlocation-cd8880cc04b22610e835e6cc02cc16ac22ec9fae.tar.gz |
Add support for boundingGeoRectangle to QGeoShape
this patch introduces QGeoShape::boundingGeoRectangle,
which returns a QGeoRectangle containing the latitudinal/longitudinal bounds
of a geoshape.
The bounding geo rectangle is projection independent, as it returns
a georectangle containing the min/max latitudes/longitudes of the
shape.
Change-Id: Ie3a83ec41f87ea3753899d2278e664fe2469f778
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'src/positioning')
-rw-r--r-- | src/positioning/qgeocircle.cpp | 125 | ||||
-rw-r--r-- | src/positioning/qgeocircle.h | 4 | ||||
-rw-r--r-- | src/positioning/qgeocircle_p.h | 11 | ||||
-rw-r--r-- | src/positioning/qgeorectangle.cpp | 12 | ||||
-rw-r--r-- | src/positioning/qgeorectangle.h | 86 | ||||
-rw-r--r-- | src/positioning/qgeorectangle_p.h | 2 | ||||
-rw-r--r-- | src/positioning/qgeoshape.cpp | 16 | ||||
-rw-r--r-- | src/positioning/qgeoshape.h | 89 | ||||
-rw-r--r-- | src/positioning/qgeoshape_p.h | 3 | ||||
-rw-r--r-- | src/positioning/qlocationutils_p.h | 49 |
10 files changed, 288 insertions, 109 deletions
diff --git a/src/positioning/qgeocircle.cpp b/src/positioning/qgeocircle.cpp index 3f2707a4..9552ac37 100644 --- a/src/positioning/qgeocircle.cpp +++ b/src/positioning/qgeocircle.cpp @@ -42,9 +42,11 @@ #include "qgeocoordinate.h" #include "qnumeric.h" +#include "qlocationutils_p.h" #include "qdoublevector2d_p.h" #include "qdoublevector3d_p.h" +#include <cmath> QT_BEGIN_NAMESPACE /*! @@ -191,12 +193,12 @@ bool QGeoCircle::operator!=(const QGeoCircle &other) const bool QGeoCirclePrivate::isValid() const { - return m_center.isValid() && !qIsNaN(radius) && radius >= -1e-7; + return m_center.isValid() && !qIsNaN(m_radius) && m_radius >= -1e-7; } bool QGeoCirclePrivate::isEmpty() const { - return !isValid() || radius <= 1e-7; + return !isValid() || m_radius <= 1e-7; } /*! @@ -206,7 +208,7 @@ void QGeoCircle::setCenter(const QGeoCoordinate ¢er) { Q_D(QGeoCircle); - d->m_center = center; + d->setCenter(center); } /*! @@ -226,7 +228,7 @@ void QGeoCircle::setRadius(qreal radius) { Q_D(QGeoCircle); - d->radius = radius; + d->setRadius(radius); } /*! @@ -236,7 +238,7 @@ qreal QGeoCircle::radius() const { Q_D(const QGeoCircle); - return d->radius; + return d->m_radius; } bool QGeoCirclePrivate::contains(const QGeoCoordinate &coordinate) const @@ -246,7 +248,7 @@ bool QGeoCirclePrivate::contains(const QGeoCoordinate &coordinate) const // see QTBUG-41447 for details qreal distance = m_center.distanceTo(coordinate); - if (qFuzzyCompare(distance, radius) || distance <= radius) + if (qFuzzyCompare(distance, m_radius) || distance <= m_radius) return true; return false; @@ -257,6 +259,103 @@ QGeoCoordinate QGeoCirclePrivate::center() const return m_center; } +QGeoRectangle QGeoCirclePrivate::boundingGeoRectangle() const +{ + return m_bbox; +} + +void QGeoCirclePrivate::updateBoundingBox() +{ + if (isEmpty()) { + if (m_center.isValid()) { + m_bbox.setTopLeft(m_center); + m_bbox.setBottomRight(m_center); + } + return; + } + + bool crossNorth = crossNorthPole(); + bool crossSouth = crossSouthPole(); + + if (crossNorth && crossSouth) { + // Circle crossing both poles fills the whole map + m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(-90.0, 180.0)); + } else if (crossNorth) { + // Circle crossing one pole fills the map in the longitudinal direction + m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 180.0).latitude(), 180.0)); + } else if (crossSouth) { + m_bbox = QGeoRectangle(QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 0.0).latitude(), -180.0), QGeoCoordinate(-90, 180.0)); + } else { + // Regular circle not crossing anything + + // Calculate geo bounding box of the circle + // + // A circle tangential point with a meridian, together with pole and + // the circle center create a spherical triangle. + // Finding the tangential point with the spherical law of sines: + // + // * lon_delta_in_rad : delta between the circle center and a tangential + // point (absolute value). + // * r_in_rad : angular radius of the circle + // * lat_in_rad : latitude of the circle center + // * alpha_in_rad : angle between meridian and radius of the circle. + // At the tangential point, sin(alpha_in_rad) == 1. + // * lat_delta_in_rad - absolute delta of latitudes between the circle center and + // any of the two points where the great circle going through the circle + // center and the pole crosses the circle. In other words, the points + // on the circle with azimuth 0 or 180. + // + // Using: + // sin(lon_delta_in_rad)/sin(r_in_rad) = sin(alpha_in_rad)/sin(pi/2 - lat_in_rad) + + double r_in_rad = m_radius / QLocationUtils::earthMeanRadius(); // angular r + double lat_delta_in_deg = QLocationUtils::degrees(r_in_rad); + double lon_delta_in_deg = QLocationUtils::degrees(std::asin( + std::sin(r_in_rad) / + std::cos(QLocationUtils::radians(m_center.latitude())) + )); + + QGeoCoordinate topLeft; + topLeft.setLatitude(m_center.latitude() + lat_delta_in_deg); + topLeft.setLongitude(m_center.longitude() - lon_delta_in_deg); + QGeoCoordinate bottomRight; + bottomRight.setLatitude(m_center.latitude() - lat_delta_in_deg); + bottomRight.setLongitude(m_center.longitude() + lon_delta_in_deg); + + m_bbox = QGeoRectangle(topLeft, bottomRight); + } +} + +void QGeoCirclePrivate::setCenter(const QGeoCoordinate &c) +{ + m_center = c; + updateBoundingBox(); +} + +void QGeoCirclePrivate::setRadius(const qreal r) +{ + m_radius = r; + updateBoundingBox(); +} + +bool QGeoCirclePrivate::crossNorthPole() const +{ + const QGeoCoordinate northPole(90.0, m_center.longitude()); + qreal distanceToPole = m_center.distanceTo(northPole); + if (distanceToPole < m_radius) + return true; + return false; +} + +bool QGeoCirclePrivate::crossSouthPole() const +{ + const QGeoCoordinate southPole(-90.0, m_center.longitude()); + qreal distanceToPole = m_center.distanceTo(southPole); + if (distanceToPole < m_radius) + return true; + return false; +} + /*! Extends the circle to include \a coordinate */ @@ -265,7 +364,7 @@ void QGeoCirclePrivate::extendShape(const QGeoCoordinate &coordinate) if (!isValid() || !coordinate.isValid() || contains(coordinate)) return; - radius = m_center.distanceTo(coordinate); + setRadius(m_center.distanceTo(coordinate)); } /*! @@ -307,7 +406,7 @@ void QGeoCircle::translate(double degreesLatitude, double degreesLongitude) lon -= 180; } - d->m_center = QGeoCoordinate(lat, lon); + d->setCenter(QGeoCoordinate(lat, lon)); } /*! @@ -349,18 +448,19 @@ QString QGeoCircle::toString() const *******************************************************************************/ QGeoCirclePrivate::QGeoCirclePrivate() -: QGeoShapePrivate(QGeoShape::CircleType), radius(-1.0) +: QGeoShapePrivate(QGeoShape::CircleType), m_radius(-1.0) { } QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCoordinate ¢er, qreal radius) -: QGeoShapePrivate(QGeoShape::CircleType), m_center(center), radius(radius) +: QGeoShapePrivate(QGeoShape::CircleType), m_center(center), m_radius(radius) { + updateBoundingBox(); } QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCirclePrivate &other) : QGeoShapePrivate(QGeoShape::CircleType), m_center(other.m_center), - radius(other.radius) + m_radius(other.m_radius), m_bbox(other.m_bbox) { } @@ -378,8 +478,7 @@ bool QGeoCirclePrivate::operator==(const QGeoShapePrivate &other) const const QGeoCirclePrivate &otherCircle = static_cast<const QGeoCirclePrivate &>(other); - return radius == otherCircle.radius && m_center == otherCircle.m_center; + return m_radius == otherCircle.m_radius && m_center == otherCircle.m_center; } QT_END_NAMESPACE - diff --git a/src/positioning/qgeocircle.h b/src/positioning/qgeocircle.h index b4b6c45e..e8632fe9 100644 --- a/src/positioning/qgeocircle.h +++ b/src/positioning/qgeocircle.h @@ -75,8 +75,8 @@ public: void setRadius(qreal radius); qreal radius() const; - void translate(double degreesLatitude, double degreesLongitude); - QGeoCircle translated(double degreesLatitude, double degreesLongitude) const; + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoCircle translated(double degreesLatitude, double degreesLongitude) const; Q_INVOKABLE QString toString() const; diff --git a/src/positioning/qgeocircle_p.h b/src/positioning/qgeocircle_p.h index 311aba8b..07d79db4 100644 --- a/src/positioning/qgeocircle_p.h +++ b/src/positioning/qgeocircle_p.h @@ -70,6 +70,14 @@ public: QGeoCoordinate center() const Q_DECL_OVERRIDE; + QGeoRectangle boundingGeoRectangle() const Q_DECL_OVERRIDE; + + bool crossNorthPole() const; + bool crossSouthPole() const; + void updateBoundingBox(); + void setCenter(const QGeoCoordinate &c); + void setRadius(const qreal r); + void extendShape(const QGeoCoordinate &coordinate) Q_DECL_OVERRIDE; QGeoShapePrivate *clone() const Q_DECL_OVERRIDE; @@ -77,7 +85,8 @@ public: bool operator==(const QGeoShapePrivate &other) const Q_DECL_OVERRIDE; QGeoCoordinate m_center; - qreal radius; + qreal m_radius; + QGeoRectangle m_bbox; }; QT_END_NAMESPACE diff --git a/src/positioning/qgeorectangle.cpp b/src/positioning/qgeorectangle.cpp index bbcafd6b..a8d74414 100644 --- a/src/positioning/qgeorectangle.cpp +++ b/src/positioning/qgeorectangle.cpp @@ -40,6 +40,8 @@ #include "qgeorectangle.h" #include "qgeorectangle_p.h" +#include "qgeoprojection_p.h" +#include "qdoublevector2d_p.h" #include "qgeocoordinate.h" #include "qnumeric.h" #include <QList> @@ -614,10 +616,7 @@ double QGeoRectangle::height() const Q_D(const QGeoRectangle); - double result = d->topLeft.latitude() - d->bottomRight.latitude(); - if (result < 0.0) - result = qQNaN(); - return result; + return d->topLeft.latitude() - d->bottomRight.latitude(); } bool QGeoRectanglePrivate::contains(const QGeoCoordinate &coordinate) const @@ -674,6 +673,11 @@ QGeoCoordinate QGeoRectanglePrivate::center() const return QGeoCoordinate(cLat, cLon); } +QGeoRectangle QGeoRectanglePrivate::boundingGeoRectangle() const +{ + return QGeoRectangle(topLeft, bottomRight); +} + /*! Returns whether the geo rectangle \a rectangle is contained within this geo rectangle. diff --git a/src/positioning/qgeorectangle.h b/src/positioning/qgeorectangle.h index 7e921730..9e9acf91 100644 --- a/src/positioning/qgeorectangle.h +++ b/src/positioning/qgeorectangle.h @@ -42,89 +42,7 @@ #include <QtPositioning/QGeoShape> -QT_BEGIN_NAMESPACE - -class QGeoCoordinate; -class QGeoRectanglePrivate; - -class Q_POSITIONING_EXPORT QGeoRectangle : public QGeoShape -{ - Q_GADGET - Q_PROPERTY(QGeoCoordinate bottomLeft READ bottomLeft WRITE setBottomLeft) - Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight) - Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft) - Q_PROPERTY(QGeoCoordinate topRight READ topRight WRITE setTopRight) - Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter) - Q_PROPERTY(double height READ height WRITE setHeight) - Q_PROPERTY(double width READ width WRITE setWidth) - -public: - QGeoRectangle(); - QGeoRectangle(const QGeoCoordinate ¢er, double degreesWidth, double degreesHeight); - QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight); - QGeoRectangle(const QList<QGeoCoordinate> &coordinates); - QGeoRectangle(const QGeoRectangle &other); - QGeoRectangle(const QGeoShape &other); - - ~QGeoRectangle(); - - QGeoRectangle &operator=(const QGeoRectangle &other); - - using QGeoShape::operator==; - bool operator==(const QGeoRectangle &other) const; - - using QGeoShape::operator!=; - bool operator!=(const QGeoRectangle &other) const; - - void setTopLeft(const QGeoCoordinate &topLeft); - QGeoCoordinate topLeft() const; - - void setTopRight(const QGeoCoordinate &topRight); - QGeoCoordinate topRight() const; - - void setBottomLeft(const QGeoCoordinate &bottomLeft); - QGeoCoordinate bottomLeft() const; - - void setBottomRight(const QGeoCoordinate &bottomRight); - QGeoCoordinate bottomRight() const; - - void setCenter(const QGeoCoordinate ¢er); - QGeoCoordinate center() const; - - void setWidth(double degreesWidth); - double width() const; - - void setHeight(double degreesHeight); - double height() const; - - using QGeoShape::contains; - bool contains(const QGeoRectangle &rectangle) const; - bool intersects(const QGeoRectangle &rectangle) const; - - void translate(double degreesLatitude, double degreesLongitude); - QGeoRectangle translated(double degreesLatitude, double degreesLongitude) const; - - QGeoRectangle united(const QGeoRectangle &rectangle) const; - QGeoRectangle operator|(const QGeoRectangle &rectangle) const; - QGeoRectangle &operator|=(const QGeoRectangle &rectangle); - - Q_INVOKABLE QString toString() const; - -private: - inline QGeoRectanglePrivate *d_func(); - inline const QGeoRectanglePrivate *d_func() const; -}; - -Q_DECLARE_TYPEINFO(QGeoRectangle, Q_MOVABLE_TYPE); - -inline QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const -{ - return united(rectangle); -} - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QGeoRectangle) +// QGeoRectangle declaration now in qgeoshape.h. +// This file remains for compatibility. #endif - diff --git a/src/positioning/qgeorectangle_p.h b/src/positioning/qgeorectangle_p.h index 188e5548..ee1b1732 100644 --- a/src/positioning/qgeorectangle_p.h +++ b/src/positioning/qgeorectangle_p.h @@ -70,6 +70,8 @@ public: QGeoCoordinate center() const Q_DECL_OVERRIDE; + QGeoRectangle boundingGeoRectangle() const Q_DECL_OVERRIDE; + void extendShape(const QGeoCoordinate &coordinate) Q_DECL_OVERRIDE; QGeoShapePrivate *clone() const Q_DECL_OVERRIDE; diff --git a/src/positioning/qgeoshape.cpp b/src/positioning/qgeoshape.cpp index bac37605..dd358fb5 100644 --- a/src/positioning/qgeoshape.cpp +++ b/src/positioning/qgeoshape.cpp @@ -227,6 +227,22 @@ bool QGeoShape::contains(const QGeoCoordinate &coordinate) const } /*! + Returns a \a QGeoRectangle representing the geographical bounding rectangle of the + geo shape, that defines the latitudinal/longitudinal bounds of the geo shape. + + \since 5.9 +*/ +QGeoRectangle QGeoShape::boundingGeoRectangle() const +{ + Q_D(const QGeoShape); + + if (d) + return d->boundingGeoRectangle(); + else + return QGeoRectangle(); +} + +/*! Returns the coordinate located at the geometric center of the geo shape. \since 5.5 diff --git a/src/positioning/qgeoshape.h b/src/positioning/qgeoshape.h index f8a30358..58e17c7d 100644 --- a/src/positioning/qgeoshape.h +++ b/src/positioning/qgeoshape.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE class QDebug; class QGeoShapePrivate; +class QGeoRectangle; class Q_POSITIONING_EXPORT QGeoShape { @@ -72,10 +73,10 @@ public: bool isValid() const; bool isEmpty() const; Q_INVOKABLE bool contains(const QGeoCoordinate &coordinate) const; + Q_INVOKABLE QGeoRectangle boundingGeoRectangle() const; + Q_INVOKABLE QGeoCoordinate center() const; - QGeoCoordinate center() const; - - void extendShape(const QGeoCoordinate &coordinate); + Q_INVOKABLE void extendShape(const QGeoCoordinate &coordinate); bool operator==(const QGeoShape &other) const; bool operator!=(const QGeoShape &other) const; @@ -104,9 +105,89 @@ Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoShap Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoShape &shape); #endif + + +// QGeoRectangle is declared here because of QGeoShape::boundingGeoRectangle +class QGeoRectanglePrivate; + +class Q_POSITIONING_EXPORT QGeoRectangle : public QGeoShape +{ + Q_GADGET + Q_PROPERTY(QGeoCoordinate bottomLeft READ bottomLeft WRITE setBottomLeft) + Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight) + Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft) + Q_PROPERTY(QGeoCoordinate topRight READ topRight WRITE setTopRight) + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter) + Q_PROPERTY(double height READ height WRITE setHeight) + Q_PROPERTY(double width READ width WRITE setWidth) + +public: + QGeoRectangle(); + QGeoRectangle(const QGeoCoordinate ¢er, double degreesWidth, double degreesHeight); + QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight); + QGeoRectangle(const QList<QGeoCoordinate> &coordinates); + QGeoRectangle(const QGeoRectangle &other); + QGeoRectangle(const QGeoShape &other); + + ~QGeoRectangle(); + + QGeoRectangle &operator=(const QGeoRectangle &other); + + using QGeoShape::operator==; + bool operator==(const QGeoRectangle &other) const; + + using QGeoShape::operator!=; + bool operator!=(const QGeoRectangle &other) const; + + void setTopLeft(const QGeoCoordinate &topLeft); + QGeoCoordinate topLeft() const; + + void setTopRight(const QGeoCoordinate &topRight); + QGeoCoordinate topRight() const; + + void setBottomLeft(const QGeoCoordinate &bottomLeft); + QGeoCoordinate bottomLeft() const; + + void setBottomRight(const QGeoCoordinate &bottomRight); + QGeoCoordinate bottomRight() const; + + void setCenter(const QGeoCoordinate ¢er); + QGeoCoordinate center() const; + + void setWidth(double degreesWidth); + double width() const; + + void setHeight(double degreesHeight); + double height() const; + + using QGeoShape::contains; + bool contains(const QGeoRectangle &rectangle) const; + Q_INVOKABLE bool intersects(const QGeoRectangle &rectangle) const; + + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoRectangle translated(double degreesLatitude, double degreesLongitude) const; + + Q_INVOKABLE QGeoRectangle united(const QGeoRectangle &rectangle) const; + QGeoRectangle operator|(const QGeoRectangle &rectangle) const; + QGeoRectangle &operator|=(const QGeoRectangle &rectangle); + + Q_INVOKABLE QString toString() const; + +private: + inline QGeoRectanglePrivate *d_func(); + inline const QGeoRectanglePrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoRectangle, Q_MOVABLE_TYPE); + +inline QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const +{ + return united(rectangle); +} + QT_END_NAMESPACE Q_DECLARE_METATYPE(QGeoShape) +Q_DECLARE_METATYPE(QGeoRectangle) #endif - diff --git a/src/positioning/qgeoshape_p.h b/src/positioning/qgeoshape_p.h index 275886ff..251e872c 100644 --- a/src/positioning/qgeoshape_p.h +++ b/src/positioning/qgeoshape_p.h @@ -69,6 +69,8 @@ public: virtual QGeoCoordinate center() const = 0; + virtual QGeoRectangle boundingGeoRectangle() const = 0; + virtual void extendShape(const QGeoCoordinate &coordinate) = 0; virtual QGeoShapePrivate *clone() const = 0; @@ -89,4 +91,3 @@ Q_INLINE_TEMPLATE QGeoShapePrivate *QSharedDataPointer<QGeoShapePrivate>::clone( QT_END_NAMESPACE #endif - diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h index 00c4d3e3..32addb63 100644 --- a/src/positioning/qlocationutils_p.h +++ b/src/positioning/qlocationutils_p.h @@ -53,6 +53,14 @@ #include <QtCore/QtGlobal> #include <math.h> +#ifndef M_1_180 +#define M_1_180 0.0055555555555555555555555555555555555555556 +#endif + +#ifndef M_1_PI +#define M_1_PI 0.31830988618379067154 +#endif + QT_BEGIN_NAMESPACE class QTime; class QByteArray; @@ -176,6 +184,47 @@ public: return CardinalNNW; } + // For values exceeding +- 720.0 + inline static double wrapLongExt(double lng) { + double remainder = fmod(lng + 180.0, 360.0); + return fmod(remainder + 360.0, 360.0) - 180.0; + } + + // Mirrors the azimuth against the X axis. Azimuth assumed to be in [0,360[ + inline static double mirrorAzimuthX(double azimuth) { + if (azimuth <= 90.0) + return 180.0 - azimuth; + else + return 180.0 + (360.0 - azimuth); + } + + // Mirrors the azimuth against the Y axis. Azimuth assumed to be in [0,360[ + inline static double mirrorAzimuthY(double azimuth) { + if (azimuth == 0.0) + return 0.0; + return 360.0 - azimuth; + } + + inline static double radians(double degrees) + { + return degrees * M_PI * M_1_180; + } + + inline static double degrees(double radians) + { + return radians * 180.0 * M_1_PI; + } + + inline static double earthMeanRadius() + { + return 6371007.2; + } + + inline static double mercatorMaxLatitude() + { + return 85.05113; + } + /* Creates a QGeoPositionInfo from a GGA, GLL, RMC, VTG or ZDA sentence. |