1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtLocation module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qdeclarativerectanglemapitem_p.h" |
38 | #include "qdeclarativerectanglemapitem_p_p.h" |
39 | #include "qdeclarativepolygonmapitem_p.h" |
40 | #include "qlocationutils_p.h" |
41 | #include <QPainterPath> |
42 | #include <qnumeric.h> |
43 | #include <QRectF> |
44 | #include <QPointF> |
45 | #include <QtPositioning/private/qwebmercator_p.h> |
46 | #include <QtLocation/private/qgeomap_p.h> |
47 | #include <QtPositioning/private/qdoublevector2d_p.h> |
48 | #include <QtCore/QScopedValueRollback> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | /*! |
53 | \qmltype MapRectangle |
54 | \instantiates QDeclarativeRectangleMapItem |
55 | \inqmlmodule QtLocation |
56 | \ingroup qml-QtLocation5-maps |
57 | \since QtLocation 5.5 |
58 | |
59 | \brief The MapRectangle type displays a rectangle on a Map. |
60 | |
61 | The MapRectangle type displays a rectangle on a Map. Rectangles are a |
62 | special case of Polygon with exactly 4 vertices and 4 "straight" edges. In |
63 | this case, "straight" means that the top-left point has the same latitude |
64 | as the top-right point (the top edge), and the bottom-left point has the |
65 | same latitude as the bottom-right point (the bottom edge). Similarly, the |
66 | points on the left side have the same longitude, and the points on the |
67 | right side have the same longitude. |
68 | |
69 | To specify the rectangle, it requires a \l topLeft and \l bottomRight point, |
70 | both given by a \l {coordinate}. |
71 | |
72 | By default, the rectangle is displayed with transparent fill and a 1-pixel |
73 | thick black border. This can be changed using the \l color, \l border.color |
74 | and \l border.width properties. |
75 | |
76 | \note Similar to the \l MapPolygon type, MapRectangles are geographic |
77 | items, thus dragging a MapRectangle causes its vertices to be recalculated |
78 | in the geographic coordinate space. Apparent stretching of the item |
79 | occurs when dragged to the a different latitude, however, its edges |
80 | remain straight. |
81 | |
82 | \section2 Performance |
83 | |
84 | MapRectangles have a rendering cost identical to a MapPolygon with 4 |
85 | vertices. |
86 | |
87 | Like the other map objects, MapRectangle is normally drawn without a smooth |
88 | appearance. Setting the \l opacity property will force the object to be |
89 | blended, which decreases performance considerably depending on the hardware |
90 | in use. |
91 | |
92 | \section2 Example Usage |
93 | |
94 | The following snippet shows a map containing a MapRectangle, spanning |
95 | from (-27, 153) to (-28, 153.5), near Brisbane, Australia. The rectangle |
96 | is filled in green, with a 2 pixel black border. |
97 | |
98 | \code |
99 | Map { |
100 | MapRectangle { |
101 | color: 'green' |
102 | border.width: 2 |
103 | topLeft { |
104 | latitude: -27 |
105 | longitude: 153 |
106 | } |
107 | bottomRight { |
108 | latitude: -28 |
109 | longitude: 153.5 |
110 | } |
111 | } |
112 | } |
113 | \endcode |
114 | |
115 | \image api-maprectangle.png |
116 | */ |
117 | |
118 | /*! |
119 | \qmlproperty bool QtLocation::MapRectangle::autoFadeIn |
120 | |
121 | This property holds whether the item automatically fades in when zooming into the map |
122 | starting from very low zoom levels. By default this is \c true. |
123 | Setting this property to \c false causes the map item to always have the opacity specified |
124 | with the \l QtQuick::Item::opacity property, which is 1.0 by default. |
125 | |
126 | \since 5.14 |
127 | */ |
128 | |
129 | struct RectangleBackendSelector |
130 | { |
131 | RectangleBackendSelector() |
132 | { |
133 | backend = (qgetenv(varName: "QTLOCATION_OPENGL_ITEMS" ).toInt()) ? QDeclarativeRectangleMapItem::OpenGL : QDeclarativeRectangleMapItem::Software; |
134 | } |
135 | QDeclarativeRectangleMapItem::Backend backend = QDeclarativeRectangleMapItem::Software; |
136 | }; |
137 | |
138 | Q_GLOBAL_STATIC(RectangleBackendSelector, mapRectangleBackendSelector) |
139 | |
140 | QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) |
141 | : QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), |
142 | m_updatingGeometry(false) |
143 | , m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this)) |
144 | { |
145 | // ToDo: handle envvar, and switch implementation. |
146 | m_itemType = QGeoMap::MapRectangle; |
147 | setFlag(flag: ItemHasContents, enabled: true); |
148 | QObject::connect(sender: &m_border, SIGNAL(colorChanged(QColor)), |
149 | receiver: this, SLOT(onLinePropertiesChanged())); |
150 | QObject::connect(sender: &m_border, SIGNAL(widthChanged(qreal)), |
151 | receiver: this, SLOT(onLinePropertiesChanged())); |
152 | setBackend(mapRectangleBackendSelector->backend); |
153 | } |
154 | |
155 | QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() |
156 | { |
157 | } |
158 | |
159 | /*! |
160 | \qmlproperty MapRectangle.Backend QtLocation::MapRectangle::backend |
161 | |
162 | This property holds which backend is in use to render the map item. |
163 | Valid values are \b MapRectangle.Software and \b{MapRectangle.OpenGL}. |
164 | The default value is \b{MapRectangle.Software}. |
165 | |
166 | \note \b{The release of this API with Qt 5.15 is a Technology Preview}. |
167 | Ideally, as the OpenGL backends for map items mature, there will be |
168 | no more need to also offer the legacy software-projection backend. |
169 | So this property will likely disappear at some later point. |
170 | To select OpenGL-accelerated item backends without using this property, |
171 | it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS |
172 | to \b{1}. |
173 | Also note that all current OpenGL backends won't work as expected when enabling |
174 | layers on the individual item, or when running on OpenGL core profiles greater than 2.x. |
175 | |
176 | \since 5.15 |
177 | */ |
178 | QDeclarativeRectangleMapItem::Backend QDeclarativeRectangleMapItem::backend() const |
179 | { |
180 | return m_backend; |
181 | } |
182 | |
183 | void QDeclarativeRectangleMapItem::setBackend(QDeclarativeRectangleMapItem::Backend b) |
184 | { |
185 | if (b == m_backend) |
186 | return; |
187 | m_backend = b; |
188 | QScopedPointer<QDeclarativeRectangleMapItemPrivate> d( |
189 | (m_backend == Software) ? static_cast<QDeclarativeRectangleMapItemPrivate *>( |
190 | new QDeclarativeRectangleMapItemPrivateCPU(*this)) |
191 | #if QT_CONFIG(opengl) |
192 | : static_cast<QDeclarativeRectangleMapItemPrivate *>( |
193 | new QDeclarativeRectangleMapItemPrivateOpenGL(*this))); |
194 | #else |
195 | : nullptr); |
196 | qFatal("Requested non software rendering backend, but source code is compiled wihtout opengl " |
197 | "support" ); |
198 | #endif |
199 | |
200 | m_d.swap(other&: d); |
201 | m_d->onGeoGeometryChanged(); |
202 | emit backendChanged(); |
203 | } |
204 | |
205 | /*! |
206 | \internal |
207 | */ |
208 | void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) |
209 | { |
210 | QDeclarativeGeoMapItemBase::setMap(quickMap,map); |
211 | if (!map) |
212 | return; |
213 | m_d->onMapSet(); |
214 | } |
215 | |
216 | /*! |
217 | \qmlpropertygroup Location::MapRectangle::border |
218 | \qmlproperty int MapRectangle::border.width |
219 | \qmlproperty color MapRectangle::border.color |
220 | |
221 | This property is part of the border property group. The border property group |
222 | holds the width and color used to draw the border of the rectangle. |
223 | The width is in pixels and is independent of the zoom level of the map. |
224 | |
225 | The default values correspond to a black border with a width of 1 pixel. |
226 | For no line, use a width of 0 or a transparent color. |
227 | */ |
228 | QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() |
229 | { |
230 | return &m_border; |
231 | } |
232 | |
233 | /*! |
234 | \qmlproperty coordinate MapRectangle::topLeft |
235 | |
236 | This property holds the top-left coordinate of the MapRectangle which |
237 | can be used to retrieve its longitude, latitude and altitude. |
238 | */ |
239 | void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) |
240 | { |
241 | if (m_rectangle.topLeft() == topLeft) |
242 | return; |
243 | |
244 | m_rectangle.setTopLeft(topLeft); |
245 | m_d->onGeoGeometryChanged(); |
246 | emit topLeftChanged(topLeft); |
247 | } |
248 | |
249 | QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() |
250 | { |
251 | return m_rectangle.topLeft(); |
252 | } |
253 | |
254 | /*! |
255 | \internal |
256 | */ |
257 | void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() |
258 | { |
259 | m_d->markSourceDirtyAndUpdate(); |
260 | } |
261 | |
262 | void QDeclarativeRectangleMapItem::onLinePropertiesChanged() |
263 | { |
264 | m_d->onLinePropertiesChanged(); |
265 | } |
266 | |
267 | /*! |
268 | \qmlproperty coordinate MapRectangle::bottomRight |
269 | |
270 | This property holds the bottom-right coordinate of the MapRectangle which |
271 | can be used to retrieve its longitude, latitude and altitude. |
272 | */ |
273 | void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) |
274 | { |
275 | if (m_rectangle.bottomRight() == bottomRight) |
276 | return; |
277 | |
278 | m_rectangle.setBottomRight(bottomRight); |
279 | m_d->onGeoGeometryChanged(); |
280 | emit bottomRightChanged(bottomRight); |
281 | } |
282 | |
283 | QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() |
284 | { |
285 | return m_rectangle.bottomRight(); |
286 | } |
287 | |
288 | /*! |
289 | \qmlproperty color MapRectangle::color |
290 | |
291 | This property holds the fill color of the rectangle. For no fill, use |
292 | a transparent color. |
293 | */ |
294 | QColor QDeclarativeRectangleMapItem::color() const |
295 | { |
296 | return m_color; |
297 | } |
298 | |
299 | void QDeclarativeRectangleMapItem::setColor(const QColor &color) |
300 | { |
301 | if (m_color == color) |
302 | return; |
303 | m_color = color; |
304 | m_dirtyMaterial = true; |
305 | polishAndUpdate(); |
306 | emit colorChanged(color: m_color); |
307 | } |
308 | |
309 | /*! |
310 | \qmlproperty real MapRectangle::opacity |
311 | |
312 | This property holds the opacity of the item. Opacity is specified as a |
313 | number between 0 (fully transparent) and 1 (fully opaque). The default is 1. |
314 | |
315 | An item with 0 opacity will still receive mouse events. To stop mouse events, set the |
316 | visible property of the item to false. |
317 | */ |
318 | |
319 | /*! |
320 | \internal |
321 | */ |
322 | QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) |
323 | { |
324 | return m_d->updateMapItemPaintNode(oldNode, data); |
325 | } |
326 | |
327 | /*! |
328 | \internal |
329 | */ |
330 | void QDeclarativeRectangleMapItem::updatePolish() |
331 | { |
332 | if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) |
333 | return; |
334 | m_d->updatePolish(); |
335 | } |
336 | |
337 | /*! |
338 | \internal |
339 | */ |
340 | void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) |
341 | { |
342 | if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) |
343 | return; |
344 | m_d->afterViewportChanged(); |
345 | } |
346 | |
347 | /*! |
348 | \internal |
349 | */ |
350 | bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const |
351 | { |
352 | return m_d->contains(point); |
353 | } |
354 | |
355 | const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const |
356 | { |
357 | return m_rectangle; |
358 | } |
359 | |
360 | void QDeclarativeRectangleMapItem::setGeoShape(const QGeoShape &shape) |
361 | { |
362 | if (shape == m_rectangle) |
363 | return; |
364 | |
365 | const QGeoRectangle rectangle = m_rectangle.boundingGeoRectangle(); |
366 | const bool tlHasChanged = rectangle.topLeft() != m_rectangle.topLeft(); |
367 | const bool brHasChanged = rectangle.bottomRight() != m_rectangle.bottomRight(); |
368 | m_rectangle = rectangle; |
369 | |
370 | m_d->onGeoGeometryChanged(); |
371 | if (tlHasChanged) |
372 | emit topLeftChanged(topLeft: m_rectangle.topLeft()); |
373 | if (brHasChanged) |
374 | emit bottomRightChanged(bottomRight: m_rectangle.bottomRight()); |
375 | } |
376 | |
377 | /*! |
378 | \internal |
379 | */ |
380 | void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
381 | { |
382 | if (!map() || !m_rectangle.isValid() || m_updatingGeometry || newGeometry.topLeft() == oldGeometry.topLeft()) { |
383 | QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); |
384 | return; |
385 | } |
386 | // TODO: change the algorithm to preserve the distances and size |
387 | QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(newGeometry.center()), clipToViewport: false); |
388 | QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(oldGeometry.center()), clipToViewport: false); |
389 | if (!newCenter.isValid() || !oldCenter.isValid()) |
390 | return; |
391 | double offsetLongi = newCenter.longitude() - oldCenter.longitude(); |
392 | double offsetLati = newCenter.latitude() - oldCenter.latitude(); |
393 | if (offsetLati == 0.0 && offsetLongi == 0.0) |
394 | return; |
395 | |
396 | m_rectangle.translate(degreesLatitude: offsetLati, degreesLongitude: offsetLongi); |
397 | m_d->onItemGeometryChanged(); |
398 | emit topLeftChanged(topLeft: m_rectangle.topLeft()); |
399 | emit bottomRightChanged(bottomRight: m_rectangle.bottomRight()); |
400 | |
401 | // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested |
402 | // call to this function. |
403 | } |
404 | |
405 | QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {} |
406 | |
407 | QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {} |
408 | |
409 | #if QT_CONFIG(opengl) |
410 | QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {} |
411 | #endif |
412 | |
413 | QT_END_NAMESPACE |
414 | |