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>
49QT_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
182inline QGeoRectanglePrivate *QGeoRectangle::d_func()
183{
184 return static_cast<QGeoRectanglePrivate *>(d_ptr.data());
185}
186
187inline const QGeoRectanglePrivate *QGeoRectangle::d_func() const
188{
189 return static_cast<const QGeoRectanglePrivate *>(d_ptr.constData());
190}
191
192struct RectangleVariantConversions
193{
194 RectangleVariantConversions()
195 {
196 QMetaType::registerConverter<QGeoRectangle, QGeoShape>();
197 QMetaType::registerConverter<QGeoShape, QGeoRectangle>();
198 }
199};
200
201
202Q_GLOBAL_STATIC(RectangleVariantConversions, initRectangleConversions)
203
204/*!
205 Constructs a new, invalid geo rectangle.
206*/
207QGeoRectangle::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*/
223QGeoRectangle::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*/
235QGeoRectangle::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*/
244QGeoRectangle::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*/
262QGeoRectangle::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*/
271QGeoRectangle::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*/
282QGeoRectangle::~QGeoRectangle()
283{
284}
285
286/*!
287 Assigns \a other to this geo rectangle and returns a reference to this geo rectangle.
288*/
289QGeoRectangle &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*/
298bool 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*/
308bool QGeoRectangle::operator!=(const QGeoRectangle &other) const
309{
310 Q_D(const QGeoRectangle);
311
312 return !(*d == *other.d_func());
313}
314
315bool QGeoRectanglePrivate::isValid() const
316{
317 return topLeft.isValid() && bottomRight.isValid() &&
318 topLeft.latitude() >= bottomRight.latitude();
319}
320
321bool 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*/
333void 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*/
343QGeoCoordinate 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*/
353void 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*/
364QGeoCoordinate 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*/
378void 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*/
389QGeoCoordinate 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*/
403void 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*/
413QGeoCoordinate 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*/
430void 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(lng: tlLon);
447 brLon = QLocationUtils::wrapLong(lng: 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*/
481QGeoCoordinate 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*/
491void 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(lng: tlLon);
514
515 double brLon = c.longitude() + degreesWidth / 2.0;
516 brLon = QLocationUtils::wrapLong(lng: 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*/
527double 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*/
546void 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*/
597double 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
607bool 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
642QGeoCoordinate 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(lng: cLon);
654 return QGeoCoordinate(cLat, cLon);
655}
656
657QGeoRectangle 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*/
666bool QGeoRectangle::contains(const QGeoRectangle &rectangle) const
667{
668 Q_D(const QGeoRectangle);
669
670 return (d->contains(coordinate: rectangle.topLeft())
671 && d->contains(coordinate: rectangle.topRight())
672 && d->contains(coordinate: rectangle.bottomLeft())
673 && d->contains(coordinate: 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*/
683bool 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*/
740void 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(a: degreesLatitude, b: 90.0 - tlLat);
753 else
754 degreesLatitude = qMax(a: degreesLatitude, b: -90.0 - brLat);
755
756 if ( (tlLon != -180.0) || (brLon != 180.0) ) {
757 tlLon = QLocationUtils::wrapLong(lng: tlLon + degreesLongitude);
758 brLon = QLocationUtils::wrapLong(lng: 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*/
777QGeoRectangle 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*/
789void 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*/
804QGeoRectangle 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*/
820void 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(a: top, b: inputLat);
834 bottom = qMin(a: bottom, b: inputLat);
835
836 bool wrap = left > right;
837
838 if (wrap && inputLon > right && inputLon < left) {
839 if (qAbs(t: left - inputLon) < qAbs(t: 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*/
881QGeoRectangle &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(a: top1, b: top2);
898 double bottom = qMin(a: bottom1, b: 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(t: (left1 + right1 - left2 - right2) / 2.0);
909
910 if (w < 180.0) {
911 left = qMin(a: left1, b: left2);
912 right = qMax(a: right1, b: right2);
913 } else if (w > 180.0) {
914 left = qMax(a: left1, b: left2);
915 right = qMin(a: right1, b: 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*/
994QString QGeoRectangle::toString() const
995{
996 if (type() != QGeoShape::RectangleType) {
997 qWarning(msg: "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(a: topLeft().latitude())
1003 .arg(a: topLeft().longitude())
1004 .arg(a: bottomRight().latitude())
1005 .arg(a: bottomRight().longitude());
1006}
1007
1008/*******************************************************************************
1009*******************************************************************************/
1010
1011QGeoRectanglePrivate::QGeoRectanglePrivate()
1012: QGeoShapePrivate(QGeoShape::RectangleType)
1013{
1014}
1015
1016QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoCoordinate &topLeft,
1017 const QGeoCoordinate &bottomRight)
1018: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(topLeft), bottomRight(bottomRight)
1019{
1020}
1021
1022QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoRectanglePrivate &other)
1023: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(other.topLeft),
1024 bottomRight(other.bottomRight)
1025{
1026}
1027
1028QGeoRectanglePrivate::~QGeoRectanglePrivate() {}
1029
1030QGeoShapePrivate *QGeoRectanglePrivate::clone() const
1031{
1032 return new QGeoRectanglePrivate(*this);
1033}
1034
1035bool 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
1045QT_END_NAMESPACE
1046
1047

source code of qtlocation/src/positioning/qgeorectangle.cpp