20QT_IMPL_METATYPE_EXTERN(QGeoPolygon)
22constexpr int kMaxInt = std::numeric_limits<
int>::max();
23constexpr auto kTooManyHoles = u"The polygon has more holes than fit into an int. "
24 "This can cause errors while querying holes from QML";
25constexpr auto kTooManyElements = u"The polygon has more elements than fit into an int. "
26 "This can cause errors while querying elements from QML";
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
56
57
58
59
60
61
62
64inline QGeoPolygonPrivate *QGeoPolygon::d_func()
66 return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
69inline const QGeoPolygonPrivate *QGeoPolygon::d_func()
const
71 return static_cast<
const QGeoPolygonPrivate *>(d_ptr.constData());
75
76
77QGeoPolygon::QGeoPolygon()
78: QGeoShape(
new QGeoPolygonPrivate())
83
84
85
86QGeoPolygon::QGeoPolygon(
const QList<QGeoCoordinate> &path)
87: QGeoShape(
new QGeoPolygonPrivate(path))
92
93
94QGeoPolygon::QGeoPolygon(
const QGeoPolygon &other)
100 const QGeoCircle &circle,
103 const QGeoCoordinate ¢er = circle.center();
104 const qreal distance = circle.radius();
110 steps = qMax(steps, 3);
111 qreal centerLon = center.longitude();
112 qreal latRad = QLocationUtils::radians(center.latitude());
113 qreal lonRad = QLocationUtils::radians(centerLon);
114 qreal cosLatRad =
std::cos(latRad);
115 qreal sinLatRad =
std::sin(latRad);
116 qreal ratio = (distance / QLocationUtils::earthMeanRadius());
117 qreal cosRatio =
std::cos(ratio);
118 qreal sinRatio =
std::sin(ratio);
119 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
120 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
121 for (
int i = 0; i < steps; ++i) {
122 qreal azimuthRad = 2 *
M_PI * i / steps;
123 qreal resultLatRad =
std::asin(sinLatRad_x_cosRatio
124 + cosLatRad_x_sinRatio *
std::cos(azimuthRad));
125 qreal resultLonRad = lonRad +
std::atan2(
std::sin(azimuthRad) * cosLatRad_x_sinRatio,
126 cosRatio - sinLatRad *
std::sin(resultLatRad));
127 qreal lat2 = QLocationUtils::degrees(resultLatRad);
128 qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
130 path << QGeoCoordinate(lat2, lon2, center.altitude());
135
136
137QGeoPolygon::QGeoPolygon(
const QGeoShape &other)
140 if (type() != QGeoShape::PolygonType) {
141 QGeoPolygonPrivate *poly =
new QGeoPolygonPrivate();
142 if (type() == QGeoShape::CircleType) {
143 const QGeoCircle &circle =
static_cast<
const QGeoCircle &>(other);
144 QList<QGeoCoordinate> perimeter;
145 calculatePeripheralPoints(perimeter, circle, 128);
146 poly->setPath(perimeter);
147 }
else if (type() == QGeoShape::RectangleType) {
148 const QGeoRectangle &rect =
static_cast<
const QGeoRectangle &>(other);
149 QList<QGeoCoordinate> perimeter;
150 perimeter << rect.topLeft() << rect.topRight()
151 << rect.bottomRight() << rect.bottomLeft();
152 poly->setPath(perimeter);
159
160
161QGeoPolygon::~QGeoPolygon() {}
164
165
166QGeoPolygon &QGeoPolygon::operator=(
const QGeoPolygon &other)
168 QGeoShape::operator=(other);
173
174
175
176
177void QGeoPolygon::setPerimeter(
const QList<QGeoCoordinate> &path)
180 return d->setPath(path);
184
185
186
189
190
191
192
193const QList<QGeoCoordinate> &QGeoPolygon::perimeter()
const
195 Q_D(
const QGeoPolygon);
200
201
202
203
204
205void QGeoPolygon::translate(
double degreesLatitude,
double degreesLongitude)
208 d->translate(degreesLatitude, degreesLongitude);
212
213
214
215
216
217
218
219
220QGeoPolygon QGeoPolygon::translated(
double degreesLatitude,
double degreesLongitude)
const
222 QGeoPolygon result(*
this);
223 result.translate(degreesLatitude, degreesLongitude);
228
229
230
231double QGeoPolygon::length(qsizetype indexFrom, qsizetype indexTo)
const
233 Q_D(
const QGeoPolygon);
234 return d->length(indexFrom, indexTo);
238
239
240
241
242qsizetype QGeoPolygon::size()
const
244 Q_D(
const QGeoPolygon);
245 const qsizetype result = d->size();
246 if (result > kMaxInt)
247 qWarning() << kTooManyElements;
252
253
254void QGeoPolygon::addCoordinate(
const QGeoCoordinate &coordinate)
257 d->addCoordinate(coordinate);
258 if (d->size() > kMaxInt)
259 qWarning() << kTooManyElements;
263
264
265void QGeoPolygon::insertCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
268 d->insertCoordinate(index, coordinate);
272
273
274void QGeoPolygon::replaceCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
277 d->replaceCoordinate(index, coordinate);
281
282
283QGeoCoordinate QGeoPolygon::coordinateAt(qsizetype index)
const
285 Q_D(
const QGeoPolygon);
286 return d->coordinateAt(index);
290
291
292bool QGeoPolygon::containsCoordinate(
const QGeoCoordinate &coordinate)
const
294 Q_D(
const QGeoPolygon);
295 return d->containsCoordinate(coordinate);
299
300
301void QGeoPolygon::removeCoordinate(
const QGeoCoordinate &coordinate)
304 d->removeCoordinate(coordinate);
308
309
310void QGeoPolygon::removeCoordinate(qsizetype index)
313 d->removeCoordinate(index);
317
318
319QString QGeoPolygon::toString()
const
321 if (type() != QGeoShape::PolygonType) {
322 qWarning(
"Not a polygon");
323 return QStringLiteral(
"QGeoPolygon(not a polygon)");
327 for (
const auto &p : perimeter())
328 pathString += p.toString() + QLatin1StringView(
"; ");
331 return QStringLiteral(
"QGeoPolygon([ %1 ])").arg(pathString);
335
336
337
338
339
340void QGeoPolygon::addHole(
const QVariant &holePath)
342 QList<QGeoCoordinate> qgcHolePath;
343 if (holePath.canConvert<QVariantList>()) {
344 const QVariantList qvlHolePath = holePath.toList();
345 for (
const QVariant &vertex : qvlHolePath) {
346 if (vertex.canConvert<QGeoCoordinate>())
347 qgcHolePath << vertex.value<QGeoCoordinate>();
351 addHole(qgcHolePath);
355
356
357
358
359void QGeoPolygon::addHole(
const QList<QGeoCoordinate> &holePath)
362 d->addHole(holePath);
363 if (d->holesCount() > kMaxInt)
364 qDebug() << kTooManyHoles;
368
369
370
371
372
373const QVariantList QGeoPolygon::hole(qsizetype index)
const
375 Q_D(
const QGeoPolygon);
376 QVariantList holeCoordinates;
377 for (
const QGeoCoordinate &coords: d->holePath(index))
378 holeCoordinates << QVariant::fromValue(coords);
379 return holeCoordinates;
383
384
385
386
387const QList<QGeoCoordinate> QGeoPolygon::holePath(qsizetype index)
const
389 Q_D(
const QGeoPolygon);
390 return d->holePath(index);
394
395
396
397
398void QGeoPolygon::removeHole(qsizetype index)
401 return d->removeHole(index);
405
406
407
408
409qsizetype QGeoPolygon::holesCount()
const
411 Q_D(
const QGeoPolygon);
412 const qsizetype result = d->holesCount();
413 if (result > kMaxInt)
414 qWarning() << kTooManyHoles;
419
420
421
422
424QGeoPolygonPrivate::QGeoPolygonPrivate()
427 type = QGeoShape::PolygonType;
430QGeoPolygonPrivate::QGeoPolygonPrivate(
const QList<QGeoCoordinate> &path)
431: QGeoPathPrivate(path)
433 type = QGeoShape::PolygonType;
436QGeoPolygonPrivate::~QGeoPolygonPrivate() {}
438QGeoShapePrivate *QGeoPolygonPrivate::clone()
const
440 return new QGeoPolygonPrivate(*
this);
443bool QGeoPolygonPrivate::isValid()
const
445 return path().size() > 2;
448bool QGeoPolygonPrivate::contains(
const QGeoCoordinate &coordinate)
const
450 return polygonContains(coordinate);
454 QList<QList<QGeoCoordinate>> &m_holesList,
455 QGeoRectangle &m_bbox,
456 double degreesLatitude,
457 double degreesLongitude,
461 if (degreesLatitude > 0.0)
462 degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati);
464 degreesLatitude = qMax(degreesLatitude, -90.0 - m_minLati);
465 for (QGeoCoordinate &p: m_path) {
466 p.setLatitude(p.latitude() + degreesLatitude);
467 p.setLongitude(QLocationUtils::wrapLong(p.longitude() + degreesLongitude));
469 if (!m_holesList.isEmpty()){
470 for (QList<QGeoCoordinate> &hole: m_holesList){
471 for (QGeoCoordinate &holeVertex: hole){
472 holeVertex.setLatitude(holeVertex.latitude() + degreesLatitude);
473 holeVertex.setLongitude(QLocationUtils::wrapLong(holeVertex.longitude() + degreesLongitude));
477 m_bbox.translate(degreesLatitude, degreesLongitude);
480void QGeoPolygonPrivate::translate(
double degreesLatitude,
double degreesLongitude)
483 QList<
double> m_deltaXs;
484 double m_minX, m_maxX, m_minLati, m_maxLati;
486 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
487 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
488 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
489 m_clipperDirty =
true;
492bool QGeoPolygonPrivate::operator==(
const QGeoShapePrivate &other)
const
494 if (!QGeoShapePrivate::operator==(other))
497 const QGeoPolygonPrivate &otherPath =
static_cast<
const QGeoPolygonPrivate &>(other);
498 if (m_path.size() != otherPath.m_path.size()
499 || m_holesList.size() != otherPath.m_holesList.size())
501 return m_path == otherPath.m_path && m_holesList == otherPath.m_holesList;
504size_t QGeoPolygonPrivate::hash(size_t seed)
const
506 const size_t pointsHash = qHashRange(m_path.cbegin(), m_path.cend(), seed);
507 const size_t holesHash = qHashRange(m_holesList.cbegin(), m_holesList.cend(), seed);
508 return qHashMulti(seed, pointsHash, holesHash);
511void QGeoPolygonPrivate::addHole(
const QList<QGeoCoordinate> &holePath)
513 for (
const QGeoCoordinate &holeVertex: holePath)
514 if (!holeVertex.isValid())
517 m_holesList << holePath;
521const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(qsizetype index)
const
523 return m_holesList.at(index);
526void QGeoPolygonPrivate::removeHole(qsizetype index)
528 if (index < 0 || index >= m_holesList.size())
531 m_holesList.removeAt(index);
535qsizetype QGeoPolygonPrivate::holesCount()
const
537 return m_holesList.size();
540bool QGeoPolygonPrivate::polygonContains(
const QGeoCoordinate &coordinate)
const
543 const_cast<QGeoPolygonPrivate *>(
this)->updateClipperPath();
545 QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate);
547 if (coord.x() < m_leftBoundWrapped)
548 coord.setX(coord.x() + 1.0);
550 if (!m_clipperWrapper.pointInPolygon(coord))
554 for (
const QList<QGeoCoordinate> &holePath : std::as_const(m_holesList)) {
556 QGeoPolygon holePolygon;
557 holePolygon.setPerimeter(holePath);
558 if (holePolygon.contains(coordinate))
564void QGeoPolygonPrivate::markDirty()
566 m_bboxDirty = m_clipperDirty =
true;
569void QGeoPolygonPrivate::updateClipperPath()
572 computeBoundingBox();
573 m_clipperDirty =
false;
575 QList<QDoubleVector2D> preservedPath;
576 for (
const QGeoCoordinate &c : m_path) {
577 QDoubleVector2D crd = QWebMercator::coordToMercator(c);
578 if (crd.x() < m_leftBoundWrapped)
579 crd.setX(crd.x() + 1.0);
580 preservedPath << crd;
582 m_clipperWrapper.setPolygon(preservedPath);
585QGeoPolygonPrivateEager::QGeoPolygonPrivateEager() : QGeoPolygonPrivate()
590QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(
const QList<QGeoCoordinate> &path) : QGeoPolygonPrivate(path)
595QGeoPolygonPrivateEager::~QGeoPolygonPrivateEager()
600QGeoShapePrivate *QGeoPolygonPrivateEager::clone()
const
602 return new QGeoPolygonPrivate(*
this);
605void QGeoPolygonPrivateEager::translate(
double degreesLatitude,
double degreesLongitude)
607 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
608 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
609 m_clipperDirty =
true;
612void QGeoPolygonPrivateEager::markDirty()
614 m_clipperDirty =
true;
615 computeBoundingBox();
618void QGeoPolygonPrivateEager::addCoordinate(
const QGeoCoordinate &coordinate)
620 if (!coordinate.isValid())
622 m_path.append(coordinate);
623 m_clipperDirty =
true;
627void QGeoPolygonPrivateEager::computeBoundingBox()
629 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
630 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
633void QGeoPolygonPrivateEager::updateBoundingBox()
635 updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
638QGeoPolygonEager::QGeoPolygonEager() : QGeoPolygon()
640 d_ptr =
new QGeoPolygonPrivateEager;
643QGeoPolygonEager::QGeoPolygonEager(
const QList<QGeoCoordinate> &path) : QGeoPolygon()
645 d_ptr =
new QGeoPolygonPrivateEager(path);
648QGeoPolygonEager::QGeoPolygonEager(
const QGeoPolygon &other) : QGeoPolygon()
651 d_ptr =
new QGeoPolygonPrivateEager;
652 setPerimeter(other.perimeter());
653 for (qsizetype i = 0; i < other.holesCount(); i++)
654 addHole(other.holePath(i));
657QGeoPolygonEager::QGeoPolygonEager(
const QGeoShape &other) : QGeoPolygon()
659 if (other.type() == QGeoShape::PolygonType)
660 *
this = QGeoPolygonEager(QGeoPolygon(other));
662 d_ptr =
new QGeoPolygonPrivateEager;
665QGeoPolygonEager::~QGeoPolygonEager()
672#include "moc_qgeopolygon_p.cpp"
673#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