| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtPositioning module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qgeorectangle.h" |
| #include "qgeorectangle_p.h" |
| |
| #include "qwebmercator_p.h" |
| #include "qdoublevector2d_p.h" |
| #include "qgeocoordinate.h" |
| #include "qnumeric.h" |
| #include "qlocationutils_p.h" |
| #include <QList> |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class QGeoRectangle |
| \inmodule QtPositioning |
| \ingroup QtPositioning-positioning |
| \since 5.2 |
| |
| \brief The QGeoRectangle class defines a rectangular geographic area. |
| |
| The rectangle is defined in terms of a QGeoCoordinate which specifies the |
| top left coordinate of the rectangle and a QGeoCoordinate which specifies |
| the bottom right coordinate of the rectangle. |
| |
| A geo rectangle is considered invalid if the top left or bottom right |
| coordinates are invalid or if the top left coordinate is south of the |
| bottom right coordinate. |
| |
| Geo rectangles can never cross the poles. |
| |
| Several methods behave as though the geo rectangle is defined in terms of a |
| center coordinate, the width of the geo rectangle in degrees and the height |
| of the geo rectangle in degrees. |
| |
| If the height or center of a geo rectangle is adjusted such that it would |
| cross one of the poles the height is modified such that the geo rectangle |
| touches but does not cross the pole and that the center coordinate is still |
| in the center of the geo rectangle. |
| |
| This class is a \l Q_GADGET since Qt 5.5. It can be |
| \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. |
| */ |
| |
| /*! |
| \property QGeoRectangle::bottomLeft |
| \brief This property holds the bottom left coorindate of this geo rectangle. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::bottomRight |
| \brief This property holds the bottom right coordinate of this geo rectangle. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::topLeft |
| \brief This property holds the top left coordinate of this geo rectangle. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::topRight |
| \brief This property holds the top right coordinate of this geo rectangle. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::center |
| \brief This property holds the center of this geo rectangle. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \sa QGeoShape::center |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::width |
| \brief This property holds the width of this geo rectangle in degrees. |
| |
| The property value is undefined if this geo rectangle is invalid. |
| |
| If the new width is less than 0.0 or if this geo rectangle is invalid, this |
| function does nothing. To set up the values of an invalid |
| geo rectangle based on the center, width, and height, you should use |
| \l setCenter() first to make the geo rectangle valid. |
| |
| 360.0 is the width used only if the new width is equal or greater than 360. |
| In such cases the leftmost longitude of the geo rectangle is set to -180.0 |
| degrees and the rightmost longitude of the geo rectangle is set to 180.0 |
| degrees. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| /*! |
| \property QGeoRectangle::height |
| \brief This property holds the height of this geo rectangle in degrees. |
| |
| The property value is undefined if this geo rectangle is invalid. |
| |
| If the new height is less than 0.0 or if this geo rectangle is invalid, |
| the property is not changed. To set up the values of an invalid |
| geo rectangle based on the center, width, and height, you should use |
| \l setCenter() first to make the geo rectangle valid. |
| |
| If the change in height would cause the geo rectangle to cross a pole, |
| the height is adjusted such that the geo rectangle only touches the pole. |
| |
| This change is done such that the center coordinate is still at the |
| center of the geo rectangle, which may result in a geo rectangle with |
| a smaller height than expected. |
| |
| 180.0 is the height used only if the new height is greater or equal than 180. |
| |
| While this property is introduced in Qt 5.5, the related accessor functions |
| exist since the first version of this class. |
| |
| \since 5.5 |
| */ |
| |
| inline QGeoRectanglePrivate *QGeoRectangle::d_func() |
| { |
| return static_cast<QGeoRectanglePrivate *>(d_ptr.data()); |
| } |
| |
| inline const QGeoRectanglePrivate *QGeoRectangle::d_func() const |
| { |
| return static_cast<const QGeoRectanglePrivate *>(d_ptr.constData()); |
| } |
| |
| struct RectangleVariantConversions |
| { |
| RectangleVariantConversions() |
| { |
| QMetaType::registerConverter<QGeoRectangle, QGeoShape>(); |
| QMetaType::registerConverter<QGeoShape, QGeoRectangle>(); |
| } |
| }; |
| |
| |
| Q_GLOBAL_STATIC(RectangleVariantConversions, initRectangleConversions) |
| |
| /*! |
| Constructs a new, invalid geo rectangle. |
| */ |
| QGeoRectangle::QGeoRectangle() |
| : QGeoShape(new QGeoRectanglePrivate) |
| { |
| initRectangleConversions(); |
| } |
| |
| /*! |
| Constructs a new geo rectangle centered at \a center with a |
| width in degrees of \a degreesWidth and a height in degrees of \a degreesHeight. |
| |
| If \a degreesHeight would take the geo rectangle beyond one of the poles, |
| the height of the geo rectangle will be truncated such that the geo rectangle |
| only extends up to the pole. The center of the geo rectangle will be |
| unchanged, and the height will be adjusted such that the center point is at |
| the center of the truncated geo rectangle. |
| */ |
| QGeoRectangle::QGeoRectangle(const QGeoCoordinate ¢er, double degreesWidth, double degreesHeight) |
| { |
| initRectangleConversions(); |
| d_ptr = new QGeoRectanglePrivate(center, center); |
| setWidth(degreesWidth); |
| setHeight(degreesHeight); |
| } |
| |
| /*! |
| Constructs a new geo rectangle with a top left coordinate \a topLeft and a bottom right |
| coordinate \a bottomRight. |
| */ |
| QGeoRectangle::QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight) |
| { |
| initRectangleConversions(); |
| d_ptr = new QGeoRectanglePrivate(topLeft, bottomRight); |
| } |
| |
| /*! |
| Constructs a new geo rectangle, of minimum size, containing all of the \a coordinates. |
| */ |
| QGeoRectangle::QGeoRectangle(const QList<QGeoCoordinate> &coordinates) |
| { |
| initRectangleConversions(); |
| if (coordinates.isEmpty()) { |
| d_ptr = new QGeoRectanglePrivate; |
| } else { |
| const QGeoCoordinate &startCoordinate = coordinates.first(); |
| d_ptr = new QGeoRectanglePrivate(startCoordinate, startCoordinate); |
| |
| foreach (const QGeoCoordinate &coordinate, coordinates) { |
| d_ptr->extendShape(coordinate); |
| } |
| } |
| } |
| |
| /*! |
| Constructs a geo rectangle from the contents of \a other. |
| */ |
| QGeoRectangle::QGeoRectangle(const QGeoRectangle &other) |
| : QGeoShape(other) |
| { |
| initRectangleConversions(); |
| } |
| |
| /*! |
| Constructs a geo rectangle from the contents of \a other. |
| */ |
| QGeoRectangle::QGeoRectangle(const QGeoShape &other) |
| : QGeoShape(other) |
| { |
| initRectangleConversions(); |
| if (type() != QGeoShape::RectangleType) |
| d_ptr = new QGeoRectanglePrivate; |
| } |
| |
| /*! |
| Destroys this geo rectangle. |
| */ |
| QGeoRectangle::~QGeoRectangle() |
| { |
| } |
| |
| /*! |
| Assigns \a other to this geo rectangle and returns a reference to this geo rectangle. |
| */ |
| QGeoRectangle &QGeoRectangle::operator=(const QGeoRectangle &other) |
| { |
| QGeoShape::operator=(other); |
| return *this; |
| } |
| |
| /*! |
| Returns whether this geo rectangle is equal to \a other. |
| */ |
| bool QGeoRectangle::operator==(const QGeoRectangle &other) const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return *d == *other.d_func(); |
| } |
| |
| /*! |
| Returns whether this geo rectangle is not equal to \a other. |
| */ |
| bool QGeoRectangle::operator!=(const QGeoRectangle &other) const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return !(*d == *other.d_func()); |
| } |
| |
| bool QGeoRectanglePrivate::isValid() const |
| { |
| return topLeft.isValid() && bottomRight.isValid() && |
| topLeft.latitude() >= bottomRight.latitude(); |
| } |
| |
| bool QGeoRectanglePrivate::isEmpty() const |
| { |
| if (!isValid()) |
| return true; |
| |
| return topLeft.latitude() == bottomRight.latitude() || |
| topLeft.longitude() == bottomRight.longitude(); |
| } |
| |
| /*! |
| Sets the top left coordinate of this geo rectangle to \a topLeft. |
| */ |
| void QGeoRectangle::setTopLeft(const QGeoCoordinate &topLeft) |
| { |
| Q_D(QGeoRectangle); |
| |
| d->topLeft = topLeft; |
| } |
| |
| /*! |
| Returns the top left coordinate of this geo rectangle. |
| */ |
| QGeoCoordinate QGeoRectangle::topLeft() const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return d->topLeft; |
| } |
| |
| /*! |
| Sets the top right coordinate of this geo rectangle to \a topRight. |
| */ |
| void QGeoRectangle::setTopRight(const QGeoCoordinate &topRight) |
| { |
| Q_D(QGeoRectangle); |
| |
| d->topLeft.setLatitude(topRight.latitude()); |
| d->bottomRight.setLongitude(topRight.longitude()); |
| } |
| |
| /*! |
| Returns the top right coordinate of this geo rectangle. |
| */ |
| QGeoCoordinate QGeoRectangle::topRight() const |
| { |
| // TODO remove? |
| if (!isValid()) |
| return QGeoCoordinate(); |
| |
| Q_D(const QGeoRectangle); |
| |
| return QGeoCoordinate(d->topLeft.latitude(), d->bottomRight.longitude()); |
| } |
| |
| /*! |
| Sets the bottom left coordinate of this geo rectangle to \a bottomLeft. |
| */ |
| void QGeoRectangle::setBottomLeft(const QGeoCoordinate &bottomLeft) |
| { |
| Q_D(QGeoRectangle); |
| |
| d->bottomRight.setLatitude(bottomLeft.latitude()); |
| d->topLeft.setLongitude(bottomLeft.longitude()); |
| } |
| |
| /*! |
| Returns the bottom left coordinate of this geo rectangle. |
| */ |
| QGeoCoordinate QGeoRectangle::bottomLeft() const |
| { |
| // TODO remove? |
| if (!isValid()) |
| return QGeoCoordinate(); |
| |
| Q_D(const QGeoRectangle); |
| |
| return QGeoCoordinate(d->bottomRight.latitude(), d->topLeft.longitude()); |
| } |
| |
| /*! |
| Sets the bottom right coordinate of this geo rectangle to \a bottomRight. |
| */ |
| void QGeoRectangle::setBottomRight(const QGeoCoordinate &bottomRight) |
| { |
| Q_D(QGeoRectangle); |
| |
| d->bottomRight = bottomRight; |
| } |
| |
| /*! |
| Returns the bottom right coordinate of this geo rectangle. |
| */ |
| QGeoCoordinate QGeoRectangle::bottomRight() const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return d->bottomRight; |
| } |
| |
| /*! |
| Sets the center of this geo rectangle to \a center. |
| |
| If this causes the geo rectangle to cross on of the poles the height of the |
| geo rectangle will be truncated such that the geo rectangle only extends up |
| to the pole. The center of the geo rectangle will be unchanged, and the |
| height will be adjusted such that the center point is at the center of the |
| truncated geo rectangle. |
| |
| */ |
| void QGeoRectangle::setCenter(const QGeoCoordinate ¢er) |
| { |
| Q_D(QGeoRectangle); |
| |
| if (!isValid()) { |
| d->topLeft = center; |
| d->bottomRight = center; |
| return; |
| } |
| double width = this->width(); |
| double height = this->height(); |
| |
| double tlLat = center.latitude() + height / 2.0; |
| double tlLon = center.longitude() - width / 2.0; |
| double brLat = center.latitude() - height / 2.0; |
| double brLon = center.longitude() + width / 2.0; |
| tlLon = QLocationUtils::wrapLong(tlLon); |
| brLon = QLocationUtils::wrapLong(brLon); |
| |
| if (tlLat > 90.0) { |
| brLat = 2 * center.latitude() - 90.0; |
| tlLat = 90.0; |
| } |
| |
| if (tlLat < -90.0) { |
| brLat = -90.0; |
| tlLat = -90.0; |
| } |
| |
| if (brLat > 90.0) { |
| tlLat = 90.0; |
| brLat = 90.0; |
| } |
| |
| if (brLat < -90.0) { |
| tlLat = 2 * center.latitude() + 90.0; |
| brLat = -90.0; |
| } |
| |
| if (width == 360.0) { |
| tlLon = -180.0; |
| brLon = 180.0; |
| } |
| |
| d->topLeft = QGeoCoordinate(tlLat, tlLon); |
| d->bottomRight = QGeoCoordinate(brLat, brLon); |
| } |
| |
| /*! |
| Returns the center of this geo rectangle. Equivalent to QGeoShape::center(). |
| */ |
| QGeoCoordinate QGeoRectangle::center() const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return d->center(); |
| } |
| |
| /*! |
| Sets the width of this geo rectangle in degrees to \a degreesWidth. |
| */ |
| void QGeoRectangle::setWidth(double degreesWidth) |
| { |
| if (!isValid()) |
| return; |
| |
| if (degreesWidth < 0.0) |
| return; |
| |
| Q_D(QGeoRectangle); |
| |
| if (degreesWidth >= 360.0) { |
| d->topLeft.setLongitude(-180.0); |
| d->bottomRight.setLongitude(180.0); |
| return; |
| } |
| |
| double tlLat = d->topLeft.latitude(); |
| double brLat = d->bottomRight.latitude(); |
| |
| QGeoCoordinate c = center(); |
| |
| double tlLon = c.longitude() - degreesWidth / 2.0; |
| tlLon = QLocationUtils::wrapLong(tlLon); |
| |
| double brLon = c.longitude() + degreesWidth / 2.0; |
| brLon = QLocationUtils::wrapLong(brLon); |
| |
| d->topLeft = QGeoCoordinate(tlLat, tlLon); |
| d->bottomRight = QGeoCoordinate(brLat, brLon); |
| } |
| |
| /*! |
| Returns the width of this geo rectangle in degrees. |
| |
| The return value is undefined if this geo rectangle is invalid. |
| */ |
| double QGeoRectangle::width() const |
| { |
| if (!isValid()) |
| return qQNaN(); |
| |
| Q_D(const QGeoRectangle); |
| |
| double result = d->bottomRight.longitude() - d->topLeft.longitude(); |
| if (result < 0.0) |
| result += 360.0; |
| if (result > 360.0) |
| result -= 360.0; |
| |
| return result; |
| } |
| |
| /*! |
| Sets the height of this geo rectangle in degrees to \a degreesHeight. |
| */ |
| void QGeoRectangle::setHeight(double degreesHeight) |
| { |
| if (!isValid()) |
| return; |
| |
| if (degreesHeight < 0.0) |
| return; |
| |
| if (degreesHeight >= 180.0) { |
| degreesHeight = 180.0; |
| } |
| |
| Q_D(QGeoRectangle); |
| |
| double tlLon = d->topLeft.longitude(); |
| double brLon = d->bottomRight.longitude(); |
| |
| QGeoCoordinate c = center(); |
| |
| double tlLat = c.latitude() + degreesHeight / 2.0; |
| double brLat = c.latitude() - degreesHeight / 2.0; |
| |
| if (tlLat > 90.0) { |
| brLat = 2* c.latitude() - 90.0; |
| tlLat = 90.0; |
| } |
| |
| if (tlLat < -90.0) { |
| brLat = -90.0; |
| tlLat = -90.0; |
| } |
| |
| if (brLat > 90.0) { |
| tlLat = 90.0; |
| brLat = 90.0; |
| } |
| |
| if (brLat < -90.0) { |
| tlLat = 2 * c.latitude() + 90.0; |
| brLat = -90.0; |
| } |
| |
| d->topLeft = QGeoCoordinate(tlLat, tlLon); |
| d->bottomRight = QGeoCoordinate(brLat, brLon); |
| } |
| |
| /*! |
| Returns the height of this geo rectangle in degrees. |
| |
| The return value is undefined if this geo rectangle is invalid. |
| */ |
| double QGeoRectangle::height() const |
| { |
| if (!isValid()) |
| return qQNaN(); |
| |
| Q_D(const QGeoRectangle); |
| |
| return d->topLeft.latitude() - d->bottomRight.latitude(); |
| } |
| |
| bool QGeoRectanglePrivate::contains(const QGeoCoordinate &coordinate) const |
| { |
| if (!isValid() || !coordinate.isValid()) |
| return false; |
| |
| double left = topLeft.longitude(); |
| double right = bottomRight.longitude(); |
| double top = topLeft.latitude(); |
| double bottom = bottomRight.latitude(); |
| |
| double lon = coordinate.longitude(); |
| double lat = coordinate.latitude(); |
| |
| if (lat > top) |
| return false; |
| if (lat < bottom) |
| return false; |
| |
| if ((lat == 90.0) && (top == 90.0)) |
| return true; |
| |
| if ((lat == -90.0) && (bottom == -90.0)) |
| return true; |
| |
| if (left <= right) { |
| if ((lon < left) || (lon > right)) |
| return false; |
| } else { |
| if ((lon < left) && (lon > right)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| QGeoCoordinate QGeoRectanglePrivate::center() const |
| { |
| if (!isValid()) |
| return QGeoCoordinate(); |
| |
| double cLat = (topLeft.latitude() + bottomRight.latitude()) / 2.0; |
| double cLon = (bottomRight.longitude() + topLeft.longitude()) / 2.0; |
| |
| if (topLeft.longitude() > bottomRight.longitude()) |
| cLon = cLon - 180.0; |
| |
| cLon = QLocationUtils::wrapLong(cLon); |
| 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. |
| */ |
| bool QGeoRectangle::contains(const QGeoRectangle &rectangle) const |
| { |
| Q_D(const QGeoRectangle); |
| |
| return (d->contains(rectangle.topLeft()) |
| && d->contains(rectangle.topRight()) |
| && d->contains(rectangle.bottomLeft()) |
| && d->contains(rectangle.bottomRight())); |
| } |
| |
| /*! |
| Returns whether the geo rectangle \a rectangle intersects this geo rectangle. |
| |
| If the top or bottom edges of both geo rectangles are at one of the poles |
| the geo rectangles are considered to be intersecting, since the longitude |
| is irrelevant when the edges are at the pole. |
| */ |
| bool QGeoRectangle::intersects(const QGeoRectangle &rectangle) const |
| { |
| Q_D(const QGeoRectangle); |
| |
| double left1 = d->topLeft.longitude(); |
| double right1 = d->bottomRight.longitude(); |
| double top1 = d->topLeft.latitude(); |
| double bottom1 = d->bottomRight.latitude(); |
| |
| double left2 = rectangle.d_func()->topLeft.longitude(); |
| double right2 = rectangle.d_func()->bottomRight.longitude(); |
| double top2 = rectangle.d_func()->topLeft.latitude(); |
| double bottom2 = rectangle.d_func()->bottomRight.latitude(); |
| |
| if (top1 < bottom2) |
| return false; |
| |
| if (bottom1 > top2) |
| return false; |
| |
| if ((top1 == 90.0) && (top1 == top2)) |
| return true; |
| |
| if ((bottom1 == -90.0) && (bottom1 == bottom2)) |
| return true; |
| |
| if (left1 < right1) { |
| if (left2 < right2) { |
| if ((left1 > right2) || (right1 < left2)) |
| return false; |
| } else { |
| if ((left1 > right2) && (right1 < left2)) |
| return false; |
| } |
| } else { |
| if (left2 < right2) { |
| if ((left2 > right1) && (right2 < left1)) |
| return false; |
| } else { |
| // if both wrap then they have to intersect |
| } |
| } |
| |
| return true; |
| } |
| |
| /*! |
| Translates this geo rectangle by \a degreesLatitude northwards and \a |
| degreesLongitude eastwards. |
| |
| Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
| southward and westward translation respectively. |
| |
| If the translation would have caused the geo rectangle to cross a pole the |
| geo rectangle will be translated until the top or bottom edge of the geo rectangle |
| touches the pole but not further. |
| */ |
| void QGeoRectangle::translate(double degreesLatitude, double degreesLongitude) |
| { |
| // TODO handle dlat, dlon larger than 360 degrees |
| |
| Q_D(QGeoRectangle); |
| |
| double tlLat = d->topLeft.latitude(); |
| double tlLon = d->topLeft.longitude(); |
| double brLat = d->bottomRight.latitude(); |
| double brLon = d->bottomRight.longitude(); |
| |
| if (degreesLatitude >= 0.0) |
| degreesLatitude = qMin(degreesLatitude, 90.0 - tlLat); |
| else |
| degreesLatitude = qMax(degreesLatitude, -90.0 - brLat); |
| |
| if ( (tlLon != -180.0) || (brLon != 180.0) ) { |
| tlLon = QLocationUtils::wrapLong(tlLon + degreesLongitude); |
| brLon = QLocationUtils::wrapLong(brLon + degreesLongitude); |
| } |
| |
| tlLat += degreesLatitude; |
| brLat += degreesLatitude; |
| |
| d->topLeft = QGeoCoordinate(tlLat, tlLon); |
| d->bottomRight = QGeoCoordinate(brLat, brLon); |
| } |
| |
| /*! |
| Returns a copy of this geo rectangle translated by \a degreesLatitude northwards and \a |
| degreesLongitude eastwards. |
| |
| Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
| southward and westward translation respectively. |
| |
| \sa translate() |
| */ |
| QGeoRectangle QGeoRectangle::translated(double degreesLatitude, double degreesLongitude) const |
| { |
| QGeoRectangle result(*this); |
| result.translate(degreesLatitude, degreesLongitude); |
| return result; |
| } |
| |
| /*! |
| Extends the geo rectangle to also cover the coordinate \a coordinate |
| |
| \since 5.9 |
| */ |
| void QGeoRectangle::extendRectangle(const QGeoCoordinate &coordinate) |
| { |
| Q_D(QGeoRectangle); |
| d->extendShape(coordinate); |
| } |
| |
| /*! |
| Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. |
| |
| If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the |
| width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the |
| rightmost longitude set to 180.0 degrees. This is done to ensure that the result is |
| independent of the order of the operands. |
| |
| */ |
| QGeoRectangle QGeoRectangle::united(const QGeoRectangle &rectangle) const |
| { |
| QGeoRectangle result(*this); |
| if (rectangle.isValid()) |
| result |= rectangle; |
| return result; |
| } |
| |
| /*! |
| Extends the rectangle in the smallest possible way to include \a coordinate in |
| the shape. |
| |
| Both the rectangle and coordinate needs to be valid. If the rectangle already covers |
| the coordinate noting happens. |
| |
| */ |
| void QGeoRectanglePrivate::extendShape(const QGeoCoordinate &coordinate) |
| { |
| if (!isValid() || !coordinate.isValid() || contains(coordinate)) |
| return; |
| |
| double left = topLeft.longitude(); |
| double right = bottomRight.longitude(); |
| double top = topLeft.latitude(); |
| double bottom = bottomRight.latitude(); |
| |
| double inputLat = coordinate.latitude(); |
| double inputLon = coordinate.longitude(); |
| |
| top = qMax(top, inputLat); |
| bottom = qMin(bottom, inputLat); |
| |
| bool wrap = left > right; |
| |
| if (wrap && inputLon > right && inputLon < left) { |
| if (qAbs(left - inputLon) < qAbs(right - inputLon)) |
| left = inputLon; |
| else |
| right = inputLon; |
| } else if (!wrap) { |
| if (inputLon < left) { |
| if (360 - (right - inputLon) < left - inputLon) |
| right = inputLon; |
| else |
| left = inputLon; |
| } else if (inputLon > right) { |
| if (360 - (inputLon - left) < inputLon - right) |
| left = inputLon; |
| else |
| right = inputLon; |
| } |
| } |
| topLeft = QGeoCoordinate(top, left); |
| bottomRight = QGeoCoordinate(bottom, right); |
| } |
| |
| /*! |
| \fn QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const |
| |
| Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. |
| |
| If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the |
| width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the |
| rightmost longitude set to 180.0 degrees. This is done to ensure that the result is |
| independent of the order of the operands. |
| |
| */ |
| |
| /*! |
| Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. |
| |
| If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the |
| width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the |
| rightmost longitude set to 180.0 degrees. This is done to ensure that the result is |
| independent of the order of the operands. |
| |
| */ |
| QGeoRectangle &QGeoRectangle::operator|=(const QGeoRectangle &rectangle) |
| { |
| // If non-intersecting goes for most narrow box |
| |
| Q_D(QGeoRectangle); |
| |
| double left1 = d->topLeft.longitude(); |
| double right1 = d->bottomRight.longitude(); |
| double top1 = d->topLeft.latitude(); |
| double bottom1 = d->bottomRight.latitude(); |
| |
| double left2 = rectangle.d_func()->topLeft.longitude(); |
| double right2 = rectangle.d_func()->bottomRight.longitude(); |
| double top2 = rectangle.d_func()->topLeft.latitude(); |
| double bottom2 = rectangle.d_func()->bottomRight.latitude(); |
| |
| double top = qMax(top1, top2); |
| double bottom = qMin(bottom1, bottom2); |
| |
| double left = 0.0; |
| double right = 0.0; |
| |
| bool wrap1 = (left1 > right1); |
| bool wrap2 = (left2 > right2); |
| |
| if ((wrap1 && wrap2) || (!wrap1 && !wrap2)) { |
| |
| double w = qAbs((left1 + right1 - left2 - right2) / 2.0); |
| |
| if (w < 180.0) { |
| left = qMin(left1, left2); |
| right = qMax(right1, right2); |
| } else if (w > 180.0) { |
| left = qMax(left1, left2); |
| right = qMin(right1, right2); |
| } else { |
| left = -180.0; |
| right = 180.0; |
| } |
| |
| } else { |
| double wrapLeft = 0.0; |
| double wrapRight = 0.0; |
| double nonWrapLeft = 0.0; |
| double nonWrapRight = 0.0; |
| |
| if (wrap1) { |
| wrapLeft = left1; |
| wrapRight = right1; |
| nonWrapLeft = left2; |
| nonWrapRight = right2; |
| } else { |
| wrapLeft = left2; |
| wrapRight = right2; |
| nonWrapLeft = left1; |
| nonWrapRight = right1; |
| } |
| |
| bool joinWrapLeft = (nonWrapRight >= wrapLeft); |
| bool joinWrapRight = (nonWrapLeft <= wrapRight); |
| |
| if (wrapLeft <= nonWrapLeft) { // The wrapping rectangle contains the non-wrapping one entirely |
| left = wrapLeft; |
| right = wrapRight; |
| } else { |
| if (joinWrapLeft) { |
| if (joinWrapRight) { |
| left = -180.0; |
| right = 180.0; |
| } else { |
| left = nonWrapLeft; |
| right = wrapRight; |
| } |
| } else { |
| if (joinWrapRight) { |
| left = wrapLeft; |
| right = nonWrapRight; |
| } else { |
| double wrapRightDistance = nonWrapLeft - wrapRight; |
| double wrapLeftDistance = wrapLeft - nonWrapRight; |
| |
| if (wrapLeftDistance == wrapRightDistance) { |
| left = -180.0; |
| right = 180.0; |
| } else if (wrapLeftDistance < wrapRightDistance) { |
| left = nonWrapLeft; |
| right = wrapRight; |
| } else { |
| left = wrapLeft; |
| right = nonWrapRight; |
| } |
| } |
| } |
| } |
| } |
| |
| if (((left1 == -180) && (right1 == 180.0)) |
| || ((left2 == -180) && (right2 == 180.0))) { |
| left = -180; |
| right = 180; |
| } |
| |
| d->topLeft = QGeoCoordinate(top, left); |
| d->bottomRight = QGeoCoordinate(bottom, right); |
| |
| return *this; |
| } |
| |
| /*! |
| Returns the geo rectangle properties as a string. |
| |
| \since 5.5 |
| */ |
| QString QGeoRectangle::toString() const |
| { |
| if (type() != QGeoShape::RectangleType) { |
| qWarning("Not a rectangle a %d\n", type()); |
| return QStringLiteral("QGeoRectangle(not a rectangle)"); |
| } |
| |
| return QStringLiteral("QGeoRectangle({%1, %2}, {%3, %4})") |
| .arg(topLeft().latitude()) |
| .arg(topLeft().longitude()) |
| .arg(bottomRight().latitude()) |
| .arg(bottomRight().longitude()); |
| } |
| |
| /******************************************************************************* |
| *******************************************************************************/ |
| |
| QGeoRectanglePrivate::QGeoRectanglePrivate() |
| : QGeoShapePrivate(QGeoShape::RectangleType) |
| { |
| } |
| |
| QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoCoordinate &topLeft, |
| const QGeoCoordinate &bottomRight) |
| : QGeoShapePrivate(QGeoShape::RectangleType), topLeft(topLeft), bottomRight(bottomRight) |
| { |
| } |
| |
| QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoRectanglePrivate &other) |
| : QGeoShapePrivate(QGeoShape::RectangleType), topLeft(other.topLeft), |
| bottomRight(other.bottomRight) |
| { |
| } |
| |
| QGeoRectanglePrivate::~QGeoRectanglePrivate() {} |
| |
| QGeoShapePrivate *QGeoRectanglePrivate::clone() const |
| { |
| return new QGeoRectanglePrivate(*this); |
| } |
| |
| bool QGeoRectanglePrivate::operator==(const QGeoShapePrivate &other) const |
| { |
| if (!QGeoShapePrivate::operator==(other)) |
| return false; |
| |
| const QGeoRectanglePrivate &otherBox = static_cast<const QGeoRectanglePrivate &>(other); |
| |
| return topLeft == otherBox.topLeft && bottomRight == otherBox.bottomRight; |
| } |
| |
| QT_END_NAMESPACE |
| |