Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "qgeopolygon.h"
5#include "qgeopolygon_p.h"
6#include "qgeopath_p.h"
7#include "qgeocircle.h"
8
9#include "qgeocoordinate.h"
10#include "qnumeric.h"
11#include "qlocationutils_p.h"
12#include "qwebmercator_p.h"
13
14#include "qdoublevector2d_p.h"
15#include "qdoublevector3d_p.h"
16#include "qwebmercator_p.h"
17
19
21
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";
27
55/*
56 \property QGeoPolygon::path
57 \brief This property holds the list of coordinates for the geo polygon.
58
59 The polygon is both invalid and empty if it contains no coordinate.
60
61 A default constructed QGeoPolygon is therefore invalid.
62*/
63
64inline QGeoPolygonPrivate *QGeoPolygon::d_func()
65{
66 return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
67}
68
69inline const QGeoPolygonPrivate *QGeoPolygon::d_func() const
70{
71 return static_cast<const QGeoPolygonPrivate *>(d_ptr.constData());
72}
73
81
86QGeoPolygon::QGeoPolygon(const QList<QGeoCoordinate> &path)
88{
89}
90
98
99static void calculatePeripheralPoints(QList<QGeoCoordinate> &path,
100 const QGeoCircle &circle,
101 int steps)
102{
103 const QGeoCoordinate &center = circle.center();
104 const qreal distance = circle.radius();
105 // Calculate points based on great-circle distance
106 // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
107 // but tweaked here for computing multiple points
108
109 // pre-calculations
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);
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);
129
130 path << QGeoCoordinate(lat2, lon2, center.altitude());
131 }
132}
133
139{
140 if (type() != QGeoShape::PolygonType) {
142 if (type() == QGeoShape::CircleType) {
143 const QGeoCircle &circle = static_cast<const QGeoCircle &>(other);
144 QList<QGeoCoordinate> perimeter;
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);
153 }
154 d_ptr = poly;
155 }
156}
157
162
171
177void QGeoPolygon::setPerimeter(const QList<QGeoCoordinate> &path)
178{
179 Q_D(QGeoPolygon);
180 return d->setPath(path);
181}
182
188const QList<QGeoCoordinate> &QGeoPolygon::perimeter() const
189{
190 Q_D(const QGeoPolygon);
191 return d->path();
192}
193
200void QGeoPolygon::translate(double degreesLatitude, double degreesLongitude)
201{
202 Q_D(QGeoPolygon);
203 d->translate(degreesLatitude, degreesLongitude);
204}
205
215QGeoPolygon QGeoPolygon::translated(double degreesLatitude, double degreesLongitude) const
216{
217 QGeoPolygon result(*this);
218 result.translate(degreesLatitude, degreesLongitude);
219 return result;
220}
221
226double QGeoPolygon::length(qsizetype indexFrom, qsizetype indexTo) const
227{
228 Q_D(const QGeoPolygon);
229 return d->length(indexFrom, indexTo);
230}
231
238{
239 Q_D(const QGeoPolygon);
240 const qsizetype result = d->size();
241 if (result > kMaxInt)
243 return result;
244}
245
250{
251 Q_D(QGeoPolygon);
252 d->addCoordinate(coordinate);
253 if (d->size() > kMaxInt)
255}
256
261{
262 Q_D(QGeoPolygon);
263 d->insertCoordinate(index, coordinate);
264}
265
270{
271 Q_D(QGeoPolygon);
272 d->replaceCoordinate(index, coordinate);
273}
274
279{
280 Q_D(const QGeoPolygon);
281 return d->coordinateAt(index);
282}
283
288{
289 Q_D(const QGeoPolygon);
290 return d->containsCoordinate(coordinate);
291}
292
297{
298 Q_D(QGeoPolygon);
299 d->removeCoordinate(coordinate);
300}
301
306{
307 Q_D(QGeoPolygon);
308 d->removeCoordinate(index);
309}
310
315{
316 if (type() != QGeoShape::PolygonType) {
317 qWarning("Not a polygon");
318 return QStringLiteral("QGeoPolygon(not a polygon)");
319 }
320
322 for (const auto &p : perimeter())
323 pathString += p.toString() + QLatin1Char(',');
324
325 return QStringLiteral("QGeoPolygon([ %1 ])").arg(pathString);
326}
327
334void QGeoPolygon::addHole(const QVariant &holePath)
335{
336 QList<QGeoCoordinate> qgcHolePath;
337 if (holePath.canConvert<QVariantList>()) {
338 const QVariantList qvlHolePath = holePath.toList();
339 for (const QVariant &vertex : qvlHolePath) {
340 if (vertex.canConvert<QGeoCoordinate>())
341 qgcHolePath << vertex.value<QGeoCoordinate>();
342 }
343 }
344 //ToDo: add QGeoShape support
345 addHole(qgcHolePath);
346}
347
353void QGeoPolygon::addHole(const QList<QGeoCoordinate> &holePath)
354{
355 Q_D(QGeoPolygon);
356 d->addHole(holePath);
357 if (d->holesCount() > kMaxInt)
359}
360
368{
369 Q_D(const QGeoPolygon);
370 QVariantList holeCoordinates;
371 for (const QGeoCoordinate &coords: d->holePath(index))
372 holeCoordinates << QVariant::fromValue(coords);
373 return holeCoordinates;
374}
375
381const QList<QGeoCoordinate> QGeoPolygon::holePath(qsizetype index) const
382{
383 Q_D(const QGeoPolygon);
384 return d->holePath(index);
385}
386
393{
394 Q_D(QGeoPolygon);
395 return d->removeHole(index);
396}
397
404{
405 Q_D(const QGeoPolygon);
406 const qsizetype result = d->holesCount();
407 if (result > kMaxInt)
409 return result;
410}
411
412/*******************************************************************************
413 *
414 * QGeoPathPrivate & friends
415 *
416*******************************************************************************/
417
423
429
431
433{
434 return new QGeoPolygonPrivate(*this);
435}
436
438{
439 return path().size() > 2;
440}
441
442bool QGeoPolygonPrivate::contains(const QGeoCoordinate &coordinate) const
443{
444 return polygonContains(coordinate);
445}
446
447inline static void translatePoly( QList<QGeoCoordinate> &m_path,
448 QList<QList<QGeoCoordinate>> &m_holesList,
449 QGeoRectangle &m_bbox,
450 double degreesLatitude,
451 double degreesLongitude,
452 double m_maxLati,
453 double m_minLati)
454{
455 if (degreesLatitude > 0.0)
456 degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati);
457 else
458 degreesLatitude = qMax(degreesLatitude, -90.0 - m_minLati);
459 for (QGeoCoordinate &p: m_path) {
460 p.setLatitude(p.latitude() + degreesLatitude);
461 p.setLongitude(QLocationUtils::wrapLong(p.longitude() + degreesLongitude));
462 }
463 if (!m_holesList.isEmpty()){
464 for (QList<QGeoCoordinate> &hole: m_holesList){
465 for (QGeoCoordinate &holeVertex: hole){
466 holeVertex.setLatitude(holeVertex.latitude() + degreesLatitude);
467 holeVertex.setLongitude(QLocationUtils::wrapLong(holeVertex.longitude() + degreesLongitude));
468 }
469 }
470 }
471 m_bbox.translate(degreesLatitude, degreesLongitude);
472}
473
474void QGeoPolygonPrivate::translate(double degreesLatitude, double degreesLongitude)
475{
476 // Need min/maxLati, so update bbox
477 QList<double> m_deltaXs;
478 double m_minX, m_maxX, m_minLati, m_maxLati;
479 m_bboxDirty = false; // Updated in translatePoly
480 computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox);
481 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
483 m_clipperDirty = true;
484}
485
487{
488 if (!QGeoShapePrivate::operator==(other)) // checks type
489 return false;
490
491 const QGeoPolygonPrivate &otherPath = static_cast<const QGeoPolygonPrivate &>(other);
492 if (m_path.size() != otherPath.m_path.size()
493 || m_holesList.size() != otherPath.m_holesList.size())
494 return false;
495 return m_path == otherPath.m_path && m_holesList == otherPath.m_holesList;
496}
497
498size_t QGeoPolygonPrivate::hash(size_t seed) const
499{
500 const size_t pointsHash = qHashRange(m_path.cbegin(), m_path.cend(), seed);
501 const size_t holesHash = qHashRange(m_holesList.cbegin(), m_holesList.cend(), seed);
502 return qHashMulti(seed, pointsHash, holesHash);
503}
504
505void QGeoPolygonPrivate::addHole(const QList<QGeoCoordinate> &holePath)
506{
507 for (const QGeoCoordinate &holeVertex: holePath)
508 if (!holeVertex.isValid())
509 return;
510
512 // ToDo: mark clipper dirty when hole caching gets added
513}
514
515const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(qsizetype index) const
516{
517 return m_holesList.at(index);
518}
519
521{
522 if (index < 0 || index >= m_holesList.size())
523 return;
524
526 // ToDo: mark clipper dirty when hole caching gets added
527}
528
533
535{
536 if (m_clipperDirty)
537 const_cast<QGeoPolygonPrivate *>(this)->updateClipperPath(); // this one updates bbox too if needed
538
540
541 if (coord.x() < m_leftBoundWrapped)
542 coord.setX(coord.x() + 1.0);
543
545 return false;
546
547 // else iterates the holes List checking whether the point is contained inside the holes
548 for (const QList<QGeoCoordinate> &holePath : std::as_const(m_holesList)) {
549 // ToDo: cache these
550 QGeoPolygon holePolygon;
551 holePolygon.setPerimeter(holePath);
552 if (holePolygon.contains(coordinate))
553 return false;
554 }
555 return true;
556}
557
562
564{
565 if (m_bboxDirty)
567 m_clipperDirty = false;
568
569 QList<QDoubleVector2D> preservedPath;
570 for (const QGeoCoordinate &c : m_path) {
572 if (crd.x() < m_leftBoundWrapped)
573 crd.setX(crd.x() + 1.0);
574 preservedPath << crd;
575 }
576 m_clipperWrapper.setPolygon(preservedPath);
577}
578
580{
581 m_bboxDirty = false; // never dirty on the eager version
582}
583
585{
586 m_bboxDirty = false; // never dirty on the eager version
587}
588
593
598
599void QGeoPolygonPrivateEager::translate(double degreesLatitude, double degreesLongitude)
600{
601 translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
603 m_clipperDirty = true;
604}
605
611
613{
614 if (!coordinate.isValid())
615 return;
616 m_path.append(coordinate);
617 m_clipperDirty = true;
618 updateBoundingBox(); // do not markDirty as it uses computeBoundingBox instead
619}
620
626
631
636
637QGeoPolygonEager::QGeoPolygonEager(const QList<QGeoCoordinate> &path) : QGeoPolygon()
638{
640}
641
643{
644 // without being able to dynamic_cast the d_ptr, only way to be sure is to reconstruct a new QGeoPolygonPrivateEager
646 setPerimeter(other.perimeter());
647 for (qsizetype i = 0; i < other.holesCount(); i++)
648 addHole(other.holePath(i));
649}
650
658
663
665
666#include "moc_qgeopolygon_p.cpp"
667#include "moc_qgeopolygon.cpp"
void setPolygon(const QList< QDoubleVector2D > &polygon)
static int pointInPolygon(const QDoubleVector2D &point, const QList< QDoubleVector2D > &polygon)
\inmodule QtPositioning
Definition qgeocircle.h:15
QGeoCoordinate center
This property holds the center coordinate for the geo circle.
Definition qgeocircle.h:17
qreal radius
This property holds the circle radius in meters.
Definition qgeocircle.h:18
\inmodule QtPositioning
bool isValid
This property holds the validity of this geo coordinate.
QList< QGeoCoordinate > m_path
Definition qgeopath_p.h:175
QGeoRectangle m_bbox
Definition qgeopath_p.h:177
virtual void computeBoundingBox()
Definition qgeopath.cpp:612
virtual const QList< QGeoCoordinate > & path() const
Definition qgeopath.cpp:393
double m_leftBoundWrapped
Definition qgeopath_p.h:178
virtual void addCoordinate(const QGeoCoordinate &coordinate) override
QList< double > m_deltaXs
virtual void markDirty() override
virtual void translate(double degreesLatitude, double degreesLongitude) override
virtual QGeoShapePrivate * clone() const override
virtual void computeBoundingBox() override
QClipperUtils m_clipperWrapper
virtual void removeHole(qsizetype index)
qsizetype holesCount() const
virtual bool contains(const QGeoCoordinate &coordinate) const override
QList< QList< QGeoCoordinate > > m_holesList
bool polygonContains(const QGeoCoordinate &coordinate) const
virtual bool isValid() const override
const QList< QGeoCoordinate > holePath(qsizetype index) const
virtual void addHole(const QList< QGeoCoordinate > &holePath)
virtual void updateClipperPath()
virtual void markDirty() override
size_t hash(size_t seed) const override
virtual void translate(double degreesLatitude, double degreesLongitude) override
virtual bool operator==(const QGeoShapePrivate &other) const override
virtual QGeoShapePrivate * clone() const override
\inmodule QtPositioning
Definition qgeopolygon.h:16
Q_INVOKABLE qsizetype holesCount() const
Returns the number of holes.
~QGeoPolygon()
Destroys this polygon.
Q_INVOKABLE void replaceCoordinate(qsizetype index, const QGeoCoordinate &coordinate)
Replaces the path element at the specified index with coordinate.
const QList< QGeoCoordinate > holePath(qsizetype index) const
Returns a QList<QGeoCoordinate> which represents the hole at index.
void setPerimeter(const QList< QGeoCoordinate > &path)
Sets the perimeter of the polygon based on a list of coordinates path.
Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate)
Removes the last occurrence of coordinate from the polygon.
QGeoPolygon & operator=(const QGeoPolygon &other)
Assigns other to this geo polygon and returns a reference to this geo polygon.
Q_INVOKABLE void insertCoordinate(qsizetype index, const QGeoCoordinate &coordinate)
Inserts coordinate at the specified index.
Q_INVOKABLE void removeHole(qsizetype index)
Removes element at position index from the list of holes.
Q_INVOKABLE QGeoPolygon translated(double degreesLatitude, double degreesLongitude) const
Returns a copy of this geo polygon translated by degreesLatitude northwards and degreesLongitude east...
Q_INVOKABLE double length(qsizetype indexFrom=0, qsizetype indexTo=-1) const
Returns the length of the polygon's perimeter, in meters, from the element indexFrom to the element i...
Q_INVOKABLE QString toString() const
Returns the geo polygon properties as a string.
Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate) const
Returns true if the polygon's perimeter contains coordinate as one of the elements.
Q_INVOKABLE const QVariantList hole(qsizetype index) const
Returns a QVariant containing a QList<QGeoCoordinate> which represents the hole at index.
Q_INVOKABLE QGeoCoordinate coordinateAt(qsizetype index) const
Returns the coordinate at index .
QGeoPolygon()
Constructs a new, empty geo polygon.
Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude)
Translates this geo polygon by degreesLatitude northwards and degreesLongitude eastwards.
Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate)
Appends coordinate to the polygon.
QList< QGeoCoordinate > perimeter
Definition qgeopolygon.h:18
Q_INVOKABLE void addHole(const QVariant &holePath)
Sets the holePath for a hole inside the polygon.
Q_INVOKABLE qsizetype size() const
Returns the number of elements in the polygon.
\inmodule QtPositioning
QGeoCoordinate topLeft
This property holds the top left coordinate of this geo rectangle.
Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude)
Translates this geo rectangle by degreesLatitude northwards and degreesLongitude eastwards.
\inmodule QtPositioning
Definition qgeoshape.h:17
QGeoShape & operator=(const QGeoShape &other)
Assigns other to this geo shape and returns a reference to this geo shape.
QSharedDataPointer< QGeoShapePrivate > d_ptr
Definition qgeoshape.h:61
ShapeType type
This property holds the type of this geo shape.
Definition qgeoshape.h:19
@ PolygonType
Definition qgeoshape.h:35
@ RectangleType
Definition qgeoshape.h:32
qsizetype size() const noexcept
Definition qlist.h:397
void removeAt(qsizetype i)
Definition qlist.h:590
QList< T > toList() const noexcept
Definition qlist.h:723
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
T value(qsizetype i) const
Definition qlist.h:664
const_iterator cend() const noexcept
Definition qlist.h:631
void append(parameter_type t)
Definition qlist.h:458
const_iterator cbegin() const noexcept
Definition qlist.h:630
static double radians(double degrees)
static double degrees(double radians)
static double earthMeanRadius()
static double wrapLong(double lng)
const T * constData() const noexcept
Returns a const pointer to the shared data object.
Definition qshareddata.h:51
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qvariant.h:65
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
static QDoubleVector2D coordToMercator(const QGeoCoordinate &coord)
while(i.hasNext()) QString s
rect
[4]
Combined button and popup list for selecting options.
static void updateBBox(const QList< QGeoCoordinate > &m_path, QList< double > &m_deltaXs, double &m_minX, double &m_maxX, double &m_minLati, double &m_maxLati, QGeoRectangle &m_bbox)
Definition qgeopath_p.h:79
static QT_BEGIN_NAMESPACE void computeBBox(const QList< QGeoCoordinate > &m_path, QList< double > &m_deltaXs, double &m_minX, double &m_maxX, double &m_minLati, double &m_maxLati, QGeoRectangle &m_bbox)
Definition qgeopath_p.h:27
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
size_t qHashRange(InputIterator first, InputIterator last, size_t seed=0) noexcept(noexcept(qHash(*first)))
constexpr QtPrivate::QHashMultiReturnType< T... > qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v< QtPrivate::QNothrowHashable< T >... >)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define M_PI
Definition qmath.h:209
#define QT_IMPL_METATYPE_EXTERN(TYPE)
Definition qmetatype.h:1390
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint index
[2]
GLsizei const GLubyte GLsizei GLenum const void * coords
GLenum GLsizei const void * pathString
GLsizei GLsizei GLfloat distance
GLenum type
const GLubyte * c
GLuint coord
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define QStringLiteral(str)
static const QTextHtmlElement elements[Html_NumElements]
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
QSharedPointer< T > other(t)
[5]
\inmodule QtCore \reentrant
Definition qchar.h:18