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 "qdeclarativegeomapitembase_p.h"
38 #include "qgeocameradata_p.h"
39 #include <QtLocation/private/qgeomap_p.h>
40 #include <QtQml/QQmlInfo>
41 #include <QtQuick/QSGOpacityNode>
42 #include <QtQuick/private/qquickmousearea_p.h>
43 #include <QtQuick/private/qquickitem_p.h>
44 
45 QT_BEGIN_NAMESPACE
46 
QGeoMapViewportChangeEvent()47 QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent()
48     : zoomLevelChanged(false),
49       centerChanged(false),
50       mapSizeChanged(false),
51       tiltChanged(false),
52       bearingChanged(false),
53       rollChanged(false)
54 {
55 }
56 
QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent & other)57 QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other)
58 {
59     this->operator=(other);
60 }
61 
operator =(const QGeoMapViewportChangeEvent & other)62 QGeoMapViewportChangeEvent &QGeoMapViewportChangeEvent::operator=(const QGeoMapViewportChangeEvent &other)
63 {
64     if (this == &other)
65         return (*this);
66 
67     cameraData = other.cameraData;
68     mapSize = other.mapSize;
69     zoomLevelChanged = other.zoomLevelChanged;
70     centerChanged = other.centerChanged;
71     mapSizeChanged = other.mapSizeChanged;
72     tiltChanged = other.tiltChanged;
73     bearingChanged = other.bearingChanged;
74     rollChanged = other.rollChanged;
75 
76     return (*this);
77 }
78 
QDeclarativeGeoMapItemBase(QQuickItem * parent)79 QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent)
80 :   QQuickItem(parent), map_(0), quickMap_(0), parentGroup_(0)
81 {
82     setFiltersChildMouseEvents(true);
83     connect(this, SIGNAL(childrenChanged()),
84             this, SLOT(afterChildrenChanged()));
85     // Changing opacity on a mapItemGroup should affect also the opacity on the children.
86     // This must be notified to plugins, if they are to render the item.
87     connect(this, &QQuickItem::opacityChanged, this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged);
88 }
89 
~QDeclarativeGeoMapItemBase()90 QDeclarativeGeoMapItemBase::~QDeclarativeGeoMapItemBase()
91 {
92     disconnect(this, SLOT(afterChildrenChanged()));
93     if (quickMap_)
94         quickMap_->removeMapItem(this);
95 }
96 
97 /*!
98     \internal
99 */
afterChildrenChanged()100 void QDeclarativeGeoMapItemBase::afterChildrenChanged()
101 {
102     QList<QQuickItem *> kids = childItems();
103     if (kids.size() > 0) {
104         bool printedWarning = false;
105         foreach (QQuickItem *i, kids) {
106             if (i->flags() & QQuickItem::ItemHasContents
107                     && !qobject_cast<QQuickMouseArea *>(i)) {
108                 if (!printedWarning) {
109                     qmlWarning(this) << "Geographic map items do not support child items";
110                     printedWarning = true;
111                 }
112 
113                 qmlWarning(i) << "deleting this child";
114                 i->deleteLater();
115             }
116         }
117     }
118 }
119 
120 /*!
121     \internal
122 */
setMap(QDeclarativeGeoMap * quickMap,QGeoMap * map)123 void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
124 {
125     if (quickMap == quickMap_)
126         return;
127     if (quickMap && quickMap_)
128         return; // don't allow association to more than one map
129 
130     quickMap_ = quickMap;
131     map_ = map;
132 
133     if (map_ && quickMap_) {
134         // For performance reasons we're not connecting map_'s and quickMap_'s signals to this.
135         // Rather, the handling of cameraDataChanged, visibleAreaChanged, heightChanged and widthChanged is done explicitly in QDeclarativeGeoMap by directly calling methods on the items.
136         // See QTBUG-76950
137         lastSize_ = QSizeF(quickMap_->width(), quickMap_->height());
138         lastCameraData_ = map_->cameraData();
139     }
140 }
141 
142 /*!
143     \internal
144 */
baseCameraDataChanged(const QGeoCameraData & cameraData)145 void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cameraData)
146 {
147     QGeoMapViewportChangeEvent evt;
148     evt.cameraData = cameraData;
149     evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height());
150 
151     if (evt.mapSize != lastSize_)
152         evt.mapSizeChanged = true;
153 
154     if (cameraData.bearing() != lastCameraData_.bearing())
155         evt.bearingChanged = true;
156     if (cameraData.center() != lastCameraData_.center())
157         evt.centerChanged = true;
158     if (cameraData.roll() != lastCameraData_.roll())
159         evt.rollChanged = true;
160     if (cameraData.tilt() != lastCameraData_.tilt())
161         evt.tiltChanged = true;
162     if (cameraData.zoomLevel() != lastCameraData_.zoomLevel())
163         evt.zoomLevelChanged = true;
164 
165     lastSize_ = evt.mapSize;
166     lastCameraData_ = cameraData;
167 
168     afterViewportChanged(evt);
169 }
170 
visibleAreaChanged()171 void QDeclarativeGeoMapItemBase::visibleAreaChanged()
172 {
173     QGeoMapViewportChangeEvent evt;
174     evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height());
175     afterViewportChanged(evt);
176 }
177 
178 /*!
179     \internal
180 */
setPositionOnMap(const QGeoCoordinate & coordinate,const QPointF & offset)181 void QDeclarativeGeoMapItemBase::setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset)
182 {
183     if (!map_ || !quickMap_)
184         return;
185 
186     QDoubleVector2D pos;
187     if (map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) {
188         const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
189         QDoubleVector2D wrappedProjection = p.geoToWrappedMapProjection(coordinate);
190         if (!p.isProjectable(wrappedProjection))
191             return;
192         pos = p.wrappedMapProjectionToItemPosition(wrappedProjection);
193     } else {
194         pos = map()->geoProjection().coordinateToItemPosition(coordinate, false);
195         if (qIsNaN(pos.x()))
196             return;
197     }
198 
199     QPointF topLeft = pos.toPointF() - offset;
200 
201     setPosition(topLeft);
202 }
203 
autoFadeIn() const204 bool QDeclarativeGeoMapItemBase::autoFadeIn() const
205 {
206     return m_autoFadeIn;
207 }
208 
209 static const double opacityRampMin = 1.5;
210 static const double opacityRampMax = 2.5;
211 
setAutoFadeIn(bool fadeIn)212 void QDeclarativeGeoMapItemBase::setAutoFadeIn(bool fadeIn)
213 {
214     if (fadeIn == m_autoFadeIn)
215         return;
216     m_autoFadeIn = fadeIn;
217     if (quickMap_ && quickMap_->zoomLevel() < opacityRampMax)
218         polishAndUpdate();
219 }
220 
lodThreshold() const221 int QDeclarativeGeoMapItemBase::lodThreshold() const
222 {
223     return m_lodThreshold;
224 }
225 
setLodThreshold(int lt)226 void QDeclarativeGeoMapItemBase::setLodThreshold(int lt)
227 {
228     if (lt == m_lodThreshold)
229         return;
230     m_lodThreshold = lt;
231     update();
232 }
233 
234 /*!
235     \internal
236 
237     This returns the zoom level to be used when requesting the LOD.
238     Essentially it clamps to m_lodThreshold, and if above, it selects
239     a ZL higher than the maximum LODable level.
240 */
zoomForLOD(int zoom) const241 unsigned int QDeclarativeGeoMapItemBase::zoomForLOD(int zoom) const
242 {
243     if (zoom >= m_lodThreshold)
244         return 30; // some arbitrarily large zoom
245     return uint(zoom);
246 }
247 
248 /*!
249     \internal
250 */
zoomLevelOpacity() const251 float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const
252 {
253     if (!m_autoFadeIn) // Consider skipping the opacity node instead.
254         return 1.0;
255     else if (quickMap_->zoomLevel() > opacityRampMax)
256         return 1.0;
257     else if (quickMap_->zoomLevel() > opacityRampMin)
258         return quickMap_->zoomLevel() - opacityRampMin;
259     else
260         return 0.0;
261 }
262 
childMouseEventFilter(QQuickItem * item,QEvent * event)263 bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent *event)
264 {
265     Q_UNUSED(item);
266     if (event->type() == QEvent::MouseButtonPress && !contains(static_cast<QMouseEvent*>(event)->pos())) {
267         // In case of items that are not rectangles, this filter is used to test if the event has landed
268         // inside the actual item shape.
269         // If so, the method returns true, meaning that it prevents the event delivery to child "*item" (for example,
270         // a mouse area that is on top of this map item).
271         // However, this method sets "accepted" to false, so that the event can still be passed further up,
272         // specifically to the parent Map, that is a sort of flickable.
273         // Otherwise, if the event is not contained within the map item, the method returns false, meaning the event
274         // is delivered to the child *item (like the mouse area associated).
275         event->setAccepted(false);
276         return true;
277     }
278     return false;
279 }
280 
281 /*!
282     \internal
283 */
updatePaintNode(QSGNode * oldNode,UpdatePaintNodeData * pd)284 QSGNode *QDeclarativeGeoMapItemBase::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *pd)
285 {
286     if (!map_ || !quickMap_ || map_->supportedMapItemTypes() & itemType()) {
287         if (oldNode)
288             delete oldNode;
289         oldNode = 0;
290         return 0;
291     }
292 
293     QSGOpacityNode *opn = static_cast<QSGOpacityNode *>(oldNode);
294     if (!opn)
295         opn = new QSGOpacityNode();
296 
297     opn->setOpacity(zoomLevelOpacity());
298 
299     QSGNode *oldN = opn->childCount() ? opn->firstChild() : 0;
300     opn->removeAllChildNodes();
301     if (opn->opacity() > 0.0) {
302         QSGNode *n = this->updateMapItemPaintNode(oldN, pd);
303         if (n)
304             opn->appendChildNode(n);
305     } else {
306         delete oldN;
307     }
308 
309     return opn;
310 }
311 
312 /*!
313     \internal
314 */
updateMapItemPaintNode(QSGNode * oldNode,UpdatePaintNodeData *)315 QSGNode *QDeclarativeGeoMapItemBase::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
316 {
317     delete oldNode;
318     return 0;
319 }
320 
itemType() const321 QGeoMap::ItemType QDeclarativeGeoMapItemBase::itemType() const
322 {
323     return m_itemType;
324 }
325 
326 /*!
327     \internal
328 
329     The actual combined opacity of the item. Needed by custom renderer to look like
330     the scene-graph one.
331 */
mapItemOpacity() const332 qreal QDeclarativeGeoMapItemBase::mapItemOpacity() const
333 {
334     if (parentGroup_)
335         return parentGroup_->mapItemOpacity() * opacity();
336     return opacity();
337 }
338 
setParentGroup(QDeclarativeGeoMapItemGroup & parentGroup)339 void QDeclarativeGeoMapItemBase::setParentGroup(QDeclarativeGeoMapItemGroup &parentGroup)
340 {
341     parentGroup_ = &parentGroup;
342     if (parentGroup_) {
343         connect(parentGroup_, &QDeclarativeGeoMapItemGroup::mapItemOpacityChanged,
344                 this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged);
345     }
346 }
347 
isPolishScheduled() const348 bool QDeclarativeGeoMapItemBase::isPolishScheduled() const
349 {
350     return QQuickItemPrivate::get(this)->polishScheduled;
351 }
352 
setMaterialDirty()353 void QDeclarativeGeoMapItemBase::setMaterialDirty() {}
354 
polishAndUpdate()355 void QDeclarativeGeoMapItemBase::polishAndUpdate()
356 {
357     polish();
358     update();
359 }
360 
361 QT_END_NAMESPACE
362