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 "qdeclarativegeomap_p.h" |
38 | #include "qdeclarativegeomapquickitem_p.h" |
39 | #include "qdeclarativegeomapcopyrightsnotice_p.h" |
40 | #include "qdeclarativegeoserviceprovider_p.h" |
41 | #include "qdeclarativegeomaptype_p.h" |
42 | #include "qgeomappingmanager_p.h" |
43 | #include "qgeocameracapabilities_p.h" |
44 | #include "qgeomap_p.h" |
45 | #include "qdeclarativegeomapparameter_p.h" |
46 | #include "qgeomapobject_p.h" |
47 | #include <QtPositioning/QGeoCircle> |
48 | #include <QtPositioning/QGeoRectangle> |
49 | #include <QtPositioning/QGeoPath> |
50 | #include <QtPositioning/QGeoPolygon> |
51 | #include <QtQuick/QQuickWindow> |
52 | #include <QtQuick/QSGRectangleNode> |
53 | #include <QtQuick/private/qquickwindow_p.h> |
54 | #include <QtQml/qqmlinfo.h> |
55 | #include <QtQuick/private/qquickitem_p.h> |
56 | #include <cmath> |
57 | |
58 | #ifndef M_PI |
59 | #define M_PI 3.141592653589793238463 |
60 | #endif |
61 | |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | |
65 | static qreal sanitizeBearing(qreal bearing) |
66 | { |
67 | bearing = std::fmod(x: bearing, y: qreal(360.0)); |
68 | if (bearing < 0.0) |
69 | bearing += 360.0; |
70 | |
71 | return bearing; |
72 | } |
73 | |
74 | /*! |
75 | \qmltype Map |
76 | \instantiates QDeclarativeGeoMap |
77 | \inqmlmodule QtLocation |
78 | \ingroup qml-QtLocation5-maps |
79 | \since QtLocation 5.0 |
80 | |
81 | \brief The Map type displays a map. |
82 | |
83 | The Map type is used to display a map or image of the Earth, with |
84 | the capability to also display interactive objects tied to the map's |
85 | surface. |
86 | |
87 | There are a variety of different ways to visualize the Earth's surface |
88 | in a 2-dimensional manner, but all of them involve some kind of projection: |
89 | a mathematical relationship between the 3D coordinates (latitude, longitude |
90 | and altitude) and 2D coordinates (X and Y in pixels) on the screen. |
91 | |
92 | Different sources of map data can use different projections, and from the |
93 | point of view of the Map type, we treat these as one replaceable unit: |
94 | the Map plugin. A Map plugin consists of a data source, as well as all other |
95 | details needed to display its data on-screen. |
96 | |
97 | The current Map plugin in use is contained in the \l plugin property of |
98 | the Map item. In order to display any image in a Map item, you will need |
99 | to set this property. See the \l Plugin type for a description of how |
100 | to retrieve an appropriate plugin for use. |
101 | |
102 | The geographic region displayed in the Map item is referred to as its |
103 | viewport, and this is defined by the properties \l center, and |
104 | \l zoomLevel. The \l center property contains a \l {coordinate} |
105 | specifying the center of the viewport, while \l zoomLevel controls the scale of the |
106 | map. See each of these properties for further details about their values. |
107 | |
108 | When the map is displayed, each possible geographic coordinate that is |
109 | visible will map to some pixel X and Y coordinate on the screen. To perform |
110 | conversions between these two, Map provides the \l toCoordinate and |
111 | \l fromCoordinate functions, which are of general utility. |
112 | |
113 | \section2 Map Objects |
114 | |
115 | Map related objects can be declared within the body of a Map object in Qt Quick and will |
116 | automatically appear on the Map. To add an object programmatically, first be |
117 | sure it is created with the Map as its parent (for example in an argument to |
118 | Component::createObject). |
119 | Then call the \l addMapItem method on the Map, if the type of this object is one of |
120 | \l MapCircle, \l MapRectangle, \l MapPolyline, \l MapPolygon, \l MapRoute or \l MapQuickItem. |
121 | A corresponding \l removeMapItem method also exists to do the opposite and |
122 | remove any of the above types of map objects from the Map. |
123 | |
124 | Moving Map objects around, resizing them or changing their shape normally |
125 | does not involve any special interaction with Map itself -- changing these |
126 | properties in a map object will automatically update the display. |
127 | |
128 | \section2 Interaction |
129 | |
130 | The Map type includes support for pinch and flick gestures to control |
131 | zooming and panning. These are enabled by default, and available at any |
132 | time by using the \l gesture object. The actual GestureArea is constructed |
133 | specially at startup and cannot be replaced or destroyed. Its properties |
134 | can be altered, however, to control its behavior. |
135 | |
136 | \section2 Performance |
137 | |
138 | Maps are rendered using OpenGL (ES) and the Qt Scene Graph stack, and as |
139 | a result perform quite well where GL accelerated hardware is available. |
140 | |
141 | For "online" Map plugins, network bandwidth and latency can be major |
142 | contributors to the user's perception of performance. Extensive caching is |
143 | performed to mitigate this, but such mitigation is not always perfect. For |
144 | "offline" plugins, the time spent retrieving the stored geographic data |
145 | and rendering the basic map features can often play a dominant role. Some |
146 | offline plugins may use hardware acceleration themselves to (partially) |
147 | avert this. |
148 | |
149 | In general, large and complex Map items such as polygons and polylines with |
150 | large numbers of vertices can have an adverse effect on UI performance. |
151 | Further, more detailed notes on this are in the documentation for each |
152 | map item type. |
153 | |
154 | \section2 Example Usage |
155 | |
156 | The following snippet shows a simple Map and the necessary Plugin type |
157 | to use it. The map is centered over Oslo, Norway, with zoom level 14. |
158 | |
159 | \quotefromfile minimal_map/main.qml |
160 | \skipto import |
161 | \printuntil } |
162 | \printline } |
163 | \skipto Map |
164 | \printuntil } |
165 | \printline } |
166 | |
167 | \image minimal_map.png |
168 | */ |
169 | |
170 | /*! |
171 | \qmlsignal QtLocation::Map::copyrightLinkActivated(string link) |
172 | |
173 | This signal is emitted when the user clicks on a \a link in the copyright notice. The |
174 | application should open the link in a browser or display its contents to the user. |
175 | */ |
176 | |
177 | QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) |
178 | : QQuickItem(parent), |
179 | m_plugin(0), |
180 | m_mappingManager(0), |
181 | m_activeMapType(0), |
182 | m_gestureArea(new QQuickGeoMapGestureArea(this)), |
183 | m_map(0), |
184 | m_error(QGeoServiceProvider::NoError), |
185 | m_color(QColor::fromRgbF(r: 0.9, g: 0.9, b: 0.9)), |
186 | m_componentCompleted(false), |
187 | m_pendingFitViewport(false), |
188 | m_copyrightsVisible(true), |
189 | m_maximumViewportLatitude(0.0), |
190 | m_initialized(false), |
191 | m_userMinimumZoomLevel(qQNaN()), |
192 | m_userMaximumZoomLevel(qQNaN()), |
193 | m_userMinimumTilt(qQNaN()), |
194 | m_userMaximumTilt(qQNaN()), |
195 | m_userMinimumFieldOfView(qQNaN()), |
196 | m_userMaximumFieldOfView(qQNaN()) |
197 | { |
198 | setAcceptHoverEvents(false); |
199 | setAcceptedMouseButtons(Qt::LeftButton); |
200 | setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); |
201 | setFiltersChildMouseEvents(true); // needed for childMouseEventFilter to work. |
202 | |
203 | m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, |
204 | tr(s: "No Map" ), |
205 | tr(s: "No Map" ), |
206 | false, false, |
207 | 0, |
208 | QByteArrayLiteral("" ), |
209 | QGeoCameraCapabilities()), this); |
210 | m_cameraData.setCenter(QGeoCoordinate(51.5073,-0.1277)); //London city center |
211 | m_cameraData.setZoomLevel(8.0); |
212 | |
213 | m_cameraCapabilities.setTileSize(256); |
214 | m_cameraCapabilities.setSupportsBearing(true); |
215 | m_cameraCapabilities.setSupportsTilting(true); |
216 | m_cameraCapabilities.setMinimumZoomLevel(0); |
217 | m_cameraCapabilities.setMaximumZoomLevel(30); |
218 | m_cameraCapabilities.setMinimumTilt(0); |
219 | m_cameraCapabilities.setMaximumTilt(89.5); |
220 | m_cameraCapabilities.setMinimumFieldOfView(1); |
221 | m_cameraCapabilities.setMaximumFieldOfView(179); |
222 | |
223 | m_minimumTilt = m_cameraCapabilities.minimumTilt(); |
224 | m_maximumTilt = m_cameraCapabilities.maximumTilt(); |
225 | m_minimumFieldOfView = m_cameraCapabilities.minimumFieldOfView(); |
226 | m_maximumFieldOfView = m_cameraCapabilities.maximumFieldOfView(); |
227 | } |
228 | |
229 | QDeclarativeGeoMap::~QDeclarativeGeoMap() |
230 | { |
231 | // Removing map parameters and map items from m_map |
232 | if (m_map) { |
233 | m_map->clearParameters(); |
234 | m_map->clearMapItems(); |
235 | } |
236 | |
237 | // Remove the items from the map, making them deletable. |
238 | // Go in the same order as in removeMapChild: views, groups, then items |
239 | if (!m_mapViews.isEmpty()) { |
240 | const auto mapViews = m_mapViews; |
241 | for (QDeclarativeGeoMapItemView *v : mapViews) { // so that removeMapItemView_real can safely modify m_mapViews; |
242 | if (!v) |
243 | continue; |
244 | |
245 | QQuickItem *parent = v->parentItem(); |
246 | QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(object: parent); |
247 | if (group) |
248 | continue; // Ignore non-top-level MIVs. They will be recursively processed. |
249 | // Identify them as being parented by a MapItemGroup. |
250 | |
251 | removeMapItemView_real(itemView: v); |
252 | } |
253 | } |
254 | |
255 | if (!m_mapItemGroups.isEmpty()) { |
256 | const auto mapGroups = m_mapItemGroups; |
257 | for (QDeclarativeGeoMapItemGroup *g : mapGroups) { |
258 | if (!g) |
259 | continue; |
260 | |
261 | QQuickItem *parent =g->parentItem(); |
262 | QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(object: parent); |
263 | if (group) |
264 | continue; // Ignore non-top-level Groups. They will be recursively processed. |
265 | // Identify them as being parented by a MapItemGroup. |
266 | |
267 | removeMapItemGroup_real(itemGroup: g); |
268 | } |
269 | } |
270 | |
271 | // remove any remaining map items associations |
272 | const auto mapItems = m_mapItems; |
273 | for (auto mi: mapItems) |
274 | removeMapItem_real(item: mi.data()); |
275 | |
276 | if (m_copyrights.data()) |
277 | delete m_copyrights.data(); |
278 | m_copyrights.clear(); |
279 | |
280 | for (auto obj: qAsConst(t&: m_pendingMapObjects)) |
281 | obj->setMap(nullptr); // worst case: going to be setMap(nullptr)'d twice |
282 | |
283 | delete m_map; // map objects get reset here |
284 | } |
285 | |
286 | static QDeclarativeGeoMapType *findMapType(const QList<QDeclarativeGeoMapType *> &types, const QGeoMapType &type) |
287 | { |
288 | for (int i = 0; i < types.size(); ++i) |
289 | if (types[i]->mapType() == type) |
290 | return types[i]; |
291 | return nullptr; |
292 | } |
293 | |
294 | void QDeclarativeGeoMap::onSupportedMapTypesChanged() |
295 | { |
296 | QList<QDeclarativeGeoMapType *> supportedMapTypes; |
297 | QList<QGeoMapType> types = m_mappingManager->supportedMapTypes(); |
298 | for (int i = 0; i < types.size(); ++i) { |
299 | // types that are present and get removed will be deleted at QObject destruction |
300 | QDeclarativeGeoMapType *type = findMapType(types: m_supportedMapTypes, type: types[i]); |
301 | if (!type) |
302 | type = new QDeclarativeGeoMapType(types[i], this); |
303 | supportedMapTypes.append(t: type); |
304 | } |
305 | m_supportedMapTypes.swap(other&: supportedMapTypes); |
306 | if (m_supportedMapTypes.isEmpty()) { |
307 | m_map->setActiveMapType(QGeoMapType()); // no supported map types: setting an invalid one |
308 | } else { |
309 | bool hasMapType = false; |
310 | foreach (QDeclarativeGeoMapType *declarativeType, m_supportedMapTypes) { |
311 | if (declarativeType->mapType() == m_map->activeMapType()) |
312 | hasMapType = true; |
313 | } |
314 | if (!hasMapType) { |
315 | QDeclarativeGeoMapType *type = m_supportedMapTypes.at(i: 0); |
316 | m_activeMapType = type; |
317 | m_map->setActiveMapType(type->mapType()); |
318 | } |
319 | } |
320 | |
321 | emit supportedMapTypesChanged(); |
322 | } |
323 | |
324 | void QDeclarativeGeoMap::setError(QGeoServiceProvider::Error error, const QString &errorString) |
325 | { |
326 | if (m_error == error && m_errorString == errorString) |
327 | return; |
328 | m_error = error; |
329 | m_errorString = errorString; |
330 | emit errorChanged(); |
331 | } |
332 | |
333 | /*! |
334 | \internal |
335 | Called when the mapping manager is initialized AND the declarative element has a valid size > 0 |
336 | */ |
337 | void QDeclarativeGeoMap::initialize() |
338 | { |
339 | // try to keep change signals in the end |
340 | bool visibleAreaHasChanged = false; |
341 | |
342 | QGeoCoordinate center = m_cameraData.center(); |
343 | |
344 | if (!qIsFinite(d: m_userMinimumZoomLevel)) |
345 | setMinimumZoomLevel(minimumZoomLevel: m_map->minimumZoom(), userSet: false); |
346 | else |
347 | setMinimumZoomLevel(minimumZoomLevel: qMax<qreal>(a: m_map->minimumZoom(), b: m_userMinimumZoomLevel), userSet: false); |
348 | |
349 | double bearing = m_cameraData.bearing(); |
350 | double tilt = m_cameraData.tilt(); |
351 | double fov = m_cameraData.fieldOfView(); // Must be 45.0 |
352 | QGeoCameraData cameraData = m_cameraData; |
353 | |
354 | if (!m_cameraCapabilities.supportsBearing() && bearing != 0.0) |
355 | cameraData.setBearing(0); |
356 | |
357 | if (!m_cameraCapabilities.supportsTilting() && tilt != 0.0) |
358 | cameraData.setTilt(0); |
359 | |
360 | m_map->setVisibleArea(m_visibleArea); |
361 | if (m_map->visibleArea() != m_visibleArea) |
362 | visibleAreaHasChanged = true; |
363 | |
364 | cameraData.setFieldOfView(qBound(min: m_cameraCapabilities.minimumFieldOfView(), |
365 | val: fov, |
366 | max: m_cameraCapabilities.maximumFieldOfView())); |
367 | |
368 | // set latitude boundary check |
369 | m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(cameraData); |
370 | m_minimumViewportLatitude = m_map->minimumCenterLatitudeAtZoom(cameraData); |
371 | |
372 | center.setLatitude(qBound(min: m_minimumViewportLatitude, val: center.latitude(), max: m_maximumViewportLatitude)); |
373 | cameraData.setCenter(center); |
374 | |
375 | connect(sender: m_map.data(), signal: &QGeoMap::cameraDataChanged, |
376 | receiver: this, slot: &QDeclarativeGeoMap::onCameraDataChanged); |
377 | m_map->setCameraData(cameraData); // This normally triggers property changed signals. |
378 | // BUT not in this case, since m_cameraData is already == cameraData. |
379 | // So, emit visibleRegionChanged() separately, as |
380 | // the effective visible region becomes available only now. |
381 | |
382 | for (auto obj : qAsConst(t&: m_pendingMapObjects)) |
383 | obj->setMap(m_map); |
384 | |
385 | m_initialized = true; |
386 | |
387 | if (visibleAreaHasChanged) |
388 | emit visibleAreaChanged(); |
389 | connect(sender: m_map.data(), signal: &QGeoMap::visibleAreaChanged, receiver: this, slot: &QDeclarativeGeoMap::visibleAreaChanged); |
390 | |
391 | emit mapReadyChanged(ready: true); |
392 | emit visibleRegionChanged(); |
393 | |
394 | if (m_copyrights) // To not update during initialize() |
395 | update(); |
396 | } |
397 | |
398 | /*! |
399 | \internal |
400 | */ |
401 | void QDeclarativeGeoMap::pluginReady() |
402 | { |
403 | QGeoServiceProvider *provider = m_plugin->sharedGeoServiceProvider(); |
404 | m_mappingManager = provider->mappingManager(); |
405 | |
406 | if (provider->mappingError() != QGeoServiceProvider::NoError) { |
407 | setError(error: provider->mappingError(), errorString: provider->mappingErrorString()); |
408 | return; |
409 | } |
410 | |
411 | if (!m_mappingManager) { |
412 | //TODO Should really be EngineNotSetError (see QML GeoCodeModel) |
413 | setError(error: QGeoServiceProvider::NotSupportedError, errorString: tr(s: "Plugin does not support mapping." )); |
414 | return; |
415 | } |
416 | |
417 | if (!m_mappingManager->isInitialized()) |
418 | connect(sender: m_mappingManager, SIGNAL(initialized()), receiver: this, SLOT(mappingManagerInitialized())); |
419 | else |
420 | mappingManagerInitialized(); |
421 | |
422 | // make sure this is only called once |
423 | disconnect(receiver: this, SLOT(pluginReady())); |
424 | } |
425 | |
426 | /*! |
427 | \internal |
428 | */ |
429 | void QDeclarativeGeoMap::componentComplete() |
430 | { |
431 | m_componentCompleted = true; |
432 | populateParameters(); |
433 | populateMap(); |
434 | QQuickItem::componentComplete(); |
435 | } |
436 | |
437 | /*! |
438 | \qmlproperty MapGestureArea QtLocation::Map::gesture |
439 | |
440 | Contains the MapGestureArea created with the Map. This covers pan, flick and pinch gestures. |
441 | Use \c{gesture.enabled: true} to enable basic gestures, or see \l{MapGestureArea} for |
442 | further details. |
443 | */ |
444 | |
445 | QQuickGeoMapGestureArea *QDeclarativeGeoMap::gesture() |
446 | { |
447 | return m_gestureArea; |
448 | } |
449 | |
450 | /*! |
451 | \internal |
452 | |
453 | This may happen before mappingManagerInitialized() |
454 | */ |
455 | void QDeclarativeGeoMap::populateMap() |
456 | { |
457 | QSet<QObject *> kids(children().cbegin(), children().cend()); |
458 | const QList<QQuickItem *> quickKids = childItems(); |
459 | for (QQuickItem *ite: quickKids) |
460 | kids.insert(value: ite); |
461 | |
462 | for (QObject *k : qAsConst(t&: kids)) { |
463 | addMapChild(child: k); |
464 | } |
465 | } |
466 | |
467 | void QDeclarativeGeoMap::populateParameters() |
468 | { |
469 | QObjectList kids = children(); |
470 | QList<QQuickItem *> quickKids = childItems(); |
471 | for (int i = 0; i < quickKids.count(); ++i) |
472 | kids.append(t: quickKids.at(i)); |
473 | for (int i = 0; i < kids.size(); ++i) { |
474 | QDeclarativeGeoMapParameter *mapParameter = qobject_cast<QDeclarativeGeoMapParameter *>(object: kids.at(i)); |
475 | if (mapParameter) |
476 | addMapParameter(parameter: mapParameter); |
477 | } |
478 | } |
479 | |
480 | /*! |
481 | \internal |
482 | */ |
483 | void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view) |
484 | { |
485 | view->setMap(this); |
486 | } |
487 | |
488 | /*! |
489 | * \internal |
490 | */ |
491 | QSGNode *QDeclarativeGeoMap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) |
492 | { |
493 | if (!m_map) { |
494 | delete oldNode; |
495 | return 0; |
496 | } |
497 | |
498 | QSGRectangleNode *root = static_cast<QSGRectangleNode *>(oldNode); |
499 | if (!root) |
500 | root = window()->createRectangleNode(); |
501 | |
502 | root->setRect(boundingRect()); |
503 | root->setColor(m_color); |
504 | |
505 | QSGNode *content = root->childCount() ? root->firstChild() : 0; |
506 | content = m_map->updateSceneGraph(node: content, window: window()); |
507 | if (content && root->childCount() == 0) |
508 | root->appendChildNode(node: content); |
509 | |
510 | return root; |
511 | } |
512 | |
513 | /*! |
514 | \qmlproperty Plugin QtLocation::Map::plugin |
515 | |
516 | This property holds the plugin which provides the mapping functionality. |
517 | |
518 | This is a write-once property. Once the map has a plugin associated with |
519 | it, any attempted modifications of the plugin will be ignored. |
520 | */ |
521 | |
522 | void QDeclarativeGeoMap::setPlugin(QDeclarativeGeoServiceProvider *plugin) |
523 | { |
524 | if (m_plugin) { |
525 | qmlWarning(me: this) << QStringLiteral("Plugin is a write-once property, and cannot be set again." ); |
526 | return; |
527 | } |
528 | m_plugin = plugin; |
529 | emit pluginChanged(plugin: m_plugin); |
530 | |
531 | if (m_plugin->isAttached()) { |
532 | pluginReady(); |
533 | } else { |
534 | connect(sender: m_plugin, SIGNAL(attached()), |
535 | receiver: this, SLOT(pluginReady())); |
536 | } |
537 | } |
538 | |
539 | /*! |
540 | \internal |
541 | */ |
542 | void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities) |
543 | { |
544 | if (m_map->cameraCapabilities() == oldCameraCapabilities) |
545 | return; |
546 | m_cameraCapabilities = m_map->cameraCapabilities(); |
547 | |
548 | //The zoom level limits are only restricted by the plugins values, if the user has set a more |
549 | //strict zoom level limit before initialization nothing is done here. |
550 | //minimum zoom level might be changed to limit gray bundaries |
551 | //This code assumes that plugins' maximum zoom level will never exceed 30.0 |
552 | if (m_cameraCapabilities.maximumZoomLevelAt256() < m_gestureArea->maximumZoomLevel()) { |
553 | setMaximumZoomLevel(maximumZoomLevel: m_cameraCapabilities.maximumZoomLevelAt256(), userSet: false); |
554 | } else if (m_cameraCapabilities.maximumZoomLevelAt256() > m_gestureArea->maximumZoomLevel()) { |
555 | if (!qIsFinite(d: m_userMaximumZoomLevel)) { |
556 | // If the user didn't set anything |
557 | setMaximumZoomLevel(maximumZoomLevel: m_cameraCapabilities.maximumZoomLevelAt256(), userSet: false); |
558 | } else { // Try to set what the user requested |
559 | // Else if the user set something larger, but that got clamped by the previous camera caps |
560 | setMaximumZoomLevel(maximumZoomLevel: qMin<qreal>(a: m_cameraCapabilities.maximumZoomLevelAt256(), |
561 | b: m_userMaximumZoomLevel), userSet: false); |
562 | } |
563 | } |
564 | |
565 | if (m_cameraCapabilities.minimumZoomLevelAt256() > m_gestureArea->minimumZoomLevel()) { |
566 | setMinimumZoomLevel(minimumZoomLevel: m_cameraCapabilities.minimumZoomLevelAt256(), userSet: false); |
567 | } else if (m_cameraCapabilities.minimumZoomLevelAt256() < m_gestureArea->minimumZoomLevel()) { |
568 | if (!qIsFinite(d: m_userMinimumZoomLevel)) { |
569 | // If the user didn't set anything, trying to set the new caps. |
570 | setMinimumZoomLevel(minimumZoomLevel: m_cameraCapabilities.minimumZoomLevelAt256(), userSet: false); |
571 | } else { // Try to set what the user requested |
572 | // Else if the user set a minimum, m_gestureArea->minimumZoomLevel() might be larger |
573 | // because of different reasons. Resetting it, as if it ends to be the same, |
574 | // no signal will be emitted. |
575 | setMinimumZoomLevel(minimumZoomLevel: qMax<qreal>(a: m_cameraCapabilities.minimumZoomLevelAt256(), |
576 | b: m_userMinimumZoomLevel), userSet: false); |
577 | } |
578 | } |
579 | |
580 | // Tilt |
581 | if (m_cameraCapabilities.maximumTilt() < m_maximumTilt) { |
582 | setMaximumTilt(maximumTilt: m_cameraCapabilities.maximumTilt(), userSet: false); |
583 | } else if (m_cameraCapabilities.maximumTilt() > m_maximumTilt) { |
584 | if (!qIsFinite(d: m_userMaximumTilt)) |
585 | setMaximumTilt(maximumTilt: m_cameraCapabilities.maximumTilt(), userSet: false); |
586 | else // Try to set what the user requested |
587 | setMaximumTilt(maximumTilt: qMin<qreal>(a: m_cameraCapabilities.maximumTilt(), b: m_userMaximumTilt), userSet: false); |
588 | } |
589 | |
590 | if (m_cameraCapabilities.minimumTilt() > m_minimumTilt) { |
591 | setMinimumTilt(minimumTilt: m_cameraCapabilities.minimumTilt(), userSet: false); |
592 | } else if (m_cameraCapabilities.minimumTilt() < m_minimumTilt) { |
593 | if (!qIsFinite(d: m_userMinimumTilt)) |
594 | setMinimumTilt(minimumTilt: m_cameraCapabilities.minimumTilt(), userSet: false); |
595 | else // Try to set what the user requested |
596 | setMinimumTilt(minimumTilt: qMax<qreal>(a: m_cameraCapabilities.minimumTilt(), b: m_userMinimumTilt), userSet: false); |
597 | } |
598 | |
599 | // FoV |
600 | if (m_cameraCapabilities.maximumFieldOfView() < m_maximumFieldOfView) { |
601 | setMaximumFieldOfView(maximumFieldOfView: m_cameraCapabilities.maximumFieldOfView(), userSet: false); |
602 | } else if (m_cameraCapabilities.maximumFieldOfView() > m_maximumFieldOfView) { |
603 | if (!qIsFinite(d: m_userMaximumFieldOfView)) |
604 | setMaximumFieldOfView(maximumFieldOfView: m_cameraCapabilities.maximumFieldOfView(), userSet: false); |
605 | else // Try to set what the user requested |
606 | setMaximumFieldOfView(maximumFieldOfView: qMin<qreal>(a: m_cameraCapabilities.maximumFieldOfView(), b: m_userMaximumFieldOfView), userSet: false); |
607 | } |
608 | |
609 | if (m_cameraCapabilities.minimumFieldOfView() > m_minimumFieldOfView) { |
610 | setMinimumFieldOfView(minimumFieldOfView: m_cameraCapabilities.minimumFieldOfView(), userSet: false); |
611 | } else if (m_cameraCapabilities.minimumFieldOfView() < m_minimumFieldOfView) { |
612 | if (!qIsFinite(d: m_userMinimumFieldOfView)) |
613 | setMinimumFieldOfView(minimumFieldOfView: m_cameraCapabilities.minimumFieldOfView(), userSet: false); |
614 | else // Try to set what the user requested |
615 | setMinimumFieldOfView(minimumFieldOfView: qMax<qreal>(a: m_cameraCapabilities.minimumFieldOfView(), b: m_userMinimumFieldOfView), userSet: false); |
616 | } |
617 | } |
618 | |
619 | /*! |
620 | \internal |
621 | this function will only be ever called once |
622 | */ |
623 | void QDeclarativeGeoMap::mappingManagerInitialized() |
624 | { |
625 | m_map = m_mappingManager->createMap(parent: this); |
626 | |
627 | if (!m_map) |
628 | return; |
629 | |
630 | // Any map items that were added before the plugin was ready |
631 | // need to have setMap called again |
632 | for (const QPointer<QDeclarativeGeoMapItemBase> &item : qAsConst(t&: m_mapItems)) { |
633 | if (item) { |
634 | item->setMap(quickMap: this, map: m_map); |
635 | m_map->addMapItem(item: item.data()); // m_map filters out what is not supported. |
636 | } |
637 | } |
638 | |
639 | /* COPY NOTICE SETUP */ |
640 | m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); |
641 | m_copyrights->setCopyrightsZ(m_maxChildZ + 1); |
642 | m_copyrights->setCopyrightsVisible(m_copyrightsVisible); |
643 | m_copyrights->setMapSource(this); |
644 | |
645 | m_gestureArea->setMap(m_map); |
646 | |
647 | QList<QGeoMapType> types = m_mappingManager->supportedMapTypes(); |
648 | for (int i = 0; i < types.size(); ++i) { |
649 | QDeclarativeGeoMapType *type = new QDeclarativeGeoMapType(types[i], this); |
650 | m_supportedMapTypes.append(t: type); |
651 | } |
652 | |
653 | if (m_activeMapType && m_plugin->name().toLatin1() == m_activeMapType->mapType().pluginName()) { |
654 | m_map->setActiveMapType(m_activeMapType->mapType()); |
655 | } else { |
656 | if (m_activeMapType) |
657 | m_activeMapType->deleteLater(); |
658 | |
659 | if (!m_supportedMapTypes.isEmpty()) { |
660 | m_activeMapType = m_supportedMapTypes.at(i: 0); |
661 | m_map->setActiveMapType(m_activeMapType->mapType()); |
662 | } else { |
663 | m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, |
664 | tr(s: "No Map" ), |
665 | tr(s: "No Map" ), |
666 | false, |
667 | false, |
668 | 0, |
669 | QByteArrayLiteral("" ), |
670 | QGeoCameraCapabilities()), this); |
671 | } |
672 | } |
673 | |
674 | // Update camera capabilities |
675 | onCameraCapabilitiesChanged(oldCameraCapabilities: m_cameraCapabilities); |
676 | |
677 | // Map tiles are built in this call. m_map->minimumZoom() becomes operational |
678 | // after this has been called at least once, after creation. |
679 | // However, getting into the following block may fire a copyrightsChanged that would get lost, |
680 | // as the connections are set up after. |
681 | QString copyrightString; |
682 | QImage copyrightImage; |
683 | if (!m_initialized && width() > 0 && height() > 0) { |
684 | QMetaObject::Connection copyrightStringCatcherConnection = |
685 | connect(sender: m_map.data(), |
686 | signal: QOverload<const QString &>::of(ptr: &QGeoMap::copyrightsChanged), |
687 | slot: [©rightString](const QString ©){ copyrightString = copy; }); |
688 | QMetaObject::Connection copyrightImageCatcherConnection = |
689 | connect(sender: m_map.data(), |
690 | signal: QOverload<const QImage &>::of(ptr: &QGeoMap::copyrightsChanged), |
691 | slot: [©rightImage](const QImage ©){ copyrightImage = copy; }); |
692 | m_map->setViewportSize(QSize(width(), height())); |
693 | initialize(); // This emits the caught signals above |
694 | QObject::disconnect(copyrightStringCatcherConnection); |
695 | QObject::disconnect(copyrightImageCatcherConnection); |
696 | } |
697 | |
698 | |
699 | /* COPYRIGHT SIGNALS REWIRING */ |
700 | connect(sender: m_map.data(), SIGNAL(copyrightsChanged(QImage)), |
701 | receiver: this, SIGNAL(copyrightsChanged(QImage))); |
702 | connect(sender: m_map.data(), SIGNAL(copyrightsChanged(QString)), |
703 | receiver: this, SIGNAL(copyrightsChanged(QString))); |
704 | if (!copyrightString.isEmpty()) |
705 | emit m_map->copyrightsChanged(copyrightsHtml: copyrightString); |
706 | else if (!copyrightImage.isNull()) |
707 | emit m_map->copyrightsChanged(copyrightsImage: copyrightImage); |
708 | |
709 | |
710 | connect(sender: window(), signal: &QQuickWindow::beforeSynchronizing, receiver: this, slot: &QDeclarativeGeoMap::updateItemToWindowTransform, type: Qt::DirectConnection); |
711 | connect(sender: m_map.data(), signal: &QGeoMap::sgNodeChanged, receiver: this, slot: &QDeclarativeGeoMap::onSGNodeChanged); |
712 | connect(sender: m_map.data(), signal: &QGeoMap::cameraCapabilitiesChanged, receiver: this, slot: &QDeclarativeGeoMap::onCameraCapabilitiesChanged); |
713 | |
714 | // This prefetches a buffer around the map |
715 | m_map->prefetchData(); |
716 | |
717 | connect(sender: m_mappingManager, SIGNAL(supportedMapTypesChanged()), receiver: this, SLOT(onSupportedMapTypesChanged())); |
718 | emit minimumZoomLevelChanged(); |
719 | emit maximumZoomLevelChanged(); |
720 | emit supportedMapTypesChanged(); |
721 | emit activeMapTypeChanged(); |
722 | |
723 | // Any map item groups that were added before the plugin was ready |
724 | // DO NOT need to have setMap called again on their children map items |
725 | // because they have been added to m_mapItems, which is processed right above. |
726 | |
727 | |
728 | // All map parameters that were added before the plugin was ready |
729 | // need to be added to m_map |
730 | for (QDeclarativeGeoMapParameter *p : qAsConst(t&: m_mapParameters)) |
731 | m_map->addParameter(param: p); |
732 | |
733 | if (m_initialized) |
734 | update(); |
735 | } |
736 | |
737 | /*! |
738 | \internal |
739 | */ |
740 | QDeclarativeGeoServiceProvider *QDeclarativeGeoMap::plugin() const |
741 | { |
742 | return m_plugin; |
743 | } |
744 | |
745 | /*! |
746 | \internal |
747 | Sets the gesture areas minimum zoom level. If the camera capabilities |
748 | has been set this method honors the boundaries set by it. |
749 | The minimum zoom level will also have a lower bound dependent on the size |
750 | of the canvas, effectively preventing to display out of bounds areas. |
751 | */ |
752 | void QDeclarativeGeoMap::setMinimumZoomLevel(qreal minimumZoomLevel, bool userSet) |
753 | { |
754 | if (minimumZoomLevel >= 0) { |
755 | qreal oldUserMinimumZoomLevel = m_userMinimumZoomLevel; |
756 | if (userSet) |
757 | m_userMinimumZoomLevel = minimumZoomLevel; |
758 | qreal oldMinimumZoomLevel = this->minimumZoomLevel(); |
759 | |
760 | minimumZoomLevel = qBound(min: qreal(m_cameraCapabilities.minimumZoomLevelAt256()), val: minimumZoomLevel, max: maximumZoomLevel()); |
761 | if (m_map) |
762 | minimumZoomLevel = qMax<qreal>(a: minimumZoomLevel, b: m_map->minimumZoom()); |
763 | |
764 | // minimumZoomLevel is, at this point, the implicit minimum zoom level |
765 | m_gestureArea->setMinimumZoomLevel(minimumZoomLevel); |
766 | |
767 | if (zoomLevel() < minimumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) |
768 | setZoomLevel(minimumZoomLevel); |
769 | |
770 | if (qIsNaN(d: m_userMinimumZoomLevel) && oldMinimumZoomLevel != minimumZoomLevel) |
771 | emit minimumZoomLevelChanged(); |
772 | else if (userSet && oldUserMinimumZoomLevel != m_userMinimumZoomLevel) |
773 | emit minimumZoomLevelChanged(); |
774 | } |
775 | } |
776 | |
777 | /*! |
778 | \qmlproperty real QtLocation::Map::minimumZoomLevel |
779 | |
780 | This property holds the minimum valid zoom level for the map. |
781 | |
782 | The minimum zoom level defined by the \l plugin used is a lower bound for |
783 | this property. However, the returned value is also canvas-size-dependent, and |
784 | can be higher than the user-specified value, or than the minimum zoom level |
785 | defined by the plugin used, to prevent the map from being smaller than the |
786 | viewport in either dimension. |
787 | |
788 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0. |
789 | */ |
790 | |
791 | qreal QDeclarativeGeoMap::minimumZoomLevel() const |
792 | { |
793 | if (!qIsNaN(d: m_userMinimumZoomLevel)) |
794 | return m_userMinimumZoomLevel; |
795 | else |
796 | return m_gestureArea->minimumZoomLevel(); |
797 | } |
798 | |
799 | /*! |
800 | \internal |
801 | */ |
802 | qreal QDeclarativeGeoMap::implicitMinimumZoomLevel() const |
803 | { |
804 | return m_gestureArea->minimumZoomLevel(); |
805 | } |
806 | |
807 | /*! |
808 | \internal |
809 | */ |
810 | qreal QDeclarativeGeoMap::effectiveMinimumZoomLevel() const |
811 | { |
812 | return qMax<qreal>(a: minimumZoomLevel(), b: implicitMinimumZoomLevel()); |
813 | } |
814 | |
815 | /*! |
816 | \internal |
817 | Sets the gesture areas maximum zoom level. If the camera capabilities |
818 | has been set this method honors the boundaries set by it. |
819 | */ |
820 | void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel, bool userSet) |
821 | { |
822 | if (maximumZoomLevel >= 0) { |
823 | if (userSet) |
824 | m_userMaximumZoomLevel = maximumZoomLevel; |
825 | qreal oldMaximumZoomLevel = this->maximumZoomLevel(); |
826 | |
827 | maximumZoomLevel = qBound(min: minimumZoomLevel(), val: maximumZoomLevel, max: qreal(m_cameraCapabilities.maximumZoomLevelAt256())); |
828 | |
829 | m_gestureArea->setMaximumZoomLevel(maximumZoomLevel); |
830 | |
831 | if (zoomLevel() > maximumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) |
832 | setZoomLevel(maximumZoomLevel); |
833 | |
834 | if (oldMaximumZoomLevel != maximumZoomLevel) |
835 | emit maximumZoomLevelChanged(); |
836 | } |
837 | } |
838 | |
839 | /*! |
840 | \qmlproperty real QtLocation::Map::maximumZoomLevel |
841 | |
842 | This property holds the maximum valid zoom level for the map. |
843 | |
844 | The maximum zoom level is defined by the \l plugin used. |
845 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 30. |
846 | */ |
847 | |
848 | qreal QDeclarativeGeoMap::maximumZoomLevel() const |
849 | { |
850 | return m_gestureArea->maximumZoomLevel(); |
851 | } |
852 | |
853 | /*! |
854 | \qmlproperty real QtLocation::Map::zoomLevel |
855 | |
856 | This property holds the zoom level for the map. |
857 | |
858 | Larger values for the zoom level provide more detail. Zoom levels |
859 | are always non-negative. The default value is 8.0. Depending on the plugin in use, |
860 | values outside the [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which |
861 | tiles are available, may be accepted, or clamped. |
862 | */ |
863 | void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) |
864 | { |
865 | return setZoomLevel(zoomLevel, overzoom: m_cameraCapabilities.overzoomEnabled()); |
866 | } |
867 | |
868 | /*! |
869 | \internal |
870 | |
871 | Sets the zoom level. |
872 | Larger values for the zoom level provide more detail. Zoom levels |
873 | are always non-negative. The default value is 8.0. Values outside the |
874 | [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which |
875 | tiles are available, can be accepted or clamped by setting the overzoom argument |
876 | to true or false respectively. |
877 | */ |
878 | void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel, bool overzoom) |
879 | { |
880 | if (zoomLevel < 0) |
881 | return; |
882 | |
883 | if (m_initialized) { |
884 | QGeoCameraData cameraData = m_map->cameraData(); |
885 | if (cameraData.zoomLevel() == zoomLevel) |
886 | return; |
887 | |
888 | cameraData.setZoomLevel(qBound<qreal>(min: overzoom ? m_map->minimumZoom() : effectiveMinimumZoomLevel(), |
889 | val: zoomLevel, |
890 | max: overzoom ? 30 : maximumZoomLevel())); |
891 | m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(cameraData); |
892 | m_minimumViewportLatitude = m_map->minimumCenterLatitudeAtZoom(cameraData); |
893 | QGeoCoordinate coord = cameraData.center(); |
894 | coord.setLatitude(qBound(min: m_minimumViewportLatitude, val: coord.latitude(), max: m_maximumViewportLatitude)); |
895 | cameraData.setCenter(coord); |
896 | m_map->setCameraData(cameraData); |
897 | } else { |
898 | const bool zlHasChanged = zoomLevel != m_cameraData.zoomLevel(); |
899 | m_cameraData.setZoomLevel(zoomLevel); |
900 | if (zlHasChanged) { |
901 | emit zoomLevelChanged(zoomLevel); |
902 | // do not emit visibleRegionChanged() here, because, if the map isn't initialized, |
903 | // the getter won't return anything updated |
904 | } |
905 | } |
906 | } |
907 | |
908 | bool QDeclarativeGeoMap::addMapChild(QObject *child) |
909 | { |
910 | // dispatch items appropriately |
911 | QDeclarativeGeoMapItemView *mapView = qobject_cast<QDeclarativeGeoMapItemView *>(object: child); |
912 | if (mapView) |
913 | return addMapItemView_real(itemView: mapView); |
914 | |
915 | QDeclarativeGeoMapItemGroup *itemGroup = qobject_cast<QDeclarativeGeoMapItemGroup *>(object: child); |
916 | if (itemGroup) // addMapItemView calls addMapItemGroup |
917 | return addMapItemGroup_real(itemGroup); |
918 | |
919 | QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(object: child); |
920 | if (mapItem) |
921 | return addMapItem_real(item: mapItem); |
922 | |
923 | QGeoMapObject *mapObject = qobject_cast<QGeoMapObject *>(object: child); |
924 | if (mapObject) |
925 | addMapObject(object: mapObject); // this emits mapObjectsChanged, != mapItemsChanged |
926 | return false; |
927 | } |
928 | |
929 | bool QDeclarativeGeoMap::removeMapChild(QObject *child) |
930 | { |
931 | // dispatch items appropriately |
932 | QDeclarativeGeoMapItemView *mapView = qobject_cast<QDeclarativeGeoMapItemView *>(object: child); |
933 | if (mapView) |
934 | return removeMapItemView_real(itemView: mapView); |
935 | |
936 | QDeclarativeGeoMapItemGroup *itemGroup = qobject_cast<QDeclarativeGeoMapItemGroup *>(object: child); |
937 | if (itemGroup) // removeMapItemView calls removeMapItemGroup for itself. |
938 | return removeMapItemGroup_real(itemGroup); |
939 | |
940 | QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(object: child); |
941 | if (mapItem) |
942 | return removeMapItem_real(item: mapItem); |
943 | |
944 | QGeoMapObject *mapObject = qobject_cast<QGeoMapObject *>(object: child); |
945 | if (mapObject) |
946 | removeMapObject(object: mapObject); // this emits mapObjectsChanged, != mapItemsChanged |
947 | return false; |
948 | } |
949 | |
950 | bool QDeclarativeGeoMap::isGroupNested(QDeclarativeGeoMapItemGroup *group) |
951 | { |
952 | QObject *parent = group->parent(); |
953 | // Nested groups have parent set in parent's componentComplete() |
954 | // Those instantiated by MapItemView's delegateModel, however, do not, |
955 | // but have setParentItem set. |
956 | return qobject_cast<QDeclarativeGeoMapItemGroup *>(object: parent) |
957 | || qobject_cast<QDeclarativeGeoMapItemGroup *>(object: group->parentItem()); |
958 | } |
959 | |
960 | qreal QDeclarativeGeoMap::zoomLevel() const |
961 | { |
962 | if (m_initialized) |
963 | return m_map->cameraData().zoomLevel(); |
964 | return m_cameraData.zoomLevel(); |
965 | } |
966 | |
967 | /*! |
968 | \qmlproperty real QtLocation::Map::bearing |
969 | |
970 | This property holds the bearing for the map. |
971 | The default value is 0. |
972 | If the Plugin used for the Map supports bearing, the valid range for this value is between 0 and 360. |
973 | If the Plugin used for the Map does not support bearing, changing this property will have no effect. |
974 | |
975 | \since QtLocation 5.9 |
976 | */ |
977 | void QDeclarativeGeoMap::setBearing(qreal bearing) |
978 | { |
979 | bearing = sanitizeBearing(bearing); |
980 | if (m_initialized) { |
981 | QGeoCameraData cameraData = m_map->cameraData(); |
982 | cameraData.setBearing(bearing); |
983 | m_map->setCameraData(cameraData); |
984 | } else { |
985 | const bool bearingHasChanged = bearing != m_cameraData.bearing(); |
986 | m_cameraData.setBearing(bearing); |
987 | if (bearingHasChanged) { |
988 | emit bearingChanged(bearing); |
989 | // do not emit visibleRegionChanged() here, because, if the map isn't initialized, |
990 | // the getter won't return anything updated |
991 | } |
992 | } |
993 | } |
994 | |
995 | /*! |
996 | \qmlmethod void QtLocation::Map::setBearing(real bearing, coordinate coordinate) |
997 | |
998 | Sets the bearing for the map to \a bearing, rotating it around \a coordinate. |
999 | If the Plugin used for the Map supports bearing, the valid range for \a bearing is between 0 and 360. |
1000 | If the Plugin used for the Map does not support bearing, or if the map is tilted and \a coordinate happens |
1001 | to be behind the camera, or if the map is not ready (see \l mapReady), calling this method will have no effect. |
1002 | |
1003 | The release of this API with Qt 5.10 is a Technology Preview. |
1004 | |
1005 | \since 5.10 |
1006 | */ |
1007 | void QDeclarativeGeoMap::setBearing(qreal bearing, const QGeoCoordinate &coordinate) |
1008 | { |
1009 | if (!m_initialized) |
1010 | return; |
1011 | |
1012 | const QGeoCoordinate currentCenter = center(); |
1013 | const qreal currentBearing = QDeclarativeGeoMap::bearing(); |
1014 | bearing = sanitizeBearing(bearing); |
1015 | |
1016 | if (!coordinate.isValid() |
1017 | || !qIsFinite(d: bearing) |
1018 | || (coordinate == currentCenter && bearing == currentBearing)) |
1019 | return; |
1020 | |
1021 | if (m_map->capabilities() & QGeoMap::SupportsSetBearing) |
1022 | m_map->setBearing(bearing, coordinate); |
1023 | } |
1024 | |
1025 | qreal QDeclarativeGeoMap::bearing() const |
1026 | { |
1027 | if (m_initialized) |
1028 | return m_map->cameraData().bearing(); |
1029 | return m_cameraData.bearing(); |
1030 | } |
1031 | |
1032 | /*! |
1033 | \qmlproperty real QtLocation::Map::tilt |
1034 | |
1035 | This property holds the tilt for the map, in degrees. |
1036 | The default value is 0. |
1037 | The valid range for this value is [ minimumTilt, maximumTilt ]. |
1038 | If the Plugin used for the Map does not support tilting, changing this property will have no effect. |
1039 | |
1040 | \sa minimumTilt, maximumTilt |
1041 | |
1042 | \since QtLocation 5.9 |
1043 | */ |
1044 | void QDeclarativeGeoMap::setTilt(qreal tilt) |
1045 | { |
1046 | tilt = qBound(min: minimumTilt(), val: tilt, max: maximumTilt()); |
1047 | |
1048 | if (m_initialized) { |
1049 | QGeoCameraData cameraData = m_map->cameraData(); |
1050 | cameraData.setTilt(tilt); |
1051 | m_map->setCameraData(cameraData); |
1052 | } else { |
1053 | const bool tiltHasChanged = tilt != m_cameraData.tilt(); |
1054 | m_cameraData.setTilt(tilt); |
1055 | if (tiltHasChanged) { |
1056 | emit tiltChanged(tilt); |
1057 | // do not emit visibleRegionChanged() here, because, if the map isn't initialized, |
1058 | // the getter won't return anything updated |
1059 | } |
1060 | } |
1061 | } |
1062 | |
1063 | qreal QDeclarativeGeoMap::tilt() const |
1064 | { |
1065 | if (m_initialized) |
1066 | return m_map->cameraData().tilt(); |
1067 | return m_cameraData.tilt(); |
1068 | } |
1069 | |
1070 | void QDeclarativeGeoMap::setMinimumTilt(qreal minimumTilt, bool userSet) |
1071 | { |
1072 | if (minimumTilt >= 0) { |
1073 | if (userSet) |
1074 | m_userMinimumTilt = minimumTilt; |
1075 | qreal oldMinimumTilt = this->minimumTilt(); |
1076 | |
1077 | m_minimumTilt = qBound<double>(min: m_cameraCapabilities.minimumTilt(), |
1078 | val: minimumTilt, |
1079 | max: m_cameraCapabilities.maximumTilt()); |
1080 | |
1081 | if (tilt() < m_minimumTilt) |
1082 | setTilt(m_minimumTilt); |
1083 | |
1084 | if (oldMinimumTilt != m_minimumTilt) |
1085 | emit minimumTiltChanged(minimumTilt: m_minimumTilt); |
1086 | } |
1087 | } |
1088 | |
1089 | /*! |
1090 | \qmlproperty real QtLocation::Map::fieldOfView |
1091 | |
1092 | This property holds the field of view of the camera used to look at the map, in degrees. |
1093 | If the plugin property of the map is not set, or the plugin does not support mapping, the value is 45 degrees. |
1094 | |
1095 | Note that changing this value implicitly changes also the distance between the camera and the map, |
1096 | so that, at a tilting angle of 0 degrees, the resulting image is identical for any value used for this property. |
1097 | |
1098 | For more information about this parameter, consult the Wikipedia articles about \l {https://en.wikipedia.org/wiki/Field_of_view} {Field of view} and |
1099 | \l {https://en.wikipedia.org/wiki/Angle_of_view} {Angle of view}. |
1100 | |
1101 | \sa minimumFieldOfView, maximumFieldOfView |
1102 | |
1103 | \since QtLocation 5.9 |
1104 | */ |
1105 | void QDeclarativeGeoMap::setFieldOfView(qreal fieldOfView) |
1106 | { |
1107 | fieldOfView = qBound(min: minimumFieldOfView(), val: fieldOfView, max: maximumFieldOfView()); |
1108 | |
1109 | if (m_initialized) { |
1110 | QGeoCameraData cameraData = m_map->cameraData(); |
1111 | cameraData.setFieldOfView(fieldOfView); |
1112 | m_map->setCameraData(cameraData); |
1113 | } else { |
1114 | const bool fovChanged = fieldOfView != m_cameraData.fieldOfView(); |
1115 | m_cameraData.setFieldOfView(fieldOfView); |
1116 | if (fovChanged) { |
1117 | emit fieldOfViewChanged(fieldOfView); |
1118 | // do not emit visibleRegionChanged() here, because, if the map isn't initialized, |
1119 | // the getter won't return anything updated |
1120 | } |
1121 | } |
1122 | } |
1123 | |
1124 | qreal QDeclarativeGeoMap::fieldOfView() const |
1125 | { |
1126 | if (m_initialized) |
1127 | return m_map->cameraData().fieldOfView(); |
1128 | return m_cameraData.fieldOfView(); |
1129 | } |
1130 | |
1131 | void QDeclarativeGeoMap::setMinimumFieldOfView(qreal minimumFieldOfView, bool userSet) |
1132 | { |
1133 | if (minimumFieldOfView > 0 && minimumFieldOfView < 180.0) { |
1134 | if (userSet) |
1135 | m_userMinimumFieldOfView = minimumFieldOfView; |
1136 | qreal oldMinimumFoV = this->minimumFieldOfView(); |
1137 | |
1138 | m_minimumFieldOfView = qBound<double>(min: m_cameraCapabilities.minimumFieldOfView(), |
1139 | val: minimumFieldOfView, |
1140 | max: m_cameraCapabilities.maximumFieldOfView()); |
1141 | |
1142 | if (fieldOfView() < m_minimumFieldOfView) |
1143 | setFieldOfView(m_minimumFieldOfView); |
1144 | |
1145 | if (oldMinimumFoV != m_minimumFieldOfView) |
1146 | emit minimumFieldOfViewChanged(minimumFieldOfView: m_minimumFieldOfView); |
1147 | } |
1148 | } |
1149 | |
1150 | /*! |
1151 | \qmlproperty real QtLocation::Map::minimumFieldOfView |
1152 | |
1153 | This property holds the minimum valid field of view for the map, in degrees. |
1154 | |
1155 | The minimum tilt field of view by the \l plugin used is a lower bound for |
1156 | this property. |
1157 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 1. |
1158 | |
1159 | \sa fieldOfView, maximumFieldOfView |
1160 | |
1161 | \since QtLocation 5.9 |
1162 | */ |
1163 | qreal QDeclarativeGeoMap::minimumFieldOfView() const |
1164 | { |
1165 | return m_minimumFieldOfView; |
1166 | } |
1167 | |
1168 | void QDeclarativeGeoMap::setMaximumFieldOfView(qreal maximumFieldOfView, bool userSet) |
1169 | { |
1170 | if (maximumFieldOfView > 0 && maximumFieldOfView < 180.0) { |
1171 | if (userSet) |
1172 | m_userMaximumFieldOfView = maximumFieldOfView; |
1173 | qreal oldMaximumFoV = this->maximumFieldOfView(); |
1174 | |
1175 | m_maximumFieldOfView = qBound<double>(min: m_cameraCapabilities.minimumFieldOfView(), |
1176 | val: maximumFieldOfView, |
1177 | max: m_cameraCapabilities.maximumFieldOfView()); |
1178 | |
1179 | if (fieldOfView() > m_maximumFieldOfView) |
1180 | setFieldOfView(m_maximumFieldOfView); |
1181 | |
1182 | if (oldMaximumFoV != m_maximumFieldOfView) |
1183 | emit maximumFieldOfViewChanged(maximumFieldOfView: m_maximumFieldOfView); |
1184 | } |
1185 | } |
1186 | |
1187 | /*! |
1188 | \qmlproperty real QtLocation::Map::maximumFieldOfView |
1189 | |
1190 | This property holds the maximum valid field of view for the map, in degrees. |
1191 | |
1192 | The minimum tilt field of view by the \l plugin used is an upper bound for |
1193 | this property. |
1194 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 179. |
1195 | |
1196 | \sa fieldOfView, minimumFieldOfView |
1197 | |
1198 | \since QtLocation 5.9 |
1199 | */ |
1200 | qreal QDeclarativeGeoMap::maximumFieldOfView() const |
1201 | { |
1202 | return m_maximumFieldOfView; |
1203 | } |
1204 | |
1205 | /*! |
1206 | \qmlproperty real QtLocation::Map::minimumTilt |
1207 | |
1208 | This property holds the minimum valid tilt for the map, in degrees. |
1209 | |
1210 | The minimum tilt defined by the \l plugin used is a lower bound for |
1211 | this property. |
1212 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0. |
1213 | |
1214 | Since QtLocation 5.12, plugins can additionally restrict this value depending on the current zoom level. |
1215 | |
1216 | \sa tilt, maximumTilt |
1217 | |
1218 | \since QtLocation 5.9 |
1219 | */ |
1220 | qreal QDeclarativeGeoMap::minimumTilt() const |
1221 | { |
1222 | return m_minimumTilt; |
1223 | } |
1224 | |
1225 | void QDeclarativeGeoMap::setMaximumTilt(qreal maximumTilt, bool userSet) |
1226 | { |
1227 | if (maximumTilt >= 0) { |
1228 | if (userSet) |
1229 | m_userMaximumTilt = maximumTilt; |
1230 | qreal oldMaximumTilt = this->maximumTilt(); |
1231 | |
1232 | m_maximumTilt = qBound<double>(min: m_cameraCapabilities.minimumTilt(), |
1233 | val: maximumTilt, |
1234 | max: m_cameraCapabilities.maximumTilt()); |
1235 | |
1236 | if (tilt() > m_maximumTilt) |
1237 | setTilt(m_maximumTilt); |
1238 | |
1239 | if (oldMaximumTilt != m_maximumTilt) |
1240 | emit maximumTiltChanged(maximumTilt: m_maximumTilt); |
1241 | } |
1242 | } |
1243 | |
1244 | /*! |
1245 | \qmlproperty real QtLocation::Map::maximumTilt |
1246 | |
1247 | This property holds the maximum valid tilt for the map, in degrees. |
1248 | |
1249 | The maximum tilt defined by the \l plugin used is an upper bound for |
1250 | this property. |
1251 | If the \l plugin property is not set or the plugin does not support mapping, this property is \c 89.5. |
1252 | |
1253 | Since QtLocation 5.12, plugins can additionally restrict this value depending on the current zoom level. |
1254 | |
1255 | \sa tilt, minimumTilt |
1256 | |
1257 | \since QtLocation 5.9 |
1258 | */ |
1259 | qreal QDeclarativeGeoMap::maximumTilt() const |
1260 | { |
1261 | return m_maximumTilt; |
1262 | } |
1263 | |
1264 | /*! |
1265 | \qmlproperty coordinate QtLocation::Map::center |
1266 | |
1267 | This property holds the coordinate which occupies the center of the |
1268 | mapping viewport. Invalid center coordinates are ignored. |
1269 | |
1270 | The default value is an arbitrary valid coordinate. |
1271 | */ |
1272 | void QDeclarativeGeoMap::setCenter(const QGeoCoordinate ¢er) |
1273 | { |
1274 | if (!center.isValid()) |
1275 | return; |
1276 | |
1277 | if (m_initialized) { |
1278 | QGeoCoordinate coord(center); |
1279 | coord.setLatitude(qBound(min: m_minimumViewportLatitude, val: center.latitude(), max: m_maximumViewportLatitude)); |
1280 | QGeoCameraData cameraData = m_map->cameraData(); |
1281 | cameraData.setCenter(coord); |
1282 | m_map->setCameraData(cameraData); |
1283 | } else { |
1284 | const bool centerHasChanged = center != m_cameraData.center(); |
1285 | m_cameraData.setCenter(center); |
1286 | if (centerHasChanged) { |
1287 | emit centerChanged(coordinate: center); |
1288 | // do not emit visibleRegionChanged() here, because, if the map isn't initialized, |
1289 | // the getter won't return anything updated |
1290 | } |
1291 | } |
1292 | } |
1293 | |
1294 | QGeoCoordinate QDeclarativeGeoMap::center() const |
1295 | { |
1296 | if (m_initialized) |
1297 | return m_map->cameraData().center(); |
1298 | return m_cameraData.center(); |
1299 | } |
1300 | |
1301 | |
1302 | /*! |
1303 | \qmlproperty geoshape QtLocation::Map::visibleRegion |
1304 | |
1305 | This property holds the region which occupies the viewport of |
1306 | the map. The camera is positioned in the center of the shape, and |
1307 | at the largest integral zoom level possible which allows the |
1308 | whole shape to be visible on the screen. This implies that |
1309 | reading this property back shortly after having been set the |
1310 | returned area is equal or larger than the set area. |
1311 | |
1312 | Setting this property implicitly changes the \l center and |
1313 | \l zoomLevel of the map. Any previously set value to those |
1314 | properties will be overridden. |
1315 | |
1316 | \note Since Qt 5.14 This property provides change notifications. |
1317 | |
1318 | \since 5.6 |
1319 | */ |
1320 | void QDeclarativeGeoMap::setVisibleRegion(const QGeoShape &shape) |
1321 | { |
1322 | if (shape.boundingGeoRectangle() == visibleRegion()) |
1323 | return; |
1324 | |
1325 | m_visibleRegion = shape.boundingGeoRectangle(); |
1326 | if (!m_visibleRegion.isValid() |
1327 | || (m_visibleRegion.bottomRight().latitude() >= 85.0) // rect entirely outside web mercator |
1328 | || (m_visibleRegion.topLeft().latitude() <= -85.0)) { |
1329 | // shape invalidated -> nothing to fit anymore |
1330 | m_visibleRegion = QGeoRectangle(); |
1331 | m_pendingFitViewport = false; |
1332 | emit visibleRegionChanged(); |
1333 | return; |
1334 | } |
1335 | |
1336 | if (!m_map || !width() || !height()) { |
1337 | m_pendingFitViewport = true; |
1338 | emit visibleRegionChanged(); |
1339 | return; |
1340 | } |
1341 | |
1342 | fitViewportToGeoShape(shape: m_visibleRegion); |
1343 | emit visibleRegionChanged(); |
1344 | } |
1345 | |
1346 | QGeoShape QDeclarativeGeoMap::visibleRegion() const |
1347 | { |
1348 | if (!m_map || !width() || !height()) |
1349 | return m_visibleRegion; |
1350 | |
1351 | if (m_map->capabilities() & QGeoMap::SupportsVisibleRegion) { |
1352 | return m_map->visibleRegion(); |
1353 | } else { |
1354 | // ToDo: handle projections not supporting visible region in a better way. |
1355 | // This approach will fail when horizon is in the view or the map is greatly zoomed out. |
1356 | QList<QGeoCoordinate> visiblePoly; |
1357 | visiblePoly << m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(0,0), clipToViewport: false); |
1358 | visiblePoly << m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(m_map->viewportWidth() - 1, |
1359 | 0), clipToViewport: false); |
1360 | visiblePoly << m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(m_map->viewportWidth() - 1, |
1361 | m_map->viewportHeight() - 1), clipToViewport: false); |
1362 | visiblePoly << m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(0, |
1363 | m_map->viewportHeight() - 1), clipToViewport: false); |
1364 | QGeoPath path; |
1365 | path.setPath(visiblePoly); |
1366 | return path.boundingGeoRectangle(); |
1367 | } |
1368 | } |
1369 | |
1370 | /*! |
1371 | \qmlproperty bool QtLocation::Map::copyrightsVisible |
1372 | |
1373 | This property holds the visibility of the copyrights notice. The notice is usually |
1374 | displayed in the bottom left corner. By default, this property is set to \c true. |
1375 | |
1376 | \note Many map providers require the notice to be visible as part of the terms and conditions. |
1377 | Please consult the relevant provider documentation before turning this notice off. |
1378 | |
1379 | \since 5.7 |
1380 | */ |
1381 | void QDeclarativeGeoMap::setCopyrightsVisible(bool visible) |
1382 | { |
1383 | if (m_copyrightsVisible == visible) |
1384 | return; |
1385 | |
1386 | if (!m_copyrights.isNull()) |
1387 | m_copyrights->setCopyrightsVisible(visible); |
1388 | |
1389 | m_copyrightsVisible = visible; |
1390 | emit copyrightsVisibleChanged(visible); |
1391 | } |
1392 | |
1393 | bool QDeclarativeGeoMap::copyrightsVisible() const |
1394 | { |
1395 | return m_copyrightsVisible; |
1396 | } |
1397 | |
1398 | |
1399 | |
1400 | /*! |
1401 | \qmlproperty color QtLocation::Map::color |
1402 | |
1403 | This property holds the background color of the map element. |
1404 | |
1405 | \since 5.6 |
1406 | */ |
1407 | void QDeclarativeGeoMap::setColor(const QColor &color) |
1408 | { |
1409 | if (color != m_color) { |
1410 | m_color = color; |
1411 | update(); |
1412 | emit colorChanged(color: m_color); |
1413 | } |
1414 | } |
1415 | |
1416 | QColor QDeclarativeGeoMap::color() const |
1417 | { |
1418 | return m_color; |
1419 | } |
1420 | |
1421 | /*! |
1422 | \qmlproperty rect QtLocation::Map::visibleArea |
1423 | |
1424 | This property holds the visible area inside the Map QML element. |
1425 | It is a rect whose coordinates are relative to the Map element. |
1426 | Its size will be clamped to the size of the Map element. |
1427 | A null visibleArea means that the whole Map is visible. |
1428 | |
1429 | \since 5.12 |
1430 | */ |
1431 | QRectF QDeclarativeGeoMap::visibleArea() const |
1432 | { |
1433 | if (m_initialized) |
1434 | return m_map->visibleArea(); |
1435 | return m_visibleArea; |
1436 | } |
1437 | |
1438 | void QDeclarativeGeoMap::setVisibleArea(const QRectF &visibleArea) |
1439 | { |
1440 | const QRectF oldVisibleArea = QDeclarativeGeoMap::visibleArea(); |
1441 | if (visibleArea == oldVisibleArea) |
1442 | return; |
1443 | |
1444 | if (!visibleArea.isValid() && !visibleArea.isEmpty()) // values < 0 |
1445 | return; |
1446 | |
1447 | if (m_initialized) { |
1448 | m_map->setVisibleArea(visibleArea); |
1449 | const QRectF newVisibleArea = QDeclarativeGeoMap::visibleArea(); |
1450 | if (newVisibleArea != oldVisibleArea) { |
1451 | // polish map items |
1452 | for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(t&: m_mapItems)) { |
1453 | if (i) |
1454 | i->visibleAreaChanged(); |
1455 | } |
1456 | } |
1457 | } else { |
1458 | m_visibleArea = visibleArea; |
1459 | const QRectF newVisibleArea = QDeclarativeGeoMap::visibleArea(); |
1460 | if (newVisibleArea != oldVisibleArea) |
1461 | emit visibleAreaChanged(); |
1462 | } |
1463 | } |
1464 | |
1465 | /*! |
1466 | \qmlproperty bool QtLocation::Map::mapReady |
1467 | |
1468 | This property holds whether the map has been successfully initialized and is ready to be used. |
1469 | Some methods, such as \l fromCoordinate and \l toCoordinate, will not work before the map is ready. |
1470 | Due to the architecture of the \l Map, it's advised to use the signal emitted for this property |
1471 | in place of \l {QtQml::Component::completed()}{Component.onCompleted}, to make sure that everything behaves as expected. |
1472 | |
1473 | \since 5.9 |
1474 | */ |
1475 | bool QDeclarativeGeoMap::mapReady() const |
1476 | { |
1477 | return m_initialized; |
1478 | } |
1479 | |
1480 | QMargins QDeclarativeGeoMap::mapMargins() const |
1481 | { |
1482 | const QRectF va = m_map->visibleArea(); |
1483 | if (va.isEmpty()) |
1484 | return QMargins(); |
1485 | return QMargins( va.x() |
1486 | , va.y() |
1487 | , width() - va.width() - va.x() |
1488 | , height() - va.height() - va.y()); |
1489 | } |
1490 | |
1491 | /*! |
1492 | \qmlproperty list<MapType> QtLocation::Map::supportedMapTypes |
1493 | |
1494 | This read-only property holds the set of \l{MapType}{map types} supported by this map. |
1495 | |
1496 | \sa activeMapType |
1497 | */ |
1498 | QQmlListProperty<QDeclarativeGeoMapType> QDeclarativeGeoMap::supportedMapTypes() |
1499 | { |
1500 | return QQmlListProperty<QDeclarativeGeoMapType>(this, &m_supportedMapTypes); |
1501 | } |
1502 | |
1503 | /*! |
1504 | \qmlmethod void QtLocation::Map::alignCoordinateToPoint(coordinate coordinate, QPointF point) |
1505 | |
1506 | Aligns \a coordinate to \a point. |
1507 | This method effectively extends the functionality offered by the \l center qml property, allowing |
1508 | to align a coordinate to point of the Map element other than its center. |
1509 | This is useful in those applications where the center of the scene (e.g., a cursor) is not to be |
1510 | placed exactly in the center of the map. |
1511 | |
1512 | If the map is tilted, and \a coordinate happens to be behind the camera, or if the map is not ready |
1513 | (see \l mapReady), calling this method will have no effect. |
1514 | |
1515 | The release of this API with Qt 5.10 is a Technology Preview. |
1516 | |
1517 | \sa center |
1518 | |
1519 | \since 5.10 |
1520 | */ |
1521 | void QDeclarativeGeoMap::alignCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &point) |
1522 | { |
1523 | if (!m_map || !(m_map->capabilities() & QGeoMap::SupportsAnchoringCoordinate)) |
1524 | return; |
1525 | |
1526 | if (!coordinate.isValid() |
1527 | || !qIsFinite(d: point.x()) |
1528 | || !qIsFinite(d: point.y())) |
1529 | return; |
1530 | |
1531 | m_map->anchorCoordinateToPoint(coordinate, anchorPoint: point); |
1532 | } |
1533 | |
1534 | /*! |
1535 | \qmlmethod coordinate QtLocation::Map::toCoordinate(QPointF position, bool clipToViewPort) |
1536 | |
1537 | Returns the coordinate which corresponds to the \a position relative to the map item. |
1538 | |
1539 | If \a clipToViewPort is \c true, or not supplied then returns an invalid coordinate if |
1540 | \a position is not within the current viewport. |
1541 | */ |
1542 | QGeoCoordinate QDeclarativeGeoMap::toCoordinate(const QPointF &position, bool clipToViewPort) const |
1543 | { |
1544 | if (m_map) |
1545 | return m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(position), clipToViewport: clipToViewPort); |
1546 | else |
1547 | return QGeoCoordinate(); |
1548 | } |
1549 | |
1550 | /*! |
1551 | \qmlmethod point QtLocation::Map::fromCoordinate(coordinate coordinate, bool clipToViewPort) |
1552 | |
1553 | Returns the position relative to the map item which corresponds to the \a coordinate. |
1554 | |
1555 | If \a clipToViewPort is \c true, or not supplied then returns an invalid QPointF if |
1556 | \a coordinate is not within the current viewport. |
1557 | */ |
1558 | QPointF QDeclarativeGeoMap::fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort) const |
1559 | { |
1560 | if (m_map) |
1561 | return m_map->geoProjection().coordinateToItemPosition(coordinate, clipToViewport: clipToViewPort).toPointF(); |
1562 | else |
1563 | return QPointF(qQNaN(), qQNaN()); |
1564 | } |
1565 | |
1566 | /*! |
1567 | \qmlmethod void QtLocation::Map::pan(int dx, int dy) |
1568 | |
1569 | Starts panning the map by \a dx pixels along the x-axis and |
1570 | by \a dy pixels along the y-axis. |
1571 | |
1572 | Positive values for \a dx move the map right, negative values left. |
1573 | Positive values for \a dy move the map down, negative values up. |
1574 | |
1575 | During panning the \l center, and \l zoomLevel may change. |
1576 | */ |
1577 | void QDeclarativeGeoMap::pan(int dx, int dy) |
1578 | { |
1579 | if (!m_map) |
1580 | return; |
1581 | if (dx == 0 && dy == 0) |
1582 | return; |
1583 | |
1584 | QGeoCoordinate coord = m_map->geoProjection().itemPositionToCoordinate( |
1585 | pos: QDoubleVector2D(m_map->viewportWidth() / 2 + dx, |
1586 | m_map->viewportHeight() / 2 + dy)); |
1587 | setCenter(coord); |
1588 | } |
1589 | |
1590 | |
1591 | /*! |
1592 | \qmlmethod void QtLocation::Map::prefetchData() |
1593 | |
1594 | Optional hint that allows the map to prefetch during this idle period |
1595 | */ |
1596 | void QDeclarativeGeoMap::prefetchData() |
1597 | { |
1598 | if (!m_map) |
1599 | return; |
1600 | m_map->prefetchData(); |
1601 | } |
1602 | |
1603 | /*! |
1604 | \qmlmethod void QtLocation::Map::clearData() |
1605 | |
1606 | Clears map data collected by the currently selected plugin. |
1607 | \note This method will delete cached files. |
1608 | \sa plugin |
1609 | */ |
1610 | void QDeclarativeGeoMap::clearData() |
1611 | { |
1612 | if (m_map) |
1613 | m_map->clearData(); |
1614 | } |
1615 | |
1616 | /*! |
1617 | \qmlmethod void QtLocation::Map::fitViewportToGeoShape(geoShape, margins) |
1618 | |
1619 | Fits the viewport to a specific geo shape \a geoShape. |
1620 | The \a margins are in screen pixels. |
1621 | |
1622 | \note If the projection used by the plugin is not WebMercator, and the plugin does not have fitting to |
1623 | shape capability, this method will do nothing. |
1624 | |
1625 | \sa visibleRegion |
1626 | \since 5.13 |
1627 | */ |
1628 | void QDeclarativeGeoMap::fitViewportToGeoShape(const QGeoShape &shape, QVariant margins) |
1629 | { |
1630 | QMargins m(10, 10, 10, 10); // lets defaults to 10 if margins is invalid |
1631 | switch (static_cast<QMetaType::Type>(margins.type())) { |
1632 | case QMetaType::Int: |
1633 | case QMetaType::Double: { |
1634 | const int value = int(margins.toDouble()); |
1635 | m = QMargins(value, value, value, value); |
1636 | } |
1637 | break; |
1638 | // ToDo: Support distinct margins in some QML form. Perhaps QRect? |
1639 | default: |
1640 | break; |
1641 | } |
1642 | fitViewportToGeoShape(shape, borders: m); |
1643 | } |
1644 | |
1645 | void QDeclarativeGeoMap::fitViewportToGeoShape(const QGeoShape &shape, const QMargins &borders) |
1646 | { |
1647 | if (!m_map || !shape.isValid()) |
1648 | return; |
1649 | |
1650 | if (m_map->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { |
1651 | // This case remains handled here, and not inside QGeoMap*::fitViewportToGeoRectangle, |
1652 | // in order to honor animations on center and zoomLevel |
1653 | const QMargins margins = borders + mapMargins(); |
1654 | const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); |
1655 | const QPair<QGeoCoordinate, qreal> fitData = p.fitViewportToGeoRectangle(rectangle: shape.boundingGeoRectangle(), |
1656 | margins); |
1657 | if (!fitData.first.isValid()) |
1658 | return; |
1659 | |
1660 | // position camera to the center of bounding box |
1661 | setProperty(name: "center" , value: QVariant::fromValue(value: fitData.first)); // not using setCenter(centerCoordinate) to honor a possible animation set on the center property |
1662 | |
1663 | if (!qIsFinite(d: fitData.second)) |
1664 | return; |
1665 | double newZoom = qMax<double>(a: minimumZoomLevel(), b: fitData.second); |
1666 | setProperty(name: "zoomLevel" , value: QVariant::fromValue(value: newZoom)); // not using setZoomLevel(newZoom) to honor a possible animation set on the zoomLevel property |
1667 | } else if (m_map->capabilities() & QGeoMap::SupportsFittingViewportToGeoRectangle) { |
1668 | // Animations cannot be honored in this case, as m_map acts as a black box |
1669 | m_map->fitViewportToGeoRectangle(rectangle: m_visibleRegion, borders); |
1670 | } |
1671 | // else out of luck |
1672 | } |
1673 | |
1674 | /*! |
1675 | \qmlproperty string QtLocation::Map::errorString |
1676 | |
1677 | This read-only property holds the textual presentation of the latest mapping provider error. |
1678 | If no error has occurred, an empty string is returned. |
1679 | |
1680 | An empty string may also be returned if an error occurred which has no associated |
1681 | textual representation. |
1682 | |
1683 | \sa QGeoServiceProvider::errorString() |
1684 | */ |
1685 | |
1686 | QString QDeclarativeGeoMap::errorString() const |
1687 | { |
1688 | return m_errorString; |
1689 | } |
1690 | |
1691 | /*! |
1692 | \qmlproperty enumeration QtLocation::Map::error |
1693 | |
1694 | This read-only property holds the last occurred mapping service provider error. |
1695 | |
1696 | \list |
1697 | \li Map.NoError - No error has occurred. |
1698 | \li Map.NotSupportedError -The maps plugin property was not set or there is no mapping manager associated with the plugin. |
1699 | \li Map.UnknownParameterError -The plugin did not recognize one of the parameters it was given. |
1700 | \li Map.MissingRequiredParameterError - The plugin did not find one of the parameters it was expecting. |
1701 | \li Map.ConnectionError - The plugin could not connect to its backend service or database. |
1702 | \endlist |
1703 | |
1704 | \sa QGeoServiceProvider::Error |
1705 | */ |
1706 | |
1707 | QGeoServiceProvider::Error QDeclarativeGeoMap::error() const |
1708 | { |
1709 | return m_error; |
1710 | } |
1711 | |
1712 | QGeoMap *QDeclarativeGeoMap::map() const |
1713 | { |
1714 | return m_map; |
1715 | } |
1716 | |
1717 | void QDeclarativeGeoMap::itemChange(ItemChange change, const ItemChangeData &value) |
1718 | { |
1719 | if (change == ItemChildAddedChange) { |
1720 | QQuickItem *child = value.item; |
1721 | QQuickItem *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(object: child); |
1722 | if (!mapItem) |
1723 | mapItem = qobject_cast<QDeclarativeGeoMapItemGroup *>(object: child); |
1724 | |
1725 | if (mapItem) { |
1726 | qreal z = mapItem->z(); |
1727 | if (z > m_maxChildZ) { // Ignore children removal |
1728 | m_maxChildZ = z; |
1729 | // put the copyrights notice object at the highest z order |
1730 | if (m_copyrights) |
1731 | m_copyrights->setCopyrightsZ(m_maxChildZ + 1); |
1732 | } |
1733 | } |
1734 | } |
1735 | QQuickItem::itemChange(change, value); |
1736 | } |
1737 | |
1738 | bool QDeclarativeGeoMap::isInteractive() |
1739 | { |
1740 | return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive(); |
1741 | } |
1742 | |
1743 | void QDeclarativeGeoMap::attachCopyrightNotice(bool initialVisibility) |
1744 | { |
1745 | if (initialVisibility) { |
1746 | ++m_copyNoticesVisible; |
1747 | if (m_map) |
1748 | m_map->setCopyrightVisible(m_copyNoticesVisible > 0); |
1749 | } |
1750 | } |
1751 | |
1752 | void QDeclarativeGeoMap::detachCopyrightNotice(bool currentVisibility) |
1753 | { |
1754 | if (currentVisibility) { |
1755 | --m_copyNoticesVisible; |
1756 | if (m_map) |
1757 | m_map->setCopyrightVisible(m_copyNoticesVisible > 0); |
1758 | } |
1759 | } |
1760 | |
1761 | void QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged() |
1762 | { |
1763 | QDeclarativeGeoMapCopyrightNotice *copy = static_cast<QDeclarativeGeoMapCopyrightNotice *>(sender()); |
1764 | m_copyNoticesVisible += ( int(copy->copyrightsVisible()) * 2 - 1); |
1765 | if (m_map) |
1766 | m_map->setCopyrightVisible(m_copyNoticesVisible > 0); |
1767 | } |
1768 | |
1769 | void QDeclarativeGeoMap::onCameraDataChanged(const QGeoCameraData &cameraData) |
1770 | { |
1771 | bool centerHasChanged = cameraData.center() != m_cameraData.center(); |
1772 | bool bearingHasChanged = cameraData.bearing() != m_cameraData.bearing(); |
1773 | bool tiltHasChanged = cameraData.tilt() != m_cameraData.tilt(); |
1774 | bool fovHasChanged = cameraData.fieldOfView() != m_cameraData.fieldOfView(); |
1775 | bool zoomHasChanged = cameraData.zoomLevel() != m_cameraData.zoomLevel(); |
1776 | |
1777 | m_cameraData = cameraData; |
1778 | // polish map items |
1779 | for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(t&: m_mapItems)) { |
1780 | if (i) |
1781 | i->baseCameraDataChanged(camera: m_cameraData); // Consider optimizing this further, removing the contained duplicate if conditions. |
1782 | } |
1783 | |
1784 | if (centerHasChanged) |
1785 | emit centerChanged(coordinate: m_cameraData.center()); |
1786 | if (zoomHasChanged) |
1787 | emit zoomLevelChanged(zoomLevel: m_cameraData.zoomLevel()); |
1788 | if (bearingHasChanged) |
1789 | emit bearingChanged(bearing: m_cameraData.bearing()); |
1790 | if (tiltHasChanged) |
1791 | emit tiltChanged(tilt: m_cameraData.tilt()); |
1792 | if (fovHasChanged) |
1793 | emit fieldOfViewChanged(fieldOfView: m_cameraData.fieldOfView()); |
1794 | if (centerHasChanged || zoomHasChanged || bearingHasChanged |
1795 | || tiltHasChanged || fovHasChanged) |
1796 | emit visibleRegionChanged(); |
1797 | } |
1798 | |
1799 | /*! |
1800 | \qmlmethod void QtLocation::Map::addMapParameter(MapParameter parameter) |
1801 | |
1802 | Adds the \a parameter object to the map. The effect of this call is dependent |
1803 | on the combination of the content of the MapParameter and the type of |
1804 | underlying QGeoMap. If a MapParameter that is not supported by the underlying |
1805 | QGeoMap gets added, the call has no effect. |
1806 | |
1807 | The release of this API with Qt 5.9 is a Technology Preview. |
1808 | |
1809 | \sa MapParameter, removeMapParameter, mapParameters, clearMapParameters |
1810 | |
1811 | \since 5.9 |
1812 | */ |
1813 | void QDeclarativeGeoMap::addMapParameter(QDeclarativeGeoMapParameter *parameter) |
1814 | { |
1815 | if (!parameter->isComponentComplete()) { |
1816 | connect(sender: parameter, signal: &QDeclarativeGeoMapParameter::completed, receiver: this, slot: &QDeclarativeGeoMap::addMapParameter); |
1817 | return; |
1818 | } |
1819 | |
1820 | disconnect(receiver: parameter); |
1821 | if (m_mapParameters.contains(t: parameter)) |
1822 | return; |
1823 | parameter->setParent(this); |
1824 | m_mapParameters.append(t: parameter); // parameter now owned by QDeclarativeGeoMap |
1825 | if (m_map) |
1826 | m_map->addParameter(param: parameter); |
1827 | } |
1828 | |
1829 | /*! |
1830 | \qmlmethod void QtLocation::Map::removeMapParameter(MapParameter parameter) |
1831 | |
1832 | Removes the given \a parameter object from the map. |
1833 | |
1834 | The release of this API with Qt 5.9 is a Technology Preview. |
1835 | |
1836 | \sa MapParameter, addMapParameter, mapParameters, clearMapParameters |
1837 | |
1838 | \since 5.9 |
1839 | */ |
1840 | void QDeclarativeGeoMap::removeMapParameter(QDeclarativeGeoMapParameter *parameter) |
1841 | { |
1842 | if (!m_mapParameters.contains(t: parameter)) |
1843 | return; |
1844 | if (m_map) |
1845 | m_map->removeParameter(param: parameter); |
1846 | m_mapParameters.removeOne(t: parameter); |
1847 | } |
1848 | |
1849 | /*! |
1850 | \qmlmethod void QtLocation::Map::clearMapParameters() |
1851 | |
1852 | Removes all map parameters from the map. |
1853 | |
1854 | The release of this API with Qt 5.9 is a Technology Preview. |
1855 | |
1856 | \sa MapParameter, mapParameters, addMapParameter, removeMapParameter, clearMapParameters |
1857 | |
1858 | \since 5.9 |
1859 | */ |
1860 | void QDeclarativeGeoMap::clearMapParameters() |
1861 | { |
1862 | if (m_map) |
1863 | m_map->clearParameters(); |
1864 | m_mapParameters.clear(); |
1865 | } |
1866 | |
1867 | /*! |
1868 | \qmlproperty list<MapParameters> QtLocation::Map::mapParameters |
1869 | |
1870 | Returns the list of all map parameters in no particular order. |
1871 | These items include map parameters that were declared statically as part of |
1872 | the type declaration, as well as dynamical map parameters (\l addMapParameter). |
1873 | |
1874 | The release of this API with Qt 5.9 is a Technology Preview. |
1875 | |
1876 | \sa MapParameter, addMapParameter, removeMapParameter, clearMapParameters |
1877 | |
1878 | \since 5.9 |
1879 | */ |
1880 | QList<QObject *> QDeclarativeGeoMap::mapParameters() |
1881 | { |
1882 | QList<QObject *> ret; |
1883 | for (QDeclarativeGeoMapParameter *p : qAsConst(t&: m_mapParameters)) |
1884 | ret << p; |
1885 | return ret; |
1886 | } |
1887 | |
1888 | /* |
1889 | \internal |
1890 | */ |
1891 | void QDeclarativeGeoMap::addMapObject(QGeoMapObject *object) |
1892 | { |
1893 | if (!object || object->map()) |
1894 | return; |
1895 | |
1896 | if (!m_initialized) { |
1897 | m_pendingMapObjects.append(t: object); |
1898 | return; |
1899 | } |
1900 | |
1901 | int curObjects = m_map->mapObjects().size(); |
1902 | // object adds itself to the map |
1903 | object->setMap(m_map); |
1904 | |
1905 | if (curObjects != m_map->mapObjects().size()) |
1906 | emit mapObjectsChanged(); |
1907 | } |
1908 | |
1909 | /* |
1910 | \internal |
1911 | */ |
1912 | void QDeclarativeGeoMap::removeMapObject(QGeoMapObject *object) |
1913 | { |
1914 | if (!object || object->map() != m_map) // if !initialized this is fine, since both object and m_map are supposed to be NULL |
1915 | return; |
1916 | |
1917 | if (!m_initialized) { |
1918 | m_pendingMapObjects.removeOne(t: object); |
1919 | return; |
1920 | } |
1921 | |
1922 | int curObjects = m_map->mapObjects().size(); |
1923 | // object adds itself to the map |
1924 | object->setMap(nullptr); |
1925 | |
1926 | if (curObjects != m_map->mapObjects().size()) |
1927 | emit mapObjectsChanged(); |
1928 | } |
1929 | |
1930 | /* |
1931 | \internal |
1932 | */ |
1933 | void QDeclarativeGeoMap::clearMapObjects() |
1934 | { |
1935 | if (!m_initialized) { |
1936 | m_pendingMapObjects.clear(); |
1937 | } else { |
1938 | const QList<QGeoMapObject *> objs = m_map->mapObjects(); |
1939 | for (QGeoMapObject *o: objs) |
1940 | o->setMap(nullptr); |
1941 | if (objs.size()) |
1942 | emit mapObjectsChanged(); |
1943 | } |
1944 | } |
1945 | |
1946 | /* |
1947 | \internal |
1948 | */ |
1949 | QList<QGeoMapObject *> QDeclarativeGeoMap::mapObjects() |
1950 | { |
1951 | if (!m_initialized) |
1952 | return m_pendingMapObjects; |
1953 | else |
1954 | return m_map->mapObjects(); |
1955 | } |
1956 | |
1957 | /*! |
1958 | \qmlproperty list<MapItem> QtLocation::Map::mapItems |
1959 | |
1960 | Returns the list of all map items in no particular order. |
1961 | These items include items that were declared statically as part of |
1962 | the type declaration, as well as dynamical items (\l addMapItem, |
1963 | \l MapItemView). |
1964 | |
1965 | \sa addMapItem, removeMapItem, clearMapItems |
1966 | */ |
1967 | |
1968 | QList<QObject *> QDeclarativeGeoMap::mapItems() |
1969 | { |
1970 | QList<QObject *> ret; |
1971 | foreach (const QPointer<QDeclarativeGeoMapItemBase> &ptr, m_mapItems) { |
1972 | if (ptr) |
1973 | ret << ptr.data(); |
1974 | } |
1975 | return ret; |
1976 | } |
1977 | |
1978 | /*! |
1979 | \qmlmethod void QtLocation::Map::addMapItem(MapItem item) |
1980 | |
1981 | Adds the given \a item to the Map (for example MapQuickItem, MapCircle). If the object |
1982 | already is on the Map, it will not be added again. |
1983 | |
1984 | As an example, consider the case where you have a MapCircle representing your current position: |
1985 | |
1986 | \snippet declarative/maps.qml QtQuick import |
1987 | \snippet declarative/maps.qml QtLocation import |
1988 | \codeline |
1989 | \snippet declarative/maps.qml Map addMapItem MapCircle at current position |
1990 | |
1991 | \note MapItemViews cannot be added with this method. |
1992 | |
1993 | \sa mapItems, removeMapItem, clearMapItems |
1994 | */ |
1995 | |
1996 | void QDeclarativeGeoMap::addMapItem(QDeclarativeGeoMapItemBase *item) |
1997 | { |
1998 | if (addMapItem_real(item)) |
1999 | emit mapItemsChanged(); |
2000 | } |
2001 | |
2002 | bool QDeclarativeGeoMap::addMapItem_real(QDeclarativeGeoMapItemBase *item) |
2003 | { |
2004 | if (!item || item->quickMap()) |
2005 | return false; |
2006 | // If the item comes from a MapItemGroup, do not reparent it. |
2007 | if (!qobject_cast<QDeclarativeGeoMapItemGroup *>(object: item->parentItem())) |
2008 | item->setParentItem(this); |
2009 | m_mapItems.append(t: item); |
2010 | if (m_map) { |
2011 | item->setMap(quickMap: this, map: m_map); |
2012 | m_map->addMapItem(item); |
2013 | } |
2014 | return true; |
2015 | } |
2016 | |
2017 | /*! |
2018 | \qmlmethod void QtLocation::Map::removeMapItem(MapItem item) |
2019 | |
2020 | Removes the given \a item from the Map (for example MapQuickItem, MapCircle). If |
2021 | the MapItem does not exist or was not previously added to the map, the |
2022 | method does nothing. |
2023 | |
2024 | \sa mapItems, addMapItem, clearMapItems |
2025 | */ |
2026 | void QDeclarativeGeoMap::removeMapItem(QDeclarativeGeoMapItemBase *ptr) |
2027 | { |
2028 | if (removeMapItem_real(item: ptr)) |
2029 | emit mapItemsChanged(); |
2030 | } |
2031 | |
2032 | bool QDeclarativeGeoMap::removeMapItem_real(QDeclarativeGeoMapItemBase *ptr) |
2033 | { |
2034 | if (!ptr) |
2035 | return false; |
2036 | QPointer<QDeclarativeGeoMapItemBase> item(ptr); |
2037 | if (!m_mapItems.contains(t: item)) |
2038 | return false; |
2039 | if (m_map) |
2040 | m_map->removeMapItem(item: ptr); |
2041 | if (item->parentItem() == this) |
2042 | item->setParentItem(0); |
2043 | item->setMap(quickMap: 0, map: 0); |
2044 | // these can be optimized for perf, as we already check the 'contains' above |
2045 | m_mapItems.removeOne(t: item); |
2046 | return true; |
2047 | } |
2048 | |
2049 | /*! |
2050 | \qmlmethod void QtLocation::Map::clearMapItems() |
2051 | |
2052 | Removes all items and item groups from the map. |
2053 | |
2054 | \sa mapItems, addMapItem, removeMapItem, addMapItemGroup, removeMapItemGroup |
2055 | */ |
2056 | void QDeclarativeGeoMap::clearMapItems() |
2057 | { |
2058 | if (m_mapItems.isEmpty()) |
2059 | return; |
2060 | |
2061 | int removed = 0; |
2062 | for (auto i : qAsConst(t&: m_mapItemGroups)) { |
2063 | // Processing only top-level groups (!views) |
2064 | QDeclarativeGeoMapItemView *view = qobject_cast<QDeclarativeGeoMapItemView *>(object: i); |
2065 | if (view) |
2066 | continue; |
2067 | |
2068 | if (i->parentItem() != this) |
2069 | continue; |
2070 | |
2071 | removed += removeMapItemGroup_real(itemGroup: i); |
2072 | } |
2073 | |
2074 | for (auto i : qAsConst(t&: m_mapItems)) |
2075 | removed += removeMapItem_real(ptr: i); |
2076 | |
2077 | if (removed) |
2078 | emit mapItemsChanged(); |
2079 | } |
2080 | |
2081 | /*! |
2082 | \qmlmethod void QtLocation::Map::addMapItemGroup(MapItemGroup itemGroup) |
2083 | |
2084 | Adds the map items contained in the given \a itemGroup to the Map |
2085 | (for example MapQuickItem, MapCircle). |
2086 | |
2087 | \sa MapItemGroup, removeMapItemGroup |
2088 | |
2089 | \since 5.9 |
2090 | */ |
2091 | void QDeclarativeGeoMap::addMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup) |
2092 | { |
2093 | if (addMapItemGroup_real(itemGroup)) |
2094 | emit mapItemsChanged(); |
2095 | } |
2096 | |
2097 | bool QDeclarativeGeoMap::addMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup) |
2098 | { |
2099 | if (!itemGroup || itemGroup->quickMap()) // Already added to some map |
2100 | return false; |
2101 | |
2102 | itemGroup->setQuickMap(this); |
2103 | |
2104 | if (!isGroupNested(group: itemGroup)) |
2105 | itemGroup->setParentItem(this); |
2106 | |
2107 | QPointer<QDeclarativeGeoMapItemGroup> g(itemGroup); |
2108 | m_mapItemGroups.append(t: g); |
2109 | |
2110 | const QList<QQuickItem *> quickKids = itemGroup->childItems(); |
2111 | int count = 0; |
2112 | for (auto c: quickKids) { |
2113 | count += addMapChild(child: c); // this calls addMapItemGroup recursively, if needed |
2114 | } |
2115 | return count; |
2116 | } |
2117 | |
2118 | /*! |
2119 | \qmlmethod void QtLocation::Map::removeMapItemGroup(MapItemGroup itemGroup) |
2120 | |
2121 | Removes \a itemGroup and the items contained therein from the Map. |
2122 | |
2123 | \sa MapItemGroup, addMapItemGroup |
2124 | |
2125 | \since 5.9 |
2126 | */ |
2127 | void QDeclarativeGeoMap::removeMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup) |
2128 | { |
2129 | if (removeMapItemGroup_real(itemGroup)) |
2130 | emit mapItemsChanged(); |
2131 | } |
2132 | |
2133 | bool QDeclarativeGeoMap::removeMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup) |
2134 | { |
2135 | if (!itemGroup || itemGroup->quickMap() != this) // cant remove an itemGroup added to another map |
2136 | return false; |
2137 | |
2138 | QPointer<QDeclarativeGeoMapItemGroup> g(itemGroup); |
2139 | if (!m_mapItemGroups.removeOne(t: g)) |
2140 | return false; |
2141 | |
2142 | const QList<QQuickItem *> quickKids = itemGroup->childItems(); |
2143 | int count = 0; |
2144 | for (auto c: quickKids) { |
2145 | count += removeMapChild(child: c); |
2146 | } |
2147 | itemGroup->setQuickMap(nullptr); |
2148 | if (itemGroup->parentItem() == this) |
2149 | itemGroup->setParentItem(0); |
2150 | return count; |
2151 | } |
2152 | |
2153 | /*! |
2154 | \qmlmethod void QtLocation::Map::removeMapItemView(MapItemView itemView) |
2155 | |
2156 | Removes \a itemView and the items instantiated by it from the Map. |
2157 | |
2158 | \sa MapItemView, addMapItemView |
2159 | |
2160 | \since 5.10 |
2161 | */ |
2162 | void QDeclarativeGeoMap::removeMapItemView(QDeclarativeGeoMapItemView *itemView) |
2163 | { |
2164 | if (removeMapItemView_real(itemView)) |
2165 | emit mapItemsChanged(); |
2166 | } |
2167 | |
2168 | bool QDeclarativeGeoMap::removeMapItemView_real(QDeclarativeGeoMapItemView *itemView) |
2169 | { |
2170 | if (!itemView || itemView->m_map != this) // can't remove a view that is already added to another map |
2171 | return false; |
2172 | |
2173 | itemView->removeInstantiatedItems(transition: false); // remove the items without using transitions AND abort ongoing ones |
2174 | itemView->m_map = 0; |
2175 | m_mapViews.removeOne(t: itemView); |
2176 | return removeMapItemGroup_real(itemGroup: itemView); // at this point, all delegate instances have been removed. |
2177 | } |
2178 | |
2179 | void QDeclarativeGeoMap::updateItemToWindowTransform() |
2180 | { |
2181 | if (!m_initialized) |
2182 | return; |
2183 | |
2184 | // Update itemToWindowTransform into QGeoProjection |
2185 | const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform(); |
2186 | QTransform item2Window = QQuickItemPrivate::get(item: this)->itemToWindowTransform(); |
2187 | if (!property(name: "layer" ).isNull() && property(name: "layer" ).value<QObject *>()->property(name: "enabled" ).toBool()) |
2188 | item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied |
2189 | |
2190 | m_map->setItemToWindowTransform(item2Window); |
2191 | |
2192 | // This method is called at every redraw, including those redraws not generated by |
2193 | // sgNodeChanged. |
2194 | // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of |
2195 | // the item or one of its ancestors changed), a forced update of the map items using accelerated |
2196 | // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform. |
2197 | if (!m_sgNodeHasChanged && item2WindowOld != item2Window) { |
2198 | for (auto i: qAsConst(t&: m_mapItems)) |
2199 | i->setMaterialDirty(); |
2200 | } |
2201 | |
2202 | m_sgNodeHasChanged = false; |
2203 | } |
2204 | |
2205 | void QDeclarativeGeoMap::onSGNodeChanged() |
2206 | { |
2207 | m_sgNodeHasChanged = true; |
2208 | update(); |
2209 | } |
2210 | |
2211 | /*! |
2212 | \qmlmethod void QtLocation::Map::addMapItemView(MapItemView itemView) |
2213 | |
2214 | Adds \a itemView to the Map. |
2215 | |
2216 | \sa MapItemView, removeMapItemView |
2217 | |
2218 | \since 5.10 |
2219 | */ |
2220 | void QDeclarativeGeoMap::addMapItemView(QDeclarativeGeoMapItemView *itemView) |
2221 | { |
2222 | if (addMapItemView_real(itemView)) |
2223 | emit mapItemsChanged(); |
2224 | } |
2225 | |
2226 | bool QDeclarativeGeoMap::addMapItemView_real(QDeclarativeGeoMapItemView *itemView) |
2227 | { |
2228 | if (!itemView || itemView->m_map) // can't add a view twice |
2229 | return false; |
2230 | |
2231 | int count = addMapItemGroup_real(itemGroup: itemView); // at this point, delegates aren't yet incubated. |
2232 | // Not appending it to m_mapViews because it seems unnecessary even if the |
2233 | // itemView is a child of this (in which case it would be destroyed |
2234 | m_mapViews.append(t: itemView); |
2235 | setupMapView(itemView); |
2236 | return count; |
2237 | } |
2238 | |
2239 | /*! |
2240 | \qmlproperty MapType QtLocation::Map::activeMapType |
2241 | |
2242 | \brief Access to the currently active \l{MapType}{map type}. |
2243 | |
2244 | This property can be set to change the active \l{MapType}{map type}. |
2245 | See the \l{Map::supportedMapTypes}{supportedMapTypes} property for possible values. |
2246 | |
2247 | \sa MapType |
2248 | */ |
2249 | void QDeclarativeGeoMap::setActiveMapType(QDeclarativeGeoMapType *mapType) |
2250 | { |
2251 | if (m_activeMapType->mapType() != mapType->mapType()) { |
2252 | if (m_map) { |
2253 | if (mapType->mapType().pluginName() == m_plugin->name().toLatin1()) { |
2254 | m_map->setActiveMapType(mapType->mapType()); |
2255 | m_activeMapType = mapType; |
2256 | emit activeMapTypeChanged(); |
2257 | } |
2258 | } else { |
2259 | m_activeMapType = mapType; |
2260 | emit activeMapTypeChanged(); |
2261 | } |
2262 | } |
2263 | } |
2264 | |
2265 | QDeclarativeGeoMapType * QDeclarativeGeoMap::activeMapType() const |
2266 | { |
2267 | return m_activeMapType; |
2268 | } |
2269 | |
2270 | /*! |
2271 | \internal |
2272 | */ |
2273 | void QDeclarativeGeoMap::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
2274 | { |
2275 | m_gestureArea->setSize(newGeometry.size()); |
2276 | QQuickItem::geometryChanged(newGeometry, oldGeometry); |
2277 | |
2278 | if (!m_map || newGeometry.size().isEmpty()) |
2279 | return; |
2280 | |
2281 | m_map->setViewportSize(newGeometry.size().toSize()); |
2282 | |
2283 | if (!m_initialized) { |
2284 | initialize(); |
2285 | } else { |
2286 | setMinimumZoomLevel(minimumZoomLevel: m_map->minimumZoom(), userSet: false); |
2287 | |
2288 | // Update the center latitudinal threshold |
2289 | QGeoCameraData cameraData = m_map->cameraData(); |
2290 | const double maximumCenterLatitudeAtZoom = m_map->maximumCenterLatitudeAtZoom(cameraData); |
2291 | const double minimumCenterLatitudeAtZoom = m_map->minimumCenterLatitudeAtZoom(cameraData); |
2292 | if (maximumCenterLatitudeAtZoom != m_maximumViewportLatitude |
2293 | || minimumCenterLatitudeAtZoom != m_minimumViewportLatitude) { |
2294 | m_maximumViewportLatitude = maximumCenterLatitudeAtZoom; |
2295 | m_minimumViewportLatitude = minimumCenterLatitudeAtZoom; |
2296 | QGeoCoordinate coord = cameraData.center(); |
2297 | coord.setLatitude(qBound(min: m_minimumViewportLatitude, val: coord.latitude(), max: m_maximumViewportLatitude)); |
2298 | cameraData.setCenter(coord); |
2299 | m_map->setCameraData(cameraData); // this polishes map items |
2300 | } else if (oldGeometry.size() != newGeometry.size()) { |
2301 | // polish map items |
2302 | for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(t&: m_mapItems)) { |
2303 | if (i) |
2304 | i->polishAndUpdate(); |
2305 | } |
2306 | } |
2307 | } |
2308 | |
2309 | /* |
2310 | The fitViewportTo*() functions depend on a valid map geometry. |
2311 | If they were called prior to the first resize they cause |
2312 | the zoomlevel to jump to 0 (showing the world). Therefore the |
2313 | calls were queued up until now. |
2314 | |
2315 | Multiple fitViewportTo*() calls replace each other. |
2316 | */ |
2317 | if (m_pendingFitViewport && width() && height()) { |
2318 | fitViewportToGeoShape(shape: m_visibleRegion); |
2319 | m_pendingFitViewport = false; |
2320 | } |
2321 | |
2322 | } |
2323 | |
2324 | /*! |
2325 | \qmlmethod void QtLocation::Map::fitViewportToMapItems(list<MapItems> items = {}) |
2326 | |
2327 | If no argument is provided, fits the current viewport to the boundary of all map items. |
2328 | The camera is positioned in the center of the map items, and at the largest integral zoom level |
2329 | possible which allows all map items to be visible on screen. |
2330 | If \a items is provided, fits the current viewport to the boundary of the specified map items only. |
2331 | |
2332 | \note This method gained the optional \a items argument since Qt 5.15. |
2333 | In previous releases, this method fitted the map to all map items. |
2334 | |
2335 | \sa fitViewportToVisibleMapItems |
2336 | */ |
2337 | void QDeclarativeGeoMap::fitViewportToMapItems(const QVariantList &items) |
2338 | { |
2339 | if (items.size()) { |
2340 | QList<QPointer<QDeclarativeGeoMapItemBase> > itms; |
2341 | for (const QVariant &i: items) { |
2342 | QDeclarativeGeoMapItemBase *itm = qobject_cast<QDeclarativeGeoMapItemBase *>(object: i.value<QObject *>()); |
2343 | if (itm) |
2344 | itms.append(t: itm); |
2345 | } |
2346 | fitViewportToMapItemsRefine(mapItems: itms, refine: true, onlyVisible: false); |
2347 | } else { |
2348 | fitViewportToMapItemsRefine(mapItems: m_mapItems, refine: true, onlyVisible: false); |
2349 | } |
2350 | } |
2351 | |
2352 | /*! |
2353 | \qmlmethod void QtLocation::Map::fitViewportToVisibleMapItems() |
2354 | |
2355 | Fits the current viewport to the boundary of all \b visible map items. |
2356 | The camera is positioned in the center of the map items, and at the largest integral |
2357 | zoom level possible which allows all map items to be visible on screen. |
2358 | |
2359 | \sa fitViewportToMapItems |
2360 | */ |
2361 | void QDeclarativeGeoMap::fitViewportToVisibleMapItems() |
2362 | { |
2363 | fitViewportToMapItemsRefine(mapItems: m_mapItems, refine: true, onlyVisible: true); |
2364 | } |
2365 | |
2366 | /*! |
2367 | \internal |
2368 | */ |
2369 | void QDeclarativeGeoMap::fitViewportToMapItemsRefine(const QList<QPointer<QDeclarativeGeoMapItemBase> > &mapItems, |
2370 | bool refine, |
2371 | bool onlyVisible) |
2372 | { |
2373 | if (!m_map) |
2374 | return; |
2375 | |
2376 | if (mapItems.size() == 0) |
2377 | return; |
2378 | |
2379 | double minX = qInf(); |
2380 | double maxX = -qInf(); |
2381 | double minY = qInf(); |
2382 | double maxY = -qInf(); |
2383 | double topLeftX = 0; |
2384 | double topLeftY = 0; |
2385 | double bottomRightX = 0; |
2386 | double bottomRightY = 0; |
2387 | bool haveQuickItem = false; |
2388 | |
2389 | // find bounds of all map items |
2390 | int itemCount = 0; |
2391 | for (int i = 0; i < mapItems.count(); ++i) { |
2392 | if (!mapItems.at(i)) |
2393 | continue; |
2394 | QDeclarativeGeoMapItemBase *item = mapItems.at(i).data(); |
2395 | if (!item || (onlyVisible && (!item->isVisible() || item->mapItemOpacity() <= 0.0))) |
2396 | continue; |
2397 | |
2398 | // skip quick items in the first pass and refine the fit later |
2399 | QDeclarativeGeoMapQuickItem *quickItem = |
2400 | qobject_cast<QDeclarativeGeoMapQuickItem*>(object: item); |
2401 | if (refine && quickItem) { |
2402 | haveQuickItem = true; |
2403 | continue; |
2404 | } |
2405 | // Force map items to update immediately. Needed to ensure correct item size and positions |
2406 | // when recursively calling this function. |
2407 | // TODO: See if we really need updatePolish on delegated items, in particular |
2408 | // in relation to |
2409 | // a) fitViewportToMapItems |
2410 | // b) presence of MouseArea |
2411 | // |
2412 | // This is also legacy code. It must be updated to not operate on screen sizes. |
2413 | if (item->isPolishScheduled()) |
2414 | item->updatePolish(); |
2415 | |
2416 | if (quickItem && quickItem->matrix_ && !quickItem->matrix_->m_matrix.isIdentity()) { |
2417 | // TODO: recalculate the center/zoom level so that the item becomes projectable again |
2418 | if (quickItem->zoomLevel() == 0.0) // the item is unprojectable, should be skipped. |
2419 | continue; |
2420 | |
2421 | QRectF brect = item->boundingRect(); |
2422 | brect = quickItem->matrix_->m_matrix.mapRect(rect: brect); |
2423 | QPointF transformedPosition = quickItem->matrix_->m_matrix * item->position(); |
2424 | topLeftX = transformedPosition.x(); |
2425 | topLeftY = transformedPosition.y(); |
2426 | bottomRightX = topLeftX + brect.width(); |
2427 | bottomRightY = topLeftY + brect.height(); |
2428 | } else { |
2429 | topLeftX = item->position().x(); |
2430 | topLeftY = item->position().y(); |
2431 | bottomRightX = topLeftX + item->width(); |
2432 | bottomRightY = topLeftY + item->height(); |
2433 | } |
2434 | |
2435 | minX = qMin(a: minX, b: topLeftX); |
2436 | maxX = qMax(a: maxX, b: bottomRightX); |
2437 | minY = qMin(a: minY, b: topLeftY); |
2438 | maxY = qMax(a: maxY, b: bottomRightY); |
2439 | |
2440 | ++itemCount; |
2441 | } |
2442 | |
2443 | if (itemCount == 0) { |
2444 | if (haveQuickItem) |
2445 | fitViewportToMapItemsRefine(mapItems, refine: false, onlyVisible); |
2446 | return; |
2447 | } |
2448 | double bboxWidth = maxX - minX; |
2449 | double bboxHeight = maxY - minY; |
2450 | double bboxCenterX = minX + (bboxWidth / 2.0); |
2451 | double bboxCenterY = minY + (bboxHeight / 2.0); |
2452 | |
2453 | // position camera to the center of bounding box |
2454 | QGeoCoordinate coordinate; |
2455 | coordinate = m_map->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(bboxCenterX, bboxCenterY), clipToViewport: false); |
2456 | setProperty(name: "center" , value: QVariant::fromValue(value: coordinate)); |
2457 | |
2458 | // adjust zoom |
2459 | double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); |
2460 | double mapWidthRatio = width() / (width() + height()); |
2461 | double zoomRatio; |
2462 | |
2463 | if (bboxWidthRatio > mapWidthRatio) |
2464 | zoomRatio = bboxWidth / width(); |
2465 | else |
2466 | zoomRatio = bboxHeight / height(); |
2467 | |
2468 | qreal newZoom = std::log10(x: zoomRatio) / std::log10(x: 0.5); |
2469 | newZoom = std::floor(x: qMax(a: minimumZoomLevel(), b: (zoomLevel() + newZoom))); |
2470 | setProperty(name: "zoomLevel" , value: QVariant::fromValue(value: newZoom)); |
2471 | |
2472 | // as map quick items retain the same screen size after the camera zooms in/out |
2473 | // we refine the viewport again to achieve better results |
2474 | if (refine) |
2475 | fitViewportToMapItemsRefine(mapItems, refine: false, onlyVisible); |
2476 | } |
2477 | |
2478 | /*! |
2479 | \internal |
2480 | */ |
2481 | void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event) |
2482 | { |
2483 | if (isInteractive()) |
2484 | m_gestureArea->handleMousePressEvent(event); |
2485 | else |
2486 | QQuickItem::mousePressEvent(event); |
2487 | } |
2488 | |
2489 | /*! |
2490 | \internal |
2491 | */ |
2492 | void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) |
2493 | { |
2494 | if (isInteractive()) |
2495 | m_gestureArea->handleMouseMoveEvent(event); |
2496 | else |
2497 | QQuickItem::mouseMoveEvent(event); |
2498 | } |
2499 | |
2500 | /*! |
2501 | \internal |
2502 | */ |
2503 | void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event) |
2504 | { |
2505 | if (isInteractive()) |
2506 | m_gestureArea->handleMouseReleaseEvent(event); |
2507 | else |
2508 | QQuickItem::mouseReleaseEvent(event); |
2509 | } |
2510 | |
2511 | /*! |
2512 | \internal |
2513 | */ |
2514 | void QDeclarativeGeoMap::mouseUngrabEvent() |
2515 | { |
2516 | if (isInteractive()) |
2517 | m_gestureArea->handleMouseUngrabEvent(); |
2518 | else |
2519 | QQuickItem::mouseUngrabEvent(); |
2520 | } |
2521 | |
2522 | void QDeclarativeGeoMap::touchUngrabEvent() |
2523 | { |
2524 | if (isInteractive()) |
2525 | m_gestureArea->handleTouchUngrabEvent(); |
2526 | else |
2527 | QQuickItem::touchUngrabEvent(); |
2528 | } |
2529 | |
2530 | /*! |
2531 | \internal |
2532 | */ |
2533 | void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) |
2534 | { |
2535 | if (isInteractive()) { |
2536 | m_gestureArea->handleTouchEvent(event); |
2537 | } else { |
2538 | //ignore event so sythesized event is generated; |
2539 | QQuickItem::touchEvent(event); |
2540 | } |
2541 | } |
2542 | |
2543 | #if QT_CONFIG(wheelevent) |
2544 | /*! |
2545 | \internal |
2546 | */ |
2547 | void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event) |
2548 | { |
2549 | if (isInteractive()) |
2550 | m_gestureArea->handleWheelEvent(event); |
2551 | else |
2552 | QQuickItem::wheelEvent(event); |
2553 | |
2554 | } |
2555 | #endif |
2556 | |
2557 | /*! |
2558 | \internal |
2559 | */ |
2560 | bool QDeclarativeGeoMap::childMouseEventFilter(QQuickItem *item, QEvent *event) |
2561 | { |
2562 | Q_UNUSED(item); |
2563 | if (!isVisible() || !isEnabled() || !isInteractive()) |
2564 | return QQuickItem::childMouseEventFilter(item, event); |
2565 | |
2566 | switch (event->type()) { |
2567 | case QEvent::MouseButtonPress: |
2568 | case QEvent::MouseMove: |
2569 | case QEvent::MouseButtonRelease: |
2570 | return sendMouseEvent(event: static_cast<QMouseEvent *>(event)); |
2571 | case QEvent::UngrabMouse: { |
2572 | QQuickWindow *win = window(); |
2573 | if (!win) break; |
2574 | if (!win->mouseGrabberItem() || |
2575 | (win->mouseGrabberItem() && |
2576 | win->mouseGrabberItem() != this)) { |
2577 | // child lost grab, we could even lost |
2578 | // some events if grab already belongs for example |
2579 | // in item in diffrent window , clear up states |
2580 | mouseUngrabEvent(); |
2581 | } |
2582 | break; |
2583 | } |
2584 | case QEvent::TouchBegin: |
2585 | case QEvent::TouchUpdate: |
2586 | case QEvent::TouchEnd: |
2587 | case QEvent::TouchCancel: |
2588 | if (static_cast<QTouchEvent *>(event)->touchPoints().count() >= 2) { |
2589 | // 1 touch point = handle with MouseEvent (event is always synthesized) |
2590 | // let the synthesized mouse event grab the mouse, |
2591 | // note there is no mouse grabber at this point since |
2592 | // touch event comes first (see Qt::AA_SynthesizeMouseForUnhandledTouchEvents) |
2593 | return sendTouchEvent(event: static_cast<QTouchEvent *>(event)); |
2594 | } |
2595 | default: |
2596 | break; |
2597 | } |
2598 | return QQuickItem::childMouseEventFilter(item, event); |
2599 | } |
2600 | |
2601 | bool QDeclarativeGeoMap::sendMouseEvent(QMouseEvent *event) |
2602 | { |
2603 | QPointF localPos = mapFromScene(point: event->windowPos()); |
2604 | QQuickWindow *win = window(); |
2605 | QQuickItem *grabber = win ? win->mouseGrabberItem() : 0; |
2606 | bool stealEvent = m_gestureArea->isActive(); |
2607 | |
2608 | if ((stealEvent || contains(point: localPos)) && (!grabber || (!grabber->keepMouseGrab() && !grabber->keepTouchGrab()))) { |
2609 | QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, transformedLocalPos: &localPos)); |
2610 | mouseEvent->setAccepted(false); |
2611 | |
2612 | switch (mouseEvent->type()) { |
2613 | case QEvent::MouseMove: |
2614 | m_gestureArea->handleMouseMoveEvent(event: mouseEvent.data()); |
2615 | break; |
2616 | case QEvent::MouseButtonPress: |
2617 | m_gestureArea->handleMousePressEvent(event: mouseEvent.data()); |
2618 | break; |
2619 | case QEvent::MouseButtonRelease: |
2620 | m_gestureArea->handleMouseReleaseEvent(event: mouseEvent.data()); |
2621 | break; |
2622 | default: |
2623 | break; |
2624 | } |
2625 | |
2626 | stealEvent = m_gestureArea->isActive(); |
2627 | grabber = win ? win->mouseGrabberItem() : 0; |
2628 | |
2629 | if (grabber && stealEvent && !grabber->keepMouseGrab() && !grabber->keepTouchGrab() && grabber != this) |
2630 | grabMouse(); |
2631 | |
2632 | if (stealEvent) { |
2633 | //do not deliver |
2634 | event->setAccepted(true); |
2635 | return true; |
2636 | } else { |
2637 | return false; |
2638 | } |
2639 | } |
2640 | |
2641 | return false; |
2642 | } |
2643 | |
2644 | bool QDeclarativeGeoMap::sendTouchEvent(QTouchEvent *event) |
2645 | { |
2646 | QQuickPointerDevice *touchDevice = QQuickPointerDevice::touchDevice(d: event->device()); |
2647 | const QTouchEvent::TouchPoint &point = event->touchPoints().first(); |
2648 | QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(c: window()); |
2649 | |
2650 | auto touchPointGrabberItem = [touchDevice, windowPriv](const QTouchEvent::TouchPoint &point) -> QQuickItem* { |
2651 | if (QQuickEventPoint *eventPointer = windowPriv->pointerEventInstance(device: touchDevice)->pointById(pointId: point.id())) |
2652 | return eventPointer->grabberItem(); |
2653 | return nullptr; |
2654 | }; |
2655 | |
2656 | QQuickItem *grabber = touchPointGrabberItem(point); |
2657 | |
2658 | bool stealEvent = m_gestureArea->isActive(); |
2659 | bool containsPoint = contains(point: mapFromScene(point: point.scenePos())); |
2660 | |
2661 | if ((stealEvent || containsPoint) && (!grabber || !grabber->keepTouchGrab())) { |
2662 | QScopedPointer<QTouchEvent> touchEvent(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); |
2663 | touchEvent->setTimestamp(event->timestamp()); |
2664 | touchEvent->setAccepted(false); |
2665 | |
2666 | m_gestureArea->handleTouchEvent(event: touchEvent.data()); |
2667 | stealEvent = m_gestureArea->isActive(); |
2668 | grabber = touchPointGrabberItem(point); |
2669 | |
2670 | if (grabber && stealEvent && !grabber->keepTouchGrab() && grabber != this) { |
2671 | QVector<int> ids; |
2672 | foreach (const QTouchEvent::TouchPoint &tp, event->touchPoints()) { |
2673 | if (!(tp.state() & Qt::TouchPointReleased)) { |
2674 | ids.append(t: tp.id()); |
2675 | } |
2676 | } |
2677 | grabTouchPoints(ids); |
2678 | } |
2679 | |
2680 | if (stealEvent) { |
2681 | //do not deliver |
2682 | event->setAccepted(true); |
2683 | return true; |
2684 | } else { |
2685 | return false; |
2686 | } |
2687 | } |
2688 | |
2689 | return false; |
2690 | } |
2691 | |
2692 | QT_END_NAMESPACE |
2693 | |