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 {
RectangleBackendSelectorRectangleBackendSelector131     RectangleBackendSelector()
132     {
133         backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeRectangleMapItem::OpenGL : QDeclarativeRectangleMapItem::Software;
134     }
135     QDeclarativeRectangleMapItem::Backend backend = QDeclarativeRectangleMapItem::Software;
136 };
137 
Q_GLOBAL_STATIC(RectangleBackendSelector,mapRectangleBackendSelector)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(ItemHasContents, true);
148     QObject::connect(&m_border, SIGNAL(colorChanged(QColor)),
149                      this, SLOT(onLinePropertiesChanged()));
150     QObject::connect(&m_border, SIGNAL(widthChanged(qreal)),
151                      this, SLOT(onLinePropertiesChanged()));
152     setBackend(mapRectangleBackendSelector->backend);
153 }
154 
~QDeclarativeRectangleMapItem()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 */
backend() const178 QDeclarativeRectangleMapItem::Backend QDeclarativeRectangleMapItem::backend() const
179 {
180     return m_backend;
181 }
182 
setBackend(QDeclarativeRectangleMapItem::Backend b)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(d);
201     m_d->onGeoGeometryChanged();
202     emit backendChanged();
203 }
204 
205 /*!
206     \internal
207 */
setMap(QDeclarativeGeoMap * quickMap,QGeoMap * map)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 */
border()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 */
setTopLeft(const QGeoCoordinate & topLeft)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 
topLeft()249 QGeoCoordinate QDeclarativeRectangleMapItem::topLeft()
250 {
251     return m_rectangle.topLeft();
252 }
253 
254 /*!
255     \internal
256 */
markSourceDirtyAndUpdate()257 void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate()
258 {
259     m_d->markSourceDirtyAndUpdate();
260 }
261 
onLinePropertiesChanged()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 */
setBottomRight(const QGeoCoordinate & bottomRight)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 
bottomRight()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 */
color() const294 QColor QDeclarativeRectangleMapItem::color() const
295 {
296     return m_color;
297 }
298 
setColor(const QColor & color)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(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 */
updateMapItemPaintNode(QSGNode * oldNode,UpdatePaintNodeData * data)322 QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
323 {
324     return m_d->updateMapItemPaintNode(oldNode, data);
325 }
326 
327 /*!
328     \internal
329 */
updatePolish()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 */
afterViewportChanged(const QGeoMapViewportChangeEvent & event)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 */
contains(const QPointF & point) const350 bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const
351 {
352     return m_d->contains(point);
353 }
354 
geoShape() const355 const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const
356 {
357     return m_rectangle;
358 }
359 
setGeoShape(const QGeoShape & shape)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(m_rectangle.topLeft());
373     if (brHasChanged)
374         emit bottomRightChanged(m_rectangle.bottomRight());
375 }
376 
377 /*!
378     \internal
379 */
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)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(QDoubleVector2D(newGeometry.center()), false);
388     QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), 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(offsetLati, offsetLongi);
397     m_d->onItemGeometryChanged();
398     emit topLeftChanged(m_rectangle.topLeft());
399     emit bottomRightChanged(m_rectangle.bottomRight());
400 
401     // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
402     // call to this function.
403 }
404 
~QDeclarativeRectangleMapItemPrivate()405 QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {}
406 
~QDeclarativeRectangleMapItemPrivateCPU()407 QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {}
408 
409 #if QT_CONFIG(opengl)
~QDeclarativeRectangleMapItemPrivateOpenGL()410 QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {}
411 #endif
412 
413 QT_END_NAMESPACE
414