Qt 6.x
The Qt SDK
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
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]});
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++) {
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)
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);
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
290{
291}
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
312{
313 delete m_shape;
314}
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();
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
359{
360 Q_UNUSED(data);
361 delete oldNode;
364 }
365 return nullptr;
366}
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 [=]() {m_d->onGeoGeometryChanged();});
394}
395
397{
398}
399
415{
416 return &m_border;
417}
418
423{
425 if (map)
426 m_d->onMapSet();
427}
428
439{
440 return m_geopoly.perimeter();
441}
442
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
503{
504 return m_color;
505}
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 addClipPolygon(const QList< QDoubleVector2D > &path)
void addSubjectPath(const QList< QDoubleVector2D > &path, bool closed)
QList< QList< QDoubleVector2D > > execute(Operation op, PolyFillType subjFillType=pftNonZero, PolyFillType clipFillType=pftNonZero)
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
void colorChanged(const QColor &color)
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
double longitude
This property holds the longitude in decimal degrees.
double latitude
This property holds the latitude in decimal degrees.
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:74
qsizetype size() const noexcept
Definition qlist.h:386
void push_front(rvalue_ref t)
Definition qlist.h:674
bool isEmpty() const noexcept
Definition qlist.h:390
T & first()
Definition qlist.h:628
T & last()
Definition qlist.h:631
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
void swapItemsAt(qsizetype i, qsizetype j)
Definition qlist.h:664
qsizetype length() const noexcept
Definition qlist.h:388
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:114
\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:214
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
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:483
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:748
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:496
bool intersects(const QRectF &r) const noexcept
Returns true if this rectangle intersects with the given rectangle (i.e.
Definition qrect.cpp:2263
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
constexpr QPointF center() const noexcept
Returns the center point of the rectangle.
Definition qrect.h:685
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:497
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:498
\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]
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.
@ transparent
Definition qnamespace.h:46
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
GLsizei const GLuint * paths
GLint GLint xoffset
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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:70
double qreal
Definition qtypes.h:92
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent