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
63QT_BEGIN_NAMESPACE
64
65static 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
177QDeclarativeGeoMap::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
229QDeclarativeGeoMap::~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
286static 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
294void 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
324void 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*/
337void 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*/
401void 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*/
429void 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
445QQuickGeoMapGestureArea *QDeclarativeGeoMap::gesture()
446{
447 return m_gestureArea;
448}
449
450/*!
451 \internal
452
453 This may happen before mappingManagerInitialized()
454*/
455void 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
467void 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*/
483void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view)
484{
485 view->setMap(this);
486}
487
488/*!
489 * \internal
490 */
491QSGNode *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
522void 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*/
542void 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*/
623void 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: [&copyrightString](const QString &copy){ copyrightString = copy; });
688 QMetaObject::Connection copyrightImageCatcherConnection =
689 connect(sender: m_map.data(),
690 signal: QOverload<const QImage &>::of(ptr: &QGeoMap::copyrightsChanged),
691 slot: [&copyrightImage](const QImage &copy){ 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*/
740QDeclarativeGeoServiceProvider *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*/
752void 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
791qreal 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*/
802qreal QDeclarativeGeoMap::implicitMinimumZoomLevel() const
803{
804 return m_gestureArea->minimumZoomLevel();
805}
806
807/*!
808 \internal
809*/
810qreal 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*/
820void 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
848qreal 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*/
863void 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*/
878void 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
908bool 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
929bool 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
950bool 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
960qreal 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*/
977void 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*/
1007void 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
1025qreal 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*/
1044void 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
1063qreal QDeclarativeGeoMap::tilt() const
1064{
1065 if (m_initialized)
1066 return m_map->cameraData().tilt();
1067 return m_cameraData.tilt();
1068}
1069
1070void 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*/
1105void 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
1124qreal QDeclarativeGeoMap::fieldOfView() const
1125{
1126 if (m_initialized)
1127 return m_map->cameraData().fieldOfView();
1128 return m_cameraData.fieldOfView();
1129}
1130
1131void 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*/
1163qreal QDeclarativeGeoMap::minimumFieldOfView() const
1164{
1165 return m_minimumFieldOfView;
1166}
1167
1168void 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*/
1200qreal 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*/
1220qreal QDeclarativeGeoMap::minimumTilt() const
1221{
1222 return m_minimumTilt;
1223}
1224
1225void 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*/
1259qreal 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*/
1272void QDeclarativeGeoMap::setCenter(const QGeoCoordinate &center)
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
1294QGeoCoordinate 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*/
1320void 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
1346QGeoShape 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*/
1381void 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
1393bool 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*/
1407void 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
1416QColor 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*/
1431QRectF QDeclarativeGeoMap::visibleArea() const
1432{
1433 if (m_initialized)
1434 return m_map->visibleArea();
1435 return m_visibleArea;
1436}
1437
1438void 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*/
1475bool QDeclarativeGeoMap::mapReady() const
1476{
1477 return m_initialized;
1478}
1479
1480QMargins 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*/
1498QQmlListProperty<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*/
1521void 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*/
1542QGeoCoordinate 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*/
1558QPointF 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*/
1577void 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*/
1596void 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*/
1610void 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*/
1628void 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
1645void 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
1686QString 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
1707QGeoServiceProvider::Error QDeclarativeGeoMap::error() const
1708{
1709 return m_error;
1710}
1711
1712QGeoMap *QDeclarativeGeoMap::map() const
1713{
1714 return m_map;
1715}
1716
1717void 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
1738bool QDeclarativeGeoMap::isInteractive()
1739{
1740 return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive();
1741}
1742
1743void 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
1752void 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
1761void 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
1769void 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*/
1813void 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*/
1840void 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*/
1860void 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*/
1880QList<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*/
1891void 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*/
1912void 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*/
1933void 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*/
1949QList<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
1968QList<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
1996void QDeclarativeGeoMap::addMapItem(QDeclarativeGeoMapItemBase *item)
1997{
1998 if (addMapItem_real(item))
1999 emit mapItemsChanged();
2000}
2001
2002bool 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*/
2026void QDeclarativeGeoMap::removeMapItem(QDeclarativeGeoMapItemBase *ptr)
2027{
2028 if (removeMapItem_real(item: ptr))
2029 emit mapItemsChanged();
2030}
2031
2032bool 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*/
2056void 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*/
2091void QDeclarativeGeoMap::addMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup)
2092{
2093 if (addMapItemGroup_real(itemGroup))
2094 emit mapItemsChanged();
2095}
2096
2097bool 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*/
2127void QDeclarativeGeoMap::removeMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup)
2128{
2129 if (removeMapItemGroup_real(itemGroup))
2130 emit mapItemsChanged();
2131}
2132
2133bool 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*/
2162void QDeclarativeGeoMap::removeMapItemView(QDeclarativeGeoMapItemView *itemView)
2163{
2164 if (removeMapItemView_real(itemView))
2165 emit mapItemsChanged();
2166}
2167
2168bool 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
2179void 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
2205void 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*/
2220void QDeclarativeGeoMap::addMapItemView(QDeclarativeGeoMapItemView *itemView)
2221{
2222 if (addMapItemView_real(itemView))
2223 emit mapItemsChanged();
2224}
2225
2226bool 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*/
2249void 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
2265QDeclarativeGeoMapType * QDeclarativeGeoMap::activeMapType() const
2266{
2267 return m_activeMapType;
2268}
2269
2270/*!
2271 \internal
2272*/
2273void 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*/
2337void 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*/
2361void QDeclarativeGeoMap::fitViewportToVisibleMapItems()
2362{
2363 fitViewportToMapItemsRefine(mapItems: m_mapItems, refine: true, onlyVisible: true);
2364}
2365
2366/*!
2367 \internal
2368*/
2369void 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*/
2481void 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*/
2492void 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*/
2503void 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*/
2514void QDeclarativeGeoMap::mouseUngrabEvent()
2515{
2516 if (isInteractive())
2517 m_gestureArea->handleMouseUngrabEvent();
2518 else
2519 QQuickItem::mouseUngrabEvent();
2520}
2521
2522void QDeclarativeGeoMap::touchUngrabEvent()
2523{
2524 if (isInteractive())
2525 m_gestureArea->handleTouchUngrabEvent();
2526 else
2527 QQuickItem::touchUngrabEvent();
2528}
2529
2530/*!
2531 \internal
2532*/
2533void 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*/
2547void 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*/
2560bool 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
2601bool 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
2644bool 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
2692QT_END_NAMESPACE
2693

source code of qtlocation/src/location/declarativemaps/qdeclarativegeomap.cpp