19#include <QtCore/qmutex.h>
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";
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
60
61
62
63
64
65
66
68inline QGeoPolygonPrivate *QGeoPolygon::d_func()
70 return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
73inline const QGeoPolygonPrivate *QGeoPolygon::d_func()
const
75 return static_cast<
const QGeoPolygonPrivate *>(d_ptr.constData());
79
80
81QGeoPolygon::QGeoPolygon()
82: QGeoShape(
new QGeoPolygonPrivate())
87
88
89
90QGeoPolygon::QGeoPolygon(
const QList<QGeoCoordinate> &path)
91: QGeoShape(
new QGeoPolygonPrivate(path))
96
97
98QGeoPolygon::QGeoPolygon(
const QGeoPolygon &other)
104 const QGeoCircle &circle,
107 const QGeoCoordinate ¢er = circle.center();
108 const qreal distance = circle.radius();
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));
134 path << QGeoCoordinate(lat2, lon2, center.altitude());
139
140
141QGeoPolygon::QGeoPolygon(
const QGeoShape &other)
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);
163
164
165QGeoPolygon::~QGeoPolygon() {}
168
169
170QGeoPolygon &QGeoPolygon::operator=(
const QGeoPolygon &other)
172 QGeoShape::operator=(other);
177
178
179
180
181void QGeoPolygon::setPerimeter(
const QList<QGeoCoordinate> &path)
184 return d->setPath(path);
188
189
190
193
194
195
196
197const QList<QGeoCoordinate> &QGeoPolygon::perimeter()
const
199 Q_D(
const QGeoPolygon);
204
205
206
207
208
209void QGeoPolygon::translate(
double degreesLatitude,
double degreesLongitude)
212 d->translate(degreesLatitude, degreesLongitude);
216
217
218
219
220
221
222
223
224QGeoPolygon QGeoPolygon::translated(
double degreesLatitude,
double degreesLongitude)
const
226 QGeoPolygon result(*
this);
227 result.translate(degreesLatitude, degreesLongitude);
232
233
234
235double QGeoPolygon::length(qsizetype indexFrom, qsizetype indexTo)
const
237 Q_D(
const QGeoPolygon);
238 return d->length(indexFrom, indexTo);
242
243
244
245
246qsizetype QGeoPolygon::size()
const
248 Q_D(
const QGeoPolygon);
249 const qsizetype result = d->size();
250 if (result > kMaxInt)
251 qWarning() << kTooManyElements;
256
257
258void QGeoPolygon::addCoordinate(
const QGeoCoordinate &coordinate)
261 d->addCoordinate(coordinate);
262 if (d->size() > kMaxInt)
263 qWarning() << kTooManyElements;
267
268
269void QGeoPolygon::insertCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
272 d->insertCoordinate(index, coordinate);
276
277
278void QGeoPolygon::replaceCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
281 d->replaceCoordinate(index, coordinate);
285
286
287QGeoCoordinate QGeoPolygon::coordinateAt(qsizetype index)
const
289 Q_D(
const QGeoPolygon);
290 return d->coordinateAt(index);
294
295
296bool QGeoPolygon::containsCoordinate(
const QGeoCoordinate &coordinate)
const
298 Q_D(
const QGeoPolygon);
299 return d->containsCoordinate(coordinate);
303
304
305void QGeoPolygon::removeCoordinate(
const QGeoCoordinate &coordinate)
308 d->removeCoordinate(coordinate);
312
313
314void QGeoPolygon::removeCoordinate(qsizetype index)
317 d->removeCoordinate(index);
321
322
323QString QGeoPolygon::toString()
const
325 if (type() != QGeoShape::PolygonType) {
326 qWarning(
"Not a polygon");
327 return QStringLiteral(
"QGeoPolygon(not a polygon)");
331 for (
const auto &p : perimeter())
332 pathString += p.toString() + QLatin1StringView(
"; ");
335 return QStringLiteral(
"QGeoPolygon([ %1 ])").arg(pathString);
339
340
341
342
343
344void QGeoPolygon::addHole(
const QVariant &holePath)
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>();
355 addHole(qgcHolePath);
359
360
361
362
363void QGeoPolygon::addHole(
const QList<QGeoCoordinate> &holePath)
366 d->addHole(holePath);
367 if (d->holesCount() > kMaxInt)
368 qDebug() << kTooManyHoles;
372
373
374
375
376
377const QVariantList QGeoPolygon::hole(qsizetype index)
const
379 Q_D(
const QGeoPolygon);
380 QVariantList holeCoordinates;
381 for (
const QGeoCoordinate &coords: d->holePath(index))
382 holeCoordinates << QVariant::fromValue(coords);
383 return holeCoordinates;
387
388
389
390
391const QList<QGeoCoordinate> QGeoPolygon::holePath(qsizetype index)
const
393 Q_D(
const QGeoPolygon);
394 return d->holePath(index);
398
399
400
401
402void QGeoPolygon::removeHole(qsizetype index)
405 return d->removeHole(index);
409
410
411
412
413qsizetype QGeoPolygon::holesCount()
const
415 Q_D(
const QGeoPolygon);
416 const qsizetype result = d->holesCount();
417 if (result > kMaxInt)
418 qWarning() << kTooManyHoles;
423
424
425
426
428Q_CONSTINIT
static QBasicMutex globalPolygonMutex;
430QGeoPolygonPrivate::QGeoPolygonPrivate()
431: QGeoPathPrivateBase()
433 type = QGeoShape::PolygonType;
436QGeoPolygonPrivate::QGeoPolygonPrivate(
const QList<QGeoCoordinate> &path)
437: QGeoPathPrivateBase(path)
439 type = QGeoShape::PolygonType;
442QGeoPolygonPrivate::QGeoPolygonPrivate(
const QGeoPolygonPrivate &other)
443 : QGeoPathPrivateBase(other),
444 m_holesList(other.m_holesList)
446 type = QGeoShape::PolygonType;
452QGeoPolygonPrivate::~QGeoPolygonPrivate() {}
454QGeoShapePrivate *QGeoPolygonPrivate::clone()
const
456 return new QGeoPolygonPrivate(*
this);
459bool QGeoPolygonPrivate::isValid()
const
461 return path().size() > 2;
464bool QGeoPolygonPrivate::contains(
const QGeoCoordinate &coordinate)
const
466 return polygonContains(coordinate);
470 QList<QList<QGeoCoordinate>> &m_holesList,
471 QGeoRectangle &m_bbox,
472 double degreesLatitude,
473 double degreesLongitude,
477 if (degreesLatitude > 0.0)
478 degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati);
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));
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));
493 m_bbox.translate(degreesLatitude, degreesLongitude);
496void QGeoPolygonPrivate::translate(
double degreesLatitude,
double degreesLongitude)
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);
507bool QGeoPolygonPrivate::operator==(
const QGeoShapePrivate &other)
const
509 if (!QGeoPathPrivateBase::operator==(other))
512 const QGeoPolygonPrivate &otherPolygon =
static_cast<
const QGeoPolygonPrivate &>(other);
513 return m_holesList == otherPolygon.m_holesList;
516size_t QGeoPolygonPrivate::hash(size_t seed)
const
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);
523void QGeoPolygonPrivate::addHole(
const QList<QGeoCoordinate> &holePath)
525 for (
const QGeoCoordinate &holeVertex: holePath)
526 if (!holeVertex.isValid())
529 m_holesList << holePath;
533const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(qsizetype index)
const
535 return m_holesList.at(index);
538void QGeoPolygonPrivate::removeHole(qsizetype index)
540 if (index < 0 || index >= m_holesList.size())
543 m_holesList.removeAt(index);
547qsizetype QGeoPolygonPrivate::holesCount()
const
549 return m_holesList.size();
552bool QGeoPolygonPrivate::polygonContains(
const QGeoCoordinate &coordinate)
const
554 ensureClipperPathUpdated();
556 QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate);
558 if (coord.x() < m_leftBoundWrapped)
559 coord.setX(coord.x() + 1.0);
561 if (!m_clipperWrapper.pointInPolygon(coord))
565 for (
const QList<QGeoCoordinate> &holePath : std::as_const(m_holesList)) {
567 QGeoPolygon holePolygon;
568 holePolygon.setPerimeter(holePath);
569 if (holePolygon.contains(coordinate))
575void QGeoPolygonPrivate::markDirty()
577 QGeoPathPrivateBase::markDirty();
578 m_clipperDirty.store(
true, std::memory_order_release);
581void QGeoPolygonPrivate::ensureClipperPathUpdated()
const
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;
596 m_clipperWrapper.setPolygon(preservedPath);
598 m_clipperDirty.store(
false, std::memory_order_release);
603QGeoPolygonPrivateEager::QGeoPolygonPrivateEager() : QGeoPolygonPrivate()
605 m_bboxDirty.store(
false, std::memory_order_relaxed);
608QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(
const QList<QGeoCoordinate> &path) : QGeoPolygonPrivate(path)
610 m_bboxDirty.store(
false, std::memory_order_relaxed);
614QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(
const QGeoPolygonPrivateEager &other)
615 : QGeoPolygonPrivate(other)
617 m_bboxDirty.store(
false, std::memory_order_relaxed);
621QGeoPolygonPrivateEager::~QGeoPolygonPrivateEager()
626QGeoShapePrivate *QGeoPolygonPrivateEager::clone()
const
628 return new QGeoPolygonPrivateEager(*
this);
631void QGeoPolygonPrivateEager::translate(
double degreesLatitude,
double degreesLongitude)
633 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
634 m_clipperDirty.store(
true, std::memory_order_release);
637void QGeoPolygonPrivateEager::markDirty()
639 m_clipperDirty.store(
true, std::memory_order_release);
641 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
644void QGeoPolygonPrivateEager::addCoordinate(
const QGeoCoordinate &coordinate)
646 if (!coordinate.isValid())
648 m_path.append(coordinate);
649 m_clipperDirty.store(
true, std::memory_order_release);
653void QGeoPolygonPrivateEager::updateBoundingBox()
655 updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
658QGeoPolygonEager::QGeoPolygonEager() : QGeoPolygon()
660 d_ptr =
new QGeoPolygonPrivateEager;
663QGeoPolygonEager::QGeoPolygonEager(
const QList<QGeoCoordinate> &path) : QGeoPolygon()
665 d_ptr =
new QGeoPolygonPrivateEager(path);
668QGeoPolygonEager::QGeoPolygonEager(
const QGeoPolygon &other) : QGeoPolygon()
671 d_ptr =
new QGeoPolygonPrivateEager;
672 setPerimeter(other.perimeter());
673 for (qsizetype i = 0; i < other.holesCount(); i++)
674 addHole(other.holePath(i));
677QGeoPolygonEager::QGeoPolygonEager(
const QGeoShape &other) : QGeoPolygon()
679 if (other.type() == QGeoShape::PolygonType)
680 *
this = QGeoPolygonEager(QGeoPolygon(other));
682 d_ptr =
new QGeoPolygonPrivateEager;
685QGeoPolygonEager::~QGeoPolygonEager()
692#include "moc_qgeopolygon_p.cpp"
693#include "moc_qgeopolygon.cpp"
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