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
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
111
113: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent),
114 m_updatingGeometry(false)
116{
117 // ToDo: handle envvar, and switch implementation.
125 [this]() {m_d->onGeoGeometryChanged();});
126}
127
131
145{
146 return &m_border;
147}
148
150{
151 m_d->markSourceDirtyAndUpdate();
152}
153
155{
156 m_d->onLinePropertiesChanged();
157}
158
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
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
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
488 QList<QDoubleVector2D> circlePath = m_circlePath;
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);
497 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce);
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 {
525 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath});
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
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
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)
\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
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:75
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: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}).
\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...
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:72
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:73
void setHeight(qreal)
QSizeF size() const
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
void setVisible(bool)
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
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
\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]
void colorChanged()
QRectF boundingRectangleFromList(const QList< QDoubleVector2D > &list)
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ transparent
Definition qnamespace.h:47
#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]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei GLsizei GLfloat distance
GLuint color
[2]
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:187
\qmltype MapCircle \instantiates QDeclarativeCircleMapItem \inqmlmodule QtLocation