Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeorectangle.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
10#include "qgeocoordinate.h"
11#include "qnumeric.h"
13#include <QList>
15
16QT_IMPL_METATYPE_EXTERN(QGeoRectangle)
17
18/*!
19 \class QGeoRectangle
20 \inmodule QtPositioning
21 \ingroup QtPositioning-positioning
22 \since 5.2
23
24 \brief The QGeoRectangle class defines a rectangular geographic area.
25
26 The rectangle is defined in terms of a QGeoCoordinate which specifies the
27 top left coordinate of the rectangle and a QGeoCoordinate which specifies
28 the bottom right coordinate of the rectangle.
29
30 A geo rectangle is considered invalid if the top left or bottom right
31 coordinates are invalid or if the top left coordinate is south of the
32 bottom right coordinate.
33
34 Geo rectangles can never cross the poles.
35
36 Several methods behave as though the geo rectangle is defined in terms of a
37 center coordinate, the width of the geo rectangle in degrees and the height
38 of the geo rectangle in degrees.
39
40 If the height or center of a geo rectangle is adjusted such that it would
41 cross one of the poles the height is modified such that the geo rectangle
42 touches but does not cross the pole and that the center coordinate is still
43 in the center of the geo rectangle.
44
45 This class is also accessible in QML as \l[QML]{geoRectangle}.
46*/
47
48/*!
49 \property QGeoRectangle::bottomLeft
50 \brief This property holds the bottom left coorindate of this geo rectangle.
51
52 While this property is introduced in Qt 5.5, the related accessor functions
53 exist since the first version of this class.
54
55 \since 5.5
56*/
57
58/*!
59 \property QGeoRectangle::bottomRight
60 \brief This property holds the bottom right coordinate of this geo rectangle.
61
62 While this property is introduced in Qt 5.5, the related accessor functions
63 exist since the first version of this class.
64
65 \since 5.5
66*/
67
68/*!
69 \property QGeoRectangle::topLeft
70 \brief This property holds the top left coordinate of this geo rectangle.
71
72 While this property is introduced in Qt 5.5, the related accessor functions
73 exist since the first version of this class.
74
75 \since 5.5
76*/
77
78/*!
79 \property QGeoRectangle::topRight
80 \brief This property holds the top right coordinate of this geo rectangle.
81
82 While this property is introduced in Qt 5.5, the related accessor functions
83 exist since the first version of this class.
84
85 \since 5.5
86*/
87
88/*!
89 \property QGeoRectangle::center
90 \brief This property holds the center of this geo rectangle.
91
92 While this property is introduced in Qt 5.5, the related accessor functions
93 exist since the first version of this class.
94
95 \sa QGeoShape::center
96
97 \since 5.5
98*/
99
100/*!
101 \property QGeoRectangle::width
102 \brief This property holds the width of this geo rectangle in degrees.
103
104 The property value is undefined if this geo rectangle is invalid.
105
106 If the new width is less than 0.0 or if this geo rectangle is invalid, this
107 function does nothing. To set up the values of an invalid
108 geo rectangle based on the center, width, and height, you should use
109 \l setCenter() first to make the geo rectangle valid.
110
111 360.0 is the width used only if the new width is equal or greater than 360.
112 In such cases the leftmost longitude of the geo rectangle is set to -180.0
113 degrees and the rightmost longitude of the geo rectangle is set to 180.0
114 degrees.
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::height
124 \brief This property holds the height of this geo rectangle in degrees.
125
126 The property value is undefined if this geo rectangle is invalid.
127
128 If the new height is less than 0.0 or if this geo rectangle is invalid,
129 the property is not changed. To set up the values of an invalid
130 geo rectangle based on the center, width, and height, you should use
131 \l setCenter() first to make the geo rectangle valid.
132
133 If the change in height would cause the geo rectangle to cross a pole,
134 the height is adjusted such that the geo rectangle only touches the pole.
135
136 This change is done such that the center coordinate is still at the
137 center of the geo rectangle, which may result in a geo rectangle with
138 a smaller height than expected.
139
140 180.0 is the height used only if the new height is greater or equal than 180.
141
142 While this property is introduced in Qt 5.5, the related accessor functions
143 exist since the first version of this class.
144
145 \since 5.5
146*/
147
148inline QGeoRectanglePrivate *QGeoRectangle::d_func()
149{
150 return static_cast<QGeoRectanglePrivate *>(d_ptr.data());
151}
152
153inline const QGeoRectanglePrivate *QGeoRectangle::d_func() const
154{
155 return static_cast<const QGeoRectanglePrivate *>(d_ptr.constData());
156}
157
158/*!
159 Constructs a new, invalid geo rectangle.
160*/
161QGeoRectangle::QGeoRectangle()
162: QGeoShape(new QGeoRectanglePrivate)
163{
164}
165
166/*!
167 Constructs a new geo rectangle centered at \a center with a
168 width in degrees of \a degreesWidth and a height in degrees of \a degreesHeight.
169
170 If \a degreesHeight would take the geo rectangle beyond one of the poles,
171 the height of the geo rectangle will be truncated such that the geo rectangle
172 only extends up to the pole. The center of the geo rectangle will be
173 unchanged, and the height will be adjusted such that the center point is at
174 the center of the truncated geo rectangle.
175*/
176QGeoRectangle::QGeoRectangle(const QGeoCoordinate &center, double degreesWidth, double degreesHeight)
177{
178 d_ptr = new QGeoRectanglePrivate(center, center);
179 setWidth(degreesWidth);
180 setHeight(degreesHeight);
181}
182
183/*!
184 Constructs a new geo rectangle with a top left coordinate \a topLeft and a bottom right
185 coordinate \a bottomRight.
186*/
187QGeoRectangle::QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight)
188{
189 d_ptr = new QGeoRectanglePrivate(topLeft, bottomRight);
190}
191
192/*!
193 Constructs a new geo rectangle, of minimum size, containing all of the \a coordinates.
194*/
195QGeoRectangle::QGeoRectangle(const QList<QGeoCoordinate> &coordinates)
196{
197 if (coordinates.isEmpty()) {
198 d_ptr = new QGeoRectanglePrivate;
199 } else {
200 const QGeoCoordinate &startCoordinate = coordinates.first();
201 d_ptr = new QGeoRectanglePrivate(startCoordinate, startCoordinate);
202
203 for (const QGeoCoordinate &coordinate : coordinates)
204 d_func()->extendRectangle(coordinate);
205 }
206}
207
208/*!
209 Constructs a geo rectangle from the contents of \a other.
210*/
211QGeoRectangle::QGeoRectangle(const QGeoRectangle &other)
212: QGeoShape(other)
213{
214}
215
216/*!
217 Constructs a geo rectangle from the contents of \a other.
218*/
219QGeoRectangle::QGeoRectangle(const QGeoShape &other)
220: QGeoShape(other)
221{
222 if (type() != QGeoShape::RectangleType)
223 d_ptr = new QGeoRectanglePrivate;
224}
225
226/*!
227 Destroys this geo rectangle.
228*/
229QGeoRectangle::~QGeoRectangle()
230{
231}
232
233/*!
234 Assigns \a other to this geo rectangle and returns a reference to this geo rectangle.
235*/
236QGeoRectangle &QGeoRectangle::operator=(const QGeoRectangle &other)
237{
238 QGeoShape::operator=(other);
239 return *this;
240}
241
242/*!
243 \class QGeoRectanglePrivate
244 \inmodule QtPositioning
245 \internal
246*/
247
248bool QGeoRectanglePrivate::isValid() const
249{
250 return topLeft.isValid() && bottomRight.isValid() &&
251 topLeft.latitude() >= bottomRight.latitude();
252}
253
254bool QGeoRectanglePrivate::isEmpty() const
255{
256 if (!isValid())
257 return true;
258
259 return topLeft.latitude() == bottomRight.latitude() ||
260 topLeft.longitude() == bottomRight.longitude();
261}
262
263/*!
264 Sets the top left coordinate of this geo rectangle to \a topLeft.
265*/
266void QGeoRectangle::setTopLeft(const QGeoCoordinate &topLeft)
267{
268 Q_D(QGeoRectangle);
269
270 d->topLeft = topLeft;
271}
272
273/*!
274 Returns the top left coordinate of this geo rectangle.
275*/
276QGeoCoordinate QGeoRectangle::topLeft() const
277{
278 Q_D(const QGeoRectangle);
279
280 return d->topLeft;
281}
282
283/*!
284 Sets the top right coordinate of this geo rectangle to \a topRight.
285*/
286void QGeoRectangle::setTopRight(const QGeoCoordinate &topRight)
287{
288 Q_D(QGeoRectangle);
289
290 d->topLeft.setLatitude(topRight.latitude());
291 d->bottomRight.setLongitude(topRight.longitude());
292}
293
294/*!
295 Returns the top right coordinate of this geo rectangle.
296*/
297QGeoCoordinate QGeoRectangle::topRight() const
298{
299 // TODO remove?
300 if (!isValid())
301 return QGeoCoordinate();
302
303 Q_D(const QGeoRectangle);
304
305 return QGeoCoordinate(d->topLeft.latitude(), d->bottomRight.longitude());
306}
307
308/*!
309 Sets the bottom left coordinate of this geo rectangle to \a bottomLeft.
310*/
311void QGeoRectangle::setBottomLeft(const QGeoCoordinate &bottomLeft)
312{
313 Q_D(QGeoRectangle);
314
315 d->bottomRight.setLatitude(bottomLeft.latitude());
316 d->topLeft.setLongitude(bottomLeft.longitude());
317}
318
319/*!
320 Returns the bottom left coordinate of this geo rectangle.
321*/
322QGeoCoordinate QGeoRectangle::bottomLeft() const
323{
324 // TODO remove?
325 if (!isValid())
326 return QGeoCoordinate();
327
328 Q_D(const QGeoRectangle);
329
330 return QGeoCoordinate(d->bottomRight.latitude(), d->topLeft.longitude());
331}
332
333/*!
334 Sets the bottom right coordinate of this geo rectangle to \a bottomRight.
335*/
336void QGeoRectangle::setBottomRight(const QGeoCoordinate &bottomRight)
337{
338 Q_D(QGeoRectangle);
339
340 d->bottomRight = bottomRight;
341}
342
343/*!
344 Returns the bottom right coordinate of this geo rectangle.
345*/
346QGeoCoordinate QGeoRectangle::bottomRight() const
347{
348 Q_D(const QGeoRectangle);
349
350 return d->bottomRight;
351}
352
353/*!
354 Sets the center of this geo rectangle to \a center.
355
356 If this causes the geo rectangle to cross on of the poles the height of the
357 geo rectangle will be truncated such that the geo rectangle only extends up
358 to the pole. The center of the geo rectangle will be unchanged, and the
359 height will be adjusted such that the center point is at the center of the
360 truncated geo rectangle.
361
362*/
363void QGeoRectangle::setCenter(const QGeoCoordinate &center)
364{
365 Q_D(QGeoRectangle);
366
367 if (!isValid()) {
368 d->topLeft = center;
369 d->bottomRight = center;
370 return;
371 }
372 double width = this->width();
373 double height = this->height();
374
375 double tlLat = center.latitude() + height / 2.0;
376 double tlLon = center.longitude() - width / 2.0;
377 double brLat = center.latitude() - height / 2.0;
378 double brLon = center.longitude() + width / 2.0;
379 tlLon = QLocationUtils::wrapLong(tlLon);
380 brLon = QLocationUtils::wrapLong(brLon);
381
382 if (tlLat > 90.0) {
383 brLat = 2 * center.latitude() - 90.0;
384 tlLat = 90.0;
385 }
386
387 if (tlLat < -90.0) {
388 brLat = -90.0;
389 tlLat = -90.0;
390 }
391
392 if (brLat > 90.0) {
393 tlLat = 90.0;
394 brLat = 90.0;
395 }
396
397 if (brLat < -90.0) {
398 tlLat = 2 * center.latitude() + 90.0;
399 brLat = -90.0;
400 }
401
402 if (width == 360.0) {
403 tlLon = -180.0;
404 brLon = 180.0;
405 }
406
407 d->topLeft = QGeoCoordinate(tlLat, tlLon);
408 d->bottomRight = QGeoCoordinate(brLat, brLon);
409}
410
411/*!
412 Returns the center of this geo rectangle. Equivalent to QGeoShape::center().
413*/
414QGeoCoordinate QGeoRectangle::center() const
415{
416 Q_D(const QGeoRectangle);
417
418 return d->center();
419}
420
421/*!
422 Sets the width of this geo rectangle in degrees to \a degreesWidth.
423*/
424void QGeoRectangle::setWidth(double degreesWidth)
425{
426 if (!isValid())
427 return;
428
429 if (degreesWidth < 0.0)
430 return;
431
432 Q_D(QGeoRectangle);
433
434 if (degreesWidth >= 360.0) {
435 d->topLeft.setLongitude(-180.0);
436 d->bottomRight.setLongitude(180.0);
437 return;
438 }
439
440 double tlLat = d->topLeft.latitude();
441 double brLat = d->bottomRight.latitude();
442
443 QGeoCoordinate c = center();
444
445 double tlLon = c.longitude() - degreesWidth / 2.0;
446 tlLon = QLocationUtils::wrapLong(tlLon);
447
448 double brLon = c.longitude() + degreesWidth / 2.0;
449 brLon = QLocationUtils::wrapLong(brLon);
450
451 d->topLeft = QGeoCoordinate(tlLat, tlLon);
452 d->bottomRight = QGeoCoordinate(brLat, brLon);
453}
454
455/*!
456 Returns the width of this geo rectangle in degrees.
457
458 The return value is undefined if this geo rectangle is invalid.
459*/
460double QGeoRectangle::width() const
461{
462 if (!isValid())
463 return qQNaN();
464
465 Q_D(const QGeoRectangle);
466
467 double result = d->bottomRight.longitude() - d->topLeft.longitude();
468 if (result < 0.0)
469 result += 360.0;
470 if (result > 360.0)
471 result -= 360.0;
472
473 return result;
474}
475
476/*!
477 Sets the height of this geo rectangle in degrees to \a degreesHeight.
478*/
479void QGeoRectangle::setHeight(double degreesHeight)
480{
481 if (!isValid())
482 return;
483
484 if (degreesHeight < 0.0)
485 return;
486
487 if (degreesHeight >= 180.0) {
488 degreesHeight = 180.0;
489 }
490
491 Q_D(QGeoRectangle);
492
493 double tlLon = d->topLeft.longitude();
494 double brLon = d->bottomRight.longitude();
495
496 QGeoCoordinate c = center();
497
498 double tlLat = c.latitude() + degreesHeight / 2.0;
499 double brLat = c.latitude() - degreesHeight / 2.0;
500
501 if (tlLat > 90.0) {
502 brLat = 2* c.latitude() - 90.0;
503 tlLat = 90.0;
504 }
505
506 if (tlLat < -90.0) {
507 brLat = -90.0;
508 tlLat = -90.0;
509 }
510
511 if (brLat > 90.0) {
512 tlLat = 90.0;
513 brLat = 90.0;
514 }
515
516 if (brLat < -90.0) {
517 tlLat = 2 * c.latitude() + 90.0;
518 brLat = -90.0;
519 }
520
521 d->topLeft = QGeoCoordinate(tlLat, tlLon);
522 d->bottomRight = QGeoCoordinate(brLat, brLon);
523}
524
525/*!
526 Returns the height of this geo rectangle in degrees.
527
528 The return value is undefined if this geo rectangle is invalid.
529*/
530double QGeoRectangle::height() const
531{
532 if (!isValid())
533 return qQNaN();
534
535 Q_D(const QGeoRectangle);
536
537 return d->topLeft.latitude() - d->bottomRight.latitude();
538}
539
540bool QGeoRectanglePrivate::contains(const QGeoCoordinate &coordinate) const
541{
542 if (!isValid() || !coordinate.isValid())
543 return false;
544
545 double left = topLeft.longitude();
546 double right = bottomRight.longitude();
547 double top = topLeft.latitude();
548 double bottom = bottomRight.latitude();
549
550 double lon = coordinate.longitude();
551 double lat = coordinate.latitude();
552
553 if (lat > top)
554 return false;
555 if (lat < bottom)
556 return false;
557
558 if ((lat == 90.0) && (top == 90.0))
559 return true;
560
561 if ((lat == -90.0) && (bottom == -90.0))
562 return true;
563
564 if (left <= right) {
565 if ((lon < left) || (lon > right))
566 return false;
567 } else {
568 if ((lon < left) && (lon > right))
569 return false;
570 }
571
572 return true;
573}
574
575QGeoCoordinate QGeoRectanglePrivate::center() const
576{
577 if (!isValid())
578 return QGeoCoordinate();
579
580 double cLat = (topLeft.latitude() + bottomRight.latitude()) / 2.0;
581 double cLon = (bottomRight.longitude() + topLeft.longitude()) / 2.0;
582
583 if (topLeft.longitude() > bottomRight.longitude())
584 cLon = cLon - 180.0;
585
586 cLon = QLocationUtils::wrapLong(cLon);
587 return QGeoCoordinate(cLat, cLon);
588}
589
590QGeoRectangle QGeoRectanglePrivate::boundingGeoRectangle() const
591{
592 return QGeoRectangle(topLeft, bottomRight);
593}
594
595/*!
596 Returns whether the geo rectangle \a rectangle is contained within this
597 geo rectangle.
598*/
599bool QGeoRectangle::contains(const QGeoRectangle &rectangle) const
600{
601 Q_D(const QGeoRectangle);
602
603 return (d->contains(rectangle.topLeft())
604 && d->contains(rectangle.topRight())
605 && d->contains(rectangle.bottomLeft())
606 && d->contains(rectangle.bottomRight()));
607}
608
609/*!
610 Returns whether the geo rectangle \a rectangle intersects this geo rectangle.
611
612 If the top or bottom edges of both geo rectangles are at one of the poles
613 the geo rectangles are considered to be intersecting, since the longitude
614 is irrelevant when the edges are at the pole.
615*/
616bool QGeoRectangle::intersects(const QGeoRectangle &rectangle) const
617{
618 Q_D(const QGeoRectangle);
619
620 double left1 = d->topLeft.longitude();
621 double right1 = d->bottomRight.longitude();
622 double top1 = d->topLeft.latitude();
623 double bottom1 = d->bottomRight.latitude();
624
625 double left2 = rectangle.d_func()->topLeft.longitude();
626 double right2 = rectangle.d_func()->bottomRight.longitude();
627 double top2 = rectangle.d_func()->topLeft.latitude();
628 double bottom2 = rectangle.d_func()->bottomRight.latitude();
629
630 if (top1 < bottom2)
631 return false;
632
633 if (bottom1 > top2)
634 return false;
635
636 if ((top1 == 90.0) && (top1 == top2))
637 return true;
638
639 if ((bottom1 == -90.0) && (bottom1 == bottom2))
640 return true;
641
642 if (left1 < right1) {
643 if (left2 < right2) {
644 if ((left1 > right2) || (right1 < left2))
645 return false;
646 } else {
647 if ((left1 > right2) && (right1 < left2))
648 return false;
649 }
650 } else {
651 if (left2 < right2) {
652 if ((left2 > right1) && (right2 < left1))
653 return false;
654 } else {
655 // if both wrap then they have to intersect
656 }
657 }
658
659 return true;
660}
661
662/*!
663 Translates this geo rectangle by \a degreesLatitude northwards and \a
664 degreesLongitude eastwards.
665
666 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
667 southward and westward translation respectively.
668
669 If the translation would have caused the geo rectangle to cross a pole the
670 geo rectangle will be translated until the top or bottom edge of the geo rectangle
671 touches the pole but not further.
672*/
673void QGeoRectangle::translate(double degreesLatitude, double degreesLongitude)
674{
675 // TODO handle dlat, dlon larger than 360 degrees
676
677 Q_D(QGeoRectangle);
678
679 double tlLat = d->topLeft.latitude();
680 double tlLon = d->topLeft.longitude();
681 double brLat = d->bottomRight.latitude();
682 double brLon = d->bottomRight.longitude();
683
684 if (degreesLatitude >= 0.0)
685 degreesLatitude = qMin(degreesLatitude, 90.0 - tlLat);
686 else
687 degreesLatitude = qMax(degreesLatitude, -90.0 - brLat);
688
689 if ( (tlLon != -180.0) || (brLon != 180.0) ) {
690 tlLon = QLocationUtils::wrapLong(tlLon + degreesLongitude);
691 brLon = QLocationUtils::wrapLong(brLon + degreesLongitude);
692 }
693
694 tlLat += degreesLatitude;
695 brLat += degreesLatitude;
696
697 d->topLeft = QGeoCoordinate(tlLat, tlLon);
698 d->bottomRight = QGeoCoordinate(brLat, brLon);
699}
700
701/*!
702 Returns a copy of this geo rectangle translated by \a degreesLatitude northwards and \a
703 degreesLongitude eastwards.
704
705 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
706 southward and westward translation respectively.
707
708 \sa translate()
709*/
710QGeoRectangle QGeoRectangle::translated(double degreesLatitude, double degreesLongitude) const
711{
712 QGeoRectangle result(*this);
713 result.translate(degreesLatitude, degreesLongitude);
714 return result;
715}
716
717/*!
718 Extends the geo rectangle to also cover the coordinate \a coordinate
719
720 \since 5.9
721*/
722void QGeoRectangle::extendRectangle(const QGeoCoordinate &coordinate)
723{
724 Q_D(QGeoRectangle);
725 d->extendRectangle(coordinate);
726}
727
728/*!
729 Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle.
730
731 If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the
732 width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the
733 rightmost longitude set to 180.0 degrees. This is done to ensure that the result is
734 independent of the order of the operands.
735
736*/
737QGeoRectangle QGeoRectangle::united(const QGeoRectangle &rectangle) const
738{
739 QGeoRectangle result(*this);
740 if (rectangle.isValid())
741 result |= rectangle;
742 return result;
743}
744
745/*!
746 Extends the rectangle in the smallest possible way to include \a coordinate in
747 the shape.
748
749 Both the rectangle and coordinate needs to be valid. If the rectangle already covers
750 the coordinate noting happens.
751
752*/
753void QGeoRectanglePrivate::extendRectangle(const QGeoCoordinate &coordinate)
754{
755 if (!isValid() || !coordinate.isValid() || contains(coordinate))
756 return;
757
758 double left = topLeft.longitude();
759 double right = bottomRight.longitude();
760 double top = topLeft.latitude();
761 double bottom = bottomRight.latitude();
762
763 double inputLat = coordinate.latitude();
764 double inputLon = coordinate.longitude();
765
766 top = qMax(top, inputLat);
767 bottom = qMin(bottom, inputLat);
768
769 bool wrap = left > right;
770
771 if (wrap && inputLon > right && inputLon < left) {
772 if (qAbs(left - inputLon) < qAbs(right - inputLon))
773 left = inputLon;
774 else
775 right = inputLon;
776 } else if (!wrap) {
777 if (inputLon < left) {
778 if (360 - (right - inputLon) < left - inputLon)
779 right = inputLon;
780 else
781 left = inputLon;
782 } else if (inputLon > right) {
783 if (360 - (inputLon - left) < inputLon - right)
784 left = inputLon;
785 else
786 right = inputLon;
787 }
788 }
789 topLeft = QGeoCoordinate(top, left);
790 bottomRight = QGeoCoordinate(bottom, right);
791}
792
793/*!
794 \fn QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const
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*/
804
805/*!
806 Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle.
807
808 If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the
809 width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the
810 rightmost longitude set to 180.0 degrees. This is done to ensure that the result is
811 independent of the order of the operands.
812
813*/
814QGeoRectangle &QGeoRectangle::operator|=(const QGeoRectangle &rectangle)
815{
816 // If non-intersecting goes for most narrow box
817
818 Q_D(QGeoRectangle);
819
820 double top = qMax(d->topLeft.latitude(), rectangle.d_func()->topLeft.latitude());
821 double bottom = qMin(d->bottomRight.latitude(), rectangle.d_func()->bottomRight.latitude());
822
823 QGeoRectangle candidate(
824 {top, d->topLeft.longitude()},
825 {bottom, rectangle.d_func()->bottomRight.longitude()}
826 );
827 QGeoRectangle otherCandidate(
828 {top, rectangle.d_func()->topLeft.longitude()},
829 {bottom, d->bottomRight.longitude()}
830 );
831 double unwrappedWidth = (candidate.width() < rectangle.width() ? 360 : 0) + candidate.width();
832 double otherUnwrappedWidth = (otherCandidate.width() < width() ? 360 : 0) + otherCandidate.width();
833 if (otherUnwrappedWidth < unwrappedWidth) {
834 candidate = otherCandidate;
835 unwrappedWidth = otherUnwrappedWidth;
836 }
837 if (360 <= unwrappedWidth) {
838 candidate.d_func()->topLeft.setLongitude(-180.0);
839 candidate.d_func()->bottomRight.setLongitude(180.0);
840 }
841
842 candidate = (candidate.width() < width() ? *this : candidate);
843 candidate = (candidate.width() < rectangle.width() ? rectangle : candidate);
844
845 double middle1 = center().longitude();
846 double middle2 = rectangle.center().longitude();
847 if ((middle1 <= middle2 ? 0 : 360) + middle2 - middle1 == 180) {
848 candidate.d_func()->topLeft.setLongitude(-180.0);
849 candidate.d_func()->bottomRight.setLongitude(180.0);
850 }
851
852 *this = candidate;
853 this->d_func()->topLeft.setLatitude(top);
854 this->d_func()->bottomRight.setLatitude(bottom);
855
856 return *this;
857}
858
859/*!
860 Returns the geo rectangle properties as a string.
861
862 \since 5.5
863*/
864QString QGeoRectangle::toString() const
865{
866 if (type() != QGeoShape::RectangleType) {
867 qWarning("Not a rectangle a %d\n", type());
868 return QStringLiteral("QGeoRectangle(not a rectangle)");
869 }
870
871 return QStringLiteral("QGeoRectangle({%1, %2}, {%3, %4})")
872 .arg(topLeft().latitude())
873 .arg(topLeft().longitude())
874 .arg(bottomRight().latitude())
875 .arg(bottomRight().longitude());
876}
877
878/*******************************************************************************
879*******************************************************************************/
880
881QGeoRectanglePrivate::QGeoRectanglePrivate()
882: QGeoShapePrivate(QGeoShape::RectangleType)
883{
884}
885
886QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoCoordinate &topLeft,
887 const QGeoCoordinate &bottomRight)
888: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(topLeft), bottomRight(bottomRight)
889{
890}
891
892QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoRectanglePrivate &other)
893: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(other.topLeft),
894 bottomRight(other.bottomRight)
895{
896}
897
898QGeoRectanglePrivate::~QGeoRectanglePrivate() {}
899
900QGeoShapePrivate *QGeoRectanglePrivate::clone() const
901{
902 return new QGeoRectanglePrivate(*this);
903}
904
905bool QGeoRectanglePrivate::operator==(const QGeoShapePrivate &other) const
906{
907 if (!QGeoShapePrivate::operator==(other))
908 return false;
909
910 const QGeoRectanglePrivate &otherBox = static_cast<const QGeoRectanglePrivate &>(other);
911
912 return topLeft == otherBox.topLeft && bottomRight == otherBox.bottomRight;
913}
914
915size_t QGeoRectanglePrivate::hash(size_t seed) const
916{
917 return qHashMulti(seed, topLeft, bottomRight);
918}
919
920QT_END_NAMESPACE
921
922#include "moc_qgeorectangle.cpp"
Combined button and popup list for selecting options.