Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdeclarativerectanglemapitem.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// Qt-Security score:significant reason:default
4
7
8#include <QtCore/QScopedValueRollback>
9#include <QPainterPath>
10#include <qnumeric.h>
11#include <QRectF>
12#include <QPointF>
13
14#include <QtLocation/private/qgeomap_p.h>
15#include <QtPositioning/private/qlocationutils_p.h>
16#include <QtPositioning/private/qwebmercator_p.h>
17#include <QtPositioning/private/qdoublevector2d_p.h>
18
20
21/*!
22 \qmltype MapRectangle
23 \nativetype QDeclarativeRectangleMapItem
24 \inqmlmodule QtLocation
25 \ingroup qml-QtLocation5-maps
26 \since QtLocation 5.5
27
28 \brief The MapRectangle type displays a rectangle on a Map.
29
30 The MapRectangle type displays a rectangle on a Map. Rectangles are a
31 special case of Polygon with exactly 4 vertices and 4 "straight" edges. In
32 this case, "straight" means that the top-left point has the same latitude
33 as the top-right point (the top edge), and the bottom-left point has the
34 same latitude as the bottom-right point (the bottom edge). Similarly, the
35 points on the left side have the same longitude, and the points on the
36 right side have the same longitude.
37
38 To specify the rectangle, it requires a \l topLeft and \l bottomRight point,
39 both given by a \l [QtPositioning]{geoCoordinate}.
40
41 By default, the rectangle is displayed with transparent fill and a 1-pixel
42 thick black border. This can be changed using the \l color, \l border.color
43 and \l border.width properties.
44
45 \note Similar to the \l MapPolygon type, MapRectangles are geographic
46 items, thus dragging a MapRectangle causes its vertices to be recalculated
47 in the geographic coordinate space. Apparent stretching of the item
48 occurs when dragged to the a different latitude, however, its edges
49 remain straight.
50
51 \section2 Example Usage
52
53 The following snippet shows a map containing a MapRectangle, spanning
54 from (-27, 153) to (-28, 153.5), near Brisbane, Australia. The rectangle
55 is filled in green, with a 2 pixel black border.
56
57 \code
58 Map {
59 MapRectangle {
60 color: 'green'
61 border.width: 2
62 topLeft {
63 latitude: -27
64 longitude: 153
65 }
66 bottomRight {
67 latitude: -28
68 longitude: 153.5
69 }
70 }
71 }
72 \endcode
73
74 \image api-maprectangle.png
75*/
76
77/*!
78 \qmlproperty bool QtLocation::MapRectangle::autoFadeIn
79
80 This property holds whether the item automatically fades in when zooming into the map
81 starting from very low zoom levels. By default this is \c true.
82 Setting this property to \c false causes the map item to always have the opacity specified
83 with the \l QtQuick::Item::opacity property, which is 1.0 by default.
84
85 \since 5.14
86*/
87
88/*!
89 \qmlproperty enum QtLocation::MapRectangle::referenceSurface
90
91 This property determines the reference surface of the rectangle. If it is set to
92 \l QLocation::ReferenceSurface::Map the edge points are connected with straight lines
93 on the map. If it is set to \l QLocation::ReferenceSurface::Globe, the edge points
94 are connected following the great circle path, describing the shortest connection of
95 two points on a sphere.
96 Default value is \l QLocation::ReferenceSurface::Map.
97
98 \since 6.5
99*/
100
101QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent)
102 : QDeclarativeGeoMapItemBase(parent), m_border(this),
103 m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this))
104{
105 // ToDo: handle envvar, and switch implementation.
106 m_itemType = QGeoMap::MapRectangle;
107 setFlag(ItemHasContents, true);
108 QObject::connect(&m_border, &QDeclarativeMapLineProperties::colorChanged,
109 this, &QDeclarativeRectangleMapItem::onLinePropertiesChanged);
110 QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged,
111 this, &QDeclarativeRectangleMapItem::onLinePropertiesChanged);
112}
113
114QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem()
115{
116}
117
118/*!
119 \internal
120*/
121void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
122{
123 QDeclarativeGeoMapItemBase::setMap(quickMap,map);
124 if (!map)
125 return;
126 m_d->onMapSet();
127}
128
129/*!
130 \qmlpropertygroup Location::MapRectangle::border
131 \qmlproperty int MapRectangle::border.width
132 \qmlproperty color MapRectangle::border.color
133
134 This property is part of the border property group. The border property group
135 holds the width and color used to draw the border of the rectangle.
136 The width is in pixels and is independent of the zoom level of the map.
137
138 The default values correspond to a black border with a width of 1 pixel.
139 For no line, use a width of 0 or a transparent color.
140*/
141QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border()
142{
143 return &m_border;
144}
145
146/*!
147 \qmlproperty coordinate MapRectangle::topLeft
148
149 This property holds the top-left coordinate of the MapRectangle which
150 can be used to retrieve its longitude, latitude and altitude.
151*/
152void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft)
153{
154 if (m_rectangle.topLeft() == topLeft)
155 return;
156
157 m_rectangle.setTopLeft(topLeft);
158 m_d->onGeoGeometryChanged();
159 emit topLeftChanged(topLeft);
160}
161
162QGeoCoordinate QDeclarativeRectangleMapItem::topLeft()
163{
164 return m_rectangle.topLeft();
165}
166
167/*!
168 \internal
169*/
170void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate()
171{
172 m_d->markSourceDirtyAndUpdate();
173}
174
175void QDeclarativeRectangleMapItem::onLinePropertiesChanged()
176{
177 m_d->onLinePropertiesChanged();
178}
179
180/*!
181 \qmlproperty coordinate MapRectangle::bottomRight
182
183 This property holds the bottom-right coordinate of the MapRectangle which
184 can be used to retrieve its longitude, latitude and altitude.
185*/
186void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight)
187{
188 if (m_rectangle.bottomRight() == bottomRight)
189 return;
190
191 m_rectangle.setBottomRight(bottomRight);
192 m_d->onGeoGeometryChanged();
193 emit bottomRightChanged(bottomRight);
194}
195
196QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight()
197{
198 return m_rectangle.bottomRight();
199}
200
201/*!
202 \qmlproperty color MapRectangle::color
203
204 This property holds the fill color of the rectangle. For no fill, use
205 a transparent color.
206*/
207QColor QDeclarativeRectangleMapItem::color() const
208{
209 return m_color;
210}
211
212void QDeclarativeRectangleMapItem::setColor(const QColor &color)
213{
214 if (m_color == color)
215 return;
216 m_color = color;
217 polishAndUpdate();
218 emit colorChanged(m_color);
219}
220
221/*!
222 \qmlproperty real MapRectangle::opacity
223
224 This property holds the opacity of the item. Opacity is specified as a
225 number between 0 (fully transparent) and 1 (fully opaque). The default is 1.
226
227 An item with 0 opacity will still receive mouse events. To stop mouse events, set the
228 visible property of the item to false.
229*/
230
231/*!
232 \internal
233*/
234QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
235{
236 return m_d->updateMapItemPaintNode(oldNode, data);
237}
238
239/*!
240 \internal
241*/
242void QDeclarativeRectangleMapItem::updatePolish()
243{
244 if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
245 return;
246 m_d->updatePolish();
247}
248
249/*!
250 \internal
251*/
252void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
253{
254 if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
255 return;
256 m_d->afterViewportChanged();
257}
258
259/*!
260 \internal
261*/
262bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const
263{
264 return m_d->contains(point);
265}
266
267const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const
268{
269 return m_rectangle;
270}
271
272void QDeclarativeRectangleMapItem::setGeoShape(const QGeoShape &shape)
273{
274 if (shape == m_rectangle)
275 return;
276
277 const QGeoRectangle rectangle = m_rectangle.boundingGeoRectangle();
278 const bool tlHasChanged = rectangle.topLeft() != m_rectangle.topLeft();
279 const bool brHasChanged = rectangle.bottomRight() != m_rectangle.bottomRight();
280 m_rectangle = rectangle;
281
282 m_d->onGeoGeometryChanged();
283 if (tlHasChanged)
284 emit topLeftChanged(m_rectangle.topLeft());
285 if (brHasChanged)
286 emit bottomRightChanged(m_rectangle.bottomRight());
287}
288
289/*!
290 \internal
291*/
292void QDeclarativeRectangleMapItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
293{
294 if (!map() || !m_rectangle.isValid() || m_updatingGeometry || newGeometry.topLeft() == oldGeometry.topLeft()) {
295 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
296 return;
297 }
298 // TODO: change the algorithm to preserve the distances and size
299 QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false);
300 QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false);
301 if (!newCenter.isValid() || !oldCenter.isValid())
302 return;
303 double offsetLongi = newCenter.longitude() - oldCenter.longitude();
304 double offsetLati = newCenter.latitude() - oldCenter.latitude();
305 if (offsetLati == 0.0 && offsetLongi == 0.0)
306 return;
307
308 m_rectangle.translate(offsetLati, offsetLongi);
309 m_d->onItemGeometryChanged();
310 emit topLeftChanged(m_rectangle.topLeft());
311 emit bottomRightChanged(m_rectangle.bottomRight());
312
313 // Not calling QDeclarativeGeoMapItemBase::geometryChange() as it will be called from a nested
314 // call to this function.
315}
316
317QDeclarativeRectangleMapItemPrivate::QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect)
318 : m_rect(rect)
319{
320}
321
322QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate()
323{
324}
325
326QDeclarativeRectangleMapItemPrivateCPU::QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect)
327 : QDeclarativeRectangleMapItemPrivate(rect)
328{
329 m_shape = new QQuickShape(&m_rect);
330 m_shape->setObjectName("_qt_map_item_shape");
331 m_shape->setZ(-1);
332 m_shape->setContainsMode(QQuickShape::FillContains);
333
334 m_shapePath = new QQuickShapePath(m_shape);
335 m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath);
336
337 auto pathElements = m_shapePath->pathElements();
338 pathElements.append(&pathElements, m_painterPath);
339
340 auto shapePaths = m_shape->data();
341 shapePaths.append(&shapePaths, m_shapePath);
342}
343
344QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU()
345{
346 delete m_shape;
347}
348
349void QDeclarativeRectangleMapItemPrivateCPU::updatePolish()
350{
351 if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) {
352 m_geometry.clear();
353 m_rect.setWidth(0);
354 m_rect.setHeight(0);
355 m_shape->setVisible(false);
356 return;
357 }
358
359 QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry);
360 m_rect.m_updatingGeometry = true;
361
362 QList<QGeoCoordinate> perimeter = QGeoMapItemGeometry::path(m_rect.m_rectangle);
363
364 if (m_rect.referenceSurface() == QLocation::ReferenceSurface::Globe) {
365 perimeter = QDeclarativeGeoMapItemUtils::greaterCirclePath(perimeter,
366 QDeclarativeGeoMapItemUtils::ClosedPath);
367 }
368
369 const QList<QDoubleVector2D> pathMercator = QGeoMapItemGeometry::pathMercator(perimeter);
370
371 m_geometry.updateSourcePoints(*m_rect.map(), QList<QList<QDoubleVector2D>>{pathMercator},
372 m_rect.referenceSurface() == QLocation::ReferenceSurface::Globe ? QGeoMapPolygonGeometry::WrapAround :
373 QGeoMapPolygonGeometry::Duplicate);
374 m_rect.setShapeTriangulationScale(m_shape, m_geometry.maxCoord());
375
376 const bool hasBorder = m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0;
377 m_shapePath->setStrokeColor(hasBorder ? m_rect.m_border.color() : Qt::transparent);
378 const float borderWidth = hasBorder ? m_rect.m_border.width() : 0.0f;
379 m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f);
380 m_shapePath->setFillColor(m_rect.color());
381
382 const QRectF bb = m_geometry.sourceBoundingBox();
383 QPainterPath path = m_geometry.srcPath();
384 path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth);
385 path.closeSubpath();
386 m_painterPath->setPath(path);
387
388 m_rect.setSize(bb.size() + QSize(2 * borderWidth, 2 * borderWidth));
389 m_shape->setSize(m_rect.size());
390 m_shape->setOpacity(m_rect.zoomLevelOpacity());
391 m_shape->setVisible(true);
392
393 m_rect.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth));
394}
395
396QSGNode *QDeclarativeRectangleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode,
397 QQuickItem::UpdatePaintNodeData *data)
398{
399 Q_UNUSED(data);
400 delete oldNode;
401 if (m_geometry.isScreenDirty()) {
402 m_geometry.markClean();
403 }
404 return nullptr;
405}
406
407bool QDeclarativeRectangleMapItemPrivateCPU::contains(const QPointF &point) const
408{
409 return m_shape->contains(m_rect.mapToItem(m_shape, point));
410}
411
412QT_END_NAMESPACE
Combined button and popup list for selecting options.