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
55
56
57
58
59
60
61
63inline QGeoPolygonPrivate *QGeoPolygon::d_func()
65 return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
68inline const QGeoPolygonPrivate *QGeoPolygon::d_func()
const
70 return static_cast<
const QGeoPolygonPrivate *>(d_ptr.constData());
74
75
76QGeoPolygon::QGeoPolygon()
77: QGeoShape(
new QGeoPolygonPrivate())
82
83
84
85QGeoPolygon::QGeoPolygon(
const QList<QGeoCoordinate> &path)
86: QGeoShape(
new QGeoPolygonPrivate(path))
91
92
93QGeoPolygon::QGeoPolygon(
const QGeoPolygon &other)
99 const QGeoCircle &circle,
102 const QGeoCoordinate ¢er = circle.center();
103 const qreal distance = circle.radius();
109 steps = qMax(steps, 3);
110 qreal centerLon = center.longitude();
111 qreal latRad = QLocationUtils::radians(center.latitude());
112 qreal lonRad = QLocationUtils::radians(centerLon);
113 qreal cosLatRad =
std::cos(latRad);
114 qreal sinLatRad =
std::sin(latRad);
115 qreal ratio = (distance / QLocationUtils::earthMeanRadius());
116 qreal cosRatio =
std::cos(ratio);
117 qreal sinRatio =
std::sin(ratio);
118 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
119 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
120 for (
int i = 0; i < steps; ++i) {
121 qreal azimuthRad = 2 *
M_PI * i / steps;
122 qreal resultLatRad =
std::asin(sinLatRad_x_cosRatio
123 + cosLatRad_x_sinRatio *
std::cos(azimuthRad));
124 qreal resultLonRad = lonRad +
std::atan2(
std::sin(azimuthRad) * cosLatRad_x_sinRatio,
125 cosRatio - sinLatRad *
std::sin(resultLatRad));
126 qreal lat2 = QLocationUtils::degrees(resultLatRad);
127 qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
129 path << QGeoCoordinate(lat2, lon2, center.altitude());
134
135
136QGeoPolygon::QGeoPolygon(
const QGeoShape &other)
139 if (type() != QGeoShape::PolygonType) {
140 QGeoPolygonPrivate *poly =
new QGeoPolygonPrivate();
141 if (type() == QGeoShape::CircleType) {
142 const QGeoCircle &circle =
static_cast<
const QGeoCircle &>(other);
143 QList<QGeoCoordinate> perimeter;
144 calculatePeripheralPoints(perimeter, circle, 128);
145 poly->setPath(perimeter);
146 }
else if (type() == QGeoShape::RectangleType) {
147 const QGeoRectangle &rect =
static_cast<
const QGeoRectangle &>(other);
148 QList<QGeoCoordinate> perimeter;
149 perimeter << rect.topLeft() << rect.topRight()
150 << rect.bottomRight() << rect.bottomLeft();
151 poly->setPath(perimeter);
158
159
160QGeoPolygon::~QGeoPolygon() {}
163
164
165QGeoPolygon &QGeoPolygon::operator=(
const QGeoPolygon &other)
167 QGeoShape::operator=(other);
172
173
174
175
176void QGeoPolygon::setPerimeter(
const QList<QGeoCoordinate> &path)
179 return d->setPath(path);
183
184
185
188
189
190
191
192const QList<QGeoCoordinate> &QGeoPolygon::perimeter()
const
194 Q_D(
const QGeoPolygon);
199
200
201
202
203
204void QGeoPolygon::translate(
double degreesLatitude,
double degreesLongitude)
207 d->translate(degreesLatitude, degreesLongitude);
211
212
213
214
215
216
217
218
219QGeoPolygon QGeoPolygon::translated(
double degreesLatitude,
double degreesLongitude)
const
221 QGeoPolygon result(*
this);
222 result.translate(degreesLatitude, degreesLongitude);
227
228
229
230double QGeoPolygon::length(qsizetype indexFrom, qsizetype indexTo)
const
232 Q_D(
const QGeoPolygon);
233 return d->length(indexFrom, indexTo);
237
238
239
240
241qsizetype QGeoPolygon::size()
const
243 Q_D(
const QGeoPolygon);
244 const qsizetype result = d->size();
245 if (result > kMaxInt)
246 qWarning() << kTooManyElements;
251
252
253void QGeoPolygon::addCoordinate(
const QGeoCoordinate &coordinate)
256 d->addCoordinate(coordinate);
257 if (d->size() > kMaxInt)
258 qWarning() << kTooManyElements;
262
263
264void QGeoPolygon::insertCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
267 d->insertCoordinate(index, coordinate);
271
272
273void QGeoPolygon::replaceCoordinate(qsizetype index,
const QGeoCoordinate &coordinate)
276 d->replaceCoordinate(index, coordinate);
280
281
282QGeoCoordinate QGeoPolygon::coordinateAt(qsizetype index)
const
284 Q_D(
const QGeoPolygon);
285 return d->coordinateAt(index);
289
290
291bool QGeoPolygon::containsCoordinate(
const QGeoCoordinate &coordinate)
const
293 Q_D(
const QGeoPolygon);
294 return d->containsCoordinate(coordinate);
298
299
300void QGeoPolygon::removeCoordinate(
const QGeoCoordinate &coordinate)
303 d->removeCoordinate(coordinate);
307
308
309void QGeoPolygon::removeCoordinate(qsizetype index)
312 d->removeCoordinate(index);
316
317
318QString QGeoPolygon::toString()
const
320 if (type() != QGeoShape::PolygonType) {
321 qWarning(
"Not a polygon");
322 return QStringLiteral(
"QGeoPolygon(not a polygon)");
326 for (
const auto &p : perimeter())
327 pathString += p.toString() + QLatin1StringView(
"; ");
330 return QStringLiteral(
"QGeoPolygon([ %1 ])").arg(pathString);
334
335
336
337
338
339void QGeoPolygon::addHole(
const QVariant &holePath)
341 QList<QGeoCoordinate> qgcHolePath;
342 if (holePath.canConvert<QVariantList>()) {
343 const QVariantList qvlHolePath = holePath.toList();
344 for (
const QVariant &vertex : qvlHolePath) {
345 if (vertex.canConvert<QGeoCoordinate>())
346 qgcHolePath << vertex.value<QGeoCoordinate>();
350 addHole(qgcHolePath);
354
355
356
357
358void QGeoPolygon::addHole(
const QList<QGeoCoordinate> &holePath)
361 d->addHole(holePath);
362 if (d->holesCount() > kMaxInt)
363 qDebug() << kTooManyHoles;
367
368
369
370
371
372const QVariantList QGeoPolygon::hole(qsizetype index)
const
374 Q_D(
const QGeoPolygon);
375 QVariantList holeCoordinates;
376 for (
const QGeoCoordinate &coords: d->holePath(index))
377 holeCoordinates << QVariant::fromValue(coords);
378 return holeCoordinates;
382
383
384
385
386const QList<QGeoCoordinate> QGeoPolygon::holePath(qsizetype index)
const
388 Q_D(
const QGeoPolygon);
389 return d->holePath(index);
393
394
395
396
397void QGeoPolygon::removeHole(qsizetype index)
400 return d->removeHole(index);
404
405
406
407
408qsizetype QGeoPolygon::holesCount()
const
410 Q_D(
const QGeoPolygon);
411 const qsizetype result = d->holesCount();
412 if (result > kMaxInt)
413 qWarning() << kTooManyHoles;
418
419
420
421
423QGeoPolygonPrivate::QGeoPolygonPrivate()
426 type = QGeoShape::PolygonType;
429QGeoPolygonPrivate::QGeoPolygonPrivate(
const QList<QGeoCoordinate> &path)
430: QGeoPathPrivate(path)
432 type = QGeoShape::PolygonType;
435QGeoPolygonPrivate::~QGeoPolygonPrivate() {}
437QGeoShapePrivate *QGeoPolygonPrivate::clone()
const
439 return new QGeoPolygonPrivate(*
this);
442bool QGeoPolygonPrivate::isValid()
const
444 return path().size() > 2;
447bool QGeoPolygonPrivate::contains(
const QGeoCoordinate &coordinate)
const
449 return polygonContains(coordinate);
453 QList<QList<QGeoCoordinate>> &m_holesList,
454 QGeoRectangle &m_bbox,
455 double degreesLatitude,
456 double degreesLongitude,
460 if (degreesLatitude > 0.0)
461 degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati);
463 degreesLatitude = qMax(degreesLatitude, -90.0 - m_minLati);
464 for (QGeoCoordinate &p: m_path) {
465 p.setLatitude(p.latitude() + degreesLatitude);
466 p.setLongitude(QLocationUtils::wrapLong(p.longitude() + degreesLongitude));
468 if (!m_holesList.isEmpty()){
469 for (QList<QGeoCoordinate> &hole: m_holesList){
470 for (QGeoCoordinate &holeVertex: hole){
471 holeVertex.setLatitude(holeVertex.latitude() + degreesLatitude);
472 holeVertex.setLongitude(QLocationUtils::wrapLong(holeVertex.longitude() + degreesLongitude));
476 m_bbox.translate(degreesLatitude, degreesLongitude);
479void QGeoPolygonPrivate::translate(
double degreesLatitude,
double degreesLongitude)
482 QList<
double> m_deltaXs;
483 double m_minX, m_maxX, m_minLati, m_maxLati;
485 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
486 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
487 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
488 m_clipperDirty =
true;
491bool QGeoPolygonPrivate::operator==(
const QGeoShapePrivate &other)
const
493 if (!QGeoShapePrivate::operator==(other))
496 const QGeoPolygonPrivate &otherPath =
static_cast<
const QGeoPolygonPrivate &>(other);
497 if (m_path.size() != otherPath.m_path.size()
498 || m_holesList.size() != otherPath.m_holesList.size())
500 return m_path == otherPath.m_path && m_holesList == otherPath.m_holesList;
503size_t QGeoPolygonPrivate::hash(size_t seed)
const
505 const size_t pointsHash = qHashRange(m_path.cbegin(), m_path.cend(), seed);
506 const size_t holesHash = qHashRange(m_holesList.cbegin(), m_holesList.cend(), seed);
507 return qHashMulti(seed, pointsHash, holesHash);
510void QGeoPolygonPrivate::addHole(
const QList<QGeoCoordinate> &holePath)
512 for (
const QGeoCoordinate &holeVertex: holePath)
513 if (!holeVertex.isValid())
516 m_holesList << holePath;
520const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(qsizetype index)
const
522 return m_holesList.at(index);
525void QGeoPolygonPrivate::removeHole(qsizetype index)
527 if (index < 0 || index >= m_holesList.size())
530 m_holesList.removeAt(index);
534qsizetype QGeoPolygonPrivate::holesCount()
const
536 return m_holesList.size();
539bool QGeoPolygonPrivate::polygonContains(
const QGeoCoordinate &coordinate)
const
542 const_cast<QGeoPolygonPrivate *>(
this)->updateClipperPath();
544 QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate);
546 if (coord.x() < m_leftBoundWrapped)
547 coord.setX(coord.x() + 1.0);
549 if (!m_clipperWrapper.pointInPolygon(coord))
553 for (
const QList<QGeoCoordinate> &holePath : std::as_const(m_holesList)) {
555 QGeoPolygon holePolygon;
556 holePolygon.setPerimeter(holePath);
557 if (holePolygon.contains(coordinate))
563void QGeoPolygonPrivate::markDirty()
565 m_bboxDirty = m_clipperDirty =
true;
568void QGeoPolygonPrivate::updateClipperPath()
571 computeBoundingBox();
572 m_clipperDirty =
false;
574 QList<QDoubleVector2D> preservedPath;
575 for (
const QGeoCoordinate &c : m_path) {
576 QDoubleVector2D crd = QWebMercator::coordToMercator(c);
577 if (crd.x() < m_leftBoundWrapped)
578 crd.setX(crd.x() + 1.0);
579 preservedPath << crd;
581 m_clipperWrapper.setPolygon(preservedPath);
584QGeoPolygonPrivateEager::QGeoPolygonPrivateEager() : QGeoPolygonPrivate()
589QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(
const QList<QGeoCoordinate> &path) : QGeoPolygonPrivate(path)
594QGeoPolygonPrivateEager::~QGeoPolygonPrivateEager()
599QGeoShapePrivate *QGeoPolygonPrivateEager::clone()
const
601 return new QGeoPolygonPrivate(*
this);
604void QGeoPolygonPrivateEager::translate(
double degreesLatitude,
double degreesLongitude)
606 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
607 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
608 m_clipperDirty =
true;
611void QGeoPolygonPrivateEager::markDirty()
613 m_clipperDirty =
true;
614 computeBoundingBox();
617void QGeoPolygonPrivateEager::addCoordinate(
const QGeoCoordinate &coordinate)
619 if (!coordinate.isValid())
621 m_path.append(coordinate);
622 m_clipperDirty =
true;
626void QGeoPolygonPrivateEager::computeBoundingBox()
628 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
629 m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
632void QGeoPolygonPrivateEager::updateBoundingBox()
634 updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
637QGeoPolygonEager::QGeoPolygonEager() : QGeoPolygon()
639 d_ptr =
new QGeoPolygonPrivateEager;
642QGeoPolygonEager::QGeoPolygonEager(
const QList<QGeoCoordinate> &path) : QGeoPolygon()
644 d_ptr =
new QGeoPolygonPrivateEager(path);
647QGeoPolygonEager::QGeoPolygonEager(
const QGeoPolygon &other) : QGeoPolygon()
650 d_ptr =
new QGeoPolygonPrivateEager;
651 setPerimeter(other.perimeter());
652 for (qsizetype i = 0; i < other.holesCount(); i++)
653 addHole(other.holePath(i));
656QGeoPolygonEager::QGeoPolygonEager(
const QGeoShape &other) : QGeoPolygon()
658 if (other.type() == QGeoShape::PolygonType)
659 *
this = QGeoPolygonEager(QGeoPolygon(other));
661 d_ptr =
new QGeoPolygonPrivateEager;
664QGeoPolygonEager::~QGeoPolygonEager()
671#include "moc_qgeopolygon_p.cpp"
672#include "moc_qgeopolygon.cpp"
Combined button and popup list for selecting options.
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