Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qdeclarativecirclemapitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 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
6
7#include <QtCore/QScopedValueRollback>
8#include <QPen>
9#include <qgeocircle.h>
10
11#include <QtGui/private/qtriangulator_p.h>
12#include <QtLocation/private/qgeomap_p.h>
13#include <QtPositioning/private/qlocationutils_p.h>
14
15#include <qmath.h>
16#include <algorithm>
17
18#include <QtQuick/private/qquickitem_p.h>
19
21
103struct Vertex
104{
106};
107
109{
110}
111
113: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent),
114 m_updatingGeometry(false)
116{
117 // ToDo: handle envvar, and switch implementation.
125 [=]() {m_d->onGeoGeometryChanged();});
126}
127
129{
130}
131
145{
146 return &m_border;
147}
148
150{
151 m_d->markSourceDirtyAndUpdate();
152}
153
155{
156 m_d->onLinePropertiesChanged();
157}
158
160{
162 if (map)
163 m_d->onMapSet();
164}
165
174{
175 if (m_circle.center() == center)
176 return;
177
178 m_circle.setCenter(center);
179 m_d->onGeoGeometryChanged();
181}
182
184{
185 return m_circle.center();
186}
187
195{
196 if (m_color == color)
197 return;
198
199 m_color = color;
200 polishAndUpdate(); // in case color was transparent and now is not or vice versa
201 emit colorChanged(m_color);
202}
203
205{
206 return m_color;
207}
208
217{
218 if (m_circle.radius() == radius)
219 return;
220
221 m_circle.setRadius(radius);
222 m_d->onGeoGeometryChanged();
224}
225
227{
228 return m_circle.radius();
229}
230
245{
246 return m_d->updateMapItemPaintNode(oldNode, data);
247}
248
253{
254 if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
255 return;
256 m_d->updatePolish();
257}
258
263{
264 if (event.mapSize.isEmpty())
265 return;
266
267 m_d->afterViewportChanged();
268}
269
274{
275 return m_d->contains(point);
276}
277
279{
280 return m_circle;
281}
282
284{
285 if (shape == m_circle)
286 return;
287
288 const QGeoCircle circle(shape); // if shape isn't a circle, circle will be created as a default-constructed circle
289 const bool centerHasChanged = circle.center() != m_circle.center();
290 const bool radiusHasChanged = circle.radius() != m_circle.radius();
291 m_circle = circle;
292
293 m_d->onGeoGeometryChanged();
294 if (centerHasChanged)
295 emit centerChanged(m_circle.center());
296 if (radiusHasChanged)
297 emit radiusChanged(m_circle.radius());
298}
299
303void QDeclarativeCircleMapItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
304{
305 if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) {
306 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
307 return;
308 }
309
310 QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5;
311 QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false);
312 if (newCoordinate.isValid())
313 setCenter(newCoordinate); // ToDo: this is incorrect. setting such center might yield to another geometry changed.
314
315 // Not calling QDeclarativeGeoMapItemBase::geometryChange() as it will be called from a nested
316 // call to this function.
317}
318
320{
321}
322
325{
327 m_shape->setObjectName("_qt_map_item_shape");
328 m_shape->setZ(-1);
330
333
334 auto pathElements = m_shapePath->pathElements();
335 pathElements.append(&pathElements, m_painterPath);
336
337 auto shapePaths = m_shape->data();
338 shapePaths.append(&shapePaths, m_shapePath);
339}
340
342{
343 delete m_shape;
344}
345
346/*
347 * A workaround for circle path to be drawn correctly using a polygon geometry
348 * This method generates a polygon like
349 * ______________
350 * | ____ |
351 * \__/ \__/
352 */
354 const QGeoCoordinate &center,
356{
357 const qreal poleLat = 90;
358 const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0));
359 const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0));
360 const bool crossNorthPole = distanceToNorthPole < distance;
361 const bool crossSouthPole = distanceToSouthPole < distance;
362
363 if (!crossNorthPole && !crossSouthPole)
364 return;
365
366 if (crossNorthPole && crossSouthPole)
367 return;
368
369 const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
370 const qreal xAtBorder = cameraRect.left();
371
372 // The strategy is to order the points from left to right as they appear on the screen.
373 // Then add the 3 missing sides that form the box for painting at the front and at the end of the list.
374 // We ensure that the box aligns with the cameraRect in order to avoid rendering it twice (wrap around).
375 // Notably, this leads to outlines at the right side of the map.
376 // Set xAtBorder to 0.0 to avoid this, however, for an increased rendering cost.
377 for (auto &c : path) {
378 c.setX(c.x());
379 while (c.x() - xAtBorder > 1.0)
380 c.setX(c.x() - 1.0);
381 while (c.x() - xAtBorder < 0.0)
382 c.setX(c.x() + 1.0);
383 }
384
385 std::sort(path.begin(), path.end(),
386 [](const QDoubleVector2D &a, const QDoubleVector2D &b) -> bool
387 {return a.x() < b.x();});
388
389 const qreal newPoleLat = crossNorthPole ? -0.1 : 1.1;
390 const QDoubleVector2D P1 = path.first() + QDoubleVector2D(1.0, 0.0);
391 const QDoubleVector2D P2 = path.last() - QDoubleVector2D(1.0, 0.0);
392 path.push_front(P2);
393 path.push_front(QDoubleVector2D(P2.x(), newPoleLat));
394 path.append(P1);
395 path.append(QDoubleVector2D(P1.x(), newPoleLat));
396}
397
399{
400 qreal poleLat = 90;
401 QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
402 QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
403 // approximate using great circle distance
404 qreal distanceToNorthPole = center.distanceTo(northPole);
405 qreal distanceToSouthPole = center.distanceTo(southPole);
406 return (distanceToNorthPole < distance? 1 : 0) +
407 (distanceToSouthPole < distance? 1 : 0);
408}
409
411 const QGeoCoordinate &center,
414 int steps)
415{
416 const double lambda = 0.0001;
417 const QDoubleVector2D c = p.geoToMapProjection(center);
418 const double lambda_geo = center.distanceTo(p.mapProjectionToGeo(c + QDoubleVector2D(lambda, 0)));
419 const qreal mapDistance = distance * lambda / lambda_geo;
420
421 for (int i = 0; i < steps; ++i) {
422 const qreal rad = 2 * M_PI * i / steps;
423 path << c + QDoubleVector2D(cos(rad), sin(rad)) * mapDistance;
424 }
425}
426
428 const QGeoCoordinate &center,
431 int steps)
432{
433 // Calculate points based on great-circle distance
434 // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
435 // but tweaked here for computing multiple points
436
437 // pre-calculations
438 steps = qMax(steps, 3);
439 qreal centerLon = center.longitude();
440 qreal latRad = QLocationUtils::radians(center.latitude());
441 qreal lonRad = QLocationUtils::radians(centerLon);
442 qreal cosLatRad = std::cos(latRad);
443 qreal sinLatRad = std::sin(latRad);
445 qreal cosRatio = std::cos(ratio);
446 qreal sinRatio = std::sin(ratio);
447 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
448 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
449 for (int i = 0; i < steps; ++i) {
450 const qreal azimuthRad = 2 * M_PI * i / steps;
451 const qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
452 + cosLatRad_x_sinRatio * std::cos(azimuthRad));
453 const qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
454 cosRatio - sinLatRad * std::sin(resultLatRad));
455 const qreal lat2 = QLocationUtils::degrees(resultLatRad);
456 qreal lon2 = QLocationUtils::degrees(resultLonRad);
457
458 //Workaround as QGeoCoordinate does not take Longitudes outside [-180,180]
459 qreal offset = 0.0;
460 while (lon2 > 180.0) {
461 offset += 1.0;
462 lon2 -= 360.0;
463 }
464 while (lon2 < -180.0) {
465 offset -= 1.0;
466 lon2 += 360.0;
467 }
468 path << p.geoToMapProjection(QGeoCoordinate(lat2, lon2, center.altitude())) + QDoubleVector2D(offset, 0.0);
469 }
470}
471
473
475{
476 if (!m_circle.m_circle.isValid()) {
480 m_shape->setVisible(false);
481 return;
482 }
483
485 QScopedValueRollback<bool> rollback(m_circle.m_updatingGeometry);
486 m_circle.m_updatingGeometry = true;
487
489
490 const QGeoCoordinate &center = m_circle.m_circle.center();
491 const qreal &radius = m_circle.m_circle.radius();
492
493 // if circle crosses north/south pole, then don't preserve circular shape,
494 int crossingPoles = m_circle.referenceSurface() == QLocation::ReferenceSurface::Globe ? crossEarthPole(center, radius) : 0;
495 if (crossingPoles == 1) { // If the circle crosses both poles, we will remove it from a rectangle
496 includeOnePoleInPath(circlePath, center, radius, p);
498 }
499 else if (crossingPoles == 2) { // If the circle crosses both poles, we will remove it from a rectangle
500 // The circle covers both poles. This appears on the map as a total fill with a hole on the opposite side of the planet
501 // This can be represented by a rectangle that spans the entire planet with a hole defined by the calculated points.
502 // The points on one side have to be wraped around the globe
503 const qreal centerX = p.geoToMapProjection(center).x();
504 for (int i = 0; i < circlePath.count(); i++) {
505 if (circlePath.at(i).x() > centerX)
506 circlePath[i].setX(circlePath.at(i).x() - 1.0);
507 }
511 QList<QDoubleVector2D> surroundingRect;
512 if (cameraRect.contains(circleRect)){
513 cameraRect = cameraRect.adjusted(-0.1, -0.1, 0.2, 0.2);
514 surroundingRect = {{cameraRect.left(), cameraRect.top()}, {cameraRect.right(), cameraRect.top()},
515 {cameraRect.right(), cameraRect.bottom()}, {cameraRect.left() , cameraRect.bottom()}};
516 } else {
517 const qreal anchorRect = centerX;
518
519 surroundingRect = {{anchorRect, -0.1}, {anchorRect + 1.0, -0.1},
520 {anchorRect + 1.0, 1.1}, {anchorRect, 1.1}};
522 }
523 m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode);
524 } else {
526 }
527
529
530 const bool hasBorder = m_circle.m_border.color().alpha() != 0 && m_circle.m_border.width() > 0;
531 const float borderWidth = hasBorder ? m_circle.m_border.width() : 0.0f;
532 m_shapePath->setStrokeColor(hasBorder ? m_circle.m_border.color() : Qt::transparent);
533 m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f);
535
538 path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth);
539 path.closeSubpath();
541
542 m_circle.setSize(bb.size());
545 m_shape->setVisible(true);
546
547 m_circle.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth));
548}
549
552{
553 Q_UNUSED(data);
554 delete oldNode;
557 }
558 return nullptr;
559}
560
562{
563 return m_shape->contains(m_circle.mapToItem(m_shape, point));
564}
565
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
QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
bool contains(const QPointF &point) const override
QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle)
static void calculatePeripheralPointsGreatCircle(QList< QDoubleVector2D > &path, const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p, int steps)
static void calculatePeripheralPointsSimple(QList< QDoubleVector2D > &path, const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p, int steps)
static void includeOnePoleInPath(QList< QDoubleVector2D > &path, const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p)
static int crossEarthPole(const QGeoCoordinate &center, qreal distance)
void setGeoShape(const QGeoShape &shape) override
const QGeoShape & geoShape() const override
void setColor(const QColor &color)
\qmlproperty color MapCircle::color
void colorChanged(const QColor &color)
QSGNode * updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override
\qmlproperty real MapCircle::opacity
void setCenter(const QGeoCoordinate &center)
\qmlproperty coordinate MapCircle::center
void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override
void setRadius(qreal radius)
\qmlproperty real MapCircle::radius
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void radiusChanged(qreal radius)
void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override
bool contains(const QPointF &point) const override
QDeclarativeMapLineProperties * border
\qmlpropertygroup Location::MapCircle::border \qmlproperty int MapCircle::border.width \qmlproperty c...
QDeclarativeCircleMapItem(QQuickItem *parent=nullptr)
void centerChanged(const QGeoCoordinate &center)
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)
Q_DECL_CONSTEXPR double x() const
\inmodule QtPositioning
Definition qgeocircle.h:15
void setCenter(const QGeoCoordinate &center)
Sets the center coordinate of this geo circle to center.
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
void setRadius(qreal radius)
Sets the radius in meters of this geo circle to radius.
\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)
const QGeoProjection & geoProjection() const
Definition qgeomap.cpp:164
@ MapCircle
Definition qgeomap_p.h:49
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
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
qsizetype count() const noexcept
Definition qlist.h:387
static double radians(double degrees)
static double degrees(double radians)
static double earthMeanRadius()
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}).
\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...
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:73
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:74
void setHeight(qreal)
QSizeF size() const
qreal width
This property holds the width of this item.
Definition qquickitem.h:76
void setVisible(bool)
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
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 qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:499
bool contains(const QRectF &r) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qrect.cpp:1985
constexpr QRectF adjusted(qreal x1, qreal y1, qreal x2, qreal y2) const noexcept
Returns a new rectangle with dx1, dy1, dx2 and dy2 added respectively to the existing coordinates of ...
Definition qrect.h:799
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:496
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
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
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
#define this
Definition dialogs.cpp:9
QMap< QString, QString > map
[6]
QRectF boundingRectangleFromList(const QList< QDoubleVector2D > &list)
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:46
#define M_PI
Definition qmath.h:209
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLsizei GLsizei GLfloat distance
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
struct _cl_event * event
const GLubyte * c
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:92
\qmltype MapCircle \instantiates QDeclarativeCircleMapItem \inqmlmodule QtLocation
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent