1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtPositioning module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qgeorectangle.h"
41 #include "qgeorectangle_p.h"
42 
43 #include "qwebmercator_p.h"
44 #include "qdoublevector2d_p.h"
45 #include "qgeocoordinate.h"
46 #include "qnumeric.h"
47 #include "qlocationutils_p.h"
48 #include <QList>
49 QT_BEGIN_NAMESPACE
50 
51 /*!
52     \class QGeoRectangle
53     \inmodule QtPositioning
54     \ingroup QtPositioning-positioning
55     \since 5.2
56 
57     \brief The QGeoRectangle class defines a rectangular geographic area.
58 
59     The rectangle is defined in terms of a QGeoCoordinate which specifies the
60     top left coordinate of the rectangle and a QGeoCoordinate which specifies
61     the bottom right coordinate of the rectangle.
62 
63     A geo rectangle is considered invalid if the top left or bottom right
64     coordinates are invalid or if the top left coordinate is south of the
65     bottom right coordinate.
66 
67     Geo rectangles can never cross the poles.
68 
69     Several methods behave as though the geo rectangle is defined in terms of a
70     center coordinate, the width of the geo rectangle in degrees and the height
71     of the geo rectangle in degrees.
72 
73     If the height or center of a geo rectangle is adjusted such that it would
74     cross one of the poles the height is modified such that the geo rectangle
75     touches but does not cross the pole and that the center coordinate is still
76     in the center of the geo rectangle.
77 
78     This class is a \l Q_GADGET since Qt 5.5. It can be
79     \l{Cpp_value_integration_positioning}{directly used from C++ and QML}.
80 */
81 
82 /*!
83     \property QGeoRectangle::bottomLeft
84     \brief This property holds the bottom left coorindate of this geo rectangle.
85 
86     While this property is introduced in Qt 5.5, the related accessor functions
87     exist since the first version of this class.
88 
89     \since 5.5
90 */
91 
92 /*!
93     \property QGeoRectangle::bottomRight
94     \brief This property holds the bottom right coordinate of this geo rectangle.
95 
96     While this property is introduced in Qt 5.5, the related accessor functions
97     exist since the first version of this class.
98 
99     \since 5.5
100 */
101 
102 /*!
103     \property QGeoRectangle::topLeft
104     \brief This property holds the top left coordinate of this geo rectangle.
105 
106     While this property is introduced in Qt 5.5, the related accessor functions
107     exist since the first version of this class.
108 
109     \since 5.5
110 */
111 
112 /*!
113     \property QGeoRectangle::topRight
114     \brief This property holds the top right coordinate of this geo rectangle.
115 
116     While this property is introduced in Qt 5.5, the related accessor functions
117     exist since the first version of this class.
118 
119     \since 5.5
120 */
121 
122 /*!
123     \property QGeoRectangle::center
124     \brief This property holds the center of this geo rectangle.
125 
126     While this property is introduced in Qt 5.5, the related accessor functions
127     exist since the first version of this class.
128 
129     \sa QGeoShape::center
130 
131     \since 5.5
132 */
133 
134 /*!
135     \property QGeoRectangle::width
136     \brief This property holds the width of this geo rectangle in degrees.
137 
138     The property value is undefined if this geo rectangle is invalid.
139 
140     If the new width is less than 0.0 or if this geo rectangle is invalid, this
141     function does nothing. To set up the values of an invalid
142     geo rectangle based on the center, width, and height, you should use
143     \l setCenter() first to make the geo rectangle valid.
144 
145     360.0 is the width used only if the new width is equal or greater than 360.
146     In such cases the leftmost longitude of the geo rectangle is set to -180.0
147     degrees and the rightmost longitude of the geo rectangle is set to 180.0
148     degrees.
149 
150     While this property is introduced in Qt 5.5, the related accessor functions
151     exist since the first version of this class.
152 
153     \since 5.5
154 */
155 
156 /*!
157     \property QGeoRectangle::height
158     \brief This property holds the height of this geo rectangle in degrees.
159 
160     The property value is undefined if this geo rectangle is invalid.
161 
162     If the new height is less than 0.0 or if this geo rectangle is invalid,
163     the property is not changed. To set up the values of an invalid
164     geo rectangle based on the center, width, and height, you should use
165     \l setCenter() first to make the geo rectangle valid.
166 
167     If the change in height would cause the geo rectangle to cross a pole,
168     the height is adjusted such that the geo rectangle only touches the pole.
169 
170     This change is done such that the center coordinate is still at the
171     center of the geo rectangle, which may result in a geo rectangle with
172     a smaller height than expected.
173 
174     180.0 is the height used only if the new height is greater or equal than 180.
175 
176     While this property is introduced in Qt 5.5, the related accessor functions
177     exist since the first version of this class.
178 
179     \since 5.5
180 */
181 
d_func()182 inline QGeoRectanglePrivate *QGeoRectangle::d_func()
183 {
184     return static_cast<QGeoRectanglePrivate *>(d_ptr.data());
185 }
186 
d_func() const187 inline const QGeoRectanglePrivate *QGeoRectangle::d_func() const
188 {
189     return static_cast<const QGeoRectanglePrivate *>(d_ptr.constData());
190 }
191 
192 struct RectangleVariantConversions
193 {
RectangleVariantConversionsRectangleVariantConversions194     RectangleVariantConversions()
195     {
196         QMetaType::registerConverter<QGeoRectangle, QGeoShape>();
197         QMetaType::registerConverter<QGeoShape, QGeoRectangle>();
198     }
199 };
200 
201 
Q_GLOBAL_STATIC(RectangleVariantConversions,initRectangleConversions)202 Q_GLOBAL_STATIC(RectangleVariantConversions, initRectangleConversions)
203 
204 /*!
205     Constructs a new, invalid geo rectangle.
206 */
207 QGeoRectangle::QGeoRectangle()
208 :   QGeoShape(new QGeoRectanglePrivate)
209 {
210     initRectangleConversions();
211 }
212 
213 /*!
214     Constructs a new geo rectangle centered at \a center with a
215     width in degrees of \a degreesWidth and a height in degrees of \a degreesHeight.
216 
217     If \a degreesHeight would take the geo rectangle beyond one of the poles,
218     the height of the geo rectangle will be truncated such that the geo rectangle
219     only extends up to the pole. The center of the geo rectangle will be
220     unchanged, and the height will be adjusted such that the center point is at
221     the center of the truncated geo rectangle.
222 */
QGeoRectangle(const QGeoCoordinate & center,double degreesWidth,double degreesHeight)223 QGeoRectangle::QGeoRectangle(const QGeoCoordinate &center, double degreesWidth, double degreesHeight)
224 {
225     initRectangleConversions();
226     d_ptr = new QGeoRectanglePrivate(center, center);
227     setWidth(degreesWidth);
228     setHeight(degreesHeight);
229 }
230 
231 /*!
232     Constructs a new geo rectangle with a top left coordinate \a topLeft and a bottom right
233     coordinate \a bottomRight.
234 */
QGeoRectangle(const QGeoCoordinate & topLeft,const QGeoCoordinate & bottomRight)235 QGeoRectangle::QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight)
236 {
237     initRectangleConversions();
238     d_ptr = new QGeoRectanglePrivate(topLeft, bottomRight);
239 }
240 
241 /*!
242     Constructs a new geo rectangle, of minimum size, containing all of the \a coordinates.
243 */
QGeoRectangle(const QList<QGeoCoordinate> & coordinates)244 QGeoRectangle::QGeoRectangle(const QList<QGeoCoordinate> &coordinates)
245 {
246     initRectangleConversions();
247     if (coordinates.isEmpty()) {
248         d_ptr = new QGeoRectanglePrivate;
249     } else {
250         const QGeoCoordinate &startCoordinate = coordinates.first();
251         d_ptr = new QGeoRectanglePrivate(startCoordinate, startCoordinate);
252 
253         foreach (const QGeoCoordinate &coordinate, coordinates) {
254             d_ptr->extendShape(coordinate);
255         }
256     }
257 }
258 
259 /*!
260     Constructs a geo rectangle from the contents of \a other.
261 */
QGeoRectangle(const QGeoRectangle & other)262 QGeoRectangle::QGeoRectangle(const QGeoRectangle &other)
263 :   QGeoShape(other)
264 {
265     initRectangleConversions();
266 }
267 
268 /*!
269     Constructs a geo rectangle from the contents of \a other.
270 */
QGeoRectangle(const QGeoShape & other)271 QGeoRectangle::QGeoRectangle(const QGeoShape &other)
272 :   QGeoShape(other)
273 {
274     initRectangleConversions();
275     if (type() != QGeoShape::RectangleType)
276         d_ptr = new QGeoRectanglePrivate;
277 }
278 
279 /*!
280     Destroys this geo rectangle.
281 */
~QGeoRectangle()282 QGeoRectangle::~QGeoRectangle()
283 {
284 }
285 
286 /*!
287     Assigns \a other to this geo rectangle and returns a reference to this geo rectangle.
288 */
operator =(const QGeoRectangle & other)289 QGeoRectangle &QGeoRectangle::operator=(const QGeoRectangle &other)
290 {
291     QGeoShape::operator=(other);
292     return *this;
293 }
294 
295 /*!
296     Returns whether this geo rectangle is equal to \a other.
297 */
operator ==(const QGeoRectangle & other) const298 bool QGeoRectangle::operator==(const QGeoRectangle &other) const
299 {
300     Q_D(const QGeoRectangle);
301 
302     return *d == *other.d_func();
303 }
304 
305 /*!
306     Returns whether this geo rectangle is not equal to \a other.
307 */
operator !=(const QGeoRectangle & other) const308 bool QGeoRectangle::operator!=(const QGeoRectangle &other) const
309 {
310     Q_D(const QGeoRectangle);
311 
312     return !(*d == *other.d_func());
313 }
314 
isValid() const315 bool QGeoRectanglePrivate::isValid() const
316 {
317     return topLeft.isValid() && bottomRight.isValid() &&
318            topLeft.latitude() >= bottomRight.latitude();
319 }
320 
isEmpty() const321 bool QGeoRectanglePrivate::isEmpty() const
322 {
323     if (!isValid())
324         return true;
325 
326     return topLeft.latitude() == bottomRight.latitude() ||
327            topLeft.longitude() == bottomRight.longitude();
328 }
329 
330 /*!
331     Sets the top left coordinate of this geo rectangle to \a topLeft.
332 */
setTopLeft(const QGeoCoordinate & topLeft)333 void QGeoRectangle::setTopLeft(const QGeoCoordinate &topLeft)
334 {
335     Q_D(QGeoRectangle);
336 
337     d->topLeft = topLeft;
338 }
339 
340 /*!
341     Returns the top left coordinate of this geo rectangle.
342 */
topLeft() const343 QGeoCoordinate QGeoRectangle::topLeft() const
344 {
345     Q_D(const QGeoRectangle);
346 
347     return d->topLeft;
348 }
349 
350 /*!
351     Sets the top right coordinate of this geo rectangle to \a topRight.
352 */
setTopRight(const QGeoCoordinate & topRight)353 void QGeoRectangle::setTopRight(const QGeoCoordinate &topRight)
354 {
355     Q_D(QGeoRectangle);
356 
357     d->topLeft.setLatitude(topRight.latitude());
358     d->bottomRight.setLongitude(topRight.longitude());
359 }
360 
361 /*!
362     Returns the top right coordinate of this geo rectangle.
363 */
topRight() const364 QGeoCoordinate QGeoRectangle::topRight() const
365 {
366     // TODO remove?
367     if (!isValid())
368         return QGeoCoordinate();
369 
370     Q_D(const QGeoRectangle);
371 
372     return QGeoCoordinate(d->topLeft.latitude(), d->bottomRight.longitude());
373 }
374 
375 /*!
376     Sets the bottom left coordinate of this geo rectangle to \a bottomLeft.
377 */
setBottomLeft(const QGeoCoordinate & bottomLeft)378 void QGeoRectangle::setBottomLeft(const QGeoCoordinate &bottomLeft)
379 {
380     Q_D(QGeoRectangle);
381 
382     d->bottomRight.setLatitude(bottomLeft.latitude());
383     d->topLeft.setLongitude(bottomLeft.longitude());
384 }
385 
386 /*!
387     Returns the bottom left coordinate of this geo rectangle.
388 */
bottomLeft() const389 QGeoCoordinate QGeoRectangle::bottomLeft() const
390 {
391     // TODO remove?
392     if (!isValid())
393         return QGeoCoordinate();
394 
395     Q_D(const QGeoRectangle);
396 
397     return QGeoCoordinate(d->bottomRight.latitude(), d->topLeft.longitude());
398 }
399 
400 /*!
401     Sets the bottom right coordinate of this geo rectangle to \a bottomRight.
402 */
setBottomRight(const QGeoCoordinate & bottomRight)403 void QGeoRectangle::setBottomRight(const QGeoCoordinate &bottomRight)
404 {
405     Q_D(QGeoRectangle);
406 
407     d->bottomRight = bottomRight;
408 }
409 
410 /*!
411     Returns the bottom right coordinate of this geo rectangle.
412 */
bottomRight() const413 QGeoCoordinate QGeoRectangle::bottomRight() const
414 {
415     Q_D(const QGeoRectangle);
416 
417     return d->bottomRight;
418 }
419 
420 /*!
421     Sets the center of this geo rectangle to \a center.
422 
423     If this causes the geo rectangle to cross on of the poles the height of the
424     geo rectangle will be truncated such that the geo rectangle only extends up
425     to the pole. The center of the geo rectangle will be unchanged, and the
426     height will be adjusted such that the center point is at the center of the
427     truncated geo rectangle.
428 
429 */
setCenter(const QGeoCoordinate & center)430 void QGeoRectangle::setCenter(const QGeoCoordinate &center)
431 {
432     Q_D(QGeoRectangle);
433 
434     if (!isValid()) {
435         d->topLeft = center;
436         d->bottomRight = center;
437         return;
438     }
439     double width = this->width();
440     double height = this->height();
441 
442     double tlLat = center.latitude() + height / 2.0;
443     double tlLon = center.longitude() - width / 2.0;
444     double brLat = center.latitude() - height / 2.0;
445     double brLon = center.longitude() + width / 2.0;
446     tlLon = QLocationUtils::wrapLong(tlLon);
447     brLon = QLocationUtils::wrapLong(brLon);
448 
449     if (tlLat > 90.0) {
450         brLat = 2 * center.latitude() - 90.0;
451         tlLat = 90.0;
452     }
453 
454     if (tlLat < -90.0) {
455         brLat = -90.0;
456         tlLat = -90.0;
457     }
458 
459     if (brLat > 90.0) {
460         tlLat = 90.0;
461         brLat = 90.0;
462     }
463 
464     if (brLat < -90.0) {
465         tlLat = 2 * center.latitude() + 90.0;
466         brLat = -90.0;
467     }
468 
469     if (width == 360.0) {
470         tlLon = -180.0;
471         brLon = 180.0;
472     }
473 
474     d->topLeft = QGeoCoordinate(tlLat, tlLon);
475     d->bottomRight = QGeoCoordinate(brLat, brLon);
476 }
477 
478 /*!
479     Returns the center of this geo rectangle. Equivalent to QGeoShape::center().
480 */
center() const481 QGeoCoordinate QGeoRectangle::center() const
482 {
483     Q_D(const QGeoRectangle);
484 
485     return d->center();
486 }
487 
488 /*!
489     Sets the width of this geo rectangle in degrees to \a degreesWidth.
490 */
setWidth(double degreesWidth)491 void QGeoRectangle::setWidth(double degreesWidth)
492 {
493     if (!isValid())
494         return;
495 
496     if (degreesWidth < 0.0)
497         return;
498 
499     Q_D(QGeoRectangle);
500 
501     if (degreesWidth >= 360.0) {
502         d->topLeft.setLongitude(-180.0);
503         d->bottomRight.setLongitude(180.0);
504         return;
505     }
506 
507     double tlLat = d->topLeft.latitude();
508     double brLat = d->bottomRight.latitude();
509 
510     QGeoCoordinate c = center();
511 
512     double tlLon = c.longitude() - degreesWidth / 2.0;
513     tlLon = QLocationUtils::wrapLong(tlLon);
514 
515     double brLon = c.longitude() + degreesWidth / 2.0;
516     brLon = QLocationUtils::wrapLong(brLon);
517 
518     d->topLeft = QGeoCoordinate(tlLat, tlLon);
519     d->bottomRight = QGeoCoordinate(brLat, brLon);
520 }
521 
522 /*!
523     Returns the width of this geo rectangle in degrees.
524 
525     The return value is undefined if this geo rectangle is invalid.
526 */
width() const527 double QGeoRectangle::width() const
528 {
529     if (!isValid())
530         return qQNaN();
531 
532     Q_D(const QGeoRectangle);
533 
534     double result = d->bottomRight.longitude() - d->topLeft.longitude();
535     if (result < 0.0)
536         result += 360.0;
537     if (result > 360.0)
538         result -= 360.0;
539 
540     return result;
541 }
542 
543 /*!
544     Sets the height of this geo rectangle in degrees to \a degreesHeight.
545 */
setHeight(double degreesHeight)546 void QGeoRectangle::setHeight(double degreesHeight)
547 {
548     if (!isValid())
549         return;
550 
551     if (degreesHeight < 0.0)
552         return;
553 
554     if (degreesHeight >= 180.0) {
555         degreesHeight = 180.0;
556     }
557 
558     Q_D(QGeoRectangle);
559 
560     double tlLon = d->topLeft.longitude();
561     double brLon = d->bottomRight.longitude();
562 
563     QGeoCoordinate c = center();
564 
565     double tlLat = c.latitude() + degreesHeight / 2.0;
566     double brLat = c.latitude() - degreesHeight / 2.0;
567 
568     if (tlLat > 90.0) {
569         brLat = 2* c.latitude() - 90.0;
570         tlLat = 90.0;
571     }
572 
573     if (tlLat < -90.0) {
574         brLat = -90.0;
575         tlLat = -90.0;
576     }
577 
578     if (brLat > 90.0) {
579         tlLat = 90.0;
580         brLat = 90.0;
581     }
582 
583     if (brLat < -90.0) {
584         tlLat = 2 * c.latitude() + 90.0;
585         brLat = -90.0;
586     }
587 
588     d->topLeft = QGeoCoordinate(tlLat, tlLon);
589     d->bottomRight = QGeoCoordinate(brLat, brLon);
590 }
591 
592 /*!
593     Returns the height of this geo rectangle in degrees.
594 
595     The return value is undefined if this geo rectangle is invalid.
596 */
height() const597 double QGeoRectangle::height() const
598 {
599     if (!isValid())
600         return qQNaN();
601 
602     Q_D(const QGeoRectangle);
603 
604     return d->topLeft.latitude() - d->bottomRight.latitude();
605 }
606 
contains(const QGeoCoordinate & coordinate) const607 bool QGeoRectanglePrivate::contains(const QGeoCoordinate &coordinate) const
608 {
609     if (!isValid() || !coordinate.isValid())
610         return false;
611 
612     double left = topLeft.longitude();
613     double right = bottomRight.longitude();
614     double top = topLeft.latitude();
615     double bottom = bottomRight.latitude();
616 
617     double lon = coordinate.longitude();
618     double lat = coordinate.latitude();
619 
620     if (lat > top)
621         return false;
622     if (lat < bottom)
623         return false;
624 
625     if ((lat == 90.0) && (top == 90.0))
626         return true;
627 
628     if ((lat == -90.0) && (bottom == -90.0))
629         return true;
630 
631     if (left <= right) {
632         if ((lon < left) || (lon > right))
633             return false;
634     } else {
635         if ((lon < left) && (lon > right))
636             return false;
637     }
638 
639     return true;
640 }
641 
center() const642 QGeoCoordinate QGeoRectanglePrivate::center() const
643 {
644     if (!isValid())
645         return QGeoCoordinate();
646 
647     double cLat = (topLeft.latitude() + bottomRight.latitude()) / 2.0;
648     double cLon = (bottomRight.longitude() + topLeft.longitude()) / 2.0;
649 
650     if (topLeft.longitude() > bottomRight.longitude())
651         cLon = cLon - 180.0;
652 
653     cLon = QLocationUtils::wrapLong(cLon);
654     return QGeoCoordinate(cLat, cLon);
655 }
656 
boundingGeoRectangle() const657 QGeoRectangle QGeoRectanglePrivate::boundingGeoRectangle() const
658 {
659     return QGeoRectangle(topLeft, bottomRight);
660 }
661 
662 /*!
663     Returns whether the geo rectangle \a rectangle is contained within this
664     geo rectangle.
665 */
contains(const QGeoRectangle & rectangle) const666 bool QGeoRectangle::contains(const QGeoRectangle &rectangle) const
667 {
668     Q_D(const QGeoRectangle);
669 
670     return (d->contains(rectangle.topLeft())
671             && d->contains(rectangle.topRight())
672             && d->contains(rectangle.bottomLeft())
673             && d->contains(rectangle.bottomRight()));
674 }
675 
676 /*!
677     Returns whether the geo rectangle \a rectangle intersects this geo rectangle.
678 
679     If the top or bottom edges of both geo rectangles are at one of the poles
680     the geo rectangles are considered to be intersecting, since the longitude
681     is irrelevant when the edges are at the pole.
682 */
intersects(const QGeoRectangle & rectangle) const683 bool QGeoRectangle::intersects(const QGeoRectangle &rectangle) const
684 {
685     Q_D(const QGeoRectangle);
686 
687     double left1 = d->topLeft.longitude();
688     double right1 = d->bottomRight.longitude();
689     double top1 = d->topLeft.latitude();
690     double bottom1 = d->bottomRight.latitude();
691 
692     double left2 = rectangle.d_func()->topLeft.longitude();
693     double right2 = rectangle.d_func()->bottomRight.longitude();
694     double top2 = rectangle.d_func()->topLeft.latitude();
695     double bottom2 = rectangle.d_func()->bottomRight.latitude();
696 
697     if (top1 < bottom2)
698         return false;
699 
700     if (bottom1 > top2)
701         return false;
702 
703     if ((top1 == 90.0) && (top1 == top2))
704         return true;
705 
706     if ((bottom1 == -90.0) && (bottom1 == bottom2))
707         return true;
708 
709     if (left1 < right1) {
710         if (left2 < right2) {
711             if ((left1 > right2) || (right1 < left2))
712                 return false;
713         } else {
714             if ((left1 > right2) && (right1 < left2))
715                 return false;
716         }
717     } else {
718         if (left2 < right2) {
719             if ((left2 > right1) && (right2 < left1))
720                 return false;
721         } else {
722             // if both wrap then they have to intersect
723         }
724     }
725 
726     return true;
727 }
728 
729 /*!
730     Translates this geo rectangle by \a degreesLatitude northwards and \a
731     degreesLongitude eastwards.
732 
733     Negative values of \a degreesLatitude and \a degreesLongitude correspond to
734     southward and westward translation respectively.
735 
736     If the translation would have caused the geo rectangle to cross a pole the
737     geo rectangle will be translated until the top or bottom edge of the geo rectangle
738     touches the pole but not further.
739 */
translate(double degreesLatitude,double degreesLongitude)740 void QGeoRectangle::translate(double degreesLatitude, double degreesLongitude)
741 {
742     // TODO handle dlat, dlon larger than 360 degrees
743 
744     Q_D(QGeoRectangle);
745 
746     double tlLat = d->topLeft.latitude();
747     double tlLon = d->topLeft.longitude();
748     double brLat = d->bottomRight.latitude();
749     double brLon = d->bottomRight.longitude();
750 
751     if (degreesLatitude >= 0.0)
752         degreesLatitude = qMin(degreesLatitude, 90.0 - tlLat);
753     else
754         degreesLatitude = qMax(degreesLatitude, -90.0 - brLat);
755 
756     if ( (tlLon != -180.0) || (brLon != 180.0) ) {
757         tlLon = QLocationUtils::wrapLong(tlLon + degreesLongitude);
758         brLon = QLocationUtils::wrapLong(brLon + degreesLongitude);
759     }
760 
761     tlLat += degreesLatitude;
762     brLat += degreesLatitude;
763 
764     d->topLeft = QGeoCoordinate(tlLat, tlLon);
765     d->bottomRight = QGeoCoordinate(brLat, brLon);
766 }
767 
768 /*!
769     Returns a copy of this geo rectangle translated by \a degreesLatitude northwards and \a
770     degreesLongitude eastwards.
771 
772     Negative values of \a degreesLatitude and \a degreesLongitude correspond to
773     southward and westward translation respectively.
774 
775     \sa translate()
776 */
translated(double degreesLatitude,double degreesLongitude) const777 QGeoRectangle QGeoRectangle::translated(double degreesLatitude, double degreesLongitude) const
778 {
779     QGeoRectangle result(*this);
780     result.translate(degreesLatitude, degreesLongitude);
781     return result;
782 }
783 
784 /*!
785     Extends the geo rectangle to also cover the coordinate \a coordinate
786 
787     \since 5.9
788 */
extendRectangle(const QGeoCoordinate & coordinate)789 void QGeoRectangle::extendRectangle(const QGeoCoordinate &coordinate)
790 {
791     Q_D(QGeoRectangle);
792     d->extendShape(coordinate);
793 }
794 
795 /*!
796     Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle.
797 
798     If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the
799     width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the
800     rightmost longitude set to 180.0 degrees.  This is done to ensure that the result is
801     independent of the order of the operands.
802 
803 */
united(const QGeoRectangle & rectangle) const804 QGeoRectangle QGeoRectangle::united(const QGeoRectangle &rectangle) const
805 {
806     QGeoRectangle result(*this);
807     if (rectangle.isValid())
808         result |= rectangle;
809     return result;
810 }
811 
812 /*!
813     Extends the rectangle in the smallest possible way to include \a coordinate in
814     the shape.
815 
816     Both the rectangle and coordinate needs to be valid. If the rectangle already covers
817     the coordinate noting happens.
818 
819 */
extendShape(const QGeoCoordinate & coordinate)820 void QGeoRectanglePrivate::extendShape(const QGeoCoordinate &coordinate)
821 {
822     if (!isValid() || !coordinate.isValid() || contains(coordinate))
823         return;
824 
825     double left = topLeft.longitude();
826     double right = bottomRight.longitude();
827     double top = topLeft.latitude();
828     double bottom = bottomRight.latitude();
829 
830     double inputLat = coordinate.latitude();
831     double inputLon = coordinate.longitude();
832 
833     top = qMax(top, inputLat);
834     bottom = qMin(bottom, inputLat);
835 
836     bool wrap = left > right;
837 
838     if (wrap && inputLon > right && inputLon < left) {
839         if (qAbs(left - inputLon) < qAbs(right - inputLon))
840             left = inputLon;
841         else
842             right = inputLon;
843     } else if (!wrap) {
844         if (inputLon < left) {
845             if (360 - (right - inputLon) < left - inputLon)
846                 right = inputLon;
847             else
848                 left = inputLon;
849         } else if (inputLon > right) {
850             if (360 - (inputLon - left) < inputLon - right)
851                 left = inputLon;
852             else
853                 right = inputLon;
854         }
855     }
856     topLeft = QGeoCoordinate(top, left);
857     bottomRight = QGeoCoordinate(bottom, right);
858 }
859 
860 /*!
861     \fn QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const
862 
863     Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle.
864 
865     If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the
866     width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the
867     rightmost longitude set to 180.0 degrees.  This is done to ensure that the result is
868     independent of the order of the operands.
869 
870 */
871 
872 /*!
873     Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle.
874 
875     If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the
876     width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the
877     rightmost longitude set to 180.0 degrees.  This is done to ensure that the result is
878     independent of the order of the operands.
879 
880 */
operator |=(const QGeoRectangle & rectangle)881 QGeoRectangle &QGeoRectangle::operator|=(const QGeoRectangle &rectangle)
882 {
883     // If non-intersecting goes for most narrow box
884 
885     Q_D(QGeoRectangle);
886 
887     double left1 = d->topLeft.longitude();
888     double right1 = d->bottomRight.longitude();
889     double top1 = d->topLeft.latitude();
890     double bottom1 = d->bottomRight.latitude();
891 
892     double left2 = rectangle.d_func()->topLeft.longitude();
893     double right2 = rectangle.d_func()->bottomRight.longitude();
894     double top2 = rectangle.d_func()->topLeft.latitude();
895     double bottom2 = rectangle.d_func()->bottomRight.latitude();
896 
897     double top = qMax(top1, top2);
898     double bottom = qMin(bottom1, bottom2);
899 
900     double left = 0.0;
901     double right = 0.0;
902 
903     bool wrap1 = (left1 > right1);
904     bool wrap2 = (left2 > right2);
905 
906     if ((wrap1 && wrap2) || (!wrap1 && !wrap2)) {
907 
908         double w = qAbs((left1 + right1 - left2 - right2) / 2.0);
909 
910         if (w < 180.0) {
911             left = qMin(left1, left2);
912             right = qMax(right1, right2);
913         } else if (w > 180.0) {
914             left = qMax(left1, left2);
915             right = qMin(right1, right2);
916         } else {
917             left = -180.0;
918             right = 180.0;
919         }
920 
921     } else {
922         double wrapLeft = 0.0;
923         double wrapRight = 0.0;
924         double nonWrapLeft = 0.0;
925         double nonWrapRight = 0.0;
926 
927         if (wrap1) {
928             wrapLeft = left1;
929             wrapRight = right1;
930             nonWrapLeft = left2;
931             nonWrapRight = right2;
932         } else {
933             wrapLeft = left2;
934             wrapRight = right2;
935             nonWrapLeft = left1;
936             nonWrapRight = right1;
937         }
938 
939         bool joinWrapLeft = (nonWrapRight >= wrapLeft);
940         bool joinWrapRight = (nonWrapLeft <= wrapRight);
941 
942         if (wrapLeft <= nonWrapLeft) { // The wrapping rectangle contains the non-wrapping one entirely
943             left = wrapLeft;
944             right = wrapRight;
945         } else {
946             if (joinWrapLeft) {
947                 if (joinWrapRight) {
948                     left = -180.0;
949                     right = 180.0;
950                 } else {
951                     left = nonWrapLeft;
952                     right = wrapRight;
953                 }
954             } else {
955                 if (joinWrapRight) {
956                     left = wrapLeft;
957                     right = nonWrapRight;
958                 } else {
959                     double wrapRightDistance = nonWrapLeft - wrapRight;
960                     double wrapLeftDistance = wrapLeft - nonWrapRight;
961 
962                     if (wrapLeftDistance == wrapRightDistance) {
963                         left = -180.0;
964                         right = 180.0;
965                     } else if (wrapLeftDistance < wrapRightDistance) {
966                         left = nonWrapLeft;
967                         right = wrapRight;
968                     } else {
969                         left = wrapLeft;
970                         right = nonWrapRight;
971                     }
972                 }
973             }
974         }
975     }
976 
977     if (((left1 == -180) && (right1 == 180.0))
978             || ((left2 == -180) && (right2 == 180.0))) {
979         left = -180;
980         right = 180;
981     }
982 
983     d->topLeft = QGeoCoordinate(top, left);
984     d->bottomRight = QGeoCoordinate(bottom, right);
985 
986     return *this;
987 }
988 
989 /*!
990     Returns the geo rectangle properties as a string.
991 
992     \since 5.5
993 */
toString() const994 QString QGeoRectangle::toString() const
995 {
996     if (type() != QGeoShape::RectangleType) {
997         qWarning("Not a rectangle a %d\n", type());
998         return QStringLiteral("QGeoRectangle(not a rectangle)");
999     }
1000 
1001     return QStringLiteral("QGeoRectangle({%1, %2}, {%3, %4})")
1002         .arg(topLeft().latitude())
1003         .arg(topLeft().longitude())
1004         .arg(bottomRight().latitude())
1005         .arg(bottomRight().longitude());
1006 }
1007 
1008 /*******************************************************************************
1009 *******************************************************************************/
1010 
QGeoRectanglePrivate()1011 QGeoRectanglePrivate::QGeoRectanglePrivate()
1012 :   QGeoShapePrivate(QGeoShape::RectangleType)
1013 {
1014 }
1015 
QGeoRectanglePrivate(const QGeoCoordinate & topLeft,const QGeoCoordinate & bottomRight)1016 QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoCoordinate &topLeft,
1017                                                const QGeoCoordinate &bottomRight)
1018 :   QGeoShapePrivate(QGeoShape::RectangleType), topLeft(topLeft), bottomRight(bottomRight)
1019 {
1020 }
1021 
QGeoRectanglePrivate(const QGeoRectanglePrivate & other)1022 QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoRectanglePrivate &other)
1023 :   QGeoShapePrivate(QGeoShape::RectangleType), topLeft(other.topLeft),
1024     bottomRight(other.bottomRight)
1025 {
1026 }
1027 
~QGeoRectanglePrivate()1028 QGeoRectanglePrivate::~QGeoRectanglePrivate() {}
1029 
clone() const1030 QGeoShapePrivate *QGeoRectanglePrivate::clone() const
1031 {
1032     return new QGeoRectanglePrivate(*this);
1033 }
1034 
operator ==(const QGeoShapePrivate & other) const1035 bool QGeoRectanglePrivate::operator==(const QGeoShapePrivate &other) const
1036 {
1037     if (!QGeoShapePrivate::operator==(other))
1038         return false;
1039 
1040     const QGeoRectanglePrivate &otherBox = static_cast<const QGeoRectanglePrivate &>(other);
1041 
1042     return topLeft == otherBox.topLeft && bottomRight == otherBox.bottomRight;
1043 }
1044 
1045 QT_END_NAMESPACE
1046 
1047