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