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
qdeclarativepolygonmapitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
7
8#include <QtCore/QScopedValueRollback>
9#include <qnumeric.h>
10#include <QPainterPath>
11
12#include <QtLocation/private/qgeomap_p.h>
13#include <QtPositioning/private/qlocationutils_p.h>
14#include <QtPositioning/private/qdoublevector2d_p.h>
15#include <QtPositioning/private/qclipperutils_p.h>
16#include <QtPositioning/private/qgeopolygon_p.h>
17#include <QtPositioning/private/qwebmercator_p.h>
18
20
109
114 const QList<QList <QDoubleVector2D>> &basePaths,
115 MapBorderBehaviour wrapping)
116{
117 // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes
118 // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly
119 if (!sourceDirty_)
120 return;
121 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
123 srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0.0, 0.0)); //avoid warning of NaN values if function is returned early
124 const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
125
126 QList<QList<QDoubleVector2D>> paths;
127
128 if (wrapping == WrapAround) {
129 // 0.1 Wrap the points around the globe if the path makes more sense that way.
130 // Ultimately, this is done if it is closer to walk around the day-border than the other direction
131 paths.reserve(basePaths.size());
132 for (qsizetype j = 0; j< basePaths.size(); j++) {
133 const QList<QDoubleVector2D> &bp = basePaths[j];
134 if (bp.isEmpty())
135 continue;
136 paths << QList<QDoubleVector2D>({bp[0]});
137 QList<QDoubleVector2D> &pp = paths[j];
138 pp.reserve(bp.size());
139 for (qsizetype i = 1; i < bp.size(); i++) {
140 if (bp[i].x() > pp.last().x() + 0.5)
141 pp << bp[i] - QDoubleVector2D(1.0, 0.0);
142 else if (bp[i].x() < pp.last().x() - 0.5)
143 pp << bp[i] + QDoubleVector2D(1.0, 0.0);
144 else
145 pp << bp[i];
146 }
147 }
148
149 // 0.2 Check and include one of the poles if necessary to make sense out of the polygon
150 for (qsizetype j = 0; j < paths.size(); j++) {
151 QList<QDoubleVector2D> &pp = paths[j];
152
153 if (pp.last().x() - pp.first().x() < -0.5) {
154 for (qsizetype i = 0; i < floor(pp.length()/2.); i++)
155 pp.swapItemsAt(i, pp.length() - i - 1);
156 }
157 if (pp.last().x() - pp.first().x() > 0.5) {
158
159 const double leftBorder = cameraRect.left();
160 const double rightBorder = cameraRect.right();
161
162 qsizetype originalPathLength = pp.length();
163
164 if (pp.last().x() < rightBorder) {
165 for (qsizetype i = 0; i < originalPathLength; i++)
166 pp.append(pp[i] + QDoubleVector2D(1.0, 0.0));
167 }
168 if (pp.first().x() > leftBorder) {
169 for (qsizetype i = 0; i < originalPathLength; i++)
170 pp.insert(i, pp[2*i] - QDoubleVector2D(1.0, 0.0));
171 }
172 const double newPoleLat = (pp.first().y() + pp.last().y() < 1.0) ? 0.0 : 1.0; //mean of y < 0.5?
173 const QDoubleVector2D P1 = pp.first();
174 const QDoubleVector2D P2 = pp.last();
175 pp.push_front(QDoubleVector2D(P1.x(), newPoleLat));
176 pp.append(QDoubleVector2D(P2.x(), newPoleLat));
177
178 wrapping = DrawOnce;
179 }
180 }
181 } else {
182 paths = basePaths;
183 }
184
185 //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible
186 // The viewport is periodic in x-direction in the interval [-1; 1].
187 // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1;
188 QList<QList<QDoubleVector2D>> wrappedPaths;
189
190 if (wrapping == Duplicate || wrapping == WrapAround) {
191 QRectF itemRect;
192 for (const auto &path : paths)
194
195 for (double xoffset : {-1.0, 0.0, 1.0}) {
196 if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset, 0.0))))
197 continue;
198 for (const auto &path : paths) {
199 wrappedPaths.append(QList<QDoubleVector2D>());
200 QList<QDoubleVector2D> &wP = wrappedPaths.last();
201 wP.reserve(path.size());
202 for (const QDoubleVector2D &coord : path)
203 wP.append(coord+QDoubleVector2D(xoffset, 0.0));
204 }
205 }
206 } else {
207 wrappedPaths = paths;
208 }
209
210 if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle
211 return;
212
213
214 //2 The polygons that are at least partially in the viewport are cliped to reduce their size
215 QList<QList<QDoubleVector2D>> clippedPaths;
216 const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded();
217 for (const auto &path : wrappedPaths) {
218 if (visibleRegion.size()) {
219 QClipperUtils clipper;
220 clipper.addSubjectPath(path, true);
221 clipper.addClipPolygon(visibleRegion);
222 clippedPaths << clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd,
224 }
225 else {
226 clippedPaths.append(path); //Do we really need this if there are no visible regions??
227 }
228 }
229 if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion
230 return;
231
232 QRectF bb;
233 for (const auto &path: clippedPaths)
235 //Offset by origin, find the maximum coordinate
236 srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top()));
237 QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections
238 maxCoord_ = 0.0;
239 for (const auto &path: clippedPaths) {
240 QDoubleVector2D prevPoint = p.wrappedMapProjectionToItemPosition(path.at(0)) - origin;
241 QDoubleVector2D nextPoint = p.wrappedMapProjectionToItemPosition(path.at(1)) - origin;
242 srcPath_.moveTo(prevPoint.toPointF());
243 maxCoord_ = qMax(maxCoord_, qMax(prevPoint.x(), prevPoint.y()));
244 qsizetype pointsAdded = 1;
245 for (qsizetype i = 1; i < path.size(); ++i) {
246 const QDoubleVector2D point = nextPoint;
247
248 if (qMax(point.x(), point.y()) > maxCoord_)
249 maxCoord_ = qMax(point.x(), point.y());
250
251 if (i == path.size() - 1) {
252 srcPath_.lineTo(point.toPointF()); //close the path
253 } else {
254 nextPoint = p.wrappedMapProjectionToItemPosition(path.at(i+1)) - origin;
255
256 bool addPoint = ( i > pointsAdded * 10 || //make sure that at least every 10th point is drawn
257 path.size() < 10 ); //draw small paths completely
258
259 const double tolerance = 0.1;
260 if (!addPoint) { //add the point to the shape if it deflects the boundary by more than the tolerance
262 point.x(), point.y(),
263 nextPoint.x(), nextPoint.y(),
264 prevPoint.x(), prevPoint.y());
265 addPoint = addPoint || (dsqr > (tolerance*tolerance));
266 }
267
268 if (addPoint) {
269 srcPath_.lineTo(point.toPointF());
270 pointsAdded++;
271 prevPoint = point;
272 }
273
274 }
275 }
277 }
278
279 if (!assumeSimple_)
281
283}
284
285/*
286 * QDeclarativePolygonMapItem Private Implementations
287 */
288
292
295{
296 m_shape = new QQuickShape(&m_poly);
297 m_shape->setObjectName("_qt_map_item_shape");
298 m_shape->setZ(-1);
300
303
304 auto pathElements = m_shapePath->pathElements();
305 pathElements.append(&pathElements, m_painterPath);
306
307 auto shapePaths = m_shape->data();
308 shapePaths.append(&shapePaths, m_shapePath);
309}
310
315
317{
318 if (m_poly.m_geopoly.perimeter().length() == 0) { // Possibly cleared
320 m_poly.setWidth(0);
321 m_poly.setHeight(0);
322 m_shape->setVisible(false);
323 return;
324 }
325 const QGeoMap *map = m_poly.map();
326 const qreal borderWidth = m_poly.m_border.width();
327 QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
329
334
336
338
339 const bool hasBorder = m_poly.m_border.color().alpha() != 0 && m_poly.m_border.width() > 0;
341 m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f);
343
345 path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth);
346 path.closeSubpath();
348
349 m_poly.setSize(bb.size() + QSize(2 * borderWidth, 2 * borderWidth));
352 m_shape->setVisible(true);
353
354 m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth));
355}
356
367
369{
370 return m_shape->contains(m_poly.mapToItem(m_shape, point));
371}
372
373/*
374 * QDeclarativePolygonMapItem Implementation
375 */
376
378: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent),
379 m_updatingGeometry(false)
381
382{
383 // ToDo: handle envvar, and switch implementation.
387 // ToDo: fix this, only flag material?
393 this, [this]() { m_d->onGeoGeometryChanged(); });
394}
395
399
415{
416 return &m_border;
417}
418
428
438QList<QGeoCoordinate> QDeclarativePolygonMapItem::path() const
439{
440 return m_geopoly.perimeter();
441}
442
443void QDeclarativePolygonMapItem::setPath(const QList<QGeoCoordinate> &path)
444{
445 // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList
446 if (m_geopoly.perimeter() == path)
447 return;
448
450
451 m_d->onGeoGeometryChanged();
453}
454
464{
465 if (!coordinate.isValid())
466 return;
467
468 m_geopoly.addCoordinate(coordinate);
469 m_d->onGeoGeometryUpdated();
471}
472
484{
486 m_geopoly.removeCoordinate(coordinate);
487 if (m_geopoly.perimeter().length() == length)
488 return;
489
490 m_d->onGeoGeometryChanged();
492}
493
506
508{
509 if (m_color == color)
510 return;
511
512 m_color = color;
513 polishAndUpdate(); // in case color was transparent and now is not or vice versa
515}
516
521{
522 return m_d->updateMapItemPaintNode(oldNode, data);
523}
524
529{
530 if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
531 return;
532 m_d->updatePolish();
533}
534
536{
537 m_d->markSourceDirtyAndUpdate();
538}
539
541{
542 m_d->onLinePropertiesChanged();
543}
544
549{
550 if (event.mapSize.isEmpty())
551 return;
552
553 m_d->afterViewportChanged();
554}
555
560{
561 return m_d->contains(point);
562}
563
565{
566 return m_geopoly;
567}
568
570{
571 if (shape == m_geopoly)
572 return;
573
575 m_d->onGeoGeometryChanged();
577}
578
582void QDeclarativePolygonMapItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
583{
584 if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopoly.isValid() || m_updatingGeometry) {
585 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
586 return;
587 }
588 // TODO: change the algorithm to preserve the distances and size!
589 QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false);
590 QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false);
591 if (!newCenter.isValid() || !oldCenter.isValid())
592 return;
593 double offsetLongi = newCenter.longitude() - oldCenter.longitude();
594 double offsetLati = newCenter.latitude() - oldCenter.latitude();
595 if (offsetLati == 0.0 && offsetLongi == 0.0)
596 return;
597
598 m_geopoly.translate(offsetLati, offsetLongi);
599 m_d->onGeoGeometryChanged();
601
602 // Not calling QDeclarativeGeoMapItemBase::geometryChange() as it will be called from a nested
603 // call to this function.
604}
605
607
void addSubjectPath(const QList< QDoubleVector2D > &path, bool closed)
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
int alpha() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1466
virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset)
void setShapeTriangulationScale(QQuickShape *shape, qreal maxCoord) const
virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
QLocation::ReferenceSurface referenceSurface
QDeclarativeGeoMap * quickMap() const
void setPath(const QPainterPath &path)
void widthChanged(qreal width)
void colorChanged(const QColor &color)
bool contains(const QPointF &point) const override
QList< QList< QDoubleVector2D > > m_geopathProjected
QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon)
QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate)
\qmlmethod void MapPolygon::addCoordinate(coordinate)
bool contains(const QPointF &point) const override
void setGeoShape(const QGeoShape &shape) override
std::unique_ptr< QDeclarativePolygonMapItemPrivate > m_d
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override
QSGNode * updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override
QDeclarativeMapLineProperties * border
\qmlpropertygroup Location::MapPolygon::border \qmlproperty int MapPolygon::border....
const QGeoShape & geoShape() const override
void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override
void setPath(const QList< QGeoCoordinate > &value)
Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate)
\qmlmethod void MapPolygon::removeCoordinate(coordinate)
QDeclarativeMapLineProperties m_border
QDeclarativePolygonMapItem(QQuickItem *parent=nullptr)
Q_DECL_CONSTEXPR QPointF toPointF() const
Q_DECL_CONSTEXPR double x() const
Q_DECL_CONSTEXPR double y() const
\inmodule QtPositioning
bool isValid
This property holds the validity of this geo coordinate.
QRectF sourceBoundingBox() const
const QGeoCoordinate & origin() const
void updateSourcePoints(const QGeoMap &map, const QList< QList< QDoubleVector2D > > &path, MapBorderBehaviour wrapping=Duplicate)
QGeoMapPolygonGeometry()
\qmltype MapPolygon \instantiates QDeclarativePolygonMapItem \inqmlmodule QtLocation
const QGeoProjection & geoProjection() const
Definition qgeomap.cpp:164
@ MapPolygon
Definition qgeomap_p.h:51
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.
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
virtual QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport=true) const =0
\inmodule QtPositioning
Definition qgeoshape.h:17
bool isValid
This property holds the validity of the geo shape.
Definition qgeoshape.h:20
Definition qlist.h:75
qsizetype length() const noexcept
Definition qlist.h:399
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:127
\inmodule QtGui
void translate(qreal dx, qreal dy)
Translates all elements in the path by ({dx}, {dy}).
void moveTo(const QPointF &p)
Moves the current point to the given point, implicitly starting a new subpath and closing the previou...
QPainterPath simplified() const
QRectF boundingRect() const
Returns the bounding rectangle of this painter path as a rectangle with floating point precision.
void closeSubpath()
Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting ...
void lineTo(const QPointF &p)
Adds a straight line from the current position to the given endPoint.
\inmodule QtCore\reentrant
Definition qpoint.h:217
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setOpacity(qreal)
void setSize(const QSizeF &size)
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
Q_INVOKABLE QPointF mapToItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within item's coordinat...
void setHeight(qreal)
QSizeF size() const
void setVisible(bool)
void setZ(qreal)
void setWidth(qreal)
QQmlListProperty< QQuickPathElement > pathElements
\qmlproperty list<PathElement> QtQuick::Path::pathElements This property holds the objects composing ...
void setStrokeColor(const QColor &color)
void setStrokeWidth(qreal w)
void setFillColor(const QColor &color)
bool contains(const QPointF &point) const override
\qmlmethod bool QtQuick::Item::contains(point point)
void setContainsMode(ContainsMode containsMode)
FINALQQmlListProperty< QObject > data
\qmlproperty list<Object> QtQuick.Shapes::Shape::data
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr QRectF translated(qreal dx, qreal dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:762
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:511
constexpr QPointF center() const noexcept
Returns the center point of the rectangle.
Definition qrect.h:699
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
\inmodule QtCore
Definition qsize.h:25
#define this
Definition dialogs.cpp:9
QMap< QString, QString > map
[6]
void colorChanged()
QRectF boundingRectangleFromList(const QList< QDoubleVector2D > &list)
double distanceSqrPointLine(double p0_x, double p0_y, double p1_x, double p1_y, double p2_x, double p2_y)
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ transparent
Definition qnamespace.h:47
static void addPoint(QPolygon &a, const QPoint &p)
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLint GLint GLint GLint GLint x
[0]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint color
[2]
GLsizei const GLuint * paths
GLint GLint xoffset
struct _cl_event * event
GLuint coord
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187