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
qgeopolygon.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
5#include "qgeopolygon.h"
7#include "qgeopath_p.h"
8#include "qgeocircle.h"
9
10#include "qgeocoordinate.h"
11#include "qnumeric.h"
13#include "qwebmercator_p.h"
14
17#include "qwebmercator_p.h"
18
19#include <QtCore/qmutex.h>
20
21#include <mutex>
22
23QT_BEGIN_NAMESPACE
24
25QT_IMPL_METATYPE_EXTERN(QGeoPolygon)
26
27constexpr int kMaxInt = std::numeric_limits<int>::max();
28constexpr auto kTooManyHoles = u"The polygon has more holes than fit into an int. "
29 "This can cause errors while querying holes from QML";
30constexpr auto kTooManyElements = u"The polygon has more elements than fit into an int. "
31 "This can cause errors while querying elements from QML";
32
33/*!
34 \class QGeoPolygon
35 \inmodule QtPositioning
36 \ingroup QtPositioning-positioning
37 \since 5.10
38
39 \brief The QGeoPolygon class defines a geographic polygon.
40
41 The polygon is defined by an ordered list of \l QGeoCoordinate objects
42 representing its perimeter.
43
44 Each two adjacent elements in this list are intended to be connected
45 together by the shortest line segment of constant bearing passing
46 through both elements.
47 This type of connection can cross the date line in the longitudinal direction,
48 but never crosses the poles.
49
50 This is relevant for the calculation of the bounding box returned by
51 \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of
52 the top left corner set to the maximum latitude in the path point set.
53 Similarly, the latitude of the bottom right corner will be the minimum latitude
54 in the path point set.
55
56 This class is also accessible in QML as \l[QML]{geoPolygon}.
57*/
58
59/*
60 \property QGeoPolygon::path
61 \brief This property holds the list of coordinates for the geo polygon.
62
63 The polygon is both invalid and empty if it contains no coordinate.
64
65 A default constructed QGeoPolygon is therefore invalid.
66*/
67
68inline QGeoPolygonPrivate *QGeoPolygon::d_func()
69{
70 return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
71}
72
73inline const QGeoPolygonPrivate *QGeoPolygon::d_func() const
74{
75 return static_cast<const QGeoPolygonPrivate *>(d_ptr.constData());
76}
77
78/*!
79 Constructs a new, empty geo polygon.
80*/
81QGeoPolygon::QGeoPolygon()
82: QGeoShape(new QGeoPolygonPrivate())
83{
84}
85
86/*!
87 Constructs a new geo polygon from the coordinates specified
88 in \a path.
89*/
90QGeoPolygon::QGeoPolygon(const QList<QGeoCoordinate> &path)
91: QGeoShape(new QGeoPolygonPrivate(path))
92{
93}
94
95/*!
96 Constructs a new geo polygon from the contents of \a other.
97*/
98QGeoPolygon::QGeoPolygon(const QGeoPolygon &other)
99: QGeoShape(other)
100{
101}
102
103static void calculatePeripheralPoints(QList<QGeoCoordinate> &path,
104 const QGeoCircle &circle,
105 int steps)
106{
107 const QGeoCoordinate &center = circle.center();
108 const qreal distance = circle.radius();
109 // Calculate points based on great-circle distance
110 // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
111 // but tweaked here for computing multiple points
112
113 // pre-calculations
114 steps = qMax(steps, 3);
115 qreal centerLon = center.longitude();
116 qreal latRad = QLocationUtils::radians(center.latitude());
117 qreal lonRad = QLocationUtils::radians(centerLon);
118 qreal cosLatRad = std::cos(latRad);
119 qreal sinLatRad = std::sin(latRad);
120 qreal ratio = (distance / QLocationUtils::earthMeanRadius());
121 qreal cosRatio = std::cos(ratio);
122 qreal sinRatio = std::sin(ratio);
123 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
124 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
125 for (int i = 0; i < steps; ++i) {
126 qreal azimuthRad = 2 * M_PI * i / steps;
127 qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
128 + cosLatRad_x_sinRatio * std::cos(azimuthRad));
129 qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
130 cosRatio - sinLatRad * std::sin(resultLatRad));
131 qreal lat2 = QLocationUtils::degrees(resultLatRad);
132 qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
133
134 path << QGeoCoordinate(lat2, lon2, center.altitude());
135 }
136}
137
138/*!
139 Constructs a new geo polygon from the contents of \a other.
140*/
141QGeoPolygon::QGeoPolygon(const QGeoShape &other)
142: QGeoShape(other)
143{
144 if (type() != QGeoShape::PolygonType) {
145 QGeoPolygonPrivate *poly = new QGeoPolygonPrivate();
146 if (type() == QGeoShape::CircleType) {
147 const QGeoCircle &circle = static_cast<const QGeoCircle &>(other);
148 QList<QGeoCoordinate> perimeter;
149 calculatePeripheralPoints(perimeter, circle, 128);
150 poly->setPath(perimeter);
151 } else if (type() == QGeoShape::RectangleType) {
152 const QGeoRectangle &rect = static_cast<const QGeoRectangle &>(other);
153 QList<QGeoCoordinate> perimeter;
154 perimeter << rect.topLeft() << rect.topRight()
155 << rect.bottomRight() << rect.bottomLeft();
156 poly->setPath(perimeter);
157 }
158 d_ptr = poly;
159 }
160}
161
162/*!
163 Destroys this polygon.
164*/
165QGeoPolygon::~QGeoPolygon() {}
166
167/*!
168 Assigns \a other to this geo polygon and returns a reference to this geo polygon.
169*/
170QGeoPolygon &QGeoPolygon::operator=(const QGeoPolygon &other)
171{
172 QGeoShape::operator=(other);
173 return *this;
174}
175
176/*!
177 Sets the perimeter of the polygon based on a list of coordinates \a path.
178
179 \since QtPositioning 5.12
180*/
181void QGeoPolygon::setPerimeter(const QList<QGeoCoordinate> &path)
182{
183 Q_D(QGeoPolygon);
184 return d->setPath(path);
185}
186
187/*!
188 \property QGeoPolygon::perimeter
189 \brief the coordinates of the polygon's perimeter.
190*/
191
192/*!
193 Returns all the elements of the polygon's perimeter.
194
195 \since QtPositioning 5.12
196*/
197const QList<QGeoCoordinate> &QGeoPolygon::perimeter() const
198{
199 Q_D(const QGeoPolygon);
200 return d->path();
201}
202
203/*!
204 Translates this geo polygon by \a degreesLatitude northwards and \a degreesLongitude eastwards.
205
206 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
207 southward and westward translation respectively.
208*/
209void QGeoPolygon::translate(double degreesLatitude, double degreesLongitude)
210{
211 Q_D(QGeoPolygon);
212 d->translate(degreesLatitude, degreesLongitude);
213}
214
215/*!
216 Returns a copy of this geo polygon translated by \a degreesLatitude northwards and
217 \a degreesLongitude eastwards.
218
219 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
220 southward and westward translation respectively.
221
222 \sa translate()
223*/
224QGeoPolygon QGeoPolygon::translated(double degreesLatitude, double degreesLongitude) const
225{
226 QGeoPolygon result(*this);
227 result.translate(degreesLatitude, degreesLongitude);
228 return result;
229}
230
231/*!
232 Returns the length of the polygon's perimeter, in meters, from the element \a indexFrom to the element \a indexTo.
233 The length is intended to be the sum of the shortest distances for each pair of adjacent points.
234*/
235double QGeoPolygon::length(qsizetype indexFrom, qsizetype indexTo) const
236{
237 Q_D(const QGeoPolygon);
238 return d->length(indexFrom, indexTo);
239}
240
241/*!
242 Returns the number of elements in the polygon.
243
244 \since 5.10
245*/
246qsizetype QGeoPolygon::size() const
247{
248 Q_D(const QGeoPolygon);
249 const qsizetype result = d->size();
250 if (result > kMaxInt)
251 qWarning() << kTooManyElements;
252 return result;
253}
254
255/*!
256 Appends \a coordinate to the polygon.
257*/
258void QGeoPolygon::addCoordinate(const QGeoCoordinate &coordinate)
259{
260 Q_D(QGeoPolygon);
261 d->addCoordinate(coordinate);
262 if (d->size() > kMaxInt)
263 qWarning() << kTooManyElements;
264}
265
266/*!
267 Inserts \a coordinate at the specified \a index.
268*/
269void QGeoPolygon::insertCoordinate(qsizetype index, const QGeoCoordinate &coordinate)
270{
271 Q_D(QGeoPolygon);
272 d->insertCoordinate(index, coordinate);
273}
274
275/*!
276 Replaces the path element at the specified \a index with \a coordinate.
277*/
278void QGeoPolygon::replaceCoordinate(qsizetype index, const QGeoCoordinate &coordinate)
279{
280 Q_D(QGeoPolygon);
281 d->replaceCoordinate(index, coordinate);
282}
283
284/*!
285 Returns the coordinate at \a index .
286*/
287QGeoCoordinate QGeoPolygon::coordinateAt(qsizetype index) const
288{
289 Q_D(const QGeoPolygon);
290 return d->coordinateAt(index);
291}
292
293/*!
294 Returns true if the polygon's perimeter contains \a coordinate as one of the elements.
295*/
296bool QGeoPolygon::containsCoordinate(const QGeoCoordinate &coordinate) const
297{
298 Q_D(const QGeoPolygon);
299 return d->containsCoordinate(coordinate);
300}
301
302/*!
303 Removes the last occurrence of \a coordinate from the polygon.
304*/
305void QGeoPolygon::removeCoordinate(const QGeoCoordinate &coordinate)
306{
307 Q_D(QGeoPolygon);
308 d->removeCoordinate(coordinate);
309}
310
311/*!
312 Removes element at position \a index from the polygon.
313*/
314void QGeoPolygon::removeCoordinate(qsizetype index)
315{
316 Q_D(QGeoPolygon);
317 d->removeCoordinate(index);
318}
319
320/*!
321 Returns the geo polygon properties as a string.
322*/
323QString QGeoPolygon::toString() const
324{
325 if (type() != QGeoShape::PolygonType) {
326 qWarning("Not a polygon");
327 return QStringLiteral("QGeoPolygon(not a polygon)");
328 }
329
330 QString pathString;
331 for (const auto &p : perimeter())
332 pathString += p.toString() + QLatin1StringView("; ");
333 pathString.chop(2);
334
335 return QStringLiteral("QGeoPolygon([ %1 ])").arg(pathString);
336}
337
338/*!
339 Sets the \a holePath for a hole inside the polygon. The hole is a
340 QVariant containing a QList<QGeoCoordinate>.
341
342 \since 5.12
343*/
344void QGeoPolygon::addHole(const QVariant &holePath)
345{
346 QList<QGeoCoordinate> qgcHolePath;
347 if (holePath.canConvert<QVariantList>()) {
348 const QVariantList qvlHolePath = holePath.toList();
349 for (const QVariant &vertex : qvlHolePath) {
350 if (vertex.canConvert<QGeoCoordinate>())
351 qgcHolePath << vertex.value<QGeoCoordinate>();
352 }
353 }
354 //ToDo: add QGeoShape support
355 addHole(qgcHolePath);
356}
357
358/*!
359 Overloaded method. Sets the \a holePath for a hole inside the polygon. The hole is a QList<QGeoCoordinate>.
360
361 \since 5.12
362*/
363void QGeoPolygon::addHole(const QList<QGeoCoordinate> &holePath)
364{
365 Q_D(QGeoPolygon);
366 d->addHole(holePath);
367 if (d->holesCount() > kMaxInt)
368 qDebug() << kTooManyHoles;
369}
370
371/*!
372 Returns a QVariant containing a QList<QGeoCoordinate>
373 which represents the hole at \a index.
374
375 \since 5.12
376*/
377const QVariantList QGeoPolygon::hole(qsizetype index) const
378{
379 Q_D(const QGeoPolygon);
380 QVariantList holeCoordinates;
381 for (const QGeoCoordinate &coords: d->holePath(index))
382 holeCoordinates << QVariant::fromValue(coords);
383 return holeCoordinates;
384}
385
386/*!
387 Returns a QList<QGeoCoordinate> which represents the hole at \a index.
388
389 \since 5.12
390*/
391const QList<QGeoCoordinate> QGeoPolygon::holePath(qsizetype index) const
392{
393 Q_D(const QGeoPolygon);
394 return d->holePath(index);
395}
396
397/*!
398 Removes element at position \a index from the list of holes.
399
400 \since 5.12
401*/
402void QGeoPolygon::removeHole(qsizetype index)
403{
404 Q_D(QGeoPolygon);
405 return d->removeHole(index);
406}
407
408/*!
409 Returns the number of holes.
410
411 \since 5.12
412*/
413qsizetype QGeoPolygon::holesCount() const
414{
415 Q_D(const QGeoPolygon);
416 const qsizetype result = d->holesCount();
417 if (result > kMaxInt)
418 qWarning() << kTooManyHoles;
419 return result;
420}
421
422/*******************************************************************************
423 *
424 * QGeoPathPrivate & friends
425 *
426*******************************************************************************/
427
428Q_CONSTINIT static QBasicMutex globalPolygonMutex;
429
430QGeoPolygonPrivate::QGeoPolygonPrivate()
431: QGeoPathPrivateBase()
432{
433 type = QGeoShape::PolygonType;
434}
435
436QGeoPolygonPrivate::QGeoPolygonPrivate(const QList<QGeoCoordinate> &path)
437: QGeoPathPrivateBase(path)
438{
439 type = QGeoShape::PolygonType;
440}
441
442QGeoPolygonPrivate::QGeoPolygonPrivate(const QGeoPolygonPrivate &other)
443 : QGeoPathPrivateBase(other),
444 m_holesList(other.m_holesList)
445{
446 type = QGeoShape::PolygonType;
447 // Do not copy cached members: they would need mutex locked!
448 // m_clipperDirty is set to true by default, so that it'll properly trigger
449 // re-evaluation when needed.
450}
451
452QGeoPolygonPrivate::~QGeoPolygonPrivate() {}
453
454QGeoShapePrivate *QGeoPolygonPrivate::clone() const
455{
456 return new QGeoPolygonPrivate(*this);
457}
458
459bool QGeoPolygonPrivate::isValid() const
460{
461 return path().size() > 2;
462}
463
464bool QGeoPolygonPrivate::contains(const QGeoCoordinate &coordinate) const
465{
466 return polygonContains(coordinate);
467}
468
469inline static void translatePoly( QList<QGeoCoordinate> &m_path,
470 QList<QList<QGeoCoordinate>> &m_holesList,
471 QGeoRectangle &m_bbox,
472 double degreesLatitude,
473 double degreesLongitude,
474 double m_maxLati,
475 double m_minLati)
476{
477 if (degreesLatitude > 0.0)
478 degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati);
479 else
480 degreesLatitude = qMax(degreesLatitude, -90.0 - m_minLati);
481 for (QGeoCoordinate &p: m_path) {
482 p.setLatitude(p.latitude() + degreesLatitude);
483 p.setLongitude(QLocationUtils::wrapLong(p.longitude() + degreesLongitude));
484 }
485 if (!m_holesList.isEmpty()){
486 for (QList<QGeoCoordinate> &hole: m_holesList){
487 for (QGeoCoordinate &holeVertex: hole){
488 holeVertex.setLatitude(holeVertex.latitude() + degreesLatitude);
489 holeVertex.setLongitude(QLocationUtils::wrapLong(holeVertex.longitude() + degreesLongitude));
490 }
491 }
492 }
493 m_bbox.translate(degreesLatitude, degreesLongitude);
494}
495
496void QGeoPolygonPrivate::translate(double degreesLatitude, double degreesLongitude)
497{
498 // Need min/maxLati, so update bbox
499 QList<double> m_deltaXs;
500 double m_minX, m_maxX, m_minLati, m_maxLati;
501 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
502 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
503 m_bboxDirty.store(false, std::memory_order_release);
504 m_clipperDirty.store(true, std::memory_order_release);
505}
506
507bool QGeoPolygonPrivate::operator==(const QGeoShapePrivate &other) const
508{
509 if (!QGeoPathPrivateBase::operator==(other)) // checks type
510 return false;
511
512 const QGeoPolygonPrivate &otherPolygon = static_cast<const QGeoPolygonPrivate &>(other);
513 return m_holesList == otherPolygon.m_holesList;
514}
515
516size_t QGeoPolygonPrivate::hash(size_t seed) const
517{
518 const size_t pointsHash = qHashRange(m_path.cbegin(), m_path.cend(), seed);
519 const size_t holesHash = qHashRange(m_holesList.cbegin(), m_holesList.cend(), seed);
520 return qHashMulti(seed, pointsHash, holesHash);
521}
522
523void QGeoPolygonPrivate::addHole(const QList<QGeoCoordinate> &holePath)
524{
525 for (const QGeoCoordinate &holeVertex: holePath)
526 if (!holeVertex.isValid())
527 return;
528
529 m_holesList << holePath;
530 // ToDo: mark clipper dirty when hole caching gets added
531}
532
533const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(qsizetype index) const
534{
535 return m_holesList.at(index);
536}
537
538void QGeoPolygonPrivate::removeHole(qsizetype index)
539{
540 if (index < 0 || index >= m_holesList.size())
541 return;
542
543 m_holesList.removeAt(index);
544 // ToDo: mark clipper dirty when hole caching gets added
545}
546
547qsizetype QGeoPolygonPrivate::holesCount() const
548{
549 return m_holesList.size();
550}
551
552bool QGeoPolygonPrivate::polygonContains(const QGeoCoordinate &coordinate) const
553{
554 ensureClipperPathUpdated(); // this one updates bbox too if needed
555
556 QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate);
557
558 if (coord.x() < m_leftBoundWrapped)
559 coord.setX(coord.x() + 1.0);
560
561 if (!m_clipperWrapper.pointInPolygon(coord))
562 return false;
563
564 // else iterates the holes List checking whether the point is contained inside the holes
565 for (const QList<QGeoCoordinate> &holePath : std::as_const(m_holesList)) {
566 // ToDo: cache these
567 QGeoPolygon holePolygon;
568 holePolygon.setPerimeter(holePath);
569 if (holePolygon.contains(coordinate))
570 return false;
571 }
572 return true;
573}
574
575void QGeoPolygonPrivate::markDirty()
576{
577 QGeoPathPrivateBase::markDirty();
578 m_clipperDirty.store(true, std::memory_order_release);
579}
580
581void QGeoPolygonPrivate::ensureClipperPathUpdated() const
582{
583 ensureBoundingBoxUpdated();
584 if (m_clipperDirty.load(std::memory_order_acquire)) {
585 const std::scoped_lock lock(globalPolygonMutex);
586 if (m_clipperDirty.load(std::memory_order_acquire)) {
587 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
588 QList<QDoubleVector2D> preservedPath;
589 preservedPath.reserve(m_path.size());
590 for (const QGeoCoordinate &c : std::as_const(m_path)) {
591 QDoubleVector2D crd = QWebMercator::coordToMercator(c);
592 if (crd.x() < m_leftBoundWrapped)
593 crd.setX(crd.x() + 1.0);
594 preservedPath << crd;
595 }
596 m_clipperWrapper.setPolygon(preservedPath);
597
598 m_clipperDirty.store(false, std::memory_order_release);
599 }
600 }
601}
602
603QGeoPolygonPrivateEager::QGeoPolygonPrivateEager() : QGeoPolygonPrivate()
604{
605 m_bboxDirty.store(false, std::memory_order_relaxed); // never dirty on the eager version
606}
607
608QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(const QList<QGeoCoordinate> &path) : QGeoPolygonPrivate(path)
609{
610 m_bboxDirty.store(false, std::memory_order_relaxed); // never dirty on the eager version
611 markDirty(); // calculate the cached values
612}
613
614QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(const QGeoPolygonPrivateEager &other)
615 : QGeoPolygonPrivate(other)
616{
617 m_bboxDirty.store(false, std::memory_order_relaxed); // never dirty on the eager version
618 markDirty(); // calculate the cached values
619}
620
621QGeoPolygonPrivateEager::~QGeoPolygonPrivateEager()
622{
623
624}
625
626QGeoShapePrivate *QGeoPolygonPrivateEager::clone() const
627{
628 return new QGeoPolygonPrivateEager(*this);
629}
630
631void QGeoPolygonPrivateEager::translate(double degreesLatitude, double degreesLongitude)
632{
633 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
634 m_clipperDirty.store(true, std::memory_order_release);
635}
636
637void QGeoPolygonPrivateEager::markDirty()
638{
639 m_clipperDirty.store(true, std::memory_order_release);
640 // do the calculations directly
641 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
642}
643
644void QGeoPolygonPrivateEager::addCoordinate(const QGeoCoordinate &coordinate)
645{
646 if (!coordinate.isValid())
647 return;
648 m_path.append(coordinate);
649 m_clipperDirty.store(true, std::memory_order_release);
650 updateBoundingBox(); // do not markDirty as it uses computeBoundingBox instead
651}
652
653void QGeoPolygonPrivateEager::updateBoundingBox()
654{
655 updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
656}
657
658QGeoPolygonEager::QGeoPolygonEager() : QGeoPolygon()
659{
660 d_ptr = new QGeoPolygonPrivateEager;
661}
662
663QGeoPolygonEager::QGeoPolygonEager(const QList<QGeoCoordinate> &path) : QGeoPolygon()
664{
665 d_ptr = new QGeoPolygonPrivateEager(path);
666}
667
668QGeoPolygonEager::QGeoPolygonEager(const QGeoPolygon &other) : QGeoPolygon()
669{
670 // without being able to dynamic_cast the d_ptr, only way to be sure is to reconstruct a new QGeoPolygonPrivateEager
671 d_ptr = new QGeoPolygonPrivateEager;
672 setPerimeter(other.perimeter());
673 for (qsizetype i = 0; i < other.holesCount(); i++)
674 addHole(other.holePath(i));
675}
676
677QGeoPolygonEager::QGeoPolygonEager(const QGeoShape &other) : QGeoPolygon()
678{
679 if (other.type() == QGeoShape::PolygonType)
680 *this = QGeoPolygonEager(QGeoPolygon(other));
681 else
682 d_ptr = new QGeoPolygonPrivateEager;
683}
684
685QGeoPolygonEager::~QGeoPolygonEager()
686{
687
688}
689
690QT_END_NAMESPACE
691
692#include "moc_qgeopolygon_p.cpp"
693#include "moc_qgeopolygon.cpp"
\inmodule QtPositioning
Definition qgeopolygon.h:17
static void calculatePeripheralPoints(QList< QGeoCoordinate > &path, const QGeoCircle &circle, int steps)
static void translatePoly(QList< QGeoCoordinate > &m_path, QList< QList< QGeoCoordinate > > &m_holesList, QGeoRectangle &m_bbox, double degreesLatitude, double degreesLongitude, double m_maxLati, double m_minLati)
constexpr auto kTooManyElements
constexpr auto kTooManyHoles
QT_BEGIN_NAMESPACE constexpr int kMaxInt
#define M_PI
Definition qmath.h:201